/*
 * run.c --- stubs for unused cvs functions in 'src/run.c'
 *
 * MDLadwig <mike@twinpeaks.prc.com> --- Nov 1995
 * Modified : Alexandre Parenteau <aubonbeurre@hotmail.com> --- December 1997
 */

#include "config.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <MacErrors.h>

#include "GUSIlsh.h"

#ifdef MAC_SHL
#	include <Folders.h>
#	include <errno.h>
#	include "dll_loader.h"
#	include "cvs.h"
#	include "VolsPaths.h"
#	include "FSpCompat.h"
#	include "lsh_dll.h"
#endif /* MAC_SHL */

#define CLOSE_ON_EXEC_MAX 10
static int _close_on_exec_num = 0;
static int _close_on_exec_list[CLOSE_ON_EXEC_MAX];

void close_on_exec (int s)
{
	if(_close_on_exec_num < CLOSE_ON_EXEC_MAX)
		_close_on_exec_list[_close_on_exec_num++] = s;
}

void macos_cleanup(void)
{
	int i;
	for(i = 0; i < _close_on_exec_num; i++)
		close(_close_on_exec_list[i]);
	GUSIwithoutLSHSockets();
	
}

void macos_initialize(int *argc, char ***argv)
{
	GUSIwithLSHSockets();
}

void run_arg (const char *s) { }
void run_print (FILE * fp) { }
void run_setup (const char *fmt/*,...*/) { }
void run_args (const char *fmt/*,...*/) { }
int run_exec (const char *stin, const char *stout, const char *sterr, int flags) { return 0; }
int piped_child (char **, int *, int *) { return 0; }
int filter_stream_through_program (int, int, char **, pid_t *) { return 0; }
void *alloca (size_t size) {return malloc(size);} /* MacCVS garbages allocations anyway */

char *getpass (const char *prompt)
{
#ifndef MAC_SHL
	return NULL;
#else /* MAC_SHL */
	char *value = (char *)xmalloc(strlen(prompt) + strlen("CVS_GETPASS") + 2);
	char *res;
	strcpy(value, "CVS_GETPASS");
	strcat(value, "=");
	strcat(value, prompt);
	res = getenv(value); /* let MacCvs get the password */
	free(value);
	return res;
#endif /* MAC_SHL */
}

#ifdef MAC_SHL
static FILE *gRunOut = 0L;

static long consoleout(char *txt, long len)
{
	FILE *stream = gRunOut != 0L ? gRunOut : stdout;
	
	int tmp = len;
	while(tmp--)
	{
		char c = *txt++;
		if(c == 0x0d)
			c = 0x0a;
		putc(c, stream);
	}
	return len;
}

static long consoleerr(char *txt, long len)
{
	int tmp = len;
	while(tmp--)
	{
		char c = *txt++;
		if(c == 0x0a)
			c = 0x0a;
		putc(c, stderr);
	}
	return len;
}

static long consolein(char *txt, long len)
{
	int tmp = len;
	int c;
	
	while((c = getc(stdin)) != EOF && tmp > 0)
	{
		*txt++ = (char)c;
		tmp--;
	}
	return len - tmp;
}

static int myeventdispatcher(EventRecord *ev)
{
	return dllglue_dispatchevent(ev);
}

static char *mygetenv(char *name)
{
	return getenv(name);
}

static int loadCVS(CompatConnectID * connID)
{
	int result = 0;
	const char *name_dll;
	
	/* get cvs shared library */
#if TARGET_API_MAC_CARBON
#if qCvsDebug
	name_dll = "cvsXDbgLib";
#else
	name_dll = "cvsXLib";
#endif
#else
#if qCvsDebug
	name_dll = "cvs2DbgLib";
#else
	name_dll = "cvs2Lib";
#endif
#endif

	/* open the library */
	if(!CompatLoadLibrary(connID, name_dll))
	{
		fprintf(stderr, "Error: Unable to load the library %s\n", name_dll);
		return false;
	}

	/* set the console out */
	if(!CompatCallLibrary1(*connID, result, uppSetGlueProcInfo, "dllglue_setconsoleout",
		dllglue_setglue_func, CompatFuncArg(uppConsoleOutProcInfo, consoleout)))
	{
		fprintf(stderr, "Error : Cannot find symbol ""dllglue_setconsoleout""\n");
		CompatCloseLibrary(connID);
		return false;
	}

	/* set the console err */
	if(!CompatCallLibrary1(*connID, result, uppSetGlueProcInfo, "dllglue_setconsoleerr",
		dllglue_setglue_func, CompatFuncArg(uppConsoleOutProcInfo, consoleerr)))
	{
		fprintf(stderr, "Error : Cannot find symbol ""dllglue_setconsoleerr""\n");
		CompatCloseLibrary(connID);
		return false;
	}

	/* set the console in */
	if(!CompatCallLibrary1(*connID, result, uppSetGlueProcInfo, "dllglue_setconsolein",
		dllglue_setglue_func, CompatFuncArg(uppConsoleInProcInfo, consolein)))
	{
		fprintf(stderr, "Error : Cannot find symbol ""dllglue_setconsolein""\n");
		CompatCloseLibrary(connID);
		return false;
	}

	/* set the getenv */
	if(!CompatCallLibrary1(*connID, result, uppSetGlueProcInfo, "dllglue_setgetenv",
		dllglue_setglue_func, CompatFuncArg(uppGetEnvProcInfo, mygetenv)))
	{
		fprintf(stderr, "Error : Cannot find symbol ""dllglue_setgetenv""\n");
		CompatCloseLibrary(connID);
		return false;
	}

	/* set the event dispatcher */
	if(!CompatCallLibrary1(*connID, result, uppSetGlueProcInfo, "dllglue_seteventdispatch",
		dllglue_setglue_func, CompatFuncArg(uppEventDispatchProcInfo, myeventdispatcher)))
	{
		fprintf(stderr, "Error : Cannot find symbol ""dllglue_seteventdispatch""\n");
		CompatCloseLibrary(connID);
		return false;
	}

	return true;
}

static void unloadCVS(CompatConnectID connID)
{	
	CompatCloseLibrary(&connID);
}

static int launchCVS(const char *path, int argc, char **argv)
{
	CompatConnectID connID;
	int exitc = 0;
	
	if(!loadCVS(&connID))
		return 1;
	
	/* call the main */
	if(!CompatCallLibrary3(connID, exitc, uppMainProcInfo, "dllglue_main",
		dllglue_main_func, path, argc, argv))
	{
		fprintf(stderr, "Error : Cannot find symbol ""dllglue_main""\n");
		exitc = 1;
	}

	unloadCVS(connID);
	
	return exitc;
}

static char **split_cmdline(const char *cmd, int *resargc)
{
#	define MAX_ARGS 40
#	define MAX_CMD_LEN 2048

	char *argv[MAX_ARGS];
	int argc = 0, i;
	char **result;
	
	char	Quote = '\0';				/*	Contains the quote character if we are 
											inside "" or '', or \0 otherwise */
	const char*	walker1 = cmd;				/*	walker1 traverses the original
											command line */
	char*	walker2;					/*	walker2 traverses the parsed copy of
											the command line */
	char	theChar;					/*	theChar is the character we are currently
											parsing */
	char	arguments [MAX_CMD_LEN];			/*	will contain a parsed copy of the
											command line */
	if(cmd == 0L || cmd[0] == '\0')
		return 0L;
	
	walker2 = arguments;										/*	copy into arguments */
	while ((theChar = *walker1++) != '\0') {					/*	parse command line to \0 */
		if (theChar == ' ')										/*	skip leading space before an argument */
			continue;		
		argv [argc++] = walker2;								/*	set argv to point to the space
																	where we copy the next argument */
		if (argc > MAX_ARGS) {									/*	if we filled argv, skip the rest */
			argc--;
			break;
		}
		do {													/*	parse an argument */
			if (theChar == '\\' && *walker1++)					/*	skip \s */
				theChar = *walker1++;
			else
				if ((theChar == '"') || (theChar == '\'')) {	/*	when we find a quote... */
					if (Quote == '\0') {						/*	if we are not inside quotes already, */
						Quote = theChar;						/*	remember we are inside quotes now */
						continue;								/*	go to the next character */
					}
					if (theChar == Quote) {						/*	if we are already inside that kind of quotes */
						Quote = '\0';							/*	close quotes */
						continue;								/*	go to the next character */
					}
				}
			*walker2++ = theChar;								/*	copy the character into args */
																/*	repeat until we reach the end of arguments
																	or we reach a space that is not inside quotes */
		} while ((*walker1 != '\0') && (((theChar = *walker1++) != ' ') || (Quote != '\0')));
		*walker2++ = 0;											/* null-terminate the last argument */
	}

	*resargc = argc;
	result = (char **)xmalloc(argc * sizeof(char *));
	for (i = 0; i < argc; i++)
		result[i] = xstrdup(argv[i]);

	return result;
}

