[mod.sources] Fmtr - simple text formatter

sources-request@panda.UUCP (05/15/86)

Mod.sources:  Volume 4, Issue 120
Submitted by: harvard!hscfvax!popvax!mohamed (Mohamed ellozy)


Fmtr is a simple text formatter which evens out the lines of
files prepared for submission to the Unix formatters nroff,
troff, or ditroff.

     It is for users who find editing neatly formatted files
pleasanter than working with files with irregular lines.  It
knows that lines starting with a period or with an apostrophe
should be output as is, and that certain requests and macros
start no-fill mode, while others return to fill mode.  So, unlike
fmt, it will process the following example properly:

     .TS
     n n.
     1       2
     3       4
     .TE

Fmt run on that example would produce:

     .TS
     n n.  1       2 3       4
     .TE

destroying the table.

Mohamed el Lozy				mohamed@hscfvax.uucp
Health Sciences Computing Facility	...!harvard!hscfvax!mohamed
Harvard School of Public Health
665 Huntington Avenue
Boston, MA 02115

------------------------------  CUT HERE  ------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	Makefile
#	command.c
#	efopen.3s
#	efopen.c
#	fgetsmod.3s
#	fgetsmod.c
#	fmtr.1
#	fmtr.c
#	fmtr.h
#	getopt.3
#	getopt.c
#	lowtext.c
# This archive created: Thu May 15 16:20:30 1986
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1089 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
The shell archive contains the files needed to make fmtr, a formatter
for {n,t,dit}roff input files.  Four of the source files:

	fmtr.h
	fmtr.c
	command.c
	lowtext.c

contain the program proper.  In addition it uses three functions that
I have put in my library:

	getopt()	a public domain version of the System V utility
			apparently due to Henry Spencer taht I have
			modified minimally
	efopen()	a modified version of K & P's function
	fgetsmod()	a modified version of fgets() that truncates
			lines longer than the array they are being read
			into.

Manual pages for fmtr and for the three functions named above are
included.  The Makefile does not have any provision for adding these
functions to any library.  I have added them to /lib/libc.a, you may
want to put them elsewhere.  I have also not put in any provision for
installing the manual pages associated with them.

Comments, bug reports etc to:

Mohamed el Lozy				mohamed@hscfvax.uucp
Health Sciences Computing Facility	...!harvard!hscfvax!mohamed
Harvard School of Public Health
665 Huntington Avenue
Boston, MA 02115
SHAR_EOF
if test 1089 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1089 characters)'
fi
chmod +x 'README'
fi
echo shar: extracting "'Makefile'" '(789 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
CFLAGS = -O

# comment out next line if you have getopt() in your library
GETOPT = getopt.o

# comment out next line if you have efopen() in your library
EFOPEN = efopen.o

# comment out next line if you have fgetsmod() in your library
FGETSMOD = fgetsmod.o

SRC = fmtr.c command.c lowtext.c efopen.c getopt.c fgetsmod.c
OBJ = fmtr.o command.o lowtext.o $(EFOPEN) $(GETOPT) $(FGETSMOD)

# You may wish to modify either or both of the following:
BINDIR = /usr/local
MANDIR = /usr/man/man1

fmtr:	$(OBJ)
	cc $(CFLAGS) -s -o fmtr $(OBJ)

fmtr.o command.o lowtext.o:	fmtr.h

install:	fmtr fmtr.1h
	cp -c fmtr $(BINDIR)
	cp -c fmtr.1h $(MANDIR)

shar:
	shar -a README Makefile $(SRC) fmtr.h fmtr.1 efopen.3s \
	fgetsmod.3s getopt.3 > fmtr.shar

clean:
	/bin/rm -f *.o a.out core fmtr fmtr.shar
SHAR_EOF
if test 789 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 789 characters)'
fi
chmod +x 'Makefile'
fi
echo shar: extracting "'command.c'" '(4522 characters)'
if test -f 'command.c'
then
	echo shar: will not over-write existing file "'command.c'"
else
cat << \SHAR_EOF > 'command.c'
#ifndef lint
static char rcsid[] = "$Header: command.c,v 1.4 86/05/05 14:08:32 root Exp $";
#endif

/* command.c decodes command.  Uses binary search in array
 * of struct cmd.
 */

#include "fmtr.h"

#define HUGE 1000
#define PLUS '+'
#define MINUS '-'

#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define MIN(x,y) ((x) < (y) ? (x) : (y))

/*  cmdtype is upper case version of command.  All commands of
 *  interest to us are either ce, ul, or else equivalent to
 *  fi or nf
 */

enum cmdtype
    {
    CE, UL, FI, NF, OTHER
    } cmd, getcmd();

int len;

struct cmd {
    char *name;
    enum cmdtype type;
} cmd_table[50] = { "ce", CE,	/* basic nroff requests */
		    "ul", UL,
		    "nf", NF,
		    "fi", FI,
		    "TS", NF,	/* universal macros */
		    "TE", FI,
		    "EQ", NF,
		    "EN", FI,
		    "PS", NF,
		    "PE", FI,
		    "IS", NF,
		    "IE", FI,
		    "DS", NF,	/* ms macros */
		    "ID", NF,
		    "CD", NF,
		    "LD", NF,
		    "DE", FI,
		    "(b", NF,	/* me macros */
		    ")b", FI,
		    "(c", NF,
		    ")c", FI,
		    "(l", NF,
		    ")l", FI,
		    "(z", NF,
		    ")z", FI,
		    (char *) NULL, OTHER
		};

int val;	/* both are set in getval() */
char argtype;	/* and used in setparam() */

/*  command() takes a line starting with some form of the command start
 *  sequence (period or apostrophe, optionally preceded by \&), and
 *  calls getcmd() to return an integer representing the command name,
 *  and then takes appropriate action.
 *
 *  In all cases it produces a break and prints out the command line as is.
 */

command(line)
char line[];
{
    cmd = getcmd(line);

    if (cmd != OTHER)
	switch (cmd) {
	case CE:
	    getval(line);		/* only need getval() with CE and UL */
	    ce_val = setparam(ce_val, 1, 0, HUGE);
	    break;
	case UL:
	    getval(line);
	    ul_val = setparam(ul_val, 1, 0, HUGE);
	    break;
	case NF:
	    nf_val = 1;
	    break;
	case FI:
	    nf_val = 0;
	    break;
	}

    n_brk();
    puts(line);
}

enum cmdtype getcmd(line)	/* gets command type by binary search */
char *line;		/* stolen from K & R p. 125 with minor changes */
{
    int high, low, mid, cond;
    char *cp;

    low = 0;
    high = len - 1;

    cp = (*line == '\\') ? line + 3 : line + 1;

    while (low <= high) {
	mid = (low + high)/2;
	if ((cond = strncmp(cp, cmd_table[mid].name, 2)) < 0)
	    high = mid - 1;
	else if (cond > 0)
	    low = mid + 1;
	else
	    return(cmd_table[mid].type);
    }
    return(OTHER);
}

mk_table(sarray, earray)
char *sarray, *earray;
{
    int mycmp();
    char *cp, *malloc(), *strncpy();
    struct cmd *cmdptr;

    if (len == 0) {		/* find end */
	cmdptr = cmd_table;
	while (cmdptr->name)
	    cmdptr++;		/* now pointing to NULL ending defined */
	len = cmdptr - cmd_table;
    }
    else
	cmdptr = cmd_table + len;

    if (sarray)
	for (cp = sarray; *cp; cp += 3) {
	    cmdptr->name = malloc(3);
	    (void) strncpy(cmdptr->name, cp + 1, 2);
	    cmdptr->type = NF;
	    cmdptr++;
	}

    if (earray)
	for (cp = earray; *cp; cp += 3) {
	    cmdptr->name = malloc(3);
	    (void) strncpy(cmdptr->name, cp + 1, 2);
	    cmdptr->type = FI;
	    cmdptr++;
	}

    len = cmdptr - cmd_table;

    qsort((char *) cmd_table, len, sizeof cmd_table[0], mycmp);
}

mycmp(s1, s2)
struct cmd *s1, *s2;
{
    return(strcmp(s1->name, s2->name));
}

/*  getval() gets value of argument preceded by optional sign.  Here
    we are following the nroff rules: commands are exactly two
    letters long followed by optional spaces before arguments.  */

getval(line)
char *line;
{
    char *cp;

    if (*line == '\\')		/* don't test for z_flag, since otherwise */
	cp = line + 5;		/* we would not be here */
    else
	cp = line + 3;
    for ( ; isspace(*cp); *cp++)
	;
    argtype = *cp;
    if (argtype == PLUS  ||  argtype == MINUS)
	cp++;
    val = atoi(cp);
}

/* setparam() sets parameter.  May be set to param if present and legal,
 * otherwise to def_val, if absent, less than min_val or greater than
 * max_val.
 */

