11232Skas # 21232Skas 31232Skas #include <stdio.h> 41232Skas #include <ctype.h> 51232Skas 61232Skas /* 71232Skas * fmt -- format the concatenation of input files or standard input 81232Skas * onto standard output. Designed for use with Mail ~| 91232Skas * 10*3197Skas * Syntax: fmt [ -width ] [ name ... ] 111232Skas * Author: Kurt Shoens (UCB) 12/7/78 121232Skas */ 131232Skas 14*3197Skas static char *SccsId = "@(#)fmt.c 1.3 03/11/81"; 151232Skas 161232Skas #define NOSTR ((char *) 0) /* Null string pointer for lint */ 171232Skas 181232Skas int pfx; /* Current leading blank count */ 191232Skas int lineno; /* Current input line */ 201232Skas int mark; /* Last place we saw a head line */ 21*3197Skas int width = 72; /* Width that we will not exceed */ 221232Skas 231232Skas char *calloc(); /* for lint . . . */ 241232Skas char *headnames[] = {"To", "Subject", "Cc", 0}; 251232Skas 261232Skas /* 271232Skas * Drive the whole formatter by managing input files. Also, 281232Skas * cause initialization of the output stuff and flush it out 291232Skas * at the end. 301232Skas */ 311232Skas 321232Skas main(argc, argv) 331232Skas char **argv; 341232Skas { 351232Skas register FILE *fi; 361232Skas register int errs = 0; 37*3197Skas char sobuf[BUFSIZ]; 38*3197Skas register char *cp; 39*3197Skas int nofile; 401232Skas 411232Skas setout(); 421232Skas lineno = 1; 431232Skas mark = -10; 44*3197Skas setbuf(stdout, sobuf); 451232Skas if (argc < 2) { 46*3197Skas single: 471232Skas fmt(stdin); 481232Skas oflush(); 491232Skas exit(0); 501232Skas } 51*3197Skas nofile = 1; 521232Skas while (--argc) { 53*3197Skas cp = *++argv; 54*3197Skas if (*cp == '-') { 55*3197Skas width = atoi(cp+1); 56*3197Skas if (width <= 0 || width >= BUFSIZ-2) { 57*3197Skas fprintf(stderr, "fmt: bad width: %d\n", width); 58*3197Skas exit(1); 59*3197Skas } 60*3197Skas continue; 61*3197Skas } 62*3197Skas nofile = 0; 63*3197Skas if ((fi = fopen(cp, "r")) == NULL) { 64*3197Skas perror(cp); 651232Skas errs++; 661232Skas continue; 671232Skas } 681232Skas fmt(fi); 691232Skas fclose(fi); 701232Skas } 71*3197Skas if (nofile) 72*3197Skas goto single; 731232Skas oflush(); 741232Skas exit(errs); 751232Skas } 761232Skas 771232Skas /* 781232Skas * Read up characters from the passed input file, forming lines, 791232Skas * doing ^H processing, expanding tabs, stripping trailing blanks, 801232Skas * and sending each line down for analysis. 811232Skas */ 821232Skas 831232Skas fmt(fi) 841232Skas FILE *fi; 851232Skas { 861232Skas char linebuf[BUFSIZ], canonb[BUFSIZ]; 871232Skas register char *cp, *cp2; 881232Skas register int c, col; 891232Skas 901232Skas c = getc(fi); 911232Skas while (c != EOF) { 921232Skas 931232Skas /* 941232Skas * Collect a line, doing ^H processing. 951232Skas * Leave tabs for now. 961232Skas */ 971232Skas 981232Skas cp = linebuf; 991232Skas while (c != '\n' && c != EOF && cp-linebuf < BUFSIZ-1) { 1001232Skas if (c == '\b') { 1011232Skas if (cp > linebuf) 1021232Skas cp--; 1031232Skas c = getc(fi); 1041232Skas continue; 1051232Skas } 1061232Skas if ((c < ' ' || c >= 0177) && c != '\t') { 1071232Skas c = getc(fi); 1081232Skas continue; 1091232Skas } 1101232Skas *cp++ = c; 1111232Skas c = getc(fi); 1121232Skas } 1131232Skas *cp = '\0'; 1141232Skas 1151232Skas /* 1161232Skas * Toss anything remaining on the input line. 1171232Skas */ 1181232Skas 1191232Skas while (c != '\n' && c != EOF) 1201232Skas c = getc(fi); 1211232Skas 1221232Skas /* 1231232Skas * Expand tabs on the way to canonb. 1241232Skas */ 1251232Skas 1261232Skas col = 0; 1271232Skas cp = linebuf; 1281232Skas cp2 = canonb; 1291232Skas while (c = *cp++) { 1301232Skas if (c != '\t') { 1311232Skas col++; 1321232Skas if (cp2-canonb < BUFSIZ-1) 1331232Skas *cp2++ = c; 1341232Skas continue; 1351232Skas } 1361232Skas do { 1371232Skas if (cp2-canonb < BUFSIZ-1) 1381232Skas *cp2++ = ' '; 1391232Skas col++; 1401232Skas } while ((col & 07) != 0); 1411232Skas } 1421232Skas 1431232Skas /* 1441232Skas * Swipe trailing blanks from the line. 1451232Skas */ 1461232Skas 1471232Skas for (cp2--; cp2 >= canonb && *cp2 == ' '; cp2--) 1481232Skas ; 1491232Skas *++cp2 = '\0'; 1501232Skas prefix(canonb); 1511232Skas if (c != EOF) 1521232Skas c = getc(fi); 1531232Skas } 1541232Skas } 1551232Skas 1561232Skas /* 1571232Skas * Take a line devoid of tabs and other garbage and determine its 1581232Skas * blank prefix. If the indent changes, call for a linebreak. 1591232Skas * If the input line is blank, echo the blank line on the output. 1601232Skas * Finally, if the line minus the prefix is a mail header, try to keep 1611232Skas * it on a line by itself. 1621232Skas */ 1631232Skas 1641232Skas prefix(line) 1651232Skas char line[]; 1661232Skas { 1671232Skas register char *cp, **hp; 1681232Skas register int np, h; 1691232Skas 1701232Skas if (strlen(line) == 0) { 1711232Skas oflush(); 1721232Skas putchar('\n'); 1731232Skas return; 1741232Skas } 1751232Skas for (cp = line; *cp == ' '; cp++) 1761232Skas ; 1771232Skas np = cp - line; 1781232Skas 1791232Skas /* 1801232Skas * The following horrible expression attempts to avoid linebreaks 1811232Skas * when the indent changes due to a paragraph. 1821232Skas */ 1831232Skas 1841232Skas if (np != pfx && (np > pfx || abs(pfx-np) > 8)) 1851232Skas oflush(); 1861232Skas if (h = ishead(cp)) 1871232Skas oflush(), mark = lineno; 1881232Skas if (lineno - mark < 3 && lineno - mark > 0) 1891232Skas for (hp = &headnames[0]; *hp != (char *) 0; hp++) 1901232Skas if (ispref(*hp, cp)) { 1911232Skas h = 1; 1921232Skas oflush(); 1931232Skas break; 1941232Skas } 1951232Skas if (!h && (h = (*cp == '.'))) 1961232Skas oflush(); 1971232Skas pfx = np; 1981232Skas split(cp); 1991232Skas if (h) 2001232Skas oflush(); 2011232Skas lineno++; 2021232Skas } 2031232Skas 2041232Skas /* 2051232Skas * Split up the passed line into output "words" which are 2061232Skas * maximal strings of non-blanks with the blank separation 2071232Skas * attached at the end. Pass these words along to the output 2081232Skas * line packer. 2091232Skas */ 2101232Skas 2111232Skas split(line) 2121232Skas char line[]; 2131232Skas { 2141232Skas register char *cp, *cp2; 2151232Skas char word[BUFSIZ]; 2161232Skas 2171232Skas cp = line; 2181232Skas while (*cp) { 2191232Skas cp2 = word; 2201232Skas 2211232Skas /* 2221232Skas * Collect a 'word,' allowing it to contain escaped 2231232Skas * white space. 2241232Skas */ 2251232Skas 2261232Skas while (*cp && *cp != ' ') { 2271232Skas if (*cp == '\\' && isspace(cp[1])) 2281232Skas *cp2++ = *cp++; 2291232Skas *cp2++ = *cp++; 2301232Skas } 2311232Skas 2321232Skas /* 2331232Skas * Guarantee a space at end of line. 2341232Skas * Two spaces after end of sentence punctuation. 2351232Skas */ 2361232Skas 2371232Skas if (*cp == '\0') { 2381232Skas *cp2++ = ' '; 2392060Skas if (any(cp[-1], ".:!?")) 2401232Skas *cp2++ = ' '; 2411232Skas } 2421232Skas while (*cp == ' ') 2431232Skas *cp2++ = *cp++; 2441232Skas *cp2 = '\0'; 2451232Skas pack(word); 2461232Skas } 2471232Skas } 2481232Skas 2491232Skas /* 2501232Skas * Output section. 2511232Skas * Build up line images from the words passed in. Prefix 2521232Skas * each line with correct number of blanks. The buffer "outbuf" 2531232Skas * contains the current partial line image, including prefixed blanks. 2541232Skas * "outp" points to the next available space therein. When outp is NOSTR, 2551232Skas * there ain't nothing in there yet. At the bottom of this whole mess, 2561232Skas * leading tabs are reinserted. 2571232Skas */ 2581232Skas 2591232Skas char outbuf[BUFSIZ]; /* Sandbagged output line image */ 2601232Skas char *outp; /* Pointer in above */ 2611232Skas 2621232Skas /* 2631232Skas * Initialize the output section. 2641232Skas */ 2651232Skas 2661232Skas setout() 2671232Skas { 2681232Skas outp = NOSTR; 2691232Skas } 2701232Skas 2711232Skas /* 2721232Skas * Pack a word onto the output line. If this is the beginning of 2731232Skas * the line, push on the appropriately-sized string of blanks first. 2741232Skas * If the word won't fit on the current line, flush and begin a new 2751232Skas * line. If the word is too long to fit all by itself on a line, 2761232Skas * just give it its own and hope for the best. 2771232Skas */ 2781232Skas 2791232Skas pack(word) 2801232Skas char word[]; 2811232Skas { 2821232Skas register char *cp; 2831232Skas register int s, t; 2841232Skas 2851232Skas if (outp == NOSTR) 2861232Skas leadin(); 2871232Skas t = strlen(word); 2881232Skas s = outp-outbuf; 289*3197Skas if (t+s <= width) { 2901232Skas 2911232Skas /* 2921232Skas * In like flint! 2931232Skas */ 2941232Skas 2951232Skas for (cp = word; *cp; *outp++ = *cp++) 2961232Skas ; 2971232Skas return; 2981232Skas } 2991232Skas if (s > pfx) { 3001232Skas oflush(); 3011232Skas leadin(); 3021232Skas } 3031232Skas for (cp = word; *cp; *outp++ = *cp++) 3041232Skas ; 3051232Skas } 3061232Skas 3071232Skas /* 3081232Skas * If there is anything on the current output line, send it on 3091232Skas * its way. Set outp to NOSTR to indicate the absence of the current 3101232Skas * line prefix. 3111232Skas */ 3121232Skas 3131232Skas oflush() 3141232Skas { 3151232Skas if (outp == NOSTR) 3161232Skas return; 3171232Skas *outp = '\0'; 3181232Skas tabulate(outbuf); 3191232Skas outp = NOSTR; 3201232Skas } 3211232Skas 3221232Skas /* 3231232Skas * Take the passed line buffer, insert leading tabs where possible, and 3241232Skas * output on standard output (finally). 3251232Skas */ 3261232Skas 3271232Skas tabulate(line) 3281232Skas char line[]; 3291232Skas { 3301232Skas register char *cp, *cp2; 3311232Skas register int b, t; 3321232Skas 3331232Skas /* 3341232Skas * Toss trailing blanks in the output line. 3351232Skas */ 3361232Skas 3371232Skas cp = line + strlen(line) - 1; 3381232Skas while (cp >= line && *cp == ' ') 3391232Skas cp--; 3401232Skas *++cp = '\0'; 3411232Skas 3421232Skas /* 3431232Skas * Count the leading blank space and tabulate. 3441232Skas */ 3451232Skas 3461232Skas for (cp = line; *cp == ' '; cp++) 3471232Skas ; 3481232Skas b = cp-line; 3491232Skas t = b >> 3; 3501232Skas b &= 07; 3511232Skas if (t > 0) 3521232Skas do 3531232Skas putc('\t', stdout); 3541232Skas while (--t); 3551232Skas if (b > 0) 3561232Skas do 3571232Skas putc(' ', stdout); 3581232Skas while (--b); 3591232Skas while (*cp) 3601232Skas putc(*cp++, stdout); 3611232Skas putc('\n', stdout); 3621232Skas } 3631232Skas 3641232Skas /* 3651232Skas * Initialize the output line with the appropriate number of 3661232Skas * leading blanks. 3671232Skas */ 3681232Skas 3691232Skas leadin() 3701232Skas { 3711232Skas register int b; 3721232Skas register char *cp; 3731232Skas 3741232Skas for (b = 0, cp = outbuf; b < pfx; b++) 3751232Skas *cp++ = ' '; 3761232Skas outp = cp; 3771232Skas } 3781232Skas 3791232Skas /* 3801232Skas * Save a string in dynamic space. 3811232Skas * This little goodie is needed for 3821232Skas * a headline detector in head.c 3831232Skas */ 3841232Skas 3851232Skas char * 3861232Skas savestr(str) 3871232Skas char str[]; 3881232Skas { 3891232Skas register char *top; 3901232Skas 3911232Skas top = calloc(strlen(str) + 1, 1); 3921232Skas if (top == NOSTR) { 3931232Skas fprintf(stderr, "fmt: Ran out of memory\n"); 3941232Skas exit(1); 3951232Skas } 3961232Skas copy(str, top); 3971232Skas return(top); 3981232Skas } 3991232Skas 4001232Skas /* 4011232Skas * Is s1 a prefix of s2?? 4021232Skas */ 4031232Skas 4041232Skas ispref(s1, s2) 4051232Skas register char *s1, *s2; 4061232Skas { 4071232Skas 4081232Skas while (*s1++ == *s2) 4091232Skas ; 4101232Skas return(*s1 == '\0'); 4111232Skas } 412