1/**
2 * @file
3 * @date 17.5.2007
4 *
5 * Implementation of class HBOutputRedirect.
6 */
7
8#import "HBOutputRedirect.h"
9
10/// Global pointer to HBOutputRedirect object that manages redirects for stdout.
11static HBOutputRedirect *g_stdoutRedirect = nil;
12
13/// Global pointer to HBOutputRedirect object that manages redirects for stderr.
14static HBOutputRedirect *g_stderrRedirect = nil;
15
16static int stdoutwrite(void *inFD, const char *buffer, int size);
17static int stderrwrite(void *inFD, const char *buffer, int size);
18
19@interface HBOutputRedirect ()
20
21/// Output stream (@c stdout or @c stderr) redirected by this object.
22@property (nonatomic, readonly) FILE *stream;
23
24/// Pointer to old write function for the stream.
25@property (nonatomic, readonly) int (*oldWriteFunc)(void *, const char *, int);
26
27@end
28
29/**
30 * Function that replaces stdout->_write and forwards stdout to g_stdoutRedirect.
31 */
32int	stdoutwrite(void *inFD, const char *buffer, int size)
33{
34    @autoreleasepool
35    {
36        NSString *string = [[NSString alloc] initWithBytes:buffer length:size encoding:NSUTF8StringEncoding];
37        if (string)
38        {
39            [g_stdoutRedirect forwardOutput:string];
40        }
41    }
42    return size;
43}
44
45int	stderrwrite(void *inFD, const char *buffer, int size)
46{
47    @autoreleasepool
48    {
49        NSString *string = [[NSString alloc] initWithBytes:buffer length:size encoding:NSUTF8StringEncoding];
50        if (string)
51        {
52            [g_stderrRedirect forwardOutput:string];
53        }
54    }
55    return size;
56}
57
58@implementation HBOutputRedirect
59
60/**
61 * Returns HBOutputRedirect object used to redirect stdout.
62 */
63+ (instancetype)stdoutRedirect
64{
65	if (!g_stdoutRedirect)
66    {
67		g_stdoutRedirect = [[HBOutputRedirect alloc] initWithStream:stdout type:HBRedirectTypeOutput];
68    }
69	return g_stdoutRedirect;
70}
71
72/**
73 * Returns HBOutputRedirect object used to redirect stderr.
74 */
75+ (instancetype)stderrRedirect
76{
77	if (!g_stderrRedirect)
78    {
79        g_stderrRedirect = [[HBOutputRedirect alloc] initWithStream:stderr type:HBRedirectTypeError];
80    }
81	return g_stderrRedirect;
82}
83
84/**
85 * Private constructor which should not be called from outside. This is used to
86 * initialize the class at @c stdoutRedirect and @c stderrRedirect.
87 *
88 * @param stream	Stream that will be redirected (stdout or stderr).
89 * @param type   	Type that will be called in listeners to redirect the stream.
90 *
91 * @return New HBOutputRedirect object.
92 */
93- (instancetype)initWithStream:(FILE *)stream type:(HBRedirectType)type
94{
95	if (self = [self initWithType:type])
96	{
97		_stream = stream;
98		_oldWriteFunc = NULL;
99	}
100	return self;
101}
102
103/**
104 * Starts redirecting the stream by redirecting its output to function
105 * @c stdoutwrite() or @c stderrwrite(). Old _write function is stored to
106 * @c oldWriteFunc so it can be restored.
107 */
108- (void)startRedirect
109{
110	if (!_oldWriteFunc)
111	{
112		_oldWriteFunc = _stream->_write;
113		_stream->_write = _stream == stdout ? stdoutwrite : stderrwrite;
114	}
115}
116
117/**
118 * Stops redirecting of the stream by returning the stream's _write function
119 * to original.
120 */
121- (void)stopRedirect
122{
123	if (_oldWriteFunc)
124	{
125		_stream->_write = _oldWriteFunc;
126		_oldWriteFunc = NULL;
127	}
128}
129
130@end
131