master xplshn/aruu / cmd / posix / expand.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <stdint.h>
  4#include <stdlib.h>
  5#include <string.h>
  6
  7#include "utf.h"
  8#include "util.h"
  9
 10static int     iflag      = 0;
 11static size_t *tablist    = NULL;
 12static size_t  tablistlen = 0;
 13
 14static size_t
 15parselist(const char *s)
 16{
 17  size_t i;
 18  char  *p, *tmp;
 19
 20  tmp = estrdup(s);
 21  for (i = 0; (p = strsep(&tmp, " ,")); i++) {
 22    if (*p == '\0')
 23      eprintf("empty field in tablist\n");
 24    tablist    = ereallocarray(tablist, i + 1, sizeof(*tablist));
 25    tablist[i] = estrtonum(p, 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
 26    if (i > 0 && tablist[i - 1] >= tablist[i])
 27      eprintf("tablist must be ascending\n");
 28  }
 29  tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
 30  /* tab length = 1 for the overflowing case later in the matcher */
 31  tablist[i] = 1;
 32
 33  return i;
 34}
 35
 36static int
 37expand(const char *file, FILE *fp)
 38{
 39  size_t bol = 1, col = 0, i;
 40  Rune   r;
 41
 42  while (efgetrune(&r, fp, file)) {
 43    switch (r) {
 44      case '\t':
 45        if (tablistlen == 1)
 46          i = 0;
 47        else
 48          for (i = 0; i < tablistlen; i++)
 49            if (col < tablist[i])
 50              break;
 51        if (bol || !iflag) {
 52          do {
 53            col++;
 54            putchar(' ');
 55          } while (col % tablist[i]);
 56        } else {
 57          putchar('\t');
 58          col = tablist[i];
 59        }
 60        break;
 61      case '\b':
 62        bol = 0;
 63        if (col)
 64          col--;
 65        putchar('\b');
 66        break;
 67      case '\n':
 68        bol = 1;
 69        col = 0;
 70        putchar('\n');
 71        break;
 72      default:
 73        col++;
 74        if (r != ' ')
 75          bol = 0;
 76        efputrune(&r, stdout, "<stdout>");
 77        break;
 78    }
 79  }
 80
 81  return 0;
 82}
 83
 84static void
 85usage(void)
 86{
 87  eprintf("usage: %s [-i] [-t tablist] [file ...]\n", argv0);
 88}
 89
 90// ?man expand: convert tabs to spaces
 91// ?man arguments: file ...
 92// ?man convert tab characters to space characters
 93int
 94main(int argc, char *argv[])
 95{
 96  FILE *fp;
 97  int   ret = 0;
 98  char *tl  = "8";
 99
100  ARGBEGIN
101  {
102    // ?man -i: interactive mode or prompt for confirmation
103    case 'i':
104      iflag = 1;
105      break;
106    // ?man -t:str: sort or specify timestamp
107    case 't':
108      tl = EARGF(usage());
109      if (!*tl)
110        eprintf("tablist cannot be empty\n");
111      break;
112    default:
113      usage();
114  }
115  ARGEND
116
117  tablistlen = parselist(tl);
118
119  if (!argc) {
120    expand("<stdin>", stdin);
121  } else {
122    for (; *argv; argc--, argv++) {
123      if (!strcmp(*argv, "-")) {
124        *argv = "<stdin>";
125        fp    = stdin;
126      } else if (!(fp = fopen(*argv, "r"))) {
127        weprintf("fopen %s:", *argv);
128        ret = 1;
129        continue;
130      }
131      expand(*argv, fp);
132      if (fp != stdin && fshut(fp, *argv))
133        ret = 1;
134    }
135  }
136
137  ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
138
139  return ret;
140}