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