xref: /dragonfly/usr.bin/seq/seq.c (revision 86d7f5d3)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * Copyright (c) 2005 The DragonFly Project.  All rights reserved.
3*86d7f5d3SJohn Marino  *
4*86d7f5d3SJohn Marino  * This code is derived from software contributed to The DragonFly Project
5*86d7f5d3SJohn Marino  * by Brian Ginsbach.
6*86d7f5d3SJohn Marino  *
7*86d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
8*86d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
9*86d7f5d3SJohn Marino  * are met:
10*86d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
11*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
12*86d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
13*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
14*86d7f5d3SJohn Marino  *    documentation and/or other materials provided with the distribution.
15*86d7f5d3SJohn Marino  * 3. Neither the name of The DragonFly Project nor the names of its
16*86d7f5d3SJohn Marino  *    contributors may be used to endorse or promote products derived
17*86d7f5d3SJohn Marino  *    from this software without specific prior written permission.
18*86d7f5d3SJohn Marino  *
19*86d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*86d7f5d3SJohn Marino  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*86d7f5d3SJohn Marino  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22*86d7f5d3SJohn Marino  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
23*86d7f5d3SJohn Marino  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24*86d7f5d3SJohn Marino  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25*86d7f5d3SJohn Marino  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26*86d7f5d3SJohn Marino  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27*86d7f5d3SJohn Marino  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28*86d7f5d3SJohn Marino  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
29*86d7f5d3SJohn Marino  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30*86d7f5d3SJohn Marino  * SUCH DAMAGE.
31*86d7f5d3SJohn Marino  *
32*86d7f5d3SJohn Marino  * $DragonFly: src/usr.bin/seq/seq.c,v 1.1 2005/01/22 19:09:40 joerg Exp $
33*86d7f5d3SJohn Marino  */
34*86d7f5d3SJohn Marino 
35*86d7f5d3SJohn Marino #include <ctype.h>
36*86d7f5d3SJohn Marino #include <err.h>
37*86d7f5d3SJohn Marino #include <errno.h>
38*86d7f5d3SJohn Marino #include <math.h>
39*86d7f5d3SJohn Marino #include <locale.h>
40*86d7f5d3SJohn Marino #include <stdio.h>
41*86d7f5d3SJohn Marino #include <stdlib.h>
42*86d7f5d3SJohn Marino #include <string.h>
43*86d7f5d3SJohn Marino #include <unistd.h>
44*86d7f5d3SJohn Marino 
45*86d7f5d3SJohn Marino #define ZERO	'0'
46*86d7f5d3SJohn Marino #define SPACE	' '
47*86d7f5d3SJohn Marino 
48*86d7f5d3SJohn Marino #define MAX(a, b)	(((a) < (b))? (b) : (a))
49*86d7f5d3SJohn Marino #define ISSIGN(c)	((int)(c) == '-' || (int)(c) == '+')
50*86d7f5d3SJohn Marino #define ISEXP(c)	((int)(c) == 'e' || (int)(c) == 'E')
51*86d7f5d3SJohn Marino #define ISODIGIT(c)	((int)(c) >= '0' && (int)(c) <= '7')
52*86d7f5d3SJohn Marino 
53*86d7f5d3SJohn Marino /* Globals */
54*86d7f5d3SJohn Marino 
55*86d7f5d3SJohn Marino const char *decimal_point = ".";	/* default */
56*86d7f5d3SJohn Marino char default_format[] = { "%g" };	/* default */
57*86d7f5d3SJohn Marino 
58*86d7f5d3SJohn Marino /* Prototypes */
59*86d7f5d3SJohn Marino 
60*86d7f5d3SJohn Marino double	e_atof(const char *);
61*86d7f5d3SJohn Marino 
62*86d7f5d3SJohn Marino int	decimal_places(const char *);
63*86d7f5d3SJohn Marino int	numeric(const char *);
64*86d7f5d3SJohn Marino int	valid_format(const char *);
65*86d7f5d3SJohn Marino 
66*86d7f5d3SJohn Marino char	*generate_format(double, double, double, int, char);
67*86d7f5d3SJohn Marino char	*unescape(char *);
68*86d7f5d3SJohn Marino 
69*86d7f5d3SJohn Marino /*
70*86d7f5d3SJohn Marino  * The seq command will print out a numeric sequence from 1, the default,
71*86d7f5d3SJohn Marino  * to a user specified upper limit by 1.  The lower bound and increment
72*86d7f5d3SJohn Marino  * maybe indicated by the user on the command line.  The sequence can
73*86d7f5d3SJohn Marino  * be either whole, the default, or decimal numbers.
74*86d7f5d3SJohn Marino  */
75*86d7f5d3SJohn Marino int
main(int argc,char ** argv)76*86d7f5d3SJohn Marino main(int argc, char **argv)
77*86d7f5d3SJohn Marino {
78*86d7f5d3SJohn Marino 	int c = 0, errflg = 0;
79*86d7f5d3SJohn Marino 	int equalize = 0;
80*86d7f5d3SJohn Marino 	double first = 1.0;
81*86d7f5d3SJohn Marino 	double last = 0.0;
82*86d7f5d3SJohn Marino 	double incr = 0.0;
83*86d7f5d3SJohn Marino 	struct lconv *locale;
84*86d7f5d3SJohn Marino 	char *fmt = NULL;
85*86d7f5d3SJohn Marino 	const char *sep = "\n";
86*86d7f5d3SJohn Marino 	const char *term = NULL;
87*86d7f5d3SJohn Marino 	char pad = ZERO;
88*86d7f5d3SJohn Marino 
89*86d7f5d3SJohn Marino 	/* Determine the locale's decimal point. */
90*86d7f5d3SJohn Marino 	locale = localeconv();
91*86d7f5d3SJohn Marino 	if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
92*86d7f5d3SJohn Marino 		decimal_point = locale->decimal_point;
93*86d7f5d3SJohn Marino 
94*86d7f5d3SJohn Marino 	/*
95*86d7f5d3SJohn Marino          * Process options, but handle negative numbers separately
96*86d7f5d3SJohn Marino          * least they trip up getopt(3).
97*86d7f5d3SJohn Marino          */
98*86d7f5d3SJohn Marino 	while ((optind < argc) && !numeric(argv[optind]) &&
99*86d7f5d3SJohn Marino 	    (c = getopt(argc, argv, "f:hs:t:w")) != -1) {
100*86d7f5d3SJohn Marino 
101*86d7f5d3SJohn Marino 		switch (c) {
102*86d7f5d3SJohn Marino 		case 'f':	/* format (plan9) */
103*86d7f5d3SJohn Marino 			fmt = optarg;
104*86d7f5d3SJohn Marino 			equalize = 0;
105*86d7f5d3SJohn Marino 			break;
106*86d7f5d3SJohn Marino 		case 's':	/* separator (GNU) */
107*86d7f5d3SJohn Marino 			sep = unescape(optarg);
108*86d7f5d3SJohn Marino 			break;
109*86d7f5d3SJohn Marino 		case 't':	/* terminator (new) */
110*86d7f5d3SJohn Marino 			term = unescape(optarg);
111*86d7f5d3SJohn Marino 			break;
112*86d7f5d3SJohn Marino 		case 'w':	/* equal width (plan9) */
113*86d7f5d3SJohn Marino 			if (!fmt)
114*86d7f5d3SJohn Marino 				if (equalize++)
115*86d7f5d3SJohn Marino 					pad = SPACE;
116*86d7f5d3SJohn Marino 			break;
117*86d7f5d3SJohn Marino 		case 'h':	/* help (GNU) */
118*86d7f5d3SJohn Marino 		default:
119*86d7f5d3SJohn Marino 			errflg++;
120*86d7f5d3SJohn Marino 			break;
121*86d7f5d3SJohn Marino 		}
122*86d7f5d3SJohn Marino 	}
123*86d7f5d3SJohn Marino 
124*86d7f5d3SJohn Marino 	argc -= optind;
125*86d7f5d3SJohn Marino 	argv += optind;
126*86d7f5d3SJohn Marino 	if (argc < 1 || argc > 3)
127*86d7f5d3SJohn Marino 		errflg++;
128*86d7f5d3SJohn Marino 
129*86d7f5d3SJohn Marino 	if (errflg) {
130*86d7f5d3SJohn Marino 		fprintf(stderr,
131*86d7f5d3SJohn Marino 		    "usage: %s [-f format] [-s str] [-t str] [-w] [first [incr]] last\n",
132*86d7f5d3SJohn Marino 		    getprogname());
133*86d7f5d3SJohn Marino 		exit(1);
134*86d7f5d3SJohn Marino 	}
135*86d7f5d3SJohn Marino 
136*86d7f5d3SJohn Marino 	last = e_atof(argv[argc - 1]);
137*86d7f5d3SJohn Marino 
138*86d7f5d3SJohn Marino 	if (argc > 1)
139*86d7f5d3SJohn Marino 		first = e_atof(argv[0]);
140*86d7f5d3SJohn Marino 
141*86d7f5d3SJohn Marino 	if (argc > 2) {
142*86d7f5d3SJohn Marino 		incr = e_atof(argv[1]);
143*86d7f5d3SJohn Marino 		/* Plan 9/GNU don't do zero */
144*86d7f5d3SJohn Marino 		if (incr == 0.0)
145*86d7f5d3SJohn Marino 			errx(1, "zero %screment", (first < last)? "in" : "de");
146*86d7f5d3SJohn Marino 	}
147*86d7f5d3SJohn Marino 
148*86d7f5d3SJohn Marino 	/* default is one for Plan 9/GNU work alike */
149*86d7f5d3SJohn Marino 	if (incr == 0.0)
150*86d7f5d3SJohn Marino 		incr = (first < last) ? 1.0 : -1.0;
151*86d7f5d3SJohn Marino 
152*86d7f5d3SJohn Marino 	if (incr <= 0.0 && first < last)
153*86d7f5d3SJohn Marino 		errx(1, "needs positive increment");
154*86d7f5d3SJohn Marino 
155*86d7f5d3SJohn Marino 	if (incr >= 0.0 && first > last)
156*86d7f5d3SJohn Marino 		errx(1, "needs negative decrement");
157*86d7f5d3SJohn Marino 
158*86d7f5d3SJohn Marino 	if (fmt != NULL) {
159*86d7f5d3SJohn Marino 		if (!valid_format(fmt))
160*86d7f5d3SJohn Marino 			errx(1, "invalid format string: `%s'", fmt);
161*86d7f5d3SJohn Marino 		fmt = unescape(fmt);
162*86d7f5d3SJohn Marino 		/*
163*86d7f5d3SJohn Marino 	         * XXX to be bug for bug compatible with Plan 9 add a
164*86d7f5d3SJohn Marino 		 * newline if none found at the end of the format string.
165*86d7f5d3SJohn Marino 		 */
166*86d7f5d3SJohn Marino 	} else
167*86d7f5d3SJohn Marino 		fmt = generate_format(first, incr, last, equalize, pad);
168*86d7f5d3SJohn Marino 
169*86d7f5d3SJohn Marino 	if (incr > 0) {
170*86d7f5d3SJohn Marino 		for (; first <= last; first += incr) {
171*86d7f5d3SJohn Marino 			printf(fmt, first);
172*86d7f5d3SJohn Marino 			fputs(sep, stdout);
173*86d7f5d3SJohn Marino 		}
174*86d7f5d3SJohn Marino 	} else {
175*86d7f5d3SJohn Marino 		for (; first >= last; first += incr) {
176*86d7f5d3SJohn Marino 			printf(fmt, first);
177*86d7f5d3SJohn Marino 			fputs(sep, stdout);
178*86d7f5d3SJohn Marino 		}
179*86d7f5d3SJohn Marino 	}
180*86d7f5d3SJohn Marino 	if (term != NULL)
181*86d7f5d3SJohn Marino 		fputs(term, stdout);
182*86d7f5d3SJohn Marino 
183*86d7f5d3SJohn Marino 	return(0);
184*86d7f5d3SJohn Marino }
185*86d7f5d3SJohn Marino 
186*86d7f5d3SJohn Marino /*
187*86d7f5d3SJohn Marino  * numeric - verify that string is numeric
188*86d7f5d3SJohn Marino  */
189*86d7f5d3SJohn Marino int
numeric(const char * s)190*86d7f5d3SJohn Marino numeric(const char *s)
191*86d7f5d3SJohn Marino {
192*86d7f5d3SJohn Marino 	int seen_decimal_pt, decimal_pt_len;
193*86d7f5d3SJohn Marino 
194*86d7f5d3SJohn Marino 	/* skip any sign */
195*86d7f5d3SJohn Marino 	if (ISSIGN((unsigned char)*s))
196*86d7f5d3SJohn Marino 		s++;
197*86d7f5d3SJohn Marino 
198*86d7f5d3SJohn Marino 	seen_decimal_pt = 0;
199*86d7f5d3SJohn Marino 	decimal_pt_len = strlen(decimal_point);
200*86d7f5d3SJohn Marino 	while (*s != '\0') {
201*86d7f5d3SJohn Marino 		if (!isdigit((unsigned char)*s)) {
202*86d7f5d3SJohn Marino 			if (!seen_decimal_pt &&
203*86d7f5d3SJohn Marino 			    strncmp(s, decimal_point, decimal_pt_len) == 0) {
204*86d7f5d3SJohn Marino 				s += decimal_pt_len;
205*86d7f5d3SJohn Marino 				seen_decimal_pt = 1;
206*86d7f5d3SJohn Marino 				continue;
207*86d7f5d3SJohn Marino 			}
208*86d7f5d3SJohn Marino 			if (ISEXP((unsigned char)*s)) {
209*86d7f5d3SJohn Marino 				s++;
210*86d7f5d3SJohn Marino 				if (ISSIGN((unsigned char)*s)) {
211*86d7f5d3SJohn Marino 					s++;
212*86d7f5d3SJohn Marino 					continue;
213*86d7f5d3SJohn Marino 				}
214*86d7f5d3SJohn Marino 			}
215*86d7f5d3SJohn Marino 			break;
216*86d7f5d3SJohn Marino 		}
217*86d7f5d3SJohn Marino 		s++;
218*86d7f5d3SJohn Marino 	}
219*86d7f5d3SJohn Marino 	return(*s == '\0');
220*86d7f5d3SJohn Marino }
221*86d7f5d3SJohn Marino 
222*86d7f5d3SJohn Marino /*
223*86d7f5d3SJohn Marino  * valid_format - validate user specified format string
224*86d7f5d3SJohn Marino  */
225*86d7f5d3SJohn Marino int
valid_format(const char * fmt)226*86d7f5d3SJohn Marino valid_format(const char *fmt)
227*86d7f5d3SJohn Marino {
228*86d7f5d3SJohn Marino 	int conversions = 0;
229*86d7f5d3SJohn Marino 
230*86d7f5d3SJohn Marino 	while (*fmt != '\0') {
231*86d7f5d3SJohn Marino 		/* scan for conversions */
232*86d7f5d3SJohn Marino 		if (*fmt != '\0' && *fmt != '%') {
233*86d7f5d3SJohn Marino 			do {
234*86d7f5d3SJohn Marino 				fmt++;
235*86d7f5d3SJohn Marino 			} while (*fmt != '\0' && *fmt != '%');
236*86d7f5d3SJohn Marino 		}
237*86d7f5d3SJohn Marino 		/* scan a conversion */
238*86d7f5d3SJohn Marino 		if (*fmt != '\0') {
239*86d7f5d3SJohn Marino 			do {
240*86d7f5d3SJohn Marino 				fmt++;
241*86d7f5d3SJohn Marino 
242*86d7f5d3SJohn Marino 				/* ok %% */
243*86d7f5d3SJohn Marino 				if (*fmt == '%') {
244*86d7f5d3SJohn Marino 					fmt++;
245*86d7f5d3SJohn Marino 					break;
246*86d7f5d3SJohn Marino 				}
247*86d7f5d3SJohn Marino 				/* valid conversions */
248*86d7f5d3SJohn Marino 				if (strchr("eEfgG", *fmt) &&
249*86d7f5d3SJohn Marino 				    conversions++ < 1) {
250*86d7f5d3SJohn Marino 					fmt++;
251*86d7f5d3SJohn Marino 					break;
252*86d7f5d3SJohn Marino 				}
253*86d7f5d3SJohn Marino 				/* flags, width and precsision */
254*86d7f5d3SJohn Marino 				if (isdigit((unsigned char)*fmt) ||
255*86d7f5d3SJohn Marino 				    strchr("+- 0#.", *fmt))
256*86d7f5d3SJohn Marino 					continue;
257*86d7f5d3SJohn Marino 
258*86d7f5d3SJohn Marino 				/* oops! bad conversion format! */
259*86d7f5d3SJohn Marino 				return(0);
260*86d7f5d3SJohn Marino 			} while (*fmt != '\0');
261*86d7f5d3SJohn Marino 		}
262*86d7f5d3SJohn Marino 	}
263*86d7f5d3SJohn Marino 
264*86d7f5d3SJohn Marino 	return(conversions <= 1);
265*86d7f5d3SJohn Marino }
266*86d7f5d3SJohn Marino 
267*86d7f5d3SJohn Marino /*
268*86d7f5d3SJohn Marino  * unescape - handle C escapes in a string
269*86d7f5d3SJohn Marino  */
270*86d7f5d3SJohn Marino char *
unescape(char * orig)271*86d7f5d3SJohn Marino unescape(char *orig)
272*86d7f5d3SJohn Marino {
273*86d7f5d3SJohn Marino 	char c, *cp, *new = orig;
274*86d7f5d3SJohn Marino 	int i;
275*86d7f5d3SJohn Marino 
276*86d7f5d3SJohn Marino 	for (cp = orig; (*orig = *cp) != '\0'; cp++, orig++) {
277*86d7f5d3SJohn Marino 		if (*cp != '\\')
278*86d7f5d3SJohn Marino 			continue;
279*86d7f5d3SJohn Marino 
280*86d7f5d3SJohn Marino 		switch (*++cp) {
281*86d7f5d3SJohn Marino 		case 'a':	/* alert (bell) */
282*86d7f5d3SJohn Marino 			*orig = '\a';
283*86d7f5d3SJohn Marino 			continue;
284*86d7f5d3SJohn Marino 		case 'b':	/* backspace */
285*86d7f5d3SJohn Marino 			*orig = '\b';
286*86d7f5d3SJohn Marino 			continue;
287*86d7f5d3SJohn Marino 		case 'e':	/* escape */
288*86d7f5d3SJohn Marino 			*orig = '\e';
289*86d7f5d3SJohn Marino 			continue;
290*86d7f5d3SJohn Marino 		case 'f':	/* formfeed */
291*86d7f5d3SJohn Marino 			*orig = '\f';
292*86d7f5d3SJohn Marino 			continue;
293*86d7f5d3SJohn Marino 		case 'n':	/* newline */
294*86d7f5d3SJohn Marino 			*orig = '\n';
295*86d7f5d3SJohn Marino 			continue;
296*86d7f5d3SJohn Marino 		case 'r':	/* carriage return */
297*86d7f5d3SJohn Marino 			*orig = '\r';
298*86d7f5d3SJohn Marino 			continue;
299*86d7f5d3SJohn Marino 		case 't':	/* horizontal tab */
300*86d7f5d3SJohn Marino 			*orig = '\t';
301*86d7f5d3SJohn Marino 			continue;
302*86d7f5d3SJohn Marino 		case 'v':	/* vertical tab */
303*86d7f5d3SJohn Marino 			*orig = '\v';
304*86d7f5d3SJohn Marino 			continue;
305*86d7f5d3SJohn Marino 		case '\\':	/* backslash */
306*86d7f5d3SJohn Marino 			*orig = '\\';
307*86d7f5d3SJohn Marino 			continue;
308*86d7f5d3SJohn Marino 		case '\'':	/* single quote */
309*86d7f5d3SJohn Marino 			*orig = '\'';
310*86d7f5d3SJohn Marino 			continue;
311*86d7f5d3SJohn Marino 		case '\"':	/* double quote */
312*86d7f5d3SJohn Marino 			*orig = '"';
313*86d7f5d3SJohn Marino 			continue;
314*86d7f5d3SJohn Marino 		case '0':
315*86d7f5d3SJohn Marino 		case '1':
316*86d7f5d3SJohn Marino 		case '2':
317*86d7f5d3SJohn Marino 		case '3':	/* octal */
318*86d7f5d3SJohn Marino 		case '4':
319*86d7f5d3SJohn Marino 		case '5':
320*86d7f5d3SJohn Marino 		case '6':
321*86d7f5d3SJohn Marino 		case '7':	/* number */
322*86d7f5d3SJohn Marino 			for (i = 0, c = 0;
323*86d7f5d3SJohn Marino 			     ISODIGIT((unsigned char)*cp) && i < 3;
324*86d7f5d3SJohn Marino 			     i++, cp++) {
325*86d7f5d3SJohn Marino 				c <<= 3;
326*86d7f5d3SJohn Marino 				c |= (*cp - '0');
327*86d7f5d3SJohn Marino 			}
328*86d7f5d3SJohn Marino 			*orig = c;
329*86d7f5d3SJohn Marino 			continue;
330*86d7f5d3SJohn Marino 		case 'x':	/* hexidecimal number */
331*86d7f5d3SJohn Marino 			cp++;	/* skip 'x' */
332*86d7f5d3SJohn Marino 			for (i = 0, c = 0;
333*86d7f5d3SJohn Marino 			     isxdigit((unsigned char)*cp) && i < 2;
334*86d7f5d3SJohn Marino 			     i++, cp++) {
335*86d7f5d3SJohn Marino 				c <<= 4;
336*86d7f5d3SJohn Marino 				if (isdigit((unsigned char)*cp))
337*86d7f5d3SJohn Marino 					c |= (*cp - '0');
338*86d7f5d3SJohn Marino 				else
339*86d7f5d3SJohn Marino 					c |= ((toupper((unsigned char)*cp) -
340*86d7f5d3SJohn Marino 					    'A') + 10);
341*86d7f5d3SJohn Marino 			}
342*86d7f5d3SJohn Marino 			*orig = c;
343*86d7f5d3SJohn Marino 			continue;
344*86d7f5d3SJohn Marino 		default:
345*86d7f5d3SJohn Marino 			--cp;
346*86d7f5d3SJohn Marino 			break;
347*86d7f5d3SJohn Marino 		}
348*86d7f5d3SJohn Marino 	}
349*86d7f5d3SJohn Marino 
350*86d7f5d3SJohn Marino 	return(new);
351*86d7f5d3SJohn Marino }
352*86d7f5d3SJohn Marino 
353*86d7f5d3SJohn Marino /*
354*86d7f5d3SJohn Marino  * e_atof - convert an ASCII string to a double
355*86d7f5d3SJohn Marino  *	exit if string is not a valid double, or if converted value would
356*86d7f5d3SJohn Marino  *	cause overflow or underflow
357*86d7f5d3SJohn Marino  */
358*86d7f5d3SJohn Marino double
e_atof(const char * num)359*86d7f5d3SJohn Marino e_atof(const char *num)
360*86d7f5d3SJohn Marino {
361*86d7f5d3SJohn Marino 	char *endp;
362*86d7f5d3SJohn Marino 	double dbl;
363*86d7f5d3SJohn Marino 
364*86d7f5d3SJohn Marino 	errno = 0;
365*86d7f5d3SJohn Marino 	dbl = strtod(num, &endp);
366*86d7f5d3SJohn Marino 
367*86d7f5d3SJohn Marino 	if (errno == ERANGE)
368*86d7f5d3SJohn Marino 		/* under or overflow */
369*86d7f5d3SJohn Marino 		err(2, "%s", num);
370*86d7f5d3SJohn Marino 	else if (*endp != '\0')
371*86d7f5d3SJohn Marino 		/* "junk" left in number */
372*86d7f5d3SJohn Marino 		errx(2, "invalid floating point argument: %s", num);
373*86d7f5d3SJohn Marino 
374*86d7f5d3SJohn Marino 	/* zero shall have no sign */
375*86d7f5d3SJohn Marino 	if (dbl == -0.0)
376*86d7f5d3SJohn Marino 		dbl = 0;
377*86d7f5d3SJohn Marino 	return(dbl);
378*86d7f5d3SJohn Marino }
379*86d7f5d3SJohn Marino 
380*86d7f5d3SJohn Marino /*
381*86d7f5d3SJohn Marino  * decimal_places - count decimal places in a number (string)
382*86d7f5d3SJohn Marino  */
383*86d7f5d3SJohn Marino int
decimal_places(const char * number)384*86d7f5d3SJohn Marino decimal_places(const char *number)
385*86d7f5d3SJohn Marino {
386*86d7f5d3SJohn Marino 	int places = 0;
387*86d7f5d3SJohn Marino 	char *dp;
388*86d7f5d3SJohn Marino 
389*86d7f5d3SJohn Marino 	/* look for a decimal point */
390*86d7f5d3SJohn Marino 	if ((dp = strstr(number, decimal_point)) != NULL) {
391*86d7f5d3SJohn Marino 		dp += strlen(decimal_point);
392*86d7f5d3SJohn Marino 
393*86d7f5d3SJohn Marino 		while (isdigit((unsigned char)*dp++))
394*86d7f5d3SJohn Marino 			places++;
395*86d7f5d3SJohn Marino 	}
396*86d7f5d3SJohn Marino 	return(places);
397*86d7f5d3SJohn Marino }
398*86d7f5d3SJohn Marino 
399*86d7f5d3SJohn Marino /*
400*86d7f5d3SJohn Marino  * generate_format - create a format string
401*86d7f5d3SJohn Marino  *
402*86d7f5d3SJohn Marino  * XXX to be bug for bug compatable with Plan9 and GNU return "%g"
403*86d7f5d3SJohn Marino  * when "%g" prints as "%e" (this way no width adjustments are made)
404*86d7f5d3SJohn Marino  */
405*86d7f5d3SJohn Marino char *
generate_format(double first,double incr,double last,int equalize,char pad)406*86d7f5d3SJohn Marino generate_format(double first, double incr, double last, int equalize, char pad)
407*86d7f5d3SJohn Marino {
408*86d7f5d3SJohn Marino 	static char buf[256];
409*86d7f5d3SJohn Marino 	char cc = '\0';
410*86d7f5d3SJohn Marino 	int precision, width1, width2, places;
411*86d7f5d3SJohn Marino 
412*86d7f5d3SJohn Marino 	if (equalize == 0)
413*86d7f5d3SJohn Marino 		return(default_format);
414*86d7f5d3SJohn Marino 
415*86d7f5d3SJohn Marino 	/* figure out "last" value printed */
416*86d7f5d3SJohn Marino 	if (first > last)
417*86d7f5d3SJohn Marino 		last = first - incr * floor((first - last) / incr);
418*86d7f5d3SJohn Marino 	else
419*86d7f5d3SJohn Marino 		last = first + incr * floor((last - first) / incr);
420*86d7f5d3SJohn Marino 
421*86d7f5d3SJohn Marino 	snprintf(buf, sizeof(buf), "%g", incr);
422*86d7f5d3SJohn Marino 	if (strchr(buf, 'e'))
423*86d7f5d3SJohn Marino 		cc = 'e';
424*86d7f5d3SJohn Marino 	precision = decimal_places(buf);
425*86d7f5d3SJohn Marino 
426*86d7f5d3SJohn Marino 	width1 = snprintf(buf, sizeof(buf), "%g", first);
427*86d7f5d3SJohn Marino 	if (strchr(buf, 'e'))
428*86d7f5d3SJohn Marino 		cc = 'e';
429*86d7f5d3SJohn Marino 	if ((places = decimal_places(buf)))
430*86d7f5d3SJohn Marino 		width1 -= (places + strlen(decimal_point));
431*86d7f5d3SJohn Marino 
432*86d7f5d3SJohn Marino 	precision = MAX(places, precision);
433*86d7f5d3SJohn Marino 
434*86d7f5d3SJohn Marino 	width2 = snprintf(buf, sizeof(buf), "%g", last);
435*86d7f5d3SJohn Marino 	if (strchr(buf, 'e'))
436*86d7f5d3SJohn Marino 		cc = 'e';
437*86d7f5d3SJohn Marino 	if ((places = decimal_places(buf)))
438*86d7f5d3SJohn Marino 		width2 -= (places + strlen(decimal_point));
439*86d7f5d3SJohn Marino 
440*86d7f5d3SJohn Marino 	if (precision) {
441*86d7f5d3SJohn Marino 		snprintf(buf, sizeof(buf), "%%%c%d.%d%c", pad,
442*86d7f5d3SJohn Marino 		    MAX(width1, width2) + (int) strlen(decimal_point) +
443*86d7f5d3SJohn Marino 		    precision, precision, (cc) ? cc : 'f');
444*86d7f5d3SJohn Marino 	} else {
445*86d7f5d3SJohn Marino 		snprintf(buf, sizeof(buf), "%%%c%d%c", pad,
446*86d7f5d3SJohn Marino 		    MAX(width1, width2), (cc) ? cc : 'g');
447*86d7f5d3SJohn Marino 	}
448*86d7f5d3SJohn Marino 
449*86d7f5d3SJohn Marino 	return(buf);
450*86d7f5d3SJohn Marino }
451