/*  Command-Line Parser Implementation */

/*  From SUIF source code */

/*  Copyright (C) 1995 Stanford University

    All rights reserved.

    NOTICE:  This software is provided ``as is'', without any
    warranty, including any implied warranty for merchantability or
    fitness for a particular purpose.  Under no circumstances shall
    Stanford University or its agents be liable for any use of, misuse
    of, or inability to use this software, including incidental and
    consequential damages.

    License is hereby given to use, modify, and redistribute this
    software, in whole or in part, for any purpose, commercial or
    non-commercial, provided that the user agrees to the terms of this
    copyright notice, including disclaimer of warranty, and provided
    that this copyright notice, including disclaimer of warranty, is
    preserved in the source code and documentation of anything derived
    from this software.  Any redistributor of this software or
    anything derived from this software assumes responsibility for
    ensuring that any parties to whom such a redistribution is made
    are fully aware of the terms of this license and disclaimer.

    "SUIF" is a trademark of Stanford University.
*/

#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "misc.h"
#include "cmdparse.h"


/*
 *  Useful utility routines.
 */


char *const_string (char *s)
{
  return (s == NULL) ? (char *)NULL : strcpy(new char[strlen(s) + 1], s);
}


void error_line (int return_code, char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  putc('\n', stderr);
  fflush(stderr);
  va_end(ap);
  exit(return_code);
}



/*
 *  Parse a command line and extract options we know about.
 */

static int
parse_cmd_line_int(char *s)
{
  int ret_int;
    
  if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
    sscanf(s+2, "%x", &ret_int);
  else
    ret_int = atoi(s);

  return ret_int;
}


void cmd_line_option_usage(int nclos)
{
  fprintf(stderr, "  Options (with defaults) are:\n");
  int i;
  for (i=0; i<nclos; i++) {
    if (cmd_line_options[i].knd == CLO_NOARG) {
      fprintf(stderr, "    %c%s: %s\n", atoi(cmd_line_options[i].deflt) ?
	      '+' : '-', cmd_line_options[i].name,
	      cmd_line_options[i].descr);
    } else {
      fprintf(stderr, "    -%s %s: %s\n", cmd_line_options[i].name,
	      cmd_line_options[i].deflt, cmd_line_options[i].descr);
    }
  }
  exit(1);
}


void
parse_cmd_line (int &argc, char *argv[], cmd_line_option clos[], int nclos)
{
  int i, ap;

  int *elem_num; /* current position in array for CLO_MULTI_STRING */
  elem_num = new int[nclos];

  /*
   * First initialize the default values
   */

  for (i=0; i<nclos; i++) {
    switch (clos[i].knd) {
    case CLO_NOARG:
    case CLO_INT:
      *(int*)clos[i].data = parse_cmd_line_int(clos[i].deflt);
      break;
    case CLO_DOUBLE:
      *(double*)clos[i].data = atof(clos[i].deflt);
      break;
    case CLO_STRING:
      *(char**)clos[i].data = clos[i].deflt;
      break;
    case CLO_MULTI_STRING:
      elem_num[i] = 0;
      (*(char ***)clos[i].data)[0] = clos[i].deflt;
      break;
    default:
      assert_msg(FALSE, ("parse_cmd_line - unknown argument kind %d",
			 clos[i].knd));
    }
  }

  /*
   * Now search through the argument list for matches.
   */

  Bool foundone;
    
  for (ap=0; ap<argc;) {
    foundone = FALSE;
    char *av = argv[ap];
    if (*av != '-' && *av != '+') {
      ap++;
      continue;
    }
    av++;
    for (i=0; i<nclos; i++) {
      if (!strcmp(av, clos[i].name)) {
	switch (clos[i].knd) {
	case CLO_NOARG:
	  *(int*)clos[i].data = (*argv[ap] == '+');
	  break;
	case CLO_INT:
	  if (ap == argc-1)
	    error_line(1, "'%s' requires an argument",
		       clos[i].name);
	  *(int*)clos[i].data = parse_cmd_line_int(argv[ap+1]);
	  break;
	case CLO_DOUBLE:
	  if (ap == argc-1)
	    error_line(1, "'%s' requires an argument",
		       clos[i].name);
	  *(double*)clos[i].data = atof(argv[ap+1]);
	  break;
	case CLO_STRING:
	  if (ap == argc-1)
	    error_line(1, "'%s' requires an argument",
		       clos[i].name);
	  *(char**)clos[i].data = const_string(argv[ap+1]);
	  break;
	case CLO_MULTI_STRING:
	  if (ap == argc-1)
	    error_line(1, "'%s' requires an argument",
		       clos[i].name);
	  (*(char ***)(clos[i].data))[elem_num[i]] =
	    const_string(argv[ap+1]);
	  ++(elem_num[i]);
	  (*(char ***)(clos[i].data))[elem_num[i]] = clos[i].deflt;
	  break;
	}
	int ap2 = ap;
	if (clos[i].knd != CLO_NOARG)
	  ap2 = ap+2;
	else
	  ap2 = ap+1;
	for (int j=ap; ap2<=argc;) /* make sure to copy end NULL */
	  argv[j++] = argv[ap2++];
	if (clos[i].knd != CLO_NOARG)
	  argc -= 2;
	else
	  argc --;
	foundone = TRUE;
	break;
      }
    }
    if (!foundone)
      ap++;
  }

  delete[] elem_num;
}
