/* Automatically generated by
	VMPluginCodeGenerator * VMMaker.oscog-eem.2510 uuid: 4108a547-dc5c-47ed-9521-d25f5918b426
   from
	Win32OSProcessPlugin VMConstruction-Plugins-OSProcessPlugin.oscog-dtl.66 uuid: a8a2bf10-e7b3-4963-90b3-d69a2bee77bb
 */
static char __buildInfo[] = "Win32OSProcessPlugin VMConstruction-Plugins-OSProcessPlugin.oscog-dtl.66 uuid: a8a2bf10-e7b3-4963-90b3-d69a2bee77bb " __DATE__ ;
/* D T Lewis - Win32OSProcessPlugin.c translated from class
   Win32OSProcessPlugin 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 <windows.h>
#include <process.h>

/* 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 "sqWin32.h"
#include "sqMemoryAccess.h"


/* Win32OSProcessPlugin class>>preambleCCode */
/* N.B. Use STACK_SIZE_PARAM_IS_A_RESERVATION because Windows apparently
   ignores the stack size parameter and uses the default in the executable. So the
   only way to reduce memory usage is via STACK_SIZE_PARAM_IS_A_RESERVATION.
 */
#if !defined(STACK_SIZE_PARAM_IS_A_RESERVATION)
#	define STACK_SIZE_PARAM_IS_A_RESERVATION 0x10000
#endif
/* end Win32OSProcessPlugin class>>preambleCCode */


/*** Constants ***/
#define FILEHANDLETYPE FILE *  /* the type of low level stream to be used in a struct SQFile */
#define PrimErrBadArgument 3
#define READCHARARRAYSIZE 512	/* Corresponds to expected max size of external semaphore table */
									/* Table expands dynamically, so just use a big number */
#define SESSIONIDENTIFIERTYPE int


/*** Function Prototypes ***/
static void * callocWrappersize(sqInt count, sqInt objectSize);
static sqInt copyBytesFromtolength(void *charArray1, void *charArray2, sqInt len);
static sqInt createPipeForReaderwriter(FILEHANDLETYPE *readerIOStreamPtr, FILEHANDLETYPE *writerIOStreamPtr);
static HANDLE createThreadMutex(void);
static char * cStringFromString(sqInt aString);
static sqInt cStringasCollection(const char *aCString, sqInt classIdentifier);
static HANDLE exitThreadMutexHandle(void);
static FILEHANDLETYPE fileHandleFrom(sqInt sqFileStructByteArray);
static usqIntptr_t fileRecordSize(void);
static SQFile * fileValueOf(sqInt anSQFileRecord);
static char ** fixPointersInArrayOfStringswithOffsets(sqInt flattenedStrings, sqInt offsets);
static sqInt getCurrentWorkingDirectoryAsType(sqInt classIdentifier);
static sqInt getEnvironmentStringsAsType(sqInt classIdentifier);
EXPORT(const char*) getModuleName(void);
static sqInt getThisSessionIdentifier(void);
static DWORD WINAPI handleAnyChildProc(void *args);
static HANDLE handleFromSQFile(sqInt anSQFileRecord);
static HANDLE handleFrom(sqInt aHandleObject);
static sqInt handleObjectFrom(HANDLE aHandle);
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 makePipeForReaderwriter(FILEHANDLETYPE *readerIOStreamPtr, FILEHANDLETYPE *writerIOStreamPtr);
EXPORT(sqInt) moduleUnloaded(char *aModuleName);
static sqInt newSQFileByteArray(void);
static sqInt newSQSocketByteArray(void);
static void * pointerFrom(sqInt aByteArray);
EXPORT(sqInt) primitiveAllocConsole(void);
EXPORT(sqInt) primitiveBufferValuesAt(void);
EXPORT(sqInt) primitiveCanAccessChildProcess(void);
EXPORT(sqInt) primitiveCloseHandle(void);
EXPORT(sqInt) primitiveCommand(void);
EXPORT(sqInt) primitiveCommandWithInputOutputError(void);
EXPORT(sqInt) primitiveCreatePipe(void);
EXPORT(sqInt) primitiveCreatePipeWithSessionIdentifier(void);
EXPORT(sqInt) primitiveFixPointersInArrayOfStrings(void);
EXPORT(sqInt) primitiveFreeConsole(void);
EXPORT(sqInt) primitiveGetCurrentWorkingDirectory(void);
EXPORT(sqInt) primitiveGetCurrentWorkingDirectoryAsBytes(void);
EXPORT(sqInt) primitiveGetEnvironmentStrings(void);
EXPORT(sqInt) primitiveGetEnvironmentStringsAsBytes(void);
EXPORT(sqInt) primitiveGetExitStatusForHandle(void);
EXPORT(sqInt) primitiveGetMainThreadHandle(void);
EXPORT(sqInt) primitiveGetMainThreadID(void);
EXPORT(sqInt) primitiveGetPid(void);
EXPORT(sqInt) primitiveGetPidHandle(void);
EXPORT(sqInt) primitiveGetSession(void);
EXPORT(sqInt) primitiveGetStdError(void);
EXPORT(sqInt) primitiveGetStdInput(void);
EXPORT(sqInt) primitiveGetStdOutput(void);
EXPORT(sqInt) primitiveLastReadFor(void);
EXPORT(sqInt) primitiveLastReadForStoreIn(void);
EXPORT(sqInt) primitiveMakePipe(void);
EXPORT(sqInt) primitiveMakePipeWithSessionIdentifier(void);
EXPORT(sqInt) primitiveModuleName(void);
EXPORT(sqInt) primitiveNextFromSignaling(void);
EXPORT(sqInt) primitiveOneShot(void);
EXPORT(sqInt) primitiveSetStdErr(void);
EXPORT(sqInt) primitiveSetStdIn(void);
EXPORT(sqInt) primitiveSetStdOut(void);
EXPORT(sqInt) primitiveSetWaitForAnyProcessExitThenSignalSemaphoreWithIndex(void);
EXPORT(sqInt) primitiveSizeOfInt(void);
EXPORT(sqInt) primitiveSizeOfPointer(void);
EXPORT(sqInt) primitiveTerminateThread(void);
EXPORT(sqInt) primitiveVersionString(void);
static DWORD WINAPI readCharThreadIndexhandle(sqInt index, HANDLE handle);
static DWORD WINAPI readCharThread(void *args);
static HANDLE readThreadMutexHandle(void);
static BOOL releaseThreadMutex(HANDLE aHandle);
static sqInt sandboxSecurity(void);
static sqInt securityHeurisitic(void);
static SESSIONIDENTIFIERTYPE sessionIdentifierFrom(sqInt aByteArray);
EXPORT(sqInt) setInterpreter(struct VirtualMachine *anInterpreter);
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 startHandlerThreadForAnycountsignalingSemaphoreAt(HANDLE *handleArrayPointer, sqInt arraySize, sqInt semaphoreIndex);
static DWORD stdErrHandle(void);
static HANDLE stderrHandle(void);
static DWORD stdInHandle(void);
static HANDLE stdinHandle(void);
static DWORD stdOutHandle(void);
static HANDLE stdoutHandle(void);
static sqInt stringFromCString(const char *aCString);
static HANDLE threadMutexHandle(void);
static char * transientCStringFromString(sqInt aString);
static char * versionString(void);
static DWORD waitForThreadMutex(HANDLE aHandle);


/*** Variables ***/

#if !defined(SQUEAK_BUILTIN_PLUGIN)
static void * (*arrayValueOf)(sqInt oop);
static sqInt (*byteSizeOf)(sqInt oop);
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 (*nilObject)(void);
static sqInt (*pop)(sqInt nItems);
static sqInt (*popthenPush)(sqInt nItems, sqInt oop);
static sqInt (*popRemappableOop)(void);
static sqInt (*positive32BitIntegerFor)(unsigned int integerValue);
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 (*storePointerofObjectwithValue)(sqInt index, sqInt oop, sqInt valuePointer);
static sqInt (*trueObject)(void);
#else /* !defined(SQUEAK_BUILTIN_PLUGIN) */
extern void * arrayValueOf(sqInt oop);
extern sqInt byteSizeOf(sqInt oop);
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);
extern sqInt nilObject(void);
extern sqInt pop(sqInt nItems);
extern sqInt popthenPush(sqInt nItems, sqInt oop);
extern sqInt popRemappableOop(void);
extern sqInt positive32BitIntegerFor(unsigned int integerValue);
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 storePointerofObjectwithValue(sqInt index, sqInt oop, sqInt valuePointer);
extern sqInt trueObject(void);
extern
#endif
struct VirtualMachine* interpreterProxy;
static const char *moduleName =
#ifdef SQUEAK_BUILTIN_PLUGIN
	"Win32OSProcessPlugin VMConstruction-Plugins-OSProcessPlugin.oscog-dtl.66 (i)"
#else
	"Win32OSProcessPlugin VMConstruction-Plugins-OSProcessPlugin.oscog-dtl.66 (e)"
#endif
;
static int osprocessSandboxSecurity;
static int readCharArraySize = READCHARARRAYSIZE;
static unsigned char readCharBufferArray[READCHARARRAYSIZE];
static DWORD readCharCountArray[READCHARARRAYSIZE];
static int readCharStatusArray[READCHARARRAYSIZE];
static DWORD readCharThreadIdArray[READCHARARRAYSIZE];


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



/*	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. Answer true for success, else false */

	/* Win32OSProcessPlugin>>#createPipeForReader:writer: */
static sqInt
createPipeForReaderwriter(FILEHANDLETYPE *readerIOStreamPtr, FILEHANDLETYPE *writerIOStreamPtr)
{
	return CreatePipe(readerIOStreamPtr, writerIOStreamPtr, NULL, 0);
}


/*	Answer a HANDLE for a new mutex semaphore. */

	/* Win32OSProcessPlugin>>#createThreadMutex */
static HANDLE
createThreadMutex(void)
{
	return CreateMutex(NULL, false, NULL);
}


/*	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;
}


/*	Answer a HANDLE for a mutex semaphore for the child handler thread. */

	/* Win32OSProcessPlugin>>#exitThreadMutexHandle */
static HANDLE
exitThreadMutexHandle(void)
{
    static HANDLE h= 0;

	if (h == null) {
		h = createThreadMutex();
	}
	return h;
}


/*	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);
}


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

	/* Win32OSProcessPlugin>>#getCurrentWorkingDirectoryAsType: */
static sqInt
getCurrentWorkingDirectoryAsType(sqInt classIdentifier)
{
    char *buffer;
    usqIntptr_t charSize;
    DWORD cwdLen;
    sqInt cwdString;
    DWORD requiredLen;


	/* Determine required size of result buffer (this form counts the terminating null) */
	charSize = sizeof(TCHAR);
	requiredLen = GetCurrentDirectory(0, 0);
	if (requiredLen == 0) {
		return primitiveFail();
	}

	/* Call GetCurrentDirectory() again to get the actual result (which does not count the terminating null (!!)) */
	buffer = alloca(requiredLen * charSize);
	cwdLen = GetCurrentDirectory(requiredLen, buffer);
	if ((cwdLen == 0)
	 || (cwdLen > requiredLen)) {
		return primitiveFail();
	}
	cwdString = instantiateClassindexableSize(classIdentifier, cwdLen * charSize);
	strncpy(arrayValueOf(cwdString), buffer, cwdLen * charSize);
	popthenPush(1, cwdString);
	return 0;
}


/*	Answer the environment block in the form of an Array of Strings. The
	caller is expected to parse the strings into a dictionary of keys and
	values. 
 */

	/* Win32OSProcessPlugin>>#getEnvironmentStringsAsType: */
static sqInt
getEnvironmentStringsAsType(sqInt classIdentifier)
{
    sqInt count;
    sqInt envArray;
    sqInt len;
    sqInt newString;
    TCHAR *p;
    TCHAR *pVarBlock;
    sqInt string;


	/* Count the environment strings so the result array can be pre-allocated.
	   This minimizes the number of objects which must be pushed onto the
	   stack of remappable objects. */
	pVarBlock = GetEnvironmentStrings();
	p = pVarBlock;
	count = 0;
	while (*p != 0) {
		count += 1;
		while (*p != 0) {
			p += 1;
		}
		p += 1;
	}

	/* Populate the array. */
	envArray = instantiateClassindexableSize(classArray(), count);
	p = pVarBlock;
	count = 0;
	while (*p != 0) {
		count += 1;
		pushRemappableOop(envArray);
		/* begin cString:asCollection: */
		len = strlen(((char *) p));
		newString = instantiateClassindexableSize(classIdentifier, len);
		strncpy(arrayValueOf(newString), ((char *) p), len);
		string = newString;
		envArray = popRemappableOop();
		stObjectatput(envArray, count, string);
		while (*p != 0) {
			p += 1;
		}
		p += 1;
	}
	FreeEnvironmentStrings(pVarBlock);
	popthenPush(1, envArray);
	return 0;
}


/*	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;
}

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


/*	Wait for a process HANDLE to exit, then signal the Semaphore at index.
	Answer the exit status of the process. This is the code to be executed in
	a thread to provide
	asychronous notification of an external process exit.
 */

	/* Win32OSProcessPlugin>>#handleAnyChildProc: */
static DWORD WINAPI
handleAnyChildProc(void *args)
{
    HANDLE aHandle;
    volatile int count;
    DWORD exitStatus;
    static HANDLE h= 0;
    volatile LPHANDLE handles;
    volatile int index;
    struct {int count; HANDLE *handles; int semaIndex;} *threadArgs;

	threadArgs = args;
	count= threadArgs->count;
	handles= threadArgs->handles;
	index= threadArgs->semaIndex;
	/* begin releaseThreadMutex: */
	if (h == null) {
		h = createThreadMutex();
	}
	aHandle = h;
	ReleaseMutex(aHandle);
	exitStatus = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
	signalSemaphoreWithIndex(index);
	return exitStatus;
}

	/* Win32OSProcessPlugin>>#handleFromSQFile: */
