LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 12-24-2005, 02:18 AM   #1
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
Can't get user input when using a pipe to my C++ program


I have a program which has a mode that takes data from standard input, processes it, and sends it to standard output (like many Unix command line tools.) The problem is that when I use a pipe to supply the input, I completely lose the ability to prompt the user for input. Is this normal? Here is a very simple example of what I am doing:
Code:
#include <iostream>

int main()
{
        char StandardInput[1024], UserInput[1024];

        while (std::cin)
        {
        std::cin.read(StandardInput, 1024);
        //process the data
        }

        std::cin.clear();

        std::cerr << "What is your password?\n";
        std::cin.getline(UserInput, 1024);
}
Code:
bash> cat myfile | ./myprogram
I've tried almost everything to "reset" std::cin after taking the pipe input (I've read through pretty much every header that istream is associated with) but I cannot come up with anything. What makes it worse is I'm positive I had this working sometime earlier but can't get it to work anymore. This seems like a shell issue, however I would like the solution to be standard C or C++. Any help is greatly appreciated.
ta0kira
 
Old 12-24-2005, 12:21 PM   #2
elyk1212
Member
 
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186

Rep: Reputation: 30
iostream is wicked picky, your logic looks a little off, try:

Code:
      // Do clearing if the object has an invalid state
      while(!std::cin)
      {
         std::cin.clear();
         std::cin.ignore(25000, '\n');
      }
This seems to work for me, but it may require you to push enter to continue. There are probably better ways to do this, but note: you probably want to do the fixing only when the stream has an issue. Anyhow, you cannot pipe anything to your program if you are not allowing for any parameters. You must have argv, as such:

Code:
int main (int argc, char *argv[])
{

  // Do junk with arguments

  return 0;
}
Anyhow, without main having parameters, you cannot'pipe' anything to it (generally). And I do not think that cin is getting the parameters (unless its functionality extends to uses I am not familiar with)
 
Old 12-24-2005, 12:24 PM   #3
elyk1212
Member
 
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186

Rep: Reputation: 30
Oh yeah, argc is your count of arguments, and argv is a 2d array (or you could call it an array of pointers) to all the arguments.
 
Old 12-24-2005, 02:42 PM   #4
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
Thanks. I've got the argc and argv under control, and I actually use them. The problem is when I replace the standard input on the command line using the shell to redirect output from another program to be used as the input for this program.

The reason std::cin errors out of the loop is because of EOF, whether I used piped input or user input. The difference is that when user input reaches EOF, the next read call prompts the user for more input. When the pipe hits EOF, however, I'm pretty sure the next read call should also prompt the user for input, which it doesn't.

What I think is happening is that when I pipe input, the shell (or kernel) makes the streambuf for std::cin point (indirectly) to the pipe file. When no piped input is provided, the streambuf defaults to console input, which is not available to the program if piped input is used.

I looked on comp.lang.c++.moderated and several people seem to think there is a non-portable solution. In the case of POSIX, it is a tty thing. I need to remain portable, however. I guess I'll leave off that part of the functionality for now until I find either a portable solution or both POSIX and DOS solutions. Thanks.
ta0kira
 
Old 12-24-2005, 05:04 PM   #5
elyk1212
Member
 
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186

Rep: Reputation: 30
But I am still unclear, are you trying to get piped input via cin? Why not use argv instead? Did you output each successive read to see what you are using from the buffer?

I/O usually keeps a buffer ready register that applications poll (after an interrupt is given) to tell the application that there are bytes on the buffer that are ready to be read. On some streams, like in the case of sockets, there is not a definitive EOF. It is just a I/O register that is being mapped to a memory location (or hardware address).

Last edited by elyk1212; 12-24-2005 at 05:14 PM.
 
Old 12-25-2005, 12:38 AM   #6
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
To understand this better, compile the example in my first post. After you do that, execute it with the command "a.out" (or whatever it comes out as.) You should be prompted for input until ctrl-D is pressed, then you should be prompted for a password until enter is pressed. Then type "ls | a.out". This will probably just show the password prompt and skip the user input no matter what code you put in between the read and getline calls. I would like it to wait for user input, however. I am using std::cin for data input from the first input operation (read), and text from the user from the second input (getline.) I am using argv to take options and file names from the command line, but I also need the ability to take things from standard input. Much like this:
Code:
> grep "find me" myfile
 -vs-
> ls -1 | grep "myfile"
In the first case, grep takes the data from the file used as an argument. In the second case, it takes the data piped from ls. When taking data from a pipe in this manner, I also would like to be able to take user input in the same call to my program, which I cannot seem to do.

If you are curious, here is what I've been working on:
http://sourceforge.net/projects/spiral-ta0kira/
ta0kira
 