setparam(param, def_val, min_val, max_val)
int param, def_val, min_val, max_val;
{
    if (argtype == '\0')
	return(def_val);
    else if (argtype == PLUS)
	param += val;
    else if (argtype == MINUS)
	param -= val;
    else
	param = val;
    param = MAX(param, min_val);
    param = MIN(param, max_val);

    return(param);
}

#ifdef DEBUG
print_tab()	/* prints table, useful while debugging */
{
    struct cmd *cmdptr;

    for (cmdptr = cmd_table; cmdptr < cmd_table + len; cmdptr++)
	printf("%s\n", cmdptr->name);
}
#endif
SHAR_EOF
if test 4522 -ne "`wc -c < 'command.c'`"
then
	echo shar: error transmitting "'command.c'" '(should have been 4522 characters)'
fi
chmod +x 'command.c'
fi
echo shar: extracting "'efopen.3s'" '(918 characters)'
if test -f 'efopen.3s'
then
	echo shar: will not over-write existing file "'efopen.3s'"
else
cat << \SHAR_EOF > 'efopen.3s'
.TH EFOPEN 3S  "11 May 1986"
.SH NAME
efopen \- open a stream, exiting with message in case of failure
.SH SYNOPSIS
.B #include <stdio.h>
.br
.B extern char *progname;
.PP
.SM
.B FILE
.B *efopen(filename, type)
.br
.B char *filename, *type;
.SH DESCRIPTION
.I Efopen
calls
.IR fopen (3S)
to open the file named by
.IR filename ,
and if successful returns a pointer to be used to identify the stream in
subsequent operations.
.PP
If
.I filename
is the string \-
.I efopen
will return
.IR stdin .
.PP
.I Type
is a character string as in
.IR fopen (3S).
.PP
On failure,
.I efopen
exits, after printing out the name of the program (if a value has been
given to
.B progname
by main), the name of the file, and calling
.IR perror (3).
.SH "SEE ALSO"
fopen(3S),
open(2),
fclose(3)
.SH "AUTHOR"
Modified by Mohamed el Lozy from program given in
.I "The UNIX Programming Environment"
by Brian W. Kernighan and Rob Pike, p 182.
SHAR_EOF
if test 918 -ne "`wc -c < 'efopen.3s'`"
then
	echo shar: error transmitting "'efopen.3s'" '(should have been 918 characters)'
fi
chmod +x 'efopen.3s'
fi
echo shar: extracting "'efopen.c'" '(591 characters)'
if test -f 'efopen.c'
then
	echo shar: will not over-write existing file "'efopen.c'"
else
cat << \SHAR_EOF > 'efopen.c'
#ifndef lint
static char rcsid[] = "$Header: efopen.c,v 1.3 86/05/11 09:50:48 root Exp $";
#endif

#include <stdio.h>

FILE *
efopen(file, mode)	/* fopen file, die if cannot */
char *file, *mode;	/* from K & P with addition of perror() and handling
			   of "-" as stdin */
{
    FILE *fp;
    extern char *progname;

    if (strcmp(file, "-") == 0)
	return(stdin);

    if ((fp = fopen(file, mode)) != NULL)
	return (fp);

    if (progname)
	fprintf(stderr, "%s ", progname);
    fprintf(stderr, "can't open file %s mode %s: ", file, mode);
    perror("");
    exit(1);
	/* NOTREACHED */
}
SHAR_EOF
if test 591 -ne "`wc -c < 'efopen.c'`"
then
	echo shar: error transmitting "'efopen.c'" '(should have been 591 characters)'
fi
chmod +x 'efopen.c'
fi
echo shar: extracting "'fgetsmod.3s'" '(1470 characters)'
if test -f 'fgetsmod.3s'
then
	echo shar: will not over-write existing file "'fgetsmod.3s'"
