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.