#include "fido.h"
#include "fidomem.h"
#include <ascii.h>
#include "proto.h"

/*
Contains all prompt and priv table code, except the initial
table load.
*/

/* Context sensitive help: if there is a command table, checks that the
command is in the table, then tries to find text for it in the specified
file. If no command character is supplied, it assumes general help.

	Help system syntax:

	"?"		general help
	"G?"		help with the G command
	"? G"		help with the G command

	The help file is assumed to be in one of two formats: a plain 
sequential text file, or the newer format with the following markers:

\c
	help text
	...

\c

	A line beginning with \ is the start of help for the c command. Etc.
*/

help(cp,t,fname)
char *cp;			/* possible cmd name and ? */
struct _cmd *t;			/* command table, */
char *fname;			/* help file name */
{
int i,f;
char c;				/* command char to help with, */

	if (*cp && (cp[1] == '?')) c= *cp;
	else if (isargs()) c= *getarg(FM+0);
	else c= '?';

	if ( (int) t != 0) {			/* may not be a table, */
		if (! is_cmd(c,t)) {
			mprintf(CM+15,c);
			return(0);
		}
	}

	f= opentf(fname,0);			/* find the file, */
	if (f == -1) {
		mprintf(CM+16);
		lprintf(LM+12,fname);
		return(0);
	}
	mcrlf();
	i= dumphelp(c,f);			/* look for specific help */
	if (! i) {
		mprintf(CM+17);
		lprintf(LM+13,fname,c);
	}
	close(f);
	return(i);
}
/* Search the file for the first occurence of "\c" where c is the passed
character, and output wrapped text until the next occurence of "\". 
Returns 0 if not found. */

int dumphelp(c,f)
char c;
int f;
{
char *cp,word[SS],lastc;
int width,col,i;

	c= tolower(c);					/* easier compare */
	width= uval(CLR_WID,CLR_WIDS);			/* set width for wrapping */
	if (width > sizeof(word)) width= sizeof(word);
	width -= 2;

/* Look for the line that starts the help section we want; "\c", where
c is the command character. */

	while (i= rline(f,word,sizeof(word))) {
		if ((word[0] == '\\') && (tolower(word[1]) == c)) break;
	}
	if (! i) return(0);				/* never found it */

/* Now output wrapped text until we find the sequence CR \, ie. any line
beginning with \, presumably the next command help section. */

	c= CR;						/* start the machine */
	while (! abort) {				/* let the user abort */
		i= 0;
		word[i]= NUL;
		while (wordlen(word,col) < width) {
			if (c) lastc= c;
			if (! read(f,&c,1)) return(1);	/* stop if EOF */
			if ((lastc == CR) && (c == '\\')) /* if another command help */
				return(1);		/* stop now */

			switch (c) {
				case CR + 128: if (lastc == ' ') c= NUL;
						else c= ' '; break;
				case LF: c= NUL;
			}
			if (c == NUL) continue;		/* skip this */
			if (c == CR) break;		/* stop if CR, */

			word[i++]= c;			/* store others, */
			word[i]= NUL;			/* end of the word */
			if ((c == TAB) || (c == ' ')) break;
		}

		if (wordlen(word,col) + col > width) {
			mcrlf();			/* too long; wrap */
			col= 0;				/* before typing word */
		}
		col += wordlen(word,col);		/* size on screen */

		for (cp= word; *cp; ++cp) mconoutr(*cp); /* display raw text */

		if (c == CR) {				/* if a hard CR, */
			mcrlf();			/* do a CR LF */
			col= 0;
		}
		if (c == NUL) break;			/* check EOF */
	}
	mcrlf();
	return(1);
}

/* Prompt for command input. Handles command-ahead, etc. This handles
help, "no such command", and returns the index (2 - N) of valid command
input. */

prompt(t,id,hlp,def)
struct _cmd *t;		/* command table */
unsigned id;		/* prompt string ID */
char *hlp;		/* help filename */
int def;		/* which command [CR] means */
{
int i;
char *cp;

	while (1) {
		if (! isargs()) {			/* if no command-ahead */
			sprompt(t,id,def);		/* display the prompt */
			mprintf(CM+35);			/* "Command (?=help): " */
		}
		cp= getarg(FM+0);			/* input commands */
		if (num_args(cp) < 1) {			/* for just CR */
			if (def >= 2) cp= t[def - 2].name;/* possible default command */
			else continue;			/* nothing entered */
		} 
		i= is_cmd(*cp,t);			/* check against cmd table */
		if (cp[1] == '?') help(cp,t,hlp);	/* do help now */
		else if (i == 0) {
			mprintf(CM+36,*cp);		/* "%c is not a command" */
			cmdflush();

		} else if (i == 1) {			/* do help */
			help("",t,hlp); 

		} else return(i);			/* valid input */
	}
}

/* Do the prompt part of the command input only. */

sprompt(t,id,def)
struct _cmd *t;		/* command table */
unsigned id;		/* prompt string ID */
int def;		/* default command */
{
	if (def >= 2) def -= 2;				/* start at 2 (see is_cd) */
	else def= -1;

	if (uval(CLR_HLP,CLR_HLPS) == NOVICE) {		/* full prompt */
		doprompt(t,0,def);			/* full command list */
		mcrlf();				/* cr/lf */
		mprintf(id);				/* "xxx SECTION " */

	} else if (uval(CLR_HLP,CLR_HLPS) == REGULAR) {
		mprintf(id);				/* "xxx SECTION " */
		doprompt(t,1,def);			/* short list */
							
	} else mprintf(id); 				/* else no list */
}

/* Display the command list depending on caller help level: NOVICE, full
command name list; REGULAR, first character only; EXPERT, no list. */

static
doprompt(t,flag,def)
struct _cmd *t;
int flag;
int def;
{
int i,n,width,maxw;

	maxw= uval(CLR_WID,CLR_WIDS);			/* max. width */
	if (maxw > 46) maxw= 46;			/* bound to 46 cols */

	if (flag) mputs("(");
	n= 0;
	while (*t-> name) {
		if (allowedx(t-> locks,t-> priv)) {	/* if command accessible, */
			if (flag) i= 1;			/* width of command */
			else i= strlen(t-> name);	/* name or character */
			if ((column + i) >= maxw)
				mcrlf();		/* watch screen width */

			if (def == n) bracket();	/* "[" bracket default command */
			if (flag) mprintf(0,"%c",*t-> name); /* first character */
			else mprintf(0,"%s",t-> name);	/* or full word */
			if (def == n) unbracket();	/* "]" bracket default command */

			++t;
			if (*t-> name) mputs(" ");	/* space between each */

		} else ++t;
		++n;
	}
	if (flag) mputs(")");
	mputs(" ");
}

/* Return the index of the character command in the table, and the
callers privelege level is high enough. Returns 0 if not found, and 
1 if ?. Commands then start at 2. */

is_cmd(c,t)
char c;
struct _cmd *t;
{
int i;
	if (c == '?') return(1);

	for (i= 2; *t-> name; ++t, ++i) {
		if (tolower(c) == tolower(*t-> name)) { 	/* in the list */
			if (allowedx(t-> locks,t-> priv)) 	/* not locked */
				return(i);			/* and OK priv */
		}
	}
	return(0);
}

/* Return true if the keys fit and the priv is high enough. */

allowedx(lock,prv)
long lock;
char prv;
{
	return(unlocked(lock) && (uval(CLR_PRV,CLR_PRVS) >= prv));
}

/* Return true if the keys fit and the priv is high enough in the given
area structure, plus is the right task ID. */

allowed(a)
struct _area *a;
{
	if (a-> taskid && (a-> taskid != taskid)) 	/* taskID specified */
		return(0);				/* and doesnt match */
	if (! unlocked(a-> locks)) return(0);		/* keys dont fit */
	return(a-> priv <= uval(CLR_PRV,CLR_PRVS));	/* priv. OK */
}

/* Return true if the callers keys unlock this lock. All the magic crud
is to coerce Lattice to do logical ops with longs. */

unlocked(lock)
long lock;
{
unsigned ul,ll,uk,lk;		/* upper lock, lower lock, upper key, lower key */

	if (uval(CLR_PRV,CLR_PRVS) >= SYSOP) return(1);

	ul= (long) (lock >> 16L);		/* two 16 bit halfs */
	ll= lock;				/* so much for portability */

	uk= (long) (caller.keys >> 16L);		/* but its all in one place */
	lk= caller.keys;				/* so fuck it */

	uk &= ul;				/* locks & keys */
	lk &= ll;

	return ((uk == ul) && (lk == ll));	/* a key for each lock */
}

/* Output the highlight string; unfortunately we have to check to see if
the string is the same as the language string (CM0) because an undefined
string is not "" but CM0! */

void highlight() {
char *cp;
int col;

	cp= string(CM+223);			/* ptr to highlight string */
	if (cp == string(CM+0)) return;		/* oops undefined */

	col= column;				/* remember starting column, */
	mputs(cp);
	column= col;				/* make 0 length */
}

/* Turn off highlighting; same issue as above. */

void unhighlight() {
char *cp;
int col;

	cp= string(CM+225);			/* ptr to un-highlight string */
	if (cp == string(CM+0)) return;		/* oops undefined */

	col= column;				/* remember starting column, */
	mputs(cp);
	column= col;				/* make 0 length */
}

/* Print the default-command opening bracket. */

bracket() {
char *cp;
int col;

	cp= string(CM+18);			/* ptr to the string */
	col= column;				/* remember starting column, */
	mputs(cp);				/* display it */
	column= col;				/* make 0 length */
}

/* Print the default-command closing bracket. */

unbracket() {
char *cp;
int col;

	cp= string(CM+19);			/* ptr to the string */
	col= column;				/* remember starting column, */
	mputs(cp);				/* display it */
	column= col;				/* make 0 length */
}
