VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/dir.cpp@ 25517

Last change on this file since 25517 was 21675, checked in by vboxsync, 15 years ago

IPRT: Moved rtPathVolumeSpecLen.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 23.8 KB
Line 
1/* $Id: dir.cpp 21675 2009-07-17 12:18:30Z vboxsync $ */
2/** @file
3 * IPRT - Directory Manipulation.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#define LOG_GROUP RTLOGGROUP_DIR
36#ifdef RT_OS_WINDOWS /* PORTME: Assumes everyone else is using dir-posix.cpp */
37# include <Windows.h>
38#else
39# include <dirent.h>
40# include <unistd.h>
41# include <limits.h>
42#endif
43
44#include <iprt/dir.h>
45#include <iprt/file.h>
46#include <iprt/path.h>
47#include <iprt/alloc.h>
48#include <iprt/log.h>
49#include <iprt/param.h>
50#include <iprt/string.h>
51#include <iprt/err.h>
52#include <iprt/assert.h>
53#include <iprt/uni.h>
54#include "internal/dir.h"
55#include "internal/path.h"
56
57
58static DECLCALLBACK(bool) rtDirFilterWinNtMatch(PRTDIR pDir, const char *pszName);
59static DECLCALLBACK(bool) rtDirFilterWinNtMatchNoWildcards(PRTDIR pDir, const char *pszName);
60DECLINLINE(bool) rtDirFilterWinNtMatchEon(PCRTUNICP puszFilter);
61static bool rtDirFilterWinNtMatchDosStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
62static bool rtDirFilterWinNtMatchStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
63static bool rtDirFilterWinNtMatchBase(unsigned iDepth, const char *pszName, PCRTUNICP puszFilter);
64
65
66
67RTDECL(int) RTDirCreateFullPath(const char *pszPath, RTFMODE fMode)
68{
69 /*
70 * Resolve the path.
71 */
72 char szAbsPath[RTPATH_MAX];
73 int rc = RTPathAbs(pszPath, szAbsPath, sizeof(szAbsPath));
74 if (RT_FAILURE(rc))
75 return rc;
76
77 /*
78 * Iterate the path components making sure each of them exists.
79 */
80 /* skip volume name */
81 char *psz = &szAbsPath[rtPathVolumeSpecLen(szAbsPath)];
82
83 /* skip the root slash if any */
84 if ( psz[0] == '/'
85#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
86 || psz[0] == '\\'
87#endif
88 )
89 psz++;
90
91 /* iterate over path components. */
92 do
93 {
94 /* the next component is NULL, stop iterating */
95 if (!*psz)
96 break;
97#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
98 psz = strpbrk(psz, "\\/");
99#else
100 psz = strchr(psz, '/');
101#endif
102 if (psz)
103 *psz = '\0';
104 /*
105 * ASSUME that RTDirCreate will return VERR_ALREADY_EXISTS and not VERR_ACCESS_DENIED in those cases
106 * where the directory exists but we don't have write access to the parent directory.
107 */
108 rc = RTDirCreate(szAbsPath, fMode);
109 if (rc == VERR_ALREADY_EXISTS)
110 rc = VINF_SUCCESS;
111 if (!psz)
112 break;
113 *psz++ = RTPATH_DELIMITER;
114 } while (RT_SUCCESS(rc));
115
116 return rc;
117}
118
119
120/**
121 * Filter a the filename in the against a filter.
122 *
123 * @returns true if the name matches the filter.
124 * @returns false if the name doesn't match filter.
125 * @param pDir The directory handle.
126 * @param pszName The path to match to the filter.
127 */
128static DECLCALLBACK(bool) rtDirFilterWinNtMatchNoWildcards(PRTDIR pDir, const char *pszName)
129{
130 /*
131 * Walk the string and compare.
132 */
133 PCRTUNICP pucFilter = pDir->puszFilter;
134 const char *psz = pszName;
135 RTUNICP uc;
136 do
137 {
138 int rc = RTStrGetCpEx(&psz, &uc);
139 AssertRCReturn(rc, false);
140 RTUNICP ucFilter = *pucFilter++;
141 if ( uc != ucFilter
142 && RTUniCpToUpper(uc) != ucFilter)
143 return false;
144 } while (uc);
145 return true;
146}
147
148
149/**
150 * Matches end of name.
151 */
152DECLINLINE(bool) rtDirFilterWinNtMatchEon(PCRTUNICP puszFilter)
153{
154 RTUNICP ucFilter;
155 while ( (ucFilter = *puszFilter) == '>'
156 || ucFilter == '<'
157 || ucFilter == '*'
158 || ucFilter == '"')
159 puszFilter++;
160 return !ucFilter;
161}
162
163
164/**
165 * Recursive star matching.
166 * Practically the same as normal star, except that the dos star stops
167 * when hitting the last dot.
168 *
169 * @returns true on match.
170 * @returns false on miss.
171 */
172static bool rtDirFilterWinNtMatchDosStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter)
173{
174 AssertReturn(iDepth++ < 256, false);
175
176 /*
177 * If there is no dos star, we should work just like the NT star.
178 * Since that's generally faster algorithms, we jump down to there if we can.
179 */
180 const char *pszDosDot = strrchr(pszNext, '.');
181 if (!pszDosDot && uc == '.')
182 pszDosDot = pszNext - 1;
183 if (!pszDosDot)
184 return rtDirFilterWinNtMatchStar(iDepth, uc, pszNext, puszFilter);
185
186 /*
187 * Inspect the next filter char(s) until we find something to work on.
188 */
189 RTUNICP ucFilter = *puszFilter++;
190 switch (ucFilter)
191 {
192 /*
193 * The star expression is the last in the pattern.
194 * We're fine if the name ends with a dot.
195 */
196 case '\0':
197 return !pszDosDot[1];
198
199 /*
200 * Simplified by brute force.
201 */
202 case '>': /* dos question mark */
203 case '?':
204 case '*':
205 case '<': /* dos star */
206 case '"': /* dos dot */
207 {
208 puszFilter--;
209 const char *pszStart = pszNext;
210 do
211 {
212 if (rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
213 return true;
214 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
215 } while ((intptr_t)pszDosDot - (intptr_t)pszNext >= -1);
216
217 /* backtrack and do the current char. */
218 pszNext = RTStrPrevCp(NULL, pszStart); AssertReturn(pszNext, false);
219 return rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter);
220 }
221
222 /*
223 * Ok, we've got zero or more characters.
224 * We'll try match starting at each occurence of this character.
225 */
226 default:
227 {
228 if ( RTUniCpToUpper(uc) == ucFilter
229 && rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
230 return true;
231 do
232 {
233 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
234 if ( RTUniCpToUpper(uc) == ucFilter
235 && rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
236 return true;
237 } while ((intptr_t)pszDosDot - (intptr_t)pszNext >= -1);
238 return false;
239 }
240 }
241 /* won't ever get here! */
242}
243
244
245/**
246 * Recursive star matching.
247 *
248 * @returns true on match.
249 * @returns false on miss.
250 */
251static bool rtDirFilterWinNtMatchStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter)
252{
253 AssertReturn(iDepth++ < 256, false);
254
255 /*
256 * Inspect the next filter char(s) until we find something to work on.
257 */
258 for (;;)
259 {
260 RTUNICP ucFilter = *puszFilter++;
261 switch (ucFilter)
262 {
263 /*
264 * The star expression is the last in the pattern.
265 * Cool, that means we're done!
266 */
267 case '\0':
268 return true;
269
270 /*
271 * Just in case (doubt we ever get here), just merge it with the current one.
272 */
273 case '*':
274 break;
275
276 /*
277 * Skip a fixed number of chars.
278 * Figure out how many by walking the filter ignoring '*'s.
279 */
280 case '?':
281 {
282 unsigned cQms = 1;
283 while ((ucFilter = *puszFilter) == '*' || ucFilter == '?')
284 {
285 cQms += ucFilter == '?';
286 puszFilter++;
287 }
288 do
289 {
290 if (!uc)
291 return false;
292 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
293 } while (--cQms > 0);
294 /* done? */
295 if (!ucFilter)
296 return true;
297 break;
298 }
299
300 /*
301 * The simple way is to try char by char and match the remaining
302 * expression. If it's trailing we're done.
303 */
304 case '>': /* dos question mark */
305 {
306 if (rtDirFilterWinNtMatchEon(puszFilter))
307 return true;
308 const char *pszStart = pszNext;
309 do
310 {
311 if (rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
312 return true;
313 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
314 } while (uc);
315
316 /* backtrack and do the current char. */
317 pszNext = RTStrPrevCp(NULL, pszStart); AssertReturn(pszNext, false);
318 return rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter);
319 }
320
321 /*
322 * This bugger is interesting.
323 * Time for brute force. Iterate the name char by char.
324 */
325 case '<':
326 {
327 do
328 {
329 if (rtDirFilterWinNtMatchDosStar(iDepth, uc, pszNext, puszFilter))
330 return true;
331 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
332 } while (uc);
333 return false;
334 }
335
336 /*
337 * This guy matches a '.' or the end of the name.
338 * It's very simple if the rest of the filter expression also matches eon.
339 */
340 case '"':
341 if (rtDirFilterWinNtMatchEon(puszFilter))
342 return true;
343 ucFilter = '.';
344 /* fall thru */
345
346 /*
347 * Ok, we've got zero or more characters.
348 * We'll try match starting at each occurence of this character.
349 */
350 default:
351 {
352 do
353 {
354 if ( RTUniCpToUpper(uc) == ucFilter
355 && rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
356 return true;
357 int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
358 } while (uc);
359 return false;
360 }
361 }
362 } /* for (;;) */
363
364 /* won't ever get here! */
365}
366
367
368/**
369 * Filter a the filename in the against a filter.
370 *
371 * The rules are as follows:
372 * '?' Matches exactly one char.
373 * '*' Matches zero or more chars.
374 * '<' The dos star, matches zero or more chars except the DOS dot.
375 * '>' The dos question mark, matches one char, but dots and end-of-name eats them.
376 * '"' The dos dot, matches a dot or end-of-name.
377 *
378 * @returns true if the name matches the filter.
379 * @returns false if the name doesn't match filter.
380 * @param iDepth The recursion depth.
381 * @param pszName The path to match to the filter.
382 * @param puszFilter The filter string.
383 */
384static bool rtDirFilterWinNtMatchBase(unsigned iDepth, const char *pszName, PCRTUNICP puszFilter)
385{
386 AssertReturn(iDepth++ < 256, false);
387
388 /*
389 * Walk the string and match it up char by char.
390 */
391 RTUNICP uc;
392 do
393 {
394 RTUNICP ucFilter = *puszFilter++;
395 int rc = RTStrGetCpEx(&pszName, &uc); AssertRCReturn(rc, false);
396 switch (ucFilter)
397 {
398 /* Exactly one char. */
399 case '?':
400 if (!uc)
401 return false;
402 break;
403
404 /* One char, but the dos dot and end-of-name eats '>' and '<'. */
405 case '>': /* dos ? */
406 if (!uc)
407 return rtDirFilterWinNtMatchEon(puszFilter);
408 if (uc == '.')
409 {
410 while ((ucFilter = *puszFilter) == '>' || ucFilter == '<')
411 puszFilter++;
412 if (ucFilter == '"' || ucFilter == '.') /* not 100% sure about the last dot */
413 ++puszFilter;
414 else /* the does question mark doesn't match '.'s, so backtrack. */
415 pszName = RTStrPrevCp(NULL, pszName);
416 }
417 break;
418
419 /* Match a dot or the end-of-name. */
420 case '"': /* dos '.' */
421 if (uc != '.')
422 {
423 if (uc)
424 return false;
425 return rtDirFilterWinNtMatchEon(puszFilter);
426 }
427 break;
428
429 /* zero or more */
430 case '*':
431 return rtDirFilterWinNtMatchStar(iDepth, uc, pszName, puszFilter);
432 case '<': /* dos '*' */
433 return rtDirFilterWinNtMatchDosStar(iDepth, uc, pszName, puszFilter);
434
435
436 /* uppercased match */
437 default:
438 {
439 if (RTUniCpToUpper(uc) != ucFilter)
440 return false;
441 break;
442 }
443 }
444 } while (uc);
445
446 return true;
447}
448
449
450/**
451 * Filter a the filename in the against a filter.
452 *
453 * @returns true if the name matches the filter.
454 * @returns false if the name doesn't match filter.
455 * @param pDir The directory handle.
456 * @param pszName The path to match to the filter.
457 */
458static DECLCALLBACK(bool) rtDirFilterWinNtMatch(PRTDIR pDir, const char *pszName)
459{
460 return rtDirFilterWinNtMatchBase(0, pszName, pDir->puszFilter);
461}
462
463
464/**
465 * Initializes a WinNt like wildcard filter.
466 *
467 * @returns Pointer to the filter function.
468 * @returns NULL if the filter doesn't filter out anything.
469 * @param pDir The directory handle (not yet opened).
470 */
471static PFNRTDIRFILTER rtDirFilterWinNtInit(PRTDIR pDir)
472{
473 /*
474 * Check for the usual * and <"< (*.* in DOS language) patterns.
475 */
476 if ( (pDir->cchFilter == 1 && pDir->pszFilter[0] == '*')
477 || (pDir->cchFilter == 3 && !memcmp(pDir->pszFilter, "<\".>", 3))
478 )
479 return NULL;
480
481 /*
482 * Uppercase the expression, also do a little optimizations when possible.
483 */
484 bool fHaveWildcards = false;
485 unsigned iRead = 0;
486 unsigned iWrite = 0;
487 while (iRead < pDir->cucFilter)
488 {
489 RTUNICP uc = pDir->puszFilter[iRead++];
490 if (uc == '*')
491 {
492 fHaveWildcards = true;
493 /* remove extra stars. */
494 RTUNICP uc2;
495 while ((uc2 = pDir->puszFilter[iRead + 1]) == '*')
496 iRead++;
497 }
498 else if (uc == '?' || uc == '>' || uc == '<' || uc == '"')
499 fHaveWildcards = true;
500 else
501 uc = RTUniCpToUpper(uc);
502 pDir->puszFilter[iWrite++] = uc;
503 }
504 pDir->puszFilter[iWrite] = 0;
505 pDir->cucFilter = iWrite;
506
507 return fHaveWildcards
508 ? rtDirFilterWinNtMatch
509 : rtDirFilterWinNtMatchNoWildcards;
510}
511
512
513/**
514 * Common worker for opening a directory.
515 *
516 * @returns IPRT status code.
517 * @param ppDir Where to store the directory handle.
518 * @param pszPath The specified path.
519 * @param pszFilter Pointer to where the filter start in the path. NULL if no filter.
520 * @param enmFilter The type of filter to apply.
521 */
522static int rtDirOpenCommon(PRTDIR *ppDir, const char *pszPath, const char *pszFilter, RTDIRFILTER enmFilter)
523{
524 /*
525 * Expand the path.
526 *
527 * The purpose of this exercise to have the abs path around
528 * for querying extra information about the objects we list.
529 * As a sideeffect we also validate the path here.
530 */
531 char szRealPath[RTPATH_MAX + 1];
532 int rc;
533 size_t cbFilter; /* includes '\0' (thus cb and not cch). */
534 size_t cucFilter0; /* includes U+0. */
535 if (!pszFilter)
536 {
537 cbFilter = cucFilter0 = 0;
538 rc = RTPathReal(pszPath, szRealPath, sizeof(szRealPath) - 1);
539 }
540 else
541 {
542 cbFilter = strlen(pszFilter) + 1;
543 cucFilter0 = RTStrUniLen(pszFilter) + 1;
544
545 if (pszFilter != pszPath)
546 {
547 /* yea, I'm lazy. sue me. */
548 char *pszTmp = RTStrDup(pszPath);
549 if (!pszTmp)
550 return VERR_NO_MEMORY;
551 pszTmp[pszFilter - pszPath] = '\0';
552 rc = RTPathReal(pszTmp, szRealPath, sizeof(szRealPath) - 1);
553 RTStrFree(pszTmp);
554 }
555 else
556 rc = RTPathReal(".", szRealPath, sizeof(szRealPath) - 1);
557 }
558 if (RT_FAILURE(rc))
559 return rc;
560
561 /* add trailing '/' if missing. */
562 size_t cchRealPath = strlen(szRealPath);
563 if (!RTPATH_IS_SEP(szRealPath[cchRealPath - 1]))
564 {
565 szRealPath[cchRealPath++] = RTPATH_SLASH;
566 szRealPath[cchRealPath] = '\0';
567 }
568
569 /*
570 * Allocate and initialize the directory handle.
571 *
572 * The posix definition of Data.d_name allows it to be < NAME_MAX + 1,
573 * thus the horrible uglyness here. Solaris uses d_name[1] for instance.
574 */
575#ifndef RT_OS_WINDOWS
576 long cbNameMax = pathconf(szRealPath, _PC_NAME_MAX);
577# ifdef NAME_MAX
578 if (cbNameMax < NAME_MAX) /* This is plain paranoia, but it doesn't hurt. */
579 cbNameMax = NAME_MAX;
580# endif
581# ifdef _XOPEN_NAME_MAX
582 if (cbNameMax < _XOPEN_NAME_MAX) /* Ditto. */
583 cbNameMax = _XOPEN_NAME_MAX;
584# endif
585 size_t cbDir = RT_OFFSETOF(RTDIR, Data.d_name[cbNameMax + 1]);
586 if (cbDir < sizeof(RTDIR)) /* Ditto. */
587 cbDir = sizeof(RTDIR);
588 cbDir = RT_ALIGN_Z(cbDir, 8);
589#else
590 size_t cbDir = sizeof(RTDIR);
591#endif
592 size_t const cbAllocated = cbDir
593 + cucFilter0 * sizeof(RTUNICP)
594 + cbFilter
595 + cchRealPath + 1 + 4;
596 PRTDIR pDir = (PRTDIR)RTMemAlloc(cbAllocated);
597 if (!pDir)
598 return VERR_NO_MEMORY;
599 uint8_t *pb = (uint8_t *)pDir + cbDir;
600
601 /* initialize it */
602 pDir->u32Magic = RTDIR_MAGIC;
603 if (cbFilter)
604 {
605 pDir->puszFilter = (PRTUNICP)pb;
606 rc = RTStrToUniEx(pszFilter, RTSTR_MAX, &pDir->puszFilter, cucFilter0, &pDir->cucFilter);
607 AssertRC(rc);
608 pb += cucFilter0 * sizeof(RTUNICP);
609 pDir->pszFilter = (char *)memcpy(pb, pszFilter, cbFilter);
610 pDir->cchFilter = cbFilter - 1;
611 pb += cbFilter;
612 }
613 else
614 {
615 pDir->puszFilter = NULL;
616 pDir->cucFilter = 0;
617 pDir->pszFilter = NULL;
618 pDir->cchFilter = 0;
619 }
620 pDir->enmFilter = enmFilter;
621 switch (enmFilter)
622 {
623 default:
624 case RTDIRFILTER_NONE:
625 pDir->pfnFilter = NULL;
626 break;
627 case RTDIRFILTER_WINNT:
628 pDir->pfnFilter = rtDirFilterWinNtInit(pDir);
629 break;
630 case RTDIRFILTER_UNIX:
631 pDir->pfnFilter = NULL;
632 break;
633 case RTDIRFILTER_UNIX_UPCASED:
634 pDir->pfnFilter = NULL;
635 break;
636 }
637 pDir->cchPath = cchRealPath;
638 pDir->pszPath = (char *)memcpy(pb, szRealPath, cchRealPath + 1);
639 Assert(pb - (uint8_t *)pDir + cchRealPath + 1 <= cbAllocated);
640 pDir->fDataUnread = false;
641#ifndef RT_DONT_CONVERT_FILENAMES
642 pDir->pszName = NULL;
643 pDir->cchName = 0;
644#endif
645#ifndef RT_OS_WINDOWS
646 pDir->cbMaxName = cbDir - RT_OFFSETOF(RTDIR, Data.d_name);
647#endif
648
649 /*
650 * Hand it over to the native part.
651 */
652 rc = rtOpenDirNative(pDir, szRealPath);
653 if (RT_SUCCESS(rc))
654 *ppDir = pDir;
655 else
656 RTMemFree(pDir);
657
658 return rc;
659}
660
661
662
663RTDECL(int) RTDirOpen(PRTDIR *ppDir, const char *pszPath)
664{
665 /*
666 * Validate input.
667 */
668 AssertMsgReturn(VALID_PTR(ppDir), ("%p\n", ppDir), VERR_INVALID_POINTER);
669 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
670
671 /*
672 * Take common cause with RTDirOpenFiltered().
673 */
674 int rc = rtDirOpenCommon(ppDir, pszPath, NULL, RTDIRFILTER_NONE);
675 LogFlow(("RTDirOpen(%p:{%p}, %p:{%s}): return %Rrc\n", ppDir, *ppDir, pszPath, pszPath, rc));
676 return rc;
677}
678
679
680RTDECL(int) RTDirOpenFiltered(PRTDIR *ppDir, const char *pszPath, RTDIRFILTER enmFilter)
681{
682 /*
683 * Validate input.
684 */
685 AssertMsgReturn(VALID_PTR(ppDir), ("%p\n", ppDir), VERR_INVALID_POINTER);
686 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
687 switch (enmFilter)
688 {
689 case RTDIRFILTER_UNIX:
690 case RTDIRFILTER_UNIX_UPCASED:
691 AssertMsgFailed(("%d is not implemented!\n", enmFilter));
692 return VERR_NOT_IMPLEMENTED;
693 case RTDIRFILTER_NONE:
694 case RTDIRFILTER_WINNT:
695 break;
696 default:
697 AssertMsgFailedReturn(("%d\n", enmFilter), VERR_INVALID_PARAMETER);
698 }
699
700 /*
701 * Find the last component, i.e. where the filter criteria starts and the dir name ends.
702 */
703 const char *pszFilter;
704 if (enmFilter == RTDIRFILTER_NONE)
705 pszFilter = NULL;
706 else
707 {
708 pszFilter = RTPathFilename(pszPath);
709 if (!pszFilter) /* trailing slash => directory to read => no filter. */
710 enmFilter = RTDIRFILTER_NONE;
711 }
712
713 /*
714 * Call worker common with RTDirOpen which will verify the path, allocate
715 * and initialize the handle, and finally call the backend.
716 */
717 int rc = rtDirOpenCommon(ppDir, pszPath, pszFilter, enmFilter);
718
719 LogFlow(("RTDirOpenFiltered(%p:{%p}, %p:{%s}, %d): return %Rrc\n",
720 ppDir, *ppDir, pszPath, pszPath, enmFilter, rc));
721 return rc;
722}
723
724
725RTDECL(int) RTDirRemoveRecursive(const char *pszPath)
726{
727 int rc;
728
729 if (!RTDirExists(pszPath))
730 return VINF_SUCCESS;
731
732 char szAbsPath[RTPATH_MAX];
733 /** @todo use RTPathReal here instead? */
734 rc = RTPathAbs(pszPath, szAbsPath, sizeof(szAbsPath));
735 if (RT_FAILURE(rc))
736 return rc;
737
738 PRTDIR pDir = NULL;
739 rc = RTDirOpen(&pDir, szAbsPath);
740 if (RT_SUCCESS(rc))
741 {
742 RTDIRENTRY dirEntry;
743 size_t cbDirEntry = sizeof(dirEntry);
744
745 while ((rc = RTDirRead(pDir, &dirEntry, NULL /* Passing an argument won't work here yet. */)) == VINF_SUCCESS)
746 {
747 char* pszEntry = NULL;
748 rc = RTStrAPrintf(&pszEntry, "%s/%s", szAbsPath, dirEntry.szName);
749 if( RT_SUCCESS(rc)
750 && strcmp(dirEntry.szName, ".")
751 && strcmp(dirEntry.szName, ".."))
752 {
753 switch (dirEntry.enmType)
754 {
755 case RTDIRENTRYTYPE_FILE:
756
757 rc = RTFileDelete(pszEntry);
758 break;
759
760 case RTDIRENTRYTYPE_DIRECTORY:
761
762 rc = RTDirRemoveRecursive(pszEntry);
763 break;
764
765 default:
766 /** @todo not implemented yet. */
767 break;
768 }
769
770 RTStrFree(pszEntry);
771 }
772
773 if (RT_FAILURE(rc))
774 break;
775 }
776 if (rc == VERR_NO_MORE_FILES)
777 rc = VINF_SUCCESS;
778
779 RTDirClose(pDir);
780 rc = RTDirRemove(szAbsPath);
781 }
782
783 LogFlow(("RTDirRemoveRecursive(%p:{%s}): returns %Rrc\n", pszPath, pszPath, rc));
784 return rc;
785}
786
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