/* ncbierr.c * =========================================================================== * * PUBLIC DOMAIN NOTICE * National Center for Biotechnology Information * * This software/database is a "United States Government Work" under the * terms of the United States Copyright Act. It was written as part of * the author's official duties as a United States Government employee and * thus cannot be copyrighted. This software/database is freely available * to the public for use. The National Library of Medicine and the U.S. * Government have not placed any restriction on its use or reproduction. * * Although all reasonable efforts have been taken to ensure the accuracy * and reliability of the software and data, the NLM and the U.S. * Government do not and cannot warrant the performance or results that * may be obtained by using this software or data. The NLM and the U.S. * Government disclaim all warranties, express or implied, including * warranties of performance, merchantability or fitness for any particular * purpose. * * Please cite the author in any work or product based on this material. * * =========================================================================== * * $Id: ncbierr.c,v 6.26 2009/08/14 18:01:09 lavr Exp $ * * $Revision: 6.26 $ * * Authors: Schuler, Sirotkin (UserErr stuff) * * Version Creation Date: 9-19-91 * * File Description: Error handling functions * * Modifications: * -------------------------------------------------------------------------- * Date Name Description of modification * ------- ---------- ----------------------------------------------------- * 04-13-93 Schuler Added TRACE, VERIFY, ASSERT macros. * 04-13-93 Schuler Added AbnormalExit function. * 05-11-93 Schuler Added ErrSetHandler function. * 05-21-93 Schuler Remove PROTO from ErrSetHandler implementation (oops) * 05-26-93 Schuler Nlm_TraceStr flushes stderr, if appropriate * 07-26-93 Schuler Moved globals into a (per-app) context struct * 07-26-93 Schuler Fixed ErrClear() * 11-15-93 Schuler Fixed double error reporting problem in ErrPost() * 12-13-93 Schuler Message file support and lots of other changes. * 12-22-93 Schuler Added log_level setting (min. severity to log) * 01-03-94 Schuler Added ErrSaveOptions and ErrRestoreOptions * 01-04-94 Schuler Added code to get settings from NCBI config file * 01-07-94 Schuler Fixed bug in ErrMsgRoot_Read(). * 01-10-94 Schuler Added check for not_avail in ErrMsgRoot_fopen() * 01-10-94 Schuler Fixed bug in ErrGetMsgRoot() * 01-13-94 Schuler Added an handful of typecasts to supress warnings * 01-23-94 Schuler Fixed bug in ErrSetOpts * 02-02-94 Schuler ErrOpt structure has fields for actopt, logopt * 02-02-94 Schuler Revisions related to change in message file format * 02-02-94 Schuler Use TEST_BITS macro throughout * 02-02-94 Schuler Improved back.compatability of ErrGetOpts/ErrSetOpts * 02-10-94 Schuler Workaround for obsolete ERR_IGNORE option * 02-10-94 Schuler Fixed bug in ErrSetFatalLevel * 02-18-94 Schuler Fix to deal with possibility of userstrings being NULL * 03-23-94 Schuler Fix memory leak in ErrGetExplanation * 04-19-94 Schuler Keep error log file open all the time * 08-23-94 Schuler ErrLogStr, ErrLogPrintStr modified * 08-09-94 Schuler Dont try to open message file if FileLength ==0 * 11-15-94 Schuler Tweaks to ErrPostStr to avoid acc errors * 11-18-94 Schuler Changed return type of Get/SetXXXLevel() family * of functions from int to ErrSev to eliminate warnings * 03-06-95 Schuler Fixed problem with ErrMsgRoot_fopen * * $Log: ncbierr.c,v $ * Revision 6.26 2009/08/14 18:01:09 lavr * Use {Get|Set}ProgramName() * * Revision 6.25 2008/01/18 17:09:13 lavr * Use SEV_MIN (instead of explicit SEV_INFO) in level setting routines * * Revision 6.24 2006/11/09 17:46:58 kans * added cast to quiet CodeWarrior complaint * * Revision 6.23 2006/07/13 17:10:35 bollin * use Uint4 instead of Uint2 for itemID values * * Revision 6.22 2005/04/06 19:34:13 lavr * Reassign severity (if found in message file) in case of C++ Tkit hook * * Revision 6.21 2003/03/25 14:56:08 ivanov * Nlm_AbnormalExitPure(): FatalAppExit() call changed to exit() under MS Windows (by Howard Feldman feldman@mshri.on.ca) * * Revision 6.20 2002/10/03 16:22:03 kans * changed fgets to Nlm_FileGets * * Revision 6.19 2002/08/13 20:44:19 lavr * Force logging to stderr/stdout if DEBUG_PRINTOUT env.var. is set as follows: * STDOUT (case-insensitively) - log to stdout; anything else - log to stderr. * By Denis Vakatov and Anton Lavrentiev. * * Revision 6.18 2002/07/11 20:26:03 lavr * Prevent possible buffer overruns in storing text parts of a message * * Revision 6.17 2001/02/08 22:27:04 vakatov * + Nlm_CallErrHandlerOnly() -- to allow processing of posted * err.messages by the hook only. * * Revision 6.16 2000/06/14 22:27:52 vakatov * Nlm_AbnormalExit() -- print message only if called for the first time * * Revision 6.15 1999/10/01 14:41:39 kans * added SEV_REJECT between SEV_ERROR and SEV_FATAL * * Revision 6.14 1999/08/23 19:14:34 vakatov * Let SEV_MAX not be an exception among the "SEV_*" * * Revision 6.13 1998/12/29 06:16:21 vakatov * Nlm_ErrPostStr() -- Check for the severity code validity * * Revision 6.12 1998/10/22 16:52:40 shavirin * Added function *Nlm_GetErrLongText able to retrieve long error messages * * Revision 6.11 1998/09/29 16:20:53 vakatov * Nlm_AssertionFailed(): printout the module name and file/line in Message * * Revision 6.10 1998/08/24 17:42:00 kans * fixed old style function definition warnings * * Revision 6.9 1998/06/11 21:02:23 shavirin * Fixed few warnings * * Revision 6.8 1998/05/04 23:25:46 vakatov * Raised the message posting level to SEV_ERROR(for non-_DEBUG only) * * Revision 6.7 1998/02/24 19:06:19 vakatov * [DEBUG_HARD] Nlm_ErrShow(): init. "*message"(by garyt@timelogic.com) * * Revision 6.6 1998/02/17 20:57:45 vakatov * Fixed delete_ErrNode() and started to use it; use Nlm_ErrUserClear() * too(got rid of mem.leaks) // in cooperation with S.Bazhin. * * Revision 6.5 1998/02/13 15:22:40 vakatov * ReleaseAppErrInfo(): dont forget to destroy list of error message; * +fixed a minor mem.leak(free _szSeverity[SEV_MAX]) * * Revision 6.4 1997/12/04 22:00:15 vakatov * Nlm_ErrPostEx(): dont forget to zero "info->busy" flag when do nothing * * Revision 6.3 1997/11/26 21:26:14 vakatov * Fixed errors and warnings issued by C and C++ (GNU and Sun) compilers * * Revision 6.2 1997/11/04 19:08:25 ostell * added ErrGetUserStrings() to eliminate direct access to ErrDesc.userstr * so this could become a thread specific chain * * Revision 6.1 1997/08/27 20:40:23 kans * added ErrPathReset * * Revision 6.0 1997/08/25 18:15:24 madden * Revision changed to 6.0 * * Revision 5.12 1997/07/22 19:11:22 vakatov * Separated Main() from GetArg[svc]() functions; [WIN_MSWIN] converged * console and GUI libraries; [for WIN32-DLL] encapsulated global variables * * Revision 5.11 1997/03/31 18:52:11 vakatov * Minor fix to pass through the C++ compiler * * Revision 5.10 1997/03/17 21:51:01 vakatov * Nlm_ErrSetLogfile(): put the logfile banner as a monolith string * * Revision 5.9 1997/01/29 16:52:48 vakatov * Nlm_ErrSetLogfile(): fixed an unsafe StrNCpy() use(might produce * a non-'\0'-terminated string) * * Revision 5.8 1996/12/17 05:49:04 vakatov * Got rid of some ugly warnings (JK) * * Revision 5.7 1996/12/16 22:37:54 vakatov * Added more protection from recursive error posing function calls. Rolled * back the changes made in "* Revision 5.6 1996/12/11 23:01:48 vakatov" * * Revision 5.6 1996/12/11 23:01:48 vakatov * ErrMsgRoot_fopen(): Suppressed error posting when opening "*.msg" * (often non-existing) auxiliary files. * * Revision 5.5 1996/12/03 21:48:33 vakatov * Adopted for 32-bit MS-Windows DLLs * * Revision 5.4 1996/11/25 19:03:58 vakatov * Moved 'eo_save'(formerly MT-unsafe 'static _eo_save') and * 'busy'(formerly MT-unsafe 'static _busy') to the 'AppErrInfo' structure. * Added Nlm_ErrGetLogfile() and Nlm_AbnormalExitPure(); the latter * does nothing but exit without any error posting, etc. * "MT-safed" error context initialization and posting functions. * * Revision 5.3 1996/08/12 18:40:31 vakatov * Nlm_ErrPostEx(), Nlm_ErrPostEx() -- prevented from crashing when an * empty formatting string specified( === "") * * Revision 5.2 1996/08/07 19:24:31 vakatov * [_DEBUG] Nlm_AbnormalExit(): "exit()" replaced by "abort()" * * Revision 5.1 1996/07/16 19:56:37 vakatov * VSPRINTF macros has been replaced by the TSPRINTF macros in the * code responsible for the error message composing -- in order to * avoid the memory limits overrun when printing the error messages * (by the means of function "vsprintf()") to a memory-located string. * The application name is added as the error message caption. * Added ReleaseAppErrInfo() function to clean dynamically allocated * memory used by the error posing routines; other memory-leak- * resulting flaws fixed; and eliminated multiple(duplicated) reading * of the parameter file. * * Revision 5.0 1996/05/28 13:18:57 ostell * Set to revision 5.0 * * Revision 4.0 1995/07/26 13:46:50 ostell * force revision to 4.0 * * Revision 2.50 1995/06/27 16:21:45 kans * protect ErrPostEx against single strings > ERRTEXT_MAX in length * * Revision 2.49 1995/05/15 18:45:58 ostell * added Log line * * * * ========================================================================== */ static char *_filename = __FILE__; #define THIS_FILE _filename #include "corepriv.h" #include char * g_corelib = "CoreLib"; struct AppErrInfo { ErrDesc desc; ErrOpts opts; ErrHookProc hook; unsigned long ini_mask; unsigned long ini_bits; unsigned int any_error :1; unsigned int err_formatted :1; unsigned int debug_mode :1; size_t ustrcnt; size_t ustrlen; FILE *fdLog; char logfile[PATH_MAX]; char msgpath[PATH_MAX]; ErrMsgRoot *idxlist; ErrMsgNode *node; ErrOpts _eo_save[2]; #define AEI_BUSY_1 0x1 #define AEI_BUSY_2 0x2 #define AEI_BUSY_3 0x4 int busy; }; typedef struct AppErrInfo *AppErrInfoPtr; static char * _szPropKey = "_AppErrInfo"; static char * _szSevKey [SEV_MAX + 1] = { "", "SEV_INFO", "SEV_WARNING", "SEV_ERROR", "SEV_REJECT", "SEV_FATAL", "SEV_MAX"}; static char * _szSevDef [SEV_MAX + 1] = { "", "NOTE:", "WARNING:", "ERROR:", "REJECT:", "FATAL ERROR:", "FATAL ERROR:" }; static char * _szSeverity[SEV_MAX + 1]; static AppErrInfoPtr GetAppErrInfo PROTO((void)); #define FUSE_BITS(inf) ( ((inf)->ini_bits & (inf)->ini_mask) | ((inf)->opts.flags & ~((inf)->ini_mask)) ) #define TEST_BITS(inf,x) (FUSE_BITS(inf) & (x)) static Nlm_Boolean s_HookOnly = FALSE; /***************************************************************************\ | POSTING AN ERROR | \***************************************************************************/ static void die_if_necessary(ErrSev sev, AppErrInfoPtr info) { if (sev >= info->opts.die_level) Nlm_AbnormalExitPure( info->desc.errcode ); } /*------------------------------------------------------------------------- * ErrPost [Schuler] * * MODIFICATIONS: * 04-13-93 Schuler * 07-26-93 Schuler Modified to use AppErrInfo struct * 11-15-93 Schuler Fixed bug that resulted in reporting errors twice * 12-14-93 Schuler Modified to call the new ErrPostStr function * 01-14-94 Schuler Check _busy flag */ #ifdef VAR_ARGS NLM_EXTERN void CDECL Nlm_ErrPost (context, errcode, fmt, va_alist) int context; int errcode; const char *fmt; va_dcl #else NLM_EXTERN void CDECL Nlm_ErrPost (int context, int errcode, const char *fmt, ...) #endif { AppErrInfoPtr info = GetAppErrInfo(); ErrSev sev = (context==CTX_DEBUG) ? SEV_INFO : SEV_FATAL; if ( info->busy ) { die_if_necessary(sev, info); return; } info->busy = AEI_BUSY_1; info->desc.context = (short)context; {{ const Nlm_Char PNTR scratch_str = NULL; TSPRINTF(scratch_str, fmt); if (scratch_str == NULL) { ErrClear(); return; } Nlm_StrNCpy(info->desc.errtext, scratch_str, sizeof(info->desc.errtext) - 1); info->desc.errtext[sizeof(info->desc.errtext) - 1] = '\0'; }} info->err_formatted = 1; Nlm_ErrPostStr(sev,errcode,0,NULL); info->busy = 0; } /*------------------------------------------------------------------------- * ErrPostEx [Schuler, 12-13-93] * * MODIFICATIONS: * 01-14-94 Schuler Check _busy flag */ #ifdef VAR_ARGS NLM_EXTERN int CDECL Nlm_ErrPostEx (sev, lev1, lev2, fmt, va_alist) ErrSev sev; int lev1; int lev2; const char *fmt; va_dcl #else NLM_EXTERN int CDECL Nlm_ErrPostEx (ErrSev sev, int lev1, int lev2, const char *fmt, ...) #endif { AppErrInfoPtr info = GetAppErrInfo(); if ( info->busy ) { die_if_necessary(sev, info); return ANS_NONE; } info->busy = AEI_BUSY_1; {{ const Nlm_Char PNTR scratch_str = NULL; TSPRINTF(scratch_str, fmt); if (scratch_str == NULL) { ErrClear(); info->busy = 0; return ANS_NONE; } Nlm_StrNCpy(info->desc.errtext, scratch_str, sizeof(info->desc.errtext) - 1); info->desc.errtext[sizeof(info->desc.errtext) - 1] = '\0'; }} info->err_formatted =1; {{ int code = Nlm_ErrPostStr(sev,lev1,lev2,NULL); info->busy = 0; return code; }} } /*------------------------------------------------------------------------- * ErrPostStr [Schuler, 12-13-93] * * MODIFICATIONS: * 12-22-93 Schuler Only logs error if severity >= log_level * 01-14-94 Schuler Check _busy flag * 02-03-94 Schuler Use severity from message file if there is one * 02-10-94 Schuler Workaround for obsolete ERR_IGNORE option * 02-18-94 Schuler Check for NULL user strings before printing * 08-23-94 Schuler Clear the err_formatted flag after use * 11-15-94 Schuler De-const some pointers that need to be lvalues */ NLM_EXTERN int LIBCALL Nlm_ErrPostStr (ErrSev sev, int lev1, int lev2, const char *str) { AppErrInfoPtr info = GetAppErrInfo(); ErrMsgRoot *root = NULL; ErrMsgNode *node1 = NULL; ErrMsgNode *node2 = NULL; int severity; if (sev < 0 || SEV_MAX < sev) sev = SEV_FATAL; severity = sev; if (info->busy & AEI_BUSY_2) { die_if_necessary(sev, info); return ANS_NONE; } info->busy |= AEI_BUSY_2; /* ----- Fill in the fields of the error descriptor ----- */ info->any_error = 1; info->desc.severity = (short)sev; info->desc.errcode = lev1; info->desc.subcode = lev2; /* ----- set up the root and node for message file ----- */ if ( (info->desc.module[0] != '\0') ) { root = (ErrMsgRoot*) ErrGetMsgRoot(info->desc.module); for (node1=root->list; node1; node1=node1->next) { if (node1->code == info->desc.errcode) { for (node2=node1->list; node2; node2=node2->next) if (node2->code == info->desc.subcode) break; break; } } } info->desc.root = root; info->desc.node = node2 ? node2 : node1; if (info->desc.node != NULL && info->desc.node->sev != SEV_NONE) severity = info->desc.node->sev; /* ----- format some strings ----- */ if (!info->err_formatted) { info->desc.errtext[0] = '\0'; if (str != NULL) strncat(info->desc.errtext,str,ERRTEXT_MAX - 1); } info->err_formatted = 0; if (info->desc.context != 0) sprintf(info->desc.codestr,"[%03d:%03d] ",info->desc.context,info->desc.errcode); else if (node1 != NULL && (TEST_BITS(info,EO_XLATE_CODES))) { if (node2 != NULL) sprintf(info->desc.codestr,"[%s.%s] ",node1->name,node2->name); else sprintf(info->desc.codestr,"[%s] ",node1->name); } else { sprintf(info->desc.codestr,"[%03d.%03d] ",info->desc.errcode,info->desc.subcode); } /* ----- Log the error according to the current options ----- */ if (!s_HookOnly && severity >= info->opts.log_level) { NlmMutexLockEx( &corelibMutex ); #ifdef WIN_DUMB if (TEST_BITS(info,EO_LOGTO_STDOUT)) fflush(stderr); if (TEST_BITS(info,EO_LOGTO_STDERR)) fflush(stdout); #endif if (TEST_BITS(info,EO_LOG_SEVERITY)) { ErrLogPrintf("%s ",_szSeverity[severity]); } if (TEST_BITS(info,EO_LOG_CODES)) { if (info->desc.module[0]) ErrLogPrintf("%s ",info->desc.module); ErrLogPrintStr(info->desc.codestr); } if (TEST_BITS(info,EO_LOG_FILELINE)) { ErrLogPrintf("{%s, line %d} ",info->desc.srcfile,info->desc.srcline); } if (TEST_BITS(info,EO_LOG_USERSTR)) { const ValNode *node; for (node=info->desc.userstr; node; node=node->next) { if (node->data.ptrvalue != NULL) ErrLogPrintf("%s ",(char*)node->data.ptrvalue); } } if (TEST_BITS(info,EO_LOG_ERRTEXT)) { ErrLogPrintStr(info->desc.errtext); ErrLogPrintStr("\n"); } if (node1 != NULL && TEST_BITS(info,EO_LOG_MSGTEXT)) { ErrMsgNodePtr node = (node2==NULL) ? node1 : node2; const char *text = ErrGetExplanation(root,node); ErrLogPrintStr(text); } NlmMutexUnlock( corelibMutex ); } info->busy &= ~AEI_BUSY_2; /* ----- Call the user-installed hook function if there is one ----- */ if (info->hook != NULL) { int retval; info->desc.severity = severity; if ((retval = (*info->hook)(&info->desc)) != 0) { ErrClear(); return retval; } } if ( s_HookOnly ) { die_if_necessary((ErrSev)severity, info); ErrClear(); return ANS_NONE; } /* ----- Workaround for obsolete ERR_IGNORE option ----- */ if (info->opts.actopt == ERR_IGNORE) { ErrClear(); return ANS_NONE; } /* ----- If not already handled, perform default error handling ----- */ if (severity >= info->opts.msg_level || severity >= info->opts.die_level) return ErrShow(); return ANS_OK; } /*------------------------------------------------------------------------- * ErrLogPrintf [Schuler, 12-13-93] * * Formats the string and passes it along to ErrLogPrintStr(). */ #ifdef VAR_ARGS NLM_EXTERN void CDECL Nlm_ErrLogPrintf (fmt, va_alist) const char *fmt; va_dcl #else NLM_EXTERN void CDECL Nlm_ErrLogPrintf (const char *fmt, ...) #endif { const Nlm_Char PNTR scratch_str = NULL; TSPRINTF(scratch_str, fmt); if (scratch_str != NULL) ErrLogPrintStr( scratch_str ); } /*------------------------------------------------------------------------- * ErrLogPrintStr [Schuler, 12-13-93] * * Sends a string of text to whichever output streams have been enabled * for error logging (stderr, trace, or logfile). * * MODIFICATIONS * 12-15-93 Schuler Added fflush(stdout) before writing to stderr. * 04-19-94 Schuler Open file if not already open, but don't close * 08-23-94 Schuler stdout/stderr output only #ifndef _WINDLL */ NLM_EXTERN void LIBCALL Nlm_ErrLogPrintStr (const char *str) { AppErrInfoPtr info; size_t bytes; if (str == NULL || *str == '\0' || (info = GetAppErrInfo()) == NULL || info->busy & AEI_BUSY_3) return; info->busy |= AEI_BUSY_3; bytes = strlen( str ); #ifdef WIN_DUMB {{ static unsigned long s_ForceBits = EO_ALL_FLAGS; if (s_ForceBits == EO_ALL_FLAGS) { char buf[64]; static const char s_SecretDefault[] = "_NoOneCanGuessThisValue_Duh_"; Nlm_GetEnvParam(0, 0, "DEBUG_PRINTOUT", buf, sizeof(buf), s_SecretDefault); if (Nlm_StrCmp(buf, s_SecretDefault) == 0) { s_ForceBits = 0; } else if (Nlm_StrICmp(buf, "stdout") == 0) { s_ForceBits = EO_LOGTO_STDOUT; } else { s_ForceBits = EO_LOGTO_STDERR; } } if ((s_ForceBits & EO_LOGTO_STDOUT) || TEST_BITS(info,EO_LOGTO_STDOUT)) { fflush(stderr); fwrite(str,1,bytes,stdout); fflush(stdout); } if ((s_ForceBits & EO_LOGTO_STDERR) || TEST_BITS(info,EO_LOGTO_STDERR)) { fflush(stdout); fwrite(str,1,bytes,stderr); fflush(stderr); } }} #endif info->busy &= ~AEI_BUSY_3; if (TEST_BITS(info,EO_LOGTO_TRACE)) { Nlm_TraceStr(str); } if (TEST_BITS(info,EO_LOGTO_USRFILE)) { if (info->fdLog == NULL && info->logfile[0] != '\0') info->fdLog = FileOpen(info->logfile, "a+"); if (info->fdLog != NULL) { fwrite(str, 1, bytes, info->fdLog); fflush(info->fdLog); } } } /*------------------------------------------------------------------------- * ErrSetContext [Schuler, 12-13-93] * * NOTE: Don't call this function directly * * MODIFICATIONS * 02-03-94 Schuler Return 1 if busy */ NLM_EXTERN int LIBCALL Nlm_ErrSetContext (const char *ctx, const char *fname, int line, int db, Nlm_Uint2 entityID, Nlm_Uint4 itemID, Nlm_Uint2 itemtype) { AppErrInfoPtr info = GetAppErrInfo(); if ( info->busy ) return 1; info->desc.module[0] = '\0'; if (ctx != NULL) strncat(info->desc.module,ctx,MODSTR_MAX - 1); info->desc.srcfile[0] = '\0'; if (fname != NULL) { const char *p; if ((p = strrchr(fname,DIRDELIMCHR)) != NULL) ++p; else p = fname; strncat(info->desc.srcfile,p,SRCFILE_MAX - 1); } info->desc.srcline = line; info->desc.entityID = entityID; info->desc.itemID = itemID; info->desc.itemtype = itemtype; info->debug_mode = (unsigned)db; return 0; } /***************************************************************************\ | FETCHING AND REPORTING ERRORS | \***************************************************************************/ /*------------------------------------------------------------------------- * ErrFetch [Schuler] * * MODIFICATIONS * 07-26-93 Schuler Modified to use AppErrInfo struct */ NLM_EXTERN int LIBCALL Nlm_ErrFetch (ErrDesc *err) { if (!Nlm_ErrCopy(err)) return FALSE; ErrClear(); return TRUE; } /*------------------------------------------------------------------------- * ErrCopy [Schuler, 07-26-93] * * MODIFICATIONS: * 12-12-93 Schuler Check info->any_error flag before copying */ NLM_EXTERN int LIBCALL Nlm_ErrCopy (ErrDesc FAR *err) { AppErrInfoPtr info = GetAppErrInfo(); if (info->any_error) { if (err != NULL) memcpy((void*)err,(void*)&(info->desc),sizeof(ErrDesc)); return TRUE; } return FALSE; } /*------------------------------------------------------------------------- * ErrClear [Schuler, 07-26-93] * * MODIFICATIONS * 12-13-93 Schuler Just clear flags instead of zeroing the whole struct. */ NLM_EXTERN void LIBCALL Nlm_ErrClear (void) { AppErrInfoPtr info = GetAppErrInfo(); info->any_error = 0; info->err_formatted = 0; info->debug_mode = 0; info->desc.context = 0; info->desc.entityID = 0; info->desc.itemID = 0; info->desc.itemtype = 0; } /*------------------------------------------------------------------------- * ErrShow [Schuler] * * MODIFICATIONS * 12-13-93 Schuler Rewritten to use msg_level and die_level settings. * 12-14-93 Schuler Now returns the result of Message(). * 12-15-93 Schuler Added proper handling of Abort/Retry/Ignore choice. * 12-21-93 Schuler Added special case for WIN_DUMB + logging to stderr * 12-24-94 Schuler Changed Message to MsgAlert * 12-27-94 Schuler Removed Beep() because MsgAlert is taking care of it * 01-31-94 Schuler Put Beep() back because MsgAlert is no longer doing it. * 02-03-94 Schuler Now honors all EO_MSG_... option flags. * 02-18-94 Schuler Check for NULL user strings before copying. * * TO DO * - allocate buffer for the message */ NLM_EXTERN int LIBCALL Nlm_ErrShow (void) { AppErrInfoPtr info = GetAppErrInfo(); if (info->any_error) { int answer = ANS_OK; int severity = info->desc.severity; if (info->desc.node != NULL && info->desc.node->sev != SEV_NONE) severity = info->desc.node->sev; if (severity >= info->opts.msg_level) { size_t bytes; const char *caption = GetProgramName(); char *message; const char *msgtext; char *p; MsgKey key; /* ----- beep if requested ----- */ if (TEST_BITS(info,EO_BEEP)) Nlm_Beep(); /* ----- set up the buffer to hold the error message ---- */ if (TEST_BITS(info,EO_MSG_MSGTEXT)) msgtext = ErrGetExplanation(info->desc.root,info->desc.node); else msgtext = NULL; /* forget about the options for now -- this is the max we would need*/ bytes = 2 + strlen(info->desc.module) + 32 /* severity string. TEMPORARY (need to keep track of longest string) */ + 2 + strlen(info->desc.codestr) /* error codes */ + 16 + strlen(info->desc.srcfile) /* source file and line number */ + 2 + info->ustrcnt + info->ustrlen /* user strings */ + 2 + strlen(info->desc.errtext) /* error message */ + 2 + ((msgtext) ? strlen(msgtext) : 0); /* verbose message */ if ((message = (char*)Malloc(bytes)) == NULL) message = info->desc.errtext; else { /* ----- format the message in the buffer ----- */ *message = '\0'; p = message; if (TEST_BITS(info,EO_MSG_SEVERITY)) { NlmMutexLockEx( &corelibMutex ); p = strchr(strcpy(p,_szSeverity[severity]),'\0'); NlmMutexUnlock( corelibMutex ); *p++ = ' '; } if (TEST_BITS(info,EO_MSG_CODES)) { p = strchr(strcpy(p,info->desc.module),'\0'); *p++ = ' '; p = strchr(strcpy(p,info->desc.codestr),'\0'); *p++ = ' '; } if (TEST_BITS(info,EO_MSG_FILELINE)) { /*p = strchr(strcpy(p,info->desc.srcfile),'\0');*/ sprintf(p,"{%s line %d} \n",info->desc.srcfile,info->desc.srcline); p = strchr(p,'\0'); } if (TEST_BITS(info,EO_MSG_USERSTR)) { const ValNode *node; for (node=info->desc.userstr; node; node=node->next) { if (node->data.ptrvalue != NULL) { p = strchr(strcpy(p,(char*)node->data.ptrvalue),'\0'); *p++ = ' '; } } } if (TEST_BITS(info,EO_MSG_ERRTEXT)) { p = strchr(strcpy(p,info->desc.errtext),'\0'); } if (TEST_BITS(info,EO_MSG_MSGTEXT) && msgtext!=NULL) { if (p != message) *p++ = '\n'; p = strchr(strcpy(p,msgtext),'\0'); } ASSERT_HARD ( strlen(p) < bytes ); } /* ----- show the message ----- */ if (TEST_BITS(info,EO_PROMPT_ABORT)) key = KEY_ARI; else if (TEST_BITS(info,EO_WAIT_KEY)) key = KEY_OK; else key = KEY_NONE; answer = MsgAlertStr(key,(ErrSev)severity,caption,message); /* ----- clean up ----- */ if (message != info->desc.errtext) Free(message); } /* ----- die if appropriate ----- */ die_if_necessary((ErrSev)severity, info); ErrClear(); return answer; } return 0; } /***************************************************************************\ | MESSAGE FILE SUPPORT | \***************************************************************************/ static FILE * ErrMsgRoot_fopen PROTO((ErrMsgRoot *ctx)); static ErrMsgNode * new_ErrMsgNode PROTO((const char *name, int code, ErrSev sev)); static void delete_ErrMsgNode PROTO((ErrMsgNode *item)); static ErrSev atosev PROTO((const char *sevstr)); static ErrMsgRoot * new_ErrMsgRoot (const char *context) { ErrMsgRoot *idx = (ErrMsgRoot*) MemNew(sizeof(ErrMsgRoot)); if (idx != NULL) idx->name = (context) ? StrSave(context) : 0; return idx; } static void delete_ErrMsgRoot(ErrMsgRoot *idx) { if ( idx->name ) MemFree((void *)idx->name); if ( idx->name ) delete_ErrMsgNode(idx->list); MemFree(idx); } /*------------------------------------------------------------------------- * ErrMsgRoot_fopen [Schuler, 12-09-93] * * Opens an error message file corresponding to an error context. * * MODIFICATIONS * 01-07-94 Schuler Looks in the current directory first. * 01-10-94 Schuler Uses FileOpen() instead of fopen() (now that * reentrancy problem is solved). * 09-08-94 Schuler Don't try to open file if FileLength ==0 * 03-06-95 Schuler Eliminated the mode argument to this function. The * required for proper operation depends on the platform. */ #if defined(OS_MSWIN) || defined(OS_NT) static char * s_msg_mode = "rb"; #else static char * s_msg_mode = "r"; #endif #define MODFNAME_MAX 32 static FILE * ErrMsgRoot_fopen (ErrMsgRoot *ctx) { AppErrInfoPtr info = GetAppErrInfo(); FILE *fd; char file[MODFNAME_MAX]; char path[PATH_MAX]; char *p1 = file; const char *p2 = ctx->name; int i, ch; for (i=0; (ch= *p2++) != 0 && imsgpath); strncat(path,file,sizeof(path)); fd = FileOpen(path,s_msg_mode); } return fd; } /*------------------------------------------------------------------------- * ErrMsgRoot_Read [Schuler, 12-08-93] * * Scans an error message file and builds a data structure that contains * the mappings between integer error codes and strings as well as file * offset for the verbose error explanations so they can later be retrieved * when (or if) needed. * * MODIFICATIONS * 01-07-94 Schuler Fixed bug that resulted in failure to set the text * length for the last item in the file. * 01-10-94 Schuler Added _busy flag to indicate that we are in the process * of parsing the file. Otherwise, there is a problem if * you post an error while attempting to read corelib.msg * (leads to infinite recursion). * 01-10-94 Schuler Added linenumber reporting for all error messages. * 01-23-94 Schuler Changed all ErrPostEx to ErrLogPrintf (don't want to * be posting errors in here -- will recurse ad infinitum). * 01-21-94 Schuler Changed fclose() to FileClose() * 02-02-94 Schuler Revisions for new file format * * TO DO * Check for missing integer code * Capture comments */ static ErrSev atosev (const char *sevstr) { int i; if (sevstr) { for (i=SEV_MIN; i <= SEV_MAX; ++i) { if (strcmp(sevstr,_szSevKey[i]) == 0) return (ErrSev)i; } } return SEV_NONE; } static int ErrMsgRoot_Read (ErrMsgRoot *idx) { FILE *fd; if (idx->list != NULL) return TRUE; if ( idx->busy ) return FALSE; idx->busy = 1; if ((fd = ErrMsgRoot_fopen(idx)) == NULL) { idx->busy = 0; return FALSE; } {{ char line[80], *p; int linenum = 0; long tofs = 0, cofs = 0, tmpofs; ErrMsgNode *lev1; ErrMsgNode *lev2; int any_text; int any_comt; /* ----- Look for MODULE line ----- */ while (Nlm_FileGets(line,sizeof line,fd)) { linenum++; if ( strchr("#\r\n", line[0]) ) continue; if (strncmp(line,"MODULE",6) == 0) { p = strtok(line," \t\r\n"); p = strtok(NULL," \t\r\n"); if (strcmp(idx->name,p) !=0) ErrLogPrintf("Context string mismatch (%s vs %s)\n",idx->name,p); break; } else { idx->busy = 0; ErrLogPrintf("Syntax error: \"MODULE\" expected, line %d\n",linenum); return FALSE; } } /* ----- Process the rest of the file ----- */ any_text = any_comt = FALSE; lev1 = lev2 = NULL; tmpofs = ftell(fd); while (Nlm_FileGets(line,sizeof line,fd)) { linenum++; if (line[0] == '$') { if (any_text) { ErrMsgNode *n = lev2 ? lev2 : lev1; if (n != NULL) { n->tofs = tofs; n->tlen = tmpofs - tofs; } any_text = FALSE; } } if (line[0]=='$' && line[1]=='$') { /*** NEED ERROR CHECKING HERE ***/ char *tok1 = strtok(line+2,", \t\r\n"); char *tok2 = strtok(NULL,", \t\r\n"); char *tok3 = strtok(NULL,", \t\r\n"); ASSERT(tok1 && tok2); lev2 = NULL; lev1 = new_ErrMsgNode(tok1,atoi(tok2),atosev(tok3)); if (idx->list == NULL) idx->list = lev1; else { ErrMsgNode *t0=NULL, *t1; for (t1=idx->list; TRUE; t1=t1->next) { if (t1==NULL || lev1->code < t1->code) { if (t0 == NULL) idx->list = lev1; else t0->next = lev1; lev1->next = t1; break; } if (lev1->code == t1->code) { ErrLogPrintf("Code %d duplicated, line %d\n",t1->code,linenum); break; } t0 = t1; } } /*any_text = FALSE;*/ } else if (line[0]=='$' && line[1]=='^') { /*** NEED ERROR CHECKING HERE ***/ char *tok1 = strtok(line+2,", \t\r\n"); char *tok2 = strtok(NULL,", \t\r\n"); char *tok3 = strtok(NULL,", \t\r\n"); ASSERT(tok1 && tok2); lev2 = new_ErrMsgNode(tok1,atoi(tok2),atosev(tok3)); if (lev1->list == NULL) lev1->list = lev2; else { ErrMsgNode *t0=NULL, *t1; for (t1=lev1->list; TRUE; t1=t1->next) { if (t1==NULL || lev2->code < t1->code) { if (t0 == NULL) lev1->list = lev2; else t0->next = lev2; lev2->next = t1; break; } if (lev2->code == t1->code) { ErrLogPrintf("Code %d duplicated, line %d\n",t1->code,linenum); break; } t0 = t1; } } /*any_text = FALSE;*/ } else if (line[0] == '#') { if (!any_comt) { cofs = tmpofs; any_comt = TRUE; } } else { if (any_comt) { ErrMsgNode *n = lev2 ? lev2 : lev1; if (n != NULL) { n->cofs = cofs; n->clen = tmpofs - cofs; } } if (!any_text) { /* if (not a blank line) */ tofs = tmpofs; any_text = TRUE; } } tmpofs = ftell(fd); } if (any_text) { ErrMsgNode *n = lev2 ? lev2 : lev1; if (n != NULL) { n->tofs = tofs; n->tlen = tmpofs - tofs; } } FileClose(fd); }} idx->busy = 0; return TRUE; } /*------------------------------------------------------------------------- * new_ErrMsgNode, delete_ErrMsgNode * Constructor/Destructor for ErrMsgNode */ static ErrMsgNode * new_ErrMsgNode(const char *name, int code, ErrSev sev) { ErrMsgNode *item = (ErrMsgNode*)MemNew(sizeof(ErrMsgNode)); if ( item ) { item->name = (name) ? StrSave(name) : 0; item->code = code; item->sev = sev; } return item; } static void delete_ErrMsgNode(ErrMsgNode *item) { ErrMsgNode *next; while ( item ) { next = item->next; delete_ErrMsgNode(item->list); MemFree((void *)item->name); MemFree((void *)item->tstr); MemFree((void *)item->cstr); MemSet(item, '\xFF', sizeof(*item)); MemFree(item); item = next; } } /*------------------------------------------------------------------------- * ErrGetMsgRoot [Schuler, 12-09-93] * * Gets the index structure for an error context, creating and initializing * it if necessary. * * MODIFICATIONS * 01-10-94 Schuler Changed to call ErrMsgRoot_Read() *after* linking * into list. Otherwise, if an error is posted from * within ErrMsgRoot_Read(), it won't find the CoreLib * modlue and will read it again (ad infinitum). */ NLM_EXTERN ErrMsgRootPtr LIBCALL ErrGetMsgRoot (const char *context) { AppErrInfoPtr info = GetAppErrInfo(); ErrMsgRoot *idx, *idx0=NULL; int d; for (idx=info->idxlist; TRUE; idx=idx->next) { if (idx==NULL || (d = strcmp(idx->name,context)) > 0) { ErrMsgRoot *idx2 = new_ErrMsgRoot(context); idx2->next = idx; if (idx0 == NULL) info->idxlist = idx2; else idx0->next = idx2; ErrMsgRoot_Read(idx2); return (ErrMsgRootPtr) idx2; } if (d == 0) break; idx0 = idx; } return (ErrMsgRootPtr)idx; } /*------------------------------------------------------------------------- * ErrGetExplanation [Schuler, 12-09-93] * * Gets the explanatory text for a particular error from an error * message file. * * MODIFICATIONS * 02-02-94 Schuler Fix for memory leak when length==0 * 02-02-94 Schuler Now exported (was static) */ NLM_EXTERN const char* LIBCALL ErrGetExplanation (ErrMsgRootPtr idx, ErrMsgNodePtr item) { if (idx != NULL && item != NULL && item->tlen >0) { size_t bytes; char *text; if (item->tstr != NULL) return item->tstr; bytes = (size_t)item->tlen; if ((text=(char *)MemNew(bytes+1)) != NULL) { FILE *fd = ErrMsgRoot_fopen((ErrMsgRoot *) idx); if (fd != NULL) { if (fseek(fd,item->tofs,SEEK_SET) !=0) goto ErrReturn; if (fread(text,1,bytes,fd) != bytes) goto ErrReturn; fclose(fd); *(text+bytes) = '\0'; /* make sure null-terminated */ ((ErrMsgNode*)item)->tstr = text; return text; } ErrReturn : fclose(fd); MemFree(text); return NULL; } } return NULL; } /***************************************************************************\ | CUSTOMIZATION | \***************************************************************************/ /*------------------------------------------------------------------------- * ErrSetLogfile [Schuler] * * MODIFICATIONS * 07-26-93 Schuler Modified to use AppErrInfo struct * 12-17-93 Schuler Changed back to returning a Boolean * 01-21-94 Schuler Renamed & added flags argument. * 01-21-94 Schuler Changed fopen/fclose to FileOpen/FileClose * 04-19-94 Schuler Only open * * TO DO * - honor ELOG_NOCREATE flag */ NLM_EXTERN int LIBCALL Nlm_ErrSetLogfile (const char *filename, unsigned long flags) { static char *fmode_append = "a+"; static char *fmode_overwrite = "w"; AppErrInfoPtr info = GetAppErrInfo(); if (info->fdLog != NULL) /* close if already open */ { FileClose(info->fdLog); info->fdLog = NULL; } if (filename == NULL) info->logfile[0] = '\0'; else { if ( !(flags & ELOG_NOCREATE) ) { char *fmode = (flags & ELOG_APPEND) ? fmode_append : fmode_overwrite; FILE *fp; if ((fp = FileOpen(filename,fmode)) == NULL) return FALSE; if (flags & ELOG_BANNER) { char buffer[64]; Nlm_DayTimeStr(buffer,TRUE,TRUE); fprintf(fp, "\n========================[ %s ]========================\n", buffer); } FileClose(fp); } strncpy(info->logfile,filename,PATH_MAX - 1); info->logfile[PATH_MAX - 1] = '\0'; } return TRUE; } const char* LIBCALL Nlm_ErrGetLogfile( void ) { AppErrInfoPtr info = GetAppErrInfo(); if (info->logfile != NULL && *info->logfile == '\0') return NULL; return info->logfile; } /*------------------------------------------------------------------------- * ErrSetHandler [Schuler, 05-11-93] * * Allows the application to set a hook procedure that will be called * when an error is posted via ErrPost. It is always called regardless * of any error reporting/logging options that may have been set. The * return value is the pointer to the previous error hook procedure * if there was one. * * MODIFICATIONS * 05-21-93 Schuler */ NLM_EXTERN ErrHookProc LIBCALL Nlm_ErrSetHandler (ErrHookProc hookNew) { AppErrInfoPtr info = GetAppErrInfo(); ErrHookProc hookOld =info->hook; info->hook = hookNew; return hookOld; } NLM_EXTERN Nlm_Boolean LIBCALL Nlm_CallErrHandlerOnly(Nlm_Boolean hook_only) { Nlm_Boolean prev; NlmMutexLockEx(&corelibMutex); prev = s_HookOnly; s_HookOnly = hook_only; NlmMutexUnlock(corelibMutex); return prev; } /*------------------------------------------------------------------------- * ErrUserInstall [Sirotkin] * * Add or replace a user-string. * * MODIFICATIONS * 12-15-93 Schuler No longer keeps track of string lengths. */ static char *_strNull = "(null)"; NLM_EXTERN ErrStrId LIBCALL Nlm_ErrUserInstall (const char *msg, ErrStrId magic_cookie) { AppErrInfoPtr info = GetAppErrInfo(); ValNode *list = (ValNode *) info->desc.userstr; ValNode *node; ErrStrId cookie; if (msg == NULL) msg = _strNull; if (magic_cookie) { for (node=list; node; node=node->next) { if (node->choice == magic_cookie) { /** replace **/ info->ustrlen -= strlen((char*)node->data.ptrvalue); MemFree(node->data.ptrvalue); node->data.ptrvalue = (void*) StringSave(msg); info->ustrlen += strlen(msg); return magic_cookie; } } ErrPostEx(SEV_WARNING,-1,0, "ErrUserInstall: bad string id (%d)", (int) magic_cookie); return 0; } else { for (cookie = 1; cookie < 255; ++cookie) { for (node=list; node; node=node->next) { if (node->choice == cookie) break; } if (node==NULL) /* free cookie is magic */ { node = ValNodeNew((ValNode*)info->desc.userstr); if ( ! info->desc.userstr) info->desc.userstr = node; node->choice = cookie; node->data.ptrvalue = StringSave(msg); info->ustrlen += strlen(msg); info->ustrcnt ++; return cookie; } } /*ErrPostEx(SEV_WARNING,-1,0,"ErrUserInstall: no more string id's");*/ return 0; } } /*------------------------------------------------------------------------- * ErrUserDelete [Sirotkin] * * Delete a single user-string. * * MODIFICATIONS * 12-15-93 Schuler No longer keeps track of string lengths. */ NLM_EXTERN Nlm_Boolean LIBCALL Nlm_ErrUserDelete (ErrStrId magic_cookie) { AppErrInfoPtr info = GetAppErrInfo(); ValNodePtr node = ValNodeExtract((ValNode**)(&info->desc.userstr), magic_cookie); if (node) { ASSERT(node->data.ptrvalue != NULL); info->ustrlen -= strlen((char*)node->data.ptrvalue); info->ustrcnt --; ValNodeFreeData(node); return TRUE; } return FALSE; } /*------------------------------------------------------------------------- * ErrUserClear [Sirotkin] * * Deletes the entire list of user-strings. */ NLM_EXTERN void LIBCALL Nlm_ErrUserClear (void) { AppErrInfoPtr info = GetAppErrInfo(); ValNodeFreeData((ValNode*)info->desc.userstr); info->ustrlen = 0; info->ustrcnt = 0; } /************************************************************************** * * ErrGetUserStrings(ErrDescPtr err) * returns ValNodePtr to chain of user strings for this thread * ***************************************************************************/ NLM_EXTERN ValNodePtr LIBCALL Nlm_ErrGetUserStrings(ErrDescPtr err) { ValNodePtr vnp = NULL; if (err != NULL) vnp = (ValNodePtr)(err->userstr); return vnp; } /*------------------------------------------------------------------------- * ErrGetLogLevel, ErrSetLogLevel [Schuler, 12-22-93] * * Get/Set the minimum severity level that will be logged. */ NLM_EXTERN ErrSev LIBCALL ErrSetLogLevel (ErrSev level) { AppErrInfoPtr info = GetAppErrInfo(); ErrSev prev = (ErrSev) info->opts.log_level; info->opts.log_level = (short) MAX(SEV_MIN,MIN(level,SEV_MAX)); return prev; } NLM_EXTERN ErrSev LIBCALL ErrGetLogLevel (void) { return (ErrSev)GetAppErrInfo()->opts.log_level; } /*------------------------------------------------------------------------- * ErrGetMessageLevel, ErrSetMessageLevel [Schuler, 12-14-93] * * Get/Set the minimum severity level that will be reported via Message(). */ NLM_EXTERN ErrSev LIBCALL ErrSetMessageLevel (ErrSev level) { AppErrInfoPtr info = GetAppErrInfo(); ErrSev prev = (ErrSev) info->opts.msg_level; info->opts.msg_level = (short) MAX(SEV_MIN,MIN(level,SEV_MAX)); return prev; } NLM_EXTERN ErrSev LIBCALL ErrGetMessageLevel (void) { return (ErrSev) GetAppErrInfo()->opts.msg_level; } /*------------------------------------------------------------------------- * ErrGetFatalLevel, ErrSetFatalLevel [Schuler, 12-14-93] * * Get/Set the minimum severity level that will cause an abnormal exit. */ NLM_EXTERN ErrSev LIBCALL ErrSetFatalLevel (ErrSev level) { AppErrInfoPtr info = GetAppErrInfo(); ErrSev prev = (ErrSev) info->opts.die_level; info->opts.die_level = (short) MAX(SEV_MIN,MIN(level,SEV_MAX)); return prev; } NLM_EXTERN ErrSev LIBCALL ErrGetFatalLevel (void) { return (ErrSev) GetAppErrInfo()->opts.die_level; } /*------------------------------------------------------------------------- * ErrSetOptFlags, ErrClearOptFlags, ErrTestOptFlags [Schuler, 12-14-93] * * Set, clear, or test the current state of any error option flag. */ NLM_EXTERN unsigned long LIBCALL ErrSetOptFlags (unsigned long flags) { AppErrInfoPtr info = GetAppErrInfo(); info->opts.flags |= (flags & EO_ALL_FLAGS); return info->opts.flags; } NLM_EXTERN unsigned long LIBCALL ErrClearOptFlags (unsigned long flags) { AppErrInfoPtr info = GetAppErrInfo(); info->opts.flags &= ~(flags & EO_ALL_FLAGS); return info->opts.flags; } NLM_EXTERN unsigned long LIBCALL ErrTestOptFlags (unsigned long flags) { AppErrInfoPtr info = GetAppErrInfo(); return (info->opts.flags & flags); } /*------------------------------------------------------------------------- * ErrSaveOptions [Schuler, 01-03-94] * */ NLM_EXTERN void LIBCALL ErrSaveOptions (ErrOpts *opts) { AppErrInfoPtr info = GetAppErrInfo(); ASSERT(opts != NULL); memcpy((void*)opts,(void*)(&info->opts),sizeof(ErrOpts)); } /*------------------------------------------------------------------------- * ErrRestoreOptions [Schuler, 01-03-94] */ NLM_EXTERN void LIBCALL ErrRestoreOptions (const ErrOpts *opts) { AppErrInfoPtr info = GetAppErrInfo(); ASSERT(opts != NULL); memcpy((void*)(&info->opts),(void*)opts,sizeof(ErrOpts)); } /*------------------------------------------------------------------------- * ErrGetOpts [Schuler] * * MODIFICATIONS * 07-26-93 Schuler Modified to use AppErrInfo struct * 02-02-94 Schuler Added local option saving strategy */ NLM_EXTERN void LIBCALL Nlm_ErrGetOpts (short * erract, short * errlog) { AppErrInfoPtr info = GetAppErrInfo(); if (erract != NULL) { size_t i; for (i=0; i_eo_save); ++i) { if (info->_eo_save[i].flags ==0) break; } if (i_eo_save)) { info->_eo_save[i] = info->opts; *erract = (short)(-1-i); } else { TRACE("ErrGetOpts: overflow\n"); *erract = info->opts.actopt; } } if (errlog != NULL) *errlog = info->opts.logopt; } /*------------------------------------------------------------------------- * ErrSetOpts [Schuler] * * MODIFICATIONS * 07-26-93 Schuler Modified to use AppErrInfo struct * 12-15-93 Schuler Modified to map old settings to the new options. * 12-21-93 Schuler Now always starts from defaults and then twiddles. * 01-27-94 Schuler Fixed bug whereby logopt not always saved. * 02-02-94 Schuler Added local option saving strategy */ NLM_EXTERN void LIBCALL Nlm_ErrSetOpts (short actopt, short logopt) { AppErrInfoPtr info = GetAppErrInfo(); if (actopt < 0) { int i = -(actopt+1); ASSERT(i>=0 && i_eo_save)); if (info->_eo_save[i].flags !=0) { info->opts = info->_eo_save[i]; info->_eo_save[i].flags =0; return; } ErrLogPrintf("*** Improper use of ErrGetOpts/ErrSetOpts ***\n"); } /****info->opts.flags = EO_DEFAULTS; info->opts.log_level = SEV_INFO;****/ if (actopt !=0) info->opts.flags &= ~EO_PROMPT_ABORT; switch (actopt) { case ERR_CONTINUE: case ERR_IGNORE: info->opts.msg_level = SEV_MAX; info->opts.die_level = SEV_MAX; break; case ERR_TEE: info->opts.msg_level = SEV_WARNING; info->opts.die_level = SEV_MAX; break; case ERR_ADVISE: info->opts.msg_level = SEV_WARNING; info->opts.die_level = SEV_MAX; break; case ERR_ABORT: info->opts.msg_level = SEV_WARNING; info->opts.die_level = SEV_FATAL; break; case ERR_PROMPT: info->opts.msg_level = SEV_WARNING; info->opts.die_level = SEV_FATAL; info->opts.flags |= EO_PROMPT_ABORT; break; } if (actopt != 0) info->opts.actopt = actopt; switch(logopt) { case ERR_LOG_ON: info->opts.flags |= EO_LOGTO_USRFILE; break; case ERR_LOG_OFF: info->opts.flags &= ~EO_LOGTO_USRFILE; break; default: logopt = 0; } if (logopt != 0) info->opts.logopt = logopt; /***** if anybody liked this, I can put it back... if (logopt != 0) { int i; FILE *fp; if ((fp = FileOpen(info->logfile, "a+")) != NULL) { fputc ('\n', fp); for (i=0; i<4; i++) fputc (' ', fp); for (i=0; i<21; i++) fputc ('-', fp); fprintf (fp, " error logging %sabled ", logopt==ERR_LOG_ON ? " en" : "dis"); for (i=0; i<21; i++) fputc ('-', fp); fputc ('\n', fp); FileClose(fp); } } *****/ } /***************************************************************************\ | DEBUGGING | \***************************************************************************/ /*------------------------------------------------------------------------- * Trace [Schuler, 04-13-93] * * Formats a string and sends it to the "trace device" (see the * description of Nlm_TraceStr about "trace device"). Normally, it * is desirable to trace some (possibly verbose) informational messages * during the development phase of a program, but inhibit them in the * final version that is released to end users. The TRACE macro supports * this style of usage, calling Nlm_Trace only if _DEBUG is defined. * For example: * * TRACE("!@#$ screwed up (%s,%d)\n",__FILE__,___LINE__); * * Note that it is declared as CDECL (because of the variable argument * list) and may therefore not be callable from some other programming * languages, such as Basic and Pascal. * * MODIFICATIONS * Schuler 07-26-93 * Schuler 12-14-93 Merged varargs and stdargs versions of the function */ #ifdef VAR_ARGS NLM_EXTERN void Nlm_Trace (fmt, va_alist) const char *fmt; va_dcl #else NLM_EXTERN void CDECL Nlm_Trace (const char *fmt, ...) #endif { const Nlm_Char PNTR scratch_str = NULL; TSPRINTF(scratch_str, fmt); if (scratch_str != NULL) Nlm_TraceStr( scratch_str ); } /*------------------------------------------------------------------------- * TraceStr [Schuler, 04-13-93] * * Users of the C and C++ should use the TRACE macro and not call this * function directly. This function, unlike Nlm_Trace, it may be called * other programming languages, such as Basic and Pascal. * * MODIFICATIONS * 05-26-93 Schuler * 12-15-93 Schuler Changed to use TRACE_TO_FILE macro. * 12-15-93 Schuler Removed tracing to stderr (caused an echo echo ...). * 01-21-94 Schuler Changed fopen/fclose to FileOpen/FileClose */ NLM_EXTERN void LIBCALL Nlm_TraceStr (const char *str) { if (str==NULL) return; #ifdef TRACE_TO_STDOUT fprintf(stdout,"%s",str); #endif #ifdef TRACE_TO_AUX #ifdef OS_MSWIN OutputDebugString(str); #endif #endif /* TRACE_TO_AUX */ #ifdef TRACE_TO_FILE { FILE *fd = FileOpen("trace.log","a+"); if (fd != NULL) { fprintf(fd,"%s",str); FileClose(fd); } } #endif /* TRACE_TO_FILE */ } /*------------------------------------------------------------------------- * Nlm_AssertionFailed [Schuler, 04-13-93] * * Function needed to support the ASSERT and VERIFY macros. * * MODIFICATIONS * 05-11-93 Schuler * 12-15-93 Schuler Use ErrLogPrintf() inseead of Nlm_Trace() * 12-15-93 Schuler Added call to Message() so user knows what happened! * 01-21-94 Schuler Changed MSG_OK to MSG_POST */ NLM_EXTERN void LIBCALL Nlm_AssertionFailed (const char *expression, const char *module, const char *fname, int linenum) { ErrLogPrintf("\nAssertion Failed: %s\n", expression); if (module && *module) ErrLogPrintf(" %s",module); if ( fname ) ErrLogPrintf(" %s, line %d", fname, linenum); ErrLogPrintStr("\n"); Message(MSG_POST, "Assertion Failed:\n%s", expression); if (module && *module) Message(MSG_POST, "Module \"%s\"", module); if ( fname ) Message(MSG_POST, "File \"%s\", line %d", fname, linenum); AbnormalExit(1); } /*------------------------------------------------------------------------- * AbnormalExit [Schuler, 04-13-93] * * Terminates the application immediately. This should only be done * as a last resort since some (possibly necessary) cleanup code will * not get executed. * * MODIFICATIONS * Schuler 05-11-93 * Schuler 07-26-93 */ NLM_EXTERN void LIBCALL Nlm_AbnormalExitPure(int code) { #if defined(_DEBUG) abort(); #elif defined(OS_MAC) ExitToShell(); #else exit( code ); #endif } NLM_EXTERN void LIBCALL Nlm_AbnormalExit (int code) { static Nlm_Boolean s_Exiting = FALSE; if ( !s_Exiting ) { s_Exiting = TRUE; ErrLogPrintStr( "\n***** ABNORMAL PROGRAM EXIT *****\n" ); } Nlm_AbnormalExitPure( code ); } /***************************************************************************\ | STATIC HELPER FUNCTIONS | \***************************************************************************/ /*------------------------------------------------------------------------- * GetAppErrInfo [Schuler, 07-26-93] * * MODIFICATIONS * 12-13-93 Schuler Added initialization for new settings. * 01-04-94 Schuler Added code to get settings from ncbi.ini * 01-27-94 Schuler Set & clear _busy when reading from ncbi.ini * 02-01-94 Schuler Updated _flag array */ static char * _file = "ncbi"; static char * _section = "ErrorProcessing"; struct FlagInf { char *key; unsigned long bits; }; static struct FlagInf _flag [] = { { "EO_LOG_SEVERITY", EO_LOG_SEVERITY }, { "EO_LOG_CODES", EO_LOG_CODES }, { "EO_LOG_FILELINE", EO_LOG_FILELINE }, { "EO_LOG_USERSTR", EO_LOG_USERSTR }, { "EO_LOG_ERRTEXT", EO_LOG_ERRTEXT }, { "EO_LOG_MSGTEXT", EO_LOG_MSGTEXT }, { "EO_MSG_SEVERITY", EO_MSG_SEVERITY }, { "EO_MSG_CODES", EO_MSG_CODES }, { "EO_MSG_FILELINE", EO_MSG_FILELINE }, { "EO_MSG_USERSTR", EO_MSG_USERSTR }, { "EO_MSG_ERRTEXT", EO_MSG_ERRTEXT }, { "EO_MSG_MSGTEXT", EO_MSG_MSGTEXT }, { "EO_LOGTO_STDOUT", EO_LOGTO_STDOUT }, { "EO_LOGTO_STDERR", EO_LOGTO_STDERR }, { "EO_LOGTO_TRACE", EO_LOGTO_TRACE }, { "EO_LOGTO_USRFILE", EO_LOGTO_USRFILE }, { "EO_XLATE_CODES", EO_XLATE_CODES }, { "EO_WAIT_KEY", EO_WAIT_KEY }, { "EO_PROMPT_ABORT", EO_PROMPT_ABORT }, { "EO_BEEP", EO_BEEP } }; static int _nAppErrInfo = 0; extern void ReleaseAppErrInfo( void ) { AppErrInfoPtr info = (AppErrInfoPtr)GetAppProperty( _szPropKey ); if (info == NULL) return; NlmMutexLockEx( &corelibMutex ); ASSERT ( _nAppErrInfo > 0 ); if (--_nAppErrInfo == 0) { int i; for (i=SEV_MIN; i<=SEV_MAX; ++i) { Nlm_MemFree( _szSeverity[i] ); _szSeverity[i] = NULL; } } if (info->fdLog != NULL) { FileClose( info->fdLog ); } while ( info->idxlist ) { ErrMsgRoot *idx = info->idxlist; info->idxlist = idx->next; delete_ErrMsgRoot(idx); } Nlm_ErrUserClear(); Nlm_MemFill(info, '\xFF', sizeof(*info)); Nlm_MemFree( info ); RemoveAppProperty( _szPropKey ); NlmMutexUnlock( corelibMutex ); } static struct AppErrInfo * GetAppErrInfo(void) { AppErrInfoPtr info = (AppErrInfoPtr) GetAppProperty(_szPropKey); if (info == NULL) { char buffer[128], *p; int i; int severity_init = 0; NlmMutexLockEx( &corelibMutex ); info = (struct AppErrInfo*) MemGet(sizeof(struct AppErrInfo), TRUE); if (info == NULL) Message(MSG_FATAL,"Out of memory"); info->opts.actopt = ERR_ABORT; /* OBSOLETE */ info->opts.logopt = ERR_LOG_OFF; /* OBSOLETE */ info->opts.flags = EO_DEFAULTS; info->opts.log_level = SEV_INFO; #ifdef _DEBUG info->opts.msg_level = SEV_WARNING; #else info->opts.msg_level = SEV_ERROR; #endif info->opts.die_level = SEV_FATAL; strcpy(info->logfile,"error.log"); for (i = SEV_MIN; i <= SEV_MAX; i++) { if (_szSeverity[i] == NULL) { severity_init = 1; sprintf(buffer, "*SEV*%d", i); _szSeverity[i] = StrSave( buffer ); } } ++_nAppErrInfo; SetAppProperty(_szPropKey, (void*)info); if (GetAppParam(_file, _section, "MsgPath", "", info->msgpath, PATH_MAX-2)) FileBuildPath(info->msgpath, NULL, NULL); if ( severity_init ) { for (i = SEV_MIN; i <= SEV_MAX; i++) { GetAppParam(_file, _section, _szSevKey[i], _szSevDef[i], buffer,sizeof buffer); if (buffer[0] == '"') { if ((p = strchr(&buffer[1],'"')) != NULL) *p = '\0'; p = &buffer[1]; } else p = buffer; {{ char *dummy_sev = _szSeverity[i]; _szSeverity[i] = StrSave(p); Nlm_MemFree( dummy_sev ); }} } } NlmMutexUnlock( corelibMutex ); for (i=0; iini_mask |= _flag[i].bits; if (strchr("1TtYy",buffer[0])) info->ini_bits |= _flag[i].bits; } } SetAppProperty(_szPropKey,(void*)info); } return info; } /*------------------------------------------------------------------------- * ErrPathReset [Kans] * * Resets info->msgpath */ NLM_EXTERN void LIBCALL Nlm_ErrPathReset (void) { AppErrInfoPtr info = GetAppErrInfo(); if (GetAppParam(_file, _section, "MsgPath", "", info->msgpath, PATH_MAX-2)) FileBuildPath(info->msgpath, NULL, NULL); } NLM_EXTERN char *Nlm_GetErrLongText (char *module, int errcode, int subcode) { ErrMsgRoot *root = NULL; ErrMsgNode *node1 = NULL; ErrMsgNode *node2 = NULL; ErrMsgNode *the_node = NULL; char *text = NULL; if (module != NULL) { root = (ErrMsgRoot*) ErrGetMsgRoot(module); for (node1=root->list; node1; node1=node1->next) { if (node1->code == errcode) { for (node2=node1->list; node2; node2=node2->next) if (node2->code == subcode) break; break; } } } the_node = node2 ? node2 : node1; text = (char *) ErrGetExplanation(root, the_node); return text; }