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