[comp.sources.misc] v03i077: hold -- terminate a pipe with its input

bill@qst1.tcc.com (Bill Cox) (07/04/88)

Posting-number: Volume 3, Issue 77
Submitted-by: "Bill Cox" <bill@qst1.tcc.com>
Archive-name: hold2

[Is my memory going?  I don't remember seeing a first version of this.  ++bsa]

Here's an unpdated version of hold.c, that lets you terminate a pipe with its
input file, effectively putting the output back into the input file, as in
	tail SYSLOG | hold SYSLOG
This version contains some fixes noticed by readers at ncoast.

To build it, simply
	cc -O -o hold hold.c
It's been compiled and run under V7, SYSV, XENIX and MS-DOS in its present 
form.

Questions/bugs/flames to Bill Cox, (714)631-4452 (voice)
			or uunet!ccicpg!qst1!bill

RATIONALE: Why use hold?

Let's say you have the file tmp, which contains:
AA#AAAA
BBBB#BB

And you wish to change the '#'s to '~'s. If you
simply say sed 's/#/~/' tmp, you see
AA~AAAA
BBBB~BB
just as you would expect.

Now, enter sed 's/#/~/' tmp > tmp (in effect asking the shell to
over-write the input file with the output) then cat tmp.  Surprise,
tmp is a null file!

Now, enter sed 's/#/~/' tmp | hold tmp; cat tmp
AA~AAAA
BBBB~BB
Just as you expect.  Because hold creates a temporary file FOR YOU,
which doesn't appear as an output on the command line.

#! /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:  hold.c
# Wrapped by srcs@qst1 on Mon Jul  4 17:21:31 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'hold.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hold.c'\"
else
echo shar: Extracting \"'hold.c'\" \(2960 characters\)
sed "s/^X//" >'hold.c' <<'END_OF_FILE'
X/*
X * hold  terminate a pipe, gather stdin into a temporary,
X *	 then rename the temporary to the argument's name.
X *
X * Example: ... | hold filename
X *	or: ... | hold > filename
X *
X */
X
X#ifdef MSDOS
X#include <io.h>
X#endif
X
X#include <stdio.h>
X#include <errno.h>
X#ifndef	MSDOS
X#include <signal.h>
X#endif
XFILE *fp = NULL;
Xint stdoutf;
X
X#ifdef MSDOS
Xchar fname[11] = "holdXXXXXX";
X#endif
X#ifdef M_XENIX
Xchar fname[11] = "holdXXXXXX";
X#else
Xchar fname[20] = "/usr/tmp/holdXXXXXX";
X#endif
X
X
Xextern int errno;	/* Declare global error value. */
X
X#ifndef MSDOS
X/* This routine gains control when a signal is trapped.  It unlinks the
X * temporary file.  This is necessary when a pipe gets broken.
X */
Xint	Trap()
X{
Xunlink(fname);	/* Ignore errors at this point, we are dead anyway. */
Xexit(1);
X}
X#endif
X
Xint main(argc, argv)
X    int argc;
X    char *argv[];
X{
X    int c;
X
X    stdoutf = 0;
X    if (argc > 1)
X        stdoutf = 1;
X        
X    if (argc > 2) {
X	fprintf(stderr, "Usage: ... | hold filename\n");
X	fprintf(stderr, " or    ... | hold > filename\n");
X	return(1);
X	}
X#ifndef MSDOS
X    /* Trap signals to remove file on. */
X    signal(SIGHUP, Trap);
X    signal(SIGINT, Trap);
X    signal(SIGQUIT, Trap);
X    signal(SIGTERM, Trap);
X#endif
X    if ((fp = fopen(mktemp(fname), "w")) == NULL) 
X	errclean(2, "open %s", fname, 0);
X    
X    while ((c = getchar()) != EOF)
X	fputc(c, fp);
X
X    if (ferror(stdin) || ferror(fp))
X     	errclean(3, "copy stdin to %s", 0, fname);
X    	
X    if (fclose(fp) != 0) 
X	errclean(4, "close %s", fname, 0);
X
X    if (stdoutf) {
X        if (rename(fname, argv[1]) != 0)	
X            errclean(5, "rename %s to %s", fname, argv[1]);
X	}
X    else {
X	if ((fp = fopen(fname, "r")) == NULL) 
X	    errclean(6, "open %s", fname, 0);
X    
X	while ((c = getc(fp)) != EOF)
X	    fputc(c, stdout);
X
X	if (ferror(stdout) || ferror(fp))
X    	    errclean(7, "copy %s to stdout", 0, fname);
X    	
X	if (fclose(fp) != 0) 
X	    errclean(8, "close %s", fname, 0);
X
X	if (unlink(fname) != 0)
X	    errclean(9, "remove %s", fname, 0);
X	}
X
X    return(0);
X}
X
X
X#ifndef MSDOS
Xint rename(s1, s2)		/* s2 = new name, s1 = existing name */
X    char *s1, *s2;
X{
X    /* assure that new name doesn't exist */
X    if (unlink(s2) != 0 && errno != ENOENT) {
X	errclean(10, "remove %s", s2, 0);
X        return(1);   
X        }
X    /* connect new name to existing file */
X    if (link(s1, s2) != 0) {
X	errclean(11, "link %s to %s", s1, s2);
X        return(1);   
X        }
X    /* remove old name for the file */
X    if (unlink(s1) != 0) {
X	errclean(12, "remove %s", s1, 0);
X        return(1);   
X        }
X    return(0);
X}
X#endif
X
X/*
X * errclean - output error message and exit to system
X */
Xerrclean(code, string, arg1, arg2)
X    int code;
X    char *string;
X    char *arg1, *arg2;
X{
X    char lstr[80];
X
X    if (fp != NULL) {
X	(void)unlink(fname);
X        fp = NULL;
X	}
X    sprintf(lstr, "hold %2d: can't %s\n", code, string);
X    fprintf(stderr, lstr, arg1, arg2);
X    exit(code);
X}
X
END_OF_FILE
if test 2960 -ne `wc -c <'hold.c'`; then
    echo shar: \"'hold.c'\" unpacked with wrong size!
fi
# end of 'hold.c'
fi
echo shar: End of shell archive.
exit 0