/*******************************************************************************
* mbk     : services functions and global variables                            *
*                                                                              *
* version : 3.14                                                               *
* date    : 31/08/93                                                           *
*******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include MUT_H
#include "mbk_util.h"

/*******************************************************************************
* global variables                                                             *
*******************************************************************************/
chain_list *HEAD_CHAIN = NULL;                 /* chain buffer head           */
ptype_list *HEAD_PTYPE = NULL;                 /* ptype buffer head           */
num_list *HEAD_NUM = NULL;                     /* num   buffer head           */
char TRACE_MODE  = 'N';                        /* trace if 'Y'                */
char DEBUG_MODE  = 'N';                        /* debug if 'Y'                */
char FAST_MODE   = 'N';                        /* no consistency check if 'Y' */
char SEPAR  = '.';                             /* char used in concatenation  */
char *WORK_LIB = NULL;                         /* working directory           */
char **CATA_LIB = NULL;                        /* read only directories       */
char *CATAL = NULL;                            /* catalog file                */
char IN_LO[5] = "al";                          /* input logical format        */
char IN_PH[5] = "ap";                          /* input physical format       */
char OUT_LO[5] = "al";                         /* output logical format       */
char OUT_PH[5] = "ap";                         /* output physical format      */
long SCALE_X = 10;                             /* distance scale definition   */
char PARSER_INFO[100] = "nothing yet";         /* version number, and so on   */
char *VDD = NULL;                              /* user name for power high    */
char *VSS = NULL;                              /* user name for power ground  */

static char buffer[BUFSIZ];                    /* buffer for namealloc strcpy */
static char str[BUFSIZ];                       /* buffer for concatname       */

/*******************************************************************************
* fonction handler()                                                           *
*******************************************************************************/
static void dflhandler()
{
	exit(100);
}
/*******************************************************************************
* fonction mbkenv()                                                            *
*******************************************************************************/
void mbkenv()
{
char *getenv();
char *str;

	read_lib(); /* read the contents of MBK_WORK_LIB and MBK_CATA_LIB */

	str = getenv("MBK_DEBUG_MODE");
	if (str != NULL)
		if (!strcmp(str,"yes"))
			DEBUG_MODE = 'Y';

	str = getenv("MBK_TRACE_MODE");
	if (str != NULL)
		if (!strcmp(str,"yes"))
			TRACE_MODE = 'Y';

	str = getenv("MBK_FAST_MODE");
	if (str != NULL)
		if (!strcmp(str,"yes")) 
			FAST_MODE = 'Y';

	str = getenv("MBK_SCALE_X");
	if (str != NULL)
		SCALE_X = (long)atoi(str);

	str = getenv("MBK_IN_LO");
	if (str != NULL) {
		if (!strcmp(str, "hns"))
			(void)strcpy(IN_LO, "hns");
		else if (!strcmp(str, "fne"))
			(void)strcpy(IN_LO, "fne");
		else if (!strcmp(str, "hdn"))
			(void)strcpy(IN_LO, "hdn");
		else if (!strcmp(str, "fdn"))
			(void)strcpy(IN_LO, "fdn");
		else if (!strcmp(str, "al" ))
			(void)strcpy(IN_LO, "al");
		else if (!strcmp(str, "alx"))
			(void)strcpy(IN_LO, "alx");
		else if (!strcmp(str, "spi"))
			(void)strcpy(IN_LO, "spi");
		else if (!strcmp(str, "edi"))
			(void)strcpy(IN_LO, "edi");
		else if (!strcmp(str, "vst"))
			(void)strcpy(IN_LO, "vst");
		else {
			(void)fflush(stdout);
			(void)fprintf(stderr,"*** mbk error ***\n");
			(void)fprintf(stderr,"netlist input format '%s' not supported\n",str);
			EXIT(1);
		}
	}

	str = getenv("MBK_OUT_LO");
	if (str != NULL) {
		if (!strcmp(str, "hns"))
			(void)strcpy(OUT_LO, "hns");
		else if (!strcmp(str, "fne"))
			(void)strcpy(OUT_LO, "fne");
		else if (!strcmp(str, "hdn"))
			(void)strcpy(OUT_LO, "hdn");
		else if (!strcmp(str, "fdn"))
			(void)strcpy(OUT_LO, "fdn");
		else if (!strcmp(str, "al" ))
			(void)strcpy(OUT_LO, "al");
		else if (!strcmp(str, "alx"))
			(void)strcpy(OUT_LO, "alx");
		else if (!strcmp(str, "spi"))
			(void)strcpy(OUT_LO, "spi");
		else if (!strcmp(str, "edi"))
			(void)strcpy(OUT_LO, "edi");
		else if (!strcmp(str, "vst"))
			(void)strcpy(OUT_LO, "vst");
		else if (!strcmp(str, "cct"))
			(void)strcpy(OUT_LO, "cct");
		else {
			(void)fflush(stdout);
			(void)fprintf(stderr,"*** mbk error ***\n");
			(void)fprintf(stderr,"netlist output format '%s' not supported\n",str);
			EXIT(1);
		}
	}

	str = getenv("MBK_IN_PH");
	if (str != NULL) {
		if (!strcmp(str, "cp"))
			(void)strcpy(IN_PH, "cp");
		else if (!strcmp(str, "ap"))
			(void)strcpy(IN_PH, "ap");
		else if (!strcmp(str, "mg"))
			(void)strcpy(IN_PH, "mg");
		else {
			(void)fflush(stdout);
			(void)fprintf(stderr,"*** mbk error ***\n");
			(void)fprintf(stderr,"layout input format '%s' not supported\n",str);
			EXIT(1);
		}
	}

	str = getenv("MBK_OUT_PH");
	if (str != NULL) {
		if (!strcmp(str, "cp"))
			(void)strcpy(OUT_PH, "cp");
		else if (!strcmp(str, "ap"))
			(void)strcpy(OUT_PH, "ap");
		else if (!strcmp(str, "mg"))
			(void)strcpy(OUT_PH, "mg");
		else {
			(void)fflush(stdout);
			(void)fprintf(stderr,"*** mbk error ***\n");
			(void)fprintf(stderr,"layout output format '%s' not supported\n",str);
			EXIT(1);
		}
	}

	str = getenv("MBK_CATAL_NAME");
	if (str != NULL)
		CATAL = str;
	else /* default value for catalog name */
		CATAL = "CATAL";

	str = getenv("MBK_SEPAR");
	if (str != NULL)
		SEPAR = *str;

	str = getenv("MBK_VDD");
	if (str != NULL)
		VDD = str;
	else /* default value for power high */
		VDD = "vdd";

	str = getenv("MBK_VSS");
	if (str != NULL)
		VSS = str;
	else /* default value for power high */
		VSS = "vss";

	/* EXIT shall produce a real exit if not trapped */
	signal(SIGTERM, dflhandler);
}

