/*
 * nasd_rpcgen_output_dceidl.c
 *
 * Output DCE .idl files from NASD rpc file spec
 *
 * 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 <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "nasd_rpcgen.h"

void
output_dce_idl_header(
  FILE  *f,
  char  *filename)
{
  int needcomma;

  needcomma = 0;

  output_cpp_comment_header(f, filename);

#define DOCOMMA() if (needcomma) { fprintf(f, ",\n"); needcomma = 0; } else { fprintf(f, "\n"); }

  if (global_vals.uuid.str
    || global_vals.dce_endpoint.str
    || global_vals.version.str)
  {
    fprintf(f, "[");
    if (global_vals.uuid.str) {
      DOCOMMA();
      fprintf(f, "uuid(%s)", global_vals.uuid.str);
      needcomma = 1;
    }
    if (global_vals.dce_endpoint.str) {
      DOCOMMA();
      fprintf(f, "endpoint(\"%s\")", global_vals.dce_endpoint.str);
      needcomma = 1;
    }
    if (global_vals.version.str) {
      DOCOMMA();
      fprintf(f, "version(%s)", global_vals.version.str);
      needcomma = 1;
    }
    fprintf(f, "\n]\n");
  }

  fprintf(f, "interface %s\n", global_spec.out_if_name);
  fprintf(f, "{\n");
  fprintf(f, "\timport \"%snasd_rpcgen_dceidl.idl\";\n",
    global_spec.base_import_path);
  fprintf(f, "\n");
}

void
output_dce_idl_trailer(
   FILE  *f)
{
  fprintf(f, "\n");
  fprintf(f, "}\n");
}

void
output_dce_idl_type(
  FILE                *f,
  nasd_rpcgen_type_t  *type)
{
  int i, printdim;

  if (type->out_flags&NASD_RPCGEN_OUT_FLAGS_WRITTEN)
    return;

  switch(type->type_is) {
    case NASD_RPCGEN_TYPE_DERIVED:
      if (type->derived_from->type_is == NASD_RPCGEN_TYPE_STRUCT) {
        fprintf(f, "typedef struct %s %s;\n",
          type->derived_from->dceidl_name,
          type->dceidl_name);
      }
      else {
        fprintf(f, "typedef %s %s;\n",
          type->derived_from->dceidl_name,
          type->dceidl_name);
      }
      break;
    case NASD_RPCGEN_TYPE_ARRAY:
      printdim = 1;
      if (type->derived_from->type_is == NASD_RPCGEN_TYPE_STRUCT) {
        fprintf(f, "typedef struct %s %s",
          type->derived_from->dceidl_name,
          type->dceidl_name);
      }
      else {
        fprintf(f, "typedef %s %s",
          type->derived_from->dceidl_name,
          type->dceidl_name);
      }
      if (printdim) {
        for(i=0;i<type->ndim;i++)
          fprintf(f, "[%d]", type->nelements[i]);
        fprintf(f, ";\n");
      }
      break;
    case NASD_RPCGEN_TYPE_PIPE:
      if (type->derived_from->type_is == NASD_RPCGEN_TYPE_STRUCT) {
        fprintf(f, "typedef pipe struct %s %s;\n",
          type->derived_from->dceidl_name,
          type->dceidl_name);
      }
      else {
        fprintf(f, "typedef pipe %s %s;\n",
          type->derived_from->dceidl_name,
          type->dceidl_name);
      }
      break;
    default:
      NASD_PANIC();
  }
}

void
output_dce_idl_import(
  FILE                  *f,
  nasd_rpcgen_import_t  *import)
{
  fprintf(f, "import \"%s.idl\";\n",
    import->filename);
}

void
output_dce_idl_const(
  FILE                 *f,
  nasd_rpcgen_const_t  *cnst)
{
  switch(cnst->base_type->type_is) {
    case NASD_RPCGEN_TYPE_INT8:
      fprintf(f, "const %s %s =\'%c\';\n",
        cnst->type->dceidl_name, cnst->name, cnst->val.val_int8);
      break;
    case NASD_RPCGEN_TYPE_UINT8:
      fprintf(f, "const %s %s = 0x%x;\n",
        cnst->type->dceidl_name, cnst->name, cnst->val.val_uint8&0xff);
      break;
    case NASD_RPCGEN_TYPE_INT16:
      fprintf(f, "const %s %s = %hd;\n",
        cnst->type->dceidl_name, cnst->name, cnst->val.val_int16);
      break;
    case NASD_RPCGEN_TYPE_UINT16:
      fprintf(f, "const %s %s = %hu;\n",
        cnst->type->dceidl_name, cnst->name, cnst->val.val_uint16);
      break;
    case NASD_RPCGEN_TYPE_INT32:
      fprintf(f, "const %s %s = %d;\n",
        cnst->type->dceidl_name, cnst->name, cnst->val.val_int32);
      break;
    case NASD_RPCGEN_TYPE_UINT32:
      fprintf(f, "const %s %s = %u;\n",
        cnst->type->dceidl_name, cnst->name, cnst->val.val_uint32);
      break;
    case NASD_RPCGEN_TYPE_INT64:
      fprintf(f, "const %s %s = %" NASD_64s_FMT ";\n",
        cnst->type->dceidl_name, cnst->name, cnst->val.val_int64);
      break;
    case NASD_RPCGEN_TYPE_UINT64:
      fprintf(f, "const %s %s = %" NASD_64u_FMT ";\n",
        cnst->type->dceidl_name, cnst->name, cnst->val.val_uint64);
      break;
    case NASD_RPCGEN_TYPE_STRING:
      fprintf(f, "const char* %s = \"%s\";\n", cnst->name, cnst->val.val_str);
      break;
    default:
      NASD_PANIC();
  }
}

void
output_dce_idl_struct(
  FILE                *f,
  nasd_rpcgen_type_t  *st)
{
  nasd_rpcgen_type_t *elem;
  int i;

  static int structnum = 1;

  fprintf(f, "typedef struct %s {\n", st->dceidl_name);

  for(elem=st->struct_list;elem;elem=elem->struct_next) {
    if (global_spec.annotate) {
      fprintf(f, "/* %s:%d */\n", elem->decl.define_file,
        elem->decl.define_line);
    }
    switch(elem->type_is) {
      case NASD_RPCGEN_TYPE_DERIVED:
        if (elem->derived_from->type_is == NASD_RPCGEN_TYPE_STRUCT) {
          fprintf(f, "  struct %s %s;\n", elem->derived_from->dceidl_name,
            elem->name);
        }
        else {
          fprintf(f, "  %s %s;\n", elem->derived_from->dceidl_name,
            elem->name);
        }
        break;
      case NASD_RPCGEN_TYPE_ARRAY:
        if (elem->derived_from->type_is == NASD_RPCGEN_TYPE_STRUCT) {
          fprintf(f, "  struct %s %s", elem->derived_from->dceidl_name,
            elem->name);
        }
        else {
          fprintf(f, "  %s %s", elem->derived_from->dceidl_name,
            elem->name);
        }
        for(i=0;i<elem->ndim;i++)
          fprintf(f, "[%d]", elem->nelements[i]);
        fprintf(f, ";\n");
        break;
      default:
        NASD_PANIC();
    }
  }
  if (st->first_typedef) {
    fprintf(f, "} %s;\n", st->first_typedef->name);
    st->first_typedef->out_flags |= NASD_RPCGEN_OUT_FLAGS_WRITTEN;
  }
  else {
    /*
     * This is counted carefully, and when global_spec.out_if_name is a valid
     * interface name (17 chars or less), this will be 31 chars
     * or less- a valid type name.
     */
    fprintf(f, "} %s_wehatedce%04d;\n", global_spec.out_if_name, structnum);
    structnum++;
  }
}

void
output_dce_idl_call(
  FILE                *f,
  nasd_rpcgen_call_t  *call)
{
  nasd_rpcgen_type_t *elem, *bt;
  char *an, *callname;

  an = NULL;

  if (global_spec.std_prefix) {
    str_append_prefix(global_spec.std_prefix, "dceidl_", &callname);
  }
  else {
    strclone(call->name, &callname);
  }

  fprintf(f, "void %s\n", callname);
  fprintf(f, "(\n");
  fprintf(f, "  [in]   handle_t  handle,\n");

  for(elem=call->call_list;elem;elem=elem->call_next) {
    if (global_spec.annotate) {
      fprintf(f, "/* %s:%d */\n", elem->decl.define_file,
        elem->decl.define_line);
    }
    switch(elem->call_dir) {
      case NASD_RPCGEN_DIR_IN:
        fprintf(f, "  [in]   %s %s,\n",
          elem->derived_from->dceidl_name, elem->name);
        break;
      case NASD_RPCGEN_DIR_OUT:
        bt = basic_type_of_extended(elem);
        if ((bt == NULL) || (bt->type_is == NASD_RPCGEN_TYPE_ARRAY)
           || (bt->type_is == NASD_RPCGEN_TYPE_PIPE))
        {
          fprintf(f, "  [out]  %s %s,\n",
            elem->derived_from->dceidl_name, elem->name);
        }
        else {
          fprintf(f, "  [out]  %s *%s,\n",
            elem->derived_from->dceidl_name, elem->name);
        }
        break;
      default:
        NASD_PANIC();
    }
  }

  if (an)
    free(an);

  free(callname);

  fprintf(f, "  [out]  error_status_t  *op_status\n");
  fprintf(f, ");\n");
}

void
output_dce_idl_uuid(
  FILE               *f,
  nasd_rpcgen_val_t  *strval)
{
  /* handled earlier */
  return;
}

void
output_dce_idl_dce_endpoint(
  FILE               *f,
  nasd_rpcgen_val_t  *strval)
{
  /* handled earlier */
  return;
}

void
output_dce_idl_version(
  FILE               *f,
  nasd_rpcgen_val_t  *strval)
{
  /* handled earlier */
  return;
}

void
output_dce_idl_marshall(
  FILE                    *f,
  nasd_rpcgen_marshall_t  *marshall)
{
}

void
output_dce_idl(
  char  *filename)
{
  nasd_rpcgen_decl_t *decl;
  FILE *f;

  if (filename[0] == '.') {
    fprintf(oerr, "ERROR: invalid filename \"%s\"\n", filename);
    output_error();
    return;
  }

  f = fopen(filename, "w");
  if (f == NULL) {
    fprintf(oerr, "ERROR: cannot open %s for output\n", filename);
    output_error();
    return;
  }

  output_dce_idl_header(f, filename);

  for(decl=global_decls.gnext;decl!=&global_decls;decl=decl->gnext) {
    if (global_spec.annotate) {
      fprintf(f, "/* %s:%d (internal %d) */\n", decl->define_file,
        decl->define_line, decl->decl_type);
    }
    switch(decl->decl_type) {
      case NASD_RPCGEN_DECL_TYPE:
        output_dce_idl_type(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_IMPORT:
        output_dce_idl_import(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_CONST:
        output_dce_idl_const(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_STRUCT:
        output_dce_idl_struct(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_CALL:
        output_dce_idl_call(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_UUID:
        output_dce_idl_uuid(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_DCE_ENDPOINT:
        output_dce_idl_dce_endpoint(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_VERSION:
        output_dce_idl_version(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_MARSHALL:
        output_dce_idl_marshall(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_BASEID:
        break;
      default:
        NASD_PANIC();
    }
  }

  output_dce_idl_trailer(f);

  fclose(f);
  printf("Wrote %s as DCE IDL\n", filename);
}

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