This is Subprocess.m in view mode; [Download] [Up]
//====================================================================== // // Portions written by FreemanSoft Inc. // // FreemanSoft disclaims any warranty of any kind, expressed or implied, // as to this source code's fitness for any particular use. // // For more information, use the following electronic mail addresses: // // info@FreemanSoft.com general questions // support@FreemanSoft.com technical questions // //====================================================================== /* Subprocess.m (v10) by Charles L. Oei pty support by Joe Freeman Subprocess Example, Release 2.0 NeXT Computer, Inc. */ #import "Subprocess.h" #import <sgtty.h> // needed to compile under Release 1.0 #import <appkit/nextstd.h> #import <appkit/Application.h> #import <appkit/Panel.h> #define BUFFERSIZE 2048 #define PTY_TEMPLATE "/dev/pty??" #define PTY_LENGTH 11 static void showError(); /*========================================================== * * Private Instance Methods * *==========================================================*/ @interface Subprocess(Private) - childDidExit; - fdHandler:(int)theFd; @end @implementation Subprocess(Private) - childDidExit // cleanup after a child process exits { if (childPid) { DPSRemoveFD(fromChild); close(fromChild); fclose(fpToChild); childPid=0; // specify that child is dead if (delegate && [delegate respondsTo:@selector(subprocessDone)]) [delegate perform:@selector(subprocessDone)]; } return self; } - fdHandler:(int)theFd // DPS handler for output from subprocess { static int count; static char buffer[BUFFERSIZE]; if (((count = read(theFd, buffer, BUFFERSIZE-1)) < 0) || (!count)) { [self childDidExit]; return self; } buffer[count] = '\0'; if (delegate && [delegate respondsTo:@selector(subprocessOutput:)]) [delegate perform:@selector(subprocessOutput:) with:(void *)&buffer]; return self; } @end /*========================================================== * * Private Utility Routines * *==========================================================*/ static void showError (const char *errorString, id theDelegate) // ensure errors never get dropped on the floor { if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)]) [theDelegate perform:@selector(subprocessError:) with:(void *)errorString]; else if (NXApp) // no delegate, but we're running w/in an App NXRunAlertPanel(0, errorString, 0, 0, 0); else perror(errorString); } static void fdHandler (int theFd, id self) // DPS handler for output from subprocess { [self fdHandler:theFd]; } static void getptys (int *master, int *slave) // attempt to setup the ptys { char device[PTY_LENGTH]; char *block, *num; char *blockLoc; // specifies the location of block for the device string char *numLoc; // specifies the pty name with the digit ptyxD char *msLoc; // specifies the master (ptyxx) or slave (ttyxx) struct sgttyb setp = {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)}; struct tchars setc = {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK}; struct ltchars sltc = {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT}; int lset = (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ); int setd = NTTYDISC; strcpy(device, PTY_TEMPLATE); // string constants are not writable blockLoc = &device[ strlen("/dev/pty") ]; numLoc = &device[ strlen("/dev/pty?") ]; msLoc = &device[ strlen("/dev/") ]; for (block = "pqrs"; *block; block++) { *blockLoc = *block; for (num = "0123456789abcdef"; *num; num++) { *numLoc = *num; *master = open(device, O_RDWR); if (*master >= 0) { *msLoc = 't'; *slave = open(device, O_RDWR); if (*slave >= 0) { (void) ioctl(*slave, TIOCSETP, (char *)&setp); (void) ioctl(*slave, TIOCSETC, (char *)&setc); (void) ioctl(*slave, TIOCSETD, (char *)&setd); (void) ioctl(*slave, TIOCSLTC, (char *)&sltc); (void) ioctl(*slave, TIOCLSET, (char *)&lset); return; } } } /* hunting through a bank of ptys */ } /* hunting through blocks of ptys in all the right places */ *master = -1; *slave = -1; } @implementation Subprocess /*========================================================== * * Public Factory Methods * *==========================================================*/ + new:(const char *)subprocessString // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES { return [Subprocess new:subprocessString withDelegate:nil andPtySupport:NO andStdErr:YES]; } + new:(const char *)subprocessString withDelegate:theDelegate andPtySupport:(BOOL)wantsPty andStdErr:(BOOL)wantsStdErr // creates a new instance of Subprocess and corresponding UNIX process { int pipeTo[2]; // for non-Pty support int pipeFrom[2]; static int master; // file descriptor for master/slave pty static int slave; int tty, numFds, fd; // for temporary use int processGroup; int pidChild; // needed because childPid does not exist // until Subprocess is instantiated if (wantsPty) { tty = open("/dev/tty", O_RDWR); getptys(&master,&slave); if (master <= 0 || slave <= 0) { showError("Error grabbing ptys for subprocess.", theDelegate); return self; } // remove the controlling tty if launched from a shell, // but not Workspace; // so that we have job control over the parent application in shell // and so that subprocesses can be restarted in Workspace if ((tty<0) && ((tty = open("/dev/tty", 2))>=0)) { ioctl(tty, TIOCNOTTY, 0); close(tty); } } else { if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0) { showError("Error starting UNIX pipes to subprocess.", theDelegate); return self; } } switch (pidChild = vfork()) { case -1: // error showError("Error starting UNIX vfork of subprocess.", theDelegate); return self; case 0: // child if (wantsPty) { dup2(slave, 0); dup2(slave, 1); if (wantsStdErr) dup2(slave, 2); } else { dup2(pipeTo[0], 0); dup2(pipeFrom[1], 1); if (wantsStdErr) dup2(pipeFrom[1], 2); } 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 self = [super new]; [self setDelegate:theDelegate]; childPid = pidChild; if (wantsPty) { close(slave); fpToChild = fdopen(master, "w"); fromChild = master; } else { close(pipeTo[0]); close(pipeFrom[1]); fpToChild = fdopen(pipeTo[1], "w"); fromChild = pipeFrom[0]; } setbuf(fpToChild, NULL); DPSAddFD( fromChild, (DPSFDProc)fdHandler, (id)self, NX_MODALRESPTHRESHOLD+1); return self; } } /*========================================================== * * Public Instance Methods * *==========================================================*/ - 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; } - terminateInput // effectively sends an EOF to the child process stdin { fclose(fpToChild); return self; } - terminate:sender { if (childPid) { kill(childPid+1, SIGTERM); [self childDidExit]; } return self; } - setDelegate:anObject { delegate = anObject; return self; } - delegate { return delegate; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.