1 /*   ncbierr.c
2 * ===========================================================================
3 *
4 *                            PUBLIC DOMAIN NOTICE
5 *               National Center for Biotechnology Information
6 *
7 *  This software/database is a "United States Government Work" under the
8 *  terms of the United States Copyright Act.  It was written as part of
9 *  the author's official duties as a United States Government employee and
10 *  thus cannot be copyrighted.  This software/database is freely available
11 *  to the public for use. The National Library of Medicine and the U.S.
12 *  Government have not placed any restriction on its use or reproduction.
13 *
14 *  Although all reasonable efforts have been taken to ensure the accuracy
15 *  and reliability of the software and data, the NLM and the U.S.
16 *  Government do not and cannot warrant the performance or results that
17 *  may be obtained by using this software or data. The NLM and the U.S.
18 *  Government disclaim all warranties, express or implied, including
19 *  warranties of performance, merchantability or fitness for any particular
20 *  purpose.
21 *
22 *  Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * $Id: ncbierr.c,v 6.26 2009/08/14 18:01:09 lavr Exp $
27 *
28 * $Revision: 6.26 $
29 *
30 * Authors:  Schuler, Sirotkin (UserErr stuff)
31 *
32 * Version Creation Date:   9-19-91
33 *
34 * File Description:  Error handling functions
35 *
36 * Modifications:
37 * --------------------------------------------------------------------------
38 * Date     Name        Description of modification
39 * -------  ----------  -----------------------------------------------------
40 * 04-13-93 Schuler     Added TRACE, VERIFY, ASSERT macros.
41 * 04-13-93 Schuler     Added AbnormalExit function.
42 * 05-11-93 Schuler     Added ErrSetHandler function.
43 * 05-21-93 Schuler     Remove PROTO from ErrSetHandler implementation (oops)
44 * 05-26-93 Schuler     Nlm_TraceStr flushes stderr, if appropriate
45 * 07-26-93 Schuler     Moved globals into a (per-app) context struct
46 * 07-26-93 Schuler     Fixed ErrClear()
47 * 11-15-93 Schuler     Fixed double error reporting problem in ErrPost()
48 * 12-13-93 Schuler     Message file support and lots of other changes.
49 * 12-22-93 Schuler     Added log_level setting (min. severity to log)
50 * 01-03-94 Schuler     Added ErrSaveOptions and ErrRestoreOptions
51 * 01-04-94 Schuler     Added code to get settings from NCBI config file
52 * 01-07-94 Schuler     Fixed bug in ErrMsgRoot_Read().
53 * 01-10-94 Schuler     Added check for not_avail in ErrMsgRoot_fopen()
54 * 01-10-94 Schuler     Fixed bug in ErrGetMsgRoot()
55 * 01-13-94 Schuler     Added an handful of typecasts to supress warnings
56 * 01-23-94 Schuler     Fixed bug in ErrSetOpts
57 * 02-02-94 Schuler     ErrOpt structure has fields for actopt, logopt
58 * 02-02-94 Schuler     Revisions related to change in message file format
59 * 02-02-94 Schuler     Use TEST_BITS macro throughout
60 * 02-02-94 Schuler     Improved back.compatability of ErrGetOpts/ErrSetOpts
61 * 02-10-94 Schuler     Workaround for obsolete ERR_IGNORE option
62 * 02-10-94 Schuler     Fixed bug in ErrSetFatalLevel
63 * 02-18-94 Schuler     Fix to deal with possibility of userstrings being NULL
64 * 03-23-94 Schuler     Fix memory leak in ErrGetExplanation
65 * 04-19-94 Schuler     Keep error log file open all the time
66 * 08-23-94 Schuler     ErrLogStr, ErrLogPrintStr modified
67 * 08-09-94 Schuler     Dont try to open message file if FileLength ==0
68 * 11-15-94 Schuler     Tweaks to ErrPostStr to avoid acc errors
69 * 11-18-94 Schuler     Changed return type of Get/SetXXXLevel() family
70 *                      of functions from int to ErrSev to eliminate warnings
71 * 03-06-95 Schuler     Fixed problem with ErrMsgRoot_fopen
72 *
73 * $Log: ncbierr.c,v $
74 * Revision 6.26  2009/08/14 18:01:09  lavr
75 * Use {Get|Set}ProgramName()
76 *
77 * Revision 6.25  2008/01/18 17:09:13  lavr
78 * Use SEV_MIN (instead of explicit SEV_INFO) in level setting routines
79 *
80 * Revision 6.24  2006/11/09 17:46:58  kans
81 * added cast to quiet CodeWarrior complaint
82 *
83 * Revision 6.23  2006/07/13 17:10:35  bollin
84 * use Uint4 instead of Uint2 for itemID values
85 *
86 * Revision 6.22  2005/04/06 19:34:13  lavr
87 * Reassign severity (if found in message file) in case of C++ Tkit hook
88 *
89 * Revision 6.21  2003/03/25 14:56:08  ivanov
90 * Nlm_AbnormalExitPure(): FatalAppExit() call changed to exit() under MS Windows (by Howard Feldman feldman@mshri.on.ca)
91 *
92 * Revision 6.20  2002/10/03 16:22:03  kans
93 * changed fgets to Nlm_FileGets
94 *
95 * Revision 6.19  2002/08/13 20:44:19  lavr
96 * Force logging to stderr/stdout if DEBUG_PRINTOUT env.var. is set as follows:
97 * STDOUT (case-insensitively) - log to stdout; anything else - log to stderr.
98 * By Denis Vakatov and Anton Lavrentiev.
99 *
100 * Revision 6.18  2002/07/11 20:26:03  lavr
101 * Prevent possible buffer overruns in storing text parts of a message
102 *
103 * Revision 6.17  2001/02/08 22:27:04  vakatov
104 * + Nlm_CallErrHandlerOnly() -- to allow processing of posted
105 * err.messages by the hook only.
106 *
107 * Revision 6.16  2000/06/14 22:27:52  vakatov
108 * Nlm_AbnormalExit() -- print message only if called for the first time
109 *
110 * Revision 6.15  1999/10/01 14:41:39  kans
111 * added SEV_REJECT between SEV_ERROR and SEV_FATAL
112 *
113 * Revision 6.14  1999/08/23 19:14:34  vakatov
114 * Let SEV_MAX not be an exception among the "SEV_*"
115 *
116 * Revision 6.13  1998/12/29 06:16:21  vakatov
117 * Nlm_ErrPostStr() -- Check for the severity code validity
118 *
119 * Revision 6.12  1998/10/22 16:52:40  shavirin
120 * Added function *Nlm_GetErrLongText able to retrieve long error messages
121 *
122 * Revision 6.11  1998/09/29 16:20:53  vakatov
123 * Nlm_AssertionFailed():  printout the module name and file/line in Message
124 *
125 * Revision 6.10  1998/08/24 17:42:00  kans
126 * fixed old style function definition warnings
127 *
128 * Revision 6.9  1998/06/11 21:02:23  shavirin
129 * Fixed few warnings
130 *
131 * Revision 6.8  1998/05/04 23:25:46  vakatov
132 * Raised the message posting level to SEV_ERROR(for non-_DEBUG only)
133 *
134 * Revision 6.7  1998/02/24 19:06:19  vakatov
135 * [DEBUG_HARD] Nlm_ErrShow(): init. "*message"(by garyt@timelogic.com)
136 *
137 * Revision 6.6  1998/02/17 20:57:45  vakatov
138 * Fixed delete_ErrNode() and started to use it; use Nlm_ErrUserClear()
139 * too(got rid of mem.leaks) // in cooperation with S.Bazhin.
140 *
141 * Revision 6.5  1998/02/13 15:22:40  vakatov
142 * ReleaseAppErrInfo():  dont forget to destroy list of error message;
143 * +fixed a minor mem.leak(free _szSeverity[SEV_MAX])
144 *
145 * Revision 6.4  1997/12/04 22:00:15  vakatov
146 * Nlm_ErrPostEx():  dont forget to zero "info->busy" flag when do nothing
147 *
148 * Revision 6.3  1997/11/26 21:26:14  vakatov
149 * Fixed errors and warnings issued by C and C++ (GNU and Sun) compilers
150 *
151 * Revision 6.2  1997/11/04 19:08:25  ostell
152 * added ErrGetUserStrings() to eliminate direct access to ErrDesc.userstr
153 * so this could become a thread specific chain
154 *
155 * Revision 6.1  1997/08/27 20:40:23  kans
156 * added ErrPathReset
157 *
158 * Revision 6.0  1997/08/25 18:15:24  madden
159 * Revision changed to 6.0
160 *
161 * Revision 5.12  1997/07/22 19:11:22  vakatov
162 * Separated Main() from GetArg[svc]() functions;  [WIN_MSWIN] converged
163 * console and GUI libraries; [for WIN32-DLL] encapsulated global variables
164 *
165 * Revision 5.11  1997/03/31 18:52:11  vakatov
166 * Minor fix to pass through the C++ compiler
167 *
168  * Revision 5.10  1997/03/17  21:51:01  vakatov
169  * Nlm_ErrSetLogfile():  put the logfile banner as a monolith string
170  *
171  * Revision 5.9  1997/01/29  16:52:48  vakatov
172  * Nlm_ErrSetLogfile():  fixed an unsafe StrNCpy() use(might produce
173  * a non-'\0'-terminated string)
174  *
175  * Revision 5.8  1996/12/17  05:49:04  vakatov
176  * Got rid of some ugly warnings (JK)
177  *
178  * Revision 5.7  1996/12/16  22:37:54  vakatov
179  * Added more protection from recursive error posing function calls. Rolled
180  * back the changes made in "* Revision 5.6  1996/12/11  23:01:48  vakatov"
181  *
182  * Revision 5.6  1996/12/11  23:01:48  vakatov
183  * ErrMsgRoot_fopen(): Suppressed error posting when opening "*.msg"
184  * (often non-existing) auxiliary files.
185  *
186  * Revision 5.5  1996/12/03  21:48:33  vakatov
187  * Adopted for 32-bit MS-Windows DLLs
188  *
189  * Revision 5.4  1996/11/25  19:03:58  vakatov
190  * Moved 'eo_save'(formerly MT-unsafe 'static _eo_save') and
191  * 'busy'(formerly MT-unsafe 'static _busy') to the 'AppErrInfo' structure.
192  * Added Nlm_ErrGetLogfile() and Nlm_AbnormalExitPure();  the latter
193  * does nothing but exit without any error posting, etc.
194  * "MT-safed" error context initialization and posting functions.
195  *
196  * Revision 5.3  1996/08/12  18:40:31  vakatov
197  * Nlm_ErrPostEx(), Nlm_ErrPostEx() -- prevented from crashing when an
198  * empty formatting string specified(<fmt> === "")
199  *
200  * Revision 5.2  1996/08/07  19:24:31  vakatov
201  * [_DEBUG]  Nlm_AbnormalExit():  "exit()" replaced by "abort()"
202  *
203  * Revision 5.1  1996/07/16  19:56:37  vakatov
204  * VSPRINTF macros has been replaced by the TSPRINTF macros in the
205  * code responsible for the error message composing -- in order to
206  * avoid the memory limits overrun when printing the error messages
207  * (by the means of function "vsprintf()") to a memory-located string.
208  * The application name is added as the error message caption.
209  * Added ReleaseAppErrInfo() function to clean dynamically allocated
210  * memory used by the error posing routines;  other memory-leak-
211  * resulting flaws fixed; and eliminated multiple(duplicated) reading
212  * of the parameter file.
213  *
214  * Revision 5.0  1996/05/28  13:18:57  ostell
215  * Set to revision 5.0
216  *
217  * Revision 4.0  1995/07/26  13:46:50  ostell
218  * force revision to 4.0
219  *
220  * Revision 2.50  1995/06/27  16:21:45  kans
221  * protect ErrPostEx against single strings > ERRTEXT_MAX in length
222  *
223  * Revision 2.49  1995/05/15  18:45:58  ostell
224  * added Log line
225  *
226 *
227 *
228 * ==========================================================================
229 */
230 
231 
232 static char *_filename = __FILE__;
233 #define THIS_FILE _filename
234 
235 #include "corepriv.h"
236 #include <tsprintf.h>
237 
238 char * g_corelib = "CoreLib";
239 
240 struct AppErrInfo
241 {
242 	ErrDesc desc;
243 	ErrOpts opts;
244 	ErrHookProc	hook;
245 	unsigned long ini_mask;
246 	unsigned long ini_bits;
247 	unsigned int any_error :1;
248 	unsigned int err_formatted :1;
249 	unsigned int debug_mode :1;
250 	size_t ustrcnt;
251 	size_t ustrlen;
252 	FILE *fdLog;
253 	char  logfile[PATH_MAX];
254 	char  msgpath[PATH_MAX];
255 	ErrMsgRoot *idxlist;
256 	ErrMsgNode *node;
257         ErrOpts _eo_save[2];
258 #define AEI_BUSY_1  0x1
259 #define AEI_BUSY_2  0x2
260 #define AEI_BUSY_3  0x4
261         int busy;
262 };
263 
264 
265 typedef struct AppErrInfo *AppErrInfoPtr;
266 
267 
268 static char * _szPropKey = "_AppErrInfo";
269 
270 static char * _szSevKey [SEV_MAX + 1] = {
271   "", "SEV_INFO", "SEV_WARNING", "SEV_ERROR", "SEV_REJECT", "SEV_FATAL", "SEV_MAX"};
272 
273 static char * _szSevDef [SEV_MAX + 1] = {
274   "", "NOTE:", "WARNING:", "ERROR:", "REJECT:", "FATAL ERROR:", "FATAL ERROR:" };
275 
276 static char * _szSeverity[SEV_MAX + 1];
277 
278 static AppErrInfoPtr GetAppErrInfo PROTO((void));
279 
280 #define FUSE_BITS(inf)  ( ((inf)->ini_bits & (inf)->ini_mask) | ((inf)->opts.flags & ~((inf)->ini_mask)) )
281 #define TEST_BITS(inf,x)  (FUSE_BITS(inf) & (x))
282 
283 
284 static Nlm_Boolean s_HookOnly = FALSE;
285 
286 
287 
288 /***************************************************************************\
289 |                           POSTING AN ERROR                                |
290 \***************************************************************************/
291 
die_if_necessary(ErrSev sev,AppErrInfoPtr info)292 static void die_if_necessary(ErrSev sev, AppErrInfoPtr info)
293 {
294   if (sev >= info->opts.die_level)
295     Nlm_AbnormalExitPure( info->desc.errcode );
296 }
297 
298 
299 /*-------------------------------------------------------------------------
300 * ErrPost   [Schuler]
301 *
302 * MODIFICATIONS:
303 * 04-13-93 Schuler
304 * 07-26-93 Schuler   Modified to use AppErrInfo struct
305 * 11-15-93 Schuler   Fixed bug that resulted in reporting errors twice
306 * 12-14-93 Schuler   Modified to call the new ErrPostStr function
307 * 01-14-94 Schuler   Check _busy flag
308 */
309 
310 #ifdef VAR_ARGS
Nlm_ErrPost(context,errcode,fmt,va_alist)311 NLM_EXTERN void CDECL  Nlm_ErrPost (context, errcode, fmt, va_alist)
312   int context;
313   int errcode;
314   const char *fmt;
315   va_dcl
316 #else
317 NLM_EXTERN void CDECL Nlm_ErrPost (int context, int errcode, const char *fmt, ...)
318 #endif
319 {
320   AppErrInfoPtr info = GetAppErrInfo();
321   ErrSev sev = (context==CTX_DEBUG) ? SEV_INFO : SEV_FATAL;
322 
323   if ( info->busy )
324     {
325       die_if_necessary(sev, info);
326       return;
327     }
328   info->busy = AEI_BUSY_1;
329 
330   info->desc.context = (short)context;
331 
332   {{
333     const Nlm_Char PNTR scratch_str = NULL;
334     TSPRINTF(scratch_str, fmt);
335     if (scratch_str == NULL)
336       {
337         ErrClear();
338         return;
339       }
340     Nlm_StrNCpy(info->desc.errtext, scratch_str,
341                 sizeof(info->desc.errtext) - 1);
342     info->desc.errtext[sizeof(info->desc.errtext) - 1] = '\0';
343   }}
344 
345   info->err_formatted = 1;
346   Nlm_ErrPostStr(sev,errcode,0,NULL);
347 
348   info->busy = 0;
349 }
350 
351 
352 /*-------------------------------------------------------------------------
353 *	ErrPostEx   [Schuler, 12-13-93]
354 *
355 * MODIFICATIONS:
356 * 01-14-94 Schuler   Check _busy flag
357 */
358 
359 #ifdef VAR_ARGS
Nlm_ErrPostEx(sev,lev1,lev2,fmt,va_alist)360 NLM_EXTERN int CDECL  Nlm_ErrPostEx (sev, lev1, lev2, fmt, va_alist)
361   ErrSev sev;
362   int lev1;
363   int lev2;
364   const char *fmt;
365   va_dcl
366 #else
367 NLM_EXTERN int CDECL Nlm_ErrPostEx (ErrSev sev, int lev1, int lev2, const char *fmt, ...)
368 #endif
369 {
370   AppErrInfoPtr info = GetAppErrInfo();
371 
372   if ( info->busy )
373     {
374       die_if_necessary(sev, info);
375       return ANS_NONE;
376     }
377   info->busy = AEI_BUSY_1;
378 
379   {{
380     const Nlm_Char PNTR scratch_str = NULL;
381     TSPRINTF(scratch_str, fmt);
382     if (scratch_str == NULL) {
383       ErrClear();
384       info->busy = 0;
385       return ANS_NONE;
386     }
387     Nlm_StrNCpy(info->desc.errtext, scratch_str,
388                 sizeof(info->desc.errtext) - 1);
389     info->desc.errtext[sizeof(info->desc.errtext) - 1] = '\0';
390   }}
391 
392   info->err_formatted =1;
393   {{
394     int code = Nlm_ErrPostStr(sev,lev1,lev2,NULL);
395     info->busy = 0;
396     return code;
397   }}
398 }
399 
400 
401 /*-------------------------------------------------------------------------
402 * ErrPostStr   [Schuler, 12-13-93]
403 *
404 * MODIFICATIONS:
405 * 12-22-93 Schuler   Only logs error if severity >= log_level
406 * 01-14-94 Schuler   Check _busy flag
407 * 02-03-94 Schuler   Use severity from message file if there is one
408 * 02-10-94 Schuler   Workaround for obsolete ERR_IGNORE option
409 * 02-18-94 Schuler   Check for NULL user strings before printing
410 * 08-23-94 Schuler   Clear the err_formatted flag after use
411 * 11-15-94 Schuler   De-const some pointers that need to be lvalues
412 */
413 
Nlm_ErrPostStr(ErrSev sev,int lev1,int lev2,const char * str)414 NLM_EXTERN int LIBCALL Nlm_ErrPostStr (ErrSev sev, int lev1, int lev2, const char *str)
415 {
416   AppErrInfoPtr info = GetAppErrInfo();
417   ErrMsgRoot *root  = NULL;
418   ErrMsgNode *node1 = NULL;
419   ErrMsgNode *node2 = NULL;
420 
421   int severity;
422   if (sev < 0  ||  SEV_MAX < sev)
423     sev = SEV_FATAL;
424   severity = sev;
425 
426   if (info->busy & AEI_BUSY_2)
427     {
428       die_if_necessary(sev, info);
429       return ANS_NONE;
430     }
431   info->busy |= AEI_BUSY_2;
432 
433   /* ----- Fill in the fields of the error descriptor ----- */
434   info->any_error = 1;
435   info->desc.severity = (short)sev;
436   info->desc.errcode = lev1;
437   info->desc.subcode = lev2;
438 
439   /* ----- set up the root and node for message file ----- */
440   if ( (info->desc.module[0] != '\0') )
441     {
442       root = (ErrMsgRoot*) ErrGetMsgRoot(info->desc.module);
443       for (node1=root->list; node1; node1=node1->next)
444         {
445           if (node1->code == info->desc.errcode)
446             {
447               for (node2=node1->list; node2; node2=node2->next)
448                 if (node2->code == info->desc.subcode)
449                   break;
450               break;
451             }
452         }
453     }
454   info->desc.root = root;
455   info->desc.node = node2 ? node2 : node1;
456   if (info->desc.node != NULL && info->desc.node->sev != SEV_NONE)
457     severity = info->desc.node->sev;
458 
459   /* ----- format some strings ----- */
460   if (!info->err_formatted)
461     {
462       info->desc.errtext[0] = '\0';
463       if (str != NULL)
464         strncat(info->desc.errtext,str,ERRTEXT_MAX - 1);
465     }
466   info->err_formatted = 0;
467 
468   if (info->desc.context != 0)
469     sprintf(info->desc.codestr,"[%03d:%03d] ",info->desc.context,info->desc.errcode);
470   else if (node1 != NULL && (TEST_BITS(info,EO_XLATE_CODES)))
471     {
472       if (node2 != NULL)
473         sprintf(info->desc.codestr,"[%s.%s] ",node1->name,node2->name);
474       else
475         sprintf(info->desc.codestr,"[%s] ",node1->name);
476     }
477   else
478     {
479       sprintf(info->desc.codestr,"[%03d.%03d] ",info->desc.errcode,info->desc.subcode);
480     }
481 
482 
483   /* ----- Log the error according to the current options ----- */
484   if (!s_HookOnly  &&  severity >= info->opts.log_level)
485     {
486       NlmMutexLockEx( &corelibMutex );
487 
488 #ifdef WIN_DUMB
489       if (TEST_BITS(info,EO_LOGTO_STDOUT))
490         fflush(stderr);
491       if (TEST_BITS(info,EO_LOGTO_STDERR))
492         fflush(stdout);
493 #endif
494 
495       if (TEST_BITS(info,EO_LOG_SEVERITY))
496         {
497           ErrLogPrintf("%s ",_szSeverity[severity]);
498         }
499       if (TEST_BITS(info,EO_LOG_CODES))
500         {
501           if (info->desc.module[0])
502             ErrLogPrintf("%s ",info->desc.module);
503           ErrLogPrintStr(info->desc.codestr);
504         }
505       if (TEST_BITS(info,EO_LOG_FILELINE))
506         {
507           ErrLogPrintf("{%s, line %d} ",info->desc.srcfile,info->desc.srcline);
508         }
509       if (TEST_BITS(info,EO_LOG_USERSTR))
510         {
511           const ValNode *node;
512           for (node=info->desc.userstr; node; node=node->next)
513             {
514               if (node->data.ptrvalue != NULL)
515                 ErrLogPrintf("%s ",(char*)node->data.ptrvalue);
516             }
517         }
518       if (TEST_BITS(info,EO_LOG_ERRTEXT))
519         {
520           ErrLogPrintStr(info->desc.errtext);
521           ErrLogPrintStr("\n");
522         }
523       if (node1 != NULL && TEST_BITS(info,EO_LOG_MSGTEXT))
524         {
525           ErrMsgNodePtr node = (node2==NULL) ? node1 : node2;
526           const char *text = ErrGetExplanation(root,node);
527           ErrLogPrintStr(text);
528         }
529 
530       NlmMutexUnlock( corelibMutex );
531     }
532 
533   info->busy &= ~AEI_BUSY_2;
534 
535   /* ----- Call the user-installed hook function if there is one ----- */
536   if (info->hook != NULL)
537     {
538       int retval;
539       info->desc.severity = severity;
540       if ((retval = (*info->hook)(&info->desc)) != 0)
541         {
542           ErrClear();
543           return retval;
544         }
545     }
546 
547   if ( s_HookOnly ) {
548       die_if_necessary((ErrSev)severity, info);
549       ErrClear();
550       return ANS_NONE;
551   }
552 
553   /* ----- Workaround for obsolete ERR_IGNORE option ----- */
554   if (info->opts.actopt == ERR_IGNORE)
555     {
556       ErrClear();
557       return ANS_NONE;
558     }
559 
560   /* ----- If not already handled, perform default error handling ----- */
561   if (severity >= info->opts.msg_level || severity >= info->opts.die_level)
562     return ErrShow();
563 
564   return ANS_OK;
565 }
566 
567 
568 /*-------------------------------------------------------------------------
569 *	ErrLogPrintf   [Schuler, 12-13-93]
570 *
571 *  Formats the string and passes it along to ErrLogPrintStr().
572 */
573 #ifdef VAR_ARGS
Nlm_ErrLogPrintf(fmt,va_alist)574 NLM_EXTERN void CDECL Nlm_ErrLogPrintf (fmt, va_alist)
575   const char *fmt;
576   va_dcl
577 #else
578 NLM_EXTERN void CDECL Nlm_ErrLogPrintf (const char *fmt, ...)
579 #endif
580 {
581   const Nlm_Char PNTR scratch_str = NULL;
582   TSPRINTF(scratch_str, fmt);
583   if (scratch_str != NULL)
584     ErrLogPrintStr( scratch_str );
585 }
586 
587 
588 /*-------------------------------------------------------------------------
589 * ErrLogPrintStr   [Schuler, 12-13-93]
590 *
591 * Sends a string of text to whichever output streams have been enabled
592 * for error logging (stderr, trace, or logfile).
593 *
594 * MODIFICATIONS
595 * 12-15-93 Schuler   Added fflush(stdout) before writing to stderr.
596 * 04-19-94 Schuler   Open file if not already open, but don't close
597 * 08-23-94 Schuler   stdout/stderr output only #ifndef _WINDLL
598 */
Nlm_ErrLogPrintStr(const char * str)599 NLM_EXTERN void LIBCALL Nlm_ErrLogPrintStr (const char *str)
600 {
601   AppErrInfoPtr info;
602   size_t bytes;
603 
604   if (str == NULL  ||  *str == '\0'  ||
605       (info = GetAppErrInfo()) == NULL  ||  info->busy & AEI_BUSY_3)
606     return;
607   info->busy |= AEI_BUSY_3;
608 
609   bytes = strlen( str );
610 
611 #ifdef WIN_DUMB
612   {{
613       static unsigned long s_ForceBits = EO_ALL_FLAGS;
614       if (s_ForceBits == EO_ALL_FLAGS) {
615           char buf[64];
616           static const char s_SecretDefault[] = "_NoOneCanGuessThisValue_Duh_";
617           Nlm_GetEnvParam(0, 0, "DEBUG_PRINTOUT",
618                           buf, sizeof(buf), s_SecretDefault);
619           if (Nlm_StrCmp(buf, s_SecretDefault) == 0) {
620               s_ForceBits = 0;
621           } else if (Nlm_StrICmp(buf, "stdout") == 0) {
622               s_ForceBits = EO_LOGTO_STDOUT;
623           } else {
624               s_ForceBits = EO_LOGTO_STDERR;
625           }
626       }
627 
628       if ((s_ForceBits & EO_LOGTO_STDOUT)  ||  TEST_BITS(info,EO_LOGTO_STDOUT))
629           {
630               fflush(stderr);
631               fwrite(str,1,bytes,stdout);
632               fflush(stdout);
633           }
634 
635       if ((s_ForceBits & EO_LOGTO_STDERR)  ||  TEST_BITS(info,EO_LOGTO_STDERR))
636           {
637               fflush(stdout);
638               fwrite(str,1,bytes,stderr);
639               fflush(stderr);
640           }
641   }}
642 #endif
643 
644   info->busy &= ~AEI_BUSY_3;
645 
646   if (TEST_BITS(info,EO_LOGTO_TRACE))
647     {
648       Nlm_TraceStr(str);
649     }
650 
651   if (TEST_BITS(info,EO_LOGTO_USRFILE))
652     {
653       if (info->fdLog == NULL  &&  info->logfile[0] != '\0')
654         info->fdLog = FileOpen(info->logfile, "a+");
655 
656       if (info->fdLog != NULL)
657         {
658           fwrite(str, 1, bytes, info->fdLog);
659           fflush(info->fdLog);
660         }
661     }
662 }
663 
664 
665 /*-------------------------------------------------------------------------
666 *	ErrSetContext   [Schuler, 12-13-93]
667 *
668 *  NOTE: Don't call this function directly
669 *
670 * MODIFICATIONS
671 * 02-03-94 Schuler   Return 1 if busy
672 */
Nlm_ErrSetContext(const char * ctx,const char * fname,int line,int db,Nlm_Uint2 entityID,Nlm_Uint4 itemID,Nlm_Uint2 itemtype)673 NLM_EXTERN int LIBCALL Nlm_ErrSetContext (const char *ctx, const char *fname, int line, int db,
674                                         Nlm_Uint2 entityID, Nlm_Uint4 itemID, Nlm_Uint2 itemtype)
675 {
676 	AppErrInfoPtr info = GetAppErrInfo();
677 
678         if ( info->busy )
679           return 1;
680 
681 	info->desc.module[0] = '\0';
682 	if (ctx != NULL)
683 		strncat(info->desc.module,ctx,MODSTR_MAX - 1);
684 	info->desc.srcfile[0] = '\0';
685 	if (fname != NULL)
686 	{
687 		const char *p;
688 		if ((p = strrchr(fname,DIRDELIMCHR)) != NULL)
689 			++p;
690 		else
691 			p = fname;
692 		strncat(info->desc.srcfile,p,SRCFILE_MAX - 1);
693 	}
694 	info->desc.srcline = line;
695 	info->desc.entityID = entityID;
696 	info->desc.itemID = itemID;
697 	info->desc.itemtype = itemtype;
698 	info->debug_mode = (unsigned)db;
699 	return 0;
700 }
701 
702 
703 /***************************************************************************\
704 |                     FETCHING AND REPORTING ERRORS                         |
705 \***************************************************************************/
706 
707 /*-------------------------------------------------------------------------
708  * ErrFetch  [Schuler]
709  *
710  * MODIFICATIONS
711  * 07-26-93 Schuler   Modified to use AppErrInfo struct
712  */
Nlm_ErrFetch(ErrDesc * err)713 NLM_EXTERN int LIBCALL  Nlm_ErrFetch (ErrDesc *err)
714 {
715 	if (!Nlm_ErrCopy(err))
716 		return FALSE;
717    	ErrClear();
718 	return TRUE;
719 }
720 
721 
722 /*-------------------------------------------------------------------------
723  * ErrCopy  [Schuler, 07-26-93]
724  *
725  * MODIFICATIONS:
726  * 12-12-93 Schuler   Check info->any_error flag before copying
727  */
Nlm_ErrCopy(ErrDesc FAR * err)728 NLM_EXTERN int LIBCALL  Nlm_ErrCopy (ErrDesc FAR *err)
729 {
730 	AppErrInfoPtr info = GetAppErrInfo();
731 	if (info->any_error)
732 	{
733 		if (err != NULL)
734 			memcpy((void*)err,(void*)&(info->desc),sizeof(ErrDesc));
735 		return TRUE;
736 	}
737 	return FALSE;
738 }
739 
740 
741 /*-------------------------------------------------------------------------
742  * ErrClear   [Schuler, 07-26-93]
743  *
744  * MODIFICATIONS
745  * 12-13-93 Schuler   Just clear flags instead of zeroing the whole struct.
746  */
Nlm_ErrClear(void)747 NLM_EXTERN void LIBCALL Nlm_ErrClear (void)
748 {
749 	AppErrInfoPtr info = GetAppErrInfo();
750 	info->any_error = 0;
751 	info->err_formatted = 0;
752 	info->debug_mode = 0;
753 	info->desc.context = 0;
754 	info->desc.entityID = 0;
755 	info->desc.itemID = 0;
756 	info->desc.itemtype = 0;
757 }
758 
759 /*-------------------------------------------------------------------------
760 * ErrShow   [Schuler]
761 *
762 * MODIFICATIONS
763 * 12-13-93 Schuler   Rewritten to use msg_level and die_level settings.
764 * 12-14-93 Schuler   Now returns the result of Message().
765 * 12-15-93 Schuler   Added proper handling of Abort/Retry/Ignore choice.
766 * 12-21-93 Schuler   Added special case for WIN_DUMB + logging to stderr
767 * 12-24-94 Schuler   Changed Message to MsgAlert
768 * 12-27-94 Schuler   Removed Beep() because MsgAlert is taking care of it
769 * 01-31-94 Schuler   Put Beep() back because MsgAlert is no longer doing it.
770 * 02-03-94 Schuler   Now honors all EO_MSG_... option flags.
771 * 02-18-94 Schuler   Check for NULL user strings before copying.
772 *
773 * TO DO
774 * - allocate buffer for the message
775 */
Nlm_ErrShow(void)776 NLM_EXTERN int LIBCALL Nlm_ErrShow (void)
777 {
778   AppErrInfoPtr info = GetAppErrInfo();
779   if (info->any_error)
780     {
781       int answer = ANS_OK;
782       int severity = info->desc.severity;
783       if (info->desc.node != NULL && info->desc.node->sev != SEV_NONE)
784         severity = info->desc.node->sev;
785       if (severity >= info->opts.msg_level)
786         {
787           size_t bytes;
788           const char *caption = GetProgramName();
789           char *message;
790           const char *msgtext;
791           char *p;
792           MsgKey key;
793 
794           /* ----- beep if requested ----- */
795           if (TEST_BITS(info,EO_BEEP))
796             Nlm_Beep();
797 
798           /* ----- set up the buffer to hold the error message ---- */
799           if (TEST_BITS(info,EO_MSG_MSGTEXT))
800             msgtext = ErrGetExplanation(info->desc.root,info->desc.node);
801           else
802             msgtext = NULL;
803           /* forget about the options for now -- this is the max we would need*/
804           bytes = 2 + strlen(info->desc.module)
805             + 32    /* severity string.  TEMPORARY (need to keep track of longest string) */
806             + 2  + strlen(info->desc.codestr)  /* error codes */
807             + 16 + strlen(info->desc.srcfile)  /* source file and line number */
808             + 2  + info->ustrcnt + info->ustrlen  /* user strings */
809             + 2  + strlen(info->desc.errtext)  /* error message */
810             + 2  + ((msgtext) ? strlen(msgtext) : 0);  /* verbose message */
811 
812           if ((message = (char*)Malloc(bytes)) == NULL)
813             message = info->desc.errtext;
814           else
815             {
816               /* ----- format the message in the buffer ----- */
817               *message = '\0';
818               p = message;
819               if (TEST_BITS(info,EO_MSG_SEVERITY))
820                 {
821                   NlmMutexLockEx( &corelibMutex );
822                   p = strchr(strcpy(p,_szSeverity[severity]),'\0');
823                   NlmMutexUnlock( corelibMutex );
824                   *p++ = ' ';
825                 }
826               if (TEST_BITS(info,EO_MSG_CODES))
827                 {
828                   p = strchr(strcpy(p,info->desc.module),'\0');
829                   *p++ = ' ';
830                   p = strchr(strcpy(p,info->desc.codestr),'\0');
831                   *p++ = ' ';
832                 }
833               if (TEST_BITS(info,EO_MSG_FILELINE))
834                 {
835                   /*p = strchr(strcpy(p,info->desc.srcfile),'\0');*/
836                   sprintf(p,"{%s line %d} \n",info->desc.srcfile,info->desc.srcline);
837                   p = strchr(p,'\0');
838                 }
839               if (TEST_BITS(info,EO_MSG_USERSTR))
840                 {
841                   const ValNode *node;
842                   for (node=info->desc.userstr; node; node=node->next)
843                     {
844                       if (node->data.ptrvalue != NULL)
845                         {
846                           p = strchr(strcpy(p,(char*)node->data.ptrvalue),'\0');
847                           *p++ = ' ';
848                         }
849                     }
850                 }
851               if (TEST_BITS(info,EO_MSG_ERRTEXT))
852                 {
853                   p = strchr(strcpy(p,info->desc.errtext),'\0');
854                 }
855               if (TEST_BITS(info,EO_MSG_MSGTEXT) && msgtext!=NULL)
856                 {
857                   if (p != message)  *p++ = '\n';
858                   p = strchr(strcpy(p,msgtext),'\0');
859                 }
860               ASSERT_HARD ( strlen(p) < bytes );
861             }
862 
863           /* ----- show the message ----- */
864           if (TEST_BITS(info,EO_PROMPT_ABORT)) key = KEY_ARI;
865           else if (TEST_BITS(info,EO_WAIT_KEY)) key = KEY_OK;
866           else key = KEY_NONE;
867           answer = MsgAlertStr(key,(ErrSev)severity,caption,message);
868 
869           /* ----- clean up ----- */
870           if (message != info->desc.errtext)
871             Free(message);
872         }
873 
874       /* ----- die if appropriate ----- */
875       die_if_necessary((ErrSev)severity, info);
876 
877       ErrClear();
878       return answer;
879     }
880   return 0;
881 }
882 
883 
884 
885 /***************************************************************************\
886 |                           MESSAGE FILE SUPPORT                            |
887 \***************************************************************************/
888 static FILE * ErrMsgRoot_fopen PROTO((ErrMsgRoot *ctx));
889 static ErrMsgNode * new_ErrMsgNode PROTO((const char *name, int code, ErrSev sev));
890 static void delete_ErrMsgNode PROTO((ErrMsgNode *item));
891 static ErrSev atosev PROTO((const char *sevstr));
892 
893 
new_ErrMsgRoot(const char * context)894 static ErrMsgRoot * new_ErrMsgRoot (const char *context)
895 {
896 	ErrMsgRoot *idx = (ErrMsgRoot*) MemNew(sizeof(ErrMsgRoot));
897 	if (idx != NULL)
898           idx->name = (context) ? StrSave(context) : 0;
899 	return idx;
900 }
901 
delete_ErrMsgRoot(ErrMsgRoot * idx)902 static void delete_ErrMsgRoot(ErrMsgRoot *idx)
903 {
904   if ( idx->name )
905     MemFree((void *)idx->name);
906   if ( idx->name )
907     delete_ErrMsgNode(idx->list);
908   MemFree(idx);
909 }
910 
911 
912 /*-------------------------------------------------------------------------
913 * ErrMsgRoot_fopen    [Schuler, 12-09-93]
914 *
915 * Opens an error message file corresponding to an error context.
916 *
917 * MODIFICATIONS
918 * 01-07-94 Schuler   Looks in the current directory first.
919 * 01-10-94 Schuler   Uses FileOpen() instead of fopen() (now that
920 *                    reentrancy problem is solved).
921 * 09-08-94 Schuler   Don't try to open file if FileLength ==0
922 * 03-06-95 Schuler   Eliminated the mode argument to this function.  The
923 *                    required for proper operation depends on the platform.
924 */
925 
926 #if defined(OS_MSWIN) || defined(OS_NT)
927 static char * s_msg_mode = "rb";
928 #else
929 static char * s_msg_mode = "r";
930 #endif
931 
932 #define MODFNAME_MAX 32
933 
934 
ErrMsgRoot_fopen(ErrMsgRoot * ctx)935 static FILE * ErrMsgRoot_fopen (ErrMsgRoot *ctx)
936 {
937 	AppErrInfoPtr info = GetAppErrInfo();
938 	FILE *fd;
939 	char file[MODFNAME_MAX];
940 	char path[PATH_MAX];
941 	char *p1 = file;
942 	const char *p2 = ctx->name;
943 	int i, ch;
944 
945 	for (i=0; (ch= *p2++) != 0 && i<MODFNAME_MAX-5; ++i)
946 	{
947 		if (isalpha(ch)) ch = tolower(ch);
948 		*p1++ = (char)ch;
949 	}
950 	strcpy(p1,".msg");
951 
952 	if (FileLength(file)==0 || (fd = FileOpen(file,s_msg_mode)) == NULL)
953           {
954             strcpy(path,info->msgpath);
955             strncat(path,file,sizeof(path));
956             fd = FileOpen(path,s_msg_mode);
957           }
958 
959 	return fd;
960 }
961 
962 
963 /*-------------------------------------------------------------------------
964 * ErrMsgRoot_Read    [Schuler, 12-08-93]
965 *
966 * Scans an error message file and builds a data structure that contains
967 * the mappings between integer error codes and strings as well as file
968 * offset for the verbose error explanations so they can later be retrieved
969 * when (or if) needed.
970 *
971 * MODIFICATIONS
972 * 01-07-94 Schuler   Fixed bug that resulted in failure to set the text
973 *                    length for the last item in the file.
974 * 01-10-94 Schuler   Added _busy flag to indicate that we are in the process
975 *                    of parsing the file.  Otherwise, there is a problem if
976 *                    you post an error while attempting to read corelib.msg
977 *                    (leads to infinite recursion).
978 * 01-10-94 Schuler   Added linenumber reporting for all error messages.
979 * 01-23-94 Schuler   Changed all ErrPostEx to ErrLogPrintf (don't want to
980 *                    be posting errors in here -- will recurse ad infinitum).
981 * 01-21-94 Schuler   Changed fclose() to FileClose()
982 * 02-02-94 Schuler   Revisions for new file format
983 *
984 * TO DO
985 * Check for missing integer code
986 * Capture comments
987 */
988 
atosev(const char * sevstr)989 static ErrSev atosev (const char *sevstr)
990 {
991 	int i;
992 	if (sevstr)
993 	{
994 		for (i=SEV_MIN;  i <= SEV_MAX;  ++i)
995 		{
996 			if (strcmp(sevstr,_szSevKey[i]) == 0)
997 				return (ErrSev)i;
998 		}
999 	}
1000 	return SEV_NONE;
1001 }
1002 
ErrMsgRoot_Read(ErrMsgRoot * idx)1003 static int ErrMsgRoot_Read (ErrMsgRoot *idx)
1004 {
1005   FILE *fd;
1006 
1007   if (idx->list != NULL)
1008     return TRUE;
1009   if ( idx->busy )
1010     return FALSE;
1011 
1012   idx->busy = 1;
1013 
1014   if ((fd = ErrMsgRoot_fopen(idx)) == NULL)
1015     {
1016       idx->busy = 0;
1017       return FALSE;
1018     }
1019 
1020   {{
1021     char line[80], *p;
1022     int  linenum = 0;
1023     long tofs = 0, cofs = 0, tmpofs;
1024     ErrMsgNode *lev1;
1025     ErrMsgNode *lev2;
1026     int any_text;
1027     int any_comt;
1028 
1029     /* ----- Look for MODULE line ----- */
1030     while (Nlm_FileGets(line,sizeof line,fd))
1031       {
1032         linenum++;
1033         if ( strchr("#\r\n", line[0]) )
1034           continue;
1035 
1036         if (strncmp(line,"MODULE",6) == 0)
1037           {
1038             p = strtok(line," \t\r\n");
1039             p = strtok(NULL," \t\r\n");
1040             if (strcmp(idx->name,p) !=0)
1041               ErrLogPrintf("Context string mismatch (%s vs %s)\n",idx->name,p);
1042             break;
1043           }
1044         else
1045           {
1046             idx->busy = 0;
1047             ErrLogPrintf("Syntax error: \"MODULE\" expected, line %d\n",linenum);
1048             return FALSE;
1049           }
1050       }
1051 
1052     /* ----- Process the rest of the file ----- */
1053     any_text = any_comt = FALSE;
1054     lev1 = lev2 = NULL;
1055     tmpofs = ftell(fd);
1056 
1057     while (Nlm_FileGets(line,sizeof line,fd))
1058       {
1059         linenum++;
1060 
1061         if (line[0] == '$')
1062           {
1063             if (any_text)
1064               {
1065                 ErrMsgNode *n = lev2 ? lev2 : lev1;
1066                 if (n != NULL)
1067                   {
1068                     n->tofs = tofs;
1069                     n->tlen = tmpofs - tofs;
1070                   }
1071                 any_text = FALSE;
1072               }
1073           }
1074 
1075         if (line[0]=='$' && line[1]=='$')
1076           {
1077             /*** NEED ERROR CHECKING HERE ***/
1078             char *tok1 = strtok(line+2,", \t\r\n");
1079             char *tok2 = strtok(NULL,", \t\r\n");
1080             char *tok3 = strtok(NULL,", \t\r\n");
1081 
1082             ASSERT(tok1 && tok2);
1083 
1084             lev2 = NULL;
1085             lev1 = new_ErrMsgNode(tok1,atoi(tok2),atosev(tok3));
1086             if (idx->list == NULL)
1087               idx->list = lev1;
1088             else
1089               {
1090                 ErrMsgNode *t0=NULL, *t1;
1091                 for (t1=idx->list; TRUE; t1=t1->next)
1092                   {
1093                     if (t1==NULL || lev1->code < t1->code)
1094                       {
1095                         if (t0 == NULL)
1096                           idx->list = lev1;
1097                         else
1098                           t0->next = lev1;
1099                         lev1->next = t1;
1100                         break;
1101                       }
1102                     if (lev1->code == t1->code)
1103                       {
1104                         ErrLogPrintf("Code %d duplicated, line %d\n",t1->code,linenum);
1105                         break;
1106                       }
1107                     t0 = t1;
1108                   }
1109               }
1110             /*any_text = FALSE;*/
1111           }
1112         else if (line[0]=='$' && line[1]=='^')
1113           {
1114             /*** NEED ERROR CHECKING HERE ***/
1115             char *tok1 = strtok(line+2,", \t\r\n");
1116             char *tok2 = strtok(NULL,", \t\r\n");
1117             char *tok3 = strtok(NULL,", \t\r\n");
1118 
1119             ASSERT(tok1 && tok2);
1120 
1121             lev2 = new_ErrMsgNode(tok1,atoi(tok2),atosev(tok3));
1122             if (lev1->list == NULL)
1123               lev1->list = lev2;
1124             else
1125               {
1126                 ErrMsgNode *t0=NULL, *t1;
1127                 for (t1=lev1->list; TRUE; t1=t1->next)
1128                   {
1129                     if (t1==NULL || lev2->code < t1->code)
1130                       {
1131                         if (t0 == NULL)
1132                           lev1->list = lev2;
1133                         else
1134                           t0->next = lev2;
1135                         lev2->next = t1;
1136                         break;
1137                       }
1138                     if (lev2->code == t1->code)
1139                       {
1140                         ErrLogPrintf("Code %d duplicated, line %d\n",t1->code,linenum);
1141                         break;
1142                       }
1143                     t0 = t1;
1144                   }
1145               }
1146             /*any_text = FALSE;*/
1147           }
1148         else if (line[0] == '#')
1149           {
1150             if (!any_comt)
1151               {
1152                 cofs = tmpofs;
1153                 any_comt = TRUE;
1154               }
1155           }
1156         else
1157           {
1158             if (any_comt)
1159               {
1160                 ErrMsgNode *n = lev2 ? lev2 : lev1;
1161                 if (n != NULL)
1162                   {
1163                     n->cofs = cofs;
1164                     n->clen = tmpofs - cofs;
1165                   }
1166               }
1167             if (!any_text)
1168               {
1169                 /* if (not a blank line) */
1170                 tofs = tmpofs;
1171                 any_text = TRUE;
1172               }
1173           }
1174         tmpofs = ftell(fd);
1175       }
1176 
1177     if (any_text)
1178       {
1179         ErrMsgNode *n = lev2 ? lev2 : lev1;
1180         if (n != NULL)
1181           {
1182             n->tofs = tofs;
1183             n->tlen = tmpofs - tofs;
1184           }
1185       }
1186 
1187     FileClose(fd);
1188   }}
1189 
1190   idx->busy = 0;
1191   return TRUE;
1192 }
1193 
1194 
1195 /*-------------------------------------------------------------------------
1196  * new_ErrMsgNode, delete_ErrMsgNode
1197  * Constructor/Destructor for ErrMsgNode
1198  */
1199 
new_ErrMsgNode(const char * name,int code,ErrSev sev)1200 static ErrMsgNode * new_ErrMsgNode(const char *name, int code, ErrSev sev)
1201 {
1202   ErrMsgNode *item = (ErrMsgNode*)MemNew(sizeof(ErrMsgNode));
1203   if ( item ) {
1204     item->name = (name) ? StrSave(name) : 0;
1205     item->code = code;
1206     item->sev  = sev;
1207   }
1208   return item;
1209 }
1210 
delete_ErrMsgNode(ErrMsgNode * item)1211 static void delete_ErrMsgNode(ErrMsgNode *item)
1212 {
1213   ErrMsgNode *next;
1214 
1215   while ( item ) {
1216     next = item->next;
1217 
1218     delete_ErrMsgNode(item->list);
1219     MemFree((void *)item->name);
1220     MemFree((void *)item->tstr);
1221     MemFree((void *)item->cstr);
1222     MemSet(item, '\xFF', sizeof(*item));
1223     MemFree(item);
1224 
1225     item = next;
1226   }
1227 }
1228 
1229 
1230 /*-------------------------------------------------------------------------
1231  * ErrGetMsgRoot    [Schuler, 12-09-93]
1232  *
1233  * Gets the index structure for an error context, creating and initializing
1234  * it if necessary.
1235  *
1236  * MODIFICATIONS
1237  * 01-10-94 Schuler   Changed to call ErrMsgRoot_Read() *after* linking
1238  *                    into list.  Otherwise, if an error is posted from
1239  *                    within ErrMsgRoot_Read(), it won't find the CoreLib
1240  *                    modlue and will read it again (ad infinitum).
1241  */
1242 
ErrGetMsgRoot(const char * context)1243 NLM_EXTERN ErrMsgRootPtr LIBCALL ErrGetMsgRoot (const char *context)
1244 {
1245   AppErrInfoPtr info = GetAppErrInfo();
1246   ErrMsgRoot *idx, *idx0=NULL;
1247   int d;
1248 
1249   for (idx=info->idxlist; TRUE; idx=idx->next)
1250     {
1251       if (idx==NULL || (d = strcmp(idx->name,context)) > 0)
1252         {
1253           ErrMsgRoot *idx2 = new_ErrMsgRoot(context);
1254           idx2->next = idx;
1255           if (idx0 == NULL)
1256             info->idxlist = idx2;
1257           else
1258             idx0->next = idx2;
1259           ErrMsgRoot_Read(idx2);
1260           return (ErrMsgRootPtr) idx2;
1261         }
1262 
1263       if (d == 0)
1264         break;
1265 
1266       idx0 = idx;
1267     }
1268 
1269   return (ErrMsgRootPtr)idx;
1270 }
1271 
1272 
1273 /*-------------------------------------------------------------------------
1274  * ErrGetExplanation    [Schuler, 12-09-93]
1275  *
1276  * Gets the explanatory text for a particular error from an error
1277  * message file.
1278  *
1279  * MODIFICATIONS
1280  * 02-02-94 Schuler   Fix for memory leak when length==0
1281  * 02-02-94 Schuler   Now exported (was static)
1282  */
1283 
ErrGetExplanation(ErrMsgRootPtr idx,ErrMsgNodePtr item)1284 NLM_EXTERN const char* LIBCALL ErrGetExplanation (ErrMsgRootPtr idx, ErrMsgNodePtr item)
1285 {
1286 	if (idx != NULL && item != NULL && item->tlen >0)
1287 	{
1288 		size_t bytes;
1289 		char *text;
1290 
1291 		if (item->tstr != NULL)
1292 			return item->tstr;
1293 
1294 		bytes = (size_t)item->tlen;
1295 		if ((text=(char *)MemNew(bytes+1)) != NULL)
1296 		{
1297 			FILE *fd = ErrMsgRoot_fopen((ErrMsgRoot *) idx);
1298 			if (fd != NULL)
1299 			{
1300 				if (fseek(fd,item->tofs,SEEK_SET) !=0)
1301 					goto ErrReturn;
1302 				if (fread(text,1,bytes,fd) != bytes)
1303 					goto ErrReturn;
1304 				fclose(fd);
1305 				*(text+bytes) = '\0';    /* make sure null-terminated */
1306 				((ErrMsgNode*)item)->tstr = text;
1307 				return text;
1308 			}
1309 		ErrReturn :
1310 			fclose(fd);
1311 			MemFree(text);
1312 			return NULL;
1313 		}
1314 	}
1315 	return NULL;
1316 }
1317 
1318 
1319 /***************************************************************************\
1320 |                             CUSTOMIZATION                                 |
1321 \***************************************************************************/
1322 
1323 /*-------------------------------------------------------------------------
1324 * ErrSetLogfile  [Schuler]
1325 *
1326 * MODIFICATIONS
1327 * 07-26-93 Schuler   Modified to use AppErrInfo struct
1328 * 12-17-93 Schuler   Changed back to returning a Boolean
1329 * 01-21-94 Schuler   Renamed & added flags argument.
1330 * 01-21-94 Schuler   Changed fopen/fclose to FileOpen/FileClose
1331 * 04-19-94 Schuler   Only open
1332 *
1333 * TO DO
1334 * - honor ELOG_NOCREATE flag
1335 */
1336 
Nlm_ErrSetLogfile(const char * filename,unsigned long flags)1337 NLM_EXTERN int LIBCALL  Nlm_ErrSetLogfile (const char *filename, unsigned long flags)
1338 {
1339   static char *fmode_append = "a+";
1340   static char *fmode_overwrite = "w";
1341 
1342   AppErrInfoPtr info = GetAppErrInfo();
1343 
1344   if (info->fdLog != NULL)  /* close if already open */
1345     {
1346       FileClose(info->fdLog);
1347       info->fdLog = NULL;
1348     }
1349 
1350   if (filename == NULL)
1351     info->logfile[0] = '\0';
1352   else
1353     {
1354       if ( !(flags & ELOG_NOCREATE) )
1355         {
1356           char *fmode = (flags & ELOG_APPEND) ? fmode_append : fmode_overwrite;
1357           FILE *fp;
1358 
1359           if ((fp = FileOpen(filename,fmode)) == NULL)
1360             return FALSE;
1361           if (flags & ELOG_BANNER)
1362             {
1363               char buffer[64];
1364               Nlm_DayTimeStr(buffer,TRUE,TRUE);
1365               fprintf(fp, "\n========================[ %s ]========================\n", buffer);
1366             }
1367           FileClose(fp);
1368         }
1369       strncpy(info->logfile,filename,PATH_MAX - 1);
1370       info->logfile[PATH_MAX - 1] = '\0';
1371     }
1372   return TRUE;
1373 }
1374 
1375 
Nlm_ErrGetLogfile(void)1376 const char* LIBCALL Nlm_ErrGetLogfile( void )
1377 {
1378   AppErrInfoPtr info = GetAppErrInfo();
1379 
1380   if (info->logfile != NULL  &&  *info->logfile == '\0')
1381     return NULL;
1382 
1383   return info->logfile;
1384 }
1385 
1386 
1387 /*-------------------------------------------------------------------------
1388  * ErrSetHandler	[Schuler, 05-11-93]
1389  *
1390  * Allows the application to set a hook procedure that will be called
1391  * when an error is posted via ErrPost.  It is always called regardless
1392  * of any error reporting/logging options that may have been set.  The
1393  * return value is the pointer to the previous error hook procedure
1394  * if there was one.
1395  *
1396  * MODIFICATIONS
1397  * 05-21-93 Schuler
1398  */
Nlm_ErrSetHandler(ErrHookProc hookNew)1399 NLM_EXTERN ErrHookProc LIBCALL Nlm_ErrSetHandler (ErrHookProc hookNew)
1400 {
1401 	AppErrInfoPtr info = GetAppErrInfo();
1402 	ErrHookProc hookOld =info->hook;
1403 	info->hook = hookNew;
1404 	return hookOld;
1405 }
1406 
1407 
Nlm_CallErrHandlerOnly(Nlm_Boolean hook_only)1408 NLM_EXTERN Nlm_Boolean LIBCALL Nlm_CallErrHandlerOnly(Nlm_Boolean hook_only)
1409 {
1410     Nlm_Boolean prev;
1411     NlmMutexLockEx(&corelibMutex);
1412     prev       = s_HookOnly;
1413     s_HookOnly = hook_only;
1414     NlmMutexUnlock(corelibMutex);
1415     return prev;
1416 }
1417 
1418 
1419 /*-------------------------------------------------------------------------
1420  * ErrUserInstall   [Sirotkin]
1421  *
1422  * Add or replace a user-string.
1423  *
1424  * MODIFICATIONS
1425  * 12-15-93 Schuler   No longer keeps track of string lengths.
1426  */
1427 
1428 static char *_strNull = "(null)";
1429 
Nlm_ErrUserInstall(const char * msg,ErrStrId magic_cookie)1430 NLM_EXTERN ErrStrId LIBCALL Nlm_ErrUserInstall (const char *msg, ErrStrId magic_cookie)
1431 {
1432 	AppErrInfoPtr info = GetAppErrInfo();
1433 	ValNode *list = (ValNode *) info->desc.userstr;
1434 	ValNode *node;
1435 	ErrStrId cookie;
1436 
1437 	if (msg == NULL)
1438 		msg = _strNull;
1439 
1440 	if (magic_cookie)
1441 	{
1442 		for (node=list; node; node=node->next)
1443 		{
1444 			if (node->choice == magic_cookie)
1445 			{
1446 				/** replace **/
1447 				info->ustrlen -= strlen((char*)node->data.ptrvalue);
1448 				MemFree(node->data.ptrvalue);
1449 				node->data.ptrvalue = (void*) StringSave(msg);
1450 				info->ustrlen += strlen(msg);
1451 				return magic_cookie;
1452 			}
1453 		}
1454 		ErrPostEx(SEV_WARNING,-1,0,
1455 				"ErrUserInstall:  bad string id (%d)",
1456 				(int) magic_cookie);
1457 		return 0;
1458 	}
1459 	else
1460 	{
1461 		for (cookie = 1; cookie < 255; ++cookie)
1462 		{
1463 			for (node=list; node; node=node->next)
1464 			{
1465 				if (node->choice == cookie)
1466 					break;
1467 			}
1468 			if (node==NULL)  /* free cookie is magic */
1469 			{
1470 				node = ValNodeNew((ValNode*)info->desc.userstr);
1471 				if ( ! info->desc.userstr)
1472 					info->desc.userstr = node;
1473 				node->choice = cookie;
1474 				node->data.ptrvalue = StringSave(msg);
1475 				info->ustrlen += strlen(msg);
1476 				info->ustrcnt ++;
1477 				return cookie;
1478 			}
1479 		}
1480 		/*ErrPostEx(SEV_WARNING,-1,0,"ErrUserInstall:  no more string id's");*/
1481 		return 0;
1482 	}
1483 }
1484 
1485 
1486 /*-------------------------------------------------------------------------
1487  * ErrUserDelete   [Sirotkin]
1488  *
1489  * Delete a single user-string.
1490  *
1491  * MODIFICATIONS
1492  * 12-15-93 Schuler   No longer keeps track of string lengths.
1493  */
1494 
Nlm_ErrUserDelete(ErrStrId magic_cookie)1495 NLM_EXTERN Nlm_Boolean LIBCALL Nlm_ErrUserDelete (ErrStrId magic_cookie)
1496 {
1497 	AppErrInfoPtr info = GetAppErrInfo();
1498 	ValNodePtr node = ValNodeExtract((ValNode**)(&info->desc.userstr), magic_cookie);
1499 
1500 	if (node)
1501 	{
1502 		ASSERT(node->data.ptrvalue != NULL);
1503 		info->ustrlen -= strlen((char*)node->data.ptrvalue);
1504 		info->ustrcnt --;
1505 		ValNodeFreeData(node);
1506 		return TRUE;
1507 	}
1508 	return FALSE;
1509 }
1510 
1511 
1512 /*-------------------------------------------------------------------------
1513  * ErrUserClear   [Sirotkin]
1514  *
1515  * Deletes the entire list of user-strings.
1516  */
Nlm_ErrUserClear(void)1517 NLM_EXTERN void LIBCALL Nlm_ErrUserClear (void)
1518 {
1519 	AppErrInfoPtr info = GetAppErrInfo();
1520 	ValNodeFreeData((ValNode*)info->desc.userstr);
1521 	info->ustrlen = 0;
1522 	info->ustrcnt = 0;
1523 }
1524 
1525 /**************************************************************************
1526 *
1527 *  ErrGetUserStrings(ErrDescPtr err)
1528 *    returns ValNodePtr to chain of user strings for this thread
1529 *
1530 ***************************************************************************/
Nlm_ErrGetUserStrings(ErrDescPtr err)1531 NLM_EXTERN ValNodePtr LIBCALL Nlm_ErrGetUserStrings(ErrDescPtr err)
1532 {
1533 	ValNodePtr vnp = NULL;
1534 
1535 	if (err != NULL)
1536 		vnp = (ValNodePtr)(err->userstr);
1537 	return vnp;
1538 }
1539 
1540 
1541 
1542 /*-------------------------------------------------------------------------
1543  * ErrGetLogLevel, ErrSetLogLevel   [Schuler, 12-22-93]
1544  *
1545  * Get/Set the minimum severity level that will be logged.
1546  */
ErrSetLogLevel(ErrSev level)1547 NLM_EXTERN ErrSev LIBCALL ErrSetLogLevel (ErrSev level)
1548 {
1549 	AppErrInfoPtr info = GetAppErrInfo();
1550 	ErrSev prev = (ErrSev) info->opts.log_level;
1551 	info->opts.log_level = (short) MAX(SEV_MIN,MIN(level,SEV_MAX));
1552 	return prev;
1553 }
1554 
ErrGetLogLevel(void)1555 NLM_EXTERN ErrSev LIBCALL ErrGetLogLevel (void)
1556 {
1557 	return (ErrSev)GetAppErrInfo()->opts.log_level;
1558 }
1559 
1560 
1561 /*-------------------------------------------------------------------------
1562  * ErrGetMessageLevel, ErrSetMessageLevel   [Schuler, 12-14-93]
1563  *
1564  * Get/Set the minimum severity level that will be reported via Message().
1565  */
ErrSetMessageLevel(ErrSev level)1566 NLM_EXTERN ErrSev LIBCALL ErrSetMessageLevel (ErrSev level)
1567 {
1568 	AppErrInfoPtr info = GetAppErrInfo();
1569 	ErrSev prev = (ErrSev) info->opts.msg_level;
1570 	info->opts.msg_level = (short) MAX(SEV_MIN,MIN(level,SEV_MAX));
1571 	return prev;
1572 }
1573 
ErrGetMessageLevel(void)1574 NLM_EXTERN ErrSev LIBCALL ErrGetMessageLevel (void)
1575 {
1576 	return (ErrSev) GetAppErrInfo()->opts.msg_level;
1577 }
1578 
1579 
1580 /*-------------------------------------------------------------------------
1581  * ErrGetFatalLevel, ErrSetFatalLevel   [Schuler, 12-14-93]
1582  *
1583  * Get/Set the minimum severity level that will cause an abnormal exit.
1584  */
ErrSetFatalLevel(ErrSev level)1585 NLM_EXTERN ErrSev LIBCALL ErrSetFatalLevel (ErrSev level)
1586 {
1587 	AppErrInfoPtr info = GetAppErrInfo();
1588 	ErrSev prev = (ErrSev) info->opts.die_level;
1589 	info->opts.die_level = (short) MAX(SEV_MIN,MIN(level,SEV_MAX));
1590 	return prev;
1591 }
1592 
ErrGetFatalLevel(void)1593 NLM_EXTERN ErrSev LIBCALL ErrGetFatalLevel (void)
1594 {
1595 	return (ErrSev) GetAppErrInfo()->opts.die_level;
1596 }
1597 
1598 
1599 /*-------------------------------------------------------------------------
1600  * ErrSetOptFlags, ErrClearOptFlags, ErrTestOptFlags   [Schuler, 12-14-93]
1601  *
1602  * Set, clear, or test the current state of any error option flag.
1603  */
ErrSetOptFlags(unsigned long flags)1604 NLM_EXTERN unsigned long LIBCALL ErrSetOptFlags (unsigned long flags)
1605 {
1606 	AppErrInfoPtr info = GetAppErrInfo();
1607 	info->opts.flags |= (flags & EO_ALL_FLAGS);
1608 	return info->opts.flags;
1609 }
1610 
ErrClearOptFlags(unsigned long flags)1611 NLM_EXTERN unsigned long LIBCALL ErrClearOptFlags (unsigned long flags)
1612 {
1613 	AppErrInfoPtr info = GetAppErrInfo();
1614 	info->opts.flags &= ~(flags & EO_ALL_FLAGS);
1615 	return info->opts.flags;
1616 }
1617 
ErrTestOptFlags(unsigned long flags)1618 NLM_EXTERN unsigned long LIBCALL ErrTestOptFlags (unsigned long flags)
1619 {
1620 	AppErrInfoPtr info = GetAppErrInfo();
1621 	return (info->opts.flags & flags);
1622 }
1623 
1624 
1625 /*-------------------------------------------------------------------------
1626  * ErrSaveOptions  [Schuler, 01-03-94]
1627  *
1628  */
ErrSaveOptions(ErrOpts * opts)1629 NLM_EXTERN void LIBCALL ErrSaveOptions (ErrOpts *opts)
1630 {
1631 	AppErrInfoPtr info = GetAppErrInfo();
1632 
1633 	ASSERT(opts != NULL);
1634 	memcpy((void*)opts,(void*)(&info->opts),sizeof(ErrOpts));
1635 }
1636 
1637 
1638 /*-------------------------------------------------------------------------
1639  * ErrRestoreOptions  [Schuler, 01-03-94]
1640  */
1641 
ErrRestoreOptions(const ErrOpts * opts)1642 NLM_EXTERN void LIBCALL ErrRestoreOptions (const ErrOpts *opts)
1643 {
1644 	AppErrInfoPtr info = GetAppErrInfo();
1645 
1646 	ASSERT(opts != NULL);
1647 	memcpy((void*)(&info->opts),(void*)opts,sizeof(ErrOpts));
1648 }
1649 
1650 
1651 /*-------------------------------------------------------------------------
1652 * ErrGetOpts  [Schuler]
1653 *
1654 * MODIFICATIONS
1655 * 07-26-93 Schuler   Modified to use AppErrInfo struct
1656 * 02-02-94 Schuler   Added local option saving strategy
1657 */
1658 
Nlm_ErrGetOpts(short * erract,short * errlog)1659 NLM_EXTERN void LIBCALL  Nlm_ErrGetOpts (short * erract, short * errlog)
1660 {
1661 	AppErrInfoPtr info = GetAppErrInfo();
1662 
1663 	if (erract != NULL)
1664 	{
1665 		size_t i;
1666 		for (i=0; i<DIM(info->_eo_save); ++i)
1667 		{
1668 			if (info->_eo_save[i].flags ==0)
1669 				break;
1670 		}
1671 		if (i<DIM(info->_eo_save))
1672 		{
1673 			info->_eo_save[i] = info->opts;
1674 			*erract = (short)(-1-i);
1675 		}
1676 		else
1677 		{
1678 			TRACE("ErrGetOpts: overflow\n");
1679 			*erract = info->opts.actopt;
1680 		}
1681 	}
1682 	if (errlog != NULL)
1683 		*errlog = info->opts.logopt;
1684 }
1685 
1686 
1687 /*-------------------------------------------------------------------------
1688 * ErrSetOpts  [Schuler]
1689 *
1690 * MODIFICATIONS
1691 * 07-26-93 Schuler   Modified to use AppErrInfo struct
1692 * 12-15-93 Schuler   Modified to map old settings to the new options.
1693 * 12-21-93 Schuler   Now always starts from defaults and then twiddles.
1694 * 01-27-94 Schuler   Fixed bug whereby logopt not always saved.
1695 * 02-02-94 Schuler   Added local option saving strategy
1696 */
1697 
1698 
Nlm_ErrSetOpts(short actopt,short logopt)1699 NLM_EXTERN void LIBCALL  Nlm_ErrSetOpts (short actopt, short logopt)
1700 {
1701 	AppErrInfoPtr info = GetAppErrInfo();
1702 
1703 	if (actopt < 0)
1704 	{
1705 		int i = -(actopt+1);
1706 		ASSERT(i>=0 && i<DIM(info->_eo_save));
1707 		if (info->_eo_save[i].flags !=0)
1708 		{
1709 			info->opts = info->_eo_save[i];
1710 			info->_eo_save[i].flags =0;
1711 			return;
1712 		}
1713 		ErrLogPrintf("*** Improper use of ErrGetOpts/ErrSetOpts ***\n");
1714 	}
1715 
1716 	/****info->opts.flags = EO_DEFAULTS;
1717 	info->opts.log_level = SEV_INFO;****/
1718 
1719 	if (actopt !=0)
1720 		info->opts.flags &= ~EO_PROMPT_ABORT;
1721 
1722 	switch (actopt)
1723 	{
1724 		case ERR_CONTINUE:
1725 		case ERR_IGNORE:
1726 			info->opts.msg_level = SEV_MAX;
1727 			info->opts.die_level = SEV_MAX;
1728 			break;
1729 		case ERR_TEE:
1730 			info->opts.msg_level = SEV_WARNING;
1731 			info->opts.die_level = SEV_MAX;
1732 			break;
1733 		case ERR_ADVISE:
1734 			info->opts.msg_level = SEV_WARNING;
1735 			info->opts.die_level = SEV_MAX;
1736 			break;
1737 		case ERR_ABORT:
1738 			info->opts.msg_level = SEV_WARNING;
1739 			info->opts.die_level = SEV_FATAL;
1740 			break;
1741 		case ERR_PROMPT:
1742 			info->opts.msg_level = SEV_WARNING;
1743 			info->opts.die_level = SEV_FATAL;
1744 			info->opts.flags |= EO_PROMPT_ABORT;
1745 			break;
1746 	}
1747 	if (actopt != 0)
1748 		info->opts.actopt = actopt;
1749 
1750 
1751 	switch(logopt)
1752 	{
1753 		case ERR_LOG_ON:
1754 			info->opts.flags |=  EO_LOGTO_USRFILE;
1755 			break;
1756 		case ERR_LOG_OFF:
1757 			info->opts.flags &= ~EO_LOGTO_USRFILE;
1758 			break;
1759 		default:
1760 			logopt = 0;
1761 	}
1762 	if (logopt != 0)
1763 		info->opts.logopt = logopt;
1764 
1765 
1766 	/***** if anybody liked this, I can put it back...
1767 	if (logopt != 0)
1768 	{
1769 		int   i;
1770 		FILE *fp;
1771 		if ((fp = FileOpen(info->logfile, "a+")) != NULL)
1772 		{
1773 			fputc ('\n', fp);
1774 			for (i=0; i<4; i++)  fputc (' ', fp);
1775 			for (i=0; i<21; i++)  fputc ('-', fp);
1776 			fprintf (fp, " error logging %sabled ",
1777 			                logopt==ERR_LOG_ON ? " en" : "dis");
1778 			for (i=0; i<21; i++)  fputc ('-', fp);
1779 			fputc ('\n', fp);
1780 			FileClose(fp);
1781 		}
1782 	}
1783 	*****/
1784 }
1785 
1786 
1787 /***************************************************************************\
1788 |                               DEBUGGING                                   |
1789 \***************************************************************************/
1790 
1791 
1792 /*-------------------------------------------------------------------------
1793  * Trace   [Schuler, 04-13-93]
1794  *
1795  * Formats a string and sends it to the "trace device" (see the
1796  * description of Nlm_TraceStr about "trace device").  Normally, it
1797  * is desirable to trace some (possibly verbose) informational messages
1798  * during the development phase of a program, but inhibit them in the
1799  * final version that is released to end users.  The TRACE macro supports
1800  * this style of usage, calling Nlm_Trace only if _DEBUG is defined.
1801  * For example:
1802  *
1803  *		TRACE("!@#$ screwed up (%s,%d)\n",__FILE__,___LINE__);
1804  *
1805  * Note that it is declared as CDECL (because of the variable argument
1806  * list) and may therefore not be callable from some other programming
1807  * languages, such as Basic and Pascal.
1808  *
1809  * MODIFICATIONS
1810  * Schuler 07-26-93
1811  * Schuler 12-14-93   Merged varargs and stdargs versions of the function
1812  */
1813 
1814 #ifdef VAR_ARGS
Nlm_Trace(fmt,va_alist)1815 NLM_EXTERN void Nlm_Trace (fmt, va_alist)
1816  const char *fmt;
1817  va_dcl
1818 #else
1819 NLM_EXTERN void CDECL  Nlm_Trace (const char *fmt, ...)
1820 #endif
1821 {
1822   const Nlm_Char PNTR scratch_str = NULL;
1823   TSPRINTF(scratch_str, fmt);
1824   if (scratch_str != NULL)
1825     Nlm_TraceStr( scratch_str );
1826 }
1827 
1828 
1829 
1830 /*-------------------------------------------------------------------------
1831 * TraceStr   [Schuler, 04-13-93]
1832 *
1833 * Users of the C and C++ should use the TRACE macro and not call this
1834 * function directly.  This function, unlike Nlm_Trace, it may be called
1835 * other programming languages, such as Basic and Pascal.
1836 *
1837 * MODIFICATIONS
1838 * 05-26-93 Schuler
1839 * 12-15-93 Schuler   Changed to use TRACE_TO_FILE macro.
1840 * 12-15-93 Schuler   Removed tracing to stderr (caused an echo echo ...).
1841 * 01-21-94 Schuler   Changed fopen/fclose to FileOpen/FileClose
1842 */
1843 
Nlm_TraceStr(const char * str)1844 NLM_EXTERN void LIBCALL Nlm_TraceStr (const char *str)
1845 {
1846 	if (str==NULL)  return;
1847 
1848 #ifdef TRACE_TO_STDOUT
1849 	fprintf(stdout,"%s",str);
1850 #endif
1851 
1852 #ifdef TRACE_TO_AUX
1853 #ifdef OS_MSWIN
1854 	OutputDebugString(str);
1855 #endif
1856 #endif /* TRACE_TO_AUX */
1857 
1858 #ifdef TRACE_TO_FILE
1859 	{
1860 		FILE *fd = FileOpen("trace.log","a+");
1861 		if (fd != NULL)
1862 		{
1863 			fprintf(fd,"%s",str);
1864 			FileClose(fd);
1865 		}
1866 	}
1867 #endif /* TRACE_TO_FILE */
1868 }
1869 
1870 
1871 /*-------------------------------------------------------------------------
1872 * Nlm_AssertionFailed   [Schuler, 04-13-93]
1873 *
1874 * Function needed to support the ASSERT and VERIFY macros.
1875 *
1876 * MODIFICATIONS
1877 * 05-11-93 Schuler
1878 * 12-15-93 Schuler   Use ErrLogPrintf() inseead of Nlm_Trace()
1879 * 12-15-93 Schuler   Added call to Message() so user knows what happened!
1880 * 01-21-94 Schuler   Changed MSG_OK to MSG_POST
1881 */
Nlm_AssertionFailed(const char * expression,const char * module,const char * fname,int linenum)1882 NLM_EXTERN void LIBCALL Nlm_AssertionFailed
1883 (const char *expression, const char *module,
1884  const char *fname, int linenum)
1885 {
1886 	ErrLogPrintf("\nAssertion Failed:  %s\n", expression);
1887 	if (module  &&  *module)
1888      ErrLogPrintf("  %s",module);
1889 	if ( fname )
1890      ErrLogPrintf("  %s, line %d", fname, linenum);
1891 	ErrLogPrintStr("\n");
1892 
1893 	Message(MSG_POST, "Assertion Failed:\n%s", expression);
1894 	if (module  &&  *module)
1895      Message(MSG_POST, "Module \"%s\"", module);
1896 	if ( fname )
1897 	  Message(MSG_POST, "File \"%s\", line %d", fname, linenum);
1898 
1899 	AbnormalExit(1);
1900 }
1901 
1902 
1903 /*-------------------------------------------------------------------------
1904  * AbnormalExit   [Schuler, 04-13-93]
1905  *
1906  * Terminates the application immediately.  This should only be done
1907  * as a last resort since some (possibly necessary) cleanup code will
1908  * not get executed.
1909  *
1910  * MODIFICATIONS
1911  * Schuler  05-11-93
1912  * Schuler  07-26-93
1913  */
Nlm_AbnormalExitPure(int code)1914 NLM_EXTERN void LIBCALL Nlm_AbnormalExitPure(int code)
1915 {
1916 #if defined(_DEBUG)
1917   abort();
1918 #elif defined(OS_MAC)
1919   ExitToShell();
1920 #else
1921   exit( code );
1922 #endif
1923 }
1924 
1925 
Nlm_AbnormalExit(int code)1926 NLM_EXTERN void LIBCALL  Nlm_AbnormalExit (int code)
1927 {
1928   static Nlm_Boolean s_Exiting = FALSE;
1929   if ( !s_Exiting ) {
1930     s_Exiting = TRUE;
1931     ErrLogPrintStr( "\n***** ABNORMAL PROGRAM EXIT *****\n" );
1932   }
1933   Nlm_AbnormalExitPure( code );
1934 }
1935 
1936 
1937 /***************************************************************************\
1938 |                         STATIC HELPER FUNCTIONS                           |
1939 \***************************************************************************/
1940 
1941 /*-------------------------------------------------------------------------
1942 * GetAppErrInfo  [Schuler, 07-26-93]
1943 *
1944 * MODIFICATIONS
1945 * 12-13-93 Schuler   Added initialization for new settings.
1946 * 01-04-94 Schuler   Added code to get settings from ncbi.ini
1947 * 01-27-94 Schuler   Set & clear _busy when reading from ncbi.ini
1948 * 02-01-94 Schuler   Updated _flag array
1949 */
1950 
1951 static char * _file = "ncbi";
1952 static char * _section = "ErrorProcessing";
1953 
1954 struct FlagInf { char *key; unsigned long bits; };
1955 static struct FlagInf _flag [] =
1956 {
1957   { "EO_LOG_SEVERITY",  EO_LOG_SEVERITY  },
1958   { "EO_LOG_CODES",     EO_LOG_CODES     },
1959   { "EO_LOG_FILELINE",  EO_LOG_FILELINE  },
1960   { "EO_LOG_USERSTR",   EO_LOG_USERSTR   },
1961   { "EO_LOG_ERRTEXT",   EO_LOG_ERRTEXT   },
1962   { "EO_LOG_MSGTEXT",   EO_LOG_MSGTEXT   },
1963   { "EO_MSG_SEVERITY",  EO_MSG_SEVERITY  },
1964   { "EO_MSG_CODES",     EO_MSG_CODES     },
1965   { "EO_MSG_FILELINE",  EO_MSG_FILELINE  },
1966   { "EO_MSG_USERSTR",   EO_MSG_USERSTR   },
1967   { "EO_MSG_ERRTEXT",   EO_MSG_ERRTEXT   },
1968   { "EO_MSG_MSGTEXT",   EO_MSG_MSGTEXT   },
1969   { "EO_LOGTO_STDOUT",  EO_LOGTO_STDOUT  },
1970   { "EO_LOGTO_STDERR",  EO_LOGTO_STDERR  },
1971   { "EO_LOGTO_TRACE",   EO_LOGTO_TRACE   },
1972   { "EO_LOGTO_USRFILE", EO_LOGTO_USRFILE },
1973   { "EO_XLATE_CODES",   EO_XLATE_CODES   },
1974   { "EO_WAIT_KEY",      EO_WAIT_KEY      },
1975   { "EO_PROMPT_ABORT",  EO_PROMPT_ABORT  },
1976   { "EO_BEEP",          EO_BEEP          }
1977 };
1978 
1979 
1980 static int _nAppErrInfo = 0;
1981 
ReleaseAppErrInfo(void)1982 extern void ReleaseAppErrInfo( void )
1983 {
1984   AppErrInfoPtr info = (AppErrInfoPtr)GetAppProperty( _szPropKey );
1985 
1986   if (info == NULL)
1987     return;
1988 
1989   NlmMutexLockEx( &corelibMutex );
1990   ASSERT ( _nAppErrInfo > 0 );
1991 
1992   if (--_nAppErrInfo == 0)
1993     {
1994       int i;
1995       for (i=SEV_MIN; i<=SEV_MAX; ++i)
1996         {
1997           Nlm_MemFree( _szSeverity[i] );
1998           _szSeverity[i] = NULL;
1999         }
2000     }
2001 
2002   if (info->fdLog != NULL) {
2003     FileClose( info->fdLog );
2004   }
2005 
2006   while ( info->idxlist ) {
2007     ErrMsgRoot *idx = info->idxlist;
2008     info->idxlist = idx->next;
2009     delete_ErrMsgRoot(idx);
2010   }
2011 
2012   Nlm_ErrUserClear();
2013 
2014   Nlm_MemFill(info, '\xFF', sizeof(*info));
2015   Nlm_MemFree( info );
2016 
2017   RemoveAppProperty( _szPropKey );
2018 
2019   NlmMutexUnlock( corelibMutex );
2020 }
2021 
2022 
GetAppErrInfo(void)2023 static struct AppErrInfo * GetAppErrInfo(void)
2024 {
2025   AppErrInfoPtr info = (AppErrInfoPtr) GetAppProperty(_szPropKey);
2026 
2027   if (info == NULL)
2028     {
2029       char buffer[128], *p;
2030       int i;
2031 
2032       int severity_init = 0;
2033       NlmMutexLockEx( &corelibMutex );
2034 
2035       info = (struct AppErrInfo*) MemGet(sizeof(struct AppErrInfo), TRUE);
2036       if (info == NULL)
2037         Message(MSG_FATAL,"Out of memory");
2038       info->opts.actopt = ERR_ABORT;       /* OBSOLETE */
2039       info->opts.logopt = ERR_LOG_OFF;     /* OBSOLETE */
2040       info->opts.flags = EO_DEFAULTS;
2041       info->opts.log_level = SEV_INFO;
2042 #ifdef _DEBUG
2043       info->opts.msg_level = SEV_WARNING;
2044 #else
2045       info->opts.msg_level = SEV_ERROR;
2046 #endif
2047       info->opts.die_level = SEV_FATAL;
2048       strcpy(info->logfile,"error.log");
2049       for (i = SEV_MIN;  i <= SEV_MAX;  i++)
2050         {
2051           if (_szSeverity[i] == NULL)
2052             {
2053               severity_init = 1;
2054               sprintf(buffer, "*SEV*%d", i);
2055               _szSeverity[i] = StrSave( buffer );
2056             }
2057         }
2058       ++_nAppErrInfo;
2059       SetAppProperty(_szPropKey, (void*)info);
2060 
2061       if (GetAppParam(_file, _section, "MsgPath", "",
2062                       info->msgpath, PATH_MAX-2))
2063         FileBuildPath(info->msgpath, NULL, NULL);
2064 
2065       if ( severity_init )
2066         {
2067           for (i = SEV_MIN;  i <= SEV_MAX;  i++)
2068             {
2069               GetAppParam(_file, _section, _szSevKey[i], _szSevDef[i],
2070                           buffer,sizeof buffer);
2071               if (buffer[0] == '"')
2072                 {
2073                   if ((p = strchr(&buffer[1],'"')) != NULL)  *p = '\0';
2074                   p = &buffer[1];
2075                 }
2076               else
2077                 p = buffer;
2078 
2079               {{
2080                 char *dummy_sev = _szSeverity[i];
2081                 _szSeverity[i] = StrSave(p);
2082                 Nlm_MemFree( dummy_sev );
2083               }}
2084             }
2085         }
2086       NlmMutexUnlock( corelibMutex );
2087 
2088       for (i=0; i<DIM(_flag); ++i)
2089         {
2090           p = _flag[i].key;
2091           if (GetAppParam(_file,_section,p,"",buffer,sizeof buffer))
2092             {
2093               info->ini_mask |= _flag[i].bits;
2094               if (strchr("1TtYy",buffer[0]))
2095                 info->ini_bits |= _flag[i].bits;
2096             }
2097         }
2098 
2099       SetAppProperty(_szPropKey,(void*)info);
2100     }
2101 
2102   return info;
2103 }
2104 
2105 /*-------------------------------------------------------------------------
2106  * ErrPathReset   [Kans]
2107  *
2108  * Resets info->msgpath
2109  */
Nlm_ErrPathReset(void)2110 NLM_EXTERN void LIBCALL Nlm_ErrPathReset (void)
2111 {
2112 	AppErrInfoPtr info = GetAppErrInfo();
2113     if (GetAppParam(_file, _section, "MsgPath", "",
2114                     info->msgpath, PATH_MAX-2))
2115       FileBuildPath(info->msgpath, NULL, NULL);
2116 }
2117 
Nlm_GetErrLongText(char * module,int errcode,int subcode)2118 NLM_EXTERN char *Nlm_GetErrLongText (char *module,
2119                                      int errcode, int subcode)
2120 {
2121     ErrMsgRoot *root  = NULL;
2122     ErrMsgNode *node1 = NULL;
2123     ErrMsgNode *node2 = NULL;
2124     ErrMsgNode *the_node = NULL;
2125     char *text = NULL;
2126 
2127     if (module != NULL) {
2128         root = (ErrMsgRoot*) ErrGetMsgRoot(module);
2129         for (node1=root->list; node1; node1=node1->next) {
2130             if (node1->code == errcode) {
2131                 for (node2=node1->list; node2; node2=node2->next)
2132                     if (node2->code == subcode)
2133                         break;
2134                 break;
2135             }
2136         }
2137     }
2138 
2139     the_node = node2 ? node2 : node1;
2140 
2141     text = (char *) ErrGetExplanation(root, the_node);
2142 
2143     return text;
2144 }
2145 
2146