Line data Source code
1 : /**********************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: Portable filename/path parsing, and forming ala "Glob API".
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : **********************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "cpl_conv.h"
16 :
17 : #include <cctype>
18 : #include <climits>
19 : #include <cstddef>
20 : #include <cstdio>
21 : #include <cstring>
22 : #if HAVE_UNISTD_H
23 : #include <unistd.h>
24 : #endif
25 :
26 : #include <algorithm>
27 : #include <string>
28 :
29 : #include "cpl_atomic_ops.h"
30 : #include "cpl_config.h"
31 : #include "cpl_error.h"
32 : #include "cpl_multiproc.h"
33 : #include "cpl_string.h"
34 : #include "cpl_vsi.h"
35 :
36 : // Should be size of larged possible filename.
37 : constexpr int CPL_PATH_BUF_SIZE = 2048;
38 : constexpr int CPL_PATH_BUF_COUNT = 10;
39 :
40 0 : static const char *CPLStaticBufferTooSmall(char *pszStaticResult)
41 : {
42 0 : CPLError(CE_Failure, CPLE_AppDefined, "Destination buffer too small");
43 0 : if (pszStaticResult == nullptr)
44 0 : return "";
45 0 : strcpy(pszStaticResult, "");
46 0 : return pszStaticResult;
47 : }
48 :
49 : /************************************************************************/
50 : /* CPLGetStaticResult() */
51 : /************************************************************************/
52 :
53 2986490 : static char *CPLGetStaticResult()
54 :
55 : {
56 2986490 : int bMemoryError = FALSE;
57 : char *pachBufRingInfo =
58 2986490 : static_cast<char *>(CPLGetTLSEx(CTLS_PATHBUF, &bMemoryError));
59 2986440 : if (bMemoryError)
60 0 : return nullptr;
61 2986440 : if (pachBufRingInfo == nullptr)
62 : {
63 1375 : pachBufRingInfo = static_cast<char *>(VSI_CALLOC_VERBOSE(
64 : 1, sizeof(int) + CPL_PATH_BUF_SIZE * CPL_PATH_BUF_COUNT));
65 1375 : if (pachBufRingInfo == nullptr)
66 0 : return nullptr;
67 1375 : CPLSetTLS(CTLS_PATHBUF, pachBufRingInfo, TRUE);
68 : }
69 :
70 : /* -------------------------------------------------------------------- */
71 : /* Work out which string in the "ring" we want to use this */
72 : /* time. */
73 : /* -------------------------------------------------------------------- */
74 2986400 : int *pnBufIndex = reinterpret_cast<int *>(pachBufRingInfo);
75 2986400 : const size_t nOffset =
76 2986400 : sizeof(int) + static_cast<size_t>(*pnBufIndex * CPL_PATH_BUF_SIZE);
77 2986400 : char *pachBuffer = pachBufRingInfo + nOffset;
78 :
79 2986400 : *pnBufIndex = (*pnBufIndex + 1) % CPL_PATH_BUF_COUNT;
80 :
81 2986400 : return pachBuffer;
82 : }
83 :
84 : /************************************************************************/
85 : /* CPLFindFilenameStart() */
86 : /************************************************************************/
87 :
88 3211280 : static int CPLFindFilenameStart(const char *pszFilename, size_t nStart = 0)
89 :
90 : {
91 3211280 : size_t iFileStart = nStart ? nStart : strlen(pszFilename);
92 :
93 47899600 : for (; iFileStart > 0 && pszFilename[iFileStart - 1] != '/' &&
94 44690100 : pszFilename[iFileStart - 1] != '\\';
95 : iFileStart--)
96 : {
97 : }
98 :
99 3211280 : return static_cast<int>(iFileStart);
100 : }
101 :
102 : /************************************************************************/
103 : /* CPLGetPath() */
104 : /************************************************************************/
105 :
106 : /**
107 : * Extract directory path portion of filename.
108 : *
109 : * Returns a string containing the directory path portion of the passed
110 : * filename. If there is no path in the passed filename an empty string
111 : * will be returned (not NULL).
112 : *
113 : * <pre>
114 : * CPLGetPath( "abc/def.xyz" ) == "abc"
115 : * CPLGetPath( "/abc/def/" ) == "/abc/def"
116 : * CPLGetPath( "/" ) == "/"
117 : * CPLGetPath( "/abc/def" ) == "/abc"
118 : * CPLGetPath( "abc" ) == ""
119 : * </pre>
120 : *
121 : * @param pszFilename the filename potentially including a path.
122 : *
123 : * @return Path in an internal string which must not be freed. The string
124 : * may be destroyed by the next CPL filename handling call. The returned
125 : * will generally not contain a trailing path separator.
126 : */
127 :
128 208372 : const char *CPLGetPath(const char *pszFilename)
129 :
130 : {
131 208372 : size_t nSuffixPos = 0;
132 208372 : if (STARTS_WITH(pszFilename, "/vsicurl/http"))
133 : {
134 12 : const char *pszQuestionMark = strchr(pszFilename, '?');
135 12 : if (pszQuestionMark)
136 1 : nSuffixPos = static_cast<size_t>(pszQuestionMark - pszFilename);
137 : }
138 :
139 208372 : const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
140 208372 : char *pszStaticResult = CPLGetStaticResult();
141 :
142 208372 : if (pszStaticResult == nullptr || iFileStart >= CPL_PATH_BUF_SIZE)
143 0 : return CPLStaticBufferTooSmall(pszStaticResult);
144 :
145 208372 : CPLAssert(!(pszFilename >= pszStaticResult &&
146 : pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
147 :
148 208372 : if (iFileStart == 0)
149 : {
150 550 : strcpy(pszStaticResult, "");
151 550 : return pszStaticResult;
152 : }
153 :
154 207822 : CPLStrlcpy(pszStaticResult, pszFilename,
155 207822 : static_cast<size_t>(iFileStart) + 1);
156 :
157 207822 : if (iFileStart > 1 && (pszStaticResult[iFileStart - 1] == '/' ||
158 0 : pszStaticResult[iFileStart - 1] == '\\'))
159 207482 : pszStaticResult[iFileStart - 1] = '\0';
160 :
161 207822 : if (nSuffixPos)
162 : {
163 1 : if (CPLStrlcat(pszStaticResult, pszFilename + nSuffixPos,
164 1 : CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE)
165 0 : return CPLStaticBufferTooSmall(pszStaticResult);
166 : }
167 :
168 207822 : return pszStaticResult;
169 : }
170 :
171 : /************************************************************************/
172 : /* CPLGetDirname() */
173 : /************************************************************************/
174 :
175 : /**
176 : * Extract directory path portion of filename.
177 : *
178 : * Returns a string containing the directory path portion of the passed
179 : * filename. If there is no path in the passed filename the dot will be
180 : * returned. It is the only difference from CPLGetPath().
181 : *
182 : * <pre>
183 : * CPLGetDirname( "abc/def.xyz" ) == "abc"
184 : * CPLGetDirname( "/abc/def/" ) == "/abc/def"
185 : * CPLGetDirname( "/" ) == "/"
186 : * CPLGetDirname( "/abc/def" ) == "/abc"
187 : * CPLGetDirname( "abc" ) == "."
188 : * </pre>
189 : *
190 : * @param pszFilename the filename potentially including a path.
191 : *
192 : * @return Path in an internal string which must not be freed. The string
193 : * may be destroyed by the next CPL filename handling call. The returned
194 : * will generally not contain a trailing path separator.
195 : */
196 :
197 139893 : const char *CPLGetDirname(const char *pszFilename)
198 :
199 : {
200 139893 : size_t nSuffixPos = 0;
201 139893 : if (STARTS_WITH(pszFilename, "/vsicurl/http"))
202 : {
203 111 : const char *pszQuestionMark = strchr(pszFilename, '?');
204 111 : if (pszQuestionMark)
205 1 : nSuffixPos = static_cast<size_t>(pszQuestionMark - pszFilename);
206 : }
207 :
208 139893 : const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
209 139873 : char *pszStaticResult = CPLGetStaticResult();
210 :
211 139826 : if (pszStaticResult == nullptr || iFileStart >= CPL_PATH_BUF_SIZE)
212 48 : return CPLStaticBufferTooSmall(pszStaticResult);
213 :
214 139778 : CPLAssert(!(pszFilename >= pszStaticResult &&
215 : pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
216 :
217 139778 : if (iFileStart == 0)
218 : {
219 63 : strcpy(pszStaticResult, ".");
220 63 : return pszStaticResult;
221 : }
222 :
223 139715 : CPLStrlcpy(pszStaticResult, pszFilename,
224 139715 : static_cast<size_t>(iFileStart) + 1);
225 :
226 139813 : if (iFileStart > 1 && (pszStaticResult[iFileStart - 1] == '/' ||
227 22 : pszStaticResult[iFileStart - 1] == '\\'))
228 139750 : pszStaticResult[iFileStart - 1] = '\0';
229 :
230 139813 : if (nSuffixPos)
231 : {
232 1 : if (CPLStrlcat(pszStaticResult, pszFilename + nSuffixPos,
233 1 : CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE)
234 46 : return CPLStaticBufferTooSmall(pszStaticResult);
235 : }
236 :
237 139767 : return pszStaticResult;
238 : }
239 :
240 : /************************************************************************/
241 : /* CPLGetFilename() */
242 : /************************************************************************/
243 :
244 : /**
245 : * Extract non-directory portion of filename.
246 : *
247 : * Returns a string containing the bare filename portion of the passed
248 : * filename. If there is no filename (passed value ends in trailing directory
249 : * separator) an empty string is returned.
250 : *
251 : * <pre>
252 : * CPLGetFilename( "abc/def.xyz" ) == "def.xyz"
253 : * CPLGetFilename( "/abc/def/" ) == ""
254 : * CPLGetFilename( "abc/def" ) == "def"
255 : * </pre>
256 : *
257 : * @param pszFullFilename the full filename potentially including a path.
258 : *
259 : * @return just the non-directory portion of the path (points back into
260 : * original string).
261 : */
262 :
263 755031 : const char *CPLGetFilename(const char *pszFullFilename)
264 :
265 : {
266 755031 : const int iFileStart = CPLFindFilenameStart(pszFullFilename);
267 :
268 755030 : return pszFullFilename + iFileStart;
269 : }
270 :
271 : /************************************************************************/
272 : /* CPLGetBasename() */
273 : /************************************************************************/
274 :
275 : /**
276 : * Extract basename (non-directory, non-extension) portion of filename.
277 : *
278 : * Returns a string containing the file basename portion of the passed
279 : * name. If there is no basename (passed value ends in trailing directory
280 : * separator, or filename starts with a dot) an empty string is returned.
281 : *
282 : * <pre>
283 : * CPLGetBasename( "abc/def.xyz" ) == "def"
284 : * CPLGetBasename( "abc/def" ) == "def"
285 : * CPLGetBasename( "abc/def/" ) == ""
286 : * </pre>
287 : *
288 : * @param pszFullFilename the full filename potentially including a path.
289 : *
290 : * @return just the non-directory, non-extension portion of the path in
291 : * an internal string which must not be freed. The string
292 : * may be destroyed by the next CPL filename handling call.
293 : */
294 :
295 442140 : const char *CPLGetBasename(const char *pszFullFilename)
296 :
297 : {
298 : const size_t iFileStart =
299 442140 : static_cast<size_t>(CPLFindFilenameStart(pszFullFilename));
300 442140 : char *pszStaticResult = CPLGetStaticResult();
301 442140 : if (pszStaticResult == nullptr)
302 0 : return CPLStaticBufferTooSmall(pszStaticResult);
303 :
304 442140 : CPLAssert(!(pszFullFilename >= pszStaticResult &&
305 : pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
306 :
307 442140 : size_t iExtStart = strlen(pszFullFilename);
308 2502590 : for (; iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
309 : iExtStart--)
310 : {
311 : }
312 :
313 442140 : if (iExtStart == iFileStart)
314 27176 : iExtStart = strlen(pszFullFilename);
315 :
316 442140 : const size_t nLength = iExtStart - iFileStart;
317 :
318 442140 : if (nLength >= static_cast<size_t>(CPL_PATH_BUF_SIZE))
319 0 : return CPLStaticBufferTooSmall(pszStaticResult);
320 :
321 442140 : CPLStrlcpy(pszStaticResult, pszFullFilename + iFileStart, nLength + 1);
322 :
323 442140 : return pszStaticResult;
324 : }
325 :
326 : /************************************************************************/
327 : /* CPLGetExtension() */
328 : /************************************************************************/
329 :
330 : /**
331 : * Extract filename extension from full filename.
332 : *
333 : * Returns a string containing the extension portion of the passed
334 : * name. If there is no extension (the filename has no dot) an empty string
335 : * is returned. The returned extension will not include the period.
336 : *
337 : * <pre>
338 : * CPLGetExtension( "abc/def.xyz" ) == "xyz"
339 : * CPLGetExtension( "abc/def" ) == ""
340 : * </pre>
341 : *
342 : * @param pszFullFilename the full filename potentially including a path.
343 : *
344 : * @return just the extension portion of the path in
345 : * an internal string which must not be freed. The string
346 : * may be destroyed by the next CPL filename handling call.
347 : */
348 :
349 1685270 : const char *CPLGetExtension(const char *pszFullFilename)
350 :
351 : {
352 1685270 : if (pszFullFilename[0] == '\0')
353 19378 : return "";
354 :
355 : size_t iFileStart =
356 1665890 : static_cast<size_t>(CPLFindFilenameStart(pszFullFilename));
357 1665920 : char *pszStaticResult = CPLGetStaticResult();
358 1665870 : if (pszStaticResult == nullptr)
359 0 : return CPLStaticBufferTooSmall(pszStaticResult);
360 :
361 1665870 : CPLAssert(!(pszFullFilename >= pszStaticResult &&
362 : pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
363 :
364 1665870 : size_t iExtStart = strlen(pszFullFilename);
365 13191400 : for (; iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
366 : iExtStart--)
367 : {
368 : }
369 :
370 1665870 : if (iExtStart == iFileStart)
371 547565 : iExtStart = strlen(pszFullFilename) - 1;
372 :
373 : // If the extension is too long, it is very much likely not an extension,
374 : // but another component of the path
375 1665870 : const size_t knMaxExtensionSize = 10;
376 1665870 : if (strlen(pszFullFilename + iExtStart + 1) > knMaxExtensionSize)
377 8062 : return "";
378 :
379 1657810 : if (CPLStrlcpy(pszStaticResult, pszFullFilename + iExtStart + 1,
380 1657820 : CPL_PATH_BUF_SIZE) >= static_cast<size_t>(CPL_PATH_BUF_SIZE))
381 0 : return CPLStaticBufferTooSmall(pszStaticResult);
382 :
383 1657820 : return pszStaticResult;
384 : }
385 :
386 : /************************************************************************/
387 : /* CPLGetCurrentDir() */
388 : /************************************************************************/
389 :
390 : /**
391 : * Get the current working directory name.
392 : *
393 : * @return a pointer to buffer, containing current working directory path
394 : * or NULL in case of error. User is responsible to free that buffer
395 : * after usage with CPLFree() function.
396 : * If HAVE_GETCWD macro is not defined, the function returns NULL.
397 : **/
398 :
399 : #ifdef _WIN32
400 : char *CPLGetCurrentDir()
401 : {
402 : const size_t nPathMax = _MAX_PATH;
403 : wchar_t *pwszDirPath =
404 : static_cast<wchar_t *>(VSI_MALLOC_VERBOSE(nPathMax * sizeof(wchar_t)));
405 : char *pszRet = nullptr;
406 : if (pwszDirPath != nullptr && _wgetcwd(pwszDirPath, nPathMax) != nullptr)
407 : {
408 : pszRet = CPLRecodeFromWChar(pwszDirPath, CPL_ENC_UCS2, CPL_ENC_UTF8);
409 : }
410 : CPLFree(pwszDirPath);
411 : return pszRet;
412 : }
413 : #elif defined(HAVE_GETCWD)
414 5063 : char *CPLGetCurrentDir()
415 : {
416 : #if PATH_MAX
417 5063 : const size_t nPathMax = PATH_MAX;
418 : #else
419 : const size_t nPathMax = 8192;
420 : #endif
421 :
422 5063 : char *pszDirPath = static_cast<char *>(VSI_MALLOC_VERBOSE(nPathMax));
423 5063 : if (!pszDirPath)
424 0 : return nullptr;
425 :
426 5063 : return getcwd(pszDirPath, nPathMax);
427 : }
428 : #else // !HAVE_GETCWD
429 : char *CPLGetCurrentDir()
430 : {
431 : return nullptr;
432 : }
433 : #endif // HAVE_GETCWD
434 :
435 : /************************************************************************/
436 : /* CPLResetExtension() */
437 : /************************************************************************/
438 :
439 : /**
440 : * Replace the extension with the provided one.
441 : *
442 : * @param pszPath the input path, this string is not altered.
443 : * @param pszExt the new extension to apply to the given path.
444 : *
445 : * @return an altered filename with the new extension. Do not
446 : * modify or free the returned string. The string may be destroyed by the
447 : * next CPL call.
448 : */
449 :
450 158196 : const char *CPLResetExtension(const char *pszPath, const char *pszExt)
451 :
452 : {
453 158196 : char *pszStaticResult = CPLGetStaticResult();
454 158195 : if (pszStaticResult == nullptr)
455 0 : return CPLStaticBufferTooSmall(pszStaticResult);
456 :
457 158195 : CPLAssert(!(pszPath >= pszStaticResult &&
458 : pszPath < pszStaticResult + CPL_PATH_BUF_SIZE));
459 :
460 : /* -------------------------------------------------------------------- */
461 : /* First, try and strip off any existing extension. */
462 : /* -------------------------------------------------------------------- */
463 158195 : if (CPLStrlcpy(pszStaticResult, pszPath, CPL_PATH_BUF_SIZE) >=
464 : static_cast<size_t>(CPL_PATH_BUF_SIZE))
465 0 : return CPLStaticBufferTooSmall(pszStaticResult);
466 :
467 158196 : if (*pszStaticResult)
468 : {
469 853576 : for (size_t i = strlen(pszStaticResult) - 1; i > 0; i--)
470 : {
471 853301 : if (pszStaticResult[i] == '.')
472 : {
473 141662 : pszStaticResult[i] = '\0';
474 141662 : break;
475 : }
476 :
477 711639 : if (pszStaticResult[i] == '/' || pszStaticResult[i] == '\\' ||
478 695432 : pszStaticResult[i] == ':')
479 : break;
480 : }
481 : }
482 :
483 : /* -------------------------------------------------------------------- */
484 : /* Append the new extension. */
485 : /* -------------------------------------------------------------------- */
486 158196 : if (CPLStrlcat(pszStaticResult, ".", CPL_PATH_BUF_SIZE) >=
487 316392 : static_cast<size_t>(CPL_PATH_BUF_SIZE) ||
488 158196 : CPLStrlcat(pszStaticResult, pszExt, CPL_PATH_BUF_SIZE) >=
489 : static_cast<size_t>(CPL_PATH_BUF_SIZE))
490 : {
491 0 : return CPLStaticBufferTooSmall(pszStaticResult);
492 : }
493 :
494 158196 : return pszStaticResult;
495 : }
496 :
497 : /************************************************************************/
498 : /* CPLFormFilename() */
499 : /************************************************************************/
500 :
501 : /**
502 : * Build a full file path from a passed path, file basename and extension.
503 : *
504 : * The path, and extension are optional. The basename may in fact contain
505 : * an extension if desired.
506 : *
507 : * <pre>
508 : * CPLFormFilename("abc/xyz", "def", ".dat" ) == "abc/xyz/def.dat"
509 : * CPLFormFilename(NULL,"def", NULL ) == "def"
510 : * CPLFormFilename(NULL, "abc/def.dat", NULL ) == "abc/def.dat"
511 : * CPLFormFilename("/abc/xyz/", "def.dat", NULL ) == "/abc/xyz/def.dat"
512 : * </pre>
513 : *
514 : * @param pszPath directory path to the directory containing the file. This
515 : * may be relative or absolute, and may have a trailing path separator or
516 : * not. May be NULL.
517 : *
518 : * @param pszBasename file basename. May optionally have path and/or
519 : * extension. Must *NOT* be NULL.
520 : *
521 : * @param pszExtension file extension, optionally including the period. May
522 : * be NULL.
523 : *
524 : * @return a fully formed filename in an internal static string. Do not
525 : * modify or free the returned string. The string may be destroyed by the
526 : * next CPL call.
527 : */
528 :
529 367760 : const char *CPLFormFilename(const char *pszPath, const char *pszBasename,
530 : const char *pszExtension)
531 :
532 : {
533 367760 : char *pszStaticResult = CPLGetStaticResult();
534 367757 : if (pszStaticResult == nullptr)
535 0 : return CPLStaticBufferTooSmall(pszStaticResult);
536 :
537 367757 : CPLAssert(!(pszPath >= pszStaticResult &&
538 : pszPath < pszStaticResult + CPL_PATH_BUF_SIZE));
539 367757 : CPLAssert(!(pszBasename >= pszStaticResult &&
540 : pszBasename < pszStaticResult + CPL_PATH_BUF_SIZE));
541 :
542 367757 : if (pszBasename[0] == '.' &&
543 9149 : (pszBasename[1] == '/' || pszBasename[1] == '\\'))
544 62 : pszBasename += 2;
545 :
546 367757 : const char *pszAddedPathSep = "";
547 367757 : const char *pszAddedExtSep = "";
548 :
549 367757 : if (pszPath == nullptr)
550 10862 : pszPath = "";
551 367757 : size_t nLenPath = strlen(pszPath);
552 :
553 367757 : size_t nSuffixPos = 0;
554 367757 : if (STARTS_WITH_CI(pszPath, "/vsicurl/http"))
555 : {
556 125 : const char *pszQuestionMark = strchr(pszPath, '?');
557 125 : if (pszQuestionMark)
558 : {
559 1 : nSuffixPos = static_cast<size_t>(pszQuestionMark - pszPath);
560 1 : nLenPath = nSuffixPos;
561 : }
562 125 : pszAddedPathSep = "/";
563 : }
564 :
565 367757 : if (!CPLIsFilenameRelative(pszPath) && strcmp(pszBasename, "..") == 0)
566 : {
567 : // /a/b + .. --> /a
568 251 : if (pszPath[nLenPath - 1] == '\\' || pszPath[nLenPath - 1] == '/')
569 9 : nLenPath--;
570 251 : size_t nLenPathOri = nLenPath;
571 4254 : while (nLenPath > 0 && pszPath[nLenPath - 1] != '\\' &&
572 4243 : pszPath[nLenPath - 1] != '/')
573 : {
574 4003 : nLenPath--;
575 : }
576 251 : if (nLenPath == 1 && pszPath[0] == '/')
577 : {
578 7 : pszBasename = "";
579 : }
580 244 : else if ((nLenPath > 1 && pszPath[0] == '/') ||
581 11 : (nLenPath > 2 && pszPath[1] == ':') ||
582 1 : (nLenPath > 6 && strncmp(pszPath, "\\\\$\\", 4) == 0))
583 : {
584 237 : nLenPath--;
585 237 : pszBasename = "";
586 : }
587 : else
588 : {
589 7 : nLenPath = nLenPathOri;
590 7 : if (pszAddedPathSep[0] == 0)
591 7 : pszAddedPathSep = VSIGetDirectorySeparator(pszPath);
592 : }
593 : }
594 367494 : else if (nLenPath > 0 && pszPath[nLenPath - 1] != '/' &&
595 354692 : pszPath[nLenPath - 1] != '\\')
596 : {
597 354689 : if (pszAddedPathSep[0] == 0)
598 354593 : pszAddedPathSep = VSIGetDirectorySeparator(pszPath);
599 : }
600 :
601 367768 : if (pszExtension == nullptr)
602 217949 : pszExtension = "";
603 149819 : else if (pszExtension[0] != '.' && strlen(pszExtension) > 0)
604 137104 : pszAddedExtSep = ".";
605 :
606 367768 : if (nLenPath >= static_cast<size_t>(CPL_PATH_BUF_SIZE))
607 0 : return CPLStaticBufferTooSmall(pszStaticResult);
608 :
609 367768 : memcpy(pszStaticResult, pszPath, nLenPath);
610 367768 : pszStaticResult[nLenPath] = 0;
611 :
612 367768 : if (CPLStrlcat(pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE) >=
613 367780 : static_cast<size_t>(CPL_PATH_BUF_SIZE) ||
614 367776 : CPLStrlcat(pszStaticResult, pszBasename, CPL_PATH_BUF_SIZE) >=
615 367784 : static_cast<size_t>(CPL_PATH_BUF_SIZE) ||
616 367774 : CPLStrlcat(pszStaticResult, pszAddedExtSep, CPL_PATH_BUF_SIZE) >=
617 735558 : static_cast<size_t>(CPL_PATH_BUF_SIZE) ||
618 367787 : CPLStrlcat(pszStaticResult, pszExtension, CPL_PATH_BUF_SIZE) >=
619 : static_cast<size_t>(CPL_PATH_BUF_SIZE))
620 : {
621 0 : return CPLStaticBufferTooSmall(pszStaticResult);
622 : }
623 :
624 367783 : if (nSuffixPos &&
625 1 : CPLStrlcat(pszStaticResult, pszPath + nSuffixPos, CPL_PATH_BUF_SIZE) >=
626 : static_cast<size_t>(CPL_PATH_BUF_SIZE))
627 : {
628 0 : return CPLStaticBufferTooSmall(pszStaticResult);
629 : }
630 :
631 367782 : return pszStaticResult;
632 : }
633 :
634 : /************************************************************************/
635 : /* CPLFormCIFilename() */
636 : /************************************************************************/
637 :
638 : /**
639 : * Case insensitive file searching, returning full path.
640 : *
641 : * This function tries to return the path to a file regardless of
642 : * whether the file exactly matches the basename, and extension case, or
643 : * is all upper case, or all lower case. The path is treated as case
644 : * sensitive. This function is equivalent to CPLFormFilename() on
645 : * case insensitive file systems (like Windows).
646 : *
647 : * @param pszPath directory path to the directory containing the file. This
648 : * may be relative or absolute, and may have a trailing path separator or
649 : * not. May be NULL.
650 : *
651 : * @param pszBasename file basename. May optionally have path and/or
652 : * extension. May not be NULL.
653 : *
654 : * @param pszExtension file extension, optionally including the period. May
655 : * be NULL.
656 : *
657 : * @return a fully formed filename in an internal static string. Do not
658 : * modify or free the returned string. The string may be destroyed by the
659 : * next CPL call.
660 : */
661 :
662 4628 : const char *CPLFormCIFilename(const char *pszPath, const char *pszBasename,
663 : const char *pszExtension)
664 :
665 : {
666 : // On case insensitive filesystems, just default to CPLFormFilename().
667 4628 : if (!VSIIsCaseSensitiveFS(pszPath))
668 0 : return CPLFormFilename(pszPath, pszBasename, pszExtension);
669 :
670 4628 : const char *pszAddedExtSep = "";
671 4628 : size_t nLen = strlen(pszBasename) + 2;
672 :
673 4628 : if (pszExtension != nullptr)
674 2195 : nLen += strlen(pszExtension);
675 :
676 4628 : char *pszFilename = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
677 4628 : if (pszFilename == nullptr)
678 0 : return "";
679 :
680 4628 : if (pszExtension == nullptr)
681 2433 : pszExtension = "";
682 2195 : else if (pszExtension[0] != '.' && strlen(pszExtension) > 0)
683 2143 : pszAddedExtSep = ".";
684 :
685 4628 : snprintf(pszFilename, nLen, "%s%s%s", pszBasename, pszAddedExtSep,
686 : pszExtension);
687 :
688 4628 : const char *pszFullPath = CPLFormFilename(pszPath, pszFilename, nullptr);
689 : VSIStatBufL sStatBuf;
690 4628 : int nStatRet = VSIStatExL(pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG);
691 4628 : if (nStatRet != 0)
692 : {
693 55278 : for (size_t i = 0; pszFilename[i] != '\0'; i++)
694 : {
695 51136 : pszFilename[i] = static_cast<char>(CPLToupper(pszFilename[i]));
696 : }
697 :
698 4142 : pszFullPath = CPLFormFilename(pszPath, pszFilename, nullptr);
699 4142 : nStatRet = VSIStatExL(pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG);
700 : }
701 :
702 4628 : if (nStatRet != 0)
703 : {
704 55254 : for (size_t i = 0; pszFilename[i] != '\0'; i++)
705 : {
706 51115 : pszFilename[i] = static_cast<char>(
707 51115 : CPLTolower(static_cast<unsigned char>(pszFilename[i])));
708 : }
709 :
710 4139 : pszFullPath = CPLFormFilename(pszPath, pszFilename, nullptr);
711 4139 : nStatRet = VSIStatExL(pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG);
712 : }
713 :
714 4628 : if (nStatRet != 0)
715 4131 : pszFullPath = CPLFormFilename(pszPath, pszBasename, pszExtension);
716 :
717 4628 : CPLFree(pszFilename);
718 :
719 4628 : return pszFullPath;
720 : }
721 :
722 : /************************************************************************/
723 : /* CPLProjectRelativeFilename() */
724 : /************************************************************************/
725 :
726 : /**
727 : * Find a file relative to a project file.
728 : *
729 : * Given the path to a "project" directory, and a path to a secondary file
730 : * referenced from that project, build a path to the secondary file
731 : * that the current application can use. If the secondary path is already
732 : * absolute, rather than relative, then it will be returned unaltered.
733 : *
734 : * Examples:
735 : * <pre>
736 : * CPLProjectRelativeFilename("abc/def", "tmp/abc.gif") == "abc/def/tmp/abc.gif"
737 : * CPLProjectRelativeFilename("abc/def", "/tmp/abc.gif") == "/tmp/abc.gif"
738 : * CPLProjectRelativeFilename("/xy", "abc.gif") == "/xy/abc.gif"
739 : * CPLProjectRelativeFilename("/abc/def", "../abc.gif") == "/abc/def/../abc.gif"
740 : * CPLProjectRelativeFilename("C:\WIN", "abc.gif") == "C:\WIN\abc.gif"
741 : * </pre>
742 : *
743 : * @param pszProjectDir the directory relative to which the secondary files
744 : * path should be interpreted.
745 : * @param pszSecondaryFilename the filename (potentially with path) that
746 : * is to be interpreted relative to the project directory.
747 : *
748 : * @return a composed path to the secondary file. The returned string is
749 : * internal and should not be altered, freed, or depending on past the next
750 : * CPL call.
751 : */
752 :
753 4255 : const char *CPLProjectRelativeFilename(const char *pszProjectDir,
754 : const char *pszSecondaryFilename)
755 :
756 : {
757 4255 : char *pszStaticResult = CPLGetStaticResult();
758 4255 : if (pszStaticResult == nullptr)
759 0 : return CPLStaticBufferTooSmall(pszStaticResult);
760 :
761 4255 : CPLAssert(!(pszProjectDir >= pszStaticResult &&
762 : pszProjectDir < pszStaticResult + CPL_PATH_BUF_SIZE));
763 4255 : CPLAssert(!(pszSecondaryFilename >= pszStaticResult &&
764 : pszSecondaryFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
765 :
766 4255 : if (!CPLIsFilenameRelative(pszSecondaryFilename))
767 451 : return pszSecondaryFilename;
768 :
769 3804 : if (pszProjectDir == nullptr || strlen(pszProjectDir) == 0)
770 216 : return pszSecondaryFilename;
771 :
772 3588 : if (CPLStrlcpy(pszStaticResult, pszProjectDir, CPL_PATH_BUF_SIZE) >=
773 : static_cast<size_t>(CPL_PATH_BUF_SIZE))
774 0 : return CPLStaticBufferTooSmall(pszStaticResult);
775 :
776 3588 : if (pszProjectDir[strlen(pszProjectDir) - 1] != '/' &&
777 3588 : pszProjectDir[strlen(pszProjectDir) - 1] != '\\')
778 : {
779 3588 : const char *pszAddedPathSep = VSIGetDirectorySeparator(pszProjectDir);
780 3588 : if (CPLStrlcat(pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE) >=
781 : static_cast<size_t>(CPL_PATH_BUF_SIZE))
782 0 : return CPLStaticBufferTooSmall(pszStaticResult);
783 : }
784 :
785 3588 : if (CPLStrlcat(pszStaticResult, pszSecondaryFilename, CPL_PATH_BUF_SIZE) >=
786 : static_cast<size_t>(CPL_PATH_BUF_SIZE))
787 0 : return CPLStaticBufferTooSmall(pszStaticResult);
788 :
789 3588 : return pszStaticResult;
790 : }
791 :
792 : /************************************************************************/
793 : /* CPLIsFilenameRelative() */
794 : /************************************************************************/
795 :
796 : /**
797 : * Is filename relative or absolute?
798 : *
799 : * The test is filesystem convention agnostic. That is it will test for
800 : * Unix style and windows style path conventions regardless of the actual
801 : * system in use.
802 : *
803 : * @param pszFilename the filename with path to test.
804 : *
805 : * @return TRUE if the filename is relative or FALSE if it is absolute.
806 : */
807 :
808 389639 : int CPLIsFilenameRelative(const char *pszFilename)
809 :
810 : {
811 389639 : if ((pszFilename[0] != '\0' &&
812 378619 : (STARTS_WITH(pszFilename + 1, ":\\") ||
813 378586 : STARTS_WITH(pszFilename + 1, ":/") ||
814 378586 : strstr(pszFilename + 1, "://") // http://, ftp:// etc....
815 389086 : )) ||
816 389086 : STARTS_WITH(pszFilename, "\\\\?\\") // Windows extended Length Path.
817 389067 : || pszFilename[0] == '\\' || pszFilename[0] == '/')
818 266801 : return FALSE;
819 :
820 122838 : return TRUE;
821 : }
822 :
823 : /************************************************************************/
824 : /* CPLExtractRelativePath() */
825 : /************************************************************************/
826 :
827 : /**
828 : * Get relative path from directory to target file.
829 : *
830 : * Computes a relative path for pszTarget relative to pszBaseDir.
831 : * Currently this only works if they share a common base path. The returned
832 : * path is normally into the pszTarget string. It should only be considered
833 : * valid as long as pszTarget is valid or till the next call to
834 : * this function, whichever comes first.
835 : *
836 : * @param pszBaseDir the name of the directory relative to which the path
837 : * should be computed. pszBaseDir may be NULL in which case the original
838 : * target is returned without relativizing.
839 : *
840 : * @param pszTarget the filename to be changed to be relative to pszBaseDir.
841 : *
842 : * @param pbGotRelative Pointer to location in which a flag is placed
843 : * indicating that the returned path is relative to the basename (TRUE) or
844 : * not (FALSE). This pointer may be NULL if flag is not desired.
845 : *
846 : * @return an adjusted path or the original if it could not be made relative
847 : * to the pszBaseFile's path.
848 : **/
849 :
850 2673 : const char *CPLExtractRelativePath(const char *pszBaseDir,
851 : const char *pszTarget, int *pbGotRelative)
852 :
853 : {
854 : /* -------------------------------------------------------------------- */
855 : /* If we don't have a basedir, then we can't relativize the path. */
856 : /* -------------------------------------------------------------------- */
857 2673 : if (pszBaseDir == nullptr)
858 : {
859 0 : if (pbGotRelative != nullptr)
860 0 : *pbGotRelative = FALSE;
861 :
862 0 : return pszTarget;
863 : }
864 :
865 2673 : const size_t nBasePathLen = strlen(pszBaseDir);
866 :
867 : /* -------------------------------------------------------------------- */
868 : /* One simple case is when the base dir is '.' and the target */
869 : /* filename is relative. */
870 : /* -------------------------------------------------------------------- */
871 2695 : if ((nBasePathLen == 0 || EQUAL(pszBaseDir, ".")) &&
872 22 : CPLIsFilenameRelative(pszTarget))
873 : {
874 22 : if (pbGotRelative != nullptr)
875 22 : *pbGotRelative = TRUE;
876 :
877 22 : return pszTarget;
878 : }
879 :
880 : /* -------------------------------------------------------------------- */
881 : /* By this point, if we don't have a base path, we can't have a */
882 : /* meaningful common prefix. */
883 : /* -------------------------------------------------------------------- */
884 2651 : if (nBasePathLen == 0)
885 : {
886 0 : if (pbGotRelative != nullptr)
887 0 : *pbGotRelative = FALSE;
888 :
889 0 : return pszTarget;
890 : }
891 :
892 : /* -------------------------------------------------------------------- */
893 : /* If we don't have a common path prefix, then we can't get a */
894 : /* relative path. */
895 : /* -------------------------------------------------------------------- */
896 2651 : if (!EQUALN(pszBaseDir, pszTarget, nBasePathLen) ||
897 2218 : (pszTarget[nBasePathLen] != '\\' && pszTarget[nBasePathLen] != '/'))
898 : {
899 434 : if (pbGotRelative != nullptr)
900 434 : *pbGotRelative = FALSE;
901 :
902 434 : return pszTarget;
903 : }
904 :
905 : /* -------------------------------------------------------------------- */
906 : /* We have a relative path. Strip it off to get a string to */
907 : /* return. */
908 : /* -------------------------------------------------------------------- */
909 2217 : if (pbGotRelative != nullptr)
910 2148 : *pbGotRelative = TRUE;
911 :
912 2217 : return pszTarget + nBasePathLen + 1;
913 : }
914 :
915 : /************************************************************************/
916 : /* CPLCleanTrailingSlash() */
917 : /************************************************************************/
918 :
919 : /**
920 : * Remove trailing forward/backward slash from the path for UNIX/Windows resp.
921 : *
922 : * Returns a string containing the portion of the passed path string with
923 : * trailing slash removed. If there is no path in the passed filename
924 : * an empty string will be returned (not NULL).
925 : *
926 : * <pre>
927 : * CPLCleanTrailingSlash( "abc/def/" ) == "abc/def"
928 : * CPLCleanTrailingSlash( "abc/def" ) == "abc/def"
929 : * CPLCleanTrailingSlash( "c:\\abc\\def\\" ) == "c:\\abc\\def"
930 : * CPLCleanTrailingSlash( "c:\\abc\\def" ) == "c:\\abc\\def"
931 : * CPLCleanTrailingSlash( "abc" ) == "abc"
932 : * </pre>
933 : *
934 : * @param pszPath the path to be cleaned up
935 : *
936 : * @return Path in an internal string which must not be freed. The string
937 : * may be destroyed by the next CPL filename handling call.
938 : */
939 :
940 9 : const char *CPLCleanTrailingSlash(const char *pszPath)
941 :
942 : {
943 9 : char *pszStaticResult = CPLGetStaticResult();
944 9 : if (pszStaticResult == nullptr)
945 0 : return CPLStaticBufferTooSmall(pszStaticResult);
946 9 : CPLAssert(!(pszPath >= pszStaticResult &&
947 : pszPath < pszStaticResult + CPL_PATH_BUF_SIZE));
948 :
949 9 : const size_t iPathLength = strlen(pszPath);
950 9 : if (iPathLength >= static_cast<size_t>(CPL_PATH_BUF_SIZE))
951 0 : return CPLStaticBufferTooSmall(pszStaticResult);
952 :
953 9 : CPLStrlcpy(pszStaticResult, pszPath, iPathLength + 1);
954 :
955 9 : if (iPathLength > 0 && (pszStaticResult[iPathLength - 1] == '\\' ||
956 9 : pszStaticResult[iPathLength - 1] == '/'))
957 0 : pszStaticResult[iPathLength - 1] = '\0';
958 :
959 9 : return pszStaticResult;
960 : }
961 :
962 : /************************************************************************/
963 : /* CPLCorrespondingPaths() */
964 : /************************************************************************/
965 :
966 : /**
967 : * Identify corresponding paths.
968 : *
969 : * Given a prototype old and new filename this function will attempt
970 : * to determine corresponding names for a set of other old filenames that
971 : * will rename them in a similar manner. This correspondence assumes there
972 : * are two possibly kinds of renaming going on. A change of path, and a
973 : * change of filename stem.
974 : *
975 : * If a consistent renaming cannot be established for all the files this
976 : * function will return indicating an error.
977 : *
978 : * The returned file list becomes owned by the caller and should be destroyed
979 : * with CSLDestroy().
980 : *
981 : * @param pszOldFilename path to old prototype file.
982 : * @param pszNewFilename path to new prototype file.
983 : * @param papszFileList list of other files associated with pszOldFilename to
984 : * rename similarly.
985 : *
986 : * @return a list of files corresponding to papszFileList but renamed to
987 : * correspond to pszNewFilename.
988 : */
989 :
990 179 : char **CPLCorrespondingPaths(const char *pszOldFilename,
991 : const char *pszNewFilename, char **papszFileList)
992 :
993 : {
994 179 : if (CSLCount(papszFileList) == 0)
995 0 : return nullptr;
996 :
997 : /* -------------------------------------------------------------------- */
998 : /* There is a special case for a one item list which exactly */
999 : /* matches the old name, to rename to the new name. */
1000 : /* -------------------------------------------------------------------- */
1001 347 : if (CSLCount(papszFileList) == 1 &&
1002 168 : strcmp(pszOldFilename, papszFileList[0]) == 0)
1003 : {
1004 168 : return CSLAddString(nullptr, pszNewFilename);
1005 : }
1006 :
1007 22 : const CPLString osOldPath = CPLGetPath(pszOldFilename);
1008 22 : const CPLString osOldBasename = CPLGetBasename(pszOldFilename);
1009 22 : const CPLString osNewBasename = CPLGetBasename(pszNewFilename);
1010 :
1011 : /* -------------------------------------------------------------------- */
1012 : /* If the basename is changing, verify that all source files */
1013 : /* have the same starting basename. */
1014 : /* -------------------------------------------------------------------- */
1015 11 : if (osOldBasename != osNewBasename)
1016 : {
1017 34 : for (int i = 0; papszFileList[i] != nullptr; i++)
1018 : {
1019 24 : if (osOldBasename == CPLGetBasename(papszFileList[i]))
1020 16 : continue;
1021 :
1022 8 : const CPLString osFilePath = CPLGetPath(papszFileList[i]);
1023 8 : const CPLString osFileName = CPLGetFilename(papszFileList[i]);
1024 :
1025 8 : if (!EQUALN(osFileName, osOldBasename, osOldBasename.size()) ||
1026 16 : !EQUAL(osFilePath, osOldPath) ||
1027 8 : osFileName[osOldBasename.size()] != '.')
1028 : {
1029 0 : CPLError(CE_Failure, CPLE_AppDefined,
1030 : "Unable to rename fileset due irregular basenames.");
1031 0 : return nullptr;
1032 : }
1033 : }
1034 : }
1035 :
1036 : /* -------------------------------------------------------------------- */
1037 : /* If the filename portions differs, ensure they only differ in */
1038 : /* basename. */
1039 : /* -------------------------------------------------------------------- */
1040 11 : if (osOldBasename != osNewBasename)
1041 : {
1042 : const CPLString osOldExtra =
1043 10 : CPLGetFilename(pszOldFilename) + osOldBasename.size();
1044 : const CPLString osNewExtra =
1045 10 : CPLGetFilename(pszNewFilename) + osNewBasename.size();
1046 :
1047 10 : if (osOldExtra != osNewExtra)
1048 : {
1049 0 : CPLError(CE_Failure, CPLE_AppDefined,
1050 : "Unable to rename fileset due to irregular filename "
1051 : "correspondence.");
1052 0 : return nullptr;
1053 : }
1054 : }
1055 :
1056 : /* -------------------------------------------------------------------- */
1057 : /* Generate the new filenames. */
1058 : /* -------------------------------------------------------------------- */
1059 11 : char **papszNewList = nullptr;
1060 11 : const CPLString osNewPath = CPLGetPath(pszNewFilename);
1061 :
1062 37 : for (int i = 0; papszFileList[i] != nullptr; i++)
1063 : {
1064 52 : const CPLString osOldFilename = CPLGetFilename(papszFileList[i]);
1065 :
1066 : const CPLString osNewFilename =
1067 26 : osOldBasename == osNewBasename
1068 2 : ? CPLFormFilename(osNewPath, osOldFilename, nullptr)
1069 24 : : CPLFormFilename(osNewPath, osNewBasename,
1070 52 : osOldFilename.c_str() + osOldBasename.size());
1071 :
1072 26 : papszNewList = CSLAddString(papszNewList, osNewFilename);
1073 : }
1074 :
1075 11 : return papszNewList;
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* CPLGenerateTempFilename() */
1080 : /************************************************************************/
1081 :
1082 : /**
1083 : * Generate temporary file name.
1084 : *
1085 : * Returns a filename that may be used for a temporary file. The location
1086 : * of the file tries to follow operating system semantics but may be
1087 : * forced via the CPL_TMPDIR configuration option.
1088 : *
1089 : * @param pszStem if non-NULL this will be part of the filename.
1090 : *
1091 : * @return a filename which is valid till the next CPL call in this thread.
1092 : */
1093 :
1094 2326 : const char *CPLGenerateTempFilename(const char *pszStem)
1095 :
1096 : {
1097 2326 : const char *pszDir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
1098 :
1099 2326 : if (pszDir == nullptr)
1100 2324 : pszDir = CPLGetConfigOption("TMPDIR", nullptr);
1101 :
1102 2326 : if (pszDir == nullptr)
1103 2324 : pszDir = CPLGetConfigOption("TEMP", nullptr);
1104 :
1105 2326 : if (pszDir == nullptr)
1106 2324 : pszDir = ".";
1107 :
1108 2326 : if (pszStem == nullptr)
1109 2157 : pszStem = "";
1110 :
1111 : static int nTempFileCounter = 0;
1112 4652 : CPLString osFilename;
1113 : osFilename.Printf("%s_%d_%d", pszStem, CPLGetCurrentProcessID(),
1114 2326 : CPLAtomicInc(&nTempFileCounter));
1115 :
1116 4652 : return CPLFormFilename(pszDir, osFilename, nullptr);
1117 : }
1118 :
1119 : /************************************************************************/
1120 : /* CPLExpandTilde() */
1121 : /************************************************************************/
1122 :
1123 : /**
1124 : * Expands ~/ at start of filename.
1125 : *
1126 : * Assumes that the HOME configuration option is defined.
1127 : *
1128 : * @param pszFilename filename potentially starting with ~/
1129 : *
1130 : * @return an expanded filename.
1131 : *
1132 : * @since GDAL 2.2
1133 : */
1134 :
1135 188 : const char *CPLExpandTilde(const char *pszFilename)
1136 :
1137 : {
1138 188 : if (!STARTS_WITH_CI(pszFilename, "~/"))
1139 187 : return pszFilename;
1140 :
1141 1 : const char *pszHome = CPLGetConfigOption("HOME", nullptr);
1142 1 : if (pszHome == nullptr)
1143 0 : return pszFilename;
1144 :
1145 1 : return CPLFormFilename(pszHome, pszFilename + 2, nullptr);
1146 : }
1147 :
1148 : /************************************************************************/
1149 : /* CPLGetHomeDir() */
1150 : /************************************************************************/
1151 :
1152 : /**
1153 : * Return the path to the home directory
1154 : *
1155 : * That is the value of the USERPROFILE environment variable on Windows,
1156 : * or HOME on other platforms.
1157 : *
1158 : * @return the home directory, or NULL.
1159 : *
1160 : * @since GDAL 2.3
1161 : */
1162 :
1163 0 : const char *CPLGetHomeDir()
1164 :
1165 : {
1166 : #ifdef _WIN32
1167 : return CPLGetConfigOption("USERPROFILE", nullptr);
1168 : #else
1169 0 : return CPLGetConfigOption("HOME", nullptr);
1170 : #endif
1171 : }
1172 :
1173 : /************************************************************************/
1174 : /* CPLLaunderForFilename() */
1175 : /************************************************************************/
1176 :
1177 : /**
1178 : * Launder a string to be compatible of a filename.
1179 : *
1180 : * @param pszName The input string to launder.
1181 : * @param pszOutputPath The directory where the file would be created.
1182 : * Unused for now. May be NULL.
1183 : * @return the laundered name.
1184 : *
1185 : * @since GDAL 3.1
1186 : */
1187 :
1188 1173 : const char *CPLLaunderForFilename(const char *pszName,
1189 : CPL_UNUSED const char *pszOutputPath)
1190 : {
1191 2346 : std::string osRet(pszName);
1192 10970 : for (char &ch : osRet)
1193 : {
1194 : // https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
1195 9797 : if (ch == '<' || ch == '>' || ch == ':' || ch == '"' || ch == '/' ||
1196 9791 : ch == '\\' || ch == '?' || ch == '*')
1197 : {
1198 9 : ch = '_';
1199 : }
1200 : }
1201 2346 : return CPLSPrintf("%s", osRet.c_str());
1202 : }
|