/* Automatically generated by
	VMPluginCodeGenerator * VMMaker.oscog-eem.2510 uuid: 4108a547-dc5c-47ed-9521-d25f5918b426
   from
	UnixOSProcessPlugin VMConstruction-Plugins-OSProcessPlugin.oscog-dtl.65 uuid: c7e17769-a85b-487a-b67d-ae2e7969ccd1
 */
static char __buildInfo[] = "UnixOSProcessPlugin VMConstruction-Plugins-OSProcessPlugin.oscog-dtl.65 uuid: c7e17769-a85b-487a-b67d-ae2e7969ccd1 " __DATE__ ;
/* D T Lewis - UnixOSProcessPlugin.c translated from class
   UnixOSProcessPlugin of OSProcessPlugin version 4.6.4 Cog */



#include "config.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#ifdef __OpenBSD__
# include <sys/signalvar.h>
#endif

/* Default EXPORT macro that does nothing (see comment in sq.h): */
#define EXPORT(returnType) returnType

/* Do not include the entire sq.h file but just those parts needed. */
#include "sqConfig.h"			/* Configuration options */
#include "sqVirtualMachine.h"	/*  The virtual machine proxy definition */
#include "sqPlatformSpecific.h"	/* Platform specific definitions */

#define true 1
#define false 0
#define null 0  /* using 'null' because nil is predefined in Think C */
#ifdef SQUEAK_BUILTIN_PLUGIN
# undef EXPORT
# define EXPORT(returnType) static returnType
#endif

#include "FilePlugin.h"
#include "SocketPlugin.h"
#include "sqaio.h"
#include "sqMemoryAccess.h"


/*** Constants ***/
#define FILEHANDLETYPE FILE *  /* the type of low level stream to be used in a struct SQFile */
#define PrimErrBadArgument 3
#define PrimErrBadNumArgs 5
#define PrimErrNoMemory 9
#define PrimErrNotFound 11
#define PrimErrUnsupported 7
#define SESSIONIDENTIFIERTYPE int


/*** Function Prototypes ***/
static sqInt argumentAtAsType(sqInt classIdentifier);
static void * callocWrappersize(sqInt count, sqInt objectSize);
static sqInt copyBytesFromtolength(void *charArray1, void *charArray2, sqInt len);
static sqInt createPipeForReaderwriter(FILEHANDLETYPE *readerIOStreamPtr, FILEHANDLETYPE *writerIOStreamPtr);
static char * cStringFromString(sqInt aString);
static sqInt cStringasCollection(const char *aCString, sqInt classIdentifier);
static sqInt descriptorTableSize(void);
static void dupToStdErr(sqInt anSQFileDataStructure);
static void dupToStdIn(sqInt anSQFileDataStructure);
static void dupToStdOut(sqInt anSQFileDataStructure);
static sqInt environmentAtAsType(sqInt classIdentifier);
static sqInt environmentAtSymbolAsType(sqInt classIdentifier);
static sqInt fileDescriptorFrom(sqInt aSQFileByteArray);
static FILEHANDLETYPE fileHandleFrom(sqInt sqFileStructByteArray);
static usqIntptr_t fileRecordSize(void);
static SQFile * fileValueOf(sqInt anSQFileRecord);
static char ** fixPointersInArrayOfStringswithOffsets(sqInt flattenedStrings, sqInt offsets);
static sqInt forkAndExecInDirectory(sqInt useSignalHandler);
EXPORT(pid_t) forkSqueak(sqInt useSignalHandler);
static void * forwardSignaltoSemaphoreAt(sqInt sigNum, sqInt semaphoreIndex);
EXPORT(sqInt) getCurrentWorkingDirectoryAsType(sqInt classIdentifier);
static char ** getEnvironmentVector(void);
EXPORT(const char*) getModuleName(void);
static sqInt getStdHandle(sqInt n);
static sqInt getThisSessionIdentifier(void);
static void * handleSignalFunctionAddress(void);
static void handleSignal(int sigNum);
EXPORT(sqInt) initialiseModule(void);
static sqInt initializeModuleForPlatform(void);
static sqInt isNonNullSQFile(sqInt objectPointer);
static sqInt isNullSQSocket(sqInt objectPointer);
static sqInt isSQFileObject(sqInt objectPointer);
static sqInt isSQSocketObject(sqInt objectPointer);
static sqInt isValidFileSession(sqInt objectPointer);
static sqInt isVmThread(void);
static sqInt makePipeForReaderwriter(FILEHANDLETYPE *readerIOStreamPtr, FILEHANDLETYPE *writerIOStreamPtr);
static sqInt maskForThisThreadAndResend(int sigNum);
static sqInt maskSignalForThisThread(int sigNum);
EXPORT(sqInt) moduleUnloaded(char *aModuleName);
static sqInt msg(char *s);
static sqInt needSigaltstack(void);
static sqInt newPthreadTypeByteArray(pthread_t aPthreadType);
static sqInt newSQFileByteArray(void);
static sqInt newSQSocketByteArray(void);
static void ** originalSignalHandlers(void);
static void * pointerFrom(sqInt aByteArray);
EXPORT(sqInt) primitiveArgumentAt(void);
EXPORT(sqInt) primitiveArgumentAtAsBytes(void);
EXPORT(sqInt) primitiveCanReceiveSignals(void);
EXPORT(sqInt) primitiveChdir(void);
EXPORT(sqInt) primitiveCreatePipe(void);
EXPORT(sqInt) primitiveCreatePipeWithSessionIdentifier(void);
EXPORT(sqInt) primitiveDup(sqInt fileDescriptor);
EXPORT(sqInt) primitiveDupTo(sqInt oldFileDescriptor, sqInt newFileDescriptor);
EXPORT(sqInt) primitiveEnvironmentAt(void);
EXPORT(sqInt) primitiveEnvironmentAtAsBytes(void);
EXPORT(sqInt) primitiveEnvironmentAtSymbol(void);
EXPORT(sqInt) primitiveEnvironmentAtSymbolAsBytes(void);
EXPORT(sqInt) primitiveErrorMessageAt(void);
EXPORT(sqInt) primitiveFileProtectionMask(void);
EXPORT(sqInt) primitiveFileStat(void);
EXPORT(sqInt) primitiveFixPointersInArrayOfStrings(void);
EXPORT(sqInt) primitiveForkAndExecInDirectory(void);
EXPORT(sqInt) primitiveForkExec(void);
EXPORT(sqInt) primitiveForkSqueak(void);
EXPORT(sqInt) primitiveForkSqueakWithoutSigHandler(void);
EXPORT(sqInt) primitiveForwardSignalToSemaphore(void);
EXPORT(sqInt) primitiveGetCurrentWorkingDirectory(void);
EXPORT(sqInt) primitiveGetCurrentWorkingDirectoryAsBytes(void);
EXPORT(sqInt) primitiveGetEGid(void);
EXPORT(sqInt) primitiveGetEUid(void);
EXPORT(sqInt) primitiveGetGid(void);
EXPORT(sqInt) primitiveGetPGid(void);
EXPORT(sqInt) primitiveGetPGrp(void);
EXPORT(sqInt) primitiveGetPid(void);
EXPORT(sqInt) primitiveGetPPid(void);
EXPORT(sqInt) primitiveGetSession(void);
EXPORT(sqInt) primitiveGetStdErrHandle(void);
EXPORT(sqInt) primitiveGetStdErrHandleWithSessionIdentifier(void);
EXPORT(sqInt) primitiveGetStdInHandle(void);
EXPORT(sqInt) primitiveGetStdInHandleWithSessionIdentifier(void);
EXPORT(sqInt) primitiveGetStdOutHandle(void);
EXPORT(sqInt) primitiveGetStdOutHandleWithSessionIdentifier(void);
EXPORT(sqInt) primitiveGetThreadID(void);
EXPORT(sqInt) primitiveGetUid(void);
EXPORT(sqInt) primitiveIsAtEndOfFile(void);
EXPORT(sqInt) primitiveKillOnExit(void);
EXPORT(sqInt) primitiveLockFileRegion(void);
EXPORT(sqInt) primitiveMakePipe(void);
EXPORT(sqInt) primitiveMakePipeWithSessionIdentifier(void);
EXPORT(sqInt) primitiveModuleName(void);
EXPORT(sqInt) primitiveNice(void);
EXPORT(sqInt) primitivePutEnv(void);
EXPORT(sqInt) primitiveRealpath(void);
EXPORT(sqInt) primitiveRealpathAsBytes(void);
EXPORT(sqInt) primitiveReapChildProcess(void);
EXPORT(sqInt) primitiveSemaIndexFor(void);
EXPORT(sqInt) primitiveSendSigabrtTo(void);
EXPORT(sqInt) primitiveSendSigalrmTo(void);
EXPORT(sqInt) primitiveSendSigchldTo(void);
EXPORT(sqInt) primitiveSendSigcontTo(void);
EXPORT(sqInt) primitiveSendSighupTo(void);
EXPORT(sqInt) primitiveSendSigintTo(void);
EXPORT(sqInt) primitiveSendSigkillTo(void);
EXPORT(sqInt) primitiveSendSigpipeTo(void);
EXPORT(sqInt) primitiveSendSigquitTo(void);
EXPORT(sqInt) primitiveSendSigstopTo(void);
EXPORT(sqInt) primitiveSendSigtermTo(void);
EXPORT(sqInt) primitiveSendSigusr1To(void);
EXPORT(sqInt) primitiveSendSigusr2To(void);
EXPORT(sqInt) primitiveSetPGid(void);
EXPORT(sqInt) primitiveSetPGrp(void);
EXPORT(sqInt) primitiveSetSemaIndex(void);
EXPORT(sqInt) primitiveSetSid(void);
EXPORT(sqInt) primitiveSigChldNumber(void);
EXPORT(sqInt) primitiveSigHupNumber(void);
EXPORT(sqInt) primitiveSigIntNumber(void);
EXPORT(sqInt) primitiveSigKillNumber(void);
EXPORT(sqInt) primitiveSigPipeNumber(void);
EXPORT(sqInt) primitiveSigQuitNumber(void);
EXPORT(sqInt) primitiveSigTermNumber(void);
EXPORT(sqInt) primitiveSigUsr1Number(void);
EXPORT(sqInt) primitiveSigUsr2Number(void);
EXPORT(sqInt) primitiveSizeOfInt(void);
EXPORT(sqInt) primitiveSizeOfPointer(void);
EXPORT(sqInt) primitiveSQFileFlush(void);
EXPORT(sqInt) primitiveSQFileFlushWithSessionIdentifier(void);
EXPORT(sqInt) primitiveSQFileSetBlocking(void);
EXPORT(sqInt) primitiveSQFileSetBlockingWithSessionIdentifier(void);
EXPORT(sqInt) primitiveSQFileSetNonBlocking(void);
EXPORT(sqInt) primitiveSQFileSetNonBlockingWithSessionIdentifier(void);
EXPORT(sqInt) primitiveSQFileSetUnbuffered(void);
EXPORT(sqInt) primitiveSQFileSetUnbufferedWithSessionIdentifier(void);
EXPORT(sqInt) primitiveTestEndOfFileFlag(void);
EXPORT(sqInt) primitiveTestLockableFileRegion(void);
EXPORT(sqInt) primitiveUnixFileClose(sqInt anIntegerFileNumber);
EXPORT(sqInt) primitiveUnixFileNumber(void);
EXPORT(sqInt) primitiveUnlockFileRegion(void);
EXPORT(sqInt) primitiveUnsetEnv(void);
EXPORT(sqInt) primitiveVersionString(void);
static sqInt realpathAsType(sqInt classIdentifier);
static void reapChildProcess(int sigNum);
static sqInt resendSignal(int sigNum);
static void restoreDefaultSignalHandlers(void);
static sqInt sandboxSecurity(void);
static sqInt securityHeurisitic(void);
static void sendSignalToPids(void);
static sqInt sendSignaltoPid(sqInt sig, sqInt pid);
static SESSIONIDENTIFIERTYPE sessionIdentifierFrom(sqInt aByteArray);
EXPORT(sqInt) setInterpreter(struct VirtualMachine *anInterpreter);
static void setSigChldHandler(void);
static void setSigIntDefaultHandler(void);
static void setSigIntIgnore(void);
static void * setSignalNumberhandler(sqInt signalNumber, void *signalHandlerAddress);
static void setSigPipeDefaultHandler(void);
static sqInt setSigPipeHandler(void);
static void setSigPipeIgnore(void);
EXPORT(sqInt) shutdownModule(void);
static sqInt sigAbrtNumber(void);
static sqInt sigAlrmNumber(void);
static sqInt sigChldNumber(void);
static sqInt sigContNumber(void);
static void * sigDefaultNumber(void);
static void * sigErrorNumber(void);
static void * sigHoldNumber(void);
static sqInt sigHupNumber(void);
static void * sigIgnoreNumber(void);
static sqInt sigIntNumber(void);
static sqInt sigKillNumber(void);
static sqInt signalArraySize(void);
static sqInt sigPipeNumber(void);
static sqInt sigQuitNumber(void);
static sqInt sigStopNumber(void);
static sqInt sigTermNumber(void);
static sqInt sigUsr1Number(void);
static sqInt sigUsr2Number(void);
static usqIntptr_t sizeOfInt(void);
static usqIntptr_t sizeOfPointer(void);
static sqInt sizeOfSession(void);
static int socketDescriptorFrom(sqInt sqSocketOop);
static usqIntptr_t socketRecordSize(void);
static SocketPtr socketValueOf(sqInt anSQSocketRecord);
static sqInt stringFromCString(const char *aCString);
static char * transientCStringFromString(sqInt aString);
static int unixFileNumber(FILEHANDLETYPE fileHandle);
static char * versionString(void);


/*** Variables ***/

#if !defined(SQUEAK_BUILTIN_PLUGIN)
static void * (*arrayValueOf)(sqInt oop);
static sqInt (*byteSizeOf)(sqInt oop);
static sqInt (*checkedIntegerValueOf)(sqInt intOop);
static sqInt (*classArray)(void);
static sqInt (*classByteArray)(void);
static sqInt (*classString)(void);
static sqInt (*failed)(void);
static sqInt (*falseObject)(void);
static void * (*firstIndexableField)(sqInt oop);
static sqInt (*getThisSessionID)(void);
static sqInt (*instantiateClassindexableSize)(sqInt classPointer, sqInt size);
static sqInt (*integerObjectOf)(sqInt value);
static sqInt (*integerValueOf)(sqInt oop);
static void * (*ioLoadFunctionFrom)(char *functionName, char *moduleName);
static sqInt (*isBytes)(sqInt oop);
static sqInt (*isIntegerObject)(sqInt objectPointer);
static sqInt (*methodArgumentCount)(void);
static sqInt (*methodReturnValue)(sqInt oop);
static sqInt (*nilObject)(void);
static sqInt (*pop)(sqInt nItems);
static sqInt (*popthenPush)(sqInt nItems, sqInt oop);
static sqInt (*popRemappableOop)(void);
static sqInt (*primitiveFail)(void);
static sqInt (*primitiveFailFor)(sqInt reasonCode);
static sqInt (*push)(sqInt object);
static sqInt (*pushInteger)(sqInt integerValue);
static sqInt (*pushRemappableOop)(sqInt oop);
static sqInt (*signalSemaphoreWithIndex)(sqInt semaIndex);
static sqInt (*sizeOfSTArrayFromCPrimitive)(void *cPtr);
static sqInt (*stObjectatput)(sqInt array, sqInt index, sqInt value);
static sqInt (*stSizeOf)(sqInt oop);
static sqInt (*stackIntegerValue)(sqInt offset);
static sqInt (*stackObjectValue)(sqInt offset);
static sqInt (*stackValue)(sqInt offset);
static sqInt (*trueObject)(void);
#else /* !defined(SQUEAK_BUILTIN_PLUGIN) */
extern void * arrayValueOf(sqInt oop);
extern sqInt byteSizeOf(sqInt oop);
extern sqInt checkedIntegerValueOf(sqInt intOop);
extern sqInt classArray(void);
extern sqInt classByteArray(void);
extern sqInt classString(void);
extern sqInt failed(void);
extern sqInt falseObject(void);
extern void * firstIndexableField(sqInt oop);
extern sqInt getThisSessionID(void);
extern sqInt instantiateClassindexableSize(sqInt classPointer, sqInt size);
extern sqInt integerObjectOf(sqInt value);
extern sqInt integerValueOf(sqInt oop);
extern void * ioLoadFunctionFrom(char *functionName, char *moduleName);
extern sqInt isBytes(sqInt oop);
#if !defined(isIntegerObject)
extern sqInt isIntegerObject(sqInt objectPointer);
#endif
extern sqInt methodArgumentCount(void);
extern sqInt methodReturnValue(sqInt oop);
extern sqInt nilObject(void);
extern sqInt pop(sqInt nItems);
extern sqInt popthenPush(sqInt nItems, sqInt oop);
extern sqInt popRemappableOop(void);
extern sqInt primitiveFail(void);
extern sqInt primitiveFailFor(sqInt reasonCode);
extern sqInt push(sqInt object);
extern sqInt pushInteger(sqInt integerValue);
extern sqInt pushRemappableOop(sqInt oop);
extern sqInt signalSemaphoreWithIndex(sqInt semaIndex);
extern sqInt sizeOfSTArrayFromCPrimitive(void *cPtr);
extern sqInt stObjectatput(sqInt array, sqInt index, sqInt value);
extern sqInt stSizeOf(sqInt oop);
extern sqInt stackIntegerValue(sqInt offset);
extern sqInt stackObjectValue(sqInt offset);
extern sqInt stackValue(sqInt offset);
extern sqInt trueObject(void);
extern
#endif
struct VirtualMachine* interpreterProxy;
static const char *moduleName =
#ifdef SQUEAK_BUILTIN_PLUGIN
	"UnixOSProcessPlugin VMConstruction-Plugins-OSProcessPlugin.oscog-dtl.65 (i)"
#else
	"UnixOSProcessPlugin VMConstruction-Plugins-OSProcessPlugin.oscog-dtl.65 (e)"
#endif
;
static void *originalSigHandlers[NSIG + 1];
static int osprocessSandboxSecurity;
static pid_t *pidArray = NULL;
static sqInt pidCount;
static unsigned char semaIndices[NSIG + 1];
static sqInt sigChldSemaIndex;
static int sigNumToSend = SIGTERM;
static sqInt useSignalStack;
static pthread_t vmThread;


/*** Macros ***/
#define sessionIdentifierFromSqFile(sqFile) (((SQFile *)(sqFile))->sessionID)



/*	Answer a string containing the OS process argument at index (an Integer)
	in the
	argument list.
 */

	/* UnixOSProcessPlugin>>#argumentAtAsType: */
static sqInt
argumentAtAsType(sqInt classIdentifier)
{
    extern int argCnt;
    extern char **argVec;
    sqInt index;
    sqInt len;
    sqInt newString;
    sqInt s;
    char *sPtr;

	index = stackIntegerValue(0);
	if ((index > argCnt) || (index < 1)) {
		popthenPush(2, nilObject());
	}
	else {
		sPtr = argVec[index - 1];
		/* begin cString:asCollection: */
		len = strlen(sPtr);
		newString = instantiateClassindexableSize(classIdentifier, len);
		strncpy(arrayValueOf(newString), sPtr, len);
		s = newString;
		popthenPush(2, s);
	}
	return 0;
}


/*	Using malloc() and calloc() is something I would like to avoid, since it
	is likely to cause problems some time in the future if somebody redesigns
	object memory allocation. This wrapper just makes it easy to find senders
	of calloc() in my code. -dtl */

	/* OSProcessPlugin>>#callocWrapper:size: */
