[comp.lang.c] pipe flushing

Saville@micro.udel.edu (11/20/86)

Does any body know of any function to flush out
the write buffer at one end of a pipe?
and i don't want waht is flushed to go
through the pipe, i want is to be clean as if nothing were
to be written at all. or better yet, a function that
allows the write buffer to be removed(or not allocated at all).
and i'm using file descriptors......not FILE *fileptr.

ma6nrr@bath63.bath.ac.uk (Rashbrook) (11/26/86)

Does anybody know how to set a pipe to be
flushed at newlines even over execs?
Even a function so a process can flush a
pipe,which is common to and written by a
forked process,so the original process can
can read it would be useful.
Thanks in advance,Neil.
==================================================
Any opinions expressed above may be taked as yours
==================================================

throopw@dg_rtp.UUCP (Wayne Throop) (12/01/86)

> From: ma6nrr@bath63.bath.ac.uk (Rashbrook)
> Does anybody know how to set a pipe to be
> flushed at newlines even over execs?

Assuming we are talking about the "fmumble" library routines here, this
is simply not possible, unless the program to be "exec"ed is rewritten
to co-operate with the program which will "exec" it (by recognizing a
"flush pipe output" option on its command line, for example).

> Even a function so a process can flush a
> pipe,which is common to and written by a
> forked process,so the original process can
> can read it would be useful.

If "fflush(3)" isn't what you have in mind, then I haven't the foggiest
notion what this means.  A specific example would be helpful.

--
Sometimes I think the only universal in the computing field is the
fetch-execute cycle.
                                --- Alan J. Perlis
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

ma6nrr@bath63.ux63.bath.ac.uk (Rashbrook) (12/15/86)

Sorry,reply on my rn doesn't work.
If you are Wayne Throop,or you think
you can help,read on,else junk quickly.

Please could you either
a) mail me the fflush manual (I can't find it anywhere)
b) show me what to do:
Here is the actual program (called sio,at the moment,it could be called tee):
------------------cut here,it's your vdu------------------
/********************************************************/
/*							*/
/* Program to synchronise i/o for a program into a file.*/
/* Usage: sio file program 				*/
/*							*/
/********************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#define lenu (unsigned)len

char buffer[128];			/* read/write buffer
					   arbitrary length */
int pipe0[2],pipe1[2],tty,outfile,len;
void exit();	/* this is for lint */
main(argc,argv,envp)
int argc;
char *argv[],*envp[];
{
  if (argc!=3)
  {	/* bad no. of args */
    fprintf(stderr,"Usage: %s file program\n",*argv);
    exit(2);
  }
  if (outfile=creat(*++argv,0666),outfile==EOF)
  {	/* 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 */
  if (fork()) parent(); else child(++argv,envp);
		/* now execute required program */
  exit(0);	/* this is for lint */
}

stop()
{
  while ((len=read(pipe1[0],buffer,128))>0)
  {	/* clear output buffer */
    write(tty,buffer,lenu);	/* NB lenu is a #define ! */
    write(outfile,buffer,lenu);
  }
exit(0);
}

parent()	/* parent writes to outfile */
{
  int (*signal())();	/* this is for lint */
  close(pipe0[0]);		/* enable read from pipe without waiting */
  fcntl(pipe1[0],F_SETFL,fcntl(pipe1[0],F_GETFL,0)|O_NDELAY);
  tty=open("/dev/tty",O_RDWR | O_NDELAY);	/* open to console */
  signal(18,stop);	/* trap program's death */
  for (;;)
  {	/* transfer i/o until program dies */
    len=read(tty,buffer,128);
    write(pipe0[1],buffer,lenu);
    write(outfile,buffer,lenu);
    len=read(pipe1[0],buffer,128);
    if (len==EOF) stop();	/* so it's messy,but it's the
				   only way with O_NDELAY    */
    write(tty,buffer,lenu);
    write(outfile,buffer,lenu);
  }
}

child(argv,envp)
char *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]);
  close(outfile);
  setbuf(stdout,(char *)0);
/* 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?	*/
/* END! Thanks for any help. */

jsdy@hadron.UUCP (Joseph S. D. Yao) (01/02/87)

I am not the Throop, but here's how:

In article <658@bath63.ux63.bath.ac.uk> ma6nrr@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@seismo.{CSS.GOV,ARPA,UUCP}
			jsdy@hadron.COM (not yet domainised)

karl@haddock.UUCP (Karl Heuer) (01/09/87)

In article <404@hadron.UUCP> jsdy@hadron.UUCP (Joseph S. D. Yao) writes:
>>  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.

(Perhaps he means exit(0) is better than falling off the end?)  Btw, SysV lint
doesn't require return; it just wants you to assert /*NOTREACHED*/.

>>  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().

On SysV, O_NDELAY (defined in <fcntl.h>) is appropriate for either open() or
fcntl(); I don't know if there is an ioctl() that uses it.  FNDELAY is only
used in the kernel.  In what sense is 0 not a "real int"?

>>  signal(18,stop);	/* trap program's death */
>... Do you perchance mean SIGCHLD?  Which is  n o t  18!

The most portable name is SIGCLD, which *is* 18 on SysV, and a synonym for
SIGCHLD (which is 20) on BSD.  (And nonexistent on older unixes.)

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