LCOV - code coverage report
Current view: top level - port - cpl_error.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 321 456 70.4 %
Date: 2025-03-28 11:40:40 Functions: 36 40 90.0 %

          Line data    Source code
       1             : 
       2             : /**********************************************************************
       3             :  *
       4             :  * Name:     cpl_error.cpp
       5             :  * Project:  CPL - Common Portability Library
       6             :  * Purpose:  Error handling functions.
       7             :  * Author:   Daniel Morissette, danmo@videotron.ca
       8             :  *
       9             :  **********************************************************************
      10             :  * Copyright (c) 1998, Daniel Morissette
      11             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "cpl_error.h"
      17             : 
      18             : #include <cstdarg>
      19             : #include <cstdio>
      20             : #include <cstdlib>
      21             : #include <cstring>
      22             : 
      23             : #include <algorithm>
      24             : 
      25             : #include "cpl_config.h"
      26             : #include "cpl_conv.h"
      27             : #include "cpl_multiproc.h"
      28             : #include "cpl_string.h"
      29             : #include "cpl_vsi.h"
      30             : #include "cpl_error_internal.h"
      31             : 
      32             : #if !defined(va_copy) && defined(__va_copy)
      33             : #define va_copy __va_copy
      34             : #endif
      35             : 
      36             : #define TIMESTAMP_DEBUG
      37             : // #define MEMORY_DEBUG
      38             : 
      39             : static CPLMutex *hErrorMutex = nullptr;
      40             : static void *pErrorHandlerUserData = nullptr;
      41             : static CPLErrorHandler pfnErrorHandler = CPLDefaultErrorHandler;
      42             : static bool gbCatchDebug = true;
      43             : 
      44             : constexpr int DEFAULT_LAST_ERR_MSG_SIZE =
      45             : #if !defined(HAVE_VSNPRINTF)
      46             :     20000
      47             : #else
      48             :     500
      49             : #endif
      50             :     ;
      51             : 
      52             : typedef struct errHandler
      53             : {
      54             :     struct errHandler *psNext;
      55             :     void *pUserData;
      56             :     CPLErrorHandler pfnHandler;
      57             :     bool bCatchDebug;
      58             : } CPLErrorHandlerNode;
      59             : 
      60             : typedef struct
      61             : {
      62             :     CPLErrorNum nLastErrNo;
      63             :     CPLErr eLastErrType;
      64             :     CPLErrorHandlerNode *psHandlerStack;
      65             :     int nLastErrMsgMax;
      66             :     int nFailureIntoWarning;
      67             :     bool bProgressMode;
      68             :     bool bEmitNewlineBeforeNextDbgMsg;
      69             :     GUInt32 nErrorCounter;
      70             :     char szLastErrMsg[DEFAULT_LAST_ERR_MSG_SIZE];
      71             :     // Do not add anything here. szLastErrMsg must be the last field.
      72             :     // See CPLRealloc() below.
      73             : } CPLErrorContext;
      74             : 
      75             : constexpr CPLErrorContext sNoErrorContext = {0,     CE_None, nullptr, 0, 0,
      76             :                                              false, false,   0,       ""};
      77             : 
      78             : constexpr CPLErrorContext sWarningContext = {
      79             :     0, CE_Warning, nullptr, 0, 0, false, false, 0, "A warning was emitted"};
      80             : 
      81             : constexpr CPLErrorContext sFailureContext = {
      82             :     0, CE_Warning, nullptr, 0, 0, false, false, 0, "A failure was emitted"};
      83             : 
      84             : #define IS_PREFEFINED_ERROR_CTX(psCtxt)                                        \
      85             :     (psCtx == &sNoErrorContext || psCtx == &sWarningContext ||                 \
      86             :      psCtxt == &sFailureContext)
      87             : 
      88             : /************************************************************************/
      89             : /*                     CPLErrorContextGetString()                       */
      90             : /************************************************************************/
      91             : 
      92             : // Makes clang -fsanitize=undefined happy since it doesn't like
      93             : // dereferencing szLastErrMsg[i>=DEFAULT_LAST_ERR_MSG_SIZE]
      94             : 
      95      596535 : static char *CPLErrorContextGetString(CPLErrorContext *psCtxt)
      96             : {
      97      596535 :     return psCtxt->szLastErrMsg;
      98             : }
      99             : 
     100             : /************************************************************************/
     101             : /*                         CPLGetErrorContext()                         */
     102             : /************************************************************************/
     103             : 
     104    47181900 : static CPLErrorContext *CPLGetErrorContext()
     105             : 
     106             : {
     107    47181900 :     int bError = FALSE;
     108             :     CPLErrorContext *psCtx = reinterpret_cast<CPLErrorContext *>(
     109    47181900 :         CPLGetTLSEx(CTLS_ERRORCONTEXT, &bError));
     110    47178700 :     if (bError)
     111           0 :         return nullptr;
     112             : 
     113    47178700 :     if (psCtx == nullptr)
     114             :     {
     115             :         psCtx = static_cast<CPLErrorContext *>(
     116        3175 :             VSICalloc(sizeof(CPLErrorContext), 1));
     117        3176 :         if (psCtx == nullptr)
     118             :         {
     119           0 :             fprintf(stderr, "Out of memory attempting to report error.\n");
     120           0 :             return nullptr;
     121             :         }
     122        3176 :         psCtx->eLastErrType = CE_None;
     123        3176 :         psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg);
     124        3176 :         CPLSetTLS(CTLS_ERRORCONTEXT, psCtx, TRUE);
     125             :     }
     126             : 
     127    47178800 :     return psCtx;
     128             : }
     129             : 
     130             : /************************************************************************/
     131             : /*                         CPLGetErrorHandlerUserData()                 */
     132             : /************************************************************************/
     133             : 
     134             : /**
     135             :  * Fetch the user data for the error context
     136             :  *
     137             :  * Fetches the user data for the current error context.  You can
     138             :  * set the user data for the error context when you add your handler by
     139             :  * issuing CPLSetErrorHandlerEx() and CPLPushErrorHandlerEx().  Note that
     140             :  * user data is primarily intended for providing context within error handlers
     141             :  * themselves, but they could potentially be abused in other useful ways with
     142             :  * the usual caveat emptor understanding.
     143             :  *
     144             :  * @return the user data pointer for the error context
     145             :  */
     146             : 
     147     5946430 : void *CPL_STDCALL CPLGetErrorHandlerUserData(void)
     148             : {
     149             :     // get the current threadlocal or global error context user data
     150     5946430 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     151     5946430 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
     152           0 :         abort();
     153     5946430 :     return reinterpret_cast<void *>(psCtx->psHandlerStack
     154     5946430 :                                         ? psCtx->psHandlerStack->pUserData
     155     5946430 :                                         : pErrorHandlerUserData);
     156             : }
     157             : 
     158             : /************************************************************************/
     159             : /*                         CPLGetErrorHandler()                         */
     160             : /************************************************************************/
     161             : 
     162             : /**
     163             :  * Fetch the current error handler for the current error context.
     164             :  *
     165             :  * This will be the last error handler pushed in the thread-local error stack
     166             :  * with CPLPushErrorHandler()/CPLPushErrorHandlerEx(), or if the stack is
     167             :  * empty, the global error handler set with
     168             :  * CPLSetErrorHandler()/CPLSetErrorHandlerEx(), or the default global error
     169             :  * handler.
     170             :  *
     171             :  * @param[out] ppUserData Pointer to store the user data pointer. May be NULL
     172             :  * @since GDAL 3.7
     173             :  */
     174             : 
     175           0 : CPLErrorHandler CPLGetErrorHandler(void **ppUserData)
     176             : {
     177           0 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     178             : 
     179           0 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
     180             :     {
     181           0 :         fprintf(stderr, "CPLGetErrorHandler() failed.\n");
     182           0 :         if (ppUserData)
     183           0 :             *ppUserData = nullptr;
     184           0 :         return CPLDefaultErrorHandler;
     185             :     }
     186             : 
     187           0 :     if (psCtx->psHandlerStack != nullptr)
     188             :     {
     189           0 :         if (ppUserData)
     190           0 :             *ppUserData = psCtx->psHandlerStack->pUserData;
     191           0 :         return psCtx->psHandlerStack->pfnHandler;
     192             :     }
     193             : 
     194           0 :     CPLMutexHolderD(&hErrorMutex);
     195           0 :     if (ppUserData)
     196           0 :         *ppUserData = pErrorHandlerUserData;
     197           0 :     return pfnErrorHandler;
     198             : }
     199             : 
     200             : /************************************************************************/
     201             : /*                          ApplyErrorHandler()                         */
     202             : /************************************************************************/
     203             : 
     204       96077 : static void ApplyErrorHandler(CPLErrorContext *psCtx, CPLErr eErrClass,
     205             :                               CPLErrorNum err_no, const char *pszMessage)
     206             : {
     207       96077 :     bool bProcessed = false;
     208             : 
     209       96077 :     if (psCtx->psHandlerStack != nullptr)
     210             :     {
     211             :         // iterate through the threadlocal handler stack
     212       56671 :         if ((eErrClass != CE_Debug) || psCtx->psHandlerStack->bCatchDebug)
     213             :         {
     214             :             // call the error handler
     215       56594 :             CPLErrorHandlerNode *psNewCurNode = psCtx->psHandlerStack;
     216       56594 :             psCtx->psHandlerStack->pfnHandler(eErrClass, err_no, pszMessage);
     217       56594 :             if (psNewCurNode != psCtx->psHandlerStack)
     218             :             {
     219           0 :                 fprintf(stderr, "ApplyErrorHandler() has detected that a "
     220             :                                 "previous error handler messed up with the "
     221             :                                 "error stack. Chaos guaranteed!\n");
     222             :             }
     223       56594 :             bProcessed = true;
     224             :         }
     225             :         else
     226             :         {
     227             :             // need to iterate to a parent handler for debug messages
     228          77 :             CPLErrorHandlerNode *psNode = psCtx->psHandlerStack->psNext;
     229          77 :             while (psNode != nullptr)
     230             :             {
     231          76 :                 if (psNode->bCatchDebug)
     232             :                 {
     233          76 :                     CPLErrorHandlerNode *psBackupCurNode =
     234             :                         psCtx->psHandlerStack;
     235          76 :                     psCtx->psHandlerStack = psNode;
     236          76 :                     CPLErrorHandlerNode *psNewCurNode = psCtx->psHandlerStack;
     237          76 :                     psNode->pfnHandler(eErrClass, err_no, pszMessage);
     238             :                     // cppcheck-suppress knownConditionTrueFalse
     239          76 :                     if (psNewCurNode != psCtx->psHandlerStack)
     240             :                     {
     241           0 :                         fprintf(stderr,
     242             :                                 "ApplyErrorHandler() has detected that a "
     243             :                                 "previous error handler messed up with the "
     244             :                                 "error stack. Chaos guaranteed!\n");
     245             :                     }
     246          76 :                     psCtx->psHandlerStack = psBackupCurNode;
     247          76 :                     bProcessed = true;
     248          76 :                     break;
     249             :                 }
     250           0 :                 psNode = psNode->psNext;
     251             :             }
     252             :         }
     253             :     }
     254             : 
     255       96077 :     if (!bProcessed)
     256             :     {
     257             :         // hit the global error handler
     258       78814 :         CPLMutexHolderD(&hErrorMutex);
     259       39407 :         if ((eErrClass != CE_Debug) || gbCatchDebug)
     260             :         {
     261       39405 :             if (pfnErrorHandler != nullptr)
     262             :             {
     263       39403 :                 pfnErrorHandler(eErrClass, err_no, pszMessage);
     264             :             }
     265             :         }
     266             :         else /* if( eErrClass == CE_Debug ) */
     267             :         {
     268             :             // for CPLDebug messages we propagate to the default error handler
     269           2 :             CPLDefaultErrorHandler(eErrClass, err_no, pszMessage);
     270             :         }
     271             :     }
     272       96077 : }
     273             : 
     274             : /**********************************************************************
     275             :  *                          CPLError()
     276             :  **********************************************************************/
     277             : 
     278             : /**
     279             :  * Report an error.
     280             :  *
     281             :  * This function reports an error in a manner that can be hooked
     282             :  * and reported appropriate by different applications.
     283             :  *
     284             :  * The effect of this function can be altered by applications by installing
     285             :  * a custom error handling using CPLSetErrorHandler().
     286             :  *
     287             :  * The eErrClass argument can have the value CE_Warning indicating that the
     288             :  * message is an informational warning, CE_Failure indicating that the
     289             :  * action failed, but that normal recover mechanisms will be used or
     290             :  * CE_Fatal meaning that a fatal error has occurred, and that CPLError()
     291             :  * should not return.
     292             :  *
     293             :  * The default behavior of CPLError() is to report errors to stderr,
     294             :  * and to abort() after reporting a CE_Fatal error.  It is expected that
     295             :  * some applications will want to suppress error reporting, and will want to
     296             :  * install a C++ exception, or longjmp() approach to no local fatal error
     297             :  * recovery.
     298             :  *
     299             :  * Regardless of how application error handlers or the default error
     300             :  * handler choose to handle an error, the error number, and message will
     301             :  * be stored for recovery with CPLGetLastErrorNo() and CPLGetLastErrorMsg().
     302             :  *
     303             :  * @param eErrClass one of CE_Warning, CE_Failure or CE_Fatal.
     304             :  * @param err_no the error number (CPLE_*) from cpl_error.h.
     305             :  * @param fmt a printf() style format string.  Any additional arguments
     306             :  * will be treated as arguments to fill in this format in a manner
     307             :  * similar to printf().
     308             :  */
     309             : 
     310       94244 : void CPLError(CPLErr eErrClass, CPLErrorNum err_no,
     311             :               CPL_FORMAT_STRING(const char *fmt), ...)
     312             : {
     313             :     va_list args;
     314             : 
     315             :     // Expand the error message.
     316       94244 :     va_start(args, fmt);
     317       94244 :     CPLErrorV(eErrClass, err_no, fmt, args);
     318       94260 :     va_end(args);
     319       94260 : }
     320             : 
     321             : /************************************************************************/
     322             : /*                             CPLErrorV()                              */
     323             : /************************************************************************/
     324             : 
     325             : /** Same as CPLError() but with a va_list */
     326       95277 : void CPLErrorV(CPLErr eErrClass, CPLErrorNum err_no, const char *fmt,
     327             :                va_list args)
     328             : {
     329       95277 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     330       95271 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
     331             :     {
     332          41 :         int bMemoryError = FALSE;
     333          41 :         if (eErrClass == CE_Warning)
     334             :         {
     335           0 :             CPLSetTLSWithFreeFuncEx(
     336             :                 CTLS_ERRORCONTEXT,
     337             :                 reinterpret_cast<void *>(
     338             :                     const_cast<CPLErrorContext *>(&sWarningContext)),
     339             :                 nullptr, &bMemoryError);
     340             :         }
     341          41 :         else if (eErrClass == CE_Failure)
     342             :         {
     343           0 :             CPLSetTLSWithFreeFuncEx(
     344             :                 CTLS_ERRORCONTEXT,
     345             :                 reinterpret_cast<void *>(
     346             :                     const_cast<CPLErrorContext *>(&sFailureContext)),
     347             :                 nullptr, &bMemoryError);
     348             :         }
     349             : 
     350             :         // TODO: Is it possible to move the entire szShortMessage under the if
     351             :         // pfnErrorHandler?
     352          41 :         char szShortMessage[80] = {};
     353          41 :         CPLvsnprintf(szShortMessage, sizeof(szShortMessage), fmt, args);
     354             : 
     355           0 :         CPLMutexHolderD(&hErrorMutex);
     356           0 :         if (pfnErrorHandler != nullptr)
     357           0 :             pfnErrorHandler(eErrClass, err_no, szShortMessage);
     358           0 :         return;
     359             :     }
     360             : 
     361       95230 :     if (psCtx->nFailureIntoWarning > 0 && eErrClass == CE_Failure)
     362           5 :         eErrClass = CE_Warning;
     363             : 
     364             : /* -------------------------------------------------------------------- */
     365             : /*      Expand the error message                                        */
     366             : /* -------------------------------------------------------------------- */
     367             : #if defined(HAVE_VSNPRINTF)
     368             :     {
     369             :         va_list wrk_args;
     370             : 
     371             : #ifdef va_copy
     372       95230 :         va_copy(wrk_args, args);
     373             : #else
     374             :         wrk_args = args;
     375             : #endif
     376             : 
     377             :         /* --------------------------------------------------------------------
     378             :          */
     379             :         /*      If CPL_ACCUM_ERROR_MSG=ON accumulate the error messages, */
     380             :         /*      rather than just replacing the last error message. */
     381             :         /* --------------------------------------------------------------------
     382             :          */
     383       95230 :         int nPreviousSize = 0;
     384      151748 :         if (psCtx->psHandlerStack != nullptr &&
     385       56454 :             EQUAL(CPLGetConfigOption("CPL_ACCUM_ERROR_MSG", ""), "ON"))
     386             :         {
     387           0 :             nPreviousSize = static_cast<int>(strlen(psCtx->szLastErrMsg));
     388           0 :             if (nPreviousSize)
     389             :             {
     390           0 :                 if (nPreviousSize + 1 + 1 >= psCtx->nLastErrMsgMax)
     391             :                 {
     392           0 :                     psCtx->nLastErrMsgMax *= 3;
     393             :                     psCtx = static_cast<CPLErrorContext *>(
     394           0 :                         CPLRealloc(psCtx, sizeof(CPLErrorContext) -
     395             :                                               DEFAULT_LAST_ERR_MSG_SIZE +
     396           0 :                                               psCtx->nLastErrMsgMax + 1));
     397           0 :                     CPLSetTLS(CTLS_ERRORCONTEXT, psCtx, TRUE);
     398             :                 }
     399           0 :                 char *pszLastErrMsg = CPLErrorContextGetString(psCtx);
     400           0 :                 pszLastErrMsg[nPreviousSize] = '\n';
     401           0 :                 pszLastErrMsg[nPreviousSize + 1] = '\0';
     402           0 :                 nPreviousSize++;
     403             :             }
     404             :         }
     405             : 
     406       95294 :         int nPR = 0;
     407          15 :         while (((nPR = CPLvsnprintf(psCtx->szLastErrMsg + nPreviousSize,
     408       95309 :                                     psCtx->nLastErrMsgMax - nPreviousSize, fmt,
     409       95226 :                                     wrk_args)) == -1 ||
     410       95257 :                 nPR >= psCtx->nLastErrMsgMax - nPreviousSize - 1) &&
     411          23 :                psCtx->nLastErrMsgMax < 1000000)
     412             :         {
     413             : #ifdef va_copy
     414          15 :             va_end(wrk_args);
     415          15 :             va_copy(wrk_args, args);
     416             : #else
     417             :             wrk_args = args;
     418             : #endif
     419          15 :             psCtx->nLastErrMsgMax *= 3;
     420          30 :             psCtx = static_cast<CPLErrorContext *>(CPLRealloc(
     421             :                 psCtx, sizeof(CPLErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE +
     422          15 :                            psCtx->nLastErrMsgMax + 1));
     423          15 :             CPLSetTLS(CTLS_ERRORCONTEXT, psCtx, TRUE);
     424             :         }
     425             : 
     426       95219 :         va_end(wrk_args);
     427             :     }
     428             : #else
     429             :     // !HAVE_VSNPRINTF
     430             :     CPLvsnprintf(psCtx->szLastErrMsg, psCtx->nLastErrMsgMax, fmt, args);
     431             : #endif
     432             : 
     433             :     /* -------------------------------------------------------------------- */
     434             :     /*      Obfuscate any password in error message                         */
     435             :     /* -------------------------------------------------------------------- */
     436       95219 :     char *pszPassword = strstr(psCtx->szLastErrMsg, "password=");
     437       95219 :     if (pszPassword != nullptr)
     438             :     {
     439           0 :         char *pszIter = pszPassword + strlen("password=");
     440           0 :         while (*pszIter != ' ' && *pszIter != '\0')
     441             :         {
     442           0 :             *pszIter = 'X';
     443           0 :             pszIter++;
     444             :         }
     445             :     }
     446             : 
     447             :     /* -------------------------------------------------------------------- */
     448             :     /*      If the user provided an handling function, then                 */
     449             :     /*      call it, otherwise print the error to stderr and return.        */
     450             :     /* -------------------------------------------------------------------- */
     451       95219 :     psCtx->nLastErrNo = err_no;
     452       95219 :     psCtx->eLastErrType = eErrClass;
     453       95219 :     if (psCtx->nErrorCounter == ~(0U))
     454           0 :         psCtx->nErrorCounter = 0;
     455             :     else
     456       95219 :         psCtx->nErrorCounter++;
     457             : 
     458       95219 :     if (CPLGetConfigOption("CPL_LOG_ERRORS", nullptr) != nullptr)
     459           0 :         CPLDebug("CPLError", "%s", psCtx->szLastErrMsg);
     460             : 
     461             :     /* -------------------------------------------------------------------- */
     462             :     /*      Invoke the current error handler.                               */
     463             :     /* -------------------------------------------------------------------- */
     464       95301 :     ApplyErrorHandler(psCtx, eErrClass, err_no, psCtx->szLastErrMsg);
     465             : 
     466       95301 :     if (eErrClass == CE_Fatal)
     467           0 :         abort();
     468             : }
     469             : 
     470             : /************************************************************************/
     471             : /*                         CPLEmergencyError()                          */
     472             : /************************************************************************/
     473             : 
     474             : /**
     475             :  * Fatal error when things are bad.
     476             :  *
     477             :  * This function should be called in an emergency situation where
     478             :  * it is unlikely that a regular error report would work.  This would
     479             :  * include in the case of heap exhaustion for even small allocations,
     480             :  * or any failure in the process of reporting an error (such as TLS
     481             :  * allocations).
     482             :  *
     483             :  * This function should never return.  After the error message has been
     484             :  * reported as best possible, the application will abort() similarly to how
     485             :  * CPLError() aborts on CE_Fatal class errors.
     486             :  *
     487             :  * @param pszMessage the error message to report.
     488             :  */
     489             : 
     490           0 : void CPLEmergencyError(const char *pszMessage)
     491             : {
     492             :     static bool bInEmergencyError = false;
     493             : 
     494             :     // If we are already in emergency error then one of the
     495             :     // following failed, so avoid them the second time through.
     496           0 :     if (!bInEmergencyError)
     497             :     {
     498           0 :         bInEmergencyError = true;
     499             :         CPLErrorContext *psCtx =
     500           0 :             static_cast<CPLErrorContext *>(CPLGetTLS(CTLS_ERRORCONTEXT));
     501             : 
     502           0 :         ApplyErrorHandler(psCtx, CE_Fatal, CPLE_AppDefined, pszMessage);
     503             :     }
     504             : 
     505             :     // Ultimate fallback.
     506           0 :     fprintf(stderr, "FATAL: %s\n", pszMessage);
     507             : 
     508           0 :     abort();
     509             : }
     510             : 
     511             : /************************************************************************/
     512             : /*                    CPLGetProcessMemorySize()                         */
     513             : /************************************************************************/
     514             : 
     515             : #ifdef MEMORY_DEBUG
     516             : 
     517             : #ifdef __linux
     518             : static int CPLGetProcessMemorySize()
     519             : {
     520             :     FILE *fp = fopen("/proc/self/status", "r");
     521             :     if (fp == nullptr)
     522             :         return -1;
     523             :     int nRet = -1;
     524             :     char szLine[128] = {};
     525             :     while (fgets(szLine, sizeof(szLine), fp) != nullptr)
     526             :     {
     527             :         if (STARTS_WITH(szLine, "VmSize:"))
     528             :         {
     529             :             const char *pszPtr = szLine;
     530             :             while (!(*pszPtr == '\0' || (*pszPtr >= '0' && *pszPtr <= '9')))
     531             :                 pszPtr++;
     532             :             nRet = atoi(pszPtr);
     533             :             break;
     534             :         }
     535             :     }
     536             :     fclose(fp);
     537             :     return nRet;
     538             : }
     539             : #else
     540             : #error CPLGetProcessMemorySize() unimplemented for this OS
     541             : #endif
     542             : 
     543             : #endif  // def MEMORY_DEBUG
     544             : 
     545             : /************************************************************************/
     546             : /*                        CPLGettimeofday()                             */
     547             : /************************************************************************/
     548             : 
     549             : #if defined(_WIN32) && !defined(__CYGWIN__)
     550             : #include <sys/timeb.h>
     551             : 
     552             : namespace
     553             : {
     554             : struct CPLTimeVal
     555             : {
     556             :     time_t tv_sec; /* seconds */
     557             :     long tv_usec;  /* and microseconds */
     558             : };
     559             : }  // namespace
     560             : 
     561             : static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
     562             : {
     563             :     struct _timeb theTime;
     564             : 
     565             :     _ftime(&theTime);
     566             :     tp->tv_sec = static_cast<time_t>(theTime.time);
     567             :     tp->tv_usec = theTime.millitm * 1000;
     568             :     return 0;
     569             : }
     570             : #else
     571             : #include <sys/time.h> /* for gettimeofday() */
     572             : #define CPLTimeVal timeval
     573             : #define CPLGettimeofday(t, u) gettimeofday(t, u)
     574             : #endif
     575             : 
     576             : #ifndef WITHOUT_CPLDEBUG
     577             : 
     578             : /************************************************************************/
     579             : /*                             CPLvDebug()                              */
     580             : /************************************************************************/
     581             : 
     582      622330 : static void CPLvDebug(const char *pszCategory,
     583             :                       CPL_FORMAT_STRING(const char *pszFormat), va_list args)
     584             : {
     585      622330 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     586      622303 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
     587         101 :         return;
     588      622202 :     const char *pszDebug = CPLGetConfigOption("CPL_DEBUG", nullptr);
     589             : 
     590             :     /* -------------------------------------------------------------------- */
     591             :     /*      Does this message pass our current criteria?                    */
     592             :     /* -------------------------------------------------------------------- */
     593      622509 :     if (pszDebug == nullptr || EQUAL(pszDebug, "NO") ||
     594         859 :         EQUAL(pszDebug, "OFF") || EQUAL(pszDebug, "FALSE") ||
     595         840 :         EQUAL(pszDebug, "0"))
     596             :     {
     597      621670 :         return;
     598             :     }
     599             : 
     600         839 :     if (!EQUAL(pszDebug, "ON") && !EQUAL(pszDebug, "YES") &&
     601         131 :         !EQUAL(pszDebug, "TRUE") && !EQUAL(pszDebug, "1") &&
     602         125 :         !EQUAL(pszDebug, ""))
     603             :     {
     604             :         // check if value of CPL_DEBUG contains the category
     605         124 :         const size_t nLen = strlen(pszCategory);
     606             : 
     607         124 :         size_t i = 0;
     608         873 :         for (i = 0; pszDebug[i] != '\0'; i++)
     609             :         {
     610         810 :             if (EQUALN(pszCategory, pszDebug + i, nLen))
     611          61 :                 break;
     612             :         }
     613             : 
     614         124 :         if (pszDebug[i] == '\0')
     615          63 :             return;
     616             :     }
     617             : 
     618             :     /* -------------------------------------------------------------------- */
     619             :     /*    Allocate a block for the error.                                   */
     620             :     /* -------------------------------------------------------------------- */
     621         776 :     const int ERROR_MAX = 25000;
     622         776 :     char *pszMessage = static_cast<char *>(VSIMalloc(ERROR_MAX));
     623         776 :     if (pszMessage == nullptr)
     624           0 :         return;
     625             : 
     626             :     /* -------------------------------------------------------------------- */
     627             :     /*      Dal -- always log a timestamp as the first part of the line     */
     628             :     /*      to ensure one is looking at what one should be looking at!      */
     629             :     /* -------------------------------------------------------------------- */
     630             : 
     631         776 :     pszMessage[0] = '\0';
     632             : #ifdef TIMESTAMP_DEBUG
     633         776 :     if (CPLTestBool(CPLGetConfigOption("CPL_TIMESTAMP", "NO")))
     634             :     {
     635             :         static struct CPLTimeVal tvStart;
     636           4 :         static const auto unused = CPLGettimeofday(&tvStart, nullptr);
     637           4 :         CPL_IGNORE_RET_VAL(unused);
     638             :         struct CPLTimeVal tv;
     639           4 :         CPLGettimeofday(&tv, nullptr);
     640           4 :         strcpy(pszMessage, "[");
     641           4 :         strcat(pszMessage, VSICTime(static_cast<unsigned long>(tv.tv_sec)));
     642             : 
     643             :         // On windows anyway, ctime puts a \n at the end, but I'm not
     644             :         // convinced this is standard behavior, so we'll get rid of it
     645             :         // carefully
     646             : 
     647           4 :         if (pszMessage[strlen(pszMessage) - 1] == '\n')
     648             :         {
     649           4 :             pszMessage[strlen(pszMessage) - 1] = 0;  // blow it out
     650             :         }
     651           4 :         CPLsnprintf(pszMessage + strlen(pszMessage),
     652           4 :                     ERROR_MAX - strlen(pszMessage),
     653           4 :                     "].%04d, %03.04f: ", static_cast<int>(tv.tv_usec / 100),
     654           4 :                     tv.tv_sec + tv.tv_usec * 1e-6 -
     655           4 :                         (tvStart.tv_sec + tvStart.tv_usec * 1e-6));
     656             :     }
     657             : #endif
     658             : 
     659             : /* -------------------------------------------------------------------- */
     660             : /*      Add the process memory size.                                    */
     661             : /* -------------------------------------------------------------------- */
     662             : #ifdef MEMORY_DEBUG
     663             :     char szVmSize[32] = {};
     664             :     CPLsprintf(szVmSize, "[VmSize: %d] ", CPLGetProcessMemorySize());
     665             :     strcat(pszMessage, szVmSize);
     666             : #endif
     667             : 
     668             :     /* -------------------------------------------------------------------- */
     669             :     /*      Add the category.                                               */
     670             :     /* -------------------------------------------------------------------- */
     671         776 :     strcat(pszMessage, pszCategory);
     672         776 :     strcat(pszMessage, ": ");
     673             : 
     674             :     /* -------------------------------------------------------------------- */
     675             :     /*      Format the application provided portion of the debug message.   */
     676             :     /* -------------------------------------------------------------------- */
     677         776 :     CPLvsnprintf(pszMessage + strlen(pszMessage),
     678         776 :                  ERROR_MAX - strlen(pszMessage), pszFormat, args);
     679             : 
     680             :     /* -------------------------------------------------------------------- */
     681             :     /*      Obfuscate any password in error message                         */
     682             :     /* -------------------------------------------------------------------- */
     683             : 
     684         776 :     char *pszPassword = strstr(pszMessage, "password=");
     685         776 :     if (pszPassword != nullptr)
     686             :     {
     687           0 :         char *pszIter = pszPassword + strlen("password=");
     688           0 :         while (*pszIter != ' ' && *pszIter != '\0')
     689             :         {
     690           0 :             *pszIter = 'X';
     691           0 :             pszIter++;
     692             :         }
     693             :     }
     694             : 
     695             :     /* -------------------------------------------------------------------- */
     696             :     /*      Invoke the current error handler.                               */
     697             :     /* -------------------------------------------------------------------- */
     698         776 :     ApplyErrorHandler(psCtx, CE_Debug, CPLE_None, pszMessage);
     699             : 
     700         776 :     VSIFree(pszMessage);
     701             : }
     702             : 
     703             : #endif  // !WITHOUT_CPLDEBUG
     704             : 
     705             : /************************************************************************/
     706             : /*                              CPLDebug()                              */
     707             : /************************************************************************/
     708             : 
     709             : /**
     710             :  * Display a debugging message.
     711             :  *
     712             :  * The category argument is used in conjunction with the CPL_DEBUG
     713             :  * environment variable to establish if the message should be displayed.
     714             :  * If the CPL_DEBUG environment variable is not set, no debug messages
     715             :  * are emitted (use CPLError(CE_Warning, ...) to ensure messages are displayed).
     716             :  * If CPL_DEBUG is set, but is an empty string or the word "ON" then all
     717             :  * debug messages are shown.  Otherwise only messages whose category appears
     718             :  * somewhere within the CPL_DEBUG value are displayed (as determined by
     719             :  * strstr()).
     720             :  *
     721             :  * Categories are usually an identifier for the subsystem producing the
     722             :  * error.  For instance "GDAL" might be used for the GDAL core, and "TIFF"
     723             :  * for messages from the TIFF translator.
     724             :  *
     725             :  * @param pszCategory name of the debugging message category.
     726             :  * @param pszFormat printf() style format string for message to display.
     727             :  *        Remaining arguments are assumed to be for format.
     728             :  */
     729             : 
     730             : #ifdef WITHOUT_CPLDEBUG
     731             : // Do not include CPLDebug.  Only available in custom builds.
     732             : #else
     733             : 
     734      622426 : void CPLDebug(const char *pszCategory, CPL_FORMAT_STRING(const char *pszFormat),
     735             :               ...)
     736             : 
     737             : {
     738             :     va_list args;
     739      622426 :     va_start(args, pszFormat);
     740      622426 :     CPLvDebug(pszCategory, pszFormat, args);
     741      622507 :     va_end(args);
     742      622507 : }
     743             : 
     744             : #endif  // WITHOUT_CPLDEBUG
     745             : 
     746             : /************************************************************************/
     747             : /*                         CPLDebugProgress()                           */
     748             : /************************************************************************/
     749             : 
     750             : /**
     751             :  * Display a debugging message indicating a progression.
     752             :  *
     753             :  * This is the same as CPLDebug(), except that when displaying on the terminal,
     754             :  * it will erase the previous debug progress message. This is for example
     755             :  * appropriate to display increasing percentages for a task.
     756             :  *
     757             :  * The category argument is used in conjunction with the CPL_DEBUG
     758             :  * environment variable to establish if the message should be displayed.
     759             :  * If the CPL_DEBUG environment variable is not set, no debug messages
     760             :  * are emitted (use CPLError(CE_Warning, ...) to ensure messages are displayed).
     761             :  * If CPL_DEBUG is set, but is an empty string or the word "ON" then all
     762             :  * debug messages are shown.  Otherwise only messages whose category appears
     763             :  * somewhere within the CPL_DEBUG value are displayed (as determined by
     764             :  * strstr()).
     765             :  *
     766             :  * Categories are usually an identifier for the subsystem producing the
     767             :  * error.  For instance "GDAL" might be used for the GDAL core, and "TIFF"
     768             :  * for messages from the TIFF translator.
     769             :  *
     770             :  * @param pszCategory name of the debugging message category.
     771             :  * @param pszFormat printf() style format string for message to display.
     772             :  *        Remaining arguments are assumed to be for format.
     773             :  * @since 3.9
     774             :  */
     775             : 
     776             : #ifdef WITHOUT_CPLDEBUG
     777             : // Do not include CPLDebugProgress. Only available in custom builds.
     778             : #else
     779           2 : void CPLDebugProgress(const char *pszCategory,
     780             :                       CPL_FORMAT_STRING(const char *pszFormat), ...)
     781             : 
     782             : {
     783           2 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     784           2 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
     785           0 :         return;
     786             : 
     787           2 :     psCtx->bProgressMode = true;
     788             : 
     789             :     va_list args;
     790           2 :     va_start(args, pszFormat);
     791           2 :     CPLvDebug(pszCategory, pszFormat, args);
     792           2 :     va_end(args);
     793             : 
     794           2 :     psCtx->bProgressMode = false;
     795             : }
     796             : #endif  // !WITHOUT_CPLDEBUG
     797             : 
     798             : /**********************************************************************
     799             :  *                          CPLErrorReset()
     800             :  **********************************************************************/
     801             : 
     802             : /**
     803             :  * Erase any traces of previous errors.
     804             :  *
     805             :  * This is normally used to ensure that an error which has been recovered
     806             :  * from does not appear to be still in play with high level functions.
     807             :  */
     808             : 
     809     6689970 : void CPL_STDCALL CPLErrorReset()
     810             : {
     811     6689970 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     812     6689970 :     if (psCtx == nullptr)
     813           0 :         return;
     814     6689970 :     if (IS_PREFEFINED_ERROR_CTX(psCtx))
     815             :     {
     816           1 :         int bMemoryError = FALSE;
     817           1 :         CPLSetTLSWithFreeFuncEx(
     818             :             CTLS_ERRORCONTEXT,
     819             :             reinterpret_cast<void *>(
     820             :                 const_cast<CPLErrorContext *>(&sNoErrorContext)),
     821             :             nullptr, &bMemoryError);
     822           0 :         return;
     823             :     }
     824             : 
     825     6689970 :     psCtx->nLastErrNo = CPLE_None;
     826     6689970 :     psCtx->szLastErrMsg[0] = '\0';
     827     6689970 :     psCtx->eLastErrType = CE_None;
     828     6689970 :     psCtx->nErrorCounter = 0;
     829             : }
     830             : 
     831             : /**********************************************************************
     832             :  *                       CPLErrorSetState()
     833             :  **********************************************************************/
     834             : 
     835      596542 : static void CPLErrorSetState(CPLErr eErrClass, CPLErrorNum err_no,
     836             :                              const char *pszMsg, GUInt32 *pnErrorCounter)
     837             : {
     838      596542 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     839      596528 :     if (psCtx == nullptr)
     840           0 :         return;
     841      596528 :     if (IS_PREFEFINED_ERROR_CTX(psCtx))
     842             :     {
     843           0 :         int bMemoryError = FALSE;
     844           0 :         if (eErrClass == CE_None)
     845           0 :             CPLSetTLSWithFreeFuncEx(
     846             :                 CTLS_ERRORCONTEXT,
     847             :                 reinterpret_cast<void *>(
     848             :                     const_cast<CPLErrorContext *>(&sNoErrorContext)),
     849             :                 nullptr, &bMemoryError);
     850           0 :         else if (eErrClass == CE_Warning)
     851           0 :             CPLSetTLSWithFreeFuncEx(
     852             :                 CTLS_ERRORCONTEXT,
     853             :                 reinterpret_cast<void *>(
     854             :                     const_cast<CPLErrorContext *>(&sWarningContext)),
     855             :                 nullptr, &bMemoryError);
     856           0 :         else if (eErrClass == CE_Failure)
     857           0 :             CPLSetTLSWithFreeFuncEx(
     858             :                 CTLS_ERRORCONTEXT,
     859             :                 reinterpret_cast<void *>(
     860             :                     const_cast<CPLErrorContext *>(&sFailureContext)),
     861             :                 nullptr, &bMemoryError);
     862           0 :         return;
     863             :     }
     864             : 
     865      596529 :     psCtx->nLastErrNo = err_no;
     866     1193060 :     const size_t size = std::min(static_cast<size_t>(psCtx->nLastErrMsgMax - 1),
     867      596529 :                                  strlen(pszMsg));
     868      596536 :     char *pszLastErrMsg = CPLErrorContextGetString(psCtx);
     869      596531 :     memcpy(pszLastErrMsg, pszMsg, size);
     870      596531 :     pszLastErrMsg[size] = '\0';
     871      596531 :     psCtx->eLastErrType = eErrClass;
     872      596531 :     if (pnErrorCounter)
     873      100301 :         psCtx->nErrorCounter = *pnErrorCounter;
     874             : }
     875             : 
     876             : /**
     877             :  * Restore an error state, without emitting an error.
     878             :  *
     879             :  * Can be useful if a routine might call CPLErrorReset() and one wants to
     880             :  * preserve the previous error state.
     881             :  *
     882             :  * @since GDAL 2.0
     883             :  */
     884             : 
     885      496238 : void CPL_DLL CPLErrorSetState(CPLErr eErrClass, CPLErrorNum err_no,
     886             :                               const char *pszMsg)
     887             : {
     888      496238 :     CPLErrorSetState(eErrClass, err_no, pszMsg, nullptr);
     889      496239 : }
     890             : 
     891             : /**********************************************************************
     892             :  *                          CPLGetLastErrorNo()
     893             :  **********************************************************************/
     894             : 
     895             : /**
     896             :  * Fetch the last error number.
     897             :  *
     898             :  * Fetches the last error number posted with CPLError(), that hasn't
     899             :  * been cleared by CPLErrorReset().  This is the error number, not the error
     900             :  * class.
     901             :  *
     902             :  * @return the error number of the last error to occur, or CPLE_None (0)
     903             :  * if there are no posted errors.
     904             :  */
     905             : 
     906      726492 : CPLErrorNum CPL_STDCALL CPLGetLastErrorNo()
     907             : {
     908      726492 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     909      725408 :     if (psCtx == nullptr)
     910           0 :         return 0;
     911             : 
     912      725408 :     return psCtx->nLastErrNo;
     913             : }
     914             : 
     915             : /**********************************************************************
     916             :  *                          CPLGetLastErrorType()
     917             :  **********************************************************************/
     918             : 
     919             : /**
     920             :  * Fetch the last error type.
     921             :  *
     922             :  * Fetches the last error type posted with CPLError(), that hasn't
     923             :  * been cleared by CPLErrorReset().  This is the error class, not the error
     924             :  * number.
     925             :  *
     926             :  * @return the error type of the last error to occur, or CE_None (0)
     927             :  * if there are no posted errors.
     928             :  */
     929             : 
     930     9229440 : CPLErr CPL_STDCALL CPLGetLastErrorType()
     931             : {
     932     9229440 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     933     9229430 :     if (psCtx == nullptr)
     934           0 :         return CE_None;
     935             : 
     936     9229430 :     return psCtx->eLastErrType;
     937             : }
     938             : 
     939             : /**********************************************************************
     940             :  *                          CPLGetLastErrorMsg()
     941             :  **********************************************************************/
     942             : 
     943             : /**
     944             :  * Get the last error message.
     945             :  *
     946             :  * Fetches the last error message posted with CPLError(), that hasn't
     947             :  * been cleared by CPLErrorReset().  The returned pointer is to an internal
     948             :  * string that should not be altered or freed.
     949             :  *
     950             :  * @return the last error message, or an empty string ("") if there is no
     951             :  * posted error message.
     952             :  */
     953             : 
     954      347831 : const char *CPL_STDCALL CPLGetLastErrorMsg()
     955             : {
     956      347831 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     957      347833 :     if (psCtx == nullptr)
     958           0 :         return "";
     959             : 
     960      347833 :     return psCtx->szLastErrMsg;
     961             : }
     962             : 
     963             : /**********************************************************************
     964             :  *                          CPLGetErrorCounter()
     965             :  **********************************************************************/
     966             : 
     967             : /**
     968             :  * Get the error counter
     969             :  *
     970             :  * Fetches the number of errors emitted in the current error context,
     971             :  * since the last call to CPLErrorReset()
     972             :  *
     973             :  * @return the error counter.
     974             :  * @since GDAL 2.3
     975             :  */
     976             : 
     977    10533700 : GUInt32 CPL_STDCALL CPLGetErrorCounter()
     978             : {
     979    10533700 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     980    10530800 :     if (psCtx == nullptr)
     981           0 :         return 0;
     982             : 
     983    10530800 :     return psCtx->nErrorCounter;
     984             : }
     985             : 
     986             : /************************************************************************/
     987             : /*                       CPLDefaultErrorHandler()                       */
     988             : /************************************************************************/
     989             : 
     990             : static FILE *fpLog = stderr;
     991             : static bool bLogInit = false;
     992             : 
     993           1 : static FILE *CPLfopenUTF8(const char *pszFilename, const char *pszAccess)
     994             : {
     995             :     FILE *f;
     996             : #ifdef _WIN32
     997             :     wchar_t *pwszFilename =
     998             :         CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
     999             :     wchar_t *pwszAccess =
    1000             :         CPLRecodeToWChar(pszAccess, CPL_ENC_UTF8, CPL_ENC_UCS2);
    1001             :     f = _wfopen(pwszFilename, pwszAccess);
    1002             :     VSIFree(pwszFilename);
    1003             :     VSIFree(pwszAccess);
    1004             : #else
    1005           1 :     f = fopen(pszFilename, pszAccess);
    1006             : #endif
    1007           1 :     return f;
    1008             : }
    1009             : 
    1010             : /** Default error handler. */
    1011       58411 : void CPL_STDCALL CPLDefaultErrorHandler(CPLErr eErrClass, CPLErrorNum nError,
    1012             :                                         const char *pszErrorMsg)
    1013             : 
    1014             : {
    1015             :     static int nCount = 0;
    1016             :     static int nMaxErrors = -1;
    1017             :     static const char *pszErrorSeparator = ":";
    1018             : 
    1019       58411 :     if (eErrClass != CE_Debug)
    1020             :     {
    1021       57781 :         if (nMaxErrors == -1)
    1022             :         {
    1023         206 :             nMaxErrors =
    1024         206 :                 atoi(CPLGetConfigOption("CPL_MAX_ERROR_REPORTS", "1000"));
    1025             :             // If running GDAL as a CustomBuild Command os MSBuild, "ERROR bla:"
    1026             :             // is considered as failing the job. This is rarely the intended
    1027             :             // behavior
    1028         206 :             pszErrorSeparator = CPLGetConfigOption("CPL_ERROR_SEPARATOR", ":");
    1029             :         }
    1030             : 
    1031       57781 :         nCount++;
    1032       57781 :         if (nCount > nMaxErrors && nMaxErrors > 0)
    1033       54507 :             return;
    1034             :     }
    1035             : 
    1036        3904 :     if (!bLogInit)
    1037             :     {
    1038         220 :         bLogInit = true;
    1039             : 
    1040         220 :         fpLog = stderr;
    1041         220 :         const char *pszLog = CPLGetConfigOption("CPL_LOG", nullptr);
    1042         220 :         if (pszLog != nullptr)
    1043             :         {
    1044             :             const bool bAppend =
    1045           1 :                 CPLGetConfigOption("CPL_LOG_APPEND", nullptr) != nullptr;
    1046           1 :             const char *pszAccess = bAppend ? "at" : "wt";
    1047           1 :             fpLog = CPLfopenUTF8(pszLog, pszAccess);
    1048           1 :             if (fpLog == nullptr)
    1049           0 :                 fpLog = stderr;
    1050             :         }
    1051             :     }
    1052             : 
    1053        3904 :     if (eErrClass == CE_Debug)
    1054             :     {
    1055             : #ifndef _WIN32
    1056         630 :         CPLErrorContext *psCtx = CPLGetErrorContext();
    1057         630 :         if (psCtx != nullptr && !IS_PREFEFINED_ERROR_CTX(psCtx) &&
    1058        1260 :             fpLog == stderr && CPLIsInteractive(stderr))
    1059             :         {
    1060           0 :             if (psCtx->bProgressMode)
    1061             :             {
    1062             :                 // Erase the content of the current line
    1063           0 :                 fprintf(stderr, "\r");
    1064           0 :                 fprintf(stderr, "%s", pszErrorMsg);
    1065           0 :                 fflush(stderr);
    1066           0 :                 psCtx->bEmitNewlineBeforeNextDbgMsg = true;
    1067             :             }
    1068             :             else
    1069             :             {
    1070           0 :                 if (psCtx->bEmitNewlineBeforeNextDbgMsg)
    1071             :                 {
    1072           0 :                     psCtx->bEmitNewlineBeforeNextDbgMsg = false;
    1073           0 :                     fprintf(fpLog, "\n");
    1074             :                 }
    1075           0 :                 fprintf(fpLog, "%s\n", pszErrorMsg);
    1076             :             }
    1077             :         }
    1078             :         else
    1079             : #endif
    1080             :         {
    1081         630 :             fprintf(fpLog, "%s\n", pszErrorMsg);
    1082             :         }
    1083             :     }
    1084        3274 :     else if (eErrClass == CE_Warning)
    1085        2516 :         fprintf(fpLog, "Warning %d: %s\n", nError, pszErrorMsg);
    1086             :     else
    1087         758 :         fprintf(fpLog, "ERROR %d%s %s\n", nError, pszErrorSeparator,
    1088             :                 pszErrorMsg);
    1089             : 
    1090        3904 :     if (eErrClass != CE_Debug && nMaxErrors > 0 && nCount == nMaxErrors)
    1091             :     {
    1092           2 :         fprintf(fpLog,
    1093             :                 "More than %d errors or warnings have been reported. "
    1094             :                 "No more will be reported from now.\n",
    1095             :                 nMaxErrors);
    1096             :     }
    1097             : 
    1098        3904 :     fflush(fpLog);
    1099             : }
    1100             : 
    1101             : /************************************************************************/
    1102             : /*                        CPLQuietErrorHandler()                        */
    1103             : /************************************************************************/
    1104             : 
    1105             : /** Error handler that does not do anything, except for debug messages. */
    1106       34237 : void CPL_STDCALL CPLQuietErrorHandler(CPLErr eErrClass, CPLErrorNum nError,
    1107             :                                       const char *pszErrorMsg)
    1108             : 
    1109             : {
    1110       34237 :     if (eErrClass == CE_Debug)
    1111           3 :         CPLDefaultErrorHandler(eErrClass, nError, pszErrorMsg);
    1112       34237 : }
    1113             : 
    1114             : /************************************************************************/
    1115             : /*                    CPLQuietWarningsErrorHandler()                    */
    1116             : /************************************************************************/
    1117             : 
    1118             : /** Error handler that ignores CE_Warning messages. */
    1119           7 : void CPL_STDCALL CPLQuietWarningsErrorHandler(CPLErr eErrClass,
    1120             :                                               CPLErrorNum nError,
    1121             :                                               const char *pszErrorMsg)
    1122             : 
    1123             : {
    1124           7 :     if (eErrClass != CE_Warning)
    1125           2 :         CPLDefaultErrorHandler(eErrClass, nError, pszErrorMsg);
    1126           7 : }
    1127             : 
    1128             : /************************************************************************/
    1129             : /*                       CPLLoggingErrorHandler()                       */
    1130             : /************************************************************************/
    1131             : 
    1132             : /** Error handler that logs into the file defined by the CPL_LOG configuration
    1133             :  * option, or stderr otherwise.
    1134             :  */
    1135           0 : void CPL_STDCALL CPLLoggingErrorHandler(CPLErr eErrClass, CPLErrorNum nError,
    1136             :                                         const char *pszErrorMsg)
    1137             : 
    1138             : {
    1139           0 :     if (!bLogInit)
    1140             :     {
    1141           0 :         bLogInit = true;
    1142             : 
    1143           0 :         CPLSetConfigOption("CPL_TIMESTAMP", "ON");
    1144             : 
    1145           0 :         const char *cpl_log = CPLGetConfigOption("CPL_LOG", nullptr);
    1146             : 
    1147           0 :         fpLog = stderr;
    1148           0 :         if (cpl_log != nullptr && EQUAL(cpl_log, "OFF"))
    1149             :         {
    1150           0 :             fpLog = nullptr;
    1151             :         }
    1152           0 :         else if (cpl_log != nullptr)
    1153             :         {
    1154           0 :             size_t nPathLen = strlen(cpl_log) + 20;
    1155           0 :             char *pszPath = static_cast<char *>(CPLMalloc(nPathLen));
    1156           0 :             strcpy(pszPath, cpl_log);
    1157             : 
    1158           0 :             int i = 0;
    1159           0 :             while ((fpLog = CPLfopenUTF8(pszPath, "rt")) != nullptr)
    1160             :             {
    1161           0 :                 fclose(fpLog);
    1162             : 
    1163             :                 // Generate sequenced log file names, inserting # before ext.
    1164           0 :                 if (strrchr(cpl_log, '.') == nullptr)
    1165             :                 {
    1166           0 :                     snprintf(pszPath, nPathLen, "%s_%d%s", cpl_log, i++,
    1167             :                              ".log");
    1168             :                 }
    1169             :                 else
    1170             :                 {
    1171           0 :                     size_t pos = 0;
    1172           0 :                     char *cpl_log_base = CPLStrdup(cpl_log);
    1173           0 :                     pos = strcspn(cpl_log_base, ".");
    1174           0 :                     if (pos > 0)
    1175             :                     {
    1176           0 :                         cpl_log_base[pos] = '\0';
    1177             :                     }
    1178           0 :                     snprintf(pszPath, nPathLen, "%s_%d%s", cpl_log_base, i++,
    1179             :                              ".log");
    1180           0 :                     CPLFree(cpl_log_base);
    1181             :                 }
    1182             :             }
    1183             : 
    1184           0 :             fpLog = CPLfopenUTF8(pszPath, "wt");
    1185           0 :             CPLFree(pszPath);
    1186             :         }
    1187             :     }
    1188             : 
    1189           0 :     if (fpLog == nullptr)
    1190           0 :         return;
    1191             : 
    1192           0 :     if (eErrClass == CE_Debug)
    1193           0 :         fprintf(fpLog, "%s\n", pszErrorMsg);
    1194           0 :     else if (eErrClass == CE_Warning)
    1195           0 :         fprintf(fpLog, "Warning %d: %s\n", nError, pszErrorMsg);
    1196             :     else
    1197           0 :         fprintf(fpLog, "ERROR %d: %s\n", nError, pszErrorMsg);
    1198             : 
    1199           0 :     fflush(fpLog);
    1200             : }
    1201             : 
    1202             : /**********************************************************************
    1203             :  *                      CPLTurnFailureIntoWarning()                   *
    1204             :  **********************************************************************/
    1205             : 
    1206             : /** Whether failures should be turned into warnings.
    1207             :  */
    1208       11150 : void CPLTurnFailureIntoWarning(int bOn)
    1209             : {
    1210       11150 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1211       11150 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1212             :     {
    1213           0 :         fprintf(stderr, "CPLTurnFailureIntoWarning() failed.\n");
    1214           0 :         return;
    1215             :     }
    1216       11150 :     psCtx->nFailureIntoWarning += (bOn) ? 1 : -1;
    1217       11150 :     if (psCtx->nFailureIntoWarning < 0)
    1218             :     {
    1219           0 :         CPLDebug("CPL", "Wrong nesting of CPLTurnFailureIntoWarning(TRUE) / "
    1220             :                         "CPLTurnFailureIntoWarning(FALSE)");
    1221             :     }
    1222             : }
    1223             : 
    1224             : /**********************************************************************
    1225             :  *                          CPLSetErrorHandlerEx()                    *
    1226             :  **********************************************************************/
    1227             : 
    1228             : /**
    1229             :  * Install custom error handle with user's data. This method is
    1230             :  * essentially CPLSetErrorHandler with an added pointer to pUserData.
    1231             :  * The pUserData is not returned in the CPLErrorHandler, however, and
    1232             :  * must be fetched via CPLGetErrorHandlerUserData.
    1233             :  *
    1234             :  * @param pfnErrorHandlerNew new error handler function.
    1235             :  * @param pUserData User data to carry along with the error context.
    1236             :  * @return returns the previously installed error handler.
    1237             :  */
    1238             : 
    1239             : CPLErrorHandler CPL_STDCALL
    1240          19 : CPLSetErrorHandlerEx(CPLErrorHandler pfnErrorHandlerNew, void *pUserData)
    1241             : {
    1242          19 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1243          19 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1244             :     {
    1245           0 :         fprintf(stderr, "CPLSetErrorHandlerEx() failed.\n");
    1246           0 :         return nullptr;
    1247             :     }
    1248             : 
    1249          19 :     if (psCtx->psHandlerStack != nullptr)
    1250             :     {
    1251           0 :         CPLDebug("CPL", "CPLSetErrorHandler() called with an error handler on "
    1252             :                         "the local stack.  New error handler will not be used "
    1253             :                         "immediately.");
    1254             :     }
    1255             : 
    1256          19 :     CPLErrorHandler pfnOldHandler = nullptr;
    1257             :     {
    1258          19 :         CPLMutexHolderD(&hErrorMutex);
    1259             : 
    1260          19 :         pfnOldHandler = pfnErrorHandler;
    1261             : 
    1262          19 :         pfnErrorHandler = pfnErrorHandlerNew;
    1263             : 
    1264          19 :         pErrorHandlerUserData = pUserData;
    1265             :     }
    1266             : 
    1267          19 :     return pfnOldHandler;
    1268             : }
    1269             : 
    1270             : /**********************************************************************
    1271             :  *                          CPLSetErrorHandler()                      *
    1272             :  **********************************************************************/
    1273             : 
    1274             : /**
    1275             :  * Install custom error handler.
    1276             :  *
    1277             :  * Allow the library's user to specify an error handler function.
    1278             :  * A valid error handler is a C function with the following prototype:
    1279             :  *
    1280             :  * \code{.cpp}
    1281             :  *     void MyErrorHandler(CPLErr eErrClass, int err_no, const char *msg)
    1282             :  * \endcode
    1283             :  *
    1284             :  * Pass NULL to come back to the default behavior.  The default behavior
    1285             :  * (CPLDefaultErrorHandler()) is to write the message to stderr.
    1286             :  *
    1287             :  * The msg will be a partially formatted error message not containing the
    1288             :  * "ERROR %d:" portion emitted by the default handler.  Message formatting
    1289             :  * is handled by CPLError() before calling the handler.  If the error
    1290             :  * handler function is passed a CE_Fatal class error and returns, then
    1291             :  * CPLError() will call abort(). Applications wanting to interrupt this
    1292             :  * fatal behavior will have to use longjmp(), or a C++ exception to
    1293             :  * indirectly exit the function.
    1294             :  *
    1295             :  * Another standard error handler is CPLQuietErrorHandler() which doesn't
    1296             :  * make any attempt to report the passed error or warning messages but
    1297             :  * will process debug messages via CPLDefaultErrorHandler.
    1298             :  *
    1299             :  * Note that error handlers set with CPLSetErrorHandler() apply to all
    1300             :  * threads in an application, while error handlers set with CPLPushErrorHandler
    1301             :  * are thread-local.  However, any error handlers pushed with
    1302             :  * CPLPushErrorHandler (and not removed with CPLPopErrorHandler) take
    1303             :  * precedence over the global error handlers set with CPLSetErrorHandler().
    1304             :  * Generally speaking CPLSetErrorHandler() would be used to set a desired
    1305             :  * global error handler, while CPLPushErrorHandler() would be used to install
    1306             :  * a temporary local error handler, such as CPLQuietErrorHandler() to suppress
    1307             :  * error reporting in a limited segment of code.
    1308             :  *
    1309             :  * @param pfnErrorHandlerNew new error handler function.
    1310             :  * @return returns the previously installed error handler.
    1311             :  */
    1312             : CPLErrorHandler CPL_STDCALL
    1313          13 : CPLSetErrorHandler(CPLErrorHandler pfnErrorHandlerNew)
    1314             : {
    1315          13 :     return CPLSetErrorHandlerEx(pfnErrorHandlerNew, nullptr);
    1316             : }
    1317             : 
    1318             : /************************************************************************/
    1319             : /*                        CPLPushErrorHandler()                         */
    1320             : /************************************************************************/
    1321             : 
    1322             : /**
    1323             :  * Push a new CPLError handler.
    1324             :  *
    1325             :  * This pushes a new error handler on the thread-local error handler
    1326             :  * stack.  This handler will be used until removed with CPLPopErrorHandler().
    1327             :  *
    1328             :  * The CPLSetErrorHandler() docs have further information on how
    1329             :  * CPLError handlers work.
    1330             :  *
    1331             :  * @param pfnErrorHandlerNew new error handler function.
    1332             :  */
    1333             : 
    1334      160475 : void CPL_STDCALL CPLPushErrorHandler(CPLErrorHandler pfnErrorHandlerNew)
    1335             : 
    1336             : {
    1337      160475 :     CPLPushErrorHandlerEx(pfnErrorHandlerNew, nullptr);
    1338      160471 : }
    1339             : 
    1340             : /************************************************************************/
    1341             : /*                        CPLPushErrorHandlerEx()                       */
    1342             : /************************************************************************/
    1343             : 
    1344             : /**
    1345             :  * Push a new CPLError handler with user data on the error context.
    1346             :  *
    1347             :  * This pushes a new error handler on the thread-local error handler
    1348             :  * stack.  This handler will be used until removed with CPLPopErrorHandler().
    1349             :  * Obtain the user data back by using CPLGetErrorContext().
    1350             :  *
    1351             :  * The CPLSetErrorHandler() docs have further information on how
    1352             :  * CPLError handlers work.
    1353             :  *
    1354             :  * @param pfnErrorHandlerNew new error handler function.
    1355             :  * @param pUserData User data to put on the error context.
    1356             :  */
    1357     6168150 : void CPL_STDCALL CPLPushErrorHandlerEx(CPLErrorHandler pfnErrorHandlerNew,
    1358             :                                        void *pUserData)
    1359             : 
    1360             : {
    1361     6168150 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1362             : 
    1363     6168120 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1364             :     {
    1365          39 :         fprintf(stderr, "CPLPushErrorHandlerEx() failed.\n");
    1366           0 :         return;
    1367             :     }
    1368             : 
    1369             :     CPLErrorHandlerNode *psNode = static_cast<CPLErrorHandlerNode *>(
    1370     6168080 :         CPLMalloc(sizeof(CPLErrorHandlerNode)));
    1371     6168140 :     psNode->psNext = psCtx->psHandlerStack;
    1372     6168140 :     psNode->pfnHandler = pfnErrorHandlerNew;
    1373     6168140 :     psNode->pUserData = pUserData;
    1374     6168140 :     psNode->bCatchDebug = true;
    1375     6168140 :     psCtx->psHandlerStack = psNode;
    1376             : }
    1377             : 
    1378             : /************************************************************************/
    1379             : /*                         CPLPopErrorHandler()                         */
    1380             : /************************************************************************/
    1381             : 
    1382             : /**
    1383             :  * Pop error handler off stack.
    1384             :  *
    1385             :  * Discards the current error handler on the error handler stack, and restores
    1386             :  * the one in use before the last CPLPushErrorHandler() call.  This method
    1387             :  * has no effect if there are no error handlers on the current threads error
    1388             :  * handler stack.
    1389             :  */
    1390             : 
    1391     6168120 : void CPL_STDCALL CPLPopErrorHandler()
    1392             : 
    1393             : {
    1394     6168120 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1395             : 
    1396     6168060 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1397             :     {
    1398          36 :         fprintf(stderr, "CPLPopErrorHandler() failed.\n");
    1399           0 :         return;
    1400             :     }
    1401             : 
    1402     6168020 :     if (psCtx->psHandlerStack != nullptr)
    1403             :     {
    1404     6168100 :         CPLErrorHandlerNode *psNode = psCtx->psHandlerStack;
    1405             : 
    1406     6168100 :         psCtx->psHandlerStack = psNode->psNext;
    1407     6168100 :         VSIFree(psNode);
    1408             :     }
    1409             : }
    1410             : 
    1411             : /************************************************************************/
    1412             : /*                         CPLCallPreviousHandler()                     */
    1413             : /************************************************************************/
    1414             : 
    1415             : /**
    1416             :  * Call the previously installed error handler in the error handler stack.
    1417             :  *
    1418             :  * Only to be used by a custom error handler that wants to forward events to
    1419             :  * the previous error handler.
    1420             :  *
    1421             :  * @since GDAL 3.8
    1422             :  */
    1423             : 
    1424       19734 : void CPLCallPreviousHandler(CPLErr eErrClass, CPLErrorNum err_no,
    1425             :                             const char *pszMsg)
    1426             : {
    1427       19734 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1428             : 
    1429       19734 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1430             :     {
    1431           0 :         fprintf(stderr, "CPLCallPreviousHandler() failed.\n");
    1432           0 :         return;
    1433             :     }
    1434             : 
    1435       19734 :     if (psCtx->psHandlerStack != nullptr)
    1436             :     {
    1437       19734 :         CPLErrorHandlerNode *psCurNode = psCtx->psHandlerStack;
    1438       19734 :         psCtx->psHandlerStack = psCurNode->psNext;
    1439       19734 :         if (psCtx->psHandlerStack)
    1440             :         {
    1441         715 :             CPLErrorHandlerNode *psNewCurNode = psCtx->psHandlerStack;
    1442         715 :             psCtx->psHandlerStack->pfnHandler(eErrClass, err_no, pszMsg);
    1443         715 :             if (psNewCurNode != psCtx->psHandlerStack)
    1444             :             {
    1445           0 :                 fprintf(stderr, "CPLCallPreviousHandler() has detected that a "
    1446             :                                 "previous error handler messed up with the "
    1447             :                                 "error stack. Chaos guaranteed!\n");
    1448             :             }
    1449             :         }
    1450             :         else
    1451       19019 :             CPLDefaultErrorHandler(eErrClass, err_no, pszMsg);
    1452       19734 :         psCtx->psHandlerStack = psCurNode;
    1453             :     }
    1454             :     else
    1455             :     {
    1456           0 :         CPLDefaultErrorHandler(eErrClass, err_no, pszMsg);
    1457             :     }
    1458             : }
    1459             : 
    1460             : /************************************************************************/
    1461             : /*                 CPLSetCurrentErrorHandlerCatchDebug()                */
    1462             : /************************************************************************/
    1463             : 
    1464             : /**
    1465             :  * Set if the current error handler should intercept debug messages, or if
    1466             :  * they should be processed by the previous handler.
    1467             :  *
    1468             :  * By default when installing a custom error handler, this one intercepts
    1469             :  * debug messages. In some cases, this might not be desirable and the user
    1470             :  * would prefer that the previous installed handler (or the default one if no
    1471             :  * previous installed handler exists in the stack) deal with it. In which
    1472             :  * case, this function should be called with bCatchDebug = FALSE.
    1473             :  *
    1474             :  * @param bCatchDebug FALSE if the current error handler should not intercept
    1475             :  * debug messages
    1476             :  * @since GDAL 2.1
    1477             :  */
    1478             : 
    1479       22421 : void CPL_STDCALL CPLSetCurrentErrorHandlerCatchDebug(int bCatchDebug)
    1480             : {
    1481       22421 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1482             : 
    1483       22406 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1484             :     {
    1485          70 :         fprintf(stderr, "CPLSetCurrentErrorHandlerCatchDebug() failed.\n");
    1486           0 :         return;
    1487             :     }
    1488             : 
    1489       22336 :     if (psCtx->psHandlerStack != nullptr)
    1490       22362 :         psCtx->psHandlerStack->bCatchDebug = CPL_TO_BOOL(bCatchDebug);
    1491             :     else
    1492           0 :         gbCatchDebug = CPL_TO_BOOL(bCatchDebug);
    1493             : }
    1494             : 
    1495             : /************************************************************************/
    1496             : /*                             _CPLAssert()                             */
    1497             : /*                                                                      */
    1498             : /*      This function is called only when an assertion fails.           */
    1499             : /************************************************************************/
    1500             : 
    1501             : /**
    1502             :  * Report failure of a logical assertion.
    1503             :  *
    1504             :  * Applications would normally use the CPLAssert() macro which expands
    1505             :  * into code calling _CPLAssert() only if the condition fails.  _CPLAssert()
    1506             :  * will generate a CE_Fatal error call to CPLError(), indicating the file
    1507             :  * name, and line number of the failed assertion, as well as containing
    1508             :  * the assertion itself.
    1509             :  *
    1510             :  * There is no reason for application code to call _CPLAssert() directly.
    1511             :  */
    1512             : 
    1513           0 : void CPL_STDCALL _CPLAssert(const char *pszExpression, const char *pszFile,
    1514             :                             int iLine)
    1515             : 
    1516             : {
    1517           0 :     CPLError(CE_Fatal, CPLE_AssertionFailed,
    1518             :              "Assertion `%s' failed "
    1519             :              "in file `%s', line %d",
    1520             :              pszExpression, pszFile, iLine);
    1521             : 
    1522             :     // Just to please compiler so it is aware the function does not return.
    1523           0 :     abort();
    1524             : }
    1525             : 
    1526             : /************************************************************************/
    1527             : /*                       CPLCleanupErrorMutex()                         */
    1528             : /************************************************************************/
    1529             : 
    1530        1343 : void CPLCleanupErrorMutex()
    1531             : {
    1532        1343 :     if (hErrorMutex != nullptr)
    1533             :     {
    1534         175 :         CPLDestroyMutex(hErrorMutex);
    1535         175 :         hErrorMutex = nullptr;
    1536             :     }
    1537        1343 :     if (fpLog != nullptr && fpLog != stderr)
    1538             :     {
    1539           1 :         fclose(fpLog);
    1540           1 :         fpLog = nullptr;
    1541           1 :         bLogInit = false;
    1542             :     }
    1543        1343 : }
    1544             : 
    1545        4971 : bool CPLIsDefaultErrorHandlerAndCatchDebug()
    1546             : {
    1547        4971 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1548        4971 :     return (psCtx == nullptr || psCtx->psHandlerStack == nullptr) &&
    1549        9942 :            gbCatchDebug && pfnErrorHandler == CPLDefaultErrorHandler;
    1550             : }
    1551             : 
    1552             : /************************************************************************/
    1553             : /*               CPLErrorStateBackuper::CPLErrorStateBackuper()         */
    1554             : /************************************************************************/
    1555             : 
    1556      100309 : CPLErrorStateBackuper::CPLErrorStateBackuper(CPLErrorHandler hHandler)
    1557      100309 :     : m_nLastErrorNum(CPLGetLastErrorNo()),
    1558      200597 :       m_nLastErrorType(CPLGetLastErrorType()),
    1559             :       m_osLastErrorMsg(CPLGetLastErrorMsg()),
    1560      200606 :       m_nLastErrorCounter(CPLGetErrorCounter()),
    1561             :       m_poErrorHandlerPusher(
    1562             :           hHandler ? std::make_unique<CPLErrorHandlerPusher>(hHandler)
    1563      100298 :                    : nullptr)
    1564             : {
    1565      100298 : }
    1566             : 
    1567             : /************************************************************************/
    1568             : /*               CPLErrorStateBackuper::~CPLErrorStateBackuper()        */
    1569             : /************************************************************************/
    1570             : 
    1571      100296 : CPLErrorStateBackuper::~CPLErrorStateBackuper()
    1572             : {
    1573      100305 :     CPLErrorSetState(m_nLastErrorType, m_nLastErrorNum,
    1574             :                      m_osLastErrorMsg.c_str(), &m_nLastErrorCounter);
    1575      100296 : }
    1576             : 
    1577             : /*! @cond Doxygen_Suppress */
    1578             : 
    1579             : /************************************************************************/
    1580             : /*                CPLErrorAccumulator::Context::~Context()              */
    1581             : /************************************************************************/
    1582             : 
    1583       81659 : CPLErrorAccumulator::Context::~Context()
    1584             : {
    1585       81659 :     CPLPopErrorHandler();
    1586       81623 : }
    1587             : 
    1588             : /************************************************************************/
    1589             : /*             CPLErrorAccumulator::InstallForCurrentScope()            */
    1590             : /************************************************************************/
    1591             : 
    1592       81698 : CPLErrorAccumulator::Context CPLErrorAccumulator::InstallForCurrentScope()
    1593             : {
    1594       81698 :     CPLPushErrorHandlerEx(CPLErrorAccumulator::Accumulator, this);
    1595       81676 :     return CPLErrorAccumulator::Context();
    1596             : }
    1597             : 
    1598             : /************************************************************************/
    1599             : /*                    CPLErrorAccumulator::ReplayErrors()               */
    1600             : /************************************************************************/
    1601             : 
    1602         322 : void CPLErrorAccumulator::ReplayErrors()
    1603             : {
    1604         644 :     std::lock_guard oLock(mutex);
    1605         327 :     for (const auto &err : errors)
    1606             :     {
    1607           5 :         CPLError(err.type, err.no, "%s", err.msg.c_str());
    1608             :     }
    1609         322 : }
    1610             : 
    1611             : /************************************************************************/
    1612             : /*                 CPLErrorAccumulator::Accumulator()                   */
    1613             : /************************************************************************/
    1614             : 
    1615         352 : /* static */ void CPL_STDCALL CPLErrorAccumulator::Accumulator(CPLErr eErr,
    1616             :                                                                CPLErrorNum no,
    1617             :                                                                const char *msg)
    1618             : {
    1619         352 :     if (eErr != CE_Debug)
    1620             :     {
    1621             :         CPLErrorAccumulator *pThis =
    1622         352 :             static_cast<CPLErrorAccumulator *>(CPLGetErrorHandlerUserData());
    1623         352 :         std::lock_guard oLock(pThis->mutex);
    1624         352 :         pThis->errors.push_back(
    1625         704 :             CPLErrorHandlerAccumulatorStruct(eErr, no, msg));
    1626             :     }
    1627         352 : }
    1628             : 
    1629             : /*! @endcond */

Generated by: LCOV version 1.14