static void *
callocWrappersize(sqInt count, sqInt objectSize)
{
	return calloc(count, objectSize);
}


/*	self cCode: 'memcpy(charArray2, charArray1, len' */

	/* OSProcessPlugin>>#copyBytesFrom:to:length: */
static sqInt
copyBytesFromtolength(void *charArray1, void *charArray2, sqInt len)
{
	memcpy(charArray2, charArray1, len);
	return 0;
}


/*	Create a pipe and populate the readerIOStream and writerIOStream
	variables. The SIGPIPE handler must have been set before creating the
	pipe. Answer true for
	success, else false.
 */

	/* UnixOSProcessPlugin>>#createPipeForReader:writer: */
static sqInt
createPipeForReaderwriter(FILEHANDLETYPE *readerIOStreamPtr, FILEHANDLETYPE *writerIOStreamPtr)
{
    int filedes[2];

	if ((pipe(filedes)) == -1) {

		/* Translates to a pipe() system call */
		return 0;
	}
	else {
		*writerIOStreamPtr= (FILE *) fdopen (filedes[1], "a");
		*readerIOStreamPtr= (FILE *) fdopen (filedes[0], "r");
		return 1;
	}
}


/*	Answer a new null-terminated C string copied from aString. The C string
	is allocated from the C runtime heap. See transientCStringFromString for
	a version which allocates from object memory.
	Caution: This may invoke the garbage collector. */

	/* OSProcessPlugin>>#cStringFromString: */
static char *
cStringFromString(sqInt aString)
{
    char *cString;
    sqInt len;
    char *sPtr;

	sPtr = arrayValueOf(aString);
	len = sizeOfSTArrayFromCPrimitive(sPtr);

	/* Space for a null terminated C string. */
	cString = callocWrappersize(len + 1, 1);
	(char *) strncpy (cString, sPtr, len);
	return cString;
}


/*	Answer a new collection, usually of type ByteArray or ByteString copied
	from a null-terminated C string. Caution: This may invoke the garbage
	collector. 
 */

	/* OSProcessPlugin>>#cString:asCollection: */
static sqInt
cStringasCollection(const char *aCString, sqInt classIdentifier)
{
    sqInt len;
    sqInt newString;

	len = strlen(aCString);
	newString = instantiateClassindexableSize(classIdentifier, len);
	strncpy(arrayValueOf(newString), aCString, len);
	return newString;
}


/*	dtl:
	Answer the size of the file descriptor table for a process. I am not sure
	of the most portable
	way to do this. If this implementation does not work on your Unix
	platform, try changing
	it to answer the value of FOPEN:=MAX, which will hopefully be defined in
	stdio.h. 
	eem:
	If we know that (e.g.) stdin is open we can use fd=dup(0);close(fd);fd
	instead. 
 */

	/* UnixOSProcessPlugin>>#descriptorTableSize */
static sqInt
descriptorTableSize(void)
{
	return getdtablesize();
}


/*	Dup a file descriptor to allow it to be attached as the standard error
	when we
	exec() a new executable. This is Unix specific, in that it assumes that
	file descriptor
	0 is stdin, 1 is stdout, and 2 is stderr. The dup2() call is used to copy
	the open file
	descriptors into file descriptors 0, 1 and 2 so that the image which we
	execute will
	use them as stdin, stdout, and stderr.
 */

	/* UnixOSProcessPlugin>>#dupToStdErr: */
static void
dupToStdErr(sqInt anSQFileDataStructure)
{
    sqInt filenoToDup;

	/* begin fileDescriptorFrom: */
	if (!((((isBytes(anSQFileDataStructure))
		 && ((byteSizeOf(anSQFileDataStructure)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(anSQFileDataStructure)))))
		 && (isNonNullSQFile(anSQFileDataStructure)))) {
		filenoToDup = -1;
		goto l1;
	}
	filenoToDup = fileno(fileHandleFrom(anSQFileDataStructure));
	l1:	/* end fileDescriptorFrom: */;
	if (!(filenoToDup < 0)) {
		if (!(filenoToDup == 2)) {
			fflush(stderr);
			dup2(filenoToDup, 2);
		}
	}
}


/*	Dup a file descriptor to allow it to be attached as the standard input
	when we
	exec() a new executable. This is Unix specific, in that it assumes that
	file descriptor
	0 is stdin, 1 is stdout, and 2 is stderr. The dup2() call is used to copy
	the open file
	descriptors into file descriptors 0, 1 and 2 so that the image which we
	execute will
	use them as stdin, stdout, and stderr.
 */

	/* UnixOSProcessPlugin>>#dupToStdIn: */
static void
dupToStdIn(sqInt anSQFileDataStructure)
{
    sqInt filenoToDup;

	/* begin fileDescriptorFrom: */
	if (!((((isBytes(anSQFileDataStructure))
		 && ((byteSizeOf(anSQFileDataStructure)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(anSQFileDataStructure)))))
		 && (isNonNullSQFile(anSQFileDataStructure)))) {
		filenoToDup = -1;
		goto l1;
	}
	filenoToDup = fileno(fileHandleFrom(anSQFileDataStructure));
	l1:	/* end fileDescriptorFrom: */;
	if (!(filenoToDup < 0)) {
		if (!(filenoToDup == 0)) {
			fflush(stdin);
			dup2(filenoToDup, 0);
			rewind(stdin);
		}
	}
}


/*	Dup a file descriptor to allow it to be attached as the standard output
	when we
	exec() a new executable. This is Unix specific, in that it assumes that
	file descriptor
	0 is stdin, 1 is stdout, and 2 is stderr. The dup2() call is used to copy
	the open file
	descriptors into file descriptors 0, 1 and 2 so that the image which we
	execute will
	use them as stdin, stdout, and stderr.
 */

	/* UnixOSProcessPlugin>>#dupToStdOut: */
static void
dupToStdOut(sqInt anSQFileDataStructure)
{
    sqInt filenoToDup;

	/* begin fileDescriptorFrom: */
	if (!((((isBytes(anSQFileDataStructure))
		 && ((byteSizeOf(anSQFileDataStructure)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(anSQFileDataStructure)))))
		 && (isNonNullSQFile(anSQFileDataStructure)))) {
		filenoToDup = -1;
		goto l1;
	}
	filenoToDup = fileno(fileHandleFrom(anSQFileDataStructure));
	l1:	/* end fileDescriptorFrom: */;
	if (!(filenoToDup < 0)) {
		if (!(filenoToDup == 1)) {
			fflush(stdout);
			dup2(filenoToDup, 1);
		}
	}
}


/*	Answer a string containing the OS process environment string at index (an
	Integer) in the environment list. */

	/* UnixOSProcessPlugin>>#environmentAtAsType: */
static sqInt
environmentAtAsType(sqInt classIdentifier)
{
    sqInt envCnt;
    sqInt index;
    sqInt len;
    sqInt newString;
    char **p;
    sqInt s;
    char *sPtr;


	/* Count number of environment variables. */
	p = getEnvironmentVector();
	if (p == null) {
		return primitiveFail();
	}
	envCnt = 0;
	while (*p++) envCnt++;

	/* restore pointer */
	p = getEnvironmentVector();
	index = stackIntegerValue(0);
	if ((index > envCnt) || (index < 1)) {
		pop(2);
		push(nilObject());
	}
	else {
		sPtr = p[index - 1];
		/* begin cString:asCollection: */
		len = strlen(sPtr);
		newString = instantiateClassindexableSize(classIdentifier, len);
		strncpy(arrayValueOf(newString), sPtr, len);
		s = newString;
		pop(2);
		push(s);
	}
	return 0;
}


/*	Answer the value of an environment variable keyed by a Symbol. */

	/* UnixOSProcessPlugin>>#environmentAtSymbolAsType: */
static sqInt
environmentAtSymbolAsType(sqInt classIdentifier)
{
    char * getenvResult;

	getenvResult = getenv(transientCStringFromString(stackObjectValue(0)));
	if (getenvResult == 0) {
		return primitiveFail();
	}
	else {
		pop(2);
		push(cStringasCollection(getenvResult, classIdentifier));
	}
	return 0;
}


/*	Answer the OS file descriptor, an integer value, from a SQFile data
	structure byte array, or answer -1 if unable to obtain the file descriptor
	(probably due
	to receiving an incorrect type of object as aFileHandle).
 */
/*	return type should be int, but skip the declaration to permit inlining */

	/* UnixOSProcessPlugin>>#fileDescriptorFrom: */
static sqInt
fileDescriptorFrom(sqInt aSQFileByteArray)
{
	if (!((((isBytes(aSQFileByteArray))
		 && ((byteSizeOf(aSQFileByteArray)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(aSQFileByteArray)))))
		 && (isNonNullSQFile(aSQFileByteArray)))) {
		return -1;
	}
	return fileno(fileHandleFrom(aSQFileByteArray));
}


/*	Answer a file handle from a SQFile structure. On most platforms, this
	will be a (FILE *). On Win32, it is a HANDLE. */

	/* OSProcessPlugin>>#fileHandleFrom: */
static FILEHANDLETYPE
fileHandleFrom(sqInt sqFileStructByteArray)
{
    SQFile *sqFile;

	sqFile = arrayValueOf(sqFileStructByteArray);
	return sqFile->file;
}


/*	Return the size of a Smalltalk file record in bytes. */

	/* OSProcessPlugin>>#fileRecordSize */
static usqIntptr_t
fileRecordSize(void)
{
	return sizeof(SQFile);
}


/*	Return a pointer to the first byte of of the SQFile data structure file
	record within
	anSQFileRecord, which is expected to be a ByteArray of size
	self>>fileRecordSize. 
 */

	/* OSProcessPlugin>>#fileValueOf: */
static SQFile *
fileValueOf(sqInt anSQFileRecord)
{
	return arrayValueOf(anSQFileRecord);
}


/*	The image constructs a flattened string of all the argument and/or
	environment strings.
	There is room at the beginning for the null-terminated array of pointers
	to strings.
	The rest of the string contains the null-terminated strings. */
/*	Use the address offsets in offsetArray to fix up the pointers in
	cStringArray. The result is a C array of pointers to char, used for argv
	and env vectors.
 */

	/* OSProcessPlugin>>#fixPointersInArrayOfStrings:withOffsets: */
static char **
fixPointersInArrayOfStringswithOffsets(sqInt flattenedStrings, sqInt offsets)
{
    sqInt count;
    sqInt idx;
    sqInt *offsetArray;
    char **ptr;
    usqInt sz;
    sqInt val;

	count = stSizeOf(offsets);
	offsetArray = firstIndexableField(offsets);
	sz = ((usqInt)(byteSizeOf(flattenedStrings)));
	if ((count * (sizeof(char *))) >= sz) {
		primitiveFailFor(PrimErrBadArgument);
		return 0;
	}
	ptr = ((char **) (arrayValueOf(flattenedStrings)));
	idx = 0;
	while (idx < count) {
		val = integerValueOf(offsetArray[idx]);
		if ((((usqInt)val)) >= sz) {
			primitiveFailFor(PrimErrBadArgument);
			return 0;
		}
		ptr[idx] = ((((char *) ptr)) + val);
		idx += 1;
	}
	if ((ptr[idx]) != 0) {
		primitiveFailFor(PrimErrBadArgument);
	}
	return (failed()
		? 0
		: ptr);
}


/*	Fork a child OS process, and do an exec in the child. The parent continues
	on in
	Smalltalk, and this method answers the pid of the child which was created.
	If useSignalHandler is true, set the signal handler for SIGCHLD.
	Otherwise, assume
	that death of child events are handled through some other mechanism.
	
	In this implementation, memory for the argument and environment arrays is
	allocated in the image prior to calling this primitive. This allows us to
	avoid invoking the
	garbage collector in this primitive (thereby moving the locations of
	environment and argument memory), but comes at the cost of twiddling C
	pointers here in the
	primitive. An alternative to this whole mess is just to malloc the
	environment and
	argument vectors, but I think it is a good idea to avoid malloc as much as
	possible so as not to limit future ObjectMemory implementations.
	
	This primitive replaces #primitiveForkAndExec from earlier versions of the
	plugin. The new name permits backward compatibility for an image running
	on a VM
	which does not yet have the updated plugin. This implementation uses a
	different argument format on the stack, and differs functionally in that
	the child now closes
	all file descriptors (including sockets) not required (that is, everything
	except stdin,
	stdout, and stderr on descriptors 0, 1 and 2). This eliminates some flakey
	behavior in child processes connected to Squeak by pipes, which failed to
	exit at expected times
	due to the old file descriptors remaining open. This is also cleaner in
	that garbage
	descriptors are not left hanging around the the child.
	
	On entry, the stack contains:
	0: workingDir, a null terminated string specifying the working directory
	to use, or nil.
	1: envOffsets, an array of integers for calculating environment string
	address offsets.
	2: envVecBuffer, a String buffer containing environment strings arranged
	to look like char **.
	3: argOffsets, an array of integers for calculating argument string
	address offsets.
	4: argVecBuffer, a String buffer containing argument strings arranged to
	look like char **.
	5: stdErr, a ByteArray handle.
	6: stdOut, a ByteArray handle.
	7: stdIn, a ByteArray handle.
	8: executableFile, a null terminated string with the name of the file to
	execute. 9: the sender
 */

	/* UnixOSProcessPlugin>>#forkAndExecInDirectory: */
static sqInt
forkAndExecInDirectory(sqInt useSignalHandler)
{
    sqInt argOffsets;
    char **args;
    sqInt argVecBuffer;
    char **env;
    sqInt envOffsets;
    extern char **envVec;
    sqInt envVecBuffer;
    sqInt executableFile;
    sqInt fd;
    sqInt fdLimiT;
    struct itimerval intervalTimer;
    pid_t pid;
    char *progNamePtr;
    char *pwdPtr;
    struct itimerval saveIntervalTimer;
    sqInt sigNum;
    sqInt stdErr;
    sqInt stdIn;
    sqInt stdOut;
    sqInt workingDir;

	if ((methodArgumentCount()) != 9) {
		return primitiveFailFor(PrimErrBadNumArgs);
	}
	if (useSignalHandler) {
		setSigChldHandler();
	}
	if ((sandboxSecurity()) == 1) {
		pop(10);
		pushInteger(-1);
		return null;
	}
	workingDir = stackObjectValue(0);
	envOffsets = stackObjectValue(1);
	envVecBuffer = stackObjectValue(2);
	argOffsets = stackObjectValue(3);
	argVecBuffer = stackObjectValue(4);
	stdErr = stackObjectValue(5);
	stdOut = stackObjectValue(6);
	stdIn = stackObjectValue(7);
	executableFile = stackObjectValue(8);
	if (failed()) {
		return primitiveFailFor(PrimErrBadArgument);
	}
	intervalTimer.it_interval.tv_sec = 0;
	intervalTimer.it_interval.tv_usec = 0;
	intervalTimer.it_value.tv_sec = 0;
	intervalTimer.it_value.tv_usec = 0;
	setitimer (ITIMER_REAL, &intervalTimer, &saveIntervalTimer);
	if (((pid = vfork())) != 0) {

		/* Normal return to Smalltalk - this is the old parent process. */
		/* Enable the timer again before resuming Smalltalk. */
		setitimer (ITIMER_REAL, &saveIntervalTimer, 0L);
		if (!(failed())) {

			/* Pop 9 arguments plus receiver, push pid. */
			pop(10);
			pushInteger(pid);
		}
		return null;
	}
	if (workingDir != (nilObject())) {
		pwdPtr = arrayValueOf(workingDir);
		if ((failed())
		 || (pwdPtr == 0)) {
			primitiveFailFor(PrimErrBadArgument);
			fprintf(stderr, "bad workingDir parameter\n");
			_exit(-1);
		}
		if ((chdir(pwdPtr)) != 0) {
			primitiveFailFor(PrimErrNotFound);
			perror("chdir");
			_exit(-1);
		}
	}

	/* Dup the file handles to attach the new child process to the right streams
	   on descriptors 0, 1 and 2. */
	progNamePtr = arrayValueOf(executableFile);
	if (!(stdErr == (nilObject()))) {
		dupToStdErr(stdErr);
	}
	if (!(stdOut == (nilObject()))) {
		dupToStdOut(stdOut);
	}
	if (!(stdIn == (nilObject()))) {
		dupToStdIn(stdIn);
	}
	for (fd = 3, fdLimiT = ((getdtablesize()) - 1); fd <= fdLimiT; fd += 1) {
		close(fd);
	}
	if (envVecBuffer == (nilObject())) {
		env = envVec;
	}
	else {
		env = ((char **) (fixPointersInArrayOfStringswithOffsets(envVecBuffer, envOffsets)));
	}
	args = ((char **) (fixPointersInArrayOfStringswithOffsets(argVecBuffer, argOffsets)));
	if ((env == 0)
	 || (args == 0)) {
		perror("bad env or bad args");
		_exit(-1);
	}
	/* begin restoreDefaultSignalHandlers */
	if (!(semaIndices == null)) {

		/* nil if in interpreter simulation */
		sigNum = 1;
		while (sigNum <= (signalArraySize())) {
			if ((semaIndices[sigNum]) > 0) {
				setSignalNumberhandler(sigNum, (originalSignalHandlers())[sigNum]);
			}
			sigNum += 1;
		}
	}
	execve(progNamePtr, args, env);
	perror(progNamePtr);
	_exit(-1);
	return 0;
}


/*	Fork a child process, and continue running squeak in the child process.
	Answer the result of the fork() call, either the child pid or zero.
	
	After calling fork(), two OS processes exist, one of which is the child of
	the other. On
	systems which implement copy-on-write memory management, and which support
	the fork() system call, both processes will be running Smalltalk images,
	and will be sharing
	the same memory space. In the original OS process, the resulting value of
	pid is the
	process id of the child process (a non-zero integer). In the child
	process, the value of
	pid is zero.
	
	The child recreates sufficient external resources to continue running.
	This is done by
	attaching to a new X session. The child is otherwise a copy of the parent
	process, and
	will continue executing the Smalltalk image at the same point as its
	parent. The return
	value of this primitive may be used by the two running Smalltalk images to
	determine which is the parent and which is the child.
	
	The child should not depend on using existing connections to external
	resources. For
	example, the child may lose its connections to stdin, stdout, and stderr
	after its parent
	exits.
	
	The new child image does not start itself from the image in the file
	system; rather it is
	a clone of the parent image as it existed at the time of
	primitiveForkSqueak. For this
	reason, the parent and child should agree in advance as to whom is allowed
	to save the
	image to the file system, otherwise one Smalltalk may overwrite the image
	of the other.
	
	This is a simple call to fork(), rather than the more common idiom of
	vfork() followed
	by exec(). The vfork() call cannot be used here because it is designed to
	be followed by
	an exec(), and its semantics require the parent process to wait for the
	child to exit. See
	the BSD programmers documentation for details.
 */

	/* UnixOSProcessPlugin>>#forkSqueak: */
EXPORT(pid_t)
forkSqueak(sqInt useSignalHandler)
{
    struct itimerval intervalTimer;
    pid_t pid;
    struct itimerval saveIntervalTimer;


	/* Turn off the interval timer. If this is not done, then the program which we exec in
	   the child process will receive a timer interrupt, and will not know how to handle it. */
	intervalTimer.it_interval.tv_sec = 0;
	intervalTimer.it_interval.tv_usec = 0;
	intervalTimer.it_value.tv_sec = 0;
	intervalTimer.it_value.tv_usec = 0;
	setitimer (ITIMER_REAL, &intervalTimer, &saveIntervalTimer);
	if (useSignalHandler) {
		setSigChldHandler();
	}

	/* Enable the timer again before resuming Smalltalk. */
	pid = fork();
	setitimer (ITIMER_REAL, &saveIntervalTimer, 0L);
	return pid;
}


