xref: /dragonfly/bin/dd/args.c (revision 32c20b8b)
1 /*-
2  * Copyright (c) 1991, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Keith Muller of the University of California, San Diego and Lance
7  * Visser of Convex Computer Corporation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)args.c	8.3 (Berkeley) 4/2/94
34  * $FreeBSD: src/bin/dd/args.c,v 1.25.2.2 2001/01/23 14:20:03 asmodai Exp $
35  * $DragonFly: src/bin/dd/args.c,v 1.6 2008/01/28 16:08:02 matthias Exp $
36  */
37 
38 #include <sys/types.h>
39 
40 #include <err.h>
41 #include <errno.h>
42 #include <limits.h>
43 #include <stdlib.h>
44 #include <string.h>
45 
46 #include "dd.h"
47 #include "extern.h"
48 
49 static int	c_arg (const void *, const void *);
50 static int	c_conv (const void *, const void *);
51 static void	f_bs (char *);
52 static void	f_cbs (char *);
53 static void	f_conv (char *);
54 static void	f_count (char *);
55 static void	f_files (char *);
56 static void	f_ibs (char *);
57 static void	f_if (char *);
58 static void	f_obs(char *);
59 static void	f_of (char *);
60 static void	f_seek (char *);
61 static void	f_skip (char *);
62 static quad_t	get_num (char *);
63 static off_t	get_offset (char *);
64 
65 static const struct arg {
66 	const char *name;
67 	void (*f) (char *);
68 	u_int set, noset;
69 } args[] = {
70 	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
71 	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
72 	{ "conv",	f_conv,		0,	 0 },
73 	{ "count",	f_count,	C_COUNT, C_COUNT },
74 	{ "files",	f_files,	C_FILES, C_FILES },
75 	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
76 	{ "if",		f_if,		C_IF,	 C_IF },
77 	{ "iseek",	f_skip,		C_SKIP,	 C_SKIP },
78 	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
79 	{ "of",		f_of,		C_OF,	 C_OF },
80 	{ "oseek",	f_seek,		C_SEEK,	 C_SEEK },
81 	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
82 	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
83 };
84 
85 static char *oper;
86 
87 /*
88  * args -- parse JCL syntax of dd.
89  */
90 void
91 jcl(char **argv)
92 {
93 	struct arg *ap, tmp;
94 	char *arg;
95 
96 	in.dbsz = out.dbsz = 512;
97 
98 	while ((oper = *++argv) != NULL) {
99 		if ((oper = strdup(oper)) == NULL)
100 			errx(1, "unable to allocate space for the argument \"%s\"", *argv);
101 		if ((arg = strchr(oper, '=')) == NULL)
102 			errx(1, "unknown operand %s", oper);
103 		*arg++ = '\0';
104 		if (!*arg)
105 			errx(1, "no value specified for %s", oper);
106 		tmp.name = oper;
107 		if (!(ap = (struct arg *)bsearch(&tmp, args,
108 		    sizeof(args)/sizeof(struct arg), sizeof(struct arg),
109 		    c_arg)))
110 			errx(1, "unknown operand %s", tmp.name);
111 		if (ddflags & ap->noset)
112 			errx(1, "%s: illegal argument combination or already set",
113 			    tmp.name);
114 		ddflags |= ap->set;
115 		ap->f(arg);
116 	}
117 
118 	/* Final sanity checks. */
119 
120 	if (ddflags & C_BS) {
121 		/*
122 		 * Bs is turned off by any conversion -- we assume the user
123 		 * just wanted to set both the input and output block sizes
124 		 * and didn't want the bs semantics, so we don't warn.
125 		 */
126 		if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
127 		    C_UNBLOCK))
128 			ddflags &= ~C_BS;
129 
130 		/* Bs supersedes ibs and obs. */
131 		if (ddflags & C_BS && ddflags & (C_IBS | C_OBS))
132 			warnx("bs supersedes ibs and obs");
133 	}
134 
135 	/*
136 	 * Ascii/ebcdic and cbs implies block/unblock.
137 	 * Block/unblock requires cbs and vice-versa.
138 	 */
139 	if (ddflags & (C_BLOCK | C_UNBLOCK)) {
140 		if (!(ddflags & C_CBS))
141 			errx(1, "record operations require cbs");
142 		if (cbsz == 0)
143 			errx(1, "cbs cannot be zero");
144 		cfunc = ddflags & C_BLOCK ? block : unblock;
145 	} else if (ddflags & C_CBS) {
146 		if (ddflags & (C_ASCII | C_EBCDIC)) {
147 			if (ddflags & C_ASCII) {
148 				ddflags |= C_UNBLOCK;
149 				cfunc = unblock;
150 			} else {
151 				ddflags |= C_BLOCK;
152 				cfunc = block;
153 			}
154 		} else
155 			errx(1, "cbs meaningless if not doing record operations");
156 	} else
157 		cfunc = def;
158 
159 	/*
160 	 * Bail out if the calculation of a file offset would overflow.
161 	 */
162 	if (in.offset > QUAD_MAX / (ssize_t)in.dbsz ||
163 	    out.offset > QUAD_MAX / (ssize_t)out.dbsz) {
164 		errx(1, "seek offsets cannot be larger than %lld", QUAD_MAX);
165 	}
166 }
167 
168 static int
169 c_arg(const void *a, const void *b)
170 {
171 
172 	return (strcmp(((const struct arg *)a)->name,
173 	    ((const struct arg *)b)->name));
174 }
175 
176 static void
177 f_bs(char *arg)
178 {
179 	quad_t res;
180 
181 	res = get_num(arg);
182 	if (res < 1 || res > SSIZE_MAX)
183 		errx(1, "bs must be between 1 and %zd", SSIZE_MAX);
184 	in.dbsz = out.dbsz = (size_t)res;
185 }
186 
187 static void
188 f_cbs(char *arg)
189 {
190 	quad_t res;
191 
192 	res = get_num(arg);
193 	if (res < 1 || res > SSIZE_MAX)
194 		errx(1, "cbs must be between 1 and %zd", SSIZE_MAX);
195 	cbsz = (size_t)res;
196 }
197 
198 static void
199 f_count(char *arg)
200 {
201 
202 	cpy_cnt = get_num(arg);
203 	if (cpy_cnt < 0)
204 		errx(1, "count cannot be negative");
205 	if (cpy_cnt == 0)
206 		cpy_cnt = -1;
207 }
208 
209 static void
210 f_files(char *arg)
211 {
212 
213 	files_cnt = get_num(arg);
214 	if (files_cnt < 1)
215 		errx(1, "files must be between 1 and %qd", QUAD_MAX);
216 }
217 
218 static void
219 f_ibs(char *arg)
220 {
221 	quad_t res;
222 
223 	if (!(ddflags & C_BS)) {
224 		res = get_num(arg);
225 		if (res < 1 || res > SSIZE_MAX)
226 			errx(1, "ibs must be between 1 and %zd", SSIZE_MAX);
227 		in.dbsz = (size_t)res;
228 	}
229 }
230 
231 static void
232 f_if(char *arg)
233 {
234 
235 	in.name = arg;
236 }
237 
238 static void
239 f_obs(char *arg)
240 {
241 	quad_t res;
242 
243 	if (!(ddflags & C_BS)) {
244 		res = get_num(arg);
245 		if (res < 1 || res > SSIZE_MAX)
246 			errx(1, "obs must be between 1 and %zd", SSIZE_MAX);
247 		out.dbsz = (size_t)res;
248 	}
249 }
250 
251 static void
252 f_of(char *arg)
253 {
254 
255 	out.name = arg;
256 }
257 
258 static void
259 f_seek(char *arg)
260 {
261 
262 	out.offset = get_offset(arg);
263 }
264 
265 static void
266 f_skip(char *arg)
267 {
268 
269 	in.offset = get_offset(arg);
270 }
271 
272 static const struct conv {
273 	const char *name;
274 	u_int set, noset;
275 	const u_char *ctab;
276 } clist[] = {
277 	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
278 	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
279 	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
280 	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
281 	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
282 	{ "noerror",	C_NOERROR,	0,		NULL },
283 	{ "notrunc",	C_NOTRUNC,	0,		NULL },
284 	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
285 	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
286 	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
287 	{ "osync",	C_OSYNC,	C_BS,		NULL },
288 	{ "sparse",	C_SPARSE,	0,		NULL },
289 	{ "swab",	C_SWAB,		0,		NULL },
290 	{ "sync",	C_SYNC,		0,		NULL },
291 	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
292 	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
293 };
294 
295 static void
296 f_conv(char *arg)
297 {
298 	struct conv *cp, tmp;
299 
300 	while (arg != NULL) {
301 		tmp.name = strsep(&arg, ",");
302 		cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv),
303 		    sizeof(struct conv), c_conv);
304 		if (cp == NULL)
305 			errx(1, "unknown conversion %s", tmp.name);
306 		if (ddflags & cp->noset)
307 			errx(1, "%s: illegal conversion combination", tmp.name);
308 		ddflags |= cp->set;
309 		if (cp->ctab)
310 			ctab = cp->ctab;
311 	}
312 }
313 
314 static int
315 c_conv(const void *a, const void *b)
316 {
317 
318 	return (strcmp(((const struct conv *)a)->name,
319 	    ((const struct conv *)b)->name));
320 }
321 
322 /*
323  * Convert an expression of the following forms to a quad_t.
324  * 	1) A positive decimal number.
325  *	2) A positive decimal number followed by a b (mult by 512.)
326  *	3) A positive decimal number followed by a k (mult by 1 << 10.)
327  *	4) A positive decimal number followed by a m (mult by 1 << 20.)
328  *	5) A positive decimal number followed by a g (mult by 1 << 30.)
329  *	5) A positive decimal number followed by a w (mult by sizeof int.)
330  *	6) Two or more positive decimal numbers (with/without [bkmgw])
331  *	   separated by x (also * for backwards compatibility), specifying
332  *	   the product of the indicated values.
333  */
334 static quad_t
335 get_num(char *val)
336 {
337 	quad_t num, t;
338 	char *expr;
339 
340 	errno = 0;
341 	num = strtoll(val, &expr, 0);
342 	if (errno != 0)				/* Overflow or underflow. */
343 		err(1, "%s", oper);
344 
345 	if (expr == val)			/* No valid digits. */
346 		errx(1, "%s: illegal numeric value", oper);
347 
348 	switch (*expr) {
349 	case 'b':
350 	case 'B':
351 		t = num;
352 		num *= 512;
353 		if (t > num)
354 			goto erange;
355 		++expr;
356 		break;
357 	case 'k':
358 	case 'K':
359 		t = num;
360 		num *= 1 << 10;
361 		if (t > num)
362 			goto erange;
363 		++expr;
364 		break;
365 	case 'm':
366 	case 'M':
367 		t = num;
368 		num *= 1 << 20;
369 		if (t > num)
370 			goto erange;
371 		++expr;
372 		break;
373 	case 'g':
374 	case 'G':
375 		t = num;
376 		num *= 1 << 30;
377 		if (t > num)
378 			goto erange;
379 		++expr;
380 		break;
381 	case 'w':
382 		t = num;
383 		num *= sizeof(int);
384 		if (t > num)
385 			goto erange;
386 		++expr;
387 		break;
388 	}
389 
390 	switch (*expr) {
391 		case '\0':
392 			break;
393 		case '*':			/* Backward compatible. */
394 		case 'x':
395 			t = num;
396 			num *= get_num(expr + 1);
397 			if (t <= num)
398 				break;
399 erange:
400 			errx(1, "%s: %s", oper, strerror(ERANGE));
401 		default:
402 			errx(1, "%s: illegal numeric value", oper);
403 	}
404 	return (num);
405 }
406 
407 static off_t
408 get_offset(char *val)
409 {
410 	quad_t num;
411 
412 	num = get_num(val);
413 	if (num > QUAD_MAX)	/* XXX can't happen && quad_t != off_t */
414 		errx(1, "%s: illegal offset", oper);	/* Too big. */
415 	return ((off_t)num);
416 }
417