1 # 2 3 #include <stdio.h> 4 #include <ctype.h> 5 6 /* 7 * fmt -- format the concatenation of input files or standard input 8 * onto standard output. Designed for use with Mail ~| 9 * 10 * Syntax: fmt [ -width ] [ name ... ] 11 * Author: Kurt Shoens (UCB) 12/7/78 12 */ 13 14 static char *SccsId = "@(#)fmt.c 2.1 07/01/81"; 15 16 #define NOSTR ((char *) 0) /* Null string pointer for lint */ 17 18 int pfx; /* Current leading blank count */ 19 int lineno; /* Current input line */ 20 int mark; /* Last place we saw a head line */ 21 int width = 72; /* Width that we will not exceed */ 22 23 char *calloc(); /* for lint . . . */ 24 char *headnames[] = {"To", "Subject", "Cc", 0}; 25 26 /* 27 * Drive the whole formatter by managing input files. Also, 28 * cause initialization of the output stuff and flush it out 29 * at the end. 30 */ 31 32 main(argc, argv) 33 char **argv; 34 { 35 register FILE *fi; 36 register int errs = 0; 37 char sobuf[BUFSIZ]; 38 register char *cp; 39 int nofile; 40 41 setout(); 42 lineno = 1; 43 mark = -10; 44 setbuf(stdout, sobuf); 45 if (argc < 2) { 46 single: 47 fmt(stdin); 48 oflush(); 49 exit(0); 50 } 51 nofile = 1; 52 while (--argc) { 53 cp = *++argv; 54 if (*cp == '-') { 55 width = atoi(cp+1); 56 if (width <= 0 || width >= BUFSIZ-2) { 57 fprintf(stderr, "fmt: bad width: %d\n", width); 58 exit(1); 59 } 60 continue; 61 } 62 nofile = 0; 63 if ((fi = fopen(cp, "r")) == NULL) { 64 perror(cp); 65 errs++; 66 continue; 67 } 68 fmt(fi); 69 fclose(fi); 70 } 71 if (nofile) 72 goto single; 73 oflush(); 74 exit(errs); 75 } 76 77 /* 78 * Read up characters from the passed input file, forming lines, 79 * doing ^H processing, expanding tabs, stripping trailing blanks, 80 * and sending each line down for analysis. 81 */ 82 83 fmt(fi) 84 FILE *fi; 85 { 86 char linebuf[BUFSIZ], canonb[BUFSIZ]; 87 register char *cp, *cp2; 88 register int c, col; 89 90 c = getc(fi); 91 while (c != EOF) { 92 93 /* 94 * Collect a line, doing ^H processing. 95 * Leave tabs for now. 96 */ 97 98 cp = linebuf; 99 while (c != '\n' && c != EOF && cp-linebuf < BUFSIZ-1) { 100 if (c == '\b') { 101 if (cp > linebuf) 102 cp--; 103 c = getc(fi); 104 continue; 105 } 106 if ((c < ' ' || c >= 0177) && c != '\t') { 107 c = getc(fi); 108 continue; 109 } 110 *cp++ = c; 111 c = getc(fi); 112 } 113 *cp = '\0'; 114 115 /* 116 * Toss anything remaining on the input line. 117 */ 118 119 while (c != '\n' && c != EOF) 120 c = getc(fi); 121 122 /* 123 * Expand tabs on the way to canonb. 124 */ 125 126 col = 0; 127 cp = linebuf; 128 cp2 = canonb; 129 while (c = *cp++) { 130 if (c != '\t') { 131 col++; 132 if (cp2-canonb < BUFSIZ-1) 133 *cp2++ = c; 134 continue; 135 } 136 do { 137 if (cp2-canonb < BUFSIZ-1) 138 *cp2++ = ' '; 139 col++; 140 } while ((col & 07) != 0); 141 } 142 143 /* 144 * Swipe trailing blanks from the line. 145 */ 146 147 for (cp2--; cp2 >= canonb && *cp2 == ' '; cp2--) 148 ; 149 *++cp2 = '\0'; 150 prefix(canonb); 151 if (c != EOF) 152 c = getc(fi); 153 } 154 } 155 156 /* 157 * Take a line devoid of tabs and other garbage and determine its 158 * blank prefix. If the indent changes, call for a linebreak. 159 * If the input line is blank, echo the blank line on the output. 160 * Finally, if the line minus the prefix is a mail header, try to keep 161 * it on a line by itself. 162 */ 163 164 prefix(line) 165 char line[]; 166 { 167 register char *cp, **hp; 168 register int np, h; 169 170 if (strlen(line) == 0) { 171 oflush(); 172 putchar('\n'); 173 return; 174 } 175 for (cp = line; *cp == ' '; cp++) 176 ; 177 np = cp - line; 178 179 /* 180 * The following horrible expression attempts to avoid linebreaks 181 * when the indent changes due to a paragraph. 182 */ 183 184 if (np != pfx && (np > pfx || abs(pfx-np) > 8)) 185 oflush(); 186 if (h = ishead(cp)) 187 oflush(), mark = lineno; 188 if (lineno - mark < 3 && lineno - mark > 0) 189 for (hp = &headnames[0]; *hp != (char *) 0; hp++) 190 if (ispref(*hp, cp)) { 191 h = 1; 192 oflush(); 193 break; 194 } 195 if (!h && (h = (*cp == '.'))) 196 oflush(); 197 pfx = np; 198 split(cp); 199 if (h) 200 oflush(); 201 lineno++; 202 } 203 204 /* 205 * Split up the passed line into output "words" which are 206 * maximal strings of non-blanks with the blank separation 207 * attached at the end. Pass these words along to the output 208 * line packer. 209 */ 210 211 split(line) 212 char line[]; 213 { 214 register char *cp, *cp2; 215 char word[BUFSIZ]; 216 217 cp = line; 218 while (*cp) { 219 cp2 = word; 220 221 /* 222 * Collect a 'word,' allowing it to contain escaped 223 * white space. 224 */ 225 226 while (*cp && *cp != ' ') { 227 if (*cp == '\\' && isspace(cp[1])) 228 *cp2++ = *cp++; 229 *cp2++ = *cp++; 230 } 231 232 /* 233 * Guarantee a space at end of line. 234 * Two spaces after end of sentence punctuation. 235 */ 236 237 if (*cp == '\0') { 238 *cp2++ = ' '; 239 if (any(cp[-1], ".:!?")) 240 *cp2++ = ' '; 241 } 242 while (*cp == ' ') 243 *cp2++ = *cp++; 244 *cp2 = '\0'; 245 pack(word); 246 } 247 } 248 249 /* 250 * Output section. 251 * Build up line images from the words passed in. Prefix 252 * each line with correct number of blanks. The buffer "outbuf" 253 * contains the current partial line image, including prefixed blanks. 254 * "outp" points to the next available space therein. When outp is NOSTR, 255 * there ain't nothing in there yet. At the bottom of this whole mess, 256 * leading tabs are reinserted. 257 */ 258 259 char outbuf[BUFSIZ]; /* Sandbagged output line image */ 260 char *outp; /* Pointer in above */ 261 262 /* 263 * Initialize the output section. 264 */ 265 266 setout() 267 { 268 outp = NOSTR; 269 } 270 271 /* 272 * Pack a word onto the output line. If this is the beginning of 273 * the line, push on the appropriately-sized string of blanks first. 274 * If the word won't fit on the current line, flush and begin a new 275 * line. If the word is too long to fit all by itself on a line, 276 * just give it its own and hope for the best. 277 */ 278 279 pack(word) 280 char word[]; 281 { 282 register char *cp; 283 register int s, t; 284 285 if (outp == NOSTR) 286 leadin(); 287 t = strlen(word); 288 s = outp-outbuf; 289 if (t+s <= width) { 290 291 /* 292 * In like flint! 293 */ 294 295 for (cp = word; *cp; *outp++ = *cp++) 296 ; 297 return; 298 } 299 if (s > pfx) { 300 oflush(); 301 leadin(); 302 } 303 for (cp = word; *cp; *outp++ = *cp++) 304 ; 305 } 306 307 /* 308 * If there is anything on the current output line, send it on 309 * its way. Set outp to NOSTR to indicate the absence of the current 310 * line prefix. 311 */ 312 313 oflush() 314 { 315 if (outp == NOSTR) 316 return; 317 *outp = '\0'; 318 tabulate(outbuf); 319 outp = NOSTR; 320 } 321 322 /* 323 * Take the passed line buffer, insert leading tabs where possible, and 324 * output on standard output (finally). 325 */ 326 327 tabulate(line) 328 char line[]; 329 { 330 register char *cp, *cp2; 331 register int b, t; 332 333 /* 334 * Toss trailing blanks in the output line. 335 */ 336 337 cp = line + strlen(line) - 1; 338 while (cp >= line && *cp == ' ') 339 cp--; 340 *++cp = '\0'; 341 342 /* 343 * Count the leading blank space and tabulate. 344 */ 345 346 for (cp = line; *cp == ' '; cp++) 347 ; 348 b = cp-line; 349 t = b >> 3; 350 b &= 07; 351 if (t > 0) 352 do 353 putc('\t', stdout); 354 while (--t); 355 if (b > 0) 356 do 357 putc(' ', stdout); 358 while (--b); 359 while (*cp) 360 putc(*cp++, stdout); 361 putc('\n', stdout); 362 } 363 364 /* 365 * Initialize the output line with the appropriate number of 366 * leading blanks. 367 */ 368 369 leadin() 370 { 371 register int b; 372 register char *cp; 373 374 for (b = 0, cp = outbuf; b < pfx; b++) 375 *cp++ = ' '; 376 outp = cp; 377 } 378 379 /* 380 * Save a string in dynamic space. 381 * This little goodie is needed for 382 * a headline detector in head.c 383 */ 384 385 char * 386 savestr(str) 387 char str[]; 388 { 389 register char *top; 390 391 top = calloc(strlen(str) + 1, 1); 392 if (top == NOSTR) { 393 fprintf(stderr, "fmt: Ran out of memory\n"); 394 exit(1); 395 } 396 copy(str, top); 397 return(top); 398 } 399 400 /* 401 * Is s1 a prefix of s2?? 402 */ 403 404 ispref(s1, s2) 405 register char *s1, *s2; 406 { 407 408 while (*s1++ == *s2) 409 ; 410 return(*s1 == '\0'); 411 } 412