args.c revision 51208
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
4235773Scharnierstatic const char rcsid[] =
4350471Speter  "$FreeBSD: head/bin/dd/args.c 51208 1999-09-12 16:51:53Z green $";
441556Srgrimes#endif /* not lint */
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
571556Srgrimesstatic int	c_arg __P((const void *, const void *));
581556Srgrimesstatic int	c_conv __P((const void *, const void *));
591556Srgrimesstatic void	f_bs __P((char *));
601556Srgrimesstatic void	f_cbs __P((char *));
611556Srgrimesstatic void	f_conv __P((char *));
621556Srgrimesstatic void	f_count __P((char *));
631556Srgrimesstatic void	f_files __P((char *));
641556Srgrimesstatic void	f_ibs __P((char *));
651556Srgrimesstatic void	f_if __P((char *));
661556Srgrimesstatic void	f_obs __P((char *));
671556Srgrimesstatic void	f_of __P((char *));
681556Srgrimesstatic void	f_seek __P((char *));
691556Srgrimesstatic void	f_skip __P((char *));
7051137Sgreenstatic quad_t	get_num __P((char *));
7151208Sgreenstatic off_t	get_offset __P((char *));
721556Srgrimes
7351208Sgreenstatic const struct arg {
741556Srgrimes	char *name;
751556Srgrimes	void (*f) __P((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 },
851556Srgrimes	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
861556Srgrimes	{ "of",		f_of,		C_OF,	 C_OF },
871556Srgrimes	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
881556Srgrimes	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
891556Srgrimes};
901556Srgrimes
911556Srgrimesstatic char *oper;
921556Srgrimes
931556Srgrimes/*
941556Srgrimes * args -- parse JCL syntax of dd.
951556Srgrimes */
961556Srgrimesvoid
971556Srgrimesjcl(argv)
981556Srgrimes	char **argv;
991556Srgrimes{
1001556Srgrimes	struct arg *ap, tmp;
1011556Srgrimes	char *arg;
1021556Srgrimes
1031556Srgrimes	in.dbsz = out.dbsz = 512;
1041556Srgrimes
10519720Sphk	while ((oper = *++argv) != NULL) {
10630230Seivind		if ((oper = strdup(oper)) == NULL)
10748051Sgreen			errx(1, "unable to allocate space for the argument \"%s\"", *argv);
1081556Srgrimes		if ((arg = strchr(oper, '=')) == NULL)
1091556Srgrimes			errx(1, "unknown operand %s", oper);
1101556Srgrimes		*arg++ = '\0';
1111556Srgrimes		if (!*arg)
1121556Srgrimes			errx(1, "no value specified for %s", oper);
1131556Srgrimes		tmp.name = oper;
11448051Sgreen		if (!(ap = (struct arg *)bsearch(&tmp, args,
11548051Sgreen		    sizeof(args)/sizeof(struct arg), sizeof(struct arg),
11648051Sgreen		    c_arg)))
1171556Srgrimes			errx(1, "unknown operand %s", tmp.name);
1181556Srgrimes		if (ddflags & ap->noset)
11948051Sgreen			errx(1, "%s: illegal argument combination or already set",
12048051Sgreen			    tmp.name);
1211556Srgrimes		ddflags |= ap->set;
1221556Srgrimes		ap->f(arg);
1231556Srgrimes	}
1241556Srgrimes
1251556Srgrimes	/* Final sanity checks. */
1261556Srgrimes
1271556Srgrimes	if (ddflags & C_BS) {
1281556Srgrimes		/*
1291556Srgrimes		 * Bs is turned off by any conversion -- we assume the user
1301556Srgrimes		 * just wanted to set both the input and output block sizes
1311556Srgrimes		 * and didn't want the bs semantics, so we don't warn.
1321556Srgrimes		 */
1331556Srgrimes		if (ddflags & (C_BLOCK|C_LCASE|C_SWAB|C_UCASE|C_UNBLOCK))
1341556Srgrimes			ddflags &= ~C_BS;
1351556Srgrimes
1361556Srgrimes		/* Bs supersedes ibs and obs. */
1371556Srgrimes		if (ddflags & C_BS && ddflags & (C_IBS|C_OBS))
1381556Srgrimes			warnx("bs supersedes ibs and obs");
1391556Srgrimes	}
1401556Srgrimes
1411556Srgrimes	/*
1421556Srgrimes	 * Ascii/ebcdic and cbs implies block/unblock.
1431556Srgrimes	 * Block/unblock requires cbs and vice-versa.
1441556Srgrimes	 */
1451556Srgrimes	if (ddflags & (C_BLOCK|C_UNBLOCK)) {
1461556Srgrimes		if (!(ddflags & C_CBS))
1471556Srgrimes			errx(1, "record operations require cbs");
1481556Srgrimes		if (cbsz == 0)
1491556Srgrimes			errx(1, "cbs cannot be zero");
1501556Srgrimes		cfunc = ddflags & C_BLOCK ? block : unblock;
1511556Srgrimes	} else if (ddflags & C_CBS) {
1521556Srgrimes		if (ddflags & (C_ASCII|C_EBCDIC)) {
1531556Srgrimes			if (ddflags & C_ASCII) {
1541556Srgrimes				ddflags |= C_UNBLOCK;
1551556Srgrimes				cfunc = unblock;
1561556Srgrimes			} else {
1571556Srgrimes				ddflags |= C_BLOCK;
1581556Srgrimes				cfunc = block;
1591556Srgrimes			}
1601556Srgrimes		} else
1611556Srgrimes			errx(1, "cbs meaningless if not doing record operations");
1621556Srgrimes	} else
1631556Srgrimes		cfunc = def;
16451208Sgreen	/*
16551208Sgreen	 * Bail out if the calculation of a file offset would overflow.
16651208Sgreen	 */
16751208Sgreen	if (in.offset > QUAD_MAX / in.dbsz || out.offset > QUAD_MAX / out.dbsz)
16851208Sgreen		errx(1, "seek offsets cannot be larger than %qd", QUAD_MAX);
1691556Srgrimes}
1701556Srgrimes
1711556Srgrimesstatic int
1721556Srgrimesc_arg(a, b)
1731556Srgrimes	const void *a, *b;
1741556Srgrimes{
1751556Srgrimes
1761556Srgrimes	return (strcmp(((struct arg *)a)->name, ((struct arg *)b)->name));
1771556Srgrimes}
1781556Srgrimes
1791556Srgrimesstatic void
1801556Srgrimesf_bs(arg)
1811556Srgrimes	char *arg;
1821556Srgrimes{
18351208Sgreen	quad_t res;
1841556Srgrimes
18551208Sgreen	res = get_num(arg);
18651208Sgreen	if (res < 1 || res > SSIZE_MAX)
18751208Sgreen		errx(1, "bs must be between 1 and %d", SSIZE_MAX);
18851208Sgreen	in.dbsz = out.dbsz = (size_t)res;
1891556Srgrimes}
1901556Srgrimes
1911556Srgrimesstatic void
1921556Srgrimesf_cbs(arg)
1931556Srgrimes	char *arg;
1941556Srgrimes{
19551208Sgreen	quad_t res;
1961556Srgrimes
19751208Sgreen	res = get_num(arg);
19851208Sgreen	if (res < 1 || res > SSIZE_MAX)
19951208Sgreen		errx(1, "cbs must be between 1 and %d", SSIZE_MAX);
20051208Sgreen	cbsz = (size_t)res;
2011556Srgrimes}
2021556Srgrimes
2031556Srgrimesstatic void
2041556Srgrimesf_count(arg)
2051556Srgrimes	char *arg;
2061556Srgrimes{
2071556Srgrimes
20851137Sgreen	cpy_cnt = get_num(arg);
20951137Sgreen
2101556Srgrimes	if (!cpy_cnt)
2111556Srgrimes		terminate(0);
2121556Srgrimes}
2131556Srgrimes
2141556Srgrimesstatic void
2151556Srgrimesf_files(arg)
2161556Srgrimes	char *arg;
2171556Srgrimes{
2181556Srgrimes
21951137Sgreen	files_cnt = get_num(arg);
2201556Srgrimes}
2211556Srgrimes
2221556Srgrimesstatic void
2231556Srgrimesf_ibs(arg)
2241556Srgrimes	char *arg;
2251556Srgrimes{
22651208Sgreen	quad_t res;
2271556Srgrimes
22848051Sgreen	if (!(ddflags & C_BS)) {
22951208Sgreen		res = get_num(arg);
23051208Sgreen		if (res < 1 || res > SSIZE_MAX)
23148051Sgreen			errx(1, "ibs must be between 1 and %d", INT_MAX);
23248051Sgreen		in.dbsz = (int)res;
23348051Sgreen	}
2341556Srgrimes}
2351556Srgrimes
2361556Srgrimesstatic void
2371556Srgrimesf_if(arg)
2381556Srgrimes	char *arg;
2391556Srgrimes{
2401556Srgrimes
2411556Srgrimes	in.name = arg;
2421556Srgrimes}
2431556Srgrimes
2441556Srgrimesstatic void
2451556Srgrimesf_obs(arg)
2461556Srgrimes	char *arg;
2471556Srgrimes{
24851208Sgreen	quad_t res;
2491556Srgrimes
25048051Sgreen	if (!(ddflags & C_BS)) {
25151208Sgreen		res = get_num(arg);
25251208Sgreen		if (res < 1 || res > SSIZE_MAX)
25351208Sgreen			errx(1, "obs must be between 1 and %d", SSIZE_MAX);
25451208Sgreen		out.dbsz = (size_t)res;
25548051Sgreen	}
2561556Srgrimes}
2571556Srgrimes
2581556Srgrimesstatic void
2591556Srgrimesf_of(arg)
2601556Srgrimes	char *arg;
2611556Srgrimes{
2621556Srgrimes
2631556Srgrimes	out.name = arg;
2641556Srgrimes}
2651556Srgrimes
2661556Srgrimesstatic void
2671556Srgrimesf_seek(arg)
2681556Srgrimes	char *arg;
2691556Srgrimes{
2701556Srgrimes
27151208Sgreen	out.offset = get_offset(arg);
2721556Srgrimes}
2731556Srgrimes
2741556Srgrimesstatic void
2751556Srgrimesf_skip(arg)
2761556Srgrimes	char *arg;
2771556Srgrimes{
2781556Srgrimes
27951208Sgreen	in.offset = get_offset(arg);
2801556Srgrimes}
2811556Srgrimes
28251208Sgreenstatic const struct conv {
2831556Srgrimes	char *name;
2841556Srgrimes	u_int set, noset;
28551208Sgreen	const u_char *ctab;
2861556Srgrimes} clist[] = {
2871556Srgrimes	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
2881556Srgrimes	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
2891556Srgrimes	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
2901556Srgrimes	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
2911556Srgrimes	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
2921556Srgrimes	{ "noerror",	C_NOERROR,	0,		NULL },
2931556Srgrimes	{ "notrunc",	C_NOTRUNC,	0,		NULL },
2941556Srgrimes	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
2951556Srgrimes	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
2961556Srgrimes	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
29731120Sjoerg	{ "osync",	C_OSYNC,	C_BS,		NULL },
29830312Sjoerg	{ "sparse",	C_SPARSE,	0,		NULL },
2991556Srgrimes	{ "swab",	C_SWAB,		0,		NULL },
3001556Srgrimes	{ "sync",	C_SYNC,		0,		NULL },
3011556Srgrimes	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
3021556Srgrimes	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
3031556Srgrimes};
3041556Srgrimes
3051556Srgrimesstatic void
3061556Srgrimesf_conv(arg)
3071556Srgrimes	char *arg;
3081556Srgrimes{
3091556Srgrimes	struct conv *cp, tmp;
3101556Srgrimes
3111556Srgrimes	while (arg != NULL) {
3121556Srgrimes		tmp.name = strsep(&arg, ",");
31351208Sgreen		cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv),
31451208Sgreen		    sizeof(struct conv), c_conv);
31551208Sgreen		if (cp == NULL)
3161556Srgrimes			errx(1, "unknown conversion %s", tmp.name);
3171556Srgrimes		if (ddflags & cp->noset)
3181556Srgrimes			errx(1, "%s: illegal conversion combination", tmp.name);
3191556Srgrimes		ddflags |= cp->set;
3201556Srgrimes		if (cp->ctab)
3211556Srgrimes			ctab = cp->ctab;
3221556Srgrimes	}
3231556Srgrimes}
3241556Srgrimes
3251556Srgrimesstatic int
3261556Srgrimesc_conv(a, b)
3271556Srgrimes	const void *a, *b;
3281556Srgrimes{
3291556Srgrimes
3301556Srgrimes	return (strcmp(((struct conv *)a)->name, ((struct conv *)b)->name));
3311556Srgrimes}
3321556Srgrimes
3331556Srgrimes/*
33448051Sgreen * Convert an expression of the following forms to a quad_t.
3351556Srgrimes * 	1) A positive decimal number.
33648051Sgreen *	2) A positive decimal number followed by a b (mult by 512.)
33748051Sgreen *	3) A positive decimal number followed by a k (mult by 1 << 10.)
33848051Sgreen *	4) A positive decimal number followed by a m (mult by 1 << 20.)
33948051Sgreen *	5) A positive decimal number followed by a g (mult by 1 << 30.)
34048051Sgreen *	5) A positive decimal number followed by a w (mult by sizeof int.)
34148051Sgreen *	6) Two or more positive decimal numbers (with/without [bkmgw])
34246684Skris *	   separated by x (also * for backwards compatibility), specifying
3431556Srgrimes *	   the product of the indicated values.
3441556Srgrimes */
34548051Sgreenstatic quad_t
34651137Sgreenget_num(val)
3471556Srgrimes	char *val;
3481556Srgrimes{
34948051Sgreen	quad_t num, t;
3501556Srgrimes	char *expr;
3511556Srgrimes
35248051Sgreen	errno = 0;
35348026Sgreen	num = strtoq(val, &expr, 0);
35451208Sgreen	if (errno != 0)				/* Overflow or underflow. */
35548051Sgreen		err(1, "%s", oper);
35648051Sgreen
35751208Sgreen	if (expr == val)			/* No valid digits. */
3581556Srgrimes		errx(1, "%s: illegal numeric value", oper);
3591556Srgrimes
36051137Sgreen	switch (*expr) {
3611556Srgrimes	case 'b':
3621556Srgrimes		t = num;
3631556Srgrimes		num *= 512;
3641556Srgrimes		if (t > num)
3651556Srgrimes			goto erange;
3661556Srgrimes		++expr;
3671556Srgrimes		break;
3681556Srgrimes	case 'k':
3691556Srgrimes		t = num;
37048051Sgreen		num *= 1 << 10;
3711556Srgrimes		if (t > num)
3721556Srgrimes			goto erange;
3731556Srgrimes		++expr;
3741556Srgrimes		break;
3751556Srgrimes	case 'm':
3761556Srgrimes		t = num;
37748051Sgreen		num *= 1 << 20;
3781556Srgrimes		if (t > num)
3791556Srgrimes			goto erange;
3801556Srgrimes		++expr;
3811556Srgrimes		break;
38248051Sgreen	case 'g':
38348051Sgreen		t = num;
38448051Sgreen		num *= 1 << 30;
38548051Sgreen		if (t > num)
38648051Sgreen			goto erange;
38748051Sgreen		++expr;
38848051Sgreen		break;
3891556Srgrimes	case 'w':
3901556Srgrimes		t = num;
3911556Srgrimes		num *= sizeof(int);
3921556Srgrimes		if (t > num)
3931556Srgrimes			goto erange;
3941556Srgrimes		++expr;
3951556Srgrimes		break;
3961556Srgrimes	}
3971556Srgrimes
39851137Sgreen	switch (*expr) {
3991556Srgrimes		case '\0':
4001556Srgrimes			break;
4011556Srgrimes		case '*':			/* Backward compatible. */
4021556Srgrimes		case 'x':
4031556Srgrimes			t = num;
40451137Sgreen			num *= get_num(expr + 1);
40551137Sgreen			if (t <= num)
40651137Sgreen				break;
40751137Sgreenerange:
40851137Sgreen			errx(1, "%s: %s", oper, strerror(ERANGE));
4091556Srgrimes		default:
4101556Srgrimes			errx(1, "%s: illegal numeric value", oper);
4111556Srgrimes	}
4121556Srgrimes	return (num);
4131556Srgrimes}
41451208Sgreen
41551208Sgreenstatic off_t
41651208Sgreenget_offset(val)
41751208Sgreen	char *val;
41851208Sgreen{
41951208Sgreen	quad_t num;
42051208Sgreen
42151208Sgreen	num = get_num(val);
42251208Sgreen	if (num > QUAD_MAX || num < 0)		/* XXX quad_t != off_t */
42351208Sgreen		errx(1, "%s: illegal offset", oper);	/* Too big/negative. */
42451208Sgreen	return ((off_t)num);
42551208Sgreen}
426