/*
  By Elliot Lee <sopwith@redhat.com>
  Copyright (C) 1998 Red Hat Software.
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <glib.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "orbit-idl.h"
#include "orbit-c-backend.h"

/* Input: main part of filename to write, namespace & tree from parser
   Output: (in files) header, skeletons, stubs, and common code.
*/
#ifdef HAVE_INDENT_KR
#  define INDENT "indent -npro -kr -i8 -bad -fca -sc -sob"
#else
#  ifdef HAVE_INDENT
#    define INDENT "indent -npro -bad -bap -bc -sob -br -ce -cli2 -npcs -di1 -psl -i3 -lp"
#  else
#    define INDENT "cat"
#  endif
#endif

void
orbit_IDL_tree_to_c(const char *base_filename, IDL_ns namespace, IDL_tree tree)
{
  FILE *curfile;
  GString *curfile_name = g_string_new(NULL);
  char *hdrname;

  if(debuglevel >= 4)
    orbit_cbe_print_node(tree, 0);
  
  base_filename = g_basename(base_filename);

  if(!disable_headers) {
    g_string_sprintf(curfile_name, INDENT " > %s.h", base_filename);
    curfile = popen(curfile_name->str, "w");
    if(!curfile) {
      g_warning("Failed to open %s for writing: %s",
	        curfile_name->str, g_strerror(errno));
      return;
    }
    orbit_cbe_write_header(curfile, namespace, tree);
    fflush(curfile);
    fclose(curfile);
  }
  g_string_sprintf(curfile_name, "%s.h", base_filename);
  hdrname = g_strdup(g_basename(curfile_name->str));

  if(!disable_common) {
    g_string_sprintf(curfile_name, INDENT " > %s-common.c", base_filename);
    curfile = popen(curfile_name->str, "w");
    if(!curfile) {
      g_warning("Failed to open %s for writing: %s",
	        curfile_name->str, g_strerror(errno));
      return;
    }
    orbit_cbe_write_common(curfile, namespace, tree, hdrname);
    fflush(curfile);
    fclose(curfile);
  }

  if(!disable_stubs) {
    g_string_sprintf(curfile_name, INDENT " > %s-stubs.c", base_filename);
    curfile = popen(curfile_name->str, "w");
    if(!curfile) {
      g_warning("Failed to open %s for writing: %s",
	        curfile_name->str, g_strerror(errno));
      return;
    }
    orbit_cbe_write_stubs(curfile, namespace, tree, hdrname);
    fflush(curfile);
    fclose(curfile);
  }

  if(!disable_skels) {
    g_string_sprintf(curfile_name, INDENT " > %s-skels.c", base_filename);
    curfile = popen(curfile_name->str, "w");
    if(!curfile) {
      g_warning("Failed to open %s for writing: %s",
	        curfile_name->str, g_strerror(errno));
      return;
    }
    orbit_cbe_write_skeletons(curfile, namespace, tree, hdrname);
    fflush(curfile);
    fclose(curfile);
  }

  if(enable_skeleton_impl) {
    g_string_sprintf(curfile_name, INDENT " > %s-impl.c", base_filename);
    curfile = popen(curfile_name->str, "w");
    if(!curfile) {
      g_warning("Failed to open %s for writing: %s",
		curfile_name->str, g_strerror(errno));
      return;
    }
    orbit_cbe_write_skelimpl(curfile, namespace, tree, hdrname);
    fflush(curfile);
    fclose(curfile);
  }

  g_string_free(curfile_name, TRUE);
  g_free(hdrname);
}

void indent(int level) {
  int i;
  for(i = 0; i < level; i++) g_print(" ");
}

void
orbit_cbe_print_node(IDL_tree node, int indent_level)
{
  IDL_tree curnode;
  char *s;

  indent(indent_level);

  if(node == NULL) {
    g_print("(null)\n");
    return;
  }

  g_print("[%d] ", IDL_NODE_REFS(node));

  switch(IDL_NODE_TYPE(node)) {

  case IDLN_NONE:
    g_print("NONE\n");
    break;

  case IDLN_LIST:
    g_print("LIST:\n");
    for(curnode = node; curnode;
	curnode = IDL_LIST(curnode).next) {
      orbit_cbe_print_node(IDL_LIST(curnode).data, indent_level + 2);
    }
    break;

  case IDLN_GENTREE:
    g_print("GENTREE:\n");
#if 0
    /* Changed in libIDL.  But don't need it here anyway. */
    orbit_cbe_print_node(IDL_GENTREE(node).data, indent_level + 2);
    indent(indent_level + 2);
    g_print("children:\n");
    orbit_cbe_print_node(IDL_GENTREE(node).children, indent_level + 4);
#endif
    break;

  case IDLN_INTEGER:
    g_print("INTEGER: %" IDL_LL "d\n", IDL_INTEGER(node).value);
    break;

  case IDLN_STRING:
    g_print("STRING: %s\n", IDL_STRING(node).value);
    break;

  case IDLN_WIDE_STRING:
    g_print("WIDE STRING: %ls\n", IDL_WIDE_STRING(node).value);
    break;

  case IDLN_CHAR:
    g_print("CHAR: %s\n", IDL_CHAR(node).value);
    break;

  case IDLN_WIDE_CHAR:
    g_print("WIDE CHAR: %ls\n", IDL_WIDE_CHAR(node).value);
    break;

  case IDLN_FIXED:
    g_print("FIXED: %s\n", IDL_FIXED(node).value);
    break;

  case IDLN_FLOAT:
    g_print("FLOAT: %f\n", IDL_FLOAT(node).value);
    break;

  case IDLN_BOOLEAN:
    g_print("BOOLEAN: %s\n", (IDL_BOOLEAN(node).value)?"True":"False");
    break;

  case IDLN_IDENT:
    s = IDL_ns_ident_to_qstring(IDL_IDENT_TO_NS(node), "_", 0);
    g_print("IDENT: %s NSQ: %s RID: \"%s\"\n",
	    IDL_IDENT(node).str, s,
	    IDL_IDENT_REPO_ID(node) ? IDL_IDENT_REPO_ID(node) : "");
    g_free(s);
    break;

  case IDLN_TYPE_DCL:
    g_print("TYPE DCL:\n");
    orbit_cbe_print_node(IDL_TYPE_DCL(node).type_spec, indent_level + 2);
    indent(indent_level + 2); g_print("decls:\n");
    orbit_cbe_print_node(IDL_TYPE_DCL(node).dcls, indent_level + 4);
    break;

  case IDLN_CONST_DCL:
    g_print("CONST DCL:\n");
    orbit_cbe_print_node(IDL_CONST_DCL(node).const_type, indent_level + 2);
    indent(indent_level + 2); g_print("ident:\n");
    orbit_cbe_print_node(IDL_CONST_DCL(node).ident, indent_level + 4);
    indent(indent_level + 2); g_print("const_exp:\n");
    orbit_cbe_print_node(IDL_CONST_DCL(node).const_exp, indent_level + 4);
    break;

  case IDLN_EXCEPT_DCL:
    g_print("EXCEPT DCL:\n");
    orbit_cbe_print_node(IDL_EXCEPT_DCL(node).ident, indent_level + 2);
    indent(indent_level + 2); g_print("members:\n");
    orbit_cbe_print_node(IDL_EXCEPT_DCL(node).members, indent_level + 4);
    break;

  case IDLN_ATTR_DCL:
    g_print("ATTR_DCL (%s):\n", (IDL_ATTR_DCL(node).f_readonly)?"readonly":"rw");
    orbit_cbe_print_node(IDL_ATTR_DCL(node).param_type_spec, indent_level + 2);
    indent(indent_level + 2); g_print("simple_declarations:\n");
    orbit_cbe_print_node(IDL_ATTR_DCL(node).simple_declarations, indent_level + 4);
    break;

  case IDLN_OP_DCL:
    g_print("OP DCL (%s):\n", (IDL_OP_DCL(node).f_oneway)?"oneway":"normal");
    orbit_cbe_print_node(IDL_OP_DCL(node).ident, indent_level + 2);
    indent(indent_level + 2); g_print("op_type_spec:\n");
    orbit_cbe_print_node(IDL_OP_DCL(node).op_type_spec, indent_level + 4);
    indent(indent_level + 2); g_print("parameter_dcls:\n");
    orbit_cbe_print_node(IDL_OP_DCL(node).parameter_dcls, indent_level + 4);
    indent(indent_level + 2); g_print("raises_expr:\n");
    orbit_cbe_print_node(IDL_OP_DCL(node).raises_expr, indent_level + 4);
    indent(indent_level + 2); g_print("context_expr:\n");
    orbit_cbe_print_node(IDL_OP_DCL(node).context_expr, indent_level + 4);
    break;

  case IDLN_PARAM_DCL:
    g_print("PARAM DCL: ");
    switch(IDL_PARAM_DCL(node).attr) {
    case IDL_PARAM_IN: g_print("(in)\n"); break;
    case IDL_PARAM_OUT: g_print("(out)\n"); break;
    case IDL_PARAM_INOUT: g_print("(inout)\n"); break;
    }
    orbit_cbe_print_node(IDL_PARAM_DCL(node).param_type_spec, indent_level + 2);
    indent(indent_level + 2); g_print("simple_declarator:\n");
    orbit_cbe_print_node(IDL_PARAM_DCL(node).simple_declarator, indent_level + 4);
    break;
  case IDLN_FORWARD_DCL:
    g_print("FORWARD DCL:\n");
    orbit_cbe_print_node(IDL_FORWARD_DCL(node).ident, indent_level + 2);
    break;
  case IDLN_INTERFACE:
    g_print("INTERFACE:\n");
    orbit_cbe_print_node(IDL_INTERFACE(node).ident, indent_level + 2);
    indent(indent_level + 2); g_print("inheritance_spec:\n");
    orbit_cbe_print_node(IDL_INTERFACE(node).inheritance_spec, indent_level + 4);
    indent(indent_level + 2); g_print("body:\n");
    orbit_cbe_print_node(IDL_INTERFACE(node).body, indent_level + 4);
    break;
  case IDLN_MODULE:
    g_print("MODULE:\n");
    orbit_cbe_print_node(IDL_MODULE(node).ident, indent_level + 2);
    indent(indent_level + 2); g_print("definition_list:\n");
    orbit_cbe_print_node(IDL_MODULE(node).definition_list, indent_level + 4);
    break;

  case IDLN_TYPE_INTEGER:
    if(!IDL_TYPE_INTEGER(node).f_signed) g_print("TYPE unsigned ");
    switch(IDL_TYPE_INTEGER(node).f_type) {
    case IDL_INTEGER_TYPE_SHORT: g_print("short\n"); break;
    case IDL_INTEGER_TYPE_LONG: g_print("long\n"); break;
    case IDL_INTEGER_TYPE_LONGLONG: g_print("long long\n"); break;
    }
    break;
  case IDLN_TYPE_FLOAT:
    switch(IDL_TYPE_FLOAT(node).f_type) {
    case IDL_FLOAT_TYPE_FLOAT: g_print("TYPE float\n"); break;
    case IDL_FLOAT_TYPE_DOUBLE: g_print("TYPE double\n"); break;
    case IDL_FLOAT_TYPE_LONGDOUBLE: g_print("TYPE long double\n"); break;
    }
    break;
  case IDLN_TYPE_FIXED:
    g_print("TYPE fixed:\n");
    orbit_cbe_print_node(IDL_TYPE_FIXED(node).positive_int_const, indent_level + 2);
    orbit_cbe_print_node(IDL_TYPE_FIXED(node).integer_lit, indent_level + 2);
    break;
  case IDLN_TYPE_STRING:
    g_print("TYPE string:\n");
    orbit_cbe_print_node(IDL_TYPE_STRING(node).positive_int_const, indent_level + 2);
    break;
  case IDLN_TYPE_WIDE_STRING:
    g_print("TYPE wide string:\n");
    orbit_cbe_print_node(IDL_TYPE_WIDE_STRING(node).positive_int_const, indent_level + 2);
    break;
  case IDLN_TYPE_ENUM:
    g_print("TYPE enum:\n");
    orbit_cbe_print_node(IDL_TYPE_ENUM(node).ident, indent_level + 2);
    indent(indent_level + 2); g_print("enumerator_list:\n");
    orbit_cbe_print_node(IDL_TYPE_ENUM(node).enumerator_list, indent_level + 4);
    break;
  case IDLN_TYPE_ARRAY:
    g_print("TYPE array:\n");
    orbit_cbe_print_node(IDL_TYPE_ARRAY(node).ident, indent_level + 2);
    indent(indent_level + 2); g_print("size_list:\n");
    orbit_cbe_print_node(IDL_TYPE_ARRAY(node).size_list, indent_level + 4);
    break;
  case IDLN_TYPE_SEQUENCE:
    g_print("TYPE sequence:\n");
    orbit_cbe_print_node(IDL_TYPE_SEQUENCE(node).simple_type_spec, indent_level + 2);
    indent(indent_level + 2); g_print("positive_int_const:\n");
    orbit_cbe_print_node(IDL_TYPE_SEQUENCE(node).positive_int_const, indent_level + 4);
    break;
  case IDLN_TYPE_STRUCT:
    g_print("TYPE struct:\n");
    orbit_cbe_print_node(IDL_TYPE_STRUCT(node).ident, indent_level + 2);
    indent(indent_level + 2); g_print("member_list:\n");
    orbit_cbe_print_node(IDL_TYPE_STRUCT(node).member_list, indent_level + 4);
    break;
  case IDLN_TYPE_UNION:
    g_print("TYPE union:\n");
    orbit_cbe_print_node(IDL_TYPE_UNION(node).ident, indent_level + 2);
    indent(indent_level + 2); g_print("switch_type_spec:\n");
    orbit_cbe_print_node(IDL_TYPE_UNION(node).switch_type_spec, indent_level + 4);
    indent(indent_level + 2); g_print("switch_body:\n");
    orbit_cbe_print_node(IDL_TYPE_UNION(node).switch_body, indent_level + 4);
    break;
  case IDLN_MEMBER:
    g_print("MEMBER:\n");
    orbit_cbe_print_node(IDL_MEMBER(node).type_spec, indent_level + 2);
    indent(indent_level + 2); g_print("dcls:\n");
    orbit_cbe_print_node(IDL_MEMBER(node).dcls, indent_level + 4);
    break;
  case IDLN_CASE_STMT:
    g_print("CASE_STMT:\n");
    orbit_cbe_print_node(IDL_CASE_STMT(node).labels, indent_level + 2);
    indent(indent_level + 2); g_print("element_spec:\n");
    orbit_cbe_print_node(IDL_CASE_STMT(node).element_spec, indent_level + 4);
    break;
  case IDLN_BINOP:
    g_print("BINOP ");
    switch(IDL_BINOP(node).op) {
    case IDL_BINOP_OR: g_print("or:\n"); break;
    case IDL_BINOP_XOR: g_print("xor:\n"); break;
    case IDL_BINOP_AND: g_print("and:\n"); break;
    case IDL_BINOP_SHR: g_print("shr:\n"); break;
    case IDL_BINOP_SHL: g_print("shl:\n"); break;
    case IDL_BINOP_ADD: g_print("add:\n"); break;
    case IDL_BINOP_SUB: g_print("sub:\n"); break;
    case IDL_BINOP_MULT: g_print("mult:\n"); break;
    case IDL_BINOP_DIV: g_print("div:\n"); break;
    case IDL_BINOP_MOD: g_print("mod:\n"); break;
    }
    indent(indent_level + 2); g_print("left:\n");
    orbit_cbe_print_node(IDL_BINOP(node).left, indent_level + 4);
    indent(indent_level + 2); g_print("right:\n");
    orbit_cbe_print_node(IDL_BINOP(node).right, indent_level + 4);
    break;
  case IDLN_UNARYOP:
    g_print("UNARYOP ");
    switch(IDL_UNARYOP(node).op) {
    case IDL_UNARYOP_PLUS: g_print("plus:\n"); break;
    case IDL_UNARYOP_MINUS: g_print("minus:\n"); break;
    case IDL_UNARYOP_COMPLEMENT: g_print("complement:\n"); break;
    }
    orbit_cbe_print_node(IDL_UNARYOP(node).operand, indent_level + 2);
    break;
  case IDLN_TYPE_CHAR:
    g_print("TYPE char\n");
    break;
  case IDLN_TYPE_WIDE_CHAR:
    g_print("TYPE wide char\n");
    break;
  case IDLN_TYPE_BOOLEAN:
    g_print("TYPE boolean\n");
    break;
  case IDLN_TYPE_OCTET:
    g_print("TYPE octet\n");
    break;
  case IDLN_TYPE_OBJECT:
    g_print("TYPE object\n");
    break;
  case IDLN_TYPE_ANY:
    g_print("TYPE any\n");
    break;
  case IDLN_TYPE_TYPECODE:
    g_print("TYPE TypeCode\n");
    break;
  default:
    g_print("unhandled %d\n", IDL_NODE_TYPE(node));
  }
}

char *
orbit_cbe_get_typename(IDL_tree tree)
{
  GString *tmpstr;
  char *retval = NULL;

  if(!tree) {
    return g_strdup("void");
  }

  tmpstr = g_string_new(NULL);

  switch(IDL_NODE_TYPE(tree)) {
  case IDLN_TYPE_ANY:
    retval = "CORBA_any";
    break;
  case IDLN_TYPE_FLOAT:
    switch(IDL_TYPE_FLOAT(tree).f_type) {
    case IDL_FLOAT_TYPE_FLOAT:
      retval = "CORBA_float";
      break;
    case IDL_FLOAT_TYPE_DOUBLE:
      retval = "CORBA_double";
      break;
    case IDL_FLOAT_TYPE_LONGDOUBLE:
      retval = "CORBA_long_double";
      break;
    }
    break;
  case IDLN_TYPE_FIXED:
#if 0
    fprintf(of, "CORBA_fixed_%d_%d",
	    IDL_TYPE_FIXED(tree).positive_int_const,
	    IDL_TYPE_FIXED(tree).integer_lit);
#else
    retval = "CORBA_fixed_d_s";
#endif
    break;
  case IDLN_TYPE_INTEGER:
    g_string_assign(tmpstr, "CORBA_");
    if(!IDL_TYPE_INTEGER(tree).f_signed)
      g_string_append(tmpstr, "unsigned_");

    switch(IDL_TYPE_INTEGER(tree).f_type) {
    case IDL_INTEGER_TYPE_SHORT:
      g_string_append(tmpstr, "short");
      break;
    case IDL_INTEGER_TYPE_LONGLONG:
      g_string_append(tmpstr, "long_");
    case IDL_INTEGER_TYPE_LONG:
      g_string_append(tmpstr, "long");
      break;
    }
    break;
  case IDLN_TYPE_STRING:
    retval = "CORBA_string";
    break;
  case IDLN_TYPE_OCTET:
    retval = "CORBA_octet";
    break;
  case IDLN_TYPE_WIDE_STRING:
    retval = "CORBA_wstring";
    break;
  case IDLN_TYPE_CHAR:
    retval = "CORBA_char";
    break;
  case IDLN_TYPE_WIDE_CHAR:
    retval = "CORBA_wchar";
    break;
  case IDLN_TYPE_BOOLEAN:
    retval = "CORBA_boolean";
    break;
  case IDLN_TYPE_STRUCT:
    retval = orbit_cbe_get_typename(IDL_TYPE_STRUCT(tree).ident);
    g_string_assign(tmpstr, retval); g_free(retval); retval = NULL;
    break;
  case IDLN_EXCEPT_DCL:
    retval = orbit_cbe_get_typename(IDL_EXCEPT_DCL(tree).ident);
    g_string_assign(tmpstr, retval); g_free(retval); retval = NULL;
    break;
  case IDLN_TYPE_ARRAY:
    retval = orbit_cbe_get_typename(IDL_TYPE_ARRAY(tree).ident);
    g_string_assign(tmpstr, retval); g_free(retval); retval = NULL;
    break;
  case IDLN_TYPE_UNION:
    retval = orbit_cbe_get_typename(IDL_TYPE_UNION(tree).ident);
    g_string_assign(tmpstr, retval); g_free(retval); retval = NULL;
    break;
  case IDLN_TYPE_ENUM:
    retval = orbit_cbe_get_typename(IDL_TYPE_ENUM(tree).ident);
    g_string_assign(tmpstr, retval); g_free(retval); retval = NULL;
    break;
  case IDLN_IDENT:
    {
      char *id = IDL_ns_ident_to_qstring(IDL_IDENT_TO_NS(tree), "_", 0);
      g_string_assign(tmpstr, id);
      g_free(id);
    }
    break;
  case IDLN_PARAM_DCL:
    return orbit_cbe_get_typename(IDL_PARAM_DCL(tree).param_type_spec);
    break;
  case IDLN_TYPE_SEQUENCE:
    return orbit_cbe_get_typename(IDL_get_parent_node(tree, IDLN_TYPE_DCL, NULL));
    break;
  case IDLN_INTERFACE:
    retval = orbit_cbe_get_typename(IDL_INTERFACE(tree).ident);
    g_string_assign(tmpstr, retval); g_free(retval); retval = NULL;
    break;
  case IDLN_NATIVE:
    retval = "gpointer";
    break;
  case IDLN_TYPE_OBJECT:
    retval = "CORBA_Object";
    break;
  case IDLN_TYPE_TYPECODE:
    retval = "CORBA_TypeCode";
    break;
  default:
    g_error("We were asked to get a typename for a %s",
	    IDL_tree_type_names[IDL_NODE_TYPE(tree)]);
  }

  if(retval)
    return g_strdup(retval);
  else {
    retval = g_strdup(tmpstr->str);
    g_string_free(tmpstr, TRUE);
    return retval;
  }
}

void
orbit_cbe_write_typename(FILE *of, IDL_tree tree)
{
  char *t = orbit_cbe_get_typename(tree);

  fprintf(of, "%s", t);

  g_free(t);
}

void
orbit_cbe_write_typespec(FILE *of, IDL_tree tree)
{
  if(!tree) {
    fprintf(of, "void");
    return;
  }

  switch(IDL_NODE_TYPE(tree)) {
  case IDLN_TYPE_FLOAT:
    switch(IDL_TYPE_FLOAT(tree).f_type) {
    case IDL_FLOAT_TYPE_FLOAT:
      fprintf(of, "CORBA_float");
      break;
    case IDL_FLOAT_TYPE_DOUBLE:
      fprintf(of, "CORBA_double");
      break;
    case IDL_FLOAT_TYPE_LONGDOUBLE:
      fprintf(of, "CORBA_long_double");
      break;
    };
    break;
  case IDLN_TYPE_BOOLEAN:
    fprintf(of, "CORBA_boolean");
    break;
  case IDLN_TYPE_FIXED:
#if 0
    fprintf(of, "CORBA_fixed_%d_%d",
	    IDL_TYPE_FIXED(tree).positive_int_const,
	    IDL_TYPE_FIXED(tree).integer_lit);
#else
    fprintf(of, "CORBA_fixed_d_s");
#endif
    break;
  case IDLN_TYPE_INTEGER:
    fprintf(of, "CORBA_");
    if(!IDL_TYPE_INTEGER(tree).f_signed)
      fprintf(of, "unsigned_");
    switch(IDL_TYPE_INTEGER(tree).f_type) {
    case IDL_INTEGER_TYPE_SHORT:
      fprintf(of, "short");
      break;
    case IDL_INTEGER_TYPE_LONGLONG:
      fprintf(of, "long_");
    case IDL_INTEGER_TYPE_LONG:
      fprintf(of, "long");
      break;
    }
    break;
  case IDLN_TYPE_STRING:
    fprintf(of, "CORBA_char *");
    break;
  case IDLN_TYPE_OCTET:
    fprintf(of, "CORBA_octet");
    break;
  case IDLN_TYPE_WIDE_STRING:
    fprintf(of, "CORBA_wchar *");
    break;
  case IDLN_TYPE_CHAR:
    fprintf(of, "CORBA_char");
    break;
  case IDLN_TYPE_WIDE_CHAR:
    fprintf(of, "CORBA_wchar");
    break;
  case IDLN_TYPE_STRUCT:
    fprintf(of, "struct ");
  case IDLN_TYPE_ARRAY:    
  case IDLN_TYPE_UNION:
  case IDLN_TYPE_ENUM:
  case IDLN_EXCEPT_DCL:
    {
      char *id = IDL_ns_ident_to_qstring(IDL_IDENT_TO_NS(IDL_TYPE_ENUM(tree).ident), "_", 0);
      fprintf(of, "%s", id);
      g_free(id);
    }
    break;
  case IDLN_IDENT:
    {
      char *id = IDL_ns_ident_to_qstring(IDL_IDENT_TO_NS(tree), "_", 0);
      fprintf(of, "%s", id);
      g_free(id);
    }
    break;
  case IDLN_TYPE_OBJECT:
    fprintf(of, "CORBA_Object");
    break;
  case IDLN_TYPE_SEQUENCE:
    fprintf(of, "struct { CORBA_unsigned_long _maximum, _length;\n");
    orbit_cbe_write_typespec(of, IDL_TYPE_SEQUENCE(tree).simple_type_spec);
    fprintf(of, " *_buffer;\nCORBA_boolean _release; } \n");
    break;
  case IDLN_TYPE_ANY:
    fprintf(of, "CORBA_any");
    break;
  case IDLN_NATIVE:
    fprintf(of, "gpointer");
    break;
  case IDLN_TYPE_TYPECODE:
    fprintf(of, "CORBA_TypeCode");
    break;
  default:
    g_error("We were asked to print a typespec %s", IDL_tree_type_names[tree->_type]);
  }
}

/* Writes the value of the constant in 'tree' to file handle 'of' */
void
orbit_cbe_write_const(FILE *of, IDL_tree tree)
{
  char *opc = NULL;
  switch(IDL_NODE_TYPE(tree)) {
  case IDLN_BOOLEAN:
    fprintf(of, "%s", IDL_BOOLEAN(tree).value?"CORBA_TRUE":"CORBA_FALSE");
    break;
  case IDLN_CHAR:
    fprintf(of, "'%s'", IDL_CHAR(tree).value);
    break;
  case IDLN_FLOAT:
    fprintf(of, "%f", IDL_FLOAT(tree).value);
    break;
  case IDLN_INTEGER:
    fprintf(of, "%" IDL_LL "d", IDL_INTEGER(tree).value);
    break;
  case IDLN_STRING:
    fprintf(of, "\"%s\"", IDL_STRING(tree).value);
    break;
  case IDLN_WIDE_CHAR:
    fprintf(of, "L'%ls'", IDL_WIDE_CHAR(tree).value);
    break;
  case IDLN_WIDE_STRING:
    fprintf(of, "L\"%ls\"", IDL_WIDE_STRING(tree).value);
    break;
  case IDLN_BINOP:
    fprintf(of, "(");
    orbit_cbe_write_const(of, IDL_BINOP(tree).left);
    switch(IDL_BINOP(tree).op) {
    case IDL_BINOP_OR:
      opc = "|";
      break;
    case IDL_BINOP_XOR:
      opc = "!!!";
      break;
    case IDL_BINOP_AND:
      opc = "&";
      break;
    case IDL_BINOP_SHR:
      opc = ">>";
      break;
    case IDL_BINOP_SHL:
      opc = "<<";
      break;
    case IDL_BINOP_ADD:
      opc = "+";
      break;
    case IDL_BINOP_SUB:
      opc = "-";
      break;
    case IDL_BINOP_MULT:
      opc = "*";
      break;
    case IDL_BINOP_DIV:
      opc = "/";
      break;
    case IDL_BINOP_MOD:
      opc = "%";
      break;
    }
    fprintf(of, " %s ", opc);
    orbit_cbe_write_const(of, IDL_BINOP(tree).right);
    fprintf(of, ")");
    break;
  case IDLN_UNARYOP:
    switch(IDL_UNARYOP(tree).op) {
    case IDL_UNARYOP_PLUS: opc = "+"; break;
    case IDL_UNARYOP_MINUS: opc = "-"; break;
    case IDL_UNARYOP_COMPLEMENT: opc = "~"; break;
    }
    fprintf(of, "%s", opc);
    orbit_cbe_write_const(of, IDL_UNARYOP(tree).operand);
    break;
  case IDLN_IDENT:
    {
      /* XXX fixme, brokenness */
      char *id;
      id = IDL_ns_ident_to_qstring(IDL_IDENT_TO_NS(tree), "_", 0);
      fprintf(of, "%s", id);
      g_free(id);
    }
    break;
  default:
    g_error("We were asked to print a constant for %s", IDL_tree_type_names[tree->_type]);
  }
}

static const int * const
orbit_cbe_get_typeoffsets_table (void)
{
  static int typeoffsets[IDLN_LAST];
  static gboolean initialized = FALSE;
  
  if (!initialized) {
    int i;

    for (i = 0; i < IDLN_LAST; ++i)
      typeoffsets[i] = -1;

    typeoffsets[IDLN_FORWARD_DCL] = 8; /* (same as objref) */
    typeoffsets[IDLN_TYPE_INTEGER] = 0;
    typeoffsets[IDLN_TYPE_FLOAT] = 0;
    typeoffsets[IDLN_TYPE_FIXED] = 3;
    typeoffsets[IDLN_TYPE_CHAR] = 5;
    typeoffsets[IDLN_TYPE_WIDE_CHAR] = 6;
    typeoffsets[IDLN_TYPE_STRING] = 12;
    typeoffsets[IDLN_TYPE_WIDE_STRING] = 13;
    typeoffsets[IDLN_TYPE_BOOLEAN] = 4;
    typeoffsets[IDLN_TYPE_OCTET] = 7;
    typeoffsets[IDLN_TYPE_ANY] = 16;
    typeoffsets[IDLN_TYPE_OBJECT] = 9;
    typeoffsets[IDLN_TYPE_TYPECODE] = 9;
    typeoffsets[IDLN_TYPE_ENUM] = 8;
    typeoffsets[IDLN_TYPE_SEQUENCE] = 14;
    typeoffsets[IDLN_TYPE_ARRAY] = 15;
    typeoffsets[IDLN_TYPE_STRUCT] = 10;
    typeoffsets[IDLN_TYPE_UNION] = 11;
    typeoffsets[IDLN_NATIVE] = 15; /* no pointers ever, same as fixed array */
    typeoffsets[IDLN_INTERFACE] = 9; /* (same as objref) */
    
    initialized = TRUE;
  }
  
  return typeoffsets;
}

/*******

	This is a rather hairy function. Its purpose is to output the
	required number of *'s that indicate the amount of indirection
	for input, output, & input-output parameters, and return
	values.  We do this by having a table of the number of *'s for
	each type and purpose (nptrrefs_required), taken from 19.20
	of the CORBA 2.2 spec, and then having a table that translates
	from the IDLN_* enums into an index into nptrrefs_required (typeoffsets)

 *******/
gint
orbit_cbe_param_numptrs(IDL_tree param, IDL_ParamRole role)
{
  const int * const typeoffsets = orbit_cbe_get_typeoffsets_table ();
  const int nptrrefs_required[][4] = {
    {0,1,1,0} /* float */,
    {0,1,1,0} /* double */,
    {0,1,1,0} /* long double */,
    {1,1,1,0} /* fixed_d_s 3 */, 
    {0,1,1,0} /* boolean */,
    {0,1,1,0} /* char */,
    {0,1,1,0} /* wchar */,
    {0,1,1,0} /* octet */,
    {0,1,1,0} /* enum */,
    {0,1,1,0} /* objref */,
    {1,1,1,0} /* fixed struct 10 */,
    {1,1,1,0} /* fixed union */,
    {0,1,1,0} /* string */,
    {0,1,1,0} /* wstring */,
    {1,1,2,1} /* sequence */,
    {0,0,0,0} /* fixed array */,
    {1,1,2,1} /* any 16 */
  };
  int retval = 0;

  if(!param) /* void */
    return 0;

  /* Now, how do we use this table? :) */
  param = cbe_get_typespec(param);

  g_assert(param);

  switch(IDL_NODE_TYPE(param))
    {
    case IDLN_TYPE_STRUCT:
    case IDLN_TYPE_UNION:
      if(((role == DATA_RETURN) || (role == DATA_OUT))
	 && !cbe_type_is_fixed_length(param))
	retval++;

      break;
    case IDLN_TYPE_ARRAY:
      if(!cbe_type_is_fixed_length(param) && role == DATA_OUT)
	retval++;
      break;
    default:
      break;
    }

  g_assert(typeoffsets[IDL_NODE_TYPE(param)] >= 0);

  switch(role) {
  case DATA_IN: role = 0; break;
  case DATA_INOUT: role = 1; break;
  case DATA_OUT: role = 2; break;
  case DATA_RETURN: role = 3; break;
  }

  retval+=nptrrefs_required[typeoffsets[IDL_NODE_TYPE(param)]][role];

  return retval;
}

void
orbit_cbe_param_printptrs(FILE *of, IDL_tree param, IDL_ParamRole role)
{
  int i, n;

  if(param == NULL)
    return;

  param = cbe_get_typespec(param);
  n = orbit_cbe_param_numptrs(param, role);

  if(IDL_NODE_TYPE(param) == IDLN_TYPE_ARRAY
     && ((role == DATA_OUT) || (role == DATA_RETURN))) {
    if(role == DATA_RETURN)
      fprintf(of, "_slice*");
    else if(!cbe_type_is_fixed_length(param)) /* && role == DATA_OUT */
      fprintf(of, "_slice*");
  }

  for(i = 0; i < n; i++)
    fprintf(of, "*");
}

