Windows-Server-2003/sdktools/link16/errors.c

741 lines
17 KiB
C

/*** error.c - error handling functions
*
* Copyright <C> 1989, Microsoft Corporation
*
* Purpose:
* This module contains all error message functions and variuos
* functions that check if errror condition has occured.
*
* This Module contains Proprietary Information of Microsoft
* Corporation and should be treated as Confidential.
*
* Revision History:
*
* [WJK] 28-Jun-1990 Created
*
*************************************************************************/
#include <minlit.h> /* Types, constants */
#include <bndtrn.h> /* More types and constants */
#include <bndrel.h> /* Types and constants */
#include <lnkio.h> /* Linker I/O definitions */
#include <lnkmsg.h> /* Error messages */
#include <nmsg.h> /* Near message strings */
#include <extern.h> /* External declarations */
#include <string.h>
#if (defined(WIN_NT) OR defined(DOSX32)) AND (NOT defined( _WIN32 ))
#define i386
#endif
#include <stdarg.h>
#if EXE386
#include <exe386.h>
#endif
#if WIN_3
#include <windows.h>
#endif
#if NEWIO
#include <errno.h> /* System error codes */
#endif
#define DEBUG_WIN FALSE
#if DEBUG_WIN
char szDebugBuffer[80];
#define DEBUGW(parm1,parm2)\
{\
wsprintf(szDebugBuffer,parm1,(char far*)parm2);\
OutputDebugString(szDebugBuffer);\
}
#else
#define DEBUGW(parm1,parm2)
#endif
#if OSEGEXE AND NOT QCLINK
extern int yylineno; /* Current line in definitions file */
#else
#define yylineno -1
#endif
#if AUTOVM
extern BYTE FAR * NEAR FetchSym1(RBTYPE rb, WORD Dirty);
#define FETCHSYM FetchSym1
#else
#define FETCHSYM FetchSym
#endif
LOCAL char chErr = 'L'; /* Error message prefix */
/*
* LOCAL FUNCTION PROTOTYPES
*/
LOCAL void cdecl NEAR ErrSub(MSGTYPE msg, WORD fWarn, va_list pArgList);
LOCAL void vFmtPrint(char *fmt, va_list pArgList);
/*** ChkInput - check input file for I/O errors
*
* Purpose:
* Check if there were any I/O errors on input file.
*
* Input:
* No explicit value is passed. The global input file bsInput is
* used.
*
* Output:
* If everything is OK function returns, otherwise it calls Fatal
* with appropriate error message.
*
* Exceptions:
* None.
*
* Notes:
* None.
*
*************************************************************************/
void ChkInput(void)
{
if (feof(bsInput))
Fatal(ER_eofobj);
else if (ferror(bsInput))
Fatal(ER_ioerr, strerror(errno));
}
// Comment : GetMsg and __NMSG_TEXT are generated by MKMSG [jp]
/*** ErrPrefix - write error message prefix
*
* Purpose:
* Write out error message prefix. If we are parsinf .DEF file or reading
* .OBJ files then the error message prefix takes form "<filename> : "
* otherwise this is "LINK : ".
*
* Input:
* No explicit value is passed.
*
* Output:
* No explicit value is returned.
*
* Exceptions:
* None.
*
* Notes:
* None.
*
*************************************************************************/
void ErrPrefix(void)
{
DisplayBanner();
if (fDrivePass || yylineno > 0)
OutFileCur(bsErr);
else
FmtPrint(lnknam);
FmtPrint(" : ");
}
#pragma check_stack(on)
/*** OutFileCur - write out current input file name
*
* Purpose:
* Write out current input file name. Used by error message functions.
* File name is written in the following formats:
* <filename>(nnn)
* <filename>(<modulename>)
* <filename>
*
* Input:
* bs - file to which current input file name is written
*
* Output:
* No explicit value is returned.
*
* Exceptions:
* None.
*
* Notes:
* None.
*
*************************************************************************/
void OutFileCur(BSTYPE bs)
{
APROPFILEPTR apropFile; /* Pointer to file property cell */
AHTEPTR ahte; /* Pointer symbol name */
BSTYPE bsTmp; /* Temporary file pointer */
SBTYPE fileName; /* File name buffer */
SBTYPE moduleName; /* Object module name */
int n; /* String length counter */
#if OSEGEXE
if (yylineno > 0)
{
apropFile = (APROPFILEPTR) FETCHSYM(rhteDeffile,FALSE);
ahte = GetHte(rhteDeffile);
}
else
#endif
{
apropFile = (APROPFILEPTR ) FETCHSYM(vrpropFile,FALSE);
ahte = GetHte(vrpropFile);
}
bsTmp = bsErr;
bsErr = bs;
// Copy file name
n = (ahte->cch[0] < sizeof(fileName) - 1) ? ahte->cch[0] : sizeof(fileName) - 1;
FMEMCPY((char FAR *) fileName, &ahte->cch[1], n);
fileName[n] = '\0';
if (yylineno > 0)
FmtPrint("%s(%d)", fileName, yylineno);
else if (apropFile->af_rMod)
{
// Get object module name
ahte = (AHTEPTR ) FETCHSYM(apropFile->af_rMod,FALSE);
while(ahte->attr != ATTRNIL)
ahte = (AHTEPTR ) FETCHSYM(ahte->rhteNext,FALSE);
n = (ahte->cch[0] < sizeof(moduleName) - 1) ? ahte->cch[0] : sizeof(moduleName) - 1;
FMEMCPY((char FAR *) moduleName, &ahte->cch[1], n);
moduleName[n] = '\0';
FmtPrint("%s(%s)", fileName, moduleName);
}
else
FmtPrint("%s", fileName);
bsErr = bsTmp;
}
#pragma check_stack(off)
#if (QCLINK OR OSEGEXE) AND NOT EXE386
typedef int (cdecl FAR * FARFPTYPE)(char FAR *buf);
/* Far function pointer type */
extern FARFPTYPE FAR *pfQTab; /* Table of addresses */
#endif
/*** vFmtPrint - print formated message
*
* Purpose:
* Print on bsErr formated error or warning message.
* Check for any I/O errors.
*
* Input:
* fmt - error message format string
* pArgList - pointer to variable number of parameters desrcibing error message
* bsErr - error file - global variable
* bsLst - listing file - global variable
*
* Output:
* No explicit value is returned.
*
* Exceptions:
* I/O errors. If error detected and stdout is an error file then silently
* exit to system with return code 4 (something must be terribly wrong
* if stdout is not working). If we are writing to listing file and
* error was detected then close listing file and notify user.
*
* Notes:
* This function handles output to QC enviroment.
*
*************************************************************************/
LOCAL void vFmtPrint(char *fmt, va_list pArgList)
{
#if WIN_3
char buf[512];
vsprintf(buf, fmt, pArgList);
ErrMsgWin(buf);
#if DEBUG_WIN2
OutputDebugString((char far*)"\r\nDebS: ");
OutputDebugString((char far*)buf);
#endif
#else
#if (QCLINK) AND NOT EXE386
SBTYPE buf;
if (fZ1)
{
// Output via QC call-back
vsprintf(buf, fmt, pArgList);
(*pfQTab[0])((char far *) buf);
}
else
#endif
{
vfprintf(bsErr, fmt, pArgList);
if (ferror(bsErr))
{
if (bsErr == stdout)
{
#if USE_REAL
RealMemExit();
#endif
exit(4);
}
else if (bsErr == bsLst)
{
fclose(bsLst);
fLstFileOpen = FALSE;
bsErr = stdout;
}
ExitCode = 4;
Fatal(ER_spclst);
}
fflush(bsErr);
}
#endif // WIN_3
}
/*** FmtPrint - print formated message
*
* Purpose:
* Print on bsErr formated error or warning message.
* Check for any I/O errors.
*
* Input:
* fmt - error message format string
* ... - variable number of parameters desrcibing error message
*
* Output:
* No explicit value is returned.
*
* Exceptions:
* I/O errors.
*
* Notes:
* The actual job is done by the vFmtPrint.
*
*************************************************************************/
void cdecl FmtPrint(char *fmt, ...)
{
va_list pArgList;
va_start(pArgList, fmt);
vFmtPrint(fmt, pArgList);
}
#if OSMSDOS AND NOT WIN_3
/*
* PromptStd : Standard prompt routine
*
* Display a warning message and prompt, with optional arguments.
* Optionally read a response into a given buffer. If the given
* buffer is null, get a yes/no response with <ENTER> being yes.
*
* Returns:
* TRUE if yes or response read.
* FALSE if no.
*/
int cdecl PromptStd (sbNew,msg,msgparm,pmt,pmtparm)
BYTE *sbNew; /* Buffer for response */
MSGTYPE msg; /* Error message */
int msgparm; /* Message parameter */
MSGTYPE pmt; /* Prompt */
int pmtparm; /* Prompt parameter */
{
register BYTE *p;
int ch;
int n;
if(msg)
OutWarn(msg, msgparm);
if(!pmt)
return(TRUE);
fprintf(stderr,GetMsg(pmt),pmtparm);
fflush(stderr); /* Flush stderr */
#if CPU286
flskbd(); /* Flush DOS keyboard buffer */
#endif
fflush(stdin); /* Flush console input */
if(sbNew != NULL)
{
/* Read response */
for(p = &sbNew[1], n = 0;
(ch = fgetc(stdin)) != '\n' && ch != EOF && n < sizeof(SBTYPE); )
{
#if CRLF
if(ch == '\r')
continue;
#endif
*p++ = (BYTE) ch;
n++;
}
sbNew[0] = (BYTE) n;
return(TRUE);
}
#if CRLF
if(fgetc(stdin) != '\r')
return(FALSE);
#endif
if(fgetc(stdin) != '\n')
return(FALSE);
return(TRUE);
}
#endif /* OSMSDOS */
/*
* CputcStd : standard console character output routine.
* Call fputc to stdout. Will be called through pfCputc.
*/
void CputcStd (ch)
int ch;
{
putc(ch,stdout);
if (ferror(stdout))
exit(4);
}
/*
* CputsStd : standard console string output routine
* Call fputs to stdout. Will be called through pfCputs.
*/
void CputsStd (str)
char *str;
{
fputs(str,stdout);
if (ferror(stdout))
{
#if USE_REAL
RealMemExit();
#endif
exit(4);
}
fflush(stdout);
}
/*** ErrSub - write out nonfatal error message
*
* Purpose:
* Fromat and write out nonfatal error message. If error message number
* is equal to zero then we treat it as a prompt.
*
* Input:
* msg - error message number
* fWarn - TRUE if this warnnig
* ... - variable number of parameters for message
* bsErr - error file - global variable
* bsLst - listing file - global variable
*
* Output:
* No explicit value is returned.
*
* Exceptions:
* None.
*
* Notes:
* None.
*
*************************************************************************/
LOCAL void cdecl NEAR ErrSub(MSGTYPE msg, WORD fWarn, va_list pArgList)
{
if (fLstFileOpen && bsErr == bsLst && vgsnLineNosPrev)
{ /* If we've listed line numbers */
NEWLINE(bsErr); /* Newline */
vgsnLineNosPrev = 0; /* Reset */
}
if (msg)
{
/* If there is any message to print */
#if WIN_3
if(fWarn)
fSeverity=SEV_WARNING;
else
fSeverity=SEV_ERROR;
#endif
#if MSGMOD AND NOT WIN_3
if (msg >= 1000)
{
#endif
/* Error or warning */
ErrPrefix();
#if MSGMOD
FmtPrint("%s %c%04d: ",
fWarn ? __NMSG_TEXT(N_warning) : __NMSG_TEXT(N_error),
(int) chErr, msg);
#else
FmtPrint("%s: ",fWarn ? "warning" : "error");
#endif
vFmtPrint(GetMsg(msg), pArgList);
#if NOT WIN_3
#if QCLINK
if (fZ1)
FmtPrint("\n");
else
#endif
NEWLINE(bsErr);
#else
FmtPrint("\r\n");
// for the second part of the message
fSeverity = SEV_WARNING;
#endif
if (fDrivePass && !fWarn
#if MSGMOD
&& (msg >= 2005 && msg < 2022) || msg == 1101
#endif
)
FmtPrint("%s: %lx %s: %02x\r\n",
__NMSG_TEXT(N_pos),ftell(bsInput),
__NMSG_TEXT(N_rectyp),rect & 0xff);
#if MSGMOD AND NOT WIN_3
}
else
{
/* Prompt */
#if QCLINK
if (fZ1)
(*pfPrompt)(NULL, msg, (int) pArgList, 0, 0);
else
{
#endif
vFmtPrint(GetMsg(msg), pArgList);
NEWLINE(bsErr);
#if QCLINK
}
#endif
}
#endif
}
}
/*** OutError - write out nonfatal error message
*
* Purpose:
* Top level function called when error message has to be displayed.
* Bumps the error counter and calls ErrSub to do the job.
*
* Input:
* msg - error message number
* ... - variable number of error parameters
*
* Output:
* No explicit value is returned. Global error counter cErrors is
* incremented.
*
* Exceptions:
* None.
*
* Notes:
* None.
*
*************************************************************************/
void cdecl OutError(MSGTYPE msg, ...)
{
va_list pArgList;
va_start(pArgList, msg);
++cErrors; /* Increment error count */
ErrSub(msg, FALSE, pArgList);
}
/*** OutWarn - write out warning message
*
* Purpose:
* Top level function called when warning message has to be displayed.
* Calls ErrSub to do the job.
*
* Input:
* msg - error message number
* ... - variable number of error parameters
*
* Output:
* No explicit value is returned.
* incremented.
*
* Exceptions:
* None.
*
* Notes:
* None.
*
*************************************************************************/
void cdecl OutWarn (MSGTYPE msg, ...)
{
va_list pArgList;
DEBUGW("\r\nOutWarn entered",0);
va_start(pArgList, msg);
ErrSub(msg, TRUE, pArgList);
}
/*** KillRunFile - delete .EXE file
*
* Purpose:
* Delete the .EXE file created by the linker.
*
* Input:
* No explicit value is passed.
* bsRunfile - output file handle - global variable.
* psbRun - output file name - global variable.
*
* Output:
* No explicit value is returned.
*
* Exceptions:
* None.
*
* Notes:
* None.
*
*************************************************************************/
void KillRunfile(void)
{
if (bsRunfile != NULL)
{
CloseFile(bsRunfile);
_unlink(&psbRun[1]);
}
}
/*** Fatal - write out fatal error message
*
* Purpose:
* Format and write out fatal error message. Terminate linker.
*
* Input:
* msg - error message number
* ... - variable number of message parameters
* bsLst - listing file - global variable
*
* Output:
* No explicit value is returned. Linker terminates.
*
* Exceptions:
* None.
*
* Notes:
* None.
*
*************************************************************************/
void cdecl Fatal (MSGTYPE msg, ...)
{
static WORD cInvoked =0;
va_list pArgList;
va_start(pArgList, msg); /* Get start of argument list */
if (++cInvoked > 1) // Fatal during Fatal
{
#if USE_REAL
RealMemExit();
#endif
if (ExitCode)
EXIT(ExitCode);
else
EXIT(2); /* Program error */
}
/* If msg is nonzero then print a message */
if (msg)
{
if (fLstFileOpen)
fflush(bsLst);
ErrPrefix();
#if MSGMOD
FmtPrint("%s %c%04d: ", __NMSG_TEXT(N_fatal), chErr, msg);
#else
FmtPrint("fatal error: ");
#endif
vFmtPrint(GetMsg(msg), pArgList);
#if WIN_3
fSeverity = SEV_ERROR; // Send as error to QCwin
FmtPrint("\r\n");
#else
NEWLINE(stderr);
#endif
if(fDrivePass && ftell(bsInput)
#if MSGMOD
&& msg >= 2005 && msg < 2022 || msg == 1101
#endif
)
FmtPrint("%s: %lx %s: %02x\r\n",
__NMSG_TEXT(N_pos),ftell(bsInput),
__NMSG_TEXT(N_rectyp),rect & 0xff);
}
KillRunfile();
if (fLstFileOpen) fclose(bsLst);
#if OWNSTDIO
FlsStdio();
#endif
// If someone else assigned the exit code, use it. Otherwise,
// assume program error.
if (ExitCode)
EXIT(ExitCode);
else
EXIT(2); /* Program error */
}
/*** CtrlC - display Ctrl-C error message and die
*
* Purpose:
* Do minimal work required to display error message and die.
*
* Input:
* No explicit value is passed.
*
* Output:
* No explicit value is returned.
*
* Exceptions:
* None.
*
* Notes:
* This function doesn't return.
*
*************************************************************************/
void CtrlC(void)
{
#if USE_REAL
RealMemExit();
#endif
#ifdef OS2
if (_osmode == OS2_MODE)
fputs("\r\n", stdout);
#endif
#if DOSEXTENDER AND NOT WIN_NT
if (!_fDosExt)
{
#endif
if (fLstFileOpen)
fflush(bsLst);
ErrPrefix();
FmtPrint("%s %c%04d: %s", __NMSG_TEXT(N_fatal), chErr, ER_intrpt, GetMsg(ER_intrpt));
KillRunfile();
if (fLstFileOpen)
fclose(bsLst);
#if OWNSTDIO
FlsStdio();
#endif
EXIT(4);
#if DOSEXTENDER AND NOT WIN_NT
}
else
_exit(4);
#endif
}