www.pudn.com > mitab.rar > cpl_error.cpp


/********************************************************************** 
 * $Id: cpl_error.cpp,v 1.28 2005/04/04 15:23:31 fwarmerdam Exp $ 
 * 
 * Name:     cpl_error.cpp 
 * Project:  CPL - Common Portability Library 
 * Purpose:  Error handling functions. 
 * Author:   Daniel Morissette, danmo@videotron.ca 
 * 
 ********************************************************************** 
 * Copyright (c) 1998, Daniel Morissette 
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions: 
 *  
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software. 
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER  
 * DEALINGS IN THE SOFTWARE. 
 ********************************************************************** 
 * 
 * $Log: cpl_error.cpp,v $ 
 * Revision 1.28  2005/04/04 15:23:31  fwarmerdam 
 * some functions now CPL_STDCALL 
 * 
 * Revision 1.27  2003/05/27 20:44:16  warmerda 
 * use VSI time services 
 * 
 * Revision 1.26  2003/05/08 21:51:14  warmerda 
 * added CPL{G,S}etConfigOption() usage 
 * 
 * Revision 1.25  2003/04/04 14:57:38  dron 
 * _vsnprintf() hack moved to the cpl_config.h.vc. 
 * 
 * Revision 1.24  2003/04/04 14:16:07  dron 
 * Use _vsnprintf() in Windows environment. 
 * 
 * Revision 1.23  2002/10/23 20:19:37  warmerda 
 * Modify log file naming convention as per patch from Dale. 
 * 
 * Revision 1.22  2002/08/01 20:02:54  warmerda 
 * added CPL_LOG_ERRORS support 
 * 
 * Revision 1.21  2001/12/14 19:45:17  warmerda 
 * Avoid use of errno in prototype. 
 * 
 * Revision 1.20  2001/11/27 17:01:06  warmerda 
 * added timestamp to debug messages 
 * 
 * Revision 1.19  2001/11/15 16:11:08  warmerda 
 * use vsnprintf() for debug calls if it is available 
 * 
 * Revision 1.18  2001/11/02 22:07:58  warmerda 
 * added logging error handler 
 * 
 * Revision 1.17  2001/07/18 04:00:49  warmerda 
 * added CPL_CVSID 
 * 
 * Revision 1.16  2001/02/15 16:30:57  warmerda 
 * fixed initialization of fpLog 
 * 
 * Revision 1.15  2001/01/19 21:16:41  warmerda 
 * expanded tabs 
 * 
 * Revision 1.14  2000/11/30 17:30:10  warmerda 
 * added CPLGetLastErrorType 
 * 
 * Revision 1.13  2000/03/31 14:37:48  warmerda 
 * only use vsnprintf where available 
 * 
 * Revision 1.12  2000/03/31 14:11:55  warmerda 
 * added CPLErrorV 
 * 
 * Revision 1.11  2000/01/10 17:35:45  warmerda 
 * added push down stack of error handlers 
 * 
 * Revision 1.10  1999/11/23 04:16:56  danmo 
 * Fixed var. initialization that failed to compile as C 
 * 
 * Revision 1.9  1999/09/03 17:03:45  warmerda 
 * Completed partial help line. 
 * 
 * Revision 1.8  1999/07/23 14:27:47  warmerda 
 * CPLSetErrorHandler returns old handler 
 * 
 * Revision 1.7  1999/06/27 16:50:52  warmerda 
 * added support for CPL_DEBUG and CPL_LOG variables 
 * 
 * Revision 1.6  1999/06/26 02:46:11  warmerda 
 * Fixed initialization of debug messages. 
 * 
 * Revision 1.5  1999/05/20 14:59:05  warmerda 
 * added CPLDebug() 
 * 
 * Revision 1.4  1999/05/20 02:54:38  warmerda 
 * Added API documentation 
 * 
 * Revision 1.3  1998/12/15 19:02:27  warmerda 
 * Avoid use of errno as a variable 
 * 
 * Revision 1.2  1998/12/06 02:52:52  warmerda 
 * Implement assert support 
 * 
 * Revision 1.1  1998/12/03 18:26:02  warmerda 
 * New 
 * 
 **********************************************************************/ 
 
#include "cpl_error.h" 
#include "cpl_vsi.h" 
#include "cpl_conv.h" 
 
#define TIMESTAMP_DEBUG 
 
