RTDECL(size_t) RTPathCountComponents(const char *pszPath)
{
    size_t off = rtPathRootSpecLen(pszPath);
    size_t c   = off != 0;
    while (pszPath[off])
    {
        c++;
        while (!RTPATH_IS_SLASH(pszPath[off]) && pszPath[off])
            off++;
        while (RTPATH_IS_SLASH(pszPath[off]))
            off++;
    }
    return c;
}
RTDECL(int) RTPathCopyComponents(char *pszDst, size_t cbDst, const char *pszSrc, size_t cComponents)
{
    /*
     * Quick input validation.
     */
    AssertPtr(pszDst);
    AssertPtr(pszSrc);
    if (cbDst == 0)
        return VERR_BUFFER_OVERFLOW;

    /*
     * Fend of the simple case where nothing is wanted.
     */
    if (cComponents == 0)
    {
        *pszDst = '\0';
        return VINF_SUCCESS;
    }

    /*
     * Parse into the path until we've counted the desired number of objects
     * or hit the end.
     */
    size_t off = rtPathRootSpecLen(pszSrc);
    size_t c   = off != 0;
    while (c < cComponents && pszSrc[off])
    {
        c++;
        while (!RTPATH_IS_SLASH(pszSrc[off]) && pszSrc[off])
            off++;
        while (RTPATH_IS_SLASH(pszSrc[off]))
            off++;
    }

    /*
     * Copy up to but not including 'off'.
     */
    if (off >= cbDst)
        return VERR_BUFFER_OVERFLOW;

    memcpy(pszDst, pszSrc, off);
    pszDst[off] = '\0';
    return VINF_SUCCESS;
}
RTDECL(int) RTPathCalcRelative(char *pszPathDst, size_t cbPathDst,
                               const char *pszPathFrom,
                               const char *pszPathTo)
{
    int rc = VINF_SUCCESS;

    AssertPtrReturn(pszPathDst, VERR_INVALID_POINTER);
    AssertReturn(cbPathDst, VERR_INVALID_PARAMETER);
    AssertPtrReturn(pszPathFrom, VERR_INVALID_POINTER);
    AssertPtrReturn(pszPathTo, VERR_INVALID_POINTER);
    AssertReturn(RTPathStartsWithRoot(pszPathFrom), VERR_INVALID_PARAMETER);
    AssertReturn(RTPathStartsWithRoot(pszPathTo), VERR_INVALID_PARAMETER);
    AssertReturn(RTStrCmp(pszPathFrom, pszPathTo), VERR_INVALID_PARAMETER);

    /*
     * Check for different root specifiers (drive letters), creating a relative path doesn't work here.
     * @todo: How to handle case insensitive root specifiers correctly?
     */
    size_t offRootFrom = rtPathRootSpecLen(pszPathFrom);
    size_t offRootTo   = rtPathRootSpecLen(pszPathTo);

    if (   offRootFrom != offRootTo
        || RTStrNCmp(pszPathFrom, pszPathTo, offRootFrom))
        return VERR_NOT_SUPPORTED;

    /* Filter out the parent path which is equal to both paths. */
    while (   *pszPathFrom == *pszPathTo
           && *pszPathFrom != '\0'
           && *pszPathTo != '\0')
    {
        pszPathFrom++;
        pszPathTo++;
    }

    /*
     * Because path components can start with an equal string but differ afterwards we
     * need to go back to the beginning of the current component.
     */
    while (!RTPATH_IS_SEP(*pszPathFrom))
        pszPathFrom--;

    pszPathFrom++; /* Skip path separator. */

    while (!RTPATH_IS_SEP(*pszPathTo))
        pszPathTo--;

    pszPathTo++; /* Skip path separator. */

    /* Paths point to the first non equal component now. */
    char aszPathTmp[RTPATH_MAX + 1];
    unsigned offPathTmp = 0;

    /* Create the part to go up from pszPathFrom. */
    while (*pszPathFrom != '\0')
    {
        while (   !RTPATH_IS_SEP(*pszPathFrom)
               && *pszPathFrom != '\0')
            pszPathFrom++;

        if (RTPATH_IS_SEP(*pszPathFrom))
        {
            if (offPathTmp + 3 >= sizeof(aszPathTmp) - 1)
                return VERR_FILENAME_TOO_LONG;
            aszPathTmp[offPathTmp++] = '.';
            aszPathTmp[offPathTmp++] = '.';
            aszPathTmp[offPathTmp++] = RTPATH_SLASH;
            pszPathFrom++;
        }
    }

    aszPathTmp[offPathTmp] = '\0';

    /* Now append the rest of pszPathTo to the final path. */
    char *pszPathTmp = &aszPathTmp[offPathTmp];
    size_t cbPathTmp = sizeof(aszPathTmp) - offPathTmp - 1;
    rc = RTStrCatP(&pszPathTmp, &cbPathTmp, pszPathTo);
    if (RT_SUCCESS(rc))
    {
        *pszPathTmp = '\0';

        size_t cchPathTmp = strlen(aszPathTmp);
        if (cchPathTmp >= cbPathDst)
           return VERR_BUFFER_OVERFLOW;
        memcpy(pszPathDst, aszPathTmp, cchPathTmp + 1);
    }
    else
        rc = VERR_FILENAME_TOO_LONG;

    return rc;
}
RTDECL(char *) RTPathSkipRootSpec(const char *pszPath)
{
    return (char *)&pszPath[rtPathRootSpecLen(pszPath)];
}
RTDECL(bool) RTPathStartsWithRoot(const char *pszPath)
{
    return rtPathRootSpecLen(pszPath) > 0;
}