/*
 * nasd_rpcgen_parse.c
 *
 * I should learn lex/yacc and replace this.
 *
 * 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"

int nparse_errors = 0;

int import_level = 0;

FILE *pserr;

#define LINE_MAX_WORDS 512

char **
lex_line(
  char                   *s,
  int                    *count,
  int                     flags,
  nasd_rpcgen_fhandle_t  *fhandle)
{
  static char *ret[LINE_MAX_WORDS];
  int x, bracelev;
  char *r;

  r = s;
  x = 0;
  bracelev = 0;

  while(x<LINE_MAX_WORDS) {
    /* skip whitespace */
    while((*r) && isspace(*r))
      r++;

    /* end of input string */
    if ((*r) == 0) {
      if (bracelev) {
        NASD_ASSERT(flags&NASD_RPCGEN_LEX_BRACE_EXPR);
        fprintf(pserr, "%s:%d  mismatched braces\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
        *count = 0;
        return(NULL);
      }
      (*count) = x;
      return(ret);
    }

    ret[x] = r;
    x++;

    while((*r) && ((!isspace(*r)) || bracelev)) {
      if ((flags&NASD_RPCGEN_LEX_BRACE_EXPR) && (*r == '[')) {
        bracelev++;
      }
      if ((flags&NASD_RPCGEN_LEX_BRACE_EXPR) && (*r == ']')) {
        bracelev--;
      }
      r++;
    }
    if (*r) {
      (*r) = 0;
      r++;
    }
  }
  (*count) = x;
  return(ret);
}

void
parse_error()
{
  fflush(pserr);
  nparse_errors++;
  if (nparse_errors >= global_spec.max_parse_errors) {
    fprintf(stderr, "\nFATAL ERROR: too many parse errors (%d)\n",
      nparse_errors);
    fflush(stderr);
    exit(1);
  }
}

