source: trunk/dll/errutil.c@ 1840

Last change on this file since 1840 was 1840, checked in by Steven Levine, 10 years ago

Expose GetTidForThread and use

  • 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 1840 2015-08-12 05:06:11Z stevenhl $
5
6 Error reporting
7
8 Copyright (c) 1993-98 M. Kimes
9 Copyright (c) 2004, 2015 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 09 Nov 13 SHL Use GetTidForThread in DbgMsg and tweak for for editors that understand file:line
45
46***********************************************************************/
47
48#include <stdio.h>
49#include <string.h>
50#include <stdarg.h>
51#include <stdlib.h>
52
53#define INCL_DOS
54#define INCL_WIN
55#define INCL_DOSERRORS
56#define INCL_DOSPROCESS // PPIB PTIB
57#define INCL_LONGLONG
58
59#include "errutil.h"
60#include "strutil.h" // GetPString
61#include "fm3str.h"
62#include "notebook.h" // fErrorBeepOff
63#include "init.h" // Data declares
64#include "misc.h" // GetTidForThread
65#include "wrappers.h" // xmallocz
66#include "fm3dll2.h"
67
68#pragma data_seg(DATA1)
69
70static PSZ pszSrcFile = __FILE__;
71
72static VOID formatWinError(PSZ pszBuf, UINT cBufBytes, HWND hwndErr, HWND hwndOwner,
73 PCSZ pszSrcFile, UINT uSrcLineNo,
74 PCSZ pszFmt, va_list pva);
75
76static APIRET showMsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog);
77
78/**
79 * Format debug message and output to stderr
80 * @note: Local errors also written to stderr
81 */
82
83VOID DbgMsg(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
84{
85 ULONG ultid;
86 va_list va;
87
88#if 1 // fixme to be selectable
89
90 static ULONG ul1stMSec;
91 // static ULONG ulLastMSec;
92
93 ULONG msec = GetMSecTimer();
94 ULONG delta;
95
96 if (!ul1stMSec) {
97 ul1stMSec = msec;
98 // ulLastMSec = msec; // Avoid big delta 1st time
99 }
100
101 delta = msec - ul1stMSec;
102 // ulLastMSec = msec;
103 fprintf(stderr, "%03lu.%03lu ", delta / 1000, delta % 1000);
104
105#endif
106
107 ultid = GetTidForThread();
108
109 // OK for source file name to be null
110 // 2015-08-08 SHL Use file:line for editors that support it
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",
245 strlen(pszBuf) + 1);
246 fflush(stderr);
247 }
248
249 if (strchr(pszBuf, ' ') == NULL) {
250 strcat(pszBuf, " ");
251 strcat(pszBuf, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
252 }
253
254 // Append file name and line number and trailing space
255 sprintf(pszBuf + strlen(pszBuf),
256 GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo);
257
258 // Get last PM error for the current thread
259 pErrInfoBlk = WinGetErrorInfo(hab);
260 if (!pErrInfoBlk) {
261 ERRORID id = WinGetLastError(hab);
262 psz = pszBuf + strlen(pszBuf);
263 sprintf(psz, " WinGetErrorInfo failed (%u)", id);
264 }
265 else {
266 if (!hwndOwner)
267 hwndOwner = HWND_DESKTOP;
268 // Find message offset in array of message offsets Assume 1 message - fixme?
269 pszOffset = ((PSZ) pErrInfoBlk) + pErrInfoBlk->offaoffszMsg;
270 // Address error message in array of messages and append error message to source code linenumber
271 psz = pszBuf + strlen(pszBuf);
272 sprintf(psz, " #0x%04x \"", ERRORIDERROR(pErrInfoBlk->idError));
273 psz += strlen(psz);
274 strcpy(psz, ((PSZ) pErrInfoBlk) + *(PSHORT) pszOffset);
275 psz += strlen(psz);
276 // Chop trailing mush
277 psz--;
278 while (*psz == '\r' || *psz == '\n' || *psz == ' ')
279 *psz-- = 0;
280 if (*psz)
281 psz++;
282 strcpy(psz, "\"");
283 WinFreeErrorInfo(pErrInfoBlk); // Free resource segment
284 }
285
286} // formatWinError
287
288/**
289 * Return millisecond timer value
290 * Resolution is milliseconds, but accuracy will be less
291 * depending on systems settings
292 */
293
294ULONG GetMSecTimer(void)
295{
296 ULONG msec;
297
298 APIRET rc = DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
299 &msec, sizeof(msec));
300 if (rc) {
301 Dos_Error(MB_ENTER, rc, HWND_DESKTOP, pszSrcFile, __LINE__,
302 "DosQuerySysInfo");
303 msec = 0;
304 }
305 return msec;
306}
307
308/**
309 * Format runtime error message and output using showMsg
310 * @note: Local errors written directly to stderr
311 * @note: If pszFmt is NULL a No Data error message is returned GKY 20 Feb 09 (Replaces Runtime_Error2)
312 */
313
314VOID Runtime_Error(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
315{
316 CHAR szMsg[4096];
317 va_list va;
318
319 // Format caller's message
320 if (!pszFmt)
321 pszFmt = PCSZ_NODATA;
322 va_start(va, pszFmt);
323 szMsg[sizeof(szMsg) - 1] = 0;
324 vsprintf(szMsg, pszFmt, va);
325 va_end(va);
326
327 if (szMsg[sizeof(szMsg) - 1]) {
328 fprintf(stderr, "Buffer overflow in Runtime_Error - need %u bytes\n", strlen(szMsg) + 1);
329 fflush(stderr);
330 }
331
332 if (strchr(szMsg, ' ') == NULL) {
333 strcat(szMsg, " ");
334 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
335 }
336
337 sprintf(szMsg + strlen(szMsg),
338 GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo);
339
340 showMsg(MB_ICONEXCLAMATION, HWND_DESKTOP, GetPString(IDS_DEBUG_STRING), szMsg, TRUE);
341
342} // Runtime_Error
343
344/**
345 * Format message and output using showMsg
346 * @note: Local errors written directly to stderr
347 */
348
349APIRET saymsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszFmt, ...)
350{
351 CHAR szMsg[4096];
352 va_list va;
353
354 va_start(va, pszFmt);
355 szMsg[sizeof(szMsg) - 1] = 0;
356 vsprintf(szMsg, pszFmt, va);
357 va_end(va);
358
359 if (szMsg[sizeof(szMsg) - 1]) {
360 fprintf(stderr, "Buffer overflow in saymsg - need %u bytes\n", strlen(szMsg) + 1);
361 fflush(stderr);
362 }
363
364 return showMsg(mb_type, hwnd, pszTitle, szMsg, FALSE);
365
366} // saymsg
367
368/**
369 * Format message with custom buttons and output using showMsg
370 * Local errors written to stderr
371 */
372
373APIRET saymsg2(PCSZ pszButtonNames, int DefaultButton, HWND hwnd,
374 PCSZ pszTitle, PCSZ pszFmt, ...)
375{
376 ULONG i;
377 APIRET rc;
378 CHAR szMsg[4096];
379 va_list va;
380 MB2INFO *pmbInfo;
381 MB2D mb2dBut[4];
382 ULONG ulInfoSize = (sizeof(MB2INFO) + (sizeof(MB2D) * 3));
383
384 va_start(va, pszFmt);
385 szMsg[sizeof(szMsg) - 1] = 0;
386 vsprintf(szMsg, pszFmt, va);
387 va_end(va);
388
389 if (szMsg[sizeof(szMsg) - 1]) {
390 fprintf(stderr, "Buffer overflow in saymsg2 - need %u bytes\n", strlen(szMsg) + 1);
391 fflush(stderr);
392 }
393
394 memset(mb2dBut, 0, sizeof(MB2D) * 4);
395 strcpy(mb2dBut[0].achText,GetPString(IDS_MB2DYES));
396 strcpy(mb2dBut[1].achText,GetPString(IDS_MB2DYESDONTASK));
397 strcpy(mb2dBut[2].achText,GetPString(IDS_MB2DNO));
398 strcpy(mb2dBut[3].achText,GetPString(IDS_MB2DCANCELOP));
399 mb2dBut[0].idButton = SM2_YES;
400 mb2dBut[1].idButton = SM2_DONTASK;
401 mb2dBut[2].idButton = SM2_NO;
402 mb2dBut[3].idButton = SM2_CANCEL;
403 if (DefaultButton)
404 mb2dBut[DefaultButton - 1].flStyle = BS_DEFAULT;
405 pmbInfo = xmallocz(ulInfoSize, pszSrcFile, __LINE__);
406 if (pmbInfo) {
407 pmbInfo->cb = ulInfoSize;
408 pmbInfo->hIcon = 0;
409 pmbInfo->cButtons = 4;
410 pmbInfo->flStyle = MB_MOVEABLE | MB_ICONQUESTION ;
411 pmbInfo->hwndNotify = NULLHANDLE;
412 for (i = 0; i < 4; i++) {
413 memcpy( pmbInfo->mb2d+i , mb2dBut+i , sizeof(MB2D));
414 }
415 rc = WinMessageBox2(HWND_DESKTOP, hwnd,
416 szMsg, pszTitle, SM2_DIALOG,
417 pmbInfo);
418 WinSetFocus(HWND_DESKTOP, SM2_DIALOG);
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.