args.c revision 250469
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Keith Muller of the University of California, San Diego and Lance
71556Srgrimes * Visser of Convex Computer Corporation.
81556Srgrimes *
91556Srgrimes * Redistribution and use in source and binary forms, with or without
101556Srgrimes * modification, are permitted provided that the following conditions
111556Srgrimes * are met:
121556Srgrimes * 1. Redistributions of source code must retain the above copyright
131556Srgrimes *    notice, this list of conditions and the following disclaimer.
141556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151556Srgrimes *    notice, this list of conditions and the following disclaimer in the
161556Srgrimes *    documentation and/or other materials provided with the distribution.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
321556Srgrimes */
331556Srgrimes
341556Srgrimes#ifndef lint
3535773Scharnier#if 0
3636007Scharnierstatic char sccsid[] = "@(#)args.c	8.3 (Berkeley) 4/2/94";
3735773Scharnier#endif
381556Srgrimes#endif /* not lint */
3999109Sobrien#include <sys/cdefs.h>
4099109Sobrien__FBSDID("$FreeBSD: head/bin/dd/args.c 250469 2013-05-10 18:43:36Z eadler $");
411556Srgrimes
421556Srgrimes#include <sys/types.h>
431556Srgrimes
441556Srgrimes#include <err.h>
451556Srgrimes#include <errno.h>
46111629Smarkm#include <inttypes.h>
471556Srgrimes#include <limits.h>
48250469Seadler#include <signal.h>
491556Srgrimes#include <stdlib.h>
501556Srgrimes#include <string.h>
511556Srgrimes
521556Srgrimes#include "dd.h"
531556Srgrimes#include "extern.h"
541556Srgrimes
5590108Simpstatic int	c_arg(const void *, const void *);
5690108Simpstatic int	c_conv(const void *, const void *);
5790108Simpstatic void	f_bs(char *);
5890108Simpstatic void	f_cbs(char *);
5990108Simpstatic void	f_conv(char *);
6090108Simpstatic void	f_count(char *);
6190108Simpstatic void	f_files(char *);
62133762Srwatsonstatic void	f_fillchar(char *);
6390108Simpstatic void	f_ibs(char *);
6490108Simpstatic void	f_if(char *);
6590108Simpstatic void	f_obs(char *);
6690108Simpstatic void	f_of(char *);
6790108Simpstatic void	f_seek(char *);
6890108Simpstatic void	f_skip(char *);
69111629Smarkmstatic uintmax_t get_num(const char *);
7090108Simpstatic off_t	get_off_t(const char *);
711556Srgrimes
7251208Sgreenstatic const struct arg {
7354245Sgreen	const char *name;
7490108Simp	void (*f)(char *);
751556Srgrimes	u_int set, noset;
761556Srgrimes} args[] = {
771556Srgrimes	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
781556Srgrimes	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
791556Srgrimes	{ "conv",	f_conv,		0,	 0 },
801556Srgrimes	{ "count",	f_count,	C_COUNT, C_COUNT },
811556Srgrimes	{ "files",	f_files,	C_FILES, C_FILES },
82133762Srwatson	{ "fillchar",	f_fillchar,	C_FILL,	 C_FILL },
831556Srgrimes	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
841556Srgrimes	{ "if",		f_if,		C_IF,	 C_IF },
8557523Sgreen	{ "iseek",	f_skip,		C_SKIP,	 C_SKIP },
861556Srgrimes	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
871556Srgrimes	{ "of",		f_of,		C_OF,	 C_OF },
8857523Sgreen	{ "oseek",	f_seek,		C_SEEK,	 C_SEEK },
891556Srgrimes	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
901556Srgrimes	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
911556Srgrimes};
921556Srgrimes
931556Srgrimesstatic char *oper;
941556Srgrimes
951556Srgrimes/*
961556Srgrimes * args -- parse JCL syntax of dd.
971556Srgrimes */
981556Srgrimesvoid
9990108Simpjcl(char **argv)
1001556Srgrimes{
1011556Srgrimes	struct arg *ap, tmp;
1021556Srgrimes	char *arg;
1031556Srgrimes
1041556Srgrimes	in.dbsz = out.dbsz = 512;
1051556Srgrimes
10619720Sphk	while ((oper = *++argv) != NULL) {
10730230Seivind		if ((oper = strdup(oper)) == NULL)
10848051Sgreen			errx(1, "unable to allocate space for the argument \"%s\"", *argv);
1091556Srgrimes		if ((arg = strchr(oper, '=')) == NULL)
1101556Srgrimes			errx(1, "unknown operand %s", oper);
1111556Srgrimes		*arg++ = '\0';
1121556Srgrimes		if (!*arg)
1131556Srgrimes			errx(1, "no value specified for %s", oper);
1141556Srgrimes		tmp.name = oper;
11548051Sgreen		if (!(ap = (struct arg *)bsearch(&tmp, args,
11648051Sgreen		    sizeof(args)/sizeof(struct arg), sizeof(struct arg),
11748051Sgreen		    c_arg)))
1181556Srgrimes			errx(1, "unknown operand %s", tmp.name);
1191556Srgrimes		if (ddflags & ap->noset)
12048051Sgreen			errx(1, "%s: illegal argument combination or already set",
12148051Sgreen			    tmp.name);
1221556Srgrimes		ddflags |= ap->set;
1231556Srgrimes		ap->f(arg);
1241556Srgrimes	}
1251556Srgrimes
1261556Srgrimes	/* Final sanity checks. */
1271556Srgrimes
1281556Srgrimes	if (ddflags & C_BS) {
1291556Srgrimes		/*
1301556Srgrimes		 * Bs is turned off by any conversion -- we assume the user
1311556Srgrimes		 * just wanted to set both the input and output block sizes
1321556Srgrimes		 * and didn't want the bs semantics, so we don't warn.
1331556Srgrimes		 */
13462311Sgreen		if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
13562311Sgreen		    C_UNBLOCK))
1361556Srgrimes			ddflags &= ~C_BS;
1371556Srgrimes
1381556Srgrimes		/* Bs supersedes ibs and obs. */
13962311Sgreen		if (ddflags & C_BS && ddflags & (C_IBS | C_OBS))
1401556Srgrimes			warnx("bs supersedes ibs and obs");
1411556Srgrimes	}
1421556Srgrimes
1431556Srgrimes	/*
1441556Srgrimes	 * Ascii/ebcdic and cbs implies block/unblock.
1451556Srgrimes	 * Block/unblock requires cbs and vice-versa.
1461556Srgrimes	 */
14751249Sgreen	if (ddflags & (C_BLOCK | C_UNBLOCK)) {
1481556Srgrimes		if (!(ddflags & C_CBS))
1491556Srgrimes			errx(1, "record operations require cbs");
1501556Srgrimes		if (cbsz == 0)
1511556Srgrimes			errx(1, "cbs cannot be zero");
1521556Srgrimes		cfunc = ddflags & C_BLOCK ? block : unblock;
1531556Srgrimes	} else if (ddflags & C_CBS) {
15451249Sgreen		if (ddflags & (C_ASCII | C_EBCDIC)) {
1551556Srgrimes			if (ddflags & C_ASCII) {
1561556Srgrimes				ddflags |= C_UNBLOCK;
1571556Srgrimes				cfunc = unblock;
1581556Srgrimes			} else {
1591556Srgrimes				ddflags |= C_BLOCK;
1601556Srgrimes				cfunc = block;
1611556Srgrimes			}
1621556Srgrimes		} else
1631556Srgrimes			errx(1, "cbs meaningless if not doing record operations");
1641556Srgrimes	} else
1651556Srgrimes		cfunc = def;
16651249Sgreen
16751208Sgreen	/*
16851208Sgreen	 * Bail out if the calculation of a file offset would overflow.
16951208Sgreen	 */
170111629Smarkm	if (in.offset > OFF_MAX / (ssize_t)in.dbsz ||
171111629Smarkm	    out.offset > OFF_MAX / (ssize_t)out.dbsz)
172111629Smarkm		errx(1, "seek offsets cannot be larger than %jd",
173111629Smarkm		    (intmax_t)OFF_MAX);
1741556Srgrimes}
1751556Srgrimes
1761556Srgrimesstatic int
17790108Simpc_arg(const void *a, const void *b)
1781556Srgrimes{
1791556Srgrimes
18054278Sgreen	return (strcmp(((const struct arg *)a)->name,
18154278Sgreen	    ((const struct arg *)b)->name));
1821556Srgrimes}
1831556Srgrimes
1841556Srgrimesstatic void
18590108Simpf_bs(char *arg)
1861556Srgrimes{
187111629Smarkm	uintmax_t res;
1881556Srgrimes
18951208Sgreen	res = get_num(arg);
19051208Sgreen	if (res < 1 || res > SSIZE_MAX)
191112265Sru		errx(1, "bs must be between 1 and %jd", (intmax_t)SSIZE_MAX);
19251208Sgreen	in.dbsz = out.dbsz = (size_t)res;
1931556Srgrimes}
1941556Srgrimes
1951556Srgrimesstatic void
19690108Simpf_cbs(char *arg)
1971556Srgrimes{
198111629Smarkm	uintmax_t res;
1991556Srgrimes
20051208Sgreen	res = get_num(arg);
20151208Sgreen	if (res < 1 || res > SSIZE_MAX)
202112265Sru		errx(1, "cbs must be between 1 and %jd", (intmax_t)SSIZE_MAX);
20351208Sgreen	cbsz = (size_t)res;
2041556Srgrimes}
2051556Srgrimes
2061556Srgrimesstatic void
20790108Simpf_count(char *arg)
2081556Srgrimes{
209111629Smarkm	intmax_t res;
2101556Srgrimes
211111629Smarkm	res = (intmax_t)get_num(arg);
212111629Smarkm	if (res < 0)
21351326Sgreen		errx(1, "count cannot be negative");
21489788Sgreen	if (res == 0)
215111629Smarkm		cpy_cnt = (uintmax_t)-1;
21689788Sgreen	else
217111629Smarkm		cpy_cnt = (uintmax_t)res;
2181556Srgrimes}
2191556Srgrimes
2201556Srgrimesstatic void
22190108Simpf_files(char *arg)
2221556Srgrimes{
2231556Srgrimes
22451137Sgreen	files_cnt = get_num(arg);
22551249Sgreen	if (files_cnt < 1)
226111629Smarkm		errx(1, "files must be between 1 and %jd", (uintmax_t)-1);
2271556Srgrimes}
2281556Srgrimes
2291556Srgrimesstatic void
230133762Srwatsonf_fillchar(char *arg)
231133762Srwatson{
232133762Srwatson
233133762Srwatson	if (strlen(arg) != 1)
234133762Srwatson		errx(1, "need exactly one fill char");
235133762Srwatson
236133762Srwatson	fill_char = arg[0];
237133762Srwatson}
238133762Srwatson
239133762Srwatsonstatic void
24090108Simpf_ibs(char *arg)
2411556Srgrimes{
242111629Smarkm	uintmax_t res;
2431556Srgrimes
24448051Sgreen	if (!(ddflags & C_BS)) {
24551208Sgreen		res = get_num(arg);
24651208Sgreen		if (res < 1 || res > SSIZE_MAX)
247112265Sru			errx(1, "ibs must be between 1 and %jd",
248112265Sru			    (intmax_t)SSIZE_MAX);
24951249Sgreen		in.dbsz = (size_t)res;
25048051Sgreen	}
2511556Srgrimes}
2521556Srgrimes
2531556Srgrimesstatic void
25490108Simpf_if(char *arg)
2551556Srgrimes{
2561556Srgrimes
2571556Srgrimes	in.name = arg;
2581556Srgrimes}
2591556Srgrimes
2601556Srgrimesstatic void
26190108Simpf_obs(char *arg)
2621556Srgrimes{
263111629Smarkm	uintmax_t res;
2641556Srgrimes
26548051Sgreen	if (!(ddflags & C_BS)) {
26651208Sgreen		res = get_num(arg);
26751208Sgreen		if (res < 1 || res > SSIZE_MAX)
268112265Sru			errx(1, "obs must be between 1 and %jd",
269112265Sru			    (intmax_t)SSIZE_MAX);
27051208Sgreen		out.dbsz = (size_t)res;
27148051Sgreen	}
2721556Srgrimes}
2731556Srgrimes
2741556Srgrimesstatic void
27590108Simpf_of(char *arg)
2761556Srgrimes{
2771556Srgrimes
2781556Srgrimes	out.name = arg;
2791556Srgrimes}
2801556Srgrimes
2811556Srgrimesstatic void
28290108Simpf_seek(char *arg)
2831556Srgrimes{
2841556Srgrimes
28589788Sgreen	out.offset = get_off_t(arg);
2861556Srgrimes}
2871556Srgrimes
2881556Srgrimesstatic void
28990108Simpf_skip(char *arg)
2901556Srgrimes{
2911556Srgrimes
29289788Sgreen	in.offset = get_off_t(arg);
2931556Srgrimes}
2941556Srgrimes
29551208Sgreenstatic const struct conv {
29654245Sgreen	const char *name;
2971556Srgrimes	u_int set, noset;
29851208Sgreen	const u_char *ctab;
2991556Srgrimes} clist[] = {
3001556Srgrimes	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
3011556Srgrimes	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
3021556Srgrimes	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
3031556Srgrimes	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
3041556Srgrimes	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
3051556Srgrimes	{ "noerror",	C_NOERROR,	0,		NULL },
3061556Srgrimes	{ "notrunc",	C_NOTRUNC,	0,		NULL },
3071556Srgrimes	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
3081556Srgrimes	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
3091556Srgrimes	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
31031120Sjoerg	{ "osync",	C_OSYNC,	C_BS,		NULL },
311126667Sphk	{ "pareven",	C_PAREVEN,	C_PARODD|C_PARSET|C_PARNONE, NULL},
312126667Sphk	{ "parnone",	C_PARNONE,	C_PARODD|C_PARSET|C_PAREVEN, NULL},
313126667Sphk	{ "parodd",	C_PARODD,	C_PAREVEN|C_PARSET|C_PARNONE, NULL},
314126667Sphk	{ "parset",	C_PARSET,	C_PARODD|C_PAREVEN|C_PARNONE, NULL},
31530312Sjoerg	{ "sparse",	C_SPARSE,	0,		NULL },
3161556Srgrimes	{ "swab",	C_SWAB,		0,		NULL },
3171556Srgrimes	{ "sync",	C_SYNC,		0,		NULL },
3181556Srgrimes	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
3191556Srgrimes	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
3201556Srgrimes};
3211556Srgrimes
3221556Srgrimesstatic void
32390108Simpf_conv(char *arg)
3241556Srgrimes{
3251556Srgrimes	struct conv *cp, tmp;
3261556Srgrimes
3271556Srgrimes	while (arg != NULL) {
3281556Srgrimes		tmp.name = strsep(&arg, ",");
32951208Sgreen		cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv),
33051208Sgreen		    sizeof(struct conv), c_conv);
33151208Sgreen		if (cp == NULL)
3321556Srgrimes			errx(1, "unknown conversion %s", tmp.name);
3331556Srgrimes		if (ddflags & cp->noset)
3341556Srgrimes			errx(1, "%s: illegal conversion combination", tmp.name);
3351556Srgrimes		ddflags |= cp->set;
3361556Srgrimes		if (cp->ctab)
3371556Srgrimes			ctab = cp->ctab;
3381556Srgrimes	}
3391556Srgrimes}
3401556Srgrimes
3411556Srgrimesstatic int
34290108Simpc_conv(const void *a, const void *b)
3431556Srgrimes{
3441556Srgrimes
34554278Sgreen	return (strcmp(((const struct conv *)a)->name,
34654278Sgreen	    ((const struct conv *)b)->name));
3471556Srgrimes}
3481556Srgrimes
3491556Srgrimes/*
350111629Smarkm * Convert an expression of the following forms to a uintmax_t.
3511556Srgrimes * 	1) A positive decimal number.
352132933Spjd *	2) A positive decimal number followed by a 'b' or 'B' (mult by 512).
353132933Spjd *	3) A positive decimal number followed by a 'k' or 'K' (mult by 1 << 10).
354132933Spjd *	4) A positive decimal number followed by a 'm' or 'M' (mult by 1 << 20).
355132933Spjd *	5) A positive decimal number followed by a 'g' or 'G' (mult by 1 << 30).
356132933Spjd *	5) A positive decimal number followed by a 'w' or 'W' (mult by sizeof int).
357132933Spjd *	6) Two or more positive decimal numbers (with/without [BbKkMmGgWw])
358132933Spjd *	   separated by 'x' or 'X' (also '*' for backwards compatibility),
359132933Spjd *	   specifying the product of the indicated values.
3601556Srgrimes */
361111629Smarkmstatic uintmax_t
36290108Simpget_num(const char *val)
3631556Srgrimes{
364111629Smarkm	uintmax_t num, mult, prevnum;
3651556Srgrimes	char *expr;
3661556Srgrimes
36748051Sgreen	errno = 0;
36889788Sgreen	num = strtouq(val, &expr, 0);
36951208Sgreen	if (errno != 0)				/* Overflow or underflow. */
37048051Sgreen		err(1, "%s", oper);
37148051Sgreen
37251208Sgreen	if (expr == val)			/* No valid digits. */
3731556Srgrimes		errx(1, "%s: illegal numeric value", oper);
3741556Srgrimes
37589788Sgreen	mult = 0;
37651137Sgreen	switch (*expr) {
377132933Spjd	case 'B':
3781556Srgrimes	case 'b':
37989788Sgreen		mult = 512;
3801556Srgrimes		break;
381132933Spjd	case 'K':
3821556Srgrimes	case 'k':
38389788Sgreen		mult = 1 << 10;
3841556Srgrimes		break;
385132933Spjd	case 'M':
3861556Srgrimes	case 'm':
38789788Sgreen		mult = 1 << 20;
3881556Srgrimes		break;
389132933Spjd	case 'G':
39048051Sgreen	case 'g':
39189788Sgreen		mult = 1 << 30;
39248051Sgreen		break;
393132933Spjd	case 'W':
3941556Srgrimes	case 'w':
39589788Sgreen		mult = sizeof(int);
3961556Srgrimes		break;
39791079Smarkm	default:
39896383Sjedgar		;
3991556Srgrimes	}
4001556Srgrimes
40189788Sgreen	if (mult != 0) {
40289788Sgreen		prevnum = num;
40389788Sgreen		num *= mult;
40489788Sgreen		/* Check for overflow. */
40589788Sgreen		if (num / mult != prevnum)
40689788Sgreen			goto erange;
40789788Sgreen		expr++;
40889788Sgreen	}
40989788Sgreen
41051137Sgreen	switch (*expr) {
4111556Srgrimes		case '\0':
4121556Srgrimes			break;
4131556Srgrimes		case '*':			/* Backward compatible. */
414132933Spjd		case 'X':
4151556Srgrimes		case 'x':
41689788Sgreen			mult = get_num(expr + 1);
41789788Sgreen			prevnum = num;
41889788Sgreen			num *= mult;
41989788Sgreen			if (num / mult == prevnum)
42051137Sgreen				break;
42151137Sgreenerange:
42251137Sgreen			errx(1, "%s: %s", oper, strerror(ERANGE));
4231556Srgrimes		default:
4241556Srgrimes			errx(1, "%s: illegal numeric value", oper);
4251556Srgrimes	}
4261556Srgrimes	return (num);
4271556Srgrimes}
42851208Sgreen
42989788Sgreen/*
43089788Sgreen * Convert an expression of the following forms to an off_t.  This is the
43189788Sgreen * same as get_num(), but it uses signed numbers.
43289788Sgreen *
433111629Smarkm * The major problem here is that an off_t may not necessarily be a intmax_t.
43489788Sgreen */
43551208Sgreenstatic off_t
43690108Simpget_off_t(const char *val)
43751208Sgreen{
438111629Smarkm	intmax_t num, mult, prevnum;
43989788Sgreen	char *expr;
44051208Sgreen
44189788Sgreen	errno = 0;
44289788Sgreen	num = strtoq(val, &expr, 0);
44389788Sgreen	if (errno != 0)				/* Overflow or underflow. */
44489788Sgreen		err(1, "%s", oper);
44589788Sgreen
44689788Sgreen	if (expr == val)			/* No valid digits. */
44789788Sgreen		errx(1, "%s: illegal numeric value", oper);
44889788Sgreen
44989788Sgreen	mult = 0;
45089788Sgreen	switch (*expr) {
451132933Spjd	case 'B':
45289788Sgreen	case 'b':
45389788Sgreen		mult = 512;
45489788Sgreen		break;
455132933Spjd	case 'K':
45689788Sgreen	case 'k':
45789788Sgreen		mult = 1 << 10;
45889788Sgreen		break;
459132933Spjd	case 'M':
46089788Sgreen	case 'm':
46189788Sgreen		mult = 1 << 20;
46289788Sgreen		break;
463132933Spjd	case 'G':
46489788Sgreen	case 'g':
46589788Sgreen		mult = 1 << 30;
46689788Sgreen		break;
467132933Spjd	case 'W':
46889788Sgreen	case 'w':
46989788Sgreen		mult = sizeof(int);
47089788Sgreen		break;
47189788Sgreen	}
47289788Sgreen
47389788Sgreen	if (mult != 0) {
47489788Sgreen		prevnum = num;
47589788Sgreen		num *= mult;
47689788Sgreen		/* Check for overflow. */
47789788Sgreen		if ((prevnum > 0) != (num > 0) || num / mult != prevnum)
47889788Sgreen			goto erange;
47989788Sgreen		expr++;
48089788Sgreen	}
48189788Sgreen
48289788Sgreen	switch (*expr) {
48389788Sgreen		case '\0':
48489788Sgreen			break;
48589788Sgreen		case '*':			/* Backward compatible. */
486132933Spjd		case 'X':
48789788Sgreen		case 'x':
488111629Smarkm			mult = (intmax_t)get_off_t(expr + 1);
48989788Sgreen			prevnum = num;
49089788Sgreen			num *= mult;
49190331Sgreen			if ((prevnum > 0) == (num > 0) && num / mult == prevnum)
49289788Sgreen				break;
49389788Sgreenerange:
49489788Sgreen			errx(1, "%s: %s", oper, strerror(ERANGE));
49589788Sgreen		default:
49689788Sgreen			errx(1, "%s: illegal numeric value", oper);
49789788Sgreen	}
49889788Sgreen	return (num);
49951208Sgreen}
500