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
RevLine 
[121]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
[1718]9 Copyright (c) 2004, 2014 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
[121]42
43***********************************************************************/
44
[907]45#include <stdio.h>
46#include <string.h>
47#include <stdarg.h>
[1686]48#include <stdlib.h>
[907]49
[2]50#define INCL_DOS
[1373]51#define INCL_WIN
[2]52#define INCL_DOSERRORS
[1373]53#define INCL_DOSPROCESS // PPIB PTIB
[1395]54#define INCL_LONGLONG
[2]55
[907]56#include "errutil.h"
57#include "strutil.h" // GetPString
[2]58#include "fm3str.h"
[1558]59#include "notebook.h" // fErrorBeepOff
60#include "init.h" // Data declares
[1698]61#include "wrappers.h" // xmallocz
[2]62
63#pragma data_seg(DATA1)
64
[787]65static PSZ pszSrcFile = __FILE__;
66
[636]67static VOID formatWinError(PSZ pszBuf, UINT cBufBytes, HWND hwndErr, HWND hwndOwner,
68 PCSZ pszSrcFile, UINT uSrcLineNo,
69 PCSZ pszFmt, va_list pva);
70
[613]71static APIRET showMsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog);
[2]72
[1698]73/**
74 * Format debug message and output to stderr
75 * @note: Local errors also written to stderr
76 */
[144]77
[636]78VOID DbgMsg(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[121]79{
[1373]80 PIB *ppib;
81 TIB *ptib;
82 ULONG ultid;
83 APIRET apiret;
[144]84 va_list va;
[2]85
[787]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;
[1558]96 // ulLastMSec = msec; // Avoid big delta 1st time
[787]97 }
98
99 delta = msec - ul1stMSec;
100 // ulLastMSec = msec;
101 fprintf(stderr, "%03lu.%03lu ", delta / 1000, delta % 1000);
102
103#endif
104
[1373]105 apiret = DosGetInfoBlocks(&ptib, &ppib);
106 if (apiret)
107 ultid = 0;
108 else
109 ultid = ptib->tib_ptib2->tib2_ultid;
110
[636]111 // OK for source file name to be null
[1373]112 fprintf(stderr, "%s %u (%lu)", pszSrcFile ? pszSrcFile : "n/a", uSrcLineNo, ultid);
[636]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);
[613]119 }
[636]120 fputc('\n', stderr);
121 fflush(stderr);
[613]122
[636]123} // DbgMsg
[327]124
[1698]125/**
126 * Format Dos...() error and output using showMsg
127 * @note: Local errors written directly to stderr
128 */
[144]129
[1558]130// 2010-12-01 SHL fixme for ULONG to be APIRET
131
132INT Dos_Error(ULONG mb_type, ULONG apiret, HWND hwndOwner,
[636]133 PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[121]134{
[182]135 CHAR szMsg[4096];
[1450]136 CHAR szMsgFile[20], szMsgFileH[20];
[636]137 ULONG Class; // Error class
138 ULONG action; // Error action
[1558]139 ULONG Locus; // Error location
[144]140 ULONG ulMsgLen;
[1558]141 APIRET mapped_apiret;
[144]142 CHAR *pszMsgStart;
143 CHAR *psz;
144 va_list va;
[2]145
[1558]146 if (!apiret)
[144]147 return MBID_ENTER; // Should not have been called
[121]148
[144]149 // Format caller's message
150 va_start(va, pszFmt);
[613]151 szMsg[sizeof(szMsg) - 1] = 0;
[182]152 vsprintf(szMsg, pszFmt, va);
[144]153 va_end(va);
[121]154
[613]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
[1395]160 if (strchr(szMsg, ' ') == NULL) {
161 strcat(szMsg, " ");
[1558]162 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]163 }
[327]164
[1558]165 DosErrClass(apiret, &Class, &action, &Locus);
[144]166
[182]167 sprintf(szMsg + strlen(szMsg),
[144]168 GetPString(IDS_DOSERR1TEXT),
[636]169 pszSrcFile,
170 uSrcLineNo,
[1558]171 apiret,
[144]172 GetPString(IDS_ERRCLASS1TEXT + (Class - 1)),
173 GetPString(IDS_ERRACTION1TEXT + (action - 1)),
[1558]174 GetPString(IDS_ERRLOCUS1TEXT + (Locus - 1)));
[182]175 pszMsgStart = szMsg + strlen(szMsg) + 1;
[1449]176 strcpy(szMsgFile, "OSO001.MSG");
177 strcpy(szMsgFileH, "OSO001H.MSG");
[144]178 // Get message leaving space for NL separator
[1558]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)) {
[144]190 // Got message
191 pszMsgStart[ulMsgLen + 1] = 0; // Terminate
[182]192 *(pszMsgStart - 1) = '\n'; // Stuff NL before message text
[144]193 *pszMsgStart = '\"'; // Prefix message text with quote
[182]194
[144]195 psz = pszMsgStart + ulMsgLen; // Point at last char
196 // Chop trailing NL CR TAB
197 while (*psz &&
[551]198 (*psz == '\r' || *psz == '\n' || *psz == ' ' || *psz == '\t')) {
[144]199 *psz-- = 0;
200 }
201 strcat(psz, "\""); // Append trailing quote
202
203 // Convert CR and NL combos to single space
204 psz = pszMsgStart;
[551]205 while (*psz) {
206 if (*psz == '\n' || *psz == '\r') {
[144]207 while (*(psz + 1) == '\n' || *(psz + 1) == '\r')
208 memmove(psz, psz + 1, strlen(psz));
209 *psz = ' ';
[2]210 }
[144]211 else
212 psz++;
[2]213 }
214 }
[137]215
[613]216 return showMsg(mb_type | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_DOSERR2TEXT),
217 szMsg, TRUE);
[137]218
[636]219} // Dos_Error
[137]220
[636]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{
[1673]230 PERRINFO pErrInfoBlk; // Pointer to ERRINFO structure filled by WinGetErrorInfo
231 PSZ pszOffset; // Pointer to current error message returned by WinGetErrorInfo
[636]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]) {
[1718]245 fprintf(stderr, "Buffer overflow in formatWinError - need %u bytes\n",
246 strlen(pszBuf) + 1);
[636]247 fflush(stderr);
248 }
249
[1395]250 if (strchr(pszBuf, ' ') == NULL) {
251 strcat(pszBuf, " ");
[1558]252 strcat(pszBuf, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]253 }
[636]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) {
[907]262 ERRORID id = WinGetLastError(hab);
[636]263 psz = pszBuf + strlen(pszBuf);
[904]264 sprintf(psz, " WinGetErrorInfo failed (%u)", id);
[636]265 }
266 else {
267 if (!hwndOwner)
268 hwndOwner = HWND_DESKTOP;
[1673]269 // Find message offset in array of message offsets Assume 1 message - fixme?
[636]270 pszOffset = ((PSZ) pErrInfoBlk) + pErrInfoBlk->offaoffszMsg;
[1673]271 // Address error message in array of messages and append error message to source code linenumber
[636]272 psz = pszBuf + strlen(pszBuf);
[904]273 sprintf(psz, " #0x%04x \"", ERRORIDERROR(pErrInfoBlk->idError));
[636]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
[787]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
[1698]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 */
[327]314
[551]315VOID Runtime_Error(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[327]316{
317 CHAR szMsg[4096];
318 va_list va;
319
320 // Format caller's message
[1398]321 if (!pszFmt)
322 pszFmt = PCSZ_NODATA;
[327]323 va_start(va, pszFmt);
[613]324 szMsg[sizeof(szMsg) - 1] = 0;
[327]325 vsprintf(szMsg, pszFmt, va);
326 va_end(va);
327
[613]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
[1395]333 if (strchr(szMsg, ' ') == NULL) {
334 strcat(szMsg, " ");
[1558]335 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]336 }
[327]337
338 sprintf(szMsg + strlen(szMsg),
[613]339 GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo);
[327]340
[1395]341 showMsg(MB_ICONEXCLAMATION, HWND_DESKTOP, GetPString(IDS_DEBUG_STRING), szMsg, TRUE);
[327]342
[636]343} // Runtime_Error
[327]344
[1698]345/**
346 * Format message and output using showMsg
347 * @note: Local errors written directly to stderr
348 */
[144]349
[551]350APIRET saymsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszFmt, ...)
[137]351{
[182]352 CHAR szMsg[4096];
[144]353 va_list va;
[137]354
[144]355 va_start(va, pszFmt);
[613]356 szMsg[sizeof(szMsg) - 1] = 0;
[182]357 vsprintf(szMsg, pszFmt, va);
[144]358 va_end(va);
359
[613]360 if (szMsg[sizeof(szMsg) - 1]) {
361 fprintf(stderr, "Buffer overflow in saymsg - need %u bytes\n", strlen(szMsg) + 1);
362 fflush(stderr);
363 }
[452]364
[613]365 return showMsg(mb_type, hwnd, pszTitle, szMsg, FALSE);
366
[636]367} // saymsg
[182]368
[1698]369/**
370 * Format message with custom buttons and output using showMsg
371 * Local errors written to stderr
372 */
373
[1718]374APIRET saymsg2(PCSZ pszButtonNames, int DefaultButton, HWND hwnd,
375 PCSZ pszTitle, PCSZ pszFmt, ...)
[1686]376{
[1698]377 ULONG i;
378 APIRET rc;
[1686]379 CHAR szMsg[4096];
380 va_list va;
[1698]381 MB2INFO *pmbInfo;
382 MB2D mb2dBut[4];
383 ULONG ulInfoSize = (sizeof(MB2INFO) + (sizeof(MB2D) * 3));
[1686]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);
[1718]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));
[1686]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) {
[1698]408 pmbInfo->cb = ulInfoSize;
[1686]409 pmbInfo->hIcon = 0;
410 pmbInfo->cButtons = 4;
[1718]411 pmbInfo->flStyle = MB_MOVEABLE | MB_SYSTEMMODAL | MB_ICONQUESTION ;
[1686]412 pmbInfo->hwndNotify = NULLHANDLE;
413 for (i = 0; i < 4; i++) {
414 memcpy( pmbInfo->mb2d+i , mb2dBut+i , sizeof(MB2D));
[1698]415 }
[1686]416 rc = WinMessageBox2(HWND_DESKTOP, hwnd,
[1698]417 szMsg, pszTitle, 1234,
[1718]418 pmbInfo);
[1686]419 free(pmbInfo);
420 return rc;
421 }
422 return MBID_ERROR;
423}
424
[1698]425/**
426 * Display message in popup message box
427 * Optionally writes formatted message to stderr
428 */
[182]429
[636]430static APIRET showMsg(ULONG mb_type, HWND hwndOwner,
431 PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog)
[182]432{
[613]433 if (wantLog) {
[258]434 fputs(pszMsg, stderr);
435 fputc('\n', stderr);
[614]436 fputc('\n', stderr);
[258]437 fflush(stderr);
438 }
439
[616]440 if (!hwndOwner)
441 hwndOwner = HWND_DESKTOP;
[1395]442 if (!fErrorBeepOff)
443 DosBeep(250, 100);
[144]444
[182]445 return WinMessageBox(HWND_DESKTOP, // Parent
[616]446 hwndOwner,
[551]447 (PSZ) pszMsg, (PSZ) pszTitle, 0, // help id
[182]448 mb_type | MB_MOVEABLE);
[613]449} // showMsg
[616]450
[1698]451/**
452 * Format Win...() error and output using showMsg
453 */
[616]454
[636]455VOID Win_Error(HWND hwndErr, HWND hwndOwner,
456 PCSZ pszSrcFile, UINT uSrcLineNo,
457 PCSZ pszFmt, ...)
[616]458{
[636]459 CHAR szMsg[4096];
[616]460 va_list va;
461
[636]462 // Format callers message
463 va_start(va, pszFmt);
[1718]464 formatWinError(szMsg, sizeof(szMsg), hwndErr, hwndOwner, pszSrcFile,
465 uSrcLineNo, pszFmt, va);
[636]466 va_end(va);
467
468 showMsg(MB_ENTER | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_GENERR2TEXT),
469 szMsg, TRUE);
470
471} // Win_Error
472
473/**
[1698]474 * Format PM error messsage and output to stderr
475 * This does the same reporting as Win_Error, but bypasses the
[636]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);
[1718]488 formatWinError(szMsg, sizeof(szMsg), hwndErr, hwndOwner, pszSrcFile, uSrcLineNo,
489 pszFmt, va);
[636]490 va_end(va);
491
492 fputs(szMsg, stderr);
[616]493 fputc('\n', stderr);
[636]494 fputc('\n', stderr);
[616]495 fflush(stderr);
[1395]496 if (!fErrorBeepOff)
497 DosBeep(250, 100);
[616]498
[636]499} // Win_Error_NoMsgBox
[787]500
[1718]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.