LCOV - code coverage report
Current view: top level - port - cpl_error.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 322 457 70.5 %
Date: 2025-10-22 21:46:17 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      684768 : static char *CPLErrorContextGetString(CPLErrorContext *psCtxt)
      96             : {
      97      684768 :     return psCtxt->szLastErrMsg;
      98             : }
      99             : 
     100             : /************************************************************************/
     101             : /*                         CPLGetErrorContext()                         */
     102             : /************************************************************************/
     103             : 
     104    49146900 : static CPLErrorContext *CPLGetErrorContext()
     105             : 
     106             : {
     107    49146900 :     int bError = FALSE;
     108             :     CPLErrorContext *psCtx = reinterpret_cast<CPLErrorContext *>(
     109    49146900 :         CPLGetTLSEx(CTLS_ERRORCONTEXT, &bError));
     110    49146100 :     if (bError)
     111           0 :         return nullptr;
     112             : 
     113    49146100 :     if (psCtx == nullptr)
     114             :     {
     115             :         psCtx = static_cast<CPLErrorContext *>(
     116        3348 :             VSICalloc(sizeof(CPLErrorContext), 1));
     117        3352 :         if (psCtx == nullptr)
     118             :         {
     119           0 :             fprintf(stderr, "Out of memory attempting to report error.\n");
     120           0 :             return nullptr;
     121             :         }
     122        3352 :         psCtx->eLastErrType = CE_None;
     123        3352 :         psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg);
     124        3352 :         CPLSetTLS(CTLS_ERRORCONTEXT, psCtx, TRUE);
     125             :     }
     126             : 
     127    49145900 :     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     6103030 : void *CPL_STDCALL CPLGetErrorHandlerUserData(void)
     148             : {
     149             :     // get the current threadlocal or global error context user data
     150     6103030 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     151     6103030 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
     152           0 :         abort();
     153     6103030 :     return reinterpret_cast<void *>(psCtx->psHandlerStack
     154     6103030 :                                         ? psCtx->psHandlerStack->pUserData
     155     6103030 :                                         : 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      102241 : static void ApplyErrorHandler(CPLErrorContext *psCtx, CPLErr eErrClass,
     205             :                               CPLErrorNum err_no, const char *pszMessage)
     206             : {
     207      102241 :     bool bProcessed = false;
     208             : 
     209      102241 :     if (psCtx->psHandlerStack != nullptr)
     210             :     {
     211             :         // iterate through the threadlocal handler stack
     212       61448 :         if ((eErrClass != CE_Debug) || psCtx->psHandlerStack->bCatchDebug)
     213             :         {
     214             :             // call the error handler
     215       61371 :             CPLErrorHandlerNode *psNewCurNode = psCtx->psHandlerStack;
     216       61371 :             psCtx->psHandlerStack->pfnHandler(eErrClass, err_no, pszMessage);
     217       61372 :             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       61372 :             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      102242 :     if (!bProcessed)
     256             :     {
     257             :         // hit the global error handler
     258       81588 :         CPLMutexHolderD(&hErrorMutex);
     259       40794 :         if ((eErrClass != CE_Debug) || gbCatchDebug)
     260             :         {
     261       40792 :             if (pfnErrorHandler != nullptr)
     262             :             {
     263       40790 :                 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      102242 : }
     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       99575 : 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       99575 :     va_start(args, fmt);
     317       99575 :     CPLErrorV(eErrClass, err_no, fmt, args);
     318       99574 :     va_end(args);
     319       99574 : }
     320             : 
     321             : /************************************************************************/
     322             : /*                             CPLErrorV()                              */
     323             : /************************************************************************/
     324             : 
     325             : /** Same as CPLError() but with a va_list */
     326      100636 : void CPLErrorV(CPLErr eErrClass, CPLErrorNum err_no, const char *fmt,
     327             :                va_list args)
     328             : {
     329      100636 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     330      100633 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
     331             :     {
     332           0 :         int bMemoryError = FALSE;
     333           0 :         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           0 :         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           0 :         char szShortMessage[80] = {};
     353           0 :         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      100638 :     if (psCtx->nFailureIntoWarning > 0 && eErrClass == CE_Failure)
     362           7 :         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      100638 :         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      100638 :         int nPreviousSize = 0;
     384      161574 :         if (psCtx->psHandlerStack != nullptr &&
     385       60930 :             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      100644 :         int nPR = 0;
     407          20 :         while (((nPR = CPLvsnprintf(psCtx->szLastErrMsg + nPreviousSize,
     408      100664 :                                     psCtx->nLastErrMsgMax - nPreviousSize, fmt,
     409      100628 :                                     wrk_args)) == -1 ||
     410      100676 :                 nPR >= psCtx->nLastErrMsgMax - nPreviousSize - 1) &&
     411          34 :                psCtx->nLastErrMsgMax < 1000000)
     412             :         {
     413             : #ifdef va_copy
     414          20 :             va_end(wrk_args);
     415          20 :             va_copy(wrk_args, args);
     416             : #else
     417             :             wrk_args = args;
     418             : #endif
     419          20 :             psCtx->nLastErrMsgMax *= 3;
     420          40 :             psCtx = static_cast<CPLErrorContext *>(CPLRealloc(
     421             :                 psCtx, sizeof(CPLErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE +
     422          20 :                            psCtx->nLastErrMsgMax + 1));
     423          20 :             CPLSetTLS(CTLS_ERRORCONTEXT, psCtx, TRUE);
     424             :         }
     425             : 
     426      100622 :         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      100622 :     char *pszPassword = strstr(psCtx->szLastErrMsg, "password=");
     437      100622 :     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      100622 :     psCtx->nLastErrNo = err_no;
     452      100622 :     psCtx->eLastErrType = eErrClass;
     453      100622 :     if (psCtx->nErrorCounter == ~(0U))
     454           0 :         psCtx->nErrorCounter = 0;
     455             :     else
     456      100622 :         psCtx->nErrorCounter++;
     457             : 
     458      100622 :     if (CPLGetConfigOption("CPL_LOG_ERRORS", nullptr) != nullptr)
     459           0 :         CPLDebug("CPLError", "%s", psCtx->szLastErrMsg);
     460             : 
     461             :     /* -------------------------------------------------------------------- */
     462             :     /*      Invoke the current error handler.                               */
     463             :     /* -------------------------------------------------------------------- */
     464      100638 :     ApplyErrorHandler(psCtx, eErrClass, err_no, psCtx->szLastErrMsg);
     465             : 
     466      100638 :     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      695020 : static void CPLvDebug(const char *pszCategory,
     583             :                       CPL_FORMAT_STRING(const char *pszFormat), va_list args)
     584             : {
     585      695020 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     586      694845 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
     587           5 :         return;
     588      694840 :     const char *pszDebug = CPLGetConfigOption("CPL_DEBUG", nullptr);
     589             : 
     590             :     /* -------------------------------------------------------------------- */
     591             :     /*      Does this message pass our current criteria?                    */
     592             :     /* -------------------------------------------------------------------- */
     593      695214 :     if (pszDebug == nullptr || EQUAL(pszDebug, "NO") ||
     594        1689 :         EQUAL(pszDebug, "OFF") || EQUAL(pszDebug, "FALSE") ||
     595        1669 :         EQUAL(pszDebug, "0"))
     596             :     {
     597      693547 :         return;
     598             :     }
     599             : 
     600        1667 :     if (!EQUAL(pszDebug, "ON") && !EQUAL(pszDebug, "YES") &&
     601         140 :         !EQUAL(pszDebug, "TRUE") && !EQUAL(pszDebug, "1") &&
     602         128 :         !EQUAL(pszDebug, ""))
     603             :     {
     604             :         // check if value of CPL_DEBUG contains the category
     605         127 :         const size_t nLen = strlen(pszCategory);
     606             : 
     607         127 :         size_t i = 0;
     608         891 :         for (i = 0; pszDebug[i] != '\0'; i++)
     609             :         {
     610         828 :             if (EQUALN(pszCategory, pszDebug + i, nLen))
     611          64 :                 break;
     612             :         }
     613             : 
     614         127 :         if (pszDebug[i] == '\0')
     615          63 :             return;
     616             :     }
     617             : 
     618             :     /* -------------------------------------------------------------------- */
     619             :     /*    Allocate a block for the error.                                   */
     620             :     /* -------------------------------------------------------------------- */
     621        1604 :     const int ERROR_MAX = 25000;
     622        1604 :     char *pszMessage = static_cast<char *>(VSIMalloc(ERROR_MAX));
     623        1604 :     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        1604 :     pszMessage[0] = '\0';
     632             : #ifdef TIMESTAMP_DEBUG
     633        1604 :     if (CPLTestBool(CPLGetConfigOption("CPL_TIMESTAMP", "NO")))
     634             :     {
     635             :         static struct CPLTimeVal tvStart;
     636           6 :         static const auto unused = CPLGettimeofday(&tvStart, nullptr);
     637           6 :         CPL_IGNORE_RET_VAL(unused);
     638             :         struct CPLTimeVal tv;
     639           6 :         CPLGettimeofday(&tv, nullptr);
     640           6 :         strcpy(pszMessage, "[");
     641           6 :         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           6 :         if (pszMessage[strlen(pszMessage) - 1] == '\n')
     648             :         {
     649           6 :             pszMessage[strlen(pszMessage) - 1] = 0;  // blow it out
     650             :         }
     651           6 :         CPLsnprintf(pszMessage + strlen(pszMessage),
     652           6 :                     ERROR_MAX - strlen(pszMessage),
     653           6 :                     "].%04d, %03.04f: ", static_cast<int>(tv.tv_usec / 100),
     654           6 :                     tv.tv_sec + tv.tv_usec * 1e-6 -
     655           6 :                         (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        1604 :     strcat(pszMessage, pszCategory);
     672        1604 :     strcat(pszMessage, ": ");
     673             : 
     674             :     /* -------------------------------------------------------------------- */
     675             :     /*      Format the application provided portion of the debug message.   */
     676             :     /* -------------------------------------------------------------------- */
     677        1604 :     CPLvsnprintf(pszMessage + strlen(pszMessage),
     678        1604 :                  ERROR_MAX - strlen(pszMessage), pszFormat, args);
     679             : 
     680             :     /* -------------------------------------------------------------------- */
     681             :     /*      Obfuscate any password in error message                         */
     682             :     /* -------------------------------------------------------------------- */
     683             : 
     684        1604 :     char *pszPassword = strstr(pszMessage, "password=");
     685        1604 :     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        1604 :     ApplyErrorHandler(psCtx, CE_Debug, CPLE_None, pszMessage);
     699             : 
     700        1604 :     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      694984 : void CPLDebug(const char *pszCategory, CPL_FORMAT_STRING(const char *pszFormat),
     735             :               ...)
     736             : 
     737             : {
     738             :     va_list args;
     739      694984 :     va_start(args, pszFormat);
     740      694984 :     CPLvDebug(pszCategory, pszFormat, args);
     741      695212 :     va_end(args);
     742      695212 : }
     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     6957180 : void CPL_STDCALL CPLErrorReset()
     810             : {
     811     6957180 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     812     6957180 :     if (psCtx == nullptr)
     813           0 :         return;
     814     6957180 :     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     6957180 :     psCtx->nLastErrNo = CPLE_None;
     826     6957180 :     psCtx->szLastErrMsg[0] = '\0';
     827     6957180 :     psCtx->eLastErrType = CE_None;
     828     6957180 :     psCtx->nErrorCounter = 0;
     829             : }
     830             : 
     831             : /**********************************************************************
     832             :  *                       CPLErrorSetState()
     833             :  **********************************************************************/
     834             : 
     835      684771 : void CPLErrorSetState(CPLErr eErrClass, CPLErrorNum err_no, const char *pszMsg,
     836             :                       const GUInt32 *pnErrorCounter)
     837             : {
     838      684771 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     839      684781 :     if (psCtx == nullptr)
     840           0 :         return;
     841      684781 :     if (IS_PREFEFINED_ERROR_CTX(psCtx))
     842             :     {
     843          11 :         int bMemoryError = FALSE;
     844          11 :         if (eErrClass == CE_None)
     845           0 :             CPLSetTLSWithFreeFuncEx(
     846             :                 CTLS_ERRORCONTEXT,
     847             :                 reinterpret_cast<void *>(
     848             :                     const_cast<CPLErrorContext *>(&sNoErrorContext)),
     849             :                 nullptr, &bMemoryError);
     850          11 :         else if (eErrClass == CE_Warning)
     851           0 :             CPLSetTLSWithFreeFuncEx(
     852             :                 CTLS_ERRORCONTEXT,
     853             :                 reinterpret_cast<void *>(
     854             :                     const_cast<CPLErrorContext *>(&sWarningContext)),
     855             :                 nullptr, &bMemoryError);
     856          11 :         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      684770 :     psCtx->nLastErrNo = err_no;
     866     1369550 :     const size_t size = std::min(static_cast<size_t>(psCtx->nLastErrMsgMax - 1),
     867      684770 :                                  strlen(pszMsg));
     868      684776 :     char *pszLastErrMsg = CPLErrorContextGetString(psCtx);
     869      684768 :     memcpy(pszLastErrMsg, pszMsg, size);
     870      684768 :     pszLastErrMsg[size] = '\0';
     871      684768 :     psCtx->eLastErrType = eErrClass;
     872      684768 :     if (pnErrorCounter)
     873      132368 :         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             :  */
     883             : 
     884      552412 : void CPL_DLL CPLErrorSetState(CPLErr eErrClass, CPLErrorNum err_no,
     885             :                               const char *pszMsg)
     886             : {
     887      552412 :     CPLErrorSetState(eErrClass, err_no, pszMsg, nullptr);
     888      552412 : }
     889             : 
     890             : /**********************************************************************
     891             :  *                          CPLGetLastErrorNo()
     892             :  **********************************************************************/
     893             : 
     894             : /**
     895             :  * Fetch the last error number.
     896             :  *
     897             :  * Fetches the last error number posted with CPLError(), that hasn't
     898             :  * been cleared by CPLErrorReset().  This is the error number, not the error
     899             :  * class.
     900             :  *
     901             :  * @return the error number of the last error to occur, or CPLE_None (0)
     902             :  * if there are no posted errors.
     903             :  */
     904             : 
     905      798945 : CPLErrorNum CPL_STDCALL CPLGetLastErrorNo()
     906             : {
     907      798945 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     908      798654 :     if (psCtx == nullptr)
     909           0 :         return 0;
     910             : 
     911      798654 :     return psCtx->nLastErrNo;
     912             : }
     913             : 
     914             : /**********************************************************************
     915             :  *                          CPLGetLastErrorType()
     916             :  **********************************************************************/
     917             : 
     918             : /**
     919             :  * Fetch the last error type.
     920             :  *
     921             :  * Fetches the last error type posted with CPLError(), that hasn't
     922             :  * been cleared by CPLErrorReset().  This is the error class, not the error
     923             :  * number.
     924             :  *
     925             :  * @return the error type of the last error to occur, or CE_None (0)
     926             :  * if there are no posted errors.
     927             :  */
     928             : 
     929     9526520 : CPLErr CPL_STDCALL CPLGetLastErrorType()
     930             : {
     931     9526520 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     932     9526520 :     if (psCtx == nullptr)
     933           0 :         return CE_None;
     934             : 
     935     9526520 :     return psCtx->eLastErrType;
     936             : }
     937             : 
     938             : /**********************************************************************
     939             :  *                          CPLGetLastErrorMsg()
     940             :  **********************************************************************/
     941             : 
     942             : /**
     943             :  * Get the last error message.
     944             :  *
     945             :  * Fetches the last error message posted with CPLError(), that hasn't
     946             :  * been cleared by CPLErrorReset().  The returned pointer is to an internal
     947             :  * string that should not be altered or freed.
     948             :  *
     949             :  * @return the last error message, or an empty string ("") if there is no
     950             :  * posted error message.
     951             :  */
     952             : 
     953      406637 : const char *CPL_STDCALL CPLGetLastErrorMsg()
     954             : {
     955      406637 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     956      406635 :     if (psCtx == nullptr)
     957           0 :         return "";
     958             : 
     959      406635 :     return psCtx->szLastErrMsg;
     960             : }
     961             : 
     962             : /**********************************************************************
     963             :  *                          CPLGetErrorCounter()
     964             :  **********************************************************************/
     965             : 
     966             : /**
     967             :  * Get the error counter
     968             :  *
     969             :  * Fetches the number of errors emitted in the current error context,
     970             :  * since the last call to CPLErrorReset()
     971             :  *
     972             :  * @return the error counter.
     973             :  */
     974             : 
     975    11071300 : GUInt32 CPL_STDCALL CPLGetErrorCounter()
     976             : {
     977    11071300 :     CPLErrorContext *psCtx = CPLGetErrorContext();
     978    11070600 :     if (psCtx == nullptr)
     979           0 :         return 0;
     980             : 
     981    11070600 :     return psCtx->nErrorCounter;
     982             : }
     983             : 
     984             : /************************************************************************/
     985             : /*                       CPLDefaultErrorHandler()                       */
     986             : /************************************************************************/
     987             : 
     988             : static FILE *fpLog = stderr;
     989             : static bool bLogInit = false;
     990             : 
     991          13 : static FILE *CPLfopenUTF8(const char *pszFilename, const char *pszAccess)
     992             : {
     993             :     FILE *f;
     994             : #ifdef _WIN32
     995             :     wchar_t *pwszFilename =
     996             :         CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
     997             :     wchar_t *pwszAccess =
     998             :         CPLRecodeToWChar(pszAccess, CPL_ENC_UTF8, CPL_ENC_UCS2);
     999             :     f = _wfopen(pwszFilename, pwszAccess);
    1000             :     VSIFree(pwszFilename);
    1001             :     VSIFree(pwszAccess);
    1002             : #else
    1003          13 :     f = fopen(pszFilename, pszAccess);
    1004             : #endif
    1005          13 :     return f;
    1006             : }
    1007             : 
    1008             : /** Default error handler. */
    1009       60561 : void CPL_STDCALL CPLDefaultErrorHandler(CPLErr eErrClass, CPLErrorNum nError,
    1010             :                                         const char *pszErrorMsg)
    1011             : 
    1012             : {
    1013             :     static int nCount = 0;
    1014             :     static int nMaxErrors = -1;
    1015             :     static const char *pszErrorSeparator = ":";
    1016             : 
    1017       60561 :     if (eErrClass != CE_Debug)
    1018             :     {
    1019       59160 :         if (nMaxErrors == -1)
    1020             :         {
    1021         248 :             nMaxErrors =
    1022         248 :                 atoi(CPLGetConfigOption("CPL_MAX_ERROR_REPORTS", "1000"));
    1023             :             // If running GDAL as a CustomBuild Command os MSBuild, "ERROR bla:"
    1024             :             // is considered as failing the job. This is rarely the intended
    1025             :             // behavior
    1026         248 :             pszErrorSeparator = CPLGetConfigOption("CPL_ERROR_SEPARATOR", ":");
    1027             :         }
    1028             : 
    1029       59160 :         nCount++;
    1030       59160 :         if (nCount > nMaxErrors && nMaxErrors > 0)
    1031       55692 :             return;
    1032             :     }
    1033             : 
    1034        4869 :     if (!bLogInit)
    1035             :     {
    1036         272 :         bLogInit = true;
    1037             : 
    1038         272 :         fpLog = stderr;
    1039         272 :         const char *pszLog = CPLGetConfigOption("CPL_LOG", nullptr);
    1040         272 :         if (pszLog != nullptr)
    1041             :         {
    1042             :             const bool bAppend =
    1043          13 :                 CPLGetConfigOption("CPL_LOG_APPEND", nullptr) != nullptr;
    1044          13 :             const char *pszAccess = bAppend ? "at" : "wt";
    1045          13 :             fpLog = CPLfopenUTF8(pszLog, pszAccess);
    1046          13 :             if (fpLog == nullptr)
    1047           0 :                 fpLog = stderr;
    1048             :         }
    1049             :     }
    1050             : 
    1051        4869 :     if (eErrClass == CE_Debug)
    1052             :     {
    1053             : #ifndef _WIN32
    1054        1401 :         CPLErrorContext *psCtx = CPLGetErrorContext();
    1055        1401 :         if (psCtx != nullptr && !IS_PREFEFINED_ERROR_CTX(psCtx) &&
    1056        2802 :             fpLog == stderr && CPLIsInteractive(stderr))
    1057             :         {
    1058           0 :             if (psCtx->bProgressMode)
    1059             :             {
    1060             :                 // Erase the content of the current line
    1061           0 :                 fprintf(stderr, "\r");
    1062           0 :                 fprintf(stderr, "%s", pszErrorMsg);
    1063           0 :                 fflush(stderr);
    1064           0 :                 psCtx->bEmitNewlineBeforeNextDbgMsg = true;
    1065             :             }
    1066             :             else
    1067             :             {
    1068           0 :                 if (psCtx->bEmitNewlineBeforeNextDbgMsg)
    1069             :                 {
    1070           0 :                     psCtx->bEmitNewlineBeforeNextDbgMsg = false;
    1071           0 :                     fprintf(fpLog, "\n");
    1072             :                 }
    1073           0 :                 fprintf(fpLog, "%s\n", pszErrorMsg);
    1074             :             }
    1075             :         }
    1076             :         else
    1077             : #endif
    1078             :         {
    1079        1401 :             fprintf(fpLog, "%s\n", pszErrorMsg);
    1080             :         }
    1081             :     }
    1082        3468 :     else if (eErrClass == CE_Warning)
    1083        2588 :         fprintf(fpLog, "Warning %d: %s\n", nError, pszErrorMsg);
    1084             :     else
    1085         880 :         fprintf(fpLog, "ERROR %d%s %s\n", nError, pszErrorSeparator,
    1086             :                 pszErrorMsg);
    1087             : 
    1088        4869 :     if (eErrClass != CE_Debug && nMaxErrors > 0 && nCount == nMaxErrors)
    1089             :     {
    1090           2 :         fprintf(fpLog,
    1091             :                 "More than %d errors or warnings have been reported. "
    1092             :                 "No more will be reported from now.\n",
    1093             :                 nMaxErrors);
    1094             :     }
    1095             : 
    1096        4869 :     fflush(fpLog);
    1097             : }
    1098             : 
    1099             : /************************************************************************/
    1100             : /*                        CPLQuietErrorHandler()                        */
    1101             : /************************************************************************/
    1102             : 
    1103             : /** Error handler that does not do anything, except for debug messages. */
    1104       36984 : void CPL_STDCALL CPLQuietErrorHandler(CPLErr eErrClass, CPLErrorNum nError,
    1105             :                                       const char *pszErrorMsg)
    1106             : 
    1107             : {
    1108       36984 :     if (eErrClass == CE_Debug)
    1109         308 :         CPLDefaultErrorHandler(eErrClass, nError, pszErrorMsg);
    1110       36984 : }
    1111             : 
    1112             : /************************************************************************/
    1113             : /*                    CPLQuietWarningsErrorHandler()                    */
    1114             : /************************************************************************/
    1115             : 
    1116             : /** Error handler that ignores CE_Warning messages. */
    1117           7 : void CPL_STDCALL CPLQuietWarningsErrorHandler(CPLErr eErrClass,
    1118             :                                               CPLErrorNum nError,
    1119             :                                               const char *pszErrorMsg)
    1120             : 
    1121             : {
    1122           7 :     if (eErrClass != CE_Warning)
    1123           2 :         CPLDefaultErrorHandler(eErrClass, nError, pszErrorMsg);
    1124           7 : }
    1125             : 
    1126             : /************************************************************************/
    1127             : /*                       CPLLoggingErrorHandler()                       */
    1128             : /************************************************************************/
    1129             : 
    1130             : /** Error handler that logs into the file defined by the CPL_LOG configuration
    1131             :  * option, or stderr otherwise.
    1132             :  */
    1133           0 : void CPL_STDCALL CPLLoggingErrorHandler(CPLErr eErrClass, CPLErrorNum nError,
    1134             :                                         const char *pszErrorMsg)
    1135             : 
    1136             : {
    1137           0 :     if (!bLogInit)
    1138             :     {
    1139           0 :         bLogInit = true;
    1140             : 
    1141           0 :         CPLSetConfigOption("CPL_TIMESTAMP", "ON");
    1142             : 
    1143           0 :         const char *cpl_log = CPLGetConfigOption("CPL_LOG", nullptr);
    1144             : 
    1145           0 :         fpLog = stderr;
    1146           0 :         if (cpl_log != nullptr && EQUAL(cpl_log, "OFF"))
    1147             :         {
    1148           0 :             fpLog = nullptr;
    1149             :         }
    1150           0 :         else if (cpl_log != nullptr)
    1151             :         {
    1152           0 :             size_t nPathLen = strlen(cpl_log) + 20;
    1153           0 :             char *pszPath = static_cast<char *>(CPLMalloc(nPathLen));
    1154           0 :             strcpy(pszPath, cpl_log);
    1155             : 
    1156           0 :             int i = 0;
    1157           0 :             while ((fpLog = CPLfopenUTF8(pszPath, "rt")) != nullptr)
    1158             :             {
    1159           0 :                 fclose(fpLog);
    1160             : 
    1161             :                 // Generate sequenced log file names, inserting # before ext.
    1162           0 :                 if (strrchr(cpl_log, '.') == nullptr)
    1163             :                 {
    1164           0 :                     snprintf(pszPath, nPathLen, "%s_%d%s", cpl_log, i++,
    1165             :                              ".log");
    1166             :                 }
    1167             :                 else
    1168             :                 {
    1169           0 :                     size_t pos = 0;
    1170           0 :                     char *cpl_log_base = CPLStrdup(cpl_log);
    1171           0 :                     pos = strcspn(cpl_log_base, ".");
    1172           0 :                     if (pos > 0)
    1173             :                     {
    1174           0 :                         cpl_log_base[pos] = '\0';
    1175             :                     }
    1176           0 :                     snprintf(pszPath, nPathLen, "%s_%d%s", cpl_log_base, i++,
    1177             :                              ".log");
    1178           0 :                     CPLFree(cpl_log_base);
    1179             :                 }
    1180             :             }
    1181             : 
    1182           0 :             fpLog = CPLfopenUTF8(pszPath, "wt");
    1183           0 :             CPLFree(pszPath);
    1184             :         }
    1185             :     }
    1186             : 
    1187           0 :     if (fpLog == nullptr)
    1188           0 :         return;
    1189             : 
    1190           0 :     if (eErrClass == CE_Debug)
    1191           0 :         fprintf(fpLog, "%s\n", pszErrorMsg);
    1192           0 :     else if (eErrClass == CE_Warning)
    1193           0 :         fprintf(fpLog, "Warning %d: %s\n", nError, pszErrorMsg);
    1194             :     else
    1195           0 :         fprintf(fpLog, "ERROR %d: %s\n", nError, pszErrorMsg);
    1196             : 
    1197           0 :     fflush(fpLog);
    1198             : }
    1199             : 
    1200             : /**********************************************************************
    1201             :  *                      CPLTurnFailureIntoWarning()                   *
    1202             :  **********************************************************************/
    1203             : 
    1204             : /** Whether failures should be turned into warnings.
    1205             :  */
    1206       12812 : void CPLTurnFailureIntoWarning(int bOn)
    1207             : {
    1208       12812 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1209       12812 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1210             :     {
    1211           0 :         fprintf(stderr, "CPLTurnFailureIntoWarning() failed.\n");
    1212           0 :         return;
    1213             :     }
    1214       12812 :     psCtx->nFailureIntoWarning += (bOn) ? 1 : -1;
    1215       12812 :     if (psCtx->nFailureIntoWarning < 0)
    1216             :     {
    1217           0 :         CPLDebug("CPL", "Wrong nesting of CPLTurnFailureIntoWarning(TRUE) / "
    1218             :                         "CPLTurnFailureIntoWarning(FALSE)");
    1219             :     }
    1220             : }
    1221             : 
    1222             : /**********************************************************************
    1223             :  *                          CPLSetErrorHandlerEx()                    *
    1224             :  **********************************************************************/
    1225             : 
    1226             : /**
    1227             :  * Install custom error handle with user's data. This method is
    1228             :  * essentially CPLSetErrorHandler with an added pointer to pUserData.
    1229             :  * The pUserData is not returned in the CPLErrorHandler, however, and
    1230             :  * must be fetched via CPLGetErrorHandlerUserData.
    1231             :  *
    1232             :  * @param pfnErrorHandlerNew new error handler function.
    1233             :  * @param pUserData User data to carry along with the error context.
    1234             :  * @return returns the previously installed error handler.
    1235             :  */
    1236             : 
    1237             : CPLErrorHandler CPL_STDCALL
    1238          19 : CPLSetErrorHandlerEx(CPLErrorHandler pfnErrorHandlerNew, void *pUserData)
    1239             : {
    1240          19 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1241          19 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1242             :     {
    1243           0 :         fprintf(stderr, "CPLSetErrorHandlerEx() failed.\n");
    1244           0 :         return nullptr;
    1245             :     }
    1246             : 
    1247          19 :     if (psCtx->psHandlerStack != nullptr)
    1248             :     {
    1249           0 :         CPLDebug("CPL", "CPLSetErrorHandler() called with an error handler on "
    1250             :                         "the local stack.  New error handler will not be used "
    1251             :                         "immediately.");
    1252             :     }
    1253             : 
    1254          19 :     CPLErrorHandler pfnOldHandler = nullptr;
    1255             :     {
    1256          19 :         CPLMutexHolderD(&hErrorMutex);
    1257             : 
    1258          19 :         pfnOldHandler = pfnErrorHandler;
    1259             : 
    1260          19 :         pfnErrorHandler = pfnErrorHandlerNew;
    1261             : 
    1262          19 :         pErrorHandlerUserData = pUserData;
    1263             :     }
    1264             : 
    1265          19 :     return pfnOldHandler;
    1266             : }
    1267             : 
    1268             : /**********************************************************************
    1269             :  *                          CPLSetErrorHandler()                      *
    1270             :  **********************************************************************/
    1271             : 
    1272             : /**
    1273             :  * Install custom error handler.
    1274             :  *
    1275             :  * Allow the library's user to specify an error handler function.
    1276             :  * A valid error handler is a C function with the following prototype:
    1277             :  *
    1278             :  * \code{.cpp}
    1279             :  *     void MyErrorHandler(CPLErr eErrClass, int err_no, const char *msg)
    1280             :  * \endcode
    1281             :  *
    1282             :  * Pass NULL to come back to the default behavior.  The default behavior
    1283             :  * (CPLDefaultErrorHandler()) is to write the message to stderr.
    1284             :  *
    1285             :  * The msg will be a partially formatted error message not containing the
    1286             :  * "ERROR %d:" portion emitted by the default handler.  Message formatting
    1287             :  * is handled by CPLError() before calling the handler.  If the error
    1288             :  * handler function is passed a CE_Fatal class error and returns, then
    1289             :  * CPLError() will call abort(). Applications wanting to interrupt this
    1290             :  * fatal behavior will have to use longjmp(), or a C++ exception to
    1291             :  * indirectly exit the function.
    1292             :  *
    1293             :  * Another standard error handler is CPLQuietErrorHandler() which doesn't
    1294             :  * make any attempt to report the passed error or warning messages but
    1295             :  * will process debug messages via CPLDefaultErrorHandler.
    1296             :  *
    1297             :  * Note that error handlers set with CPLSetErrorHandler() apply to all
    1298             :  * threads in an application, while error handlers set with CPLPushErrorHandler
    1299             :  * are thread-local.  However, any error handlers pushed with
    1300             :  * CPLPushErrorHandler (and not removed with CPLPopErrorHandler) take
    1301             :  * precedence over the global error handlers set with CPLSetErrorHandler().
    1302             :  * Generally speaking CPLSetErrorHandler() would be used to set a desired
    1303             :  * global error handler, while CPLPushErrorHandler() would be used to install
    1304             :  * a temporary local error handler, such as CPLQuietErrorHandler() to suppress
    1305             :  * error reporting in a limited segment of code.
    1306             :  *
    1307             :  * @param pfnErrorHandlerNew new error handler function.
    1308             :  * @return returns the previously installed error handler.
    1309             :  */
    1310             : CPLErrorHandler CPL_STDCALL
    1311          13 : CPLSetErrorHandler(CPLErrorHandler pfnErrorHandlerNew)
    1312             : {
    1313          13 :     return CPLSetErrorHandlerEx(pfnErrorHandlerNew, nullptr);
    1314             : }
    1315             : 
    1316             : /************************************************************************/
    1317             : /*                        CPLPushErrorHandler()                         */
    1318             : /************************************************************************/
    1319             : 
    1320             : /**
    1321             :  * Push a new CPLError handler.
    1322             :  *
    1323             :  * This pushes a new error handler on the thread-local error handler
    1324             :  * stack.  This handler will be used until removed with CPLPopErrorHandler().
    1325             :  *
    1326             :  * The CPLSetErrorHandler() docs have further information on how
    1327             :  * CPLError handlers work.
    1328             :  *
    1329             :  * @param pfnErrorHandlerNew new error handler function.
    1330             :  */
    1331             : 
    1332      197973 : void CPL_STDCALL CPLPushErrorHandler(CPLErrorHandler pfnErrorHandlerNew)
    1333             : 
    1334             : {
    1335      197973 :     CPLPushErrorHandlerEx(pfnErrorHandlerNew, nullptr);
    1336      197973 : }
    1337             : 
    1338             : /************************************************************************/
    1339             : /*                        CPLPushErrorHandlerEx()                       */
    1340             : /************************************************************************/
    1341             : 
    1342             : /**
    1343             :  * Push a new CPLError handler with user data on the error context.
    1344             :  *
    1345             :  * This pushes a new error handler on the thread-local error handler
    1346             :  * stack.  This handler will be used until removed with CPLPopErrorHandler().
    1347             :  * Obtain the user data back by using CPLGetErrorContext().
    1348             :  *
    1349             :  * The CPLSetErrorHandler() docs have further information on how
    1350             :  * CPLError handlers work.
    1351             :  *
    1352             :  * @param pfnErrorHandlerNew new error handler function.
    1353             :  * @param pUserData User data to put on the error context.
    1354             :  */
    1355     6368280 : void CPL_STDCALL CPLPushErrorHandlerEx(CPLErrorHandler pfnErrorHandlerNew,
    1356             :                                        void *pUserData)
    1357             : 
    1358             : {
    1359     6368280 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1360             : 
    1361     6368180 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1362             :     {
    1363           5 :         fprintf(stderr, "CPLPushErrorHandlerEx() failed.\n");
    1364           0 :         return;
    1365             :     }
    1366             : 
    1367             :     CPLErrorHandlerNode *psNode = static_cast<CPLErrorHandlerNode *>(
    1368     6368170 :         CPLMalloc(sizeof(CPLErrorHandlerNode)));
    1369     6368240 :     psNode->psNext = psCtx->psHandlerStack;
    1370     6368240 :     psNode->pfnHandler = pfnErrorHandlerNew;
    1371     6368240 :     psNode->pUserData = pUserData;
    1372     6368240 :     psNode->bCatchDebug = true;
    1373     6368240 :     psCtx->psHandlerStack = psNode;
    1374             : }
    1375             : 
    1376             : /************************************************************************/
    1377             : /*                         CPLPopErrorHandler()                         */
    1378             : /************************************************************************/
    1379             : 
    1380             : /**
    1381             :  * Pop error handler off stack.
    1382             :  *
    1383             :  * Discards the current error handler on the error handler stack, and restores
    1384             :  * the one in use before the last CPLPushErrorHandler() call.  This method
    1385             :  * has no effect if there are no error handlers on the current threads error
    1386             :  * handler stack.
    1387             :  */
    1388             : 
    1389     6368110 : void CPL_STDCALL CPLPopErrorHandler()
    1390             : 
    1391             : {
    1392     6368110 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1393             : 
    1394     6368130 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1395             :     {
    1396          55 :         fprintf(stderr, "CPLPopErrorHandler() failed.\n");
    1397           0 :         return;
    1398             :     }
    1399             : 
    1400     6368080 :     if (psCtx->psHandlerStack != nullptr)
    1401             :     {
    1402     6368170 :         CPLErrorHandlerNode *psNode = psCtx->psHandlerStack;
    1403             : 
    1404     6368170 :         psCtx->psHandlerStack = psNode->psNext;
    1405     6368170 :         VSIFree(psNode);
    1406             :     }
    1407             : }
    1408             : 
    1409             : /************************************************************************/
    1410             : /*                         CPLCallPreviousHandler()                     */
    1411             : /************************************************************************/
    1412             : 
    1413             : /**
    1414             :  * Call the previously installed error handler in the error handler stack.
    1415             :  *
    1416             :  * Only to be used by a custom error handler that wants to forward events to
    1417             :  * the previous error handler.
    1418             :  *
    1419             :  * @since GDAL 3.8
    1420             :  */
    1421             : 
    1422       20314 : void CPLCallPreviousHandler(CPLErr eErrClass, CPLErrorNum err_no,
    1423             :                             const char *pszMsg)
    1424             : {
    1425       20314 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1426             : 
    1427       20314 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1428             :     {
    1429           0 :         fprintf(stderr, "CPLCallPreviousHandler() failed.\n");
    1430           0 :         return;
    1431             :     }
    1432             : 
    1433       20314 :     if (psCtx->psHandlerStack != nullptr)
    1434             :     {
    1435       20314 :         CPLErrorHandlerNode *psCurNode = psCtx->psHandlerStack;
    1436       20314 :         psCtx->psHandlerStack = psCurNode->psNext;
    1437       20314 :         if (psCtx->psHandlerStack)
    1438             :         {
    1439         837 :             CPLErrorHandlerNode *psNewCurNode = psCtx->psHandlerStack;
    1440         837 :             psCtx->psHandlerStack->pfnHandler(eErrClass, err_no, pszMsg);
    1441         837 :             if (psNewCurNode != psCtx->psHandlerStack)
    1442             :             {
    1443           0 :                 fprintf(stderr, "CPLCallPreviousHandler() has detected that a "
    1444             :                                 "previous error handler messed up with the "
    1445             :                                 "error stack. Chaos guaranteed!\n");
    1446             :             }
    1447             :         }
    1448             :         else
    1449       19477 :             CPLDefaultErrorHandler(eErrClass, err_no, pszMsg);
    1450       20314 :         psCtx->psHandlerStack = psCurNode;
    1451             :     }
    1452             :     else
    1453             :     {
    1454           0 :         CPLDefaultErrorHandler(eErrClass, err_no, pszMsg);
    1455             :     }
    1456             : }
    1457             : 
    1458             : /************************************************************************/
    1459             : /*                 CPLSetCurrentErrorHandlerCatchDebug()                */
    1460             : /************************************************************************/
    1461             : 
    1462             : /**
    1463             :  * Set if the current error handler should intercept debug messages, or if
    1464             :  * they should be processed by the previous handler.
    1465             :  *
    1466             :  * By default when installing a custom error handler, this one intercepts
    1467             :  * debug messages. In some cases, this might not be desirable and the user
    1468             :  * would prefer that the previous installed handler (or the default one if no
    1469             :  * previous installed handler exists in the stack) deal with it. In which
    1470             :  * case, this function should be called with bCatchDebug = FALSE.
    1471             :  *
    1472             :  * @param bCatchDebug FALSE if the current error handler should not intercept
    1473             :  * debug messages
    1474             :  */
    1475             : 
    1476       26178 : void CPL_STDCALL CPLSetCurrentErrorHandlerCatchDebug(int bCatchDebug)
    1477             : {
    1478       26178 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1479             : 
    1480       26159 :     if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx))
    1481             :     {
    1482          42 :         fprintf(stderr, "CPLSetCurrentErrorHandlerCatchDebug() failed.\n");
    1483           0 :         return;
    1484             :     }
    1485             : 
    1486       26117 :     if (psCtx->psHandlerStack != nullptr)
    1487       26112 :         psCtx->psHandlerStack->bCatchDebug = CPL_TO_BOOL(bCatchDebug);
    1488             :     else
    1489           5 :         gbCatchDebug = CPL_TO_BOOL(bCatchDebug);
    1490             : }
    1491             : 
    1492             : /************************************************************************/
    1493             : /*                             _CPLAssert()                             */
    1494             : /*                                                                      */
    1495             : /*      This function is called only when an assertion fails.           */
    1496             : /************************************************************************/
    1497             : 
    1498             : /**
    1499             :  * Report failure of a logical assertion.
    1500             :  *
    1501             :  * Applications would normally use the CPLAssert() macro which expands
    1502             :  * into code calling _CPLAssert() only if the condition fails.  _CPLAssert()
    1503             :  * will generate a CE_Fatal error call to CPLError(), indicating the file
    1504             :  * name, and line number of the failed assertion, as well as containing
    1505             :  * the assertion itself.
    1506             :  *
    1507             :  * There is no reason for application code to call _CPLAssert() directly.
    1508             :  */
    1509             : 
    1510           0 : void CPL_STDCALL _CPLAssert(const char *pszExpression, const char *pszFile,
    1511             :                             int iLine)
    1512             : 
    1513             : {
    1514           0 :     CPLError(CE_Fatal, CPLE_AssertionFailed,
    1515             :              "Assertion `%s' failed "
    1516             :              "in file `%s', line %d",
    1517             :              pszExpression, pszFile, iLine);
    1518             : 
    1519             :     // Just to please compiler so it is aware the function does not return.
    1520           0 :     abort();
    1521             : }
    1522             : 
    1523             : /************************************************************************/
    1524             : /*                       CPLCleanupErrorMutex()                         */
    1525             : /************************************************************************/
    1526             : 
    1527        1553 : void CPLCleanupErrorMutex()
    1528             : {
    1529        1553 :     if (hErrorMutex != nullptr)
    1530             :     {
    1531         181 :         CPLDestroyMutex(hErrorMutex);
    1532         181 :         hErrorMutex = nullptr;
    1533             :     }
    1534        1553 :     if (fpLog != nullptr && fpLog != stderr)
    1535             :     {
    1536           1 :         fclose(fpLog);
    1537           1 :         fpLog = nullptr;
    1538           1 :         bLogInit = false;
    1539             :     }
    1540        1553 : }
    1541             : 
    1542        6243 : bool CPLIsDefaultErrorHandlerAndCatchDebug()
    1543             : {
    1544        6243 :     CPLErrorContext *psCtx = CPLGetErrorContext();
    1545        6243 :     return (psCtx == nullptr || psCtx->psHandlerStack == nullptr) &&
    1546       12486 :            gbCatchDebug && pfnErrorHandler == CPLDefaultErrorHandler;
    1547             : }
    1548             : 
    1549             : /************************************************************************/
    1550             : /*               CPLErrorStateBackuper::CPLErrorStateBackuper()         */
    1551             : /************************************************************************/
    1552             : 
    1553      132366 : CPLErrorStateBackuper::CPLErrorStateBackuper(CPLErrorHandler hHandler)
    1554      132366 :     : m_nLastErrorNum(CPLGetLastErrorNo()),
    1555      264725 :       m_nLastErrorType(CPLGetLastErrorType()),
    1556             :       m_osLastErrorMsg(CPLGetLastErrorMsg()),
    1557      264730 :       m_nLastErrorCounter(CPLGetErrorCounter()),
    1558             :       m_poErrorHandlerPusher(
    1559             :           hHandler ? std::make_unique<CPLErrorHandlerPusher>(hHandler)
    1560      132364 :                    : nullptr)
    1561             : {
    1562      132362 : }
    1563             : 
    1564             : /************************************************************************/
    1565             : /*               CPLErrorStateBackuper::~CPLErrorStateBackuper()        */
    1566             : /************************************************************************/
    1567             : 
    1568      132354 : CPLErrorStateBackuper::~CPLErrorStateBackuper()
    1569             : {
    1570      132350 :     CPLErrorSetState(m_nLastErrorType, m_nLastErrorNum,
    1571      132368 :                      m_osLastErrorMsg.c_str(), &m_nLastErrorCounter);
    1572      132360 : }
    1573             : 
    1574             : /*! @cond Doxygen_Suppress */
    1575             : 
    1576             : /************************************************************************/
    1577             : /*                CPLErrorAccumulator::Context::~Context()              */
    1578             : /************************************************************************/
    1579             : 
    1580       89500 : CPLErrorAccumulator::Context::~Context()
    1581             : {
    1582       89500 :     CPLPopErrorHandler();
    1583       89503 : }
    1584             : 
    1585             : /************************************************************************/
    1586             : /*             CPLErrorAccumulator::InstallForCurrentScope()            */
    1587             : /************************************************************************/
    1588             : 
    1589       89573 : CPLErrorAccumulator::Context CPLErrorAccumulator::InstallForCurrentScope()
    1590             : {
    1591       89573 :     CPLPushErrorHandlerEx(CPLErrorAccumulator::Accumulator, this);
    1592       89541 :     return CPLErrorAccumulator::Context();
    1593             : }
    1594             : 
    1595             : /************************************************************************/
    1596             : /*                    CPLErrorAccumulator::ReplayErrors()               */
    1597             : /************************************************************************/
    1598             : 
    1599         324 : void CPLErrorAccumulator::ReplayErrors()
    1600             : {
    1601         648 :     std::lock_guard oLock(mutex);
    1602         330 :     for (const auto &err : errors)
    1603             :     {
    1604           6 :         CPLError(err.type, err.no, "%s", err.msg.c_str());
    1605             :     }
    1606         324 : }
    1607             : 
    1608             : /************************************************************************/
    1609             : /*                 CPLErrorAccumulator::Accumulator()                   */
    1610             : /************************************************************************/
    1611             : 
    1612         360 : /* static */ void CPL_STDCALL CPLErrorAccumulator::Accumulator(CPLErr eErr,
    1613             :                                                                CPLErrorNum no,
    1614             :                                                                const char *msg)
    1615             : {
    1616         360 :     if (eErr != CE_Debug)
    1617             :     {
    1618             :         CPLErrorAccumulator *pThis =
    1619         360 :             static_cast<CPLErrorAccumulator *>(CPLGetErrorHandlerUserData());
    1620         360 :         std::lock_guard oLock(pThis->mutex);
    1621         360 :         pThis->errors.push_back(
    1622         720 :             CPLErrorHandlerAccumulatorStruct(eErr, no, msg));
    1623             :     }
    1624         360 : }
    1625             : 
    1626             : /*! @endcond */

Generated by: LCOV version 1.14