ff: fast text formatter (part 1 of 2)

sources-request at panda.UUCP sources-request at panda.UUCP
Fri Nov 29 02:32:36 AEST 1985


Mod.sources:  Volume 3, Issue 51
Submitted by: decvax!wanginst!perlman

                 ff: A Fast Text Formatter

Here is ff, a fast text  formatter.  It fills a gap  between
the fmt program in Berkeley UNIX and systems like nroff.  ff
is sort  of an  inside-out  nroff.   There are  no  commands
inside a file, but the common options like line width,  line
spacing, indentation,  pagination,  etc., are  command  line
options.  ff is a general  utility that lets you throw  away
most uses of programs like pr, expand, and especially fmt.

There are  a lot  of options  for ff--some  would argue  too
many--but they are necessary  to provide the  functionality.
I make shell scripts that encode most of my needs.  Here  is
the shell script I  am using to  format these paragraphs  (I
have the filter bound to a function key; I go to the top  of
the paragraph and type PF1).
	exec ff -w 60 -j -B "	 '*.@|" $*
I have  scripts  for  centering  regions  and  for  indented
paragraphs.   These  and  a  nice  one  for  making  program
listings are listed in the manual entry.  emacs users  might
find the centering  option useful, even  though many of  the
other functions are built in to  emacs.  vi users will  find
ff and option-variants on it much more useful.

ff really is fast--roughly  twice the speed  as fmt for  the
formats fmt  supports.   For paginating  text, ff  is  about
twice as fast as  pr.  The tab  expansion options on ff  are
comparable to  those of  the  expand program,  but ff  is  a
little slower than expand  on expanding tabs--it simply  has
too many concerns that expand can ignore.  Still, ff is fast
enough to bind  to keys in  emacs or vi  to filter  regions,
making vi a passable wysiwyg editor.  One reason for this is
extensive profiling and  optimization, some  by my  students
for a programming efficiency assignment.

#! /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:
#	ff.1
#	ff.test
#	makefile
#	number.c
#	filter.c
#	getopt.c
# This archive created: Wed Nov 13 19:27:58 1985
# By:	Gary Perlman (Wang Institute, Tyngsboro, MA 01879 USA)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'ff.1'" '(6812 characters)'
if test -f 'ff.1'
then
	echo shar: "will not over-write existing file 'ff.1'"
else
sed 's/^	X//' << \SHAR_EOF > 'ff.1'
	X.TH FF 1 "August 10, 1985" "Wang Institute" "UNIX User's Manual"
	X.\" $Compile: iroff -man.new %f
	X.SH NAME
	Xff \- fast text formatter
	X.SH USAGE
	X.B ff
	X[options] [-] [files]
	X.SH DESCRIPTION
	X.I ff
	Xis a simple text formatter for flexible uniform formatting of
	Xinput files.
	XProgram options are used to control formatting.
	XThis is in contrast to text formatters like
	X.I nroff (1)
	Xthat require special format requests to be part of their input files.
	XBesides avoiding cryptic format requests in text,
	X.I ff
	Xis considerably faster than traditional formatters like
	X.I nroff (1)
	Xand even simple formatters like
	X.I fmt (1).
	X.PP
	XThe most complicated concept with
	X.I ff
	Xis that of a line break.
	XA line break causes an interruption in the filling
	X(evening out of the text lines).
	XLine breaks occur when special characters are seen at the beginnings
	Xof lines, or when all lines are broken.
	XBy default, any non-alphanumeric character will cause a break,
	Xbut this can be controlled with the
	X.B -B
	Xoption.
	XA blank line always causes a break.
	X.SH OPTIONS
	XThere are many, many options to allow control of
	Xindentation, line width, line spacing, filling,
	Xpagination with headers and footers,
	Xline numbering, right justification,
	Xand perhaps some other things.
	XThey have extensive type and range checking
	Xthat produces diagnostic error messages,
	Xso warnings of obviously wrong options will not be discussed here.
	XIn general, options that imply the use of others
	Xwork the way they should; if the page size is set,
	Xthen pagination is automatically assumed.
	XSome combinations of options give impressive, even artistic, effects.
	XMaking a small text file and playing with it is the easiest
	Xway to learn how the options interact.
	X.de OP
	X.TP
	X.B -\\$1 \\$2
	X..
	X.OP b
	XBreak all lines of text.
	XThat is, don't even-out lines by filling.
	XBy default, text lines are filled.
	X.OP B breakchars
	XChange the set of characters that cause line breaks at the start of lines to
	X.I breakchars.
	XBy default, any characters but letters and numbers cause a break.
	XA good choice for break characters might be "*-+" and TABS
	Xthat might be used for lists.
	X.OP c
	XCenter all lines of text.
	XThis option stops all filling of text.
	X.OP d
	XDelete white space at the beginning and end of lines.
	XThis option is useful to help un-format text to be re-formatted.
	X.OP D
	XDelete empty input lines.
	XAn input line is empty if it has no characters,
	Xnot even invisible character like tabs or spaces.
	XThis option can be combined with the option to remove white space
	Xto delete visibly blank lines.
	X.OP f footer
	XSet the page footer to the string
	X.I footer.
	XThis can be any string,
	Xbut if the first character is not a letter or a digit,
	Xbut a punctuation character like /,
	Xthen that character separates the left,
	Xcenter, and right fields of a title.
	XFor example, the title
	X.ce
	X"/ff: fast formatter//1985/"
	Xwould have "ff: fast formatter" as a left justified field
	Xand 1985 as a right justified field on each page.
	XNote that there is no middle field in this example,
	Xbut there could have been, between the two consecutive /'s.
	XThere are two special characters, % and *,
	Xthat respectively are variables for the page number
	Xand the input file name.
	XThe default page footer is blank.
	X.OP F footersize
	XSet the number of blank lines at the bottom of the page.
	XThe footer, if any, is placed in the middle of the space,
	Xwhich by default, is five lines.
	XIf
	X.I footersize
	Xis 0, no footer will be printed.
	X.OP h header
	XSet the page header.
	XSee the description of three-part titles for the
	X.B -f footer
	Xoption.
	XThe default page header is
	X.ce
	X"|File: *||Page: %|".
	X.OP H headersize
	XSee the description of the footer size.
	X.OP i indent
	XSet the indentation of the text to
	X.I indent
	Xspaces.
	XNote that this effectively reduces the usable width of the page.
	X.OP I tempindent
	XSet the temporary indent.
	XThis causes filled text found immediately after a break to
	Xbe indented for one line.
	XIt is useful for indenting the first lines of paragraphs.
	XIf
	X.I tempindent
	Xis negative,
	Xthe the temporary indent will be relative to the current
	X.I indent
	Xvalue.
	XIf the
	X.I tempindent
	Xvalue is more negative than the
	X.I indent
	Xvalue is positive,
	X.I ff
	Xwill automatically increase
	X.I indent.
	X.OP j
	XJustify the text.
	XThat is, even the right margin by inserting spaces in the line.
	XOnly filled text can be justified.
	X.OP n
	XNumber all output lines produced by the input text.
	XLines from multiple line spacing or pagination will not
	Xbe numbered.
	X.OP N numberwidth
	XSet the width of the line numbers.
	XThe default is to take up 4 spaces.
	XNote that this effectively reduces the usable part of the page.
	X.OP p
	XPaginate the output.
	XSee the options for page header and footer control.
	X.OP P pagesize
	XSet the number of lines in a page to
	X.I pagesize.
	XBy default, the standard 66 line page is assumed.
	X.OP s spacing
	XSet the line spacing.
	XBy default, text is single spaced (\fIspacing\fR equals 1).
	X.OP t tab
	XSet individual absolute and relative tab stops.
	XThese values tell the formatter
	Xwhere to put the text (from an unfilled line)
	Xthat follows a tab character.
	XEach tab stop is supplied with its own
	X.B -t
	Xoption; there is no way to bundle them.
	X.I tab
	Xvalues can be integers without a plus sign.
	XThese are \fIabsolute\fR tab settings;
	Xthe tabs go to that position.
	XThe values must increase monotonically.
	XIf a
	X.I tab
	Xvalue is preceded by a plus sign,
	Xthen it is interpreted \fIrelative\fR to the previous tab setting.
	XFor example, a tab setting of 40 followed by one of +20
	Xwill set the second tab stop to 60.
	X.OP T tabs
	XSet tab stops to every
	X.I tabs
	Xspaces.
	XIt is useful to get equally spaced tabs.
	XThis option cannot be used with the other tab setting option.
	X.OP u
	XPrint All Input Text As Initial Upper-Case Titles,
	XLike This Sentence.
	XThis option goes well with the one for centering lines.
	X.OP U
	XPrint a usage summary of all the options and stop.
	X.OP w width
	XSet the page width.
	XBy default, the page width is 72 characters.
	XNote that the usable line length is sometimes less
	Xthan the page width.
	XIf line numbering or indentation is requested,
	Xthese subtract from the line length.
	X.SH EXAMPLES
	XSome of these examples can make shell scripts or aliases.
	X.nf
	X.ta .5i
	X.sp
	XCentered Titles: title
	X	ff -dcu $*
	XDouble Spaced Unfilled Paginated indented (for editing): draft
	X	ff -s 2 -b -p -f "`date`" -i 8 $*
	XProgram Listing: cpr
	X	H="@        Dir: `pwd`@@File: *@"
	X	F="@        $NAME@`date`@Page %@"
	X	ff -b -N 8 -H 3 -h "$H" -F 3 -f "$F" -T 4 -w 79 -i 2 $*
	XReformat Paragraphed Text: nr
	X	ff -jd -I 5 -i 10 -w 65 -B "TAB SP'*.@" $*
	X.fi
	X.SH DIAGNOSTICS
	XSome options are incompatible with others.
	XFor example, centered text cannot be right-justified.
	X.I ff
	Xwill not allow inconsistent combinations of options.
	X.SH "SEE ALSO"
	Xfmt(1), nroff(1), scribe(1w)
	X.SH AUTHOR
	XGary Perlman (with help from many students)
	X.SH STATUS
	XPretty well tested.
SHAR_EOF
if test 6812 -ne "`wc -c < 'ff.1'`"
then
	echo shar: "error transmitting 'ff.1'" '(should have been 6812 characters)'
fi
fi
echo shar: "extracting 'ff.test'" '(7703 characters)'
if test -f 'ff.test'
then
	echo shar: "will not over-write existing file 'ff.test'"
else
sed 's/^	X//' << \SHAR_EOF > 'ff.test'
	X#! /usr/local/bin/ksh
	X# test script for ff fast text formatter
	X
	XPATH=$PATH:/tmp
	Xdelim="-------------- "
	Xtrap "rm -f /tmp/linelength fourcol ff.in; exit 1" 2
	X
	X# Define utility functions
	Xfunction runcom
	X	{
	X	echo "$delim($1):" "$2"
	X	shift
	X	echo $* | sh
	X	}
	Xfunction newtest
	X	{
	X	echo
	X	echo $1
	X	echo ===============================================
	X	}
	X# the following must be available to the Bourne shell
	Xecho "dm '#INPUT'" > /tmp/linelength
	Xchmod +x /tmp/linelength
	X
	X# create utility files
	Xcat << \EOF > fourcol
	Xa	b	c	d
	X	w	x	y	z
	XEOF
	Xcat << \EOF > ff.in
	XThe rain in Spain falls mainly in the plains.
	XShe sells sea shells on the sea shore. 
	XPeter piper picked a peck of pickled peppers.
	XThe quick red fox jumped over the lazy brown dog.
	XA stitch in time saves nine.
	XNow is the time for all good men to come to the aid of the party.
	XAnd now a word from our sponsor...
	X    
	XThe rain in Spain falls mainly in the plains.  She sells sea
	Xshells on  the sea  shore.   Peter piper  picked a  peck  of
	Xpickled peppers.   The quick  red fox jumped  over the  lazy
	Xbrown dog.  A stitch  in time saves nine.   Now is the  time
	Xfor all good men to come to the aid of the party.  And now a
	Xword from our sponsor...
	XEOF
	X
	Xnewtest "WIDTH WIDTH WIDTH WIDTH WIDTH WIDTH WIDTH WIDTH"
	Xruncom "width of 1"                  "ff -w 1 ff.in | linelength"
	Xruncom "width of 20"                 "ff -w 20 ff.in | linelength"
	Xruncom "width of 60"                 "ff -w 60 ff.in | linelength"
	X
	Xnewtest "JUSTIFY JUSTIFY JUSTIFY JUSTIFY JUSTIFY JUSTIFY JUSTIFY"
	Xruncom "justify text"                    "ff -j ff.in"
	Xruncom "justify with width = 30"         "ff -w 30 -j ff.in | linelength"
	Xruncom "justify with indent and temp indent"  "ff -j -i 20 -I 5 ff.in | linelength"
	X
	Xnewtest "ALLTABS ALLTABS ALLTABS ALLTABS ALLTABS ALLTABS"
	Xruncom "tabs set at 1"          "ff -T 1 -b fourcol"
	Xruncom "tabs set at 10"         "ff -T 10 -b fourcol"
	Xruncom "tabs set at 100"        "ff -T 100 -b fourcol"
	X
	Xnewtest "TABS TABS TABS TABS TABS TABS TABS TABS TABS TABS"
	Xruncom "decreasing values"      "ff -t 10 -t 11 -t 10"
	Xruncom "non increasing values"  "ff -t 10 -t 11 -t 11"
	Xruncom "1 too many values"      "ff -t 1 -t 2 -t 3 -t 4 -t 5 -t 6 -t 7 -t 8 -t 9 -t 10 -t 11 -t 12 -t 13 -t 14 -t 15 -t 16 -t 17 -t 18 -t 19 -t 20 -t 21"
	Xruncom "max number of values - exits from unknown option" "ff -t 1 -t 2 -t 3 -t 4 -t 5 -t 6 -t 7 -t 8 -t 9 -t 10 -t 11 -t 12 -t 13 -t 14 -t 15 -t 16 -t 17 -t 18 -t 19 -t 20 -z"
	Xruncom "huge tab value"         "ff -t 300"
	Xruncom "tab value at extreme"   "ff -t 255 fourcol"
	Xruncom "indented ff -U table"   "ff -U | ff -t 20"
	Xruncom "all plussed values" "ff -t +55 -t +15 fourcol"
	Xruncom "more tabs than tabstops" "ff -t 10 -t +15 fourcol"
	X
	Xnewtest "UPPER CASE TITLE UPPER CASE TITLE UPPER CASE TITLE"
	Xruncom "all titles"              "ff -u ff.in"
	Xruncom "centered titles"         "ff -cu ff.in"
	X
	Xnewtest "SPACING SPACING SPACING SPACING SPACING SPACING SPACING"
	Xruncom "24 lines spacing of 10 lines - 217 lines" "series 1 10 | ff -bs 24 | wc"
	X
	Xnewtest "NUMWIDTH NUMWIDTH NUMWIDTH NUMWIDTH NUMWIDTH NUMWIDTH"
	Xruncom "1 value"                 "ff -N 1 ff.in"
	Xruncom "numwidth = 10"           "ff -N 10 ff.in"
	Xruncom "numwidth 20 centered"    "ff -N 20 -c ff.in"
	Xruncom "numwidth=10 indent=10 width=50 tindent=-5 justified" "ff -N 10 -i 10 -w 50 -I -5 -j ff.in"
	X
	Xnewtest "NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER"
	Xruncom "number zero lines"       "ff -n /dev/null"
	Xruncom "number centered lines"   "ff -n -c ff.in"
	Xruncom "number 4 spaced broken lines" "ff -s 4 -n ff.in"
	Xruncom "number 3 spaced lines on 23 line pages" "series 1 200 | ff -n -s 3 -b -P 23 | wc"
	X
	Xnewtest "TINDENT TINDENT TINDENT TINDENT TINDENT TINDENT TINDENT"
	Xruncom "temp indent 5"                   "ff -I 5 ff.in"
	Xruncom "negative temp indent will automatically bump -i option" "ff -I -5 ff.in"
	Xruncom "negative temp indent less than -i option" "ff -I -5 -i 10 ff.in"
	X
	Xnewtest "INDENT INDENT INDENT INDENT INDENT INDENT INDENT INDENT"
	Xruncom "indent 10"                  "ff -i 10 ff.in"
	Xruncom "indent 50"                  "ff -i 50 ff.in"
	X#runcom "indent 300 - does not crash but is really tedious" "ff -i 300 ff.in"
	X
	Xnewtest "HEADSIZE HEADSIZE HEADSIZE HEADSIZE HEADSIZE HEADSIZE"
	Xruncom "1 header size"              "ff -H 1 ff.in | wc"
	Xruncom "100 header size"            "ff -h 'Huge header!!' -H 100 ff.in | wc"
	Xnewtest "tricky part can come with even and odd sizes"
	Xruncom "20 header w/out -p option"  "ff -H 20 ff.in | wc"
	Xruncom "odd header size = 19"       "ff -H 19 ff.in | wc"
	X
	Xnewtest "HEADER HEADER HEADER HEADER HEADER HEADER HEADER"
	Xruncom "nice header"                'ff -h "@$NAME on $TERM@@`date`" ff.in'
	X
	Xnewtest "FOOTSIZE FOOTSIZE FOOTSIZE FOOTSIZE FOOTSIZE FOOTSIZE"
	Xruncom "1 foot size"                'ff -F 0 -f "Howdy $NAME" ff.in | wc'
	Xruncom "zero foot size without -p"  "ff -F 0 ff.in"
	Xruncom "20 foot size"               'ff -F 20 -f "Howdy $NAME" ff.in | wc'
	Xruncom "19 foot size"               'ff -F 19 -f "Howdy $NAME" ff.in | wc'
	X
	Xnewtest "FOOTER FOOTER FOOTER FOOTER FOOTER FOOTER FOOTER"
	Xruncom "nice threepart"             'ff -f "@`date`@$NAME at File * Page %@" ff.in'
	X
	Xnewtest "DELETE DELETE DELETE DELETE DELETE DELETE DELETE"
	Xruncom "delete blank lines - not really blank" "ff -D ff.in"
	Xruncom "delete blank chars - breaking all lines" "ff -bd ff.in | diff ff.in -"
	Xruncom "delete chars then lines"    "ff -Dd ff.in"
	X
	Xnewtest "BREAKCHARS BREAKCHARS BREAKCHARS BREAKCHARS"
	Xruncom "null break chars"            'ff -B "" ff.in'
	Xruncom "break on T"                  "ff -B T ff.in"
	X
	Xnewtest "PAGELENGTH PAGELENGTH PAGELENGTH PAGELENGTH PAGELENGTH"
	Xruncom "0dd page length of 19 should imply -p option" "ff -P 19 ff.in | wc"
	Xruncom "ridiculously small page length" "ff -P 1 ff.in | wc"
	X
	Xnewtest "PAGINATE PAGINATE PAGINATE PAGINATE PAGINATE PAGINATE"
	Xruncom "paginate"                    "ff -p ff.in | wc"
	X
	Xnewtest "BREAK BREAK BREAK BREAK BREAK BREAK BREAK BREAK"
	Xruncom "break lines + check with diff" "ff -b ff.in | diff - ff.in"
	X
	Xnewtest "CENTER CENTER CENTER CENTER CENTER CENTER CENTER "
	Xruncom "center all lines"            "ff -c ff.in"
	Xruncom "center on lines 50 wide"     "ff -c -w 50 ff.in"
	Xruncom "center on lines 1 wide"      "ff -c -w 1 ff.in"
	X
	Xnewtest "BASIC BASIC BASIC BASIC BASIC BASIC BASIC BASIC "
	Xruncom "no options from stdin"       "ff < ff.in"
	Xruncom "no options from stdin -"     "ff - < ff.in"
	Xruncom "no options from ff.in"       "ff ff.in"
	Xruncom "three times: ff.in - ff.in"  "ff ff.in - ff.in < ff.in"
	Xruncom "usage menu"                  "ff -U"
	X
	Xnewtest "BAD OPTIONS BAD OPTIONS BAD OPTIONS BAD OPTIONS"
	Xruncom "bad option"                  "ff -z"
	Xruncom "bad file"                    "ff howdy pal"
	Xruncom "too many calls to stdin"     "ff - - - < ff.in"
	X
	Xnewtest "PARSER PARSER PARSER PARSER PARSER PARSER PARSER PARSER"
	X
	Xnewtest "CHECK OPTIONS CHECK OPTIONS CHECK OPTIONS CHECK OPTIONS"
	Xruncom "center iff no justify"       "ff -cj ff.in"
	Xruncom "break lines iff no justify"  "ff -bj ff.in"
	Xruncom "justify iff not center"          "ff -cj"
	Xruncom "justify iff not breaklines"      "ff -bj"
	Xruncom "justify iff not tabs"            "ff -t 5 -j"
	X
	Xnewtest "NUMBER OPTIONS NUMBER OPTIONS NUMBER OPTIONS NUMBER OPTIONS"
	Xfor numopt in P w T t s N i I H F P
	Xdo
	X	runcom "Missing Value"               "ff -$numopt < ff.in"
	X	runcom "Bad Type Value"              "ff -$numopt foo < ff.in"
	X	runcom "Zero Integer"                "ff -$numopt 0 < ff.in"
	X	runcom "Negative Integer"            "ff -$numopt -1 < ff.in"
	Xdone
	X
	Xnewtest "MISSING ARGS MISSING ARGS MISSING ARGS MISSING ARGS"
	Xfor argopt in h f B
	Xdo
	X	runcom "Missing Value"               "ff -$argopt < ff.in"
	Xdone
