xref: /minix/usr.bin/fmt/fmt.c (revision 5ae330e2)
1*5ae330e2SDavid van Moolenbroek /*	$NetBSD: fmt.c,v 1.32 2012/06/30 21:31:15 christos Exp $	*/
2*5ae330e2SDavid van Moolenbroek 
3*5ae330e2SDavid van Moolenbroek /*
4*5ae330e2SDavid van Moolenbroek  * Copyright (c) 1980, 1993
5*5ae330e2SDavid van Moolenbroek  *	The Regents of the University of California.  All rights reserved.
6*5ae330e2SDavid van Moolenbroek  *
7*5ae330e2SDavid van Moolenbroek  * Redistribution and use in source and binary forms, with or without
8*5ae330e2SDavid van Moolenbroek  * modification, are permitted provided that the following conditions
9*5ae330e2SDavid van Moolenbroek  * are met:
10*5ae330e2SDavid van Moolenbroek  * 1. Redistributions of source code must retain the above copyright
11*5ae330e2SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer.
12*5ae330e2SDavid van Moolenbroek  * 2. Redistributions in binary form must reproduce the above copyright
13*5ae330e2SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer in the
14*5ae330e2SDavid van Moolenbroek  *    documentation and/or other materials provided with the distribution.
15*5ae330e2SDavid van Moolenbroek  * 3. Neither the name of the University nor the names of its contributors
16*5ae330e2SDavid van Moolenbroek  *    may be used to endorse or promote products derived from this software
17*5ae330e2SDavid van Moolenbroek  *    without specific prior written permission.
18*5ae330e2SDavid van Moolenbroek  *
19*5ae330e2SDavid van Moolenbroek  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20*5ae330e2SDavid van Moolenbroek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21*5ae330e2SDavid van Moolenbroek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22*5ae330e2SDavid van Moolenbroek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23*5ae330e2SDavid van Moolenbroek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24*5ae330e2SDavid van Moolenbroek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25*5ae330e2SDavid van Moolenbroek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26*5ae330e2SDavid van Moolenbroek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27*5ae330e2SDavid van Moolenbroek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28*5ae330e2SDavid van Moolenbroek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29*5ae330e2SDavid van Moolenbroek  * SUCH DAMAGE.
30*5ae330e2SDavid van Moolenbroek  */
31*5ae330e2SDavid van Moolenbroek 
32*5ae330e2SDavid van Moolenbroek #include <sys/cdefs.h>
33*5ae330e2SDavid van Moolenbroek #ifndef lint
34*5ae330e2SDavid van Moolenbroek __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
35*5ae330e2SDavid van Moolenbroek  The Regents of the University of California.  All rights reserved.");
36*5ae330e2SDavid van Moolenbroek #endif /* not lint */
37*5ae330e2SDavid van Moolenbroek 
38*5ae330e2SDavid van Moolenbroek #ifndef lint
39*5ae330e2SDavid van Moolenbroek #if 0
40*5ae330e2SDavid van Moolenbroek static char sccsid[] = "@(#)fmt.c	8.1 (Berkeley) 7/20/93";
41*5ae330e2SDavid van Moolenbroek #endif
42*5ae330e2SDavid van Moolenbroek __RCSID("$NetBSD: fmt.c,v 1.32 2012/06/30 21:31:15 christos Exp $");
43*5ae330e2SDavid van Moolenbroek #endif /* not lint */
44*5ae330e2SDavid van Moolenbroek 
45*5ae330e2SDavid van Moolenbroek #include <ctype.h>
46*5ae330e2SDavid van Moolenbroek #include <locale.h>
47*5ae330e2SDavid van Moolenbroek #include <stdio.h>
48*5ae330e2SDavid van Moolenbroek #include <stdlib.h>
49*5ae330e2SDavid van Moolenbroek #include <unistd.h>
50*5ae330e2SDavid van Moolenbroek #include <errno.h>
51*5ae330e2SDavid van Moolenbroek #include <err.h>
52*5ae330e2SDavid van Moolenbroek #include <limits.h>
53*5ae330e2SDavid van Moolenbroek #include <string.h>
54*5ae330e2SDavid van Moolenbroek #include "buffer.h"
55*5ae330e2SDavid van Moolenbroek 
56*5ae330e2SDavid van Moolenbroek /*
57*5ae330e2SDavid van Moolenbroek  * fmt -- format the concatenation of input files or standard input
58*5ae330e2SDavid van Moolenbroek  * onto standard output.  Designed for use with Mail ~|
59*5ae330e2SDavid van Moolenbroek  *
60*5ae330e2SDavid van Moolenbroek  * Syntax : fmt [ goal [ max ] ] [ name ... ]
61*5ae330e2SDavid van Moolenbroek  * Authors: Kurt Shoens (UCB) 12/7/78;
62*5ae330e2SDavid van Moolenbroek  *          Liz Allen (UMCP) 2/24/83 [Addition of goal length concept].
63*5ae330e2SDavid van Moolenbroek  */
64*5ae330e2SDavid van Moolenbroek 
65*5ae330e2SDavid van Moolenbroek /* LIZ@UOM 6/18/85 --New variables goal_length and max_length */
66*5ae330e2SDavid van Moolenbroek #define GOAL_LENGTH 65
67*5ae330e2SDavid van Moolenbroek #define MAX_LENGTH 75
68*5ae330e2SDavid van Moolenbroek static size_t	goal_length;	/* Target or goal line length in output */
69*5ae330e2SDavid van Moolenbroek static size_t	max_length;	/* Max line length in output */
70*5ae330e2SDavid van Moolenbroek static size_t	pfx;		/* Current leading blank count */
71*5ae330e2SDavid van Moolenbroek static int	raw;		/* Don't treat mail specially */
72*5ae330e2SDavid van Moolenbroek static int	lineno;		/* Current input line */
73*5ae330e2SDavid van Moolenbroek static int	mark;		/* Last place we saw a head line */
74*5ae330e2SDavid van Moolenbroek static int	center;
75*5ae330e2SDavid van Moolenbroek static struct buffer outbuf;
76*5ae330e2SDavid van Moolenbroek 
77*5ae330e2SDavid van Moolenbroek static const char	*headnames[] = {"To", "Subject", "Cc", 0};
78*5ae330e2SDavid van Moolenbroek 
79*5ae330e2SDavid van Moolenbroek static void	usage(void) __dead;
80*5ae330e2SDavid van Moolenbroek static int 	getnum(const char *, const char *, size_t *, int);
81*5ae330e2SDavid van Moolenbroek static void	fmt(FILE *);
82*5ae330e2SDavid van Moolenbroek static int	ispref(const char *, const char *);
83*5ae330e2SDavid van Moolenbroek static void	leadin(void);
84*5ae330e2SDavid van Moolenbroek static void	oflush(void);
85*5ae330e2SDavid van Moolenbroek static void	pack(const char *, size_t);
86*5ae330e2SDavid van Moolenbroek static void	prefix(const struct buffer *, int);
87*5ae330e2SDavid van Moolenbroek static void	split(const char *, int);
88*5ae330e2SDavid van Moolenbroek static void	tabulate(struct buffer *);
89*5ae330e2SDavid van Moolenbroek 
90*5ae330e2SDavid van Moolenbroek 
91*5ae330e2SDavid van Moolenbroek int		ishead(const char *);
92*5ae330e2SDavid van Moolenbroek 
93*5ae330e2SDavid van Moolenbroek /*
94*5ae330e2SDavid van Moolenbroek  * Drive the whole formatter by managing input files.  Also,
95*5ae330e2SDavid van Moolenbroek  * cause initialization of the output stuff and flush it out
96*5ae330e2SDavid van Moolenbroek  * at the end.
97*5ae330e2SDavid van Moolenbroek  */
98*5ae330e2SDavid van Moolenbroek 
99*5ae330e2SDavid van Moolenbroek int
main(int argc,char ** argv)100*5ae330e2SDavid van Moolenbroek main(int argc, char **argv)
101*5ae330e2SDavid van Moolenbroek {
102*5ae330e2SDavid van Moolenbroek 	FILE *fi;
103*5ae330e2SDavid van Moolenbroek 	int errs = 0;
104*5ae330e2SDavid van Moolenbroek 	int compat = 1;
105*5ae330e2SDavid van Moolenbroek 	int c;
106*5ae330e2SDavid van Moolenbroek 
107*5ae330e2SDavid van Moolenbroek 	goal_length = GOAL_LENGTH;
108*5ae330e2SDavid van Moolenbroek 	max_length = MAX_LENGTH;
109*5ae330e2SDavid van Moolenbroek 	buf_init(&outbuf);
110*5ae330e2SDavid van Moolenbroek 	lineno = 1;
111*5ae330e2SDavid van Moolenbroek 	mark = -10;
112*5ae330e2SDavid van Moolenbroek 
113*5ae330e2SDavid van Moolenbroek 	setprogname(*argv);
114*5ae330e2SDavid van Moolenbroek 	(void)setlocale(LC_ALL, "");
115*5ae330e2SDavid van Moolenbroek 
116*5ae330e2SDavid van Moolenbroek 	while ((c = getopt(argc, argv, "Cg:m:rw:")) != -1)
117*5ae330e2SDavid van Moolenbroek 		switch (c) {
118*5ae330e2SDavid van Moolenbroek 		case 'C':
119*5ae330e2SDavid van Moolenbroek 			center++;
120*5ae330e2SDavid van Moolenbroek 			break;
121*5ae330e2SDavid van Moolenbroek 		case 'g':
122*5ae330e2SDavid van Moolenbroek 			(void)getnum(optarg, "goal", &goal_length, 1);
123*5ae330e2SDavid van Moolenbroek 			compat = 0;
124*5ae330e2SDavid van Moolenbroek 			break;
125*5ae330e2SDavid van Moolenbroek 		case 'm':
126*5ae330e2SDavid van Moolenbroek 		case 'w':
127*5ae330e2SDavid van Moolenbroek 			(void)getnum(optarg, "max", &max_length, 1);
128*5ae330e2SDavid van Moolenbroek 			compat = 0;
129*5ae330e2SDavid van Moolenbroek 			break;
130*5ae330e2SDavid van Moolenbroek 		case 'r':
131*5ae330e2SDavid van Moolenbroek 			raw++;
132*5ae330e2SDavid van Moolenbroek 			break;
133*5ae330e2SDavid van Moolenbroek 		default:
134*5ae330e2SDavid van Moolenbroek 			usage();
135*5ae330e2SDavid van Moolenbroek 		}
136*5ae330e2SDavid van Moolenbroek 
137*5ae330e2SDavid van Moolenbroek 	argc -= optind;
138*5ae330e2SDavid van Moolenbroek 	argv += optind;
139*5ae330e2SDavid van Moolenbroek 
140*5ae330e2SDavid van Moolenbroek 	/*
141*5ae330e2SDavid van Moolenbroek 	 * compatibility with old usage.
142*5ae330e2SDavid van Moolenbroek 	 */
143*5ae330e2SDavid van Moolenbroek 	if (compat && argc > 0 && getnum(*argv, "goal", &goal_length, 0)) {
144*5ae330e2SDavid van Moolenbroek 		argv++;
145*5ae330e2SDavid van Moolenbroek 		argc--;
146*5ae330e2SDavid van Moolenbroek 		if (argc > 0 && getnum(*argv, "max", &max_length, 0)) {
147*5ae330e2SDavid van Moolenbroek 			argv++;
148*5ae330e2SDavid van Moolenbroek 			argc--;
149*5ae330e2SDavid van Moolenbroek 		}
150*5ae330e2SDavid van Moolenbroek 	}
151*5ae330e2SDavid van Moolenbroek 
152*5ae330e2SDavid van Moolenbroek 	if (max_length <= goal_length) {
153*5ae330e2SDavid van Moolenbroek 		errx(1, "Max length (%zu) must be greater than goal "
154*5ae330e2SDavid van Moolenbroek 		    "length (%zu)", max_length, goal_length);
155*5ae330e2SDavid van Moolenbroek 	}
156*5ae330e2SDavid van Moolenbroek 	if (argc == 0) {
157*5ae330e2SDavid van Moolenbroek 		fmt(stdin);
158*5ae330e2SDavid van Moolenbroek 		oflush();
159*5ae330e2SDavid van Moolenbroek 		return 0;
160*5ae330e2SDavid van Moolenbroek 	}
161*5ae330e2SDavid van Moolenbroek 	for (;argc; argc--, argv++) {
162*5ae330e2SDavid van Moolenbroek 		if ((fi = fopen(*argv, "r")) == NULL) {
163*5ae330e2SDavid van Moolenbroek 			warn("Cannot open `%s'", *argv);
164*5ae330e2SDavid van Moolenbroek 			errs++;
165*5ae330e2SDavid van Moolenbroek 			continue;
166*5ae330e2SDavid van Moolenbroek 		}
167*5ae330e2SDavid van Moolenbroek 		fmt(fi);
168*5ae330e2SDavid van Moolenbroek 		(void)fclose(fi);
169*5ae330e2SDavid van Moolenbroek 	}
170*5ae330e2SDavid van Moolenbroek 	oflush();
171*5ae330e2SDavid van Moolenbroek 	buf_end(&outbuf);
172*5ae330e2SDavid van Moolenbroek 	return errs;
173*5ae330e2SDavid van Moolenbroek }
174*5ae330e2SDavid van Moolenbroek 
175*5ae330e2SDavid van Moolenbroek static void
usage(void)176*5ae330e2SDavid van Moolenbroek usage(void)
177*5ae330e2SDavid van Moolenbroek {
178*5ae330e2SDavid van Moolenbroek 	(void)fprintf(stderr,
179*5ae330e2SDavid van Moolenbroek 	    "Usage: %s [-Cr] [-g <goal>] [-m|w <max>] [<files>..]\n"
180*5ae330e2SDavid van Moolenbroek 	    "\t %s [-Cr] [<goal>] [<max>] [<files>]\n",
181*5ae330e2SDavid van Moolenbroek 	    getprogname(), getprogname());
182*5ae330e2SDavid van Moolenbroek 	exit(1);
183*5ae330e2SDavid van Moolenbroek }
184*5ae330e2SDavid van Moolenbroek 
185*5ae330e2SDavid van Moolenbroek static int
getnum(const char * str,const char * what,size_t * res,int badnum)186*5ae330e2SDavid van Moolenbroek getnum(const char *str, const char *what, size_t *res, int badnum)
187*5ae330e2SDavid van Moolenbroek {
188*5ae330e2SDavid van Moolenbroek 	unsigned long ul;
189*5ae330e2SDavid van Moolenbroek 	char *ep;
190*5ae330e2SDavid van Moolenbroek 
191*5ae330e2SDavid van Moolenbroek 	errno = 0;
192*5ae330e2SDavid van Moolenbroek 	ul = strtoul(str, &ep, 0);
193*5ae330e2SDavid van Moolenbroek         if (*str != '\0' && *ep == '\0') {
194*5ae330e2SDavid van Moolenbroek 		 if ((errno == ERANGE && ul == ULONG_MAX) || ul > SIZE_T_MAX)
195*5ae330e2SDavid van Moolenbroek 			errx(1, "%s number `%s' too big", what, str);
196*5ae330e2SDavid van Moolenbroek 		*res = (size_t)ul;
197*5ae330e2SDavid van Moolenbroek 		return 1;
198*5ae330e2SDavid van Moolenbroek 	} else if (badnum)
199*5ae330e2SDavid van Moolenbroek 		errx(1, "Bad %s number `%s'", what, str);
200*5ae330e2SDavid van Moolenbroek 
201*5ae330e2SDavid van Moolenbroek 	return 0;
202*5ae330e2SDavid van Moolenbroek }
203*5ae330e2SDavid van Moolenbroek 
204*5ae330e2SDavid van Moolenbroek /*
205*5ae330e2SDavid van Moolenbroek  * Read up characters from the passed input file, forming lines,
206*5ae330e2SDavid van Moolenbroek  * doing ^H processing, expanding tabs, stripping trailing blanks,
207*5ae330e2SDavid van Moolenbroek  * and sending each line down for analysis.
208*5ae330e2SDavid van Moolenbroek  */
209*5ae330e2SDavid van Moolenbroek static void
fmt(FILE * fi)210*5ae330e2SDavid van Moolenbroek fmt(FILE *fi)
211*5ae330e2SDavid van Moolenbroek {
212*5ae330e2SDavid van Moolenbroek 	struct buffer lbuf, cbuf;
213*5ae330e2SDavid van Moolenbroek 	char *cp, *cp2;
214*5ae330e2SDavid van Moolenbroek 	int c, add_space;
215*5ae330e2SDavid van Moolenbroek 	size_t len, col, i;
216*5ae330e2SDavid van Moolenbroek 
217*5ae330e2SDavid van Moolenbroek 	if (center) {
218*5ae330e2SDavid van Moolenbroek 		for (;;) {
219*5ae330e2SDavid van Moolenbroek 			cp = fgetln(fi, &len);
220*5ae330e2SDavid van Moolenbroek 			if (!cp)
221*5ae330e2SDavid van Moolenbroek 				return;
222*5ae330e2SDavid van Moolenbroek 
223*5ae330e2SDavid van Moolenbroek 			/* skip over leading space */
224*5ae330e2SDavid van Moolenbroek 			while (len > 0) {
225*5ae330e2SDavid van Moolenbroek 				if (!isspace((unsigned char)*cp))
226*5ae330e2SDavid van Moolenbroek 					break;
227*5ae330e2SDavid van Moolenbroek 				cp++;
228*5ae330e2SDavid van Moolenbroek 				len--;
229*5ae330e2SDavid van Moolenbroek 			}
230*5ae330e2SDavid van Moolenbroek 
231*5ae330e2SDavid van Moolenbroek 			/* clear trailing space */
232*5ae330e2SDavid van Moolenbroek 			while (len > 0) {
233*5ae330e2SDavid van Moolenbroek 				if (!isspace((unsigned char)cp[len-1]))
234*5ae330e2SDavid van Moolenbroek 					break;
235*5ae330e2SDavid van Moolenbroek 				len--;
236*5ae330e2SDavid van Moolenbroek 			}
237*5ae330e2SDavid van Moolenbroek 
238*5ae330e2SDavid van Moolenbroek 			if (len == 0) {
239*5ae330e2SDavid van Moolenbroek 				/* blank line */
240*5ae330e2SDavid van Moolenbroek 				(void)putchar('\n');
241*5ae330e2SDavid van Moolenbroek 				continue;
242*5ae330e2SDavid van Moolenbroek 			}
243*5ae330e2SDavid van Moolenbroek 
244*5ae330e2SDavid van Moolenbroek 			if (goal_length > len) {
245*5ae330e2SDavid van Moolenbroek 				for (i = 0; i < (goal_length - len) / 2; i++) {
246*5ae330e2SDavid van Moolenbroek 					(void)putchar(' ');
247*5ae330e2SDavid van Moolenbroek 				}
248*5ae330e2SDavid van Moolenbroek 			}
249*5ae330e2SDavid van Moolenbroek 			for (i = 0; i < len; i++) {
250*5ae330e2SDavid van Moolenbroek 				(void)putchar(cp[i]);
251*5ae330e2SDavid van Moolenbroek 			}
252*5ae330e2SDavid van Moolenbroek 			(void)putchar('\n');
253*5ae330e2SDavid van Moolenbroek 		}
254*5ae330e2SDavid van Moolenbroek 	}
255*5ae330e2SDavid van Moolenbroek 
256*5ae330e2SDavid van Moolenbroek 	buf_init(&lbuf);
257*5ae330e2SDavid van Moolenbroek 	buf_init(&cbuf);
258*5ae330e2SDavid van Moolenbroek 	c = getc(fi);
259*5ae330e2SDavid van Moolenbroek 
260*5ae330e2SDavid van Moolenbroek 	while (c != EOF) {
261*5ae330e2SDavid van Moolenbroek 		/*
262*5ae330e2SDavid van Moolenbroek 		 * Collect a line, doing ^H processing.
263*5ae330e2SDavid van Moolenbroek 		 * Leave tabs for now.
264*5ae330e2SDavid van Moolenbroek 		 */
265*5ae330e2SDavid van Moolenbroek 		buf_reset(&lbuf);
266*5ae330e2SDavid van Moolenbroek 		while (c != '\n' && c != EOF) {
267*5ae330e2SDavid van Moolenbroek 			if (c == '\b') {
268*5ae330e2SDavid van Moolenbroek 				(void)buf_unputc(&lbuf);
269*5ae330e2SDavid van Moolenbroek 				c = getc(fi);
270*5ae330e2SDavid van Moolenbroek 				continue;
271*5ae330e2SDavid van Moolenbroek 			}
272*5ae330e2SDavid van Moolenbroek 			if(!(isprint(c) || c == '\t' || c >= 160)) {
273*5ae330e2SDavid van Moolenbroek 				c = getc(fi);
274*5ae330e2SDavid van Moolenbroek 				continue;
275*5ae330e2SDavid van Moolenbroek 			}
276*5ae330e2SDavid van Moolenbroek 			buf_putc(&lbuf, c);
277*5ae330e2SDavid van Moolenbroek 			c = getc(fi);
278*5ae330e2SDavid van Moolenbroek 		}
279*5ae330e2SDavid van Moolenbroek 		buf_putc(&lbuf, '\0');
280*5ae330e2SDavid van Moolenbroek 		(void)buf_unputc(&lbuf);
281*5ae330e2SDavid van Moolenbroek 		add_space = c != EOF;
282*5ae330e2SDavid van Moolenbroek 
283*5ae330e2SDavid van Moolenbroek 		/*
284*5ae330e2SDavid van Moolenbroek 		 * Expand tabs on the way.
285*5ae330e2SDavid van Moolenbroek 		 */
286*5ae330e2SDavid van Moolenbroek 		col = 0;
287*5ae330e2SDavid van Moolenbroek 		cp = lbuf.bptr;
288*5ae330e2SDavid van Moolenbroek 		buf_reset(&cbuf);
289*5ae330e2SDavid van Moolenbroek 		while ((c = *cp++) != '\0') {
290*5ae330e2SDavid van Moolenbroek 			if (c != '\t') {
291*5ae330e2SDavid van Moolenbroek 				col++;
292*5ae330e2SDavid van Moolenbroek 				buf_putc(&cbuf, c);
293*5ae330e2SDavid van Moolenbroek 				continue;
294*5ae330e2SDavid van Moolenbroek 			}
295*5ae330e2SDavid van Moolenbroek 			do {
296*5ae330e2SDavid van Moolenbroek 				buf_putc(&cbuf, ' ');
297*5ae330e2SDavid van Moolenbroek 				col++;
298*5ae330e2SDavid van Moolenbroek 			} while ((col & 07) != 0);
299*5ae330e2SDavid van Moolenbroek 		}
300*5ae330e2SDavid van Moolenbroek 
301*5ae330e2SDavid van Moolenbroek 		/*
302*5ae330e2SDavid van Moolenbroek 		 * Swipe trailing blanks from the line.
303*5ae330e2SDavid van Moolenbroek 		 */
304*5ae330e2SDavid van Moolenbroek 		for (cp2 = cbuf.ptr - 1; cp2 >= cbuf.bptr && *cp2 == ' '; cp2--)
305*5ae330e2SDavid van Moolenbroek 			continue;
306*5ae330e2SDavid van Moolenbroek 		cbuf.ptr = cp2 + 1;
307*5ae330e2SDavid van Moolenbroek 		buf_putc(&cbuf, '\0');
308*5ae330e2SDavid van Moolenbroek 		(void)buf_unputc(&cbuf);
309*5ae330e2SDavid van Moolenbroek 		prefix(&cbuf, add_space);
310*5ae330e2SDavid van Moolenbroek 		if (c != EOF)
311*5ae330e2SDavid van Moolenbroek 			c = getc(fi);
312*5ae330e2SDavid van Moolenbroek 	}
313*5ae330e2SDavid van Moolenbroek 	buf_end(&cbuf);
314*5ae330e2SDavid van Moolenbroek 	buf_end(&lbuf);
315*5ae330e2SDavid van Moolenbroek }
316*5ae330e2SDavid van Moolenbroek 
317*5ae330e2SDavid van Moolenbroek /*
318*5ae330e2SDavid van Moolenbroek  * Take a line devoid of tabs and other garbage and determine its
319*5ae330e2SDavid van Moolenbroek  * blank prefix.  If the indent changes, call for a linebreak.
320*5ae330e2SDavid van Moolenbroek  * If the input line is blank, echo the blank line on the output.
321*5ae330e2SDavid van Moolenbroek  * Finally, if the line minus the prefix is a mail header, try to keep
322*5ae330e2SDavid van Moolenbroek  * it on a line by itself.
323*5ae330e2SDavid van Moolenbroek  */
324*5ae330e2SDavid van Moolenbroek static void
prefix(const struct buffer * buf,int add_space)325*5ae330e2SDavid van Moolenbroek prefix(const struct buffer *buf, int add_space)
326*5ae330e2SDavid van Moolenbroek {
327*5ae330e2SDavid van Moolenbroek 	const char *cp;
328*5ae330e2SDavid van Moolenbroek 	const char **hp;
329*5ae330e2SDavid van Moolenbroek 	size_t np;
330*5ae330e2SDavid van Moolenbroek 	int h;
331*5ae330e2SDavid van Moolenbroek 
332*5ae330e2SDavid van Moolenbroek 	if (buf->ptr == buf->bptr) {
333*5ae330e2SDavid van Moolenbroek 		oflush();
334*5ae330e2SDavid van Moolenbroek 		(void)putchar('\n');
335*5ae330e2SDavid van Moolenbroek 		return;
336*5ae330e2SDavid van Moolenbroek 	}
337*5ae330e2SDavid van Moolenbroek 	for (cp = buf->bptr; *cp == ' '; cp++)
338*5ae330e2SDavid van Moolenbroek 		continue;
339*5ae330e2SDavid van Moolenbroek 	np = cp - buf->bptr;
340*5ae330e2SDavid van Moolenbroek 
341*5ae330e2SDavid van Moolenbroek 	/*
342*5ae330e2SDavid van Moolenbroek 	 * The following horrible expression attempts to avoid linebreaks
343*5ae330e2SDavid van Moolenbroek 	 * when the indent changes due to a paragraph.
344*5ae330e2SDavid van Moolenbroek 	 */
345*5ae330e2SDavid van Moolenbroek 	if (np != pfx && (np > pfx || abs((int)(pfx - np)) > 8))
346*5ae330e2SDavid van Moolenbroek 		oflush();
347*5ae330e2SDavid van Moolenbroek 	if (!raw) {
348*5ae330e2SDavid van Moolenbroek 		if ((h = ishead(cp)) != 0) {
349*5ae330e2SDavid van Moolenbroek 			oflush();
350*5ae330e2SDavid van Moolenbroek 			mark = lineno;
351*5ae330e2SDavid van Moolenbroek 		}
352*5ae330e2SDavid van Moolenbroek 		if (lineno - mark < 3 && lineno - mark > 0)
353*5ae330e2SDavid van Moolenbroek 			for (hp = &headnames[0]; *hp != NULL; hp++)
354*5ae330e2SDavid van Moolenbroek 				if (ispref(*hp, cp)) {
355*5ae330e2SDavid van Moolenbroek 					h = 1;
356*5ae330e2SDavid van Moolenbroek 					oflush();
357*5ae330e2SDavid van Moolenbroek 					break;
358*5ae330e2SDavid van Moolenbroek 				}
359*5ae330e2SDavid van Moolenbroek 		if (!h && (h = (*cp == '.')))
360*5ae330e2SDavid van Moolenbroek 			oflush();
361*5ae330e2SDavid van Moolenbroek 	} else
362*5ae330e2SDavid van Moolenbroek 		h = 0;
363*5ae330e2SDavid van Moolenbroek 	pfx = np;
364*5ae330e2SDavid van Moolenbroek 	if (h) {
365*5ae330e2SDavid van Moolenbroek 		pack(cp, (size_t)(buf->ptr - cp));
366*5ae330e2SDavid van Moolenbroek 		oflush();
367*5ae330e2SDavid van Moolenbroek 	} else
368*5ae330e2SDavid van Moolenbroek 		split(cp, add_space);
369*5ae330e2SDavid van Moolenbroek 	lineno++;
370*5ae330e2SDavid van Moolenbroek }
371*5ae330e2SDavid van Moolenbroek 
372*5ae330e2SDavid van Moolenbroek /*
373*5ae330e2SDavid van Moolenbroek  * Split up the passed line into output "words" which are
374*5ae330e2SDavid van Moolenbroek  * maximal strings of non-blanks with the blank separation
375*5ae330e2SDavid van Moolenbroek  * attached at the end.  Pass these words along to the output
376*5ae330e2SDavid van Moolenbroek  * line packer.
377*5ae330e2SDavid van Moolenbroek  */
378*5ae330e2SDavid van Moolenbroek static void
split(const char line[],int add_space)379*5ae330e2SDavid van Moolenbroek split(const char line[], int add_space)
380*5ae330e2SDavid van Moolenbroek {
381*5ae330e2SDavid van Moolenbroek 	const char *cp;
382*5ae330e2SDavid van Moolenbroek 	struct buffer word;
383*5ae330e2SDavid van Moolenbroek 	size_t wlen;
384*5ae330e2SDavid van Moolenbroek 
385*5ae330e2SDavid van Moolenbroek 	buf_init(&word);
386*5ae330e2SDavid van Moolenbroek 	cp = line;
387*5ae330e2SDavid van Moolenbroek 	while (*cp) {
388*5ae330e2SDavid van Moolenbroek 		buf_reset(&word);
389*5ae330e2SDavid van Moolenbroek 		wlen = 0;
390*5ae330e2SDavid van Moolenbroek 
391*5ae330e2SDavid van Moolenbroek 		/*
392*5ae330e2SDavid van Moolenbroek 		 * Collect a 'word,' allowing it to contain escaped white
393*5ae330e2SDavid van Moolenbroek 		 * space.
394*5ae330e2SDavid van Moolenbroek 		 */
395*5ae330e2SDavid van Moolenbroek 		while (*cp && *cp != ' ') {
396*5ae330e2SDavid van Moolenbroek 			if (*cp == '\\' && isspace((unsigned char)cp[1]))
397*5ae330e2SDavid van Moolenbroek 				buf_putc(&word, *cp++);
398*5ae330e2SDavid van Moolenbroek 			buf_putc(&word, *cp++);
399*5ae330e2SDavid van Moolenbroek 			wlen++;
400*5ae330e2SDavid van Moolenbroek 		}
401*5ae330e2SDavid van Moolenbroek 
402*5ae330e2SDavid van Moolenbroek 		/*
403*5ae330e2SDavid van Moolenbroek 		 * Guarantee a space at end of line. Two spaces after end of
404*5ae330e2SDavid van Moolenbroek 		 * sentence punctuation.
405*5ae330e2SDavid van Moolenbroek 		 */
406*5ae330e2SDavid van Moolenbroek 		if (*cp == '\0' && add_space) {
407*5ae330e2SDavid van Moolenbroek 			buf_putc(&word, ' ');
408*5ae330e2SDavid van Moolenbroek 			if (strchr(".:!", cp[-1]))
409*5ae330e2SDavid van Moolenbroek 				buf_putc(&word, ' ');
410*5ae330e2SDavid van Moolenbroek 		}
411*5ae330e2SDavid van Moolenbroek 		while (*cp == ' ')
412*5ae330e2SDavid van Moolenbroek 			buf_putc(&word, *cp++);
413*5ae330e2SDavid van Moolenbroek 
414*5ae330e2SDavid van Moolenbroek 		buf_putc(&word, '\0');
415*5ae330e2SDavid van Moolenbroek 		(void)buf_unputc(&word);
416*5ae330e2SDavid van Moolenbroek 
417*5ae330e2SDavid van Moolenbroek 		pack(word.bptr, wlen);
418*5ae330e2SDavid van Moolenbroek 	}
419*5ae330e2SDavid van Moolenbroek 	buf_end(&word);
420*5ae330e2SDavid van Moolenbroek }
421*5ae330e2SDavid van Moolenbroek 
422*5ae330e2SDavid van Moolenbroek /*
423*5ae330e2SDavid van Moolenbroek  * Output section.
424*5ae330e2SDavid van Moolenbroek  * Build up line images from the words passed in.  Prefix
425*5ae330e2SDavid van Moolenbroek  * each line with correct number of blanks.
426*5ae330e2SDavid van Moolenbroek  *
427*5ae330e2SDavid van Moolenbroek  * At the bottom of this whole mess, leading tabs are reinserted.
428*5ae330e2SDavid van Moolenbroek  */
429*5ae330e2SDavid van Moolenbroek 
430*5ae330e2SDavid van Moolenbroek /*
431*5ae330e2SDavid van Moolenbroek  * Pack a word onto the output line.  If this is the beginning of
432*5ae330e2SDavid van Moolenbroek  * the line, push on the appropriately-sized string of blanks first.
433*5ae330e2SDavid van Moolenbroek  * If the word won't fit on the current line, flush and begin a new
434*5ae330e2SDavid van Moolenbroek  * line.  If the word is too long to fit all by itself on a line,
435*5ae330e2SDavid van Moolenbroek  * just give it its own and hope for the best.
436*5ae330e2SDavid van Moolenbroek  *
437*5ae330e2SDavid van Moolenbroek  * LIZ@UOM 6/18/85 -- If the new word will fit in at less than the
438*5ae330e2SDavid van Moolenbroek  *	goal length, take it.  If not, then check to see if the line
439*5ae330e2SDavid van Moolenbroek  *	will be over the max length; if so put the word on the next
440*5ae330e2SDavid van Moolenbroek  *	line.  If not, check to see if the line will be closer to the
441*5ae330e2SDavid van Moolenbroek  *	goal length with or without the word and take it or put it on
442*5ae330e2SDavid van Moolenbroek  *	the next line accordingly.
443*5ae330e2SDavid van Moolenbroek  */
444*5ae330e2SDavid van Moolenbroek 
445*5ae330e2SDavid van Moolenbroek static void
pack(const char * word,size_t wlen)446*5ae330e2SDavid van Moolenbroek pack(const char *word, size_t wlen)
447*5ae330e2SDavid van Moolenbroek {
448*5ae330e2SDavid van Moolenbroek 	const char *cp;
449*5ae330e2SDavid van Moolenbroek 	size_t s, t;
450*5ae330e2SDavid van Moolenbroek 
451*5ae330e2SDavid van Moolenbroek 	if (outbuf.bptr == outbuf.ptr)
452*5ae330e2SDavid van Moolenbroek 		leadin();
453*5ae330e2SDavid van Moolenbroek 	/*
454*5ae330e2SDavid van Moolenbroek 	 * LIZ@UOM 6/18/85 -- change condition to check goal_length; s is the
455*5ae330e2SDavid van Moolenbroek 	 * length of the line before the word is added; t is now the length
456*5ae330e2SDavid van Moolenbroek 	 * of the line after the word is added
457*5ae330e2SDavid van Moolenbroek 	 */
458*5ae330e2SDavid van Moolenbroek 	s = outbuf.ptr - outbuf.bptr;
459*5ae330e2SDavid van Moolenbroek 	t = wlen + s;
460*5ae330e2SDavid van Moolenbroek 	if ((t <= goal_length) || ((t <= max_length) &&
461*5ae330e2SDavid van Moolenbroek 	    (s <= goal_length) && (t - goal_length <= goal_length - s))) {
462*5ae330e2SDavid van Moolenbroek 		/*
463*5ae330e2SDavid van Moolenbroek 		 * In like flint!
464*5ae330e2SDavid van Moolenbroek 		 */
465*5ae330e2SDavid van Moolenbroek 		for (cp = word; *cp;)
466*5ae330e2SDavid van Moolenbroek 			buf_putc(&outbuf, *cp++);
467*5ae330e2SDavid van Moolenbroek 		return;
468*5ae330e2SDavid van Moolenbroek 	}
469*5ae330e2SDavid van Moolenbroek 	if (s > pfx) {
470*5ae330e2SDavid van Moolenbroek 		oflush();
471*5ae330e2SDavid van Moolenbroek 		leadin();
472*5ae330e2SDavid van Moolenbroek 	}
473*5ae330e2SDavid van Moolenbroek 	for (cp = word; *cp;)
474*5ae330e2SDavid van Moolenbroek 		buf_putc(&outbuf, *cp++);
475*5ae330e2SDavid van Moolenbroek }
476*5ae330e2SDavid van Moolenbroek 
477*5ae330e2SDavid van Moolenbroek /*
478*5ae330e2SDavid van Moolenbroek  * If there is anything on the current output line, send it on
479*5ae330e2SDavid van Moolenbroek  * its way.  Reset outbuf.
480*5ae330e2SDavid van Moolenbroek  */
481*5ae330e2SDavid van Moolenbroek static void
oflush(void)482*5ae330e2SDavid van Moolenbroek oflush(void)
483*5ae330e2SDavid van Moolenbroek {
484*5ae330e2SDavid van Moolenbroek 	if (outbuf.bptr == outbuf.ptr)
485*5ae330e2SDavid van Moolenbroek 		return;
486*5ae330e2SDavid van Moolenbroek 	buf_putc(&outbuf, '\0');
487*5ae330e2SDavid van Moolenbroek 	(void)buf_unputc(&outbuf);
488*5ae330e2SDavid van Moolenbroek 	tabulate(&outbuf);
489*5ae330e2SDavid van Moolenbroek 	buf_reset(&outbuf);
490*5ae330e2SDavid van Moolenbroek }
491*5ae330e2SDavid van Moolenbroek 
492*5ae330e2SDavid van Moolenbroek /*
493*5ae330e2SDavid van Moolenbroek  * Take the passed line buffer, insert leading tabs where possible, and
494*5ae330e2SDavid van Moolenbroek  * output on standard output (finally).
495*5ae330e2SDavid van Moolenbroek  */
496*5ae330e2SDavid van Moolenbroek static void
tabulate(struct buffer * buf)497*5ae330e2SDavid van Moolenbroek tabulate(struct buffer *buf)
498*5ae330e2SDavid van Moolenbroek {
499*5ae330e2SDavid van Moolenbroek 	char *cp;
500*5ae330e2SDavid van Moolenbroek 	size_t b, t;
501*5ae330e2SDavid van Moolenbroek 
502*5ae330e2SDavid van Moolenbroek 	/*
503*5ae330e2SDavid van Moolenbroek 	 * Toss trailing blanks in the output line.
504*5ae330e2SDavid van Moolenbroek 	 */
505*5ae330e2SDavid van Moolenbroek 	for (cp = buf->ptr - 1; cp >= buf->bptr && *cp == ' '; cp--)
506*5ae330e2SDavid van Moolenbroek 		continue;
507*5ae330e2SDavid van Moolenbroek 	*++cp = '\0';
508*5ae330e2SDavid van Moolenbroek 
509*5ae330e2SDavid van Moolenbroek 	/*
510*5ae330e2SDavid van Moolenbroek 	 * Count the leading blank space and tabulate.
511*5ae330e2SDavid van Moolenbroek 	 */
512*5ae330e2SDavid van Moolenbroek 	for (cp = buf->bptr; *cp == ' '; cp++)
513*5ae330e2SDavid van Moolenbroek 		continue;
514*5ae330e2SDavid van Moolenbroek 	b = cp - buf->bptr;
515*5ae330e2SDavid van Moolenbroek 	t = b / 8;
516*5ae330e2SDavid van Moolenbroek 	b = b % 8;
517*5ae330e2SDavid van Moolenbroek 	if (t > 0)
518*5ae330e2SDavid van Moolenbroek 		do
519*5ae330e2SDavid van Moolenbroek 			(void)putchar('\t');
520*5ae330e2SDavid van Moolenbroek 		while (--t);
521*5ae330e2SDavid van Moolenbroek 	if (b > 0)
522*5ae330e2SDavid van Moolenbroek 		do
523*5ae330e2SDavid van Moolenbroek 			(void)putchar(' ');
524*5ae330e2SDavid van Moolenbroek 		while (--b);
525*5ae330e2SDavid van Moolenbroek 	while (*cp)
526*5ae330e2SDavid van Moolenbroek 		(void)putchar(*cp++);
527*5ae330e2SDavid van Moolenbroek 	(void)putchar('\n');
528*5ae330e2SDavid van Moolenbroek }
529*5ae330e2SDavid van Moolenbroek 
530*5ae330e2SDavid van Moolenbroek /*
531*5ae330e2SDavid van Moolenbroek  * Initialize the output line with the appropriate number of
532*5ae330e2SDavid van Moolenbroek  * leading blanks.
533*5ae330e2SDavid van Moolenbroek  */
534*5ae330e2SDavid van Moolenbroek static void
leadin(void)535*5ae330e2SDavid van Moolenbroek leadin(void)
536*5ae330e2SDavid van Moolenbroek {
537*5ae330e2SDavid van Moolenbroek 	size_t b;
538*5ae330e2SDavid van Moolenbroek 
539*5ae330e2SDavid van Moolenbroek 	buf_reset(&outbuf);
540*5ae330e2SDavid van Moolenbroek 
541*5ae330e2SDavid van Moolenbroek 	for (b = 0; b < pfx; b++)
542*5ae330e2SDavid van Moolenbroek 		buf_putc(&outbuf, ' ');
543*5ae330e2SDavid van Moolenbroek }
544*5ae330e2SDavid van Moolenbroek 
545*5ae330e2SDavid van Moolenbroek /*
546*5ae330e2SDavid van Moolenbroek  * Is s1 a prefix of s2??
547*5ae330e2SDavid van Moolenbroek  */
548*5ae330e2SDavid van Moolenbroek static int
ispref(const char * s1,const char * s2)549*5ae330e2SDavid van Moolenbroek ispref(const char *s1, const char *s2)
550*5ae330e2SDavid van Moolenbroek {
551*5ae330e2SDavid van Moolenbroek 
552*5ae330e2SDavid van Moolenbroek 	while (*s1++ == *s2)
553*5ae330e2SDavid van Moolenbroek 		continue;
554*5ae330e2SDavid van Moolenbroek 	return *s1 == '\0';
555*5ae330e2SDavid van Moolenbroek }
556