/******************************************************************************
 *
 * 	METAL library functions	for VERSION 1.20a
 *
 *	FILE: HMLIB.C version 1.20a
 *
 *
 *			Copyright (c) 1984  Tim Gary
 *			    Delphi Data Systems
 *			    All rights reserved.
 *
 *
 *	Common utility finctions used in METAL, MENTER and MUTIL
 *
 ******************************************************************************
 *
 * 1.20a 11/04/84  Counters routines made less dependant upon right info.
 * 1.10e 11/02/84  Added to pcounters routine, now wil write to specified file
 *		  if arg!=0.  Put writestat routine here, for easy access.
 * 1.10e 10/30/84  Parsing routine fixed to zap trailing blanks in ask func.
 *		  Future note: CAN'T change in getline!!!!!!!!
 *
 * 1.10c 10/0x/84  Hayes clock date order changed to reasonable mm/dd/yy form.
 *
 * 1.10b <none>
 * 1.10a  8/31/84  Overlay stuff started.
 *
 * 1.01a  6/27/84  Multi user stuff inserted.
 * 1.01a  6/10/84  Setup for Aztec C 1.06.  CCS clock stuff added.
 *
 * 1.0c   5/17/84 Added Hayes clock support.
 *
 * 1.0b   4/20/84 (continued) Added usindex() (index ignoring case), distext()
 * 1.0b   4/14/84 (continued) Findbye routine setup..
 * 1.0b	  4/13/84  Added Upper/lower case routines.  Added Novice user help.
 *		Putchar returns char typed at [More] prompt for checking.
 *		Removed several MENTER only functions (putcaller, writestat..)
 *		External height variable used for screen height now.
 *
 * 1.0a	  1/25/84  Added terminal height code.  Now pauses with [more] prompt
 *
 * 1.0	  1/17/84  Version 3.0a new format of files.
 *
 *****************************************************************************/

#include "xpm.h"
#include "hmh.h"
#include "hmconfg.h"
#include "ctype.h"

#define dgetchar getchar


/********************************
 * Return index into type table *
 ********************************/

get_type(t_char)
 char t_char;
{
register int i;

for (i=0; O.user_type[i].type!='\0'; i++) 
	if (O.user_type[i].type==t_char) break;

if (O.user_type[i].type=='\0') --i;	/* return last real type */
return i;
}


/********************************
 * type a file, stop on a break *
 ********************************/

type(filename)
 char *filename;
{
FILE *fil;
register int cfast;
int c;	

if ((fil=open(filename,F_RD | F_UNLOCK))==NULL) return ERROR;  /* open text file for read */

novhelp();	/* help novice if he is one */

read(fil,1);	/* getc doesn't do this initially, sigh */
while ( (cfast=getc(fil))!=ERROR && cfast!=EOF )
		{
		if (cfast!='\r') if (bcputchar(cfast)==ERROR) break;
		*(char *)O.RESETDRIVE;
		}

close(fil);

return NULL;
}


/*******************************************************
 * display text from pointer to array of char pointers *
 *   (eg   static char *it_hlp[] = { "1","2","3",0 }; )*
 *******************************************************/

dis_text(sp)
 register char **sp;
{
while (*sp && (send(*sp++)!=ERROR));
}


/* A one liner to help novices out on the system */

novhelp()
{
if (user.parm.expert==POFF)
	send("\nControl-K to cancel, Control-S to pause.\n");
}


/* Regular old putchar, but checks for break (^K, etc..) chars. 
   Returns ERROR if one hit, else it returns the char		*/

bcputchar(ch)
 int ch;
{
register int a;

if (!breakkey())
	{
	if ( ((a=toupper(putchar(ch)))=='N') || ((a & 0x1f)==0xb) ) a=ERROR;
	}
  else a=ERROR;

return a;
}


send(s)		/* send string while checking for break */
register char *s;
{
while (*s) if (bcputchar(*s++)==ERROR) return ERROR;;
return 0;	/* else ok */
}

/****************************************
 * Print string to printer (LST:)	*
 ****************************************/

print(str)
 register char *str;
{
while (*str) bdos(5,*str++);
}


/********************************
 * check for ^k ^x, ^s, etc..	*
 ********************************/

