VirtualBox

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

Last change on this file since 2705 was 1307, checked in by vboxsync, 18 years ago

Added temps to avoid attempts to free bogus (because modified) pointers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.9 KB
Line 
1/* $Id: path.cpp 1307 2007-03-07 16:53:15Z vboxsync $ */
2/** @file
3 * InnoTek Portable Runtime - Path Manipulation.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung 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
37
38/**
39 * Strips the filename from a path.
40 *
41 * @param pszPath Path which filename should be extracted from.
42 *
43 */
44RTDECL(void) RTPathStripFilename(char *pszPath)
45{
46 char *psz = pszPath;
47 char *pszLastSep = pszPath;
48
49 for (;; psz++)
50 {
51 switch (*psz)
52 {
53 /* handle separators. */
54#if defined(__WIN__) || defined(__OS2__)
55 case ':':
56 pszLastSep = psz + 1;
57 break;
58
59 case '\\':
60#endif
61 case '/':
62 pszLastSep = psz;
63 break;
64
65 /* the end */
66 case '\0':
67 if (pszLastSep == pszPath)
68 *pszLastSep++ = '.';
69 *pszLastSep = '\0';
70 return;
71 }
72 }
73 /* will never get here */
74}
75
76
77/**
78 * Strips the extension from a path.
79 *
80 * @param pszPath Path which extension should be stripped.
81 */
82RTDECL(void) RTPathStripExt(char *pszPath)
83{
84 char *pszDot = NULL;
85 for (;; pszPath++)
86 {
87 switch (*pszPath)
88 {
89 /* handle separators. */
90#if defined(__WIN__) || defined(__OS2__)
91 case ':':
92 case '\\':
93#endif
94 case '/':
95 pszDot = NULL;
96 break;
97 case '.':
98 pszDot = pszPath;
99 break;
100
101 /* the end */
102 case '\0':
103 if (pszDot)
104 *pszDot = '\0';
105 return;
106 }
107 }
108 /* will never get here */
109}
110
111
112/**
113 * Finds the filename in a path.
114 *
115 * @returns Pointer to filename within pszPath.
116 * @returns NULL if no filename (i.e. empty string or ends with a slash).
117 * @param pszPath Path to find filename in.
118 */
119RTDECL(char *) RTPathFilename(const char *pszPath)
120{
121 const char *psz = pszPath;
122 const char *pszLastComp = pszPath;
123
124 for (;; psz++)
125 {
126 switch (*psz)
127 {
128 /* handle separators. */
129#if defined(__WIN__) || defined(__OS2__)
130 case ':':
131 pszLastComp = psz + 1;
132 break;
133
134 case '\\':
135#endif
136 case '/':
137 pszLastComp = psz + 1;
138 break;
139
140 /* the end */
141 case '\0':
142 if (*pszLastComp)
143 return (char *)(void *)pszLastComp;
144 return NULL;
145 }
146 }
147
148 /* will never get here */
149 return NULL;
150}
151
152
153/**
154 * Strips the trailing slashes of a path name.
155 *
156 * @param pszPath Path to strip.
157 */
158RTDECL(void) RTPathStripTrailingSlash(char *pszPath)
159{
160 char *pszEnd = strchr(pszPath, '\0');
161 while (pszEnd-- > pszPath)
162 {
163 switch (*pszEnd)
164 {
165 case '/':
166#if defined(__WIN__) || defined(__OS2__)
167 case '\\':
168#endif
169 *pszEnd = '\0';
170 break;
171 default:
172 return;
173 }
174 }
175 return;
176}
177
178
179/**
180 * Finds the extension part of in a path.
181 *
182 * @returns Pointer to extension within pszPath.
183 * @returns NULL if no extension.
184 * @param pszPath Path to find extension in.
185 */
186RTDECL(char *) RTPathExt(const char *pszPath)
187{
188 const char *psz = pszPath;
189 const char *pszExt = NULL;
190
191 for (;; psz++)
192 {
193 switch (*psz)
194 {
195 /* handle separators. */
196#if defined(__WIN__) || defined(__OS2__)
197 case ':':
198 pszExt = NULL;
199 break;
200
201 case '\\':
202#endif
203 case '/':
204 pszExt = NULL;
205 break;
206 case '.':
207 pszExt = psz;
208 break;
209
210 /* the end */
211 case '\0':
212 if (pszExt)
213 return (char *)(void *)pszExt;
214 return NULL;
215 }
216 }
217
218 /* will never get here */
219 return NULL;
220}
221
222
223/**
224 * Checks if a path have an extension.
225 *
226 * @returns true if extension present.
227 * @returns false if no extension.
228 * @param pszPath Path to check.
229 */
230RTDECL(bool) RTPathHaveExt(const char *pszPath)
231{
232 return RTPathExt(pszPath) != NULL;
233}
234
235
236/**
237 * Checks if a path includes more than a filename.
238 *
239 * @returns true if path present.
240 * @returns false if no path.
241 * @param pszPath Path to check.
242 */
243RTDECL(bool) RTPathHavePath(const char *pszPath)
244{
245#if defined(__WIN__) || defined(__OS2__)
246 return strpbrk(pszPath, "/\\:") != NULL;
247#else
248 return strpbrk(pszPath, "/") != NULL;
249#endif
250}
251
252
253/**
254 * Helper for RTPathCompare() and RTPathStartsWith().
255 *
256 * @returns similar to strcmp.
257 * @param pszPath1 Path to compare.
258 * @param pszPath2 Path to compare.
259 * @param fLimit Limit the comparison to the length of \a pszPath2
260 * @internal
261 */
262static int rtPathCompare(const char *pszPath1, const char *pszPath2, bool fLimit)
263{
264 if (pszPath1 == pszPath2)
265 return 0;
266 if (!pszPath1)
267 return -1;
268 if (!pszPath2)
269 return 1;
270
271#if defined(__WIN__) || defined(__OS2__)
272 PRTUNICP puszPath1;
273 int rc = RTStrToUni(pszPath1, &puszPath1);
274 if (RT_FAILURE(rc))
275 return -1;
276 PRTUNICP puszPath2;
277 rc = RTStrToUni(pszPath2, &puszPath2);
278 if (RT_FAILURE(rc))
279 {
280 RTUniFree(puszPath1);
281 return 1;
282 }
283
284 int iDiff = 0;
285 PRTUNICP puszTmpPath1 = puszPath1;
286 PRTUNICP puszTmpPath2 = puszPath2;
287 for (;;)
288 {
289 register RTUNICP uc1 = *puszTmpPath1;
290 register RTUNICP uc2 = *puszTmpPath2;
291 if (uc1 != uc2)
292 {
293 if (uc1 == '\\')
294 uc1 = '/';
295 else
296 uc1 = RTUniCpToUpper(uc1);
297 if (uc2 == '\\')
298 uc2 = '/';
299 else
300 uc2 = RTUniCpToUpper(uc2);
301 if (uc1 != uc2)
302 {
303 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
304 if (fLimit && uc2 == '\0')
305 iDiff = 0;
306 break;
307 }
308 }
309 if (!uc1)
310 break;
311 puszTmpPath1++;
312 puszTmpPath2++;
313
314 }
315
316 RTUniFree(puszPath2);
317 RTUniFree(puszPath1);
318 return iDiff;
319
320#else
321 if (!fLimit)
322 return strcmp(pszPath1, pszPath2);
323 return strncmp(pszPath1, pszPath2, strlen(pszPath2));
324#endif
325}
326
327
328/**
329 * Compares two paths.
330 *
331 * The comparison takes platform-dependent details into account,
332 * such as:
333 * <ul>
334 * <li>On DOS-like platforms, both |\| and |/| separator chars are considered
335 * to be equal.
336 * <li>On platforms with case-insensitive file systems, mismatching characters
337 * are uppercased and compared again.
338 * </ul>
339 *
340 * @remark
341 *
342 * File system details are currently ignored. This means that you won't get
343 * case-insentive compares on unix systems when a path goes into a case-insensitive
344 * filesystem like FAT, HPFS, HFS, NTFS, JFS, or similar. For NT, OS/2 and similar
345 * you'll won't get case-sensitve compares on a case-sensitive file system.
346 *
347 * @param pszPath1 Path to compare (must be an absolute path).
348 * @param pszPath2 Path to compare (must be an absolute path).
349 *
350 * @returns < 0 if the first path less than the second path.
351 * @returns 0 if the first path identical to the second path.
352 * @returns > 0 if the first path greater than the second path.
353 */
354RTDECL(int) RTPathCompare(const char *pszPath1, const char *pszPath2)
355{
356 return rtPathCompare(pszPath1, pszPath2, false /* full path lengths */);
357}
358
359
360/**
361 * Checks if a path starts with the given parent path.
362 *
363 * This means that either the path and the parent path matches completely, or that
364 * the path is to some file or directory residing in the tree given by the parent
365 * directory.
366 *
367 * The path comparison takes platform-dependent details into account,
368 * see RTPathCompare() for details.
369 *
370 * @param pszPath Path to check, must be an absolute path.
371 * @param pszParentPath Parent path, must be an absolute path.
372 * No trailing directory slash!
373 *
374 * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
375 * are identical), or |false| otherwise.
376 *
377 * @remark This API doesn't currently handle root directory compares in a manner
378 * consistant with the other APIs. RTPathStartsWith(pszSomePath, "/") will
379 * not work if pszSomePath isn't "/".
380 */
381RTDECL(bool) RTPathStartsWith(const char *pszPath, const char *pszParentPath)
382{
383 if (pszPath == pszParentPath)
384 return true;
385 if (!pszPath || !pszParentPath)
386 return false;
387
388 if (rtPathCompare(pszPath, pszParentPath, true /* limited by path 2 */) != 0)
389 return false;
390
391 const size_t cchParentPath = strlen(pszParentPath);
392 return RTPATH_IS_SLASH(pszPath[cchParentPath])
393 || pszPath[cchParentPath] == '\0';
394}
395
396
397/**
398 * Same as RTPathReal only the result is RTStrDup()'ed.
399 *
400 * @returns Pointer to real path. Use RTStrFree() to free this string.
401 * @returns NULL if RTPathReal() or RTStrDup() fails.
402 * @param pszPath
403 */
404RTDECL(char *) RTPathRealDup(const char *pszPath)
405{
406 char szPath[RTPATH_MAX];
407 int rc = RTPathReal(pszPath, szPath, sizeof(szPath));
408 if (RT_SUCCESS(rc))
409 return RTStrDup(szPath);
410 return NULL;
411}
412
413
414/**
415 * Same as RTPathAbs only the result is RTStrDup()'ed.
416 *
417 * @returns Pointer to real path. Use RTStrFree() to free this string.
418 * @returns NULL if RTPathAbs() or RTStrDup() fails.
419 * @param pszPath The path to resolve.
420 */
421RTDECL(char *) RTPathAbsDup(const char *pszPath)
422{
423 char szPath[RTPATH_MAX];
424 int rc = RTPathAbs(pszPath, szPath, sizeof(szPath));
425 if (RT_SUCCESS(rc))
426 return RTStrDup(szPath);
427 return NULL;
428}
429
430
431/**
432 * Returns the length of the volume name specifier of the given path.
433 * If no such specifier zero is returned.
434 */
435size_t rtPathVolumeSpecLen(const char *pszPath)
436{
437#if defined (__OS2__) || defined (__WIN__)
438 if (pszPath && *pszPath)
439 {
440 /* UTC path. */
441 if ( (pszPath[0] == '\\' || pszPath[0] == '/')
442 && (pszPath[1] == '\\' || pszPath[1] == '/'))
443 return strcspn(pszPath + 2, "\\/") + 2;
444
445 /* Drive letter. */
446 if ( pszPath[1] == ':'
447 && toupper(pszPath[0]) >= 'A' && toupper(pszPath[0]) <= 'Z')
448 return 2;
449 }
450 return 0;
451
452#else
453 /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
454 /// @todo (dmik) well, it's better to consider there's no volume name
455 // at all on *nix systems
456 return 0;
457// return pszPath && pszPath[0] == '/';
458#endif
459}
460
461
462/**
463 * Get the absolute path (no symlinks, no . or .. components), assuming the
464 * given base path as the current directory. The resulting path doesn't have
465 * to exist.
466 *
467 * @returns iprt status code.
468 * @param pszBase The base path to act like a current directory.
469 * When NULL, the actual cwd is used (i.e. the call
470 * is equivalent to RTPathAbs(pszPath, ...).
471 * @param pszPath The path to resolve.
472 * @param pszAbsPath Where to store the absolute path.
473 * @param cchAbsPath Size of the buffer.
474 */
475RTDECL(int) RTPathAbsEx(const char *pszBase, const char *pszPath, char *pszAbsPath, unsigned cchAbsPath)
476{
477 if (pszBase && pszPath && !rtPathVolumeSpecLen(pszPath))
478 {
479#if defined(__WIN__)
480 /* The format for very long paths is not supported. */
481 if ( (pszBase[0] == '/' || pszBase[0] == '\\')
482 && (pszBase[1] == '/' || pszBase[1] == '\\')
483 && pszBase[2] == '?'
484 && (pszBase[3] == '/' || pszBase[3] == '\\'))
485 return VERR_INVALID_NAME;
486#endif
487
488 /** @todo there are a couple of things which isn't 100% correct, although the
489 * current code will have to work for now - I don't have time to fix it right now.
490 *
491 * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
492 * not necessarily resolve it on the right drive.
493 * 2) A trailing slash in the base might cause UNC names to be created.
494 * 3) The lengths total doesn't have to be less than max length
495 * if the pszPath starts with a slash.
496 */
497 size_t cchBase = strlen(pszBase);
498 size_t cchPath = strlen(pszPath);
499 if (cchBase + cchPath >= RTPATH_MAX)
500 return VERR_FILENAME_TOO_LONG;
501
502 bool fRootSpec = pszPath[0] == '/'
503#if defined(__WIN__) || defined(__OS2__)
504 || pszPath[0] == '\\'
505#endif
506 ;
507 size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
508 char szPath[RTPATH_MAX];
509 if (fRootSpec)
510 {
511 /* join the disk name from base and the path */
512 memcpy(szPath, pszBase, cchVolSpec);
513 strcpy(&szPath[cchVolSpec], pszPath);
514 }
515 else
516 {
517 /* join the base path and the path */
518 strcpy(szPath, pszBase);
519 szPath[cchBase] = RTPATH_DELIMITER;
520 strcpy(&szPath[cchBase + 1], pszPath);
521 }
522 return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
523 }
524
525 /* Fallback to the non *Ex version */
526 return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
527}
528
529
530/**
531 * Same as RTPathAbsEx only the result is RTStrDup()'ed.
532 *
533 * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
534 * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
535 * @param pszBase The base path to act like a current directory.
536 * When NULL, the actual cwd is used (i.e. the call
537 * is equivalent to RTPathAbs(pszPath, ...).
538 * @param pszPath The path to resolve.
539 */
540RTDECL(char *) RTPathAbsExDup(const char *pszBase, const char *pszPath)
541{
542 char szPath[RTPATH_MAX];
543 int rc = RTPathAbsEx(pszBase, pszPath, szPath, sizeof(szPath));
544 if (RT_SUCCESS(rc))
545 return RTStrDup(szPath);
546 return NULL;
547}
548
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