carroll@s.cs.uiuc.edu (04/17/89)
I must be missing something - given that FILE *my_file; has been properly set up (with fopen(), no errors, etc.), why can't you switch stdin by having another variable FILE *tmp; and doing tmp = stdin; stdin = my_file; 'stdin' is declared as a pointer, and so setting the pointer to point at a different FILE I/O block should cause routines that use it to read from that file instead. You can then restore by stdin = tmp; Mr. Salz indicated that things are more complex than this. Is this because of library routines with file descriptor 0 wired in, or because file info is kept in places other than the FILE I/O block stdin points to? While this might not work in all cases, it seems to me it should in the original case, if twiddling the file descriptor in the block works. (i.e., it must not be hard-wired and must look in the FILE I/O block for things). Alan M. Carroll "And then you say, carroll@s.cs.uiuc.edu We have the Moon, so now the Stars..." - YES CS Grad / U of Ill @ Urbana ...{ucbvax,pur-ee,convex}!s.cs.uiuc.edu!carroll
kremer@cs.odu.edu (Lloyd Kremer) (04/19/89)
In article <207600018@s.cs.uiuc.edu> carroll@s.cs.uiuc.edu (Alan M. Carroll) writes: >I must be missing something - given that >FILE *my_file; >has been properly set up (with fopen(), no errors, etc.), why can't you >switch stdin by having another variable >FILE *tmp; >and doing >tmp = stdin; stdin = my_file; There are several concepts missing here. Although any discussion of I/O is, strictly speaking, not relevant to the C language, in practice almost every C program does some I/O, and hopefully the commonly used interfaces are sufficiently consistent across operating systems, at least conceptually, to make a discussion here useful. When I say "conceptually," I mean that even if it isn't really implemented in this way, if you assume that it is, your program will work properly in all cases. In virtually any system that has a UNIX ancestry or that attempts to emulate the UNIX I/O methodology, the following should be conceptually correct. The names of the various internal objects may vary or may not be defined. Low level I/O consists of a number (often 20) of integer file descriptors that can be returned from low level I/O calls such as open, creat, and dup. In some systems the symbol _NFILE is #define'd as this maximum number of open files. High level I/O consists of a low level file combined with associated buffering. The buffering avoids the necessity of a system I/O call to process every character. A FILE is typedef'd or #define'd as a struct containing a low level file descriptor and a few other members pertaining to the buffering (type of buffering, pointers to the buffer, count of characters in the buffer, read/write capability, error flags, etc.). The first three FILEs are normally inherited from the parent process and are provided pre-opened. They are open to the same things and in the same modes as they were in the parent. There is an array of _NFILE FILEs often called _iob or some similar name. The names stdin, stdout, and stderr are usually #define'd as the addresses of the first three of these FILEs (structs). #define stdin (&_iob[0]) #define stdout (&_iob[1]) #define stderr (&_iob[2]) Hence stdin cannot, in general, be used as an lvalue. This is the reason that "changing stdin" is, in general, non-trivial. Stdin cannot be changed; it's the address of an absolute location in memory; it's immovable. When we speak of "changing stdin", we mean changing the *contents* of the structure referenced by stdin. This involves clearing out the previous contents properly, with fflushing to preclude any data loss, and then opening the new file such that *stdin (_iob[0]) will be selected as its FILE structure, the FILE structure will contain file descriptor 0 (this is not automatic; it must be arranged), the file descriptor will be validly open, it will be open for reading, the FILE will be set for reading ("r"), and all the other structure members will be properly and consistently initialized for the new stream. I have found that programmers who perform surgery on stdio without due regard for these considerations produce programs whose I/O sort of works most of the time, but suffers occasional lost data, misdirected data, invalid file descriptor problems, and memory errors. Moral: be sure you understand both low level and high level I/O, and the relationships between them, before you start rewiring them in the middle of an executable. -- Lloyd Kremer Brooks Financial Systems ...!uunet!xanth!brooks!lloyd Have terminal...will hack!
bobmon@iuvax.cs.indiana.edu (RAMontante) (04/19/89)
I've approached this "changing stdin" idea from the other way around:
all my I/O calls are of the form
fgets(buffer, size, MyIn);
fputs(string, MyOut);
and the initialization logic is sort of like:
if (input_name_supplied) {
MyIn = fopen(input_name,"r");
/* what? me, error-check? */
} else
MyIn = stdin;
/* likewise for output */
For what kinds of situation is this approach inadequate?
hamilton@osiris.cso.uiuc.edu (04/26/89)
rds95@leah.Albany.Edu says: > 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. i would do it like this: void fexchange(a, b) FILE *a; FILE *b; { FILE tmp; tmp = *a; *a = *b; *b = tmp; } FILE *my_file; /* open my_file */ /* exchange stdin & my_file */ fexchange(stdin, my_file); /* ... use stdin ... */ /* restore stdin */ fexchange(stdin, my_file); fclose(my_file); i've been using this technique for years in an application where i wanted a nested "#include" capability for interactive input. wayne hamilton U of Il and US Army Corps of Engineers CERL UUCP: {convex,uunet}!uiucuxc!osiris!hamilton ARPA: hamilton@osiris.cso.uiuc.edu USMail: Box 476, Urbana, IL 61801 CSNET: hamilton%osiris@uiuc.csnet Phone: (217)333-8703
chris@mimsy.UUCP (Chris Torek) (04/27/89)
In article <1239500006@osiris.cso.uiuc.edu> hamilton@osiris.cso.uiuc.edu writes: >void fexchange(a, b) >FILE *a; >FILE *b; >{ > FILE tmp; > > tmp = *a; > *a = *b; > *b = tmp; >} This is not a good idea. Consider the following (legal) extraction from a hypothetical implementation of stdio: stdio.h: typedef struct _file { int putc_freespace; int getc_unconsumed; unsigned char *putc_ptr; unsigned char *getc_ptr; unsigned char *buffer; int bufsize; int flags; } FILE; FILE _iob[20]; #define stdin (&_iob[0]) #define stdout (&_iob[1]) #define stderr (&_iob[2]) #define fileno(fp) ((fp) - _iob) The result of fexchange(stdin, f), in this implementation, is to replace the counts and pointers for file descriptor 0 (stdin) with those for file descriptor fileno(f) such that when stdin->getc_unconsumed (copied from f->getc_unconsumed) goes to zero, the program reads from file descriptor 0 ... stdin! The only reason fexchange() works in existing stdio implementations is that they happen to store the file descriptor in the FILE structure (rather than making it implicit, as above). Some SysV implementations store some of their information outside the FILE structure, however, making this doubly dangerous. (Storing important information outside the FILE objects is not illegal, merely stupid.) -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris