#include "global.h"
#include "ctype.h"
#include "commands.h"
#include "mbuf.h"
#include "proc.h"
#include "assoc.h"
#include "socket.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: script.c,v 1.14 1997/07/31 00:44:20 root Exp $";
#endif

#ifdef SCRIPTING
static AarrayPtr Aliases = NULLASSOC;
AarrayPtr SetVariables = NULLASSOC;

extern struct cmds Cmds[];
extern const char hitEnter[];
extern int MAttended;

#ifdef ALLSERV
extern int Attended;
#endif

static void _adjust (char *label, int offset);
void variable_expansion (struct mbuf ** bp);

static int _ifdef (int argc, char *argv[], int notflag);
static int _ifequal (int argc, char *argv[], int notflag);
static int doifequal (int argc, char *argv[], void *p);
static int doifnequal (int argc, char *argv[], void *p);
static int doifless (int argc, char *argv[], void *p);
static int doifgreat (int argc, char *argv[], void *p);
static int doiflessequal (int argc, char *argv[], void *p);
static int doifgreatequal (int argc, char *argv[], void *p);
static int _ifstrequal (int argc, char *argv[], int notflag);
static int doifstrequal (int argc, char *argv[], void *p);
static int doifnstrequal (int argc, char *argv[], void *p);

#define _EQUAL		0
#define _NEQUAL		1
#define _LESS		2
#define _GREAT		3
#define _LESSEQUAL	4
#define _GREATEQUAL	5


int
dogone (argc, argv, p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
#ifdef ALLSERV
	Attended = FALSE;
#endif
#ifdef MAILBOX
	MAttended = FALSE;
#endif
	return 0;
}


int
dohere (argc, argv, p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
#ifdef ALLSERV
	Attended = TRUE;
#endif
#ifdef MAILBOX
	MAttended = TRUE;
#endif
	return 0;
}


int
dosleep (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
int32 len = 0;

	if (argc > 1)
		len = atol (argv[1]);
	if (argc < 3 || tolower (*argv[2]) != 'm')
		len = (!len) ? 1000 : (len * 1000);
	(void) kpause (len);
	return 0;
}


int
dopause (argc, argv, p)
int argc;
char *argv[];
void *p;
{
struct mbuf *bp;

	if (argc == 2)		/* if a param given, use as a minimum delay */
		(void) dosleep (argc, argv, p);

	tputs (hitEnter);
	tflush ();
	(void) recv_mbuf (Curproc->input, &bp, 0, NULLCHAR, 0);
	return 0;
}


int
doecho (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
int k;
int docr = 1;

	for (k = 1; k < argc; k++) {
		if (k == 1 && !strcmp (argv[k], "-n")) {
			docr = 0;
			continue;
		}
		if (!strncmp (argv[k], "$(", 2) && argv[k][strlen (argv[k]) - 1] == ')') {	/* command substitution */
			argv[k][strlen (argv[k]) - 1] = 0;
			(void) cmdparse (Cmds, &argv[k][2], NULL);
		} else {
			tputs (argv[k]);
			tputc (' ');
		}
	}
	if (docr)
		tputc ('\n');
	tflush ();
	return 0;
}


int
doalias (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	if (argc < 3)
		assoc_list (Aliases, 15, (argc == 1) ? NULLCHAR : argv[1]);
	else
		(void) assoc_addstr (&Aliases, argv[1], argv[2], 1);

	return 0;
}


int
dounalias (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p OPTIONAL;
{
	(void) assoc_free (&Aliases, argv[1]);
	return 0;
}


int
doset (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	if (argc < 3)
		assoc_list (SetVariables, 15, (argc == 1) ? NULLCHAR : argv[1]);
	else
		(void) assoc_addstr (&SetVariables, argv[1], argv[2], 1);

	return 0;
}


int
dounset (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p OPTIONAL;
{
	(void) assoc_free (&SetVariables, argv[1]);
	return 0;
}


static void
_adjust (label, offset)
char *label;
int offset;
{
char *valstr;
int value = 0;
char buf[24];

	if ((valstr = (char *)assoc_lookup (SetVariables, label)) != NULLCHAR)
		value = atoi (valstr);

	value += offset;
	sprintf (buf, "%-d", value);
	(void) assoc_addstr (&SetVariables, label, buf, 1);
}


int
doincr (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
int offset = 1;

	if (argc > 2)
		offset = atoi (argv[2]);
	_adjust (argv[1], offset);
	return 0;
}


int
dodecr (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
int offset = -1;

	if (argc > 2)
		offset = (atoi (argv[2]) * -1);
	_adjust (argv[1], offset);
	return 0;
}


int
dogoto (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	if (argc == 1) {
		if (Curproc->gotolabel)
			tprintf ("goto: %s\n", Curproc->gotolabel);
	} else {
		if (Curproc->gotolabel)
			free (Curproc->gotolabel);
		Curproc->gotolabel = strdup (argv[1]);
#if 0
		tprintf ("Skipping command till label '%s'\n", Curproc->gotolabel);
#endif
	}
	return 0;
}


int
dolabel (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	if (argc == 1) {
		if (!Curproc->gotolabel)
			return 0;
		tprintf ("label: goto '%s' cleared\n", Curproc->gotolabel);
		goto clear;
	}
	if (Curproc->gotolabel && !strcmp (Curproc->gotolabel, argv[1])) {
clear:		free (Curproc->gotolabel);
		Curproc->gotolabel = NULLCHAR;
	}
	return 0;
}


int
doforeach (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p;
{
char *lpt = argv[2];
char *next, *cp;

	while (*lpt) {
		next = skipnonwhite (lpt);
		if (*next) {
			*next++ = 0;
			next = skipwhite (next);
		}
		(void) assoc_addstr (&SetVariables, argv[1], lpt, 1);
		cp = _variable_expansion (strdup (argv[3]));
		(void) cmdparse (Cmds, cp, p);
		lpt = next;
	}
	return 0;
}


/* if subcommand table */
static struct cmds IFtab[] =
{
	{ "==",		doifequal,	0, 4, "if|while|until string == string2 {cmd}"  },
	{ ">",		doifgreat,	0, 4, "if|while|until string > string2 {cmd}"   },
	{ ">=",		doifgreatequal,	0, 4, "if|while|until string >= string2 {cmd}"  },
	{ "<",		doifless,	0, 4, "if|while|until string < string2 {cmd}"   },
	{ "<=",		doiflessequal,	0, 4, "if|while|until string <= string2 {cmd}"  },
	{ "!=",		doifnequal,	0, 4, "if|while|until string != string2 {cmd}"  },
	{ "eq",		doifstrequal,	0, 4, "if|while|until string eq string2 {cmd}"  },
	{ "!eq",	doifnstrequal,	0, 4, "if|while|until string !eq string2 {cmd}" },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR}
};


int
doif (argc, argv, p)
int argc;
char *argv[];
void *p;
{
int retval, swapped = 0;
char *cp;

	if (argc >= 3) {
		cp = argv[1];
		argv[1] = argv[2];
		argv[2] = cp;
		swapped = 1;
	}
	if ((retval = subcmd (IFtab, argc, argv, p)) > 0)	{
		if (!Curproc->condfalse) {
			cp = _variable_expansion (strdup (argv[retval]));
			retval = cmdparse (Cmds, cp, NULL);
			free (cp);
		} else
			retval = 0;
	}
	if (swapped) {
		cp = argv[1];
		argv[1] = argv[2];
		argv[2] = cp;
	}
	return retval;
}


int
doelse (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p OPTIONAL;
{
int retval = 0;
char *cp;

	if (Curproc->condfalse) {
		cp = _variable_expansion (strdup (argv[1]));
		retval = cmdparse (Cmds, cp, NULL);
		free (cp);
	}
	return retval;
}


int
doifdef (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifdef (argc, argv, 0));
}


int
doifndef (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifdef (argc, argv, 1));
}


int
_ifdef (argc, argv, notflag)
int argc OPTIONAL;
char *argv[];
int notflag;
{
AarrayPtr aa;
char *cp;
int retval = 0;

	cp = _variable_expansion (strdup (argv[1]));
	aa = assoc_find (SetVariables, cp);
	free (cp);
	Curproc->condfalse = ((notflag && aa == NULLASSOC) || (!notflag && aa != NULLASSOC)) ? 0 : 1;
	if (!Curproc->condfalse) {
		cp = _variable_expansion (strdup (argv[2]));
		retval = cmdparse (Cmds, cp, NULL);
		free (cp);
	}
	return retval;
}


int
doifstrequal (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifstrequal (argc, argv, 0));
}


int
doifnstrequal (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifstrequal (argc, argv, 1));
}


int
_ifstrequal (argc, argv, notflag)
int argc OPTIONAL;
char *argv[];
int notflag;
{
int cmp;
char *cp1, *cp2;

	cp1 = _variable_expansion (strdup (argv[1]));
	cp2 = _variable_expansion (strdup (argv[2]));
	cmp = strcmp (cp1, cp2);
	free (cp1);
	free (cp2);
	Curproc->condfalse = ((notflag && cmp) || (!notflag && !cmp)) ? 0 : 1;
	return 4;
}


int
doifequal (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifequal (argc, argv, _EQUAL));
}


int
doifnequal (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifequal (argc, argv, _NEQUAL));
}


int
doifless (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifequal (argc, argv, _LESS));
}


int
doifgreat (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifequal (argc, argv, _GREAT));
}


int
doiflessequal (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifequal (argc, argv, _LESSEQUAL));
}


int
doifgreatequal (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (_ifequal (argc, argv, _GREATEQUAL));
}


