xref: /onnv-gate/usr/src/cmd/fmt/fmt.c (revision 7308:e8067e7e46f8)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57307SJohn.Sonnenschein@Sun.COM  * Common Development and Distribution License (the "License").
67307SJohn.Sonnenschein@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
227307SJohn.Sonnenschein@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <stdio.h>
310Sstevel@tonic-gate #include <stdlib.h>
320Sstevel@tonic-gate #include <ctype.h>
330Sstevel@tonic-gate #include <wctype.h>
340Sstevel@tonic-gate #include <widec.h>
350Sstevel@tonic-gate #include <dlfcn.h>
360Sstevel@tonic-gate #include <locale.h>
370Sstevel@tonic-gate #include <sys/param.h>
380Sstevel@tonic-gate #include <string.h>
390Sstevel@tonic-gate 
400Sstevel@tonic-gate /*
410Sstevel@tonic-gate  * fmt -- format the concatenation of input files or standard input
420Sstevel@tonic-gate  * onto standard output.  Designed for use with Mail ~|
430Sstevel@tonic-gate  *
440Sstevel@tonic-gate  * Syntax: fmt [ -width | -w width ] [ -cs ] [ name ... ]
450Sstevel@tonic-gate  * Author: Kurt Shoens (UCB) 12/7/78
460Sstevel@tonic-gate  */
470Sstevel@tonic-gate 
48373Sceastha #define	NOSTR	((wchar_t *)0)	/* Null string pointer for lint */
490Sstevel@tonic-gate #define	MAXLINES	100	/* maximum mail header lines to verify */
500Sstevel@tonic-gate 
510Sstevel@tonic-gate wchar_t	outbuf[BUFSIZ];			/* Sandbagged output line image */
520Sstevel@tonic-gate wchar_t	*outp;				/* Pointer in above */
530Sstevel@tonic-gate int	filler;				/* Filler amount in outbuf */
547307SJohn.Sonnenschein@Sun.COM char sobuf[BUFSIZ];	/* Global buffer */
550Sstevel@tonic-gate 
560Sstevel@tonic-gate int	pfx;			/* Current leading blank count */
570Sstevel@tonic-gate int	width = 72;		/* Width that we will not exceed */
580Sstevel@tonic-gate int	nojoin = 0;		/* split lines only, don't join short ones */
590Sstevel@tonic-gate int	errs = 0;		/* Current number of errors */
600Sstevel@tonic-gate 
610Sstevel@tonic-gate enum crown_type	{c_none, c_reset, c_head, c_lead, c_fixup, c_body};
620Sstevel@tonic-gate enum crown_type	crown_state;	/* Crown margin state */
630Sstevel@tonic-gate int	crown_head;		/* The header offset */
640Sstevel@tonic-gate int	crown_body;		/* The body offset */
650Sstevel@tonic-gate 	/* currently-known initial strings found in mail headers */
660Sstevel@tonic-gate wchar_t	*headnames[] = {
670Sstevel@tonic-gate 	L"Apparently-To", L"Bcc", L"bcc", L"Cc", L"cc", L"Confirmed-By",
680Sstevel@tonic-gate 	L"Content", L"content-length", L"From", L"Date", L"id",
690Sstevel@tonic-gate 	L"Message-I", L"MIME-Version", L"Precedence", L"Return-Path",
700Sstevel@tonic-gate 	L"Received", L"Reply-To", L"Status", L"Subject", L"To", L"X-IMAP",
710Sstevel@tonic-gate 	L"X-Lines", L"X-Sender", L"X-Sun", L"X-Status", L"X-UID",
720Sstevel@tonic-gate 	0};
730Sstevel@tonic-gate 
740Sstevel@tonic-gate enum hdr_type {
750Sstevel@tonic-gate 	off,		/* mail header processing is off */
760Sstevel@tonic-gate 	not_in_hdr,	/* not currently processing a mail header */
770Sstevel@tonic-gate 	in_hdr, 	/* currently filling hdrbuf with potential hdr lines */
780Sstevel@tonic-gate 	flush_hdr,	/* flush hdrbuf; not a header, no special processing */
790Sstevel@tonic-gate 	do_hdr		/* process hdrbuf as a mail header */
800Sstevel@tonic-gate };
810Sstevel@tonic-gate 				/* current state of hdrbuf */
820Sstevel@tonic-gate enum hdr_type	hdr_state = not_in_hdr;
830Sstevel@tonic-gate 
840Sstevel@tonic-gate wchar_t *hdrbuf[MAXLINES];	/* buffer to hold potential mail header lines */
850Sstevel@tonic-gate int 	h_lines;		/* index into lines of hdrbuf */
860Sstevel@tonic-gate 
87373Sceastha void (*(split))(wchar_t []);
880Sstevel@tonic-gate extern int scrwidth(wchar_t);
89373Sceastha extern int ishead(char []);
900Sstevel@tonic-gate 
91373Sceastha 
92373Sceastha static void fill_hdrbuf(wchar_t []);
930Sstevel@tonic-gate static void header_chk(void);
940Sstevel@tonic-gate static void process_hdrbuf(void);
95373Sceastha static void leadin(void);
96373Sceastha static void tabulate(wchar_t []);
97373Sceastha static void oflush(void);
98373Sceastha static void pack(wchar_t []);
99373Sceastha static void msplit(wchar_t []);
100373Sceastha static void csplit(wchar_t []);
101373Sceastha static void _wckind_init(void);
102373Sceastha static void prefix(wchar_t []);
103373Sceastha static void fmt(FILE *);
104373Sceastha static int setopt(char *);
105373Sceastha int _wckind(wchar_t);
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate /*
1080Sstevel@tonic-gate  * Drive the whole formatter by managing input files.  Also,
1090Sstevel@tonic-gate  * cause initialization of the output stuff and flush it out
1100Sstevel@tonic-gate  * at the end.
1110Sstevel@tonic-gate  */
1120Sstevel@tonic-gate 
113373Sceastha int
main(int argc,char ** argv)1140Sstevel@tonic-gate main(int argc, char **argv)
1150Sstevel@tonic-gate {
116373Sceastha 	FILE *fi;
117373Sceastha 	char *cp;
1180Sstevel@tonic-gate 	int nofile;
1190Sstevel@tonic-gate 	char *locale;
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	outp = NOSTR;
1220Sstevel@tonic-gate 	setbuf(stdout, sobuf);
1230Sstevel@tonic-gate 	setlocale(LC_ALL, "");
1240Sstevel@tonic-gate 	locale = setlocale(LC_CTYPE, "");
1250Sstevel@tonic-gate 	if (strcmp(locale, "C") == 0) {
1260Sstevel@tonic-gate 		split = csplit;
1270Sstevel@tonic-gate 	} else {
1280Sstevel@tonic-gate 		split = msplit;
129373Sceastha 		_wckind_init();
1300Sstevel@tonic-gate 	}
1310Sstevel@tonic-gate 	if (argc < 2) {
1320Sstevel@tonic-gate single:
1330Sstevel@tonic-gate 		fmt(stdin);
1340Sstevel@tonic-gate 		oflush();
1350Sstevel@tonic-gate 		exit(0);
1360Sstevel@tonic-gate 	}
1370Sstevel@tonic-gate 	nofile = 1;
1380Sstevel@tonic-gate 	while (--argc) {
1390Sstevel@tonic-gate 		cp = *++argv;
1400Sstevel@tonic-gate 		if (setopt(cp))
1410Sstevel@tonic-gate 			continue;
1420Sstevel@tonic-gate 		nofile = 0;
1430Sstevel@tonic-gate 		if ((fi = fopen(cp, "r")) == NULL) {
1440Sstevel@tonic-gate 			perror(cp);
1450Sstevel@tonic-gate 			errs++;
1460Sstevel@tonic-gate 			continue;
1470Sstevel@tonic-gate 		}
1480Sstevel@tonic-gate 		fmt(fi);
1490Sstevel@tonic-gate 		fclose(fi);
1500Sstevel@tonic-gate 	}
1510Sstevel@tonic-gate 	if (nofile)
1520Sstevel@tonic-gate 		goto single;
1530Sstevel@tonic-gate 	oflush();
1541130Sakaplan 	fclose(stdout);
155373Sceastha 	return (errs);
1560Sstevel@tonic-gate }
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate /*
1590Sstevel@tonic-gate  * Read up characters from the passed input file, forming lines,
1600Sstevel@tonic-gate  * doing ^H processing, expanding tabs, stripping trailing blanks,
1610Sstevel@tonic-gate  * and sending each line down for analysis.
1620Sstevel@tonic-gate  */
1630Sstevel@tonic-gate 
164373Sceastha static void
fmt(FILE * fi)1650Sstevel@tonic-gate fmt(FILE *fi)
1660Sstevel@tonic-gate {
1670Sstevel@tonic-gate 	wchar_t linebuf[BUFSIZ], canonb[BUFSIZ];
168373Sceastha 	wchar_t *cp, *cp2;
169373Sceastha 	int col;
1700Sstevel@tonic-gate 	wchar_t	c;
1710Sstevel@tonic-gate 	char	cbuf[BUFSIZ];	/* stores wchar_t string as char string */
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	c = getwc(fi);
1740Sstevel@tonic-gate 	while (c != EOF) {
1750Sstevel@tonic-gate 		/*
1760Sstevel@tonic-gate 		 * Collect a line, doing ^H processing.
1770Sstevel@tonic-gate 		 * Leave tabs for now.
1780Sstevel@tonic-gate 		 */
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 		cp = linebuf;
1810Sstevel@tonic-gate 		while (c != L'\n' && c != EOF && cp-linebuf < BUFSIZ-1) {
1820Sstevel@tonic-gate 			if (c == L'\b') {
1830Sstevel@tonic-gate 				if (cp > linebuf)
1840Sstevel@tonic-gate 					cp--;
1850Sstevel@tonic-gate 				c = getwc(fi);
1860Sstevel@tonic-gate 				continue;
1870Sstevel@tonic-gate 			}
1880Sstevel@tonic-gate 			if (!(iswprint(c)) && c != L'\t') {
1890Sstevel@tonic-gate 				c = getwc(fi);
1900Sstevel@tonic-gate 				continue;
1910Sstevel@tonic-gate 			}
1920Sstevel@tonic-gate 			*cp++ = c;
1930Sstevel@tonic-gate 			c = getwc(fi);
1940Sstevel@tonic-gate 		}
1950Sstevel@tonic-gate 		*cp = L'\0';
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 		/*
1980Sstevel@tonic-gate 		 * Toss anything remaining on the input line.
1990Sstevel@tonic-gate 		 */
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 		while (c != L'\n' && c != EOF)
2020Sstevel@tonic-gate 			c = getwc(fi);
2030Sstevel@tonic-gate 		/*
2040Sstevel@tonic-gate 		 * Expand tabs on the way to canonb.
2050Sstevel@tonic-gate 		 */
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 		col = 0;
2080Sstevel@tonic-gate 		cp = linebuf;
2090Sstevel@tonic-gate 		cp2 = canonb;
2100Sstevel@tonic-gate 		while (c = *cp++) {
2110Sstevel@tonic-gate 			if (c != L'\t') {
2120Sstevel@tonic-gate 				col += scrwidth(c);
2130Sstevel@tonic-gate 				if (cp2-canonb < BUFSIZ-1)
2140Sstevel@tonic-gate 					*cp2++ = c;
2150Sstevel@tonic-gate 				continue;
2160Sstevel@tonic-gate 			}
2170Sstevel@tonic-gate 			do {
2180Sstevel@tonic-gate 				if (cp2-canonb < BUFSIZ-1)
2190Sstevel@tonic-gate 					*cp2++ = L' ';
2200Sstevel@tonic-gate 				col++;
2210Sstevel@tonic-gate 			} while ((col & 07) != 0);
2220Sstevel@tonic-gate 		}
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 		/*
2250Sstevel@tonic-gate 		 * Swipe trailing blanks from the line.
2260Sstevel@tonic-gate 		 */
2270Sstevel@tonic-gate 
2287307SJohn.Sonnenschein@Sun.COM 		for (cp2--; cp2 >= canonb && *cp2 == L' '; cp2--) {
2297307SJohn.Sonnenschein@Sun.COM 		}
2300Sstevel@tonic-gate 		*++cp2 = '\0';
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 			/* special processing to look for mail header lines */
2330Sstevel@tonic-gate 		switch (hdr_state) {
2340Sstevel@tonic-gate 		case off:
2350Sstevel@tonic-gate 			prefix(canonb);
2360Sstevel@tonic-gate 		case not_in_hdr:
2370Sstevel@tonic-gate 			/* look for an initial mail header line */
2380Sstevel@tonic-gate 			/* skip initial blanks */
2397307SJohn.Sonnenschein@Sun.COM 			for (cp = canonb; *cp == L' '; cp++) {
2407307SJohn.Sonnenschein@Sun.COM 			}
2410Sstevel@tonic-gate 			/*
2420Sstevel@tonic-gate 			 * Need to convert string from wchar_t to char,
2430Sstevel@tonic-gate 			 * since this is what ishead() expects.  Since we
2440Sstevel@tonic-gate 			 * only want to make sure cp points to a "From" line
2450Sstevel@tonic-gate 			 * of the email, we don't have to alloc
2460Sstevel@tonic-gate 			 * BUFSIZ * MB_LEN_MAX to cbuf.
2470Sstevel@tonic-gate 			 */
2480Sstevel@tonic-gate 			wcstombs(cbuf, cp, (BUFSIZ - 1));
2490Sstevel@tonic-gate 			if (ishead(cbuf)) {
2500Sstevel@tonic-gate 				hdr_state = in_hdr;
2510Sstevel@tonic-gate 				fill_hdrbuf(canonb);
2520Sstevel@tonic-gate 			} else {
2530Sstevel@tonic-gate 				/* no mail header line; process normally */
2540Sstevel@tonic-gate 				prefix(canonb);
2550Sstevel@tonic-gate 			}
2560Sstevel@tonic-gate 			break;
2570Sstevel@tonic-gate 		case in_hdr:
2580Sstevel@tonic-gate 			/* already saw 1st mail header line; look for more */
2590Sstevel@tonic-gate 			if (canonb[0] == L'\0') {
2600Sstevel@tonic-gate 				/*
2610Sstevel@tonic-gate 				 * blank line means end of mail header;
2620Sstevel@tonic-gate 				 * verify current mail header buffer
2630Sstevel@tonic-gate 				 * then process it accordingly
2640Sstevel@tonic-gate 				 */
2650Sstevel@tonic-gate 				header_chk();
2660Sstevel@tonic-gate 				process_hdrbuf();
2670Sstevel@tonic-gate 				/* now process the current blank line */
2680Sstevel@tonic-gate 				prefix(canonb);
2690Sstevel@tonic-gate 			} else
2700Sstevel@tonic-gate 				/*
2710Sstevel@tonic-gate 				 * not a blank line--save this line as
2720Sstevel@tonic-gate 				 * a potential mail header line
2730Sstevel@tonic-gate 				 */
2740Sstevel@tonic-gate 				fill_hdrbuf(canonb);
2750Sstevel@tonic-gate 			break;
2760Sstevel@tonic-gate 		}
2770Sstevel@tonic-gate 		if (c != EOF)
2780Sstevel@tonic-gate 			c = getwc(fi);
2790Sstevel@tonic-gate 	}
2800Sstevel@tonic-gate 	/*
2810Sstevel@tonic-gate 	 * end of this file--make sure we process the stuff in
2820Sstevel@tonic-gate 	 * hdrbuf before we're finished
2830Sstevel@tonic-gate 	 */
2840Sstevel@tonic-gate 	if (hdr_state == in_hdr) {
2850Sstevel@tonic-gate 		header_chk();
2860Sstevel@tonic-gate 		process_hdrbuf();
2870Sstevel@tonic-gate 	}
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate /*
2910Sstevel@tonic-gate  * Take a line devoid of tabs and other garbage and determine its
2920Sstevel@tonic-gate  * blank prefix.  If the indent changes, call for a linebreak.
2930Sstevel@tonic-gate  * If the input line is blank, echo the blank line on the output.
2940Sstevel@tonic-gate  * Finally, if the line minus the prefix is a mail header, try to keep
2950Sstevel@tonic-gate  * it on a line by itself.
2960Sstevel@tonic-gate  */
2970Sstevel@tonic-gate 
298373Sceastha static void
prefix(wchar_t line[])2990Sstevel@tonic-gate prefix(wchar_t line[])
3000Sstevel@tonic-gate {
301373Sceastha 	wchar_t *cp;
302373Sceastha 	int np;
3030Sstevel@tonic-gate 	int nosplit = 0;	/* flag set if line should not be split */
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	if (line[0] == L'\0') {
3060Sstevel@tonic-gate 		oflush();
3070Sstevel@tonic-gate 		putchar('\n');
3080Sstevel@tonic-gate 		if (crown_state != c_none)
3090Sstevel@tonic-gate 			crown_state = c_reset;
3100Sstevel@tonic-gate 		return;
3110Sstevel@tonic-gate 	}
3127307SJohn.Sonnenschein@Sun.COM 	for (cp = line; *cp == L' '; cp++) {
3137307SJohn.Sonnenschein@Sun.COM 	}
3140Sstevel@tonic-gate 	np = cp - line;
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	/*
3170Sstevel@tonic-gate 	 * The following horrible expression attempts to avoid linebreaks
3180Sstevel@tonic-gate 	 * when the indent changes due to a paragraph.
3190Sstevel@tonic-gate 	 */
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	if (crown_state == c_none && np != pfx && (np > pfx || abs(pfx-np) > 8))
3220Sstevel@tonic-gate 		oflush();
3230Sstevel@tonic-gate 	/*
3240Sstevel@tonic-gate 	 * if this is a mail header line, don't split it; flush previous
3250Sstevel@tonic-gate 	 * line, if any, so we don't join this line to it
3260Sstevel@tonic-gate 	 */
3270Sstevel@tonic-gate 	if (hdr_state == do_hdr) {
3280Sstevel@tonic-gate 		nosplit = 1;
3290Sstevel@tonic-gate 		oflush();
3300Sstevel@tonic-gate 	}
3310Sstevel@tonic-gate 	/* flush previous line so we don't join this one to it */
3320Sstevel@tonic-gate 	if (nojoin)
3330Sstevel@tonic-gate 		oflush();
3340Sstevel@tonic-gate 	/* nroff-type lines starting with '.' are not split nor joined */
3350Sstevel@tonic-gate 	if (!nosplit && (nosplit = (*cp == L'.')))
3360Sstevel@tonic-gate 		oflush();
3370Sstevel@tonic-gate 	pfx = np;
3380Sstevel@tonic-gate 	switch (crown_state) {
3390Sstevel@tonic-gate 	case c_reset:
3400Sstevel@tonic-gate 		crown_head = pfx;
3410Sstevel@tonic-gate 		crown_state = c_head;
3420Sstevel@tonic-gate 		break;
3430Sstevel@tonic-gate 	case c_lead:
3440Sstevel@tonic-gate 		crown_body = pfx;
3450Sstevel@tonic-gate 		crown_state = c_body;
3460Sstevel@tonic-gate 		break;
3470Sstevel@tonic-gate 	case c_fixup:
3480Sstevel@tonic-gate 		crown_body = pfx;
3490Sstevel@tonic-gate 		crown_state = c_body;
3500Sstevel@tonic-gate 		if (outp) {
3510Sstevel@tonic-gate 			wchar_t s[BUFSIZ];
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 			*outp = L'\0';
3540Sstevel@tonic-gate 			wscpy(s, &outbuf[crown_head]);
3550Sstevel@tonic-gate 			outp = NOSTR;
3560Sstevel@tonic-gate 			split(s);
3570Sstevel@tonic-gate 		}
3580Sstevel@tonic-gate 		break;
3590Sstevel@tonic-gate 	}
3600Sstevel@tonic-gate 	if (nosplit) {
3610Sstevel@tonic-gate 		/* put whole input line onto outbuf and print it out */
3620Sstevel@tonic-gate 		pack(cp);
3630Sstevel@tonic-gate 		oflush();
3640Sstevel@tonic-gate 	} else
3650Sstevel@tonic-gate 		/*
3660Sstevel@tonic-gate 		 * split puts current line onto outbuf, but splits it
3670Sstevel@tonic-gate 		 * at word boundaries, if it exceeds desired length
3680Sstevel@tonic-gate 		 */
3690Sstevel@tonic-gate 		split(cp);
3700Sstevel@tonic-gate 	if (nojoin)
3710Sstevel@tonic-gate 		/*
3720Sstevel@tonic-gate 		 * flush current line so next lines, if any,
3730Sstevel@tonic-gate 		 * won't join to this one
3740Sstevel@tonic-gate 		 */
3750Sstevel@tonic-gate 		oflush();
3760Sstevel@tonic-gate }
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate /*
3790Sstevel@tonic-gate  * Split up the passed line into output "words" which are
3800Sstevel@tonic-gate  * maximal strings of non-blanks with the blank separation
3810Sstevel@tonic-gate  * attached at the end.  Pass these words along to the output
3820Sstevel@tonic-gate  * line packer.
3830Sstevel@tonic-gate  */
3840Sstevel@tonic-gate 
385373Sceastha static void
csplit(wchar_t line[])3860Sstevel@tonic-gate csplit(wchar_t line[])
3870Sstevel@tonic-gate {
388373Sceastha 	wchar_t *cp, *cp2;
3890Sstevel@tonic-gate 	wchar_t word[BUFSIZ];
3900Sstevel@tonic-gate 	static const wchar_t *srchlist = (const wchar_t *) L".:!?";
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	cp = line;
3930Sstevel@tonic-gate 	while (*cp) {
3940Sstevel@tonic-gate 		cp2 = word;
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 		/*
3970Sstevel@tonic-gate 		 * Collect a 'word,' allowing it to contain escaped
3980Sstevel@tonic-gate 		 * white space.
3990Sstevel@tonic-gate 		 */
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 		while (*cp && !(iswspace(*cp))) {
4020Sstevel@tonic-gate 			if (*cp == '\\' && iswspace(cp[1]))
4030Sstevel@tonic-gate 				*cp2++ = *cp++;
4040Sstevel@tonic-gate 			*cp2++ = *cp++;
4050Sstevel@tonic-gate 		}
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 		/*
4080Sstevel@tonic-gate 		 * Guarantee a space at end of line.
4090Sstevel@tonic-gate 		 * Two spaces after end of sentence punctuation.
4100Sstevel@tonic-gate 		 */
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 		if (*cp == L'\0') {
4130Sstevel@tonic-gate 			*cp2++ = L' ';
4140Sstevel@tonic-gate 			if (wschr(srchlist, cp[-1]) != NULL)
4150Sstevel@tonic-gate 				*cp2++ = L' ';
4160Sstevel@tonic-gate 		}
4170Sstevel@tonic-gate 		while (iswspace(*cp))
4180Sstevel@tonic-gate 			*cp2++ = *cp++;
4190Sstevel@tonic-gate 		*cp2 = L'\0';
4200Sstevel@tonic-gate 		pack(word);
4210Sstevel@tonic-gate 	}
4220Sstevel@tonic-gate }
4230Sstevel@tonic-gate 
424373Sceastha static void
msplit(wchar_t line[])4250Sstevel@tonic-gate msplit(wchar_t line[])
4260Sstevel@tonic-gate {
427373Sceastha 	wchar_t *cp, *cp2, prev;
4280Sstevel@tonic-gate 	wchar_t word[BUFSIZ];
4290Sstevel@tonic-gate 	static const wchar_t *srchlist = (const wchar_t *) L".:!?";
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	cp = line;
4320Sstevel@tonic-gate 	while (*cp) {
4330Sstevel@tonic-gate 		cp2 = word;
4340Sstevel@tonic-gate 		prev = *cp;
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 		/*
4370Sstevel@tonic-gate 		 * Collect a 'word,' allowing it to contain escaped
4380Sstevel@tonic-gate 		 * white space.
4390Sstevel@tonic-gate 		 */
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 		while (*cp) {
4420Sstevel@tonic-gate 			if (iswspace(*cp))
4430Sstevel@tonic-gate 				break;
4440Sstevel@tonic-gate 			if (_wckind(*cp) != _wckind(prev))
4450Sstevel@tonic-gate 				if (wcsetno(*cp) != 0 || wcsetno(prev) != 0)
4460Sstevel@tonic-gate 					break;
4470Sstevel@tonic-gate 			if (*cp == '\\' && iswspace(cp[1]))
4480Sstevel@tonic-gate 				*cp2++ = *cp++;
4490Sstevel@tonic-gate 			prev = *cp;
4500Sstevel@tonic-gate 			*cp2++ = *cp++;
4510Sstevel@tonic-gate 		}
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 		/*
4540Sstevel@tonic-gate 		 * Guarantee a space at end of line.
4550Sstevel@tonic-gate 		 * Two spaces after end of sentence punctuation.
4560Sstevel@tonic-gate 		 */
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 		if (*cp == L'\0') {
4590Sstevel@tonic-gate 			*cp2++ = L' ';
4600Sstevel@tonic-gate 			if (wschr(srchlist, cp[-1]) != NULL)
4610Sstevel@tonic-gate 				*cp2++ = L' ';
4620Sstevel@tonic-gate 		}
4630Sstevel@tonic-gate 		while (iswspace(*cp))
4640Sstevel@tonic-gate 			*cp2++ = *cp++;
4650Sstevel@tonic-gate 		*cp2 = L'\0';
4660Sstevel@tonic-gate 		pack(word);
4670Sstevel@tonic-gate 	}
4680Sstevel@tonic-gate }
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate /*
4710Sstevel@tonic-gate  * Output section.
4720Sstevel@tonic-gate  * Build up line images from the words passed in.  Prefix
4730Sstevel@tonic-gate  * each line with correct number of blanks.  The buffer "outbuf"
4740Sstevel@tonic-gate  * contains the current partial line image, including prefixed blanks.
4750Sstevel@tonic-gate  * "outp" points to the next available space therein.  When outp is NOSTR,
4760Sstevel@tonic-gate  * there ain't nothing in there yet.  At the bottom of this whole mess,
4770Sstevel@tonic-gate  * leading tabs are reinserted.
4780Sstevel@tonic-gate  */
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate /*
4810Sstevel@tonic-gate  * Pack a word onto the output line.  If this is the beginning of
4820Sstevel@tonic-gate  * the line, push on the appropriately-sized string of blanks first.
4830Sstevel@tonic-gate  * If the word won't fit on the current line, flush and begin a new
4840Sstevel@tonic-gate  * line.  If the word is too long to fit all by itself on a line,
4850Sstevel@tonic-gate  * just give it its own and hope for the best.
4860Sstevel@tonic-gate  */
4870Sstevel@tonic-gate 
488373Sceastha static void
pack(wchar_t word[])4890Sstevel@tonic-gate pack(wchar_t word[])
4900Sstevel@tonic-gate {
491373Sceastha 	wchar_t *cp;
492373Sceastha 	int s, t;
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 	if (outp == NOSTR)
4950Sstevel@tonic-gate 		leadin();
4960Sstevel@tonic-gate 	t = wscol(word);
4970Sstevel@tonic-gate 	*outp = L'\0';
4980Sstevel@tonic-gate 	s = wscol(outbuf);
4990Sstevel@tonic-gate 	if (t+s <= width) {
5007307SJohn.Sonnenschein@Sun.COM 		for (cp = word; *cp; *outp++ = *cp++) {
5017307SJohn.Sonnenschein@Sun.COM 		}
5020Sstevel@tonic-gate 		return;
5030Sstevel@tonic-gate 	}
5040Sstevel@tonic-gate 	if (s > filler) {
5050Sstevel@tonic-gate 		oflush();
5060Sstevel@tonic-gate 		leadin();
5070Sstevel@tonic-gate 	}
5087307SJohn.Sonnenschein@Sun.COM 	for (cp = word; *cp; *outp++ = *cp++) {
5097307SJohn.Sonnenschein@Sun.COM 	}
5100Sstevel@tonic-gate }
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate /*
5130Sstevel@tonic-gate  * If there is anything on the current output line, send it on
5140Sstevel@tonic-gate  * its way.  Set outp to NOSTR to indicate the absence of the current
5150Sstevel@tonic-gate  * line prefix.
5160Sstevel@tonic-gate  */
5170Sstevel@tonic-gate 
518373Sceastha static void
oflush(void)5190Sstevel@tonic-gate oflush(void)
5200Sstevel@tonic-gate {
5210Sstevel@tonic-gate 	if (outp == NOSTR)
5220Sstevel@tonic-gate 		return;
5230Sstevel@tonic-gate 	*outp = L'\0';
5240Sstevel@tonic-gate 	tabulate(outbuf);
5250Sstevel@tonic-gate 	outp = NOSTR;
5260Sstevel@tonic-gate }
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate /*
5290Sstevel@tonic-gate  * Take the passed line buffer, insert leading tabs where possible, and
5300Sstevel@tonic-gate  * output on standard output (finally).
5310Sstevel@tonic-gate  */
5320Sstevel@tonic-gate 
533373Sceastha static void
tabulate(wchar_t line[])5340Sstevel@tonic-gate tabulate(wchar_t line[])
5350Sstevel@tonic-gate {
536373Sceastha 	wchar_t *cp;
537373Sceastha 	int b, t;
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	/* Toss trailing blanks in the output line */
5410Sstevel@tonic-gate 	cp = line + wslen(line) - 1;
5420Sstevel@tonic-gate 	while (cp >= line && *cp == L' ')
5430Sstevel@tonic-gate 		cp--;
5440Sstevel@tonic-gate 	*++cp = L'\0';
5450Sstevel@tonic-gate 	/* Count the leading blank space and tabulate */
5467307SJohn.Sonnenschein@Sun.COM 	for (cp = line; *cp == L' '; cp++) {
5477307SJohn.Sonnenschein@Sun.COM 	}
5480Sstevel@tonic-gate 	b = cp - line;
5490Sstevel@tonic-gate 	t = b >> 3;
5500Sstevel@tonic-gate 	b &= 07;
5510Sstevel@tonic-gate 	if (t > 0)
552*7308SJohn.Sonnenschein@Sun.COM 		do {
5530Sstevel@tonic-gate 			putc('\t', stdout);
554*7308SJohn.Sonnenschein@Sun.COM 		} while (--t);
5550Sstevel@tonic-gate 	if (b > 0)
556*7308SJohn.Sonnenschein@Sun.COM 		do {
5570Sstevel@tonic-gate 			putc(' ', stdout);
558*7308SJohn.Sonnenschein@Sun.COM 		} while (--b);
5590Sstevel@tonic-gate 	while (*cp)
5600Sstevel@tonic-gate 		putwc(*cp++, stdout);
5610Sstevel@tonic-gate 	putc('\n', stdout);
5620Sstevel@tonic-gate }
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate /*
5650Sstevel@tonic-gate  * Initialize the output line with the appropriate number of
5660Sstevel@tonic-gate  * leading blanks.
5670Sstevel@tonic-gate  */
5680Sstevel@tonic-gate 
569373Sceastha static void
leadin(void)570373Sceastha leadin(void)
5710Sstevel@tonic-gate {
572373Sceastha 	int b;
573373Sceastha 	wchar_t *cp;
574373Sceastha 	int l;
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 	switch (crown_state) {
5770Sstevel@tonic-gate 	case c_head:
5780Sstevel@tonic-gate 		l = crown_head;
5790Sstevel@tonic-gate 		crown_state = c_lead;
5800Sstevel@tonic-gate 		break;
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	case c_lead:
5830Sstevel@tonic-gate 	case c_fixup:
5840Sstevel@tonic-gate 		l = crown_head;
5850Sstevel@tonic-gate 		crown_state = c_fixup;
5860Sstevel@tonic-gate 		break;
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 	case c_body:
5890Sstevel@tonic-gate 		l = crown_body;
5900Sstevel@tonic-gate 		break;
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 	default:
5930Sstevel@tonic-gate 		l = pfx;
5940Sstevel@tonic-gate 		break;
5950Sstevel@tonic-gate 	}
5960Sstevel@tonic-gate 	filler = l;
5970Sstevel@tonic-gate 	for (b = 0, cp = outbuf; b < l; b++)
5980Sstevel@tonic-gate 		*cp++ = L' ';
5990Sstevel@tonic-gate 	outp = cp;
6000Sstevel@tonic-gate }
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate /*
6030Sstevel@tonic-gate  * Is s1 a prefix of s2??
6040Sstevel@tonic-gate  */
6050Sstevel@tonic-gate 
606373Sceastha static int
ispref(wchar_t * s1,wchar_t * s2)6070Sstevel@tonic-gate ispref(wchar_t *s1, wchar_t *s2)
6080Sstevel@tonic-gate {
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	while (*s1 != L'\0' && *s2 != L'\0')
6110Sstevel@tonic-gate 		if (*s1++ != *s2++)
6120Sstevel@tonic-gate 			return (0);
6130Sstevel@tonic-gate 	return (1);
6140Sstevel@tonic-gate }
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate /*
6170Sstevel@tonic-gate  * Set an input option
6180Sstevel@tonic-gate  */
6190Sstevel@tonic-gate 
620373Sceastha static int
setopt(char * cp)621373Sceastha setopt(char *cp)
6220Sstevel@tonic-gate {
6230Sstevel@tonic-gate 	static int ws = 0;
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	if (*cp == '-') {
6260Sstevel@tonic-gate 		if (cp[1] == 'c' && cp[2] == '\0') {
6270Sstevel@tonic-gate 			crown_state = c_reset;
6280Sstevel@tonic-gate 			return (1);
6290Sstevel@tonic-gate 		}
6300Sstevel@tonic-gate 		if (cp[1] == 's' && cp[2] == '\0') {
6310Sstevel@tonic-gate 			nojoin = 1;
6320Sstevel@tonic-gate 			return (1);
6330Sstevel@tonic-gate 		}
6340Sstevel@tonic-gate 		if (cp[1] == 'w' && cp[2] == '\0') {
6350Sstevel@tonic-gate 			ws++;
6360Sstevel@tonic-gate 			return (1);
6370Sstevel@tonic-gate 		}
6380Sstevel@tonic-gate 		width = atoi(cp+1);
6390Sstevel@tonic-gate 	} else if (ws) {
6400Sstevel@tonic-gate 		width = atoi(cp);
6410Sstevel@tonic-gate 		ws = 0;
6420Sstevel@tonic-gate 	} else
6430Sstevel@tonic-gate 		return (0);
6440Sstevel@tonic-gate 	if (width <= 0 || width >= BUFSIZ-2) {
6450Sstevel@tonic-gate 		fprintf(stderr, "fmt:  bad width: %d\n", width);
6460Sstevel@tonic-gate 		exit(1);
6470Sstevel@tonic-gate 	}
6480Sstevel@tonic-gate 	return (1);
6490Sstevel@tonic-gate }
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate #define	LIB_WDRESOLVE	"/usr/lib/locale/%s/LC_CTYPE/wdresolve.so"
6530Sstevel@tonic-gate #define	WCHKIND		"_wdchkind_"
6540Sstevel@tonic-gate 
655373Sceastha static int	_wckind_c_locale(wchar_t);
6560Sstevel@tonic-gate 
657373Sceastha static int	(*__wckind)(wchar_t) = _wckind_c_locale;
6580Sstevel@tonic-gate static void	*dlhandle = NULL;
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 
661373Sceastha static void
_wckind_init(void)662373Sceastha _wckind_init(void)
6630Sstevel@tonic-gate {
6640Sstevel@tonic-gate 	char	*locale;
6650Sstevel@tonic-gate 	char	path[MAXPATHLEN + 1];
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	if (dlhandle != NULL) {
6690Sstevel@tonic-gate 		(void) dlclose(dlhandle);
6700Sstevel@tonic-gate 		dlhandle = NULL;
6710Sstevel@tonic-gate 	}
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	locale = setlocale(LC_CTYPE, NULL);
6740Sstevel@tonic-gate 	if (strcmp(locale, "C") == 0)
6750Sstevel@tonic-gate 		goto c_locale;
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	(void) sprintf(path, LIB_WDRESOLVE, locale);
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	if ((dlhandle = dlopen(path, RTLD_LAZY)) != NULL) {
680373Sceastha 		__wckind = (int (*)(wchar_t))dlsym(dlhandle, WCHKIND);
6810Sstevel@tonic-gate 		if (__wckind != NULL)
6820Sstevel@tonic-gate 			return;
6830Sstevel@tonic-gate 		(void) dlclose(dlhandle);
6840Sstevel@tonic-gate 		dlhandle = NULL;
6850Sstevel@tonic-gate 	}
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate c_locale:
6880Sstevel@tonic-gate 	__wckind = _wckind_c_locale;
6890Sstevel@tonic-gate }
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate int
_wckind(wchar_t wc)693373Sceastha _wckind(wchar_t wc)
6940Sstevel@tonic-gate {
6950Sstevel@tonic-gate 	return (*__wckind) (wc);
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate static int
_wckind_c_locale(wchar_t wc)700373Sceastha _wckind_c_locale(wchar_t wc)
7010Sstevel@tonic-gate {
7020Sstevel@tonic-gate 	int	ret;
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 	/*
7050Sstevel@tonic-gate 	 * DEPEND_ON_ANSIC: L notion for the character is new in
7060Sstevel@tonic-gate 	 * ANSI-C, k&r compiler won't work.
7070Sstevel@tonic-gate 	 */
7080Sstevel@tonic-gate 	if (iswascii(wc))
7090Sstevel@tonic-gate 		ret = (iswalnum(wc) || wc == L'_') ? 0 : 1;
7100Sstevel@tonic-gate 	else
7110Sstevel@tonic-gate 		ret = wcsetno(wc) + 1;
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	return (ret);
7140Sstevel@tonic-gate }
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate /*
7170Sstevel@tonic-gate  * header_chk -
7180Sstevel@tonic-gate  * Called when done looking for a set mail header lines.
7190Sstevel@tonic-gate  * Either a blank line was seen, or EOF was reached.
7200Sstevel@tonic-gate  *
7210Sstevel@tonic-gate  * Verifies if current hdrbuf of potential mail header lines
7220Sstevel@tonic-gate  * is really a mail header.  A mail header must be at least 2
7230Sstevel@tonic-gate  * lines and more than half of them must start with one of the
7240Sstevel@tonic-gate  * known mail header strings in headnames.
7250Sstevel@tonic-gate  *
7260Sstevel@tonic-gate  * header_chk sets hdr_state to do_hdr if hdrbuf contained a valid
7270Sstevel@tonic-gate  * mail header.  Otherwise, it sets hdr_state to flush_hdr.
7280Sstevel@tonic-gate  *
7290Sstevel@tonic-gate  * h_lines = hdrbuf index for next line to be saved;
7300Sstevel@tonic-gate  *	     also indicates current # of lines in potential header
7310Sstevel@tonic-gate  */
7320Sstevel@tonic-gate static void
header_chk(void)7330Sstevel@tonic-gate header_chk(void)
7340Sstevel@tonic-gate {
7350Sstevel@tonic-gate 	wchar_t  *cp; 		/* ptr to current char of line */
7360Sstevel@tonic-gate 	wchar_t **hp; 		/* ptr to current char of a valid */
7370Sstevel@tonic-gate 				/* mail header string */
7380Sstevel@tonic-gate 	int	  l;		/* index */
7390Sstevel@tonic-gate 				/*
7400Sstevel@tonic-gate 				 * number of lines in hdrbuf that look
7410Sstevel@tonic-gate 				 * like mail header lines (start with
7420Sstevel@tonic-gate 				 * a known mail header prefix)
7430Sstevel@tonic-gate 				 */
7440Sstevel@tonic-gate 	int	 hdrcount = 0;
7450Sstevel@tonic-gate 		/* header must have at least 2 lines (h_lines > 1) */
7460Sstevel@tonic-gate 		if (h_lines < 2) {
7470Sstevel@tonic-gate 			hdr_state = flush_hdr;
7480Sstevel@tonic-gate 			return;
7490Sstevel@tonic-gate 		}
7500Sstevel@tonic-gate 		/*
7510Sstevel@tonic-gate 		 * go through each line in hdrbuf and see how many
7520Sstevel@tonic-gate 		 * look like mail header lines
7530Sstevel@tonic-gate 		 */
7540Sstevel@tonic-gate 		for (l = 0; l < h_lines; l++) {
7550Sstevel@tonic-gate 			/* skip initial blanks */
7567307SJohn.Sonnenschein@Sun.COM 			for (cp = hdrbuf[l]; *cp == L' '; cp++) {
7577307SJohn.Sonnenschein@Sun.COM 			}
758373Sceastha 			for (hp = &headnames[0]; *hp != (wchar_t *)0; hp++)
7590Sstevel@tonic-gate 				if (ispref(*hp, cp)) {
7600Sstevel@tonic-gate 					hdrcount++;
7610Sstevel@tonic-gate 					break;
7620Sstevel@tonic-gate 				}
7630Sstevel@tonic-gate 		}
7640Sstevel@tonic-gate 		/*
7650Sstevel@tonic-gate 		 * if over half match, we'll assume this is a header;
7660Sstevel@tonic-gate 		 * set hdr_state to indicate whether to treat
7670Sstevel@tonic-gate 		 * these lines as mail header (do_hdr) or not (flush_hdr)
7680Sstevel@tonic-gate 		 */
7690Sstevel@tonic-gate 		if (hdrcount > h_lines / 2)
7700Sstevel@tonic-gate 			hdr_state = do_hdr;
7710Sstevel@tonic-gate 		else
7720Sstevel@tonic-gate 			hdr_state = flush_hdr;
7730Sstevel@tonic-gate }
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate /*
7760Sstevel@tonic-gate  * fill_hdrbuf -
7770Sstevel@tonic-gate  * Save given input line into next element of hdrbuf,
7780Sstevel@tonic-gate  * as a potential mail header line, to be processed later
7790Sstevel@tonic-gate  * once we decide whether or not the contents of hdrbuf is
7800Sstevel@tonic-gate  * really a mail header, via header_chk().
7810Sstevel@tonic-gate  *
7820Sstevel@tonic-gate  * Does not allow hdrbuf to exceed MAXLINES lines.
7830Sstevel@tonic-gate  * Dynamically allocates space for each line.  If we are unable
7840Sstevel@tonic-gate  * to allocate space for the current string, stop special mail
7850Sstevel@tonic-gate  * header preservation at this point and continue formatting
7860Sstevel@tonic-gate  * without it.
7870Sstevel@tonic-gate  */
7880Sstevel@tonic-gate static void
fill_hdrbuf(wchar_t line[])7890Sstevel@tonic-gate fill_hdrbuf(wchar_t line[])
7900Sstevel@tonic-gate {
7910Sstevel@tonic-gate 	wchar_t *cp;	/* pointer to characters in input line */
7920Sstevel@tonic-gate 	int	 i;	/* index into characters a hdrbuf line */
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 	if (h_lines >= MAXLINES) {
7950Sstevel@tonic-gate 		/*
7960Sstevel@tonic-gate 		 * if we run over MAXLINES potential mail header
7970Sstevel@tonic-gate 		 * lines, stop checking--this is most likely NOT a
7980Sstevel@tonic-gate 		 * mail header; flush out the hdrbuf, then process
7990Sstevel@tonic-gate 		 * the current 'line' normally.
8000Sstevel@tonic-gate 		 */
8010Sstevel@tonic-gate 		hdr_state = flush_hdr;
8020Sstevel@tonic-gate 		process_hdrbuf();
8030Sstevel@tonic-gate 		prefix(line);
8040Sstevel@tonic-gate 		return;
8050Sstevel@tonic-gate 	}
8060Sstevel@tonic-gate 	hdrbuf[h_lines] = (wchar_t *)malloc(sizeof (wchar_t) *
8070Sstevel@tonic-gate 	    (wslen(line) + 1));
8080Sstevel@tonic-gate 	if (hdrbuf[h_lines] == NULL) {
8090Sstevel@tonic-gate 		perror("malloc");
8100Sstevel@tonic-gate 		fprintf(stderr, "fmt: unable to do mail header preservation\n");
8110Sstevel@tonic-gate 		errs++;
8120Sstevel@tonic-gate 		/*
8130Sstevel@tonic-gate 		 * Can't process mail header; flush current contents
8140Sstevel@tonic-gate 		 * of mail header and continue with no more mail
8150Sstevel@tonic-gate 		 * header processing
8160Sstevel@tonic-gate 		 */
8170Sstevel@tonic-gate 		if (h_lines == 0)
8180Sstevel@tonic-gate 			/* hdrbuf is empty; process this line normally */
8190Sstevel@tonic-gate 			prefix(line);
8200Sstevel@tonic-gate 		else {
8210Sstevel@tonic-gate 			hdr_state = flush_hdr;
8220Sstevel@tonic-gate 			for (i = 0; i < h_lines; i++) {
8230Sstevel@tonic-gate 				prefix(hdrbuf[i]);
8240Sstevel@tonic-gate 				free(hdrbuf[i]);
8250Sstevel@tonic-gate 			}
8260Sstevel@tonic-gate 			h_lines = 0;
8270Sstevel@tonic-gate 		}
8280Sstevel@tonic-gate 		hdr_state = off;
8290Sstevel@tonic-gate 		return;
8300Sstevel@tonic-gate 	}
8310Sstevel@tonic-gate 	/* save this line as a potential mail header line */
8327307SJohn.Sonnenschein@Sun.COM 	for (i = 0, cp = line; (hdrbuf[h_lines][i] = *cp) != L'\0'; i++, cp++) {
8337307SJohn.Sonnenschein@Sun.COM 	}
8340Sstevel@tonic-gate 	h_lines++;
8350Sstevel@tonic-gate }
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate /*
8380Sstevel@tonic-gate  * process_hdrbuf -
8390Sstevel@tonic-gate  * Outputs the lines currently stored in hdrbuf, according
8400Sstevel@tonic-gate  * to the current hdr_state value, assumed to be either do_hdr
8410Sstevel@tonic-gate  * or flush_hdr.
8420Sstevel@tonic-gate  * This should be called after doing a header_chk() to verify
8430Sstevel@tonic-gate  * the hdrbuf and set the hdr_state flag.
8440Sstevel@tonic-gate  */
8450Sstevel@tonic-gate static void
process_hdrbuf(void)8460Sstevel@tonic-gate process_hdrbuf(void)
8470Sstevel@tonic-gate {
8480Sstevel@tonic-gate int i;
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate 	for (i = 0; i < h_lines; i++) {
8510Sstevel@tonic-gate 		prefix(hdrbuf[i]);
8520Sstevel@tonic-gate 		free(hdrbuf[i]);
8530Sstevel@tonic-gate 	}
8540Sstevel@tonic-gate 	hdr_state = not_in_hdr;
8550Sstevel@tonic-gate 	h_lines = 0;
8560Sstevel@tonic-gate }
857