[comp.unix.programmer] How do I tell if STDIN is a PIPE?

jjr@ace.ece.arizona.edu (Jeffrey J. Rodriguez) (05/27/91)

How do I tell whether stdin is coming from a pipe?
There must be some system call I can use from a C program.
My problem is that lseek & fseek won't work with a pipe.
Therefore, I need to check stdin to see if it is coming from a pipe.

If stdin is coming from a pipe, then what is the best way to do a seek?
If the input data is not ASCII, then a loop of getchar() won't work.
Therefore, it seems like a loop of fread (one byte at a time)
would be required.
Please let me know if there's a better way to handle this.

		Jeff
		rodriguez@ece.arizona.edu

dkeisen@leland.Stanford.EDU (Dave Eisen) (05/27/91)

In article <1991May26.172328.713@arizona.edu> jjr@ace.ece.arizona.edu (Jeffrey J. Rodriguez) writes:
>How do I tell whether stdin is coming from a pipe?
>There must be some system call I can use from a C program.
>My problem is that lseek & fseek won't work with a pipe.
>Therefore, I need to check stdin to see if it is coming from a pipe.

As usual, the best way to see if an operation *would* succeed if you were
to try it is to just go ahead and try it and see if it *did* succeed. If
you seek and it fails and errno is ESPIPE, you know that stdin is 
a pipe.




-- 
Dave Eisen                           dkeisen@leland.Stanford.EDU
1101 San Antonio Road, Suite 102     (Gang-of-Four is being taken off the net)
Mountain View, CA 94043
(415) 967-5644

boyd@prl.dec.com (Boyd Roberts) (05/28/91)

In article <1991May27.022727.9860@leland.Stanford.EDU>, dkeisen@leland.Stanford.EDU (Dave Eisen) writes:
> ... If
> you seek and it fails and errno is ESPIPE, you know that stdin is 
> a pipe.
> 

Do me a favour, man stat(2).


Boyd Roberts			boyd@prl.dec.com

``When the going gets wierd, the weird turn pro...''

lbr@holos0.uucp (Len Reed) (05/29/91)

In article <1991May26.172328.713@arizona.edu> jjr@ace.ece.arizona.edu (Jeffrey J. Rodriguez) writes:
>How do I tell whether stdin is coming from a pipe?

Use isapipe(0).

>My problem is that lseek & fseek won't work with a pipe.

Sure, but if you could check the error return from lseek.  The isapipe()
routine is cleaner, though.

>If stdin is coming from a pipe, then what is the best way to do a seek?

You can't. :-)

>If the input data is not ASCII, then a loop of getchar() won't work.

You are asking how can you move forward in the file from the current point.
So who says getchar() won't work?  It'll read binary bytes with no problem.
-- 
Len Reed
Holos Software, Inc.
Voice: (404) 496-1358
UUCP: ...!gatech!holos0!lbr

subbarao@phoenix.Princeton.EDU (Kartik Subbarao) (05/29/91)

In article <1991May28.172456.1585@holos0.uucp> lbr@holos0.uucp (Len Reed) writes:
>In article <1991May26.172328.713@arizona.edu> jjr@ace.ece.arizona.edu (Jeffrey J. Rodriguez) writes:
>>How do I tell whether stdin is coming from a pipe?
>
>Use isapipe(0).

On which wonderful OS is this system call supported?


		-Kartik


--
internet% whoami

subbarao@phoenix.Princeton.EDU -| Internet
kartik@silvertone.Princeton.EDU (NeXT mail)  
SUBBARAO@PUCC.BITNET			          - Bitnet

mouse@thunder.mcrcim.mcgill.edu (der Mouse) (05/30/91)

In article <1991May26.172328.713@arizona.edu>, jjr@ace.ece.arizona.edu (Jeffrey J. Rodriguez) writes:

> How do I tell whether stdin is coming from a pipe?

Difficult.  Under some systems, a "pipe" is not a distinguishable
thing; in particular, BSD implements pipes as connected pairs of
sockets, so a pipe appears identical to any other socket connection (of
the same address family, of course).

> There must be some system call I can use from a C program.  My
> problem is that lseek & fseek won't work with a pipe.  Therefore, I
> need to check stdin to see if it is coming from a pipe.