else
cat << \SHAR_EOF > 'fgetsmod.3s'
.TH FGETSMOD 3S  "11 May 1986"
.SH NAME
fgetsmod \- get a line from a stream, truncating it if it is too long
.SH SYNOPSIS
.B #include <stdio.h>
.br
.B #define TOOLONG -2
.PP
.B int *fgets(s, n, stream)
.br
.B char *s;
.br
.SM
.B FILE
.B *stream;
.SH DESCRIPTION
.I Fgetsmod
is a modified version of
.IR fgets ,
which truncates lines longer than the size of the array
into which they are being read.  It reads 
.IR n \-2
characters, or up to a newline
character, whichever comes first,
from the
.I stream
into the string
.IR s .
If no newline is found after reading the first
.IR n \-2
characters,
.I fgetsmod
will add a newline and null character, then
read and discard characters from the
input stream up to a newline.  In other words, it
will discard characters in excess of the
capacity of
.IR s .
The last character read into
.I s
is followed by a null character.
.I Fgetsmod
returns the number of characters read
if it can read the whole line.
.SH "SEE ALSO"
gets(3S),
puts(3S),
getc(3S),
scanf(3S),
fread(3S),
ferror(3S)
.SH AUTHOR
Mohamed el Lozy, Harvard Health Sciences Computing Faility
.SH DIAGNOSTICS
.I Fgetsmod
return the integer zero
upon end of file or read error.
It returns \-2 (to avoid confusion with
.BR \s9EOF\s0 )
if the line is too long.
.SH BUGS
.I Fgetsmod
returns an
.IR int ,
unlike
.IR fgets ,
which returns a
.IR char .
It should therefore probably return
.SM
.B EOF
on end of file, but returning zero makes it
more similar to
.IR fgets .
SHAR_EOF
if test 1470 -ne "`wc -c < 'fgetsmod.3s'`"
then
	echo shar: error transmitting "'fgetsmod.3s'" '(should have been 1470 characters)'
fi
chmod +x 'fgetsmod.3s'
fi
echo shar: extracting "'fgetsmod.c'" '(1683 characters)'
if test -f 'fgetsmod.c'
then
	echo shar: will not over-write existing file "'fgetsmod.c'"
else
cat << \SHAR_EOF > 'fgetsmod.c'
#ifndef lint
static char rcsid[] = "$Header: fgetsmod.c,v 1.3 86/05/05 14:19:17 root Exp $";
#endif

#include	<stdio.h>

#define TOOLONG -2

/* New and improved version of fgets.  Unlike original, eats up extra chars.
 * fgets1 will read n - 1 characters, or up to a new line, whichever
 * comes first, from stream iop into string s.  The last character read
 * into s is followed by a null character.
 *
 * It deals with all possibilities.  If line ends with newline or have
 * isolated EOF, no problem.  Otherwise, it will insert a newline and eat
 * any excess characters.  Hence guarantees line ending with newline
 * followed by null.
 *
 * It returns:
 *   1.  NULL at end of file, for compatible with fgets.
 *   2.  TOOLONG if line is too long.
 *       This is usable as a warning.
 *   3.  Length of line, excluding null (like strlen), otherwise.
 *       This is useful in the usual case when line is read uneventfully.
 */

fgetsmod(s, n, iop)
char *s;
register FILE *iop;
{
    register c;
    register char *cs;

    cs = s;

    while (--n > 0 && (c = getc(iop)) != EOF) {
	*cs++ = c;
	if (c == '\n')
	    break;
    }

    if (c == '\n') {	/* normal ending, commonest case */
	*cs = '\0';
	return (cs - s);
    }

    if ((c == EOF) && (cs == s))  /* isolated EOF, second commonest case */
	return (NULL);

    if (n == 0) {	/* line too long */
	*cs = '\0';
	*(--cs) = '\n';	/* put in missing newline */
	while ((c = getc(iop)) != EOF && c != '\n')	/* eat up extra chars */
	    ;
	return (TOOLONG);
    }

    if (c == EOF) {	/* final line has no newline -- rare */
	*cs++ = '\n';
	*cs = '\0';
	return (cs - s);	/* pretend all was OK */
    }
    /* NOTREACHED */
}
SHAR_EOF
if test 1683 -ne "`wc -c < 'fgetsmod.c'`"
then
	echo shar: error transmitting "'fgetsmod.c'" '(should have been 1683 characters)'
fi
chmod +x 'fgetsmod.c'
fi
echo shar: extracting "'fmtr.1'" '(3397 characters)'
if test -f 'fmtr.1'
then
	echo shar: will not over-write existing file "'fmtr.1'"
