1bf855dd3SXin LI /* $NetBSD: seq.c,v 1.7 2010/05/27 08:40:19 dholland Exp $ */
21de7b4b8SPedro F. Giffuni /*-
3b61a5730SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
41de7b4b8SPedro F. Giffuni *
5208987a5SXin LI * Copyright (c) 2005 The NetBSD Foundation, Inc.
6208987a5SXin LI * All rights reserved.
7208987a5SXin LI *
8208987a5SXin LI * This code is derived from software contributed to The NetBSD Foundation
9208987a5SXin LI * by Brian Ginsbach.
10208987a5SXin LI *
11208987a5SXin LI * Redistribution and use in source and binary forms, with or without
12208987a5SXin LI * modification, are permitted provided that the following conditions
13208987a5SXin LI * are met:
14208987a5SXin LI * 1. Redistributions of source code must retain the above copyright
15208987a5SXin LI * notice, this list of conditions and the following disclaimer.
16208987a5SXin LI * 2. Redistributions in binary form must reproduce the above copyright
17208987a5SXin LI * notice, this list of conditions and the following disclaimer in the
18208987a5SXin LI * documentation and/or other materials provided with the distribution.
19208987a5SXin LI *
20208987a5SXin LI * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21208987a5SXin LI * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22208987a5SXin LI * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23208987a5SXin LI * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24208987a5SXin LI * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25208987a5SXin LI * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26208987a5SXin LI * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27208987a5SXin LI * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28208987a5SXin LI * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29208987a5SXin LI * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30208987a5SXin LI * POSSIBILITY OF SUCH DAMAGE.
31208987a5SXin LI */
32208987a5SXin LI
33208987a5SXin LI #include <sys/cdefs.h>
34208987a5SXin LI #include <ctype.h>
35208987a5SXin LI #include <err.h>
36208987a5SXin LI #include <errno.h>
37a3f2c2feSKyle Evans #include <getopt.h>
38208987a5SXin LI #include <math.h>
39208987a5SXin LI #include <locale.h>
40208987a5SXin LI #include <stdio.h>
41208987a5SXin LI #include <stdlib.h>
42208987a5SXin LI #include <string.h>
43208987a5SXin LI #include <unistd.h>
44208987a5SXin LI
45208987a5SXin LI #define ZERO '0'
46208987a5SXin LI #define SPACE ' '
47208987a5SXin LI
48208987a5SXin LI #define MAX(a, b) (((a) < (b))? (b) : (a))
49208987a5SXin LI #define ISSIGN(c) ((int)(c) == '-' || (int)(c) == '+')
50208987a5SXin LI #define ISEXP(c) ((int)(c) == 'e' || (int)(c) == 'E')
51208987a5SXin LI #define ISODIGIT(c) ((int)(c) >= '0' && (int)(c) <= '7')
52208987a5SXin LI
53208987a5SXin LI /* Globals */
54208987a5SXin LI
55cfbd8d46SEd Schouten static const char *decimal_point = "."; /* default */
56cfbd8d46SEd Schouten static char default_format[] = { "%g" }; /* default */
57208987a5SXin LI
588f8da1bcSEd Maste static const struct option long_opts[] = {
59ad4e78b5SKyle Evans {"format", required_argument, NULL, 'f'},
60ad4e78b5SKyle Evans {"separator", required_argument, NULL, 's'},
61ad4e78b5SKyle Evans {"terminator", required_argument, NULL, 't'},
62ad4e78b5SKyle Evans {"equal-width", no_argument, NULL, 'w'},
63ad4e78b5SKyle Evans {NULL, no_argument, NULL, 0}
64ad4e78b5SKyle Evans };
65ad4e78b5SKyle Evans
66208987a5SXin LI /* Prototypes */
67208987a5SXin LI
68cfbd8d46SEd Schouten static double e_atof(const char *);
69208987a5SXin LI
70cfbd8d46SEd Schouten static int decimal_places(const char *);
71cfbd8d46SEd Schouten static int numeric(const char *);
72cfbd8d46SEd Schouten static int valid_format(const char *);
73208987a5SXin LI
74cfbd8d46SEd Schouten static char *generate_format(double, double, double, int, char);
75cfbd8d46SEd Schouten static char *unescape(char *);
76208987a5SXin LI
77208987a5SXin LI /*
78208987a5SXin LI * The seq command will print out a numeric sequence from 1, the default,
79208987a5SXin LI * to a user specified upper limit by 1. The lower bound and increment
80208987a5SXin LI * maybe indicated by the user on the command line. The sequence can
81208987a5SXin LI * be either whole, the default, or decimal numbers.
82208987a5SXin LI */
83208987a5SXin LI int
main(int argc,char * argv[])84208987a5SXin LI main(int argc, char *argv[])
85208987a5SXin LI {
863049d4ccSConrad Meyer const char *sep, *term;
87208987a5SXin LI struct lconv *locale;
88e54db9a9SEd Maste char pad, *fmt, *cur_print, *last_print, *prev_print;
89e54db9a9SEd Maste double first, last, incr, prev, cur, step;
903049d4ccSConrad Meyer int c, errflg, equalize;
913049d4ccSConrad Meyer
923049d4ccSConrad Meyer pad = ZERO;
933049d4ccSConrad Meyer fmt = NULL;
943049d4ccSConrad Meyer first = 1.0;
95e54db9a9SEd Maste last = incr = prev = 0.0;
963049d4ccSConrad Meyer c = errflg = equalize = 0;
973049d4ccSConrad Meyer sep = "\n";
983049d4ccSConrad Meyer term = NULL;
99208987a5SXin LI
100208987a5SXin LI /* Determine the locale's decimal point. */
101208987a5SXin LI locale = localeconv();
102208987a5SXin LI if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
103208987a5SXin LI decimal_point = locale->decimal_point;
104208987a5SXin LI
105208987a5SXin LI /*
106208987a5SXin LI * Process options, but handle negative numbers separately
107208987a5SXin LI * least they trip up getopt(3).
108208987a5SXin LI */
109208987a5SXin LI while ((optind < argc) && !numeric(argv[optind]) &&
110a3f2c2feSKyle Evans (c = getopt_long(argc, argv, "+f:hs:t:w", long_opts, NULL)) != -1) {
111208987a5SXin LI
112208987a5SXin LI switch (c) {
113208987a5SXin LI case 'f': /* format (plan9) */
114208987a5SXin LI fmt = optarg;
115208987a5SXin LI equalize = 0;
116208987a5SXin LI break;
117208987a5SXin LI case 's': /* separator (GNU) */
118208987a5SXin LI sep = unescape(optarg);
119208987a5SXin LI break;
120208987a5SXin LI case 't': /* terminator (new) */
121208987a5SXin LI term = unescape(optarg);
122208987a5SXin LI break;
123208987a5SXin LI case 'w': /* equal width (plan9) */
124208987a5SXin LI if (!fmt)
125208987a5SXin LI if (equalize++)
126208987a5SXin LI pad = SPACE;
127208987a5SXin LI break;
128208987a5SXin LI case 'h': /* help (GNU) */
129208987a5SXin LI default:
130208987a5SXin LI errflg++;
131208987a5SXin LI break;
132208987a5SXin LI }
133208987a5SXin LI }
134208987a5SXin LI
135208987a5SXin LI argc -= optind;
136208987a5SXin LI argv += optind;
137208987a5SXin LI if (argc < 1 || argc > 3)
138208987a5SXin LI errflg++;
139208987a5SXin LI
140208987a5SXin LI if (errflg) {
141208987a5SXin LI fprintf(stderr,
142208987a5SXin LI "usage: %s [-w] [-f format] [-s string] [-t string] [first [incr]] last\n",
143208987a5SXin LI getprogname());
144208987a5SXin LI exit(1);
145208987a5SXin LI }
146208987a5SXin LI
147208987a5SXin LI last = e_atof(argv[argc - 1]);
148208987a5SXin LI
149208987a5SXin LI if (argc > 1)
150208987a5SXin LI first = e_atof(argv[0]);
151208987a5SXin LI
152208987a5SXin LI if (argc > 2) {
153208987a5SXin LI incr = e_atof(argv[1]);
154208987a5SXin LI /* Plan 9/GNU don't do zero */
155208987a5SXin LI if (incr == 0.0)
156208987a5SXin LI errx(1, "zero %screment", (first < last) ? "in" : "de");
157208987a5SXin LI }
158208987a5SXin LI
159208987a5SXin LI /* default is one for Plan 9/GNU work alike */
160208987a5SXin LI if (incr == 0.0)
161208987a5SXin LI incr = (first < last) ? 1.0 : -1.0;
162208987a5SXin LI
163208987a5SXin LI if (incr <= 0.0 && first < last)
164208987a5SXin LI errx(1, "needs positive increment");
165208987a5SXin LI
166208987a5SXin LI if (incr >= 0.0 && first > last)
167208987a5SXin LI errx(1, "needs negative decrement");
168208987a5SXin LI
169208987a5SXin LI if (fmt != NULL) {
170208987a5SXin LI if (!valid_format(fmt))
171208987a5SXin LI errx(1, "invalid format string: `%s'", fmt);
172208987a5SXin LI fmt = unescape(fmt);
173bf855dd3SXin LI if (!valid_format(fmt))
174bf855dd3SXin LI errx(1, "invalid format string");
175208987a5SXin LI /*
176208987a5SXin LI * XXX to be bug for bug compatible with Plan 9 add a
177208987a5SXin LI * newline if none found at the end of the format string.
178208987a5SXin LI */
179208987a5SXin LI } else
180208987a5SXin LI fmt = generate_format(first, incr, last, equalize, pad);
181208987a5SXin LI
1823049d4ccSConrad Meyer for (step = 1, cur = first; incr > 0 ? cur <= last : cur >= last;
1833049d4ccSConrad Meyer cur = first + incr * step++) {
184*eb4d1312SPawel Jakub Dawidek if (step > 1)
1853049d4ccSConrad Meyer fputs(sep, stdout);
186*eb4d1312SPawel Jakub Dawidek printf(fmt, cur);
187e54db9a9SEd Maste prev = cur;
1883049d4ccSConrad Meyer }
1893049d4ccSConrad Meyer
1903049d4ccSConrad Meyer /*
1913049d4ccSConrad Meyer * Did we miss the last value of the range in the loop above?
1923049d4ccSConrad Meyer *
1933049d4ccSConrad Meyer * We might have, so check if the printable version of the last
1943049d4ccSConrad Meyer * computed value ('cur') and desired 'last' value are equal. If they
195e54db9a9SEd Maste * are equal after formatting truncation, but 'cur' and 'prev' are not
196e54db9a9SEd Maste * equal, it means the exit condition of the loop held true due to a
197e54db9a9SEd Maste * rounding error and we still need to print 'last'.
1983049d4ccSConrad Meyer */
1995dae8905SEd Maste if (asprintf(&cur_print, fmt, cur) < 0 ||
2005dae8905SEd Maste asprintf(&last_print, fmt, last) < 0 ||
2015dae8905SEd Maste asprintf(&prev_print, fmt, prev) < 0) {
202e54db9a9SEd Maste err(1, "asprintf");
203e54db9a9SEd Maste }
204e54db9a9SEd Maste if (strcmp(cur_print, last_print) == 0 &&
205e54db9a9SEd Maste strcmp(cur_print, prev_print) != 0) {
206208987a5SXin LI fputs(sep, stdout);
207*eb4d1312SPawel Jakub Dawidek fputs(last_print, stdout);
208208987a5SXin LI }
2093049d4ccSConrad Meyer free(cur_print);
2103049d4ccSConrad Meyer free(last_print);
211e54db9a9SEd Maste free(prev_print);
2123049d4ccSConrad Meyer
213*eb4d1312SPawel Jakub Dawidek if (term != NULL) {
214*eb4d1312SPawel Jakub Dawidek fputs(sep, stdout);
215208987a5SXin LI fputs(term, stdout);
216*eb4d1312SPawel Jakub Dawidek }
217*eb4d1312SPawel Jakub Dawidek
218*eb4d1312SPawel Jakub Dawidek fputs("\n", stdout);
219208987a5SXin LI
220208987a5SXin LI return (0);
221208987a5SXin LI }
222208987a5SXin LI
223208987a5SXin LI /*
224208987a5SXin LI * numeric - verify that string is numeric
225208987a5SXin LI */
226cfbd8d46SEd Schouten static int
numeric(const char * s)227208987a5SXin LI numeric(const char *s)
228208987a5SXin LI {
229208987a5SXin LI int seen_decimal_pt, decimal_pt_len;
230208987a5SXin LI
231208987a5SXin LI /* skip any sign */
232208987a5SXin LI if (ISSIGN((unsigned char)*s))
233208987a5SXin LI s++;
234208987a5SXin LI
235208987a5SXin LI seen_decimal_pt = 0;
236208987a5SXin LI decimal_pt_len = strlen(decimal_point);
237208987a5SXin LI while (*s) {
238208987a5SXin LI if (!isdigit((unsigned char)*s)) {
239208987a5SXin LI if (!seen_decimal_pt &&
240208987a5SXin LI strncmp(s, decimal_point, decimal_pt_len) == 0) {
241208987a5SXin LI s += decimal_pt_len;
242208987a5SXin LI seen_decimal_pt = 1;
243208987a5SXin LI continue;
244208987a5SXin LI }
245208987a5SXin LI if (ISEXP((unsigned char)*s)) {
246208987a5SXin LI s++;
2470a167a9bSXin LI if (ISSIGN((unsigned char)*s) ||
2480a167a9bSXin LI isdigit((unsigned char)*s)) {
249208987a5SXin LI s++;
250208987a5SXin LI continue;
251208987a5SXin LI }
252208987a5SXin LI }
253208987a5SXin LI break;
254208987a5SXin LI }
255208987a5SXin LI s++;
256208987a5SXin LI }
257208987a5SXin LI return (*s == '\0');
258208987a5SXin LI }
259208987a5SXin LI
260208987a5SXin LI /*
261208987a5SXin LI * valid_format - validate user specified format string
262208987a5SXin LI */
263cfbd8d46SEd Schouten static int
valid_format(const char * fmt)264208987a5SXin LI valid_format(const char *fmt)
265208987a5SXin LI {
266bf855dd3SXin LI unsigned conversions = 0;
267208987a5SXin LI
268208987a5SXin LI while (*fmt != '\0') {
269208987a5SXin LI /* scan for conversions */
270bf855dd3SXin LI if (*fmt != '%') {
271208987a5SXin LI fmt++;
272bf855dd3SXin LI continue;
273208987a5SXin LI }
274208987a5SXin LI fmt++;
275208987a5SXin LI
276bf855dd3SXin LI /* allow %% but not things like %10% */
277208987a5SXin LI if (*fmt == '%') {
278208987a5SXin LI fmt++;
279208987a5SXin LI continue;
280bf855dd3SXin LI }
281208987a5SXin LI
282bf855dd3SXin LI /* flags */
283bf855dd3SXin LI while (*fmt != '\0' && strchr("#0- +'", *fmt)) {
284bf855dd3SXin LI fmt++;
285bf855dd3SXin LI }
286bf855dd3SXin LI
287bf855dd3SXin LI /* field width */
288bf855dd3SXin LI while (*fmt != '\0' && strchr("0123456789", *fmt)) {
289bf855dd3SXin LI fmt++;
290bf855dd3SXin LI }
291bf855dd3SXin LI
292bf855dd3SXin LI /* precision */
293bf855dd3SXin LI if (*fmt == '.') {
294bf855dd3SXin LI fmt++;
295bf855dd3SXin LI while (*fmt != '\0' && strchr("0123456789", *fmt)) {
296bf855dd3SXin LI fmt++;
297bf855dd3SXin LI }
298bf855dd3SXin LI }
299bf855dd3SXin LI
300bf855dd3SXin LI /* conversion */
301bf855dd3SXin LI switch (*fmt) {
302bf855dd3SXin LI case 'A':
303bf855dd3SXin LI case 'a':
304bf855dd3SXin LI case 'E':
305bf855dd3SXin LI case 'e':
306bf855dd3SXin LI case 'F':
307bf855dd3SXin LI case 'f':
308bf855dd3SXin LI case 'G':
309bf855dd3SXin LI case 'g':
310bf855dd3SXin LI /* floating point formats are accepted */
311bf855dd3SXin LI conversions++;
312bf855dd3SXin LI break;
313bf855dd3SXin LI default:
314bf855dd3SXin LI /* anything else is not */
315bf855dd3SXin LI return 0;
316208987a5SXin LI }
317208987a5SXin LI }
318208987a5SXin LI
319905fdc3fSConrad Meyer /* PR 236347 -- user format strings must have a conversion */
320905fdc3fSConrad Meyer return (conversions == 1);
321208987a5SXin LI }
322208987a5SXin LI
323208987a5SXin LI /*
324208987a5SXin LI * unescape - handle C escapes in a string
325208987a5SXin LI */
326cfbd8d46SEd Schouten static char *
unescape(char * orig)327208987a5SXin LI unescape(char *orig)
328208987a5SXin LI {
329208987a5SXin LI char c, *cp, *new = orig;
330208987a5SXin LI int i;
331208987a5SXin LI
332208987a5SXin LI for (cp = orig; (*orig = *cp); cp++, orig++) {
333208987a5SXin LI if (*cp != '\\')
334208987a5SXin LI continue;
335208987a5SXin LI
336208987a5SXin LI switch (*++cp) {
337208987a5SXin LI case 'a': /* alert (bell) */
338208987a5SXin LI *orig = '\a';
339208987a5SXin LI continue;
340208987a5SXin LI case 'b': /* backspace */
341208987a5SXin LI *orig = '\b';
342208987a5SXin LI continue;
343208987a5SXin LI case 'e': /* escape */
344208987a5SXin LI *orig = '\e';
345208987a5SXin LI continue;
346208987a5SXin LI case 'f': /* formfeed */
347208987a5SXin LI *orig = '\f';
348208987a5SXin LI continue;
349208987a5SXin LI case 'n': /* newline */
350208987a5SXin LI *orig = '\n';
351208987a5SXin LI continue;
352208987a5SXin LI case 'r': /* carriage return */
353208987a5SXin LI *orig = '\r';
354208987a5SXin LI continue;
355208987a5SXin LI case 't': /* horizontal tab */
356208987a5SXin LI *orig = '\t';
357208987a5SXin LI continue;
358208987a5SXin LI case 'v': /* vertical tab */
359208987a5SXin LI *orig = '\v';
360208987a5SXin LI continue;
361208987a5SXin LI case '\\': /* backslash */
362208987a5SXin LI *orig = '\\';
363208987a5SXin LI continue;
364208987a5SXin LI case '\'': /* single quote */
365208987a5SXin LI *orig = '\'';
366208987a5SXin LI continue;
367208987a5SXin LI case '\"': /* double quote */
368208987a5SXin LI *orig = '"';
369208987a5SXin LI continue;
370208987a5SXin LI case '0':
371208987a5SXin LI case '1':
372208987a5SXin LI case '2':
373208987a5SXin LI case '3': /* octal */
374208987a5SXin LI case '4':
375208987a5SXin LI case '5':
376208987a5SXin LI case '6':
377208987a5SXin LI case '7': /* number */
378208987a5SXin LI for (i = 0, c = 0;
379208987a5SXin LI ISODIGIT((unsigned char)*cp) && i < 3;
380208987a5SXin LI i++, cp++) {
381208987a5SXin LI c <<= 3;
382208987a5SXin LI c |= (*cp - '0');
383208987a5SXin LI }
384208987a5SXin LI *orig = c;
385208987a5SXin LI --cp;
386208987a5SXin LI continue;
387b1ce21c6SRebecca Cran case 'x': /* hexadecimal number */
388208987a5SXin LI cp++; /* skip 'x' */
389208987a5SXin LI for (i = 0, c = 0;
390208987a5SXin LI isxdigit((unsigned char)*cp) && i < 2;
391208987a5SXin LI i++, cp++) {
392208987a5SXin LI c <<= 4;
393208987a5SXin LI if (isdigit((unsigned char)*cp))
394208987a5SXin LI c |= (*cp - '0');
395208987a5SXin LI else
396208987a5SXin LI c |= ((toupper((unsigned char)*cp) -
397208987a5SXin LI 'A') + 10);
398208987a5SXin LI }
399208987a5SXin LI *orig = c;
400208987a5SXin LI --cp;
401208987a5SXin LI continue;
402208987a5SXin LI default:
403208987a5SXin LI --cp;
404208987a5SXin LI break;
405208987a5SXin LI }
406208987a5SXin LI }
407208987a5SXin LI
408208987a5SXin LI return (new);
409208987a5SXin LI }
410208987a5SXin LI
411208987a5SXin LI /*
412208987a5SXin LI * e_atof - convert an ASCII string to a double
413208987a5SXin LI * exit if string is not a valid double, or if converted value would
414208987a5SXin LI * cause overflow or underflow
415208987a5SXin LI */
416cfbd8d46SEd Schouten static double
e_atof(const char * num)417208987a5SXin LI e_atof(const char *num)
418208987a5SXin LI {
419208987a5SXin LI char *endp;
420208987a5SXin LI double dbl;
421208987a5SXin LI
422208987a5SXin LI errno = 0;
423208987a5SXin LI dbl = strtod(num, &endp);
424208987a5SXin LI
425208987a5SXin LI if (errno == ERANGE)
426208987a5SXin LI /* under or overflow */
427208987a5SXin LI err(2, "%s", num);
428208987a5SXin LI else if (*endp != '\0')
429208987a5SXin LI /* "junk" left in number */
430208987a5SXin LI errx(2, "invalid floating point argument: %s", num);
431208987a5SXin LI
432208987a5SXin LI /* zero shall have no sign */
433208987a5SXin LI if (dbl == -0.0)
434208987a5SXin LI dbl = 0;
435208987a5SXin LI return (dbl);
436208987a5SXin LI }
437208987a5SXin LI
438208987a5SXin LI /*
439208987a5SXin LI * decimal_places - count decimal places in a number (string)
440208987a5SXin LI */
441cfbd8d46SEd Schouten static int
decimal_places(const char * number)442208987a5SXin LI decimal_places(const char *number)
443208987a5SXin LI {
444208987a5SXin LI int places = 0;
445208987a5SXin LI char *dp;
446208987a5SXin LI
447208987a5SXin LI /* look for a decimal point */
448208987a5SXin LI if ((dp = strstr(number, decimal_point))) {
449208987a5SXin LI dp += strlen(decimal_point);
450208987a5SXin LI
451208987a5SXin LI while (isdigit((unsigned char)*dp++))
452208987a5SXin LI places++;
453208987a5SXin LI }
454208987a5SXin LI return (places);
455208987a5SXin LI }
456208987a5SXin LI
457208987a5SXin LI /*
458208987a5SXin LI * generate_format - create a format string
459208987a5SXin LI *
460b1ce21c6SRebecca Cran * XXX to be bug for bug compatible with Plan9 and GNU return "%g"
461208987a5SXin LI * when "%g" prints as "%e" (this way no width adjustments are made)
462208987a5SXin LI */
463cfbd8d46SEd Schouten static char *
generate_format(double first,double incr,double last,int equalize,char pad)464208987a5SXin LI generate_format(double first, double incr, double last, int equalize, char pad)
465208987a5SXin LI {
466208987a5SXin LI static char buf[256];
467208987a5SXin LI char cc = '\0';
468208987a5SXin LI int precision, width1, width2, places;
469208987a5SXin LI
470208987a5SXin LI if (equalize == 0)
471208987a5SXin LI return (default_format);
472208987a5SXin LI
473208987a5SXin LI /* figure out "last" value printed */
474208987a5SXin LI if (first > last)
475208987a5SXin LI last = first - incr * floor((first - last) / incr);
476208987a5SXin LI else
477208987a5SXin LI last = first + incr * floor((last - first) / incr);
478208987a5SXin LI
479208987a5SXin LI sprintf(buf, "%g", incr);
480208987a5SXin LI if (strchr(buf, 'e'))
481208987a5SXin LI cc = 'e';
482208987a5SXin LI precision = decimal_places(buf);
483208987a5SXin LI
484208987a5SXin LI width1 = sprintf(buf, "%g", first);
485208987a5SXin LI if (strchr(buf, 'e'))
486208987a5SXin LI cc = 'e';
487208987a5SXin LI if ((places = decimal_places(buf)))
488208987a5SXin LI width1 -= (places + strlen(decimal_point));
489208987a5SXin LI
490208987a5SXin LI precision = MAX(places, precision);
491208987a5SXin LI
492208987a5SXin LI width2 = sprintf(buf, "%g", last);
493208987a5SXin LI if (strchr(buf, 'e'))
494208987a5SXin LI cc = 'e';
495208987a5SXin LI if ((places = decimal_places(buf)))
496208987a5SXin LI width2 -= (places + strlen(decimal_point));
497208987a5SXin LI
498208987a5SXin LI if (precision) {
499208987a5SXin LI sprintf(buf, "%%%c%d.%d%c", pad,
500208987a5SXin LI MAX(width1, width2) + (int) strlen(decimal_point) +
501208987a5SXin LI precision, precision, (cc) ? cc : 'f');
502208987a5SXin LI } else {
503208987a5SXin LI sprintf(buf, "%%%c%d%c", pad, MAX(width1, width2),
504208987a5SXin LI (cc) ? cc : 'g');
505208987a5SXin LI }
506208987a5SXin LI
507208987a5SXin LI return (buf);
508208987a5SXin LI }
509