VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/zip/tarcmd.cpp@ 34179

Last change on this file since 34179 was 34179, checked in by vboxsync, 14 years ago

iprt/tarvfs: Rewrote the tar parser to deal with header sequences used by solaris, gnu and pax while being offline yesterday. Only GNU long link and long names are supported, but adding pax and solaris support is possible. This also resolves some of the ustar<space><space> vs. ustart<null>00 problems (the latter was not readable previously).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.8 KB
Line 
1/* $Id: tarcmd.cpp 34179 2010-11-18 15:49:17Z vboxsync $ */
2/** @file
3 * IPRT - TAR Command.
4 */
5
6/*
7 * Copyright (C) 2010 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/asm.h>
34#include <iprt/buildconfig.h>
35#include <iprt/ctype.h>
36#include <iprt/file.h>
37#include <iprt/getopt.h>
38#include <iprt/initterm.h>
39#include <iprt/mem.h>
40#include <iprt/message.h>
41#include <iprt/param.h>
42#include <iprt/stream.h>
43#include <iprt/string.h>
44#include <iprt/vfs.h>
45
46
47/*******************************************************************************
48* Defined Constants And Macros *
49*******************************************************************************/
50#define RTZIPTARCMD_OPT_DELETE 1000
51#define RTZIPTARCMD_OPT_OWNER 1001
52#define RTZIPTARCMD_OPT_GROUP 1002
53#define RTZIPTARCMD_OPT_UTC 1003
54#define RTZIPTARCMD_OPT_PREFIX 1004
55
56
57/*******************************************************************************
58* Structures and Typedefs *
59*******************************************************************************/
60/**
61 * IPT TAR option structure.
62 */
63typedef struct RTZIPTARCMDOPS
64{
65 /** The operation (Acdrtux or RTZIPTARCMD_OPT_DELETE). */
66 int iOperation;
67 /** The long operation option name. */
68 const char *pszOperation;
69
70 /** The directory to change into when packing and unpacking. */
71 const char *pszDirectory;
72 /** The tar file name. */
73 const char *pszFile;
74 /** Whether we're verbose or quiet. */
75 bool fVerbose;
76 /** Whether to preserve permissions when restoring. */
77 bool fPreservePermissions;
78 /** The compressor/decompressor method to employ (0, z or j). */
79 char chZipper;
80
81 /** The owner to set. */
82 const char *pszOwner;
83 /** The owner ID to set when unpacking if pszOwner is not NULL. */
84 RTUID uidOwner;
85 /** The group to set. */
86 const char *pszGroup;
87 /** The group ID to set when unpacking if pszGroup is not NULL. */
88 RTGID gidGroup;
89 /** Display the modification times in UTC instead of local time. */
90 bool fDisplayUtc;
91
92 /** What to prefix all names with when creating, adding, whatever. */
93 const char *pszPrefix;
94
95 /** The number of files(, directories or whatever) specified. */
96 uint32_t cFiles;
97 /** Array of files(, directories or whatever).
98 * Terminated by a NULL entry. */
99 const char * const *papszFiles;
100} RTZIPTARCMDOPS;
101/** Pointer to the IPRT tar options. */
102typedef RTZIPTARCMDOPS *PRTZIPTARCMDOPS;
103
104
105/**
106 * Checks if @a pszName is a member of @a papszNames, optionally returning the
107 * index.
108 *
109 * @returns true if the name is in the list, otherwise false.
110 * @param pszName The name to find.
111 * @param papszNames The array of names.
112 * @param piName Where to optionally return the array index.
113 */
114static bool rtZipTarCmdIsNameInArray(const char *pszName, const char * const *papszNames, uint32_t *piName)
115{
116 for (uint32_t iName = 0; papszNames[iName]; iName)
117 if (!strcmp(papszNames[iName], pszName))
118 {
119 if (piName)
120 *piName = iName;
121 return true;
122 }
123 return false;
124}
125
126
127/**
128 * Opens the input archive specified by the options.
129 *
130 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
131 * @param pOpts The options.
132 * @param phVfsFss Where to return the TAR filesystem stream handle.
133 */
134static RTEXITCODE rtZipTarCmdOpenInputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTREAM phVfsFss)
135{
136 int rc;
137
138 /*
139 * Open the input file.
140 */
141 RTVFSIOSTREAM hVfsIos;
142 if ( pOpts->pszFile
143 && strcmp(pOpts->pszFile, "-") != 0)
144 {
145 const char *pszError;
146 rc = RTVfsChainOpenIoStream(pOpts->pszFile,
147 RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
148 &hVfsIos,
149 &pszError);
150 if (RT_FAILURE(rc))
151 {
152 if (pszError && *pszError)
153 return RTMsgErrorExit(RTEXITCODE_FAILURE,
154 "RTVfsChainOpenIoStream failed with rc=%Rrc:\n"
155 " '%s'\n",
156 " %*s^\n",
157 rc, pOpts->pszFile, pszError - pOpts->pszFile, "");
158 return RTMsgErrorExit(RTEXITCODE_FAILURE,
159 "Failed with %Rrc opening the input archive '%s'", rc, pOpts->pszFile);
160 }
161 }
162 else
163 {
164 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_INPUT,
165 RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
166 true /*fLeaveOpen*/,
167 &hVfsIos);
168 if (RT_FAILURE(rc))
169 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard in for reading: %Rrc", rc);
170 }
171
172 /*
173 * Pass it thru a decompressor?
174 */
175 RTVFSIOSTREAM hVfsIosDecomp = NIL_RTVFSIOSTREAM;
176 switch (pOpts->chZipper)
177 {
178 /* no */
179 case '\0':
180 rc = VINF_SUCCESS;
181 break;
182
183 /* gunzip */
184 case 'z':
185 rc = RTZipGzipDecompressIoStream(hVfsIos, 0 /*fFlags*/, &hVfsIosDecomp);
186 if (RT_FAILURE(rc))
187 RTMsgError("Failed to open gzip decompressor: %Rrc", rc);
188 break;
189
190 /* bunzip2 */
191 case 'j':
192 rc = VERR_NOT_SUPPORTED;
193 RTMsgError("bzip2 is not supported by this build");
194 break;
195
196 /* bug */
197 default:
198 rc = VERR_INTERNAL_ERROR_2;
199 RTMsgError("unknown decompression method '%c'", pOpts->chZipper);
200 break;
201 }
202 if (RT_FAILURE(rc))
203 {
204 RTVfsIoStrmRelease(hVfsIos);
205 return RTEXITCODE_FAILURE;
206 }
207
208 if (hVfsIosDecomp != NIL_RTVFSIOSTREAM)
209 {
210 RTVfsIoStrmRelease(hVfsIos);
211 hVfsIos = hVfsIosDecomp;
212 hVfsIosDecomp = NIL_RTVFSIOSTREAM;
213 }
214
215 /*
216 * Open the tar filesystem stream.
217 */
218 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
219 RTVfsIoStrmRelease(hVfsIos);
220 if (RT_FAILURE(rc))
221 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
222
223 return RTEXITCODE_SUCCESS;
224}
225
226
227/**
228 * Display a tar entry in the verbose form.
229 *
230 * @returns rcExit or RTEXITCODE_FAILURE.
231 * @param rcExit The current exit code.
232 * @param hVfsObj The tar object to display
233 * @param pszName The name.
234 * @param pOpts The tar options.
235 */
236static RTEXITCODE rtZipTarCmdDisplayEntryVerbose(RTEXITCODE rcExit, RTVFSOBJ hVfsObj, const char *pszName,
237 PRTZIPTARCMDOPS pOpts)
238{
239 /*
240 * Query all the information.
241 */
242 RTFSOBJINFO UnixInfo;
243 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
244 if (RT_FAILURE(rc))
245 {
246 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
247 RT_ZERO(UnixInfo);
248 }
249
250 RTFSOBJINFO Owner;
251 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
252 if (RT_FAILURE(rc))
253 {
254 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
255 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
256 rc, pszName);
257 RT_ZERO(Owner);
258 }
259
260 RTFSOBJINFO Group;
261 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
262 if (RT_FAILURE(rc))
263 {
264 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
265 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
266 rc, pszName);
267 RT_ZERO(Group);
268 }
269
270 const char *pszLinkType = NULL;
271 char szTarget[RTPATH_MAX];
272 szTarget[0] = '\0';
273 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
274 if (hVfsSymlink != NIL_RTVFSSYMLINK)
275 {
276 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
277 if (RT_FAILURE(rc))
278 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsSymlinkRead returned %Rrc on '%s'", rc, pszName);
279 RTVfsSymlinkRelease(hVfsSymlink);
280 pszLinkType = RTFS_IS_SYMLINK(UnixInfo.Attr.fMode) ? "->" : "link to";
281 }
282 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
283 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
284
285 /*
286 * Translate the mode mask.
287 */
288 char szMode[16];
289 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
290 {
291 case RTFS_TYPE_FIFO: szMode[0] = 'f'; break;
292 case RTFS_TYPE_DEV_CHAR: szMode[0] = 'c'; break;
293 case RTFS_TYPE_DIRECTORY: szMode[0] = 'd'; break;
294 case RTFS_TYPE_DEV_BLOCK: szMode[0] = 'b'; break;
295 case RTFS_TYPE_FILE: szMode[0] = '-'; break;
296 case RTFS_TYPE_SYMLINK: szMode[0] = 'l'; break;
297 case RTFS_TYPE_SOCKET: szMode[0] = 's'; break;
298 case RTFS_TYPE_WHITEOUT: szMode[0] = 'w'; break;
299 default: szMode[0] = '?'; break;
300 }
301 if (pszLinkType && szMode[0] != 's')
302 szMode[0] = 'h';
303
304 szMode[1] = UnixInfo.Attr.fMode & RTFS_UNIX_IRUSR ? 'r' : '-';
305 szMode[2] = UnixInfo.Attr.fMode & RTFS_UNIX_IWUSR ? 'w' : '-';
306 szMode[3] = UnixInfo.Attr.fMode & RTFS_UNIX_IXUSR ? 'x' : '-';
307
308 szMode[4] = UnixInfo.Attr.fMode & RTFS_UNIX_IRGRP ? 'r' : '-';
309 szMode[5] = UnixInfo.Attr.fMode & RTFS_UNIX_IWGRP ? 'w' : '-';
310 szMode[6] = UnixInfo.Attr.fMode & RTFS_UNIX_IXGRP ? 'x' : '-';
311
312 szMode[7] = UnixInfo.Attr.fMode & RTFS_UNIX_IROTH ? 'r' : '-';
313 szMode[8] = UnixInfo.Attr.fMode & RTFS_UNIX_IWOTH ? 'w' : '-';
314 szMode[9] = UnixInfo.Attr.fMode & RTFS_UNIX_IXOTH ? 'x' : '-';
315 szMode[10] = '\0';
316
317 /** @todo sticky and set-uid/gid bits. */
318
319 /*
320 * Make sure we've got valid owner and group strings.
321 */
322 if (!Owner.Attr.u.UnixGroup.szName[0])
323 RTStrPrintf(Owner.Attr.u.UnixOwner.szName, sizeof(Owner.Attr.u.UnixOwner.szName),
324 "%u", UnixInfo.Attr.u.Unix.uid);
325
326 if (!Group.Attr.u.UnixOwner.szName[0])
327 RTStrPrintf(Group.Attr.u.UnixGroup.szName, sizeof(Group.Attr.u.UnixGroup.szName),
328 "%u", UnixInfo.Attr.u.Unix.gid);
329
330 /*
331 * Format the modification time.
332 */
333 char szModTime[32];
334 RTTIME ModTime;
335 PRTTIME pTime;
336 if (!pOpts->fDisplayUtc)
337 pTime = RTTimeLocalExplode(&ModTime, &UnixInfo.ModificationTime);
338 else
339 pTime = RTTimeExplode(&ModTime, &UnixInfo.ModificationTime);
340 if (!pTime)
341 RT_ZERO(ModTime);
342 RTStrPrintf(szModTime, sizeof(szModTime), "%04d-%02u-%02u %02u:%02u",
343 ModTime.i32Year, ModTime.u8Month, ModTime.u8MonthDay, ModTime.u8Hour, ModTime.u8Minute);
344
345 /*
346 * Format the size and figure how much space is needed between the
347 * user/group and the size.
348 */
349 char szSize[64];
350 size_t cchSize;
351 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
352 {
353 case RTFS_TYPE_DEV_CHAR:
354 case RTFS_TYPE_DEV_BLOCK:
355 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%u,%u",
356 RTDEV_MAJOR(UnixInfo.Attr.u.Unix.Device), RTDEV_MINOR(UnixInfo.Attr.u.Unix.Device));
357 break;
358 default:
359 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%RU64", UnixInfo.cbObject);
360 break;
361 }
362
363 size_t cchUserGroup = strlen(Owner.Attr.u.UnixOwner.szName)
364 + 1
365 + strlen(Group.Attr.u.UnixGroup.szName);
366 ssize_t cchPad = cchUserGroup + cchSize + 1 < 19
367 ? 19 - (cchUserGroup + cchSize + 1)
368 : 0;
369
370 /*
371 * Go to press.
372 */
373 if (pszLinkType)
374 RTPrintf("%s %s/%s%*s %s %s %s %s %s\n",
375 szMode,
376 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
377 cchPad, "",
378 szSize,
379 szModTime,
380 pszName,
381 pszLinkType,
382 szTarget);
383 else
384 RTPrintf("%s %s/%s%*s %s %s %s\n",
385 szMode,
386 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
387 cchPad, "",
388 szSize,
389 szModTime,
390 pszName);
391
392 return rcExit;
393}
394
395/**
396 * Implements the -t/--list operation.
397 *
398 * @returns The appropriate exit code.
399 * @param pOpts The tar options.
400 */
401static RTEXITCODE rtZipTarCmdList(PRTZIPTARCMDOPS pOpts)
402{
403 /*
404 * Allocate a bitmap to go with the file list. This will be used to
405 * indicate which files we've processed and which not.
406 */
407 uint32_t *pbmFound = NULL;
408 if (pOpts->cFiles)
409 {
410 pbmFound = (uint32_t *)RTMemAllocZ(((pOpts->cFiles + 31) / 32) * sizeof(uint32_t));
411 if (!pbmFound)
412 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to allocate the found-file-bitmap");
413 }
414
415
416 /*
417 * Open the input archive.
418 */
419 RTVFSFSSTREAM hVfsFssIn;
420 RTEXITCODE rcExit = rtZipTarCmdOpenInputArchive(pOpts, &hVfsFssIn);
421 if (rcExit == RTEXITCODE_SUCCESS)
422 {
423 /*
424 * Process the stream.
425 */
426 for (;;)
427 {
428 /*
429 * Retrive the next object.
430 */
431 char *pszName;
432 RTVFSOBJ hVfsObj;
433 int rc = RTVfsFsStrmNext(hVfsFssIn, &pszName, NULL, &hVfsObj);
434 if (RT_FAILURE(rc))
435 {
436 if (rc != VERR_EOF)
437 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext returned %Rrc", rc);
438 break;
439 }
440
441 /*
442 * Should we display this entry?
443 */
444 uint32_t iFile = UINT32_MAX;
445 if ( !pOpts->cFiles
446 || rtZipTarCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile) )
447 {
448 if (pbmFound)
449 ASMBitSet(pbmFound, iFile);
450
451 if (!pOpts->fVerbose)
452 RTPrintf("%s\n", pszName);
453 else
454 rcExit = rtZipTarCmdDisplayEntryVerbose(rcExit, hVfsObj, pszName, pOpts);
455 }
456
457 /*
458 * Release the current object and string.
459 */
460 RTVfsObjRelease(hVfsObj);
461 RTStrFree(pszName);
462 }
463
464 /*
465 * Complain about any files we didn't find.
466 */
467 for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
468 if (!ASMBitTest(pbmFound, iFile))
469 {
470 RTMsgError("%s: Was not found in the archive", pOpts->papszFiles[iFile]);
471 rcExit = RTEXITCODE_FAILURE;
472 }
473 }
474 RTMemFree(pbmFound);
475 return rcExit;
476}
477
478
479
480RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
481{
482 /*
483 * Parse the command line.
484 *
485 * N.B. This is less flexible that your regular tar program in that it
486 * requires the operation to be specified as an option. On the other
487 * hand, you can specify it where ever you like in the command line.
488 */
489 static const RTGETOPTDEF s_aOptions[] =
490 {
491 /* operations */
492 { "--concatenate", 'A', RTGETOPT_REQ_NOTHING },
493 { "--catenate", 'A', RTGETOPT_REQ_NOTHING },
494 { "--create", 'c', RTGETOPT_REQ_NOTHING },
495 { "--diff", 'd', RTGETOPT_REQ_NOTHING },
496 { "--compare", 'd', RTGETOPT_REQ_NOTHING },
497 { "--append", 'r', RTGETOPT_REQ_NOTHING },
498 { "--list", 't', RTGETOPT_REQ_NOTHING },
499 { "--update", 'u', RTGETOPT_REQ_NOTHING },
500 { "--extract", 'x', RTGETOPT_REQ_NOTHING },
501 { "--get", 'x', RTGETOPT_REQ_NOTHING },
502 { "--delete", RTZIPTARCMD_OPT_DELETE, RTGETOPT_REQ_NOTHING },
503
504 /* basic options */
505 { "--directory", 'C', RTGETOPT_REQ_STRING },
506 { "--file", 'f', RTGETOPT_REQ_STRING },
507 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
508 { "--preserve-permissions", 'p', RTGETOPT_REQ_NOTHING },
509 { "--bzip2", 'j', RTGETOPT_REQ_NOTHING },
510 { "--gzip", 'z', RTGETOPT_REQ_NOTHING },
511 { "--gunzip", 'z', RTGETOPT_REQ_NOTHING },
512 { "--ungzip", 'z', RTGETOPT_REQ_NOTHING },
513
514 /* other options. */
515 { "--owner", RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
516 { "--group", RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
517 { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
518
519 /* IPRT extensions */
520 { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
521 };
522
523 RTGETOPTSTATE GetState;
524 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
525 RTGETOPTINIT_FLAGS_OPTS_FIRST);
526 if (RT_FAILURE(rc))
527 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
528
529 RTZIPTARCMDOPS Opts;
530 RT_ZERO(Opts); /* nice defaults :-) */
531
532 RTGETOPTUNION ValueUnion;
533 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
534 && rc != VINF_GETOPT_NOT_OPTION)
535 {
536 switch (rc)
537 {
538 /* operations */
539 case 'A':
540 case 'c':
541 case 'd':
542 case 'r':
543 case 't':
544 case 'u':
545 case 'x':
546 case RTZIPTARCMD_OPT_DELETE:
547 if (Opts.iOperation)
548 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Conflicting tar operation (%s already set, now %s)",
549 Opts.pszOperation, ValueUnion.pDef->pszLong);
550 Opts.iOperation = rc;
551 Opts.pszOperation = ValueUnion.pDef->pszLong;
552 break;
553
554 /* basic options */
555 case 'C':
556 if (Opts.pszDirectory)
557 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -C/--directory once");
558 Opts.pszDirectory = ValueUnion.psz;
559 break;
560
561 case 'f':
562 if (Opts.pszFile)
563 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -f/--file once");
564 Opts.pszFile = ValueUnion.psz;
565 break;
566
567 case 'v':
568 Opts.fVerbose = true;
569 break;
570
571 case 'p':
572 Opts.fPreservePermissions = true;
573 break;
574
575 case 'j':
576 case 'z':
577 if (Opts.chZipper)
578 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify one compressor / decompressor");
579 Opts.chZipper = rc;
580 break;
581
582 case RTZIPTARCMD_OPT_OWNER:
583 if (Opts.pszOwner)
584 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --owner once");
585 Opts.pszOwner = ValueUnion.psz;
586 break;
587
588 case RTZIPTARCMD_OPT_GROUP:
589 if (Opts.pszGroup)
590 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --group once");
591 Opts.pszGroup = ValueUnion.psz;
592 break;
593
594 case RTZIPTARCMD_OPT_UTC:
595 Opts.fDisplayUtc = true;
596 break;
597
598 /* iprt extensions */
599 case RTZIPTARCMD_OPT_PREFIX:
600 if (Opts.pszPrefix)
601 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --prefix once");
602 Opts.pszPrefix = ValueUnion.psz;
603 break;
604
605 case 'h':
606 RTPrintf("Usage: to be written\nOption dump:\n");
607 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
608 if (RT_C_IS_PRINT(s_aOptions[i].iShort))
609 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
610 else
611 RTPrintf(" %s\n", s_aOptions[i].pszLong);
612 return RTEXITCODE_SUCCESS;
613
614 case 'V':
615 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
616 return RTEXITCODE_SUCCESS;
617
618 default:
619 return RTGetOptPrintError(rc, &ValueUnion);
620 }
621 }
622
623 if (rc == VINF_GETOPT_NOT_OPTION)
624 {
625 /* this is kind of ugly. */
626 Assert((unsigned)GetState.iNext - 1 <= cArgs);
627 Opts.papszFiles = (const char * const *)&papszArgs[GetState.iNext - 1];
628 Opts.cFiles = cArgs - GetState.iNext + 1;
629 }
630
631 /*
632 * Post proceess the options.
633 */
634 if (Opts.iOperation == 0)
635 {
636 Opts.iOperation = 't';
637 Opts.pszOperation = "--list";
638 }
639
640 if ( Opts.iOperation == 'x'
641 && Opts.pszOwner)
642 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --owner with %s has not implemented yet", Opts.pszOperation);
643
644 if ( Opts.iOperation == 'x'
645 && Opts.pszGroup)
646 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --group with %s has not implemented yet", Opts.pszOperation);
647
648 /*
649 * Do the job.
650 */
651 switch (Opts.iOperation)
652 {
653 case 't':
654 return rtZipTarCmdList(&Opts);
655
656 case 'A':
657 case 'c':
658 case 'd':
659 case 'r':
660 case 'u':
661 case 'x':
662 case RTZIPTARCMD_OPT_DELETE:
663 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The operation %s is not implemented yet", Opts.pszOperation);
664
665 default:
666 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Internal error");
667 }
668}
669
670
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