VirtualBox

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

Last change on this file since 8170 was 8170, checked in by vboxsync, 16 years ago

Rebranding: replacing more innotek strings.

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