
 
/* Copyright (c) 1991, 1992 Cornell University.  All Rights Reserved.
 *
 * Use, reproduction and preparation of derivative works of this software is
 * permitted.  Any copy of this software or of any derivative work must
 * include both the above copyright notices of Cornell University and this
 * paragraph.  This software is made available AS IS, and Cornell University
 * disclaims all warranties, express or implied.
 */

 
/* Greg Klanderman  Spring 1991 */
 
/* clarg.c: powerful command line arg interface */

/* Definitely overkill, but provided some amusement */
/* at an otherwise dull summer job */
 
 
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>

extern int fprintf(FILE *stream, const char *format, ...);
#if 0
extern int sscanf(char *p, const char *format, ...);
#endif

#include "clarg.h"
 
 
typedef struct	/* store parsed command line */
{
  char *name;	/* option name */
  long num;	/* number of arguments specified */
  char **aargs;	/* array [0..num-1] of arg strings; or NULL if option */
} ARG;			/* was never encountered. */
 

static bool verbose = FALSE;
static bool debug   = FALSE;

static long size;	/* size of parsed command line */
static ARG *args;	/* store parsed cmd line args[0..size-1] */
static long called_before = 0;		/* make sure only called once */
static CLINIT *g_inits;
 
static void *mymalloc(long i);
 
/***************************************************************************/
/****   Dependency parser **************************************************/
/***************************************************************************/
 
 
typedef struct { long ty;
		 union { long n;
			 char *s;
		       } u;
	       } value;
 
static value valuezero;
static value valueone;
 
#define isstr(v)	((v).ty == 1)
#define isint(v)	((v).ty == 0)
#define integer(v)	((v).u.n)
#define string(v)	((v).u.s)
#define setint(v, x)	((v).ty = 0, (v).u.n = x)
#define setstr(v, x)	((v).ty = 1, (v).u.s = x)
 
#define lookupvar(name, pval)  vardispatch(0, name, pval)
#define setvar(name, val)      vardispatch(1, name, &val)
#define killvar(name)          vardispatch(2, name, NULL)
#define makevar(name)          vardispatch(3, name, NULL)
 
static long  cl_ev_err = 0;
static long  cl_ev_arg = 0;
 
 
#define parse(str)    ((void) input_dispatch(0, str,  0))
#define curr()	      ((char) input_dispatch(1, NULL, 0))
#define next(x)	      ((char) input_dispatch(2, NULL, x))
#define adv(x)	      ((void) input_dispatch(3, NULL, x)) 
#define save()	      ((long) input_dispatch(4, NULL, 0))
#define revert(x)     ((void) input_dispatch(5, NULL, x))
 
 
#define MAXVARS 200
 
 
static bool vardispatch(int op, char *name, value *val)
{
  static struct { char *name;
		  value value;
		} varlist[MAXVARS];
  static long numvars = 0;
  long i;
 
  if (op == 0)			/* lookup */
  {
    for (i = numvars - 1; i >= 0; i--)
      if (strcmp(varlist[i].name, name) == 0)
      {
	*val = varlist[i].value;
	return 0;
      }
 
    cl_ev_err = 80;
    return 1;
  }
  else if (op == 1)		/* set */
  {
    for (i = numvars - 1; i >= 0; i--)
      if (strcmp(varlist[i].name, name) == 0)
      {
	varlist[i].value = *val;
	return 0;
      }
 
    cl_ev_err = 81;
    return 1;
  }
  else if (op == 2)		/* kill var */
  {
    long j;
    
    for (i = numvars - 1; i >= 0; i--)
      if (strcmp(varlist[i].name, name) == 0)
	break;
 
    if (i <= -1)
    {
      cl_ev_err = 82;
      return 1;
    }
 
    numvars--;
    free(varlist[i].name);
    for (j = i; j < numvars; j++)
    {
      varlist[j].name = varlist[j+1].name;
      varlist[j].value = varlist[j+1].value;
    }
    return 0;
  }
  else if (op == 3)		/* make, set = 0 */
  {
    if (numvars >= MAXVARS)
    {
      cl_ev_err = 83;
      return 1;
    }
 
    varlist[numvars].name = (char *) mymalloc(strlen(name) + 1);
    strcpy(varlist[numvars].name, name);
    varlist[numvars].value = valuezero;
    numvars++;
    return 0;
  }
  else				/* err */
    return 1;
}
#undef MAXVARS
 
 
static long input_dispatch(long op, char *str, long x)
{
  static char *cl_ev_str;
  static long  cl_ev_n;
 
  if (op == 0)			/* set up to parse str */
  {
    cl_ev_str = str;
    cl_ev_n = 0;
    return (0);
  }
  else if (op == 1)		/* curr */
  {
    return ((long) cl_ev_str[cl_ev_n]);
  }
  else if (op == 2)		/* next(x) */
  {
    return ((long) cl_ev_str[cl_ev_n+x]);
  }
  else if (op == 3)		/* adv(x) */
  {
    cl_ev_n += x;
    return 0;
  }
  else if (op == 4)		/* save() */
  {
    return (cl_ev_n);
  }
  else if (op == 5)		/* revert(x) */
  {
    cl_ev_n = x;
    return 0;
  }
  else
  {
    if (verbose)
      fprintf(stderr, "CLARG: input_dispatch given illegal option\n");
    exit(0);
  }
}
 
 
static void skipspace(void)
{
  while (isspace(curr()))  adv(1);
}
 
 
 
static value eval_unary(void);
static value eval_mdr(void);
static value eval_as(void);
static value eval_compare(void);
static value eval_equal(void);
static value eval_and(void);
static value eval_xor(void);
static value eval_or(void);
static value eval_impl(void);
static value eval_statms(void);
static value eval_if(void);
static value eval_for(int dirn);
static value eval_expr(void);
static value eval_semi(void);
 
bool getvarname(char *os);
bool getoptname(char *os);
 
 
static value eval_semi(void)		/* same as && but lowest precedence and no nesting */
{
  value t, t2;
 
  t = eval_expr();
  if (cl_ev_err)    return valuezero;
  while (1)
  {
    skipspace();
    if (curr() == ';')
    {
      adv(1);
 
      t2 = eval_expr();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) && integer(t2));
      else
      {
	cl_ev_err = 10;
	return valuezero;
      }
    }
    else  break;
  }
 
  return t;
}
 
