xref: /freebsd/contrib/libarchive/tar/bsdtar.c (revision 2e113ef82465598b8c26e0ca415fbe90677fbd47)
1caf54c4fSMartin Matuska /*-
2bd66c1b4SMartin Matuska  * SPDX-License-Identifier: BSD-2-Clause
3bd66c1b4SMartin Matuska  *
4caf54c4fSMartin Matuska  * Copyright (c) 2003-2008 Tim Kientzle
5caf54c4fSMartin Matuska  * All rights reserved.
6caf54c4fSMartin Matuska  */
7caf54c4fSMartin Matuska 
8caf54c4fSMartin Matuska #include "bsdtar_platform.h"
9caf54c4fSMartin Matuska 
10*2e113ef8SMartin Matuska #ifdef HAVE_LIMITS_H
11*2e113ef8SMartin Matuska #include <limits.h>
12*2e113ef8SMartin Matuska #endif
13caf54c4fSMartin Matuska #ifdef HAVE_SYS_PARAM_H
14caf54c4fSMartin Matuska #include <sys/param.h>
15caf54c4fSMartin Matuska #endif
16caf54c4fSMartin Matuska #ifdef HAVE_SYS_STAT_H
17caf54c4fSMartin Matuska #include <sys/stat.h>
18caf54c4fSMartin Matuska #endif
196c95142eSMartin Matuska #ifdef HAVE_COPYFILE_H
206c95142eSMartin Matuska #include <copyfile.h>
216c95142eSMartin Matuska #endif
22caf54c4fSMartin Matuska #ifdef HAVE_ERRNO_H
23caf54c4fSMartin Matuska #include <errno.h>
24caf54c4fSMartin Matuska #endif
25caf54c4fSMartin Matuska #ifdef HAVE_FCNTL_H
26caf54c4fSMartin Matuska #include <fcntl.h>
27caf54c4fSMartin Matuska #endif
28caf54c4fSMartin Matuska #ifdef HAVE_LANGINFO_H
29caf54c4fSMartin Matuska #include <langinfo.h>
30caf54c4fSMartin Matuska #endif
31bd66c1b4SMartin Matuska #ifdef HAVE_LIMITS_H
32bd66c1b4SMartin Matuska #include <limits.h>
33bd66c1b4SMartin Matuska #endif
34caf54c4fSMartin Matuska #ifdef HAVE_LOCALE_H
35caf54c4fSMartin Matuska #include <locale.h>
36caf54c4fSMartin Matuska #endif
37caf54c4fSMartin Matuska #ifdef HAVE_PATHS_H
38caf54c4fSMartin Matuska #include <paths.h>
39caf54c4fSMartin Matuska #endif
40caf54c4fSMartin Matuska #ifdef HAVE_SIGNAL_H
41caf54c4fSMartin Matuska #include <signal.h>
42caf54c4fSMartin Matuska #endif
43caf54c4fSMartin Matuska #include <stdio.h>
44caf54c4fSMartin Matuska #ifdef HAVE_STDLIB_H
45caf54c4fSMartin Matuska #include <stdlib.h>
46caf54c4fSMartin Matuska #endif
47caf54c4fSMartin Matuska #ifdef HAVE_STRING_H
48caf54c4fSMartin Matuska #include <string.h>
49caf54c4fSMartin Matuska #endif
50caf54c4fSMartin Matuska #ifdef HAVE_TIME_H
51caf54c4fSMartin Matuska #include <time.h>
52caf54c4fSMartin Matuska #endif
53caf54c4fSMartin Matuska #ifdef HAVE_UNISTD_H
54caf54c4fSMartin Matuska #include <unistd.h>
55caf54c4fSMartin Matuska #endif
56caf54c4fSMartin Matuska 
57caf54c4fSMartin Matuska #include "bsdtar.h"
58caf54c4fSMartin Matuska #include "err.h"
59caf54c4fSMartin Matuska 
60833a452eSMartin Matuska #if ARCHIVE_VERSION_NUMBER < 4000000 && !defined(_PATH_DEFTAPE)
61833a452eSMartin Matuska // Libarchive 4.0 and later will NOT define _PATH_DEFTAPE
62833a452eSMartin Matuska // but will honor it if it's set in the build.
63833a452eSMartin Matuska // Until then, we'll continue to set it by default on certain platforms:
64833a452eSMartin Matuska #if defined(__linux)
65caf54c4fSMartin Matuska #define _PATH_DEFTAPE "/dev/st0"
66833a452eSMartin Matuska #elif defined(_WIN32) && !defined(__CYGWIN__)
67caf54c4fSMartin Matuska #define _PATH_DEFTAPE "\\\\.\\tape0"
68833a452eSMartin Matuska #elif !defined(__APPLE__)
69caf54c4fSMartin Matuska #define _PATH_DEFTAPE "/dev/tape"
70caf54c4fSMartin Matuska #endif
71833a452eSMartin Matuska #endif
72833a452eSMartin Matuska 
73833a452eSMartin Matuska #define _PATH_STDIO "-"
74caf54c4fSMartin Matuska 
75caf54c4fSMartin Matuska #ifdef __MINGW32__
76caf54c4fSMartin Matuska int _CRT_glob = 0; /* Disable broken CRT globbing. */
77caf54c4fSMartin Matuska #endif
78caf54c4fSMartin Matuska 
79caf54c4fSMartin Matuska #if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1))
80caf54c4fSMartin Matuska static volatile int siginfo_occurred;
81caf54c4fSMartin Matuska 
82caf54c4fSMartin Matuska static void
siginfo_handler(int sig)83caf54c4fSMartin Matuska siginfo_handler(int sig)
84caf54c4fSMartin Matuska {
85caf54c4fSMartin Matuska 	(void)sig; /* UNUSED */
86caf54c4fSMartin Matuska 	siginfo_occurred = 1;
87caf54c4fSMartin Matuska }
88caf54c4fSMartin Matuska 
89caf54c4fSMartin Matuska int
need_report(void)90caf54c4fSMartin Matuska need_report(void)
91caf54c4fSMartin Matuska {
92caf54c4fSMartin Matuska 	int r = siginfo_occurred;
93caf54c4fSMartin Matuska 	siginfo_occurred = 0;
94caf54c4fSMartin Matuska 	return (r);
95caf54c4fSMartin Matuska }
96caf54c4fSMartin Matuska #else
97caf54c4fSMartin Matuska int
need_report(void)98caf54c4fSMartin Matuska need_report(void)
99caf54c4fSMartin Matuska {
100caf54c4fSMartin Matuska 	return (0);
101caf54c4fSMartin Matuska }
102caf54c4fSMartin Matuska #endif
103caf54c4fSMartin Matuska 
104b9128a37SMartin Matuska static __LA_NORETURN void		 long_help(void);
105caf54c4fSMartin Matuska static void		 only_mode(struct bsdtar *, const char *opt,
106caf54c4fSMartin Matuska 			     const char *valid);
107*2e113ef8SMartin Matuska static void		 set_mode(struct bsdtar *, int opt);
108b9128a37SMartin Matuska static __LA_NORETURN void		 version(void);
109caf54c4fSMartin Matuska 
110caf54c4fSMartin Matuska /* A basic set of security flags to request from libarchive. */
111caf54c4fSMartin Matuska #define	SECURITY					\
112caf54c4fSMartin Matuska 	(ARCHIVE_EXTRACT_SECURE_SYMLINKS		\
113caf54c4fSMartin Matuska 	 | ARCHIVE_EXTRACT_SECURE_NODOTDOT)
114caf54c4fSMartin Matuska 
11552c2bb75SMartin Matuska static char const * const vcs_files[] = {
11652c2bb75SMartin Matuska   /* CVS */
11752c2bb75SMartin Matuska   "CVS", ".cvsignore",
11852c2bb75SMartin Matuska   /* RCS */
11952c2bb75SMartin Matuska   "RCS",
12052c2bb75SMartin Matuska   /* SCCS */
12152c2bb75SMartin Matuska   "SCCS",
12252c2bb75SMartin Matuska   /* SVN */
12352c2bb75SMartin Matuska   ".svn",
12452c2bb75SMartin Matuska   /* git */
12552c2bb75SMartin Matuska   ".git", ".gitignore", ".gitattributes", ".gitmodules",
12652c2bb75SMartin Matuska   /* Arch */
12752c2bb75SMartin Matuska   ".arch-ids", "{arch}", "=RELEASE-ID", "=meta-update", "=update",
12852c2bb75SMartin Matuska   /* Bazaar */
12952c2bb75SMartin Matuska   ".bzr", ".bzrignore", ".bzrtags",
13052c2bb75SMartin Matuska   /* Mercurial */
13152c2bb75SMartin Matuska   ".hg", ".hgignore", ".hgtags",
13252c2bb75SMartin Matuska   /* darcs */
13352c2bb75SMartin Matuska   "_darcs",
13452c2bb75SMartin Matuska   NULL
13552c2bb75SMartin Matuska };
13652c2bb75SMartin Matuska 
137caf54c4fSMartin Matuska int
main(int argc,char ** argv)138caf54c4fSMartin Matuska main(int argc, char **argv)
139caf54c4fSMartin Matuska {
140caf54c4fSMartin Matuska 	struct bsdtar		*bsdtar, bsdtar_storage;
141caf54c4fSMartin Matuska 	int			 opt, t;
142*2e113ef8SMartin Matuska 	int			 compression, compression2;
143acc60b03SMartin Matuska 	const char		*compression_name, *compression2_name;
144acc60b03SMartin Matuska 	const char		*compress_program;
145b9128a37SMartin Matuska 	char			*tptr, *uptr;
146caf54c4fSMartin Matuska 	char			 possible_help_request;
147caf54c4fSMartin Matuska 	char			 buff[16];
14813d826ffSMartin Matuska 	long			 l;
149*2e113ef8SMartin Matuska 	time_t			now;
150caf54c4fSMartin Matuska 
151caf54c4fSMartin Matuska 	/*
152caf54c4fSMartin Matuska 	 * Use a pointer for consistency, but stack-allocated storage
153caf54c4fSMartin Matuska 	 * for ease of cleanup.
154caf54c4fSMartin Matuska 	 */
1556c95142eSMartin Matuska 	bsdtar = &bsdtar_storage;
156caf54c4fSMartin Matuska 	memset(bsdtar, 0, sizeof(*bsdtar));
157caf54c4fSMartin Matuska 	bsdtar->fd = -1; /* Mark as "unused" */
158caf54c4fSMartin Matuska 	bsdtar->gid = -1;
159caf54c4fSMartin Matuska 	bsdtar->uid = -1;
16064287048SMartin Matuska 	bsdtar->flags = 0;
161acc60b03SMartin Matuska 	compression = compression2 = '\0';
162acc60b03SMartin Matuska 	compression_name = compression2_name = NULL;
163acc60b03SMartin Matuska 	compress_program = NULL;
164*2e113ef8SMartin Matuska 	time(&now);
165caf54c4fSMartin Matuska 
1666c95142eSMartin Matuska #if defined(HAVE_SIGACTION)
1676c95142eSMartin Matuska 	{ /* Set up signal handling. */
168caf54c4fSMartin Matuska 		struct sigaction sa;
169caf54c4fSMartin Matuska 		sa.sa_handler = siginfo_handler;
170caf54c4fSMartin Matuska 		sigemptyset(&sa.sa_mask);
171caf54c4fSMartin Matuska 		sa.sa_flags = 0;
172caf54c4fSMartin Matuska #ifdef SIGINFO
173caf54c4fSMartin Matuska 		if (sigaction(SIGINFO, &sa, NULL))
174caf54c4fSMartin Matuska 			lafe_errc(1, errno, "sigaction(SIGINFO) failed");
175caf54c4fSMartin Matuska #endif
176caf54c4fSMartin Matuska #ifdef SIGUSR1
177caf54c4fSMartin Matuska 		/* ... and treat SIGUSR1 the same way as SIGINFO. */
178caf54c4fSMartin Matuska 		if (sigaction(SIGUSR1, &sa, NULL))
179caf54c4fSMartin Matuska 			lafe_errc(1, errno, "sigaction(SIGUSR1) failed");
180caf54c4fSMartin Matuska #endif
1816c95142eSMartin Matuska #ifdef SIGPIPE
1826c95142eSMartin Matuska 		/* Ignore SIGPIPE signals. */
1836c95142eSMartin Matuska 		sa.sa_handler = SIG_IGN;
1846c95142eSMartin Matuska 		sigaction(SIGPIPE, &sa, NULL);
1856c95142eSMartin Matuska #endif
186caf54c4fSMartin Matuska 	}
187caf54c4fSMartin Matuska #endif
188caf54c4fSMartin Matuska 
189cdf63a70SMartin Matuska 	/* Set lafe_progname before calling lafe_warnc. */
190cdf63a70SMartin Matuska 	lafe_setprogname(*argv, "bsdtar");
191caf54c4fSMartin Matuska 
192caf54c4fSMartin Matuska #if HAVE_SETLOCALE
193caf54c4fSMartin Matuska 	if (setlocale(LC_ALL, "") == NULL)
194caf54c4fSMartin Matuska 		lafe_warnc(0, "Failed to set default locale");
195caf54c4fSMartin Matuska #endif
196caf54c4fSMartin Matuska #if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER)
197caf54c4fSMartin Matuska 	bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd');
198caf54c4fSMartin Matuska #endif
199caf54c4fSMartin Matuska 	possible_help_request = 0;
200caf54c4fSMartin Matuska 
201caf54c4fSMartin Matuska 	/* Look up uid of current user for future reference */
202caf54c4fSMartin Matuska 	bsdtar->user_uid = geteuid();
203caf54c4fSMartin Matuska 
204caf54c4fSMartin Matuska 	/* Default: open tape drive. */
205caf54c4fSMartin Matuska 	bsdtar->filename = getenv("TAPE");
206833a452eSMartin Matuska #if defined(_PATH_DEFTAPE)
207833a452eSMartin Matuska 	if (bsdtar->filename == NULL) {
208833a452eSMartin Matuska #if defined(_WIN32) && !defined(__CYGWIN__)
209833a452eSMartin Matuska 		int tapeExists = !_access(_PATH_DEFTAPE, 0);
210833a452eSMartin Matuska #else
211833a452eSMartin Matuska 		int tapeExists = !access(_PATH_DEFTAPE, F_OK);
212833a452eSMartin Matuska #endif
213833a452eSMartin Matuska 		if (tapeExists) {
214caf54c4fSMartin Matuska 			bsdtar->filename = _PATH_DEFTAPE;
215833a452eSMartin Matuska 		}
216833a452eSMartin Matuska 	}
217833a452eSMartin Matuska #endif
218833a452eSMartin Matuska 	if (bsdtar->filename == NULL) {
219833a452eSMartin Matuska 		bsdtar->filename = _PATH_STDIO;
220833a452eSMartin Matuska 	}
221caf54c4fSMartin Matuska 
2226c95142eSMartin Matuska 	/* Default block size settings. */
2236c95142eSMartin Matuska 	bsdtar->bytes_per_block = DEFAULT_BYTES_PER_BLOCK;
2246c95142eSMartin Matuska 	/* Allow library to default this unless user specifies -b. */
2256c95142eSMartin Matuska 	bsdtar->bytes_in_last_block = -1;
2266c95142eSMartin Matuska 
227caf54c4fSMartin Matuska 	/* Default: preserve mod time on extract */
228caf54c4fSMartin Matuska 	bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME;
229caf54c4fSMartin Matuska 
230caf54c4fSMartin Matuska 	/* Default: Perform basic security checks. */
231caf54c4fSMartin Matuska 	bsdtar->extract_flags |= SECURITY;
232caf54c4fSMartin Matuska 
233caf54c4fSMartin Matuska #ifndef _WIN32
234caf54c4fSMartin Matuska 	/* On POSIX systems, assume --same-owner and -p when run by
235caf54c4fSMartin Matuska 	 * the root user.  This doesn't make any sense on Windows. */
236caf54c4fSMartin Matuska 	if (bsdtar->user_uid == 0) {
237caf54c4fSMartin Matuska 		/* --same-owner */
238caf54c4fSMartin Matuska 		bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
239caf54c4fSMartin Matuska 		/* -p */
240caf54c4fSMartin Matuska 		bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
241caf54c4fSMartin Matuska 		bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
242caf54c4fSMartin Matuska 		bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
243caf54c4fSMartin Matuska 		bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
2446c95142eSMartin Matuska 		bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
245caf54c4fSMartin Matuska 	}
246caf54c4fSMartin Matuska #endif
247caf54c4fSMartin Matuska 
2486c95142eSMartin Matuska 	/*
2496c95142eSMartin Matuska 	 * Enable Mac OS "copyfile()" extension by default.
2506c95142eSMartin Matuska 	 * This has no effect on other platforms.
2516c95142eSMartin Matuska 	 */
252fd082e96SMartin Matuska 	bsdtar->readdisk_flags |= ARCHIVE_READDISK_MAC_COPYFILE;
2536c95142eSMartin Matuska #ifdef COPYFILE_DISABLE_VAR
2546c95142eSMartin Matuska 	if (getenv(COPYFILE_DISABLE_VAR))
255fd082e96SMartin Matuska 		bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE;
2566c95142eSMartin Matuska #endif
25764287048SMartin Matuska #if defined(__APPLE__)
25864287048SMartin Matuska 	/*
25964287048SMartin Matuska 	 * On Mac OS ACLs are archived with copyfile() (--mac-metadata)
26064287048SMartin Matuska 	 * Translation to NFSv4 ACLs has to be requested explicitly with --acls
26164287048SMartin Matuska 	 */
26264287048SMartin Matuska 	bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_ACL;
26364287048SMartin Matuska #endif
26464287048SMartin Matuska 
265fd082e96SMartin Matuska 	bsdtar->matching = archive_match_new();
266fd082e96SMartin Matuska 	if (bsdtar->matching == NULL)
267fd082e96SMartin Matuska 		lafe_errc(1, errno, "Out of memory");
268acc60b03SMartin Matuska 	bsdtar->cset = cset_new();
269acc60b03SMartin Matuska 	if (bsdtar->cset == NULL)
270acc60b03SMartin Matuska 		lafe_errc(1, errno, "Out of memory");
2716c95142eSMartin Matuska 
272caf54c4fSMartin Matuska 	bsdtar->argv = argv;
273caf54c4fSMartin Matuska 	bsdtar->argc = argc;
274caf54c4fSMartin Matuska 
275caf54c4fSMartin Matuska 	/*
276caf54c4fSMartin Matuska 	 * Comments following each option indicate where that option
277caf54c4fSMartin Matuska 	 * originated:  SUSv2, POSIX, GNU tar, star, etc.  If there's
278caf54c4fSMartin Matuska 	 * no such comment, then I don't know of anyone else who
279caf54c4fSMartin Matuska 	 * implements that option.
280caf54c4fSMartin Matuska 	 */
281caf54c4fSMartin Matuska 	while ((opt = bsdtar_getopt(bsdtar)) != -1) {
282caf54c4fSMartin Matuska 		switch (opt) {
283acc60b03SMartin Matuska 		case 'a': /* GNU tar */
28464287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_AUTO_COMPRESS;
28564287048SMartin Matuska 			break;
28664287048SMartin Matuska 		case OPTION_ACLS: /* GNU tar */
28764287048SMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
28864287048SMartin Matuska 			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_ACL;
28964287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_ACLS;
290acc60b03SMartin Matuska 			break;
291caf54c4fSMartin Matuska 		case 'B': /* GNU tar */
292caf54c4fSMartin Matuska 			/* libarchive doesn't need this; just ignore it. */
293caf54c4fSMartin Matuska 			break;
294caf54c4fSMartin Matuska 		case 'b': /* SUSv2 */
295b1c91e4bSMartin Matuska 			tptr = NULL;
29613d826ffSMartin Matuska 			l = strtol(bsdtar->argument, &tptr, 10);
29713d826ffSMartin Matuska 			if (l <= 0 || l > 8192L ||
298b1c91e4bSMartin Matuska 			    *(bsdtar->argument) == '\0' || tptr == NULL ||
299b1c91e4bSMartin Matuska 			    *tptr != '\0') {
300b1c91e4bSMartin Matuska 				lafe_errc(1, 0, "Invalid or out of range "
301b1c91e4bSMartin Matuska 				    "(1..8192) argument to -b");
302b1c91e4bSMartin Matuska 			}
30313d826ffSMartin Matuska 			bsdtar->bytes_per_block = 512 * (int)l;
3046c95142eSMartin Matuska 			/* Explicit -b forces last block size. */
3056c95142eSMartin Matuska 			bsdtar->bytes_in_last_block = bsdtar->bytes_per_block;
306caf54c4fSMartin Matuska 			break;
307acc60b03SMartin Matuska 		case OPTION_B64ENCODE:
308acc60b03SMartin Matuska 			if (compression2 != '\0')
309acc60b03SMartin Matuska 				lafe_errc(1, 0,
310acc60b03SMartin Matuska 				    "Can't specify both --uuencode and "
311acc60b03SMartin Matuska 				    "--b64encode");
312acc60b03SMartin Matuska 			compression2 = opt;
313acc60b03SMartin Matuska 			compression2_name = "b64encode";
314acc60b03SMartin Matuska 			break;
315caf54c4fSMartin Matuska 		case 'C': /* GNU tar */
3166c95142eSMartin Matuska 			if (strlen(bsdtar->argument) == 0)
3176c95142eSMartin Matuska 				lafe_errc(1, 0,
3186c95142eSMartin Matuska 				    "Meaningless option: -C ''");
3196c95142eSMartin Matuska 
3206c95142eSMartin Matuska 			set_chdir(bsdtar, bsdtar->argument);
321caf54c4fSMartin Matuska 			break;
322caf54c4fSMartin Matuska 		case 'c': /* SUSv2 */
323caf54c4fSMartin Matuska 			set_mode(bsdtar, opt);
324caf54c4fSMartin Matuska 			break;
325caf54c4fSMartin Matuska 		case OPTION_CHECK_LINKS: /* GNU tar */
32664287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_WARN_LINKS;
327caf54c4fSMartin Matuska 			break;
328caf54c4fSMartin Matuska 		case OPTION_CHROOT: /* NetBSD */
32964287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_CHROOT;
330caf54c4fSMartin Matuska 			break;
331cdf63a70SMartin Matuska 		case OPTION_CLEAR_NOCHANGE_FFLAGS:
332cdf63a70SMartin Matuska 			bsdtar->extract_flags |=
333cdf63a70SMartin Matuska 			    ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS;
334cdf63a70SMartin Matuska 			break;
335caf54c4fSMartin Matuska 		case OPTION_EXCLUDE: /* GNU tar */
336fd082e96SMartin Matuska 			if (archive_match_exclude_pattern(
337fd082e96SMartin Matuska 			    bsdtar->matching, bsdtar->argument) != ARCHIVE_OK)
338caf54c4fSMartin Matuska 				lafe_errc(1, 0,
339*2e113ef8SMartin Matuska 				    "Couldn't exclude %s", bsdtar->argument);
340caf54c4fSMartin Matuska 			break;
34152c2bb75SMartin Matuska 		case OPTION_EXCLUDE_VCS: /* GNU tar */
34252c2bb75SMartin Matuska 			for(t=0; vcs_files[t]; t++) {
34352c2bb75SMartin Matuska 				if (archive_match_exclude_pattern(
34452c2bb75SMartin Matuska 				    bsdtar->matching,
34552c2bb75SMartin Matuska 				    vcs_files[t]) != ARCHIVE_OK)
34652c2bb75SMartin Matuska 					lafe_errc(1, 0, "Couldn't "
347*2e113ef8SMartin Matuska 					    "exclude %s", vcs_files[t]);
34852c2bb75SMartin Matuska 			}
34952c2bb75SMartin Matuska 			break;
35064287048SMartin Matuska 		case OPTION_FFLAGS:
35164287048SMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
35264287048SMartin Matuska 			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_FFLAGS;
35364287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_FFLAGS;
35464287048SMartin Matuska 			break;
355caf54c4fSMartin Matuska 		case OPTION_FORMAT: /* GNU tar, others */
356acc60b03SMartin Matuska 			cset_set_format(bsdtar->cset, bsdtar->argument);
357caf54c4fSMartin Matuska 			break;
358caf54c4fSMartin Matuska 		case 'f': /* SUSv2 */
3596c95142eSMartin Matuska 			bsdtar->filename = bsdtar->argument;
360caf54c4fSMartin Matuska 			break;
361caf54c4fSMartin Matuska 		case OPTION_GID: /* cpio */
362b1c91e4bSMartin Matuska 			tptr = NULL;
36313d826ffSMartin Matuska 			l = strtol(bsdtar->argument, &tptr, 10);
36413d826ffSMartin Matuska 			if (l < 0 || l >= INT_MAX || *(bsdtar->argument) == '\0' ||
365b1c91e4bSMartin Matuska 			    tptr == NULL || *tptr != '\0') {
366b1c91e4bSMartin Matuska 				lafe_errc(1, 0, "Invalid argument to --gid");
367b1c91e4bSMartin Matuska 			}
36813d826ffSMartin Matuska 			bsdtar->gid = (int)l;
369caf54c4fSMartin Matuska 			break;
370caf54c4fSMartin Matuska 		case OPTION_GNAME: /* cpio */
3716c95142eSMartin Matuska 			bsdtar->gname = bsdtar->argument;
372caf54c4fSMartin Matuska 			break;
373b9128a37SMartin Matuska 		case OPTION_GROUP: /* GNU tar */
374b9128a37SMartin Matuska 			tptr = NULL;
375b9128a37SMartin Matuska 
376b9128a37SMartin Matuska 			uptr = strchr(bsdtar->argument, ':');
377b9128a37SMartin Matuska 			if (uptr != NULL) {
37813d826ffSMartin Matuska 				if (uptr[1] == '\0') {
379b9128a37SMartin Matuska 					lafe_errc(1, 0, "Invalid argument to --group (missing id after :)");
380b9128a37SMartin Matuska 				}
381b9128a37SMartin Matuska 				uptr[0] = 0;
382b9128a37SMartin Matuska 				uptr++;
38313d826ffSMartin Matuska 				l = strtol(uptr, &tptr, 10);
38413d826ffSMartin Matuska 				if (l < 0 || l >= INT_MAX || *uptr == '\0' ||
385b9128a37SMartin Matuska 				    tptr == NULL || *tptr != '\0') {
386b9128a37SMartin Matuska 					lafe_errc(1, 0, "Invalid argument to --group (%s is not a number)", uptr);
387b9128a37SMartin Matuska 				} else {
38813d826ffSMartin Matuska 					bsdtar->gid = (int)l;
389b9128a37SMartin Matuska 				}
390b9128a37SMartin Matuska 				bsdtar->gname = bsdtar->argument;
391b9128a37SMartin Matuska 			} else {
39213d826ffSMartin Matuska 				l = strtol(bsdtar->argument, &tptr, 10);
39313d826ffSMartin Matuska 				if (l < 0 || l >= INT_MAX || *(bsdtar->argument) == '\0' ||
394b9128a37SMartin Matuska 				    tptr == NULL || *tptr != '\0') {
395b9128a37SMartin Matuska 					bsdtar->gname = bsdtar->argument;
396b9128a37SMartin Matuska 				} else {
39713d826ffSMartin Matuska 					bsdtar->gid = (int)l;
398b9128a37SMartin Matuska 					bsdtar->gname = "";
399b9128a37SMartin Matuska 				}
400b9128a37SMartin Matuska 			}
401b9128a37SMartin Matuska 			break;
402acc60b03SMartin Matuska 		case OPTION_GRZIP:
403acc60b03SMartin Matuska 			if (compression != '\0')
404acc60b03SMartin Matuska 				lafe_errc(1, 0,
405acc60b03SMartin Matuska 				    "Can't specify both -%c and -%c", opt,
406acc60b03SMartin Matuska 				    compression);
407acc60b03SMartin Matuska 			compression = opt;
408acc60b03SMartin Matuska 			compression_name = "grzip";
409acc60b03SMartin Matuska 			break;
410caf54c4fSMartin Matuska 		case 'H': /* BSD convention */
411caf54c4fSMartin Matuska 			bsdtar->symlink_mode = 'H';
412caf54c4fSMartin Matuska 			break;
413caf54c4fSMartin Matuska 		case 'h': /* Linux Standards Base, gtar; synonym for -L */
414caf54c4fSMartin Matuska 			bsdtar->symlink_mode = 'L';
415caf54c4fSMartin Matuska 			/* Hack: -h by itself is the "help" command. */
416caf54c4fSMartin Matuska 			possible_help_request = 1;
417caf54c4fSMartin Matuska 			break;
418caf54c4fSMartin Matuska 		case OPTION_HELP: /* GNU tar, others */
419caf54c4fSMartin Matuska 			long_help();
420b9128a37SMartin Matuska 			/* NOTREACHED*/
421acc60b03SMartin Matuska 		case OPTION_HFS_COMPRESSION: /* Mac OS X v10.6 or later */
422acc60b03SMartin Matuska 			bsdtar->extract_flags |=
423acc60b03SMartin Matuska 			    ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED;
424acc60b03SMartin Matuska 			break;
425cdf63a70SMartin Matuska 		case OPTION_IGNORE_ZEROS:
42664287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_IGNORE_ZEROS;
427cdf63a70SMartin Matuska 			break;
428caf54c4fSMartin Matuska 		case 'I': /* GNU tar */
429caf54c4fSMartin Matuska 			/*
430caf54c4fSMartin Matuska 			 * TODO: Allow 'names' to come from an archive,
431caf54c4fSMartin Matuska 			 * not just a text file.  Design a good UI for
432caf54c4fSMartin Matuska 			 * allowing names and mode/owner to be read
433caf54c4fSMartin Matuska 			 * from an archive, with contents coming from
434caf54c4fSMartin Matuska 			 * disk.  This can be used to "refresh" an
435caf54c4fSMartin Matuska 			 * archive or to design archives with special
436caf54c4fSMartin Matuska 			 * permissions without having to create those
437caf54c4fSMartin Matuska 			 * permissions on disk.
438caf54c4fSMartin Matuska 			 */
4396c95142eSMartin Matuska 			bsdtar->names_from_file = bsdtar->argument;
440caf54c4fSMartin Matuska 			break;
441caf54c4fSMartin Matuska 		case OPTION_INCLUDE:
442caf54c4fSMartin Matuska 			/*
443caf54c4fSMartin Matuska 			 * No one else has the @archive extension, so
444caf54c4fSMartin Matuska 			 * no one else needs this to filter entries
445caf54c4fSMartin Matuska 			 * when transforming archives.
446caf54c4fSMartin Matuska 			 */
447fd082e96SMartin Matuska 			if (archive_match_include_pattern(bsdtar->matching,
448fd082e96SMartin Matuska 			    bsdtar->argument) != ARCHIVE_OK)
449caf54c4fSMartin Matuska 				lafe_errc(1, 0,
450caf54c4fSMartin Matuska 				    "Failed to add %s to inclusion list",
4516c95142eSMartin Matuska 				    bsdtar->argument);
452caf54c4fSMartin Matuska 			break;
453caf54c4fSMartin Matuska 		case 'j': /* GNU tar */
454acc60b03SMartin Matuska 			if (compression != '\0')
455caf54c4fSMartin Matuska 				lafe_errc(1, 0,
456caf54c4fSMartin Matuska 				    "Can't specify both -%c and -%c", opt,
457acc60b03SMartin Matuska 				    compression);
458acc60b03SMartin Matuska 			compression = opt;
459acc60b03SMartin Matuska 			compression_name = "bzip2";
460caf54c4fSMartin Matuska 			break;
461caf54c4fSMartin Matuska 		case 'J': /* GNU tar 1.21 and later */
462acc60b03SMartin Matuska 			if (compression != '\0')
463caf54c4fSMartin Matuska 				lafe_errc(1, 0,
464caf54c4fSMartin Matuska 				    "Can't specify both -%c and -%c", opt,
465acc60b03SMartin Matuska 				    compression);
466acc60b03SMartin Matuska 			compression = opt;
467acc60b03SMartin Matuska 			compression_name = "xz";
468caf54c4fSMartin Matuska 			break;
469caf54c4fSMartin Matuska 		case 'k': /* GNU tar */
470caf54c4fSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
471caf54c4fSMartin Matuska 			break;
472caf54c4fSMartin Matuska 		case OPTION_KEEP_NEWER_FILES: /* GNU tar */
473caf54c4fSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
474caf54c4fSMartin Matuska 			break;
475caf54c4fSMartin Matuska 		case 'L': /* BSD convention */
476caf54c4fSMartin Matuska 			bsdtar->symlink_mode = 'L';
477caf54c4fSMartin Matuska 			break;
478caf54c4fSMartin Matuska 	        case 'l': /* SUSv2 and GNU tar beginning with 1.16 */
479caf54c4fSMartin Matuska 			/* GNU tar 1.13  used -l for --one-file-system */
48064287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_WARN_LINKS;
481caf54c4fSMartin Matuska 			break;
482acc60b03SMartin Matuska 		case OPTION_LRZIP:
483cdf63a70SMartin Matuska 		case OPTION_LZ4:
4846c95142eSMartin Matuska 		case OPTION_LZIP: /* GNU tar beginning with 1.23 */
4856c95142eSMartin Matuska 		case OPTION_LZMA: /* GNU tar beginning with 1.20 */
486acc60b03SMartin Matuska 		case OPTION_LZOP: /* GNU tar beginning with 1.21 */
4875c831a5bSMartin Matuska 		case OPTION_ZSTD:
488acc60b03SMartin Matuska 			if (compression != '\0')
489caf54c4fSMartin Matuska 				lafe_errc(1, 0,
490caf54c4fSMartin Matuska 				    "Can't specify both -%c and -%c", opt,
491acc60b03SMartin Matuska 				    compression);
492acc60b03SMartin Matuska 			compression = opt;
493acc60b03SMartin Matuska 			switch (opt) {
494acc60b03SMartin Matuska 			case OPTION_LRZIP: compression_name = "lrzip"; break;
495cdf63a70SMartin Matuska 			case OPTION_LZ4:  compression_name = "lz4"; break;
496acc60b03SMartin Matuska 			case OPTION_LZIP: compression_name = "lzip"; break;
497acc60b03SMartin Matuska 			case OPTION_LZMA: compression_name = "lzma"; break;
498acc60b03SMartin Matuska 			case OPTION_LZOP: compression_name = "lzop"; break;
4995c831a5bSMartin Matuska 			case OPTION_ZSTD: compression_name = "zstd"; break;
500acc60b03SMartin Matuska 			}
501caf54c4fSMartin Matuska 			break;
502caf54c4fSMartin Matuska 		case 'm': /* SUSv2 */
503caf54c4fSMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
504caf54c4fSMartin Matuska 			break;
50564287048SMartin Matuska 		case OPTION_MAC_METADATA: /* Mac OS X */
50664287048SMartin Matuska 			bsdtar->readdisk_flags |= ARCHIVE_READDISK_MAC_COPYFILE;
50764287048SMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
50864287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_MAC_METADATA;
50964287048SMartin Matuska 			break;
510caf54c4fSMartin Matuska 		case 'n': /* GNU tar */
51164287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_NO_SUBDIRS;
512caf54c4fSMartin Matuska 			break;
513caf54c4fSMartin Matuska 	        /*
514caf54c4fSMartin Matuska 		 * Selecting files by time:
515caf54c4fSMartin Matuska 		 *    --newer-?time='date' Only files newer than 'date'
516caf54c4fSMartin Matuska 		 *    --newer-?time-than='file' Only files newer than time
517caf54c4fSMartin Matuska 		 *         on specified file (useful for incremental backups)
518caf54c4fSMartin Matuska 		 */
519caf54c4fSMartin Matuska 		case OPTION_NEWER_CTIME: /* GNU tar */
520fd082e96SMartin Matuska 			if (archive_match_include_date(bsdtar->matching,
521fd082e96SMartin Matuska 			    ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER,
522fd082e96SMartin Matuska 			    bsdtar->argument) != ARCHIVE_OK)
523fd082e96SMartin Matuska 				lafe_errc(1, 0, "Error : %s",
524fd082e96SMartin Matuska 				    archive_error_string(bsdtar->matching));
525caf54c4fSMartin Matuska 			break;
526caf54c4fSMartin Matuska 		case OPTION_NEWER_CTIME_THAN:
527fd082e96SMartin Matuska 			if (archive_match_include_file_time(bsdtar->matching,
528fd082e96SMartin Matuska 			    ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER,
529fd082e96SMartin Matuska 			    bsdtar->argument) != ARCHIVE_OK)
530fd082e96SMartin Matuska 				lafe_errc(1, 0, "Error : %s",
531fd082e96SMartin Matuska 				    archive_error_string(bsdtar->matching));
532caf54c4fSMartin Matuska 			break;
533caf54c4fSMartin Matuska 		case OPTION_NEWER_MTIME: /* GNU tar */
534fd082e96SMartin Matuska 			if (archive_match_include_date(bsdtar->matching,
535fd082e96SMartin Matuska 			    ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER,
536fd082e96SMartin Matuska 			    bsdtar->argument) != ARCHIVE_OK)
537fd082e96SMartin Matuska 				lafe_errc(1, 0, "Error : %s",
538fd082e96SMartin Matuska 				    archive_error_string(bsdtar->matching));
539caf54c4fSMartin Matuska 			break;
540caf54c4fSMartin Matuska 		case OPTION_NEWER_MTIME_THAN:
541fd082e96SMartin Matuska 			if (archive_match_include_file_time(bsdtar->matching,
542fd082e96SMartin Matuska 			    ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER,
543fd082e96SMartin Matuska 			    bsdtar->argument) != ARCHIVE_OK)
544fd082e96SMartin Matuska 				lafe_errc(1, 0, "Error : %s",
545fd082e96SMartin Matuska 				    archive_error_string(bsdtar->matching));
546caf54c4fSMartin Matuska 			break;
547caf54c4fSMartin Matuska 		case OPTION_NODUMP: /* star */
548fd082e96SMartin Matuska 			bsdtar->readdisk_flags |= ARCHIVE_READDISK_HONOR_NODUMP;
549caf54c4fSMartin Matuska 			break;
550acc60b03SMartin Matuska 		case OPTION_NOPRESERVE_HFS_COMPRESSION:
551acc60b03SMartin Matuska 			/* Mac OS X v10.6 or later */
552acc60b03SMartin Matuska 			bsdtar->extract_flags |=
553acc60b03SMartin Matuska 			    ARCHIVE_EXTRACT_NO_HFS_COMPRESSION;
554acc60b03SMartin Matuska 			break;
55564287048SMartin Matuska 		case OPTION_NO_ACLS: /* GNU tar */
55664287048SMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL;
55764287048SMartin Matuska 			bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_ACL;
55864287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_NO_ACLS;
55964287048SMartin Matuska 			break;
56064287048SMartin Matuska 		case OPTION_NO_FFLAGS:
56164287048SMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS;
56264287048SMartin Matuska 			bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_FFLAGS;
56364287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_NO_FFLAGS;
56464287048SMartin Matuska 			break;
56564287048SMartin Matuska 		case OPTION_NO_MAC_METADATA: /* Mac OS X */
56664287048SMartin Matuska 			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE;
56764287048SMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA;
56864287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_NO_MAC_METADATA;
56964287048SMartin Matuska 			break;
570833a452eSMartin Matuska 		case OPTION_NO_READ_SPARSE:
571833a452eSMartin Matuska 			bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_SPARSE;
572833a452eSMartin Matuska 			bsdtar->flags |= OPTFLAG_NO_READ_SPARSE;
573833a452eSMartin Matuska 			break;
574f9762417SMartin Matuska 		case OPTION_NO_SAFE_WRITES:
575f9762417SMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_SAFE_WRITES;
576f9762417SMartin Matuska 			break;
577caf54c4fSMartin Matuska 		case OPTION_NO_SAME_OWNER: /* GNU tar */
578caf54c4fSMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
579caf54c4fSMartin Matuska 			break;
580caf54c4fSMartin Matuska 		case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */
581caf54c4fSMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM;
582caf54c4fSMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL;
583caf54c4fSMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR;
584caf54c4fSMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS;
5856c95142eSMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA;
586caf54c4fSMartin Matuska 			break;
58764287048SMartin Matuska 		case OPTION_NO_XATTRS: /* GNU tar */
588cdf63a70SMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR;
589cdf63a70SMartin Matuska 			bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_XATTR;
59064287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_NO_XATTRS;
591cdf63a70SMartin Matuska 			break;
592caf54c4fSMartin Matuska 		case OPTION_NULL: /* GNU tar */
59364287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_NULL;
594caf54c4fSMartin Matuska 			break;
595caf54c4fSMartin Matuska 		case OPTION_NUMERIC_OWNER: /* GNU tar */
596caf54c4fSMartin Matuska 			bsdtar->uname = "";
597caf54c4fSMartin Matuska 			bsdtar->gname = "";
59864287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_NUMERIC_OWNER;
599caf54c4fSMartin Matuska 			break;
600caf54c4fSMartin Matuska 		case 'O': /* GNU tar */
60164287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_STDOUT;
602caf54c4fSMartin Matuska 			break;
603caf54c4fSMartin Matuska 		case 'o': /* SUSv2 and GNU conflict here, but not fatally */
60464287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_O;
605caf54c4fSMartin Matuska 			break;
606acc60b03SMartin Matuska 	        /*
607acc60b03SMartin Matuska 		 * Selecting files by time:
608acc60b03SMartin Matuska 		 *    --older-?time='date' Only files older than 'date'
609acc60b03SMartin Matuska 		 *    --older-?time-than='file' Only files older than time
610acc60b03SMartin Matuska 		 *         on specified file
611acc60b03SMartin Matuska 		 */
612acc60b03SMartin Matuska 		case OPTION_OLDER_CTIME:
613acc60b03SMartin Matuska 			if (archive_match_include_date(bsdtar->matching,
614acc60b03SMartin Matuska 			    ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER,
615acc60b03SMartin Matuska 			    bsdtar->argument) != ARCHIVE_OK)
616acc60b03SMartin Matuska 				lafe_errc(1, 0, "Error : %s",
617acc60b03SMartin Matuska 				    archive_error_string(bsdtar->matching));
618acc60b03SMartin Matuska 			break;
619acc60b03SMartin Matuska 		case OPTION_OLDER_CTIME_THAN:
620acc60b03SMartin Matuska 			if (archive_match_include_file_time(bsdtar->matching,
621acc60b03SMartin Matuska 			    ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER,
622acc60b03SMartin Matuska 			    bsdtar->argument) != ARCHIVE_OK)
623acc60b03SMartin Matuska 				lafe_errc(1, 0, "Error : %s",
624acc60b03SMartin Matuska 				    archive_error_string(bsdtar->matching));
625acc60b03SMartin Matuska 			break;
626acc60b03SMartin Matuska 		case OPTION_OLDER_MTIME:
627acc60b03SMartin Matuska 			if (archive_match_include_date(bsdtar->matching,
628acc60b03SMartin Matuska 			    ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER,
629acc60b03SMartin Matuska 			    bsdtar->argument) != ARCHIVE_OK)
630acc60b03SMartin Matuska 				lafe_errc(1, 0, "Error : %s",
631acc60b03SMartin Matuska 				    archive_error_string(bsdtar->matching));
632acc60b03SMartin Matuska 			break;
633acc60b03SMartin Matuska 		case OPTION_OLDER_MTIME_THAN:
634acc60b03SMartin Matuska 			if (archive_match_include_file_time(bsdtar->matching,
635acc60b03SMartin Matuska 			    ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER,
636acc60b03SMartin Matuska 			    bsdtar->argument) != ARCHIVE_OK)
637acc60b03SMartin Matuska 				lafe_errc(1, 0, "Error : %s",
638acc60b03SMartin Matuska 				    archive_error_string(bsdtar->matching));
639acc60b03SMartin Matuska 			break;
640caf54c4fSMartin Matuska 		case OPTION_ONE_FILE_SYSTEM: /* GNU tar */
641fd082e96SMartin Matuska 			bsdtar->readdisk_flags |=
642fd082e96SMartin Matuska 			    ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS;
643caf54c4fSMartin Matuska 			break;
644caf54c4fSMartin Matuska 		case OPTION_OPTIONS:
645b9128a37SMartin Matuska 			if (bsdtar->option_options != NULL) {
646b9128a37SMartin Matuska 				lafe_warnc(0,
647b9128a37SMartin Matuska 				    "Ignoring previous option '%s', separate multiple options with commas",
648b9128a37SMartin Matuska 				    bsdtar->option_options);
649b9128a37SMartin Matuska 			}
6506c95142eSMartin Matuska 			bsdtar->option_options = bsdtar->argument;
651caf54c4fSMartin Matuska 			break;
652b9128a37SMartin Matuska 		case OPTION_OWNER: /* GNU tar */
653b9128a37SMartin Matuska 			tptr = NULL;
654b9128a37SMartin Matuska 
655b9128a37SMartin Matuska 			uptr = strchr(bsdtar->argument, ':');
656b9128a37SMartin Matuska 			if (uptr != NULL) {
657b9128a37SMartin Matuska 				if (uptr[1] == 0) {
658b9128a37SMartin Matuska 					lafe_errc(1, 0, "Invalid argument to --owner (missing id after :)");
659b9128a37SMartin Matuska 				}
660b9128a37SMartin Matuska 				uptr[0] = 0;
661b9128a37SMartin Matuska 				uptr++;
66213d826ffSMartin Matuska 				l = strtol(uptr, &tptr, 10);
66313d826ffSMartin Matuska 				if (l < 0 || l >= INT_MAX || *uptr == '\0' ||
664b9128a37SMartin Matuska 				    tptr == NULL || *tptr != '\0') {
665b9128a37SMartin Matuska 					lafe_errc(1, 0, "Invalid argument to --owner (%s is not a number)", uptr);
666b9128a37SMartin Matuska 				} else {
66713d826ffSMartin Matuska 					bsdtar->uid = (int)l;
668b9128a37SMartin Matuska 				}
669b9128a37SMartin Matuska 				bsdtar->uname = bsdtar->argument;
670b9128a37SMartin Matuska 			} else {
67113d826ffSMartin Matuska 				l = strtol(bsdtar->argument, &tptr, 10);
67213d826ffSMartin Matuska 				if (l < 0 || l >= INT_MAX || *(bsdtar->argument) == '\0' ||
673b9128a37SMartin Matuska 				    tptr == NULL || *tptr != '\0') {
674b9128a37SMartin Matuska 					bsdtar->uname = bsdtar->argument;
675b9128a37SMartin Matuska 				} else {
67613d826ffSMartin Matuska 					bsdtar->uid = (int)l;
677b9128a37SMartin Matuska 					bsdtar->uname = "";
678b9128a37SMartin Matuska 				}
679b9128a37SMartin Matuska 			}
680b9128a37SMartin Matuska 			break;
681*2e113ef8SMartin Matuska 		case OPTION_MTIME: /* GNU tar */
682*2e113ef8SMartin Matuska 			bsdtar->has_mtime = 1;
683*2e113ef8SMartin Matuska 			bsdtar->mtime = archive_parse_date(now, bsdtar->argument);
684*2e113ef8SMartin Matuska 			if (bsdtar->mtime == (time_t)-1) {
685*2e113ef8SMartin Matuska 				lafe_errc(1, 0, "Invalid argument to --mtime (bad date string)");
686*2e113ef8SMartin Matuska 			}
687*2e113ef8SMartin Matuska 			break;
688*2e113ef8SMartin Matuska 		case OPTION_CLAMP_MTIME: /* GNU tar */
689*2e113ef8SMartin Matuska 			bsdtar->clamp_mtime = 1;
690*2e113ef8SMartin Matuska 			break;
691caf54c4fSMartin Matuska #if 0
692caf54c4fSMartin Matuska 		/*
693caf54c4fSMartin Matuska 		 * The common BSD -P option is not necessary, since
694caf54c4fSMartin Matuska 		 * our default is to archive symlinks, not follow
695caf54c4fSMartin Matuska 		 * them.  This is convenient, as -P conflicts with GNU
696caf54c4fSMartin Matuska 		 * tar anyway.
697caf54c4fSMartin Matuska 		 */
698caf54c4fSMartin Matuska 		case 'P': /* BSD convention */
699caf54c4fSMartin Matuska 			/* Default behavior, no option necessary. */
700caf54c4fSMartin Matuska 			break;
701caf54c4fSMartin Matuska #endif
702caf54c4fSMartin Matuska 		case 'P': /* GNU tar */
703caf54c4fSMartin Matuska 			bsdtar->extract_flags &= ~SECURITY;
70464287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_ABSOLUTE_PATHS;
705caf54c4fSMartin Matuska 			break;
706caf54c4fSMartin Matuska 		case 'p': /* GNU tar, star */
707caf54c4fSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
708caf54c4fSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
709caf54c4fSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
710caf54c4fSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
7116c95142eSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
712caf54c4fSMartin Matuska 			break;
713cdf63a70SMartin Matuska 		case OPTION_PASSPHRASE:
714cdf63a70SMartin Matuska 			bsdtar->passphrase = bsdtar->argument;
715cdf63a70SMartin Matuska 			break;
716caf54c4fSMartin Matuska 		case OPTION_POSIX: /* GNU tar */
717acc60b03SMartin Matuska 			cset_set_format(bsdtar->cset, "pax");
718caf54c4fSMartin Matuska 			break;
719caf54c4fSMartin Matuska 		case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */
72064287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_FAST_READ;
721caf54c4fSMartin Matuska 			break;
722caf54c4fSMartin Matuska 		case 'r': /* SUSv2 */
723caf54c4fSMartin Matuska 			set_mode(bsdtar, opt);
724caf54c4fSMartin Matuska 			break;
725833a452eSMartin Matuska 		case OPTION_READ_SPARSE:
726833a452eSMartin Matuska 			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_SPARSE;
727833a452eSMartin Matuska 			bsdtar->flags |= OPTFLAG_READ_SPARSE;
728833a452eSMartin Matuska 			break;
729caf54c4fSMartin Matuska 		case 'S': /* NetBSD pax-as-tar */
730caf54c4fSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE;
731caf54c4fSMartin Matuska 			break;
732caf54c4fSMartin Matuska 		case 's': /* NetBSD pax-as-tar */
733b9128a37SMartin Matuska #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H)
7346c95142eSMartin Matuska 			add_substitution(bsdtar, bsdtar->argument);
735caf54c4fSMartin Matuska #else
736caf54c4fSMartin Matuska 			lafe_warnc(0,
737caf54c4fSMartin Matuska 			    "-s is not supported by this version of bsdtar");
738caf54c4fSMartin Matuska 			usage();
739caf54c4fSMartin Matuska #endif
740caf54c4fSMartin Matuska 			break;
741f9762417SMartin Matuska 		case OPTION_SAFE_WRITES:
742f9762417SMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_SAFE_WRITES;
743f9762417SMartin Matuska 			break;
744caf54c4fSMartin Matuska 		case OPTION_SAME_OWNER: /* GNU tar */
745caf54c4fSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
746caf54c4fSMartin Matuska 			break;
747caf54c4fSMartin Matuska 		case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */
748b1c91e4bSMartin Matuska 			tptr = NULL;
74913d826ffSMartin Matuska 			l = strtol(bsdtar->argument, &tptr, 10);
75013d826ffSMartin Matuska 			if (l < 0 || l > 100000L || *(bsdtar->argument) == '\0' ||
751b1c91e4bSMartin Matuska 			    tptr == NULL || *tptr != '\0') {
752b1c91e4bSMartin Matuska 				lafe_errc(1, 0, "Invalid argument to "
753b1c91e4bSMartin Matuska 				    "--strip-components");
754b1c91e4bSMartin Matuska 			}
75513d826ffSMartin Matuska 			bsdtar->strip_components = (int)l;
756caf54c4fSMartin Matuska 			break;
757caf54c4fSMartin Matuska 		case 'T': /* GNU tar */
758*2e113ef8SMartin Matuska 			if (bsdtar->names_from_file)
759*2e113ef8SMartin Matuska 				lafe_errc(1, 0, "Multiple --files-from/-T options are not supported");
7606c95142eSMartin Matuska 			bsdtar->names_from_file = bsdtar->argument;
761caf54c4fSMartin Matuska 			break;
762caf54c4fSMartin Matuska 		case 't': /* SUSv2 */
763caf54c4fSMartin Matuska 			set_mode(bsdtar, opt);
764caf54c4fSMartin Matuska 			bsdtar->verbose++;
765caf54c4fSMartin Matuska 			break;
766caf54c4fSMartin Matuska 		case OPTION_TOTALS: /* GNU tar */
76764287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_TOTALS;
768caf54c4fSMartin Matuska 			break;
769caf54c4fSMartin Matuska 		case 'U': /* GNU tar */
770caf54c4fSMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
77164287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_UNLINK_FIRST;
772caf54c4fSMartin Matuska 			break;
773caf54c4fSMartin Matuska 		case 'u': /* SUSv2 */
774caf54c4fSMartin Matuska 			set_mode(bsdtar, opt);
775caf54c4fSMartin Matuska 			break;
776caf54c4fSMartin Matuska 		case OPTION_UID: /* cpio */
777b1c91e4bSMartin Matuska 			tptr = NULL;
77813d826ffSMartin Matuska 			l = strtol(bsdtar->argument, &tptr, 10);
77913d826ffSMartin Matuska 			if (l < 0 || l >= INT_MAX || *(bsdtar->argument) == '\0' ||
780b1c91e4bSMartin Matuska 			    tptr == NULL || *tptr != '\0') {
781b1c91e4bSMartin Matuska 				lafe_errc(1, 0, "Invalid argument to --uid");
782b1c91e4bSMartin Matuska 			}
78313d826ffSMartin Matuska 			bsdtar->uid = (int)l;
784caf54c4fSMartin Matuska 			break;
785caf54c4fSMartin Matuska 		case OPTION_UNAME: /* cpio */
7866c95142eSMartin Matuska 			bsdtar->uname = bsdtar->argument;
787caf54c4fSMartin Matuska 			break;
788acc60b03SMartin Matuska 		case OPTION_UUENCODE:
789acc60b03SMartin Matuska 			if (compression2 != '\0')
790acc60b03SMartin Matuska 				lafe_errc(1, 0,
791acc60b03SMartin Matuska 				    "Can't specify both --uuencode and "
792acc60b03SMartin Matuska 				    "--b64encode");
793acc60b03SMartin Matuska 			compression2 = opt;
794acc60b03SMartin Matuska 			compression2_name = "uuencode";
795acc60b03SMartin Matuska 			break;
796caf54c4fSMartin Matuska 		case 'v': /* SUSv2 */
797caf54c4fSMartin Matuska 			bsdtar->verbose++;
798caf54c4fSMartin Matuska 			break;
799caf54c4fSMartin Matuska 		case OPTION_VERSION: /* GNU convention */
800caf54c4fSMartin Matuska 			version();
801b9128a37SMartin Matuska 			/* NOTREACHED */
802caf54c4fSMartin Matuska #if 0
803caf54c4fSMartin Matuska 		/*
804caf54c4fSMartin Matuska 		 * The -W longopt feature is handled inside of
805caf54c4fSMartin Matuska 		 * bsdtar_getopt(), so -W is not available here.
806caf54c4fSMartin Matuska 		 */
807caf54c4fSMartin Matuska 		case 'W': /* Obscure GNU convention. */
808caf54c4fSMartin Matuska 			break;
809caf54c4fSMartin Matuska #endif
810caf54c4fSMartin Matuska 		case 'w': /* SUSv2 */
81164287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_INTERACTIVE;
812caf54c4fSMartin Matuska 			break;
813caf54c4fSMartin Matuska 		case 'X': /* GNU tar */
814fd082e96SMartin Matuska 			if (archive_match_exclude_pattern_from_file(
815fd082e96SMartin Matuska 			    bsdtar->matching, bsdtar->argument, 0)
816fd082e96SMartin Matuska 			    != ARCHIVE_OK)
817fd082e96SMartin Matuska 				lafe_errc(1, 0, "Error : %s",
818fd082e96SMartin Matuska 				    archive_error_string(bsdtar->matching));
819caf54c4fSMartin Matuska 			break;
820caf54c4fSMartin Matuska 		case 'x': /* SUSv2 */
821caf54c4fSMartin Matuska 			set_mode(bsdtar, opt);
822caf54c4fSMartin Matuska 			break;
82364287048SMartin Matuska 		case OPTION_XATTRS: /* GNU tar */
82464287048SMartin Matuska 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
82564287048SMartin Matuska 			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_XATTR;
82664287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_XATTRS;
82764287048SMartin Matuska 			break;
828caf54c4fSMartin Matuska 		case 'y': /* FreeBSD version of GNU tar */
829acc60b03SMartin Matuska 			if (compression != '\0')
830caf54c4fSMartin Matuska 				lafe_errc(1, 0,
831caf54c4fSMartin Matuska 				    "Can't specify both -%c and -%c", opt,
832acc60b03SMartin Matuska 				    compression);
833acc60b03SMartin Matuska 			compression = opt;
834acc60b03SMartin Matuska 			compression_name = "bzip2";
835caf54c4fSMartin Matuska 			break;
836caf54c4fSMartin Matuska 		case 'Z': /* GNU tar */
837acc60b03SMartin Matuska 			if (compression != '\0')
838caf54c4fSMartin Matuska 				lafe_errc(1, 0,
839caf54c4fSMartin Matuska 				    "Can't specify both -%c and -%c", opt,
840acc60b03SMartin Matuska 				    compression);
841acc60b03SMartin Matuska 			compression = opt;
842acc60b03SMartin Matuska 			compression_name = "compress";
843caf54c4fSMartin Matuska 			break;
844caf54c4fSMartin Matuska 		case 'z': /* GNU tar, star, many others */
845acc60b03SMartin Matuska 			if (compression != '\0')
846caf54c4fSMartin Matuska 				lafe_errc(1, 0,
847caf54c4fSMartin Matuska 				    "Can't specify both -%c and -%c", opt,
848acc60b03SMartin Matuska 				    compression);
849acc60b03SMartin Matuska 			compression = opt;
850acc60b03SMartin Matuska 			compression_name = "gzip";
851caf54c4fSMartin Matuska 			break;
852caf54c4fSMartin Matuska 		case OPTION_USE_COMPRESS_PROGRAM:
853acc60b03SMartin Matuska 			compress_program = bsdtar->argument;
854caf54c4fSMartin Matuska 			break;
855caf54c4fSMartin Matuska 		default:
856caf54c4fSMartin Matuska 			usage();
857caf54c4fSMartin Matuska 		}
858caf54c4fSMartin Matuska 	}
859caf54c4fSMartin Matuska 
860caf54c4fSMartin Matuska 	/*
861caf54c4fSMartin Matuska 	 * Sanity-check options.
862caf54c4fSMartin Matuska 	 */
863caf54c4fSMartin Matuska 
864caf54c4fSMartin Matuska 	/* If no "real" mode was specified, treat -h as --help. */
865caf54c4fSMartin Matuska 	if ((bsdtar->mode == '\0') && possible_help_request) {
866caf54c4fSMartin Matuska 		long_help();
867caf54c4fSMartin Matuska 	}
868caf54c4fSMartin Matuska 
869caf54c4fSMartin Matuska 	/* Otherwise, a mode is required. */
870caf54c4fSMartin Matuska 	if (bsdtar->mode == '\0')
871caf54c4fSMartin Matuska 		lafe_errc(1, 0,
872caf54c4fSMartin Matuska 		    "Must specify one of -c, -r, -t, -u, -x");
873caf54c4fSMartin Matuska 
874caf54c4fSMartin Matuska 	/* Check boolean options only permitted in certain modes. */
875833a452eSMartin Matuska 	if (bsdtar->flags & OPTFLAG_AUTO_COMPRESS) {
876833a452eSMartin Matuska 		only_mode(bsdtar, "-a", "cx");
877833a452eSMartin Matuska 		if (bsdtar->mode == 'x') {
878833a452eSMartin Matuska 			bsdtar->flags &= ~OPTFLAG_AUTO_COMPRESS;
879833a452eSMartin Matuska 			lafe_warnc(0,
880833a452eSMartin Matuska 			    "Ignoring option -a in mode -x");
881833a452eSMartin Matuska 		}
882833a452eSMartin Matuska 	}
883fd082e96SMartin Matuska 	if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS)
884caf54c4fSMartin Matuska 		only_mode(bsdtar, "--one-file-system", "cru");
88564287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_FAST_READ)
886caf54c4fSMartin Matuska 		only_mode(bsdtar, "--fast-read", "xt");
887acc60b03SMartin Matuska 	if (bsdtar->extract_flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED)
888acc60b03SMartin Matuska 		only_mode(bsdtar, "--hfsCompression", "x");
889acc60b03SMartin Matuska 	if (bsdtar->extract_flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION)
890acc60b03SMartin Matuska 		only_mode(bsdtar, "--nopreserveHFSCompression", "x");
891fd082e96SMartin Matuska 	if (bsdtar->readdisk_flags & ARCHIVE_READDISK_HONOR_NODUMP)
892caf54c4fSMartin Matuska 		only_mode(bsdtar, "--nodump", "cru");
89364287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_ACLS)
89464287048SMartin Matuska 		only_mode(bsdtar, "--acls", "crux");
89564287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_NO_ACLS)
89664287048SMartin Matuska 		only_mode(bsdtar, "--no-acls", "crux");
89764287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_XATTRS)
89864287048SMartin Matuska 		only_mode(bsdtar, "--xattrs", "crux");
89964287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_NO_XATTRS)
90064287048SMartin Matuska 		only_mode(bsdtar, "--no-xattrs", "crux");
90164287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_FFLAGS)
90264287048SMartin Matuska 		only_mode(bsdtar, "--fflags", "crux");
90364287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_NO_FFLAGS)
90464287048SMartin Matuska 		only_mode(bsdtar, "--no-fflags", "crux");
90564287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_MAC_METADATA)
90664287048SMartin Matuska 		only_mode(bsdtar, "--mac-metadata", "crux");
90764287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_NO_MAC_METADATA)
90864287048SMartin Matuska 		only_mode(bsdtar, "--no-mac-metadata", "crux");
90964287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_O) {
910caf54c4fSMartin Matuska 		switch (bsdtar->mode) {
911caf54c4fSMartin Matuska 		case 'c':
912caf54c4fSMartin Matuska 			/*
913caf54c4fSMartin Matuska 			 * In GNU tar, -o means "old format."  The
914caf54c4fSMartin Matuska 			 * "ustar" format is the closest thing
915caf54c4fSMartin Matuska 			 * supported by libarchive.
916caf54c4fSMartin Matuska 			 */
917acc60b03SMartin Matuska 			cset_set_format(bsdtar->cset, "ustar");
918caf54c4fSMartin Matuska 			/* TODO: bsdtar->create_format = "v7"; */
919caf54c4fSMartin Matuska 			break;
920caf54c4fSMartin Matuska 		case 'x':
921caf54c4fSMartin Matuska 			/* POSIX-compatible behavior. */
92264287048SMartin Matuska 			bsdtar->flags |= OPTFLAG_NO_OWNER;
923caf54c4fSMartin Matuska 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
924caf54c4fSMartin Matuska 			break;
925caf54c4fSMartin Matuska 		default:
926caf54c4fSMartin Matuska 			only_mode(bsdtar, "-o", "xc");
927caf54c4fSMartin Matuska 			break;
928caf54c4fSMartin Matuska 		}
929caf54c4fSMartin Matuska 	}
93064287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_STDOUT)
931caf54c4fSMartin Matuska 		only_mode(bsdtar, "-O", "xt");
93264287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_UNLINK_FIRST)
933caf54c4fSMartin Matuska 		only_mode(bsdtar, "-U", "x");
93464287048SMartin Matuska 	if (bsdtar->flags & OPTFLAG_WARN_LINKS)
935caf54c4fSMartin Matuska 		only_mode(bsdtar, "--check-links", "cr");
936caf54c4fSMartin Matuska 
93764287048SMartin Matuska 	if ((bsdtar->flags & OPTFLAG_AUTO_COMPRESS) &&
93864287048SMartin Matuska 	    cset_auto_compress(bsdtar->cset, bsdtar->filename)) {
939acc60b03SMartin Matuska 		/* Ignore specified compressions if auto-compress works. */
940acc60b03SMartin Matuska 		compression = '\0';
941acc60b03SMartin Matuska 		compression2 = '\0';
942caf54c4fSMartin Matuska 	}
943acc60b03SMartin Matuska 	/* Check other parameters only permitted in certain modes. */
944acc60b03SMartin Matuska 	if (compress_program != NULL) {
945acc60b03SMartin Matuska 		only_mode(bsdtar, "--use-compress-program", "cxt");
946acc60b03SMartin Matuska 		cset_add_filter_program(bsdtar->cset, compress_program);
947acc60b03SMartin Matuska 		/* Ignore specified compressions. */
948acc60b03SMartin Matuska 		compression = '\0';
949acc60b03SMartin Matuska 		compression2 = '\0';
950acc60b03SMartin Matuska 	}
951acc60b03SMartin Matuska 	if (compression != '\0') {
952acc60b03SMartin Matuska 		switch (compression) {
953acc60b03SMartin Matuska 		case 'J': case 'j': case 'y': case 'Z': case 'z':
954acc60b03SMartin Matuska 			strcpy(buff, "-?");
955*2e113ef8SMartin Matuska 			buff[1] = (char)compression;
956acc60b03SMartin Matuska 			break;
957acc60b03SMartin Matuska 		default:
958acc60b03SMartin Matuska 			strcpy(buff, "--");
959acc60b03SMartin Matuska 			strcat(buff, compression_name);
960acc60b03SMartin Matuska 			break;
961acc60b03SMartin Matuska 		}
962acc60b03SMartin Matuska 		only_mode(bsdtar, buff, "cxt");
963acc60b03SMartin Matuska 		cset_add_filter(bsdtar->cset, compression_name);
964acc60b03SMartin Matuska 	}
965acc60b03SMartin Matuska 	if (compression2 != '\0') {
966acc60b03SMartin Matuska 		strcpy(buff, "--");
967acc60b03SMartin Matuska 		strcat(buff, compression2_name);
968acc60b03SMartin Matuska 		only_mode(bsdtar, buff, "cxt");
969acc60b03SMartin Matuska 		cset_add_filter(bsdtar->cset, compression2_name);
970acc60b03SMartin Matuska 	}
971acc60b03SMartin Matuska 	if (cset_get_format(bsdtar->cset) != NULL)
972caf54c4fSMartin Matuska 		only_mode(bsdtar, "--format", "cru");
973caf54c4fSMartin Matuska 	if (bsdtar->symlink_mode != '\0') {
974caf54c4fSMartin Matuska 		strcpy(buff, "-?");
975caf54c4fSMartin Matuska 		buff[1] = bsdtar->symlink_mode;
976caf54c4fSMartin Matuska 		only_mode(bsdtar, buff, "cru");
977caf54c4fSMartin Matuska 	}
9786c95142eSMartin Matuska 
979*2e113ef8SMartin Matuska 	if (!bsdtar->has_mtime && bsdtar->clamp_mtime)
980*2e113ef8SMartin Matuska 		lafe_errc(1, 0,
981*2e113ef8SMartin Matuska 		    "--clamp-mtime is not valid without --mtime <date>");
982*2e113ef8SMartin Matuska 
98352c2bb75SMartin Matuska 	/*
98452c2bb75SMartin Matuska 	 * When creating an archive from a directory tree, the directory
98552c2bb75SMartin Matuska 	 * walking code will already avoid entering directories when
98652c2bb75SMartin Matuska 	 * recursive inclusion of directory content is disabled, therefore
98752c2bb75SMartin Matuska 	 * changing the matching behavior has no effect for creation modes.
98852c2bb75SMartin Matuska 	 * It is relevant for extraction or listing.
98952c2bb75SMartin Matuska 	 */
99052c2bb75SMartin Matuska 	archive_match_set_inclusion_recursion(bsdtar->matching,
99152c2bb75SMartin Matuska 					      !(bsdtar->flags & OPTFLAG_NO_SUBDIRS));
99252c2bb75SMartin Matuska 
9936c95142eSMartin Matuska 	/* Filename "-" implies stdio. */
9946c95142eSMartin Matuska 	if (strcmp(bsdtar->filename, "-") == 0)
9956c95142eSMartin Matuska 		bsdtar->filename = NULL;
996caf54c4fSMartin Matuska 
997caf54c4fSMartin Matuska 	switch(bsdtar->mode) {
998caf54c4fSMartin Matuska 	case 'c':
999caf54c4fSMartin Matuska 		tar_mode_c(bsdtar);
1000caf54c4fSMartin Matuska 		break;
1001caf54c4fSMartin Matuska 	case 'r':
1002caf54c4fSMartin Matuska 		tar_mode_r(bsdtar);
1003caf54c4fSMartin Matuska 		break;
1004caf54c4fSMartin Matuska 	case 't':
1005caf54c4fSMartin Matuska 		tar_mode_t(bsdtar);
1006caf54c4fSMartin Matuska 		break;
1007caf54c4fSMartin Matuska 	case 'u':
1008caf54c4fSMartin Matuska 		tar_mode_u(bsdtar);
1009caf54c4fSMartin Matuska 		break;
1010caf54c4fSMartin Matuska 	case 'x':
1011caf54c4fSMartin Matuska 		tar_mode_x(bsdtar);
1012caf54c4fSMartin Matuska 		break;
1013caf54c4fSMartin Matuska 	}
1014caf54c4fSMartin Matuska 
1015fd082e96SMartin Matuska 	archive_match_free(bsdtar->matching);
1016b9128a37SMartin Matuska #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H)
1017caf54c4fSMartin Matuska 	cleanup_substitution(bsdtar);
1018caf54c4fSMartin Matuska #endif
1019acc60b03SMartin Matuska 	cset_free(bsdtar->cset);
1020cdf63a70SMartin Matuska 	passphrase_free(bsdtar->ppbuff);
1021caf54c4fSMartin Matuska 
1022caf54c4fSMartin Matuska 	if (bsdtar->return_value != 0)
1023caf54c4fSMartin Matuska 		lafe_warnc(0,
1024caf54c4fSMartin Matuska 		    "Error exit delayed from previous errors.");
1025caf54c4fSMartin Matuska 	return (bsdtar->return_value);
1026caf54c4fSMartin Matuska }
1027caf54c4fSMartin Matuska 
1028caf54c4fSMartin Matuska static void
set_mode(struct bsdtar * bsdtar,int opt)1029*2e113ef8SMartin Matuska set_mode(struct bsdtar *bsdtar, int opt)
1030caf54c4fSMartin Matuska {
1031caf54c4fSMartin Matuska 	if (bsdtar->mode != '\0' && bsdtar->mode != opt)
1032caf54c4fSMartin Matuska 		lafe_errc(1, 0,
1033caf54c4fSMartin Matuska 		    "Can't specify both -%c and -%c", opt, bsdtar->mode);
1034caf54c4fSMartin Matuska 	bsdtar->mode = opt;
1035caf54c4fSMartin Matuska }
1036caf54c4fSMartin Matuska 
1037caf54c4fSMartin Matuska /*
1038caf54c4fSMartin Matuska  * Verify that the mode is correct.
1039caf54c4fSMartin Matuska  */
1040caf54c4fSMartin Matuska static void
only_mode(struct bsdtar * bsdtar,const char * opt,const char * valid_modes)1041caf54c4fSMartin Matuska only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes)
1042caf54c4fSMartin Matuska {
1043caf54c4fSMartin Matuska 	if (strchr(valid_modes, bsdtar->mode) == NULL)
1044caf54c4fSMartin Matuska 		lafe_errc(1, 0,
1045caf54c4fSMartin Matuska 		    "Option %s is not permitted in mode -%c",
1046caf54c4fSMartin Matuska 		    opt, bsdtar->mode);
1047caf54c4fSMartin Matuska }
1048caf54c4fSMartin Matuska 
1049caf54c4fSMartin Matuska 
1050caf54c4fSMartin Matuska void
usage(void)1051caf54c4fSMartin Matuska usage(void)
1052caf54c4fSMartin Matuska {
1053caf54c4fSMartin Matuska 	const char	*p;
1054caf54c4fSMartin Matuska 
1055cdf63a70SMartin Matuska 	p = lafe_getprogname();
1056caf54c4fSMartin Matuska 
1057caf54c4fSMartin Matuska 	fprintf(stderr, "Usage:\n");
1058caf54c4fSMartin Matuska 	fprintf(stderr, "  List:    %s -tf <archive-filename>\n", p);
1059caf54c4fSMartin Matuska 	fprintf(stderr, "  Extract: %s -xf <archive-filename>\n", p);
1060caf54c4fSMartin Matuska 	fprintf(stderr, "  Create:  %s -cf <archive-filename> [filenames...]\n", p);
1061caf54c4fSMartin Matuska 	fprintf(stderr, "  Help:    %s --help\n", p);
1062caf54c4fSMartin Matuska 	exit(1);
1063caf54c4fSMartin Matuska }
1064caf54c4fSMartin Matuska 
1065caf54c4fSMartin Matuska static void
version(void)1066caf54c4fSMartin Matuska version(void)
1067caf54c4fSMartin Matuska {
1068caf54c4fSMartin Matuska 	printf("bsdtar %s - %s \n",
1069caf54c4fSMartin Matuska 	    BSDTAR_VERSION_STRING,
1070cdf63a70SMartin Matuska 	    archive_version_details());
1071caf54c4fSMartin Matuska 	exit(0);
1072caf54c4fSMartin Matuska }
1073caf54c4fSMartin Matuska 
1074caf54c4fSMartin Matuska static const char *long_help_msg =
1075caf54c4fSMartin Matuska 	"First option must be a mode specifier:\n"
1076caf54c4fSMartin Matuska 	"  -c Create  -r Add/Replace  -t List  -u Update  -x Extract\n"
1077caf54c4fSMartin Matuska 	"Common Options:\n"
1078caf54c4fSMartin Matuska 	"  -b #  Use # 512-byte records per I/O block\n"
1079caf54c4fSMartin Matuska 	"  -f <filename>  Location of archive (default " _PATH_DEFTAPE ")\n"
1080caf54c4fSMartin Matuska 	"  -v    Verbose\n"
1081caf54c4fSMartin Matuska 	"  -w    Interactive\n"
1082caf54c4fSMartin Matuska 	"Create: %p -c [options] [<file> | <dir> | @<archive> | -C <dir> ]\n"
1083caf54c4fSMartin Matuska 	"  <file>, <dir>  add these items to archive\n"
1084caf54c4fSMartin Matuska 	"  -z, -j, -J, --lzma  Compress archive with gzip/bzip2/xz/lzma\n"
1085caf54c4fSMartin Matuska 	"  --format {ustar|pax|cpio|shar}  Select archive format\n"
1086caf54c4fSMartin Matuska 	"  --exclude <pattern>  Skip files that match pattern\n"
1087*2e113ef8SMartin Matuska 	"  --mtime <date>  Set modification times for added files\n"
1088*2e113ef8SMartin Matuska 	"  --clamp-mtime   Only set modification times for files newer than --mtime\n"
1089caf54c4fSMartin Matuska 	"  -C <dir>  Change to <dir> before processing remaining files\n"
1090caf54c4fSMartin Matuska 	"  @<archive>  Add entries from <archive> to output\n"
1091caf54c4fSMartin Matuska 	"List: %p -t [options] [<patterns>]\n"
1092caf54c4fSMartin Matuska 	"  <patterns>  If specified, list only entries that match\n"
1093caf54c4fSMartin Matuska 	"Extract: %p -x [options] [<patterns>]\n"
1094caf54c4fSMartin Matuska 	"  <patterns>  If specified, extract only entries that match\n"
1095caf54c4fSMartin Matuska 	"  -k    Keep (don't overwrite) existing files\n"
1096caf54c4fSMartin Matuska 	"  -m    Don't restore modification times\n"
1097caf54c4fSMartin Matuska 	"  -O    Write entries to stdout, don't restore to disk\n"
1098caf54c4fSMartin Matuska 	"  -p    Restore permissions (including ACLs, owner, file flags)\n";
1099caf54c4fSMartin Matuska 
1100caf54c4fSMartin Matuska 
1101caf54c4fSMartin Matuska /*
1102caf54c4fSMartin Matuska  * Note that the word 'bsdtar' will always appear in the first line
1103caf54c4fSMartin Matuska  * of output.
1104caf54c4fSMartin Matuska  *
1105caf54c4fSMartin Matuska  * In particular, /bin/sh scripts that need to test for the presence
1106caf54c4fSMartin Matuska  * of bsdtar can use the following template:
1107caf54c4fSMartin Matuska  *
1108caf54c4fSMartin Matuska  * if (tar --help 2>&1 | grep bsdtar >/dev/null 2>&1 ) then \
1109caf54c4fSMartin Matuska  *          echo bsdtar; else echo not bsdtar; fi
1110caf54c4fSMartin Matuska  */
1111caf54c4fSMartin Matuska static void
long_help(void)1112caf54c4fSMartin Matuska long_help(void)
1113caf54c4fSMartin Matuska {
1114caf54c4fSMartin Matuska 	const char	*prog;
1115caf54c4fSMartin Matuska 	const char	*p;
1116caf54c4fSMartin Matuska 
1117cdf63a70SMartin Matuska 	prog = lafe_getprogname();
1118caf54c4fSMartin Matuska 
1119caf54c4fSMartin Matuska 	fflush(stderr);
1120caf54c4fSMartin Matuska 
1121caf54c4fSMartin Matuska 	p = (strcmp(prog,"bsdtar") != 0) ? "(bsdtar)" : "";
1122caf54c4fSMartin Matuska 	printf("%s%s: manipulate archive files\n", prog, p);
1123caf54c4fSMartin Matuska 
1124caf54c4fSMartin Matuska 	for (p = long_help_msg; *p != '\0'; p++) {
1125caf54c4fSMartin Matuska 		if (*p == '%') {
1126caf54c4fSMartin Matuska 			if (p[1] == 'p') {
1127caf54c4fSMartin Matuska 				fputs(prog, stdout);
1128caf54c4fSMartin Matuska 				p++;
1129caf54c4fSMartin Matuska 			} else
1130caf54c4fSMartin Matuska 				putchar('%');
1131caf54c4fSMartin Matuska 		} else
1132caf54c4fSMartin Matuska 			putchar(*p);
1133caf54c4fSMartin Matuska 	}
1134caf54c4fSMartin Matuska 	version();
1135caf54c4fSMartin Matuska }
1136