sbase

suckless unix tools
git clone git://git.2f30.org/sbase
Log | Files | Refs | README | LICENSE

unexpand.c (3318B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <stdint.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 
      6 #include "utf.h"
      7 #include "util.h"
      8 
      9 static int     aflag      = 0;
     10 static size_t *tablist    = NULL;
     11 static size_t  tablistlen = 8;
     12 
     13 static size_t
     14 parselist(const char *s)
     15 {
     16 	size_t i;
     17 	char  *p, *tmp;
     18 
     19 	tmp = estrdup(s);
     20 	for (i = 0; (p = strsep(&tmp, " ,")); i++) {
     21 		if (*p == '\0')
     22 			eprintf("empty field in tablist\n");
     23 		tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
     24 		tablist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
     25 		if (i > 0 && tablist[i - 1] >= tablist[i])
     26 			eprintf("tablist must be ascending\n");
     27 	}
     28 	tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
     29 
     30 	return i;
     31 }
     32 
     33 static void
     34 unexpandspan(size_t last, size_t col)
     35 {
     36 	size_t off, i, j;
     37 	Rune r;
     38 
     39 	if (tablistlen == 1) {
     40 		i = 0;
     41 		off = last % tablist[i];
     42 
     43 		if ((col - last) + off >= tablist[i] && last < col)
     44 			last -= off;
     45 
     46 		r = '\t';
     47 		for (; last + tablist[i] <= col; last += tablist[i])
     48 			efputrune(&r, stdout, "<stdout>");
     49 		r = ' ';
     50 		for (; last < col; last++)
     51 			efputrune(&r, stdout, "<stdout>");
     52 	} else {
     53 		for (i = 0; i < tablistlen; i++)
     54 			if (col < tablist[i])
     55 				break;
     56 		for (j = 0; j < tablistlen; j++)
     57 			if (last < tablist[j])
     58 				break;
     59 		r = '\t';
     60 		for (; j < i; j++) {
     61 			efputrune(&r, stdout, "<stdout>");
     62 			last = tablist[j];
     63 		}
     64 		r = ' ';
     65 		for (; last < col; last++)
     66 			efputrune(&r, stdout, "<stdout>");
     67 	}
     68 }
     69 
     70 static void
     71 unexpand(const char *file, FILE *fp)
     72 {
     73 	Rune r;
     74 	size_t last = 0, col = 0, i;
     75 	int bol = 1;
     76 
     77 	while (efgetrune(&r, fp, file)) {
     78 		switch (r) {
     79 		case ' ':
     80 			if (!bol && !aflag)
     81 				last++;
     82 			col++;
     83 			break;
     84 		case '\t':
     85 			if (tablistlen == 1) {
     86 				if (!bol && !aflag)
     87 					last += tablist[0] - col % tablist[0];
     88 				col += tablist[0] - col % tablist[0];
     89 			} else {
     90 				for (i = 0; i < tablistlen; i++)
     91 					if (col < tablist[i])
     92 						break;
     93 				if (!bol && !aflag)
     94 					last = tablist[i];
     95 				col = tablist[i];
     96 			}
     97 			break;
     98 		case '\b':
     99 			if (bol || aflag)
    100 				unexpandspan(last, col);
    101 			col -= (col > 0);
    102 			last = col;
    103 			bol = 0;
    104 			break;
    105 		case '\n':
    106 			if (bol || aflag)
    107 				unexpandspan(last, col);
    108 			last = col = 0;
    109 			bol = 1;
    110 			break;
    111 		default:
    112 			if (bol || aflag)
    113 				unexpandspan(last, col);
    114 			last = ++col;
    115 			bol = 0;
    116 			break;
    117 		}
    118 		if ((r != ' ' && r != '\t') || (!aflag && !bol))
    119 			efputrune(&r, stdout, "<stdout>");
    120 	}
    121 	if (last < col && (bol || aflag))
    122 		unexpandspan(last, col);
    123 }
    124 
    125 static void
    126 usage(void)
    127 {
    128 	eprintf("usage: %s [-a] [-t tablist] [file ...]\n", argv0);
    129 }
    130 
    131 int
    132 main(int argc, char *argv[])
    133 {
    134 	FILE *fp;
    135 	int ret = 0;
    136 	char *tl = "8";
    137 
    138 	ARGBEGIN {
    139 	case 't':
    140 		tl = EARGF(usage());
    141 		if (!*tl)
    142 			eprintf("tablist cannot be empty\n");
    143 		/* Fallthrough: -t implies -a */
    144 	case 'a':
    145 		aflag = 1;
    146 		break;
    147 	default:
    148 		usage();
    149 	} ARGEND
    150 
    151 	tablistlen = parselist(tl);
    152 
    153 	if (!argc) {
    154 		unexpand("<stdin>", stdin);
    155 	} else {
    156 		for (; *argv; argc--, argv++) {
    157 			if (!strcmp(*argv, "-")) {
    158 				*argv = "<stdin>";
    159 				fp = stdin;
    160 			} else if (!(fp = fopen(*argv, "r"))) {
    161 				weprintf("fopen %s:", *argv);
    162 				ret = 1;
    163 				continue;
    164 			}
    165 			unexpand(*argv, fp);
    166 			if (fp != stdin && fshut(fp, *argv))
    167 				ret = 1;
    168 		}
    169 	}
    170 
    171 	ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
    172 
    173 	return ret;
    174 }