/* See LICENSE file for copyright and license details. */ #include #include #include #include "utf.h" #include "util.h" static int aflag = 0; static size_t *tablist = NULL; static size_t tablistlen = 8; 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)); return i; } static void unexpandspan(size_t last, size_t col) { size_t off, i, j; Rune r; if (tablistlen == 1) { i = 0; off = last % tablist[i]; if ((col - last) + off >= tablist[i] && last < col) last -= off; r = '\t'; for (; last + tablist[i] <= col; last += tablist[i]) efputrune(&r, stdout, ""); r = ' '; for (; last < col; last++) efputrune(&r, stdout, ""); } else { for (i = 0; i < tablistlen; i++) if (col < tablist[i]) break; for (j = 0; j < tablistlen; j++) if (last < tablist[j]) break; r = '\t'; for (; j < i; j++) { efputrune(&r, stdout, ""); last = tablist[j]; } r = ' '; for (; last < col; last++) efputrune(&r, stdout, ""); } } static void unexpand(const char *file, FILE *fp) { Rune r; size_t last = 0, col = 0, i; int bol = 1; while (efgetrune(&r, fp, file)) { switch (r) { case ' ': if (!bol && !aflag) last++; col++; break; case '\t': if (tablistlen == 1) { if (!bol && !aflag) last += tablist[0] - col % tablist[0]; col += tablist[0] - col % tablist[0]; } else { for (i = 0; i < tablistlen; i++) if (col < tablist[i]) break; if (!bol && !aflag) last = tablist[i]; col = tablist[i]; } break; case '\b': if (bol || aflag) unexpandspan(last, col); col -= (col > 0); last = col; bol = 0; break; case '\n': if (bol || aflag) unexpandspan(last, col); last = col = 0; bol = 1; break; default: if (bol || aflag) unexpandspan(last, col); last = ++col; bol = 0; break; } if ((r != ' ' && r != '\t') || (!aflag && !bol)) efputrune(&r, stdout, ""); } if (last < col && (bol || aflag)) unexpandspan(last, col); } static void usage(void) { eprintf("usage: %s [-a] [-t tablist] [file ...]\n", argv0); } int main(int argc, char *argv[]) { FILE *fp; int ret = 0; char *tl = "8"; ARGBEGIN { case 't': tl = EARGF(usage()); if (!*tl) eprintf("tablist cannot be empty\n"); /* Fallthrough: -t implies -a */ case 'a': aflag = 1; break; default: usage(); } ARGEND tablistlen = parselist(tl); if (!argc) { unexpand("", stdin); } else { for (; *argv; argc--, argv++) { if (!strcmp(*argv, "-")) { *argv = ""; fp = stdin; } else if (!(fp = fopen(*argv, "r"))) { weprintf("fopen %s:", *argv); ret = 1; continue; } unexpand(*argv, fp); if (fp != stdin && fshut(fp, *argv)) ret = 1; } } ret |= fshut(stdin, "") | fshut(stdout, ""); return ret; }