fmt.c revision 196c7f05d2deba7404e90ad67f3861185c78ca2d
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <wctype.h>
#include <widec.h>
#include <dlfcn.h>
#include <locale.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
*/
int filler; /* Filler amount in outbuf */
int pfx; /* Current leading blank count */
int nojoin = 0; /* split lines only, don't join short ones */
int errs = 0; /* Current number of errors */
int crown_head; /* The header offset */
int crown_body; /* The body offset */
/* currently-known initial strings found in mail headers */
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 */
int h_lines; /* index into lines of hdrbuf */
extern boolean_t is_headline(const char *);
static void fill_hdrbuf(wchar_t []);
static void header_chk(void);
static void process_hdrbuf(void);
static void leadin(void);
static void oflush(void);
static void _wckind_init(void);
static int setopt(char *);
/*
* Drive the whole formatter by managing input files. Also,
* cause initialization of the output stuff and flush it out
* at the end.
*/
int
{
char *cp;
int nofile;
char *locale;
} else {
_wckind_init();
}
if (argc < 2) {
oflush();
exit(0);
}
nofile = 1;
while (--argc) {
continue;
nofile = 0;
errs++;
continue;
}
}
if (nofile)
goto single;
oflush();
return (errs);
}
/*
* 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.
*/
static void
{
int col;
wchar_t c;
while (c != EOF) {
/*
* Collect a line, doing ^H processing.
* Leave tabs for now.
*/
if (c == L'\b') {
cp--;
continue;
}
if (!(iswprint(c)) && c != L'\t') {
continue;
}
*cp++ = c;
}
*cp = L'\0';
/*
* Toss anything remaining on the input line.
*/
while (c != L'\n' && c != EOF)
/*
* Expand tabs on the way to canonb.
*/
col = 0;
while (c = *cp++) {
if (c != L'\t') {
*cp2++ = c;
continue;
}
do {
*cp2++ = L' ';
col++;
} while ((col & 07) != 0);
}
/*
* Swipe trailing blanks from the line.
*/
}
*++cp2 = '\0';
/* special processing to look for mail header lines */
switch (hdr_state) {
case off:
case not_in_hdr:
/* look for an initial mail header line */
/* skip initial blanks */
}
/*
* Need to convert string from wchar_t to char,
* since this is what is_headline() 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.
*/
} else {
/* no mail header line; process normally */
}
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();
/* now process the current blank line */
} else
/*
* not a blank line--save this line as
* a potential mail header line
*/
break;
}
if (c != EOF)
}
/*
* end of this file--make sure we process the stuff in
* hdrbuf before we're finished
*/
header_chk();
}
}
/*
* 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.
*/
static void
{
int np;
int nosplit = 0; /* flag set if line should not be split */
if (line[0] == L'\0') {
oflush();
putchar('\n');
if (crown_state != c_none)
return;
}
}
/*
* The following horrible expression attempts to avoid linebreaks
* when the indent changes due to a paragraph.
*/
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
*/
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 */
oflush();
switch (crown_state) {
case c_reset:
crown_head = pfx;
break;
case c_lead:
crown_body = pfx;
break;
case c_fixup:
crown_body = pfx;
if (outp) {
*outp = L'\0';
split(s);
}
break;
}
if (nosplit) {
/* put whole input line onto outbuf and print it out */
oflush();
} else
/*
* split puts current line onto outbuf, but splits it
* at word boundaries, if it exceeds desired length
*/
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.
*/
static void
{
while (*cp) {
/*
* Collect a 'word,' allowing it to contain escaped
* white space.
*/
}
/*
* Guarantee a space at end of line.
* Two spaces after end of sentence punctuation.
*/
if (*cp == L'\0') {
*cp2++ = L' ';
*cp2++ = L' ';
}
*cp2 = L'\0';
}
}
static void
{
while (*cp) {
/*
* Collect a 'word,' allowing it to contain escaped
* white space.
*/
while (*cp) {
break;
break;
}
/*
* Guarantee a space at end of line.
* Two spaces after end of sentence punctuation.
*/
if (*cp == L'\0') {
*cp2++ = L' ';
*cp2++ = L' ';
}
*cp2 = L'\0';
}
}
/*
* 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.
*/
static void
{
int s, t;
leadin();
*outp = L'\0';
if (t+s <= width) {
}
return;
}
if (s > filler) {
oflush();
leadin();
}
}
}
/*
* 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.
*/
static void
oflush(void)
{
return;
*outp = L'\0';
}
/*
* Take the passed line buffer, insert leading tabs where possible, and
* output on standard output (finally).
*/
static void
{
int b, t;
/* Toss trailing blanks in the output line */
cp--;
*++cp = L'\0';
/* Count the leading blank space and tabulate */
}
t = b >> 3;
b &= 07;
if (t > 0)
do {
} while (--t);
if (b > 0)
do {
} while (--b);
while (*cp)
}
/*
* Initialize the output line with the appropriate number of
* leading blanks.
*/
static void
leadin(void)
{
int b;
int l;
switch (crown_state) {
case c_head:
l = crown_head;
break;
case c_lead:
case c_fixup:
l = crown_head;
break;
case c_body:
l = crown_body;
break;
default:
l = pfx;
break;
}
filler = l;
*cp++ = L' ';
}
/*
* Is s1 a prefix of s2??
*/
static int
{
return (0);
return (1);
}
/*
* Set an input option
*/
static int
{
static int ws = 0;
if (*cp == '-') {
return (1);
}
nojoin = 1;
return (1);
}
ws++;
return (1);
}
} else if (ws) {
ws = 0;
} else
return (0);
exit(1);
}
return (1);
}
#define LIB_WDRESOLVE "/usr/lib/locale/%s/LC_CTYPE/wdresolve.so"
#define WCHKIND "_wdchkind_"
static int _wckind_c_locale(wchar_t);
static void
_wckind_init(void)
{
char *locale;
}
goto c_locale;
return;
}
}
int
{
}
static int
{
int ret;
/*
* DEPEND_ON_ANSIC: L notion for the character is new in
* ANSI-C, k&r compiler won't work.
*/
else
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)
{
/* 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) {
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 */
}
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)
*/
else
}
/*
* 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
{
int i; /* index into characters a hdrbuf line */
/*
* 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.
*/
return;
}
perror("malloc");
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 */
else {
for (i = 0; i < h_lines; i++) {
}
h_lines = 0;
}
return;
}
/* save this line as a potential mail header line */
}
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++) {
}
h_lines = 0;
}