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.