int
_ifequal (argc, argv, notflag)
int argc OPTIONAL;
char *argv[];
int notflag;
{
int val1, val2;
char *cp1, *cp2;

	cp1 = _variable_expansion (strdup (argv[1]));
	cp2 = _variable_expansion (strdup (argv[2]));
	val1 = atoi (cp1);
	val2 = atoi (cp2);
	free (cp1);
	free (cp2);
	if (notflag == _EQUAL)
		Curproc->condfalse = (val1 == val2) ? 0 : 1;
	else if (notflag == _NEQUAL)
		Curproc->condfalse = (val1 != val2) ? 0 : 1;
	else if (notflag == _LESS)
		Curproc->condfalse = (val1 < val2) ? 0 : 1;
	else if (notflag == _GREAT)
		Curproc->condfalse = (val1 > val2) ? 0 : 1;
	else if (notflag == _LESSEQUAL)
		Curproc->condfalse = (val1 <= val2) ? 0 : 1;
	else if (notflag == _GREATEQUAL)
		Curproc->condfalse = (val1 >= val2) ? 0 : 1;
	else
		Curproc->condfalse = 1;
	return 4;
}


int
dowhile (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
int loop = 1;
int retval;

	if (argc == 1 || (argc == 2 && argv[1][0] == '?'))
		loop = 0;
	do {
		retval = doif (argc, argv, p);
	} while (!retval && loop && !Curproc->condfalse);
	Curproc->condfalse = 0;
	return 0;
}


int
dountil (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
int loop = 1;
int retval;
char *cp;

	if (argc >= 3) {
		cp = argv[1];
		argv[1] = argv[2];
		argv[2] = cp;
	}
	if (argc == 1 || (argc == 2 && argv[1][0] == '?'))
		loop = 0;
	do {
		if ((retval = subcmd (IFtab, argc, argv, p)) > 0)
			if (Curproc->condfalse) {
				cp = _variable_expansion (strdup (argv[retval]));
				retval = cmdparse (Cmds, cp, NULL);
				free (cp);
			}
	} while (!retval && loop && Curproc->condfalse);
	Curproc->condfalse = 0;
	return 0;
}


#endif /* SCRIPTING */


void
variable_expansion (bp)
struct mbuf **bp;
{
#ifdef SCRIPTING
char *new;

	new = mallocw ((*bp)->cnt);
	(void) pullup (bp, (unsigned char *) new, (*bp)->cnt);
	new = _variable_expansion (new);
	if (strlen (new)) {
		*bp = pushdown (*bp, (int16)(strlen (new) + 1));
		strcpy ((char *) (*bp)->data, new);
	} else {
		*bp = pushdown (*bp, 1);
		(*bp)->data[0] = 0;
	}
	free (new);
#endif
}


char *
_variable_expansion (input)
char *input;
{
#ifdef SCRIPTING
char *cp, *cp2, *cp3, old, *alias, *new;
int offset = 0, size;
int inbrace = 0;

	/* analyze input, and if necessary, replace it with a new one */
	rip (input);
	cp = skipnonwhite (input);
	old = *cp;
	cp2 = cp;
	if (old) {
		*cp = 0;
		if (old != '\n')
			cp2++;
	}
	if ((alias = (char *) assoc_lookup (Aliases, input)) != NULLCHAR) {
		new = mallocw (strlen (alias) + strlen (cp2) + 2);
		sprintf (new, "%s%c%s", alias, old, cp2);
		free (input);
		input = new;
	} else
		*cp = old;

#if 1
	/* now that alias expansion is complete, now we go for variable substitution */
	for (offset = 0; offset < (int) strlen (input); offset++) {
		cp = &input[offset];
		switch (*cp) {
			case '{':
				inbrace++;
				break;
			case '}':
				if (inbrace)
					inbrace--;
				break;
			case '$':
				if (inbrace)
					break;
				*cp = 0;
				cp2 = (cp + 1);
				switch (*cp2) {
					case '$':
						alias = cp;
						old = *cp2++;
						break;
					case '{':
						cp3 = strchr (cp2 + 1, '}');
						if (cp3) {
							alias = ++cp2;
							*cp3++ = 0;
							old = *cp3;
							cp2 = ++cp3;
						} else
							alias = cp;
						break;
					default:
						cp3 = strpbrk (cp2, "; \t\n");
						if (cp3 == NULLCHAR)
							cp3 = &cp2[strlen (cp2)];
						old = *cp3;
						if (old)
							*cp3++ = 0;
						alias = cp2;
						cp2 = cp3;
				}
				if (*alias)
					alias = (char *) assoc_lookup (SetVariables, alias);
				size = (int) ((alias) ? strlen (alias) : 0);
				new = mallocw (strlen (input) + (unsigned) size + strlen (cp2) + 2);
				sprintf (new, "%s%s%c%s", input, (alias) ? alias : "", old, cp2);
				free (input);
				input = new;
				break;
			default:
				break;
		}
	}
#endif
#endif /* SCRIPTING */
	return input;
}
