VirtualBox

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

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

fix building Windows guest additions from Linux hosts

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