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