/*********************************************************************** $Id: errutil.c 1673 2012-12-30 18:51:01Z gyoung $ Error reporting Copyright (c) 1993-98 M. Kimes Copyright (c) 2004, 2010 Steven H. Levine 12 Aug 04 SHL Comments 23 May 05 SHL Move saymsg here 24 May 05 SHL Rename General_Error to more accurate Win_Error 24 May 05 SHL Rename saymsg to more accurate Misc_Error 24 May 05 SHL Rework Win_Error args and clean up logic 27 May 05 SHL Rework to use common showMsg 14 Aug 05 SHL showMsg: suppress write to stdout if not error message 13 Jul 06 SHL Add Runtime_Error 22 Jul 06 SHL Optimize calling sequences 26 Jul 06 SHL Add ..._Error2 16 Aug 06 SHL Tweak message formatting 07 Jan 07 GKY Move error strings etc. to string file 18 Apr 07 SHL showMsg: correct selective logging checks 19 Apr 07 SHL Add DbgMsg 20 Apr 07 SHL Correct IDS_GENERR1TEXT formatting 23 Apr 07 SHL Add Win_Error_NoMsgBox. Rework others 14 Aug 07 SHL Add GetMSecTimer 14 Aug 07 SHL Use GetMSecTimer in DbgMsg 05 Jan 08 SHL Renamed from error.c to match errutil.h 18 Dec 08 SHL Show thread id in DbgMsg 07 Feb 09 GKY Allow user to turn off alert and/or error beeps in settings notebook. 07 Feb 09 GKY Eliminate Win_Error2 by moving function names to PCSZs used in Win_Error 07 Feb 09 GKY Allow user to turn off alert and/or error beeps in settings notebook. 08 Mar 09 GKY Remove Dos_Error2 (unused) and Runtime_Error2 (no advantage over using Runtime_Error) 23 Jul 09 GKY Add low mem buffers for the msg file name so DosGetMessage works in HIMEM builds 01 Dec 10 SHL Dos_Error - remap API errors code that with odd oso001*.msg messages ***********************************************************************/ #include #include #include #define INCL_DOS #define INCL_WIN #define INCL_DOSERRORS #define INCL_DOSPROCESS // PPIB PTIB #define INCL_LONGLONG #include "errutil.h" #include "strutil.h" // GetPString #include "fm3str.h" #include "notebook.h" // fErrorBeepOff #include "init.h" // Data declares #pragma data_seg(DATA1) static PSZ pszSrcFile = __FILE__; static VOID formatWinError(PSZ pszBuf, UINT cBufBytes, HWND hwndErr, HWND hwndOwner, PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, va_list pva); static APIRET showMsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog); //=== DbgMsg: output debug message stderr === VOID DbgMsg(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...) { PIB *ppib; TIB *ptib; ULONG ultid; APIRET apiret; va_list va; #if 1 // fixme to be selectable static ULONG ul1stMSec; // static ULONG ulLastMSec; ULONG msec = GetMSecTimer(); ULONG delta; if (!ul1stMSec) { ul1stMSec = msec; // ulLastMSec = msec; // Avoid big delta 1st time } delta = msec - ul1stMSec; // ulLastMSec = msec; fprintf(stderr, "%03lu.%03lu ", delta / 1000, delta % 1000); #endif apiret = DosGetInfoBlocks(&ptib, &ppib); if (apiret) ultid = 0; else ultid = ptib->tib_ptib2->tib2_ultid; // OK for source file name to be null fprintf(stderr, "%s %u (%lu)", pszSrcFile ? pszSrcFile : "n/a", uSrcLineNo, ultid); // If format null want just file and line if (pszFmt) { fputc(' ', stderr); va_start(va, pszFmt); vfprintf(stderr, pszFmt, va); va_end(va); } fputc('\n', stderr); fflush(stderr); } // DbgMsg //== Dos_Error: report Dos...() error using passed message string === // 2010-12-01 SHL fixme for ULONG to be APIRET INT Dos_Error(ULONG mb_type, ULONG apiret, HWND hwndOwner, PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...) { CHAR szMsg[4096]; CHAR szMsgFile[20], szMsgFileH[20]; ULONG Class; // Error class ULONG action; // Error action ULONG Locus; // Error location ULONG ulMsgLen; APIRET mapped_apiret; CHAR *pszMsgStart; CHAR *psz; va_list va; if (!apiret) return MBID_ENTER; // Should not have been called // Format caller's message va_start(va, pszFmt); szMsg[sizeof(szMsg) - 1] = 0; vsprintf(szMsg, pszFmt, va); va_end(va); if (szMsg[sizeof(szMsg) - 1]) { fprintf(stderr, "Buffer overflow in Dos_Error - need %u bytes\n", strlen(szMsg) + 1); fflush(stderr); } if (strchr(szMsg, ' ') == NULL) { strcat(szMsg, " "); strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name } DosErrClass(apiret, &Class, &action, &Locus); sprintf(szMsg + strlen(szMsg), GetPString(IDS_DOSERR1TEXT), pszSrcFile, uSrcLineNo, apiret, GetPString(IDS_ERRCLASS1TEXT + (Class - 1)), GetPString(IDS_ERRACTION1TEXT + (action - 1)), GetPString(IDS_ERRLOCUS1TEXT + (Locus - 1))); pszMsgStart = szMsg + strlen(szMsg) + 1; strcpy(szMsgFile, "OSO001.MSG"); strcpy(szMsgFileH, "OSO001H.MSG"); // Get message leaving space for NL separator // 2010-12-01 SHL Handle cases where message file message does not make sense relative to API error switch (apiret) { case ERROR_TIMEOUT: mapped_apiret = ERROR_SEM_TIMEOUT; // Assume semaphore timeout break; default: mapped_apiret = apiret; } if (!DosGetMessage(NULL, 0L, (PCHAR) pszMsgStart + 1, 1024, mapped_apiret, szMsgFile, &ulMsgLen) || !DosGetMessage(NULL, 0L, (PCHAR) pszMsgStart + 1, 1024, mapped_apiret, szMsgFileH, &ulMsgLen)) { // Got message pszMsgStart[ulMsgLen + 1] = 0; // Terminate *(pszMsgStart - 1) = '\n'; // Stuff NL before message text *pszMsgStart = '\"'; // Prefix message text with quote psz = pszMsgStart + ulMsgLen; // Point at last char // Chop trailing NL CR TAB while (*psz && (*psz == '\r' || *psz == '\n' || *psz == ' ' || *psz == '\t')) { *psz-- = 0; } strcat(psz, "\""); // Append trailing quote // Convert CR and NL combos to single space psz = pszMsgStart; while (*psz) { if (*psz == '\n' || *psz == '\r') { while (*(psz + 1) == '\n' || *(psz + 1) == '\r') memmove(psz, psz + 1, strlen(psz)); *psz = ' '; } else psz++; } } return showMsg(mb_type | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_DOSERR2TEXT), szMsg, TRUE); } // Dos_Error /** * Format last PM error into passed buffer */ static VOID formatWinError(PSZ pszBuf, UINT cBufBytes, HWND hwndErr, HWND hwndOwner, PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, va_list pva) { PERRINFO pErrInfoBlk; // Pointer to ERRINFO structure filled by WinGetErrorInfo PSZ pszOffset; // Pointer to current error message returned by WinGetErrorInfo PSZ psz; HAB hab; if (hwndErr == NULLHANDLE) hab = (HAB)0; else hab = WinQueryAnchorBlock(hwndErr); // Format callers message pszBuf[cBufBytes - 1] = 0; vsprintf(pszBuf, pszFmt, pva); if (pszBuf[cBufBytes - 1]) { fprintf(stderr, "Buffer overflow in formatWinError - need %u bytes\n", strlen(pszBuf) + 1); fflush(stderr); } if (strchr(pszBuf, ' ') == NULL) { strcat(pszBuf, " "); strcat(pszBuf, GetPString(IDS_FAILEDTEXT)); // Assume simple function name } // Append file name and line number and trailing space sprintf(pszBuf + strlen(pszBuf), GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo); // Get last PM error for the current thread pErrInfoBlk = WinGetErrorInfo(hab); if (!pErrInfoBlk) { ERRORID id = WinGetLastError(hab); psz = pszBuf + strlen(pszBuf); sprintf(psz, " WinGetErrorInfo failed (%u)", id); } else { if (!hwndOwner) hwndOwner = HWND_DESKTOP; // Find message offset in array of message offsets Assume 1 message - fixme? pszOffset = ((PSZ) pErrInfoBlk) + pErrInfoBlk->offaoffszMsg; // Address error message in array of messages and append error message to source code linenumber psz = pszBuf + strlen(pszBuf); sprintf(psz, " #0x%04x \"", ERRORIDERROR(pErrInfoBlk->idError)); psz += strlen(psz); strcpy(psz, ((PSZ) pErrInfoBlk) + *(PSHORT) pszOffset); psz += strlen(psz); // Chop trailing mush psz--; while (*psz == '\r' || *psz == '\n' || *psz == ' ') *psz-- = 0; if (*psz) psz++; strcpy(psz, "\""); WinFreeErrorInfo(pErrInfoBlk); // Free resource segment } } // formatWinError /** * Return millisecond timer value * Resolution is milliseconds, but accuracy will be less * depending on systems settings */ ULONG GetMSecTimer(void) { ULONG msec; APIRET rc = DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &msec, sizeof(msec)); if (rc) { Dos_Error(MB_ENTER, rc, HWND_DESKTOP, pszSrcFile, __LINE__, "DosQuerySysInfo"); msec = 0; } return msec; } //== Runtime_Error: report runtime library error using passed message string === //If pszFmt is NULL a No Data error message is returned GKY 20 Feb 09 (Replaces Runtime_Error2) VOID Runtime_Error(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...) { CHAR szMsg[4096]; va_list va; // Format caller's message if (!pszFmt) pszFmt = PCSZ_NODATA; va_start(va, pszFmt); szMsg[sizeof(szMsg) - 1] = 0; vsprintf(szMsg, pszFmt, va); va_end(va); if (szMsg[sizeof(szMsg) - 1]) { fprintf(stderr, "Buffer overflow in Runtime_Error - need %u bytes\n", strlen(szMsg) + 1); fflush(stderr); } if (strchr(szMsg, ' ') == NULL) { strcat(szMsg, " "); strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name } sprintf(szMsg + strlen(szMsg), GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo); showMsg(MB_ICONEXCLAMATION, HWND_DESKTOP, GetPString(IDS_DEBUG_STRING), szMsg, TRUE); } // Runtime_Error //=== saymsg: report misc error using passed message === APIRET saymsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszFmt, ...) { CHAR szMsg[4096]; va_list va; va_start(va, pszFmt); szMsg[sizeof(szMsg) - 1] = 0; vsprintf(szMsg, pszFmt, va); va_end(va); if (szMsg[sizeof(szMsg) - 1]) { fprintf(stderr, "Buffer overflow in saymsg - need %u bytes\n", strlen(szMsg) + 1); fflush(stderr); } return showMsg(mb_type, hwnd, pszTitle, szMsg, FALSE); } // saymsg //=== showMsg: display error popup === static APIRET showMsg(ULONG mb_type, HWND hwndOwner, PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog) { if (wantLog) { fputs(pszMsg, stderr); fputc('\n', stderr); fputc('\n', stderr); fflush(stderr); } if (!hwndOwner) hwndOwner = HWND_DESKTOP; if (!fErrorBeepOff) DosBeep(250, 100); return WinMessageBox(HWND_DESKTOP, // Parent hwndOwner, (PSZ) pszMsg, (PSZ) pszTitle, 0, // help id mb_type | MB_MOVEABLE); } // showMsg //== Win_Error: report Win...() error using passed message string === VOID Win_Error(HWND hwndErr, HWND hwndOwner, PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...) { CHAR szMsg[4096]; va_list va; // Format callers message va_start(va, pszFmt); formatWinError(szMsg, sizeof(szMsg), hwndErr, hwndOwner, pszSrcFile, uSrcLineNo, pszFmt, va); va_end(va); showMsg(MB_ENTER | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_GENERR2TEXT), szMsg, TRUE); } // Win_Error /** * Output PM error messsage to stderr * This does to same reporting as Win_Error, but bypasses the * message box popup. * Use this version when the popup would hang PM. */ VOID Win_Error_NoMsgBox(HWND hwndErr, HWND hwndOwner, PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...) { CHAR szMsg[4096]; va_list va; // Format callers message va_start(va, pszFmt); formatWinError(szMsg, sizeof(szMsg), hwndErr, hwndOwner, pszSrcFile, uSrcLineNo, pszFmt, va); va_end(va); fputs(szMsg, stderr); fputc('\n', stderr); fputc('\n', stderr); fflush(stderr); if (!fErrorBeepOff) DosBeep(250, 100); } // Win_Error_NoMsgBox #pragma alloc_text(ERROR,Win_Error,Dos_Error,saymsg,showMsg,Runtime_Error,GetMSecTimer)