source: trunk/dll/errutil.c@ 1698

Last change on this file since 1698 was 1698, checked in by Steven Levine, 12 years ago

Update comments

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 KB
RevLine 
[121]1
2/***********************************************************************
3
4 $Id: errutil.c 1698 2013-11-07 17:18:42Z stevenhl $
5
6 Error reporting
7
8 Copyright (c) 1993-98 M. Kimes
[1698]9 Copyright (c) 2004, 2013 Steven H. Levine
[121]10
[137]11 12 Aug 04 SHL Comments
12 23 May 05 SHL Move saymsg here
[144]13 24 May 05 SHL Rename General_Error to more accurate Win_Error
14 24 May 05 SHL Rename saymsg to more accurate Misc_Error
15 24 May 05 SHL Rework Win_Error args and clean up logic
[182]16 27 May 05 SHL Rework to use common showMsg
[258]17 14 Aug 05 SHL showMsg: suppress write to stdout if not error message
[327]18 13 Jul 06 SHL Add Runtime_Error
19 22 Jul 06 SHL Optimize calling sequences
[383]20 26 Jul 06 SHL Add ..._Error2
[452]21 16 Aug 06 SHL Tweak message formatting
[552]22 07 Jan 07 GKY Move error strings etc. to string file
[613]23 18 Apr 07 SHL showMsg: correct selective logging checks
[616]24 19 Apr 07 SHL Add DbgMsg
25 20 Apr 07 SHL Correct IDS_GENERR1TEXT formatting
[636]26 23 Apr 07 SHL Add Win_Error_NoMsgBox. Rework others
[787]27 14 Aug 07 SHL Add GetMSecTimer
28 14 Aug 07 SHL Use GetMSecTimer in DbgMsg
[907]29 05 Jan 08 SHL Renamed from error.c to match errutil.h
[1373]30 18 Dec 08 SHL Show thread id in DbgMsg
[1395]31 07 Feb 09 GKY Allow user to turn off alert and/or error beeps in settings notebook.
32 07 Feb 09 GKY Eliminate Win_Error2 by moving function names to PCSZs used in Win_Error
33 07 Feb 09 GKY Allow user to turn off alert and/or error beeps in settings notebook.
[1402]34 08 Mar 09 GKY Remove Dos_Error2 (unused) and Runtime_Error2 (no advantage over using Runtime_Error)
[1449]35 23 Jul 09 GKY Add low mem buffers for the msg file name so DosGetMessage
[1558]36 works in HIMEM builds
37 01 Dec 10 SHL Dos_Error - remap API errors code that with odd oso001*.msg messages
[1686]38 10 Mar 13 GKY Improvrd readonly check on delete to allow cancel and don't ask again options
[1698]39 Added saymsg2 for this purpose
40 07 Nov 13 SHL Update comments
[121]41
42***********************************************************************/
43
[907]44#include <stdio.h>
45#include <string.h>
46#include <stdarg.h>
[1686]47#include <stdlib.h>
[907]48
[2]49#define INCL_DOS
[1373]50#define INCL_WIN
[2]51#define INCL_DOSERRORS
[1373]52#define INCL_DOSPROCESS // PPIB PTIB
[1395]53#define INCL_LONGLONG
[2]54
[907]55#include "errutil.h"
56#include "strutil.h" // GetPString
[2]57#include "fm3str.h"
[1558]58#include "notebook.h" // fErrorBeepOff
59#include "init.h" // Data declares
[1698]60#include "wrappers.h" // xmallocz
[2]61
62#pragma data_seg(DATA1)
63
[787]64static PSZ pszSrcFile = __FILE__;
65
[636]66static VOID formatWinError(PSZ pszBuf, UINT cBufBytes, HWND hwndErr, HWND hwndOwner,
67 PCSZ pszSrcFile, UINT uSrcLineNo,
68 PCSZ pszFmt, va_list pva);
69
[613]70static APIRET showMsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog);
[2]71
[1698]72/**
73 * Format debug message and output to stderr
74 * @note: Local errors also written to stderr
75 */
[144]76
[636]77VOID DbgMsg(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[121]78{
[1373]79 PIB *ppib;
80 TIB *ptib;
81 ULONG ultid;
82 APIRET apiret;
[144]83 va_list va;
[2]84
[787]85#if 1 // fixme to be selectable
86
87 static ULONG ul1stMSec;
88 // static ULONG ulLastMSec;
89
90 ULONG msec = GetMSecTimer();
91 ULONG delta;
92
93 if (!ul1stMSec) {
94 ul1stMSec = msec;
[1558]95 // ulLastMSec = msec; // Avoid big delta 1st time
[787]96 }
97
98 delta = msec - ul1stMSec;
99 // ulLastMSec = msec;
100 fprintf(stderr, "%03lu.%03lu ", delta / 1000, delta % 1000);
101
102#endif
103
[1373]104 apiret = DosGetInfoBlocks(&ptib, &ppib);
105 if (apiret)
106 ultid = 0;
107 else
108 ultid = ptib->tib_ptib2->tib2_ultid;
109
[636]110 // OK for source file name to be null
[1373]111 fprintf(stderr, "%s %u (%lu)", pszSrcFile ? pszSrcFile : "n/a", uSrcLineNo, ultid);
[636]112 // If format null want just file and line
113 if (pszFmt) {
114 fputc(' ', stderr);
115 va_start(va, pszFmt);
116 vfprintf(stderr, pszFmt, va);
117 va_end(va);
[613]118 }
[636]119 fputc('\n', stderr);
120 fflush(stderr);
[613]121
[636]122} // DbgMsg
[327]123
[1698]124/**
125 * Format Dos...() error and output using showMsg
126 * @note: Local errors written directly to stderr
127 */
[144]128
[1558]129// 2010-12-01 SHL fixme for ULONG to be APIRET
130
131INT Dos_Error(ULONG mb_type, ULONG apiret, HWND hwndOwner,
[636]132 PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[121]133{
[182]134 CHAR szMsg[4096];
[1450]135 CHAR szMsgFile[20], szMsgFileH[20];
[636]136 ULONG Class; // Error class
137 ULONG action; // Error action
[1558]138 ULONG Locus; // Error location
[144]139 ULONG ulMsgLen;
[1558]140 APIRET mapped_apiret;
[144]141 CHAR *pszMsgStart;
142 CHAR *psz;
143 va_list va;
[2]144
[1558]145 if (!apiret)
[144]146 return MBID_ENTER; // Should not have been called
[121]147
[144]148 // Format caller's message
149 va_start(va, pszFmt);
[613]150 szMsg[sizeof(szMsg) - 1] = 0;
[182]151 vsprintf(szMsg, pszFmt, va);
[144]152 va_end(va);
[121]153
[613]154 if (szMsg[sizeof(szMsg) - 1]) {
155 fprintf(stderr, "Buffer overflow in Dos_Error - need %u bytes\n", strlen(szMsg) + 1);
156 fflush(stderr);
157 }
158
[1395]159 if (strchr(szMsg, ' ') == NULL) {
160 strcat(szMsg, " ");
[1558]161 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]162 }
[327]163
[1558]164 DosErrClass(apiret, &Class, &action, &Locus);
[144]165
[182]166 sprintf(szMsg + strlen(szMsg),
[144]167 GetPString(IDS_DOSERR1TEXT),
[636]168 pszSrcFile,
169 uSrcLineNo,
[1558]170 apiret,
[144]171 GetPString(IDS_ERRCLASS1TEXT + (Class - 1)),
172 GetPString(IDS_ERRACTION1TEXT + (action - 1)),
[1558]173 GetPString(IDS_ERRLOCUS1TEXT + (Locus - 1)));
[182]174 pszMsgStart = szMsg + strlen(szMsg) + 1;
[1449]175 strcpy(szMsgFile, "OSO001.MSG");
176 strcpy(szMsgFileH, "OSO001H.MSG");
[144]177 // Get message leaving space for NL separator
[1558]178 // 2010-12-01 SHL Handle cases where message file message does not make sense relative to API error
179 switch (apiret) {
180 case ERROR_TIMEOUT:
181 mapped_apiret = ERROR_SEM_TIMEOUT; // Assume semaphore timeout
182 break;
183 default:
184 mapped_apiret = apiret;
185 }
186 if (!DosGetMessage(NULL, 0L, (PCHAR) pszMsgStart + 1, 1024, mapped_apiret, szMsgFile, &ulMsgLen)
187 || !DosGetMessage(NULL, 0L, (PCHAR) pszMsgStart + 1, 1024, mapped_apiret,
188 szMsgFileH, &ulMsgLen)) {
[144]189 // Got message
190 pszMsgStart[ulMsgLen + 1] = 0; // Terminate
[182]191 *(pszMsgStart - 1) = '\n'; // Stuff NL before message text
[144]192 *pszMsgStart = '\"'; // Prefix message text with quote
[182]193
[144]194 psz = pszMsgStart + ulMsgLen; // Point at last char
195 // Chop trailing NL CR TAB
196 while (*psz &&
[551]197 (*psz == '\r' || *psz == '\n' || *psz == ' ' || *psz == '\t')) {
[144]198 *psz-- = 0;
199 }
200 strcat(psz, "\""); // Append trailing quote
201
202 // Convert CR and NL combos to single space
203 psz = pszMsgStart;
[551]204 while (*psz) {
205 if (*psz == '\n' || *psz == '\r') {
[144]206 while (*(psz + 1) == '\n' || *(psz + 1) == '\r')
207 memmove(psz, psz + 1, strlen(psz));
208 *psz = ' ';
[2]209 }
[144]210 else
211 psz++;
[2]212 }
213 }
[137]214
[613]215 return showMsg(mb_type | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_DOSERR2TEXT),
216 szMsg, TRUE);
[137]217
[636]218} // Dos_Error
[137]219
[636]220/**
221 * Format last PM error into passed buffer
222 */
223
224static VOID formatWinError(PSZ pszBuf, UINT cBufBytes,
225 HWND hwndErr, HWND hwndOwner,
226 PCSZ pszSrcFile, UINT uSrcLineNo,
227 PCSZ pszFmt, va_list pva)
228{
[1673]229 PERRINFO pErrInfoBlk; // Pointer to ERRINFO structure filled by WinGetErrorInfo
230 PSZ pszOffset; // Pointer to current error message returned by WinGetErrorInfo
[636]231 PSZ psz;
232 HAB hab;
233
234 if (hwndErr == NULLHANDLE)
235 hab = (HAB)0;
236 else
237 hab = WinQueryAnchorBlock(hwndErr);
238
239 // Format callers message
240 pszBuf[cBufBytes - 1] = 0;
241 vsprintf(pszBuf, pszFmt, pva);
242
243 if (pszBuf[cBufBytes - 1]) {
244 fprintf(stderr, "Buffer overflow in formatWinError - need %u bytes\n", strlen(pszBuf) + 1);
245 fflush(stderr);
246 }
247
[1395]248 if (strchr(pszBuf, ' ') == NULL) {
249 strcat(pszBuf, " ");
[1558]250 strcat(pszBuf, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]251 }
[636]252
253 // Append file name and line number and trailing space
254 sprintf(pszBuf + strlen(pszBuf),
255 GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo);
256
257 // Get last PM error for the current thread
258 pErrInfoBlk = WinGetErrorInfo(hab);
259 if (!pErrInfoBlk) {
[907]260 ERRORID id = WinGetLastError(hab);
[636]261 psz = pszBuf + strlen(pszBuf);
[904]262 sprintf(psz, " WinGetErrorInfo failed (%u)", id);
[636]263 }
264 else {
265 if (!hwndOwner)
266 hwndOwner = HWND_DESKTOP;
[1673]267 // Find message offset in array of message offsets Assume 1 message - fixme?
[636]268 pszOffset = ((PSZ) pErrInfoBlk) + pErrInfoBlk->offaoffszMsg;
[1673]269 // Address error message in array of messages and append error message to source code linenumber
[636]270 psz = pszBuf + strlen(pszBuf);
[904]271 sprintf(psz, " #0x%04x \"", ERRORIDERROR(pErrInfoBlk->idError));
[636]272 psz += strlen(psz);
273 strcpy(psz, ((PSZ) pErrInfoBlk) + *(PSHORT) pszOffset);
274 psz += strlen(psz);
275 // Chop trailing mush
276 psz--;
277 while (*psz == '\r' || *psz == '\n' || *psz == ' ')
278 *psz-- = 0;
279 if (*psz)
280 psz++;
281 strcpy(psz, "\"");
282 WinFreeErrorInfo(pErrInfoBlk); // Free resource segment
283 }
284
285} // formatWinError
286
[787]287/**
288 * Return millisecond timer value
289 * Resolution is milliseconds, but accuracy will be less
290 * depending on systems settings
291 */
292
293ULONG GetMSecTimer(void)
294{
295 ULONG msec;
296
297 APIRET rc = DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
298 &msec, sizeof(msec));
299 if (rc) {
300 Dos_Error(MB_ENTER, rc, HWND_DESKTOP, pszSrcFile, __LINE__,
301 "DosQuerySysInfo");
302 msec = 0;
303 }
304 return msec;
305}
306
[1698]307/**
308 * Format runtime error message and output using showMsg
309 * @note: Local errors written directly to stderr
310 * @note: If pszFmt is NULL a No Data error message is returned GKY 20 Feb 09 (Replaces Runtime_Error2)
311 */
[327]312
[551]313VOID Runtime_Error(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[327]314{
315 CHAR szMsg[4096];
316 va_list va;
317
318 // Format caller's message
[1398]319 if (!pszFmt)
320 pszFmt = PCSZ_NODATA;
[327]321 va_start(va, pszFmt);
[613]322 szMsg[sizeof(szMsg) - 1] = 0;
[327]323 vsprintf(szMsg, pszFmt, va);
324 va_end(va);
325
[613]326 if (szMsg[sizeof(szMsg) - 1]) {
327 fprintf(stderr, "Buffer overflow in Runtime_Error - need %u bytes\n", strlen(szMsg) + 1);
328 fflush(stderr);
329 }
330
[1395]331 if (strchr(szMsg, ' ') == NULL) {
332 strcat(szMsg, " ");
[1558]333 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]334 }
[327]335
336 sprintf(szMsg + strlen(szMsg),
[613]337 GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo);
[327]338
[1395]339 showMsg(MB_ICONEXCLAMATION, HWND_DESKTOP, GetPString(IDS_DEBUG_STRING), szMsg, TRUE);
[327]340
[636]341} // Runtime_Error
[327]342
[1698]343/**
344 * Format message and output using showMsg
345 * @note: Local errors written directly to stderr
346 */
[144]347
[551]348APIRET saymsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszFmt, ...)
[137]349{
[182]350 CHAR szMsg[4096];
[144]351 va_list va;
[137]352
[144]353 va_start(va, pszFmt);
[613]354 szMsg[sizeof(szMsg) - 1] = 0;
[182]355 vsprintf(szMsg, pszFmt, va);
[144]356 va_end(va);
357
[613]358 if (szMsg[sizeof(szMsg) - 1]) {
359 fprintf(stderr, "Buffer overflow in saymsg - need %u bytes\n", strlen(szMsg) + 1);
360 fflush(stderr);
361 }
[452]362
[613]363 return showMsg(mb_type, hwnd, pszTitle, szMsg, FALSE);
364
[636]365} // saymsg
[182]366
[1698]367/**
368 * Format message with custom buttons and output using showMsg
369 * Local errors written to stderr
370 */
371
[1686]372APIRET saymsg2(PCSZ pszButtonNames, int DefaultButton, HWND hwnd, PCSZ pszTitle, PCSZ pszFmt, ...)
373{
[1698]374 ULONG i;
375 APIRET rc;
[1686]376 CHAR szMsg[4096];
377 va_list va;
[1698]378 MB2INFO *pmbInfo;
379 MB2D mb2dBut[4];
380 ULONG ulInfoSize = (sizeof(MB2INFO) + (sizeof(MB2D) * 3));
[1686]381
382 va_start(va, pszFmt);
383 szMsg[sizeof(szMsg) - 1] = 0;
384 vsprintf(szMsg, pszFmt, va);
385 va_end(va);
386
387 if (szMsg[sizeof(szMsg) - 1]) {
388 fprintf(stderr, "Buffer overflow in saymsg2 - need %u bytes\n", strlen(szMsg) + 1);
389 fflush(stderr);
390 }
391
392 memset(mb2dBut, 0, sizeof(MB2D) * 4);
393 //fixme to use GetPString
394 strcpy(mb2dBut[0].achText, /*pszButtonNames[0] ? &pszButtonNames[0] :*/ GetPString(IDS_MB2DYES));
395 strcpy(mb2dBut[1].achText, /*pszButtonNames[1] ? &pszButtonNames[1] :*/ GetPString(IDS_MB2DYESDONTASK));
396 strcpy(mb2dBut[2].achText, /*pszButtonNames[2] ? &pszButtonNames[2] :*/ GetPString(IDS_MB2DNO));
397 strcpy(mb2dBut[3].achText,/* pszButtonNames[3] ? &pszButtonNames[3] :*/ GetPString(IDS_MB2DCANCELOP));
398 mb2dBut[0].idButton = 1;
399 mb2dBut[1].idButton = 2;
400 mb2dBut[2].idButton = 3;
401 mb2dBut[3].idButton = 4;
402 if (DefaultButton)
403 mb2dBut[DefaultButton - 1].flStyle = BS_DEFAULT;
404 pmbInfo = xmallocz(ulInfoSize, pszSrcFile, __LINE__);
405 if (pmbInfo) {
[1698]406 pmbInfo->cb = ulInfoSize;
[1686]407 pmbInfo->hIcon = 0;
408 pmbInfo->cButtons = 4;
409 pmbInfo->flStyle = MB_MOVEABLE;
410 pmbInfo->hwndNotify = NULLHANDLE;
411 for (i = 0; i < 4; i++) {
412 memcpy( pmbInfo->mb2d+i , mb2dBut+i , sizeof(MB2D));
[1698]413 }
[1686]414 rc = WinMessageBox2(HWND_DESKTOP, hwnd,
[1698]415 szMsg, pszTitle, 1234,
416 pmbInfo);
[1686]417 free(pmbInfo);
418 return rc;
419 }
420 return MBID_ERROR;
421}
422
[1698]423/**
424 * Display message in popup message box
425 * Optionally writes formatted message to stderr
426 */
[182]427
[636]428static APIRET showMsg(ULONG mb_type, HWND hwndOwner,
429 PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog)
[182]430{
[613]431 if (wantLog) {
[258]432 fputs(pszMsg, stderr);
433 fputc('\n', stderr);
[614]434 fputc('\n', stderr);
[258]435 fflush(stderr);
436 }
437
[616]438 if (!hwndOwner)
439 hwndOwner = HWND_DESKTOP;
[1395]440 if (!fErrorBeepOff)
441 DosBeep(250, 100);
[144]442
[182]443 return WinMessageBox(HWND_DESKTOP, // Parent
[616]444 hwndOwner,
[551]445 (PSZ) pszMsg, (PSZ) pszTitle, 0, // help id
[182]446 mb_type | MB_MOVEABLE);
[613]447} // showMsg
[616]448
[1698]449/**
450 * Format Win...() error and output using showMsg
451 */
[616]452
[636]453VOID Win_Error(HWND hwndErr, HWND hwndOwner,
454 PCSZ pszSrcFile, UINT uSrcLineNo,
455 PCSZ pszFmt, ...)
[616]456{
[636]457 CHAR szMsg[4096];
[616]458 va_list va;
459
[636]460 // Format callers message
461 va_start(va, pszFmt);
462 formatWinError(szMsg, sizeof(szMsg), hwndErr, hwndOwner, pszSrcFile, uSrcLineNo, pszFmt, va);
463 va_end(va);
464
465 showMsg(MB_ENTER | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_GENERR2TEXT),
466 szMsg, TRUE);
467
468} // Win_Error
469
470/**
[1698]471 * Format PM error messsage and output to stderr
472 * This does the same reporting as Win_Error, but bypasses the
[636]473 * message box popup.
474 * Use this version when the popup would hang PM.
475 */
476
477VOID Win_Error_NoMsgBox(HWND hwndErr, HWND hwndOwner,
478 PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
479{
480 CHAR szMsg[4096];
481 va_list va;
482
483 // Format callers message
484 va_start(va, pszFmt);
485 formatWinError(szMsg, sizeof(szMsg), hwndErr, hwndOwner, pszSrcFile, uSrcLineNo, pszFmt, va);
486 va_end(va);
487
488 fputs(szMsg, stderr);
[616]489 fputc('\n', stderr);
[636]490 fputc('\n', stderr);
[616]491 fflush(stderr);
[1395]492 if (!fErrorBeepOff)
493 DosBeep(250, 100);
[616]494
[636]495} // Win_Error_NoMsgBox
[787]496
[1402]497#pragma alloc_text(ERROR,Win_Error,Dos_Error,saymsg,showMsg,Runtime_Error,GetMSecTimer)
Note: See TracBrowser for help on using the repository browser.