/* $Header$
   Read the symbol table from an ACK a.out format file.
*/

#include <stb.h>
#include <alloc.h>
#include <assert.h>

#include "position.h"
#include "file.h"
#include "symbol.h"
#include "idf.h"
#include "scope.h"
#include "rd.h"

extern char		*strindex();
extern struct outname	*DbxString();

int			saw_code = 0;

static char		*AckStrings;	/* ACK a.out string table */
static struct outname	*AckNames;	/* ACK a.out symbol table entries */
static unsigned int	NAckNames;	/* Number of ACK symbol table entries */
static struct outname	*EndAckNames;	/* &AckNames[NAckNames] */

/* Read the symbol table from file 'f', which is supposed to be an
   ACK a.out format file. Offer DBX strings to the DBX string parser.
*/
int
DbxRead(f)
  char	*f;
{
  struct outhead h;
  register struct outname *n;
  register struct outname *line_file = 0;
  long OffsetStrings;
  int had_lbrac = 0;

  /* Open file, read header, and check magic word */
  if (! rd_open(f)) {
  	fatal("%s: could not open", f);
  }
  rd_ohead(&h);
  if (BADMAGIC(h) && h.oh_magic != O_CONVERTED) {
  	fatal("%s: not an object file", f);
  }

  /* Allocate space for name table and read it */
  AckNames = (struct outname *) 
  		Malloc((unsigned)(sizeof(struct outname) * h.oh_nname));
  AckStrings = Malloc((unsigned) h.oh_nchar);
  rd_name(AckNames, h.oh_nname);
  rd_string(AckStrings, h.oh_nchar);

  /* Adjust file offsets in name table to point at strings */
  OffsetStrings = OFF_CHAR(h);
  NAckNames = h.oh_nname;
  EndAckNames = &AckNames[h.oh_nname];
  for (n = EndAckNames; --n >= AckNames;) {
	if (n->on_foff) {
		if ((unsigned)(n->on_foff - OffsetStrings) >= h.oh_nchar) {
			fatal("%s: error in object file", f);
		}
		n->on_mptr = AckStrings + (n->on_foff - OffsetStrings);
	}
	else	n->on_mptr = 0;
  }

  /* Offer strings to the DBX string parser if they contain a ':'.
     Also offer filename-line number information to add_position_addr().
     Here, the order may be important.
  */
  for (n = &AckNames[0]; n < EndAckNames; n++) {
	int tp = n->on_type >> 8;
	register p_symbol sym;

	if (tp & (S_STB >> 8)) {
		switch(tp) {
#ifdef N_BINCL
		case N_BINCL:
			n->on_valu = (long) line_file;
			line_file = n;
			break;
		case N_EINCL:
			if (line_file) {
				line_file = (struct outname *) line_file->on_valu;
			}
			break;
#endif
		case N_SO:
			if (n->on_mptr[strlen(n->on_mptr)-1] == '/') {
				/* another N_SO follows ... */
				break;
			}
			while (CurrentScope != PervasiveScope) {
				close_scope();
			}
			saw_code = 0;
			sym = add_file(n->on_mptr);

			if (! listfile) newfile(sym->sy_idf);
			open_scope(sym, 0);
			sym->sy_file->f_scope = CurrentScope;
			FileScope = CurrentScope;
			clean_tp_tab();
			/* fall through */
		case N_SOL:
			if (! line_file) line_file = n;
			else line_file->on_mptr = n->on_mptr;
			break;
		case N_MAIN:
			newfile(FileScope->sc_definedby->sy_idf);
			break;
		case N_SLINE:
			assert(line_file);
			if (! saw_code && !CurrentScope->sc_bp_opp) {
			    CurrentScope->sc_bp_opp = n->on_valu;
			    if (! CurrentScope->sc_start) {
				CurrentScope->sc_start = n->on_valu;
				if (CurrentScope->sc_has_activation_record) {
					add_scope_addr(CurrentScope);
				}
			    }
			}
			saw_code = 1;
			add_position_addr(line_file->on_mptr, n);
			break;
		case N_LBRAC:	/* block, desc = nesting level */
			if (had_lbrac) {
				open_scope((p_symbol) 0, 0);
				saw_code = 0;
			}
			else {
				register p_scope sc = 
					get_scope_from_addr(n->on_valu);

				if (!sc || sc->sc_bp_opp) {
					had_lbrac = 1;
				}
				else CurrentScope = sc;
			}
			break;
#ifdef N_SCOPE
		case N_SCOPE:
			if (n->on_mptr && strindex(n->on_mptr, ':')) {
				n = DbxString(n);
			}
			break;
#endif
		case N_RBRAC:	/* end block, desc = nesting level */
			had_lbrac = 0;
			if (CurrentScope != FileScope) close_scope();
			saw_code = 0;
			break;
		case N_FUN:	/* function, value = address */
		case N_GSYM:	/* global variable */
		case N_STSYM:	/* data, static, value = address */
		case N_LCSYM:	/* bss, static, value = address */
		case N_RSYM:	/* register var, value = reg number */
		case N_SSYM:	/* struct/union el, value = offset */
		case N_PSYM:	/* parameter, value = offset from AP */
		case N_LSYM:	/* local sym, value = offset from FP */
			if (had_lbrac) {
				open_scope((p_symbol) 0, 0);
				saw_code = 0;
				had_lbrac = 0;
			}
			if (n->on_mptr && strindex(n->on_mptr, ':')) {
				n = DbxString(n);
			}
			break;
		default:
/*
			if (n->on_mptr && (n->on_type&S_TYP) >= S_MIN) {
				struct idf *id = str2idf(n->on_mptr, 0);

				sym = new_symbol();
				sym->sy_next = id->id_def;
				id->id_def = sym;
				sym->sy_class = SYMENTRY;
				sym->sy_onam = *n;
				sym->sy_idf = id;
			}
*/
			break;
		}
	}
  }
  close_scope();
  add_position_addr((char *) 0, (struct outname *) 0);
  clean_tp_tab();
  rd_close();
  return (h.oh_magic == O_CONVERTED);
}