breakkey()		/* check for break */
{
register int key;

if (key=bdos(6,0xff))
	{
	if (((key & 0x1f)==0xb)		/* stop if ^K, ^X, etc.. */
		|| ((key & 0x1f)==0x18))
		{
		putchar('\n');
		return ERROR;
		}
	if ((key & 0x1f)==0x13)	/* pause if ^S */
		getd();
	globalchar=key;		/* save the char for later reference */
	}
return 0;	/* none pressed */
}


/******************************************************************************
 * ask: get string after prompting user.
 *
 *	question= prompt string (will NOT be printed if ';' used
 *		to get mult. commands/options..
 *	str	= place to put string
 *	maxchars= max # of chars to be accepted from terminal
 *	options	= following BITs that may be combined:
 *		UPLOW	: accept upper/lower case input
 *		UP	: upper case conversion is made
 *		NOECHO	: turn off display of chars as they are entered
 *		NUMBER	: a 'position' number is output instead of chars typed
 *		MSGLINE	: ONLY FOR getl(*).. for it to accept MAXMSGLINE chars
 *
 *****************************************************************************/

ask(question,str,maxchars,options)
 char *question,*str;
 int maxchars;
 register char options;
{
register char *temp,*temp1;

if ((strloc==0) || (glbstr[strloc]=='\0'))
	{
	send(question);				/* ask the question */
	getl(glbstr,options);			/* get the line */
	strloc=0;				/* make sure of this */
	}

/* now that we have line input	*/

temp1=0xffff;			/* initial not found flag */
temp=index(&glbstr[strloc],';');
if (temp==0) temp=0xffff;	/* convert 0 to ffff for easy compare */

if (sepstr)
	{
	temp1=index(&glbstr[strloc],sepstr);
	if (temp1==0) temp1=0xffff;
	}
if (temp!=temp1) /* if not both 0xffff */
	temp<temp1 ? (*temp='\0') : (*temp1='\0',temp=temp1);

if (strlen(&glbstr[strloc])>=maxchars)
	glbstr[strloc+maxchars]='\0';		/* same here */

if (options & UP) upcase(&glbstr[strloc]);	/* all upper case if spec'd */

strcpy(str,&glbstr[strloc]);		/* copy it to where it's wanted */

if (temp!=0xffff) strloc=(temp-glbstr)+1;	/* updates strloc if ';' */
	else strloc=0;				/* else start over */

	/* kill trailing blanks */
/* for (temp=str+strlen(str); temp!=str && isspace(*temp); *(temp--)='\0'); */

}	/* ask */


/* read a line of input from console */

getl(s,options)
 char *s,options;
{
register char c;
register int llen;
char *temp;
register int count;

if (options&MSGLINE) llen=O.MAXMSGLINE;
	else llen=MAXLINE;
temp=s; count=0; s=temp;

if ((user.parm.bell)==PON) putchar(7);	/* ring bell */
while ((c=getd()) != '\n' && c !='\r' )
	{
	if (c=='\b' || c==0x7f)
		{		/* backspace or DEL */
		if (!count) continue;
		--count;
		--s;
		if (!(options & NOECHO) || (options & NUMBER)) send("\b \b");
		continue;
		}
	if (c>31)		/* tab goes in directly */
		{
		if (count<llen)
			{
			*s++=c;
			++count;
			}
			else c=0;	/* don't echo */
		if (count>=llen-4)
			putchar(7);
				
		}
	if (c==9)
		{
		if (count+16<llen)
			for (c=0;c<8;++c,++count) *s++=' '; /* fill spaces */
		c=9;
		}
	if ((c==24) || (c==21))
		{		/* ^X or ^U cancels line */
		send("#\n");
		count=0; s=temp;	/* clear buffer (effectively) */
		}
	else if (!(options & NOECHO))
		{
		if (c==9) for (c=0; c<8; c++) putchar(' '); /* tab */
		  else if (c>31) putchar(c);
		}
	     else if ((options & NUMBER) != 0) putchar( (count % 10)+'0');

	} /* while */
*s='\0';	/* end of string */
putchar('\n');
return temp;
}


static char line=1;	/* current line (relative to last input)	*/

/**************
 * New putchar that counts columns
 *************/

putchar(ch)
 register int ch;
{
static int pos=0;	/* position on line */
static char lastchar='\0';
register int i,c;

c=NULL;	/* returned character for MORE prompt */

if (ch=='\t')		/* check for tab and expand */
	{
	for (i = 0;  i <= (pos+8)-((pos+8) & 0xf8);  i++)
		putchar(' ');
	return;
	}

if (ch=='\n') { pos=0; line++; bdos(6,'\r'); bdos(6,ch); }
 else { if (ch>=' ') pos++; bdos(6,ch); }

if (ch=='\r') pos=0;	/* reset posistion on line to zip */

if (line>=height && height!=0)
	{
	line=1;
	printf("[more]");
	c=getd();		/* get char which clears lines */
	printf("\r      \r");
	}

return c;
} /* new putchar */


/* new getchar to clear line variable, etc..	*/

getchar()
{
register int cfast;
putchar((cfast=getd()));	/* freaky eh? */
return cfast;
}

getd()
{
register int cfast;
line=1;
while(!(cfast=bdos(6,0xff)));
return cfast;
}


/************************************************************************
 * To add your own clock routine, define the RTC clock setup to COMPUPRO or
 * HAYES then change the routine below to read your clock board, and put the
 * time and date (formated hh:mm:ss and mm-dd-yy) in the variables
 * (character pointers) time and date.	 
 ************************************************************************/

/***********************************************
 * CompuPro/xxx and Hayes read clock functions *
 ***********************************************/

readclock()
{

#ifndef NOSS1
	/* to make code smaller.. allow only certain clocks... */

/* compupro/ccs variables */
static char x[2] = "\0";
static int p[12] = { 5,4,3,2,1,0,10,9,8,7,12,11 };	/* commands */

register int i;

*time=*date='\0';	/* null strings */

if (O.RTC==COMPUPRO /* || O.RTC==CCS */ )
  {
  for (i=0; i<12; i++)
	{

/*	if (O.RTC==CCS)
		{
		out((char)O.CLKCMD,p[i]|0x40);		 hold digit
		while (in((char)O.CLKDATA) & 0x40);	 wait til held
		}	*/

	out((char)O.CLKCMD,p[i]+0x10);
/* if (O.RTC==CCS) while (in((char)O.CLKDATA) & 0x80); *//* ok data wait */
	*x=in((char)O.CLKDATA)+'0';
/* if (O.RTC==CCS) out(O.CLKCMD,0); */	/* clear wait flag */
	if (i==0) (*x)-=8;
	if (i<6)  {
		if ( (i==2) || (i==4) ) strcat(time,":");
		 /* add a ':' above, for looks */
		strcat(time,x);	/* and always add the digit */
		}
	else	  {
		if ( (i==8) || (i==10) ) strcat(date,"-");
		strcat(date,x);
		}
	} /* for loop */
  } /* end of compupro routine */
  else

#endif		/* no ss1/ccs */

#ifndef NOHAYES

     if (O.RTC==HAYES)		/* hayes chronogragh */
	{
	char tdate[DATELEN+1];
	outpstr(O.CLKCMD,O.CLKOMASK,O.CLKDATA,"\r\r\r\r\rATVD-\r");
	inpstr(O.CLKCMD,O.CLKIMASK,O.CLKDATA,'\r',2,date);
	outpstr(O.CLKCMD,O.CLKOMASK,O.CLKDATA,"\r\r\r\r\rATVT:\r");
	inpstr(O.CLKCMD,O.CLKIMASK,O.CLKDATA,'\r',2,time);
	for (i=0; i<500; i++);
	outpstr(O.CLKCMD,O.CLKOMASK,O.CLKDATA,"\r\r\r\r\rATRD\r");
	inpstr(O.CLKCMD,O.CLKIMASK,O.CLKDATA,'\r',9,tdate);	/* get date */
	outpstr(O.CLKCMD,O.CLKOMASK,O.CLKDATA,"\r\r\r\r\rATRT\r");
	inpstr(O.CLKCMD,O.CLKIMASK,O.CLKDATA,'\r',9,time);	/* get time */
	tdate[2]='\0';  tdate[8]='\0';
	sprintf(date,"%s/%s",tdate+3,tdate);	/* change to mm/dd/yy */
	} /* Hayes.. */

#endif		/* nohayes */

} /* readclock */


