ftp.nice.ch/pub/next/tools/archiver/JumpBack.4.s.tar.gz#/JumpBack_4/Subprocess.m

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.