Enzo Caceres
Enzo Caceres

Reputation: 519

ifstream: /dev/stdin is not working the same as std::cin

For my formation, an exercise ask us to create a program similar to the linux 'cat' command.

So to read the file, i use an ifstream, and everything work fine for regular file. But not when i try to open /dev/ files like /dev/stdin: the 'enter' is not detected and so, getline really exit only when the fd is being closed (with a CTRL-D).

The problem seems to be around how ifstream or getline handle reading, because with the regular 'read' function from libc, this problem is not to be seen.

Here is my code:

#include <iostream>
#include <string>
#include <fstream>
#include <errno.h>

#ifndef PROGRAM_NAME
# define PROGRAM_NAME "cato9tails"
#endif

int g_exitCode = 0;

void
displayErrno(std::string &file)
{
    if (errno)
    {
        g_exitCode = 1;
        std::cerr << PROGRAM_NAME << ": " << file << ": " << strerror(errno) << std::endl;
    }
}

void
handleStream(std::string file, std::istream &stream)
{
    std::string read;

    stream.peek(); /* try to read: will set fail bit if it is a folder. */
    if (!stream.good())
        displayErrno(file);
    while (stream.good())
    {
        std::getline(stream, read);
        std::cout << read;

        if (stream.eof())
            break;

        std::cout << std::endl;
    }
}

int
main(int argc, char **argv)
{
    if (argc == 1)
        handleStream("", std::cin);
    else
    {
        for (int index = 1; index < argc; index++)
        {
            errno = 0;

            std::string file = std::string(argv[index]);
            std::ifstream stream(file, std::ifstream::in);

            if (stream.is_open())
            {
                handleStream(file, stream);
                stream.close();
            }
            else
                displayErrno(file);
        }
    }

    return (g_exitCode);
}

We can only use method from libcpp.

I have search this problem for a long time, and i only find this post where they seems to have a very similar problem to me: https://github.com/bigartm/bigartm/pull/258#issuecomment-128131871

But found no really usable solution from them.

I tried to do a very ugly solution but... well...:

bool
isUnixStdFile(std::string file)
{
    return (file == "/dev/stdin" || file == "/dev/stdout" || file == "/dev/stderr"
            || file == "/dev/fd/0" || file == "/dev/fd/1" || file == "/dev/fd/2");
}

...
    if (isUnixStdFile(file))
        handleStream(file, std::cin);
    else
    {
        std::ifstream stream(file, std::ifstream::in);
...

As you can see, a lot of files are missing, this can only be called a temporary solution.

Any help would be appreciated!

Upvotes: 4

Views: 364

Answers (1)

kenorb
kenorb

Reputation: 166447

The following code worked for me to deal with /dev/fd files or when using shell substitute syntax:

std::ifstream stream(file_name);
std::cout << "Opening file '" << file_name << "'" << std::endl;
if (stream.fail() || !stream.good())
{
    std::cout << "Error: Failed to open file '" << file_name << "'" << std::endl;
    return false;
}
while (!stream.eof() && stream.good() && stream.peek() != EOF)
{
    std::getline(stream, buffer);
    std::cout << buffer << std::endl;
}
stream.close();

Basically std::getline() fails when content from the special file is not ready yet.

Upvotes: 2

Related Questions