getdate()
{
if (O.RTC!=NOCLOCK) readclock();
  else askdate();
return date;
}


/*************************************
 * output string to port specified.  *
 *   expected is:  1) status port    *
 *   2) data out mask  3) data port  *
 *   4) the string to be sent        *
 * note: nulls may not be sent via   *
 * this routine!!  0 terminates str  *
 * if data/status>255 then memory    *
 * mapped i/o is performed	     *
 *************************************/

outpstr(stat,stmask,data,string)
 unsigned stat,data;
 char stmask;
 register char *string;
{
while (*string) outchar(stat,stmask,data,*string++);
}


outchar(stat,stmask,data,c)
 register unsigned stat,data;
 char stmask,c;
{
if (stat>255)
	{
	while (!( (*(char *)stat & stmask)));
	*(char *)data=c;
	}
  else {
	while (!( in((char)stat) & stmask));
	out((char)data,c);
	}
}


/********************************
 * input string from port...	*
 * parms:  1) status port	*
 * 2) status mask  3) data port	*
 * 4) end of string char	*
 * 5) max chars over all	*
 * 6) string pointer to place	*
 * string			*
 *  note: same as above for 	*
 * memory mapped ports..	*
 ********************************/

inpstr(stat,stmask,data,eos,max,string)
 unsigned stat,data,max;
 char stmask,eos;
 register char *string;
{
char inpchar();

*(string+max)='\0';	/* make sure of termination */

while (max--)
	{
	*string=inpchar(stat,stmask,data) & 0x7f;
	if (*(string++)==eos)
		{
		*(string-1)='\0'; /* terminate, and abort */	
		break;
		}
	}
} /* inpstr */


char inpchar(stat,stmask,data)
 register unsigned stat,data;
 char stmask;
{
unsigned to=50000;
while ( to-- && !( ((stat>255) ? *(char *)stat : in((char)stat)) & stmask) );
return ( (data>255) ? *(char *)data : in((char)data) );
}


/* get date from keyboard (for those without a clock) */

askdate()
{
char temp[2];

strcpy(time,"00:00:00");
sprintf(buffer,"Is %s today's date (y/n)?",*date=='\0' ? "00/00/00" : date);

ask(buffer,temp,1,UP);
if (*temp=='Y') return date;
do  ask("What's todays date (mm/dd/yy)? ",date,DATELEN,UP);
	while (strlen(date)!=DATELEN-1);

} /* askdate */


/************************************************************************
 * String functions...							*/


/* convert string to all upper case */

upcase(str)
register char *str;
{
for (;*str;++str) *str=toupper(*str);
}


/* check for a space in the given string  (returns 1 if space, else 0) */

isspc(str)
register char *str;
{
while(*str) if (*str++<=' ') return TRUE; /* return TRUE if space/cntrl */
return FALSE;	/* else FALSE returned */
}


/* capitalize a string (all words in the string */

capstr(str)
 char *str;
{
register char *tp;

*str=toupper(*str);	/* first char is easy */

for ( tp=str; tp<str+strlen(str); ++tp )
	{
	if (*tp==' ') if (*(tp+1)!='\0') *(tp+1)=toupper(*(tp+1));
			else break;
	}
}


/* case independent string compare function */

ustrcmp(s1,s2)
 register char *s1,*s2;
{
for ( ; toupper(*s1)==toupper(*s2); s1++, s2++)
	if (*s1=='\0') return (toupper(*s1)-toupper(*s2));
			/* if at end of first string, test for=length/return */

return (toupper(*s1)-toupper(*s2));
}

/* case independent string compare function */

ustrncmp(s1,s2,n)
 register char *s1,*s2;
 register int n;
{
for ( ; (toupper(*s1)==toupper(*s2)) && (n>0); s1++, s2++, n--)
	if (*s1=='\0') return (toupper(*s1)-toupper(*s2));
			/* if at end of first string, test for=length/return */

return (n ? (toupper(*s1)-toupper(*s2)) : 0);
}


/*****************************
 * Same as index(s,c), but with two strings
 *****************************/

sindex(s1,s2)
 register char *s1,*s2;
{
 register char *sp;

for (sp=s2; strlen(s1)<=strlen(sp); sp++)
	if (!strncmp(s1,sp,strlen(s1))) return sp;

return 0;	/* no match */
}


