#include <fido.h>
#include <ascii.h>
#include <\drivers\driver.h>

/*
	Rolodex cum sysop utility

*/

#define TITLE "     Fido/FidoNet SYSOP Utility - Copyright Tom Jennings (23 May 90)"

extern char _spr_sepchar;	/* printf() global */

#define KEEP (1L << CLR_KEPS)	/* "keep record" bit */
#define STAMPED (1L << CLR_DELS)/* deleted bit in caller.stuff */

struct _clr caller;		/* caller record */
struct _fido fido;		/* Fido system file */

/* Global stuff */

unsigned recd;			/* current record, */
int usr;			/* index file handle, */
FLAG changed;			/* true if record has been changed */
unsigned _stack = 4000;		/* for autos and stack, */
char date[20];			/* date for purging */
char linebuff[SS];		/* inptu line buffer */

int rate;			/* satisfy OTR drivers */

main(argc,argv)
int argc;
char *argv[];
{
int i;
char *p,c;
char path[SS];
long m,a;

	allmem();
	fastfile(4);			/* enable buffered I/O */
	meminit();
	scrinit();			/* set screen stuff etc */
	place(bottom,0);
	for (i= lines; i--;) lconout('\n'); /* clear screen, */

	i= open("fido.sys",0);		/* get system stuff */
	if (i == -1) {
		cprintf("Missing Fido/FidoNet system file FIDO.SYS\r\n");
		exit(1);
	}
	read(i,&fido,sizeof(struct _fido));
	close(i);
	if (fido.fido_version != FIDOVER) {
		cprintf("\rWrong Fido/FidoNet or Sysop program version\r\n");
		exit(1);
	}
	_spr_sepchar= fido.sep_char;	/* set it globally */

	strcpy(path,fido.syspath);
	strcat(path,"caller.sys");	/* full filename */
	usr= open(path,2);	/* open it, */
	if (usr == -1) {
		usr= creat(path,2);
		clear();
		strcpy(caller.name,"Sysop User Name");
		strcpy(caller.city,"Fido Software, Box 77731,");
		strcpy(caller.pwd,"S.F. CA 94107, USA");
		caller.sum= strsum(caller.name);
		wrt();
		close(usr);
		usr= open(path,2);
	}
	if (usr == -1) {		/* usual error handling, */
		cprintf("\rCant open or create \"%s\"!\r\n",path);
		exit(1);
	}

	gtod(date);			/* todays date */
	menu();				/* paint the menu, */
	stat("");
	recd= 1;			/* open first record, */
	clear();			/* in case empty file! */
	get(recd);
	changed= 0;			/* obviously not changed yet */
	while (1) {
		display();		/* display record, */
		place(0,0);
		cputs("Command: "); 	/* prompt, etc */
		clreol();
		do c= lconin();
		while ((c == CR) || (c == ' '));
		process(c);		/* execute commands, */
	}
}
/* Process commands. */

