VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/path/RTPathAbsEx.cpp

Last change on this file was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.5 KB
Line 
1/* $Id: RTPathAbsEx.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - RTPathAbsEx and RTPathAbs.
4 */
5
6/*
7 * Copyright (C) 2019-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_PATH
42#include "internal/iprt.h"
43#include <iprt/path.h>
44
45#include <iprt/err.h>
46#include <iprt/ctype.h>
47#include <iprt/log.h>
48#include <iprt/mem.h>
49#include <iprt/param.h>
50#include <iprt/string.h>
51#include "internal/path.h"
52
53
54
55/**
56 * Ensures that the drive letter is capitalized (prereq: RTPATH_PROP_VOLUME).
57 */
58DECLINLINE(void) rtPathAbsExUpperCaseDriveLetter(char *pszAbsPath)
59{
60 AssertReturnVoid(pszAbsPath[1] == ':');
61 char ch = *pszAbsPath;
62 AssertReturnVoid(RT_C_IS_ALPHA(ch));
63 *pszAbsPath = RT_C_TO_UPPER(ch);
64}
65
66
67/**
68 * Common worker for relative paths.
69 *
70 * Uses RTPATHABS_F_STOP_AT_BASE for RTPATHABS_F_STOP_AT_CWD.
71 */
72static int rtPathAbsExWithCwdOrBaseCommon(const char *pszBase, size_t cchBaseInPlace, PRTPATHPARSED pBaseParsed,
73 const char *pszPath, PRTPATHPARSED pParsed, uint32_t fFlags,
74 char *pszAbsPath, size_t *pcbAbsPath)
75{
76 AssertReturn(pBaseParsed->cComps > 0, VERR_INVALID_PARAMETER);
77
78 /*
79 * Clean up the base path first if necessary.
80 *
81 * Note! UNC tries to preserve the first two elements in the base path,
82 * unless it's a \\.\ or \\?\ prefix.
83 */
84 uint32_t const iBaseStop = (pBaseParsed->fProps & (RTPATH_PROP_UNC | RTPATH_PROP_SPECIAL_UNC)) != RTPATH_PROP_UNC
85 || pBaseParsed->cComps < 2 ? 0 : 1;
86 uint32_t iBaseLast = iBaseStop;
87 if (pBaseParsed->fProps & (RTPATH_PROP_DOT_REFS | RTPATH_PROP_DOTDOT_REFS))
88 {
89 uint32_t const cComps = pBaseParsed->cComps;
90 uint32_t i = iBaseStop + 1;
91 while (i < cComps)
92 {
93 uint32_t const cchComp = pBaseParsed->aComps[i].cch;
94 if ( cchComp > 2
95 || pszPath[pBaseParsed->aComps[i].off] != '.'
96 || (cchComp == 2 && pszPath[pBaseParsed->aComps[i].off + 1] != '.') )
97 iBaseLast = i;
98 else
99 {
100 Assert(cchComp == 1 || cchComp == 2);
101 pBaseParsed->aComps[i].cch = 0;
102 if (cchComp == 2)
103 {
104 while (iBaseLast > 0 && pBaseParsed->aComps[iBaseLast].cch == 0)
105 iBaseLast--;
106 if (iBaseLast > iBaseStop)
107 {
108 Assert(pBaseParsed->aComps[iBaseLast].cch != 0);
109 pBaseParsed->aComps[iBaseLast].cch = 0;
110 iBaseLast--;
111 }
112 }
113 }
114 i++;
115 }
116 Assert(iBaseLast < cComps);
117 }
118 else
119 iBaseLast = pBaseParsed->cComps - 1;
120
121 /*
122 * Clean up the path next if needed.
123 */
124 int32_t iLast = -1; /* Is signed here! */
125 if (pParsed->fProps & (RTPATH_PROP_DOT_REFS | RTPATH_PROP_DOTDOT_REFS))
126 {
127 uint32_t const cComps = pParsed->cComps;
128 uint32_t i = 0;
129
130 /* If we have a volume specifier, take it from the base path. */
131 if (pParsed->fProps & RTPATH_PROP_VOLUME)
132 pParsed->aComps[i++].cch = 0;
133
134 while (i < cComps)
135 {
136 uint32_t const cchComp = pParsed->aComps[i].cch;
137 if ( cchComp > 2
138 || pszPath[pParsed->aComps[i].off] != '.'
139 || (cchComp == 2 && pszPath[pParsed->aComps[i].off + 1] != '.') )
140 iLast = i;
141 else
142 {
143 Assert(cchComp == 1 || cchComp == 2);
144 pParsed->aComps[i].cch = 0;
145 if (cchComp == 2)
146 {
147 while (iLast >= 0 && pParsed->aComps[iLast].cch == 0)
148 iLast--;
149 if (iLast >= 0)
150 {
151 Assert(pParsed->aComps[iLast].cch != 0);
152 pParsed->aComps[iLast].cch = 0;
153 iLast--;
154 }
155 else if ( iBaseLast > iBaseStop
156 && !(fFlags & RTPATHABS_F_STOP_AT_BASE))
157 {
158 while (iBaseLast > iBaseStop && pBaseParsed->aComps[iBaseLast].cch == 0)
159 iBaseLast--;
160 if (iBaseLast > iBaseStop)
161 {
162 Assert(pBaseParsed->aComps[iBaseLast].cch != 0);
163 pBaseParsed->aComps[iBaseLast].cch = 0;
164 iBaseLast--;
165 }
166 }
167 }
168 }
169 i++;
170 }
171 Assert(iLast < (int32_t)cComps);
172 }
173 else
174 {
175 /* If we have a volume specifier, take it from the base path. */
176 iLast = pParsed->cComps - 1;
177 if (pParsed->fProps & RTPATH_PROP_VOLUME)
178 {
179 pParsed->aComps[0].cch = 0;
180 if (iLast == 0)
181 iLast = -1;
182 }
183 }
184
185 /*
186 * Do we need a trailing slash in the base?
187 * If nothing is taken from pszPath, preserve its trailing slash,
188 * otherwise make sure there is a slash for joining the two.
189 */
190 Assert(!(pParsed->fProps & RTPATH_PROP_ROOT_SLASH));
191 if (pBaseParsed->cComps == 1)
192 {
193 AssertReturn(pBaseParsed->fProps & RTPATH_PROP_ROOT_SLASH, VERR_PATH_DOES_NOT_START_WITH_ROOT);
194 Assert(!(pBaseParsed->fProps & RTPATH_PROP_DIR_SLASH));
195 }
196 else
197 {
198 Assert(pBaseParsed->cComps > 1);
199 if ( iLast >= 0
200 || (pParsed->fProps & RTPATH_PROP_DIR_SLASH)
201 || (fFlags & RTPATHABS_F_ENSURE_TRAILING_SLASH) )
202 pBaseParsed->fProps |= RTPATH_PROP_DIR_SLASH;
203 else
204 pBaseParsed->fProps &= ~RTPATH_PROP_DIR_SLASH;
205 }
206
207 /* Apply the trailing flash flag to the input path: */
208 if ( iLast >= 0
209 && (fFlags & RTPATHABS_F_ENSURE_TRAILING_SLASH))
210 pParsed->fProps |= RTPATH_PROP_DIR_SLASH;
211
212 /*
213 * Combine the two. RTPathParsedReassemble can handle in place stuff, as
214 * long as the path doesn't grow.
215 */
216 int rc = RTPathParsedReassemble(pszBase, pBaseParsed, fFlags & RTPATH_STR_F_STYLE_MASK, pszAbsPath, *pcbAbsPath);
217 if (RT_SUCCESS(rc))
218 {
219 if (pBaseParsed->fProps & RTPATH_PROP_VOLUME)
220 rtPathAbsExUpperCaseDriveLetter(pszAbsPath);
221
222 cchBaseInPlace = pBaseParsed->cchPath;
223 Assert(cchBaseInPlace == strlen(pszAbsPath));
224 if (iLast >= 0)
225 {
226 rc = RTPathParsedReassemble(pszPath, pParsed, fFlags & RTPATH_STR_F_STYLE_MASK,
227 &pszAbsPath[cchBaseInPlace], *pcbAbsPath - cchBaseInPlace);
228 if (RT_SUCCESS(rc))
229 {
230 *pcbAbsPath = cchBaseInPlace + pParsed->cchPath;
231 Assert(*pcbAbsPath == strlen(pszAbsPath));
232 }
233 else
234 *pcbAbsPath = cchBaseInPlace + pParsed->cchPath + 1;
235 }
236 else
237 *pcbAbsPath = cchBaseInPlace;
238 }
239 else if (rc == VERR_BUFFER_OVERFLOW)
240 {
241 if (iLast >= 0)
242 {
243 RTPathParsedReassemble(pszPath, pParsed, fFlags & RTPATH_STR_F_STYLE_MASK, pszAbsPath, 0);
244 *pcbAbsPath = pBaseParsed->cchPath + pParsed->cchPath + 1;
245 }
246 else
247 *pcbAbsPath = pBaseParsed->cchPath + 1;
248 }
249
250 return rc;
251}
252
253
254/**
255 * Handles the no-root-path scenario where we do CWD prefixing.
256 */
257static int rtPathAbsExWithCwd(const char *pszPath, PRTPATHPARSED pParsed, uint32_t fFlags, char *pszAbsPath, size_t *pcbAbsPath)
258{
259 /*
260 * Get the current directory and place it in the output buffer.
261 */
262 size_t cchInPlace;
263 size_t cbCwd = *pcbAbsPath;
264 char *pszCwdFree = NULL;
265 char *pszCwd = pszAbsPath;
266 int rc;
267 if ( !(fFlags & RTPATH_STR_F_STYLE_DOS)
268 || (pParsed->fProps & (RTPATH_PROP_VOLUME | RTPATH_PROP_ROOT_SLASH)) != RTPATH_PROP_VOLUME )
269 rc = RTPathGetCurrent(pszCwd, cbCwd);
270 else
271 rc = RTPathGetCurrentOnDrive(pszPath[0], pszCwd, cbCwd);
272 if (RT_SUCCESS(rc))
273 cchInPlace = strlen(pszCwd);
274 else if (rc == VERR_BUFFER_OVERFLOW)
275 {
276 /* Allocate a big temporary buffer so we can return the correct length
277 (the destination buffer might even be big enough if pszPath includes
278 sufficient '..' entries). */
279 cchInPlace = 0;
280 cbCwd = RT_MAX(cbCwd * 4, RTPATH_BIG_MAX);
281 pszCwdFree = pszCwd = (char *)RTMemTmpAlloc(cbCwd);
282 if (pszCwdFree)
283 {
284 if ( !(fFlags & RTPATH_STR_F_STYLE_DOS)
285 || (pParsed->fProps & (RTPATH_PROP_VOLUME | RTPATH_PROP_ROOT_SLASH)) != RTPATH_PROP_VOLUME )
286 rc = RTPathGetCurrent(pszCwd, cbCwd);
287 else
288 rc = RTPathGetCurrentOnDrive(pszPath[0], pszCwd, cbCwd);
289 if (RT_FAILURE(rc))
290 {
291 if (rc == VERR_BUFFER_OVERFLOW)
292 rc = VERR_FILENAME_TOO_LONG;
293 RTMemTmpFree(pszCwdFree);
294 return rc;
295 }
296 }
297 else
298 {
299 *pcbAbsPath = cbCwd + 1 + pParsed->cchPath + 1;
300 return rc;
301 }
302 }
303 else
304 return rc;
305
306 /*
307 * Parse the path.
308 */
309 union
310 {
311 RTPATHPARSED Parsed;
312 uint8_t abPadding[1024];
313 } uCwd;
314 PRTPATHPARSED pCwdParsedFree = NULL;
315 PRTPATHPARSED pCwdParsed = &uCwd.Parsed;
316 size_t cbCwdParsed = sizeof(uCwd);
317 rc = RTPathParse(pszCwd, pCwdParsed, cbCwdParsed, fFlags & RTPATH_STR_F_STYLE_MASK);
318 if (RT_SUCCESS(rc))
319 { /* likely */ }
320 else if (rc == VERR_BUFFER_OVERFLOW)
321 {
322 cbCwdParsed = RT_UOFFSETOF_DYN(RTPATHPARSED, aComps[pCwdParsed->cComps + 2]);
323 pCwdParsedFree = pCwdParsed = (PRTPATHPARSED)RTMemTmpAlloc(cbCwdParsed);
324 AssertReturnStmt(pCwdParsed, RTMemTmpFree(pszCwdFree), VERR_NO_TMP_MEMORY);
325 rc = RTPathParse(pszCwd, pCwdParsed, cbCwdParsed, fFlags & RTPATH_STR_F_STYLE_MASK);
326 AssertRCReturnStmt(rc, RTMemTmpFree(pCwdParsedFree); RTMemTmpFree(pszCwdFree), rc);
327 }
328 else
329 AssertMsgFailedReturn(("rc=%Rrc '%s'\n", rc, pszPath), rc);
330
331 /*
332 * Join paths with the base-path code.
333 */
334 if (fFlags & RTPATHABS_F_STOP_AT_CWD)
335 fFlags |= RTPATHABS_F_STOP_AT_BASE;
336 else
337 fFlags &= ~RTPATHABS_F_STOP_AT_BASE;
338 rc = rtPathAbsExWithCwdOrBaseCommon(pszCwd, cchInPlace, pCwdParsed, pszPath, pParsed, fFlags, pszAbsPath, pcbAbsPath);
339 if (pCwdParsedFree)
340 RTMemTmpFree(pCwdParsedFree);
341 if (pszCwdFree)
342 RTMemTmpFree(pszCwdFree);
343 return rc;
344}
345
346
347/**
348 * Handles the no-root-path scenario where we've got a base path.
349 */
350static int rtPathAbsExWithBase(const char *pszBase, const char *pszPath, PRTPATHPARSED pParsed, uint32_t fFlags,
351 char *pszAbsPath, size_t *pcbAbsPath)
352{
353 /*
354 * Parse the base path.
355 */
356 union
357 {
358 RTPATHPARSED Parsed;
359 uint8_t abPadding[1024];
360 } uBase;
361 PRTPATHPARSED pBaseParsedFree = NULL;
362 PRTPATHPARSED pBaseParsed = &uBase.Parsed;
363 size_t cbBaseParsed = sizeof(uBase);
364 int rc = RTPathParse(pszBase, pBaseParsed, cbBaseParsed, fFlags & RTPATH_STR_F_STYLE_MASK);
365 if (RT_SUCCESS(rc))
366 { /* likely */ }
367 else if (rc == VERR_BUFFER_OVERFLOW)
368 {
369 cbBaseParsed = RT_UOFFSETOF_DYN(RTPATHPARSED, aComps[pBaseParsed->cComps + 2]);
370 pBaseParsedFree = pBaseParsed = (PRTPATHPARSED)RTMemTmpAlloc(cbBaseParsed);
371 AssertReturn(pBaseParsed, VERR_NO_TMP_MEMORY);
372 rc = RTPathParse(pszBase, pBaseParsed, cbBaseParsed, fFlags & RTPATH_STR_F_STYLE_MASK);
373 AssertRCReturnStmt(rc, RTMemTmpFree(pBaseParsedFree), rc);
374 }
375 else
376 AssertMsgFailedReturn(("rc=%Rrc '%s'\n", rc, pszPath), rc);
377
378 /*
379 * If the base path isn't absolute, we need to deal with that.
380 */
381 size_t cchInPlace = 0;
382 if ((pBaseParsed->fProps & (RTPATH_PROP_ABSOLUTE | RTPATH_PROP_EXTRA_SLASHES | RTPATH_PROP_DOT_REFS)) == RTPATH_PROP_ABSOLUTE)
383 { /* likely */ }
384 else
385 {
386 cchInPlace = *pcbAbsPath;
387 rc = RTPathAbsEx(NULL, pszBase, fFlags, pszAbsPath, &cchInPlace);
388 if (RT_SUCCESS(rc))
389 {
390 Assert(strlen(pszAbsPath) == cchInPlace);
391 Assert(cchInPlace > 0);
392 }
393 else
394 {
395/** @todo Allocate temp buffer like we do for CWD? */
396 /* This is over generious, but don't want to put too much effort into it yet. */
397 if (rc == VERR_BUFFER_OVERFLOW)
398 *pcbAbsPath = cchInPlace + 1 + pParsed->cchPath + 1;
399 return rc;
400 }
401
402 /*
403 * Reparse it.
404 */
405 rc = RTPathParse(pszAbsPath, pBaseParsed, cbBaseParsed, fFlags & RTPATH_STR_F_STYLE_MASK);
406 if (RT_SUCCESS(rc))
407 { /* likely */ }
408 else if (rc == VERR_BUFFER_OVERFLOW)
409 {
410 if (pBaseParsedFree)
411 RTMemTmpFree(pBaseParsedFree);
412 cbBaseParsed = RT_UOFFSETOF_DYN(RTPATHPARSED, aComps[pBaseParsed->cComps + 2]);
413 pBaseParsedFree = pBaseParsed = (PRTPATHPARSED)RTMemTmpAlloc(cbBaseParsed);
414 AssertReturn(pBaseParsed, VERR_NO_TMP_MEMORY);
415 rc = RTPathParse(pszAbsPath, pBaseParsed, cbBaseParsed, fFlags & RTPATH_STR_F_STYLE_MASK);
416 AssertRCReturnStmt(rc, RTMemTmpFree(pBaseParsedFree), rc);
417 }
418 else
419 AssertMsgFailedReturn(("rc=%Rrc '%s'\n", rc, pszPath), rc);
420 }
421
422 /*
423 * Join paths with the CWD code.
424 */
425 rc = rtPathAbsExWithCwdOrBaseCommon(cchInPlace ? pszAbsPath : pszBase, cchInPlace, pBaseParsed,
426 pszPath, pParsed, fFlags, pszAbsPath, pcbAbsPath);
427 if (pBaseParsedFree)
428 RTMemTmpFree(pBaseParsedFree);
429 return rc;
430}
431
432
433/**
434 * Handles the RTPATH_PROP_ROOT_SLASH case.
435 */
436static int rtPathAbsExRootSlash(const char *pszBase, const char *pszPath, PRTPATHPARSED pParsed,
437 uint32_t fFlags, char *pszAbsPath, size_t *pcbAbsPath)
438{
439 /*
440 * Eliminate dot and dot-dot components.
441 * Note! aComp[0] is the root stuff and must never be dropped.
442 */
443 uint32_t const cComps = pParsed->cComps;
444 uint32_t const iStop = (pParsed->fProps & (RTPATH_PROP_UNC | RTPATH_PROP_SPECIAL_UNC)) != RTPATH_PROP_UNC
445 || pParsed->cComps < 2 ? 0 : 1;
446 uint32_t iLast = iStop;
447 uint32_t i = iStop + 1;
448 while (i < cComps)
449 {
450 uint32_t const cchComp = pParsed->aComps[i].cch;
451 if ( cchComp > 2
452 || pszPath[pParsed->aComps[i].off] != '.'
453 || (cchComp == 2 && pszPath[pParsed->aComps[i].off + 1] != '.') )
454 iLast = i;
455 else
456 {
457 Assert(cchComp == 1 || cchComp == 2);
458 pParsed->aComps[i].cch = 0;
459 if (cchComp == 2)
460 {
461 while (iLast > iStop && pParsed->aComps[iLast].cch == 0)
462 iLast--;
463 if (iLast > iStop)
464 {
465 Assert(pParsed->aComps[iLast].cch > 0);
466 pParsed->aComps[iLast].cch = 0;
467 iLast--;
468 }
469 }
470 }
471 i++;
472 }
473
474 /*
475 * Before we continue, ensure trailing slash if requested.
476 */
477 if ( (fFlags & RTPATHABS_F_ENSURE_TRAILING_SLASH)
478 && iLast > 0)
479 pParsed->fProps |= RTPATH_PROP_DIR_SLASH;
480
481 /*
482 * DOS-style: Do we need to supply a drive letter or UNC root?
483 */
484 size_t cchRootPrefix = 0;
485 if ( (fFlags & RTPATH_STR_F_STYLE_DOS)
486 && !(pParsed->fProps & (RTPATH_PROP_VOLUME | RTPATH_PROP_UNC)) )
487 {
488 /* Use the drive/UNC from the base path if we have one and it has such a component: */
489 if (pszBase)
490 {
491 union
492 {
493 RTPATHPARSED Parsed;
494 uint8_t abPadding[sizeof(RTPATHPARSED) + sizeof(pParsed->aComps[0]) * 2];
495 } uBase;
496 int rc = RTPathParse(pszBase, &uBase.Parsed, sizeof(uBase), fFlags & RTPATH_STR_F_STYLE_MASK);
497 AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW, ("%Rrc - '%s'\n", rc, pszBase), rc);
498
499 if (uBase.Parsed.fProps & RTPATH_PROP_VOLUME)
500 {
501 /* get the drive letter. */
502 Assert(uBase.Parsed.aComps[0].cch == 2 || uBase.Parsed.aComps[0].cch == 3);
503 cchRootPrefix = RT_MIN(uBase.Parsed.aComps[0].cch, 2);
504 if (cchRootPrefix < *pcbAbsPath)
505 memcpy(pszAbsPath, &pszBase[uBase.Parsed.aComps[0].off], cchRootPrefix);
506 else
507 {
508 rc = RTPathParsedReassemble(pszPath, pParsed, fFlags & RTPATH_STR_F_STYLE_MASK, pszAbsPath, 0);
509 Assert(rc == VERR_BUFFER_OVERFLOW);
510
511 *pcbAbsPath = cchRootPrefix + pParsed->cchPath + 1;
512 return VERR_BUFFER_OVERFLOW;
513 }
514 rtPathAbsExUpperCaseDriveLetter(pszAbsPath);
515 }
516 else if (uBase.Parsed.fProps & RTPATH_PROP_UNC)
517 {
518 /* Include the share if we've got one. */
519 cchRootPrefix = uBase.Parsed.aComps[0].cch;
520 if (uBase.Parsed.cComps >= 2 && !(uBase.Parsed.fProps & RTPATH_PROP_SPECIAL_UNC))
521 cchRootPrefix += uBase.Parsed.aComps[1].cch;
522 else if (uBase.Parsed.fProps & RTPATH_PROP_ROOT_SLASH)
523 cchRootPrefix--;
524 if (cchRootPrefix < *pcbAbsPath)
525 {
526 if (uBase.Parsed.cComps < 2 || (uBase.Parsed.fProps & RTPATH_PROP_SPECIAL_UNC))
527 memcpy(pszAbsPath, &pszBase[uBase.Parsed.aComps[0].off], cchRootPrefix);
528 else
529 {
530 size_t cchFirst = uBase.Parsed.aComps[0].cch;
531 memcpy(pszAbsPath, &pszBase[uBase.Parsed.aComps[0].off], cchFirst);
532 memcpy(&pszAbsPath[cchFirst], &pszBase[uBase.Parsed.aComps[1].off], uBase.Parsed.aComps[1].cch);
533 }
534 }
535 else
536 {
537 rc = RTPathParsedReassemble(pszPath, pParsed, fFlags & RTPATH_STR_F_STYLE_MASK, pszAbsPath, 0);
538 Assert(rc == VERR_BUFFER_OVERFLOW);
539
540 *pcbAbsPath = cchRootPrefix + pParsed->cchPath + 1;
541 return VERR_BUFFER_OVERFLOW;
542 }
543 }
544 else
545 pszBase = NULL;
546 }
547
548 /* Otherwise, query the current drive: */
549 if (!pszBase)
550 {
551 int rc = RTPathGetCurrentDrive(pszAbsPath, *pcbAbsPath);
552 if (RT_SUCCESS(rc))
553 cchRootPrefix = strlen(pszAbsPath);
554 else
555 {
556 if (rc == VERR_BUFFER_OVERFLOW)
557 {
558 int rc2 = RTPathParsedReassemble(pszPath, pParsed, fFlags & RTPATH_STR_F_STYLE_MASK, pszAbsPath, 0);
559 Assert(rc2 == VERR_BUFFER_OVERFLOW);
560
561 char *pszTmp = (char *)RTMemTmpAlloc(RTPATH_BIG_MAX);
562 if (pszTmp)
563 {
564 rc2 = RTPathGetCurrentDrive(pszTmp, RTPATH_BIG_MAX);
565 if (RT_SUCCESS(rc2))
566 *pcbAbsPath = strlen(pszTmp) + pParsed->cchPath + 1;
567 else
568 *pcbAbsPath = RT_MAX(*pcbAbsPath * 2, (size_t)RTPATH_BIG_MAX * 3 + pParsed->cchPath + 1);
569 RTMemTmpFree(pszTmp);
570 }
571 else
572 rc = VERR_NO_TMP_MEMORY;
573 }
574 return rc;
575 }
576 }
577 }
578
579 /*
580 * Reassemble the path and return.
581 */
582 int rc = RTPathParsedReassemble(pszPath, pParsed, fFlags & RTPATH_STR_F_STYLE_MASK,
583 pszAbsPath + cchRootPrefix, *pcbAbsPath - cchRootPrefix);
584 *pcbAbsPath = cchRootPrefix + pParsed->cchPath + (rc == VERR_BUFFER_OVERFLOW);
585 return rc;
586}
587
588
589/**
590 * Handles the RTPATH_PROP_ABSOLUTE case.
591 */
592static int rtPathAbsExAbsolute(const char *pszPath, PRTPATHPARSED pParsed, uint32_t fFlags, char *pszAbsPath, size_t *pcbAbsPath)
593{
594 if (pParsed->fProps & RTPATH_PROP_DOT_REFS)
595 {
596 uint32_t i = pParsed->cComps;
597 while (i-- > 0)
598 if ( pParsed->aComps[i].cch == 1
599 && pszPath[pParsed->aComps[i].off] == '.')
600 pParsed->aComps[i].cch = 0;
601 }
602
603 if ( (fFlags & RTPATHABS_F_ENSURE_TRAILING_SLASH)
604 && pParsed->cComps > 1)
605 pParsed->fProps |= RTPATH_PROP_DIR_SLASH;
606
607 int rc = RTPathParsedReassemble(pszPath, pParsed, fFlags & RTPATH_STR_F_STYLE_MASK, pszAbsPath, *pcbAbsPath);
608 *pcbAbsPath = pParsed->cchPath + (rc == VERR_BUFFER_OVERFLOW);
609 if (RT_SUCCESS(rc) && (pParsed->fProps & RTPATH_PROP_VOLUME))
610 rtPathAbsExUpperCaseDriveLetter(pszAbsPath);
611 return rc;
612}
613
614
615RTDECL(int) RTPathAbsEx(const char *pszBase, const char *pszPath, uint32_t fFlags, char *pszAbsPath, size_t *pcbAbsPath)
616{
617 LogFlow(("RTPathAbsEx: pszBase=%s pszPath=%s fFlags=%#x\n", pszBase, pszPath, fFlags));
618
619 /*
620 * Some input validation.
621 */
622 AssertPtr(pszPath);
623 AssertPtr(pszAbsPath);
624 AssertPtr(pcbAbsPath);
625 AssertReturn(*pszPath != '\0', VERR_PATH_ZERO_LENGTH);
626
627 AssertCompile(RTPATH_STR_F_STYLE_HOST == 0);
628 AssertReturn( RTPATH_STR_F_IS_VALID(fFlags, RTPATHABS_F_STOP_AT_BASE | RTPATHABS_F_STOP_AT_CWD | RTPATHABS_F_ENSURE_TRAILING_SLASH)
629 && !(fFlags & RTPATH_STR_F_MIDDLE), VERR_INVALID_FLAGS);
630 if ((fFlags & RTPATH_STR_F_STYLE_MASK) == RTPATH_STR_F_STYLE_HOST)
631 fFlags |= RTPATH_STYLE;
632
633 /*
634 * Parse the path we're to straigthen out.
635 */
636 union
637 {
638 RTPATHPARSED Parsed;
639 uint8_t abPadding[1024];
640 } uBuf;
641 PRTPATHPARSED pParsedFree = NULL;
642 PRTPATHPARSED pParsed = &uBuf.Parsed;
643 size_t cbParsed = sizeof(uBuf);
644 int rc = RTPathParse(pszPath, pParsed, cbParsed, fFlags & RTPATH_STR_F_STYLE_MASK);
645 if (RT_SUCCESS(rc))
646 { /* likely */ }
647 else if (rc == VERR_BUFFER_OVERFLOW)
648 {
649 cbParsed = RT_UOFFSETOF_DYN(RTPATHPARSED, aComps[pParsed->cComps + 2]);
650 pParsedFree = pParsed = (PRTPATHPARSED)RTMemTmpAlloc(cbParsed);
651 AssertReturn(pParsed, VERR_NO_TMP_MEMORY);
652 rc = RTPathParse(pszPath, pParsed, cbParsed, fFlags & RTPATH_STR_F_STYLE_MASK);
653 AssertRCReturnStmt(rc, RTMemTmpFree(pParsedFree), rc);
654 }
655 else
656 AssertMsgFailedReturn(("rc=%Rrc '%s'\n", rc, pszPath), rc);
657
658 /*
659 * Check if the input is more or less perfect as it is.
660 */
661 if (pParsed->fProps & RTPATH_PROP_ABSOLUTE)
662 rc = rtPathAbsExAbsolute(pszPath, pParsed, fFlags, pszAbsPath, pcbAbsPath);
663 /*
664 * What about relative but with a root slash.
665 */
666 else if (pParsed->fProps & RTPATH_PROP_ROOT_SLASH)
667 rc = rtPathAbsExRootSlash(pszBase, pszPath, pParsed, fFlags, pszAbsPath, pcbAbsPath);
668 /*
669 * Not exactly perfect. No root slash.
670 *
671 * If we have a base path, we use it unless we're into drive letters and
672 * pszPath refers to a different drive letter.
673 */
674 else if ( pszBase
675 && ( !(fFlags & RTPATH_STR_F_STYLE_DOS)
676 /** @todo add flag for skipping this and always using the base path? */
677 || !(pParsed->fProps & RTPATH_PROP_VOLUME)
678 || ( RT_C_IS_ALPHA(pszBase[0])
679 && pszBase[1] == ':'
680 && RT_C_TO_UPPER(pszBase[0]) == RT_C_TO_UPPER(pszPath[0])
681 )
682 )
683 )
684 rc = rtPathAbsExWithBase(pszBase, pszPath, pParsed, fFlags, pszAbsPath, pcbAbsPath);
685 else
686 rc = rtPathAbsExWithCwd(pszPath, pParsed, fFlags, pszAbsPath, pcbAbsPath);
687
688 if (pParsedFree)
689 RTMemTmpFree(pParsedFree);
690 LogFlow(("RTPathAbsEx: returns %Rrc *pcbAbsPath=%#zx\n", rc, *pcbAbsPath));
691 return rc;
692}
693
694
695RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cbAbsPath)
696{
697 return RTPathAbsEx(NULL, pszPath, RTPATH_STR_F_STYLE_HOST, pszAbsPath, &cbAbsPath);
698}
699
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use