/*
 * This file contains BFD symbol table and STAB type information 
 * access routines.
 *
 * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
 */
#include <klib.h>
#include <bfd.h>
#include <stab.h>

/* "roots" for binary search trees containing various types of symbol 
 * data. There are multiple trees for performance reasons (quicker tree 
 * building and searching) and to allow records of different types to 
 * have the same name (e.g., when a typedef is the same name as a type). 
 * We COULD have allowed duplicate entries in a single tree, but this 
 * approach is faster and cleaner. Note that these trees are global and 
 * may contain information from multiple namelists. 
 */
static st_sym_t *type_tree = (st_sym_t *)NULL;
static st_sym_t *typedef_tree = (st_sym_t *)NULL;
static st_sym_t *func_tree = (st_sym_t *)NULL;
static st_sym_t *srcfile_tree = (st_sym_t *)NULL;
static st_sym_t *var_tree = (st_sym_t *)NULL;
static st_sym_t *xtype_tree = (st_sym_t *)NULL;

/* Linked list of st_sym_t records we don't quite know what to do 
 * with...
 */
static st_sym_t *symlist = (st_sym_t *)NULL;
static st_sym_t *symlist_end = (st_sym_t *)NULL;

/* Global error stabs 'errno' value
 */
static int stab_err;

/* Local structure that contains the current type string (which may be 
 * just a part of the complete type defenition string) and the character 
 * index (current) pointer.
 */
typedef struct stab_str_s {
        char            *str;
        char            *ptr;
} stab_str_t;

/* Local structure containing global values that allow us to cycle
 * through multiple object files without reinitializing. 
 */
typedef struct st_global_s {
	bfd		*abfd;		/* current bfd pointer */
	int		 type;		/* symbol entry type */
	int		 flags;		/* want flags */
	int		 flag;		/* current ST flag */
	int		 nmlist;	/* current namelist index */
	int		 srcfile;	/* current source file number */
	int		 incfile;	/* current include file */
	int		 symnum;	/* symbol entry number */
	bfd_byte	*stabp;		/* beg of current string table */
	bfd_byte	*stabs_end;	/* end of current string table */
	int		 staboff;	/* current stab table offset */
	unsigned int	 value;		/* value (e.g., function addr) */
	int		 stroffset;	/* offset in stab string table */
	short		 desc;		/* desc value (e.g, line number) */
	stab_str_t 	 stab_str;	/* current stab string */
} st_global_t;

static st_global_t G_values;

/* Macros for accessing the current global values
 */
#define G_abfd		G_values.abfd
#define G_type 		G_values.type
#define G_flags 	G_values.flags
#define G_flag  	G_values.flag 
#define G_nmlist 	G_values.nmlist
#define G_srcfile 	G_values.srcfile
#define G_incfile 	G_values.incfile
#define G_symnum	G_values.symnum
#define G_stabp         G_values.stabp
#define G_stabs_end     G_values.stabs_end
#define G_staboff	G_values.staboff
#define G_value     	G_values.value
#define G_stroffset	G_values.stroffset
#define G_desc     	G_values.desc
#define G_stab_str     	G_values.stab_str
#define CUR_CHAR 	G_stab_str.ptr

/* Global flag that indicates if we are in the process of opening a
 * namelist.
 */
static int opening_namelist = 0;

/* Structure used to track start and end of include file scope.
 * Note that include files can include other include files -- between 
 * start_off and end_off. 
 */
typedef struct incfile_s {
	struct incfile_s 	*next;		/* on list */
	struct incfile_s 	*prev;		/* on list/stack */
	int			 srcfile;
	int 			 incfile;
	int			 stroff;
	unsigned int		 value;
	int			 staboff;
	int			 start_num;
	int			 end_num;
} incfile_t;

/* Top of the include file stack
 */
static incfile_t *incp_top = (incfile_t *)NULL;
static int next_incp = 1;

/* Structure that contains all 'private' data for a namelist (archive 
 * or object file).
 */
typedef struct st_namelist_s {
	bfd 		*abfd;
	incfile_t	*incp_list;
} st_namelist_t;

#define ST_PRIVATE 	((st_namelist_t *)nmlist[curnmlist].private)
#define ABFD 		ST_PRIVATE->abfd
#define INCP_LIST 	ST_PRIVATE->incp_list

#define TYPE_NUM(X) 	((uint64_t)(X) & 0xffffffff) 
#define SRC_FILE(X) 	(((uint64_t)(X) >> 48) & 0xfff) 
#define INC_FILE(X) 	(((uint64_t)(X) >> 32) & 0xffff)
#define NMLIST(X) 	(((uint64_t)(X) >> 60) & 0xf)

#define TYPE_NUM_SLOTS (255)
#define TYPE_NUM_HASH(X) \
	(((SRC_FILE(X)<<1)+TYPE_NUM(X)) % (TYPE_NUM_SLOTS - 1))

/* Hash table for hashing sym records via typenum. If the typenum is
 * for a duplicate definition, then the hash record will point to the
 * one that was already captured. 
 */
static st_hashrec_t *st_hash[TYPE_NUM_SLOTS];

/* Local static variables (stabs memory)
 */
static bfd_byte *stabs = (bfd_byte *)NULL;
static bfd_size_type stab_size = (bfd_size_type)0;
static char *strtab = (char *)NULL;
static bfd_size_type stabstr_size = (bfd_size_type)0;;

/* Forward function declarations
 */
static char *get_next_sym_str(bfd *, int);
static int get_next_str(void);
static st_sym_t *get_next_stab_entry(void);
static int read_stab_data(bfd *);
static int st_get_typeinfo(st_sym_t *);
static st_sym_t *get_sym_info(void);
static st_sym_t *alloc_st_sym(void);
static void free_st_sym(st_sym_t *);
st_sym_t *find_st_sym(char *, int, uint64_t);
static char *bump_str_ptr(int);
static char *get_name(char **);
static char *get_symdesc(char *);
static char *get_typenum(uint64_t *);
static char *get_typenum_type(uint64_t *, int);
static char *get_array(uint64_t *, uint64_t *, int *, int *);
static char *get_members(stab_type_t *);
static char *get_byte_size(int *);
static char *get_enumlist(stab_type_t *);
static int __setup_typeinfo(st_sym_t *);
static int __get_typeinfo(stab_type_t *);
void set_type_ptrs(stab_type_t *);
static int insert_st_sym(st_sym_t *);
static st_sym_t *find_embedded_type(uint64_t);
static int set_srcfile(bfd *, int);
static int get_typenum_embedded(uint64_t);
static char *get_range(uint64_t *, int *, int *);
static int __get_typestring(stab_type_t *);
stab_type_t *find_type(uint64_t);
int st_setup_typeinfo(void);

/* 
 * save_global_values()
 */
static void
save_global_values(st_global_t *G)
{
	G->abfd = G_abfd;
	G->type = G_type;
	G->flags = G_flags;
	G->flag = G_flag;
	G->nmlist = G_nmlist;
	G->srcfile = G_srcfile;
	G->incfile = G_incfile;
	G->symnum = G_symnum;
	G->stabp = G_stabp;
	G->stabs_end = G_stabs_end;
	G->value = G_value;
	G->stroffset = G_stroffset;
	G->desc = G_desc;
	G->stab_str = G_stab_str;
	G->stab_str.ptr = CUR_CHAR;
}

/* 
 * restore_global_values()
 */
static void
restore_global_values(st_global_t *G)
{
	G_abfd = G->abfd;
	G_type = G->type;
	G_flags = G->flags;
	G_flag = G->flag;
	G_nmlist = G->nmlist;
	G_srcfile = G->srcfile;
	G_incfile = G->incfile;
	G_symnum = G->symnum;
	G_stabp = G->stabp;
	G_stabs_end = G->stabs_end;
	G_value = G->value;
	G_stroffset = G->stroffset;
	G_desc = G->desc;
	G_stab_str = G->stab_str;
	CUR_CHAR = G->stab_str.ptr;
}

/*
 * alloc_incp()
 */
incfile_t *
alloc_incp(void)
{
	incfile_t *incp;

	incp = kl_alloc_block(sizeof(incfile_t), K_PERM);
	return(incp);
}

/*
 * free_incp()
 */
void
free_incp(incfile_t *incp)
{
	if (incp) {
		kl_free_block(incp);
	}
}

/* 
 * push_incp()
 */
void
push_incp(incfile_t *incp)
{
	if (incp_top) {
		incp->prev = incp_top;
	} 
	incp_top = incp;
}

/*
 * pop_incp()
 */
incfile_t *
pop_incp(void)
{
	incfile_t *incp = (incfile_t *)NULL;

	if ((incp = incp_top)) {
		incp_top = incp->prev;
	}
	return(incp);
}

/*
 * queue_incp()
 */
void
queue_incp(incfile_t *incp)
{
	incfile_t *p, *last = (incfile_t *)NULL;

	incp->prev = incp->next = (incfile_t *)NULL;
	if ((p = INCP_LIST)) {
		if (incp->srcfile < p->srcfile) {
			p->prev = incp;
			incp->next = p;
			INCP_LIST = incp;
			return;
		}
		while(p) {
			if (incp->srcfile > p->srcfile) {
				last = p;
				p = p->next;
			} else if (incp->srcfile == p->srcfile) {
				while(incp->incfile > p->incfile) {
					last = p;
					if (!(p = p->next)) {
						last->next = incp;
						incp->prev = last;
						return;
					}
					if (p->srcfile != incp->srcfile) {
						break;
					} 
				}
				if (!p->prev) {
					/* Our new record goes first on the
					 * list
					 */
					p->prev = incp;
					incp->next = p;
					INCP_LIST = incp;
					return;
				}
				p->prev->next = incp;
				incp->prev = p->prev;
				p->prev = incp;
				incp->next = p;
				return;
			} else {
				break;
			}
		}
		if (last->next) {
			last->next->prev = incp;
			incp->next = last->next;
		}
		last->next = incp;
		incp->prev = last;
	} else {
		INCP_LIST = incp;
	}
}

/*
 * find_incp()
 */
incfile_t *
find_incp(int srcfile, int incfile)
{
	incfile_t *incp = INCP_LIST;

	while (incp) {
		if (incp->srcfile < srcfile) {
			incp = incp->next;
			continue;
		} else if (incp->srcfile == srcfile) {
			while (incp->incfile < incfile) {
				if (!(incp = incp->next)) {
					return((incfile_t *)NULL);
				}
				if (incp->srcfile != srcfile) {
					return((incfile_t *)NULL);
				}
			}
			if (incp->incfile == incfile) {
				return(incp);
			} else {
				return((incfile_t *)NULL);
			}
		} else {
			return((incfile_t *)NULL);
		}
	}
	return((incfile_t *)NULL);
}

/*
 * hash_stab()
 */
static void
hash_stab(uint64_t typenum, st_sym_t *stp)
{
	st_hashrec_t *shp, *hshp;

	if ((typenum == 0) || (!stp)) {
		return;
	}
	shp = (st_hashrec_t *)xmalloc(sizeof(st_hashrec_t));
	shp->h_typenum = typenum; 
	shp->h_ptr = stp;
	shp->h_next = (st_hashrec_t *)NULL;
	if ((hshp = st_hash[TYPE_NUM_HASH(typenum)])) {
		while (hshp->h_next) {
			hshp = hshp->h_next;
		}
		hshp->h_next = shp;
	} else {
		st_hash[TYPE_NUM_HASH(typenum)] = shp;
	}
}

/*
 * get_stab_entries()
 */
static int
get_stab_entries(void)
{
	st_sym_t *stp;	
	char **matching;

	if (!bfd_check_format_matches(G_abfd, bfd_object, &matching)) {
		 if (bfd_get_error() == bfd_error_file_ambiguously_recognized) {
			free(matching);
		 }
		return(1);
	}

	/* Read in the stab section data.
	 */
	if (!read_stab_data(G_abfd)) {
		return(1);
	}

	/* Now loop through all the stab type entries 
	 */
	while (1) {

		if (!(stp = get_next_stab_entry())) {
			break;
		}
		if (!(G_symnum % 1000)) {
			fprintf(stderr, ".");
		}
		/* We only log records for source files, types, typedefs,
		 * functions and variables...
		 */
		if (stp->sym_type & 
			(ST_SRCFILE|ST_TYPE|ST_TYPEDEF|ST_FUNC|ST_VAR)) {
			if (insert_st_sym(stp)) {
				fprintf(stderr, "ERROR!\n");
			}
		} else {
			free_st_sym(stp);
		}
	}
	return(0);
}

/*
 * st_open_namelist()
 */
static int
st_open_namelist(char *filename, int flags)
{
	bfd *arbfd = (bfd *)NULL;

	/* Set a flag to indicate that we are in the process of 
	 * opening a namelist. This allows some routines to be 
	 * used during initialization and for symbol lookup.
	 */
	opening_namelist = 1;

	/* Allocate a private data structure and link it into the 
	 * current namelist record.
	 */
	ST_PRIVATE = kl_alloc_block(sizeof(st_namelist_t), K_PERM);
	if (KL_ERROR) {
		return(1);	
	}

	if (!(ABFD = bfd_openr(filename, "default"))) {
		fprintf(stderr, "\nCould not open namelist file: %s\n", 
			filename);
		return(1);
	}

	/* Initialize global values
	 */
	G_srcfile = 0;               
	G_incfile = 0;               
	G_symnum = -2;                  
	G_type = 0;                  
	G_stroffset = 0;             
	G_desc = 0;                
	G_value = 0;       
	G_stabp = 0;           
	G_stabs_end = 0;       
	G_flags = flags;

	/* Initialize the include file stack
	 */
	incp_top = (incfile_t *)NULL;
	next_incp = 1;

	/* Check to see if this namelist is an archive. If it is, then
	 * we need to cycle through the individual object files and
	 * process them one-by-one. 
	 */
	if (bfd_check_format(ABFD, bfd_archive) == TRUE) {

		for (;;) {
			bfd_set_error(bfd_error_no_error);

			arbfd = bfd_openr_next_archived_file(ABFD, arbfd);
			if (arbfd == NULL) {
				if (bfd_get_error() != 
					bfd_error_no_more_archived_files) {
					/* XXX -- non-fatal error */ 
				}
				break;
			}
			G_abfd = arbfd;
			if (get_stab_entries()) {
				fprintf(stdout, "%s is not an object file\n",
					filename);
			}
			G_symnum = -2;
			G_stabp = 0;
			G_stabs_end = 0;
		}
	} else {
		G_abfd = ABFD;
		if (get_stab_entries()) {
			fprintf(stdout, "%s is not an object file\n", filename);
		} 
	}
	opening_namelist = 0;
	return(0);
}

static asection *last_stabsect = 0, *last_stabstrsect = 0; 

/*
 * free_stab_memory()
 */
static void
free_stab_memory(void)
{
	last_stabsect = 0;
	last_stabstrsect = 0;
	if (stabs) {
		free(stabs);
		stabs = (bfd_byte *)NULL;
		stab_size = (bfd_size_type)0;
	}
	if (strtab) {
		free(strtab);
		strtab = (char *)NULL;
		stabstr_size = (bfd_size_type)0;;
	}
}

/*
 * read_stab_data()
 */
static int
read_stab_data(bfd *abfd)
{
	asection *stabsect, *stabstrsect;
	static char stabsect_name[] = ".stab";
	static char strsect_name[] = ".stabstr";

	if (!(stabsect = bfd_get_section_by_name(abfd, stabsect_name))) {
		return(FALSE);
	}
	if (!(stabstrsect = bfd_get_section_by_name(abfd, strsect_name))) {
		return(FALSE);
	}

	/* Check to see if the last one we loaded is the same one
	 * we are trying to load now.
	 */
	if ((stabsect == last_stabsect) && (stabstrsect == last_stabstrsect)) {
		return(TRUE);
	}
	free_stab_memory();

	stab_size = bfd_section_size(abfd, stabsect);
	stabstr_size = bfd_section_size(abfd, stabstrsect);

	stabs  = (bfd_byte *)xmalloc(stab_size);
	strtab = (char *)xmalloc(stabstr_size);

	if (!bfd_get_section_contents(abfd, stabsect, 
			(PTR)stabs, 0, stab_size)) {
		free_stab_memory();
		return(FALSE);
	}
	if (!bfd_get_section_contents(abfd, stabstrsect, 
				(PTR)strtab, 0, stabstr_size)) {
		free_stab_memory();
		return(FALSE);
	}
	last_stabsect = stabsect;
	last_stabstrsect = stabstrsect;
	return(TRUE);
}

/*
 * find_st_sym()
 */
st_sym_t *
find_st_sym(char *name, int type, uint64_t typenum)
{
	st_sym_t *stp = (st_sym_t *)NULL;
	if (name) {
		/* Cycle through the type flags and see if any records are
		 * present. Note that if multiple type flags or ST_ALL is 
		 * passed in, only the first occurance of 'name' will be 
		 * found and returned. If name exists in multiple trees, 
		 * then multiple searches are necessary to find them.
		 */
		if (type & ST_SRCFILE) {
			if ((stp = (st_sym_t *)kl_find_btnode((btnode_t *)
					srcfile_tree, name, (int *)NULL))) {
				goto found_sym;
			}
		} 
		if (type & ST_TYPE) {
			if ((stp = (st_sym_t *)kl_find_btnode((btnode_t *)
					type_tree, name, (int *)NULL))) {
				goto found_sym;
			}
		} 
		if (type & ST_TYPEDEF) {
			if ((stp = (st_sym_t *)kl_find_btnode((btnode_t *)
					typedef_tree, name, (int *)NULL))) {
				goto found_sym;
			}
		} 
		if (type & ST_FUNC) {
			if ((stp = (st_sym_t *)kl_find_btnode((btnode_t *)
					func_tree, name, (int *)NULL))) {
				goto found_sym;
			}
		} 
		if (type & ST_VAR) {
			if ((stp = (st_sym_t *)kl_find_btnode((btnode_t *)
					var_tree, name, (int *)NULL))) {
				goto found_sym;
			}
		}
		if (type & ST_XTYPE) {
			if ((stp = (st_sym_t *)kl_find_btnode((btnode_t *)
					xtype_tree, name, (int *)NULL))) {
				goto found_sym;
			}
		}
		if (!stp) {
			return((st_sym_t*)NULL);
		}
	}
found_sym:
	if (typenum) {
		st_hashrec_t *hshp;

		if (stp) {
			if (stp->sym_typenum == typenum) {
				return(stp);
			}
		} else if ((hshp = st_hash[TYPE_NUM_HASH(typenum)])) {
			while (hshp) {
				if (hshp->h_typenum == typenum) {
					return(hshp->h_ptr);
				}
				hshp = hshp->h_next;
			}
		}
		/* If we drop through to here, it means we did not find the
		 * type number in the hash table. This might be because the
		 * type declaration was embedded inside of another type
		 * declaration. Since we didn't suck in all of the types
		 * during initialization, we need to look for it now (based
		 * on the source file number and include file number values
		 * encoded in typenum). 
		 */
		stp = find_embedded_type(typenum);
	}
	return(stp);
}

/*
 * insert_st_sym()
 */
static int
insert_st_sym(st_sym_t *stp)
{
	short type;
	int ret = 0;
	st_sym_t *match_stp;

	if (!stp) {
		return(1);
	}
	/* Check to see if this is a cross reference type. If it is, 
	 * and we find a type already on the type tree, then we can just
	 * link it to the one already there.
	 */
	if ((type = stp->sym_type) == ST_XTYPE) {
		type = ST_TYPE;
	} 
	if ((match_stp = find_st_sym(stp->sym_name, type, 0))) {
		/* This is an instance of the same symbol definition,
		 * from a different location in the symbol table. If 
		 * the symbol includes a type definition, then we
		 * need to create a cross mapping to allow other 
		 * types that might reference this type definition
		 * to use the one already captured. Otherwise, just
		 * drop the duplicate type.
		 */
		if (stp->sym_typenum) {
			hash_stab(stp->sym_typenum, match_stp);
		}
		free_st_sym(stp);
		return(0);
	}
	/* We can't place the sym on one of the sym trees if there is no
	 * type name. If there is a type number, we can put it on the
	 * hash table. Otherwise, we'll just link it on a list (we will 
	 * try and figure out what to do with it later).
	 */
	if (!stp->sym_name) {
		if (stp->sym_typenum) {
			hash_stab(stp->sym_typenum, stp);
		} else {
			if (symlist_end) {
				symlist_end->sym_next = stp;
			} else {
				symlist = stp;
			}
			symlist_end = stp;
		}
		return(0);
	}
	switch (stp->sym_type) {
		case ST_SRCFILE:
			ret = kl_insert_btnode((btnode_t **)&srcfile_tree, 
				(btnode_t *)stp, 0);
			break;
		case ST_TYPE:
			ret = kl_insert_btnode((btnode_t **)&type_tree, 
				(btnode_t *)stp, 0);
			break;
		case ST_TYPEDEF:
			ret = kl_insert_btnode((btnode_t **)&typedef_tree, 
				(btnode_t *)stp, 0);
			break;
		case ST_FUNC:
			ret = kl_insert_btnode((btnode_t **)&func_tree, 
				(btnode_t *)stp, 0);
			break;
		case ST_VAR:
			ret = kl_insert_btnode((btnode_t **)&var_tree, 
				(btnode_t *)stp, 0);
			break;

		case ST_XTYPE:
			ret = kl_insert_btnode((btnode_t **)&xtype_tree,
				(btnode_t *)stp, 0);
			break;
	}
	/* If this is a type and there is a type number, then add this 
	 * record to the hash table (so that we can do quick lookups via 
	 * type number).
	 */
	if (stp->sym_typenum && ((stp->sym_type == ST_TYPE) || 
			 (stp->sym_type == ST_XTYPE) || 
			 (stp->sym_type == ST_TYPEDEF))) {
		hash_stab(stp->sym_typenum, stp);
	}
	return(ret);
}

/*
 * st_find_sym()
 */
st_sym_t *
st_find_sym(char *name) 
{
	st_sym_t *stp;

	if (!(stp = find_st_sym(name, ST_ALL, 0))) {
		return((st_sym_t *)NULL);
	}
	if ((stp->sym_type == ST_TYPE) || (stp->sym_type == ST_TYPEDEF)) {
		(void)__setup_typeinfo(stp);
		(void)__get_typestring((stab_type_t *)stp->sym_kltype);
	}
	return(stp);
}

/*
 * st_find_type()
 */
stab_type_t *
st_find_type(char *name, uint64_t type_num) 
{
	st_sym_t *stp;

	if ((stp = find_st_sym(name, ST_TYPE, type_num))) {
		(void)__setup_typeinfo(stp);
		(void)__get_typestring((stab_type_t *)stp->sym_kltype);
		return((stab_type_t *)stp->sym_kltype);
	}
	return((stab_type_t *)NULL);
}

/*
 * want_sym()
 */
static int
want_sym(unsigned char type, int flags)
{
	switch (type) {
		case N_UNDF:
			if (flags & ST_UNDF) {
				return(1);
			}
			break;
		case N_LSYM:
			if (flags & ST_LSYM) {
				return(1);
			}
			break;
		case N_GSYM:
			if (flags & ST_GSYM) {
				return(1);
			}
			break;
		case N_PSYM:
			if (flags & ST_PSYM) {
				return(1);
			}
			break;
		case N_RSYM:
			if (flags & ST_RSYM) {
				return(1);
			}
			break;
		case N_STSYM:
			if (flags & ST_STSYM) {
				return(1);
			}
			break;
		case N_LCSYM:
			if (flags & ST_LCSYM) {
				return(1);
			}
			break;
		case N_BINCL:
			if (flags & ST_BINCL) {
				return(1);
			}
			break;
		case N_EINCL:
			if (flags & ST_EINCL) {
				return(1);
			}
			break;
		case N_EXCL:
			if (flags & ST_EXCL) {
				return(1);
			}
			break;
		case N_FUN:
			if (flags & ST_FUN) {
				return(1);
			}
			break;
		case N_SLINE:
			if (flags & ST_SLINE) {
				return(1);
			}
			break;
		case N_SO:
			if (flags & ST_SO) {
				return(1);
			}
			break;
	}
	return(0);
}

/***
 *** Functions for accessing STABS data
 ***/

/* Stabs entries use a 12 byte format:
 * 	4 byte string table index
 *	1 byte stab type
 *	1 byte stab other field
 *	2 byte stab desc field
 *	4 byte stab value
 *	FIXME: This will have to change for a 64 bit object format.  
 */
#define STRDXOFF (0)
#define TYPEOFF (4)
#define OTHEROFF (5)
#define DESCOFF (6)
#define VALOFF (8)
#define STABSIZE (12)

/*
#define INCP_DEBUG
*/

/*
 * find_embedded_type()
 */
static st_sym_t *
find_embedded_type(uint64_t typenum)
{
	int incfile, srcfile, incfile_number = 0;
	uint64_t tnum;
	incfile_t *incp;
	st_global_t G;
	st_sym_t *stp;
	char *p;

	tnum = typenum;
	incfile = INC_FILE(tnum);
	srcfile = SRC_FILE(tnum);
	save_global_values(&G);
	kl_set_curnmlist(NMLIST(tnum));	
	if (!(incp = find_incp(srcfile, incfile))) {

		/* If we didn't find the include file record, it most likely
		 * means that the type was declared in another file. The include
		 * file number is for the local EXCL entry for the include file.
		 * We need to figure out which include file it is, get the
		 * stroffset for that entry and then go back to where it was
		 * actually included.
		 */
		if (!(incp = find_incp(srcfile, 0))) {
			return((st_sym_t *)NULL);
		}
	}
        if (set_srcfile(ABFD, incp->srcfile)) {
		return((st_sym_t *)NULL);
	}

	/* Set the global values to reflect the start of the include
	 * file.
	 */
	G_type = 0;
	G_srcfile = incp->srcfile;
	G_incfile = incp->incfile;
	G_symnum = incp->start_num;
	G_stroffset = incp->stroff;
	G_stabp = (bfd_byte *)((uaddr_t)stabs + incp->staboff);
	G_stabs_end = stabs + stab_size;
	G_value = bfd_h_get_32(ABFD, G_stabp + VALOFF);
	G_desc = bfd_h_get_16(ABFD, G_stabp + DESCOFF);
	G_nmlist = curnmlist;
	G_stab_str.str = &strtab[G_stroffset];
	CUR_CHAR = G_stab_str.str;

	if ((incp->incfile == 0) && incfile) {
		G_flags = (ST_BINCL|ST_EXCL);	       
	} else {
		if (G_incfile) { 
			G_incfile--; 	/* Gets bumpped first time through */
		} else {
			G_srcfile--; 	/* Gets bumpped first time through */
		}
		/* We need to look at all symbol entries that could define
		 * new types.
		 */
		G_flags = ST_LSYM|ST_GSYM|ST_PSYM|ST_RSYM|ST_STSYM|ST_LCSYM;
	}

	while (G_symnum < incp->end_num) {
		get_next_str();

		if ((incp->incfile == 0) && incfile) {
			incfile_number++;
			if (incfile_number == incfile) {
				incfile_t *incp2;
				
				incp2 = INCP_LIST;
				while (incp2 != incp) {
					if ((incp2->stroff == G_stroffset) &&
							(incp2->value == G_value)) {
						tnum = (tnum & (~0x0fffffff00000000)) | 
							((uint64_t)incp2->incfile << 32) |
							((uint64_t)incp2->srcfile << 48);
						if ((stp = find_st_sym(0, ST_TYPE, tnum))) {
							restore_global_values(&G);
							return(stp);
						}
					}
					incp2 = incp2->next;
				}
				restore_global_values(&G);
				return((st_sym_t *)NULL);
			}
			continue;
		}

		/* Only look for typenum in the target include file
		 */
		if (G_incfile != incp->incfile) {
			continue;
		}

		p = CUR_CHAR;
		while ((p = strchr(p, '='))) {
			while ((p > CUR_CHAR) && *p != '(') {
				p--;
			}
			if (p < CUR_CHAR) {
				break;
			}
			CUR_CHAR = p;

			if (get_typenum_embedded(tnum)) {
				if (stab_err) {
					break;
				}
				p = CUR_CHAR;
				continue;
			}
			stp = find_st_sym(0, ST_TYPE, tnum);
			restore_global_values(&G);
			return(stp);
		}
	}
	restore_global_values(&G);
	return((st_sym_t *)NULL);
}

/* 
 * set_sym_globals()
 */
void
set_sym_globals(bfd *abfd, st_sym_t *stp)
{
	G_type = stp->sym_stabtype;
	G_flags = stp->sym_flag;
	G_symnum = stp->sym_num;
	G_stabp = (bfd_byte *)((uaddr_t)stabs + stp->sym_staboff);
	G_stabs_end = stabs + stab_size;
	G_value = bfd_h_get_32(abfd, G_stabp + VALOFF);
	G_desc = bfd_h_get_16(abfd, G_stabp + DESCOFF);
	G_srcfile = stp->sym_srcfile;
	G_incfile = stp->sym_incfile;
	G_nmlist = stp->sym_nmlist;
	G_stab_str.str = &strtab[stp->sym_off];
	CUR_CHAR = G_stab_str.str;
}

/*
 * get_next_sym_str()
 */
static char *
get_next_sym_str(bfd *abfd, int flags)
{
	char *str;
	incfile_t *incp;

	if (G_stabp == (bfd_byte *)NULL) {
		G_stabp = stabs;
		G_stabs_end = G_stabp + stab_size;
	}

	/* Loop through all STABS symbol entries. 
	 */
again:
	if (G_stabp < G_stabs_end) {
		unsigned long stroff;
		unsigned char type;
		unsigned char other;
		unsigned short desc;
		bfd_vma value;

		G_symnum++;

		/* Get the symbol information
		 */
		type = bfd_h_get_8(abfd, G_stabp + TYPEOFF);
		stroff = bfd_h_get_32(abfd, G_stabp + STRDXOFF);
		desc = bfd_h_get_16(abfd, G_stabp + DESCOFF);
		other = bfd_h_get_8(abfd, G_stabp + OTHEROFF);
		value = bfd_h_get_32(abfd, G_stabp + VALOFF);

		/* Using the string table offset, locate the start
		 * of the definition string for this symbol.
		 */
		if (stroff < stabstr_size) {
			str = &strtab[stroff];
		} else {
			str = (char *)NULL;
		}

		G_staboff = (UADDR(G_stabp) - UADDR(stabs));

		/* Check to see if the symbol type is N_SO (source
		 * file).  If it is, we need to bump the G_srcfile 
		 * number. This allows us to destinguish between 
		 * the same symbol name being used in more than one 
		 * source module (possibly as a local variable/type 
		 * definition in one file and a global in another). 
		 */
		if (type == N_SO) {
			if (str && str[0]) {
				if (strstr(&str[strlen(str) - 2], ".c")) {
					G_srcfile++;
					if (opening_namelist) {
						incp = alloc_incp();
						incp->incfile = 0;
						incp->srcfile = G_srcfile; 
						incp->stroff = stroff; 
						incp->value = value; 
						incp->staboff = G_staboff;
						incp->start_num = G_symnum; 
						G_incfile = incp->incfile;
						push_incp(incp);
					}
					next_incp = 1;
					G_incfile = 0;
				}
			} else {
				if (opening_namelist) {
					if ((incp = pop_incp())) {
						incp->end_num = G_symnum; 
						queue_incp(incp);
					}
					if (incp_top) {
						G_incfile = incp_top->incfile;
					} else {
						G_incfile = 0;
					}
				} else {
					G_incfile--;
				}
			}
		} else if (type == N_BINCL) {
			/* If we are cycling through the symbol entries 
			 * during a namelist open, we need to capture the 
			 * begin and end points for each include file. In 
			 * all cases, we need to track the current include 
			 * file number.
			 */
			if (opening_namelist) {
				incp = alloc_incp();
				incp->incfile = next_incp++;
				incp->srcfile = G_srcfile; 
				incp->stroff = stroff; 
				incp->value = value; 
				incp->staboff = G_staboff;
				incp->start_num = G_symnum; 
				G_incfile = incp->incfile;
				push_incp(incp);
			} else {
				G_incfile++;
			}
		} else if (type == N_EINCL) {
			if (opening_namelist) {
				if ((incp = pop_incp())) {
					incp->end_num = G_symnum; 
					queue_incp(incp);
				}
				if (incp_top) {
					G_incfile = incp_top->incfile;
				} else {
					G_incfile = 0;
				}
			} else {
				G_incfile--;
			}
		} else if (opening_namelist && (type == N_EXCL)) {
			next_incp++;
		}

		/* See if this is a symbol entry type that we are
		 * interested in.
		 */
		if (!want_sym(type, flags)) {
			G_stabp += STABSIZE;
			goto again;
		}

		G_stroffset = stroff;
		G_value = value;
		G_desc = desc;
		G_type = (int)type;
	} else {
		str = (char *)NULL;
	}

	/* Get ready for the next read
	 */
	G_stabp += STABSIZE;
	return(str);
}

/*
 * set_srcfile()
 */
static int
set_srcfile(bfd *abfd, int srcfile)
{
	if (bfd_check_format(abfd, bfd_archive) == TRUE) {
		int srcnum = 1;
		bfd *arbfd = (bfd *)NULL;

		for (;;) {
			bfd_set_error(bfd_error_no_error);

			arbfd = bfd_openr_next_archived_file(abfd, arbfd);
			if (arbfd == NULL) {
				return(1);
			}
			if (srcnum == srcfile) {
				/* Read in the stab section data.
				 */
				if (!read_stab_data(arbfd)) {
					return(1);
				}
				break;
			}
			srcnum++;
		}
	} else {
		if (!read_stab_data(abfd)) {
			return(1);
		}
	}
	return(0);
}

/* 
 * set_sym_entry()
 */
static int
set_sym_entry(bfd *abfd, st_sym_t *stp)
{
	if (set_srcfile(abfd, stp->sym_srcfile)) {
		return(1);
	}
	set_sym_globals(abfd, stp);
	/* This gets bumpped to the correct value in the get_next_str()
	 * call.
	 */
	G_symnum--;
	get_next_str();
	return(0);
}

/*
 * get_next_stab_entry()
 */
static st_sym_t *
get_next_stab_entry(void)
{
	int symnum, staboff, type, flag = 0;
	st_sym_t *stp = (st_sym_t *)NULL;

again:
	if (get_next_str()) {
		return((st_sym_t *)NULL);
	}
	staboff = G_staboff;
	symnum = G_symnum;
	type = G_type;
	switch(G_type) {
		case N_LSYM:
			if ((flag = (G_flags & ST_LSYM))) {
				stp = get_sym_info();
			}
			break;
		case N_GSYM:
			if ((flag = (G_flags & ST_GSYM))) {
				if ((stp = get_sym_info())) {
					stp->sym_type = ST_VAR;
				}
			}
			break;
		case N_PSYM:
			if ((flag = (G_flags & ST_PSYM))) {
				if ((stp = get_sym_info())) {
					stp->sym_type = ST_PARAM;
				}
			}
			break;
		case N_RSYM:
			if ((flag = (G_flags & ST_RSYM))) {
				if ((stp = get_sym_info())) {
					stp->sym_type = ST_PARAM;
				}
			}
			break;
		case N_SO:
			if ((flag = (G_flags & ST_SO))) {
				if (CUR_CHAR && CUR_CHAR[0]) {
					if (!strstr(&CUR_CHAR[strlen(CUR_CHAR) 
							- 2], ".c")) {
						break;
					}
				} else {
					break;
				}
				if (!(stp = alloc_st_sym())) {
					break;
				}
				stp->sym_name = 
					(char *)xmalloc(strlen(CUR_CHAR) +1);
				strcpy(stp->sym_name, CUR_CHAR);
				stp->sym_type = ST_SRCFILE;
				stp->sym_off = G_stroffset;
			}
			break;
		case N_FUN:
			if ((flag = (G_flags & ST_FUN))) {
				if ((stp = get_sym_info())) {
					stp->sym_type = ST_FUNC;
				}
			}
			break;
		case N_SLINE:
			if ((flag = (G_flags & ST_SLINE))) {
				if ((stp = alloc_st_sym())) {
					stp->sym_type = ST_SLINE;
					stp->sym_off = G_stroffset;
				}
			}
			break;
		default:	
			break;
	}
	if (stp) {
		stp->sym_num = symnum;
		stp->sym_flag = flag;
		stp->sym_stabtype = type;
		stp->sym_staboff = staboff;
		stp->sym_nmlist = curnmlist;
		stp->sym_srcfile = G_srcfile;
		stp->sym_incfile = G_incfile;
	} else if (CUR_CHAR) {
		goto again;
	}
	return(stp);
}

#define CHK_STABERR() \
	if (stab_err) { \
		if (stp) { \
			free(stp); \
		} \
		return((st_sym_t *)NULL); \
	}

/*
 * get_sym_info()
 */
static st_sym_t *
get_sym_info(void)
{
	uint64_t type_num;
	char *ptr, *name, symdesc;

	st_sym_t *stp = (st_sym_t *)NULL;

	ptr = get_name(&name);
	CHK_STABERR();
	ptr = get_symdesc(&symdesc);
	CHK_STABERR();
	if (symdesc == 0) {
		/* This is a parameter, not a type...
		 */
		return((st_sym_t *)NULL);
	}
	ptr = get_typenum(&type_num);
	CHK_STABERR();

	if (!(stp = alloc_st_sym())) {
		return((st_sym_t*)NULL);
	}
	stp->sym_name = name;
	stp->sym_typenum = type_num;
	if (*ptr == '=') {
		ptr = bump_str_ptr(1);
	}
	if (*ptr == '(') {
		uint64_t real_type_num;

		ptr = get_typenum(&real_type_num);
		if (type_num == real_type_num) {
			/* This is a void type
			 */
			stp->sym_type = ST_TYPE;
		} else {
			/* This is a typedef
			 */
			stp->sym_type = ST_TYPEDEF;
		}
	} else {
		stp->sym_type = ST_TYPE;
	}
	stp->sym_off = G_stroffset;

	while (strchr(G_stab_str.ptr, '\\')) {
		if (get_next_str()) {
			free(stp);
			return((st_sym_t *)NULL);
		}
	}
	return(stp);
}


/*
 * get_next_str()
 */
static int
get_next_str(void)
{
	if (!(G_stab_str.str = get_next_sym_str(G_abfd, G_flags))) {
		return(1);
	}
	CUR_CHAR = G_stab_str.str;
	return(0);
}

/*
 * alloc_st_sym()
 */
static st_sym_t *
alloc_st_sym(void)
{
	st_sym_t *stp;

	stp = (st_sym_t *)xmalloc(sizeof(st_sym_t));
	bzero(stp, sizeof(st_sym_t));
	return (stp);
}

/*
 * free_st_sym()
 */
static void
free_st_sym(st_sym_t *stp)
{
	if (!stp) {
		return;
	}
	if (stp->sym_name) {
		free(stp->sym_name);
	}
	free(stp);
}

/*
 * alloc_stab_type()
 */
static stab_type_t *
alloc_stab_type(void)
{
	stab_type_t *stp;

	stp = (stab_type_t *)kl_alloc_block(sizeof(stab_type_t), K_PERM);
	return (stp);
}

/*
 * free_stab_type()
 */
static void
free_stab_type(stab_type_t *stp)
{
	stab_type_t *mp, *mpnext;

	if (!stp) {
		return;
	}
	if ((mp = (stab_type_t *)stp->st_member)) {
		while(mp) {
			mpnext = (stab_type_t *)mp->st_member;
			kl_free_block(mp);
			mp = mpnext;
		}
	}
	kl_free_block(stp);
}

/*
 * bump_str_ptr()
 */
static char *
bump_str_ptr(int count)
{
	if (!CUR_CHAR || !(*CUR_CHAR)) {
		return((char *)NULL);
	}
	if (strlen(CUR_CHAR) < count) {
		/* ERROR? */
		return((char *)NULL);
	} else {
		CUR_CHAR += count;
		if (*CUR_CHAR == '\\') {
			if (get_next_str()) {
				return((char *)NULL);
			}
		}
	}
	return(CUR_CHAR);
}

/*
 * advance_ptr()
 */
static int
advance_ptr(char c)
{
	int i = 0;
	char *ptr;

	stab_err = 0;
	if (!CUR_CHAR) {
		stab_err = 1;
		return(0);
	}
	if (!(ptr = strchr(CUR_CHAR, c))) {
		CUR_CHAR = &CUR_CHAR[strlen(CUR_CHAR)];
		stab_err = 1;
		return(1);
	}
	i = (int)(ptr - CUR_CHAR);
	CUR_CHAR = ptr;
	return(i);
}

/*
 * get_name()
 */
static char *
get_name(char **namep)
{
	int i = 0, j;
	char name[256], *ptr = CUR_CHAR;

	stab_err = 0;
	i = advance_ptr(':');
	if (stab_err) {
		return(ptr);
	}
	strncpy(name, ptr, i);
	name[i] = 0;
	/* Make sure there IS a name...if there is nothing but 
	 * spaces, return a NULL string.
	 */
	for (j = 0; j < strlen(name); j++) {
		if (name[j] != ' ') {
			break;
		}
	}
	if (j == i) {
		*namep = (char *)NULL; 
	} else {
		*namep = (char *)xmalloc(strlen(name) + 1);
		strcpy(*namep, name);
	}
	bump_str_ptr(1);
	return(CUR_CHAR);
}

/*
 * get_symdesc()
 */
static char *
get_symdesc(char *symdesc)
{
	stab_err = 0;
	if (*CUR_CHAR == '(') {
		*symdesc = 0;
	} else {
		*symdesc = *CUR_CHAR;
		bump_str_ptr(1);
	}
	return(CUR_CHAR);
}

/*
 * get_typenum()
 */
static char *
get_typenum(uint64_t *type_num)
{
	int i = 0;
	uint64_t tnum;
	char *p, num[25];
	char *ptr = CUR_CHAR;

	stab_err = 0;
	if (!ptr) {
		stab_err = 1;
		return(ptr);
	}
	/* Get the file number from the stabs entry
	 */
	advance_ptr('(');
	if (stab_err) {
		return(ptr);
	}
	if (!(ptr = bump_str_ptr(1))) {
		stab_err = 1;
		return(ptr);
	}
	i = advance_ptr(',');
	if (stab_err) {
		return(ptr);
	}
	strncpy(num, ptr, i);
	num[i] = 0;
	tnum = ((uint64_t)atoi(num)) << 32;

	if (!(ptr = bump_str_ptr(1))) {
		stab_err = 1;
		return(ptr);
	}

	/* Get the type number from the stabs entry
	 */
	i = advance_ptr(')');
	if (stab_err) {
		return(ptr);
	}
	strncpy(num, ptr, i);
	num[i] = 0;
	tnum |= atoi(num);

	/* Add the current source file number
	 */
	tnum |= ((uint64_t)G_srcfile) << 48;

	/* Add the current namelist number
	 */
	tnum |= ((uint64_t)curnmlist) << 60;

	p = bump_str_ptr(1);

	*type_num = tnum;

	/* 'type_num' should now contain a unique value that will 
	 * allow us to distinguish this type definition from ones 
	 * defined in other namelists having the same file number, type
	 * number and source file number (however unlikely that may be).
	 * This scheme will also allow use to use a single hash table
	 * for types even if multiple namelists are in use.
	 */
	return(p);
}

/*
 * get_embedded_type()
 */
static char *
get_embedded_type(uint64_t type_num)
{
	stab_type_t *sp = ((stab_type_t *)NULL);
	char *p,*ptr;
	st_sym_t *stp;
	
	p = ptr = CUR_CHAR;
	if (!p) {
		stab_err = 1;
		return(ptr);
	}
	if (*p == 'f') {
		/* This is a function
		 */
		sp = alloc_stab_type();
		sp->st_type = KLT_FUNCTION;
		sp->st_type_num = type_num; 
		p = bump_str_ptr(1);
		p = get_typenum_type(&sp->st_real_type, 1);
	} else if (*p == 'x') {
		/* This is a cross-reference type. We need to check
		 * and see if we have sucked in this type during
		 * initialization. If we did, then we need to set up
		 * the cross-reference link here.
		 */
		p = bump_str_ptr(1);
		sp = alloc_stab_type();
		switch (*p) {
			case 's':
				sp->st_type = STAB_XSTRUCT;
				break;
			case 'u':
				sp->st_type = STAB_XUNION;
				break;
			case 'e':
				sp->st_type = STAB_XENUM;
				break;
			default:
				free_stab_type(sp);
				stab_err = 1;
				return(ptr);
		}
		p = bump_str_ptr(1);
		p = get_name(&sp->st_name);
		sp->st_type_num = type_num; 
	} else if ((*p == 's') || (*p == 'u')) {
		sp = alloc_stab_type();
		if (*p == 's') {
			sp->st_type = KLT_STRUCT;
		} else if (*p == 'u') {
			sp->st_type = KLT_UNION;
		}
		p = bump_str_ptr(1);
		sp->st_type_num = type_num; 
		p = get_byte_size(&sp->st_size);
		if (stab_err) {
			free_stab_type(sp);
			return(ptr);
		}
		/* Check to see if this struct/union has
		 * a byte_size equal to zero. If it does,
		 * it's OK...we just have to make sure we
		 * don't try to get the members...
		 */
		if (sp->st_size) {
			p = get_members(sp);
			if (stab_err) {
				free_stab_type(sp);
				return(ptr);
			}
		}
	} else if (*p == 'e') {
		sp = alloc_stab_type();
		sp->st_type = KLT_ENUMERATION;
		p = bump_str_ptr(1);
		sp->st_type_num = type_num;
		p = get_enumlist(sp);
		if (stab_err) {
			free_stab_type(sp);
			return(ptr);
		}
	} else if (*p == 'a') {
		sp = alloc_stab_type();
		sp->st_type = KLT_ARRAY;
		p = get_array(&sp->st_index_type, 
			&sp->st_element_type, 
			&sp->st_low_bounds, 
			&sp->st_high_bounds);
		if (stab_err) {
			free_stab_type(sp);
			return(ptr);
		}
	} else if (*p == '*') {
		sp = alloc_stab_type();
		sp->st_type = KLT_POINTER;
		p = get_typenum_type(&sp->st_real_type, 1);
		if (stab_err) {
			free_stab_type(sp);
			return(ptr);
		}
	} else if (*p == 'r') {
		sp = alloc_stab_type();
		sp->st_type = KLT_BASE;
		ptr = get_range(&sp->st_index_type, 
			&sp->st_low_bounds, &sp->st_high_bounds);
	} else if (*p == '(') {
		/* This is a hack, but there are known cases where there
		 * is no 'void' entry in the stab data. We need to find
		 * the embedded type definition and make sure that it
		 * is named appropriately.
		 */
		sp = alloc_stab_type();
		p = get_typenum_type(&sp->st_real_type, 1);
		if (type_num == sp->st_real_type) {
			/* Treat this as the actual 'void' case.
			 */
			sp->st_type = KLT_BASE;
			sp->st_name = (char *)xmalloc(5);
			strcpy(sp->st_name, "void");
		} else {
			/* XXX - If it's not void, toss it out?
			 */
			fprintf(stderr, "get_embedded_type: "
				"stab type references itself!\n");
			free_stab_type(sp);
			sp = (stab_type_t *)NULL;
			stab_err = 1;
		}
	}
	if (sp) {
		stp = alloc_st_sym();
		if ((sp->st_type == STAB_XSTRUCT) 
				|| (sp->st_type == STAB_XUNION) 
				|| (sp->st_type == STAB_XENUM)) {
			stp->sym_type = ST_XTYPE;
		} else {
			stp->sym_type = ST_TYPE;
		}
		if (sp->st_name) {
			stp->sym_name = (char *)xmalloc(strlen(sp->st_name) +1);
			strcpy(stp->sym_name, sp->st_name);
		}
		stp->sym_typenum = type_num;
		stp->sym_stabtype = G_type;
		stp->sym_flag = G_flag;
		stp->sym_num = G_symnum;
		stp->sym_staboff = G_stabp - stabs;
		stp->sym_srcfile = G_srcfile;
		stp->sym_nmlist = G_nmlist;
		stp->sym_off = G_stroffset;
		stp->sym_kltype = (kltype_t*)sp;
		sp->st_ptr = stp;
		insert_st_sym(stp);
	}
	return(p);
}

/*
 * get_typenum_embedded()
 */
static int
get_typenum_embedded(uint64_t typenum)
{
	char *p;
	uint64_t type_num;

	p = get_typenum(&type_num);
	if (stab_err) {
		return(1);
	}
	if (p && *p && (*p == '=')) {
		/* We have a new type. We need to make sure that the 
		 * type_num we just got matches the one we are looking for.
		 */
		p = bump_str_ptr(1);
		if (type_num == typenum) {
			p = get_embedded_type(typenum);
			if (stab_err) {
				return(1);
			}
			return(0);
		}
		return(1);
	}
	stab_err = 1;
	return(1);
}


/*
 * get_typenum_type()
 */
static char *
get_typenum_type(uint64_t *type_num, int follow)
{
	char *p, *ptr = CUR_CHAR;
	
	p = get_typenum(type_num);
	if (stab_err) {
		return(p);
	}
	if (p && *p && (*p == '=')) {
		/* This is a new type. If the follow flag is set, we 
		 * need to capture the type information and create a 
		 * new stab_type record.
		 */
		p = bump_str_ptr(1);
		if (follow) {
			p = get_embedded_type(*type_num);
			if (!p) {
				stab_err = 1;
				return(ptr);
			}
		}
	}
	return(p);
}

/*
 * get_byte_size()
 */
static char *
get_byte_size(int *byte_size)
{
	int i = 0;
	char *ptr = CUR_CHAR, num[25];

	stab_err = 0;
	while(CUR_CHAR && *CUR_CHAR 
			&& (*CUR_CHAR >= '0') && (*CUR_CHAR <= '9')) {
		bump_str_ptr(1);
		i++;
	}
	if (CUR_CHAR && !(*CUR_CHAR)) {
		stab_err = 1;
		return(ptr);
	}
	strncpy(num, ptr, i);
	num[i] = 0;
	*byte_size = atoi(num);
	return(CUR_CHAR);
}

/*
 * get_range()
 */
static char *
get_range(uint64_t *index_type, int *low_bounds, int *high_bounds) 
{
	int i;
	char *p, num[25];
	char *ptr = CUR_CHAR;

	stab_err = 0;
	p = get_typenum_type(index_type, 1);
	p = bump_str_ptr(1);
	i = advance_ptr(';');
	if (stab_err) {
		return(ptr);
	}
	strncpy(num, p, i);
	num[i] = 0;
	*low_bounds = atoi(num);
	p = bump_str_ptr(1);
	i = advance_ptr(';');
	if (stab_err) {
		return(ptr);
	}
	strncpy(num, p, i);
	num[i] = 0;
	*high_bounds = atoi(num);
	p = bump_str_ptr(1);
	return(p);
}

/*
 * get_array()
 */
static char *
get_array(
	uint64_t *index_type, 
	uint64_t *element_type, 
	int *low_bounds, 
	int *high_bounds)
{
	char *p;
	char *ptr = CUR_CHAR;

	p = bump_str_ptr(1);
	if (*p == 'r') {
		p = get_range(index_type, low_bounds, high_bounds);
		if (stab_err) {
			return(ptr);
		}

	}
	p = get_typenum_type(element_type, 1);
	if (stab_err) {
		return(ptr);
	}
	return(p);
}

/* 
 * get_enum()
 */
static char *
get_enum(stab_type_t **stpp)
{
	int i, value;
	char *ptr = CUR_CHAR, *p, *name, num[25];
	stab_type_t *stp;

	p = get_name(&name);
	if (stab_err) {
		return(ptr);
	}
	i = advance_ptr(',');
	if (stab_err) {
		return(ptr);
	}
	strncpy(num, p, i);
	num[i] = 0;
	value = atoi(num);
	p = bump_str_ptr(1);
	stp = alloc_stab_type();
	if (stab_err) {
		return(ptr);
	}
	stp->st_type = KLT_MEMBER;
	stp->st_name = name;
	stp->st_value = value;
	*stpp = stp;
	return(p);
}

/*
 * get_enumlist()
 */
static char *
get_enumlist(stab_type_t *stp)
{
	stab_type_t *member, *mp = (stab_type_t *)NULL;
	char *ptr = CUR_CHAR, *p = ptr;
	
	while (1) {
		p = get_enum(&member);
		if (stab_err) {
			return(ptr);
		}
		if (mp) {
			mp->st_member = (kltype_t *)member;
			mp = member;
		} else {
			stp->st_member = (kltype_t *)member;
			mp = member;
		}
		if (*p == ';') {
			p = bump_str_ptr(1);
			break;
		}
	}
	return(p);
}

/* 
 * get_member()
 */
static char *
get_member(stab_type_t **spp)
{
	int i;
	int bit_offset, bit_size; 
	uint64_t real_type;
	char *ptr = CUR_CHAR, *name, *p, num[25];
	stab_type_t *sp;

	p = get_name(&name);
	if (stab_err) {
		return(ptr);
	}
	p = get_typenum_type(&real_type, 1);
	p = bump_str_ptr(1);
	i = advance_ptr(',');
	if (stab_err) {
		return(ptr);
	}
	strncpy(num, p, i);
	num[i] = 0;
	bit_offset = atoi(num);
	p = bump_str_ptr(1);
	i = advance_ptr(';');
	strncpy(num, p, i);
	num[i] = 0;
	bit_size = atoi(num);
	p = bump_str_ptr(1);

	sp = alloc_stab_type();
	sp->st_type = KLT_MEMBER;
	sp->st_name = name;
	sp->st_real_type = real_type;
	sp->st_bit_offset = bit_offset;
	sp->st_bit_size = bit_size;
	*spp = sp;
	return(p);
}

/*
 * get_members()
 */
static char *
get_members(stab_type_t *stp)
{
	stab_type_t *member, *mp = (stab_type_t *)NULL;
	char *ptr = CUR_CHAR, *p = ptr;
	
	while (1) {
		p = get_member(&member);
		if (stab_err) {
			return(ptr);
		}
		if (mp) {
			mp->st_member = (kltype_t *)member;
			mp = member;
		} else {
			stp->st_member = (kltype_t *)member;
			mp = member;
		}
		if (*p == ';') {
			p = bump_str_ptr(1);
			break;
		}
	}
	return(p);
}

/*
 * setup_base_type()
 */
static void
setup_base_type(stab_type_t *stp)
{
	if (!stp->st_name) {
		return;
	}
	if (!strcmp(stp->st_name, "int")) {
		stp->st_size = 4;
		stp->st_encoding = ENC_SIGNED;
	} else if (!strcmp(stp->st_name, "char")) {
		stp->st_size = 1;
		stp->st_encoding = ENC_CHAR;
	} else if (!strcmp(stp->st_name, "long int")) {
		stp->st_size = KL_NBPW;
		stp->st_encoding = ENC_SIGNED;
	} else if (!strcmp(stp->st_name, "unsigned int")) {
		stp->st_size = 4;
		stp->st_encoding = ENC_UNSIGNED;
	} else if (!strcmp(stp->st_name, "long unsigned int")) {
		stp->st_size = KL_NBPW;
		stp->st_encoding = ENC_UNSIGNED;
	} else if (!strcmp(stp->st_name, "long long int")) {
		stp->st_size = 8;
		stp->st_encoding = ENC_SIGNED;
	} else if (!strcmp(stp->st_name, "long long unsigned int")) {
		stp->st_size = 8;
		stp->st_encoding = ENC_UNSIGNED;
	} else if (!strcmp(stp->st_name, "short int")) {
		stp->st_size = 2;
		stp->st_encoding = ENC_SIGNED;
	} else if (!strcmp(stp->st_name, "short unsigned int")) {
		stp->st_size = 2;
		stp->st_encoding = ENC_UNSIGNED;
	} else if (!strcmp(stp->st_name, "signed char")) {
		stp->st_size = 1;
		stp->st_encoding = ENC_SIGNED;
	} else if (!strcmp(stp->st_name, "unsigned char")) {
		stp->st_size = 1;
		stp->st_encoding = ENC_UNSIGNED;
	} else if (!strcmp(stp->st_name, "float")) {
		stp->st_size = stp->st_low_bounds;
		stp->st_encoding = ENC_FLOAT;
	} else if (!strcmp(stp->st_name, "double")) {
		stp->st_size = stp->st_low_bounds;
		stp->st_encoding = ENC_FLOAT;
	} else if (!strcmp(stp->st_name, "long double")) {
		stp->st_size = stp->st_low_bounds;
		stp->st_encoding = ENC_FLOAT;
	} else if (!strcmp(stp->st_name, "complex float")) {
		stp->st_size = stp->st_low_bounds;
		stp->st_encoding = ENC_FLOAT;
	} else if (!strcmp(stp->st_name, "complex double")) {
		stp->st_size = stp->st_low_bounds;
		stp->st_encoding = ENC_FLOAT;
	} else if (!strcmp(stp->st_name, "complex long double")) {
		stp->st_size = stp->st_low_bounds;
		stp->st_encoding = ENC_FLOAT;
	} else if (!strcmp(stp->st_name, "void")) {
		stp->st_size = -1;
		stp->st_encoding = ENC_UNDEFINED;
	}
}

#define CHK_STABERR_TYPE() \
	if (stab_err) { \
		if (sp) { \
			free_stab_type(sp); \
		} \
		return(1); \
	}

/*
 * st_get_typeinfo()
 */
static int
st_get_typeinfo(st_sym_t *stp) 
{
	char *ptr, symdesc;
	stab_type_t *sp = (stab_type_t *)NULL;

	/* If we already have type info, just return.
	 */
	if (stp->sym_kltype) {
		return(0);
	}
	/* Check to see if this is a cross-reference type. If it is,
	 * we need to see if we can locate the real type this is a 
	 * cross-reference to.
	 */
	if (stp->sym_type == ST_XTYPE) {
		st_sym_t *xstp;

		fprintf(stdout, "%s: THIS IS AN XTYPE!\n", stp->sym_name);
		if ((xstp = find_st_sym(stp->sym_name, ST_TYPE, 0))) {
			fprintf(stdout, "%s: FOUND REAL TYPE!\n", 
				stp->sym_name);
		}
	}
	kl_set_curnmlist(stp->sym_nmlist);
	if (set_sym_entry(ABFD, stp)) {
		return(1);
	}
	if (!(sp = alloc_stab_type())) {
		return(1);
	}
	ptr = get_name(&sp->st_name);
	CHK_STABERR_TYPE();
	/* It's possible that this type does not have a name...
	 */
	if (sp->st_name || stp->sym_name) {
		if (!sp->st_name || !stp->sym_name) { 
			free_stab_type(sp);
			return(1);
		}
		if (strcmp(sp->st_name, stp->sym_name)) {
			free_stab_type(sp);
			return(1);
		}
	}
	ptr = get_symdesc(&symdesc);
	CHK_STABERR_TYPE();
	ptr = get_typenum_type(&sp->st_type_num, 0);
	if (sp->st_type_num != stp->sym_typenum) {
		free_stab_type(sp);
		return(1);
	}
	CHK_STABERR_TYPE();
	if ((*ptr == 's' || (*ptr == 'u') || (*ptr == 'e'))) {
		switch (*ptr) {
			case 's':
				sp->st_type = KLT_STRUCT;
				break;

			case 'u':
				sp->st_type = KLT_UNION;
				break;

			case 'e':
				sp->st_type = KLT_ENUMERATION;
				break;
		}
		ptr = bump_str_ptr(1);
		ptr = get_byte_size(&sp->st_size);
		CHK_STABERR_TYPE();
	} else if (*ptr == 'r') {
		sp->st_type = KLT_BASE;
		ptr = get_range(&sp->st_index_type, 
				&sp->st_low_bounds, &sp->st_high_bounds);
		CHK_STABERR_TYPE();
	} else if (*ptr == '(') {
		ptr = get_typenum_type(&sp->st_real_type, 1);
		if (sp->st_type_num == sp->st_real_type) {
			/* Make sure we catch the the 'void' case
			 */
			sp->st_type = KLT_BASE;
		} else {
			sp->st_type = KLT_TYPEDEF;
		}
		CHK_STABERR_TYPE();
	} else if (*ptr == '*') {
		sp->st_type = KLT_POINTER;
		ptr++;
		ptr = get_typenum_type(&sp->st_real_type, 1);
	}
	switch (sp->st_type) {
		case KLT_BASE:
			setup_base_type(sp);
			break;
		case KLT_STRUCT:
		case KLT_UNION:
			if (sp->st_size) {
				ptr = get_members(sp);
				CHK_STABERR_TYPE();
			}
			break;
		case KLT_ENUMERATION:
			ptr = get_enumlist(sp);
			CHK_STABERR_TYPE();
			break;
	}
	stp->sym_kltype = (kltype_t *)sp;
	sp->st_ptr = stp;
	return(0);
}

/*
 * set_type_ptrs()
 */
void
set_type_ptrs(stab_type_t *sp)
{
	if (!sp) {
		return;
	}
	if (sp->st_real_type && !sp->st_realtype) {
		if ((sp->st_realtype = (kltype_t *)find_type(sp->st_real_type))) {
			set_type_ptrs((stab_type_t *)sp->st_realtype);
		}
	}
	if (sp->st_index_type && !sp->st_indextype) {
		if ((sp->st_indextype = 
				(kltype_t *)find_type(sp->st_index_type))) {
			set_type_ptrs((stab_type_t *)sp->st_indextype);
		}
	}
	if (sp->st_element_type && !sp->st_elementtype) {
		if ((sp->st_elementtype =
				(kltype_t *)find_type(sp->st_element_type))) {
			set_type_ptrs((stab_type_t *)sp->st_elementtype);
		}
	}
}

/*
 * __get_typeinfo()
 */
static int
__get_typeinfo(stab_type_t *sp)
{
	stab_type_t *mp;
	set_type_ptrs(sp);
	if ((sp->st_type == KLT_STRUCT) || (sp->st_type == KLT_UNION)) {
		mp = (stab_type_t *)sp->st_member;
		while (mp) {
			if (mp->st_offset == 0) {
				mp->st_offset = (mp->st_bit_offset / 8);
			}
			if (mp->st_size == 0) {
				mp->st_size = (mp->st_bit_size / 8);
				if (mp->st_bit_size % 8) {
					mp->st_size++;
				}
				if (((mp->st_bit_offset % 8) +
					(mp->st_bit_size % 8)) > 8) {
					mp->st_size++;
				}
			}
			mp->st_klt.kl_bit_offset = (mp->st_bit_offset % 8);
			set_type_ptrs(mp);
			mp = (stab_type_t *)mp->st_member;
		}
	} 
	return(0);
}

/*
 * find_type()
 */
stab_type_t *
find_type(uint64_t typenum)
{
	st_sym_t *stp;
	stab_type_t *sp = (stab_type_t *)NULL;

	if ((stp = find_st_sym(0, ST_TYPE, typenum))) {
		__setup_typeinfo(stp);
		sp = (stab_type_t *)stp->sym_kltype;
	}
	return(sp);
}

/*
 * strip_traling_blanks()
 */
void
strip_trailing_blanks(char *str)
{
	char *cp;

	if (!str || !(cp = &str[strlen(str) - 1])) {
		return;
	}
	while((cp >= str) && *cp == ' ') {
		*cp-- = 0;
	}
}

/*
 * __get_typestring()
 */
static int 
__get_typestring(stab_type_t *sp)
{
	int ptrcnt = 0;
	stab_type_t *rsp, *itp;
	char typestr[256];
	st_sym_t *stp;

	/* check to see if we are already setting the typestring for
	 * this type. This will stop and endless loop when we process
	 * next, prev, etc. pointers.
	 */
	if (sp->st_flags & TYP_TYPESTRING_FLG) {
		return(0);
	}
	sp->st_flags |= TYP_TYPESTRING_FLG;

	if ((sp->st_type == KLT_STRUCT) || (sp->st_type == KLT_UNION)) {
		stab_type_t *mp;

		/* Capture the typestring now, in case one of the members
		 * needs it.
		 */
		typestr[0] = 0;
		switch (sp->st_type) {
			case KLT_STRUCT:
				strcpy(typestr, "struct");	
				break;
			case KLT_UNION:
				strcpy(typestr, "union");	
				break;
		}
		if (sp->st_name) {
			strcat(typestr, " ");	
			strcat(typestr, sp->st_name);	
			strcat(typestr, " ");	
		} 
		sp->st_typestr = kl_get_string(nmlist[curnmlist].stringtab, 
					typestr, K_PERM);

		/* Now walk through the members
		 */
		mp = (stab_type_t *)sp->st_member;
		while (mp) {
			(void)__get_typestring(mp);
			mp = (stab_type_t *)mp->st_member;
		}
		return(0);
	} 
	
	if (sp->st_type == KLT_MEMBER) {
		if (!(rsp = (stab_type_t *)sp->st_realtype)) {
			rsp = find_type(sp->st_real_type);
		}
		if (!rsp) {
			sprintf(typestr, "<ERROR_1>");
			goto got_typedef;
		}
	} else {
		rsp = sp;
	}
	while (rsp->st_type == KLT_POINTER) {
		ptrcnt++;
		if (rsp->st_realtype) {
			rsp = (stab_type_t *)rsp->st_realtype;
		} else if (rsp->st_real_type) {
			rsp = find_type(rsp->st_real_type);
		} else {
			sprintf(typestr, "<ERROR_2>");
			goto got_typedef;
		}
		if (!rsp) {
			sprintf(typestr, "<ERROR_3>");
			goto got_typedef;
		}
	}
	if ((rsp->st_type == STAB_XSTRUCT) || (rsp->st_type == STAB_XUNION) 
			|| (rsp->st_type == STAB_XENUM)) {

		if ((stp = find_st_sym(rsp->st_name, ST_TYPE|ST_XTYPE, 0))) {
			__setup_typeinfo(stp);
			if (stp->sym_kltype) {
				rsp = (stab_type_t *)stp->sym_kltype;
			}
		}
	}

	typestr[0] = 0;
	switch (rsp->st_type) {

		case STAB_XSTRUCT:
			strcpy(typestr, "struct");	
			break;

		case STAB_XUNION:
			strcpy(typestr, "union");	
			break;

		case KLT_STRUCT:
		case KLT_UNION:
			if (!rsp->st_typestr) {
				__get_typestring(rsp);
			}
			if (rsp->st_typestr) {
				strcpy(typestr, rsp->st_typestr);
				strip_trailing_blanks(typestr);
			} else {
				if (rsp->st_name) {
					sprintf(typestr, "<%s>", rsp->st_name);
				}
			}
			goto got_typedef;

		case KLT_ENUMERATION:
		case STAB_XENUM:
			strcpy(typestr, "enum");
			break;

		case KLT_ARRAY: {
			if ((itp = (stab_type_t *)rsp->st_elementtype)) {
				while (itp->st_type == KLT_POINTER) {
					ptrcnt++;
					if (itp->st_realtype) {
						itp = (stab_type_t *)
							itp->st_realtype;
					} else if (itp->st_real_type) {
						itp = find_type(
							itp->st_real_type);
					}
					if (!itp) {
						sprintf(typestr, "<ARRAY>");
						goto got_typedef;
					}
				}
				if (!itp->st_typestr) {
					__get_typestring(itp);
				}
				if (itp->st_type == KLT_TYPEDEF) {
					if (itp->st_name) {
						strcpy(typestr, itp->st_name);
					} else {
						strcpy(typestr, "<TYPEDEF>");
					}
				} else if (itp->st_typestr) {
					strcpy(typestr, itp->st_typestr);
					strip_trailing_blanks(typestr);
				} else if (itp->st_name) {
					sprintf(typestr, "<%s>", itp->st_name);
				}
				goto got_typedef;
			} else {
				sprintf(typestr, "<ARRAY>");
				goto got_typedef;
			}
			break;
		}

		case KLT_FUNCTION:
			if (!(itp = (stab_type_t *)rsp->st_realtype) 
					&& rsp->st_real_type) {
				itp = find_type(rsp->st_real_type);
			}
			ptrcnt = 0;
			while (itp && (itp->st_type == KLT_POINTER)) {
				ptrcnt++;
				if (itp->st_realtype) {
					itp = (stab_type_t *)itp->st_realtype;
				} else if (itp->st_real_type) {
					itp = find_type(itp->st_real_type);
				}
			}
			if (itp) {
				if (!itp->st_typestr) {
					__get_typestring(itp);
				}
				if (itp->st_type == KLT_TYPEDEF) {
					if (itp->st_name) {
						strcpy(typestr, itp->st_name);
					} else {
						strcpy(typestr, "<TYPEDEF>");
					}
				} else if (itp->st_typestr) {
					strcpy(typestr, itp->st_typestr);
					strip_trailing_blanks(typestr);
				} else if (itp->st_name) {
					strcat(typestr, itp->st_name);
				} else {
					strcat(typestr, "<UNKNOWN>");
				}
				goto got_typedef;
			} 
			strcpy(typestr, "<FUNCTION>");
			goto got_typedef;

		case KLT_TYPEDEF:
			if (sp->st_type == KLT_MEMBER) {
				strcpy(typestr, rsp->st_name);
			} else {
				itp = (stab_type_t *)sp->st_realtype;
				if (!itp && sp->st_real_type) {
					itp = find_type(sp->st_real_type);
				}
				if (itp) {
					if (!itp->st_typestr) {
						__get_typestring(itp);
					}
					if (itp->st_typestr) {
						strcpy(typestr, 
							itp->st_typestr);
						strip_trailing_blanks(typestr);
						goto got_typedef;
					} else if (itp->st_name) {
						strcpy(typestr, itp->st_name);
						goto got_typedef;
					}
				}
				strcat(typestr, "<TYPEDEF>");
				goto got_typedef;
			}
			goto got_typedef;

		case KLT_BASE:
			strcpy(typestr, rsp->st_name);
			goto got_typedef;

		default: 
			sprintf(typestr, "<type=%d>", rsp->st_type);
			break;
	}
	if (rsp->st_name) {
		strcat(typestr, " ");
		strcat(typestr, rsp->st_name);	
	}
got_typedef:
	if (ptrcnt) {
		strcat(typestr, " ");
		while(ptrcnt--) {
			strcat(typestr, "*");
		}
	} 
	if (!strchr(typestr, '*')) {
		strcat(typestr, " ");
	}
	sp->st_typestr = 
		kl_get_string(nmlist[curnmlist].stringtab, typestr, K_PERM);

	/* Now that we have the typestring for this type, check to see
	 * if it is a struct or union. If it is, then we need to make 
	 * a recursive call to __get_typestring() so that the typestrings
	 * for the struct/union members get filled in properly.
	 */
	if ((rsp->st_type == KLT_STRUCT) || (rsp->st_type == KLT_UNION)) {
		(void)__get_typestring(rsp);
	}
	return(0);
}

/*
 * __setup_typeinfo()
 */
static int
__setup_typeinfo(st_sym_t *stp)
{
	st_global_t G;

	/* If we've already set this type up or are in the process of
	 * doing so, then just return without doing anyting (and with
	 * no error).
	 */
	if (stp->sym_state == ST_SETUP) {
		return(0);
	}
	stp->sym_state = ST_SETUP;

	save_global_values(&G);
	if (st_get_typeinfo(stp)) {
		stp->sym_state = ST_SETUP_FAILED;
		restore_global_values(&G);
		return(1);
	}
	if (__get_typeinfo((stab_type_t *)stp->sym_kltype)) {
		stp->sym_state = ST_SETUP_FAILED;
		restore_global_values(&G);
		return(1);
	}
	restore_global_values(&G);
	stp->sym_state = ST_SETUP_DONE;
	return(0);
}

/**
 ** STAB type lookup functions (which can be called directly or through
 ** the platform independent type lookup functions). 
 **/

/*
 * st_first_sym()
 */
st_sym_t *
st_first_sym(int type) 
{
	st_sym_t *stp = (st_sym_t *)NULL;

	switch(type) {
		case ST_SRCFILE:
			stp = (st_sym_t *)
				kl_first_btnode((btnode_t *)srcfile_tree);
			break;
		case ST_TYPE:
			stp = (st_sym_t *)
				kl_first_btnode((btnode_t *)type_tree);
			break;
		case ST_TYPEDEF:
			stp = (st_sym_t *)
				kl_first_btnode((btnode_t *)typedef_tree);
			break;
		case ST_FUNC:
			stp = (st_sym_t *)
				kl_first_btnode((btnode_t *)func_tree);
			break;
		case ST_VAR:
			stp = (st_sym_t *)
				kl_first_btnode((btnode_t *)var_tree);
			break;
	}
	return(stp);
}

/*
 * st_next_sym()
 */
st_sym_t *
st_next_sym(st_sym_t *stp)
{
	st_sym_t *next_stp;

	if ((next_stp = (st_sym_t *)kl_next_btnode((btnode_t *)stp))) {
		(void)__setup_typeinfo(next_stp);
	}
	return(next_stp);
}

/*
 * st_get_member()
 */
stab_type_t *
st_get_member(char *s, char *f)
{
	stab_type_t *sp, *rsp, *mp;
	st_sym_t *stp;

	if (!(stp = st_find_sym(s))) {
		return((stab_type_t *)NULL);	
	}
	if (!(sp = (stab_type_t *)stp->sym_kltype)) {
		return((stab_type_t *)NULL);	
	}
	if (sp->st_type == KLT_TYPEDEF) {
		if (!(rsp = (stab_type_t *)sp->st_realtype)) {
			return((stab_type_t *)NULL);
		}
	} else {
		rsp = sp;
	}
	if ((rsp->st_type != KLT_STRUCT) && (rsp->st_type != KLT_UNION)) {
		return((stab_type_t *)NULL);
	}
	mp = (stab_type_t *)rsp->st_member;
	while(mp) {
		if (!strcmp(mp->st_name, f)) {
			return(mp);
		}
		mp = (stab_type_t *)mp->st_member;
	}
	return((stab_type_t *)NULL);
}

/*
 * st_first_type()
 */
stab_type_t *
st_first_type(void)
{
	st_sym_t *stp;

	if ((stp = st_first_sym(ST_TYPE))) {
		(void)__setup_typeinfo(stp);
		(void)__get_typestring((stab_type_t *)stp->sym_kltype);
		return((stab_type_t *)stp->sym_kltype);
	}
	return((stab_type_t *)NULL);
}

/*
 * st_next_type()
 */
stab_type_t *
st_next_type(stab_type_t *sp)
{
	st_sym_t *stp, *nstp;

	if ((stp = sp->st_ptr)) {
		if ((nstp = st_next_sym(stp))) {
			(void)__setup_typeinfo(nstp);
			(void)__get_typestring((stab_type_t *)nstp->sym_kltype);
			return((stab_type_t *)nstp->sym_kltype);
		}
	}
	return((stab_type_t *)NULL);
}

/*
 * st_struct_len()
 */
int
st_struct_len(char *s)
{               
	st_sym_t *stp;

	if ((stp = find_st_sym(s, ST_TYPE|ST_TYPEDEF, 0))) {
		if (!stp->sym_kltype) {
			(void)__setup_typeinfo(stp);
			(void)__get_typestring((stab_type_t *)stp->sym_kltype);
		}
		if (stp->sym_kltype) {
			if(stp->sym_kltype->kl_type ==  KLT_TYPEDEF){
				kltype_t *rkltp;
				rkltp = stp->sym_kltype->kl_realtype;
				while (rkltp && (rkltp->kl_realtype)
				       && (rkltp != rkltp->kl_realtype)) {
					rkltp = rkltp->kl_realtype;
				}
				return(rkltp->kl_size);
			}
			return(stp->sym_kltype->kl_size);
		}
	}
	return(0);
}       

/*
 * st_member_offset()
 */
int
st_member_offset(char *s, char *f)
{
	stab_type_t *mp;

	if ((mp = st_get_member(s, f))) {
		return(mp->st_offset);
	}
	KL_ERROR = 1;
	return(-1);
}

/*
 * st_is_member() -- Return 1 if 'f' is field of 's', else 0.
 */
int
st_is_member(char *s, char *f)
{
	stab_type_t *mp;

	if ((mp = st_get_member(s, f))) {
		return(1);
	}
	return(0);
}

/*
 * st_member_size()
 */
int
st_member_size(char *s, char *f)
{
	stab_type_t *mp;

	if ((mp = st_get_member(s, f))) {
		return(mp->st_size);
	}
	return(0);
}

/*
 * __open_namelist()
 */
int
__open_namelist(char *namelist, int flags)
{
	return(st_open_namelist(namelist, flags));
}

/*
 * __find_sym()
 */
kltype_t *
__find_sym(char *name, int type)
{
	st_sym_t *stp = (st_sym_t *)NULL;
	kltype_t *kltp = (kltype_t *)NULL;

	if (type & KLT_TYPE) {
		stp = find_st_sym(name, ST_TYPE, 0);
	} 
	if (!stp && (type & KLT_TYPEDEF)) {
		stp = find_st_sym(name, ST_TYPEDEF, 0);
	} 
	if (!stp && (type & KLT_FUNCTION)) {
		stp = find_st_sym(name, ST_FUNC, 0);
	} 
	if (!stp && (type & KLT_VARIABLE)) {
		stp = find_st_sym(name, ST_VAR, 0);
	} 
	if (!stp && (type & KLT_SRCFILE)) {
		stp = find_st_sym(name, ST_SRCFILE, 0);
	} 
	if (!stp && (type & KLT_UNKNOWN)) {
		stp = find_st_sym(name, ST_XTYPE, 0);
	}
	if (stp) {
		(void)__setup_typeinfo(stp);
		(void)__get_typestring((stab_type_t *)stp->sym_kltype);
		kltp = stp->sym_kltype;
	}
	return(kltp);
}

/*
 * __first_sym()
 */
kltype_t *
__first_sym(int type) 
{
	st_sym_t *stp = (st_sym_t *)NULL;
	kltype_t *kltp = (kltype_t *)NULL;

	if (type & KLT_TYPE) {
		stp = st_first_sym(ST_TYPE);
	} else if (type & KLT_TYPEDEF) {
		stp = st_first_sym(ST_TYPEDEF);
	} else if (type & KLT_FUNCTION) {
		stp = st_first_sym(ST_FUNC);
	} else if (type & KLT_VARIABLE) {
		stp = st_first_sym(ST_VAR);
	} else if (type & KLT_SRCFILE) {
		stp = st_first_sym(ST_SRCFILE);
	} else if (type & KLT_UNKNOWN) {
		stp = st_first_sym(ST_XTYPE);
	}
	if (stp) {
		(void)__setup_typeinfo(stp);
		(void)__get_typestring((stab_type_t *)stp->sym_kltype);
		kltp = stp->sym_kltype;
	}
	return(kltp);
}

/*
 * __next_sym()
 */
kltype_t *
__next_sym(kltype_t *kltp)
{
	st_sym_t *stp;
	kltype_t *nkltp = (kltype_t *)NULL;

	if ((stp = st_next_sym((st_sym_t *)kltp->kl_ptr))) {
		(void)__setup_typeinfo(stp);
		(void)__get_typestring((stab_type_t *)stp->sym_kltype);
		nkltp = stp->sym_kltype;
	}
	return(nkltp);
}

/*
 * __find_type()
 */
kltype_t *
__find_type(char *name)
{
	kltype_t *kltp;

	kltp = (kltype_t *)st_find_type(name, 0);
	return(kltp);
}

/*
 * __first_type()
 */
kltype_t *
__first_type(void) 
{
	kltype_t *kltp;

	kltp = (kltype_t*)st_first_type();
	return(kltp);
}

/*
 * __next_type()
 */
kltype_t *
__next_type(kltype_t *kltp)
{
	kltype_t *nstp;

	nstp = (kltype_t*)st_next_type((stab_type_t *)kltp);
	return(nstp);
}

/*
 * __struct_len()
 */
int
__struct_len(char *s)
{               
	return(st_struct_len(s));
}       

/*
 * __get_member()
 */
kltype_t *
__get_member(char *s, char *f)
{
	kltype_t *mp;
	
	mp = (kltype_t *)st_get_member(s, f);
	return(mp);
}

/*      
 * __member_offset()
 */      
int     
__member_offset(char *s, char *f)
{       
	return(st_member_offset(s, f));
}

/*
 * __is_member() 
 */
int
__is_member(char *s, char *f)
{
	return(st_is_member(s, f));
}

/*
 * __member_size()
 */
int
__member_size(char *s, char *f)
{
	return(st_member_size(s, f));
}


/*
 * setup_typeinfo()
 */
int
st_setup_typeinfo(void)
{
	st_sym_t *fsym = NULL, *nsym = NULL;
	if( (fsym = st_first_sym(ST_TYPE)) ){
		(void)__setup_typeinfo(fsym);
		(void)__get_typestring((stab_type_t *)fsym->sym_kltype);
		nsym = st_next_sym(fsym);
		while(nsym) {
			(void)__get_typestring((stab_type_t *)nsym->sym_kltype);
			nsym = st_next_sym(nsym);
		}
	}
	if( (fsym = st_first_sym(ST_TYPEDEF)) ){
		(void)__setup_typeinfo(fsym);
		(void)__get_typestring((stab_type_t *)fsym->sym_kltype);
		nsym = st_next_sym(fsym);
		while(nsym) {
			(void)__get_typestring((stab_type_t *)nsym->sym_kltype);
			nsym = st_next_sym(nsym);
		}
	}
	return(0);
}