CPL_CVSID("$Id: cpl_error.cpp,v 1.28 2005/04/04 15:23:31 fwarmerdam Exp $"); 
 
/* static buffer to store the last error message.  We'll assume that error 
 * messages cannot be longer than 2000 chars... which is quite reasonable 
 * (that's 25 lines of 80 chars!!!) 
 */ 
static char gszCPLLastErrMsg[2000] = ""; 
static int  gnCPLLastErrNo = 0; 
static CPLErr geCPLLastErrType = CE_None; 
 
static CPLErrorHandler gpfnCPLErrorHandler = CPLDefaultErrorHandler; 
 
typedef struct errHandler 
{ 
    struct errHandler   *psNext; 
    CPLErrorHandler     pfnHandler; 
} CPLErrorHandlerNode; 
 
static CPLErrorHandlerNode * psHandlerStack = NULL; 
 
/********************************************************************** 
 *                          CPLError() 
 **********************************************************************/ 
 
/** 
 * Report an error. 
 * 
 * This function reports an error in a manner that can be hooked 
 * and reported appropriate by different applications. 
 * 
 * The effect of this function can be altered by applications by installing 
 * a custom error handling using CPLSetErrorHandler(). 
 * 
 * The eErrClass argument can have the value CE_Warning indicating that the 
 * message is an informational warning, CE_Failure indicating that the 
 * action failed, but that normal recover mechanisms will be used or 
 * CE_Fatal meaning that a fatal error has occured, and that CPLError() 
 * should not return.   
 * 
 * The default behaviour of CPLError() is to report errors to stderr, 
 * and to abort() after reporting a CE_Fatal error.  It is expected that 
 * some applications will want to supress error reporting, and will want to 
 * install a C++ exception, or longjmp() approach to no local fatal error 
 * recovery. 
 * 
 * Regardless of how application error handlers or the default error 
 * handler choose to handle an error, the error number, and message will 
 * be stored for recovery with CPLGetLastErrorNo() and CPLGetLastErrorMsg(). 
 * 
 * @param eErrClass one of CE_Warning, CE_Failure or CE_Fatal. 
 * @param err_no the error number (CPLE_*) from cpl_error.h. 
 * @param fmt a printf() style format string.  Any additional arguments 
 * will be treated as arguments to fill in this format in a manner 
 * similar to printf(). 
 */ 
 
void    CPLError(CPLErr eErrClass, int err_no, const char *fmt, ...) 
{ 
    va_list args; 
 
    /* Expand the error message  
     */ 
    va_start(args, fmt); 
    CPLErrorV( eErrClass, err_no, fmt, args ); 
    va_end(args); 
} 
 
/************************************************************************/ 
/*                             CPLErrorV()                              */ 
/************************************************************************/ 
 
void    CPLErrorV(CPLErr eErrClass, int err_no, const char *fmt, va_list args ) 
{ 
    /* Expand the error message  
     */ 
#if defined(HAVE_VSNPRINTF) 
    vsnprintf( gszCPLLastErrMsg, sizeof(gszCPLLastErrMsg), fmt, args ); 
#else 
    vsprintf(gszCPLLastErrMsg, fmt, args); 
#endif 
 
    /* If the user provided his own error handling function, then call 
     * it, otherwise print the error to stderr and return. 
     */ 
    gnCPLLastErrNo = err_no; 
    geCPLLastErrType = eErrClass; 
 
    if( CPLGetConfigOption("CPL_LOG_ERRORS",NULL) != NULL ) 
        CPLDebug( "CPLError", "%s", gszCPLLastErrMsg ); 
 
    if( gpfnCPLErrorHandler ) 
        gpfnCPLErrorHandler(eErrClass, err_no, gszCPLLastErrMsg); 
 
    if( eErrClass == CE_Fatal ) 
        abort(); 
} 
 
/************************************************************************/ 
/*                              CPLDebug()                              */ 
/************************************************************************/ 
 
/** 
 * Display a debugging message. 
 * 
 * The category argument is used in conjunction with the CPL_DEBUG 
 * environment variable to establish if the message should be displayed. 
 * If the CPL_DEBUG environment variable is not set, no debug messages 
 * are emitted (use CPLError(CE_Warning,...) to ensure messages are displayed). 
 * If CPL_DEBUG is set, but is an empty string or the word "ON" then all 
 * debug messages are shown.  Otherwise only messages whose category appears 
 * somewhere within the CPL_DEBUG value are displayed (as determinted by 
 * strstr()). 
 * 
 * Categories are usually an identifier for the subsystem producing the 
 * error.  For instance "GDAL" might be used for the GDAL core, and "TIFF" 
 * for messages from the TIFF translator.   
 * 
 * @param pszCategory name of the debugging message category. 
 * @param pszFormat printf() style format string for message to display. 
 *        Remaining arguments are assumed to be for format. 
 */  
 