Old 12-25-2005, 04:04 AM   #7
freegianghu
Member
 
Registered: Oct 2004
Location: somewhere in the street
Distribution: Window$
Posts: 192

Rep: Reputation: 30
I think, you should first check stdin is a tty which isatty(0).

http://msdn.microsoft.com/library/de...fer_events.asp

Just a thought,
GH
 
Old 12-25-2005, 05:56 PM   #8
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
Thanks, however I know already which circumstances it is a tty and which it isn't. The problem is that if the program starts with the buffer pointing to a pipe, it cannot then point to a tty for the purposes of user input.
ta0kira
 
Old 12-26-2005, 01:38 PM   #9
kev82
Senior Member
 
Registered: Apr 2003
Location: Lancaster, England
Distribution: Debian Etch, OS X 10.4
Posts: 1,263

Rep: Reputation: 51
I'm not really an expert on this, but I think after the shell forks a new process that takes piped input, it calls dup2() setting the stdin file descriptor(0) to the reading side of the pipe. If you want to put file descriptor 0 back to stdin of the controlling terminal you'll have to do it yourself.

I don't know enough about istream to see if this would work, but I would suggest that after you have finished reading from the file you do something like dup2(0, open("/dev/tty", O_WRONLY)); Obviously this is platform dependent, so doesn't really solve your problem, but I think it's the best you're gonna get.
 
Old 12-26-2005, 03:39 PM   #10
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
I figured as much. I'm really surprised at how vague the C++ standard is when it comes to streams and buffers. Some functions aren't even required to work in any given implementation of the STL. For some reason I thought that if a pipe ran out of data it would attempt to get user input instead of unconditionally reporting EOF. Thanks for your help.
ta0kira
 
Old 04-05-2006, 03:35 AM   #11
elyk1212
Member
 
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186

Rep: Reputation: 30
I know this thread is old, but I had come across this same issue and here is a fix listed in 'info libc' ,I also checked and it works great.
Code:
      /* example */
      char* id = ctermid(NULL);
      FILE* term = fopen(id, "r");
      char* buffer = (char*)malloc(800);
     fgets(buffer, 80, term);
Invoked as
Code:
$./binary_with_output | ./example
ctermid(char*) returns the current terminal that the program was invoked from, provided it exists. Then, just open a descriptor to it as usual and read from it. It will block on a read as expected without having to redirect stdin to the keyboard I/O.
 
Old 04-14-2006, 11:18 AM   #12
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
Wow, is that really a part of libc, or just the Linux implementation of it? I'll take a look. Thanks.
ta0kira
 
Old 04-14-2006, 11:40 AM   #13
elyk1212
Member
 
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186

Rep: Reputation: 30
Yep, it is in libc, or rather glibc. It is likely a linux part of libc since it refers to calling terminals etc.. but I don't really know for sure.

The idea is you treat the current terminal as a FILE descriptor and open it as usual.

Details are in "info libc"
Under the menu as:
-> Job Control->Functions for Job Control -> Identifying the Controlling Terminal

Also, Gnu.org under online documentation/manuals has glibc.

Last edited by elyk1212; 04-14-2006 at 12:02 PM.
 
Old 04-18-2006, 09:44 AM   #14
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Original Poster
Rep: Reputation: Disabled
Do you happen to know why 'isatty(NULL)' doesn't return true when stdin isn't a terminal? Thanks.
ta0kira
 
Old 04-18-2006, 03:58 PM   #15
elyk1212
Member
 
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186

Rep: Reputation: 30
Quote:
-- Function: int isatty (int FILEDES)
This function returns `1' if FILEDES is a file descriptor
associated with an open terminal device, and 0 otherwise.
My interpretation of this is that the function will return false when the file descriptor passed (or implicit stdin, I guess) is NOT associated with a valid terminal device.

Your question stated:
"...why 'isatty(NULL)' doesn't return true when stdin isn't a terminal?"

Meaning that isatty does not return true when passed a non terminal. This is desired functionality. Did you mean that it returned 1 (undesired functionality)? If it did, perhaps an explicit passing of the stdin may be required.

Please let me know. Thanks! Kyle

Last edited by elyk1212; 04-18-2006 at 04:01 PM.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Shell script pipe input - bash mostly laikos Programming 4 11-09-2008 05:14 PM
C program to see user log on in system and print user with real user name also naveen245 Programming 2 12-21-2005 12:53 AM
pipe for adding UI on console program okeyla Linux - Newbie 1 09-20-2005 08:36 PM
Program received signal SIGPIPE, Broken pipe. grupoapunte Programming 1 06-03-2005 05:49 AM
an open source program that only gives the user a certain amount of time to input? dr_zayus69 Programming 4 05-22-2005 10:56 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 01:16 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration