1 : /**********************************************************************
2 : * $Id: cpl_path.cpp 21583 2011-01-27 02:35:12Z warmerdam $
3 : *
4 : * Project: CPL - Common Portability Library
5 : * Purpose: Portable filename/path parsing, and forming ala "Glob API".
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : **********************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_conv.h"
31 : #include "cpl_string.h"
32 : #include "cpl_multiproc.h"
33 :
34 : CPL_CVSID("$Id: cpl_path.cpp 21583 2011-01-27 02:35:12Z warmerdam $");
35 :
36 :
37 : /* should be size of larged possible filename */
38 : #define CPL_PATH_BUF_SIZE 2048
39 : #define CPL_PATH_BUF_COUNT 10
40 :
41 : #if defined(WIN32) || defined(WIN32CE)
42 : #define SEP_CHAR '\\'
43 : #define SEP_STRING "\\"
44 : #else
45 : #define SEP_CHAR '/'
46 : #define SEP_STRING "/"
47 : #endif
48 :
49 0 : static const char* CPLStaticBufferTooSmall(char *pszStaticResult)
50 : {
51 0 : CPLError(CE_Failure, CPLE_AppDefined, "Destination buffer too small");
52 0 : strcpy( pszStaticResult, "" );
53 0 : return pszStaticResult;
54 : }
55 :
56 : /************************************************************************/
57 : /* CPLGetStaticResult() */
58 : /************************************************************************/
59 :
60 943362 : static char *CPLGetStaticResult()
61 :
62 : {
63 943362 : char *pachBufRingInfo = (char *) CPLGetTLS( CTLS_PATHBUF );
64 943362 : if( pachBufRingInfo == NULL )
65 : {
66 1341 : pachBufRingInfo = (char *) CPLCalloc(1, sizeof(int) + CPL_PATH_BUF_SIZE * CPL_PATH_BUF_COUNT);
67 1341 : 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 943362 : int *pnBufIndex = (int *) pachBufRingInfo;
75 943362 : int nOffset = sizeof(int) + *pnBufIndex * CPL_PATH_BUF_SIZE;
76 943362 : char *pachBuffer = pachBufRingInfo + nOffset;
77 :
78 943362 : *pnBufIndex = (*pnBufIndex + 1) % CPL_PATH_BUF_COUNT;
79 :
80 943362 : return pachBuffer;
81 : }
82 :
83 :
84 : /************************************************************************/
85 : /* CPLFindFilenameStart() */
86 : /************************************************************************/
87 :
88 940713 : static int CPLFindFilenameStart( const char * pszFilename )
89 :
90 : {
91 : size_t iFileStart;
92 :
93 23230154 : for( iFileStart = strlen(pszFilename);
94 : iFileStart > 0
95 11550295 : && pszFilename[iFileStart-1] != '/'
96 10739146 : && pszFilename[iFileStart-1] != '\\';
97 : iFileStart-- ) {}
98 :
99 940713 : return (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 12809 : const char *CPLGetPath( const char *pszFilename )
129 :
130 : {
131 12809 : int iFileStart = CPLFindFilenameStart(pszFilename);
132 12809 : char *pszStaticResult = CPLGetStaticResult();
133 :
134 12809 : if( iFileStart >= CPL_PATH_BUF_SIZE )
135 0 : return CPLStaticBufferTooSmall(pszStaticResult);
136 :
137 12809 : CPLAssert( ! (pszFilename >= pszStaticResult && pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
138 :
139 12809 : if( iFileStart == 0 )
140 : {
141 128 : strcpy( pszStaticResult, "" );
142 128 : return pszStaticResult;
143 : }
144 :
145 12681 : CPLStrlcpy( pszStaticResult, pszFilename, iFileStart+1 );
146 :
147 25362 : if( iFileStart > 1
148 12681 : && (pszStaticResult[iFileStart-1] == '/'
149 0 : || pszStaticResult[iFileStart-1] == '\\') )
150 12681 : pszStaticResult[iFileStart-1] = '\0';
151 :
152 12681 : return pszStaticResult;
153 : }
154 :
155 : /************************************************************************/
156 : /* CPLGetDirname() */
157 : /************************************************************************/
158 :
159 : /**
160 : * Extract directory path portion of filename.
161 : *
162 : * Returns a string containing the directory path portion of the passed
163 : * filename. If there is no path in the passed filename the dot will be
164 : * returned. It is the only difference from CPLGetPath().
165 : *
166 : * <pre>
167 : * CPLGetDirname( "abc/def.xyz" ) == "abc"
168 : * CPLGetDirname( "/abc/def/" ) == "/abc/def"
169 : * CPLGetDirname( "/" ) == "/"
170 : * CPLGetDirname( "/abc/def" ) == "/abc"
171 : * CPLGetDirname( "abc" ) == "."
172 : * </pre>
173 : *
174 : * @param pszFilename the filename potentially including a path.
175 : *
176 : * @return Path in an internal string which must not be freed. The string
177 : * may be destroyed by the next CPL filename handling call. The returned
178 : * will generally not contain a trailing path separator.
179 : */
180 :
181 17228 : const char *CPLGetDirname( const char *pszFilename )
182 :
183 : {
184 17228 : int iFileStart = CPLFindFilenameStart(pszFilename);
185 17228 : char *pszStaticResult = CPLGetStaticResult();
186 :
187 17228 : if( iFileStart >= CPL_PATH_BUF_SIZE )
188 0 : return CPLStaticBufferTooSmall(pszStaticResult);
189 :
190 17228 : CPLAssert( ! (pszFilename >= pszStaticResult && pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
191 :
192 17228 : if( iFileStart == 0 )
193 : {
194 10 : strcpy( pszStaticResult, "." );
195 10 : return pszStaticResult;
196 : }
197 :
198 17218 : CPLStrlcpy( pszStaticResult, pszFilename, iFileStart+1 );
199 :
200 34438 : if( iFileStart > 1
201 17218 : && (pszStaticResult[iFileStart-1] == '/'
202 2 : || pszStaticResult[iFileStart-1] == '\\') )
203 17218 : pszStaticResult[iFileStart-1] = '\0';
204 :
205 17218 : return pszStaticResult;
206 : }
207 :
208 : /************************************************************************/
209 : /* CPLGetFilename() */
210 : /************************************************************************/
211 :
212 : /**
213 : * Extract non-directory portion of filename.
214 : *
215 : * Returns a string containing the bare filename portion of the passed
216 : * filename. If there is no filename (passed value ends in trailing directory
217 : * separator) an empty string is returned.
218 : *
219 : * <pre>
220 : * CPLGetFilename( "abc/def.xyz" ) == "def.xyz"
221 : * CPLGetFilename( "/abc/def/" ) == ""
222 : * CPLGetFilename( "abc/def" ) == "def"
223 : * </pre>
224 : *
225 : * @param pszFullFilename the full filename potentially including a path.
226 : *
227 : * @return just the non-directory portion of the path (points back into original string).
228 : */
229 :
230 123995 : const char *CPLGetFilename( const char *pszFullFilename )
231 :
232 : {
233 123995 : int iFileStart = CPLFindFilenameStart( pszFullFilename );
234 :
235 123995 : return pszFullFilename + iFileStart;
236 : }
237 :
238 : /************************************************************************/
239 : /* CPLGetBasename() */
240 : /************************************************************************/
241 :
242 : /**
243 : * Extract basename (non-directory, non-extension) portion of filename.
244 : *
245 : * Returns a string containing the file basename portion of the passed
246 : * name. If there is no basename (passed value ends in trailing directory
247 : * separator, or filename starts with a dot) an empty string is returned.
248 : *
249 : * <pre>
250 : * CPLGetBasename( "abc/def.xyz" ) == "def"
251 : * CPLGetBasename( "abc/def" ) == "def"
252 : * CPLGetBasename( "abc/def/" ) == ""
253 : * </pre>
254 : *
255 : * @param pszFullFilename the full filename potentially including a path.
256 : *
257 : * @return just the non-directory, non-extension portion of the path in
258 : * an internal string which must not be freed. The string
259 : * may be destroyed by the next CPL filename handling call.
260 : */
261 :
262 539984 : const char *CPLGetBasename( const char *pszFullFilename )
263 :
264 : {
265 539984 : size_t iFileStart = CPLFindFilenameStart( pszFullFilename );
266 : size_t iExtStart, nLength;
267 539984 : char *pszStaticResult = CPLGetStaticResult();
268 :
269 539984 : CPLAssert( ! (pszFullFilename >= pszStaticResult && pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
270 :
271 3245558 : for( iExtStart = strlen(pszFullFilename);
272 2705574 : iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
273 : iExtStart-- ) {}
274 :
275 539984 : if( iExtStart == iFileStart )
276 4746 : iExtStart = strlen(pszFullFilename);
277 :
278 539984 : nLength = iExtStart - iFileStart;
279 :
280 539984 : if (nLength >= CPL_PATH_BUF_SIZE)
281 0 : return CPLStaticBufferTooSmall(pszStaticResult);
282 :
283 539984 : CPLStrlcpy( pszStaticResult, pszFullFilename + iFileStart, nLength + 1 );
284 :
285 539984 : return pszStaticResult;
286 : }
287 :
288 :
289 : /************************************************************************/
290 : /* CPLGetExtension() */
291 : /************************************************************************/
292 :
293 : /**
294 : * Extract filename extension from full filename.
295 : *
296 : * Returns a string containing the extention portion of the passed
297 : * name. If there is no extension (the filename has no dot) an empty string
298 : * is returned. The returned extension will not include the period.
299 : *
300 : * <pre>
301 : * CPLGetExtension( "abc/def.xyz" ) == "xyz"
302 : * CPLGetExtension( "abc/def" ) == ""
303 : * </pre>
304 : *
305 : * @param pszFullFilename the full filename potentially including a path.
306 : *
307 : * @return just the extension portion of the path in
308 : * an internal string which must not be freed. The string
309 : * may be destroyed by the next CPL filename handling call.
310 : */
311 :
312 246697 : const char *CPLGetExtension( const char *pszFullFilename )
313 :
314 : {
315 246697 : size_t iFileStart = CPLFindFilenameStart( pszFullFilename );
316 : size_t iExtStart;
317 246697 : char *pszStaticResult = CPLGetStaticResult();
318 :
319 246697 : CPLAssert( ! (pszFullFilename >= pszStaticResult && pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
320 :
321 1751717 : for( iExtStart = strlen(pszFullFilename);
322 1505020 : iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
323 : iExtStart-- ) {}
324 :
325 246697 : if( iExtStart == iFileStart )
326 138986 : iExtStart = strlen(pszFullFilename)-1;
327 :
328 246697 : if (CPLStrlcpy( pszStaticResult, pszFullFilename+iExtStart+1, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
329 0 : return CPLStaticBufferTooSmall(pszStaticResult);
330 :
331 246697 : return pszStaticResult;
332 : }
333 :
334 : /************************************************************************/
335 : /* CPLGetCurrentDir() */
336 : /************************************************************************/
337 :
338 : /**
339 : * Get the current working directory name.
340 : *
341 : * @return a pointer to buffer, containing current working directory path
342 : * or NULL in case of error. User is responsible to free that buffer
343 : * after usage with CPLFree() function.
344 : * If HAVE_GETCWD macro is not defined, the function returns NULL.
345 : **/
346 :
347 0 : char *CPLGetCurrentDir()
348 :
349 : {
350 : size_t nPathMax;
351 : char *pszDirPath;
352 :
353 : # ifdef _MAX_PATH
354 : nPathMax = _MAX_PATH;
355 : # elif PATH_MAX
356 0 : nPathMax = PATH_MAX;
357 : # else
358 : nPathMax = 8192;
359 : # endif
360 :
361 0 : pszDirPath = (char*)CPLMalloc( nPathMax );
362 0 : if ( !pszDirPath )
363 0 : return NULL;
364 :
365 : #ifdef HAVE_GETCWD
366 0 : return getcwd( pszDirPath, nPathMax );
367 : #else
368 : return NULL;
369 : #endif /* HAVE_GETCWD */
370 : }
371 :
372 : /************************************************************************/
373 : /* CPLResetExtension() */
374 : /************************************************************************/
375 :
376 : /**
377 : * Replace the extension with the provided one.
378 : *
379 : * @param pszPath the input path, this string is not altered.
380 : * @param pszExt the new extension to apply to the given path.
381 : *
382 : * @return an altered filename with the new extension. Do not
383 : * modify or free the returned string. The string may be destroyed by the
384 : * next CPL call.
385 : */
386 :
387 83390 : const char *CPLResetExtension( const char *pszPath, const char *pszExt )
388 :
389 : {
390 83390 : char *pszStaticResult = CPLGetStaticResult();
391 : size_t i;
392 :
393 83390 : CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) );
394 :
395 : /* -------------------------------------------------------------------- */
396 : /* First, try and strip off any existing extension. */
397 : /* -------------------------------------------------------------------- */
398 83390 : if (CPLStrlcpy( pszStaticResult, pszPath, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
399 0 : return CPLStaticBufferTooSmall(pszStaticResult);
400 :
401 83390 : if (*pszStaticResult)
402 : {
403 354724 : for( i = strlen(pszStaticResult) - 1; i > 0; i-- )
404 : {
405 354044 : if( pszStaticResult[i] == '.' )
406 : {
407 74810 : pszStaticResult[i] = '\0';
408 74810 : break;
409 : }
410 :
411 550584 : if( pszStaticResult[i] == '/' || pszStaticResult[i] == '\\'
412 271350 : || pszStaticResult[i] == ':' )
413 7888 : break;
414 : }
415 : }
416 :
417 : /* -------------------------------------------------------------------- */
418 : /* Append the new extension. */
419 : /* -------------------------------------------------------------------- */
420 83390 : if (CPLStrlcat( pszStaticResult, ".", CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
421 : CPLStrlcat( pszStaticResult, pszExt, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE)
422 0 : return CPLStaticBufferTooSmall(pszStaticResult);
423 :
424 83390 : return pszStaticResult;
425 : }
426 :
427 : /************************************************************************/
428 : /* CPLFormFilename() */
429 : /************************************************************************/
430 :
431 : /**
432 : * Build a full file path from a passed path, file basename and extension.
433 : *
434 : * The path, and extension are optional. The basename may in fact contain
435 : * an extension if desired.
436 : *
437 : * <pre>
438 : * CPLFormFilename("abc/xyz","def", ".dat" ) == "abc/xyz/def.dat"
439 : * CPLFormFilename(NULL,"def", NULL ) == "def"
440 : * CPLFormFilename(NULL,"abc/def.dat", NULL ) == "abc/def.dat"
441 : * CPLFormFilename("/abc/xyz/","def.dat", NULL ) == "/abc/xyz/def.dat"
442 : * </pre>
443 : *
444 : * @param pszPath directory path to the directory containing the file. This
445 : * may be relative or absolute, and may have a trailing path separator or
446 : * not. May be NULL.
447 : *
448 : * @param pszBasename file basename. May optionally have path and/or
449 : * extension. May not be NULL.
450 : *
451 : * @param pszExtension file extension, optionally including the period. May
452 : * be NULL.
453 : *
454 : * @return a fully formed filename in an internal static string. Do not
455 : * modify or free the returned string. The string may be destroyed by the
456 : * next CPL call.
457 : */
458 :
459 42576 : const char *CPLFormFilename( const char * pszPath,
460 : const char * pszBasename,
461 : const char * pszExtension )
462 :
463 : {
464 42576 : char *pszStaticResult = CPLGetStaticResult();
465 42576 : const char *pszAddedPathSep = "";
466 42576 : const char *pszAddedExtSep = "";
467 :
468 42576 : CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) );
469 42576 : CPLAssert( ! (pszBasename >= pszStaticResult && pszBasename < pszStaticResult + CPL_PATH_BUF_SIZE) );
470 :
471 42576 : if( pszPath == NULL )
472 9906 : pszPath = "";
473 98000 : else if( strlen(pszPath) > 0
474 32668 : && pszPath[strlen(pszPath)-1] != '/'
475 32662 : && pszPath[strlen(pszPath)-1] != '\\' )
476 : {
477 : /* FIXME? would be better to ask the filesystems what they */
478 : /* prefer as directory separator */
479 32662 : if (strncmp(pszPath, "/vsicurl/", 9) == 0)
480 38 : pszAddedPathSep = "/";
481 : else
482 32624 : pszAddedPathSep = SEP_STRING;
483 : }
484 :
485 42576 : if( pszExtension == NULL )
486 29866 : pszExtension = "";
487 12710 : else if( pszExtension[0] != '.' && strlen(pszExtension) > 0 )
488 12050 : pszAddedExtSep = ".";
489 :
490 42576 : if (CPLStrlcpy( pszStaticResult, pszPath, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
491 : CPLStrlcat( pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
492 : CPLStrlcat( pszStaticResult, pszBasename, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
493 : CPLStrlcat( pszStaticResult, pszAddedExtSep, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
494 : CPLStrlcat( pszStaticResult, pszExtension, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE)
495 0 : return CPLStaticBufferTooSmall(pszStaticResult);
496 :
497 42576 : return pszStaticResult;
498 : }
499 :
500 : /************************************************************************/
501 : /* CPLFormCIFilename() */
502 : /************************************************************************/
503 :
504 : /**
505 : * Case insensitive file searching, returing full path.
506 : *
507 : * This function tries to return the path to a file regardless of
508 : * whether the file exactly matches the basename, and extension case, or
509 : * is all upper case, or all lower case. The path is treated as case
510 : * sensitive. This function is equivelent to CPLFormFilename() on
511 : * case insensitive file systems (like Windows).
512 : *
513 : * @param pszPath directory path to the directory containing the file. This
514 : * may be relative or absolute, and may have a trailing path separator or
515 : * not. May be NULL.
516 : *
517 : * @param pszBasename file basename. May optionally have path and/or
518 : * extension. May not be NULL.
519 : *
520 : * @param pszExtension file extension, optionally including the period. May
521 : * be NULL.
522 : *
523 : * @return a fully formed filename in an internal static string. Do not
524 : * modify or free the returned string. The string may be destroyed by the
525 : * next CPL call.
526 : */
527 :
528 2360 : const char *CPLFormCIFilename( const char * pszPath,
529 : const char * pszBasename,
530 : const char * pszExtension )
531 :
532 : {
533 : // On case insensitive filesystems, just default to
534 : // CPLFormFilename()
535 2360 : if( !VSIIsCaseSensitiveFS(pszPath) )
536 0 : return CPLFormFilename( pszPath, pszBasename, pszExtension );
537 :
538 2360 : const char *pszAddedExtSep = "";
539 : char *pszFilename;
540 : const char *pszFullPath;
541 2360 : int nLen = strlen(pszBasename)+2, i;
542 : VSIStatBufL sStatBuf;
543 : int nStatRet;
544 :
545 2360 : if( pszExtension != NULL )
546 1964 : nLen += strlen(pszExtension);
547 :
548 2360 : pszFilename = (char *) CPLMalloc(nLen);
549 :
550 2360 : if( pszExtension == NULL )
551 396 : pszExtension = "";
552 1964 : else if( pszExtension[0] != '.' && strlen(pszExtension) > 0 )
553 1502 : pszAddedExtSep = ".";
554 :
555 : sprintf( pszFilename, "%s%s%s",
556 2360 : pszBasename, pszAddedExtSep, pszExtension );
557 :
558 2360 : pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
559 2360 : nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG );
560 2360 : if( nStatRet != 0 )
561 : {
562 19940 : for( i = 0; pszFilename[i] != '\0'; i++ )
563 : {
564 18198 : if( islower(pszFilename[i]) )
565 12302 : pszFilename[i] = (char) toupper(pszFilename[i]);
566 : }
567 :
568 1742 : pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
569 1742 : nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG );
570 : }
571 :
572 2360 : if( nStatRet != 0 )
573 : {
574 19908 : for( i = 0; pszFilename[i] != '\0'; i++ )
575 : {
576 18170 : if( isupper(pszFilename[i]) )
577 14808 : pszFilename[i] = (char) tolower(pszFilename[i]);
578 : }
579 :
580 1738 : pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
581 1738 : nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG );
582 : }
583 :
584 2360 : if( nStatRet != 0 )
585 1732 : pszFullPath = CPLFormFilename( pszPath, pszBasename, pszExtension );
586 :
587 2360 : CPLFree( pszFilename );
588 :
589 2360 : return pszFullPath;
590 : }
591 :
592 : /************************************************************************/
593 : /* CPLProjectRelativeFilename() */
594 : /************************************************************************/
595 :
596 : /**
597 : * Find a file relative to a project file.
598 : *
599 : * Given the path to a "project" directory, and a path to a secondary file
600 : * referenced from that project, build a path to the secondary file
601 : * that the current application can use. If the secondary path is already
602 : * absolute, rather than relative, then it will be returned unaltered.
603 : *
604 : * Examples:
605 : * <pre>
606 : * CPLProjectRelativeFilename("abc/def","tmp/abc.gif") == "abc/def/tmp/abc.gif"
607 : * CPLProjectRelativeFilename("abc/def","/tmp/abc.gif") == "/tmp/abc.gif"
608 : * CPLProjectRelativeFilename("/xy", "abc.gif") == "/xy/abc.gif"
609 : * CPLProjectRelativeFilename("/abc/def","../abc.gif") == "/abc/def/../abc.gif"
610 : * CPLProjectRelativeFilename("C:\WIN","abc.gif") == "C:\WIN\abc.gif"
611 : * </pre>
612 : *
613 : * @param pszProjectDir the directory relative to which the secondary files
614 : * path should be interpreted.
615 : * @param pszSecondaryFilename the filename (potentially with path) that
616 : * is to be interpreted relative to the project directory.
617 : *
618 : * @return a composed path to the secondary file. The returned string is
619 : * internal and should not be altered, freed, or depending on past the next
620 : * CPL call.
621 : */
622 :
623 664 : const char *CPLProjectRelativeFilename( const char *pszProjectDir,
624 : const char *pszSecondaryFilename )
625 :
626 : {
627 664 : char *pszStaticResult = CPLGetStaticResult();
628 :
629 664 : CPLAssert( ! (pszProjectDir >= pszStaticResult && pszProjectDir < pszStaticResult + CPL_PATH_BUF_SIZE) );
630 664 : CPLAssert( ! (pszSecondaryFilename >= pszStaticResult && pszSecondaryFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
631 :
632 664 : if( !CPLIsFilenameRelative( pszSecondaryFilename ) )
633 0 : return pszSecondaryFilename;
634 :
635 664 : if( pszProjectDir == NULL || strlen(pszProjectDir) == 0 )
636 0 : return pszSecondaryFilename;
637 :
638 664 : if (CPLStrlcpy( pszStaticResult, pszProjectDir, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
639 0 : goto error;
640 :
641 1328 : if( pszProjectDir[strlen(pszProjectDir)-1] != '/'
642 664 : && pszProjectDir[strlen(pszProjectDir)-1] != '\\' )
643 : {
644 : /* FIXME? would be better to ask the filesystems what they */
645 : /* prefer as directory separator */
646 : const char* pszAddedPathSep;
647 664 : if (strncmp(pszStaticResult, "/vsicurl/", 9) == 0)
648 0 : pszAddedPathSep = "/";
649 : else
650 664 : pszAddedPathSep = SEP_STRING;
651 664 : if (CPLStrlcat( pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
652 0 : goto error;
653 : }
654 :
655 664 : if (CPLStrlcat( pszStaticResult, pszSecondaryFilename, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
656 0 : goto error;
657 :
658 664 : return pszStaticResult;
659 : error:
660 0 : return CPLStaticBufferTooSmall(pszStaticResult);
661 : }
662 :
663 :
664 : /************************************************************************/
665 : /* CPLIsFilenameRelative() */
666 : /************************************************************************/
667 :
668 : /**
669 : * Is filename relative or absolute?
670 : *
671 : * The test is filesystem convention agnostic. That is it will test for
672 : * Unix style and windows style path conventions regardless of the actual
673 : * system in use.
674 : *
675 : * @param pszFilename the filename with path to test.
676 : *
677 : * @return TRUE if the filename is relative or FALSE if it is absolute.
678 : */
679 :
680 1120 : int CPLIsFilenameRelative( const char *pszFilename )
681 :
682 : {
683 3360 : if( (strlen(pszFilename) > 2
684 : && (strncmp(pszFilename+1,":\\",2) == 0
685 : || strncmp(pszFilename+1,":/",2) == 0))
686 1120 : || pszFilename[0] == '\\'
687 1120 : || pszFilename[0] == '/' )
688 0 : return FALSE;
689 : else
690 1120 : return TRUE;
691 : }
692 :
693 : /************************************************************************/
694 : /* CPLExtractRelativePath() */
695 : /************************************************************************/
696 :
697 : /**
698 : * Get relative path from directory to target file.
699 : *
700 : * Computes a relative path for pszTarget relative to pszBaseDir.
701 : * Currently this only works if they share a common base path. The returned
702 : * path is normally into the pszTarget string. It should only be considered
703 : * valid as long as pszTarget is valid or till the next call to
704 : * this function, whichever comes first.
705 : *
706 : * @param pszBaseDir the name of the directory relative to which the path
707 : * should be computed. pszBaseDir may be NULL in which case the original
708 : * target is returned without relitivizing.
709 : *
710 : * @param pszTarget the filename to be changed to be relative to pszBaseDir.
711 : *
712 : * @param pbGotRelative Pointer to location in which a flag is placed
713 : * indicating that the returned path is relative to the basename (TRUE) or
714 : * not (FALSE). This pointer may be NULL if flag is not desired.
715 : *
716 : * @return an adjusted path or the original if it could not be made relative
717 : * to the pszBaseFile's path.
718 : **/
719 :
720 394 : const char *CPLExtractRelativePath( const char *pszBaseDir,
721 : const char *pszTarget,
722 : int *pbGotRelative )
723 :
724 : {
725 : size_t nBasePathLen;
726 :
727 : /* -------------------------------------------------------------------- */
728 : /* If we don't have a basedir, then we can't relativize the path. */
729 : /* -------------------------------------------------------------------- */
730 394 : if( pszBaseDir == NULL )
731 : {
732 0 : if( pbGotRelative != NULL )
733 0 : *pbGotRelative = FALSE;
734 :
735 0 : return pszTarget;
736 : }
737 :
738 394 : nBasePathLen = strlen(pszBaseDir);
739 :
740 : /* -------------------------------------------------------------------- */
741 : /* One simple case is when the base dir is '.' and the target */
742 : /* filename is relative. */
743 : /* -------------------------------------------------------------------- */
744 394 : if( (nBasePathLen == 0 || EQUAL(pszBaseDir,"."))
745 : && CPLIsFilenameRelative(pszTarget) )
746 : {
747 0 : if( pbGotRelative != NULL )
748 0 : *pbGotRelative = TRUE;
749 :
750 0 : return pszTarget;
751 : }
752 :
753 : /* -------------------------------------------------------------------- */
754 : /* By this point, if we don't have a base path, we can't have a */
755 : /* meaningful common prefix. */
756 : /* -------------------------------------------------------------------- */
757 394 : if( nBasePathLen == 0 )
758 : {
759 0 : if( pbGotRelative != NULL )
760 0 : *pbGotRelative = FALSE;
761 :
762 0 : return pszTarget;
763 : }
764 :
765 : /* -------------------------------------------------------------------- */
766 : /* If we don't have a common path prefix, then we can't get a */
767 : /* relative path. */
768 : /* -------------------------------------------------------------------- */
769 626 : if( !EQUALN(pszBaseDir,pszTarget,nBasePathLen)
770 116 : || (pszTarget[nBasePathLen] != '\\'
771 116 : && pszTarget[nBasePathLen] != '/') )
772 : {
773 278 : if( pbGotRelative != NULL )
774 278 : *pbGotRelative = FALSE;
775 :
776 278 : return pszTarget;
777 : }
778 :
779 : /* -------------------------------------------------------------------- */
780 : /* We have a relative path. Strip it off to get a string to */
781 : /* return. */
782 : /* -------------------------------------------------------------------- */
783 116 : if( pbGotRelative != NULL )
784 116 : *pbGotRelative = TRUE;
785 :
786 116 : return pszTarget + nBasePathLen + 1;
787 : }
788 :
789 : /************************************************************************/
790 : /* CPLCleanTrailingSlash() */
791 : /************************************************************************/
792 :
793 : /**
794 : * Remove trailing forward/backward slash from the path for unix/windows resp.
795 : *
796 : * Returns a string containing the portion of the passed path string with
797 : * trailing slash removed. If there is no path in the passed filename
798 : * an empty string will be returned (not NULL).
799 : *
800 : * <pre>
801 : * CPLCleanTrailingSlash( "abc/def/" ) == "abc/def"
802 : * CPLCleanTrailingSlash( "abc/def" ) == "abc/def"
803 : * CPLCleanTrailingSlash( "c:\abc\def\" ) == "c:\abc\def"
804 : * CPLCleanTrailingSlash( "c:\abc\def" ) == "c:\abc\def"
805 : * CPLCleanTrailingSlash( "abc" ) == "abc"
806 : * </pre>
807 : *
808 : * @param pszPath the path to be cleaned up
809 : *
810 : * @return Path in an internal string which must not be freed. The string
811 : * may be destroyed by the next CPL filename handling call.
812 : */
813 :
814 14 : const char *CPLCleanTrailingSlash( const char *pszPath )
815 :
816 : {
817 14 : char *pszStaticResult = CPLGetStaticResult();
818 14 : int iPathLength = strlen(pszPath);
819 :
820 14 : CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) );
821 :
822 14 : if (iPathLength >= CPL_PATH_BUF_SIZE)
823 0 : return CPLStaticBufferTooSmall(pszStaticResult);
824 :
825 14 : CPLStrlcpy( pszStaticResult, pszPath, iPathLength+1 );
826 :
827 42 : if( iPathLength > 0
828 14 : && (pszStaticResult[iPathLength-1] == '\\'
829 14 : || pszStaticResult[iPathLength-1] == '/'))
830 0 : pszStaticResult[iPathLength-1] = '\0';
831 :
832 14 : return pszStaticResult;
833 : }
834 :
835 : /************************************************************************/
836 : /* CPLCorrespondingPaths() */
837 : /************************************************************************/
838 :
839 : /**
840 : * Identify corresponding paths.
841 : *
842 : * Given a prototype old and new filename this function will attempt
843 : * to determine corresponding names for a set of other old filenames that
844 : * will rename them in a similar manner. This correspondance assumes there
845 : * are two possibly kinds of renaming going on. A change of path, and a
846 : * change of filename stem.
847 : *
848 : * If a consistent renaming cannot be established for all the files this
849 : * function will return indicating an error.
850 : *
851 : * The returned file list becomes owned by the caller and should be destroyed
852 : * with CSLDestroy().
853 : *
854 : * @param pszOldFilename path to old prototype file.
855 : * @param pszNewFilename path to new prototype file.
856 : * @param papszFileList list of other files associated with pszOldFilename to
857 : * rename similarly.
858 : *
859 : * @return a list of files corresponding to papszFileList but renamed to
860 : * correspond to pszNewFilename.
861 : */
862 :
863 4 : char **CPLCorrespondingPaths( const char *pszOldFilename,
864 : const char *pszNewFilename,
865 : char **papszFileList )
866 :
867 : {
868 4 : CPLString osOldPath = CPLGetPath( pszOldFilename );
869 4 : CPLString osNewPath = CPLGetPath( pszNewFilename );
870 4 : CPLString osOldBasename = CPLGetBasename( pszOldFilename );
871 4 : CPLString osNewBasename = CPLGetBasename( pszNewFilename );
872 : int i;
873 :
874 4 : if( CSLCount(papszFileList) == 0 )
875 0 : return NULL;
876 :
877 : /* -------------------------------------------------------------------- */
878 : /* There is a special case for a one item list which exactly */
879 : /* matches the old name, to rename to the new name. */
880 : /* -------------------------------------------------------------------- */
881 4 : if( CSLCount(papszFileList) == 1
882 0 : && strcmp(pszOldFilename,papszFileList[0]) == 0 )
883 : {
884 0 : return CSLAddString( NULL, pszNewFilename );
885 : }
886 :
887 : /* -------------------------------------------------------------------- */
888 : /* If the basename is changing, verify that all source files */
889 : /* have the same starting basename. */
890 : /* -------------------------------------------------------------------- */
891 4 : if( osOldBasename != osNewBasename )
892 : {
893 4 : for( i=0; papszFileList[i] != NULL; i++ )
894 : {
895 16 : if( osOldBasename == CPLGetBasename( papszFileList[i] ) )
896 16 : continue;
897 :
898 0 : CPLString osFilePath, osFileName;
899 :
900 0 : osFilePath = CPLGetPath( papszFileList[i] );
901 0 : osFileName = CPLGetFilename( papszFileList[i] );
902 :
903 0 : if( !EQUALN(osFileName,osOldBasename,osOldBasename.size())
904 : || !EQUAL(osFilePath,osOldPath)
905 : || osFileName[osOldBasename.size()] != '.' )
906 : {
907 : CPLError( CE_Failure, CPLE_AppDefined,
908 0 : "Unable to rename fileset due irregular basenames.");
909 0 : return NULL;
910 : }
911 : }
912 : }
913 :
914 : /* -------------------------------------------------------------------- */
915 : /* If the filename portions differs, ensure they only differ in */
916 : /* basename. */
917 : /* -------------------------------------------------------------------- */
918 4 : if( osOldBasename != osNewBasename )
919 : {
920 : CPLString osOldExtra = CPLGetFilename(pszOldFilename)
921 4 : + strlen(osOldBasename);
922 : CPLString osNewExtra = CPLGetFilename(pszNewFilename)
923 4 : + strlen(osNewBasename);
924 :
925 4 : if( osOldExtra != osNewExtra )
926 : {
927 : CPLError( CE_Failure, CPLE_AppDefined,
928 0 : "Unable to rename fileset due to irregular filename correspondence." );
929 0 : return NULL;
930 0 : }
931 : }
932 :
933 : /* -------------------------------------------------------------------- */
934 : /* Generate the new filenames. */
935 : /* -------------------------------------------------------------------- */
936 4 : char **papszNewList = NULL;
937 :
938 20 : for( i=0; papszFileList[i] != NULL; i++ )
939 : {
940 16 : CPLString osNewFilename;
941 16 : CPLString osOldFilename = CPLGetFilename( papszFileList[i] );
942 :
943 16 : if( osOldBasename == osNewBasename )
944 : osNewFilename =
945 0 : CPLFormFilename( osNewPath, osOldFilename, NULL );
946 : else
947 : osNewFilename =
948 : CPLFormFilename( osNewPath, osNewBasename,
949 16 : osOldFilename.c_str()+strlen(osOldBasename));
950 :
951 16 : papszNewList = CSLAddString( papszNewList, osNewFilename );
952 : }
953 :
954 4 : return papszNewList;
955 : }
956 :
957 : /************************************************************************/
958 : /* CPLGenerateTempFilename() */
959 : /************************************************************************/
960 :
961 : /**
962 : * Generate temporary file name.
963 : *
964 : * Returns a filename that may be used for a temporary file. The location
965 : * of the file tries to follow operating system semantics but may be
966 : * forced via the CPL_TMPDIR configuration option.
967 : *
968 : * @param pszStem if non-NULL this will be part of the filename.
969 : *
970 : * @return a filename which is valid till the next CPL call in this thread.
971 : */
972 :
973 72 : const char *CPLGenerateTempFilename( const char *pszStem )
974 :
975 : {
976 72 : const char *pszDir = CPLGetConfigOption( "CPL_TMPDIR", NULL );
977 : static volatile int nTempFileCounter = 0;
978 :
979 72 : if( pszDir == NULL )
980 72 : pszDir = CPLGetConfigOption( "TMPDIR", NULL );
981 :
982 72 : if( pszDir == NULL )
983 72 : pszDir = CPLGetConfigOption( "TEMP", NULL );
984 :
985 72 : if( pszDir == NULL )
986 72 : pszDir = ".";
987 :
988 72 : CPLString osFilename;
989 :
990 72 : if( pszStem == NULL )
991 56 : pszStem = "";
992 :
993 : osFilename.Printf( "%s%u_%d", pszStem,
994 72 : (int) CPLGetPID(), nTempFileCounter++ );
995 :
996 72 : return CPLFormFilename( pszDir, osFilename, NULL );
997 : }
|