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
RevLine 
[121]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
[1840]9 Copyright (c) 2004, 2015 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.
[1840]44 09 Nov 13 SHL Use GetTidForThread in DbgMsg and tweak for for editors that understand file:line
[121]45
46***********************************************************************/
47
[907]48#include <stdio.h>
49#include <string.h>
50#include <stdarg.h>
[1686]51#include <stdlib.h>
[907]52
[2]53#define INCL_DOS
[1373]54#define INCL_WIN
[2]55#define INCL_DOSERRORS
[1373]56#define INCL_DOSPROCESS // PPIB PTIB
[1395]57#define INCL_LONGLONG
[2]58
[907]59#include "errutil.h"
60#include "strutil.h" // GetPString
[2]61#include "fm3str.h"
[1558]62#include "notebook.h" // fErrorBeepOff
63#include "init.h" // Data declares
[1840]64#include "misc.h" // GetTidForThread
[1698]65#include "wrappers.h" // xmallocz
[1722]66#include "fm3dll2.h"
[2]67
68#pragma data_seg(DATA1)
69
[787]70static PSZ pszSrcFile = __FILE__;
71
[636]72static VOID formatWinError(PSZ pszBuf, UINT cBufBytes, HWND hwndErr, HWND hwndOwner,
73 PCSZ pszSrcFile, UINT uSrcLineNo,
74 PCSZ pszFmt, va_list pva);
75
[613]76static APIRET showMsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszMsg, BOOL wantLog);
[2]77
[1698]78/**
79 * Format debug message and output to stderr
80 * @note: Local errors also written to stderr
81 */
[144]82
[636]83VOID DbgMsg(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[121]84{
[1373]85 ULONG ultid;
[144]86 va_list va;
[2]87
[787]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;
[1558]98 // ulLastMSec = msec; // Avoid big delta 1st time
[787]99 }
100
101 delta = msec - ul1stMSec;
102 // ulLastMSec = msec;
103 fprintf(stderr, "%03lu.%03lu ", delta / 1000, delta % 1000);
104
105#endif
106
[1840]107 ultid = GetTidForThread();
[1373]108
[636]109 // OK for source file name to be null
[1840]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);
[636]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);
[613]118 }
[636]119 fputc('\n', stderr);
120 fflush(stderr);
[613]121
[636]122} // DbgMsg
[327]123
[1698]124/**
125 * Format Dos...() error and output using showMsg
126 * @note: Local errors written directly to stderr
127 */
[144]128
[1558]129// 2010-12-01 SHL fixme for ULONG to be APIRET
130
131INT Dos_Error(ULONG mb_type, ULONG apiret, HWND hwndOwner,
[636]132 PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[121]133{
[182]134 CHAR szMsg[4096];
[1450]135 CHAR szMsgFile[20], szMsgFileH[20];
[636]136 ULONG Class; // Error class
137 ULONG action; // Error action
[1558]138 ULONG Locus; // Error location
[144]139 ULONG ulMsgLen;
[1558]140 APIRET mapped_apiret;
[144]141 CHAR *pszMsgStart;
142 CHAR *psz;
143 va_list va;
[2]144
[1558]145 if (!apiret)
[144]146 return MBID_ENTER; // Should not have been called
[121]147
[144]148 // Format caller's message
149 va_start(va, pszFmt);
[613]150 szMsg[sizeof(szMsg) - 1] = 0;
[182]151 vsprintf(szMsg, pszFmt, va);
[144]152 va_end(va);
[121]153
[613]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
[1395]159 if (strchr(szMsg, ' ') == NULL) {
160 strcat(szMsg, " ");
[1558]161 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]162 }
[327]163
[1558]164 DosErrClass(apiret, &Class, &action, &Locus);
[144]165
[182]166 sprintf(szMsg + strlen(szMsg),
[144]167 GetPString(IDS_DOSERR1TEXT),
[636]168 pszSrcFile,
169 uSrcLineNo,
[1558]170 apiret,
[144]171 GetPString(IDS_ERRCLASS1TEXT + (Class - 1)),
172 GetPString(IDS_ERRACTION1TEXT + (action - 1)),
[1558]173 GetPString(IDS_ERRLOCUS1TEXT + (Locus - 1)));
[182]174 pszMsgStart = szMsg + strlen(szMsg) + 1;
[1449]175 strcpy(szMsgFile, "OSO001.MSG");
176 strcpy(szMsgFileH, "OSO001H.MSG");
[144]177 // Get message leaving space for NL separator
[1558]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)) {
[144]189 // Got message
190 pszMsgStart[ulMsgLen + 1] = 0; // Terminate
[182]191 *(pszMsgStart - 1) = '\n'; // Stuff NL before message text
[144]192 *pszMsgStart = '\"'; // Prefix message text with quote
[182]193
[144]194 psz = pszMsgStart + ulMsgLen; // Point at last char
195 // Chop trailing NL CR TAB
196 while (*psz &&
[551]197 (*psz == '\r' || *psz == '\n' || *psz == ' ' || *psz == '\t')) {
[144]198 *psz-- = 0;
199 }
200 strcat(psz, "\""); // Append trailing quote
201
202 // Convert CR and NL combos to single space
203 psz = pszMsgStart;
[551]204 while (*psz) {
205 if (*psz == '\n' || *psz == '\r') {
[144]206 while (*(psz + 1) == '\n' || *(psz + 1) == '\r')
207 memmove(psz, psz + 1, strlen(psz));
208 *psz = ' ';
[2]209 }
[144]210 else
211 psz++;
[2]212 }
213 }
[137]214
[613]215 return showMsg(mb_type | MB_ICONEXCLAMATION, hwndOwner, GetPString(IDS_DOSERR2TEXT),
216 szMsg, TRUE);
[137]217
[636]218} // Dos_Error
[137]219
[636]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{
[1673]229 PERRINFO pErrInfoBlk; // Pointer to ERRINFO structure filled by WinGetErrorInfo
230 PSZ pszOffset; // Pointer to current error message returned by WinGetErrorInfo
[636]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]) {
[1718]244 fprintf(stderr, "Buffer overflow in formatWinError - need %u bytes\n",
245 strlen(pszBuf) + 1);
[636]246 fflush(stderr);
247 }
248
[1395]249 if (strchr(pszBuf, ' ') == NULL) {
250 strcat(pszBuf, " ");
[1558]251 strcat(pszBuf, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]252 }
[636]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) {
[907]261 ERRORID id = WinGetLastError(hab);
[636]262 psz = pszBuf + strlen(pszBuf);
[904]263 sprintf(psz, " WinGetErrorInfo failed (%u)", id);
[636]264 }
265 else {
266 if (!hwndOwner)
267 hwndOwner = HWND_DESKTOP;
[1673]268 // Find message offset in array of message offsets Assume 1 message - fixme?
[636]269 pszOffset = ((PSZ) pErrInfoBlk) + pErrInfoBlk->offaoffszMsg;
[1673]270 // Address error message in array of messages and append error message to source code linenumber
[636]271 psz = pszBuf + strlen(pszBuf);
[904]272 sprintf(psz, " #0x%04x \"", ERRORIDERROR(pErrInfoBlk->idError));
[636]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
[787]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
[1698]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 */
[327]313
[551]314VOID Runtime_Error(PCSZ pszSrcFile, UINT uSrcLineNo, PCSZ pszFmt, ...)
[327]315{
316 CHAR szMsg[4096];
317 va_list va;
318
319 // Format caller's message
[1398]320 if (!pszFmt)
321 pszFmt = PCSZ_NODATA;
[327]322 va_start(va, pszFmt);
[613]323 szMsg[sizeof(szMsg) - 1] = 0;
[327]324 vsprintf(szMsg, pszFmt, va);
325 va_end(va);
326
[613]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
[1395]332 if (strchr(szMsg, ' ') == NULL) {
333 strcat(szMsg, " ");
[1558]334 strcat(szMsg, GetPString(IDS_FAILEDTEXT)); // Assume simple function name
[1395]335 }
[327]336
337 sprintf(szMsg + strlen(szMsg),
[613]338 GetPString(IDS_GENERR1TEXT), pszSrcFile, uSrcLineNo);
[327]339
[1395]340 showMsg(MB_ICONEXCLAMATION, HWND_DESKTOP, GetPString(IDS_DEBUG_STRING), szMsg, TRUE);
[327]341
[636]342} // Runtime_Error
[327]343
[1698]344/**
345 * Format message and output using showMsg
346 * @note: Local errors written directly to stderr
347 */
[144]348
[551]349APIRET saymsg(ULONG mb_type, HWND hwnd, PCSZ pszTitle, PCSZ pszFmt, ...)
[137]350{
[182]351 CHAR szMsg[4096];
[144]352 va_list va;
[137]353
[144]354 va_start(va, pszFmt);
[613]355 szMsg[sizeof(szMsg) - 1] = 0;
[182]356 vsprintf(szMsg, pszFmt, va);
[144]357 va_end(va);
358
[613]359 if (szMsg[sizeof(szMsg) - 1]) {
360 fprintf(stderr, "Buffer overflow in saymsg - need %u bytes\n", strlen(szMsg) + 1);
361 fflush(stderr);
362 }
[452]363
[613]364 return showMsg(mb_type, hwnd, pszTitle, szMsg, FALSE);
365
[636]366} // saymsg
[182]367
[1698]368/**
369 * Format message with custom buttons and output using showMsg
370 * Local errors written to stderr
371 */
372
[1718]373APIRET saymsg2(PCSZ pszButtonNames, int DefaultButton, HWND hwnd,
374 PCSZ pszTitle, PCSZ pszFmt, ...)
[1686]375{
[1698]376 ULONG i;
377 APIRET rc;
[1686]378 CHAR szMsg[4096];
379 va_list va;
[1698]380 MB2INFO *pmbInfo;
381 MB2D mb2dBut[4];
382 ULONG ulInfoSize = (sizeof(MB2INFO) + (sizeof(MB2D) * 3));
[1686]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);
[1718]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));
[1722]399 mb2dBut[0].idButton = SM2_YES;
400 mb2dBut[1].idButton = SM2_DONTASK;
401 mb2dBut[2].idButton = SM2_NO;
402 mb2dBut[3].idButton = SM2_CANCEL;
[1686]403 if (DefaultButton)
404 mb2dBut[DefaultButton - 1].flStyle = BS_DEFAULT;
405 pmbInfo = xmallocz(ulInfoSize, pszSrcFile, __LINE__);
406 if (pmbInfo) {
[1698]407 pmbInfo->cb = ulInfoSize;
[1686]408 pmbInfo->hIcon = 0;
409 pmbInfo->cButtons = 4;
[1722]410 pmbInfo->flStyle = MB_MOVEABLE | MB_ICONQUESTION ;
[1686]411 pmbInfo->hwndNotify = NULLHANDLE;
412 for (i = 0; i < 4; i++) {
413 memcpy( pmbInfo->mb2d+i , mb2dBut+i , sizeof(MB2D));
[1698]414 }
[1686]415 rc = WinMessageBox2(HWND_DESKTOP, hwnd,
[1722]416 szMsg, pszTitle, SM2_DIALOG,
[1718]417 pmbInfo);
[1722]418 WinSetFocus(HWND_DESKTOP, SM2_DIALOG);
[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.