/*****************************
 * Same as sindex(s1,s2), but IGNORE CASE in comparison
 *****************************/

usindex(s1,s2)
 char *s1,*s2;
{
 register char *sp;

for (sp=s2; strlen(s1)<=strlen(sp); sp++)
	if (!ustrncmp(s1,sp,strlen(s1))) return sp;

return 0;	/* no match */
}


/* get the counters from counters file */

gcntrs(f)
char *f;
{
char tdate[DATELEN+1];

#ifndef MULTI_USER
int id_num;		/* make dumby local copy if single user */
#endif

/* in case of failure.... */

callnum=id_num=0;	/* for defaults in case nothing found */

if ((counters=open(f ? f : COUNTERS,F_RD | F_LOCK))!=0)
	{
	read(counters,0);
	sscanf(bufloc(counters),"%d %d %d %s %d %d",&msgcount,
		&callnum,&nextmsg,(O.RTC ? tdate : date),&id_num,&privmsgs);

#ifdef TURBODOS

	if (strncmp(in_loc,ID_STR,3))
		{
		sprintf(bufloc(counters),"%d %u %u %s %d %d",msgcount,callnum,
			nextmsg,date,++id_num,privmsgs);
		write(counters,0);
		strcpy(id_loc,ID_STR);
		*(id_loc+3)=(char)id_num;
		}

#endif	/* TurboDos */

	close(counters);
	}
}


/* put the counters file on disk */

pcounters(f)
 char *f;
{
#ifndef MULTI_USER
int id_num=0;
#endif

counters=open(f ? f : COUNTERS,F_RW | F_LOCK);
 sprintf(bufloc(counters),"%d %u %u %s %d %d",msgcount,callnum,
	nextmsg,date,id_num,privmsgs);
 write(counters,0);
close(counters);
}


/* write user specified to correct place in users file, 0=write to current
   possition in the OPEN users file */ 

putuser(un)
register unsigned un;
{
register int *tnum;
register int flag;
 if (un!=0)		/* EXPECTS user file to already be open if nu=0 */
	{
	if ((users=open(USERFILE,F_RW | F_LOCK))==NULL) return ERROR;   /* else, open it */
	tnum=bufloc(users);			/* pointer to user #	*/
	do  flag=read(users,1);
	  while (*tnum!=un && flag==128);	/* til match, or eof.. */
	if (flag==128) setrrec(users,-1);	/* if not eof, backup one */
	}	/* end nu>0 routine */
  movmem(&user,bufloc(users),128);	/* get user vars */
  write(users,0);	/* write record, no increment... */
  close(users);		/* either method called closes the users file */
  return NULL;		/* NULL=all's well */
}


/* write user, and 8 char stat parm to status line if available */

wustat(s)
 char *s;
{
char buf[128];
sprintf(buf,"%8s -- %s %s - '%c' %s -- %s",s ? s : "",user.first,
	user.last,user.status,time,user.city);
writestat(buf);
}


/* output string to the 25th status line.. */

writestat(str)
 register char *str;
{
if (O.STATUSLINE)		/* 25th line status display on???? */
   {
   if (O.OSMASK)
	{
	outpstr(O.OSTAT,O.OSMASK,O.ODATA,O.TO25);  /* get to 25th line	*/
	outpstr(O.OSTAT,O.OSMASK,O.ODATA,str);	   /* output string	*/
	outpstr(O.OSTAT,O.OSMASK,O.ODATA,O.FROM25);  /* get back	*/
	}
   else	{	/* otherwise O.ODATA is address of output routine BIOS DEP! */
	outsbios(O.TO25);
	outsbios(str);
	outsbios(O.FROM25);
	}
   }
}

outsbios(s)
 register char *s;
{
while (*s) outcbios(*s++);
}

char glob_ch;	/* so I can find the char easily */
char *glob_ad;

outcbios(c)
 register char c;
{
glob_ch=c;
glob_ad=O.ODATA;

#asm
	lda	glob_ch_
	mov	c,a
sillyk:	lxi	d,sillyk+8	; terrible kluge, don not change following
	push	d		; instructions without changing this too!!!
	lhld	glob_ad_
	pchl
	nop

#endasm

}


/* EOF hmlib.c */

