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