VirtualBox

Changeset 19926 in vbox


Ignore:
Timestamp:
May 22, 2009 11:37:39 PM (16 years ago)
Author:
vboxsync
Message:

IPRT: Added RTPathAppend.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/path.h

    r19924 r19926  
    299299 * such as:
    300300 * <ul>
    301  * <li>On DOS-like platforms, both |\| and |/| separator chars are considered
     301 * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    302302 *     to be equal.
    303303 * <li>On platforms with case-insensitive file systems, mismatching characters
     
    305305 * </ul>
    306306 *
    307  * File system details are currently ignored. This means that you won't get
    308  * case-insensitive compares on unix systems when a path goes into a case-insensitive
    309  * filesystem like FAT, HPFS, HFS, NTFS, JFS, or similar. For NT, OS/2 and similar
    310  * you'll won't get case-sensitive compares on a case-sensitive file system.
    311  *
    312  * @param   pszPath1    Path to compare (must be an absolute path).
    313  * @param   pszPath2    Path to compare (must be an absolute path).
    314  *
    315307 * @returns @< 0 if the first path less than the second path.
    316308 * @returns 0 if the first path identical to the second path.
    317309 * @returns @> 0 if the first path greater than the second path.
     310 *
     311 * @param   pszPath1    Path to compare (must be an absolute path).
     312 * @param   pszPath2    Path to compare (must be an absolute path).
     313 *
     314 * @remarks File system details are currently ignored. This means that you won't
     315 *          get case-insentive compares on unix systems when a path goes into a
     316 *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
     317 *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
     318 *          compares on a case-sensitive file system.
    318319 */
    319320RTDECL(int) RTPathCompare(const char *pszPath1, const char *pszPath2);
     
    322323 * Checks if a path starts with the given parent path.
    323324 *
    324  * This means that either the path and the parent path matches completely, or that
    325  * the path is to some file or directory residing in the tree given by the parent
    326  * directory.
     325 * This means that either the path and the parent path matches completely, or
     326 * that the path is to some file or directory residing in the tree given by the
     327 * parent directory.
    327328 *
    328329 * The path comparison takes platform-dependent details into account,
    329330 * see RTPathCompare() for details.
     331 *
     332 * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
     333 *          are identical), or |false| otherwise.
    330334 *
    331335 * @param   pszPath         Path to check, must be an absolute path.
     
    333337 *                          No trailing directory slash!
    334338 *
    335  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    336  *          are identical), or |false| otherwise.
    337  *
    338  * @remark  This API doesn't currently handle root directory compares in a manner
    339  *          consistent with the other APIs. RTPathStartsWith(pszSomePath, "/") will
    340  *          not work if pszSomePath isn't "/".
     339 * @remarks This API doesn't currently handle root directory compares in a
     340 *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
     341 *          "/") will not work if pszSomePath isn't "/".
    341342 */
    342343RTDECL(bool) RTPathStartsWith(const char *pszPath, const char *pszParentPath);
     344
     345/**
     346 * Appends one partial path to another.
     347 *
     348 * The main purpose of this function is to deal correctly with leading and
     349 * trailing slashes.
     350 *
     351 * @returns IPRT status code.
     352 * @retval  VERR_PATH
     353 *
     354 * @param   pszPath         The path to append pszAppend to. This serves as both
     355 *                          input and output. This can be empty, in which case
     356 *                          pszAppend is just copied over.
     357 * @param   cchPathDst      The size of the buffer pszPath points to. This
     358 *                          should NOT be strlen(pszPath).
     359 * @param   pszAppend       The partial path to append to pszPath. This can be
     360 *                          NULL, in which case nothing is done.
     361 *
     362 * @remarks On OS/2, Window and similar systems, concatenating a drive letter
     363 *          specifier with a root prefixed path will result in an absolute path.
     364 *          Meaning, RTPathAppend(strcpy(szBuf, "C:"), sizeof(szBuf), "/bar")
     365 *          will result in "C:/bar". (This follows directly from the behavior
     366 *          when pszPath is empty.)
     367 */
     368RTDECL(int) RTPathAppend(char *pszPath, size_t cchPathDst, const char *pszAppend);
    343369
    344370
  • trunk/src/VBox/Runtime/r3/path.cpp

    r19924 r19926  
    448448 * such as:
    449449 * <ul>
    450  * <li>On DOS-like platforms, both |\| and |/| separator chars are considered
     450 * <li>On DOS-like platforms, both separator chars (|\| and |/|) are considered
    451451 *     to be equal.
    452452 * <li>On platforms with case-insensitive file systems, mismatching characters
     
    454454 * </ul>
    455455 *
    456  * @remark
    457  *
    458  * File system details are currently ignored. This means that you won't get
    459  * case-insentive compares on unix systems when a path goes into a case-insensitive
    460  * filesystem like FAT, HPFS, HFS, NTFS, JFS, or similar. For NT, OS/2 and similar
    461  * you'll won't get case-sensitve compares on a case-sensitive file system.
    462  *
    463  * @param   pszPath1    Path to compare (must be an absolute path).
    464  * @param   pszPath2    Path to compare (must be an absolute path).
    465  *
    466456 * @returns @< 0 if the first path less than the second path.
    467457 * @returns 0 if the first path identical to the second path.
    468458 * @returns @> 0 if the first path greater than the second path.
     459 *
     460 * @param   pszPath1    Path to compare (must be an absolute path).
     461 * @param   pszPath2    Path to compare (must be an absolute path).
     462 *
     463 * @remarks File system details are currently ignored. This means that you won't
     464 *          get case-insentive compares on unix systems when a path goes into a
     465 *          case-insensitive filesystem like FAT, HPFS, HFS, NTFS, JFS, or
     466 *          similar. For NT, OS/2 and similar you'll won't get case-sensitve
     467 *          compares on a case-sensitive file system.
    469468 */
    470469RTDECL(int) RTPathCompare(const char *pszPath1, const char *pszPath2)
     
    477476 * Checks if a path starts with the given parent path.
    478477 *
    479  * This means that either the path and the parent path matches completely, or that
    480  * the path is to some file or directory residing in the tree given by the parent
    481  * directory.
     478 * This means that either the path and the parent path matches completely, or
     479 * that the path is to some file or directory residing in the tree given by the
     480 * parent directory.
    482481 *
    483482 * The path comparison takes platform-dependent details into account,
    484483 * see RTPathCompare() for details.
     484 *
     485 * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
     486 *          are identical), or |false| otherwise.
    485487 *
    486488 * @param   pszPath         Path to check, must be an absolute path.
     
    488490 *                          No trailing directory slash!
    489491 *
    490  * @returns |true| when \a pszPath starts with \a pszParentPath (or when they
    491  *          are identical), or |false| otherwise.
    492  *
    493  * @remark  This API doesn't currently handle root directory compares in a manner
    494  *          consistant with the other APIs. RTPathStartsWith(pszSomePath, "/") will
    495  *          not work if pszSomePath isn't "/".
     492 * @remarks This API doesn't currently handle root directory compares in a
     493 *          manner consistant with the other APIs. RTPathStartsWith(pszSomePath,
     494 *          "/") will not work if pszSomePath isn't "/".
    496495 */
    497496RTDECL(bool) RTPathStartsWith(const char *pszPath, const char *pszParentPath)
     
    542541        return RTStrDup(szPath);
    543542    return NULL;
     543}
     544
     545
     546/**
     547 * Figures the length of the root part of the path.
     548 *
     549 * @returns length of the root specifier.
     550 * @retval  0 if none.
     551 *
     552 * @param   pszPath         The path to investigate.
     553 *
     554 * @remarks Unnecessary root slashes will not be counted. The caller will have
     555 *          to deal with it where it matters.
     556 */
     557static size_t rtPathRootSpecLen(const char *pszPath)
     558{
     559    /* fend of wildlife. */
     560    if (!pszPath)
     561        return 0;
     562
     563    /* Root slash? */
     564    if (RTPATH_IS_SLASH(pszPath[0]))
     565    {
     566#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
     567        /* UNC? */
     568        if (    RTPATH_IS_SLASH(pszPath[1])
     569            &&  pszPath[2] != '\0'
     570            &&  !RTPATH_IS_SLASH(pszPath[2]))
     571        {
     572            /* Find the end of the server name. */
     573            const char *pszEnd = pszPath + 2;
     574            pszEnd += 2;
     575            while (   *pszEnd != '\0'
     576                   && !RTPATH_IS_SLASH(*pszEnd))
     577                pszEnd++;
     578            if (RTPATH_IS_SLASH(*pszEnd))
     579            {
     580                pszEnd++;
     581                while (RTPATH_IS_SLASH(*pszEnd))
     582                    pszEnd++;
     583
     584                /* Find the end of the share name */
     585                while (   *pszEnd != '\0'
     586                       && !RTPATH_IS_SLASH(*pszEnd))
     587                    pszEnd++;
     588                if (RTPATH_IS_SLASH(*pszEnd))
     589                    pszEnd++;
     590                return pszPath - pszEnd;
     591            }
     592        }
     593#endif
     594        return 1;
     595    }
     596
     597#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
     598    /* Drive specifier? */
     599    if (   pszPath[0] != '\0'
     600        && pszPath[1] == ':'
     601        && RT_C_IS_ALPHA(pszPath[0]))
     602    {
     603        if (RTPATH_IS_SLASH(pszPath[2]))
     604            return 3;
     605        return 2;
     606    }
     607#endif
     608    return 0;
    544609}
    545610
     
    664729
    665730
     731RTDECL(int) RTPathAppend(char *pszPath, size_t cchPath, const char *pszAppend)
     732{
     733    char *pszPathEnd = (char *)memchr(pszPath, '\0', cchPath);
     734    AssertReturn(pszPathEnd, VERR_INVALID_PARAMETER);
     735
     736    /*
     737     * Special cases.
     738     */
     739    if (!pszAppend)
     740        return VINF_SUCCESS;
     741    size_t cchAppend = strlen(pszAppend);
     742    if (!cchAppend)
     743        return VINF_SUCCESS;
     744    if (pszPathEnd == pszPath)
     745    {
     746        if (cchAppend >= cchPath)
     747            return VERR_BUFFER_OVERFLOW;
     748        memcpy(pszPath, pszAppend, cchAppend + 1);
     749        return VINF_SUCCESS;
     750    }
     751
     752    /*
     753     * Balance slashes and check for buffer overflow.
     754     */
     755    bool fAddSlash = false;
     756    if (!RTPATH_IS_SLASH(pszPathEnd[-1]))
     757    {
     758        if (!RTPATH_IS_SLASH(pszAppend[0]))
     759        {
     760#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
     761            if (    pszPath[1] == ':'
     762                &&  RT_C_IS_ALPHA(pszPath[0]))
     763            {
     764                if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cchPath)
     765                    return VERR_BUFFER_OVERFLOW;
     766            }
     767            else
     768#endif
     769            {
     770                if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cchPath)
     771                    return VERR_BUFFER_OVERFLOW;
     772                *pszPathEnd++ = '/';
     773            }
     774        }
     775        else
     776        {
     777            /* One slash is sufficient at this point. */
     778            while (RTPATH_IS_SLASH(pszAppend[1]))
     779                pszAppend++, cchAppend--;
     780
     781            if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cchPath)
     782                return VERR_BUFFER_OVERFLOW;
     783        }
     784    }
     785    else
     786    {
     787        /* No slashes needed in the appended bit. */
     788        while (RTPATH_IS_SLASH(*pszAppend))
     789            pszAppend++, cchAppend--;
     790
     791        /* In the leading path we can skip unnecessary trailing slashes, but
     792           be sure to leave one. */
     793        size_t const cchRoot = rtPathRootSpecLen(pszPath);
     794        while (     (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot)
     795               &&   RTPATH_IS_SLASH(pszPathEnd[-2]))
     796            pszPathEnd--;
     797
     798        if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cchPath)
     799            return VERR_BUFFER_OVERFLOW;
     800    }
     801
     802    /*
     803     * What remains now is the just the copying.
     804     */
     805    memcpy(pszPathEnd, pszAppend, cchAppend + 1);
     806    return VINF_SUCCESS;
     807}
     808
     809
    666810#ifndef RT_MINI
    667811
  • trunk/src/VBox/Runtime/testcase/tstPath.cpp

    r19925 r19926  
    4545int main()
    4646{
     47    char szPath[RTPATH_MAX];
     48
    4749    /*
    4850     * Init RT+Test.
     
    6264     */
    6365    RTTestSub(hTest, "RTPathExecDir");
    64     char szPath[RTPATH_MAX];
    6566    RTTESTI_CHECK_RC(RTPathExecDir(szPath, sizeof(szPath)), VINF_SUCCESS);
    6667    if (RT_SUCCESS(rc))
     
    232233        const char *pszInput  = s_apszStripFilenameTests[i];
    233234        const char *pszExpect = s_apszStripFilenameTests[i + 1];
    234         char szPath[RTPATH_MAX];
    235235        strcpy(szPath, pszInput);
    236236        RTPathStripFilename(szPath);
     
    248248     * RTPathAppend.
    249249     */
     250    RTTestSub(hTest, "RTPathAppend");
     251    static const char *s_apszAppendTests[] =
     252    {
     253        /* base                 append                  result */
     254        "/",                    "",                     "/",
     255        "",                     "/",                    "/",
     256        "/",                    "/",                    "/",
     257        "/x",                   "",                     "/x",
     258        "/x",                   "/",                    "/x/",
     259        "/",                    "x",                    "/x",
     260        "dir",                  "file",                 "dir/file",
     261        "dir",                  "/file",                "dir/file",
     262        "dir",                  "//file",               "dir/file",
     263        "dir",                  "///file",              "dir/file",
     264        "dir/",                 "/file",                "dir/file",
     265        "dir/",                 "//file",               "dir/file",
     266        "dir/",                 "///file",              "dir/file",
     267        "dir//",                "file",                 "dir/file",
     268        "dir//",                "/file",                "dir/file",
     269        "dir//",                "//file",               "dir/file",
     270        "dir///",               "///file",              "dir/file",
     271#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
     272        "/",                    "\\",                   "/",
     273        "\\",                   "/",                    "\\",
     274        "\\\\srv\\shr",         "dir//",                "\\\\srv\\shr/dir//",
     275        "\\\\srv\\shr",         "dir//file",            "\\\\srv\\shr/dir//file",
     276        "\\\\srv\\shr",         "//dir//",              "\\\\srv\\shr/dir//",
     277        "\\\\srv\\shr",         "/\\dir//",             "\\\\srv\\shr\\dir//",
     278        "\\\\",                 "not-srv/not-shr/file", "\\not-srv/not-shr/file",
     279        "C:",                   "autoexec.bat",         "C:autoexec.bat",
     280        "C:",                   "/autoexec.bat",        "C:/autoexec.bat",
     281        "C:",                   "\\autoexec.bat",       "C:\\autoexec.bat",
     282        "C:\\",                 "/autoexec.bat",        "C:\\autoexec.bat",
     283        "C:\\\\",               "autoexec.bat",         "C:\\autoexec.bat",
     284#endif
     285    };
     286    for (unsigned i = 0; i < RT_ELEMENTS(s_apszAppendTests); i += 3)
     287    {
     288        const char *pszInput  = s_apszAppendTests[i];
     289        const char *pszAppend = s_apszAppendTests[i + 1];
     290        const char *pszExpect = s_apszAppendTests[i + 2];
     291        strcpy(szPath, pszInput);
     292        RTTESTI_CHECK_RC(rc = RTPathAppend(szPath, sizeof(szPath), pszAppend), VINF_SUCCESS);
     293        if (RT_FAILURE(rc))
     294            continue;
     295        if (strcmp(szPath, pszExpect))
     296        {
     297            RTTestIFailed("Unexpected result\n"
     298                          "   input: '%s'\n"
     299                          "  append: '%s'\n"
     300                          "  output: '%s'\n"
     301                          "expected: '%s'",
     302                          pszInput, pszAppend, szPath, pszExpect);
     303        }
     304        else
     305        {
     306            size_t const cchResult = strlen(szPath);
     307
     308            strcpy(szPath, pszInput);
     309            RTTESTI_CHECK_RC(rc = RTPathAppend(szPath, cchResult + 2, pszAppend), VINF_SUCCESS);
     310            RTTESTI_CHECK(RT_FAILURE(rc) || !strcmp(szPath, pszExpect));
     311
     312            strcpy(szPath, pszInput);
     313            RTTESTI_CHECK_RC(rc = RTPathAppend(szPath, cchResult + 1, pszAppend), VINF_SUCCESS);
     314            RTTESTI_CHECK(RT_FAILURE(rc) || !strcmp(szPath, pszExpect));
     315
     316            if (strlen(pszInput) < cchResult)
     317            {
     318                strcpy(szPath, pszInput);
     319                RTTESTI_CHECK_RC(RTPathAppend(szPath, cchResult, pszAppend), VERR_BUFFER_OVERFLOW);
     320            }
     321        }
     322    }
    250323
    251324
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