VirtualBox

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

Last change on this file since 78306 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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