VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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