/*
 * nasd_rpcgen.c
 *
 * main() and command-line parsing for nasd rpc stub generator
 *
 * 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 <nasd/nasd_getopt.h>

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

#include "nasd_rpcgen.h"

char *progname;

nasd_rpcgen_global_spec_t global_spec;
#define DEF_MIN_ALIGNMENT       1
#define DEF_PAD_TO              8
#define DEF_MAX_PARSE_ERRORS    20
#define DEF_BASE_IMPORT_PATH    "nasd/"
#define DEF_MARSH_ARRAY_THRESH  32

int                    verbose            = 0;
char                  *cpp_cmd;
char                  *cpp_path           = NASD_CPP_LOC;
nasd_rpcgen_cpparg_t  *cppargs            = NULL;
nasd_rpcgen_cpparg_t  *cppargs_tail       = NULL;
nasd_rpcgen_cpparg_t  *searchdirs         = NULL;
nasd_rpcgen_cpparg_t  *searchdirs_tail    = NULL;

FILE *oerr;

void
str_append_prefix(
  char   *str,
  char   *infix,
  char  **outp)
{
  int il, l, nl;
  char *out;

  if (global_spec.std_prefix == NULL) {
    strclone(str, outp);
    return;
  }

  il = strlen(infix);
  l = strlen(global_spec.std_prefix);
  if (!strncmp(str, global_spec.std_prefix, l)) {
    nl = strlen(str);
    out = malloc(nl+il+1);
    if (out == NULL) {
      fprintf(stderr, "ERROR: out of memory\n");
      fflush(stderr);
      exit(1);
    }
    sprintf(out, "%s%s%s",
      global_spec.std_prefix,
      infix,
      &str[l]);
    *outp = out;
  }
  else {
    strclone(str, outp);
  }
}

void
str_prepend_suffix(
  char   *in_str,
  char   *infix,
  char  **outp)
{
  int sl, il, l, nl;
  char *out, *str;

  if (global_spec.std_suffix == NULL) {
    sl = strlen(in_str);
    il = strlen(infix);
    out = malloc(sl+il+1);
    if (out == NULL) {
      fprintf(stderr, "ERROR: out of memory\n");
      fflush(stderr);
      exit(1);
    }
    sprintf(out, "%s%s", in_str, infix);
    *outp = out;
    return;
  }
  str = in_str;
  sl = strlen(str);
  il = strlen(infix);
  l = strlen(global_spec.std_suffix);
  if ((l == 0) || (l > sl)) {
    sl = strlen(in_str);
    il = strlen(infix);
    out = malloc(sl+il+l+1);
    if (out == NULL) {
      fprintf(stderr, "ERROR: out of memory\n");
      fflush(stderr);
      exit(1);
    }
    sprintf(out, "%s%s%s", in_str, infix, global_spec.std_suffix);
    *outp = out;
    return;
  }
  if (!strcmp(&str[sl-l], global_spec.std_suffix)) {
    strclone(in_str, &str);
    str[sl-l] = '\0';
    nl = strlen(str);
    out = malloc(nl+il+l+1);
    if (out == NULL) {
      fprintf(stderr, "ERROR: out of memory\n");
      fflush(stderr);
      exit(1);
    }
    sprintf(out, "%s%s%s",
      str,
      infix,
      global_spec.std_suffix);
    *outp = out;
    free(str);
  }
  else {
    strclone(str, outp);
  }
}

void
output_error()
{
  fflush(oerr);
  fprintf(stderr, "Fatal output error, exiting\n");
  fflush(stderr);
  exit(1);
}

void
usage()
{
  fprintf(stderr, "USAGE: %s [options] infile [infile [infile...]]\n", progname);
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "  -a  minimum alignment [default %d]\n", DEF_MIN_ALIGNMENT);
  fprintf(stderr, "  -A  annotate output\n");
  fprintf(stderr, "  -b  base import path [default %s]\n", DEF_BASE_IMPORT_PATH);
  fprintf(stderr, "  -B  import file\n");
  fprintf(stderr, "  -C  CPP path [default %s]\n", NASD_CPP_LOC);
  fprintf(stderr, "  -D  CPP define\n");
  fprintf(stderr, "  -I  add include dir (nil to reset)\n");
  fprintf(stderr, "  -m  maximum parse errors [default %d]\n",
    DEF_MAX_PARSE_ERRORS);
  fprintf(stderr, "  -P  standard prefix [default none]\n");
  fprintf(stderr, "  -p  pad-to multiple [default %d]\n", DEF_PAD_TO);
  fprintf(stderr, "  -S  standard suffix [default none]\n");
  fprintf(stderr, "  -t  marshalling array threshold [default %d]\n",
    DEF_MARSH_ARRAY_THRESH);
  fprintf(stderr, "  -v  verbose\n");
  fprintf(stderr, "Output options:\n");
  fprintf(stderr, "  -e  marshall types header output file\n");
  fprintf(stderr, "  -E  old marshall types C output file\n");
  fprintf(stderr, "  -g  SRPC global header\n");
  fprintf(stderr, "  -h  C header output file\n");
  fprintf(stderr, "  -i  DCE IDL output file\n");
  fprintf(stderr, "  -M  new marshall types C output file\n");
  fprintf(stderr, "  -n  net_convert.h output file\n");
  fprintf(stderr, "  -x  client C output file\n");
  fprintf(stderr, "  -X  client H output file\n");
  fprintf(stderr, "  -y  server C output file\n");
  fprintf(stderr, "  -Y  server H output file\n");
  fprintf(stderr, "  -u  usc output file\n");
  fflush(stderr);
  exit(1);
}

int
is_power_2(
  nasd_rpcgen_uint64  val)
{
  nasd_rpcgen_uint64 n;

  n = val;
  while(!(n&1))
    n >>= 1;
  n >>= 1;
  if (n)
    return(0);
  return(1);
}

void
compute_out_if_name(
  char  *filename)
{
  char *interface_name, real_interface_name[MAXPATHLEN], *s;
  int i, l, n;

  interface_name = real_interface_name;
  strcpy(interface_name, filename);
  l = strlen(interface_name);
  s = interface_name;

  /* get trailing component of pathname */
  for(i=l-1;i;i--) {
    if (interface_name[i] == NASD_RPCGEN_PATHSEP) {
      interface_name = &interface_name[i+1];
      break;
    }
  }
  /* is name still good? */
  l = strlen(interface_name);
  if ((l < 1) || (interface_name[0] == '.')) {
    fprintf(oerr, "ERROR: invalid filename \"%s\"\n", filename);
    output_error();
    return;
  }
  /* lose trailing dots */
  while(l && (interface_name[l-1] == '.')) {
    interface_name[l-1] = '\0';
     l--;
  }
  NASD_ASSERT(l > 0);
  n = 0;
  /* find last component before dot */
  for(i=l-1;i;i--) {
    if (interface_name[i] == '.') {
      n++;
      if (n == 1)
        interface_name[i] = '\0';
      else {
        interface_name = &interface_name[i+1];
        break;
      }
    }
  }

  strclone(interface_name, &global_spec.out_if_name);
}

