args.c revision 295261
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 295261 2016-02-04 15:21:01Z trasz $");
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 *);
69264059Sdelphijstatic void	f_status(char *);
70111629Smarkmstatic uintmax_t get_num(const char *);
7190108Simpstatic off_t	get_off_t(const char *);
721556Srgrimes
7351208Sgreenstatic const struct arg {
7454245Sgreen	const char *name;
7590108Simp	void (*f)(char *);
761556Srgrimes	u_int set, noset;
771556Srgrimes} args[] = {
781556Srgrimes	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
791556Srgrimes	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
801556Srgrimes	{ "conv",	f_conv,		0,	 0 },
811556Srgrimes	{ "count",	f_count,	C_COUNT, C_COUNT },
821556Srgrimes	{ "files",	f_files,	C_FILES, C_FILES },
83133762Srwatson	{ "fillchar",	f_fillchar,	C_FILL,	 C_FILL },
841556Srgrimes	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
851556Srgrimes	{ "if",		f_if,		C_IF,	 C_IF },
8657523Sgreen	{ "iseek",	f_skip,		C_SKIP,	 C_SKIP },
871556Srgrimes	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
881556Srgrimes	{ "of",		f_of,		C_OF,	 C_OF },
8957523Sgreen	{ "oseek",	f_seek,		C_SEEK,	 C_SEEK },
901556Srgrimes	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
911556Srgrimes	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
92264059Sdelphij	{ "status",	f_status,	C_STATUS,C_STATUS },
931556Srgrimes};
941556Srgrimes
951556Srgrimesstatic char *oper;
961556Srgrimes
971556Srgrimes/*
981556Srgrimes * args -- parse JCL syntax of dd.
991556Srgrimes */
1001556Srgrimesvoid
10190108Simpjcl(char **argv)
1021556Srgrimes{
1031556Srgrimes	struct arg *ap, tmp;
1041556Srgrimes	char *arg;
1051556Srgrimes
1061556Srgrimes	in.dbsz = out.dbsz = 512;
1071556Srgrimes
10819720Sphk	while ((oper = *++argv) != NULL) {
10930230Seivind		if ((oper = strdup(oper)) == NULL)
11048051Sgreen			errx(1, "unable to allocate space for the argument \"%s\"", *argv);
1111556Srgrimes		if ((arg = strchr(oper, '=')) == NULL)
1121556Srgrimes			errx(1, "unknown operand %s", oper);
1131556Srgrimes		*arg++ = '\0';
1141556Srgrimes		if (!*arg)
1151556Srgrimes			errx(1, "no value specified for %s", oper);
1161556Srgrimes		tmp.name = oper;
11748051Sgreen		if (!(ap = (struct arg *)bsearch(&tmp, args,
11848051Sgreen		    sizeof(args)/sizeof(struct arg), sizeof(struct arg),
11948051Sgreen		    c_arg)))
1201556Srgrimes			errx(1, "unknown operand %s", tmp.name);
1211556Srgrimes		if (ddflags & ap->noset)
12248051Sgreen			errx(1, "%s: illegal argument combination or already set",
12348051Sgreen			    tmp.name);
1241556Srgrimes		ddflags |= ap->set;
1251556Srgrimes		ap->f(arg);
1261556Srgrimes	}
1271556Srgrimes
1281556Srgrimes	/* Final sanity checks. */
1291556Srgrimes
1301556Srgrimes	if (ddflags & C_BS) {
1311556Srgrimes		/*
1321556Srgrimes		 * Bs is turned off by any conversion -- we assume the user
1331556Srgrimes		 * just wanted to set both the input and output block sizes
1341556Srgrimes		 * and didn't want the bs semantics, so we don't warn.
1351556Srgrimes		 */
13662311Sgreen		if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
13762311Sgreen		    C_UNBLOCK))
1381556Srgrimes			ddflags &= ~C_BS;
1391556Srgrimes
1401556Srgrimes		/* Bs supersedes ibs and obs. */
14162311Sgreen		if (ddflags & C_BS && ddflags & (C_IBS | C_OBS))
1421556Srgrimes			warnx("bs supersedes ibs and obs");
1431556Srgrimes	}
1441556Srgrimes
1451556Srgrimes	/*
1461556Srgrimes	 * Ascii/ebcdic and cbs implies block/unblock.
1471556Srgrimes	 * Block/unblock requires cbs and vice-versa.
1481556Srgrimes	 */
14951249Sgreen	if (ddflags & (C_BLOCK | C_UNBLOCK)) {
1501556Srgrimes		if (!(ddflags & C_CBS))
1511556Srgrimes			errx(1, "record operations require cbs");
1521556Srgrimes		if (cbsz == 0)
1531556Srgrimes			errx(1, "cbs cannot be zero");
1541556Srgrimes		cfunc = ddflags & C_BLOCK ? block : unblock;
1551556Srgrimes	} else if (ddflags & C_CBS) {
15651249Sgreen		if (ddflags & (C_ASCII | C_EBCDIC)) {
1571556Srgrimes			if (ddflags & C_ASCII) {
1581556Srgrimes				ddflags |= C_UNBLOCK;
1591556Srgrimes				cfunc = unblock;
1601556Srgrimes			} else {
1611556Srgrimes				ddflags |= C_BLOCK;
1621556Srgrimes				cfunc = block;
1631556Srgrimes			}
1641556Srgrimes		} else
1651556Srgrimes			errx(1, "cbs meaningless if not doing record operations");
1661556Srgrimes	} else
1671556Srgrimes		cfunc = def;
16851249Sgreen
16951208Sgreen	/*
17051208Sgreen	 * Bail out if the calculation of a file offset would overflow.
17151208Sgreen	 */
172111629Smarkm	if (in.offset > OFF_MAX / (ssize_t)in.dbsz ||
173111629Smarkm	    out.offset > OFF_MAX / (ssize_t)out.dbsz)
174273743Spi		errx(1, "seek offsets cannot be larger than %jd",
175273743Spi		    (intmax_t)OFF_MAX);
1761556Srgrimes}
1771556Srgrimes
1781556Srgrimesstatic int
17990108Simpc_arg(const void *a, const void *b)
1801556Srgrimes{
1811556Srgrimes
18254278Sgreen	return (strcmp(((const struct arg *)a)->name,
18354278Sgreen	    ((const struct arg *)b)->name));
1841556Srgrimes}
1851556Srgrimes
1861556Srgrimesstatic void
18790108Simpf_bs(char *arg)
1881556Srgrimes{
189273743Spi	uintmax_t res;
1901556Srgrimes
191273743Spi	res = get_num(arg);
192273743Spi	if (res < 1 || res > SSIZE_MAX)
193273743Spi		errx(1, "bs must be between 1 and %jd", (intmax_t)SSIZE_MAX);
194273743Spi	in.dbsz = out.dbsz = (size_t)res;
1951556Srgrimes}
1961556Srgrimes
1971556Srgrimesstatic void
19890108Simpf_cbs(char *arg)
1991556Srgrimes{
200273743Spi	uintmax_t res;
2011556Srgrimes
202273743Spi	res = get_num(arg);
203273743Spi	if (res < 1 || res > SSIZE_MAX)
204273743Spi		errx(1, "cbs must be between 1 and %jd", (intmax_t)SSIZE_MAX);
205273743Spi	cbsz = (size_t)res;
2061556Srgrimes}
2071556Srgrimes
2081556Srgrimesstatic void
20990108Simpf_count(char *arg)
2101556Srgrimes{
211273743Spi	intmax_t res;
2121556Srgrimes
213273743Spi	res = (intmax_t)get_num(arg);
214273743Spi	if (res < 0)
215273743Spi		errx(1, "count cannot be negative");
216273743Spi	if (res == 0)
217273743Spi		cpy_cnt = (uintmax_t)-1;
218273743Spi	else
219273743Spi		cpy_cnt = (uintmax_t)res;
2201556Srgrimes}
2211556Srgrimes
2221556Srgrimesstatic void
22390108Simpf_files(char *arg)
2241556Srgrimes{
2251556Srgrimes
22651137Sgreen	files_cnt = get_num(arg);
22751249Sgreen	if (files_cnt < 1)
228273743Spi		errx(1, "files must be between 1 and %jd", (uintmax_t)-1);
2291556Srgrimes}
2301556Srgrimes
2311556Srgrimesstatic void
232133762Srwatsonf_fillchar(char *arg)
233133762Srwatson{
234133762Srwatson
235133762Srwatson	if (strlen(arg) != 1)
236133762Srwatson		errx(1, "need exactly one fill char");
237133762Srwatson
238133762Srwatson	fill_char = arg[0];
239133762Srwatson}
240133762Srwatson
241133762Srwatsonstatic void
24290108Simpf_ibs(char *arg)
2431556Srgrimes{
244273743Spi	uintmax_t res;
2451556Srgrimes
24648051Sgreen	if (!(ddflags & C_BS)) {
247273743Spi		res = get_num(arg);
248273743Spi		if (res < 1 || res > SSIZE_MAX)
249273743Spi			errx(1, "ibs must be between 1 and %jd",
250273743Spi			    (intmax_t)SSIZE_MAX);
251273743Spi		in.dbsz = (size_t)res;
25248051Sgreen	}
2531556Srgrimes}
2541556Srgrimes
2551556Srgrimesstatic void
25690108Simpf_if(char *arg)
2571556Srgrimes{
2581556Srgrimes
2591556Srgrimes	in.name = arg;
2601556Srgrimes}
2611556Srgrimes
2621556Srgrimesstatic void
26390108Simpf_obs(char *arg)
2641556Srgrimes{
265273743Spi	uintmax_t res;
2661556Srgrimes
26748051Sgreen	if (!(ddflags & C_BS)) {
268273743Spi		res = get_num(arg);
269273743Spi		if (res < 1 || res > SSIZE_MAX)
270273743Spi			errx(1, "obs must be between 1 and %jd",
271273743Spi			    (intmax_t)SSIZE_MAX);
272273743Spi		out.dbsz = (size_t)res;
27348051Sgreen	}
2741556Srgrimes}
2751556Srgrimes
2761556Srgrimesstatic void
27790108Simpf_of(char *arg)
2781556Srgrimes{
2791556Srgrimes
2801556Srgrimes	out.name = arg;
2811556Srgrimes}
2821556Srgrimes
2831556Srgrimesstatic void
28490108Simpf_seek(char *arg)
2851556Srgrimes{
2861556Srgrimes
28789788Sgreen	out.offset = get_off_t(arg);
2881556Srgrimes}
2891556Srgrimes
2901556Srgrimesstatic void
29190108Simpf_skip(char *arg)
2921556Srgrimes{
2931556Srgrimes
29489788Sgreen	in.offset = get_off_t(arg);
2951556Srgrimes}
2961556Srgrimes
297264059Sdelphijstatic void
298264059Sdelphijf_status(char *arg)
299264059Sdelphij{
300264059Sdelphij
301264059Sdelphij	if (strcmp(arg, "none") == 0)
302264059Sdelphij		ddflags |= C_NOINFO;
303264059Sdelphij	else if (strcmp(arg, "noxfer") == 0)
304264059Sdelphij		ddflags |= C_NOXFER;
305264059Sdelphij	else
306264059Sdelphij		errx(1, "unknown status %s", arg);
307264059Sdelphij}
308264059Sdelphij
30951208Sgreenstatic const struct conv {
31054245Sgreen	const char *name;
3111556Srgrimes	u_int set, noset;
31251208Sgreen	const u_char *ctab;
3131556Srgrimes} clist[] = {
3141556Srgrimes	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
3151556Srgrimes	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
3161556Srgrimes	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
3171556Srgrimes	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
3181556Srgrimes	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
3191556Srgrimes	{ "noerror",	C_NOERROR,	0,		NULL },
3201556Srgrimes	{ "notrunc",	C_NOTRUNC,	0,		NULL },
3211556Srgrimes	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
3221556Srgrimes	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
3231556Srgrimes	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
32431120Sjoerg	{ "osync",	C_OSYNC,	C_BS,		NULL },
325126667Sphk	{ "pareven",	C_PAREVEN,	C_PARODD|C_PARSET|C_PARNONE, NULL},
326126667Sphk	{ "parnone",	C_PARNONE,	C_PARODD|C_PARSET|C_PAREVEN, NULL},
327126667Sphk	{ "parodd",	C_PARODD,	C_PAREVEN|C_PARSET|C_PARNONE, NULL},
328126667Sphk	{ "parset",	C_PARSET,	C_PARODD|C_PAREVEN|C_PARNONE, NULL},
32930312Sjoerg	{ "sparse",	C_SPARSE,	0,		NULL },
3301556Srgrimes	{ "swab",	C_SWAB,		0,		NULL },
3311556Srgrimes	{ "sync",	C_SYNC,		0,		NULL },
3321556Srgrimes	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
3331556Srgrimes	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
3341556Srgrimes};
3351556Srgrimes
3361556Srgrimesstatic void
33790108Simpf_conv(char *arg)
3381556Srgrimes{
3391556Srgrimes	struct conv *cp, tmp;
3401556Srgrimes
3411556Srgrimes	while (arg != NULL) {
3421556Srgrimes		tmp.name = strsep(&arg, ",");
34351208Sgreen		cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv),
34451208Sgreen		    sizeof(struct conv), c_conv);
34551208Sgreen		if (cp == NULL)
3461556Srgrimes			errx(1, "unknown conversion %s", tmp.name);
3471556Srgrimes		if (ddflags & cp->noset)
3481556Srgrimes			errx(1, "%s: illegal conversion combination", tmp.name);
3491556Srgrimes		ddflags |= cp->set;
3501556Srgrimes		if (cp->ctab)
3511556Srgrimes			ctab = cp->ctab;
3521556Srgrimes	}
3531556Srgrimes}
3541556Srgrimes
3551556Srgrimesstatic int
35690108Simpc_conv(const void *a, const void *b)
3571556Srgrimes{
3581556Srgrimes
35954278Sgreen	return (strcmp(((const struct conv *)a)->name,
36054278Sgreen	    ((const struct conv *)b)->name));
3611556Srgrimes}
3621556Srgrimes
363295260Straszstatic uintmax_t
364295260Straszpostfix_to_mult(const char expr)
365295260Strasz{
366295260Strasz	uintmax_t mult;
367295260Strasz
368295260Strasz	mult = 0;
369295260Strasz	switch (expr) {
370295260Strasz	case 'B':
371295260Strasz	case 'b':
372295260Strasz		mult = 512;
373295260Strasz		break;
374295260Strasz	case 'K':
375295260Strasz	case 'k':
376295260Strasz		mult = 1 << 10;
377295260Strasz		break;
378295260Strasz	case 'M':
379295260Strasz	case 'm':
380295260Strasz		mult = 1 << 20;
381295260Strasz		break;
382295260Strasz	case 'G':
383295260Strasz	case 'g':
384295260Strasz		mult = 1 << 30;
385295260Strasz		break;
386295261Strasz	case 'T':
387295261Strasz	case 't':
388295261Strasz		mult = (uintmax_t)1 << 40;
389295261Strasz		break;
390295261Strasz	case 'P':
391295261Strasz	case 'p':
392295261Strasz		mult = (uintmax_t)1 << 50;
393295261Strasz		break;
394295260Strasz	case 'W':
395295260Strasz	case 'w':
396295260Strasz		mult = sizeof(int);
397295260Strasz		break;
398295260Strasz	}
399295260Strasz
400295260Strasz	return (mult);
401295260Strasz}
402295260Strasz
4031556Srgrimes/*
404111629Smarkm * Convert an expression of the following forms to a uintmax_t.
4051556Srgrimes * 	1) A positive decimal number.
406132933Spjd *	2) A positive decimal number followed by a 'b' or 'B' (mult by 512).
407132933Spjd *	3) A positive decimal number followed by a 'k' or 'K' (mult by 1 << 10).
408132933Spjd *	4) A positive decimal number followed by a 'm' or 'M' (mult by 1 << 20).
409132933Spjd *	5) A positive decimal number followed by a 'g' or 'G' (mult by 1 << 30).
410132933Spjd *	5) A positive decimal number followed by a 'w' or 'W' (mult by sizeof int).
411132933Spjd *	6) Two or more positive decimal numbers (with/without [BbKkMmGgWw])
412132933Spjd *	   separated by 'x' or 'X' (also '*' for backwards compatibility),
413132933Spjd *	   specifying the product of the indicated values.
4141556Srgrimes */
415111629Smarkmstatic uintmax_t
41690108Simpget_num(const char *val)
4171556Srgrimes{
418111629Smarkm	uintmax_t num, mult, prevnum;
4191556Srgrimes	char *expr;
4201556Srgrimes
42148051Sgreen	errno = 0;
422273743Spi	num = strtouq(val, &expr, 0);
42351208Sgreen	if (errno != 0)				/* Overflow or underflow. */
42448051Sgreen		err(1, "%s", oper);
425273743Spi
42651208Sgreen	if (expr == val)			/* No valid digits. */
4271556Srgrimes		errx(1, "%s: illegal numeric value", oper);
4281556Srgrimes
429295260Strasz	mult = postfix_to_mult(*expr);
4301556Srgrimes
43189788Sgreen	if (mult != 0) {
43289788Sgreen		prevnum = num;
43389788Sgreen		num *= mult;
43489788Sgreen		/* Check for overflow. */
43589788Sgreen		if (num / mult != prevnum)
43689788Sgreen			goto erange;
43789788Sgreen		expr++;
43889788Sgreen	}
43989788Sgreen
44051137Sgreen	switch (*expr) {
4411556Srgrimes		case '\0':
4421556Srgrimes			break;
4431556Srgrimes		case '*':			/* Backward compatible. */
444132933Spjd		case 'X':
4451556Srgrimes		case 'x':
44689788Sgreen			mult = get_num(expr + 1);
44789788Sgreen			prevnum = num;
44889788Sgreen			num *= mult;
44989788Sgreen			if (num / mult == prevnum)
45051137Sgreen				break;
45151137Sgreenerange:
45251137Sgreen			errx(1, "%s: %s", oper, strerror(ERANGE));
4531556Srgrimes		default:
4541556Srgrimes			errx(1, "%s: illegal numeric value", oper);
4551556Srgrimes	}
4561556Srgrimes	return (num);
4571556Srgrimes}
45851208Sgreen
45989788Sgreen/*
46089788Sgreen * Convert an expression of the following forms to an off_t.  This is the
46189788Sgreen * same as get_num(), but it uses signed numbers.
46289788Sgreen *
463111629Smarkm * The major problem here is that an off_t may not necessarily be a intmax_t.
46489788Sgreen */
46551208Sgreenstatic off_t
46690108Simpget_off_t(const char *val)
46751208Sgreen{
468111629Smarkm	intmax_t num, mult, prevnum;
46989788Sgreen	char *expr;
47051208Sgreen
47189788Sgreen	errno = 0;
47289788Sgreen	num = strtoq(val, &expr, 0);
47389788Sgreen	if (errno != 0)				/* Overflow or underflow. */
47489788Sgreen		err(1, "%s", oper);
47589788Sgreen
47689788Sgreen	if (expr == val)			/* No valid digits. */
47789788Sgreen		errx(1, "%s: illegal numeric value", oper);
47889788Sgreen
479295260Strasz	mult = postfix_to_mult(*expr);
48089788Sgreen
48189788Sgreen	if (mult != 0) {
48289788Sgreen		prevnum = num;
48389788Sgreen		num *= mult;
48489788Sgreen		/* Check for overflow. */
48589788Sgreen		if ((prevnum > 0) != (num > 0) || num / mult != prevnum)
48689788Sgreen			goto erange;
48789788Sgreen		expr++;
48889788Sgreen	}
48989788Sgreen
49089788Sgreen	switch (*expr) {
49189788Sgreen		case '\0':
49289788Sgreen			break;
49389788Sgreen		case '*':			/* Backward compatible. */
494132933Spjd		case 'X':
49589788Sgreen		case 'x':
496111629Smarkm			mult = (intmax_t)get_off_t(expr + 1);
49789788Sgreen			prevnum = num;
49889788Sgreen			num *= mult;
49990331Sgreen			if ((prevnum > 0) == (num > 0) && num / mult == prevnum)
50089788Sgreen				break;
50189788Sgreenerange:
50289788Sgreen			errx(1, "%s: %s", oper, strerror(ERANGE));
50389788Sgreen		default:
50489788Sgreen			errx(1, "%s: illegal numeric value", oper);
50589788Sgreen	}
50689788Sgreen	return (num);
50751208Sgreen}
508