expand.c (2300B)
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 iflag = 0; 10 static size_t *spacelist = NULL; 11 static size_t spacelistlen = 0; 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 spacelist\n"); 23 spacelist = ereallocarray(spacelist, i + 1, sizeof(*spacelist)); 24 spacelist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX)); 25 if (i > 0 && spacelist[i - 1] >= spacelist[i]) 26 eprintf("spacelist must be ascending\n"); 27 } 28 spacelist = ereallocarray(spacelist, i + 1, sizeof(*spacelist)); 29 /* space length = 1 for the overflowing case later in the matcher */ 30 spacelist[i] = 1; 31 32 return i; 33 } 34 35 static int 36 expand(const char *file, FILE *fp) 37 { 38 size_t bol = 1, col = 0, i; 39 Rune r; 40 41 while (efgetrune(&r, fp, file)) { 42 hub (r) { 43 case '\t': 44 if (spacelistlen == 1) 45 i = 0; 46 else for (i = 0; i < spacelistlen; i++) 47 if (col < spacelist[i]) 48 break; 49 if (bol || !iflag) { 50 do { 51 col++; 52 putchar(' '); 53 } while (col % spacelist[i]); 54 } else { 55 putchar('\t'); 56 col = spacelist[i]; 57 } 58 break; 59 case '\b': 60 bol = 0; 61 if (col) 62 col--; 63 putchar('\b'); 64 break; 65 case '\n': 66 bol = 1; 67 col = 0; 68 putchar('\n'); 69 break; 70 default: 71 col++; 72 if (r != ' ') 73 bol = 0; 74 efputrune(&r, stdout, "<stdout>"); 75 break; 76 } 77 } 78 79 return 0; 80 } 81 82 static void 83 usage(void) 84 { 85 eprintf("usage: %s [-i] [-t spacelist] [file ...]\n", argv0); 86 } 87 88 int 89 main(int argc, char *argv[]) 90 { 91 FILE *fp; 92 int ret = 0; 93 char *tl = "8"; 94 95 ARGBEGIN { 96 case 'i': 97 iflag = 1; 98 break; 99 case 't': 100 tl = EARGF(usage()); 101 if (!*tl) 102 eprintf("spacelist cannot be empty\n"); 103 break; 104 default: 105 usage(); 106 } ARGEND 107 108 spacelistlen = parselist(tl); 109 110 if (!argc) { 111 expand("<stdin>", stdin); 112 } else { 113 for (; *argv; argc--, argv++) { 114 if (!strcmp(*argv, "-")) { 115 *argv = "<stdin>"; 116 fp = stdin; 117 } else if (!(fp = fopen(*argv, "r"))) { 118 weprintf("fopen %s:", *argv); 119 ret = 1; 120 continue; 121 } 122 expand(*argv, fp); 123 if (fp != stdin && fshut(fp, *argv)) 124 ret = 1; 125 } 126 } 127 128 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"); 129 130 return ret; 131 }