static value eval_expr(void)
{
  if (curr() == 'i' && next(1) == 'f')
    return (eval_if());
 
  if (curr() == 'f' && next(1) == 'o' && next(2) == 'r')
    return (eval_for(0));
 
  if (curr() == 'r' && next(1) == 'o' && next(2) == 'f')
    return (eval_for(1));
 
  return (eval_impl());
}
 
 
static value eval_for(int reverse)
{
  value t;
  char  opt_name[201], var_name[201];
  long index, loop_top, i, count;
  value ind_val;
  
 
  adv(3);			/* skip "for" or "rof" */
  skipspace();
 
  if (curr() != '(')
  {
    cl_ev_err = 100;
    return valuezero;
  }
 
  adv(1);
  if (getoptname(opt_name))  return valuezero;
  skipspace();
  if (curr() != ':')
  {
    cl_ev_err = 101;
    return valuezero;
  }
 
  adv(1);
  if (getvarname(var_name)) return valuezero;
 
  skipspace();
  if (curr() != ')')
  {
    cl_ev_err = 102;
    return valuezero;
  }
  adv(1);
 
  skipspace();
  if (curr() != '{')
  {
    cl_ev_err = 102;
    return valuezero;
  }
  adv(1);
 
  loop_top = save();
  if (makevar(var_name))  return valuezero;
 
  if (strcmp(opt_name, "UNNAMED") == 0)
    i = 0;
  else
    for (i = 1; i < size; i++)
      if (strcmp(opt_name, args[i].name) == 0)
	break;
 
  if (i >= size)		/* option dne */
  {
    cl_ev_err = 103;
    return valuezero;
  }
 
  if (args[i].aargs == NULL)	/* not specified */
  {
    cl_ev_err = 104;
    return valuezero;
  }
 
  if (args[i].num == 0)		/* no args to iterate over */
  {
    for (count = 1; curr() != '\0' && count > 0; adv(1)) /* skip to closing '}' */
    {
      if (curr() == '{')
	count++;
      else if (curr() == '}')
	count--;
    }
 
    if (count != 0)		/* terminated before matching '}' */
    {
      cl_ev_err = 105;
      return valuezero;
    }
 
    return valueone;
  }
 
  index = (reverse) ? (args[i].num - 1) : 0;
  setint(ind_val, index);
  if (setvar(var_name, ind_val)) return valuezero;
  while (index >= 0 && index < args[i].num)
  {
    revert(loop_top);
 
    t = eval_statms();
 
    skipspace();
 
    if (curr() != '}')
    {
      cl_ev_err = 106;
      return valuezero;
    }
 
    adv(1);
 
    if (reverse)  index--;
    else	  index++;
    setint(ind_val, index);
    if (setvar(var_name, ind_val)) return valuezero;
  }
 
  return t;
}
 
static value eval_if(void)
{
  value t, t2;
  int count;
  int do_if;
 
  
  adv(2);
  skipspace();
 
  if (curr() != '(')		/* error */
  {
    cl_ev_err = 30;
    return valuezero;
  }
  
  adv(1);
  t = eval_expr();
  if (cl_ev_err)	return valuezero;
 
  skipspace();
 
  if (curr() != ')')		/* error */
  {
    cl_ev_err = 31;
    return valuezero;
  }
  
  adv(1);
  
  if (!isint(t))		/* condition expression must be integer */
  {
    cl_ev_err = 32;
    return valuezero;
  }
  
  skipspace();
  do_if = integer(t);
 
  if (do_if)			/* do 'if' statements and skip 'else' */
  {
    if (curr() != '{')
    {
      cl_ev_err = 33;
      return valuezero;
    }
    
    adv(1);
    t2 = eval_statms();
    if (cl_ev_err)	return valuezero;
    
    skipspace();
 
    if (curr() != '}')
    {
      cl_ev_err = 34;
      return valuezero;
    }
 
    adv(1);
    skipspace();
 
    if (curr() == 'e' && next(1) == 'l' && next(2) == 's' && next(3) == 'e')
    {				/* skip else part */
      adv(4);
      skipspace();
 
      if (curr()  != '{')
      {
	cl_ev_err = 35;
	return valuezero;
      }
 
      adv(1);
      for (count = 1; curr() != '\0' && count > 0; adv(1))
      {
	if (curr() == '{')
	  count++;
	else if (curr() == '}')
	  count--;
      }
 
      if (count != 0)		/* terminated before matching '}' */
      {
	cl_ev_err = 36;
	return valuezero;
      }
    }
 
    return t2;
  }
  else				/* do 'else' and skip 'if' */
  {
    if (curr()  != '{')
    {
      cl_ev_err = 37;
      return valuezero;
    }
 
    adv(1);
    for (count = 1; curr() != '\0' && count > 0; adv(1))
    {
      if (curr() == '{')
	count++;
      else if (curr() == '}')
	count--;
    }
 
    if (count != 0)		/* terminated before matching '}' */
    {
      cl_ev_err = 38;
      return valuezero;
    }
    
    skipspace();
    if (!(curr() == 'e' && next(1) == 'l' && next(2) == 's' && next(3) == 'e'))
    {
      return valueone;		/* no else clause => return one */
    }
 
    adv(4);
    skipspace();
    
    if (curr() != '{')
    {
      cl_ev_err = 39;
      return valuezero;
    }
    
    adv(1);
    t2 = eval_statms();
    if (cl_ev_err)	return valuezero;
    
    skipspace();
 
    if (curr() != '}')
    {
      cl_ev_err = 40;
      return valuezero;
    }
 
    adv(1);
 
    return t2;
  }
}
 
 
static value eval_statms(void)	/* returns value of last expression */
{
  value t;
 
  t = eval_expr();
  if (cl_ev_err)	return valuezero;
 
  while (1)
  {
    skipspace();
 
    if (curr() == ';')
    {
      adv(1);
      continue;
    }
    
    if (curr() == '}')		/* found end of statement list */
      return t;
 
    t = eval_expr();
    if (cl_ev_err)	return valuezero;
  }
}
 
 
static value eval_impl(void)
{
  value t, t2;
 
  t = eval_or();
  if (cl_ev_err)	return valuezero;
  while (1)
  {
    skipspace();
    if (curr() == '=' && next(1) == '>')
    {
      adv(2);
 
      t2 = eval_or();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (!integer(t) || integer(t2));
      else
      {
	cl_ev_err = 11;
	return valuezero;
      }
    }
    else  break;
  }
 
  return t;
}
 