/*	Set a signal handler in the VM which will signal a Smalltalk semaphore at
	semaphoreIndex whenever an external signal sigNum is received. Answer the
	prior value of the signal handler. If semaphoreIndex is zero, the handler
	is unregistered, and the VM returns to its default behavior for handling
	that signal. A handler must be unregistered before it can be registered
	again. 
	The Smalltalk semaphore is expected to be kept at the same index location
	indefinitely during the lifetime of a Squeak session. If that is not the
	case, the
	handler must be unregistered prior to unregistering the Smalltalk
	semaphore. 
 */

	/* UnixOSProcessPlugin>>#forwardSignal:toSemaphoreAt: */
static void *
forwardSignaltoSemaphoreAt(sqInt sigNum, sqInt semaphoreIndex)
{
    void *oldHandler;

	if (semaIndices == null) {
		return null;
	}
	if (semaphoreIndex == 0) {

		/* Disable the handler */
		if ((semaIndices[sigNum]) != 0) {
			oldHandler = (originalSignalHandlers())[sigNum];
			oldHandler = setSignalNumberhandler(sigNum, oldHandler);
			semaIndices[sigNum] = 0;
			return oldHandler;
		}
		else {

			/* either -1 for printAllStacks or a positive integer for semaphore forwarding */
			/* Signal handler had not been set, answer an error */
			/* begin sigErrorNumber */
			return SIG_ERR;
		}
	}
	if ((semaIndices[sigNum]) > 0) {

		/* Handler is already set, answer an error */
		/* begin sigErrorNumber */
		return SIG_ERR;
	}
	oldHandler = setSignalNumberhandler(sigNum, handleSignalFunctionAddress());
	if (oldHandler != (sigErrorNumber())) {
		(originalSignalHandlers())[sigNum] = oldHandler;
		semaIndices[sigNum] = semaphoreIndex;
	}
	return oldHandler;
}


/*	Answer a string containing the current working directory. */

	/* UnixOSProcessPlugin>>#getCurrentWorkingDirectoryAsType: */
EXPORT(sqInt)
getCurrentWorkingDirectoryAsType(sqInt classIdentifier)
{
    char *buffer;
    sqInt bufferSize;
    char *cwd;
    sqInt cwdString;
    sqInt incrementBy;
    sqInt len;
    sqInt maxSize;
    sqInt newString;

	bufferSize = 100;
	incrementBy = 100;
	maxSize = 5000;
	while (1) {
		cwdString = instantiateClassindexableSize(classString(), bufferSize);
		if (cwdString == null) {
			primitiveFailFor(PrimErrNoMemory);
		}
		buffer = arrayValueOf(cwdString);

		/* getcwd(buffer, bufferSize) */
		cwd = getcwd(buffer, bufferSize);
		if (!((cwd == 0)
		 && (bufferSize < maxSize))) break;
		bufferSize += incrementBy;
	}
	if (cwd == 0) {
		primitiveFail();
	}
	else {
		/* begin cString:asCollection: */
		len = strlen(cwd);
		newString = instantiateClassindexableSize(classIdentifier, len);
		strncpy(arrayValueOf(newString), cwd, len);
		cwdString = newString;
		methodReturnValue(cwdString);
	}
	return 0;
}


/*	Answer a pointer to the environment vector. If the main module has
	exported a function called getEnvVec, call that function. Otherwise try to
	use a global
	reference to the variable envVec, which is expected to be declared in the
	main VM, and may or may not be visible depending on platform and compiler
	conventions. The value of the environment vector is cached in a static
	variable, so this method should not be inlined.
 */

	/* UnixOSProcessPlugin>>#getEnvironmentVector */
static char **
getEnvironmentVector(void)
{
    extern char **envVec;
    void (*func)(void);
    static char **ptr = null;

	if (ptr == null) {
		func = ioLoadFunctionFrom("ioGetEnvVec", "");
		if (!(func == null)) {
			ptr =  ((char ** (*) (void)) func)();
		}
	}
	if (ptr == null) {
		ptr = envVec;
	}
	return ptr;
}


/*	Note: This is hardcoded so it can be run from Squeak.
	The module name is used for validating a module *after*
	it is loaded to check if it does really contain the module
	we're thinking it contains. This is important! */

	/* InterpreterPlugin>>#getModuleName */
EXPORT(const char*)
getModuleName(void)
{
	return moduleName;
}


/*	Answer the standard i/o file handle with the given index of my OS process.
	0 = stdin, 1 = stdout, 2 = stderr. */

	/* UnixOSProcessPlugin>>#getStdHandle: */
static sqInt
getStdHandle(sqInt n)
{
    sqInt fileOop;
    SQFile fileRecords[3];
    sqInt validMask;

	validMask = sqFileStdioHandlesInto(fileRecords);
	if ((validMask & (1U << n)) == 0) {
		return primitiveFailFor(PrimErrUnsupported);
	}
	fileOop = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	if (failed()) {
		return primitiveFailFor(PrimErrNoMemory);
	}
	memcpy(firstIndexableField(fileOop), &fileRecords[n], sizeof(SQFile));
	return popthenPush(1, fileOop);
}

	/* OSProcessPlugin>>#getThisSessionIdentifier */
static sqInt
getThisSessionIdentifier(void)
{
	return getThisSessionID();
}

	/* UnixOSProcessPlugin>>#handleSignalFunctionAddress */
static void *
handleSignalFunctionAddress(void)
{
	return handleSignal;
}


/*	This is a signal handler function which runs when a signal is received
	from the operating system. When the signal is received, a Smalltalk
	Semaphore is signaled. This effectively passes the external signal to
	Squeak to allow
	it to be handled in Smalltalk.
 */

	/* UnixOSProcessPlugin>>#handleSignal: */
static void
handleSignal(int sigNum)
{
     char semaIndex;

	semaIndex = semaIndices[sigNum];
	forwardSignaltoSemaphoreAt(sigNum, semaIndex);
	if (isVmThread()) {
		if (semaIndex > 0) {
			signalSemaphoreWithIndex(semaIndex);
		}
	}
	else {
		/* begin maskForThisThreadAndResend: */
		maskSignalForThisThread(sigNum);
		resendSignal(sigNum);
	}
}

	/* OSProcessPlugin>>#initialiseModule */
EXPORT(sqInt)
initialiseModule(void)
{
	osprocessSandboxSecurity = -1;
	initializeModuleForPlatform();
	return 1;
}


/*	Platform specific initialization */

	/* UnixOSProcessPlugin>>#initializeModuleForPlatform */
static sqInt
initializeModuleForPlatform(void)
{
	pidCount = 0;
	atexit(sendSignalToPids);
	vmThread = pthread_self();
	useSignalStack = -1;
	return 0;
}


/*	Check for the common failure mode of a SQFile record with all zeros. */

	/* OSProcessPlugin>>#isNonNullSQFile: */
static sqInt
isNonNullSQFile(sqInt objectPointer)
{
    unsigned idx;
    unsigned char *sqFileBytes;

	sqFileBytes = arrayValueOf(objectPointer);
	idx = 0;
	while (idx < (sizeof(SQFile))) {
		if ((sqFileBytes[idx]) != 0) {
			return 1;
		}
		idx += 1;
	}
	return 0;
}


/*	Check for the common failure mode of a SQSocket record with all zeros. */

	/* OSProcessPlugin>>#isNullSQSocket: */
static sqInt
isNullSQSocket(sqInt objectPointer)
{
    sqInt idx;
    unsigned char *sqSocketBytes;

	sqSocketBytes = arrayValueOf(objectPointer);
	idx = 0;
	while (idx < (sizeof(SQSocket))) {
		if ((sqSocketBytes[idx]) != 0) {
			return 0;
		}
		idx += 1;
	}
	return 1;
}


/*	Answer true if objectPointer appears to be a valid SQFile ByteArray.
	This check is appropriate if objectPointer has been passed as a parameter
	to a primitive, and is expected to represent a valid file reference. */

	/* OSProcessPlugin>>#isSQFileObject: */
static sqInt
isSQFileObject(sqInt objectPointer)
{
	return (((isBytes(objectPointer))
	 && ((byteSizeOf(objectPointer)) == (sizeof(SQFile))))
	 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(objectPointer)))))
	 && (isNonNullSQFile(objectPointer));
}


/*	Answer true if objectPointer appears to be a valid SQSocket ByteArray.
	This check
	is appropriate if objectPointer has been passed as a parameter to a
	primitive, and
	is expected to represent a valid socket reference.
 */

	/* OSProcessPlugin>>#isSQSocketObject: */
static sqInt
isSQSocketObject(sqInt objectPointer)
{
	return ((isBytes(objectPointer))
	 && ((byteSizeOf(objectPointer)) == (sizeof(SQSocket))))
	 && (!(isNullSQSocket(objectPointer)));
}


/*	Answer true if the file session matches the current interpreter session
	identifier. 
 */

	/* OSProcessPlugin>>#isValidFileSession: */
static sqInt
isValidFileSession(sqInt objectPointer)
{
	return (getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(objectPointer)));
}


/*	Answer true if this method is executing in the context of the pthread in
	which the interpreter executes. */

	/* UnixOSProcessPlugin>>#isVmThread */
static sqInt
isVmThread(void)
{
    pthread_t thisThread;

	thisThread = pthread_self();
	return pthread_equal(thisThread, vmThread);
}


/*	Create a pipe and populate the readerIOStream and writerIOStream
	variables. Make sure the SIGPIPE handler is set before creating the pipe.
	Answer true for
	success, else false
 */

	/* UnixOSProcessPlugin>>#makePipeForReader:writer: */
static sqInt
makePipeForReaderwriter(FILEHANDLETYPE *readerIOStreamPtr, FILEHANDLETYPE *writerIOStreamPtr)
{
    int filedes[2];

	/* begin setSigPipeHandler */
	setSignalNumberhandler(SIGPIPE, sigIgnoreNumber());
	if ((pipe(filedes)) == -1) {

		/* Translates to a pipe() system call */
		return 0;
	}
	else {
		*writerIOStreamPtr= (FILE *) fdopen (filedes[1], "a");
		*readerIOStreamPtr= (FILE *) fdopen (filedes[0], "r");
		return 1;
	}
}


/*	Prevent any future instances of signal sigNum from being delivered
	to this pthread, and reschedule the current signal to be delivered to
	this OS process. Any pthread other than the interpreter thread will
	call this method. Eventually the signal will be delivered to the
	interpreter thread, which will handle it in the context of the interpreter
	thread. */

	/* UnixOSProcessPlugin>>#maskForThisThreadAndResend: */
static sqInt
maskForThisThreadAndResend(int sigNum)
{
	maskSignalForThisThread(sigNum);
	resendSignal(sigNum);
	return 0;
}


/*	Prevent future invocations of signal sigNum from being delivered to this
	pthread. 
 */

	/* UnixOSProcessPlugin>>#maskSignalForThisThread: */
static sqInt
maskSignalForThisThread(int sigNum)
{
    sigset_t sigset;

	sigemptyset(&sigset);
	sigaddset(&sigset, sigNum);
	pthread_sigmask(SIG_BLOCK, &sigset, NULL);
	return 0;
}


/*	The module with the given name was just unloaded.
	Make sure we have no dangling references. */

	/* OSProcessPlugin>>#moduleUnloaded: */
EXPORT(sqInt)
moduleUnloaded(char *aModuleName)
{
	return 0;
}

	/* InterpreterPlugin>>#msg: */
static sqInt
msg(char *s)
{
	fprintf(stderr, "\n%s: %s", moduleName, s);
	return 0;
}


/*	Answer if the plugin should use a signalstack to avoid delivering signals
	on the native stack, which can interfere with the JIT. As a side-effect,
	allocate one if one is not already available. We use GetAttributeString
	to detect the JIT, and if so detected we use a signalstack, We reuse
	any existing signalstack if available. */

	/* UnixOSProcessPlugin>>#needSigaltstack */
static sqInt
needSigaltstack(void)
{
    char * (*GetAttributeString)(int);
    sqInt SigStackSize;
    stack_t sigstack;

	if (useSignalStack >= 0) {
		return useSignalStack != 0;
	}
	GetAttributeString = ioLoadFunctionFrom("GetAttributeString", "os_exports");
	if ((GetAttributeString == null)
	 || ((GetAttributeString(1008)) == null)) {
		useSignalStack = 0;
		return 0;
	}

	/* Now see if there's already a sigaltstack in place */
	useSignalStack = 1;
	if ((sigaltstack(0,&sigstack)) < 0) {
		perror("sigaltstack");
	}
	
#  if defined(SA_DISABLE)
	if (!(sigstack.ss_size == 0 || (sigstack.ss_flags & SA_DISABLE))) {
		return 1;
	}
#  else /* defined(SA_DISABLE) */

	/* e.g. Mac OS documents SA_DISABLE but defines SS_DISABLE */
	if (!(sigstack.ss_size == 0 || (sigstack.ss_flags & SS_DISABLE))) {
		return 1;
	}
#  endif /* defined(SA_DISABLE) */
	SigStackSize = ((((1024 * (sizeof(void *))) * 16) < MINSIGSTKSZ) ? MINSIGSTKSZ : ((1024 * (sizeof(void *))) * 16));
	if (null == (sigstack.ss_size = SigStackSize, sigstack.ss_sp = malloc(SigStackSize))) {
		msg("sigstack malloc failed");
		useSignalStack = 0;
		return 0;
	}
	if (sigaltstack(&sigstack, 0) < 0) {
		msg("sigaltstack install failed");
		(void)free(sigstack.ss_sp);
		useSignalStack = 0;
		return 0;
	}
	return 1;
}


/*	A pthread_t is an unsigned long, which may be 8 bytes on some 64 bit
	platforms. This stores the value in a byte array that can be easily
	passed to the image. */

	/* UnixOSProcessPlugin>>#newPthreadTypeByteArray: */
static sqInt
newPthreadTypeByteArray(pthread_t aPthreadType)
{
    sqInt byteArray;
    sqInt len;
    pthread_t *ptr;

	len = sizeof(pthread_t);
	byteArray = instantiateClassindexableSize(classByteArray(), len);
	ptr = arrayValueOf(byteArray);
	ptr[0] = aPthreadType;
	return byteArray;
}


/*	Answer a new ByteArray sized to contain a SQFile data structure. */

	/* OSProcessPlugin>>#newSQFileByteArray */
static sqInt
newSQFileByteArray(void)
{
	return instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
}


/*	Answer a new ByteArray sized to contain a SQSocket data structure. */

	/* OSProcessPlugin>>#newSQSocketByteArray */
static sqInt
newSQSocketByteArray(void)
{
	return instantiateClassindexableSize(classByteArray(), sizeof(SQSocket));
}


/*	An array of signal handler function addresses, one for each signal type.
	The value at each entry is the address of the original signal handler
	function prior
	to having set a handler.
 */

	/* UnixOSProcessPlugin>>#originalSignalHandlers */
static void **
originalSignalHandlers(void)
{
	return originalSigHandlers;
}


/*	Answer the pointer represented by aByteArray. */

	/* OSProcessPlugin>>#pointerFrom: */
static void *
pointerFrom(sqInt aByteArray)
{
    sqInt idx;
    union {void *address; unsigned char bytes[sizeof(void *)];} pointerUnion;
    unsigned char *ptr;

	if (!((isBytes(aByteArray))
		 && ((stSizeOf(aByteArray)) == (sizeof(void *))))) {
		return null;
	}
	ptr = arrayValueOf(aByteArray);
	idx = 0;
	while (idx < (sizeof(void *))) {
		pointerUnion.bytes[idx] = ptr[idx];
		idx += 1;
	}
	return pointerUnion.address;
}


/*	Answer a string containing the OS process argument at index (an Integer)
	in the
	argument list.
 */

	/* UnixOSProcessPlugin>>#primitiveArgumentAt */
EXPORT(sqInt)
primitiveArgumentAt(void)
{
    extern int argCnt;
    extern char **argVec;
    sqInt classIdentifier;
    sqInt index;
    sqInt len;
    sqInt newString;
    sqInt s;
    char *sPtr;

	/* begin argumentAtAsType: */
	classIdentifier = classString();
	index = stackIntegerValue(0);
	if ((index > argCnt) || (index < 1)) {
		popthenPush(2, nilObject());
	}
	else {
		sPtr = argVec[index - 1];
		/* begin cString:asCollection: */
		len = strlen(sPtr);
		newString = instantiateClassindexableSize(classIdentifier, len);
		strncpy(arrayValueOf(newString), sPtr, len);
		s = newString;
		popthenPush(2, s);
	}
	return 0;
}


/*	Answer a byte array containing the OS process argument at index (an
	Integer) in the argument list. */

	/* UnixOSProcessPlugin>>#primitiveArgumentAtAsBytes */
EXPORT(sqInt)
primitiveArgumentAtAsBytes(void)
{
    extern int argCnt;
    extern char **argVec;
    sqInt classIdentifier;
    sqInt index;
    sqInt len;
    sqInt newString;
    sqInt s;
    char *sPtr;

	/* begin argumentAtAsType: */
	classIdentifier = classByteArray();
	index = stackIntegerValue(0);
	if ((index > argCnt) || (index < 1)) {
		popthenPush(2, nilObject());
	}
	else {
		sPtr = argVec[index - 1];
		/* begin cString:asCollection: */
		len = strlen(sPtr);
		newString = instantiateClassindexableSize(classIdentifier, len);
		strncpy(arrayValueOf(newString), sPtr, len);
		s = newString;
		popthenPush(2, s);
	}
	return 0;
}


/*	Send a null signal to the OS process identified by the argument. Answer
	false for
	a bad parameter on the stack (the common case is for the argument equal to
	nil, for which case we should answer false). Answer true if the process
	exists and can
	receive signals from this process, otherwise false. This test is useful
	for determining
	if a child process still exists following a Squeak image restart.
 */

	/* UnixOSProcessPlugin>>#primitiveCanReceiveSignals */
EXPORT(sqInt)
primitiveCanReceiveSignals(void)
{
    pid_t pidToSignal;
    int result;

	if (isIntegerObject(stackValue(0))) {
		pidToSignal = stackIntegerValue(0);
		result = kill(pidToSignal, 0);
		pop(2);
		if (result == 0) {
			push(trueObject());
		}
		else {
			push(falseObject());
		}
	}
	else {
		pop(2);
		push(falseObject());
	}
	return 0;
}


/*	Call chdir(2) to change current working directory to the specified path
	string. Answer
	nil for success, or errno on failure.
 */

	/* UnixOSProcessPlugin>>#primitiveChdir */
EXPORT(sqInt)
primitiveChdir(void)
{
    extern int errno;
    char *path;

	path = transientCStringFromString(stackObjectValue(0));
	if (chdir(path)) {
		pop(2);
		pushInteger(errno);
	}
	else {
		pop(2);
		push(nilObject());
	}
	return 0;
}


/*	Create a pipe, and answer an array of two file handles for the pipe writer
	and reader.
	The readerIOStream and writerIOStream variables represent the low level
	pipe streams,
	which will be of type (FILE *) or HANDLE, depending on what the FilePlugin
	support code is using to represent file streams. FILEHANDLETYPE is defined
	in my subclasses
	in the #declareCVarsIn: class method.
 */

	/* OSProcessPlugin>>#primitiveCreatePipe */
EXPORT(sqInt)
primitiveCreatePipe(void)
{
    sqInt arrayResult;
    sqInt reader;
    FILEHANDLETYPE readerIOStream;
    FILEHANDLETYPE *readerIOStreamPtr;
    SQFile *readerPtr;
    SESSIONIDENTIFIERTYPE thisSession;
    sqInt writer;
    FILEHANDLETYPE writerIOStream;
    FILEHANDLETYPE *writerIOStreamPtr;
    SQFile *writerPtr;

	/* begin getThisSessionIdentifier */
	thisSession = getThisSessionID();
	readerIOStreamPtr = (&readerIOStream);
	writerIOStreamPtr = (&writerIOStream);
	if (!(createPipeForReaderwriter(readerIOStreamPtr, writerIOStreamPtr))) {
		return primitiveFail();
	}
	/* begin newSQFileByteArray */
	writer = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	writerPtr = arrayValueOf(writer);
	writerPtr->file = writerIOStream;
	writerPtr->sessionID = thisSession;
	writerPtr->writable = 1;
	writerPtr->lastOp = 0;
	pushRemappableOop(writer);
	/* begin newSQFileByteArray */
	reader = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	readerPtr = arrayValueOf(reader);
	readerPtr->file = readerIOStream;
	readerPtr->sessionID = thisSession;
	readerPtr->writable = 0;
	readerPtr->lastOp = 0;
	pushRemappableOop(reader);
	arrayResult = instantiateClassindexableSize(classArray(), 2);
	stObjectatput(arrayResult, 1, popRemappableOop());
	stObjectatput(arrayResult, 2, popRemappableOop());
	popthenPush(1, arrayResult);
	return 0;
}


/*	Create a pipe, and answer an array of two file handles for the pipe writer
	and reader.
	The session identifier is passed as the parameter to this primitive. Use
	this variant
	if the session identifier is not available directly in the VM (as may be
	the case if
	it is not possible to link from this plugin to a variable elsewhere in the
	VM). The readerIOStream and writerIOStream variables represent the low
	level pipe streams,
	which will be of type (FILE *) or HANDLE, depending on what the FilePlugin
	support code is using to represent file streams. FILEHANDLETYPE is defined
	in my subclasses
	in the #declareCVarsIn: class method.
 */

	/* OSProcessPlugin>>#primitiveCreatePipeWithSessionIdentifier */
EXPORT(sqInt)
primitiveCreatePipeWithSessionIdentifier(void)
{
    sqInt arrayResult;
    sqInt reader;
    FILEHANDLETYPE readerIOStream;
    FILEHANDLETYPE *readerIOStreamPtr;
    SQFile *readerPtr;
    SESSIONIDENTIFIERTYPE thisSession;
    sqInt writer;
    FILEHANDLETYPE writerIOStream;
    FILEHANDLETYPE *writerIOStreamPtr;
    SQFile *writerPtr;


	/* Create the anonymous OS pipe */
	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	readerIOStreamPtr = (&readerIOStream);
	writerIOStreamPtr = (&writerIOStream);
	if (!(createPipeForReaderwriter(readerIOStreamPtr, writerIOStreamPtr))) {
		return primitiveFail();
	}
	/* begin newSQFileByteArray */
	writer = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	writerPtr = arrayValueOf(writer);
	writerPtr->file = writerIOStream;
	writerPtr->sessionID = thisSession;
	writerPtr->writable = 1;
	writerPtr->lastOp = 0;
	pushRemappableOop(writer);
	/* begin newSQFileByteArray */
	reader = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	readerPtr = arrayValueOf(reader);
	readerPtr->file = readerIOStream;
	readerPtr->sessionID = thisSession;
	readerPtr->writable = 0;
	readerPtr->lastOp = 0;
	pushRemappableOop(reader);
	arrayResult = instantiateClassindexableSize(classArray(), 2);
	stObjectatput(arrayResult, 1, popRemappableOop());
	stObjectatput(arrayResult, 2, popRemappableOop());
	popthenPush(2, arrayResult);
	return 0;
}


/*	Call dup() to duplicate a file descriptor using the next available
	descriptor. Answer
	the new file descriptor or -1 on failure.
 */

	/* UnixOSProcessPlugin>>#primitiveDup: */
EXPORT(sqInt)
primitiveDup(sqInt fileDescriptor)
{
    int fd;
    int result;

	fd = stackIntegerValue(0);
	result = dup(fd);
	pop(2);
	pushInteger(result);
	return 0;
}


/*	Call dup2() to duplicate a file descriptor. Answer the duplicated file
	descriptor or -1 on failure. */

	/* UnixOSProcessPlugin>>#primitiveDup:To: */
EXPORT(sqInt)
primitiveDupTo(sqInt oldFileDescriptor, sqInt newFileDescriptor)
{
    int newfd;
    int oldfd;
    int result;

	newfd = stackIntegerValue(0);
	oldfd = stackIntegerValue(1);
	result = dup2(oldfd, newfd);
	pop(3);
	pushInteger(result);
	return 0;
}


/*	Answer a string containing the OS process environment string at index (an
	Integer) in the environment list. */

	/* UnixOSProcessPlugin>>#primitiveEnvironmentAt */
EXPORT(sqInt)
primitiveEnvironmentAt(void)
{
	environmentAtAsType(classString());
	return 0;
}


/*	Answer a byte array containing the OS process environment string at index
	(an Integer) in the environment list. */

	/* UnixOSProcessPlugin>>#primitiveEnvironmentAtAsBytes */
EXPORT(sqInt)
primitiveEnvironmentAtAsBytes(void)
{
	environmentAtAsType(classByteArray());
	return 0;
}


/*	Answer the value of an environment variable keyed by a Symbol. */

	/* UnixOSProcessPlugin>>#primitiveEnvironmentAtSymbol */
EXPORT(sqInt)
primitiveEnvironmentAtSymbol(void)
{
    sqInt classIdentifier;
    char * getenvResult;

	/* begin environmentAtSymbolAsType: */
	classIdentifier = classString();
	getenvResult = getenv(transientCStringFromString(stackObjectValue(0)));
	if (getenvResult == 0) {
		return primitiveFail();
	}
	else {
		pop(2);
		push(cStringasCollection(getenvResult, classIdentifier));
	}
	return null;
}


/*	Answer a byte array with the value of an environment variable keyed
	by a Symbol. */

	/* UnixOSProcessPlugin>>#primitiveEnvironmentAtSymbolAsBytes */
EXPORT(sqInt)
primitiveEnvironmentAtSymbolAsBytes(void)
{
    sqInt classIdentifier;
    char * getenvResult;

	/* begin environmentAtSymbolAsType: */
	classIdentifier = classByteArray();
	getenvResult = getenv(transientCStringFromString(stackObjectValue(0)));
	if (getenvResult == 0) {
		return primitiveFail();
	}
	else {
		pop(2);
		push(cStringasCollection(getenvResult, classIdentifier));
	}
	return null;
}


/*	Answer a string describing an error message */

	/* UnixOSProcessPlugin>>#primitiveErrorMessageAt */
EXPORT(sqInt)
primitiveErrorMessageAt(void)
{
    sqInt classIdentifier;
    sqInt errMessage;
    sqInt index;
    sqInt len;
    sqInt newString;
    char *p;

	index = stackIntegerValue(0);
	p = ((char *) (strerror(index)));
	/* begin cString:asCollection: */
	classIdentifier = classString();
	len = strlen(p);
	newString = instantiateClassindexableSize(classIdentifier, len);
	strncpy(arrayValueOf(newString), p, len);
	errMessage = newString;
	pop(2);
	push(errMessage);
	return 0;
}


/*	Call stat(2) to obtain the file protection mask for a file. Answer an
	Array of
	four integers representing the protection mask, or answer errno on
	failure. The
	protection mask is four Integers, each of which may be considered an octal
	digit (0-7), with bit values 4, 2, and 1. The first digit selects the set
	user ID (4) and set
	group ID (2) and save text image (1) attributes. The second digit selects
	permissions for the user who owns the file: read (4), write (2), and
	execute (1); the third
	selects permissions for other users in the file's group, with the same
	values; and
	the fourth for other users not in the file's group, with the same values.
 */

	/* UnixOSProcessPlugin>>#primitiveFileProtectionMask */
EXPORT(sqInt)
primitiveFileProtectionMask(void)
{
    sqInt buffer;
    extern int errno;
    sqInt mode;
    char *path;
    sqInt result;
    struct stat *statBuf;

	buffer = instantiateClassindexableSize(classByteArray(), sizeof(struct stat));
	statBuf = arrayValueOf(buffer);
	path = transientCStringFromString(stackObjectValue(0));
	mode = stat(path, statBuf);
	if (mode == 0) {
		mode = statBuf->st_mode;
		result = instantiateClassindexableSize(classArray(), 4);
		stObjectatput(result, 4, integerObjectOf(mode & 07));
		stObjectatput(result, 3, integerObjectOf((mode & 070) >> 3));
		stObjectatput(result, 2, integerObjectOf((mode & 0700) >> 6));
		stObjectatput(result, 1, integerObjectOf((mode & 07000) >> 9));
		pop(2);
		push(result);
	}
	else {
		pop(2);
		pushInteger(errno);
	}
	return 0;
}


/*	Call stat(2) to obtain the file protection mask for a file. Answer errno
	on failure,
	or on success answer an array with: UID with: GID with: protectionMask.
	The	 protectionMask is an Array of four integers representing the
	protection mask, or
	answer errno on failure. The protection mask is four Integers, each of
	which may
	be considered an octal digit (0-7), with bit values 4, 2, and 1. The first
	digit selects
	the set user ID (4) and set group ID (2) and save text image (1)
	attributes. The second
	digit selects permissions for the user who owns the file: read (4), write
	(2), and
	execute (1); the third selects permissions for other users in the file's
	group, with
	the same values; and the fourth for other users not in the file's group,
	with the
	same values.
 */

	/* UnixOSProcessPlugin>>#primitiveFileStat */
EXPORT(sqInt)
primitiveFileStat(void)
{
    sqInt buffer;
    extern int errno;
    sqInt gid;
    sqInt mask;
    sqInt mode;
    char *path;
    sqInt result;
    struct stat *statBuf;
    sqInt uid;

	result = instantiateClassindexableSize(classArray(), 3);
	uid = instantiateClassindexableSize(classByteArray(), sizeof(uid_t));
	gid = instantiateClassindexableSize(classByteArray(), sizeof(gid_t));
	mask = instantiateClassindexableSize(classArray(), 4);
	buffer = instantiateClassindexableSize(classByteArray(), sizeof(struct stat));
	statBuf = arrayValueOf(buffer);
	path = transientCStringFromString(stackObjectValue(0));
	mode = stat(path, statBuf);
	if (mode == 0) {
		mode = statBuf->st_mode;
		stObjectatput(mask, 4, integerObjectOf(mode & 07));
		stObjectatput(mask, 3, integerObjectOf((mode & 070) >> 3));
		stObjectatput(mask, 2, integerObjectOf((mode & 0700) >> 6));
		stObjectatput(mask, 1, integerObjectOf((mode & 07000) >> 9));
		stObjectatput(result, 3, mask);
		stObjectatput(result, 2, integerObjectOf(statBuf->st_gid));
		stObjectatput(result, 1, integerObjectOf(statBuf->st_uid));
		pop(2);
		push(result);
	}
	else {
		pop(2);
		pushInteger(errno);
	}
	return 0;
}


/*	This primitive exists only for purposes of testing the
	fixPointersInArrayOfStrings:withOffsets:count: method. I believe it to be
	reasonably machine and compiler independent, but have no way of verifying
	this on a variety of machines, so I'll leave this test method here in case
	someone runs into problems on other hardware or compilers. -dtl */

	/* OSProcessPlugin>>#primitiveFixPointersInArrayOfStrings */
EXPORT(sqInt)
primitiveFixPointersInArrayOfStrings(void)
{
    sqInt count;
    sqInt cStringArray;
    sqInt offsetArray;

	count = stackIntegerValue(0);
	offsetArray = stackObjectValue(1);
	cStringArray = stackObjectValue(2);
	if ((failed())
	 || ((fixPointersInArrayOfStringswithOffsets(cStringArray, offsetArray)) == 0)) {
		primitiveFail();
	}
	else {
		popthenPush(4, cStringArray);
	}
	return 0;
}


/*	Fork a child OS process, and do an exec in the child. The parent continues
	on in
	Smalltalk, and this method answers the pid of the child which was created.
	
	On entry, the stack contains:
	0: workingDir, a null terminated string specifying the working directory
	to use, or nil.
	1: envOffsets, an array of integers for calculating environment string
	address offsets.
	2: envVecBuffer, a String buffer containing environment strings arranged
	to look like char **.
	3: argOffsets, an array of integers for calculating argument string
	address offsets.
	4: argVecBuffer, a String buffer containing argument strings arranged to
	look like char **.
	5: stdErr, a ByteArray handle.
	6: stdOut, a ByteArray handle.
	7: stdIn, a ByteArray handle.
	8: executableFile, a null terminated string with the name of the file to
	execute. 9: the sender */

	/* UnixOSProcessPlugin>>#primitiveForkAndExecInDirectory */
EXPORT(sqInt)
primitiveForkAndExecInDirectory(void)
{
	return forkAndExecInDirectory(1);
}


/*	Fork a child OS process, and do an exec in the child. The parent continues
	on in
	Smalltalk, and this method answers the pid of the child which was created.
	
	On entry, the stack contains:
	0: workingDir, a null terminated string specifying the working directory
	to use, or nil.
	1: envOffsets, an array of integers for calculating environment string
	address offsets.
	2: envVecBuffer, a String buffer containing environment strings arranged
	to look like char **.
	3: argOffsets, an array of integers for calculating argument string
	address offsets.
	4: argVecBuffer, a String buffer containing argument strings arranged to
	look like char **.
	5: stdErr, a ByteArray handle.
	6: stdOut, a ByteArray handle.
	7: stdIn, a ByteArray handle.
	8: executableFile, a null terminated string with the name of the file to
	execute. 9: the sender */

	/* UnixOSProcessPlugin>>#primitiveForkExec */
EXPORT(sqInt)
primitiveForkExec(void)
{
	return forkAndExecInDirectory(0);
}


/*	Fork a child process, and continue running squeak in the child process.
	Leave the
	X session connected to the parent process, but close its file descriptor
	for the child
	process. Open a new X session for the child.
	
	The child should not depend on using existing connections to external
	resources. For
	example, the child may lose its connections to stdin, stdout, and stderr
	after its parent
	exits.
 */

	/* UnixOSProcessPlugin>>#primitiveForkSqueak */
