/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ifdef CACA
#define _DEBUG2	1
#endif
/*
 * Copyright  (c) 1986 AT&T
 *	All Rights Reserved
 */

#ident	"@(#)evfuncs.c	1.6	93/12/26 SMI"       /* SVr4.0 1.32 */

#define	MAX_IF_DEPTH	256	/* maximum number of nested if's */

/*
 * Possible "if" states
 */
#define	IN_A_CONDITION	1	/* we are evaluating if */
#define	IN_A_THEN	2	/* we are in the then portion */
#define	IN_AN_ELSE	4	/* we are in the else portion */
#define	IN_AN_ELIF_SKIP	8	/* we are in the elif (ignore conditional) */
#define	IF_IS_TRUE	16	/* the condition is true */

/*
 * Miscellaneous macros to set/test various "if/then/else" states
 */
#define ANY_IF_STATE 		(IN_A_CONDITION | \
				 IN_A_THEN | \
				 IN_AN_ELSE | \
				 IN_AN_ELIF_SKIP)

int	in_an_if = 0;			/* keeps track of if depth */
char	status_of_if[MAX_IF_DEPTH];	/* status and pos. of each if */

#include	<stdio.h>
#include	<fcntl.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<errno.h>
#include	<signal.h>
#include	<termio.h>
#include	"wish.h"
#include	"eval.h"
#include	"ctl.h"
#include	"moremacros.h"
#include 	"message.h"
#include  	"interrupt.h"
#include	"retcodes.h"	/* abs */
#include	"sizes.h"

extern void exit();		/* fmli's own exit routine */

/*
 * return value of lastly executed command within an "if" statement
 */ 
static int Lastret = SUCCESS;


#ifdef TEST
main(argc, argv)
char	*argv[];
{
	IOSTRUCT	*in, *out, *err;

	wish_init(argc, argv);
	in = io_open(EV_USE_FP, stdin);
	out = io_open(EV_USE_FP, stdout);
	err = io_open(EV_USE_FP, stderr);
	exit(evalargv(argc - 1, argv + 1, in, out, err));
}

mess_temp(s)
char	*s;
{
	fprintf(stderr, "%s\n", s);
}

mess_perm(s)
char	*s;
{
	fprintf(stderr, "%s\n", s);
}

#endif 

int	cmd_if();
int	cmd_elif();

