/* See LICENSE file for copyright and license details. */ #include <stdint.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 = ereallocarray(tablist, i + 1, sizeof(*tablist)); tablist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX)); if (i > 0 && tablist[i - 1] >= tablist[i]) eprintf("tablist must be ascending\n"); } tablist = ereallocarray(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 (efgetrune(&r, fp, file)) { 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; efputrune(&r, stdout, "<stdout>"); break; } } return 0; } static void usage(void) { eprintf("usage: %s [-i] [-t tablist] [file ...]\n", argv0); } int main(int argc, char *argv[]) { FILE *fp; int ret = 0; char *tl = "8"; 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) { expand("<stdin>", stdin); } else { for (; *argv; argc--, argv++) { if (!strcmp(*argv, "-")) { *argv = "<stdin>"; fp = stdin; } else if (!(fp = fopen(*argv, "r"))) { weprintf("fopen %s:", *argv); ret = 1; continue; } expand(*argv, fp); if (fp != stdin && fshut(fp, *argv)) ret = 1; } } ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"); return ret; }