/*
 * nasd_rpcgen_tab.c
 *
 * Maintain name tables for nasd_rpcgen
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>

#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "nasd_rpcgen.h"

nasd_rpcgen_namehash_t *global_type_hash;
nasd_rpcgen_namehash_t *global_const_hash;
nasd_rpcgen_namehash_t *global_struct_hash;
nasd_rpcgen_namehash_t *import_seen_hash;
nasd_rpcgen_namehash_t *uniq_seen_hash;
nasd_rpcgen_namehash_t *global_call_hash;
nasd_rpcgen_namehash_t *global_marshall_hash;

nasd_rpcgen_type_t *pad_type;

/*
 * Toplevel definitions (added when import level is 0)
 */
nasd_rpcgen_global_val_t  global_vals;
nasd_rpcgen_const_t       global_consts;
nasd_rpcgen_type_t        global_types;
nasd_rpcgen_call_t        global_calls;
nasd_rpcgen_import_t      global_imports;
nasd_rpcgen_decl_t        global_decls;
nasd_rpcgen_marshall_t    global_marshalls;

int
strhash(
  char  *key)
{
  int h, n, l, m;

  l = strlen(key);
  m = NASD_MAX(l-4,0);

  h = 0;
  for(n=l-1;n>=m;n--) {
    h <<= 8;
    h |= ((int)key[n])&0xff;
  }

  h %= NASD_RPCGEN_NAMEHASH_BUCKETS;
  return(h);
}

void
strclone(
  char   *src,
  char  **dest)
{
  char *d;
  int l;

  l = strlen(src);
  d = (char *)malloc(l+1);
  if (d == NULL) {
    fprintf(stderr, "ERROR: cannot allocate %d bytes for clone string\n",
      l+1);
    fflush(stderr);
    exit(1);
  }
  bcopy(src, d, l);
  d[l] = '\0';
  *dest = d;
}

void
init_tables()
{
  global_type_hash = nasd_rpcgen_namehash_get();
  global_const_hash = nasd_rpcgen_namehash_get();
  global_struct_hash = nasd_rpcgen_namehash_get();
  import_seen_hash = nasd_rpcgen_namehash_get();
  uniq_seen_hash = nasd_rpcgen_namehash_get();
  global_call_hash = nasd_rpcgen_namehash_get();
  global_marshall_hash = nasd_rpcgen_namehash_get();

  bzero((char *)&global_vals, sizeof(global_vals));
  bzero((char *)&global_consts, sizeof(global_consts));
  bzero((char *)&global_types, sizeof(global_types));
  bzero((char *)&global_imports, sizeof(global_imports));
  bzero((char *)&global_calls, sizeof(global_calls));
  bzero((char *)&global_decls, sizeof(global_decls));
  bzero((char *)&global_marshalls, sizeof(global_marshalls));

  global_consts.gnext = global_consts.gprev = &global_consts;
  global_types.gnext = global_types.gprev = &global_types;
  global_imports.gnext = global_imports.gprev = &global_imports;
  global_calls.gnext = global_calls.gprev = &global_calls;
  global_decls.gnext = global_decls.gprev = &global_decls;
  global_marshalls.gnext = global_marshalls.gprev = &global_marshalls;
}

nasd_rpcgen_type_t *
init_basic_type(
  char  *name,
  int    type_is,
  int    size,
  int    min_alignment,
  char  *dceidl_name,
  char  *c_name,
  char  *file,
  int    line)
{
  nasd_rpcgen_type_t *type;

  type = nasd_rpcgen_type_get();

  strclone(name, &type->name);
  strclone(dceidl_name, &type->dceidl_name);
  strclone(c_name, &type->c_name);
  type->type_is = type_is;
  type->ndim = 0;
  type->derived_from = NULL;
  type->struct_next = NULL;
  type->struct_list = NULL;
  type->size = size;
  type->min_align = min_alignment;
  type->import_level = (-1);
  type->decl.define_file = file;
  type->decl.define_line = line;

  add_global_type(type, NULL);

  return(type);
}

void
init_basic_types()
{
  pad_type = init_basic_type(
    "int8",
    NASD_RPCGEN_TYPE_INT8,
    1, 1,
    "nasd_dceidl_int8",
    "nasd_rpcgen_int8",
    __FILE__,__LINE__);

  init_basic_type("uint8",
    NASD_RPCGEN_TYPE_UINT8,
    1, 1,
    "nasd_dceidl_uint8",
    "nasd_rpcgen_uint8",
    __FILE__,__LINE__);

  init_basic_type(
    "int16",
    NASD_RPCGEN_TYPE_INT16,
    2, 2,
    "nasd_dceidl_int16",
    "nasd_rpcgen_int16",
    __FILE__,__LINE__);

  init_basic_type("uint16",
    NASD_RPCGEN_TYPE_UINT16,
    2, 2,
    "nasd_dceidl_uint16",
    "nasd_rpcgen_uint16",
    __FILE__,__LINE__);

  init_basic_type(
    "int32",
    NASD_RPCGEN_TYPE_INT32,
    4, 4,
    "nasd_dceidl_int32",
    "nasd_rpcgen_int32",
    __FILE__,__LINE__);

  init_basic_type(
    "uint32",
    NASD_RPCGEN_TYPE_UINT32,
    4, 4,
    "nasd_dceidl_uint32",
    "nasd_rpcgen_uint32",
    __FILE__,__LINE__);

  init_basic_type(
    "int64",
    NASD_RPCGEN_TYPE_INT64,
    8, 8,
    "nasd_dceidl_int64",
    "nasd_rpcgen_int64",
    __FILE__,__LINE__);

  init_basic_type(
    "uint64",
    NASD_RPCGEN_TYPE_UINT64,
    8, 8,
    "nasd_dceidl_uint64",
    "nasd_rpcgen_uint64",
    __FILE__,__LINE__);

  init_basic_type(
    "int",
    NASD_RPCGEN_TYPE_INT32,
    4, 4,
    "nasd_dceidl_int32",
    "int",
    __FILE__,__LINE__);

  init_basic_type("char",
    NASD_RPCGEN_TYPE_INT8,
    1, 1,
    "char",
    "char",
    __FILE__,__LINE__);

  init_basic_type(
    "float",
    NASD_RPCGEN_TYPE_FLOAT,
    sizeof(float), sizeof(float),
    "float",
    "float",
    __FILE__,__LINE__);

  init_basic_type(
    "double",
    NASD_RPCGEN_TYPE_DOUBLE,
    sizeof(double), sizeof(double),
    "double",
    "double",
    __FILE__,__LINE__);

  init_basic_type(
    "string",
    NASD_RPCGEN_TYPE_STRING,
    0, 8,
    "string",
    "char *",
    __FILE__,__LINE__);

  init_basic_type(
    "pipe",
    NASD_RPCGEN_TYPE_PIPE,
    0, 8,
    "pipe",
    "",
    __FILE__,__LINE__);
}

int
nasd_rpcgen_namehash_add(
  nasd_rpcgen_namehash_t  *nh,
  char                    *key,
  void                    *rock,
  int                      flags,
  void                    *in_dup_rockp)
{
  nasd_rpcgen_namehash_ent_t *e;
  void **dup_rockp;
  int i, h, l;

  dup_rockp = (void **)in_dup_rockp;

  l = strlen(key);
  NASD_ASSERT(l != 0);

  /* check for illegal chars */

  if ((flags&NASD_RPCGEN_NAMEHASH_NODIGIT) && isdigit(key[0])) {
    return(NASD_RPCGEN_BAD_KEY);
  }

  if (!(flags&NASD_RPCGEN_NAMEHASH_PUNCTOK)) {
    if (key[0] < '0')
      return(NASD_RPCGEN_BAD_KEY);

    if (key[0] > 'z')
      return(NASD_RPCGEN_BAD_KEY);

    if ((key[0] > 'Z') && (key[0] < 'a') && (key[0] != '_'))
      return(NASD_RPCGEN_BAD_KEY);

    if ((key[0] > '9') && (key[0] < 'A'))
      return(NASD_RPCGEN_BAD_KEY);

    for(i=0;i<l;i++) {
      if (((key[i] < 'A') && ((key[i] < '0') || (key[i] > '9')))
        || ((key[i] > 'Z') && (key[i] < 'a') && (key[i] != '_'))
        || (key[i] > 'z'))
      {
        return(NASD_RPCGEN_BAD_KEY);
      }
    }
  }

  if (dup_rockp)
    *dup_rockp = NULL;

  h = strhash(key);
  for(e=nh->buckets[h];e;e=e->next) {
    if (e->len == l) {
      if (!strcmp(e->key, key)) {
        if (flags & NASD_RPCGEN_NAMEHASH_OVERRIDE) {
          if (dup_rockp) {
            *dup_rockp = e->rock;
          }
          e->rock = rock;
          return(NASD_RPCGEN_OK);
        }
        else {
          if (dup_rockp) {
            *dup_rockp = e->rock;
          }
          return(NASD_RPCGEN_DUP_ENT);
        }
      }
    }
  }

  e = nasd_rpcgen_namehash_ent_get();
  if (flags & NASD_RPCGEN_NAMEHASH_CLONEKEY) {
   strclone(key, &e->key);
  }
  else {
    e->key = key;
  }
  e->len = strlen(e->key);
  e->rock = rock;
  e->next = nh->buckets[h];
  nh->buckets[h] = e;

  return(NASD_RPCGEN_OK);
}