int
global_val_str(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count,
  nasd_rpcgen_val_t       *strval,
  char                    *valname,
  int                      decl_type)
{
  int i, l, j;
  char *str;

  if (count < 2) {
    fprintf(pserr, "%s:%d  missing argument\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return(NASD_RPCGEN_BAD_EXPR);
  }

  if (words[1][0] == '\"') {
    l = strlen(fhandle->line_copy);
    NASD_ASSERT(l > 0);
    if (fhandle->line_copy[l-1] == '\n') {
      fhandle->line_copy[l-1] = '\0';
      l--;
    }
    str = NULL;
    for(i=0;i<l;i++) {
      if (fhandle->line_copy[i] == '\"') {
        str = &fhandle->line_copy[i];
        break;
      }
    }
    NASD_ASSERT(str && (str[0] == '\"'));
    i++;
    if (i >= l) {
      fprintf(pserr, "%s:%d  improper string\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return(NASD_RPCGEN_BAD_EXPR);
    }
    str = &str[1];
    l = strlen(str);
    j = 0;
    for(i=1;((i<l)&&(j==0));i++) {
      if (str[i] == '\"') {
        j = i;
        break;
       }
    }
    if (j == 0) {
      fprintf(pserr, "%s:%d  unterminated string\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return(NASD_RPCGEN_BAD_EXPR);
    }
    if (j < (l-1)) {
      fprintf(pserr, "%s:%d  extra characters after string\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return(NASD_RPCGEN_BAD_EXPR);
    }
    str[j] = '\0';
  }
  else {
    str = words[1];
  }

  if (strval->str) {
    if (global_spec.allow_keyword_dup)
      return(NASD_RPCGEN_OK);
    fprintf(pserr, "%s:%d  duplicate specification of %s,"
      " previous at %s:%d\n",
      fhandle->cur_filename, fhandle->cur_line, valname,
      strval->decl.define_file, strval->decl.define_line);
    parse_error();
    return(NASD_RPCGEN_DUP_ENT);
  }

  strclone(str, &strval->str);
  strclone(fhandle->cur_filename, &strval->decl.define_file);
  strval->decl.define_line = fhandle->cur_line;
  strval->decl.decl_type = decl_type;
  strval->decl.decl = strval;
  add_global_decl(&strval->decl);

  return(NASD_RPCGEN_OK);
}

int
global_val_u32(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count,
  nasd_rpcgen_val_t       *val,
  char                    *valname,
  int                      decl_type)
{
  if (count < 2) {
    fprintf(pserr, "%s:%d  missing argument\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return(NASD_RPCGEN_BAD_EXPR);
  }

  if (count > 2) {
    fprintf(pserr, "%s:%d  too many arguments\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return(NASD_RPCGEN_BAD_EXPR);
  }

  if (sscanf(words[1], "%u", &val->u32) != 1) {
    fprintf(pserr, "%s:%d  not a valid 32-bit unsigned value\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return(NASD_RPCGEN_BAD_EXPR);
  }

  if (val->decl.define_file) {
    if (global_spec.allow_keyword_dup)
      return(NASD_RPCGEN_OK);
    fprintf(pserr, "%s:%d  duplicate specification of %s,"
      " previous at %s:%d\n",
      fhandle->cur_filename, fhandle->cur_line, valname,
      val->decl.define_file, val->decl.define_line);
    parse_error();
    return(NASD_RPCGEN_DUP_ENT);
  }

  strclone(fhandle->cur_filename, &val->decl.define_file);
  val->decl.define_line = fhandle->cur_line;
  val->decl.decl_type = decl_type;
  val->decl.decl = val;
  add_global_decl(&val->decl);

  return(NASD_RPCGEN_OK);
}

void
global_uuid(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  int ret;

  ret = global_val_str(fhandle, words, count, &global_vals.uuid, "uuid",
    NASD_RPCGEN_DECL_UUID);
  if (ret) {
    fprintf(pserr, "%s:%d  bad uuid specification\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }
}

void
global_dce_endpoint(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  int ret;

  ret = global_val_str(fhandle, words, count, &global_vals.dce_endpoint,
    "DCE endpoint", NASD_RPCGEN_DECL_DCE_ENDPOINT);
  if (ret) {
    fprintf(pserr, "%s:%d  bad DCE endpoint specification\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }
}

void
global_version(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  int ret;

  ret = global_val_str(fhandle, words, count, &global_vals.version,
    "version", NASD_RPCGEN_DECL_VERSION);
  if (ret) {
    fprintf(pserr, "%s:%d  bad version specification\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }
}

void
global_baseid(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  int ret;

  ret = global_val_u32(fhandle, words, count, &global_vals.baseid,
    "base id", NASD_RPCGEN_DECL_BASEID);
  if (ret) {
    fprintf(pserr, "%s:%d  bad base id specification\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }
}

void
global_marshall(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  nasd_rpcgen_marshall_t *marshall, *dup;
  nasd_rpcgen_type_t *type, *bt;
  char *typename;
  int ret;

  if (count != 2) {
    fprintf(pserr, "%s:%d  unexpected number of tokens\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  typename = words[1];

  type = lookup_type(typename);
  if (type == NULL) {
    fprintf(pserr, "%s:%d  unrecognized type \"%s\"\n",
      fhandle->cur_filename, fhandle->cur_line,
      typename);
    parse_error();
    return;
  }

  bt = basic_type_of_extended(type);
  if (bt->type_is == NASD_RPCGEN_TYPE_PIPE) {
    fprintf(pserr, "%s:%d  attempt to marshall pipe\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  marshall = nasd_rpcgen_marshall_get();

  marshall->type = type;
  strclone(fhandle->cur_filename, &marshall->decl.define_file);
  marshall->decl.define_line = fhandle->cur_line;
  marshall->import_level = import_level;

  ret = add_global_marshall(marshall, &dup);

  switch(ret) {
    case NASD_RPCGEN_OK:
      return;
    case NASD_RPCGEN_DUP_ENT:
      if (dup == NULL) {
        NASD_PANIC();
      }
      fprintf(pserr, "%s:%d  multiple marshall directives for %s"
        " previous at %s:%d\n",
        fhandle->cur_filename, fhandle->cur_line,
        marshall->type->name, dup->decl.define_file, dup->decl.define_line);
      parse_error();
      return;
    case NASD_RPCGEN_BAD_KEY:
    case NASD_RPCGEN_BAD_EXPR:
      fprintf(pserr, "%s:%d  invalid name for marshall %s\n",
        fhandle->cur_filename, fhandle->cur_line,
        marshall->type->name);
      parse_error();
      return;
    default:
      fprintf(pserr, "%s:%d  error %d marshalling %s\n",
        fhandle->cur_filename, fhandle->cur_line,
        ret, marshall->type->name);
  }
}

void
global_const(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  int seen_eq, i, j, l, ret, vnw, cw, ci, nb, ab, term, bk, val_oneword;
  char *typename, *valstr, *constname, *valfuncstr, *tmp, *newstr;
  nasd_rpcgen_type_t *type, *base_type;
  nasd_rpcgen_const_t *cnst, *dup;
  nasd_rpcgen_uint64 valu64;
  nasd_rpcgen_int64 val64;

  if (count < 3) {
    fprintf(pserr, "%s:%d not enough tokens\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  typename = words[1];
  constname = words[2];
  seen_eq = 0;
  l = strlen(words[2]);
  valstr = NULL;
  valfuncstr = NULL;
  val_oneword = 0;
  vnw = (-1);
  ci = (-1);
  newstr = NULL;

  for(i=0;i<l;i++) {
    if (words[2][i] == '=') {
      words[2][i] = '\0';
      seen_eq = 1;
      i++;
      if (i < l) {
        valstr = &words[2][i];
        vnw = 3;
        val_oneword = 1;
      }
      break;
    }
  }

  if (seen_eq && valstr && (count > 3)) {
    fprintf(pserr, "%s:%d  syntax error (too many tokens)\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  if (valstr == NULL) {
    if (seen_eq == 0) {
      if (count < 4) {
        fprintf(pserr, "%s:%d  syntax error (too few tokens)\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
        return;
      }
      if (words[3][0] != '=') {
        fprintf(pserr, "%s:%d  syntax error\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
        return;
      }
      seen_eq = 1;
      if (words[3][1]) {
        words[3] = &words[3][1];
        valstr = &words[3][1];
        vnw = 4;
      }
    }
    if (valstr == NULL) {
      if (count < 5) {
        fprintf(pserr, "%s:%d  syntax error (too few tokens)\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
        return;
      }
      valstr = words[4];
      vnw = 5;
    }
  }

  type = lookup_type(typename);
  if (type == NULL) {
    fprintf(pserr, "%s:%d  unknown type \"%s\"\n",
      fhandle->cur_filename, fhandle->cur_line, typename);
    parse_error();
    return;
  }

  base_type = basic_type_of(type);
  if (base_type == NULL) {
    fprintf(pserr, "%s:%d  type %s does not devolve to a basic type"
      " that is valid as a constant assignment\n",
      fhandle->cur_filename, fhandle->cur_line, type->name);
    parse_error();
    return;
  }

  if ((base_type->type_is == NASD_RPCGEN_TYPE_FLOAT)
    || (base_type->type_is == NASD_RPCGEN_TYPE_DOUBLE))
  {
    fprintf(pserr, "%s:%d  nonintegral constants not supported\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  if (base_type->type_is == NASD_RPCGEN_TYPE_STRING) {
    NASD_ASSERT(seen_eq);
    valstr = strtok(fhandle->line_copy, "=");
    NASD_ASSERT(valstr != NULL);
    valstr = strtok(NULL, "");
    NASD_ASSERT(valstr != NULL);
    l = strlen(valstr);
    for(i=0;i<l;i++) {
      if (valstr[i] == '\"') {
        break;
      }
    }
    if (i >= l) {
      fprintf(pserr, "%s:%d  missing string\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return;
    }
    ab = NASD_RPCGEN_MAX_LINE_LEN;
    nb = 0;
    tmp = (char *)malloc(ab);
    if (tmp == NULL) {
      fprintf(pserr, "%s:%d  INTERNAL ERROR could not allocate %d bytes\n",
        fhandle->cur_filename, fhandle->cur_line, ab);
      parse_error();
      return;
    }
    NASD_ASSERT(valstr[i] == '\"');
    term = 0;
    bk = 0;
    if (valstr[l-1] == '\n') {
      valstr[l-1] = '\0';
      l--;
    }
    for(i++;i<l;i++) {
      if (bk) {
        tmp[nb] = valstr[i];
        nb++;
      }
      else {
        if (valstr[i] == '\\') {
          bk = 1;
        }
        else if (valstr[i] == '\"') {
          break;
        }
        else {
          tmp[nb] = valstr[i];
          nb++;
        }
      }
      if (nb >= ab) {
        ab += NASD_RPCGEN_MAX_LINE_LEN;
        tmp = (char *)realloc(tmp, ab);
        if (tmp == NULL) {
          fprintf(pserr, "%s:%d  INTERNAL ERROR could not allocate %d bytes\n",
            fhandle->cur_filename, fhandle->cur_line, ab);
          parse_error();
          return;
        }
      }
    }
    NASD_ASSERT(nb < ab);
    tmp[nb] = '\0';
    /* -1 for quote */
    if (i < (l-1)) {
      fprintf(pserr, "%s:%d  extra characters after string termination\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
    }
    newstr = tmp;
    goto got_val;
  }

  if (valstr[0] != '[') {
    NASD_ASSERT(vnw >= 0);
    tmp = strstr(valstr, "[");
    if (tmp == NULL) {
      valfuncstr = valstr;
      valstr = &words[vnw][1];
      vnw++;
    }
    else {
      valfuncstr = strtok(valstr, "[");
      valstr = strtok(NULL, "");
      NASD_ASSERT(valstr == &tmp[1]);
      words[vnw-1] = tmp;
    }
  }
  else {
    valstr = &valstr[1];
  }

  cw = (-1);
  l = strlen(valstr);

  for(i=0;i<l;i++) {
    if (valstr[i] == ']') {
      NASD_ASSERT(vnw > 0);
      if (val_oneword)
        val_oneword++;
      cw = vnw - 1;
      ci = i;
      break;
    }
  }

  if (val_oneword == 2) {
    if (count > 3) {
      fprintf(pserr, "%s:%d unexpected text follows expression\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return;
    }
  }
  else {
    if (cw < 0) {
      NASD_ASSERT(vnw > 0);
      for(j=vnw;((j<count)&&(cw<0));j++) {
        l = strlen(words[j]);
        for(i=0;i<l;i++) {
          if (words[j][i] == ']') {
            cw = j;
            ci = i-1;
            break;
           }
         }
      }
    }

    if (cw < 0) {
      fprintf(pserr, "%s:%d unterminated expression\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return;
    }

    if ((cw < (count-1)) || (ci < (l-1))) {
      fprintf(pserr, "%s:%d unexpected text follows expression\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return;
    }
    NASD_ASSERT(words[cw][ci+1] == ']');
    words[cw][ci+1] = '\0';
  }

  NASD_ASSERT(vnw >= 0);
  if (valfuncstr) {
    ret = parse_func(fhandle, valfuncstr, valstr, &words[vnw], count-vnw,
      &val64, &valu64);
    if (ret) {
      fprintf(pserr, "%s:%d  cannot evaluate function \"%s\"\n",
        fhandle->cur_filename, fhandle->cur_line, valfuncstr);
      parse_error();
      return;
    }
  }
  else {
    ret = parse_val(fhandle, valstr, &words[vnw], count-vnw, &val64, &valu64);
    if (ret) {
      fprintf(pserr, "%s:%d  cannot parse expression \"%s\"\n",
        fhandle->cur_filename, fhandle->cur_line, valstr);
      parse_error();
      return;
    }
  }

got_val:
  cnst = nasd_rpcgen_const_get();
  strclone(constname, &cnst->name);
  cnst->type = type;
  cnst->base_type = base_type;
  cnst->import_level = import_level;
  strclone(fhandle->cur_filename, &cnst->decl.define_file);
  cnst->decl.define_line = fhandle->cur_line;

  switch(cnst->base_type->type_is) {
    case NASD_RPCGEN_TYPE_INT8:
      cnst->val.val_int8 = (nasd_rpcgen_int8)(valu64&0xffUL);
      break;
    case NASD_RPCGEN_TYPE_UINT8:
      cnst->val.val_uint8 = (nasd_rpcgen_uint8)(valu64&0xffUL);
      break;
    case NASD_RPCGEN_TYPE_INT16:
      cnst->val.val_int16 = (nasd_rpcgen_int16)(valu64&0xffffUL);
      break;
    case NASD_RPCGEN_TYPE_UINT16:
      cnst->val.val_uint16 = (nasd_rpcgen_uint16)(valu64&0xffffUL);
      break;
    case NASD_RPCGEN_TYPE_INT32:
      cnst->val.val_int32 = (nasd_rpcgen_int32)(valu64&0xffffffffUL);
      break;
    case NASD_RPCGEN_TYPE_UINT32:
      cnst->val.val_uint32 = (nasd_rpcgen_uint32)(valu64&0xffffffffUL);
      break;
    case NASD_RPCGEN_TYPE_INT64:
      cnst->val.val_int64 = val64;
      break;
    case NASD_RPCGEN_TYPE_UINT64:
      cnst->val.val_int64 = valu64;
      break;
    case NASD_RPCGEN_TYPE_STRING:
      cnst->val.val_str = newstr;
      break;
    case NASD_RPCGEN_TYPE_PIPE:
      fprintf(pserr, "%s:%d  improper basic type\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return;
    default:
      NASD_PANIC();
  }

  ret = add_global_const(cnst, &dup);
  switch(ret) {
    case NASD_RPCGEN_OK:
      return;
    case NASD_RPCGEN_DUP_ENT:
      if (dup == NULL) {
        NASD_PANIC();
      }
      fprintf(pserr, "%s:%d  attempt to redefine constant %s"
        " previous definition at %s:%d\n",
        fhandle->cur_filename, fhandle->cur_line,
        cnst->name, dup->decl.define_file, dup->decl.define_line);
      parse_error();
      return;
    case NASD_RPCGEN_BAD_KEY:
    case NASD_RPCGEN_BAD_EXPR:
      fprintf(pserr, "%s:%d  invalid name for constant %s\n",
        fhandle->cur_filename, fhandle->cur_line,
        cnst->name);
      parse_error();
      return;
    default:
      fprintf(pserr, "%s:%d  error %d adding %s\n",
        fhandle->cur_filename, fhandle->cur_line,
        ret, cnst->name);
  }
}

void
see_uniq(
  char  *str)
{
  nasd_rpcgen_filename_t *uniq;
  int ret;

  uniq = nasd_rpcgen_filename_get();

  ret = nasd_rpcgen_namehash_add(uniq_seen_hash, str, uniq,
    NASD_RPCGEN_NAMEHASH_CLONEKEY|NASD_RPCGEN_NAMEHASH_PUNCTOK, NULL);
  if (ret) {
    NASD_PANIC();
  }

  strclone(str, &uniq->filename);
}

int
have_seen_uniq(
  char  *str)
{
  nasd_rpcgen_filename_t *uniq;

  nasd_rpcgen_namehash_lookup(uniq_seen_hash, str, &uniq);
  if (uniq) {
    /* already saw this */
    return(1);
  }
  return(0);
}

void
global_import(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  nasd_rpcgen_import_t *import;
  int l, ret, i;
  char *name;

  if (count != 2) {
    fprintf(pserr, "%s:%d unexpected number of tokens for IMPORT keyword\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  name = words[1];
  l = strlen(name);

  if (name[0] == '\"') {
    if (name[l-1] != '\"') {
      fprintf(pserr, "%s:%d improper quoting of filename\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return;
    }
    name[l-1] = '\0';
    name = &name[1];
    l = strlen(name);
  }

  nasd_rpcgen_namehash_lookup(import_seen_hash, name, &import);
  if (import) {
    /* already imported this */
    return;
  }

  import = nasd_rpcgen_import_get();
  strclone(fhandle->cur_filename, &import->decl.define_file);
  import->decl.define_line = fhandle->cur_line;

  ret = nasd_rpcgen_namehash_add(import_seen_hash, name, import,
    NASD_RPCGEN_NAMEHASH_CLONEKEY|NASD_RPCGEN_NAMEHASH_PUNCTOK, NULL);
  if (ret) {
    NASD_PANIC();
  }

  strclone(name, &import->filename);

  l = strlen(import->filename);
  for(i=l-1;i>0;i--) {
    /* lop off extension */
    if (import->filename[i] == '.') {
      import->filename[i] = '\0';
      break;
    }
  }

  if (import_level == 0) {
    import->gnext = &global_imports;
    import->gprev = global_imports.gprev;
    import->gprev->gnext = import;
    import->gnext->gprev = import;
    add_global_decl(&import->decl);
  }

  if (global_spec.nextra_inputs == 0) {
    import_level++;
  }
  parse_input_file(name, 1);
  if (global_spec.nextra_inputs == 0) {
    import_level--;
  }
}

void
global_type_m(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  nasd_rpcgen_type_t *type, *mt, *der_type, *dup_type;
  char *der_name, *type_name, *str;
  int ret, size;

  size = 0;
  if (count < 3) {
    fprintf(pserr, "%s:%d  missing typename\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  if (count > 4) {
    fprintf(pserr, "%s:%d  too many words\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  if (count == 4){
    int ret, l;
    char *tmp, *tmp2;
    nasd_rpcgen_int64 val_s;
    nasd_rpcgen_uint64 val_u;

    strclone(words[3], &tmp);
    l = strlen(tmp);
    if ((tmp[0] != '[') || (tmp[l-1] != ']')) {
      fprintf(pserr, "%s:%d  malformed size in TYPEM expression \"%s\"\n",
              fhandle->cur_filename, fhandle->cur_line, tmp);
      parse_error();
      absorb_bad_struct(fhandle);
      return;
    }
    tmp2 = &tmp[1];
    tmp[l-1] = '\0';
    ret = parse_val(fhandle, tmp2, NULL, 0, &val_s, &val_u);
    if (ret) {
      fprintf(pserr, "%s:%d  malformed size in TYPEM expression \"%s\"\n",
              fhandle->cur_filename, fhandle->cur_line, tmp2);
      parse_error();
      absorb_bad_struct(fhandle);
      return;
    }
    size = val_u;
  }

  type_name = words[1];
  der_name = words[2];

  type = lookup_type(type_name);
  if (type == NULL) {
    fprintf(pserr, "%s:%d  Unknown type \"%s\" (1)\n",
      fhandle->cur_filename, fhandle->cur_line,
      type_name);
    parse_error();
    return;
  }

  der_type = lookup_type(der_name);
  if (der_type == NULL) {
    fprintf(pserr, "%s:%d  Unknown type \"%s\" (2)\n",
      fhandle->cur_filename, fhandle->cur_line,
      der_name);
    parse_error();
    return;
  }

  NASD_ASSERT(type->size > 0);
  NASD_ASSERT(der_type->size > 0);

  str_prepend_suffix(type->name, "_otw", &str);

  mt = nasd_rpcgen_type_get();
  mt->name = str; /* str is cloned */
  mt->type_is = NASD_RPCGEN_TYPE_ARRAY;
  mt->ndim = 1;
  mt->nelements[0] = (type->size + (der_type->size-1)) / der_type->size;
  mt->size = mt->nelements[0] * der_type->size;
  mt->derived_from = der_type;
  mt->struct_next = NULL;
  mt->struct_list = NULL;
  mt->min_align = type->min_align;
  mt->import_level = import_level;
  strclone(fhandle->cur_filename, &mt->decl.define_file);
  mt->decl.define_line = fhandle->cur_line;
  mt->is_typem = 1;
  mt->m_orig_type = type;

  if(size && mt->size != size) {
    fprintf(pserr, "%s:%d structure %s is not of size %d, but is size %d\n",
            fhandle->cur_filename, fhandle->cur_line,
            mt->name, size, mt->size);
    parse_error();
    exit(1);
  }

  ret = add_global_type(mt, &dup_type);
  if (ret) {
    switch(ret) {
      case NASD_RPCGEN_OK:
        return;
      case NASD_RPCGEN_DUP_ENT:
        if (dup_type == NULL) {
          NASD_PANIC();
        }
        fprintf(pserr, "%s:%d  duplicate typename %s"
          " previous at %s:%d\n",
          fhandle->cur_filename, fhandle->cur_line,
          mt->name, dup_type->decl.define_file, dup_type->decl.define_line);
        parse_error();
        return;
      case NASD_RPCGEN_BAD_KEY:
        fprintf(pserr, "%s:%d  invalid typename %s\n",
          fhandle->cur_filename, fhandle->cur_line,
          mt->name);
        parse_error();
        return;
      default:
        fprintf(pserr, "%s:%d  error %d adding type %s\n",
          fhandle->cur_filename, fhandle->cur_line,
          ret, mt->name);
        parse_error();
        return;
    }
  }
}

void
global_type(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  nasd_rpcgen_type_t *type, *dup_type;
  int ret;

  type = parse_type(fhandle, &words[1], count-1);

  if (type == NULL) {
    fprintf(pserr, "%s:%d  cannot parse type\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  ret = add_global_type(type, &dup_type);
  if (ret) {
    switch(ret) {
      case NASD_RPCGEN_OK:
        return;
      case NASD_RPCGEN_DUP_ENT:
        if (dup_type == NULL) {
          NASD_PANIC();
        }
        fprintf(pserr, "%s:%d  duplicate typename %s"
          " previous at %s:%d\n",
          fhandle->cur_filename, fhandle->cur_line,
          type->name, dup_type->decl.define_file, dup_type->decl.define_line);
        parse_error();
        return;
      case NASD_RPCGEN_BAD_KEY:
        fprintf(pserr, "%s:%d  invalid typename %s\n",
          fhandle->cur_filename, fhandle->cur_line,
          type->name);
        parse_error();
        return;
      default:
        fprintf(pserr, "%s:%d  error %d adding type %s\n",
          fhandle->cur_filename, fhandle->cur_line,
          ret, type->name);
        parse_error();
        return;
    }
  }
}

void
absorb_bad_struct(
  nasd_rpcgen_fhandle_t  *fhandle)
{
  char line[NASD_RPCGEN_MAX_LINE_LEN], **words;
  int count;

  while(nasd_cpp_fgets(line, sizeof(line), fhandle) != NULL) {
    words = lex_line(line, &count,
      NASD_RPCGEN_LEX_BRACE_EXPR,
      fhandle);
    if (count == 0)
      continue;

    if (words[0][0] == '}') {
      if ((count > 1) || (strlen(words[0]) > 1)) {
        if ((!((count == 2) && (!strcmp(words[0], "}"))))
          && (!((count == 1) && (words[0][1]))))
        {
          fprintf(pserr, "%s:%d  extra tokens closing bad struct\n",
            fhandle->cur_filename, fhandle->cur_line);
          parse_error();
        }
      }
      return;
    }
  }

  fprintf(pserr, "%s:%d unexpected EOF during struct\n",
    fhandle->cur_filename, fhandle->cur_line);
  parse_error();
  exit(1);
}

void
absorb_bad_call(
  nasd_rpcgen_fhandle_t  *fhandle)
{
  char line[NASD_RPCGEN_MAX_LINE_LEN], **words;
  int count;

  while(nasd_cpp_fgets(line, sizeof(line), fhandle) != NULL) {
    words = lex_line(line, &count,
      NASD_RPCGEN_LEX_BRACE_EXPR,
      fhandle);
    if (count == 0)
      continue;

    if (!strcmp(words[0], ")")) {
      if (count > 1) {
        fprintf(pserr, "%s:%d  extra tokens closing bad call\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
      }
      return;
    }
  }

  fprintf(pserr, "%s:%d unexpected EOF during struct\n",
    fhandle->cur_filename, fhandle->cur_line);
  parse_error();
  exit(1);
}

nasd_rpcgen_type_t *
get_pad_elem(
  int  padsize,
  int  padnum)
{
  nasd_rpcgen_type_t *pad;
  char str[128];

  sprintf(str, "pad%03d", padnum);
  pad = nasd_rpcgen_type_get();
  strclone(str, &pad->name);
  pad->type_is = NASD_RPCGEN_TYPE_ARRAY;
  pad->nelements[0] = padsize;
  pad->ndim = 1;
  pad->size = padsize;
  pad->min_align = 1;
  pad->import_level = import_level;
  pad->ispad = 1;
  pad->decl.define_file = __FILE__;
  pad->decl.define_line = __LINE__;
  pad->derived_from = pad_type;

  return(pad);
}

void
global_struct(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **in_words,
  int                      in_count)
{
  nasd_rpcgen_type_t *struct_type, *st, *e, *elem, *dup, *autotype, *pad_elem;
  int count, ret, closed, padnum, cur_align, pad_out, next_word, term, l, size;
  char line[NASD_RPCGEN_MAX_LINE_LEN], **words, *autotype_name;
  char *struct_name, *tmp, *tmp2;
  nasd_rpcgen_uint64 val_u;
  nasd_rpcgen_int64 val_s;
  int nomarshall;

  nomarshall = 0;
  closed = 0;
  autotype = NULL;
  autotype_name = NULL;
  padnum = 1;
  cur_align = 0;
  size = 0;
  pad_out = NASD_MAX(global_spec.min_alignment, global_spec.pad_to);

  if (in_count < 3) {
    fprintf(pserr, "%s:%d  not enough tokens for STRUCT\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    if (!strcmp(in_words[in_count-1], "{")) {
      absorb_bad_struct(fhandle);
    }
    return;
  }

  struct_name = in_words[1];

  next_word = 2;
  term = 0;

  while(next_word < in_count) {
    if (in_words[next_word][0] == '{') {
      term = 1;
      if (strcmp(in_words[next_word], "{")) {
        fprintf(pserr, "%s:%d  malformed STRUCT line, not terminated with {\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
        return;
      }
      break;
    }
    if (!strncmp(in_words[next_word], "PAD-TO:", 7)) {
      strclone(&in_words[next_word][7], &tmp);
      l = strlen(tmp);
      if ((tmp[0] != '[') || (tmp[l-1] != ']')) {
        fprintf(pserr, "%s:%d  malformed PAD-TO expression \"%s\"\n",
          fhandle->cur_filename, fhandle->cur_line, tmp);
        parse_error();
        absorb_bad_struct(fhandle);
        return;
      }
      tmp2 = &tmp[1];
      tmp[l-1] = '\0';
      ret = parse_val(fhandle, tmp2, NULL, 0, &val_s, &val_u);
      if (ret) {
        fprintf(pserr, "%s:%d  malformed PAD-TO expression \"%s\"\n",
          fhandle->cur_filename, fhandle->cur_line, tmp2);
        parse_error();
        absorb_bad_struct(fhandle);
        return;
      }
      pad_out = NASD_MAX(pad_out, val_u);
      free(tmp);
    }
    else if (!strncmp(in_words[next_word], "SIZE:", 5)) {
      strclone(&in_words[next_word][5], &tmp);
      l = strlen(tmp);
      if ((tmp[0] != '[') || (tmp[l-1] != ']')) {
        fprintf(pserr, "%s:%d  malformed SIZE expression \"%s\"\n",
          fhandle->cur_filename, fhandle->cur_line, tmp);
        parse_error();
        absorb_bad_struct(fhandle);
        return;
      }
      tmp2 = &tmp[1];
      tmp[l-1] = '\0';
      ret = parse_val(fhandle, tmp2, NULL, 0, &val_s, &val_u);
      if (ret) {
        fprintf(pserr, "%s:%d  malformed SIZE expression \"%s\"\n",
          fhandle->cur_filename, fhandle->cur_line, tmp2);
        parse_error();
        absorb_bad_struct(fhandle);
        return;
      }
      size = val_u;
      free(tmp);
    }
    else if (!strcmp(in_words[next_word], "NOMARSHALL")) {
      nomarshall = 1;
    }
    else {
      fprintf(pserr, "%s:%d  malformed struct directive \"%s\"\n",
        fhandle->cur_filename, fhandle->cur_line, in_words[next_word]);
      parse_error();
      absorb_bad_struct(fhandle);
      return;
    }
    next_word++;
  }

  if (term == 0) {
    fprintf(pserr, "%s:%d  malformed STRUCT line, not terminated\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  if (next_word < (in_count - 1)) {
    fprintf(pserr, "%s:%d  malformed STRUCT line, tokens after term\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return;
  }

  struct_type = lookup_struct(struct_name);
  if (struct_type) {
    fprintf(pserr, "%s:%d  attempt to redefine struct %s"
      " previously defined at %s:%d\n",
      fhandle->cur_filename, fhandle->cur_line, struct_name,
      struct_type->decl.define_file, struct_type->decl.define_line);
    parse_error();
    absorb_bad_struct(fhandle);
    return;
  }

  st = nasd_rpcgen_type_get();
  st->decl.decl_type = NASD_RPCGEN_DECL_STRUCT;
  st->struct_elems = nasd_rpcgen_namehash_get();
  strclone(struct_name, &st->name);
  st->type_is = NASD_RPCGEN_TYPE_STRUCT;
  st->size = 0;
  st->min_align = NASD_MAX(global_spec.min_alignment, global_spec.pad_to);
  st->import_level = import_level;
  strclone(fhandle->cur_filename, &st->decl.define_file);
  st->decl.define_line = fhandle->cur_line;
  st->nomarshall = nomarshall;

  while(nasd_cpp_fgets(line, sizeof(line), fhandle) != NULL) {
    words = lex_line(line, &count,
      NASD_RPCGEN_LEX_BRACE_EXPR,
      fhandle);
    if (count == 0)
      continue;

    if (words[0][0] == '}') {
      closed = 1;
      if ((count > 1) || (strlen(words[0]) > 1)) {
        /* are we defining a new type? */
        if ((count == 2) && (!strcmp(words[0], "}"))) {
          autotype_name = words[1];
          autotype = nasd_rpcgen_type_get();
          strclone(autotype_name, &autotype->name);
          strclone(fhandle->cur_filename, &autotype->decl.define_file);
          autotype->decl.define_line = fhandle->cur_line;
        }
        else if ((count == 1) && (words[0][1])) {
          autotype_name = &words[0][1];
          autotype = nasd_rpcgen_type_get();
          strclone(autotype_name, &autotype->name);
          strclone(fhandle->cur_filename, &autotype->decl.define_file);
          autotype->decl.define_line = fhandle->cur_line;
        }
        else {
          fprintf(pserr, "%s:%d  extra tokens closing struct\n",
            fhandle->cur_filename, fhandle->cur_line);
          parse_error();
        }
      }
      break;
    }

    /* now we have a valid struct line pulled in */
    elem = parse_type(fhandle, words, count);
    if (elem == NULL) {
      fprintf(pserr, "%s:%d  could not parse structure element\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      continue;
    }

    for(e=elem;e;e=e->derived_from) {
      if (e->type_is == NASD_RPCGEN_TYPE_PIPE) {
        fprintf(pserr, "%s:%d  struct cannot contain pipe\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
        continue;
      }
    }

    ret = nasd_rpcgen_namehash_add(st->struct_elems,
      elem->name, elem, NASD_RPCGEN_NAMEHASH_NODIGIT, &dup);
    switch (ret) {
      case NASD_RPCGEN_OK:
        break;
      case NASD_RPCGEN_DUP_ENT:
        fprintf(pserr, "%s:%d  duplicate structure element %s"
          " (previous %s:%d)\n",
          fhandle->cur_filename, fhandle->cur_line,
          elem->name, dup->decl.define_file, dup->decl.define_line);
        parse_error();
        break;
      case NASD_RPCGEN_BAD_EXPR:
        fprintf(pserr, "%s:%d  invalid name for structure element %s\n",
          fhandle->cur_filename, fhandle->cur_line,
          elem->name);
        parse_error();
        break;
      default:
        fprintf(pserr, "%s:%d  internal error %d adding structure "
          "element %s\n",
          fhandle->cur_filename, fhandle->cur_line,
          ret, elem->name);
        parse_error();
        break;
    }

    if (ret)
      continue;

    NASD_ASSERT(st->min_align >= elem->min_align);

    if (cur_align &&
      ((elem->min_align > cur_align) || (cur_align % elem->min_align)))
    {
      /* we need to pad */
      NASD_ASSERT(elem->min_align != cur_align);
      if (elem->min_align > cur_align) {
        pad_elem = get_pad_elem(elem->min_align - cur_align, padnum);
      }
      else {
        pad_elem = get_pad_elem(elem->min_align - (cur_align%elem->min_align),
          padnum);
      }
      padnum++;
      st->size += pad_elem->size;
      cur_align += pad_elem->size;
      cur_align %= elem->min_align;
      NASD_ASSERT(cur_align == 0);
      cur_align = elem->min_align;
      if (st->struct_list_tail) {
        st->struct_list_tail->struct_next = pad_elem;
        st->struct_list_tail = pad_elem;
      }
      else {
        st->struct_list = st->struct_list_tail = pad_elem;
      }
      ret = nasd_rpcgen_namehash_add(st->struct_elems,
        pad_elem->name, pad_elem, NASD_RPCGEN_NAMEHASH_NODIGIT, &dup);
      if (ret) {
        fprintf(pserr, "%s:%d  internal error %d padding structure\n",
          fhandle->cur_filename, fhandle->cur_line, ret);
        parse_error();
        absorb_bad_struct(fhandle);
        return;
      }
    }

    st->size += elem->size;
    cur_align += elem->size;
    cur_align %= st->min_align;
    if (cur_align == 0)
      cur_align = st->min_align;

    if (st->struct_list_tail) {
      st->struct_list_tail->struct_next = elem;
      st->struct_list_tail = elem;
    }
    else {
      st->struct_list = st->struct_list_tail = elem;
    }
  }

  if (st->size % pad_out) {
    /* we need to pad */
    pad_elem = get_pad_elem(pad_out - (st->size % pad_out), padnum);
    padnum++;
    st->size += pad_elem->size;
    cur_align += pad_elem->size;
    if (st->struct_list_tail) {
      st->struct_list_tail->struct_next = pad_elem;
      st->struct_list_tail = pad_elem;
    }
    else {
      st->struct_list = st->struct_list_tail = pad_elem;
    }
  }

  if (size && st->size != size) {
    fprintf(pserr, "%s:%d structure %s is not of size %d, but is size %d\n",
            st->decl.define_file, st->decl.define_line,
            st->name, size, st->size);
    parse_error();
    exit(1);
  }

  if (closed == 0) {
    fprintf(pserr, "%s:%d unexpected EOF during struct\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    exit(1);
  }

  ret = add_global_type(st, &dup);
  switch(ret) {
    case NASD_RPCGEN_OK:
      break;
    case NASD_RPCGEN_DUP_ENT:
      fprintf(pserr, "%s:%d  duplicate structure %s"
        " (previously defined at %s:%d)\n",
        st->decl.define_file, st->decl.define_line,
        st->name, dup->decl.define_file, dup->decl.define_line);
      parse_error();
      break;
    case NASD_RPCGEN_BAD_KEY:
    case NASD_RPCGEN_BAD_EXPR:
      fprintf(pserr, "%s:%d  invalid name for structure %s\n",
        fhandle->cur_filename, fhandle->cur_line,
        st->name);
      parse_error();
      break;
    default:
      fprintf(pserr, "%s:%d  internal error %d adding structure %s\n",
        fhandle->cur_filename, fhandle->cur_line,
        ret, st->name);
      parse_error();
      break;
  }

  if (st->struct_list_tail == NULL) {
    fprintf(pserr, "%s:%d  empty structure\n",
      st->decl.define_file, st->decl.define_line);
    parse_error();
  }

  if (autotype) {
    autotype->type_is = NASD_RPCGEN_TYPE_DERIVED;
    autotype->ndim = 0;
    autotype->size = st->size;
    autotype->min_align = st->min_align;
    autotype->import_level = import_level;
    autotype->derived_from = st;

    ret = add_global_type(autotype, &dup);
    if (ret) {
      switch(ret) {
        case NASD_RPCGEN_OK:
          return;
        case NASD_RPCGEN_DUP_ENT:
          if (dup == NULL) {
            NASD_PANIC();
          }
          fprintf(pserr, "%s:%d  duplicate typename %s"
            " previous at %s:%d\n",
            fhandle->cur_filename, fhandle->cur_line,
            autotype->name, dup->decl.define_file, dup->decl.define_line);
          parse_error();
          return;
        case NASD_RPCGEN_BAD_KEY:
          fprintf(pserr, "%s:%d  invalid typename %s\n",
            fhandle->cur_filename, fhandle->cur_line,
            autotype->name);
          parse_error();
          return;
        default:
          fprintf(pserr, "%s:%d  error %d adding type %s\n",
            fhandle->cur_filename, fhandle->cur_line,
            ret, autotype->name);
          parse_error();
          return;
      }
    }
  }
}

void
global_call(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **in_words,
  int                      in_count)
{
  char line[NASD_RPCGEN_MAX_LINE_LEN], **words;
  nasd_rpcgen_call_t *call, *dup_call;
  nasd_rpcgen_type_t *elem, *dup;
  int l, closed, dir, count, ret;
  char *call_name;

  closed = 0;

  if (in_count == 2) {
    l = strlen(in_words[1]);
    if (in_words[1][l-1] != '(') {
      fprintf(pserr, "%s:%d  missing \'(\'\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return;
    }
    in_words[1][l-1] = '\0';
  }

  if ((in_count != 2) && (in_count != 3)) {
    fprintf(pserr, "%s:%d  unexpected number of tokens for CALL\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    if (in_words[in_count-1][strlen(in_words[in_count-1])-1] == '(')
      absorb_bad_call(fhandle);
    return;
  }

  if (in_count == 3) {
    if (strcmp(in_words[2], "(")) {
      l = strlen(in_words[2]);
      fprintf(pserr, "%s:%d  unexpected number of tokens for CALL\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      if (in_words[2][l-1] == '(')
        absorb_bad_call(fhandle);
      return;
    }
  }

  call_name = in_words[1];

  call = lookup_call(call_name);
  if (call) {
    fprintf(pserr, "%s:%d  attempt to redefine call %s, "
      "previously defined at %s:%d\n",
      fhandle->cur_filename, fhandle->cur_line, call_name,
      call->decl.define_file, call->decl.define_line);
  }

  call = nasd_rpcgen_call_get();
  strclone(call_name, &call->name);
  strclone(fhandle->cur_filename, &call->decl.define_file);
  call->decl.define_line = fhandle->cur_line;
  call->import_level = import_level;
  call->call_elems = nasd_rpcgen_namehash_get();
  call->call_args = call->call_list_tail = NULL;

  while(nasd_cpp_fgets(line, sizeof(line), fhandle) != NULL) {
    words = lex_line(line, &count,
      NASD_RPCGEN_LEX_BRACE_EXPR,
      fhandle);
    if (count == 0)
      continue;

    if (words[0][0] == ')') {
      closed = 1;
      if ((count > 1) || (strlen(words[0]) > 1)) {
        fprintf(pserr, "%s:%d  extra tokens closing call\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
      }
      break;
    }

    /* now we have a valid call line pulled in */
    if (!strcasecmp(words[0], "in")) {
      dir = NASD_RPCGEN_DIR_IN;
    }
    else if (!strcasecmp(words[0], "out")) {
      dir = NASD_RPCGEN_DIR_OUT;
    }
    else {
      fprintf(pserr, "%s:%d  bad direction \"%s\"\n",
        fhandle->cur_filename, fhandle->cur_line, words[0]);
      parse_error();
      continue;
    }

    elem = parse_type(fhandle, &words[1], count-1);
    if (elem == NULL) {
      fprintf(pserr, "%s:%d  could not parse call element\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      continue;
    }

    ret = nasd_rpcgen_namehash_add(call->call_elems,
      elem->name, elem, NASD_RPCGEN_NAMEHASH_NODIGIT, &dup);
    switch (ret) {
      case NASD_RPCGEN_OK:
        break;
      case NASD_RPCGEN_BAD_KEY:
        fprintf(pserr, "%s:%d  illegal element name %s\n",
          fhandle->cur_filename, fhandle->cur_line,
          elem->name);
        parse_error();
        break;
      case NASD_RPCGEN_DUP_ENT:
        fprintf(pserr, "%s:%d  duplicate call element %s"
          " (previous %s:%d)\n",
          fhandle->cur_filename, fhandle->cur_line,
          elem->name, dup->decl.define_file, dup->decl.define_line);
        parse_error();
        break;
      case NASD_RPCGEN_BAD_EXPR:
        fprintf(pserr, "%s:%d  invalid name for call element %s\n",
          fhandle->cur_filename, fhandle->cur_line,
          elem->name);
        parse_error();
        break;
      default:
        fprintf(pserr, "%s:%d  internal error %d adding call "
          "element %s\n",
          fhandle->cur_filename, fhandle->cur_line,
          ret, elem->name);
        parse_error();
        break;
    }

    if (ret)
      continue;

    elem->call_dir = dir;
    elem->call_next = NULL;

    if (call->call_list_tail) {
      call->call_list_tail->call_next = elem;
      call->call_list_tail = elem;
    }
    else {
      call->call_list = call->call_list_tail = elem;
    }
  }

  if (closed == 0) {
    fprintf(pserr, "%s:%d unexpected EOF during call\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    exit(1);
  }

  ret = add_global_call(call, &dup_call);
  switch(ret) {
    case NASD_RPCGEN_OK:
      break;
    case NASD_RPCGEN_DUP_ENT:
      fprintf(pserr, "%s:%d  duplicate structure %s"
        " (previously defined at %s:%d)\n",
        call->decl.define_file, call->decl.define_line,
        call->name, dup_call->decl.define_file, dup_call->decl.define_line);
      parse_error();
      break;
    case NASD_RPCGEN_BAD_EXPR:
      fprintf(pserr, "%s:%d  invalid name for structure %s\n",
        fhandle->cur_filename, fhandle->cur_line,
        call->name);
      parse_error();
      break;
    default:
      fprintf(pserr, "%s:%d  internal error %d adding structure %s\n",
        fhandle->cur_filename, fhandle->cur_line,
        ret, call->name);
      parse_error();
      break;
  }
}

nasd_rpcgen_type_t *
parse_type(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                   **words,
  int                      count)
{
  int next_word, ndim, l, nelem[NASD_RPCGEN_MAX_ARRDIM], i, ret, isstruct, j, ispipe;
  char *arr_n, *base_type_name, *new_name, *tmp;
  char tmp2[NASD_RPCGEN_MAX_LINE_LEN];
  nasd_rpcgen_type_t *base_type, *type;
  nasd_rpcgen_uint64 val_u;
  nasd_rpcgen_int64 val_s;

  isstruct = 0;
  ispipe = 0;
  ndim = 0;

  next_word = 0;

  if (count < 2) {
    fprintf(pserr, "%s:%d  Not enough tokens for TYPE keyword\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return(NULL);
  }

  if (!strcasecmp(words[next_word], "pipe")) {
    ispipe = 1;
    next_word++;
  }

  if (!strcasecmp(words[next_word], "struct")) {
    isstruct = 1;
    next_word++;
  }

  base_type_name = strtok(words[next_word], "[");
  while((arr_n = strtok(NULL, "["))) {
    l = strlen(arr_n);
    if (arr_n[l-1] != ']') {
      fprintf(pserr, "%s:%d  cannot resolve array dimension [\"%s\", "
        "no closing\n", fhandle->cur_filename, fhandle->cur_line, arr_n);
      parse_error();
      return(NULL);
    }
    strclone(arr_n, &tmp);
    tmp[l-1] = '\0';
    ret = parse_val(fhandle, tmp, NULL, 0, &val_s, &val_u);
    if (ret) {
      fprintf(pserr, "%s:%d  bad expression \"%s\"\n",
        fhandle->cur_filename, fhandle->cur_line, tmp);
      parse_error();
      return(NULL);
    }
    free(tmp);
    if (val_s < 0) {
      fprintf(pserr, "%s:%d  negative value\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return(NULL);
    }
    nelem[ndim] = val_u;
    ndim++;
    if (ndim >= NASD_RPCGEN_MAX_ARRDIM) {
      fprintf(pserr, "%s:%d  too many array dimensions\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return(NULL);
    }
  }

  if (isstruct) {
    base_type = lookup_struct(base_type_name);

    if (base_type == NULL) {
      fprintf(pserr, "%s:%d  Unknown struct \"%s\"\n",
        fhandle->cur_filename, fhandle->cur_line,
        base_type_name);
      parse_error();
      return(NULL);
    }
  }
  else {
    base_type = lookup_type(base_type_name);

    if (base_type == NULL) {
      fprintf(pserr, "%s:%d  Unknown type \"%s\"\n",
        fhandle->cur_filename, fhandle->cur_line,
        base_type_name);
      parse_error();
      return(NULL);
    }
  }

  next_word++;
  while((next_word < count) && (words[next_word][0] == '[')) {
    arr_n = strtok(words[next_word], "[");
    while(arr_n) {
      l = strlen(arr_n);
      if (arr_n[l-1] != ']') {
        fprintf(pserr, "%s:%d  cannot resolve array dimension \"%s\", "
          "no closing\n", fhandle->cur_filename, fhandle->cur_line,
          arr_n);
        parse_error();
        return(NULL);
      }
      for(j=0;j<l;j++) {
        if (arr_n[j] == ']')
          break;
        tmp2[j] = arr_n[j];
      }
      tmp2[j] = '\0';
      ret = parse_val(fhandle, tmp2, NULL, 0, &val_s, &val_u);
      if (ret) {
        fprintf(pserr, "%s:%d  bad expression \"%s\"\n",
          fhandle->cur_filename, fhandle->cur_line, arr_n);
        parse_error();
        return(NULL);
      }
      if (val_s < 0) {
        fprintf(pserr, "%s:%d  negative value\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
        return(NULL);
      }
      nelem[ndim] = val_u;
      ndim++;
      if (ndim >= NASD_RPCGEN_MAX_ARRDIM) {
        fprintf(pserr, "%s:%d  too many array dimensions\n",
          fhandle->cur_filename, fhandle->cur_line);
        parse_error();
        return(NULL);
      }
      arr_n = strtok(NULL, "[");
    }
    next_word++;
  }

  if (next_word >= count) {
    fprintf(pserr, "%s:%d  missing new typename\n",
      fhandle->cur_filename, fhandle->cur_line);
    parse_error();
    return(NULL);
  }

  new_name = words[next_word];
  next_word++;

  if (next_word < count) {
    fprintf(pserr, "%s:%d  too many fields\n",
      fhandle->cur_filename, fhandle->cur_line);
    fflush(stderr);
    exit(1);
  }

  type = nasd_rpcgen_type_get();

  strclone(new_name, &type->name);
  if (ndim) {
    if (ispipe) {
      fprintf(pserr, "%s:%d  cannot directly pipe arrays\n",
        fhandle->cur_filename, fhandle->cur_line);
      parse_error();
      return(NULL);
    }
    type->type_is = NASD_RPCGEN_TYPE_ARRAY;
    for(i=0;i<ndim;i++)
      type->nelements[i] = nelem[i];
    for(;i<NASD_RPCGEN_MAX_ARRDIM;i++)
      type->nelements[i] = 0;
    type->ndim = ndim;
    type->size = base_type->size;
    for(i=ndim-1;i>=0;i--) {
      type->size *= nelem[i];
    }
  }
  else {
    if (ispipe)
      type->type_is = NASD_RPCGEN_TYPE_PIPE;
    else
      type->type_is = NASD_RPCGEN_TYPE_DERIVED;
    type->ndim = 0;
    type->size = base_type->size;
  }
  type->derived_from = base_type;
  type->struct_next = NULL;
  type->struct_list = NULL;
  type->min_align = base_type->min_align;
  type->import_level = import_level;
  strclone(fhandle->cur_filename, &type->decl.define_file);
  type->decl.define_line = fhandle->cur_line;

  return(type);
}

void
parse_input_file(
  char  *filename,
  int    search_path)
{
  char fname[MAXPATHLEN], line[NASD_RPCGEN_MAX_LINE_LEN], **words;
  nasd_rpcgen_fhandle_t fhandle;
  nasd_rpcgen_cpparg_t *sd;
  int count, ret;

  pserr = stderr;

  ret = nasd_cpp_fopen(filename, &fhandle);

  if (ret && search_path) {
    /* search include path */
    for(sd=searchdirs;sd&&ret;sd=sd->next) {
      if ((strlen(sd->arg)+strlen(filename)+1) < MAXPATHLEN) {
        sprintf(fname, "%s%s", sd->arg, filename);
        ret = nasd_cpp_fopen(fname, &fhandle);
      }
    }
  }

  if (ret) {
    fprintf(stderr, "ERROR: could not read %s through cpp\n", filename);
    fflush(stderr);
    exit(1);
  }

  while(nasd_cpp_fgets(line, sizeof(line), &fhandle) != NULL) {

    words = lex_line(line, &count,
      NASD_RPCGEN_LEX_BRACE_EXPR,
      &fhandle);
    if (count == 0)
      continue;

    /*
     * Look for keywords. Bad keyword? Barf and continue.
     */
    if (!strcmp(words[0], "TYPE")) {
      global_type(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "TYPEM")) {
      global_type_m(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "IMPORT")) {
      global_import(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "CONSTANT")) {
      global_const(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "STRUCT")) {
      global_struct(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "CALL")) {
      global_call(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "UUID")) {
      global_uuid(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "DCE-ENDPOINT")) {
      global_dce_endpoint(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "BASEID")) {
      global_baseid(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "VERSION")) {
      global_version(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "MARSHALL")) {
      global_marshall(&fhandle, words, count);
    }
    else if (!strcmp(words[0], "UNIQ")) {
      if (count != 2) {
        fprintf(pserr, "%s:%d unexpected number of tokens for UNIQ keyword\n",
          fhandle.cur_filename, fhandle.cur_line);
        parse_error();
        return;
      }
      if (have_seen_uniq(words[1])) {
        /*
         * This is because cpp on linux is a buggy piece of crap,
         * and retches if we don't read everything from it
         * before closing the pipe.
         */
        nasd_cpp_flush(&fhandle);
        goto done;
      }
      see_uniq(words[1]);
    }
    else {
      fprintf(pserr, "%s:%d  bad keyword \"%s\"\n",
        fhandle.cur_filename, fhandle.cur_line,
        words[0]);
      parse_error();
    }
  }

done:
  nasd_cpp_fclose(&fhandle);
}

int
parse_val(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                    *str,
  char                   **extra_words,
  int                      extra_word_count,
  nasd_rpcgen_int64       *result_s,
  nasd_rpcgen_uint64      *result_u)
{
  nasd_rpcgen_const_t *cnst;
  int ret, val, pos;

  /* skip whitespace */
  for(pos=0;str[pos]&&isspace(str[pos]);pos++);

  if ((isdigit(str[pos])) || (str[pos] == '-')) {
    if (str[pos] == '0'){
      if (str[pos+1] == 'x') {
        ret = sscanf(&str[pos+2], "%x", &val);
      }
      else {
        ret = sscanf(&str[pos], "%o", &val);
      }
    }
    else {
      ret = sscanf(&str[pos], "%d", &val);
    }
    if (ret != 1)
      return(NASD_RPCGEN_BAD_EXPR);
    *result_s = val;
    *result_u = val;
    return(NASD_RPCGEN_OK);
  }

  cnst = lookup_const(&str[pos]);
  if (cnst == NULL) {
    return(NASD_RPCGEN_BAD_KEY);
  }

  switch(cnst->base_type->type_is) {
    case NASD_RPCGEN_TYPE_INT8:
      *result_s = (nasd_rpcgen_int64)cnst->val.val_int8;
      *result_u = ((nasd_rpcgen_uint64)cnst->val.val_int8)&0xffUL;
      break;
    case NASD_RPCGEN_TYPE_UINT8:
      *result_s = ((nasd_rpcgen_int64)cnst->val.val_uint8)&0xffUL;
      *result_u = ((nasd_rpcgen_uint64)cnst->val.val_uint8)&0xffUL;
      break;
    case NASD_RPCGEN_TYPE_INT16:
      *result_s = (nasd_rpcgen_int64)cnst->val.val_int16;
      *result_u = ((nasd_rpcgen_uint64)cnst->val.val_int16)&0xffffUL;
      break;
    case NASD_RPCGEN_TYPE_UINT16:
      *result_s = ((nasd_rpcgen_int64)cnst->val.val_uint16)&0xffffUL;
      *result_u = ((nasd_rpcgen_uint64)cnst->val.val_uint16)&0xffffUL;
      break;
    case NASD_RPCGEN_TYPE_INT32:
      *result_s = (nasd_rpcgen_int64)cnst->val.val_int32;
      *result_u = ((nasd_rpcgen_uint64)cnst->val.val_int32)&0xffffffffUL;
      break;
    case NASD_RPCGEN_TYPE_UINT32:
      *result_s = ((nasd_rpcgen_int64)cnst->val.val_uint32)&0xffffffffUL;
      *result_u = ((nasd_rpcgen_uint64)cnst->val.val_uint32)&0xffffffffUL;
      break;
    case NASD_RPCGEN_TYPE_INT64:
      *result_s = cnst->val.val_int64;
      *result_u = cnst->val.val_int64;
      break;
    case NASD_RPCGEN_TYPE_UINT64:
      *result_s = cnst->val.val_uint64;
      *result_u = cnst->val.val_uint64;
      break;
    case NASD_RPCGEN_TYPE_STRING:
      fprintf(pserr, "%s:%d  string used as value\n",
        fhandle->cur_filename, fhandle->cur_line);
      return(NASD_RPCGEN_BAD_EXPR);
      /*NOTREACHED*/
      break;
    case NASD_RPCGEN_TYPE_PIPE:
      fprintf(pserr, "%s:%d  pipe used as value\n",
        fhandle->cur_filename, fhandle->cur_line);
      return(NASD_RPCGEN_BAD_EXPR);
      /*NOTREACHED*/
      break;
    default:
      NASD_PANIC();
  }

  return(NASD_RPCGEN_OK);
}

int
parse_func_size(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                    *str,
  char                   **extra_words,
  int                      extra_word_count,
  nasd_rpcgen_int64       *result_s,
  nasd_rpcgen_uint64      *result_u)
{
  nasd_rpcgen_type_t *type;

  type = lookup_type(str);
  if (type == NULL) {
    fprintf(pserr, "%s:%d  unknown type \"%s\"\n",
      fhandle->cur_filename, fhandle->cur_line,
      str);
    parse_error();
    return(NASD_RPCGEN_BAD_EXPR);
  }

  *result_s = type->size;
  *result_u = type->size;

  return(NASD_RPCGEN_OK);
}

int
parse_func(
  nasd_rpcgen_fhandle_t   *fhandle,
  char                    *funcstr,
  char                    *str,
  char                   **extra_words,
  int                      extra_word_count,
  nasd_rpcgen_int64       *result_s,
  nasd_rpcgen_uint64      *result_u)
{
  int ret;

  if (!strcasecmp(funcstr, "size")) {
    ret = parse_func_size(fhandle, str, extra_words, extra_word_count,
      result_s, result_u);
  }
  else if (!strcasecmp(funcstr, "math")) {
    ret = parse_func_math(fhandle, str, extra_words, extra_word_count,
      result_s, result_u);
  }
  else {
    fprintf(pserr, "%s:%d  unknown function %s\n",
      fhandle->cur_filename, fhandle->cur_line, funcstr);
    parse_error();
    ret = NASD_RPCGEN_BAD_EXPR;
  }
  return(ret);
}

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