/*******************************************************************************
* function nameindex()                                                         *
* return a string that is the concatenation of the name argument, the mbk      *
* separator, and an index                                                      *
*******************************************************************************/
char *nameindex(name, index)
char *name;
long index;
{
char str[100];

	(void)sprintf(str,"%s%c%ld", name, SEPAR, index);
	return namealloc(str);
}

/*******************************************************************************
* fonction addnum()                                                            *
* num list specialized allocator to avoid too many mallocs                     *
*******************************************************************************/
num_list *addnum(ptnum, data)
num_list *ptnum;
long data;
{
num_list *pt;
register int i;

	if (HEAD_NUM == NULL) {
		HEAD_NUM = (num_list *)mbkalloc(BUFSIZE*sizeof(num_list));
		pt = HEAD_NUM;
		for (i = 1; i < BUFSIZE; i++) {
			pt->NEXT = pt + 1;
			pt++;
		}
		pt->NEXT = NULL;
	}

	pt = HEAD_NUM;
	HEAD_NUM = HEAD_NUM->NEXT;
	pt->NEXT = ptnum;
	pt->DATA = data;
	return pt;
}

/*******************************************************************************
* function freenum()                                                           *
* gives back freed block to the num memory allocator                           *
*******************************************************************************/
void freenum(pt)
num_list *pt;
{
	HEAD_NUM = (num_list *)append((chain_list *)pt, (chain_list *)HEAD_NUM);
}

/*******************************************************************************
* function addchain()                                                          *
* chain list specialized allocator to avoid too many mallocs                   *
*******************************************************************************/
chain_list *addchain(pthead, ptdata)
chain_list *pthead;
void *ptdata;
{
chain_list *pt;
register int i;

	if (HEAD_CHAIN == NULL) {
		pt = (chain_list *)mbkalloc(BUFSIZE*sizeof(chain_list));
		HEAD_CHAIN = pt;
		for (i = 1; i < BUFSIZE; i++) {
			pt->NEXT = pt + 1;
			pt++;
		}
		pt->NEXT = NULL;
	}

	pt = HEAD_CHAIN;
	HEAD_CHAIN = HEAD_CHAIN->NEXT;
	pt->NEXT = pthead;
	pt->DATA = ptdata;
	return pt;
}

/*******************************************************************************
* function freechain()                                                         *
* gives back freed block or blocks to the chain_list memory allocator          *
*******************************************************************************/
void freechain(pt)
chain_list *pt;
{
	HEAD_CHAIN = append(pt, HEAD_CHAIN);
}

/*******************************************************************************
* function delchain()                                                          *
* delete a single element of a chain_list and gives it back to freechain       *
*******************************************************************************/
chain_list *delchain(pthead, ptdel)
chain_list *pthead;
chain_list *ptdel;
{
chain_list *pt;
chain_list *ptsav;

	if (pthead == NULL || ptdel == NULL) {
		(void)fflush(stdout);
		(void)fprintf(stderr,"*** mbk error ***");
		(void)fprintf(stderr,"  delchain() impossible : pointer = NULL !\n");
		EXIT(1);
	}

	if (ptdel == pthead) {
		pt = pthead->NEXT;
		pthead->NEXT = NULL;
		freechain(pthead);
		return pt;
	} else {
		for (pt = pthead; pt; pt = pt->NEXT) {
			if (pt == ptdel)
				break;
			ptsav = pt;
		}
		if (pt != NULL) {
			ptsav->NEXT = pt->NEXT;
			ptdel->NEXT = NULL;
			freechain(ptdel);
			return pthead;
		} else
			return NULL;
	}
}
  
/*******************************************************************************
* function addptype()                                                          *
*******************************************************************************/
ptype_list *addptype(pthead,type,ptdata)
ptype_list *pthead;
void *ptdata;
long type;
{
ptype_list *pt;
register int i;

	if (HEAD_PTYPE == NULL) {
		pt = (ptype_list *)mbkalloc(BUFSIZE * sizeof(ptype_list));
		HEAD_PTYPE = pt;
		for (i = 1; i < BUFSIZE; i++) {
			pt->NEXT = pt + 1;
			pt++;
		}
		pt->NEXT = NULL;
	}

	pt = HEAD_PTYPE;
	HEAD_PTYPE = HEAD_PTYPE->NEXT;
	pt->NEXT = pthead;
	pt->DATA = ptdata;
	pt->TYPE = type;
	return pt;
}
  
/*******************************************************************************
* function delptype()                                                          *
*******************************************************************************/
ptype_list *delptype(pthead, type)
ptype_list *pthead;
long type;
{
ptype_list *pt;
ptype_list *ptsav;

	if (pthead == NULL) {
		(void)fflush(stdout);
		(void)fprintf(stderr,"*** mbk error ***\n");
		(void)fprintf(stderr,"delptype() impossible : pthead = NULL !\n");
		EXIT(1);
	}

	if (pthead->TYPE == type) {
		pt = pthead->NEXT;
		pthead->NEXT = NULL;
		freeptype(pthead);
		return pt;
	} else {
		for (pt = pthead; pt; pt = pt->NEXT) {
			if (pt->TYPE == type)
				break;
			ptsav = pt;
		}
		if (pt != NULL) {
			ptsav->NEXT = pt->NEXT;
			pt->NEXT = NULL;
			freeptype(pt);
			return pthead;
		} else
			return NULL;
	}
}

/*******************************************************************************
* function freeptype()                                                         *
*******************************************************************************/
void freeptype(pt)
ptype_list	*pt;
{
	HEAD_PTYPE = (ptype_list *)append((chain_list *)pt,(chain_list *)HEAD_PTYPE);
}

/*******************************************************************************
* function getptype()                                                          *
*******************************************************************************/
ptype_list *getptype(pthead, type)
ptype_list *pthead;
long type;
{
ptype_list	*pt;

	for (pt = pthead; pt; pt = pt->NEXT)
		if (pt->TYPE == type)
			return pt;
	return NULL;
}

/*******************************************************************************
* function append()                                                            *
*******************************************************************************/
chain_list *append(pt1, pt2)
chain_list *pt1,*pt2;	
{
chain_list *pt;

	if (pt1 == NULL)
		return pt2;
	else { 
		for (pt = pt1; pt->NEXT; pt = pt->NEXT);
		pt->NEXT = pt2; /* append the list 2 at the end of list 1 */
		return pt1;
	}
}

/*******************************************************************************
* function namealloc()                                                         *
*******************************************************************************/
char *namealloc(inputname)
char *inputname;
{
static chain_list *HASHTABLE[HASHVAL];
chain_list *pt;
char *name = buffer; /* ensure no modification of parameter string */
register int code = 0;

	if (inputname == NULL)
		return NULL;

	while (*inputname) {
		*name = isupper(*inputname) ? tolower(*inputname) : *inputname;
		code =  code << 1 ^ *name++;
		inputname++;
	}
	*name = '\0';
	if (code < 0)
		code = -code;
	code %= HASHVAL;

	for (pt = HASHTABLE[code]; pt; pt = pt->NEXT)
		if (!strcmp(buffer, (char *)pt->DATA))
			return (char *)pt->DATA;

	name = (char *)mbkalloc((unsigned int)(strlen(buffer) + 1));
	(void)strcpy(name, buffer);
	HASHTABLE[code] = addchain(HASHTABLE[code], (void *)name);
	return (char *)(HASHTABLE[code]->DATA);
}

/*******************************************************************************
* function downstr()                                                           *
*******************************************************************************/
void downstr(s, t)
char *s, *t;
{
	for (; *s; s++, t++)
		*t = isupper(*s) ? tolower(*s) : *s;
}

/*******************************************************************************
* function upstr()                                                             *
*******************************************************************************/
void upstr(s, t)
char *s, *t;
{
	for (; *s; s++, t++)
		*t = islower(*s) ? toupper(*s) : *s;
}

/*******************************************************************************
* function instr() : about strstr, but some systems don't have it              *
*******************************************************************************/
char *instr(s, find, separ)
char *s, *find, separ;
{
char *t, c, sc;
int len;

	if (!s || !find)
		return NULL;
	if (separ) {
		t = s;
		if ((s = strrchr(s, separ)) == NULL)
			s = t;
		else
			s++; /* skip the separator itself */
	}
	if ((c = *find++) != 0) {
		len = strlen(find);
		do {
			do {
				if ((sc = *s++) == 0)
					return NULL;
			} while (sc != c);
		} while (strncmp(s, find, len) != 0);
		s--;
	}
	return s;
}

/*******************************************************************************
* function vectorize : builds a legal mbk vector from a name and an index      *
*******************************************************************************/
char *vectorize(radical, index)
char *radical;
long index;
{
  (void)sprintf(str,"%s %ld", radical, index);
  return namealloc(str);
}

/*******************************************************************************
* function vectorradical : returns the radical of an mbk vector                *
*******************************************************************************/
char *vectorradical(name)
char *name;
{
char *s;

	strcpy(str, name);
	s = strchr(str, ' ');

	if (!s)
		return name;

	*s = '\0';
	return namealloc(str);
}

/*******************************************************************************
* function vectorindex : returns the index of an mbk vector                    *
*******************************************************************************/
int vectorindex(name)
char *name;
{
char *s = strchr(name, ' ');

	if (!s)
		return -1;

	return atoi(s);
}

/*******************************************************************************
*  generic compaison function :                                                *
*  unlike strcmp, ensure that 10 > 2.                                          *
*  first, check strings, then check numerical values as numbers, not strings.  *
*******************************************************************************/
int naturalstrcmp(s, t)
char *s, *t;
{
char *spt, *tpt , *st = s, *tt = t;
int u, ls, lt;

	spt = buffer, tpt = str;

	while ((!isdigit(*st)) && *st)
		*spt++ = *st++;
	*spt = '\0';

	while ((!isdigit(*tt)) && *tt)
		*tpt++ = *tt++;
	*tpt = '\0';

	if ((u = strcmp(buffer, str)) != 0)
		return u;

	if ((ls = strlen(s)) == (lt = strlen(t)))
		return strcmp(s, t);

	return ls - lt;
}

/*******************************************************************************
* function concatname()                                                        *
*******************************************************************************/
char *concatname(name1, name2)
char *name1, *name2;
{
  (void)sprintf(str,"%s%c%s", name1, SEPAR, name2);
  return namealloc(str);
}

/*******************************************************************************
* mbkstrdup : since brain damaged system we aim at do not have it              *
*******************************************************************************/
char *mbkstrdup(s)
char *s;
{
char *t = (char *)mbkalloc((unsigned int)(strlen(s) + 1));

	return strcpy(t, s);
}

/*******************************************************************************
* function reverse                                                             *
*******************************************************************************/
chain_list *reverse(head)
chain_list *head;
{
chain_list *p;
chain_list *q = (chain_list *)NULL;

	if (!head)
		return NULL;
	while (p = head->NEXT) {
		head->NEXT = q;
		q = head;
		head = p;
	}
	head->NEXT = q;
	return head;
}

/*******************************************************************************
* function pstrcmp                                                             *
* used for qsort and bsearch use for catalog sorting and acessing              *
*******************************************************************************/
static int pstrcmp(s, t)
char **s, **t;
{
	return strcmp(*s, *t);
}

/*******************************************************************************
* function incatalogfeed                                                       *
* tests if a model is present in the catalog with the F attribut               *
*******************************************************************************/
int incatalogfeed(figname)
char *figname;
{
static int size;
static char **table;

	if (!size)
		loadcatalog(&table, &size, 'F');
	return size ?
				(int)bsearch(&figname, table, size, sizeof(char *), pstrcmp) : 0;
}