static HANDLE
handleFromSQFile(sqInt anSQFileRecord)
{
    SQFile *sqFile;

	if (!((isBytes(anSQFileRecord))
		 && ((stSizeOf(anSQFileRecord)) == (sizeof(SQFile))))) {
		primitiveFail();
		return null;
	}
	/* begin fileValueOf: */
	sqFile = arrayValueOf(anSQFileRecord);
	return sqFile->file;
}


/*	Answer the HANDLE represented by aHandleObject. */

	/* Win32OSProcessPlugin>>#handleFrom: */
static HANDLE
handleFrom(sqInt aHandleObject)
{
    union {void *address; unsigned char bytes[sizeof(void *)];} handleUnion;
    sqInt idx;
    unsigned char *ptr;

	if (!((isBytes(aHandleObject))
		 && ((stSizeOf(aHandleObject)) == (sizeof(HANDLE))))) {
		primitiveFail();
		return null;
	}
	ptr = arrayValueOf(aHandleObject);
	idx = 0;
	while (idx < (sizeof(HANDLE))) {
		handleUnion.bytes[idx] = ptr[idx];
		idx += 1;
	}
	return handleUnion.address;
}


/*	Answer an object which contains the value of a HANDLE. */

	/* Win32OSProcessPlugin>>#handleObjectFrom: */
static sqInt
handleObjectFrom(HANDLE aHandle)
{
    sqInt handleOop;
    union {HANDLE handle; char bytes[sizeof(HANDLE)];} handleUnion;
    sqInt idx;
    unsigned char *ptr;

	handleUnion.handle = aHandle;
	handleOop = instantiateClassindexableSize(classByteArray(), sizeof(HANDLE));
	ptr = arrayValueOf(handleOop);
	idx = 0;
	while (idx < (sizeof(HANDLE))) {
		ptr[idx]= handleUnion.bytes[idx];
		idx += 1;
	}
	return handleOop;
}

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


/*	Platform specific initialization */

	/* Win32OSProcessPlugin>>#initializeModuleForPlatform */
static sqInt
initializeModuleForPlatform(void)
{
	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)));
}


/*	Create a pipe and populate the readerIOStream and writerIOStream
	variables. Answer true for success, else false */

	/* Win32OSProcessPlugin>>#makePipeForReader:writer: */
