VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/RTPathAbs-generic.cpp@ 72056

Last change on this file since 72056 was 69111, checked in by vboxsync, 7 years ago

(C) year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 10.9 KB
Line 
1/* $Id: RTPathAbs-generic.cpp 69111 2017-10-17 14:26:02Z vboxsync $ */
2/** @file
3 * IPRT - RTPathAbs, generic implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_PATH
32#include <iprt/path.h>
33
34#include <iprt/assert.h>
35#include <iprt/ctype.h>
36#include <iprt/err.h>
37#include <iprt/log.h>
38#include <iprt/string.h>
39#include "internal/path.h"
40#include "internal/fs.h"
41
42
43static char *rtPathSkipRootSpec(char *pszCur)
44{
45#ifdef HAVE_DRIVE
46 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == RTPATH_SLASH)
47 pszCur += 3;
48# ifdef HAVE_UNC
49 else if (pszCur[0] == RTPATH_SLASH && pszCur[1] == RTPATH_SLASH)
50 {
51 pszCur += 2;
52 while (*pszCur == RTPATH_SLASH)
53 pszCur++;
54 if (*pszCur)
55 {
56 while (*pszCur != RTPATH_SLASH && *pszCur)
57 pszCur++;
58 if (*pszCur == RTPATH_SLASH)
59 {
60 pszCur++;
61 while (*pszCur != RTPATH_SLASH && *pszCur)
62 pszCur++;
63 if (*pszCur == RTPATH_SLASH)
64 pszCur++;
65 }
66 }
67 }
68# endif
69#else
70 if (pszCur[0] == RTPATH_SLASH)
71 pszCur += 1;
72#endif
73 return pszCur;
74}
75
76
77/**
78 * Cleans up a path specifier a little bit.
79 *
80 * This includes removing duplicate slashes, unnecessary single dots, and
81 * trailing slashes. Also, replaces all slash characters with RTPATH_SLASH.
82 *
83 * @returns Length of the cleaned path (in chars).
84 * @param pszPath The path to cleanup.
85 */
86static int fsCleanPath(char *pszPath)
87{
88 char *pszSrc = pszPath;
89 char *pszTrg = pszPath;
90
91 /*
92 * On windows, you either use on or two slashes at the head of a path,
93 * seems like it treats additional slashes as part of the UNC server name.
94 * Just change slashes to RTPATH_SLASH and skip them.
95 */
96 /** @todo check how OS/2 treats unnecessary leading slashes */
97 /*int cchIgnoreLeading = 0;*/
98#ifdef HAVE_UNC
99 if ( RTPATH_IS_SLASH(pszSrc[0])
100 && RTPATH_IS_SLASH(pszSrc[1]))
101 {
102 pszTrg[0] = RTPATH_SLASH;
103 pszTrg[1] = RTPATH_SLASH;
104 pszTrg += 2;
105 pszSrc += 2;
106 /*cchIgnoreLeading = 1;*/
107 while (RTPATH_IS_SLASH(*pszSrc))
108 {
109 /*cchIgnoreLeading++;*/
110 pszSrc++;
111 *pszTrg++ = RTPATH_SLASH;
112 }
113 }
114#endif
115
116 /*
117 * Change slashes to RTPATH_SLASH and remove duplicates.
118 */
119 for (;;)
120 {
121 char ch = *pszSrc++;
122 if (RTPATH_IS_SLASH(ch))
123 {
124 *pszTrg++ = RTPATH_SLASH;
125 for (;;)
126 {
127 do
128 ch = *pszSrc++;
129 while (RTPATH_IS_SLASH(ch));
130
131 /* Remove '/./' and '/.'. */
132 if ( ch != '.'
133 || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc)))
134 break;
135 }
136 }
137 *pszTrg = ch;
138 if (!ch)
139 break;
140 pszTrg++;
141 }
142
143 return pszTrg - pszPath;
144}
145
146
147RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
148{
149 int rc;
150
151 /*
152 * Validation.
153 */
154 AssertPtr(pszAbsPath);
155 AssertPtr(pszPath);
156 if (RT_UNLIKELY(!*pszPath))
157 return VERR_INVALID_PARAMETER; //VERR_INVALID_NAME;
158
159 /*
160 * Make a clean working copy of the input.
161 */
162 size_t cchPath = strlen(pszPath);
163 if (cchPath >= RTPATH_MAX)
164 {
165 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
166 return VERR_FILENAME_TOO_LONG;
167 }
168
169 char szTmpPath[RTPATH_MAX];
170 memcpy(szTmpPath, pszPath, cchPath + 1);
171 size_t cchTmpPath = fsCleanPath(szTmpPath);
172
173 /*
174 * Handle "." specially (fsCleanPath does).
175 */
176 if (szTmpPath[0] == '.')
177 {
178 if ( cchTmpPath == 1
179 || (cchTmpPath == 2 && szTmpPath[1] == RTPATH_SLASH))
180 {
181 rc = RTPathGetCurrent(pszAbsPath, cchAbsPath);
182 if (RT_SUCCESS(rc))
183 {
184 size_t cch = fsCleanPath(pszAbsPath);
185 char *pszTop = rtPathSkipRootSpec(pszAbsPath);
186#if 1
187 if ((uintptr_t)&pszAbsPath[cch] > (uintptr_t)pszTop && pszAbsPath[cch - 1] == RTPATH_SLASH)
188 pszAbsPath[cch - 1] = '\0';
189#else
190 if ( cchTmpPath == 2
191 && (uintptr_t)&pszAbsPath[cch - 1] > (uintptr_t)pszTop && pszAbsPath[cch - 1] != RTPATH_SLASH)
192 {
193 if (cch + 1 < cchAbsPath)
194 {
195 pszAbsPath[cch++] = RTPATH_SLASH;
196 pszAbsPath[cch] = '\0';
197 }
198 else
199 rc = VERR_BUFFER_OVERFLOW;
200 }
201#endif
202 }
203 return rc;
204 }
205 }
206
207 /*
208 * Do we have an incomplete root spec? Supply the missing bits.
209 */
210#ifdef HAVE_DRIVE
211 if ( !(szTmpPath[0] && RTPATH_IS_VOLSEP(szTmpPath[1]) && szTmpPath[2] == RTPATH_SLASH)
212# ifdef HAVE_UNC
213 && !(szTmpPath[0] == RTPATH_SLASH && szTmpPath[1] == RTPATH_SLASH)
214# endif
215 )
216#else
217 if (szTmpPath[0] != RTPATH_SLASH)
218#endif
219 {
220 char szCurDir[RTPATH_MAX];
221 size_t cchCurDir;
222 int offApplyAt;
223 bool fNeedSlash;
224#ifdef HAVE_DRIVE
225 if (szTmpPath[0] && RTPATH_IS_VOLSEP(szTmpPath[1]) && szTmpPath[2] != RTPATH_SLASH)
226 {
227 /*
228 * Relative to drive specific current directory.
229 */
230 rc = RTPathGetCurrentOnDrive(szTmpPath[0], szCurDir, sizeof(szCurDir));
231 fNeedSlash = true;
232 offApplyAt = 2;
233 }
234# ifdef HAVE_UNC
235 else if (szTmpPath[0] == RTPATH_SLASH && szTmpPath[1] != RTPATH_SLASH)
236# else
237 else if (szTmpPath[0] == RTPATH_SLASH)
238# endif
239 {
240 /*
241 * Root of current drive. This may return a UNC root if we're not
242 * standing on a drive but on a UNC share.
243 */
244 rc = RTPathGetCurrentDrive(szCurDir, sizeof(szCurDir));
245 fNeedSlash = false;
246 offApplyAt = 0;
247 }
248 else
249#endif
250 {
251 /*
252 * Relative to current directory.
253 */
254 rc = RTPathGetCurrent(szCurDir, sizeof(szCurDir));
255 fNeedSlash = true;
256 offApplyAt = 0;
257 }
258 if (RT_SUCCESS(rc))
259 {
260 cchCurDir = fsCleanPath(szCurDir);
261 if (fNeedSlash && cchCurDir > 0 && szCurDir[cchCurDir - 1] == RTPATH_SLASH)
262 fNeedSlash = false;
263
264 if (cchCurDir + fNeedSlash + cchTmpPath - offApplyAt <= RTPATH_MAX)
265 {
266 memmove(szTmpPath + cchCurDir + fNeedSlash, szTmpPath + offApplyAt, cchTmpPath + 1 - offApplyAt);
267 memcpy(szTmpPath, szCurDir, cchCurDir);
268 if (fNeedSlash)
269 szTmpPath[cchCurDir] = RTPATH_SLASH;
270 }
271 else
272 rc = VERR_FILENAME_TOO_LONG;
273 }
274 if (RT_FAILURE(rc))
275 {
276 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, rc));
277 return rc;
278 }
279 }
280
281 /*
282 * Skip past the root spec.
283 */
284 char *pszCur = rtPathSkipRootSpec(szTmpPath);
285 AssertMsgReturn(pszCur != &szTmpPath[0], ("pszCur=%s\n", pszCur), VERR_INTERNAL_ERROR);
286 char * const pszTop = pszCur;
287
288 /*
289 * Get rid of double dot path components by evaluating them.
290 */
291 for (;;)
292 {
293 char const chFirst = pszCur[0];
294 if ( chFirst == '.'
295 && pszCur[1] == '.'
296 && (!pszCur[2] || pszCur[2] == RTPATH_SLASH))
297 {
298 /* rewind to the previous component if any */
299 char *pszPrev = pszCur;
300 if ((uintptr_t)pszPrev > (uintptr_t)pszTop)
301 {
302 pszPrev--;
303 while ( (uintptr_t)pszPrev > (uintptr_t)pszTop
304 && pszPrev[-1] != RTPATH_SLASH)
305 pszPrev--;
306 }
307 if (!pszCur[2])
308 {
309 if (pszPrev != pszTop)
310 pszPrev[-1] = '\0';
311 else
312 *pszPrev = '\0';
313 break;
314 }
315 Assert(pszPrev[-1] == RTPATH_SLASH);
316 memmove(pszPrev, pszCur + 3, strlen(pszCur + 3) + 1);
317 pszCur = pszPrev - 1;
318 }
319 else if ( chFirst == '.'
320 && (!pszCur[1] || pszCur[1] == RTPATH_SLASH))
321 {
322 /* remove unnecessary '.' */
323 if (!pszCur[1])
324 {
325 if (pszCur != pszTop)
326 pszCur[-1] = '\0';
327 else
328 *pszCur = '\0';
329 break;
330 }
331 memmove(pszCur, pszCur + 2, strlen(pszCur + 2) + 1);
332 continue;
333 }
334 else
335 {
336 /* advance to end of component. */
337 while (*pszCur && *pszCur != RTPATH_SLASH)
338 pszCur++;
339 }
340
341 if (!*pszCur)
342 break;
343
344 /* skip the slash */
345 ++pszCur;
346 }
347
348 cchTmpPath = pszCur - szTmpPath;
349
350#if 1
351 /*
352 * Strip trailing slash if that's what's desired.
353 */
354
355 if ((uintptr_t)&szTmpPath[cchTmpPath] > (uintptr_t)pszTop && szTmpPath[cchTmpPath - 1] == RTPATH_SLASH)
356 szTmpPath[--cchTmpPath] = '\0';
357#endif
358
359 /*
360 * Copy the result to the user buffer.
361 */
362 if (cchTmpPath < cchAbsPath)
363 {
364 memcpy(pszAbsPath, szTmpPath, cchTmpPath + 1);
365 rc = VINF_SUCCESS;
366 }
367 else
368 rc = VERR_BUFFER_OVERFLOW;
369
370 LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath,
371 RT_SUCCESS(rc) ? pszAbsPath : "<failed>", cchAbsPath, rc));
372 return rc;
373}
374
Note: See TracBrowser for help on using the repository browser.

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