source: trunk/dll/errutil.c@ 1891

Last change on this file since 1891 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
RevLine 
[121]1
2/***********************************************************************
3
4 $Id: errutil.c 1891 2020-01-31 02:47:37Z stevenhl $
5
6 Error reporting
7
[1891]8 Copyright (c) 1993-1998 M. Kimes
9 Copyright (c) 2004-2020 Steven H. Levine
[121]10
[137]11 12 Aug 04 SHL Comments
12 23 May 05 SHL Move saymsg here
[144]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
[182]16 27 May 05 SHL Rework to use common showMsg
[258]17 14 Aug 05 SHL showMsg: suppress write to stdout if not error message
[327]18 13 Jul 06 SHL Add Runtime_Error
19 22 Jul 06 SHL Optimize calling sequences
[383]20 26 Jul 06 SHL Add ..._Error2
[452]21 16 Aug 06 SHL Tweak message formatting
[552]22 07 Jan 07 GKY Move error strings etc. to string file
[613]23 18 Apr 07 SHL showMsg: correct selective logging checks
[616]24 19 Apr 07 SHL Add DbgMsg
25 20 Apr 07 SHL Correct IDS_GENERR1TEXT formatting
[636]26 23 Apr 07 SHL Add Win_Error_NoMsgBox. Rework others
[787]27 14 Aug 07 SHL Add GetMSecTimer
28 14 Aug 07 SHL Use GetMSecTimer in DbgMsg
[907]29 05 Jan 08 SHL Renamed from error.c to match errutil.h
[1373]30 18 Dec 08 SHL Show thread id in DbgMsg
[1395]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.
[1402]34 08 Mar 09 GKY Remove Dos_Error2 (unused) and Runtime_Error2 (no advantage over using Runtime_Error)
[1449]35 23 Jul 09 GKY Add low mem buffers for the msg file name so DosGetMessage
[1558]36 works in HIMEM builds
37 01 Dec 10 SHL Dos_Error - remap API errors code that with odd oso001*.msg messages
[1686]38 10 Mar 13 GKY Improvrd readonly check on delete to allow cancel and don't ask again options
[1698]39 Added saymsg2 for this purpose
40 07 Nov 13 SHL Update comments
[1718]41 15 Feb 14 GKY Improvements to saymsg2 some code cleanup
[1722]42 16 Feb 14 GKY Rework readonly check on delete code so it actually works in a logical way
[1891]43 and so it works with move to trashcan inabled.
[1840]44 09 Nov 13 SHL Use GetTidForThread in DbgMsg and tweak for for editors that understand file:line
[1891]45 28 Jan 20 SHL Add PmPrintf_Report
[121]46
47***********************************************************************/
48
[907]49#include <stdio.h>
50#include <string.h>
51#include <stdarg.h>
[1686]52#include <stdlib.h>
[907]53
[2]54#define INCL_DOS
[1373]55#define INCL_WIN
[2]56#define INCL_DOSERRORS
[1373]57#define INCL_DOSPROCESS // PPIB PTIB
[1395]58#define INCL_LONGLONG
[2]59
[907]60#include "errutil.h"
61#include "strutil.h" // GetPString
[2]62#include "fm3str.h"
[1558]63#include "notebook.h" // fErrorBeepOff
64#include "init.h" // Data declares
[1840]65#include "misc.h" // GetTidForThread
[1698]66#include "wrappers.h" // xmallocz
[1722]67#include "fm3dll2.h"
[2]68
[1891]69#ifdef PMPRINTF
70#define _PMPRINTF_ // Enable debug macros
71#include "PMPRINTF.H"
72#endif
73
[2]74#pragma data_seg(DATA1)
75
[787]76static PSZ pszSrcFile = __FILE__;
77
[636]78static VOID formatWinError(PSZ pszBuf, UINT cBufBytes, HWND hwndErr, HWND hwndOwner,
79 PCSZ pszSrcFile, UINT uSrcLineNo,
80 PCSZ pszFmt, va_list pva);
81
[613]82static APIRET showMsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog);
[2]83
[1698]84/**
85 * Format debug message and output to stderr
86 * @note: Local errors also written to stderr
87 */
[144]88
[636]89VOID DbgMsg(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[121]90{
[1373]91 ULONG ultid;
[144]92 va_list va;
[2]93
[1891]94#if 1 // FIXME to be selectable
[787]95
96 static ULONG ul1stMSec;
97
98 ULONG msec = GetMSecTimer();
99 ULONG delta;
100
101 if (!ul1stMSec) {
[1891]102 ul1stMSec = msec; // Avoid big delta 1st time
[787]103 }
104
105 delta = msec - ul1stMSec;
106 fprintf(stderr, "%03lu.%03lu ", delta / 1000, delta % 1000);
107
108#endif
109
[1840]110 ultid = GetTidForThread();
[1373]111
[636]112 // OK for source file name to be null
[1840]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);
[636]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);
[613]121 }
[636]122 fputc('\n', stderr);
123 fflush(stderr);
[613]124
[636]125} // DbgMsg
[327]126
[1698]127/**
128 * Format Dos...() error and output using showMsg
129 * @note: Local errors written directly to stderr
130 */
[144]131
[1891]132// 2010-12-01 SHL FIXME for ULONG to be APIRET
[1558]133
134INT Dos_Error(ULONG mb_type, ULONG apiret, HWND hwndOwner,
[636]135 PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[121]136{
[182]137 CHAR szMsg[4096];
[1450]138 CHAR szMsgFile[20], szMsgFileH[20];
[636]139 ULONG Class; // Error class
140 ULONG action; // Error action
[1558]141 ULONG Locus; // Error location
[144]142 ULONG ulMsgLen;
[1558]143 APIRET mapped_apiret;
[144]144 CHAR *pszMsgStart;
145 CHAR *psz;
146 va_list va;
[2]147
[1558]148 if (!apiret)
[144]149 return MBID_ENTER; // Should not have been called
[121]150
[144]151 // Format caller's message
152 va_start(va, pszFmt);
[613]153 szMsg[sizeof(szMsg) - 1] = 0;
[182]154 vsprintf(szMsg, pszFmt, va);
[144]155 va_end(va);
[121]156
[613]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
[1395]162 if (strchr(szMsg, ' ') == NULL) {
163 strcat(szMsg, " ");
[1558]164 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]165 }
[327]166
[1558]167 DosErrClass(apiret, &Class, &action, &Locus);
[144]168
[182]169 sprintf(szMsg + strlen(szMsg),
[144]170 GetPString(IDS_DOSERR1TEXT),
[636]171 pszSrcFile,
172 uSrcLineNo,
[1558]173 apiret,
[144]174 GetPString(IDS_ERRCLASS1TEXT + (Class - 1)),
175 GetPString(IDS_ERRACTION1TEXT + (action - 1)),
[1558]176 GetPString(IDS_ERRLOCUS1TEXT + (Locus - 1)));
[182]177 pszMsgStart = szMsg + strlen(szMsg) + 1;
[1449]178 strcpy(szMsgFile, "OSO001.MSG");
179 strcpy(szMsgFileH, "OSO001H.MSG");
[144]180 // Get message leaving space for NL separator
[1558]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)) {
[144]192 // Got message
193 pszMsgStart[ulMsgLen + 1] = 0; // Terminate
[182]194 *(pszMsgStart - 1) = '\n'; // Stuff NL before message text
[144]195 *pszMsgStart = '\"'; // Prefix message text with quote
[182]196
[144]197 psz = pszMsgStart + ulMsgLen; // Point at last char
198 // Chop trailing NL CR TAB
199 while (*psz &&
[551]200 (*psz == '\r' || *psz == '\n' || *psz == ' ' || *psz == '\t')) {
[144]201 *psz-- = 0;
202 }
203 strcat(psz, "\""); // Append trailing quote
204
205 // Convert CR and NL combos to single space
206 psz = pszMsgStart;
[551]207 while (*psz) {
208 if (*psz == '\n' || *psz == '\r') {
[144]209 while (*(psz + 1) == '\n' || *(psz + 1) == '\r')
210 memmove(psz, psz + 1, strlen(psz));
211 *psz = ' ';
[2]212 }
[144]213 else
214 psz++;
[2]215 }
216 }
[137]217
[613]218 return showMsg(mb_type | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_DOSERR2TEXT),
219 szMsg, TRUE);
[137]220
[636]221} // Dos_Error
[137]222
[636]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{
[1673]232 PERRINFO pErrInfoBlk; // Pointer to ERRINFO structure filled by WinGetErrorInfo
233 PSZ pszOffset; // Pointer to current error message returned by WinGetErrorInfo
[636]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]) {
[1718]247 fprintf(stderr, "Buffer overflow in formatWinError - need %u bytes\n",
[1891]248 strlen(pszBuf) + 1);
[636]249 fflush(stderr);
250 }
251
[1395]252 if (strchr(pszBuf, ' ') == NULL) {
253 strcat(pszBuf, " ");
[1558]254 strcat(pszBuf, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]255 }
[636]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) {
[907]264 ERRORID id = WinGetLastError(hab);
[636]265 psz = pszBuf + strlen(pszBuf);
[904]266 sprintf(psz, " WinGetErrorInfo failed (%u)", id);
[636]267 }
268 else {
269 if (!hwndOwner)
270 hwndOwner = HWND_DESKTOP;
[1891]271 // Find message offset in array of message offsets Assume 1 message - FIXME?
[636]272 pszOffset = ((PSZ) pErrInfoBlk) + pErrInfoBlk->offaoffszMsg;
[1673]273 // Address error message in array of messages and append error message to source code linenumber
[636]274 psz = pszBuf + strlen(pszBuf);
[904]275 sprintf(psz, " #0x%04x \"", ERRORIDERROR(pErrInfoBlk->idError));
[636]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
[787]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
[1891]311#ifdef PMPRINTF
312
[1698]313/**
[1891]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/**
[1698]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 */
[327]358
[551]359VOID Runtime_Error(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[327]360{
361 CHAR szMsg[4096];
362 va_list va;
363
364 // Format caller's message
[1398]365 if (!pszFmt)
366 pszFmt = PCSZ_NODATA;
[327]367 va_start(va, pszFmt);
[613]368 szMsg[sizeof(szMsg) - 1] = 0;
[327]369 vsprintf(szMsg, pszFmt, va);
370 va_end(va);
371
[613]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
[1395]377 if (strchr(szMsg, ' ') == NULL) {
378 strcat(szMsg, " ");
[1558]379 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]380 }
[327]381
382 sprintf(szMsg + strlen(szMsg),
[613]383 GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo);
[327]384
[1395]385 showMsg(MB_ICONEXCLAMATION, HWND_DESKTOP, GetPString(IDS_DEBUG_STRING), szMsg, TRUE);
[327]386
[636]387} // Runtime_Error
[327]388
[1698]389/**
390 * Format message and output using showMsg
391 * @note: Local errors written directly to stderr
392 */
[144]393
[551]394APIRET saymsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszFmt, ...)
[137]395{
[182]396 CHAR szMsg[4096];
[144]397 va_list va;
[137]398
[144]399 va_start(va, pszFmt);
[613]400 szMsg[sizeof(szMsg) - 1] = 0;
[182]401 vsprintf(szMsg, pszFmt, va);
[144]402 va_end(va);
403
[613]404 if (szMsg[sizeof(szMsg) - 1]) {
405 fprintf(stderr, "Buffer overflow in saymsg - need %u bytes\n", strlen(szMsg) + 1);
406 fflush(stderr);
407 }
[452]408
[613]409 return showMsg(mb_type, hwnd, pszTitle, szMsg, FALSE);
410
[636]411} // saymsg
[182]412
[1698]413/**
414 * Format message with custom buttons and output using showMsg
415 * Local errors written to stderr
416 */
417
[1718]418APIRET saymsg2(PCSZ pszButtonNames, int DefaultButton, HWND hwnd,
[1891]419 PCSZ pszTitle, PCSZ pszFmt, ...)
[1686]420{
[1698]421 ULONG i;
422 APIRET rc;
[1686]423 CHAR szMsg[4096];
424 va_list va;
[1698]425 MB2INFO *pmbInfo;
426 MB2D mb2dBut[4];
427 ULONG ulInfoSize = (sizeof(MB2INFO) + (sizeof(MB2D) * 3));
[1686]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);
[1718]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));
[1722]444 mb2dBut[0].idButton = SM2_YES;
445 mb2dBut[1].idButton = SM2_DONTASK;
446 mb2dBut[2].idButton = SM2_NO;
447 mb2dBut[3].idButton = SM2_CANCEL;
[1686]448 if (DefaultButton)
449 mb2dBut[DefaultButton - 1].flStyle = BS_DEFAULT;
450 pmbInfo = xmallocz(ulInfoSize, pszSrcFile, __LINE__);
451 if (pmbInfo) {
[1698]452 pmbInfo->cb = ulInfoSize;
[1686]453 pmbInfo->hIcon = 0;
454 pmbInfo->cButtons = 4;
[1722]455 pmbInfo->flStyle = MB_MOVEABLE | MB_ICONQUESTION ;
[1686]456 pmbInfo->hwndNotify = NULLHANDLE;
457 for (i = 0; i < 4; i++) {
458 memcpy( pmbInfo->mb2d+i , mb2dBut+i , sizeof(MB2D));
[1698]459 }
[1686]460 rc = WinMessageBox2(HWND_DESKTOP, hwnd,
[1722]461 szMsg, pszTitle, SM2_DIALOG,
[1891]462 pmbInfo);
[1722]463 WinSetFocus(HWND_DESKTOP, SM2_DIALOG);
[1686]464 free(pmbInfo);
465 return rc;
466 }
467 return MBID_ERROR;
468}
469
[1698]470/**
471 * Display message in popup message box
472 * Optionally writes formatted message to stderr
473 */
[182]474
[636]475static APIRET showMsg(ULONG mb_type, HWND hwndOwner,
476 PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog)
[182]477{
[613]478 if (wantLog) {
[258]479 fputs(pszMsg, stderr);
480 fputc('\n', stderr);
[614]481 fputc('\n', stderr);
[258]482 fflush(stderr);
483 }
484
[616]485 if (!hwndOwner)
486 hwndOwner = HWND_DESKTOP;
[1395]487 if (!fErrorBeepOff)
488 DosBeep(250, 100);
[144]489
[182]490 return WinMessageBox(HWND_DESKTOP, // Parent
[616]491 hwndOwner,
[551]492 (PSZ) pszMsg, (PSZ) pszTitle, 0, // help id
[182]493 mb_type | MB_MOVEABLE);
[613]494} // showMsg
[616]495
[1698]496/**
497 * Format Win...() error and output using showMsg
498 */
[616]499
[636]500VOID Win_Error(HWND hwndErr, HWND hwndOwner,
501 PCSZ pszSrcFile, UINT uSrcLineNo,
502 PCSZ pszFmt, ...)
[616]503{
[636]504 CHAR szMsg[4096];
[616]505 va_list va;
506
[636]507 // Format callers message
508 va_start(va, pszFmt);
[1718]509 formatWinError(szMsg, sizeof(szMsg), hwndErr, hwndOwner, pszSrcFile,
[1891]510 uSrcLineNo, pszFmt, va);
[636]511 va_end(va);
512
513 showMsg(MB_ENTER | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_GENERR2TEXT),
514 szMsg, TRUE);
515
516} // Win_Error
517
518/**
[1698]519 * Format PM error messsage and output to stderr
520 * This does the same reporting as Win_Error, but bypasses the
[636]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);
[1718]533 formatWinError(szMsg, sizeof(szMsg), hwndErr, hwndOwner, pszSrcFile, uSrcLineNo,
[1891]534 pszFmt, va);
[636]535 va_end(va);
536
537 fputs(szMsg, stderr);
[616]538 fputc('\n', stderr);
[636]539 fputc('\n', stderr);
[616]540 fflush(stderr);
[1395]541 if (!fErrorBeepOff)
542 DosBeep(250, 100);
[616]543
[636]544} // Win_Error_NoMsgBox
[787]545
[1718]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.