fold.c (2616B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <ctype.h> 3 #include <stdint.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #include "text.h" 9 #include "util.h" 10 #include "utf.h" 11 12 static int bflag = 0; 13 static int sflag = 0; 14 static size_t width = 80; 15 16 static void 17 foldline(struct line *l, const char *fname) { 18 size_t i, col, last, spacesect, len; 19 Rune r; 20 int runelen; 21 22 for (i = 0, last = 0, col = 0, spacesect = 0; i < l->len; i += runelen) { 23 if (col >= width && ((l->data[i] != '\r' && l->data[i] != '\b') || bflag)) { 24 if (bflag && col > width) 25 i -= runelen; /* never split a character */ 26 len = ((sflag && spacesect) ? spacesect : i) - last; 27 if (fwrite(l->data + last, 1, len, stdout) != len) 28 eprintf("fwrite <stdout>:"); 29 if (l->data[i] != '\n') 30 putchar('\n'); 31 if (sflag && spacesect) 32 i = spacesect; 33 last = i; 34 col = 0; 35 spacesect = 0; 36 } 37 runelen = charntorune(&r, l->data + i, l->len - i); 38 if (!runelen || r == Runeerror) 39 eprintf("charntorune: %s: invalid utf\n", fname); 40 if (sflag && isblankrune(r)) 41 spacesect = i + runelen; 42 if (!bflag && iscntrl(l->data[i])) { 43 switch(l->data[i]) { 44 case '\b': 45 col -= (col > 0); 46 break; 47 case '\r': 48 col = 0; 49 break; 50 case '\t': 51 col += (8 - (col % 8)); 52 if (col >= width) 53 i--; 54 break; 55 } 56 } else { 57 col += bflag ? runelen : 1; 58 } 59 } 60 if (l->len - last) 61 fwrite(l->data + last, 1, l->len - last, stdout); 62 } 63 64 static void 65 fold(FILE *fp, const char *fname) 66 { 67 static struct line line; 68 static size_t size = 0; 69 ssize_t len; 70 71 while ((len = getline(&line.data, &size, fp)) > 0) { 72 line.len = len; 73 foldline(&line, fname); 74 } 75 if (ferror(fp)) 76 eprintf("getline %s:", fname); 77 } 78 79 static void 80 usage(void) 81 { 82 eprintf("usage: %s [-bs] [-w num | -num] [FILE ...]\n", argv0); 83 } 84 85 int 86 main(int argc, char *argv[]) 87 { 88 FILE *fp; 89 int ret = 0; 90 91 ARGBEGIN { 92 case 'b': 93 bflag = 1; 94 break; 95 case 's': 96 sflag = 1; 97 break; 98 case 'w': 99 width = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX)); 100 break; 101 ARGNUM: 102 if (!(width = ARGNUMF())) 103 eprintf("illegal width value, too small\n"); 104 break; 105 default: 106 usage(); 107 } ARGEND 108 109 if (!argc) { 110 fold(stdin, "<stdin>"); 111 } else { 112 for (; *argv; argc--, argv++) { 113 if (!strcmp(*argv, "-")) { 114 *argv = "<stdin>"; 115 fp = stdin; 116 } else if (!(fp = fopen(*argv, "r"))) { 117 weprintf("fopen %s:", *argv); 118 ret = 1; 119 continue; 120 } 121 fold(fp, *argv); 122 if (fp != stdin && fshut(fp, *argv)) 123 ret = 1; 124 } 125 } 126 127 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"); 128 129 return ret; 130 }