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.