else
cat << \SHAR_EOF > 'fmtr.1'
.TH FMTR 1H LOCAL "4th Berkeley Distribution"
.SH NAME
fmtr \- simple formatter for
.I roff
source files
.SH SYNOPSIS
.B fmtr
[ \-w
.I width
] [ \-z ] [ \-s
.I .s1.s2.s3 ... .sn
] [ \-e
.I .e1.e2.e3 ... .en
] [ name ...  ]
.SH DESCRIPTION
.I Fmtr
is a simple text formatter which evens out the lines of files prepared
for submission to the Unix formatters
.IR nroff ,
.IR troff ,
or
.IR ditroff .
.PP
It is for users who find editing neatly formatted files pleasanter than
working with files with irregular lines.  It knows that lines starting
with a period or with an apostrophe should be output as is, and that
certain requests and macros start no-fill mode, while others return to
fill mode.  So, unlike
.IR fmt ,
it will process the following example properly:
.sp
.in +5
.nf
Some short lines
to be joined,
then a table which should be respected:
\&.TS
n n.
1	2
3	4
\&.TE
.nf
More short
lines to be joined.
.in
.sp
.fi
The above will produce:
.sp
.nf
.in +5
Some short lines to be joined, then a table which should be respected:
\&.TS
n n.
1	2
3	4
\&.TE
More short lines to be joined.
.fi
.sp
.in
.I Fmt
run on that example would produce:
.sp
.in +5
.nf
Some short lines to be joined, then a table which should be respected:
\&.TS
n n.  1       2 3       4
\&.TE
More short lines to be joined.
.fi
.in
.sp
destroying the table.
.PP
.I Fmtr
reads the concatenation of input files (or standard input if none are
given) and produces on standard output a version of its input with lines
as close as possible to 72 characters.  This default line length may be
modified with the
.IR \-w
(for width) flag.  If you specify the
.I \-z
flag it will peek under a zero width character at the start of a line
seeking a command.
.I Fmtr
knows about the
.IR ms (7)
and
.IR me (7)
macro packages.
.PP
Since
.I fmtr
knows and respects most formatter constructs, it can be safely used on
an entire document, as long as that document does not contain any
strange sequences of commands.
.PP
There are two ways in which additional macros that start or end no-fill
mode may be specified to
.IR fmtr .
The
.I \-s
and
.I \-e
flags can be used to introduce sequences of macros that start and end
no-fill mode respectively.  Any number of macros, each of which must
consist of a period followed by exactly two letters, can be concatenated
into a string.  So if you are using two macros,
.I .OS
and
.IR .FS ,
both of which start no-fill mode, and a corresponding pair,
.I .OE
and
.IR .FE ,
which end no-fill mode, you would enter
.sp
.ti +5
.I "fmtr -s .OS.FS -e .OE.FE file"
.sp
For users who regularly use macros of there own, the environmental
variables
.I FMTR_S
and
.I FMTR_E
are taken as strings of macros which start and end no-fill mode.  They
should be in the same format as the strings used as arguments to the
.I \-s
and
.I \-e
flags.
.SH "SEE ALSO"
nroff(1), environ(7), ms(7), me(7)
.SH AUTHOR
Mohamed el Lozy, Health Sciences Computing Facility, Harvard School of
Public Health
.SH BUGS
The program does not attempt to duplicate
.IR fmt ,
and will not deal with mail headers or with indented (preformatted)
text.
.br
The current version does not support the
.I mm
macros, I hope to correct that soon.
.br
Macros recognized must be exactly two characters long.
.br
.I Fmtr
is not a full
.I roff
recognizer.  For example, changing the definition of the control or the
escape character, will cause confusion.
SHAR_EOF
if test 3397 -ne "`wc -c < 'fmtr.1'`"
then
	echo shar: error transmitting "'fmtr.1'" '(should have been 3397 characters)'
fi
chmod +x 'fmtr.1'
fi
echo shar: extracting "'fmtr.c'" '(3468 characters)'
if test -f 'fmtr.c'
then
	echo shar: will not over-write existing file "'fmtr.c'"
else
cat << \SHAR_EOF > 'fmtr.c'
#ifndef lint
static char rcsid[] = "$Header: fmtr.c,v 1.4 86/05/05 14:09:41 root Exp $";
#endif

/*  fmtr, a formatter for roff source text respecting no fill mode.
 *  The code is derived from a translation to C of Kernighan and
 *  Plaugher's format program.
 */

#include "fmtr.h"

int optind, opterr;
char *optarg, *progname;		/* program name */

main(argc, argv)
int argc;
char *argv[];
{
    int i, c;
    char *sarray, *earray, *getenv();
    FILE *efopen(), *fp;

    progname = argv[0];
    outp = outbuf;
    sarray = earray = (char *) NULL;
    llength = 72;	/* default */

    sarray = getenv("FMTR_S");
    check(sarray, "environmental variable FMTR_S");
    earray = getenv("FMTR_E");
    check(earray, "environmental variable FMTR_E");

    mk_table(sarray, earray);	/* make table of commands from env var */
				/* we call mk_table whether the arrays */
				/* null or not */
    sarray = earray = (char *) 0;

    while ((c = getopt(argc, argv, "zw:s:e:")) != EOF)
	switch (c) {
	    case 'z':
		z_flag = 1;
		break;
	    case 'w':
		llength = atoi(optarg);
		if (llength <= 0 || llength >= BUFSIZ - 2) {
		    fprintf(stderr,"%s: bad line length: %s\n", progname, optarg);
		    exit (1);
		}
		break;
	    case 's':
		sarray = optarg;
		check(sarray, "-s flag");
		break;
	    case 'e':
		earray = optarg;
		check(earray, "-e flag");
		break;
	    case '?':
		usage();
		exit(1);
	}

    mk_table(sarray, earray);	/* make table of commands from flags */
				/* again, always call */

    argc -= optind;
    argv += optind;
    
    fp = stdin;
    i = 0;
    do {
	if (argc > 0)
	    fp = efopen(*argv, "r");
	do_fmt(fp);		/* does real work */
	argv++;
    } while (++i < argc);

    n_brk();
}

check(string, origin)	/* check format of string of macros */
char *string, *origin;
{
    if (string == NULL)
	return;
    if ((string[0] != '.') || strlen(string) % 3 != 0) {
	fprintf(stderr,
	"%s: list of macros '%s' supplied with %s is not in correct format\n",
	progname, string, origin);
	exit(1);
    }
}

/*  do_fmt is where work is done, or rather assigned.  Each line
 *  read has the terminatig newline, and any trailing blanks, removed.
 *  Then it is sent to either command() or text(), depending on whether
 *  it appears to be a command or not.
 */

do_fmt(fp)
FILE *fp;
{
    char *cp, line[10*BUFSIZ];
    int length;

    while (length = fgetsmod(line, sizeof line, fp)) {
	for (cp = &line[length - 2]; *cp == ' ' && cp >= line; cp--)
	    ;
	*++cp = '\0';
        if (*line == '.' || *line == '\'' ||
	  (z_flag && strncmp(line, "\\&.", 3) == 0) ||
	  (z_flag && strncmp(line, "\\&'", 3) == 0))
            command(line);
        else
            text(line);
    }
}

/*  text() checks for leading blanks or blank line, in which case causes
 *  break.  Blank lines output as is, others broken into words by getword()
 *  with words sent to outbuf by putword().
 */

text(line)
char *line;
{
    char *pline, wordbuf[BUFSIZ], *getword();

    if (ul_val || ce_val || nf_val) {
	puts(line);
	if (ul_val)
	    ul_val--;
	if (ce_val)
	    ce_val--;
	return;
    }

    if (*line == ' ' || *line == '\0')
	leadbl(line);
    if (*line == '\0')
	put(line);
    else {		/* main case */
	pline = line;
	do {
	    pline = getword(pline, wordbuf);
	    if (pline)
	        putword(wordbuf);
	} while (pline);
    }
}

usage()
{
    fprintf(stderr,
"usage: %s [ -z ] [ -wn ] [ -s start_string ] [ -e end_string] [ file ... ]\n",
    progname);
}
SHAR_EOF
if test 3468 -ne "`wc -c < 'fmtr.c'`"
then
	echo shar: error transmitting "'fmtr.c'" '(should have been 3468 characters)'
fi
chmod +x 'fmtr.c'
fi
echo shar: extracting "'fmtr.h'" '(365 characters)'
if test -f 'fmtr.h'
then
	echo shar: will not over-write existing file "'fmtr.h'"
else
cat << \SHAR_EOF > 'fmtr.h'
#include <stdio.h>
#include <ctype.h>

int z_flag;		/* look behind zero width spaces? */
int nf_val;		/* no fill on or off? */
int ul_val;		/* number of lines to underline */
int ce_val;		/* number of lines to center */
int llength;

char outbuf[BUFSIZ];	/* output buffer, ridiculously large */
char *outp;		/* pointer into outbuf */

void n_brk();
char *strcpy();
SHAR_EOF
if test 365 -ne "`wc -c < 'fmtr.h'`"
then
	echo shar: error transmitting "'fmtr.h'" '(should have been 365 characters)'
