Search code examples
c++cincat

Should I handle multiple instances of cin / stdin?


Below is a little program in C++ which is supposed to act as the cat linux binutil: it gets one or several inputs as detailed in the command line arguments (possibly specifying stdin via '-') and copy them onto the standard output. Unfortunately, it shows an unintended behaviour that I cannot understand the root causes of...

Upon the following command

./ccat - test.text

I hit CTRL-D directly without passing any character. I would expect the program to display anyway the content of test.txt, but instead, the program exits without passing any more characters onto the standard output stream.

Any idea on how I should correct my code below to have the correct behaviour in this situation? Should I handle multiple instances of the standard streams (cin, cout...)? If so, do you know how this can be achieved in C++?

Thank you in advance.

/**** ccat.cpp ****/

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

int main(int argc, char **argv) {
    if (argc <= 1) {
        cout << cin.rdbuf();
    } else {
        vector<string> inputs;
        for (int i=1; i<argc; ++i) inputs.push_back(argv[i]);

        for (auto &in: inputs) {
            if (in == "-" || in == "--") {
                cout << cin.rdbuf();
            }
            else {
                ifstream *fd = new ifstream(in);
                if (!fd->is_open()) cerr << "Cannot open file \'" << in << "\'\n";
                else cout << fd->rdbuf();
                delete fd;
            }
        }
    }

    return 0;
}

I tried the following commands in sequence:

$ ./ccat > test.txt
Let's try this text.
I would expect a correct behaviour.
$ ./ccat - test.txt # I hit CTRL-D directly without passing any character first
$ ./ccat - test.txt
But when I add some characters before hitting CTRL-D... This works fine.
But when I add some characters before hitting CTRL-D... This works fine.
Let's try this text.
I would expect a correct behaviour.

As the example shows, I would expect in any of the two cases (last two shell prompts) that test.txt gets displayed onto the standard output, but this occurs only if I inject characters through the standard input first. Hitting CTRL-D straight away makes the program exit prematurely.


Solution

  • That's overload 10 here;

    basic_ostream& operator<<( std::basic_streambuf<CharT, Traits>* sb );
    

    and it says

    If no characters were inserted, executes setstate(failbit).

    In other words, cout is now in an error state and will not output anything.

    Doing

    cout.clear();
    

    first of all in the else branch, or last of all in the if branch, should do it.

    Note that sending end-of-file to standard input is usually not something you can recover or "restart" from, so you might only be able to use one standard input "section".