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 $Log$ ----------------------------------------------------------------------------*/ # import <sys/wait.h> # import <sys/resource.h> # import <appkit/nextstd.h> # import <appkit/Application.h> # import <appkit/Panel.h> # import <nextutils.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." @implementation Subprocess /*---------------------------< INIT/FREE METHODS >---------------------------*/ /* * 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]; 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); } } /*-----------------------------< OTHER METHODS >-----------------------------*/ /* * 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 = %d, w_stopval = %d, " "w_stopsig = %d, 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]; } 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 = EOSTR; [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); } /* * DPS handler for output from subprocess */ static void fdHandler(int theFd, id self) { [self fdHandler:theFd]; } - 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 = EOSTR; [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); } /* * And standard error from subprocess */ static void stderrFdHandler(int theFd, id self) { [self stderrFdHandler:theFd]; } - 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); } /* * 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
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.