cmd_fi(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
	if (in_an_if <= 0) {
		mess_temp("Syntax error - \"fi\" with no pending \"if\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}
	if (((status_of_if[in_an_if] & IN_A_THEN) == 0) && 
	    ((status_of_if[in_an_if] & IN_AN_ELSE) == 0) && 
	    ((status_of_if[in_an_if] & IN_AN_ELIF_SKIP) == 0)) { 
		mess_temp("Syntax error - \"fi\" with no pending \"then\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	in_an_if--;

	if (argc > 1) {
		mess_temp("Syntax error - missing semi-colon after \"fi\" statement.");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	return(Lastret);
}

cmd_then(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
	if (in_an_if <= 0) {
		mess_temp("Syntax error - \"then\" with no pending \"if\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	if (status_of_if[in_an_if] & IN_A_THEN) {
		mess_temp("Syntax error - \"then\" within \"then\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	if (status_of_if[in_an_if] & IN_AN_ELSE) {
		mess_temp("Syntax error - \"then\" within \"else\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	if (((status_of_if[in_an_if] & IN_A_CONDITION) == 0) &&
	    ((status_of_if[in_an_if] & IN_AN_ELIF_SKIP) == 0)) {
		mess_temp("Syntax error - \"then\" without a preceeding \"if\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	/*
	 * If we are in an "elif" but are NOT evaluating its condition
	 * statement(s) (i.e., a previous "if" condition already evauated
	 * to true) ... then just return SUCCESS;
	 */
	if (status_of_if[in_an_if] & IN_AN_ELIF_SKIP)
		return SUCCESS;

	status_of_if[in_an_if] &= ~(ANY_IF_STATE);
	status_of_if[in_an_if] &= ~(IN_A_CONDITION);
	status_of_if[in_an_if] |= IN_A_THEN;

	return SUCCESS;
}

cmd_else(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
	if (in_an_if <= 0) {
		mess_temp("Syntax error - \"else\" with no pending \"if\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	if (status_of_if[in_an_if] & IN_AN_ELSE) {
		mess_temp("Syntax error - \"else\" within \"else\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	if (((status_of_if[in_an_if] & IN_A_THEN) == 0) &&  
	    ((status_of_if[in_an_if] & IN_AN_ELIF_SKIP) == 0)) {
		mess_temp("Syntax error - \"else\" with no pending \"then\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	status_of_if[in_an_if] &= ~(ANY_IF_STATE);
	status_of_if[in_an_if] |= IN_AN_ELSE;

	return SUCCESS;
}
shell(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
	register int	i;
	register int	len;
	register char	*crunch;
	char	*myargv[4];
	char	*strnsave();

	len = 0;
	for (i = 1; i < argc; i++)
		len += strlen(argv[i]);
	crunch = strnsave(nil, len + argc - 1);
	for (i = 1; i < argc; i++) {
		strcat(crunch, " ");
		strcat(crunch, argv[i]);
	}
	myargv[0] = "sh";
	myargv[1] = "-c";
	myargv[2] = crunch;
	myargv[3] = NULL;
	i = execute(3, myargv, instr, outstr, errstr);
	free(crunch);
	return i;
}

execute(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
    register char	*p;
    register FILE       *errfp;
    register pid_t	pid;	/* EFT abs k16 */
    int	pfd[2];
    char	*strchr();
    unsigned char cc_vintr_sav = '\0';
    bool  changed_vintr = FALSE;

    if (strcmp(argv[0], "extern") == 0)
	argv++;
#ifdef _DEBUG2
    for (pid = 0; pid < argc; pid++)
	_debug2(stderr, "argv[%d] = '%s'\n", pid, argv[pid]);
#endif
    if (pipe(pfd))
	return FAIL;
    if (errstr == NULL)
	if ((errfp = fopen(p = tmpnam(NULL), "w+")) != NULL)
/*	if ((errfd = open(p = tmpnam(NULL), O_EXCL | O_CREAT | O_RDWR, 0600)) >= 0)
abs */
	    unlink(p);
	else
	    return FAIL;
    else
	errfp = errstr->mu.fp;

    switch (pid = fork()) {
    case -1:
	return FAIL;
    case 0:			/* child */
	close(pfd[0]);
	close(1);
	dup(pfd[1]);
	close(pfd[1]);
	close(2);
	dup(fileno(errfp));
	fclose(errfp);

    {
	if (instr->flags & EV_USE_FP) {
	    close(0);
	    dup(fileno(instr->mu.fp));
	}
	else if (instr->mu.str.count > 0) {
	    register int	c;
	    register FILE	*infp;
	    FILE	*tempfile();

	    if ((infp = tempfile(NULL, "w+")) == NULL)
		exit(1);
	    while (c = getac(instr))
		putc(c, infp);
	    close(0);
	    dup(fileno(infp));
	    fclose(infp);
	}
	if (Cur_intr.interrupt)   	/* if (interrupts enabled) */
	    sigset(SIGINT, SIG_DFL);
	else			        /* hide the interrupt key */
	{
	    struct termio  tbuf;

	    if (ioctl(stdin, TCGETA, &tbuf) != -1) /* if successful.. */
	    {
		
		cc_vintr_sav = tbuf.c_cc[VINTR];
		tbuf.c_cc[VINTR] = 0xff;
		if (ioctl(stdin, TCSETA, &tbuf) != -1)
		    changed_vintr = TRUE;
	    }
	}
	execvp(argv[0], argv);
	error_exec(errno);
	perror("fmli");
	exit(R_BAD_CHILD);	/* abs changed from exit(1).
				   This is fmli's exit not the C lib. call */
    }
	break;
    default:			/* parent (FMLI) */
    {
	register int	c;
	register int	retval;
	FILE	*fp;

	close(pfd[1]);
	if ((fp = fdopen(pfd[0], "r")) == NULL)
	    return FAIL;

	/* the errno == EINTR is added below to check for
	   system interrupts like MAILCHECK that were terminating
	   the read in progress -- added 7/89 by njp */
	while ((c = getc(fp)) != EOF || errno == EINTR)

	{
	    putac(c, outstr);
	    errno = 0;
	}
	fclose(fp);
	retval = waitspawn(pid);
/****
	if ((retval = waitspawn(pid)) && errfp != NULL && errstr != NULL)
	{
	    char	buf[MESSIZ];

	    if ((c = fread(buf, sizeof(char), MESSIZ-1, errfp)) > 0)
	    {
******/	    
/* for unknown reasons  this never worked (returns c = 0) abs
            if ((c = read(errfd, buf, sizeof(buf) - 1)) > 0) {
*/
/*****		buf[c] = '\0';
		if (p = strchr(buf, '\n'))
		    *p = '\0';
		mess_temp(buf);
	    }
	}
******/
	/* reset the interrupt key */
	if (changed_vintr == TRUE)
	{
	    struct termio  tbuf;

	    if (ioctl(stdin, TCGETA, &tbuf) != -1) /* if successful.. */
	    {
		tbuf.c_cc[VINTR] = cc_vintr_sav;
		ioctl(stdin, TCSETA, &tbuf);
	    }
	}

	if (errfp != NULL)
	    fclose(errfp);
	return retval;
    }
	break;
    }
    /*
     * lint will complain about it, but there's actually no way to
     * reach here because of the exit(), so this is not a return
     * without an expression
     */
}

get_wdw(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
	int wdwnum;

	wdwnum = ar_ctl(ar_get_current(), CTGETWDW, NULL, NULL, NULL, NULL, NULL, NULL);
	putastr(itoa((long)wdwnum, 10), outstr); /* abs k16 */
	return SUCCESS;
}

getmod(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
    static mode_t modes;	/* EFT abs k16 */
    int i;
    struct stat	statbuf;
    long	strtol();
    char	*bsd_path_to_title();

    if (argc < 2)
	return FAIL;
    i = 1;
    if (argc > 2) {
	i++;
	if (strcmp(argv[1], "-u") == 0) {
	    register char	*p;
	    char	*getepenv();

	    if ((p = getepenv("UMASK")) == NULL || *p == '\0')
		modes = 0775;
	    else
		modes = ~(strtol(p, NULL, 8)) & 0777; 
	} else if (stat(argv[1], &statbuf) == -1) {
	    mess_temp(nstrcat("Could not access object ",
			      bsd_path_to_title(argv[1], MESS_COLS-24), NULL));
	    return FAIL;
	} else
	    modes = statbuf.st_mode;
    }
    if (strtol(argv[i], NULL, 8) & modes)
	putastr("yes", outstr);
    else
	putastr("no", outstr);
    return SUCCESS;
}

setmod(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
    register int	i;
    register mode_t	mode;	/* EFT abs k16 */
    char *bsd_path_to_title();

    if (argc < 2)
	return FAIL;
    for (i = 2, mode = 0; argv[i]; i++)
	mode = (mode << 1) | !strCcmp(argv[i], "yes");

    if ((mode & 0600) != 0600)
	mess_temp("WARNING: You are denying some permissions to yourself!");

    if (strcmp(argv[1], "-u") == 0) {
	char buf[20];

	mode = ~mode & 0777;
	(void) umask(mode);
	sprintf(buf, "0%o", mode);
	return chgepenv("UMASK", buf);
    } else if (chmod(argv[1], mode) < 0) {
	mess_temp(nstrcat("Unable to change security on ",
			  bsd_path_to_title(argv[1], MESS_COLS-29), NULL));
	return(FAIL);
    } else
	return SUCCESS;
}

int Long_line = -1;

long_line(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
	register int maxcount, c, count;
	FILE *fp;

	if (argv[1]) {		/* if there is a file argument */
		if ((fp = fopen(argv[1], "r")) == NULL)
			return(FAIL);
		for (maxcount = 0, count = 0; (c = getc(fp)) != EOF; count++) {
			if (c == '\n') {
				maxcount = max(maxcount, count);
				count = -1;
			}
		}
		fclose(fp);
		Long_line = max(maxcount, count);
	}
	else if (Long_line < 0)
		return(FAIL);
	putastr(itoa((long)Long_line + 1, 10), outstr);	/* abs k16 */
	return SUCCESS;
}

read_file(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
	char	*p, *f;
	register int c, count, maxcount;
	FILE	*fp;
	char	*path_to_full();

	if (argc > 1)
		f = path_to_full(argv[1]);
	else {
		f = path_to_full(p = io_string(instr));
		free(p);
	}
	if ((fp = fopen(f, "r")) == NULL)
		return(FAIL);
	free(f);
	for (count = 0, maxcount = 0; (c = getc(fp)) != EOF; count++) {
		if (c == '\n') {
			maxcount = max(maxcount, count);
			count = -1;
		}
		putac(c, outstr);
	}
	Long_line = max(maxcount, count);
	fclose(fp);
	return SUCCESS;
}

cmd_echo(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
	register char	*p;
	register int	i;
	char	*strrchr();

	for (i = 1; i < argc; i++) {
		if (i > 1)
			putac(' ', outstr);
		if (i == argc - 1 && (p = strrchr(argv[i], '\\')) && strcmp(p, "\\c'") == 0) {
			*p = '\0';
			putastr(argv[i], outstr);
			return SUCCESS;
		}
		putastr(argv[i], outstr);
	}
	putac('\n', outstr);
	return SUCCESS;
}

#ifndef TEST
extern int	cocreate();
extern int	cosend();
extern int	codestroy();
extern int	cocheck();
extern int	coreceive();
extern int	genfind();
extern int	cmd_pathconv();
#endif 
extern int	cmd_set();
extern int	cmd_run();
extern int	cmd_regex();
extern int	cmd_getlist();
extern int	cmd_setcolor();
extern int	cmd_reinit();
extern int	cmd_message();
extern int	cmd_indicator();
extern int	cmd_unset();
extern int	cmd_getodi();
extern int	cmd_setodi();
extern int	cmd_cut();
extern int	cmd_grep();
extern int	cmd_test();		/* ehr3 */
extern int	cmd_expr();

#define NUM_FUNCS	(sizeof(func) / sizeof(*func))

static struct {
	char	*name;
	int	(*function)();
} func[] = {
	{ "extern",	execute },
	{ "shell",	shell },
	{ "regex",	cmd_regex },
	{ "echo",	cmd_echo },
	{ "fmlcut",	cmd_cut },
	{ "fmlgrep",	cmd_grep },
	{ "fmlexpr",	cmd_expr },
	{ "set",	cmd_set },
	{ "unset",	cmd_unset },
	{ "getmod",	getmod },
	{ "getodi",	cmd_getodi },
	{ "getfrm",	get_wdw },
	{ "getwdw",	get_wdw },	/* alias to getfrm */
	{ "setmod",	setmod },
	{ "setodi",	cmd_setodi },
	{ "readfile",	read_file },
	{ "longline",	long_line },
	{ "message",	cmd_message },
	{ "indicator", 	cmd_indicator },
	{ "run",	cmd_run },
	{ "getitems",	cmd_getlist },
	{ "genfind",	genfind },
	{ "pathconv",	cmd_pathconv },
	{ "setcolor",	cmd_setcolor},
	{ "reinit",	cmd_reinit},
	{ "test",	cmd_test},	/* ehr3 */
	{ "[",		cmd_test},	/* ehr3 */
	{ "if",		cmd_if},	/* ehr3 */
	{ "then",	cmd_then},	/* ehr3 */
	{ "else",	cmd_else},	/* ehr3 */
	{ "elif",	cmd_elif},	/* ehr3 */
	{ "fi",		cmd_fi},	/* ehr3 */
/*
 * not yet ...
 *
	{ "true",	cmd_true},
	{ "false",	cmd_false},
 */
#ifndef TEST
	{ "cocreate",	cocreate },
	{ "cosend",	cosend },
	{ "codestroy",	codestroy },
	{ "cocheck",	cocheck },
	{ "coreceive",	coreceive }
#endif 
};

evalargv(argc, argv, instr, outstr, errstr)
int	argc;
char	*argv[];
IOSTRUCT	*instr;
IOSTRUCT	*outstr;
IOSTRUCT	*errstr;
{
	register int	n, ret;
	int	n2;

/*	test moved to calling routine, SUCCESS is wrong here. abs
 *	if (argc < 1)
 *		return SUCCESS;
 */

	for (n = 0; n < NUM_FUNCS; n++)
		if (strcmp(argv[0], func[n].name) == 0)
			break;

	if (n >= NUM_FUNCS)
		n = 0;

	if (in_an_if) {
		switch(argv[0][0]) {
			case 'i':
				if (!strcmp(argv[0], "if")) {
					ret = cmd_if(argc, argv, instr, outstr);
					return ret;
				}
				break;

			case 't':
				if (!strcmp(argv[0], "then")) {
					ret = cmd_then(argc, argv, instr, outstr);
					return ret;
				}
				break;

			case 'e':
				if (!strcmp(argv[0], "else")) {
					ret = cmd_else(argc, argv, instr, outstr);
					return ret;
				}

				if (!strcmp(argv[0], "elif")) {
					ret = cmd_elif(argc, argv, instr, outstr);
					return ret;
				}
				break;

			case 'f':
				if (!strcmp(argv[0], "fi")) {
					ret = cmd_fi(argc, argv, instr, outstr);
					return ret;
				}
				break;
		}

		/*
			AFTER checking for if-then-else stuff 
			we need to determine if we are in 
			executable code or not. We do this by 
			checking each prior level of nesting. 
			If any of them fails, then we know 
			we should not execute this command.
		*/
		for (n2 = 1; n2 <= in_an_if; n2++) {
			if (status_of_if[n2] & IF_IS_TRUE) {
				/*
				 * The condition is TRUE ...
				 * skip the command if:
				 *
				 * we are in an "else"
				 *
				 * we are in an "elif" but a previous
				 * "if" or "elif" evaluated to true
				 */
				if ((status_of_if[n2] & IN_AN_ELSE) ||
				    (status_of_if[n2] & IN_AN_ELIF_SKIP)) 
					return SUCCESS;
			} else {
				/*
				 * The condition is FALSE ...
				 * skip the command if we are in
				 * a "then"
				 */
				if (status_of_if[n2] & IN_A_THEN)
					return SUCCESS;
			}
		}

		if (status_of_if[in_an_if] & IN_A_CONDITION) {
			int	cmd_rc;

			cmd_rc = (*func[n].function)(argc, argv, instr, outstr, errstr);
			if (cmd_rc == SUCCESS)
				status_of_if[in_an_if] |= IF_IS_TRUE;
			else 
				status_of_if[in_an_if] &= ~IF_IS_TRUE;
			return(cmd_rc);
		} else {
			/*
			 * Keep track of the return value from the
			 * lastly executed built-in/executable ...
			 * This value will determine the SUCCESS/FAILURE
			 * of the if/then/else statement (see cmd_fi).
			 */
			Lastret = (*func[n].function)(argc, argv, instr, outstr, errstr);
			return(Lastret);
		}
	}
	else return (*func[n].function)(argc, argv, instr, outstr, errstr);
}

cmd_if(argc, argv, in, out, err)
int	argc;
char	*argv[];
IOSTRUCT	*in;
IOSTRUCT	*out;
IOSTRUCT	*err;
{
	int	n;
	int	n2;

	in_an_if++;
	status_of_if[in_an_if] = IN_A_CONDITION;

	if (in_an_if == MAX_IF_DEPTH) {
		mess_temp("Internal error - \"if\" stack overflow");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	return SUCCESS;
}

cmd_elif(argc, argv, in, out, err)
int	argc;
char	*argv[];
IOSTRUCT	*in;
IOSTRUCT	*out;
IOSTRUCT	*err;
{
	int	n;
	int	n2;

	if (in_an_if <= 0) {
		mess_temp("Syntax error - \"elif\" with no pending \"if\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	if (status_of_if[in_an_if] & IN_AN_ELSE) {
		mess_temp("Syntax error - \"elif\" after an \"else\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}

	if (((status_of_if[in_an_if] & IN_A_THEN) == 0) &&  
	    ((status_of_if[in_an_if] & IN_AN_ELIF_SKIP) == 0)) {
		mess_temp("Syntax error - \"elif\" with no pending \"then\"");
		mess_lock();
		in_an_if = 0;
		return FAIL;
	}


	/*
	 * if a previous "if/elif" condition is TRUE 
	 * then don't evaluate the "elif" condition.
	 */
	
	if (status_of_if[in_an_if] & IF_IS_TRUE) {
		status_of_if[in_an_if] &= ~(ANY_IF_STATE);
		status_of_if[in_an_if] |= IN_AN_ELIF_SKIP;
	}
	else 
		status_of_if[in_an_if] = IN_A_CONDITION; 

	return SUCCESS;
}

/*
 * not yet ...
 *
cmd_true(argc, argv, in, out, err)
int	argc;
char	*argv[];
IOSTRUCT	*in;
IOSTRUCT	*out;
IOSTRUCT	*err;
{
	return(SUCCESS);
}

cmd_false(argc, argv, in, out, err)
int	argc;
char	*argv[];
IOSTRUCT	*in;
IOSTRUCT	*out;
IOSTRUCT	*err;
{
	return(FAIL);
}
*/
