/* Copyright (c) 1988 by Sozobon, Limited.  Author: Johann Ruegg
 *
 * Permission is granted to anyone to use this software for any purpose
 * on any computer system, and to redistribute it freely, with the
 * following restrictions:
 * 1) No charge may be made other than reasonable charges for reproduction.
 * 2) Modified versions must be clearly marked as such.
 * 3) The authors are not responsible for any harmful consequences
 *    of using this software, even if they result from defects in it.
 *
 *	nodes.c
 *
 *	Node allocation, deallocation, searching, printing
 *	and other node handling
 */

#include <stdio.h>
#include "param.h"
#include "nodes.h"

extern FILE *output;
NODE *freelist;

#define NODEINCR	100

extern int oflags[];
#define debug oflags['n'-'a']

#define NODELEN	(sizeof(NODE)/4)

int nodesmade, nodesavail;

NODE *
allocnode()
{
	char *calloc();
	NODE *t;
	int i;

retry:
	if (freelist != 0) {
		t = freelist;
		freelist = t->n_next;
		lclr(t, NODELEN);
		nodesavail--;
		if (debug)
			printf("%lx+ ", t);
		return t;
	}
	t = (NODE *)calloc(NODEINCR, sizeof(NODE));
	if (t == 0) {
		printf("malloc failure\n");
		exit(1);
	}
	nodesmade += NODEINCR;
	nodesavail += NODEINCR;
	for (i=0; i<NODEINCR; i++)
		t[i].n_next = &t[i+1];
	t[NODEINCR-1].n_next = 0;
	freelist = t;
	goto retry;
}

freeunit(t)
NODE *t;
{
	if (t->n_flags & N_ISFREE) {
		printf("%lx ", t);
		error("Freeing free node");
		exit(1);
	} else
		t->n_flags |= N_ISFREE;
	t->n_next = freelist;
	freelist = t;
	nodesavail++;
	if (debug)
		printf("%lx- ", t);
}

freenode(t)
NODE *t;
{
	register NODE *nxt;

	if (t == NULL) return;
again:
	if (t->n_right)
		freenode(t->n_right);
	if (t->n_nmx)
		freenode(t->n_nmx);
	if (t->n_tptr && (t->n_flags & N_COPYT) == 0)
		freenode(t->n_tptr);
	nxt = t->n_left;
	freeunit(t);
	if (nxt) {
		t = nxt;
		goto again;	/* minimize left recursion */
	}
}

put_nnm(t)
NODE *t;
{
	printf("%s", t->n_name);
	while (t->n_nmx) {
		t = t->n_nmx;
		printf("%s", t->n_name);
	}
}

qput_nnm(t, fd)
NODE *t;
FILE *fd;
{
	fprintf(fd, "%s", t->n_name);
	while (t->n_nmx) {
		t = t->n_nmx;
		fprintf(fd, "%s", t->n_name);
	}
}

fput_nnm(t)
NODE *t;
{
	fprintf(output, "%s", t->n_name);
	while (t->n_nmx) {
		t = t->n_nmx;
		fprintf(output, "%s", t->n_name);
	}
}

/* add a short string (less than NMXSIZE) to front of name */
nnmins(t, s)
NODEP t;
char *s;
{
	register i, j;
	char tbuf[NMSIZE];
	NODEP n;

	i = strlen(t->n_name);
	j = strlen(s);
	if (j > NMSIZE-1)
		return;		/* compiler error */
	if (i+j <= NMSIZE-1) {		/* fits in node */
		strcpy(tbuf, t->n_name);
		strcpy(t->n_name, s);
		strcpy(t->n_name+j, tbuf);
	} else {
		n = allocnode();
		n->n_nmx = t->n_nmx;
		t->n_nmx = n;
		strcpy(n->n_name, t->n_name);
		strcpy(t->n_name, s);
	}
}

/* add a short string (less than NMXSIZE) to end of name */
nnmadd(t, s)
NODE *t;
char *s;
{
	register i,j;
	int sizeb;
	NODEP n;

	/* find last node */
	sizeb = NMSIZE;
	while (t->n_nmx) {
		t = t->n_nmx;
		sizeb = NMXSIZE;
	}
	/* fits in current last node? */
	i = strlen(s);
	j = strlen(t->n_name);
	if (i < sizeb-j) {
		strcat(t->n_name, s);
		return;
	}
	/* put all of s in new node */
	n = allocnode();
	t->n_nmx = n;
	t = n;
	strncpy(t->n_name, s, NMXSIZE-1);
	t->n_name[NMXSIZE-1] = 0;
}

