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