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