xref: /netbsd/bin/dd/args.c (revision c4a72b64)
1 /*	$NetBSD: args.c,v 1.21 2002/11/29 13:11:10 lukem 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.21 2002/11/29 13:11:10 lukem 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 
62 static int	c_arg(const void *, const void *);
63 #ifndef	NO_CONV
64 static int	c_conv(const void *, const void *);
65 #endif
66 static void	f_bs(char *);
67 static void	f_cbs(char *);
68 static void	f_conv(char *);
69 static void	f_count(char *);
70 static void	f_files(char *);
71 static void	f_ibs(char *);
72 static void	f_if(char *);
73 static void	f_obs(char *);
74 static void	f_of(char *);
75 static void	f_seek(char *);
76 static void	f_skip(char *);
77 static void	f_progress(char *);
78 
79 static const struct arg {
80 	const char *name;
81 	void (*f)(char *);
82 	u_int set, noset;
83 } args[] = {
84      /* the array needs to be sorted by the first column so
85 	bsearch() can be used to find commands quickly */
86 	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
87 	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
88 	{ "conv",	f_conv,		0,	 0 },
89 	{ "count",	f_count,	C_COUNT, C_COUNT },
90 	{ "files",	f_files,	C_FILES, C_FILES },
91 	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
92 	{ "if",		f_if,		C_IF,	 C_IF },
93 	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
94 	{ "of",		f_of,		C_OF,	 C_OF },
95 	{ "progress",	f_progress,	0,	 0 },
96 	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
97 	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
98 };
99 
100 /*
101  * args -- parse JCL syntax of dd.
102  */
103 void
104 jcl(char **argv)
105 {
106 	struct arg *ap, tmp;
107 	char *oper, *arg;
108 
109 	in.dbsz = out.dbsz = 512;
110 
111 	while ((oper = *++argv) != NULL) {
112 		if ((arg = strchr(oper, '=')) == NULL)
113 			errx(1, "unknown operand %s", oper);
114 		*arg++ = '\0';
115 		if (!*arg)
116 			errx(1, "no value specified for %s", oper);
117 		tmp.name = oper;
118 		if (!(ap = (struct arg *)bsearch(&tmp, args,
119 		    sizeof(args)/sizeof(struct arg), sizeof(struct arg),
120 		    c_arg)))
121 			errx(1, "unknown operand %s", tmp.name);
122 		if (ddflags & ap->noset)
123 			errx(1,
124 			    "%s: illegal argument combination or already set",
125 			    tmp.name);
126 		ddflags |= ap->set;
127 		ap->f(arg);
128 	}
129 
130 	/* Final sanity checks. */
131 
132 	if (ddflags & C_BS) {
133 		/*
134 		 * Bs is turned off by any conversion -- we assume the user
135 		 * just wanted to set both the input and output block sizes
136 		 * and didn't want the bs semantics, so we don't warn.
137 		 */
138 		if (ddflags & (C_BLOCK|C_LCASE|C_SWAB|C_UCASE|C_UNBLOCK))
139 			ddflags &= ~C_BS;
140 
141 		/* Bs supersedes ibs and obs. */
142 		if (ddflags & C_BS && ddflags & (C_IBS|C_OBS))
143 			warnx("bs supersedes ibs and obs");
144 	}
145 
146 	/*
147 	 * Ascii/ebcdic and cbs implies block/unblock.
148 	 * Block/unblock requires cbs and vice-versa.
149 	 */
150 	if (ddflags & (C_BLOCK|C_UNBLOCK)) {
151 		if (!(ddflags & C_CBS))
152 			errx(1, "record operations require cbs");
153 		cfunc = ddflags & C_BLOCK ? block : unblock;
154 	} else if (ddflags & C_CBS) {
155 		if (ddflags & (C_ASCII|C_EBCDIC)) {
156 			if (ddflags & C_ASCII) {
157 				ddflags |= C_UNBLOCK;
158 				cfunc = unblock;
159 			} else {
160 				ddflags |= C_BLOCK;
161 				cfunc = block;
162 			}
163 		} else
164 			errx(1,
165 			    "cbs meaningless if not doing record operations");
166 	} else
167 		cfunc = def;
168 
169 	/* Read, write and seek calls take off_t as arguments.
170 	 *
171 	 * The following check is not done because an off_t is a quad
172 	 *  for current NetBSD implementations.
173 	 *
174 	 * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz)
175 	 *	errx(1, "seek offsets cannot be larger than %d", INT_MAX);
176 	 */
177 }
178 
179 static int
180 c_arg(const void *a, const void *b)
181 {
182 
183 	return (strcmp(((const struct arg *)a)->name,
184 	    ((const struct arg *)b)->name));
185 }
186 
187 static void
188 f_bs(char *arg)
189 {
190 
191 	in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX);
192 }
193 
194 static void
195 f_cbs(char *arg)
196 {
197 
198 	cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX);
199 }
200 
201 static void
202 f_count(char *arg)
203 {
204 
205 	cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX);
206 	if (!cpy_cnt)
207 		terminate(0);
208 }
209 
210 static void
211 f_files(char *arg)
212 {
213 
214 	files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX);
215 	if (!files_cnt)
216 		terminate(0);
217 }
218 
219 static void
220 f_ibs(char *arg)
221 {
222 
223 	if (!(ddflags & C_BS))
224 		in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX);
225 }
226 
227 static void
228 f_if(char *arg)
229 {
230 
231 	in.name = arg;
232 }
233 
234 static void
235 f_obs(char *arg)
236 {
237 
238 	if (!(ddflags & C_BS))
239 		out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX);
240 }
241 
242 static void
243 f_of(char *arg)
244 {
245 
246 	out.name = arg;
247 }
248 
249 static void
250 f_seek(char *arg)
251 {
252 
253 	out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX);
254 }
255 
256 static void
257 f_skip(char *arg)
258 {
259 
260 	in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX);
261 }
262 
263 static void
264 f_progress(char *arg)
265 {
266 
267 	if (*arg != '0')
268 		progress = 1;
269 }
270 
271 #ifdef	NO_CONV
272 /* Build a small version (i.e. for a ramdisk root) */
273 static void
274 f_conv(char *arg)
275 {
276 
277 	errx(1, "conv option disabled");
278 }
279 #else	/* NO_CONV */
280 
281 static const struct conv {
282 	const char *name;
283 	u_int set, noset;
284 	const u_char *ctab;
285 } clist[] = {
286 	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
287 	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
288 	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
289 	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
290 	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
291 	{ "noerror",	C_NOERROR,	0,		NULL },
292 	{ "notrunc",	C_NOTRUNC,	0,		NULL },
293 	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
294 	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
295 	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
296 	{ "osync",	C_OSYNC,	C_BS,		NULL },
297 	{ "swab",	C_SWAB,		0,		NULL },
298 	{ "sync",	C_SYNC,		0,		NULL },
299 	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
300 	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
301 };
302 
303 static void
304 f_conv(char *arg)
305 {
306 	struct conv *cp, tmp;
307 
308 	while (arg != NULL) {
309 		tmp.name = strsep(&arg, ",");
310 		if (!(cp = (struct conv *)bsearch(&tmp, clist,
311 		    sizeof(clist)/sizeof(struct conv), sizeof(struct conv),
312 		    c_conv)))
313 			errx(1, "unknown conversion %s", tmp.name);
314 		if (ddflags & cp->noset)
315 			errx(1, "%s: illegal conversion combination", tmp.name);
316 		ddflags |= cp->set;
317 		if (cp->ctab)
318 			ctab = cp->ctab;
319 	}
320 }
321 
322 static int
323 c_conv(const void *a, const void *b)
324 {
325 
326 	return (strcmp(((const struct conv *)a)->name,
327 	    ((const struct conv *)b)->name));
328 }
329 
330 #endif	/* NO_CONV */
331