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