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