LCOV - code coverage report
Current view: top level - port - cpl_error.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 295 440 67.0 %
Date: 2024-04-29 01:40:10 Functions: 34 38 89.5 %

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

Generated by: LCOV version 1.14