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