static sqInt
makePipeForReaderwriter(FILEHANDLETYPE *readerIOStreamPtr, FILEHANDLETYPE *writerIOStreamPtr)
{
	return CreatePipe(readerIOStreamPtr, writerIOStreamPtr, NULL, 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;
}


/*	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));
}


/*	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;
}


/*	Allocate a console if not already allocated. Answer true on success. */

	/* Win32OSProcessPlugin>>#primitiveAllocConsole */
EXPORT(sqInt)
primitiveAllocConsole(void)
{
	if (AllocConsole()) {
		popthenPush(1, trueObject());
	}
	else {
		popthenPush(1, falseObject());
	}
	return 0;
}


/*	For debugging only. Answer the current values of readCharBufferArray,
	readCharCountArray, and readCharStatusArray at index, an integer
	corresponding to a semaphore for one read handler thread. Answer an Array
	with the buffered
	character, the character count, and the status value.
 */

	/* Win32OSProcessPlugin>>#primitiveBufferValuesAt */
EXPORT(sqInt)
primitiveBufferValuesAt(void)
{
    unsigned char byte;
    DWORD count;
    sqInt index;
    sqInt result;
    int status;

	index = stackIntegerValue(0);
	byte = readCharBufferArray[index];
	count = readCharCountArray[index];
	status = readCharStatusArray[index];
	result = instantiateClassindexableSize(classArray(), 3);
	storePointerofObjectwithValue(0, result, positive32BitIntegerFor(byte));
	storePointerofObjectwithValue(1, result, positive32BitIntegerFor(count));
	storePointerofObjectwithValue(2, result, positive32BitIntegerFor(status));
	popthenPush(2, result);
	return 0;
}


/*	Answer true if the OS process represented by a HANDLE can be accessed by
	this OS process.
 */

	/* Win32OSProcessPlugin>>#primitiveCanAccessChildProcess */
EXPORT(sqInt)
primitiveCanAccessChildProcess(void)
{
    DWORD exitStatus;
    HANDLE handle;

	if (((handle = handleFrom(stackObjectValue(0)))) == null) {
		return null;
	}
	if (((GetExitCodeProcess(handle, &exitStatus)) != 0)
	 && (exitStatus == STILL_ACTIVE)) {
		popthenPush(2, trueObject());
	}
	else {
		popthenPush(2, falseObject());
	}
	return 0;
}


/*	Close the specified handle, which may refer to a process, a thread, or
	some other Win32 object. */

	/* Win32OSProcessPlugin>>#primitiveCloseHandle */
EXPORT(sqInt)
primitiveCloseHandle(void)
{
    HANDLE handle;
    sqInt handleOop;
    BOOL result;

	handleOop = stackObjectValue(0);
	if (((handle = handleFrom(handleOop))) == null) {
		return null;
	}
	result = CloseHandle(handle);
	pop(2);
	if (result != 0) {
		push(trueObject());
	}
	else {
		push(falseObject());
	}
	return 0;
}


/*	Create a process to run commandString. Answer an Array formed from the
	PROCESS_INFORMATION structure for the new process. The caller is expected
	to close the process and thread handles when they are no longer needed.
	The result array contains hProcess, hThread, dwProcessId, dwThreadId.
	
	This primitive has been replaced by #primitiveCommandWithInputOutputError,
	and will be removed in future versions of OSProcess.
 */

	/* Win32OSProcessPlugin>>#primitiveCommand */
EXPORT(sqInt)
primitiveCommand(void)
{
    sqInt commandString;
    void *cStringPtr;
    sqInt processInformation;
    PROCESS_INFORMATION procInfo;
    STARTUPINFO startUp;

	commandString = stackObjectValue(0);
	cStringPtr = cStringFromString(commandString);
	GetStartupInfo (&startUp);
	startUp.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
	startUp.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	startUp.hStdError = GetStdHandle(STD_ERROR_HANDLE);
	if (!(CreateProcess(NULL, cStringPtr, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startUp, &procInfo))) {
		return primitiveFail();
	}
	pushRemappableOop(positive32BitIntegerFor(procInfo.dwThreadId));
	pushRemappableOop(positive32BitIntegerFor(procInfo.dwProcessId));
	pushRemappableOop(handleObjectFrom(procInfo.hThread));
	pushRemappableOop(handleObjectFrom(procInfo.hProcess));
	processInformation = instantiateClassindexableSize(classArray(), 4);
	storePointerofObjectwithValue(0, processInformation, popRemappableOop());
	storePointerofObjectwithValue(1, processInformation, popRemappableOop());
	storePointerofObjectwithValue(2, processInformation, popRemappableOop());
	storePointerofObjectwithValue(3, processInformation, popRemappableOop());
	pop(2);
	push(processInformation);
	return 0;
}


/*	Create a process to run commandString, using the specified input, output,
	and error streams. If any of the input, output, or error streams is nil,
	then the current values of these handles in this OS process will be passed
	to the
	new process. Answer an Array formed from the PROCESS_INFORMATION
	structure for the new process. The caller is expected to close the process
	and thread handles when they are no longer needed. The result array
	contains hProcess, hThread, dwProcessId, dwThreadId.
 */

	/* Win32OSProcessPlugin>>#primitiveCommandWithInputOutputError */
EXPORT(sqInt)
primitiveCommandWithInputOutputError(void)
{
    sqInt commandString;
    void *cStringPtr;
    HANDLE errorHandle;
    HANDLE inputHandle;
    HANDLE outputHandle;
    sqInt processInformation;
    PROCESS_INFORMATION procInfo;
    STARTUPINFO startUp;
    sqInt stdErr;
    sqInt stdIn;
    sqInt stdOut;

	stdErr = stackObjectValue(0);
	stdOut = stackObjectValue(1);
	stdIn = stackObjectValue(2);
	commandString = stackObjectValue(3);
	cStringPtr = cStringFromString(commandString);
	if (stdIn == (nilObject())) {
		inputHandle = GetStdHandle(stdInHandle());
	}
	else {
		inputHandle = handleFromSQFile(stdIn);
	}
	if (stdOut == (nilObject())) {
		outputHandle = GetStdHandle(stdOutHandle());
	}
	else {
		outputHandle = handleFromSQFile(stdOut);
	}
	if (stdErr == (nilObject())) {
		errorHandle = GetStdHandle(stdErrHandle());
	}
	else {
		errorHandle = handleFromSQFile(stdErr);
	}
	GetStartupInfo (&startUp);
	startUp.hStdInput = inputHandle;
	startUp.hStdOutput = outputHandle;
	startUp.hStdError = errorHandle;
	if (!(CreateProcess(NULL, cStringPtr, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startUp, &procInfo))) {
		return primitiveFail();
	}
	pushRemappableOop(positive32BitIntegerFor(procInfo.dwThreadId));
	pushRemappableOop(positive32BitIntegerFor(procInfo.dwProcessId));
	pushRemappableOop(handleObjectFrom(procInfo.hThread));
	pushRemappableOop(handleObjectFrom(procInfo.hProcess));
	processInformation = instantiateClassindexableSize(classArray(), 4);
	storePointerofObjectwithValue(0, processInformation, popRemappableOop());
	storePointerofObjectwithValue(1, processInformation, popRemappableOop());
	storePointerofObjectwithValue(2, processInformation, popRemappableOop());
	storePointerofObjectwithValue(3, processInformation, popRemappableOop());
	pop(5);
	push(processInformation);
	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;
}


/*	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;
}


/*	Deallocate the console if allocated. Answer true on success. */

	/* Win32OSProcessPlugin>>#primitiveFreeConsole */
EXPORT(sqInt)
primitiveFreeConsole(void)
{
	if (FreeConsole()) {
		popthenPush(1, trueObject());
	}
	else {
		popthenPush(1, falseObject());
	}
	return 0;
}


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

	/* Win32OSProcessPlugin>>#primitiveGetCurrentWorkingDirectory */
EXPORT(sqInt)
primitiveGetCurrentWorkingDirectory(void)
{
    WCHAR *buffer;
    usqIntptr_t charSize;
    DWORD cwdLen;
    sqInt cwdString;
    DWORD requiredLen;
    DWORD utf8Len;


	/* Determine required size of result buffer (this form counts the terminating null) */
	charSize = sizeof(WCHAR);
	requiredLen = GetCurrentDirectoryW(0, 0);
	if (requiredLen == 0) {
		return primitiveFail();
	}

	/* Call GetCurrentDirectory() again to get the actual result (which does not count the terminating null (!!)) */
	buffer = alloca(requiredLen * charSize);
	cwdLen = GetCurrentDirectoryW(requiredLen, buffer);
	if ((cwdLen == 0)
	 || (cwdLen > requiredLen)) {
		return primitiveFail();
	}

	/* Allocate the real buffer */
	utf8Len = WideCharToMultiByte(OSPP_CP_UTF8(), 0, buffer, cwdLen, null, 0, null, null);
	cwdString = instantiateClassindexableSize(classString(), utf8Len);
	if (utf8Len > 0) {

		/* copy directly to the buffer, because this form does not include terminating NULL */
		WideCharToMultiByte(OSPP_CP_UTF8(), 0, buffer, cwdLen, arrayValueOf(cwdString), utf8Len, null, null);
	}
	popthenPush(1, cwdString);
	return 0;
}


/*	Answer a byte array containing the current working directory. */

	/* Win32OSProcessPlugin>>#primitiveGetCurrentWorkingDirectoryAsBytes */
EXPORT(sqInt)
primitiveGetCurrentWorkingDirectoryAsBytes(void)
{
    char *buffer;
    usqIntptr_t charSize;
    sqInt classIdentifier;
    DWORD cwdLen;
    sqInt cwdString;
    DWORD requiredLen;

	/* begin getCurrentWorkingDirectoryAsType: */
	classIdentifier = classByteArray();

	/* Determine required size of result buffer (this form counts the terminating null) */
	charSize = sizeof(TCHAR);
	requiredLen = GetCurrentDirectory(0, 0);
	if (requiredLen == 0) {
		primitiveFail();
		goto l1;
	}

	/* Call GetCurrentDirectory() again to get the actual result (which does not count the terminating null (!!)) */
	buffer = alloca(requiredLen * charSize);
	cwdLen = GetCurrentDirectory(requiredLen, buffer);
	if ((cwdLen == 0)
	 || (cwdLen > requiredLen)) {
		primitiveFail();
		goto l1;
	}
	cwdString = instantiateClassindexableSize(classIdentifier, cwdLen * charSize);
	strncpy(arrayValueOf(cwdString), buffer, cwdLen * charSize);
	popthenPush(1, cwdString);
	l1:	/* end getCurrentWorkingDirectoryAsType: */;
	return 0;
}


/*	Answer the environment block in the form of an Array of Strings. The
	caller is expected to parse the strings into a dictionary of keys and
	values. 
 */

	/* Win32OSProcessPlugin>>#primitiveGetEnvironmentStrings */
EXPORT(sqInt)
primitiveGetEnvironmentStrings(void)
{
    sqInt count;
    sqInt envArray;
    WCHAR *p;
    WCHAR *pVarBlock;
    sqInt string;
    DWORD utf8Len;


	/* Count the environment strings so the result array can be pre-allocated.
	   This minimizes the number of objects which must be pushed onto the
	   stack of remappable objects. */
	pVarBlock = GetEnvironmentStringsW();
	p = pVarBlock;
	count = 0;
	while (*p != 0) {
		count += 1;
		while (*p != 0) {
			p += 1;
		}
		p += 1;
	}

	/* Populate the array. */
	envArray = instantiateClassindexableSize(classArray(), count);
	p = pVarBlock;
	count = 0;
	while (*p != 0) {

		/* Get required length of UTF8 encoded string, without terminating NULL */
		count += 1;
		utf8Len = WideCharToMultiByte(OSPP_CP_UTF8(), 0, p, wcslen(p), null, 0, null, null);
		pushRemappableOop(envArray);

		/* May trigger garbage collection */
		string = instantiateClassindexableSize(classString(), utf8Len);

		/* Write the UTF8 conversion directly in the Smalltalk String, without terminating NULL */
		envArray = popRemappableOop();
		WideCharToMultiByte(OSPP_CP_UTF8(), 0, p, wcslen(p), arrayValueOf(string), utf8Len, null, null);
		stObjectatput(envArray, count, string);
		while (*p != 0) {
			p += 1;
		}
		p += 1;
	}
	FreeEnvironmentStringsW(pVarBlock);
	popthenPush(1, envArray);
	return 0;
}


/*	Answer the environment block in the form of an Array of ByteArray. The
	caller is expected to parse the result into a dictionary of keys and
	values. 
 */

	/* Win32OSProcessPlugin>>#primitiveGetEnvironmentStringsAsBytes */
EXPORT(sqInt)
primitiveGetEnvironmentStringsAsBytes(void)
{
	getEnvironmentStringsAsType(classByteArray());
	return 0;
}


/*	Answer the exit status for the process represented by a HANDLE. Fail if
	the process is still active, or if the GetExitCodeProcess call fails. */

	/* Win32OSProcessPlugin>>#primitiveGetExitStatusForHandle */
EXPORT(sqInt)
primitiveGetExitStatusForHandle(void)
{
    DWORD exitStatus;
    HANDLE handle;

	if (((handle = handleFrom(stackObjectValue(0)))) == null) {
		return null;
	}
	if ((GetExitCodeProcess(handle, &exitStatus)) != 0) {
		if (exitStatus == STILL_ACTIVE) {

			/* Child process is still active, fail the primitive */
			return primitiveFail();
		}
		popthenPush(2, positive32BitIntegerFor(exitStatus));
	}
	else {
		return primitiveFail();
	}
	return 0;
}


/*	Answer the ID of my main thread. */

	/* Win32OSProcessPlugin>>#primitiveGetMainThreadHandle */
EXPORT(sqInt)
primitiveGetMainThreadHandle(void)
{
    HANDLE handle;
    sqInt handleOop;

	handle = GetCurrentThread();
	handleOop = handleObjectFrom(handle);
	popthenPush(1, handleOop);
	return 0;
}


/*	Answer the ID of my main thread. */

	/* Win32OSProcessPlugin>>#primitiveGetMainThreadID */
EXPORT(sqInt)
primitiveGetMainThreadID(void)
{
    DWORD tid;
    sqInt tidOop;

	tid = GetCurrentThreadId();
	tidOop = positive32BitIntegerFor(tid);
	popthenPush(1, tidOop);
	return 0;
}


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

	/* Win32OSProcessPlugin>>#primitiveGetPid */
EXPORT(sqInt)
primitiveGetPid(void)
{
    DWORD pid;
    sqInt pidOop;

	pid = GetCurrentProcessId();
	pidOop = positive32BitIntegerFor(pid);
	popthenPush(1, pidOop);
	return 0;
}


/*	Answer the handle for my OS process */

	/* Win32OSProcessPlugin>>#primitiveGetPidHandle */
EXPORT(sqInt)
primitiveGetPidHandle(void)
{
    HANDLE handle;
    sqInt handleOop;

	handle = GetCurrentProcess();
	handleOop = handleObjectFrom(handle);
	popthenPush(1, handleOop);
	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 a two element array containing the sqFile data structure
	representing standard error stream for my OS process, and a flag (true or
	false) to indicate
	whether the sqFile data structure contains a valid HANDLE. If no standard
	error stream is available for this OS process, the sqFile data structure
	will contain an
	invalid HANDLE value, which will result in failures on subsequent
	accesses. 
 */

	/* Win32OSProcessPlugin>>#primitiveGetStdError */
EXPORT(sqInt)
primitiveGetStdError(void)
{
    SQFile *file;
    sqInt fileOop;
    HANDLE handle;
    HANDLE invalid;
    sqInt result;
    sqInt thisSession;

	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	invalid = INVALID_HANDLE_VALUE;
	result = instantiateClassindexableSize(classArray(), 2);
	/* begin newSQFileByteArray */
	fileOop = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	file = arrayValueOf(fileOop);
	handle = GetStdHandle(STD_ERROR_HANDLE);
	if (handle != invalid) {
		stObjectatput(result, 2, trueObject());
	}
	else {
		stObjectatput(result, 2, falseObject());
	}
	file->file = handle;
	file->sessionID = thisSession;
	file->writable = 1;
	file->lastOp = 0;
	stObjectatput(result, 1, fileOop);
	pop(2);
	push(result);
	return 0;
}


/*	Answer a two element array containing the sqFile data structure
	representing standard input stream for my OS process, and a flag (true or
	false) to indicate
	whether the sqFile data structure contains a valid HANDLE. If no standard
	input stream is available for this OS process, the sqFile data structure
	will contain an
	invalid HANDLE value, which will result in failures on subsequent
	accesses. 
 */

	/* Win32OSProcessPlugin>>#primitiveGetStdInput */
EXPORT(sqInt)
primitiveGetStdInput(void)
{
    SQFile *file;
    sqInt fileOop;
    HANDLE handle;
    HANDLE invalid;
    sqInt result;
    sqInt thisSession;

	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	invalid = INVALID_HANDLE_VALUE;
	result = instantiateClassindexableSize(classArray(), 2);
	/* begin newSQFileByteArray */
	fileOop = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	file = arrayValueOf(fileOop);
	handle = GetStdHandle(STD_INPUT_HANDLE);
	if (handle != invalid) {
		stObjectatput(result, 2, trueObject());
	}
	else {
		stObjectatput(result, 2, falseObject());
	}
	file->file = handle;
	file->sessionID = thisSession;
	file->writable = 0;
	file->lastOp = 0;
	stObjectatput(result, 1, fileOop);
	pop(2);
	push(result);
	return 0;
}


/*	Answer a two element array containing the sqFile data structure
	representing standard output stream for my OS process, and a flag (true or
	false) to indicate
	whether the sqFile data structure contains a valid HANDLE. If no standard
	output stream is available for this OS process, the sqFile data structure
	will contain an
	invalid HANDLE value, which will result in failures on subsequent
	accesses. 
 */

	/* Win32OSProcessPlugin>>#primitiveGetStdOutput */
EXPORT(sqInt)
primitiveGetStdOutput(void)
{
    SQFile *file;
    sqInt fileOop;
    HANDLE handle;
    HANDLE invalid;
    sqInt result;
    sqInt thisSession;

	thisSession = sessionIdentifierFrom(stackObjectValue(0));
	invalid = INVALID_HANDLE_VALUE;
	result = instantiateClassindexableSize(classArray(), 2);
	/* begin newSQFileByteArray */
	fileOop = instantiateClassindexableSize(classByteArray(), sizeof(SQFile));
	/* begin fileValueOf: */
	file = arrayValueOf(fileOop);
	handle = GetStdHandle(STD_OUTPUT_HANDLE);
	if (handle != invalid) {
		stObjectatput(result, 2, trueObject());
	}
	else {
		stObjectatput(result, 2, falseObject());
	}
	file->file = handle;
	file->sessionID = thisSession;
	file->writable = 1;
	file->lastOp = 0;
	stObjectatput(result, 1, fileOop);
	pop(2);
	push(result);
	return 0;
}


/*	A character has been read into an external buffer corresponding to
	aSemaphoreIndex, and is now available. Answer the integer value of the
	character, or nil if no character
	was read, or -1 if an error occurred on the read.
 */

	/* Win32OSProcessPlugin>>#primitiveLastReadFor */
EXPORT(sqInt)
primitiveLastReadFor(void)
{
    sqInt index;
    sqInt result;

	index = stackIntegerValue(0);
	if (readCharStatusArray[index]) {
		if ((readCharCountArray[index]) < 1) {
			result = nilObject();
		}
		else {
			result = integerObjectOf(readCharBufferArray[index]);
		}
	}
	else {
		result = integerObjectOf(-1);
	}
	popthenPush(2, result);
	return 0;
}


/*	A character has been read into an external buffer corresponding to
	aSemaphoreIndex, and is now available. Answer the integer value of the
	character, or nil if no character
	was read, or -1 if an error occurred on the read. Store the full results
	of the most
	recent read into a three element array provided by the sender. Contents of
	the array
	will be status of the read call, character read, and character count
	(which should
	always be 1).
 */

	/* Win32OSProcessPlugin>>#primitiveLastReadForStoreIn */
EXPORT(sqInt)
primitiveLastReadForStoreIn(void)
{
    sqInt index;
    sqInt result;
    sqInt resultArray;

	index = stackIntegerValue(1);
	resultArray = stackValue(0);
	stObjectatput(resultArray, 1, positive32BitIntegerFor(readCharStatusArray[index]));
	stObjectatput(resultArray, 2, integerObjectOf(readCharBufferArray[index]));
	stObjectatput(resultArray, 3, integerObjectOf(readCharCountArray[index]));
	if (readCharStatusArray[index]) {
		if ((readCharCountArray[index]) < 1) {
			result = nilObject();
		}
		else {
			result = integerObjectOf(readCharBufferArray[index]);
		}
	}
	else {
		result = integerObjectOf(-1);
	}
	popthenPush(3, 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;
}


/*	Read the next character from anIOHandle (a SQFile struct) into a buffer in
	the VM. When
	the read completes, signal the specified Semaphore to notify that the
	character is available.
	Answer the handle of the new thread, or nil on error.
 */

	/* Win32OSProcessPlugin>>#primitiveNextFromSignaling */
EXPORT(sqInt)
primitiveNextFromSignaling(void)
{
    HANDLE handle;
    HANDLE hThread;
    static int index;
    static struct {int index; HANDLE handle;} threadArgs;
    DWORD threadID;

	index = stackIntegerValue(0);
	if (index >= readCharArraySize) {
		return primitiveFail();
	}
	waitForThreadMutex(readThreadMutexHandle());
	handle = handleFromSQFile(stackObjectValue(1));
	if (handle == null) {
		return null;
	}
	readCharBufferArray[index] = 0;
	readCharCountArray[index] = 0;
	readCharStatusArray[index] = 0;
	threadArgs.index = index;
	threadArgs.handle = handle;

	/* Safety net: Remember the identity of the thread which should be active for this
	   semaphore index. */
	hThread = CreateThread(NULL, 1024, readCharThread, &threadArgs, STACK_SIZE_PARAM_IS_A_RESERVATION, &threadID);
	readCharThreadIdArray[index] = threadID;
	if (hThread == null) {
		return nilObject();
	}
	popthenPush(3, handleObjectFrom(hThread));
	return 0;
}


/*	Anwer true the first time this primitive is called in a Squeak session,
	and false thereafter.
 */

	/* Win32OSProcessPlugin>>#primitiveOneShot */
EXPORT(sqInt)
primitiveOneShot(void)
{
    static int thisPrimHasBeenCalled= 0;

	if (thisPrimHasBeenCalled == 0) {
		thisPrimHasBeenCalled = 1;
		popthenPush(1, trueObject());
	}
	else {
		popthenPush(1, falseObject());
	}
	return 0;
}


/*	Set the file handle for standard error of my OS process to the handle
	passed in a SQFile struct.
 */

	/* Win32OSProcessPlugin>>#primitiveSetStdErr */
EXPORT(sqInt)
primitiveSetStdErr(void)
{
    FILEHANDLETYPE handle;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	handle = fileHandleFrom(sqFileOop);
	if (SetStdHandle(STD_ERROR_HANDLE, handle)) {
		popthenPush(2, trueObject());
	}
	else {
		popthenPush(2, falseObject());
	}
	return 0;
}


/*	Set the file handle for standard input of my OS process to the handle
	passed in a SQFile struct.
 */

	/* Win32OSProcessPlugin>>#primitiveSetStdIn */
EXPORT(sqInt)
primitiveSetStdIn(void)
{
    FILEHANDLETYPE handle;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	handle = fileHandleFrom(sqFileOop);
	if (SetStdHandle(STD_INPUT_HANDLE, handle)) {
		popthenPush(2, trueObject());
	}
	else {
		popthenPush(2, falseObject());
	}
	return 0;
}


/*	Set the file handle for standard output of my OS process to the handle
	passed in a SQFile struct.
 */

	/* Win32OSProcessPlugin>>#primitiveSetStdOut */
EXPORT(sqInt)
primitiveSetStdOut(void)
{
    FILEHANDLETYPE handle;
    sqInt sqFileOop;

	sqFileOop = stackValue(0);
	if (!((((isBytes(sqFileOop))
		 && ((byteSizeOf(sqFileOop)) == (sizeof(SQFile))))
		 && ((getThisSessionID()) == (sessionIdentifierFromSqFile(arrayValueOf(sqFileOop)))))
		 && (isNonNullSQFile(sqFileOop)))) {
		return primitiveFail();
	}
	handle = fileHandleFrom(sqFileOop);
	if (SetStdHandle(STD_OUTPUT_HANDLE, handle)) {
		popthenPush(2, trueObject());
	}
	else {
		popthenPush(2, falseObject());
	}
	return 0;
}


/*	Set up a thread to wait for a process HANDLE to exit, then signal the
	Semaphore at index. This provides asychronous notification of an external
	process exit. Answer
	the handle of the handler thread.
 */

	/* Win32OSProcessPlugin>>#primitiveSetWaitForAnyProcessExitThenSignalSemaphoreWithIndex */
EXPORT(sqInt)
primitiveSetWaitForAnyProcessExitThenSignalSemaphoreWithIndex(void)
{
    sqInt arraySize;
    static HANDLE handleArray[MAXIMUM_WAIT_OBJECTS];
    sqInt handleArrayOop;
    sqInt *handleArrayOopPtr;
    static HANDLE *handleArrayPtr;
    sqInt handleCount;
    sqInt idx;
    sqInt semaIndex;
    sqInt threadInformation;

	semaIndex = stackIntegerValue(0);
	handleArrayOop = stackObjectValue(1);
	handleArrayOopPtr = firstIndexableField(handleArrayOop);
	handleCount = stSizeOf(handleArrayOop);
	arraySize = MAXIMUM_WAIT_OBJECTS;
	if (handleCount > arraySize) {
		return primitiveFail();
	}
	idx = 0;
	while (idx < handleCount) {
		handleArray[idx] = (handleFrom(handleArrayOopPtr[idx]));
		idx += 1;
	}
	handleArrayPtr = handleArray;
	threadInformation = startHandlerThreadForAnycountsignalingSemaphoreAt(handleArrayPtr, handleCount, semaIndex);
	popthenPush(3, threadInformation);
	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;
}


/*	Kill the thread. No cleanup is performed, so use with caution for a thread
	which is (for example) manipulating a mutex. Answer true for success, else
	false. 
 */

	/* Win32OSProcessPlugin>>#primitiveTerminateThread */
EXPORT(sqInt)
primitiveTerminateThread(void)
{
    HANDLE handle;

	if (((handle = handleFrom(stackObjectValue(0)))) == null) {
		return null;
	}
	if (TerminateThread(handle, 0)) {
		popthenPush(2, trueObject());
	}
	else {
		popthenPush(2, falseObject());
	}
	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;
}


/*	This is the thread procedure for reading one character from a file or pipe
	handle. The values of index and handle have been passed on the local
	thread stack, so they are now local to the thread which is executing this
	method. Once the values are safely on a local thread stack, the mutex
	semaphore is released, allowing other threads to be created. */

	/* Win32OSProcessPlugin>>#readCharThreadIndex:handle: */
static DWORD WINAPI
readCharThreadIndexhandle(sqInt index, HANDLE handle)
{
    HANDLE aHandle;
    static HANDLE h= 0;
    sqInt status;


	/* Release mutex, allowing additional threads to be set up */
	/* begin releaseThreadMutex: */
	if (h == null) {
		h = createThreadMutex();
	}
	aHandle = h;
	ReleaseMutex(aHandle);
	status = ReadFile(handle, &readCharBufferArray[index], 1, &readCharCountArray[index], NULL);
	readCharStatusArray[index] = status;
	if ((GetCurrentThreadId()) == (readCharThreadIdArray[index])) {

		/* Signal Squeak that the character is now available */
		signalSemaphoreWithIndex(index);
	}
	else {

		/* Invalidate any data we just placed in the buffer */
		readCharBufferArray[index] = 0;
		readCharCountArray[index] = 0;
		readCharStatusArray[index] = 0;
	}
	return status;
}


/*	This is the entry point for the thread procedure for reading one character
	from a file or pipe handle. There may be many instances of this thread
	procedure, in
	which case each thread may be distinguished by the index value with which
	it is
	called. Remember the index value by saving it in a variable on the thread
	stack. Read one character, and signal a Smalltalk Semaphore to indicate
	completion. 
 */

	/* Win32OSProcessPlugin>>#readCharThread: */
static DWORD WINAPI
readCharThread(void *args)
{
    HANDLE handle;
    sqInt index;
    struct {int index; HANDLE handle;} *threadArgs;

	index = 0;
	threadArgs = args;
	index = threadArgs->index;
	handle = threadArgs->handle;
	return readCharThreadIndexhandle(index, handle);
}


/*	Answer a HANDLE for a mutex semaphore for sychronizing setup of read
	threads. 
 */

	/* Win32OSProcessPlugin>>#readThreadMutexHandle */
static HANDLE
readThreadMutexHandle(void)
{
    static HANDLE h= 0;

	if (h == null) {
		h = createThreadMutex();
	}
	return h;
}


/*	To be called at the end of a critical section. */

	/* Win32OSProcessPlugin>>#releaseThreadMutex: */
static BOOL
releaseThreadMutex(HANDLE aHandle)
{
	return ReleaseMutex(aHandle);
}


/*	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);
}


/*	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;
		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;
		nilObject = interpreterProxy->nilObject;
		pop = interpreterProxy->pop;
		popthenPush = interpreterProxy->popthenPush;
		popRemappableOop = interpreterProxy->popRemappableOop;
		positive32BitIntegerFor = interpreterProxy->positive32BitIntegerFor;
		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;
		storePointerofObjectwithValue = interpreterProxy->storePointerofObjectwithValue;
		trueObject = interpreterProxy->trueObject;
#endif /* !defined(SQUEAK_BUILTIN_PLUGIN) */
	}
	return ok;
}


/*	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);
}


/*	Wait for a process HANDLE to exit, then signal the Semaphore at index.
	Answer the thread ID of the handler. This is the code to be executed in a
	thread to provide
	asychronous notification of an external process exit.
 */

	/* Win32OSProcessPlugin>>#startHandlerThreadForAny:count:signalingSemaphoreAt: */
static sqInt
startHandlerThreadForAnycountsignalingSemaphoreAt(HANDLE *handleArrayPointer, sqInt arraySize, sqInt semaphoreIndex)
{
    HANDLE hThread;
    static struct {int count; HANDLE *handles; int semaIndex;} threadArgs;
    DWORD threadID;
    sqInt threadInformation;


	/* Complete setup of one handler before doing the next. Mutex will be
	   released in the newly started thread. */
	waitForThreadMutex(exitThreadMutexHandle());
	threadArgs.count= arraySize;
	threadArgs.handles= handleArrayPointer;
	threadArgs.semaIndex= semaphoreIndex;
	hThread = CreateThread(NULL, 1024, handleAnyChildProc, &threadArgs, STACK_SIZE_PARAM_IS_A_RESERVATION, &threadID);
	if (hThread == null) {
		return nilObject();
	}
	pushRemappableOop(positive32BitIntegerFor(threadID));
	pushRemappableOop(handleObjectFrom(hThread));
	threadInformation = instantiateClassindexableSize(classArray(), 2);
	storePointerofObjectwithValue(0, threadInformation, popRemappableOop());
	storePointerofObjectwithValue(1, threadInformation, popRemappableOop());
	return threadInformation;
}


/*	Answer the pseudo HANDLE for standard error. */

	/* Win32OSProcessPlugin>>#stdErrHandle */
static DWORD
stdErrHandle(void)
{
	return STD_ERROR_HANDLE;
}

	/* Win32OSProcessPlugin>>#stderrHandle */
static HANDLE
stderrHandle(void)
{
    HANDLE h;

	h = GetStdHandle(STD_ERROR_HANDLE);
	if (h == INVALID_HANDLE_VALUE) {
		primitiveFail();
	}
	return h;
}


/*	Answer the pseudo HANDLE for standard input. */

	/* Win32OSProcessPlugin>>#stdInHandle */
static DWORD
stdInHandle(void)
{
	return STD_INPUT_HANDLE;
}

	/* Win32OSProcessPlugin>>#stdinHandle */
static HANDLE
stdinHandle(void)
{
    HANDLE h;

	h = GetStdHandle(STD_INPUT_HANDLE);
	if (h == INVALID_HANDLE_VALUE) {
		primitiveFail();
	}
	return h;
}


/*	Answer the pseudo HANDLE for standard output. */

	/* Win32OSProcessPlugin>>#stdOutHandle */
static DWORD
stdOutHandle(void)
{
	return STD_OUTPUT_HANDLE;
}

	/* Win32OSProcessPlugin>>#stdoutHandle */
static HANDLE
stdoutHandle(void)
{
    HANDLE h;

	h = GetStdHandle(STD_OUTPUT_HANDLE);
	if (h == INVALID_HANDLE_VALUE) {
		primitiveFail();
	}
	return h;
}


/*	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 HANDLE for a mutex semaphore. */

	/* Win32OSProcessPlugin>>#threadMutexHandle */
static HANDLE
threadMutexHandle(void)
{
    static HANDLE h= 0;

	if (h == 0) {
		h = CreateMutex(NULL, false, NULL);
	}
	return h;
}


/*	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 a string identifying the version level for this plugin. */

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

	return version;
}


