/**/
#define		KEY_ERR			-1
#include	"ksysdefs.h"
#include	"ksysvtyp.h"
#include	"ksysprot.h"
#include	"keyproto.h"
/**/
/**/
#ifndef		MAXCARDS
#define		MAXCARDS	512
#endif
#ifndef		BUFFER
#define		BUFFER		1024
#endif
/**/
#define		KEYSUNIX		".keys"
#define		KEYSDOS			"biblbibl.key"
/**/
#ifndef		KEYSIZ
#define		KEYSIZ	32
#endif
/**/
extern	KENV	kenv;
/**/
/*GLOBAL*/
NOSIGN	check = YES;		/* error checking */
KARD	*kd;				/* pointers to cards, field ids */
KBUF	kb;					/* input line and work buffer */
static	char	*kfn;		/* key file name */
static	char	*keys;
static	char	ks[KEYSIZ];
/**/
badkey(is)
char	*is;
{
	auto	int	nk = iskey(is);
	if(nk < 0)	{
		errmsg("<bad key> '%s'\r",is);
		}
	return(nk);
}
char	*
getdol(os,us,is)	/* get an environment string */
char	*os;	/* receiving area */
char	*us;	/* upper limit of os */
char	*is;	/* pattern to find */
{
	auto	FILE	*kfp;
	auto	char	ws[FNMSIZ];
	auto	int		lis = strlen(is);
/**/
	os[0] = ZERO;
/**/
	kfp = efopen(kfn,"r");
/**/
	while(kfp && fgetlf(ws,ws+FNMSIZ-1,kfp))	{
		if(ws[0] == '$' &&
			strnkmp(ws+1,is,lis) == 0 &&
				ws[lis+1] == '=')	strkat(os,us,ws+lis+2);
		}
/**/
	xfclose(kfp);
/**/
	return( os[0] ? os : NULL );
}
/**/
char	*
nocore(os,is)
char	*os;
char	*is;
{
	if(!is)	{
		errmsg("%s <no core>\n",is);
		terror(FUBAR);
		}
	return(os);
}
nullcd()	/* is card all null (empty) ? */
{
	REG		NOSIGN	i;
	for(i = 0 ; i < kb.nfld; i++)
		if(kd[i].data)	return(0);
	return(YES);
}
KARD	*
setkd(is)		/* create and fill in kd (card) structure */
char	*is;
{
	auto	KARD			*kd;
	auto	char			*kp[BLKSIZ];
	auto	char			*xp[4];
	REG		KEYNUM			nk;
	REG		NOSIGN			tk;
	REG		NOSIGN			i;
	auto	NOSIGN			kdsiz;
	nk = splitc(kp,BLKSIZ,is,LF);
/**/
	for( i = tk = 0; i < nk; i++ )	{
		if(isalnum(*kp[i]))	tk += 1;
		}
/**/
	kdsiz = sizeof(KARD) * (tk + 1);
	kd = (KARD *) emalloc(kdsiz,"setkd");
	memset( (char *) kd, 0, sizeof(KARD) * (tk + 1));
/**/
	for( i = tk = 0; i < nk; i++ )	{
		if(!isalnum(*kp[i]))		continue;
		splitc(xp,4,kp[i],'/');
		kd[tk].skey =  strcnd(xp[0]);
		kd[tk].lkey =  strcnd(xp[1]);
		kd[tk].label = strcnd(xp[2]);
		tk += 1;
		}
	kb.nfld = tk;
	return(kd);
}
keyscheck()		/* check for duplicated keys */
{
	auto	int	err = 0;
	auto	int	i;
	auto	int	j;
	for(i = 0; i < kb.nfld; i++)		{
		for(j = i + 1; j < kb.nfld; j++)	{
			err = 0;
			if(strcmp(kd[i].skey,kd[j].skey) == 0)	err = DA;
			else
			if(strcmp(kd[i].skey,kd[j].lkey) == 0)	err = DA;
			else
			if(strcmp(kd[i].lkey,kd[j].skey) == 0)	err = DA;
			else
			if(strcmp(kd[i].lkey,kd[j].lkey) == 0)	err = DA;
			if(err)	{
				errmsg("<duplicated keys>\n");
				print("%s/%s/%s\n",kd[i].skey,kd[i].lkey,kd[i].label);
				print("%s/%s/%s\n",kd[j].skey,kd[j].lkey,kd[j].label);
#ifdef	SLEEP
				sleep(SLEEP);
#endif
				}
			}
		}
	if(err)	terror(FUBAR);
	return(ZERO);
}
keysread(is)	/* read keys from kfn */
char	*is;
{
/**/
	if(isnull(is))	return(ZERO);
	clcd();
/**/
	free_if(kb.base);
	free_if(keys);
	free_if((char *) kd);
/**/
	keys = blkrd(is);
/**/
	kd = setkd(keys);
	keyscheck();
/**/
	bufferget(0);
/**/
	return(kb.nfld);
}
void
keyslist()	/* list key file and keys */
{
	if(kenv.keying & DA) 	khint(keysget(),NULL);
	else					prwho("<no keys>\n");
}
char	*
keysfile(is)	/* search for ".keys" */
char	*is;
{
#ifdef	COMMENT
*	if a file is named, look for it.
*	if no file name is given, look for the default keys file
*   in the current directory, home directory, and PUBKIN.
*   UNIX default: .keys
*   MSDOS default: biblbibl.key
*   this routine does not actually read the keys file
#endif	COMMENT
	extern	char	*findfile();
	auto	char	*home = kgetenv("HOME");
	auto	char	*pubk = kgetenv("PUBKIN");
	auto	char	*keys = kenv.dosdos ? KEYSDOS : KEYSUNIX;
	auto	char	*file = NULL;
	auto	FILE	*tfp;
/**/
	if(!isnull(is))	{
		kenv.keying &= ~DA;
		kfn = free_if(kfn);
		kfn = fexpand(is);
		tfp = efopen(kfn,"r");
		if(tfp)	{
			kenv.keying |= DA;
			tfp = xfclose(tfp);
			return(kfn);
			}
		else	return(kfn = free_if(kfn));
		}
/**/
	if(!home)	home = undefined("HOME","c:",".");
	if(!pubk)	pubk = undefined("PUBKIN","c:/bib",".");
/**/
	if(!(file = findfile(keys,3,".",home,pubk)))	{
		errmsg("keysfile <no keys file>\n");
		kenv.keying &= ~DA;
		}
	else	{
		kenv.keying |= DA;
		kfn = free_if(kfn);
		kfn = strsave(file);
		return(kfn);
		}
}
char	*
undefined(vn,dd,ud)
char	*vn;
char	*dd;
char	*ud;
{
	auto	char	*rs = kenv.dosdos ? dd : ud;
	errmsg("keysfile <undefined %s> %s -> %s\n",vn,rs,vn);
	return(rs);
}
NOSIGN
bufferget(nc)	/* get a work buffer */
NOSIGN	nc;
{
	auto	char	ws[FNMSIZ];
	auto	char	*nx;
/**/
	if(nc == 0)	{
		nx = getdol(ws,ws+FNMSIZ-1,"buffer");
		if(nx)	nc = atoi(nx);
		}
/**/
	nc = nc ? nc : BUFFER;
	kb.base = kb.bmax = free_if(kb.base);
	while(nc >= 1 && kb.base == NULL)	{
		kb.base = emalloc( nc, "bufferget" );
		if(kb.base == NULL)	{
			nc -= BLKSIZ;
			errmsg("bufferget <reducing request> %u bytes\n",nc);
			continue;
			}
		kb.bmax = kb.base + nc;
		}
	return(kb.bsize = nc);
}
cardsget()		/* return maximum cards as set by $maxcards */
{
	auto	char	ws[FNMSIZ];
	auto	char	*max;
	if((int) (max = getdol(ws,ws+FNMSIZ-1,"maxcards"))) return(atoi(max));
	else												return(MAXCARDS);
}
#ifdef	KEYSSET
char	*
keysset(is)		/* set the key file name */
char	*is;
{
	return(kfn = stringer(is,kfn));
}
#endif	KEYSSET
char	*
keysget()		/* return the key file name */
{
	return(kfn);
}
iskey(is)	/* is the string is a key */
char	*is;
{
	static		int		lk = -1;
	REG			KEYNUM		nk;
	REG			int		np;
	nk = (++lk < kb.nfld) ? lk : (lk = 0);
	np = kb.nfld;
	while(np-- >= 1)	{
	if(*kd[nk].skey == *is &&
		strcmp(kd[nk].skey,is) == 0)
			return(lk = nk);
	if(*kd[nk].lkey == *is &&
		strcmp(kd[nk].lkey,is) == 0)
			return(lk = nk);
	nk = (++nk < kb.nfld) ? nk : 0;
	}
	return(-1);
}
clrdcd(fp,ty)		/* clear and read a card */
FILE	*fp;
{
	clcd();
	return(rdcd(fp,ty));
}
void
clcd()			/* clear card to NULLs; free memory */
{
	REG		KARD		*kdx = kd;
	REG		KARD		*kdu = kd + kb.nfld;
	while(kdx < kdu)	{
	if(kdx->data)	kdx->data = free_if(kdx->data);
	kdx += 1;
	}
}
keysmem(fp)		/* report on memory usage */
FILE	*fp;
{
	fprintf(fp,"buffer = %u bytes\n",kb.bmax - kb.base);
	return(ZERO);
}
void
cdcond()		/* condition (pretty) a card */
{
	REG		KARD		*kdx = kd;
	REG		KARD		*kdu = kd + kb.nfld;
	while(kdx < kdu)	{
	if(kdx->data)	strcnd(kdx->data);
	kdx += 1;
	}
}
cdsize()		/* report size of card */
{
	REG		KARD		*kdx = kd;
	REG		KARD		*kdu = kd + kb.nfld;
	REG		NOSIGN		ks = 0;
	while(kdx < kdu)	{
	if(kdx->data)	{
		ks += (strlen(kdx->skey) + 1);
		ks += strlen(kdx->data);
		}
	kdx += 1;
	}
	return(ks);
}
keylist(il,nl,is)	/* convert key list to numbers*/
NOSIGN	il[];		/* keys converted to numbers */
NOSIGN	nl;			/* max size nl */
char	*is;		/* comma or blank separated list */
{
	auto	char	ws[FNMSIZ];
	auto	char	*isx = is;
	auto	int	nlk = ZERO;
	auto	int	num = ZERO;
	if(isnull(is))	{
		while(num < nl && nlk < kb.nfld)	{
			if(kd[nlk].data)	il[num++] = nlk;
			nlk += 1;
		}
		return(num);
		}
	strxty(is,',',' ');
	while(nlk < nl && (int) (isx = xgnc(ws,ws+FNMSIZ-1,isx,' ')))	{
		if((num = iskey(ws)) < 0)	{
			errmsg("keylist <bad key> '%s' in '%s'\n",
				ws,is);
			return(KEY_ERR);
			}
		il[nlk++] = num;
		}
	if(nlk >= nl)	{
		errmsg("keylist <long list> '%s'\n",is);
		return(KEY_ERR);
		}
	return(nlk);
}
char	*
rderr(is)			/* return error message; otherwise NULL */
char	*is;
{
/*
**	code to check for errors is
	if(rdcd(***) == 0)	{
		if(rderr(NULL))	{
			errmsg(***);
			}
		}
*/
	if(is == ZERO)	{
		if(isnull(ks))	return(NULL);
		else			return(ks);
		}
	else			{
		strncpy(ks,is,KEYSIZ-1);
		ks[KEYSIZ-1] = ZERO;
		return(ks);
		}
}
cdtox(os,us)	/* from rdcd to string format */
char	*os;
char	*us;
{
	auto	NOSIGN	ix;
	auto	NOSIGN	nc;
	for(nc = ix = 0; ix < kb.nfld; ix++)	{
		if(kd[ix].data)	{
			os = strkat(os,us,kd[ix].skey);
			os = strkat(os,us," ");
			os = strkat(os,us,kd[ix].data);
			os = strkat(os,us,"\n");
			nc += 1;
			}
		}
	if(os >= us)	return(-1);
	else			return(nc);
}
rdcd(fp,ty)		/* read card from buffered stream */
FILE	*fp;
NOSIGN	ty;
{
	kb.used = xrdcd(kb.base,kb.bmax,fp);
	if(kb.used != NULL)	 	return(xtocd(kb.base,ty));
	else					return(ZERO);
}
xtocd(is,ty)    /* convert a xrdcd record to rdcd format */
char    *is;	/* string with card image */
NOSIGN   ty;   	/* over read type */
{
    REG     char    *isx = is;
    REG     char    *ksx;
    REG     char    *dsx;
    REG     KEYNUM 	nk;
	REG		char	**kdx;
	rderr("");
    while((int)(isx = strtoc(ksx = isx,'\n'))) {
/**/
	while(isspace(*ksx))		++ksx;
/**/
	if(*ksx == ZERO)			continue;
/**/
    dsx = getalnum(ksx);
/**/
    if((nk = iskey(ksx)) < 0)   {
        rderr(ksx);
        return(0);
        }
/**/
	while(isspace(*dsx))		++dsx;
	if(*dsx == ZERO)			continue;
/**/
	kdx = &kd[nk].data;
/**/
/*
**	with TURBOC a free of data to be used must not precede usage
**	of that data.
*/
	switch(ty)	{
		case	'v':	if(*kdx)	{
							rderr(ksx);
							return(0);
							}
		case	'o':	*kdx = free_if(*kdx);
						*kdx = strsave(dsx);
						break;
		case	's':	if(*kdx == NULL)	
							*kdx = strsave(dsx);
						break;
		case	'a':	
						if(*kdx == NULL)		{
							*kdx = strsave(dsx);
							}
						else
						if(strkmp(*kdx,dsx))	{
							*kdx = strjoin(*kdx,dsx,"::");
							}
						break;
		default:		abort();
		}
/**/
    }
    return(1);
}
wrcd(fp,ty,fw)			/* write a card */
FILE	*fp;
int		ty;
int		fw;
{
	REG		NOSIGN	i;
	REG		NOSIGN	nfld = kb.nfld;
	REG		cnt = 0;
	for(i = 0; i < nfld; i++)
		if(kd[i].data)		{
			cdprp(i,fp,ty,fw);
			cnt += 1;
			}
	if(cnt)		fputs("\n",fp);
	fflush(fp);
	if(ferror(fp))	{
		prterr("wrcd");
		errmsg("wrcd <error on output>\n");
		terror(FUBAR);
		}
	return(cnt);
}
cdprp(nk,fp,ty,fw) 	/* print individual data fields */
KEYNUM	nk;			/* key number */
NOSIGN	ty;			/* type (format) */
NOSIGN	fw;			/* fold width */
FILE	*fp;
{
	auto	char	*ss = " ";
	auto	char	*os;
	auto	char	*ks;
	auto	char	*(*ptx)();	/* printing function */
	ptx = NULL;
	os = kd[nk].data;
	if(isnull(os)) return(ZERO);
	ks = kd[nk].skey;
	switch(ty)	{
	case	'p':
	case	'f':
	case	1:		ptx = wrf;
	when	'l':
	case	2:		ptx = wrl;
	when	'h':
	case	3:		ks = kd[nk].label;
					ss = "\n";
					ptx = wrf;
	when	'w':	ptx = wrw;
					break;
	default:		errmsg("cdprp <bad case> '%d'\n",ty);
	case	'z':	highkey(fp,kd[nk].skey);
					wrf(kd[nk].data,fp,fw,"\n        ");
					ptx = NULL;
					break;
	}
	if(ptx)	{
		fputs(ks,fp);
		fputs(ss,fp);
		(*ptx)(os,fp,fw,ZERO);
		}
	return(ZERO);
}
void
highkey(fp,is)
FILE	*fp;
char	*is;
{
	scr_so(fp);
	fprintf(fp,"%-7.7s ",is);
	scr_se(fp);
	return;
}


