File descriptors and streams and copying thereof.

Lloyd Kremer kremer at cs.odu.edu
Fri Apr 14 07:10:35 AEST 1989



In article <1743 at leah.Albany.Edu> rds95 at leah.Albany.Edu (Robert Seals) writes:

>I want to be able to make "stdin" read from someplace besides, well,
>standard input in the middle of my program, and then go back to where
>it was again.
>
>So what I did was this:
>
>	FILE *my_file;
>
>	/* sucessfully open "my_file" */
>	...
>	stdin->fd = my_file->fd;
>	if (yyparse()) /* stuff*/ else /* stuff */
>	stdin->fd = 0;	/* presume stdin is/was fd 0 */
>	...


Arbitrarily changing the FILE's descriptor without any other treatment of the
stdio stream can confuse stdio terribly.  If the stream's buffer has any
unflushed data, they may be lost or go to the "wrong place."

Although you are performing a mixture of high-level and low-level I/O
operations, your declaration of 'my_file' as a 'FILE *' would suggest that you
ultimately want to do high-level I/O.

Reserving the right to go back to the original stdin complicates things.  If
the work requiring redirected input is performed entirely within a child
process, the best bet would be to fork the process and rehook stdin between
the fork and the exec:

	if(!fork()){
		if(freopen("my_filename", "r", stdin) == (FILE *)0 || exec*(...) == -1){
			perror("");
			exit(1);
		}
	}
	wait(...);

The parent does not need to restore its stdin, since its stdin was never
redirected.

For the more general case in which stdin is to be temporarily changed and
subsequently restored within the same executable, it depends whether only
low-level I/O or high-level I/O is required.  If only low-level I/O
(using integer file descriptors) is required, this should suffice:


	int insave;
	/* for storage of original input file descriptor */


	insave = dup(0);
	/* give me a new file descriptor referring to the same file as
		stdin's original file descriptor */

	close(0);
	/* temporarily close fd 0 */

	if(open("my_filename", O_RDONLY) == -1)
	/* attempt to open your file.  If open succeeds, it will return
		file descriptor 0, since it always returns the earliest
		one, and we just closed 0 */

		forget the whole thing;
	else{
		do work requiring input redirection;
		close(0);
	}
	dup(insave);
	/* get a copy of the saved original input file descriptor.
		Again we know the lowest available file descriptor (0)
		will be returned */

	close(insave);
	/* we don't need the copy any more */

	proceed with non-redirected work;


But since you want high-level structures, we must enhance it a bit:


	int insave;
	/* for storage of original input file descriptor */


	insave = dup(fileno(stdin));
	/* give me a new file descriptor referring to the same file as
		stdin's original file descriptor (which was probably 0
		but let's not assume anything) */

	if(freopen("my_filename", "r", stdin) == (FILE *)0)
	/* fclose (including fflush) stdin, and perhaps fopen your file.
		If fopen succeeds, it will return a pointer to the same
		FILE as the original stdin had, since it always returns
		the earliest one, and stdin refers to _iob[0] */

		forget the whole thing;
	else{
		do work requiring input redirection;
		fclose(stdin);
	}
	fdopen(dup(insave), "r");
	/* get a copy of the saved original input file descriptor and
		associate it with a buffered stdio FILE structure.
		Again we know the lowest file descriptor and the lowest
		FILE (_iob[0]) will be returned */

	close(insave);
	/* we don't need the copy any more */

	proceed with non-redirected work;


					Lloyd Kremer
					Brooks Financial Systems
					{uunet,sun,...}!xanth!brooks!lloyd



More information about the Comp.lang.c mailing list