/*	To be called before entering a critical section. */

	/* Win32OSProcessPlugin>>#waitForThreadMutex: */
static DWORD
waitForThreadMutex(HANDLE aHandle)
{
	return WaitForSingleObject(aHandle, INFINITE);
}


#ifdef SQUEAK_BUILTIN_PLUGIN

static char _m[] = "Win32OSProcessPlugin";
void* Win32OSProcessPlugin_exports[][3] = {
	{(void*)_m, "getModuleName", (void*)getModuleName},
	{(void*)_m, "initialiseModule", (void*)initialiseModule},
	{(void*)_m, "moduleUnloaded", (void*)moduleUnloaded},
	{(void*)_m, "primitiveAllocConsole\000\377", (void*)primitiveAllocConsole},
	{(void*)_m, "primitiveBufferValuesAt\000\001", (void*)primitiveBufferValuesAt},
	{(void*)_m, "primitiveCanAccessChildProcess\000\001", (void*)primitiveCanAccessChildProcess},
	{(void*)_m, "primitiveCloseHandle\000\001", (void*)primitiveCloseHandle},
	{(void*)_m, "primitiveCommand\000\002", (void*)primitiveCommand},
	{(void*)_m, "primitiveCommandWithInputOutputError\000\002", (void*)primitiveCommandWithInputOutputError},
	{(void*)_m, "primitiveCreatePipe\000\377", (void*)primitiveCreatePipe},
	{(void*)_m, "primitiveCreatePipeWithSessionIdentifier\000\001", (void*)primitiveCreatePipeWithSessionIdentifier},
	{(void*)_m, "primitiveFixPointersInArrayOfStrings\000\001", (void*)primitiveFixPointersInArrayOfStrings},
	{(void*)_m, "primitiveFreeConsole\000\377", (void*)primitiveFreeConsole},
	{(void*)_m, "primitiveGetCurrentWorkingDirectory\000\377", (void*)primitiveGetCurrentWorkingDirectory},
	{(void*)_m, "primitiveGetCurrentWorkingDirectoryAsBytes\000\377", (void*)primitiveGetCurrentWorkingDirectoryAsBytes},
	{(void*)_m, "primitiveGetEnvironmentStrings\000\377", (void*)primitiveGetEnvironmentStrings},
	{(void*)_m, "primitiveGetEnvironmentStringsAsBytes\000\377", (void*)primitiveGetEnvironmentStringsAsBytes},
	{(void*)_m, "primitiveGetExitStatusForHandle\000\001", (void*)primitiveGetExitStatusForHandle},
	{(void*)_m, "primitiveGetMainThreadHandle\000\377", (void*)primitiveGetMainThreadHandle},
	{(void*)_m, "primitiveGetMainThreadID\000\377", (void*)primitiveGetMainThreadID},
	{(void*)_m, "primitiveGetPid\000\377", (void*)primitiveGetPid},
	{(void*)_m, "primitiveGetPidHandle\000\377", (void*)primitiveGetPidHandle},
	{(void*)_m, "primitiveGetSession\000\377", (void*)primitiveGetSession},
	{(void*)_m, "primitiveGetStdError\000\001", (void*)primitiveGetStdError},
	{(void*)_m, "primitiveGetStdInput\000\001", (void*)primitiveGetStdInput},
	{(void*)_m, "primitiveGetStdOutput\000\001", (void*)primitiveGetStdOutput},
	{(void*)_m, "primitiveLastReadFor\000\000", (void*)primitiveLastReadFor},
	{(void*)_m, "primitiveLastReadForStoreIn\000\000", (void*)primitiveLastReadForStoreIn},
	{(void*)_m, "primitiveMakePipe\000\377", (void*)primitiveMakePipe},
	{(void*)_m, "primitiveMakePipeWithSessionIdentifier\000\001", (void*)primitiveMakePipeWithSessionIdentifier},
	{(void*)_m, "primitiveModuleName\000\377", (void*)primitiveModuleName},
	{(void*)_m, "primitiveNextFromSignaling\000\001", (void*)primitiveNextFromSignaling},
	{(void*)_m, "primitiveOneShot\000\377", (void*)primitiveOneShot},
	{(void*)_m, "primitiveSetStdErr\000\001", (void*)primitiveSetStdErr},
	{(void*)_m, "primitiveSetStdIn\000\001", (void*)primitiveSetStdIn},
	{(void*)_m, "primitiveSetStdOut\000\001", (void*)primitiveSetStdOut},
	{(void*)_m, "primitiveSetWaitForAnyProcessExitThenSignalSemaphoreWithIndex\000\001", (void*)primitiveSetWaitForAnyProcessExitThenSignalSemaphoreWithIndex},
	{(void*)_m, "primitiveSizeOfInt\000\377", (void*)primitiveSizeOfInt},
	{(void*)_m, "primitiveSizeOfPointer\000\377", (void*)primitiveSizeOfPointer},
	{(void*)_m, "primitiveTerminateThread\000\001", (void*)primitiveTerminateThread},
	{(void*)_m, "primitiveVersionString\000\377", (void*)primitiveVersionString},
	{(void*)_m, "setInterpreter", (void*)setInterpreter},
	{NULL, NULL, NULL}
};

#else /* ifdef SQ_BUILTIN_PLUGIN */

signed char primitiveBufferValuesAtAccessorDepth = 1;
signed char primitiveCanAccessChildProcessAccessorDepth = 1;
signed char primitiveCloseHandleAccessorDepth = 1;
signed char primitiveCommandAccessorDepth = 2;
signed char primitiveCommandWithInputOutputErrorAccessorDepth = 2;
signed char primitiveCreatePipeWithSessionIdentifierAccessorDepth = 1;
signed char primitiveFixPointersInArrayOfStringsAccessorDepth = 1;
signed char primitiveGetExitStatusForHandleAccessorDepth = 1;
signed char primitiveGetStdErrorAccessorDepth = 1;
signed char primitiveGetStdInputAccessorDepth = 1;
signed char primitiveGetStdOutputAccessorDepth = 1;
signed char primitiveLastReadForAccessorDepth = 0;
signed char primitiveLastReadForStoreInAccessorDepth = 0;
signed char primitiveMakePipeWithSessionIdentifierAccessorDepth = 1;
signed char primitiveNextFromSignalingAccessorDepth = 1;
signed char primitiveSetStdErrAccessorDepth = 1;
signed char primitiveSetStdInAccessorDepth = 1;
signed char primitiveSetStdOutAccessorDepth = 1;
signed char primitiveSetWaitForAnyProcessExitThenSignalSemaphoreWithIndexAccessorDepth = 1;
signed char primitiveTerminateThreadAccessorDepth = 1;

#endif /* ifdef SQ_BUILTIN_PLUGIN */
