VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/path/RTPathRmCmd.cpp@ 106580

Last change on this file since 106580 was 106061, checked in by vboxsync, 2 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: 22.9 KB
Line 
1/* $Id: RTPathRmCmd.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - RM Command.
4 */
5
6/*
7 * Copyright (C) 2013-2024 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#include <iprt/path.h>
42
43#include <iprt/buildconfig.h>
44#include <iprt/ctype.h>
45#include <iprt/err.h>
46#include <iprt/file.h>
47#include <iprt/dir.h>
48#include <iprt/getopt.h>
49#include <iprt/initterm.h>
50#include <iprt/message.h>
51#include <iprt/stream.h>
52#include <iprt/string.h>
53#include <iprt/symlink.h>
54
55
56/*********************************************************************************************************************************
57* Defined Constants And Macros *
58*********************************************************************************************************************************/
59#define RTPATHRMCMD_OPT_INTERACTIVE 1000
60#define RTPATHRMCMD_OPT_ONE_FILE_SYSTEM 1001
61#define RTPATHRMCMD_OPT_PRESERVE_ROOT 1002
62#define RTPATHRMCMD_OPT_NO_PRESERVE_ROOT 1003
63#define RTPATHRMCMD_OPT_MACHINE_READABLE 1004
64
65/** The max directory entry size. */
66#define RTPATHRM_DIR_MAX_ENTRY_SIZE (sizeof(RTDIRENTRYEX) + 4096)
67
68
69/*********************************************************************************************************************************
70* Structures and Typedefs *
71*********************************************************************************************************************************/
72/** Interactive option. */
73typedef enum
74{
75 RTPATHRMCMDINTERACTIVE_NONE = 1,
76 RTPATHRMCMDINTERACTIVE_ALL,
77 RTPATHRMCMDINTERACTIVE_ONCE
78 /** @todo possible that we should by default prompt if removing read-only
79 * files or files owned by someone else. We currently don't. */
80} RTPATHRMCMDINTERACTIVE;
81
82/**
83 * IPRT rm option structure.
84 */
85typedef struct RTPATHRMCMDOPTS
86{
87 /** Whether to delete recursively. */
88 bool fRecursive;
89 /** Whether to delete directories as well as other kinds of files. */
90 bool fDirsAndOther;
91 /** Whether to remove files without prompting and ignoring non-existing
92 * files. */
93 bool fForce;
94 /** Machine readable output. */
95 bool fMachineReadable;
96 /** Don't try remove root ('/') if set, otherwise don't treat root specially. */
97 bool fPreserveRoot;
98 /** Whether to keep to one file system. */
99 bool fOneFileSystem;
100 /** Whether to safely delete files (overwrite 3x before unlinking). */
101 bool fSafeDelete;
102 /** Whether to be verbose about the operation. */
103 bool fVerbose;
104 /** The interactive setting. */
105 RTPATHRMCMDINTERACTIVE enmInteractive;
106} RTPATHRMCMDOPTS;
107/** Pointer to the IPRT rm options. */
108typedef RTPATHRMCMDOPTS *PRTPATHRMCMDOPTS;
109
110
111/*********************************************************************************************************************************
112* Global Variables *
113*********************************************************************************************************************************/
114/** A bunch of zeros. */
115static uint8_t const g_abZeros[16384] = { 0 };
116/** A bunch of 0xFF bytes. (lazy init) */
117static uint8_t g_ab0xFF[16384];
118
119
120static void rtPathRmVerbose(PRTPATHRMCMDOPTS pOpts, const char *pszPath)
121{
122 if (!pOpts->fMachineReadable)
123 RTPrintf("%s\n", pszPath);
124}
125
126
127static int rtPathRmError(PRTPATHRMCMDOPTS pOpts, const char *pszPath, int rc,
128 const char *pszFormat, ...)
129{
130 if (pOpts->fMachineReadable)
131 RTPrintf("fname=%s%crc=%d%c", pszPath, 0, rc, 0);
132 else
133 {
134 va_list va;
135 va_start(va, pszFormat);
136 RTMsgErrorV(pszFormat, va);
137 va_end(va);
138 }
139 return rc;
140}
141
142
143/**
144 * Worker that removes a symbolic link.
145 *
146 * @returns IPRT status code, errors go via rtPathRmError.
147 * @param pOpts The RM options.
148 * @param pszPath The path to the symbolic link.
149 */
150static int rtPathRmOneSymlink(PRTPATHRMCMDOPTS pOpts, const char *pszPath)
151{
152 if (pOpts->fVerbose)
153 rtPathRmVerbose(pOpts, pszPath);
154 int rc = RTSymlinkDelete(pszPath, 0);
155 if (RT_FAILURE(rc))
156 return rtPathRmError(pOpts, pszPath, rc, "Error removing symbolic link '%s': %Rrc\n", pszPath, rc);
157 return rc;
158}
159
160
161/**
162 * Worker that removes a file.
163 *
164 * Currently used to delete both regular and special files.
165 *
166 * @returns IPRT status code, errors go via rtPathRmError.
167 * @param pOpts The RM options.
168 * @param pszPath The path to the file.
169 * @param pObjInfo The FS object info for the file.
170 */
171static int rtPathRmOneFile(PRTPATHRMCMDOPTS pOpts, const char *pszPath, PRTFSOBJINFO pObjInfo)
172{
173 int rc;
174 if (pOpts->fVerbose)
175 rtPathRmVerbose(pOpts, pszPath);
176
177 /*
178 * Wipe the file if requested and possible.
179 */
180 if (pOpts->fSafeDelete && RTFS_IS_FILE(pObjInfo->Attr.fMode))
181 {
182 /* Lazy init of the 0xff buffer. */
183 if (g_ab0xFF[0] != 0xff || g_ab0xFF[sizeof(g_ab0xFF) - 1] != 0xff)
184 memset(g_ab0xFF, 0xff, sizeof(g_ab0xFF));
185
186 RTFILE hFile;
187 rc = RTFileOpen(&hFile, pszPath, RTFILE_O_WRITE);
188 if (RT_FAILURE(rc))
189 return rtPathRmError(pOpts, pszPath, rc, "Opening '%s' for overwriting: %Rrc\n", pszPath, rc);
190
191 for (unsigned iPass = 0; iPass < 3; iPass++)
192 {
193 uint8_t const *pabFiller = iPass == 1 ? g_abZeros : g_ab0xFF;
194 size_t const cbFiller = iPass == 1 ? sizeof(g_abZeros) : sizeof(g_ab0xFF);
195
196 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_BEGIN, NULL);
197 if (RT_FAILURE(rc))
198 {
199 rc = rtPathRmError(pOpts, pszPath, rc, "Error seeking to start of '%s': %Rrc\n", pszPath, rc);
200 break;
201 }
202 for (RTFOFF cbLeft = pObjInfo->cbObject; cbLeft > 0; cbLeft -= cbFiller)
203 {
204 size_t cbToWrite = cbFiller;
205 if (cbLeft < (RTFOFF)cbToWrite)
206 cbToWrite = (size_t)cbLeft;
207 rc = RTFileWrite(hFile, pabFiller, cbToWrite, NULL);
208 if (RT_FAILURE(rc))
209 {
210 rc = rtPathRmError(pOpts, pszPath, rc, "Error writing to '%s': %Rrc\n", pszPath, rc);
211 break;
212 }
213 }
214 }
215
216 int rc2 = RTFileClose(hFile);
217 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
218 return rtPathRmError(pOpts, pszPath, rc2, "Closing '%s' failed: %Rrc\n", pszPath, rc);
219 if (RT_FAILURE(rc))
220 return rc;
221 }
222
223 /*
224 * Remove the file.
225 */
226 rc = RTFileDelete(pszPath);
227 if (RT_FAILURE(rc))
228 return rtPathRmError(pOpts, pszPath, rc,
229 RTFS_IS_FILE(pObjInfo->Attr.fMode)
230 ? "Error removing regular file '%s': %Rrc\n"
231 : "Error removing special file '%s': %Rrc\n",
232 pszPath, rc);
233 return rc;
234}
235
236
237/**
238 * Deletes one directory (if it's empty).
239 *
240 * @returns IPRT status code, errors go via rtPathRmError.
241 * @param pOpts The RM options.
242 * @param pszPath The path to the directory.
243 */
244static int rtPathRmOneDir(PRTPATHRMCMDOPTS pOpts, const char *pszPath)
245{
246 if (pOpts->fVerbose)
247 rtPathRmVerbose(pOpts, pszPath);
248
249 int rc = RTDirRemove(pszPath);
250 if (RT_FAILURE(rc))
251 return rtPathRmError(pOpts, pszPath, rc, "Error removing directory '%s': %Rrc", pszPath, rc);
252 return rc;
253}
254
255
256/**
257 * Recursively delete a directory.
258 *
259 * @returns IPRT status code, errors go via rtPathRmError.
260 * @param pOpts The RM options.
261 * @param pszPath Pointer to a writable buffer holding the path to
262 * the directory.
263 * @param cchPath The length of the path (avoid strlen).
264 * @param pDirEntry Pointer to a directory entry buffer that is
265 * RTPATHRM_DIR_MAX_ENTRY_SIZE bytes big.
266 */
267static int rtPathRmRecursive(PRTPATHRMCMDOPTS pOpts, char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry)
268{
269 /*
270 * Make sure the path ends with a slash.
271 */
272 if (!cchPath || !RTPATH_IS_SLASH(pszPath[cchPath - 1]))
273 {
274 if (cchPath + 1 >= RTPATH_MAX)
275 return rtPathRmError(pOpts, pszPath, VERR_BUFFER_OVERFLOW, "Buffer overflow fixing up '%s'.\n", pszPath);
276 pszPath[cchPath++] = RTPATH_SLASH;
277 pszPath[cchPath] = '\0';
278 }
279
280 /*
281 * Traverse the directory.
282 */
283 RTDIR hDir;
284 int rc = RTDirOpen(&hDir, pszPath);
285 if (RT_FAILURE(rc))
286 return rtPathRmError(pOpts, pszPath, rc, "Error opening directory '%s': %Rrc", pszPath, rc);
287 int rcRet = VINF_SUCCESS;
288 for (;;)
289 {
290 /*
291 * Read the next entry, constructing an full path for it.
292 */
293 size_t cbEntry = RTPATHRM_DIR_MAX_ENTRY_SIZE;
294 rc = RTDirReadEx(hDir, pDirEntry, &cbEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
295 if (rc == VERR_NO_MORE_FILES)
296 {
297 /*
298 * Reached the end of the directory.
299 */
300 pszPath[cchPath] = '\0';
301 rc = RTDirClose(hDir);
302 if (RT_FAILURE(rc))
303 return rtPathRmError(pOpts, pszPath, rc, "Error closing directory '%s': %Rrc", pszPath, rc);
304
305 /* Delete the directory. */
306 int rc2 = rtPathRmOneDir(pOpts, pszPath);
307 if (RT_FAILURE(rc2) && RT_SUCCESS(rcRet))
308 return rc2;
309 return rcRet;
310 }
311
312 if (RT_FAILURE(rc))
313 {
314 rc = rtPathRmError(pOpts, pszPath, rc, "Error reading directory '%s': %Rrc", pszPath, rc);
315 break;
316 }
317
318 /* Skip '.' and '..'. */
319 if ( pDirEntry->szName[0] == '.'
320 && ( pDirEntry->cbName == 1
321 || ( pDirEntry->cbName == 2
322 && pDirEntry->szName[1] == '.')))
323 continue;
324
325 /* Construct full path. */
326 if (cchPath + pDirEntry->cbName >= RTPATH_MAX)
327 {
328 pszPath[cchPath] = '\0';
329 rc = rtPathRmError(pOpts, pszPath, VERR_BUFFER_OVERFLOW, "Path buffer overflow in directory '%s'.", pszPath);
330 break;
331 }
332 memcpy(pszPath + cchPath, pDirEntry->szName, pDirEntry->cbName + 1);
333
334 /*
335 * Take action according to the type.
336 */
337 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
338 {
339 case RTFS_TYPE_FILE:
340 rc = rtPathRmOneFile(pOpts, pszPath, &pDirEntry->Info);
341 break;
342
343 case RTFS_TYPE_DIRECTORY:
344 rc = rtPathRmRecursive(pOpts, pszPath, cchPath + pDirEntry->cbName, pDirEntry);
345 break;
346
347 case RTFS_TYPE_SYMLINK:
348 rc = rtPathRmOneSymlink(pOpts, pszPath);
349 break;
350
351 case RTFS_TYPE_FIFO:
352 case RTFS_TYPE_DEV_CHAR:
353 case RTFS_TYPE_DEV_BLOCK:
354 case RTFS_TYPE_SOCKET:
355 rc = rtPathRmOneFile(pOpts, pszPath, &pDirEntry->Info);
356 break;
357
358 case RTFS_TYPE_WHITEOUT:
359 default:
360 rc = rtPathRmError(pOpts, pszPath, VERR_UNEXPECTED_FS_OBJ_TYPE,
361 "Object '%s' has an unknown file type: %o\n",
362 pszPath, pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK);
363 break;
364 }
365 if (RT_FAILURE(rc) && RT_SUCCESS(rcRet))
366 rcRet = rc;
367 }
368
369 /*
370 * Some error occured, close and return.
371 */
372 RTDirClose(hDir);
373 return rc;
374}
375
376/**
377 * Validates the specified file or directory.
378 *
379 * @returns IPRT status code, errors go via rtPathRmError.
380 * @param pOpts The RM options.
381 * @param pszPath The path to the file, directory, whatever.
382 */
383static int rtPathRmOneValidate(PRTPATHRMCMDOPTS pOpts, const char *pszPath)
384{
385 /*
386 * RTPathFilename doesn't do the trailing slash thing the way we need it to.
387 * E.g. both '..' and '../' should be rejected.
388 */
389 size_t cchPath = strlen(pszPath);
390 while (cchPath > 0 && RTPATH_IS_SLASH(pszPath[cchPath - 1]))
391 cchPath--;
392
393 if ( ( cchPath == 0
394 || 0 /** @todo drive letter + UNC crap */)
395 && pOpts->fPreserveRoot)
396 return rtPathRmError(pOpts, pszPath, VERR_CANT_DELETE_DIRECTORY, "Cannot remove root directory ('%s').\n", pszPath);
397
398 size_t offLast = cchPath - 1;
399 while (offLast > 0 && !RTPATH_IS_SEP(pszPath[offLast - 1]))
400 offLast--;
401
402 size_t cchLast = cchPath - offLast;
403 if ( pszPath[offLast] == '.'
404 && ( cchLast == 1
405 || (cchLast == 2 && pszPath[offLast + 1] == '.')))
406 return rtPathRmError(pOpts, pszPath, VERR_CANT_DELETE_DIRECTORY, "Cannot remove special directory '%s'.\n", pszPath);
407
408 return VINF_SUCCESS;
409}
410
411
412/**
413 * Remove one user specified file or directory.
414 *
415 * @returns IPRT status code, errors go via rtPathRmError.
416 * @param pOpts The RM options.
417 * @param pszPath The path to the file, directory, whatever.
418 */
419static int rtPathRmOne(PRTPATHRMCMDOPTS pOpts, const char *pszPath)
420{
421 /*
422 * RM refuses to delete some directories.
423 */
424 int rc = rtPathRmOneValidate(pOpts, pszPath);
425 if (RT_FAILURE(rc))
426 return rc;
427
428 /*
429 * Query file system object info.
430 */
431 RTFSOBJINFO ObjInfo;
432 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
433 if (RT_FAILURE(rc))
434 {
435 if (pOpts->fForce && (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND))
436 return VINF_SUCCESS;
437 return rtPathRmError(pOpts, pszPath, rc, "Error deleting '%s': %Rrc", pszPath, rc);
438 }
439
440 /*
441 * Take type specific action.
442 */
443 switch (ObjInfo.Attr.fMode & RTFS_TYPE_MASK)
444 {
445 case RTFS_TYPE_FILE:
446 return rtPathRmOneFile(pOpts, pszPath, &ObjInfo);
447
448 case RTFS_TYPE_DIRECTORY:
449 if (pOpts->fRecursive)
450 {
451 char szPath[RTPATH_MAX];
452 rc = RTPathAbs(pszPath, szPath, sizeof(szPath));
453 if (RT_FAILURE(rc))
454 return rtPathRmError(pOpts, pszPath, rc, "RTPathAbs failed on '%s': %Rrc\n", pszPath, rc);
455
456 union
457 {
458 RTDIRENTRYEX Core;
459 uint8_t abPadding[RTPATHRM_DIR_MAX_ENTRY_SIZE];
460 } DirEntry;
461
462 return rtPathRmRecursive(pOpts, szPath, strlen(szPath), &DirEntry.Core);
463 }
464 if (pOpts->fDirsAndOther)
465 return rtPathRmOneDir(pOpts, pszPath);
466 return rtPathRmError(pOpts, pszPath, VERR_IS_A_DIRECTORY, "Cannot remove '%s': %Rrc\n", pszPath, VERR_IS_A_DIRECTORY);
467
468 case RTFS_TYPE_SYMLINK:
469 return rtPathRmOneSymlink(pOpts, pszPath);
470
471 case RTFS_TYPE_FIFO:
472 case RTFS_TYPE_DEV_CHAR:
473 case RTFS_TYPE_DEV_BLOCK:
474 case RTFS_TYPE_SOCKET:
475 return rtPathRmOneFile(pOpts, pszPath, &ObjInfo);
476
477 case RTFS_TYPE_WHITEOUT:
478 default:
479 return rtPathRmError(pOpts, pszPath, VERR_UNEXPECTED_FS_OBJ_TYPE,
480 "Object '%s' has an unknown file type: %o\n", pszPath, ObjInfo.Attr.fMode & RTFS_TYPE_MASK);
481
482 }
483}
484
485
486RTDECL(RTEXITCODE) RTPathRmCmd(unsigned cArgs, char **papszArgs)
487{
488 /*
489 * Parse the command line.
490 */
491 static const RTGETOPTDEF s_aOptions[] =
492 {
493 /* operations */
494 { "--dirs-and-more", 'd', RTGETOPT_REQ_NOTHING },
495 { "--force", 'f', RTGETOPT_REQ_NOTHING },
496 { "--prompt", 'i', RTGETOPT_REQ_NOTHING },
497 { "--prompt-once", 'I', RTGETOPT_REQ_NOTHING },
498 { "--interactive", RTPATHRMCMD_OPT_INTERACTIVE, RTGETOPT_REQ_STRING },
499 { "--one-file-system", RTPATHRMCMD_OPT_ONE_FILE_SYSTEM, RTGETOPT_REQ_NOTHING },
500 { "--preserve-root", RTPATHRMCMD_OPT_PRESERVE_ROOT, RTGETOPT_REQ_NOTHING },
501 { "--no-preserve-root", RTPATHRMCMD_OPT_NO_PRESERVE_ROOT, RTGETOPT_REQ_NOTHING },
502 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
503 { "--recursive", 'r', RTGETOPT_REQ_NOTHING },
504 { "--safe-delete", 'P', RTGETOPT_REQ_NOTHING },
505 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
506
507 /* IPRT extensions */
508 { "--machine-readable", RTPATHRMCMD_OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
509 { "--machinereadable", RTPATHRMCMD_OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING }, /* bad long option style */
510 };
511
512 RTGETOPTSTATE GetState;
513 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
514 RTGETOPTINIT_FLAGS_OPTS_FIRST);
515 if (RT_FAILURE(rc))
516 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
517
518 RTPATHRMCMDOPTS Opts;
519 RT_ZERO(Opts);
520 Opts.fPreserveRoot = true;
521 Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_NONE;
522
523 RTGETOPTUNION ValueUnion;
524 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
525 && rc != VINF_GETOPT_NOT_OPTION)
526 {
527 switch (rc)
528 {
529 case 'd':
530 Opts.fDirsAndOther = true;
531 break;
532
533 case 'f':
534 Opts.fForce = true;
535 Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_NONE;
536 break;
537
538 case 'i':
539 Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ALL;
540 break;
541
542 case 'I':
543 Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ONCE;
544 break;
545
546 case RTPATHRMCMD_OPT_INTERACTIVE:
547 if (!strcmp(ValueUnion.psz, "always"))
548 Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ALL;
549 else if (!strcmp(ValueUnion.psz, "once"))
550 Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ONCE;
551 else
552 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown --interactive option value: '%s'\n", ValueUnion.psz);
553 break;
554
555 case RTPATHRMCMD_OPT_ONE_FILE_SYSTEM:
556 Opts.fOneFileSystem = true;
557 break;
558
559 case RTPATHRMCMD_OPT_PRESERVE_ROOT:
560 Opts.fPreserveRoot = true;
561 break;
562
563 case RTPATHRMCMD_OPT_NO_PRESERVE_ROOT:
564 Opts.fPreserveRoot = false;
565 break;
566
567 case 'R':
568 case 'r':
569 Opts.fRecursive = true;
570 Opts.fDirsAndOther = true;
571 break;
572
573 case 'P':
574 Opts.fSafeDelete = true;
575 break;
576
577 case 'v':
578 Opts.fVerbose = true;
579 break;
580
581 case RTPATHRMCMD_OPT_MACHINE_READABLE:
582 Opts.fMachineReadable = true;
583 break;
584
585 case 'h':
586 RTPrintf("Usage: to be written\nOption dump:\n");
587 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
588 if (RT_C_IS_PRINT(s_aOptions[i].iShort))
589 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
590 else
591 RTPrintf(" %s\n", s_aOptions[i].pszLong);
592 return RTEXITCODE_SUCCESS;
593
594 case 'V':
595 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
596 return RTEXITCODE_SUCCESS;
597
598 default:
599 return RTGetOptPrintError(rc, &ValueUnion);
600 }
601 }
602
603 /*
604 * Options we don't support.
605 */
606 if (Opts.fOneFileSystem)
607 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --one-file-system option is not yet implemented.\n");
608 if (Opts.enmInteractive != RTPATHRMCMDINTERACTIVE_NONE)
609 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The -i, -I and --interactive options are not implemented yet.\n");
610
611 /*
612 * No files means error.
613 */
614 if (rc != VINF_GETOPT_NOT_OPTION && !Opts.fForce)
615 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No files or directories specified.\n");
616
617 /*
618 * Machine readable init + header.
619 */
620 if (Opts.fMachineReadable)
621 {
622 int rc2 = RTStrmSetMode(g_pStdOut, true /*fBinary*/, false /*fCurrentCodeSet*/);
623 if (RT_FAILURE(rc2))
624 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTStrmSetMode failed: %Rrc.\n", rc2);
625 static const char s_achHeader[] = "hdr_id=rm\0hdr_ver=1";
626 RTStrmWrite(g_pStdOut, s_achHeader, sizeof(s_achHeader));
627 }
628
629 /*
630 * Delete the specified files/dirs/whatever.
631 */
632 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
633 while (rc == VINF_GETOPT_NOT_OPTION)
634 {
635 rc = rtPathRmOne(&Opts, ValueUnion.psz);
636 if (RT_FAILURE(rc))
637 rcExit = RTEXITCODE_FAILURE;
638
639 /* next */
640 rc = RTGetOpt(&GetState, &ValueUnion);
641 }
642 if (rc != 0)
643 rcExit = RTGetOptPrintError(rc, &ValueUnion);
644
645 /*
646 * Terminate the machine readable stuff.
647 */
648 if (Opts.fMachineReadable)
649 {
650 RTStrmWrite(g_pStdOut, "\0\0\0", 4);
651 rc = RTStrmFlush(g_pStdOut);
652 if (RT_FAILURE(rc) && rcExit == RTEXITCODE_SUCCESS)
653 rcExit = RTEXITCODE_FAILURE;
654 }
655
656 return rcExit;
657}
658
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