Restore keyboard stdin after reading from pipe

Hello,

I use the following code segment to read and output the text piped to a program. If there is no piped text, the code segment is skipped and then the program continues. What I need to do is restore stdin to the keyboard after reading from the pipe. I use the PeekNamedPipe() and ReadFile() because reading from stdin blocks until it has something, and I don't want that to happen. After reading from the pipe, the program begins to execute, and the main loop can be paused and it prompts for a command. I just can't figure out how to restore input to the keyboard. Obviously the platform is Windows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
   
char char_buffer[1024];
DWORD bytes_read;
DWORD bytes_avail;
DWORD dw;
HANDLE stdin_handle;
bool is_pipe = false;

stdin_handle = GetStdHandle(STD_INPUT_HANDLE);

//This will fail if it is not a console, indicating a pipe.
is_pipe = !GetConsoleMode(stdin_handle, &dw);

if (stdin_handle == INVALID_HANDLE_VALUE) {
    cout << "ERROR: Invalid handle value!\n" << std::endl;
}
else {
    if (is_pipe) {
        while (1) {
            if (::PeekNamedPipe(stdin_handle, NULL, 0, NULL, &bytes_avail, NULL)) {
                if (bytes_avail != 0) {
                    if (! ReadFile(stdin_handle, char_buffer, min(bytes_avail, 1024), &bytes_read, NULL)) {
                        cout << "ReadFile failed!" << endl;
                        break;
                    }
                    else {
                        if (bytes_read == 0) {
                            cout << "Bytes read = 0" << endl;
                            break;
                        }
                        else {
                            for (size_t i = 0; i < bytes_read; ++i) {
                                cout << char_buffer[i];
                            }
                        
                            cout << endl;
                        }
                    }
                }
                else {
                    break;
                }
            }
        }
    }
}

// Attempt to get stdin back to std::cin / keyboard
::CloseHandle(stdin_handle);
bool paused = false;

while (1) {
    // Poll for keyboard if user wants to pause
    if (::GetAsyncKeyState(VK_ESCAPE)) {
        paused = true;
        continue;
    }
    
    // If program paused, ask for a command
    if (paused) {
        cout << "> " << flush;
        std::string input;

        // This is where it fails, doesn't prompt/wait at all
        std::getline(std::cin, input);
    }
    else {
        cout << "Program output here!" << endl
    }
}
Last edited on
Your design sounds fishy to me.

The 'pipe' is the standard input. You can't "restore" it to the keyboard because your program never had it that way to begin with.

Here's the problem: your program is trying to second-guess the user's intent and do stuff that Unix's streams concept saved people from having to do back in the 70's.

So, if your user starts your program by piping something in -- when the source is closed, so should your program!

If you wish to read a file in addition to the user's standard input, then do that specifically -- don't confound the two.

So, while what you want to do is technically possible, it is, in computer terms, immoral to do to your user.

Sorry this isn't the answer you were looking for.
@Duoas,

Thank you very much for your response. You certainly can bring a lot to the table. However I should have provided more information to the question.

This program is to be a driver that interacts with a piece of hardware that generates data. This driver takes in another type of data that is then converted for use. This program needs to be able to take in this data, either from a pipe (output of a another program that will terminate after x amount of time) or a file redirect, then it continues generating data indefinitely until Ctrl-C'd or, if interactivity can be designed, exited with a command. I believe it is possible to allow it to remain interactive afterwards, so that it can then be controlled, allowing a user to pause it's execution with the escape key,type a command to change the data being generated, and then let it continue. I know this can be solved using threads, but I believe there is a better way.

I have seen an example of how to restore input to stdin here, however it is purely linux:
http://stackoverflow.com/questions/12164448/how-to-restore-stdcin-to-keyboard-after-using-pipe

Based on this further explanation, I would hope that someone might be able to provide some assistance instead of jumping to conclusions immediately.
Last edited on
After thinking about it some more I agree that this is kind of a bad design. Although the program needs to continue to run after reading the input, if the user decides to pipe something to it it is expected that keyboard interactivity will not be used, like cat-ing a file and piping it to grep. I think I will ignore this and redesign it to take an argument for interactivity, such as -i, and allow a file input for initial configuration if piping is not desired.
A much better design.

Be careful, though, whom you accuse of jumping to conclusions. Bad design is bad design, and it is obvious from <however many miles away> we are. Which is why I posted an answer I knew you would not like.

If you want to reopen the console on windows, the device name is "CONIN$". The example at stackoverflow would work if you just excange that for "/dev/tty".

Beware, though, that there is more going on behind the scenes than just opening an apparent file. Starting your program with a pipe and then opening CONIN$ might leave some things in your clib improperly initialized. (But I don't know for sure.) JSYK.

Glad to see you've got it sorted into a better design.
Topic archived. No new replies allowed.