Skip to content

std::cin doesn't read pipe redirection #320

Open
@wmarini

Description

@wmarini

Hello,

I'd like to present a use case that I'm currently working on.

I'm trying to create a program that changes the color of some words based on a defined pattern. I'd like to use this program with pipe redirection. Something similar to the following bash script below:

#!/usr/bin/env bash

RED=$(echo -e '\033[1;31m')
CLEAR=$(echo -e '\033[0m')

sed -E "s/\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b/${RED}&${CLEAR}/g"

The direct use of this script is using unamed pipe redirection, like (manually edited to illustrate the months written in red color):

$ cat /tmp/test.txt | ./hlight.bash
\33[0;31mJanJan\33[0m 31st
\33[0;31mFebFeb\33[0m 01st
\33[0;31mMayMay\33[0m 15th

I tried making a C++20 program in Linux (Ubuntu 22.04, xterm) with that same behavior and also using pipe redirection, but it seems that program never reads the pipe input because the cpp-terminal library modifies the std::cin.rdbuf in iostream.cpp and discards the initial stdin content during the program initialization.

void Term::StreamInitializer::init()
{
  if(m_counter++ == 0)
  {
    std::ios_base::Init();
    new(&Term::cout) TOstream(Term::Buffer::Type::FullBuffered, BUFSIZ);
    new(&Term::clog) TOstream(Term::Buffer::Type::LineBuffered, BUFSIZ);
    new(&Term::cerr) TOstream(Term::Buffer::Type::Unbuffered, 0);
    new(&Term::cin) TIstream(Term::Buffer::Type::FullBuffered, BUFSIZ);
    std::cin.rdbuf(Term::cin.rdbuf());
  }
}

void Term::Private::FileInitializer::init()
{   
    // ...
#if defined(_WIN32)
    // ...
#else
    new(&Term::Private::in) InputFileHandler(io_mutex, "/dev/tty");
    new(&Term::Private::out) OutputFileHandler(io_mutex, "/dev/tty");
#endif
    // ...
}

I believe this is a project decision, but instead of always opening a new /dev/tty in file.cpp::Term::Private::FileInitializer::init, it might be useful to reuse the already opened fd = 0 (stdin) when the program uses pipe redirection. A possible solution could be addind a new ctor Term::Private::FileHandler::FileHandler that reuses stdin instead.

To ensure that the program correctly opens a pipe redirection for stdin, you can verify it by using the isatty function. So the Term::Private::FileInitializer::init can be modified to something like:

void Term::Private::FileInitializer::init()
{   
    // ...
#if defined(_WIN32)
    // ...
#else
    if (isatty(STDIN_FILENO)) {
        new(&Term::Private::in) InputFileHandler(io_mutex, stdin);
    } else {
        new(&Term::Private::in) InputFileHandler(io_mutex, "/dev/tty");
    }
    new(&Term::Private::out) OutputFileHandler(io_mutex, "/dev/tty");
#endif
    // ...
}

I aim to enable cpp-terminal to maintain the same expected behavior for std::cin in all scenarios.

Thank you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions