tags:

views:

283

answers:

2

how to capture the command line output in a window using ncurses?

Suppose I am excecuting a command like "ls" I want to print that output in a specific window which is designed in ncurses. I am new to ncurses.help me.Thanks in advance

A: 

One thing I can think of is using system() to execute the command, redirecting its output to a temp file:

system("ls > temp");

Then opening the file temp, reading its content and displaying it on the window.

Not an elegant solution, but works.

codaddict
A: 

The more elegant solution might be to implement the redirect within your program. Look into the dup() and dup2() system calls (see the dup(2) manpage). So, what you would want to do is (this is essentially what the shell called by system() ends up doing):

Code Snippet:

char *tmpname;  
int tmpfile;  
pid_t pid;  
int r;  

tmpname = strdup("/tmp/ls_out_XXXXXX");  
assert(tmpname);  
tmpfile = mkstemp(tmpname);  
assert(tmpfile &gt= 0);  
pid = fork();  
if (pid == 0) { // child process  
    r = dup2(STDOUT_FILENO, tmpfile);  
    assert(r == STDOUT_FILENO);  
    execl("/bin/ls", "ls", NULL);  
    assert(0);  
} else if (pid > 0) { // parent  
    waitpid(pid, &r, 0);  
    /* you can mmap(2) tmpfile here, and read from it like it was a memory buffer, or  
     * read and write as normal, mmap() is nicer--the kernel handles the buffering  
     * and other memory management hassles.  
     */  
} else {  
    /* fork() failed, bail violently for this example, you can handle differently as  
     * appropriately.  
     */  
    assert(0);  
}  
// tmpfile is the file descriptor for the ls output.  
unlink(tmpname); // file stays around until close(2) for this process only  

For more picky programs (ones that care that they have a terminal for input and output), you'll want to look into pseudo ttys, see the pty(7) manpage. (Or google 'pty'.) This would be needed if you want ls to do its multicolumn pretty-printing (eg, ls will detect it is outputting to a file, and write one filename to a line. If you want ls to do the hard work for you, you'll need a pty. Also, you should be able to set the $LINES and $COLUMNS environment variables after the fork() to get ls to pretty print to your window size--again, assuming you are using a pty. The essential change is that you would delete the tmpfile = mkstemp(...); line and replace that and the surrounding logic with the pty opening logic and expand the dup2() call to handle stdin and stderr as well, dup2()ing them from the pty file handles).

If the user can execute arbitrary programs in the window, you'll want to be careful of ncurses programs--ncurses translates the move() and printw()/addch()/addstr() commands into the appropriate console codes, so blindly printing the output of ncurses programs will stomp your program's output and ignore your window location. GNU screen is a good example to look into for how to handle this--it implements a VT100 terminal emulator to catch the ncurses codes, and implements its own 'screen' terminal with its own termcap/terminfo entries. Screen's subprograms are run in pseudo-terminals. (xterm and other terminal emulators perform a similar trick.)

Final note: I haven't compiled the above code. It may have small typos, but should be generally correct. If you mmap(), make sure to munmap(). Also, after you are done with the ls output, you'll want to close(tmpfile). unlink() might be able to go much earlier in the code, or right before the close() call--depends on if you want people to see the output your playing with--I usually put the unlink() directly after the mkstemp() call--this prevents the kernel from writing the file back to disk if the tmp directory is disk backed (this is less and less common thanks to tmpfs). Also, you'll want to free(tmpname) after you unlink() to keep from leaking memory. The strdup() is necessary, as tmpname is modified by mkstemp().

Jeff