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