source: trunk/dll/errutil.c@ 1722

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

The rest of the files for CS 1721

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