00001 /********************************************************************** 00002 * $Id: cpl_error_cpp-source.html,v 1.13 2002/12/21 19:13:12 warmerda Exp $ 00003 * 00004 * Name: cpl_error.cpp 00005 * Project: CPL - Common Portability Library 00006 * Purpose: Error handling functions. 00007 * Author: Daniel Morissette, danmo@videotron.ca 00008 * 00009 ********************************************************************** 00010 * Copyright (c) 1998, Daniel Morissette 00011 * 00012 * Permission is hereby granted, free of charge, to any person obtaining a 00013 * copy of this software and associated documentation files (the "Software"), 00014 * to deal in the Software without restriction, including without limitation 00015 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00016 * and/or sell copies of the Software, and to permit persons to whom the 00017 * Software is furnished to do so, subject to the following conditions: 00018 * 00019 * The above copyright notice and this permission notice shall be included 00020 * in all copies or substantial portions of the Software. 00021 * 00022 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00023 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00024 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00025 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00026 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00027 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00028 * DEALINGS IN THE SOFTWARE. 00029 ********************************************************************** 00030 * 00031 * $Log: cpl_error_cpp-source.html,v $ 00031 * Revision 1.13 2002/12/21 19:13:12 warmerda 00031 * updated 00031 * 00032 * Revision 1.23 2002/10/23 20:19:37 warmerda 00033 * Modify log file naming convention as per patch from Dale. 00034 * 00035 * Revision 1.22 2002/08/01 20:02:54 warmerda 00036 * added CPL_LOG_ERRORS support 00037 * 00038 * Revision 1.21 2001/12/14 19:45:17 warmerda 00039 * Avoid use of errno in prototype. 00040 * 00041 * Revision 1.20 2001/11/27 17:01:06 warmerda 00042 * added timestamp to debug messages 00043 * 00044 * Revision 1.19 2001/11/15 16:11:08 warmerda 00045 * use vsnprintf() for debug calls if it is available 00046 * 00047 * Revision 1.18 2001/11/02 22:07:58 warmerda 00048 * added logging error handler 00049 * 00050 * Revision 1.17 2001/07/18 04:00:49 warmerda 00051 * added CPL_CVSID 00052 * 00053 * Revision 1.16 2001/02/15 16:30:57 warmerda 00054 * fixed initialization of fpLog 00055 * 00056 * Revision 1.15 2001/01/19 21:16:41 warmerda 00057 * expanded tabs 00058 * 00059 * Revision 1.14 2000/11/30 17:30:10 warmerda 00060 * added CPLGetLastErrorType 00061 * 00062 * Revision 1.13 2000/03/31 14:37:48 warmerda 00063 * only use vsnprintf where available 00064 * 00065 * Revision 1.12 2000/03/31 14:11:55 warmerda 00066 * added CPLErrorV 00067 * 00068 * Revision 1.11 2000/01/10 17:35:45 warmerda 00069 * added push down stack of error handlers 00070 * 00071 * Revision 1.10 1999/11/23 04:16:56 danmo 00072 * Fixed var. initialization that failed to compile as C 00073 * 00074 * Revision 1.9 1999/09/03 17:03:45 warmerda 00075 * Completed partial help line. 00076 * 00077 * Revision 1.8 1999/07/23 14:27:47 warmerda 00078 * CPLSetErrorHandler returns old handler 00079 * 00080 * Revision 1.7 1999/06/27 16:50:52 warmerda 00081 * added support for CPL_DEBUG and CPL_LOG variables 00082 * 00083 * Revision 1.6 1999/06/26 02:46:11 warmerda 00084 * Fixed initialization of debug messages. 00085 * 00086 * Revision 1.5 1999/05/20 14:59:05 warmerda 00087 * added CPLDebug() 00088 * 00089 * Revision 1.4 1999/05/20 02:54:38 warmerda 00090 * Added API documentation 00091 * 00092 * Revision 1.3 1998/12/15 19:02:27 warmerda 00093 * Avoid use of errno as a variable 00094 * 00095 * Revision 1.2 1998/12/06 02:52:52 warmerda 00096 * Implement assert support 00097 * 00098 * Revision 1.1 1998/12/03 18:26:02 warmerda 00099 * New 00100 * 00101 **********************************************************************/ 00102 00103 #include "cpl_error.h" 00104 #include "cpl_vsi.h" 00105 00106 #define TIMESTAMP_DEBUG 00107 #ifdef TIMESTAMP_DEBUG 00108 #include <time.h> 00109 #endif 00110 00111 CPL_CVSID("$Id: cpl_error_cpp-source.html,v 1.13 2002/12/21 19:13:12 warmerda Exp $"); 00112 00113 /* static buffer to store the last error message. We'll assume that error 00114 * messages cannot be longer than 2000 chars... which is quite reasonable 00115 * (that's 25 lines of 80 chars!!!) 00116 */ 00117 static char gszCPLLastErrMsg[2000] = ""; 00118 static int gnCPLLastErrNo = 0; 00119 static CPLErr geCPLLastErrType = CE_None; 00120 00121 static CPLErrorHandler gpfnCPLErrorHandler = CPLDefaultErrorHandler; 00122 00123 typedef struct errHandler 00124 { 00125 struct errHandler *psNext; 00126 CPLErrorHandler pfnHandler; 00127 } CPLErrorHandlerNode; 00128 00129 static CPLErrorHandlerNode * psHandlerStack = NULL; 00130 00131 /********************************************************************** 00132 * CPLError() 00133 **********************************************************************/ 00134 00167 void CPLError(CPLErr eErrClass, int err_no, const char *fmt, ...) 00168 { 00169 va_list args; 00170 00171 /* Expand the error message 00172 */ 00173 va_start(args, fmt); 00174 CPLErrorV( eErrClass, err_no, fmt, args ); 00175 va_end(args); 00176 } 00177 00178 /************************************************************************/ 00179 /* CPLErrorV() */ 00180 /************************************************************************/ 00181 00182 void CPLErrorV(CPLErr eErrClass, int err_no, const char *fmt, va_list args ) 00183 { 00184 /* Expand the error message 00185 */ 00186 #if defined(HAVE_VSNPRINTF) 00187 vsnprintf(gszCPLLastErrMsg, sizeof(gszCPLLastErrMsg), fmt, args); 00188 #else 00189 vsprintf(gszCPLLastErrMsg, fmt, args); 00190 #endif 00191 00192 /* If the user provided his own error handling function, then call 00193 * it, otherwise print the error to stderr and return. 00194 */ 00195 gnCPLLastErrNo = err_no; 00196 geCPLLastErrType = eErrClass; 00197 00198 if( getenv("CPL_LOG_ERRORS") != NULL ) 00199 CPLDebug( "CPLError", "%s", gszCPLLastErrMsg ); 00200 00201 if( gpfnCPLErrorHandler ) 00202 gpfnCPLErrorHandler(eErrClass, err_no, gszCPLLastErrMsg); 00203 00204 if( eErrClass == CE_Fatal ) 00205 abort(); 00206 } 00207 00208 /************************************************************************/ 00209 /* CPLDebug() */ 00210 /************************************************************************/ 00211 00233 void CPLDebug( const char * pszCategory, const char * pszFormat, ... ) 00234 00235 { 00236 char *pszMessage; 00237 va_list args; 00238 const char *pszDebug = getenv("CPL_DEBUG"); 00239 00240 #define ERROR_MAX 25000 00241 00242 /* -------------------------------------------------------------------- */ 00243 /* Does this message pass our current criteria? */ 00244 /* -------------------------------------------------------------------- */ 00245 if( pszDebug == NULL ) 00246 return; 00247 00248 if( !EQUAL(pszDebug,"ON") && !EQUAL(pszDebug,"") ) 00249 { 00250 int i, nLen = strlen(pszCategory); 00251 00252 for( i = 0; pszDebug[i] != '\0'; i++ ) 00253 { 00254 if( EQUALN(pszCategory,pszDebug+i,nLen) ) 00255 break; 00256 } 00257 00258 if( pszDebug[i] == '\0' ) 00259 return; 00260 } 00261 00262 /* -------------------------------------------------------------------- */ 00263 /* Allocate a block for the error. */ 00264 /* -------------------------------------------------------------------- */ 00265 pszMessage = (char *) VSIMalloc(ERROR_MAX); 00266 if( pszMessage == NULL ) 00267 return; 00268 00269 /* -------------------------------------------------------------------- */ 00270 /* Dal -- always log a timestamp as the first part of the line */ 00271 /* to ensure one is looking at what one should be looking at! */ 00272 /* -------------------------------------------------------------------- */ 00273 00274 #ifdef TIMESTAMP_DEBUG 00275 { 00276 time_t ltime; 00277 00278 time( <ime ); 00279 strcpy( pszMessage, ctime( <ime ) ); 00280 00281 // On windows anyway, ctime puts a \n at the end, but I'm not 00282 // convinced this is standard behaviour, so we'll get rid of it 00283 // carefully 00284 00285 if (pszMessage[strlen(pszMessage) -1 ] == '\n') 00286 { 00287 pszMessage[strlen(pszMessage) - 1] = 0; // blow it out 00288 } 00289 strcat( pszMessage, ": " ); 00290 } 00291 #else 00292 pszMessage[0] = '\0'; 00293 #endif 00294 00295 /* -------------------------------------------------------------------- */ 00296 /* Add the category. */ 00297 /* -------------------------------------------------------------------- */ 00298 strcat( pszMessage, pszCategory ); 00299 strcat( pszMessage, ": " ); 00300 00301 /* -------------------------------------------------------------------- */ 00302 /* Format the application provided portion of the debug message. */ 00303 /* -------------------------------------------------------------------- */ 00304 va_start(args, pszFormat); 00305 #if defined(HAVE_VSNPRINTF) 00306 vsnprintf(pszMessage+strlen(pszMessage), ERROR_MAX - strlen(pszMessage), 00307 pszFormat, args); 00308 #else 00309 vsprintf(pszMessage+strlen(pszMessage), pszFormat, args); 00310 #endif 00311 va_end(args); 00312 00313 /* -------------------------------------------------------------------- */ 00314 /* If the user provided his own error handling function, then call */ 00315 /* it, otherwise print the error to stderr and return. */ 00316 /* -------------------------------------------------------------------- */ 00317 if( gpfnCPLErrorHandler ) 00318 gpfnCPLErrorHandler(CE_Debug, CPLE_None, pszMessage); 00319 00320 VSIFree( pszMessage ); 00321 } 00322 00323 /********************************************************************** 00324 * CPLErrorReset() 00325 **********************************************************************/ 00326 00334 void CPLErrorReset() 00335 { 00336 gnCPLLastErrNo = CPLE_None; 00337 gszCPLLastErrMsg[0] = '\0'; 00338 geCPLLastErrType = CE_None; 00339 } 00340 00341 00342 /********************************************************************** 00343 * CPLGetLastErrorNo() 00344 **********************************************************************/ 00345 00355 int CPLGetLastErrorNo() 00356 { 00357 return gnCPLLastErrNo; 00358 } 00359 00360 /********************************************************************** 00361 * CPLGetLastErrorType() 00362 **********************************************************************/ 00363 00373 CPLErr CPLGetLastErrorType() 00374 { 00375 return geCPLLastErrType; 00376 } 00377 00378 /********************************************************************** 00379 * CPLGetLastErrorMsg() 00380 **********************************************************************/ 00381 00393 const char* CPLGetLastErrorMsg() 00394 { 00395 return gszCPLLastErrMsg; 00396 } 00397 00398 /************************************************************************/ 00399 /* CPLDefaultErrorHandler() */ 00400 /************************************************************************/ 00401 00402 void CPLDefaultErrorHandler( CPLErr eErrClass, int nError, 00403 const char * pszErrorMsg ) 00404 00405 { 00406 static int bLogInit = FALSE; 00407 static FILE * fpLog = stderr; 00408 00409 if( !bLogInit ) 00410 { 00411 bLogInit = TRUE; 00412 00413 fpLog = stderr; 00414 if( getenv( "CPL_LOG" ) != NULL ) 00415 { 00416 fpLog = fopen( getenv("CPL_LOG"), "wt" ); 00417 if( fpLog == NULL ) 00418 fpLog = stderr; 00419 } 00420 } 00421 00422 if( eErrClass == CE_Debug ) 00423 fprintf( fpLog, "%s\n", pszErrorMsg ); 00424 else if( eErrClass == CE_Warning ) 00425 fprintf( fpLog, "Warning %d: %s\n", nError, pszErrorMsg ); 00426 else 00427 fprintf( fpLog, "ERROR %d: %s\n", nError, pszErrorMsg ); 00428 00429 fflush( fpLog ); 00430 } 00431 00432 /************************************************************************/ 00433 /* CPLQuietErrorHandler() */ 00434 /************************************************************************/ 00435 00436 void CPLQuietErrorHandler( CPLErr eErrClass , int nError, 00437 const char * pszErrorMsg ) 00438 00439 { 00440 if( eErrClass == CE_Debug ) 00441 CPLDefaultErrorHandler( eErrClass, nError, pszErrorMsg ); 00442 } 00443 00444 /************************************************************************/ 00445 /* CPLLoggingErrorHandler() */ 00446 /************************************************************************/ 00447 00448 void CPLLoggingErrorHandler( CPLErr eErrClass, int nError, 00449 const char * pszErrorMsg ) 00450 00451 { 00452 static int bLogInit = FALSE; 00453 static FILE * fpLog = stderr; 00454 00455 if( !bLogInit ) 00456 { 00457 const char *cpl_log = NULL; 00458 00459 bLogInit = TRUE; 00460 00461 if( getenv("CPL_LOG") != NULL ) 00462 cpl_log = getenv("CPL_LOG"); 00463 00464 fpLog = stderr; 00465 if( cpl_log != NULL && EQUAL(cpl_log,"OFF") ) 00466 { 00467 fpLog = NULL; 00468 } 00469 else if( cpl_log != NULL ) 00470 { 00471 char path[5000]; 00472 int i = 0; 00473 00474 strcpy( path, cpl_log ); 00475 00476 while( (fpLog = fopen( path, "rt" )) != NULL ) 00477 { 00478 fclose( fpLog ); 00479 00480 /* generate sequenced log file names, inserting # before ext.*/ 00481 if (strrchr(cpl_log, '.') == NULL) 00482 { 00483 sprintf( path, "%s_%d%s", cpl_log, i++, 00484 ".log" ); 00485 } 00486 else 00487 { 00488 int pos = 0; 00489 char *cpl_log_base = strdup(cpl_log); 00490 pos = strcspn(cpl_log_base, "."); 00491 if (pos > 0) 00492 { 00493 cpl_log_base[pos] = '\0'; 00494 } 00495 sprintf( path, "%s_%d%s", cpl_log_base, 00496 i++, ".log" ); 00497 } 00498 } 00499 00500 fpLog = fopen( path, "wt" ); 00501 } 00502 } 00503 00504 if( fpLog == NULL ) 00505 return; 00506 00507 if( eErrClass == CE_Debug ) 00508 fprintf( fpLog, "%s\n", pszErrorMsg ); 00509 else if( eErrClass == CE_Warning ) 00510 fprintf( fpLog, "Warning %d: %s\n", nError, pszErrorMsg ); 00511 else 00512 fprintf( fpLog, "ERROR %d: %s\n", nError, pszErrorMsg ); 00513 00514 fflush( fpLog ); 00515 } 00516 00517 /********************************************************************** 00518 * CPLSetErrorHandler() 00519 **********************************************************************/ 00520 00550 CPLErrorHandler CPLSetErrorHandler( CPLErrorHandler pfnErrorHandler ) 00551 { 00552 CPLErrorHandler pfnOldHandler = gpfnCPLErrorHandler; 00553 00554 gpfnCPLErrorHandler = pfnErrorHandler; 00555 00556 return pfnOldHandler; 00557 } 00558 00559 00560 00561 /************************************************************************/ 00562 /* CPLPushErrorHandler() */ 00563 /************************************************************************/ 00564 00576 void CPLPushErrorHandler( CPLErrorHandler pfnErrorHandler ) 00577 00578 { 00579 CPLErrorHandlerNode *psNode; 00580 00581 psNode = (CPLErrorHandlerNode *) VSIMalloc(sizeof(CPLErrorHandlerNode)); 00582 psNode->psNext = psHandlerStack; 00583 psNode->pfnHandler = gpfnCPLErrorHandler; 00584 00585 psHandlerStack = psNode; 00586 00587 CPLSetErrorHandler( pfnErrorHandler ); 00588 } 00589 00590 /************************************************************************/ 00591 /* CPLPopErrorHandler() */ 00592 /************************************************************************/ 00593 00601 void CPLPopErrorHandler() 00602 00603 { 00604 if( psHandlerStack != NULL ) 00605 { 00606 CPLErrorHandlerNode *psNode = psHandlerStack; 00607 00608 psHandlerStack = psNode->psNext; 00609 CPLSetErrorHandler( psNode->pfnHandler ); 00610 VSIFree( psNode ); 00611 } 00612 } 00613 00614 /************************************************************************/ 00615 /* _CPLAssert() */ 00616 /* */ 00617 /* This function is called only when an assertion fails. */ 00618 /************************************************************************/ 00619 00632 void _CPLAssert( const char * pszExpression, const char * pszFile, 00633 int iLine ) 00634 00635 { 00636 CPLError( CE_Fatal, CPLE_AssertionFailed, 00637 "Assertion `%s' failed\n" 00638 "in file `%s', line %d\n", 00639 pszExpression, pszFile, iLine ); 00640 } 00641