source: trunk/dll/errutil.c

Last change on this file was 1891, checked in by Steven Levine, 6 years ago

Rework FreeCnrItem to ensure all CNRITEMs deleted.
Use WinSendMsg CMA_NEXT.
Was using preccNextRecord which is not recommended because control does
not maintain linkage after inserts and deletes.
Note: arccnrs.c still needs to be reworked to use common functions from filldir.c.

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