process(c)
char c;
{
char work[SS],d,*p;
long n;
int recd_save,found,i;

	if (caller.version != CLRVER) {
		stat("WRONG VERSION CALLER.SYS or SYSOP.EXE!");
		changed= 0;
	}

	if (c == fido.bucks) c= '$';		/* so we can switch() it */

	switch(tolower(c)) {
		case '*':
			for (i= 9; i < bottom; i++) {
				place(i,0);
				clreol();
			}
			place(10,0);
			copr();
			place(0,0); cprintf("Hit any key: ");
			while (! keyhit());
			for (i= 10; i < bottom; i++) {
				place(i,0);
				clreol();
			}
			menu();
			break;

		case 'r':
			stat("Repair Record");
			sanity();
			menu();
			display();
			changed= 1;
			break;

		case 'c':
			stat("Cancel Changes");
			changed= 0;
			get(recd);
			break;

		case 'g':
			if (changed) upd();
			stat("Exit to DOS");
			close(usr);	/* close the file, */
			place(bottom,0); clreol();
			place(bottom - 1,0); clreol();
			exit(0);
			break;

		case 'k':
			stat("Add Keys");
			getfield(linebuff,"Keys to add (1 - 32, NONE): ",0,99,sizeof(linebuff),1);
			stolower(linebuff);
			p= linebuff;
			if (! *p) break;
			while (num_args(p)) {
				i= 0;			/* assume no - sign */
				if (*p == '-') {	/* if -nn, */
					++p;		/* turn key off, */
					i= 1;
				}
				d= atoi(p); 		/* get Key number */

				if (*p == 'a') caller.keys |= KALL;
				else if (*p == 'n') caller.keys= 0L;
				else if ((d > 0) && (d < 33)) {
					n= 1L; n <<= (d - 1);
					if (i) caller.keys &= ~n;
					else caller.keys |= n;
				}
				p= next_arg(p);
			}
			changed= 1;
			break;

		case 'n':
			stat("Set new Name");
			getfield(linebuff,"Name: ",0,99,sizeof(caller.name),0);
			if (! *linebuff) break;
			strcpy(caller.name,linebuff);
			strproc(work,linebuff,99);	/* make formatted version */
			caller.sum= strsum(work);	/* sum that */
			changed= 1;
			break;

		case 'a':
			stat("Set New Address");
			getfield(linebuff,"Address: ",0,99,sizeof(caller.city),0);
			if (! *linebuff) break;
			strcpy(caller.city,linebuff);
			changed= 1;
			break;

		case 'w':
			stat("Set new Password");
			getfield(linebuff,"Password: ",0,1,sizeof(caller.pwd),1);
			if (! *linebuff) break;
			strcpy(caller.pwd,linebuff);
			changed= 1;
			break;

		case '?':
			stat("HELP!");
			if (help()) menu();
			break;

		case 77 + 128:		/* IBM -> */
		case '=':		/* same key */
		case '+':
		case '>':
		case '.':
			if (changed) upd();
			stat("Next");
			if (get(recd + 1)) ++recd;
			else stat("Last One");
			break;

		case 75 + 128:		/* IBM <- */
		case '_':		/* same key */
		case '-':
		case '<':
		case ',':
			if (changed) upd();
			stat("Previous");
			if (recd > 1) {
				if (get(recd - 1)) --recd;
				else stat("At the Beginning");

			} else stat("At the Beginning");
			break;

		case 'i':
			if (changed) upd();
			stat("Input New");
			while (get(recd)) ++recd;	/* leave at last + 1 */
			clear();			/* clear it */
			wrt();				/* write empty record, */
			changed= 1;			/* needs writing */
			break;

		case 79 + 128:		/* IBM end */
		case 'e':
			if (changed) upd();
			stat("End of file");
			for (recd= 0, i= 1000; i > 0; i /= 10) {
				while (get(recd + i)) {
					sprintf(linebuff,"Recd #%d",recd);
					stat(linebuff);
					recd += i;
				}
			}
			get(recd);
			break;
	
		case 71 + 128:		/* IBM home */
		case 'b':
			if (changed) upd();
			stat("Beginning of file");
			recd= 1;
			get(recd);
			break;

		case 'l':
			if (changed) upd();
			stat("Locate a Name");
			getfield(linebuff,"Name or partial name to search for: ",0,1,sizeof(caller.name),1);
			if (! *linebuff) break;
			find(linebuff);
			break;

		case 's':
			if (caller.stuff & KEEP) {
				stat("Can't STAMP records marked KEEP");
				break;
			}
			caller.stuff ^= STAMPED;
			changed= 1;
			display();
			break;

		case 'j':
			caller.stuff ^= KEEP;
			changed= 1;
			display();
			break;

		case '!':
			if (changed) upd();
			stat("Purge Stamped");
			purge();
			break;

		case 'o':
			stat("Stamp callers by Age");
			place(0,0); clreol();
			getfield(linebuff,"Stamp callers how many days old: ",0,1,10,1);
			i= atoi(linebuff);
			if (i < 2) {
				stat("Aborted");
				break;
			}
			if (changed) upd();
			mark_old(i);
			break;

		case '#':
			stat("Position to Record Number");
			getfield(linebuff,"Enter Record Number: ",0,1,10,0);
			i= atoi(linebuff);
			if (i == 0) break;
			if (changed) upd();
			if (get(i)) recd= i;
			else stat("No such Record");
			break;

		case 'p':
			stat("Change Privilege");
			getfield(linebuff,"Priv Level: (0 - 4, 7=Sysop): ",0,1,sizeof(linebuff),1);
			if (! *linebuff) break;
			i= atoi(linebuff);
			if ((i <= EXTRA) || (i == SYSOP))
				setuval(atoi(linebuff),CLR_PRV,CLR_PRVS);
			changed= 1;
			break;

		case '$':
			stat("Set Credit (clear debit)");
			sprintf(work,"Credit to add (%c0 - %c327, NONE): ",fido.bucks,fido.bucks);
			getfield(linebuff,work,0,99,sizeof(linebuff),1);
			p= linebuff;
			if (! *p) break;
			if (*p == 'N') {
				caller.credit= 0;
				p= next_arg(p);
			}
			i= atoi(p) * 100;
			caller.credit+= atoi(p) * 100;
			caller.debit= 0;
			changed= 1;
			break;

		case 'm':
			stat("Set default Message Area Number");
			sprintf(work,"Message Area Number (1 - %d): ",fido.marea_max);
			getfield(linebuff,work,0,1,sizeof(linebuff),1);
			if (! *linebuff) break;
			i= atoi(linebuff);
			if ((i > 0) && (i <= fido.marea_max)) {
				caller.msg= i - 1;
				changed= 1;
			}
			break;

		case 'f':
			stat("Set default File Area Number");
			sprintf(work,"File Area Number (1 - %d): ",fido.farea_max);
			getfield(linebuff,work,0,1,sizeof(linebuff),1);
			if (! *linebuff) break;
			i= atoi(linebuff);
			if ((i > 0) && (i <= fido.farea_max)) {
				caller.files= i - 1;
				changed= 1;
			}
			break;
	}
}

