args.c revision 99109
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Keith Muller of the University of California, San Diego and Lance
71556Srgrimes * Visser of Convex Computer Corporation.
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 * 3. All advertising materials mentioning features or use of this software
181556Srgrimes *    must display the following acknowledgement:
191556Srgrimes *	This product includes software developed by the University of
201556Srgrimes *	California, Berkeley and its contributors.
211556Srgrimes * 4. Neither the name of the University nor the names of its contributors
221556Srgrimes *    may be used to endorse or promote products derived from this software
231556Srgrimes *    without specific prior written permission.
241556Srgrimes *
251556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
261556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
271556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
281556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
291556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
301556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
311556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
321556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
331556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
341556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
351556Srgrimes * SUCH DAMAGE.
361556Srgrimes */
371556Srgrimes
381556Srgrimes#ifndef lint
3935773Scharnier#if 0
4036007Scharnierstatic char sccsid[] = "@(#)args.c	8.3 (Berkeley) 4/2/94";
4135773Scharnier#endif
421556Srgrimes#endif /* not lint */
4399109Sobrien#include <sys/cdefs.h>
4499109Sobrien__FBSDID("$FreeBSD: head/bin/dd/args.c 99109 2002-06-30 05:13:54Z obrien $");
451556Srgrimes
461556Srgrimes#include <sys/types.h>
471556Srgrimes
481556Srgrimes#include <err.h>
491556Srgrimes#include <errno.h>
501556Srgrimes#include <limits.h>
511556Srgrimes#include <stdlib.h>
521556Srgrimes#include <string.h>
531556Srgrimes
541556Srgrimes#include "dd.h"
551556Srgrimes#include "extern.h"
561556Srgrimes
5790108Simpstatic int	c_arg(const void *, const void *);
5890108Simpstatic int	c_conv(const void *, const void *);
5990108Simpstatic void	f_bs(char *);
6090108Simpstatic void	f_cbs(char *);
6190108Simpstatic void	f_conv(char *);
6290108Simpstatic void	f_count(char *);
6390108Simpstatic void	f_files(char *);
6490108Simpstatic void	f_ibs(char *);
6590108Simpstatic void	f_if(char *);
6690108Simpstatic void	f_obs(char *);
6790108Simpstatic void	f_of(char *);
6890108Simpstatic void	f_seek(char *);
6990108Simpstatic void	f_skip(char *);
7090108Simpstatic u_quad_t	get_num(const char *);
7190108Simpstatic off_t	get_off_t(const char *);
721556Srgrimes
7351208Sgreenstatic const struct arg {
7454245Sgreen	const char *name;
7590108Simp	void (*f)(char *);
761556Srgrimes	u_int set, noset;
771556Srgrimes} args[] = {
781556Srgrimes	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
791556Srgrimes	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
801556Srgrimes	{ "conv",	f_conv,		0,	 0 },
811556Srgrimes	{ "count",	f_count,	C_COUNT, C_COUNT },
821556Srgrimes	{ "files",	f_files,	C_FILES, C_FILES },
831556Srgrimes	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
841556Srgrimes	{ "if",		f_if,		C_IF,	 C_IF },
8557523Sgreen	{ "iseek",	f_skip,		C_SKIP,	 C_SKIP },
861556Srgrimes	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
871556Srgrimes	{ "of",		f_of,		C_OF,	 C_OF },
8857523Sgreen	{ "oseek",	f_seek,		C_SEEK,	 C_SEEK },
891556Srgrimes	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
901556Srgrimes	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
911556Srgrimes};
921556Srgrimes
931556Srgrimesstatic char *oper;
941556Srgrimes
951556Srgrimes/*
961556Srgrimes * args -- parse JCL syntax of dd.
971556Srgrimes */
981556Srgrimesvoid
9990108Simpjcl(char **argv)
1001556Srgrimes{
1011556Srgrimes	struct arg *ap, tmp;
1021556Srgrimes	char *arg;
1031556Srgrimes
1041556Srgrimes	in.dbsz = out.dbsz = 512;
1051556Srgrimes
10619720Sphk	while ((oper = *++argv) != NULL) {
10730230Seivind		if ((oper = strdup(oper)) == NULL)
10848051Sgreen			errx(1, "unable to allocate space for the argument \"%s\"", *argv);
1091556Srgrimes		if ((arg = strchr(oper, '=')) == NULL)
1101556Srgrimes			errx(1, "unknown operand %s", oper);
1111556Srgrimes		*arg++ = '\0';
1121556Srgrimes		if (!*arg)
1131556Srgrimes			errx(1, "no value specified for %s", oper);
1141556Srgrimes		tmp.name = oper;
11548051Sgreen		if (!(ap = (struct arg *)bsearch(&tmp, args,
11648051Sgreen		    sizeof(args)/sizeof(struct arg), sizeof(struct arg),
11748051Sgreen		    c_arg)))
1181556Srgrimes			errx(1, "unknown operand %s", tmp.name);
1191556Srgrimes		if (ddflags & ap->noset)
12048051Sgreen			errx(1, "%s: illegal argument combination or already set",
12148051Sgreen			    tmp.name);
1221556Srgrimes		ddflags |= ap->set;
1231556Srgrimes		ap->f(arg);
1241556Srgrimes	}
1251556Srgrimes
1261556Srgrimes	/* Final sanity checks. */
1271556Srgrimes
1281556Srgrimes	if (ddflags & C_BS) {
1291556Srgrimes		/*
1301556Srgrimes		 * Bs is turned off by any conversion -- we assume the user
1311556Srgrimes		 * just wanted to set both the input and output block sizes
1321556Srgrimes		 * and didn't want the bs semantics, so we don't warn.
1331556Srgrimes		 */
13462311Sgreen		if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
13562311Sgreen		    C_UNBLOCK))
1361556Srgrimes			ddflags &= ~C_BS;
1371556Srgrimes
1381556Srgrimes		/* Bs supersedes ibs and obs. */
13962311Sgreen		if (ddflags & C_BS && ddflags & (C_IBS | C_OBS))
1401556Srgrimes			warnx("bs supersedes ibs and obs");
1411556Srgrimes	}
1421556Srgrimes
1431556Srgrimes	/*
1441556Srgrimes	 * Ascii/ebcdic and cbs implies block/unblock.
1451556Srgrimes	 * Block/unblock requires cbs and vice-versa.
1461556Srgrimes	 */
14751249Sgreen	if (ddflags & (C_BLOCK | C_UNBLOCK)) {
1481556Srgrimes		if (!(ddflags & C_CBS))
1491556Srgrimes			errx(1, "record operations require cbs");
1501556Srgrimes		if (cbsz == 0)
1511556Srgrimes			errx(1, "cbs cannot be zero");
1521556Srgrimes		cfunc = ddflags & C_BLOCK ? block : unblock;
1531556Srgrimes	} else if (ddflags & C_CBS) {
15451249Sgreen		if (ddflags & (C_ASCII | C_EBCDIC)) {
1551556Srgrimes			if (ddflags & C_ASCII) {
1561556Srgrimes				ddflags |= C_UNBLOCK;
1571556Srgrimes				cfunc = unblock;
1581556Srgrimes			} else {
1591556Srgrimes				ddflags |= C_BLOCK;
1601556Srgrimes				cfunc = block;
1611556Srgrimes			}
1621556Srgrimes		} else
1631556Srgrimes			errx(1, "cbs meaningless if not doing record operations");
1641556Srgrimes	} else
1651556Srgrimes		cfunc = def;
16651249Sgreen
16751208Sgreen	/*
16851208Sgreen	 * Bail out if the calculation of a file offset would overflow.
16951208Sgreen	 */
17051208Sgreen	if (in.offset > QUAD_MAX / in.dbsz || out.offset > QUAD_MAX / out.dbsz)
17151208Sgreen		errx(1, "seek offsets cannot be larger than %qd", QUAD_MAX);
1721556Srgrimes}
1731556Srgrimes
1741556Srgrimesstatic int
17590108Simpc_arg(const void *a, const void *b)
1761556Srgrimes{
1771556Srgrimes
17854278Sgreen	return (strcmp(((const struct arg *)a)->name,
17954278Sgreen	    ((const struct arg *)b)->name));
1801556Srgrimes}
1811556Srgrimes
1821556Srgrimesstatic void
18390108Simpf_bs(char *arg)
1841556Srgrimes{
18589788Sgreen	u_quad_t res;
1861556Srgrimes
18751208Sgreen	res = get_num(arg);
18851208Sgreen	if (res < 1 || res > SSIZE_MAX)
18951208Sgreen		errx(1, "bs must be between 1 and %d", SSIZE_MAX);
19051208Sgreen	in.dbsz = out.dbsz = (size_t)res;
1911556Srgrimes}
1921556Srgrimes
1931556Srgrimesstatic void
19490108Simpf_cbs(char *arg)
1951556Srgrimes{
19689788Sgreen	u_quad_t res;
1971556Srgrimes
19851208Sgreen	res = get_num(arg);
19951208Sgreen	if (res < 1 || res > SSIZE_MAX)
20051208Sgreen		errx(1, "cbs must be between 1 and %d", SSIZE_MAX);
20151208Sgreen	cbsz = (size_t)res;
2021556Srgrimes}
2031556Srgrimes
2041556Srgrimesstatic void
20590108Simpf_count(char *arg)
2061556Srgrimes{
20789788Sgreen	u_quad_t res;
2081556Srgrimes
20989788Sgreen	res = get_num(arg);
21089788Sgreen	if ((quad_t)res < 0)
21151326Sgreen		errx(1, "count cannot be negative");
21289788Sgreen	if (res == 0)
21351335Sgreen		cpy_cnt = -1;
21489788Sgreen	else
21589788Sgreen		cpy_cnt = (quad_t)res;
2161556Srgrimes}
2171556Srgrimes
2181556Srgrimesstatic void
21990108Simpf_files(char *arg)
2201556Srgrimes{
2211556Srgrimes
22251137Sgreen	files_cnt = get_num(arg);
22351249Sgreen	if (files_cnt < 1)
22451249Sgreen		errx(1, "files must be between 1 and %qd", QUAD_MAX);
2251556Srgrimes}
2261556Srgrimes
2271556Srgrimesstatic void
22890108Simpf_ibs(char *arg)
2291556Srgrimes{
23089788Sgreen	u_quad_t res;
2311556Srgrimes
23248051Sgreen	if (!(ddflags & C_BS)) {
23351208Sgreen		res = get_num(arg);
23451208Sgreen		if (res < 1 || res > SSIZE_MAX)
23551249Sgreen			errx(1, "ibs must be between 1 and %d", SSIZE_MAX);
23651249Sgreen		in.dbsz = (size_t)res;
23748051Sgreen	}
2381556Srgrimes}
2391556Srgrimes
2401556Srgrimesstatic void
24190108Simpf_if(char *arg)
2421556Srgrimes{
2431556Srgrimes
2441556Srgrimes	in.name = arg;
2451556Srgrimes}
2461556Srgrimes
2471556Srgrimesstatic void
24890108Simpf_obs(char *arg)
2491556Srgrimes{
25089788Sgreen	u_quad_t res;
2511556Srgrimes
25248051Sgreen	if (!(ddflags & C_BS)) {
25351208Sgreen		res = get_num(arg);
25451208Sgreen		if (res < 1 || res > SSIZE_MAX)
25551208Sgreen			errx(1, "obs must be between 1 and %d", SSIZE_MAX);
25651208Sgreen		out.dbsz = (size_t)res;
25748051Sgreen	}
2581556Srgrimes}
2591556Srgrimes
2601556Srgrimesstatic void
26190108Simpf_of(char *arg)
2621556Srgrimes{
2631556Srgrimes
2641556Srgrimes	out.name = arg;
2651556Srgrimes}
2661556Srgrimes
2671556Srgrimesstatic void
26890108Simpf_seek(char *arg)
2691556Srgrimes{
2701556Srgrimes
27189788Sgreen	out.offset = get_off_t(arg);
2721556Srgrimes}
2731556Srgrimes
2741556Srgrimesstatic void
27590108Simpf_skip(char *arg)
2761556Srgrimes{
2771556Srgrimes
27889788Sgreen	in.offset = get_off_t(arg);
2791556Srgrimes}
2801556Srgrimes
28151208Sgreenstatic const struct conv {
28254245Sgreen	const char *name;
2831556Srgrimes	u_int set, noset;
28451208Sgreen	const u_char *ctab;
2851556Srgrimes} clist[] = {
2861556Srgrimes	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
2871556Srgrimes	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
2881556Srgrimes	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
2891556Srgrimes	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
2901556Srgrimes	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
2911556Srgrimes	{ "noerror",	C_NOERROR,	0,		NULL },
2921556Srgrimes	{ "notrunc",	C_NOTRUNC,	0,		NULL },
2931556Srgrimes	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
2941556Srgrimes	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
2951556Srgrimes	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
29631120Sjoerg	{ "osync",	C_OSYNC,	C_BS,		NULL },
29730312Sjoerg	{ "sparse",	C_SPARSE,	0,		NULL },
2981556Srgrimes	{ "swab",	C_SWAB,		0,		NULL },
2991556Srgrimes	{ "sync",	C_SYNC,		0,		NULL },
3001556Srgrimes	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
3011556Srgrimes	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
3021556Srgrimes};
3031556Srgrimes
3041556Srgrimesstatic void
30590108Simpf_conv(char *arg)
3061556Srgrimes{
3071556Srgrimes	struct conv *cp, tmp;
3081556Srgrimes
3091556Srgrimes	while (arg != NULL) {
3101556Srgrimes		tmp.name = strsep(&arg, ",");
31151208Sgreen		cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv),
31251208Sgreen		    sizeof(struct conv), c_conv);
31351208Sgreen		if (cp == NULL)
3141556Srgrimes			errx(1, "unknown conversion %s", tmp.name);
3151556Srgrimes		if (ddflags & cp->noset)
3161556Srgrimes			errx(1, "%s: illegal conversion combination", tmp.name);
3171556Srgrimes		ddflags |= cp->set;
3181556Srgrimes		if (cp->ctab)
3191556Srgrimes			ctab = cp->ctab;
3201556Srgrimes	}
3211556Srgrimes}
3221556Srgrimes
3231556Srgrimesstatic int
32490108Simpc_conv(const void *a, const void *b)
3251556Srgrimes{
3261556Srgrimes
32754278Sgreen	return (strcmp(((const struct conv *)a)->name,
32854278Sgreen	    ((const struct conv *)b)->name));
3291556Srgrimes}
3301556Srgrimes
3311556Srgrimes/*
33289788Sgreen * Convert an expression of the following forms to a u_quad_t.
3331556Srgrimes * 	1) A positive decimal number.
33489788Sgreen *	2) A positive decimal number followed by a b (mult by 512).
33589788Sgreen *	3) A positive decimal number followed by a k (mult by 1 << 10).
33689788Sgreen *	4) A positive decimal number followed by a m (mult by 1 << 20).
33789788Sgreen *	5) A positive decimal number followed by a g (mult by 1 << 30).
33889788Sgreen *	5) A positive decimal number followed by a w (mult by sizeof int).
33948051Sgreen *	6) Two or more positive decimal numbers (with/without [bkmgw])
34046684Skris *	   separated by x (also * for backwards compatibility), specifying
3411556Srgrimes *	   the product of the indicated values.
3421556Srgrimes */
34389788Sgreenstatic u_quad_t
34490108Simpget_num(const char *val)
3451556Srgrimes{
34689788Sgreen	u_quad_t num, mult, prevnum;
3471556Srgrimes	char *expr;
3481556Srgrimes
34948051Sgreen	errno = 0;
35089788Sgreen	num = strtouq(val, &expr, 0);
35151208Sgreen	if (errno != 0)				/* Overflow or underflow. */
35248051Sgreen		err(1, "%s", oper);
35348051Sgreen
35451208Sgreen	if (expr == val)			/* No valid digits. */
3551556Srgrimes		errx(1, "%s: illegal numeric value", oper);
3561556Srgrimes
35789788Sgreen	mult = 0;
35851137Sgreen	switch (*expr) {
3591556Srgrimes	case 'b':
36089788Sgreen		mult = 512;
3611556Srgrimes		break;
3621556Srgrimes	case 'k':
36389788Sgreen		mult = 1 << 10;
3641556Srgrimes		break;
3651556Srgrimes	case 'm':
36689788Sgreen		mult = 1 << 20;
3671556Srgrimes		break;
36848051Sgreen	case 'g':
36989788Sgreen		mult = 1 << 30;
37048051Sgreen		break;
3711556Srgrimes	case 'w':
37289788Sgreen		mult = sizeof(int);
3731556Srgrimes		break;
37491079Smarkm	default:
37596383Sjedgar		;
3761556Srgrimes	}
3771556Srgrimes
37889788Sgreen	if (mult != 0) {
37989788Sgreen		prevnum = num;
38089788Sgreen		num *= mult;
38189788Sgreen		/* Check for overflow. */
38289788Sgreen		if (num / mult != prevnum)
38389788Sgreen			goto erange;
38489788Sgreen		expr++;
38589788Sgreen	}
38689788Sgreen
38751137Sgreen	switch (*expr) {
3881556Srgrimes		case '\0':
3891556Srgrimes			break;
3901556Srgrimes		case '*':			/* Backward compatible. */
3911556Srgrimes		case 'x':
39289788Sgreen			mult = get_num(expr + 1);
39389788Sgreen			prevnum = num;
39489788Sgreen			num *= mult;
39589788Sgreen			if (num / mult == prevnum)
39651137Sgreen				break;
39751137Sgreenerange:
39851137Sgreen			errx(1, "%s: %s", oper, strerror(ERANGE));
3991556Srgrimes		default:
4001556Srgrimes			errx(1, "%s: illegal numeric value", oper);
4011556Srgrimes	}
4021556Srgrimes	return (num);
4031556Srgrimes}
40451208Sgreen
40589788Sgreen/*
40689788Sgreen * Convert an expression of the following forms to an off_t.  This is the
40789788Sgreen * same as get_num(), but it uses signed numbers.
40889788Sgreen *
40989788Sgreen * The major problem here is that an off_t may not necessarily be a quad_t.
41089788Sgreen * The right thing to do would be to use intmax_t when available and then
41189788Sgreen * cast down to an off_t, if possible.
41289788Sgreen */
41351208Sgreenstatic off_t
41490108Simpget_off_t(const char *val)
41551208Sgreen{
41689788Sgreen	quad_t num, mult, prevnum;
41789788Sgreen	char *expr;
41851208Sgreen
41989788Sgreen	errno = 0;
42089788Sgreen	num = strtoq(val, &expr, 0);
42189788Sgreen	if (errno != 0)				/* Overflow or underflow. */
42289788Sgreen		err(1, "%s", oper);
42389788Sgreen
42489788Sgreen	if (expr == val)			/* No valid digits. */
42589788Sgreen		errx(1, "%s: illegal numeric value", oper);
42689788Sgreen
42789788Sgreen	mult = 0;
42889788Sgreen	switch (*expr) {
42989788Sgreen	case 'b':
43089788Sgreen		mult = 512;
43189788Sgreen		break;
43289788Sgreen	case 'k':
43389788Sgreen		mult = 1 << 10;
43489788Sgreen		break;
43589788Sgreen	case 'm':
43689788Sgreen		mult = 1 << 20;
43789788Sgreen		break;
43889788Sgreen	case 'g':
43989788Sgreen		mult = 1 << 30;
44089788Sgreen		break;
44189788Sgreen	case 'w':
44289788Sgreen		mult = sizeof(int);
44389788Sgreen		break;
44489788Sgreen	}
44589788Sgreen
44689788Sgreen	if (mult != 0) {
44789788Sgreen		prevnum = num;
44889788Sgreen		num *= mult;
44989788Sgreen		/* Check for overflow. */
45089788Sgreen		if ((prevnum > 0) != (num > 0) || num / mult != prevnum)
45189788Sgreen			goto erange;
45289788Sgreen		expr++;
45389788Sgreen	}
45489788Sgreen
45589788Sgreen	switch (*expr) {
45689788Sgreen		case '\0':
45789788Sgreen			break;
45889788Sgreen		case '*':			/* Backward compatible. */
45989788Sgreen		case 'x':
46089788Sgreen			mult = (quad_t)get_off_t(expr + 1);
46189788Sgreen			prevnum = num;
46289788Sgreen			num *= mult;
46390331Sgreen			if ((prevnum > 0) == (num > 0) && num / mult == prevnum)
46489788Sgreen				break;
46589788Sgreenerange:
46689788Sgreen			errx(1, "%s: %s", oper, strerror(ERANGE));
46789788Sgreen		default:
46889788Sgreen			errx(1, "%s: illegal numeric value", oper);
46989788Sgreen	}
47089788Sgreen	return (num);
47151208Sgreen}
472