Ah.  You don't care whether it's a pipe, you only care whether it's
seekable.  There are lots of things it could be, and pipes aren't the
only unseekable ones (nor are plain files the only seekable ones).

The simplest thing to do is, as someone else recommended, try to seek:
a good harmless seek to try is a relative seek to 0 bytes from the
current position.

But whether even that is really what you want depends on what you're
doing.  Even when you're connected to something seekable, it may change
between one read and the next - even if it's a plain file.  You should
balance the dangers and decide what you *really* want: do you want to
reread the input, do you want to see what's somewhere else in the
"file", just what *do* you want to do?

> If stdin is coming from a pipe, then what is the best way to do a
> seek?

There is none, in general.

> If the input data is not ASCII, then a loop of getchar() won't work.

What's ASCII got to do with it?  getchar() is perfectly happy reading
binary data.  (You aren't making the mistake of thinking getchar()
returns a char, are you?  (It doesn't; it returns an int.))

A loop with getchar() can't possibly seek any way but forwards.  If
that's all youi want to do, you *can* do it on a pipe - or a file, or
pretty much anything else.  Just be prepared to get EOF while reading,
of course.

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

nanook@eskimo.celestial.com (Robert Dinse) (06/02/91)

In article <1991May30.101153.27842@thunder.mcrcim.mcgill.edu>, mouse@thunder.mcrcim.mcgill.edu (der Mouse) writes:
> In article <1991May26.172328.713@arizona.edu>, jjr@ace.ece.arizona.edu (Jeffrey J. Rodriguez) writes:
> 
> > How do I tell whether stdin is coming from a pipe?
> 
> Difficult.  Under some systems, a "pipe" is not a distinguishable
> thing; in particular, BSD implements pipes as connected pairs of
> sockets, so a pipe appears identical to any other socket connection (of
> the same address family, of course).

     Try:

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

main()
{
	struct stat s;

	if(fstat(0, &s))
	{
		fprintf(stderr, "Can't stat stdin.\n");
		exit(-1);
	}
	if(s.st_mode & S_IFIFO)
		printf("Stdin is a pipe.\n");
	else
		printf("Stdin is not a pipe.\n");
	exit(0);
}

     This works on my machine but I don't have sockets so that may cloud
the issue, but maybe this will be helpful.

dkeisen@leland.Stanford.EDU (Dave Eisen) (06/03/91)

In article <653@eskimo.celestial.com> nanook@eskimo.celestial.com (Robert Dinse) writes: 
> How do I tell whether stdin is coming from a pipe?

>> In article <1991May26.172328.713@arizona.edu>, jjr@ace.ece.arizona.edu (Jeffrey J. Rodriguez) writes:
>
>     Try:
>	fstat(0, &s);
>	if(s.st_mode & S_IFIFO)
>		printf("Stdin is a pipe.\n");
>	else
>		printf("Stdin is not a pipe.\n");

This doesn't work everywhere, in particular, it doesn't work on the machine I
am posting from. 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.

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.



-- 
Dave Eisen                           dkeisen@leland.Stanford.EDU
1101 San Antonio Road, Suite 102     
Mountain View, CA 94043
(415) 967-5644

boyd@prl.dec.com (Boyd Roberts) (06/03/91)

In article <1991Jun3.141144.23620@leland.Stanford.EDU>, dkeisen@leland.Stanford.EDU (Dave Eisen) writes:
>
> >> In article <1991May26.172328.713@arizona.edu>, jjr@ace.ece.arizona.edu (Jeffrey J. Rodriguez) writes:
> >
> >     Try:
> >	fstat(0, &s);
> >	if(s.st_mode & S_IFIFO)
> >     ...
> 
> This doesn't work everywhere, in particular, it doesn't work on the machine I
> am posting from. 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.
> 

You don't test for anything that way.  This:

    if ((sb.st_mode & S_IFMT) == S_IFIFO)

is the way it's done (unless you have those gory POSIX macros).  But,
of course they're not `portable'; except to POSIX.


Boyd Roberts			boyd@prl.dec.com

``When the going gets wierd, the weird turn pro...''

torek@elf.ee.lbl.gov (Chris Torek) (06/03/91)

In article <1991May26.172328.713@arizona.edu> jjr@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@eskimo.celestial.com> nanook@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@leland.Stanford.EDU>
dkeisen@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@ee.lbl.gov

dkeisen@leland.Stanford.EDU (Dave Eisen) (06/04/91)

In article <1991Jun3.150851.21915@prl.dec.com> boyd@prl.dec.com (Boyd Roberts) writes:
>You don't test for anything that way.  This:
>
>    if ((sb.st_mode & S_IFMT) == S_IFIFO)
>
>is the way it's done (unless you have those gory POSIX macros).  But,

Of course, it is (my brain slipped when I was typing and I wrote S_IFREG in
place of S_IFMT). That still doesn't change the point of my posting ---
on this machine (running ULTRIX 4.1), sb.stmode & S_IFMT is 0 for a pipe.



-- 
Dave Eisen                           dkeisen@leland.Stanford.EDU
1101 San Antonio Road, Suite 102     
Mountain View, CA 94043
(415) 967-5644

boyd@prl.dec.com (Boyd Roberts) (06/04/91)

In article <1991Jun3.175117.9882@leland.Stanford.EDU>, dkeisen@leland.Stanford.EDU (Dave Eisen) writes:
> 
> Of course, it is (my brain slipped when I was typing and I wrote S_IFREG in
> place of S_IFMT). That still doesn't change the point of my posting ---
> on this machine (running ULTRIX 4.1), sb.stmode & S_IFMT is 0 for a pipe.
> 

Yep, that really sucks.  stat(2) on a pipe(2) returns with st_mode == 0.

But, stat(2) on a named pipe returns with st_mode == S_IFIFO | <mode>.
I just checked this out on my 3max (ULTRIX prl313 4.2 0 RISC).

Neat kernel work, guys...  real neat.


Boyd Roberts			boyd@prl.dec.com

``When the going gets wierd, the weird turn pro...''

lan_csse@netrix.nac.dec.com (CSSE LAN Test Account) (06/06/91)

In article <653@eskimo.celestial.com> nanook@eskimo.celestial.com (Robert Dinse) writes:
>In article <1991May30.101153.27842@thunder.mcrcim.mcgill.edu>, mouse@thunder.mcrcim.mcgill.edu (der Mouse) writes:
>> In article <1991May26.172328.713@arizona.edu>, jjr@ace.ece.arizona.edu (Jeffrey J. Rodriguez) writes:
>> 
>> > How do I tell whether stdin is coming from a pipe?
>> 
>> Difficult.  Under some systems, a "pipe" is not a distinguishable
>> thing; in particular, BSD implements pipes as connected pairs of
>> sockets, so a pipe appears identical to any other socket connection (of
>> the same address family, of course).
>     Try:
	...
>	if(s.st_mode & S_IFIFO)
>		printf("Stdin is a pipe.\n");
>	else
>		printf("Stdin is not a pipe.\n");

On this machine (Ultrix 4.1) and on the next one  over  (Ultrix  3.1),
this code says "Stdin is not a pipe.\n".  On investigation, the status
struct was *entirely* filled with null bytes. It's quite reproducible.
Some  time back, I discovered that a method that works here (and on on
most of the systems I've tried to port the test to) is:

	switch (s.st_mode & S_IFMT) {
		case S_IFSOCK:
			...;
			break;
		case S_IFIFO:
		case 0:
			...;
			break;
		default:
			fprintf(stderr,"+++ File %d: Unexpected mode 0%o.",f,s.st_mode);
	}

Of course, I expect this to break on the next system I try.  Ain't  it
just wunnerful, having such a nice basis for portable code?

mouse@thunder.mcrcim.mcgill.edu (der Mouse) (06/07/91)

In article <23190@shlump.lkg.dec.com>, lan_csse@netrix.nac.dec.com (CSSE LAN Test Account) writes:
> In article <653@eskimo.celestial.com> nanook@eskimo.celestial.com (Robert Dinse) writes:
>> if(s.st_mode & S_IFIFO)
> On this machine (Ultrix 4.1) and on the next one over (Ultrix 3.1),
> this code says "Stdin is not a pipe.\n". On investigation, the status
> struct was *entirely* filled with null bytes.

mtXinu 4.3+NFS stat(2) manpage:

.SH BUGS
Applying
.I fstat
to a socket (and thus to a pipe)
returns a zero'd buffer,
except for the blocksize field,
and a unique device and inode number.

It seems other systems (like your Ultrices and probably vanilla 4.3) do
this as well.

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu