summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/generic/RTPathAbs-generic.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/generic/RTPathAbs-generic.cpp')
-rw-r--r--src/VBox/Runtime/generic/RTPathAbs-generic.cpp374
1 files changed, 374 insertions, 0 deletions
diff --git a/src/VBox/Runtime/generic/RTPathAbs-generic.cpp b/src/VBox/Runtime/generic/RTPathAbs-generic.cpp
new file mode 100644
index 00000000..a8f93492
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTPathAbs-generic.cpp
@@ -0,0 +1,374 @@
+/* $Id: RTPathAbs-generic.cpp $ */
+/** @file
+ * IPRT - RTPathAbs, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PATH
+#include <iprt/path.h>
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include "internal/path.h"
+#include "internal/fs.h"
+
+
+static char *rtPathSkipRootSpec(char *pszCur)
+{
+#ifdef HAVE_DRIVE
+ if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == RTPATH_SLASH)
+ pszCur += 3;
+# ifdef HAVE_UNC
+ else if (pszCur[0] == RTPATH_SLASH && pszCur[1] == RTPATH_SLASH)
+ {
+ pszCur += 2;
+ while (*pszCur == RTPATH_SLASH)
+ pszCur++;
+ if (*pszCur)
+ {
+ while (*pszCur != RTPATH_SLASH && *pszCur)
+ pszCur++;
+ if (*pszCur == RTPATH_SLASH)
+ {
+ pszCur++;
+ while (*pszCur != RTPATH_SLASH && *pszCur)
+ pszCur++;
+ if (*pszCur == RTPATH_SLASH)
+ pszCur++;
+ }
+ }
+ }
+# endif
+#else
+ if (pszCur[0] == RTPATH_SLASH)
+ pszCur += 1;
+#endif
+ return pszCur;
+}
+
+
+/**
+ * Cleans up a path specifier a little bit.
+ *
+ * This includes removing duplicate slashes, unnecessary single dots, and
+ * trailing slashes. Also, replaces all slash characters with RTPATH_SLASH.
+ *
+ * @returns Length of the cleaned path (in chars).
+ * @param pszPath The path to cleanup.
+ */
+static int fsCleanPath(char *pszPath)
+{
+ char *pszSrc = pszPath;
+ char *pszTrg = pszPath;
+
+ /*
+ * On windows, you either use on or two slashes at the head of a path,
+ * seems like it treats additional slashes as part of the UNC server name.
+ * Just change slashes to RTPATH_SLASH and skip them.
+ */
+ /** @todo check how OS/2 treats unnecessary leading slashes */
+ /*int cchIgnoreLeading = 0;*/
+#ifdef HAVE_UNC
+ if ( RTPATH_IS_SLASH(pszSrc[0])
+ && RTPATH_IS_SLASH(pszSrc[1]))
+ {
+ pszTrg[0] = RTPATH_SLASH;
+ pszTrg[1] = RTPATH_SLASH;
+ pszTrg += 2;
+ pszSrc += 2;
+ /*cchIgnoreLeading = 1;*/
+ while (RTPATH_IS_SLASH(*pszSrc))
+ {
+ /*cchIgnoreLeading++;*/
+ pszSrc++;
+ *pszTrg++ = RTPATH_SLASH;
+ }
+ }
+#endif
+
+ /*
+ * Change slashes to RTPATH_SLASH and remove duplicates.
+ */
+ for (;;)
+ {
+ char ch = *pszSrc++;
+ if (RTPATH_IS_SLASH(ch))
+ {
+ *pszTrg++ = RTPATH_SLASH;
+ for (;;)
+ {
+ do
+ ch = *pszSrc++;
+ while (RTPATH_IS_SLASH(ch));
+
+ /* Remove '/./' and '/.'. */
+ if ( ch != '.'
+ || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc)))
+ break;
+ }
+ }
+ *pszTrg = ch;
+ if (!ch)
+ break;
+ pszTrg++;
+ }
+
+ return pszTrg - pszPath;
+}
+
+
+RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
+{
+ int rc;
+
+ /*
+ * Validation.
+ */
+ AssertPtr(pszAbsPath);
+ AssertPtr(pszPath);
+ if (RT_UNLIKELY(!*pszPath))
+ return VERR_INVALID_PARAMETER; //VERR_INVALID_NAME;
+
+ /*
+ * Make a clean working copy of the input.
+ */
+ size_t cchPath = strlen(pszPath);
+ if (cchPath >= RTPATH_MAX)
+ {
+ LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
+ return VERR_FILENAME_TOO_LONG;
+ }
+
+ char szTmpPath[RTPATH_MAX];
+ memcpy(szTmpPath, pszPath, cchPath + 1);
+ size_t cchTmpPath = fsCleanPath(szTmpPath);
+
+ /*
+ * Handle "." specially (fsCleanPath does).
+ */
+ if (szTmpPath[0] == '.')
+ {
+ if ( cchTmpPath == 1
+ || (cchTmpPath == 2 && szTmpPath[1] == RTPATH_SLASH))
+ {
+ rc = RTPathGetCurrent(pszAbsPath, cchAbsPath);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cch = fsCleanPath(pszAbsPath);
+ char *pszTop = rtPathSkipRootSpec(pszAbsPath);
+#if 1
+ if ((uintptr_t)&pszAbsPath[cch] > (uintptr_t)pszTop && pszAbsPath[cch - 1] == RTPATH_SLASH)
+ pszAbsPath[cch - 1] = '\0';
+#else
+ if ( cchTmpPath == 2
+ && (uintptr_t)&pszAbsPath[cch - 1] > (uintptr_t)pszTop && pszAbsPath[cch - 1] != RTPATH_SLASH)
+ {
+ if (cch + 1 < cchAbsPath)
+ {
+ pszAbsPath[cch++] = RTPATH_SLASH;
+ pszAbsPath[cch] = '\0';
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+#endif
+ }
+ return rc;
+ }
+ }
+
+ /*
+ * Do we have an incomplete root spec? Supply the missing bits.
+ */
+#ifdef HAVE_DRIVE
+ if ( !(szTmpPath[0] && RTPATH_IS_VOLSEP(szTmpPath[1]) && szTmpPath[2] == RTPATH_SLASH)
+# ifdef HAVE_UNC
+ && !(szTmpPath[0] == RTPATH_SLASH && szTmpPath[1] == RTPATH_SLASH)
+# endif
+ )
+#else
+ if (szTmpPath[0] != RTPATH_SLASH)
+#endif
+ {
+ char szCurDir[RTPATH_MAX];
+ size_t cchCurDir;
+ int offApplyAt;
+ bool fNeedSlash;
+#ifdef HAVE_DRIVE
+ if (szTmpPath[0] && RTPATH_IS_VOLSEP(szTmpPath[1]) && szTmpPath[2] != RTPATH_SLASH)
+ {
+ /*
+ * Relative to drive specific current directory.
+ */
+ rc = RTPathGetCurrentOnDrive(szTmpPath[0], szCurDir, sizeof(szCurDir));
+ fNeedSlash = true;
+ offApplyAt = 2;
+ }
+# ifdef HAVE_UNC
+ else if (szTmpPath[0] == RTPATH_SLASH && szTmpPath[1] != RTPATH_SLASH)
+# else
+ else if (szTmpPath[0] == RTPATH_SLASH)
+# endif
+ {
+ /*
+ * Root of current drive. This may return a UNC root if we're not
+ * standing on a drive but on a UNC share.
+ */
+ rc = RTPathGetCurrentDrive(szCurDir, sizeof(szCurDir));
+ fNeedSlash = false;
+ offApplyAt = 0;
+ }
+ else
+#endif
+ {
+ /*
+ * Relative to current directory.
+ */
+ rc = RTPathGetCurrent(szCurDir, sizeof(szCurDir));
+ fNeedSlash = true;
+ offApplyAt = 0;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ cchCurDir = fsCleanPath(szCurDir);
+ if (fNeedSlash && cchCurDir > 0 && szCurDir[cchCurDir - 1] == RTPATH_SLASH)
+ fNeedSlash = false;
+
+ if (cchCurDir + fNeedSlash + cchTmpPath - offApplyAt <= RTPATH_MAX)
+ {
+ memmove(szTmpPath + cchCurDir + fNeedSlash, szTmpPath + offApplyAt, cchTmpPath + 1 - offApplyAt);
+ memcpy(szTmpPath, szCurDir, cchCurDir);
+ if (fNeedSlash)
+ szTmpPath[cchCurDir] = RTPATH_SLASH;
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ if (RT_FAILURE(rc))
+ {
+ LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, rc));
+ return rc;
+ }
+ }
+
+ /*
+ * Skip past the root spec.
+ */
+ char *pszCur = rtPathSkipRootSpec(szTmpPath);
+ AssertMsgReturn(pszCur != &szTmpPath[0], ("pszCur=%s\n", pszCur), VERR_INTERNAL_ERROR);
+ char * const pszTop = pszCur;
+
+ /*
+ * Get rid of double dot path components by evaluating them.
+ */
+ for (;;)
+ {
+ char const chFirst = pszCur[0];
+ if ( chFirst == '.'
+ && pszCur[1] == '.'
+ && (!pszCur[2] || pszCur[2] == RTPATH_SLASH))
+ {
+ /* rewind to the previous component if any */
+ char *pszPrev = pszCur;
+ if ((uintptr_t)pszPrev > (uintptr_t)pszTop)
+ {
+ pszPrev--;
+ while ( (uintptr_t)pszPrev > (uintptr_t)pszTop
+ && pszPrev[-1] != RTPATH_SLASH)
+ pszPrev--;
+ }
+ if (!pszCur[2])
+ {
+ if (pszPrev != pszTop)
+ pszPrev[-1] = '\0';
+ else
+ *pszPrev = '\0';
+ break;
+ }
+ Assert(pszPrev[-1] == RTPATH_SLASH);
+ memmove(pszPrev, pszCur + 3, strlen(pszCur + 3) + 1);
+ pszCur = pszPrev - 1;
+ }
+ else if ( chFirst == '.'
+ && (!pszCur[1] || pszCur[1] == RTPATH_SLASH))
+ {
+ /* remove unnecessary '.' */
+ if (!pszCur[1])
+ {
+ if (pszCur != pszTop)
+ pszCur[-1] = '\0';
+ else
+ *pszCur = '\0';
+ break;
+ }
+ memmove(pszCur, pszCur + 2, strlen(pszCur + 2) + 1);
+ continue;
+ }
+ else
+ {
+ /* advance to end of component. */
+ while (*pszCur && *pszCur != RTPATH_SLASH)
+ pszCur++;
+ }
+
+ if (!*pszCur)
+ break;
+
+ /* skip the slash */
+ ++pszCur;
+ }
+
+ cchTmpPath = pszCur - szTmpPath;
+
+#if 1
+ /*
+ * Strip trailing slash if that's what's desired.
+ */
+
+ if ((uintptr_t)&szTmpPath[cchTmpPath] > (uintptr_t)pszTop && szTmpPath[cchTmpPath - 1] == RTPATH_SLASH)
+ szTmpPath[--cchTmpPath] = '\0';
+#endif
+
+ /*
+ * Copy the result to the user buffer.
+ */
+ if (cchTmpPath < cchAbsPath)
+ {
+ memcpy(pszAbsPath, szTmpPath, cchTmpPath + 1);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath,
+ RT_SUCCESS(rc) ? pszAbsPath : "<failed>", cchAbsPath, rc));
+ return rc;
+}
+