/* Perform a sanity check on the user record, fix any fields that are
messed up. */

sanity() {
int i;

	caller.version= CLRVER;
	strfix(caller.name,sizeof(caller.name),99);
	caller.sum= strsum(caller.name);
	strfix(caller.city,sizeof(caller.city),99);
	strfix(caller.pwd,sizeof(caller.pwd),1);
	gtod(caller.date);
	for (i= 0; i < MAXLREAD; i++) {
		if (caller.lastmsg[i].area > fido.marea_max) {
			caller.lastmsg[i].area= 255;
			caller.lastmsg[i].msg= 0;
		}
	}

	switch (uval(CLR_PRV,CLR_PRVS)) {
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 7:
			break;

		default:
			setuval(NORMAL,CLR_PRV,CLR_PRVS);
			break;
	}
	if (caller.msg >= fido.marea_max) caller.msg= fido.defmnbr;
	if (caller.files >= fido.farea_max) caller.files= fido.deffnbr;
	caller.time= 0;
	caller.dnld= 0;
	caller.upld= 0;
	caller.dnldl= 0;
	caller.tleft= 0;
	caller.baud= 0;

	caller.stuff= 0L;			/* clear all bits */
	setuval(NOVICE,CLR_HLP,CLR_HLPS);	/* set defaults, */
	setuval(80,CLR_WID,CLR_WIDS);		/* screen width, */
	setuval(24,CLR_LEN,CLR_LENS);		/* screen length */
}

/* Replace any control characters in a string with @'s, and make
strings into Fido format. */

strfix(s,len,args)
char *s;
int len,args;
{
char *cp,word[SS],buff[256];

	strcpy(buff,s);				/* make a copy, */
	buff[len - 1]= NUL;			/* truncate, */

	for (cp= buff; *cp; cp++) {		/* replace control codes */
		*cp &= 0x7f;			/* force ASCII */
		if (*cp < ' ') *cp= '@';	/* DELs, etc */
		if (*cp == 0x7f) *cp= '@';
	}

	*s= NUL; cp= buff;
	while (args-- && *cp) {
		cpyarg(word,cp);		/* copy out a word, */
		cp= next_arg(cp);		/* next ... */
		stolower(word);			/* all lower case, */
		word[0]= toupper(word[0]);	/* first char each word upper case */
		strcat(s,word);			/* add it in, */
		if ( args && *cp) strcat(s," "); /* if more words, add seperators */
	}
}

/* Search for the specified name. */

find(pattern)
char *pattern;
{
int recd_save,found,i;
char work[SS];

	recd_save= recd;			/* save current, */
	found= 0;				/* not found yet */
	stat("Wait ... or ESCape to abort");
	while (!found) {			/* stop if at start */
		if (get(++recd) == 0) {		/* wrap to BOF, */
			if (recd == 1) break;	/* empty file */
			recd= 0;		/* (preincrement) */
			continue;
		}				/* if we hit the starting */
		if (recd == recd_save) break;	/* point stop */
		if (keyhit() == ESC) break;
		if (substr(pattern,caller.name)) {
			found= 1;
			break;
		}
	}
	if (found) stat("Found It");
	else stat("Cant Find It");
}
/* Mark all records that are over n days old. */

mark_old(n)
int n;
{
int recd_save,marked,i;
char work[SS];

	marked= 0;
	recd_save= recd;			/* save current, */
	stat("Wait ...");
	while (1) {
		if (get(++recd) == 0) {		/* wrap to BOF, */
			if (recd == 1) break;	/* empty file! */
			recd= 0;		/* (preincrement) */
			continue;
		}				/* if we hit the starting */
		if (recd % 100 == 0) {
			sprintf(work,"Recd #%d",recd);
			stat(work);
		}
		if (recd == recd_save) break;	/* point stop */
		if (keyhit() == ESC) break;

		if ((days(caller.date,date) > n) && (caller.credit == 0) && (uval(CLR_PRV,CLR_PRVS) < SYSOP)) {
			caller.stuff |= STAMPED;
			++marked;

		} else caller.stuff &= ~STAMPED; /* else unmark */

		put(recd);
	}
	sprintf(work,"Marked %u as deleted",marked);
	stat(work);
}
/* Purge all deleted records. Works by copying to a temp file, except the
deleted ones, then renaming. */

purge() {

int t,o,n;
char s[SS];

	t= creat("caller$$.$$$",2);
	if (t == -1) {
		stat("Cant create temp file CALLER$$.$$$!");
		return;
	}
	o= creat("caller.old",2);
	if (o == -1) {
		stat("cant create old caller file CALLER.OLD!");
		return;
	}
	n= 0;
	recd= 0;
	while (get(++recd)) {
		if (recd % 100 == 0) {
			sprintf(s,"Recd #%d",recd);
			stat(s);
		}
		if (caller.credit || 				/* money left */
		    ((caller.stuff & STAMPED) == 0L) || 	/* not stamped */
		        (uval(CLR_PRV,CLR_PRVS) >= SYSOP) ||	/*  or high priv */
		    (caller.stuff & KEEP)) {			/* or marked KEEP */
			caller.stuff &= ~STAMPED;		/* un-stamp */
			if (write(t,&caller,sizeof(struct _clr)) != sizeof(struct _clr)) {
				stat("\07Disk Full");
				close(t);			/* write to new file */
				close(o);
				return;
			}
		} else {
			caller.stuff &= ~STAMPED;		/* unmark deleted */
			if (write(o,&caller,sizeof(struct _clr)) != sizeof(struct _clr)) {
				stat("\07Disk Full");
				close(t);			/* to deleted file */
				close(o);
				return;
			}
			++n;
		}
	}
	close(t);
	close(o);
	close(usr);
	delete("caller.bak");
	rename("caller.sys","caller.bak");
	rename("caller$$.$$$","caller.sys");
	sprintf(s,"Purged %u recds",n);
	stat(s);
	usr= open("caller.sys",2);
	if (usr == -1) {
		stat("Cant reopen CALLER.SYS!");
		exit();
	}
	recd= 1;
	get(recd);
	changed= 0;
}

/* Local function: if the record has changed, write it out. */

upd() {
	if (changed) {
		stat("Saving First");
		put(recd);
		changed= 0;
	}
}
/* Display the menu. */

menu() {
int i,j,n;

	i= 1;
	place(++i,2); cprintf("L)ocate           N)ame        M)sg Area      S)tamp On/Off      ? Help!");
	place(++i,2); cprintf("I)nput New        W Password   F)ile Area     J Keep On/Off      > Next");
	place(++i,2); cprintf("# Record Number   A)ddress     %c Credit       O)ld-Stamp         < Previous",fido.bucks);
	place(++i,2); cprintf("R)epair Record    K)eys                       ! Purge-Stamped    B)eginning");
	place(++i,2); cprintf("C)ancel Changes   P)rivilege   G)oodbye!      * Copyright        E)nd");
	place(++i,2); cputs(TITLE);
	box(1,++i);
	place(i,32); cputs(" COMMANDS ");

	j= ++i;
	place(++i,8); cputs("NAME:"); clreol();
	place(++i,8); cputs("PASSWORD:"); clreol();
	place(++i,8); cputs("ADDRESS:"); clreol();

	place(++i,8); cputs("TIMES CALLED:      "); 
	  place(i,28); cputs("LAST CALLED:");  clreol();
	place(++i,8); cputs("MSG AREA:         FILE AREA:"); clreol();
	place(++i,8); cputs("UPLOADED:         DOWNLOADED:"); clreol();
	place(++i,8); cputs("ACCUM. D/L:       ACCUM. TIME:"); clreol();
	place(++i,8); cputs("CREDIT:           DEBIT:"); clreol();
	place(++i,8); cputs("PRIVILEGE:"); clreol();
	place(++i,8); cputs("KEYS:"); clreol();
	place(++i,8); clreol();			/* 2nd line of keys */
	box(j,++i);
	place(i,30); cputs(" CALLER RECORD ");
}
/* Display a status string. */

stat(s)
char *s;
{
int i;
	place(1,10); lconout(' '); cputs(s); lconout(' ');
	cputs(graph_on);
	for (i= 30; --i;) lconout(horizbar);
	cputs(graph_off);
}
/* Display the current record on the screen. */

display() {

int i,j,t;

	i= 10;
	place(i,3); cprintf("#%-4u",recd); 
	place(i,14); cprintf("%-20s",caller.name);  
	  place(i,80 - 24); 
	  if (caller.version != CLRVER) {
						 cputs("DAMAGED/WRONG VERSION");
	  } else {
		if (caller.stuff & KEEP)         cputs("(KEEP)               ");
		else if (caller.stuff & STAMPED) cputs("(STAMPED)            ");
		else 				 cputs("                     ");
	  }

	place(++i,18); cprintf("%-20s",caller.pwd); 
	place(++i,18); cprintf("%-36s",caller.city);  

	place(++i,22); cprintf("%-6,u",caller.times); 
	  place(i,41); cprintf("%20s",caller.date);
	place(++i,18); cprintf("%-6,u",caller.msg + 1);
	place(i,37); cprintf("%-6,u",caller.files + 1);

	place(++i,17); cprintf("%-6,uK",caller.upld);
	place(i,37); cprintf("%-6,uK",caller.dnld);
	place(++i,18); cprintf("%-6,uK",caller.dnldl);
	place(i,39); cprintf("%-6,u min.",caller.time);
	place(++i,16); cprintf("%-7s",bucks_str(caller.credit));
	place(i,33); cprintf("%-7s",bucks_str(caller.debit));
	place(++i,19); cprintf("%-6,u",uval(CLR_PRV,CLR_PRVS));
	place(++i,14); dkeys(0);			/* display keys 1 - 16 */
	place(++i,14); dkeys(16);			/* then 17 - 32 */
}
/* Display caller keys */

