source: trunk/dll/errutil.c@ 1718

Last change on this file since 1718 was 1718, checked in by Gregg Young, 12 years ago

Improvements to saymsg2 some code cleanup

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