void
nasd_rpcgen_namehash_lookup(
  nasd_rpcgen_namehash_t  *nh,
  char                    *key,
  void                    *rockp)
{
  nasd_rpcgen_namehash_ent_t *e;
  void **rockp_real;
  int h, l;

  rockp_real = (void **)rockp;
  *rockp_real = NULL;
  l = strlen(key);

  h = strhash(key);

  for(e=nh->buckets[h];e;e=e->next) {
    if (e->len == l) {
      if (!strcmp(e->key, key)) {
        *rockp_real = e->rock;
        return;
      }
    }
  }
}

nasd_rpcgen_cpparg_t *
nasd_rpcgen_cpparg_get()
{
  nasd_rpcgen_cpparg_t *cppa;

  cppa = (nasd_rpcgen_cpparg_t *)malloc(sizeof(nasd_rpcgen_cpparg_t));
  if (cppa == NULL) {
    fprintf(stderr, "ERROR: cannot allocate cpparg\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)cppa, sizeof(nasd_rpcgen_cpparg_t));

  return(cppa);
}

nasd_rpcgen_namehash_t *
nasd_rpcgen_namehash_get()
{
  nasd_rpcgen_namehash_t *nh;

  nh = (nasd_rpcgen_namehash_t *)malloc(sizeof(nasd_rpcgen_namehash_t));
  if (nh == NULL) {
    fprintf(stderr, "ERROR: cannot allocate namehash\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)nh, sizeof(nasd_rpcgen_namehash_t));

  return(nh);
}

nasd_rpcgen_namehash_ent_t *
nasd_rpcgen_namehash_ent_get()
{
  nasd_rpcgen_namehash_ent_t *ne;

  ne = (nasd_rpcgen_namehash_ent_t *)malloc(sizeof(nasd_rpcgen_namehash_ent_t));
  if (ne == NULL) {
    fprintf(stderr, "ERROR: cannot allocate namehash ent\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)ne, sizeof(nasd_rpcgen_namehash_ent_t));

  return(ne);
}

nasd_rpcgen_decl_t *
nasd_rpcgen_decl_get()
{
  nasd_rpcgen_decl_t *decl;

  decl = (nasd_rpcgen_decl_t *)malloc(sizeof(nasd_rpcgen_decl_t));
  if (decl == NULL) {
    fprintf(stderr, "ERROR: cannot allocate decl holder\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)decl, sizeof(nasd_rpcgen_decl_t));

  return(decl);
}

nasd_rpcgen_type_t *
nasd_rpcgen_type_get()
{
  nasd_rpcgen_type_t *type;

  type = (nasd_rpcgen_type_t *)malloc(sizeof(nasd_rpcgen_type_t));
  if (type == NULL) {
    fprintf(stderr, "ERROR: cannot allocate type holder\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)type, sizeof(nasd_rpcgen_type_t));
  type->decl.decl_type = NASD_RPCGEN_DECL_TYPE;
  type->decl.decl = type;

  return(type);
}

nasd_rpcgen_const_t *
nasd_rpcgen_const_get()
{
  nasd_rpcgen_const_t *cnst;

  cnst = (nasd_rpcgen_const_t *)malloc(sizeof(nasd_rpcgen_const_t));
  if (cnst == NULL) {
    fprintf(stderr, "ERROR: cannot allocate const holder\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)cnst, sizeof(nasd_rpcgen_const_t));
  cnst->decl.decl_type = NASD_RPCGEN_DECL_CONST;
  cnst->decl.decl = cnst;

  return(cnst);
}

nasd_rpcgen_import_t *
nasd_rpcgen_import_get()
{
  nasd_rpcgen_import_t *import;

  import = (nasd_rpcgen_import_t *)malloc(sizeof(nasd_rpcgen_import_t));
  if (import == NULL) {
    fprintf(stderr, "ERROR: cannot allocate import holder\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)import, sizeof(nasd_rpcgen_import_t));
  import->decl.decl_type = NASD_RPCGEN_DECL_IMPORT;
  import->decl.decl = import;

  return(import);
}

nasd_rpcgen_marshall_t *
nasd_rpcgen_marshall_get()
{
  nasd_rpcgen_marshall_t *marshall;

  marshall = (nasd_rpcgen_marshall_t *)malloc(sizeof(nasd_rpcgen_marshall_t));
  if (marshall == NULL) {
    fprintf(stderr, "ERROR: cannot allocate marshall holder\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)marshall, sizeof(nasd_rpcgen_marshall_t));
  marshall->decl.decl_type = NASD_RPCGEN_DECL_MARSHALL;
  marshall->decl.decl = marshall;

  return(marshall);
}

nasd_rpcgen_filename_t *
nasd_rpcgen_filename_get()
{
  nasd_rpcgen_filename_t *filename;

  filename = (nasd_rpcgen_filename_t *)malloc(sizeof(nasd_rpcgen_filename_t));
  if (filename == NULL) {
    fprintf(stderr, "ERROR: cannot allocate filename holder\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)filename, sizeof(nasd_rpcgen_filename_t));

  return(filename);
}

nasd_rpcgen_call_t *
nasd_rpcgen_call_get()
{
  nasd_rpcgen_call_t *call;

  call = (nasd_rpcgen_call_t *)malloc(sizeof(nasd_rpcgen_call_t));
  if (call == NULL) {
    fprintf(stderr, "ERROR: cannot allocate call holder\n");
    fflush(stderr);
    exit(1);
  }

  bzero((char *)call, sizeof(nasd_rpcgen_call_t));
  call->decl.decl_type = NASD_RPCGEN_DECL_CALL;
  call->decl.decl = call;

  return(call);
}

nasd_rpcgen_type_t *
lookup_type(
  char  *name)
{
  nasd_rpcgen_type_t *res;

  nasd_rpcgen_namehash_lookup(global_type_hash, name, &res);
  return(res);
}

nasd_rpcgen_type_t *
lookup_struct(
  char  *name)
{
  nasd_rpcgen_type_t *res;

  nasd_rpcgen_namehash_lookup(global_struct_hash, name, &res);
  return(res);
}

nasd_rpcgen_const_t *
lookup_const(
  char  *name)
{
  nasd_rpcgen_const_t *res;

  nasd_rpcgen_namehash_lookup(global_const_hash, name, &res);
  return(res);
}

nasd_rpcgen_call_t *
lookup_call(
  char  *name)
{
  nasd_rpcgen_call_t *res;

  nasd_rpcgen_namehash_lookup(global_call_hash, name, &res);
  return(res);
}

int
add_global_type(
  nasd_rpcgen_type_t   *type,
  nasd_rpcgen_type_t  **dupp)
{
  nasd_rpcgen_type_t *st;
  int ret, a;

  /* fix up alignment if necessary */
  a = NASD_MAX(type->min_align, global_spec.min_alignment);
  type->min_align = a;

  if (type->dceidl_name == NULL)
    type->dceidl_name = type->name;

  if (type->c_name == NULL)
    type->c_name = type->name;

  if (type->type_is == NASD_RPCGEN_TYPE_STRUCT) {
    ret = nasd_rpcgen_namehash_add(global_struct_hash, type->name,
      type, NASD_RPCGEN_NAMEHASH_NODIGIT, dupp);
    NASD_ASSERT(type->decl.decl_type == NASD_RPCGEN_DECL_STRUCT);
  }
  else {
    ret = nasd_rpcgen_namehash_add(global_type_hash, type->name,
      type, NASD_RPCGEN_NAMEHASH_NODIGIT, dupp);
    NASD_ASSERT(type->decl.decl_type == NASD_RPCGEN_DECL_TYPE);

    if ((type->type_is == NASD_RPCGEN_TYPE_DERIVED) && 
      (type->derived_from->type_is == NASD_RPCGEN_TYPE_STRUCT))
    {
      st = type->derived_from;
      if ((st->first_typedef == NULL)
        && (st->import_level == type->import_level)
        && (!strcmp(st->decl.define_file, type->decl.define_file)))
      {
        st->first_typedef = type;
      }
    }
  }

  if ((ret == NASD_RPCGEN_OK) && (type->import_level == 0)) {
    type->gnext = &global_types;
    type->gprev = global_types.gprev;
    type->gprev->gnext = type;
    type->gnext->gprev = type;
    add_global_decl(&type->decl);
  }

  return(ret);
}

int
add_global_const(
  nasd_rpcgen_const_t   *cnst,
  nasd_rpcgen_const_t  **dupp)
{
  int ret;

  ret = nasd_rpcgen_namehash_add(global_const_hash, cnst->name,
    cnst, NASD_RPCGEN_NAMEHASH_NODIGIT, dupp);

  if ((ret == NASD_RPCGEN_OK) && (cnst->import_level == 0)) {
    cnst->gnext = &global_consts;
    cnst->gprev = global_consts.gprev;
    cnst->gprev->gnext = cnst;
    cnst->gnext->gprev = cnst;
    add_global_decl(&cnst->decl);
  }

  return(ret);
}

int
add_global_call(
  nasd_rpcgen_call_t   *call,
  nasd_rpcgen_call_t  **dupp)
{
  int ret;

  ret = nasd_rpcgen_namehash_add(global_call_hash, call->name,
    call, NASD_RPCGEN_NAMEHASH_NODIGIT, dupp);

  if ((ret == NASD_RPCGEN_OK) && (call->import_level == 0)) {
    call->gnext = &global_calls;
    call->gprev = global_calls.gprev;
    call->gprev->gnext = call;
    call->gnext->gprev = call;
    add_global_decl(&call->decl);
  }

  return(ret);
}

int
add_global_marshall(
  nasd_rpcgen_marshall_t   *marshall,
  nasd_rpcgen_marshall_t  **dupp)
{
  int ret;

  ret = nasd_rpcgen_namehash_add(global_marshall_hash, marshall->type->name,
    marshall, NASD_RPCGEN_NAMEHASH_NODIGIT, dupp);

  if ((ret == NASD_RPCGEN_OK) && (marshall->import_level == 0)) {
    marshall->gnext = &global_marshalls;
    marshall->gprev = global_marshalls.gprev;
    marshall->gprev->gnext = marshall;
    marshall->gnext->gprev = marshall;
    add_global_decl(&marshall->decl);
  }

  return(ret);
}

void
add_global_decl(
  nasd_rpcgen_decl_t  *decl)
{
  decl->gnext = &global_decls;
  decl->gprev = global_decls.gprev;
  decl->gprev->gnext = decl;
  decl->gnext->gprev = decl;
}

nasd_rpcgen_type_t *
basic_type_of(
  nasd_rpcgen_type_t  *type)
{
  nasd_rpcgen_type_t *t;

  t = type;
  while(t) {
    switch(t->type_is) {
      case NASD_RPCGEN_TYPE_INT8:
      case NASD_RPCGEN_TYPE_UINT8:
      case NASD_RPCGEN_TYPE_INT16:
      case NASD_RPCGEN_TYPE_UINT16:
      case NASD_RPCGEN_TYPE_INT32:
      case NASD_RPCGEN_TYPE_UINT32:
      case NASD_RPCGEN_TYPE_INT64:
      case NASD_RPCGEN_TYPE_UINT64:
      case NASD_RPCGEN_TYPE_STRING:
      case NASD_RPCGEN_TYPE_FLOAT:
      case NASD_RPCGEN_TYPE_DOUBLE:
        return(t);
      case NASD_RPCGEN_TYPE_ARRAY:
      case NASD_RPCGEN_TYPE_STRUCT:
      case NASD_RPCGEN_TYPE_PIPE:
        return(NULL);
      case NASD_RPCGEN_TYPE_DERIVED:
        t = t->derived_from;
        break;
      default:
        NASD_PANIC();
    }
  }

  return(t);
}

nasd_rpcgen_type_t *
basic_type_of_extended(
  nasd_rpcgen_type_t  *type)
{
  nasd_rpcgen_type_t *t;

  t = type;
  while(t) {
    switch(t->type_is) {
      case NASD_RPCGEN_TYPE_INT8:
      case NASD_RPCGEN_TYPE_UINT8:
      case NASD_RPCGEN_TYPE_INT16:
      case NASD_RPCGEN_TYPE_UINT16:
      case NASD_RPCGEN_TYPE_INT32:
      case NASD_RPCGEN_TYPE_UINT32:
      case NASD_RPCGEN_TYPE_INT64:
      case NASD_RPCGEN_TYPE_UINT64:
      case NASD_RPCGEN_TYPE_STRING:
      case NASD_RPCGEN_TYPE_FLOAT:
      case NASD_RPCGEN_TYPE_DOUBLE:
      case NASD_RPCGEN_TYPE_STRUCT:
      case NASD_RPCGEN_TYPE_ARRAY:
      case NASD_RPCGEN_TYPE_PIPE:
        return(t);
      case NASD_RPCGEN_TYPE_DERIVED:
        t = t->derived_from;
        break;
      default:
        NASD_PANIC();
    }
  }

  return(t);
}

void
reset_output_bits()
{
  nasd_rpcgen_namehash_ent_t *e;
  nasd_rpcgen_type_t *t;
  int h;

  for(h=0;h<NASD_RPCGEN_NAMEHASH_BUCKETS;h++) {
    for(e=global_type_hash->buckets[h];e;e=e->next) {
      t = e->rock;
      t->out_flags = 0;
    }
  }
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