gboolean
cbe_type_is_fixed_length(IDL_tree ts)
{
  gboolean is_fixed = TRUE;
  IDL_tree curitem;

  ts = cbe_get_typespec(ts);
  switch(IDL_NODE_TYPE(ts)) {
  case IDLN_TYPE_FLOAT:
  case IDLN_TYPE_INTEGER:
  case IDLN_TYPE_ENUM:
  case IDLN_TYPE_CHAR:
  case IDLN_TYPE_WIDE_CHAR:
  case IDLN_TYPE_OCTET:
  case IDLN_TYPE_BOOLEAN:
    return TRUE;
    break;
  case IDLN_TYPE_SEQUENCE:
  case IDLN_TYPE_STRING:
  case IDLN_TYPE_WIDE_STRING:
  case IDLN_TYPE_OBJECT:
  case IDLN_FORWARD_DCL:
  case IDLN_INTERFACE:
  case IDLN_TYPE_ANY:
  case IDLN_NATIVE:
  case IDLN_TYPE_TYPECODE:
    return FALSE;
    break;
  case IDLN_TYPE_UNION:
#if 1
    for(curitem = IDL_TYPE_UNION(ts).switch_body; curitem;
	curitem = IDL_LIST(curitem).next) {
      is_fixed &= cbe_type_is_fixed_length(IDL_LIST(IDL_CASE_STMT(IDL_LIST(curitem).data).element_spec).data);
    }
    return is_fixed;
    break;
#endif
  case IDLN_EXCEPT_DCL:
  case IDLN_TYPE_STRUCT:
    for(curitem = IDL_TYPE_STRUCT(ts).member_list; curitem;
	curitem = IDL_LIST(curitem).next) {
      is_fixed &= cbe_type_is_fixed_length(IDL_LIST(curitem).data);
    }
    return is_fixed;
    break;
  case IDLN_TYPE_ARRAY:
    return cbe_type_is_fixed_length(IDL_TYPE_DCL(IDL_get_parent_node(ts, IDLN_TYPE_DCL, NULL)).type_spec);
    break;
  default:
    g_warning("I'm not sure if type %s is fixed-length", IDL_tree_type_names[IDL_NODE_TYPE(ts)]);
    return FALSE;
  }
}

IDL_tree
cbe_get_typespec(IDL_tree node)
{
  if(node == NULL)
    return NULL;

  switch(IDL_NODE_TYPE(node)) {
  case IDLN_TYPE_INTEGER:
  case IDLN_TYPE_FLOAT:
  case IDLN_TYPE_FIXED:
  case IDLN_TYPE_CHAR:
  case IDLN_TYPE_WIDE_CHAR:
  case IDLN_TYPE_STRING:
  case IDLN_TYPE_WIDE_STRING:
  case IDLN_TYPE_BOOLEAN:
  case IDLN_TYPE_OCTET:
  case IDLN_TYPE_ANY:
  case IDLN_TYPE_OBJECT:
  case IDLN_TYPE_ENUM:
  case IDLN_TYPE_SEQUENCE:
  case IDLN_TYPE_ARRAY:
  case IDLN_TYPE_STRUCT:
  case IDLN_TYPE_UNION:
  case IDLN_EXCEPT_DCL:
  case IDLN_INTERFACE:
  case IDLN_NATIVE:
  case IDLN_TYPE_TYPECODE:
    return node;
    break;
  case IDLN_TYPE_DCL:
    return cbe_get_typespec(IDL_TYPE_DCL(node).type_spec);
    break;
  case IDLN_PARAM_DCL:
    return cbe_get_typespec(IDL_PARAM_DCL(node).param_type_spec);
    break;
  case IDLN_MEMBER:
    return cbe_get_typespec(IDL_MEMBER(node).type_spec);
    break;
  case IDLN_FORWARD_DCL:
    return cbe_get_typespec(IDL_FORWARD_DCL(node).ident);
    break;
  case IDLN_LIST:
  case IDLN_IDENT:
    return cbe_get_typespec(IDL_get_parent_node(node, IDLN_ANY, NULL));
    break;
  default:
    g_warning("Unhandled node type %s!", IDL_tree_type_names[IDL_NODE_TYPE(node)]);
    return NULL;
  }
}

int
cbe_get_type_head_alignment(IDL_tree typespec)
{
  switch(IDL_NODE_TYPE(typespec)) {
  case IDLN_TYPE_INTEGER:
    switch(IDL_TYPE_INTEGER(typespec).f_type) {
    case IDL_INTEGER_TYPE_SHORT: return sizeof(CORBA_short);
    case IDL_INTEGER_TYPE_LONG: return sizeof(CORBA_long);
    case IDL_INTEGER_TYPE_LONGLONG: return sizeof(CORBA_long_long);
    }
    break;
  case IDLN_TYPE_FLOAT:
    switch(IDL_TYPE_FLOAT(typespec).f_type) {
    case IDL_FLOAT_TYPE_FLOAT: return sizeof(CORBA_float);
    case IDL_FLOAT_TYPE_DOUBLE: return sizeof(CORBA_double);
    case IDL_FLOAT_TYPE_LONGDOUBLE: return sizeof(CORBA_long_double);
    }
    break;
  case IDLN_TYPE_FIXED:
    return sizeof(CORBA_short);
    break;
  case IDLN_TYPE_CHAR:
    return sizeof(CORBA_char);
    break;
  case IDLN_TYPE_WIDE_CHAR:
    return sizeof(CORBA_wchar);
    break;
  case IDLN_TYPE_STRING:
  case IDLN_INTERFACE:
  case IDLN_TYPE_OBJECT:
  case IDLN_FORWARD_DCL:
  case IDLN_TYPE_TYPECODE:
    return sizeof(CORBA_unsigned_long);
    break;
  case IDLN_TYPE_WIDE_STRING:
  case IDLN_TYPE_BOOLEAN:
  case IDLN_TYPE_OCTET:
  case IDLN_NATIVE:
    return 1;
    break;
  case IDLN_TYPE_ANY:
  case IDLN_TYPE_ENUM:
  case IDLN_TYPE_SEQUENCE:
  case IDLN_EXCEPT_DCL:
    return sizeof(CORBA_unsigned_long);
    break;
  case IDLN_TYPE_ARRAY:
    return cbe_get_type_head_alignment(cbe_get_typespec(IDL_TYPE_ARRAY(typespec).ident));
    break;
  case IDLN_TYPE_STRUCT:
    return cbe_get_type_head_alignment(cbe_get_typespec(IDL_LIST(IDL_TYPE_STRUCT(typespec).member_list).data));
    break;
  case IDLN_TYPE_UNION:
    return cbe_get_type_head_alignment(cbe_get_typespec(IDL_TYPE_UNION(typespec).switch_type_spec));
    break;
  default:
    return cbe_get_type_head_alignment(cbe_get_typespec(typespec));
  }
  return 0;
}

int
cbe_get_type_tail_alignment(IDL_tree typespec)
{
  int minalign;
  IDL_tree curitem;

  switch(IDL_NODE_TYPE(typespec)) {
  case IDLN_TYPE_INTEGER:
    switch(IDL_TYPE_INTEGER(typespec).f_type) {
    case IDL_INTEGER_TYPE_SHORT: return sizeof(CORBA_short);
    case IDL_INTEGER_TYPE_LONG: return sizeof(CORBA_long);
    case IDL_INTEGER_TYPE_LONGLONG: return sizeof(CORBA_long_long);
    }
    break;
  case IDLN_TYPE_FLOAT:
    switch(IDL_TYPE_FLOAT(typespec).f_type) {
    case IDL_FLOAT_TYPE_FLOAT: return sizeof(CORBA_float);
    case IDL_FLOAT_TYPE_DOUBLE: return sizeof(CORBA_double);
    case IDL_FLOAT_TYPE_LONGDOUBLE: return sizeof(CORBA_long_double);
    }
    break;
  case IDLN_TYPE_FIXED:
    return sizeof(CORBA_short);
    break;
  case IDLN_TYPE_CHAR:
    return sizeof(CORBA_char);
    break;
  case IDLN_TYPE_WIDE_CHAR:
    return sizeof(CORBA_wchar);
    break;
  case IDLN_TYPE_STRING:
  case IDLN_TYPE_WIDE_STRING:
  case IDLN_TYPE_BOOLEAN:
  case IDLN_TYPE_OCTET:
  case IDLN_INTERFACE:
  case IDLN_TYPE_OBJECT:
  case IDLN_FORWARD_DCL:
  case IDLN_TYPE_ANY:
  case IDLN_NATIVE:
  case IDLN_TYPE_TYPECODE:
    return 1;
    break;
  case IDLN_TYPE_ENUM:
    return sizeof(CORBA_unsigned_long);
    break;
  case IDLN_TYPE_SEQUENCE:
    return cbe_get_type_tail_alignment(cbe_get_typespec(IDL_TYPE_SEQUENCE(typespec).simple_type_spec));
    break;
  case IDLN_TYPE_ARRAY:
    return cbe_get_type_tail_alignment(cbe_get_typespec(IDL_TYPE_ARRAY(typespec).ident));
    break;
  case IDLN_EXCEPT_DCL:
  case IDLN_TYPE_STRUCT:
    curitem = IDL_list_nth(IDL_TYPE_STRUCT(typespec).member_list,
			   IDL_list_length(IDL_TYPE_STRUCT(typespec).member_list) - 1);
    return cbe_get_type_tail_alignment(cbe_get_typespec(IDL_LIST(curitem).data));
    break;
  case IDLN_TYPE_UNION:
    for(minalign = 8 /* FIXME */, curitem = IDL_TYPE_UNION(typespec).switch_body;
	curitem && minalign > 1;
	curitem = IDL_LIST(curitem).next) {
      IDL_tree uts;

      uts = IDL_CASE_STMT(IDL_LIST(curitem).data).element_spec;

      minalign = MIN(cbe_get_type_tail_alignment(cbe_get_typespec(uts)), minalign);
    }
    return minalign;
    break;
  default:
    return cbe_get_type_tail_alignment(cbe_get_typespec(typespec));
  }
  return 0;
}

static void
IDL_tree_traverse_helper(IDL_tree p, GFunc f,
			 gconstpointer func_data,
			 GHashTable *visited_nodes)
{
	IDL_tree curitem;

	if(g_hash_table_lookup(visited_nodes, p))
		return;

	g_hash_table_insert(visited_nodes, p, ((gpointer)1));

	for(curitem = IDL_INTERFACE(p).inheritance_spec; curitem;
	    curitem = IDL_LIST(curitem).next) {
		IDL_tree_traverse_helper(IDL_get_parent_node(IDL_LIST(curitem).data, IDLN_INTERFACE, NULL), f, func_data, visited_nodes);
	}

	f(p, (gpointer)func_data);
}

void
IDL_tree_traverse_parents(IDL_tree p,
			  GFunc f,
			  gconstpointer func_data)
{
	GHashTable *visited_nodes = g_hash_table_new(NULL, g_direct_equal);

	if(!(p && f))
		return;

	if(IDL_NODE_TYPE(p) != IDLN_INTERFACE)
		p = IDL_get_parent_node(p, IDLN_INTERFACE, NULL);

	if(!p)
		return;

	IDL_tree_traverse_helper(p, f, func_data, visited_nodes);

	g_hash_table_destroy(visited_nodes);
}

IDL_ParamRole
cbe_attr_to_paramrole(enum IDL_param_attr attr)
{
  switch(attr) {
  case IDL_PARAM_IN:
    return DATA_IN;
  case IDL_PARAM_OUT:
    return DATA_OUT;
  case IDL_PARAM_INOUT:
    return DATA_INOUT;
  default:
    g_warning("Unknown IDL_param_attr %d", attr);
    return -1;
  }
}

gboolean
cbe_type_is_endian_sensitive(IDL_tree typespec)
{
  IDL_tree curitem;
  gboolean retval;

  switch(IDL_NODE_TYPE(typespec)) {
  case IDLN_TYPE_CHAR:
  case IDLN_TYPE_BOOLEAN:
  case IDLN_TYPE_OCTET:
    return FALSE;
  case IDLN_TYPE_ARRAY:
    curitem = IDL_get_parent_node(typespec, IDLN_ANY, NULL);
    curitem = IDL_get_parent_node(curitem, IDLN_ANY, NULL);
    return cbe_type_is_endian_sensitive(IDL_MEMBER(curitem).type_spec);
  case IDLN_TYPE_STRUCT:
    retval = TRUE;
    for(curitem = IDL_TYPE_STRUCT(typespec).member_list; curitem;
	curitem = IDL_LIST(curitem).next) {
      retval = retval && cbe_type_is_endian_sensitive(IDL_LIST(curitem).data);
    }
    return retval;
    break;
  default:
    return TRUE;
  }
}
