/*
 * nasd_rpcgen_output_c.c
 *
 * Output C code 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"

#define MAX_PARAM 32

void
output_c_header_header(
  FILE  *f,
  char  *filename)
{
  output_cpp_comment_header(f, filename);

  fprintf(f, "#ifndef _%s_%s_H_\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
  fprintf(f, "#define _%s_%s_H_\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
  fprintf(f, "\n");
  fprintf(f, "#include <%snasd_rpcgen_c.h>\n",
    global_spec.base_import_path);
  fprintf(f, "\n");
}

void
output_c_header_trailer(
   FILE  *f)
{
  fprintf(f, "\n");
  fprintf(f, "#endif /* !_%s_%s_H_ */\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
}

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

  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->c_name,
          type->c_name);
      }
      else {
        fprintf(f, "typedef %s %s;\n",
          type->derived_from->c_name,
          type->c_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->c_name,
          type->c_name);
      }
      else {
        fprintf(f, "typedef %s %s",
          type->derived_from->c_name,
          type->c_name);
      }
      if (printdim) {
        for(i=0;i<type->ndim;i++)
          fprintf(f, "[%d]", type->nelements[i]);
        fprintf(f, ";\n");
      }
      break;
#if 0
    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->c_name,
          type->c_name);
      }
      else {
        fprintf(f, "typedef pipe %s %s;\n",
          type->derived_from->c_name,
          type->c_name);
      }
      break;
#else
    case NASD_RPCGEN_TYPE_PIPE:
      fprintf(f, "/* !!! pipes not supported yet !!! */\n");
      fprintf(f, "typedef struct %s_s {\n", type->c_name);
      fprintf(f, "  int  placeholder;\n");
      fprintf(f, "} %s;\n", type->c_name);
      break;
#endif
    default:
      NASD_PANIC();
  }
}

void
output_c_header_import(
  FILE                  *f,
  nasd_rpcgen_import_t  *import)
{
  fprintf(f, "#include <%s.h>\n",
    import->filename);
}

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

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

  fprintf(f, "struct %s {\n", st->c_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->c_name,
            elem->name);
        }
        else {
          fprintf(f, "  %s %s;\n", elem->derived_from->c_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->c_name,
            elem->name);
        }
        else {
          fprintf(f, "  %s %s", elem->derived_from->c_name,
            elem->name);
        }
        for(i=0;i<elem->ndim;i++)
          fprintf(f, "[%d]", elem->nelements[i]);
        fprintf(f, ";\n");
        break;
      default:
        NASD_PANIC();
    }
  }

  fprintf(f, "};\n");
}

void
output_c_header_call(
  FILE                *f,
  nasd_rpcgen_call_t  *call)
{
  nasd_rpcgen_type_t *elem;
  char *an, *callname;
  int l, nl;

  callname = call->name;
  an = NULL;

  fprintf(f, "\n");

  if (global_spec.std_prefix) {
    l = strlen(global_spec.std_prefix);
    if (!strncmp(call->name, global_spec.std_prefix, l)) {
      nl = strlen(call->name);
      an = callname = malloc(nl+8);
      if (callname == NULL) {
        fprintf(stderr, "ERROR: out of memory\n");
        fflush(stderr);
        exit(1);
      }
      sprintf(callname, "%s%s%s",
        global_spec.std_prefix,
        "srpc_",
        &call->name[l]);
    }
  }

  fprintf(f, "void %s\n", callname);
  fprintf(f, "(\n");
  fprintf(f, "  nasd_srpc_handle_t handle");

  for(elem=call->call_list;elem;elem=elem->call_next) {
    if (global_spec.annotate) {
      fprintf(f, ",\n  /* %s:%d */", elem->decl.define_file,
        elem->decl.define_line);
    }
    if (elem->derived_from->type_is == NASD_RPCGEN_TYPE_ARRAY) {
      fprintf(f, ",\n  %s %s",
        elem->derived_from->c_name, elem->name);
    }
    else {
      fprintf(f, ",\n  %s *%s",
        elem->derived_from->c_name, elem->name);
    }
  }

  if (an)
    free(an);

  fprintf(f, ",\n  nasd_srpc_status_t *op_status\n");
  fprintf(f, ");\n");
}

void
output_c_header_uuid(
  FILE               *f,
  nasd_rpcgen_val_t  *strval)
{
  fprintf(f, "#define %s_uuid \"%s\"\n", global_spec.out_if_name, strval->str);
}

void
output_c_header_version(
  FILE               *f,
  nasd_rpcgen_val_t  *strval)
{
  fprintf(f, "#define %s_version \"%s\"\n", global_spec.out_if_name, strval->str);
}

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

void
output_c_header_baseid(
  FILE               *f,
  nasd_rpcgen_val_t  *val)
{
  fprintf(f, "#define %s_baseid %u\n", global_spec.out_if_name, val->u32);
}

void
output_c_header(
  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_c_header_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_c_header_type(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_IMPORT:
        output_c_header_import(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_CONST:
        output_c_header_const(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_STRUCT:
        output_c_header_struct(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_CALL:
        output_c_header_call(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_UUID:
        output_c_header_uuid(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_DCE_ENDPOINT:
        /* we do NOTHING with this */
        break;
      case NASD_RPCGEN_DECL_VERSION:
        output_c_header_version(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_MARSHALL:
        output_c_header_marshall(f, decl->decl);
        break;
      case NASD_RPCGEN_DECL_BASEID:
        output_c_header_baseid(f, decl->decl);
        break;
      default:
        NASD_PANIC();
    }
  }

  output_c_header_trailer(f);

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

void
output_srpc_glob_header_header(
  FILE  *f,
  char  *filename)
{
  output_cpp_comment_header(f, filename);

  fprintf(f, "#ifndef _%s_GLOB_HEADER_H_\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""));
  fprintf(f, "#define _%s_GLOB_HEADER_H_\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""));
  fprintf(f, "\n");
  fprintf(f, "#include <%snasd_rpcgen_c.h>\n",
    global_spec.base_import_path);
  fprintf(f, "\n");
}

void
output_srpc_glob_header_trailer(
   FILE  *f)
{
  fprintf(f, "\n");
  fprintf(f, "#endif /* !_%s_GLOB_HEADER_H_ */\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""));
}

void
output_srpc_glob_header(
  char  *filename)
{
  int max_call_args, max_call_res, max_call_args_size, max_call_res_size,
    nargs, nres, sz;
  nasd_rpcgen_type_t *type, *base_type;
  nasd_rpcgen_decl_t *decl;
  nasd_rpcgen_call_t *call;
  FILE *f;

  max_call_args = 1;
  max_call_res = 1;
  max_call_args_size = 8;
  max_call_res_size = 8;

  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_srpc_glob_header_header(f, filename);

  for(decl=global_decls.gnext;decl!=&global_decls;decl=decl->gnext) {
    switch(decl->decl_type) {
      case NASD_RPCGEN_DECL_TYPE:
        break;
      case NASD_RPCGEN_DECL_IMPORT:
        break;
      case NASD_RPCGEN_DECL_CONST:
        break;
      case NASD_RPCGEN_DECL_STRUCT:
        break;
      case NASD_RPCGEN_DECL_CALL:
        call = decl->decl;
        nargs = 0;
        nres = 0;
        for(type=call->call_list;type;type=type->call_next) {
          base_type = basic_type_of_extended(type);
          if (base_type->type_is != NASD_RPCGEN_TYPE_PIPE) {
            sz = base_type->size;
            if (type->call_dir == NASD_RPCGEN_DIR_IN) {
              nargs++;
              max_call_args_size = NASD_MAX(max_call_args_size, sz);
            }
            else {
              NASD_ASSERT(type->call_dir == NASD_RPCGEN_DIR_OUT);
              nres++;
              max_call_res_size = NASD_MAX(max_call_res_size, sz);
            }
          }
        }
        max_call_args = NASD_MAX(max_call_args, nargs);
        max_call_res = NASD_MAX(max_call_res, nres);
        break;
      case NASD_RPCGEN_DECL_UUID:
        break;
      case NASD_RPCGEN_DECL_DCE_ENDPOINT:
        break;
      case NASD_RPCGEN_DECL_VERSION:
        break;
      case NASD_RPCGEN_DECL_MARSHALL:
        break;
      case NASD_RPCGEN_DECL_BASEID:
        break;
      default:
        NASD_PANIC();
    }
  }

  fprintf(f, "#define NASD_SRPC_MAX_NARGS %d\n", max_call_args);
  fprintf(f, "#define NASD_SRPC_MAX_NRES %d\n", max_call_res);
  fprintf(f, "#define NASD_SRPC_MAX_ARGSIZE %d\n", max_call_args_size);
  fprintf(f, "#define NASD_SRPC_MAX_RESSIZE %d\n", max_call_res_size);

  output_srpc_glob_header_trailer(f);

  fclose(f);
  printf("Wrote %s as SRPC glob header\n", filename);
}

void
output_c_server_c_header(
  FILE  *f,
  char  *filename)
{
  nasd_rpcgen_filename_t *fn;

  output_cpp_comment_header(f, filename);

  fprintf(f, "#include <%snasd_rpcgen_c.h>\n",
    global_spec.base_import_path);
  fprintf(f, "#include <%snasd_srpc_types.h>\n",
    global_spec.base_import_path);
  fprintf(f, "#include <%snasd_srpc.h>\n",
    global_spec.base_import_path);
  fprintf(f, "\n");

  for(fn=global_spec.usc_import_list;fn;fn=fn->next) {
    fprintf(f, "#include <%s>\n", fn->filename);
  }
  if (global_spec.usc_import_list) {
    fprintf(f, "\n");
  }
}

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

void
output_c_server_c(
  char  *filename)
{
  int paramoff[MAX_PARAM], nparam, paramisout[MAX_PARAM], npipes, has_inpipe;
  int nargs, nres, sz, total_arg_sz, total_res_sz, i, l, first_pipe_in;
  nasd_rpcgen_type_t *type, *base_type;
  nasd_rpcgen_uint32 pipeword;
  nasd_rpcgen_uint32 curid;
  nasd_rpcgen_decl_t *decl;
  nasd_rpcgen_call_t *call;
  char *callname;
  FILE *f;

  if (global_vals.baseid.decl.define_file == NULL) {
    fprintf(oerr, "ERROR: no baseid specified\n");
    output_error();
    return;
  }
  curid = global_vals.baseid.u32;

  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_c_server_c_header(f, filename);

  has_inpipe = 0;
  for(decl=global_decls.gnext;decl!=&global_decls;decl=decl->gnext) {
    if (decl->decl_type == NASD_RPCGEN_DECL_CALL) {
      call = decl->decl;
      for(type=call->call_list;type;type=type->call_next) {
        base_type = basic_type_of_extended(type);
        if (base_type->type_is == NASD_RPCGEN_TYPE_PIPE) {
          if (type->call_dir == NASD_RPCGEN_DIR_IN) {
            has_inpipe = 1;
            break;
          }
        }
      }
    }
  }

  fprintf(f, "nasd_status_t\n");
  fprintf(f, "%s%s_demux(\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
  fprintf(f, "  nasd_srpc_listener_t     *listener,\n");
  fprintf(f, "  nasd_srpc_conn_t         *conn,\n");
  fprintf(f, "  nasd_srpc_server_call_t  *call)\n");
  fprintf(f, "{\n");
  fprintf(f, "  nasd_srpc_status_t src;\n");
  if (has_inpipe) {
    fprintf(f, "  nasd_status_t rc;\n");
  }
  fprintf(f, "\n");
  fprintf(f, "  switch(call->req.rpcid) {\n");

  for(decl=global_decls.gnext;decl!=&global_decls;decl=decl->gnext) {
    switch(decl->decl_type) {
      case NASD_RPCGEN_DECL_TYPE:
        break;
      case NASD_RPCGEN_DECL_IMPORT:
        break;
      case NASD_RPCGEN_DECL_CONST:
        break;
      case NASD_RPCGEN_DECL_STRUCT:
        break;
      case NASD_RPCGEN_DECL_CALL:
        fprintf(f, "    case %u:\n", curid);
        curid++;
        call = decl->decl;
        nargs = 0;
        nres = 0;
        nparam = 0;
        pipeword = 0;
        total_arg_sz = 0;
        total_res_sz = 0;
        first_pipe_in = 0;
        npipes = 0;
        l = strlen(call->name);
        callname = malloc(l+1+strlen("_server"));
        if (callname == NULL) {
          fprintf(stderr, "ERROR: out of memory\n");
          fflush(stderr);
          exit(1);
        }
        sprintf(callname, "%s_server", call->name);
        for(type=call->call_list;type;type=type->call_next) {
          base_type = basic_type_of_extended(type);
          if (base_type->type_is != NASD_RPCGEN_TYPE_PIPE) {
            sz = base_type->size;
            if (((base_type->type_is != NASD_RPCGEN_TYPE_ARRAY)
                || (base_type->derived_from->size != 1))
              && (base_type->is_typem == 0))
            {
              fprintf(oerr, "ERROR: automarshalling not yet supported "
                "(declaration at %s:%d)\n",
                type->decl.define_file, type->decl.define_line);
              output_error();
              return;
            }
            if (type->call_dir == NASD_RPCGEN_DIR_IN) {
              paramoff[nparam] = total_arg_sz;
              paramisout[nparam] = 1;
              nparam++;
              nargs++;
              if ((nargs > MAX_PARAM) || (nres > MAX_PARAM)) {
                NASD_PANIC();
              }
              total_arg_sz += sz;
            }
            else {
              NASD_ASSERT(type->call_dir == NASD_RPCGEN_DIR_OUT);
              paramoff[nparam] = total_res_sz;
              paramisout[nparam] = 0;
              nparam++;
              nres++;
              if ((nargs > MAX_PARAM) || (nres > MAX_PARAM)) {
                NASD_PANIC();
              }
              total_res_sz += sz;
            }
          }
          else {
            /* pipe */
            if (pipeword & 0xC0000000) {
              fprintf(oerr, "ERROR: too many pipes for call (%s:%d)\n",
                call->decl.define_file, call->decl.define_line);
              output_error();
              return;
            }
            pipeword <<= 2;
            fprintf(f, "      call->pipes[%d].pipenum = %d;\n",
              npipes, npipes);
            fprintf(f, "      call->pipes[%d].listener = listener;\n", npipes);
            fprintf(f, "      call->pipes[%d].conn = conn;\n", npipes);
            fprintf(f, "      call->pipes[%d].call = call;\n", npipes);
            fprintf(f, "      call->pipes[%d].state = NASD_SRPC_PIPESTATE_PEND;\n",
              npipes);
            if (type->call_dir == NASD_RPCGEN_DIR_IN) {
              pipeword |= NASD_RPCGEN_SRPC_OPSTAT_PIPE_IN;
              first_pipe_in = npipes+1;
              fprintf(f, "      call->pipes[%d].dir = NASD_SRPC_OPSTAT_PIPE_IN;\n",
                npipes);
            }
            else {
              NASD_ASSERT(type->call_dir == NASD_RPCGEN_DIR_OUT);
              pipeword |= NASD_RPCGEN_SRPC_OPSTAT_PIPE_OUT;
              fprintf(f, "      call->pipes[%d].dir = NASD_SRPC_OPSTAT_PIPE_OUT;\n",
                npipes);
            }
            npipes++;
          }
        }
        fprintf(f, "      if (%d > NASD_SRPC_MAX_RES_BYTES) {\n", total_res_sz);
        fprintf(f, "        call->reslen = 0;\n");
        fprintf(f, "        call->result = NASD_SRPC_S_GENERR;\n");
        fprintf(f, "        return(NASD_SUCCESS);\n");
        fprintf(f, "      }\n");
        fprintf(f, "      if (call->arglen != %d) {\n", total_arg_sz);
        fprintf(f, "        call->reslen = 0;\n");
        fprintf(f, "        call->result = NASD_SRPC_S_BAD_OPLEN;\n");
        fprintf(f, "        return(NASD_SUCCESS);\n");
        fprintf(f, "      }\n");
        fprintf(f, "      if (call->call_header.opstat != %u) {\n", pipeword);
        fprintf(f, "        call->reslen = 0;\n");
        fprintf(f, "        call->result = NASD_SRPC_S_BAD_PIPEWORD;\n");
        fprintf(f, "        return(NASD_SUCCESS);\n");
        fprintf(f, "      }\n");
        fprintf(f, "      call->npipes = %d;\n", npipes);
        if (first_pipe_in) {
          fprintf(f, "      rc = nasd_srpc_call_enable_pipes(listener, conn, call);\n");
          fprintf(f, "      if (rc) {\n");
          fprintf(f, "        call->reslen = 0;\n");
          fprintf(f, "        call->result = NASD_SRPC_S_ENABFAIL;\n");
          fprintf(f, "        return(NASD_SUCCESS);\n");
          fprintf(f, "      }\n");
        }
        fprintf(f, "      src = %s(\n", callname);
        fprintf(f, "        listener,\n");
        fprintf(f, "        conn,\n");
        fprintf(f, "        call,\n");
        for(i=0;i<nparam;i++) {
          fprintf(f, "        (nasd_otw_base_t *)&((nasd_byte_t *)call->%smem)[%d]%s\n",
            (paramisout[i] ? "arg" : "res"),
            paramoff[i],
            (((i == (nparam-1)) && (npipes == 0)) ? ");" : ","));
        }
        for(i=0;i<npipes;i++) {
          fprintf(f, "        &call->pipes[%d]%s\n",
            i, ((i == (npipes-1)) ? ");" : ","));
        }
        fprintf(f, "      call->reslen = %d;\n", total_res_sz);
        fprintf(f, "      call->result = src;\n");
        fprintf(f, "      return(NASD_SUCCESS);\n");
        fprintf(f, "      /* NOTREACHED */\n");
        fprintf(f, "      break;\n");
        free(callname);
        break;
      case NASD_RPCGEN_DECL_UUID:
        break;
      case NASD_RPCGEN_DECL_DCE_ENDPOINT:
        break;
      case NASD_RPCGEN_DECL_VERSION:
        break;
      case NASD_RPCGEN_DECL_MARSHALL:
        break;
      case NASD_RPCGEN_DECL_BASEID:
        break;
      default:
        NASD_PANIC();
    }
  }

  fprintf(f, "    default:\n");
  fprintf(f, "      call->reslen = 0;\n");
  fprintf(f, "      call->result = NASD_SRPC_S_BAD_CALLID;\n");
  fprintf(f, "      return(NASD_SUCCESS);\n");
  fprintf(f, "      /* NOTREACHED */\n");
  fprintf(f, "      break;\n");
  fprintf(f, "  }\n");
  fprintf(f, "  /* NOTREACHED */\n");
  fprintf(f, "  NASD_PANIC();\n");
  fprintf(f, "  return(NASD_FAIL);\n");
  fprintf(f, "}\n");

  output_c_server_c_trailer(f);

  fclose(f);
  printf("Wrote %s as SRPC server C file\n", filename);
}

void
output_c_client_c_header(
  FILE  *f,
  char  *filename)
{
  nasd_rpcgen_filename_t *fn;

  output_cpp_comment_header(f, filename);

  fprintf(f, "#include <%snasd_rpcgen_c.h>\n",
    global_spec.base_import_path);
  fprintf(f, "#include <%snasd_srpc_types.h>\n",
    global_spec.base_import_path);
  fprintf(f, "#include <%snasd_srpc.h>\n",
    global_spec.base_import_path);
  fprintf(f, "\n");

  for(fn=global_spec.usc_import_list;fn;fn=fn->next) {
    fprintf(f, "#include <%s>\n", fn->filename);
  }
  if (global_spec.usc_import_list) {
    fprintf(f, "\n");
  }
}

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

void
output_c_client_c(
  char  *filename)
{
  nasd_rpcgen_type_t *type, *base_type, *typearr[MAX_PARAM];
  int nargs, nres, sz, total_arg_sz, total_res_sz, i, j, l;
  int paramoff[MAX_PARAM], nparam, paramisout[MAX_PARAM];
  int has_pipe, pn, need_pe, restargstate, nmemvecs;
  nasd_rpcgen_uint32 pipeword;
  nasd_rpcgen_uint32 curid;
  nasd_rpcgen_decl_t *decl;
  nasd_rpcgen_call_t *call;
  char *callname;
  FILE *f;

  if (global_vals.baseid.decl.define_file == NULL) {
    fprintf(oerr, "ERROR: no baseid specified\n");
    output_error();
    return;
  }
  curid = global_vals.baseid.u32;

  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_c_client_c_header(f, filename);

  for(decl=global_decls.gnext;decl!=&global_decls;decl=decl->gnext) {
    switch(decl->decl_type) {
      case NASD_RPCGEN_DECL_TYPE:
        break;
      case NASD_RPCGEN_DECL_IMPORT:
        break;
      case NASD_RPCGEN_DECL_CONST:
        break;
      case NASD_RPCGEN_DECL_STRUCT:
        break;
      case NASD_RPCGEN_DECL_CALL:
        call = decl->decl;
        nargs = 0;
        nres = 0;
        nparam = 0;
        pipeword = 0;
        total_arg_sz = 0;
        total_res_sz = 0;
        restargstate = 2;
        need_pe = 0;
        l = strlen(call->name);
        callname = malloc(l+1+strlen("_client"));
        if (callname == NULL) {
          fprintf(stderr, "ERROR: out of memory\n");
          fflush(stderr);
          exit(1);
        }
        has_pipe = 0;
        for(type=call->call_list;type;type=type->call_next) {
          base_type = basic_type_of_extended(type);
          if (base_type->type_is == NASD_RPCGEN_TYPE_PIPE) {
            has_pipe = 1;
            break;
          }
        }
        sprintf(callname, "%s_client", call->name);
        fprintf(f, "nasd_srpc_status_t\n");
        fprintf(f, "%s(\n", callname);
        fprintf(f, "  nasd_srpc_handle_t   handle");
        for(type=call->call_list;type;type=type->call_next) {
          base_type = basic_type_of_extended(type);
          if (base_type->type_is != NASD_RPCGEN_TYPE_PIPE) {
            sz = base_type->size;
            if (((base_type->type_is != NASD_RPCGEN_TYPE_ARRAY)
                || (base_type->derived_from->size != 1))
              && (base_type->is_typem == 0))
            {
              fprintf(oerr, "ERROR: automarshalling not yet supported "
                "(declaration at %s:%d)\n",
                type->decl.define_file, type->decl.define_line);
              output_error();
              return;
            }
            fprintf(f, ",\n  %s %s", type->derived_from->name, type->name);
            if (type->call_dir == NASD_RPCGEN_DIR_IN) {
              paramoff[nparam] = total_arg_sz;
              paramisout[nparam] = 0;
              typearr[nparam] = type;
              nparam++;
              nargs++;
              if ((nargs > MAX_PARAM) || (nres > MAX_PARAM)) {
                NASD_PANIC();
              }
              total_arg_sz += sz;
            }
            else {
              NASD_ASSERT(type->call_dir == NASD_RPCGEN_DIR_OUT);
              paramoff[nparam] = total_res_sz;
              paramisout[nparam] = 1;
              typearr[nparam] = type;
              nparam++;
              nres++;
              if ((nargs > MAX_PARAM) || (nres > MAX_PARAM)) {
                NASD_PANIC();
              }
              total_res_sz += sz;
            }
          }
          else {
            /* pipe */
            if (pipeword & 0xC0000000) {
              fprintf(oerr, "ERROR: too many pipes for call (%s:%d)\n",
                call->decl.define_file, call->decl.define_line);
              output_error();
              return;
            }
            pipeword <<= 2;
            if (type->call_dir == NASD_RPCGEN_DIR_IN) {
              /* client pushes */
              pipeword |= NASD_RPCGEN_SRPC_OPSTAT_PIPE_IN;
              need_pe = 1;
              fprintf(f, ",\n  nasd_srpc_client_pull_func_t %s_proc", type->name);
              fprintf(f, ",\n  void *%s", type->name);
            }
            else {
              /* server pushes */
              NASD_ASSERT(type->call_dir == NASD_RPCGEN_DIR_OUT);
              pipeword |= NASD_RPCGEN_SRPC_OPSTAT_PIPE_OUT;
              fprintf(f, ",\n  nasd_srpc_client_push_func_t %s_proc", type->name);
              fprintf(f, ",\n  void *%s", type->name);
              fprintf(f, ",\n  int *%s_pushed", type->name);
            }
          }
        }
        fprintf(f, ")\n");
        fprintf(f, "{\n");
        fprintf(f, "  nasd_srpc_memvec_t           vec[%d];\n", 1+nargs);
        fprintf(f, "  nasd_srpc_req_header_t       req_otw;\n");
        fprintf(f, "  nasd_status_t                rc;\n");
        fprintf(f, "  nasd_srpc_req_t              req;\n");
        fprintf(f, "  nasd_srpc_conn_t            *conn;\n");
        fprintf(f, "  nasd_srpc_call_t            *call;\n");
        fprintf(f, "  nasd_srpc_status_t           src;\n");
        fprintf(f, "\n");
        fprintf(f, "  conn = (nasd_srpc_conn_t *)handle;\n");
        fprintf(f, "\n");
        fprintf(f, "  req.rpcid = %d;\n", curid);
        fprintf(f, "  nasd_srpc_req_t_marshall(&req, req_otw.reqheader);\n");
        fprintf(f, "\n");
        fprintf(f, "  rc = nasd_srpc_initiate_call(conn, &call);\n");
        fprintf(f, "  if (rc)\n");
        fprintf(f, "    return(NASD_SRPC_S_FAIL);\n");
        fprintf(f, "\n");
        if (has_pipe) {
          fprintf(f, "  call->pipe_expected = 1;\n");
        }
        else {
          fprintf(f, "  call->pipe_expected = 0;\n");
        }
        fprintf(f, "  call->last_pipe_finished = "
          "NASD_SRPC_PIPENUM_NOPIPES;\n");
        fprintf(f, "\n");
        fprintf(f, "  vec[0].buf = &req_otw;\n");
        fprintf(f, "  vec[0].len = sizeof(nasd_srpc_req_header_t);\n");

        j = 1;
        for(i=0;i<nparam;i++) {
          if (paramisout[i] == 0) {
            fprintf(f, "  vec[%d].next = &vec[%d];\n", j-1, j);
            fprintf(f, "  vec[%d].buf = %s;\n", j, typearr[i]->name);
            fprintf(f, "  vec[%d].len = %d;\n",
              j, typearr[i]->size);
            j++;
          }
        }
        fprintf(f, "  vec[%d].next = NULL;\n", j-1);
        nmemvecs = j;

        fprintf(f, "\n");
        fprintf(f, "  rc = nasd_srpc_place_call"
          "(conn, call, vec, %d, %d+sizeof(req), %u);\n",
          nmemvecs, total_arg_sz, pipeword);
        fprintf(f, "  if (rc) {\n");
        fprintf(f, "    nasd_srpc_call_free(call);\n");
        fprintf(f, "    return(NASD_SRPC_S_FAIL);\n");
        fprintf(f, "  }\n");

        /* now iterate through pipes */
        pn = 0;
        for(type=call->call_list;type;type=type->call_next) {
          base_type = basic_type_of_extended(type);
          if (base_type->type_is == NASD_RPCGEN_TYPE_PIPE) {
            if (type->call_dir == NASD_RPCGEN_DIR_IN) {
              /* run pipe PULL */
              if (need_pe) {
                fprintf(f, "  rc = nasd_srpc_client_wait_pipeenab(");
                fprintf(f, "conn, call, %d, &src);\n", restargstate);
                fprintf(f, "  if (src || rc) {\n");
                fprintf(f, "    if (call->ishashed) {\n");
                fprintf(f, "      call->prev->next = call->next;\n");
                fprintf(f, "      call->next->prev = call->prev;\n");
                fprintf(f, "      call->ishashed = 0;\n");
                fprintf(f, "    }\n");
                fprintf(f, "    nasd_srpc_call_free(call);\n");
                fprintf(f, "    return(src ? src : NASD_SRPC_S_FAIL);\n");
                fprintf(f, "  }\n");
                need_pe = 0;
              }
              fprintf(f, "  rc = %s_proc(conn, call, %s, %d, &src);\n",
                type->name, type->name, pn);
            }
            else {
              /* pipe is ready to go- run it PUSH */
              fprintf(f, "  rc = %s_proc(", type->name);
              fprintf(f, "conn, call, %s, %d, %s_pushed, %d, &src);\n",
                type->name, pn, type->name, restargstate);
            }
            fprintf(f, "  if (src || rc) {\n");
            fprintf(f, "    if (call->ishashed) {\n");
            fprintf(f, "      call->prev->next = call->next;\n");
            fprintf(f, "      call->next->prev = call->prev;\n");
            fprintf(f, "      call->ishashed = 0;\n");
            fprintf(f, "    }\n");
            fprintf(f, "    nasd_srpc_call_free(call);\n");
            fprintf(f, "    return(src ? src : NASD_SRPC_S_FAIL);\n");
            fprintf(f, "  }\n");
            pn++;
          }
        }

        fprintf(f, "\n");
        fprintf(f, "  rc = nasd_srpc_call_complete(conn, call,\n");
        fprintf(f, "    %d, %d, &src);\n",
          restargstate, total_res_sz);
        fprintf(f, "  if (rc) {\n");
        fprintf(f, "    return(NASD_SRPC_S_ABORTED);\n");
        fprintf(f, "  }\n");

        if (total_res_sz) {
          fprintf(f, "\n");
          fprintf(f, "  if (src == NASD_SRPC_S_SUCCESS) {\n");
          for(i=0;i<nparam;i++) {
            if (paramisout[i] == 1) {
              fprintf(f,
                "    bcopy((char *)&((nasd_byte_t *)call->resmem)[%d],"
                " (char *)%s, %d);\n",
                paramoff[i], typearr[i]->name,
                typearr[i]->derived_from->size);
            }
          }
          fprintf(f, "    nasd_srpc_call_free(call);\n");
          fprintf(f, "  }\n");
        }

        fprintf(f, "\n");
        fprintf(f, "  return(src);\n");

        fprintf(f, "}\n");
        fprintf(f, "\n");
        free(callname);
        curid++;
        break;
      case NASD_RPCGEN_DECL_UUID:
        break;
      case NASD_RPCGEN_DECL_DCE_ENDPOINT:
        break;
      case NASD_RPCGEN_DECL_VERSION:
        break;
      case NASD_RPCGEN_DECL_MARSHALL:
        break;
      case NASD_RPCGEN_DECL_BASEID:
        break;
      default:
        NASD_PANIC();
    }
  }

  output_c_client_c_trailer(f);

  fclose(f);
  printf("Wrote %s as SRPC client C file\n", filename);
}

void
output_c_server_h_header(
  FILE  *f,
  char  *filename)
{
  nasd_rpcgen_filename_t *fn;

  output_cpp_comment_header(f, filename);

  fprintf(f, "#ifndef _%s_%s_SERVER_H_\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
  fprintf(f, "#define _%s_%s_SERVER_H_\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
  fprintf(f, "\n");
  fprintf(f, "#include <%snasd_rpcgen_c.h>\n",
    global_spec.base_import_path);
  fprintf(f, "\n");

  for(fn=global_spec.usc_import_list;fn;fn=fn->next) {
    fprintf(f, "#include <%s>\n", fn->filename);
  }
  if (global_spec.usc_import_list) {
    fprintf(f, "\n");
  }
}

void
output_c_server_h_trailer(
   FILE  *f)
{
  fprintf(f, "\n");
  fprintf(f, "#endif /* !_%s_%s_SERVERH_ */\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
}

void
output_c_server_h(
  char  *filename)
{
  nasd_rpcgen_type_t *type, *base_type;
  nasd_rpcgen_uint32 curid;
  nasd_rpcgen_decl_t *decl;
  nasd_rpcgen_call_t *call;
  int l, npipes, n;
  char *callname;
  FILE *f;

  if (global_vals.baseid.decl.define_file == NULL) {
    fprintf(oerr, "ERROR: no baseid specified\n");
    output_error();
    return;
  }
  curid = global_vals.baseid.u32;

  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_c_server_h_header(f, filename);

  fprintf(f, "nasd_status_t\n");
  fprintf(f, "%s%s_demux(\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
  fprintf(f, "  nasd_srpc_listener_t     *listener,\n");
  fprintf(f, "  nasd_srpc_conn_t         *conn,\n");
  fprintf(f, "  nasd_srpc_server_call_t  *call);\n");
  fprintf(f, "\n");

  for(decl=global_decls.gnext;decl!=&global_decls;decl=decl->gnext) {
    switch(decl->decl_type) {
      case NASD_RPCGEN_DECL_TYPE:
        break;
      case NASD_RPCGEN_DECL_IMPORT:
        break;
      case NASD_RPCGEN_DECL_CONST:
        break;
      case NASD_RPCGEN_DECL_STRUCT:
        break;
      case NASD_RPCGEN_DECL_CALL:
        call = decl->decl;
        l = strlen(call->name);
        callname = malloc(l+1+strlen("_server"));
        if (callname == NULL) {
          fprintf(stderr, "ERROR: out of memory\n");
          fflush(stderr);
          exit(1);
        }
        npipes = 0;
        sprintf(callname, "%s_server", call->name);
        fprintf(f, "nasd_srpc_status_t\n");
        fprintf(f, "%s(\n", callname);
        fprintf(f, "  nasd_srpc_listener_t     *listener,\n");
        fprintf(f, "  nasd_srpc_conn_t         *conn,\n");
        fprintf(f, "  nasd_srpc_server_call_t  *call%s",
          call->call_list ? ",\n" : ");");
        for(type=call->call_list;type;type=type->call_next) {
          base_type = basic_type_of_extended(type);
          if (base_type->type_is != NASD_RPCGEN_TYPE_PIPE) {
            if (((base_type->type_is != NASD_RPCGEN_TYPE_ARRAY)
                || (base_type->derived_from->size != 1))
              && (base_type->is_typem == 0))
            {
              fprintf(oerr, "ERROR: automarshalling not yet supported "
                "(declaration at %s:%d)\n",
                type->decl.define_file, type->decl.define_line);
              output_error();
              return;
            }
            fprintf(f, "  %s %s%s\n",
              type->derived_from->name, type->name,
              ((type->call_next || npipes) ? "," : ");"));
          }
          else {
            /* do nothing- pipes appended */
            npipes++;
          }
        }
        for(n=0,type=call->call_list;type;type=type->call_next) {
          base_type = basic_type_of_extended(type);
          if (base_type->type_is == NASD_RPCGEN_TYPE_PIPE) {
            n++;
            fprintf(f, "  nasd_srpc_server_pipestate_t *%s%s\n",
              type->name,
              ((n == (npipes-1)) ? "," : ");"));
          }
        }
        fprintf(f, "\n");
        free(callname);
        break;
      case NASD_RPCGEN_DECL_UUID:
        break;
      case NASD_RPCGEN_DECL_DCE_ENDPOINT:
        break;
      case NASD_RPCGEN_DECL_VERSION:
        break;
      case NASD_RPCGEN_DECL_MARSHALL:
        break;
      case NASD_RPCGEN_DECL_BASEID:
        break;
      default:
        NASD_PANIC();
    }
  }

  output_c_server_h_trailer(f);

  fclose(f);
  printf("Wrote %s as SRPC server H file\n", filename);
}

void
output_c_client_h_header(
  FILE  *f,
  char  *filename)
{
  nasd_rpcgen_filename_t *fn;

  output_cpp_comment_header(f, filename);

  fprintf(f, "#ifndef _%s_%s_CLIENT_H_\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
  fprintf(f, "#define _%s_%s_CLIENT_H_\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
  fprintf(f, "\n");
  fprintf(f, "#include <%snasd_rpcgen_c.h>\n",
    global_spec.base_import_path);
  fprintf(f, "\n");

  for(fn=global_spec.usc_import_list;fn;fn=fn->next) {
    fprintf(f, "#include <%s>\n", fn->filename);
  }
  if (global_spec.usc_import_list) {
    fprintf(f, "\n");
  }
}

void
output_c_client_h_trailer(
   FILE  *f)
{
  fprintf(f, "\n");
  fprintf(f, "#endif /* !_%s_%s_CLIENTH_ */\n",
    (global_spec.std_prefix ? global_spec.std_prefix : ""),
    global_spec.out_if_name);
}

void
output_c_client_h(
  char  *filename)
{
  nasd_rpcgen_type_t *type, *base_type;
  nasd_rpcgen_uint32 curid;
  nasd_rpcgen_decl_t *decl;
  nasd_rpcgen_call_t *call;
  int l, has_pipe;
  char *callname;
  FILE *f;

  if (global_vals.baseid.decl.define_file == NULL) {
    fprintf(oerr, "ERROR: no baseid specified\n");
    output_error();
    return;
  }
  curid = global_vals.baseid.u32;

  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_c_client_h_header(f, filename);

  for(decl=global_decls.gnext;decl!=&global_decls;decl=decl->gnext) {
    switch(decl->decl_type) {
      case NASD_RPCGEN_DECL_TYPE:
        break;
      case NASD_RPCGEN_DECL_IMPORT:
        break;
      case NASD_RPCGEN_DECL_CONST:
        break;
      case NASD_RPCGEN_DECL_STRUCT:
        break;
      case NASD_RPCGEN_DECL_CALL:
        call = decl->decl;
        l = strlen(call->name);
        callname = malloc(l+1+strlen("_client"));
        if (callname == NULL) {
          fprintf(stderr, "ERROR: out of memory\n");
          fflush(stderr);
          exit(1);
        }
        has_pipe = 0;
        for(type=call->call_list;type;type=type->call_next) {
          base_type = basic_type_of_extended(type);
          if (base_type->type_is == NASD_RPCGEN_TYPE_PIPE) {
            has_pipe = 1;
            break;
          }
        }
        sprintf(callname, "%s_client", call->name);
        fprintf(f, "nasd_srpc_status_t\n");
        fprintf(f, "%s(\n", callname);
        fprintf(f, "  nasd_srpc_handle_t   handle%s",
          call->call_list ? ",\n" : ");");
        for(type=call->call_list;type;type=type->call_next) {
          base_type = basic_type_of_extended(type);
          if (base_type->type_is != NASD_RPCGEN_TYPE_PIPE) {
            if (((base_type->type_is != NASD_RPCGEN_TYPE_ARRAY)
                || (base_type->derived_from->size != 1))
              && (base_type->is_typem == 0))
            {
              fprintf(oerr, "ERROR: automarshalling not yet supported "
                "(declaration at %s:%d)\n",
                type->decl.define_file, type->decl.define_line);
              output_error();
              return;
            }
            fprintf(f, "  %s %s%s\n",
              type->derived_from->name, type->name,
              (type->call_next ? "," : ");"));
          }
          else {
            /* XXX pipe */
            if (type->call_dir == NASD_RPCGEN_DIR_OUT) {
              fprintf(f, "  nasd_srpc_client_push_func_t %s_proc,\n", type->name);
              fprintf(f, "  void *%s,\n", type->name);
              fprintf(f, "  int *%s_pushed,\n", type->name);
            }
            else {
              NASD_ASSERT(type->call_dir == NASD_RPCGEN_DIR_IN);
              fprintf(f, "  nasd_srpc_client_pull_func_t %s_proc,\n", type->name);
              fprintf(f, "  void *%s,\n", type->name);
            }
          }
        }
        fprintf(f, "\n");
        free(callname);
        break;
      case NASD_RPCGEN_DECL_UUID:
        break;
      case NASD_RPCGEN_DECL_DCE_ENDPOINT:
        break;
      case NASD_RPCGEN_DECL_VERSION:
        break;
      case NASD_RPCGEN_DECL_MARSHALL:
        break;
      case NASD_RPCGEN_DECL_BASEID:
        break;
      default:
        NASD_PANIC();
    }
  }

  output_c_client_h_trailer(f);

  fclose(f);
  printf("Wrote %s as SRPC client H file\n", filename);
}

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