[comp.unix.wizards] Seek on a pipe

chris@mimsy.UUCP (06/16/87)

In article <791@mcgill-vision.UUCP> mouse@mcgill-vision.UUCP (der Mouse) writes:
>A program that expects to do seeks should ensure that the object can
>seek ...

... which is exactly why I wrote a routine to do this.

/*
 * Copyright (c) 1987 University of Maryland Computer Science Department.
 * All rights reserved.
 * Permission to copy for any purpose is hereby granted so long as this
 * copyright notice remains intact.
 */

#ifndef lint
static char rcsid[] = "$Header: seek.c,v 2.3 87/04/15 18:38:30 chris Exp $";
#endif

/*
 * Seekable is a predicate which returns true iff a Unix fd is seekable.
 *
 * MakeSeekable forces an input stdio file to be seekable, by copying to
 * a temporary file if necessary.
 */

#include <stdio.h>
#ifdef sys5
#include <sys/types.h>
#include <sys/fcntl.h>
#else
#include <sys/param.h>
#endif
#include <sys/file.h>
#include <sys/stat.h>

long	lseek();
char	*getenv();

int
Seekable(fd)
	int fd;
{

	return (lseek(fd, 0L, 1) >= 0 && !isatty(fd));
}

/*
 * We use the despicable trick of unlinking an open temporary file.
 * The alternatives are too painful.  If it becomes necessary to
 * do this another way, however, here is a method suggested by Fred
 * Blonder: fork, and have the parent wait for the child to exit.
 * (The parent must avoid being killed during this phase.)  When
 * the child exits, the parent should then remove the temporary file,
 * then exit with the same status, or send itself the same signal.
 */
int
MakeSeekable(f)
	register FILE *f;
{
	register int tf, n;
	int mypid, tries, blksize;
	char *tmpdir;
#ifdef MAXBSIZE
	char buf[MAXBSIZE];
	struct stat st;
#else
	char buf[BUFSIZ];
#endif

	if (Seekable(fileno(f)))
		return (0);

	if ((tmpdir = getenv("TMPDIR")) == 0)
		tmpdir = "/tmp";

	/* compose a suitable temporary file name, and get an r/w descriptor */
	mypid = getpid();
	n = 0;
	tries = 0;
	do {
		(void) sprintf(buf, "%s/#%d.%d", tmpdir, mypid, n++);
		(void) unlink(buf);
#ifdef O_CREAT			/* have three-argument open syscall */
		tries++;
		tf = open(buf, O_RDWR | O_CREAT | O_EXCL, 0666);
#else
		if (access(buf, 0) == 0) {
			/*
			 * Skip existing files.  Note that tf might
			 * not be set yet.
			 */
			tf = -1;
			continue;
		}
		tries++;
		tf = creat(buf, 0666);
		if (tf >= 0) {
			(void) close(tf);
			tf = open(buf, 2);
			if (tf < 0)
				(void) unlink(buf);
		}
#endif
	} while (tf < 0 && tries < 20);
	if (tf < 0)		/* probably unrecoverable user error */
		return (-1);

	(void) unlink(buf);

	/* copy from input file to temp file */
#ifdef MAXBSIZE
	if (fstat(tf, &st))	/* how can this ever fail? */
		blksize = MAXBSIZE;
	else
		blksize = MIN(MAXBSIZE, st.st_blksize);
#else
	blksize = BUFSIZ;
#endif
	while ((n = fread(buf, 1, blksize, f)) > 0) {
		if (write(tf, buf, n) != n) {
			(void) close(tf);
			return (-1);
		}
	}
	/* ferror() is broken in Ultrix 1.2; hence the && */
	if (ferror(f) && !feof(f)) {
		(void) close(tf);
		return (-1);
	}

	/*
	 * Now switch f to point at the temp file.  Since we hit EOF, there
	 * is nothing in f's stdio buffers, so we can play a dirty trick: 
	 */
	clearerr(f);
	if (dup2(tf, fileno(f))) {
		(void) close(tf);
		return (-1);
	}
	(void) close(tf);
	return (0);
}
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	seismo!mimsy!chris

karl@haddock.UUCP (Karl Heuer) (06/26/87)

In article <7074@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>In article <791@mcgill-vision.UUCP> mouse@mcgill-vision.UUCP (der Mouse) writes:
>>A program that expects to do seeks should ensure that the object can seek
>
>... which is exactly why I wrote a routine to do this.
>	return (lseek(fd, 0L, 1) >= 0 && !isatty(fd));

"isatty" is not sufficient; character-special files in general are incapable
of seeking.  I ran into this same question a couple of years ago, reluctantly
decided that I had to use fstat(), and wrote it that way in my library.  (I
called it "canseek".)

I wish lseek would just fail, instead of being a no-op, on nonseekable files
other than pipes.  Does anyone know what POSIX says about the matter?

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint