/* $Header:sym.c 12.0$ */
/* $ACIS:sym.c 12.0$ */
/* $Source: /ibm/acis/usr/src/bin/adb_ca/RCS/sym.c,v $ */

#ifndef lint
static char *rcsid = "$Header:sym.c 12.0$";
#endif

static	char sccsid[] = "@(#)sym.c 4.1 5/14/81";
/*
 * adb - symbol table routines
 */
#include "defs.h"
#include <stab.h>

/*
 * Lookup a symbol by name.
 */
struct nlist *
lookup(userSymStr)
	char *userSymStr;
{
	register struct nlist *sp;
	register char *name;
	register int underScore = '_';
	struct nlist *tmpSymPtr = (struct nlist *) 0;
	/* 
	 * User wants _x,  give him _x.
	 * User wants _.x, give him _.x.
	 * User wants x, if _.x is present give it to him, else give _x.
	 * Assume that the symbol table is in no special order.
	 */
	if (symtab)
	for (sp = symtab; sp < esymtab; sp++) {
		name = sp->n_un.n_name;
		if ((sp->n_type&N_STAB) == 0
			&& eqsym(name,userSymStr,underScore)) {
			if(userSymStr[0] == underScore)
				return(cursym = sp);
			else	/* if(userSymStr[0] != '_') */
				if(name[1] == '.' || name[0] != underScore)
					return(cursym = sp);
				else
					tmpSymPtr = sp;
		}
	}
	return (cursym = tmpSymPtr);
}

/*
 * Find the closest symbol to val, and return
 * the difference between val and the symbol found.
 * Leave a pointer to the symbol found as cursym.
 */
findsym(val, type)
	long val;
	int type;
{
	long diff = MAXINT;
	register struct nlist *sp;
	int len;
	char *np;
	
	cursym = 0;
	if (type == NSYM || symtab == 0)
		return (diff);
	for (sp = symtab; sp < esymtab; sp++) {
		if (	/* (sp->n_type&N_EXT) == 0 || */
			sp->n_type&N_STAB
		    || sp->n_value == 0)  /* Don't find clutter like
					     .oVxxx absolute symbols */
			continue;
		if (val - sp->n_value < diff && val >= sp->n_value) {
			np = sp->n_un.n_name;
			len = strlen(np);
			if (np[len-2] == '.' && np[len-1] == 'o'
				&& np[0] != '_')	/* e.g. x.o */
					continue;
			diff = val - sp->n_value;
			cursym = sp;
			if (diff == 0)
				break;
		}
	}
	return (diff);
}

/* Like findsym(), but finds symbol table entries for
 * static and global functions. */
staticSym(val)
	long val;
{
	register struct nlist *sp;
	register long diff = MAXINT;
	register int len;
	register char *np;

	cursym = (struct nlist *)0;
	for (sp = symtab; sp < esymtab; sp++) {
		if ((sp->n_type & N_TEXT)==0 || sp->n_type & N_STAB)
			continue;
			/* Accept only global or static text symbol */
		if (val - sp->n_value < diff && val >= sp->n_value) {
			np = sp->n_un.n_name;
			len = strlen(np);
			if (np[len-2] == '.' && np[len-1] == 'o'
				&& np[0] != '_')	/* e.g. x.o */
					continue;
			diff = val - sp->n_value;
			cursym = sp;
			if (diff == 0)
				break;
		}
	}
	return (diff);
}

/*
 * Advance cursym to the next local variable.
 * Leave its value in localval as a side effect.
 * Return 0 at end of file.
 */
localsym(cframe, cargp)
	ADDR cframe, cargp;
{
	register int type;
	register struct nlist *sp;

	if (cursym)
	for (sp = cursym; ++sp < esymtab; ) {
/**/		printf("next sym = %s\n", sp->n_un.n_name);
		if (sp->n_un.n_name[0] =='_' || sp->n_type == N_FN)
			return (0);
		type = sp->n_type;
		switch (sp->n_type) {

		case N_TEXT:
		case N_TEXT|N_EXT:
		case N_DATA:
		case N_DATA|N_EXT:
		case N_BSS:
		case N_BSS|N_EXT:
			localval = sp->n_value;
			cursym = sp;
			return (1);

		case N_LSYM:
			localval = cframe - sp->n_value;
			cursym = sp;
			return (1);

		case N_PSYM:
			/* code below works since n_value > 0 */
		case N_ABS:
			if (sp->n_value < 0)
				localval = cframe + sp->n_value;
			else
				localval = cargp + sp->n_value;
			cursym = sp;
			return (1);
		}
	}
	cursym = 0;
	return (0);
}

/*
 * Print value v and then the string s.
 * If v is not zero, then we look for a nearby symbol
 * and print name+offset if we find a symbol for which
 * offset is small enough.
 *
 * For values which are just into kernel address space
 * that they match exactly or that they be more than maxoff
 * bytes into kernel space.
 */
psymoff(v, type, s)
	long v;
	int type;
	char *s;
{
	long w;
	if(symTableIsPresent() && v && type == ISP) {
		if(valpr(v, type) == 0)
			printf(LPRMODE,v);
	}
	else {
		if (v) 
			w = findsym(v, type);
		if (v==0 || w >= maxoff || (KVTOPH(v) < maxoff && w))
			printf(LPRMODE, v);
		else {
			printf("%s", cursym->n_un.n_name);
			if (w)
				printf(OFFMODE, w);
		}
	}
	printf(s);
}

/*
 * Print value v symbolically if it has a reasonable
 * interpretation as name+offset.  If not, print nothing.
 * Used in printing out registers $r.
 */
valpr(v, idsp)
	register long v;
{
	off_t d;

	d = (idsp == ISP) ? staticSym(v) : findsym(v,idsp);
	dprintf(("\t+psymoff:idsp=%X d=%D v=%X\n",idsp,d,v));
	if(d >= maxoff)
		return(0);
	printf("%s", cursym->n_un.n_name);
	if (d)
		printf(OFFMODE, d);
	return(1);
}
