xref: /netbsd/bin/dd/args.c (revision bf9ec67e)
1 /*	$NetBSD: args.c,v 1.20 2001/11/26 00:56:33 enami Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Keith Muller of the University of California, San Diego and Lance
9  * Visser of Convex Computer Corporation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #include <sys/cdefs.h>
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)args.c	8.3 (Berkeley) 4/2/94";
44 #else
45 __RCSID("$NetBSD: args.c,v 1.20 2001/11/26 00:56:33 enami Exp $");
46 #endif
47 #endif /* not lint */
48 
49 #include <sys/types.h>
50 #include <sys/time.h>
51 
52 #include <err.h>
53 #include <errno.h>
54 #include <limits.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 
59 #include "dd.h"
60 #include "extern.h"
61 #include "strsuftoull.h"
62 
63 static int	c_arg(const void *, const void *);
64 #ifndef	NO_CONV
65 static int	c_conv(const void *, const void *);
66 #endif
67 static void	f_bs(char *);
68 static void	f_cbs(char *);
69 static void	f_conv(char *);
70 static void	f_count(char *);
71 static void	f_files(char *);
72 static void	f_ibs(char *);
73 static void	f_if(char *);
74 static void	f_obs(char *);
75 static void	f_of(char *);
76 static void	f_seek(char *);
77 static void	f_skip(char *);
78 static void	f_progress(char *);
79 
80 static const struct arg {
81 	const char *name;
82 	void (*f)(char *);
83 	u_int set, noset;
84 } args[] = {
85      /* the array needs to be sorted by the first column so
86 	bsearch() can be used to find commands quickly */
87 	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
88 	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
89 	{ "conv",	f_conv,		0,	 0 },
90 	{ "count",	f_count,	C_COUNT, C_COUNT },
91 	{ "files",	f_files,	C_FILES, C_FILES },
92 	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
93 	{ "if",		f_if,		C_IF,	 C_IF },
94 	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
95 	{ "of",		f_of,		C_OF,	 C_OF },
96 	{ "progress",	f_progress,	0,	 0 },
97 	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
98 	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
99 };
100 
101 /*
102  * args -- parse JCL syntax of dd.
103  */
104 void
105 jcl(char **argv)
106 {
107 	struct arg *ap, tmp;
108 	char *oper, *arg;
109 
110 	in.dbsz = out.dbsz = 512;
111 
112 	while ((oper = *++argv) != NULL) {
113 		if ((arg = strchr(oper, '=')) == NULL)
114 			errx(1, "unknown operand %s", oper);
115 		*arg++ = '\0';
116 		if (!*arg)
117 			errx(1, "no value specified for %s", oper);
118 		tmp.name = oper;
119 		if (!(ap = (struct arg *)bsearch(&tmp, args,
120 		    sizeof(args)/sizeof(struct arg), sizeof(struct arg),
121 		    c_arg)))
122 			errx(1, "unknown operand %s", tmp.name);
123 		if (ddflags & ap->noset)
124 			errx(1,
125 			    "%s: illegal argument combination or already set",
126 			    tmp.name);
127 		ddflags |= ap->set;
128 		ap->f(arg);
129 	}
130 
131 	/* Final sanity checks. */
132 
133 	if (ddflags & C_BS) {
134 		/*
135 		 * Bs is turned off by any conversion -- we assume the user
136 		 * just wanted to set both the input and output block sizes
137 		 * and didn't want the bs semantics, so we don't warn.
138 		 */
139 		if (ddflags & (C_BLOCK|C_LCASE|C_SWAB|C_UCASE|C_UNBLOCK))
140 			ddflags &= ~C_BS;
141 
142 		/* Bs supersedes ibs and obs. */
143 		if (ddflags & C_BS && ddflags & (C_IBS|C_OBS))
144 			warnx("bs supersedes ibs and obs");
145 	}
146 
147 	/*
148 	 * Ascii/ebcdic and cbs implies block/unblock.
149 	 * Block/unblock requires cbs and vice-versa.
150 	 */
151 	if (ddflags & (C_BLOCK|C_UNBLOCK)) {
152 		if (!(ddflags & C_CBS))
153 			errx(1, "record operations require cbs");
154 		cfunc = ddflags & C_BLOCK ? block : unblock;
155 	} else if (ddflags & C_CBS) {
156 		if (ddflags & (C_ASCII|C_EBCDIC)) {
157 			if (ddflags & C_ASCII) {
158 				ddflags |= C_UNBLOCK;
159 				cfunc = unblock;
160 			} else {
161 				ddflags |= C_BLOCK;
162 				cfunc = block;
163 			}
164 		} else
165 			errx(1,
166 			    "cbs meaningless if not doing record operations");
167 	} else
168 		cfunc = def;
169 
170 	/* Read, write and seek calls take off_t as arguments.
171 	 *
172 	 * The following check is not done because an off_t is a quad
173 	 *  for current NetBSD implementations.
174 	 *
175 	 * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz)
176 	 *	errx(1, "seek offsets cannot be larger than %d", INT_MAX);
177 	 */
178 }
179 
180 static int
181 c_arg(const void *a, const void *b)
182 {
183 
184 	return (strcmp(((const struct arg *)a)->name,
185 	    ((const struct arg *)b)->name));
186 }
187 
188 static void
189 f_bs(char *arg)
190 {
191 
192 	in.dbsz = out.dbsz = strsuftoull("block size", arg, 1, UINT_MAX);
193 }
194 
195 static void
196 f_cbs(char *arg)
197 {
198 
199 	cbsz = strsuftoull("conversion record size", arg, 1, UINT_MAX);
200 }
201 
202 static void
203 f_count(char *arg)
204 {
205 
206 	cpy_cnt = strsuftoull("block count", arg, 0, ULLONG_MAX);
207 	if (!cpy_cnt)
208 		terminate(0);
209 }
210 
211 static void
212 f_files(char *arg)
213 {
214 
215 	files_cnt = (u_int)strsuftoull("file count", arg, 0, UINT_MAX);
216 	if (!files_cnt)
217 		terminate(0);
218 }
219 
220 static void
221 f_ibs(char *arg)
222 {
223 
224 	if (!(ddflags & C_BS))
225 		in.dbsz = strsuftoull("input block size", arg, 1, UINT_MAX);
226 }
227 
228 static void
229 f_if(char *arg)
230 {
231 
232 	in.name = arg;
233 }
234 
235 static void
236 f_obs(char *arg)
237 {
238 
239 	if (!(ddflags & C_BS))
240 		out.dbsz = strsuftoull("output block size", arg, 1, UINT_MAX);
241 }
242 
243 static void
244 f_of(char *arg)
245 {
246 
247 	out.name = arg;
248 }
249 
250 static void
251 f_seek(char *arg)
252 {
253 
254 	out.offset = strsuftoull("seek blocks", arg, 0, ULLONG_MAX);
255 }
256 
257 static void
258 f_skip(char *arg)
259 {
260 
261 	in.offset = strsuftoull("skip blocks", arg, 0, ULLONG_MAX);
262 }
263 
264 static void
265 f_progress(char *arg)
266 {
267 
268 	if (*arg != '0')
269 		progress = 1;
270 }
271 
272 #ifdef	NO_CONV
273 /* Build a small version (i.e. for a ramdisk root) */
274 static void
275 f_conv(char *arg)
276 {
277 
278 	errx(1, "conv option disabled");
279 }
280 #else	/* NO_CONV */
281 
282 static const struct conv {
283 	const char *name;
284 	u_int set, noset;
285 	const u_char *ctab;
286 } clist[] = {
287 	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
288 	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
289 	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
290 	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
291 	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
292 	{ "noerror",	C_NOERROR,	0,		NULL },
293 	{ "notrunc",	C_NOTRUNC,	0,		NULL },
294 	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
295 	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
296 	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
297 	{ "osync",	C_OSYNC,	C_BS,		NULL },
298 	{ "swab",	C_SWAB,		0,		NULL },
299 	{ "sync",	C_SYNC,		0,		NULL },
300 	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
301 	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
302 };
303 
304 static void
305 f_conv(char *arg)
306 {
307 	struct conv *cp, tmp;
308 
309 	while (arg != NULL) {
310 		tmp.name = strsep(&arg, ",");
311 		if (!(cp = (struct conv *)bsearch(&tmp, clist,
312 		    sizeof(clist)/sizeof(struct conv), sizeof(struct conv),
313 		    c_conv)))
314 			errx(1, "unknown conversion %s", tmp.name);
315 		if (ddflags & cp->noset)
316 			errx(1, "%s: illegal conversion combination", tmp.name);
317 		ddflags |= cp->set;
318 		if (cp->ctab)
319 			ctab = cp->ctab;
320 	}
321 }
322 
323 static int
324 c_conv(const void *a, const void *b)
325 {
326 
327 	return (strcmp(((const struct conv *)a)->name,
328 	    ((const struct conv *)b)->name));
329 }
330 
331 #endif	/* NO_CONV */
332