]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/tar/bsdtar.c
Convert several typedefs from beeing pointers to structs to be the structs
[FreeBSD/FreeBSD.git] / usr.bin / tar / bsdtar.c
1 /*-
2  * Copyright (c) 2003-2004 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "bsdtar_platform.h"
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/stat.h>
32 #include <archive.h>
33 #include <archive_entry.h>
34 #include <dirent.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #ifdef HAVE_GETOPT_LONG
38 #include <getopt.h>
39 #else
40 struct option {
41         const char *name;
42         int has_arg;
43         int *flag;
44         int val;
45 };
46 #define no_argument 0
47 #define required_argument 1
48 #endif
49 #ifdef HAVE_LANGINFO_H
50 #include <langinfo.h>
51 #endif
52 #include <locale.h>
53 #ifdef HAVE_PATHS_H
54 #include <paths.h>
55 #endif
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <time.h>
60 #include <unistd.h>
61 #if HAVE_ZLIB_H
62 #include <zlib.h>
63 #endif
64
65 #include "bsdtar.h"
66
67 #ifdef linux
68 #define _PATH_DEFTAPE "/dev/st0"
69 #endif
70
71 #ifndef _PATH_DEFTAPE
72 #define _PATH_DEFTAPE "/dev/tape"
73 #endif
74
75 static int               bsdtar_getopt(struct bsdtar *, const char *optstring,
76     const struct option **poption);
77 static void              long_help(struct bsdtar *);
78 static void              only_mode(struct bsdtar *, const char *opt,
79                              const char *valid);
80 static char **           rewrite_argv(struct bsdtar *,
81                              int *argc, char ** src_argv,
82                              const char *optstring);
83 static void              set_mode(struct bsdtar *, char opt);
84 static void              version(void);
85
86 /*
87  * The leading '+' here forces the GNU version of getopt() (as well as
88  * both the GNU and BSD versions of getopt_long) to stop at the first
89  * non-option.  Otherwise, GNU getopt() permutes the arguments and
90  * screws up -C processing.
91  */
92 static const char *tar_opts = "+Bb:C:cF:f:HhI:jkLlmnOoPprtT:UuvW:wX:xyZz";
93
94 /*
95  * Most of these long options are deliberately not documented.  They
96  * are provided only to make life easier for people who also use GNU tar.
97  * The only long options documented in the manual page are the ones
98  * with no corresponding short option, such as --exclude, --nodump,
99  * and --fast-read.
100  *
101  * On systems that lack getopt_long, long options can be specified
102  * using -W longopt and -W longopt=value, e.g. "-W nodump" is the same
103  * as "--nodump" and "-W exclude=pattern" is the same as "--exclude
104  * pattern".  This does not rely the GNU getopt() "W;" extension, so
105  * should work correctly on any system with a POSIX-compliant getopt().
106  */
107
108 /* Fake short equivalents for long options that otherwise lack them. */
109 #define OPTION_CHECK_LINKS 3
110 #define OPTION_EXCLUDE 6
111 #define OPTION_FAST_READ 9
112 #define OPTION_FORMAT 10
113 #define OPTION_HELP 12
114 #define OPTION_INCLUDE 15
115 #define OPTION_NODUMP 18
116 #define OPTION_NO_SAME_PERMISSIONS 21
117 #define OPTION_NULL 24
118 #define OPTION_ONE_FILE_SYSTEM 27
119 #define OPTION_TOTALS 28
120 #define OPTION_VERSION 30
121
122 /*
123  * If you add anything, be very careful
124  * to keep this list properly sorted.
125  */
126 static const struct option tar_longopts[] = {
127         { "absolute-paths",     no_argument,       NULL, 'P' },
128         { "append",             no_argument,       NULL, 'r' },
129         { "block-size",         required_argument, NULL, 'b' },
130         { "bunzip2",            no_argument,       NULL, 'j' },
131         { "bzip",               no_argument,       NULL, 'j' },
132         { "bzip2",              no_argument,       NULL, 'j' },
133         { "cd",                 required_argument, NULL, 'C' },
134         { "check-links",        no_argument,       NULL, OPTION_CHECK_LINKS },
135         { "confirmation",       no_argument,       NULL, 'w' },
136         { "create",             no_argument,       NULL, 'c' },
137         { "dereference",        no_argument,       NULL, 'L' },
138         { "directory",          required_argument, NULL, 'C' },
139         { "exclude",            required_argument, NULL, OPTION_EXCLUDE },
140         { "exclude-from",       required_argument, NULL, 'X' },
141         { "extract",            no_argument,       NULL, 'x' },
142         { "fast-read",          no_argument,       NULL, OPTION_FAST_READ },
143         { "file",               required_argument, NULL, 'f' },
144         { "files-from",         required_argument, NULL, 'T' },
145         { "format",             required_argument, NULL, OPTION_FORMAT },
146         { "gunzip",             no_argument,       NULL, 'z' },
147         { "gzip",               no_argument,       NULL, 'z' },
148         { "help",               no_argument,       NULL, OPTION_HELP },
149         { "include",            required_argument, NULL, OPTION_INCLUDE },
150         { "interactive",        no_argument,       NULL, 'w' },
151         { "keep-old-files",     no_argument,       NULL, 'k' },
152         { "list",               no_argument,       NULL, 't' },
153         { "modification-time",  no_argument,       NULL, 'm' },
154         { "nodump",             no_argument,       NULL, OPTION_NODUMP },
155         { "norecurse",          no_argument,       NULL, 'n' },
156         { "no-recursion",       no_argument,       NULL, 'n' },
157         { "no-same-owner",      no_argument,       NULL, 'o' },
158         { "no-same-permissions",no_argument,       NULL, OPTION_NO_SAME_PERMISSIONS },
159         { "null",               no_argument,       NULL, OPTION_NULL },
160         { "one-file-system",    no_argument,       NULL, OPTION_ONE_FILE_SYSTEM },
161         { "preserve-permissions", no_argument,     NULL, 'p' },
162         { "read-full-blocks",   no_argument,       NULL, 'B' },
163         { "same-permissions",   no_argument,       NULL, 'p' },
164         { "to-stdout",          no_argument,       NULL, 'O' },
165         { "totals",             no_argument,       NULL, OPTION_TOTALS },
166         { "unlink",             no_argument,       NULL, 'U' },
167         { "unlink-first",       no_argument,       NULL, 'U' },
168         { "update",             no_argument,       NULL, 'u' },
169         { "verbose",            no_argument,       NULL, 'v' },
170         { "version",            no_argument,       NULL, OPTION_VERSION },
171         { NULL, 0, NULL, 0 }
172 };
173
174 int
175 main(int argc, char **argv)
176 {
177         struct bsdtar           *bsdtar, bsdtar_storage;
178         const struct option     *option;
179         int                      opt, t;
180         char                     option_o;
181         char                     possible_help_request;
182         char                     buff[16];
183
184         /*
185          * Use a pointer for consistency, but stack-allocated storage
186          * for ease of cleanup.
187          */
188         bsdtar = &bsdtar_storage;
189         memset(bsdtar, 0, sizeof(*bsdtar));
190         bsdtar->fd = -1; /* Mark as "unused" */
191         option_o = 0;
192
193         if (setlocale(LC_ALL, "") == NULL)
194                 bsdtar_warnc(bsdtar, 0, "Failed to set default locale");
195 #if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER)
196         bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd');
197 #endif
198         possible_help_request = 0;
199
200         /* Look up uid of current user for future reference */
201         bsdtar->user_uid = geteuid();
202
203         /* Default: open tape drive. */
204         bsdtar->filename = getenv("TAPE");
205         if (bsdtar->filename == NULL)
206                 bsdtar->filename = _PATH_DEFTAPE;
207
208         /* Default: preserve mod time on extract */
209         bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME;
210
211         if (bsdtar->user_uid == 0)
212                 bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
213
214         if (*argv == NULL)
215                 bsdtar->progname = "bsdtar";
216         else {
217                 bsdtar->progname = strrchr(*argv, '/');
218                 if (bsdtar->progname != NULL)
219                         bsdtar->progname++;
220                 else
221                         bsdtar->progname = *argv;
222         }
223
224         /* Rewrite traditional-style tar arguments, if used. */
225         argv = rewrite_argv(bsdtar, &argc, argv, tar_opts);
226
227         bsdtar->argv = argv;
228         bsdtar->argc = argc;
229
230         /* Process all remaining arguments now. */
231         while ((opt = bsdtar_getopt(bsdtar, tar_opts, &option)) != -1) {
232                 switch (opt) {
233                 case 'B': /* GNU tar */
234                         /* libarchive doesn't need this; just ignore it. */
235                         break;
236                 case 'b': /* SUSv2 */
237                         t = atoi(optarg);
238                         if (t <= 0 || t > 1024)
239                                 bsdtar_errc(bsdtar, 1, 0,
240                                     "Argument to -b is out of range (1..1024)");
241                         bsdtar->bytes_per_block = 512 * t;
242                         break;
243                 case 'C': /* GNU tar */
244                         set_chdir(bsdtar, optarg);
245                         break;
246                 case 'c': /* SUSv2 */
247                         set_mode(bsdtar, opt);
248                         break;
249                 case OPTION_CHECK_LINKS: /* GNU tar */
250                         bsdtar->option_warn_links = 1;
251                         break;
252                 case OPTION_EXCLUDE: /* GNU tar */
253                         if (exclude(bsdtar, optarg))
254                                 bsdtar_errc(bsdtar, 1, 0,
255                                     "Couldn't exclude %s\n", optarg);
256                         break;
257                 case OPTION_FORMAT:
258                         bsdtar->create_format = optarg;
259                         break;
260                 case 'f': /* SUSv2 */
261                         bsdtar->filename = optarg;
262                         if (strcmp(bsdtar->filename, "-") == 0)
263                                 bsdtar->filename = NULL;
264                         break;
265                 case OPTION_FAST_READ: /* GNU tar */
266                         bsdtar->option_fast_read = 1;
267                         break;
268                 case 'H': /* BSD convention */
269                         bsdtar->symlink_mode = 'H';
270                         break;
271                 case 'h': /* Linux Standards Base, gtar; synonym for -L */
272                         bsdtar->symlink_mode = 'L';
273                         /* Hack: -h by itself is the "help" command. */
274                         possible_help_request = 1;
275                         break;
276                 case OPTION_HELP:
277                         long_help(bsdtar);
278                         exit(0);
279                         break;
280                 case 'I': /* GNU tar */
281                         bsdtar->names_from_file = optarg;
282                         break;
283                 case OPTION_INCLUDE:
284                         if (include(bsdtar, optarg))
285                                 bsdtar_errc(bsdtar, 1, 0,
286                                     "Failed to add %s to inclusion list",
287                                     optarg);
288                         break;
289                 case 'j': /* GNU tar */
290 #if HAVE_LIBBZ2
291                         if (bsdtar->create_compression != '\0')
292                                 bsdtar_errc(bsdtar, 1, 0,
293                                     "Can't specify both -%c and -%c", opt,
294                                     bsdtar->create_compression);
295                         bsdtar->create_compression = opt;
296 #else
297                         bsdtar_warnc(bsdtar, 0, "-j compression not supported by this version of bsdtar");
298                         usage(bsdtar);
299 #endif
300                         break;
301                 case 'k': /* GNU tar */
302                         bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
303                         break;
304                 case 'L': /* BSD convention */
305                         bsdtar->symlink_mode = 'L';
306                         break;
307                 case 'l': /* SUSv2 and GNU conflict badly here */
308                         if (getenv("POSIXLY_CORRECT") != NULL) {
309                                 /* User has asked for POSIX/SUS behavior. */
310                                 bsdtar->option_warn_links = 1;
311                         } else {
312                                 fprintf(stderr,
313 "Error: -l has different behaviors in different tar programs.\n");
314                                 fprintf(stderr,
315 "  For the GNU behavior, use --one-file-system instead.\n");
316                                 fprintf(stderr,
317 "  For the POSIX behavior, use --check-links instead.\n");
318                                 usage(bsdtar);
319                         }
320                         break;
321                 case 'm': /* SUSv2 */
322                         bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
323                         break;
324                 case 'n': /* GNU tar */
325                         bsdtar->option_no_subdirs = 1;
326                         break;
327                 case OPTION_NODUMP: /* star */
328                         bsdtar->option_honor_nodump = 1;
329                         break;
330                 case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */
331                         /*
332                          * This is always the default in FreeBSD's
333                          * version of GNU tar; it's also the default
334                          * behavior for bsdtar, so treat the
335                          * command-line option as a no-op.
336                          */
337                         break;
338                 case OPTION_NULL: /* GNU tar */
339                         bsdtar->option_null++;
340                         break;
341                 case 'O': /* GNU tar */
342                         bsdtar->option_stdout = 1;
343                         break;
344                 case 'o': /* SUSv2 and GNU conflict here */
345                         option_o = 1; /* Record it and resolve it later. */
346                         break;
347                 case OPTION_ONE_FILE_SYSTEM: /* -l in GNU tar */
348                         bsdtar->option_dont_traverse_mounts = 1;
349                         break;
350 #if 0
351                 /*
352                  * The common BSD -P option is not necessary, since
353                  * our default is to archive symlinks, not follow
354                  * them.  This is convenient, as -P conflicts with GNU
355                  * tar anyway.
356                  */
357                 case 'P': /* BSD convention */
358                         /* Default behavior, no option necessary. */
359                         break;
360 #endif
361                 case 'P': /* GNU tar */
362                         bsdtar->option_absolute_paths = 1;
363                         break;
364                 case 'p': /* GNU tar, star */
365                         bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
366                         bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
367                         bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
368                         break;
369                 case 'r': /* SUSv2 */
370                         set_mode(bsdtar, opt);
371                         break;
372                 case 'T': /* GNU tar */
373                         bsdtar->names_from_file = optarg;
374                         break;
375                 case 't': /* SUSv2 */
376                         set_mode(bsdtar, opt);
377                         bsdtar->verbose++;
378                         break;
379                 case OPTION_TOTALS: /* GNU tar */
380                         bsdtar->option_totals++;
381                         break;
382                 case 'U': /* GNU tar */
383                         bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
384                         bsdtar->option_unlink_first = 1;
385                         break;
386                 case 'u': /* SUSv2 */
387                         set_mode(bsdtar, opt);
388                         break;
389                 case 'v': /* SUSv2 */
390                         bsdtar->verbose++;
391                         break;
392                 case OPTION_VERSION:
393                         version();
394                         break;
395                 case 'w': /* SUSv2 */
396                         bsdtar->option_interactive = 1;
397                         break;
398                 case 'X': /* GNU tar */
399                         if (exclude_from_file(bsdtar, optarg))
400                                 bsdtar_errc(bsdtar, 1, 0,
401                                     "failed to process exclusions from file %s",
402                                     optarg);
403                         break;
404                 case 'x': /* SUSv2 */
405                         set_mode(bsdtar, opt);
406                         break;
407                 case 'y': /* FreeBSD version of GNU tar */
408 #if HAVE_LIBBZ2
409                         if (bsdtar->create_compression != '\0')
410                                 bsdtar_errc(bsdtar, 1, 0,
411                                     "Can't specify both -%c and -%c", opt,
412                                     bsdtar->create_compression);
413                         bsdtar->create_compression = opt;
414 #else
415                         bsdtar_warnc(bsdtar, 0, "-y compression not supported by this version of bsdtar");
416                         usage(bsdtar);
417 #endif
418                         break;
419                 case 'Z': /* GNU tar */
420                         if (bsdtar->create_compression != '\0')
421                                 bsdtar_errc(bsdtar, 1, 0,
422                                     "Can't specify both -%c and -%c", opt,
423                                     bsdtar->create_compression);
424                         bsdtar->create_compression = opt;
425                         break;
426                 case 'z': /* GNU tar, star, many others */
427 #if HAVE_LIBZ
428                         if (bsdtar->create_compression != '\0')
429                                 bsdtar_errc(bsdtar, 1, 0,
430                                     "Can't specify both -%c and -%c", opt,
431                                     bsdtar->create_compression);
432                         bsdtar->create_compression = opt;
433 #else
434                         bsdtar_warnc(bsdtar, 0, "-z compression not supported by this version of bsdtar");
435                         usage(bsdtar);
436 #endif
437                         break;
438                 default:
439                         usage(bsdtar);
440                 }
441         }
442
443         /*
444          * Sanity-check options.
445          */
446         if ((bsdtar->mode == '\0') && possible_help_request) {
447                 long_help(bsdtar);
448                 exit(0);
449         }
450
451         if (bsdtar->mode == '\0')
452                 bsdtar_errc(bsdtar, 1, 0,
453                     "Must specify one of -c, -r, -t, -u, -x");
454
455         /* Check boolean options only permitted in certain modes. */
456         if (bsdtar->option_dont_traverse_mounts)
457                 only_mode(bsdtar, "-X", "cru");
458         if (bsdtar->option_fast_read)
459                 only_mode(bsdtar, "--fast-read", "xt");
460         if (bsdtar->option_honor_nodump)
461                 only_mode(bsdtar, "--nodump", "cru");
462         if (option_o > 0) {
463                 switch (bsdtar->mode) {
464                 case 'c':
465                         /*
466                          * In GNU tar, -o means "old format."  The
467                          * "ustar" format is the closest thing
468                          * supported by libarchive.
469                          */
470                         bsdtar->create_format = "ustar";
471                         /* TODO: bsdtar->create_format = "v7"; */
472                         break;
473                 case 'x':
474                         /* POSIX-compatible behavior. */
475                         bsdtar->option_no_owner = 1;
476                         bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
477                         break;
478                 default:
479                         only_mode(bsdtar, "-o", "xc");
480                         break;
481                 }
482         }
483         if (bsdtar->option_no_subdirs)
484                 only_mode(bsdtar, "-n", "cru");
485         if (bsdtar->option_stdout)
486                 only_mode(bsdtar, "-O", "xt");
487         if (bsdtar->option_warn_links)
488                 only_mode(bsdtar, "--check-links", "cr");
489
490         /* Check other parameters only permitted in certain modes. */
491         if (bsdtar->create_compression == 'Z' && bsdtar->mode == 'c') {
492                 bsdtar_warnc(bsdtar, 0, ".Z compression not supported");
493                 usage(bsdtar);
494         }
495         if (bsdtar->create_compression != '\0') {
496                 strcpy(buff, "-?");
497                 buff[1] = bsdtar->create_compression;
498                 only_mode(bsdtar, buff, "cxt");
499         }
500         if (bsdtar->create_format != NULL)
501                 only_mode(bsdtar, "-F", "c");
502         if (bsdtar->symlink_mode != '\0') {
503                 strcpy(buff, "-?");
504                 buff[1] = bsdtar->symlink_mode;
505                 only_mode(bsdtar, buff, "cru");
506         }
507
508         bsdtar->argc -= optind;
509         bsdtar->argv += optind;
510
511         switch(bsdtar->mode) {
512         case 'c':
513                 tar_mode_c(bsdtar);
514                 break;
515         case 'r':
516                 tar_mode_r(bsdtar);
517                 break;
518         case 't':
519                 tar_mode_t(bsdtar);
520                 break;
521         case 'u':
522                 tar_mode_u(bsdtar);
523                 break;
524         case 'x':
525                 tar_mode_x(bsdtar);
526                 break;
527         }
528
529         cleanup_exclusions(bsdtar);
530         return (bsdtar->return_value);
531 }
532
533 static void
534 set_mode(struct bsdtar *bsdtar, char opt)
535 {
536         if (bsdtar->mode != '\0' && bsdtar->mode != opt)
537                 bsdtar_errc(bsdtar, 1, 0,
538                     "Can't specify both -%c and -%c", opt, bsdtar->mode);
539         bsdtar->mode = opt;
540 }
541
542 /*
543  * Verify that the mode is correct.
544  */
545 static void
546 only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes)
547 {
548         if (strchr(valid_modes, bsdtar->mode) == NULL)
549                 bsdtar_errc(bsdtar, 1, 0,
550                     "Option %s is not permitted in mode -%c",
551                     opt, bsdtar->mode);
552 }
553
554
555 /*-
556  * Convert traditional tar arguments into new-style.
557  * For example,
558  *     tar tvfb file.tar 32 --exclude FOO
559  * will be converted to
560  *     tar -t -v -f file.tar -b 32 --exclude FOO
561  *
562  * This requires building a new argv array.  The initial bundled word
563  * gets expanded into a new string that looks like "-t\0-v\0-f\0-b\0".
564  * The new argv array has pointers into this string intermingled with
565  * pointers to the existing arguments.  Arguments are moved to
566  * immediately follow their options.
567  *
568  * The optstring argument here is the same one passed to getopt(3).
569  * It is used to determine which option letters have trailing arguments.
570  */
571 char **
572 rewrite_argv(struct bsdtar *bsdtar, int *argc, char **src_argv,
573     const char *optstring)
574 {
575         char **new_argv, **dest_argv;
576         const char *p;
577         char *src, *dest;
578
579         if (src_argv[0] == NULL ||
580             src_argv[1] == NULL || src_argv[1][0] == '-')
581                 return (src_argv);
582
583         *argc += strlen(src_argv[1]) - 1;
584         new_argv = malloc((*argc + 1) * sizeof(new_argv[0]));
585         if (new_argv == NULL)
586                 bsdtar_errc(bsdtar, 1, errno, "No Memory");
587
588         dest_argv = new_argv;
589         *dest_argv++ = *src_argv++;
590
591         dest = malloc(strlen(*src_argv) * 3);
592         if (dest == NULL)
593                 bsdtar_errc(bsdtar, 1, errno, "No memory");
594         for (src = *src_argv++; *src != '\0'; src++) {
595                 *dest_argv++ = dest;
596                 *dest++ = '-';
597                 *dest++ = *src;
598                 *dest++ = '\0';
599                 /* If option takes an argument, insert that into the list. */
600                 for (p = optstring; p != NULL && *p != '\0'; p++) {
601                         if (*p != *src)
602                                 continue;
603                         if (p[1] != ':')        /* No arg required, done. */
604                                 break;
605                         if (*src_argv == NULL)  /* No arg available? Error. */
606                                 bsdtar_errc(bsdtar, 1, 0,
607                                     "Option %c requires an argument",
608                                     *src);
609                         *dest_argv++ = *src_argv++;
610                         break;
611                 }
612         }
613
614         /* Copy remaining arguments, including trailing NULL. */
615         while ((*dest_argv++ = *src_argv++) != NULL)
616                 ;
617
618         return (new_argv);
619 }
620
621 void
622 usage(struct bsdtar *bsdtar)
623 {
624         const char      *p;
625
626         p = bsdtar->progname;
627
628         fprintf(stderr, "Usage:\n");
629         fprintf(stderr, "  List:    %s -tf <archive-filename>\n", p);
630         fprintf(stderr, "  Extract: %s -xf <archive-filename>\n", p);
631         fprintf(stderr, "  Create:  %s -cf <archive-filename> [filenames...]\n", p);
632 #ifdef HAVE_GETOPT_LONG
633         fprintf(stderr, "  Help:    %s --help\n", p);
634 #else
635         fprintf(stderr, "  Help:    %s -h\n", p);
636 #endif
637         exit(1);
638 }
639
640 static void
641 version(void)
642 {
643         printf("bsdtar %s, ", PACKAGE_VERSION);
644         printf("%s\n", archive_version());
645         printf("Copyright (C) 2003-2004 Tim Kientzle\n");
646         exit(1);
647 }
648
649 static const char *long_help_msg =
650         "First option must be a mode specifier:\n"
651         "  -c Create  -r Add/Replace  -t List  -u Update  -x Extract\n"
652         "Common Options:\n"
653         "  -b #  Use # 512-byte records per I/O block\n"
654         "  -f <filename>  Location of archive (default " _PATH_DEFTAPE ")\n"
655         "  -v    Verbose\n"
656         "  -w    Interactive\n"
657         "Create: %p -c [options] [<file> | <dir> | @<archive> | -C <dir> ]\n"
658         "  <file>, <dir>  add these items to archive\n"
659         "  -z, -j  Compress archive with gzip/bzip2\n"
660         "  --format {ustar|pax|cpio|shar}  Select archive format\n"
661 #ifdef HAVE_GETOPT_LONG
662         "  --exclude <pattern>  Skip files that match pattern\n"
663 #else
664         "  -W exclude=<pattern>  Skip files that match pattern\n"
665 #endif
666         "  -C <dir>  Change to <dir> before processing remaining files\n"
667         "  @<archive>  Add entries from <archive> to output\n"
668         "List: %p -t [options] [<patterns>]\n"
669         "  <patterns>  If specified, list only entries that match\n"
670         "Extract: %p -x [options] [<patterns>]\n"
671         "  <patterns>  If specified, extract only entries that match\n"
672         "  -k    Keep (don't overwrite) existing files\n"
673         "  -m    Don't restore modification times\n"
674         "  -O    Write entries to stdout, don't restore to disk\n"
675         "  -p    Restore permissions (including ACLs, owner, file flags)\n";
676
677
678 /*
679  * Note that the word 'bsdtar' will always appear in the first line
680  * of output.
681  *
682  * In particular, /bin/sh scripts that need to test for the presence
683  * of bsdtar can use the following template:
684  *
685  * if (tar --help 2>&1 | grep bsdtar >/dev/null 2>&1 ) then \
686  *          echo bsdtar; else echo not bsdtar; fi
687  */
688 static void
689 long_help(struct bsdtar *bsdtar)
690 {
691         const char      *prog;
692         const char      *p;
693
694         prog = bsdtar->progname;
695
696         fflush(stderr);
697
698         p = (strcmp(prog,"bsdtar") != 0) ? "(bsdtar)" : "";
699         printf("%s%s: manipulate archive files\n", prog, p);
700
701         for (p = long_help_msg; *p != '\0'; p++) {
702                 if (*p == '%') {
703                         if (p[1] == 'p') {
704                                 fputs(prog, stdout);
705                                 p++;
706                         } else
707                                 putchar('%');
708                 } else
709                         putchar(*p);
710         }
711         fprintf(stdout, "\n%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
712         fprintf(stdout, "%s\n", archive_version());
713 }
714
715 static int
716 bsdtar_getopt(struct bsdtar *bsdtar, const char *optstring,
717     const struct option **poption)
718 {
719         char *p, *q;
720         const struct option *option;
721         int opt;
722         int option_index;
723         size_t option_length;
724
725         option_index = -1;
726         *poption = NULL;
727
728 #ifdef HAVE_GETOPT_LONG
729         opt = getopt_long(bsdtar->argc, bsdtar->argv, optstring,
730             tar_longopts, &option_index);
731         if (option_index > -1)
732                 *poption = tar_longopts + option_index;
733 #else
734         opt = getopt(bsdtar->argc, bsdtar->argv, optstring);
735 #endif
736
737         /* Support long options through -W longopt=value */
738         if (opt == 'W') {
739                 p = optarg;
740                 q = strchr(optarg, '=');
741                 if (q != NULL) {
742                         option_length = (size_t)(q - p);
743                         optarg = q + 1;
744                 } else {
745                         option_length = strlen(p);
746                         optarg = NULL;
747                 }
748                 option = tar_longopts;
749                 while (option->name != NULL &&
750                     (strlen(option->name) < option_length ||
751                     strncmp(p, option->name, option_length) != 0 )) {
752                         option++;
753                 }
754
755                 if (option->name != NULL) {
756                         *poption = option;
757                         opt = option->val;
758
759                         /* If the first match was exact, we're done. */
760                         if (strncmp(p, option->name, strlen(option->name)) == 0) {
761                                 while (option->name != NULL)
762                                         option++;
763                         } else {
764                                 /* Check if there's another match. */
765                                 option++;
766                                 while (option->name != NULL &&
767                                     (strlen(option->name) < option_length ||
768                                     strncmp(p, option->name, option_length) != 0)) {
769                                         option++;
770                                 }
771                         }
772                         if (option->name != NULL)
773                                 bsdtar_errc(bsdtar, 1, 0,
774                                     "Ambiguous option %s "
775                                     "(matches both %s and %s)",
776                                     p, (*poption)->name, option->name);
777
778                 } else {
779                         opt = '?';
780                         /* TODO: Set up a fake 'struct option' for
781                          * error reporting... ? ? ? */
782                 }
783         }
784
785         return (opt);
786 }