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
Line 
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
9 Copyright (c) 2004, 2013 Steven H. Levine
10
11 12 Aug 04 SHL Comments
12 23 May 05 SHL Move saymsg here
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
16 27 May 05 SHL Rework to use common showMsg
17 14 Aug 05 SHL showMsg: suppress write to stdout if not error message
18 13 Jul 06 SHL Add Runtime_Error
19 22 Jul 06 SHL Optimize calling sequences
20 26 Jul 06 SHL Add ..._Error2
21 16 Aug 06 SHL Tweak message formatting
22 07 Jan 07 GKY Move error strings etc. to string file
23 18 Apr 07 SHL showMsg: correct selective logging checks
24 19 Apr 07 SHL Add DbgMsg
25 20 Apr 07 SHL Correct IDS_GENERR1TEXT formatting
26 23 Apr 07 SHL Add Win_Error_NoMsgBox. Rework others
27 14 Aug 07 SHL Add GetMSecTimer
28 14 Aug 07 SHL Use GetMSecTimer in DbgMsg
29 05 Jan 08 SHL Renamed from error.c to match errutil.h
30 18 Dec 08 SHL Show thread id in DbgMsg
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.
34 08 Mar 09 GKY Remove Dos_Error2 (unused) and Runtime_Error2 (no advantage over using Runtime_Error)
35 23 Jul 09 GKY Add low mem buffers for the msg file name so DosGetMessage
36 works in HIMEM builds
37 01 Dec 10 SHL Dos_Error - remap API errors code that with odd oso001*.msg messages
38 10 Mar 13 GKY Improvrd readonly check on delete to allow cancel and don't ask again options
39 Added saymsg2 for this purpose
40 07 Nov 13 SHL Update comments
41
42***********************************************************************/
43
44#include <stdio.h>
45#include <string.h>
46#include <stdarg.h>
47#include <stdlib.h>
48
49#define INCL_DOS
50#define INCL_WIN
51#define INCL_DOSERRORS
52#define INCL_DOSPROCESS // PPIB PTIB
53#define INCL_LONGLONG
54
55#include "errutil.h"
56#include "strutil.h" // GetPString
57#include "fm3str.h"
58#include "notebook.h" // fErrorBeepOff
59#include "init.h" // Data declares
60#include "wrappers.h" // xmallocz
61
62#pragma data_seg(DATA1)
63
64static PSZ pszSrcFile = __FILE__;
65
66static VOID formatWinError(PSZ pszBuf, UINT cBufBytes, HWND hwndErr, HWND hwndOwner,
67 PCSZ pszSrcFile, UINT uSrcLineNo,
68 PCSZ pszFmt, va_list pva);
69
70static APIRET showMsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog);
71
72/**
73 * Format debug message and output to stderr
74 * @note: Local errors also written to stderr
75 */
76
77VOID DbgMsg(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
78{
79 PIB *ppib;
80 TIB *ptib;
81 ULONG ultid;
82 APIRET apiret;
83 va_list va;
84
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;
95 // ulLastMSec = msec; // Avoid big delta 1st time
96 }
97
98 delta = msec - ul1stMSec;
99 // ulLastMSec = msec;
100 fprintf(stderr, "%03lu.%03lu ", delta / 1000, delta % 1000);
101
102#endif
103
104 apiret = DosGetInfoBlocks(&ptib, &ppib);
105 if (apiret)
106 ultid = 0;
107 else
108 ultid = ptib->tib_ptib2->tib2_ultid;
109
110 // OK for source file name to be null
111 fprintf(stderr, "%s %u (%lu)", pszSrcFile ? pszSrcFile : "n/a", uSrcLineNo, ultid);
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);
118 }
119 fputc('\n', stderr);
120 fflush(stderr);
121
122} // DbgMsg
123
124/**
125 * Format Dos...() error and output using showMsg
126 * @note: Local errors written directly to stderr
127 */
128
129// 2010-12-01 SHL fixme for ULONG to be APIRET
130
131INT Dos_Error(ULONG mb_type, ULONG apiret, HWND hwndOwner,
132 PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
133{
134 CHAR szMsg[4096];
135 CHAR szMsgFile[20], szMsgFileH[20];
136 ULONG Class; // Error class
137 ULONG action; // Error action
138 ULONG Locus; // Error location
139 ULONG ulMsgLen;
140 APIRET mapped_apiret;
141 CHAR *pszMsgStart;
142 CHAR *psz;
143 va_list va;
144
145 if (!apiret)
146 return MBID_ENTER; // Should not have been called
147
148 // Format caller's message
149 va_start(va, pszFmt);
150 szMsg[sizeof(szMsg) - 1] = 0;
151 vsprintf(szMsg, pszFmt, va);
152 va_end(va);
153
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
159 if (strchr(szMsg, ' ') == NULL) {
160 strcat(szMsg, " ");
161 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
162 }
163
164 DosErrClass(apiret, &Class, &action, &Locus);
165
166 sprintf(szMsg + strlen(szMsg),
167 GetPString(IDS_DOSERR1TEXT),
168 pszSrcFile,
169 uSrcLineNo,
170 apiret,
171 GetPString(IDS_ERRCLASS1TEXT + (Class - 1)),
172 GetPString(IDS_ERRACTION1TEXT + (action - 1)),
173 GetPString(IDS_ERRLOCUS1TEXT + (Locus - 1)));
174 pszMsgStart = szMsg + strlen(szMsg) + 1;
175 strcpy(szMsgFile, "OSO001.MSG");
176 strcpy(szMsgFileH, "OSO001H.MSG");
177 // Get message leaving space for NL separator
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)) {
189 // Got message
190 pszMsgStart[ulMsgLen + 1] = 0; // Terminate
191 *(pszMsgStart - 1) = '\n'; // Stuff NL before message text
192 *pszMsgStart = '\"'; // Prefix message text with quote
193
194 psz = pszMsgStart + ulMsgLen; // Point at last char
195 // Chop trailing NL CR TAB
196 while (*psz &&
197 (*psz == '\r' || *psz == '\n' || *psz == ' ' || *psz == '\t')) {
198 *psz-- = 0;
199 }
200 strcat(psz, "\""); // Append trailing quote
201
202 // Convert CR and NL combos to single space
203 psz = pszMsgStart;
204 while (*psz) {
205 if (*psz == '\n' || *psz == '\r') {
206 while (*(psz + 1) == '\n' || *(psz + 1) == '\r')
207 memmove(psz, psz + 1, strlen(psz));
208 *psz = ' ';
209 }
210 else
211 psz++;
212 }
213 }
214
215 return showMsg(mb_type | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_DOSERR2TEXT),
216 szMsg, TRUE);
217
218} // Dos_Error
219
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{
229 PERRINFO pErrInfoBlk; // Pointer to ERRINFO structure filled by WinGetErrorInfo
230 PSZ pszOffset; // Pointer to current error message returned by WinGetErrorInfo
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
248 if (strchr(pszBuf, ' ') == NULL) {
249 strcat(pszBuf, " ");
250 strcat(pszBuf, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
251 }
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) {
260 ERRORID id = WinGetLastError(hab);
261 psz = pszBuf + strlen(pszBuf);
262 sprintf(psz, " WinGetErrorInfo failed (%u)", id);
263 }
264 else {
265 if (!hwndOwner)
266 hwndOwner = HWND_DESKTOP;
267 // Find message offset in array of message offsets Assume 1 message - fixme?
268 pszOffset = ((PSZ) pErrInfoBlk) + pErrInfoBlk->offaoffszMsg;
269 // Address error message in array of messages and append error message to source code linenumber
270 psz = pszBuf + strlen(pszBuf);
271 sprintf(psz, " #0x%04x \"", ERRORIDERROR(pErrInfoBlk->idError));
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
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
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 */
312
313VOID Runtime_Error(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
314{
315 CHAR szMsg[4096];
316 va_list va;
317
318 // Format caller's message
319 if (!pszFmt)
320 pszFmt = PCSZ_NODATA;
321 va_start(va, pszFmt);
322 szMsg[sizeof(szMsg) - 1] = 0;
323 vsprintf(szMsg, pszFmt, va);
324 va_end(va);
325
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
331 if (strchr(szMsg, ' ') == NULL) {
332 strcat(szMsg, " ");
333 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
334 }
335
336 sprintf(szMsg + strlen(szMsg),
337 GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo);
338
339 showMsg(MB_ICONEXCLAMATION, HWND_DESKTOP, GetPString(IDS_DEBUG_STRING), szMsg, TRUE);
340
341} // Runtime_Error
342
343/**
344 * Format message and output using showMsg
345 * @note: Local errors written directly to stderr
346 */
347
348APIRET saymsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszFmt, ...)
349{
350 CHAR szMsg[4096];
351 va_list va;
352
353 va_start(va, pszFmt);
354 szMsg[sizeof(szMsg) - 1] = 0;
355 vsprintf(szMsg, pszFmt, va);
356 va_end(va);
357
358 if (szMsg[sizeof(szMsg) - 1]) {
359 fprintf(stderr, "Buffer overflow in saymsg - need %u bytes\n", strlen(szMsg) + 1);
360 fflush(stderr);
361 }
362
363 return showMsg(mb_type, hwnd, pszTitle, szMsg, FALSE);
364
365} // saymsg
366
367/**
368 * Format message with custom buttons and output using showMsg
369 * Local errors written to stderr
370 */
371
372APIRET saymsg2(PCSZ pszButtonNames, int DefaultButton, HWND hwnd, PCSZ pszTitle, PCSZ pszFmt, ...)
373{
374 ULONG i;
375 APIRET rc;
376 CHAR szMsg[4096];
377 va_list va;
378 MB2INFO *pmbInfo;
379 MB2D mb2dBut[4];
380 ULONG ulInfoSize = (sizeof(MB2INFO) + (sizeof(MB2D) * 3));
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) {
406 pmbInfo->cb = ulInfoSize;
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));
413 }
414 rc = WinMessageBox2(HWND_DESKTOP, hwnd,
415 szMsg, pszTitle, 1234,
416 pmbInfo);
417 free(pmbInfo);
418 return rc;
419 }
420 return MBID_ERROR;
421}
422
423/**
424 * Display message in popup message box
425 * Optionally writes formatted message to stderr
426 */
427
428static APIRET showMsg(ULONG mb_type, HWND hwndOwner,
429 PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog)
430{
431 if (wantLog) {
432 fputs(pszMsg, stderr);
433 fputc('\n', stderr);
434 fputc('\n', stderr);
435 fflush(stderr);
436 }
437
438 if (!hwndOwner)
439 hwndOwner = HWND_DESKTOP;
440 if (!fErrorBeepOff)
441 DosBeep(250, 100);
442
443 return WinMessageBox(HWND_DESKTOP, // Parent
444 hwndOwner,
445 (PSZ) pszMsg, (PSZ) pszTitle, 0, // help id
446 mb_type | MB_MOVEABLE);
447} // showMsg
448
449/**
450 * Format Win...() error and output using showMsg
451 */
452
453VOID Win_Error(HWND hwndErr, HWND hwndOwner,
454 PCSZ pszSrcFile, UINT uSrcLineNo,
455 PCSZ pszFmt, ...)
456{
457 CHAR szMsg[4096];
458 va_list va;
459
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/**
471 * Format PM error messsage and output to stderr
472 * This does the same reporting as Win_Error, but bypasses the
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);
489 fputc('\n', stderr);
490 fputc('\n', stderr);
491 fflush(stderr);
492 if (!fErrorBeepOff)
493 DosBeep(250, 100);
494
495} // Win_Error_NoMsgBox
496
497#pragma alloc_text(ERROR,Win_Error,Dos_Error,saymsg,showMsg,Runtime_Error,GetMSecTimer)
Note: See TracBrowser for help on using the repository browser.