fi
chmod +x 'fmtr.h'
fi
echo shar: extracting "'getopt.3'" '(2749 characters)'
if test -f 'getopt.3'
then
	echo shar: will not over-write existing file "'getopt.3'"
else
cat << \SHAR_EOF > 'getopt.3'
.TH GETOPT 3
.DA 25 March 1982
.SH NAME
getopt \- get option letter from argv
.SH SYNOPSIS
.ft B
int getopt(argc, argv, optstring)
.br
int argc;
.br
char **argv;
.br
char *optstring;
.sp
extern char *optarg;
.br
extern int optind;
.ft
.SH DESCRIPTION
.I Getopt
returns the next option letter in
.I argv
that matches a letter in
.IR optstring .
.I Optstring
is a string of recognized option letters;
if a letter is followed by a colon, the option is expected to have
an argument that may or may not be separated from it by white space.
.I Optarg
is set to point to the start of the option argument on return from
.IR getopt .
.PP
.I Getopt
places in
.I optind
the
.I argv
index of the next argument to be processed.
Because
.I optind
is external, it is normally initialized to zero automatically
before the first call to 
.IR getopt .
.PP
When all options have been processed (i.e., up to the first
non-option argument),
.I getopt
returns
.BR EOF .
The special option
.B \-\-
may be used to delimit the end of the options;
.B EOF
will be returned, and
.B \-\-
will be skipped.
.SH SEE ALSO
getopt(1)
.SH DIAGNOSTICS
.I Getopt
prints an error message on
.I stderr
and returns a question mark
.RB ( ? )
when it encounters an option letter not included in
.IR optstring .
.SH EXAMPLE
The following code fragment shows how one might process the arguments
for a command that can take the mutually exclusive options
.B a
and
.BR b ,
and the options
.B f
and
.BR o ,
both of which require arguments:
.PP
.RS
.nf
main(argc, argv)
int argc;
char **argv;
{
	int c;
	extern int optind;
	extern char *optarg;
	\&.
	\&.
	\&.
	while ((c = getopt(argc, argv, "abf:o:")) != EOF)
		switch (c) {
		case 'a':
			if (bflg)
				errflg++;
			else
				aflg++;
			break;
		case 'b':
			if (aflg)
				errflg++;
			else
				bproc();
			break;
		case 'f':
			ifile = optarg;
			break;
		case 'o':
			ofile = optarg;
			break;
		case '?':
		default:
			errflg++;
			break;
		}
	if (errflg) {
		fprintf(stderr, "Usage: ...");
		exit(2);
	}
	for (; optind < argc; optind++) {
		\&.
		\&.
		\&.
	}
	\&.
	\&.
	\&.
}
.RE
.PP
A template similar to this can be found in
.IR /usr/pub/template.c .
.SH HISTORY
Written by Henry Spencer, working from a Bell Labs manual page.
Behavior believed identical to the Bell version.
.SH BUGS
It is not obvious how
`\-'
standing alone should be treated;  this version treats it as
a non-option argument, which is not always right.
.PP
Option arguments are allowed to begin with `\-';
this is reasonable but reduces the amount of error checking possible.
.PP
.I Getopt
is quite flexible but the obvious price must be paid:  there is much
it could do that it doesn't, like
checking mutually exclusive options, checking type of
option arguments, etc.
SHAR_EOF
if test 2749 -ne "`wc -c < 'getopt.3'`"
then
	echo shar: error transmitting "'getopt.3'" '(should have been 2749 characters)'
fi
chmod +x 'getopt.3'
fi
echo shar: extracting "'getopt.c'" '(1608 characters)'
if test -f 'getopt.c'
then
	echo shar: will not over-write existing file "'getopt.c'"
else
cat << \SHAR_EOF > 'getopt.c'
#ifndef lint
static char rcsid[] = "$Header: getopt.c,v 1.3 86/05/05 14:20:35 root Exp $";
#endif

/* got this off net.sources */
#include <stdio.h>

/*
 * get option letter from argument vector
 */
int	opterr = 1,	/* if set to zero no message for bad option */
	optind = 1,	/* index into parent argv vector */
	optopt;		/* character checked for validity */
char	*optarg;	/* argument associated with option */

#define BADCH	(int)'?'
#define EMSG	""

getopt(nargc,nargv,ostr)
int	nargc;
char	**nargv,
	*ostr;
{
    static char	*place = EMSG;	/* option letter processing */
    register char	*oli;		/* option letter list index */
    char	*index();

    if(!*place) {			/* update scanning pointer */
	if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place)
	    return(EOF);
	if (*place == '-') {	/* found "--" */
	    ++optind;
	    return(EOF);
	}
    }				/* option letter okay? */
    if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
	if(!*place) ++optind;
	if (opterr)
	    fprintf(stderr, "%s: illegal option -- %c\n", *nargv, optopt);
	return(BADCH);
    }
    if (*++oli != ':') {		/* don't need argument */
	optarg = NULL;
	if (!*place) ++optind;
    }
    else {				/* need an argument */
	if (*place) optarg = place;	/* no white space */
	else if (nargc <= ++optind) {	/* no arg */
	    place = EMSG;
	    if (opterr)
		fprintf(stderr, "%s: option requires an argument -- %c\n",
		    *nargv, optopt);
	    return(BADCH);
	}
	 else optarg = nargv[optind];	/* white space */
	place = EMSG;
	++optind;
    }
    return(optopt);			/* dump back option letter */
}
SHAR_EOF
if test 1608 -ne "`wc -c < 'getopt.c'`"
then
	echo shar: error transmitting "'getopt.c'" '(should have been 1608 characters)'
fi
chmod +x 'getopt.c'
fi
echo shar: extracting "'lowtext.c'" '(2420 characters)'
if test -f 'lowtext.c'
then
	echo shar: will not over-write existing file "'lowtext.c'"
else
cat << \SHAR_EOF > 'lowtext.c'
#ifndef lint
static char rcsid[] = "$Header: lowtext.c,v 1.3 86/04/25 17:15:14 root Exp $";
#endif

#include "fmtr.h"

int ti_val;

/*  leadbl() deals with leading blanks, causes break, then sets
 *  ti_val to number of blanks, unless line is blank.  Then pulls
 *  line forewards, so text() now has line starting with text, with
 *  ti_val containing the needed indent.
 */

leadbl(line)
char *line;
{
    char *ip;

    n_brk();

    ip = line;
    while (*ip == ' ')
	ip++;
    if (*ip != '\n')
	ti_val += ip - line;
    (void) strcpy(line, ip);
}

/* n_brk() causes a break */

void n_brk()
{
    if (outp > outbuf) {
	    outp--;	/* back off from EOS */
	while (*outp == ' ' && outp >= outbuf)	/* remove trailing blanks */
	    outp--;
	*++outp  = '\0';
	put(outbuf);
    }
    outp = outbuf;
}

/*
 *	getword gets the next word plus trailing space
 *      from the array pointed to by pline and
 *	returns it in that pointed at by word.  If there are
 *	no further words on that line, it returns NULL.
 *	It returns a pointer to the start of the next word.
 */

char *getword(pline, word)
char *pline, *word;
{
    if (*pline == '\0') {
	*word = '\0';
	return(NULL);
    }

    while (*pline != ' ' && *pline != '\0') {
	if (*pline == '\\' && isspace(pline[1])) /* get escaped space in word */
	    *word++ = *pline++;
        *word++ = *pline++;
    }

    /*  get trailing spaces, and guarantee spaces at end of line;
     *  normally one but two at end of sentence.
     */

    if (*pline == '\0') {
	char *cptmp = pline;

	*word++ = ' ';
	while (any(*--cptmp, "\"']})"))
	    ;
	if (any(*cptmp, ".:!?"))
	    *word++ = ' ';
    }
    while (*pline == ' ')
	*word++ = *pline++;
    *word = '\0';
    return(pline);
}

putword(word)	/* put word into output buffer */
char *word;
{
    int s, t;	/* not needed, but greatly simplify if */

    t = strlen(word) - 1;	/* -1 for one trailing blank */
    s = outp - outbuf;
    if (s + t <= llength - ti_val) {
	for (; *word; *outp++ = *word++)
	    ;
	return;
    }
    n_brk();
    for (; *word; *outp++ = *word++)
	;
}

put(line)	/* output routine, separate as is more complex in original */
char *line;
{
    int i;

    for (i = 1; i <= ti_val; i++)
	putchar(' ');
    ti_val = 0;
    puts(line);
}

any(ch, string)	/* returns true if character is in string */
char ch;
char *string;
{
    while (*string)
	if (ch == *string++)
	    return(1);
    return(0);
}
SHAR_EOF
if test 2420 -ne "`wc -c < 'lowtext.c'`"
then
	echo shar: error transmitting "'lowtext.c'" '(should have been 2420 characters)'
fi
chmod +x 'lowtext.c'
fi
exit 0
#	End of shell archive