EXPORT(sqInt)
primitiveForkSqueak(void)
{
    pid_t pid;


	/* Do not fork child if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(1);
		pushInteger(-1);
	}
	else {
		pid = forkSqueak(1);
		pop(1);
		pushInteger(pid);
	}
	return 0;
}


/*	Fork a child process, and continue running squeak in the child process.
	Leave the
	X session connected to the parent process, but close its file descriptor
	for the child
	process. Open a new X session for the child.
	
	The child should not depend on using existing connections to external
	resources. For
	example, the child may lose its connections to stdin, stdout, and stderr
	after its parent
	exits.
 */

	/* UnixOSProcessPlugin>>#primitiveForkSqueakWithoutSigHandler */
EXPORT(sqInt)
primitiveForkSqueakWithoutSigHandler(void)
{
    pid_t pid;


	/* Do not fork child if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(1);
		pushInteger(-1);
	}
	else {
		pid = forkSqueak(0);
		pop(1);
		pushInteger(pid);
	}
	return 0;
}


/*	Set a signal handler in the VM which will signal a Smalltalk semaphore at
	semaphoreIndex whenever an external signal sigNum is received. Answer the
	prior value of the signal handler. If semaphoreIndex is zero or nil, the
	handler is unregistered, and the VM returns to its default behavior for
	handling that
	signal.
	
	The Smalltalk semaphore is expected to be kept at the same index location
	indefinitely during the lifetime of a Squeak session. If that is not the
	case, the
	handler must be unregistered prior to unregistering the Smalltalk
	semaphore. 
 */

	/* UnixOSProcessPlugin>>#primitiveForwardSignalToSemaphore */
EXPORT(sqInt)
primitiveForwardSignalToSemaphore(void)
{
    void *handler;
    char *hPtr;
    sqInt idx;
    sqInt index;
    union {void *handler; unsigned char bytes[sizeof(void *)];} priorHandler;
    sqInt priorHandlerObject;
    sqInt semaphoreIndex;
    sqInt sigNum;

	semaphoreIndex = 0;
	index = stackValue(0);
	if (index == (nilObject())) {
		semaphoreIndex = 0;
	}
	else {
		if (isIntegerObject(index)) {
			semaphoreIndex = stackIntegerValue(0);
		}
		else {
			return primitiveFail();
		}
	}
	sigNum = stackIntegerValue(1);
	handler = forwardSignaltoSemaphoreAt(sigNum, semaphoreIndex);
	if (handler == (sigErrorNumber())) {
		return primitiveFail();
	}
	priorHandlerObject = instantiateClassindexableSize(classByteArray(), sizeof(void *));
	hPtr = arrayValueOf(priorHandlerObject);
	priorHandler.handler = handler;
	idx = 0;
	while (idx < (sizeof(void *))) {
		hPtr[idx] = priorHandler.bytes[idx];
		idx += 1;
	}
	popthenPush(3, priorHandlerObject);
	return 0;
}


/*	Answer the current working directory. as a ByteString */

	/* UnixOSProcessPlugin>>#primitiveGetCurrentWorkingDirectory */
EXPORT(sqInt)
primitiveGetCurrentWorkingDirectory(void)
{
	getCurrentWorkingDirectoryAsType(classString());
	return 0;
}


/*	Answer the current working directory as a ByteArray.. */

	/* UnixOSProcessPlugin>>#primitiveGetCurrentWorkingDirectoryAsBytes */
EXPORT(sqInt)
primitiveGetCurrentWorkingDirectoryAsBytes(void)
{
	getCurrentWorkingDirectoryAsType(classByteArray());
	return 0;
}


/*	Answer the effective group ID of my OS process */

	/* UnixOSProcessPlugin>>#primitiveGetEGid */
EXPORT(sqInt)
primitiveGetEGid(void)
{
    gid_t eGid;

	eGid = getegid();
	pop(1);
	pushInteger(eGid);
	return 0;
}


/*	Answer the effective user ID of my OS process */

	/* UnixOSProcessPlugin>>#primitiveGetEUid */
EXPORT(sqInt)
primitiveGetEUid(void)
{
    uid_t eUid;

	eUid = geteuid();
	pop(1);
	pushInteger(eUid);
	return 0;
}


/*	Answer the group ID of my OS process */

	/* UnixOSProcessPlugin>>#primitiveGetGid */
EXPORT(sqInt)
primitiveGetGid(void)
{
    gid_t gid;

	gid = getgid();
	pop(1);
	pushInteger(gid);
	return 0;
}


/*	Answer the process group ID of the process identified by pid */

	/* UnixOSProcessPlugin>>#primitiveGetPGid */
EXPORT(sqInt)
primitiveGetPGid(void)
{
    pid_t pgid;
    pid_t pid;

	pid = stackIntegerValue(0);
	pgid = getpgid(pid);
	if (pgid == -1) {
		return primitiveFail();
	}
	pop(2);
	pushInteger(pgid);
	return 0;
}


/*	Answer the process group ID of this OS process */

	/* UnixOSProcessPlugin>>#primitiveGetPGrp */
EXPORT(sqInt)
primitiveGetPGrp(void)
{
    pid_t pgid;

	pgid = getpgrp();
	if (pgid == -1) {
		return primitiveFail();
	}
	pop(1);
	pushInteger(pgid);
	return 0;
}


/*	Answer the process ID of my OS process */

	/* UnixOSProcessPlugin>>#primitiveGetPid */
EXPORT(sqInt)
primitiveGetPid(void)
{
    pid_t pid;

	pid = getpid();
	pop(1);
	pushInteger(pid);
	return 0;
}


/*	Answer the process ID of the parent process of my OS process */

	/* UnixOSProcessPlugin>>#primitiveGetPPid */
EXPORT(sqInt)
primitiveGetPPid(void)
{
    pid_t ppid;

	ppid = getppid();
	pop(1);
	pushInteger(ppid);
	return 0;
}


/*	Answer the unique session identifier for this Smalltalk instance running
	in this
	OS process. The C integer value is coerced into a Smalltalk ByteArray to
	preserve the full range of possible values.
 */

	/* OSProcessPlugin>>#primitiveGetSession */
EXPORT(sqInt)
primitiveGetSession(void)
{
    void *charArray1;
    unsigned char *sessionByteArrayPointer;
    usqIntptr_t sessionIDSize;
    sqInt sessionOop;
    SESSIONIDENTIFIERTYPE thisSessionID;

	/* begin getThisSessionIdentifier */
	thisSessionID = getThisSessionID();
	sessionIDSize = sizeof(thisSessionID);
	sessionOop = instantiateClassindexableSize(classByteArray(), sessionIDSize);
	sessionByteArrayPointer = arrayValueOf(sessionOop);
	if (thisSessionID == null) {
		return primitiveFail();
	}
	/* begin copyBytesFrom:to:length: */
	charArray1 = ((void *) ((unsigned char *)&thisSessionID));
	memcpy(sessionByteArrayPointer, charArray1, sessionIDSize);
	popthenPush(1, sessionOop);
	return 0;
}


/*	Answer the file handle for standard error of my OS process */

	/* UnixOSProcessPlugin>>#primitiveGetStdErrHandle */
EXPORT(sqInt)
primitiveGetStdErrHandle(void)
{
	getStdHandle(2);
	return 0;
}


/*	Answer the file handle for standard error of my OS process. The session
	identifier is passed as the parameter to this primitive. Use this variant
	if the session identifier is not available directly in the VM (as may be
	the case if it is not possible to link from this plugin to a variable
	elsewhere in the VM). */

	/* UnixOSProcessPlugin>>#primitiveGetStdErrHandleWithSessionIdentifier */
EXPORT(sqInt)
primitiveGetStdErrHandleWithSessionIdentifier(void)
{
    SQFile *file;
    sqInt fileOop;
    SESSIONIDENTIFIERTYPE thisSession;

	/* begin newSQFileByteArray */
	fileOop = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	file = arrayValueOf(fileOop);
	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	file->file = stderr;
	file->sessionID = thisSession;
	file->writable = 1;
	file->lastOp = 0;
	pop(2);
	push(fileOop);
	return 0;
}


/*	Answer the file handle for standard input of my OS process */

	/* UnixOSProcessPlugin>>#primitiveGetStdInHandle */
EXPORT(sqInt)
primitiveGetStdInHandle(void)
{
	getStdHandle(0);
	return 0;
}


/*	Answer the file handle for standard input of my OS process. The session
	identifier is passed as the parameter to this primitive. Use this variant
	if the session identifier is not available directly in the VM (as may be
	the case if it is not possible to link from this plugin to a variable
	elsewhere in the VM). */

	/* UnixOSProcessPlugin>>#primitiveGetStdInHandleWithSessionIdentifier */
EXPORT(sqInt)
primitiveGetStdInHandleWithSessionIdentifier(void)
{
    SQFile *file;
    sqInt fileOop;
    SESSIONIDENTIFIERTYPE thisSession;

	/* begin newSQFileByteArray */
	fileOop = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	file = arrayValueOf(fileOop);
	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	file->file = stdin;
	file->sessionID = thisSession;
	file->writable = 0;
	file->lastOp = 0;
	pop(2);
	push(fileOop);
	return 0;
}


/*	Answer the file handle for standard output of my OS process */

	/* UnixOSProcessPlugin>>#primitiveGetStdOutHandle */
EXPORT(sqInt)
primitiveGetStdOutHandle(void)
{
	getStdHandle(1);
	return 0;
}


/*	Answer the file handle for standard output of my OS process. The session
	identifier is passed as the parameter to this primitive. Use this variant
	if the session identifier is not available directly in the VM (as may be
	the case if it is not possible to link from this plugin to a variable
	elsewhere in the VM). */

	/* UnixOSProcessPlugin>>#primitiveGetStdOutHandleWithSessionIdentifier */
EXPORT(sqInt)
primitiveGetStdOutHandleWithSessionIdentifier(void)
{
    SQFile *file;
    sqInt fileOop;
    SESSIONIDENTIFIERTYPE thisSession;

	/* begin newSQFileByteArray */
	fileOop = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	file = arrayValueOf(fileOop);
	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	file->file = stdout;
	file->sessionID = thisSession;
	file->writable = 1;
	file->lastOp = 0;
	pop(2);
	push(fileOop);
	return 0;
}


/*	Answer the ID of the pthread that is currently executing (the interpreter
	thread). A thread ID may be a 64 bit value on some platforms, so answer a
	byte array
	containing the value in machine-dependent byte order.
 */

	/* UnixOSProcessPlugin>>#primitiveGetThreadID */
EXPORT(sqInt)
primitiveGetThreadID(void)
{
	popthenPush(1, newPthreadTypeByteArray(vmThread));
	return 0;
}


/*	Answer the user ID of my OS process */

	/* UnixOSProcessPlugin>>#primitiveGetUid */
EXPORT(sqInt)
primitiveGetUid(void)
{
    uid_t uid;

	uid = getuid();
	pop(1);
	pushInteger(uid);
	return 0;
}


/*	Take a struct SQFile from the stack, and call feof(3) to determine if the
	file has
	reached end of file.
 */
/*	Deprecated. The return values are reversed. Use
	primitiveTestEndOfFileFlag. 
 */

	/* UnixOSProcessPlugin>>#primitiveIsAtEndOfFile */
EXPORT(sqInt)
primitiveIsAtEndOfFile(void)
{
    FILEHANDLETYPE file;
    sqInt result;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	file = fileHandleFrom(sqFileOop);
	if (file == 0) {
		return primitiveFail();
	}
	if (feof(file)) {
		result = trueObject();
	}
	else {
		result = falseObject();
	}
	pop(2);
	push(result);
	return 0;
}


/*	Set a list of pids to kill with signum when VM exits. If the signum
	parameter is nil, the default value of SIGTERM will be used. */

	/* UnixOSProcessPlugin>>#primitiveKillOnExit */
EXPORT(sqInt)
primitiveKillOnExit(void)
{
    sqInt count;
    pid_t *p;
    pid_t pid;
    sqInt *pidPointer;
    sqInt pids;
    sqInt signum;

	pids = stackValue(1);
	signum = stackValue(0);
	pidCount = stSizeOf(pids);
	if (failed()) {
		return null;
	}
	if (!(pidArray == 0)) {
		free(pidArray);
	}
	pidArray = malloc(pidCount * sizeof(pid_t));
	if (pidArray == 0) {
		pidCount = 0;
		return primitiveFail();
	}
	pidPointer = firstIndexableField(pids);
	count = 0;
	p = pidArray;
	while (count <= pidCount) {
		pid = integerValueOf(*pidPointer++);
		*p++ = pid;
		count += 1;
	}
	if (!(signum == (nilObject()))) {
		sigNumToSend = checkedIntegerValueOf(signum);
	}
	if (failed()) {
		pidCount = 0;
	}
	pop(2);
	return 0;
}


/*	Take a struct SQFile from the stack, and request a lock on the specified
	region. If the exclusive flag is true, then request an exclusive (F_WRLCK)
	lock on the
	file. Otherwise, request a shared (F_RDLCK) lock. Any number of Unix
	processes may hold a read lock (shared lock) on a file region, but only
	one process may
	hold a write lock (exclusive lock). Answer the result of the call to
	fcntl(). 
	If length is zero, then the entire file will be locked, including region
	extents that
	have not yet been allocated for the file. */

	/* UnixOSProcessPlugin>>#primitiveLockFileRegion */
EXPORT(sqInt)
primitiveLockFileRegion(void)
{
    int exclusive;
    FILEHANDLETYPE fileHandle;
    int fileNo;
    sqInt len;
    struct flock lockStruct;
    int result;
    sqInt sqFileOop;
    sqInt start;


	/* Get the parameters from the stack */
	exclusive = (stackValue(0)) == (trueObject());
	len = stackIntegerValue(1);
	start = stackIntegerValue(2);
	sqFileOop = stackValue(3);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	/* begin unixFileNumber: */
	fileHandle = fileHandleFrom(sqFileOop);
	fileNo = fileno(fileHandle);
	if (exclusive) {
		lockStruct.l_type = F_WRLCK;
	}
	else {
		lockStruct.l_type = F_RDLCK;
	}
	lockStruct.l_whence = SEEK_SET;
	lockStruct.l_start = start;
	lockStruct.l_len = len;
	lockStruct.l_pid = 0;
	result = fcntl(fileNo, F_SETLK, &lockStruct);
	pop(5);
	pushInteger(result);
	return 0;
}


/*	Create a pipe, and answer an array of two file handles for the pipe writer
	and reader.
	The readerIOStream and writerIOStream variables represent the low level
	pipe streams,
	which will be of type (FILE *) or HANDLE, depending on what the FilePlugin
	support code is using to represent file streams. FILEHANDLETYPE is defined
	in my subclasses
	in the #declareCVarsIn: class method.
 */

	/* OSProcessPlugin>>#primitiveMakePipe */
EXPORT(sqInt)
primitiveMakePipe(void)
{
    sqInt arrayResult;
    sqInt reader;
    FILEHANDLETYPE readerIOStream;
    FILEHANDLETYPE *readerIOStreamPtr;
    SQFile *readerPtr;
    SESSIONIDENTIFIERTYPE thisSession;
    sqInt writer;
    FILEHANDLETYPE writerIOStream;
    FILEHANDLETYPE *writerIOStreamPtr;
    SQFile *writerPtr;

	/* begin getThisSessionIdentifier */
	thisSession = getThisSessionID();
	readerIOStreamPtr = (&readerIOStream);
	writerIOStreamPtr = (&writerIOStream);
	if (!(makePipeForReaderwriter(readerIOStreamPtr, writerIOStreamPtr))) {
		return primitiveFail();
	}
	/* begin newSQFileByteArray */
	writer = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	writerPtr = arrayValueOf(writer);
	writerPtr->file = writerIOStream;
	writerPtr->sessionID = thisSession;
	writerPtr->writable = 1;
	writerPtr->lastOp = 0;
	pushRemappableOop(writer);
	/* begin newSQFileByteArray */
	reader = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	readerPtr = arrayValueOf(reader);
	readerPtr->file = readerIOStream;
	readerPtr->sessionID = thisSession;
	readerPtr->writable = 0;
	readerPtr->lastOp = 0;
	pushRemappableOop(reader);
	arrayResult = instantiateClassindexableSize(classArray(), 2);
	stObjectatput(arrayResult, 1, popRemappableOop());
	stObjectatput(arrayResult, 2, popRemappableOop());
	pop(1);
	push(arrayResult);
	return 0;
}


/*	Create a pipe, and answer an array of two file handles for the pipe writer
	and reader.
	The session identifier is passed as the parameter to this primitive. Use
	this variant
	if the session identifier is not available directly in the VM (as may be
	the case if
	it is not possible to link from this plugin to a variable elsewhere in the
	VM). The readerIOStream and writerIOStream variables represent the low
	level pipe streams,
	which will be of type (FILE *) or HANDLE, depending on what the FilePlugin
	support code is using to represent file streams. FILEHANDLETYPE is defined
	in my subclasses
	in the #declareCVarsIn: class method.
 */

	/* OSProcessPlugin>>#primitiveMakePipeWithSessionIdentifier */
EXPORT(sqInt)
primitiveMakePipeWithSessionIdentifier(void)
{
    sqInt arrayResult;
    sqInt reader;
    FILEHANDLETYPE readerIOStream;
    FILEHANDLETYPE *readerIOStreamPtr;
    SQFile *readerPtr;
    SESSIONIDENTIFIERTYPE thisSession;
    sqInt writer;
    FILEHANDLETYPE writerIOStream;
    FILEHANDLETYPE *writerIOStreamPtr;
    SQFile *writerPtr;


	/* Create the anonymous OS pipe */
	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	readerIOStreamPtr = (&readerIOStream);
	writerIOStreamPtr = (&writerIOStream);
	if (!(makePipeForReaderwriter(readerIOStreamPtr, writerIOStreamPtr))) {
		return primitiveFail();
	}
	/* begin newSQFileByteArray */
	writer = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	writerPtr = arrayValueOf(writer);
	writerPtr->file = writerIOStream;
	writerPtr->sessionID = thisSession;
	writerPtr->writable = 1;
	writerPtr->lastOp = 0;
	pushRemappableOop(writer);
	/* begin newSQFileByteArray */
	reader = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	readerPtr = arrayValueOf(reader);
	readerPtr->file = readerIOStream;
	readerPtr->sessionID = thisSession;
	readerPtr->writable = 0;
	readerPtr->lastOp = 0;
	pushRemappableOop(reader);
	arrayResult = instantiateClassindexableSize(classArray(), 2);
	stObjectatput(arrayResult, 1, popRemappableOop());
	stObjectatput(arrayResult, 2, popRemappableOop());
	pop(2);
	push(arrayResult);
	return 0;
}


/*	Answer a string containing the module name string for this plugin. */

	/* OSProcessPlugin>>#primitiveModuleName */
EXPORT(sqInt)
primitiveModuleName(void)
{
	popthenPush(1, stringFromCString(getModuleName()));
	return 0;
}


/*	Change the scheduling priority of this process by the given nice
	increment. A positive increment decreases the priority. Only the superuser
	can specify
	a negative value (to increase the priority). See man(2) nice.
	
	Different versions of Unix are inconsistent in their return values. The
	only reliable test for success is to clear errno prior to the call, and
	test its value
	if the result of nice() is -1.
 */

	/* UnixOSProcessPlugin>>#primitiveNice */
EXPORT(sqInt)
primitiveNice(void)
{
    extern int errno;
    int niceIncrement;
    sqInt result;

	niceIncrement = stackIntegerValue(0);
	errno = 0;
	result = nice(niceIncrement);
	if (result == -1) {

		/* sys call may have failed, test errno to be sure */
		if (!(errno == 0)) {
			return primitiveFail();
		}
	}
	pop(2);
	pushInteger(result);
	return 0;
}


/*	Set an environment variable using a string of the form 'KEY=value'. This
	implementation allocates a C string using malloc to allocate from the C
	heap (using cStringFromString rather than transientCStringFromString).
	This is necessary because the C runtime library does not make a copy of
	the string into separately allocated environment memory. */

	/* UnixOSProcessPlugin>>#primitivePutEnv */
EXPORT(sqInt)
primitivePutEnv(void)
{
    char *cStringPtr;
    sqInt keyValueString;

	keyValueString = stackObjectValue(0);
	cStringPtr = cStringFromString(keyValueString);
	if ((putenv(cStringPtr)) == 0) {

		/* Set environment variable. */
		pop(2);
		push(keyValueString);
	}
	else {
		return primitiveFail();
	}
	return 0;
}


/*	Answer the real path for a path string as determined by realpath(). */

	/* UnixOSProcessPlugin>>#primitiveRealpath */
EXPORT(sqInt)
primitiveRealpath(void)
{
	return realpathAsType(classString());
}


/*	Answer a byte array with the real path for a path string as determined by
	realpath(). 
 */

	/* UnixOSProcessPlugin>>#primitiveRealpathAsBytes */
EXPORT(sqInt)
primitiveRealpathAsBytes(void)
{
	return realpathAsType(classByteArray());
}


/*	Clean up after the death of a child, and answer an array with the pid and
	the exit status of the child process. Answer nil if the pidToHandle does
	not exist.
 */

	/* UnixOSProcessPlugin>>#primitiveReapChildProcess */
EXPORT(sqInt)
primitiveReapChildProcess(void)
{
    sqInt *arrayPtr;
    int exitStatus;
    sqInt pid;
    pid_t pidResult;
    sqInt pidToHandle;
    sqInt resultArray;
    sqInt status;


	/* Force C code translator to declare the variable */
	exitStatus = 0;
	pidToHandle = stackIntegerValue(0);
	pidResult = waitpid ( pidToHandle, &exitStatus, WNOHANG );
	if (pidResult <= 0) {
		pop(2);
		push(nilObject());
	}
	else {

		/* Answer an array with pid and result status */
		pid = integerObjectOf(pidResult);
		status = integerObjectOf(exitStatus);
		resultArray = instantiateClassindexableSize(classArray(), 2);
		arrayPtr = firstIndexableField(resultArray);
		arrayPtr[0] = pid;
		arrayPtr[1] = status;
		pop(2);
		push(resultArray);
	}
	return 0;
}


/*	Answer the registration index of the semaphore currently associated with
	the signal handler for sigNum. */

	/* UnixOSProcessPlugin>>#primitiveSemaIndexFor */
EXPORT(sqInt)
primitiveSemaIndexFor(void)
{
    sqInt index;
    sqInt sigNum;

	sigNum = stackIntegerValue(0);
	if (semaIndices == null) {

		/* interpreter simulation */
		index = 0;
	}
	else {
		index = semaIndices[sigNum];
	}
	pop(2);
	pushInteger(index);
	return 0;
}


/*	Send SIGABRT (abort) to the OS process identified by the argument. Use an
	explicit check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigabrtTo */
EXPORT(sqInt)
primitiveSendSigabrtTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGABRT);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGALRM (alarm clock) to the OS process identified by the argument.
	Use an explicit
	check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigalrmTo */
EXPORT(sqInt)
primitiveSendSigalrmTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGALRM);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGCHLD (child status has changed, usually death of child) to the OS
	process identified by the argument. Use an explicit check for
	isIntegerObject so we can
	return -1 on error (the stackIntegerValue: method answers 1 on error, and
	1 is a
	valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigchldTo */
EXPORT(sqInt)
primitiveSendSigchldTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGCHLD);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGCONT (continue) to the OS process identified by the argument. Use
	an explicit
	check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigcontTo */
EXPORT(sqInt)
primitiveSendSigcontTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGCONT);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGHUP (hangup) to the OS process identified by the argument. Use an
	explicit check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSighupTo */
EXPORT(sqInt)
primitiveSendSighupTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGHUP);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGINT (interrupt) to the OS process identified by the argument. Use
	an explicit
	check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigintTo */
EXPORT(sqInt)
primitiveSendSigintTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGINT);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGKILL (kill, unblockable) to the OS process identified by the
	argument. Use an explicit
	check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigkillTo */
EXPORT(sqInt)
primitiveSendSigkillTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGKILL);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGPIPE (broken pipe) to the OS process identified by the argument.
	Use an explicit
	check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigpipeTo */
EXPORT(sqInt)
primitiveSendSigpipeTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGPIPE);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGQUIT (quit) to the OS process identified by the argument. Use an
	explicit check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigquitTo */
EXPORT(sqInt)
primitiveSendSigquitTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGQUIT);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGSTOP (stop, unblockable) to the OS process identified by the
	argument. Use an explicit
	check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigstopTo */
EXPORT(sqInt)
primitiveSendSigstopTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGSTOP);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGTERM (termination) to the OS process identified by the argument.
	Use an explicit
	check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method
	answers 1 on error, and 1 is a valid pid number).
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigtermTo */
EXPORT(sqInt)
primitiveSendSigtermTo(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGTERM);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGUSR1 (User-defined signal 1) to the OS process identified by the
	argument. Use
	an explicit check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method answers 1 on error, and 1 is a valid pid
	number). 
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigusr1To */
EXPORT(sqInt)
primitiveSendSigusr1To(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGUSR1);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Send SIGUSR2 (User-defined signal 2) to the OS process identified by the
	argument. Use
	an explicit check for isIntegerObject so we can return -1 on error (the
	stackIntegerValue: method answers 1 on error, and 1 is a valid pid
	number). 
 */

	/* UnixOSProcessPlugin>>#primitiveSendSigusr2To */
EXPORT(sqInt)
primitiveSendSigusr2To(void)
{
    pid_t pidToSignal;
    int result;


	/* Do not allow signal sending if running in secure mode */
	if ((sandboxSecurity()) == 1) {
		pop(2);
		pushInteger(-1);
	}
	else {
		if (isIntegerObject(stackValue(0))) {
			pidToSignal = stackIntegerValue(0);
			/* begin sendSignal:toPid: */
			result = kill(pidToSignal, SIGUSR2);
			pop(2);
			pushInteger(result);
		}
		else {
			pop(2);
			pushInteger(-1);
		}
	}
	return 0;
}


/*	Set the process group ID of the process identified by pid to a new process
	group ID. */

	/* UnixOSProcessPlugin>>#primitiveSetPGid */
EXPORT(sqInt)
primitiveSetPGid(void)
{
    pid_t pgid;
    pid_t pid;

	pid = stackIntegerValue(1);
	pgid = stackIntegerValue(0);
	if ((setpgid(pid, pgid)) == -1) {
		return primitiveFail();
	}
	pop(2);
	return 0;
}


/*	Set a new process group for this OS process. Newly created child processes
	will be members of the new process group. Note: Use setpgid(0,0) rather
	than the equivalent setpgrp() because setpgrp() is implemented differently
	on some flavors of Unix. */

	/* UnixOSProcessPlugin>>#primitiveSetPGrp */
EXPORT(sqInt)
primitiveSetPGrp(void)
{
	if ((setpgid(0, 0)) == -1) {
		return primitiveFail();
	}
	return 0;
}


/*	Set the index of the semaphore used by the OSProcess with which I
	collaborate. My
	OSProcess should set this value so that I can use it when handling SIGCHLD
	signals (death of child). In the C translation this is a static int which
	would be shared by all
	instances of UnixOSProcessPlugin, which is expected to be a singleton.
	Answer the
	value of the semaphore index.
 */

	/* UnixOSProcessPlugin>>#primitiveSetSemaIndex */
EXPORT(sqInt)
primitiveSetSemaIndex(void)
{
	sigChldSemaIndex = stackIntegerValue(0);
	pop(2);
	pushInteger(sigChldSemaIndex);
	return 0;
}


/*	Quoted from Linux man pages:
	setsid() creates a new session if the calling process is not a process
	group leader.
	The calling process is the leader of the new session, the process group
	leader of
	the new process group, and has no controlling tty. The process group ID
	and session
	ID of the calling process are set to the PID of the calling process. The
	calling process will be the only process in this new process group and in
	this new session. */

	/* UnixOSProcessPlugin>>#primitiveSetSid */
EXPORT(sqInt)
primitiveSetSid(void)
{
    pid_t sessionId;

	sessionId = setsid();
	if (sessionId == -1) {
		return primitiveFail();
	}
	pop(1);
	pushInteger(sessionId);
	return 0;
}


/*	Integer value corresponding to SIGCHLD */

	/* UnixOSProcessPlugin>>#primitiveSigChldNumber */
EXPORT(sqInt)
primitiveSigChldNumber(void)
{
	pop(1);
	pushInteger(SIGCHLD);
	return 0;
}


/*	Integer value corresponding to SIGHUP */

	/* UnixOSProcessPlugin>>#primitiveSigHupNumber */
EXPORT(sqInt)
primitiveSigHupNumber(void)
{
	pop(1);
	pushInteger(SIGHUP);
	return 0;
}


/*	Integer value corresponding to SIGINT */

	/* UnixOSProcessPlugin>>#primitiveSigIntNumber */
EXPORT(sqInt)
primitiveSigIntNumber(void)
{
	pop(1);
	pushInteger(SIGINT);
	return 0;
}


/*	Integer value corresponding to SIGKILL */

	/* UnixOSProcessPlugin>>#primitiveSigKillNumber */
EXPORT(sqInt)
primitiveSigKillNumber(void)
{
	pop(1);
	pushInteger(SIGKILL);
	return 0;
}


/*	Integer value corresponding to SIGPIPE */

	/* UnixOSProcessPlugin>>#primitiveSigPipeNumber */
EXPORT(sqInt)
primitiveSigPipeNumber(void)
{
	pop(1);
	pushInteger(SIGPIPE);
	return 0;
}


/*	Integer value corresponding to SIGQUIT */

	/* UnixOSProcessPlugin>>#primitiveSigQuitNumber */
EXPORT(sqInt)
primitiveSigQuitNumber(void)
{
	pop(1);
	pushInteger(SIGQUIT);
	return 0;
}


/*	Integer value corresponding to SIGTERM */

	/* UnixOSProcessPlugin>>#primitiveSigTermNumber */
EXPORT(sqInt)
primitiveSigTermNumber(void)
{
	pop(1);
	pushInteger(SIGTERM);
	return 0;
}


/*	Integer value corresponding to SIGUSR1 */

	/* UnixOSProcessPlugin>>#primitiveSigUsr1Number */
EXPORT(sqInt)
primitiveSigUsr1Number(void)
{
	pop(1);
	pushInteger(SIGUSR1);
	return 0;
}


/*	Integer value corresponding to SIGUSR2 */

	/* UnixOSProcessPlugin>>#primitiveSigUsr2Number */
EXPORT(sqInt)
primitiveSigUsr2Number(void)
{
	pop(1);
	pushInteger(SIGUSR2);
	return 0;
}


/*	Size in bytes of an integer, for this C compiler on this machine. */

	/* OSProcessPlugin>>#primitiveSizeOfInt */
EXPORT(sqInt)
primitiveSizeOfInt(void)
{
	pop(1);
	pushInteger(sizeof(int));
	return 0;
}


/*	Size in bytes of a void pointer, for this C compiler on this machine. */

	/* OSProcessPlugin>>#primitiveSizeOfPointer */
EXPORT(sqInt)
primitiveSizeOfPointer(void)
{
	pop(1);
	pushInteger(sizeof(void *));
	return 0;
}


/*	Take a struct SQFile from the stack, and call fflush() to flush the OS
	stream. This flushes the
	file stream in the C library, not the stream in Smalltalk. For output
	streams, consider setting
	the OS stream (C library) to unbuffered output, and letting Smalltalk do
	all the buffering.
 */

	/* UnixOSProcessPlugin>>#primitiveSQFileFlush */
EXPORT(sqInt)
primitiveSQFileFlush(void)
{
    int result;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	result = fflush(fileHandleFrom(sqFileOop));
	pop(2);
	pushInteger(result);
	return 0;
}


/*	Take a struct SQFile from the stack, and call fflush() to flush the OS
	stream. This flushes the
	file stream in the C library, not the stream in Smalltalk. For output
	streams, consider setting
	the OS stream (C library) to unbuffered output, and letting Smalltalk do
	all the buffering.
	The session identifier is passed as the parameter to this primitive. Use
	this variant if the session
	identifier is not available directly in the VM (as may be the case if it
	is not possible to link from
	this plugin to a variable elsewhere in the VM).
 */

	/* UnixOSProcessPlugin>>#primitiveSQFileFlushWithSessionIdentifier */
EXPORT(sqInt)
primitiveSQFileFlushWithSessionIdentifier(void)
{
    int result;
    sqInt sqFileOop;

	sqFileOop = stackValue(1);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	result = fflush(fileHandleFrom(sqFileOop));
	pop(3);
	pushInteger(result);
	return 0;
}


/*	Take a struct SQFile from the stack, and call fcntl() to set the file for
	blocking I/O.
 */

	/* UnixOSProcessPlugin>>#primitiveSQFileSetBlocking */
EXPORT(sqInt)
primitiveSQFileSetBlocking(void)
{
    sqInt descriptor;
    int flags;
    sqInt retVal;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	/* begin fileDescriptorFrom: */
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		descriptor = -1;
		goto l1;
	}
	descriptor = fileno(fileHandleFrom(sqFileOop));
	l1:	/* end fileDescriptorFrom: */;
	if (descriptor == -1) {
		return primitiveFail();
	}
	flags = fcntl(descriptor, F_GETFL);
	retVal = fcntl(descriptor, F_SETFL, flags & ~O_NONBLOCK);
	pop(2);
	pushInteger(retVal);
	return 0;
}


/*	Take a struct SQFile from the stack, and call fcntl() to set the file for
	blocking I/O.
	Use this variant if the session identifier is not available directly in
	the VM (as may be
	the case if it is not possible to link from this plugin to a variable
	elsewhere in the VM).
 */

	/* UnixOSProcessPlugin>>#primitiveSQFileSetBlockingWithSessionIdentifier */
EXPORT(sqInt)
primitiveSQFileSetBlockingWithSessionIdentifier(void)
{
    sqInt descriptor;
    sqInt flags;
    sqInt retVal;
    SQFile *sqFile;
    sqInt sqFileOop;
    SESSIONIDENTIFIERTYPE thisSession;

	sqFileOop = stackValue(1);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	sqFile = arrayValueOf(sqFileOop);
	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	if (thisSession == (sqFile->sessionID)) {
		/* begin fileDescriptorFrom: */
		if (!((((isBytes(sqFileOop))
			 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
			 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
			 && (isNonNullSQFile(sqFileOop)))) {
			descriptor = -1;
			goto l1;
		}
		descriptor = fileno(fileHandleFrom(sqFileOop));
	l1:	/* end fileDescriptorFrom: */;
		if (descriptor < 0) {
			return primitiveFail();
		}
		flags = fcntl(descriptor, F_GETFL);
		retVal = fcntl(descriptor, F_SETFL, flags & ~O_NONBLOCK);
		pop(3);
		pushInteger(retVal);
	}
	else {
		return primitiveFail();
	}
	return 0;
}


/*	Take a struct SQFile from the stack, and call fcntl() to set the file
	non-blocking I/O.
 */

	/* UnixOSProcessPlugin>>#primitiveSQFileSetNonBlocking */
EXPORT(sqInt)
primitiveSQFileSetNonBlocking(void)
{
    sqInt descriptor;
    sqInt flags;
    sqInt retVal;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	/* begin fileDescriptorFrom: */
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		descriptor = -1;
		goto l1;
	}
	descriptor = fileno(fileHandleFrom(sqFileOop));
	l1:	/* end fileDescriptorFrom: */;
	if (descriptor < 0) {
		return primitiveFail();
	}
	flags = fcntl(descriptor, F_GETFL);
	retVal = fcntl(descriptor, F_SETFL, flags | O_NONBLOCK);
	pop(2);
	pushInteger(retVal);
	return 0;
}


/*	Take a struct SQFile from the stack, and call fcntl() to set the file
	non-blocking I/O.
	Use this variant if the session identifier is not available directly in
	the VM (as may be
	the case if it is not possible to link from this plugin to a variable
	elsewhere in the VM).
 */

	/* UnixOSProcessPlugin>>#primitiveSQFileSetNonBlockingWithSessionIdentifier */
EXPORT(sqInt)
primitiveSQFileSetNonBlockingWithSessionIdentifier(void)
{
    sqInt descriptor;
    sqInt flags;
    sqInt retVal;
    SQFile *sqFile;
    sqInt sqFileOop;
    SESSIONIDENTIFIERTYPE thisSession;

	sqFileOop = stackValue(1);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	sqFile = arrayValueOf(sqFileOop);
	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	if (thisSession == (sqFile->sessionID)) {
		/* begin fileDescriptorFrom: */
		if (!((((isBytes(sqFileOop))
			 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
			 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
			 && (isNonNullSQFile(sqFileOop)))) {
			descriptor = -1;
			goto l1;
		}
		descriptor = fileno(fileHandleFrom(sqFileOop));
	l1:	/* end fileDescriptorFrom: */;
		if (descriptor < 0) {
			return primitiveFail();
		}
		flags = fcntl(descriptor, F_GETFL);
		retVal = fcntl(descriptor, F_SETFL, flags | O_NONBLOCK);
		pop(3);
		pushInteger(retVal);
	}
	else {
		return primitiveFail();
	}
	return 0;
}


/*	Take a struct SQFile from the stack, and call setbuf() to set the OS file
	stream (implemented in the C library) for unbuffered I/O. Answers the
	result of a fflush()
	call, not the result of the setbuf() call (which is type void). This is
	nearly useless,
	but may at least provide an indicator that we are operating on a valid
	file stream.
 */

	/* UnixOSProcessPlugin>>#primitiveSQFileSetUnbuffered */
EXPORT(sqInt)
primitiveSQFileSetUnbuffered(void)
{
    FILEHANDLETYPE file;
    sqInt retVal;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	file = fileHandleFrom(sqFileOop);
	retVal = fflush(file);
	setbuf(file, 0);
	pop(2);
	pushInteger(retVal);
	return 0;
}


/*	Take a struct SQFile from the stack, and call setbuf() to set the OS file
	stream (implemented in
	the C library) for unbuffered I/O. Answers the result of a fflush() call,
	not the result of the
	setbuf() call (which is type void). This is nearly useless, but may at
	least provide an indicator
	that we are operating on a valid file stream. Use this variant if the
	session identifier is not
	available directly in the VM (as may be the case if it is not possible to
	link from this plugin
	to a variable elsewhere in the VM).
 */

	/* UnixOSProcessPlugin>>#primitiveSQFileSetUnbufferedWithSessionIdentifier */
EXPORT(sqInt)
primitiveSQFileSetUnbufferedWithSessionIdentifier(void)
{
    sqInt retVal;
    SQFile *sqFile;
    sqInt sqFileOop;
    SESSIONIDENTIFIERTYPE thisSession;

	sqFileOop = stackValue(1);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	sqFile = arrayValueOf(sqFileOop);
	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	if (thisSession == (sqFile->sessionID)) {
		retVal = fflush(sqFile->file);
		setbuf(sqFile->file, NULL);
		pop(3);
		pushInteger(retVal);
	}
	else {
		return primitiveFail();
	}
	return 0;
}


/*	Take a struct SQFile from the stack, and call feof(3) to determine if the
	file has
	reached end of file. The flag is set only by a previous read operation, so
	end of
	file is not detected until an actual EOF condition has been detected by a
	read attempt.
 */

	/* UnixOSProcessPlugin>>#primitiveTestEndOfFileFlag */
EXPORT(sqInt)
primitiveTestEndOfFileFlag(void)
{
    FILEHANDLETYPE file;
    sqInt result;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	file = fileHandleFrom(sqFileOop);
	if (file == 0) {
		return primitiveFail();
	}
	if (feof(file)) {
		result = trueObject();
	}
	else {
		result = falseObject();
	}
	pop(2);
	push(result);
	return 0;
}


/*	Take a struct SQFile from the stack, and check for ability to lock the
	specified region.
	If the exclusive flag is true, then specify an exclusive (F_WRLCK) lock on
	the file. Otherwise, specify a shared (F_RDLCK) lock. Any number of Unix
	processes may hold a read lock (shared lock) on a file region, but only
	one process may
	hold a write lock (exclusive lock).
	
	If length is zero, then the request is for the entire file to be locked,
	including region extents that have not yet been allocated for the file.
	
	If the fcntl() call fails, answer -1 (the result of the failed call).
	Otherwise, answer an array with the following six fields:
	lockable (true or false)
	l_pid (pid of the process preventing this lock request, or nil)
	l_type (request type F_WRLCK or F_RDLOCK of the process preventing this
	lock request)
	l_whence (the SEEK_SET, SEEK_CUR, or SEEK_END value of the lock preventing
	this lock request).
	l_start (offset of the region lock preventing this lock request)
	l_len (length of the region lock preventing this lock request) */

	/* UnixOSProcessPlugin>>#primitiveTestLockableFileRegion */
EXPORT(sqInt)
primitiveTestLockableFileRegion(void)
{
    sqInt canObtainLock;
    int exclusive;
    FILEHANDLETYPE fileHandle;
    int fileNo;
    sqInt len;
    struct flock lockStruct;
    int result;
    sqInt resultArray;
    sqInt sqFileOop;
    sqInt start;


	/* Get the parameters from the stack */
	exclusive = (stackValue(0)) == (trueObject());
	len = stackIntegerValue(1);
	start = stackIntegerValue(2);
	sqFileOop = stackValue(3);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	/* begin unixFileNumber: */
	fileHandle = fileHandleFrom(sqFileOop);
	fileNo = fileno(fileHandle);
	if (exclusive) {
		lockStruct.l_type = F_WRLCK;
	}
	else {
		lockStruct.l_type = F_RDLCK;
	}
	lockStruct.l_whence = SEEK_SET;
	lockStruct.l_start = start;
	lockStruct.l_len = len;
	lockStruct.l_pid = 0;
	result = fcntl(fileNo, F_GETLK, &lockStruct);
	if (result == -1) {
		pop(5);
		pushInteger(result);
	}
	else {
		if (lockStruct.l_type == F_UNLCK) {
			canObtainLock = trueObject();
		}
		else {
			canObtainLock = falseObject();
		}
		resultArray = instantiateClassindexableSize(classArray(), 6);
		stObjectatput(resultArray, 1, canObtainLock);
		stObjectatput(resultArray, 2, integerObjectOf(lockStruct.l_pid));
		stObjectatput(resultArray, 3, integerObjectOf(lockStruct.l_type));
		stObjectatput(resultArray, 4, integerObjectOf(lockStruct.l_whence));
		stObjectatput(resultArray, 5, integerObjectOf(lockStruct.l_start));
		stObjectatput(resultArray, 6, integerObjectOf(lockStruct.l_len));
		popthenPush(5, resultArray);
	}
	return 0;
}


/*	Close a file handle at the close(2) level, using a handle returned by
	#primitiveUnixFileNumber. */

	/* UnixOSProcessPlugin>>#primitiveUnixFileClose: */
EXPORT(sqInt)
primitiveUnixFileClose(sqInt anIntegerFileNumber)
{
    int handle;
    int result;

	handle = stackIntegerValue(0);
	result = close(handle);
	pop(2);
	pushInteger(result);
	return 0;
}


/*	Take a struct SQFile from the stack, and answer the value of its Unix file
	number. The Unix file number is not directly useful to Squeak, but may be
	interesting for
	debugging problems involving failure to close unused file handles.
 */

	/* UnixOSProcessPlugin>>#primitiveUnixFileNumber */
EXPORT(sqInt)
primitiveUnixFileNumber(void)
{
    FILEHANDLETYPE fileHandle;
    int fileNo;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	/* begin unixFileNumber: */
	fileHandle = fileHandleFrom(sqFileOop);
	fileNo = fileno(fileHandle);
	pop(2);
	pushInteger(fileNo);
	return 0;
}


/*	Take a struct SQFile from the stack, and unlock the specified region.
	Answer the result of the call to fcntl(). If the region is in the file
	lock cache,
	remove it, but otherwise ignore the cache. The cache supports Win32
	semantics within a single Squeak image, but not across separate images,
	therefore the
	unlock should be attempted regardless of whether this image thinks that
	the region has previously been locked. Answer the result of the call to
	fcntl(). 
 */

	/* UnixOSProcessPlugin>>#primitiveUnlockFileRegion */
EXPORT(sqInt)
primitiveUnlockFileRegion(void)
{
    FILEHANDLETYPE fileHandle;
    int fileNo;
    sqInt len;
    struct flock lockStruct;
    int result;
    sqInt sqFileOop;
    sqInt start;


	/* Get the parameters from the stack */
	len = stackIntegerValue(0);
	start = stackIntegerValue(1);
	sqFileOop = stackValue(2);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	/* begin unixFileNumber: */
	fileHandle = fileHandleFrom(sqFileOop);
	fileNo = fileno(fileHandle);
	lockStruct.l_type = F_UNLCK;
	lockStruct.l_whence = SEEK_SET;
	lockStruct.l_start = start;
	lockStruct.l_len = len;
	lockStruct.l_pid = 0;
	result = fcntl(fileNo, F_SETLK, &lockStruct);
	pop(4);
	pushInteger(result);
	return 0;
}


/*	Unset an environment variable. */
/*	FIXME: unsetenv() is not portable. For Solaris or any other system which
	does not
	support unsetenv(), just comment it out in this method and rebuild the
	plugin. 
 */

	/* UnixOSProcessPlugin>>#primitiveUnsetEnv */
EXPORT(sqInt)
primitiveUnsetEnv(void)
{
	unsetenv(transientCStringFromString(stackObjectValue(0)));
	pop(1);
	return 0;
}


/*	Answer a string containing the version string for this plugin. */

	/* OSProcessPlugin>>#primitiveVersionString */
EXPORT(sqInt)
primitiveVersionString(void)
{
	pop(1);
	push(stringFromCString(versionString()));
	return 0;
}


/*	Answer the real path for a path string as determined by realpath(). */

	/* UnixOSProcessPlugin>>#realpathAsType: */
static sqInt
realpathAsType(sqInt classIdentifier)
{
    char *buffer;
    sqInt bufferSize;
    sqInt len;
    sqInt newPathString;
    sqInt newString;
    char *pathString;
    char * realpathResult;
    sqInt s;

	bufferSize = 1024;
	newPathString = instantiateClassindexableSize(classString(), bufferSize);
	pushRemappableOop(newPathString);
	pathString = transientCStringFromString(stackObjectValue(0));
	newPathString = popRemappableOop();
	buffer = arrayValueOf(newPathString);
	realpathResult = realpath(pathString, buffer);
	if (realpathResult == 0) {
		return primitiveFail();
	}
	else {
		if ((strlen(realpathResult)) >= 1024) {
			perror("warning: statically allocated array exceeded in UnixOSProcessPlugin>>primitiveRealPath, object memory may have been corrupted");
			return primitiveFail();
		}
		/* begin cString:asCollection: */
		len = strlen(realpathResult);
		newString = instantiateClassindexableSize(classIdentifier, len);
		strncpy(arrayValueOf(newString), realpathResult, len);
		s = newString;
		pop(2);
		push(s);
	}
	return 0;
}


/*	This is a signal handler for SIGCHLD. It is not meant to be called from
	Smalltalk, and should only be called indirectly as a result of a death of
	child signal from
	the operating system.
	
	Child processes must be cleaned up by the parent, otherwise they continue
	to exist as zombies until the parent exits. This handler resets the signal
	handler to catch the next SIGCHLD signal, then sets a semaphore to notify
	the system
	that a child process needs to be cleaned up. The actual clean up is done
	by a
	Smalltalk process which waits on the semaphore, then calls
	primitiveReapChildProcess. 
	Note: If child processes die faster than we can clean them up, signals
	will be lost
	and child processes will remain as zombies.
 */

	/* UnixOSProcessPlugin>>#reapChildProcess: */
static void
reapChildProcess(int sigNum)
{
	
#if !defined(SA_NOCLDSTOP)
	setSigChldHandler();
	
#endif /* defined(SA_NOCLDSTOP) */
	if (sigChldSemaIndex > 0) {
		signalSemaphoreWithIndex(sigChldSemaIndex);
	}
}


/*	Signal sigNum has been caught by a thread other than the pthread in which
	the interpreter is executing. Rather than handling it in this thread,
	resend it to the interpreter thread. */

	/* UnixOSProcessPlugin>>#resendSignal: */
static sqInt
resendSignal(int sigNum)
{
	pthread_kill(vmThread, sigNum);
	return 0;
}


/*	Restore signal handlers to their original behaviors. */

	/* UnixOSProcessPlugin>>#restoreDefaultSignalHandlers */
static void
restoreDefaultSignalHandlers(void)
{
    sqInt sigNum;

	if (!(semaIndices == null)) {

		/* nil if in interpreter simulation */
		sigNum = 1;
		while (sigNum <= (signalArraySize())) {
			if ((semaIndices[sigNum]) > 0) {
				setSignalNumberhandler(sigNum, (originalSignalHandlers())[sigNum]);
			}
			sigNum += 1;
		}
	}
}


/*	Answer 1 if running in secure mode, else 0. The osprocessSandboxSecurity
	variable is initialized to -1. On the first call to this method, set its
	value to
	either 0 (user has full access to the plugin) or 1 (user is not permitted
	to do
	dangerous things).
 */

	/* OSProcessPlugin>>#sandboxSecurity */
static sqInt
sandboxSecurity(void)
{
	if (osprocessSandboxSecurity < 0) {
		osprocessSandboxSecurity = securityHeurisitic();
	}
	return osprocessSandboxSecurity;
}


/*	Answer 0 to permit full access to OSProcess functions, or 1 if access
	should be
	restricted for dangerous functions. The rules are:
	- If the security plugin is not present, grant full access
	- If the security plugin can be loaded, restrict access unless user has
	all of secCanWriteImage, secHasFileAccess and secHasSocketAccess */
/*	FIXME: This function has not been tested. -dtl */
/*	If the security plugin can be loaded, use it to check. If not, assume it's
	ok 
 */

	/* OSProcessPlugin>>#securityHeurisitic */
static sqInt
securityHeurisitic(void)
{
    sqInt canWriteImage;
    sqInt hasFileAccess;
    sqInt hasSocketAccess;
    void (*sCWIfn)(void);
    void (*sHFAfn)(void);
    void (*sHSAfn)(void);

	sCWIfn = ioLoadFunctionFrom("secCanWriteImage", "SecurityPlugin");
	if (sCWIfn == 0) {
		return 0;
	}
	canWriteImage = ((sqInt (*) (void)) sCWIfn)();
	sHFAfn = ioLoadFunctionFrom("secHasFileAccess", "SecurityPlugin");
	if (sHFAfn == 0) {
		return 0;
	}
	hasFileAccess = ((sqInt (*) (void)) sHFAfn)();
	sHSAfn = ioLoadFunctionFrom("secHasSocketAccess", "SecurityPlugin");
	if (sHSAfn == 0) {
		return 0;
	}
	hasSocketAccess = ((sqInt (*) (void)) sHSAfn)();
	return ((canWriteImage
	 && (hasFileAccess))
	 && (hasSocketAccess)
		? 0
		: 1);
}


/*	Exit function to be registered with atexit() to signal child processes on
	VM exit.
 */

	/* UnixOSProcessPlugin>>#sendSignalToPids */
static void
sendSignalToPids(void)
{
    sqInt count;
    pid_t pid;

	count = 0;
	while (count < pidCount) {
		pid = pidArray[count];
		kill(pid, sigNumToSend);
		count += 1;
	}
}


/*	kill(pid, sig) */

	/* UnixOSProcessPlugin>>#sendSignal:toPid: */
static sqInt
sendSignaltoPid(sqInt sig, sqInt pid)
{
	return kill(pid, sig);
}


/*	Answer a session ID represented by aByteArray. The session ID is used in
	the SQFile structure. If that data structure changes, we should see
	compiler warnings about type mismatch with SESSIONIDENTIFIERTYPE. */

	/* OSProcessPlugin>>#sessionIdentifierFrom: */
static SESSIONIDENTIFIERTYPE
sessionIdentifierFrom(sqInt aByteArray)
{
    sqInt idx;
    unsigned char *session;
    union {SESSIONIDENTIFIERTYPE session; unsigned char bytes[sizeof(SESSIONIDENTIFIERTYPE)];} sessionUnion;

	if (!((isBytes(aByteArray))
		 && ((stSizeOf(aByteArray)) == (sizeOfSession())))) {
		return null;
	}
	session = arrayValueOf(aByteArray);
	idx = 0;
	while (idx < (sizeOfSession())) {
		sessionUnion.bytes[idx] = session[idx];
		idx += 1;
	}
	return sessionUnion.session;
}


/*	Note: This is coded so that it can be run in Squeak. */

	/* InterpreterPlugin>>#setInterpreter: */
EXPORT(sqInt)
setInterpreter(struct VirtualMachine *anInterpreter)
{
    sqInt ok;

	interpreterProxy = anInterpreter;
	ok = ((interpreterProxy->majorVersion()) == (VM_PROXY_MAJOR))
	 && ((interpreterProxy->minorVersion()) >= (VM_PROXY_MINOR));
	if (ok) {
		
#if !defined(SQUEAK_BUILTIN_PLUGIN)
		arrayValueOf = interpreterProxy->arrayValueOf;
		byteSizeOf = interpreterProxy->byteSizeOf;
		checkedIntegerValueOf = interpreterProxy->checkedIntegerValueOf;
		classArray = interpreterProxy->classArray;
		classByteArray = interpreterProxy->classByteArray;
		classString = interpreterProxy->classString;
		failed = interpreterProxy->failed;
		falseObject = interpreterProxy->falseObject;
		firstIndexableField = interpreterProxy->firstIndexableField;
		getThisSessionID = interpreterProxy->getThisSessionID;
		instantiateClassindexableSize = interpreterProxy->instantiateClassindexableSize;
		integerObjectOf = interpreterProxy->integerObjectOf;
		integerValueOf = interpreterProxy->integerValueOf;
		ioLoadFunctionFrom = interpreterProxy->ioLoadFunctionFrom;
		isBytes = interpreterProxy->isBytes;
		isIntegerObject = interpreterProxy->isIntegerObject;
		methodArgumentCount = interpreterProxy->methodArgumentCount;
		methodReturnValue = interpreterProxy->methodReturnValue;
		nilObject = interpreterProxy->nilObject;
		pop = interpreterProxy->pop;
		popthenPush = interpreterProxy->popthenPush;
		popRemappableOop = interpreterProxy->popRemappableOop;
		primitiveFail = interpreterProxy->primitiveFail;
		primitiveFailFor = interpreterProxy->primitiveFailFor;
		push = interpreterProxy->push;
		pushInteger = interpreterProxy->pushInteger;
		pushRemappableOop = interpreterProxy->pushRemappableOop;
		signalSemaphoreWithIndex = interpreterProxy->signalSemaphoreWithIndex;
		sizeOfSTArrayFromCPrimitive = interpreterProxy->sizeOfSTArrayFromCPrimitive;
		stObjectatput = interpreterProxy->stObjectatput;
		stSizeOf = interpreterProxy->stSizeOf;
		stackIntegerValue = interpreterProxy->stackIntegerValue;
		stackObjectValue = interpreterProxy->stackObjectValue;
		stackValue = interpreterProxy->stackValue;
		trueObject = interpreterProxy->trueObject;
#endif /* !defined(SQUEAK_BUILTIN_PLUGIN) */
	}
	return ok;
}


/*	Set the SIGCHLD signal handler in the virtual machine. */

	/* UnixOSProcessPlugin>>#setSigChldHandler */
static void
setSigChldHandler(void)
{
    struct sigaction sigchldHandlerAction;

	
#  if defined(SA_NOCLDSTOP)
	sigchldHandlerAction.sa_sigaction = reapChildProcess;
	sigchldHandlerAction.sa_flags = SA_NODEFER | SA_NOCLDSTOP;
	if (needSigaltstack()) {
		sigchldHandlerAction.sa_flags |= SA_ONSTACK;
	}
	sigemptyset(&sigchldHandlerAction.sa_mask);
	if ((sigaction(SIGCHLD, &sigchldHandlerAction, 0)) == (sigErrorNumber())) {
		perror("signal");
	}
#  else /* defined(SA_NOCLDSTOP) */
	setSignalNumberhandler(SIGCHLD, reapChildProcess);
#  endif /* defined(SA_NOCLDSTOP) */
}

	/* UnixOSProcessPlugin>>#setSigIntDefaultHandler */
static void
setSigIntDefaultHandler(void)
{
	setSignalNumberhandler(SIGINT, sigDefaultNumber());
}


/*	Set the SIGINT signal handler in the virtual machine to ignore interrupts. */

	/* UnixOSProcessPlugin>>#setSigIntIgnore */
static void
setSigIntIgnore(void)
{
	setSignalNumberhandler(SIGINT, sigIgnoreNumber());
}


/*	Set a signal handler. The C code translator will convert #sig:nal: into
	'signal(parm1, parm2)'
 */

	/* UnixOSProcessPlugin>>#setSignalNumber:handler: */
static void *
setSignalNumberhandler(sqInt signalNumber, void *signalHandlerAddress)
{
    struct sigaction oldHandlerAction;
    struct sigaction sigHandlerAction;

	if (!(needSigaltstack())) {
		return signal(signalNumber, signalHandlerAddress);
	}
	sigHandlerAction.sa_sigaction = signalHandlerAddress;
	sigHandlerAction.sa_flags = SA_ONSTACK | SA_RESTART;
	sigemptyset(&sigHandlerAction.sa_mask);
	if ((sigaction(signalNumber, (&sigHandlerAction), (&oldHandlerAction))) == (sigErrorNumber())) {
		perror("signal");
	}
	return oldHandlerAction.sa_sigaction;
}

	/* UnixOSProcessPlugin>>#setSigPipeDefaultHandler */
static void
setSigPipeDefaultHandler(void)
{
	setSignalNumberhandler(SIGPIPE, sigDefaultNumber());
}


/*	Set the SIGPIPE signal handler in the virtual machine to ignore pipe error
	signals. If a pipe is opened to a child process, and the child exits, then
	subsequent writes to
	the pipe generate a SIGPIPE signal. If this signal is not handled, the VM
	will exit
	without warning.
 */

	/* UnixOSProcessPlugin>>#setSigPipeHandler */
static sqInt
setSigPipeHandler(void)
{
	/* begin setSigPipeIgnore */
	setSignalNumberhandler(SIGPIPE, sigIgnoreNumber());
	return 0;
}


/*	Set the SIGPIPE signal handler in the virtual machine to ignore pipe error
	signals. 
 */

	/* UnixOSProcessPlugin>>#setSigPipeIgnore */
static void
setSigPipeIgnore(void)
{
	setSignalNumberhandler(SIGPIPE, sigIgnoreNumber());
}

	/* UnixOSProcessPlugin>>#shutdownModule */
EXPORT(sqInt)
shutdownModule(void)
{
    sqInt sigNum;

	/* begin restoreDefaultSignalHandlers */
	if (!(semaIndices == null)) {

		/* nil if in interpreter simulation */
		sigNum = 1;
		while (sigNum <= (signalArraySize())) {
			if ((semaIndices[sigNum]) > 0) {
				setSignalNumberhandler(sigNum, (originalSignalHandlers())[sigNum]);
			}
			sigNum += 1;
		}
	}
	return 0;
}


/*	Abort signal from abort(3) */

	/* UnixOSProcessPlugin>>#sigAbrtNumber */
static sqInt
sigAbrtNumber(void)
{
	return SIGABRT;
}


/*	Timer signal from alarm(2) */

	/* UnixOSProcessPlugin>>#sigAlrmNumber */
static sqInt
sigAlrmNumber(void)
{
	return SIGALRM;
}


/*	Child status has changed (POSIX). */

	/* UnixOSProcessPlugin>>#sigChldNumber */
static sqInt
sigChldNumber(void)
{
	return SIGCHLD;
}


/*	Continue if stopped */

	/* UnixOSProcessPlugin>>#sigContNumber */
static sqInt
sigContNumber(void)
{
	return SIGCONT;
}


/*	Default action for a signal */

	/* UnixOSProcessPlugin>>#sigDefaultNumber */
static void *
sigDefaultNumber(void)
{
	return SIG_DFL;
}


/*	Error return from signal() */

	/* UnixOSProcessPlugin>>#sigErrorNumber */
static void *
sigErrorNumber(void)
{
	return SIG_ERR;
}


/*	Hold action for a signal */

	/* UnixOSProcessPlugin>>#sigHoldNumber */
static void *
sigHoldNumber(void)
{
	return SIG_HOLD;
}


/*	Hangup detected on controlling terminal or death of controlling process */

	/* UnixOSProcessPlugin>>#sigHupNumber */
static sqInt
sigHupNumber(void)
{
	return SIGHUP;
}


/*	Ignore action for a signal */

	/* UnixOSProcessPlugin>>#sigIgnoreNumber */
static void *
sigIgnoreNumber(void)
{
	return SIG_IGN;
}


/*	Interrupt (ANSI). */

	/* UnixOSProcessPlugin>>#sigIntNumber */
static sqInt
sigIntNumber(void)
{
	return SIGINT;
}


/*	Kill signal */

	/* UnixOSProcessPlugin>>#sigKillNumber */
static sqInt
sigKillNumber(void)
{
	return SIGKILL;
}


/*	Number of possible signals for this OS plus one. The signal handler arrays
	declared in #declareCVarsIn: are this size. */

	/* UnixOSProcessPlugin>>#signalArraySize */
static sqInt
signalArraySize(void)
{
	return NSIG;
}


/*	Broken pipe (POSIX). */

	/* UnixOSProcessPlugin>>#sigPipeNumber */
static sqInt
sigPipeNumber(void)
{
	return SIGPIPE;
}


/*	Quit from keyboard */

	/* UnixOSProcessPlugin>>#sigQuitNumber */
static sqInt
sigQuitNumber(void)
{
	return SIGQUIT;
}


/*	Stop process */

	/* UnixOSProcessPlugin>>#sigStopNumber */
static sqInt
sigStopNumber(void)
{
	return SIGSTOP;
}


/*	Termination signal. This is the default signal sent by the unix kill(1)
	command. 
 */

	/* UnixOSProcessPlugin>>#sigTermNumber */
static sqInt
sigTermNumber(void)
{
	return SIGTERM;
}


/*	User defined signal number 1. This is value is platform-dependent, so the
	inSmalltalk default of 30 may be wrong on some platforms. */

	/* UnixOSProcessPlugin>>#sigUsr1Number */
static sqInt
sigUsr1Number(void)
{
	return SIGUSR1;
}


/*	User defined signal number 2. This is value is platform-dependent, so the
	inSmalltalk default of 32 may be wrong on some platforms. */

	/* UnixOSProcessPlugin>>#sigUsr2Number */
static sqInt
sigUsr2Number(void)
{
	return SIGUSR2;
}


/*	Size in bytes of an integer, for this C compiler on this machine. */

	/* OSProcessPlugin>>#sizeOfInt */
static usqIntptr_t
sizeOfInt(void)
{
	return sizeof(int);
}


/*	Size in bytes of a void pointer, for this C compiler on this machine. */

	/* OSProcessPlugin>>#sizeOfPointer */
static usqIntptr_t
sizeOfPointer(void)
{
	return sizeof(void *);
}


/*	Size of a SESSIONIDENTIFIERTYPE. Should match usage in the SQFile data
	structure, otherwise we should get compiler warnings. */

	/* OSProcessPlugin>>#sizeOfSession */
static sqInt
sizeOfSession(void)
{
	return sizeof(SESSIONIDENTIFIERTYPE);
}


/*	Answer the OS file descriptor, an integer value, from a SQSocket data
	structure, or answer -1 if unable to obtain the file descriptor (probably
	due to receiving
	an incorrect type of object as aFileHandle).
	
	Warning: The first element of privateSocketStruct happens to be the Unix
	file number of the socket. See sqUnixSocket.c for the definition. This
	method takes
	advantage of this, and will break if anyone ever redefines the data
	structure. 
 */

	/* OSProcessPlugin>>#socketDescriptorFrom: */
static int
socketDescriptorFrom(sqInt sqSocketOop)
{
    void *privateSocketStruct;
    SocketPtr sqSocket;

	/* begin socketValueOf: */
	sqSocket = arrayValueOf(sqSocketOop);
	privateSocketStruct = sqSocket->privateSocketPtr;
	if (privateSocketStruct == 0) {
		return -1;
	}
	return * (int *) privateSocketStruct;
}


/*	Answer the size of a SQSocket data structure in bytes. */

	/* OSProcessPlugin>>#socketRecordSize */
static usqIntptr_t
socketRecordSize(void)
{
	return sizeof(SQSocket);
}


/*	Return a pointer to the first byte of of the SQsocket data structure
	socket record within
	anSQSocketRecord, which is expected to be a ByteArray of size
	self>>socketRecordSize. 
 */

	/* OSProcessPlugin>>#socketValueOf: */
static SocketPtr
socketValueOf(sqInt anSQSocketRecord)
{
	return arrayValueOf(anSQSocketRecord);
}


/*	Answer a new ByteString copied from a null-terminated C string.
	Caution: This may invoke the garbage collector. */

	/* OSProcessPlugin>>#stringFromCString: */
static sqInt
stringFromCString(const char *aCString)
{
    sqInt classIdentifier;
    sqInt len;
    sqInt newString;

	/* begin cString:asCollection: */
	classIdentifier = classString();
	len = strlen(aCString);
	newString = instantiateClassindexableSize(classIdentifier, len);
	strncpy(arrayValueOf(newString), aCString, len);
	return newString;
}


/*	Answer a new null-terminated C string copied from aString.
	The string is allocated in object memory, and will be moved
	without warning by the garbage collector. Any C pointer
	reference the the result is valid only until the garbage
	collector next runs. Therefore, this method should only be used
	within a single primitive in a section of code in which the
	garbage collector is guaranteed not to run. Note also that
	this method may itself invoke the garbage collector prior
	to allocating the new C string.
	
	Warning: The result of this method will be invalidated by the
	next garbage collection, including a GC triggered by creation
	of a new object within a primitive. Do not call this method
	twice to obtain two string pointers.
 */

	/* OSProcessPlugin>>#transientCStringFromString: */
static char *
transientCStringFromString(sqInt aString)
{
    char *cString;
    sqInt len;
    sqInt newString;
    char *stringPtr;


	/* Allocate space for a null terminated C string. */
	len = sizeOfSTArrayFromCPrimitive(arrayValueOf(aString));
	pushRemappableOop(aString);
	newString = instantiateClassindexableSize(classString(), len + 1);
	stringPtr = arrayValueOf(popRemappableOop());

	/* Point to the actual C string. */
	cString = arrayValueOf(newString);
	(char *)strncpy(cString, stringPtr, len);
	cString[len] = 0;
	return cString;
}


/*	Answer the integer Unix file number corresponding to a file handle (FILE*
	). 
 */

	/* UnixOSProcessPlugin>>#unixFileNumber: */
static int
unixFileNumber(FILEHANDLETYPE fileHandle)
{
	return fileno(fileHandle);
}


/*	Answer a string identifying the version level for this plugin. */

	/* OSProcessPlugin>>#versionString */
static char *
versionString(void)
{
    static char version[]= "4.6.4 Cog";

	return version;
}


#ifdef SQUEAK_BUILTIN_PLUGIN

static char _m[] = "UnixOSProcessPlugin";
void* UnixOSProcessPlugin_exports[][3] = {
	{(void*)_m, "forkSqueak", (void*)forkSqueak},
	{(void*)_m, "getCurrentWorkingDirectoryAsType", (void*)getCurrentWorkingDirectoryAsType},
	{(void*)_m, "getModuleName", (void*)getModuleName},
	{(void*)_m, "initialiseModule", (void*)initialiseModule},
	{(void*)_m, "moduleUnloaded", (void*)moduleUnloaded},
	{(void*)_m, "primitiveArgumentAt\000\000", (void*)primitiveArgumentAt},
	{(void*)_m, "primitiveArgumentAtAsBytes\000\000", (void*)primitiveArgumentAtAsBytes},
	{(void*)_m, "primitiveCanReceiveSignals\000\000", (void*)primitiveCanReceiveSignals},
	{(void*)_m, "primitiveChdir\000\000", (void*)primitiveChdir},
	{(void*)_m, "primitiveCreatePipe\000\377", (void*)primitiveCreatePipe},
	{(void*)_m, "primitiveCreatePipeWithSessionIdentifier\000\001", (void*)primitiveCreatePipeWithSessionIdentifier},
	{(void*)_m, "primitiveDup", (void*)primitiveDup},
	{(void*)_m, "primitiveDupTo", (void*)primitiveDupTo},
	{(void*)_m, "primitiveEnvironmentAt\000\000", (void*)primitiveEnvironmentAt},
	{(void*)_m, "primitiveEnvironmentAtAsBytes\000\000", (void*)primitiveEnvironmentAtAsBytes},
	{(void*)_m, "primitiveEnvironmentAtSymbol\000\000", (void*)primitiveEnvironmentAtSymbol},
	{(void*)_m, "primitiveEnvironmentAtSymbolAsBytes\000\000", (void*)primitiveEnvironmentAtSymbolAsBytes},
	{(void*)_m, "primitiveErrorMessageAt\000\000", (void*)primitiveErrorMessageAt},
	{(void*)_m, "primitiveFileProtectionMask\000\000", (void*)primitiveFileProtectionMask},
	{(void*)_m, "primitiveFileStat\000\000", (void*)primitiveFileStat},
	{(void*)_m, "primitiveFixPointersInArrayOfStrings\000\001", (void*)primitiveFixPointersInArrayOfStrings},
	{(void*)_m, "primitiveForkAndExecInDirectory\000\001", (void*)primitiveForkAndExecInDirectory},
	{(void*)_m, "primitiveForkExec\000\001", (void*)primitiveForkExec},
	{(void*)_m, "primitiveForkSqueak\000\377", (void*)primitiveForkSqueak},
	{(void*)_m, "primitiveForkSqueakWithoutSigHandler\000\377", (void*)primitiveForkSqueakWithoutSigHandler},
	{(void*)_m, "primitiveForwardSignalToSemaphore\000\002", (void*)primitiveForwardSignalToSemaphore},
	{(void*)_m, "primitiveGetCurrentWorkingDirectory\000\377", (void*)primitiveGetCurrentWorkingDirectory},
	{(void*)_m, "primitiveGetCurrentWorkingDirectoryAsBytes\000\377", (void*)primitiveGetCurrentWorkingDirectoryAsBytes},
	{(void*)_m, "primitiveGetEGid\000\377", (void*)primitiveGetEGid},
	{(void*)_m, "primitiveGetEUid\000\377", (void*)primitiveGetEUid},
	{(void*)_m, "primitiveGetGid\000\377", (void*)primitiveGetGid},
	{(void*)_m, "primitiveGetPGid\000\000", (void*)primitiveGetPGid},
	{(void*)_m, "primitiveGetPGrp\000\377", (void*)primitiveGetPGrp},
	{(void*)_m, "primitiveGetPid\000\377", (void*)primitiveGetPid},
	{(void*)_m, "primitiveGetPPid\000\377", (void*)primitiveGetPPid},
	{(void*)_m, "primitiveGetSession\000\377", (void*)primitiveGetSession},
	{(void*)_m, "primitiveGetStdErrHandle\000\377", (void*)primitiveGetStdErrHandle},
	{(void*)_m, "primitiveGetStdErrHandleWithSessionIdentifier\000\001", (void*)primitiveGetStdErrHandleWithSessionIdentifier},
	{(void*)_m, "primitiveGetStdInHandle\000\377", (void*)primitiveGetStdInHandle},
	{(void*)_m, "primitiveGetStdInHandleWithSessionIdentifier\000\001", (void*)primitiveGetStdInHandleWithSessionIdentifier},
	{(void*)_m, "primitiveGetStdOutHandle\000\377", (void*)primitiveGetStdOutHandle},
	{(void*)_m, "primitiveGetStdOutHandleWithSessionIdentifier\000\001", (void*)primitiveGetStdOutHandleWithSessionIdentifier},
	{(void*)_m, "primitiveGetThreadID\000\377", (void*)primitiveGetThreadID},
	{(void*)_m, "primitiveGetUid\000\377", (void*)primitiveGetUid},
	{(void*)_m, "primitiveIsAtEndOfFile\000\001", (void*)primitiveIsAtEndOfFile},
	{(void*)_m, "primitiveKillOnExit\000\001", (void*)primitiveKillOnExit},
	{(void*)_m, "primitiveLockFileRegion\000\001", (void*)primitiveLockFileRegion},
	{(void*)_m, "primitiveMakePipe\000\377", (void*)primitiveMakePipe},
	{(void*)_m, "primitiveMakePipeWithSessionIdentifier\000\001", (void*)primitiveMakePipeWithSessionIdentifier},
	{(void*)_m, "primitiveModuleName\000\377", (void*)primitiveModuleName},
	{(void*)_m, "primitiveNice\000\000", (void*)primitiveNice},
	{(void*)_m, "primitivePutEnv\000\002", (void*)primitivePutEnv},
	{(void*)_m, "primitiveRealpath\000\000", (void*)primitiveRealpath},
	{(void*)_m, "primitiveRealpathAsBytes\000\000", (void*)primitiveRealpathAsBytes},
	{(void*)_m, "primitiveReapChildProcess\000\000", (void*)primitiveReapChildProcess},
	{(void*)_m, "primitiveSemaIndexFor\000\001", (void*)primitiveSemaIndexFor},
	{(void*)_m, "primitiveSendSigabrtTo\000\000", (void*)primitiveSendSigabrtTo},
	{(void*)_m, "primitiveSendSigalrmTo\000\000", (void*)primitiveSendSigalrmTo},
	{(void*)_m, "primitiveSendSigchldTo\000\000", (void*)primitiveSendSigchldTo},
	{(void*)_m, "primitiveSendSigcontTo\000\000", (void*)primitiveSendSigcontTo},
	{(void*)_m, "primitiveSendSighupTo\000\000", (void*)primitiveSendSighupTo},
	{(void*)_m, "primitiveSendSigintTo\000\000", (void*)primitiveSendSigintTo},
	{(void*)_m, "primitiveSendSigkillTo\000\000", (void*)primitiveSendSigkillTo},
	{(void*)_m, "primitiveSendSigpipeTo\000\000", (void*)primitiveSendSigpipeTo},
	{(void*)_m, "primitiveSendSigquitTo\000\000", (void*)primitiveSendSigquitTo},
	{(void*)_m, "primitiveSendSigstopTo\000\000", (void*)primitiveSendSigstopTo},
	{(void*)_m, "primitiveSendSigtermTo\000\000", (void*)primitiveSendSigtermTo},
	{(void*)_m, "primitiveSendSigusr1To\000\000", (void*)primitiveSendSigusr1To},
	{(void*)_m, "primitiveSendSigusr2To\000\000", (void*)primitiveSendSigusr2To},
	{(void*)_m, "primitiveSetPGid\000\000", (void*)primitiveSetPGid},
	{(void*)_m, "primitiveSetPGrp\000\377", (void*)primitiveSetPGrp},
	{(void*)_m, "primitiveSetSemaIndex\000\000", (void*)primitiveSetSemaIndex},
	{(void*)_m, "primitiveSetSid\000\377", (void*)primitiveSetSid},
	{(void*)_m, "primitiveSigChldNumber\000\377", (void*)primitiveSigChldNumber},
	{(void*)_m, "primitiveSigHupNumber\000\377", (void*)primitiveSigHupNumber},
	{(void*)_m, "primitiveSigIntNumber\000\377", (void*)primitiveSigIntNumber},
	{(void*)_m, "primitiveSigKillNumber\000\377", (void*)primitiveSigKillNumber},
	{(void*)_m, "primitiveSigPipeNumber\000\377", (void*)primitiveSigPipeNumber},
	{(void*)_m, "primitiveSigQuitNumber\000\377", (void*)primitiveSigQuitNumber},
	{(void*)_m, "primitiveSigTermNumber\000\377", (void*)primitiveSigTermNumber},
	{(void*)_m, "primitiveSigUsr1Number\000\377", (void*)primitiveSigUsr1Number},
	{(void*)_m, "primitiveSigUsr2Number\000\377", (void*)primitiveSigUsr2Number},
	{(void*)_m, "primitiveSizeOfInt\000\377", (void*)primitiveSizeOfInt},
	{(void*)_m, "primitiveSizeOfPointer\000\377", (void*)primitiveSizeOfPointer},
	{(void*)_m, "primitiveSQFileFlush\000\001", (void*)primitiveSQFileFlush},
	{(void*)_m, "primitiveSQFileFlushWithSessionIdentifier\000\001", (void*)primitiveSQFileFlushWithSessionIdentifier},
	{(void*)_m, "primitiveSQFileSetBlocking\000\001", (void*)primitiveSQFileSetBlocking},
	{(void*)_m, "primitiveSQFileSetBlockingWithSessionIdentifier\000\001", (void*)primitiveSQFileSetBlockingWithSessionIdentifier},
	{(void*)_m, "primitiveSQFileSetNonBlocking\000\001", (void*)primitiveSQFileSetNonBlocking},
	{(void*)_m, "primitiveSQFileSetNonBlockingWithSessionIdentifier\000\001", (void*)primitiveSQFileSetNonBlockingWithSessionIdentifier},
	{(void*)_m, "primitiveSQFileSetUnbuffered\000\001", (void*)primitiveSQFileSetUnbuffered},
	{(void*)_m, "primitiveSQFileSetUnbufferedWithSessionIdentifier\000\001", (void*)primitiveSQFileSetUnbufferedWithSessionIdentifier},
	{(void*)_m, "primitiveTestEndOfFileFlag\000\001", (void*)primitiveTestEndOfFileFlag},
	{(void*)_m, "primitiveTestLockableFileRegion\000\001", (void*)primitiveTestLockableFileRegion},
	{(void*)_m, "primitiveUnixFileClose", (void*)primitiveUnixFileClose},
	{(void*)_m, "primitiveUnixFileNumber\000\001", (void*)primitiveUnixFileNumber},
	{(void*)_m, "primitiveUnlockFileRegion\000\001", (void*)primitiveUnlockFileRegion},
	{(void*)_m, "primitiveUnsetEnv\000\000", (void*)primitiveUnsetEnv},
	{(void*)_m, "primitiveVersionString\000\377", (void*)primitiveVersionString},
	{(void*)_m, "setInterpreter", (void*)setInterpreter},
	{(void*)_m, "shutdownModule\000\377", (void*)shutdownModule},
	{NULL, NULL, NULL}
};

#else /* ifdef SQ_BUILTIN_PLUGIN */

signed char primitiveArgumentAtAccessorDepth = 0;
signed char primitiveArgumentAtAsBytesAccessorDepth = 0;
signed char primitiveCanReceiveSignalsAccessorDepth = 0;
signed char primitiveChdirAccessorDepth = 0;
signed char primitiveCreatePipeWithSessionIdentifierAccessorDepth = 1;
signed char primitiveEnvironmentAtAccessorDepth = 0;
signed char primitiveEnvironmentAtAsBytesAccessorDepth = 0;
signed char primitiveEnvironmentAtSymbolAccessorDepth = 0;
signed char primitiveEnvironmentAtSymbolAsBytesAccessorDepth = 0;
signed char primitiveErrorMessageAtAccessorDepth = 0;
signed char primitiveFileProtectionMaskAccessorDepth = 0;
signed char primitiveFileStatAccessorDepth = 0;
signed char primitiveFixPointersInArrayOfStringsAccessorDepth = 1;
signed char primitiveForkAndExecInDirectoryAccessorDepth = 1;
signed char primitiveForkExecAccessorDepth = 1;
signed char primitiveForwardSignalToSemaphoreAccessorDepth = 2;
signed char primitiveGetPGidAccessorDepth = 0;
signed char primitiveGetStdErrHandleWithSessionIdentifierAccessorDepth = 1;
signed char primitiveGetStdInHandleWithSessionIdentifierAccessorDepth = 1;
signed char primitiveGetStdOutHandleWithSessionIdentifierAccessorDepth = 1;
signed char primitiveIsAtEndOfFileAccessorDepth = 1;
signed char primitiveKillOnExitAccessorDepth = 1;
signed char primitiveLockFileRegionAccessorDepth = 1;
signed char primitiveMakePipeWithSessionIdentifierAccessorDepth = 1;
signed char primitiveNiceAccessorDepth = 0;
signed char primitivePutEnvAccessorDepth = 2;
signed char primitiveRealpathAccessorDepth = 0;
signed char primitiveRealpathAsBytesAccessorDepth = 0;
signed char primitiveReapChildProcessAccessorDepth = 0;
signed char primitiveSemaIndexForAccessorDepth = 1;
signed char primitiveSendSigabrtToAccessorDepth = 0;
signed char primitiveSendSigalrmToAccessorDepth = 0;
signed char primitiveSendSigchldToAccessorDepth = 0;
signed char primitiveSendSigcontToAccessorDepth = 0;
signed char primitiveSendSighupToAccessorDepth = 0;
signed char primitiveSendSigintToAccessorDepth = 0;
signed char primitiveSendSigkillToAccessorDepth = 0;
signed char primitiveSendSigpipeToAccessorDepth = 0;
signed char primitiveSendSigquitToAccessorDepth = 0;
signed char primitiveSendSigstopToAccessorDepth = 0;
signed char primitiveSendSigtermToAccessorDepth = 0;
signed char primitiveSendSigusr1ToAccessorDepth = 0;
signed char primitiveSendSigusr2ToAccessorDepth = 0;
signed char primitiveSetPGidAccessorDepth = 0;
signed char primitiveSetSemaIndexAccessorDepth = 0;
signed char primitiveSQFileFlushAccessorDepth = 1;
signed char primitiveSQFileFlushWithSessionIdentifierAccessorDepth = 1;
signed char primitiveSQFileSetBlockingAccessorDepth = 1;
signed char primitiveSQFileSetBlockingWithSessionIdentifierAccessorDepth = 1;
signed char primitiveSQFileSetNonBlockingAccessorDepth = 1;
signed char primitiveSQFileSetNonBlockingWithSessionIdentifierAccessorDepth = 1;
signed char primitiveSQFileSetUnbufferedAccessorDepth = 1;
signed char primitiveSQFileSetUnbufferedWithSessionIdentifierAccessorDepth = 1;
signed char primitiveTestEndOfFileFlagAccessorDepth = 1;
signed char primitiveTestLockableFileRegionAccessorDepth = 1;
signed char primitiveUnixFileNumberAccessorDepth = 1;
signed char primitiveUnlockFileRegionAccessorDepth = 1;
signed char primitiveUnsetEnvAccessorDepth = 0;

#endif /* ifdef SQ_BUILTIN_PLUGIN */
