VirtualBox

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

Last change on this file since 980 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.8 KB
Line 
1/* $Id: path.cpp 1 1970-01-01 00:00:00Z 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 for (;;)
286 {
287 register RTUNICP uc1 = *puszPath1;
288 register RTUNICP uc2 = *puszPath2;
289 if (uc1 != uc2)
290 {
291 if (uc1 == '\\')
292 uc1 = '/';
293 else
294 uc1 = RTUniCpToUpper(uc1);
295 if (uc2 == '\\')
296 uc2 = '/';
297 else
298 uc2 = RTUniCpToUpper(uc2);
299 if (uc1 != uc2)
300 {
301 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
302 if (fLimit && uc2 == '\0')
303 iDiff = 0;
304 break;
305 }
306 }
307 if (!uc1)
308 break;
309 puszPath1++;
310 puszPath2++;
311
312 }
313
314 RTUniFree(puszPath2);
315 RTUniFree(puszPath1);
316 return iDiff;
317
318#else
319 if (!fLimit)
320 return strcmp(pszPath1, pszPath2);
321 return strncmp(pszPath1, pszPath2, strlen(pszPath2));
322#endif
323}
324
325
326/**
327 * Compares two paths.
328 *
329 * The comparison takes platform-dependent details into account,
330 * such as:
331 * <ul>
332 * <li>On DOS-like platforms, both |\| and |/| separator chars are considered
333 * to be equal.
334 * <li>On platforms with case-insensitive file systems, mismatching characters
335 * are uppercased and compared again.
336 * </ul>
337 *
338 * @remark
339 *
340 * File system details are currently ignored. This means that you won't get
341 * case-insentive compares on unix systems when a path goes into a case-insensitive
342 * filesystem like FAT, HPFS, HFS, NTFS, JFS, or similar. For NT, OS/2 and similar
343 * you'll won't get case-sensitve compares on a case-sensitive file system.
344 *
345 * @param pszPath1 Path to compare (must be an absolute path).
346 * @param pszPath2 Path to compare (must be an absolute path).
347 *
348 * @returns < 0 if the first path less than the second path.
349 * @returns 0 if the first path identical to the second path.
350 * @returns > 0 if the first path greater than the second path.
351 */
352RTDECL(int) RTPathCompare(const char *pszPath1, const char *pszPath2)
353{
354 return rtPathCompare(pszPath1, pszPath2, false /* full path lengths */);
355}
356
357
358/**
359 * Checks if a path starts with the given parent path.
360 *
361 * This means that either the path and the parent path matches completely, or that
362 * the path is to some file or directory residing in the tree given by the parent
363 * directory.
364 *
365 * The path comparison takes platform-dependent details into account,
366 * see RTPathCompare() for details.
367 *
368 * @param pszPath Path to check, must be an absolute path.
369 * @param pszParentPath Parent path, must be an absolute path.
370 * No trailing directory slash!
371 *
372 * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
373 * are identical), or |false| otherwise.
374 *
375 * @remark This API doesn't currently handle root directory compares in a manner
376 * consistant with the other APIs. RTPathStartsWith(pszSomePath, "/") will
377 * not work if pszSomePath isn't "/".
378 */
379RTDECL(bool) RTPathStartsWith(const char *pszPath, const char *pszParentPath)
380{
381 if (pszPath == pszParentPath)
382 return true;
383 if (!pszPath || !pszParentPath)
384 return false;
385
386 if (rtPathCompare(pszPath, pszParentPath, true /* limited by path 2 */) != 0)
387 return false;
388
389 const size_t cchParentPath = strlen(pszParentPath);
390 return RTPATH_IS_SLASH(pszPath[cchParentPath])
391 || pszPath[cchParentPath] == '\0';
392}
393
394
395/**
396 * Same as RTPathReal only the result is RTStrDup()'ed.
397 *
398 * @returns Pointer to real path. Use RTStrFree() to free this string.
399 * @returns NULL if RTPathReal() or RTStrDup() fails.
400 * @param pszPath
401 */
402RTDECL(char *) RTPathRealDup(const char *pszPath)
403{
404 char szPath[RTPATH_MAX];
405 int rc = RTPathReal(pszPath, szPath, sizeof(szPath));
406 if (RT_SUCCESS(rc))
407 return RTStrDup(szPath);
408 return NULL;
409}
410
411
412/**
413 * Same as RTPathAbs only the result is RTStrDup()'ed.
414 *
415 * @returns Pointer to real path. Use RTStrFree() to free this string.
416 * @returns NULL if RTPathAbs() or RTStrDup() fails.
417 * @param pszPath The path to resolve.
418 */
419RTDECL(char *) RTPathAbsDup(const char *pszPath)
420{
421 char szPath[RTPATH_MAX];
422 int rc = RTPathAbs(pszPath, szPath, sizeof(szPath));
423 if (RT_SUCCESS(rc))
424 return RTStrDup(szPath);
425 return NULL;
426}
427
428
429/**
430 * Returns the length of the volume name specifier of the given path.
431 * If no such specifier zero is returned.
432 */
433size_t rtPathVolumeSpecLen(const char *pszPath)
434{
435#if defined (__OS2__) || defined (__WIN__)
436 if (pszPath && *pszPath)
437 {
438 /* UTC path. */
439 if ( (pszPath[0] == '\\' || pszPath[0] == '/')
440 && (pszPath[1] == '\\' || pszPath[1] == '/'))
441 return strcspn(pszPath + 2, "\\/") + 2;
442
443 /* Drive letter. */
444 if ( pszPath[1] == ':'
445 && toupper(pszPath[0]) >= 'A' && toupper(pszPath[0]) <= 'Z')
446 return 2;
447 }
448 return 0;
449
450#else
451 /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
452 /// @todo (dmik) well, it's better to consider there's no volume name
453 // at all on *nix systems
454 return 0;
455// return pszPath && pszPath[0] == '/';
456#endif
457}
458
459
460/**
461 * Get the absolute path (no symlinks, no . or .. components), assuming the
462 * given base path as the current directory. The resulting path doesn't have
463 * to exist.
464 *
465 * @returns iprt status code.
466 * @param pszBase The base path to act like a current directory.
467 * When NULL, the actual cwd is used (i.e. the call
468 * is equivalent to RTPathAbs(pszPath, ...).
469 * @param pszPath The path to resolve.
470 * @param pszAbsPath Where to store the absolute path.
471 * @param cchAbsPath Size of the buffer.
472 */
473RTDECL(int) RTPathAbsEx(const char *pszBase, const char *pszPath, char *pszAbsPath, unsigned cchAbsPath)
474{
475 if (pszBase && pszPath && !rtPathVolumeSpecLen(pszPath))
476 {
477#if defined(__WIN__)
478 /* The format for very long paths is not supported. */
479 if ( (pszBase[0] == '/' || pszBase[0] == '\\')
480 && (pszBase[1] == '/' || pszBase[1] == '\\')
481 && pszBase[2] == '?'
482 && (pszBase[3] == '/' || pszBase[3] == '\\'))
483 return VERR_INVALID_NAME;
484#endif
485
486 /** @todo there are a couple of things which isn't 100% correct, although the
487 * current code will have to work for now - I don't have time to fix it right now.
488 *
489 * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
490 * not necessarily resolve it on the right drive.
491 * 2) A trailing slash in the base might cause UNC names to be created.
492 * 3) The lengths total doesn't have to be less than max length
493 * if the pszPath starts with a slash.
494 */
495 size_t cchBase = strlen(pszBase);
496 size_t cchPath = strlen(pszPath);
497 if (cchBase + cchPath >= RTPATH_MAX)
498 return VERR_FILENAME_TOO_LONG;
499
500 bool fRootSpec = pszPath[0] == '/'
501#if defined(__WIN__) || defined(__OS2__)
502 || pszPath[0] == '\\'
503#endif
504 ;
505 size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
506 char szPath[RTPATH_MAX];
507 if (fRootSpec)
508 {
509 /* join the disk name from base and the path */
510 memcpy(szPath, pszBase, cchVolSpec);
511 strcpy(&szPath[cchVolSpec], pszPath);
512 }
513 else
514 {
515 /* join the base path and the path */
516 strcpy(szPath, pszBase);
517 szPath[cchBase] = RTPATH_DELIMITER;
518 strcpy(&szPath[cchBase + 1], pszPath);
519 }
520 return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
521 }
522
523 /* Fallback to the non *Ex version */
524 return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
525}
526
527
528/**
529 * Same as RTPathAbsEx only the result is RTStrDup()'ed.
530 *
531 * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
532 * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
533 * @param pszBase The base path to act like a current directory.
534 * When NULL, the actual cwd is used (i.e. the call
535 * is equivalent to RTPathAbs(pszPath, ...).
536 * @param pszPath The path to resolve.
537 */
538RTDECL(char *) RTPathAbsExDup(const char *pszBase, const char *pszPath)
539{
540 char szPath[RTPATH_MAX];
541 int rc = RTPathAbsEx(pszBase, pszPath, szPath, sizeof(szPath));
542 if (RT_SUCCESS(rc))
543 return RTStrDup(szPath);
544 return NULL;
545}
546
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