xref: /netbsd-src/usr.bin/fmt/fmt.c (revision d40754b094fab3343b249f59ade092ca3bddf330)
1*d40754b0Schristos /*	$NetBSD: fmt.c,v 1.33 2017/10/13 00:11:56 christos Exp $	*/
27642736cSjtc 
361f28255Scgd /*
47642736cSjtc  * Copyright (c) 1980, 1993
57642736cSjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
320c2c736dSlukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3498e5374cSlukem __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
3598e5374cSlukem  The Regents of the University of California.  All rights reserved.");
3661f28255Scgd #endif /* not lint */
3761f28255Scgd 
3861f28255Scgd #ifndef lint
397642736cSjtc #if 0
407642736cSjtc static char sccsid[] = "@(#)fmt.c	8.1 (Berkeley) 7/20/93";
417642736cSjtc #endif
42*d40754b0Schristos __RCSID("$NetBSD: fmt.c,v 1.33 2017/10/13 00:11:56 christos Exp $");
4361f28255Scgd #endif /* not lint */
4461f28255Scgd 
45*d40754b0Schristos #include <wctype.h>
46482ca466Swiz #include <locale.h>
4761f28255Scgd #include <stdio.h>
4855d8a77fScgd #include <stdlib.h>
493d9ab41dSchristos #include <unistd.h>
503d9ab41dSchristos #include <errno.h>
513d9ab41dSchristos #include <err.h>
523d9ab41dSchristos #include <limits.h>
5355d8a77fScgd #include <string.h>
54*d40754b0Schristos #include <locale.h>
559ac95763Schristos #include "buffer.h"
5661f28255Scgd 
5761f28255Scgd /*
5861f28255Scgd  * fmt -- format the concatenation of input files or standard input
5961f28255Scgd  * onto standard output.  Designed for use with Mail ~|
6061f28255Scgd  *
6161f28255Scgd  * Syntax : fmt [ goal [ max ] ] [ name ... ]
6261f28255Scgd  * Authors: Kurt Shoens (UCB) 12/7/78;
6361f28255Scgd  *          Liz Allen (UMCP) 2/24/83 [Addition of goal length concept].
6461f28255Scgd  */
6561f28255Scgd 
6661f28255Scgd /* LIZ@UOM 6/18/85 --New variables goal_length and max_length */
6761f28255Scgd #define GOAL_LENGTH 65
6861f28255Scgd #define MAX_LENGTH 75
699ac95763Schristos static size_t	goal_length;	/* Target or goal line length in output */
709ac95763Schristos static size_t	max_length;	/* Max line length in output */
719ac95763Schristos static size_t	pfx;		/* Current leading blank count */
723d9ab41dSchristos static int	raw;		/* Don't treat mail specially */
73bd359116Schristos static int	lineno;		/* Current input line */
74bd359116Schristos static int	mark;		/* Last place we saw a head line */
759ac95763Schristos static int	center;
769ac95763Schristos static struct buffer outbuf;
7761f28255Scgd 
78*d40754b0Schristos static const wchar_t *headnames[] = { L"To", L"Subject", L"Cc", NULL };
7961f28255Scgd 
808b0f9554Sperry static void	usage(void) __dead;
813d9ab41dSchristos static int 	getnum(const char *, const char *, size_t *, int);
82482ca466Swiz static void	fmt(FILE *);
83*d40754b0Schristos static int	ispref(const wchar_t *, const wchar_t *);
84482ca466Swiz static void	leadin(void);
85482ca466Swiz static void	oflush(void);
86*d40754b0Schristos static void	pack(const wchar_t *, size_t);
879ac95763Schristos static void	prefix(const struct buffer *, int);
88*d40754b0Schristos static void	split(const wchar_t *, int);
899ac95763Schristos static void	tabulate(struct buffer *);
909ac95763Schristos 
915f8283eaSjdolecek 
92*d40754b0Schristos int		ishead(const wchar_t *);
930c2c736dSlukem 
9461f28255Scgd /*
9561f28255Scgd  * Drive the whole formatter by managing input files.  Also,
9661f28255Scgd  * cause initialization of the output stuff and flush it out
9761f28255Scgd  * at the end.
9861f28255Scgd  */
9961f28255Scgd 
1000c2c736dSlukem int
main(int argc,char ** argv)101482ca466Swiz main(int argc, char **argv)
10261f28255Scgd {
1030c2c736dSlukem 	FILE *fi;
1040c2c736dSlukem 	int errs = 0;
1053d9ab41dSchristos 	int compat = 1;
1063d9ab41dSchristos 	int c;
10761f28255Scgd 
10861f28255Scgd 	goal_length = GOAL_LENGTH;
10961f28255Scgd 	max_length = MAX_LENGTH;
1109ac95763Schristos 	buf_init(&outbuf);
11161f28255Scgd 	lineno = 1;
112bd359116Schristos 	mark = -10;
11386d80fb6Skleink 
1149ac95763Schristos 	setprogname(*argv);
11568e618ceSchristos 	(void)setlocale(LC_ALL, "");
11686d80fb6Skleink 
117cc700845Schristos 	while ((c = getopt(argc, argv, "Cg:m:rw:")) != -1)
1183d9ab41dSchristos 		switch (c) {
1193d9ab41dSchristos 		case 'C':
120ef12b8c8Sabs 			center++;
1213d9ab41dSchristos 			break;
1223d9ab41dSchristos 		case 'g':
1233d9ab41dSchristos 			(void)getnum(optarg, "goal", &goal_length, 1);
1243d9ab41dSchristos 			compat = 0;
1253d9ab41dSchristos 			break;
1263d9ab41dSchristos 		case 'm':
127cc700845Schristos 		case 'w':
1283d9ab41dSchristos 			(void)getnum(optarg, "max", &max_length, 1);
1293d9ab41dSchristos 			compat = 0;
1303d9ab41dSchristos 			break;
1313d9ab41dSchristos 		case 'r':
1323d9ab41dSchristos 			raw++;
1333d9ab41dSchristos 			break;
1343d9ab41dSchristos 		default:
1353d9ab41dSchristos 			usage();
136ef12b8c8Sabs 		}
1373d9ab41dSchristos 
1383d9ab41dSchristos 	argc -= optind;
1393d9ab41dSchristos 	argv += optind;
1403d9ab41dSchristos 
1413d9ab41dSchristos 	/*
1423d9ab41dSchristos 	 * compatibility with old usage.
1433d9ab41dSchristos 	 */
144c3f4be5bSchristos 	if (compat && argc > 0 && getnum(*argv, "goal", &goal_length, 0)) {
14561f28255Scgd 		argv++;
14661f28255Scgd 		argc--;
147c3f4be5bSchristos 		if (argc > 0 && getnum(*argv, "max", &max_length, 0)) {
14861f28255Scgd 			argv++;
14961f28255Scgd 			argc--;
15061f28255Scgd 		}
15161f28255Scgd 	}
1523d9ab41dSchristos 
15361f28255Scgd 	if (max_length <= goal_length) {
1549ac95763Schristos 		errx(1, "Max length (%zu) must be greater than goal "
1559ac95763Schristos 		    "length (%zu)", max_length, goal_length);
15661f28255Scgd 	}
1573d9ab41dSchristos 	if (argc == 0) {
15861f28255Scgd 		fmt(stdin);
15961f28255Scgd 		oflush();
1609ac95763Schristos 		return 0;
16161f28255Scgd 	}
162bb0b4504Schristos 	for (;argc; argc--, argv++) {
163bb0b4504Schristos 		if ((fi = fopen(*argv, "r")) == NULL) {
1649ac95763Schristos 			warn("Cannot open `%s'", *argv);
16561f28255Scgd 			errs++;
16661f28255Scgd 			continue;
16761f28255Scgd 		}
16861f28255Scgd 		fmt(fi);
16968e618ceSchristos 		(void)fclose(fi);
17061f28255Scgd 	}
17161f28255Scgd 	oflush();
1729ac95763Schristos 	buf_end(&outbuf);
1739ac95763Schristos 	return errs;
17461f28255Scgd }
17561f28255Scgd 
1763d9ab41dSchristos static void
usage(void)1773d9ab41dSchristos usage(void)
1783d9ab41dSchristos {
1793d9ab41dSchristos 	(void)fprintf(stderr,
180cc700845Schristos 	    "Usage: %s [-Cr] [-g <goal>] [-m|w <max>] [<files>..]\n"
1813d9ab41dSchristos 	    "\t %s [-Cr] [<goal>] [<max>] [<files>]\n",
1823d9ab41dSchristos 	    getprogname(), getprogname());
1833d9ab41dSchristos 	exit(1);
1843d9ab41dSchristos }
1853d9ab41dSchristos 
1863d9ab41dSchristos static int
getnum(const char * str,const char * what,size_t * res,int badnum)1873d9ab41dSchristos getnum(const char *str, const char *what, size_t *res, int badnum)
1883d9ab41dSchristos {
1893d9ab41dSchristos 	unsigned long ul;
1903d9ab41dSchristos 	char *ep;
1913d9ab41dSchristos 
1923d9ab41dSchristos 	errno = 0;
1933d9ab41dSchristos 	ul = strtoul(str, &ep, 0);
1943d9ab41dSchristos         if (*str != '\0' && *ep == '\0') {
1953d9ab41dSchristos 		 if ((errno == ERANGE && ul == ULONG_MAX) || ul > SIZE_T_MAX)
1963d9ab41dSchristos 			errx(1, "%s number `%s' too big", what, str);
1973d9ab41dSchristos 		*res = (size_t)ul;
1983d9ab41dSchristos 		return 1;
1993d9ab41dSchristos 	} else if (badnum)
2003d9ab41dSchristos 		errx(1, "Bad %s number `%s'", what, str);
2013d9ab41dSchristos 
2023d9ab41dSchristos 	return 0;
2033d9ab41dSchristos }
2043d9ab41dSchristos 
20561f28255Scgd /*
20661f28255Scgd  * Read up characters from the passed input file, forming lines,
20761f28255Scgd  * doing ^H processing, expanding tabs, stripping trailing blanks,
20861f28255Scgd  * and sending each line down for analysis.
20961f28255Scgd  */
2105f8283eaSjdolecek static void
fmt(FILE * fi)211482ca466Swiz fmt(FILE *fi)
21261f28255Scgd {
2139ac95763Schristos 	struct buffer lbuf, cbuf;
214*d40754b0Schristos 	wchar_t *cp, *cp2;
215*d40754b0Schristos 	wint_t c;
216*d40754b0Schristos 	int add_space;
217420b514aSdholland 	size_t len, col, i;
21861f28255Scgd 
219ef12b8c8Sabs 	if (center) {
2209ac95763Schristos 		for (;;) {
221*d40754b0Schristos 			cp = fgetwln(fi, &len);
222ef12b8c8Sabs 			if (!cp)
223ef12b8c8Sabs 				return;
224420b514aSdholland 
225420b514aSdholland 			/* skip over leading space */
226420b514aSdholland 			while (len > 0) {
227*d40754b0Schristos 				if (!iswspace(*cp))
228420b514aSdholland 					break;
229ef12b8c8Sabs 				cp++;
230420b514aSdholland 				len--;
231420b514aSdholland 			}
232420b514aSdholland 
233420b514aSdholland 			/* clear trailing space */
234420b514aSdholland 			while (len > 0) {
235*d40754b0Schristos 				if (!iswspace((unsigned char)cp[len-1]))
236420b514aSdholland 					break;
237420b514aSdholland 				len--;
238420b514aSdholland 			}
239420b514aSdholland 
240420b514aSdholland 			if (len == 0) {
241420b514aSdholland 				/* blank line */
242*d40754b0Schristos 				(void)putwchar(L'\n');
243420b514aSdholland 				continue;
244420b514aSdholland 			}
245420b514aSdholland 
246420b514aSdholland 			if (goal_length > len) {
247420b514aSdholland 				for (i = 0; i < (goal_length - len) / 2; i++) {
248*d40754b0Schristos 					(void)putwchar(L' ');
249420b514aSdholland 				}
250420b514aSdholland 			}
251420b514aSdholland 			for (i = 0; i < len; i++) {
252*d40754b0Schristos 				(void)putwchar(cp[i]);
253420b514aSdholland 			}
254*d40754b0Schristos 			(void)putwchar(L'\n');
255ef12b8c8Sabs 		}
256ef12b8c8Sabs 	}
2579ac95763Schristos 
2589ac95763Schristos 	buf_init(&lbuf);
2599ac95763Schristos 	buf_init(&cbuf);
260*d40754b0Schristos 	c = getwc(fi);
2619ac95763Schristos 
262*d40754b0Schristos 	while (c != WEOF) {
26361f28255Scgd 		/*
26461f28255Scgd 		 * Collect a line, doing ^H processing.
26561f28255Scgd 		 * Leave tabs for now.
26661f28255Scgd 		 */
2679ac95763Schristos 		buf_reset(&lbuf);
268*d40754b0Schristos 		while (c != '\n' && c != WEOF) {
26961f28255Scgd 			if (c == '\b') {
27068e618ceSchristos 				(void)buf_unputc(&lbuf);
271*d40754b0Schristos 				c = getwc(fi);
27261f28255Scgd 				continue;
27361f28255Scgd 			}
274*d40754b0Schristos 			if(!(iswprint(c) || c == '\t' || c >= 160)) {
275*d40754b0Schristos 				c = getwc(fi);
27661f28255Scgd 				continue;
27761f28255Scgd 			}
2789ac95763Schristos 			buf_putc(&lbuf, c);
279*d40754b0Schristos 			c = getwc(fi);
28061f28255Scgd 		}
2819ac95763Schristos 		buf_putc(&lbuf, '\0');
28268e618ceSchristos 		(void)buf_unputc(&lbuf);
283*d40754b0Schristos 		add_space = c != WEOF;
28461f28255Scgd 
28561f28255Scgd 		/*
2869ac95763Schristos 		 * Expand tabs on the way.
28761f28255Scgd 		 */
28861f28255Scgd 		col = 0;
2899ac95763Schristos 		cp = lbuf.bptr;
2909ac95763Schristos 		buf_reset(&cbuf);
2919ac95763Schristos 		while ((c = *cp++) != '\0') {
29261f28255Scgd 			if (c != '\t') {
29361f28255Scgd 				col++;
2949ac95763Schristos 				buf_putc(&cbuf, c);
29561f28255Scgd 				continue;
29661f28255Scgd 			}
29761f28255Scgd 			do {
2989ac95763Schristos 				buf_putc(&cbuf, ' ');
29961f28255Scgd 				col++;
30061f28255Scgd 			} while ((col & 07) != 0);
30161f28255Scgd 		}
30261f28255Scgd 
30361f28255Scgd 		/*
30461f28255Scgd 		 * Swipe trailing blanks from the line.
30561f28255Scgd 		 */
3069ac95763Schristos 		for (cp2 = cbuf.ptr - 1; cp2 >= cbuf.bptr && *cp2 == ' '; cp2--)
3079ac95763Schristos 			continue;
3089ac95763Schristos 		cbuf.ptr = cp2 + 1;
3099ac95763Schristos 		buf_putc(&cbuf, '\0');
31068e618ceSchristos 		(void)buf_unputc(&cbuf);
3119ac95763Schristos 		prefix(&cbuf, add_space);
312*d40754b0Schristos 		if (c != WEOF)
313*d40754b0Schristos 			c = getwc(fi);
31461f28255Scgd 	}
3159ac95763Schristos 	buf_end(&cbuf);
3169ac95763Schristos 	buf_end(&lbuf);
31761f28255Scgd }
31861f28255Scgd 
31961f28255Scgd /*
32061f28255Scgd  * Take a line devoid of tabs and other garbage and determine its
32161f28255Scgd  * blank prefix.  If the indent changes, call for a linebreak.
32261f28255Scgd  * If the input line is blank, echo the blank line on the output.
32361f28255Scgd  * Finally, if the line minus the prefix is a mail header, try to keep
32461f28255Scgd  * it on a line by itself.
32561f28255Scgd  */
3265f8283eaSjdolecek static void
prefix(const struct buffer * buf,int add_space)3279ac95763Schristos prefix(const struct buffer *buf, int add_space)
32861f28255Scgd {
329*d40754b0Schristos 	const wchar_t *cp;
330*d40754b0Schristos 	const wchar_t **hp;
3319ac95763Schristos 	size_t np;
3329ac95763Schristos 	int h;
33361f28255Scgd 
3349ac95763Schristos 	if (buf->ptr == buf->bptr) {
33561f28255Scgd 		oflush();
336*d40754b0Schristos 		(void)putwchar(L'\n');
33761f28255Scgd 		return;
33861f28255Scgd 	}
3399ac95763Schristos 	for (cp = buf->bptr; *cp == ' '; cp++)
3409ac95763Schristos 		continue;
3419ac95763Schristos 	np = cp - buf->bptr;
34261f28255Scgd 
34361f28255Scgd 	/*
34461f28255Scgd 	 * The following horrible expression attempts to avoid linebreaks
34561f28255Scgd 	 * when the indent changes due to a paragraph.
34661f28255Scgd 	 */
3479ac95763Schristos 	if (np != pfx && (np > pfx || abs((int)(pfx - np)) > 8))
34861f28255Scgd 		oflush();
3493d9ab41dSchristos 	if (!raw) {
3509ac95763Schristos 		if ((h = ishead(cp)) != 0) {
3519ac95763Schristos 			oflush();
3529ac95763Schristos 			mark = lineno;
3539ac95763Schristos 		}
354bd359116Schristos 		if (lineno - mark < 3 && lineno - mark > 0)
3559ac95763Schristos 			for (hp = &headnames[0]; *hp != NULL; hp++)
35661f28255Scgd 				if (ispref(*hp, cp)) {
35761f28255Scgd 					h = 1;
35861f28255Scgd 					oflush();
35961f28255Scgd 					break;
36061f28255Scgd 				}
36161f28255Scgd 		if (!h && (h = (*cp == '.')))
36261f28255Scgd 			oflush();
3633d9ab41dSchristos 	} else
3643d9ab41dSchristos 		h = 0;
36561f28255Scgd 	pfx = np;
3665f8283eaSjdolecek 	if (h) {
3679ac95763Schristos 		pack(cp, (size_t)(buf->ptr - cp));
36861f28255Scgd 		oflush();
3695f8283eaSjdolecek 	} else
3705f8283eaSjdolecek 		split(cp, add_space);
37161f28255Scgd 	lineno++;
37261f28255Scgd }
37361f28255Scgd 
37461f28255Scgd /*
37561f28255Scgd  * Split up the passed line into output "words" which are
37661f28255Scgd  * maximal strings of non-blanks with the blank separation
37761f28255Scgd  * attached at the end.  Pass these words along to the output
37861f28255Scgd  * line packer.
37961f28255Scgd  */
3805f8283eaSjdolecek static void
split(const wchar_t line[],int add_space)381*d40754b0Schristos split(const wchar_t line[], int add_space)
38261f28255Scgd {
383*d40754b0Schristos 	const wchar_t *cp;
3849ac95763Schristos 	struct buffer word;
3859ac95763Schristos 	size_t wlen;
38661f28255Scgd 
3879ac95763Schristos 	buf_init(&word);
38861f28255Scgd 	cp = line;
38961f28255Scgd 	while (*cp) {
3909ac95763Schristos 		buf_reset(&word);
3919ac95763Schristos 		wlen = 0;
39261f28255Scgd 
39361f28255Scgd 		/*
39461f28255Scgd 		 * Collect a 'word,' allowing it to contain escaped white
39561f28255Scgd 		 * space.
39661f28255Scgd 		 */
39761f28255Scgd 		while (*cp && *cp != ' ') {
398*d40754b0Schristos 			if (*cp == '\\' && iswspace(cp[1]))
3999ac95763Schristos 				buf_putc(&word, *cp++);
4009ac95763Schristos 			buf_putc(&word, *cp++);
4019ac95763Schristos 			wlen++;
40261f28255Scgd 		}
40361f28255Scgd 
40461f28255Scgd 		/*
40561f28255Scgd 		 * Guarantee a space at end of line. Two spaces after end of
40661f28255Scgd 		 * sentence punctuation.
40761f28255Scgd 		 */
4085f8283eaSjdolecek 		if (*cp == '\0' && add_space) {
4099ac95763Schristos 			buf_putc(&word, ' ');
4106533f941Slukem 			if (strchr(".:!", cp[-1]))
4119ac95763Schristos 				buf_putc(&word, ' ');
41261f28255Scgd 		}
41361f28255Scgd 		while (*cp == ' ')
4149ac95763Schristos 			buf_putc(&word, *cp++);
4159ac95763Schristos 
4169ac95763Schristos 		buf_putc(&word, '\0');
41768e618ceSchristos 		(void)buf_unputc(&word);
4189ac95763Schristos 
4199ac95763Schristos 		pack(word.bptr, wlen);
42061f28255Scgd 	}
4219ac95763Schristos 	buf_end(&word);
42261f28255Scgd }
42361f28255Scgd 
42461f28255Scgd /*
42561f28255Scgd  * Output section.
42661f28255Scgd  * Build up line images from the words passed in.  Prefix
42775bdcdeaSchristos  * each line with correct number of blanks.
42875bdcdeaSchristos  *
42975bdcdeaSchristos  * At the bottom of this whole mess, leading tabs are reinserted.
43061f28255Scgd  */
43161f28255Scgd 
43261f28255Scgd /*
43361f28255Scgd  * Pack a word onto the output line.  If this is the beginning of
43461f28255Scgd  * the line, push on the appropriately-sized string of blanks first.
43561f28255Scgd  * If the word won't fit on the current line, flush and begin a new
43661f28255Scgd  * line.  If the word is too long to fit all by itself on a line,
43761f28255Scgd  * just give it its own and hope for the best.
43861f28255Scgd  *
43961f28255Scgd  * LIZ@UOM 6/18/85 -- If the new word will fit in at less than the
44061f28255Scgd  *	goal length, take it.  If not, then check to see if the line
44161f28255Scgd  *	will be over the max length; if so put the word on the next
44261f28255Scgd  *	line.  If not, check to see if the line will be closer to the
44361f28255Scgd  *	goal length with or without the word and take it or put it on
44461f28255Scgd  *	the next line accordingly.
44561f28255Scgd  */
44661f28255Scgd 
4475f8283eaSjdolecek static void
pack(const wchar_t * word,size_t wlen)448*d40754b0Schristos pack(const wchar_t *word, size_t wlen)
44961f28255Scgd {
450*d40754b0Schristos 	const wchar_t *cp;
4519ac95763Schristos 	size_t s, t;
45261f28255Scgd 
4539ac95763Schristos 	if (outbuf.bptr == outbuf.ptr)
45461f28255Scgd 		leadin();
45561f28255Scgd 	/*
45661f28255Scgd 	 * LIZ@UOM 6/18/85 -- change condition to check goal_length; s is the
45761f28255Scgd 	 * length of the line before the word is added; t is now the length
45861f28255Scgd 	 * of the line after the word is added
45961f28255Scgd 	 */
4609ac95763Schristos 	s = outbuf.ptr - outbuf.bptr;
4619ac95763Schristos 	t = wlen + s;
462e4a41ed5Schristos 	if ((t <= goal_length) || ((t <= max_length) &&
463e4a41ed5Schristos 	    (s <= goal_length) && (t - goal_length <= goal_length - s))) {
46461f28255Scgd 		/*
46561f28255Scgd 		 * In like flint!
46661f28255Scgd 		 */
4679ac95763Schristos 		for (cp = word; *cp;)
4689ac95763Schristos 			buf_putc(&outbuf, *cp++);
46961f28255Scgd 		return;
47061f28255Scgd 	}
47161f28255Scgd 	if (s > pfx) {
47261f28255Scgd 		oflush();
47361f28255Scgd 		leadin();
47461f28255Scgd 	}
4759ac95763Schristos 	for (cp = word; *cp;)
4769ac95763Schristos 		buf_putc(&outbuf, *cp++);
47761f28255Scgd }
47861f28255Scgd 
47961f28255Scgd /*
48061f28255Scgd  * If there is anything on the current output line, send it on
48175bdcdeaSchristos  * its way.  Reset outbuf.
48261f28255Scgd  */
4835f8283eaSjdolecek static void
oflush(void)484482ca466Swiz oflush(void)
48561f28255Scgd {
4869ac95763Schristos 	if (outbuf.bptr == outbuf.ptr)
48761f28255Scgd 		return;
4889ac95763Schristos 	buf_putc(&outbuf, '\0');
48968e618ceSchristos 	(void)buf_unputc(&outbuf);
4909ac95763Schristos 	tabulate(&outbuf);
4919ac95763Schristos 	buf_reset(&outbuf);
49261f28255Scgd }
49361f28255Scgd 
49461f28255Scgd /*
49561f28255Scgd  * Take the passed line buffer, insert leading tabs where possible, and
49661f28255Scgd  * output on standard output (finally).
49761f28255Scgd  */
4985f8283eaSjdolecek static void
tabulate(struct buffer * buf)4999ac95763Schristos tabulate(struct buffer *buf)
50061f28255Scgd {
501*d40754b0Schristos 	wchar_t *cp;
5029ac95763Schristos 	size_t b, t;
50361f28255Scgd 
50461f28255Scgd 	/*
50561f28255Scgd 	 * Toss trailing blanks in the output line.
50661f28255Scgd 	 */
5079ebf6192Schristos 	for (cp = buf->ptr - 1; cp >= buf->bptr && *cp == ' '; cp--)
5089ac95763Schristos 		continue;
5099ebf6192Schristos 	*++cp = '\0';
51061f28255Scgd 
51161f28255Scgd 	/*
51261f28255Scgd 	 * Count the leading blank space and tabulate.
51361f28255Scgd 	 */
5149ac95763Schristos 	for (cp = buf->bptr; *cp == ' '; cp++)
5159ac95763Schristos 		continue;
5169ac95763Schristos 	b = cp - buf->bptr;
51775bdcdeaSchristos 	t = b / 8;
51875bdcdeaSchristos 	b = b % 8;
51961f28255Scgd 	if (t > 0)
52061f28255Scgd 		do
521*d40754b0Schristos 			(void)putwchar(L'\t');
52261f28255Scgd 		while (--t);
52361f28255Scgd 	if (b > 0)
52461f28255Scgd 		do
525*d40754b0Schristos 			(void)putwchar(L' ');
52661f28255Scgd 		while (--b);
52761f28255Scgd 	while (*cp)
528*d40754b0Schristos 		(void)putwchar(*cp++);
529*d40754b0Schristos 	(void)putwchar(L'\n');
53061f28255Scgd }
53161f28255Scgd 
53261f28255Scgd /*
53361f28255Scgd  * Initialize the output line with the appropriate number of
53461f28255Scgd  * leading blanks.
53561f28255Scgd  */
5365f8283eaSjdolecek static void
leadin(void)537482ca466Swiz leadin(void)
53861f28255Scgd {
5399ac95763Schristos 	size_t b;
54061f28255Scgd 
5419ac95763Schristos 	buf_reset(&outbuf);
5429ac95763Schristos 
5439ac95763Schristos 	for (b = 0; b < pfx; b++)
5449ac95763Schristos 		buf_putc(&outbuf, ' ');
54561f28255Scgd }
54661f28255Scgd 
54761f28255Scgd /*
54861f28255Scgd  * Is s1 a prefix of s2??
54961f28255Scgd  */
5505f8283eaSjdolecek static int
ispref(const wchar_t * s1,const wchar_t * s2)551*d40754b0Schristos ispref(const wchar_t *s1, const wchar_t *s2)
55261f28255Scgd {
55361f28255Scgd 
55461f28255Scgd 	while (*s1++ == *s2)
5559ac95763Schristos 		continue;
5569ac95763Schristos 	return *s1 == '\0';
55761f28255Scgd }
558