/*******************************************************************************
* function incataloggds                                                        *
* tests if a model is present in the catalog with the G attribut               *
*******************************************************************************/
int incataloggds(figname)
char *figname;
{
static int size;
static char **table;

	if (!size)
		loadcatalog(&table, &size, 'G');
	return size ?
				(int)bsearch(&figname, table, size, sizeof(char *), pstrcmp) : 0;
}

/*******************************************************************************
* function incatalog                                                           *
* tests if a model is present in the catalog with the F attribut               *
*******************************************************************************/
int incatalog(figname)
char *figname;
{
static int size;
static char **table;

	if (!size)
		loadcatalog(&table, &size, 'C');
	return size ?
				(int)bsearch(&figname, table, size, sizeof(char *), pstrcmp) : 0;
}

/*******************************************************************************
* function incatalogdelete                                                     *
* tests if a model is present in the catalog with the D attribut               *
*******************************************************************************/
int incatalogdelete(figname)
char *figname;
{
static int size;
static char **table;

	if (!size)
		loadcatalog(&table, &size, 'D');
	return size ?
				(int)bsearch(&figname, table, size, sizeof(char *), pstrcmp) : 0;
}

/*******************************************************************************
* function loadcatalog                                                         *
* read the catalog from disk checking the given type                           *
*******************************************************************************/

static void loadcatalog(table, size, type)
char ***table;
int *size;
char type;
{
void *pt;
char attrib;
int nb;
int nb_input; 
int i = 0;
chain_list *files = (chain_list *)NULL;
/* Tables for quick cell search :
   The catalog file is read only once, and sorted for speed.
   The later calls to loadcatalog only return the approriate table. */
static chain_list *cells[4];
static char **tabs[4];
static int sizes[4];
static int read;
 
	if (!read) {
		read++;
		(void)sprintf(buffer, "%s/%s", WORK_LIB, CATAL ? CATAL : "CATAL");
		files = addchain(files, (void *)fopen(buffer, READ_TEXT));
		while (CATA_LIB[i]) {
			(void)sprintf(buffer, "%s/CATAL", CATA_LIB[i++]);
			files = addchain(files, (void *)fopen(buffer, READ_TEXT));
		}
		/* error message :
		   To avoid malloc/free on file name, the error message is output
		   relatively to its order. */
		files = reverse(files);
		for (i = 0, pt = (void *)files; pt;
				pt = (void *)((chain_list *)pt)->NEXT, i++) {
			if (!((chain_list *)pt)->DATA)
				continue;
			nb = 0;
			while (!feof((FILE *)((chain_list *)pt)->DATA)) {
				nb++; /* count lines */
				nb_input = fscanf((FILE *)((chain_list *)pt)->DATA, "%s %c\n",
										buffer, &attrib);
				if (nb_input == 0) /* skip white lines */
					continue;
				if (nb_input != 2) {
					(void)fflush(stdout);
					(void)fprintf(stderr, "*** mbk error ***\n");
					(void)fprintf(stderr, "loadcatalog syntax error line %d ", nb);
					if (i == 0)
						(void)fprintf(stderr, "in file %s/%s\n", WORK_LIB, CATAL);
					else
						(void)fprintf(stderr, "in file %s/CATAL\n", CATA_LIB[i - 1]);
					EXIT(1);
				}	 
				attrib = islower(attrib) ? (char)toupper(attrib) : attrib;
				switch (attrib) {
					case 'C' :
						cells[0] = addchain(cells[0], namealloc(buffer));
						sizes[0]++;
						break;
					case 'G' :
						cells[1] = addchain(cells[1], namealloc(buffer));
						sizes[1]++;
						break;
					case 'F' :
						cells[2] = addchain(cells[2], namealloc(buffer));
						sizes[2]++;
						break;
					case 'D' :
						cells[3] = addchain(cells[3], namealloc(buffer));
						sizes[3]++;
						break;
					default :
						(void)fflush(stdout);
						(void)fprintf(stderr, "*** mbk error ***\n");
						(void)fprintf(stderr, "loadcatalog syntax error line %d ",
											nb);
						if (i == 0)
							(void)fprintf(stderr, "in file %s/%s\n", WORK_LIB, CATAL);
						else
							(void)fprintf(stderr, "in file %s/CATAL\n",
												CATA_LIB[i - 1]);
						(void)fprintf(stderr,"unknown attribut %c\n", attrib);
						EXIT(1);
				}
			}
			(void)fclose((FILE *)((chain_list *)pt)->DATA);
		}
		for (nb = 0; nb < 4; nb++) {
			if (sizes[nb]) {
				tabs[nb] = (char **)mbkalloc(sizes[nb] * sizeof(char *));
				for (i = 0, pt = (void *)cells[nb]; pt;
						pt = (void *)((chain_list *)pt)->NEXT, i++)
					tabs[nb][i] = (char *)((chain_list *)pt)->DATA;
				qsort(tabs[nb], sizes[nb], sizeof(char *), pstrcmp);
				freechain(cells[nb]);
			}
		}
	}

	switch (type) {
		case 'C' :
			*table = tabs[0];
			*size = sizes[0];
			break;
		case 'G' :
			*table = tabs[1];
			*size = sizes[1];
			break;
		case 'F' :
			*table = tabs[2];
			*size = sizes[2];
			break;
		case 'D' :
			*table = tabs[3];
			*size = sizes[3];
			break;
	}
}

/*******************************************************************************
* function read_lib()                                                          *
* fills an array of char * in order to have a list of names as CATA_LIB        *
*******************************************************************************/
static void read_lib()
{
char *getenv();
char *str, *s, *stc, *c;
int argc = 0;

	str = getenv("MBK_WORK_LIB");
	if (str != NULL) {
		WORK_LIB =
			(char *)mbkalloc((unsigned int)(strlen(str) + 1) * sizeof(char));
		(void)strcpy(WORK_LIB, str);
	} else { /* no specific path is given */
		WORK_LIB = (char *)mbkalloc((unsigned int)2 * sizeof(char));
		(void)strcpy(WORK_LIB, ".");
	}

	str = getenv("MBK_CATA_LIB");
	if(str != NULL) {
		s = (char *)mbkalloc((unsigned int)(strlen(str) + 1) * sizeof(char));
		(void)strcpy(s, str);
		str = s;   /* let's not modify the environement values */
		stc = str; /* for counting purposes */
		while (1) {
			if ((c = strchr(stc, ':')) == NULL)
				break;
			argc++;
			stc = ++c;
		}
		CATA_LIB = (char **)mbkalloc((unsigned int)(argc + 2) * sizeof(char *));
		argc = 0;
		while (1) {
			if ((s = strchr(str, ':')) == NULL)
				break;
			*(s++) = '\0';
			CATA_LIB[argc++] = str; /* no allocation necessary */
			str = s;
		}
		if (s == NULL)
			CATA_LIB[argc++] = str == NULL || *str == '\0' ? NULL : str;
		CATA_LIB[argc] = NULL;
	} else { /* no specific path is given */
		CATA_LIB = (char **)mbkalloc((unsigned int)2 * sizeof(char *));
		CATA_LIB[0] = ".";
		CATA_LIB[1] = NULL;
	}
}
/*******************************************************************************
* Hash tables management functions, contributed to by Luc Burgun on 20/06/92   *
*******************************************************************************/
/*******************************************************************************
* dilution function for the table accesses                                     *
*******************************************************************************/
static unsigned int hash(p)
void *p;
{
	return abs((int)p * ((int)p >> 5) >> 4);
}

/*******************************************************************************
* function addht, create a hash table                                          *
*******************************************************************************/
ht *addht(len)
unsigned int len;
{
ht *pTable;
htitem *pEl;
int i;

	if (len == 0) {
		fflush(stdout);
		(void)fprintf(stderr, "*** mbk error ***\n");
		(void)fprintf(stderr, "addht impossible : hash table size is '0'\n");
		EXIT(1);
	}
	pTable = (ht *)mbkalloc(sizeof(struct htable));
	pTable->length = len;
	pEl = (htitem *)mbkalloc(len * (sizeof(struct htitem)));
	pTable->pElem = pEl;
	for (i = 0; i < len; i++) {
		pEl[i].key = NULL;
		pEl[i].value = EMPTYHT;
	}
	pTable->count = 0;
	return pTable;
}

/*******************************************************************************
* function delht, delete a hash table                                          *
*******************************************************************************/
void delht(pTable)
ht *pTable;
{
htitem * pEl;

	pEl = pTable->pElem;
	mbkfree(pEl);
	mbkfree(pTable);
}

/*******************************************************************************
* function gethtitem, get an element in a hash table                            *
*******************************************************************************/
int gethtitem(pTable, key)
ht *pTable;
void *key;
{
int co = 0;
int indice = 0;
htitem * pEl;

	indice = hash(key) % pTable->length;
	do {
		if (co++ > HMAX_CALLS) {
			reallocht(pTable);
			return gethtitem(pTable, key);
		}

		pEl = (pTable->pElem) + indice;
		if (pEl->value != EMPTYHT && pEl->value != DELETEHT) {
			if ((int) key == (int) pEl->key)
				return pEl->value;
		} else if (pEl->value == EMPTYHT)
			return EMPTYHT;
		indice = (indice + 1) % pTable->length;
	} while (1);
}

/*******************************************************************************
* function addhtitem, get an element in a hash table                            *
*******************************************************************************/
int addhtitem(pTable, key, value)
ht *pTable;
void *key;
int value;
{
int indice = 0;
htitem *pEl;
int co = 0;

	if (value == EMPTYHT || value == DELETEHT) {
		fflush(stdout);
		(void)fprintf(stderr, "*** mbk error ***\n");
		(void)fprintf(stderr, "addhtitem impossible : value is EMPTYHT or DELETEHT\n");
		EXIT(1);
	}
	if (pTable->count++ > (pTable->length) * 8 / 10) {
		reallocht(pTable);
		return addhtitem(pTable, key, value);
	}

	indice = hash(key) % pTable->length;
	do {
		if (co++ > HMAX_CALLS) {
			reallocht(pTable);
			return addhtitem(pTable, key, value);
		}
		pEl = (pTable->pElem) + indice;
		if (pEl->value == EMPTYHT || pEl->value == DELETEHT) {
			pEl->value = value;
			pEl->key = key;
			return value;
		} else if ((int) pEl->key == (int) key) {
			pTable->count--;
			pEl->value = value;
			return value;
		}
		indice = (indice + 1) % pTable->length;
	} while (1);
}

/*******************************************************************************
* function sethtitem, test if an element exists in a hash table,                *
* adds it anyway, and returns 1 if it used to exist, 0 else.                   *
*******************************************************************************/
int sethtitem(pTable, key, value)
ht *pTable;
void *key;
int value;
{
int indice = 0;
htitem *pEl;
int co = 0;

	if (value == EMPTYHT || value == DELETEHT) {
		fflush(stdout);
		(void)fprintf(stderr, "*** mbk error ***\n");
		(void)fprintf(stderr, "sethtitem impossible : value is EMPTYHT or DELETEHT\n");
		EXIT(1);
	}
	if (pTable->count++ > (pTable->length) * 8 / 10) {
		reallocht(pTable);
		return sethtitem(pTable, key, value);
	}

	indice = hash(key) % pTable->length;
	do {
		if (co++ > HMAX_CALLS) {
			reallocht(pTable);
			return sethtitem(pTable, key, value);
		}
		pEl = (pTable->pElem) + indice;
		if (pEl->value == EMPTYHT || pEl->value == DELETEHT) {
			pEl->value = value;
			pEl->key = key;
			return 0;
		} else if ((int) pEl->key == (int) key) {
			pTable->count--;
			pEl->value = value;
			return 1;
		}
		indice = (indice + 1) % pTable->length;
	} while (1);
}

/*******************************************************************************
* function delhtitem, delete an element in a hash table                         *
*******************************************************************************/
int delhtitem(pTable, key)
ht *pTable;
void *key;
{
int indice = 0;
htitem *pEl;
int co = 0;

	indice = hash(key) % pTable->length;
	do {
		if (co++ > HMAX_CALLS) {
			reallocht(pTable);
			return delhtitem(pTable, key);
		}
		pEl = (pTable->pElem) + indice;
		if (pEl->value != EMPTYHT && pEl->value != DELETEHT) {
			if ((int) key == (int)pEl->key) {
				pTable->count--;
				pEl->value = DELETEHT;
				return pEl->value;
			}
		} else if (pEl->value == EMPTYHT)
			return EMPTYHT;
		indice = (indice + 1) % pTable->length;
	} while (1);
}

/*******************************************************************************
* display contents of an hash table                                            *
*******************************************************************************/
void viewht(pTable, pout)
ht *pTable;
char *(*pout)();
{
int i;
htitem *pEl = pTable->pElem;

	(void)printf("================== viewht ================\n");
	(void)printf("length = %d\t		count = %d\n",
						pTable->length, pTable->count);
	(void)printf("==========================================\n");
	for (i = 0; i < pTable->length; i++) {
		if (pEl->value != EMPTYHT && pEl->value != DELETEHT) {
			printf("index  %d\t", i);
			printf("key    %s\t", pout(pEl->key));
			printf("value  %d \n", pEl->value);
		}
		pEl++;
	}
}

/*******************************************************************************
* realloc space to adapt hash table size to number of entries                  *
*******************************************************************************/
static void reallocht(pTable)
ht *pTable;
{
ht *tabBis;
htitem *pEl;
int i;

	pEl = pTable->pElem;
	tabBis = addht((pTable->length) * 5);
	for (i = 0; i < pTable->length; i++) {
		if (pEl->value != EMPTYHT && pEl->value != DELETEHT)
			addhtitem(tabBis, pEl->key, pEl->value);
		pEl++;
	}
	mbkfree(pTable->pElem);
	pTable->length = tabBis->length;
	pTable->pElem = tabBis->pElem;
	pTable->count = tabBis->count;
}