static value eval_or(void)
{
  value t, t2;
 
  t = eval_xor();
  if (cl_ev_err)	return valuezero;
  while (1)
  {
    while (isspace(curr()))	adv(1);
    if (curr() == '|')
    {
      adv(1);
      if (curr() == '|')	adv(1);
 
      t2 = eval_xor();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) || integer(t2));
      else
      {
	cl_ev_err = 12;
	return valuezero;
      }
    }
    else  break;
  }
 
  return t;
}
 
static value eval_xor(void)
{
  value t, t2;
 
  t = eval_and();
  if (cl_ev_err)	return valuezero;
  while (1)
  {
    while (isspace(curr()))	adv(1);
    if (curr() == '^')
    {
      adv(1);
      t2 = eval_and();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = ((integer(t) && !integer(t2)) || (!integer(t) && integer(t2)));
      else
      {
	cl_ev_err = 13;
	return valuezero;
      }
    }
    else  break;
  }
 
  return t;
}
 
static value eval_and(void)
{
  value t, t2;
 
  t = eval_equal();
  if (cl_ev_err)	return valuezero;
  while (1)
  {
    while (isspace(curr()))	adv(1);
    if (curr() == '&')
    {
      adv(1);
      if (curr() == '&')	adv(1);
 
      t2 = eval_equal();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) && integer(t2));
      else
      {
	cl_ev_err = 14;
	return valuezero;
      }
    }
    else  break;
  }
 
  return t;
}
 
 
 
static value eval_equal(void)
{
  value t, t2;
 
  t = eval_compare();
  if (cl_ev_err)	return valuezero;
  while (1)
  {
    skipspace();
    if (curr() == '=' && next(1) == '=')
    {
      adv(2);
 
      t2 = eval_compare();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) == integer(t2));
      else if (isstr(t) && isstr(t2))
	setint(t, (strcmp(string(t), string(t2)) == 0));
      else
      {
	cl_ev_err = 15;
	return valuezero;
      }
    }
    else if (curr() == '!' && next(1) == '=')
    {
      adv(2);
 
      t2 = eval_compare();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) != integer(t2));
      else if (isstr(t) && isstr(t2))
	setint(t, (strcmp(string(t), string(t2)) != 0));
      else
      {
	cl_ev_err = 16;
	return valuezero;
      }
    }
    else  break;
  }
 
  return t;
}
 
static value eval_compare(void)
{
  value t, t2;
 
  t = eval_as();
  if (cl_ev_err)	return valuezero;
  while (1)
  {
    if (curr() == '>' && next(1) == '=')
    {
      adv(2);
 
      t2 = eval_as();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) >= integer(t2));
      else if (isstr(t) && isstr(t2))
	setint(t, (strcmp(string(t), string(t2)) >= 0));
      else
      {
	cl_ev_err = 17;
	return valuezero;
      }
    }
    else if (curr() == '<' && next(1) == '=')
    {
      adv(2);
 
      t2 = eval_as();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) <= integer(t2));
      else if (isstr(t) && isstr(t2))
	setint(t, (strcmp(string(t), string(t2)) <= 0));
      else
      {
	cl_ev_err = 18;
	return valuezero;
      }
    }
    else if (curr() == '>')
    {
      adv(1);
 
      t2 = eval_as();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) > integer(t2));
      else if (isstr(t) && isstr(t2))
	setint(t, (strcmp(string(t), string(t2)) > 0));
      else
      {
	cl_ev_err = 19;
	return valuezero;
      }
    }
    else if (curr() == '<')
    {
      adv(1);
 
      t2 = eval_as();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) < integer(t2));
      else if (isstr(t) && isstr(t2))
	setint(t, (strcmp(string(t), string(t2)) < 0));
      else
      {
	cl_ev_err = 20;
	return valuezero;
      }
    }
    else  break;
  }
 
  return t;
}
 
 
static value eval_as(void)
{
  value t, t2;
 
  t = eval_mdr();
  if (cl_ev_err)	return valuezero;
  while (1)
  {
    while (isspace(curr()))	adv(1);
    if (curr() == '+')
    {
      adv(1);
 
      t2 = eval_mdr();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) + integer(t2));
      else
      {
	cl_ev_err = 21;
	return valuezero;
      }
    }
    else if (curr() == '-')
    {
      adv(1);
 
      t2 = eval_mdr();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) - integer(t2));
      else
      {
	cl_ev_err = 22;
	return valuezero;
      }
    }
    else  break;
  }
 
  return t;
}
 
 
 
 
static value eval_mdr(void)
{
  value t, t2;
 
  t = eval_unary();
  if (cl_ev_err)	return valuezero;
  while (1)
  {
    while (isspace(curr()))	adv(1);
    if (curr() == '*')
    {
      adv(1);
 
      t2 = eval_unary();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) * integer(t2));
      else
      {
	cl_ev_err = 23;
	return valuezero;
      }
    }
    else if (curr() == '/')
    {
      adv(1);
 
      t2 = eval_unary();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) / integer(t2));
      else
      {
	cl_ev_err = 24;
	return valuezero;
      }
    }
    else if (curr() == '%')
    {
      adv(1);
 
      t2 = eval_unary();
      if (cl_ev_err)	return valuezero;
 
      if (isint(t) && isint(t2))
	integer(t) = (integer(t) % integer(t2));
      else
      {
	cl_ev_err = 25;
	return valuezero;
      }
    }
    else if (curr() == '.')	/* concatenate strings */
    {
      char *ts;
      
      adv(1);
 
      t2 = eval_unary();
      if (cl_ev_err)	return valuezero;
 
      if (isstr(t) && isstr(t2))
      {
	ts = (char *) mymalloc(strlen(string(t)) + strlen(string(t2)) + 1);
	strcpy(ts, string(t));
	strcat(ts, string(t2));
	free(string(t));
	free(string(t2));
	string(t) = ts;
      }
      else
      {
	cl_ev_err = 25;
	return valuezero;
      }
    }
    else  break;
  }
 
  return t;
}
 
 
 
 
bool getvarname(char *os)
{
  long i;
 
 
  if (curr() != '_')
  {
    cl_ev_err = 83;
    return 1;
  }
  
  for (i = 0; (isalpha(curr()) || isdigit(curr()) || curr() == '_'); adv(1))
    os[i++] = curr();
  os[i] = '\0';
 
  return 0;
}
 
bool getoptname(char *os)
{
  long i;
  
  if (curr() == '\'')		/* quoted */
  {
    adv(1);
 
    skipspace();
    for (i = 0; curr() != '\0' && curr() != '\'' && !isspace(curr()); adv(1))
      os[i++] = curr();
    os[i] = '\0';
 
    skipspace();
    if (curr() != '\'')
    {
      cl_ev_err = 1;		/* no matching ' */
      return 1;
    }
 
    adv(1);
    return 0;
  }
 
  /* unquoted: 1st char must be letter, and rest must be letter or number or . */
 
  if (!isalpha(curr()))
  {
    cl_ev_err = 70;
    return 1;
  }
  
  for (i = 0; (isalpha(curr()) || isdigit(curr()) || curr() == '.'); adv(1))
    os[i++] = curr();
  os[i] = '\0';
 
  return 0;
}
 
 
 
bool getspecified(char *os, value *t)
{
  long i;
 
  if (strcmp(os, "UNNAMED") == 0) /* "specified" iff num in {min..max} */
  {
    setint(*t, (args[0].num >= g_inits[0].min));
    return 0;
  }
  
  for (i = 1; i < size; i++)	/* find if option specified */
    if (strcmp(os, args[i].name) == 0)
    {
      setint(*t, (args[i].aargs != NULL));
      return 0;
    }
 
  setint(*t, 0);
  cl_ev_err = 2;
  return 1;			/* error - not found */
}	
 
 
bool getvalue(char *os, value *t)
{
  long i;
  
  if (strcmp(os, "UNNAMED") == 0)
  {
    setint(*t, args[0].num);
    return 0;
  }
      
  for (i = 1; i < size; i++)	/* find if option specified */
    if (strcmp(os, args[i].name) == 0)
    {
      setint(*t, (args[i].aargs != NULL) ? args[i].num : -1);
      return 0;
    }
 
  setint(*t, 0);
  cl_ev_err = 3;
  return 1;			/* error - not found */
}	


static value eval_delopt(void)
{
  value t;
  char os[201];
    
  adv(1);
    
  if (getoptname(os))
    return valuezero;
 
  skipspace();
 
  if (curr() == '[')		/* delete and return an argument */
  {
    int n, i, j;
    char *str;
 
      
    adv(1);
    t = eval_expr();
    if (cl_ev_err)	return valuezero;
 
    if (!isint(t))
    {
      cl_ev_err = 51;
      return valuezero;
    }
      
    skipspace();
    if (curr() != ']')
    {
      cl_ev_err = 52;
      return valuezero;
    }
 
    adv(1);
    n = integer(t) - 1;                      /* 1 based user index -> 0 based internal */
 
    if (n < 0)
    {
      cl_ev_err = 53;
      return valuezero;
    }
 
    if (strcmp(os, "UNNAMED") == 0) 
      i = 0;
    else
      for (i = 1; i < size; i++)
	if (strcmp(os, args[i].name) == 0)
	  break;
 
    if (i >= size)
    {
      cl_ev_err = 55;
      return valuezero;
    }
      
    if (n >= args[i].num)
    {
      cl_ev_err = 54;
      return valuezero;
    }
 
    str = (char *) mymalloc(strlen(args[i].aargs[n]) + 1);
    strcpy(str, args[i].aargs[n]);
      
    free(args[i].aargs[n]);
    (args[i].num)--;
    for (j = n; j < args[i].num; j++)
      args[i].aargs[j] = args[i].aargs[j+1];
    args[i].aargs[j] = NULL;
 
    setstr(t, str);
    return t;
  }
  else				/* delete whole option, return 1 */
  {
    int i, j;
 
    if (strcmp(os, "UNNAMED") == 0) 
      i = 0;
    else
      for (i = 1; i < size; i++)
	if (strcmp(os, args[i].name) == 0)
	  break;
 
    if (i >= size)
    {
      cl_ev_err = 56;
      return valuezero;
    }
 
    if (args[i].aargs == NULL)
      return valueone;
      
    for (j = 0; j < args[i].num; j++)
      free(args[i].aargs[j]);
 
    args[i].num = 0;
    free(args[i].aargs);
    args[i].aargs = NULL;
      
    return valueone;
  }
}



static value eval_addopt(void)
{
  value t, t2;
  char os[201];
    
  adv(1);
    
  if (getoptname(os))
    return valuezero;
 
  skipspace();
 
  if (curr() == '[')		/* add and return new argument */
  {
    int n, i, j;
    char *str1, *str2, *str3;
 
      
    adv(1);
    t = eval_expr();		/* string: arg string to add */
    if (cl_ev_err)	return valuezero;
 
    if (!isstr(t))
    {
      cl_ev_err = 71;
      return valuezero;
    }
 
    skipspace();
    if (curr() != ':')
    {
      cl_ev_err = 72;
      return valuezero;
    }
      
    adv(1);
    t2 = eval_expr();		/* number: arg number to add */
    if (cl_ev_err)	return valuezero;
 
    if (!isint(t2))
    {
      cl_ev_err = 73;
      return valuezero;
    }
      
    skipspace();
    if (curr() != ']')
    {
      cl_ev_err = 74;
      return valuezero;
    }
 
    adv(1);
    str1 = string(t);
    n = integer(t2) - 1;                     /* convert from user 1 based index to internal */
 
    if (n < 0)
    {
      cl_ev_err = 75;
      return valuezero;
    }
 
    if (strcmp(os, "UNNAMED") == 0) 
      i = 0;
    else
      for (i = 1; i < size; i++)
	if (strcmp(os, args[i].name) == 0)
	  break;
 
    if (i >= size)
    {
      cl_ev_err = 76;
      return valuezero;
    }
 
    if (args[i].aargs == NULL)
    {
      if (g_inits[i].max == 0)
      {
	cl_ev_err = 77;		/* cant add an argument */
	return valuezero;
      }
      else
      {
	args[i].aargs = (char **) mymalloc(sizeof(char *)*g_inits[i].max);
	args[i].num = 0;
      }
    }
    
    if (n > args[i].num || args[i].num >= g_inits[i].max)
    {
      cl_ev_err = 78;
      return valuezero;
    }
 
    str2 = mymalloc(strlen(str1 + 1));
    strcpy(str2, str1);
    str3 = mymalloc(strlen(str1 + 1));
    strcpy(str3, str1);
      
    free(str1);
    for (j = args[i].num; j > n; j--)
      args[i].aargs[j] = args[i].aargs[j-1];
    args[i].aargs[n] = str2;
    (args[i].num)++;
 
    setstr(t, str3);
    return t;
  }
  else				/* add the option, return 1 */
  {
    int i;
 
    if (strcmp(os, "UNNAMED") == 0) 
      i = 0;
    else
      for (i = 1; i < size; i++)
	if (strcmp(os, args[i].name) == 0)
	  break;
 
    if (i >= size)
    {
      cl_ev_err = 79;
      return valuezero;
    }
 
    if (args[i].aargs != NULL)	/* already exists */
      return valueone;
 
    if (g_inits[i].max == 0)	/* pure option */
      args[i].aargs = (char **) 1;
    else
      args[i].aargs = (char **) mymalloc(sizeof(char *)*g_inits[i].max);
    
    args[i].num = 0;
      
    return valueone;
  }
}