nscpy(t, s)
NODE *t;
char *s;
{
	register i;
	NODEP n;

	i = strlen(s);
	strncpy(t->n_name, s, NMSIZE-1);
	t->n_name[NMSIZE-1] = 0;
	i -= NMSIZE-1;
	s += NMSIZE-1;
	while (i > 0) {
		n = allocnode();
		t->n_nmx = n;
		t = n;
		strncpy(t->n_name, s, NMXSIZE-1);
		t->n_name[NMXSIZE-1] = 0;
		i -= NMXSIZE-1;
		s += NMXSIZE-1;
	}
}

putlist(head, np)
NODE **head, *np;
{
	np->n_next = *head;
	*head = np;
}

puthlist(head, np)
NODE *head[], *np;
{
	putlist(&head[hash(np->n_name)], np);
}

NODE *
llook(head, np)
NODE *head, *np;
{
	register NODEP p;

	for (p=head; p != NULL; p = p->n_next)
		if (xstrcmp(p, np) == 0) {
			return p;
		}
	return NULL;
}

NODE *
hlook(head, np)
NODE *head[], *np;
{
	register NODEP p;

	p = head[hash(np->n_name)];
	return llook(p, np);
}

hash(s)
register char *s;
{
	register hval;

	hval = 0;
	while (*s)
		hval += *s++;
	return hval & (NHASH-1);
}

xstrcmp(p1, p2)
NODE *p1, *p2;
{
	int rv;

	if ((rv = strcmp(p1->n_name, p2->n_name)) != 0)
		return rv;
	if (p1->n_nmx == NULL) {
		if (p2->n_nmx == NULL)
			return 0;
		return -1;
	}
	if (p2->n_nmx == NULL)
		return 1;
	return xstrcmp(p1->n_nmx, p2->n_nmx);
}

char *
scopy(s)
char *s;
{
	int i;
	char *p;

	i = strlen(s)+1;
	if (i > sizeof(NODE)) {
		error("preproc name too big");
		i = sizeof(NODE);
		s[i-1] = 0;
	}
	p = (char *)allocnode();
	strcpy(p, s);
	return p;
}

sfree(s)
char *s;
{
	NODEP np;

	np = (NODEP)s;
	np->n_flags = 0;
	freeunit(np);
}

printlist(np)
NODE *np;
{
	putchar('\n');
	prln(np, 2);
}

prln(np, indent)
NODE *np;
{
	register NODE *svl, *nxtl;

	for (svl=np; svl != NULL; svl = nxtl) {
		nxtl = svl->n_next;
		svl->n_next = NULL;
		prnode(svl,indent);
		svl->n_next = nxtl;
		/* special hack for tag list */
		if ((svl->n_flags & N_BRKPR) && svl->n_right)
			prln(svl->n_right, indent+2);
	}
}

codeprint(np)
NODEP np;
{
	putchar('\n');
	cprnode(np,0);
}

cprnode(np,indent)
NODE *np;
{
	int ni;
	NODEP tp;

	ni = indent+1;
	while (indent--)
		putchar(' ');
	if (np == NULL) {
		printf("<NULL>\n");
		return;
	}
	put_nnm(np);	/* Note: BRKPR doesnt break long names */
	if (np->g_offs)
		printf(" o%ld ", np->g_offs);
	if (np->g_rno)
		printf(" r%d ", np->g_rno);
	if (np->g_needs)
		printf(" n%x ", np->g_needs);
	if (debug) {
		printf("@%lx ", np);
		if (np->n_flags & N_COPYT)
			printf("C ");
		if (np->n_flags & N_BRKPR)
			printf("B ");
	}
	if (np->n_flags & N_BRKPR) {
		putchar('\n');
		return;
	}
	if (np->g_betw)
		printf(" {%s}", np->g_betw);
	if (np->g_code) {
		if (np->n_flags & N_COPYT)
			printf(" <%s>", np->g_code);
		else
			for (tp=np->g_code; tp; tp = tp->g_code)
				printf(" <%s>", tp->n_name);
	}
	putchar(' ');
	out_a(np, stdout);
	putchar('\n');
	if (np->n_left) {
		cprnode(np->n_left,ni);
	} else if (np->n_right)
		cprnode(NULL, ni);
	if (np->n_right) {
		cprnode(np->n_right,ni);
	}
}

