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; }