This is Subprocess.m in view mode; [Download] [Up]
/*---------------------------------------------------------------------------- Subprocess.m From Subprocess example by Charles L. Oei pty support by Joe Freeman with encouragement from Kristofer Younger Subprocess Example, Release 2.0 NeXT Computer, Inc. You may freely copy, distribute and reuse the code in this example. NeXT disclaims any warranty of any kind, expressed or implied, as to its fitness for any particular use. SYNOPSIS Handles a UNIX process that runs asynchronously. REVISIONS Subprocess.m,v # Revision 1.4 1993/02/01 02:21:31 nwc # Added baud rate button. Cleaned interface. Fixed subprocess bug. # # Revision 1.3 1992/09/15 14:59:23 nwc # Made NeXTSTEP 3.0 compatible. # Added baud rate preference since that now works in uuq. # # Revision 1.2 1992/09/03 16:33:48 nwc # Clean up after Subprocesses. # # Revision 1.1.1.1 1992/08/18 14:34:20 nwc # GENESIS # # Revision 1.1 1992/07/04 03:17:22 nwc # Initial revision # ----------------------------------------------------------------------------*/ #import <sys/wait.h> #import <sys/resource.h> #import <appkit/nextstd.h> #import <appkit/Application.h> #import <appkit/Panel.h> #import "Subprocess.h" extern int wait4(int, union wait *, int, struct rusage *); static void fdHandler(int theFd, id self); static void stderrFdHandler(int theFd, id self); #define PIPE_ERROR "Error starting UNIX pipes to subprocess." #define VFORK_ERROR "Error starting UNIX vfork of subprocess." @interface Subprocess(Private) - childDidExit; - fdHandler:(int)theFd; @end @implementation Subprocess(Private) /* * cleanup after a child process exits */ - childDidExit { union wait w; int status = 0; int thePid; thePid = wait4(childPid, &w, WUNTRACED, NULL); #ifdef DEBUG fprintf(stderr, "%s: wait4() on process id #%d returned %d, with " "w_status = %d, w_retcode = %u, w_stopval = %u, " "w_stopsig = %u, w_termsig = %d\n", [self name], childPid, thePid, w.w_status, w.w_retcode, w.w_stopval, w.w_stopsig, w.w_termsig); #endif if (thePid > 0) { if (WIFEXITED(w)) status = (w.w_status >> 8); else { if (WIFSTOPPED(w)) status = SUBPROCESS_STOPPED; else { if (WIFSIGNALED(w)) status = SUBPROCESS_SIGNALED; } } DPSRemoveFD(fromChild); DPSRemoveFD(stderrFromChild); fclose(fpFromChild); close(fromChild); close(stderrFromChild); fclose(fpToChild); running = NO; [delegate perform:@selector(subprocess:done:) with :self with:(void *)status]; if(markedForFree) [NXApp delayedFree: self]; } return (self); } /* * DPS handler for output from subprocess */ - fdHandler:(int)theFd { char *s, *linep; int bufferCount; bufferCount = read(theFd, outputBuffer + outputBufferLen, BUFSIZ - outputBufferLen); if (bufferCount <= 0) { [self childDidExit]; return (self); } outputBuffer[bufferCount + outputBufferLen] = '\0'; /* * Send lines in the buffer to the delegate */ s = linep = outputBuffer; while (s != NULL) { if ((s = index(linep, '\n')) != NULL) { *s = (char)0; [delegate perform:@selector(subprocess:output:) with :self with:(void *)linep]; linep = s + 1; } } /* * Copy the last part of the line back into the input buffer for next time (incomplete line) */ outputBufferLen = strlen(linep); strncpy(outputBuffer, linep, outputBufferLen); return (self); } @end @implementation Subprocess /* * Cover for the init:withDelegate: with a nil delegate */ - init:(const char *)subprocessString { return ([self init:subprocessString withDelegate:nil]); } - init:(const char *)subprocessString withDelegate:theDelegate { int pipeTo[2]; int pipeFrom[2]; int pipeStderr[2]; /* for stderr to different fd */ int numFds, fd; int processGroup; [super init]; markedForFree = NO; outputBufferLen = 0; stderrBufferLen = 0; [self setDelegate:theDelegate]; if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0 || pipe(pipeStderr) < 0) { [delegate perform:@selector(subprocess:error:) with :self with:(void *)PIPE_ERROR]; return (self); } switch (childPid = vfork()) { case -1: /* error */ [delegate perform:@selector(subprocess:error:) with :self with:(void *)VFORK_ERROR]; return (self); case 0: /* child */ dup2(pipeTo[0], 0); dup2(pipeFrom[1], 1); /* get stdout from process */ dup2(pipeStderr[1], 2); /* get stderr here */ numFds = getdtablesize(); for (fd = 3; fd < numFds; fd++) close(fd); processGroup = getpid(); ioctl(0, TIOCSPGRP, (char *)&processGroup); setpgrp(0,processGroup); /* * we exec a /bin/sh so that cmds are easier to specify for the user */ execl("/bin/sh", "sh", "-c", subprocessString, 0); perror("vfork (child)"); /* should never gets here tho */ exit(1); default: /* parent */ running = YES; close(pipeTo[0]); close(pipeFrom[1]); close(pipeStderr[1]); fpToChild = fdopen(pipeTo[1], "w"); fromChild = pipeFrom[0]; fpFromChild = fdopen(pipeFrom[0], "r"); stderrFromChild = pipeStderr[0]; /* * Set buffering method, also make it use its own buffers */ setbuf(fpToChild, NULL); /* no buffering */ setbuf(fpFromChild, NULL); DPSAddFD(fromChild, (DPSFDProc) fdHandler, (id) self, NX_MODALRESPTHRESHOLD + 1); DPSAddFD(stderrFromChild, (DPSFDProc) stderrFdHandler, (id) self, NX_MODALRESPTHRESHOLD + 1); return (self); } } - stderrFdHandler:(int)theFd { char *s, *linep; int bufferCount; bufferCount = read(theFd, stderrBuffer + stderrBufferLen, BUFSIZ - stderrBufferLen); if (bufferCount <= 0) return (self); stderrBuffer[bufferCount + stderrBufferLen] = '\0'; /* * Send lines in the buffer to the delegate */ s = linep = stderrBuffer; while (s != NULL) { if ((s = index(linep, '\n')) != NULL) { *s = (char)0; [delegate perform:@selector(subprocess:stderrOutput:) with :self with:(void *)linep]; linep = s + 1; } } /* * Copy the last part of the line back into the input buffer for next time (incomplete line) */ stderrBufferLen = strlen(linep); strncpy(stderrBuffer, linep, stderrBufferLen); return (self); } - send:(const char *)string withNewline:(BOOL) wantNewline { fputs(string, fpToChild); if (wantNewline) fputc('\n', fpToChild); return (self); } - send:(const char *)string { [self send:string withNewline:YES]; return (self); } /* * Returns the process id of the process (and therefore the process group * of the job) */ - (int)pid { return (childPid); } - (BOOL) isPaused { return (paused); } - resume:sender { if (paused) { killpg(childPid, SIGCONT); /* resume the process group */ paused = NO; } return (self); } - pause:sender { if (!paused) { killpg(childPid, SIGSTOP); /* pause the process group */ paused = YES; } return (self); } - (BOOL) isRunning { return (running); } - terminate:sender { if (running) killpg(childPid, SIGKILL); return (self); } - free { if(running && !markedForFree) { markedForFree = YES; return [self terminate: self]; } else return [super free]; } /* * effectively sends an EOF to the child process stdin */ - terminateInput { fclose(fpToChild); return (self); } - setDelegate:anObject { delegate = anObject; return (self); } - delegate { return (delegate); } @end @implementation Object(SubprocessDelegate) - subprocess: sender done:(int)exitStatus { return (self); } - subprocess:sender output:(char *)buffer { return (self); } - subprocess:sender stderrOutput:(char *)buffer { return (self); } - subprocess:sender error:(const char *)errorString { if (NXApp) NXRunAlertPanel(0, errorString, 0, 0, 0); else perror(errorString); return (self); } @end typedef struct { @defs(Object) } object; /* * And standard error from subprocess */ static void stderrFdHandler(int theFd, id self) { if(self && ((object *)self)->isa) [self stderrFdHandler:theFd]; } /* * DPS handler for output from subprocess */ static void fdHandler(int theFd, id self) { if(self && ((object *)self)->isa) [self fdHandler:theFd]; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.