VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/path.cpp@ 5999

Last change on this file since 5999 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.9 KB
Line 
1/* $Id: path.cpp 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Path Manipulation.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/path.h>
32#include <iprt/dir.h>
33#include <iprt/param.h>
34#include <iprt/string.h>
35#include <iprt/assert.h>
36#include <iprt/string.h>
37#include <iprt/ctype.h>
38#include <iprt/err.h>
39#include <iprt/uni.h>
40#include "internal/fs.h"
41#include "internal/path.h"
42
43
44/**
45 * Strips the filename from a path.
46 *
47 * @param pszPath Path which filename should be extracted from.
48 *
49 */
50RTDECL(void) RTPathStripFilename(char *pszPath)
51{
52 char *psz = pszPath;
53 char *pszLastSep = pszPath;
54
55 for (;; psz++)
56 {
57 switch (*psz)
58 {
59 /* handle separators. */
60#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
61 case ':':
62 pszLastSep = psz + 1;
63 break;
64
65 case '\\':
66#endif
67 case '/':
68 pszLastSep = psz;
69 break;
70
71 /* the end */
72 case '\0':
73 if (pszLastSep == pszPath)
74 *pszLastSep++ = '.';
75 *pszLastSep = '\0';
76 return;
77 }
78 }
79 /* will never get here */
80}
81
82
83/**
84 * Strips the extension from a path.
85 *
86 * @param pszPath Path which extension should be stripped.
87 */
88RTDECL(void) RTPathStripExt(char *pszPath)
89{
90 char *pszDot = NULL;
91 for (;; pszPath++)
92 {
93 switch (*pszPath)
94 {
95 /* handle separators. */
96#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
97 case ':':
98 case '\\':
99#endif
100 case '/':
101 pszDot = NULL;
102 break;
103 case '.':
104 pszDot = pszPath;
105 break;
106
107 /* the end */
108 case '\0':
109 if (pszDot)
110 *pszDot = '\0';
111 return;
112 }
113 }
114 /* will never get here */
115}
116
117
118/**
119 * Finds the filename in a path.
120 *
121 * @returns Pointer to filename within pszPath.
122 * @returns NULL if no filename (i.e. empty string or ends with a slash).
123 * @param pszPath Path to find filename in.
124 */
125RTDECL(char *) RTPathFilename(const char *pszPath)
126{
127 const char *psz = pszPath;
128 const char *pszLastComp = pszPath;
129
130 for (;; psz++)
131 {
132 switch (*psz)
133 {
134 /* handle separators. */
135#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
136 case ':':
137 pszLastComp = psz + 1;
138 break;
139
140 case '\\':
141#endif
142 case '/':
143 pszLastComp = psz + 1;
144 break;
145
146 /* the end */
147 case '\0':
148 if (*pszLastComp)
149 return (char *)(void *)pszLastComp;
150 return NULL;
151 }
152 }
153
154 /* will never get here */
155 return NULL;
156}
157
158
159/**
160 * Strips the trailing slashes of a path name.
161 *
162 * @param pszPath Path to strip.
163 */
164RTDECL(void) RTPathStripTrailingSlash(char *pszPath)
165{
166 char *pszEnd = strchr(pszPath, '\0');
167 while (pszEnd-- > pszPath)
168 {
169 switch (*pszEnd)
170 {
171 case '/':
172#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
173 case '\\':
174#endif
175 *pszEnd = '\0';
176 break;
177 default:
178 return;
179 }
180 }
181 return;
182}
183
184
185/**
186 * Finds the extension part of in a path.
187 *
188 * @returns Pointer to extension within pszPath.
189 * @returns NULL if no extension.
190 * @param pszPath Path to find extension in.
191 */
192RTDECL(char *) RTPathExt(const char *pszPath)
193{
194 const char *psz = pszPath;
195 const char *pszExt = NULL;
196
197 for (;; psz++)
198 {
199 switch (*psz)
200 {
201 /* handle separators. */
202#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
203 case ':':
204 pszExt = NULL;
205 break;
206
207 case '\\':
208#endif
209 case '/':
210 pszExt = NULL;
211 break;
212 case '.':
213 pszExt = psz;
214 break;
215
216 /* the end */
217 case '\0':
218 if (pszExt)
219 return (char *)(void *)pszExt;
220 return NULL;
221 }
222 }
223
224 /* will never get here */
225 return NULL;
226}
227
228
229/**
230 * Checks if a path have an extension.
231 *
232 * @returns true if extension present.
233 * @returns false if no extension.
234 * @param pszPath Path to check.
235 */
236RTDECL(bool) RTPathHaveExt(const char *pszPath)
237{
238 return RTPathExt(pszPath) != NULL;
239}
240
241
242/**
243 * Checks if a path includes more than a filename.
244 *
245 * @returns true if path present.
246 * @returns false if no path.
247 * @param pszPath Path to check.
248 */
249RTDECL(bool) RTPathHavePath(const char *pszPath)
250{
251#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
252 return strpbrk(pszPath, "/\\:") != NULL;
253#else
254 return strpbrk(pszPath, "/") != NULL;
255#endif
256}
257
258
259/**
260 * Helper for RTPathCompare() and RTPathStartsWith().
261 *
262 * @returns similar to strcmp.
263 * @param pszPath1 Path to compare.
264 * @param pszPath2 Path to compare.
265 * @param fLimit Limit the comparison to the length of \a pszPath2
266 * @internal
267 */
268static int rtPathCompare(const char *pszPath1, const char *pszPath2, bool fLimit)
269{
270 if (pszPath1 == pszPath2)
271 return 0;
272 if (!pszPath1)
273 return -1;
274 if (!pszPath2)
275 return 1;
276
277#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
278 PRTUNICP puszPath1;
279 int rc = RTStrToUni(pszPath1, &puszPath1);
280 if (RT_FAILURE(rc))
281 return -1;
282 PRTUNICP puszPath2;
283 rc = RTStrToUni(pszPath2, &puszPath2);
284 if (RT_FAILURE(rc))
285 {
286 RTUniFree(puszPath1);
287 return 1;
288 }
289
290 int iDiff = 0;
291 PRTUNICP puszTmpPath1 = puszPath1;
292 PRTUNICP puszTmpPath2 = puszPath2;
293 for (;;)
294 {
295 register RTUNICP uc1 = *puszTmpPath1;
296 register RTUNICP uc2 = *puszTmpPath2;
297 if (uc1 != uc2)
298 {
299 if (uc1 == '\\')
300 uc1 = '/';
301 else
302 uc1 = RTUniCpToUpper(uc1);
303 if (uc2 == '\\')
304 uc2 = '/';
305 else
306 uc2 = RTUniCpToUpper(uc2);
307 if (uc1 != uc2)
308 {
309 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
310 if (fLimit && uc2 == '\0')
311 iDiff = 0;
312 break;
313 }
314 }
315 if (!uc1)
316 break;
317 puszTmpPath1++;
318 puszTmpPath2++;
319
320 }
321
322 RTUniFree(puszPath2);
323 RTUniFree(puszPath1);
324 return iDiff;
325
326#else
327 if (!fLimit)
328 return strcmp(pszPath1, pszPath2);
329 return strncmp(pszPath1, pszPath2, strlen(pszPath2));
330#endif
331}
332
333
334/**
335 * Compares two paths.
336 *
337 * The comparison takes platform-dependent details into account,
338 * such as:
339 * <ul>
340 * <li>On DOS-like platforms, both |\| and |/| separator chars are considered
341 * to be equal.
342 * <li>On platforms with case-insensitive file systems, mismatching characters
343 * are uppercased and compared again.
344 * </ul>
345 *
346 * @remark
347 *
348 * File system details are currently ignored. This means that you won't get
349 * case-insentive compares on unix systems when a path goes into a case-insensitive
350 * filesystem like FAT, HPFS, HFS, NTFS, JFS, or similar. For NT, OS/2 and similar
351 * you'll won't get case-sensitve compares on a case-sensitive file system.
352 *
353 * @param pszPath1 Path to compare (must be an absolute path).
354 * @param pszPath2 Path to compare (must be an absolute path).
355 *
356 * @returns < 0 if the first path less than the second path.
357 * @returns 0 if the first path identical to the second path.
358 * @returns > 0 if the first path greater than the second path.
359 */
360RTDECL(int) RTPathCompare(const char *pszPath1, const char *pszPath2)
361{
362 return rtPathCompare(pszPath1, pszPath2, false /* full path lengths */);
363}
364
365
366/**
367 * Checks if a path starts with the given parent path.
368 *
369 * This means that either the path and the parent path matches completely, or that
370 * the path is to some file or directory residing in the tree given by the parent
371 * directory.
372 *
373 * The path comparison takes platform-dependent details into account,
374 * see RTPathCompare() for details.
375 *
376 * @param pszPath Path to check, must be an absolute path.
377 * @param pszParentPath Parent path, must be an absolute path.
378 * No trailing directory slash!
379 *
380 * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
381 * are identical), or |false| otherwise.
382 *
383 * @remark This API doesn't currently handle root directory compares in a manner
384 * consistant with the other APIs. RTPathStartsWith(pszSomePath, "/") will
385 * not work if pszSomePath isn't "/".
386 */
387RTDECL(bool) RTPathStartsWith(const char *pszPath, const char *pszParentPath)
388{
389 if (pszPath == pszParentPath)
390 return true;
391 if (!pszPath || !pszParentPath)
392 return false;
393
394 if (rtPathCompare(pszPath, pszParentPath, true /* limited by path 2 */) != 0)
395 return false;
396
397 const size_t cchParentPath = strlen(pszParentPath);
398 return RTPATH_IS_SLASH(pszPath[cchParentPath])
399 || pszPath[cchParentPath] == '\0';
400}
401
402
403/**
404 * Same as RTPathReal only the result is RTStrDup()'ed.
405 *
406 * @returns Pointer to real path. Use RTStrFree() to free this string.
407 * @returns NULL if RTPathReal() or RTStrDup() fails.
408 * @param pszPath
409 */
410RTDECL(char *) RTPathRealDup(const char *pszPath)
411{
412 char szPath[RTPATH_MAX];
413 int rc = RTPathReal(pszPath, szPath, sizeof(szPath));
414 if (RT_SUCCESS(rc))
415 return RTStrDup(szPath);
416 return NULL;
417}
418
419
420/**
421 * Same as RTPathAbs only the result is RTStrDup()'ed.
422 *
423 * @returns Pointer to real path. Use RTStrFree() to free this string.
424 * @returns NULL if RTPathAbs() or RTStrDup() fails.
425 * @param pszPath The path to resolve.
426 */
427RTDECL(char *) RTPathAbsDup(const char *pszPath)
428{
429 char szPath[RTPATH_MAX];
430 int rc = RTPathAbs(pszPath, szPath, sizeof(szPath));
431 if (RT_SUCCESS(rc))
432 return RTStrDup(szPath);
433 return NULL;
434}
435
436
437/**
438 * Returns the length of the volume name specifier of the given path.
439 * If no such specifier zero is returned.
440 */
441size_t rtPathVolumeSpecLen(const char *pszPath)
442{
443#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
444 if (pszPath && *pszPath)
445 {
446 /* UTC path. */
447 if ( (pszPath[0] == '\\' || pszPath[0] == '/')
448 && (pszPath[1] == '\\' || pszPath[1] == '/'))
449 return strcspn(pszPath + 2, "\\/") + 2;
450
451 /* Drive letter. */
452 if ( pszPath[1] == ':'
453 && toupper(pszPath[0]) >= 'A' && toupper(pszPath[0]) <= 'Z')
454 return 2;
455 }
456 return 0;
457
458#else
459 /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
460 /// @todo (dmik) well, it's better to consider there's no volume name
461 // at all on *nix systems
462 return 0;
463// return pszPath && pszPath[0] == '/';
464#endif
465}
466
467
468/**
469 * Get the absolute path (no symlinks, no . or .. components), assuming the
470 * given base path as the current directory. The resulting path doesn't have
471 * to exist.
472 *
473 * @returns iprt status code.
474 * @param pszBase The base path to act like a current directory.
475 * When NULL, the actual cwd is used (i.e. the call
476 * is equivalent to RTPathAbs(pszPath, ...).
477 * @param pszPath The path to resolve.
478 * @param pszAbsPath Where to store the absolute path.
479 * @param cchAbsPath Size of the buffer.
480 */
481RTDECL(int) RTPathAbsEx(const char *pszBase, const char *pszPath, char *pszAbsPath, unsigned cchAbsPath)
482{
483 if (pszBase && pszPath && !rtPathVolumeSpecLen(pszPath))
484 {
485#if defined(RT_OS_WINDOWS)
486 /* The format for very long paths is not supported. */
487 if ( (pszBase[0] == '/' || pszBase[0] == '\\')
488 && (pszBase[1] == '/' || pszBase[1] == '\\')
489 && pszBase[2] == '?'
490 && (pszBase[3] == '/' || pszBase[3] == '\\'))
491 return VERR_INVALID_NAME;
492#endif
493
494 /** @todo there are a couple of things which isn't 100% correct, although the
495 * current code will have to work for now - I don't have time to fix it right now.
496 *
497 * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
498 * not necessarily resolve it on the right drive.
499 * 2) A trailing slash in the base might cause UNC names to be created.
500 * 3) The lengths total doesn't have to be less than max length
501 * if the pszPath starts with a slash.
502 */
503 size_t cchBase = strlen(pszBase);
504 size_t cchPath = strlen(pszPath);
505 if (cchBase + cchPath >= RTPATH_MAX)
506 return VERR_FILENAME_TOO_LONG;
507
508 bool fRootSpec = pszPath[0] == '/'
509#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
510 || pszPath[0] == '\\'
511#endif
512 ;
513 size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
514 char szPath[RTPATH_MAX];
515 if (fRootSpec)
516 {
517 /* join the disk name from base and the path */
518 memcpy(szPath, pszBase, cchVolSpec);
519 strcpy(&szPath[cchVolSpec], pszPath);
520 }
521 else
522 {
523 /* join the base path and the path */
524 strcpy(szPath, pszBase);
525 szPath[cchBase] = RTPATH_DELIMITER;
526 strcpy(&szPath[cchBase + 1], pszPath);
527 }
528 return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
529 }
530
531 /* Fallback to the non *Ex version */
532 return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
533}
534
535
536/**
537 * Same as RTPathAbsEx only the result is RTStrDup()'ed.
538 *
539 * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
540 * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
541 * @param pszBase The base path to act like a current directory.
542 * When NULL, the actual cwd is used (i.e. the call
543 * is equivalent to RTPathAbs(pszPath, ...).
544 * @param pszPath The path to resolve.
545 */
546RTDECL(char *) RTPathAbsExDup(const char *pszBase, const char *pszPath)
547{
548 char szPath[RTPATH_MAX];
549 int rc = RTPathAbsEx(pszBase, pszPath, szPath, sizeof(szPath));
550 if (RT_SUCCESS(rc))
551 return RTStrDup(szPath);
552 return NULL;
553}
554
555
556/**
557 * Gets the directory for architecture-independent application data, for
558 * example NLS files, module sources, ...
559 *
560 * Linux: /usr/shared/<application>
561 * Windows: <program files directory>/<application>
562 * Old path: same as RTPathProgram()
563 *
564 */
565RTDECL(int) RTPathAppPrivateNoArch(char *pszPath, unsigned cchPath)
566{
567#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
568 char *pszUtf8Path;
569 int rc;
570 rc = rtPathFromNative(&pszUtf8Path, RTPATH_APP_PRIVATE);
571 if (RT_SUCCESS(rc))
572 {
573 size_t cchPathPrivateNoArch = strlen(pszUtf8Path);
574 if (cchPathPrivateNoArch < cchPath)
575 memcpy(pszPath, pszUtf8Path, cchPathPrivateNoArch + 1);
576 else
577 rc = VERR_BUFFER_OVERFLOW;
578 RTStrFree(pszUtf8Path);
579 }
580 return rc;
581#else
582 return RTPathProgram(pszPath, cchPath);
583#endif
584}
585
586
587/**
588 * Gets the directory for architecture-dependent application data, for
589 * example modules which can be loaded at runtime.
590 *
591 * Linux: /usr/lib/<application>
592 * Windows: <program files directory>/<application>
593 * Old path: same as RTPathProgram()
594 *
595 * @returns iprt status code.
596 * @param pszPath Buffer where to store the path.
597 * @param cchPath Buffer size in bytes.
598 */
599RTDECL(int) RTPathAppPrivateArch(char *pszPath, unsigned cchPath)
600{
601#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
602 char *pszUtf8Path;
603 int rc;
604 rc = rtPathFromNative(&pszUtf8Path, RTPATH_APP_PRIVATE_ARCH);
605 if (RT_SUCCESS(rc))
606 {
607 size_t cchPathPrivateArch = strlen(pszUtf8Path);
608 if (cchPathPrivateArch < cchPath)
609 memcpy(pszPath, pszUtf8Path, cchPathPrivateArch + 1);
610 else
611 rc = VERR_BUFFER_OVERFLOW;
612 RTStrFree(pszUtf8Path);
613 }
614 return rc;
615#else
616 return RTPathProgram(pszPath, cchPath);
617#endif
618}
619
620
621/**
622 * Gets the directory of shared libraries. This is not the same as
623 * RTPathAppPrivateArch() as Linux depends all shared libraries in
624 * a common global directory where ld.so can found them.
625 *
626 * Linux: /usr/lib
627 * Windows: <program files directory>/<application>
628 * Old path: same as RTPathProgram()
629 *
630 * @returns iprt status code.
631 * @param pszPath Buffer where to store the path.
632 * @param cchPath Buffer size in bytes.
633 */
634RTDECL(int) RTPathSharedLibs(char *pszPath, unsigned cchPath)
635{
636#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
637 char *pszUtf8Path;
638 int rc;
639 rc = rtPathFromNative(&pszUtf8Path, RTPATH_SHARED_LIBS);
640 if (RT_SUCCESS(rc))
641 {
642 size_t cchPathSharedLibs = strlen(pszUtf8Path);
643 if (cchPathSharedLibs < cchPath)
644 memcpy(pszPath, pszUtf8Path, cchPathSharedLibs + 1);
645 else
646 rc = VERR_BUFFER_OVERFLOW;
647 RTStrFree(pszUtf8Path);
648 }
649 return rc;
650#else
651 return RTPathProgram(pszPath, cchPath);
652#endif
653}
654
655
656/**
657 * Gets the directory for documentation.
658 *
659 * Linux: /usr/share/doc/<application>
660 * Windows: <program files directory>/<application>
661 * Old path: same as RTPathProgram()
662 *
663 * @returns iprt status code.
664 * @param pszPath Buffer where to store the path.
665 * @param cchPath Buffer size in bytes.
666 */
667RTDECL(int) RTPathAppDocs(char *pszPath, unsigned cchPath)
668{
669#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
670 char *pszUtf8Path;
671 int rc;
672 rc = rtPathFromNative(&pszUtf8Path, RTPATH_APP_DOCS);
673 if (RT_SUCCESS(rc))
674 {
675 size_t cchPathAppDocs = strlen(pszUtf8Path);
676 if (cchPathAppDocs < cchPath)
677 memcpy(pszPath, pszUtf8Path, cchPathAppDocs + 1);
678 else
679 rc = VERR_BUFFER_OVERFLOW;
680 RTStrFree(pszUtf8Path);
681 }
682 return rc;
683#else
684 return RTPathProgram(pszPath, cchPath);
685#endif
686}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette