-
Notifications
You must be signed in to change notification settings - Fork 15
/
SdefProcessor.m
215 lines (186 loc) · 6.81 KB
/
SdefProcessor.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
/*
* SdefProcessor.m
* Sdef Editor
*
* Created by Rainbow Team.
* Copyright © 2006 - 2007 Shadow Lab. All rights reserved.
*/
#import "SdefProcessor.h"
#import "SdefDictionary.h"
#import "SdefDocument.h"
#import "SdefEditor.h"
@implementation SdefProcessor
- (id)initWithInput:(id)input {
if (self = [super init]) {
[self setInput:input];
}
return self;
}
- (id)initWithFile:(NSString *)aSdefFile {
return [self initWithInput:aSdefFile];
}
- (id)initWithSdefDocument:(SdefDocument *)aDocument {
return [self initWithInput:aDocument];
}
#pragma mark -
- (NSString *)process {
NSFileHandle *input = nil;
if (!sd_input) {
[NSException raise:NSInternalInconsistencyException format:@"input cannot be nil."];
}
if (!sd_output || ![[NSFileManager defaultManager] fileExistsAtPath:sd_output]) {
[NSException raise:NSInternalInconsistencyException format:@"ouptut cannot be nil."];
}
if (sd_format == kSdefUndefinedFormat) {
[NSException raise:NSInternalInconsistencyException format:@"Undefined output format."];
}
NSTask *task = [[NSTask alloc] init];
sd_msg = [[NSMutableString alloc] init];
// The output of stdout and stderr is sent to a pipe so that we can catch it later
// and send it along to the controller; notice that we don't bother to do anything with stdin,
// so this class isn't as useful for a task that you need to send info to, not just receive.
[task setStandardOutput:[NSPipe pipe]];
[task setStandardError:[task standardOutput]];
// The path to the binary is the first argument that was passed in
NSString *path = [[NSUserDefaults standardUserDefaults] stringForKey:@"SdefSdpToolPath"];
if (path && ![[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSRunAlertPanel(@"sdp not found", @"Set the sdp path into Sdef Editor preferences", @"OK", nil, nil);
return nil;
}
NSMutableArray *args = [[NSMutableArray alloc] init];
if (path) {
[task setLaunchPath:path];
} else {
[task setLaunchPath:@"/usr/bin/xcrun"];
[args addObject:@"sdp"];
}
/* Set format */
NSMutableString *format = [NSMutableString stringWithCapacity:3];
if (sd_format & kSdefResourceFormat) [format appendString:@"a"];
if (sd_format & kSdefScriptSuiteFormat) [format appendString:@"s"];
if (sd_format & kSdefScriptTerminologyFormat) [format appendString:@"t"];
/* Scripting bridge */
if (sd_format & kSdefScriptBridgeHeaderFormat) [format appendString:@"h"];
if (sd_format & kSdefScriptBridgeImplementationFormat) [format appendString:@"m"];
[args addObject:@"-f"];
[args addObject:format];
if ((sd_format & (kSdefScriptBridgeHeaderFormat | kSdefScriptBridgeImplementationFormat)) != 0) {
/* append name parameter */
[args addObject:@"--basename"];
if ([sd_input isKindOfClass:[NSString class]]) {
[args addObject:[[sd_input lastPathComponent] stringByDeletingPathExtension]];
} else {
[args addObject:[[(SdefDocument *)sd_input dictionary] title] ? : @"MyApplication"];
}
}
/* Set Output */
[args addObject:@"-o"];
[args addObject:sd_output];
/* Set Includes */
if ([sd_includes count] > 0) {
for (NSUInteger idx = 0; idx < [sd_includes count]; idx++) {
[args addObject:@"-i"];
[args addObject:[sd_includes objectAtIndex:idx]];
}
}
/* Set version */
if ([self version]) {
[args addObject:@"-V"];
[args addObject:[self version]];
}
/* Set input */
if ([sd_input isKindOfClass:[NSString class]]) {
[args addObject:sd_input];
} else {
[task setStandardInput:[NSPipe pipe]];
input = [[task standardInput] fileHandleForWriting];
}
[task setArguments:args];
// Here we register as an observer of the NSFileHandleReadCompletionNotification, which lets
// us know when there is data waiting for us to grab it in the task's file handle (the pipe
// to which we connected stdout and stderr above). -getData: will be called when there
// is data waiting. The reason we need to do this is because if the file handle gets
// filled up, the task will block waiting to send data and we'll never get anywhere.
// So we have to keep reading data from the file handle as we go.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(getData:)
name: NSFileHandleReadCompletionNotification
object: [[task standardOutput] fileHandleForReading]];
// We tell the file handle to go ahead and read in the background asynchronously, and notify
// us via the callback registered above when we signed up as an observer. The file handle will
// send a NSFileHandleReadCompletionNotification when it has data that is available.
[[[task standardOutput] fileHandleForReading] readInBackgroundAndNotify];
// launch the task asynchronously
[task launch];
if (input) {
NSError *error = nil;
NSData *data = [sd_input dataOfType:ScriptingDefinitionFileType error:&error];
if (data) {
@try {
[input writeData:data];
} @catch (id exception) {
spx_log_exception(exception);
}
}
[input closeFile];
}
[task waitUntilExit];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadCompletionNotification object:nil];
NSData *data;
while ((data = [[[task standardOutput] fileHandleForReading] availableData]) && [data length]) {
[sd_msg appendString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
}
NSString *result = [sd_msg length] ? sd_msg : nil;
sd_msg = nil;
return result;
}
// This method is called asynchronously when data is available from the task's file handle.
// We just pass the data along to the controller as an NSString.
- (void)getData:(NSNotification *)aNotification {
NSData *data = [[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem];
if ([data length]) {
[sd_msg appendString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
}
// we need to schedule the file handle go read more data in the background again.
[[aNotification object] readInBackgroundAndNotify];
}
#pragma mark -
- (id)input {
return sd_input;
}
- (void)setInput:(id)input {
if (sd_input != input) {
sd_input = input;
}
}
- (NSString *)output {
return sd_output;
}
- (void)setOutput:(NSString *)output {
if (sd_output != output) {
sd_output = output;
}
}
- (NSString *)version {
return sd_version;
}
- (void)setVersion:(NSString *)aVersion {
if (sd_version != aVersion) {
sd_version = aVersion;
}
}
- (NSArray *)includes {
return sd_includes;
}
- (void)setIncludes:(NSArray *)includes {
if (sd_includes != includes) {
sd_includes = includes;
}
}
- (SdefProcessorFormat)format {
return sd_format;
}
- (void)setFormat:(SdefProcessorFormat)aFormat {
sd_format = aFormat;
}
@end