a5ae899a48
Interface as proposed by cls, but internally rewritten after a few considerations. The code is much shorter and to the point, aligning itself with other standard functions. It should also be much faster, which is not bad.
167 lines
3.2 KiB
C
167 lines
3.2 KiB
C
/* See LICENSE file for copyright and license details. */
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "utf.h"
|
|
#include "util.h"
|
|
|
|
static int aflag = 0;
|
|
static size_t *tablist = NULL;
|
|
static int 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 = erealloc(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 = erealloc(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])
|
|
writerune("<stdout>", stdout, &r);
|
|
r = ' ';
|
|
for (; last < col; last++)
|
|
writerune("<stdout>", stdout, &r);
|
|
} 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++) {
|
|
writerune("<stdout>", stdout, &r);
|
|
last = tablist[j];
|
|
}
|
|
r = ' ';
|
|
for (; last < col; last++)
|
|
writerune("<stdout>", stdout, &r);
|
|
}
|
|
}
|
|
|
|
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))
|
|
writerune("<stdout>", stdout, &r);
|
|
}
|
|
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 == 0)
|
|
unexpand("<stdin>", stdin);
|
|
else {
|
|
for (; argc > 0; argc--, argv++) {
|
|
if (!(fp = fopen(argv[0], "r"))) {
|
|
weprintf("fopen %s:", argv[0]);
|
|
ret = 1;
|
|
continue;
|
|
}
|
|
unexpand(argv[0], fp);
|
|
fclose(fp);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|