#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <regex.h>

#include <mrt.h>
#include <irr_rpsl_check.h>

/* predefined type check routines */

static int    boolean_check      (parse_info_t *, char *, predef_t *, int, char *);
static int    free_text_check    (parse_info_t *, char *, predef_t *, int, char *);
static int    email_check        (parse_info_t *, char *, predef_t *, int, char *);
static int    asnum_check        (parse_info_t *, char *, predef_t *, int, char *);
static int    enum_check         (parse_info_t *, char *, predef_t *, int, char *);
static int    int_check          (parse_info_t *, char *, predef_t *, int, char *);
static int    int_check_2        (parse_info_t *, char *, predef_t *, int, char *);
static int    ipv4_check         (parse_info_t *, char *, predef_t *, int, char *);

static int    typedef_handler    (parse_info_t *, char *, type_t *, int, 
				  enum ARG_CONTEXT, char **);
static int    union_handler      (parse_info_t *, char *, type_t *, int, 
				  enum ARG_CONTEXT, char **) ;
static int    predef_handler     (parse_info_t *, char *, type_t *, int, 
				  enum ARG_CONTEXT, char **);
static void   add_type_to_list   (type_t_ll *, type_t *) ;
static type_t *find_type_in_list (type_t_ll *, char *);
static type_t *predef_dup        (char *);
static int    empty_string       (char *);
static char   *my_strdup         (int, char *);
static int    empty_list         (parse_info_t *, type_t *, char *, char **);
static int    arg_type_check     (parse_info_t *, char *, type_t *, int, 
				  enum ARG_CONTEXT, char **) ;

extern regex_t re[];

/* New.................................*/

int as_set_name_check (parse_info_t *pi, char *rp_name, predef_t *p, int show_emsg, char *parm) {
  /* parm is string to be checked */
  char * t = parm;
  char ebuf[1024];

  fprintf(dfile, "as_set_name_check() arg=(%s)\n", parm);

  if(strlen(parm) > 3){
    if( *t == 'a' && *(t+1) == 's' && *(t+2) == '-') 
      return(rpsl_word_check(pi, rp_name, p, show_emsg, (t+3)));
    else{
      if(show_emsg){
        sprintf(ebuf, "Invalid as-set name: %s", parm);
        error_msg_queue (pi, ebuf, ERROR_MSG); 
      }
      return 1;
    }
  }
  if(show_emsg){
    sprintf(ebuf, "Invalid as-set name: %s", parm);
    error_msg_queue (pi, ebuf, ERROR_MSG); 
  }
  return 1;

}

int route_set_name_check (parse_info_t *pi, char *rp_name, predef_t *p, int show_emsg, char *parm) {
  /* parm is string to be checked */
  char * t = parm;
  char ebuf[1024];
  
  fprintf(dfile, "route_set_name_check() arg=(%s)\n", parm);

  if(strlen(parm) > 3){
    if( *t == 'r' && *(t+1) == 't' && *(t+2) == '-') 
      return(rpsl_word_check(pi, rp_name, p, show_emsg, (t+3)));
    else{
      if(show_emsg){
        sprintf(ebuf, "Invalid route-set name: %s", parm);
        error_msg_queue (pi, ebuf, ERROR_MSG); 
      }
      return 1;
    }
  }
  if(show_emsg){
    sprintf(ebuf, "Invalid route-set name: %s", parm);
    error_msg_queue (pi, ebuf, ERROR_MSG); 
  }
  return 1;

}

int real_check (parse_info_t *pi, char *rp_name, predef_t *p, int show_emsg, char *parm) {
  char ebuf[1024];
  double dval;

  fprintf(dfile, "real_check () arg=(%s) lower=(%f) upper=(%f)\n",
          parm, p->u.d.lower, p->u.d.upper);

  if (regexec(&re[RE_REAL], parm, (size_t) 0, NULL, 0)) {
    if (show_emsg) {
      sprintf (ebuf, "Non-numeric argument value found for RP attribute \"%s\"", rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 1;
  } 

  /* predef_t doesn't have a float upper and lower...fix */
  if (!p->use_bounds)
    return 0;
  
  dval = atof (parm);
  if (dval < p->u.d.lower) {
    if (show_emsg) {
      sprintf (ebuf, 
               "Real value (%f) is less than lower bound (%f) for RP attribute \"%s\"", 
               dval, p->u.d.lower, rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 1;
  }
  
  if (dval > p->u.d.upper)  {
    if (show_emsg) {
      sprintf (ebuf, 
               "Real value (%f) exceeds upper bound (%f) for RP attribute \"%s\"", 
               dval, p->u.d.upper, rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 1;
  }
  return 0;
}


int rpsl_word_check (parse_info_t *pi, char *rp_name, predef_t *p, int show_emsg, char *parm) {
  char * t = parm;
  char ebuf[1024];

 fprintf(dfile, "rpsl_word_check() arg=(%s)\n", parm);

  if( !isalpha(*t) ){  /* check first letter */
    if(show_emsg){
      sprintf(ebuf, "Invalid rpsl_word: %s must start with a letter", parm);
      error_msg_queue (pi, ebuf, ERROR_MSG); 
    }
    return 1;
  }

  if ( !isalpha(  *(t+strlen(t)-1) ) &&  !isdigit( *(t+strlen(t)-1) ) ){/* check last letter, maybe save us */
    if(show_emsg){
      sprintf(ebuf, "Invalid rpsl_word: %s must end in a letter or digit", parm);
      error_msg_queue (pi, ebuf, ERROR_MSG); 
    }
    return 1;                                                              /* from going through whole string */
  }

  while(*++t){
    if(!isalpha(*t) && !isdigit(*t) && *t != '-' && *t != '_' ){
      if(show_emsg){
        sprintf(ebuf, "Invalid rpsl_word: cannot contain character %c", *t);
        error_msg_queue (pi, ebuf, ERROR_MSG); 
      }
      return 1;
    }
  }
  
  return 0;
}

int email_check (parse_info_t *pi, char *rp_name, predef_t *p, 
		 int show_emsg, char *email_addr) {
  char *q;
  char ebuf[1024];

  if ((q = strchr (email_addr, '@')) == NULL) {
    if (show_emsg) {
      sprintf (ebuf, "Missing '@' in email address for attribute \"%s\"", rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 1;
  }

  if (strchr (q + 1, '@') != NULL) {
    if (show_emsg) {
      sprintf (ebuf, 
	       "Multiple '@'s found in email address for attribute \"%s\"", rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 1;
  }

  if (regexec (&re[RE_EMAIL3], email_addr, (size_t) 0, NULL, 0)  ||
      (regexec (&re[RE_EMAIL1], email_addr, (size_t) 0, NULL, 0) &&
       regexec (&re[RE_EMAIL2], email_addr, (size_t) 0, NULL, 0))) {
    if (show_emsg) {
      sprintf (ebuf, "Malformed RFC822 email address for attribute \"%s\"", rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 1;
  }

  return 0;
}

/* boolean means either true or false */
int boolean_check (parse_info_t *pi, char *rp_name, predef_t *p, 
		   int show_emsg, char *parm) {
  char ebuf[1024];
  
  fprintf(dfile, "boolean_check() arg=(%s)\n", parm);
  
  if (!strcasecmp(parm, "true") || 
      !strcasecmp(parm, "false"))
    return 0;
  else {
    if (show_emsg) {
      sprintf(ebuf, 
	      "Invalid boolean value %s found in attribute \"%s\"", parm, rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
     }
    return 1;
  }
}

/* check to make sure of ASCII characters */
int free_text_check (parse_info_t *pi, char *rp_name, predef_t *p, 
		     int show_emsg, char *parm) {
  char ebuf[1024];
  int bad_arg;

  fprintf(dfile, "free_text_check() arg=(%s)\n", parm);

  for (; *parm && isascii(*parm); parm++);

  bad_arg = (*parm != '\0');

  if (bad_arg && show_emsg) {
    sprintf(ebuf, "Non-ASCII character found in attribute \"%s\"", rp_name);
    error_msg_queue (pi, ebuf, ERROR_MSG);
  }
  
  return bad_arg;
}


int empty_string (char *s) {
  
  for (; *s && !isgraph (*s); s++);

  return (*s == '\0');
}

/* Return
 * 
 * 1 if type check passes
 * 0 if there is a arg type mismatch
 */
int arg_type_check (parse_info_t *pi, char *rp_name, type_t *t, int show_emsg, 
		    enum ARG_CONTEXT context, char **arg) {
  char ebuf[1024];
  
  /* check for non-list input to list arg and vice-versa */
  if (t->list) {
    /*
    if (**arg != '{') {
      if (show_emsg) {
	sprintf (ebuf, "RP attribute \"%s\" list attribute expected", rp_name);
	error_msg_queue (pi, ebuf, ERROR_MSG);
      }
      return 0;
    }
    */
    return 1;
  }
  else if (**arg == '{' && context == NON_LIST_TYPE) {
    if (show_emsg) {
      sprintf (ebuf, "RP attribute \"%s\" non-list argument expected", rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 0;
  }
  
  return 1;
}

/*
 *
 * Return
 *
 *   1 if the arg is type list and the actual
 *     arg does not exceed the minimum threshold, 't->min_elem'
 *     for the minimum number of elements.
 *
 *   0 otherwise
 */
int empty_list (parse_info_t *pi, type_t *t, char *rp_name, char **arg) {
  char ebuf[1024];

  /* if **arg is list type, check to see if an empty list is ok */
  if (empty_string (*arg) && t->list) {
    if (t->max_elem > 0 && t->min_elem > 0) {
      sprintf (ebuf, "RP attribute \"%s\" must have at least %d arguments", 
	       rp_name, t->min_elem);
      error_msg_queue (pi, ebuf, ERROR_MSG);
      return 1;
    }
  }

  return 0;
}


char *my_strdup (int add_braces, char *s) {
  char buf[1024];
  
  buf[0] = '\0';
  if (add_braces)
    strcat (buf, "{");
  
  if (s != NULL)
    strcat (buf, s);
  
  if (add_braces)
    strcat (buf, "}");
  
fprintf (dfile, "my_strdup () returns (%s)\n", buf);
  return strdup (buf);
}


type_t *predef_dup (char *s) {
  int i;

  if (s == NULL)
    return NULL;

  for (i = 0; i < MAX_PREDEF_TYPES; i++)
    if (!strcasecmp (s, predef_type[i]) && (i != ENUM))
      return create_predef ((enum PREDEF_TYPE) i, 0, NULL, NULL, NULL);
	
  return NULL;
}

/* find a type typedef type that matches 's' */
type_t *find_type_in_list (type_t_ll *start, char *s) {
  type_t *t;

  if (s == NULL)
    return NULL;

  for (t = start->first; t != NULL; t = t->next)
    if (t->type     == TYPEDEF &&
	t->u.t.name != NULL    &&
	!strcasecmp (s, t->u.t.name))
      break;

  return t;
}

type_t *create_typedef (char *name, type_t *t) {
  type_t *obj;

  obj = (type_t *) malloc (sizeof (type_t));
  memset ((char *) obj, 0, sizeof (type_t));

  obj->type     = TYPEDEF;
  if (name != NULL)
    obj->u.t.name = name;
  obj->u.t.t    = t;

  return obj;
}

type_t *create_union (param_t *p_list) {
  type_t *obj;

  obj = (type_t *) malloc (sizeof (type_t));
  memset ((char *) obj, 0, sizeof (type_t));

  obj->type = UNION;
  obj->u.u.ll = p_list;

  return obj;
}

type_t *create_predef (enum PREDEF_TYPE ptype, int use_bounds, irange_t *i, 
		       drange_t *d, char *s) {
  type_t *obj;

  obj = (type_t *) malloc (sizeof (type_t));
  memset ((char *) obj, 0, sizeof (type_t));

  obj->type            = PREDEFINED;
  obj->u.p.use_bounds  = use_bounds;
  if (i != NULL) {
    obj->u.p.u.i.lower = i->lower;
    obj->u.p.u.i.upper = i->upper;
  }
  else if (d != NULL) {
    obj->u.p.u.d.lower = d->lower;
    obj->u.p.u.d.upper = d->upper;
  }
  obj->u.p.ptype       = ptype;
  obj->u.p.enum_string = s;

  return obj;
}


type_t *create_type (parse_info_t *pi, enum RPSL_DATA_TYPE type, ...) {
  va_list ap;
  irange_t i;
  drange_t d;
  param_t *p;
  type_t *t2, *t = NULL;
  enum PREDEF_TYPE ptype;
  char *s;

  va_start (ap, type);

  if (type == UNION) {
    p = va_arg (ap, struct _param_t *);
    t = create_union (p);
  }
  else if (type == PREDEFINED) {
    ptype = va_arg (ap, enum PREDEF_TYPE); 
    switch (ptype) {
    case INTEGER:
      i.lower = atol (va_arg (ap, char *));
      i.upper = atol (va_arg (ap, char *));
      t = create_predef (ptype, 1, &i, NULL, NULL);
      if (i.lower > i.upper)
	error_msg_queue (pi, "Lower bound value exceeds maximum bound", 
			 ERROR_MSG);
      break;
    case REAL:
      d.lower = atof (va_arg (ap, char *));
      d.upper = atof (va_arg (ap, char *));
      t = create_predef (ptype, 1, NULL, &d, NULL);
      if (d.lower > d.upper)
	error_msg_queue (pi, "Lower bound value exceeds maximum bound", 
			 ERROR_MSG);
      break;      
    case ENUM:
      s = va_arg (ap, char *);
      t = create_predef (ptype, 0, NULL, NULL, s);
      break;
    default:
      fprintf (dfile, "create_type () unknown predef type (%d)\n", ptype);
      break;
    }
  }
  else if (type == TYPEDEF) { /* the t2 rec need not be a typedef, thus
			       * it may not have a name
			       */
    s = va_arg (ap, char *);
    t2 = va_arg (ap, struct _type_t *);
    if (t2->type == TYPEDEF && t2->u.t.name == NULL)
      t2->u.t.name = strdup (s);
    else
      t = create_typedef (strdup (s), t2);
  }
  else if (type == LIST) {
    t = va_arg (ap, struct _type_t *);
    t->list = 1;
    s = va_arg (ap, char *);
    if (s != NULL) {
      t->min_elem = atol (s);
      s = va_arg (ap, char *);
      t->max_elem = atol (s);
      if (t->min_elem > t->max_elem)
	error_msg_queue (pi, "Minimum elements value exceeds maximum elements", 
			 ERROR_MSG);
    }
  }
  else if (type == UNKNOWN) {
    s = va_arg (ap, char *);
    if ((t2 = find_type_in_list (&type_ll, s)) != NULL)
      t = create_typedef (NULL, t2);
    else if ((t = predef_dup (s)) == NULL)
      error_msg_queue (pi, "Unknown type", ERROR_MSG);
  }
  else
    fprintf (dfile, "create_type () unknown type (%d)\n", type);

  va_end (ap);

  /* add t to the list */
  if (type != LIST)
    add_type_to_list (&type_ll, t);

  return t;
}



param_t *create_parm (type_t *t) {
  param_t *obj;

  if (t == NULL)
    return NULL;

  obj = (param_t *) malloc (sizeof (param_t));
  memset ((char *) obj, 0, sizeof (param_t));
  obj->t = t;
 
  return (obj);
}

/* add 'elem' to the end of the parm list 'p_list */
param_t *add_parm_obj (param_t *p_list, param_t *elem) {
  param_t *p;

  if (p_list == NULL || elem == NULL)
    return NULL;

  /* find the last element in the list */
  for (p = p_list; p->next != NULL; p = p->next);
  p->next = elem;

  return p_list;
}

void initialize_umethods (method_t *m) {
  method_t *mdup;

  mdup = create_method (m->name, NULL, m->context, 0);
  mdup->type = m->type;
  mdup->ll   = m->ll;
  mdup->next = NULL;
  m->type    = UNION_OF_METHODS;
  m->umeth   = mdup;
  m->ll      = NULL;
}

method_t *add_umeth_obj (method_t *list, method_t *obj) {
  method_t *m;

  for (m = list->umeth; m->next != NULL; m = m->next);
  m->next = obj;

  return list->umeth;
}

/* Add a method object to the end of a method list */
method_t *add_method (method_t *list, method_t *obj) {
  method_t *m;

  if (obj == NULL || list == NULL)
    return list;

  if ((m = find_method (list, obj->name)) != NULL) {
    if (m->type != UNION_OF_METHODS)
      initialize_umethods (m);
    m->umeth = add_umeth_obj (m, obj);
  }
  else {
    for (m = list; m->next != NULL; m = m->next);
    m->next = obj;
  }

  return list;
}

/* determine if the argument type is a list or non-list
 * argument.
 *
 * valid_arg () function will use this info to check for
 * arg list errors, ie, a list argument in a non-list context
 * and vice-versa.
 */
enum ARG_CONTEXT find_arg_context (param_t *parm_list, int is_union) {
  param_t *p, ps;


if (parm_list != NULL && parm_list->t->type != NULL) {
  if (parm_list->t == NULL) {
fprintf (dfile, "\n\n\n---------t is null\n");
exit (0);
  }
  if (parm_list->t->type == NULL) {
fprintf (dfile, "\n\n\n---------type is null\n");
exit (0);
  }
fprintf (dfile, "find_arg_context () type (%d)\n", parm_list->t->type);
if (parm_list->t->type == TYPEDEF) {
  if (parm_list->t->u.t.name != NULL)
    fprintf (dfile, "find_arg_context () type name (%s)\n", parm_list->t->u.t.name);
  else
    fprintf (dfile, "find_arg_context () type name is NULL\n");
}

  if ((parm_list->next != NULL && !is_union) || parm_list->t->list)
    return LIST_TYPE;
  else {
    if (parm_list->t->type == TYPEDEF) {
      ps.next = NULL;
      ps.t    = parm_list->t->u.t.t;
      return find_arg_context (&ps, 0);
    }
    else if (parm_list->t->type == UNION) {
      for (p = parm_list->t->u.u.ll; p != NULL; p = p->next)
	if (find_arg_context (p, 1) == LIST_TYPE)
	  return LIST_TYPE;
    }
    else {
      if (parm_list->t->list)
	return LIST_TYPE;
    }
  }

  return NON_LIST_TYPE;
}

  return NON_LIST_TYPE;
}

method_t *create_method (char *name, param_t *parm_list, 
			 enum ARG_CONTEXT context, int is_3dots) {
  method_t *obj;
  param_t *p;

  obj = (method_t *) malloc (sizeof (method_t));
  memset ((char *) obj, 0, sizeof (method_t));
  obj->name    = strdup (name);
  obj->context = context;
  obj->ll      = parm_list;
  if (is_3dots) { /* find the last element in the last and make it a list */
    for (p = parm_list; p->next != NULL; p = p->next);
    p->t->list     = 1;
    p->t->min_elem = 1;
    p->t->max_elem = 1024;
  }

  return (obj);
}

rp_attr_t *create_rp_attr (char *name, method_t *method_list) {
  rp_attr_t *obj;

  obj = (rp_attr_t *) malloc (sizeof (rp_attr_t));
  memset ((char *) obj, 0, sizeof (rp_attr_t));
  obj->name  = strdup (name);
  obj->first = method_list;

  return (obj);
}

void add_rp_attr (rp_attr_t_ll *start, rp_attr_t *obj) {

  obj->next = NULL;
  if (start->first == NULL)
    start->first = obj;
  else
    start->last->next = obj;

  start->last = obj;
}

proto_t *create_proto_attr (char *name, method_t *m) {
  proto_t *obj;

  obj = (proto_t *) malloc (sizeof (proto_t));
  memset ((proto_t *) obj, 0, sizeof (proto_t));

  obj->name  = strdup (name);
  obj->first = m;
    
  return (obj);
}


proto_t *find_protocol (proto_t_ll *start, char *proto) {
  proto_t *p;

  if (proto == NULL)
    return NULL;

  for (p = start->first; p != NULL; p = p->next)
    if (!strcasecmp (proto, p->name))
      break;

  return p;
}

void add_new_proto (proto_t_ll *start, proto_t *obj) {

  obj->next = NULL;
  if (start->first == NULL)
    start->first = obj;
  else
    start->last->next = obj;

  start->last = obj;
}


void add_type_to_list (type_t_ll *start, type_t *obj) {

  if (obj == NULL)
    return;

  obj->next = NULL;
  if (start->first == NULL)
    start->first = obj;
  else
    start->last->next = obj;

  start->last = obj;
  
}

/* debug's */

void print_typedef (type_t *x) {
  typedef_t *t = &(x->u.t);
  param_t p;

  if (t->name == NULL)
    fprintf (dfile, "%9stypedef name is NULL\n", " ");
  else
    fprintf (dfile, "%9stypedef name <(%s)>\n", " ", t->name);

  fprintf (dfile, "%9s-------->\n", " ");
  p.next = NULL;
  p.t    = t->t;
  print_parm_list (&p);
  fprintf (dfile, "%9s-------->\n", " ");
}

void print_union (type_t *t) {

  fprintf (dfile, "%9s--------\n", " ");
  print_parm_list (t->u.u.ll);
  fprintf (dfile, "%9s--------\n", " ");
}

void print_predef (type_t *t) {
  predef_t *p = &(t->u.p);

  if (p->ptype == ENUM)
    fprintf (dfile, "%9s%s: (%s)\n", " ", predef_type[p->ptype], p->enum_string);
  else if (p->ptype == INTEGER)
    fprintf (dfile, "%9s%s: upper (%d) lower (%d)\n", " ", predef_type[p->ptype], p->u.i.upper, p->u.i.lower);
  else if (p->ptype == REAL)
    fprintf (dfile, "%9s%s: upper (%f) lower (%f)\n", " ", predef_type[p->ptype], p->u.d.upper, p->u.d.lower);
  else
    fprintf (dfile, "%9s%s:\n", " ", predef_type[p->ptype]);
}

void print_parm_list (param_t *ll) {
  param_t *p;
  type_t *t;

  if (ll == NULL) {
    fprintf (dfile, "%6sEmpty parm list\n", " ");
    return;
  }

  for (p = ll; p != NULL; p = p->next) {
    t = p->t;
    if (t->type == UNKNOWN) {
      fprintf (dfile, "%6s???unknown parm type???\n", " ");
      continue;
    }
    if (t->type == LIST || t->list)
      fprintf (dfile, "%6slist of %s min_elem (%d) max_elem (%d)\n", 
	       " ", data_type[t->type], t->min_elem, t->max_elem);
    else
      fprintf (dfile, "%6s%s\n", " ", data_type[t->type]);

    if (t->type == PREDEFINED)
      print_predef (t);
    else if (t->type == UNION)
      print_union (t);
    else
      print_typedef (t);
  }
}

void print_method_list (method_t *ll) {
  method_t *m;
  int i;

  for (m = ll, i = 1; m != NULL; m = m->next, i++) {
    if (m->type == UNION_OF_METHODS) {
      fprintf (dfile, "   ------------BEGIN UNION OF METHODS-----\n");
      fprintf (dfile, "   method %d: (%s)\n", i, m->name);
      print_method_list (m->umeth);
      fprintf (dfile, "   ------------EOF UNION OF METHODS-------\n");
    }
    else {
      fprintf (dfile, "   method %d: (%s)\n", i, m->name);
      print_parm_list (m->ll);
    }
  }
}

void print_rp_list (rp_attr_t_ll *start) {
  rp_attr_t *p;

  fprintf (dfile, "\n\n-------------------\n");
  for (p = start->first; p != NULL; p = p->next) {
    fprintf (dfile, "\nrp name=(%s)\n", p->name);
    print_method_list (p->first);
  }
  fprintf (dfile, "-------------------\n\n");
}

void print_proto_list (proto_t_ll *start) {
  proto_t *p;

  fprintf (dfile, "\n\n-------------------\n");
  for (p = start->first; p != NULL; p = p->next) {
    fprintf (dfile, "\nproto name=(%s)\n", p->name);
    print_method_list (p->first);
  }
  fprintf (dfile, "-------------------\n\n");
}

void print_typedef_list (type_t_ll *ll) {
  type_t *t;
  param_t p;

  fprintf (dfile, "\n\ntype list----------------\n");
  for (t = ll->first; t != NULL; t = t->next) {
    p.next = NULL;
    p.t    = t;
    print_parm_list (&p);
  }
  fprintf (dfile, "----------------\n\n");
}

/* New.................................*/

char *find_matching_brace (char **s) {
  char *p;
  int c;

  c = 0;
  p = *s;
  for (; *p; p++) {
    if (*p == '{')
      c++;
    else if (*p == '}' && --c < 1)
      break;
  }

  return p;
}


char *find_list_arg (char *s, char **ret) {
  char *q, *elem;

  if ((q = strchr (s, ',')) != NULL) {
    *q = '\0';
    elem = strdup (s);
    /* pass over any spaces */
    for (q++; *q && isspace (*q); q++);
    *ret  = strdup (q);
  }
  else {
    /* pass over any spaces */
    for (; *s && isspace (*s); s++);
    for (q = s + strlen (s) - 1; *q && isspace (*q); *q-- = '\0');
    elem = strdup (s);
    *ret  = strdup ("");
  }

  return elem;
}

char *find_arg (char **s, int list) {
  char *q, *x, *elem, *ret;
  int c;

  x = *s;

  if (**s != '{')
    elem = find_list_arg (*s, &ret);
  else {
    /* pass over any spaces */
    for (; **s && isspace (**s); (*s)++);

    /* remove right-end surrounding brace */
    c = strlen (*s) - 1;
    if (*(*s + c) == '}') {
      *(*s + c) = ' ';
      (*s)++;
    }
    
    /* remove the left-end surrounding brace */
    if (**s == '{') {
      q = find_matching_brace (s);
      *++q = '\0';
      elem = strdup (*s);
      ret  = strdup (++q);
      }
    else
      elem = find_list_arg (*s, &ret);
  }

  free (x);
  *s = ret;

  return elem;
}

/* processing modules */


int ipv4_check (parse_info_t *pi, char *rp_name, predef_t *p, 
		int show_emsg, char *parm) {

  return !_is_ipv4_prefix (pi, parm, 1);
}

int int_check (parse_info_t *pi, char *rp_name, predef_t *p, 
		 int show_emsg, char *parm) {
  char *colon;
  int bad_arg;

  if ((colon = strchr (parm, ':')) == NULL)
    bad_arg = int_check_2 (pi, rp_name, p, show_emsg, parm);
  else {
    *colon = '\0';
    bad_arg = int_check_2 (pi, rp_name, p, show_emsg, parm);

    *colon = ':';
    if (!bad_arg)
      bad_arg = int_check_2 (pi, rp_name, p, show_emsg, colon + 1);
  }

  return bad_arg;
}

int int_check_2 (parse_info_t *pi, char *rp_name, predef_t *p, 
		 int show_emsg, char *parm) {
  char ebuf[1024];
  char *q;
  long ival;

  fprintf (dfile, "int_check () arg=(%s) lower=(%d) upper=(%d)\n", 
	   parm, p->u.i.lower, p->u.i.upper);
  for (q = parm; *q != '\0' && isdigit (*q); q++);

  if (*q != '\0') {
    if (show_emsg) {
      sprintf (ebuf, "Non-numeric argument value found for RP attribute \"%s\"", rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 1;
  }    

  if (!p->use_bounds)
    return 0;

  ival = atol (parm);
  if (ival < p->u.i.lower) {
    if (show_emsg) {
      sprintf (ebuf, 
	       "Integer value (%d) is less than lower bound (%d) for RP attribute \"%s\"", 
	       ival, p->u.i.lower, rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 1;
  }

  if (ival > p->u.i.upper)  {
    if (show_emsg) {
      sprintf (ebuf, 
	       "Integer value (%d) exceeds upper bound (%d) for RP attribute \"%s\"", 
	       ival, p->u.i.upper, rp_name);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }
    return 1;
  }
  
  return 0;
}

char *append_enum (char *curr_list, char *new_elem) {
  char *new_list;
  int slen;

  if (curr_list == NULL)
    curr_list = strdup ("");

  slen = strlen (curr_list) + strlen (new_elem) + 2;
  new_list = (char *) malloc (slen);
  strcpy (new_list, curr_list);
  strcat (new_list, new_elem);
  *(new_list + slen - 2) = ENUM_DELIMIT;
  *(new_list + slen - 1) = '\0';

  free (curr_list);
  return new_list;
}

int asnum_check (parse_info_t *pi, char *rp_name, predef_t *p, 
		 int show_emsg, char *parm) {
  int bad_arg;
  char ebuf[1024];

  bad_arg = regexec(&re[RE_ASNUM], parm, (size_t) 0, NULL, 0);

  if (bad_arg && show_emsg) {
    sprintf (ebuf, "Malformed AS number (%s) for attribute \"%s\"", parm, rp_name);
    error_msg_queue (pi, ebuf, ERROR_MSG);
  }

  return bad_arg;
}

/* Given an enum element '*parm', determine if parm is a legal
 * enum value.  The function assumes that the stored list of legal
 * enum values is stored with the ENUM_DELIMIT char immediately after
 * each enum element. eg, if ENUM_DELIMIT = ':' and we have k enum 
 * elements, the list would be stored as
 *
 * enum1:enum2:... :enumk:
 *
 * Return
 *   bad_arg = 0 parm is present in the legal enum list
 *   bad_arg = 1 otherwise
 */
int enum_check (parse_info_t *pi, char *rp_name, predef_t *p, int show_emsg, char *parm) {
  int bad_arg = 1;
  char ebuf[1024];
  char *q, *r, *s;

  q = r = s = strdup (p->enum_string);

  /* p and q bracket the enum test string, each enum string is
   * delimited by ENUM_DELIMIT.
   */
  for (; bad_arg && (q = strchr (s, ENUM_DELIMIT)) != NULL; s = ++q) {
    *q = '\0';
    if (!strcasecmp (s, parm))
      bad_arg = 0;
  }

  if (bad_arg && show_emsg) {
    sprintf (ebuf, "Invalid enumeration value (%s) for RP attribute \"%s\"", parm, rp_name);
    error_msg_queue (pi, ebuf, ERROR_MSG);
  }

  free (r);
  return bad_arg;
}


int predef_handler (parse_info_t *pi, char *rp_name, type_t *t, 
		    int show_emsg, enum ARG_CONTEXT context, char **arg) {
  int bad_arg = 0, i;
  char ebuf[1024];
  int (*arg_handler)(parse_info_t *, char *, predef_t *, int, char *);
  char *p, *s;

  fprintf (dfile, "predef_handler () arg=(%s)\n", *arg);

  /* Check for list arg type with '**arg' and vice-versa */
  if (!arg_type_check (pi, rp_name, t, show_emsg, context, arg))
    return 1;

  /* Dupe the arg list.  Return list intact if argument check fails */
  if (*arg != NULL)
    s = strdup (*arg);
  else
    return 0;

  if (t->u.p.ptype == INTEGER)
    arg_handler = &int_check;
  else if (t->u.p.ptype == ENUM)
    arg_handler = &enum_check;
  else if (t->u.p.ptype == IPV4_ADDRESS)
    arg_handler = &ipv4_check;
  else if (t->u.p.ptype == AS_NUMBER)
    arg_handler = &asnum_check;
  else if (t->u.p.ptype == BOOLEAN)
    arg_handler = &boolean_check;
  else if (t->u.p.ptype == FREE_TEXT)
    arg_handler = &free_text_check;
  else if (t->u.p.ptype == EMAIL)
    arg_handler = &email_check;
  else if (t->u.p.ptype == REAL)
    arg_handler = &real_check;
  else {
    fprintf (dfile, "predef_handler () unknown type to check (%d)\n", t->u.p.ptype);
    return 1;
  }

  /* for each element in the list, pass it to the argument handler */
  for (i = 0; !bad_arg && (p = find_arg (&s, t->list)) && *p != '\0'; free (p)) {
    if (!(bad_arg = (t->list && t->max_elem > 0 && ++i >= t->max_elem)))
      bad_arg = (*arg_handler) (pi, rp_name, &(t->u.p), show_emsg, p);
    else if (show_emsg) {
      sprintf (ebuf, "RP attr (%s) number of args (%d) exceeds maximum threshold (%d)", rp_name, i, t->max_elem);
      error_msg_queue (pi, ebuf, ERROR_MSG);
    }

    if (!t->list) {
      free (p);
      break;
    }
  }

  /* See if the minimum elements threshold is satisfied */
  if (!bad_arg                                                    && 
      (bad_arg = (t->list && t->max_elem > 0 && t->min_elem > i)) &&
      show_emsg) {
    sprintf (ebuf, "RP attr (%s) actual arguments (%d) is less than minimum arguments threshold (%d)", rp_name, i, t->min_elem);
    error_msg_queue (pi, ebuf, ERROR_MSG);
  }

  /* if the arg is good, reset the original arg list
   * with this arg removed
   */
  if (!bad_arg) {
    free (*arg);
    *arg = strdup (s);
  }
  free (s);    
  
  return bad_arg;
}

int union_handler (parse_info_t *pi, char *rp_name, type_t *t, 
		   int show_emsg, enum ARG_CONTEXT context, char **arg) {
  int i, too_many_args = 0;
  int bad_arg;
  char ebuf[1024];
  int (*arg_handler)(parse_info_t *, char *, type_t *, int, enum ARG_CONTEXT, char **);
  char *p, *s = NULL;
  param_t *parm;

  /* Check for list arg type with '**arg' and vice-versa */
  if (!arg_type_check (pi, rp_name, t, show_emsg, context, arg))
    return 1;

  /* Dupe the arg list.  Return list intact if argument check fails */
  if (*arg != NULL)
    s = strdup (*arg);
  else
    return 0;


  /* for each parm in the argument list, see if a union type check passes */
  for (bad_arg = i = 0; (p = find_arg (&s, t->list)) && *p != '\0'; free (p)) {
    bad_arg = 1;
    
    for (parm = t->u.u.ll; parm != NULL; parm = parm->next) {
      
      /* determine the union type */
      if (parm->t->type == PREDEFINED)
	arg_handler = &predef_handler;
      else if (parm->t->type == TYPEDEF)
	arg_handler = &typedef_handler;
      else if (parm->t->type == UNION)
	arg_handler = &union_handler;
      else {
	fprintf (dfile, "union_handler () unknown type (%d)\n", parm->t->type);
	return 1;
      }
      
      if (!(bad_arg = (t->list && t->max_elem > 0 && i >= t->max_elem))) {
	if (!(bad_arg = (*arg_handler) (pi, rp_name, parm->t, 0, context, &p))) {
	  i++;
	  break;
	}
      }
      else {
	too_many_args = 1;
	if (show_emsg) {
	  sprintf (ebuf, "RP attr (%s) number of args (%d) exceeds maximum threshold (%d)", rp_name, i, t->max_elem);
	  error_msg_queue (pi, ebuf, ERROR_MSG);
	}
	break;
      }
    }
    
    if (bad_arg && !too_many_args && show_emsg) {
      sprintf (ebuf, "RP attr (%s) invalid data type \"%s\"", rp_name, p);
      error_msg_queue (pi, ebuf, ERROR_MSG);
      break;
    }
    
    if (!t->list)
      break;
  }

  /* See if the minimum elements threshold is satisfied */
  if (!bad_arg                                                    && 
      (bad_arg = (t->list && t->max_elem > 0 && t->min_elem > i)) &&
      show_emsg) {
    sprintf (ebuf, "RP attr (%s) actual arguments (%d) is less than minimum arguments threshold (%d)", rp_name, i, t->min_elem);
    error_msg_queue (pi, ebuf, ERROR_MSG);
  }

  /* if the arg is good, reset the original arg list
   * with this arg removed
   */
  if (!bad_arg) {
    free (*arg);
    *arg = strdup (s);
  }
  free (s);    
  
  return bad_arg;
}

int typedef_handler (parse_info_t *pi, char *rp_name, type_t *t, 
		     int show_emsg, enum ARG_CONTEXT context, char **arg) {
  char *p, *s;
  int bad_arg = 0, i;
  char ebuf[1024];
  int (*arg_handler)(parse_info_t *, char *, type_t *, int, enum ARG_CONTEXT, char **);
  
  fprintf (dfile, "typedef_handler () arg=(%s) list=(%d)\n", *arg, t->list);
  
  /* Check for list arg type with '**arg' and vice-versa */
  if (!arg_type_check (pi, rp_name, t, show_emsg, context, arg))
    return 1;
  
  /* Dupe the arg list.  Return list intact if argument check fails */
  if (*arg != NULL)
    s = strdup (*arg);
  else
    return 0;
  
  if (t->u.t.t->type == PREDEFINED)
    arg_handler = &predef_handler;
  else if (t->u.t.t->type == TYPEDEF)
    arg_handler = &typedef_handler;
  else if (t->u.t.t->type == UNION)
    arg_handler = &union_handler;
  else {
    fprintf (dfile, "typedef_handler () unknown type (%d)\n", t->u.t.t->type);
    return 1;
  }

  if (t->list) {
    /* for each element in the list, pass it to the argument handler */
    for (i = 0; !bad_arg && (p = find_arg (&s, t->list)) && *p != '\0'; i++, free (p)) {
      if (!(bad_arg = (t->max_elem > 0 && i >= t->max_elem)))
	bad_arg = (*arg_handler) (pi, rp_name, t->u.t.t, show_emsg, context, &p);
      else if (show_emsg) {
	sprintf (ebuf, "RP attr (%s) number of args (%d) exceeds maximum threshold (%d)", rp_name, i, t->max_elem);
	error_msg_queue (pi, ebuf, ERROR_MSG);
      }
    }
  }
  else /* pass a non-list argument to the argument handler */
    bad_arg = (*arg_handler) (pi, rp_name, t->u.t.t, show_emsg, context, &s);
  
  /* See if the minimum elements threshold is satisfied */
  if (!bad_arg                                                    && 
      (bad_arg = (t->list && t->max_elem > 0 && t->min_elem > i)) &&
      show_emsg) {
    sprintf (ebuf, "RP attr (%s) actual arguments (%d) is less than minimum arguments threshold (%d)", rp_name, i, t->min_elem);
    error_msg_queue (pi, ebuf, ERROR_MSG);
  }

  /* if the arg is good, reset the original arg list
   * with this arg removed
   */
  if (!bad_arg) {
    free (*arg);
    *arg = strdup (s);
  }
  free (s);    
  
  return bad_arg;
}



int valid_args (parse_info_t *pi, method_t *m, int add_braces, 
		char *rp_attr, int show_emsg, char **args) {
  char *s;
  int bad_arg;
  param_t  *parm;
  method_t *meth;
  char ebuf[1024];

  /* dup input string to support object canonicalization */
  s = my_strdup (add_braces, *args);

  if (m->type == UNION_OF_METHODS) {
    for (bad_arg = 1, meth = m->umeth; meth != NULL && bad_arg; meth = meth->next)
      bad_arg = valid_args (pi, meth, 0, rp_attr, 0, &s);

    if (bad_arg) {
      sprintf (ebuf, "\"%s\" method \"%s\" argument does not match any of the specified types", rp_attr, m->name);
      error_msg_queue (pi, ebuf, ERROR_MSG);      
    }
  }
  else {
    if (m->context == LIST_TYPE && *s != '{') {
      if (show_emsg) {
	sprintf (ebuf, "RP attribute \"%s\" list attribute expected", rp_attr);
	error_msg_queue (pi, ebuf, ERROR_MSG);
      }
      return 0;
    }

    for (bad_arg = 0, parm = m->ll; parm != NULL && !bad_arg; parm = parm->next) {
      
      switch (parm->t->type) {
      case PREDEFINED:
	bad_arg = predef_handler  (pi, rp_attr, parm->t, show_emsg, m->context, &s);
	break;
      case TYPEDEF:
	bad_arg = typedef_handler (pi, rp_attr, parm->t, show_emsg, m->context, &s);
	break;
      case UNION:
	bad_arg = union_handler   (pi, rp_attr, parm->t, show_emsg, m->context, &s);
	break;
      default:
	break;
      }
      
      if (empty_string (s))
	break;
    }

    if (!bad_arg) {
      if (parm != NULL && parm->next != NULL) {
	bad_arg = 1;
	if (show_emsg) {
	  sprintf (ebuf, "RP-attribute \"%s\", not enough arguments for method \"%s\"", 
		   rp_attr, m->name);
	  error_msg_queue (pi, ebuf, ERROR_MSG);
	}
      }
      else if (!empty_string (s)) {
	bad_arg = 1;
	if (show_emsg) {
	  sprintf (ebuf, "RP-attribute \"%s\", too many arguments for method \"%s\"", 
		   rp_attr, m->name);
	  error_msg_queue (pi, ebuf, ERROR_MSG);
	}
      }
    }
  }

  free (s);

  return !bad_arg;
}

