summaryrefslogtreecommitdiff
path: root/usr/src/cmd/fmt
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/fmt
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/fmt')
-rw-r--r--usr/src/cmd/fmt/Makefile59
-rw-r--r--usr/src/cmd/fmt/fmt.c832
-rw-r--r--usr/src/cmd/fmt/head.c324
3 files changed, 1215 insertions, 0 deletions
diff --git a/usr/src/cmd/fmt/Makefile b/usr/src/cmd/fmt/Makefile
new file mode 100644
index 0000000000..b94afa3821
--- /dev/null
+++ b/usr/src/cmd/fmt/Makefile
@@ -0,0 +1,59 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/fmt/Makefile
+
+PROG= fmt
+
+OBJS= fmt.o
+HEAD= ../mailx/head.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../Makefile.cmd
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS) $(HEAD)
+ $(LINK.c) $(OBJS) $(HEAD) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+$(HEAD): FRC
+ cd $(@D); pwd; $(MAKE) $(@F)
+ @pwd
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../Makefile.targ
+
+FRC:
diff --git a/usr/src/cmd/fmt/fmt.c b/usr/src/cmd/fmt/fmt.c
new file mode 100644
index 0000000000..28e035a258
--- /dev/null
+++ b/usr/src/cmd/fmt/fmt.c
@@ -0,0 +1,832 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1997 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <wctype.h>
+#include <widec.h>
+#include <dlfcn.h>
+#include <locale.h>
+#include <sys/param.h>
+#include <string.h>
+
+/*
+ * fmt -- format the concatenation of input files or standard input
+ * onto standard output. Designed for use with Mail ~|
+ *
+ * Syntax: fmt [ -width | -w width ] [ -cs ] [ name ... ]
+ * Author: Kurt Shoens (UCB) 12/7/78
+ */
+
+#define NOSTR ((wchar_t *) 0) /* Null string pointer for lint */
+#define MAXLINES 100 /* maximum mail header lines to verify */
+
+wchar_t outbuf[BUFSIZ]; /* Sandbagged output line image */
+wchar_t *outp; /* Pointer in above */
+int filler; /* Filler amount in outbuf */
+
+int pfx; /* Current leading blank count */
+int width = 72; /* Width that we will not exceed */
+int nojoin = 0; /* split lines only, don't join short ones */
+int errs = 0; /* Current number of errors */
+
+enum crown_type {c_none, c_reset, c_head, c_lead, c_fixup, c_body};
+enum crown_type crown_state; /* Crown margin state */
+int crown_head; /* The header offset */
+int crown_body; /* The body offset */
+ /* currently-known initial strings found in mail headers */
+wchar_t *headnames[] = {
+ L"Apparently-To", L"Bcc", L"bcc", L"Cc", L"cc", L"Confirmed-By",
+ L"Content", L"content-length", L"From", L"Date", L"id",
+ L"Message-I", L"MIME-Version", L"Precedence", L"Return-Path",
+ L"Received", L"Reply-To", L"Status", L"Subject", L"To", L"X-IMAP",
+ L"X-Lines", L"X-Sender", L"X-Sun", L"X-Status", L"X-UID",
+ 0};
+
+enum hdr_type {
+ off, /* mail header processing is off */
+ not_in_hdr, /* not currently processing a mail header */
+ in_hdr, /* currently filling hdrbuf with potential hdr lines */
+ flush_hdr, /* flush hdrbuf; not a header, no special processing */
+ do_hdr /* process hdrbuf as a mail header */
+};
+ /* current state of hdrbuf */
+enum hdr_type hdr_state = not_in_hdr;
+
+wchar_t *hdrbuf[MAXLINES]; /* buffer to hold potential mail header lines */
+int h_lines; /* index into lines of hdrbuf */
+
+int (*(split))();
+extern int scrwidth(wchar_t);
+
+static void fill_hdrbuf(wchar_t line[]);
+static void header_chk(void);
+static void process_hdrbuf(void);
+
+/*
+ * Drive the whole formatter by managing input files. Also,
+ * cause initialization of the output stuff and flush it out
+ * at the end.
+ */
+
+main(int argc, char **argv)
+{
+ register FILE *fi;
+ char sobuf[BUFSIZ];
+ register char *cp;
+ int nofile;
+ char *locale;
+ int csplit(), msplit();
+ void _wckind_init();
+
+ outp = NOSTR;
+ setbuf(stdout, sobuf);
+ setlocale(LC_ALL, "");
+ locale = setlocale(LC_CTYPE, "");
+ if (strcmp(locale, "C") == 0) {
+ split = csplit;
+ } else {
+ split = msplit;
+ (void) _wckind_init();
+ }
+ if (argc < 2) {
+single:
+ fmt(stdin);
+ oflush();
+ exit(0);
+ }
+ nofile = 1;
+ while (--argc) {
+ cp = *++argv;
+ if (setopt(cp))
+ continue;
+ nofile = 0;
+ if ((fi = fopen(cp, "r")) == NULL) {
+ perror(cp);
+ errs++;
+ continue;
+ }
+ fmt(fi);
+ fclose(fi);
+ }
+ if (nofile)
+ goto single;
+ oflush();
+ exit(errs);
+ /* NOTREACHED */
+}
+
+/*
+ * Read up characters from the passed input file, forming lines,
+ * doing ^H processing, expanding tabs, stripping trailing blanks,
+ * and sending each line down for analysis.
+ */
+
+fmt(FILE *fi)
+{
+ wchar_t linebuf[BUFSIZ], canonb[BUFSIZ];
+ register wchar_t *cp, *cp2;
+ register int col;
+ wchar_t c;
+ char cbuf[BUFSIZ]; /* stores wchar_t string as char string */
+
+ c = getwc(fi);
+ while (c != EOF) {
+ /*
+ * Collect a line, doing ^H processing.
+ * Leave tabs for now.
+ */
+
+ cp = linebuf;
+ while (c != L'\n' && c != EOF && cp-linebuf < BUFSIZ-1) {
+ if (c == L'\b') {
+ if (cp > linebuf)
+ cp--;
+ c = getwc(fi);
+ continue;
+ }
+ if (!(iswprint(c)) && c != L'\t') {
+ c = getwc(fi);
+ continue;
+ }
+ *cp++ = c;
+ c = getwc(fi);
+ }
+ *cp = L'\0';
+
+ /*
+ * Toss anything remaining on the input line.
+ */
+
+ while (c != L'\n' && c != EOF)
+ c = getwc(fi);
+ /*
+ * Expand tabs on the way to canonb.
+ */
+
+ col = 0;
+ cp = linebuf;
+ cp2 = canonb;
+ while (c = *cp++) {
+ if (c != L'\t') {
+ col += scrwidth(c);
+ if (cp2-canonb < BUFSIZ-1)
+ *cp2++ = c;
+ continue;
+ }
+ do {
+ if (cp2-canonb < BUFSIZ-1)
+ *cp2++ = L' ';
+ col++;
+ } while ((col & 07) != 0);
+ }
+
+ /*
+ * Swipe trailing blanks from the line.
+ */
+
+ for (cp2--; cp2 >= canonb && *cp2 == L' '; cp2--);
+ *++cp2 = '\0';
+
+ /* special processing to look for mail header lines */
+ switch (hdr_state) {
+ case off:
+ prefix(canonb);
+ case not_in_hdr:
+ /* look for an initial mail header line */
+ /* skip initial blanks */
+ for (cp = canonb; *cp == L' '; cp++);
+ /*
+ * Need to convert string from wchar_t to char,
+ * since this is what ishead() expects. Since we
+ * only want to make sure cp points to a "From" line
+ * of the email, we don't have to alloc
+ * BUFSIZ * MB_LEN_MAX to cbuf.
+ */
+ wcstombs(cbuf, cp, (BUFSIZ - 1));
+ if (ishead(cbuf)) {
+ hdr_state = in_hdr;
+ fill_hdrbuf(canonb);
+ } else {
+ /* no mail header line; process normally */
+ prefix(canonb);
+ }
+ break;
+ case in_hdr:
+ /* already saw 1st mail header line; look for more */
+ if (canonb[0] == L'\0') {
+ /*
+ * blank line means end of mail header;
+ * verify current mail header buffer
+ * then process it accordingly
+ */
+ header_chk();
+ process_hdrbuf();
+ /* now process the current blank line */
+ prefix(canonb);
+ } else
+ /*
+ * not a blank line--save this line as
+ * a potential mail header line
+ */
+ fill_hdrbuf(canonb);
+ break;
+ }
+ if (c != EOF)
+ c = getwc(fi);
+ }
+ /*
+ * end of this file--make sure we process the stuff in
+ * hdrbuf before we're finished
+ */
+ if (hdr_state == in_hdr) {
+ header_chk();
+ process_hdrbuf();
+ }
+}
+
+/*
+ * Take a line devoid of tabs and other garbage and determine its
+ * blank prefix. If the indent changes, call for a linebreak.
+ * If the input line is blank, echo the blank line on the output.
+ * Finally, if the line minus the prefix is a mail header, try to keep
+ * it on a line by itself.
+ */
+
+prefix(wchar_t line[])
+{
+ register wchar_t *cp;
+ register int np;
+ register int i;
+ int nosplit = 0; /* flag set if line should not be split */
+
+ if (line[0] == L'\0') {
+ oflush();
+ putchar('\n');
+ if (crown_state != c_none)
+ crown_state = c_reset;
+ return;
+ }
+ for (cp = line; *cp == L' '; cp++);
+ np = cp - line;
+
+ /*
+ * The following horrible expression attempts to avoid linebreaks
+ * when the indent changes due to a paragraph.
+ */
+
+ if (crown_state == c_none && np != pfx && (np > pfx || abs(pfx-np) > 8))
+ oflush();
+ /*
+ * if this is a mail header line, don't split it; flush previous
+ * line, if any, so we don't join this line to it
+ */
+ if (hdr_state == do_hdr) {
+ nosplit = 1;
+ oflush();
+ }
+ /* flush previous line so we don't join this one to it */
+ if (nojoin)
+ oflush();
+ /* nroff-type lines starting with '.' are not split nor joined */
+ if (!nosplit && (nosplit = (*cp == L'.')))
+ oflush();
+ pfx = np;
+ switch (crown_state) {
+ case c_reset:
+ crown_head = pfx;
+ crown_state = c_head;
+ break;
+ case c_lead:
+ crown_body = pfx;
+ crown_state = c_body;
+ break;
+ case c_fixup:
+ crown_body = pfx;
+ crown_state = c_body;
+ if (outp) {
+ wchar_t s[BUFSIZ];
+
+ *outp = L'\0';
+ wscpy(s, &outbuf[crown_head]);
+ outp = NOSTR;
+ split(s);
+ }
+ break;
+ }
+ if (nosplit) {
+ /* put whole input line onto outbuf and print it out */
+ pack(cp);
+ oflush();
+ } else
+ /*
+ * split puts current line onto outbuf, but splits it
+ * at word boundaries, if it exceeds desired length
+ */
+ split(cp);
+ if (nojoin)
+ /*
+ * flush current line so next lines, if any,
+ * won't join to this one
+ */
+ oflush();
+}
+
+/*
+ * Split up the passed line into output "words" which are
+ * maximal strings of non-blanks with the blank separation
+ * attached at the end. Pass these words along to the output
+ * line packer.
+ */
+
+csplit(wchar_t line[])
+{
+ register wchar_t *cp, *cp2;
+ wchar_t word[BUFSIZ];
+ static const wchar_t *srchlist = (const wchar_t *) L".:!?";
+
+ cp = line;
+ while (*cp) {
+ cp2 = word;
+
+ /*
+ * Collect a 'word,' allowing it to contain escaped
+ * white space.
+ */
+
+ while (*cp && !(iswspace(*cp))) {
+ if (*cp == '\\' && iswspace(cp[1]))
+ *cp2++ = *cp++;
+ *cp2++ = *cp++;
+ }
+
+ /*
+ * Guarantee a space at end of line.
+ * Two spaces after end of sentence punctuation.
+ */
+
+ if (*cp == L'\0') {
+ *cp2++ = L' ';
+ if (wschr(srchlist, cp[-1]) != NULL)
+ *cp2++ = L' ';
+ }
+ while (iswspace(*cp))
+ *cp2++ = *cp++;
+ *cp2 = L'\0';
+ pack(word);
+ }
+}
+
+msplit(wchar_t line[])
+{
+ register wchar_t *cp, *cp2, prev;
+ wchar_t word[BUFSIZ];
+ static const wchar_t *srchlist = (const wchar_t *) L".:!?";
+
+ cp = line;
+ while (*cp) {
+ cp2 = word;
+ prev = *cp;
+
+ /*
+ * Collect a 'word,' allowing it to contain escaped
+ * white space.
+ */
+
+ while (*cp) {
+ if (iswspace(*cp))
+ break;
+ if (_wckind(*cp) != _wckind(prev))
+ if (wcsetno(*cp) != 0 || wcsetno(prev) != 0)
+ break;
+ if (*cp == '\\' && iswspace(cp[1]))
+ *cp2++ = *cp++;
+ prev = *cp;
+ *cp2++ = *cp++;
+ }
+
+ /*
+ * Guarantee a space at end of line.
+ * Two spaces after end of sentence punctuation.
+ */
+
+ if (*cp == L'\0') {
+ *cp2++ = L' ';
+ if (wschr(srchlist, cp[-1]) != NULL)
+ *cp2++ = L' ';
+ }
+ while (iswspace(*cp))
+ *cp2++ = *cp++;
+ *cp2 = L'\0';
+ pack(word);
+ }
+}
+
+/*
+ * Output section.
+ * Build up line images from the words passed in. Prefix
+ * each line with correct number of blanks. The buffer "outbuf"
+ * contains the current partial line image, including prefixed blanks.
+ * "outp" points to the next available space therein. When outp is NOSTR,
+ * there ain't nothing in there yet. At the bottom of this whole mess,
+ * leading tabs are reinserted.
+ */
+
+/*
+ * Pack a word onto the output line. If this is the beginning of
+ * the line, push on the appropriately-sized string of blanks first.
+ * If the word won't fit on the current line, flush and begin a new
+ * line. If the word is too long to fit all by itself on a line,
+ * just give it its own and hope for the best.
+ */
+
+pack(wchar_t word[])
+{
+ register wchar_t *cp;
+ register int s, t;
+
+ if (outp == NOSTR)
+ leadin();
+ t = wscol(word);
+ *outp = L'\0';
+ s = wscol(outbuf);
+ if (t+s <= width) {
+ for (cp = word; *cp; *outp++ = *cp++);
+ return;
+ }
+ if (s > filler) {
+ oflush();
+ leadin();
+ }
+ for (cp = word; *cp; *outp++ = *cp++);
+}
+
+/*
+ * If there is anything on the current output line, send it on
+ * its way. Set outp to NOSTR to indicate the absence of the current
+ * line prefix.
+ */
+
+oflush(void)
+{
+ if (outp == NOSTR)
+ return;
+ *outp = L'\0';
+ tabulate(outbuf);
+ outp = NOSTR;
+}
+
+/*
+ * Take the passed line buffer, insert leading tabs where possible, and
+ * output on standard output (finally).
+ */
+
+tabulate(wchar_t line[])
+{
+ register wchar_t *cp, *cp2;
+ register int b, t;
+
+
+ /* Toss trailing blanks in the output line */
+ cp = line + wslen(line) - 1;
+ while (cp >= line && *cp == L' ')
+ cp--;
+ *++cp = L'\0';
+ /* Count the leading blank space and tabulate */
+ for (cp = line; *cp == L' '; cp++);
+ b = cp - line;
+ t = b >> 3;
+ b &= 07;
+ if (t > 0)
+ do
+ putc('\t', stdout);
+ while (--t);
+ if (b > 0)
+ do
+ putc(' ', stdout);
+ while (--b);
+ while (*cp)
+ putwc(*cp++, stdout);
+ putc('\n', stdout);
+}
+
+/*
+ * Initialize the output line with the appropriate number of
+ * leading blanks.
+ */
+
+leadin()
+{
+ register int b;
+ register wchar_t *cp;
+ register int l;
+
+ switch (crown_state) {
+ case c_head:
+ l = crown_head;
+ crown_state = c_lead;
+ break;
+
+ case c_lead:
+ case c_fixup:
+ l = crown_head;
+ crown_state = c_fixup;
+ break;
+
+ case c_body:
+ l = crown_body;
+ break;
+
+ default:
+ l = pfx;
+ break;
+ }
+ filler = l;
+ for (b = 0, cp = outbuf; b < l; b++)
+ *cp++ = L' ';
+ outp = cp;
+}
+
+/*
+ * Is s1 a prefix of s2??
+ */
+
+ispref(wchar_t *s1, wchar_t *s2)
+{
+
+ while (*s1 != L'\0' && *s2 != L'\0')
+ if (*s1++ != *s2++)
+ return (0);
+ return (1);
+}
+
+/*
+ * Set an input option
+ */
+
+setopt(cp)
+ register char *cp;
+{
+ static int ws = 0;
+
+ if (*cp == '-') {
+ if (cp[1] == 'c' && cp[2] == '\0') {
+ crown_state = c_reset;
+ return (1);
+ }
+ if (cp[1] == 's' && cp[2] == '\0') {
+ nojoin = 1;
+ return (1);
+ }
+ if (cp[1] == 'w' && cp[2] == '\0') {
+ ws++;
+ return (1);
+ }
+ width = atoi(cp+1);
+ } else if (ws) {
+ width = atoi(cp);
+ ws = 0;
+ } else
+ return (0);
+ if (width <= 0 || width >= BUFSIZ-2) {
+ fprintf(stderr, "fmt: bad width: %d\n", width);
+ exit(1);
+ }
+ return (1);
+}
+
+
+#define LIB_WDRESOLVE "/usr/lib/locale/%s/LC_CTYPE/wdresolve.so"
+#define WCHKIND "_wdchkind_"
+
+static int _wckind_c_locale();
+
+static int (*__wckind)() = _wckind_c_locale;
+static void *dlhandle = NULL;
+
+
+void
+_wckind_init()
+{
+ char *locale;
+ char path[MAXPATHLEN + 1];
+
+
+ if (dlhandle != NULL) {
+ (void) dlclose(dlhandle);
+ dlhandle = NULL;
+ }
+
+ locale = setlocale(LC_CTYPE, NULL);
+ if (strcmp(locale, "C") == 0)
+ goto c_locale;
+
+ (void) sprintf(path, LIB_WDRESOLVE, locale);
+
+ if ((dlhandle = dlopen(path, RTLD_LAZY)) != NULL) {
+ __wckind = (int (*)(int))dlsym(dlhandle, WCHKIND);
+ if (__wckind != NULL)
+ return;
+ (void) dlclose(dlhandle);
+ dlhandle = NULL;
+ }
+
+c_locale:
+ __wckind = _wckind_c_locale;
+}
+
+
+int
+_wckind(wc)
+wchar_t wc;
+{
+ return (*__wckind) (wc);
+}
+
+
+static int
+_wckind_c_locale(wc)
+wchar_t wc;
+{
+ int ret;
+
+ /*
+ * DEPEND_ON_ANSIC: L notion for the character is new in
+ * ANSI-C, k&r compiler won't work.
+ */
+ if (iswascii(wc))
+ ret = (iswalnum(wc) || wc == L'_') ? 0 : 1;
+ else
+ ret = wcsetno(wc) + 1;
+
+ return (ret);
+}
+
+/*
+ * header_chk -
+ * Called when done looking for a set mail header lines.
+ * Either a blank line was seen, or EOF was reached.
+ *
+ * Verifies if current hdrbuf of potential mail header lines
+ * is really a mail header. A mail header must be at least 2
+ * lines and more than half of them must start with one of the
+ * known mail header strings in headnames.
+ *
+ * header_chk sets hdr_state to do_hdr if hdrbuf contained a valid
+ * mail header. Otherwise, it sets hdr_state to flush_hdr.
+ *
+ * h_lines = hdrbuf index for next line to be saved;
+ * also indicates current # of lines in potential header
+ */
+static void
+header_chk(void)
+{
+ wchar_t *cp; /* ptr to current char of line */
+ wchar_t **hp; /* ptr to current char of a valid */
+ /* mail header string */
+ int l; /* index */
+ /*
+ * number of lines in hdrbuf that look
+ * like mail header lines (start with
+ * a known mail header prefix)
+ */
+ int hdrcount = 0;
+ /* header must have at least 2 lines (h_lines > 1) */
+ if (h_lines < 2) {
+ hdr_state = flush_hdr;
+ return;
+ }
+ /*
+ * go through each line in hdrbuf and see how many
+ * look like mail header lines
+ */
+ for (l = 0; l < h_lines; l++) {
+ /* skip initial blanks */
+ for (cp = hdrbuf[l]; *cp == L' '; cp++);
+ for (hp = &headnames[0]; *hp != (wchar_t *) 0; hp++)
+ if (ispref(*hp, cp)) {
+ hdrcount++;
+ break;
+ }
+ }
+ /*
+ * if over half match, we'll assume this is a header;
+ * set hdr_state to indicate whether to treat
+ * these lines as mail header (do_hdr) or not (flush_hdr)
+ */
+ if (hdrcount > h_lines / 2)
+ hdr_state = do_hdr;
+ else
+ hdr_state = flush_hdr;
+}
+
+/*
+ * fill_hdrbuf -
+ * Save given input line into next element of hdrbuf,
+ * as a potential mail header line, to be processed later
+ * once we decide whether or not the contents of hdrbuf is
+ * really a mail header, via header_chk().
+ *
+ * Does not allow hdrbuf to exceed MAXLINES lines.
+ * Dynamically allocates space for each line. If we are unable
+ * to allocate space for the current string, stop special mail
+ * header preservation at this point and continue formatting
+ * without it.
+ */
+static void
+fill_hdrbuf(wchar_t line[])
+{
+ wchar_t *cp; /* pointer to characters in input line */
+ int i; /* index into characters a hdrbuf line */
+
+ if (h_lines >= MAXLINES) {
+ /*
+ * if we run over MAXLINES potential mail header
+ * lines, stop checking--this is most likely NOT a
+ * mail header; flush out the hdrbuf, then process
+ * the current 'line' normally.
+ */
+ hdr_state = flush_hdr;
+ process_hdrbuf();
+ prefix(line);
+ return;
+ }
+ hdrbuf[h_lines] = (wchar_t *)malloc(sizeof (wchar_t) *
+ (wslen(line) + 1));
+ if (hdrbuf[h_lines] == NULL) {
+ perror("malloc");
+ fprintf(stderr, "fmt: unable to do mail header preservation\n");
+ errs++;
+ /*
+ * Can't process mail header; flush current contents
+ * of mail header and continue with no more mail
+ * header processing
+ */
+ if (h_lines == 0)
+ /* hdrbuf is empty; process this line normally */
+ prefix(line);
+ else {
+ hdr_state = flush_hdr;
+ for (i = 0; i < h_lines; i++) {
+ prefix(hdrbuf[i]);
+ free(hdrbuf[i]);
+ }
+ h_lines = 0;
+ }
+ hdr_state = off;
+ return;
+ }
+ /* save this line as a potential mail header line */
+ for (i = 0, cp = line; (hdrbuf[h_lines][i] = *cp) != L'\0'; i++, cp++);
+ h_lines++;
+}
+
+/*
+ * process_hdrbuf -
+ * Outputs the lines currently stored in hdrbuf, according
+ * to the current hdr_state value, assumed to be either do_hdr
+ * or flush_hdr.
+ * This should be called after doing a header_chk() to verify
+ * the hdrbuf and set the hdr_state flag.
+ */
+static void
+process_hdrbuf(void)
+{
+int i;
+
+ for (i = 0; i < h_lines; i++) {
+ prefix(hdrbuf[i]);
+ free(hdrbuf[i]);
+ }
+ hdr_state = not_in_hdr;
+ h_lines = 0;
+}
diff --git a/usr/src/cmd/fmt/head.c b/usr/src/cmd/fmt/head.c
new file mode 100644
index 0000000000..12f44beac0
--- /dev/null
+++ b/usr/src/cmd/fmt/head.c
@@ -0,0 +1,324 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1988 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "rcv.h"
+
+/*
+ * mailx -- a modified version of a University of California at Berkeley
+ * mail program
+ *
+ * Routines for processing and detecting headlines.
+ */
+
+/*
+ * See if the passed line buffer is a mail header.
+ * Return true if yes. Note the extreme pains to
+ * accomodate all funny formats.
+ */
+
+extern int debug;
+
+ishead(linebuf)
+ char linebuf[];
+{
+ register char *cp;
+ struct headline hl;
+ char parbuf[BUFSIZ];
+
+ cp = linebuf;
+ if (strncmp("From ", cp, 5) != 0)
+ return(0);
+ parse(cp, &hl, parbuf);
+ if (hl.l_from == NOSTR || hl.l_date == NOSTR) {
+ fail(linebuf, "No from or date field");
+ return(0);
+ }
+#ifdef notdef
+ /*
+ * Seems to be no reason to be so fussy.
+ */
+ if (!Tflag && !isdate(hl.l_date)) {
+ fail(linebuf, "Date field not legal date");
+ return(0);
+ }
+#endif
+
+ /*
+ * I guess we got it!
+ */
+
+ return(1);
+}
+
+fail(linebuf, reason)
+ char linebuf[], reason[];
+{
+
+ if (debug)
+ (void)fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
+}
+
+/*
+ * Split a headline into its useful components.
+ * Copy the line into dynamic string space, then set
+ * pointers into the copied line in the passed headline
+ * structure. Actually, it scans.
+ */
+
+parse(line, hl, pbuf)
+ char line[], pbuf[];
+ struct headline *hl;
+{
+ register char *cp, *dp;
+ char *sp;
+ char word[LINESIZE];
+
+ hl->l_from = NOSTR;
+ hl->l_tty = NOSTR;
+ hl->l_date = NOSTR;
+ cp = line;
+ sp = pbuf;
+
+ /*
+ * Skip the first "word" of the line, which should be "From"
+ * anyway.
+ */
+
+ cp = nextword(cp, word);
+ dp = nextword(cp, word);
+ if (dp == NOSTR)
+ return;
+ if (!equal(word, ""))
+ hl->l_from = copyin(word, &sp);
+ if (strncmp(dp, "tty", 3) == 0) {
+ cp = nextword(dp, word);
+ hl->l_tty = copyin(word, &sp);
+ if (cp != NOSTR)
+ hl->l_date = copyin(cp, &sp);
+ }
+ else
+ if (dp != NOSTR)
+ hl->l_date = copyin(dp, &sp);
+}
+
+/*
+ * Copy the string on the left into the string on the right
+ * and bump the right (reference) string pointer by the length.
+ * Thus, dynamically allocate space in the right string, copying
+ * the left string into it.
+ */
+
+char *
+copyin(src, space)
+ char src[];
+ char **space;
+{
+ register char *cp, *top;
+ register int s;
+
+ s = strlen(src);
+ cp = *space;
+ top = cp;
+ (void)strcpy(cp, src);
+ cp += s + 1;
+ *space = cp;
+ return(top);
+}
+
+#ifdef notdef
+/*
+ * Test to see if the passed string is a ctime(3) generated
+ * date string as documented in the manual. The template
+ * below is used as the criterion of correctness.
+ * Also, we check for a possible trailing time zone using
+ * the auxtype template.
+ */
+
+#define L 1 /* A lower case char */
+#define S 2 /* A space */
+#define D 3 /* A digit */
+#define O 4 /* An optional digit or space */
+#define C 5 /* A colon */
+#define N 6 /* A new line */
+#define U 7 /* An upper case char */
+
+char ctypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,D,D,D,D,0};
+char tmztypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,U,U,U,S,D,D,D,D,0};
+
+isdate(date)
+ char date[];
+{
+ register char *cp;
+
+ cp = date;
+ if (cmatch(cp, ctypes))
+ return(1);
+ return(cmatch(cp, tmztypes));
+}
+
+/*
+ * Match the given string against the given template.
+ * Return 1 if they match, 0 if they don't
+ */
+
+cmatch(str, temp)
+ char str[], temp[];
+{
+ register char *cp, *tp;
+ register int c;
+
+ cp = str;
+ tp = temp;
+ while (*cp != '\0' && *tp != 0) {
+ c = *cp++;
+ switch (*tp++) {
+ case L:
+ if (c < 'a' || c > 'z')
+ return(0);
+ break;
+
+ case U:
+ if (c < 'A' || c > 'Z')
+ return(0);
+ break;
+
+ case S:
+ if (c != ' ')
+ return(0);
+ break;
+
+ case D:
+ if (!isdigit(c))
+ return(0);
+ break;
+
+ case O:
+ if (c != ' ' && !isdigit(c))
+ return(0);
+ break;
+
+ case C:
+ if (c != ':')
+ return(0);
+ break;
+
+ case N:
+ if (c != '\n')
+ return(0);
+ break;
+ }
+ }
+ if (*cp != '\0' || *tp != 0)
+ return(0);
+ return(1);
+}
+#endif
+
+/*
+ * Collect a liberal (space, tab delimited) word into the word buffer
+ * passed. Also, return a pointer to the next word following that,
+ * or NOSTR if none follow.
+ */
+
+char *
+nextword(wp, wbuf)
+ char wp[], wbuf[];
+{
+ register char *cp, *cp2;
+
+ if ((cp = wp) == NOSTR) {
+ (void)copy("", wbuf);
+ return(NOSTR);
+ }
+ cp2 = wbuf;
+ while (!any(*cp, " \t") && *cp != '\0')
+ if (*cp == '"') {
+ *cp2++ = *cp++;
+ while (*cp != '\0' && *cp != '"')
+ *cp2++ = *cp++;
+ if (*cp == '"')
+ *cp2++ = *cp++;
+ } else
+ *cp2++ = *cp++;
+ *cp2 = '\0';
+ while (any(*cp, " \t"))
+ cp++;
+ if (*cp == '\0')
+ return(NOSTR);
+ return(cp);
+}
+
+/*
+ * Copy str1 to str2, return pointer to null in str2.
+ */
+
+char *
+copy(str1, str2)
+ char *str1, *str2;
+{
+ register char *s1, *s2;
+
+ s1 = str1;
+ s2 = str2;
+ while (*s1)
+ *s2++ = *s1++;
+ *s2 = 0;
+ return(s2);
+}
+
+/*
+ * Is ch any of the characters in str?
+ */
+
+any(ch, str)
+ char *str;
+{
+ register char *f;
+ register c;
+
+ f = str;
+ c = ch;
+ while (*f)
+ if (c == *f++)
+ return(1);
+ return(0);
+}
+