VirtualBox

Changeset 21673 in vbox for trunk/src/VBox/Runtime/common


Ignore:
Timestamp:
Jul 17, 2009 12:10:10 PM (16 years ago)
Author:
vboxsync
Message:

IPRT: Split up r3/path.cpp.

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  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathAbsDup
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
     37#include <iprt/err.h>
    3738#include <iprt/param.h>
    3839#include <iprt/string.h>
    39 #include <iprt/assert.h>
    40 #include <iprt/string.h>
    41 #include <iprt/ctype.h>
    42 #include <iprt/err.h>
    43 #include <iprt/uni.h>
    44 #include <iprt/env.h>
    45 #include "internal/fs.h"
    46 #include "internal/path.h"
    47 #include "internal/process.h"
    48 
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 }
    52840
    52941
     
    54456}
    54557
    546 
    547 /**
    548  * Figures the length of the root part of the path.
    549  *
    550  * @returns length of the root specifier.
    551  * @retval  0 if none.
    552  *
    553  * @param   pszPath         The path to investigate.
    554  *
    555  * @remarks Unnecessary root slashes will not be counted. The caller will have
    556  *          to deal with it where it matters.
    557  */
    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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathAbsEx.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathAbsEx
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
     37#include <iprt/err.h>
    3738#include <iprt/param.h>
    3839#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>
    4540#include "internal/fs.h"
    46 #include "internal/path.h"
    47 #include "internal/process.h"
    4841
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    61142
    61243
     
    62152    {
    62253        /* 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. */
    62356        if (    (pszPath[0] == '\\' || pszPath[0] == '/')
    62457            &&  (pszPath[1] == '\\' || pszPath[1] == '/'))
     
    709142}
    710143
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathAbsExDup.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathAbsExDup
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
     37#include <iprt/err.h>
    3738#include <iprt/param.h>
    3839#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"
    4840
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    71041
    71142
     
    72960}
    73061
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathAppend.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathAppend
    44 */
    55
    66/*
    7  * Copyright (C) 2006-2007 Sun Microsystems, Inc.
     7 * Copyright (C) 2009 Sun Microsystems, Inc.
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
    37 #include <iprt/param.h>
     37#include <iprt/assert.h>
     38#include <iprt/err.h>
    3839#include <iprt/string.h>
    39 #include <iprt/assert.h>
    40 #include <iprt/string.h>
    41 #include <iprt/ctype.h>
    42 #include <iprt/err.h>
    43 #include <iprt/uni.h>
    44 #include <iprt/env.h>
    45 #include "internal/fs.h"
    46 #include "internal/path.h"
    47 #include "internal/process.h"
    48 
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 }
    54540
    54641
     
    608103#endif
    609104    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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;
    729105}
    730106
     
    809185}
    810186
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathExt.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathExt
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
    37 #include <iprt/param.h>
    38 #include <iprt/string.h>
    39 #include <iprt/assert.h>
    40 #include <iprt/string.h>
    41 #include <iprt/ctype.h>
    42 #include <iprt/err.h>
    43 #include <iprt/uni.h>
    44 #include <iprt/env.h>
    45 #include "internal/fs.h"
    46 #include "internal/path.h"
    47 #include "internal/process.h"
    48 
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    29437
    29538
     
    33780}
    33881
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathFilename.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathFilename
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
    37 #include <iprt/param.h>
    3837#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"
    4838
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    22539
    22640
     
    26579}
    26680
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathHaveExt.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathHaveExt
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#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"
    4837
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    33838
    33939
     
    35050}
    35151
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathHavePath.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathHavePath
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
    37 #include <iprt/param.h>
    3837#include <iprt/string.h>
    39 #include <iprt/assert.h>
    40 #include <iprt/string.h>
    41 #include <iprt/ctype.h>
    42 #include <iprt/err.h>
    43 #include <iprt/uni.h>
    44 #include <iprt/env.h>
    45 #include "internal/fs.h"
    46 #include "internal/path.h"
    47 #include "internal/process.h"
    48 
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    347 RTDECL(bool) RTPathHaveExt(const char *pszPath)
    348 {
    349     return RTPathExt(pszPath) != NULL;
    350 }
    35138
    35239
     
    36754}
    36855
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathParse.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathParse
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
    37 #include <iprt/param.h>
    38 #include <iprt/string.h>
    39 #include <iprt/assert.h>
    40 #include <iprt/string.h>
    41 #include <iprt/ctype.h>
    42 #include <iprt/err.h>
    43 #include <iprt/uni.h>
    44 #include <iprt/env.h>
    45 #include "internal/fs.h"
    46 #include "internal/path.h"
    47 #include "internal/process.h"
    48 
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    13837
    13938
     
    224123}
    225124
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathRealDup.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathRealDup
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
     37#include <iprt/err.h>
    3738#include <iprt/param.h>
    3839#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"
    4840
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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 }
    51141
    51242
     
    52757}
    52858
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathStripExt.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathStripExt
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
    37 #include <iprt/param.h>
    3837#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"
    4838
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    10339
    10440
     
    13773}
    13874
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathStripFilename.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathStripFilename
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#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"
    4837
    4938
     
    10291}
    10392
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/RTPathStripTrailingSlash.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - RTPathSTripTrailingSlash
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
    37 #include <iprt/param.h>
    3837#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"
    4838
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    26639
    26740
     
    29366}
    29467
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    368 
    369 
    370 /**
    371  * Helper for RTPathCompare() and RTPathStartsWith().
    372  *
    373  * @returns similar to strcmp.
    374  * @param   pszPath1        Path to compare.
    375  * @param   pszPath2        Path to compare.
    376  * @param   fLimit          Limit the comparison to the length of \a pszPath2
    377  * @internal
    378  */
    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             else
    413                 uc1 = RTUniCpToUpper(uc1);
    414             if (uc2 == '\\')
    415                 uc2 = '/';
    416             else
    417                 uc2 = RTUniCpToUpper(uc2);
    418             if (uc1 != uc2)
    419             {
    420                 iDiff = uc1 > uc2 ? 1 : -1; /* (overflow/underflow paranoia) */
    421                 if (fLimit && uc2 == '\0')
    422                     iDiff = 0;
    423                 break;
    424             }
    425         }
    426         if (!uc1)
    427             break;
    428         puszTmpPath1++;
    429         puszTmpPath2++;
    430 
    431     }
    432 
    433     RTUniFree(puszPath2);
    434     RTUniFree(puszPath1);
    435     return iDiff;
    436 
    437 #else
    438     if (!fLimit)
    439         return strcmp(pszPath1, pszPath2);
    440     return strncmp(pszPath1, pszPath2, strlen(pszPath2));
    441 #endif
    442 }
    443 
    444 
    445 /**
    446  * Compares two paths.
    447  *
    448  * The comparison takes platform-dependent details into account,
    449  * such as:
    450  * <ul>
    451  * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    452  *     to be equal.
    453  * <li>On platforms with case-insensitive file systems, mismatching characters
    454  *     are uppercased and compared again.
    455  * </ul>
    456  *
    457  * @returns @< 0 if the first path less than the second path.
    458  * @returns 0 if the first path identical to the second path.
    459  * @returns @> 0 if the first path greater than the second path.
    460  *
    461  * @param   pszPath1    Path to compare (must be an absolute path).
    462  * @param   pszPath2    Path to compare (must be an absolute path).
    463  *
    464  * @remarks File system details are currently ignored. This means that you won't
    465  *          get case-insentive compares on unix systems when a path goes into a
    466  *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
    467  *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
    468  *          compares on a case-sensitive file system.
    469  */
    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, or
    480  * that the path is to some file or directory residing in the tree given by the
    481  * parent directory.
    482  *
    483  * The path comparison takes platform-dependent details into account,
    484  * see RTPathCompare() for details.
    485  *
    486  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    487  *          are identical), or |false| otherwise.
    488  *
    489  * @param   pszPath         Path to check, must be an absolute path.
    490  * @param   pszParentPath   Parent path, must be an absolute path.
    491  *                          No trailing directory slash!
    492  *
    493  * @remarks This API doesn't currently handle root directory compares in a
    494  *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
    495  *          "/") will not work if pszSomePath isn't "/".
    496  */
    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   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
  • trunk/src/VBox/Runtime/common/path/comparepaths.cpp

    r21668 r21673  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Path Manipulation.
     3 * IPRT - Path Comparison.
    44 */
    55
     
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include "internal/iprt.h"
    3536#include <iprt/path.h>
    36 #include <iprt/dir.h>
    37 #include <iprt/param.h>
     37#include <iprt/err.h>
    3838#include <iprt/string.h>
    39 #include <iprt/assert.h>
    40 #include <iprt/string.h>
    41 #include <iprt/ctype.h>
    42 #include <iprt/err.h>
    43 #include <iprt/uni.h>
    44 #include <iprt/env.h>
    45 #include "internal/fs.h"
    46 #include "internal/path.h"
    47 #include "internal/process.h"
    48 
    49 
    50 /**
    51  * Strips the filename from a path. Truncates the given string in-place by overwriting the
    52  * last path separator character with a null byte in a platform-neutral way.
    53  *
    54  * @param   pszPath     Path from which filename should be extracted, will be truncated.
    55  *                      If the string contains no path separator, it will be changed to a "." string.
    56  */
    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                 else
    74                     pszPath = psz;
    75                 break;
    76 
    77             case '\\':
    78 #endif
    79             case '/':
    80                 pszLastSep = psz;
    81                 break;
    82 
    83             /* the end */
    84             case '\0':
    85                 if (!pszLastSep)
    86                 {
    87                     /* no directory component */
    88                     pszPath[0] = '.';
    89                     pszPath[1] = '\0';
    90                 }
    91                 else if (pszLastSep == pszPath)
    92                 {
    93                     /* only root. */
    94                     pszLastSep[1] = '\0';
    95                 }
    96                 else
    97                     pszLastSep[0] = '\0';
    98                 return;
    99         }
    100     }
    101     /* will never get here */
    102 }
    103 
    104 
    105 /**
    106  * Strips the extension from a path.
    107  *
    108  * @param   pszPath     Path which extension should be stripped.
    109  */
    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 #endif
    122             case '/':
    123                 pszDot = NULL;
    124                 break;
    125             case '.':
    126                 pszDot = pszPath;
    127                 break;
    128 
    129             /* the end */
    130             case '\0':
    131                 if (pszDot)
    132                     *pszDot = '\0';
    133                 return;
    134         }
    135     }
    136     /* will never get here */
    137 }
    138 
    139 
    140 /**
    141  * Parses a path.
    142  *
    143  * It figures the length of the directory component, the offset of
    144  * the file name and the location of the suffix dot.
    145  *
    146  * @returns The path length.
    147  *
    148  * @param   pszPath     Path to find filename in.
    149  * @param   pcbDir      Where to put the length of the directory component.
    150  *                      If no directory, this will be 0. Optional.
    151  * @param   poffName    Where to store the filename offset.
    152  *                      If empty string or if it's ending with a slash this
    153  *                      will be set to -1. Optional.
    154  * @param   poffSuff    Where to store the suffix offset (the last dot).
    155  *                      If empty string or if it's ending with a slash this
    156  *                      will be set to -1. Optional.
    157  * @param   pfFlags     Where to set flags returning more information about
    158  *                      the path. For the future. Optional.
    159  */
    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 #endif
    180             case '/':
    181                 pszName = psz + 1;
    182                 break;
    183 
    184             case '.':
    185                 pszLastDot = psz;
    186                 break;
    187 
    188             /*
    189              * The end. Complete the results.
    190              */
    191             case '\0':
    192             {
    193                 ssize_t offName = *pszName != '\0' ? pszName - pszPath : -1;
    194                 if (poffName)
    195                     *poffName = offName;
    196 
    197                 if (poffSuff)
    198                 {
    199                     ssize_t offSuff = -1;
    200                     if (pszLastDot)
    201                     {
    202                         offSuff = pszLastDot - pszPath;
    203                         if (offSuff <= offName)
    204                             offSuff = -1;
    205                     }
    206                     *poffSuff = offSuff;
    207                 }
    208 
    209                 if (pcchDir)
    210                 {
    211                     ssize_t off = offName - 1;
    212                     while (off >= offRoot && RTPATH_IS_SLASH(pszPath[off]))
    213                         off--;
    214                     *pcchDir = RT_MAX(off, offRoot) + 1;
    215                 }
    216 
    217                 return psz - pszPath;
    218             }
    219         }
    220     }
    221 
    222     /* will never get here */
    223     return 0;
    224 }
    225 
    226 
    227 /**
    228  * Finds the filename in a path.
    229  *
    230  * @returns Pointer to filename within pszPath.
    231  * @returns NULL if no filename (i.e. empty string or ends with a slash).
    232  * @param   pszPath     Path to find filename in.
    233  */
    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 #endif
    251             case '/':
    252                 pszName = psz + 1;
    253                 break;
    254 
    255             /* the end */
    256             case '\0':
    257                 if (*pszName)
    258                     return (char *)(void *)pszName;
    259                 return NULL;
    260         }
    261     }
    262 
    263     /* will never get here */
    264     return NULL;
    265 }
    266 
    267 
    268 /**
    269  * Strips the trailing slashes of a path name.
    270  *
    271  * @param   pszPath     Path to strip.
    272  *
    273  * @todo    This isn't safe for a root element! Needs fixing.
    274  */
    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 #endif
    286                 *pszEnd = '\0';
    287                 break;
    288             default:
    289                 return;
    290         }
    291     }
    292     return;
    293 }
    294 
    295 
    296 /**
    297  * Finds the extension part of in a path.
    298  *
    299  * @returns Pointer to extension within pszPath.
    300  * @returns NULL if no extension.
    301  * @param   pszPath     Path to find extension in.
    302  */
    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 #endif
    320             case '/':
    321                 pszExt = NULL;
    322                 break;
    323             case '.':
    324                 pszExt = psz;
    325                 break;
    326 
    327             /* the end */
    328             case '\0':
    329                 if (pszExt)
    330                     return (char *)(void *)pszExt;
    331                 return NULL;
    332         }
    333     }
    334 
    335     /* will never get here */
    336     return NULL;
    337 }
    338 
    339 
    340 /**
    341  * Checks if a path have an extension.
    342  *
    343  * @returns true if extension present.
    344  * @returns false if no extension.
    345  * @param   pszPath     Path to check.
    346  */
    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 #else
    365     return strpbrk(pszPath, "/") != NULL;
    366 #endif
    367 }
    36839
    36940
     
    510181}
    511182
    512 
    513 /**
    514  * Same as RTPathReal only the result is RTStrDup()'ed.
    515  *
    516  * @returns Pointer to real path. Use RTStrFree() to free this string.
    517  * @returns NULL if RTPathReal() or RTStrDup() fails.
    518  * @param   pszPath
    519  */
    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 have
    556  *          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 #endif
    595         return 1;
    596     }
    597 
    598 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    599     /* Drive specifier? */
    600     if (   pszPath[0] != '\0'
    601         && pszPath[1] == ':'
    602         && RT_C_IS_ALPHA(pszPath[0]))
    603     {
    604         if (RTPATH_IS_SLASH(pszPath[2]))
    605             return 3;
    606         return 2;
    607     }
    608 #endif
    609     return 0;
    610 }
    611 
    612 
    613 /**
    614  * Returns the length of the volume name specifier of the given path.
    615  * If no such specifier zero is returned.
    616  */
    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 #else
    635     /* This isn't quite right when looking at the above stuff, but it works assuming that '//' does not mean UNC. */
    636     /// @todo (dmik) well, it's better to consider there's no volume name
    637     //  at all on *nix systems
    638     return 0;
    639 //    return pszPath && pszPath[0] == '/';
    640 #endif
    641 }
    642 
    643 
    644 /**
    645  * Get the absolute path (no symlinks, no . or .. components), assuming the
    646  * given base path as the current directory. The resulting path doesn't have
    647  * to exist.
    648  *
    649  * @returns iprt status code.
    650  * @param   pszBase         The base path to act like a current directory.
    651  *                          When NULL, the actual cwd is used (i.e. the call
    652  *                          is equivalent to RTPathAbs(pszPath, ...).
    653  * @param   pszPath         The path to resolve.
    654  * @param   pszAbsPath      Where to store the absolute path.
    655  * @param   cchAbsPath      Size of the buffer.
    656  */
    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 #endif
    669 
    670         /** @todo there are a couple of things which isn't 100% correct, although the
    671          * current code will have to work for now - I don't have time to fix it right now.
    672          *
    673          * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will
    674          *    not necessarily resolve it on the right drive.
    675          * 2) A trailing slash in the base might cause UNC names to be created.
    676          * 3) The lengths total doesn't have to be less than max length
    677          *    if the pszPath starts with a slash.
    678          */
    679         size_t cchBase = strlen(pszBase);
    680         size_t cchPath = strlen(pszPath);
    681         if (cchBase + cchPath >= RTPATH_MAX)
    682             return VERR_FILENAME_TOO_LONG;
    683 
    684         bool fRootSpec = pszPath[0] == '/'
    685 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    686             || pszPath[0] == '\\'
    687 #endif
    688             ;
    689         size_t cchVolSpec = rtPathVolumeSpecLen(pszBase);
    690         char szPath[RTPATH_MAX];
    691         if (fRootSpec)
    692         {
    693             /* join the disk name from base and the path */
    694             memcpy(szPath, pszBase, cchVolSpec);
    695             strcpy(&szPath[cchVolSpec], pszPath);
    696         }
    697         else
    698         {
    699             /* join the base path and the path */
    700             strcpy(szPath, pszBase);
    701             szPath[cchBase] = RTPATH_DELIMITER;
    702             strcpy(&szPath[cchBase + 1], pszPath);
    703         }
    704         return RTPathAbs(szPath, pszAbsPath, cchAbsPath);
    705     }
    706 
    707     /* Fallback to the non *Ex version */
    708     return RTPathAbs(pszPath, pszAbsPath, cchAbsPath);
    709 }
    710 
    711 
    712 /**
    713  * Same as RTPathAbsEx only the result is RTStrDup()'ed.
    714  *
    715  * @returns Pointer to the absolute path. Use RTStrFree() to free this string.
    716  * @returns NULL if RTPathAbsEx() or RTStrDup() fails.
    717  * @param   pszBase         The base path to act like a current directory.
    718  *                          When NULL, the actual cwd is used (i.e. the call
    719  *                          is equivalent to RTPathAbs(pszPath, ...).
    720  * @param   pszPath         The path to resolve.
    721  */
    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) == 2
    763                 &&  pszPath[1] == ':'
    764                 &&  RT_C_IS_ALPHA(pszPath[0]))
    765             {
    766                 if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    767                     return VERR_BUFFER_OVERFLOW;
    768             }
    769             else
    770 #endif
    771             {
    772                 if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst)
    773                     return VERR_BUFFER_OVERFLOW;
    774                 *pszPathEnd++ = '/';
    775             }
    776         }
    777         else
    778         {
    779             /* One slash is sufficient at this point. */
    780             while (RTPATH_IS_SLASH(pszAppend[1]))
    781                 pszAppend++, cchAppend--;
    782 
    783             if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    784                 return VERR_BUFFER_OVERFLOW;
    785         }
    786     }
    787     else
    788     {
    789         /* No slashes needed in the appended bit. */
    790         while (RTPATH_IS_SLASH(*pszAppend))
    791             pszAppend++, cchAppend--;
    792 
    793         /* In the leading path we can skip unnecessary trailing slashes, but
    794            be sure to leave one. */
    795         size_t const cchRoot = rtPathRootSpecLen(pszPath);
    796         while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
    797                &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
    798             pszPathEnd--;
    799 
    800         if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst)
    801             return VERR_BUFFER_OVERFLOW;
    802     }
    803 
    804     /*
    805      * What remains now is the just the copying.
    806      */
    807     memcpy(pszPathEnd, pszAppend, cchAppend + 1);
    808     return VINF_SUCCESS;
    809 }
    810 
    811 
    812 #ifndef RT_MINI
    813 
    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, for
    836  * example NLS files, module sources, ...
    837  *
    838  * Linux:    /usr/shared/@<application@>
    839  * Windows:  @<program files directory@>/@<application@>
    840  * Old path: same as RTPathExecDir()
    841  *
    842  */
    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         else
    855             rc = VERR_BUFFER_OVERFLOW;
    856         RTStrFree(pszUtf8Path);
    857     }
    858     return rc;
    859 #else
    860     return RTPathExecDir(pszPath, cchPath);
    861 #endif
    862 }
    863 
    864 
    865 /**
    866  * Gets the directory for architecture-dependent application data, for
    867  * example modules which can be loaded at runtime.
    868  *
    869  * Linux:    /usr/lib/@<application@>
    870  * Windows:  @<program files directory@>/@<application@>
    871  * Old path: same as RTPathExecDir()
    872  *
    873  * @returns iprt status code.
    874  * @param   pszPath     Buffer where to store the path.
    875  * @param   cchPath     Buffer size in bytes.
    876  */
    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         else
    889             rc = VERR_BUFFER_OVERFLOW;
    890         RTStrFree(pszUtf8Path);
    891     }
    892     return rc;
    893 #else
    894     return RTPathExecDir(pszPath, cchPath);
    895 #endif
    896 }
    897 
    898 
    899 /**
    900  * Gets the directory of shared libraries. This is not the same as
    901  * RTPathAppPrivateArch() as Linux depends all shared libraries in
    902  * a common global directory where ld.so can found them.
    903  *
    904  * Linux:    /usr/lib
    905  * Windows:  @<program files directory@>/@<application@>
    906  * Old path: same as RTPathExecDir()
    907  *
    908  * @returns iprt status code.
    909  * @param   pszPath     Buffer where to store the path.
    910  * @param   cchPath     Buffer size in bytes.
    911  */
    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         else
    924             rc = VERR_BUFFER_OVERFLOW;
    925         RTStrFree(pszUtf8Path);
    926     }
    927     return rc;
    928 #else
    929     return RTPathExecDir(pszPath, cchPath);
    930 #endif
    931 }
    932 
    933 
    934 /**
    935  * Gets the directory for documentation.
    936  *
    937  * Linux:    /usr/share/doc/@<application@>
    938  * Windows:  @<program files directory@>/@<application@>
    939  * Old path: same as RTPathExecDir()
    940  *
    941  * @returns iprt status code.
    942  * @param   pszPath     Buffer where to store the path.
    943  * @param   cchPath     Buffer size in bytes.
    944  */
    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         else
    957             rc = VERR_BUFFER_OVERFLOW;
    958         RTStrFree(pszUtf8Path);
    959     }
    960     return rc;
    961 #else
    962     return RTPathExecDir(pszPath, cchPath);
    963 #endif
    964 }
    965 
    966 
    967 /**
    968  * Gets the temporary directory path.
    969  *
    970  * @returns iprt status code.
    971  *
    972  * @param   pszPath     Buffer where to store the path.
    973  * @param   cchPath     Buffer size in bytes.
    974  */
    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 #else
    988         , "TMPDIR"
    989 #endif
    990     };
    991     for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
    992     {
    993         int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
    994         if (rc != VERR_ENV_VAR_NOT_FOUND)
    995             return rc;
    996     }
    997 
    998     /*
    999      * Here we should use some sane system default, instead we just use
    1000      * the typical unix temp dir for now.
    1001      */
    1002     /** @todo Windows should default to the windows directory, see GetTempPath.
    1003      * Some unixes has path.h and _PATH_TMP. There is also a question about
    1004      * whether /var/tmp wouldn't be a better place...  */
    1005     if (cchPath < sizeof("/tmp") )
    1006         return VERR_BUFFER_OVERFLOW;
    1007     memcpy(pszPath, "/tmp", sizeof("/tmp"));
    1008     return VINF_SUCCESS;
    1009 }
    1010 
    1011 #endif /* !RT_MINI */
    1012 
Note: See TracChangeset for help on using the changeset viewer.

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