forked from gnachman/iTerm2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Coprocess.m
221 lines (192 loc) · 5.16 KB
/
Coprocess.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//
// Coprocess.m
// iTerm
//
// Created by George Nachman on 9/24/11.
// Copyright 2011 Georgetech. All rights reserved.
//
#import "Coprocess.h"
const int kMaxInputBufferSize = 1024;
const int kMaxOutputBufferSize = 1024;
static NSString *kCoprocessMruKey = @"Coprocess MRU";
@implementation Coprocess
@synthesize pid = pid_;
@synthesize outputFd = outputFd_;
@synthesize inputFd = inputFd_;
@synthesize inputBuffer = inputBuffer_;
@synthesize outputBuffer = outputBuffer_;
@synthesize eof = eof_;
@synthesize mute = mute_;
+ (void)addCommandToMostRecentlyUsed:(NSString *)command
{
NSArray *oldMru = [[NSUserDefaults standardUserDefaults] stringArrayForKey:kCoprocessMruKey];
NSMutableArray *newMru;
if (oldMru) {
newMru = [[oldMru mutableCopy] autorelease];
} else {
newMru = [NSMutableArray array];
}
[newMru removeObject:command];
[newMru insertObject:command atIndex:0];
const int kMaxMru = 20;
while (newMru.count > kMaxMru) {
[newMru removeLastObject];
}
[[NSUserDefaults standardUserDefaults] setObject:newMru forKey:kCoprocessMruKey];
}
+ (NSArray *)mostRecentlyUsedCommands
{
return [[NSUserDefaults standardUserDefaults] stringArrayForKey:kCoprocessMruKey];
}
+ (Coprocess *)launchedCoprocessWithCommand:(NSString *)command
{
[Coprocess addCommandToMostRecentlyUsed:command];
int inputPipe[2];
int outputPipe[2];
pipe(inputPipe);
pipe(outputPipe);
signal(SIGPIPE, SIG_IGN);
pid_t pid = fork();
if (pid == 0) {
signal(SIGCHLD, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
dup2(inputPipe[0], 0);
close(inputPipe[0]);
close(inputPipe[1]);
dup2(outputPipe[1], 1);
close(outputPipe[0]);
close(outputPipe[1]);
for (int i = 3; i < 256; i++) {
if (i != outputPipe[1] && i != inputPipe[0]) {
close(i);
}
}
signal(SIGCHLD, SIG_DFL);
execl("/bin/sh", "/bin/sh", "-c", [command UTF8String], 0);
/* exec error */
fprintf(stderr, "## exec failed %s for command /bin/sh -c %s##\n", strerror(errno), [command UTF8String]);
_exit(-1);
} else if (pid < (pid_t)0) {
[[NSAlert alertWithMessageText:@"Failed to launch coprocess."
defaultButton:@"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:nil] runModal];
return nil;
}
close(inputPipe[0]);
close(outputPipe[1]);
return [Coprocess coprocessWithPid:pid
outputFd:inputPipe[1]
inputFd:outputPipe[0]];
}
+ (Coprocess *)coprocessWithPid:(pid_t)pid
outputFd:(int)outputFd
inputFd:(int)inputFd
{
Coprocess *result = [[[Coprocess alloc] init] autorelease];
result.pid = pid;
result.outputFd = outputFd;
result.inputFd = inputFd;
// Make sure the file descriptors are non-blocking.
int flags = fcntl(outputFd, F_GETFL);
fcntl(outputFd, F_SETFL, flags | O_NONBLOCK);
flags = fcntl(inputFd, F_GETFL);
fcntl(inputFd, F_SETFL, flags | O_NONBLOCK);
return result;
}
- (id)init
{
self = [super init];
if (self) {
inputBuffer_ = [[NSMutableData alloc] init];
outputBuffer_ = [[NSMutableData alloc] init];
}
return self;
}
- (void)dealloc
{
[inputBuffer_ release];
[outputBuffer_ release];
[super dealloc];
}
- (int)write
{
if (self.pid < 0) {
return -1;
}
int fd = [self writeFileDescriptor];
int n = write(fd, [outputBuffer_ bytes], [outputBuffer_ length]);
if (n < 0 && (!(errno == EAGAIN || errno == EINTR))) {
eof_ = YES;
} else if (n == 0) {
eof_ = YES;
} else if (n > 0) {
[outputBuffer_ replaceBytesInRange:NSMakeRange(0, n)
withBytes:""
length:0];
}
return n;
}
- (int)read
{
if (self.pid < 0) {
return -1;
}
int rc = 0;
int fd = [self readFileDescriptor];
while (inputBuffer_.length < kMaxInputBufferSize) {
char buffer[1024];
int n = read(fd, buffer, sizeof(buffer));
if (n == 0) {
rc = 0;
eof_ = YES;
break;
} else if (n < 0) {
if (errno != EAGAIN && errno != EINTR) {
eof_ = YES;
rc = n;
break;
}
} else {
rc += n;
[inputBuffer_ appendBytes:buffer length:n];
}
if (n < sizeof(buffer)) {
break;
}
}
return rc;
}
- (BOOL)wantToRead
{
return self.pid >= 0 && !eof_ && (inputBuffer_.length < kMaxInputBufferSize);
}
- (BOOL)wantToWrite
{
return self.pid >= 0 && !eof_ && (outputBuffer_.length > 0);
}
- (void)mainProcessDidTerminate
{
[self terminate];
}
- (void)terminate
{
if (self.pid > 0) {
kill(self.pid, 15);
close(self.outputFd);
close(self.inputFd);
self.outputFd = -1;
self.inputFd = -1;
self.pid = -1;
}
}
- (int)readFileDescriptor
{
return self.inputFd;
}
- (int)writeFileDescriptor
{
return self.outputFd;
}
@end