void
output_cpp_comment_header(
  FILE  *f,
  char  *filename)
{
  char s[64];
  time_t t;
  int l, i;

  t = time(NULL);
  strcpy(s, ctime(&t));
  l = strlen(s);
  if (s[l-1] == '\n')
    s[l-1] = '\0';

  fprintf(f, "/*\n");
  fprintf(f, " * THIS FILE IS AUTOMATICALLY GENERATED BY %s\n", progname);
  fprintf(f, " * DO NOT MODIFY BY HAND\n");
  fprintf(f, " *\n");
  fprintf(f, " * Generated: %s\n", s);
  if (global_spec.nextra_inputs) {
    fprintf(f, " * Sources: %s\n", global_spec.input_filename);
    for(i=0;i<global_spec.nextra_inputs;i++) {
      fprintf(f, " *          %s\n", global_spec.extra_inputs[i]);
    }
  }
  else {
    fprintf(f, " * Source: %s\n", global_spec.input_filename);
  }
  fprintf(f, " * Annotation: %s\n", global_spec.annotate ? "on" : "off");
  fprintf(f, " * Min alignment: %d\n", global_spec.min_alignment);
  fprintf(f, " * Pad to: %d\n", global_spec.pad_to);
  if (global_spec.std_prefix)
    fprintf(f, " * Standard prefix: \"%s\"\n", global_spec.std_prefix);
  else
    fprintf(f, " * Standard prefix: not specified\n");
  fprintf(f, " */\n");
  fprintf(f, "\n");
}

int
main(
  int     argc,
  char  **argv)
{
  char c, tmp_path[MAXPATHLEN*2];
  nasd_rpcgen_cpparg_t *cpa, *sd;
  nasd_rpcgen_filename_t *fn;
  int i, l;

  progname = argv[0];

  bzero((char *)&global_spec, sizeof(global_spec));
  global_spec.min_alignment = DEF_MIN_ALIGNMENT;
  global_spec.pad_to = DEF_PAD_TO;
  global_spec.max_parse_errors = DEF_MAX_PARSE_ERRORS;
  global_spec.base_import_path = DEF_BASE_IMPORT_PATH;
  global_spec.array_thresh = DEF_MARSH_ARRAY_THRESH;

  while (nasd_getopt(argc, argv,
    "Aa:B:b:C:D:E:e:g:h:I:i:M:m:n:P:p:S:t:U:u:vX:x:Y:y:", &c))
  {
    switch(c) {
      case 'A':
        global_spec.annotate = 1;
        break;
      case 'a':
        if (sscanf(nasd_optarg, "%d", &global_spec.min_alignment) != 1)
          usage();
        if (global_spec.min_alignment < 1)
          usage();
        if (!is_power_2(global_spec.min_alignment))
          usage();
        break;
      case 'B':
        fn = nasd_rpcgen_filename_get();
        strclone(nasd_optarg, &fn->filename);
        if (global_spec.usc_import_list_tail) {
          global_spec.usc_import_list_tail->next = fn;
          global_spec.usc_import_list_tail = fn;
        }
        else {
          global_spec.usc_import_list_tail = global_spec.usc_import_list = fn;
        }
        break;
      case 'b':
        global_spec.base_import_path = nasd_optarg;
        break;
      case 'C':
        cpp_path = nasd_optarg;
        break;
      case 'D':
        cpa = nasd_rpcgen_cpparg_get();
        if (nasd_optarg == NULL) {
          cpa->arg = "-D";
        }
        else {
          cpa->arg = malloc(strlen(nasd_optarg)+3);
        }
        if (cpa->arg == NULL) {
          fprintf(stderr, "ERROR: out of memory\n");
          fflush(stderr);
          exit(1);
        }
        sprintf(cpa->arg, "-D%s", nasd_optarg);
        if (cppargs_tail) {
          cppargs_tail->next = cpa;
          cppargs_tail = cpa;
        }
        else {
          cppargs = cppargs_tail = cpa;
        }
        break;
      case 'E':
        strclone(nasd_optarg, &global_spec.old_marshall_types_c_output_filename);
        break;
      case 'e':
        strclone(nasd_optarg, &global_spec.marshall_types_h_output_filename);
        break;
      case 'g':
        strclone(nasd_optarg, &global_spec.srpc_glob_header_output_filename);
        break;
      case 'h':
        strclone(nasd_optarg, &global_spec.c_header_output_filename);
        break;
      case 'I':
        cpa = nasd_rpcgen_cpparg_get();
        if (nasd_optarg == NULL) {
          cpa->arg = "-I";
          while ((sd = searchdirs)) {
            searchdirs = sd->next;
            free(sd);
          }
          searchdirs_tail = NULL;
        }
        else {
          sd = nasd_rpcgen_cpparg_get();
          cpa->arg = malloc(strlen(nasd_optarg)+3);
          if (cpa->arg == NULL) {
            fprintf(stderr, "ERROR: out of memory\n");
            fflush(stderr);
            exit(1);
          }
          sd->arg = malloc(strlen(nasd_optarg)+2);
          if (sd->arg == NULL) {
            fprintf(stderr, "ERROR: out of memory\n");
            fflush(stderr);
            exit(1);
          }
          strcpy(sd->arg, nasd_optarg);
          l = strlen(sd->arg);
          if (sd->arg[l-1] != NASD_RPCGEN_PATHSEP) {
            sd->arg[l] = NASD_RPCGEN_PATHSEP;
            sd->arg[l+1] = '\0';
          }
          if (searchdirs_tail) {
            searchdirs_tail->next = sd;
            searchdirs_tail = sd;
          }
          else {
            searchdirs = searchdirs_tail = sd;
          }
        }
        sprintf(cpa->arg, "-I%s", nasd_optarg);
        if (cppargs_tail) {
          cppargs_tail->next = cpa;
          cppargs_tail = cpa;
        }
        else {
          cppargs = cppargs_tail = cpa;
        }
        break;
      case 'i':
        strclone(nasd_optarg, &global_spec.dce_idl_output_filename);
        break;
      case 'M':
        strclone(nasd_optarg, &global_spec.marshall_types_c_output_filename);
        break;
      case 'm':
        if (sscanf(nasd_optarg, "%d", &global_spec.max_parse_errors) != 1)
          usage();
        if (global_spec.max_parse_errors < 1)
          usage();
        break;
      case 'n':
        strclone(nasd_optarg, &global_spec.net_convert_h_output_filename);
        break;
      case 'P':
        strclone(nasd_optarg, &global_spec.std_prefix);
        break;
      case 'p':
        if (sscanf(nasd_optarg, "%d", &global_spec.pad_to) != 1)
          usage();
        if (global_spec.pad_to < 1)
          usage();
        if (!is_power_2(global_spec.pad_to))
          usage();
        break;
      case 'S':
        strclone(nasd_optarg, &global_spec.std_suffix);
        break;
      case 't':
        if (sscanf(nasd_optarg, "%d", &global_spec.array_thresh) != 1)
          usage();
        if (global_spec.array_thresh < 1)
          usage();
        break;
      case 'u':
        strclone(nasd_optarg, &global_spec.usc_output_filename);
        break;
      case 'v':
        verbose = 1;
        break;
      case 'X':
        strclone(nasd_optarg, &global_spec.client_h_output_filename);
        break;
      case 'x':
        strclone(nasd_optarg, &global_spec.client_c_output_filename);
        break;
      case 'Y':
        strclone(nasd_optarg, &global_spec.server_h_output_filename);
        break;
      case 'y':
        strclone(nasd_optarg, &global_spec.server_c_output_filename);
        break;
      case '?':
        usage();
        break;
      default:
        fprintf(stderr, "Unknown option '%c'\n", nasd_optopt);
        usage();
    }
  }

  if (nasd_optind >= argc)
    usage();
  global_spec.input_filename = argv[nasd_optind];
  nasd_optind++;

  if (nasd_optind < argc) {
    global_spec.allow_keyword_dup = 1;
    global_spec.nextra_inputs = argc - nasd_optind;
    global_spec.extra_inputs = &argv[nasd_optind];
  }

  l = strlen(cpp_path) + 2 + MAXPATHLEN;
  for(cpa=cppargs;cpa;cpa=cpa->next) {
    l += strlen(cpa->arg) + 1;
  }
  cpp_cmd = malloc(l);
  if (cpp_cmd == NULL) {
    fprintf(stderr, "ERROR: out of memory\n");
    fflush(stderr);
    exit(1);
  }
  strcpy(cpp_cmd, cpp_path);
  for(cpa=cppargs;cpa;cpa=cpa->next) {
    strcat(cpp_cmd, " ");
    strcat(cpp_cmd, cpa->arg);
  }
  strcat(cpp_cmd, " ");

  l = strlen(global_spec.base_import_path);
  if (l && (global_spec.base_import_path[l-1] != NASD_RPCGEN_PATHSEP)) {
    strcpy(tmp_path, global_spec.base_import_path);
    tmp_path[l] = NASD_RPCGEN_PATHSEP;
    tmp_path[l+1] = '\0';
    strclone(tmp_path, &global_spec.base_import_path);
  }

  init_tables();
  init_basic_types();

  if (global_spec.out_if_name == NULL) {
    compute_out_if_name(global_spec.input_filename);
  }

  parse_input_file(global_spec.input_filename, 0);

  /* extra input files */
  for(i=0;i<global_spec.nextra_inputs;i++) {
    parse_input_file(global_spec.extra_inputs[i], 0);
  }

  if (nparse_errors) {
    fprintf(stderr, "Errors encountered parsing %s, exiting\n",
      global_spec.input_filename);
    fflush(stderr);
    exit(1);
  }

  oerr = stderr;

  if (global_spec.dce_idl_output_filename) {
    if (global_spec.nextra_inputs) {
      fprintf(stderr,
        "ERROR: cannot use multiple input files with DCE .idl output\n");
      fflush(stderr);
      exit(1);
    }
    output_dce_idl(global_spec.dce_idl_output_filename);
    reset_output_bits();
  }

  if (global_spec.c_header_output_filename) {
    if (global_spec.nextra_inputs) {
      fprintf(stderr,
        "ERROR: cannot use multiple input files with C header output\n");
      fflush(stderr);
      exit(1);
    }
    output_c_header(global_spec.c_header_output_filename);
    reset_output_bits();
  }

  if (global_spec.srpc_glob_header_output_filename) {
    output_srpc_glob_header(global_spec.srpc_glob_header_output_filename);
    reset_output_bits();
  }

  if (global_spec.net_convert_h_output_filename) {
    output_net_convert_h(global_spec.net_convert_h_output_filename);
    reset_output_bits();
  }

  if (global_spec.usc_output_filename) {
    output_usc(global_spec.usc_output_filename);
    reset_output_bits();
  }

  if (global_spec.server_c_output_filename) {
    output_c_server_c(global_spec.server_c_output_filename);
    reset_output_bits();
  }

  if (global_spec.client_c_output_filename) {
    output_c_client_c(global_spec.client_c_output_filename);
    reset_output_bits();
  }

  if (global_spec.server_h_output_filename) {
    output_c_server_h(global_spec.server_h_output_filename);
    reset_output_bits();
  }

  if (global_spec.client_h_output_filename) {
    output_c_client_h(global_spec.client_h_output_filename);
    reset_output_bits();
  }

  if (global_spec.old_marshall_types_c_output_filename) {
    output_old_marshall_c(global_spec.old_marshall_types_c_output_filename);
    reset_output_bits();
  }

  if (global_spec.marshall_types_c_output_filename) {
    output_marshall_c(global_spec.marshall_types_c_output_filename);
    reset_output_bits();
  }

  if (global_spec.marshall_types_h_output_filename) {
    output_marshall_h(global_spec.marshall_types_h_output_filename);
    reset_output_bits();
  }

  exit(0);
}

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