[net.sources] procedure to process files like classic UNIX filter

perlman@wanginst.UUCP (Gary Perlman) (08/28/85)

I have found the following procedure useful for creating
programs that filter files (like cat, sed, and nroff).  It
checks for file accessability, allows for - as the standard
input, and prints appropriate error messages before
returning a success status.  The only trick (if you can call
it that) involved is to pass it a function of the form:
	process (file, ioptr)
that itself returns a success status.  The procedure
cooperates nicely with getopt, although getopt's use is not
required. 

#! /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:
#	filter.3
#	filter.c
# This archive created: Tue Aug 27 19:45:55 1985
# By:	Gary Perlman (Wang Institute, Tyngsboro, MA 01879 USA)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'filter.3'" '(2479 characters)'
if test -f 'filter.3'
then
	echo shar: "will not over-write existing file 'filter.3'"
else
sed 's/^	X//' << \SHAR_EOF > 'filter.3'
	X.TH GETOPT 3WI "August 12, 1985" "Wang Institute" "UNIX Programmer's Manual"
	X.SH NAME
	Xfilter \- filter program operand files in classic UNIX style
	X.SH SYNOPSIS
	X.ft B
	X.nf
	X.ta .5i 2i
	X#include <stdio.h>
	X ...
	Xint 	filter (argc, argv, optind, func)
	Xint 	argc;	/* command line argument count */
	Xchar	**argv;	/* command line argument strings */
	Xint 	optind;	/* index of first command line operand */
	Xint 	(*func) ();	/* processing function */
	X ...
	Xint	func (file, ioptr)
	Xchar	*file;	/* name of file to be read */
	XFILE	*ioptr;	/* opened file pointer */
	X.fi
	X.ft
	X.SH DESCRIPTION
	X.I filter
	Xsimplies writing C programs that read all their command
	Xline operands (those after options) as files.
	XIn the classic style of programs like
	X.I cat,
	X.I nroff,
	Xor
	X.I sed,
	X.I filter
	Xtakes each of its operand files, opens it,
	Xand passes the name and opened
	X.I ioptr
	Xto the programmer-suppled
	X.I func
	Xthat is free to do what it likes with the file.
	XAfter each time
	X.I func
	Xreturns, the
	X.I ioptr
	Xis closed (except for the standard input).
	XAs is conventional,
	Xa single minus sign,
	X.B -,
	Xis the standard input.
	XIf no operands are passed to filter,
	Xie,
	X.I argc==optind,
	Xthen the standard input is read.
	X.SH DIAGNOSTICS
	X.I filter
	Xprints error messages on
	X.I stderr
	Xand returns non-zero status on failure.
	XIf a file cannot be opened, or if the standard input
	Xis read more than once,
	Xthen no files are filtered.
	XIf the processing
	X.I func
	Xfails, processing continues,
	Xbut the return status will be the sum of the
	Xbad return statuses from
	X.I func.
	X.SH EXAMPLE
	XThe following example shows how
	X.I filter
	Xcan be used with an initializing routine that returns
	Xthe
	X.I optind
	Xvalue from
	X.I getopt.
	X(It is not necessary that
	X.I getopt
	Xbe used.)
	X.nf
	X.ta .5i +.5i +.5i +.5i +.5i +.5i +.5i
	X.sp
	X.ne 15v
	Xmain (argc, argv) char **argv;
	X	{
	X	int 	process ();
	X	int 	C;
	X	int 	status = 0;
	X	while ((C = getopt (argc, argv, "ax:")) != EOF)
	X		switch (C)
	X		{
	X		case 'a': Aopt = 1; break;
	X		case 'x': Xopt = optarg; break;
	X		default: status++; break;
	X		}
	X	status += (filter (argc, argv, optind, process) != 0);
	X	exit (status);
	X	}
	X.sp
	X.fi
	X.PP
	XWith the above processing filter,
	Xthe following documentation should be included in the
	Xmanual entry:
	X.nf
	X.sp
	X.ta .5i
	X.ne 5v
	X	.I process
	X	reads its input from the named files,
	X	or if none are supplied, the standard input is read.
	X	A solitary minus sign as a program argument can be
	X	used to read the standard input along with named files.
	X.sp
	X.fi
	X.SH SEE ALSO
	Xgetopt (3)
	X.SH AUTHOR
	XGary Perlman
SHAR_EOF
if test 2479 -ne "`wc -c < 'filter.3'`"
then
	echo shar: "error transmitting 'filter.3'" '(should have been 2479 characters)'
fi
fi
echo shar: "extracting 'filter.c'" '(3261 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 catfilter %f
	X	---------------------------------------------------------------
	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	Global Data Used:
	X		None
	X	Global Data Modified:
	X		None
	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 just because filter used it.
	X*/
	X
	X#include <stdio.h>
	X#define	isstdin(file) (file[0] == '-' && file[1] == '\0')
	X/* LINTLIBRARY */
	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	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				fputs (pgm, stderr);
	X				fputs (": Can't read '", stderr);
	X				fputs (file, stderr);
	X				putc ('\'', stderr);
	X				putc ('\n', stderr);
	X				status++;
	X				}
	X			}
	X		if (countstdin > 1)
	X			{
	X			fputs (pgm, stderr);
	X			fputs (": Can only read standard input once\n", stderr);
	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					fputs (pgm, stderr);
	X					fputs (": Can't open '", stderr);
	X					fputs (file, stderr);
	X					putc ('\'', stderr);
	X					putc ('\n', stderr);
	X					status++;
	X					}
	X				}
	X		}
	X	return (status);
	X	}
	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	if (filter (argc, argv, 1, cat))
	X		putc ('\007', stderr); /* UNIX friendly error message */
	X	}
	X
	X#endif STANDALONE
SHAR_EOF
if test 3261 -ne "`wc -c < 'filter.c'`"
then
	echo shar: "error transmitting 'filter.c'" '(should have been 3261 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
Gary Perlman  Wang Institute  Tyngsboro, MA 01879  (617) 649-9731
UUCP: decvax!wanginst!perlman             CSNET: perlman@wanginst