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