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