printnode(np)
NODE *np;
{
	putchar('\n');
	prnode(np,0);
}

prnode(np,indent)
NODE *np;
{
	int ni;

	ni = indent+1;
	while (indent--)
		putchar(' ');
	if (np == NULL) {
		printf("<NULL>\n");
		return;
	}
	put_nnm(np);	/* Note: BRKPR doesnt break long names */
	if (np->e_offs)
		printf(" o%ld ", np->e_offs);
	if (np->e_rno)
		printf(" r%d ", np->e_rno);
	if (np->e_fldw)
		printf(" (%d,%d) ", np->e_fldw, np->e_fldo);
	if (debug) {
		printf("@%lx ", np);
		if (np->n_flags & N_COPYT)
			printf("C ");
		if (np->n_flags & N_BRKPR)
			printf("B ");
	}
	if (np->n_flags & N_BRKPR) {
		putchar('\n');
		return;
	}
	if (np->n_tptr) {
		if (np->e_flags & 256)	/* IMMEDID */
			printf(" $$$ ");
		tprint(np->n_tptr);
	}
	putchar('\n');
	if (np->n_left) {
		prnode(np->n_left,ni);
	} else if (np->n_right)
		prnode(NULL, ni);
	if (np->n_right) {
		prnode(np->n_right,ni);
	}
}

tprint(np)
NODEP np;
{
	while (np != NULL) {
		putchar(' ');
		put_nnm(np);
#ifdef HANS
		if (np->t_size)
			printf(" s%ld", np->t_size);
		if (np->t_aln)
			printf(" a%d", np->t_aln);
#endif
		if (debug)
			printf("@%lx", np);
		np = np->n_tptr;
	}
}

NODEP
copynode(op)
NODEP op;
{
	NODEP np;

	if (op == NULL) return NULL;
	np = allocnode();
	lcpy(np, op, NODELEN);
	if (np->n_nmx)
		np->n_nmx = copynode(np->n_nmx);
	if (np->n_right)
		np->n_right = copynode(np->n_right);
	if (np->n_left)
		np->n_left = copynode(np->n_left);
	if (np->n_tptr)
		np->n_flags |= N_COPYT;
	return np;
}

NODEP
copyone(op)
NODEP op;
{
	NODEP np;

	if (op == NULL) return NULL;
	np = allocnode();
	lcpy(np, op, NODELEN);
	if (np->n_nmx)
		np->n_nmx = copyone(np->n_nmx);
	if (np->n_right)
		np->n_right = NULL;
	if (np->n_left)
		np->n_left = NULL;
	if (np->n_tptr)
		np->n_flags |= N_COPYT;
	return np;
}

NODEP
copy_nol(op)
NODEP op;
{
	NODEP np;

	if (op == NULL) return NULL;
	np = allocnode();
	lcpy(np, op, NODELEN);
	if (np->n_nmx)
		np->n_nmx = copynode(np->n_nmx);
	if (np->n_right) /* break right links */
		np->n_right = NULL;
	if (np->n_tptr)
		np->n_flags |= N_COPYT;
	return np;
}

NODEP
copylist(np, tailp)
NODE *np, **tailp;
{
	NODEP rv, nx;
	register NODEP tail;

	if (np == NULL) {
		*tailp = NULL;
		return NULL;
	}
	rv = copy_nol(np);
	tail = rv;
	while (tail->n_left) {
		nx = copy_nol(tail->n_left);
		tail->n_left = nx;
		tail = nx;
	}
	*tailp = tail;
	return rv;
}

NODE *
nthnode(np, n)
NODE *np;
{
	while (n--)
		if (np == NULL)
			return NULL;
		else
			np=np->n_next;
	return np;
}

NODE *
rthnode(np, n)
NODE *np;
{
	while (n--)
		if (np == NULL)
			return NULL;
		else
			np=np->n_right;
	return np;
}
