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