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 7/1/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: }