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 }