#include <stdio.h>
#include <ctype.h>
#include "proff.h"
#include "debug.h"
#include "lextab.h"

#define RUNOFF	1 		/* recognise RUNOFF commands */

char literal = NO;		/* literal flag		     */

/*
 * command - perform formatting command
 *
 */
command(buf)
char buf[];
{
	char token[MAXTOK], xtoken[MAXTOK], variable[MAXTOK], *defn;
	char special[MAXTOK];
	int argtyp, ct, at, spval, i, flags;
	register int val, n, rest;
	char onflag = FALSE; 
	char offlag = FALSE;
	struct lexlist *xp;

	dovar(tbuf1,buf);	/* use scratch buffer to expand variables */
	strcpy(buf,tbuf1);

	i = 1;
	n = getwrd(buf, &i, token);	/* get the command token */
	rest = i;			/* remaining string */
	ct = comtype(token, n, &defn, &flags);
	if (ct == UNKNOWN)
		return;
	if (literal && ct != ELT) {	/* ignore while literal  */
		put(buf);
		return;
	}

#ifdef DOUBLEWORD
	if (flags == 2) {	/* check for 2-word command */
		n = getwrd(buf, &i, xtoken);
		if (n == 0) {
			fprintf(stderr,"%c%s what ?\n", cchar, token);
			return;
		}
		if ((at = comtype(xtoken, n, &defn, &flags)) == UNKNOWN) {
			fprintf(stderr,"%c%s %s unknown.\n", cchar,
			token,
			xtoken);
			return;
		}
		else
			ct += at;
	}
#endif

	doesc(buf, variable, MAXLINE);	/* expand escapes */
	n = getarg(buf, &i, xtoken);	/* first parameter*/
	argtyp = '\n';	/* defaulted ** cludge ** */
	val = 0;
	if (n > 0) {
		if (*xtoken == '+' || *xtoken == '-') {
			argtyp = *xtoken;
			val = atoi(xtoken+1);
		}
		else if (isdigit(*xtoken)) {
			argtyp = 0;
			val = atoi(xtoken);
		}
		else {
			/* check some common flags */
			if (strcmp("on",xtoken) == 0)
				onflag = TRUE;
			else if (strcmp("off",xtoken) == 0)
				offlag = TRUE;
		}
	}

	switch(ct) {

	case MACRO:
		eval(buf, defn);
		break;
	case FI:
		brkeol();
		fill = YES;
		break;
	case NF:
		brkeol();
		fill = NO;
		break;
	case BR:
		brkeol();
		break;
	case LS:
		set(&lsval, val, argtyp, 1, 1, HUGE);
		break;
	case CE:
		brkeol();
		if (onflag)
			CEon = TRUE;
		else if (offlag) {
			CEon = FALSE;
			ceval = 0;		/* reset */
		}
		else
			set(&ceval, val, argtyp, 1, 0, HUGE);
		break;
	case UL:
		if (onflag) {
			ULon = TRUE;
			break;
		}
		else if (offlag) {
			ULon = FALSE;
	 		ulval = 0;		/* reset */
			break;
		}
		else 
			set(&ulval, val, argtyp, 0, 1, HUGE);

		if (!isdigit(*xtoken)) {
			if (strcmp("all",xtoken) == 0) {
					ulblnk = '_';
					ulval = 0;
			}
			else if (strcmp("words",xtoken) == 0) {
				ulblnk = ' ';
				ulval = 0;
			}
		}
		break;
	case BD:
		if (bolding == YES) {
			if (onflag)
				BDon = TRUE;
			else if (offlag) {
				BDon = FALSE;
				boval = 0;	/* reset */
			}
			else
				set(&boval, val, argtyp, 0, 1, HUGE);
		}
		break;
	case HE:
		gettl(buf, ehead, ehlim);
		gettl(buf, ohead, ohlim);
		break;
	case FO:
		gettl(buf, efoot, eflim);
		gettl(buf, ofoot, oflim);
		break;
	case BP:
		if (paging == NO)
			break;
		brkeol();
		if (lineno > 0)
			space(HUGE);
		set(&curpag, val, argtyp, curpag+1, -HUGE, HUGE);
		newpag = curpag;
		break;
	case SP:
		set(&spval, val, argtyp, 1, 0, HUGE);
		space(spval);
		break;
	case IN:
		brkeol();
		set(&inval, val, argtyp, 0, 0, rmval-1);
		tival = inval;
		break;
	case RM:
		set(&rmval, val, argtyp, PAGEWIDTH, tival+1, HUGE);
		break;
	case TI:
		brkeol();
		set(&tival, val, argtyp, 0, 0, rmval);
		break;
	case LEX:	/****/
		if ((xp = remove(xtoken,lextab)) != NULL) {
			if (getwrd(buf, &i, variable) != 0)
				lexinstal(variable,xp->val,xp->flag,lextab);
		}
		else 
		    fprintf(stderr,"%s undefined.\n",xtoken);
		break;
	case PN:	/****/
		if (strcmp(xtoken,"roman") == 0)
			roman = TRUE;
		else if (strcmp(xtoken,"arabic") == 0)
			roman = FALSE;
		else
			fprintf(stderr,"%c%s does not have %s option.\n",
			cchar,token,xtoken);
		break;
	case IG:	/****/
		break;
	case SET:	/****/
		if (n > 0) {
			if (isdigit(*xtoken)) {
				fprintf(stderr,"illegal variable name %s\n",
				xtoken);
				break;
			}
			*variable = '\0';
			n = getarg(buf, &i, variable);
			if (n <= 0) {
				fprintf(stderr,"%s: ", xtoken);
				gets(variable);

			}
			if (*variable != '\0')
				install(xtoken, variable, gentab);
		}
		else
			fprintf(stderr,"%c%s needs a variable name.\n",
			cchar, token);
		break;
	case GET:	/****/
		if (n > 0) {
			if (isdigit(*xtoken)) {
				fprintf(stderr,"illegal variable name %s\n",
				xtoken);
				break;
			}
			*variable = '\0';
			n = getarg(buf, &i, tbuf3); /* using temp buf3 */
			if (n > 0) {
				fprintf(stderr,"%s", tbuf3);
				gets(variable);

			}
			if (*variable != '\0')
				install(xtoken,variable, gentab);
		}
		else
			fprintf(stderr,"%c%s needs a variable name.\n",
			cchar, token);
		break;
	case CL:	/****/
		if (argtyp == '\n') {
			clast->level = 0;
			clast->str = NULL;
		}
		else {
			skipbl(buf,&i);
			if (*(buf+i) == '\0')
				break;		/* no contents line here ! */
			clast->level = val * 3; /* level * indent 	   */
			n = i;
			while(*(buf+n) != '\n')
				n++;
			*(buf+n) = '\0';	/* destroy CR with a null  */
			clast->str = strsave(buf+i);
			clast->page = curpag;
		}
		clast->nextc = (struct clist *) malloc(sizeof(struct clist));
		p_memoryus += sizeof(struct clist);
		clast = clast->nextc;
		clast->nextc = NULL;
		break;
	case PC:	/****/
		brkeol();
		clast = chead;
		while(clast->nextc != NULL) {
			if (clast->str == NULL)
				put("\n");
			else {
				tival = (int) clast->level + inval;
				i = rmval - tival;
				docline(variable, i, clast->str, clast->page);
				put(variable);
			}
			clast = clast->nextc;
		}
		break;
	case DBO:	/****/
		bolding = NO;
		break;
	case EBO:	/****/
		bolding = YES;
		break;
	case AP:	/****/
		autopar = YES;
		break;
	case NAP:	/****/
		autopar = NO;
		break;
	case SAV:	/****/
		brkeol();
		save();
		break;
	case RST:	/****/
		brkeol();
		restore();
		break;
	case NPA:	/****/
		paging = NO;
		savpl = plval;
		plval = HUGE;
		bottom = plval - m3val - m4val;
		break;
	case PGI:	/****/
		bottom = lineno - 1;	/* force end-of-page */
		brkeol();
		plval = savpl;
		break;
	case LTR:	/****/
		brkeol();
		if (save()) {
			inval = 0;
			rmval = 132;
			autopar = NO;
			lsval = 0;
			fill = NO;
			literal = YES;
		}
		break;
	case ELT:	/****/
		restore();
		literal = NO;
		break;
	case WR:	/****/
		brkeol();
		getpstr(buf+rest,special);
		defn = special;
		while(*defn)
			putchar(*defn++);
		break;
	case PL:
		if (paging == NO)
			break;
		set(&plval, val, argtyp, PAGELEN,
		m1val + m2val + m3val + m4val + 1, HUGE);
		bottom = plval - m3val - m4val;
		break;
	case PO:
		set(&offset, val, argtyp, 0, 0, rmval - 1);
		break;
	case M1:
		set(&m1val, val, argtyp, 3, 0,
		plval - m2val - m3val - m4val - 1);
		break;
	case M2:
		set(&m2val, val, argtyp, 2, 0,
		plval - m1val - m3val - m4val - 1);
		break;
	case M3:
		set(&m3val, val, argtyp, 2, 0,
		plval - m1val - m2val - m4val - 1);
		bottom = plval - m3val - m4val;
		break;
	case M4:
		set(&m4val, val, argtyp, 3, 0,
		plval - m1val - m2val - m3val - 1);
		bottom = plval - m3val - m4val;
		break;
	case EH:
		gettl(buf, ehead, ehlim);
		break;
	case OH:
		gettl(buf, ohead, ohlim);
		break;
	case EF:
		gettl(buf, efoot, eflim);
		break;
	case OF:
		gettl(buf, ofoot, oflim);
		break;
	case CC:
		cchar = *xtoken;
		if (cchar == '\0' || cchar == '\n')
			cchar = '.';
		if ((lineno + val) > bottom && lineno <= bottom) {
			space(val);
			lineno = 0;
		}
		break;
	case EC:
		genesc = *xtoken;
		if (genesc == '\0' || genesc == '\n')
			genesc = '_';
		break;
	case NE:
		if ((lineno + val) > bottom && lineno <= bottom) {
			space(val);
			lineno = 0;
		}
		break;
	case BS:
		set(&bsval, val, argtyp, 1, 0, HUGE);
		break;
	case JU:
		rjust = YES;
		break;
	case NJ:
		rjust = NO;
		break;
	case SO:
		if (n <= 0)
			return;
		if (level + 1 == NFILES)
			error("? SO commands nested too deeply.");
		if ((infile[level + 1] = fopen(xtoken, "r")) != NULL) {
			level++;
			if (verbose == YES)
#ifdef rainbow
				fprintf(stderr,"source \033[7m%s\033[0m\n",
					xtoken);
#else
				fprintf(stderr,"source %s\n",xtoken);
#endif
		}
		else
			fprintf(stderr,"%s: cannot open.\n",xtoken);
		break;
	case OU:	/*****/
		/* skip for now. */
		break;

	case OE:	/*****/
		/* skip for now. */
		break;

	case CU:
		ulblnk = '_';
		set(&ulval, val, argtyp, 0, 1, HUGE);
		break;
	case DE:
		dodef(buf, infile[level]);
		break;
	case NR:
		if (n <= 0)
			return;
		if (*xtoken < 'a' || *xtoken > 'z')
			error("invalid number register [%c].",*xtoken);

		val = getval(buf, &i, &argtyp);
		set(&nr[xtoken[0] - 'a'], val, argtyp, 0, -HUGE, HUGE);
		break;
	case ST:
		if (argtyp == '-')
			spval = plval;
		else
			spval = 0;
		set(&spval, val, argtyp, 0, 1, bottom);
		if (spval > lineno && lineno == 0)
			phead();
		if (spval > lineno)
			space(spval - lineno);
		break;
	case RESET:	/****/
		finit();
		break;
	default:
		error("? Botch in command.");
		break;
	}
}

/*
 * comtype - decode the command type
 *
 */
int
comtype(buf, siz, defn, flags)
char buf[];
int siz;
char **defn;
int *flags;
{

	struct hashlist *np;
	struct lexlist *xp;
	extern	struct lexlist *lexlook();
	int i,comtyp;
	char c1,c2;


#ifdef DEBUG
	printf("comtype: %s (token)\n",token1);
#endif

	if ((np = lookup(buf, macrotab)) != NULL) {
		*defn=np->def;
		return(MACRO);
	}
	comtyp = UNKNOWN;

	if (*buf == '#' || *buf == '!')
		return(comtyp);

	if ((xp = lexlook(buf,lextab)) != NULL)
		if (onlyrunoff && (xp->flag != RUNOFF)) {
			fprintf(stderr,"%c%s is not a runoff command.\n",
			cchar,buf);
			return(UNKNOWN);
		}
		else {	
			comtyp = xp->val;
			*flags = xp->flag;
		}

	if (comtyp == UNKNOWN)
		fprintf(stderr,"unknown command %c%s\n",cchar,buf);
	return(comtyp);
}
