master xplshn/aruu / cmd / pseudo / seq.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <stdio.h>
  4#include <stdlib.h>
  5#include <string.h>
  6
  7#include "util.h"
  8
  9static int
 10digitsleft(const char *d)
 11{
 12  int         shift;
 13  const char *exp;
 14
 15  if (*d == '+')
 16    d++;
 17  exp   = strpbrk(d, "eE");
 18  shift = exp ? estrtonum(exp + 1, INT_MIN, INT_MAX) : 0;
 19
 20  return MAX(0, (int)strspn(d, "-0123456789") + shift);
 21}
 22
 23static int
 24digitsright(const char *d)
 25{
 26  int         shift, after;
 27  const char *exp;
 28
 29  exp   = strpbrk(d, "eE");
 30  shift = exp ? estrtonum(&exp[1], INT_MIN, INT_MAX) : 0;
 31  after = (d = strchr(d, '.')) ? strspn(&d[1], "0123456789") : 0;
 32
 33  return MAX(0, after - shift);
 34}
 35
 36static int
 37validfmt(const char *fmt)
 38{
 39  int occur = 0;
 40
 41literal:
 42  while (*fmt)
 43    if (*fmt++ == '%')
 44      goto format;
 45  return occur == 1;
 46
 47format:
 48  if (*fmt == '%') {
 49    fmt++;
 50    goto literal;
 51  }
 52  fmt += strspn(fmt, "-+#0 '");
 53  fmt += strspn(fmt, "0123456789");
 54  if (*fmt == '.') {
 55    fmt++;
 56    fmt += strspn(fmt, "0123456789");
 57  }
 58  if (*fmt == 'L')
 59    fmt++;
 60
 61  switch (*fmt) {
 62    // ?man -f: force the operation
 63    case 'f':
 64    case 'F':
 65    case 'g':
 66    case 'G':
 67    case 'e':
 68    case 'E':
 69    case 'a':
 70    case 'A':
 71      occur++;
 72      goto literal;
 73    default:
 74      return 0;
 75  }
 76}
 77
 78static void
 79usage(void)
 80{
 81  eprintf(
 82      "usage: %s [-f fmt] [-s sep] [-w] "
 83      "[startnum [step]] endnum\n",
 84      argv0
 85  );
 86}
 87
 88// ?man seq: print sequence of numbers
 89// ?man print a sequence of numbers from start to end
 90int
 91main(int argc, char *argv[])
 92{
 93  double      start, step, end, out, dir;
 94  int         wflag = 0, left, right;
 95  char       *tmp, ftmp[BUFSIZ], *fmt = ftmp;
 96  const char *starts = "1", *steps = "1", *ends = "1", *sep = "\n";
 97
 98  ARGBEGIN
 99  {
100    // ?man -f:str: force the operation
101    case 'f':
102      if (!validfmt(tmp = EARGF(usage())))
103        eprintf("%s: invalid format\n", tmp);
104      fmt = tmp;
105      break;
106    // ?man -s:str: silent mode or print summary
107    case 's':
108      sep = EARGF(usage());
109      break;
110    // ?man -w: wait for completion
111    case 'w':
112      wflag = 1;
113      break;
114    default:
115      usage();
116  }
117  ARGEND
118
119  switch (argc) {
120    case 3:
121      steps   = argv[1];
122      argv[1] = argv[2];
123      /* fallthrough */
124    case 2:
125      starts = argv[0];
126      argv++;
127      /* fallthrough */
128    case 1:
129      ends = argv[0];
130      break;
131    default:
132      usage();
133  }
134  start = estrtod(starts);
135  step  = estrtod(steps);
136  end   = estrtod(ends);
137
138  dir = (step > 0) ? 1.0 : -1.0;
139  if (step == 0 || start * dir > end * dir)
140    return 1;
141
142  if (fmt == ftmp) {
143    right = MAX(digitsright(starts), MAX(digitsright(ends), digitsright(steps)));
144
145    if (wflag) {
146      left = MAX(digitsleft(starts), digitsleft(ends));
147
148      snprintf(ftmp, sizeof ftmp, "%%0%d.%df", right + left + (right != 0), right);
149    } else
150      snprintf(ftmp, sizeof ftmp, "%%.%df", right);
151  }
152  for (out = start; out * dir <= end * dir; out += step) {
153    if (out != start)
154      fputs(sep, stdout);
155    printf(fmt, out);
156  }
157  putchar('\n');
158
159  return fshut(stdout, "<stdout>");
160}