Jason
Jason

Reputation: 2671

How to direct gnu readline away from stdout

So I wanted to make a program that accepts input from stdin through re-direction or interactive input. getline can achieve the re-direction read, but I wanted to have all the nice features of readline for the interactive input. The program's purpose is to manipulate text through a language and output the results to stdout (similar to what sed does). The issue, is that I cannot do my_prog > output.txt with readline because whatever was entered into readline the output of readline goes into that file, and I cannot even see it. Currently, I have a workaround where I just send the output of readline to stderr using rl_outstream. This gives me the behavior I am looking for, but feels like a hack when there is probably a more straight-forward solution. A good example of what I am looking for is what python does.

python > output.txt
>>> print 'hello'
>>> exit()
cat output.txt
hello

Here is some code to demonstrate what I'm doing...

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "readline/readline.h"

int main(int argc, char** argv)
{
    char* from_stdin = malloc(512);
    char* line = NULL;

    rl_outstream = stderr;

    if(isatty(STDIN_FILENO)) {
        do {
            line = readline("");
            if(line) {
                strcat(from_stdin, line);
                strcat(from_stdin, "\n");
            } else {
                break;
            }
        } while(1);
    } else {
        size_t n = 0;
        while(getline(&line, &n, stdin) != -1)
            strcat(from_stdin, line);
    }
    puts(from_stdin);
}

Patch for accepted solution:

--rl_outstream = stderr;
++FILE* more_hacky = fopen("/dev/tty", "w");
++rl_outstream = more_hacky;

I agree about this being more hacky. I will probably leave my code as is, but this would leave stderr more "pure" for errors if I choose.

Upvotes: 3

Views: 693

Answers (1)

For what it is worth, CPython does use STDERR_FILENO as the output file descriptor for Readline iff !isatty(STDOUT_FILENO) a fact which you can verify with strace and alike.

Without redirection we get

% strace -o strace.out python3 && grep '>>>' strace.out
Python 3.6.7 (default, Oct 22 2018, 11:32:17) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> ^D
write(1, ">>> ", 4)                     = 4

and with redirection

% strace -o strace.out python3 > python.out && grep '>>>' strace.out
Python 3.6.7 (default, Oct 22 2018, 11:32:17) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> ^D
write(2, ">>> ", 4)                     = 4

The alternative would be opening /dev/tty for the output stream but I feel that that would be more, not less, hacky.

Upvotes: 2

Related Questions