pipe flushing
Joseph S. D. Yao
jsdy at hadron.UUCP
Fri Jan 2 21:46:10 AEST 1987
I am not the Throop, but here's how:
In article <658 at bath63.ux63.bath.ac.uk> ma6nrr at ux63.bath.ac.uk (Rashbrook) writes (to Wayne Throop):
>a) mail me the fflush manual (I can't find it anywhere)
Try 'man 3 fclose'.
>b) show me what to do:
In the below, some of what I say is flagged by '*' in the LHC.
These lines have to do with style. This is not irrelevant,
however anyone who wishes to flame those lines should be
deprecated as a crashing boor. Style helps improve programming
performance; but by its very name one should assume that there
is more than one way to do each of these things.
Some mangling of the program text bbelow has occured, in an
attempt to convince inews (the real culprit) that more
information is going out than came in.
...
>#define lenu (unsigned)len
>char buffer[128]; /* read/write buffer
> arbitrary length */
* Should be local to parent() [see note on stop()].
>int pipe0[2],pipe1[2],tty,outfile,len;
* Should be local as appropriate, and passed as parameters.
>void exit(); /* this is for lint */
* Should be local to main() [see note on stop()].
>main(argc,argv,envp)
...
>{
> if (argc!=3)
> { /* bad no. of args */
... What about programs with arguments?
> fprintf(stderr,"Usage: %s file program\n",*argv);
> exit(2);
> }
> if (outfile=creat(*++argv,0666),outfile==EOF)
*/2:
I would have made the outfile = ... and if (...) two
separate statements, to eschew confusion. Also, I never
make files 0666: that way lies lack of data integrity.
> { /* attempt to open file */
> fprintf(stderr,"%s: cannot open\n",*argv);
> exit(1);
> }
> pipe(pipe0); /* pipe for i/p to program */
> pipe(pipe1); /* pipe for o/p from program */
... MUST test these pipe()s for correct return! Either < 0
or == ERROR (if defined) to test for error.
> if (fork()) parent(); else child(++argv,envp);
... Same. best is switch(fork()) ...
> /* now execute required program */
> exit(0); /* this is for lint */
... System 5 lint requires that these exit()s be return()s;
I was unaware that any lint required exit()s.
>}
>stop()
>{
... (*/2) The only reliable thing to do in an interrupt
handler is to set a flag. Best to set a flag here,
and have parent() take notice of it and return to
main(), which then exit()s [or return()s, per above].
> while ((len=read(pipe1[0],buffer,128))>0)
> { /* clear output buffer */ ... > } >exit(0);
>}
>parent() /* parent writes to outfile */
>{
> int (*signal())(); /* this is for lint */
> close(pipe0[0]); /* enable read from pipe without waiting */
... close(pipe1[1]);
> fcntl(pipe1[0],F_SETFL,fcntl(pipe1[0],F_GETFL,0)|O_NDELAY);
... (*sigh*) Yes, this works for pipes, even though the docs
say it only works for tty's. But (just because of the way
it's defined) I'd rather have a real int than 0 as the arg.
(man 2 fcntl) Also, O_NDELAY is for open() or ioctl();
FNDELAY is for fcntl(). (Yes, one is defined as the other
in your include file. So what? Say What You Mean is the
whole of the law!)
> tty=open("/dev/tty",O_RDWR | O_NDELAY); /* open to console */
... MUST check return values.
> signal(18,stop); /* trap program's death */
... Do you perchance mean SIGCHLD? Which is n o t 18!
Always use symbolic constants. That way, you don't make
silly mistakes like this. Also, if you should try to
compile this on a machine which does not have SIGCHLD
in its <signal.h>, you immediately know.
> for (;;)
> { /* transfer i/o until program dies */
> len=read(tty,buffer,128);
... Don't do the next two if lenu == 0 !
> write(pipe0[1],buffer,lenu);
> write(outfile,buffer,lenu);
... This code seems to assume lockstep read-from-tty / write-to-tty
performance on the part of the child. If that's the way
it actually works (and if no message is >128 chars), fine.
Otherwise, you should fork one process to funnel in each
direction, OR use Berkeley's SIGIO (assuming BSD, but so
do you) to sample reads, and read each one 'til dry; OR
... (The problem here is getting out of sync.)
> len=read(pipe1[0],buffer,128);
> if (len==EOF) stop(); /* so it's messy,but it's the
> only way with O_NDELAY */
... EOF is a stdio concept. It happens to be equal to what read()
returns on error. Partly coincidence. This same value also
happens to be what is returned on a possible delay!
... Again, don't do these if lenu == 0 .
> write(tty,buffer,lenu);
> write(outfile,buffer,lenu);
> }
>}
>child(argv,envp)
...
>{
> close(0); /* these aren't needed any more */
> close(1);
> if (dup(pipe0[0])!=0 || dup(pipe1[1])!=1)
> { /* connect pipes to stdin/out */
> fprintf(stderr,"error in pipes\n");
> exit(1);
> }
> close(pipe0[1]); > close(pipe1[0]);
... Also, close(pipe0[0]); close(pipe1[1]); since they're dup'd.
> close(outfile);
... Should never have been opened before the fork().
> setbuf(stdout,(char *)0);
... Unfortunately, this does n o t affect any future exec'd program.
(See below)
>/* test lines */ >printf("Hit return:\n"); >while (getchar()!='\n');
>/* this correctly prints up prompt,
> waits for return,then acts as
> program options |tee file */
> if (execve(*argv,argv+1,envp)==EOF) fprintf(stderr,"%s: cannot execute\n",*argv); /* finally execute program (hopefully!) */
>} /* so I don't need an if,so what? */
An EOF is n o t the all-purpose error return. Stdio only.
The setbuf() changes flags and elements in the iobuf structure
in the current process image (program memory). When you exec
a new program, that reads in new program memory. The structure
would have to be re-initialised from within that program; or
fflush() would have to be called every time you write: stdout
is always buffered if it isn't hooked up to a tty. I think
that's what you were asking about.
There's more wrong with this, but I'm not sure what.
--
Joe Yao hadron!jsdy at seismo.{CSS.GOV,ARPA,UUCP}
jsdy at hadron.COM (not yet domainised)
More information about the Comp.lang.c
mailing list