ftp.nice.ch/pub/next/tools/archiver/Opener.3.4b.Source.sd.tar.gz#/Opener.3.4b.Source.sd/Process.m

This is Process.m in view mode; [Download] [Up]

/*
 * Process -- manage i/o with simple subprocesses.
 * Considerably tweaked relative of /NextDeveloper/Examples/Subprocess.
 * M. J. Hawley
 * Copyright (c) MIT Media Laboratory
 * mike@media-lab.mit.edu
 */
#import "Process.h"

@interface Process(Private)
- childDidExit;
- fdHandler:(int)theFd;
@end

static void
showError (const char *s, id delegate){ // ensure errors never get lost
    if (delegate && [delegate respondsTo:@selector(processError:)])
		[delegate perform:@selector(processError:) with:(void *)s];
    else if (NXApp)	// no delegate, but we're running w/in an App
		NXRunAlertPanel(0, s, 0, 0, 0);
    else
		perror(s);
}

static void
fdHandler (int fd, id self) { // DPS handler for output from process
    [self fdHandler:fd];
}

static void
getptys(int *master, int *slave){ // attempt to setup the ptys
    #define	PTY_TEMPLATE "/dev/pty??"
    #define	PTY_LENGTH 11
    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;
}

static int
iwait(fd, timeout)
	long fd;
	unsigned long timeout;  /* in seconds */
/*
 * Wait until 'fd' is ready for reading, or 'timeout'.
 * Return '>=0' when 'fd' is readable, '0' if timeout, '-1' on error.
 * Example: 'iwait(f,0)' polls a file descriptor
 * without blocking and returns true if it's readable;
 * e.g., 'iwait(0,0)' is true when standard input is contains something.
 */
{
	struct timeval t;
	int readfd = 1<<fd;

	t.tv_sec = timeout, t.tv_usec = 0;
	return (int)select(sizeof(int)*8, (fd_set *)&readfd, (fd_set *)0, 
		(fd_set *)0, &t);
}

@implementation Process(Private)

- childDidExit { // cleanup after a child process exits
    if (childPid) {
		if (from) DPSRemoveFD(from);
		close(from);
		if (fpTo) fclose(fpTo);
		if (fpFrom) fclose(fpFrom);
		fpTo = fpFrom = (FILE *)0;
		childPid=0;	// specify that child is dead
		if (delegate && [delegate respondsTo:@selector(processDone)])
			[delegate perform:@selector(processDone)];
    }
    return self;
}

- fdHandler:(int)fd { // DPS handler for output from process
    if (((bufferCount=read(fd,buffer,BUFSIZE-1))<0)||(!bufferCount))
		return [self childDidExit];
    buffer[bufferCount] = '\0';
    if (delegate && [delegate respondsTo:action])
		[delegate perform:action with:(void *)&buffer];
    return self;
}

@end


@implementation Process

+ new:(char *)process delegate:del {
    self = [Process alloc];
    [self init:process delegate:del andPty:YES andStderr:YES];
    return self;
}

+ new:(char *)process delegate:del andPty:(BOOL)wantsPty andStderr:(BOOL)wantsStderr {
    self = [Process alloc];
    [self init:process delegate:del andPty:wantsPty andStderr:wantsStderr];
    return self;
}

- init:(char *)process delegate:del {
    return [self init:process delegate:del andPty:NO andStderr:NO];
}

- init:(char *)process delegate:del andPty:(BOOL)pty andStderr:(BOOL)err {
    // initializes an instance of process and corresponding UNIX process
    int pipeTo[2];
    int pipeFrom[2];
    int	tty, numFds, fd;	// for temporary use
    int processGroup;
    int pidChild;		// needed because childPid does not exist
				// until process is instantiated

    [self setAction:@selector(processOutput:)];
    if (pty){
    	tty = open("/dev/tty", O_RDWR);
		getptys(&masterPty,&slavePty);
		if (masterPty <= 0 || slavePty <= 0) {
			showError("Error grabbing ptys for subprocess.", del);
			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 process.", del);
		return self;
    }
    
    switch (pidChild = vfork()){
		case -1:	// error
			showError("Error starting UNIX vfork of process.", del);
			return self;
	
		case 0:	// child
			if (pty) {
				dup2(slavePty, 0);
				dup2(slavePty, 1);
				if (err) dup2(slavePty, 2);
			} else {
				dup2(pipeTo[0], 0);
				dup2(pipeFrom[1], 1);
				if (err) dup2(pipeFrom[1], 2);
			}
			
			numFds = getdtablesize();
			for (fd=3; fd<numFds; fd++)
				close(fd);
		
			processGroup = getpid();
			ioctl(0, TIOCSPGRP, (char *)&processGroup);
			setpgrp (0, processGroup);
			
			// execl(process, 0);
			execl("/bin/sh", "sh", "-c", process, 0);
			perror("vfork (child)"); // should never gets here tho
			exit(1);
		
			default:	// parent
			[self setDelegate:del];
			childPid = pidChild;
			if (pty){
				close(slavePty);
				fpTo = fdopen(masterPty, "w");
				from = masterPty;
				fpFrom = fdopen(masterPty, "r");
			} else {
				close(pipeTo[0]);
				close(pipeFrom[1]);
			
				fpTo = fdopen(pipeTo[1], "w");
				from = pipeFrom[0];
				fpFrom = fdopen(pipeFrom[0], "r");
				}
			setbuf(fpTo, NULL);
			setbuf(fpFrom, NULL);
			DPSAddFD(from, (DPSFDProc)fdHandler, (id)self, 
				NX_MODALRESPTHRESHOLD+1);
			// printf("added %d, %d, %d [%d %d]\n",from, self, delegate, pipeTo[1], pipeFrom[0]);
			return self;
		}
}

- puts:(char *)s 
{
    fputs(s, fpTo);
    return self;
}


static void
_fgets(s,n,f) 
char *s; int n; FILE *f; 
{
    int fd = fileno(f);
    while (iwait(fd,3)>0){
        if (read(fd,s,1) != 1){
            *s = '\0';
            return;
        }
        if (*s=='\n' || --n <= 0){
            *++s = '\0';
            return ;
        }
        if (*s != '\r') ++s;
    }
    *++s = '\0';
}

- gets:(char *)s :(int)n 
{
    *s = '\0';
    _fgets(s,n,fpFrom);
    return self;
}

- terminate:sender
{
    if (childPid){
		kill(childPid+1, SIGTERM);
		[self childDidExit];
    }
    return self;
}

- setDelegate:anObject 
{
    delegate = anObject;
    return self;
}

- delegate 
{
    return delegate;
}

- setAction:(SEL)theAction 
{
    action = theAction;
    return self;
}
@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.