/*
 *  smoke16/libObj/src/Sym.c - Functions for Syms.
 *  $Revision: 1.14 $
 *  Copyright 1997, 2000, Benjamin Sittler
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* #define DEBUG_OBJ */
/* #define DEBUG_SYMS */
/* #define DEBUG_RELOCS */
/* #define DEBUG_SEARCH */

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <smoke16/libObj.h>

int defSym(Sym *sym, u8 type, s8 other, s16 desc, u16 value, int refcnt) {
  sym -> refcnt += refcnt;
  sym -> type |= type & SMOKE16_N_EXT;
  if ((type & SMOKE16_N_TYPE) == SMOKE16__N_COMM) {
    if (((sym -> type & SMOKE16_N_TYPE) == SMOKE16_N_UNDF) ||
	((sym -> type & SMOKE16_N_TYPE) == SMOKE16__N_COMM)) {
      if (value > sym -> value)
	sym -> value = value;
      sym -> type =
	(sym -> type & SMOKE16_N_EXT)
	| type;
      sym -> other = other;
      sym -> desc = desc;
    }
  } else if (type & ~SMOKE16_N_EXT) {
    if (((type & SMOKE16_N_TYPE) != SMOKE16_N_UNDF) &&
	((sym -> type & SMOKE16_N_TYPE) != SMOKE16_N_UNDF) &&
	((sym -> type & SMOKE16_N_TYPE) != SMOKE16__N_COMM)) {
      fprintf(stderr,
	      "symbol '%s' redefined from %" FMT_u8 "+%" FMT_u16 " to %" FMT_u8 "+%" FMT_u16 "\n",
	      sym -> string ? sym -> string : "(nil)",
	      sym -> type, sym -> value,
	      type, value);
      return 1;
    }
    sym -> type =
      (sym -> type & SMOKE16_N_EXT)
      | type;
    sym -> other = other;
    sym -> desc = desc;
    sym -> value = value;
  }
    
#ifdef DEBUG_SYMS
  if (sym -> string)
    fprintf(stderr, "defined sym '%s'\n", sym -> string);
#endif
  return 0;
}

Sym *newSym(char *string, u8 type, s8 other, s16 desc, u16 value) {
  Sym *sym;

  if ((sym = (Sym *)malloc(sizeof(Sym)))) {
    sym -> next = 0;
    sym -> obj = 0;
    sym -> refcnt = 0;
    sym -> type = 0;
    sym -> string = 0;
    sym -> type = SMOKE16_N_UNDF;
    sym -> value = 0;
    sym -> other = 0;
    sym -> desc = 0;
    if (defSym(sym, type, other, desc, value, 0)) {
      free((void *)sym);
      return 0;
    }
    if ((sym -> string = strdup(string))) {
#ifdef DEBUG_SYMS
      fprintf(stderr, "created sym '%s'\n", string);
#endif
      return sym;
    } else
      perror("strdup");
    free((void *)sym);
  } else
    perror("malloc");
#ifdef DEBUG_SYMS
  fprintf(stderr, "failed to create sym '%s'\n", string);
#endif
  return 0;
}

Sym *cpSym(Sym *sym) {
  Sym *dest = newSym(sym -> string, sym -> type, sym -> other, sym -> desc, sym -> value);

  dest -> refcnt = sym -> refcnt;
  return dest;
}

void freeSym(Sym *sym) {
#ifdef DEBUG_SYMS
  fprintf(stderr, "deleting sym '%s'\n", sym -> string);
#endif
  free((void *)sym -> string);
  free((void *)sym);
}
