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