[comp.unix.wizards] unix question about stdin

conan@vax1.acs.udel.EDU (Robert B Carroll) (04/22/89)

Once stdin is closed, how do you open it up again so that
all the stdio functions(scanf, gets etc. etc. etc.) will work
via a terminal keyboard? to explain it in more detail, my C
program:

1) reads user info typed in from keyboard
2) does a freopen and acts like a file is now standard input
3) then i need to 'make' standard input be the keyboard so that
   i can get info typed in from the keyboard again.

I'm an 'end-user', so i can't do any super user calls.
any help would be most appreciated. i haven't solved step 3 yet.
please send responses via email:

-- 
conan@vax1.acs.udel.edu
CONAN THE BARBARIAN

ugkamins@sunybcs.uucp (John Kaminski) (04/22/89)

The original post wanted to know how to reconnect the stdin to the terminal
after it had been attached to a file, assumedly through redirection.  What I
just tried on a BSD system, and works:

  fclose(stdin);
  *stdin = *fopen("/dev/tty", "r");

The "*" before stdin is necessary because of the way stdin is defined in
<stdio.h>.  Another method that SHOULD work is akin to the way I've seen
some shell code do it:

  close(0);
  dup2(0, open("/dev/tty", O_RDONLY, 0x700));

Of course, this doesn't check for a failed open() call.  Also, I guess:

  close(0);
  dup(open("/dev/tty", O_RDONLY, 0x700));

SHOULD work.  Note that any of read, write, or read/write should be OK, as
well as many different file modes (last arg to open()).

chris@mimsy.UUCP (Chris Torek) (04/23/89)

In article <5414@cs.Buffalo.EDU> ugkamins@sunybcs.uucp (John Kaminski) writes:
>The original post wanted to know how to reconnect the stdin to the terminal
>after it had been attached to a file, assumedly through redirection.  What I
>just tried on a BSD system, and works:
>
>  fclose(stdin);
>  *stdin = *fopen("/dev/tty", "r");

This is a horrible thing to do.  (It does not work in some SysVs and
SunOSes.)  The original question indicated that the program was to

	read from stdin
	switch to an alternate file
	switch back to stdin

and that this was all done from within a single C program being written
by the questioner.  The simplest approach is:

	main()
	{
		FILE *fp;

		read_commands(stdin);
		if ((fp = fopen(other_file, "r")) == NULL)
			... error ...
		read_commands(fp);
		(void) fclose(fp);
		read_commands(stdin);
	}

If the `switch to a file' is due to a particular command, read_commands()
can maintain its own stack of files (possibly by being recursive).

It is not a good idea to assign to or through any of the `standard'
streams.  Instead, declare your own pointer and make it point to one
of the standard streams.  There is nothing wrong with

	register FILE *fp = stdin;
	... work with fp ...

but there is with

	fileno(stdin) = expression;

and the like.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

guy@auspex.auspex.com (Guy Harris) (04/23/89)

>The original post wanted to know how to reconnect the stdin to the terminal
>after it had been attached to a file, assumedly through redirection.  What I
>just tried on a BSD system, and works:
>
>  fclose(stdin);
>  *stdin = *fopen("/dev/tty", "r");
>
>The "*" before stdin is necessary because of the way stdin is defined in
><stdio.h>.

*Urk* while this may work, I don't think it's *guaranteed* to work.  In
most UNIX standard I/O implementations, a standard I/O stream is
represented by a pointer to an object of type FILE (or of type "struct
_iobuf", which FILE is a #define for).  Said structure can be copied by
assignment; you have to dereference said pointer to do so, which is why
the "*" is needed.

*However*, I don't know whether every standard I/O implementation in the
world would work correctly if you copy one FILE object atop another one.

There's a better way of doing this, namely to declare:

	FILE *input;

and do your reading from "input" rather than "stdin".  This means you
have to use "getc(input)" rather than "getchar()", and the like.  You
first do

	input = stdin;

and all reads from "input" come from the standard input.  You then do

	input = fopen(file, "r");

and - assuming "fopen" doesn't return NULL, due to it being unable to
open "file"; DON'T forget to check for this! - all reads from "input"
will come from the file in question.  Finally, you do

	fclose(file);
	input = stdin;

which will close the file and make all reads from "input" come from the
standard input again.

One advantage of this is that, unlike schemes involving "/dev/tty", it
will work even if you redirect your standad input away from your
terminal; input will come from the file you redirected to, and then from
the file opened by the program, and then go back to the file to which
input was originally redirected.

carroll@s.cs.uiuc.edu (04/24/89)

Having already made myself look like an idiot on a related subject, I'll
try again :-)

I think you are looking at the problem backwards. You want to be able to read
from several files, one of which happens to be stdin. I strongly suggest
that you use generic file manipulations (e.g., fprintf, fgetc, etc.) on a
global (FILE *). Then the solution is simple - init the global to stdin,
later set it to the other file you want to read from, and then when you
are done, set it back to stdin (which is still there). In fact, you could
even write a set of stack routines that would allow you to "push" (FILE *)'s
and retrieve them on EOF without little difficulty. We did this for a low
level programming class - I can send you example code if you want.

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

les@chinet.chi.il.us (Leslie Mikesell) (04/24/89)

In article <3398@udccvax1.acs.udel.EDU> conan@vax1.acs.udel.EDU (Robert B Carroll) writes:
>Once stdin is closed, how do you open it up again so that
>all the stdio functions(scanf, gets etc. etc. etc.) will work
>via a terminal keyboard? to explain it in more detail, my C
>program:

>1) reads user info typed in from keyboard
>2) does a freopen and acts like a file is now standard input
>3) then i need to 'make' standard input be the keyboard so that
>   i can get info typed in from the keyboard again.

One way is to dup(2) the original file descriptor to hold a copy before
the freopen.  To get it back, close(0); dup(copy);.  There might be
some problems with buffered input if you had not read to EOF on the
alternate stream.

Les Mikesell 

gwyn@smoke.BRL.MIL (Doug Gwyn) (04/25/89)

In article <1495@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>*However*, I don't know whether every standard I/O implementation in the
>world would work correctly if you copy one FILE object atop another one.

I can think of several ways in which it could fail,
but in any case it's a bad idea because it "orphans" any resources
that were previously associated with the stream.