Soon after your program starts use the dup(2) call to make duplicate File Descriptors for fd 1 (stdout) and 2 (stderr). Save the returned values for later.
Call close(2) on FD 1 and FD 2.
Call openpty(2) twice. The first master returned should be FD 1 (because it is the first avaiable FD), and the second master should be 2. Save the two slave FDs for later. Don't worry about saving the name parameter. Now whenever your program printf(2) to stdout, or NSLogs to stderr the data will be written to your slave FDs.
Now you must choose wether you want to poll the slave FDs or setup a signal when there is data to be read.
For polling use an NSTimer. In your timer use select(2) on your two slave FDs to see if they have data. If they do read(2) it and then output it to your window.
You can also get the two slave FDs to use non blocking IO (use fcntl(2) to F_SETFL the slave FDs to O_NONBLOCK). Then you don't need select(2), you just read(2) and it will return zero if there is nothing to read.
For signaling, use fcntl(2) to F_SETFL the slave FDs to O_ASYNC. Then use signal(3) to install a signal handler for SIGIO. When your signal handler is called use one of the two methods I describe in the polling section.
If at run time you want to discard all these changes and set everything back to normal do this:
Call close(2) on FD 1, and FD 2.
Call dup(2) on the two FDs saved from step 1 in the first section, above. Do the dup(2) in the correct order so stdout uses FD 1 and stderr uses FD 2.
Now anything written to stdout and stderr will go to the original FDs.