/* Automatically generated from Squeak on #(4 April 2004 12:15:53 pm) */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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. */
/*  The virtual machine proxy definition */
#include "sqVirtualMachine.h"
/* Configuration options */
#include "sqConfig.h"
/* Platform specific definitions */
#include "sqPlatformSpecific.h"

#define true 1
#define false 0
#define null 0  /* using 'null' because nil is predefined in Think C */
#ifdef SQUEAK_BUILTIN_PLUGIN
#undef EXPORT
// was #undef EXPORT(returnType) but screws NorCroft cc
#define EXPORT(returnType) static returnType
#endif
#include "rePlugin.h"

/* memory access macros */
#define byteAt(i) (*((unsigned char *) (i)))
#define byteAtput(i, val) (*((unsigned char *) (i)) = val)
#define longAt(i) (*((int *) (i)))
#define longAtput(i, val) (*((int *) (i)) = val)


/*** Constants ***/

/*** Variables ***/
static int compileFlags;
static int errorOffset;
static int errorStr;
static const char * errorStrBuffer;
static int extraPtr;

#ifdef SQUEAK_BUILTIN_PLUGIN
extern
#endif
struct VirtualMachine* interpreterProxy;
static int lastAlloc = 0;
static int matchFlags;
static const char *moduleName =
#ifdef SQUEAK_BUILTIN_PLUGIN
	"RePlugin 4 April 2004 (i)"
#else
	"RePlugin 4 April 2004 (e)"
#endif
;
static int netMemory = 0;
static int numAllocs = 0;
static int numFrees = 0;
static int patternStr;
static const char * patternStrPtr;
static int pcrePtr;
static int rcvr;

/*** Function Prototypes ***/
static int allocateByteArrayAndSetRcvrExtraPtrFrom(int anExtraPtr);
static int allocateByteArrayAndSetRcvrPCREPtrFromPCRE(int aPCREPtr);
static int allocateStringAndSetRcvrErrorStrFromCStr(const char *aCStrBuffer);
#pragma export on
EXPORT(const char*) getModuleName(void);
#pragma export off
static int halt(void);
static int loadRcvrFromStackAt(int stackInteger);
static int msg(char *s);
#pragma export on
EXPORT(int) primLastAlloc(void);
EXPORT(int) primNetMemory(void);
EXPORT(int) primNumAllocs(void);
EXPORT(int) primNumFrees(void);
EXPORT(int) primPCRECompile(void);
EXPORT(int) primPCREExec(void);
EXPORT(int) primPCREExecfromto(void);
EXPORT(int) primPCRENumSubPatterns(void);
#pragma export off
static int rcvrCompileFlags(void);
static int rcvrErrorOffsetFrom(int anInteger);
static int rcvrErrorStrFrom(int aString);
static int rcvrExtraPtr(void);
static int rcvrExtraPtrFrom(int aByteArrayOrNilObject);
static int rcvrMatchFlags(void);
static int * rcvrMatchSpacePtr(void);
static int rcvrMatchSpaceSize(void);
static int rcvrPCREBufferFrom(int aByteArray);
static int rcvrPCREBufferPtr(void);
static char * rcvrPatternStrPtr(void);
static void rePluginFree(void * aPointer);
static void * rePluginMalloc(size_t anInteger);
#pragma export on
EXPORT(int) setInterpreter(struct VirtualMachine* anInterpreter);
#pragma export off
static int sqAssert(int aBool);

static int allocateByteArrayAndSetRcvrExtraPtrFrom(int anExtraPtr) {
	int extraObject;
	void *extraByteArrayPtr;

	if (anExtraPtr) {
		extraObject = interpreterProxy->instantiateClassindexableSize(interpreterProxy->classByteArray(), sizeof(real_pcre_extra));
		/* begin loadRcvrFromStackAt: */
		rcvr = interpreterProxy->stackObjectValue(0);
		extraByteArrayPtr = interpreterProxy->arrayValueOf(extraObject);
		memcpy(extraByteArrayPtr, (void *) anExtraPtr, sizeof(real_pcre_extra));
	} else {
		extraObject = interpreterProxy->nilObject();
	}
	/* begin rcvrExtraPtrFrom: */
	interpreterProxy->storePointerofObjectwithValue(3, rcvr, extraObject);
	;
	return extraObject;
}

static int allocateByteArrayAndSetRcvrPCREPtrFromPCRE(int aPCREPtr) {
	int patObject;
	void *patByteArrayPtr;

	patObject = interpreterProxy->instantiateClassindexableSize(interpreterProxy->classByteArray(), lastAlloc);
	/* begin loadRcvrFromStackAt: */
	rcvr = interpreterProxy->stackObjectValue(0);
	patByteArrayPtr = interpreterProxy->arrayValueOf(patObject);
	memcpy(patByteArrayPtr, (void *) aPCREPtr, lastAlloc);
	/* begin rcvrPCREBufferFrom: */
	interpreterProxy->storePointerofObjectwithValue(2, rcvr, patObject);
	;
	return patObject;
}

static int allocateStringAndSetRcvrErrorStrFromCStr(const char *aCStrBuffer) {
	int length;
	void *errorStrObjPtr;
	int errorStrObj;

	length = strlen(aCStrBuffer);
	errorStrObj = interpreterProxy->instantiateClassindexableSize(interpreterProxy->classString(), length);
	/* begin loadRcvrFromStackAt: */
	rcvr = interpreterProxy->stackObjectValue(0);
	errorStrObjPtr = interpreterProxy->arrayValueOf(errorStrObj);
	memcpy(errorStrObjPtr,aCStrBuffer,length);
	;
	;
	/* begin rcvrErrorStrFrom: */
	interpreterProxy->storePointerofObjectwithValue(4, rcvr, errorStrObj);
	return errorStrObj;
}


/*	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! */

EXPORT(const char*) getModuleName(void) {
	return moduleName;
}

static int halt(void) {
	;
}

static int loadRcvrFromStackAt(int stackInteger) {
	rcvr = interpreterProxy->stackObjectValue(stackInteger);
}

static int msg(char *s) {
	fprintf(stderr, "\n%s: %s", moduleName, s);
}

EXPORT(int) primLastAlloc(void) {
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(lastAlloc);
}

EXPORT(int) primNetMemory(void) {
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(netMemory);
}

EXPORT(int) primNumAllocs(void) {
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(numAllocs);
}

EXPORT(int) primNumFrees(void) {
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(numFrees);
}


/*	<rcvr primPCRECompile>, where rcvr is an object with instance variables:

	'patternStr compileFlags pcrePtr extraPtr errorStr errorOffset matchFlags'	

Compile the regular expression in patternStr, and if the compilation is successful, attempt to optimize the compiled expression.  Store the results in <pcrePtr> and <extratr>, or fill errorStr with a meaningful errorString and errorOffset with an indicator where the error was found, applying compileFlags throughout.  Answer nil with a clean compile (regardless of whether an optimization is possible, and answer with the string otherwise. */

EXPORT(int) primPCRECompile(void) {
	int anInteger;

	/* begin loadRcvrFromStackAt: */
	rcvr = interpreterProxy->stackObjectValue(0);
	patternStrPtr = ((char *) (interpreterProxy->fetchArrayofObject(0, rcvr)));
	compileFlags = interpreterProxy->fetchIntegerofObject(1, rcvr);
	if (interpreterProxy->failed()) {
		return null;
	}
	pcrePtr = (int) pcre_compile(patternStrPtr, compileFlags, 
					&errorStrBuffer, &errorOffset, NULL);
	if (pcrePtr) {
		allocateByteArrayAndSetRcvrPCREPtrFromPCRE(pcrePtr);
		extraPtr = (int) pcre_study((pcre *)pcrePtr, compileFlags, &errorStrBuffer);
		allocateByteArrayAndSetRcvrExtraPtrFrom(extraPtr);
		rePluginFree(((void *) pcrePtr));
		if (extraPtr) {
			rePluginFree(((void *) extraPtr));
		}
		if (interpreterProxy->failed()) {
			return null;
		}
		interpreterProxy->popthenPush(1, interpreterProxy->nilObject());
	} else {
		errorStr = allocateStringAndSetRcvrErrorStrFromCStr(errorStrBuffer);
		/* begin rcvrErrorOffsetFrom: */
		anInteger = errorOffset;
		interpreterProxy->storeIntegerofObjectwithValue(5, rcvr, anInteger);
		if (interpreterProxy->failed()) {
			return null;
		}
		interpreterProxy->popthenPush(1, errorStr);
	}
}


/*	<rcvr primPCREExec: searchObject>, where rcvr is an object with instance variables:

	'patternStr compileFlags pcrePtr extraPtr errorStr errorOffset matchFlags'	

Apply the regular expression (stored in <pcrePtr> and <extratr>, generated from calls to primPCRECompile), to smalltalk String searchObject using <matchOptions>.  If there is no match, answer nil.  Otherwise answer a ByteArray of offsets representing the results of the match. */

EXPORT(int) primPCREExec(void) {
	int *matchSpacePtr;
	int length;
	int searchObject;
	int result;
	char *searchBuffer;
	int matchSpaceSize;

	searchObject = interpreterProxy->stackObjectValue(0);
	searchBuffer = interpreterProxy->arrayValueOf(searchObject);
	length = interpreterProxy->byteSizeOf(searchObject);
	/* begin loadRcvrFromStackAt: */
	rcvr = interpreterProxy->stackObjectValue(1);
	pcrePtr = ((int) (interpreterProxy->fetchArrayofObject(2, rcvr)));
	extraPtr = rcvrExtraPtr();
	matchFlags = interpreterProxy->fetchIntegerofObject(6, rcvr);
	matchSpacePtr = ((int *) (interpreterProxy->fetchArrayofObject(7, rcvr)));
	matchSpaceSize = ((int) (interpreterProxy->byteSizeOf(interpreterProxy->fetchPointerofObject(7, rcvr))) >> 2);
	if (interpreterProxy->failed()) {
		return null;
	}
	result = pcre_exec((pcre *)pcrePtr, (pcre_extra *)extraPtr, 
				searchBuffer, length, 0, matchFlags, matchSpacePtr, matchSpaceSize);
	interpreterProxy->pop(2);
	interpreterProxy->pushInteger(result);
	;
	;
	;
	;
}


/*	<rcvr primPCREExec: searchObject> from: fromInteger to: toInteger>, where rcvr is an object with instance variables:

	'patternStr compileFlags pcrePtr extraPtr errorStr errorOffset matchFlags'	

Apply the regular expression (stored in <pcrePtr> and <extratr>, generated from calls to primPCRECompile), to smalltalk String searchObject using <matchOptions>, beginning at offset <fromInteger> and continuing until offset <toInteger>.  If there is no match, answer nil.  Otherwise answer a ByteArray of offsets representing the results of the match. */

EXPORT(int) primPCREExecfromto(void) {
	int *matchSpacePtr;
	int length;
	int searchObject;
	int toInteger;
	int result;
	char *searchBuffer;
	int matchSpaceSize;
	int fromInteger;

	toInteger = interpreterProxy->stackIntegerValue(0);
	fromInteger = interpreterProxy->stackIntegerValue(1);
	searchObject = interpreterProxy->stackObjectValue(2);
	searchBuffer = interpreterProxy->arrayValueOf(searchObject);
	length = interpreterProxy->byteSizeOf(searchObject);
	/* begin loadRcvrFromStackAt: */
	rcvr = interpreterProxy->stackObjectValue(3);
	interpreterProxy->success(1 <= fromInteger);
	interpreterProxy->success(toInteger <= length);

	/* Smalltalk offsets are 1-based */

	fromInteger -= 1;
	interpreterProxy->success(fromInteger <= toInteger);
	length = toInteger - fromInteger;

	/* Load Instance Variables */

	searchBuffer += fromInteger;
	pcrePtr = ((int) (interpreterProxy->fetchArrayofObject(2, rcvr)));
	extraPtr = rcvrExtraPtr();
	matchFlags = interpreterProxy->fetchIntegerofObject(6, rcvr);
	matchSpacePtr = ((int *) (interpreterProxy->fetchArrayofObject(7, rcvr)));
	matchSpaceSize = ((int) (interpreterProxy->byteSizeOf(interpreterProxy->fetchPointerofObject(7, rcvr))) >> 2);
	if (interpreterProxy->failed()) {
		return null;
	}
	result = pcre_exec((pcre *)pcrePtr, (pcre_extra *)extraPtr, 
				searchBuffer, length, 0, matchFlags, matchSpacePtr, matchSpaceSize);
	interpreterProxy->pop(2);
	interpreterProxy->pushInteger(result);
	;
	;
	;
	;
}


/*	<rcvr primPCRENumSubPatterns>, where rcvr is an object with instance variables:

	'patternStr compileFlags pcrePtr extraPtr errorStr errorOffset matchFlags'	

Return the number of subpatterns captured by the compiled pattern. */

EXPORT(int) primPCRENumSubPatterns(void) {
	/* begin loadRcvrFromStackAt: */
	rcvr = interpreterProxy->stackObjectValue(0);
	pcrePtr = ((int) (interpreterProxy->fetchArrayofObject(2, rcvr)));
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(pcre_info((pcre *)pcrePtr, NULL, NULL));
}

static int rcvrCompileFlags(void) {
	return interpreterProxy->fetchIntegerofObject(1, rcvr);
}

static int rcvrErrorOffsetFrom(int anInteger) {
	interpreterProxy->storeIntegerofObjectwithValue(5, rcvr, anInteger);
}

static int rcvrErrorStrFrom(int aString) {
	interpreterProxy->storePointerofObjectwithValue(4, rcvr, aString);
}

static int rcvrExtraPtr(void) {
	int extraObj;

	extraObj = interpreterProxy->fetchPointerofObject(3, rcvr);
	if (extraObj == (interpreterProxy->nilObject())) {
		return (int) NULL;
	}
	return ((int) (interpreterProxy->arrayValueOf(extraObj)));
}

static int rcvrExtraPtrFrom(int aByteArrayOrNilObject) {
	interpreterProxy->storePointerofObjectwithValue(3, rcvr, aByteArrayOrNilObject);
}

static int rcvrMatchFlags(void) {
	return interpreterProxy->fetchIntegerofObject(6, rcvr);
}

static int * rcvrMatchSpacePtr(void) {
	return ((int *) (interpreterProxy->fetchArrayofObject(7, rcvr)));
}

static int rcvrMatchSpaceSize(void) {
	return ((int) (interpreterProxy->byteSizeOf(interpreterProxy->fetchPointerofObject(7, rcvr))) >> 2);
}

static int rcvrPCREBufferFrom(int aByteArray) {
	interpreterProxy->storePointerofObjectwithValue(2, rcvr, aByteArray);
}

static int rcvrPCREBufferPtr(void) {
	return ((int) (interpreterProxy->fetchArrayofObject(2, rcvr)));
}

static char * rcvrPatternStrPtr(void) {
	return ((char *) (interpreterProxy->fetchArrayofObject(0, rcvr)));
}


/*	Free a block of fixed memory allocated with rePluginMalloc.  Instrumented version of C free() to facilitate leak analysis from Smalltalk.   OS-specific variations on malloc/free, such as with MacOS, are handled by adding a C macro to the header file redefining malloc/free -- see the class comment */

static void rePluginFree(void * aPointer) {
	numFrees += 1;
	if (aPointer) {
		free(aPointer);
	}
}


/*	Allocate a block of fixed memory using C calls to malloc().  Instrumented to facilitate leak analysis from Smalltalk.  Set global lastAlloc to anInteger.  OS-specific variations on malloc/free, such as with MacOS, are handled by adding a C macro to the header file redefining malloc/free -- see the class comment */

static void * rePluginMalloc(size_t anInteger) {
	void *aPointer;

	numAllocs += 1;
	if (aPointer = malloc(anInteger)) {
		lastAlloc = anInteger;
	}
	return aPointer;
}


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

EXPORT(int) setInterpreter(struct VirtualMachine* anInterpreter) {
	int ok;

	interpreterProxy = anInterpreter;
	ok = interpreterProxy->majorVersion() == VM_PROXY_MAJOR;
	if (ok == 0) {
		return 0;
	}
	ok = interpreterProxy->minorVersion() >= VM_PROXY_MINOR;
	return ok;
}

static int sqAssert(int aBool) {
	/* missing DebugCode */;
}


#ifdef SQUEAK_BUILTIN_PLUGIN


void* RePlugin_exports[][3] = {
	{"RePlugin", "primPCREExec", (void*)primPCREExec},
	{"RePlugin", "primPCREExecfromto", (void*)primPCREExecfromto},
	{"RePlugin", "primPCRENumSubPatterns", (void*)primPCRENumSubPatterns},
	{"RePlugin", "primPCRECompile", (void*)primPCRECompile},
	{"RePlugin", "primNumFrees", (void*)primNumFrees},
	{"RePlugin", "primNumAllocs", (void*)primNumAllocs},
	{"RePlugin", "getModuleName", (void*)getModuleName},
	{"RePlugin", "primLastAlloc", (void*)primLastAlloc},
	{"RePlugin", "primNetMemory", (void*)primNetMemory},
	{"RePlugin", "setInterpreter", (void*)setInterpreter},
	{NULL, NULL, NULL}
};


#endif /* ifdef SQ_BUILTIN_PLUGIN */

