Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: Convenience functions.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1998, Frank Warmerdam
9 : * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_config.h"
15 :
16 : #ifdef HAVE_USELOCALE
17 : // For uselocale, define _XOPEN_SOURCE = 700
18 : // but on Solaris, we don't have uselocale and we cannot have
19 : // std=c++11 with _XOPEN_SOURCE != 600
20 : #if defined(__sun__) && __cplusplus >= 201103L
21 : #if _XOPEN_SOURCE != 600
22 : #ifdef _XOPEN_SOURCE
23 : #undef _XOPEN_SOURCE
24 : #endif
25 : #define _XOPEN_SOURCE 600
26 : #endif
27 : #else
28 : #ifdef _XOPEN_SOURCE
29 : #undef _XOPEN_SOURCE
30 : #endif
31 : #define _XOPEN_SOURCE 700
32 : #endif
33 : #endif
34 :
35 : // For atoll (at least for NetBSD)
36 : #ifndef _ISOC99_SOURCE
37 : #define _ISOC99_SOURCE
38 : #endif
39 :
40 : #ifdef MSVC_USE_VLD
41 : #include <vld.h>
42 : #endif
43 :
44 : #include "cpl_conv.h"
45 :
46 : #include <algorithm>
47 : #include <atomic>
48 : #include <cctype>
49 : #include <cerrno>
50 : #include <climits>
51 : #include <clocale>
52 : #include <cmath>
53 : #include <cstdlib>
54 : #include <cstring>
55 : #include <ctime>
56 : #include <mutex>
57 : #include <set>
58 :
59 : #if HAVE_UNISTD_H
60 : #include <unistd.h>
61 : #endif
62 : #if HAVE_XLOCALE_H
63 : #include <xlocale.h> // for LC_NUMERIC_MASK on MacOS
64 : #endif
65 :
66 : #include <sys/types.h> // open
67 : #include <sys/stat.h> // open
68 : #include <fcntl.h> // open
69 :
70 : #ifdef _WIN32
71 : #include <io.h> // _isatty, _wopen
72 : #else
73 : #include <unistd.h> // isatty
74 : #endif
75 :
76 : #include <string>
77 :
78 : #if __cplusplus >= 202002L
79 : #include <bit> // For std::endian
80 : #endif
81 :
82 : #include "cpl_config.h"
83 : #include "cpl_multiproc.h"
84 : #include "cpl_string.h"
85 : #include "cpl_vsi.h"
86 : #include "cpl_vsil_curl_priv.h"
87 : #include "cpl_known_config_options.h"
88 :
89 : #ifdef DEBUG
90 : #define OGRAPISPY_ENABLED
91 : #endif
92 : #ifdef OGRAPISPY_ENABLED
93 : // Keep in sync with ograpispy.cpp
94 : void OGRAPISPYCPLSetConfigOption(const char *, const char *);
95 : void OGRAPISPYCPLSetThreadLocalConfigOption(const char *, const char *);
96 : #endif
97 :
98 : // Uncomment to get list of options that have been fetched and set.
99 : // #define DEBUG_CONFIG_OPTIONS
100 :
101 : static CPLMutex *hConfigMutex = nullptr;
102 : static volatile char **g_papszConfigOptions = nullptr;
103 : static bool gbIgnoreEnvVariables =
104 : false; // if true, only take into account configuration options set through
105 : // configuration file or
106 : // CPLSetConfigOption()/CPLSetThreadLocalConfigOption()
107 :
108 : static std::vector<std::pair<CPLSetConfigOptionSubscriber, void *>>
109 : gSetConfigOptionSubscribers{};
110 :
111 : // Used by CPLOpenShared() and friends.
112 : static CPLMutex *hSharedFileMutex = nullptr;
113 : static int nSharedFileCount = 0;
114 : static CPLSharedFileInfo *pasSharedFileList = nullptr;
115 :
116 : // Used by CPLsetlocale().
117 : static CPLMutex *hSetLocaleMutex = nullptr;
118 :
119 : // Note: ideally this should be added in CPLSharedFileInfo*
120 : // but CPLSharedFileInfo is exposed in the API, hence that trick
121 : // to hide this detail.
122 : typedef struct
123 : {
124 : GIntBig nPID; // pid of opening thread.
125 : } CPLSharedFileInfoExtra;
126 :
127 : static volatile CPLSharedFileInfoExtra *pasSharedFileListExtra = nullptr;
128 :
129 : /************************************************************************/
130 : /* CPLCalloc() */
131 : /************************************************************************/
132 :
133 : /**
134 : * Safe version of calloc().
135 : *
136 : * This function is like the C library calloc(), but raises a CE_Fatal
137 : * error with CPLError() if it fails to allocate the desired memory. It
138 : * should be used for small memory allocations that are unlikely to fail
139 : * and for which the application is unwilling to test for out of memory
140 : * conditions. It uses VSICalloc() to get the memory, so any hooking of
141 : * VSICalloc() will apply to CPLCalloc() as well. CPLFree() or VSIFree()
142 : * can be used free memory allocated by CPLCalloc().
143 : *
144 : * @param nCount number of objects to allocate.
145 : * @param nSize size (in bytes) of object to allocate.
146 : * @return pointer to newly allocated memory, only NULL if nSize * nCount is
147 : * NULL.
148 : */
149 :
150 3414620 : void *CPLCalloc(size_t nCount, size_t nSize)
151 :
152 : {
153 3414620 : if (nSize * nCount == 0)
154 9013 : return nullptr;
155 :
156 3405610 : void *pReturn = CPLMalloc(nCount * nSize);
157 3405610 : memset(pReturn, 0, nCount * nSize);
158 3405610 : return pReturn;
159 : }
160 :
161 : /************************************************************************/
162 : /* CPLMalloc() */
163 : /************************************************************************/
164 :
165 : /**
166 : * Safe version of malloc().
167 : *
168 : * This function is like the C library malloc(), but raises a CE_Fatal
169 : * error with CPLError() if it fails to allocate the desired memory. It
170 : * should be used for small memory allocations that are unlikely to fail
171 : * and for which the application is unwilling to test for out of memory
172 : * conditions. It uses VSIMalloc() to get the memory, so any hooking of
173 : * VSIMalloc() will apply to CPLMalloc() as well. CPLFree() or VSIFree()
174 : * can be used free memory allocated by CPLMalloc().
175 : *
176 : * @param nSize size (in bytes) of memory block to allocate.
177 : * @return pointer to newly allocated memory, only NULL if nSize is zero.
178 : */
179 :
180 19597400 : void *CPLMalloc(size_t nSize)
181 :
182 : {
183 19597400 : if (nSize == 0)
184 5424 : return nullptr;
185 :
186 19592000 : if ((nSize >> (8 * sizeof(nSize) - 1)) != 0)
187 : {
188 : // coverity[dead_error_begin]
189 0 : CPLError(CE_Failure, CPLE_AppDefined,
190 : "CPLMalloc(%ld): Silly size requested.",
191 : static_cast<long>(nSize));
192 0 : return nullptr;
193 : }
194 :
195 19592000 : void *pReturn = VSIMalloc(nSize);
196 19591900 : if (pReturn == nullptr)
197 : {
198 0 : if (nSize < 2000)
199 : {
200 0 : CPLEmergencyError("CPLMalloc(): Out of memory allocating a small "
201 : "number of bytes.");
202 : }
203 :
204 0 : CPLError(CE_Fatal, CPLE_OutOfMemory,
205 : "CPLMalloc(): Out of memory allocating %ld bytes.",
206 : static_cast<long>(nSize));
207 : }
208 :
209 19591900 : return pReturn;
210 : }
211 :
212 : /************************************************************************/
213 : /* CPLRealloc() */
214 : /************************************************************************/
215 :
216 : /**
217 : * Safe version of realloc().
218 : *
219 : * This function is like the C library realloc(), but raises a CE_Fatal
220 : * error with CPLError() if it fails to allocate the desired memory. It
221 : * should be used for small memory allocations that are unlikely to fail
222 : * and for which the application is unwilling to test for out of memory
223 : * conditions. It uses VSIRealloc() to get the memory, so any hooking of
224 : * VSIRealloc() will apply to CPLRealloc() as well. CPLFree() or VSIFree()
225 : * can be used free memory allocated by CPLRealloc().
226 : *
227 : * It is also safe to pass NULL in as the existing memory block for
228 : * CPLRealloc(), in which case it uses VSIMalloc() to allocate a new block.
229 : *
230 : * @param pData existing memory block which should be copied to the new block.
231 : * @param nNewSize new size (in bytes) of memory block to allocate.
232 : * @return pointer to allocated memory, only NULL if nNewSize is zero.
233 : */
234 :
235 3140500 : void *CPLRealloc(void *pData, size_t nNewSize)
236 :
237 : {
238 3140500 : if (nNewSize == 0)
239 : {
240 45 : VSIFree(pData);
241 45 : return nullptr;
242 : }
243 :
244 3140450 : if ((nNewSize >> (8 * sizeof(nNewSize) - 1)) != 0)
245 : {
246 : // coverity[dead_error_begin]
247 0 : CPLError(CE_Failure, CPLE_AppDefined,
248 : "CPLRealloc(%ld): Silly size requested.",
249 : static_cast<long>(nNewSize));
250 0 : return nullptr;
251 : }
252 :
253 3140450 : void *pReturn = nullptr;
254 :
255 3140450 : if (pData == nullptr)
256 2101670 : pReturn = VSIMalloc(nNewSize);
257 : else
258 1038780 : pReturn = VSIRealloc(pData, nNewSize);
259 :
260 3122400 : if (pReturn == nullptr)
261 : {
262 0 : if (nNewSize < 2000)
263 : {
264 0 : char szSmallMsg[80] = {};
265 :
266 0 : snprintf(szSmallMsg, sizeof(szSmallMsg),
267 : "CPLRealloc(): Out of memory allocating %ld bytes.",
268 : static_cast<long>(nNewSize));
269 0 : CPLEmergencyError(szSmallMsg);
270 : }
271 : else
272 : {
273 0 : CPLError(CE_Fatal, CPLE_OutOfMemory,
274 : "CPLRealloc(): Out of memory allocating %ld bytes.",
275 : static_cast<long>(nNewSize));
276 : }
277 : }
278 :
279 3117690 : return pReturn;
280 : }
281 :
282 : /************************************************************************/
283 : /* CPLStrdup() */
284 : /************************************************************************/
285 :
286 : /**
287 : * Safe version of strdup() function.
288 : *
289 : * This function is similar to the C library strdup() function, but if
290 : * the memory allocation fails it will issue a CE_Fatal error with
291 : * CPLError() instead of returning NULL. Memory
292 : * allocated with CPLStrdup() can be freed with CPLFree() or VSIFree().
293 : *
294 : * It is also safe to pass a NULL string into CPLStrdup(). CPLStrdup()
295 : * will allocate and return a zero length string (as opposed to a NULL
296 : * string).
297 : *
298 : * @param pszString input string to be duplicated. May be NULL.
299 : * @return pointer to a newly allocated copy of the string. Free with
300 : * CPLFree() or VSIFree().
301 : */
302 :
303 7649430 : char *CPLStrdup(const char *pszString)
304 :
305 : {
306 7649430 : if (pszString == nullptr)
307 1081850 : pszString = "";
308 :
309 7649430 : const size_t nLen = strlen(pszString);
310 7649430 : char *pszReturn = static_cast<char *>(CPLMalloc(nLen + 1));
311 7649430 : memcpy(pszReturn, pszString, nLen + 1);
312 7649430 : return (pszReturn);
313 : }
314 :
315 : /************************************************************************/
316 : /* CPLStrlwr() */
317 : /************************************************************************/
318 :
319 : /**
320 : * Convert each characters of the string to lower case.
321 : *
322 : * For example, "ABcdE" will be converted to "abcde".
323 : * Starting with GDAL 3.9, this function is no longer locale dependent.
324 : *
325 : * @param pszString input string to be converted.
326 : * @return pointer to the same string, pszString.
327 : */
328 :
329 3 : char *CPLStrlwr(char *pszString)
330 :
331 : {
332 3 : if (pszString == nullptr)
333 0 : return nullptr;
334 :
335 3 : char *pszTemp = pszString;
336 :
337 24 : while (*pszTemp)
338 : {
339 21 : *pszTemp =
340 21 : static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszTemp)));
341 21 : pszTemp++;
342 : }
343 :
344 3 : return pszString;
345 : }
346 :
347 : /************************************************************************/
348 : /* CPLFGets() */
349 : /* */
350 : /* Note: LF = \n = ASCII 10 */
351 : /* CR = \r = ASCII 13 */
352 : /************************************************************************/
353 :
354 : // ASCII characters.
355 : constexpr char knLF = 10;
356 : constexpr char knCR = 13;
357 :
358 : /**
359 : * Reads in at most one less than nBufferSize characters from the fp
360 : * stream and stores them into the buffer pointed to by pszBuffer.
361 : * Reading stops after an EOF or a newline. If a newline is read, it
362 : * is _not_ stored into the buffer. A '\\0' is stored after the last
363 : * character in the buffer. All three types of newline terminators
364 : * recognized by the CPLFGets(): single '\\r' and '\\n' and '\\r\\n'
365 : * combination.
366 : *
367 : * @param pszBuffer pointer to the targeting character buffer.
368 : * @param nBufferSize maximum size of the string to read (not including
369 : * terminating '\\0').
370 : * @param fp file pointer to read from.
371 : * @return pointer to the pszBuffer containing a string read
372 : * from the file or NULL if the error or end of file was encountered.
373 : */
374 :
375 0 : char *CPLFGets(char *pszBuffer, int nBufferSize, FILE *fp)
376 :
377 : {
378 0 : if (nBufferSize == 0 || pszBuffer == nullptr || fp == nullptr)
379 0 : return nullptr;
380 :
381 : /* -------------------------------------------------------------------- */
382 : /* Let the OS level call read what it things is one line. This */
383 : /* will include the newline. On windows, if the file happens */
384 : /* to be in text mode, the CRLF will have been converted to */
385 : /* just the newline (LF). If it is in binary mode it may well */
386 : /* have both. */
387 : /* -------------------------------------------------------------------- */
388 0 : const long nOriginalOffset = VSIFTell(fp);
389 0 : if (VSIFGets(pszBuffer, nBufferSize, fp) == nullptr)
390 0 : return nullptr;
391 :
392 0 : int nActuallyRead = static_cast<int>(strlen(pszBuffer));
393 0 : if (nActuallyRead == 0)
394 0 : return nullptr;
395 :
396 : /* -------------------------------------------------------------------- */
397 : /* If we found \r and out buffer is full, it is possible there */
398 : /* is also a pending \n. Check for it. */
399 : /* -------------------------------------------------------------------- */
400 0 : if (nBufferSize == nActuallyRead + 1 &&
401 0 : pszBuffer[nActuallyRead - 1] == knCR)
402 : {
403 0 : const int chCheck = fgetc(fp);
404 0 : if (chCheck != knLF)
405 : {
406 : // unget the character.
407 0 : if (VSIFSeek(fp, nOriginalOffset + nActuallyRead, SEEK_SET) == -1)
408 : {
409 0 : CPLError(CE_Failure, CPLE_FileIO,
410 : "Unable to unget a character");
411 : }
412 : }
413 : }
414 :
415 : /* -------------------------------------------------------------------- */
416 : /* Trim off \n, \r or \r\n if it appears at the end. We don't */
417 : /* need to do any "seeking" since we want the newline eaten. */
418 : /* -------------------------------------------------------------------- */
419 0 : if (nActuallyRead > 1 && pszBuffer[nActuallyRead - 1] == knLF &&
420 0 : pszBuffer[nActuallyRead - 2] == knCR)
421 : {
422 0 : pszBuffer[nActuallyRead - 2] = '\0';
423 : }
424 0 : else if (pszBuffer[nActuallyRead - 1] == knLF ||
425 0 : pszBuffer[nActuallyRead - 1] == knCR)
426 : {
427 0 : pszBuffer[nActuallyRead - 1] = '\0';
428 : }
429 :
430 : /* -------------------------------------------------------------------- */
431 : /* Search within the string for a \r (MacOS convention */
432 : /* apparently), and if we find it we need to trim the string, */
433 : /* and seek back. */
434 : /* -------------------------------------------------------------------- */
435 0 : char *pszExtraNewline = strchr(pszBuffer, knCR);
436 :
437 0 : if (pszExtraNewline != nullptr)
438 : {
439 0 : nActuallyRead = static_cast<int>(pszExtraNewline - pszBuffer + 1);
440 :
441 0 : *pszExtraNewline = '\0';
442 0 : if (VSIFSeek(fp, nOriginalOffset + nActuallyRead - 1, SEEK_SET) != 0)
443 0 : return nullptr;
444 :
445 : // This hackery is necessary to try and find our correct
446 : // spot on win32 systems with text mode line translation going
447 : // on. Sometimes the fseek back overshoots, but it doesn't
448 : // "realize it" till a character has been read. Try to read till
449 : // we get to the right spot and get our CR.
450 0 : int chCheck = fgetc(fp);
451 0 : while ((chCheck != knCR && chCheck != EOF) ||
452 0 : VSIFTell(fp) < nOriginalOffset + nActuallyRead)
453 : {
454 : static bool bWarned = false;
455 :
456 0 : if (!bWarned)
457 : {
458 0 : bWarned = true;
459 0 : CPLDebug("CPL",
460 : "CPLFGets() correcting for DOS text mode translation "
461 : "seek problem.");
462 : }
463 0 : chCheck = fgetc(fp);
464 : }
465 : }
466 :
467 0 : return pszBuffer;
468 : }
469 :
470 : /************************************************************************/
471 : /* CPLReadLineBuffer() */
472 : /* */
473 : /* Fetch readline buffer, and ensure it is the desired size, */
474 : /* reallocating if needed. Manages TLS (thread local storage) */
475 : /* issues for the buffer. */
476 : /* We use a special trick to track the actual size of the buffer */
477 : /* The first 4 bytes are reserved to store it as a int, hence the */
478 : /* -4 / +4 hacks with the size and pointer. */
479 : /************************************************************************/
480 2726490 : static char *CPLReadLineBuffer(int nRequiredSize)
481 :
482 : {
483 :
484 : /* -------------------------------------------------------------------- */
485 : /* A required size of -1 means the buffer should be freed. */
486 : /* -------------------------------------------------------------------- */
487 2726490 : if (nRequiredSize == -1)
488 : {
489 2302 : int bMemoryError = FALSE;
490 2302 : void *pRet = CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError);
491 2302 : if (pRet != nullptr)
492 : {
493 2050 : CPLFree(pRet);
494 2050 : CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
495 : }
496 2302 : return nullptr;
497 : }
498 :
499 : /* -------------------------------------------------------------------- */
500 : /* If the buffer doesn't exist yet, create it. */
501 : /* -------------------------------------------------------------------- */
502 2724190 : int bMemoryError = FALSE;
503 : GUInt32 *pnAlloc =
504 2724190 : static_cast<GUInt32 *>(CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError));
505 2724190 : if (bMemoryError)
506 0 : return nullptr;
507 :
508 2724190 : if (pnAlloc == nullptr)
509 : {
510 3514 : pnAlloc = static_cast<GUInt32 *>(VSI_MALLOC_VERBOSE(200));
511 3514 : if (pnAlloc == nullptr)
512 0 : return nullptr;
513 3514 : *pnAlloc = 196;
514 3514 : CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
515 : }
516 :
517 : /* -------------------------------------------------------------------- */
518 : /* If it is too small, grow it bigger. */
519 : /* -------------------------------------------------------------------- */
520 2724190 : if (static_cast<int>(*pnAlloc) - 1 < nRequiredSize)
521 : {
522 2725 : const int nNewSize = nRequiredSize + 4 + 500;
523 2725 : if (nNewSize <= 0)
524 : {
525 0 : VSIFree(pnAlloc);
526 0 : CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
527 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
528 : "CPLReadLineBuffer(): Trying to allocate more than "
529 : "2 GB.");
530 0 : return nullptr;
531 : }
532 :
533 : GUInt32 *pnAllocNew =
534 2725 : static_cast<GUInt32 *>(VSI_REALLOC_VERBOSE(pnAlloc, nNewSize));
535 2725 : if (pnAllocNew == nullptr)
536 : {
537 0 : VSIFree(pnAlloc);
538 0 : CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
539 0 : return nullptr;
540 : }
541 2725 : pnAlloc = pnAllocNew;
542 :
543 2725 : *pnAlloc = nNewSize - 4;
544 2725 : CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
545 : }
546 :
547 2724190 : return reinterpret_cast<char *>(pnAlloc + 1);
548 : }
549 :
550 : /************************************************************************/
551 : /* CPLReadLine() */
552 : /************************************************************************/
553 :
554 : /**
555 : * Simplified line reading from text file.
556 : *
557 : * Read a line of text from the given file handle, taking care
558 : * to capture CR and/or LF and strip off ... equivalent of
559 : * DKReadLine(). Pointer to an internal buffer is returned.
560 : * The application shouldn't free it, or depend on its value
561 : * past the next call to CPLReadLine().
562 : *
563 : * Note that CPLReadLine() uses VSIFGets(), so any hooking of VSI file
564 : * services should apply to CPLReadLine() as well.
565 : *
566 : * CPLReadLine() maintains an internal buffer, which will appear as a
567 : * single block memory leak in some circumstances. CPLReadLine() may
568 : * be called with a NULL FILE * at any time to free this working buffer.
569 : *
570 : * @param fp file pointer opened with VSIFOpen().
571 : *
572 : * @return pointer to an internal buffer containing a line of text read
573 : * from the file or NULL if the end of file was encountered.
574 : */
575 :
576 5 : const char *CPLReadLine(FILE *fp)
577 :
578 : {
579 : /* -------------------------------------------------------------------- */
580 : /* Cleanup case. */
581 : /* -------------------------------------------------------------------- */
582 5 : if (fp == nullptr)
583 : {
584 5 : CPLReadLineBuffer(-1);
585 5 : return nullptr;
586 : }
587 :
588 : /* -------------------------------------------------------------------- */
589 : /* Loop reading chunks of the line till we get to the end of */
590 : /* the line. */
591 : /* -------------------------------------------------------------------- */
592 0 : size_t nBytesReadThisTime = 0;
593 0 : char *pszRLBuffer = nullptr;
594 0 : size_t nReadSoFar = 0;
595 :
596 0 : do
597 : {
598 : /* --------------------------------------------------------------------
599 : */
600 : /* Grow the working buffer if we have it nearly full. Fail out */
601 : /* of read line if we can't reallocate it big enough (for */
602 : /* instance for a _very large_ file with no newlines). */
603 : /* --------------------------------------------------------------------
604 : */
605 0 : if (nReadSoFar > 100 * 1024 * 1024)
606 : // It is dubious that we need to read a line longer than 100 MB.
607 0 : return nullptr;
608 0 : pszRLBuffer = CPLReadLineBuffer(static_cast<int>(nReadSoFar) + 129);
609 0 : if (pszRLBuffer == nullptr)
610 0 : return nullptr;
611 :
612 : /* --------------------------------------------------------------------
613 : */
614 : /* Do the actual read. */
615 : /* --------------------------------------------------------------------
616 : */
617 0 : if (CPLFGets(pszRLBuffer + nReadSoFar, 128, fp) == nullptr &&
618 : nReadSoFar == 0)
619 0 : return nullptr;
620 :
621 0 : nBytesReadThisTime = strlen(pszRLBuffer + nReadSoFar);
622 0 : nReadSoFar += nBytesReadThisTime;
623 0 : } while (nBytesReadThisTime >= 127 && pszRLBuffer[nReadSoFar - 1] != knCR &&
624 0 : pszRLBuffer[nReadSoFar - 1] != knLF);
625 :
626 0 : return pszRLBuffer;
627 : }
628 :
629 : /************************************************************************/
630 : /* CPLReadLineL() */
631 : /************************************************************************/
632 :
633 : /**
634 : * Simplified line reading from text file.
635 : *
636 : * Similar to CPLReadLine(), but reading from a large file API handle.
637 : *
638 : * @param fp file pointer opened with VSIFOpenL().
639 : *
640 : * @return pointer to an internal buffer containing a line of text read
641 : * from the file or NULL if the end of file was encountered.
642 : */
643 :
644 196254 : const char *CPLReadLineL(VSILFILE *fp)
645 : {
646 196254 : return CPLReadLine2L(fp, -1, nullptr);
647 : }
648 :
649 : /************************************************************************/
650 : /* CPLReadLine2L() */
651 : /************************************************************************/
652 :
653 : /**
654 : * Simplified line reading from text file.
655 : *
656 : * Similar to CPLReadLine(), but reading from a large file API handle.
657 : *
658 : * @param fp file pointer opened with VSIFOpenL().
659 : * @param nMaxCars maximum number of characters allowed, or -1 for no limit.
660 : * @param papszOptions NULL-terminated array of options. Unused for now.
661 :
662 : * @return pointer to an internal buffer containing a line of text read
663 : * from the file or NULL if the end of file was encountered or the maximum
664 : * number of characters allowed reached.
665 : *
666 : * @since GDAL 1.7.0
667 : */
668 :
669 1314390 : const char *CPLReadLine2L(VSILFILE *fp, int nMaxCars,
670 : CPL_UNUSED CSLConstList papszOptions)
671 :
672 : {
673 : int nBufLength;
674 2628780 : return CPLReadLine3L(fp, nMaxCars, &nBufLength, papszOptions);
675 : }
676 :
677 : /************************************************************************/
678 : /* CPLReadLine3L() */
679 : /************************************************************************/
680 :
681 : /**
682 : * Simplified line reading from text file.
683 : *
684 : * Similar to CPLReadLine(), but reading from a large file API handle.
685 : *
686 : * @param fp file pointer opened with VSIFOpenL().
687 : * @param nMaxCars maximum number of characters allowed, or -1 for no limit.
688 : * @param papszOptions NULL-terminated array of options. Unused for now.
689 : * @param[out] pnBufLength size of output string (must be non-NULL)
690 :
691 : * @return pointer to an internal buffer containing a line of text read
692 : * from the file or NULL if the end of file was encountered or the maximum
693 : * number of characters allowed reached.
694 : *
695 : * @since GDAL 2.3.0
696 : */
697 1377580 : const char *CPLReadLine3L(VSILFILE *fp, int nMaxCars, int *pnBufLength,
698 : CPL_UNUSED CSLConstList papszOptions)
699 : {
700 : /* -------------------------------------------------------------------- */
701 : /* Cleanup case. */
702 : /* -------------------------------------------------------------------- */
703 1377580 : if (fp == nullptr)
704 : {
705 2297 : CPLReadLineBuffer(-1);
706 2297 : return nullptr;
707 : }
708 :
709 : /* -------------------------------------------------------------------- */
710 : /* Loop reading chunks of the line till we get to the end of */
711 : /* the line. */
712 : /* -------------------------------------------------------------------- */
713 1375280 : char *pszRLBuffer = nullptr;
714 1375280 : const size_t nChunkSize = 40;
715 1375280 : char szChunk[nChunkSize] = {};
716 1375280 : size_t nChunkBytesRead = 0;
717 1375280 : size_t nChunkBytesConsumed = 0;
718 :
719 1375280 : *pnBufLength = 0;
720 1375280 : szChunk[0] = 0;
721 :
722 : while (true)
723 : {
724 : /* --------------------------------------------------------------------
725 : */
726 : /* Read a chunk from the input file. */
727 : /* --------------------------------------------------------------------
728 : */
729 2724190 : if (*pnBufLength > INT_MAX - static_cast<int>(nChunkSize) - 1)
730 : {
731 0 : CPLError(CE_Failure, CPLE_AppDefined,
732 : "Too big line : more than 2 billion characters!.");
733 0 : CPLReadLineBuffer(-1);
734 0 : return nullptr;
735 : }
736 :
737 : pszRLBuffer =
738 2724190 : CPLReadLineBuffer(static_cast<int>(*pnBufLength + nChunkSize + 1));
739 2724190 : if (pszRLBuffer == nullptr)
740 0 : return nullptr;
741 :
742 2724190 : if (nChunkBytesRead == nChunkBytesConsumed + 1)
743 : {
744 :
745 : // case where one character is left over from last read.
746 1348910 : szChunk[0] = szChunk[nChunkBytesConsumed];
747 :
748 1348910 : nChunkBytesConsumed = 0;
749 1348910 : nChunkBytesRead = VSIFReadL(szChunk + 1, 1, nChunkSize - 1, fp) + 1;
750 : }
751 : else
752 : {
753 1375280 : nChunkBytesConsumed = 0;
754 :
755 : // fresh read.
756 1375280 : nChunkBytesRead = VSIFReadL(szChunk, 1, nChunkSize, fp);
757 1375280 : if (nChunkBytesRead == 0)
758 : {
759 11357 : if (*pnBufLength == 0)
760 11357 : return nullptr;
761 :
762 0 : break;
763 : }
764 : }
765 :
766 : /* --------------------------------------------------------------------
767 : */
768 : /* copy over characters watching for end-of-line. */
769 : /* --------------------------------------------------------------------
770 : */
771 2712830 : bool bBreak = false;
772 75050600 : while (nChunkBytesConsumed < nChunkBytesRead - 1 && !bBreak)
773 : {
774 72337800 : if ((szChunk[nChunkBytesConsumed] == knCR &&
775 172272 : szChunk[nChunkBytesConsumed + 1] == knLF) ||
776 72165800 : (szChunk[nChunkBytesConsumed] == knLF &&
777 1179920 : szChunk[nChunkBytesConsumed + 1] == knCR))
778 : {
779 171995 : nChunkBytesConsumed += 2;
780 171995 : bBreak = true;
781 : }
782 72165800 : else if (szChunk[nChunkBytesConsumed] == knLF ||
783 70985900 : szChunk[nChunkBytesConsumed] == knCR)
784 : {
785 1180190 : nChunkBytesConsumed += 1;
786 1180190 : bBreak = true;
787 : }
788 : else
789 : {
790 70985600 : pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
791 70985600 : if (nMaxCars >= 0 && *pnBufLength == nMaxCars)
792 : {
793 1 : CPLError(CE_Failure, CPLE_AppDefined,
794 : "Maximum number of characters allowed reached.");
795 1 : return nullptr;
796 : }
797 : }
798 : }
799 :
800 2712830 : if (bBreak)
801 1352190 : break;
802 :
803 : /* --------------------------------------------------------------------
804 : */
805 : /* If there is a remaining character and it is not a newline */
806 : /* consume it. If it is a newline, but we are clearly at the */
807 : /* end of the file then consume it. */
808 : /* --------------------------------------------------------------------
809 : */
810 1360640 : if (nChunkBytesConsumed == nChunkBytesRead - 1 &&
811 : nChunkBytesRead < nChunkSize)
812 : {
813 11732 : if (szChunk[nChunkBytesConsumed] == knLF ||
814 1724 : szChunk[nChunkBytesConsumed] == knCR)
815 : {
816 10008 : nChunkBytesConsumed++;
817 10008 : break;
818 : }
819 :
820 1724 : pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
821 1724 : break;
822 : }
823 1348910 : }
824 :
825 : /* -------------------------------------------------------------------- */
826 : /* If we have left over bytes after breaking out, seek back to */
827 : /* ensure they remain to be read next time. */
828 : /* -------------------------------------------------------------------- */
829 1363920 : if (nChunkBytesConsumed < nChunkBytesRead)
830 : {
831 1346680 : const size_t nBytesToPush = nChunkBytesRead - nChunkBytesConsumed;
832 :
833 1346680 : if (VSIFSeekL(fp, VSIFTellL(fp) - nBytesToPush, SEEK_SET) != 0)
834 0 : return nullptr;
835 : }
836 :
837 1363920 : pszRLBuffer[*pnBufLength] = '\0';
838 :
839 1363920 : return pszRLBuffer;
840 : }
841 :
842 : /************************************************************************/
843 : /* CPLScanString() */
844 : /************************************************************************/
845 :
846 : /**
847 : * Scan up to a maximum number of characters from a given string,
848 : * allocate a buffer for a new string and fill it with scanned characters.
849 : *
850 : * @param pszString String containing characters to be scanned. It may be
851 : * terminated with a null character.
852 : *
853 : * @param nMaxLength The maximum number of character to read. Less
854 : * characters will be read if a null character is encountered.
855 : *
856 : * @param bTrimSpaces If TRUE, trim ending spaces from the input string.
857 : * Character considered as empty using isspace(3) function.
858 : *
859 : * @param bNormalize If TRUE, replace ':' symbol with the '_'. It is needed if
860 : * resulting string will be used in CPL dictionaries.
861 : *
862 : * @return Pointer to the resulting string buffer. Caller responsible to free
863 : * this buffer with CPLFree().
864 : */
865 :
866 5202 : char *CPLScanString(const char *pszString, int nMaxLength, int bTrimSpaces,
867 : int bNormalize)
868 : {
869 5202 : if (!pszString)
870 0 : return nullptr;
871 :
872 5202 : if (!nMaxLength)
873 2 : return CPLStrdup("");
874 :
875 5200 : char *pszBuffer = static_cast<char *>(CPLMalloc(nMaxLength + 1));
876 5200 : if (!pszBuffer)
877 0 : return nullptr;
878 :
879 5200 : strncpy(pszBuffer, pszString, nMaxLength);
880 5200 : pszBuffer[nMaxLength] = '\0';
881 :
882 5200 : if (bTrimSpaces)
883 : {
884 5200 : size_t i = strlen(pszBuffer);
885 6324 : while (i > 0)
886 : {
887 6290 : i--;
888 6290 : if (!isspace(static_cast<unsigned char>(pszBuffer[i])))
889 5166 : break;
890 1124 : pszBuffer[i] = '\0';
891 : }
892 : }
893 :
894 5200 : if (bNormalize)
895 : {
896 5082 : size_t i = strlen(pszBuffer);
897 38750 : while (i > 0)
898 : {
899 33668 : i--;
900 33668 : if (pszBuffer[i] == ':')
901 0 : pszBuffer[i] = '_';
902 : }
903 : }
904 :
905 5200 : return pszBuffer;
906 : }
907 :
908 : /************************************************************************/
909 : /* CPLScanLong() */
910 : /************************************************************************/
911 :
912 : /**
913 : * Scan up to a maximum number of characters from a string and convert
914 : * the result to a long.
915 : *
916 : * @param pszString String containing characters to be scanned. It may be
917 : * terminated with a null character.
918 : *
919 : * @param nMaxLength The maximum number of character to consider as part
920 : * of the number. Less characters will be considered if a null character
921 : * is encountered.
922 : *
923 : * @return Long value, converted from its ASCII form.
924 : */
925 :
926 571 : long CPLScanLong(const char *pszString, int nMaxLength)
927 : {
928 571 : CPLAssert(nMaxLength >= 0);
929 571 : if (pszString == nullptr)
930 0 : return 0;
931 571 : const size_t nLength = CPLStrnlen(pszString, nMaxLength);
932 1142 : const std::string osValue(pszString, nLength);
933 571 : return atol(osValue.c_str());
934 : }
935 :
936 : /************************************************************************/
937 : /* CPLScanULong() */
938 : /************************************************************************/
939 :
940 : /**
941 : * Scan up to a maximum number of characters from a string and convert
942 : * the result to a unsigned long.
943 : *
944 : * @param pszString String containing characters to be scanned. It may be
945 : * terminated with a null character.
946 : *
947 : * @param nMaxLength The maximum number of character to consider as part
948 : * of the number. Less characters will be considered if a null character
949 : * is encountered.
950 : *
951 : * @return Unsigned long value, converted from its ASCII form.
952 : */
953 :
954 0 : unsigned long CPLScanULong(const char *pszString, int nMaxLength)
955 : {
956 0 : CPLAssert(nMaxLength >= 0);
957 0 : if (pszString == nullptr)
958 0 : return 0;
959 0 : const size_t nLength = CPLStrnlen(pszString, nMaxLength);
960 0 : const std::string osValue(pszString, nLength);
961 0 : return strtoul(osValue.c_str(), nullptr, 10);
962 : }
963 :
964 : /************************************************************************/
965 : /* CPLScanUIntBig() */
966 : /************************************************************************/
967 :
968 : /**
969 : * Extract big integer from string.
970 : *
971 : * Scan up to a maximum number of characters from a string and convert
972 : * the result to a GUIntBig.
973 : *
974 : * @param pszString String containing characters to be scanned. It may be
975 : * terminated with a null character.
976 : *
977 : * @param nMaxLength The maximum number of character to consider as part
978 : * of the number. Less characters will be considered if a null character
979 : * is encountered.
980 : *
981 : * @return GUIntBig value, converted from its ASCII form.
982 : */
983 :
984 16048 : GUIntBig CPLScanUIntBig(const char *pszString, int nMaxLength)
985 : {
986 16048 : CPLAssert(nMaxLength >= 0);
987 16048 : if (pszString == nullptr)
988 0 : return 0;
989 16048 : const size_t nLength = CPLStrnlen(pszString, nMaxLength);
990 32096 : const std::string osValue(pszString, nLength);
991 :
992 : /* -------------------------------------------------------------------- */
993 : /* Fetch out the result */
994 : /* -------------------------------------------------------------------- */
995 16048 : return strtoull(osValue.c_str(), nullptr, 10);
996 : }
997 :
998 : /************************************************************************/
999 : /* CPLAtoGIntBig() */
1000 : /************************************************************************/
1001 :
1002 : /**
1003 : * Convert a string to a 64 bit signed integer.
1004 : *
1005 : * @param pszString String containing 64 bit signed integer.
1006 : * @return 64 bit signed integer.
1007 : * @since GDAL 2.0
1008 : */
1009 :
1010 111100 : GIntBig CPLAtoGIntBig(const char *pszString)
1011 : {
1012 111100 : return atoll(pszString);
1013 : }
1014 :
1015 : #if defined(__MINGW32__) || defined(__sun__)
1016 :
1017 : // mingw atoll() doesn't return ERANGE in case of overflow
1018 : static int CPLAtoGIntBigExHasOverflow(const char *pszString, GIntBig nVal)
1019 : {
1020 : if (strlen(pszString) <= 18)
1021 : return FALSE;
1022 : while (*pszString == ' ')
1023 : pszString++;
1024 : if (*pszString == '+')
1025 : pszString++;
1026 : char szBuffer[32] = {};
1027 : /* x86_64-w64-mingw32-g++ (GCC) 4.8.2 annoyingly warns */
1028 : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1029 : #pragma GCC diagnostic push
1030 : #pragma GCC diagnostic ignored "-Wformat"
1031 : #endif
1032 : snprintf(szBuffer, sizeof(szBuffer), CPL_FRMT_GIB, nVal);
1033 : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1034 : #pragma GCC diagnostic pop
1035 : #endif
1036 : return strcmp(szBuffer, pszString) != 0;
1037 : }
1038 :
1039 : #endif
1040 :
1041 : /************************************************************************/
1042 : /* CPLAtoGIntBigEx() */
1043 : /************************************************************************/
1044 :
1045 : /**
1046 : * Convert a string to a 64 bit signed integer.
1047 : *
1048 : * @param pszString String containing 64 bit signed integer.
1049 : * @param bWarn Issue a warning if an overflow occurs during conversion
1050 : * @param pbOverflow Pointer to an integer to store if an overflow occurred, or
1051 : * NULL
1052 : * @return 64 bit signed integer.
1053 : * @since GDAL 2.0
1054 : */
1055 :
1056 103448 : GIntBig CPLAtoGIntBigEx(const char *pszString, int bWarn, int *pbOverflow)
1057 : {
1058 103448 : errno = 0;
1059 103448 : GIntBig nVal = strtoll(pszString, nullptr, 10);
1060 103448 : if (errno == ERANGE
1061 : #if defined(__MINGW32__) || defined(__sun__)
1062 : || CPLAtoGIntBigExHasOverflow(pszString, nVal)
1063 : #endif
1064 : )
1065 : {
1066 4 : if (pbOverflow)
1067 2 : *pbOverflow = TRUE;
1068 4 : if (bWarn)
1069 : {
1070 2 : CPLError(CE_Warning, CPLE_AppDefined,
1071 : "64 bit integer overflow when converting %s", pszString);
1072 : }
1073 4 : while (*pszString == ' ')
1074 0 : pszString++;
1075 4 : return (*pszString == '-') ? GINTBIG_MIN : GINTBIG_MAX;
1076 : }
1077 103444 : else if (pbOverflow)
1078 : {
1079 3874 : *pbOverflow = FALSE;
1080 : }
1081 103444 : return nVal;
1082 : }
1083 :
1084 : /************************************************************************/
1085 : /* CPLScanPointer() */
1086 : /************************************************************************/
1087 :
1088 : /**
1089 : * Extract pointer from string.
1090 : *
1091 : * Scan up to a maximum number of characters from a string and convert
1092 : * the result to a pointer.
1093 : *
1094 : * @param pszString String containing characters to be scanned. It may be
1095 : * terminated with a null character.
1096 : *
1097 : * @param nMaxLength The maximum number of character to consider as part
1098 : * of the number. Less characters will be considered if a null character
1099 : * is encountered.
1100 : *
1101 : * @return pointer value, converted from its ASCII form.
1102 : */
1103 :
1104 2438 : void *CPLScanPointer(const char *pszString, int nMaxLength)
1105 : {
1106 2438 : char szTemp[128] = {};
1107 :
1108 : /* -------------------------------------------------------------------- */
1109 : /* Compute string into local buffer, and terminate it. */
1110 : /* -------------------------------------------------------------------- */
1111 2438 : if (nMaxLength > static_cast<int>(sizeof(szTemp)) - 1)
1112 0 : nMaxLength = sizeof(szTemp) - 1;
1113 :
1114 2438 : strncpy(szTemp, pszString, nMaxLength);
1115 2438 : szTemp[nMaxLength] = '\0';
1116 :
1117 : /* -------------------------------------------------------------------- */
1118 : /* On MSVC we have to scanf pointer values without the 0x */
1119 : /* prefix. */
1120 : /* -------------------------------------------------------------------- */
1121 2438 : if (STARTS_WITH_CI(szTemp, "0x"))
1122 : {
1123 2421 : void *pResult = nullptr;
1124 :
1125 : #if defined(__MSVCRT__) || (defined(_WIN32) && defined(_MSC_VER))
1126 : // cppcheck-suppress invalidscanf
1127 : sscanf(szTemp + 2, "%p", &pResult);
1128 : #else
1129 : // cppcheck-suppress invalidscanf
1130 2421 : sscanf(szTemp, "%p", &pResult);
1131 :
1132 : // Solaris actually behaves like MSVCRT.
1133 2421 : if (pResult == nullptr)
1134 : {
1135 : // cppcheck-suppress invalidscanf
1136 0 : sscanf(szTemp + 2, "%p", &pResult);
1137 : }
1138 : #endif
1139 2421 : return pResult;
1140 : }
1141 :
1142 : #if SIZEOF_VOIDP == 8
1143 17 : return reinterpret_cast<void *>(CPLScanUIntBig(szTemp, nMaxLength));
1144 : #else
1145 : return reinterpret_cast<void *>(CPLScanULong(szTemp, nMaxLength));
1146 : #endif
1147 : }
1148 :
1149 : /************************************************************************/
1150 : /* CPLScanDouble() */
1151 : /************************************************************************/
1152 :
1153 : /**
1154 : * Extract double from string.
1155 : *
1156 : * Scan up to a maximum number of characters from a string and convert the
1157 : * result to a double. This function uses CPLAtof() to convert string to
1158 : * double value, so it uses a comma as a decimal delimiter.
1159 : *
1160 : * @param pszString String containing characters to be scanned. It may be
1161 : * terminated with a null character.
1162 : *
1163 : * @param nMaxLength The maximum number of character to consider as part
1164 : * of the number. Less characters will be considered if a null character
1165 : * is encountered.
1166 : *
1167 : * @return Double value, converted from its ASCII form.
1168 : */
1169 :
1170 317 : double CPLScanDouble(const char *pszString, int nMaxLength)
1171 : {
1172 317 : char szValue[32] = {};
1173 317 : char *pszValue = nullptr;
1174 :
1175 317 : if (nMaxLength + 1 < static_cast<int>(sizeof(szValue)))
1176 317 : pszValue = szValue;
1177 : else
1178 0 : pszValue = static_cast<char *>(CPLMalloc(nMaxLength + 1));
1179 :
1180 : /* -------------------------------------------------------------------- */
1181 : /* Compute string into local buffer, and terminate it. */
1182 : /* -------------------------------------------------------------------- */
1183 317 : strncpy(pszValue, pszString, nMaxLength);
1184 317 : pszValue[nMaxLength] = '\0';
1185 :
1186 : /* -------------------------------------------------------------------- */
1187 : /* Make a pass through converting 'D's to 'E's. */
1188 : /* -------------------------------------------------------------------- */
1189 6436 : for (int i = 0; i < nMaxLength; i++)
1190 6119 : if (pszValue[i] == 'd' || pszValue[i] == 'D')
1191 45 : pszValue[i] = 'E';
1192 :
1193 : /* -------------------------------------------------------------------- */
1194 : /* The conversion itself. */
1195 : /* -------------------------------------------------------------------- */
1196 317 : const double dfValue = CPLAtof(pszValue);
1197 :
1198 317 : if (pszValue != szValue)
1199 0 : CPLFree(pszValue);
1200 317 : return dfValue;
1201 : }
1202 :
1203 : /************************************************************************/
1204 : /* CPLPrintString() */
1205 : /************************************************************************/
1206 :
1207 : /**
1208 : * Copy the string pointed to by pszSrc, NOT including the terminating
1209 : * `\\0' character, to the array pointed to by pszDest.
1210 : *
1211 : * @param pszDest Pointer to the destination string buffer. Should be
1212 : * large enough to hold the resulting string.
1213 : *
1214 : * @param pszSrc Pointer to the source buffer.
1215 : *
1216 : * @param nMaxLen Maximum length of the resulting string. If string length
1217 : * is greater than nMaxLen, it will be truncated.
1218 : *
1219 : * @return Number of characters printed.
1220 : */
1221 :
1222 12126 : int CPLPrintString(char *pszDest, const char *pszSrc, int nMaxLen)
1223 : {
1224 12126 : if (!pszDest)
1225 0 : return 0;
1226 :
1227 12126 : if (!pszSrc)
1228 : {
1229 0 : *pszDest = '\0';
1230 0 : return 1;
1231 : }
1232 :
1233 12126 : int nChars = 0;
1234 12126 : char *pszTemp = pszDest;
1235 :
1236 181586 : while (nChars < nMaxLen && *pszSrc)
1237 : {
1238 169460 : *pszTemp++ = *pszSrc++;
1239 169460 : nChars++;
1240 : }
1241 :
1242 12126 : return nChars;
1243 : }
1244 :
1245 : /************************************************************************/
1246 : /* CPLPrintStringFill() */
1247 : /************************************************************************/
1248 :
1249 : /**
1250 : * Copy the string pointed to by pszSrc, NOT including the terminating
1251 : * `\\0' character, to the array pointed to by pszDest. Remainder of the
1252 : * destination string will be filled with space characters. This is only
1253 : * difference from the PrintString().
1254 : *
1255 : * @param pszDest Pointer to the destination string buffer. Should be
1256 : * large enough to hold the resulting string.
1257 : *
1258 : * @param pszSrc Pointer to the source buffer.
1259 : *
1260 : * @param nMaxLen Maximum length of the resulting string. If string length
1261 : * is greater than nMaxLen, it will be truncated.
1262 : *
1263 : * @return Number of characters printed.
1264 : */
1265 :
1266 209 : int CPLPrintStringFill(char *pszDest, const char *pszSrc, int nMaxLen)
1267 : {
1268 209 : if (!pszDest)
1269 0 : return 0;
1270 :
1271 209 : if (!pszSrc)
1272 : {
1273 0 : memset(pszDest, ' ', nMaxLen);
1274 0 : return nMaxLen;
1275 : }
1276 :
1277 209 : char *pszTemp = pszDest;
1278 1257 : while (nMaxLen && *pszSrc)
1279 : {
1280 1048 : *pszTemp++ = *pszSrc++;
1281 1048 : nMaxLen--;
1282 : }
1283 :
1284 209 : if (nMaxLen)
1285 71 : memset(pszTemp, ' ', nMaxLen);
1286 :
1287 209 : return nMaxLen;
1288 : }
1289 :
1290 : /************************************************************************/
1291 : /* CPLPrintInt32() */
1292 : /************************************************************************/
1293 :
1294 : /**
1295 : * Print GInt32 value into specified string buffer. This string will not
1296 : * be NULL-terminated.
1297 : *
1298 : * @param pszBuffer Pointer to the destination string buffer. Should be
1299 : * large enough to hold the resulting string. Note, that the string will
1300 : * not be NULL-terminated, so user should do this himself, if needed.
1301 : *
1302 : * @param iValue Numerical value to print.
1303 : *
1304 : * @param nMaxLen Maximum length of the resulting string. If string length
1305 : * is greater than nMaxLen, it will be truncated.
1306 : *
1307 : * @return Number of characters printed.
1308 : */
1309 :
1310 9 : int CPLPrintInt32(char *pszBuffer, GInt32 iValue, int nMaxLen)
1311 : {
1312 9 : if (!pszBuffer)
1313 0 : return 0;
1314 :
1315 9 : if (nMaxLen >= 64)
1316 0 : nMaxLen = 63;
1317 :
1318 9 : char szTemp[64] = {};
1319 :
1320 : #if UINT_MAX == 65535
1321 : snprintf(szTemp, sizeof(szTemp), "%*ld", nMaxLen, iValue);
1322 : #else
1323 9 : snprintf(szTemp, sizeof(szTemp), "%*d", nMaxLen, iValue);
1324 : #endif
1325 :
1326 9 : return CPLPrintString(pszBuffer, szTemp, nMaxLen);
1327 : }
1328 :
1329 : /************************************************************************/
1330 : /* CPLPrintUIntBig() */
1331 : /************************************************************************/
1332 :
1333 : /**
1334 : * Print GUIntBig value into specified string buffer. This string will not
1335 : * be NULL-terminated.
1336 : *
1337 : * @param pszBuffer Pointer to the destination string buffer. Should be
1338 : * large enough to hold the resulting string. Note, that the string will
1339 : * not be NULL-terminated, so user should do this himself, if needed.
1340 : *
1341 : * @param iValue Numerical value to print.
1342 : *
1343 : * @param nMaxLen Maximum length of the resulting string. If string length
1344 : * is greater than nMaxLen, it will be truncated.
1345 : *
1346 : * @return Number of characters printed.
1347 : */
1348 :
1349 24 : int CPLPrintUIntBig(char *pszBuffer, GUIntBig iValue, int nMaxLen)
1350 : {
1351 24 : if (!pszBuffer)
1352 0 : return 0;
1353 :
1354 24 : if (nMaxLen >= 64)
1355 0 : nMaxLen = 63;
1356 :
1357 24 : char szTemp[64] = {};
1358 :
1359 : #if defined(__MSVCRT__) || (defined(_WIN32) && defined(_MSC_VER))
1360 : /* x86_64-w64-mingw32-g++ (GCC) 4.8.2 annoyingly warns */
1361 : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1362 : #pragma GCC diagnostic push
1363 : #pragma GCC diagnostic ignored "-Wformat"
1364 : #pragma GCC diagnostic ignored "-Wformat-extra-args"
1365 : #endif
1366 : snprintf(szTemp, sizeof(szTemp), "%*I64u", nMaxLen, iValue);
1367 : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
1368 : #pragma GCC diagnostic pop
1369 : #endif
1370 : #else
1371 24 : snprintf(szTemp, sizeof(szTemp), "%*llu", nMaxLen, iValue);
1372 : #endif
1373 :
1374 24 : return CPLPrintString(pszBuffer, szTemp, nMaxLen);
1375 : }
1376 :
1377 : /************************************************************************/
1378 : /* CPLPrintPointer() */
1379 : /************************************************************************/
1380 :
1381 : /**
1382 : * Print pointer value into specified string buffer. This string will not
1383 : * be NULL-terminated.
1384 : *
1385 : * @param pszBuffer Pointer to the destination string buffer. Should be
1386 : * large enough to hold the resulting string. Note, that the string will
1387 : * not be NULL-terminated, so user should do this himself, if needed.
1388 : *
1389 : * @param pValue Pointer to ASCII encode.
1390 : *
1391 : * @param nMaxLen Maximum length of the resulting string. If string length
1392 : * is greater than nMaxLen, it will be truncated.
1393 : *
1394 : * @return Number of characters printed.
1395 : */
1396 :
1397 12048 : int CPLPrintPointer(char *pszBuffer, void *pValue, int nMaxLen)
1398 : {
1399 12048 : if (!pszBuffer)
1400 0 : return 0;
1401 :
1402 12048 : if (nMaxLen >= 64)
1403 9618 : nMaxLen = 63;
1404 :
1405 12048 : char szTemp[64] = {};
1406 :
1407 12048 : snprintf(szTemp, sizeof(szTemp), "%p", pValue);
1408 :
1409 : // On windows, and possibly some other platforms the sprintf("%p")
1410 : // does not prefix things with 0x so it is hard to know later if the
1411 : // value is hex encoded. Fix this up here.
1412 :
1413 12048 : if (!STARTS_WITH_CI(szTemp, "0x"))
1414 0 : snprintf(szTemp, sizeof(szTemp), "0x%p", pValue);
1415 :
1416 12048 : return CPLPrintString(pszBuffer, szTemp, nMaxLen);
1417 : }
1418 :
1419 : /************************************************************************/
1420 : /* CPLPrintDouble() */
1421 : /************************************************************************/
1422 :
1423 : /**
1424 : * Print double value into specified string buffer. Exponential character
1425 : * flag 'E' (or 'e') will be replaced with 'D', as in Fortran. Resulting
1426 : * string will not to be NULL-terminated.
1427 : *
1428 : * @param pszBuffer Pointer to the destination string buffer. Should be
1429 : * large enough to hold the resulting string. Note, that the string will
1430 : * not be NULL-terminated, so user should do this himself, if needed.
1431 : *
1432 : * @param pszFormat Format specifier (for example, "%16.9E").
1433 : *
1434 : * @param dfValue Numerical value to print.
1435 : *
1436 : * @param pszLocale Unused.
1437 : *
1438 : * @return Number of characters printed.
1439 : */
1440 :
1441 0 : int CPLPrintDouble(char *pszBuffer, const char *pszFormat, double dfValue,
1442 : CPL_UNUSED const char *pszLocale)
1443 : {
1444 0 : if (!pszBuffer)
1445 0 : return 0;
1446 :
1447 0 : const int knDoubleBufferSize = 64;
1448 0 : char szTemp[knDoubleBufferSize] = {};
1449 :
1450 0 : CPLsnprintf(szTemp, knDoubleBufferSize, pszFormat, dfValue);
1451 0 : szTemp[knDoubleBufferSize - 1] = '\0';
1452 :
1453 0 : for (int i = 0; szTemp[i] != '\0'; i++)
1454 : {
1455 0 : if (szTemp[i] == 'E' || szTemp[i] == 'e')
1456 0 : szTemp[i] = 'D';
1457 : }
1458 :
1459 0 : return CPLPrintString(pszBuffer, szTemp, 64);
1460 : }
1461 :
1462 : /************************************************************************/
1463 : /* CPLPrintTime() */
1464 : /************************************************************************/
1465 :
1466 : /**
1467 : * Print specified time value accordingly to the format options and
1468 : * specified locale name. This function does following:
1469 : *
1470 : * - if locale parameter is not NULL, the current locale setting will be
1471 : * stored and replaced with the specified one;
1472 : * - format time value with the strftime(3) function;
1473 : * - restore back current locale, if was saved.
1474 : *
1475 : * @param pszBuffer Pointer to the destination string buffer. Should be
1476 : * large enough to hold the resulting string. Note, that the string will
1477 : * not be NULL-terminated, so user should do this himself, if needed.
1478 : *
1479 : * @param nMaxLen Maximum length of the resulting string. If string length is
1480 : * greater than nMaxLen, it will be truncated.
1481 : *
1482 : * @param pszFormat Controls the output format. Options are the same as
1483 : * for strftime(3) function.
1484 : *
1485 : * @param poBrokenTime Pointer to the broken-down time structure. May be
1486 : * requested with the VSIGMTime() and VSILocalTime() functions.
1487 : *
1488 : * @param pszLocale Pointer to a character string containing locale name
1489 : * ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL we will not
1490 : * manipulate with locale settings and current process locale will be used for
1491 : * printing. Be aware that it may be unsuitable to use current locale for
1492 : * printing time, because all names will be printed in your native language,
1493 : * as well as time format settings also may be adjusted differently from the
1494 : * C/POSIX defaults. To solve these problems this option was introduced.
1495 : *
1496 : * @return Number of characters printed.
1497 : */
1498 :
1499 34 : int CPLPrintTime(char *pszBuffer, int nMaxLen, const char *pszFormat,
1500 : const struct tm *poBrokenTime, const char *pszLocale)
1501 : {
1502 : char *pszTemp =
1503 34 : static_cast<char *>(CPLMalloc((nMaxLen + 1) * sizeof(char)));
1504 :
1505 34 : if (pszLocale && EQUAL(pszLocale, "C") &&
1506 34 : strcmp(pszFormat, "%a, %d %b %Y %H:%M:%S GMT") == 0)
1507 : {
1508 : // Particular case when formatting RFC822 datetime, to avoid locale
1509 : // change
1510 : static const char *const aszMonthStr[] = {"Jan", "Feb", "Mar", "Apr",
1511 : "May", "Jun", "Jul", "Aug",
1512 : "Sep", "Oct", "Nov", "Dec"};
1513 : static const char *const aszDayOfWeek[] = {"Sun", "Mon", "Tue", "Wed",
1514 : "Thu", "Fri", "Sat"};
1515 68 : snprintf(pszTemp, nMaxLen + 1, "%s, %02d %s %04d %02d:%02d:%02d GMT",
1516 34 : aszDayOfWeek[std::max(0, std::min(6, poBrokenTime->tm_wday))],
1517 34 : poBrokenTime->tm_mday,
1518 34 : aszMonthStr[std::max(0, std::min(11, poBrokenTime->tm_mon))],
1519 34 : poBrokenTime->tm_year + 1900, poBrokenTime->tm_hour,
1520 68 : poBrokenTime->tm_min, poBrokenTime->tm_sec);
1521 : }
1522 : else
1523 : {
1524 : #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
1525 : char *pszCurLocale = NULL;
1526 :
1527 : if (pszLocale || EQUAL(pszLocale, ""))
1528 : {
1529 : // Save the current locale.
1530 : pszCurLocale = CPLsetlocale(LC_ALL, NULL);
1531 : // Set locale to the specified value.
1532 : CPLsetlocale(LC_ALL, pszLocale);
1533 : }
1534 : #else
1535 : (void)pszLocale;
1536 : #endif
1537 :
1538 0 : if (!strftime(pszTemp, nMaxLen + 1, pszFormat, poBrokenTime))
1539 0 : memset(pszTemp, 0, nMaxLen + 1);
1540 :
1541 : #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
1542 : // Restore stored locale back.
1543 : if (pszCurLocale)
1544 : CPLsetlocale(LC_ALL, pszCurLocale);
1545 : #endif
1546 : }
1547 :
1548 34 : const int nChars = CPLPrintString(pszBuffer, pszTemp, nMaxLen);
1549 :
1550 34 : CPLFree(pszTemp);
1551 :
1552 34 : return nChars;
1553 : }
1554 :
1555 : /************************************************************************/
1556 : /* CPLVerifyConfiguration() */
1557 : /************************************************************************/
1558 :
1559 0 : void CPLVerifyConfiguration()
1560 :
1561 : {
1562 : /* -------------------------------------------------------------------- */
1563 : /* Verify data types. */
1564 : /* -------------------------------------------------------------------- */
1565 : static_assert(sizeof(short) == 2); // We unfortunately rely on this
1566 : static_assert(sizeof(int) == 4); // We unfortunately rely on this
1567 : static_assert(sizeof(float) == 4); // We unfortunately rely on this
1568 : static_assert(sizeof(double) == 8); // We unfortunately rely on this
1569 : static_assert(sizeof(GInt64) == 8);
1570 : static_assert(sizeof(GInt32) == 4);
1571 : static_assert(sizeof(GInt16) == 2);
1572 : static_assert(sizeof(GByte) == 1);
1573 :
1574 : /* -------------------------------------------------------------------- */
1575 : /* Verify byte order */
1576 : /* -------------------------------------------------------------------- */
1577 : #ifdef CPL_LSB
1578 : #if __cplusplus >= 202002L
1579 : static_assert(std::endian::native == std::endian::little);
1580 : #elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
1581 : static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
1582 : #endif
1583 : #elif defined(CPL_MSB)
1584 : #if __cplusplus >= 202002L
1585 : static_assert(std::endian::native == std::endian::big);
1586 : #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
1587 : static_assert(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
1588 : #endif
1589 : #else
1590 : #error "CPL_LSB or CPL_MSB must be defined"
1591 : #endif
1592 0 : }
1593 :
1594 : #ifdef DEBUG_CONFIG_OPTIONS
1595 :
1596 : static CPLMutex *hRegisterConfigurationOptionMutex = nullptr;
1597 : static std::set<CPLString> *paoGetKeys = nullptr;
1598 : static std::set<CPLString> *paoSetKeys = nullptr;
1599 :
1600 : /************************************************************************/
1601 : /* CPLShowAccessedOptions() */
1602 : /************************************************************************/
1603 :
1604 : static void CPLShowAccessedOptions()
1605 : {
1606 : std::set<CPLString>::iterator aoIter;
1607 :
1608 : printf("Configuration options accessed in reading : "); /*ok*/
1609 : aoIter = paoGetKeys->begin();
1610 : while (aoIter != paoGetKeys->end())
1611 : {
1612 : printf("%s, ", (*aoIter).c_str()); /*ok*/
1613 : ++aoIter;
1614 : }
1615 : printf("\n"); /*ok*/
1616 :
1617 : printf("Configuration options accessed in writing : "); /*ok*/
1618 : aoIter = paoSetKeys->begin();
1619 : while (aoIter != paoSetKeys->end())
1620 : {
1621 : printf("%s, ", (*aoIter).c_str()); /*ok*/
1622 : ++aoIter;
1623 : }
1624 : printf("\n"); /*ok*/
1625 :
1626 : delete paoGetKeys;
1627 : delete paoSetKeys;
1628 : paoGetKeys = nullptr;
1629 : paoSetKeys = nullptr;
1630 : }
1631 :
1632 : /************************************************************************/
1633 : /* CPLAccessConfigOption() */
1634 : /************************************************************************/
1635 :
1636 : static void CPLAccessConfigOption(const char *pszKey, bool bGet)
1637 : {
1638 : CPLMutexHolderD(&hRegisterConfigurationOptionMutex);
1639 : if (paoGetKeys == nullptr)
1640 : {
1641 : paoGetKeys = new std::set<CPLString>;
1642 : paoSetKeys = new std::set<CPLString>;
1643 : atexit(CPLShowAccessedOptions);
1644 : }
1645 : if (bGet)
1646 : paoGetKeys->insert(pszKey);
1647 : else
1648 : paoSetKeys->insert(pszKey);
1649 : }
1650 : #endif
1651 :
1652 : /************************************************************************/
1653 : /* CPLGetConfigOption() */
1654 : /************************************************************************/
1655 :
1656 : /**
1657 : * Get the value of a configuration option.
1658 : *
1659 : * The value is the value of a (key, value) option set with
1660 : * CPLSetConfigOption(), or CPLSetThreadLocalConfigOption() of the same
1661 : * thread. If the given option was no defined with
1662 : * CPLSetConfigOption(), it tries to find it in environment variables.
1663 : *
1664 : * Note: the string returned by CPLGetConfigOption() might be short-lived, and
1665 : * in particular it will become invalid after a call to CPLSetConfigOption()
1666 : * with the same key.
1667 : *
1668 : * To override temporary a potentially existing option with a new value, you
1669 : * can use the following snippet :
1670 : * \code{.cpp}
1671 : * // backup old value
1672 : * const char* pszOldValTmp = CPLGetConfigOption(pszKey, NULL);
1673 : * char* pszOldVal = pszOldValTmp ? CPLStrdup(pszOldValTmp) : NULL;
1674 : * // override with new value
1675 : * CPLSetConfigOption(pszKey, pszNewVal);
1676 : * // do something useful
1677 : * // restore old value
1678 : * CPLSetConfigOption(pszKey, pszOldVal);
1679 : * CPLFree(pszOldVal);
1680 : * \endcode
1681 : *
1682 : * @param pszKey the key of the option to retrieve
1683 : * @param pszDefault a default value if the key does not match existing defined
1684 : * options (may be NULL)
1685 : * @return the value associated to the key, or the default value if not found
1686 : *
1687 : * @see CPLSetConfigOption(), https://gdal.org/user/configoptions.html
1688 : */
1689 5833820 : const char *CPL_STDCALL CPLGetConfigOption(const char *pszKey,
1690 : const char *pszDefault)
1691 :
1692 : {
1693 5833820 : const char *pszResult = CPLGetThreadLocalConfigOption(pszKey, nullptr);
1694 :
1695 5833870 : if (pszResult == nullptr)
1696 : {
1697 5795130 : pszResult = CPLGetGlobalConfigOption(pszKey, nullptr);
1698 : }
1699 :
1700 5834220 : if (gbIgnoreEnvVariables)
1701 : {
1702 6 : const char *pszEnvVar = getenv(pszKey);
1703 6 : if (pszEnvVar != nullptr)
1704 : {
1705 1 : CPLDebug("CPL",
1706 : "Ignoring environment variable %s=%s because of "
1707 : "ignore-env-vars=yes setting in configuration file",
1708 : pszKey, pszEnvVar);
1709 : }
1710 : }
1711 5834210 : else if (pszResult == nullptr)
1712 : {
1713 5787940 : pszResult = getenv(pszKey);
1714 : }
1715 :
1716 5834220 : if (pszResult == nullptr)
1717 5777030 : return pszDefault;
1718 :
1719 57195 : return pszResult;
1720 : }
1721 :
1722 : /************************************************************************/
1723 : /* CPLGetConfigOptions() */
1724 : /************************************************************************/
1725 :
1726 : /**
1727 : * Return the list of configuration options as KEY=VALUE pairs.
1728 : *
1729 : * The list is the one set through the CPLSetConfigOption() API.
1730 : *
1731 : * Options that through environment variables or with
1732 : * CPLSetThreadLocalConfigOption() will *not* be listed.
1733 : *
1734 : * @return a copy of the list, to be freed with CSLDestroy().
1735 : * @since GDAL 2.2
1736 : */
1737 7 : char **CPLGetConfigOptions(void)
1738 : {
1739 14 : CPLMutexHolderD(&hConfigMutex);
1740 14 : return CSLDuplicate(const_cast<char **>(g_papszConfigOptions));
1741 : }
1742 :
1743 : /************************************************************************/
1744 : /* CPLSetConfigOptions() */
1745 : /************************************************************************/
1746 :
1747 : /**
1748 : * Replace the full list of configuration options with the passed list of
1749 : * KEY=VALUE pairs.
1750 : *
1751 : * This has the same effect of clearing the existing list, and setting
1752 : * individually each pair with the CPLSetConfigOption() API.
1753 : *
1754 : * This does not affect options set through environment variables or with
1755 : * CPLSetThreadLocalConfigOption().
1756 : *
1757 : * The passed list is copied by the function.
1758 : *
1759 : * @param papszConfigOptions the new list (or NULL).
1760 : *
1761 : * @since GDAL 2.2
1762 : */
1763 92 : void CPLSetConfigOptions(const char *const *papszConfigOptions)
1764 : {
1765 92 : CPLMutexHolderD(&hConfigMutex);
1766 92 : CSLDestroy(const_cast<char **>(g_papszConfigOptions));
1767 92 : g_papszConfigOptions = const_cast<volatile char **>(
1768 92 : CSLDuplicate(const_cast<char **>(papszConfigOptions)));
1769 92 : }
1770 :
1771 : /************************************************************************/
1772 : /* CPLGetThreadLocalConfigOption() */
1773 : /************************************************************************/
1774 :
1775 : /** Same as CPLGetConfigOption() but only with options set with
1776 : * CPLSetThreadLocalConfigOption() */
1777 5862800 : const char *CPL_STDCALL CPLGetThreadLocalConfigOption(const char *pszKey,
1778 : const char *pszDefault)
1779 :
1780 : {
1781 : #ifdef DEBUG_CONFIG_OPTIONS
1782 : CPLAccessConfigOption(pszKey, TRUE);
1783 : #endif
1784 :
1785 5862800 : const char *pszResult = nullptr;
1786 :
1787 5862800 : int bMemoryError = FALSE;
1788 : char **papszTLConfigOptions = reinterpret_cast<char **>(
1789 5862800 : CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
1790 5862670 : if (papszTLConfigOptions != nullptr)
1791 5446780 : pszResult = CSLFetchNameValue(papszTLConfigOptions, pszKey);
1792 :
1793 5862690 : if (pszResult == nullptr)
1794 5823280 : return pszDefault;
1795 :
1796 39406 : return pszResult;
1797 : }
1798 :
1799 : /************************************************************************/
1800 : /* CPLGetGlobalConfigOption() */
1801 : /************************************************************************/
1802 :
1803 : /** Same as CPLGetConfigOption() but excludes environment variables and
1804 : * options set with CPLSetThreadLocalConfigOption().
1805 : * This function should generally not be used by applications, which should
1806 : * use CPLGetConfigOption() instead.
1807 : * @since 3.8 */
1808 5796960 : const char *CPL_STDCALL CPLGetGlobalConfigOption(const char *pszKey,
1809 : const char *pszDefault)
1810 : {
1811 : #ifdef DEBUG_CONFIG_OPTIONS
1812 : CPLAccessConfigOption(pszKey, TRUE);
1813 : #endif
1814 :
1815 11594300 : CPLMutexHolderD(&hConfigMutex);
1816 :
1817 : const char *pszResult =
1818 5797340 : CSLFetchNameValue(const_cast<char **>(g_papszConfigOptions), pszKey);
1819 :
1820 5797340 : if (pszResult == nullptr)
1821 5789610 : return pszDefault;
1822 :
1823 7733 : return pszResult;
1824 : }
1825 :
1826 : /************************************************************************/
1827 : /* CPLSubscribeToSetConfigOption() */
1828 : /************************************************************************/
1829 :
1830 : /**
1831 : * Install a callback that will be notified of calls to CPLSetConfigOption()/
1832 : * CPLSetThreadLocalConfigOption()
1833 : *
1834 : * @param pfnCallback Callback. Must not be NULL
1835 : * @param pUserData Callback user data. May be NULL.
1836 : * @return subscriber ID that can be used with CPLUnsubscribeToSetConfigOption()
1837 : * @since GDAL 3.7
1838 : */
1839 :
1840 1189 : int CPLSubscribeToSetConfigOption(CPLSetConfigOptionSubscriber pfnCallback,
1841 : void *pUserData)
1842 : {
1843 2378 : CPLMutexHolderD(&hConfigMutex);
1844 1194 : for (int nId = 0;
1845 1194 : nId < static_cast<int>(gSetConfigOptionSubscribers.size()); ++nId)
1846 : {
1847 6 : if (!gSetConfigOptionSubscribers[nId].first)
1848 : {
1849 1 : gSetConfigOptionSubscribers[nId].first = pfnCallback;
1850 1 : gSetConfigOptionSubscribers[nId].second = pUserData;
1851 1 : return nId;
1852 : }
1853 : }
1854 1188 : int nId = static_cast<int>(gSetConfigOptionSubscribers.size());
1855 1188 : gSetConfigOptionSubscribers.push_back(
1856 1188 : std::pair<CPLSetConfigOptionSubscriber, void *>(pfnCallback,
1857 : pUserData));
1858 1188 : return nId;
1859 : }
1860 :
1861 : /************************************************************************/
1862 : /* CPLUnsubscribeToSetConfigOption() */
1863 : /************************************************************************/
1864 :
1865 : /**
1866 : * Remove a subscriber installed with CPLSubscribeToSetConfigOption()
1867 : *
1868 : * @param nId Subscriber id returned by CPLSubscribeToSetConfigOption()
1869 : * @since GDAL 3.7
1870 : */
1871 :
1872 4 : void CPLUnsubscribeToSetConfigOption(int nId)
1873 : {
1874 8 : CPLMutexHolderD(&hConfigMutex);
1875 4 : if (nId == static_cast<int>(gSetConfigOptionSubscribers.size()) - 1)
1876 : {
1877 3 : gSetConfigOptionSubscribers.resize(gSetConfigOptionSubscribers.size() -
1878 : 1);
1879 : }
1880 2 : else if (nId >= 0 &&
1881 1 : nId < static_cast<int>(gSetConfigOptionSubscribers.size()))
1882 : {
1883 1 : gSetConfigOptionSubscribers[nId].first = nullptr;
1884 : }
1885 4 : }
1886 :
1887 : /************************************************************************/
1888 : /* NotifyOtherComponentsConfigOptionChanged() */
1889 : /************************************************************************/
1890 :
1891 67455 : static void NotifyOtherComponentsConfigOptionChanged(const char *pszKey,
1892 : const char *pszValue,
1893 : bool bThreadLocal)
1894 : {
1895 : // When changing authentication parameters of virtual file systems,
1896 : // partially invalidate cached state about file availability.
1897 67455 : if (STARTS_WITH_CI(pszKey, "AWS_") || STARTS_WITH_CI(pszKey, "GS_") ||
1898 64797 : STARTS_WITH_CI(pszKey, "GOOGLE_") ||
1899 64757 : STARTS_WITH_CI(pszKey, "GDAL_HTTP_HEADER_FILE") ||
1900 64743 : STARTS_WITH_CI(pszKey, "AZURE_") ||
1901 64587 : (STARTS_WITH_CI(pszKey, "SWIFT_") && !EQUAL(pszKey, "SWIFT_MAX_KEYS")))
1902 : {
1903 2972 : VSICurlAuthParametersChanged();
1904 : }
1905 :
1906 67462 : if (!gSetConfigOptionSubscribers.empty())
1907 : {
1908 133871 : for (const auto &iter : gSetConfigOptionSubscribers)
1909 : {
1910 66940 : if (iter.first)
1911 67005 : iter.first(pszKey, pszValue, bThreadLocal, iter.second);
1912 : }
1913 : }
1914 67356 : }
1915 :
1916 : /************************************************************************/
1917 : /* CPLIsDebugEnabled() */
1918 : /************************************************************************/
1919 :
1920 : static int gnDebug = -1;
1921 :
1922 : /** Returns whether CPL_DEBUG is enabled.
1923 : *
1924 : * @since 3.11
1925 : */
1926 72929 : bool CPLIsDebugEnabled()
1927 : {
1928 72929 : if (gnDebug < 0)
1929 : {
1930 : // Check that apszKnownConfigOptions is correctly sorted with
1931 : // STRCASECMP() criterion.
1932 441145 : for (size_t i = 1; i < CPL_ARRAYSIZE(apszKnownConfigOptions); ++i)
1933 : {
1934 440730 : if (STRCASECMP(apszKnownConfigOptions[i - 1],
1935 : apszKnownConfigOptions[i]) >= 0)
1936 : {
1937 0 : CPLError(CE_Failure, CPLE_AppDefined,
1938 : "ERROR: apszKnownConfigOptions[] isn't correctly "
1939 : "sorted: %s >= %s",
1940 0 : apszKnownConfigOptions[i - 1],
1941 0 : apszKnownConfigOptions[i]);
1942 : }
1943 : }
1944 415 : gnDebug = CPLTestBool(CPLGetConfigOption("CPL_DEBUG", "OFF"));
1945 : }
1946 :
1947 72898 : return gnDebug != 0;
1948 : }
1949 :
1950 : /************************************************************************/
1951 : /* CPLDeclareKnownConfigOption() */
1952 : /************************************************************************/
1953 :
1954 : static std::mutex goMutexDeclaredKnownConfigOptions;
1955 : static std::set<CPLString> goSetKnownConfigOptions;
1956 :
1957 : /** Declare that the specified configuration option is known.
1958 : *
1959 : * This is useful to avoid a warning to be emitted on unknown configuration
1960 : * options when CPL_DEBUG is enabled.
1961 : *
1962 : * @param pszKey Name of the configuration option to declare.
1963 : * @param pszDefinition Unused for now. Must be set to nullptr.
1964 : * @since 3.11
1965 : */
1966 1 : void CPLDeclareKnownConfigOption(const char *pszKey,
1967 : [[maybe_unused]] const char *pszDefinition)
1968 : {
1969 1 : std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
1970 1 : goSetKnownConfigOptions.insert(CPLString(pszKey).toupper());
1971 1 : }
1972 :
1973 : /************************************************************************/
1974 : /* CPLGetKnownConfigOptions() */
1975 : /************************************************************************/
1976 :
1977 : /** Return the list of known configuration options.
1978 : *
1979 : * Must be freed with CSLDestroy().
1980 : * @since 3.11
1981 : */
1982 3 : char **CPLGetKnownConfigOptions()
1983 : {
1984 6 : std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
1985 6 : CPLStringList aosList;
1986 3192 : for (const char *pszKey : apszKnownConfigOptions)
1987 3189 : aosList.AddString(pszKey);
1988 4 : for (const auto &osKey : goSetKnownConfigOptions)
1989 1 : aosList.AddString(osKey);
1990 6 : return aosList.StealList();
1991 : }
1992 :
1993 : /************************************************************************/
1994 : /* CPLSetConfigOptionDetectUnknownConfigOption() */
1995 : /************************************************************************/
1996 :
1997 67508 : static void CPLSetConfigOptionDetectUnknownConfigOption(const char *pszKey,
1998 : const char *pszValue)
1999 : {
2000 67508 : if (EQUAL(pszKey, "CPL_DEBUG"))
2001 : {
2002 98 : gnDebug = pszValue ? CPLTestBool(pszValue) : false;
2003 : }
2004 67410 : else if (CPLIsDebugEnabled())
2005 : {
2006 72 : if (!std::binary_search(std::begin(apszKnownConfigOptions),
2007 : std::end(apszKnownConfigOptions), pszKey,
2008 794 : [](const char *a, const char *b)
2009 794 : { return STRCASECMP(a, b) < 0; }))
2010 : {
2011 : bool bFound;
2012 : {
2013 4 : std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
2014 8 : bFound = cpl::contains(goSetKnownConfigOptions,
2015 4 : CPLString(pszKey).toupper());
2016 : }
2017 4 : if (!bFound)
2018 : {
2019 2 : const char *pszOldValue = CPLGetConfigOption(pszKey, nullptr);
2020 2 : if (!((!pszValue && !pszOldValue) ||
2021 1 : (pszValue && pszOldValue &&
2022 0 : EQUAL(pszValue, pszOldValue))))
2023 : {
2024 2 : CPLError(CE_Warning, CPLE_AppDefined,
2025 : "Unknown configuration option '%s'.", pszKey);
2026 : }
2027 : }
2028 : }
2029 : }
2030 67478 : }
2031 :
2032 : /************************************************************************/
2033 : /* CPLSetConfigOption() */
2034 : /************************************************************************/
2035 :
2036 : /**
2037 : * Set a configuration option for GDAL/OGR use.
2038 : *
2039 : * Those options are defined as a (key, value) couple. The value corresponding
2040 : * to a key can be got later with the CPLGetConfigOption() method.
2041 : *
2042 : * This mechanism is similar to environment variables, but options set with
2043 : * CPLSetConfigOption() overrides, for CPLGetConfigOption() point of view,
2044 : * values defined in the environment.
2045 : *
2046 : * If CPLSetConfigOption() is called several times with the same key, the
2047 : * value provided during the last call will be used.
2048 : *
2049 : * Options can also be passed on the command line of most GDAL utilities
2050 : * with '\--config KEY VALUE' (or '\--config KEY=VALUE' since GDAL 3.10).
2051 : * For example, ogrinfo \--config CPL_DEBUG ON ~/data/test/point.shp
2052 : *
2053 : * This function can also be used to clear a setting by passing NULL as the
2054 : * value (note: passing NULL will not unset an existing environment variable;
2055 : * it will just unset a value previously set by CPLSetConfigOption()).
2056 : *
2057 : * Starting with GDAL 3.11, if CPL_DEBUG is enabled prior to this call, and
2058 : * CPLSetConfigOption() is called with a key that is neither a known
2059 : * configuration option of GDAL itself, or one that has been declared with
2060 : * CPLDeclareKnownConfigOption(), a warning will be emitted.
2061 : *
2062 : * @param pszKey the key of the option
2063 : * @param pszValue the value of the option, or NULL to clear a setting.
2064 : *
2065 : * @see https://gdal.org/user/configoptions.html
2066 : */
2067 4697 : void CPL_STDCALL CPLSetConfigOption(const char *pszKey, const char *pszValue)
2068 :
2069 : {
2070 : #ifdef DEBUG_CONFIG_OPTIONS
2071 : CPLAccessConfigOption(pszKey, FALSE);
2072 : #endif
2073 9394 : CPLMutexHolderD(&hConfigMutex);
2074 :
2075 : #ifdef OGRAPISPY_ENABLED
2076 4697 : OGRAPISPYCPLSetConfigOption(pszKey, pszValue);
2077 : #endif
2078 :
2079 4697 : CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
2080 :
2081 4697 : g_papszConfigOptions = const_cast<volatile char **>(CSLSetNameValue(
2082 : const_cast<char **>(g_papszConfigOptions), pszKey, pszValue));
2083 :
2084 4697 : NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
2085 : /*bTheadLocal=*/false);
2086 4697 : }
2087 :
2088 : /************************************************************************/
2089 : /* CPLSetThreadLocalTLSFreeFunc() */
2090 : /************************************************************************/
2091 :
2092 : /* non-stdcall wrapper function for CSLDestroy() (#5590) */
2093 101 : static void CPLSetThreadLocalTLSFreeFunc(void *pData)
2094 : {
2095 101 : CSLDestroy(reinterpret_cast<char **>(pData));
2096 101 : }
2097 :
2098 : /************************************************************************/
2099 : /* CPLSetThreadLocalConfigOption() */
2100 : /************************************************************************/
2101 :
2102 : /**
2103 : * Set a configuration option for GDAL/OGR use.
2104 : *
2105 : * Those options are defined as a (key, value) couple. The value corresponding
2106 : * to a key can be got later with the CPLGetConfigOption() method.
2107 : *
2108 : * This function sets the configuration option that only applies in the
2109 : * current thread, as opposed to CPLSetConfigOption() which sets an option
2110 : * that applies on all threads. CPLSetThreadLocalConfigOption() will override
2111 : * the effect of CPLSetConfigOption) for the current thread.
2112 : *
2113 : * This function can also be used to clear a setting by passing NULL as the
2114 : * value (note: passing NULL will not unset an existing environment variable or
2115 : * a value set through CPLSetConfigOption();
2116 : * it will just unset a value previously set by
2117 : * CPLSetThreadLocalConfigOption()).
2118 : *
2119 : * @param pszKey the key of the option
2120 : * @param pszValue the value of the option, or NULL to clear a setting.
2121 : */
2122 :
2123 62820 : void CPL_STDCALL CPLSetThreadLocalConfigOption(const char *pszKey,
2124 : const char *pszValue)
2125 :
2126 : {
2127 : #ifdef DEBUG_CONFIG_OPTIONS
2128 : CPLAccessConfigOption(pszKey, FALSE);
2129 : #endif
2130 :
2131 : #ifdef OGRAPISPY_ENABLED
2132 62820 : OGRAPISPYCPLSetThreadLocalConfigOption(pszKey, pszValue);
2133 : #endif
2134 :
2135 62814 : int bMemoryError = FALSE;
2136 : char **papszTLConfigOptions = reinterpret_cast<char **>(
2137 62814 : CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
2138 62799 : if (bMemoryError)
2139 0 : return;
2140 :
2141 62799 : CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
2142 :
2143 : papszTLConfigOptions =
2144 62783 : CSLSetNameValue(papszTLConfigOptions, pszKey, pszValue);
2145 :
2146 62761 : CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
2147 : CPLSetThreadLocalTLSFreeFunc);
2148 :
2149 62708 : NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
2150 : /*bTheadLocal=*/true);
2151 : }
2152 :
2153 : /************************************************************************/
2154 : /* CPLGetThreadLocalConfigOptions() */
2155 : /************************************************************************/
2156 :
2157 : /**
2158 : * Return the list of thread local configuration options as KEY=VALUE pairs.
2159 : *
2160 : * Options that through environment variables or with
2161 : * CPLSetConfigOption() will *not* be listed.
2162 : *
2163 : * @return a copy of the list, to be freed with CSLDestroy().
2164 : * @since GDAL 2.2
2165 : */
2166 740885 : char **CPLGetThreadLocalConfigOptions(void)
2167 : {
2168 740885 : int bMemoryError = FALSE;
2169 : char **papszTLConfigOptions = reinterpret_cast<char **>(
2170 740885 : CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
2171 712132 : if (bMemoryError)
2172 0 : return nullptr;
2173 712132 : return CSLDuplicate(papszTLConfigOptions);
2174 : }
2175 :
2176 : /************************************************************************/
2177 : /* CPLSetThreadLocalConfigOptions() */
2178 : /************************************************************************/
2179 :
2180 : /**
2181 : * Replace the full list of thread local configuration options with the
2182 : * passed list of KEY=VALUE pairs.
2183 : *
2184 : * This has the same effect of clearing the existing list, and setting
2185 : * individually each pair with the CPLSetThreadLocalConfigOption() API.
2186 : *
2187 : * This does not affect options set through environment variables or with
2188 : * CPLSetConfigOption().
2189 : *
2190 : * The passed list is copied by the function.
2191 : *
2192 : * @param papszConfigOptions the new list (or NULL).
2193 : *
2194 : * @since GDAL 2.2
2195 : */
2196 1428480 : void CPLSetThreadLocalConfigOptions(const char *const *papszConfigOptions)
2197 : {
2198 1428480 : int bMemoryError = FALSE;
2199 : char **papszTLConfigOptions = reinterpret_cast<char **>(
2200 1428480 : CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
2201 1424580 : if (bMemoryError)
2202 0 : return;
2203 1424580 : CSLDestroy(papszTLConfigOptions);
2204 : papszTLConfigOptions =
2205 1422390 : CSLDuplicate(const_cast<char **>(papszConfigOptions));
2206 1420740 : CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
2207 : CPLSetThreadLocalTLSFreeFunc);
2208 : }
2209 :
2210 : /************************************************************************/
2211 : /* CPLFreeConfig() */
2212 : /************************************************************************/
2213 :
2214 1537 : void CPL_STDCALL CPLFreeConfig()
2215 :
2216 : {
2217 : {
2218 3074 : CPLMutexHolderD(&hConfigMutex);
2219 :
2220 1537 : CSLDestroy(const_cast<char **>(g_papszConfigOptions));
2221 1537 : g_papszConfigOptions = nullptr;
2222 :
2223 1537 : int bMemoryError = FALSE;
2224 : char **papszTLConfigOptions = reinterpret_cast<char **>(
2225 1537 : CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
2226 1537 : if (papszTLConfigOptions != nullptr)
2227 : {
2228 203 : CSLDestroy(papszTLConfigOptions);
2229 203 : CPLSetTLS(CTLS_CONFIGOPTIONS, nullptr, FALSE);
2230 : }
2231 : }
2232 1537 : CPLDestroyMutex(hConfigMutex);
2233 1537 : hConfigMutex = nullptr;
2234 1537 : }
2235 :
2236 : /************************************************************************/
2237 : /* CPLLoadConfigOptionsFromFile() */
2238 : /************************************************************************/
2239 :
2240 : /** Load configuration from a given configuration file.
2241 :
2242 : A configuration file is a text file in a .ini style format, that lists
2243 : configuration options and their values.
2244 : Lines starting with # are comment lines.
2245 :
2246 : Example:
2247 : \verbatim
2248 : [configoptions]
2249 : # set BAR as the value of configuration option FOO
2250 : FOO=BAR
2251 : \endverbatim
2252 :
2253 : Starting with GDAL 3.5, a configuration file can also contain credentials
2254 : (or more generally options related to a virtual file system) for a given path
2255 : prefix, that can also be set with VSISetPathSpecificOption(). Credentials should
2256 : be put under a [credentials] section, and for each path prefix, under a relative
2257 : subsection whose name starts with "[." (e.g. "[.some_arbitrary_name]"), and
2258 : whose first key is "path".
2259 :
2260 : Example:
2261 : \verbatim
2262 : [credentials]
2263 :
2264 : [.private_bucket]
2265 : path=/vsis3/my_private_bucket
2266 : AWS_SECRET_ACCESS_KEY=...
2267 : AWS_ACCESS_KEY_ID=...
2268 :
2269 : [.sentinel_s2_l1c]
2270 : path=/vsis3/sentinel-s2-l1c
2271 : AWS_REQUEST_PAYER=requester
2272 : \endverbatim
2273 :
2274 : Starting with GDAL 3.6, a leading [directives] section might be added with
2275 : a "ignore-env-vars=yes" setting to indicate that, starting with that point,
2276 : all environment variables should be ignored, and only configuration options
2277 : defined in the [configoptions] sections or through the CPLSetConfigOption() /
2278 : CPLSetThreadLocalConfigOption() functions should be taken into account.
2279 :
2280 : This function is typically called by CPLLoadConfigOptionsFromPredefinedFiles()
2281 :
2282 : @param pszFilename File where to load configuration from.
2283 : @param bOverrideEnvVars Whether configuration options from the configuration
2284 : file should override environment variables.
2285 : @since GDAL 3.3
2286 : */
2287 3220 : void CPLLoadConfigOptionsFromFile(const char *pszFilename, int bOverrideEnvVars)
2288 : {
2289 3220 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
2290 3220 : if (fp == nullptr)
2291 3211 : return;
2292 9 : CPLDebug("CPL", "Loading configuration from %s", pszFilename);
2293 : const char *pszLine;
2294 : enum class Section
2295 : {
2296 : NONE,
2297 : GENERAL,
2298 : CONFIG_OPTIONS,
2299 : CREDENTIALS,
2300 : };
2301 9 : Section eCurrentSection = Section::NONE;
2302 9 : bool bInSubsection = false;
2303 18 : std::string osPath;
2304 9 : int nSectionCounter = 0;
2305 :
2306 56 : const auto IsSpaceOnly = [](const char *pszStr)
2307 : {
2308 56 : for (; *pszStr; ++pszStr)
2309 : {
2310 47 : if (!isspace(static_cast<unsigned char>(*pszStr)))
2311 41 : return false;
2312 : }
2313 9 : return true;
2314 : };
2315 :
2316 59 : while ((pszLine = CPLReadLine2L(fp, -1, nullptr)) != nullptr)
2317 : {
2318 50 : if (IsSpaceOnly(pszLine))
2319 : {
2320 : // Blank line
2321 : }
2322 41 : else if (pszLine[0] == '#')
2323 : {
2324 : // Comment line
2325 : }
2326 35 : else if (strcmp(pszLine, "[configoptions]") == 0)
2327 : {
2328 6 : nSectionCounter++;
2329 6 : eCurrentSection = Section::CONFIG_OPTIONS;
2330 : }
2331 29 : else if (strcmp(pszLine, "[credentials]") == 0)
2332 : {
2333 4 : nSectionCounter++;
2334 4 : eCurrentSection = Section::CREDENTIALS;
2335 4 : bInSubsection = false;
2336 4 : osPath.clear();
2337 : }
2338 25 : else if (strcmp(pszLine, "[directives]") == 0)
2339 : {
2340 2 : nSectionCounter++;
2341 2 : if (nSectionCounter != 1)
2342 : {
2343 0 : CPLError(CE_Warning, CPLE_AppDefined,
2344 : "The [directives] section should be the first one in "
2345 : "the file, otherwise some its settings might not be "
2346 : "used correctly.");
2347 : }
2348 2 : eCurrentSection = Section::GENERAL;
2349 : }
2350 23 : else if (eCurrentSection == Section::GENERAL)
2351 : {
2352 2 : char *pszKey = nullptr;
2353 2 : const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
2354 2 : if (pszKey && pszValue)
2355 : {
2356 2 : if (strcmp(pszKey, "ignore-env-vars") == 0)
2357 : {
2358 2 : gbIgnoreEnvVariables = CPLTestBool(pszValue);
2359 : }
2360 : else
2361 : {
2362 0 : CPLError(CE_Warning, CPLE_AppDefined,
2363 : "Ignoring %s line in [directives] section",
2364 : pszLine);
2365 : }
2366 : }
2367 2 : CPLFree(pszKey);
2368 : }
2369 21 : else if (eCurrentSection == Section::CREDENTIALS)
2370 : {
2371 15 : if (strncmp(pszLine, "[.", 2) == 0)
2372 : {
2373 4 : bInSubsection = true;
2374 4 : osPath.clear();
2375 : }
2376 11 : else if (bInSubsection)
2377 : {
2378 10 : char *pszKey = nullptr;
2379 10 : const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
2380 10 : if (pszKey && pszValue)
2381 : {
2382 10 : if (strcmp(pszKey, "path") == 0)
2383 : {
2384 4 : if (!osPath.empty())
2385 : {
2386 1 : CPLError(
2387 : CE_Warning, CPLE_AppDefined,
2388 : "Duplicated 'path' key in the same subsection. "
2389 : "Ignoring %s=%s",
2390 : pszKey, pszValue);
2391 : }
2392 : else
2393 : {
2394 3 : osPath = pszValue;
2395 : }
2396 : }
2397 6 : else if (osPath.empty())
2398 : {
2399 1 : CPLError(CE_Warning, CPLE_AppDefined,
2400 : "First entry in a credentials subsection "
2401 : "should be 'path'.");
2402 : }
2403 : else
2404 : {
2405 5 : VSISetPathSpecificOption(osPath.c_str(), pszKey,
2406 : pszValue);
2407 : }
2408 : }
2409 10 : CPLFree(pszKey);
2410 : }
2411 1 : else if (pszLine[0] == '[')
2412 : {
2413 0 : eCurrentSection = Section::NONE;
2414 : }
2415 : else
2416 : {
2417 1 : CPLError(CE_Warning, CPLE_AppDefined,
2418 : "Ignoring content in [credential] section that is not "
2419 : "in a [.xxxxx] subsection");
2420 : }
2421 : }
2422 6 : else if (pszLine[0] == '[')
2423 : {
2424 0 : eCurrentSection = Section::NONE;
2425 : }
2426 6 : else if (eCurrentSection == Section::CONFIG_OPTIONS)
2427 : {
2428 6 : char *pszKey = nullptr;
2429 6 : const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
2430 6 : if (pszKey && pszValue)
2431 : {
2432 11 : if (bOverrideEnvVars || gbIgnoreEnvVariables ||
2433 5 : getenv(pszKey) == nullptr)
2434 : {
2435 5 : CPLDebugOnly("CPL", "Setting configuration option %s=%s",
2436 : pszKey, pszValue);
2437 5 : CPLSetConfigOption(pszKey, pszValue);
2438 : }
2439 : else
2440 : {
2441 1 : CPLDebug("CPL",
2442 : "Ignoring configuration option %s=%s from "
2443 : "configuration file as it is already set "
2444 : "as an environment variable",
2445 : pszKey, pszValue);
2446 : }
2447 : }
2448 6 : CPLFree(pszKey);
2449 : }
2450 : }
2451 9 : VSIFCloseL(fp);
2452 : }
2453 :
2454 : /************************************************************************/
2455 : /* CPLLoadConfigOptionsFromPredefinedFiles() */
2456 : /************************************************************************/
2457 :
2458 : /** Load configuration from a set of predefined files.
2459 : *
2460 : * If the environment variable (or configuration option) GDAL_CONFIG_FILE is
2461 : * set, then CPLLoadConfigOptionsFromFile() will be called with the value of
2462 : * this configuration option as the file location.
2463 : *
2464 : * Otherwise, for Unix builds, CPLLoadConfigOptionsFromFile() will be called
2465 : * with ${sysconfdir}/gdal/gdalrc first where ${sysconfdir} evaluates
2466 : * to ${prefix}/etc, unless the \--sysconfdir switch of configure has been
2467 : * invoked.
2468 : *
2469 : * Then CPLLoadConfigOptionsFromFile() will be called with ${HOME}/.gdal/gdalrc
2470 : * on Unix builds (potentially overriding what was loaded with the sysconfdir)
2471 : * or ${USERPROFILE}/.gdal/gdalrc on Windows builds.
2472 : *
2473 : * CPLLoadConfigOptionsFromFile() will be called with bOverrideEnvVars = false,
2474 : * that is the value of environment variables previously set will be used
2475 : * instead of the value set in the configuration files (unless the configuration
2476 : * file contains a leading [directives] section with a "ignore-env-vars=yes"
2477 : * setting).
2478 : *
2479 : * This function is automatically called by GDALDriverManager() constructor
2480 : *
2481 : * @since GDAL 3.3
2482 : */
2483 1607 : void CPLLoadConfigOptionsFromPredefinedFiles()
2484 : {
2485 1607 : const char *pszFile = CPLGetConfigOption("GDAL_CONFIG_FILE", nullptr);
2486 1607 : if (pszFile != nullptr)
2487 : {
2488 2 : CPLLoadConfigOptionsFromFile(pszFile, false);
2489 : }
2490 : else
2491 : {
2492 : #ifdef SYSCONFDIR
2493 1605 : CPLLoadConfigOptionsFromFile(
2494 3210 : CPLFormFilenameSafe(
2495 3210 : CPLFormFilenameSafe(SYSCONFDIR, "gdal", nullptr).c_str(),
2496 : "gdalrc", nullptr)
2497 : .c_str(),
2498 : false);
2499 : #endif
2500 :
2501 : #ifdef _WIN32
2502 : const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
2503 : #else
2504 1605 : const char *pszHome = CPLGetConfigOption("HOME", nullptr);
2505 : #endif
2506 1605 : if (pszHome != nullptr)
2507 : {
2508 1605 : CPLLoadConfigOptionsFromFile(
2509 3210 : CPLFormFilenameSafe(
2510 3210 : CPLFormFilenameSafe(pszHome, ".gdal", nullptr).c_str(),
2511 : "gdalrc", nullptr)
2512 : .c_str(),
2513 : false);
2514 : }
2515 : }
2516 1607 : }
2517 :
2518 : /************************************************************************/
2519 : /* CPLStat() */
2520 : /************************************************************************/
2521 :
2522 : /** Same as VSIStat() except it works on "C:" as if it were "C:\". */
2523 :
2524 0 : int CPLStat(const char *pszPath, VSIStatBuf *psStatBuf)
2525 :
2526 : {
2527 0 : if (strlen(pszPath) == 2 && pszPath[1] == ':')
2528 : {
2529 0 : char szAltPath[4] = {pszPath[0], pszPath[1], '\\', '\0'};
2530 0 : return VSIStat(szAltPath, psStatBuf);
2531 : }
2532 :
2533 0 : return VSIStat(pszPath, psStatBuf);
2534 : }
2535 :
2536 : /************************************************************************/
2537 : /* proj_strtod() */
2538 : /************************************************************************/
2539 18 : static double proj_strtod(char *nptr, char **endptr)
2540 :
2541 : {
2542 18 : char c = '\0';
2543 18 : char *cp = nptr;
2544 :
2545 : // Scan for characters which cause problems with VC++ strtod().
2546 84 : while ((c = *cp) != '\0')
2547 : {
2548 72 : if (c == 'd' || c == 'D')
2549 : {
2550 : // Found one, so NUL it out, call strtod(),
2551 : // then restore it and return.
2552 6 : *cp = '\0';
2553 6 : const double result = CPLStrtod(nptr, endptr);
2554 6 : *cp = c;
2555 6 : return result;
2556 : }
2557 66 : ++cp;
2558 : }
2559 :
2560 : // No offending characters, just handle normally.
2561 :
2562 12 : return CPLStrtod(nptr, endptr);
2563 : }
2564 :
2565 : /************************************************************************/
2566 : /* CPLDMSToDec() */
2567 : /************************************************************************/
2568 :
2569 : static const char *sym = "NnEeSsWw";
2570 : constexpr double vm[] = {1.0, 0.0166666666667, 0.00027777778};
2571 :
2572 : /** CPLDMSToDec */
2573 6 : double CPLDMSToDec(const char *is)
2574 :
2575 : {
2576 : // Copy string into work space.
2577 6 : while (isspace(static_cast<unsigned char>(*is)))
2578 0 : ++is;
2579 :
2580 6 : const char *p = is;
2581 6 : char work[64] = {};
2582 6 : char *s = work;
2583 6 : int n = sizeof(work);
2584 60 : for (; isgraph(*p) && --n;)
2585 54 : *s++ = *p++;
2586 6 : *s = '\0';
2587 : // It is possible that a really odd input (like lots of leading
2588 : // zeros) could be truncated in copying into work. But...
2589 6 : s = work;
2590 6 : int sign = *s;
2591 :
2592 6 : if (sign == '+' || sign == '-')
2593 0 : s++;
2594 : else
2595 6 : sign = '+';
2596 :
2597 6 : int nl = 0;
2598 6 : double v = 0.0;
2599 24 : for (; nl < 3; nl = n + 1)
2600 : {
2601 18 : if (!(isdigit(static_cast<unsigned char>(*s)) || *s == '.'))
2602 0 : break;
2603 18 : const double tv = proj_strtod(s, &s);
2604 18 : if (tv == HUGE_VAL)
2605 0 : return tv;
2606 18 : switch (*s)
2607 : {
2608 6 : case 'D':
2609 : case 'd':
2610 6 : n = 0;
2611 6 : break;
2612 6 : case '\'':
2613 6 : n = 1;
2614 6 : break;
2615 6 : case '"':
2616 6 : n = 2;
2617 6 : break;
2618 0 : case 'r':
2619 : case 'R':
2620 0 : if (nl)
2621 : {
2622 0 : return 0.0;
2623 : }
2624 0 : ++s;
2625 0 : v = tv;
2626 0 : goto skip;
2627 0 : default:
2628 0 : v += tv * vm[nl];
2629 0 : skip:
2630 0 : n = 4;
2631 0 : continue;
2632 : }
2633 18 : if (n < nl)
2634 : {
2635 0 : return 0.0;
2636 : }
2637 18 : v += tv * vm[n];
2638 18 : ++s;
2639 : }
2640 : // Postfix sign.
2641 6 : if (*s && ((p = strchr(sym, *s))) != nullptr)
2642 : {
2643 0 : sign = (p - sym) >= 4 ? '-' : '+';
2644 0 : ++s;
2645 : }
2646 6 : if (sign == '-')
2647 0 : v = -v;
2648 :
2649 6 : return v;
2650 : }
2651 :
2652 : /************************************************************************/
2653 : /* CPLDecToDMS() */
2654 : /************************************************************************/
2655 :
2656 : /** Translate a decimal degrees value to a DMS string with hemisphere. */
2657 :
2658 550 : const char *CPLDecToDMS(double dfAngle, const char *pszAxis, int nPrecision)
2659 :
2660 : {
2661 550 : VALIDATE_POINTER1(pszAxis, "CPLDecToDMS", "");
2662 :
2663 550 : if (std::isnan(dfAngle))
2664 0 : return "Invalid angle";
2665 :
2666 550 : const double dfEpsilon = (0.5 / 3600.0) * pow(0.1, nPrecision);
2667 550 : const double dfABSAngle = std::abs(dfAngle) + dfEpsilon;
2668 550 : if (dfABSAngle > 361.0)
2669 : {
2670 0 : return "Invalid angle";
2671 : }
2672 :
2673 550 : const int nDegrees = static_cast<int>(dfABSAngle);
2674 550 : const int nMinutes = static_cast<int>((dfABSAngle - nDegrees) * 60);
2675 550 : double dfSeconds = dfABSAngle * 3600 - nDegrees * 3600 - nMinutes * 60;
2676 :
2677 550 : if (dfSeconds > dfEpsilon * 3600.0)
2678 544 : dfSeconds -= dfEpsilon * 3600.0;
2679 :
2680 550 : const char *pszHemisphere = nullptr;
2681 550 : if (EQUAL(pszAxis, "Long") && dfAngle < 0.0)
2682 238 : pszHemisphere = "W";
2683 312 : else if (EQUAL(pszAxis, "Long"))
2684 37 : pszHemisphere = "E";
2685 275 : else if (dfAngle < 0.0)
2686 22 : pszHemisphere = "S";
2687 : else
2688 253 : pszHemisphere = "N";
2689 :
2690 550 : char szFormat[30] = {};
2691 550 : CPLsnprintf(szFormat, sizeof(szFormat), "%%3dd%%2d\'%%%d.%df\"%s",
2692 : nPrecision + 3, nPrecision, pszHemisphere);
2693 :
2694 : static CPL_THREADLOCAL char szBuffer[50] = {};
2695 550 : CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, nDegrees, nMinutes,
2696 : dfSeconds);
2697 :
2698 550 : return szBuffer;
2699 : }
2700 :
2701 : /************************************************************************/
2702 : /* CPLPackedDMSToDec() */
2703 : /************************************************************************/
2704 :
2705 : /**
2706 : * Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
2707 : *
2708 : * This function converts a packed DMS angle to seconds. The standard
2709 : * packed DMS format is:
2710 : *
2711 : * degrees * 1000000 + minutes * 1000 + seconds
2712 : *
2713 : * Example: angle = 120025045.25 yields
2714 : * deg = 120
2715 : * min = 25
2716 : * sec = 45.25
2717 : *
2718 : * The algorithm used for the conversion is as follows:
2719 : *
2720 : * 1. The absolute value of the angle is used.
2721 : *
2722 : * 2. The degrees are separated out:
2723 : * deg = angle/1000000 (fractional portion truncated)
2724 : *
2725 : * 3. The minutes are separated out:
2726 : * min = (angle - deg * 1000000) / 1000 (fractional portion truncated)
2727 : *
2728 : * 4. The seconds are then computed:
2729 : * sec = angle - deg * 1000000 - min * 1000
2730 : *
2731 : * 5. The total angle in seconds is computed:
2732 : * sec = deg * 3600.0 + min * 60.0 + sec
2733 : *
2734 : * 6. The sign of sec is set to that of the input angle.
2735 : *
2736 : * Packed DMS values used by the USGS GCTP package and probably by other
2737 : * software.
2738 : *
2739 : * NOTE: This code does not validate input value. If you give the wrong
2740 : * value, you will get the wrong result.
2741 : *
2742 : * @param dfPacked Angle in packed DMS format.
2743 : *
2744 : * @return Angle in decimal degrees.
2745 : *
2746 : */
2747 :
2748 34 : double CPLPackedDMSToDec(double dfPacked)
2749 : {
2750 34 : const double dfSign = dfPacked < 0.0 ? -1 : 1;
2751 :
2752 34 : double dfSeconds = std::abs(dfPacked);
2753 34 : double dfDegrees = floor(dfSeconds / 1000000.0);
2754 34 : dfSeconds -= dfDegrees * 1000000.0;
2755 34 : const double dfMinutes = floor(dfSeconds / 1000.0);
2756 34 : dfSeconds -= dfMinutes * 1000.0;
2757 34 : dfSeconds = dfSign * (dfDegrees * 3600.0 + dfMinutes * 60.0 + dfSeconds);
2758 34 : dfDegrees = dfSeconds / 3600.0;
2759 :
2760 34 : return dfDegrees;
2761 : }
2762 :
2763 : /************************************************************************/
2764 : /* CPLDecToPackedDMS() */
2765 : /************************************************************************/
2766 : /**
2767 : * Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
2768 : *
2769 : * This function converts a value, specified in decimal degrees into
2770 : * packed DMS angle. The standard packed DMS format is:
2771 : *
2772 : * degrees * 1000000 + minutes * 1000 + seconds
2773 : *
2774 : * See also CPLPackedDMSToDec().
2775 : *
2776 : * @param dfDec Angle in decimal degrees.
2777 : *
2778 : * @return Angle in packed DMS format.
2779 : *
2780 : */
2781 :
2782 8 : double CPLDecToPackedDMS(double dfDec)
2783 : {
2784 8 : const double dfSign = dfDec < 0.0 ? -1 : 1;
2785 :
2786 8 : dfDec = std::abs(dfDec);
2787 8 : const double dfDegrees = floor(dfDec);
2788 8 : const double dfMinutes = floor((dfDec - dfDegrees) * 60.0);
2789 8 : const double dfSeconds = (dfDec - dfDegrees) * 3600.0 - dfMinutes * 60.0;
2790 :
2791 8 : return dfSign * (dfDegrees * 1000000.0 + dfMinutes * 1000.0 + dfSeconds);
2792 : }
2793 :
2794 : /************************************************************************/
2795 : /* CPLStringToComplex() */
2796 : /************************************************************************/
2797 :
2798 : /** Fetch the real and imaginary part of a serialized complex number */
2799 2420 : CPLErr CPL_DLL CPLStringToComplex(const char *pszString, double *pdfReal,
2800 : double *pdfImag)
2801 :
2802 : {
2803 2420 : while (*pszString == ' ')
2804 1 : pszString++;
2805 :
2806 : char *end;
2807 2419 : *pdfReal = CPLStrtod(pszString, &end);
2808 :
2809 2419 : int iPlus = -1;
2810 2419 : int iImagEnd = -1;
2811 :
2812 2419 : if (pszString == end)
2813 : {
2814 5 : goto error;
2815 : }
2816 :
2817 2414 : *pdfImag = 0.0;
2818 :
2819 2468 : for (int i = static_cast<int>(end - pszString);
2820 2468 : i < 100 && pszString[i] != '\0' && pszString[i] != ' '; i++)
2821 : {
2822 56 : if (pszString[i] == '+')
2823 : {
2824 8 : if (iPlus != -1)
2825 0 : goto error;
2826 8 : iPlus = i;
2827 : }
2828 56 : if (pszString[i] == '-')
2829 : {
2830 2 : if (iPlus != -1)
2831 1 : goto error;
2832 1 : iPlus = i;
2833 : }
2834 55 : if (pszString[i] == 'i')
2835 : {
2836 9 : if (iPlus == -1)
2837 1 : goto error;
2838 8 : iImagEnd = i;
2839 : }
2840 : }
2841 :
2842 : // If we have a "+" or "-" we must also have an "i"
2843 2412 : if ((iPlus == -1) != (iImagEnd == -1))
2844 : {
2845 1 : goto error;
2846 : }
2847 :
2848 : // Parse imaginary component, if any
2849 2411 : if (iPlus > -1)
2850 : {
2851 7 : *pdfImag = CPLStrtod(pszString + iPlus, &end);
2852 : }
2853 :
2854 : // Check everything remaining is whitespace
2855 2416 : for (; *end != '\0'; end++)
2856 : {
2857 11 : if (!isspace(*end) && end - pszString != iImagEnd)
2858 : {
2859 6 : goto error;
2860 : }
2861 : }
2862 :
2863 2405 : return CE_None;
2864 :
2865 14 : error:
2866 14 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to parse number: %s",
2867 : pszString);
2868 14 : return CE_Failure;
2869 : }
2870 :
2871 : /************************************************************************/
2872 : /* CPLOpenShared() */
2873 : /************************************************************************/
2874 :
2875 : /**
2876 : * Open a shared file handle.
2877 : *
2878 : * Some operating systems have limits on the number of file handles that can
2879 : * be open at one time. This function attempts to maintain a registry of
2880 : * already open file handles, and reuse existing ones if the same file
2881 : * is requested by another part of the application.
2882 : *
2883 : * Note that access is only shared for access types "r", "rb", "r+" and
2884 : * "rb+". All others will just result in direct VSIOpen() calls. Keep in
2885 : * mind that a file is only reused if the file name is exactly the same.
2886 : * Different names referring to the same file will result in different
2887 : * handles.
2888 : *
2889 : * The VSIFOpen() or VSIFOpenL() function is used to actually open the file,
2890 : * when an existing file handle can't be shared.
2891 : *
2892 : * @param pszFilename the name of the file to open.
2893 : * @param pszAccess the normal fopen()/VSIFOpen() style access string.
2894 : * @param bLargeIn If TRUE VSIFOpenL() (for large files) will be used instead of
2895 : * VSIFOpen().
2896 : *
2897 : * @return a file handle or NULL if opening fails.
2898 : */
2899 :
2900 35 : FILE *CPLOpenShared(const char *pszFilename, const char *pszAccess,
2901 : int bLargeIn)
2902 :
2903 : {
2904 35 : const bool bLarge = CPL_TO_BOOL(bLargeIn);
2905 70 : CPLMutexHolderD(&hSharedFileMutex);
2906 35 : const GIntBig nPID = CPLGetPID();
2907 :
2908 : /* -------------------------------------------------------------------- */
2909 : /* Is there an existing file we can use? */
2910 : /* -------------------------------------------------------------------- */
2911 35 : const bool bReuse = EQUAL(pszAccess, "rb") || EQUAL(pszAccess, "rb+");
2912 :
2913 39 : for (int i = 0; bReuse && i < nSharedFileCount; i++)
2914 : {
2915 20 : if (strcmp(pasSharedFileList[i].pszFilename, pszFilename) == 0 &&
2916 4 : !bLarge == !pasSharedFileList[i].bLarge &&
2917 16 : EQUAL(pasSharedFileList[i].pszAccess, pszAccess) &&
2918 4 : nPID == pasSharedFileListExtra[i].nPID)
2919 : {
2920 4 : pasSharedFileList[i].nRefCount++;
2921 4 : return pasSharedFileList[i].fp;
2922 : }
2923 : }
2924 :
2925 : /* -------------------------------------------------------------------- */
2926 : /* Open the file. */
2927 : /* -------------------------------------------------------------------- */
2928 : FILE *fp = bLarge
2929 31 : ? reinterpret_cast<FILE *>(VSIFOpenL(pszFilename, pszAccess))
2930 0 : : VSIFOpen(pszFilename, pszAccess);
2931 :
2932 31 : if (fp == nullptr)
2933 8 : return nullptr;
2934 :
2935 : /* -------------------------------------------------------------------- */
2936 : /* Add an entry to the list. */
2937 : /* -------------------------------------------------------------------- */
2938 23 : nSharedFileCount++;
2939 :
2940 23 : pasSharedFileList = static_cast<CPLSharedFileInfo *>(
2941 46 : CPLRealloc(const_cast<CPLSharedFileInfo *>(pasSharedFileList),
2942 23 : sizeof(CPLSharedFileInfo) * nSharedFileCount));
2943 23 : pasSharedFileListExtra = static_cast<CPLSharedFileInfoExtra *>(
2944 46 : CPLRealloc(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra),
2945 23 : sizeof(CPLSharedFileInfoExtra) * nSharedFileCount));
2946 :
2947 23 : pasSharedFileList[nSharedFileCount - 1].fp = fp;
2948 23 : pasSharedFileList[nSharedFileCount - 1].nRefCount = 1;
2949 23 : pasSharedFileList[nSharedFileCount - 1].bLarge = bLarge;
2950 46 : pasSharedFileList[nSharedFileCount - 1].pszFilename =
2951 23 : CPLStrdup(pszFilename);
2952 23 : pasSharedFileList[nSharedFileCount - 1].pszAccess = CPLStrdup(pszAccess);
2953 23 : pasSharedFileListExtra[nSharedFileCount - 1].nPID = nPID;
2954 :
2955 23 : return fp;
2956 : }
2957 :
2958 : /************************************************************************/
2959 : /* CPLCloseShared() */
2960 : /************************************************************************/
2961 :
2962 : /**
2963 : * Close shared file.
2964 : *
2965 : * Dereferences the indicated file handle, and closes it if the reference
2966 : * count has dropped to zero. A CPLError() is issued if the file is not
2967 : * in the shared file list.
2968 : *
2969 : * @param fp file handle from CPLOpenShared() to deaccess.
2970 : */
2971 :
2972 27 : void CPLCloseShared(FILE *fp)
2973 :
2974 : {
2975 27 : CPLMutexHolderD(&hSharedFileMutex);
2976 :
2977 : /* -------------------------------------------------------------------- */
2978 : /* Search for matching information. */
2979 : /* -------------------------------------------------------------------- */
2980 27 : int i = 0;
2981 29 : for (; i < nSharedFileCount && fp != pasSharedFileList[i].fp; i++)
2982 : {
2983 : }
2984 :
2985 27 : if (i == nSharedFileCount)
2986 : {
2987 0 : CPLError(CE_Failure, CPLE_AppDefined,
2988 : "Unable to find file handle %p in CPLCloseShared().", fp);
2989 0 : return;
2990 : }
2991 :
2992 : /* -------------------------------------------------------------------- */
2993 : /* Dereference and return if there are still some references. */
2994 : /* -------------------------------------------------------------------- */
2995 27 : if (--pasSharedFileList[i].nRefCount > 0)
2996 4 : return;
2997 :
2998 : /* -------------------------------------------------------------------- */
2999 : /* Close the file, and remove the information. */
3000 : /* -------------------------------------------------------------------- */
3001 23 : if (pasSharedFileList[i].bLarge)
3002 : {
3003 23 : if (VSIFCloseL(reinterpret_cast<VSILFILE *>(pasSharedFileList[i].fp)) !=
3004 : 0)
3005 : {
3006 0 : CPLError(CE_Failure, CPLE_FileIO, "Error while closing %s",
3007 0 : pasSharedFileList[i].pszFilename);
3008 : }
3009 : }
3010 : else
3011 : {
3012 0 : VSIFClose(pasSharedFileList[i].fp);
3013 : }
3014 :
3015 23 : CPLFree(pasSharedFileList[i].pszFilename);
3016 23 : CPLFree(pasSharedFileList[i].pszAccess);
3017 :
3018 23 : nSharedFileCount--;
3019 23 : memmove(
3020 23 : const_cast<CPLSharedFileInfo *>(pasSharedFileList + i),
3021 23 : const_cast<CPLSharedFileInfo *>(pasSharedFileList + nSharedFileCount),
3022 : sizeof(CPLSharedFileInfo));
3023 23 : memmove(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra + i),
3024 23 : const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra +
3025 23 : nSharedFileCount),
3026 : sizeof(CPLSharedFileInfoExtra));
3027 :
3028 23 : if (nSharedFileCount == 0)
3029 : {
3030 20 : CPLFree(const_cast<CPLSharedFileInfo *>(pasSharedFileList));
3031 20 : pasSharedFileList = nullptr;
3032 20 : CPLFree(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra));
3033 20 : pasSharedFileListExtra = nullptr;
3034 : }
3035 : }
3036 :
3037 : /************************************************************************/
3038 : /* CPLCleanupSharedFileMutex() */
3039 : /************************************************************************/
3040 :
3041 1111 : void CPLCleanupSharedFileMutex()
3042 : {
3043 1111 : if (hSharedFileMutex != nullptr)
3044 : {
3045 0 : CPLDestroyMutex(hSharedFileMutex);
3046 0 : hSharedFileMutex = nullptr;
3047 : }
3048 1111 : }
3049 :
3050 : /************************************************************************/
3051 : /* CPLGetSharedList() */
3052 : /************************************************************************/
3053 :
3054 : /**
3055 : * Fetch list of open shared files.
3056 : *
3057 : * @param pnCount place to put the count of entries.
3058 : *
3059 : * @return the pointer to the first in the array of shared file info
3060 : * structures.
3061 : */
3062 :
3063 0 : CPLSharedFileInfo *CPLGetSharedList(int *pnCount)
3064 :
3065 : {
3066 0 : if (pnCount != nullptr)
3067 0 : *pnCount = nSharedFileCount;
3068 :
3069 0 : return const_cast<CPLSharedFileInfo *>(pasSharedFileList);
3070 : }
3071 :
3072 : /************************************************************************/
3073 : /* CPLDumpSharedList() */
3074 : /************************************************************************/
3075 :
3076 : /**
3077 : * Report open shared files.
3078 : *
3079 : * Dumps all open shared files to the indicated file handle. If the
3080 : * file handle is NULL information is sent via the CPLDebug() call.
3081 : *
3082 : * @param fp File handle to write to.
3083 : */
3084 :
3085 103 : void CPLDumpSharedList(FILE *fp)
3086 :
3087 : {
3088 103 : if (nSharedFileCount > 0)
3089 : {
3090 0 : if (fp == nullptr)
3091 0 : CPLDebug("CPL", "%d Shared files open.", nSharedFileCount);
3092 : else
3093 0 : fprintf(fp, "%d Shared files open.", nSharedFileCount);
3094 : }
3095 :
3096 103 : for (int i = 0; i < nSharedFileCount; i++)
3097 : {
3098 0 : if (fp == nullptr)
3099 0 : CPLDebug("CPL", "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
3100 0 : pasSharedFileList[i].bLarge,
3101 0 : pasSharedFileList[i].pszAccess,
3102 0 : pasSharedFileList[i].pszFilename);
3103 : else
3104 0 : fprintf(fp, "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
3105 0 : pasSharedFileList[i].bLarge, pasSharedFileList[i].pszAccess,
3106 0 : pasSharedFileList[i].pszFilename);
3107 : }
3108 103 : }
3109 :
3110 : /************************************************************************/
3111 : /* CPLUnlinkTree() */
3112 : /************************************************************************/
3113 :
3114 : /** Recursively unlink a directory.
3115 : *
3116 : * @return 0 on successful completion, -1 if function fails.
3117 : */
3118 :
3119 52 : int CPLUnlinkTree(const char *pszPath)
3120 :
3121 : {
3122 : /* -------------------------------------------------------------------- */
3123 : /* First, ensure there is such a file. */
3124 : /* -------------------------------------------------------------------- */
3125 : VSIStatBufL sStatBuf;
3126 :
3127 52 : if (VSIStatL(pszPath, &sStatBuf) != 0)
3128 : {
3129 2 : CPLError(CE_Failure, CPLE_AppDefined,
3130 : "It seems no file system object called '%s' exists.", pszPath);
3131 :
3132 2 : return -1;
3133 : }
3134 :
3135 : /* -------------------------------------------------------------------- */
3136 : /* If it is a simple file, just delete it. */
3137 : /* -------------------------------------------------------------------- */
3138 50 : if (VSI_ISREG(sStatBuf.st_mode))
3139 : {
3140 34 : if (VSIUnlink(pszPath) != 0)
3141 : {
3142 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
3143 : pszPath);
3144 :
3145 0 : return -1;
3146 : }
3147 :
3148 34 : return 0;
3149 : }
3150 :
3151 : /* -------------------------------------------------------------------- */
3152 : /* If it is a directory recurse then unlink the directory. */
3153 : /* -------------------------------------------------------------------- */
3154 16 : else if (VSI_ISDIR(sStatBuf.st_mode))
3155 : {
3156 16 : char **papszItems = VSIReadDir(pszPath);
3157 :
3158 32 : for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
3159 : {
3160 16 : if (papszItems[i][0] == '\0' || EQUAL(papszItems[i], ".") ||
3161 16 : EQUAL(papszItems[i], ".."))
3162 0 : continue;
3163 :
3164 : const std::string osSubPath =
3165 16 : CPLFormFilenameSafe(pszPath, papszItems[i], nullptr);
3166 :
3167 16 : const int nErr = CPLUnlinkTree(osSubPath.c_str());
3168 :
3169 16 : if (nErr != 0)
3170 : {
3171 0 : CSLDestroy(papszItems);
3172 0 : return nErr;
3173 : }
3174 : }
3175 :
3176 16 : CSLDestroy(papszItems);
3177 :
3178 16 : if (VSIRmdir(pszPath) != 0)
3179 : {
3180 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
3181 : pszPath);
3182 :
3183 0 : return -1;
3184 : }
3185 :
3186 16 : return 0;
3187 : }
3188 :
3189 : /* -------------------------------------------------------------------- */
3190 : /* otherwise report an error. */
3191 : /* -------------------------------------------------------------------- */
3192 0 : CPLError(CE_Failure, CPLE_AppDefined,
3193 : "Failed to unlink %s.\nUnrecognised filesystem object.", pszPath);
3194 0 : return 1000;
3195 : }
3196 :
3197 : /************************************************************************/
3198 : /* CPLCopyFile() */
3199 : /************************************************************************/
3200 :
3201 : /** Copy a file */
3202 2249 : int CPLCopyFile(const char *pszNewPath, const char *pszOldPath)
3203 :
3204 : {
3205 2249 : return VSICopyFile(pszOldPath, pszNewPath, nullptr,
3206 : static_cast<vsi_l_offset>(-1), nullptr, nullptr,
3207 2249 : nullptr);
3208 : }
3209 :
3210 : /************************************************************************/
3211 : /* CPLCopyTree() */
3212 : /************************************************************************/
3213 :
3214 : /** Recursively copy a tree */
3215 4 : int CPLCopyTree(const char *pszNewPath, const char *pszOldPath)
3216 :
3217 : {
3218 : VSIStatBufL sStatBuf;
3219 4 : if (VSIStatL(pszNewPath, &sStatBuf) == 0)
3220 : {
3221 1 : CPLError(
3222 : CE_Failure, CPLE_AppDefined,
3223 : "It seems that a file system object called '%s' already exists.",
3224 : pszNewPath);
3225 :
3226 1 : return -1;
3227 : }
3228 :
3229 3 : if (VSIStatL(pszOldPath, &sStatBuf) != 0)
3230 : {
3231 1 : CPLError(CE_Failure, CPLE_AppDefined,
3232 : "It seems no file system object called '%s' exists.",
3233 : pszOldPath);
3234 :
3235 1 : return -1;
3236 : }
3237 :
3238 2 : if (VSI_ISDIR(sStatBuf.st_mode))
3239 : {
3240 1 : if (VSIMkdir(pszNewPath, 0755) != 0)
3241 : {
3242 0 : CPLError(CE_Failure, CPLE_AppDefined,
3243 : "Cannot create directory '%s'.", pszNewPath);
3244 :
3245 0 : return -1;
3246 : }
3247 :
3248 1 : char **papszItems = VSIReadDir(pszOldPath);
3249 :
3250 4 : for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
3251 : {
3252 3 : if (EQUAL(papszItems[i], ".") || EQUAL(papszItems[i], ".."))
3253 2 : continue;
3254 :
3255 : const std::string osNewSubPath =
3256 1 : CPLFormFilenameSafe(pszNewPath, papszItems[i], nullptr);
3257 : const std::string osOldSubPath =
3258 1 : CPLFormFilenameSafe(pszOldPath, papszItems[i], nullptr);
3259 :
3260 : const int nErr =
3261 1 : CPLCopyTree(osNewSubPath.c_str(), osOldSubPath.c_str());
3262 :
3263 1 : if (nErr != 0)
3264 : {
3265 0 : CSLDestroy(papszItems);
3266 0 : return nErr;
3267 : }
3268 : }
3269 1 : CSLDestroy(papszItems);
3270 :
3271 1 : return 0;
3272 : }
3273 1 : else if (VSI_ISREG(sStatBuf.st_mode))
3274 : {
3275 1 : return CPLCopyFile(pszNewPath, pszOldPath);
3276 : }
3277 : else
3278 : {
3279 0 : CPLError(CE_Failure, CPLE_AppDefined,
3280 : "Unrecognized filesystem object : '%s'.", pszOldPath);
3281 0 : return -1;
3282 : }
3283 : }
3284 :
3285 : /************************************************************************/
3286 : /* CPLMoveFile() */
3287 : /************************************************************************/
3288 :
3289 : /** Move a file */
3290 186 : int CPLMoveFile(const char *pszNewPath, const char *pszOldPath)
3291 :
3292 : {
3293 186 : if (VSIRename(pszOldPath, pszNewPath) == 0)
3294 183 : return 0;
3295 :
3296 3 : const int nRet = CPLCopyFile(pszNewPath, pszOldPath);
3297 :
3298 3 : if (nRet == 0)
3299 3 : VSIUnlink(pszOldPath);
3300 3 : return nRet;
3301 : }
3302 :
3303 : /************************************************************************/
3304 : /* CPLSymlink() */
3305 : /************************************************************************/
3306 :
3307 : /** Create a symbolic link */
3308 : #ifdef _WIN32
3309 : int CPLSymlink(const char *, const char *, CSLConstList)
3310 : {
3311 : return -1;
3312 : }
3313 : #else
3314 0 : int CPLSymlink(const char *pszOldPath, const char *pszNewPath,
3315 : CSLConstList /* papszOptions */)
3316 : {
3317 0 : return symlink(pszOldPath, pszNewPath);
3318 : }
3319 : #endif
3320 :
3321 : /************************************************************************/
3322 : /* ==================================================================== */
3323 : /* CPLLocaleC */
3324 : /* ==================================================================== */
3325 : /************************************************************************/
3326 :
3327 : //! @cond Doxygen_Suppress
3328 : /************************************************************************/
3329 : /* CPLLocaleC() */
3330 : /************************************************************************/
3331 :
3332 130 : CPLLocaleC::CPLLocaleC() : pszOldLocale(nullptr)
3333 : {
3334 130 : if (CPLTestBool(CPLGetConfigOption("GDAL_DISABLE_CPLLOCALEC", "NO")))
3335 0 : return;
3336 :
3337 130 : pszOldLocale = CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr));
3338 130 : if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
3339 0 : CPLsetlocale(LC_NUMERIC, "C") == nullptr)
3340 : {
3341 130 : CPLFree(pszOldLocale);
3342 130 : pszOldLocale = nullptr;
3343 : }
3344 : }
3345 :
3346 : /************************************************************************/
3347 : /* ~CPLLocaleC() */
3348 : /************************************************************************/
3349 :
3350 0 : CPLLocaleC::~CPLLocaleC()
3351 :
3352 : {
3353 130 : if (pszOldLocale == nullptr)
3354 130 : return;
3355 :
3356 0 : CPLsetlocale(LC_NUMERIC, pszOldLocale);
3357 0 : CPLFree(pszOldLocale);
3358 130 : }
3359 :
3360 : /************************************************************************/
3361 : /* CPLThreadLocaleCPrivate */
3362 : /************************************************************************/
3363 :
3364 : #ifdef HAVE_USELOCALE
3365 :
3366 : class CPLThreadLocaleCPrivate
3367 : {
3368 : locale_t nNewLocale;
3369 : locale_t nOldLocale;
3370 :
3371 : CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3372 :
3373 : public:
3374 : CPLThreadLocaleCPrivate();
3375 : ~CPLThreadLocaleCPrivate();
3376 : };
3377 :
3378 0 : CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3379 0 : : nNewLocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)),
3380 0 : nOldLocale(uselocale(nNewLocale))
3381 : {
3382 0 : }
3383 :
3384 0 : CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3385 : {
3386 0 : uselocale(nOldLocale);
3387 0 : freelocale(nNewLocale);
3388 0 : }
3389 :
3390 : #elif defined(_MSC_VER)
3391 :
3392 : class CPLThreadLocaleCPrivate
3393 : {
3394 : int nOldValConfigThreadLocale;
3395 : char *pszOldLocale;
3396 :
3397 : CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3398 :
3399 : public:
3400 : CPLThreadLocaleCPrivate();
3401 : ~CPLThreadLocaleCPrivate();
3402 : };
3403 :
3404 : CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3405 : {
3406 : nOldValConfigThreadLocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
3407 : pszOldLocale = setlocale(LC_NUMERIC, "C");
3408 : if (pszOldLocale)
3409 : pszOldLocale = CPLStrdup(pszOldLocale);
3410 : }
3411 :
3412 : CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3413 : {
3414 : if (pszOldLocale != nullptr)
3415 : {
3416 : setlocale(LC_NUMERIC, pszOldLocale);
3417 : CPLFree(pszOldLocale);
3418 : }
3419 : _configthreadlocale(nOldValConfigThreadLocale);
3420 : }
3421 :
3422 : #else
3423 :
3424 : class CPLThreadLocaleCPrivate
3425 : {
3426 : char *pszOldLocale;
3427 :
3428 : CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
3429 :
3430 : public:
3431 : CPLThreadLocaleCPrivate();
3432 : ~CPLThreadLocaleCPrivate();
3433 : };
3434 :
3435 : CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
3436 : : pszOldLocale(CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr)))
3437 : {
3438 : if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
3439 : CPLsetlocale(LC_NUMERIC, "C") == nullptr)
3440 : {
3441 : CPLFree(pszOldLocale);
3442 : pszOldLocale = nullptr;
3443 : }
3444 : }
3445 :
3446 : CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
3447 : {
3448 : if (pszOldLocale != nullptr)
3449 : {
3450 : CPLsetlocale(LC_NUMERIC, pszOldLocale);
3451 : CPLFree(pszOldLocale);
3452 : }
3453 : }
3454 :
3455 : #endif
3456 :
3457 : /************************************************************************/
3458 : /* CPLThreadLocaleC() */
3459 : /************************************************************************/
3460 :
3461 0 : CPLThreadLocaleC::CPLThreadLocaleC() : m_private(new CPLThreadLocaleCPrivate)
3462 : {
3463 0 : }
3464 :
3465 : /************************************************************************/
3466 : /* ~CPLThreadLocaleC() */
3467 : /************************************************************************/
3468 :
3469 0 : CPLThreadLocaleC::~CPLThreadLocaleC()
3470 :
3471 : {
3472 0 : delete m_private;
3473 0 : }
3474 :
3475 : //! @endcond
3476 :
3477 : /************************************************************************/
3478 : /* CPLsetlocale() */
3479 : /************************************************************************/
3480 :
3481 : /**
3482 : * Prevents parallel executions of setlocale().
3483 : *
3484 : * Calling setlocale() concurrently from two or more threads is a
3485 : * potential data race. A mutex is used to provide a critical region so
3486 : * that only one thread at a time can be executing setlocale().
3487 : *
3488 : * The return should not be freed, and copied quickly as it may be invalidated
3489 : * by a following next call to CPLsetlocale().
3490 : *
3491 : * @param category See your compiler's documentation on setlocale.
3492 : * @param locale See your compiler's documentation on setlocale.
3493 : *
3494 : * @return See your compiler's documentation on setlocale.
3495 : */
3496 132 : char *CPLsetlocale(int category, const char *locale)
3497 : {
3498 264 : CPLMutexHolder oHolder(&hSetLocaleMutex);
3499 132 : char *pszRet = setlocale(category, locale);
3500 132 : if (pszRet == nullptr)
3501 0 : return pszRet;
3502 :
3503 : // Make it thread-locale storage.
3504 132 : return const_cast<char *>(CPLSPrintf("%s", pszRet));
3505 : }
3506 :
3507 : /************************************************************************/
3508 : /* CPLCleanupSetlocaleMutex() */
3509 : /************************************************************************/
3510 :
3511 1111 : void CPLCleanupSetlocaleMutex(void)
3512 : {
3513 1111 : if (hSetLocaleMutex != nullptr)
3514 5 : CPLDestroyMutex(hSetLocaleMutex);
3515 1111 : hSetLocaleMutex = nullptr;
3516 1111 : }
3517 :
3518 : /************************************************************************/
3519 : /* IsPowerOfTwo() */
3520 : /************************************************************************/
3521 :
3522 150 : int CPLIsPowerOfTwo(unsigned int i)
3523 : {
3524 150 : if (i == 0)
3525 0 : return FALSE;
3526 150 : return (i & (i - 1)) == 0 ? TRUE : FALSE;
3527 : }
3528 :
3529 : /************************************************************************/
3530 : /* CPLCheckForFile() */
3531 : /************************************************************************/
3532 :
3533 : /**
3534 : * Check for file existence.
3535 : *
3536 : * The function checks if a named file exists in the filesystem, hopefully
3537 : * in an efficient fashion if a sibling file list is available. It exists
3538 : * primarily to do faster file checking for functions like GDAL open methods
3539 : * that get a list of files from the target directory.
3540 : *
3541 : * If the sibling file list exists (is not NULL) it is assumed to be a list
3542 : * of files in the same directory as the target file, and it will be checked
3543 : * (case insensitively) for a match. If a match is found, pszFilename is
3544 : * updated with the correct case and TRUE is returned.
3545 : *
3546 : * If papszSiblingFiles is NULL, a VSIStatL() is used to test for the files
3547 : * existence, and no case insensitive testing is done.
3548 : *
3549 : * @param pszFilename name of file to check for - filename case updated in
3550 : * some cases.
3551 : * @param papszSiblingFiles a list of files in the same directory as
3552 : * pszFilename if available, or NULL. This list should have no path components.
3553 : *
3554 : * @return TRUE if a match is found, or FALSE if not.
3555 : */
3556 :
3557 164023 : int CPLCheckForFile(char *pszFilename, char **papszSiblingFiles)
3558 :
3559 : {
3560 : /* -------------------------------------------------------------------- */
3561 : /* Fallback case if we don't have a sibling file list. */
3562 : /* -------------------------------------------------------------------- */
3563 164023 : if (papszSiblingFiles == nullptr)
3564 : {
3565 : VSIStatBufL sStatBuf;
3566 :
3567 11262 : return VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
3568 : }
3569 :
3570 : /* -------------------------------------------------------------------- */
3571 : /* We have sibling files, compare the non-path filename portion */
3572 : /* of pszFilename too all entries. */
3573 : /* -------------------------------------------------------------------- */
3574 305522 : const CPLString osFileOnly = CPLGetFilename(pszFilename);
3575 :
3576 15196100 : for (int i = 0; papszSiblingFiles[i] != nullptr; i++)
3577 : {
3578 15043500 : if (EQUAL(papszSiblingFiles[i], osFileOnly))
3579 : {
3580 313 : strcpy(pszFilename + strlen(pszFilename) - osFileOnly.size(),
3581 156 : papszSiblingFiles[i]);
3582 157 : return TRUE;
3583 : }
3584 : }
3585 :
3586 152604 : return FALSE;
3587 : }
3588 :
3589 : /************************************************************************/
3590 : /* Stub implementation of zip services if we don't have libz. */
3591 : /************************************************************************/
3592 :
3593 : #if !defined(HAVE_LIBZ)
3594 :
3595 : void *CPLCreateZip(const char *, char **)
3596 :
3597 : {
3598 : CPLError(CE_Failure, CPLE_NotSupported,
3599 : "This GDAL/OGR build does not include zlib and zip services.");
3600 : return nullptr;
3601 : }
3602 :
3603 : CPLErr CPLCreateFileInZip(void *, const char *, char **)
3604 : {
3605 : return CE_Failure;
3606 : }
3607 :
3608 : CPLErr CPLWriteFileInZip(void *, const void *, int)
3609 : {
3610 : return CE_Failure;
3611 : }
3612 :
3613 : CPLErr CPLCloseFileInZip(void *)
3614 : {
3615 : return CE_Failure;
3616 : }
3617 :
3618 : CPLErr CPLCloseZip(void *)
3619 : {
3620 : return CE_Failure;
3621 : }
3622 :
3623 : void *CPLZLibDeflate(const void *, size_t, int, void *, size_t,
3624 : size_t *pnOutBytes)
3625 : {
3626 : if (pnOutBytes != nullptr)
3627 : *pnOutBytes = 0;
3628 : return nullptr;
3629 : }
3630 :
3631 : void *CPLZLibInflate(const void *, size_t, void *, size_t, size_t *pnOutBytes)
3632 : {
3633 : if (pnOutBytes != nullptr)
3634 : *pnOutBytes = 0;
3635 : return nullptr;
3636 : }
3637 :
3638 : #endif /* !defined(HAVE_LIBZ) */
3639 :
3640 : /************************************************************************/
3641 : /* ==================================================================== */
3642 : /* CPLConfigOptionSetter */
3643 : /* ==================================================================== */
3644 : /************************************************************************/
3645 :
3646 : //! @cond Doxygen_Suppress
3647 : /************************************************************************/
3648 : /* CPLConfigOptionSetter() */
3649 : /************************************************************************/
3650 :
3651 25831 : CPLConfigOptionSetter::CPLConfigOptionSetter(const char *pszKey,
3652 : const char *pszValue,
3653 25831 : bool bSetOnlyIfUndefined)
3654 25831 : : m_pszKey(CPLStrdup(pszKey)), m_pszOldValue(nullptr),
3655 25828 : m_bRestoreOldValue(false)
3656 : {
3657 25828 : const char *pszOldValue = CPLGetThreadLocalConfigOption(pszKey, nullptr);
3658 41627 : if ((bSetOnlyIfUndefined &&
3659 35860 : CPLGetConfigOption(pszKey, nullptr) == nullptr) ||
3660 10049 : !bSetOnlyIfUndefined)
3661 : {
3662 25826 : m_bRestoreOldValue = true;
3663 25826 : if (pszOldValue)
3664 650 : m_pszOldValue = CPLStrdup(pszOldValue);
3665 25826 : CPLSetThreadLocalConfigOption(pszKey, pszValue);
3666 : }
3667 25787 : }
3668 :
3669 : /************************************************************************/
3670 : /* ~CPLConfigOptionSetter() */
3671 : /************************************************************************/
3672 :
3673 51628 : CPLConfigOptionSetter::~CPLConfigOptionSetter()
3674 : {
3675 25833 : if (m_bRestoreOldValue)
3676 : {
3677 25824 : CPLSetThreadLocalConfigOption(m_pszKey, m_pszOldValue);
3678 25733 : CPLFree(m_pszOldValue);
3679 : }
3680 25741 : CPLFree(m_pszKey);
3681 25795 : }
3682 :
3683 : //! @endcond
3684 :
3685 : /************************************************************************/
3686 : /* CPLIsInteractive() */
3687 : /************************************************************************/
3688 :
3689 : /** Returns whether the provided file refers to a terminal.
3690 : *
3691 : * This function is a wrapper of the ``isatty()`` POSIX function.
3692 : *
3693 : * @param f File to test. Typically stdin, stdout or stderr
3694 : * @return true if it is an open file referring to a terminal.
3695 : * @since GDAL 3.11
3696 : */
3697 636 : bool CPLIsInteractive(FILE *f)
3698 : {
3699 : #ifndef _WIN32
3700 636 : return isatty(static_cast<int>(fileno(f)));
3701 : #else
3702 : return _isatty(_fileno(f));
3703 : #endif
3704 : }
3705 :
3706 : /************************************************************************/
3707 : /* CPLLockFileStruct */
3708 : /************************************************************************/
3709 :
3710 : //! @cond Doxygen_Suppress
3711 : struct CPLLockFileStruct
3712 : {
3713 : std::string osLockFilename{};
3714 : std::atomic<bool> bStop = false;
3715 : CPLJoinableThread *hThread = nullptr;
3716 : };
3717 :
3718 : //! @endcond
3719 :
3720 : /************************************************************************/
3721 : /* CPLLockFileEx() */
3722 : /************************************************************************/
3723 :
3724 : /** Create and acquire a lock file.
3725 : *
3726 : * Only one caller can acquire the lock file at a time. The O_CREAT|O_EXCL
3727 : * flags of open() are used for that purpose (there might be limitations for
3728 : * network file systems).
3729 : *
3730 : * The lock file is continuously touched by a thread started by this function,
3731 : * to indicate it is still alive. If an existing lock file is found that has
3732 : * not been recently refreshed it will be considered stalled, and will be
3733 : * deleted before attempting to recreate it.
3734 : *
3735 : * This function must be paired with CPLUnlockFileEx().
3736 : *
3737 : * Available options are:
3738 : * <ul>
3739 : * <li>WAIT_TIME=value_in_sec/inf: Maximum amount of time in second that this
3740 : * function can spend waiting for the lock. If not set, default to infinity.
3741 : * </li>
3742 : * <li>STALLED_DELAY=value_in_sec: Delay in second to consider that an existing
3743 : * lock file that has not been touched since STALLED_DELAY is stalled, and can
3744 : * be re-acquired. Defaults to 10 seconds.
3745 : * </li>
3746 : * <li>VERBOSE_WAIT_MESSAGE=YES/NO: Whether to emit a CE_Warning message while
3747 : * waiting for a busy lock. Default to NO.
3748 : * </li>
3749 : * </ul>
3750 :
3751 : * @param pszLockFileName Lock file name. The directory must already exist.
3752 : * Must not be NULL.
3753 : * @param[out] phLockFileHandle Pointer to at location where to store the lock
3754 : * handle that must be passed to CPLUnlockFileEx().
3755 : * *phLockFileHandle will be null if the return
3756 : * code of that function is not CLFS_OK.
3757 : * @param papszOptions NULL terminated list of strings, or NULL.
3758 : *
3759 : * @return lock file status.
3760 : *
3761 : * @since 3.11
3762 : */
3763 15 : CPLLockFileStatus CPLLockFileEx(const char *pszLockFileName,
3764 : CPLLockFileHandle *phLockFileHandle,
3765 : CSLConstList papszOptions)
3766 : {
3767 15 : if (!pszLockFileName || !phLockFileHandle)
3768 2 : return CLFS_API_MISUSE;
3769 :
3770 13 : *phLockFileHandle = nullptr;
3771 :
3772 : const double dfWaitTime =
3773 13 : CPLAtof(CSLFetchNameValueDef(papszOptions, "WAIT_TIME", "inf"));
3774 : const double dfStalledDelay =
3775 13 : CPLAtof(CSLFetchNameValueDef(papszOptions, "STALLED_DELAY", "10"));
3776 : const bool bVerboseWait =
3777 13 : CPLFetchBool(papszOptions, "VERBOSE_WAIT_MESSAGE", false);
3778 :
3779 14 : for (int i = 0; i < 2; ++i)
3780 : {
3781 : #ifdef _WIN32
3782 : wchar_t *pwszFilename =
3783 : CPLRecodeToWChar(pszLockFileName, CPL_ENC_UTF8, CPL_ENC_UCS2);
3784 : int fd = _wopen(pwszFilename, _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);
3785 : CPLFree(pwszFilename);
3786 : #else
3787 14 : int fd = open(pszLockFileName, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
3788 : #endif
3789 14 : if (fd == -1)
3790 : {
3791 3 : if (errno != EEXIST || i == 1)
3792 : {
3793 0 : return CLFS_CANNOT_CREATE_LOCK;
3794 : }
3795 : else
3796 : {
3797 : // Wait for the .lock file to have been removed or
3798 : // not refreshed since dfStalledDelay seconds.
3799 3 : double dfCurWaitTime = dfWaitTime;
3800 : VSIStatBufL sStat;
3801 17 : while (VSIStatL(pszLockFileName, &sStat) == 0 &&
3802 8 : static_cast<double>(sStat.st_mtime) + dfStalledDelay >
3803 8 : static_cast<double>(time(nullptr)))
3804 : {
3805 7 : if (dfCurWaitTime <= 1e-5)
3806 2 : return CLFS_LOCK_BUSY;
3807 :
3808 6 : if (bVerboseWait)
3809 : {
3810 5 : CPLError(CE_Warning, CPLE_AppDefined,
3811 : "Waiting for %s to be freed...",
3812 : pszLockFileName);
3813 : }
3814 : else
3815 : {
3816 1 : CPLDebug("CPL", "Waiting for %s to be freed...",
3817 : pszLockFileName);
3818 : }
3819 :
3820 6 : const double dfPauseDelay = std::min(0.5, dfWaitTime);
3821 6 : CPLSleep(dfPauseDelay);
3822 6 : dfCurWaitTime -= dfPauseDelay;
3823 : }
3824 :
3825 2 : if (VSIUnlink(pszLockFileName) != 0)
3826 : {
3827 1 : return CLFS_CANNOT_CREATE_LOCK;
3828 : }
3829 : }
3830 : }
3831 : else
3832 : {
3833 11 : close(fd);
3834 11 : break;
3835 : }
3836 : }
3837 :
3838 : // Touch regularly the lock file to show it is still alive
3839 : struct KeepAliveLockFile
3840 : {
3841 11 : static void func(void *user_data)
3842 : {
3843 11 : CPLLockFileHandle hLockFileHandle =
3844 : static_cast<CPLLockFileHandle>(user_data);
3845 23 : while (!hLockFileHandle->bStop)
3846 : {
3847 : auto f = VSIVirtualHandleUniquePtr(
3848 24 : VSIFOpenL(hLockFileHandle->osLockFilename.c_str(), "wb"));
3849 12 : if (f)
3850 : {
3851 12 : f.reset();
3852 : }
3853 12 : constexpr double REFRESH_DELAY = 0.5;
3854 12 : CPLSleep(REFRESH_DELAY);
3855 : }
3856 11 : }
3857 : };
3858 :
3859 11 : *phLockFileHandle = new CPLLockFileStruct();
3860 11 : (*phLockFileHandle)->osLockFilename = pszLockFileName;
3861 :
3862 22 : (*phLockFileHandle)->hThread =
3863 11 : CPLCreateJoinableThread(KeepAliveLockFile::func, *phLockFileHandle);
3864 11 : if ((*phLockFileHandle)->hThread == nullptr)
3865 : {
3866 0 : VSIUnlink(pszLockFileName);
3867 0 : delete *phLockFileHandle;
3868 0 : *phLockFileHandle = nullptr;
3869 0 : return CLFS_THREAD_CREATION_FAILED;
3870 : }
3871 :
3872 11 : return CLFS_OK;
3873 : }
3874 :
3875 : /************************************************************************/
3876 : /* CPLUnlockFileEx() */
3877 : /************************************************************************/
3878 :
3879 : /** Release and delete a lock file.
3880 : *
3881 : * This function must be paired with CPLLockFileEx().
3882 : *
3883 : * @param hLockFileHandle Lock handle (value of *phLockFileHandle argument
3884 : * set by CPLLockFileEx()), or NULL.
3885 : *
3886 : * @since 3.11
3887 : */
3888 12 : void CPLUnlockFileEx(CPLLockFileHandle hLockFileHandle)
3889 : {
3890 12 : if (hLockFileHandle)
3891 : {
3892 : // Remove .lock file
3893 11 : hLockFileHandle->bStop = true;
3894 11 : CPLJoinThread(hLockFileHandle->hThread);
3895 11 : VSIUnlink(hLockFileHandle->osLockFilename.c_str());
3896 :
3897 11 : delete hLockFileHandle;
3898 : }
3899 12 : }
|