| unexpand.c - sbase - suckless unix tools | |
| git clone git://git.suckless.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 % tabli… | |
| 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 } |