seq.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. USE_SEQ(NEWTOY(seq, "<1>3?f:s:w[!fw]", TOYFLAG_USR|TOYFLAG_BIN))
  7. config SEQ
  8. bool "seq"
  9. depends on TOYBOX_FLOAT
  10. default y
  11. help
  12. usage: seq [-w|-f fmt_str] [-s sep_str] [first] [increment] last
  13. Count from first to last, by increment. Omitted arguments default
  14. to 1. Two arguments are used as first and last. Arguments can be
  15. negative or floating point.
  16. -f Use fmt_str as a printf-style floating point format string
  17. -s Use sep_str as separator, default is a newline character
  18. -w Pad to equal width with leading zeroes
  19. */
  20. #define FOR_seq
  21. #include "toys.h"
  22. GLOBALS(
  23. char *s, *f;
  24. int precision, buflen;
  25. )
  26. // Ensure there's one %f escape with correct attributes
  27. static void insanitize(char *f)
  28. {
  29. char *s = next_printf(f, 0);
  30. if (!s) error_exit("bad -f no %%f");
  31. if (-1 == stridx("aAeEfFgG", *s) || (s = next_printf(s, 0)))
  32. error_exit("bad -f '%s'@%d", f, (int)(s-f+1));
  33. }
  34. // Parse a numeric argument setting *prec to the precision of this argument.
  35. // This reproduces the "1.234e5" precision bug from upstream.
  36. static double parsef(char *s)
  37. {
  38. char *dp = strchr(s, '.');
  39. if (dp++) TT.precision = maxof(TT.precision, strcspn(dp, "eE"));
  40. return xstrtod(s);
  41. }
  42. // fast integer conversion to decimal string
  43. // TODO move to lib?
  44. static char *itoa(char *s, int i)
  45. {
  46. char buf[16], *ff = buf;
  47. unsigned n = i;
  48. if (i<0) {
  49. *s++ = '-';
  50. n = -i;
  51. }
  52. do *ff++ = '0'+n%10; while ((n /= 10));
  53. do *s++ = *--ff; while (ff>buf);
  54. *s++ = '\n';
  55. return s;
  56. }
  57. static char *flush_toybuf(char *ss)
  58. {
  59. if (ss-toybuf<TT.buflen) return ss;
  60. xwrite(1, toybuf, ss-toybuf);
  61. return toybuf;
  62. }
  63. void seq_main(void)
  64. {
  65. char fbuf[32], *ss;
  66. double first = 1, increment = 1, last, dd;
  67. int ii, inc = 1, len, slen;
  68. // parse arguments
  69. if (!TT.s) TT.s = "\n";
  70. switch (toys.optc) {
  71. case 3: increment = parsef(toys.optargs[1]);
  72. case 2: first = parsef(*toys.optargs);
  73. default: last = parsef(toys.optargs[toys.optc-1]);
  74. }
  75. // measure arguments
  76. if (FLAG(f)) insanitize(TT.f);
  77. for (ii = len = 0; ii<3; ii++) {
  78. dd = (double []){first, increment, last}[ii];
  79. len = maxof(len, snprintf(0, 0, "%.*f", TT.precision, fabs(dd)));
  80. if (ii == 2) dd += increment;
  81. slen = dd;
  82. if (dd != slen) inc = 0;
  83. }
  84. if (!FLAG(f)) sprintf(TT.f = fbuf, "%%0%d.%df", len, TT.precision);
  85. TT.buflen = sizeof(toybuf) - 32 - len - TT.precision - strlen(TT.s);
  86. if (TT.buflen<0) error_exit("bad -s");
  87. // fast path: when everything fits in an int with no flags.
  88. if (!toys.optflags && inc) {
  89. ii = first;
  90. len = last;
  91. inc = increment;
  92. ss = toybuf;
  93. if (inc>0) for (; ii<=len; ii += inc)
  94. ss = flush_toybuf(itoa(ss, ii));
  95. else if (inc<0) for (; ii>=len; ii += inc)
  96. ss = flush_toybuf(itoa(ss, ii));
  97. if (ss != toybuf) xwrite(1, toybuf, ss-toybuf);
  98. return;
  99. }
  100. // Other implementations output nothing if increment is 0 and first > last,
  101. // but loop forever if first < last or even first == last. We output
  102. // nothing for all three, if you want endless output use "yes".
  103. if (!increment) return;
  104. // Slow path, floating point and fancy sprintf() patterns
  105. for (ii = 0, ss = toybuf;; ii++) {
  106. // Multiply to avoid accumulating rounding errors from increment.
  107. dd = first+ii*increment;
  108. if ((increment<0 && dd<last) || (increment>0 && dd>last)) break;
  109. if (ii) ss = flush_toybuf(stpcpy(ss, TT.s));
  110. ss += sprintf(ss, TT.f, dd);
  111. }
  112. *ss++ = '\n';
  113. xwrite(1, toybuf, ss-toybuf);
  114. }