cpp-terminal icon indicating copy to clipboard operation
cpp-terminal copied to clipboard

`std::cin` doesn't read pipe redirection

Open wmarini opened this issue 1 year ago • 8 comments

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!

wmarini avatar Oct 24 '23 06:10 wmarini