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