Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!csd4.milw.wisc.edu!lll-winken!xanth!kremer From: kremer@cs.odu.edu (Lloyd Kremer) Newsgroups: comp.lang.c Subject: Re: File descriptors and streams and copying thereof. Summary: a suggestion Keywords: Maybe Unix specific; maybe not. Message-ID: <8450@xanth.cs.odu.edu> Date: 13 Apr 89 21:10:35 GMT References: <1743@leah.Albany.Edu> Organization: Old Dominion University, Norfolk, Va. Lines: 120 In article <1743@leah.Albany.Edu> rds95@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