/*******************************************************************************
 * Copyright (c) 2003, 2009 IBM Corporation, Intel Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Karla Callaghan, Intel - Brought multi-platform support in from HCLauncher
 *				code which was implemented by IBM
 *    IBM Corporation - initial implementation of process launch
 *
 * $Id: ProcessControlUtil.c,v 1.52 2009/07/10 00:11:28 jwest Exp $
 *******************************************************************************/ 
#include <stdio.h>
#include <stdlib.h>
#include "tptp/TransportSupport.h"
#include "tptp/ProcCtlUtils.h"
#include "tptp/NoLog.h"
#include "tptp/TPTPConfigBinding.h"
#include "launcher_md.h"


#ifdef _WIN32
	#include <sys/types.h>
	#include <sys/stat.h>
	#define TPTP_STAT _stat
	#define mode_t unsigned short
	#define S_ISDIR(mode) (((mode)&_S_IFMT) == _S_IFDIR)
	#define S_IWUSR _S_IWRITE
	#define S_IRUSR _S_IREAD
	#define S_IXUSR _S_IEXEC
#else
	#include <string.h>
	#include <unistd.h>
	#include <errno.h>
	#include <sys/stat.h>
	#include <sys/types.h>
	#if defined(__linux__)
		#include <linux/stat.h>  //for mode S_IXUSR
	#endif
	#define TPTP_STAT stat
#endif
extern char **environ;
void freeEnvironmentStrings(void *env);
static char *resizeBuffer(char *buffer, int bufferSize, int increment);

/*
 * Allocate and initialize a tptp_listener_t structure.
 *
 * Returns:
 * Pointer to tptp_listener_t if successful.
 * Null if error occurred.
 */
tptp_listener_t* initListenerT(const int destID, const int listenerID, const char* eventsIID)
{
	tptp_listener_t* listener = NULL;

	listener = (tptp_listener_t*)tptp_malloc( sizeof(tptp_listener_t) );
	if ( listener == NULL )
	{
		// Report internal memory error.
		return NULL;
	}

	listener->destID = destID;
	listener->listenerID = listenerID;
	if (eventsIID)
	{
		int len = strlen(eventsIID) + 1;
		listener->eventInterfaceID = (char*) tptp_malloc(len);
		memcpy(listener->eventInterfaceID, eventsIID, len);
	}
	else
		listener->eventInterfaceID = NULL;

	return listener;
}
/*
 * Allocate a new tptp_listener_t structure and copy contents of
 * the listener arg into it.
 *
 * Returns:
 * Pointer to clone's tptp_listener_t if successful.
 * Null if error occurred.
 */
tptp_listener_t* cloneListenerT(const tptp_listener_t* listener)
{
	tptp_listener_t* cloneLstnr = NULL;

	cloneLstnr = (tptp_listener_t*)tptp_malloc( sizeof(tptp_listener_t) );
	if ( cloneLstnr == NULL )
	{
		/* TODO: Report an internal error */
		TPTP_LOG_ERROR_MSG("ProcessControlUtil: Error - failed to alloc listener obj");
		return NULL;
	}

	cloneLstnr->destID = listener->destID;
	cloneLstnr->listenerID = listener->listenerID;

	if (listener->eventInterfaceID)
	{
		int len = strlen(listener->eventInterfaceID) + 1;
		cloneLstnr->eventInterfaceID = (char*) tptp_malloc(len);
		memcpy(cloneLstnr->eventInterfaceID, listener->eventInterfaceID, len);
	}
	else
		cloneLstnr->eventInterfaceID = NULL;

	return cloneLstnr;
}

/*
 * Used by the tptp_list utility to get a copy of a tptp_listener_t node.
 */
int copyListenerNode(const tptp_node_t* node, tptp_node_t* nodeCopy)
{
	tptp_listener_t* listener = (tptp_listener_t*)node->data;
	nodeCopy->data = cloneListenerT(listener);
	return 0;
}



/*
 * Free any allocated members of a tptp_listener_t structure.
 */
void destroyListenerT(tptp_listener_t* listener)
{
	if (listener == NULL)
		return;

	listener->destID = -1;
	listener->listenerID = -1;

	if (listener->eventInterfaceID)
	{
		tptp_free(listener->eventInterfaceID);
		listener->eventInterfaceID = NULL;
	}

	return;
}

/*
 * Used by the tptp_list utility to free contents of a tptp_listener_t node in a list.
 */
int destroyListenerNode(tptp_node_t* node)
{
	tptp_listener_t* listener = (tptp_listener_t*)node->data;
	destroyListenerT(listener);
	return 0;
}

/*
 * Allocate and initialize a tptp_process_t structure.
 *
 * Returns:
 * Pointer to tptp_process_t if successful.
 * Null if error occurred.
 */
tptp_process_t* initProcessT(const int sourceID, 
							 const int context, 
							 const char* appName,
							 const char* cmdLineArgs, 
							 const char* workingDir,
							 const char* envVars, 
							 const int consoleConnID,
							 const unsigned int optionsFlag)
{
	tptp_process_t* proc = NULL;

	proc = (tptp_process_t*)tptp_malloc( sizeof(tptp_process_t) );
	if ( proc == NULL )
	{
		/* TODO: Report an internal error */
		TPTP_LOG_ERROR_MSG("ProcessControlUtil: Error - failed to alloc proc obj.");
		return NULL;
	}

	proc->ownerID = sourceID;
	proc->ownerContext = context;

	if (appName)
	{
		int len = strlen(appName) + 1;
		proc->appName = (char *) tptp_malloc(len);
		memcpy(proc->appName, appName, len);
	}
	else
		proc->appName = NULL;

	if (cmdLineArgs)
	{
		int len = strlen(cmdLineArgs) + 1;
		proc->cmdLineArgs = (char *) tptp_malloc(len);
		memcpy(proc->cmdLineArgs, cmdLineArgs, len);
	}
	else
		proc->cmdLineArgs = NULL;

	if (workingDir)
	{
		int len = strlen(workingDir) + 1;
		proc->workingDir = (char *) tptp_malloc(len);
		memcpy(proc->workingDir, workingDir, len);
	}
	else
		proc->workingDir = NULL;

	// This is an array of strings, so you have embedded nulls and the
	// end of the list is denoted by two Nuls.
	if (envVars)
	{
		int count = 0;

		//Count the lenth of all the strings then we
		//can just do a memcopy.
		while ( !((envVars[count] == 0) && (envVars[count+1]) == 0) )
		{
			count++;
		}

		count = count+2; //add 2: null at end of final string + null at end of list
		proc->envVars = (char *) tptp_malloc(count);
		memcpy(proc->envVars, envVars, count);
	}
	else
		proc->envVars = NULL;

	if (optionsFlag & TPTP_START_KEEP_PROCESS)
		proc->keepProcess = 1;
	else
		proc->keepProcess = 0;

	if (optionsFlag & TPTP_START_NO_NOTICES)
		proc->noNotices = 1;
	else
		proc->noNotices = 0;

	proc->consoleConnID = consoleConnID;
	proc->consoleUniqueID = NULL; //filled in later if do a console connect

	proc->pid = TPTP_INVALID_PID;	//filled in after process started
#ifdef _WIN32
	proc->hProcess = TPTP_HANDLE_NULL; //filled in after process started 
#endif

	// Create empty list of monitors (ID's that want to get notices of process state change)
	proc->monitors = (tptp_list_t*)tptp_malloc( sizeof(tptp_list_t) );
	if ( proc->monitors == NULL )
	{
		/* TODO: Report an internal error */
		TPTP_LOG_ERROR_MSG("ProcessControlUtil: Error - failed to alloc monitors list.");
		return NULL;
	}
	tptp_list_init(proc->monitors);
	tptp_list_setNodeDestructor(proc->monitors, destroyListenerNode);
	tptp_list_setNodeCopier(proc->monitors, copyListenerNode);

	return proc;

}

/*
 * Allocate a new tptp_process_t structure and copy contents of
 * the process arg into it.
 *
 * Returns:
 * Pointer to clone's tptp_process_t if successful.
 * Null if error occurred.
 */
tptp_process_t* cloneProcessT(const tptp_process_t* proc)
{
	tptp_process_t* cloneProc = NULL;

	cloneProc = (tptp_process_t*)tptp_malloc( sizeof(tptp_process_t) );
	if ( cloneProc == NULL )
	{
		/* TODO: Report an internal error */
		TPTP_LOG_ERROR_MSG("ProcessControlUtil: Error - failed to alloc proc obj");
		return NULL;
	}

	cloneProc->ownerID = proc->ownerID;
	cloneProc->ownerContext = proc->ownerContext;

	if (proc->appName)
	{
		int len = strlen(proc->appName) + 1;
		cloneProc->appName = (char *) tptp_malloc(len);
		memcpy(cloneProc->appName, proc->appName, len);
	}
	else
		cloneProc->appName = NULL;

	if (proc->cmdLineArgs) 
	{
		int len = strlen(proc->cmdLineArgs) + 1;
		cloneProc->cmdLineArgs = (char *) tptp_malloc(len);
		memcpy(cloneProc->cmdLineArgs, proc->cmdLineArgs, len);
	}
	else
		cloneProc->cmdLineArgs = NULL;

	if (proc->workingDir) 
	{
		int len = strlen(proc->workingDir) + 1;
		cloneProc->workingDir = (char *) tptp_malloc(len);
		memcpy(cloneProc->workingDir, proc->workingDir, len);
	}
	else
		cloneProc->workingDir = NULL;


	// This is an array of strings, so you have embedded nulls and the
	// end of the list is denoted by two Nulls.
	if (proc->envVars)
	{
		int count = 0;

		//Count the lenth of all the strings then we
		//can just do a memcopy.
		while ( !((proc->envVars[count] == '\0') && (proc->envVars[count+1] == '\0')) )
		{
			count++;
		}

		// add 2 for two final nulls in the list
		// please note that we exited the while loop above right when the first
		// null of the final two nulls appeared. Hence, we need to add 2 here - one for each null
		count = count + 2; 

		cloneProc->envVars = (char *) tptp_malloc(count);
		memcpy(cloneProc->envVars, proc->envVars, count);
	}
	else
		cloneProc->envVars = NULL;

	cloneProc->keepProcess = proc->keepProcess;
	cloneProc->noNotices = proc->noNotices;

	cloneProc->consoleConnID = proc->consoleConnID;

	if (proc->consoleUniqueID) 
	{
		int len = strlen(proc->consoleUniqueID) + 1;
		cloneProc->consoleUniqueID = (char *) tptp_malloc(len);
		memcpy(cloneProc->consoleUniqueID, proc->consoleUniqueID, len);
	}
	else
		cloneProc->consoleUniqueID = NULL;

	cloneProc->pid = proc->pid;
#ifdef _WIN32
	cloneProc->hProcess = proc->hProcess;
#endif

	//Copy list of listeners if any.
	cloneProc->monitors = (tptp_list_t*)tptp_malloc( sizeof(tptp_list_t) );
	if ( cloneProc->monitors == NULL )
	{
		/* TODO: Report an internal error */
		TPTP_LOG_ERROR_MSG("ProcessControlUtil: Error - failed to alloc listener obj");
		return NULL;
	}
	tptp_list_clone(cloneProc->monitors, proc->monitors);
	return cloneProc;

}

