/* Parse command line, set up command arguments Unix-style, and call function.
 * Note: argument is modified (delimiters are overwritten with nulls)
 * Improved error handling by Brian Boesch of Stanford University
 */
#include <stdio.h>
#include <ctype.h>
#include "global.h"
#include "cmdparse.h"

int
cmdparse(cmds,line)
struct cmds cmds[];
register char *line;
{
	register char *pp,*rp,*ep,*vp;
	char *cp,*argv[NARG],*getenv();
	static char pool[POOLSIZE];
	int argc,quote;
	char c;
	
	/* Remove cr/lf */
	rip(line);

	for(argc = 0;argc < NARG;argc++)
		argv[argc] = NULLCHAR;

	pp = pool;

	for(argc = 0;argc < NARG && *line != '\0';){
	    quote = 0;
	    /* Skip leading white space */
	    while(*line == ' ' || *line == '\t')
		line++;
	    if(*line == '\0')
		break;
	    /* Check for quoted token */
	    if(*line == '"' || *line == '\'')
		quote = *line++;	/* Suppress quote, remember it */
	    argv[argc++] = line;	/* Beginning of token */
	    /* Find terminating delimiter */
	    if(quote){
		/* Find quote, it must be present */
		if((line = index(line,quote)) == NULLCHAR)
		    return -1;

		*line++ = '\0';
	    } else {
		/* Find space or tab. If not present,
		 * then we've already found the last
		 * token.
		 */
		for (cp = line; *cp; cp++)
		    if (*cp == ' ' || *cp == '\t')
		    break;
		if (*cp != '\0')
		    *cp++ = '\0';
		line = cp;
	    }
	    /* Check for variablename */
	    if(quote != '\'' && index(cp = argv[argc - 1],'$') != NULLCHAR){
		argv[argc - 1] = pp;
		while (*cp != '\0') {
		    if (*cp != '$')
			*pp++ = *cp++;
		    else {
			if (*++cp == '{') {
			    if ((ep = index(++cp,'}')) == NULLCHAR)
			    return -1;
			    *ep++ = '\0';
			} else {
			    ep = NULLCHAR;
			}
			/* Scan to end of varname and mark it */
			for (vp = cp; isalnum(*vp) || *vp == '_'; vp++)
			    *vp = toupper(*vp);
			/* If a name found, get value */
			if (vp != cp){
			    c = *vp;			/* save next and make \0 */
			    *vp = '\0';
			    rp = getenv(cp);
			    *vp = c;			/* restore next character */
			    if (ep == NULLCHAR){	/* no braces */
				if (rp == NULLCHAR)	/* undef'd var */
				    rp = "";		/* then just empty */
			    } else {
				if (rp == NULLCHAR)	/* undef'd var */
				    switch (c) {	/* else look at next */
				    case '=':
					dosetenv(2,(&cp)-1);/* assign and subst */
				    case '-':
					rp = vp + 1;	/* subst next word */
					break;
				    case '?':
					if (*++vp == '\0')
					vp = "undefined variable";
					printf("%s\n",vp);
					return -1;
				    default:
					rp = "";	/* default is empty */
				    }
				else
				    if (c == '+')	/* defined and subst */
					rp = vp + 1;
			    }
			    /* Copy the result to the pool */
			    while ((*pp++ = *rp++) != '\0')
				;
			    pp--;
			    cp = vp;
			}
			if (ep != NULLCHAR)
			    cp = ep;
		    }
		}
		*pp++ = '\0';
		if (pp > pool + POOLSIZE){	/* check pool overflow */
		    printf("PANIC: pool overflow\n");
		    fflush(stdout);
		    for (;;)			/* dangerous to continue */
			;
		}
	    }
	}
	if (argc < 1) {			/* empty command line */
		argc = 1;
		argv[0] = "";
	}
	/* Lines beginning with "#" are comments */
	if(argv[0][0] == '#')
		return 0;

	return docmd(cmds,argc,argv);	/* execute command */
}

/* Call a command based on the first token in an already-parsed line */
int
docmd(tab,argc,argv)
struct cmds tab[];
int argc;
char *argv[];
{
	register struct cmds *cmdp;
	int rslt;

	/* Look up command in table; prefix matches are OK */
	for(cmdp = tab;cmdp->name != NULLCHAR;cmdp++){
		if(strncmp(argv[0],cmdp->name,strlen(argv[0])) == 0)
			break;
	}
	if(cmdp->name == NULLCHAR) {
		if(cmdp->argc_errmsg != NULLCHAR)
			printf("%s\n",cmdp->argc_errmsg);
		return -1;
	}
	if(argc < cmdp->argcmin) {
		/* Insufficient arguments */
		printf("Usage: %s %s\n",cmdp->name,cmdp->argc_errmsg);
		return -1;
	}
	argv[0] = cmdp->name;
	rslt = (*cmdp->func)(argc,argv);
	if ((rslt < 0) && (cmdp->exec_errmsg != NULLCHAR))
		printf("%s\n",cmdp->exec_errmsg);
	return(rslt);
}

/* Call a subcommand based on the first token in an already-parsed line */
int
subcmd(tab,argc,argv)
struct cmds tab[];
int argc;
char *argv[];
{
	int rslt,len;
	register struct cmds *cmdp;

	/* Strip off first token and pass rest of line to subcommand */
	if (argc < 2) {
		if (argc < 1)
			printf("SUBCMD - Don't know what to do?\n");
		else
			printf("\"%s\" - takes at least one argument\n",argv[0]);
		return -1;
	}
	for(cmdp = tab;cmdp->name != NULLCHAR;cmdp++){
		if(strncmp(argv[1],cmdp->name,strlen(argv[1])) == 0){
			if(--argc < cmdp->argcmin) {
				if (cmdp->argc_errmsg != NULLCHAR)
					printf("Usage: %s %s %s\n",
					       argv[0],cmdp->name,cmdp->argc_errmsg);
				return -1;
			}
			(++argv)[0] = cmdp->name;
			rslt = (*cmdp->func)(argc,argv);
			if ((rslt < 0) && (cmdp->exec_errmsg != NULLCHAR))
				printf("%s\n",cmdp->exec_errmsg);
			return(rslt);
		}
	}
	if (cmdp->argc_errmsg != NULLCHAR){
		if (cmdp->argc_errmsg[0] != '?'){
			printf("%s\n",cmdp->argc_errmsg);
		} else {
			printf("%s %s:",argv[0],cmdp->argc_errmsg + 1);
			len = strlen(argv[0]) + strlen(cmdp->argc_errmsg) + 1;
			for(cmdp = tab;cmdp->name != NULLCHAR;cmdp++){
				if ((len += strlen(cmdp->name) + 1) > 79){
					len = strlen(cmdp->name) + 9;
					printf("\n        ");
				}
				printf(" %s",cmdp->name);
			}
			printf("\n");
		}
	}
	return -1;
}