void CPLDebug( const char * pszCategory, const char * pszFormat, ... ) 
 
{ 
    char        *pszMessage; 
    va_list     args; 
    const char  *pszDebug = CPLGetConfigOption("CPL_DEBUG",NULL); 
 
#define ERROR_MAX 25000 
 
/* -------------------------------------------------------------------- */ 
/*      Does this message pass our current criteria?                    */ 
/* -------------------------------------------------------------------- */ 
    if( pszDebug == NULL ) 
        return; 
 
    if( !EQUAL(pszDebug,"ON") && !EQUAL(pszDebug,"") ) 
    { 
        int            i, nLen = strlen(pszCategory); 
 
        for( i = 0; pszDebug[i] != '\0'; i++ ) 
        { 
            if( EQUALN(pszCategory,pszDebug+i,nLen) ) 
                break; 
        } 
 
        if( pszDebug[i] == '\0' ) 
            return; 
    } 
 
/* -------------------------------------------------------------------- */ 
/*    Allocate a block for the error.                                   */ 
/* -------------------------------------------------------------------- */ 
    pszMessage = (char *) VSIMalloc( ERROR_MAX ); 
    if( pszMessage == NULL ) 
        return; 
         
/* -------------------------------------------------------------------- */ 
/*      Dal -- always log a timestamp as the first part of the line     */ 
/*      to ensure one is looking at what one should be looking at!      */ 
/* -------------------------------------------------------------------- */ 
 
    pszMessage[0] = '\0'; 
#ifdef TIMESTAMP_DEBUG 
    if( CPLGetConfigOption( "CPL_TIMESTAMP", NULL ) != NULL ) 
    { 
        strcpy( pszMessage, VSICTime( VSITime(NULL) ) ); 
         
        // On windows anyway, ctime puts a \n at the end, but I'm not  
        // convinced this is standard behaviour, so we'll get rid of it 
        // carefully 
 
        if (pszMessage[strlen(pszMessage) -1 ] == '\n') 
        { 
            pszMessage[strlen(pszMessage) - 1] = 0; // blow it out 
        } 
        strcat( pszMessage, ": " ); 
    } 
#endif 
 
/* -------------------------------------------------------------------- */ 
/*      Add the category.                                               */ 
/* -------------------------------------------------------------------- */ 
    strcat( pszMessage, pszCategory ); 
    strcat( pszMessage, ": " ); 
     
/* -------------------------------------------------------------------- */ 
/*      Format the application provided portion of the debug message.   */ 
/* -------------------------------------------------------------------- */ 
    va_start(args, pszFormat); 
#if defined(HAVE_VSNPRINTF) 
    vsnprintf(pszMessage+strlen(pszMessage), ERROR_MAX - strlen(pszMessage),  
              pszFormat, args); 
#else 
    vsprintf(pszMessage+strlen(pszMessage), pszFormat, args); 
#endif 
    va_end(args); 
 
/* -------------------------------------------------------------------- */ 
/*      If the user provided his own error handling function, then call */ 
/*      it, otherwise print the error to stderr and return.             */ 
/* -------------------------------------------------------------------- */ 
    if( gpfnCPLErrorHandler ) 
        gpfnCPLErrorHandler(CE_Debug, CPLE_None, pszMessage); 
 
    VSIFree( pszMessage ); 
} 
 
/********************************************************************** 
 *                          CPLErrorReset() 
 **********************************************************************/ 
 
/** 
 * Erase any traces of previous errors. 
 * 
 * This is normally used to ensure that an error which has been recovered 
 * from does not appear to be still in play with high level functions. 
 */ 
 
void CPL_STDCALL CPLErrorReset() 
{ 
    gnCPLLastErrNo = CPLE_None; 
    gszCPLLastErrMsg[0] = '\0'; 
    geCPLLastErrType = CE_None; 
} 
 
 
/********************************************************************** 
 *                          CPLGetLastErrorNo() 
 **********************************************************************/ 
 
/** 
 * Fetch the last error number. 
 * 
 * This is the error number, not the error class. 
 * 
 * @return the error number of the last error to occur, or CPLE_None (0) 
 * if there are no posted errors. 
 */ 
 
int CPL_STDCALL CPLGetLastErrorNo() 
{ 
    return gnCPLLastErrNo; 
} 
 
/********************************************************************** 
 *                          CPLGetLastErrorType() 
 **********************************************************************/ 
 
/** 
 * Fetch the last error type. 
 * 
 * This is the error class, not the error number. 
 * 
 * @return the error number of the last error to occur, or CE_None (0) 
 * if there are no posted errors. 
 */ 
 
CPLErr CPL_STDCALL CPLGetLastErrorType() 
{ 
    return geCPLLastErrType; 
} 
 
/********************************************************************** 
 *                          CPLGetLastErrorMsg() 
 **********************************************************************/ 
 
/** 
 * Get the last error message. 
 * 
 * Fetches the last error message posted with CPLError(), that hasn't 
 * been cleared by CPLErrorReset().  The returned pointer is to an internal 
 * string that should not be altered or freed. 
 * 
 * @return the last error message, or NULL if there is no posted error 
 * message. 
 */ 
 
const char* CPL_STDCALL CPLGetLastErrorMsg() 
{ 
    return gszCPLLastErrMsg; 
} 
 
/************************************************************************/ 
/*                       CPLDefaultErrorHandler()                       */ 
/************************************************************************/ 
 
void CPL_STDCALL CPLDefaultErrorHandler( CPLErr eErrClass, int nError,  
                             const char * pszErrorMsg ) 
 
{ 
    static int       bLogInit = FALSE; 
    static FILE *    fpLog = stderr; 
 
    if( !bLogInit ) 
    { 
        bLogInit = TRUE; 
 
        fpLog = stderr; 
        if( CPLGetConfigOption( "CPL_LOG", NULL ) != NULL ) 
        { 
            fpLog = fopen( CPLGetConfigOption("CPL_LOG",""), "wt" ); 
            if( fpLog == NULL ) 
                fpLog = stderr; 
        } 
    } 
 
    if( eErrClass == CE_Debug ) 
        fprintf( fpLog, "%s\n", pszErrorMsg ); 
    else if( eErrClass == CE_Warning ) 
        fprintf( fpLog, "Warning %d: %s\n", nError, pszErrorMsg ); 
    else 
        fprintf( fpLog, "ERROR %d: %s\n", nError, pszErrorMsg ); 
 
    fflush( fpLog ); 
} 
 
/************************************************************************/ 
/*                        CPLQuietErrorHandler()                        */ 
/************************************************************************/ 
 
void CPL_STDCALL CPLQuietErrorHandler( CPLErr eErrClass , int nError,  
                           const char * pszErrorMsg ) 
 
{ 
    if( eErrClass == CE_Debug ) 
        CPLDefaultErrorHandler( eErrClass, nError, pszErrorMsg ); 
} 
 
/************************************************************************/ 
/*                       CPLLoggingErrorHandler()                       */ 
/************************************************************************/ 
 
void CPL_STDCALL CPLLoggingErrorHandler( CPLErr eErrClass, int nError,  
                             const char * pszErrorMsg ) 
 
{ 
    static int       bLogInit = FALSE; 
    static FILE *    fpLog = stderr; 
 
    if( !bLogInit ) 
    { 
        const char *cpl_log = NULL; 
 
        CPLSetConfigOption( "CPL_TIMESTAMP", "ON" ); 
 
        bLogInit = TRUE; 
 
        cpl_log = CPLGetConfigOption("CPL_LOG", NULL ); 
 
        fpLog = stderr; 
        if( cpl_log != NULL && EQUAL(cpl_log,"OFF") ) 
        { 
            fpLog = NULL; 
        } 
        else if( cpl_log != NULL ) 
        { 
            char      path[5000]; 
            int       i = 0; 
 
            strcpy( path, cpl_log ); 
 
            while( (fpLog = fopen( path, "rt" )) != NULL )  
            { 
                fclose( fpLog ); 
 
                /* generate sequenced log file names, inserting # before ext.*/ 
                if (strrchr(cpl_log, '.') == NULL) 
                { 
                    sprintf( path, "%s_%d%s", cpl_log, i++, 
                             ".log" ); 
                } 
                else 
                { 
                    int pos = 0; 
                    char *cpl_log_base = strdup(cpl_log); 
                    pos = strcspn(cpl_log_base, "."); 
                    if (pos > 0) 
                    { 
                        cpl_log_base[pos] = '\0'; 
                    } 
                    sprintf( path, "%s_%d%s", cpl_log_base, 
                             i++, ".log" ); 
                } 
            } 
 
            fpLog = fopen( path, "wt" ); 
        } 
    } 
 
    if( fpLog == NULL ) 
        return; 
 
    if( eErrClass == CE_Debug ) 
        fprintf( fpLog, "%s\n", pszErrorMsg ); 
    else if( eErrClass == CE_Warning ) 
        fprintf( fpLog, "Warning %d: %s\n", nError, pszErrorMsg ); 
    else 
        fprintf( fpLog, "ERROR %d: %s\n", nError, pszErrorMsg ); 
 
    fflush( fpLog ); 
} 
 