/*******************************************************************************
* All that needed for a cute banner, by Frederic Petrot                        *
* Used to be a standalone library                                              *
*******************************************************************************/
#define WINDOW_SIZE 81
#define LINES 15
#define ASCENT 13
static char screen[LINES][WINDOW_SIZE];

static int indx(c)
char c;
{
	return c >= '0' && c <= '9' ? (int)c - '0'
										: isupper(c) ? (int)10 + c - 'A'
										: islower(c) ? (int)11 + 'Z' - 'A' + c - 'a'
										: -1;
}

static void banner(s, police, nl)
char *s;
char *police[][62];
int nl;
{
int i, j, k, l, m;
char *line;

	/* rince off :
	   the buffer is filled with nul characteres. */
	for (j = 0; j < nl; j++)
		for (i = 0; i < WINDOW_SIZE; i++)
			screen[j][i] = '\0';
	/* first :
	   filling the buffer with direct table output. */
	while (*s) {
		for (i = 0; i < nl; i++) {
			if ((j = indx(*s)) == -1) {
				fprintf(stderr,
					"alliancebanner: Error: Character out of [0-9A-Za-z] range\n");
				exit(1);
			}
			line = police[j][i];
			if (strlen(line) + strlen(screen[i]) >= WINDOW_SIZE) {
				fprintf(stderr,
	"alliancebanner: Error: Resulting size bigger than %d columns not allowed\n",
										WINDOW_SIZE - 1);
				exit(1);
			}
			strcat(screen[i], line);
			if (*(s + 1) != '\0')
				strcat(screen[i], " ");
		}
		s++;
	}
	for (m = l = -1, j = 0; j < nl; j++)
		for (i = 0; i < WINDOW_SIZE; i++)
			if (screen[j][i] == '@') {
				if (m == -1)
					m = j;
				l = j;
				break;
			}
	k = strlen(screen[0]);
	/* banner :
	   output on stdout. */
	putc('\n', stdout);
	for (j = m; j <= l; j++) {
		for (i = 0; i < (WINDOW_SIZE - k) / 2; i++)
			putc(' ', stdout);
		for (i = 0; i < k; i++)
			putc(screen[j][i], stdout);
		putc('\n', stdout);
	}
}

static void cartouche(tool, tv, comment, date, av)
char *tool, *tv, *comment, *date, *av;
{
int i, j, k, l;
static char *msg[4] = {
	"%s",
	"Alliance CAD System %s,\"%s %s",
	"Copyright (c) %s,\"MASI, CAO-VLSI Team",
	"E-mail support:\"cao-vlsi@masi.ibp.fr"
};
int msgl[4];
char *str;

	/* rince off :
	   the buffer is filled with nul characteres. */
	for (j = 0; j < 8; j++)
		for (i = 0; i < WINDOW_SIZE; i++)
			screen[j][i] = '\0';
	i = strlen(tool);
	str = mbkstrdup(tool);
	for (k = 0; k < i; k++)
		str[k] = isupper(tool[k]) ? tolower(tool[k]) : tool[k];
	sprintf(screen[0], msg[0], comment);
	sprintf(screen[1], msg[1], av, str, tv);
	mbkfree(str);
	sprintf(screen[2], msg[2], date);
	strcat(screen[3], msg[3]);
	for (i = 1; i < 4; i++) {
		msgl[i] = strlen(screen[i]);
		j = j < msgl[i] ? msgl[i] : j;
	}
	for (i = 1; i < 4; i++)
		for (l = 0, k = 0; k < WINDOW_SIZE; k++) {
			if (screen[i][k] == '\0') {
				screen[i + 4][k + l] = '\0';
				break;
			}
			if (screen[i][k] == '"') { /* only once per line */
				for (; l <= j - msgl[i]; l++)
					screen[i + 4][k + l] = ' ';
				continue;
			}
			screen[i + 4][k + l] = screen[i][k];
		}
	/* cartouche :
	   output on stdout. */
	i = strlen(comment);
	putc('\n', stdout);
	for (k = 0; k < (WINDOW_SIZE - i) / 2; k++)
		putc(' ', stdout);
	puts(screen[0]);
	putc('\n', stdout);
	for (i = 1; i < 4; i++) {
		for (k = 0; k < (WINDOW_SIZE - j) / 2; k++)
			putc(' ', stdout);
		for (k = 0; k <= j; k++)
			if (screen[i + 4][k] != 0) /* not so nice, but */
				putc(screen[i + 4][k], stdout);
		putc('\n', stdout);
	}
	putc('\n', stdout);
}

void alliancebanner(tool, tv, comment, date, av)
char *tool, *tv, *comment, *date, *av;
{
	banner(tool, Unknown_Bold_Normal_14, 15);
	cartouche(tool, tv, comment, date, av);
}
