[comp.unix.questions] Pipe question

pew@grieg.Eng.Sun.COM (John Pew) (03/13/91)

I'm writing a program which needs to have some data run through
a filter. I do not have the source to the filter I want to call. I
only have the executable.  Within my program I need to write to the
filter and then read from the filter.  I know how to do one or the other
using either popen() or pipe() but don't know how to do both.  I can't
really use popen() since it only supports reading or writing to the
exec'd program.  I've included the program I wrote which does not work.
I set up two pipes: one for writing to the filter (I used "tr" to test with)
and one from reading from the filter.  When the parent tries to read
from the filter it never returns from the read.  Any suggestions would
be appreciated.

John Pew
pew@sun.com

Here's the program:


#include <stdio.h>
char buf[] = "hello there";
char newbuf[64];

main()
{
    int pipe1[2], pipe2[2], child;
    int fd1, fd2;
    int rret;

    if(pipe(pipe1)) {
	perror("pipe1");
	exit(1);
    }
    if(pipe(pipe2)) {
	perror("pipe2");
	exit(1);
    }
    if((child = fork()) == -1)
	perror("fork");
    else if (child) {	/* This is the parent */
	close(pipe1[0]);
	close(1);
	fd1 = dup(pipe1[1]);
	close(pipe1[1]);

	close(pipe2[1]);
	close(0);
	fd2 = dup(pipe2[0]);
	close(pipe2[0]);
	fprintf(stderr,"parent: fd1 %d, fd2 %d\n",fd1,fd2);
	if(write(fd1, buf, strlen(buf)) < 0)
	    perror("writing stream message");
	sleep(1);
	rret = read(fd2, newbuf, 16);
	if(rret < 0) {
	    perror("read");
	    exit(1);
	}
	fprintf(stderr,"parent: rret = %d\n",rret);
    } else {		/* This is the child */
	close(pipe1[1]);
	close(0);
	fd1 = dup(pipe1[0]);
	close(pipe1[0]);

	close(pipe2[0]);
	close(1);
	fd2 = dup(pipe2[1]);
	close(pipe2[1]);
	execlp("tr", "tr", "a-z", "A-Z", (char *)0);
	fprintf(stderr,"unable to execvp tr\n");
    }
}

jik@athena.mit.edu (Jonathan I. Kamens) (03/13/91)

In article <9669@exodus.Eng.Sun.COM>, pew@grieg.Eng.Sun.COM (John Pew) writes:
|> When the parent tries to read
|> from the filter it never returns from the read.  Any suggestions would
|> be appreciated.

  Put "(void) close(fd1)" directly after your write() to the child in the
parent.

  The filter never sees EOF, so (if it's using stdio) it never gets any input
to process and send back to you, because the text you have sent it isn't large
enough to fill stdio's input buffer, nor does it have a carriage return at the
end of it to force it through (if the filter were working on a line-by-line
basis).

  If you only use the filter once (i.e. you always open the filter, send some
amount of data to it, read the result and then close the pipe to the filter),
you can solve your problem simply by closing the pipe to the filter before
trying to read data.

  If you need the filter to stay around, then you'll have to figure out some
way to get it to do line-buffering, possibly by running it on a pty instead of
on a pipe.  Or you could use

    execlp("pty", "tr", "tr", "a-z", "A-Z", 0);

if you don't want to bother to learn how to use pty's in C, and if you've got
pty's installed on your system.  (Note to all of you who are reading this in
disgust -- I had to say it, or Dan would have! :-)

  And, of course, if it's doing line-buffering, then you'll have to make sure
that your input always ends in a newline.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710