SHAR_EOF
if test 7703 -ne "`wc -c < 'ff.test'`"
then
	echo shar: "error transmitting 'ff.test'" '(should have been 7703 characters)'
fi
chmod +x 'ff.test'
fi
echo shar: "extracting 'makefile'" '(1056 characters)'
if test -f 'makefile'
then
	echo shar: "will not over-write existing file 'makefile'"
else
sed 's/^	X//' << \SHAR_EOF > 'makefile'
	XMAIN=ff
	XSRCS=number.c filter.c getopt.c
	XOBJS=number.o filter.o getopt.o
	XDOCS=ff.1 ff.test
	XLIBS=
	XDESTDIR=.
	XCFLAGS=-O
	XTEXT=$(HDRS) $(SRCS)
	X
	XLINT  =/usr/bin/lint -hp
	XPR    =cpr
	XSPELL =sp
	XSHAR  =shar -a
	XRCS   =ci -l
	XCC    =/bin/cc
	X
	X$(MAIN): $(MAIN).o $(OBJS)
	X	$(CC) $(CFLAGS) -o $@ $@.o $(OBJS)
	X
	Xinstall: $(MAIN)
	X	cp -i $(MAIN) $(DESTDIR)/$(MAIN)
	X
	Xprint:
	X	@$(PR) $(MAIN).c
	X
	Xlint:
	X	$(LINT) $(TEXT) $(MAIN).c
	X
	Xspell:
	X	seec -cqe $(MAIN).c | $(SPELL)
	X
	Xtest:
	X	$(MAIN).test
	X
	Xarchive: $(DOCS) [Mm]akefile $(TEXT) $(MAIN).c
	X	@$(SHAR) $(DOCS) [Mm]akefile $(TEXT) > archive.1
	X	@$(SHAR) $(MAIN).c > archive.2
	X
	Xclean:
	X	rm -f *.o core a.out mon.out gmon.out scmon.out
	X
	Xgprof:
	X	make CFLAGS="$(CFLAGS) -pg"
	Xscprof:
	X	make CFLAGS="$(CFLAGS) -p" CC=sc
	X
	Xxref: cscope.out
	X	ccall -dr > xref.r
	X	ccall -a > xref.a
	X	touch xref
	Xcscope.out: $(MAIN).c
	X	cscope $(MAIN).c
	X
	Xstyle: style.out
	Xstyle.out:
	X	cstyle $(MAIN).c > style.out
	X
	Xrcs: RCS
	X	$(RCS) $(TEXT) $(MAIN).c
	XRCS: $(TEXT) $(MAIN).c
	X
	X$(MAIN).1: $(MAIN).c
	X	@seec -t MANUAL $(MAIN).c > $(MAIN).1
	X
	X.PRECIOUS: $(TEXT) $(DOCS) $(MAIN).c
