Changeset 21673 in vbox for trunk/src/VBox/Runtime/common
- Timestamp:
- Jul 17, 2009 12:10:10 PM (16 years ago)
- Location:
- trunk/src/VBox/Runtime/common/path
- Files:
-
- 2 added
- 14 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/path/RTPathAbsDup.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathAbsDup 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/ dir.h>37 #include <iprt/err.h> 37 38 #include <iprt/param.h> 38 39 #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 the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 40 529 41 … … 544 56 } 545 57 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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathAbsEx.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathAbsEx 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/ dir.h>37 #include <iprt/err.h> 37 38 #include <iprt/param.h> 38 39 #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 40 #include "internal/fs.h" 46 #include "internal/path.h"47 #include "internal/process.h"48 41 49 50 /**51 * Strips the filename from a path. Truncates the given string in-place by overwriting the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 return 0;610 }611 42 612 43 … … 621 52 { 622 53 /* UTC path. */ 54 /** @todo r=bird: it's UNC and we have to check that the next char isn't a 55 * slash, then skip both the server and the share name. */ 623 56 if ( (pszPath[0] == '\\' || pszPath[0] == '/') 624 57 && (pszPath[1] == '\\' || pszPath[1] == '/')) … … 709 142 } 710 143 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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathAbsExDup.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathAbsExDup 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/ dir.h>37 #include <iprt/err.h> 37 38 #include <iprt/param.h> 38 39 #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 40 49 50 /**51 * Strips the filename from a path. Truncates the given string in-place by overwriting the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 41 711 42 … … 729 60 } 730 61 731 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathAppend.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathAppend 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 200 6-2007Sun Microsystems, Inc.7 * Copyright (C) 2009 Sun Microsystems, Inc. 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/ dir.h>37 #include <iprt/ param.h>37 #include <iprt/assert.h> 38 #include <iprt/err.h> 38 39 #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 the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 40 546 41 … … 608 103 #endif 609 104 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 105 } 730 106 … … 809 185 } 810 186 811 812 #ifndef RT_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathExt.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathExt 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #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 the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *pszEnd = '\0';287 break;288 default:289 return;290 }291 }292 return;293 }294 37 295 38 … … 337 80 } 338 81 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathFilename.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathFilename 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/dir.h>37 #include <iprt/param.h>38 37 #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 38 49 50 /**51 * Strips the filename from a path. Truncates the given string in-place by overwriting the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 39 226 40 … … 265 79 } 266 80 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathHaveExt.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathHaveExt 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #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 37 49 50 /**51 * Strips the filename from a path. Truncates the given string in-place by overwriting the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 38 339 39 … … 350 50 } 351 51 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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathHavePath.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathHavePath 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/dir.h>37 #include <iprt/param.h>38 37 #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 the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(bool) RTPathHaveExt(const char *pszPath)348 {349 return RTPathExt(pszPath) != NULL;350 }351 38 352 39 … … 367 54 } 368 55 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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathParse.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathParse 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #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 the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 37 139 38 … … 224 123 } 225 124 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathRealDup.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathRealDup 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/ dir.h>37 #include <iprt/err.h> 37 38 #include <iprt/param.h> 38 39 #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 40 49 50 /**51 * Strips the filename from a path. Truncates the given string in-place by overwriting the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 41 512 42 … … 527 57 } 528 58 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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathStripExt.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathStripExt 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/dir.h>37 #include <iprt/param.h>38 37 #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 38 49 50 /**51 * Strips the filename from a path. Truncates the given string in-place by overwriting the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 pszLastSep[0] = '\0';98 return;99 }100 }101 /* will never get here */102 }103 39 104 40 … … 137 73 } 138 74 139 140 /**141 * Parses a path.142 *143 * It figures the length of the directory component, the offset of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathStripFilename.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathStripFilename 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #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 37 49 38 … … 102 91 } 103 92 104 105 /**106 * Strips the extension from a path.107 *108 * @param pszPath Path which extension should be stripped.109 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/RTPathStripTrailingSlash.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - RTPathSTripTrailingSlash 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/dir.h>37 #include <iprt/param.h>38 37 #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 38 49 50 /**51 * Strips the filename from a path. Truncates the given string in-place by overwriting the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 39 267 40 … … 293 66 } 294 67 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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }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 pszPath2377 * @internal378 */379 static 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 else413 uc1 = RTUniCpToUpper(uc1);414 if (uc2 == '\\')415 uc2 = '/';416 else417 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 #else438 if (!fLimit)439 return strcmp(pszPath1, pszPath2);440 return strncmp(pszPath1, pszPath2, strlen(pszPath2));441 #endif442 }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 considered452 * to be equal.453 * <li>On platforms with case-insensitive file systems, mismatching characters454 * 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't465 * get case-insentive compares on unix systems when a path goes into a466 * case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or467 * similar. For NT, OS/2 and similar you'll won't get case-sensitve468 * compares on a case-sensitive file system.469 */470 RTDECL(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, or480 * that the path is to some file or directory residing in the tree given by the481 * 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 they487 * 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 a494 * manner consistant with the other APIs. RTPathStartsWith(pszSomePath,495 * "/") will not work if pszSomePath isn't "/".496 */497 RTDECL(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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 -
trunk/src/VBox/Runtime/common/path/comparepaths.cpp
r21668 r21673 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT - Path Manipulation.3 * IPRT - Path Comparison. 4 4 */ 5 5 … … 33 33 * Header Files * 34 34 *******************************************************************************/ 35 #include "internal/iprt.h" 35 36 #include <iprt/path.h> 36 #include <iprt/dir.h> 37 #include <iprt/param.h> 37 #include <iprt/err.h> 38 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 the52 * 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 */57 RTDECL(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 else74 pszPath = psz;75 break;76 77 case '\\':78 #endif79 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 else97 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 */110 RTDECL(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 #endif122 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 of144 * 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 this153 * 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 this156 * will be set to -1. Optional.157 * @param pfFlags Where to set flags returning more information about158 * the path. For the future. Optional.159 */160 RTDECL(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 #endif180 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 */234 RTDECL(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 #endif251 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 */275 RTDECL(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 #endif286 *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 */303 RTDECL(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 #endif320 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 */347 RTDECL(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 */360 RTDECL(bool) RTPathHavePath(const char *pszPath)361 {362 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)363 return strpbrk(pszPath, "/\\:") != NULL;364 #else365 return strpbrk(pszPath, "/") != NULL;366 #endif367 }368 39 369 40 … … 510 181 } 511 182 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 pszPath519 */520 RTDECL(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 */537 RTDECL(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 have556 * to deal with it where it matters.557 */558 static 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 #endif595 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 #endif609 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 */617 size_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 #else635 /* 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 name637 // at all on *nix systems638 return 0;639 // return pszPath && pszPath[0] == '/';640 #endif641 }642 643 644 /**645 * Get the absolute path (no symlinks, no . or .. components), assuming the646 * given base path as the current directory. The resulting path doesn't have647 * 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 call652 * 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 */657 RTDECL(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 #endif669 670 /** @todo there are a couple of things which isn't 100% correct, although the671 * 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 will674 * 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 length677 * 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 #endif688 ;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 else698 {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 call719 * is equivalent to RTPathAbs(pszPath, ...).720 * @param pszPath The path to resolve.721 */722 RTDECL(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 732 RTDECL(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) == 2763 && 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 else770 #endif771 {772 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)773 return VERR_BUFFER_OVERFLOW;774 *pszPathEnd++ = '/';775 }776 }777 else778 {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 else788 {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, but794 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_MINI813 814 RTDECL(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, for836 * 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 */843 RTDECL(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 else855 rc = VERR_BUFFER_OVERFLOW;856 RTStrFree(pszUtf8Path);857 }858 return rc;859 #else860 return RTPathExecDir(pszPath, cchPath);861 #endif862 }863 864 865 /**866 * Gets the directory for architecture-dependent application data, for867 * 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 */877 RTDECL(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 else889 rc = VERR_BUFFER_OVERFLOW;890 RTStrFree(pszUtf8Path);891 }892 return rc;893 #else894 return RTPathExecDir(pszPath, cchPath);895 #endif896 }897 898 899 /**900 * Gets the directory of shared libraries. This is not the same as901 * RTPathAppPrivateArch() as Linux depends all shared libraries in902 * a common global directory where ld.so can found them.903 *904 * Linux: /usr/lib905 * 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 */912 RTDECL(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 else924 rc = VERR_BUFFER_OVERFLOW;925 RTStrFree(pszUtf8Path);926 }927 return rc;928 #else929 return RTPathExecDir(pszPath, cchPath);930 #endif931 }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 */945 RTDECL(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 else957 rc = VERR_BUFFER_OVERFLOW;958 RTStrFree(pszUtf8Path);959 }960 return rc;961 #else962 return RTPathExecDir(pszPath, cchPath);963 #endif964 }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 */975 RTDECL(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 #else988 , "TMPDIR"989 #endif990 };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 use1000 * 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 about1004 * 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 TracChangeset
for help on using the changeset viewer.