Thursday, July 28, 2011

Adventures in Redirection III: The Revenge

Note: This blog is deprecated. @synthesize zach has moved to a new home, at zpasternack.org. This blog entry can be found on the new blog here. Please update your links.

After the last two entries on output redirection (here and here, if you missed them), a few folks said, “Zach, this is a Cocoa blog; what’s up with all the crazy C++ junk?” Fair enough. The main reason is that this was what I needed for the particular project I’m working on. Another reason is that C++ is what I did (a *lot*) in my previous life, so hopefully you’ll forgive me if I occasionally run home to mama. :)

In any case, the core code is plain ole C, so I thought, why not make a Cocoa version also? So here we go. The walkthrough is basically the same as the original C++ version, so I won't repeat it here.

@interface StandardOutputRedirector : NSObject
{
    int redirectionPipe[2];
    int oldStandardOutput;
    int oldStandardError;
    BOOL redirecting;
    NSMutableString* redirectedOutput;
}
- (void) startRedirecting;
- (void) stopRedirecting;
- (NSString*) output;
- (void) clearOutput;
@end

@implementation StandardOutputRedirector

enum { READ, WRITE };

#pragma mark - Memory Management

- (id) init
{
    if( (self = [super init]) ) {
        redirectedOutput = [[NSMutableString alloc] init];

        if( pipe( redirectionPipe ) != -1 ) {
            oldStandardOutput = dup( fileno(stdout) );
            oldStandardError = dup( fileno(stderr) );
        }
        setbuf( stdout, NULL );
        setbuf( stderr, NULL );
    }

    return self;
}

- (void) dealloc
{
    if( redirecting  ) {
        [self stopRedirecting];
    }

    if( oldStandardOutput > 0 ) {
        close( oldStandardOutput );
    }
    if( oldStandardError > 0 ) {
        close( oldStandardError );
    }
    if( redirectionPipe[READ] > 0 ) {
        close( redirectionPipe[READ] );
    }
    if( redirectionPipe[WRITE] > 0 ) {
        close( redirectionPipe[WRITE] );
    }

    [redirectedOutput release];

    [super dealloc];
}

#pragma mark - Public methods

- (void) startRedirecting
{
    if( redirecting ) return;

    dup2( redirectionPipe[WRITE], fileno(stdout) );
    dup2( redirectionPipe[WRITE], fileno(stderr) );
    redirecting = true;
}

- (void) stopRedirecting
{
    if( !redirecting ) return;

    dup2( oldStandardOutput, fileno(stdout) );
    dup2( oldStandardError, fileno(stderr) );
    redirecting = false;
}

- (NSString*) output
{
    const size_t bufferSize = 4096;
    char buffer[bufferSize];
    fcntl( redirectionPipe[READ], F_SETFL, O_NONBLOCK ); 
    ssize_t bytesRead = read( redirectionPipe[READ], buffer, bufferSize - 1 );
    while( bytesRead > 0 ) {
        buffer[bytesRead] = 0;
        NSString* tempString = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
        [redirectedOutput appendString:tempString];
        bytesRead = read( redirectionPipe[READ], buffer, bufferSize );
    }

    return [NSString stringWithFormat:@"%@", redirectedOutput];
}

- (void) clearOutput
{
    [redirectedOutput setString:@""];
}

@end


One thing I would note on the Cocoa version is that, this is intended for Mac OS X. I’ve not tried it on iOS, I’ve no idea if it would work, and I actually can’t think of a reason why you’d want to do it on iOS.

This Cocoa version (along with the original C++ implementation) is included with the sample project on github, here.

Saturday, July 23, 2011

ZPAlertView, Redirector on github

Note: This blog is deprecated. @synthesize zach has moved to a new home, at zpasternack.org. This blog entry can be found on the new blog here. Please update your links.

I finally took a minute to put ZPAlertView and Redirector up on github.

The related articles are UIAlertView with Blocks, Revisited and Adventures in Redirection, Part Deux

If you find either one useful, drop me a line and let me know.

Adventures in Redirection, Part Deux

Note: This blog is deprecated. @synthesize zach has moved to a new home, at zpasternack.org. This blog entry can be found on the new blog here. Please update your links.

In the previous chapter, I showed how to redirect cout and cerr, and how it turns out that doing so does not also redirect stdout and stderr. As this turned out not to meet my requirements, I went in search of other methods.
In Googling around, the most common way to redirect stdout/stderr involves calling freopen on them. However, this didn’t work for me, for a few reasons. For one thing, freopen is really geared toward redirecting to a file. You can engage in a bit of trickery to redirect to memory instead, but I wasn’t really interested in going down that path. Worse, it’s completely irreversible; there’s no portable way to restore stdout/stderr once you redirect them via freopen. The ability to arbitrarily redirect and restore output was a must-have for me, so freopen couldn't cut it.
I finally settled on a method which involves calling dup2 to duplicate the stdout/stderr file descriptors. In this way, you can redirect to one end of a pipe, then read from the other end whenever it's convenient. More importantly, you can save off the old file descriptors so that you can restore them.
Let’s get right to it. You might remember the IOutputRedirector interface from last time:
class IOutputRedirector
{
public:
            IOutputRedirector() {}
    virtual ~IOutputRedirector(){}

