v21i002: Apply a filter to a file, safely

Rich Salz rsalz at uunet.uu.net
Wed Feb 7 06:49:41 AEST 1990


Submitted-by: Dan Bernstein <brnstnd at stealth.acf.nyu.edu>
Posting-number: Volume 21, Issue 2
Archive-name: filterfile

[  Everyone probably has one of these in their toolkit, although not
   with such extensive copyright, warranty and version numbers; at
   any rate, it's worth having an archived one.  /r$  ]

filterfile applies a filter to a file, writing the result to another
file.  Various options let filterfile delete the input file, set the times
of the output to the times of the input, or set the protection mode of the
output to that of the input.


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README CHANGES filterfile.man filterfile.c Makefile
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(738 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
Xfilterfile - apply a filter to a file
X
Xfilterfile version 1.201, October 28, 1989.
XCopyright (c) 1989, Daniel J. Bernstein.
XAll rights reserved.
X
XThis distribution packaged October 28, 1989.
X
XFiles:
XCHANGES         Description of changes since first distributed version
XREADME          This document
XMakefile        Installation commands
Xfilterfile.c    The program
Xfilterfile.man  Documentation
X
XEdit the options in Makefile and type make. filterfile will be the
Xexecutable program; filterfile.1 will be the nroff'ed manual.
X
XI don't pretend to know your machine's setup so there's no make install.
X
XRead CHANGES for a list of changes. Type filterfile -C and filterfile -W
Xfor copyright and warranty information, filterfile -H for help.
END_OF_FILE
if test 738 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'CHANGES' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'CHANGES'\"
else
echo shar: Extracting \"'CHANGES'\" \(312 characters\)
sed "s/^X//" >'CHANGES' <<'END_OF_FILE'
XVersion 1.201, 10/28/89.
X
XChanged filefilter to filterfile at one spot in manual (whoops!).
XPut missing \n into an error message.
XPut in new copyright notice and warranty.
X
XVersion 1.2.
X
XMinor changes.
XAdded filenames into most of the errors.
XMade execve into execvp, so filterfile searches paths.
X
XVersion 1.0.
END_OF_FILE
if test 312 -ne `wc -c <'CHANGES'`; then
    echo shar: \"'CHANGES'\" unpacked with wrong size!
fi
# end of 'CHANGES'
fi
if test -f 'filterfile.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'filterfile.man'\"
else
echo shar: Extracting \"'filterfile.man'\" \(4968 characters\)
sed "s/^X//" >'filterfile.man' <<'END_OF_FILE'
X.TH filterfile 1
X.SH NAME
Xfilterfile \- apply a filter to a file
X.SH SYNTAX
Xfilterfile
X[
X\fB\-dDpPtT\fI
X] [
X\fB\-o\fIout
X] [
X\fB\-e\fIext
X] [
X\fB\-ACHUVW\fI
X]
Xfile /path/filter
X[
Xarg ...
X]
X.SH DESCRIPTION
X\fIfilterfile\fB
Xapplies a filter to a file,
Xwriting the result to another file.
XVarious options let
X\fIfilterfile\fB
Xdelete the input file,
Xset the times of the output to the times of the input,
Xor set the protection mode of the output to that of the input.
X.PP
XOptions
X.B ACHUVW
Xprint the authorship notice,
Xcopyright notice,
Xhelp notice,
Xshort usage summary,
Xversion number,
Xand warranty information respectively.
X.PP
X\fIfilterfile\fB
Xhas several flags:
X.TP 12
X.B -d
XDelete the input file after the filter runs successfully.
XThe input file will remain untouched if the filter crashes
Xor exits with an error indication.
X.TP
X.B -D
XDo not delete the input file. This is the default.
X.TP
X.B -p
XSet the protection modes of the output to the protection modes
Xof the input.
X.TP
X.B -P
XLeave the output protection modes alone. This is the default.
X.TP
X.B -t
XSet the access and modification times of the output to those
Xof the input.
X.TP
X.B -T
XLeave the times alone. This is the default.
X.TP
X\fB-o\fIout
XWrite the output on file
X\fIout\fB.
XThis overrides
Xthe 
X.B\-e
Xoption.
X.TP
X\fB-e\fIext
XWrite the output on
X\fIfile.ext\fB.
X.PP
XIf you specify neither
X.B\-o
Xnor
X\fB\-e\fI,
X\fIfilterfile\fB
Xwill look for an environment variable
X.B FILEFILTEREXT
Xgiving an extension.
XIf there is no such variable,
X\fIfilterfile\fB
Xwill give the output the same name as the input,
Xfollowed by a period.
X.PP
XIf the extension begins with a period and the input file name
Xends with that extension already, the extension is deleted from
Xthe file name rather than added to it. If you really do want
Xperiods in the extension, type them as slashes.
X.PP
XThe options and input file name can come in any order,
Xbut the second argument not starting with a hyphen
Xis taken to be the name of the filter.
XAny further arguments are passed on as options to the filter.
X.PP
XYou need not specify a full path name for the filter.
X.SH DIAGNOSTICS
XAltogether too many,
Xthough luckily most are self-explanatory.
XMost of these messages are followed by a more specific explanation
Xof the reason for the error; this list does not contain all the
Xpossible variations.
X.TP
X\fIfatal: cannot create output file name: out of memory\fB
XThere is so little memory available that
X\fIfilterfile\fB
Xcannot even put together the input file name and extension
Xto make the output file name.
X.TP
X\fIfatal: will not proceed: input file has extra links\fB
XYou have asked that the input file be deleted, but it
Xhas more than one link.
X.TP
X\fIwarning: cannot change protections of output file\fB
XThis should never happen,
Xunless the file changes to someone else's ownership.
X.TP
X\fIfatal: cannot execute filter\fB
X\fIfilterfile\fB
Xis unable to execute the filter.
X.TP
X\fIfatal: cannot fork\fB
XYou have probably run out of processes.
X.TP
X\fIfatal: cannot reopen input file\fB
X\fIfilterfile\fB
Xis unable to open the input file at its original location.
X.TP
X\fIfatal: cannot reopen output file\fB
X\fIfilterfile\fB
Xis unable to open the output file at its original location.
X.PP
XThe following errors should all be self-explanatory:
X.TP
X\fIfatal: cannot open input file\fB
X.TP
X\fIfatal: cannot create output file\fB
X.TP
X\fIfatal: will not delete input file: it has been moved\fB
X.TP
X\fIfatal: will not delete input file: it has extra links\fB
X.TP
X\fIfatal: cannot delete input file\fB
X.TP
X\fIwarning: will not set times of output file: it has been moved\fB
X.TP
X\fIwarning: cannot set times of output file\fB
X.PP
XThere are several errors that should never happen:
X.TP
X\fIfatal: cannot stat input file\fB
X.TP
X\fIfatal: cannot stat output file\fB
X.TP
X\fIfatal: cannot prepare input\fB
X.TP
X\fIfatal: cannot prepare output\fB
X.TP
X\fIfatal: can't find filter process\fB
X.TP
X\fIfatal: cannot restat input file\fB
X.TP
X\fIfatal: cannot restat output file\fB
X.SH MACHINES
X\fIfilterfile\fB
Xhas been tested
Xon an Astronautics ZS-2
Xrunning ZSUnix.
X.SH FILES
XNone.
X.SH BUGS
XNone known.
X.SH RESTRICTIONS
X\fIfilterfile\fB
Xtries its best to detect when a file has been moved,
Xso that it won't delete the wrong file or change the times of
Xthe wrong file. It also tries to detect when the input file
Xhas several links, in which case it does not even try to delete
Xthe file. Unfortunately, there is a very short period of time
Xbetween these tests and the actual operations, so
X\fIfilterfile\fB
Xcould enter a (non-looping) race condition and act incorrectly.
XThere is no way to avoid this race with available versions of UNIX.
X.PP
XIn other words, if you move the input file out of the way and give
Xa different file the same name, the moment before
X\fIfilterfile\fB
Xremoves that filename,
Xyou deserve what you get.
X.SH VERSION
Xfilterfile version 1.201, dated October 28, 1989.
X.SH AUTHOR
XCopyright 1989, Daniel J. Bernstein.
X.SH "SEE ALSO"
Xapply(1),
Xxargs(1)
END_OF_FILE
if test 4968 -ne `wc -c <'filterfile.man'`; then
    echo shar: \"'filterfile.man'\" unpacked with wrong size!
fi
# end of 'filterfile.man'
fi
if test -f 'filterfile.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'filterfile.c'\"
else
echo shar: Extracting \"'filterfile.c'\" \(9303 characters\)
sed "s/^X//" >'filterfile.c' <<'END_OF_FILE'
X/*
Xfilterfile.c: Pipe a file through a filter into another file.
X
XThis program is an obnoxious example of how to check return codes.
X*/
X
Xstatic char filterfileauthor[] =
X"filterfile was written by Daniel J. Bernstein.\n\
XInternet address: brnstnd at acf10.nyu.edu.\n";
X
Xstatic char filterfileversion[] = 
X"filterfile version 1.201, October 28, 1989.\n\
XCopyright (c) 1989, Daniel J. Bernstein.\n\
XAll rights reserved.\n";
X
Xstatic char filterfilecopyright[] =
X"filterfile version 1.201, October 28, 1989.\n\
XCopyright (c) 1989, Daniel J. Bernstein.\n\
XAll rights reserved.\n\
X\n\
XYou are granted the following rights: A. To make copies of this work in\n\
Xoriginal form, so long as (1) the copies are exact and complete; (2) the\n\
Xcopies include the copyright notice, this paragraph, and the disclaimer\n\
Xof warranty in their entirety. B. To distribute this work, or copies made\n\
Xunder the provisions above, so long as (1) this is the original work and\n\
Xnot a derivative form; (2) you do not charge a fee for copying or for\n\
Xdistribution; (3) you ensure that the distributed form includes the\n\
Xcopyright notice, this paragraph, and the disclaimer of warranty in their\n\
Xentirety. These rights are temporary and revocable upon written, oral, or\n\
Xother notice by Daniel J. Bernstein. This copyright notice shall be\n\
Xgoverned by the laws of the state of New York.\n\
X\n\
XIf you have questions about filterfile or about this copyright notice,\n\
Xor if you would like additional rights beyond those granted above,\n\
Xplease feel free to contact the author at brnstnd at acf10.nyu.edu\n\
Xon the Internet.\n";
X
Xstatic char filterfilewarranty[] =
X"To the extent permitted by applicable law, Daniel J. Bernstein disclaims\n\
Xall warranties, explicit or implied, including but not limited to the\n\
Ximplied warranties of merchantability and fitness for a particular purpose.\n\
XDaniel J. Bernstein is not and shall not be liable for any damages,\n\
Xincidental or consequential, arising from the use of this program, even\n\
Xif you inform him of the possibility of such damages. This disclaimer\n\
Xshall be governed by the laws of the state of New York.\n\
X\n\
XIn other words, use this program at your own risk.\n\
X\n\
XIf you have questions about filterfile or about this disclaimer of warranty,\n\
Xplease feel free to contact the author at brnstnd at acf10.nyu.edu\n\
Xon the Internet.\n";
X
Xstatic char filterfileusage[] =
X"Usage: filterfile [ -dDpPtTACHUVW ] [ -oout ] [ -eext ]\n\
X                  file /path/filter [ arg ... ]\n\
XHelp:  filterfile -H\n";
X
Xstatic char filterfilehelp[] =
X"filterfile applies a filter to a file, placing the output in a new file.\n\
X\n\
Xfilterfile -A: print authorship notice\n\
Xfilterfile -C: print copyright notice\n\
Xfilterfile -H: print this notice\n\
Xfilterfile -U: print short usage summary\n\
Xfilterfile -V: print version number\n\
Xfilterfile -W: print disclaimer of warranty\n\
X\n\
Xfilterfile [ -dDpPtT ] [ -oout ] [ -eext ]\n\
X           file /path/filter [ arg ... ]: apply filter to file\n\
X  -d: delete input file\n\
X  -D: do not delete input file (default)\n\
X  -p: preserve protection of file\n\
X  -P: do not preserve protection of file (default)\n\
X  -t: preserve access and modification times of file\n\
X  -T: do not preserve times (default)\n\
X  -eext: sent output to file.ext, or if ext begins with . remove it from name\n\
X  -oout: send output to out (overrides -e)\n\
X\n\
XIf you have questions about or suggestions for filterfile, please feel free\n\
Xto contact the author, Daniel J. Bernstein, at brnstnd at acf10.nyu.edu\n\
Xon the Internet.\n";
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X#ifdef BSD
X#include <limits.h>
X#endif
X#include <sys/wait.h>
X#include <sys/time.h>
X
Xextern char *getenv(); 
Xextern char *malloc(); 
X
Xint flagdelete = 0;
Xint flagprotect = 0;
Xint flagtimes = 0;
X
Xint flagunappend = 0;
X
Xchar *fnout = 0;
Xchar *fnin = 0;
Xchar **filter = 0;
Xchar *fnappend = 0;
X
Xmain(argc,argv,envp)
Xint argc;
Xchar *argv[];
Xchar *envp[];
X{
X char *s;
X int f;
X int fdin;
X int fdout;
X union wait wstat;
X struct stat stinold;
X struct stat stoutold;
X struct stat stin;
X struct stat stout;
X struct timeval tvp[2];
X
X fnappend = getenv("FILTERFILEEXT");
X
X while (*(++argv))
X   if (**argv == '-')
X     while (*(++(*argv)))
X       switch(**argv)
X        {
X	 case 'd': flagdelete = 1; break;
X	 case 'D': flagdelete = 0; break;
X	 case 'p': flagprotect = 1; break;
X	 case 'P': flagprotect = 0; break;
X	 case 't': flagtimes = 1; break;
X	 case 'T': flagtimes = 0; break;
X	 case 'e': fnappend = *argv + 1;
X		   while (*(++(*argv))) /* null */ ;
X		   --(*argv); break; /* we really want breakbreak here */
X	 case 'o': fnout = *argv + 1;
X		   while (*(++(*argv))) /* null */ ;
X		   --(*argv); break; /* we really want breakbreak here */
X	 case 'A': printf(filterfileauthor); exit(0);
X         case 'C': printf(filterfilecopyright); exit(0);
X         case 'V': printf(filterfileversion); exit(0);
X         case 'W': printf(filterfilewarranty); exit(0);
X         case 'H': printf(filterfilehelp); exit(0);
X         case 'U': printf(filterfileusage); exit(0);
X         default: ;
X	}
X   else 
X     if (fnin == 0) 
X       fnin = *argv;
X     else
X       if (filter == 0) 
X        {
X         filter = argv;
X	 break;
X        }
X 
X if ((fnin == 0) || (filter == 0))
X  {
X   printf(filterfileusage); exit(1);
X  }
X if (fnout == 0)
X  {
X   flagunappend = (*fnappend == '.');
X   for (s = fnappend;*s;s++)
X     if (*s == '/')
X       *s = '.'; 
X   fnout = malloc((unsigned) (strlen(fnin) + strlen(fnappend) + 2));
X   if (fnout == 0)
X    {
X     fprintf(stderr,"filterfile: fatal: cannot create output file name: \
Xout of memory\n");
X     exit(1);
X    }
X   (void) strcpy(fnout,fnin);
X   if (flagunappend && (strlen(fnout) > strlen(fnappend)) &&
X	(strcmp(fnout + strlen(fnout) - strlen(fnappend),fnappend) == 0))
X     fnout[strlen(fnout) - strlen(fnappend)] = 0;
X   else
X    {
X     (void) strcat(fnout,".");
X     if (fnappend != NULL)
X       (void) strcat(fnout,fnappend);
X    }
X  }
X
X /* Okay. Now we have fnin, fnout, and filter. */
X
X fdin = open(fnin,O_RDONLY);
X if (fdin == -1)
X  { fprintf(stderr,"filterfile: fatal: cannot open input file ");
X   perror(fnin); exit(1); }
X if (fstat(fdin,&stinold) == -1)
X  { perror("filterfile: fatal: cannot stat input file"); exit(1); }
X if (flagdelete && (stinold.st_nlink > 1))
X  { fprintf(stderr,"filterfile: fatal: will not proceed: \
Xinput file %s has extra links\n",fnin); exit(1); }
X
X tvp[0].tv_sec = stinold.st_atime;
X tvp[0].tv_usec = 0;
X tvp[1].tv_sec = stinold.st_mtime;
X tvp[1].tv_usec = 0;
X
X fdout = open(fnout,O_WRONLY | O_CREAT | O_EXCL,0600);
X if (fdout == -1)
X  { fprintf(stderr,"filterfile: fatal: cannot create output file ");
X   perror(fnout); exit(1); }
X if (fstat(fdout,&stoutold) == -1)
X  { perror("filterfile: fatal: cannot stat output file"); exit(1); }
X
X if (flagprotect)
X   if (fchmod(fdout,stinold.st_mode & 0777) == -1)
X     perror("filterfile: warning: cannot change protections of output file");
X
X if ((f = fork()) == 0) /* child */
X  {
X   if (dup2(fdin,0) == -1)
X    { perror("filterfile: fatal: cannot prepare input"); exit(1); }
X   if (dup2(fdout,1) == -1)
X    { perror("filterfile: fatal: cannot prepare output"); exit(1); }
X   execvp(*filter,filter);
X   fprintf(stderr,"filterfile: fatal: cannot execute filter\n");
X   exit(1);
X  }
X else if (f == -1) /* fork failed */
X  { perror("filterfile: fatal: cannot fork"); exit(1); }
X
X /* parent */
X close(fdin);
X close(fdout);
X if (wait(&wstat) == -1) 
X  { perror("filterfile: fatal: can't find filter process"); exit(1); }
X if (wstat.w_T.w_Termsig)
X   exit(1); 
X if (wstat.w_T.w_Retcode)
X   exit((int) wstat.w_T.w_Retcode); 
X if (wstat.w_T.w_Coredump)
X   exit(1); 
X
X /* Filter exited happily. */
X
X if (flagdelete)
X  {
X   /* Race! Race! Race! */
X   fdin = open(fnin,O_RDONLY);
X   if (fdin == -1)
X    { fprintf(stderr,"filterfile: fatal: cannot reopen input file ");
X     perror(fnin); exit(1); }
X   if (fstat(fdin,&stin) == -1)
X    { fprintf(stderr,"filterfile: fatal: cannot restat input file ");
X     perror(fnin); exit(1); }
X   if ((stin.st_dev != stinold.st_dev) || (stin.st_ino != stinold.st_ino))
X    { fprintf(stderr,"filterfile: fatal: will not delete input file %s: \
Xit has been moved\n",fnin); exit(1); }
X   if (stin.st_nlink > 1)
X    { fprintf(stderr,"filterfile: fatal: will not delete input file %s: \
Xit has extra links\n",fnin); exit(1); }
X   /* Race! Race! Race! */
X   if (unlink(fnin) == -1)
X    { fprintf(stderr,"filterfile: fatal: cannot delete input file ");
X     perror(fnin); exit(1); }
X   (void) close(fdin);
X  }
X
X if (flagtimes)
X  {
X   /* Race! Race! Race! */
X   fdout = open(fnout,O_RDONLY);
X   if (fdout == -1)
X    { fprintf(stderr,"filterfile: fatal: cannot reopen output file ");
X     perror(fnout); exit(1); }
X   if (fstat(fdout,&stout) == -1)
X    { perror("filterfile: fatal: cannot restat output file"); exit(1); }
X   if ((stout.st_dev != stoutold.st_dev) || (stout.st_ino != stoutold.st_ino))
X    { fprintf(stderr,"filterfile: warning: will not set times of \
Xoutput file %s: it has been moved\n",fnout); exit(1); }
X   /* Race! Race! Race! */
X   if (utimes(fnout,tvp) == -1)
X    { fprintf(stderr,"filterfile: warning: cannot set times of output file ");
X     perror(fnout); exit(1); }
X  }
X
X exit(0);
X}
END_OF_FILE
if test 9303 -ne `wc -c <'filterfile.c'`; then
    echo shar: \"'filterfile.c'\" unpacked with wrong size!
fi
# end of 'filterfile.c'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(236 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XCCOPTS=-O
XNROFFOPTS=-man
X
Xdefault: all
X
Xall: filterfile filterfile.1
X
Xfilterfile: filterfile.c Makefile
X	cc $(CCOPTS) -o filterfile filterfile.c
X
Xfilterfile.1: filterfile.man Makefile
X	nroff $(NROFFOPTS) < filterfile.man > filterfile.1
END_OF_FILE
if test 236 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
echo shar: End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list