toybox/toys/lsb/seq.c
<<
>>
Prefs
   1/* seq.c - Count from first to last, by increment.
   2 *
   3 * Copyright 2006 Rob Landley <rob@landley.net>
   4 *
   5 * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/seq.html
   6
   7USE_SEQ(NEWTOY(seq, "<1>3?f:s:w[!fw]", TOYFLAG_USR|TOYFLAG_BIN))
   8
   9config SEQ
  10  bool "seq"
  11  depends on TOYBOX_FLOAT
  12  default y
  13  help
  14    usage: seq [-w|-f fmt_str] [-s sep_str] [first] [increment] last
  15
  16    Count from first to last, by increment. Omitted arguments default
  17    to 1. Two arguments are used as first and last. Arguments can be
  18    negative or floating point.
  19
  20    -f  Use fmt_str as a printf-style floating point format string
  21    -s  Use sep_str as separator, default is a newline character
  22    -w  Pad to equal width with leading zeroes
  23*/
  24
  25#define FOR_seq
  26#include "toys.h"
  27
  28GLOBALS(
  29  char *s, *f;
  30
  31  int precision;
  32)
  33
  34// Ensure there's one %f escape with correct attributes
  35static void insanitize(char *f)
  36{
  37  char *s = next_printf(f, 0);
  38
  39  if (!s) error_exit("bad -f no %%f");
  40  if (-1 == stridx("aAeEfFgG", *s) || (s = next_printf(s, 0))) {
  41    // The @ is a byte offset, not utf8 chars. Waiting for somebody to complain.
  42    error_exit("bad -f '%s'@%d", f, (int)(s-f+1));
  43  }
  44}
  45
  46// Parse a numeric argument setting *prec to the precision of this argument.
  47// This reproduces the "1.234e5" precision bug from upstream.
  48static double parsef(char *s)
  49{
  50  char *dp = strchr(s, '.');
  51
  52  if (dp++) TT.precision = maxof(TT.precision, strcspn(dp, "eE"));
  53
  54  return xstrtod(s);
  55}
  56
  57void seq_main(void)
  58{
  59  double first = 1, increment = 1, last, dd;
  60  int i;
  61
  62  if (!TT.s) TT.s = "\n";
  63  switch (toys.optc) {
  64    case 3: increment = parsef(toys.optargs[1]);
  65    case 2: first = parsef(*toys.optargs);
  66    default: last = parsef(toys.optargs[toys.optc-1]);
  67  }
  68
  69  // Prepare format string with appropriate precision. Can't use %g because 1e6
  70  if (toys.optflags & FLAG_f) insanitize(TT.f);
  71  else sprintf(TT.f = toybuf, "%%.%df", TT.precision);
  72
  73  // Pad to largest width
  74  if (toys.optflags & FLAG_w) {
  75    int len = 0;
  76
  77    for (i=0; i<3; i++) {
  78      dd = (double []){first, increment, last}[i];
  79      len = maxof(len, snprintf(0, 0, TT.f, dd));
  80    }
  81    sprintf(TT.f = toybuf, "%%0%d.%df", len, TT.precision);
  82  }
  83
  84  // Other implementations output nothing if increment is 0 and first > last,
  85  // but loop forever if first < last or even first == last. We output
  86  // nothing for all three, if you want endless output use "yes".
  87  if (!increment) return;
  88
  89  i = 0;
  90  for (;;) {
  91    // Multiply to avoid accumulating rounding errors from increment.
  92    dd = first+i*increment;
  93    if ((increment<0 && dd<last) || (increment>0 && dd>last)) break;
  94    if (i++) printf("%s", TT.s);
  95    printf(TT.f, dd);
  96  }
  97
  98  if (i) printf("\n");
  99}
 100