]> code.delx.au - gnu-emacs/blob - test/etags/objc-src/Subprocess.m
Handle Bug#23186
[gnu-emacs] / test / etags / objc-src / Subprocess.m
1 /*
2 Subprocess.m (v10)
3 by Charles L. Oei
4 pty support by Joe Freeman
5 Subprocess Example, Release 2.0
6 NeXT Computer, Inc.
7
8 You may freely copy, distribute and reuse the code in this example.
9 NeXT disclaims any warranty of any kind, expressed or implied, as to
10 its fitness for any particular use.
11 */
12
13 #import "Subprocess.h"
14 // #import <sgtty.h> // needed to compile under Release 1.0
15 #import <appkit/nextstd.h>
16 #import <appkit/Application.h>
17 #import <appkit/Panel.h>
18 #import <sys/wait.h>
19
20 #define PTY_TEMPLATE "/dev/pty??"
21 #define PTY_LENGTH 11
22
23 static void showError();
24
25
26 /*==========================================================
27 *
28 * Private Instance Methods
29 *
30 *==========================================================*/
31
32 @interface Subprocess(Private)
33 - childDidExit;
34 - fdHandler:(int)theFd;
35 @end
36
37 @implementation Subprocess(Private)
38
39 - childDidExit
40 // cleanup after a child process exits
41 {
42 if (childPid)
43 {
44 union wait exitstatus;
45 int waitresult;
46
47 DPSRemoveFD(fromChild);
48 close(fromChild);
49 fclose(fpToChild);
50 // Cleanup zombie processes. (blocking wait is too dangerous here...)
51 waitresult = wait4(childPid, &exitstatus, WNOHANG, NULL);
52 if (waitresult != childPid) {
53 /* XXX should handle this gracefully, e.g, timed entry. */
54 }
55 childPid=0; // specify that child is dead
56 if (delegate)
57 {
58 if ([delegate respondsTo:@selector(subprocessDone:)])
59 [delegate perform:@selector(subprocessDone:) with:self];
60 else if ([delegate respondsTo:@selector(subprocessDone)])
61 [delegate perform:@selector(subprocessDone)];
62 }
63 }
64 return self;
65 }
66
67 - fdHandler:(int)theFd
68 // DPS handler for output from subprocess
69 {
70 if ((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) <= 0)
71 {
72 [self childDidExit];
73 return self;
74 }
75 outputBuffer[bufferCount] = '\0';
76 if (delegate)
77 {
78 if ([delegate respondsTo:@selector(subprocess:output:)])
79 [delegate perform:@selector(subprocess:output:)
80 with:self with:(void *)&outputBuffer];
81 else if ([delegate respondsTo:@selector(subprocessOutput:)])
82 [delegate perform:@selector(subprocessOutput:)
83 with:(void *)&outputBuffer];
84 }
85 return self;
86 }
87
88 @end
89
90
91 /*==========================================================
92 *
93 * Private Utility Routines
94 *
95 *==========================================================*/
96
97 static void
98 showError (const char *errorString, id theDelegate)
99 // ensure errors never get dropped on the floor
100 {
101 if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)])
102 [theDelegate
103 perform:@selector(subprocessError:)
104 with:(void *)errorString];
105 else if (NXApp) // no delegate, but we're running w/in an App
106 NXRunAlertPanel(0, errorString, 0, 0, 0);
107 else
108 perror(errorString);
109 }
110
111 static void
112 fdHandler (int theFd, id self)
113 // DPS handler for output from subprocess
114 {
115 [self fdHandler:theFd];
116 }
117
118 static void
119 getptys (int *master, int *slave)
120 // attempt to setup the ptys
121 {
122 char device[PTY_LENGTH];
123 char *block, *num;
124 char *blockLoc; // specifies the location of block for the device string
125 char *numLoc; // specifies the pty name with the digit ptyxD
126 char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
127
128 struct sgttyb setp =
129 {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
130 struct tchars setc =
131 {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
132 struct ltchars sltc =
133 {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
134 int lset =
135 (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
136 int setd = NTTYDISC;
137
138 strcpy(device, PTY_TEMPLATE); // string constants are not writable
139 blockLoc = &device[ strlen("/dev/pty") ];
140 numLoc = &device[ strlen("/dev/pty?") ];
141 msLoc = &device[ strlen("/dev/") ];
142 for (block = "pqrs"; *block; block++)
143 {
144 *blockLoc = *block;
145 for (num = "0123456789abcdef"; *num; num++)
146 {
147 *numLoc = *num;
148 *master = open(device, O_RDWR);
149 if (*master >= 0)
150 {
151 *msLoc = 't';
152 *slave = open(device, O_RDWR);
153 if (*slave >= 0)
154 {
155 (void) ioctl(*slave, TIOCSETP, (char *)&setp);
156 (void) ioctl(*slave, TIOCSETC, (char *)&setc);
157 (void) ioctl(*slave, TIOCSETD, (char *)&setd);
158 (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
159 (void) ioctl(*slave, TIOCLSET, (char *)&lset);
160 return;
161 } else {
162 // close the master and reset the device
163 // name so that the master opens it properly
164 *msLoc = 'p';
165 close(*master);
166 }
167 }
168 } /* hunting through a bank of ptys */
169 } /* hunting through blocks of ptys in all the right places */
170 *master = -1;
171 *slave = -1;
172 }
173
174
175 @implementation Subprocess
176
177 /*==========================================================
178 *
179 * Public Instance Methods
180 *
181 *==========================================================*/
182
183 - init:(const char *)subprocessString
184 // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
185 {
186 return
187 [self
188 init:subprocessString
189 withDelegate:nil
190 andPtySupport:NO
191 andStdErr:YES];
192 }
193
194 - init:(const char *)subprocessString
195 withDelegate:theDelegate
196 andPtySupport:(BOOL)wantsPty
197 andStdErr:(BOOL)wantsStdErr
198 // initializes an instance of Subprocess and corresponding UNIX process
199 {
200 int pipeTo[2]; // for non-Pty support
201 int pipeFrom[2];
202 int tty, numFds, fd; // for temporary use
203 int processGroup;
204 int pidChild; // needed because childPid does not exist
205 // until Subprocess is instantiated
206
207 if (wantsPty)
208 {
209 tty = open("/dev/tty", O_RDWR);
210 getptys(&masterPty,&slavePty);
211 if (masterPty <= 0 || slavePty <= 0)
212 {
213 showError("Error grabbing ptys for subprocess.", theDelegate);
214 return self;
215 }
216 // remove the controlling tty if launched from a shell,
217 // but not Workspace;
218 // so that we have job control over the parent application in shell
219 // and so that subprocesses can be restarted in Workspace
220 if ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
221 {
222 ioctl(tty, TIOCNOTTY, 0);
223 close(tty);
224 }
225 }
226 else
227 {
228 if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
229 {
230 showError("Error starting UNIX pipes to subprocess.", theDelegate);
231 return self;
232 }
233 }
234
235 switch (pidChild = vfork())
236 {
237 case -1: // error
238 showError("Error starting UNIX vfork of subprocess.", theDelegate);
239 return self;
240
241 case 0: // child
242 if (wantsPty)
243 {
244 dup2(slavePty, 0);
245 dup2(slavePty, 1);
246 if (wantsStdErr)
247 dup2(slavePty, 2);
248 }
249 else
250 {
251 dup2(pipeTo[0], 0);
252 dup2(pipeFrom[1], 1);
253 if (wantsStdErr)
254 dup2(pipeFrom[1], 2);
255 }
256
257 numFds = getdtablesize();
258 for (fd=3; fd<numFds; fd++)
259 close(fd);
260
261 processGroup = getpid();
262 ioctl(0, TIOCSPGRP, (char *)&processGroup);
263 setpgrp (0, processGroup);
264
265 // we exec a /bin/sh so that cmds are easier to specify for the user
266 execl("/bin/sh", "sh", "-c", subprocessString, 0);
267 perror("vfork (child)"); // should never gets here tho
268 exit(1);
269
270 default: // parent
271 [self setDelegate:theDelegate];
272 childPid = pidChild;
273
274 if (wantsPty)
275 {
276 close(slavePty);
277
278 fpToChild = fdopen(masterPty, "w");
279 fromChild = masterPty;
280 }
281 else
282 {
283 close(pipeTo[0]);
284 close(pipeFrom[1]);
285
286 fpToChild = fdopen(pipeTo[1], "w");
287 fromChild = pipeFrom[0];
288 }
289
290 setbuf(fpToChild, NULL);
291 DPSAddFD(
292 fromChild,
293 (DPSFDProc)fdHandler,
294 (id)self,
295 NX_MODALRESPTHRESHOLD+1);
296 return self;
297 }
298 }
299
300 - send:(const char *)string withNewline:(BOOL)wantNewline
301 {
302 fputs(string, fpToChild);
303 if (wantNewline)
304 fputc('\n', fpToChild);
305 return self;
306 }
307
308 - send:(const char *)string
309 {
310 [self send:string withNewline:YES];
311 return self;
312 }
313
314 - terminateInput
315 // effectively sends an EOF to the child process stdin
316 {
317 fclose(fpToChild);
318 return self;
319 }
320
321 - terminate:sender
322 {
323 if (childPid)
324 {
325 //kill(childPid+1, SIGTERM);
326 killpg(childPid, SIGTERM);
327 [self childDidExit];
328 }
329 return self;
330 }
331
332 - setDelegate:anObject
333 {
334 delegate = anObject;
335 return self;
336 }
337
338 - delegate
339 {
340 return delegate;
341 }
342
343 @end