/*
 * Used by the tptp_list utility to get a copy of a tptp_process_t node.
 */
int copyProcessNode(const tptp_node_t* node, tptp_node_t* nodeCopy)
{
	tptp_process_t* proc = (tptp_process_t*)node->data;
	nodeCopy->data = cloneProcessT(proc);
	return 0;
}


/*
 * Used by the tptp_list utility to free contents of a tptp_process_t node in a list.
 */
int destroyProcessNode(tptp_node_t* node )
{
	tptp_process_t* proc = (tptp_process_t*)node->data;
	destroyProcessT(proc);
	return 0;
}

/*
 * Free any allocated members of a tptp_process_t structure.
 */
void destroyProcessT(tptp_process_t* proc )
{
	if (proc == NULL)
		return;

	proc->ownerID = -1;
	proc->ownerContext = -1;

	if (proc->appName)
	{
		tptp_free(proc->appName);
		proc->appName = NULL;
	}

	if (proc->cmdLineArgs)
	{
		tptp_free(proc->cmdLineArgs);
		proc->cmdLineArgs = NULL;
	}

	if (proc->workingDir)
	{
		tptp_free(proc->workingDir);
		proc->workingDir = NULL;
	}

	if (proc->envVars)
	{
		tptp_free(proc->envVars);
		proc->envVars = NULL;
	}

	proc->keepProcess = 0;
	proc->noNotices = 0;

	proc->consoleConnID = 0;

	if (proc->consoleUniqueID)
	{
		tptp_free(proc->consoleUniqueID);
		proc->consoleUniqueID = NULL;
	}

	proc->pid = TPTP_INVALID_PID;
#ifdef _WIN32
	proc->hProcess = TPTP_HANDLE_NULL;
#endif

	//Free list of listeners if any.
	tptp_list_clear(proc->monitors);
	tptp_free(proc->monitors);
	proc->monitors = NULL;

	return;
}


//
// Check if appName exists and is a valid executable file.
// Returns 1 if it is successful, otherwise returns 0 and sets errCode.
//
int checkExecutable(const char* appName, unsigned long* errCode)
{
	int validApp=0;  // assume invalid	
	struct TPTP_STAT statBuf;
	mode_t modes;
	*errCode=0;
	
	// Check if file exists (not a directory) and that user has execute permission.
	if (appName)
	{
		if (TPTP_STAT(appName, &statBuf) != 0)
		{
			//perror("PCUtil");
			*errCode = TPTP_PC_EXECUTABLE_NOT_FOUND;
			TPTP_LOG_DEBUG_MSG1("PCUtil: Error - Invalid App %s", appName);
			return validApp;
		}
		modes = statBuf.st_mode;
		if (S_ISDIR(modes))
		{
			*errCode = TPTP_PC_FILE_IS_A_DIR;
			TPTP_LOG_DEBUG_MSG1("PCUtil: Error - App %s is a directory", appName);
		}
		else if ((modes & S_IXUSR) == S_IXUSR)
		{
			validApp = 1;
		}
		else
		{
			*errCode = TPTP_PC_FILE_NOT_EXECUTABLE;
			TPTP_LOG_DEBUG_MSG1("PCUtil: Error - App %s is not executable by owner", appName);
		}
	}
	else
	{
		//Null argument
		*errCode = TPTP_UNEXPECTED_NULL_ARG;
		TPTP_LOG_DEBUG_MSG("PCUtil: Error - Arg appName is a NULL value");
	}
	return validApp;
}

//
// Check if the directory path provided has read/write access.
// Returns 1 if it does, otherwise 0 is returned and errCode is set.
//
int checkDirectoryReadWriteAccess(const char* dir, unsigned long* errCode)
{
	int validDir=0;  // assume invalid
	struct TPTP_STAT statBuf;
	mode_t modes;
	*errCode=0;
	
	if (dir)
	{
		if (TPTP_STAT(dir, &statBuf) != 0)  //Does it exist?
		{
			//perror("PCUTil");
			TPTP_LOG_DEBUG_MSG1("PCUtil: Invalid dir=%s", dir);
			*errCode = TPTP_PC_DIR_NOT_FOUND;
			return validDir;
		}
		modes = statBuf.st_mode;
		if (!S_ISDIR(modes))  //Is it a directory?
		{
			*errCode = TPTP_PC_FILE_NOT_A_DIR;
			TPTP_LOG_DEBUG_MSG1("PCUtil: Error - Dir %s is not a directory", dir);
		}
		else if ((modes & S_IWUSR) == S_IWUSR)  //Does it have write permission?
			validDir = 1;
		else
		{
			*errCode = TPTP_PC_DIR_NO_WRITE_ACCESS;
			TPTP_LOG_DEBUG_MSG1("PCUtil: Error - Dir %s is not writeable by owner", dir);
		}
	}
	else
	{
		//Null argument
		*errCode = TPTP_UNEXPECTED_NULL_ARG;
		TPTP_LOG_DEBUG_MSG("PCUtil: Error - Arg dir is a NULL value");
	}

	return validDir;
}

//
// Check if the directory path provided has read access.
// Returns 1 if it does, otherwise 0 is returned and errCode is set.
//
int checkDirectoryReadAccess(const char* dir, unsigned long* errCode)
{
	int validDir=0;  // assume invalid
	struct TPTP_STAT statBuf;
	mode_t modes;
	*errCode=0;
	
	if (dir)
	{
		if (TPTP_STAT(dir, &statBuf) != 0)  //Does it exist?
		{
			//perror("PCUTil");
			TPTP_LOG_DEBUG_MSG1("PCUtil: Invalid dir=%s", dir);
			*errCode = TPTP_PC_DIR_NOT_FOUND;
			return validDir;
		}
		modes = statBuf.st_mode;
		if (!S_ISDIR(modes))  //Is it a directory?
		{
			*errCode = TPTP_PC_FILE_NOT_A_DIR;
			TPTP_LOG_DEBUG_MSG1("PCUtil: Error - Dir %s is not a directory", dir);
		}
		else if ((modes & S_IRUSR) == S_IRUSR)  //Does it have read permission?
			validDir = 1;
		else
		{
			*errCode = TPTP_PC_DIR_NO_READ_ACCESS;
			TPTP_LOG_DEBUG_MSG1("PCUtil: Error - Dir %s is not readable by owner", dir);
		}
	}
	else
	{
		//Null argument
		*errCode = TPTP_UNEXPECTED_NULL_ARG;
		TPTP_LOG_DEBUG_MSG("PCUtil: Error - Arg dir is a NULL value");
	}

	return validDir;
}

		
//
// Calls the platform specific code for terminating a process.
// Gets a TPTP error code back.
// If successful, the thread monitoring process state will learned of
// the terminated process and send any events.

int terminateProc( const tptp_process_t* proc )
{
	return ( hc_killProcess(proc->pid) );
}

// proc - allocated and passed in by caller, will be filled in with pid and process handle if 
//		  process is successfully created.
// errCode - contains system error indicating why process could not be created
// Return value: 0 if successful, else non-zero
int createProc( tptp_process_t* proc, RemoteConsole_t* console, unsigned long* errCode)
{

	TPTP_HANDLE consoleChild[3];

	TPTP_LOG_DEBUG_MSG1("createProc: Called on to create process: %s", proc->appName);

	// If given a non-null console pointer, then pass the handles to the process we are creating
	if (console)
	{
		consoleChild[0] = console->in;
		consoleChild[1] = console->out;
		consoleChild[2] = console->err;
	}
	else
	{
		consoleChild[0] = 0;
		consoleChild[1] = 0;
		consoleChild[2] = 0;
	}


	TPTP_LOG_DEBUG_MSG3("createProc: Recv'd console pipe handles:in=0x%0x, out=0x%0x, err=0x%0x",
			consoleChild[0], consoleChild[1], consoleChild[2]);

	/* Launch the process */
	proc->pid = hc_launchProcess(proc->appName, proc->cmdLineArgs, 
								 proc->workingDir, proc->envVars, consoleChild);

	if ( (proc->pid == TPTP_INVALID_PID) || (proc->pid < 0) )
	{
		TPTP_LOG_DEBUG_MSG1("createProc: Error - FAILED to create process: %s", proc->appName);
		//TODO: report specific errors, need to make hc_launchProcess return an error code
		*errCode = TPTP_PC_UNSPECIFIED_ERR;
		return -1;
	}

	// Successfully launched the application, fillin tptp_process_t struct
	// which is returned to the caller.
	TPTP_LOG_DEBUG_MSG1("createProc: SUCCESSFULLY created process: %lu", (unsigned long)proc->pid);
	*errCode = 0;

#ifdef _WIN32		
	// hc_lauchProcess() closes the process Handles,
	// get a new Handle to store in our proc struct.
	proc->hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proc->pid);
	if (proc->hProcess == 0)
	{
		TPTP_LOG_DEBUG_MSG("OpenProcess() call failed.");
		//printCurrentSysError();
		*errCode = TPTP_PC_OPEN_PROC_FAILED;
		return -1;
	}
#endif

	return 0;
}


//This routine is only used for linux but it has no system dependencies.
//The reason it is needed is that modifyApplicationEnvironment needs to
//be called multiple times and it returns a char* and not a char**.
//So the first time modify is called it is with a convertEnv(environ)
//this has a positive side effect of removing all ifdefs from the modify code.
//Note this routine does not add a NULL to the end of the enviornment (double NULL) as is the case with windows.
char * convertEnv(char **mparent)
{
	int bufferSize;
	int childOffset;
	int parentOffset;
	int currentLength;
	char *childEnv;

	// Calculate the length of the environment
	bufferSize=0;
	parentOffset=0;
	while(mparent[parentOffset]!=NULL) {
		bufferSize += strlen(mparent[parentOffset]) + 1;
		parentOffset++;
	}

	// Allocate space to copy the environment
	childEnv=(char*)tptp_malloc(bufferSize);
	if ( !childEnv ) {
		return NULL;
	}
	BZERO( childEnv, bufferSize );

	// Go back to the start of the environment passed in
	parentOffset=0;
	childOffset=0;

	// Make the copy
	while(mparent[parentOffset]!=NULL) {
		currentLength=strlen(mparent[parentOffset]);
		memcpy(&childEnv[childOffset], mparent[parentOffset], currentLength);
		childEnv[childOffset+currentLength]='\0';
		childOffset+=currentLength+1;
		parentOffset++;
	}

	return childEnv;
}

/** GET_ENVIRONMENT_STRINGS  *****************************************************
  *  Must free this space using freeEnvironmentStrings() and treat the contents
  * as read-only.
  */
void *getEnvironmentStrings() {
	void *result=NULL;
#ifdef _WIN32
	result=GetEnvironmentStrings();
	return result;
#else
	return (void *) convertEnv(environ);
#endif
}

/** FREE_ENVIRONMENT_STRINGS  ****************************************************
  * Only use for pointer allocated by getEnvironmentStrings().
  */
void freeEnvironmentStrings(void *env) {
#ifdef _WIN32
	FreeEnvironmentStrings(env);
#endif
}

#ifdef _WIN32
#define KEYCMP strnicmp
#else
#define KEYCMP memcmp
#define CloseHandle close
#endif

/** RESIZE_BUFFER  ***************************************************************
  READ
  */
static char *resizeBuffer(char *buffer, int bufferSize, int increment) {
	char *newbuffer;
	int   newsize = ((bufferSize+increment)*sizeof(char));

	newbuffer = tptp_realloc( buffer, newsize );

	if ( !newbuffer ) {
		// If the realloc failed, our old buffer is still good, but we'd end up with
		//   a partial copy of the what we need, so that doesn't really help us
		//   Instead, I'm going to free the old buffer and return NULL so we can
		//   treat this as an error.  This will only happen under extreme conditions.
		tptp_free( buffer );
		return NULL;
	}

	/* Clear the new space */
	BZERO( newbuffer+bufferSize, increment );

	return newbuffer;
}

/** Generic substring command. Given a string str, return a substring that begins at start and ends at end-1 (e.g. is exclusive of end).
    Calling class must explicitly free the returned string.*/
char *substring(const char *str, int start, int end) {
    char * dest;
    
	dest = tptp_malloc((end-start+10) * sizeof(char));
     
    strncpy(dest, str + start, end - start);

	// Zero terminate the final character (strncpy doesn't do this for us)
	dest[end - start] = 0;

	return dest;

}

/** Searches for the string str, inside the classpath classPath. Will only return true if the
str string is followed by either the platform-specific path separator, or is at the end of the string. */
int isStringInClasspath(const char *classPath, const char *str) {
	char *next;
	int matchFound = 0;
	char pathSep;
	char compchar;

#ifdef _WIN32
	pathSep = ';';
#else
	pathSep = ':';
#endif
	
	// If str has no value, our loop will never terminate. We catch this case early, here, and return 0.
	if(strlen(str) == 0) return 0;

	// For each occurrence of str in classPath....
	next = strstr(classPath, str);
	while(next != NULL) {

		// This is the character directly after the end of str in classPath
		compchar = classPath[(next-classPath) + strlen(str)];

		// Must be at the end of classPath, or must terminate with a ';' or ':' to be considered a true match
		if(compchar == '\0' || compchar == pathSep) {
			matchFound = 1;	
			break;
		}
		next += strlen(str);

		// Move to the next occurrence
		next = strstr(next, str);
	}

	return matchFound;

}

/** Is the string (str) in the given classpath (classpath), with the classpath beginning at the first character, and 
    ending at the given length*/
int isStringInClasspathRange(const char *classpath, const char *str, int length) {
	char * tempVal;
	int result = 0;

	tempVal = (char *)tptp_malloc(sizeof(char) * length + 10);
	
	memcpy(tempVal, classpath, sizeof(char) * length);
	
	tempVal[length] = 0;

	result = isStringInClasspath(tempVal, str);

	tptp_free(tempVal);
    
	return result;
}



/** Returns 1 if the envValue is already in the environment variable under envName, or 0 otherwise. */
int existsInEnv(char *envStart, int currentEnd, char *envName, char *envValue) {
	int x = 0;
	char *env = envStart;
	char *beforeEquals;
	char *afterEquals;
	char *eq;

	// Format of every string will be LABEL=VALUE
	while(x < currentEnd) {
		// For each environment variable in envStart.....

		eq = strchr(env, '=');
		
		if(eq == NULL) {
			break;
		}
		
		// The environment variable label
		beforeEquals = substring(env, 0, eq-env);

		// The environment variable value
		afterEquals = substring(env, eq-env+1, strlen(env));
			
		// If the labels match...
		if(strcmp(beforeEquals, envName) == 0) {

			// If the value we are looking for is found in the environment variable...
			if(isStringInClasspath(afterEquals, envValue)) {
				
				tptp_free(beforeEquals);
				tptp_free(afterEquals);
				return 1;

			}

		}

		tptp_free(beforeEquals);
		tptp_free(afterEquals);

		// Advance past the null terminator in the environment variable string
		x += strlen(env)+1;
		env += strlen(env)+1;

	}
	
	return 0;
}

/** MODIFY_APPLICATION_ENVIRONMENT  ********************************************
  * Creates a application environment for a process based upon the information.
  * provided in the configuration. There are two distinct implementations of this
  * functionality, one for Windows and the other for Unix.  On Windows, the
  * environment must be provided as one contiguous block of memory, wheras on
  * Unix, an array of pointers is provided.
  */
void* modifyApplicationEnvironment(tptp_list_t *variables, void *parentEnv) {
	int childOffset=0, parentOffset=0, variableCount, currentVariable;
	char *childEnv;
	char *envMask;
	const char *parent;
	const static int ENVIRONMENT_BUFFER_INCREMENT=4096;
	int bufferSize;
	//variable_list_node_t *currentVariableNode;
	tptp_node_t *currentVariableNode;

	parent=parentEnv;

	bufferSize=ENVIRONMENT_BUFFER_INCREMENT;

	/* Approximate size of environment data */
	childEnv=(void*)tptp_malloc(bufferSize);
	childEnv=(char*)childEnv;

	/* We need to count the number of variables we are going to change.  The idea
	   is we will keep a bitmask of variables we have already processed so we don't
	   add redundant information/variables
	*/
	currentVariableNode=(tptp_node_t *) variables->head; 
	//currentVariableNode=(variable_list_node_t *) variables->head; 
	for(variableCount=0; currentVariableNode; currentVariableNode=currentVariableNode->next, variableCount++);
#ifdef _HPUX
	envMask=(char *)tptp_malloc((unsigned long)variableCount);
#else
	envMask=tptp_malloc(variableCount);
#endif
	BZERO(envMask, variableCount);

	/* Process each string in turn.  Copy the parent string to the child data block
	   then loop through the configuration, making changes to child block as necessary
	   If the childEnv buffer is full we must enlarge.
	*/
#ifdef _WIN32
	while(parent[parentOffset]!='\0') {
#else
	while(parent[parentOffset]!=NULL) {
#endif
		int currentLength, originalLength;

		/* Copy the current parent to the child (excluding the null character) */

		originalLength=strlen(&parent[parentOffset]);
		currentLength=originalLength;

		if(bufferSize-childOffset <= originalLength) {
			int increment=(ENVIRONMENT_BUFFER_INCREMENT>originalLength) ? ENVIRONMENT_BUFFER_INCREMENT : originalLength+1;
			childEnv=resizeBuffer((char*)childEnv, bufferSize, increment);
			if ( childEnv == NULL ) {
				return NULL;
			}
			bufferSize+=increment;
		}
		memcpy(&childEnv[childOffset], &parent[parentOffset], originalLength);

		/* Look through the entire variable list for same entry */
		currentVariableNode=(tptp_node_t *) variables->head; 
		//currentVariableNode=(variable_list_node_t *) variables->head; 
		currentVariable=0;
		
		while(currentVariableNode != NULL) {
			variable_t* varNode = (variable_t *) currentVariableNode->data;
			int keyLength=strlen(varNode->name);
			int valueLength=strlen(varNode->value);

			/* If we already processed this entry skip it */
			if(envMask[currentVariable]) {
				currentVariableNode=currentVariableNode->next;
				currentVariable++;
				continue;
			}

			/* Is it the same enviroment variable (key matches and is followed immediately by an assignment) */
			/* ##RKD: This ignores case during comparison on Win32 */
			if(childEnv[childOffset+keyLength]=='=') {
				if(!KEYCMP(varNode->name, &parent[parentOffset], keyLength)) {
					/* Mark the bitmask as done*/
					envMask[currentVariable]=1;

					/* Update the variable */
					if(varNode->position==REPLACE) {
						if (childOffset+keyLength+1+valueLength >= bufferSize) {
							int neededSize=((childOffset+keyLength+1+valueLength)-bufferSize)+1;
							int increment=(ENVIRONMENT_BUFFER_INCREMENT>neededSize) ? ENVIRONMENT_BUFFER_INCREMENT : neededSize;
							/* Resize the buffer, retaining the data up to the assignment variable */
							childEnv=resizeBuffer((char*)childEnv, bufferSize, increment);
							if ( childEnv == NULL ) {
								return NULL;
							}
							bufferSize+=increment;
						}
						/* Copy the value into the buffer */
						memcpy(&childEnv[childOffset+keyLength+1], varNode->value, valueLength);
						/* When in replace mode the currentLength is the length of the latest overwrite */
						currentLength=keyLength+1+valueLength;
					}
					// If the variable is an APPEND, and the value being appended isn't already present in the environment variable...
					else if(varNode->position==APPEND && !isStringInClasspath(&parent[parentOffset], varNode->value) && !isStringInClasspathRange(&childEnv[childOffset], varNode->value, currentLength)) {
						/* Ensure the buffer is large enough to add the data */
						if((childOffset+currentLength+1+valueLength) >= bufferSize) {
							int neededSize=((childOffset+currentLength+1+valueLength)-bufferSize)+1;
							int increment=(ENVIRONMENT_BUFFER_INCREMENT>neededSize) ? ENVIRONMENT_BUFFER_INCREMENT : neededSize;
							/* Resize the buffer, retaining the entire current value */
							childEnv=resizeBuffer((char*)childEnv, bufferSize, increment);
							if ( childEnv == NULL ) {
								return NULL;
							}
							bufferSize+=increment;
						}
						childEnv[childOffset+currentLength]=PATH_SEPARATOR;
						memcpy(&childEnv[childOffset+currentLength+1], varNode->value, valueLength);
						currentLength+=valueLength+1;
					}
					// If the variable is a PREPEND, and the value being prepended isn't already present in the environment variable...
					else if(varNode->position==PREPEND && !isStringInClasspath(&parent[parentOffset], varNode->value) &&  !isStringInClasspathRange(&childEnv[childOffset], varNode->value, currentLength)) {
						char *temp;
						if((childOffset+keyLength+1+valueLength+1+(currentLength-keyLength-1)) >= bufferSize) {
							int neededSize=((childOffset+keyLength+1+valueLength+1+(currentLength-keyLength-1))-bufferSize)+1;
							int increment=(ENVIRONMENT_BUFFER_INCREMENT>neededSize) ? ENVIRONMENT_BUFFER_INCREMENT : neededSize;
							/* Resize the buffer, retaining the entire current value */
							childEnv=resizeBuffer((char*)childEnv, bufferSize, increment);
							if ( childEnv == NULL ) {
								return NULL;
							}
							bufferSize+=increment;
						}
						/* save the current state */
						temp=(char*)tptp_malloc((currentLength-keyLength-1)*sizeof(char));
						memcpy(temp, &childEnv[childOffset+keyLength+1], currentLength-keyLength-1);

						/* Copy in the prepended value */
						memcpy(&childEnv[childOffset+keyLength+1], varNode->value, valueLength);
						childEnv[childOffset+keyLength+1+valueLength]=PATH_SEPARATOR;
						/* Restore the saved state */
						memcpy(&childEnv[childOffset+keyLength+1+valueLength+1], temp, currentLength-keyLength-1);
						currentLength+=valueLength+1;
						tptp_free(temp);
					}
				}
			}
			/* Increment to the next element in the list */
			currentVariableNode=currentVariableNode->next;
			currentVariable++;
		} /* END while */

		/* Move to the next parent environment string */
		childEnv[childOffset+currentLength]='\0';
		childOffset+=currentLength+1;
		parentOffset+=originalLength+1;
	} /* END while*/

	/* What about the environment variables that are "new" and hense not in the original list */
	currentVariableNode=(tptp_node_t *) variables->head; 
	//currentVariableNode=(variable_list_node_t *) variables->head; 
	currentVariable=0;
	while(currentVariableNode != NULL) {
		int startOfEnvChange;
		int nextVariable;
		tptp_node_t *nextVariableNode;
		variable_t* varNode = (variable_t *) currentVariableNode->data;
		int keyLength=strlen(varNode->name);
		int valueLength=strlen(varNode->value);

		/* Skip this entry if alread processed */
		if(envMask[currentVariable]) {
			currentVariableNode=currentVariableNode->next;
			currentVariable++;
			continue;
		}

		/* Skip this entry if the value is already in the environment variable */
		if(existsInEnv(childEnv, childOffset, varNode->name, varNode->value)) {
			currentVariableNode=currentVariableNode->next;
			currentVariable++;
			continue;
		}

		/* Is there enough room in the child buffer? */
		if (childOffset+keyLength+1+valueLength >= bufferSize) {
			int neededSize=((childOffset+keyLength+1+valueLength)-bufferSize)+1;
			int increment=(ENVIRONMENT_BUFFER_INCREMENT>neededSize) ? ENVIRONMENT_BUFFER_INCREMENT : neededSize;
			childEnv=resizeBuffer((char*)childEnv, bufferSize, increment);
			if ( childEnv == NULL ) {
				return NULL;
			}
			bufferSize+=increment;
		}

		/* Copy the environment variable name and assignment character to the
		   child environment
		*/
		startOfEnvChange=childOffset;
		memcpy(&childEnv[childOffset], varNode->name, keyLength);
		childEnv[childOffset+keyLength]='=';
		childOffset+=keyLength+1;

		/* Copy this entry setting to the child environment */
		memcpy(&childEnv[childOffset], varNode->value, valueLength);
		childOffset+=valueLength;

		/* Loop through the remainder of the list looking for entries of the
		   same name
		*/
		nextVariableNode=currentVariableNode->next;
		nextVariable=currentVariable+1; /* Must add +1 here to check the next entry */
		while(nextVariableNode) {
			variable_t* nextNode = (variable_t *) nextVariableNode->data;
			int keyLength=strlen(nextNode->name);
			int valueLength=strlen(nextNode->value);

			/* Skip this entry if already processed */
			if(envMask[nextVariable]) {
				nextVariableNode=nextVariableNode->next;
				nextVariable++;
				continue;
			}

			/* Skip this entry if the value is already in the environment variable */
			if(existsInEnv(childEnv, childOffset, nextNode->name, nextNode->value)) {
				nextVariableNode=nextVariableNode->next;
				nextVariable++;
				continue;
			}

			/* Is this the same entry */
			if(!KEYCMP(varNode->name, nextNode->name, keyLength)) {

				// Kevin only same name if the length is the same.
				if (strlen(varNode->name) == strlen(nextNode->name)) { 

					/* Is there enough room in the child buffer? */
					if(childOffset+1+valueLength >= bufferSize) {
						int neededSize=((childOffset+valueLength)-bufferSize)+1;
						int increment=(ENVIRONMENT_BUFFER_INCREMENT>neededSize) ? ENVIRONMENT_BUFFER_INCREMENT : neededSize;
						childEnv=resizeBuffer((char*)childEnv, bufferSize, increment);
						if ( childEnv == NULL ) {
							return NULL;
						}
						bufferSize+=increment;
					}

					/* Add the separator and the new data */
					childEnv[childOffset++]=PATH_SEPARATOR;
					memcpy(&childEnv[childOffset], nextNode->value, valueLength);
					childOffset+=valueLength;

					/* Append a zero to allow our existsInEnv calls to work. This value will be overwritten by the next pass through (if applicable). */
					childEnv[childOffset]='\0';

					/* Mark this entry as complete */
					envMask[nextVariable]=1;
				}
			}

			nextVariableNode=nextVariableNode->next;
			nextVariable++;
		}

		/* Add the null character and mark this entry as complete */
		childEnv[childOffset]='\0';
		childOffset+=1;
		envMask[currentVariable]=1;

		/* Contine with the next entry in the list */
		currentVariableNode=currentVariableNode->next;
		currentVariable++;
	}

	/* We were hitting an unfortunate case where there wasn't room for this last NULL 
	 *    because we got here with the buffer exactly full.  This code checks for that.
	 */
	if ( childOffset == bufferSize )
	{
		char *temp=childEnv;
		childEnv = (char*)tptp_realloc(childEnv, bufferSize+1);
		if ( childEnv == NULL )
		{
			/* Better to truncate the last byte of the environment than to return empty-handed now */
			childEnv = temp;
			childEnv[childOffset-2]='\0';
			childEnv[childOffset-1]='\0';
		}
		else
		{
			bufferSize+=1;
			childEnv = childEnv;
			childEnv[childOffset]='\0';
		}
	}
	else
	{
		/* The second null termination */
		childEnv[childOffset]='\0';
	}

	/* Clean up our mess */
	tptp_free(envMask);
	//freeEnvironmentStrings(parentEnv);
	return childEnv;
}

/* Minimal offset is the offset past the executable...ie arg[0] */
int computeMinimalOffset(tptp_string *args)
{
	char match;
	unsigned int i;
	BOOL inQuotes = FALSE;
	BOOL nonSpaceFound = FALSE;

	match = ' ';
	for (i=0; i < strlen(args); i++)
	{
		/* If we hit a quotation mark, toggle our quotation status */
		if (args[i] == '"') {
			inQuotes = !inQuotes;
		}

		/* Ignore leading spaces */
		if ( nonSpaceFound )  {
			/* Look for the first space outside of quotes */
			if ((args[i] == match) && !inQuotes) {
				return i+1;
			}
		} else {
			if (args[i] != match) {
				nonSpaceFound = TRUE;
			}

		}
	}

	if ( nonSpaceFound ) {
		return strlen(args)+1; 
	} else {
		return 1;
	}
}


/** MODIFY_APPLICATION_PARAMETERS  ********************************************************
  *
  */
char* modifyApplicationParameters(tptp_list_t *parameters, tptp_string *args) {
	char *commandLine;
	unsigned long offset, minimalOffset, bufferSize;
	tptp_node_t *currentParameterNode;
	//I am changing this buffer increment from 128 to 1024.
	//This was to fix a heap corruption problem on windows when we used 
	//the non-debug version of the malloc libraries.
	const unsigned long BUFFER_INCREMENT=1024;

	/* If there are arguments increment the buffer by the args length plus a null character.
	   Otherwise just account for the null
	*/
	if(args) {
		minimalOffset=computeMinimalOffset(args);
		bufferSize = strlen(args)+1;
	}
	else {
		bufferSize = 1;
		minimalOffset=1;
	}

	if(bufferSize<BUFFER_INCREMENT) {
		bufferSize=BUFFER_INCREMENT;
	}

	/* Create the command line buffer */
	commandLine=resizeBuffer(NULL, 0, bufferSize);

	if(args) {
		memcpy(&commandLine[0], args, strlen(args));
		offset=strlen(args); 
	} else {
		offset = 0;
	}

	/* Create the command line arguments */
	currentParameterNode=(tptp_node_t *) parameters->head;
	while(currentParameterNode != NULL) {
	    parameter_t *parNode = (parameter_t *) currentParameterNode->data;
		/* Is there enough room for the next parameter */
	    int length=strlen(parNode->value);
		if (length+offset+2 > bufferSize) {
			unsigned long neededSize = ((length+offset+2)-bufferSize)+1;
			unsigned long increment=(BUFFER_INCREMENT>neededSize) ? BUFFER_INCREMENT : neededSize;
			commandLine=resizeBuffer(commandLine, bufferSize, increment);
			if (commandLine==NULL) {
				return NULL;
			}
			bufferSize+=increment;
		}
		if(parNode->position==REPLACE) {
			memcpy(&commandLine[minimalOffset], parNode->value, length);
			offset=minimalOffset+length;
		}
		else if(parNode->position==APPEND) {
			commandLine[offset++]=' ';
			memcpy(&commandLine[offset], parNode->value, length);
			offset+=length;
		}
		else if(parNode->position==PREPEND) {
			/* Temporary buffer for the existing commandLine data */
			char *temp=(char*)tptp_malloc((offset-minimalOffset)*sizeof(char));
			memcpy(temp, &commandLine[minimalOffset], offset-minimalOffset);

			/* Insert the new parameter */
			memcpy(&commandLine[minimalOffset], parNode->value, length);

			/* Copy the old data in, following a space */
			commandLine[minimalOffset+length]=' ';
			memcpy(&commandLine[minimalOffset+length+1], temp, offset-minimalOffset);
			offset+=length+1;

			tptp_free(temp);
			temp=NULL;
		}

		currentParameterNode=currentParameterNode->next;
	}

	/* Set the null terminator */
	commandLine[offset]='\0';
	return commandLine;
}
