/* This file contains the routines for handling the symbol table from
 * an a.out file.
 */

#include <a.out.h>
#include <stab.h>

#ifndef	DDTCAT	/* Don't reget hdrs if catting when all routines internal */
#include	<pdefs.h>
#include	<ptypes.h>
#include	"mdep.h"
#include	"ddt.h"
#endif

#include "symt-unix.h"

struct nlist *symbol_table;
struct nlist *end_syms;

retcd d_initsym(fd)
int fd;				/* File descriptor */
{
    struct exec header;
    struct nlist *sym;
    struct psym *cpsym;
    int size;
    char *strings;

    if (read(fd, &header, sizeof(header)) == -1) return ERR;
    if ((symbol_table = (struct nlist *)malloc(header.a_syms + PSYMTSIZ)) == 0)
      return ERR;
    end_syms = (struct nlist *)((char *)symbol_table + header.a_syms+PSYMTSIZ);

    /* Find symbol table in file and read it into symbol_table */
    if (lseek(fd, N_SYMOFF(header), 0) == -1) return ERR;
    if (read(fd, symbol_table, header.a_syms) == -1) return ERR;

    /* Install symbols from the permanent symbol table */
    sym = (struct nlist *)((char *)symbol_table + header.a_syms);
    cpsym = psymt;
    while (cpsym < psymt + sizeof(psymt)/sizeof(struct psym)) {
	sym->n_un.n_name = cpsym->name;
	sym->n_type = N_COMM + N_EXT;
	sym->n_value = cpsym->value;
	sym++;
	cpsym++;
    }

    /* Read in the string table */
    if (read(fd, &size, 4) == -1) return ERR;
    if ((strings = (char *)malloc(size)) == 0) return ERR;
    if (read(fd, strings, size - 4) == -1) return ERR;

    /* Set symbol name pointers */
    for (sym = symbol_table;
	 sym != (struct nlist *)((char *)symbol_table + header.a_syms);
	 sym++) {
	     if (sym->n_un.n_strx == 0) continue;
	     sym->n_un.n_name = strings + sym->n_un.n_strx - 4;
    }
    return OK;
}

#define SSIZ 64

/* Reads a symbol name off the input stream, allowing only legal
 * characters, and attempts to find it in the symbol table.  Note hack
 * to try looking up symbol first just as typed and then with an '_'
 * in front of the name.
 */
intern	d_rdsym(dsp)
xreg	ddtst	*dsp;

{
	reg	struct nlist	*sym;
	reg	int	p;
		unsw	val;
		char	c;
		char	buf[SSIZ];

	p = 0;
	buf[p++] = '_';
	for (;;) {
		c = d_getc(dsp);
		if (c == '') {
			if (p <= 1) {
				d_ungetc(dsp, '');
				return;
				}
			(*dsp->d_puts)(" ");
			p--;
			continue;
			}

		if ( ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))
		    || ((c >= '0') && (c <= '9'))
		    || (c == '$') || (c == '.') || (c == '_')) {
			if (p < SSIZ) {
				d_echoc(dsp, c);
				buf[p++] = c;
			}
			continue;
		}
		if (c == ':') {	/* Then is a file name */
		    d_echoc(dsp, c);
		    buf[p] = '\0';
		    md_rdsrcsym(dsp, buf);
		    return;
		}
		d_ungetc(dsp, c);
		break;
	}
	
	buf[p] = '\0';
	
	/* First look for name as typed */
	for (sym = symbol_table; sym != end_syms; sym++)
	  if (strcmp(sym->n_un.n_name, &buf[1]) == 0) {
	      val = sym->n_value;
	      d_setval(dsp, val);
	      return;
	  }

	/* If name not found, try it with an '_' in front */
	for (sym = symbol_table; sym != end_syms; sym++)
	  if (strcmp(sym->n_un.n_name, buf) == 0) {
	      val = sym->n_value;
	      d_setval(dsp, val);
	      return;
	  }

	d_err(dsp);
}

/* Given a value, print it in symbolic (name+offset) form.  Note that
 * the offset is printed in the input base; this is because it is assumed
 * that this form is more useful.
 */

intern
d_prsym(dsp, loc)
xreg	ddtst	*dsp;
reg	unsw	loc;

{
    reg  struct nlist	*sym;
    xreg struct nlist	*svsym;
    reg	unsw	mindif;
    
    svsym = NULL;
    sym = symbol_table;
    for (mindif = dsp->d_symoff; sym != end_syms; sym++) {
	if ((!(sym->n_type & N_EXT)) ||
	    (sym->n_type & N_STAB) ||
	    (sym->n_value > loc) ||
	    ((loc - sym->n_value) >= mindif))
	  continue;
	mindif = (loc - sym->n_value);
	svsym = sym;
    }

    if (svsym == NULL) {
	d_lprrdx(dsp, ((unsl) loc), dsp->d_ibase);
	return;
    }
    
    (*dsp->d_puts)(svsym->n_un.n_name);
    if (mindif) {
	(*dsp->d_putc)('+');
	d_lprrdx(dsp, ((unsl) mindif), dsp->d_ibase);
    }
}

/* Puts the value of the symbol given by name into dst. */
md_get_sym(name, dst)
reg char *name;
unsw *dst;
{	
    reg struct nlist *sym;

    for (sym = symbol_table; sym != end_syms; sym++)
      if (strcmp(sym->n_un.n_name, name) == 0) {
	  *dst = sym->n_value;
	  return;
      }
}

/* Is character legal in symbol? Returns non-zero if true, zero
 * if false.
 */

intern	retcd	d_issymch(c)
reg	char	c;

{
	if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
	     (c == '$') || (c == '_') || (c == '\''))
		return(c);
	 else
		return(ERR);
}


/* Do we have a symbol table? Returns true if so.
 */

intern	retcd	d_isst()

{
    return OK;
}

intern md_rdsrcsym(dsp, file)
ddtst *dsp;
char *file;
{
    reg struct nlist *sym;
    unsw line = 0;
    char c;

    for (sym = symbol_table; sym != end_syms; sym++) {
	if (sym->n_type != N_SO) continue;
	if (strcmp(sym->n_un.n_name, file) == 0) {
	    break;
	}
    }
    if (sym == end_syms) {
	d_err(dsp);
	return;
    }

    for (;;) {
	c = d_getc(dsp);
	if (c == '') {
	    if (line <= 0) {
		d_ungetc(dsp, '');
		return;
	    }
	    (*dsp->d_puts)(" ");
	    line /= 10;
	    continue;
	}

	if ((c >= '0') && (c <= '9')) {
	    line = line * 10 + (c - '0');
	    d_echoc(dsp, c);
	    continue;
	}
	d_ungetc(dsp, c);
	break;
    }
    for (; sym != end_syms; sym++) {
	if (sym->n_desc == line) {
	    d_setval(dsp, sym->n_value);
	    return;
	}
    }
    d_err(dsp);
}

intern md_pr_src_loc(dsp, loc)
ddtst *dsp;
unsw loc;
{
    struct nlist *sym, *osym;
    char *file = "<Unknown>";

    printf("%x\t", loc);
    if (dsp->d_size != sizeof(word))
      return(ERR);
    if (d_fetch(dsp, A_DATA, sizeof(word), loc, &loc) == ERR)
      return(ERR);
    printf("%x\r\n", loc);

    for (osym = sym = symbol_table; sym != end_syms; sym++) {
	if (sym->n_type == N_SO) {
	    file = sym->n_un.n_name;
	    continue;
	}
	if (sym->n_type != N_SLINE) continue;
	printf("%x\r\n", sym->n_value);
	if (sym->n_value >= loc) {
	    if (sym->n_value == loc) osym = sym;
	    dsp->d_puts(file);
	    dsp->d_putc(':');
	    d_lprrdx(dsp, osym->n_desc, 10);
	    dsp->d_isize = sizeof(word);
	    return(OK);
	}
	osym = sym;
    }
    return(ERR);
}
