VirtualBox

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

Last change on this file since 53517 was 52944, checked in by vboxsync, 10 years ago

IPRT: Separated out RTPathAbs from the posix, fixing it up for windows + OS/2, and made it generic. Also added some NT path routines.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 10.3 KB
Line 
1/* $Id: RTPathAbs-generic.cpp 52944 2014-10-05 04:37:10Z vboxsync $ */
2/** @file
3 * IPRT - RTPathAbs, generic implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2014 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 if ( pszCur[0] == '.'
294 && pszCur[1] == '.'
295 && (!pszCur[2] || pszCur[2] == RTPATH_SLASH))
296 {
297 /* rewind to the previous component if any */
298 char *pszPrev = pszCur;
299 if ((uintptr_t)pszPrev > (uintptr_t)pszTop)
300 {
301 pszPrev--;
302 while ( (uintptr_t)pszPrev > (uintptr_t)pszTop
303 && pszPrev[-1] != RTPATH_SLASH)
304 pszPrev--;
305 }
306 if (!pszCur[2])
307 {
308 if (pszPrev != pszTop)
309 pszPrev[-1] = '\0';
310 else
311 *pszPrev = '\0';
312 break;
313 }
314 Assert(pszPrev[-1] == RTPATH_SLASH);
315 memmove(pszPrev, pszCur + 3, strlen(pszCur + 3) + 1);
316 pszCur = pszPrev - 1;
317 }
318 else
319 {
320 /* advance to end of component. */
321 while (*pszCur && *pszCur != RTPATH_SLASH)
322 pszCur++;
323 }
324
325 if (!*pszCur)
326 break;
327
328 /* skip the slash */
329 ++pszCur;
330 }
331
332 cchTmpPath = pszCur - szTmpPath;
333
334#if 1
335 /*
336 * Strip trailing slash if that's what's desired.
337 */
338
339 if ((uintptr_t)&szTmpPath[cchTmpPath] > (uintptr_t)pszTop && szTmpPath[cchTmpPath - 1] == RTPATH_SLASH)
340 szTmpPath[--cchTmpPath] = '\0';
341#endif
342
343 /*
344 * Copy the result to the user buffer.
345 */
346 if (cchTmpPath < cchAbsPath)
347 {
348 memcpy(pszAbsPath, szTmpPath, cchTmpPath + 1);
349 rc = VINF_SUCCESS;
350 }
351 else
352 rc = VERR_BUFFER_OVERFLOW;
353
354 LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath,
355 RT_SUCCESS(rc) ? pszAbsPath : "<failed>", cchAbsPath, rc));
356 return rc;
357}
358
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