/*** error.c - error handling functions * * Copyright 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 /* Types, constants */ #include /* More types and constants */ #include /* Types and constants */ #include /* Linker I/O definitions */ #include /* Error messages */ #include /* Near message strings */ #include /* External declarations */ #include #if (defined(WIN_NT) OR defined(DOSX32)) AND (NOT defined( _WIN32 )) #define i386 #endif #include #if EXE386 #include #endif #if WIN_3 #include #endif #if NEWIO #include /* 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 " : " * 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: * (nnn) * () * * * 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 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 }