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. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38#include <sys/cdefs.h> 39#ifndef lint 40#if 0 41static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94"; 42#endif 43__used static const char rcsid[] = 44 "$FreeBSD: src/bin/dd/args.c,v 1.31 2002/02/22 20:51:00 markm Exp $"; 45#endif /* not lint */ 46 47#include <sys/types.h> 48 49#include <err.h> 50#include <errno.h> 51#include <limits.h> 52#include <stdlib.h> 53#include <string.h> 54 55#include "dd.h" 56#include "extern.h" 57 58static int c_arg(const void *, const void *); 59static int c_conv(const void *, const void *); 60static void f_bs(char *); 61static void f_cbs(char *); 62static void f_conv(char *); 63static void f_count(char *); 64static void f_files(char *); 65static void f_ibs(char *); 66static void f_if(char *); 67static void f_obs(char *); 68static void f_of(char *); 69static void f_seek(char *); 70static void f_skip(char *); 71static quad_t get_num(char *); 72static off_t get_offset(char *); 73 74static const struct arg { 75 const char *name; 76 void (*f)(char *); 77 u_int set, noset; 78} args[] = { 79 { "bs", f_bs, C_BS, C_BS|C_OSYNC }, 80 { "cbs", f_cbs, C_CBS, C_CBS }, 81 { "conv", f_conv, 0, 0 }, 82 { "count", f_count, C_COUNT, C_COUNT }, 83 { "files", f_files, C_FILES, C_FILES }, 84 { "ibs", f_ibs, C_IBS, C_IBS }, 85 { "if", f_if, C_IF, C_IF }, 86 { "iseek", f_skip, C_SKIP, C_SKIP }, 87 { "obs", f_obs, C_OBS, C_OBS }, 88 { "of", f_of, C_OF, C_OF }, 89 { "oseek", f_seek, C_SEEK, C_SEEK }, 90 { "seek", f_seek, C_SEEK, C_SEEK }, 91 { "skip", f_skip, C_SKIP, C_SKIP }, 92}; 93 94static char *oper; 95 96/* 97 * args -- parse JCL syntax of dd. 98 */ 99void 100jcl(char **argv) 101{ 102 struct arg *ap, tmp; 103 char *arg; 104 105 in.dbsz = out.dbsz = 512; 106 107 if (argv[1] && !strcmp(argv[1], "--")) /* skip delimiter before operands */ 108 argv++; 109 while ((oper = *++argv) != NULL) { 110 if ((oper = strdup(oper)) == NULL) 111 errx(1, "unable to allocate space for the argument \"%s\"", *argv); 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, "%s: illegal argument combination or already set", 124 tmp.name); 125 ddflags |= ap->set; 126 ap->f(arg); 127 } 128 129 /* Final sanity checks. */ 130 131 if (ddflags & C_BS) { 132 /* 133 * Bs is turned off by any conversion -- we assume the user 134 * just wanted to set both the input and output block sizes 135 * and didn't want the bs semantics, so we don't warn. 136 */ 137 if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE | 138 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 if (cbsz == 0) 154 errx(1, "cbs cannot be zero"); 155 cfunc = ddflags & C_BLOCK ? block : unblock; 156 } else if (ddflags & C_CBS) { 157 if (ddflags & (C_ASCII | C_EBCDIC)) { 158 if (ddflags & C_ASCII) { 159 ddflags |= C_UNBLOCK; 160 cfunc = unblock; 161 } else { 162 ddflags |= C_BLOCK; 163 cfunc = block; 164 } 165 } else 166 errx(1, "cbs meaningless if not doing record operations"); 167 } else 168 cfunc = def; 169 170 /* 171 * Bail out if the calculation of a file offset would overflow. 172 */ 173 if (in.offset > QUAD_MAX / in.dbsz || out.offset > QUAD_MAX / out.dbsz) 174 errx(1, "seek offsets cannot be larger than %qd", QUAD_MAX); 175} 176 177static int 178c_arg(const void *a, const void *b) 179{ 180 181 return (strcmp(((const struct arg *)a)->name, 182 ((const struct arg *)b)->name)); 183} 184 185static void 186f_bs(char *arg) 187{ 188 quad_t res; 189 190 res = get_num(arg); 191 if (res < 1 || res > SSIZE_MAX) 192 errx(1, "bs must be between 1 and %ld", SSIZE_MAX); 193 in.dbsz = out.dbsz = (size_t)res; 194} 195 196static void 197f_cbs(char *arg) 198{ 199 quad_t res; 200 201 res = get_num(arg); 202 if (res < 1 || res > SSIZE_MAX) 203 errx(1, "cbs must be between 1 and %ld", SSIZE_MAX); 204 cbsz = (size_t)res; 205} 206 207static void 208f_count(char *arg) 209{ 210 211 cpy_cnt = get_num(arg); 212 if (cpy_cnt < 0) 213 errx(1, "count cannot be negative"); 214 if (cpy_cnt == 0) 215 cpy_cnt = -1; 216} 217 218static void 219f_files(char *arg) 220{ 221 222 files_cnt = get_num(arg); 223 if (files_cnt < 1) 224 errx(1, "files must be between 1 and %qd", QUAD_MAX); 225} 226 227static void 228f_ibs(char *arg) 229{ 230 quad_t res; 231 232 if (!(ddflags & C_BS)) { 233 res = get_num(arg); 234 if (res < 1 || res > SSIZE_MAX) 235 errx(1, "ibs must be between 1 and %ld", SSIZE_MAX); 236 in.dbsz = (size_t)res; 237 } 238} 239 240static void 241f_if(char *arg) 242{ 243 244 in.name = arg; 245} 246 247static void 248f_obs(char *arg) 249{ 250 quad_t res; 251 252 if (!(ddflags & C_BS)) { 253 res = get_num(arg); 254 if (res < 1 || res > SSIZE_MAX) 255 errx(1, "obs must be between 1 and %ld", SSIZE_MAX); 256 out.dbsz = (size_t)res; 257 } 258} 259 260static void 261f_of(char *arg) 262{ 263 264 out.name = arg; 265} 266 267static void 268f_seek(char *arg) 269{ 270 271 out.offset = get_offset(arg); 272} 273 274static void 275f_skip(char *arg) 276{ 277 278 in.offset = get_offset(arg); 279} 280 281static 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 { "sparse", C_SPARSE, 0, 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 304static void 305f_conv(char *arg) 306{ 307 struct conv *cp, tmp; 308 309 while (arg != NULL) { 310 tmp.name = strsep(&arg, ","); 311 cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv), 312 sizeof(struct conv), c_conv); 313 if (cp == NULL) 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 323static int 324c_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/* 332 * Convert an expression of the following forms to a quad_t. 333 * 1) A positive decimal number. 334 * 2) A positive decimal number followed by a b (mult by 512). 335 * 3) A positive decimal number followed by a k (mult by 1 << 10). 336 * 4) A positive decimal number followed by a m (mult by 1 << 20). 337 * 5) A positive decimal number followed by a g (mult by 1 << 30). 338 * 5) A positive decimal number followed by a w (mult by sizeof int). 339 * 6) Two or more positive decimal numbers (with/without [bkmgw]) 340 * separated by x (also * for backwards compatibility), specifying 341 * the product of the indicated values. 342 */ 343static quad_t 344get_num(char *val) 345{ 346 quad_t num, t; 347 char *expr; 348 349 errno = 0; 350 num = strtoq(val, &expr, 0); 351 if (errno != 0) /* Overflow or underflow. */ 352 err(1, "%s", oper); 353 354 if (expr == val) /* No valid digits. */ 355 errx(1, "%s: illegal numeric value", oper); 356 357 switch (*expr) { 358 case 'b': 359 t = num; 360 num *= 512; 361 if (t > num) 362 goto erange; 363 ++expr; 364 break; 365 case 'k': 366 t = num; 367 num *= 1 << 10; 368 if (t > num) 369 goto erange; 370 ++expr; 371 break; 372 case 'm': 373 t = num; 374 num *= 1 << 20; 375 if (t > num) 376 goto erange; 377 ++expr; 378 break; 379 case 'g': 380 t = num; 381 num *= 1 << 30; 382 if (t > num) 383 goto erange; 384 ++expr; 385 break; 386 case 'w': 387 t = num; 388 num *= sizeof(int); 389 if (t > num) 390 goto erange; 391 ++expr; 392 break; 393 } 394 395 switch (*expr) { 396 case '\0': 397 break; 398 case '*': /* Backward compatible. */ 399 case 'x': 400 t = num; 401 num *= get_num(expr + 1); 402 if (t <= num) 403 break; 404erange: 405 errx(1, "%s: %s", oper, strerror(ERANGE)); 406 default: 407 errx(1, "%s: illegal numeric value", oper); 408 } 409 return (num); 410} 411 412static off_t 413get_offset(char *val) 414{ 415 quad_t num; 416 417 num = get_num(val); 418 if (num > QUAD_MAX) /* XXX can't happen && quad_t != off_t */ 419 errx(1, "%s: illegal offset", oper); /* Too big. */ 420 return ((off_t)num); 421} 422