Home > Net >  How do I pass the output of a c program which use curses/ncurses to another program?
How do I pass the output of a c program which use curses/ncurses to another program?

Time:05-17

I have a simple c program which use ncurses to display some text on the screen. When running, it will print out hello, when the user press the arrow key up, it will exit the program printing on screen bye see you soon.

I would like to be able to use the output printed by the program, in this case bye see you soon as input of another program, for instance I would like to pass it to grep or cat, like:

./main.out | grep "bye"

similarly of the result I would get if I could pipe it like echo "bye see you soon" | grep "bye"

or alternatively by using something like command substitution $(...).

With my current solution, if I use ./main.out | grep "bye" I get always a blank screen and I am not able to pipe to grep.

Currently looking to a solution for Linux/macOS only.

I would like to know:

  • How I could solve this problem? (I would appreciate a snippet of code)
  • Is atexit the only solution available?

I am learning a bit of c, if you are able to give me a bit more context in the answer I would really appreciate it.

Run the program with:

make all
./main.out
================================== main.c

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

void print_menu(WINDOW *menu_win);

void bye(void)
{
    printf("bye see you soon\n");
}

void print_menu(WINDOW *menu_win)
{
    printf("hi");
    wrefresh(menu_win);
}

int main()
{
    WINDOW *menu_win;
    int c;

    initscr();
    clear();
    noecho();
    cbreak();

    menu_win = newwin(30, 30, 0, 0);
    keypad(menu_win, TRUE);
    refresh();
    print_menu(menu_win);
    
    while (1)
    {
        c = wgetch(menu_win);
        switch (c)
        {
        case KEY_UP:
            endwin();

            int i = atexit(bye);
            if (i != 0)
            {
                fprintf(stderr, "cannot set exit function\n");
                exit(EXIT_FAILURE);
            }
            exit(EXIT_SUCCESS);
            break;
        default:
            refresh();
            break;
        }
    }
    clrtoeol();
    refresh();
    endwin();
    return 0;
}
================================== Makefile

SHELL   = /bin/sh
PROGRAM = main
CC      = gcc
CFLAGS  = -g -O0 -Wall -Werror
LIBS    = -lncurses

all:
    $(CC) ./${PROGRAM}.c -o ./${PROGRAM}.out $(CFLAGS) $(LIBS)

CodePudding user response:

initscr does the equivalent of newterm(NULL, stdout, stdin), which pretty well makes it impossible to also pipe output into some other utility. If you want to do both, you can force ncurses to use /dev/tty for both input and output by replacing initscr() with something like:

    FILE* tty = fopen("/dev/tty", "r ");
    SCREEN* screen = newterm(NULL, tty, tty);
    set_term(screen);

If you do that, you'll want to avoid writing to stdout (if it hasn't been redirected) while ncurses is active, since ncurses assumes that no-one else is writing to the console. You could use isatty() for a fairly conservative test.

I don't really understand why you think it is necessary to use atexit() in your sample program. As soon as you call endwin(), it's safe to write to stdout. (But the printf of Hi in print_menu is problematic.)

CodePudding user response:

Traditionally, programs that use ncurses (or the older curses) don't output anything to stdout -- they only ever use stdin (and actually write to stdin to write to the terminal). So they don't "play nice" with pipes that take the stdout of one process and connect it to the stdin of another process.

If you want to have a program that interacts with a terminal on stdin with ncurses and outputs something to stdout, you can do that -- stdin and stdout don't need to have anything to do with each other -- and that would allow you to pipe the output from stdout somewhere else.

  • Related