static OSErr
MacGetPrefsFolder(FSSpec * theFolder, Str255 thePath)
{
	short foundVRefNum;
	long foundDirID;
	OSErr err;
	char theCPath[256];

	err = FindFolder(kOnSystemDisk, kPreferencesFolderType,
		kDontCreateFolder, &foundVRefNum, &foundDirID);
	if (err != noErr)
	{
		fprintf(stderr, "MacCvs: Unable to find the preferences folder (error %d)\n", err);
		return err;
	}
		
	if(PathNameFromDirID(foundDirID, foundVRefNum, theCPath) != noErr)
	{
		fprintf(stderr, "MacCvs: Unable to convert path name\n");
		return fnfErr;
	}
	c2pstrcpy(thePath, theCPath);

	err = FSMakeFSSpecCompat(foundVRefNum, foundDirID, "\p", theFolder);
	if (err != noErr)
	{
		fprintf(stderr, "MacCvs: Unable to find the preferences folder (error %d)\n", err);
		return err;
	}
	return err;
}
#endif /* MAC_SHL */

FILE * run_popen(const char *cmd, const char *perm)
{
#ifndef MAC_SHL
	return NULL;
#else /* MAC_SHL */
	char **argv = 0L;
	int argc, i;
	const char *tmp;
	FSSpec theFolder;
	Str255 thePath;
	FILE *result = 0L;
	char cur_wd[512];
	
	getcwd(cur_wd, 512);
	
	/* resign if it is not something we know about */
	if(cmd == 0L || strncmp(cmd, program_path, strlen(program_path)) != 0)
		goto abort;
	
	if(MacGetPrefsFolder(&theFolder, thePath) != noErr)
		goto abort;
	
	p2cstrcpy((char *)thePath);
	if(thePath[strlen((char *)thePath) - 1] != ':')
		strcat((char *)thePath, ":");
	strcat((char *)thePath, "__maccvsTmpPopen");
	gRunOut = fopen((char *)thePath, "w");
	if(gRunOut == 0L)
	{
		fprintf(stderr, "Impossible to open '%s' (error %d)\n", "__maccvsTmpPopen", errno);
		goto abort;
	}
	
	/* split the command line */
	argv = split_cmdline(cmd, &argc);
	if(argv == 0L)
		goto abort;
	
	launchCVS(cur_wd, argc, argv);
	
	fclose(gRunOut);
	gRunOut = 0L;
	gRunOut = fopen((char *)thePath, "r");
	if(gRunOut == 0L)
	{
		fprintf(stderr, "Impossible to open '%s' (error %d)\n", "__maccvsTmpPopen", errno);
		goto abort;
	}
	
	result = gRunOut;
	gRunOut = 0L;
	
abort:
	if(gRunOut != 0L)
	{
		fclose(gRunOut);
		gRunOut = 0L;
	}
	if(argv != 0L)
	{
		for(i = 0; i < argc; i++)
		{
			free(argv[i]);
		}
		free(argv);
	}

	return result;
#endif /* MAC_SHL */
}

#ifndef MAC_SHL
int pclose(FILE *fp) {return 0;}
#else /* MAC_SHL */
int run_pclose(FILE *fp)
{
	return fclose(fp);
}
#endif /* MAC_SHL */
