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