fmt.c (3745B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <ctype.h> 5 6 /* 7 * block up paragraphs, possibly with indentation 8 */ 9 10 int extraindent = 0; /* how many spaces to indent all lines */ 11 int indent = 0; /* current value of indent, before extra indent */ 12 int length = 70; /* how many columns per output line */ 13 int join = 1; /* can lines be joined? */ 14 int maxtab = 8; 15 16 Biobuf bin; 17 Biobuf bout; 18 19 typedef struct Word Word; 20 struct Word 21 { 22 Word *next; 23 24 int indent; 25 int length; 26 char bol; 27 char text[]; 28 }; 29 30 void fmt(void); 31 32 void 33 usage(void) 34 { 35 fprint(2, "usage: %s [-j] [-i indent] [-l length] [file...]\n", argv0); 36 exits("usage"); 37 } 38 39 void 40 main(int argc, char **argv) 41 { 42 int i, f; 43 char *s, *err; 44 45 ARGBEGIN{ 46 case 'i': 47 extraindent = atoi(EARGF(usage())); 48 break; 49 case 'j': 50 join = 0; 51 break; 52 case 'w': 53 case 'l': 54 length = atoi(EARGF(usage())); 55 break; 56 default: 57 usage(); 58 }ARGEND 59 60 if(length <= indent){ 61 fprint(2, "%s: line length<=indentation\n", argv0); 62 exits("length"); 63 } 64 65 s=getenv("tabstop"); 66 if(s!=nil && atoi(s)>0) 67 maxtab=atoi(s); 68 err = nil; 69 Binit(&bout, 1, OWRITE); 70 if(argc <= 0){ 71 Binit(&bin, 0, OREAD); 72 fmt(); 73 }else{ 74 for(i=0; i<argc; i++){ 75 f = open(argv[i], OREAD); 76 if(f < 0){ 77 fprint(2, "%s: can't open %s: %r\n", argv0, argv[i]); 78 err = "open"; 79 }else{ 80 Binit(&bin, f, OREAD); 81 fmt(); 82 Bterm(&bin); 83 if(i != argc-1) 84 Bputc(&bout, '\n'); 85 } 86 } 87 } 88 exits(err); 89 } 90 91 int 92 indentof(char *s) 93 { 94 int ind; 95 96 ind = 0; 97 for(; *s != '\0'; s++) 98 switch(*s){ 99 default: 100 return ind; 101 case ' ': 102 ind++; 103 break; 104 case '\t': 105 ind += maxtab; 106 ind -= ind%maxtab; 107 break; 108 } 109 110 /* plain white space doesn't change the indent */ 111 return indent; 112 } 113 114 Word* 115 newword(char *s, int n, int ind, int bol) 116 { 117 Word *w; 118 119 w = malloc(sizeof(Word) + n+1); 120 w->next = nil; 121 w->indent = ind; 122 w->bol = bol; 123 memmove(w->text, s, n); 124 w->text[n] = 0; 125 w->length = utflen(w->text); 126 return w; 127 } 128 129 Word* 130 getword(void) 131 { 132 static Word *head, *tail; 133 char *line, *s; 134 Word *w; 135 136 w = head; 137 if(w != nil){ 138 head = w->next; 139 return w; 140 } 141 line = Brdstr(&bin, '\n', 1); 142 if(line == nil) 143 return nil; 144 tail = nil; 145 indent = indentof(line); 146 for(;;){ 147 while(*line == ' ' || *line == '\t') 148 line++; 149 if(*line == '\0'){ 150 if(head == nil) 151 return newword("", 0, -1, 1); 152 break; 153 } 154 /* how long is this word? */ 155 for(s=line++; *line != '\0'; line++) 156 if(*line==' ' || *line=='\t') 157 break; 158 w = newword(s, line-s, indent, head==nil); 159 if(head == nil) 160 head = w; 161 else 162 tail->next = w; 163 tail = w; 164 } 165 w = head; 166 head = w->next; 167 return w; 168 } 169 170 void 171 printindent(int w) 172 { 173 while(w >= maxtab){ 174 Bputc(&bout, '\t'); 175 w -= maxtab; 176 } 177 while(w > 0){ 178 Bputc(&bout, ' '); 179 w--; 180 } 181 } 182 183 /* give extra space if word ends with period, etc. */ 184 int 185 nspaceafter(char *s) 186 { 187 int n; 188 189 n = strlen(s); 190 if(n < 2) 191 return 1; 192 if(isupper(s[0]) && n < 4) 193 return 1; 194 if(strchr(".!?", s[n-1]) != nil) 195 return 2; 196 return 1; 197 } 198 199 void 200 fmt(void) 201 { 202 Word *w, *o; 203 int col, nsp; 204 205 w = getword(); 206 while(w != nil){ 207 if(w->indent == -1){ 208 Bputc(&bout, '\n'); 209 free(w); 210 w = getword(); 211 if(w == nil) 212 break; 213 } 214 col = w->indent; 215 printindent(extraindent+col); 216 /* emit words until overflow; always emit at least one word */ 217 for(;;){ 218 Bprint(&bout, "%s", w->text); 219 col += w->length; 220 o = w; 221 w = getword(); 222 if(w == nil) 223 break; 224 if(w->indent != o->indent) 225 break; /* indent change */ 226 nsp = nspaceafter(o->text); 227 if(col+nsp+w->length > length) 228 break; /* fold line */ 229 if(!join && w->bol) 230 break; 231 while(--nsp >= 0){ 232 Bputc(&bout, ' '); /* emit space; another word will follow */ 233 col++; 234 } 235 free(o); 236 } 237 free(o); 238 Bputc(&bout, '\n'); 239 } 240 }