SHAR_EOF
if test 1056 -ne "`wc -c < 'makefile'`"
then
	echo shar: "error transmitting 'makefile'" '(should have been 1056 characters)'
fi
fi
echo shar: "extracting 'number.c'" '(4039 characters)'
if test -f 'number.c'
then
	echo shar: "will not over-write existing file 'number.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'number.c'
	X/* Copyright (c) 1982, 1985 Gary Perlman */
	X/* Copies can be made if not for material gain */
	X
	X/*
	X	number: report if a string is a UNIX formatted number
	X
	X	notes:
	X		a number in UNIX is one that can be converted from
	X		a string to an integer or real with no loss of information
	X			due to bad format
	X		all numbers can be surrounded by whitespace
	X		an integer has an optional minus sign, followed by digits
	X		a real number has an optional minus sign followed by digits
	X		if a string has a decimal point, followed by zeros, it is real, not int
	X
	X	value:
	X		1 is string is an integer [-] 0-9+
	X		2 is string is a real number (as seen by atof)
	X		0 for non-numbers
	X
	X	compilation flags:
	X		-DSTANDALONE	includes test main program
	X		$Compile: cc -DSTANDALONE -O -o %F %f
	X	
	X	deficiencies:
	X		does not check to see if significant digits will be ignored
	X	
	X	author:
	X		Gary Perlman
	X
	X	date:
	X		Wed May 22 13:30:40 EDT 1985
	X		Sun Sep  1 14:53:51 EDT 1985 (modified test module)
	X		
	X*/
	X#include <ctype.h>
	X
	X#ifndef lint
	Xstatic char sccsfid[] = "@(#) number.c 5.2 (unix|stat) 9/1/85";
	X#endif
	X
	X#define	IS_NOT      0            /* not a number */
	X#define	IS_INT      1            /* an integer */
	X#define	IS_REAL     2            /* a real number */
	X
	X#define	EOS         '\0'
	X
	X/*LINTLIBRARY*/
	X
	Xnumber (string)
	Xchar	*string;                 /* the string to be tested */
	X	{
	X	int 	answer = IS_INT;     /* start by assuming it is an integer */
	X	int 	before = 0;          /* anything before the decimal? */
	X	int 	after = 0;           /* anything after the decimal? */
	X	while (isspace (*string))    /* skip over blank space */
	X		string++;
	X	if (*string == EOS)          /* empty string not allowed */
	X		return (IS_NOT);
	X	if (*string == '+' || *string == '-') /* old atoi didn't allow '+' */
	X		{
	X		string++;
	X		if (!isdigit (*string) && *string != '.')
	X			return (IS_NOT);
	X		}
	X	if (isdigit (*string))       /* note that there was a digit before . */
	X		{
	X		before = 1;
	X		while (isdigit (*string))
	X			string++;
	X		}
	X	if (*string == '.')          /* found a decimal point, parse for real */
	X		{
	X		answer = IS_REAL;
	X		string++;
	X		if (isdigit (*string))   /* note that there was a digit after . */
	X			{
	X			after = 1;
	X			while (isdigit (*string))
	X				string++;
	X			}
	X		}
	X	if (!before && !after)       /* must be digit somewhere */
	X		return (IS_NOT);
	X	if (*string == 'E' || *string == 'e') /* exponent */
	X		{
	X		answer = IS_REAL;
	X		string++;
	X		if (*string == '+' || *string == '-') /* optional sign */
	X			string++;
	X		if (!isdigit (*string))  /* missing exponent */
	X			return (IS_NOT);
	X		while (isdigit (*string))
	X			string++;
	X		}
	X	while (isspace (*string))    /* skip optional spaces */
	X		string++;
	X	/* should now have exhausted the input string */
	X	return (*string == EOS ? answer : IS_NOT);
	X	}
	X
	X#ifdef STANDALONE
	X
	X#include <stdio.h>
	X/*
	X	exits with status = the number of args not numerical
	X	Shell Example:
	X		if number -i $*
	X		then
	X			echo processing $*
	X		else
	X			echo $0: arguments must be integers 
	X		fi
	X	Options:
	X		-i  arguments must be integer
	X		-n  arguments must be non-negative
	X*/
	Xint 	NoNegative;   /* do the values have to be non-negative? */
	Xint 	Integer;      /* do the values have to be integers? */
	X
	Xstatic
	Xint
	Xinitial (argc, argv) char **argv;
	X	{
	X	extern	char	*optarg;
	X	extern	int 	optind;
	X	int 	errflg = 0;
	X	int 	C;
	X	char	*optstring = "in";
	X	char	*usage = "[-in] string ...";
	X	while ((C = getopt (argc, argv, optstring)) != EOF)
	X		switch (C)
	X			{
	X			case 'i': Integer = 1; break;
	X			case 'n': NoNegative = 1; break;
	X			default: errflg++; break;
	X			}
	X	if (errflg)
	X		{
	X		fprintf (stderr, "Usage: %s %s\n", argv[0], usage);
	X		exit (1);
	X		}
	X	return (optind);
	X	}
	X
	Xmain (argc, argv) char **argv;
	X	{
	X	int 	status = 0;
	X	int 	arg = initial (argc, argv);
	X	char	*string;
	X	while (arg < argc)
	X		{
	X		string = argv[arg++];
	X		if (NoNegative && *string == '-') status++;
	X		else switch (number (string))
	X			{
	X			case IS_NOT:  status++; break;
	X			case IS_REAL: if (Integer) status++; break;
	X			case IS_INT:  break;
	X			default: /* CAN'T HAPPEN */ break;
	X			}
	X		}
	X	exit (status);
	X	}
	X
	X#endif
SHAR_EOF
if test 4039 -ne "`wc -c < 'number.c'`"
then
	echo shar: "error transmitting 'number.c'" '(should have been 4039 characters)'
fi
fi
echo shar: "extracting 'filter.c'" '(4306 characters)'
if test -f 'filter.c'
then
	echo shar: "will not over-write existing file 'filter.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'filter.c'
	X/*
	X	Function:    filter "Filter Command Line Files In Classic UNIX Style"
	X	Created:     Sat Aug 10 21:57:12 EDT 1985
	X	By:          Gary Perlman (Wang Institute, Tyngsboro, MA 01879 USA)
	X	Compilation: nothing unusual
	X	Tester:      $Compile: cc -DSTANDALONE -o filter %f
	X	Preconditions:
	X		The index of the first file operand has been determined.
	X	Postconditions:
	X		All files have been opened, processed, and closed.
	X	Returns:
	X		The return status (non-zero is bad) depends on the accessibility
	X		of files, the ability to open them, and the return statuses of
	X		the called function.
	X	Exceptions:
	X		If any file cannot be accessed, then none will be processed.
	X		During processing, if something goes wrong (a file that could
	X		be accessed cannot be opened, or the file processor returns a
	X		non-zero status), processing continues.
	X	Notes:
	X		"-" is the conventional name for the standard input.
	X			It can only be read once.
	X		Fputs and putc are used to print error messages to avoid
	X			loading fat fprintf just because filter used it.
	X*/
	X
	X
	X#include <stdio.h>
	X
	X#ifdef STANDALONE
	X
	Xint
	Xcat (file, ioptr)
	Xchar	*file;
	Xregister	FILE	*ioptr;
	X	{
	X	register	int 	C;
	X	while ((C = getc (ioptr)) != EOF)
	X		putchar (C);
	X	return (0);
	X	}
	X
	Xmain (argc, argv) char **argv;
	X	{
	X	int 	cat ();
	X
	X	if (filter (argc, argv, 1, cat))
	X		{
	X		putc ('\007', stderr); /* UNIX friendly error message */
	X		exit (1);
	X		}
	X	exit (0);
	X	}
	X
	X#endif STANDALONE
	X
	X
	X/* LINTLIBRARY */
	Xstatic
	Xvoid
	Xerrmsg (pgm, file, errorno, dflt)
	Xchar	*pgm;       /* name of the program running */
	Xchar	*file;      /* file operand to be mentioned (if any) */
	Xint 	errorno;    /* system errno or some bad value */
	Xchar	*dflt;      /* default message for bad error numbers */
	X	{
	X	extern	char *sys_errlist[];  /* list of error messages */
	X	extern	int sys_nerr;         /* number of error messages */
	X
	X	fputs (pgm, stderr);
	X	putc (':', stderr);
	X	putc (' ', stderr);
	X	if (errorno > 0 && errorno < sys_nerr)
	X		fputs (sys_errlist[errorno], stderr);
	X	else
	X		fputs (dflt, stderr);
	X	if (file)
	X		{
	X		putc (' ', stderr);
	X		putc ('\'', stderr);
	X		fputs (file, stderr);
	X		putc ('\'', stderr);
	X		}
	X	putc ('\n', stderr);
	X	}
	X
	X
	X#define	isstdin(file) (file[0] == '-' && file[1] == '\0')
	X
	Xint
	Xfilter (argc, argv, curarg, process)
	Xint 	argc;          /* real number of command line args */
	Xchar	**argv;        /* command line argument pointer */
	Xint 	curarg;        /* first argv to filter */
	Xint 	(*process) (); /* status process (char *name, FILE *ioptr) */
	X	{
	X	int 	status = 0;         /* return status of this function */
	X	int 	arg;                /* loop index variable */
	X	char	*file;              /* name of the current file */
	X	char	*pgm = argv[0];     /* name of the program */
	X	FILE	*ioptr;             /* file pointer for opening */
	X	int 	countstdin = 0;     /* number of times stdin is processed */
	X	extern	int errno;          /* system error number */
	X
	X	if (curarg == argc)
	X		status += ((*process) ("-", stdin));
	X	else
	X		{
	X		/* first check to make sure all files can be opened to read */
	X		for (arg = curarg; arg < argc; arg++)
	X			{
	X			file = argv[arg];
	X			if (isstdin (file))
	X				countstdin++;
	X			else if (access (file, 4))
	X				{
	X				errmsg (pgm, file, errno, "Can't access file");
	X				status++;
	X				}
	X			}
	X		if (countstdin > 1)
	X			{
	X			errmsg (pgm, NULL, -1, "Can only read standard input once");
	X			status++;
	X			}
	X		if (status == 0)
	X			for (arg = curarg; arg < argc; arg++)
	X				{
	X				file = argv[arg];
	X				if (isstdin (file))
	X					status += ((*process) (file, stdin) != 0);
	X				else if (ioptr = fopen (file, "r"))
	X					{
	X					status += ((*process) (file, ioptr) != 0);
	X					(void) fclose (ioptr);
	X					}
	X				else
	X					{
	X					errmsg (pgm, file, errno, "Can't open file");
	X					status++;
	X					}
	X				}
	X		}
	X	return (status);
	X	}
	X
	X/*NOTES
	X	Some modifications might be useful but unpopular:
	X		If there is piped input (!isatty (fileno (stdin))),
	X		and the standard input is not read,
	X		then some information may be ignored,
	X		so a warning should be printed.
	X		Unfortunately, this would break things like vi filters.
	X
	X		If there is not piped input,
	X		and the standard input is being read from the keyboard,
	X		then prompt the user for input with something like:
	X			pgm: reading input from terminal
	X		This would avoid the problem of people forgetting to supply
	X		an input redirection.
	X*/
SHAR_EOF
if test 4306 -ne "`wc -c < 'filter.c'`"
then
	echo shar: "error transmitting 'filter.c'" '(should have been 4306 characters)'
fi
fi
echo shar: "extracting 'getopt.c'" '(3008 characters)'
if test -f 'getopt.c'
then
	echo shar: "will not over-write existing file 'getopt.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'getopt.c'
	X/*
	X	I got this off net.sources from Henry Spencer.
	X	It is a public domain getopt(3) like in System V.
	X	I have made the following modifications:
	X
	X	index(s,c) was added because too many people could
	X	not compile getopt without it.
	X
	X	A test main program was added, ifdeffed by STANDALONE.
	X	This main program is a public domain implementation
	X	of the getopt(1) program like in System V.  The getopt
	X	program can be used to standardize shell option handling.
	X		e.g.  cc -DSTANDALONE getopt.c -o getopt
	X*/
	X#include <stdio.h>
	X
	X#ifndef lint
	Xstatic	char	sccsfid[] = "@(#) getopt.c 5.0 (UTZoo) 1985";
	X#endif
	X
	X#define	ARGCH    (int)':'
	X#define BADCH	 (int)'?'
	X#define EMSG	 ""
	X#define	ENDARGS  "--"
	X
	X/* this is included because index is not on some UNIX systems */
	Xstatic
	Xchar *
	Xindex (s, c)
	Xregister	char	*s;
	Xregister	int 	c;
	X	{
	X	while (*s)
	X		if (c == *s) return (s);
	X		else s++;
	X	return (NULL);
	X	}
	X
	X/*
	X * get option letter from argument vector
	X */
	Xint	opterr = 1,		/* useless, never set or used */
	X	optind = 1,		/* index into parent argv vector */
	X	optopt;			/* character checked for validity */
	Xchar	*optarg;		/* argument associated with option */
	X
	X#define tell(s)	fputs(*nargv,stderr);fputs(s,stderr); \
	X		fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
	X
	X
	Xgetopt(nargc,nargv,ostr)
	Xint	nargc;
	Xchar	**nargv,
	X	*ostr;
	X{
	X	static char	*place = EMSG;	/* option letter processing */
	X	register char	*oli;		/* option letter list index */
	X	char	*index();
	X
	X	if(!*place) {			/* update scanning pointer */
	X		if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
	X		if (*place == '-') {	/* found "--" */
	X			++optind;
	X			return(EOF);
	X		}
	X	}				/* option letter okay? */
	X	if ((optopt = (int)*place++) == ARGCH || !(oli = index(ostr,optopt))) {
	X		if(!*place) ++optind;
	X		tell(": illegal option -- ");
	X	}
	X	if (*++oli != ARGCH) {		/* don't need argument */
	X		optarg = NULL;
	X		if (!*place) ++optind;
	X	}
	X	else {				/* need an argument */
	X		if (*place) optarg = place;	/* no white space */
	X		else if (nargc <= ++optind) {	/* no arg */
	X			place = EMSG;
	X			tell(": option requires an argument -- ");
	X		}
	X	 	else optarg = nargv[optind];	/* white space */
	X		place = EMSG;
	X		++optind;
	X	}
	X	return(optopt);			/* dump back option letter */
	X}
	X
	X
	X#ifdef STANDALONE
	X
	X#ifndef lint
	Xstatic	char	sccspid[] = "@(#) getopt.c 5.1 (WangInst) 6/15/85";
	X#endif
	X
	Xmain (argc, argv) char **argv;
	X	{
	X	char	*optstring = argv[1];
	X	char	*argv0 = argv[0];
	X	extern	int 	optind;
	X	extern	char	*optarg;
	X	int 	opterr = 0;
	X	int 	C;
	X	char	*opi;
	X	if (argc == 1)
	X		{
	X		fprintf (stderr, "Usage: %s optstring args\n", argv0);
	X		exit (1);
	X		}
	X	argv++;
	X	argc--;
	X	argv[0] = argv0;
	X	while ((C = getopt (argc, argv, optstring)) != EOF)
	X		{
	X		if (C == BADCH) opterr++;
	X		printf ("-%c ", C);
	X		opi = index (optstring, C);
	X		if (opi && opi[1] == ARGCH)
	X			if (optarg)
	X				printf ("\"%s\" ", optarg);
	X			else opterr++;
	X		}
	X	printf ("%s", ENDARGS);
	X	while (optind < argc)
	X		printf (" \"%s\"", argv[optind++]);
	X	putchar ('\n');
	X	exit (opterr);
	X	}
	X
	X#endif
SHAR_EOF
if test 3008 -ne "`wc -c < 'getopt.c'`"
then
	echo shar: "error transmitting 'getopt.c'" '(should have been 3008 characters)'
fi
fi
exit 0
#	End of shell archive



More information about the Mod.sources mailing list