1 /* $NetBSD: args.c,v 1.33 2011/01/13 23:45:13 jym 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. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94"; 40 #else 41 __RCSID("$NetBSD: args.c,v 1.33 2011/01/13 23:45:13 jym Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/types.h> 46 #include <sys/time.h> 47 48 #include <err.h> 49 #include <errno.h> 50 #include <limits.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 55 #include "dd.h" 56 #include "extern.h" 57 58 #if !defined(SMALL) && defined(__NetBSD__) 59 #define _HAVE_RUMPOPS 60 61 #include <rump/rumpclient.h> 62 #endif 63 64 static int c_arg(const void *, const void *); 65 #ifndef NO_CONV 66 static int c_conv(const void *, const void *); 67 #endif 68 static void f_bs(char *); 69 static void f_cbs(char *); 70 static void f_conv(char *); 71 static void f_count(char *); 72 static void f_files(char *); 73 static void f_ibs(char *); 74 static void f_if(char *); 75 static void f_obs(char *); 76 static void f_of(char *); 77 static void f_seek(char *); 78 static void f_skip(char *); 79 static void f_progress(char *); 80 81 #ifdef _HAVE_RUMPOPS 82 static void f_rif(char *); 83 static void f_rof(char *); 84 #endif 85 86 static const struct arg { 87 const char *name; 88 void (*f)(char *); 89 u_int set, noset; 90 } args[] = { 91 /* the array needs to be sorted by the first column so 92 bsearch() can be used to find commands quickly */ 93 { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC }, 94 { "cbs", f_cbs, C_CBS, C_CBS }, 95 { "conv", f_conv, 0, 0 }, 96 { "count", f_count, C_COUNT, C_COUNT }, 97 { "files", f_files, C_FILES, C_FILES }, 98 { "ibs", f_ibs, C_IBS, C_BS|C_IBS }, 99 { "if", f_if, C_IF, C_IF|C_RIF }, 100 { "iseek", f_skip, C_SKIP, C_SKIP }, 101 { "obs", f_obs, C_OBS, C_BS|C_OBS }, 102 { "of", f_of, C_OF, C_OF|C_ROF }, 103 { "oseek", f_seek, C_SEEK, C_SEEK }, 104 { "progress", f_progress, 0, 0 }, 105 #ifdef _HAVE_RUMPOPS 106 { "rif", f_rif, C_RIF|C_RUMP, C_RIF|C_IF }, 107 { "rof", f_rof, C_ROF|C_RUMP, C_ROF|C_OF }, 108 #endif 109 { "seek", f_seek, C_SEEK, C_SEEK }, 110 { "skip", f_skip, C_SKIP, C_SKIP }, 111 }; 112 113 /* 114 * args -- parse JCL syntax of dd. 115 */ 116 void 117 jcl(char **argv) 118 { 119 struct arg *ap, tmp; 120 char *oper, *arg; 121 122 in.dbsz = out.dbsz = 512; 123 124 while ((oper = *++argv) != NULL) { 125 if ((oper = strdup(oper)) == NULL) { 126 errx(EXIT_FAILURE, 127 "unable to allocate space for the argument %s", 128 *argv); 129 /* NOTREACHED */ 130 } 131 if ((arg = strchr(oper, '=')) == NULL) { 132 errx(EXIT_FAILURE, "unknown operand %s", oper); 133 /* NOTREACHED */ 134 } 135 *arg++ = '\0'; 136 if (!*arg) { 137 errx(EXIT_FAILURE, "no value specified for %s", oper); 138 /* NOTREACHED */ 139 } 140 tmp.name = oper; 141 if (!(ap = bsearch(&tmp, args, 142 __arraycount(args), sizeof(*args), c_arg))) { 143 errx(EXIT_FAILURE, "unknown operand %s", tmp.name); 144 /* NOTREACHED */ 145 } 146 if (ddflags & ap->noset) { 147 errx(EXIT_FAILURE, 148 "%s: illegal argument combination or already set", 149 tmp.name); 150 /* NOTREACHED */ 151 } 152 ddflags |= ap->set; 153 ap->f(arg); 154 } 155 156 /* Final sanity checks. */ 157 158 if (ddflags & C_BS) { 159 /* 160 * Bs is turned off by any conversion -- we assume the user 161 * just wanted to set both the input and output block sizes 162 * and didn't want the bs semantics, so we don't warn. 163 */ 164 if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE | 165 C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) { 166 ddflags &= ~C_BS; 167 ddflags |= C_IBS|C_OBS; 168 } 169 170 /* Bs supersedes ibs and obs. */ 171 if (ddflags & C_BS && ddflags & (C_IBS|C_OBS)) 172 warnx("bs supersedes ibs and obs"); 173 } 174 175 /* 176 * Ascii/ebcdic and cbs implies block/unblock. 177 * Block/unblock requires cbs and vice-versa. 178 */ 179 if (ddflags & (C_BLOCK|C_UNBLOCK)) { 180 if (!(ddflags & C_CBS)) { 181 errx(EXIT_FAILURE, "record operations require cbs"); 182 /* NOTREACHED */ 183 } 184 cfunc = ddflags & C_BLOCK ? block : unblock; 185 } else if (ddflags & C_CBS) { 186 if (ddflags & (C_ASCII|C_EBCDIC)) { 187 if (ddflags & C_ASCII) { 188 ddflags |= C_UNBLOCK; 189 cfunc = unblock; 190 } else { 191 ddflags |= C_BLOCK; 192 cfunc = block; 193 } 194 } else { 195 errx(EXIT_FAILURE, 196 "cbs meaningless if not doing record operations"); 197 /* NOTREACHED */ 198 } 199 } else 200 cfunc = def; 201 202 /* Read, write and seek calls take off_t as arguments. 203 * 204 * The following check is not done because an off_t is a quad 205 * for current NetBSD implementations. 206 * 207 * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz) 208 * errx(1, "seek offsets cannot be larger than %d", INT_MAX); 209 */ 210 211 #ifdef _HAVE_RUMPOPS 212 if (ddflags & C_RUMP) 213 if (rumpclient_init() == -1) 214 err(1, "rumpclient init failed"); 215 #endif 216 } 217 218 static int 219 c_arg(const void *a, const void *b) 220 { 221 222 return (strcmp(((const struct arg *)a)->name, 223 ((const struct arg *)b)->name)); 224 } 225 226 static void 227 f_bs(char *arg) 228 { 229 230 in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX); 231 } 232 233 static void 234 f_cbs(char *arg) 235 { 236 237 cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX); 238 } 239 240 static void 241 f_count(char *arg) 242 { 243 244 cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX); 245 if (!cpy_cnt) 246 terminate(0); 247 } 248 249 static void 250 f_files(char *arg) 251 { 252 253 files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX); 254 if (!files_cnt) 255 terminate(0); 256 } 257 258 static void 259 f_ibs(char *arg) 260 { 261 262 if (!(ddflags & C_BS)) 263 in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX); 264 } 265 266 static void 267 f_if(char *arg) 268 { 269 270 in.name = arg; 271 } 272 273 static void 274 f_obs(char *arg) 275 { 276 277 if (!(ddflags & C_BS)) 278 out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX); 279 } 280 281 static void 282 f_of(char *arg) 283 { 284 285 out.name = arg; 286 } 287 288 #ifdef _HAVE_RUMPOPS 289 #include <rump/rump.h> 290 #include <rump/rump_syscalls.h> 291 292 static const struct ddfops ddfops_rump = { 293 .op_open = rump_sys_open, 294 .op_close = rump_sys_close, 295 .op_fcntl = rump_sys_fcntl, 296 .op_ioctl = rump_sys_ioctl, 297 .op_fstat = rump_sys_fstat, 298 .op_fsync = rump_sys_fsync, 299 .op_ftruncate = rump_sys_ftruncate, 300 .op_lseek = rump_sys_lseek, 301 .op_read = rump_sys_read, 302 .op_write = rump_sys_write, 303 }; 304 305 static void 306 f_rif(char *arg) 307 { 308 309 in.name = arg; 310 in.ops = &ddfops_rump; 311 } 312 313 static void 314 f_rof(char *arg) 315 { 316 317 out.name = arg; 318 out.ops = &ddfops_rump; 319 } 320 #endif 321 322 static void 323 f_seek(char *arg) 324 { 325 326 out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX); 327 } 328 329 static void 330 f_skip(char *arg) 331 { 332 333 in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX); 334 } 335 336 static void 337 f_progress(char *arg) 338 { 339 340 progress = strsuftoll("progress blocks", arg, 0, LLONG_MAX); 341 } 342 343 #ifdef NO_CONV 344 /* Build a small version (i.e. for a ramdisk root) */ 345 static void 346 f_conv(char *arg) 347 { 348 349 errx(EXIT_FAILURE, "conv option disabled"); 350 /* NOTREACHED */ 351 } 352 #else /* NO_CONV */ 353 354 static const struct conv { 355 const char *name; 356 u_int set, noset; 357 const u_char *ctab; 358 } clist[] = { 359 { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, 360 { "block", C_BLOCK, C_UNBLOCK, NULL }, 361 { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, 362 { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, 363 { "lcase", C_LCASE, C_UCASE, NULL }, 364 { "noerror", C_NOERROR, 0, NULL }, 365 { "notrunc", C_NOTRUNC, 0, NULL }, 366 { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, 367 { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, 368 { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, 369 { "osync", C_OSYNC, C_BS, NULL }, 370 { "sparse", C_SPARSE, 0, NULL }, 371 { "swab", C_SWAB, 0, NULL }, 372 { "sync", C_SYNC, 0, NULL }, 373 { "ucase", C_UCASE, C_LCASE, NULL }, 374 { "unblock", C_UNBLOCK, C_BLOCK, NULL }, 375 /* If you add items to this table, be sure to add the 376 * conversions to the C_BS check in the jcl routine above. 377 */ 378 }; 379 380 static void 381 f_conv(char *arg) 382 { 383 struct conv *cp, tmp; 384 385 while (arg != NULL) { 386 tmp.name = strsep(&arg, ","); 387 if (!(cp = bsearch(&tmp, clist, 388 __arraycount(clist), sizeof(*clist), c_conv))) { 389 errx(EXIT_FAILURE, "unknown conversion %s", tmp.name); 390 /* NOTREACHED */ 391 } 392 if (ddflags & cp->noset) { 393 errx(EXIT_FAILURE, 394 "%s: illegal conversion combination", tmp.name); 395 /* NOTREACHED */ 396 } 397 ddflags |= cp->set; 398 if (cp->ctab) 399 ctab = cp->ctab; 400 } 401 } 402 403 static int 404 c_conv(const void *a, const void *b) 405 { 406 407 return (strcmp(((const struct conv *)a)->name, 408 ((const struct conv *)b)->name)); 409 } 410 411 #endif /* NO_CONV */ 412