master xplshn/aruu / cmd / posix / expand.c
  1/* See LICENSE file for copyright and license details. */
  2
  3
  4#include <stdint.h>
  5#include <stdlib.h>
  6#include <string.h>
  7
  8#include "utf.h"
  9#include "util.h"
 10
 11static int     iflag      = 0;
 12static size_t *tablist    = NULL;
 13static size_t  tablistlen = 0;
 14
 15static size_t
 16parselist(const char *s)
 17{
 18	size_t i;
 19	char  *p, *tmp;
 20
 21	tmp = estrdup(s);
 22	for (i = 0; (p = strsep(&tmp, " ,")); i++) {
 23		if (*p == '\0')
 24			eprintf("empty field in tablist\n");
 25		tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
 26		tablist[i] = estrtonum(p, 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
 27		if (i > 0 && tablist[i - 1] >= tablist[i])
 28			eprintf("tablist must be ascending\n");
 29	}
 30	tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
 31	/* tab length = 1 for the overflowing case later in the matcher */
 32	tablist[i] = 1;
 33
 34	return i;
 35}
 36
 37static int
 38expand(const char *file, FILE *fp)
 39{
 40	size_t bol = 1, col = 0, i;
 41	Rune r;
 42
 43	while (efgetrune(&r, fp, file)) {
 44		switch (r) {
 45		case '\t':
 46			if (tablistlen == 1)
 47				i = 0;
 48			else 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	// ?man -i: interactive mode or prompt for confirmation
102	case 'i':
103		iflag = 1;
104		break;
105	// ?man -t:str: sort or specify timestamp
106	case 't':
107		tl = EARGF(usage());
108		if (!*tl)
109			eprintf("tablist cannot be empty\n");
110		break;
111	default:
112		usage();
113	} ARGEND
114
115	tablistlen = parselist(tl);
116
117	if (!argc) {
118		expand("<stdin>", stdin);
119	} else {
120		for (; *argv; argc--, argv++) {
121			if (!strcmp(*argv, "-")) {
122				*argv = "<stdin>";
123				fp = stdin;
124			} else if (!(fp = fopen(*argv, "r"))) {
125				weprintf("fopen %s:", *argv);
126				ret = 1;
127				continue;
128			}
129			expand(*argv, fp);
130			if (fp != stdin && fshut(fp, *argv))
131				ret = 1;
132		}
133	}
134
135	ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
136
137	return ret;
138}