How do I tell if STDIN is a PIPE?

Chris Torek torek at elf.ee.lbl.gov
Tue Jun 4 01:57:43 AEST 1991


In article <1991May26.172328.713 at arizona.edu> jjr at ace.ece.arizona.edu
(Jeffrey J. Rodriguez) [I think; the attributions were somewhat goofed up]
asks:
>How do I tell whether stdin is coming from a pipe?

In article <653 at eskimo.celestial.com> nanook at eskimo.celestial.com
(Robert Dinse) suggests: 
>>	fstat(0, &s);
>>	if (s.st_mode & S_IFIFO)
>>		printf("Stdin is a pipe.\n");
>>	else
>>		printf("Stdin is not a pipe.\n");

In article <1991Jun3.141144.23620 at leland.Stanford.EDU>
dkeisen at leland.Stanford.EDU (Dave Eisen) writes:
>This doesn't work everywhere ...  s.st_mode & S_IFREG (which, by the way,
>is what you should be testing for) is 0 when stdin is a pipe. This doesn't
>seem to be consistently done across Unices.

It is unlikely to work (reliably) *any*where.  The st_mode member
contains a bitfield that holds values, not bitmaps; both of the above
tests (S_IFIFO and S_IFREG) are a lot like saying:

	To find out if the value is seven, see if bit 2 is set.

It might get you partway there, but certainly not all the way.

The only reasonable way to test the type subfield in st_mode is with

	if ((st.st_mode & S_IFMT) == S_IFREG) ...
	if ((st.st_mode & S_IFMT) == S_IFDIR) ...
	if ((st.st_mode & S_IFMT) == S_IFCHR) ...

and so on, or (if you have a POSIXified <sys/stat.h>) using the S_ISxxx
macros:

	if (S_ISREG(st.st_mode)) ...
	if (S_ISDIR(st.st_mode)) ...
	if (S_ISCHR(st.st_mode)) ...

In any case, testing for `fifoness' is not a portable solution, as (a)
4.[23]BSD do not have FIFOs and hence do not have an S_IFIFO definition
at all; and (b) 4.3875BSD (or whatever one has to call the not-yet-4.4
system), while it has S_IFIFO and may be compiled with FIFOs in the
kernel, nevertheless continues to implement pipes as a special case of
socketpair().  Hence a pipe on a 4.[234]BSD will show up as an S_IFSOCK
type, just like a socket, because it *is* a socket.

In addition, it is worth checking the return value from fstat(), as it
may return an error due to an NFS `stale file handle'.

>That is one of the reasons I suggested that the original poster just try his
>seek and see what happens --- as far as I know, any UNIX asked to 
>seek on a file type that is unseekable will fail with errno set to ESPIPE.

I know of none that do otherwise (which is not to say that none exist),
but in general, this is the right approach.  If you want to make sure
some operation works or is allowed, it is best to do the operation and
see if it worked, rather than `guessing' in advance.  It may still be
worth `guessing' in some situations (e.g., an editor might want to warn
you that a file is read-only before you go make changes in the editor's
copy), but this should never (never? well, hardly ever) be considered
the final answer.

Incidentally, in my MC-TeX drivers, I had the same sort of problem.
TeX DVI files are often best read backwards, but if the input to the
DVI-to-device program is a pipe, reading backwards is not possible.  So
[from lib/seek.c]:

/*
 * SeekFile copies an input stdio file, if necessary, so that
 * it produces a stdio file on which fseek() works properly.
 * It returns NULL if this cannot be done; in that case, the
 * input file is closed (or otherwise rendered worthless).
 *
 * CopyFile copies an input file unconditionally.  (On non-Unix
 * machines, it might `accidentally' not copy it.)
 *
 * On Unix machines, this means `if the input is a pipe or tty,
 * copy it to a temporary file'.  On other systems, all stdio files
 * might happen to be seekable.
 */

[...]

/*
 * Copy an input file, but only if necessary.
 */
FILE *SeekFile(f)
	FILE *f;
{
	int fd = fileno(f);

	return (lseek(fd, 0L, 1) >= 0 && !isatty(fd) ? f : CopyFile(f));
}
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA		Domain:	torek at ee.lbl.gov



More information about the Comp.unix.programmer mailing list