VirtualBox

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

Last change on this file since 21501 was 20101, checked in by vboxsync, 15 years ago

IPRT: Rewrote RTPathTemp and added a testcase for it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 28.2 KB
Line 
1/* $Id: path.cpp 20101 2009-05-27 16:43:08Z vboxsync $ */
2/** @file
3 * IPRT - 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 <iprt/env.h>
45#include "internal/fs.h"
46#include "internal/path.h"
47#include "internal/process.h"
48
49
50/**
51 * Strips the filename from a path. Truncates the given string in-place by overwriting the
52 * last path separator character with a null byte in a platform-neutral way.
53 *
54 * @param pszPath Path from which filename should be extracted, will be truncated.
55 * If the string contains no path separator, it will be changed to a "." string.
56 */
57RTDECL(void) RTPathStripFilename(char *pszPath)
58{
59 char *psz = pszPath;
60 char *pszLastSep = NULL;
61
62
63 for (;; psz++)
64 {
65 switch (*psz)
66 {
67 /* handle separators. */
68#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
69 case ':':
70 pszLastSep = psz + 1;
71 if (RTPATH_IS_SLASH(psz[1]))
72 pszPath = psz + 1;
73 else
74 pszPath = psz;
75 break;
76
77 case '\\':
78#endif
79 case '/':
80 pszLastSep = psz;
81 break;
82
83 /* the end */
84 case '\0':
85 if (!pszLastSep)
86 {
87 /* no directory component */
88 pszPath[0] = '.';
89 pszPath[1] = '\0';
90 }
91 else if (pszLastSep == pszPath)
92 {
93 /* only root. */
94 pszLastSep[1] = '\0';
95 }
96 else
97 pszLastSep[0] = '\0';
98 return;
99 }
100 }
101 /* will never get here */
102}
103
104
105/**
106 * Strips the extension from a path.
107 *
108 * @param pszPath Path which extension should be stripped.
109 */
110RTDECL(void) RTPathStripExt(char *pszPath)
111{
112 char *pszDot = NULL;
113 for (;; pszPath++)
114 {
115 switch (*pszPath)
116 {
117 /* handle separators. */
118#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
119 case ':':
120 case '\\':
121#endif
122 case '/':
123 pszDot = NULL;
124 break;
125 case '.':
126 pszDot = pszPath;
127 break;
128
129 /* the end */
130 case '\0':
131 if (pszDot)
132 *pszDot = '\0';
133 return;
134 }
135 }
136 /* will never get here */
137}
138
139
140/**
141 * Parses a path.
142 *
143 * It figures the length of the directory component, the offset of
144 * the file name and the location of the suffix dot.
145 *
146 * @returns The path length.
147 *
148 * @param pszPath Path to find filename in.
149 * @param pcbDir Where to put the length of the directory component.
150 * If no directory, this will be 0. Optional.
151 * @param poffName Where to store the filename offset.
152 * If empty string or if it's ending with a slash this
153 * will be set to -1. Optional.
154 * @param poffSuff Where to store the suffix offset (the last dot).
155 * If empty string or if it's ending with a slash this
156 * will be set to -1. Optional.
157 * @param pfFlags Where to set flags returning more information about
158 * the path. For the future. Optional.
159 */
160RTDECL(size_t) RTPathParse(const char *pszPath, size_t *pcchDir, ssize_t *poffName, ssize_t *poffSuff)
161{
162 const char *psz = pszPath;
163 ssize_t offRoot = 0;
164 const char *pszName = pszPath;
165 const char *pszLastDot = NULL;
166
167 for (;; psz++)
168 {
169 switch (*psz)
170 {
171 /* handle separators. */
172#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
173 case ':':
174 pszName = psz + 1;
175 offRoot = pszName - psz;
176 break;
177
178 case '\\':
179#endif
180 case '/':
181 pszName = psz + 1;
182 break;
183
184 case '.':
185 pszLastDot = psz;
186 break;
187
188 /*
189 * The end. Complete the results.
190 */
191 case '\0':
192 {
193 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
194 if (poffName)
195 *poffName = offName;
196
197 if (poffSuff)
198 {
199 ssize_t offSuff = -1;
200 if (pszLastDot)
201 {
202 offSuff = pszLastDot - pszPath;
203 if (offSuff <= offName)
204 offSuff = -1;
205 }
206 *poffSuff = offSuff;
207 }
208
209 if (pcchDir)
210 {
211 ssize_t off = offName - 1;
212 while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
213 off--;
214 *pcchDir = RT_MAX(off, offRoot) + 1;
215 }
216
217 return psz - pszPath;
218 }
219 }
220 }
221
222 /* will never get here */
223 return 0;
224}
225
226
227/**
228 * Finds the filename in a path.
229 *
230 * @returns Pointer to filename within pszPath.
231 * @returns NULL if no filename (i.e. empty string or ends with a slash).
232 * @param pszPath Path to find filename in.
233 */
234RTDECL(char *) RTPathFilename(const char *pszPath)
235{
236 const char *psz = pszPath;
237 const char *pszName = pszPath;
238
239 for (;; psz++)
240 {
241 switch (*psz)
242 {
243 /* handle separators. */
244#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
245 case ':':
246 pszName = psz + 1;
247 break;
248
249 case '\\':
250#endif
251 case '/':
252 pszName = psz + 1;
253 break;
254
255 /* the end */
256 case '\0':
257 if (*pszName)
258 return (char *)(void *)pszName;
259 return NULL;
260 }
261 }
262
263 /* will never get here */
264 return NULL;
265}
266
267
268/**
269 * Strips the trailing slashes of a path name.
270 *
271 * @param pszPath Path to strip.
272 *
273 * @todo This isn't safe for a root element! Needs fixing.
274 */
275RTDECL(void) RTPathStripTrailingSlash(char *pszPath)
276{
277 char *pszEnd = strchr(pszPath, '\0');
278 while (pszEnd-- > pszPath)
279 {
280 switch (*pszEnd)
281 {
282 case '/':
283#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
284 case '\\':
285#endif
286 *pszEnd = '\0';
287 break;
288 default:
289 return;
290 }
291 }
292 return;
293}
294
295
296/**
297 * Finds the extension part of in a path.
298 *
299 * @returns Pointer to extension within pszPath.
300 * @returns NULL if no extension.
301 * @param pszPath Path to find extension in.
302 */
303RTDECL(char *) RTPathExt(const char *pszPath)
304{
305 const char *psz = pszPath;
306 const char *pszExt = NULL;
307
308 for (;; psz++)
309 {
310 switch (*psz)
311 {
312 /* handle separators. */
313#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
314 case ':':
315 pszExt = NULL;
316 break;
317
318 case '\\':
319#endif
320 case '/':
321 pszExt = NULL;
322 break;
323 case '.':
324 pszExt = psz;
325 break;
326
327 /* the end */
328 case '\0':
329 if (pszExt)
330 return (char *)(void *)pszExt;
331 return NULL;
332 }
333 }
334
335 /* will never get here */
336 return NULL;
337}
338
339
340/**
341 * Checks if a path have an extension.
342 *
343 * @returns true if extension present.
344 * @returns false if no extension.
345 * @param pszPath Path to check.
346 */
347RTDECL(bool) RTPathHaveExt(const char *pszPath)
348{
349 return RTPathExt(pszPath) != NULL;
350}
351
352
353/**
354 * Checks if a path includes more than a filename.
355 *
356 * @returns true if path present.
357 * @returns false if no path.
358 * @param pszPath Path to check.
359 */
360RTDECL(bool) RTPathHavePath(const char *pszPath)
361{
362#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
363 return strpbrk(pszPath, "/\\:") != NULL;
364#else
365 return strpbrk(pszPath, "/") != NULL;
366#endif
367}
368
369
370/**
371 * Helper for RTPathCompare() and RTPathStartsWith().
372 *
373 * @returns similar to strcmp.
374 * @param pszPath1 Path to compare.
375 * @param pszPath2 Path to compare.
376 * @param fLimit Limit the comparison to the length of \a pszPath2
377 * @internal
378 */
379static int rtPathCompare(const char *pszPath1, const char *pszPath2, bool fLimit)
380{
381 if (pszPath1 == pszPath2)
382 return 0;
383 if (!pszPath1)
384 return -1;
385 if (!pszPath2)
386 return 1;
387
388#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
389 PRTUNICP puszPath1;
390 int rc = RTStrToUni(pszPath1, &puszPath1);
391 if (RT_FAILURE(rc))
392 return -1;
393 PRTUNICP puszPath2;
394 rc = RTStrToUni(pszPath2, &puszPath2);
395 if (RT_FAILURE(rc))
396 {
397 RTUniFree(puszPath1);
398 return 1;
399 }
400
401 int iDiff = 0;
402 PRTUNICP puszTmpPath1 = puszPath1;
403 PRTUNICP puszTmpPath2 = puszPath2;
404 for (;;)
405 {
406 register RTUNICP uc1 = *puszTmpPath1;
407 register RTUNICP uc2 = *puszTmpPath2;
408 if (uc1 != uc2)
409 {
410 if (uc1 == '\\')
411 uc1 = '/';
412 else
413 uc1 = RTUniCpToUpper(uc1);
414 if (uc2 == '\\')
415 uc2 = '/';
416 else
417 uc2 = RTUniCpToUpper(uc2);
418 if (uc1 != uc2)
419 {
420 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
421 if (fLimit && uc2 == '\0')
422 iDiff = 0;
423 break;
424 }
425 }
426 if (!uc1)
427 break;
428 puszTmpPath1++;
429 puszTmpPath2++;
430
431 }
432
433 RTUniFree(puszPath2);
434 RTUniFree(puszPath1);
435 return iDiff;
436
437#else
438 if (!fLimit)
439 return strcmp(pszPath1, pszPath2);
440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));
441#endif
442}
443
444
445/**
446 * Compares two paths.
447 *
448 * The comparison takes platform-dependent details into account,
449 * such as:
450 * <ul>
451 * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
452 * to be equal.
453 * <li>On platforms with case-insensitive file systems, mismatching characters
454 * are uppercased and compared again.
455 * </ul>
456 *
457 * @returns @< 0 if the first path less than the second path.
458 * @returns 0 if the first path identical to the second path.
459 * @returns @> 0 if the first path greater than the second path.
460 *
461 * @param pszPath1 Path to compare (must be an absolute path).
462 * @param pszPath2 Path to compare (must be an absolute path).
463 *
464 * @remarks File system details are currently ignored. This means that you won't
465 * get case-insentive compares on unix systems when a path goes into a
466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve
468 * compares on a case-sensitive file system.
469 */
470RTDECL(int) RTPathCompare(const char *pszPath1, const char *pszPath2)
471{
472 return rtPathCompare(pszPath1, pszPath2, false /* full path lengths */);
473}
474
475
476/**
477 * Checks if a path starts with the given parent path.
478 *
479 * This means that either the path and the parent path matches completely, or
480 * that the path is to some file or directory residing in the tree given by the
481 * parent directory.
482 *
483 * The path comparison takes platform-dependent details into account,
484 * see RTPathCompare() for details.
485 *
486 * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
487 * are identical), or |false| otherwise.
488 *
489 * @param pszPath Path to check, must be an absolute path.
490 * @param pszParentPath Parent path, must be an absolute path.
491 * No trailing directory slash!
492 *
493 * @remarks This API doesn't currently handle root directory compares in a
494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
495 * "/") will not work if pszSomePath isn't "/".
496 */
497RTDECL(bool) RTPathStartsWith(const char *pszPath, const char *pszParentPath)
498{
499 if (pszPath == pszParentPath)
500 return true;
501 if (!pszPath || !pszParentPath)
502 return false;
503
504 if (rtPathCompare(pszPath, pszParentPath, true /* limited by path 2 */) != 0)
505 return false;
506
507 const size_t cchParentPath = strlen(pszParentPath);
508 return RTPATH_IS_SLASH(pszPath[cchParentPath])
509 || pszPath[cchParentPath] == '\0';
510}
511
512
513/**
514 * Same as RTPathReal only the result is RTStrDup()'ed.
515 *
516 * @returns Pointer to real path. Use RTStrFree() to free this string.
517 * @returns NULL if RTPathReal() or RTStrDup() fails.
518 * @param pszPath
519 */
520RTDECL(char *) RTPathRealDup(const char *pszPath)
521{
522 char szPath[RTPATH_MAX];
523 int rc = RTPathReal(pszPath, szPath, sizeof(szPath));
524 if (RT_SUCCESS(rc))
525 return RTStrDup(szPath);
526 return NULL;
527}
528
529
530/**
531 * Same as RTPathAbs only the result is RTStrDup()'ed.
532 *
533 * @returns Pointer to real path. Use RTStrFree() to free this string.
534 * @returns NULL if RTPathAbs() or RTStrDup() fails.
535 * @param pszPath The path to resolve.
536 */
537RTDECL(char *) RTPathAbsDup(const char *pszPath)
538{
539 char szPath[RTPATH_MAX];
540 int rc = RTPathAbs(pszPath, szPath, sizeof(szPath));
541 if (RT_SUCCESS(rc))
542 return RTStrDup(szPath);
543 return NULL;
544}
545
546
547/**
548 * Figures the length of the root part of the path.
549 *
550 * @returns length of the root specifier.
551 * @retval 0 if none.
552 *
553 * @param pszPath The path to investigate.
554 *
555 * @remarks Unnecessary root slashes will not be counted. The caller will have
556 * to deal with it where it matters.
557 */
558static size_t rtPathRootSpecLen(const char *pszPath)
559{
560 /* fend of wildlife. */
561 if (!pszPath)
562 return 0;
563
564 /* Root slash? */
565 if (RTPATH_IS_SLASH(pszPath[0]))
566 {
567#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
568 /* UNC? */
569 if ( RTPATH_IS_SLASH(pszPath[1])
570 && pszPath[2] != '\0'
571 && !RTPATH_IS_SLASH(pszPath[2]))
572 {
573 /* Find the end of the server name. */
574 const char *pszEnd = pszPath + 2;
575 pszEnd += 2;
576 while ( *pszEnd != '\0'
577 && !RTPATH_IS_SLASH(*pszEnd))
578 pszEnd++;
579 if (RTPATH_IS_SLASH(*pszEnd))
580 {
581 pszEnd++;
582 while (RTPATH_IS_SLASH(*pszEnd))
583 pszEnd++;
584
585 /* Find the end of the share name */
586 while ( *pszEnd != '\0'
587 && !RTPATH_IS_SLASH(*pszEnd))
588 pszEnd++;
589 if (RTPATH_IS_SLASH(*pszEnd))
590 pszEnd++;
591 return pszPath - pszEnd;
592 }
593 }
594#endif
595 return 1;
596 }
597
598#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
599 /* Drive specifier? */
600 if ( pszPath[0] != '\0'
601 && pszPath[1] == ':'
602 && RT_C_IS_ALPHA(pszPath[0]))
603 {
604 if (RTPATH_IS_SLASH(pszPath[2]))
605 return 3;
606 return 2;
607 }
608#endif
609 return 0;
610}
611
612
613/**
614 * Returns the length of the volume name specifier of the given path.
615 * If no such specifier zero is returned.
616 */
617size_t rtPathVolumeSpecLen(const char *pszPath)
618{
619#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
620 if (pszPath && *pszPath)
621 {
622 /* UTC path. */
623 if ( (pszPath[0] == '\\' || pszPath[0] == '/')
624 && (pszPath[1] == '\\' || pszPath[1] == '/'))
625 return strcspn(pszPath + 2, "\\/") + 2;
626
627 /* Drive letter. */
628 if ( pszPath[1] == ':'
629 && toupper(pszPath[0]) >= 'A' && toupper(pszPath[0]) <= 'Z')
630 return 2;
631 }
632 return 0;
633
634#else
635 /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
636 /// @todo (dmik) well, it's better to consider there's no volume name
637 // at all on *nix systems
638 return 0;
639// return pszPath && pszPath[0] == '/';
640#endif
641}
642
643
644/**
645 * Get the absolute path (no symlinks, no . or .. components), assuming the
646 * given base path as the current directory. The resulting path doesn't have
647 * to exist.
648 *
649 * @returns iprt status code.
650 * @param pszBase The base path to act like a current directory.
651 * When NULL, the actual cwd is used (i.e. the call
652 * is equivalent to RTPathAbs(pszPath, ...).
653 * @param pszPath The path to resolve.
654 * @param pszAbsPath Where to store the absolute path.
655 * @param cchAbsPath Size of the buffer.
656 */
657RTDECL(int) RTPathAbsEx(const char *pszBase, const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
658{
659 if (pszBase && pszPath && !rtPathVolumeSpecLen(pszPath))
660 {
661#if defined(RT_OS_WINDOWS)
662 /* The format for very long paths is not supported. */
663 if ( (pszBase[0] == '/' || pszBase[0] == '\\')
664 && (pszBase[1] == '/' || pszBase[1] == '\\')
665 && pszBase[2] == '?'
666 && (pszBase[3] == '/' || pszBase[3] == '\\'))
667 return VERR_INVALID_NAME;
668#endif
669
670 /** @todo there are a couple of things which isn't 100% correct, although the
671 * current code will have to work for now - I don't have time to fix it right now.
672 *
673 * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
674 * not necessarily resolve it on the right drive.
675 * 2) A trailing slash in the base might cause UNC names to be created.
676 * 3) The lengths total doesn't have to be less than max length
677 * if the pszPath starts with a slash.
678 */
679 size_t cchBase = strlen(pszBase);
680 size_t cchPath = strlen(pszPath);
681 if (cchBase + cchPath >= RTPATH_MAX)
682 return VERR_FILENAME_TOO_LONG;
683
684 bool fRootSpec = pszPath[0] == '/'
685#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
686 || pszPath[0] == '\\'
687#endif
688 ;
689 size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
690 char szPath[RTPATH_MAX];
691 if (fRootSpec)
692 {
693 /* join the disk name from base and the path */
694 memcpy(szPath, pszBase, cchVolSpec);
695 strcpy(&szPath[cchVolSpec], pszPath);
696 }
697 else
698 {
699 /* join the base path and the path */
700 strcpy(szPath, pszBase);
701 szPath[cchBase] = RTPATH_DELIMITER;
702 strcpy(&szPath[cchBase + 1], pszPath);
703 }
704 return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
705 }
706
707 /* Fallback to the non *Ex version */
708 return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
709}
710
711
712/**
713 * Same as RTPathAbsEx only the result is RTStrDup()'ed.
714 *
715 * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
716 * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
717 * @param pszBase The base path to act like a current directory.
718 * When NULL, the actual cwd is used (i.e. the call
719 * is equivalent to RTPathAbs(pszPath, ...).
720 * @param pszPath The path to resolve.
721 */
722RTDECL(char *) RTPathAbsExDup(const char *pszBase, const char *pszPath)
723{
724 char szPath[RTPATH_MAX];
725 int rc = RTPathAbsEx(pszBase, pszPath, szPath, sizeof(szPath));
726 if (RT_SUCCESS(rc))
727 return RTStrDup(szPath);
728 return NULL;
729}
730
731
732RTDECL(int) RTPathAppend(char *pszPath, size_t cbPathDst, const char *pszAppend)
733{
734 char *pszPathEnd = (char *)memchr(pszPath, '\0', cbPathDst);
735 AssertReturn(pszPathEnd, VERR_INVALID_PARAMETER);
736
737 /*
738 * Special cases.
739 */
740 if (!pszAppend)
741 return VINF_SUCCESS;
742 size_t cchAppend = strlen(pszAppend);
743 if (!cchAppend)
744 return VINF_SUCCESS;
745 if (pszPathEnd == pszPath)
746 {
747 if (cchAppend >= cbPathDst)
748 return VERR_BUFFER_OVERFLOW;
749 memcpy(pszPath, pszAppend, cchAppend + 1);
750 return VINF_SUCCESS;
751 }
752
753 /*
754 * Balance slashes and check for buffer overflow.
755 */
756 bool fAddSlash = false;
757 if (!RTPATH_IS_SLASH(pszPathEnd[-1]))
758 {
759 if (!RTPATH_IS_SLASH(pszAppend[0]))
760 {
761#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
762 if ( (size_t)(pszPathEnd - pszPath) == 2
763 && pszPath[1] == ':'
764 && RT_C_IS_ALPHA(pszPath[0]))
765 {
766 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
767 return VERR_BUFFER_OVERFLOW;
768 }
769 else
770#endif
771 {
772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
773 return VERR_BUFFER_OVERFLOW;
774 *pszPathEnd++ = '/';
775 }
776 }
777 else
778 {
779 /* One slash is sufficient at this point. */
780 while (RTPATH_IS_SLASH(pszAppend[1]))
781 pszAppend++, cchAppend--;
782
783 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
784 return VERR_BUFFER_OVERFLOW;
785 }
786 }
787 else
788 {
789 /* No slashes needed in the appended bit. */
790 while (RTPATH_IS_SLASH(*pszAppend))
791 pszAppend++, cchAppend--;
792
793 /* In the leading path we can skip unnecessary trailing slashes, but
794 be sure to leave one. */
795 size_t const cchRoot = rtPathRootSpecLen(pszPath);
796 while ( (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
797 && RTPATH_IS_SLASH(pszPathEnd[-2]))
798 pszPathEnd--;
799
800 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
801 return VERR_BUFFER_OVERFLOW;
802 }
803
804 /*
805 * What remains now is the just the copying.
806 */
807 memcpy(pszPathEnd, pszAppend, cchAppend + 1);
808 return VINF_SUCCESS;
809}
810
811
812#ifndef RT_MINI
813
814RTDECL(int) RTPathExecDir(char *pszPath, size_t cchPath)
815{
816 AssertReturn(g_szrtProcExePath[0], VERR_WRONG_ORDER);
817
818 /*
819 * Calc the length and check if there is space before copying.
820 */
821 size_t cch = g_cchrtProcDir;
822 if (cch <= cchPath)
823 {
824 memcpy(pszPath, g_szrtProcExePath, cch);
825 pszPath[cch] = '\0';
826 return VINF_SUCCESS;
827 }
828
829 AssertMsgFailed(("Buffer too small (%zu <= %zu)\n", cchPath, cch));
830 return VERR_BUFFER_OVERFLOW;
831}
832
833
834/**
835 * Gets the directory for architecture-independent application data, for
836 * example NLS files, module sources, ...
837 *
838 * Linux: /usr/shared/@<application@>
839 * Windows: @<program files directory@>/@<application@>
840 * Old path: same as RTPathExecDir()
841 *
842 */
843RTDECL(int) RTPathAppPrivateNoArch(char *pszPath, size_t cchPath)
844{
845#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
846 char *pszUtf8Path;
847 int rc;
848 rc = rtPathFromNative(&pszUtf8Path, RTPATH_APP_PRIVATE);
849 if (RT_SUCCESS(rc))
850 {
851 size_t cchPathPrivateNoArch = strlen(pszUtf8Path);
852 if (cchPathPrivateNoArch < cchPath)
853 memcpy(pszPath, pszUtf8Path, cchPathPrivateNoArch + 1);
854 else
855 rc = VERR_BUFFER_OVERFLOW;
856 RTStrFree(pszUtf8Path);
857 }
858 return rc;
859#else
860 return RTPathExecDir(pszPath, cchPath);
861#endif
862}
863
864
865/**
866 * Gets the directory for architecture-dependent application data, for
867 * example modules which can be loaded at runtime.
868 *
869 * Linux: /usr/lib/@<application@>
870 * Windows: @<program files directory@>/@<application@>
871 * Old path: same as RTPathExecDir()
872 *
873 * @returns iprt status code.
874 * @param pszPath Buffer where to store the path.
875 * @param cchPath Buffer size in bytes.
876 */
877RTDECL(int) RTPathAppPrivateArch(char *pszPath, size_t cchPath)
878{
879#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
880 char *pszUtf8Path;
881 int rc;
882 rc = rtPathFromNative(&pszUtf8Path, RTPATH_APP_PRIVATE_ARCH);
883 if (RT_SUCCESS(rc))
884 {
885 size_t cchPathPrivateArch = strlen(pszUtf8Path);
886 if (cchPathPrivateArch < cchPath)
887 memcpy(pszPath, pszUtf8Path, cchPathPrivateArch + 1);
888 else
889 rc = VERR_BUFFER_OVERFLOW;
890 RTStrFree(pszUtf8Path);
891 }
892 return rc;
893#else
894 return RTPathExecDir(pszPath, cchPath);
895#endif
896}
897
898
899/**
900 * Gets the directory of shared libraries. This is not the same as
901 * RTPathAppPrivateArch() as Linux depends all shared libraries in
902 * a common global directory where ld.so can found them.
903 *
904 * Linux: /usr/lib
905 * Windows: @<program files directory@>/@<application@>
906 * Old path: same as RTPathExecDir()
907 *
908 * @returns iprt status code.
909 * @param pszPath Buffer where to store the path.
910 * @param cchPath Buffer size in bytes.
911 */
912RTDECL(int) RTPathSharedLibs(char *pszPath, size_t cchPath)
913{
914#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
915 char *pszUtf8Path;
916 int rc;
917 rc = rtPathFromNative(&pszUtf8Path, RTPATH_SHARED_LIBS);
918 if (RT_SUCCESS(rc))
919 {
920 size_t cchPathSharedLibs = strlen(pszUtf8Path);
921 if (cchPathSharedLibs < cchPath)
922 memcpy(pszPath, pszUtf8Path, cchPathSharedLibs + 1);
923 else
924 rc = VERR_BUFFER_OVERFLOW;
925 RTStrFree(pszUtf8Path);
926 }
927 return rc;
928#else
929 return RTPathExecDir(pszPath, cchPath);
930#endif
931}
932
933
934/**
935 * Gets the directory for documentation.
936 *
937 * Linux: /usr/share/doc/@<application@>
938 * Windows: @<program files directory@>/@<application@>
939 * Old path: same as RTPathExecDir()
940 *
941 * @returns iprt status code.
942 * @param pszPath Buffer where to store the path.
943 * @param cchPath Buffer size in bytes.
944 */
945RTDECL(int) RTPathAppDocs(char *pszPath, size_t cchPath)
946{
947#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
948 char *pszUtf8Path;
949 int rc;
950 rc = rtPathFromNative(&pszUtf8Path, RTPATH_APP_DOCS);
951 if (RT_SUCCESS(rc))
952 {
953 size_t cchPathAppDocs = strlen(pszUtf8Path);
954 if (cchPathAppDocs < cchPath)
955 memcpy(pszPath, pszUtf8Path, cchPathAppDocs + 1);
956 else
957 rc = VERR_BUFFER_OVERFLOW;
958 RTStrFree(pszUtf8Path);
959 }
960 return rc;
961#else
962 return RTPathExecDir(pszPath, cchPath);
963#endif
964}
965
966
967/**
968 * Gets the temporary directory path.
969 *
970 * @returns iprt status code.
971 *
972 * @param pszPath Buffer where to store the path.
973 * @param cchPath Buffer size in bytes.
974 */
975RTDECL(int) RTPathTemp(char *pszPath, size_t cchPath)
976{
977 /*
978 * Try get it from the environment first.
979 */
980 static const char * const s_apszVars[] =
981 {
982 "IPRT_TMPDIR"
983#if defined(RT_OS_WINDOWS)
984 , "TMP", "TEMP", "USERPROFILE"
985#elif defined(RT_OS_OS2)
986 , "TMP", "TEMP", "TMPDIR"
987#else
988 , "TMPDIR"
989#endif
990 };
991 for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
992 {
993 int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
994 if (rc != VERR_ENV_VAR_NOT_FOUND)
995 return rc;
996 }
997
998 /*
999 * Here we should use some sane system default, instead we just use
1000 * the typical unix temp dir for now.
1001 */
1002 /** @todo Windows should default to the windows directory, see GetTempPath.
1003 * Some unixes has path.h and _PATH_TMP. There is also a question about
1004 * whether /var/tmp wouldn't be a better place... */
1005 if (cchPath < sizeof("/tmp") )
1006 return VERR_BUFFER_OVERFLOW;
1007 memcpy(pszPath, "/tmp", sizeof("/tmp"));
1008 return VINF_SUCCESS;
1009}
1010
1011#endif /* !RT_MINI */
1012
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