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