    virtual void StartRedirecting() = 0;
    virtual void StopRedirecting() = 0;

    virtual std::string GetOutput() = 0;
    virtual void        ClearOutput() = 0;
};
For our new class, we just need a couple of ints to hold our pipe file numbers, a couple of ints for the saved stdout/stderr file numbers, a bool to indicate that we’re redirecting, and a std::string which we redirect into.
class CStdoutRedirector : public IOutputRedirector
{
public:
            CStdoutRedirector();
    virtual ~CStdoutRedirector();

    virtual void StartRedirecting();
    virtual void StopRedirecting();

    virtual std::string GetOutput();
    virtual void        ClearOutput();

private:
    int    _pipe[2];
    int    _oldStdOut;
    int    _oldStdErr;
    bool   _redirecting;
    std::string _redirectedOutput;
};
In the constructor we create a pipe, and call dup to copy the stdout and stderr file descriptors, so that we can restore them later. The setbuf calls are there to disable buffering. stdout is typically line-buffered whilst stderr is typically unbuffered, resulting in the combined output not being properly interleaved otherwise.
CStdoutRedirector::CStdoutRedirector()
: IOutputRedirector()
, _oldStdOut( 0 )
, _oldStdErr( 0 )
, _redirecting( false )
{
    _pipe[READ] = 0;
    _pipe[WRITE] = 0;
    if( pipe( _pipe ) != -1 ) {
        _oldStdOut = dup( fileno(stdout) );
        _oldStdErr = dup( fileno(stderr) );
    }

    setbuf( stdout, NULL );
    setbuf( stderr, NULL );
}
The destructor does some cleanup (stopping the redirection and closing files).
CStdoutRedirector::~CStdoutRedirector()
{
    if( _redirecting ) {
        StopRedirecting();
    }

    if( _oldStdOut > 0 ) {
        close( _oldStdOut );
    }
    if( _oldStdErr > 0 ) {
        close( _oldStdErr );
    }
    if( _pipe[READ] > 0 ) {
        close( _pipe[READ] );
    }
    if( _pipe[WRITE] > 0 ) {
        close( _pipe[WRITE] );
    }
}
StartRedirecting is where part of the magic is. We call dup2 to copy the stdout and stderr file descriptors to the write end of our pipe. This is how the redirection works: after the dup2 calls, any writes to stdout/stderr go instead to the write end of our pipe.
void
CStdoutRedirector::StartRedirecting()
{
    if( _redirecting ) return;

    dup2( _pipe[WRITE], fileno(stdout) );
    dup2( _pipe[WRITE], fileno(stderr) );
    _redirecting = true;
}
StopRedirecting copies the stdout and stderr file descriptors back to the ones we saved in the constructor, resulting in restoring stdout/stderr.
void
CStdoutRedirector::StopRedirecting()
{
    if( !_redirecting ) return;

    dup2( _oldStdOut, fileno(stdout) );
    dup2( _oldStdErr, fileno(stderr) );
    _redirecting = false;
}
GetOutput is where the rest of the magic lies. We call fcntl to set the read end of our pipe to nonblocking mode (otherwise the read call will block if the pipe is empty). Then we call read in a loop into a little temporary buffer, NULL terminate it, and append it to a string, which we then return.
string
CStdoutRedirector::GetOutput()
{
    const size_t bufSize = 4096;
    char buf[bufSize];
    fcntl( _pipe[READ], F_SETFL, O_NONBLOCK );
    ssize_t bytesRead = read( _pipe[READ], buf, bufSize - 1 );
    while( bytesRead > 0 ) {
        buf[bytesRead] = 0;
        _redirectedOutput += buf;
        bytesRead = read( _pipe[READ], buf, bufSize );
    }

    return _redirectedOutput;
}
ClearOutput simply clears out the string.
void
CStdoutRedirector::ClearOutput()
{
    _redirectedOutput.clear();
}
There is one caveat to this method: if the internal buffer fills up, subsequent output (printf, for example) will block until space is freed up in the buffer. This buffer appears to be 16k in size, though I imagine it varies depending on OS. So basically, if you intend to redirect more than 16k at once. well... that won’t work. Calling GetOutput clears the buffer out, so call it often, if you can.
I was thinking that supplying my own larger buffer via setvbuf() would help, but it turns out not to. If anyone has any ideas on how to make this better, let me know.
I would also point out that I've not used this in production code (I am using it in the project I'm working on now, and it seems to be working perfectly; we just haven't shipped it yet).
I'll put the full source and sample project up on github when I have a spare few minutes.
Update: Full source and sample project are on github, here.