xref: /illumos-gate/usr/src/cmd/fmt/fmt.c (revision 40ed0010)
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 
48b7d62af5Sceastha #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);
89196c7f05SJoshua 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
main(int argc,char ** argv)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
fmt(FILE * fi)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);
236*40ed0010SToomas Soome 			/* FALLTHROUGH */
2377c478bd9Sstevel@tonic-gate 		case not_in_hdr:
2387c478bd9Sstevel@tonic-gate 			/* look for an initial mail header line */
2397c478bd9Sstevel@tonic-gate 			/* skip initial blanks */
240e1ab4a07SJohn Sonnenschein 			for (cp = canonb; *cp == L' '; cp++) {
241e1ab4a07SJohn Sonnenschein 			}
2427c478bd9Sstevel@tonic-gate 			/*
2437c478bd9Sstevel@tonic-gate 			 * Need to convert string from wchar_t to char,
244196c7f05SJoshua M. Clulow 			 * since this is what is_headline() expects.  Since we
2457c478bd9Sstevel@tonic-gate 			 * only want to make sure cp points to a "From" line
2467c478bd9Sstevel@tonic-gate 			 * of the email, we don't have to alloc
2477c478bd9Sstevel@tonic-gate 			 * BUFSIZ * MB_LEN_MAX to cbuf.
2487c478bd9Sstevel@tonic-gate 			 */
2497c478bd9Sstevel@tonic-gate 			wcstombs(cbuf, cp, (BUFSIZ - 1));
250196c7f05SJoshua M. Clulow 			if (is_headline(cbuf) == B_TRUE) {
2517c478bd9Sstevel@tonic-gate 				hdr_state = in_hdr;
2527c478bd9Sstevel@tonic-gate 				fill_hdrbuf(canonb);
2537c478bd9Sstevel@tonic-gate 			} else {
2547c478bd9Sstevel@tonic-gate 				/* no mail header line; process normally */
2557c478bd9Sstevel@tonic-gate 				prefix(canonb);
2567c478bd9Sstevel@tonic-gate 			}
2577c478bd9Sstevel@tonic-gate 			break;
2587c478bd9Sstevel@tonic-gate 		case in_hdr:
2597c478bd9Sstevel@tonic-gate 			/* already saw 1st mail header line; look for more */
2607c478bd9Sstevel@tonic-gate 			if (canonb[0] == L'\0') {
2617c478bd9Sstevel@tonic-gate 				/*
2627c478bd9Sstevel@tonic-gate 				 * blank line means end of mail header;
2637c478bd9Sstevel@tonic-gate 				 * verify current mail header buffer
2647c478bd9Sstevel@tonic-gate 				 * then process it accordingly
2657c478bd9Sstevel@tonic-gate 				 */
2667c478bd9Sstevel@tonic-gate 				header_chk();
2677c478bd9Sstevel@tonic-gate 				process_hdrbuf();
2687c478bd9Sstevel@tonic-gate 				/* now process the current blank line */
2697c478bd9Sstevel@tonic-gate 				prefix(canonb);
2707c478bd9Sstevel@tonic-gate 			} else
2717c478bd9Sstevel@tonic-gate 				/*
2727c478bd9Sstevel@tonic-gate 				 * not a blank line--save this line as
2737c478bd9Sstevel@tonic-gate 				 * a potential mail header line
2747c478bd9Sstevel@tonic-gate 				 */
2757c478bd9Sstevel@tonic-gate 				fill_hdrbuf(canonb);
2767c478bd9Sstevel@tonic-gate 			break;
2777c478bd9Sstevel@tonic-gate 		}
2787c478bd9Sstevel@tonic-gate 		if (c != EOF)
2797c478bd9Sstevel@tonic-gate 			c = getwc(fi);
2807c478bd9Sstevel@tonic-gate 	}
2817c478bd9Sstevel@tonic-gate 	/*
2827c478bd9Sstevel@tonic-gate 	 * end of this file--make sure we process the stuff in
2837c478bd9Sstevel@tonic-gate 	 * hdrbuf before we're finished
2847c478bd9Sstevel@tonic-gate 	 */
2857c478bd9Sstevel@tonic-gate 	if (hdr_state == in_hdr) {
2867c478bd9Sstevel@tonic-gate 		header_chk();
2877c478bd9Sstevel@tonic-gate 		process_hdrbuf();
2887c478bd9Sstevel@tonic-gate 	}
2897c478bd9Sstevel@tonic-gate }
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate /*
2927c478bd9Sstevel@tonic-gate  * Take a line devoid of tabs and other garbage and determine its
2937c478bd9Sstevel@tonic-gate  * blank prefix.  If the indent changes, call for a linebreak.
2947c478bd9Sstevel@tonic-gate  * If the input line is blank, echo the blank line on the output.
2957c478bd9Sstevel@tonic-gate  * Finally, if the line minus the prefix is a mail header, try to keep
2967c478bd9Sstevel@tonic-gate  * it on a line by itself.
2977c478bd9Sstevel@tonic-gate  */
2987c478bd9Sstevel@tonic-gate 
299b7d62af5Sceastha static void
prefix(wchar_t line[])3007c478bd9Sstevel@tonic-gate prefix(wchar_t line[])
3017c478bd9Sstevel@tonic-gate {
302b7d62af5Sceastha 	wchar_t *cp;
303b7d62af5Sceastha 	int np;
3047c478bd9Sstevel@tonic-gate 	int nosplit = 0;	/* flag set if line should not be split */
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	if (line[0] == L'\0') {
3077c478bd9Sstevel@tonic-gate 		oflush();
3087c478bd9Sstevel@tonic-gate 		putchar('\n');
3097c478bd9Sstevel@tonic-gate 		if (crown_state != c_none)
3107c478bd9Sstevel@tonic-gate 			crown_state = c_reset;
3117c478bd9Sstevel@tonic-gate 		return;
3127c478bd9Sstevel@tonic-gate 	}
313e1ab4a07SJohn Sonnenschein 	for (cp = line; *cp == L' '; cp++) {
314e1ab4a07SJohn Sonnenschein 	}
3157c478bd9Sstevel@tonic-gate 	np = cp - line;
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	/*
3187c478bd9Sstevel@tonic-gate 	 * The following horrible expression attempts to avoid linebreaks
3197c478bd9Sstevel@tonic-gate 	 * when the indent changes due to a paragraph.
3207c478bd9Sstevel@tonic-gate 	 */
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 	if (crown_state == c_none && np != pfx && (np > pfx || abs(pfx-np) > 8))
3237c478bd9Sstevel@tonic-gate 		oflush();
3247c478bd9Sstevel@tonic-gate 	/*
3257c478bd9Sstevel@tonic-gate 	 * if this is a mail header line, don't split it; flush previous
3267c478bd9Sstevel@tonic-gate 	 * line, if any, so we don't join this line to it
3277c478bd9Sstevel@tonic-gate 	 */
3287c478bd9Sstevel@tonic-gate 	if (hdr_state == do_hdr) {
3297c478bd9Sstevel@tonic-gate 		nosplit = 1;
3307c478bd9Sstevel@tonic-gate 		oflush();
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate 	/* flush previous line so we don't join this one to it */
3337c478bd9Sstevel@tonic-gate 	if (nojoin)
3347c478bd9Sstevel@tonic-gate 		oflush();
3357c478bd9Sstevel@tonic-gate 	/* nroff-type lines starting with '.' are not split nor joined */
3367c478bd9Sstevel@tonic-gate 	if (!nosplit && (nosplit = (*cp == L'.')))
3377c478bd9Sstevel@tonic-gate 		oflush();
3387c478bd9Sstevel@tonic-gate 	pfx = np;
3397c478bd9Sstevel@tonic-gate 	switch (crown_state) {
3407c478bd9Sstevel@tonic-gate 	case c_reset:
3417c478bd9Sstevel@tonic-gate 		crown_head = pfx;
3427c478bd9Sstevel@tonic-gate 		crown_state = c_head;
3437c478bd9Sstevel@tonic-gate 		break;
3447c478bd9Sstevel@tonic-gate 	case c_lead:
3457c478bd9Sstevel@tonic-gate 		crown_body = pfx;
3467c478bd9Sstevel@tonic-gate 		crown_state = c_body;
3477c478bd9Sstevel@tonic-gate 		break;
3487c478bd9Sstevel@tonic-gate 	case c_fixup:
3497c478bd9Sstevel@tonic-gate 		crown_body = pfx;
3507c478bd9Sstevel@tonic-gate 		crown_state = c_body;
3517c478bd9Sstevel@tonic-gate 		if (outp) {
3527c478bd9Sstevel@tonic-gate 			wchar_t s[BUFSIZ];
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 			*outp = L'\0';
3557c478bd9Sstevel@tonic-gate 			wscpy(s, &outbuf[crown_head]);
3567c478bd9Sstevel@tonic-gate 			outp = NOSTR;
3577c478bd9Sstevel@tonic-gate 			split(s);
3587c478bd9Sstevel@tonic-gate 		}
3597c478bd9Sstevel@tonic-gate 		break;
3607c478bd9Sstevel@tonic-gate 	}
3617c478bd9Sstevel@tonic-gate 	if (nosplit) {
3627c478bd9Sstevel@tonic-gate 		/* put whole input line onto outbuf and print it out */
3637c478bd9Sstevel@tonic-gate 		pack(cp);
3647c478bd9Sstevel@tonic-gate 		oflush();
3657c478bd9Sstevel@tonic-gate 	} else
3667c478bd9Sstevel@tonic-gate 		/*
3677c478bd9Sstevel@tonic-gate 		 * split puts current line onto outbuf, but splits it
3687c478bd9Sstevel@tonic-gate 		 * at word boundaries, if it exceeds desired length
3697c478bd9Sstevel@tonic-gate 		 */
3707c478bd9Sstevel@tonic-gate 		split(cp);
3717c478bd9Sstevel@tonic-gate 	if (nojoin)
3727c478bd9Sstevel@tonic-gate 		/*
3737c478bd9Sstevel@tonic-gate 		 * flush current line so next lines, if any,
3747c478bd9Sstevel@tonic-gate 		 * won't join to this one
3757c478bd9Sstevel@tonic-gate 		 */
3767c478bd9Sstevel@tonic-gate 		oflush();
3777c478bd9Sstevel@tonic-gate }
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate /*
3807c478bd9Sstevel@tonic-gate  * Split up the passed line into output "words" which are
3817c478bd9Sstevel@tonic-gate  * maximal strings of non-blanks with the blank separation
3827c478bd9Sstevel@tonic-gate  * attached at the end.  Pass these words along to the output
3837c478bd9Sstevel@tonic-gate  * line packer.
3847c478bd9Sstevel@tonic-gate  */
3857c478bd9Sstevel@tonic-gate 
386b7d62af5Sceastha static void
csplit(wchar_t line[])3877c478bd9Sstevel@tonic-gate csplit(wchar_t line[])
3887c478bd9Sstevel@tonic-gate {
389b7d62af5Sceastha 	wchar_t *cp, *cp2;
3907c478bd9Sstevel@tonic-gate 	wchar_t word[BUFSIZ];
3917c478bd9Sstevel@tonic-gate 	static const wchar_t *srchlist = (const wchar_t *) L".:!?";
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	cp = line;
3947c478bd9Sstevel@tonic-gate 	while (*cp) {
3957c478bd9Sstevel@tonic-gate 		cp2 = word;
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 		/*
3987c478bd9Sstevel@tonic-gate 		 * Collect a 'word,' allowing it to contain escaped
3997c478bd9Sstevel@tonic-gate 		 * white space.
4007c478bd9Sstevel@tonic-gate 		 */
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 		while (*cp && !(iswspace(*cp))) {
4037c478bd9Sstevel@tonic-gate 			if (*cp == '\\' && iswspace(cp[1]))
4047c478bd9Sstevel@tonic-gate 				*cp2++ = *cp++;
4057c478bd9Sstevel@tonic-gate 			*cp2++ = *cp++;
4067c478bd9Sstevel@tonic-gate 		}
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 		/*
4097c478bd9Sstevel@tonic-gate 		 * Guarantee a space at end of line.
4107c478bd9Sstevel@tonic-gate 		 * Two spaces after end of sentence punctuation.
4117c478bd9Sstevel@tonic-gate 		 */
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 		if (*cp == L'\0') {
4147c478bd9Sstevel@tonic-gate 			*cp2++ = L' ';
4157c478bd9Sstevel@tonic-gate 			if (wschr(srchlist, cp[-1]) != NULL)
4167c478bd9Sstevel@tonic-gate 				*cp2++ = L' ';
4177c478bd9Sstevel@tonic-gate 		}
4187c478bd9Sstevel@tonic-gate 		while (iswspace(*cp))
4197c478bd9Sstevel@tonic-gate 			*cp2++ = *cp++;
4207c478bd9Sstevel@tonic-gate 		*cp2 = L'\0';
4217c478bd9Sstevel@tonic-gate 		pack(word);
4227c478bd9Sstevel@tonic-gate 	}
4237c478bd9Sstevel@tonic-gate }
4247c478bd9Sstevel@tonic-gate 
425b7d62af5Sceastha static void
msplit(wchar_t line[])4267c478bd9Sstevel@tonic-gate msplit(wchar_t line[])
4277c478bd9Sstevel@tonic-gate {
428b7d62af5Sceastha 	wchar_t *cp, *cp2, prev;
4297c478bd9Sstevel@tonic-gate 	wchar_t word[BUFSIZ];
4307c478bd9Sstevel@tonic-gate 	static const wchar_t *srchlist = (const wchar_t *) L".:!?";
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	cp = line;
4337c478bd9Sstevel@tonic-gate 	while (*cp) {
4347c478bd9Sstevel@tonic-gate 		cp2 = word;
4357c478bd9Sstevel@tonic-gate 		prev = *cp;
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate 		/*
4387c478bd9Sstevel@tonic-gate 		 * Collect a 'word,' allowing it to contain escaped
4397c478bd9Sstevel@tonic-gate 		 * white space.
4407c478bd9Sstevel@tonic-gate 		 */
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 		while (*cp) {
4437c478bd9Sstevel@tonic-gate 			if (iswspace(*cp))
4447c478bd9Sstevel@tonic-gate 				break;
4457c478bd9Sstevel@tonic-gate 			if (_wckind(*cp) != _wckind(prev))
4467c478bd9Sstevel@tonic-gate 				if (wcsetno(*cp) != 0 || wcsetno(prev) != 0)
4477c478bd9Sstevel@tonic-gate 					break;
4487c478bd9Sstevel@tonic-gate 			if (*cp == '\\' && iswspace(cp[1]))
4497c478bd9Sstevel@tonic-gate 				*cp2++ = *cp++;
4507c478bd9Sstevel@tonic-gate 			prev = *cp;
4517c478bd9Sstevel@tonic-gate 			*cp2++ = *cp++;
4527c478bd9Sstevel@tonic-gate 		}
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 		/*
4557c478bd9Sstevel@tonic-gate 		 * Guarantee a space at end of line.
4567c478bd9Sstevel@tonic-gate 		 * Two spaces after end of sentence punctuation.
4577c478bd9Sstevel@tonic-gate 		 */
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 		if (*cp == L'\0') {
4607c478bd9Sstevel@tonic-gate 			*cp2++ = L' ';
4617c478bd9Sstevel@tonic-gate 			if (wschr(srchlist, cp[-1]) != NULL)
4627c478bd9Sstevel@tonic-gate 				*cp2++ = L' ';
4637c478bd9Sstevel@tonic-gate 		}
4647c478bd9Sstevel@tonic-gate 		while (iswspace(*cp))
4657c478bd9Sstevel@tonic-gate 			*cp2++ = *cp++;
4667c478bd9Sstevel@tonic-gate 		*cp2 = L'\0';
4677c478bd9Sstevel@tonic-gate 		pack(word);
4687c478bd9Sstevel@tonic-gate 	}
4697c478bd9Sstevel@tonic-gate }
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate /*
4727c478bd9Sstevel@tonic-gate  * Output section.
4737c478bd9Sstevel@tonic-gate  * Build up line images from the words passed in.  Prefix
4747c478bd9Sstevel@tonic-gate  * each line with correct number of blanks.  The buffer "outbuf"
4757c478bd9Sstevel@tonic-gate  * contains the current partial line image, including prefixed blanks.
4767c478bd9Sstevel@tonic-gate  * "outp" points to the next available space therein.  When outp is NOSTR,
4777c478bd9Sstevel@tonic-gate  * there ain't nothing in there yet.  At the bottom of this whole mess,
4787c478bd9Sstevel@tonic-gate  * leading tabs are reinserted.
4797c478bd9Sstevel@tonic-gate  */
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate /*
4827c478bd9Sstevel@tonic-gate  * Pack a word onto the output line.  If this is the beginning of
4837c478bd9Sstevel@tonic-gate  * the line, push on the appropriately-sized string of blanks first.
4847c478bd9Sstevel@tonic-gate  * If the word won't fit on the current line, flush and begin a new
4857c478bd9Sstevel@tonic-gate  * line.  If the word is too long to fit all by itself on a line,
4867c478bd9Sstevel@tonic-gate  * just give it its own and hope for the best.
4877c478bd9Sstevel@tonic-gate  */
4887c478bd9Sstevel@tonic-gate 
489b7d62af5Sceastha static void
pack(wchar_t word[])4907c478bd9Sstevel@tonic-gate pack(wchar_t word[])
4917c478bd9Sstevel@tonic-gate {
492b7d62af5Sceastha 	wchar_t *cp;
493b7d62af5Sceastha 	int s, t;
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	if (outp == NOSTR)
4967c478bd9Sstevel@tonic-gate 		leadin();
4977c478bd9Sstevel@tonic-gate 	t = wscol(word);
4987c478bd9Sstevel@tonic-gate 	*outp = L'\0';
4997c478bd9Sstevel@tonic-gate 	s = wscol(outbuf);
5007c478bd9Sstevel@tonic-gate 	if (t+s <= width) {
501e1ab4a07SJohn Sonnenschein 		for (cp = word; *cp; *outp++ = *cp++) {
502e1ab4a07SJohn Sonnenschein 		}
5037c478bd9Sstevel@tonic-gate 		return;
5047c478bd9Sstevel@tonic-gate 	}
5057c478bd9Sstevel@tonic-gate 	if (s > filler) {
5067c478bd9Sstevel@tonic-gate 		oflush();
5077c478bd9Sstevel@tonic-gate 		leadin();
5087c478bd9Sstevel@tonic-gate 	}
509e1ab4a07SJohn Sonnenschein 	for (cp = word; *cp; *outp++ = *cp++) {
510e1ab4a07SJohn Sonnenschein 	}
5117c478bd9Sstevel@tonic-gate }
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate /*
5147c478bd9Sstevel@tonic-gate  * If there is anything on the current output line, send it on
5157c478bd9Sstevel@tonic-gate  * its way.  Set outp to NOSTR to indicate the absence of the current
5167c478bd9Sstevel@tonic-gate  * line prefix.
5177c478bd9Sstevel@tonic-gate  */
5187c478bd9Sstevel@tonic-gate 
519b7d62af5Sceastha static void
oflush(void)5207c478bd9Sstevel@tonic-gate oflush(void)
5217c478bd9Sstevel@tonic-gate {
5227c478bd9Sstevel@tonic-gate 	if (outp == NOSTR)
5237c478bd9Sstevel@tonic-gate 		return;
5247c478bd9Sstevel@tonic-gate 	*outp = L'\0';
5257c478bd9Sstevel@tonic-gate 	tabulate(outbuf);
5267c478bd9Sstevel@tonic-gate 	outp = NOSTR;
5277c478bd9Sstevel@tonic-gate }
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate /*
5307c478bd9Sstevel@tonic-gate  * Take the passed line buffer, insert leading tabs where possible, and
5317c478bd9Sstevel@tonic-gate  * output on standard output (finally).
5327c478bd9Sstevel@tonic-gate  */
5337c478bd9Sstevel@tonic-gate 
534b7d62af5Sceastha static void
tabulate(wchar_t line[])5357c478bd9Sstevel@tonic-gate tabulate(wchar_t line[])
5367c478bd9Sstevel@tonic-gate {
537b7d62af5Sceastha 	wchar_t *cp;
538b7d62af5Sceastha 	int b, t;
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 	/* Toss trailing blanks in the output line */
5427c478bd9Sstevel@tonic-gate 	cp = line + wslen(line) - 1;
5437c478bd9Sstevel@tonic-gate 	while (cp >= line && *cp == L' ')
5447c478bd9Sstevel@tonic-gate 		cp--;
5457c478bd9Sstevel@tonic-gate 	*++cp = L'\0';
5467c478bd9Sstevel@tonic-gate 	/* Count the leading blank space and tabulate */
547e1ab4a07SJohn Sonnenschein 	for (cp = line; *cp == L' '; cp++) {
548e1ab4a07SJohn Sonnenschein 	}
5497c478bd9Sstevel@tonic-gate 	b = cp - line;
5507c478bd9Sstevel@tonic-gate 	t = b >> 3;
5517c478bd9Sstevel@tonic-gate 	b &= 07;
5527c478bd9Sstevel@tonic-gate 	if (t > 0)
5535e924907SJohn Sonnenschein 		do {
5547c478bd9Sstevel@tonic-gate 			putc('\t', stdout);
5555e924907SJohn Sonnenschein 		} while (--t);
5567c478bd9Sstevel@tonic-gate 	if (b > 0)
5575e924907SJohn Sonnenschein 		do {
5587c478bd9Sstevel@tonic-gate 			putc(' ', stdout);
5595e924907SJohn Sonnenschein 		} while (--b);
5607c478bd9Sstevel@tonic-gate 	while (*cp)
5617c478bd9Sstevel@tonic-gate 		putwc(*cp++, stdout);
5627c478bd9Sstevel@tonic-gate 	putc('\n', stdout);
5637c478bd9Sstevel@tonic-gate }
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate /*
5667c478bd9Sstevel@tonic-gate  * Initialize the output line with the appropriate number of
5677c478bd9Sstevel@tonic-gate  * leading blanks.
5687c478bd9Sstevel@tonic-gate  */
5697c478bd9Sstevel@tonic-gate 
570b7d62af5Sceastha static void
leadin(void)571b7d62af5Sceastha leadin(void)
5727c478bd9Sstevel@tonic-gate {
573b7d62af5Sceastha 	int b;
574b7d62af5Sceastha 	wchar_t *cp;
575b7d62af5Sceastha 	int l;
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	switch (crown_state) {
5787c478bd9Sstevel@tonic-gate 	case c_head:
5797c478bd9Sstevel@tonic-gate 		l = crown_head;
5807c478bd9Sstevel@tonic-gate 		crown_state = c_lead;
5817c478bd9Sstevel@tonic-gate 		break;
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	case c_lead:
5847c478bd9Sstevel@tonic-gate 	case c_fixup:
5857c478bd9Sstevel@tonic-gate 		l = crown_head;
5867c478bd9Sstevel@tonic-gate 		crown_state = c_fixup;
5877c478bd9Sstevel@tonic-gate 		break;
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	case c_body:
5907c478bd9Sstevel@tonic-gate 		l = crown_body;
5917c478bd9Sstevel@tonic-gate 		break;
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	default:
5947c478bd9Sstevel@tonic-gate 		l = pfx;
5957c478bd9Sstevel@tonic-gate 		break;
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 	filler = l;
5987c478bd9Sstevel@tonic-gate 	for (b = 0, cp = outbuf; b < l; b++)
5997c478bd9Sstevel@tonic-gate 		*cp++ = L' ';
6007c478bd9Sstevel@tonic-gate 	outp = cp;
6017c478bd9Sstevel@tonic-gate }
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate /*
6047c478bd9Sstevel@tonic-gate  * Is s1 a prefix of s2??
6057c478bd9Sstevel@tonic-gate  */
6067c478bd9Sstevel@tonic-gate 
607b7d62af5Sceastha static int
ispref(wchar_t * s1,wchar_t * s2)6087c478bd9Sstevel@tonic-gate ispref(wchar_t *s1, wchar_t *s2)
6097c478bd9Sstevel@tonic-gate {
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 	while (*s1 != L'\0' && *s2 != L'\0')
6127c478bd9Sstevel@tonic-gate 		if (*s1++ != *s2++)
6137c478bd9Sstevel@tonic-gate 			return (0);
6147c478bd9Sstevel@tonic-gate 	return (1);
6157c478bd9Sstevel@tonic-gate }
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate /*
6187c478bd9Sstevel@tonic-gate  * Set an input option
6197c478bd9Sstevel@tonic-gate  */
6207c478bd9Sstevel@tonic-gate 
621b7d62af5Sceastha static int
setopt(char * cp)622b7d62af5Sceastha setopt(char *cp)
6237c478bd9Sstevel@tonic-gate {
6247c478bd9Sstevel@tonic-gate 	static int ws = 0;
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	if (*cp == '-') {
6277c478bd9Sstevel@tonic-gate 		if (cp[1] == 'c' && cp[2] == '\0') {
6287c478bd9Sstevel@tonic-gate 			crown_state = c_reset;
6297c478bd9Sstevel@tonic-gate 			return (1);
6307c478bd9Sstevel@tonic-gate 		}
6317c478bd9Sstevel@tonic-gate 		if (cp[1] == 's' && cp[2] == '\0') {
6327c478bd9Sstevel@tonic-gate 			nojoin = 1;
6337c478bd9Sstevel@tonic-gate 			return (1);
6347c478bd9Sstevel@tonic-gate 		}
6357c478bd9Sstevel@tonic-gate 		if (cp[1] == 'w' && cp[2] == '\0') {
6367c478bd9Sstevel@tonic-gate 			ws++;
6377c478bd9Sstevel@tonic-gate 			return (1);
6387c478bd9Sstevel@tonic-gate 		}
6397c478bd9Sstevel@tonic-gate 		width = atoi(cp+1);
6407c478bd9Sstevel@tonic-gate 	} else if (ws) {
6417c478bd9Sstevel@tonic-gate 		width = atoi(cp);
6427c478bd9Sstevel@tonic-gate 		ws = 0;
6437c478bd9Sstevel@tonic-gate 	} else
6447c478bd9Sstevel@tonic-gate 		return (0);
6457c478bd9Sstevel@tonic-gate 	if (width <= 0 || width >= BUFSIZ-2) {
6467c478bd9Sstevel@tonic-gate 		fprintf(stderr, "fmt:  bad width: %d\n", width);
6477c478bd9Sstevel@tonic-gate 		exit(1);
6487c478bd9Sstevel@tonic-gate 	}
6497c478bd9Sstevel@tonic-gate 	return (1);
6507c478bd9Sstevel@tonic-gate }
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate #define	LIB_WDRESOLVE	"/usr/lib/locale/%s/LC_CTYPE/wdresolve.so"
6547c478bd9Sstevel@tonic-gate #define	WCHKIND		"_wdchkind_"
6557c478bd9Sstevel@tonic-gate 
656b7d62af5Sceastha static int	_wckind_c_locale(wchar_t);
6577c478bd9Sstevel@tonic-gate 
658b7d62af5Sceastha static int	(*__wckind)(wchar_t) = _wckind_c_locale;
6597c478bd9Sstevel@tonic-gate static void	*dlhandle = NULL;
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 
662b7d62af5Sceastha static void
_wckind_init(void)663b7d62af5Sceastha _wckind_init(void)
6647c478bd9Sstevel@tonic-gate {
6657c478bd9Sstevel@tonic-gate 	char	*locale;
6667c478bd9Sstevel@tonic-gate 	char	path[MAXPATHLEN + 1];
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	if (dlhandle != NULL) {
6707c478bd9Sstevel@tonic-gate 		(void) dlclose(dlhandle);
6717c478bd9Sstevel@tonic-gate 		dlhandle = NULL;
6727c478bd9Sstevel@tonic-gate 	}
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	locale = setlocale(LC_CTYPE, NULL);
6757c478bd9Sstevel@tonic-gate 	if (strcmp(locale, "C") == 0)
6767c478bd9Sstevel@tonic-gate 		goto c_locale;
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	(void) sprintf(path, LIB_WDRESOLVE, locale);
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	if ((dlhandle = dlopen(path, RTLD_LAZY)) != NULL) {
681b7d62af5Sceastha 		__wckind = (int (*)(wchar_t))dlsym(dlhandle, WCHKIND);
6827c478bd9Sstevel@tonic-gate 		if (__wckind != NULL)
6837c478bd9Sstevel@tonic-gate 			return;
6847c478bd9Sstevel@tonic-gate 		(void) dlclose(dlhandle);
6857c478bd9Sstevel@tonic-gate 		dlhandle = NULL;
6867c478bd9Sstevel@tonic-gate 	}
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate c_locale:
6897c478bd9Sstevel@tonic-gate 	__wckind = _wckind_c_locale;
6907c478bd9Sstevel@tonic-gate }
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate int
_wckind(wchar_t wc)694b7d62af5Sceastha _wckind(wchar_t wc)
6957c478bd9Sstevel@tonic-gate {
6967c478bd9Sstevel@tonic-gate 	return (*__wckind) (wc);
6977c478bd9Sstevel@tonic-gate }
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate static int
_wckind_c_locale(wchar_t wc)701b7d62af5Sceastha _wckind_c_locale(wchar_t wc)
7027c478bd9Sstevel@tonic-gate {
7037c478bd9Sstevel@tonic-gate 	int	ret;
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	/*
7067c478bd9Sstevel@tonic-gate 	 * DEPEND_ON_ANSIC: L notion for the character is new in
7077c478bd9Sstevel@tonic-gate 	 * ANSI-C, k&r compiler won't work.
7087c478bd9Sstevel@tonic-gate 	 */
7097c478bd9Sstevel@tonic-gate 	if (iswascii(wc))
7107c478bd9Sstevel@tonic-gate 		ret = (iswalnum(wc) || wc == L'_') ? 0 : 1;
7117c478bd9Sstevel@tonic-gate 	else
7127c478bd9Sstevel@tonic-gate 		ret = wcsetno(wc) + 1;
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	return (ret);
7157c478bd9Sstevel@tonic-gate }
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate /*
7187c478bd9Sstevel@tonic-gate  * header_chk -
7197c478bd9Sstevel@tonic-gate  * Called when done looking for a set mail header lines.
7207c478bd9Sstevel@tonic-gate  * Either a blank line was seen, or EOF was reached.
7217c478bd9Sstevel@tonic-gate  *
7227c478bd9Sstevel@tonic-gate  * Verifies if current hdrbuf of potential mail header lines
7237c478bd9Sstevel@tonic-gate  * is really a mail header.  A mail header must be at least 2
7247c478bd9Sstevel@tonic-gate  * lines and more than half of them must start with one of the
7257c478bd9Sstevel@tonic-gate  * known mail header strings in headnames.
7267c478bd9Sstevel@tonic-gate  *
7277c478bd9Sstevel@tonic-gate  * header_chk sets hdr_state to do_hdr if hdrbuf contained a valid
7287c478bd9Sstevel@tonic-gate  * mail header.  Otherwise, it sets hdr_state to flush_hdr.
7297c478bd9Sstevel@tonic-gate  *
7307c478bd9Sstevel@tonic-gate  * h_lines = hdrbuf index for next line to be saved;
7317c478bd9Sstevel@tonic-gate  *	     also indicates current # of lines in potential header
7327c478bd9Sstevel@tonic-gate  */
7337c478bd9Sstevel@tonic-gate static void
header_chk(void)7347c478bd9Sstevel@tonic-gate header_chk(void)
7357c478bd9Sstevel@tonic-gate {
7367c478bd9Sstevel@tonic-gate 	wchar_t  *cp; 		/* ptr to current char of line */
7377c478bd9Sstevel@tonic-gate 	wchar_t **hp; 		/* ptr to current char of a valid */
7387c478bd9Sstevel@tonic-gate 				/* mail header string */
7397c478bd9Sstevel@tonic-gate 	int	  l;		/* index */
7407c478bd9Sstevel@tonic-gate 				/*
7417c478bd9Sstevel@tonic-gate 				 * number of lines in hdrbuf that look
7427c478bd9Sstevel@tonic-gate 				 * like mail header lines (start with
7437c478bd9Sstevel@tonic-gate 				 * a known mail header prefix)
7447c478bd9Sstevel@tonic-gate 				 */
7457c478bd9Sstevel@tonic-gate 	int	 hdrcount = 0;
7467c478bd9Sstevel@tonic-gate 		/* header must have at least 2 lines (h_lines > 1) */
7477c478bd9Sstevel@tonic-gate 		if (h_lines < 2) {
7487c478bd9Sstevel@tonic-gate 			hdr_state = flush_hdr;
7497c478bd9Sstevel@tonic-gate 			return;
7507c478bd9Sstevel@tonic-gate 		}
7517c478bd9Sstevel@tonic-gate 		/*
7527c478bd9Sstevel@tonic-gate 		 * go through each line in hdrbuf and see how many
7537c478bd9Sstevel@tonic-gate 		 * look like mail header lines
7547c478bd9Sstevel@tonic-gate 		 */
7557c478bd9Sstevel@tonic-gate 		for (l = 0; l < h_lines; l++) {
7567c478bd9Sstevel@tonic-gate 			/* skip initial blanks */
757e1ab4a07SJohn Sonnenschein 			for (cp = hdrbuf[l]; *cp == L' '; cp++) {
758e1ab4a07SJohn Sonnenschein 			}
759b7d62af5Sceastha 			for (hp = &headnames[0]; *hp != (wchar_t *)0; hp++)
7607c478bd9Sstevel@tonic-gate 				if (ispref(*hp, cp)) {
7617c478bd9Sstevel@tonic-gate 					hdrcount++;
7627c478bd9Sstevel@tonic-gate 					break;
7637c478bd9Sstevel@tonic-gate 				}
7647c478bd9Sstevel@tonic-gate 		}
7657c478bd9Sstevel@tonic-gate 		/*
7667c478bd9Sstevel@tonic-gate 		 * if over half match, we'll assume this is a header;
7677c478bd9Sstevel@tonic-gate 		 * set hdr_state to indicate whether to treat
7687c478bd9Sstevel@tonic-gate 		 * these lines as mail header (do_hdr) or not (flush_hdr)
7697c478bd9Sstevel@tonic-gate 		 */
7707c478bd9Sstevel@tonic-gate 		if (hdrcount > h_lines / 2)
7717c478bd9Sstevel@tonic-gate 			hdr_state = do_hdr;
7727c478bd9Sstevel@tonic-gate 		else
7737c478bd9Sstevel@tonic-gate 			hdr_state = flush_hdr;
7747c478bd9Sstevel@tonic-gate }
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate /*
7777c478bd9Sstevel@tonic-gate  * fill_hdrbuf -
7787c478bd9Sstevel@tonic-gate  * Save given input line into next element of hdrbuf,
7797c478bd9Sstevel@tonic-gate  * as a potential mail header line, to be processed later
7807c478bd9Sstevel@tonic-gate  * once we decide whether or not the contents of hdrbuf is
7817c478bd9Sstevel@tonic-gate  * really a mail header, via header_chk().
7827c478bd9Sstevel@tonic-gate  *
7837c478bd9Sstevel@tonic-gate  * Does not allow hdrbuf to exceed MAXLINES lines.
7847c478bd9Sstevel@tonic-gate  * Dynamically allocates space for each line.  If we are unable
7857c478bd9Sstevel@tonic-gate  * to allocate space for the current string, stop special mail
7867c478bd9Sstevel@tonic-gate  * header preservation at this point and continue formatting
7877c478bd9Sstevel@tonic-gate  * without it.
7887c478bd9Sstevel@tonic-gate  */
7897c478bd9Sstevel@tonic-gate static void
fill_hdrbuf(wchar_t line[])7907c478bd9Sstevel@tonic-gate fill_hdrbuf(wchar_t line[])
7917c478bd9Sstevel@tonic-gate {
7927c478bd9Sstevel@tonic-gate 	wchar_t *cp;	/* pointer to characters in input line */
7937c478bd9Sstevel@tonic-gate 	int	 i;	/* index into characters a hdrbuf line */
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate 	if (h_lines >= MAXLINES) {
7967c478bd9Sstevel@tonic-gate 		/*
7977c478bd9Sstevel@tonic-gate 		 * if we run over MAXLINES potential mail header
7987c478bd9Sstevel@tonic-gate 		 * lines, stop checking--this is most likely NOT a
7997c478bd9Sstevel@tonic-gate 		 * mail header; flush out the hdrbuf, then process
8007c478bd9Sstevel@tonic-gate 		 * the current 'line' normally.
8017c478bd9Sstevel@tonic-gate 		 */
8027c478bd9Sstevel@tonic-gate 		hdr_state = flush_hdr;
8037c478bd9Sstevel@tonic-gate 		process_hdrbuf();
8047c478bd9Sstevel@tonic-gate 		prefix(line);
8057c478bd9Sstevel@tonic-gate 		return;
8067c478bd9Sstevel@tonic-gate 	}
8077c478bd9Sstevel@tonic-gate 	hdrbuf[h_lines] = (wchar_t *)malloc(sizeof (wchar_t) *
8087c478bd9Sstevel@tonic-gate 	    (wslen(line) + 1));
8097c478bd9Sstevel@tonic-gate 	if (hdrbuf[h_lines] == NULL) {
8107c478bd9Sstevel@tonic-gate 		perror("malloc");
8117c478bd9Sstevel@tonic-gate 		fprintf(stderr, "fmt: unable to do mail header preservation\n");
8127c478bd9Sstevel@tonic-gate 		errs++;
8137c478bd9Sstevel@tonic-gate 		/*
8147c478bd9Sstevel@tonic-gate 		 * Can't process mail header; flush current contents
8157c478bd9Sstevel@tonic-gate 		 * of mail header and continue with no more mail
8167c478bd9Sstevel@tonic-gate 		 * header processing
8177c478bd9Sstevel@tonic-gate 		 */
8187c478bd9Sstevel@tonic-gate 		if (h_lines == 0)
8197c478bd9Sstevel@tonic-gate 			/* hdrbuf is empty; process this line normally */
8207c478bd9Sstevel@tonic-gate 			prefix(line);
8217c478bd9Sstevel@tonic-gate 		else {
8227c478bd9Sstevel@tonic-gate 			hdr_state = flush_hdr;
8237c478bd9Sstevel@tonic-gate 			for (i = 0; i < h_lines; i++) {
8247c478bd9Sstevel@tonic-gate 				prefix(hdrbuf[i]);
8257c478bd9Sstevel@tonic-gate 				free(hdrbuf[i]);
8267c478bd9Sstevel@tonic-gate 			}
8277c478bd9Sstevel@tonic-gate 			h_lines = 0;
8287c478bd9Sstevel@tonic-gate 		}
8297c478bd9Sstevel@tonic-gate 		hdr_state = off;
8307c478bd9Sstevel@tonic-gate 		return;
8317c478bd9Sstevel@tonic-gate 	}
8327c478bd9Sstevel@tonic-gate 	/* save this line as a potential mail header line */
833e1ab4a07SJohn Sonnenschein 	for (i = 0, cp = line; (hdrbuf[h_lines][i] = *cp) != L'\0'; i++, cp++) {
834e1ab4a07SJohn Sonnenschein 	}
8357c478bd9Sstevel@tonic-gate 	h_lines++;
8367c478bd9Sstevel@tonic-gate }
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate /*
8397c478bd9Sstevel@tonic-gate  * process_hdrbuf -
8407c478bd9Sstevel@tonic-gate  * Outputs the lines currently stored in hdrbuf, according
8417c478bd9Sstevel@tonic-gate  * to the current hdr_state value, assumed to be either do_hdr
8427c478bd9Sstevel@tonic-gate  * or flush_hdr.
8437c478bd9Sstevel@tonic-gate  * This should be called after doing a header_chk() to verify
8447c478bd9Sstevel@tonic-gate  * the hdrbuf and set the hdr_state flag.
8457c478bd9Sstevel@tonic-gate  */
8467c478bd9Sstevel@tonic-gate static void
process_hdrbuf(void)8477c478bd9Sstevel@tonic-gate process_hdrbuf(void)
8487c478bd9Sstevel@tonic-gate {
8497c478bd9Sstevel@tonic-gate int i;
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	for (i = 0; i < h_lines; i++) {
8527c478bd9Sstevel@tonic-gate 		prefix(hdrbuf[i]);
8537c478bd9Sstevel@tonic-gate 		free(hdrbuf[i]);
8547c478bd9Sstevel@tonic-gate 	}
8557c478bd9Sstevel@tonic-gate 	hdr_state = not_in_hdr;
8567c478bd9Sstevel@tonic-gate 	h_lines = 0;
8577c478bd9Sstevel@tonic-gate }
858