1102245Sbde/*-
21556Srgrimes * Copyright (c) 1992 Keith Muller.
31556Srgrimes * Copyright (c) 1992, 1993
41556Srgrimes *	The Regents of the University of California.  All rights reserved.
51556Srgrimes *
61556Srgrimes * This code is derived from software contributed to Berkeley by
71556Srgrimes * Keith Muller of the University of California, San Diego.
81556Srgrimes *
91556Srgrimes * Redistribution and use in source and binary forms, with or without
101556Srgrimes * modification, are permitted provided that the following conditions
111556Srgrimes * are met:
121556Srgrimes * 1. Redistributions of source code must retain the above copyright
131556Srgrimes *    notice, this list of conditions and the following disclaimer.
141556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151556Srgrimes *    notice, this list of conditions and the following disclaimer in the
161556Srgrimes *    documentation and/or other materials provided with the distribution.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
321556Srgrimes */
331556Srgrimes
34102245Sbde#if 0
351556Srgrimes#ifndef lint
3636049Scharnierstatic char sccsid[] = "@(#)options.c	8.2 (Berkeley) 4/18/94";
37102245Sbde#endif /* not lint */
3836049Scharnier#endif
391556Srgrimes
4094553Scharnier#include <sys/cdefs.h>
4194553Scharnier__FBSDID("$FreeBSD$");
4294553Scharnier
431556Srgrimes#include <sys/types.h>
441556Srgrimes#include <sys/stat.h>
451556Srgrimes#include <sys/mtio.h>
461556Srgrimes#include <stdio.h>
471556Srgrimes#include <string.h>
4876351Skris#include <errno.h>
491556Srgrimes#include <unistd.h>
501556Srgrimes#include <stdlib.h>
511556Srgrimes#include <limits.h>
5276351Skris#include <paths.h>
531556Srgrimes#include "pax.h"
541556Srgrimes#include "options.h"
551556Srgrimes#include "cpio.h"
561556Srgrimes#include "tar.h"
571556Srgrimes#include "extern.h"
581556Srgrimes
591556Srgrimes/*
601556Srgrimes * Routines which handle command line options
611556Srgrimes */
621556Srgrimes
631556Srgrimesstatic char flgch[] = FLGCH;	/* list of all possible flags */
641556Srgrimesstatic OPLIST *ophead = NULL;	/* head for format specific options -x */
651556Srgrimesstatic OPLIST *optail = NULL;	/* option tail */
661556Srgrimes
6790110Simpstatic int no_op(void);
6890110Simpstatic void printflg(unsigned int);
6990110Simpstatic int c_frmt(const void *, const void *);
7090110Simpstatic off_t str_offt(char *);
7190110Simpstatic char *getline(FILE *fp);
7290113Simpstatic void pax_options(int, char **);
7390110Simpstatic void pax_usage(void);
7490113Simpstatic void tar_options(int, char **);
7590110Simpstatic void tar_usage(void);
7690113Simpstatic void cpio_options(int, char **);
7790110Simpstatic void cpio_usage(void);
781556Srgrimes
7976351Skris/* errors from getline */
8076351Skris#define GETLINE_FILE_CORRUPT 1
8176351Skris#define GETLINE_OUT_OF_MEM 2
8276351Skrisstatic int getline_error;
8376351Skris
84241720Sedchar *chdname;
8576351Skris
8676286Skris#define GZIP_CMD	"gzip"		/* command to run as gzip */
8776286Skris#define COMPRESS_CMD	"compress"	/* command to run as compress */
8876351Skris#define BZIP2_CMD	"bzip2"		/* command to run as gzip */
8976286Skris
901556Srgrimes/*
911556Srgrimes *	Format specific routine table - MUST BE IN SORTED ORDER BY NAME
921556Srgrimes *	(see pax.h for description of each function)
931556Srgrimes *
941556Srgrimes * 	name, blksz, hdsz, udev, hlk, blkagn, inhead, id, st_read,
951556Srgrimes *	read, end_read, st_write, write, end_write, trail,
961556Srgrimes *	rd_data, wr_data, options
971556Srgrimes */
981556Srgrimes
991556SrgrimesFSUB fsub[] = {
1001556Srgrimes/* 0: OLD BINARY CPIO */
1017165Sjoerg	{"bcpio", 5120, sizeof(HD_BCPIO), 1, 0, 0, 1, bcpio_id, cpio_strd,
1021556Srgrimes	bcpio_rd, bcpio_endrd, cpio_stwr, bcpio_wr, cpio_endwr, cpio_trail,
103114583Smarkm	NULL, rd_wrfile, wr_rdfile, bad_opt},
1041556Srgrimes
1051556Srgrimes/* 1: OLD OCTAL CHARACTER CPIO */
1067165Sjoerg	{"cpio", 5120, sizeof(HD_CPIO), 1, 0, 0, 1, cpio_id, cpio_strd,
1071556Srgrimes	cpio_rd, cpio_endrd, cpio_stwr, cpio_wr, cpio_endwr, cpio_trail,
108114583Smarkm	NULL, rd_wrfile, wr_rdfile, bad_opt},
1091556Srgrimes
1101556Srgrimes/* 2: SVR4 HEX CPIO */
1117165Sjoerg	{"sv4cpio", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, vcpio_id, cpio_strd,
1121556Srgrimes	vcpio_rd, vcpio_endrd, cpio_stwr, vcpio_wr, cpio_endwr, cpio_trail,
113114583Smarkm	NULL, rd_wrfile, wr_rdfile, bad_opt},
1141556Srgrimes
1151556Srgrimes/* 3: SVR4 HEX CPIO WITH CRC */
1167165Sjoerg	{"sv4crc", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, crc_id, crc_strd,
1171556Srgrimes	vcpio_rd, vcpio_endrd, crc_stwr, vcpio_wr, cpio_endwr, cpio_trail,
118114583Smarkm	NULL, rd_wrfile, wr_rdfile, bad_opt},
1191556Srgrimes
1201556Srgrimes/* 4: OLD TAR */
1217165Sjoerg	{"tar", 10240, BLKMULT, 0, 1, BLKMULT, 0, tar_id, no_op,
122114583Smarkm	tar_rd, tar_endrd, no_op, tar_wr, tar_endwr, NULL, tar_trail,
1237165Sjoerg	rd_wrfile, wr_rdfile, tar_opt},
1241556Srgrimes
1251556Srgrimes/* 5: POSIX USTAR */
1267165Sjoerg	{"ustar", 10240, BLKMULT, 0, 1, BLKMULT, 0, ustar_id, ustar_strd,
127114583Smarkm	ustar_rd, tar_endrd, ustar_stwr, ustar_wr, tar_endwr, NULL, tar_trail,
1287165Sjoerg	rd_wrfile, wr_rdfile, bad_opt},
1291556Srgrimes};
13076351Skris#define F_OCPIO	0	/* format when called as cpio -6 */
13176351Skris#define F_ACPIO	1	/* format when called as cpio -c */
13276351Skris#define F_CPIO	3	/* format when called as cpio */
13376351Skris#define F_OTAR	4	/* format when called as tar -o */
13476351Skris#define F_TAR	5	/* format when called as tar */
1351556Srgrimes#define DEFLT	5	/* default write format from list above */
1361556Srgrimes
1371556Srgrimes/*
1381556Srgrimes * ford is the archive search order used by get_arc() to determine what kind
1391556Srgrimes * of archive we are dealing with. This helps to properly id  archive formats
1401556Srgrimes * some formats may be subsets of others....
1411556Srgrimes */
1421556Srgrimesint ford[] = {5, 4, 3, 2, 1, 0, -1 };
1431556Srgrimes
1441556Srgrimes/*
1451556Srgrimes * options()
1461556Srgrimes *	figure out if we are pax, tar or cpio. Call the appropriate options
1471556Srgrimes *	parser
1481556Srgrimes */
1491556Srgrimes
1501556Srgrimesvoid
15190113Simpoptions(int argc, char **argv)
1521556Srgrimes{
1531556Srgrimes
1541556Srgrimes	/*
1551556Srgrimes	 * Are we acting like pax, tar or cpio (based on argv[0])
1561556Srgrimes	 */
1571556Srgrimes	if ((argv0 = strrchr(argv[0], '/')) != NULL)
1581556Srgrimes		argv0++;
1591556Srgrimes	else
1601556Srgrimes		argv0 = argv[0];
1611556Srgrimes
16294553Scharnier	if (strcmp(NM_TAR, argv0) == 0) {
16394553Scharnier		tar_options(argc, argv);
16494553Scharnier		return;
16594553Scharnier	}
16694553Scharnier	else if (strcmp(NM_CPIO, argv0) == 0) {
16794553Scharnier		cpio_options(argc, argv);
16894553Scharnier		return;
16994553Scharnier	}
1701556Srgrimes	/*
1711556Srgrimes	 * assume pax as the default
1721556Srgrimes	 */
1731556Srgrimes	argv0 = NM_PAX;
17494553Scharnier	pax_options(argc, argv);
17594553Scharnier	return;
1761556Srgrimes}
1771556Srgrimes
1781556Srgrimes/*
1791556Srgrimes * pax_options()
1801556Srgrimes *	look at the user specified flags. set globals as required and check if
1811556Srgrimes *	the user specified a legal set of flags. If not, complain and exit
1821556Srgrimes */
1831556Srgrimes
1841556Srgrimesstatic void
18590113Simppax_options(int argc, char **argv)
1861556Srgrimes{
18790113Simp	int c;
188114469Sobrien	size_t i;
1891556Srgrimes	unsigned int flg = 0;
1901556Srgrimes	unsigned int bflg = 0;
19190113Simp	char *pt;
19276019Skris	FSUB tmp;
1931556Srgrimes
1941556Srgrimes	/*
1951556Srgrimes	 * process option flags
1961556Srgrimes	 */
19776286Skris	while ((c=getopt(argc,argv,"ab:cdf:iklno:p:rs:tuvwx:zB:DE:G:HLPT:U:XYZ"))
19824348Simp	    != -1) {
1991556Srgrimes		switch (c) {
2001556Srgrimes		case 'a':
2011556Srgrimes			/*
2021556Srgrimes			 * append
2031556Srgrimes			 */
2041556Srgrimes			flg |= AF;
2051556Srgrimes			break;
2061556Srgrimes		case 'b':
2071556Srgrimes			/*
2081556Srgrimes			 * specify blocksize
2091556Srgrimes			 */
2101556Srgrimes			flg |= BF;
2111556Srgrimes			if ((wrblksz = (int)str_offt(optarg)) <= 0) {
21276017Skris				paxwarn(1, "Invalid block size %s", optarg);
2131556Srgrimes				pax_usage();
2141556Srgrimes			}
2151556Srgrimes			break;
2161556Srgrimes		case 'c':
2171556Srgrimes			/*
2181556Srgrimes			 * inverse match on patterns
2191556Srgrimes			 */
2201556Srgrimes			cflag = 1;
2211556Srgrimes			flg |= CF;
2221556Srgrimes			break;
2231556Srgrimes		case 'd':
2241556Srgrimes			/*
2251556Srgrimes			 * match only dir on extract, not the subtree at dir
2261556Srgrimes			 */
2271556Srgrimes			dflag = 1;
2281556Srgrimes			flg |= DF;
2291556Srgrimes			break;
2301556Srgrimes		case 'f':
2311556Srgrimes			/*
2321556Srgrimes			 * filename where the archive is stored
2331556Srgrimes			 */
2341556Srgrimes			arcname = optarg;
2351556Srgrimes			flg |= FF;
2361556Srgrimes			break;
2371556Srgrimes		case 'i':
2381556Srgrimes			/*
2391556Srgrimes			 * interactive file rename
2401556Srgrimes			 */
2411556Srgrimes			iflag = 1;
2421556Srgrimes			flg |= IF;
2431556Srgrimes			break;
2441556Srgrimes		case 'k':
2451556Srgrimes			/*
2461556Srgrimes			 * do not clobber files that exist
2471556Srgrimes			 */
2481556Srgrimes			kflag = 1;
2491556Srgrimes			flg |= KF;
2501556Srgrimes			break;
2511556Srgrimes		case 'l':
2521556Srgrimes			/*
2531556Srgrimes			 * try to link src to dest with copy (-rw)
2541556Srgrimes			 */
2551556Srgrimes			lflag = 1;
2561556Srgrimes			flg |= LF;
2571556Srgrimes			break;
2581556Srgrimes		case 'n':
2591556Srgrimes			/*
2601556Srgrimes			 * select first match for a pattern only
2611556Srgrimes			 */
2621556Srgrimes			nflag = 1;
2631556Srgrimes			flg |= NF;
2641556Srgrimes			break;
2651556Srgrimes		case 'o':
2661556Srgrimes			/*
2671556Srgrimes			 * pass format specific options
2681556Srgrimes			 */
2691556Srgrimes			flg |= OF;
2701556Srgrimes			if (opt_add(optarg) < 0)
2711556Srgrimes				pax_usage();
2721556Srgrimes			break;
2731556Srgrimes		case 'p':
2741556Srgrimes			/*
2751556Srgrimes			 * specify file characteristic options
2761556Srgrimes			 */
2771556Srgrimes			for (pt = optarg; *pt != '\0'; ++pt) {
2781556Srgrimes				switch(*pt) {
2791556Srgrimes				case 'a':
2801556Srgrimes					/*
2811556Srgrimes					 * do not preserve access time
2821556Srgrimes					 */
2831556Srgrimes					patime = 0;
2841556Srgrimes					break;
2851556Srgrimes				case 'e':
2861556Srgrimes					/*
2871556Srgrimes					 * preserve user id, group id, file
2881556Srgrimes					 * mode, access/modification times
2891556Srgrimes					 */
2901556Srgrimes					pids = 1;
2911556Srgrimes					pmode = 1;
2921556Srgrimes					patime = 1;
2931556Srgrimes					pmtime = 1;
2941556Srgrimes					break;
2951556Srgrimes				case 'm':
2961556Srgrimes					/*
2971556Srgrimes					 * do not preserve modification time
2981556Srgrimes					 */
2991556Srgrimes					pmtime = 0;
3001556Srgrimes					break;
3011556Srgrimes				case 'o':
3021556Srgrimes					/*
3031556Srgrimes					 * preserve uid/gid
3041556Srgrimes					 */
3051556Srgrimes					pids = 1;
3061556Srgrimes					break;
3071556Srgrimes				case 'p':
3081556Srgrimes					/*
3091556Srgrimes					 * preserver file mode bits
3101556Srgrimes					 */
3111556Srgrimes					pmode = 1;
3121556Srgrimes					break;
3131556Srgrimes				default:
31476017Skris					paxwarn(1, "Invalid -p string: %c", *pt);
3151556Srgrimes					pax_usage();
3161556Srgrimes					break;
3171556Srgrimes				}
3181556Srgrimes			}
3191556Srgrimes			flg |= PF;
3201556Srgrimes			break;
3211556Srgrimes		case 'r':
3221556Srgrimes			/*
3231556Srgrimes			 * read the archive
3241556Srgrimes			 */
3251556Srgrimes			flg |= RF;
3261556Srgrimes			break;
3271556Srgrimes		case 's':
3281556Srgrimes			/*
3291556Srgrimes			 * file name substitution name pattern
3301556Srgrimes			 */
3311556Srgrimes			if (rep_add(optarg) < 0) {
3321556Srgrimes				pax_usage();
3331556Srgrimes				break;
3341556Srgrimes			}
3351556Srgrimes			flg |= SF;
3361556Srgrimes			break;
3371556Srgrimes		case 't':
3381556Srgrimes			/*
339102230Strhodes			 * preserve access time on file system nodes we read
3401556Srgrimes			 */
3411556Srgrimes			tflag = 1;
3421556Srgrimes			flg |= TF;
3431556Srgrimes			break;
3441556Srgrimes		case 'u':
3451556Srgrimes			/*
3461556Srgrimes			 * ignore those older files
3471556Srgrimes			 */
3481556Srgrimes			uflag = 1;
3491556Srgrimes			flg |= UF;
3501556Srgrimes			break;
3511556Srgrimes		case 'v':
3521556Srgrimes			/*
3531556Srgrimes			 * verbose operation mode
3541556Srgrimes			 */
3551556Srgrimes			vflag = 1;
3561556Srgrimes			flg |= VF;
3571556Srgrimes			break;
3581556Srgrimes		case 'w':
3591556Srgrimes			/*
3601556Srgrimes			 * write an archive
3611556Srgrimes			 */
3621556Srgrimes			flg |= WF;
3631556Srgrimes			break;
3641556Srgrimes		case 'x':
3651556Srgrimes			/*
3661556Srgrimes			 * specify an archive format on write
3671556Srgrimes			 */
3681556Srgrimes			tmp.name = optarg;
3697165Sjoerg			if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub,
37076351Skris			    sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) != NULL) {
3711556Srgrimes				flg |= XF;
3721556Srgrimes				break;
3731556Srgrimes			}
37476017Skris			paxwarn(1, "Unknown -x format: %s", optarg);
3751556Srgrimes			(void)fputs("pax: Known -x formats are:", stderr);
3761556Srgrimes			for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i)
3771556Srgrimes				(void)fprintf(stderr, " %s", fsub[i].name);
3781556Srgrimes			(void)fputs("\n\n", stderr);
3791556Srgrimes			pax_usage();
3801556Srgrimes			break;
38176286Skris		case 'z':
38276286Skris			/*
38376286Skris			 * use gzip.  Non standard option.
38476286Skris			 */
38576286Skris			gzip_program = GZIP_CMD;
38676286Skris			break;
3871556Srgrimes		case 'B':
3881556Srgrimes			/*
3891556Srgrimes			 * non-standard option on number of bytes written on a
3901556Srgrimes			 * single archive volume.
3911556Srgrimes			 */
3921556Srgrimes			if ((wrlimit = str_offt(optarg)) <= 0) {
39376017Skris				paxwarn(1, "Invalid write limit %s", optarg);
3941556Srgrimes				pax_usage();
3951556Srgrimes			}
3961556Srgrimes			if (wrlimit % BLKMULT) {
39776017Skris				paxwarn(1, "Write limit is not a %d byte multiple",
3981556Srgrimes				    BLKMULT);
3991556Srgrimes				pax_usage();
4001556Srgrimes			}
4011556Srgrimes			flg |= CBF;
4021556Srgrimes			break;
4031556Srgrimes		case 'D':
4041556Srgrimes			/*
4051556Srgrimes			 * On extraction check file inode change time before the
4061556Srgrimes			 * modification of the file name. Non standard option.
4071556Srgrimes			 */
4081556Srgrimes			Dflag = 1;
4091556Srgrimes			flg |= CDF;
4101556Srgrimes			break;
4111556Srgrimes		case 'E':
4121556Srgrimes			/*
4131556Srgrimes			 * non-standard limit on read faults
4141556Srgrimes			 * 0 indicates stop after first error, values
4151556Srgrimes			 * indicate a limit, "NONE" try forever
4161556Srgrimes			 */
4171556Srgrimes			flg |= CEF;
4181556Srgrimes			if (strcmp(NONE, optarg) == 0)
4191556Srgrimes				maxflt = -1;
4201556Srgrimes			else if ((maxflt = atoi(optarg)) < 0) {
42176017Skris				paxwarn(1, "Error count value must be positive");
4221556Srgrimes				pax_usage();
4231556Srgrimes			}
4241556Srgrimes			break;
4251556Srgrimes		case 'G':
4261556Srgrimes			/*
4271556Srgrimes			 * non-standard option for selecting files within an
4281556Srgrimes			 * archive by group (gid or name)
4291556Srgrimes			 */
4301556Srgrimes			if (grp_add(optarg) < 0) {
4311556Srgrimes				pax_usage();
4321556Srgrimes				break;
4331556Srgrimes			}
4341556Srgrimes			flg |= CGF;
4351556Srgrimes			break;
4361556Srgrimes		case 'H':
4371556Srgrimes			/*
4381556Srgrimes			 * follow command line symlinks only
4391556Srgrimes			 */
4401556Srgrimes			Hflag = 1;
4411556Srgrimes			flg |= CHF;
4421556Srgrimes			break;
4431556Srgrimes		case 'L':
4441556Srgrimes			/*
4451556Srgrimes			 * follow symlinks
4461556Srgrimes			 */
4471556Srgrimes			Lflag = 1;
4481556Srgrimes			flg |= CLF;
4491556Srgrimes			break;
4501556Srgrimes		case 'P':
4511556Srgrimes			/*
4521556Srgrimes			 * do NOT follow symlinks (default)
4531556Srgrimes			 */
4541556Srgrimes			Lflag = 0;
4551556Srgrimes			flg |= CPF;
4561556Srgrimes			break;
4571556Srgrimes		case 'T':
4581556Srgrimes			/*
4591556Srgrimes			 * non-standard option for selecting files within an
4601556Srgrimes			 * archive by modification time range (lower,upper)
4611556Srgrimes			 */
4621556Srgrimes			if (trng_add(optarg) < 0) {
4631556Srgrimes				pax_usage();
4641556Srgrimes				break;
4651556Srgrimes			}
4661556Srgrimes			flg |= CTF;
4671556Srgrimes			break;
4681556Srgrimes		case 'U':
4691556Srgrimes			/*
4701556Srgrimes			 * non-standard option for selecting files within an
4711556Srgrimes			 * archive by user (uid or name)
4721556Srgrimes			 */
4731556Srgrimes			if (usr_add(optarg) < 0) {
4741556Srgrimes				pax_usage();
4751556Srgrimes				break;
4761556Srgrimes			}
4771556Srgrimes			flg |= CUF;
4781556Srgrimes			break;
4791556Srgrimes		case 'X':
4801556Srgrimes			/*
481102230Strhodes			 * do not pass over mount points in the file system
4821556Srgrimes			 */
4831556Srgrimes			Xflag = 1;
4841556Srgrimes			flg |= CXF;
4851556Srgrimes			break;
4861556Srgrimes		case 'Y':
4871556Srgrimes			/*
4881556Srgrimes			 * On extraction check file inode change time after the
4891556Srgrimes			 * modification of the file name. Non standard option.
4901556Srgrimes			 */
4911556Srgrimes			Yflag = 1;
4921556Srgrimes			flg |= CYF;
4931556Srgrimes			break;
4941556Srgrimes		case 'Z':
4951556Srgrimes			/*
4961556Srgrimes			 * On extraction check modification time after the
4971556Srgrimes			 * modification of the file name. Non standard option.
4981556Srgrimes			 */
4991556Srgrimes			Zflag = 1;
5001556Srgrimes			flg |= CZF;
5011556Srgrimes			break;
5021556Srgrimes		default:
5031556Srgrimes			pax_usage();
5041556Srgrimes			break;
5051556Srgrimes		}
5061556Srgrimes	}
5071556Srgrimes
5081556Srgrimes	/*
5091556Srgrimes	 * figure out the operation mode of pax read,write,extract,copy,append
5101556Srgrimes	 * or list. check that we have not been given a bogus set of flags
5111556Srgrimes	 * for the operation mode.
5121556Srgrimes	 */
5131556Srgrimes	if (ISLIST(flg)) {
5141556Srgrimes		act = LIST;
51576351Skris		listf = stdout;
5161556Srgrimes		bflg = flg & BDLIST;
5171556Srgrimes	} else if (ISEXTRACT(flg)) {
5181556Srgrimes		act = EXTRACT;
5191556Srgrimes		bflg = flg & BDEXTR;
5201556Srgrimes	} else if (ISARCHIVE(flg)) {
5211556Srgrimes		act = ARCHIVE;
5221556Srgrimes		bflg = flg & BDARCH;
5231556Srgrimes	} else if (ISAPPND(flg)) {
5241556Srgrimes		act = APPND;
5251556Srgrimes		bflg = flg & BDARCH;
5261556Srgrimes	} else if (ISCOPY(flg)) {
5271556Srgrimes		act = COPY;
5281556Srgrimes		bflg = flg & BDCOPY;
5291556Srgrimes	} else
5301556Srgrimes		pax_usage();
5311556Srgrimes	if (bflg) {
5321556Srgrimes		printflg(flg);
5331556Srgrimes		pax_usage();
5341556Srgrimes	}
5351556Srgrimes
5361556Srgrimes	/*
5371556Srgrimes	 * if we are writing (ARCHIVE) we use the default format if the user
5381556Srgrimes	 * did not specify a format. when we write during an APPEND, we will
5391556Srgrimes	 * adopt the format of the existing archive if none was supplied.
5401556Srgrimes	 */
5411556Srgrimes	if (!(flg & XF) && (act == ARCHIVE))
5421556Srgrimes		frmt = &(fsub[DEFLT]);
5431556Srgrimes
5441556Srgrimes	/*
5451556Srgrimes	 * process the args as they are interpreted by the operation mode
5461556Srgrimes	 */
5471556Srgrimes	switch (act) {
5481556Srgrimes	case LIST:
5491556Srgrimes	case EXTRACT:
5501556Srgrimes		for (; optind < argc; optind++)
55176351Skris			if (pat_add(argv[optind], NULL) < 0)
5521556Srgrimes				pax_usage();
5531556Srgrimes		break;
5541556Srgrimes	case COPY:
5551556Srgrimes		if (optind >= argc) {
55676017Skris			paxwarn(0, "Destination directory was not supplied");
5571556Srgrimes			pax_usage();
5581556Srgrimes		}
5591556Srgrimes		--argc;
5601556Srgrimes		dirptr = argv[argc];
56194553Scharnier		/* FALLTHROUGH */
5621556Srgrimes	case ARCHIVE:
5631556Srgrimes	case APPND:
5641556Srgrimes		for (; optind < argc; optind++)
56576351Skris			if (ftree_add(argv[optind], 0) < 0)
5661556Srgrimes				pax_usage();
5671556Srgrimes		/*
5681556Srgrimes		 * no read errors allowed on updates/append operation!
5691556Srgrimes		 */
5701556Srgrimes		maxflt = 0;
5711556Srgrimes		break;
5721556Srgrimes	}
5731556Srgrimes}
5741556Srgrimes
5751556Srgrimes
5761556Srgrimes/*
5771556Srgrimes * tar_options()
5781556Srgrimes *	look at the user specified flags. set globals as required and check if
5791556Srgrimes *	the user specified a legal set of flags. If not, complain and exit
5801556Srgrimes */
5811556Srgrimes
5821556Srgrimesstatic void
58390113Simptar_options(int argc, char **argv)
5841556Srgrimes{
58590113Simp	int c;
5861556Srgrimes	int fstdin = 0;
58776351Skris	int Oflag = 0;
58876351Skris	int nincfiles = 0;
58976351Skris	int incfiles_max = 0;
59076351Skris	struct incfile {
59176351Skris		char *file;
59276351Skris		char *dir;
59376351Skris	};
59476351Skris	struct incfile *incfiles = NULL;
5951556Srgrimes
5961556Srgrimes	/*
59776351Skris	 * Set default values.
59876351Skris	 */
59976351Skris	rmleadslash = 1;
60076351Skris
60176351Skris	/*
6021556Srgrimes	 * process option flags
6031556Srgrimes	 */
60476351Skris	while ((c = getoldopt(argc, argv,
60576351Skris	    "b:cef:hjmopqruts:vwxyzBC:HI:LOPXZ014578")) != -1) {
60676351Skris		switch(c) {
6071556Srgrimes		case 'b':
6081556Srgrimes			/*
60976351Skris			 * specify blocksize in 512-byte blocks
6101556Srgrimes			 */
61176351Skris			if ((wrblksz = (int)str_offt(optarg)) <= 0) {
61276351Skris				paxwarn(1, "Invalid block size %s", optarg);
6131556Srgrimes				tar_usage();
6141556Srgrimes			}
61576351Skris			wrblksz *= 512;		/* XXX - check for int oflow */
6161556Srgrimes			break;
6171556Srgrimes		case 'c':
6181556Srgrimes			/*
6191556Srgrimes			 * create an archive
6201556Srgrimes			 */
6211556Srgrimes			act = ARCHIVE;
6221556Srgrimes			break;
6231556Srgrimes		case 'e':
6241556Srgrimes			/*
6251556Srgrimes			 * stop after first error
6261556Srgrimes			 */
6271556Srgrimes			maxflt = 0;
6281556Srgrimes			break;
6291556Srgrimes		case 'f':
6301556Srgrimes			/*
6311556Srgrimes			 * filename where the archive is stored
6321556Srgrimes			 */
63376351Skris			if ((optarg[0] == '-') && (optarg[1]== '\0')) {
6341556Srgrimes				/*
6351556Srgrimes				 * treat a - as stdin
6361556Srgrimes				 */
63776351Skris				fstdin = 1;
63876351Skris				arcname = NULL;
6391556Srgrimes				break;
6401556Srgrimes			}
6411556Srgrimes			fstdin = 0;
64276351Skris			arcname = optarg;
6431556Srgrimes			break;
64476351Skris		case 'h':
64576351Skris			/*
64676351Skris			 * follow symlinks
64776351Skris			 */
64876351Skris			Lflag = 1;
64976351Skris			break;
65076351Skris		case 'j':
65176351Skris		case 'y':
65276351Skris			/*
65376351Skris			 * use bzip2.  Non standard option.
65476351Skris			 */
65576351Skris			gzip_program = BZIP2_CMD;
65676351Skris			break;
6571556Srgrimes		case 'm':
6581556Srgrimes			/*
6591556Srgrimes			 * do not preserve modification time
6601556Srgrimes			 */
6611556Srgrimes			pmtime = 0;
6621556Srgrimes			break;
6631556Srgrimes		case 'o':
6641556Srgrimes			if (opt_add("write_opt=nodir") < 0)
6651556Srgrimes				tar_usage();
66676351Skris		case 'O':
66776351Skris			Oflag = 1;
6681556Srgrimes			break;
6691556Srgrimes		case 'p':
6701556Srgrimes			/*
67176351Skris			 * preserve uid/gid and file mode, regardless of umask
6721556Srgrimes			 */
67376351Skris			pmode = 1;
6741556Srgrimes			pids = 1;
6751556Srgrimes			break;
67676351Skris		case 'q':
67776351Skris			/*
67876351Skris			 * select first match for a pattern only
67976351Skris			 */
68076351Skris			nflag = 1;
68176351Skris			break;
6821556Srgrimes		case 'r':
6831556Srgrimes		case 'u':
6841556Srgrimes			/*
6851556Srgrimes			 * append to the archive
6861556Srgrimes			 */
6871556Srgrimes			act = APPND;
6881556Srgrimes			break;
68976351Skris		case 's':
69076351Skris			/*
69176351Skris			 * file name substitution name pattern
69276351Skris			 */
69376351Skris			if (rep_add(optarg) < 0) {
69476351Skris				tar_usage();
69576351Skris				break;
69676351Skris			}
69776351Skris			break;
6981556Srgrimes		case 't':
6991556Srgrimes			/*
7001556Srgrimes			 * list contents of the tape
7011556Srgrimes			 */
7021556Srgrimes			act = LIST;
7031556Srgrimes			break;
7041556Srgrimes		case 'v':
7051556Srgrimes			/*
7061556Srgrimes			 * verbose operation mode
7071556Srgrimes			 */
70876351Skris			vflag++;
7091556Srgrimes			break;
7101556Srgrimes		case 'w':
7111556Srgrimes			/*
7121556Srgrimes			 * interactive file rename
7131556Srgrimes			 */
7141556Srgrimes			iflag = 1;
7151556Srgrimes			break;
7161556Srgrimes		case 'x':
7171556Srgrimes			/*
71876351Skris			 * extract an archive, preserving mode,
71976351Skris			 * and mtime if possible.
7201556Srgrimes			 */
7211556Srgrimes			act = EXTRACT;
72276351Skris			pmtime = 1;
7231556Srgrimes			break;
72476286Skris		case 'z':
72576286Skris			/*
72676286Skris			 * use gzip.  Non standard option.
72776286Skris			 */
72876286Skris			gzip_program = GZIP_CMD;
72976286Skris			break;
7301556Srgrimes		case 'B':
7311556Srgrimes			/*
7321556Srgrimes			 * Nothing to do here, this is pax default
7331556Srgrimes			 */
7341556Srgrimes			break;
73576351Skris		case 'C':
73676351Skris			chdname = optarg;
73776351Skris			break;
7381556Srgrimes		case 'H':
7391556Srgrimes			/*
7401556Srgrimes			 * follow command line symlinks only
7411556Srgrimes			 */
7421556Srgrimes			Hflag = 1;
7431556Srgrimes			break;
74476351Skris		case 'I':
74576351Skris			if (++nincfiles > incfiles_max) {
74676351Skris				incfiles_max = nincfiles + 3;
74776351Skris				incfiles = realloc(incfiles,
74876351Skris				    sizeof(*incfiles) * incfiles_max);
74976351Skris				if (incfiles == NULL) {
75076351Skris					paxwarn(0, "Unable to allocate space "
75176351Skris					    "for option list");
75276351Skris					exit(1);
75376351Skris				}
75476351Skris			}
75576351Skris			incfiles[nincfiles - 1].file = optarg;
75676351Skris			incfiles[nincfiles - 1].dir = chdname;
75776351Skris			break;
7581556Srgrimes		case 'L':
7591556Srgrimes			/*
7601556Srgrimes			 * follow symlinks
7611556Srgrimes			 */
7621556Srgrimes			Lflag = 1;
7631556Srgrimes			break;
7641556Srgrimes		case 'P':
7651556Srgrimes			/*
76676351Skris			 * do not remove leading '/' from pathnames
7671556Srgrimes			 */
76876351Skris			rmleadslash = 0;
7691556Srgrimes			break;
7701556Srgrimes		case 'X':
7711556Srgrimes			/*
772102230Strhodes			 * do not pass over mount points in the file system
7731556Srgrimes			 */
7741556Srgrimes			Xflag = 1;
7751556Srgrimes			break;
77676286Skris		case 'Z':
77776286Skris			/*
77876286Skris			 * use compress.
77976286Skris			 */
78076286Skris			gzip_program = COMPRESS_CMD;
78176286Skris			break;
7821556Srgrimes		case '0':
7831556Srgrimes			arcname = DEV_0;
7841556Srgrimes			break;
7851556Srgrimes		case '1':
7861556Srgrimes			arcname = DEV_1;
7871556Srgrimes			break;
7881556Srgrimes		case '4':
7891556Srgrimes			arcname = DEV_4;
7901556Srgrimes			break;
7911556Srgrimes		case '5':
7921556Srgrimes			arcname = DEV_5;
7931556Srgrimes			break;
7941556Srgrimes		case '7':
7951556Srgrimes			arcname = DEV_7;
7961556Srgrimes			break;
7971556Srgrimes		case '8':
7981556Srgrimes			arcname = DEV_8;
7991556Srgrimes			break;
8001556Srgrimes		default:
8011556Srgrimes			tar_usage();
8021556Srgrimes			break;
8031556Srgrimes		}
8041556Srgrimes	}
80576351Skris	argc -= optind;
80676351Skris	argv += optind;
8071556Srgrimes
80876351Skris	/* Traditional tar behaviour (pax uses stderr unless in list mode) */
80976351Skris	if (fstdin == 1 && act == ARCHIVE)
81076351Skris		listf = stderr;
81176351Skris	else
81276351Skris		listf = stdout;
81376351Skris
81476351Skris	/* Traditional tar behaviour (pax wants to read file list from stdin) */
81576351Skris	if ((act == ARCHIVE || act == APPND) && argc == 0 && nincfiles == 0)
81676351Skris		exit(0);
81776351Skris
8181556Srgrimes	/*
8191556Srgrimes	 * if we are writing (ARCHIVE) specify tar, otherwise run like pax
82076351Skris	 * (unless -o specified)
8211556Srgrimes	 */
82276351Skris	if (act == ARCHIVE || act == APPND)
82376351Skris		frmt = &(fsub[Oflag ? F_OTAR : F_TAR]);
82476351Skris	else if (Oflag) {
82576351Skris		paxwarn(1, "The -O/-o options are only valid when writing an archive");
82676351Skris		tar_usage();		/* only valid when writing */
82776351Skris	}
8281556Srgrimes
8291556Srgrimes	/*
8301556Srgrimes	 * process the args as they are interpreted by the operation mode
8311556Srgrimes	 */
8321556Srgrimes	switch (act) {
8331556Srgrimes	case LIST:
8341556Srgrimes	case EXTRACT:
8351556Srgrimes	default:
83676351Skris		{
83776351Skris			int sawpat = 0;
83876351Skris			char *file, *dir = NULL;
83976351Skris
84076351Skris			while (nincfiles || *argv != NULL) {
84176351Skris				/*
84276351Skris				 * If we queued up any include files,
84376351Skris				 * pull them in now.  Otherwise, check
84476351Skris				 * for -I and -C positional flags.
84576351Skris				 * Anything else must be a file to
84676351Skris				 * extract.
84776351Skris				 */
84876351Skris				if (nincfiles) {
84976351Skris					file = incfiles->file;
85076351Skris					dir = incfiles->dir;
85176351Skris					incfiles++;
85276351Skris					nincfiles--;
85376351Skris				} else if (strcmp(*argv, "-I") == 0) {
85476351Skris					if (*++argv == NULL)
85576351Skris						break;
85676351Skris					file = *argv++;
85776351Skris					dir = chdname;
85876351Skris				} else
85976351Skris					file = NULL;
86076351Skris				if (file != NULL) {
86176351Skris					FILE *fp;
86276351Skris					char *str;
86376351Skris
86476351Skris					if (strcmp(file, "-") == 0)
86576351Skris						fp = stdin;
86676351Skris					else if ((fp = fopen(file, "r")) == NULL) {
86776351Skris						paxwarn(1, "Unable to open file '%s' for read", file);
86876351Skris						tar_usage();
86976351Skris					}
87076351Skris					while ((str = getline(fp)) != NULL) {
87176351Skris						if (pat_add(str, dir) < 0)
87276351Skris							tar_usage();
87376351Skris						sawpat = 1;
87476351Skris					}
87576351Skris					if (strcmp(file, "-") != 0)
87676351Skris						fclose(fp);
87776351Skris					if (getline_error) {
87876351Skris						paxwarn(1, "Problem with file '%s'", file);
87976351Skris						tar_usage();
88076351Skris					}
88176351Skris				} else if (strcmp(*argv, "-C") == 0) {
88276351Skris					if (*++argv == NULL)
88376351Skris						break;
88476351Skris					chdname = *argv++;
88576351Skris				} else if (pat_add(*argv++, chdname) < 0)
88676351Skris					tar_usage();
88776351Skris				else
88876351Skris					sawpat = 1;
88976351Skris			}
89076351Skris			/*
891222177Suqs			 * if patterns were added, we are doing chdir()
89276351Skris			 * on a file-by-file basis, else, just one
89376351Skris			 * global chdir (if any) after opening input.
89476351Skris			 */
89576351Skris			if (sawpat > 0)
89676351Skris				chdname = NULL;
89776351Skris		}
8981556Srgrimes		break;
8991556Srgrimes	case ARCHIVE:
9001556Srgrimes	case APPND:
90176351Skris		if (chdname != NULL) {	/* initial chdir() */
90276351Skris			if (ftree_add(chdname, 1) < 0)
9031556Srgrimes				tar_usage();
90476351Skris		}
90576351Skris
90676351Skris		while (nincfiles || *argv != NULL) {
90776351Skris			char *file, *dir = NULL;
90876351Skris
90976351Skris			/*
91076351Skris			 * If we queued up any include files, pull them in
91176351Skris			 * now.  Otherwise, check for -I and -C positional
91276351Skris			 * flags.  Anything else must be a file to include
91376351Skris			 * in the archive.
91476351Skris			 */
91576351Skris			if (nincfiles) {
91676351Skris				file = incfiles->file;
91776351Skris				dir = incfiles->dir;
91876351Skris				incfiles++;
91976351Skris				nincfiles--;
92076351Skris			} else if (strcmp(*argv, "-I") == 0) {
92176351Skris				if (*++argv == NULL)
92276351Skris					break;
92376351Skris				file = *argv++;
92476351Skris				dir = NULL;
92576351Skris			} else
92676351Skris				file = NULL;
92776351Skris			if (file != NULL) {
92876351Skris				FILE *fp;
92976351Skris				char *str;
93076351Skris
93176351Skris				/* Set directory if needed */
93276351Skris				if (dir) {
93376351Skris					if (ftree_add(dir, 1) < 0)
93476351Skris						tar_usage();
93576351Skris				}
93676351Skris
93776351Skris				if (strcmp(file, "-") == 0)
93876351Skris					fp = stdin;
93976351Skris				else if ((fp = fopen(file, "r")) == NULL) {
94076351Skris					paxwarn(1, "Unable to open file '%s' for read", file);
94176351Skris					tar_usage();
94276351Skris				}
94376351Skris				while ((str = getline(fp)) != NULL) {
94476351Skris					if (ftree_add(str, 0) < 0)
94576351Skris						tar_usage();
94676351Skris				}
94776351Skris				if (strcmp(file, "-") != 0)
94876351Skris					fclose(fp);
94976351Skris				if (getline_error) {
95076351Skris					paxwarn(1, "Problem with file '%s'",
95176351Skris					    file);
95276351Skris					tar_usage();
95376351Skris				}
95476351Skris			} else if (strcmp(*argv, "-C") == 0) {
95576351Skris				if (*++argv == NULL)
95676351Skris					break;
95776351Skris				if (ftree_add(*argv++, 1) < 0)
95876351Skris					tar_usage();
95976351Skris			} else if (ftree_add(*argv++, 0) < 0)
96076351Skris				tar_usage();
96176351Skris		}
9621556Srgrimes		/*
9631556Srgrimes		 * no read errors allowed on updates/append operation!
9641556Srgrimes		 */
9651556Srgrimes		maxflt = 0;
9661556Srgrimes		break;
9671556Srgrimes	}
96876017Skris	if (!fstdin && ((arcname == NULL) || (*arcname == '\0'))) {
9691556Srgrimes		arcname = getenv("TAPE");
97076017Skris		if ((arcname == NULL) || (*arcname == '\0'))
97176351Skris			arcname = _PATH_DEFTAPE;
9721556Srgrimes	}
9731556Srgrimes}
9741556Srgrimes
975114583Smarkmstatic int
976114583Smarkmmkpath(char *path)
97776351Skris{
97876351Skris	struct stat sb;
97990113Simp	char *slash;
98076351Skris	int done = 0;
98176351Skris
98276351Skris	slash = path;
98376351Skris
98476351Skris	while (!done) {
98576351Skris		slash += strspn(slash, "/");
98676351Skris		slash += strcspn(slash, "/");
98776351Skris
98876351Skris		done = (*slash == '\0');
98976351Skris		*slash = '\0';
99076351Skris
99176351Skris		if (stat(path, &sb)) {
99276351Skris			if (errno != ENOENT || mkdir(path, 0777)) {
99376351Skris				paxwarn(1, "%s", path);
99476351Skris				return (-1);
99576351Skris			}
99676351Skris		} else if (!S_ISDIR(sb.st_mode)) {
99776351Skris			syswarn(1, ENOTDIR, "%s", path);
99876351Skris			return (-1);
99976351Skris		}
100076351Skris
100176351Skris		if (!done)
100276351Skris			*slash = '/';
100376351Skris	}
100476351Skris
100576351Skris	return (0);
100676351Skris}
10071556Srgrimes/*
10081556Srgrimes * cpio_options()
10091556Srgrimes *	look at the user specified flags. set globals as required and check if
10101556Srgrimes *	the user specified a legal set of flags. If not, complain and exit
10111556Srgrimes */
10121556Srgrimes
10131556Srgrimesstatic void
101490113Simpcpio_options(int argc, char **argv)
10151556Srgrimes{
1016114469Sobrien	int c;
1017114469Sobrien	size_t i;
101876351Skris	char *str;
101976351Skris	FSUB tmp;
102076351Skris	FILE *fp;
102176351Skris
102276351Skris	kflag = 1;
102376351Skris	pids = 1;
102476351Skris	pmode = 1;
102576351Skris	pmtime = 0;
102676351Skris	arcname = NULL;
102776351Skris	dflag = 1;
102876351Skris	act = -1;
102976351Skris	nodirs = 1;
103076351Skris	while ((c=getopt(argc,argv,"abcdfiklmoprstuvzABC:E:F:H:I:LO:SZ6")) != -1)
103176351Skris		switch (c) {
103276351Skris			case 'a':
103376351Skris				/*
103476351Skris				 * preserve access time on files read
103576351Skris				 */
103676351Skris				tflag = 1;
103776351Skris				break;
103876351Skris			case 'b':
103976351Skris				/*
104076351Skris				 * swap bytes and half-words when reading data
104176351Skris				 */
104276351Skris				break;
104376351Skris			case 'c':
104476351Skris				/*
104576351Skris				 * ASCII cpio header
104676351Skris				 */
104776351Skris				frmt = &(fsub[F_ACPIO]);
104876351Skris				break;
104976351Skris			case 'd':
105076351Skris				/*
105176351Skris				 * create directories as needed
105276351Skris				 */
105376351Skris				nodirs = 0;
105476351Skris				break;
105576351Skris			case 'f':
105676351Skris				/*
105776351Skris				 * invert meaning of pattern list
105876351Skris				 */
105976351Skris				cflag = 1;
106076351Skris				break;
106176351Skris			case 'i':
106276351Skris				/*
106376351Skris				 * restore an archive
106476351Skris				 */
106576351Skris				act = EXTRACT;
106676351Skris				break;
106776351Skris			case 'k':
106876351Skris				break;
106976351Skris			case 'l':
107076351Skris				/*
107176351Skris				 * use links instead of copies when possible
107276351Skris				 */
107376351Skris				lflag = 1;
107476351Skris				break;
107576351Skris			case 'm':
107676351Skris				/*
107776351Skris				 * preserve modification time
107876351Skris				 */
107976351Skris				pmtime = 1;
108076351Skris				break;
108176351Skris			case 'o':
108276351Skris				/*
108376351Skris				 * create an archive
108476351Skris				 */
108576351Skris				act = ARCHIVE;
108676351Skris				frmt = &(fsub[F_CPIO]);
108776351Skris				break;
108876351Skris			case 'p':
108976351Skris				/*
109076351Skris				 * copy-pass mode
109176351Skris				 */
109276351Skris				act = COPY;
109376351Skris				break;
109476351Skris			case 'r':
109576351Skris				/*
109676351Skris				 * interactively rename files
109776351Skris				 */
109876351Skris				iflag = 1;
109976351Skris				break;
110076351Skris			case 's':
110176351Skris				/*
110276351Skris				 * swap bytes after reading data
110376351Skris				 */
110476351Skris				break;
110576351Skris			case 't':
110676351Skris				/*
110776351Skris				 * list contents of archive
110876351Skris				 */
110976351Skris				act = LIST;
111076351Skris				listf = stdout;
111176351Skris				break;
111276351Skris			case 'u':
111376351Skris				/*
111476351Skris				 * replace newer files
111576351Skris				 */
111676351Skris				kflag = 0;
111776351Skris				break;
111876351Skris			case 'v':
111976351Skris				/*
112076351Skris				 * verbose operation mode
112176351Skris				 */
112276351Skris				vflag = 1;
112376351Skris				break;
112476351Skris			case 'z':
112576351Skris				/*
112676351Skris				 * use gzip.  Non standard option.
112776351Skris				 */
112876351Skris				gzip_program = GZIP_CMD;
112976351Skris				break;
113076351Skris			case 'A':
113176351Skris				/*
113276351Skris				 * append mode
113376351Skris				 */
113476351Skris				act = APPND;
113576351Skris				break;
113676351Skris			case 'B':
113776351Skris				/*
113876351Skris				 * Use 5120 byte block size
113976351Skris				 */
114076351Skris				wrblksz = 5120;
114176351Skris				break;
114276351Skris			case 'C':
114376351Skris				/*
114476351Skris				 * set block size in bytes
114576351Skris				 */
114676351Skris				wrblksz = atoi(optarg);
114776351Skris				break;
114876351Skris			case 'E':
114976351Skris				/*
115076351Skris				 * file with patterns to extract or list
115176351Skris				 */
115276351Skris				if ((fp = fopen(optarg, "r")) == NULL) {
115376351Skris					paxwarn(1, "Unable to open file '%s' for read", optarg);
115476351Skris					cpio_usage();
115576351Skris				}
115676351Skris				while ((str = getline(fp)) != NULL) {
115776351Skris					pat_add(str, NULL);
115876351Skris				}
115976351Skris				fclose(fp);
116076351Skris				if (getline_error) {
116176351Skris					paxwarn(1, "Problem with file '%s'", optarg);
116276351Skris					cpio_usage();
116376351Skris				}
116476351Skris				break;
116576351Skris			case 'F':
116676351Skris			case 'I':
116776351Skris			case 'O':
116876351Skris				/*
116976351Skris				 * filename where the archive is stored
117076351Skris				 */
117176351Skris				if ((optarg[0] == '-') && (optarg[1]== '\0')) {
117276351Skris					/*
117376351Skris					 * treat a - as stdin
117476351Skris					 */
117576351Skris					arcname = NULL;
117676351Skris					break;
117776351Skris				}
117876351Skris				arcname = optarg;
117976351Skris				break;
118076351Skris			case 'H':
118176351Skris				/*
118276351Skris				 * specify an archive format on write
118376351Skris				 */
118476351Skris				tmp.name = optarg;
118576351Skris				if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub,
118676351Skris				    sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) != NULL)
118776351Skris					break;
118876351Skris				paxwarn(1, "Unknown -H format: %s", optarg);
118976351Skris				(void)fputs("cpio: Known -H formats are:", stderr);
119076351Skris				for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i)
119176351Skris					(void)fprintf(stderr, " %s", fsub[i].name);
119276351Skris				(void)fputs("\n\n", stderr);
119376351Skris				cpio_usage();
119476351Skris				break;
119576351Skris			case 'L':
119676351Skris				/*
119776351Skris				 * follow symbolic links
119876351Skris				 */
119976351Skris				Lflag = 1;
120076351Skris				break;
120176351Skris			case 'S':
120276351Skris				/*
120376351Skris				 * swap halfwords after reading data
120476351Skris				 */
120576351Skris				break;
120676351Skris			case 'Z':
120776351Skris				/*
120876351Skris				 * use compress.  Non standard option.
120976351Skris				 */
121076351Skris				gzip_program = COMPRESS_CMD;
121176351Skris				break;
121276351Skris			case '6':
121376351Skris				/*
121476351Skris				 * process Version 6 cpio format
121576351Skris				 */
121676351Skris				frmt = &(fsub[F_OCPIO]);
121776351Skris				break;
121876351Skris			case '?':
121976351Skris			default:
122076351Skris				cpio_usage();
122176351Skris				break;
122276351Skris		}
122376351Skris	argc -= optind;
122476351Skris	argv += optind;
122576351Skris
122676351Skris	/*
122776351Skris	 * process the args as they are interpreted by the operation mode
122876351Skris	 */
122976351Skris	switch (act) {
123076351Skris		case LIST:
123176351Skris		case EXTRACT:
123276351Skris			while (*argv != NULL)
123376351Skris				if (pat_add(*argv++, NULL) < 0)
123476351Skris					cpio_usage();
123576351Skris			break;
123676351Skris		case COPY:
123776351Skris			if (*argv == NULL) {
123876351Skris				paxwarn(0, "Destination directory was not supplied");
123976351Skris				cpio_usage();
124076351Skris			}
124176351Skris			dirptr = *argv;
124276351Skris			if (mkpath(dirptr) < 0)
124376351Skris				cpio_usage();
124476351Skris			--argc;
124576351Skris			++argv;
124694553Scharnier			/* FALLTHROUGH */
124776351Skris		case ARCHIVE:
124876351Skris		case APPND:
124976351Skris			if (*argv != NULL)
125076351Skris				cpio_usage();
125176351Skris			/*
125276351Skris			 * no read errors allowed on updates/append operation!
125376351Skris			 */
125476351Skris			maxflt = 0;
125576351Skris			while ((str = getline(stdin)) != NULL) {
1256126643Smarkm				ftree_add(str, 0);
125776351Skris			}
125876351Skris			if (getline_error) {
125976351Skris				paxwarn(1, "Problem while reading stdin");
126076351Skris				cpio_usage();
126176351Skris			}
126276351Skris			break;
126376351Skris		default:
126476351Skris			cpio_usage();
126576351Skris			break;
126676351Skris	}
12671556Srgrimes}
12681556Srgrimes
12691556Srgrimes/*
12701556Srgrimes * printflg()
12711556Srgrimes *	print out those invalid flag sets found to the user
12721556Srgrimes */
12731556Srgrimes
12741556Srgrimesstatic void
12751556Srgrimesprintflg(unsigned int flg)
12761556Srgrimes{
12771556Srgrimes	int nxt;
12781556Srgrimes	int pos = 0;
12791556Srgrimes
12801556Srgrimes	(void)fprintf(stderr,"%s: Invalid combination of options:", argv0);
128176351Skris	while ((nxt = ffs(flg)) != 0) {
12821556Srgrimes		flg = flg >> nxt;
12831556Srgrimes		pos += nxt;
12841556Srgrimes		(void)fprintf(stderr, " -%c", flgch[pos-1]);
12851556Srgrimes	}
12861556Srgrimes	(void)putc('\n', stderr);
12871556Srgrimes}
12881556Srgrimes
12891556Srgrimes/*
12901556Srgrimes * c_frmt()
12911556Srgrimes *	comparison routine used by bsearch to find the format specified
12921556Srgrimes *	by the user
12931556Srgrimes */
12941556Srgrimes
12951556Srgrimesstatic int
12961556Srgrimesc_frmt(const void *a, const void *b)
12971556Srgrimes{
1298114583Smarkm	return(strcmp(((const FSUB *)a)->name, ((const FSUB *)b)->name));
12991556Srgrimes}
13001556Srgrimes
13011556Srgrimes/*
13021556Srgrimes * opt_next()
13031556Srgrimes *	called by format specific options routines to get each format specific
13041556Srgrimes *	flag and value specified with -o
13051556Srgrimes * Return:
13061556Srgrimes *	pointer to next OPLIST entry or NULL (end of list).
13071556Srgrimes */
13081556Srgrimes
13091556SrgrimesOPLIST *
13101556Srgrimesopt_next(void)
13111556Srgrimes{
13121556Srgrimes	OPLIST *opt;
13131556Srgrimes
13141556Srgrimes	if ((opt = ophead) != NULL)
13151556Srgrimes		ophead = ophead->fow;
13161556Srgrimes	return(opt);
13171556Srgrimes}
13181556Srgrimes
13191556Srgrimes/*
13201556Srgrimes * bad_opt()
13211556Srgrimes *	generic routine used to complain about a format specific options
13221556Srgrimes *	when the format does not support options.
13231556Srgrimes */
13241556Srgrimes
13251556Srgrimesint
13261556Srgrimesbad_opt(void)
13271556Srgrimes{
132890113Simp	OPLIST *opt;
13291556Srgrimes
13301556Srgrimes	if (ophead == NULL)
13311556Srgrimes		return(0);
13321556Srgrimes	/*
13331556Srgrimes	 * print all we were given
13341556Srgrimes	 */
133576017Skris	paxwarn(1,"These format options are not supported");
13361556Srgrimes	while ((opt = opt_next()) != NULL)
13371556Srgrimes		(void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value);
13381556Srgrimes	pax_usage();
13391556Srgrimes	return(0);
13401556Srgrimes}
13411556Srgrimes
13421556Srgrimes/*
13431556Srgrimes * opt_add()
1344108533Sschweikh *	breaks the value supplied to -o into an option name and value. Options
13451556Srgrimes *	are given to -o in the form -o name-value,name=value
134646684Skris *	multiple -o may be specified.
13471556Srgrimes * Return:
1348108533Sschweikh *	0 if format in name=value format, -1 if -o is passed junk.
13491556Srgrimes */
13501556Srgrimes
13511556Srgrimesint
1352114583Smarkmopt_add(const char *str)
13531556Srgrimes{
135490113Simp	OPLIST *opt;
135590113Simp	char *frpt;
135690113Simp	char *pt;
135790113Simp	char *endpt;
1358114583Smarkm	char *lstr;
13591556Srgrimes
13601556Srgrimes	if ((str == NULL) || (*str == '\0')) {
136176017Skris		paxwarn(0, "Invalid option name");
13621556Srgrimes		return(-1);
13631556Srgrimes	}
1364114583Smarkm	if ((lstr = strdup(str)) == NULL) {
136576351Skris		paxwarn(0, "Unable to allocate space for option list");
136676351Skris		return(-1);
136776351Skris	}
1368114583Smarkm	frpt = endpt = lstr;
13691556Srgrimes
13701556Srgrimes	/*
13711556Srgrimes	 * break into name and values pieces and stuff each one into a
13721556Srgrimes	 * OPLIST structure. When we know the format, the format specific
13731556Srgrimes	 * option function will go through this list
13741556Srgrimes	 */
13751556Srgrimes	while ((frpt != NULL) && (*frpt != '\0')) {
13761556Srgrimes		if ((endpt = strchr(frpt, ',')) != NULL)
13771556Srgrimes			*endpt = '\0';
13781556Srgrimes		if ((pt = strchr(frpt, '=')) == NULL) {
137976017Skris			paxwarn(0, "Invalid options format");
1380114583Smarkm			free(lstr);
13811556Srgrimes			return(-1);
13821556Srgrimes		}
13831556Srgrimes		if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) {
138476017Skris			paxwarn(0, "Unable to allocate space for option list");
1385114583Smarkm			free(lstr);
13861556Srgrimes			return(-1);
13871556Srgrimes		}
13881556Srgrimes		*pt++ = '\0';
13891556Srgrimes		opt->name = frpt;
13901556Srgrimes		opt->value = pt;
13911556Srgrimes		opt->fow = NULL;
13921556Srgrimes		if (endpt != NULL)
13931556Srgrimes			frpt = endpt + 1;
13941556Srgrimes		else
13951556Srgrimes			frpt = NULL;
13961556Srgrimes		if (ophead == NULL) {
13971556Srgrimes			optail = ophead = opt;
13981556Srgrimes			continue;
13991556Srgrimes		}
14001556Srgrimes		optail->fow = opt;
14011556Srgrimes		optail = opt;
14021556Srgrimes	}
14031556Srgrimes	return(0);
14041556Srgrimes}
14051556Srgrimes
14061556Srgrimes/*
14071556Srgrimes * str_offt()
14081556Srgrimes *	Convert an expression of the following forms to an off_t > 0.
14091556Srgrimes * 	1) A positive decimal number.
14101556Srgrimes *	2) A positive decimal number followed by a b (mult by 512).
14111556Srgrimes *	3) A positive decimal number followed by a k (mult by 1024).
14121556Srgrimes *	4) A positive decimal number followed by a m (mult by 512).
14131556Srgrimes *	5) A positive decimal number followed by a w (mult by sizeof int)
14141556Srgrimes *	6) Two or more positive decimal numbers (with/without k,b or w).
141572089Sasmodai *	   separated by x (also * for backwards compatibility), specifying
14161556Srgrimes *	   the product of the indicated values.
14171556Srgrimes * Return:
14181556Srgrimes *	0 for an error, a positive value o.w.
14191556Srgrimes */
14201556Srgrimes
14211556Srgrimesstatic off_t
14221556Srgrimesstr_offt(char *val)
14231556Srgrimes{
14241556Srgrimes	char *expr;
14251556Srgrimes	off_t num, t;
14261556Srgrimes
14271556Srgrimes#	ifdef NET2_STAT
14281556Srgrimes	num = strtol(val, &expr, 0);
14291556Srgrimes	if ((num == LONG_MAX) || (num <= 0) || (expr == val))
14301556Srgrimes#	else
14311556Srgrimes	num = strtoq(val, &expr, 0);
14321556Srgrimes	if ((num == QUAD_MAX) || (num <= 0) || (expr == val))
14331556Srgrimes#	endif
14341556Srgrimes		return(0);
14351556Srgrimes
14361556Srgrimes	switch(*expr) {
14371556Srgrimes	case 'b':
14381556Srgrimes		t = num;
14391556Srgrimes		num *= 512;
14401556Srgrimes		if (t > num)
14411556Srgrimes			return(0);
14421556Srgrimes		++expr;
14431556Srgrimes		break;
14441556Srgrimes	case 'k':
14451556Srgrimes		t = num;
14461556Srgrimes		num *= 1024;
14471556Srgrimes		if (t > num)
14481556Srgrimes			return(0);
14491556Srgrimes		++expr;
14501556Srgrimes		break;
14511556Srgrimes	case 'm':
14521556Srgrimes		t = num;
14531556Srgrimes		num *= 1048576;
14541556Srgrimes		if (t > num)
14551556Srgrimes			return(0);
14561556Srgrimes		++expr;
14571556Srgrimes		break;
14581556Srgrimes	case 'w':
14591556Srgrimes		t = num;
14601556Srgrimes		num *= sizeof(int);
14611556Srgrimes		if (t > num)
14621556Srgrimes			return(0);
14631556Srgrimes		++expr;
14641556Srgrimes		break;
14651556Srgrimes	}
14661556Srgrimes
14671556Srgrimes	switch(*expr) {
14681556Srgrimes		case '\0':
14691556Srgrimes			break;
14701556Srgrimes		case '*':
14711556Srgrimes		case 'x':
14721556Srgrimes			t = num;
14731556Srgrimes			num *= str_offt(expr + 1);
14741556Srgrimes			if (t > num)
14751556Srgrimes				return(0);
14761556Srgrimes			break;
14771556Srgrimes		default:
14781556Srgrimes			return(0);
14791556Srgrimes	}
14801556Srgrimes	return(num);
14811556Srgrimes}
14821556Srgrimes
148376351Skrischar *
148476351Skrisgetline(FILE *f)
148576351Skris{
148676351Skris	char *name, *temp;
148776351Skris	size_t len;
148876351Skris
148976351Skris	name = fgetln(f, &len);
149076351Skris	if (!name) {
149176351Skris		getline_error = ferror(f) ? GETLINE_FILE_CORRUPT : 0;
149276351Skris		return(0);
149376351Skris	}
149476351Skris	if (name[len-1] != '\n')
149576351Skris		len++;
149676351Skris	temp = malloc(len);
149776351Skris	if (!temp) {
149876351Skris		getline_error = GETLINE_OUT_OF_MEM;
149976351Skris		return(0);
150076351Skris	}
150176351Skris	memcpy(temp, name, len-1);
150276351Skris	temp[len-1] = 0;
150376351Skris	return(temp);
150476351Skris}
150576351Skris
15061556Srgrimes/*
15071556Srgrimes * no_op()
15081556Srgrimes *	for those option functions where the archive format has nothing to do.
15091556Srgrimes * Return:
15101556Srgrimes *	0
15111556Srgrimes */
15121556Srgrimes
15131556Srgrimesstatic int
15141556Srgrimesno_op(void)
15151556Srgrimes{
15161556Srgrimes	return(0);
15171556Srgrimes}
15181556Srgrimes
15191556Srgrimes/*
15201556Srgrimes * pax_usage()
15211556Srgrimes *	print the usage summary to the user
15221556Srgrimes */
15231556Srgrimes
15241556Srgrimesvoid
15251556Srgrimespax_usage(void)
15261556Srgrimes{
152776286Skris	(void)fputs("usage: pax [-cdnvz] [-E limit] [-f archive] ", stderr);
15281556Srgrimes	(void)fputs("[-s replstr] ... [-U user] ...", stderr);
152976019Skris	(void)fputs("\n	   [-G group] ... ", stderr);
15301556Srgrimes	(void)fputs("[-T [from_date][,to_date]] ... ", stderr);
15311556Srgrimes	(void)fputs("[pattern ...]\n", stderr);
153276286Skris	(void)fputs("       pax -r [-cdiknuvzDYZ] [-E limit] ", stderr);
15331556Srgrimes	(void)fputs("[-f archive] [-o options] ... \n", stderr);
153476019Skris	(void)fputs("	   [-p string] ... [-s replstr] ... ", stderr);
153576019Skris	(void)fputs("[-U user] ... [-G group] ...\n	   ", stderr);
15361556Srgrimes	(void)fputs("[-T [from_date][,to_date]] ... ", stderr);
15371556Srgrimes	(void)fputs(" [pattern ...]\n", stderr);
153876286Skris	(void)fputs("       pax -w [-dituvzHLPX] [-b blocksize] ", stderr);
15391556Srgrimes	(void)fputs("[ [-a] [-f archive] ] [-x format] \n", stderr);
154076019Skris	(void)fputs("	   [-B bytes] [-s replstr] ... ", stderr);
15411556Srgrimes	(void)fputs("[-o options] ... [-U user] ...", stderr);
154276019Skris	(void)fputs("\n	   [-G group] ... ", stderr);
15431556Srgrimes	(void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr);
15441556Srgrimes	(void)fputs("[file ...]\n", stderr);
15451556Srgrimes	(void)fputs("       pax -r -w [-diklntuvDHLPXYZ] ", stderr);
15461556Srgrimes	(void)fputs("[-p string] ... [-s replstr] ...", stderr);
154776019Skris	(void)fputs("\n	   [-U user] ... [-G group] ... ", stderr);
15481556Srgrimes	(void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr);
154976019Skris	(void)fputs("\n	   [file ...] directory\n", stderr);
15501556Srgrimes	exit(1);
15511556Srgrimes}
15521556Srgrimes
15531556Srgrimes/*
15541556Srgrimes * tar_usage()
15551556Srgrimes *	print the usage summary to the user
15561556Srgrimes */
15571556Srgrimes
15581556Srgrimesvoid
15591556Srgrimestar_usage(void)
15601556Srgrimes{
156176351Skris	(void)fputs("usage: tar [-]{crtux}[-befhjmopqsvwyzHLOPXZ014578] [blocksize] ",
15621556Srgrimes		 stderr);
156376351Skris	(void)fputs("[archive] [replstr] [-C directory] [-I file] [file ...]\n",
156476351Skris	    stderr);
15651556Srgrimes	exit(1);
15661556Srgrimes}
15671556Srgrimes
15681556Srgrimes/*
15691556Srgrimes * cpio_usage()
15701556Srgrimes *	print the usage summary to the user
15711556Srgrimes */
15721556Srgrimes
15731556Srgrimesvoid
15741556Srgrimescpio_usage(void)
15751556Srgrimes{
157676351Skris	(void)fputs("usage: cpio -o [-aABcLvVzZ] [-C bytes] [-H format] [-O archive]\n", stderr);
157776351Skris	(void)fputs("               [-F archive] < name-list [> archive]\n", stderr);
157876351Skris	(void)fputs("       cpio -i [-bBcdfmnrsStuvVzZ6] [-C bytes] [-E file] [-H format]\n", stderr);
157976351Skris	(void)fputs("               [-I archive] [-F archive] [pattern...] [< archive]\n", stderr);
158076351Skris	(void)fputs("       cpio -p [-adlLmuvV] destination-directory < name-list\n", stderr);
15811556Srgrimes	exit(1);
15821556Srgrimes}
1583