5 * Implementation of class HBOutputRedirect.
8 #import "HBOutputRedirect.h"
10 /// Global pointer to HBOutputRedirect object that manages redirects for stdout.
11 static HBOutputRedirect *g_stdoutRedirect = nil;
13 /// Global pointer to HBOutputRedirect object that manages redirects for stderr.
14 static HBOutputRedirect *g_stderrRedirect = nil;
16 @interface HBOutputRedirect (Private)
17 - (id)initWithStream:(FILE *)aStream selector:(SEL)aSelector;
18 - (void)startRedirect;
20 - (void)forwardOutput:(NSData *)data;
24 * Function that replaces stdout->_write and forwards stdout to g_stdoutRedirect.
26 int stdoutwrite(void *inFD, const char *buffer, int size)
28 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
29 NSData *data = [[NSData alloc] initWithBytes:buffer length:size];
30 [g_stdoutRedirect performSelectorOnMainThread:@selector(forwardOutput:) withObject:data waitUntilDone:NO];
37 * Function that replaces stderr->_write and forwards stderr to g_stderrRedirect.
39 int stderrwrite(void *inFD, const char *buffer, int size)
41 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
42 NSData *data = [[NSData alloc] initWithBytes:buffer length:size];
43 [g_stderrRedirect performSelectorOnMainThread:@selector(forwardOutput:) withObject:data waitUntilDone:NO];
49 @implementation HBOutputRedirect
52 * Returns HBOutputRedirect object used to redirect stdout.
56 if (!g_stdoutRedirect)
57 g_stdoutRedirect = [[HBOutputRedirect alloc] initWithStream:stdout selector:@selector(stdoutRedirect:)];
59 return g_stdoutRedirect;
63 * Returns HBOutputRedirect object used to redirect stderr.
67 if (!g_stderrRedirect)
68 g_stderrRedirect = [[HBOutputRedirect alloc] initWithStream:stderr selector:@selector(stderrRedirect:)];
70 return g_stderrRedirect;
74 * Adds specified object as listener for this output. Method @c stdoutRedirect:
75 * or @c stderrRedirect: of the listener is called to redirect the output.
77 - (void)addListener:(id)aListener
79 NSAssert2([aListener respondsToSelector:forwardingSelector], @"Object %@ doesn't respond to selector \"%@\"", aListener, NSStringFromSelector(forwardingSelector));
81 if (![listeners containsObject:aListener])
83 [listeners addObject:aListener];
87 if ([listeners count] > 0)
92 * Stops forwarding for this output to the specified listener object.
94 - (void)removeListener:(id)aListener
96 if ([listeners containsObject:aListener])
99 [listeners removeObject:aListener];
102 // If last listener is removed, stop redirecting output and autorelease
103 // self. Remember to set proper global pointer to NULL so the object is
104 // recreated again when needed.
105 if ([listeners count] == 0)
110 if (self == g_stdoutRedirect)
111 g_stdoutRedirect = nil;
112 else if (self == g_stderrRedirect)
113 g_stderrRedirect = nil;
119 @implementation HBOutputRedirect (Private)
122 * Private constructor which should not be called from outside. This is used to
123 * initialize the class at @c stdoutRedirect and @c stderrRedirect.
125 * @param aStream Stream that wil be redirected (stdout or stderr).
126 * @param aSelector Selector that will be called in listeners to redirect the stream.
128 * @return New HBOutputRedirect object.
130 - (id)initWithStream:(FILE *)aStream selector:(SEL)aSelector
132 if (self = [super init])
134 listeners = [[NSMutableSet alloc] init];
135 forwardingSelector = aSelector;
138 lock = [[NSLock alloc] init];
144 * Frees all the listeners and deallocs the object.
153 * Starts redirecting the stream by redirecting its output to function
154 * @c stdoutwrite() or @c stderrwrite(). Old _write function is stored to
155 * @c oldWriteFunc so it can be restored.
157 - (void)startRedirect
161 oldWriteFunc = stream->_write;
162 stream->_write = stream == stdout ? stdoutwrite : stderrwrite;
167 * Stops redirecting of the stream by returning the stream's _write function
174 stream->_write = oldWriteFunc;
180 * Called from @c stdoutwrite() and @c stderrwrite() to forward the output to
183 - (void)forwardOutput:(NSData *)data
185 NSString *string = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
186 [listeners makeObjectsPerformSelector:forwardingSelector withObject:string];