[net.text] punc: a program to print sentence punctuation graphs

perlman@wanginst.UUCP (Gary Perlman) (10/17/85)

Here is the second program mentioned in my review of WWB
(the Writer's Workbench (TM AT&T Technologies).  For most
people interested in text analysis, this is a novel program.
The idea came out of my interaction with Tom Erickson while
we were at UCSD.  He has graciously agreed to allow the public
release of the program.  I would like to get feedback on the
use of this program, and I would like to hear about novel uses.

Gary Perlman/Wang Institute/Tyngsboro, MA/01879/(617) 649-9731

#! /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:
#	punc.1
#	makefile
#	punc.c
#	getopt.c
#	filter.c
#	punc.test
# This archive created: Thu Oct 17 02:28:59 1985
# By:	Gary Perlman (Wang Institute, Tyngsboro, MA 01879 USA)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'punc.1'" '(1960 characters)'
if test -f 'punc.1'
then
	echo shar: "will not over-write existing file 'punc.1'"
else
sed 's/^	X//' << \SHAR_EOF > 'punc.1'
	X.TH PUNC 1 "October 1985" "UCSD / Wang Institute" "UNIX User's Manual"
	X.SH NAME
	Xpunc \- graphically display sentences using their punctuation
	X.SH SYNOPSIS
	X.B punc
	X[-lmpsw] [-c criterion] [-] [files]
	X.SH DESCRIPTION
	X.I punc
	Xprints graphical representations of sentences.
	XThis graphical representation has the properties that
	Xthe representation is long when the sentence is long,
	Xand the representation looks complex when the sentence is complex.
	XThe program works by displaying text,
	Xone sentence per line,
	Xwith embedded punctuation retained,
	Xand underscores substituted for words.
	XFor example, the previous two sentences of this \fIman\fR entry look like:
	X.nf
	X	________________,__________.
	X	______,____,____,_____.
	X.fi
	X.SH OPTIONS
	X.de OP
	X.TP
	X.B -\\$1 \\$2
	X..
	X.OP c length
	XPrint only those sentences with "punc" lengths greater than the criterion.
	X.OP l
	XPrint the line numbers of the text where the sentences begin.
	X.OP m
	XMap words to different classes represented by characters.
	XUpper case word are shown as the ^ character.
	X.nf
	X&	conjunctions (and, but, ...)
	X|	disjunctions (or)
	X#	numbers (first, one, ...)
	X~	negations (not, never, ...)
	X"	pronouns (he, she, ...)
	Xw	who, what, where, when, why, ...
	Xt	a, the, that, those, ...
	X.fi
	XThis set of words is incomplete.
	X.OP p
	XPrint the sentences after the graphical representation.
	X.OP s
	XPrint the sentence numbers before the graphical representation.
	X.OP w
	XPrint the length of words instead of underscores for words.
	XWords longer than 10 characters are printed as *,
	Xand ten character words are printed as 0.
	X.SH SEE\ ALSO
	Xheadings(1) for a high-level representation of a paper.
	X.SH AUTHORS
	XTom Erickson and Gary Perlman
	X.SH BUGS
	XThe way the program identifies the end of a sentence is too simple
	Xand it can be fooled badly.
	XSentences must end at the end of lines.
	X
	X\fINroff\fR macros are not handled intelligently by the program;
	X\fIderoff\fR does a better but not perfect job
	Xand should be used as a preprocessor.
SHAR_EOF
if test 1960 -ne "`wc -c < 'punc.1'`"
then
	echo shar: "error transmitting 'punc.1'" '(should have been 1960 characters)'
fi
fi
echo shar: "extracting 'makefile'" '(942 characters)'
if test -f 'makefile'
then
	echo shar: "will not over-write existing file 'makefile'"
else
sed 's/^	X//' << \SHAR_EOF > 'makefile'
	XMAIN=punc
	XHDRS=
	XSRCS=getopt.c filter.c
	XOBJS=getopt.o filter.o
	XDOCS=punc.1
	XLIBS=
	XDESTDIR=.
	XCFLAGS=-O
	XTEXT=$(HDRS) $(MAIN).c $(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 $(MAIN) $(MAIN).o $(OBJS)
	X
	Xinstall: $(MAIN)
	X	cp -i $(MAIN) $(DESTDIR)/$(MAIN)
	X
	Xprint:
	X	@$(PR) $(MAIN).c
	X
	Xlint:
	X	$(LINT) $(MAIN).c
	X
	Xspell:
	X	seec -cqe $(MAIN).c | $(SPELL)
	X
	Xtest:
	X	$(MAIN).test
	X
	Xarchive:
	X	@$(SHAR) $(DOCS) [Mm]akefile $(TEXT) $(MAIN).test
	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
	XRCS: $(TEXT)
	X	$(RCS) $(TEXT)
	X
	X$(MAIN).1: $(MAIN).c
	X	@seec -t MANUAL $(MAIN).c
	X
	X.PRECIOUS: $(TEXT) $(DOCS)
SHAR_EOF
if test 942 -ne "`wc -c < 'makefile'`"
then
	echo shar: "error transmitting 'makefile'" '(should have been 942 characters)'
fi
fi
echo shar: "extracting 'punc.c'" '(7130 characters)'
if test -f 'punc.c'
then
	echo shar: "will not over-write existing file 'punc.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'punc.c'
	X/*
	X	punc: print punctuation graphs for sentences
	X	by Gary Perlman & Tom Erickson
	X	A punctuation graph places a sentence on one line
	X	with words replaced by underscores and punctuation maintained verbatim.
	X
	X	A sentence must end at the end of input lines.
	X	Lines beginning with . are ignored (nroff control lines).
	X*/
	X#include <stdio.h>
	X#include <ctype.h>
	X
	Xtypedef	int       Status;      /* return/exit status of functions */
	X#define	SUCCESS   ((Status) 0)
	X#define	FAILURE   ((Status) 1)
	Xtypedef	int       Boole;       /* no Boolean type in C */
	X#define	TRUE      ((Boole) 1)
	X#define	FALSE     ((Boole) 0)
	X
	X/* macros to identify the end of a sentence */
	X#define	Maybefinisher(c) ((c)=='\'' || (c)==')' || (c)=='"')
	X#define	Finisher(c) ((c)=='!' || (c)=='?' || (c)=='.')
	X
	X#define	WORDCHAR '_'  /* display words like this */
	X#define LONGWORD '*'  /* display long words like this */
	X#define	CAPCHAR  '^'  /* display capitalized words like this */
	X
	X#define ANDCHAR  '&'  /* display conjunctions like this */
	X#define ORCHAR   '|'  /* display disjunctions like this */
	X#define	NUMCHAR  '#'  /* display numbers like this */
	X#define	NOTCHAR  '~'  /* display negations like this */
	X#define	PRONOUN  '"'  /* display pronouns like this */
	X#define WHCHAR   'w'  /* who, what where when why, ... */
	X#define THCHAR   't'  /* the, that, those, ... */
	X
	Xchar	*Andwords[] = {"and", "but", NULL};
	Xchar	*Orwords[] = {"or", "nor", NULL};
	Xchar	*Notwords[] = {"no", "not", "never", NULL};
	Xchar	*Numwords[] = {"one", "two", "three", "four", "five",
	X						"six", "seven", "eight", "nine", "ten",
	X						"eleven", "twelve", "thirteen", "fourteen",
	X						"fifteen", "sixteen", "seventeen", "eighteen",
	X						"nineteen", "twenty", "thirty", "forty",
	X						"fifty", "sixty", "seventy", "eighty", "ninety",
	X						"hundred", "thousand", "million", "billion",
	X						"trillion", "zero", NULL};
	Xchar	*Whwords[] = {"who", "what", "where", "when", "why", "which", "whence",
	X						"how", "while", NULL};
	Xchar	*Thwords[] = {"the", "these", "those", "a", "that", NULL};
	Xchar	*Pronouns[] = {"he", "she", "they", "them", "i", "me", "my",
	X						"his", "her", "their", "myself", "themselves", NULL};
	X
	Xint 	Linecount;          /* number of lines read */
	Xint 	Sentcount;          /* number of sentences read */
	XBoole	Countsent = FALSE;  /* should sentence numbers be printed? */
	XBoole	Countline = FALSE;  /* should sentence line numbers be printed? */
	XBoole	Printsent = FALSE;  /* should sentences be printed? */
	XBoole	Printlength = FALSE;/* should words be printed as lengths? */
	Xint 	Criterion = 0;      /* only print punc lines longer than this */
	Xchar	*Argv0;             /* program name */
	XBoole	Mapwords = FALSE;   /* should words be mapped to class type? */
	X
	X/* horribly simple, and not terribly robust sentence reader */
	Xchar *
	Xgetsentence (ioptr)
	XFILE	*ioptr;
	X	{
	X	static	char	sentence[BUFSIZ];
	X	char	line[BUFSIZ];
	X	char	*ptr;
	X	int 	len;
	X	int 	sentlen;
	X
	X	*sentence = '\0';
	X	sentlen = 0;
	X
	X	for (;;)
	X		{
	X		if (fgets (ptr = line, BUFSIZ, ioptr) == NULL)
	X			return (NULL);
	X		Linecount++;
	X		if (*line == '.')
	X			continue;
	X		len = strlen (line);
	X		while (isspace (*ptr))
	X			ptr++;
	X		if (*ptr == '\0')
	X			continue;
	X		if (sentlen + len >= BUFSIZ)
	X			{
	X			fprintf (stderr, "punc: sentence too long near line %d\n", Linecount);
	X			return (NULL);
	X			}
	X		strcat (sentence, line);
	X		sentlen += len;
	X		if (Finisher (line[len-2]))
	X			break;
	X		if (Maybefinisher (line[len-2]) && Finisher (line[len-3]))
	X			break;
	X		}
	X	return (sentence);
	X	}
	X
	X
	Xchar *
	Xpuncsentence (sent)
	Xchar	*sent;
	X	{
	X	static	char	sbuf[BUFSIZ];
	X	char	*sptr;
	X
	X	if (sent == NULL || *sent == '\0')
	X		return (NULL);
	X	sptr = sbuf;
	X
	X	while (*sent)
	X		{
	X		while (isspace (*sent))
	X			sent++;
	X		if (*sent == '\0')
	X			break;
	X		if (isalnum (*sent)) /* copy word into buffer */
	X			{
	X			char	buf[100], *ptr = sent;
	X			int 	length;
	X			int 	wordchar;
	X			while (isalnum (*sent))
	X				sent++;
	X			length = sent-ptr;
	X			strncpy (buf, ptr, length);
	X			buf[length] = '\0';
	X			wordchar = WORDCHAR;
	X
	X			if (Printlength == TRUE)
	X				{
	X				if (length > 10) /* two digit word length, too long! */
	X					wordchar = LONGWORD;
	X				else if (length == 10)
	X					wordchar = '0';
	X				else wordchar = '0' + length;
	X				}
	X
	X			if (Mapwords == TRUE)
	X				{
	X				int	ucase = lcase (buf);
	X				if (check (buf, Andwords))
	X					*sptr++ = ANDCHAR;
	X				else if (check (buf, Orwords))
	X					*sptr++ = ORCHAR;
	X				else if (check (buf, Pronouns))
	X					*sptr++ = ucase ? (toupper (PRONOUN)) : PRONOUN;
	X				else if (check (buf, Thwords))
	X					*sptr++ = ucase ? (toupper (THCHAR)) : THCHAR;
	X				else if (check (buf, Whwords))
	X					*sptr++ = ucase ? (toupper (WHCHAR)) : WHCHAR;
	X				else if (check (buf, Notwords))
	X					*sptr++ = NOTCHAR;
	X				else if (isnum (buf) || check (buf, Numwords))
	X					*sptr++ = NUMCHAR;
	X				else
	X					*sptr++ = ucase ? CAPCHAR : wordchar;
	X				}
	X			else *sptr++ = wordchar;
	X			}
	X		else if (ispunct (*sent))
	X			*sptr++ = *sent++;
	X		else sent++;
	X		}
	X	*sptr = '\0';
	X	return ((strlen (sbuf) >= Criterion) ? sbuf : NULL);
	X	}
	X
	Xisnum (s)
	Xchar	*s;
	X	{
	X	while (*s)
	X		if (!isdigit (*s++)) return (0);
	X	return (1);
	X	}
	X
	X/* returns 1 if input s is upper case, maps s to lower case */
	Xlcase (s)
	Xchar	*s;
	X	{
	X	char	*ptr = s;
	X	int 	ucase = isupper (*s);
	X	while (*ptr)
	X		{
	X		if (isupper (*ptr))
	X			*ptr = tolower (*ptr);
	X		ptr++;
	X		}
	X	return (ucase);
	X	}
	X
	Xcheck (target, list)
	Xchar	*target;
	Xchar	**list;
	X	{
	X	while (*list)
	X		{
	X		if ((target[0] == list[0][0]) && !strcmp (&target[1], &list[0][1]))
	X			return (1);
	X		list++;
	X		}
	X	return (0);
	X	}
	X
	Xinitial (argc,argv)
	Xchar	**argv;
	X	{
	X	int 	C;
	X	int 	errflg = 0;
	X	extern	int optind;
	X	extern	char *optarg;
	X	char	*optstring = "lmpswc:";
	X	char	*usage = "[-lmpsw] [-c criterion] [-] [files]";
	X	Argv0 = argv[0];
	X	while ((C = getopt (argc, argv, optstring)) != EOF)
	X		switch (C)
	X			{
	X			case 'c': Criterion = atoi (optarg); break;
	X			case 'l': Countline = TRUE; break;
	X			case 'm': Mapwords = TRUE; break;
	X			case 'p': Printsent = TRUE; break;
	X			case 's': Countsent = TRUE; break;
	X			case 'w': Printlength = TRUE; break;
	X			default: errflg++;
	X			}
	X	if (errflg)
	X		{
	X		fprintf (stderr, "Usage: %s %s\n", argv[0], usage);
	X		exit (1);
	X		}
	X	return (optind);
	X	}
	X
	XStatus
	Xpunc (file, ioptr)
	Xchar	*file;
	XFILE	*ioptr;
	X	{
	X	char	*sptr;
	X	char	*pptr;
	X	int 	linecount;
	X
	X	Sentcount = 0;
	X	Linecount = 0;
	X	linecount = 1;
	X
	X	while (sptr = getsentence (ioptr))
	X		{
	X		Sentcount++;
	X		if (pptr = puncsentence (sptr))
	X			{
	X			if (Countsent == TRUE)
	X				printf ("%4d\t", Sentcount);
	X			if (Countline == TRUE)
	X				printf ("%4d\t", linecount);
	X			puts (pptr);
	X			if (Printsent == TRUE)
	X				fputs (sptr, stdout);
	X			}
	X		linecount = Linecount+1;  /* point to start of next sentence */
	X		}
	X	return (SUCCESS);
	X	}
	X
	X/*FUNCTION main: loop through files in classic UNIX filter style */
	Xmain (argc, argv)
	Xint 	argc;     /* argument count */
	Xchar	**argv;   /* argument vector */
	X	{
	X	Status 	punc ();      /* punc (file, ioptr) will filter files */
	X	Status	status;       /* return status of filter () */
	X	int 	firstfile;    /* first file name index returned by initial */
	X	firstfile = initial (argc, argv);
	X	status = filter (argc, argv, firstfile, punc);
	X	exit (status);
	X	}
SHAR_EOF
if test 7130 -ne "`wc -c < 'punc.c'`"
then
	echo shar: "error transmitting 'punc.c'" '(should have been 7130 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
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 'punc.test'" '(1296 characters)'
if test -f 'punc.test'
then
	echo shar: "will not over-write existing file 'punc.test'"
else
sed 's/^	X//' << \SHAR_EOF > 'punc.test'
	X# punc [-lmpsw] [-c criterion] [-] [files]
	X
	Xecho ""
	Xpunc -p << \EOF
	XSpeak roughly to your little VAX,
	Xand boot it when it crashes;
	XIt knows that one cannot relax
	XBecause the paging thrashes!
	X
	X		Wow!  Wow!  Wow!
	X
	XI speak severely to my VAX,
	Xand boot it when it crashes;
	XIn spite of all my favorite hacks
	XMy jobs it always thrashes!
	X
	X		Wow!  Wow!  Wow!
	XEOF
	X
	Xecho ""
	Xpunc -mw << \EOF
	XSpeak roughly to your little VAX,
	Xand boot it when it crashes;
	XIt knows that one cannot relax
	XBecause the paging thrashes!
	X
	X		Wow!  Wow!  Wow!
	X
	XI speak severely to my VAX,
	Xand boot it when it crashes;
	XIn spite of all my favorite hacks
	XMy jobs it always thrashes!
	X
	X		Wow!  Wow!  Wow!
	XEOF
	X
	Xecho ""
	Xpunc -ls << \EOF
	XSpeak roughly to your little VAX,
	Xand boot it when it crashes;
	XIt knows that one cannot relax
	XBecause the paging thrashes!
	X
	X		Wow!  Wow!  Wow!
	X
	XI speak severely to my VAX,
	Xand boot it when it crashes;
	XIn spite of all my favorite hacks
	XMy jobs it always thrashes!
	X
	X		Wow!  Wow!  Wow!
	XEOF
	X
	Xecho ""
	Xpunc -c 20 << \EOF
	XSpeak roughly to your little VAX,
	Xand boot it when it crashes;
	XIt knows that one cannot relax
	XBecause the paging thrashes!
	X
	X		Wow!  Wow!  Wow!
	X
	XI speak severely to my VAX,
	Xand boot it when it crashes;
	XIn spite of all my favorite hacks
	XMy jobs it always thrashes!
	X
	X		Wow!  Wow!  Wow!
	XEOF
SHAR_EOF
if test 1296 -ne "`wc -c < 'punc.test'`"
then
	echo shar: "error transmitting 'punc.test'" '(should have been 1296 characters)'
fi
chmod +x 'punc.test'
fi
exit 0
#	End of shell archive
-- 
Gary Perlman  Wang Institute  Tyngsboro, MA 01879  (617) 649-9731
UUCP: decvax!wanginst!perlman             CSNET: perlman@wanginst