/*
 * init the symbol table
 */

#include "defs.h"
#include "sym.h"
#include <a.out.h>
#include <stab.h>
#include <sys/types.h>

#define	NSYMS	300
#define	BIGBUF	(4*4096)

extern struct sym *symtab;

syminit(h)
struct exec *h;
{
	register struct nlist *q;
	register n, m;
	int strsiz;
	struct nlist space[BIGBUF/sizeof(struct nlist)];
	struct sym *sbuf;
	register struct sym *cur;
	struct sym *curgbl;
	register char *names;
	register int type, ltype;
	char *malloc();

	symtab = NULL;
	if (h->a_syms == 0)
		return;		/* stripped */
	lseek(fsym, (off_t)N_STROFF(*h), 0);
	if (read(fsym, &strsiz, sizeof(strsiz)) != sizeof(strsiz)) {
		printf("can't get string table size\n");
		return;
	}
	if ((names = malloc(strsiz)) == NULL) {
		printf("no mem for strings\n");
		return;
	}
	lseek(fsym, (off_t)N_STROFF(*h), 0);
	if (read(fsym, names, strsiz) != strsiz) {
		printf("error reading strings\n");
		return;
	}
	lseek(fsym, (off_t)N_SYMOFF(*h), 0);
	curgbl = sbuf = cur = NULL;
	for (n = h->a_syms; n > 0 ;) {
		m = read(fsym, (char *)space, min(n, sizeof(space)));
		if (m <= 0)
			break;
		n -= m;
		for (q = space; m > 0; q++, m-= sizeof(space[0])) {
			if (q->n_un.n_strx == 0)
				continue;
			switch (q->n_type) {
			case N_ABS:
			case N_ABS|N_EXT:
				type = S_ABS;
				break;

			case N_TEXT:
				if (names[q->n_un.n_strx] != '_')
					continue;
				/* fall in */
			case N_BFUN:
			case N_TEXT|N_EXT:
				type = S_TEXT;
				break;

			case N_DATA:
			case N_DATA|N_EXT:
			case N_BSS:
			case N_BSS|N_EXT:
				type = S_DATA;
				break;

			case N_LSYM:
				ltype = S_LSYM;
				type = S_STAB;
				break;

			case N_RSYM:
				ltype = S_RSYM;
				type = S_STAB;
				break;

			case N_PSYM:
				ltype = S_PSYM;
				type = S_STAB;
				break;

			case N_STSYM:
			case N_LCSYM:
				ltype = S_STSYM;
				type = S_STAB;
				break;

			default:
				continue;
			}
			if (sbuf == NULL || ++cur >= sbuf + NSYMS) {
				if ((sbuf = (struct sym *)malloc(sizeof(struct sym) * NSYMS)) == NULL) {
					printf("out of mem for syms\n");
					return;
				}
				cur = sbuf;
			}
			cur->y_type = type;
			cur->y_ltype = ltype;
			cur->y_value = q->n_value;
			cur->y_name = &names[q->n_un.n_strx];
			cur->y_locals = NULL;
			if (q->n_type == N_BFUN) {
				cur->y_next = curgbl;
				curgbl = cur;
			}
			else if (cur->y_type == S_STAB) {
				if (curgbl == NULL)
					continue;
				cur->y_next = curgbl->y_locals;
				curgbl->y_locals = cur;
			}
			else {
				cur->y_next = symtab;
				symtab = cur;
			}
		}
	}
	for (; curgbl; curgbl = curgbl->y_next) {
		if (curgbl->y_locals == NULL)
			continue;
		for (cur = symtab; cur; cur = cur->y_next) {
			if (cur->y_type != S_TEXT)
				continue;
			if (cur->y_value == curgbl->y_value) {
				cur->y_locals = curgbl->y_locals;
				break;
			}
		}
	}
}

/*
 * is symbol table entry s == name n?
 * this may depend on awful symbol conventions
 * e.g. _
 */
int
eqsym(s, n)
register struct sym *s;
char *n;
{

	if (strcmp(s->y_name, n) == 0)
		return (1);
	if (s->y_name[0] == '_' && strcmp(&s->y_name[1], n) == 0)
		return (1);
	return (0);
}
