VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTGzip.cpp@ 53401

Last change on this file since 53401 was 51519, checked in by vboxsync, 11 years ago

Runtime: Comment nits.

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