10f66f451Sopenharmony_ci/* seq.c - Count from first to last, by increment.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2006 Rob Landley <rob@landley.net>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/seq.html
60f66f451Sopenharmony_ci
70f66f451Sopenharmony_ciUSE_SEQ(NEWTOY(seq, "<1>3?f:s:w[!fw]", TOYFLAG_USR|TOYFLAG_BIN))
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciconfig SEQ
100f66f451Sopenharmony_ci  bool "seq"
110f66f451Sopenharmony_ci  depends on TOYBOX_FLOAT
120f66f451Sopenharmony_ci  default y
130f66f451Sopenharmony_ci  help
140f66f451Sopenharmony_ci    usage: seq [-w|-f fmt_str] [-s sep_str] [first] [increment] last
150f66f451Sopenharmony_ci
160f66f451Sopenharmony_ci    Count from first to last, by increment. Omitted arguments default
170f66f451Sopenharmony_ci    to 1. Two arguments are used as first and last. Arguments can be
180f66f451Sopenharmony_ci    negative or floating point.
190f66f451Sopenharmony_ci
200f66f451Sopenharmony_ci    -f	Use fmt_str as a printf-style floating point format string
210f66f451Sopenharmony_ci    -s	Use sep_str as separator, default is a newline character
220f66f451Sopenharmony_ci    -w	Pad to equal width with leading zeroes
230f66f451Sopenharmony_ci*/
240f66f451Sopenharmony_ci
250f66f451Sopenharmony_ci#define FOR_seq
260f66f451Sopenharmony_ci#include "toys.h"
270f66f451Sopenharmony_ci
280f66f451Sopenharmony_ciGLOBALS(
290f66f451Sopenharmony_ci  char *s, *f;
300f66f451Sopenharmony_ci
310f66f451Sopenharmony_ci  int precision;
320f66f451Sopenharmony_ci)
330f66f451Sopenharmony_ci
340f66f451Sopenharmony_ci// Ensure there's one %f escape with correct attributes
350f66f451Sopenharmony_cistatic void insanitize(char *f)
360f66f451Sopenharmony_ci{
370f66f451Sopenharmony_ci  char *s = next_printf(f, 0);
380f66f451Sopenharmony_ci
390f66f451Sopenharmony_ci  if (!s) error_exit("bad -f no %%f");
400f66f451Sopenharmony_ci  if (-1 == stridx("aAeEfFgG", *s) || (s = next_printf(s, 0))) {
410f66f451Sopenharmony_ci    // The @ is a byte offset, not utf8 chars. Waiting for somebody to complain.
420f66f451Sopenharmony_ci    error_exit("bad -f '%s'@%d", f, (int)(s-f+1));
430f66f451Sopenharmony_ci  }
440f66f451Sopenharmony_ci}
450f66f451Sopenharmony_ci
460f66f451Sopenharmony_ci// Parse a numeric argument setting *prec to the precision of this argument.
470f66f451Sopenharmony_ci// This reproduces the "1.234e5" precision bug from upstream.
480f66f451Sopenharmony_cistatic double parsef(char *s)
490f66f451Sopenharmony_ci{
500f66f451Sopenharmony_ci  char *dp = strchr(s, '.');
510f66f451Sopenharmony_ci
520f66f451Sopenharmony_ci  if (dp++) TT.precision = maxof(TT.precision, strcspn(dp, "eE"));
530f66f451Sopenharmony_ci
540f66f451Sopenharmony_ci  return xstrtod(s);
550f66f451Sopenharmony_ci}
560f66f451Sopenharmony_ci
570f66f451Sopenharmony_civoid seq_main(void)
580f66f451Sopenharmony_ci{
590f66f451Sopenharmony_ci  double first = 1, increment = 1, last, dd;
600f66f451Sopenharmony_ci  int i;
610f66f451Sopenharmony_ci
620f66f451Sopenharmony_ci  if (!TT.s) TT.s = "\n";
630f66f451Sopenharmony_ci  switch (toys.optc) {
640f66f451Sopenharmony_ci    case 3: increment = parsef(toys.optargs[1]);
650f66f451Sopenharmony_ci    case 2: first = parsef(*toys.optargs);
660f66f451Sopenharmony_ci    default: last = parsef(toys.optargs[toys.optc-1]);
670f66f451Sopenharmony_ci  }
680f66f451Sopenharmony_ci
690f66f451Sopenharmony_ci  // Prepare format string with appropriate precision. Can't use %g because 1e6
700f66f451Sopenharmony_ci  if (toys.optflags & FLAG_f) insanitize(TT.f);
710f66f451Sopenharmony_ci  else sprintf(TT.f = toybuf, "%%.%df", TT.precision);
720f66f451Sopenharmony_ci
730f66f451Sopenharmony_ci  // Pad to largest width
740f66f451Sopenharmony_ci  if (toys.optflags & FLAG_w) {
750f66f451Sopenharmony_ci    int len = 0;
760f66f451Sopenharmony_ci
770f66f451Sopenharmony_ci    for (i=0; i<3; i++) {
780f66f451Sopenharmony_ci      dd = (double []){first, increment, last}[i];
790f66f451Sopenharmony_ci      len = maxof(len, snprintf(0, 0, TT.f, dd));
800f66f451Sopenharmony_ci    }
810f66f451Sopenharmony_ci    sprintf(TT.f = toybuf, "%%0%d.%df", len, TT.precision);
820f66f451Sopenharmony_ci  }
830f66f451Sopenharmony_ci
840f66f451Sopenharmony_ci  // Other implementations output nothing if increment is 0 and first > last,
850f66f451Sopenharmony_ci  // but loop forever if first < last or even first == last. We output
860f66f451Sopenharmony_ci  // nothing for all three, if you want endless output use "yes".
870f66f451Sopenharmony_ci  if (!increment) return;
880f66f451Sopenharmony_ci
890f66f451Sopenharmony_ci  i = 0;
900f66f451Sopenharmony_ci  for (;;) {
910f66f451Sopenharmony_ci    // Multiply to avoid accumulating rounding errors from increment.
920f66f451Sopenharmony_ci    dd = first+i*increment;
930f66f451Sopenharmony_ci    if ((increment<0 && dd<last) || (increment>0 && dd>last)) break;
940f66f451Sopenharmony_ci    if (i++) printf("%s", TT.s);
950f66f451Sopenharmony_ci    printf(TT.f, dd);
960f66f451Sopenharmony_ci  }
970f66f451Sopenharmony_ci
980f66f451Sopenharmony_ci  if (i) printf("\n");
990f66f451Sopenharmony_ci}
100