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