1 : /**********************************************************************
2 : * $Id: cpl_path.cpp 25181 2012-10-27 11:52:46Z rouault $
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 25181 2012-10-27 11:52:46Z rouault $");
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 510616 : static char *CPLGetStaticResult()
61 :
62 : {
63 510616 : char *pachBufRingInfo = (char *) CPLGetTLS( CTLS_PATHBUF );
64 510616 : if( pachBufRingInfo == NULL )
65 : {
66 712 : pachBufRingInfo = (char *) CPLCalloc(1, sizeof(int) + CPL_PATH_BUF_SIZE * CPL_PATH_BUF_COUNT);
67 712 : 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 510616 : int *pnBufIndex = (int *) pachBufRingInfo;
75 510616 : int nOffset = sizeof(int) + *pnBufIndex * CPL_PATH_BUF_SIZE;
76 510616 : char *pachBuffer = pachBufRingInfo + nOffset;
77 :
78 510616 : *pnBufIndex = (*pnBufIndex + 1) % CPL_PATH_BUF_COUNT;
79 :
80 510616 : return pachBuffer;
81 : }
82 :
83 :
84 : /************************************************************************/
85 : /* CPLFindFilenameStart() */
86 : /************************************************************************/
87 :
88 504174 : static int CPLFindFilenameStart( const char * pszFilename )
89 :
90 : {
91 : size_t iFileStart;
92 :
93 12274387 : for( iFileStart = strlen(pszFilename);
94 : iFileStart > 0
95 6100640 : && pszFilename[iFileStart-1] != '/'
96 5669573 : && pszFilename[iFileStart-1] != '\\';
97 : iFileStart-- ) {}
98 :
99 504174 : 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 9495 : const char *CPLGetPath( const char *pszFilename )
129 :
130 : {
131 9495 : int iFileStart = CPLFindFilenameStart(pszFilename);
132 9495 : char *pszStaticResult = CPLGetStaticResult();
133 :
134 9495 : if( iFileStart >= CPL_PATH_BUF_SIZE )
135 0 : return CPLStaticBufferTooSmall(pszStaticResult);
136 :
137 9495 : CPLAssert( ! (pszFilename >= pszStaticResult && pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
138 :
139 9495 : if( iFileStart == 0 )
140 : {
141 64 : strcpy( pszStaticResult, "" );
142 64 : return pszStaticResult;
143 : }
144 :
145 9431 : CPLStrlcpy( pszStaticResult, pszFilename, iFileStart+1 );
146 :
147 18862 : if( iFileStart > 1
148 9431 : && (pszStaticResult[iFileStart-1] == '/'
149 0 : || pszStaticResult[iFileStart-1] == '\\') )
150 9431 : pszStaticResult[iFileStart-1] = '\0';
151 :
152 9431 : 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 9173 : const char *CPLGetDirname( const char *pszFilename )
182 :
183 : {
184 9173 : int iFileStart = CPLFindFilenameStart(pszFilename);
185 9173 : char *pszStaticResult = CPLGetStaticResult();
186 :
187 9173 : if( iFileStart >= CPL_PATH_BUF_SIZE )
188 0 : return CPLStaticBufferTooSmall(pszStaticResult);
189 :
190 9173 : CPLAssert( ! (pszFilename >= pszStaticResult && pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
191 :
192 9173 : if( iFileStart == 0 )
193 : {
194 5 : strcpy( pszStaticResult, "." );
195 5 : return pszStaticResult;
196 : }
197 :
198 9168 : CPLStrlcpy( pszStaticResult, pszFilename, iFileStart+1 );
199 :
200 18337 : if( iFileStart > 1
201 9168 : && (pszStaticResult[iFileStart-1] == '/'
202 1 : || pszStaticResult[iFileStart-1] == '\\') )
203 9168 : pszStaticResult[iFileStart-1] = '\0';
204 :
205 9168 : 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 65357 : const char *CPLGetFilename( const char *pszFullFilename )
231 :
232 : {
233 65357 : int iFileStart = CPLFindFilenameStart( pszFullFilename );
234 :
235 65357 : 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 272087 : const char *CPLGetBasename( const char *pszFullFilename )
263 :
264 : {
265 272087 : size_t iFileStart = CPLFindFilenameStart( pszFullFilename );
266 : size_t iExtStart, nLength;
267 272087 : char *pszStaticResult = CPLGetStaticResult();
268 :
269 272087 : CPLAssert( ! (pszFullFilename >= pszStaticResult && pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
270 :
271 1631510 : for( iExtStart = strlen(pszFullFilename);
272 1359423 : iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
273 : iExtStart-- ) {}
274 :
275 272087 : if( iExtStart == iFileStart )
276 1181 : iExtStart = strlen(pszFullFilename);
277 :
278 272087 : nLength = iExtStart - iFileStart;
279 :
280 272087 : if (nLength >= CPL_PATH_BUF_SIZE)
281 0 : return CPLStaticBufferTooSmall(pszStaticResult);
282 :
283 272087 : CPLStrlcpy( pszStaticResult, pszFullFilename + iFileStart, nLength + 1 );
284 :
285 272087 : 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 148062 : const char *CPLGetExtension( const char *pszFullFilename )
313 :
314 : {
315 148062 : size_t iFileStart = CPLFindFilenameStart( pszFullFilename );
316 : size_t iExtStart;
317 148062 : char *pszStaticResult = CPLGetStaticResult();
318 :
319 148062 : CPLAssert( ! (pszFullFilename >= pszStaticResult && pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
320 :
321 1003201 : for( iExtStart = strlen(pszFullFilename);
322 855139 : iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
323 : iExtStart-- ) {}
324 :
325 148062 : if( iExtStart == iFileStart )
326 83569 : iExtStart = strlen(pszFullFilename)-1;
327 :
328 148062 : if (CPLStrlcpy( pszStaticResult, pszFullFilename+iExtStart+1, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
329 0 : return CPLStaticBufferTooSmall(pszStaticResult);
330 :
331 148062 : 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 45621 : const char *CPLResetExtension( const char *pszPath, const char *pszExt )
388 :
389 : {
390 45621 : char *pszStaticResult = CPLGetStaticResult();
391 : size_t i;
392 :
393 45621 : CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) );
394 :
395 : /* -------------------------------------------------------------------- */
396 : /* First, try and strip off any existing extension. */
397 : /* -------------------------------------------------------------------- */
398 45621 : if (CPLStrlcpy( pszStaticResult, pszPath, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
399 0 : return CPLStaticBufferTooSmall(pszStaticResult);
400 :
401 45621 : if (*pszStaticResult)
402 : {
403 191663 : for( i = strlen(pszStaticResult) - 1; i > 0; i-- )
404 : {
405 191294 : if( pszStaticResult[i] == '.' )
406 : {
407 42259 : pszStaticResult[i] = '\0';
408 42259 : break;
409 : }
410 :
411 295137 : if( pszStaticResult[i] == '/' || pszStaticResult[i] == '\\'
412 146102 : || pszStaticResult[i] == ':' )
413 2987 : break;
414 : }
415 : }
416 :
417 : /* -------------------------------------------------------------------- */
418 : /* Append the new extension. */
419 : /* -------------------------------------------------------------------- */
420 45621 : 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 45621 : 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. Must *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 25652 : const char *CPLFormFilename( const char * pszPath,
460 : const char * pszBasename,
461 : const char * pszExtension )
462 :
463 : {
464 25652 : char *pszStaticResult = CPLGetStaticResult();
465 25652 : const char *pszAddedPathSep = "";
466 25652 : const char *pszAddedExtSep = "";
467 :
468 25652 : CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) );
469 25652 : CPLAssert( ! (pszBasename >= pszStaticResult && pszBasename < pszStaticResult + CPL_PATH_BUF_SIZE) );
470 :
471 25652 : if( pszBasename[0] == '.' && pszBasename[1] == '/' )
472 0 : pszBasename += 2;
473 :
474 25652 : if( pszPath == NULL )
475 6383 : pszPath = "";
476 57800 : else if( strlen(pszPath) > 0
477 19268 : && pszPath[strlen(pszPath)-1] != '/'
478 19263 : && pszPath[strlen(pszPath)-1] != '\\' )
479 : {
480 : /* FIXME? would be better to ask the filesystems what they */
481 : /* prefer as directory separator */
482 19263 : if (strncmp(pszPath, "/vsicurl/", 9) == 0)
483 19 : pszAddedPathSep = "/";
484 : else
485 19244 : pszAddedPathSep = SEP_STRING;
486 : }
487 :
488 25652 : if( pszExtension == NULL )
489 17790 : pszExtension = "";
490 7862 : else if( pszExtension[0] != '.' && strlen(pszExtension) > 0 )
491 7482 : pszAddedExtSep = ".";
492 :
493 25652 : if (CPLStrlcpy( pszStaticResult, pszPath, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
494 : CPLStrlcat( pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
495 : CPLStrlcat( pszStaticResult, pszBasename, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
496 : CPLStrlcat( pszStaticResult, pszAddedExtSep, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
497 : CPLStrlcat( pszStaticResult, pszExtension, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE)
498 0 : return CPLStaticBufferTooSmall(pszStaticResult);
499 :
500 25652 : return pszStaticResult;
501 : }
502 :
503 : /************************************************************************/
504 : /* CPLFormCIFilename() */
505 : /************************************************************************/
506 :
507 : /**
508 : * Case insensitive file searching, returing full path.
509 : *
510 : * This function tries to return the path to a file regardless of
511 : * whether the file exactly matches the basename, and extension case, or
512 : * is all upper case, or all lower case. The path is treated as case
513 : * sensitive. This function is equivelent to CPLFormFilename() on
514 : * case insensitive file systems (like Windows).
515 : *
516 : * @param pszPath directory path to the directory containing the file. This
517 : * may be relative or absolute, and may have a trailing path separator or
518 : * not. May be NULL.
519 : *
520 : * @param pszBasename file basename. May optionally have path and/or
521 : * extension. May not be NULL.
522 : *
523 : * @param pszExtension file extension, optionally including the period. May
524 : * be NULL.
525 : *
526 : * @return a fully formed filename in an internal static string. Do not
527 : * modify or free the returned string. The string may be destroyed by the
528 : * next CPL call.
529 : */
530 :
531 1199 : const char *CPLFormCIFilename( const char * pszPath,
532 : const char * pszBasename,
533 : const char * pszExtension )
534 :
535 : {
536 : // On case insensitive filesystems, just default to
537 : // CPLFormFilename()
538 1199 : if( !VSIIsCaseSensitiveFS(pszPath) )
539 0 : return CPLFormFilename( pszPath, pszBasename, pszExtension );
540 :
541 1199 : const char *pszAddedExtSep = "";
542 : char *pszFilename;
543 : const char *pszFullPath;
544 1199 : int nLen = strlen(pszBasename)+2, i;
545 : VSIStatBufL sStatBuf;
546 : int nStatRet;
547 :
548 1199 : if( pszExtension != NULL )
549 995 : nLen += strlen(pszExtension);
550 :
551 1199 : pszFilename = (char *) CPLMalloc(nLen);
552 :
553 1199 : if( pszExtension == NULL )
554 204 : pszExtension = "";
555 995 : else if( pszExtension[0] != '.' && strlen(pszExtension) > 0 )
556 757 : pszAddedExtSep = ".";
557 :
558 : sprintf( pszFilename, "%s%s%s",
559 1199 : pszBasename, pszAddedExtSep, pszExtension );
560 :
561 1199 : pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
562 1199 : nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG );
563 1199 : if( nStatRet != 0 )
564 : {
565 10033 : for( i = 0; pszFilename[i] != '\0'; i++ )
566 : {
567 9155 : if( islower(pszFilename[i]) )
568 6200 : pszFilename[i] = (char) toupper(pszFilename[i]);
569 : }
570 :
571 878 : pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
572 878 : nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG );
573 : }
574 :
575 1199 : if( nStatRet != 0 )
576 : {
577 10017 : for( i = 0; pszFilename[i] != '\0'; i++ )
578 : {
579 9141 : if( isupper(pszFilename[i]) )
580 7453 : pszFilename[i] = (char) tolower(pszFilename[i]);
581 : }
582 :
583 876 : pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
584 876 : nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG );
585 : }
586 :
587 1199 : if( nStatRet != 0 )
588 873 : pszFullPath = CPLFormFilename( pszPath, pszBasename, pszExtension );
589 :
590 1199 : CPLFree( pszFilename );
591 :
592 1199 : return pszFullPath;
593 : }
594 :
595 : /************************************************************************/
596 : /* CPLProjectRelativeFilename() */
597 : /************************************************************************/
598 :
599 : /**
600 : * Find a file relative to a project file.
601 : *
602 : * Given the path to a "project" directory, and a path to a secondary file
603 : * referenced from that project, build a path to the secondary file
604 : * that the current application can use. If the secondary path is already
605 : * absolute, rather than relative, then it will be returned unaltered.
606 : *
607 : * Examples:
608 : * <pre>
609 : * CPLProjectRelativeFilename("abc/def","tmp/abc.gif") == "abc/def/tmp/abc.gif"
610 : * CPLProjectRelativeFilename("abc/def","/tmp/abc.gif") == "/tmp/abc.gif"
611 : * CPLProjectRelativeFilename("/xy", "abc.gif") == "/xy/abc.gif"
612 : * CPLProjectRelativeFilename("/abc/def","../abc.gif") == "/abc/def/../abc.gif"
613 : * CPLProjectRelativeFilename("C:\WIN","abc.gif") == "C:\WIN\abc.gif"
614 : * </pre>
615 : *
616 : * @param pszProjectDir the directory relative to which the secondary files
617 : * path should be interpreted.
618 : * @param pszSecondaryFilename the filename (potentially with path) that
619 : * is to be interpreted relative to the project directory.
620 : *
621 : * @return a composed path to the secondary file. The returned string is
622 : * internal and should not be altered, freed, or depending on past the next
623 : * CPL call.
624 : */
625 :
626 519 : const char *CPLProjectRelativeFilename( const char *pszProjectDir,
627 : const char *pszSecondaryFilename )
628 :
629 : {
630 519 : char *pszStaticResult = CPLGetStaticResult();
631 :
632 519 : CPLAssert( ! (pszProjectDir >= pszStaticResult && pszProjectDir < pszStaticResult + CPL_PATH_BUF_SIZE) );
633 519 : CPLAssert( ! (pszSecondaryFilename >= pszStaticResult && pszSecondaryFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
634 :
635 519 : if( !CPLIsFilenameRelative( pszSecondaryFilename ) )
636 100 : return pszSecondaryFilename;
637 :
638 419 : if( pszProjectDir == NULL || strlen(pszProjectDir) == 0 )
639 0 : return pszSecondaryFilename;
640 :
641 419 : if (CPLStrlcpy( pszStaticResult, pszProjectDir, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
642 0 : goto error;
643 :
644 838 : if( pszProjectDir[strlen(pszProjectDir)-1] != '/'
645 419 : && pszProjectDir[strlen(pszProjectDir)-1] != '\\' )
646 : {
647 : /* FIXME? would be better to ask the filesystems what they */
648 : /* prefer as directory separator */
649 : const char* pszAddedPathSep;
650 419 : if (strncmp(pszStaticResult, "/vsicurl/", 9) == 0)
651 0 : pszAddedPathSep = "/";
652 : else
653 419 : pszAddedPathSep = SEP_STRING;
654 419 : if (CPLStrlcat( pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
655 0 : goto error;
656 : }
657 :
658 419 : if (CPLStrlcat( pszStaticResult, pszSecondaryFilename, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
659 0 : goto error;
660 :
661 419 : return pszStaticResult;
662 : error:
663 0 : return CPLStaticBufferTooSmall(pszStaticResult);
664 : }
665 :
666 :
667 : /************************************************************************/
668 : /* CPLIsFilenameRelative() */
669 : /************************************************************************/
670 :
671 : /**
672 : * Is filename relative or absolute?
673 : *
674 : * The test is filesystem convention agnostic. That is it will test for
675 : * Unix style and windows style path conventions regardless of the actual
676 : * system in use.
677 : *
678 : * @param pszFilename the filename with path to test.
679 : *
680 : * @return TRUE if the filename is relative or FALSE if it is absolute.
681 : */
682 :
683 747 : int CPLIsFilenameRelative( const char *pszFilename )
684 :
685 : {
686 2241 : if( (strlen(pszFilename) > 2
687 : && (strncmp(pszFilename+1,":\\",2) == 0
688 : || strncmp(pszFilename+1,":/",2) == 0))
689 747 : || pszFilename[0] == '\\'
690 747 : || pszFilename[0] == '/' )
691 100 : return FALSE;
692 : else
693 647 : return TRUE;
694 : }
695 :
696 : /************************************************************************/
697 : /* CPLExtractRelativePath() */
698 : /************************************************************************/
699 :
700 : /**
701 : * Get relative path from directory to target file.
702 : *
703 : * Computes a relative path for pszTarget relative to pszBaseDir.
704 : * Currently this only works if they share a common base path. The returned
705 : * path is normally into the pszTarget string. It should only be considered
706 : * valid as long as pszTarget is valid or till the next call to
707 : * this function, whichever comes first.
708 : *
709 : * @param pszBaseDir the name of the directory relative to which the path
710 : * should be computed. pszBaseDir may be NULL in which case the original
711 : * target is returned without relitivizing.
712 : *
713 : * @param pszTarget the filename to be changed to be relative to pszBaseDir.
714 : *
715 : * @param pbGotRelative Pointer to location in which a flag is placed
716 : * indicating that the returned path is relative to the basename (TRUE) or
717 : * not (FALSE). This pointer may be NULL if flag is not desired.
718 : *
719 : * @return an adjusted path or the original if it could not be made relative
720 : * to the pszBaseFile's path.
721 : **/
722 :
723 203 : const char *CPLExtractRelativePath( const char *pszBaseDir,
724 : const char *pszTarget,
725 : int *pbGotRelative )
726 :
727 : {
728 : size_t nBasePathLen;
729 :
730 : /* -------------------------------------------------------------------- */
731 : /* If we don't have a basedir, then we can't relativize the path. */
732 : /* -------------------------------------------------------------------- */
733 203 : if( pszBaseDir == NULL )
734 : {
735 0 : if( pbGotRelative != NULL )
736 0 : *pbGotRelative = FALSE;
737 :
738 0 : return pszTarget;
739 : }
740 :
741 203 : nBasePathLen = strlen(pszBaseDir);
742 :
743 : /* -------------------------------------------------------------------- */
744 : /* One simple case is when the base dir is '.' and the target */
745 : /* filename is relative. */
746 : /* -------------------------------------------------------------------- */
747 203 : if( (nBasePathLen == 0 || EQUAL(pszBaseDir,"."))
748 : && CPLIsFilenameRelative(pszTarget) )
749 : {
750 0 : if( pbGotRelative != NULL )
751 0 : *pbGotRelative = TRUE;
752 :
753 0 : return pszTarget;
754 : }
755 :
756 : /* -------------------------------------------------------------------- */
757 : /* By this point, if we don't have a base path, we can't have a */
758 : /* meaningful common prefix. */
759 : /* -------------------------------------------------------------------- */
760 203 : if( nBasePathLen == 0 )
761 : {
762 0 : if( pbGotRelative != NULL )
763 0 : *pbGotRelative = FALSE;
764 :
765 0 : return pszTarget;
766 : }
767 :
768 : /* -------------------------------------------------------------------- */
769 : /* If we don't have a common path prefix, then we can't get a */
770 : /* relative path. */
771 : /* -------------------------------------------------------------------- */
772 327 : if( !EQUALN(pszBaseDir,pszTarget,nBasePathLen)
773 62 : || (pszTarget[nBasePathLen] != '\\'
774 62 : && pszTarget[nBasePathLen] != '/') )
775 : {
776 141 : if( pbGotRelative != NULL )
777 141 : *pbGotRelative = FALSE;
778 :
779 141 : return pszTarget;
780 : }
781 :
782 : /* -------------------------------------------------------------------- */
783 : /* We have a relative path. Strip it off to get a string to */
784 : /* return. */
785 : /* -------------------------------------------------------------------- */
786 62 : if( pbGotRelative != NULL )
787 62 : *pbGotRelative = TRUE;
788 :
789 62 : return pszTarget + nBasePathLen + 1;
790 : }
791 :
792 : /************************************************************************/
793 : /* CPLCleanTrailingSlash() */
794 : /************************************************************************/
795 :
796 : /**
797 : * Remove trailing forward/backward slash from the path for unix/windows resp.
798 : *
799 : * Returns a string containing the portion of the passed path string with
800 : * trailing slash removed. If there is no path in the passed filename
801 : * an empty string will be returned (not NULL).
802 : *
803 : * <pre>
804 : * CPLCleanTrailingSlash( "abc/def/" ) == "abc/def"
805 : * CPLCleanTrailingSlash( "abc/def" ) == "abc/def"
806 : * CPLCleanTrailingSlash( "c:\abc\def\" ) == "c:\abc\def"
807 : * CPLCleanTrailingSlash( "c:\abc\def" ) == "c:\abc\def"
808 : * CPLCleanTrailingSlash( "abc" ) == "abc"
809 : * </pre>
810 : *
811 : * @param pszPath the path to be cleaned up
812 : *
813 : * @return Path in an internal string which must not be freed. The string
814 : * may be destroyed by the next CPL filename handling call.
815 : */
816 :
817 7 : const char *CPLCleanTrailingSlash( const char *pszPath )
818 :
819 : {
820 7 : char *pszStaticResult = CPLGetStaticResult();
821 7 : int iPathLength = strlen(pszPath);
822 :
823 7 : CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) );
824 :
825 7 : if (iPathLength >= CPL_PATH_BUF_SIZE)
826 0 : return CPLStaticBufferTooSmall(pszStaticResult);
827 :
828 7 : CPLStrlcpy( pszStaticResult, pszPath, iPathLength+1 );
829 :
830 21 : if( iPathLength > 0
831 7 : && (pszStaticResult[iPathLength-1] == '\\'
832 7 : || pszStaticResult[iPathLength-1] == '/'))
833 0 : pszStaticResult[iPathLength-1] = '\0';
834 :
835 7 : return pszStaticResult;
836 : }
837 :
838 : /************************************************************************/
839 : /* CPLCorrespondingPaths() */
840 : /************************************************************************/
841 :
842 : /**
843 : * Identify corresponding paths.
844 : *
845 : * Given a prototype old and new filename this function will attempt
846 : * to determine corresponding names for a set of other old filenames that
847 : * will rename them in a similar manner. This correspondance assumes there
848 : * are two possibly kinds of renaming going on. A change of path, and a
849 : * change of filename stem.
850 : *
851 : * If a consistent renaming cannot be established for all the files this
852 : * function will return indicating an error.
853 : *
854 : * The returned file list becomes owned by the caller and should be destroyed
855 : * with CSLDestroy().
856 : *
857 : * @param pszOldFilename path to old prototype file.
858 : * @param pszNewFilename path to new prototype file.
859 : * @param papszFileList list of other files associated with pszOldFilename to
860 : * rename similarly.
861 : *
862 : * @return a list of files corresponding to papszFileList but renamed to
863 : * correspond to pszNewFilename.
864 : */
865 :
866 2 : char **CPLCorrespondingPaths( const char *pszOldFilename,
867 : const char *pszNewFilename,
868 : char **papszFileList )
869 :
870 : {
871 2 : CPLString osOldPath = CPLGetPath( pszOldFilename );
872 2 : CPLString osNewPath = CPLGetPath( pszNewFilename );
873 2 : CPLString osOldBasename = CPLGetBasename( pszOldFilename );
874 2 : CPLString osNewBasename = CPLGetBasename( pszNewFilename );
875 : int i;
876 :
877 2 : if( CSLCount(papszFileList) == 0 )
878 0 : return NULL;
879 :
880 : /* -------------------------------------------------------------------- */
881 : /* There is a special case for a one item list which exactly */
882 : /* matches the old name, to rename to the new name. */
883 : /* -------------------------------------------------------------------- */
884 2 : if( CSLCount(papszFileList) == 1
885 0 : && strcmp(pszOldFilename,papszFileList[0]) == 0 )
886 : {
887 0 : return CSLAddString( NULL, pszNewFilename );
888 : }
889 :
890 : /* -------------------------------------------------------------------- */
891 : /* If the basename is changing, verify that all source files */
892 : /* have the same starting basename. */
893 : /* -------------------------------------------------------------------- */
894 2 : if( osOldBasename != osNewBasename )
895 : {
896 2 : for( i=0; papszFileList[i] != NULL; i++ )
897 : {
898 8 : if( osOldBasename == CPLGetBasename( papszFileList[i] ) )
899 8 : continue;
900 :
901 0 : CPLString osFilePath, osFileName;
902 :
903 0 : osFilePath = CPLGetPath( papszFileList[i] );
904 0 : osFileName = CPLGetFilename( papszFileList[i] );
905 :
906 0 : if( !EQUALN(osFileName,osOldBasename,osOldBasename.size())
907 : || !EQUAL(osFilePath,osOldPath)
908 : || osFileName[osOldBasename.size()] != '.' )
909 : {
910 : CPLError( CE_Failure, CPLE_AppDefined,
911 0 : "Unable to rename fileset due irregular basenames.");
912 0 : return NULL;
913 : }
914 : }
915 : }
916 :
917 : /* -------------------------------------------------------------------- */
918 : /* If the filename portions differs, ensure they only differ in */
919 : /* basename. */
920 : /* -------------------------------------------------------------------- */
921 2 : if( osOldBasename != osNewBasename )
922 : {
923 : CPLString osOldExtra = CPLGetFilename(pszOldFilename)
924 2 : + strlen(osOldBasename);
925 : CPLString osNewExtra = CPLGetFilename(pszNewFilename)
926 2 : + strlen(osNewBasename);
927 :
928 2 : if( osOldExtra != osNewExtra )
929 : {
930 : CPLError( CE_Failure, CPLE_AppDefined,
931 0 : "Unable to rename fileset due to irregular filename correspondence." );
932 0 : return NULL;
933 0 : }
934 : }
935 :
936 : /* -------------------------------------------------------------------- */
937 : /* Generate the new filenames. */
938 : /* -------------------------------------------------------------------- */
939 2 : char **papszNewList = NULL;
940 :
941 10 : for( i=0; papszFileList[i] != NULL; i++ )
942 : {
943 8 : CPLString osNewFilename;
944 8 : CPLString osOldFilename = CPLGetFilename( papszFileList[i] );
945 :
946 8 : if( osOldBasename == osNewBasename )
947 : osNewFilename =
948 0 : CPLFormFilename( osNewPath, osOldFilename, NULL );
949 : else
950 : osNewFilename =
951 : CPLFormFilename( osNewPath, osNewBasename,
952 8 : osOldFilename.c_str()+strlen(osOldBasename));
953 :
954 8 : papszNewList = CSLAddString( papszNewList, osNewFilename );
955 : }
956 :
957 2 : return papszNewList;
958 : }
959 :
960 : /************************************************************************/
961 : /* CPLGenerateTempFilename() */
962 : /************************************************************************/
963 :
964 : /**
965 : * Generate temporary file name.
966 : *
967 : * Returns a filename that may be used for a temporary file. The location
968 : * of the file tries to follow operating system semantics but may be
969 : * forced via the CPL_TMPDIR configuration option.
970 : *
971 : * @param pszStem if non-NULL this will be part of the filename.
972 : *
973 : * @return a filename which is valid till the next CPL call in this thread.
974 : */
975 :
976 36 : const char *CPLGenerateTempFilename( const char *pszStem )
977 :
978 : {
979 36 : const char *pszDir = CPLGetConfigOption( "CPL_TMPDIR", NULL );
980 : static volatile int nTempFileCounter = 0;
981 :
982 36 : if( pszDir == NULL )
983 36 : pszDir = CPLGetConfigOption( "TMPDIR", NULL );
984 :
985 36 : if( pszDir == NULL )
986 36 : pszDir = CPLGetConfigOption( "TEMP", NULL );
987 :
988 36 : if( pszDir == NULL )
989 36 : pszDir = ".";
990 :
991 36 : CPLString osFilename;
992 :
993 36 : if( pszStem == NULL )
994 28 : pszStem = "";
995 :
996 : osFilename.Printf( "%s%u_%d", pszStem,
997 36 : (int) CPLGetPID(), nTempFileCounter++ );
998 :
999 36 : return CPLFormFilename( pszDir, osFilename, NULL );
1000 : }
|