static value eval_unary(void)
{
  value t;
  char c;
  char os[201];
    
 
  skipspace();
 
  c = curr();
 
  if ((c == '!' && next(1) != '=') || c == '~')
  {
    adv(1);
    t = eval_unary();
    if (cl_ev_err)	return valuezero;
 
    if (isint(t))
    {
      integer(t) = (!integer(t));
      return(t);
    }
    else
    {
      cl_ev_err = 26;
      return valuezero;
    }
  }
 
  if (c == '-')
  {
    adv(1);
    t = eval_unary();
    if (cl_ev_err)	return valuezero;
 
    if (isint(t))
    {
      integer(t) = (-integer(t));
      return(t);
    }
    else
    {
      cl_ev_err = 27;
      return valuezero;
    }
  }
 
  if (c == '?')
  {
    adv(1);
    setint(t, cl_ev_arg);
    return (t);
  }
  
  if (c == '(')
  {
    adv(1);
    t = eval_expr();
    if (cl_ev_err)	return valuezero;
    skipspace();
    if (curr() == ')')
    {
      adv(1);
      return t;
    }
    else
    {
      cl_ev_err = 4;
      return valuezero;
    }
  }
 
  if (isdigit(c))
  {
    long it = 0;
    
    while (isdigit(curr()))
    {
      it = 10*it + curr() - '0';
      adv(1);
    }
 
    setint(t, it);
    return (t);
  }
 
  if (c == '\"')		/* string literal */
  {
    long i;
 
    adv(1);
    for (i = 0; i < 200 && curr() != '\0' && curr() != '\"'; adv(1))
      os[i++] = curr();
    os[i] = '\0';
 
    if (curr() != '\"')
    {
      cl_ev_err = 50;
      return valuezero;
    }
 
    adv(1);
 
    setstr(t, (char *) mymalloc(strlen(os) + 1));
    strcpy(string(t), os);
    return t;
  }
 
  if (c == '#')
  {
    adv(1);
    
    if (getoptname(os))
      return valuezero;
 
    if (getvalue(os, &t))
      return valuezero;
 
    return t;
  }
 
  if (c == '$')			/* delete */
    return (eval_delopt());
  
  if (c == '@')			/* add, don't exceed max */
    return (eval_addopt());
  
  if (c == '_')			/* variable name */
  {
    if (getvarname(os))
      return valuezero;
 
    if (lookupvar(os, &t))
      return valuezero;
    
    return t;
  }
  
  if (getoptname(os))
    return valuezero;
 
  /* check for [] reference */
 
  skipspace();
 
  if (curr() == '[')		/* argument reference for option name */
  {
    int i, n;
    char *s;
    
    adv(1);
 
    t = eval_expr();
 
    if (cl_ev_err) return valuezero;
 
    skipspace();
    if (curr() != ']')
    {
      cl_ev_err = 90;
      return valuezero;
    }
 
    adv(1);
 
    if (!isint(t))
    {
      cl_ev_err = 91;
      return valuezero;
    }
 
    n = integer(t) - 1;                      /* user 1 based -> internal 0 based */
 
    if (strcmp(os, "UNNAMED") == 0)
      i = 0;
    else
      for (i = 1; i < size; i++)
	if (strcmp(os, args[i].name) == 0)
	  break;
 
    if (i >= size)
    {
      cl_ev_err = 92;
      return valuezero;
    }
 
    if (args[i].aargs == NULL || n < 0 || n >= args[i].num)
    {
      cl_ev_err = 93;
      return valuezero;
    }
 
    s = (char *) mymalloc(strlen(args[i].aargs[n]) + 1);
    strcpy(s, args[i].aargs[n]);
    setstr(t, s);
    return t;
  }
  
  if (getspecified(os, &t))	/* otherwise, just see if option is  specified */
    return valuezero;
 
  return t;
}
 
 
static long cl_depend_eval(char *s, long arg)
{
  value result;
 
  parse(s);			/* set s to be parsed */
  cl_ev_arg = arg;
  cl_ev_err = 0;
  setint(valuezero, 0);
  setint(valueone,  1);
 
  result = eval_semi();
 
  if (cl_ev_err)  	return 0;
  
  while (isspace(curr()))	adv(1);
  if (curr() != 0)
  {
    cl_ev_err = 5;
    return 0;
  }
 
  return isint(result) ? integer(result) : 1;
}
 
#undef parse
#undef curr
#undef next
#undef adv
#undef save
#undef revert
 
#undef isstr
#undef isint
#undef integer
#undef string
#undef setint
#undef setstr
 
#undef lookupvar
#undef setvar
#undef killvar
#undef makevar
 
 
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
 
  
 
char *cl_notype(char *s, long n)	/* does no type check */
{
  return (s);
}
 