dkeys(o)
int o;
{
int i,t;

	t= 0;
	for (i= 16; i--;) {
		if ((caller.keys >> o) & 1) {		/* if a key bit set, */
			cprintf("%d ",o + 1);		/* display its number */
			t += 2;				/* column position */
			if (o > 9) ++t;			/* two digit number ... */
		}
		++o;					/* next key ... */
	}
	while (t++ < 64) lconout(' ');			/* make it 64 cols */
}

/* Clear the record. */

clear() {
int i;
char *cp;

	cp= (char *) &caller;			/* clear out the record */
	for (i= sizeof(struct _clr); i--;) *cp++= NUL;

	caller.version= CLRVER;
	setuval(NOVICE,CLR_HLP,CLR_HLPS);	/* set defaults, */
	setuval(80,CLR_WID,CLR_WIDS);		/* screen width, */
	setuval(24,CLR_LEN,CLR_LENS);		/* screen length */
	setuval(NORMAL,CLR_PRV,CLR_PRVS);	/* priv level */
	gtod(caller.date);			/* when created */
}
/* Position to record. */

posn(n)
unsigned n;
{
long p;

	p= --n;
	p *= sizeof(struct _clr);
	lseek(usr,p,0);
}
/* Read the specified record, leave the file pointer pointing to it.
Return 0 if not found. */

get(n)
int n;
{
int cnt;

	posn(n);
	cnt= read(usr,&caller,sizeof(struct _clr));
	posn(n);
	return(cnt == sizeof(struct _clr));
}
/* write the record out to the current position. Leaves the pointer at 
the current record. */

put(recd)
unsigned recd;
{
int cnt;

	posn(recd);				/* reseek */
	return(wrt());
}
/* Write the current record at the current file position. Returns 0 if write
error. */

wrt() {
	return(write(usr,&caller,sizeof(struct _clr)) == sizeof(struct _clr));
}

/* Help. Display the help screen for the specified command. */

help() {
char c,line[200];
int h,cnt,i,t,b;


	h= open("sysop.hlp",0);
	if (h == -1) {
		stat("Help file missing, can't help you");
		return(0);
	}

	place(0,0); cprintf("Help with what command: ");
	c= lconin();				/* easier testing */
	c= tolower(c);
	lconout(c);
	if (c == fido.bucks) c= '$';		/* for help file use */

	stat("Wait ..."); 

	while (cnt= rline(h,line,sizeof(line))) {
		if ((line[0] == '\\') && (tolower(line[1]) == c)) break;
	}
	if (cnt == 0) {
		stat("Cant Help with that");

	} else {
		place(0,0); clreol();
		t= 8;					/* top line, */
		for (b= t + 1; b < bottom; b++) {
			if (! rline(h,line,sizeof(line) - 1)) break;
			line[60]= NUL;
			if ((*line == NUL) || (*line == '\\')) break;
			place(b,0); cputs("        "); clreol(); cputs(line); 
		}
		box(t,b);
		for (i= b + 1; i <= bottom; i++) {
			place(i,0); clreol();
		}
	}
	close(h);
	place(0,0); cputs("Hit any key to continue: "); 
	while (keyhit());
	lconin();

	place(t,0); clreol();
	place(b,0); clreol();
	return(cnt);
}

/* Fielded input. Inputs are:

string		where to put the input,
prompt		the prompt string,
min		minimum number of words,
max		maximum number of words,
width		maximum string size,
cap		Remove extra spaces, capitalize, etc. Works
		only if one or two words.
*/

