xref: /netbsd-src/sbin/dump/main.c (revision dbc3f9418ccaf0dcdc341a469c853a080a1bd797)
1*dbc3f941Srillig /*	$NetBSD: main.c,v 1.79 2024/10/04 11:38:44 rillig Exp $	*/
20114e805Scgd 
3108d8947Scgd /*-
4ccfa3742Smycroft  * Copyright (c) 1980, 1991, 1993, 1994
5ccfa3742Smycroft  *	The Regents of the University of California.  All rights reserved.
6108d8947Scgd  *
7108d8947Scgd  * Redistribution and use in source and binary forms, with or without
8108d8947Scgd  * modification, are permitted provided that the following conditions
9108d8947Scgd  * are met:
10108d8947Scgd  * 1. Redistributions of source code must retain the above copyright
11108d8947Scgd  *    notice, this list of conditions and the following disclaimer.
12108d8947Scgd  * 2. Redistributions in binary form must reproduce the above copyright
13108d8947Scgd  *    notice, this list of conditions and the following disclaimer in the
14108d8947Scgd  *    documentation and/or other materials provided with the distribution.
15bf07c871Sagc  * 3. Neither the name of the University nor the names of its contributors
16108d8947Scgd  *    may be used to endorse or promote products derived from this software
17108d8947Scgd  *    without specific prior written permission.
18108d8947Scgd  *
19108d8947Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20108d8947Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21108d8947Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22108d8947Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23108d8947Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24108d8947Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25108d8947Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26108d8947Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27108d8947Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28108d8947Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29108d8947Scgd  * SUCH DAMAGE.
30108d8947Scgd  */
31108d8947Scgd 
32a84bab53Slukem #include <sys/cdefs.h>
33108d8947Scgd #ifndef lint
346543a91fSlukem __COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993, 1994\
356543a91fSlukem  The Regents of the University of California.  All rights reserved.");
36108d8947Scgd #endif /* not lint */
37108d8947Scgd 
38108d8947Scgd #ifndef lint
390114e805Scgd #if 0
40919c9246Slukem static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/1/95";
410114e805Scgd #else
42*dbc3f941Srillig __RCSID("$NetBSD: main.c,v 1.79 2024/10/04 11:38:44 rillig Exp $");
430114e805Scgd #endif
44108d8947Scgd #endif /* not lint */
45108d8947Scgd 
46108d8947Scgd #include <sys/param.h>
47108d8947Scgd #include <sys/time.h>
484c54f5b7Slukem #include <sys/stat.h>
494c54f5b7Slukem #include <sys/mount.h>
50262ce6cbSbouyer #include <sys/sysctl.h>
514c54f5b7Slukem 
52919c9246Slukem #include <ufs/ffs/fs.h>
5334ccbd43Sbouyer #include <ufs/ffs/ffs_extern.h>
54ccfa3742Smycroft 
55ccfa3742Smycroft #include <ctype.h>
56dd6c22edSmycroft #include <err.h>
57ccfa3742Smycroft #include <errno.h>
58108d8947Scgd #include <fcntl.h>
59108d8947Scgd #include <fstab.h>
60ccfa3742Smycroft #include <signal.h>
61108d8947Scgd #include <stdio.h>
62108d8947Scgd #include <stdlib.h>
63108d8947Scgd #include <string.h>
64578deb45Slukem #include <time.h>
65ccfa3742Smycroft #include <unistd.h>
665727faddSchristos #include <util.h>
67ccfa3742Smycroft 
68108d8947Scgd #include "dump.h"
69108d8947Scgd #include "pathnames.h"
701f51c280Shannken #include "snapshot.h"
71108d8947Scgd 
726ce4f404Sjoerg union u_spcl u_spcl;
736ce4f404Sjoerg struct ufsi *ufsib;
746ce4f404Sjoerg int	mapsize;
756ce4f404Sjoerg char	*usedinomap;
766ce4f404Sjoerg char	*dumpdirmap;
776ce4f404Sjoerg char	*dumpinomap;
786ce4f404Sjoerg char	*disk;
796ce4f404Sjoerg char	*disk_dev;
806ce4f404Sjoerg const char *tape;
816ce4f404Sjoerg const char *dumpdates;
826ce4f404Sjoerg const char *temp;
836ce4f404Sjoerg char	lastlevel;
846ce4f404Sjoerg char	level;
856ce4f404Sjoerg int	uflag;
866ce4f404Sjoerg const char *dumpdev;
876ce4f404Sjoerg int	eflag;
886ce4f404Sjoerg int	lflag;
896ce4f404Sjoerg int	diskfd;
906ce4f404Sjoerg int	tapefd;
916ce4f404Sjoerg int	pipeout;
926ce4f404Sjoerg int	trueinc;
936ce4f404Sjoerg ino_t	curino;
946ce4f404Sjoerg int	newtape;
956ce4f404Sjoerg u_int64_t	tapesize;
966ce4f404Sjoerg long	tsize;
976ce4f404Sjoerg long	asize;
986ce4f404Sjoerg int	etapes;
996ce4f404Sjoerg int	nonodump;
1006ce4f404Sjoerg int	unlimited;
1016ce4f404Sjoerg time_t	tstart_writing;
1026ce4f404Sjoerg time_t	tstart_volume;
1036ce4f404Sjoerg int	xferrate;
1046ce4f404Sjoerg char	sblock_buf[MAXBSIZE];
1056ce4f404Sjoerg int	dev_bshift;
1066ce4f404Sjoerg int	tp_bshift;
1076ce4f404Sjoerg int needswap;
1086ce4f404Sjoerg 
109a152a6a9Sblymn int	timestamp;		/* print message timestamps */
110e89e2124Sscw int	notify;			/* notify operator flag */
111b2fc8900Skre u_int64_t	blockswritten;	/* number of blocks written on current tape */
112e89e2124Sscw int	tapeno;			/* current tape number */
113e89e2124Sscw int	density;		/* density in bytes/0.1" */
114108d8947Scgd int	ntrec = NTREC;		/* # tape blocks in each tape record */
115e89e2124Sscw int	cartridge;		/* Assume non-cartridge tape */
116108d8947Scgd long	dev_bsize = 1;		/* recalculated below */
117e89e2124Sscw long	blocksperfile;		/* output blocks per file */
11864747563Schristos const char *host;		/* remote host (if any) */
119491d912fSbouyer int	readcache = -1;		/* read cache size (in readblksize blks) */
120262ce6cbSbouyer int	readblksize = -1;	/* read block size */
121a152a6a9Sblymn char    default_time_string[] = "%T %Z"; /* default timestamp string */
122a152a6a9Sblymn char    *time_string = default_time_string; /* timestamp string */
123108d8947Scgd 
12464747563Schristos static long numarg(const char *, long, long);
12599feaf5bSlukem static void obsolete(int *, char **[]);
12699feaf5bSlukem static void usage(void);
127ccfa3742Smycroft 
128ccfa3742Smycroft int
12999feaf5bSlukem main(int argc, char *argv[])
130108d8947Scgd {
13193da79deSlukem 	ino_t ino;
13293da79deSlukem 	int dirty;
13342614ed3Sfvdl 	union dinode *dp;
13493da79deSlukem 	struct fstab *dt;
1356bd1d6d4Schristos 	struct statvfs *mntinfo, fsbuf;
13664747563Schristos 	char *map, *cp;
13793da79deSlukem 	int ch;
138b1a3e114Slukem 	int i, anydirskipped, bflag = 0, Tflag = 0, Fflag = 0, honorlevel = 1;
1391f51c280Shannken 	int snap_internal = 0;
140108d8947Scgd 	ino_t maxino;
14134ccbd43Sbouyer 	time_t tnow, date;
1429af1692eSlukem 	int dirc;
1439af1692eSlukem 	char *mountpoint;
144865fe390Slukem 	int just_estimate = 0;
1452faa2d1fSlukem 	char labelstr[LBLSIZE];
146ca11e1e6Schristos 	char buf[MAXPATHLEN], rbuf[MAXPATHLEN];
147a152a6a9Sblymn 	char *new_time_format;
1481f51c280Shannken 	char *snap_backup = NULL;
149108d8947Scgd 
150108d8947Scgd 	spcl.c_date = 0;
151b73cca7bSlukem 	(void)time(&tnow);
152b73cca7bSlukem 	spcl.c_date = tnow;
153a152a6a9Sblymn 	tzset(); /* set up timezone for strftime */
154a152a6a9Sblymn 	if ((new_time_format = getenv("TIMEFORMAT")) != NULL)
155a152a6a9Sblymn 		time_string = new_time_format;
156108d8947Scgd 
157108d8947Scgd 	tsize = 0;	/* Default later, based on 'c' option for cart tapes */
1582b5cf470Smrg 	if ((tape = getenv("TAPE")) == NULL)
159108d8947Scgd 		tape = _PATH_DEFTAPE;
160108d8947Scgd 	dumpdates = _PATH_DUMPDATES;
161108d8947Scgd 	temp = _PATH_DTMP;
1622faa2d1fSlukem 	strcpy(labelstr, "none");	/* XXX safe strcpy. */
163108d8947Scgd 	if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0)
164c8d11eb8Schristos 		quit("TP_BSIZE must be a multiple of DEV_BSIZE");
165108d8947Scgd 	level = '0';
166a152a6a9Sblymn 	timestamp = 0;
167108d8947Scgd 
168dd6c22edSmycroft 	if (argc < 2)
169dd6c22edSmycroft 		usage();
170108d8947Scgd 
171dd6c22edSmycroft 	obsolete(&argc, &argv);
172865fe390Slukem 	while ((ch = getopt(argc, argv,
1737aae2902Sperseant 	    "0123456789aB:b:cd:D:eFf:h:ik:l:L:nr:s:StT:uU:Wwx:X")) != -1)
174dd6c22edSmycroft 		switch (ch) {
175dd6c22edSmycroft 		/* dump level */
176dd6c22edSmycroft 		case '0': case '1': case '2': case '3': case '4':
177dd6c22edSmycroft 		case '5': case '6': case '7': case '8': case '9':
178dd6c22edSmycroft 			level = ch;
179108d8947Scgd 			break;
180108d8947Scgd 
1819dcaec9cSlukem 		case 'a':		/* `auto-size', Write to EOM. */
1829dcaec9cSlukem 			unlimited = 1;
1839dcaec9cSlukem 			break;
1849dcaec9cSlukem 
185108d8947Scgd 		case 'B':		/* blocks per output file */
186dd6c22edSmycroft 			blocksperfile = numarg("blocks per file", 1L, 0L);
187dd6c22edSmycroft 			break;
188dd6c22edSmycroft 
189dd6c22edSmycroft 		case 'b':		/* blocks per tape write */
190dd6c22edSmycroft 			ntrec = numarg("blocks per write", 1L, 1000L);
191b31dfde1Smikel 			bflag = 1;
192108d8947Scgd 			break;
193108d8947Scgd 
194108d8947Scgd 		case 'c':		/* Tape is cart. not 9-track */
195ccfa3742Smycroft 			cartridge = 1;
196ccfa3742Smycroft 			break;
197108d8947Scgd 
198dd6c22edSmycroft 		case 'd':		/* density, in bits per inch */
199dd6c22edSmycroft 			density = numarg("density", 10L, 327670L) / 10;
200dd6c22edSmycroft 			if (density >= 625 && !bflag)
201dd6c22edSmycroft 				ntrec = HIGHDENSITYTREC;
202ccfa3742Smycroft 			break;
203108d8947Scgd 
2047aae2902Sperseant 		case 'D':		/* specify alt. dumpdates file */
2057aae2902Sperseant 			dumpdates = optarg;
2067aae2902Sperseant 			break;
2077aae2902Sperseant 
208d6be44d3Stron 		case 'e':		/* eject full tapes */
209d6be44d3Stron 			eflag = 1;
210d6be44d3Stron 			break;
211d6be44d3Stron 
212b1a3e114Slukem 		case 'F':		/* files-to-dump is an fs image */
213b1a3e114Slukem 			Fflag = 1;
214b1a3e114Slukem 			break;
215b1a3e114Slukem 
216dd6c22edSmycroft 		case 'f':		/* output file */
217dd6c22edSmycroft 			tape = optarg;
218dd6c22edSmycroft 			break;
219dd6c22edSmycroft 
220dd6c22edSmycroft 		case 'h':
221dd6c22edSmycroft 			honorlevel = numarg("honor level", 0L, 10L);
222ccfa3742Smycroft 			break;
223108d8947Scgd 
22420ebd2a4Schristos 		case 'i':	/* "true incremental" regardless level */
22520ebd2a4Schristos 			level = 'i';
226c40eb1e7Schristos 			trueinc = 1;
227c40eb1e7Schristos 			break;
228c40eb1e7Schristos 
229491d912fSbouyer 		case 'k':
230491d912fSbouyer 			readblksize = numarg("read block size", 0, 64) * 1024;
231491d912fSbouyer 			break;
232491d912fSbouyer 
233740dbf0dSbouyer 		case 'l':		/* autoload after eject full tapes */
234740dbf0dSbouyer 			eflag = 1;
235cf398818Sbouyer 			lflag = numarg("timeout (in seconds)", 1, 0);
236740dbf0dSbouyer 			break;
237740dbf0dSbouyer 
2382faa2d1fSlukem 		case 'L':
2392faa2d1fSlukem 			/*
2402faa2d1fSlukem 			 * Note that although there are LBLSIZE characters,
2412faa2d1fSlukem 			 * the last must be '\0', so the limit on strlen()
2422faa2d1fSlukem 			 * is really LBLSIZE-1.
2432faa2d1fSlukem 			 */
24494ccb14aSitojun 			if (strlcpy(labelstr, optarg, sizeof(labelstr))
24594ccb14aSitojun 			    >= sizeof(labelstr)) {
2462faa2d1fSlukem 				msg(
247bc0b1329Sitojun 		"WARNING Label `%s' is larger than limit of %lu characters.\n",
248bc0b1329Sitojun 				    optarg,
249bc0b1329Sitojun 				    (unsigned long)sizeof(labelstr) - 1);
2502faa2d1fSlukem 				msg("WARNING: Using truncated label `%s'.\n",
2512faa2d1fSlukem 				    labelstr);
2522faa2d1fSlukem 			}
2532faa2d1fSlukem 			break;
254108d8947Scgd 		case 'n':		/* notify operators */
255ccfa3742Smycroft 			notify = 1;
256ccfa3742Smycroft 			break;
257ccfa3742Smycroft 
258491d912fSbouyer 		case 'r':		/* read cache size */
259491d912fSbouyer 			readcache = numarg("read cache size", 0, 512);
260491d912fSbouyer 			break;
261491d912fSbouyer 
262dd6c22edSmycroft 		case 's':		/* tape size, feet */
263dd6c22edSmycroft 			tsize = numarg("tape size", 1L, 0L) * 12 * 10;
264ccfa3742Smycroft 			break;
265108d8947Scgd 
266865fe390Slukem 		case 'S':		/* exit after estimating # of tapes */
267865fe390Slukem 			just_estimate = 1;
268865fe390Slukem 			break;
269865fe390Slukem 
270a152a6a9Sblymn 		case 't':
271a152a6a9Sblymn 			timestamp = 1;
272a152a6a9Sblymn 			break;
273a152a6a9Sblymn 
274dd6c22edSmycroft 		case 'T':		/* time of last dump */
275dd6c22edSmycroft 			spcl.c_ddate = unctime(optarg);
276dd6c22edSmycroft 			if (spcl.c_ddate < 0) {
277dd6c22edSmycroft 				(void)fprintf(stderr, "bad time \"%s\"\n",
278dd6c22edSmycroft 				    optarg);
2798975510aSlukem 				exit(X_STARTUP);
280108d8947Scgd 			}
281dd6c22edSmycroft 			Tflag = 1;
282dd6c22edSmycroft 			lastlevel = '?';
283dd6c22edSmycroft 			break;
284dd6c22edSmycroft 
285dd6c22edSmycroft 		case 'u':		/* update /etc/dumpdates */
286dd6c22edSmycroft 			uflag = 1;
287dd6c22edSmycroft 			break;
288dd6c22edSmycroft 
289eeb96f8cSmanu 		case 'U':		/* dump device in /etc/dumpdates */
290eeb96f8cSmanu 			dumpdev = optarg;
291eeb96f8cSmanu 			break;
292eeb96f8cSmanu 
293dd6c22edSmycroft 		case 'W':		/* what to do */
294dd6c22edSmycroft 		case 'w':
295dd6c22edSmycroft 			lastdump(ch);
2968975510aSlukem 			exit(X_FINOK);	/* do nothing else */
297dd6c22edSmycroft 
2981f51c280Shannken 		case 'x':
2991f51c280Shannken 			snap_backup = optarg;
3001f51c280Shannken 			break;
3011f51c280Shannken 
3021f51c280Shannken 		case 'X':
3031f51c280Shannken 			snap_internal = 1;
3041f51c280Shannken 			break;
3051f51c280Shannken 
306dd6c22edSmycroft 		default:
307dd6c22edSmycroft 			usage();
308108d8947Scgd 		}
309dd6c22edSmycroft 	argc -= optind;
310dd6c22edSmycroft 	argv += optind;
311dd6c22edSmycroft 
312108d8947Scgd 	if (argc < 1) {
313b1a3e114Slukem 		(void)fprintf(stderr,
314b1a3e114Slukem 		    "Must specify disk or image, or file list\n");
3158975510aSlukem 		exit(X_STARTUP);
316ccfa3742Smycroft 	}
3174c54f5b7Slukem 
3189af1692eSlukem 
3194c54f5b7Slukem 	/*
3204c54f5b7Slukem 	 *	determine if disk is a subdirectory, and setup appropriately
3214c54f5b7Slukem 	 */
3229af1692eSlukem 	getfstab();		/* /etc/fstab snarfed */
3239af1692eSlukem 	disk = NULL;
324fcd5ec48Shannken 	disk_dev = NULL;
3259af1692eSlukem 	mountpoint = NULL;
3269af1692eSlukem 	dirc = 0;
3274c54f5b7Slukem 	for (i = 0; i < argc; i++) {
3284c54f5b7Slukem 		struct stat sb;
3296fa7c341Smlelstv 		int error;
3304c54f5b7Slukem 
3316fa7c341Smlelstv 		error = lstat(argv[i], &sb);
3326fa7c341Smlelstv 		if (Fflag || (!error && (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)))) {
3336fa7c341Smlelstv 			if (error)
334c8d11eb8Schristos 				quite(errno, "can't stat %s", argv[i]);
3359af1692eSlukem 			disk = argv[i];
3369af1692eSlukem  multicheck:
3379af1692eSlukem 			if (dirc != 0)
338c8d11eb8Schristos 				quit("can't dump a disk or image at the same"
339c8d11eb8Schristos 				" time as a file list");
3404c54f5b7Slukem 			break;
3414c54f5b7Slukem 		}
3429af1692eSlukem 		if ((dt = fstabsearch(argv[i])) != NULL) {
3436fa7c341Smlelstv 			disk = argv[i];
344de85f7e4Shannken 			mountpoint = xstrdup(dt->fs_file);
3459af1692eSlukem 			goto multicheck;
3464c54f5b7Slukem 		}
3476bd1d6d4Schristos 		if (statvfs(argv[i], &fsbuf) == -1)
348c8d11eb8Schristos 			quite(errno, "can't statvfs %s", argv[i]);
3499af1692eSlukem 		disk = fsbuf.f_mntfromname;
3509af1692eSlukem 		if (strcmp(argv[i], fsbuf.f_mntonname) == 0)
3519af1692eSlukem 			goto multicheck;
3529af1692eSlukem 		if (mountpoint == NULL) {
3539af1692eSlukem 			mountpoint = xstrdup(fsbuf.f_mntonname);
3544c54f5b7Slukem 			if (uflag) {
3554c54f5b7Slukem 				msg("Ignoring u flag for subdir dump\n");
3564c54f5b7Slukem 				uflag = 0;
3574c54f5b7Slukem 			}
3584c54f5b7Slukem 			if (level > '0') {
3594c54f5b7Slukem 				msg("Subdir dump is done at level 0\n");
3604c54f5b7Slukem 				level = '0';
3614c54f5b7Slukem 			}
3629af1692eSlukem 			msg("Dumping sub files/directories from %s\n",
3639af1692eSlukem 			    mountpoint);
3644c54f5b7Slukem 		} else {
3659af1692eSlukem 			if (strcmp(mountpoint, fsbuf.f_mntonname) != 0)
366c8d11eb8Schristos 				quit("%s is not on %s", argv[i], mountpoint);
3674c54f5b7Slukem 		}
3684c54f5b7Slukem 		msg("Dumping file/directory %s\n", argv[i]);
3699af1692eSlukem 		dirc++;
3704c54f5b7Slukem 	}
3719af1692eSlukem 	if (mountpoint)
3729af1692eSlukem 		free(mountpoint);
3739af1692eSlukem 
3749af1692eSlukem 	if (dirc == 0) {
3759af1692eSlukem 		argv++;
3764c54f5b7Slukem 		if (argc != 1) {
3774c54f5b7Slukem 			(void)fprintf(stderr, "Excess arguments to dump:");
3784c54f5b7Slukem 			while (--argc)
379108d8947Scgd 				(void)fprintf(stderr, " %s", *argv++);
380108d8947Scgd 			(void)fprintf(stderr, "\n");
3818975510aSlukem 			exit(X_STARTUP);
382108d8947Scgd 		}
3834c54f5b7Slukem 	}
384108d8947Scgd 	if (Tflag && uflag) {
385108d8947Scgd 		(void)fprintf(stderr,
386108d8947Scgd 		    "You cannot use the T and u flags together.\n");
3878975510aSlukem 		exit(X_STARTUP);
388108d8947Scgd 	}
389108d8947Scgd 	if (strcmp(tape, "-") == 0) {
390108d8947Scgd 		pipeout++;
391108d8947Scgd 		tape = "standard output";
392108d8947Scgd 	}
393108d8947Scgd 
394108d8947Scgd 	if (blocksperfile)
395108d8947Scgd 		blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
3969dcaec9cSlukem 	else if (!unlimited) {
397108d8947Scgd 		/*
398108d8947Scgd 		 * Determine how to default tape size and density
399108d8947Scgd 		 *
400108d8947Scgd 		 *		density				tape size
401108d8947Scgd 		 * 9-track	1600 bpi (160 bytes/.1")	2300 ft.
402108d8947Scgd 		 * 9-track	6250 bpi (625 bytes/.1")	2300 ft.
403108d8947Scgd 		 * cartridge	8000 bpi (100 bytes/.1")	1700 ft.
404108d8947Scgd 		 *						(450*4 - slop)
405108d8947Scgd 		 */
406108d8947Scgd 		if (density == 0)
407108d8947Scgd 			density = cartridge ? 100 : 160;
408108d8947Scgd 		if (tsize == 0)
409108d8947Scgd 			tsize = cartridge ? 1700L*120L : 2300L*120L;
410108d8947Scgd 	}
411108d8947Scgd 
412*dbc3f941Srillig 	/* LINTED 346 "call to 'strchr' effectively discards 'const'" */
41364747563Schristos 	if ((cp = strchr(tape, ':')) != NULL) {
414108d8947Scgd 		host = tape;
41564747563Schristos 		/* This is fine, because all the const strings don't have : */
41664747563Schristos 		*cp++ = '\0';
41764747563Schristos 		tape = cp;
418108d8947Scgd #ifdef RDUMP
419108d8947Scgd 		if (rmthost(host) == 0)
4208975510aSlukem 			exit(X_STARTUP);
421108d8947Scgd #else
422108d8947Scgd 		(void)fprintf(stderr, "remote dump not enabled\n");
4238975510aSlukem 		exit(X_STARTUP);
424108d8947Scgd #endif
425108d8947Scgd 	}
426108d8947Scgd 
427108d8947Scgd 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
428108d8947Scgd 		signal(SIGHUP, sig);
429108d8947Scgd 	if (signal(SIGTRAP, SIG_IGN) != SIG_IGN)
430108d8947Scgd 		signal(SIGTRAP, sig);
431108d8947Scgd 	if (signal(SIGFPE, SIG_IGN) != SIG_IGN)
432108d8947Scgd 		signal(SIGFPE, sig);
433108d8947Scgd 	if (signal(SIGBUS, SIG_IGN) != SIG_IGN)
434108d8947Scgd 		signal(SIGBUS, sig);
43542614ed3Sfvdl #if 0
436108d8947Scgd 	if (signal(SIGSEGV, SIG_IGN) != SIG_IGN)
437108d8947Scgd 		signal(SIGSEGV, sig);
43842614ed3Sfvdl #endif
439108d8947Scgd 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
440108d8947Scgd 		signal(SIGTERM, sig);
441108d8947Scgd 	if (signal(SIGINT, interrupt) == SIG_IGN)
442108d8947Scgd 		signal(SIGINT, SIG_IGN);
443108d8947Scgd 
444108d8947Scgd 	/*
4459af1692eSlukem 	 *	disk can be either the full special file name, or
4469af1692eSlukem 	 *	the file system name.
447108d8947Scgd 	 */
4489af1692eSlukem 	mountpoint = NULL;
4491f51c280Shannken 	mntinfo = mntinfosearch(disk);
4509af1692eSlukem 	if ((dt = fstabsearch(disk)) != NULL) {
4515727faddSchristos 		if (getfsspecname(buf, sizeof(buf), dt->fs_spec) == NULL)
452c8d11eb8Schristos 			quite(errno, "can't resolve mount %s (%s)", dt->fs_spec,
453c8d11eb8Schristos 			    buf);
454ca11e1e6Schristos 		if (getdiskrawname(rbuf, sizeof(rbuf), buf) == NULL)
455c8d11eb8Schristos 			quite(errno, "can't get disk raw name for %s", buf);
456ca11e1e6Schristos 		disk = rbuf;
4579af1692eSlukem 		mountpoint = dt->fs_file;
4589af1692eSlukem 		msg("Found %s on %s in %s\n", disk, mountpoint, _PATH_FSTAB);
4591f51c280Shannken 	} else if (mntinfo != NULL) {
460ca11e1e6Schristos 		if (getdiskrawname(rbuf, sizeof(rbuf), mntinfo->f_mntfromname)
461ca11e1e6Schristos 		    == NULL)
462c8d11eb8Schristos 			quite(errno, "can't get disk raw name for %s",
463c8d11eb8Schristos 			    mntinfo->f_mntfromname);
4646fa7c341Smlelstv 		disk = rbuf;
4659af1692eSlukem 		mountpoint = mntinfo->f_mntonname;
4669af1692eSlukem 		msg("Found %s on %s in mount table\n", disk, mountpoint);
4679af1692eSlukem 	}
4689af1692eSlukem 	if (mountpoint != NULL) {
4699af1692eSlukem 		if (dirc != 0)
47094ccb14aSitojun 			(void)snprintf(spcl.c_filesys, sizeof(spcl.c_filesys),
4719af1692eSlukem 			    "a subset of %s", mountpoint);
4724c54f5b7Slukem 		else
47394ccb14aSitojun 			(void)strlcpy(spcl.c_filesys, mountpoint,
47494ccb14aSitojun 			    sizeof(spcl.c_filesys));
475b1a3e114Slukem 	} else if (Fflag) {
47694ccb14aSitojun 		(void)strlcpy(spcl.c_filesys, "a file system image",
47794ccb14aSitojun 		    sizeof(spcl.c_filesys));
478108d8947Scgd 	} else {
47994ccb14aSitojun 		(void)strlcpy(spcl.c_filesys, "an unlisted file system",
48094ccb14aSitojun 		    sizeof(spcl.c_filesys));
481108d8947Scgd 	}
48294ccb14aSitojun 	(void)strlcpy(spcl.c_dev, disk, sizeof(spcl.c_dev));
48394ccb14aSitojun 	(void)strlcpy(spcl.c_label, labelstr, sizeof(spcl.c_label));
48494ccb14aSitojun 	(void)gethostname(spcl.c_host, sizeof(spcl.c_host));
4852beab49aSmrg 	spcl.c_host[sizeof(spcl.c_host) - 1] = '\0';
486108d8947Scgd 
4871f51c280Shannken 	if ((snap_backup != NULL || snap_internal) && mntinfo == NULL) {
4881f51c280Shannken 		msg("WARNING: Cannot use -x or -X on unmounted file system.\n");
4891f51c280Shannken 		snap_backup = NULL;
4901f51c280Shannken 		snap_internal = 0;
4911f51c280Shannken 	}
4921c57171fSperseant 
4931c57171fSperseant #ifdef DUMP_LFS
4941c57171fSperseant 	sync();
4951c57171fSperseant 	if (snap_backup != NULL || snap_internal) {
4961c57171fSperseant 		if (lfs_wrap_stop(mountpoint) < 0) {
4971c57171fSperseant 			msg("Cannot stop writing on %s\n", mountpoint);
4981c57171fSperseant 			exit(X_STARTUP);
4991c57171fSperseant 		}
5001c57171fSperseant 	}
5011c57171fSperseant 	if ((diskfd = open(disk, O_RDONLY)) < 0) {
5021c57171fSperseant 		msg("Cannot open %s\n", disk);
5031c57171fSperseant 		exit(X_STARTUP);
5041c57171fSperseant 	}
505fcd5ec48Shannken 	disk_dev = disk;
5061c57171fSperseant #else /* ! DUMP_LFS */
5071f51c280Shannken 	if (snap_backup != NULL || snap_internal) {
508fcd5ec48Shannken 		diskfd = snap_open(mntinfo->f_mntonname, snap_backup,
509fcd5ec48Shannken 		    &tnow, &disk_dev);
5101f51c280Shannken 		if (diskfd < 0) {
5111f51c280Shannken 			msg("Cannot open snapshot of %s\n",
5121f51c280Shannken 				mntinfo->f_mntonname);
5131f51c280Shannken 			exit(X_STARTUP);
5141f51c280Shannken 		}
5151f51c280Shannken 		spcl.c_date = tnow;
5161f51c280Shannken 	} else {
517108d8947Scgd 		if ((diskfd = open(disk, O_RDONLY)) < 0) {
518108d8947Scgd 			msg("Cannot open %s\n", disk);
5198975510aSlukem 			exit(X_STARTUP);
520108d8947Scgd 		}
521fcd5ec48Shannken 		disk_dev = disk;
5221f51c280Shannken 	}
523108d8947Scgd 	sync();
5241c57171fSperseant #endif /* ! DUMP_LFS */
5252763cc19Sperseant 
5262763cc19Sperseant 	needswap = fs_read_sblock(sblock_buf);
52734ccbd43Sbouyer 
52820ebd2a4Schristos 	/* true incremental is always a level 10 dump */
52920ebd2a4Schristos 	spcl.c_level = trueinc? iswap32(10): iswap32(level - '0');
53034ccbd43Sbouyer 	spcl.c_type = iswap32(TS_TAPE);
53134ccbd43Sbouyer 	spcl.c_date = iswap32(spcl.c_date);
53234ccbd43Sbouyer 	spcl.c_ddate = iswap32(spcl.c_ddate);
53334ccbd43Sbouyer 	if (!Tflag)
53434ccbd43Sbouyer 		getdumptime();		/* /etc/dumpdates snarfed */
53534ccbd43Sbouyer 
53634ccbd43Sbouyer 	date = iswap32(spcl.c_date);
53734ccbd43Sbouyer 	msg("Date of this level %c dump: %s", level,
53834ccbd43Sbouyer 		spcl.c_date == 0 ? "the epoch\n" : ctime(&date));
53934ccbd43Sbouyer 	date = iswap32(spcl.c_ddate);
54034ccbd43Sbouyer  	msg("Date of last level %c dump: %s", lastlevel,
54134ccbd43Sbouyer 		spcl.c_ddate == 0 ? "the epoch\n" : ctime(&date));
54234ccbd43Sbouyer 	msg("Dumping ");
543f4729837Shannken 	if (snap_backup != NULL || snap_internal)
5441f51c280Shannken 		msgtail("a snapshot of ");
5459af1692eSlukem 	if (dirc != 0)
54634ccbd43Sbouyer 		msgtail("a subset of ");
54734ccbd43Sbouyer 	msgtail("%s (%s) ", disk, spcl.c_filesys);
54834ccbd43Sbouyer 	if (host)
54934ccbd43Sbouyer 		msgtail("to %s on host %s\n", tape, host);
55034ccbd43Sbouyer 	else
55134ccbd43Sbouyer 		msgtail("to %s\n", tape);
5522faa2d1fSlukem 	msg("Label: %s\n", labelstr);
55334ccbd43Sbouyer 
5542763cc19Sperseant 	ufsib = fs_parametrize();
5552763cc19Sperseant 
556108d8947Scgd 	dev_bshift = ffs(dev_bsize) - 1;
557108d8947Scgd 	if (dev_bsize != (1 << dev_bshift))
5581e8e167aSbriggs 		quit("dev_bsize (%ld) is not a power of 2", dev_bsize);
559108d8947Scgd 	tp_bshift = ffs(TP_BSIZE) - 1;
560108d8947Scgd 	if (TP_BSIZE != (1 << tp_bshift))
561108d8947Scgd 		quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE);
5622763cc19Sperseant 	maxino = fs_maxino();
563ccfa3742Smycroft 	mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE);
5649af1692eSlukem 	usedinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char));
5659af1692eSlukem 	dumpdirmap = (char *)xcalloc((unsigned) mapsize, sizeof(char));
5669af1692eSlukem 	dumpinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char));
567108d8947Scgd 	tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
568108d8947Scgd 
56934ccbd43Sbouyer 	nonodump = iswap32(spcl.c_level) < honorlevel;
570ccfa3742Smycroft 
571491d912fSbouyer 	initcache(readcache, readblksize);
572491d912fSbouyer 
5734c54f5b7Slukem 	(void)signal(SIGINFO, statussig);
5744c54f5b7Slukem 
575108d8947Scgd 	msg("mapping (Pass I) [regular files]\n");
5769af1692eSlukem 	anydirskipped = mapfiles(maxino, &tapesize, mountpoint,
5779af1692eSlukem 	    (dirc ? argv : NULL));
578108d8947Scgd 
579108d8947Scgd 	msg("mapping (Pass II) [directories]\n");
580108d8947Scgd 	while (anydirskipped) {
581108d8947Scgd 		anydirskipped = mapdirs(maxino, &tapesize);
582108d8947Scgd 	}
583108d8947Scgd 
5849dcaec9cSlukem 	if (pipeout || unlimited) {
585108d8947Scgd 		tapesize += 10;	/* 10 trailer blocks */
58642614ed3Sfvdl 		msg("estimated %llu tape blocks.\n",
58742614ed3Sfvdl 		    (unsigned long long)tapesize);
588ccfa3742Smycroft 	} else {
589ccfa3742Smycroft 		double fetapes;
590ccfa3742Smycroft 
591108d8947Scgd 		if (blocksperfile)
592ccfa3742Smycroft 			fetapes = (double) tapesize / blocksperfile;
593108d8947Scgd 		else if (cartridge) {
594108d8947Scgd 			/* Estimate number of tapes, assuming streaming stops at
595108d8947Scgd 			   the end of each block written, and not in mid-block.
596108d8947Scgd 			   Assume no erroneous blocks; this can be compensated
597108d8947Scgd 			   for with an artificially low tape size. */
598108d8947Scgd 			fetapes =
5998975510aSlukem 			(	  (double) tapesize	/* blocks */
600108d8947Scgd 				* TP_BSIZE	/* bytes/block */
601108d8947Scgd 				* (1.0/density)	/* 0.1" / byte */
602108d8947Scgd 			  +
6038975510aSlukem 				  (double) tapesize	/* blocks */
604108d8947Scgd 				* (1.0/ntrec)	/* streaming-stops per block */
605108d8947Scgd 				* 15.48		/* 0.1" / streaming-stop */
606108d8947Scgd 			) * (1.0 / tsize );	/* tape / 0.1" */
607108d8947Scgd 		} else {
608108d8947Scgd 			/* Estimate number of tapes, for old fashioned 9-track
609108d8947Scgd 			   tape */
610108d8947Scgd 			int tenthsperirg = (density == 625) ? 3 : 7;
611108d8947Scgd 			fetapes =
612108d8947Scgd 			(	  tapesize	/* blocks */
613108d8947Scgd 				* TP_BSIZE	/* bytes / block */
614108d8947Scgd 				* (1.0/density)	/* 0.1" / byte */
615108d8947Scgd 			  +
616108d8947Scgd 				  tapesize	/* blocks */
617108d8947Scgd 				* (1.0/ntrec)	/* IRG's / block */
618108d8947Scgd 				* tenthsperirg	/* 0.1" / IRG */
619108d8947Scgd 			) * (1.0 / tsize );	/* tape / 0.1" */
620108d8947Scgd 		}
621108d8947Scgd 		etapes = fetapes;		/* truncating assignment */
622108d8947Scgd 		etapes++;
623108d8947Scgd 		/* count the dumped inodes map on each additional tape */
624108d8947Scgd 		tapesize += (etapes - 1) *
625108d8947Scgd 			(howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
626108d8947Scgd 		tapesize += etapes + 10;	/* headers + 10 trailer blks */
62742614ed3Sfvdl 		msg("estimated %llu tape blocks on %3.2f tape(s).\n",
62842614ed3Sfvdl 		    (unsigned long long)tapesize, fetapes);
629ccfa3742Smycroft 	}
630865fe390Slukem 	/*
631865fe390Slukem 	 * If the user only wants an estimate of the number of
632865fe390Slukem 	 * tapes, exit now.
633865fe390Slukem 	 */
634865fe390Slukem 	if (just_estimate)
6358975510aSlukem 		exit(X_FINOK);
636108d8947Scgd 
637108d8947Scgd 	/*
638ccfa3742Smycroft 	 * Allocate tape buffer.
639108d8947Scgd 	 */
640108d8947Scgd 	if (!alloctape())
641c8d11eb8Schristos 		quit("can't allocate tape buffers - try a smaller"
642c8d11eb8Schristos 		    " blocking factor.");
643108d8947Scgd 
644108d8947Scgd 	startnewtape(1);
645108d8947Scgd 	(void)time((time_t *)&(tstart_writing));
646578deb45Slukem 	xferrate = 0;
647ccfa3742Smycroft 	dumpmap(usedinomap, TS_CLRI, maxino - 1);
648108d8947Scgd 
649108d8947Scgd 	msg("dumping (Pass III) [directories]\n");
650ccfa3742Smycroft 	dirty = 0;		/* XXX just to get gcc to shut up */
651ccfa3742Smycroft 	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
652ccfa3742Smycroft 		if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
653108d8947Scgd 			dirty = *map++;
654108d8947Scgd 		else
655108d8947Scgd 			dirty >>= 1;
656108d8947Scgd 		if ((dirty & 1) == 0)
657108d8947Scgd 			continue;
658108d8947Scgd 		/*
659108d8947Scgd 		 * Skip directory inodes deleted and maybe reallocated
660108d8947Scgd 		 */
661108d8947Scgd 		dp = getino(ino);
66242614ed3Sfvdl 		if ((DIP(dp, mode) & IFMT) != IFDIR)
663108d8947Scgd 			continue;
664108d8947Scgd 		(void)dumpino(dp, ino);
665108d8947Scgd 	}
666108d8947Scgd 
667108d8947Scgd 	msg("dumping (Pass IV) [regular files]\n");
668ccfa3742Smycroft 	for (map = dumpinomap, ino = 1; ino < maxino; ino++) {
669ccfa3742Smycroft 		int mode;
670ccfa3742Smycroft 
671ccfa3742Smycroft 		if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
672108d8947Scgd 			dirty = *map++;
673108d8947Scgd 		else
674108d8947Scgd 			dirty >>= 1;
675108d8947Scgd 		if ((dirty & 1) == 0)
676108d8947Scgd 			continue;
677108d8947Scgd 		/*
678108d8947Scgd 		 * Skip inodes deleted and reallocated as directories.
679108d8947Scgd 		 */
680108d8947Scgd 		dp = getino(ino);
68142614ed3Sfvdl 		mode = DIP(dp, mode) & IFMT;
682ccfa3742Smycroft 		if (mode == IFDIR)
683108d8947Scgd 			continue;
684108d8947Scgd 		(void)dumpino(dp, ino);
685108d8947Scgd 	}
686108d8947Scgd 
68734ccbd43Sbouyer 	spcl.c_type = iswap32(TS_END);
688108d8947Scgd 	for (i = 0; i < ntrec; i++)
689ccfa3742Smycroft 		writeheader(maxino - 1);
690108d8947Scgd 	if (pipeout)
6912266c911Sbouyer 		msg("%lld tape blocks\n",(long long)iswap64(spcl.c_tapea));
692108d8947Scgd 	else
6932266c911Sbouyer 		msg("%lld tape blocks on %d volume%s\n",
6942266c911Sbouyer 		    (long long)iswap64(spcl.c_tapea), iswap32(spcl.c_volume),
69534ccbd43Sbouyer 		    (iswap32(spcl.c_volume) == 1) ? "" : "s");
696578deb45Slukem 	tnow = do_stats();
69734ccbd43Sbouyer 	date = iswap32(spcl.c_date);
698578deb45Slukem 	msg("Date of this level %c dump: %s", level,
69934ccbd43Sbouyer 		spcl.c_date == 0 ? "the epoch\n" : ctime(&date));
700578deb45Slukem 	msg("Date this dump completed:  %s", ctime(&tnow));
7011e8e167aSbriggs 	msg("Average transfer rate: %d KB/s\n", xferrate / tapeno);
702108d8947Scgd 	putdumptime();
703b72a1f53Stron 	trewind(0);
7048975510aSlukem 	broadcast("DUMP IS DONE!\a\a\n");
7051c57171fSperseant #ifdef DUMP_LFS
7061c57171fSperseant 	lfs_wrap_go();
7071c57171fSperseant #endif /* DUMP_LFS */
708108d8947Scgd 	msg("DUMP IS DONE\n");
709108d8947Scgd 	Exit(X_FINOK);
710108d8947Scgd 	/* NOTREACHED */
711a84bab53Slukem 	exit(X_FINOK);		/* XXX: to satisfy gcc */
712108d8947Scgd }
713108d8947Scgd 
714dd6c22edSmycroft static void
71599feaf5bSlukem usage(void)
716dd6c22edSmycroft {
7179dcaec9cSlukem 	const char *prog = getprogname();
718dd6c22edSmycroft 
7199dcaec9cSlukem 	(void)fprintf(stderr,
720fd6f00f4Schristos "usage: %s [-0123456789aceFinStuX] [-B records] [-b blocksize]\n"
7219dcaec9cSlukem "            [-d density] [-f file] [-h level] [-k read-blocksize]\n"
722fd6f00f4Schristos "            [-L label] [-l timeout] [-r cachesize] [-s feet]\n"
723eeb96f8cSmanu "            [-T date] [-U dumpdev] [-x snap-backup] files-to-dump\n"
7249dcaec9cSlukem "       %s [-W | -w]\n", prog, prog);
7258975510aSlukem 	exit(X_STARTUP);
726dd6c22edSmycroft }
727dd6c22edSmycroft 
728ccfa3742Smycroft /*
729ccfa3742Smycroft  * Pick up a numeric argument.  It must be nonnegative and in the given
730ccfa3742Smycroft  * range (except that a vmax of 0 means unlimited).
731ccfa3742Smycroft  */
732ccfa3742Smycroft static long
73364747563Schristos numarg(const char *meaning, long vmin, long vmax)
734ccfa3742Smycroft {
735dd6c22edSmycroft 	char *p;
736ccfa3742Smycroft 	long val;
737ccfa3742Smycroft 
738dd6c22edSmycroft 	val = strtol(optarg, &p, 10);
739dd6c22edSmycroft 	if (*p)
7408975510aSlukem 		errx(X_STARTUP, "illegal %s -- %s", meaning, optarg);
741ccfa3742Smycroft 	if (val < vmin || (vmax && val > vmax))
7428975510aSlukem 		errx(X_STARTUP, "%s must be between %ld and %ld",
7438975510aSlukem 		    meaning, vmin, vmax);
744ccfa3742Smycroft 	return (val);
745ccfa3742Smycroft }
746ccfa3742Smycroft 
747108d8947Scgd void
748713ffa0eSlukem sig(int signo)
749108d8947Scgd {
750713ffa0eSlukem 
751108d8947Scgd 	switch(signo) {
752108d8947Scgd 	case SIGALRM:
753108d8947Scgd 	case SIGBUS:
754108d8947Scgd 	case SIGFPE:
755108d8947Scgd 	case SIGHUP:
756108d8947Scgd 	case SIGTERM:
757108d8947Scgd 	case SIGTRAP:
758108d8947Scgd 		if (pipeout)
759c8d11eb8Schristos 			quit("Signal on pipe: cannot recover");
7605c897ed9Smrg 		msg("Rewriting attempted as response to signal %s.\n", sys_siglist[signo]);
761108d8947Scgd 		(void)fflush(stderr);
762108d8947Scgd 		(void)fflush(stdout);
763108d8947Scgd 		close_rewind();
764ccfa3742Smycroft 		exit(X_REWRITE);
765108d8947Scgd 		/* NOTREACHED */
766108d8947Scgd 	case SIGSEGV:
767108d8947Scgd 		msg("SIGSEGV: ABORTING!\n");
768108d8947Scgd 		(void)signal(SIGSEGV, SIG_DFL);
769108d8947Scgd 		(void)kill(0, SIGSEGV);
770108d8947Scgd 		/* NOTREACHED */
771108d8947Scgd 	}
772108d8947Scgd }
773108d8947Scgd 
774dd6c22edSmycroft /*
775dd6c22edSmycroft  * obsolete --
776dd6c22edSmycroft  *	Change set of key letters and ordered arguments into something
777dd6c22edSmycroft  *	getopt(3) will like.
778dd6c22edSmycroft  */
779dd6c22edSmycroft static void
780713ffa0eSlukem obsolete(int *argcp, char **argvp[])
781108d8947Scgd {
782dd6c22edSmycroft 	int argc, flags;
783dd6c22edSmycroft 	char *ap, **argv, *flagsp, **nargv, *p;
784108d8947Scgd 
785dd6c22edSmycroft 	/* Setup. */
786dd6c22edSmycroft 	argv = *argvp;
787dd6c22edSmycroft 	argc = *argcp;
788dd6c22edSmycroft 
789dd6c22edSmycroft 	/* Return if no arguments or first argument has leading dash. */
790dd6c22edSmycroft 	ap = argv[1];
791dd6c22edSmycroft 	if (argc == 1 || *ap == '-')
792dd6c22edSmycroft 		return;
793dd6c22edSmycroft 
794dd6c22edSmycroft 	/* Allocate space for new arguments. */
7959af1692eSlukem 	*argvp = nargv = xmalloc((argc + 1) * sizeof(char *));
7969af1692eSlukem 	p = flagsp = xmalloc(strlen(ap) + 2);
797dd6c22edSmycroft 
798dd6c22edSmycroft 	*nargv++ = *argv;
799dd6c22edSmycroft 	argv += 2;
800dd6c22edSmycroft 
801dd6c22edSmycroft 	for (flags = 0; *ap; ++ap) {
802dd6c22edSmycroft 		switch (*ap) {
803dd6c22edSmycroft 		case 'B':
804dd6c22edSmycroft 		case 'b':
805dd6c22edSmycroft 		case 'd':
806dd6c22edSmycroft 		case 'f':
807dd6c22edSmycroft 		case 'h':
808dd6c22edSmycroft 		case 's':
809dd6c22edSmycroft 		case 'T':
8101f51c280Shannken 		case 'x':
811dd6c22edSmycroft 			if (*argv == NULL) {
812dd6c22edSmycroft 				warnx("option requires an argument -- %c", *ap);
813dd6c22edSmycroft 				usage();
814108d8947Scgd 			}
8159af1692eSlukem 			nargv[0] = xmalloc(strlen(*argv) + 2 + 1);
816dd6c22edSmycroft 			nargv[0][0] = '-';
817dd6c22edSmycroft 			nargv[0][1] = *ap;
818865fe390Slukem 			(void)strcpy(&nargv[0][2], *argv); /* XXX safe strcpy */
819dd6c22edSmycroft 			++argv;
820dd6c22edSmycroft 			++nargv;
821dd6c22edSmycroft 			break;
822dd6c22edSmycroft 		default:
823dd6c22edSmycroft 			if (!flags) {
824dd6c22edSmycroft 				*p++ = '-';
825dd6c22edSmycroft 				flags = 1;
826dd6c22edSmycroft 			}
827dd6c22edSmycroft 			*p++ = *ap;
828dd6c22edSmycroft 			break;
829dd6c22edSmycroft 		}
830dd6c22edSmycroft 	}
831dd6c22edSmycroft 
832dd6c22edSmycroft 	/* Terminate flags. */
833dd6c22edSmycroft 	if (flags) {
834dd6c22edSmycroft 		*p = '\0';
835dd6c22edSmycroft 		*nargv++ = flagsp;
8366edcc275Schristos 	} else
8376edcc275Schristos 		free(flagsp);
838dd6c22edSmycroft 
839dd6c22edSmycroft 	/* Copy remaining arguments. */
8404c54f5b7Slukem 	while ((*nargv++ = *argv++) != NULL)
8414c54f5b7Slukem 		;
842dd6c22edSmycroft 
843dd6c22edSmycroft 	/* Update argument count. */
844dd6c22edSmycroft 	*argcp = nargv - *argvp - 1;
845dd6c22edSmycroft }
8469af1692eSlukem 
8479af1692eSlukem 
8489af1692eSlukem void *
8499af1692eSlukem xcalloc(size_t number, size_t size)
8509af1692eSlukem {
8519af1692eSlukem 	void *p;
8529af1692eSlukem 
8539af1692eSlukem 	p = calloc(number, size);
8549af1692eSlukem 	if (p == NULL)
855c8d11eb8Schristos 		quite(errno, "Can't allocate %zu bytes", size * number);
8569af1692eSlukem 	return (p);
8579af1692eSlukem }
8589af1692eSlukem 
8599af1692eSlukem void *
8609af1692eSlukem xmalloc(size_t size)
8619af1692eSlukem {
8629af1692eSlukem 	void *p;
8639af1692eSlukem 
8649af1692eSlukem 	p = malloc(size);
8659af1692eSlukem 	if (p == NULL)
866c8d11eb8Schristos 		quite(errno, "Can't allocate %zu bytes", size);
8679af1692eSlukem 	return (p);
8689af1692eSlukem }
8699af1692eSlukem 
8709af1692eSlukem char *
8719af1692eSlukem xstrdup(const char *str)
8729af1692eSlukem {
8739af1692eSlukem 	char *p;
8749af1692eSlukem 
8759af1692eSlukem 	p = strdup(str);
8769af1692eSlukem 	if (p == NULL)
877c8d11eb8Schristos 		quite(errno, "Can't copy %s", str);
8789af1692eSlukem 	return (p);
8799af1692eSlukem }
880