Matthew Mellott
Matthew Mellott

Reputation: 176

ncurses program not working correctly when used for command substitution

I would like to be able to call an ncurses based program via command substitution so its output can be passed as a command line argument to the another program. For example, this is easy to do in bash when the program does not go into curses mode:

echo $(pwd) # should be the same as just calling pwd alone

When I try to do this with my program (whose code follows below), curses mode is never entered, and the string "test" is never printed. It is important to enter curses mode because this is where, theoretically, the user would somehow manipulate the string that is finally printed to stdout (right now that string is just static).

echo $(./a.out) # just prints an empty line

My program will return the string "this is not a test" when run normally after curses mode is entered, "test" is printed to the screen, and the user presses a key.

./a.out # normal run

Here is the offending code:

// test.cpp
#include <ncurses.h>
#include <iostream>
using namespace std;

/* get curses up and running */
void init() {
    initscr(); // start curses mode, might clear screen
    raw(); // disable line buff, and C-z/C-c won't gen sigals; see cbreak()
    keypad(stdscr, TRUE); // enable arrow keys and function keys
    noecho(); // don't echo chars user types
}

/* shut curses down */
void end() {
   endwin(); // end curses mode
}

int main()
{
    init();
    printw("test");
    getch();
    end();

    cout << "this is not a test" << endl;
    return 0;
}

I compile with this command:

g++ test.cpp -lcurses

Thank you for your help!

Upvotes: 1

Views: 2207

Answers (1)

rici
rici

Reputation: 241721

Here's a simple solution, using newterm:

#define _XOPEN_SOURCE
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    // start curses mode
    SCREEN* s = NULL;
    FILE* out = stdout;
    if(!isatty(fileno(stdout))) {
        out = fopen("/dev/tty", "w");
        // Should really test `out` to make sure that worked.
        setbuf(out, NULL);
    }
    // Here, we don't worry about the case where stdin has been
    // redirected, but we could do something similar to out
    // for input, opening "/dev/tty" in mode "r" for in if necessary.
    s = newterm(NULL, out, stdin);

    printw("test");
    getch();

    endwin(); // end curses mode
    delscreen(s);
    puts("/home/matt");
    return 0;
}

Upvotes: 2

Related Questions