/********************************************************************** 
 *                          CPLSetErrorHandler() 
 **********************************************************************/ 
 
/** 
 * Install custom error handler. 
 * 
 * Allow the library's user to specify his own error handler function. 
 * A valid error handler is a C function with the following prototype: 
 * 
 * 
 
 *     void MyErrorHandler(CPLErr eErrClass, int err_no, const char *msg) 
 * 
* * Pass NULL to come back to the default behavior. The default behaviour * (CPLDefaultErrorHandler()) is to write the message to stderr. * * The msg will be a partially formatted error message not containing the * "ERROR %d:" portion emitted by the default handler. Message formatting * is handled by CPLError() before calling the handler. If the error * handler function is passed a CE_Fatal class error and returns, then * CPLError() will call abort(). Applications wanting to interrupt this * fatal behaviour will have to use longjmp(), or a C++ exception to * indirectly exit the function. * * Another standard error handler is CPLQuietErrorHandler() which doesn't * make any attempt to report the passed error or warning messages but * will process debug messages via CPLDefaultErrorHandler. * * @param pfnErrorHandler new error handler function. * @return returns the previously installed error handler. */ CPLErrorHandler CPL_STDCALL CPLSetErrorHandler( CPLErrorHandler pfnErrorHandler ) { CPLErrorHandler pfnOldHandler = gpfnCPLErrorHandler; gpfnCPLErrorHandler = pfnErrorHandler; return pfnOldHandler; } /************************************************************************/ /* CPLPushErrorHandler() */ /************************************************************************/ /** * Assign new CPLError handler. * * The old handler is "pushed down" onto a stack and can be easily * restored with CPLPopErrorHandler(). Otherwise this works similarly * to CPLSetErrorHandler() which contains more details on how error * handlers work. * * @param pfnErrorHandler new error handler function. */ void CPL_STDCALL CPLPushErrorHandler( CPLErrorHandler pfnErrorHandler ) { CPLErrorHandlerNode *psNode; psNode = (CPLErrorHandlerNode *) VSIMalloc(sizeof(CPLErrorHandlerNode)); psNode->psNext = psHandlerStack; psNode->pfnHandler = gpfnCPLErrorHandler; psHandlerStack = psNode; CPLSetErrorHandler( pfnErrorHandler ); } /************************************************************************/ /* CPLPopErrorHandler() */ /************************************************************************/ /** * Restore old CPLError handler. * * Discards the current error handler, and restore the one in use before * the last CPLPushErrorHandler() call. */ void CPL_STDCALL CPLPopErrorHandler() { if( psHandlerStack != NULL ) { CPLErrorHandlerNode *psNode = psHandlerStack; psHandlerStack = psNode->psNext; CPLSetErrorHandler( psNode->pfnHandler ); VSIFree( psNode ); } } /************************************************************************/ /* _CPLAssert() */ /* */ /* This function is called only when an assertion fails. */ /************************************************************************/ /** * Report failure of a logical assertion. * * Applications would normally use the CPLAssert() macro which expands * into code calling _CPLAssert() only if the condition fails. _CPLAssert() * will generate a CE_Fatal error call to CPLError(), indicating the file * name, and line number of the failed assertion, as well as containing * the assertion itself. * * There is no reason for application code to call _CPLAssert() directly. */ void CPL_STDCALL _CPLAssert( const char * pszExpression, const char * pszFile, int iLine ) { CPLError( CE_Fatal, CPLE_AssertionFailed, "Assertion `%s' failed\n" "in file `%s', line %d\n", pszExpression, pszFile, iLine ); }