ultimatepp/bazaar/plugin/gdal/port/cpl_error.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

1001 lines
34 KiB
C++

/**********************************************************************
* $Id: cpl_error.cpp 29330 2015-06-14 12:11:11Z rouault $
*
* Name: cpl_error.cpp
* Project: CPL - Common Portability Library
* Purpose: Error handling functions.
* Author: Daniel Morissette, danmo@videotron.ca
*
**********************************************************************
* Copyright (c) 1998, Daniel Morissette
* Copyright (c) 2007-2013, Even Rouault <even dot rouault at mines-paris dot org>
*
* 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.
****************************************************************************/
#include "cpl_error.h"
#include "cpl_string.h"
#include "cpl_vsi.h"
#include "cpl_conv.h"
#include "cpl_multiproc.h"
#if defined(WIN32CE)
# include "cpl_wince.h"
# include <wce_stdlib.h>
#endif
#define TIMESTAMP_DEBUG
//#define MEMORY_DEBUG
CPL_CVSID("$Id: cpl_error.cpp 29330 2015-06-14 12:11:11Z rouault $");
static CPLMutex *hErrorMutex = NULL;
static void *pErrorHandlerUserData = NULL;
static CPLErrorHandler pfnErrorHandler = CPLDefaultErrorHandler;
#if !defined(HAVE_VSNPRINTF)
# define DEFAULT_LAST_ERR_MSG_SIZE 20000
#else
# define DEFAULT_LAST_ERR_MSG_SIZE 500
#endif
typedef struct errHandler
{
struct errHandler *psNext;
void *pUserData;
CPLErrorHandler pfnHandler;
} CPLErrorHandlerNode;
typedef struct {
int nLastErrNo;
CPLErr eLastErrType;
CPLErrorHandlerNode *psHandlerStack;
int nLastErrMsgMax;
int nFailureIntoWarning;
char szLastErrMsg[DEFAULT_LAST_ERR_MSG_SIZE];
/* Do not add anything here. szLastErrMsg must be the last field. See CPLRealloc() below */
} CPLErrorContext;
/************************************************************************/
/* CPLGetErrorContext() */
/************************************************************************/
static CPLErrorContext *CPLGetErrorContext()
{
CPLErrorContext *psCtx =
(CPLErrorContext *) CPLGetTLS( CTLS_ERRORCONTEXT );
if( psCtx == NULL )
{
psCtx = (CPLErrorContext *) VSICalloc(sizeof(CPLErrorContext),1);
if (psCtx == NULL) {
CPLEmergencyError("Out of memory attempting to report error");
}
psCtx->eLastErrType = CE_None;
psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg);
CPLSetTLS( CTLS_ERRORCONTEXT, psCtx, TRUE );
}
return psCtx;
}
/************************************************************************/
/* CPLGetErrorHandlerUserData() */
/************************************************************************/
/**
* Fetch the user data for the error context
*
* Fetches the user data for the current error context. You can
* set the user data for the error context when you add your handler by
* issuing CPLSetErrorHandlerEx() and CPLPushErrorHandlerEx(). Note that
* user data is primarily intended for providing context within error handlers
* themselves, but they could potentially be abused in other useful ways with the usual
* caveat emptor understanding.
*
* @return the user data pointer for the error context
*/
void* CPL_STDCALL CPLGetErrorHandlerUserData(void)
{
CPLErrorContext *psCtx = CPLGetErrorContext();
return (void*) psCtx->psHandlerStack ? psCtx->psHandlerStack->pUserData : pErrorHandlerUserData;
}
/**********************************************************************
* 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 suppress 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 )
{
CPLErrorContext *psCtx = CPLGetErrorContext();
if (psCtx->nFailureIntoWarning > 0 && eErrClass == CE_Failure)
eErrClass = CE_Warning;
/* -------------------------------------------------------------------- */
/* Expand the error message */
/* -------------------------------------------------------------------- */
#if defined(HAVE_VSNPRINTF)
{
int nPR;
va_list wrk_args;
#ifdef va_copy
va_copy( wrk_args, args );
#else
wrk_args = args;
#endif
/* -------------------------------------------------------------------- */
/* If CPL_ACCUM_ERROR_MSG=ON accumulate the error messages, */
/* rather than just replacing the last error message. */
/* -------------------------------------------------------------------- */
int nPreviousSize = 0;
if ( psCtx->psHandlerStack != NULL &&
EQUAL(CPLGetConfigOption( "CPL_ACCUM_ERROR_MSG", "" ), "ON"))
{
nPreviousSize = strlen(psCtx->szLastErrMsg);
if (nPreviousSize)
{
if (nPreviousSize + 1 + 1 >= psCtx->nLastErrMsgMax)
{
psCtx->nLastErrMsgMax *= 3;
psCtx = (CPLErrorContext *)
CPLRealloc(psCtx, sizeof(CPLErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE + psCtx->nLastErrMsgMax + 1);
CPLSetTLS( CTLS_ERRORCONTEXT, psCtx, TRUE );
}
psCtx->szLastErrMsg[nPreviousSize] = '\n';
psCtx->szLastErrMsg[nPreviousSize+1] = '0';
nPreviousSize ++;
}
}
while( ((nPR = CPLvsnprintf( psCtx->szLastErrMsg+nPreviousSize,
psCtx->nLastErrMsgMax-nPreviousSize, fmt, wrk_args )) == -1
|| nPR >= psCtx->nLastErrMsgMax-nPreviousSize-1)
&& psCtx->nLastErrMsgMax < 1000000 )
{
#ifdef va_copy
va_end( wrk_args );
va_copy( wrk_args, args );
#else
wrk_args = args;
#endif
psCtx->nLastErrMsgMax *= 3;
psCtx = (CPLErrorContext *)
CPLRealloc(psCtx, sizeof(CPLErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE + psCtx->nLastErrMsgMax + 1);
CPLSetTLS( CTLS_ERRORCONTEXT, psCtx, TRUE );
}
va_end( wrk_args );
}
#else
// !HAVE_VSNPRINTF
CPLvsnprintf( psCtx->szLastErrMsg, psCtx->nLastErrMsgMax, fmt, args);
#endif
/* -------------------------------------------------------------------- */
/* Obfuscate any password in error message */
/* -------------------------------------------------------------------- */
char* pszPassword = strstr(psCtx->szLastErrMsg, "password=");
if( pszPassword != NULL )
{
char* pszIter = pszPassword + strlen("password=");
while( *pszIter != ' ' && *pszIter != '\0' )
{
*pszIter = 'X';
pszIter ++;
}
}
/* -------------------------------------------------------------------- */
/* If the user provided an handling function, then */
/* call it, otherwise print the error to stderr and return. */
/* -------------------------------------------------------------------- */
psCtx->nLastErrNo = err_no;
psCtx->eLastErrType = eErrClass;
if( CPLGetConfigOption("CPL_LOG_ERRORS",NULL) != NULL )
CPLDebug( "CPLError", "%s", psCtx->szLastErrMsg );
/* -------------------------------------------------------------------- */
/* Invoke the current error handler. */
/* -------------------------------------------------------------------- */
if( psCtx->psHandlerStack != NULL )
{
psCtx->psHandlerStack->pfnHandler(eErrClass, err_no,
psCtx->szLastErrMsg);
}
else
{
CPLMutexHolderD( &hErrorMutex );
if( pfnErrorHandler != NULL )
pfnErrorHandler(eErrClass, err_no, psCtx->szLastErrMsg);
}
if( eErrClass == CE_Fatal )
abort();
}
/************************************************************************/
/* CPLEmergencyError() */
/************************************************************************/
/**
* Fatal error when things are bad.
*
* This function should be called in an emergency situation where
* it is unlikely that a regular error report would work. This would
* include in the case of heap exhaustion for even small allocations,
* or any failure in the process of reporting an error (such as TLS
* allocations).
*
* This function should never return. After the error message has been
* reported as best possible, the application will abort() similarly to how
* CPLError() aborts on CE_Fatal class errors.
*
* @param pszMessage the error message to report.
*/
void CPLEmergencyError( const char *pszMessage )
{
CPLErrorContext *psCtx = NULL;
static int bInEmergencyError = FALSE;
// If we are already in emergency error then one of the
// following failed, so avoid them the second time through.
if( !bInEmergencyError )
{
bInEmergencyError = TRUE;
psCtx = (CPLErrorContext *) CPLGetTLS( CTLS_ERRORCONTEXT );
if( psCtx != NULL && psCtx->psHandlerStack != NULL )
{
psCtx->psHandlerStack->pfnHandler( CE_Fatal, CPLE_AppDefined,
pszMessage );
}
else if( pfnErrorHandler != NULL )
{
pfnErrorHandler( CE_Fatal, CPLE_AppDefined, pszMessage );
}
}
// Ultimate fallback.
fprintf( stderr, "FATAL: %s\n", pszMessage );
abort();
}
/************************************************************************/
/* CPLGetProcessMemorySize() */
/************************************************************************/
#ifdef MEMORY_DEBUG
#ifdef __linux
static int CPLGetProcessMemorySize()
{
FILE* fp = fopen("/proc/self/status", "r");
if( fp == NULL )
return -1;
int nRet = -1;
char szLine[128];
while (fgets(szLine, sizeof(szLine), fp) != NULL)
{
if (strncmp(szLine, "VmSize:", 7) == 0)
{
const char* pszPtr = szLine;
while( !(*pszPtr == '\0' || (*pszPtr >= '0' && *pszPtr <= '9')) )
pszPtr ++;
nRet = atoi(pszPtr);
break;
}
}
fclose(fp);
return nRet;
}
#else
#error CPLGetProcessMemorySize() unimplemented for this OS
#endif
#endif // def MEMORY_DEBUG
/************************************************************************/
/* 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, ... )
{
CPLErrorContext *psCtx = CPLGetErrorContext();
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,"") )
{
size_t 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 process memory size. */
/* -------------------------------------------------------------------- */
#ifdef MEMORY_DEBUG
char szVmSize[32];
CPLsprintf( szVmSize, "[VmSize: %d] ", CPLGetProcessMemorySize());
strcat( pszMessage, szVmSize );
#endif
//sprintf(pszMessage,"[%d] ", (int)getpid());
/* -------------------------------------------------------------------- */
/* Add the category. */
/* -------------------------------------------------------------------- */
strcat( pszMessage, pszCategory );
strcat( pszMessage, ": " );
/* -------------------------------------------------------------------- */
/* Format the application provided portion of the debug message. */
/* -------------------------------------------------------------------- */
va_start(args, pszFormat);
CPLvsnprintf(pszMessage+strlen(pszMessage), ERROR_MAX - strlen(pszMessage),
pszFormat, args);
va_end(args);
/* -------------------------------------------------------------------- */
/* Obfuscate any password in error message */
/* -------------------------------------------------------------------- */
char* pszPassword = strstr(pszMessage, "password=");
if( pszPassword != NULL )
{
char* pszIter = pszPassword + strlen("password=");
while( *pszIter != ' ' && *pszIter != '\0' )
{
*pszIter = 'X';
pszIter ++;
}
}
/* -------------------------------------------------------------------- */
/* Invoke the current error handler. */
/* -------------------------------------------------------------------- */
if( psCtx->psHandlerStack != NULL )
{
psCtx->psHandlerStack->pfnHandler( CE_Debug, CPLE_None, pszMessage );
}
else
{
CPLMutexHolderD( &hErrorMutex );
if( pfnErrorHandler != NULL )
pfnErrorHandler( 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()
{
CPLErrorContext *psCtx = CPLGetErrorContext();
psCtx->nLastErrNo = CPLE_None;
psCtx->szLastErrMsg[0] = '\0';
psCtx->eLastErrType = CE_None;
}
/**********************************************************************
* CPLErrorSetState()
**********************************************************************/
/**
* Restore an error state, without emitting an error.
*
* Can be useful if a routine might call CPLErrorReset() and one wants to
* preserve the previous error state.
*
* @since GDAL 2.0
*/
void CPL_DLL CPLErrorSetState( CPLErr eErrClass, int err_no, const char* pszMsg )
{
CPLErrorContext *psCtx = CPLGetErrorContext();
psCtx->nLastErrNo = err_no;
strncpy(psCtx->szLastErrMsg, pszMsg, psCtx->nLastErrMsgMax);
psCtx->szLastErrMsg[MAX(psCtx->nLastErrMsgMax-1, (int)strlen(pszMsg))] = '\0';
psCtx->eLastErrType = eErrClass;
}
/**********************************************************************
* CPLGetLastErrorNo()
**********************************************************************/
/**
* Fetch the last error number.
*
* Fetches the last error number posted with CPLError(), that hasn't
* been cleared by CPLErrorReset(). 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()
{
CPLErrorContext *psCtx = CPLGetErrorContext();
return psCtx->nLastErrNo;
}
/**********************************************************************
* CPLGetLastErrorType()
**********************************************************************/
/**
* Fetch the last error type.
*
* Fetches the last error type posted with CPLError(), that hasn't
* been cleared by CPLErrorReset(). This is the error class, not the error number.
*
* @return the error type of the last error to occur, or CE_None (0)
* if there are no posted errors.
*/
CPLErr CPL_STDCALL CPLGetLastErrorType()
{
CPLErrorContext *psCtx = CPLGetErrorContext();
return psCtx->eLastErrType;
}
/**********************************************************************
* 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()
{
CPLErrorContext *psCtx = CPLGetErrorContext();
return psCtx->szLastErrMsg;
}
/************************************************************************/
/* CPLDefaultErrorHandler() */
/************************************************************************/
void CPL_STDCALL CPLDefaultErrorHandler( CPLErr eErrClass, int nError,
const char * pszErrorMsg )
{
static int bLogInit = FALSE;
static FILE * fpLog = stderr;
static int nCount = 0;
static int nMaxErrors = -1;
if (eErrClass != CE_Debug)
{
if( nMaxErrors == -1 )
{
nMaxErrors =
atoi(CPLGetConfigOption( "CPL_MAX_ERROR_REPORTS", "1000" ));
}
nCount++;
if (nCount > nMaxErrors && nMaxErrors > 0 )
return;
}
if( !bLogInit )
{
bLogInit = TRUE;
fpLog = stderr;
if( CPLGetConfigOption( "CPL_LOG", NULL ) != NULL )
{
const char* pszAccess = "wt";
if( CPLGetConfigOption( "CPL_LOG_APPEND", NULL ) != NULL )
pszAccess = "at";
fpLog = fopen( CPLGetConfigOption("CPL_LOG",""), pszAccess );
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 );
if (eErrClass != CE_Debug
&& nMaxErrors > 0
&& nCount == nMaxErrors )
{
fprintf( fpLog,
"More than %d errors or warnings have been reported. "
"No more will be reported from now.\n",
nMaxErrors );
}
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* pszPath;
int i = 0;
pszPath = (char*)CPLMalloc(strlen(cpl_log) + 20);
strcpy(pszPath, cpl_log);
while( (fpLog = fopen( pszPath, "rt" )) != NULL )
{
fclose( fpLog );
/* generate sequenced log file names, inserting # before ext.*/
if (strrchr(cpl_log, '.') == NULL)
{
CPLsprintf( pszPath, "%s_%d%s", cpl_log, i++,
".log" );
}
else
{
size_t pos = 0;
char *cpl_log_base = CPLStrdup(cpl_log);
pos = strcspn(cpl_log_base, ".");
if (pos > 0)
{
cpl_log_base[pos] = '\0';
}
CPLsprintf( pszPath, "%s_%d%s", cpl_log_base,
i++, ".log" );
CPLFree(cpl_log_base);
}
}
fpLog = fopen( pszPath, "wt" );
CPLFree(pszPath);
}
}
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 );
}
/**********************************************************************
* CPLTurnFailureIntoWarning() *
**********************************************************************/
void CPLTurnFailureIntoWarning(int bOn )
{
CPLErrorContext *psCtx = CPLGetErrorContext();
psCtx->nFailureIntoWarning += (bOn) ? 1 : -1;
if (psCtx->nFailureIntoWarning < 0)
{
CPLDebug("CPL", "Wrong nesting of CPLTurnFailureIntoWarning(TRUE) / CPLTurnFailureIntoWarning(FALSE)");
}
}
/**********************************************************************
* CPLSetErrorHandlerEx() *
**********************************************************************/
/**
* Install custom error handle with user's data. This method is
* essentially CPLSetErrorHandler with an added pointer to pUserData.
* The pUserData is not returned in the CPLErrorHandler, however, and
* must be fetched via CPLGetLastErrorUserData
*
* @param pfnErrorHandlerNew new error handler function.
* @param pUserData User data to carry along with the error context.
* @return returns the previously installed error handler.
*/
CPLErrorHandler CPL_STDCALL
CPLSetErrorHandlerEx( CPLErrorHandler pfnErrorHandlerNew,
void* pUserData )
{
CPLErrorHandler pfnOldHandler = pfnErrorHandler;
CPLErrorContext *psCtx = CPLGetErrorContext();
if( psCtx->psHandlerStack != NULL )
{
CPLDebug( "CPL",
"CPLSetErrorHandler() called with an error handler on\n"
"the local stack. New error handler will not be used immediately.\n" );
}
{
CPLMutexHolderD( &hErrorMutex );
pfnOldHandler = pfnErrorHandler;
if( pfnErrorHandler == NULL )
pfnErrorHandler = CPLDefaultErrorHandler;
else
pfnErrorHandler = pfnErrorHandlerNew;
pErrorHandlerUserData = pUserData;
}
return pfnOldHandler;
}
/**********************************************************************
* CPLSetErrorHandler() *
**********************************************************************/
/**
* Install custom error handler.
*
* Allow the library's user to specify an error handler function.
* A valid error handler is a C function with the following prototype:
*
* <pre>
* void MyErrorHandler(CPLErr eErrClass, int err_no, const char *msg)
* </pre>
*
* 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.
*
* Note that error handlers set with CPLSetErrorHandler() apply to all
* threads in an application, while error handlers set with CPLPushErrorHandler
* are thread-local. However, any error handlers pushed with
* CPLPushErrorHandler (and not removed with CPLPopErrorHandler) take
* precidence over the global error handlers set with CPLSetErrorHandler().
* Generally speaking CPLSetErrorHandler() would be used to set a desired
* global error handler, while CPLPushErrorHandler() would be used to install
* a temporary local error handler, such as CPLQuietErrorHandler() to suppress
* error reporting in a limited segment of code.
*
* @param pfnErrorHandlerNew new error handler function.
* @return returns the previously installed error handler.
*/
CPLErrorHandler CPL_STDCALL
CPLSetErrorHandler( CPLErrorHandler pfnErrorHandlerNew )
{
return CPLSetErrorHandlerEx(pfnErrorHandlerNew, NULL);
}
/************************************************************************/
/* CPLPushErrorHandler() */
/************************************************************************/
/**
* Push a new CPLError handler.
*
* This pushes a new error handler on the thread-local error handler
* stack. This handler will be used until removed with CPLPopErrorHandler().
*
* The CPLSetErrorHandler() docs have further information on how
* CPLError handlers work.
*
* @param pfnErrorHandlerNew new error handler function.
*/
void CPL_STDCALL CPLPushErrorHandler( CPLErrorHandler pfnErrorHandlerNew )
{
CPLPushErrorHandlerEx(pfnErrorHandlerNew, NULL);
}
/************************************************************************/
/* CPLPushErrorHandlerEx() */
/************************************************************************/
/**
* Push a new CPLError handler with user data on the error context.
*
* This pushes a new error handler on the thread-local error handler
* stack. This handler will be used until removed with CPLPopErrorHandler().
* Obtain the user data back by using CPLGetErrorContext().
*
* The CPLSetErrorHandler() docs have further information on how
* CPLError handlers work.
*
* @param pfnErrorHandlerNew new error handler function.
* @param pUserData User data to put on the error context.
*/
void CPL_STDCALL CPLPushErrorHandlerEx( CPLErrorHandler pfnErrorHandlerNew,
void* pUserData )
{
CPLErrorContext *psCtx = CPLGetErrorContext();
CPLErrorHandlerNode *psNode;
psNode = (CPLErrorHandlerNode *) CPLMalloc(sizeof(CPLErrorHandlerNode));
psNode->psNext = psCtx->psHandlerStack;
psNode->pfnHandler = pfnErrorHandlerNew;
psNode->pUserData = pUserData;
psCtx->psHandlerStack = psNode;
}
/************************************************************************/
/* CPLPopErrorHandler() */
/************************************************************************/
/**
* Pop error handler off stack.
*
* Discards the current error handler on the error handler stack, and restores
* the one in use before the last CPLPushErrorHandler() call. This method
* has no effect if there are no error handlers on the current threads error
* handler stack.
*/
void CPL_STDCALL CPLPopErrorHandler()
{
CPLErrorContext *psCtx = CPLGetErrorContext();
if( psCtx->psHandlerStack != NULL )
{
CPLErrorHandlerNode *psNode = psCtx->psHandlerStack;
psCtx->psHandlerStack = psNode->psNext;
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 );
}
/************************************************************************/
/* CPLCleanupErrorMutex() */
/************************************************************************/
void CPLCleanupErrorMutex()
{
if( hErrorMutex != NULL )
{
CPLDestroyMutex(hErrorMutex);
hErrorMutex = NULL;
}
}