tags:

views:

1005

answers:

5

I'm trying to do the opposite of

http://stackoverflow.com/questions/1312922/detect-if-stdin-is-a-terminal-or-pipe-in-c-c-qt

I'm running an application that's changing its output format because it detects a pipe on stdout, and I want it to think that it's an interactive terminal so that I get the same output when redirecting.

I was thinking that wrapping it in an expect script or using a proc_open() in php would do it, but it doesn't.

Any ideas out there?

+2  A: 

expect and proc_open() are both going to use a pipe to provide stdin, so that won't make any difference. Since stdin is just a file handle, regardless of whether it's interactive or redirected, you need to cause isatty to think that the handle is a TTY when it's a pipe.

The only way I can think of to do this, without going and looking at the source for multiple implementations of the C standard library is to use LD_PRELOAD to override the default implementation of isatty with a special version that returns 1 for the particular handle that you're using as stdin.

Update:

Looks like implementations of the C standard library use ioctl to store a handle or pointer to terminal information with the file handle, and if you fake a TTY by setting a fake pointer using an ioctl code, anyone who tries to do TTY-specific stuff with that handle will likely crash.

e.g., libc uses tcgetattr which uses ioctl(TCGETS) to store a termios structure, where libbc uses gtty which uses ioctl(TIOCGETP) to store a sgttyb structure.

So I think you could trick libc by doing this:

 ioctl(p, TCSETS, 42);

You might be able to trick one or two implementations, but it won't be portable, and you'll have to be sure the program that's using the handle doesn't do anything TTY-related other than isatty.

Tim Sylvester
Expect uses ptys, not pipes.
Colin Macleod
+2  A: 

I don't know if it's doable from PHP, but if you really need the child process to see a TTY, you can create a PTY.

In C:

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

int main(int argc, char **argv) {
    int master;
    struct winsize win = {
        .ws_col = 80, .ws_row = 24,
        .ws_xpixel = 480, .ws_ypixel = 192,
    };
    pid_t child;

    if (argc < 2) {
        printf("Usage: %s cmd [args...]\n");
        exit(EX_USAGE);
    }

    child = forkpty(&master, NULL, NULL, &win);
    if (child == -1) {
        perror("forkpty failed");
        exit(EX_OSERR);
    }
    if (child == 0) {
        execvp(argv[1], argv + 1);
        perror("exec failed");
        exit(EX_OSERR);
    }

    /* now the child is attached to a real pseudo-TTY instead of a pipe,
     * while the parent can use "master" much like a normal pipe */
}

I was actually under the impression that expect itself does creates a PTY, though.

ephemient
+3  A: 

Aha!

The 'script' command does what we want...

script -c "[executable string]" /dev/null

does the trick!

Very clever!
Dennis Williamson
A: 

The unbuffer script that comes with Expect should handle this ok. If not, the application may be looking at something other than what its output is connected to, eg. what the TERM environment variable is set to.

Colin Macleod
A: 

There's also a pty program included in the sample code of the book "Advanced Programming in the UNIX Environment, Second Edition"!

Here's how to compile pty on Mac OS X:

http://codesnippets.joyent.com/posts/show/8786

frank