getfield(string,prompt,gmin,gmax,width,cap)
char *string,*prompt;
int gmin,gmax,width,cap;
{
char buff[82];
int i;
char *p;
	while (1) {
		place(0,0);
		cprintf("%s",prompt);
		clreol();
		getstring(buff);
		if (strlen(buff) > width) {
			stat("Too long");
			continue;
		}

		if ((num_args(buff) > gmax) || (num_args(buff) < gmin)) {
			if (gmin != gmax) {
				sprintf(buff,"%d to %d words",gmin,gmax);
				stat(buff);
			} else  {
				sprintf(buff,"%u word(s)",gmin);
				stat(buff);
			}
			continue;
		}
		if (cap) {
			p= skip_delim(buff);
			stolower(p);			/* make all lower case, */
			cpyarg(string,p);		/* copy in 1st name, */
			string[0]= toupper(string[0]);	/* capitalize, */
			p= next_arg(p);			/* skip that one, */
			while (num_args(p)) {
				strcat(string," ");	/* separate with a space, */
				*p= toupper(*p);
				cpyarg(&(string[strlen(string)]),p); /* add 2nd name, */
				p= next_arg(p);		/* ptr to next name, */
			}
		} else
			strcpy(string,buff);
		break;
	}
}

/* Display money. Since this returns a string, we use a static string
array and can make no more than 4 at a time. */

bucks_str(b)
unsigned b;
{
static char s[4][8],i;		/* string "$327.67", 4 each, plus index, */

	if (++i > 3) i= 0;	/* next string ... */

	sprintf(s[i],"%c%d%c%02d",
		fido.bucks,	/* dollar char, */
		b / 100,	/* dollars, */
		fido.dec_char,	/* the separator, */
		b % 100);	/* cents, */
	return(s[i]);
}

/* Return true if s1 is contained within s2. This tends to match dissimilar
items, but is fast. The 0x5f mask is a crude toupper(). */

substr(s1,s2)
char *s1,*s2;
{
char *p,*q;

	while (*s2) {
		p= s1; q= s2;
		while ((((*p & 0x5f) == (*q & 0x5f)) || (*p == '?')) && *p) {
			++p; ++q;
		}
		if (*p == NUL) return(1);
		++s2;
	}
	return(0);
}
/* Draw a box, given the four corners. It is assumes that it is a
rectangle that we're trying to draw. */

box(topline,botline)
int topline,botline;
{
int i;
int topcol,botcol;

	cputs(graph_on);
	topcol= 0;
	botcol= cols - 1;

	place(topline,topcol);
	for (i= botcol - topcol; i--;) lconout(horizbar);
	place(botline,topcol);
	for (i= botcol - topcol; i--;) lconout(horizbar);

	for (i= topline; i <= botline; i++) {
		place(i,topcol);
		lconout(vertbar);
		place(i,botcol);
		lconout(vertbar);
	}
	place(topline,topcol); lconout(ulcorner);
	place(topline,botcol); lconout(urcorner);
	place(botline,botcol); lconout(lrcorner);
	place(botline,topcol); lconout(llcorner);

	cputs(graph_off);
}

/* Return the value from the caller bit record. */

uval(mask,shift)
int mask,shift;
{
	return((caller.stuff >> shift) & mask);
}

/* Set a field in the caller bit record. */

setuval(val,mask,shift)
int val,mask,shift;
{
long n;

	n= mask; n <<= shift;			/* we NEED long arith */
	caller.stuff &= ~n;			/* remove old value */

	n= val & mask; n <<= shift;		/* set it THEN shift it */
	caller.stuff |= n;			/* add in new value */
}


/* Copy up to N words from input to output, converting to fixed format
as we go. */

strproc(out,in,n)
char *in,*out;
int n;
{
	while (n--) {
		cpyatm(out,in);			/* copy a word, */
		stolower(out);			/* make it lower case */
		*out= toupper(*out);		/* first is upper case */

		in= next_arg(in);		/* point to next input word */
		if (n && *in) strcat(out," ");	/* if more to follow, add a space */
		while (*out) ++out;		/* point to end of string */

		if (! *in) break;		/* check last to handle null input */
	}
}

/* Create the checksum of a string. */

strsum(s)
char *s;
{
int n;

	for (n= 0; *s;) n += *s++;
	return(n);
}

error(s)
char *s;
{
	cprintf("%s\r\n",s);
}
