1/*	$OpenBSD: args.c,v 1.31 2019/02/16 10:54:00 bluhm Exp $	*/
2/*	$NetBSD: args.c,v 1.7 1996/03/01 01:18:58 jtc Exp $	*/
3
4/*-
5 * Copyright (c) 1991, 1993, 1994
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Keith Muller of the University of California, San Diego and Lance
10 * Visser of Convex Computer Corporation.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#include <sys/types.h>
38#include <sys/time.h>
39
40#include <err.h>
41#include <errno.h>
42#include <limits.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46
47#include "dd.h"
48#include "extern.h"
49
50static int	c_arg(const void *, const void *);
51static void	f_bs(char *);
52static void	f_cbs(char *);
53static void	f_conv(char *);
54static void	f_count(char *);
55static void	f_files(char *);
56static void	f_ibs(char *);
57static void	f_if(char *);
58static void	f_obs(char *);
59static void	f_of(char *);
60static void	f_seek(char *);
61static void	f_skip(char *);
62static void	f_status(char *);
63static size_t	get_bsz(char *);
64static off_t	get_off(char *);
65
66static const struct arg {
67	const char *name;
68	void (*f)(char *);
69	u_int set, noset;
70} args[] = {
71	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
72	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
73	{ "conv",	f_conv,		0,	 0 },
74	{ "count",	f_count,	C_COUNT, C_COUNT },
75	{ "files",	f_files,	C_FILES, C_FILES },
76	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
77	{ "if",		f_if,		C_IF,	 C_IF },
78	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
79	{ "of",		f_of,		C_OF,	 C_OF },
80	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
81	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
82	{ "status",	f_status,	C_STATUS,C_STATUS },
83};
84
85static char *oper;
86
87/*
88 * args -- parse JCL syntax of dd.
89 */
90void
91jcl(char **argv)
92{
93	struct arg *ap, tmp;
94	char *arg;
95
96	in.dbsz = out.dbsz = 512;
97
98	while (*++argv != NULL) {
99		if ((oper = strdup(*argv)) == NULL)
100			err(1, NULL);
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		free(oper);
117	}
118
119	/* Final sanity checks. */
120
121	if (ddflags & C_BS) {
122		/*
123		 * Bs is turned off by any conversion -- we assume the user
124		 * just wanted to set both the input and output block sizes
125		 * and didn't want the bs semantics, so we don't warn.
126		 */
127		if (ddflags & (C_BLOCK|C_LCASE|C_SWAB|C_UCASE|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		if (cbsz == 0)
157			errx(1, "cbs cannot be zero");
158	} else
159		cfunc = def;
160
161	if (in.dbsz == 0 || out.dbsz == 0)
162		errx(1, "buffer sizes cannot be zero");
163
164	/*
165	 * Read and write take size_t's as arguments.  Lseek, however,
166	 * takes an off_t.
167	 */
168	if (cbsz > SSIZE_MAX || in.dbsz > SSIZE_MAX || out.dbsz > SSIZE_MAX)
169		errx(1, "buffer sizes cannot be greater than %zd",
170		    (ssize_t)SSIZE_MAX);
171	if (in.offset > LLONG_MAX / in.dbsz || out.offset > LLONG_MAX / out.dbsz)
172		errx(1, "seek offsets cannot be larger than %lld", LLONG_MAX);
173}
174
175static int
176c_arg(const void *a, const void *b)
177{
178
179	return (strcmp(((struct arg *)a)->name, ((struct arg *)b)->name));
180}
181
182static void
183f_bs(char *arg)
184{
185
186	in.dbsz = out.dbsz = get_bsz(arg);
187}
188
189static void
190f_cbs(char *arg)
191{
192
193	cbsz = get_bsz(arg);
194}
195
196static void
197f_count(char *arg)
198{
199
200	if ((cpy_cnt = get_bsz(arg)) == 0)
201		cpy_cnt = (size_t)-1;
202}
203
204static void
205f_files(char *arg)
206{
207
208	files_cnt = get_bsz(arg);
209}
210
211static void
212f_ibs(char *arg)
213{
214
215	if (!(ddflags & C_BS))
216		in.dbsz = get_bsz(arg);
217}
218
219static void
220f_if(char *arg)
221{
222	if ((in.name = strdup(arg)) == NULL)
223		err(1, NULL);
224}
225
226static void
227f_obs(char *arg)
228{
229
230	if (!(ddflags & C_BS))
231		out.dbsz = get_bsz(arg);
232}
233
234static void
235f_of(char *arg)
236{
237	if ((out.name = strdup(arg)) == NULL)
238		err(1, NULL);
239}
240
241static void
242f_seek(char *arg)
243{
244
245	out.offset = get_off(arg);
246}
247
248static void
249f_skip(char *arg)
250{
251
252	in.offset = get_off(arg);
253}
254
255static void
256f_status(char *arg)
257{
258
259	if (strcmp(arg, "none") == 0)
260		ddflags |= C_NOINFO;
261	else if (strcmp(arg, "noxfer") == 0)
262		ddflags |= C_NOXFER;
263	else
264		errx(1, "unknown status %s", arg);
265}
266
267
268static const struct conv {
269	const char *name;
270	u_int set, noset;
271	const u_char *ctab;
272} clist[] = {
273#ifndef	NO_CONV
274	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
275	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
276	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
277	{ "fsync",	C_FSYNC,	0,		NULL },
278	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
279	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
280	{ "osync",	C_OSYNC,	C_BS,		NULL },
281	{ "swab",	C_SWAB,		0,		NULL },
282	{ "sync",	C_SYNC,		0,		NULL },
283	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
284	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
285#endif
286	{ "noerror",	C_NOERROR,	0,		NULL },
287	{ "notrunc",	C_NOTRUNC,	0,		NULL },
288	{ NULL,		0,		0,		NULL }
289};
290
291static void
292f_conv(char *arg)
293{
294	const struct conv *cp;
295	const char *name;
296
297	while (arg != NULL) {
298		name = strsep(&arg, ",");
299		for (cp = &clist[0]; cp->name; cp++)
300			if (strcmp(name, cp->name) == 0)
301				break;
302		if (!cp->name)
303			errx(1, "unknown conversion %s", name);
304		if (ddflags & cp->noset)
305			errx(1, "%s: illegal conversion combination", name);
306		ddflags |= cp->set;
307		if (cp->ctab)
308			ctab = cp->ctab;
309	}
310}
311
312/*
313 * Convert an expression of the following forms to a size_t
314 *	1) A positive decimal number, optionally followed by
315 *		b - multiply by 512.
316 *		k, m or g - multiply by 1024 each.
317 *		w - multiply by sizeof int
318 *	2) Two or more of the above, separated by x
319 *	   (or * for backwards compatibility), specifying
320 *	   the product of the indicated values.
321 */
322static size_t
323get_bsz(char *val)
324{
325	size_t num, t;
326	char *expr;
327
328	if (strchr(val, '-'))
329		errx(1, "%s: illegal numeric value", oper);
330
331	errno = 0;
332	num = strtoul(val, &expr, 0);
333	if (num == ULONG_MAX && errno == ERANGE)	/* Overflow. */
334		err(1, "%s", oper);
335	if (expr == val)			/* No digits. */
336		errx(1, "%s: illegal numeric value", oper);
337
338	switch(*expr) {
339	case 'b':
340		t = num;
341		num *= 512;
342		if (t > num)
343			goto erange;
344		++expr;
345		break;
346	case 'g':
347	case 'G':
348		t = num;
349		num *= 1024;
350		if (t > num)
351			goto erange;
352		/* fallthrough */
353	case 'm':
354	case 'M':
355		t = num;
356		num *= 1024;
357		if (t > num)
358			goto erange;
359		/* fallthrough */
360	case 'k':
361	case 'K':
362		t = num;
363		num *= 1024;
364		if (t > num)
365			goto erange;
366		++expr;
367		break;
368	case 'w':
369		t = num;
370		num *= sizeof(int);
371		if (t > num)
372			goto erange;
373		++expr;
374		break;
375	}
376
377	switch(*expr) {
378		case '\0':
379			break;
380		case '*':			/* Backward compatible. */
381		case 'x':
382			t = num;
383			num *= get_bsz(expr + 1);
384			if (t > num)
385				goto erange;
386			break;
387		default:
388			errx(1, "%s: illegal numeric value", oper);
389	}
390	return (num);
391erange:
392	errc(1, ERANGE, "%s", oper);
393}
394
395/*
396 * Convert an expression of the following forms to an off_t
397 *	1) A positive decimal number, optionally followed by
398 *		b - multiply by 512.
399 *		k, m or g - multiply by 1024 each.
400 *		w - multiply by sizeof int
401 *	2) Two or more of the above, separated by x
402 *	   (or * for backwards compatibility), specifying
403 *	   the product of the indicated values.
404 */
405static off_t
406get_off(char *val)
407{
408	off_t num, t;
409	char *expr;
410
411	errno = 0;
412	num = strtoll(val, &expr, 0);
413	if (num == LLONG_MAX && errno == ERANGE)	/* Overflow. */
414		err(1, "%s", oper);
415	if (expr == val)			/* No digits. */
416		errx(1, "%s: illegal numeric value", oper);
417
418	switch(*expr) {
419	case 'b':
420		t = num;
421		num *= 512;
422		if (t > num)
423			goto erange;
424		++expr;
425		break;
426	case 'g':
427	case 'G':
428		t = num;
429		num *= 1024;
430		if (t > num)
431			goto erange;
432		/* fallthrough */
433	case 'm':
434	case 'M':
435		t = num;
436		num *= 1024;
437		if (t > num)
438			goto erange;
439		/* fallthrough */
440	case 'k':
441	case 'K':
442		t = num;
443		num *= 1024;
444		if (t > num)
445			goto erange;
446		++expr;
447		break;
448	case 'w':
449		t = num;
450		num *= sizeof(int);
451		if (t > num)
452			goto erange;
453		++expr;
454		break;
455	}
456
457	switch(*expr) {
458		case '\0':
459			break;
460		case '*':			/* Backward compatible. */
461		case 'x':
462			t = num;
463			num *= get_off(expr + 1);
464			if (t > num)
465				goto erange;
466			break;
467		default:
468			errx(1, "%s: illegal numeric value", oper);
469	}
470	return (num);
471erange:
472	errc(1, ERANGE, "%s", oper);
473}
474