VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/zip/gzipcmd.cpp@ 73516

Last change on this file since 73516 was 70424, checked in by vboxsync, 7 years ago

IPRT: Moved RTZipGZipCmd into the runtime, leaving a main wrapper behind. Want to use it too in VBoxControl for debugging purposes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.6 KB
Line 
1/* $Id: gzipcmd.cpp 70424 2018-01-02 11:59:55Z vboxsync $ */
2/** @file
3 * IPRT - GZIP Utility.
4 */
5
6/*
7 * Copyright (C) 2010-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#include <iprt/zip.h>
32
33#include <iprt/buildconfig.h>
34#include <iprt/file.h>
35#include <iprt/getopt.h>
36#include <iprt/initterm.h>
37#include <iprt/message.h>
38#include <iprt/param.h>
39#include <iprt/path.h>
40#include <iprt/stream.h>
41#include <iprt/string.h>
42#include <iprt/vfs.h>
43#include <iprt/zip.h>
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49/**
50 * Gzip command options.
51 */
52typedef struct RTGZIPCMDOPTS
53{
54 bool fAscii;
55 bool fStdOut;
56 bool fDecompress;
57 bool fForce;
58 bool fKeep;
59 bool fList;
60 bool fName;
61 bool fQuiet;
62 bool fRecursive;
63 const char *pszSuff;
64 bool fTest;
65 unsigned uLevel;
66 /** The current output filename (for deletion). */
67 char szOutput[RTPATH_MAX];
68 /** The current input filename (for deletion and messages). */
69 const char *pszInput;
70} RTGZIPCMDOPTS;
71/** Pointer to GZIP options. */
72typedef RTGZIPCMDOPTS *PRTGZIPCMDOPTS;
73/** Pointer to const GZIP options. */
74typedef RTGZIPCMDOPTS const *PCRTGZIPCMDOPTS;
75
76
77
78/**
79 * Checks if the given standard handle is a TTY.
80 *
81 * @returns true / false
82 * @param enmStdHandle The standard handle.
83 */
84static bool gzipIsStdHandleATty(RTHANDLESTD enmStdHandle)
85{
86 /** @todo Add isatty() to IPRT. */
87 RT_NOREF1(enmStdHandle);
88 return false;
89}
90
91
92/**
93 * Pushes data from the input to the output I/O streams.
94 *
95 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
96 * @param hVfsSrc The source I/O stream.
97 * @param hVfsDst The destination I/O stream.
98 */
99static RTEXITCODE gzipPush(RTVFSIOSTREAM hVfsSrc, RTVFSIOSTREAM hVfsDst)
100{
101 for (;;)
102 {
103 uint8_t abBuf[_64K];
104 size_t cbRead;
105 int rc = RTVfsIoStrmRead(hVfsSrc, abBuf, sizeof(abBuf), true /*fBlocking*/, &cbRead);
106 if (RT_FAILURE(rc))
107 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmRead failed: %Rrc", rc);
108 if (rc == VINF_EOF && cbRead == 0)
109 return RTEXITCODE_SUCCESS;
110
111 rc = RTVfsIoStrmWrite(hVfsDst, abBuf, cbRead, true /*fBlocking*/, NULL /*cbWritten*/);
112 if (RT_FAILURE(rc))
113 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmWrite failed: %Rrc", rc);
114 }
115}
116
117
118/**
119 * Pushes the bytes from the input to the output stream, flushes the output
120 * stream and closes both of them.
121 *
122 * On failure, we will delete the output file, if it's a file. The input file
123 * may be deleted, if we're not told to keep it (--keep, --to-stdout).
124 *
125 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
126 * @param phVfsSrc The input stream. Set to NIL if closed.
127 * @param pOpts The options.
128 * @param phVfsDst The output stream. Set to NIL if closed.
129 */
130static RTEXITCODE gzipPushFlushAndClose(PRTVFSIOSTREAM phVfsSrc, PCRTGZIPCMDOPTS pOpts, PRTVFSIOSTREAM phVfsDst)
131{
132 /*
133 * Push bytes, flush and close the streams.
134 */
135 RTEXITCODE rcExit = gzipPush(*phVfsSrc, *phVfsDst);
136
137 RTVfsIoStrmRelease(*phVfsSrc);
138 *phVfsSrc = NIL_RTVFSIOSTREAM;
139
140 int rc = RTVfsIoStrmFlush(*phVfsDst);
141 if (RT_FAILURE(rc) && rc != VERR_INVALID_PARAMETER)
142 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to flush the output file: %Rrc", rc);
143 RTVfsIoStrmRelease(*phVfsDst);
144 *phVfsDst = NIL_RTVFSIOSTREAM;
145
146 /*
147 * Do the cleaning up, if needed. Remove the input file, if that's the
148 * desire of the user, or remove the output file on failure.
149 */
150 if (!pOpts->fStdOut)
151 {
152 if (rcExit == RTEXITCODE_SUCCESS)
153 {
154 if (!pOpts->fKeep)
155 {
156 rc = RTFileDelete(pOpts->pszInput);
157 if (RT_FAILURE(rc))
158 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to delete '%s': %Rrc", pOpts->pszInput, rc);
159 }
160 }
161 else
162 {
163 rc = RTFileDelete(pOpts->szOutput);
164 if (RT_FAILURE(rc))
165 RTMsgError("Failed to delete '%s': %Rrc", pOpts->szOutput, rc);
166 }
167 }
168
169 return rcExit;
170}
171
172
173/**
174 * Compresses one stream to another.
175 *
176 * @returns Exit code.
177 * @param phVfsSrc The input stream. Set to NIL if closed.
178 * @param pOpts The options.
179 * @param phVfsDst The output stream. Set to NIL if closed.
180 */
181static RTEXITCODE gzipCompressFile(PRTVFSIOSTREAM phVfsSrc, PCRTGZIPCMDOPTS pOpts, PRTVFSIOSTREAM phVfsDst)
182{
183 /*
184 * Attach the ompressor to the output stream.
185 */
186 RTVFSIOSTREAM hVfsGzip;
187 int rc = RTZipGzipCompressIoStream(*phVfsDst, 0 /*fFlags*/, pOpts->uLevel, &hVfsGzip);
188 if (RT_FAILURE(rc))
189 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTZipGzipCompressIoStream failed: %Rrc", rc);
190
191 uint32_t cRefs = RTVfsIoStrmRelease(*phVfsDst);
192 Assert(cRefs > 0); RT_NOREF_PV(cRefs);
193 *phVfsDst = hVfsGzip;
194
195 return gzipPushFlushAndClose(phVfsSrc, pOpts, phVfsDst);
196}
197
198
199/**
200 * Attach a decompressor to the given source stream, replacing and releasing the
201 * input handle with the decompressor.
202 *
203 * @returns Exit code.
204 * @param phVfsSrc The input stream. Replaced on success.
205 */
206static RTEXITCODE gzipSetupDecompressor(PRTVFSIOSTREAM phVfsSrc)
207{
208 /*
209 * Attach the decompressor to the input stream.
210 */
211 uint32_t fFlags = 0;
212 fFlags |= RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR;
213 RTVFSIOSTREAM hVfsGunzip;
214 int rc = RTZipGzipDecompressIoStream(*phVfsSrc, fFlags, &hVfsGunzip);
215 if (RT_FAILURE(rc))
216 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTZipGzipDecompressIoStream failed: %Rrc", rc);
217
218 uint32_t cRefs = RTVfsIoStrmRelease(*phVfsSrc);
219 Assert(cRefs > 0); RT_NOREF_PV(cRefs);
220 *phVfsSrc = hVfsGunzip;
221
222#if 0
223 /* This is a good place for testing stuff. */
224 rc = RTVfsCreateReadAheadForIoStream(*phVfsSrc, 0, 16, _4K+1, &hVfsGunzip);
225 AssertRC(rc);
226 if (RT_SUCCESS(rc))
227 {
228 uint32_t cRefs = RTVfsIoStrmRelease(*phVfsSrc);
229 Assert(cRefs > 0);
230 *phVfsSrc = hVfsGunzip;
231 }
232#endif
233
234 return RTEXITCODE_SUCCESS;
235}
236
237
238/**
239 * Decompresses one stream to another.
240 *
241 * @returns Exit code.
242 * @param phVfsSrc The input stream. Set to NIL if closed.
243 * @param pOpts The options.
244 * @param phVfsDst The output stream. Set to NIL if closed.
245 */
246static RTEXITCODE gzipDecompressFile(PRTVFSIOSTREAM phVfsSrc, PCRTGZIPCMDOPTS pOpts, PRTVFSIOSTREAM phVfsDst)
247{
248 RTEXITCODE rcExit = gzipSetupDecompressor(phVfsSrc);
249 if (rcExit == RTEXITCODE_SUCCESS)
250 rcExit = gzipPushFlushAndClose(phVfsSrc, pOpts, phVfsDst);
251 return rcExit;
252}
253
254
255/**
256 * For testing the archive (todo).
257 *
258 * @returns Exit code.
259 * @param phVfsSrc The input stream. Set to NIL if closed.
260 * @param pOpts The options.
261 */
262static RTEXITCODE gzipTestFile(PRTVFSIOSTREAM phVfsSrc, PCRTGZIPCMDOPTS pOpts)
263{
264 RT_NOREF_PV(pOpts);
265
266 /*
267 * Read the whole stream.
268 */
269 RTEXITCODE rcExit = gzipSetupDecompressor(phVfsSrc);
270 if (rcExit == RTEXITCODE_SUCCESS)
271 {
272 for (;;)
273 {
274 uint8_t abBuf[_64K];
275 size_t cbRead;
276 int rc = RTVfsIoStrmRead(*phVfsSrc, abBuf, sizeof(abBuf), true /*fBlocking*/, &cbRead);
277 if (RT_FAILURE(rc))
278 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmRead failed: %Rrc", rc);
279 if (rc == VINF_EOF && cbRead == 0)
280 return RTEXITCODE_SUCCESS;
281 }
282 }
283 return rcExit;
284}
285
286
287static RTEXITCODE gzipListFile(PRTVFSIOSTREAM phVfsSrc, PCRTGZIPCMDOPTS pOpts)
288{
289 RT_NOREF2(phVfsSrc, pOpts);
290 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Listing has not been implemented");
291}
292
293
294/**
295 * Opens the output file.
296 *
297 * @returns Command exit, error messages written using RTMsg*.
298 *
299 * @param pszFile The input filename.
300 * @param pOpts The options, szOutput will be filled in by this
301 * function on success.
302 * @param phVfsIos Where to return the output stream handle.
303 *
304 * @remarks This is actually not quite the way we need to do things.
305 *
306 * First of all, we need a GZIP file system stream for a real GZIP
307 * implementation, since there may be more than one file in the gzipped
308 * file.
309 *
310 * Second, we need to open the output files as we encounter files in the input
311 * file system stream. The gzip format contains timestamp and usually a
312 * filename, the default is to use this name (see the --no-name
313 * option).
314 */
315static RTEXITCODE gzipOpenOutput(const char *pszFile, PRTGZIPCMDOPTS pOpts, PRTVFSIOSTREAM phVfsIos)
316{
317 int rc;
318 if (!strcmp(pszFile, "-") || pOpts->fStdOut)
319 {
320 strcpy(pOpts->szOutput, "-");
321
322 if ( !pOpts->fForce
323 && !pOpts->fDecompress
324 && gzipIsStdHandleATty(RTHANDLESTD_OUTPUT))
325 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
326 "Yeah, right. I'm not writing any compressed data to the terminal without --force.\n");
327
328 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT,
329 RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
330 true /*fLeaveOpen*/,
331 phVfsIos);
332 if (RT_FAILURE(rc))
333 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening standard output: %Rrc", rc);
334 }
335 else
336 {
337 Assert(!RTVfsChainIsSpec(pszFile));
338
339 /* Construct an output filename. */
340 rc = RTStrCopy(pOpts->szOutput, sizeof(pOpts->szOutput), pszFile);
341 if (RT_FAILURE(rc))
342 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error constructing output filename: %Rrc", rc);
343 if (pOpts->fDecompress)
344 {
345 /** @todo take filename from archive? */
346 size_t cchSuff = strlen(pOpts->pszSuff); Assert(cchSuff > 0);
347 size_t cch = strlen(pOpts->szOutput);
348 if ( cch <= cchSuff
349 || strcmp(&pOpts->szOutput[cch - cchSuff], pOpts->pszSuff))
350 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Input file does not end with: '%s'", pOpts->pszSuff);
351 pOpts->szOutput[cch - cchSuff] = '\0';
352 if (!RTPathFilename(pOpts->szOutput))
353 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error constructing output filename: Input file name is all suffix.");
354 }
355 else
356 {
357 rc = RTStrCat(pOpts->szOutput, sizeof(pOpts->szOutput), pOpts->pszSuff);
358 if (RT_FAILURE(rc))
359 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error constructing output filename: %Rrc", rc);
360 }
361
362 /* Open the output file. */
363 uint32_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
364 if (pOpts->fForce)
365 fOpen |= RTFILE_O_CREATE_REPLACE;
366 else
367 fOpen |= RTFILE_O_CREATE;
368 rc = RTVfsIoStrmOpenNormal(pOpts->szOutput, fOpen, phVfsIos);
369 if (RT_FAILURE(rc))
370 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening output file '%s': %Rrc", pOpts->szOutput, rc);
371 }
372
373 return RTEXITCODE_SUCCESS;
374}
375
376
377/**
378 * Opens the input file.
379 *
380 * @returns Command exit, error messages written using RTMsg*.
381 *
382 * @param pszFile The input filename.
383 * @param pOpts The options, szOutput will be filled in by this
384 * function on success.
385 * @param phVfsIos Where to return the input stream handle.
386 */
387static RTEXITCODE gzipOpenInput(const char *pszFile, PRTGZIPCMDOPTS pOpts, PRTVFSIOSTREAM phVfsIos)
388{
389 int rc;
390
391 pOpts->pszInput = pszFile;
392 if (!strcmp(pszFile, "-"))
393 {
394 if ( !pOpts->fForce
395 && pOpts->fDecompress
396 && gzipIsStdHandleATty(RTHANDLESTD_OUTPUT))
397 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
398 "Yeah, right. I'm not reading any compressed data from the terminal without --force.\n");
399
400 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_INPUT,
401 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
402 true /*fLeaveOpen*/,
403 phVfsIos);
404 if (RT_FAILURE(rc))
405 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening standard input: %Rrc", rc);
406 }
407 else
408 {
409 uint32_t offError = 0;
410 RTERRINFOSTATIC ErrInfo;
411 rc = RTVfsChainOpenIoStream(pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE,
412 phVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
413 if (RT_FAILURE(rc))
414 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pszFile, rc, offError, &ErrInfo.Core);
415 }
416
417 return RTEXITCODE_SUCCESS;
418
419}
420
421
422/**
423 * A mini GZIP program.
424 *
425 * @returns Program exit code.
426 *
427 * @param cArgs The number of arguments.
428 * @param papszArgs The argument vector. (Note that this may be
429 * reordered, so the memory must be writable.)
430 */
431RTDECL(RTEXITCODE) RTZipGzipCmd(unsigned cArgs, char **papszArgs)
432{
433
434 /*
435 * Parse the command line.
436 */
437 static const RTGETOPTDEF s_aOptions[] =
438 {
439 { "--ascii", 'a', RTGETOPT_REQ_NOTHING },
440 { "--stdout", 'c', RTGETOPT_REQ_NOTHING },
441 { "--to-stdout", 'c', RTGETOPT_REQ_NOTHING },
442 { "--decompress", 'd', RTGETOPT_REQ_NOTHING },
443 { "--uncompress", 'd', RTGETOPT_REQ_NOTHING },
444 { "--force", 'f', RTGETOPT_REQ_NOTHING },
445 { "--keep", 'k', RTGETOPT_REQ_NOTHING },
446 { "--list", 'l', RTGETOPT_REQ_NOTHING },
447 { "--no-name", 'n', RTGETOPT_REQ_NOTHING },
448 { "--name", 'N', RTGETOPT_REQ_NOTHING },
449 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
450 { "--recursive", 'r', RTGETOPT_REQ_NOTHING },
451 { "--suffix", 'S', RTGETOPT_REQ_STRING },
452 { "--test", 't', RTGETOPT_REQ_NOTHING },
453 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
454 { "--fast", '1', RTGETOPT_REQ_NOTHING },
455 { "-1", '1', RTGETOPT_REQ_NOTHING },
456 { "-2", '2', RTGETOPT_REQ_NOTHING },
457 { "-3", '3', RTGETOPT_REQ_NOTHING },
458 { "-4", '4', RTGETOPT_REQ_NOTHING },
459 { "-5", '5', RTGETOPT_REQ_NOTHING },
460 { "-6", '6', RTGETOPT_REQ_NOTHING },
461 { "-7", '7', RTGETOPT_REQ_NOTHING },
462 { "-8", '8', RTGETOPT_REQ_NOTHING },
463 { "-9", '9', RTGETOPT_REQ_NOTHING },
464 { "--best", '9', RTGETOPT_REQ_NOTHING }
465 };
466
467 RTGZIPCMDOPTS Opts;
468 Opts.fAscii = false;
469 Opts.fStdOut = false;
470 Opts.fDecompress = false;
471 Opts.fForce = false;
472 Opts.fKeep = false;
473 Opts.fList = false;
474 Opts.fName = true;
475 Opts.fQuiet = false;
476 Opts.fRecursive = false;
477 Opts.pszSuff = ".gz";
478 Opts.fTest = false;
479 Opts.uLevel = 6;
480
481 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
482 unsigned cProcessed = 0;
483 //RTVFSIOSTREAM hVfsStdOut= NIL_RTVFSIOSTREAM;
484
485 RTGETOPTSTATE GetState;
486 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
487 RTGETOPTINIT_FLAGS_OPTS_FIRST);
488 if (RT_FAILURE(rc))
489 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
490
491 for (;;)
492 {
493 RTGETOPTUNION ValueUnion;
494 int chOpt = RTGetOpt(&GetState, &ValueUnion);
495 switch (chOpt)
496 {
497 case 0:
498 /*
499 * If we've processed any files we're done. Otherwise take
500 * input from stdin and write the output to stdout.
501 */
502 if (cProcessed > 0)
503 return rcExit;
504 ValueUnion.psz = "-";
505 Opts.fStdOut = true;
506 RT_FALL_THRU();
507 case VINF_GETOPT_NOT_OPTION:
508 {
509 if (!*Opts.pszSuff && !Opts.fStdOut)
510 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --suffix option specified an empty string");
511 if (!Opts.fStdOut && RTVfsChainIsSpec(ValueUnion.psz))
512 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Must use standard out with VFS chain specifications");
513 if ( Opts.fName
514 && !Opts.fList
515 && !Opts.fTest
516 && !Opts.fDecompress)
517 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --name option has not yet been implemented. Use --no-name.");
518 if (Opts.fAscii)
519 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --ascii option has not yet been implemented.");
520 if (Opts.fRecursive)
521 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --recursive option has not yet been implemented.");
522
523 /* Open the input file. */
524 RTVFSIOSTREAM hVfsSrc;
525 RTEXITCODE rcExit2 = gzipOpenInput(ValueUnion.psz, &Opts, &hVfsSrc);
526 if (rcExit2 == RTEXITCODE_SUCCESS)
527 {
528 if (Opts.fList)
529 rcExit2 = gzipListFile(&hVfsSrc, &Opts);
530 else if (Opts.fTest)
531 rcExit2 = gzipTestFile(&hVfsSrc, &Opts);
532 else
533 {
534 RTVFSIOSTREAM hVfsDst;
535 rcExit2 = gzipOpenOutput(ValueUnion.psz, &Opts, &hVfsDst);
536 if (rcExit2 == RTEXITCODE_SUCCESS)
537 {
538 if (Opts.fDecompress)
539 rcExit2 = gzipDecompressFile(&hVfsSrc, &Opts, &hVfsDst);
540 else
541 rcExit2 = gzipCompressFile(&hVfsSrc, &Opts, &hVfsDst);
542 RTVfsIoStrmRelease(hVfsDst);
543 }
544 }
545 RTVfsIoStrmRelease(hVfsSrc);
546 }
547 if (rcExit2 != RTEXITCODE_SUCCESS)
548 rcExit = rcExit2;
549
550 cProcessed++;
551 break;
552 }
553
554 case 'a': Opts.fAscii = true; break;
555 case 'c':
556 Opts.fStdOut = true;
557 Opts.fKeep = true;
558 break;
559 case 'd': Opts.fDecompress = true; break;
560 case 'f': Opts.fForce = true; break;
561 case 'k': Opts.fKeep = true; break;
562 case 'l': Opts.fList = true; break;
563 case 'n': Opts.fName = false; break;
564 case 'N': Opts.fName = true; break;
565 case 'q': Opts.fQuiet = true; break;
566 case 'r': Opts.fRecursive = true; break;
567 case 'S': Opts.pszSuff = ValueUnion.psz; break;
568 case 't': Opts.fTest = true; break;
569 case 'v': Opts.fQuiet = false; break;
570 case '1': Opts.uLevel = 1; break;
571 case '2': Opts.uLevel = 2; break;
572 case '3': Opts.uLevel = 3; break;
573 case '4': Opts.uLevel = 4; break;
574 case '5': Opts.uLevel = 5; break;
575 case '6': Opts.uLevel = 6; break;
576 case '7': Opts.uLevel = 7; break;
577 case '8': Opts.uLevel = 8; break;
578 case '9': Opts.uLevel = 9; break;
579
580 case 'h':
581 RTPrintf("Usage: to be written\nOption dump:\n");
582 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
583 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
584 return RTEXITCODE_SUCCESS;
585
586 case 'V':
587 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
588 return RTEXITCODE_SUCCESS;
589
590 default:
591 return RTGetOptPrintError(chOpt, &ValueUnion);
592 }
593 }
594}
595
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