xref: /titanic_52/usr/src/cmd/fmt/fmt.c (revision 196c7f05d2deba7404e90ad67f3861185c78ca2d)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5e1ab4a07SJohn Sonnenschein  * Common Development and Distribution License (the "License").
6e1ab4a07SJohn Sonnenschein  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22e1ab4a07SJohn Sonnenschein  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
277c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <ctype.h>
337c478bd9Sstevel@tonic-gate #include <wctype.h>
347c478bd9Sstevel@tonic-gate #include <widec.h>
357c478bd9Sstevel@tonic-gate #include <dlfcn.h>
367c478bd9Sstevel@tonic-gate #include <locale.h>
377c478bd9Sstevel@tonic-gate #include <sys/param.h>
387c478bd9Sstevel@tonic-gate #include <string.h>
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate /*
417c478bd9Sstevel@tonic-gate  * fmt -- format the concatenation of input files or standard input
427c478bd9Sstevel@tonic-gate  * onto standard output.  Designed for use with Mail ~|
437c478bd9Sstevel@tonic-gate  *
447c478bd9Sstevel@tonic-gate  * Syntax: fmt [ -width | -w width ] [ -cs ] [ name ... ]
457c478bd9Sstevel@tonic-gate  * Author: Kurt Shoens (UCB) 12/7/78
467c478bd9Sstevel@tonic-gate  */
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #define	NOSTR	((wchar_t *)0)	/* Null string pointer for lint */
497c478bd9Sstevel@tonic-gate #define	MAXLINES	100	/* maximum mail header lines to verify */
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate wchar_t	outbuf[BUFSIZ];			/* Sandbagged output line image */
527c478bd9Sstevel@tonic-gate wchar_t	*outp;				/* Pointer in above */
537c478bd9Sstevel@tonic-gate int	filler;				/* Filler amount in outbuf */
54e1ab4a07SJohn Sonnenschein char sobuf[BUFSIZ];	/* Global buffer */
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate int	pfx;			/* Current leading blank count */
577c478bd9Sstevel@tonic-gate int	width = 72;		/* Width that we will not exceed */
587c478bd9Sstevel@tonic-gate int	nojoin = 0;		/* split lines only, don't join short ones */
597c478bd9Sstevel@tonic-gate int	errs = 0;		/* Current number of errors */
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate enum crown_type	{c_none, c_reset, c_head, c_lead, c_fixup, c_body};
627c478bd9Sstevel@tonic-gate enum crown_type	crown_state;	/* Crown margin state */
637c478bd9Sstevel@tonic-gate int	crown_head;		/* The header offset */
647c478bd9Sstevel@tonic-gate int	crown_body;		/* The body offset */
657c478bd9Sstevel@tonic-gate 	/* currently-known initial strings found in mail headers */
667c478bd9Sstevel@tonic-gate wchar_t	*headnames[] = {
677c478bd9Sstevel@tonic-gate 	L"Apparently-To", L"Bcc", L"bcc", L"Cc", L"cc", L"Confirmed-By",
687c478bd9Sstevel@tonic-gate 	L"Content", L"content-length", L"From", L"Date", L"id",
697c478bd9Sstevel@tonic-gate 	L"Message-I", L"MIME-Version", L"Precedence", L"Return-Path",
707c478bd9Sstevel@tonic-gate 	L"Received", L"Reply-To", L"Status", L"Subject", L"To", L"X-IMAP",
717c478bd9Sstevel@tonic-gate 	L"X-Lines", L"X-Sender", L"X-Sun", L"X-Status", L"X-UID",
727c478bd9Sstevel@tonic-gate 	0};
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate enum hdr_type {
757c478bd9Sstevel@tonic-gate 	off,		/* mail header processing is off */
767c478bd9Sstevel@tonic-gate 	not_in_hdr,	/* not currently processing a mail header */
777c478bd9Sstevel@tonic-gate 	in_hdr, 	/* currently filling hdrbuf with potential hdr lines */
787c478bd9Sstevel@tonic-gate 	flush_hdr,	/* flush hdrbuf; not a header, no special processing */
797c478bd9Sstevel@tonic-gate 	do_hdr		/* process hdrbuf as a mail header */
807c478bd9Sstevel@tonic-gate };
817c478bd9Sstevel@tonic-gate 				/* current state of hdrbuf */
827c478bd9Sstevel@tonic-gate enum hdr_type	hdr_state = not_in_hdr;
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate wchar_t *hdrbuf[MAXLINES];	/* buffer to hold potential mail header lines */
857c478bd9Sstevel@tonic-gate int 	h_lines;		/* index into lines of hdrbuf */
867c478bd9Sstevel@tonic-gate 
87b7d62af5Sceastha void (*(split))(wchar_t []);
887c478bd9Sstevel@tonic-gate extern int scrwidth(wchar_t);
89*196c7f05SJoshua M. Clulow extern boolean_t is_headline(const char *);
907c478bd9Sstevel@tonic-gate 
91b7d62af5Sceastha 
92b7d62af5Sceastha static void fill_hdrbuf(wchar_t []);
937c478bd9Sstevel@tonic-gate static void header_chk(void);
947c478bd9Sstevel@tonic-gate static void process_hdrbuf(void);
95b7d62af5Sceastha static void leadin(void);
96b7d62af5Sceastha static void tabulate(wchar_t []);
97b7d62af5Sceastha static void oflush(void);
98b7d62af5Sceastha static void pack(wchar_t []);
99b7d62af5Sceastha static void msplit(wchar_t []);
100b7d62af5Sceastha static void csplit(wchar_t []);
101b7d62af5Sceastha static void _wckind_init(void);
102b7d62af5Sceastha static void prefix(wchar_t []);
103b7d62af5Sceastha static void fmt(FILE *);
104b7d62af5Sceastha static int setopt(char *);
105b7d62af5Sceastha int _wckind(wchar_t);
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate /*
1087c478bd9Sstevel@tonic-gate  * Drive the whole formatter by managing input files.  Also,
1097c478bd9Sstevel@tonic-gate  * cause initialization of the output stuff and flush it out
1107c478bd9Sstevel@tonic-gate  * at the end.
1117c478bd9Sstevel@tonic-gate  */
1127c478bd9Sstevel@tonic-gate 
113b7d62af5Sceastha int
1147c478bd9Sstevel@tonic-gate main(int argc, char **argv)
1157c478bd9Sstevel@tonic-gate {
116b7d62af5Sceastha 	FILE *fi;
117b7d62af5Sceastha 	char *cp;
1187c478bd9Sstevel@tonic-gate 	int nofile;
1197c478bd9Sstevel@tonic-gate 	char *locale;
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	outp = NOSTR;
1227c478bd9Sstevel@tonic-gate 	setbuf(stdout, sobuf);
1237c478bd9Sstevel@tonic-gate 	setlocale(LC_ALL, "");
1247c478bd9Sstevel@tonic-gate 	locale = setlocale(LC_CTYPE, "");
1257c478bd9Sstevel@tonic-gate 	if (strcmp(locale, "C") == 0) {
1267c478bd9Sstevel@tonic-gate 		split = csplit;
1277c478bd9Sstevel@tonic-gate 	} else {
1287c478bd9Sstevel@tonic-gate 		split = msplit;
129b7d62af5Sceastha 		_wckind_init();
1307c478bd9Sstevel@tonic-gate 	}
1317c478bd9Sstevel@tonic-gate 	if (argc < 2) {
1327c478bd9Sstevel@tonic-gate single:
1337c478bd9Sstevel@tonic-gate 		fmt(stdin);
1347c478bd9Sstevel@tonic-gate 		oflush();
1357c478bd9Sstevel@tonic-gate 		exit(0);
1367c478bd9Sstevel@tonic-gate 	}
1377c478bd9Sstevel@tonic-gate 	nofile = 1;
1387c478bd9Sstevel@tonic-gate 	while (--argc) {
1397c478bd9Sstevel@tonic-gate 		cp = *++argv;
1407c478bd9Sstevel@tonic-gate 		if (setopt(cp))
1417c478bd9Sstevel@tonic-gate 			continue;
1427c478bd9Sstevel@tonic-gate 		nofile = 0;
1437c478bd9Sstevel@tonic-gate 		if ((fi = fopen(cp, "r")) == NULL) {
1447c478bd9Sstevel@tonic-gate 			perror(cp);
1457c478bd9Sstevel@tonic-gate 			errs++;
1467c478bd9Sstevel@tonic-gate 			continue;
1477c478bd9Sstevel@tonic-gate 		}
1487c478bd9Sstevel@tonic-gate 		fmt(fi);
1497c478bd9Sstevel@tonic-gate 		fclose(fi);
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 	if (nofile)
1527c478bd9Sstevel@tonic-gate 		goto single;
1537c478bd9Sstevel@tonic-gate 	oflush();
1547c98f3e0Sakaplan 	fclose(stdout);
155b7d62af5Sceastha 	return (errs);
1567c478bd9Sstevel@tonic-gate }
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate /*
1597c478bd9Sstevel@tonic-gate  * Read up characters from the passed input file, forming lines,
1607c478bd9Sstevel@tonic-gate  * doing ^H processing, expanding tabs, stripping trailing blanks,
1617c478bd9Sstevel@tonic-gate  * and sending each line down for analysis.
1627c478bd9Sstevel@tonic-gate  */
1637c478bd9Sstevel@tonic-gate 
164b7d62af5Sceastha static void
1657c478bd9Sstevel@tonic-gate fmt(FILE *fi)
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate 	wchar_t linebuf[BUFSIZ], canonb[BUFSIZ];
168b7d62af5Sceastha 	wchar_t *cp, *cp2;
169b7d62af5Sceastha 	int col;
1707c478bd9Sstevel@tonic-gate 	wchar_t	c;
1717c478bd9Sstevel@tonic-gate 	char	cbuf[BUFSIZ];	/* stores wchar_t string as char string */
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	c = getwc(fi);
1747c478bd9Sstevel@tonic-gate 	while (c != EOF) {
1757c478bd9Sstevel@tonic-gate 		/*
1767c478bd9Sstevel@tonic-gate 		 * Collect a line, doing ^H processing.
1777c478bd9Sstevel@tonic-gate 		 * Leave tabs for now.
1787c478bd9Sstevel@tonic-gate 		 */
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 		cp = linebuf;
1817c478bd9Sstevel@tonic-gate 		while (c != L'\n' && c != EOF && cp-linebuf < BUFSIZ-1) {
1827c478bd9Sstevel@tonic-gate 			if (c == L'\b') {
1837c478bd9Sstevel@tonic-gate 				if (cp > linebuf)
1847c478bd9Sstevel@tonic-gate 					cp--;
1857c478bd9Sstevel@tonic-gate 				c = getwc(fi);
1867c478bd9Sstevel@tonic-gate 				continue;
1877c478bd9Sstevel@tonic-gate 			}
1887c478bd9Sstevel@tonic-gate 			if (!(iswprint(c)) && c != L'\t') {
1897c478bd9Sstevel@tonic-gate 				c = getwc(fi);
1907c478bd9Sstevel@tonic-gate 				continue;
1917c478bd9Sstevel@tonic-gate 			}
1927c478bd9Sstevel@tonic-gate 			*cp++ = c;
1937c478bd9Sstevel@tonic-gate 			c = getwc(fi);
1947c478bd9Sstevel@tonic-gate 		}
1957c478bd9Sstevel@tonic-gate 		*cp = L'\0';
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 		/*
1987c478bd9Sstevel@tonic-gate 		 * Toss anything remaining on the input line.
1997c478bd9Sstevel@tonic-gate 		 */
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 		while (c != L'\n' && c != EOF)
2027c478bd9Sstevel@tonic-gate 			c = getwc(fi);
2037c478bd9Sstevel@tonic-gate 		/*
2047c478bd9Sstevel@tonic-gate 		 * Expand tabs on the way to canonb.
2057c478bd9Sstevel@tonic-gate 		 */
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 		col = 0;
2087c478bd9Sstevel@tonic-gate 		cp = linebuf;
2097c478bd9Sstevel@tonic-gate 		cp2 = canonb;
2107c478bd9Sstevel@tonic-gate 		while (c = *cp++) {
2117c478bd9Sstevel@tonic-gate 			if (c != L'\t') {
2127c478bd9Sstevel@tonic-gate 				col += scrwidth(c);
2137c478bd9Sstevel@tonic-gate 				if (cp2-canonb < BUFSIZ-1)
2147c478bd9Sstevel@tonic-gate 					*cp2++ = c;
2157c478bd9Sstevel@tonic-gate 				continue;
2167c478bd9Sstevel@tonic-gate 			}
2177c478bd9Sstevel@tonic-gate 			do {
2187c478bd9Sstevel@tonic-gate 				if (cp2-canonb < BUFSIZ-1)
2197c478bd9Sstevel@tonic-gate 					*cp2++ = L' ';
2207c478bd9Sstevel@tonic-gate 				col++;
2217c478bd9Sstevel@tonic-gate 			} while ((col & 07) != 0);
2227c478bd9Sstevel@tonic-gate 		}
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 		/*
2257c478bd9Sstevel@tonic-gate 		 * Swipe trailing blanks from the line.
2267c478bd9Sstevel@tonic-gate 		 */
2277c478bd9Sstevel@tonic-gate 
228e1ab4a07SJohn Sonnenschein 		for (cp2--; cp2 >= canonb && *cp2 == L' '; cp2--) {
229e1ab4a07SJohn Sonnenschein 		}
2307c478bd9Sstevel@tonic-gate 		*++cp2 = '\0';
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 			/* special processing to look for mail header lines */
2337c478bd9Sstevel@tonic-gate 		switch (hdr_state) {
2347c478bd9Sstevel@tonic-gate 		case off:
2357c478bd9Sstevel@tonic-gate 			prefix(canonb);
2367c478bd9Sstevel@tonic-gate 		case not_in_hdr:
2377c478bd9Sstevel@tonic-gate 			/* look for an initial mail header line */
2387c478bd9Sstevel@tonic-gate 			/* skip initial blanks */
239e1ab4a07SJohn Sonnenschein 			for (cp = canonb; *cp == L' '; cp++) {
240e1ab4a07SJohn Sonnenschein 			}
2417c478bd9Sstevel@tonic-gate 			/*
2427c478bd9Sstevel@tonic-gate 			 * Need to convert string from wchar_t to char,
243*196c7f05SJoshua M. Clulow 			 * since this is what is_headline() expects.  Since we
2447c478bd9Sstevel@tonic-gate 			 * only want to make sure cp points to a "From" line
2457c478bd9Sstevel@tonic-gate 			 * of the email, we don't have to alloc
2467c478bd9Sstevel@tonic-gate 			 * BUFSIZ * MB_LEN_MAX to cbuf.
2477c478bd9Sstevel@tonic-gate 			 */
2487c478bd9Sstevel@tonic-gate 			wcstombs(cbuf, cp, (BUFSIZ - 1));
249*196c7f05SJoshua M. Clulow 			if (is_headline(cbuf) == B_TRUE) {
2507c478bd9Sstevel@tonic-gate 				hdr_state = in_hdr;
2517c478bd9Sstevel@tonic-gate 				fill_hdrbuf(canonb);
2527c478bd9Sstevel@tonic-gate 			} else {
2537c478bd9Sstevel@tonic-gate 				/* no mail header line; process normally */
2547c478bd9Sstevel@tonic-gate 				prefix(canonb);
2557c478bd9Sstevel@tonic-gate 			}
2567c478bd9Sstevel@tonic-gate 			break;
2577c478bd9Sstevel@tonic-gate 		case in_hdr:
2587c478bd9Sstevel@tonic-gate 			/* already saw 1st mail header line; look for more */
2597c478bd9Sstevel@tonic-gate 			if (canonb[0] == L'\0') {
2607c478bd9Sstevel@tonic-gate 				/*
2617c478bd9Sstevel@tonic-gate 				 * blank line means end of mail header;
2627c478bd9Sstevel@tonic-gate 				 * verify current mail header buffer
2637c478bd9Sstevel@tonic-gate 				 * then process it accordingly
2647c478bd9Sstevel@tonic-gate 				 */
2657c478bd9Sstevel@tonic-gate 				header_chk();
2667c478bd9Sstevel@tonic-gate 				process_hdrbuf();
2677c478bd9Sstevel@tonic-gate 				/* now process the current blank line */
2687c478bd9Sstevel@tonic-gate 				prefix(canonb);
2697c478bd9Sstevel@tonic-gate 			} else
2707c478bd9Sstevel@tonic-gate 				/*
2717c478bd9Sstevel@tonic-gate 				 * not a blank line--save this line as
2727c478bd9Sstevel@tonic-gate 				 * a potential mail header line
2737c478bd9Sstevel@tonic-gate 				 */
2747c478bd9Sstevel@tonic-gate 				fill_hdrbuf(canonb);
2757c478bd9Sstevel@tonic-gate 			break;
2767c478bd9Sstevel@tonic-gate 		}
2777c478bd9Sstevel@tonic-gate 		if (c != EOF)
2787c478bd9Sstevel@tonic-gate 			c = getwc(fi);
2797c478bd9Sstevel@tonic-gate 	}
2807c478bd9Sstevel@tonic-gate 	/*
2817c478bd9Sstevel@tonic-gate 	 * end of this file--make sure we process the stuff in
2827c478bd9Sstevel@tonic-gate 	 * hdrbuf before we're finished
2837c478bd9Sstevel@tonic-gate 	 */
2847c478bd9Sstevel@tonic-gate 	if (hdr_state == in_hdr) {
2857c478bd9Sstevel@tonic-gate 		header_chk();
2867c478bd9Sstevel@tonic-gate 		process_hdrbuf();
2877c478bd9Sstevel@tonic-gate 	}
2887c478bd9Sstevel@tonic-gate }
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate /*
2917c478bd9Sstevel@tonic-gate  * Take a line devoid of tabs and other garbage and determine its
2927c478bd9Sstevel@tonic-gate  * blank prefix.  If the indent changes, call for a linebreak.
2937c478bd9Sstevel@tonic-gate  * If the input line is blank, echo the blank line on the output.
2947c478bd9Sstevel@tonic-gate  * Finally, if the line minus the prefix is a mail header, try to keep
2957c478bd9Sstevel@tonic-gate  * it on a line by itself.
2967c478bd9Sstevel@tonic-gate  */
2977c478bd9Sstevel@tonic-gate 
298b7d62af5Sceastha static void
2997c478bd9Sstevel@tonic-gate prefix(wchar_t line[])
3007c478bd9Sstevel@tonic-gate {
301b7d62af5Sceastha 	wchar_t *cp;
302b7d62af5Sceastha 	int np;
3037c478bd9Sstevel@tonic-gate 	int nosplit = 0;	/* flag set if line should not be split */
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	if (line[0] == L'\0') {
3067c478bd9Sstevel@tonic-gate 		oflush();
3077c478bd9Sstevel@tonic-gate 		putchar('\n');
3087c478bd9Sstevel@tonic-gate 		if (crown_state != c_none)
3097c478bd9Sstevel@tonic-gate 			crown_state = c_reset;
3107c478bd9Sstevel@tonic-gate 		return;
3117c478bd9Sstevel@tonic-gate 	}
312e1ab4a07SJohn Sonnenschein 	for (cp = line; *cp == L' '; cp++) {
313e1ab4a07SJohn Sonnenschein 	}
3147c478bd9Sstevel@tonic-gate 	np = cp - line;
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	/*
3177c478bd9Sstevel@tonic-gate 	 * The following horrible expression attempts to avoid linebreaks
3187c478bd9Sstevel@tonic-gate 	 * when the indent changes due to a paragraph.
3197c478bd9Sstevel@tonic-gate 	 */
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	if (crown_state == c_none && np != pfx && (np > pfx || abs(pfx-np) > 8))
3227c478bd9Sstevel@tonic-gate 		oflush();
3237c478bd9Sstevel@tonic-gate 	/*
3247c478bd9Sstevel@tonic-gate 	 * if this is a mail header line, don't split it; flush previous
3257c478bd9Sstevel@tonic-gate 	 * line, if any, so we don't join this line to it
3267c478bd9Sstevel@tonic-gate 	 */
3277c478bd9Sstevel@tonic-gate 	if (hdr_state == do_hdr) {
3287c478bd9Sstevel@tonic-gate 		nosplit = 1;
3297c478bd9Sstevel@tonic-gate 		oflush();
3307c478bd9Sstevel@tonic-gate 	}
3317c478bd9Sstevel@tonic-gate 	/* flush previous line so we don't join this one to it */
3327c478bd9Sstevel@tonic-gate 	if (nojoin)
3337c478bd9Sstevel@tonic-gate 		oflush();
3347c478bd9Sstevel@tonic-gate 	/* nroff-type lines starting with '.' are not split nor joined */
3357c478bd9Sstevel@tonic-gate 	if (!nosplit && (nosplit = (*cp == L'.')))
3367c478bd9Sstevel@tonic-gate 		oflush();
3377c478bd9Sstevel@tonic-gate 	pfx = np;
3387c478bd9Sstevel@tonic-gate 	switch (crown_state) {
3397c478bd9Sstevel@tonic-gate 	case c_reset:
3407c478bd9Sstevel@tonic-gate 		crown_head = pfx;
3417c478bd9Sstevel@tonic-gate 		crown_state = c_head;
3427c478bd9Sstevel@tonic-gate 		break;
3437c478bd9Sstevel@tonic-gate 	case c_lead:
3447c478bd9Sstevel@tonic-gate 		crown_body = pfx;
3457c478bd9Sstevel@tonic-gate 		crown_state = c_body;
3467c478bd9Sstevel@tonic-gate 		break;
3477c478bd9Sstevel@tonic-gate 	case c_fixup:
3487c478bd9Sstevel@tonic-gate 		crown_body = pfx;
3497c478bd9Sstevel@tonic-gate 		crown_state = c_body;
3507c478bd9Sstevel@tonic-gate 		if (outp) {
3517c478bd9Sstevel@tonic-gate 			wchar_t s[BUFSIZ];
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 			*outp = L'\0';
3547c478bd9Sstevel@tonic-gate 			wscpy(s, &outbuf[crown_head]);
3557c478bd9Sstevel@tonic-gate 			outp = NOSTR;
3567c478bd9Sstevel@tonic-gate 			split(s);
3577c478bd9Sstevel@tonic-gate 		}
3587c478bd9Sstevel@tonic-gate 		break;
3597c478bd9Sstevel@tonic-gate 	}
3607c478bd9Sstevel@tonic-gate 	if (nosplit) {
3617c478bd9Sstevel@tonic-gate 		/* put whole input line onto outbuf and print it out */
3627c478bd9Sstevel@tonic-gate 		pack(cp);
3637c478bd9Sstevel@tonic-gate 		oflush();
3647c478bd9Sstevel@tonic-gate 	} else
3657c478bd9Sstevel@tonic-gate 		/*
3667c478bd9Sstevel@tonic-gate 		 * split puts current line onto outbuf, but splits it
3677c478bd9Sstevel@tonic-gate 		 * at word boundaries, if it exceeds desired length
3687c478bd9Sstevel@tonic-gate 		 */
3697c478bd9Sstevel@tonic-gate 		split(cp);
3707c478bd9Sstevel@tonic-gate 	if (nojoin)
3717c478bd9Sstevel@tonic-gate 		/*
3727c478bd9Sstevel@tonic-gate 		 * flush current line so next lines, if any,
3737c478bd9Sstevel@tonic-gate 		 * won't join to this one
3747c478bd9Sstevel@tonic-gate 		 */
3757c478bd9Sstevel@tonic-gate 		oflush();
3767c478bd9Sstevel@tonic-gate }
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate /*
3797c478bd9Sstevel@tonic-gate  * Split up the passed line into output "words" which are
3807c478bd9Sstevel@tonic-gate  * maximal strings of non-blanks with the blank separation
3817c478bd9Sstevel@tonic-gate  * attached at the end.  Pass these words along to the output
3827c478bd9Sstevel@tonic-gate  * line packer.
3837c478bd9Sstevel@tonic-gate  */
3847c478bd9Sstevel@tonic-gate 
385b7d62af5Sceastha static void
3867c478bd9Sstevel@tonic-gate csplit(wchar_t line[])
3877c478bd9Sstevel@tonic-gate {
388b7d62af5Sceastha 	wchar_t *cp, *cp2;
3897c478bd9Sstevel@tonic-gate 	wchar_t word[BUFSIZ];
3907c478bd9Sstevel@tonic-gate 	static const wchar_t *srchlist = (const wchar_t *) L".:!?";
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 	cp = line;
3937c478bd9Sstevel@tonic-gate 	while (*cp) {
3947c478bd9Sstevel@tonic-gate 		cp2 = word;
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 		/*
3977c478bd9Sstevel@tonic-gate 		 * Collect a 'word,' allowing it to contain escaped
3987c478bd9Sstevel@tonic-gate 		 * white space.
3997c478bd9Sstevel@tonic-gate 		 */
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 		while (*cp && !(iswspace(*cp))) {
4027c478bd9Sstevel@tonic-gate 			if (*cp == '\\' && iswspace(cp[1]))
4037c478bd9Sstevel@tonic-gate 				*cp2++ = *cp++;
4047c478bd9Sstevel@tonic-gate 			*cp2++ = *cp++;
4057c478bd9Sstevel@tonic-gate 		}
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 		/*
4087c478bd9Sstevel@tonic-gate 		 * Guarantee a space at end of line.
4097c478bd9Sstevel@tonic-gate 		 * Two spaces after end of sentence punctuation.
4107c478bd9Sstevel@tonic-gate 		 */
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 		if (*cp == L'\0') {
4137c478bd9Sstevel@tonic-gate 			*cp2++ = L' ';
4147c478bd9Sstevel@tonic-gate 			if (wschr(srchlist, cp[-1]) != NULL)
4157c478bd9Sstevel@tonic-gate 				*cp2++ = L' ';
4167c478bd9Sstevel@tonic-gate 		}
4177c478bd9Sstevel@tonic-gate 		while (iswspace(*cp))
4187c478bd9Sstevel@tonic-gate 			*cp2++ = *cp++;
4197c478bd9Sstevel@tonic-gate 		*cp2 = L'\0';
4207c478bd9Sstevel@tonic-gate 		pack(word);
4217c478bd9Sstevel@tonic-gate 	}
4227c478bd9Sstevel@tonic-gate }
4237c478bd9Sstevel@tonic-gate 
424b7d62af5Sceastha static void
4257c478bd9Sstevel@tonic-gate msplit(wchar_t line[])
4267c478bd9Sstevel@tonic-gate {
427b7d62af5Sceastha 	wchar_t *cp, *cp2, prev;
4287c478bd9Sstevel@tonic-gate 	wchar_t word[BUFSIZ];
4297c478bd9Sstevel@tonic-gate 	static const wchar_t *srchlist = (const wchar_t *) L".:!?";
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	cp = line;
4327c478bd9Sstevel@tonic-gate 	while (*cp) {
4337c478bd9Sstevel@tonic-gate 		cp2 = word;
4347c478bd9Sstevel@tonic-gate 		prev = *cp;
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 		/*
4377c478bd9Sstevel@tonic-gate 		 * Collect a 'word,' allowing it to contain escaped
4387c478bd9Sstevel@tonic-gate 		 * white space.
4397c478bd9Sstevel@tonic-gate 		 */
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 		while (*cp) {
4427c478bd9Sstevel@tonic-gate 			if (iswspace(*cp))
4437c478bd9Sstevel@tonic-gate 				break;
4447c478bd9Sstevel@tonic-gate 			if (_wckind(*cp) != _wckind(prev))
4457c478bd9Sstevel@tonic-gate 				if (wcsetno(*cp) != 0 || wcsetno(prev) != 0)
4467c478bd9Sstevel@tonic-gate 					break;
4477c478bd9Sstevel@tonic-gate 			if (*cp == '\\' && iswspace(cp[1]))
4487c478bd9Sstevel@tonic-gate 				*cp2++ = *cp++;
4497c478bd9Sstevel@tonic-gate 			prev = *cp;
4507c478bd9Sstevel@tonic-gate 			*cp2++ = *cp++;
4517c478bd9Sstevel@tonic-gate 		}
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 		/*
4547c478bd9Sstevel@tonic-gate 		 * Guarantee a space at end of line.
4557c478bd9Sstevel@tonic-gate 		 * Two spaces after end of sentence punctuation.
4567c478bd9Sstevel@tonic-gate 		 */
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 		if (*cp == L'\0') {
4597c478bd9Sstevel@tonic-gate 			*cp2++ = L' ';
4607c478bd9Sstevel@tonic-gate 			if (wschr(srchlist, cp[-1]) != NULL)
4617c478bd9Sstevel@tonic-gate 				*cp2++ = L' ';
4627c478bd9Sstevel@tonic-gate 		}
4637c478bd9Sstevel@tonic-gate 		while (iswspace(*cp))
4647c478bd9Sstevel@tonic-gate 			*cp2++ = *cp++;
4657c478bd9Sstevel@tonic-gate 		*cp2 = L'\0';
4667c478bd9Sstevel@tonic-gate 		pack(word);
4677c478bd9Sstevel@tonic-gate 	}
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate /*
4717c478bd9Sstevel@tonic-gate  * Output section.
4727c478bd9Sstevel@tonic-gate  * Build up line images from the words passed in.  Prefix
4737c478bd9Sstevel@tonic-gate  * each line with correct number of blanks.  The buffer "outbuf"
4747c478bd9Sstevel@tonic-gate  * contains the current partial line image, including prefixed blanks.
4757c478bd9Sstevel@tonic-gate  * "outp" points to the next available space therein.  When outp is NOSTR,
4767c478bd9Sstevel@tonic-gate  * there ain't nothing in there yet.  At the bottom of this whole mess,
4777c478bd9Sstevel@tonic-gate  * leading tabs are reinserted.
4787c478bd9Sstevel@tonic-gate  */
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate /*
4817c478bd9Sstevel@tonic-gate  * Pack a word onto the output line.  If this is the beginning of
4827c478bd9Sstevel@tonic-gate  * the line, push on the appropriately-sized string of blanks first.
4837c478bd9Sstevel@tonic-gate  * If the word won't fit on the current line, flush and begin a new
4847c478bd9Sstevel@tonic-gate  * line.  If the word is too long to fit all by itself on a line,
4857c478bd9Sstevel@tonic-gate  * just give it its own and hope for the best.
4867c478bd9Sstevel@tonic-gate  */
4877c478bd9Sstevel@tonic-gate 
488b7d62af5Sceastha static void
4897c478bd9Sstevel@tonic-gate pack(wchar_t word[])
4907c478bd9Sstevel@tonic-gate {
491b7d62af5Sceastha 	wchar_t *cp;
492b7d62af5Sceastha 	int s, t;
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 	if (outp == NOSTR)
4957c478bd9Sstevel@tonic-gate 		leadin();
4967c478bd9Sstevel@tonic-gate 	t = wscol(word);
4977c478bd9Sstevel@tonic-gate 	*outp = L'\0';
4987c478bd9Sstevel@tonic-gate 	s = wscol(outbuf);
4997c478bd9Sstevel@tonic-gate 	if (t+s <= width) {
500e1ab4a07SJohn Sonnenschein 		for (cp = word; *cp; *outp++ = *cp++) {
501e1ab4a07SJohn Sonnenschein 		}
5027c478bd9Sstevel@tonic-gate 		return;
5037c478bd9Sstevel@tonic-gate 	}
5047c478bd9Sstevel@tonic-gate 	if (s > filler) {
5057c478bd9Sstevel@tonic-gate 		oflush();
5067c478bd9Sstevel@tonic-gate 		leadin();
5077c478bd9Sstevel@tonic-gate 	}
508e1ab4a07SJohn Sonnenschein 	for (cp = word; *cp; *outp++ = *cp++) {
509e1ab4a07SJohn Sonnenschein 	}
5107c478bd9Sstevel@tonic-gate }
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate /*
5137c478bd9Sstevel@tonic-gate  * If there is anything on the current output line, send it on
5147c478bd9Sstevel@tonic-gate  * its way.  Set outp to NOSTR to indicate the absence of the current
5157c478bd9Sstevel@tonic-gate  * line prefix.
5167c478bd9Sstevel@tonic-gate  */
5177c478bd9Sstevel@tonic-gate 
518b7d62af5Sceastha static void
5197c478bd9Sstevel@tonic-gate oflush(void)
5207c478bd9Sstevel@tonic-gate {
5217c478bd9Sstevel@tonic-gate 	if (outp == NOSTR)
5227c478bd9Sstevel@tonic-gate 		return;
5237c478bd9Sstevel@tonic-gate 	*outp = L'\0';
5247c478bd9Sstevel@tonic-gate 	tabulate(outbuf);
5257c478bd9Sstevel@tonic-gate 	outp = NOSTR;
5267c478bd9Sstevel@tonic-gate }
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate /*
5297c478bd9Sstevel@tonic-gate  * Take the passed line buffer, insert leading tabs where possible, and
5307c478bd9Sstevel@tonic-gate  * output on standard output (finally).
5317c478bd9Sstevel@tonic-gate  */
5327c478bd9Sstevel@tonic-gate 
533b7d62af5Sceastha static void
5347c478bd9Sstevel@tonic-gate tabulate(wchar_t line[])
5357c478bd9Sstevel@tonic-gate {
536b7d62af5Sceastha 	wchar_t *cp;
537b7d62af5Sceastha 	int b, t;
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 	/* Toss trailing blanks in the output line */
5417c478bd9Sstevel@tonic-gate 	cp = line + wslen(line) - 1;
5427c478bd9Sstevel@tonic-gate 	while (cp >= line && *cp == L' ')
5437c478bd9Sstevel@tonic-gate 		cp--;
5447c478bd9Sstevel@tonic-gate 	*++cp = L'\0';
5457c478bd9Sstevel@tonic-gate 	/* Count the leading blank space and tabulate */
546e1ab4a07SJohn Sonnenschein 	for (cp = line; *cp == L' '; cp++) {
547e1ab4a07SJohn Sonnenschein 	}
5487c478bd9Sstevel@tonic-gate 	b = cp - line;
5497c478bd9Sstevel@tonic-gate 	t = b >> 3;
5507c478bd9Sstevel@tonic-gate 	b &= 07;
5517c478bd9Sstevel@tonic-gate 	if (t > 0)
5525e924907SJohn Sonnenschein 		do {
5537c478bd9Sstevel@tonic-gate 			putc('\t', stdout);
5545e924907SJohn Sonnenschein 		} while (--t);
5557c478bd9Sstevel@tonic-gate 	if (b > 0)
5565e924907SJohn Sonnenschein 		do {
5577c478bd9Sstevel@tonic-gate 			putc(' ', stdout);
5585e924907SJohn Sonnenschein 		} while (--b);
5597c478bd9Sstevel@tonic-gate 	while (*cp)
5607c478bd9Sstevel@tonic-gate 		putwc(*cp++, stdout);
5617c478bd9Sstevel@tonic-gate 	putc('\n', stdout);
5627c478bd9Sstevel@tonic-gate }
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate /*
5657c478bd9Sstevel@tonic-gate  * Initialize the output line with the appropriate number of
5667c478bd9Sstevel@tonic-gate  * leading blanks.
5677c478bd9Sstevel@tonic-gate  */
5687c478bd9Sstevel@tonic-gate 
569b7d62af5Sceastha static void
570b7d62af5Sceastha leadin(void)
5717c478bd9Sstevel@tonic-gate {
572b7d62af5Sceastha 	int b;
573b7d62af5Sceastha 	wchar_t *cp;
574b7d62af5Sceastha 	int l;
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	switch (crown_state) {
5777c478bd9Sstevel@tonic-gate 	case c_head:
5787c478bd9Sstevel@tonic-gate 		l = crown_head;
5797c478bd9Sstevel@tonic-gate 		crown_state = c_lead;
5807c478bd9Sstevel@tonic-gate 		break;
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	case c_lead:
5837c478bd9Sstevel@tonic-gate 	case c_fixup:
5847c478bd9Sstevel@tonic-gate 		l = crown_head;
5857c478bd9Sstevel@tonic-gate 		crown_state = c_fixup;
5867c478bd9Sstevel@tonic-gate 		break;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	case c_body:
5897c478bd9Sstevel@tonic-gate 		l = crown_body;
5907c478bd9Sstevel@tonic-gate 		break;
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	default:
5937c478bd9Sstevel@tonic-gate 		l = pfx;
5947c478bd9Sstevel@tonic-gate 		break;
5957c478bd9Sstevel@tonic-gate 	}
5967c478bd9Sstevel@tonic-gate 	filler = l;
5977c478bd9Sstevel@tonic-gate 	for (b = 0, cp = outbuf; b < l; b++)
5987c478bd9Sstevel@tonic-gate 		*cp++ = L' ';
5997c478bd9Sstevel@tonic-gate 	outp = cp;
6007c478bd9Sstevel@tonic-gate }
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate /*
6037c478bd9Sstevel@tonic-gate  * Is s1 a prefix of s2??
6047c478bd9Sstevel@tonic-gate  */
6057c478bd9Sstevel@tonic-gate 
606b7d62af5Sceastha static int
6077c478bd9Sstevel@tonic-gate ispref(wchar_t *s1, wchar_t *s2)
6087c478bd9Sstevel@tonic-gate {
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	while (*s1 != L'\0' && *s2 != L'\0')
6117c478bd9Sstevel@tonic-gate 		if (*s1++ != *s2++)
6127c478bd9Sstevel@tonic-gate 			return (0);
6137c478bd9Sstevel@tonic-gate 	return (1);
6147c478bd9Sstevel@tonic-gate }
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate /*
6177c478bd9Sstevel@tonic-gate  * Set an input option
6187c478bd9Sstevel@tonic-gate  */
6197c478bd9Sstevel@tonic-gate 
620b7d62af5Sceastha static int
621b7d62af5Sceastha setopt(char *cp)
6227c478bd9Sstevel@tonic-gate {
6237c478bd9Sstevel@tonic-gate 	static int ws = 0;
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 	if (*cp == '-') {
6267c478bd9Sstevel@tonic-gate 		if (cp[1] == 'c' && cp[2] == '\0') {
6277c478bd9Sstevel@tonic-gate 			crown_state = c_reset;
6287c478bd9Sstevel@tonic-gate 			return (1);
6297c478bd9Sstevel@tonic-gate 		}
6307c478bd9Sstevel@tonic-gate 		if (cp[1] == 's' && cp[2] == '\0') {
6317c478bd9Sstevel@tonic-gate 			nojoin = 1;
6327c478bd9Sstevel@tonic-gate 			return (1);
6337c478bd9Sstevel@tonic-gate 		}
6347c478bd9Sstevel@tonic-gate 		if (cp[1] == 'w' && cp[2] == '\0') {
6357c478bd9Sstevel@tonic-gate 			ws++;
6367c478bd9Sstevel@tonic-gate 			return (1);
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 		width = atoi(cp+1);
6397c478bd9Sstevel@tonic-gate 	} else if (ws) {
6407c478bd9Sstevel@tonic-gate 		width = atoi(cp);
6417c478bd9Sstevel@tonic-gate 		ws = 0;
6427c478bd9Sstevel@tonic-gate 	} else
6437c478bd9Sstevel@tonic-gate 		return (0);
6447c478bd9Sstevel@tonic-gate 	if (width <= 0 || width >= BUFSIZ-2) {
6457c478bd9Sstevel@tonic-gate 		fprintf(stderr, "fmt:  bad width: %d\n", width);
6467c478bd9Sstevel@tonic-gate 		exit(1);
6477c478bd9Sstevel@tonic-gate 	}
6487c478bd9Sstevel@tonic-gate 	return (1);
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate #define	LIB_WDRESOLVE	"/usr/lib/locale/%s/LC_CTYPE/wdresolve.so"
6537c478bd9Sstevel@tonic-gate #define	WCHKIND		"_wdchkind_"
6547c478bd9Sstevel@tonic-gate 
655b7d62af5Sceastha static int	_wckind_c_locale(wchar_t);
6567c478bd9Sstevel@tonic-gate 
657b7d62af5Sceastha static int	(*__wckind)(wchar_t) = _wckind_c_locale;
6587c478bd9Sstevel@tonic-gate static void	*dlhandle = NULL;
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 
661b7d62af5Sceastha static void
662b7d62af5Sceastha _wckind_init(void)
6637c478bd9Sstevel@tonic-gate {
6647c478bd9Sstevel@tonic-gate 	char	*locale;
6657c478bd9Sstevel@tonic-gate 	char	path[MAXPATHLEN + 1];
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	if (dlhandle != NULL) {
6697c478bd9Sstevel@tonic-gate 		(void) dlclose(dlhandle);
6707c478bd9Sstevel@tonic-gate 		dlhandle = NULL;
6717c478bd9Sstevel@tonic-gate 	}
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	locale = setlocale(LC_CTYPE, NULL);
6747c478bd9Sstevel@tonic-gate 	if (strcmp(locale, "C") == 0)
6757c478bd9Sstevel@tonic-gate 		goto c_locale;
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 	(void) sprintf(path, LIB_WDRESOLVE, locale);
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 	if ((dlhandle = dlopen(path, RTLD_LAZY)) != NULL) {
680b7d62af5Sceastha 		__wckind = (int (*)(wchar_t))dlsym(dlhandle, WCHKIND);
6817c478bd9Sstevel@tonic-gate 		if (__wckind != NULL)
6827c478bd9Sstevel@tonic-gate 			return;
6837c478bd9Sstevel@tonic-gate 		(void) dlclose(dlhandle);
6847c478bd9Sstevel@tonic-gate 		dlhandle = NULL;
6857c478bd9Sstevel@tonic-gate 	}
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate c_locale:
6887c478bd9Sstevel@tonic-gate 	__wckind = _wckind_c_locale;
6897c478bd9Sstevel@tonic-gate }
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate int
693b7d62af5Sceastha _wckind(wchar_t wc)
6947c478bd9Sstevel@tonic-gate {
6957c478bd9Sstevel@tonic-gate 	return (*__wckind) (wc);
6967c478bd9Sstevel@tonic-gate }
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate static int
700b7d62af5Sceastha _wckind_c_locale(wchar_t wc)
7017c478bd9Sstevel@tonic-gate {
7027c478bd9Sstevel@tonic-gate 	int	ret;
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 	/*
7057c478bd9Sstevel@tonic-gate 	 * DEPEND_ON_ANSIC: L notion for the character is new in
7067c478bd9Sstevel@tonic-gate 	 * ANSI-C, k&r compiler won't work.
7077c478bd9Sstevel@tonic-gate 	 */
7087c478bd9Sstevel@tonic-gate 	if (iswascii(wc))
7097c478bd9Sstevel@tonic-gate 		ret = (iswalnum(wc) || wc == L'_') ? 0 : 1;
7107c478bd9Sstevel@tonic-gate 	else
7117c478bd9Sstevel@tonic-gate 		ret = wcsetno(wc) + 1;
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 	return (ret);
7147c478bd9Sstevel@tonic-gate }
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate /*
7177c478bd9Sstevel@tonic-gate  * header_chk -
7187c478bd9Sstevel@tonic-gate  * Called when done looking for a set mail header lines.
7197c478bd9Sstevel@tonic-gate  * Either a blank line was seen, or EOF was reached.
7207c478bd9Sstevel@tonic-gate  *
7217c478bd9Sstevel@tonic-gate  * Verifies if current hdrbuf of potential mail header lines
7227c478bd9Sstevel@tonic-gate  * is really a mail header.  A mail header must be at least 2
7237c478bd9Sstevel@tonic-gate  * lines and more than half of them must start with one of the
7247c478bd9Sstevel@tonic-gate  * known mail header strings in headnames.
7257c478bd9Sstevel@tonic-gate  *
7267c478bd9Sstevel@tonic-gate  * header_chk sets hdr_state to do_hdr if hdrbuf contained a valid
7277c478bd9Sstevel@tonic-gate  * mail header.  Otherwise, it sets hdr_state to flush_hdr.
7287c478bd9Sstevel@tonic-gate  *
7297c478bd9Sstevel@tonic-gate  * h_lines = hdrbuf index for next line to be saved;
7307c478bd9Sstevel@tonic-gate  *	     also indicates current # of lines in potential header
7317c478bd9Sstevel@tonic-gate  */
7327c478bd9Sstevel@tonic-gate static void
7337c478bd9Sstevel@tonic-gate header_chk(void)
7347c478bd9Sstevel@tonic-gate {
7357c478bd9Sstevel@tonic-gate 	wchar_t  *cp; 		/* ptr to current char of line */
7367c478bd9Sstevel@tonic-gate 	wchar_t **hp; 		/* ptr to current char of a valid */
7377c478bd9Sstevel@tonic-gate 				/* mail header string */
7387c478bd9Sstevel@tonic-gate 	int	  l;		/* index */
7397c478bd9Sstevel@tonic-gate 				/*
7407c478bd9Sstevel@tonic-gate 				 * number of lines in hdrbuf that look
7417c478bd9Sstevel@tonic-gate 				 * like mail header lines (start with
7427c478bd9Sstevel@tonic-gate 				 * a known mail header prefix)
7437c478bd9Sstevel@tonic-gate 				 */
7447c478bd9Sstevel@tonic-gate 	int	 hdrcount = 0;
7457c478bd9Sstevel@tonic-gate 		/* header must have at least 2 lines (h_lines > 1) */
7467c478bd9Sstevel@tonic-gate 		if (h_lines < 2) {
7477c478bd9Sstevel@tonic-gate 			hdr_state = flush_hdr;
7487c478bd9Sstevel@tonic-gate 			return;
7497c478bd9Sstevel@tonic-gate 		}
7507c478bd9Sstevel@tonic-gate 		/*
7517c478bd9Sstevel@tonic-gate 		 * go through each line in hdrbuf and see how many
7527c478bd9Sstevel@tonic-gate 		 * look like mail header lines
7537c478bd9Sstevel@tonic-gate 		 */
7547c478bd9Sstevel@tonic-gate 		for (l = 0; l < h_lines; l++) {
7557c478bd9Sstevel@tonic-gate 			/* skip initial blanks */
756e1ab4a07SJohn Sonnenschein 			for (cp = hdrbuf[l]; *cp == L' '; cp++) {
757e1ab4a07SJohn Sonnenschein 			}
7587c478bd9Sstevel@tonic-gate 			for (hp = &headnames[0]; *hp != (wchar_t *)0; hp++)
7597c478bd9Sstevel@tonic-gate 				if (ispref(*hp, cp)) {
7607c478bd9Sstevel@tonic-gate 					hdrcount++;
7617c478bd9Sstevel@tonic-gate 					break;
7627c478bd9Sstevel@tonic-gate 				}
7637c478bd9Sstevel@tonic-gate 		}
7647c478bd9Sstevel@tonic-gate 		/*
7657c478bd9Sstevel@tonic-gate 		 * if over half match, we'll assume this is a header;
7667c478bd9Sstevel@tonic-gate 		 * set hdr_state to indicate whether to treat
7677c478bd9Sstevel@tonic-gate 		 * these lines as mail header (do_hdr) or not (flush_hdr)
7687c478bd9Sstevel@tonic-gate 		 */
7697c478bd9Sstevel@tonic-gate 		if (hdrcount > h_lines / 2)
7707c478bd9Sstevel@tonic-gate 			hdr_state = do_hdr;
7717c478bd9Sstevel@tonic-gate 		else
7727c478bd9Sstevel@tonic-gate 			hdr_state = flush_hdr;
7737c478bd9Sstevel@tonic-gate }
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate /*
7767c478bd9Sstevel@tonic-gate  * fill_hdrbuf -
7777c478bd9Sstevel@tonic-gate  * Save given input line into next element of hdrbuf,
7787c478bd9Sstevel@tonic-gate  * as a potential mail header line, to be processed later
7797c478bd9Sstevel@tonic-gate  * once we decide whether or not the contents of hdrbuf is
7807c478bd9Sstevel@tonic-gate  * really a mail header, via header_chk().
7817c478bd9Sstevel@tonic-gate  *
7827c478bd9Sstevel@tonic-gate  * Does not allow hdrbuf to exceed MAXLINES lines.
7837c478bd9Sstevel@tonic-gate  * Dynamically allocates space for each line.  If we are unable
7847c478bd9Sstevel@tonic-gate  * to allocate space for the current string, stop special mail
7857c478bd9Sstevel@tonic-gate  * header preservation at this point and continue formatting
7867c478bd9Sstevel@tonic-gate  * without it.
7877c478bd9Sstevel@tonic-gate  */
7887c478bd9Sstevel@tonic-gate static void
7897c478bd9Sstevel@tonic-gate fill_hdrbuf(wchar_t line[])
7907c478bd9Sstevel@tonic-gate {
7917c478bd9Sstevel@tonic-gate 	wchar_t *cp;	/* pointer to characters in input line */
7927c478bd9Sstevel@tonic-gate 	int	 i;	/* index into characters a hdrbuf line */
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	if (h_lines >= MAXLINES) {
7957c478bd9Sstevel@tonic-gate 		/*
7967c478bd9Sstevel@tonic-gate 		 * if we run over MAXLINES potential mail header
7977c478bd9Sstevel@tonic-gate 		 * lines, stop checking--this is most likely NOT a
7987c478bd9Sstevel@tonic-gate 		 * mail header; flush out the hdrbuf, then process
7997c478bd9Sstevel@tonic-gate 		 * the current 'line' normally.
8007c478bd9Sstevel@tonic-gate 		 */
8017c478bd9Sstevel@tonic-gate 		hdr_state = flush_hdr;
8027c478bd9Sstevel@tonic-gate 		process_hdrbuf();
8037c478bd9Sstevel@tonic-gate 		prefix(line);
8047c478bd9Sstevel@tonic-gate 		return;
8057c478bd9Sstevel@tonic-gate 	}
8067c478bd9Sstevel@tonic-gate 	hdrbuf[h_lines] = (wchar_t *)malloc(sizeof (wchar_t) *
8077c478bd9Sstevel@tonic-gate 	    (wslen(line) + 1));
8087c478bd9Sstevel@tonic-gate 	if (hdrbuf[h_lines] == NULL) {
8097c478bd9Sstevel@tonic-gate 		perror("malloc");
8107c478bd9Sstevel@tonic-gate 		fprintf(stderr, "fmt: unable to do mail header preservation\n");
8117c478bd9Sstevel@tonic-gate 		errs++;
8127c478bd9Sstevel@tonic-gate 		/*
8137c478bd9Sstevel@tonic-gate 		 * Can't process mail header; flush current contents
8147c478bd9Sstevel@tonic-gate 		 * of mail header and continue with no more mail
8157c478bd9Sstevel@tonic-gate 		 * header processing
8167c478bd9Sstevel@tonic-gate 		 */
8177c478bd9Sstevel@tonic-gate 		if (h_lines == 0)
8187c478bd9Sstevel@tonic-gate 			/* hdrbuf is empty; process this line normally */
8197c478bd9Sstevel@tonic-gate 			prefix(line);
8207c478bd9Sstevel@tonic-gate 		else {
8217c478bd9Sstevel@tonic-gate 			hdr_state = flush_hdr;
8227c478bd9Sstevel@tonic-gate 			for (i = 0; i < h_lines; i++) {
8237c478bd9Sstevel@tonic-gate 				prefix(hdrbuf[i]);
8247c478bd9Sstevel@tonic-gate 				free(hdrbuf[i]);
8257c478bd9Sstevel@tonic-gate 			}
8267c478bd9Sstevel@tonic-gate 			h_lines = 0;
8277c478bd9Sstevel@tonic-gate 		}
8287c478bd9Sstevel@tonic-gate 		hdr_state = off;
8297c478bd9Sstevel@tonic-gate 		return;
8307c478bd9Sstevel@tonic-gate 	}
8317c478bd9Sstevel@tonic-gate 	/* save this line as a potential mail header line */
832e1ab4a07SJohn Sonnenschein 	for (i = 0, cp = line; (hdrbuf[h_lines][i] = *cp) != L'\0'; i++, cp++) {
833e1ab4a07SJohn Sonnenschein 	}
8347c478bd9Sstevel@tonic-gate 	h_lines++;
8357c478bd9Sstevel@tonic-gate }
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate /*
8387c478bd9Sstevel@tonic-gate  * process_hdrbuf -
8397c478bd9Sstevel@tonic-gate  * Outputs the lines currently stored in hdrbuf, according
8407c478bd9Sstevel@tonic-gate  * to the current hdr_state value, assumed to be either do_hdr
8417c478bd9Sstevel@tonic-gate  * or flush_hdr.
8427c478bd9Sstevel@tonic-gate  * This should be called after doing a header_chk() to verify
8437c478bd9Sstevel@tonic-gate  * the hdrbuf and set the hdr_state flag.
8447c478bd9Sstevel@tonic-gate  */
8457c478bd9Sstevel@tonic-gate static void
8467c478bd9Sstevel@tonic-gate process_hdrbuf(void)
8477c478bd9Sstevel@tonic-gate {
8487c478bd9Sstevel@tonic-gate int i;
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate 	for (i = 0; i < h_lines; i++) {
8517c478bd9Sstevel@tonic-gate 		prefix(hdrbuf[i]);
8527c478bd9Sstevel@tonic-gate 		free(hdrbuf[i]);
8537c478bd9Sstevel@tonic-gate 	}
8547c478bd9Sstevel@tonic-gate 	hdr_state = not_in_hdr;
8557c478bd9Sstevel@tonic-gate 	h_lines = 0;
8567c478bd9Sstevel@tonic-gate }
857