char *cl_real(char *s, long n)	/* does DOUBLE type check */
{
  int i;
  double d;
  char *ss;
  
  if (*s == '_')			     /* use these so negs aren't */
    *s = '-';				     /* confused with an option! */
  
  if (sscanf(s, "%lf%n", &d, &i) != 1)
    return (NULL);
 
  ss = s + i;	/* one past end of converted part */
 
  while (*ss != '\0' && isspace(*ss))
    ss++;
 
  return ((*ss == '\0') ? (s) : (NULL));
}
 
 
char *cl_int(char *s, long n)	/* does LONG type check */
{
  int i;
  long l;
  char *ss;
  
 
  if (*s == '_')			     /* use these so negs aren't */
    *s = '-';				     /* confused with an option! */
  
  if (sscanf(s, "%ld%n", &l, &i) != 1)
    return (NULL);
 
  ss = s + i;	/* one past end of converted part */
 
  while (*ss != '\0' && isspace(*ss))	/* allow trailing space */
    s++;
 
  return ((*ss == '\0') ? (s) : (NULL));
}
 
 
 
static bool badopnam(char *s)	/* tell if an option name is bad */
{
  long p;
 
  if (s == NULL)  return 0;	/* NULL is ok option name */
  if (*s == '\0') return 1;	/* empty string is bad */
  if (*s == '.')  return 1;	/* period can't be first => bad */
 
  for (p = 0; *s; s++)		/* count periods */
    if (*s == '.') p++;
 
  return (p > 1);	/* more than one period => bad */
}
 
 
 
static long opnamlen(char *s)  	/* gives len of prefix of option name */
{
  long i;
 
  if (s == NULL)
    return (-1);	/* NULL shorter than anything */
 
  for (i = 0; s[i] != '\0' && s[i] != '.'; i++) ;
 
  return (i);
}
 
 
static bool opnammatch(char *opn, char *s, long *pj)
{	/* tells if s matches option name opn, returns match len in pj */
  long i, j;
  bool match;
 
  for (i = 0, j = 0, match = 0; opn[i] && s[j] && opn[i] == s[j]; )
  {
    i++, j++;
 
    if (opn[i] == '.')
      match = 1, i++;
  }
 
  *pj = j;	  /* return length of s matching opn */
 
  if (opn[i] == '\0')
    return (1);
  else
    return (match);
}
 
 
static long opnamcmp(char *a, char *b)	/* compare two option names */
{
  if (a == NULL) return (-1);
  if (b == NULL) return (1);
  
  while (*a && *b && *a == *b)
  {
    a++, b++;
 
    if (*a == '.')
      a++;
 
    if (*b == '.')
      b++;
  }
 
  return ((long) *a - *b);
}
 
 
 
 
static int cmp(const void *aa, const void *bb)	/* for qsorting the inits array */
{
  const CLINIT *a = (const CLINIT *) aa;
  const CLINIT *b = (const CLINIT *) bb;
  
  long len;	/* NULL comes first, then by strlen, then strcmp */
 
  if ((a->name == NULL) && (b->name == NULL)) return (0);
  if (a->name == NULL) return (-1);
  if (b->name == NULL) return (1);
 
  len = opnamlen(b->name) - opnamlen(a->name);
  if (len != 0) return (len);
 
  return (opnamcmp(a->name, b->name));
}
 
 
 
static void *mymalloc(long i)	/* prints an error, and exits on error */
{
  void *p;
 
  p = (void *) malloc(i);
 
  if (p != NULL)
    return (p);

  fprintf(stderr, "CLARG: malloc returned NULL\n\n");
  exit(0);
}
 
 
/* this does all the work */
 
bool cl_init(long argc, char **argv, char **envp, CLINIT *inits,
	     char **depend, char *usage, long restrict)
{
  long  i, j, k;
  char *work;
  long  curr;
  long  max;
  long len;
  bool gotone;
  bool lastpure=0;
  static char cpy_arg[256];	/* arguments should be shorter than 256 chars */
  static char *chk_arg;
  long depend_argument;
  

  if (called_before)
  {
    fprintf(stderr, "CLARG: cl_init() called more than once\n");
    return (1);
  }
 
  called_before = 1;
 
  if (restrict != CL_NORESTRICT && restrict != CL_OPTPACK && restrict != CL_NOPACK)
  {
    fprintf(stderr, "CLARG: illegal restriction\n");
    return(1);
  }
  
  for (i = 0; inits[i].name != NULL; i++) 
    if (badopnam(inits[i].name))
    {
      fprintf(stderr, "CLARG: bad option name found: %s\n", inits[i].name);
      return (1);
    }
 
  i++;		/* the NULL is part of the array */
 
  size = i;     /* set size and get array to store parsed cmd line in */
  args = (ARG *) mymalloc(size * sizeof(ARG));	
						
  /* this sort determines the search order (longest prefix first) */
  qsort(inits, size, sizeof(CLINIT), cmp);
 
  for (i = 0; i < size; i++)	/* copy names and check for valid data */
  {
    if (inits[i].name == NULL)	/* can't have unnamed be type 2 */
      inits[i].type = CL_STDARG;
 
    if ((inits[i].min < 0) || (inits[i].min > inits[i].max))
    {
      fprintf(stderr, "CLARG: a min > a max or < 0 !\n");
      return (1);
    }
 
    if (inits[i].max == 0)	/* pure option => must be standard */
      inits[i].type = CL_STDARG;
    
    if (inits[i].type != CL_STDARG && inits[i].type != CL_XXXARG)
    {
      fprintf(stderr, "CLARG: bad option type for %s\n",
	      ((inits[i].name==NULL)?"UNNAMED":inits[i].name));
      return(1);
    }
 
    if (inits[i].typchk == NULL)		/* make sure there is a type chk fn */
      inits[i].typchk = cl_notype;
 
    if (inits[i].type == CL_XXXARG && inits[i].min == 0)
    {
      fprintf(stderr, "CLARG: min args must be > 0 for type 2 option %s\n", inits[i].name);
      return 1;
    }
    
    if (i == 0)
      args[i].name = inits[i].name;    	/* copy the NULL */
    else
    {
      args[i].name = (char *) mymalloc(strlen(inits[i].name) + 1);
      strcpy(args[i].name, inits[i].name);
    }
 
    args[i].num = 0;
    args[i].aargs = NULL;
  }
 
  for (i = 1; i < size-1; i++)		/* check for duplications */
    if (opnamcmp(args[i].name, args[i+1].name) == 0)
    {
      fprintf(stderr, "CLARG: option -%s duplicated in initializer\n", args[i].name);
      return (1);
    }
 
  i = 1;	/* start at argv[1] */
 
  if (i < argc && argv[i][0] == '#' && argv[i][1] == '#')
  {
    verbose = TRUE;
    if (argv[i][2] == 'd')
      debug = TRUE;
    i++;
  }

  depend_argument = -1;
  
  if (i < argc && argv[i][0] == '#')
  {
    int x;
 
    depend_argument = 0;
    for (x = 1; isdigit(argv[i][x]); x++)
      depend_argument = 10*depend_argument + argv[i][x] - '0';
    while (argv[i][x] == ' ')
      x++;
    if (argv[i][0] != '\0')
      depend_argument = -1;
    i++;
  }

  while (i < argc)	/* while more argv[]s to check */
  {
    j = i;
 
    while (i < argc && argv[i][0] != '-') /* make argv[i] next matched opt */
      i++;
 
    if ((i > j) && (args[0].aargs == NULL)) /* use a "safe" size instead of */
      args[0].aargs = (char **) mymalloc(argc*sizeof(char *)); /* inits[0].max */
 
    while (j < i)		/* add any unmatched options found */
    {
      long n = args[0].num;
 
      if (n + 1 > inits[0].max)
      {
        if (verbose)
          fprintf(stderr, "CLARG: too many unmatched arguments\n");
	goto error;
      }
 
      strncpy(cpy_arg, argv[j], 255);	     /* so we can use typchk as a filter */
      if ((chk_arg = (inits[0].typchk)(cpy_arg, n+1)) == NULL) /* illegal type for arg */
      {
        if (verbose)
          fprintf(stderr, "CLARG: bad type for unmatched arg [%s]\n", argv[j]);
	goto error;
      }
 
      args[0].aargs[n] = (char *) mymalloc(strlen(chk_arg) + 1);
      strcpy(args[0].aargs[n], chk_arg);
      args[0].num += 1;
      j++;
    }
 
    if (i >= argc)		/* argv exhausted => quit */
      break;
 
    work = argv[i];		/* get current working options string */
    i++;			/* goto next argv[] */
 
    gotone = FALSE;		/* haven't got an option out of WORK */
    while (*work != '\0')	/* get all options and args from work */
    {
      if (gotone && restrict == CL_NOPACK) /* check if full restiriction */
      {
        if (verbose)
          fprintf(stderr,
                  "CLARG: not allowed to have more than one option name per option string: %s\n",
                  work);
	goto error;
      }
      
      while (*work == '-') 	/* allow '-' in work */
	work++;			/* skip them */
 
      if (*work == '\0')
	continue;		/* make sure end not reached */
 
      for (k = 1; k < size; k++) /* find first option name */
      {
	char *s = args[k].name;
 
	if (opnammatch(s, work, &len)) /* found it */
	  break;
      }
 
      if (k >= size)		/* never found a match */
      {				/* if a "-!" or "-?" is specified print usage */
	if (*work != '!'  && *work != '?') {	/* without an error message */
          if (verbose)
            fprintf(stderr, "CLARG: illegal option -%s specified\n", work);
	}
	goto error;
      }
 
      curr = k;			/* index in args and inits of option name */
      work += len;		/* skip to next possible option name */
 
      /* check if already encountered. .aargs != NULL means option seen in arg list already  */
      if (inits[curr].type == CL_STDARG && args[curr].aargs != NULL) 
      {
        if (verbose)
          fprintf(stderr, "CLARG: option -%s specified more than once\n",
                  args[curr].name);
	goto error;
      }
 
      max = inits[curr].max;
 
      if (gotone && restrict == CL_OPTPACK && ((max != 0) || !lastpure))
      {
        if (verbose)
          fprintf(stderr,
                  "CLARG: not allowed to pack anything but pure option names in option string [%s]\n",
                  inits[curr].name);
	goto error;
      }
      
      gotone = TRUE;
      lastpure = (max == 0);	/* this option is pure */
 
      if (max > 0)		/* get space to store args to this option */
      {
	if (args[curr].aargs == NULL)
	  args[curr].aargs = (char **) mymalloc(max * sizeof(char *));
      }
      else 			/* no args to option, but mark it as seen! */
	args[curr].aargs = (char **) 1;
 
      if (inits[curr].type == CL_STDARG)
      {
        for (k = 0; i < argc; i++) /* get arguments, count with k */
	{
	  char *s = argv[i];
	
	  if (k + 1 > max)	/* found all that are allowed */
	    break;
 
	  if (*s == '-')	/* found next option name */
	    break;
 
	  strncpy(cpy_arg, s, 255);
	  if ((chk_arg = (inits[curr].typchk)(cpy_arg, k+1)) == NULL) /* illegal type for arg? */
	  {
	    if (args[curr].num < inits[curr].min) /* must go with curr */
	    {
              if (verbose)
                fprintf(stderr, "CLARG: bad type arg [%s] for option -%s\n",
                        s, args[curr].name);
	      goto error;
	    }
	    else		/* add it to unmatched or next option */
	      break;
	  }
 
	  /* arg is ok => add it to args for this option */
	  args[curr].aargs[k] = (char *) mymalloc(strlen(chk_arg) + 1);
	  strcpy(args[curr].aargs[k], chk_arg);	/* copy from checked arg */
	  args[curr].num++;
	  k++;
	}
 
	if (k < inits[curr].min) /* check min num args */
	{
          if (verbose)
            fprintf(stderr, "CLARG: not enough args to option -%s\n",
                    args[curr].name);
	  goto error;
	}
      }
      else			/* type 2 option */
      {
	if (*work == '\0')
	{
          if (verbose)
            fprintf(stderr, "CLARG: no argument specified for option [%s]\n",
                    args[curr].name);
	  goto error;
	}
 
	k = args[curr].num;
	
	if (k >= inits[curr].max)
	{
          if (verbose)
            fprintf(stderr, "CLARG: too many arguments to option -%s\n", inits[curr].name);
	  goto error;
	}
	
	strncpy(cpy_arg, work, 255);   /* for this type, arg comes from WORK  */
	if ((chk_arg = (inits[curr].typchk)(cpy_arg, k+1)) == NULL)
	{
          if (verbose)
            fprintf(stderr, "CLARG: bad type arg [%s] for option -%s\n",
                    work, args[curr].name);
	  goto error;
	}
 
	/** add argument **/
 
	while (*work) work++;     /* advance to end of work (since rest is the arg) */
	
	args[curr].aargs[k] = (char *) mymalloc(strlen(chk_arg) + 1);
	strcpy(args[curr].aargs[k], chk_arg);
	args[curr].num++;
      }
    }
  }
 

  for (i = 1; i < size; i++)		/* check all option's  mins */
    if (args[i].aargs != NULL && args[i].num < inits[i].min)    /* check mins */
    {
      if (verbose)
        fprintf(stderr, "CLARG: not enough arguments to option [%s]\n", inits[i].name);
      goto error;
    }
 
  /* for unnamed only, require {0, min..max} of them */
  if (args[0].num < inits[0].min && args[0].num != 0) 
  {
    if (verbose)
      fprintf(stderr, "CLARG: not enough unmatched args\n");
    goto error;
  }
 
  g_inits = inits;
  cl_ev_err = 0;
 
#if 0 
  {
    char user_hook[201];
 
    sprintf(user_hook, "_%s=", argv[0]);
 
    while (*envp && strncmp(user_hook, *envp, strlen(user_hook)))
      envp++;
 
    if (*envp)
    {
      long t;
 
      *envp += strlen(user_hook);
      while (isspace(**envp))  (*envp)++;
      if (**envp != '\0')
      {
	t = cl_depend_eval(*envp, depend_argument);
 
	if (cl_ev_err)
	{
          if (verbose)
            fprintf(stderr, "CLARG: error %d parsing user dependency [%s]\n",
                    cl_ev_err, *envp);
	  return 1;
	}
      
	if (t == 0)
	{
          if (verbose)
            fprintf(stderr, "CLARG: user dependency failed [%s]\n", *envp);
	  goto error;
	}
      }
    }
  }
#endif

  if (depend != NULL)		/* check that all dependencies are satisfied */
    for ( ; *depend != NULL; depend++)
    {
      long t = cl_depend_eval(*depend, depend_argument);
 
     
      if (cl_ev_err)
      {
        if (verbose)
          fprintf(stderr, "CLARG: error %ld parsing dependency [%s]\n",
                  cl_ev_err, *depend);
	return 1;
      }
      
      if (t == 0)
      {
        if (verbose)
          fprintf(stderr, "CLARG: dependency failed [%s]\n", *depend);
	goto error;
      }
    }
 
 
  if (debug) {  /* if debugging, print out the whole args[] structure to stderr */
    long i, j;
 
    for (i = 0; i < size; i++)
    {
      fprintf(stderr, "=> %s: ", args[i].name ? args[i].name : "UNNAMED");
 
      if (args[i].aargs == NULL)
      {
        fprintf(stderr, "not specified\n");
        continue;
      }
 
      for (j = 0; j < args[i].num; j++)
        fprintf(stderr, "%s ", args[i].aargs[j]);
      fprintf(stderr, "\n");
    }	
  }	
 
  return (0);		/* all ok */
 
 
 error:					/* if user error print usage */
  fprintf(stderr, (usage != NULL) ? usage : "%s: no usage specified", argv[0]);	/* print usage */
  return(1);				/* error encountered */
}
 
 
 
 
bool cl_opt(char *str)	/* was option STR specified? */
{
  long i;
 
 
  if (str == CL_UNNAMED)	/* unmatched args */
    return (args[0].aargs != NULL);
  
  for (i = 1; i < size; i++)
    if (strcmp(str, args[i].name) == 0)
      return(args[i].aargs != NULL);

  fprintf(stderr, "CLARG: bad option name passed to CL_OPT()\n");
  return (0);		/* not found (illegal option) */
}
 
 
 
 
char *cl_arg(char *str, long n)  /* return arg N to option STR; or NULL */
{				/* if DNE */
  long i;
 
 
  if (n <= 0)		/* illegal n */
  {
    fprintf(stderr, "CLARG: CL_ARG() passed bad number argument [%ld]\n", n);
    return (CL_ARGDNE);
  }
  
 
  if (str != CL_UNNAMED)
  {
    for (i = 1; i < size; i++)
      if (strcmp(args[i].name, str) == 0)
	break;
 
    if (i >= size)	/* not found => bad option name */
    {
      fprintf(stderr, "CLARG: CL_ARG() passed illegal option name [%s]\n", str);
      return (CL_ARGDNE);
    }
  }
  else		/* unnamed */
    i = 0;
 
  /* now i is index of option str in args[] */
 
  if (args[i].aargs == NULL)	/* option not specified */
    return (CL_ARGDNE);
  else if (n > args[i].num)	/* n too large */
    return (CL_ARGDNE);
  else				/* ok */
    return (args[i].aargs[n-1]);	
}
 
 
 
long cl_numarg(char *s)		/* return number of args to option STR */
{				/* or (-1) if not specified */ 
  long i;
 
  if (s == CL_UNNAMED)
  {
    return (args[0].num);
  }
  else
  {
    for (i = 1; i < size; i++)
      if (strcmp(s, args[i].name) == 0)
	break;
 
    if (i >= size)
    {
      fprintf(stderr, "CLARG: CL_NUMARG() passed illegal option name [%s]\n", s); 
      return (CL_NUMERR);		/* not a legal option */
    }
 
    if (args[i].aargs == NULL)
      return (CL_NUMERR);		/* option not specified */
    else
      return (args[i].num);	/* ok */
  }
}
 
 
 
char *argstring(int argc, char **argv)
{
  int i;
  int len;
  char *str;

  for (i = 0, len = 0; i < argc; i++)
    len += strlen(argv[i]) + 1;

  assert((str = (char *) malloc((len+1)*sizeof(char))) != NULL);

  for (i = 0, str[0] = '\0'; i < argc; i++) {
    strcat(str, argv[i]);
    strcat(str, " ");
  }
  
  return str;
}


