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