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: releng/10.3/bin/pax/options.c 281661 2015-04-17 18:33:59Z eadler $");
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	 */
197281661Seadler	while ((c=getopt(argc,argv,"ab:cdf:iklno:p:rs:tuvwx:zB:DE:G:HLOPT: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;
450281661Seadler		case 'O':
451281661Seadler			/*
452281661Seadler			 * Force one volume. Non standard option.
453281661Seadler			 */
454281661Seadler			Oflag = 1;
455281661Seadler			break;
4561556Srgrimes		case 'P':
4571556Srgrimes			/*
4581556Srgrimes			 * do NOT follow symlinks (default)
4591556Srgrimes			 */
4601556Srgrimes			Lflag = 0;
4611556Srgrimes			flg |= CPF;
4621556Srgrimes			break;
4631556Srgrimes		case 'T':
4641556Srgrimes			/*
4651556Srgrimes			 * non-standard option for selecting files within an
4661556Srgrimes			 * archive by modification time range (lower,upper)
4671556Srgrimes			 */
4681556Srgrimes			if (trng_add(optarg) < 0) {
4691556Srgrimes				pax_usage();
4701556Srgrimes				break;
4711556Srgrimes			}
4721556Srgrimes			flg |= CTF;
4731556Srgrimes			break;
4741556Srgrimes		case 'U':
4751556Srgrimes			/*
4761556Srgrimes			 * non-standard option for selecting files within an
4771556Srgrimes			 * archive by user (uid or name)
4781556Srgrimes			 */
4791556Srgrimes			if (usr_add(optarg) < 0) {
4801556Srgrimes				pax_usage();
4811556Srgrimes				break;
4821556Srgrimes			}
4831556Srgrimes			flg |= CUF;
4841556Srgrimes			break;
4851556Srgrimes		case 'X':
4861556Srgrimes			/*
487102230Strhodes			 * do not pass over mount points in the file system
4881556Srgrimes			 */
4891556Srgrimes			Xflag = 1;
4901556Srgrimes			flg |= CXF;
4911556Srgrimes			break;
4921556Srgrimes		case 'Y':
4931556Srgrimes			/*
4941556Srgrimes			 * On extraction check file inode change time after the
4951556Srgrimes			 * modification of the file name. Non standard option.
4961556Srgrimes			 */
4971556Srgrimes			Yflag = 1;
4981556Srgrimes			flg |= CYF;
4991556Srgrimes			break;
5001556Srgrimes		case 'Z':
5011556Srgrimes			/*
5021556Srgrimes			 * On extraction check modification time after the
5031556Srgrimes			 * modification of the file name. Non standard option.
5041556Srgrimes			 */
5051556Srgrimes			Zflag = 1;
5061556Srgrimes			flg |= CZF;
5071556Srgrimes			break;
5081556Srgrimes		default:
5091556Srgrimes			pax_usage();
5101556Srgrimes			break;
5111556Srgrimes		}
5121556Srgrimes	}
5131556Srgrimes
5141556Srgrimes	/*
5151556Srgrimes	 * figure out the operation mode of pax read,write,extract,copy,append
5161556Srgrimes	 * or list. check that we have not been given a bogus set of flags
5171556Srgrimes	 * for the operation mode.
5181556Srgrimes	 */
5191556Srgrimes	if (ISLIST(flg)) {
5201556Srgrimes		act = LIST;
52176351Skris		listf = stdout;
5221556Srgrimes		bflg = flg & BDLIST;
5231556Srgrimes	} else if (ISEXTRACT(flg)) {
5241556Srgrimes		act = EXTRACT;
5251556Srgrimes		bflg = flg & BDEXTR;
5261556Srgrimes	} else if (ISARCHIVE(flg)) {
5271556Srgrimes		act = ARCHIVE;
5281556Srgrimes		bflg = flg & BDARCH;
5291556Srgrimes	} else if (ISAPPND(flg)) {
5301556Srgrimes		act = APPND;
5311556Srgrimes		bflg = flg & BDARCH;
5321556Srgrimes	} else if (ISCOPY(flg)) {
5331556Srgrimes		act = COPY;
5341556Srgrimes		bflg = flg & BDCOPY;
5351556Srgrimes	} else
5361556Srgrimes		pax_usage();
5371556Srgrimes	if (bflg) {
5381556Srgrimes		printflg(flg);
5391556Srgrimes		pax_usage();
5401556Srgrimes	}
5411556Srgrimes
5421556Srgrimes	/*
5431556Srgrimes	 * if we are writing (ARCHIVE) we use the default format if the user
5441556Srgrimes	 * did not specify a format. when we write during an APPEND, we will
5451556Srgrimes	 * adopt the format of the existing archive if none was supplied.
5461556Srgrimes	 */
5471556Srgrimes	if (!(flg & XF) && (act == ARCHIVE))
5481556Srgrimes		frmt = &(fsub[DEFLT]);
5491556Srgrimes
5501556Srgrimes	/*
5511556Srgrimes	 * process the args as they are interpreted by the operation mode
5521556Srgrimes	 */
5531556Srgrimes	switch (act) {
5541556Srgrimes	case LIST:
5551556Srgrimes	case EXTRACT:
5561556Srgrimes		for (; optind < argc; optind++)
55776351Skris			if (pat_add(argv[optind], NULL) < 0)
5581556Srgrimes				pax_usage();
5591556Srgrimes		break;
5601556Srgrimes	case COPY:
5611556Srgrimes		if (optind >= argc) {
56276017Skris			paxwarn(0, "Destination directory was not supplied");
5631556Srgrimes			pax_usage();
5641556Srgrimes		}
5651556Srgrimes		--argc;
5661556Srgrimes		dirptr = argv[argc];
56794553Scharnier		/* FALLTHROUGH */
5681556Srgrimes	case ARCHIVE:
5691556Srgrimes	case APPND:
5701556Srgrimes		for (; optind < argc; optind++)
57176351Skris			if (ftree_add(argv[optind], 0) < 0)
5721556Srgrimes				pax_usage();
5731556Srgrimes		/*
5741556Srgrimes		 * no read errors allowed on updates/append operation!
5751556Srgrimes		 */
5761556Srgrimes		maxflt = 0;
5771556Srgrimes		break;
5781556Srgrimes	}
5791556Srgrimes}
5801556Srgrimes
5811556Srgrimes
5821556Srgrimes/*
5831556Srgrimes * tar_options()
5841556Srgrimes *	look at the user specified flags. set globals as required and check if
5851556Srgrimes *	the user specified a legal set of flags. If not, complain and exit
5861556Srgrimes */
5871556Srgrimes
5881556Srgrimesstatic void
58990113Simptar_options(int argc, char **argv)
5901556Srgrimes{
59190113Simp	int c;
5921556Srgrimes	int fstdin = 0;
593281661Seadler	int tar_Oflag = 0;
59476351Skris	int nincfiles = 0;
59576351Skris	int incfiles_max = 0;
59676351Skris	struct incfile {
59776351Skris		char *file;
59876351Skris		char *dir;
59976351Skris	};
60076351Skris	struct incfile *incfiles = NULL;
6011556Srgrimes
6021556Srgrimes	/*
60376351Skris	 * Set default values.
60476351Skris	 */
60576351Skris	rmleadslash = 1;
60676351Skris
60776351Skris	/*
6081556Srgrimes	 * process option flags
6091556Srgrimes	 */
61076351Skris	while ((c = getoldopt(argc, argv,
61176351Skris	    "b:cef:hjmopqruts:vwxyzBC:HI:LOPXZ014578")) != -1) {
61276351Skris		switch(c) {
6131556Srgrimes		case 'b':
6141556Srgrimes			/*
61576351Skris			 * specify blocksize in 512-byte blocks
6161556Srgrimes			 */
61776351Skris			if ((wrblksz = (int)str_offt(optarg)) <= 0) {
61876351Skris				paxwarn(1, "Invalid block size %s", optarg);
6191556Srgrimes				tar_usage();
6201556Srgrimes			}
62176351Skris			wrblksz *= 512;		/* XXX - check for int oflow */
6221556Srgrimes			break;
6231556Srgrimes		case 'c':
6241556Srgrimes			/*
6251556Srgrimes			 * create an archive
6261556Srgrimes			 */
6271556Srgrimes			act = ARCHIVE;
6281556Srgrimes			break;
6291556Srgrimes		case 'e':
6301556Srgrimes			/*
6311556Srgrimes			 * stop after first error
6321556Srgrimes			 */
6331556Srgrimes			maxflt = 0;
6341556Srgrimes			break;
6351556Srgrimes		case 'f':
6361556Srgrimes			/*
6371556Srgrimes			 * filename where the archive is stored
6381556Srgrimes			 */
63976351Skris			if ((optarg[0] == '-') && (optarg[1]== '\0')) {
6401556Srgrimes				/*
6411556Srgrimes				 * treat a - as stdin
6421556Srgrimes				 */
64376351Skris				fstdin = 1;
64476351Skris				arcname = NULL;
6451556Srgrimes				break;
6461556Srgrimes			}
6471556Srgrimes			fstdin = 0;
64876351Skris			arcname = optarg;
6491556Srgrimes			break;
65076351Skris		case 'h':
65176351Skris			/*
65276351Skris			 * follow symlinks
65376351Skris			 */
65476351Skris			Lflag = 1;
65576351Skris			break;
65676351Skris		case 'j':
65776351Skris		case 'y':
65876351Skris			/*
65976351Skris			 * use bzip2.  Non standard option.
66076351Skris			 */
66176351Skris			gzip_program = BZIP2_CMD;
66276351Skris			break;
6631556Srgrimes		case 'm':
6641556Srgrimes			/*
6651556Srgrimes			 * do not preserve modification time
6661556Srgrimes			 */
6671556Srgrimes			pmtime = 0;
6681556Srgrimes			break;
6691556Srgrimes		case 'o':
6701556Srgrimes			if (opt_add("write_opt=nodir") < 0)
6711556Srgrimes				tar_usage();
67276351Skris		case 'O':
673281661Seadler			tar_Oflag = 1;
6741556Srgrimes			break;
6751556Srgrimes		case 'p':
6761556Srgrimes			/*
67776351Skris			 * preserve uid/gid and file mode, regardless of umask
6781556Srgrimes			 */
67976351Skris			pmode = 1;
6801556Srgrimes			pids = 1;
6811556Srgrimes			break;
68276351Skris		case 'q':
68376351Skris			/*
68476351Skris			 * select first match for a pattern only
68576351Skris			 */
68676351Skris			nflag = 1;
68776351Skris			break;
6881556Srgrimes		case 'r':
6891556Srgrimes		case 'u':
6901556Srgrimes			/*
6911556Srgrimes			 * append to the archive
6921556Srgrimes			 */
6931556Srgrimes			act = APPND;
6941556Srgrimes			break;
69576351Skris		case 's':
69676351Skris			/*
69776351Skris			 * file name substitution name pattern
69876351Skris			 */
69976351Skris			if (rep_add(optarg) < 0) {
70076351Skris				tar_usage();
70176351Skris				break;
70276351Skris			}
70376351Skris			break;
7041556Srgrimes		case 't':
7051556Srgrimes			/*
7061556Srgrimes			 * list contents of the tape
7071556Srgrimes			 */
7081556Srgrimes			act = LIST;
7091556Srgrimes			break;
7101556Srgrimes		case 'v':
7111556Srgrimes			/*
7121556Srgrimes			 * verbose operation mode
7131556Srgrimes			 */
71476351Skris			vflag++;
7151556Srgrimes			break;
7161556Srgrimes		case 'w':
7171556Srgrimes			/*
7181556Srgrimes			 * interactive file rename
7191556Srgrimes			 */
7201556Srgrimes			iflag = 1;
7211556Srgrimes			break;
7221556Srgrimes		case 'x':
7231556Srgrimes			/*
72476351Skris			 * extract an archive, preserving mode,
72576351Skris			 * and mtime if possible.
7261556Srgrimes			 */
7271556Srgrimes			act = EXTRACT;
72876351Skris			pmtime = 1;
7291556Srgrimes			break;
73076286Skris		case 'z':
73176286Skris			/*
73276286Skris			 * use gzip.  Non standard option.
73376286Skris			 */
73476286Skris			gzip_program = GZIP_CMD;
73576286Skris			break;
7361556Srgrimes		case 'B':
7371556Srgrimes			/*
7381556Srgrimes			 * Nothing to do here, this is pax default
7391556Srgrimes			 */
7401556Srgrimes			break;
74176351Skris		case 'C':
74276351Skris			chdname = optarg;
74376351Skris			break;
7441556Srgrimes		case 'H':
7451556Srgrimes			/*
7461556Srgrimes			 * follow command line symlinks only
7471556Srgrimes			 */
7481556Srgrimes			Hflag = 1;
7491556Srgrimes			break;
75076351Skris		case 'I':
75176351Skris			if (++nincfiles > incfiles_max) {
75276351Skris				incfiles_max = nincfiles + 3;
75376351Skris				incfiles = realloc(incfiles,
75476351Skris				    sizeof(*incfiles) * incfiles_max);
75576351Skris				if (incfiles == NULL) {
75676351Skris					paxwarn(0, "Unable to allocate space "
75776351Skris					    "for option list");
75876351Skris					exit(1);
75976351Skris				}
76076351Skris			}
76176351Skris			incfiles[nincfiles - 1].file = optarg;
76276351Skris			incfiles[nincfiles - 1].dir = chdname;
76376351Skris			break;
7641556Srgrimes		case 'L':
7651556Srgrimes			/*
7661556Srgrimes			 * follow symlinks
7671556Srgrimes			 */
7681556Srgrimes			Lflag = 1;
7691556Srgrimes			break;
7701556Srgrimes		case 'P':
7711556Srgrimes			/*
77276351Skris			 * do not remove leading '/' from pathnames
7731556Srgrimes			 */
77476351Skris			rmleadslash = 0;
7751556Srgrimes			break;
7761556Srgrimes		case 'X':
7771556Srgrimes			/*
778102230Strhodes			 * do not pass over mount points in the file system
7791556Srgrimes			 */
7801556Srgrimes			Xflag = 1;
7811556Srgrimes			break;
78276286Skris		case 'Z':
78376286Skris			/*
78476286Skris			 * use compress.
78576286Skris			 */
78676286Skris			gzip_program = COMPRESS_CMD;
78776286Skris			break;
7881556Srgrimes		case '0':
7891556Srgrimes			arcname = DEV_0;
7901556Srgrimes			break;
7911556Srgrimes		case '1':
7921556Srgrimes			arcname = DEV_1;
7931556Srgrimes			break;
7941556Srgrimes		case '4':
7951556Srgrimes			arcname = DEV_4;
7961556Srgrimes			break;
7971556Srgrimes		case '5':
7981556Srgrimes			arcname = DEV_5;
7991556Srgrimes			break;
8001556Srgrimes		case '7':
8011556Srgrimes			arcname = DEV_7;
8021556Srgrimes			break;
8031556Srgrimes		case '8':
8041556Srgrimes			arcname = DEV_8;
8051556Srgrimes			break;
8061556Srgrimes		default:
8071556Srgrimes			tar_usage();
8081556Srgrimes			break;
8091556Srgrimes		}
8101556Srgrimes	}
81176351Skris	argc -= optind;
81276351Skris	argv += optind;
8131556Srgrimes
81476351Skris	/* Traditional tar behaviour (pax uses stderr unless in list mode) */
81576351Skris	if (fstdin == 1 && act == ARCHIVE)
81676351Skris		listf = stderr;
81776351Skris	else
81876351Skris		listf = stdout;
81976351Skris
82076351Skris	/* Traditional tar behaviour (pax wants to read file list from stdin) */
82176351Skris	if ((act == ARCHIVE || act == APPND) && argc == 0 && nincfiles == 0)
82276351Skris		exit(0);
82376351Skris
8241556Srgrimes	/*
8251556Srgrimes	 * if we are writing (ARCHIVE) specify tar, otherwise run like pax
82676351Skris	 * (unless -o specified)
8271556Srgrimes	 */
82876351Skris	if (act == ARCHIVE || act == APPND)
829281661Seadler		frmt = &(fsub[tar_Oflag ? F_OTAR : F_TAR]);
830281661Seadler	else if (tar_Oflag) {
83176351Skris		paxwarn(1, "The -O/-o options are only valid when writing an archive");
83276351Skris		tar_usage();		/* only valid when writing */
83376351Skris	}
8341556Srgrimes
8351556Srgrimes	/*
8361556Srgrimes	 * process the args as they are interpreted by the operation mode
8371556Srgrimes	 */
8381556Srgrimes	switch (act) {
8391556Srgrimes	case LIST:
8401556Srgrimes	case EXTRACT:
8411556Srgrimes	default:
84276351Skris		{
84376351Skris			int sawpat = 0;
84476351Skris			char *file, *dir = NULL;
84576351Skris
84676351Skris			while (nincfiles || *argv != NULL) {
84776351Skris				/*
84876351Skris				 * If we queued up any include files,
84976351Skris				 * pull them in now.  Otherwise, check
85076351Skris				 * for -I and -C positional flags.
85176351Skris				 * Anything else must be a file to
85276351Skris				 * extract.
85376351Skris				 */
85476351Skris				if (nincfiles) {
85576351Skris					file = incfiles->file;
85676351Skris					dir = incfiles->dir;
85776351Skris					incfiles++;
85876351Skris					nincfiles--;
85976351Skris				} else if (strcmp(*argv, "-I") == 0) {
86076351Skris					if (*++argv == NULL)
86176351Skris						break;
86276351Skris					file = *argv++;
86376351Skris					dir = chdname;
86476351Skris				} else
86576351Skris					file = NULL;
86676351Skris				if (file != NULL) {
86776351Skris					FILE *fp;
86876351Skris					char *str;
86976351Skris
87076351Skris					if (strcmp(file, "-") == 0)
87176351Skris						fp = stdin;
87276351Skris					else if ((fp = fopen(file, "r")) == NULL) {
87376351Skris						paxwarn(1, "Unable to open file '%s' for read", file);
87476351Skris						tar_usage();
87576351Skris					}
87676351Skris					while ((str = getline(fp)) != NULL) {
87776351Skris						if (pat_add(str, dir) < 0)
87876351Skris							tar_usage();
87976351Skris						sawpat = 1;
88076351Skris					}
88176351Skris					if (strcmp(file, "-") != 0)
88276351Skris						fclose(fp);
88376351Skris					if (getline_error) {
88476351Skris						paxwarn(1, "Problem with file '%s'", file);
88576351Skris						tar_usage();
88676351Skris					}
88776351Skris				} else if (strcmp(*argv, "-C") == 0) {
88876351Skris					if (*++argv == NULL)
88976351Skris						break;
89076351Skris					chdname = *argv++;
89176351Skris				} else if (pat_add(*argv++, chdname) < 0)
89276351Skris					tar_usage();
89376351Skris				else
89476351Skris					sawpat = 1;
89576351Skris			}
89676351Skris			/*
897222177Suqs			 * if patterns were added, we are doing chdir()
89876351Skris			 * on a file-by-file basis, else, just one
89976351Skris			 * global chdir (if any) after opening input.
90076351Skris			 */
90176351Skris			if (sawpat > 0)
90276351Skris				chdname = NULL;
90376351Skris		}
9041556Srgrimes		break;
9051556Srgrimes	case ARCHIVE:
9061556Srgrimes	case APPND:
90776351Skris		if (chdname != NULL) {	/* initial chdir() */
90876351Skris			if (ftree_add(chdname, 1) < 0)
9091556Srgrimes				tar_usage();
91076351Skris		}
91176351Skris
91276351Skris		while (nincfiles || *argv != NULL) {
91376351Skris			char *file, *dir = NULL;
91476351Skris
91576351Skris			/*
91676351Skris			 * If we queued up any include files, pull them in
91776351Skris			 * now.  Otherwise, check for -I and -C positional
91876351Skris			 * flags.  Anything else must be a file to include
91976351Skris			 * in the archive.
92076351Skris			 */
92176351Skris			if (nincfiles) {
92276351Skris				file = incfiles->file;
92376351Skris				dir = incfiles->dir;
92476351Skris				incfiles++;
92576351Skris				nincfiles--;
92676351Skris			} else if (strcmp(*argv, "-I") == 0) {
92776351Skris				if (*++argv == NULL)
92876351Skris					break;
92976351Skris				file = *argv++;
93076351Skris				dir = NULL;
93176351Skris			} else
93276351Skris				file = NULL;
93376351Skris			if (file != NULL) {
93476351Skris				FILE *fp;
93576351Skris				char *str;
93676351Skris
93776351Skris				/* Set directory if needed */
93876351Skris				if (dir) {
93976351Skris					if (ftree_add(dir, 1) < 0)
94076351Skris						tar_usage();
94176351Skris				}
94276351Skris
94376351Skris				if (strcmp(file, "-") == 0)
94476351Skris					fp = stdin;
94576351Skris				else if ((fp = fopen(file, "r")) == NULL) {
94676351Skris					paxwarn(1, "Unable to open file '%s' for read", file);
94776351Skris					tar_usage();
94876351Skris				}
94976351Skris				while ((str = getline(fp)) != NULL) {
95076351Skris					if (ftree_add(str, 0) < 0)
95176351Skris						tar_usage();
95276351Skris				}
95376351Skris				if (strcmp(file, "-") != 0)
95476351Skris					fclose(fp);
95576351Skris				if (getline_error) {
95676351Skris					paxwarn(1, "Problem with file '%s'",
95776351Skris					    file);
95876351Skris					tar_usage();
95976351Skris				}
96076351Skris			} else if (strcmp(*argv, "-C") == 0) {
96176351Skris				if (*++argv == NULL)
96276351Skris					break;
96376351Skris				if (ftree_add(*argv++, 1) < 0)
96476351Skris					tar_usage();
96576351Skris			} else if (ftree_add(*argv++, 0) < 0)
96676351Skris				tar_usage();
96776351Skris		}
9681556Srgrimes		/*
9691556Srgrimes		 * no read errors allowed on updates/append operation!
9701556Srgrimes		 */
9711556Srgrimes		maxflt = 0;
9721556Srgrimes		break;
9731556Srgrimes	}
97476017Skris	if (!fstdin && ((arcname == NULL) || (*arcname == '\0'))) {
9751556Srgrimes		arcname = getenv("TAPE");
97676017Skris		if ((arcname == NULL) || (*arcname == '\0'))
97776351Skris			arcname = _PATH_DEFTAPE;
9781556Srgrimes	}
9791556Srgrimes}
9801556Srgrimes
981114583Smarkmstatic int
982114583Smarkmmkpath(char *path)
98376351Skris{
98476351Skris	struct stat sb;
98590113Simp	char *slash;
98676351Skris	int done = 0;
98776351Skris
98876351Skris	slash = path;
98976351Skris
99076351Skris	while (!done) {
99176351Skris		slash += strspn(slash, "/");
99276351Skris		slash += strcspn(slash, "/");
99376351Skris
99476351Skris		done = (*slash == '\0');
99576351Skris		*slash = '\0';
99676351Skris
99776351Skris		if (stat(path, &sb)) {
99876351Skris			if (errno != ENOENT || mkdir(path, 0777)) {
99976351Skris				paxwarn(1, "%s", path);
100076351Skris				return (-1);
100176351Skris			}
100276351Skris		} else if (!S_ISDIR(sb.st_mode)) {
100376351Skris			syswarn(1, ENOTDIR, "%s", path);
100476351Skris			return (-1);
100576351Skris		}
100676351Skris
100776351Skris		if (!done)
100876351Skris			*slash = '/';
100976351Skris	}
101076351Skris
101176351Skris	return (0);
101276351Skris}
10131556Srgrimes/*
10141556Srgrimes * cpio_options()
10151556Srgrimes *	look at the user specified flags. set globals as required and check if
10161556Srgrimes *	the user specified a legal set of flags. If not, complain and exit
10171556Srgrimes */
10181556Srgrimes
10191556Srgrimesstatic void
102090113Simpcpio_options(int argc, char **argv)
10211556Srgrimes{
1022114469Sobrien	int c;
1023114469Sobrien	size_t i;
102476351Skris	char *str;
102576351Skris	FSUB tmp;
102676351Skris	FILE *fp;
102776351Skris
102876351Skris	kflag = 1;
102976351Skris	pids = 1;
103076351Skris	pmode = 1;
103176351Skris	pmtime = 0;
103276351Skris	arcname = NULL;
103376351Skris	dflag = 1;
103476351Skris	act = -1;
103576351Skris	nodirs = 1;
103676351Skris	while ((c=getopt(argc,argv,"abcdfiklmoprstuvzABC:E:F:H:I:LO:SZ6")) != -1)
103776351Skris		switch (c) {
103876351Skris			case 'a':
103976351Skris				/*
104076351Skris				 * preserve access time on files read
104176351Skris				 */
104276351Skris				tflag = 1;
104376351Skris				break;
104476351Skris			case 'b':
104576351Skris				/*
104676351Skris				 * swap bytes and half-words when reading data
104776351Skris				 */
104876351Skris				break;
104976351Skris			case 'c':
105076351Skris				/*
105176351Skris				 * ASCII cpio header
105276351Skris				 */
105376351Skris				frmt = &(fsub[F_ACPIO]);
105476351Skris				break;
105576351Skris			case 'd':
105676351Skris				/*
105776351Skris				 * create directories as needed
105876351Skris				 */
105976351Skris				nodirs = 0;
106076351Skris				break;
106176351Skris			case 'f':
106276351Skris				/*
106376351Skris				 * invert meaning of pattern list
106476351Skris				 */
106576351Skris				cflag = 1;
106676351Skris				break;
106776351Skris			case 'i':
106876351Skris				/*
106976351Skris				 * restore an archive
107076351Skris				 */
107176351Skris				act = EXTRACT;
107276351Skris				break;
107376351Skris			case 'k':
107476351Skris				break;
107576351Skris			case 'l':
107676351Skris				/*
107776351Skris				 * use links instead of copies when possible
107876351Skris				 */
107976351Skris				lflag = 1;
108076351Skris				break;
108176351Skris			case 'm':
108276351Skris				/*
108376351Skris				 * preserve modification time
108476351Skris				 */
108576351Skris				pmtime = 1;
108676351Skris				break;
108776351Skris			case 'o':
108876351Skris				/*
108976351Skris				 * create an archive
109076351Skris				 */
109176351Skris				act = ARCHIVE;
109276351Skris				frmt = &(fsub[F_CPIO]);
109376351Skris				break;
109476351Skris			case 'p':
109576351Skris				/*
109676351Skris				 * copy-pass mode
109776351Skris				 */
109876351Skris				act = COPY;
109976351Skris				break;
110076351Skris			case 'r':
110176351Skris				/*
110276351Skris				 * interactively rename files
110376351Skris				 */
110476351Skris				iflag = 1;
110576351Skris				break;
110676351Skris			case 's':
110776351Skris				/*
110876351Skris				 * swap bytes after reading data
110976351Skris				 */
111076351Skris				break;
111176351Skris			case 't':
111276351Skris				/*
111376351Skris				 * list contents of archive
111476351Skris				 */
111576351Skris				act = LIST;
111676351Skris				listf = stdout;
111776351Skris				break;
111876351Skris			case 'u':
111976351Skris				/*
112076351Skris				 * replace newer files
112176351Skris				 */
112276351Skris				kflag = 0;
112376351Skris				break;
112476351Skris			case 'v':
112576351Skris				/*
112676351Skris				 * verbose operation mode
112776351Skris				 */
112876351Skris				vflag = 1;
112976351Skris				break;
113076351Skris			case 'z':
113176351Skris				/*
113276351Skris				 * use gzip.  Non standard option.
113376351Skris				 */
113476351Skris				gzip_program = GZIP_CMD;
113576351Skris				break;
113676351Skris			case 'A':
113776351Skris				/*
113876351Skris				 * append mode
113976351Skris				 */
114076351Skris				act = APPND;
114176351Skris				break;
114276351Skris			case 'B':
114376351Skris				/*
114476351Skris				 * Use 5120 byte block size
114576351Skris				 */
114676351Skris				wrblksz = 5120;
114776351Skris				break;
114876351Skris			case 'C':
114976351Skris				/*
115076351Skris				 * set block size in bytes
115176351Skris				 */
115276351Skris				wrblksz = atoi(optarg);
115376351Skris				break;
115476351Skris			case 'E':
115576351Skris				/*
115676351Skris				 * file with patterns to extract or list
115776351Skris				 */
115876351Skris				if ((fp = fopen(optarg, "r")) == NULL) {
115976351Skris					paxwarn(1, "Unable to open file '%s' for read", optarg);
116076351Skris					cpio_usage();
116176351Skris				}
116276351Skris				while ((str = getline(fp)) != NULL) {
116376351Skris					pat_add(str, NULL);
116476351Skris				}
116576351Skris				fclose(fp);
116676351Skris				if (getline_error) {
116776351Skris					paxwarn(1, "Problem with file '%s'", optarg);
116876351Skris					cpio_usage();
116976351Skris				}
117076351Skris				break;
117176351Skris			case 'F':
117276351Skris			case 'I':
117376351Skris			case 'O':
117476351Skris				/*
117576351Skris				 * filename where the archive is stored
117676351Skris				 */
117776351Skris				if ((optarg[0] == '-') && (optarg[1]== '\0')) {
117876351Skris					/*
117976351Skris					 * treat a - as stdin
118076351Skris					 */
118176351Skris					arcname = NULL;
118276351Skris					break;
118376351Skris				}
118476351Skris				arcname = optarg;
118576351Skris				break;
118676351Skris			case 'H':
118776351Skris				/*
118876351Skris				 * specify an archive format on write
118976351Skris				 */
119076351Skris				tmp.name = optarg;
119176351Skris				if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub,
119276351Skris				    sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) != NULL)
119376351Skris					break;
119476351Skris				paxwarn(1, "Unknown -H format: %s", optarg);
119576351Skris				(void)fputs("cpio: Known -H formats are:", stderr);
119676351Skris				for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i)
119776351Skris					(void)fprintf(stderr, " %s", fsub[i].name);
119876351Skris				(void)fputs("\n\n", stderr);
119976351Skris				cpio_usage();
120076351Skris				break;
120176351Skris			case 'L':
120276351Skris				/*
120376351Skris				 * follow symbolic links
120476351Skris				 */
120576351Skris				Lflag = 1;
120676351Skris				break;
120776351Skris			case 'S':
120876351Skris				/*
120976351Skris				 * swap halfwords after reading data
121076351Skris				 */
121176351Skris				break;
121276351Skris			case 'Z':
121376351Skris				/*
121476351Skris				 * use compress.  Non standard option.
121576351Skris				 */
121676351Skris				gzip_program = COMPRESS_CMD;
121776351Skris				break;
121876351Skris			case '6':
121976351Skris				/*
122076351Skris				 * process Version 6 cpio format
122176351Skris				 */
122276351Skris				frmt = &(fsub[F_OCPIO]);
122376351Skris				break;
122476351Skris			case '?':
122576351Skris			default:
122676351Skris				cpio_usage();
122776351Skris				break;
122876351Skris		}
122976351Skris	argc -= optind;
123076351Skris	argv += optind;
123176351Skris
123276351Skris	/*
123376351Skris	 * process the args as they are interpreted by the operation mode
123476351Skris	 */
123576351Skris	switch (act) {
123676351Skris		case LIST:
123776351Skris		case EXTRACT:
123876351Skris			while (*argv != NULL)
123976351Skris				if (pat_add(*argv++, NULL) < 0)
124076351Skris					cpio_usage();
124176351Skris			break;
124276351Skris		case COPY:
124376351Skris			if (*argv == NULL) {
124476351Skris				paxwarn(0, "Destination directory was not supplied");
124576351Skris				cpio_usage();
124676351Skris			}
124776351Skris			dirptr = *argv;
124876351Skris			if (mkpath(dirptr) < 0)
124976351Skris				cpio_usage();
125076351Skris			--argc;
125176351Skris			++argv;
125294553Scharnier			/* FALLTHROUGH */
125376351Skris		case ARCHIVE:
125476351Skris		case APPND:
125576351Skris			if (*argv != NULL)
125676351Skris				cpio_usage();
125776351Skris			/*
125876351Skris			 * no read errors allowed on updates/append operation!
125976351Skris			 */
126076351Skris			maxflt = 0;
126176351Skris			while ((str = getline(stdin)) != NULL) {
1262126643Smarkm				ftree_add(str, 0);
126376351Skris			}
126476351Skris			if (getline_error) {
126576351Skris				paxwarn(1, "Problem while reading stdin");
126676351Skris				cpio_usage();
126776351Skris			}
126876351Skris			break;
126976351Skris		default:
127076351Skris			cpio_usage();
127176351Skris			break;
127276351Skris	}
12731556Srgrimes}
12741556Srgrimes
12751556Srgrimes/*
12761556Srgrimes * printflg()
12771556Srgrimes *	print out those invalid flag sets found to the user
12781556Srgrimes */
12791556Srgrimes
12801556Srgrimesstatic void
12811556Srgrimesprintflg(unsigned int flg)
12821556Srgrimes{
12831556Srgrimes	int nxt;
12841556Srgrimes	int pos = 0;
12851556Srgrimes
12861556Srgrimes	(void)fprintf(stderr,"%s: Invalid combination of options:", argv0);
128776351Skris	while ((nxt = ffs(flg)) != 0) {
12881556Srgrimes		flg = flg >> nxt;
12891556Srgrimes		pos += nxt;
12901556Srgrimes		(void)fprintf(stderr, " -%c", flgch[pos-1]);
12911556Srgrimes	}
12921556Srgrimes	(void)putc('\n', stderr);
12931556Srgrimes}
12941556Srgrimes
12951556Srgrimes/*
12961556Srgrimes * c_frmt()
12971556Srgrimes *	comparison routine used by bsearch to find the format specified
12981556Srgrimes *	by the user
12991556Srgrimes */
13001556Srgrimes
13011556Srgrimesstatic int
13021556Srgrimesc_frmt(const void *a, const void *b)
13031556Srgrimes{
1304114583Smarkm	return(strcmp(((const FSUB *)a)->name, ((const FSUB *)b)->name));
13051556Srgrimes}
13061556Srgrimes
13071556Srgrimes/*
13081556Srgrimes * opt_next()
13091556Srgrimes *	called by format specific options routines to get each format specific
13101556Srgrimes *	flag and value specified with -o
13111556Srgrimes * Return:
13121556Srgrimes *	pointer to next OPLIST entry or NULL (end of list).
13131556Srgrimes */
13141556Srgrimes
13151556SrgrimesOPLIST *
13161556Srgrimesopt_next(void)
13171556Srgrimes{
13181556Srgrimes	OPLIST *opt;
13191556Srgrimes
13201556Srgrimes	if ((opt = ophead) != NULL)
13211556Srgrimes		ophead = ophead->fow;
13221556Srgrimes	return(opt);
13231556Srgrimes}
13241556Srgrimes
13251556Srgrimes/*
13261556Srgrimes * bad_opt()
13271556Srgrimes *	generic routine used to complain about a format specific options
13281556Srgrimes *	when the format does not support options.
13291556Srgrimes */
13301556Srgrimes
13311556Srgrimesint
13321556Srgrimesbad_opt(void)
13331556Srgrimes{
133490113Simp	OPLIST *opt;
13351556Srgrimes
13361556Srgrimes	if (ophead == NULL)
13371556Srgrimes		return(0);
13381556Srgrimes	/*
13391556Srgrimes	 * print all we were given
13401556Srgrimes	 */
134176017Skris	paxwarn(1,"These format options are not supported");
13421556Srgrimes	while ((opt = opt_next()) != NULL)
13431556Srgrimes		(void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value);
13441556Srgrimes	pax_usage();
13451556Srgrimes	return(0);
13461556Srgrimes}
13471556Srgrimes
13481556Srgrimes/*
13491556Srgrimes * opt_add()
1350108533Sschweikh *	breaks the value supplied to -o into an option name and value. Options
13511556Srgrimes *	are given to -o in the form -o name-value,name=value
135246684Skris *	multiple -o may be specified.
13531556Srgrimes * Return:
1354108533Sschweikh *	0 if format in name=value format, -1 if -o is passed junk.
13551556Srgrimes */
13561556Srgrimes
13571556Srgrimesint
1358114583Smarkmopt_add(const char *str)
13591556Srgrimes{
136090113Simp	OPLIST *opt;
136190113Simp	char *frpt;
136290113Simp	char *pt;
136390113Simp	char *endpt;
1364114583Smarkm	char *lstr;
13651556Srgrimes
13661556Srgrimes	if ((str == NULL) || (*str == '\0')) {
136776017Skris		paxwarn(0, "Invalid option name");
13681556Srgrimes		return(-1);
13691556Srgrimes	}
1370114583Smarkm	if ((lstr = strdup(str)) == NULL) {
137176351Skris		paxwarn(0, "Unable to allocate space for option list");
137276351Skris		return(-1);
137376351Skris	}
1374114583Smarkm	frpt = endpt = lstr;
13751556Srgrimes
13761556Srgrimes	/*
13771556Srgrimes	 * break into name and values pieces and stuff each one into a
13781556Srgrimes	 * OPLIST structure. When we know the format, the format specific
13791556Srgrimes	 * option function will go through this list
13801556Srgrimes	 */
13811556Srgrimes	while ((frpt != NULL) && (*frpt != '\0')) {
13821556Srgrimes		if ((endpt = strchr(frpt, ',')) != NULL)
13831556Srgrimes			*endpt = '\0';
13841556Srgrimes		if ((pt = strchr(frpt, '=')) == NULL) {
138576017Skris			paxwarn(0, "Invalid options format");
1386114583Smarkm			free(lstr);
13871556Srgrimes			return(-1);
13881556Srgrimes		}
13891556Srgrimes		if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) {
139076017Skris			paxwarn(0, "Unable to allocate space for option list");
1391114583Smarkm			free(lstr);
13921556Srgrimes			return(-1);
13931556Srgrimes		}
13941556Srgrimes		*pt++ = '\0';
13951556Srgrimes		opt->name = frpt;
13961556Srgrimes		opt->value = pt;
13971556Srgrimes		opt->fow = NULL;
13981556Srgrimes		if (endpt != NULL)
13991556Srgrimes			frpt = endpt + 1;
14001556Srgrimes		else
14011556Srgrimes			frpt = NULL;
14021556Srgrimes		if (ophead == NULL) {
14031556Srgrimes			optail = ophead = opt;
14041556Srgrimes			continue;
14051556Srgrimes		}
14061556Srgrimes		optail->fow = opt;
14071556Srgrimes		optail = opt;
14081556Srgrimes	}
14091556Srgrimes	return(0);
14101556Srgrimes}
14111556Srgrimes
14121556Srgrimes/*
14131556Srgrimes * str_offt()
14141556Srgrimes *	Convert an expression of the following forms to an off_t > 0.
14151556Srgrimes * 	1) A positive decimal number.
14161556Srgrimes *	2) A positive decimal number followed by a b (mult by 512).
14171556Srgrimes *	3) A positive decimal number followed by a k (mult by 1024).
14181556Srgrimes *	4) A positive decimal number followed by a m (mult by 512).
14191556Srgrimes *	5) A positive decimal number followed by a w (mult by sizeof int)
14201556Srgrimes *	6) Two or more positive decimal numbers (with/without k,b or w).
142172089Sasmodai *	   separated by x (also * for backwards compatibility), specifying
14221556Srgrimes *	   the product of the indicated values.
14231556Srgrimes * Return:
14241556Srgrimes *	0 for an error, a positive value o.w.
14251556Srgrimes */
14261556Srgrimes
14271556Srgrimesstatic off_t
14281556Srgrimesstr_offt(char *val)
14291556Srgrimes{
14301556Srgrimes	char *expr;
14311556Srgrimes	off_t num, t;
14321556Srgrimes
14331556Srgrimes#	ifdef NET2_STAT
14341556Srgrimes	num = strtol(val, &expr, 0);
14351556Srgrimes	if ((num == LONG_MAX) || (num <= 0) || (expr == val))
14361556Srgrimes#	else
14371556Srgrimes	num = strtoq(val, &expr, 0);
14381556Srgrimes	if ((num == QUAD_MAX) || (num <= 0) || (expr == val))
14391556Srgrimes#	endif
14401556Srgrimes		return(0);
14411556Srgrimes
14421556Srgrimes	switch(*expr) {
14431556Srgrimes	case 'b':
14441556Srgrimes		t = num;
14451556Srgrimes		num *= 512;
14461556Srgrimes		if (t > num)
14471556Srgrimes			return(0);
14481556Srgrimes		++expr;
14491556Srgrimes		break;
14501556Srgrimes	case 'k':
14511556Srgrimes		t = num;
14521556Srgrimes		num *= 1024;
14531556Srgrimes		if (t > num)
14541556Srgrimes			return(0);
14551556Srgrimes		++expr;
14561556Srgrimes		break;
14571556Srgrimes	case 'm':
14581556Srgrimes		t = num;
14591556Srgrimes		num *= 1048576;
14601556Srgrimes		if (t > num)
14611556Srgrimes			return(0);
14621556Srgrimes		++expr;
14631556Srgrimes		break;
14641556Srgrimes	case 'w':
14651556Srgrimes		t = num;
14661556Srgrimes		num *= sizeof(int);
14671556Srgrimes		if (t > num)
14681556Srgrimes			return(0);
14691556Srgrimes		++expr;
14701556Srgrimes		break;
14711556Srgrimes	}
14721556Srgrimes
14731556Srgrimes	switch(*expr) {
14741556Srgrimes		case '\0':
14751556Srgrimes			break;
14761556Srgrimes		case '*':
14771556Srgrimes		case 'x':
14781556Srgrimes			t = num;
14791556Srgrimes			num *= str_offt(expr + 1);
14801556Srgrimes			if (t > num)
14811556Srgrimes				return(0);
14821556Srgrimes			break;
14831556Srgrimes		default:
14841556Srgrimes			return(0);
14851556Srgrimes	}
14861556Srgrimes	return(num);
14871556Srgrimes}
14881556Srgrimes
148976351Skrischar *
149076351Skrisgetline(FILE *f)
149176351Skris{
149276351Skris	char *name, *temp;
149376351Skris	size_t len;
149476351Skris
149576351Skris	name = fgetln(f, &len);
149676351Skris	if (!name) {
149776351Skris		getline_error = ferror(f) ? GETLINE_FILE_CORRUPT : 0;
149876351Skris		return(0);
149976351Skris	}
150076351Skris	if (name[len-1] != '\n')
150176351Skris		len++;
150276351Skris	temp = malloc(len);
150376351Skris	if (!temp) {
150476351Skris		getline_error = GETLINE_OUT_OF_MEM;
150576351Skris		return(0);
150676351Skris	}
150776351Skris	memcpy(temp, name, len-1);
150876351Skris	temp[len-1] = 0;
150976351Skris	return(temp);
151076351Skris}
151176351Skris
15121556Srgrimes/*
15131556Srgrimes * no_op()
15141556Srgrimes *	for those option functions where the archive format has nothing to do.
15151556Srgrimes * Return:
15161556Srgrimes *	0
15171556Srgrimes */
15181556Srgrimes
15191556Srgrimesstatic int
15201556Srgrimesno_op(void)
15211556Srgrimes{
15221556Srgrimes	return(0);
15231556Srgrimes}
15241556Srgrimes
15251556Srgrimes/*
15261556Srgrimes * pax_usage()
15271556Srgrimes *	print the usage summary to the user
15281556Srgrimes */
15291556Srgrimes
15301556Srgrimesvoid
15311556Srgrimespax_usage(void)
15321556Srgrimes{
1533281661Seadler	(void)fputs("usage: pax [-cdnOvz] [-E limit] [-f archive] ", stderr);
15341556Srgrimes	(void)fputs("[-s replstr] ... [-U user] ...", stderr);
153576019Skris	(void)fputs("\n	   [-G group] ... ", stderr);
15361556Srgrimes	(void)fputs("[-T [from_date][,to_date]] ... ", stderr);
15371556Srgrimes	(void)fputs("[pattern ...]\n", stderr);
1538281661Seadler	(void)fputs("       pax -r [-cdiknOuvzDYZ] [-E limit] ", stderr);
15391556Srgrimes	(void)fputs("[-f archive] [-o options] ... \n", stderr);
154076019Skris	(void)fputs("	   [-p string] ... [-s replstr] ... ", stderr);
154176019Skris	(void)fputs("[-U user] ... [-G group] ...\n	   ", stderr);
15421556Srgrimes	(void)fputs("[-T [from_date][,to_date]] ... ", stderr);
15431556Srgrimes	(void)fputs(" [pattern ...]\n", stderr);
1544281661Seadler	(void)fputs("       pax -w [-dituvzHLOPX] [-b blocksize] ", stderr);
15451556Srgrimes	(void)fputs("[ [-a] [-f archive] ] [-x format] \n", stderr);
154676019Skris	(void)fputs("	   [-B bytes] [-s replstr] ... ", stderr);
15471556Srgrimes	(void)fputs("[-o options] ... [-U user] ...", stderr);
154876019Skris	(void)fputs("\n	   [-G group] ... ", stderr);
15491556Srgrimes	(void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr);
15501556Srgrimes	(void)fputs("[file ...]\n", stderr);
1551281661Seadler	(void)fputs("       pax -r -w [-diklntuvDHLOPXYZ] ", stderr);
15521556Srgrimes	(void)fputs("[-p string] ... [-s replstr] ...", stderr);
155376019Skris	(void)fputs("\n	   [-U user] ... [-G group] ... ", stderr);
15541556Srgrimes	(void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr);
155576019Skris	(void)fputs("\n	   [file ...] directory\n", stderr);
15561556Srgrimes	exit(1);
15571556Srgrimes}
15581556Srgrimes
15591556Srgrimes/*
15601556Srgrimes * tar_usage()
15611556Srgrimes *	print the usage summary to the user
15621556Srgrimes */
15631556Srgrimes
15641556Srgrimesvoid
15651556Srgrimestar_usage(void)
15661556Srgrimes{
156776351Skris	(void)fputs("usage: tar [-]{crtux}[-befhjmopqsvwyzHLOPXZ014578] [blocksize] ",
15681556Srgrimes		 stderr);
156976351Skris	(void)fputs("[archive] [replstr] [-C directory] [-I file] [file ...]\n",
157076351Skris	    stderr);
15711556Srgrimes	exit(1);
15721556Srgrimes}
15731556Srgrimes
15741556Srgrimes/*
15751556Srgrimes * cpio_usage()
15761556Srgrimes *	print the usage summary to the user
15771556Srgrimes */
15781556Srgrimes
15791556Srgrimesvoid
15801556Srgrimescpio_usage(void)
15811556Srgrimes{
158276351Skris	(void)fputs("usage: cpio -o [-aABcLvVzZ] [-C bytes] [-H format] [-O archive]\n", stderr);
158376351Skris	(void)fputs("               [-F archive] < name-list [> archive]\n", stderr);
158476351Skris	(void)fputs("       cpio -i [-bBcdfmnrsStuvVzZ6] [-C bytes] [-E file] [-H format]\n", stderr);
158576351Skris	(void)fputs("               [-I archive] [-F archive] [pattern...] [< archive]\n", stderr);
158676351Skris	(void)fputs("       cpio -p [-adlLmuvV] destination-directory < name-list\n", stderr);
15871556Srgrimes	exit(1);
15881556Srgrimes}
1589