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 __FBSDID("$FreeBSD$"); 35208987a5SXin LI 36208987a5SXin LI #include <ctype.h> 37208987a5SXin LI #include <err.h> 38208987a5SXin LI #include <errno.h> 39a3f2c2feSKyle Evans #include <getopt.h> 40208987a5SXin LI #include <math.h> 41208987a5SXin LI #include <locale.h> 42208987a5SXin LI #include <stdio.h> 43208987a5SXin LI #include <stdlib.h> 44208987a5SXin LI #include <string.h> 45208987a5SXin LI #include <unistd.h> 46208987a5SXin LI 47208987a5SXin LI #define ZERO '0' 48208987a5SXin LI #define SPACE ' ' 49208987a5SXin LI 50208987a5SXin LI #define MAX(a, b) (((a) < (b))? (b) : (a)) 51208987a5SXin LI #define ISSIGN(c) ((int)(c) == '-' || (int)(c) == '+') 52208987a5SXin LI #define ISEXP(c) ((int)(c) == 'e' || (int)(c) == 'E') 53208987a5SXin LI #define ISODIGIT(c) ((int)(c) >= '0' && (int)(c) <= '7') 54208987a5SXin LI 55208987a5SXin LI /* Globals */ 56208987a5SXin LI 57cfbd8d46SEd Schouten static const char *decimal_point = "."; /* default */ 58cfbd8d46SEd Schouten static char default_format[] = { "%g" }; /* default */ 59208987a5SXin LI 60ad4e78b5SKyle Evans static const struct option long_opts[] = 61ad4e78b5SKyle Evans { 62ad4e78b5SKyle Evans {"format", required_argument, NULL, 'f'}, 63ad4e78b5SKyle Evans {"separator", required_argument, NULL, 's'}, 64ad4e78b5SKyle Evans {"terminator", required_argument, NULL, 't'}, 65ad4e78b5SKyle Evans {"equal-width", no_argument, NULL, 'w'}, 66ad4e78b5SKyle Evans {NULL, no_argument, NULL, 0} 67ad4e78b5SKyle Evans }; 68ad4e78b5SKyle Evans 69208987a5SXin LI /* Prototypes */ 70208987a5SXin LI 71cfbd8d46SEd Schouten static double e_atof(const char *); 72208987a5SXin LI 73cfbd8d46SEd Schouten static int decimal_places(const char *); 74cfbd8d46SEd Schouten static int numeric(const char *); 75cfbd8d46SEd Schouten static int valid_format(const char *); 76208987a5SXin LI 77cfbd8d46SEd Schouten static char *generate_format(double, double, double, int, char); 78cfbd8d46SEd Schouten static char *unescape(char *); 79208987a5SXin LI 80208987a5SXin LI /* 81208987a5SXin LI * The seq command will print out a numeric sequence from 1, the default, 82208987a5SXin LI * to a user specified upper limit by 1. The lower bound and increment 83208987a5SXin LI * maybe indicated by the user on the command line. The sequence can 84208987a5SXin LI * be either whole, the default, or decimal numbers. 85208987a5SXin LI */ 86208987a5SXin LI int 87208987a5SXin LI main(int argc, char *argv[]) 88208987a5SXin LI { 893049d4ccSConrad Meyer const char *sep, *term; 90208987a5SXin LI struct lconv *locale; 91*e54db9a9SEd Maste char pad, *fmt, *cur_print, *last_print, *prev_print; 92*e54db9a9SEd Maste double first, last, incr, prev, cur, step; 933049d4ccSConrad Meyer int c, errflg, equalize; 943049d4ccSConrad Meyer 953049d4ccSConrad Meyer pad = ZERO; 963049d4ccSConrad Meyer fmt = NULL; 973049d4ccSConrad Meyer first = 1.0; 98*e54db9a9SEd Maste last = incr = prev = 0.0; 993049d4ccSConrad Meyer c = errflg = equalize = 0; 1003049d4ccSConrad Meyer sep = "\n"; 1013049d4ccSConrad Meyer term = NULL; 102208987a5SXin LI 103208987a5SXin LI /* Determine the locale's decimal point. */ 104208987a5SXin LI locale = localeconv(); 105208987a5SXin LI if (locale && locale->decimal_point && locale->decimal_point[0] != '\0') 106208987a5SXin LI decimal_point = locale->decimal_point; 107208987a5SXin LI 108208987a5SXin LI /* 109208987a5SXin LI * Process options, but handle negative numbers separately 110208987a5SXin LI * least they trip up getopt(3). 111208987a5SXin LI */ 112208987a5SXin LI while ((optind < argc) && !numeric(argv[optind]) && 113a3f2c2feSKyle Evans (c = getopt_long(argc, argv, "+f:hs:t:w", long_opts, NULL)) != -1) { 114208987a5SXin LI 115208987a5SXin LI switch (c) { 116208987a5SXin LI case 'f': /* format (plan9) */ 117208987a5SXin LI fmt = optarg; 118208987a5SXin LI equalize = 0; 119208987a5SXin LI break; 120208987a5SXin LI case 's': /* separator (GNU) */ 121208987a5SXin LI sep = unescape(optarg); 122208987a5SXin LI break; 123208987a5SXin LI case 't': /* terminator (new) */ 124208987a5SXin LI term = unescape(optarg); 125208987a5SXin LI break; 126208987a5SXin LI case 'w': /* equal width (plan9) */ 127208987a5SXin LI if (!fmt) 128208987a5SXin LI if (equalize++) 129208987a5SXin LI pad = SPACE; 130208987a5SXin LI break; 131208987a5SXin LI case 'h': /* help (GNU) */ 132208987a5SXin LI default: 133208987a5SXin LI errflg++; 134208987a5SXin LI break; 135208987a5SXin LI } 136208987a5SXin LI } 137208987a5SXin LI 138208987a5SXin LI argc -= optind; 139208987a5SXin LI argv += optind; 140208987a5SXin LI if (argc < 1 || argc > 3) 141208987a5SXin LI errflg++; 142208987a5SXin LI 143208987a5SXin LI if (errflg) { 144208987a5SXin LI fprintf(stderr, 145208987a5SXin LI "usage: %s [-w] [-f format] [-s string] [-t string] [first [incr]] last\n", 146208987a5SXin LI getprogname()); 147208987a5SXin LI exit(1); 148208987a5SXin LI } 149208987a5SXin LI 150208987a5SXin LI last = e_atof(argv[argc - 1]); 151208987a5SXin LI 152208987a5SXin LI if (argc > 1) 153208987a5SXin LI first = e_atof(argv[0]); 154208987a5SXin LI 155208987a5SXin LI if (argc > 2) { 156208987a5SXin LI incr = e_atof(argv[1]); 157208987a5SXin LI /* Plan 9/GNU don't do zero */ 158208987a5SXin LI if (incr == 0.0) 159208987a5SXin LI errx(1, "zero %screment", (first < last)? "in" : "de"); 160208987a5SXin LI } 161208987a5SXin LI 162208987a5SXin LI /* default is one for Plan 9/GNU work alike */ 163208987a5SXin LI if (incr == 0.0) 164208987a5SXin LI incr = (first < last) ? 1.0 : -1.0; 165208987a5SXin LI 166208987a5SXin LI if (incr <= 0.0 && first < last) 167208987a5SXin LI errx(1, "needs positive increment"); 168208987a5SXin LI 169208987a5SXin LI if (incr >= 0.0 && first > last) 170208987a5SXin LI errx(1, "needs negative decrement"); 171208987a5SXin LI 172208987a5SXin LI if (fmt != NULL) { 173208987a5SXin LI if (!valid_format(fmt)) 174208987a5SXin LI errx(1, "invalid format string: `%s'", fmt); 175208987a5SXin LI fmt = unescape(fmt); 176bf855dd3SXin LI if (!valid_format(fmt)) 177bf855dd3SXin LI errx(1, "invalid format string"); 178208987a5SXin LI /* 179208987a5SXin LI * XXX to be bug for bug compatible with Plan 9 add a 180208987a5SXin LI * newline if none found at the end of the format string. 181208987a5SXin LI */ 182208987a5SXin LI } else 183208987a5SXin LI fmt = generate_format(first, incr, last, equalize, pad); 184208987a5SXin LI 1853049d4ccSConrad Meyer for (step = 1, cur = first; incr > 0 ? cur <= last : cur >= last; 1863049d4ccSConrad Meyer cur = first + incr * step++) { 1873049d4ccSConrad Meyer printf(fmt, cur); 1883049d4ccSConrad Meyer fputs(sep, stdout); 189*e54db9a9SEd Maste prev = cur; 1903049d4ccSConrad Meyer } 1913049d4ccSConrad Meyer 1923049d4ccSConrad Meyer /* 1933049d4ccSConrad Meyer * Did we miss the last value of the range in the loop above? 1943049d4ccSConrad Meyer * 1953049d4ccSConrad Meyer * We might have, so check if the printable version of the last 1963049d4ccSConrad Meyer * computed value ('cur') and desired 'last' value are equal. If they 197*e54db9a9SEd Maste * are equal after formatting truncation, but 'cur' and 'prev' are not 198*e54db9a9SEd Maste * equal, it means the exit condition of the loop held true due to a 199*e54db9a9SEd Maste * rounding error and we still need to print 'last'. 2003049d4ccSConrad Meyer */ 20194c4f663SMariusz Zaborski if (asprintf(&cur_print, fmt, cur) < 0) { 20294c4f663SMariusz Zaborski err(1, "asprintf"); 20394c4f663SMariusz Zaborski } 20494c4f663SMariusz Zaborski if (asprintf(&last_print, fmt, last) < 0) { 20594c4f663SMariusz Zaborski err(1, "asprintf"); 20694c4f663SMariusz Zaborski } 207*e54db9a9SEd Maste if (asprintf(&prev_print, fmt, prev) < 0) { 208*e54db9a9SEd Maste err(1, "asprintf"); 209*e54db9a9SEd Maste } 210*e54db9a9SEd Maste if (strcmp(cur_print, last_print) == 0 && 211*e54db9a9SEd Maste strcmp(cur_print, prev_print) != 0) { 2123049d4ccSConrad Meyer fputs(last_print, stdout); 213208987a5SXin LI fputs(sep, stdout); 214208987a5SXin LI } 2153049d4ccSConrad Meyer free(cur_print); 2163049d4ccSConrad Meyer free(last_print); 217*e54db9a9SEd Maste free(prev_print); 2183049d4ccSConrad Meyer 219208987a5SXin LI if (term != NULL) 220208987a5SXin LI fputs(term, stdout); 221208987a5SXin LI 222208987a5SXin LI return (0); 223208987a5SXin LI } 224208987a5SXin LI 225208987a5SXin LI /* 226208987a5SXin LI * numeric - verify that string is numeric 227208987a5SXin LI */ 228cfbd8d46SEd Schouten static int 229208987a5SXin LI numeric(const char *s) 230208987a5SXin LI { 231208987a5SXin LI int seen_decimal_pt, decimal_pt_len; 232208987a5SXin LI 233208987a5SXin LI /* skip any sign */ 234208987a5SXin LI if (ISSIGN((unsigned char)*s)) 235208987a5SXin LI s++; 236208987a5SXin LI 237208987a5SXin LI seen_decimal_pt = 0; 238208987a5SXin LI decimal_pt_len = strlen(decimal_point); 239208987a5SXin LI while (*s) { 240208987a5SXin LI if (!isdigit((unsigned char)*s)) { 241208987a5SXin LI if (!seen_decimal_pt && 242208987a5SXin LI strncmp(s, decimal_point, decimal_pt_len) == 0) { 243208987a5SXin LI s += decimal_pt_len; 244208987a5SXin LI seen_decimal_pt = 1; 245208987a5SXin LI continue; 246208987a5SXin LI } 247208987a5SXin LI if (ISEXP((unsigned char)*s)) { 248208987a5SXin LI s++; 2490a167a9bSXin LI if (ISSIGN((unsigned char)*s) || 2500a167a9bSXin LI isdigit((unsigned char)*s)) { 251208987a5SXin LI s++; 252208987a5SXin LI continue; 253208987a5SXin LI } 254208987a5SXin LI } 255208987a5SXin LI break; 256208987a5SXin LI } 257208987a5SXin LI s++; 258208987a5SXin LI } 259208987a5SXin LI return (*s == '\0'); 260208987a5SXin LI } 261208987a5SXin LI 262208987a5SXin LI /* 263208987a5SXin LI * valid_format - validate user specified format string 264208987a5SXin LI */ 265cfbd8d46SEd Schouten static int 266208987a5SXin LI valid_format(const char *fmt) 267208987a5SXin LI { 268bf855dd3SXin LI unsigned conversions = 0; 269208987a5SXin LI 270208987a5SXin LI while (*fmt != '\0') { 271208987a5SXin LI /* scan for conversions */ 272bf855dd3SXin LI if (*fmt != '%') { 273208987a5SXin LI fmt++; 274bf855dd3SXin LI continue; 275208987a5SXin LI } 276208987a5SXin LI fmt++; 277208987a5SXin LI 278bf855dd3SXin LI /* allow %% but not things like %10% */ 279208987a5SXin LI if (*fmt == '%') { 280208987a5SXin LI fmt++; 281208987a5SXin LI continue; 282bf855dd3SXin LI } 283208987a5SXin LI 284bf855dd3SXin LI /* flags */ 285bf855dd3SXin LI while (*fmt != '\0' && strchr("#0- +'", *fmt)) { 286bf855dd3SXin LI fmt++; 287bf855dd3SXin LI } 288bf855dd3SXin LI 289bf855dd3SXin LI /* field width */ 290bf855dd3SXin LI while (*fmt != '\0' && strchr("0123456789", *fmt)) { 291bf855dd3SXin LI fmt++; 292bf855dd3SXin LI } 293bf855dd3SXin LI 294bf855dd3SXin LI /* precision */ 295bf855dd3SXin LI if (*fmt == '.') { 296bf855dd3SXin LI fmt++; 297bf855dd3SXin LI while (*fmt != '\0' && strchr("0123456789", *fmt)) { 298bf855dd3SXin LI fmt++; 299bf855dd3SXin LI } 300bf855dd3SXin LI } 301bf855dd3SXin LI 302bf855dd3SXin LI /* conversion */ 303bf855dd3SXin LI switch (*fmt) { 304bf855dd3SXin LI case 'A': 305bf855dd3SXin LI case 'a': 306bf855dd3SXin LI case 'E': 307bf855dd3SXin LI case 'e': 308bf855dd3SXin LI case 'F': 309bf855dd3SXin LI case 'f': 310bf855dd3SXin LI case 'G': 311bf855dd3SXin LI case 'g': 312bf855dd3SXin LI /* floating point formats are accepted */ 313bf855dd3SXin LI conversions++; 314bf855dd3SXin LI break; 315bf855dd3SXin LI default: 316bf855dd3SXin LI /* anything else is not */ 317bf855dd3SXin LI return 0; 318208987a5SXin LI } 319208987a5SXin LI } 320208987a5SXin LI 321905fdc3fSConrad Meyer /* PR 236347 -- user format strings must have a conversion */ 322905fdc3fSConrad Meyer return (conversions == 1); 323208987a5SXin LI } 324208987a5SXin LI 325208987a5SXin LI /* 326208987a5SXin LI * unescape - handle C escapes in a string 327208987a5SXin LI */ 328cfbd8d46SEd Schouten static char * 329208987a5SXin LI unescape(char *orig) 330208987a5SXin LI { 331208987a5SXin LI char c, *cp, *new = orig; 332208987a5SXin LI int i; 333208987a5SXin LI 334208987a5SXin LI for (cp = orig; (*orig = *cp); cp++, orig++) { 335208987a5SXin LI if (*cp != '\\') 336208987a5SXin LI continue; 337208987a5SXin LI 338208987a5SXin LI switch (*++cp) { 339208987a5SXin LI case 'a': /* alert (bell) */ 340208987a5SXin LI *orig = '\a'; 341208987a5SXin LI continue; 342208987a5SXin LI case 'b': /* backspace */ 343208987a5SXin LI *orig = '\b'; 344208987a5SXin LI continue; 345208987a5SXin LI case 'e': /* escape */ 346208987a5SXin LI *orig = '\e'; 347208987a5SXin LI continue; 348208987a5SXin LI case 'f': /* formfeed */ 349208987a5SXin LI *orig = '\f'; 350208987a5SXin LI continue; 351208987a5SXin LI case 'n': /* newline */ 352208987a5SXin LI *orig = '\n'; 353208987a5SXin LI continue; 354208987a5SXin LI case 'r': /* carriage return */ 355208987a5SXin LI *orig = '\r'; 356208987a5SXin LI continue; 357208987a5SXin LI case 't': /* horizontal tab */ 358208987a5SXin LI *orig = '\t'; 359208987a5SXin LI continue; 360208987a5SXin LI case 'v': /* vertical tab */ 361208987a5SXin LI *orig = '\v'; 362208987a5SXin LI continue; 363208987a5SXin LI case '\\': /* backslash */ 364208987a5SXin LI *orig = '\\'; 365208987a5SXin LI continue; 366208987a5SXin LI case '\'': /* single quote */ 367208987a5SXin LI *orig = '\''; 368208987a5SXin LI continue; 369208987a5SXin LI case '\"': /* double quote */ 370208987a5SXin LI *orig = '"'; 371208987a5SXin LI continue; 372208987a5SXin LI case '0': 373208987a5SXin LI case '1': 374208987a5SXin LI case '2': 375208987a5SXin LI case '3': /* octal */ 376208987a5SXin LI case '4': 377208987a5SXin LI case '5': 378208987a5SXin LI case '6': 379208987a5SXin LI case '7': /* number */ 380208987a5SXin LI for (i = 0, c = 0; 381208987a5SXin LI ISODIGIT((unsigned char)*cp) && i < 3; 382208987a5SXin LI i++, cp++) { 383208987a5SXin LI c <<= 3; 384208987a5SXin LI c |= (*cp - '0'); 385208987a5SXin LI } 386208987a5SXin LI *orig = c; 387208987a5SXin LI --cp; 388208987a5SXin LI continue; 389b1ce21c6SRebecca Cran case 'x': /* hexadecimal number */ 390208987a5SXin LI cp++; /* skip 'x' */ 391208987a5SXin LI for (i = 0, c = 0; 392208987a5SXin LI isxdigit((unsigned char)*cp) && i < 2; 393208987a5SXin LI i++, cp++) { 394208987a5SXin LI c <<= 4; 395208987a5SXin LI if (isdigit((unsigned char)*cp)) 396208987a5SXin LI c |= (*cp - '0'); 397208987a5SXin LI else 398208987a5SXin LI c |= ((toupper((unsigned char)*cp) - 399208987a5SXin LI 'A') + 10); 400208987a5SXin LI } 401208987a5SXin LI *orig = c; 402208987a5SXin LI --cp; 403208987a5SXin LI continue; 404208987a5SXin LI default: 405208987a5SXin LI --cp; 406208987a5SXin LI break; 407208987a5SXin LI } 408208987a5SXin LI } 409208987a5SXin LI 410208987a5SXin LI return (new); 411208987a5SXin LI } 412208987a5SXin LI 413208987a5SXin LI /* 414208987a5SXin LI * e_atof - convert an ASCII string to a double 415208987a5SXin LI * exit if string is not a valid double, or if converted value would 416208987a5SXin LI * cause overflow or underflow 417208987a5SXin LI */ 418cfbd8d46SEd Schouten static double 419208987a5SXin LI e_atof(const char *num) 420208987a5SXin LI { 421208987a5SXin LI char *endp; 422208987a5SXin LI double dbl; 423208987a5SXin LI 424208987a5SXin LI errno = 0; 425208987a5SXin LI dbl = strtod(num, &endp); 426208987a5SXin LI 427208987a5SXin LI if (errno == ERANGE) 428208987a5SXin LI /* under or overflow */ 429208987a5SXin LI err(2, "%s", num); 430208987a5SXin LI else if (*endp != '\0') 431208987a5SXin LI /* "junk" left in number */ 432208987a5SXin LI errx(2, "invalid floating point argument: %s", num); 433208987a5SXin LI 434208987a5SXin LI /* zero shall have no sign */ 435208987a5SXin LI if (dbl == -0.0) 436208987a5SXin LI dbl = 0; 437208987a5SXin LI return (dbl); 438208987a5SXin LI } 439208987a5SXin LI 440208987a5SXin LI /* 441208987a5SXin LI * decimal_places - count decimal places in a number (string) 442208987a5SXin LI */ 443cfbd8d46SEd Schouten static int 444208987a5SXin LI decimal_places(const char *number) 445208987a5SXin LI { 446208987a5SXin LI int places = 0; 447208987a5SXin LI char *dp; 448208987a5SXin LI 449208987a5SXin LI /* look for a decimal point */ 450208987a5SXin LI if ((dp = strstr(number, decimal_point))) { 451208987a5SXin LI dp += strlen(decimal_point); 452208987a5SXin LI 453208987a5SXin LI while (isdigit((unsigned char)*dp++)) 454208987a5SXin LI places++; 455208987a5SXin LI } 456208987a5SXin LI return (places); 457208987a5SXin LI } 458208987a5SXin LI 459208987a5SXin LI /* 460208987a5SXin LI * generate_format - create a format string 461208987a5SXin LI * 462b1ce21c6SRebecca Cran * XXX to be bug for bug compatible with Plan9 and GNU return "%g" 463208987a5SXin LI * when "%g" prints as "%e" (this way no width adjustments are made) 464208987a5SXin LI */ 465cfbd8d46SEd Schouten static char * 466208987a5SXin LI generate_format(double first, double incr, double last, int equalize, char pad) 467208987a5SXin LI { 468208987a5SXin LI static char buf[256]; 469208987a5SXin LI char cc = '\0'; 470208987a5SXin LI int precision, width1, width2, places; 471208987a5SXin LI 472208987a5SXin LI if (equalize == 0) 473208987a5SXin LI return (default_format); 474208987a5SXin LI 475208987a5SXin LI /* figure out "last" value printed */ 476208987a5SXin LI if (first > last) 477208987a5SXin LI last = first - incr * floor((first - last) / incr); 478208987a5SXin LI else 479208987a5SXin LI last = first + incr * floor((last - first) / incr); 480208987a5SXin LI 481208987a5SXin LI sprintf(buf, "%g", incr); 482208987a5SXin LI if (strchr(buf, 'e')) 483208987a5SXin LI cc = 'e'; 484208987a5SXin LI precision = decimal_places(buf); 485208987a5SXin LI 486208987a5SXin LI width1 = sprintf(buf, "%g", first); 487208987a5SXin LI if (strchr(buf, 'e')) 488208987a5SXin LI cc = 'e'; 489208987a5SXin LI if ((places = decimal_places(buf))) 490208987a5SXin LI width1 -= (places + strlen(decimal_point)); 491208987a5SXin LI 492208987a5SXin LI precision = MAX(places, precision); 493208987a5SXin LI 494208987a5SXin LI width2 = sprintf(buf, "%g", last); 495208987a5SXin LI if (strchr(buf, 'e')) 496208987a5SXin LI cc = 'e'; 497208987a5SXin LI if ((places = decimal_places(buf))) 498208987a5SXin LI width2 -= (places + strlen(decimal_point)); 499208987a5SXin LI 500208987a5SXin LI if (precision) { 501208987a5SXin LI sprintf(buf, "%%%c%d.%d%c", pad, 502208987a5SXin LI MAX(width1, width2) + (int) strlen(decimal_point) + 503208987a5SXin LI precision, precision, (cc) ? cc : 'f'); 504208987a5SXin LI } else { 505208987a5SXin LI sprintf(buf, "%%%c%d%c", pad, MAX(width1, width2), 506208987a5SXin LI (cc) ? cc : 'g'); 507208987a5SXin LI } 508208987a5SXin LI 509208987a5SXin LI return (buf); 510208987a5SXin LI } 511