VirtualBox

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

Last change on this file since 19928 was 19928, checked in by vboxsync, 16 years ago

IPRT: More RTPathAppend docs updates.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette