/* See LICENSE file for copyright and license details. */ #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "utf.h" #include "util.h" static int iflag = 0; static size_t *tablist = NULL; static size_t tablistlen = 0; static size_t parselist(const char *s) { size_t i; char *p, *tmp; tmp = estrdup(s); for (i = 0; (p = strsep(&tmp, " ,")); i++) { if (*p == '\0') eprintf("empty field in tablist\n"); tablist = erealloc(tablist, (i + 1) * sizeof(*tablist)); tablist[i] = estrtonum(p, 1, LLONG_MAX); if (i > 0 && tablist[i - 1] >= tablist[i]) eprintf("tablist must be ascending\n"); } tablist = erealloc(tablist, (i + 1) * sizeof(*tablist)); /* tab length = 1 for the overflowing case later in the matcher */ tablist[i] = 1; return i; } static int expand(const char *file, FILE *fp) { size_t bol = 1, col = 0, i; Rune r; while (readrune(file, fp, &r)) { switch (r) { case '\t': if (tablistlen == 1) i = 0; else for (i = 0; i < tablistlen; i++) if (col < tablist[i]) break; if (bol || !iflag) { do { col++; putchar(' '); } while (col % tablist[i]); } else { putchar('\t'); col = tablist[i]; } break; case '\b': bol = 0; if (col) col--; putchar('\b'); break; case '\n': bol = 1; col = 0; putchar('\n'); break; default: col++; if (r != ' ') bol = 0; writerune("<stdout>", stdout, &r); break; } } return 0; } static void usage(void) { eprintf("usage: %s [-i] [-t tablist] [file ...]\n", argv0); } int main(int argc, char *argv[]) { FILE *fp; char *tl = "8"; int ret = 0; ARGBEGIN { case 'i': iflag = 1; break; case 't': tl = EARGF(usage()); if (!*tl) eprintf("tablist cannot be empty\n"); break; default: usage(); } ARGEND; tablistlen = parselist(tl); if (argc == 0) expand("<stdin>", stdin); else { for (; argc > 0; argc--, argv++) { if (!(fp = fopen(argv[0], "r"))) { weprintf("fopen %s:", argv[0]); ret = 1; continue; } expand(argv[0], fp); fclose(fp); } } return ret; }