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, buflen;
  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    error_exit("bad -f '%s'@%d", f, (int)(s-f+1));
  42}
  43
  44// Parse a numeric argument setting *prec to the precision of this argument.
  45// This reproduces the "1.234e5" precision bug from upstream.
  46static double parsef(char *s)
  47{
  48  char *dp = strchr(s, '.');
  49
  50  if (dp++) TT.precision = maxof(TT.precision, strcspn(dp, "eE"));
  51
  52  return xstrtod(s);
  53}
  54
  55// fast integer conversion to decimal string
  56// TODO move to lib?
  57static char *itoa(char *s, int i)
  58{
  59  char buf[16], *ff = buf;
  60  unsigned n = i;
  61
  62  if (i<0) {
  63    *s++ = '-';
  64    n = -i;
  65  }
  66  do *ff++ = '0'+n%10; while ((n /= 10));
  67  do *s++ = *--ff; while (ff>buf);
  68  *s++ = '\n';
  69
  70  return s;
  71}
  72
  73static char *flush_toybuf(char *ss)
  74{
  75  if (ss-toybuf<TT.buflen) return ss;
  76  xwrite(1, toybuf, ss-toybuf); 
  77
  78  return toybuf;
  79}
  80
  81void seq_main(void)
  82{
  83  char fbuf[32], *ss;
  84  double first = 1, increment = 1, last, dd;
  85  int ii, inc = 1, len, slen;
  86
  87  // parse arguments
  88  if (!TT.s) TT.s = "\n";
  89  switch (toys.optc) {
  90    case 3: increment = parsef(toys.optargs[1]);
  91    case 2: first = parsef(*toys.optargs);
  92    default: last = parsef(toys.optargs[toys.optc-1]);
  93  }
  94
  95  // measure arguments
  96  if (FLAG(f)) insanitize(TT.f);
  97  for (ii = len = 0; ii<3; ii++) {
  98    dd = (double []){first, increment, last}[ii];
  99    len = maxof(len, snprintf(0, 0, "%.*f", TT.precision, fabs(dd)));
 100    if (ii == 2) dd += increment;
 101    slen = dd;
 102    if (dd != slen) inc = 0;
 103  }
 104  if (!FLAG(f)) sprintf(TT.f = fbuf, "%%0%d.%df", len, TT.precision);
 105  TT.buflen = sizeof(toybuf) - 32 - len - TT.precision - strlen(TT.s);
 106  if (TT.buflen<0) error_exit("bad -s");
 107
 108  // fast path: when everything fits in an int with no flags.
 109  if (!toys.optflags && inc) {
 110    ii = first;
 111    len = last;
 112    inc = increment;
 113    ss = toybuf;
 114    if (inc>0) for (; ii<=len; ii += inc)
 115      ss = flush_toybuf(itoa(ss, ii));
 116    else if (inc<0) for (; ii>=len; ii += inc)
 117      ss = flush_toybuf(itoa(ss, ii));
 118    if (ss != toybuf) xwrite(1, toybuf, ss-toybuf);
 119
 120    return;
 121  }
 122
 123  // Other implementations output nothing if increment is 0 and first > last,
 124  // but loop forever if first < last or even first == last. We output
 125  // nothing for all three, if you want endless output use "yes".
 126  if (!increment) return;
 127
 128  // Slow path, floating point and fancy sprintf() patterns
 129  for (ii = 0, ss = toybuf;; ii++) {
 130    // Multiply to avoid accumulating rounding errors from increment.
 131    dd = first+ii*increment;
 132    if ((increment<0 && dd<last) || (increment>0 && dd>last)) break;
 133    if (ii) ss = flush_toybuf(stpcpy(ss, TT.s));
 134    ss += sprintf(ss, TT.f, dd);
 135  }
 136  *ss++ = '\n';
 137  xwrite(1, toybuf, ss-toybuf);
 138}
 139