/*		varnames_in_sel   Apr 09				*/
/*	varnames_in_sel takes a jgofs selection string,			*/
/*    returns the number of different jgofs variables in it, and	*/
/*    updates a list of jgofs variables with any newly found variables	*/
/*    that are not already in the list					*/
/*									*/
/*	Returns count of elements added to list (see Arg 1)		*/
/*	Arg								*/
/*	 1	Pointer to list to be updated.  If NULL, a list will	*/
/*		be created.  If not NULL, last element of		*/
/*		list must be NULL.  The last element of the returned	*/
/*		list will be NULL.					*/
/*		  The list must be dynamically				*/
/*		allocated so it can be dynamically extended if needed.	*/
/*		Each list element is a pointer to a jgofs varname. 	*/
/*		(Each jgofs varname is a null-terminated char string)	*/
/*		List elements returned by this program are dynamically	*/
/*		allocated						*/
/*	 2	Pointer to null-terminated char string holding the	*/
/*		selection						*/
/*	      The next 3 args are normally specified as NULL.		*/
/*	    They are pointers to functions to be called in case		*/
/*	    of various parsing errors.  If specified as NULL, the	*/
/*	    calling program will exit with error information ultimately	*/
/*	    handled according to the program's "err" function.  If the	*/
/*	    calling program does not already have an "err" function,	*/
/*	    the jgofs distribution contains err_stub* routines.  Please	*/
/*	    see readme.libupd in src/lib				*/
/*	      Non-NULL pointers	are needed only if the caller does	*/
/*	    NOT want to exit after a parsing error occurs.		*/
/*	    If such control is of interest, 2 other sources		*/
/*	    of information will be needed.  First, the type and		*/
/*	    calling sequence for each function can be determined by	*/
/*	    looking at the default functions (below).  Second, the	*/
/*	    source code of jgofs_selection_parser (src/lib/parse.c)	*/
/*	    will need to be consulted if the particulars of an error	*/
/*	    are of interest						*/
/*	 3	Defaults to (*err)() (application-dependent)		*/
/*	 4	Defaults to (*errn)() (src/lib/utils.c - will call err  */
/*				       NB: this err is NOT arg 3 above)	*/
/*	 5	Defaults to (*malloc_err_stub)() (below)		*/

#define VARNAMES_IN_SEL_VERSION "varnames_in_sel v 1.0 12 Jun 2009"

/************************************************************************/

#include "parse.h"

  /*  Next 3 are global only to communicate between 			*/
  /*  add_varnames_from_selection & varnames_in_sel in this		*/
  /*  module								*/
char **selection_varnames_list;
int n_selection_varnames;
int size_selection_varnames_list;

  /*  functions from utils (in jgofs.a library)				*/
void errn();
char *strdupl();

  /*  function from parse (in jgofs.a library)				*/
void jgofs_selection_parser();

  /*  function from "somewhere".  jgofs.a library requires an err	*/
  /*  function.  If not in user program, we've provided err_stub*	*/
  /*  files - see src/lib/readme.libupd					*/
void err();

/**********								*/

char *varnames_in_sel_return_vers()
/*  Dummy routine.  Exists only to force .h file version string into	*/
/*  this module.  Note string must not be global or we'll have con-	*/
/*  flicts if another routine similarly includes the version string	*/
{
  static char version[] = VARNAMES_IN_SEL_VERSION"/"FULL_PARSEH_VERSION;
  return version;
}



  /*  Code below stolen from outer's malloc_err routine.		*/
void malloc_err_stub(s,n)
char *s;
int n;
{
    /*  Don't want to malloc an error buffer if we're having trouble	*/
    /*  malloc'ing in the first place					*/
    /*    Tried putting malloc in here, too, returning ptr when OK, but	*/
    /*    ran into BUSERR alignment problems.  Don't know why...	*/
#define STUB_MAX_S 50
  char idbuf[32 + STUB_MAX_S + 9 + 1] = "parse could not get memory for ";
  int len;

  if (  (len = strlen(s)) > STUB_MAX_S  ) len = STUB_MAX_S;
  strncat(idbuf,s,len);
  strcat(idbuf,". nbytes ");
  errn(idbuf,n);

  return;
}



  /*  parse will want to know what level a variable is on.  Tell it 0	*/
int dummy_iovarlevel(vn)
int *vn;
{
  return 0;
}



  /*  Next function is the heart of the trick.  When parse finds a	*/
  /*  token that is syntactically a varname, it wants to associate the	*/
  /*  varname w/a varnum, so it calls a lookup routine.  We replace	*/
  /*  that call w/a call to add_varname.  We assume that the token	*/
  /*  is a legit varname, and return bogus info.  The bogus info is OK	*/
  /*  as long as the results of the parse are not used.  A dicier	*/
  /*  problem is that of correctly diagnosing errors, particularly	*/
  /*  those relating to illegal varnames and bad levels.  Punt!		*/
int add_varnames_from_selection(s)
char *s;
{
  int index,nbytes;

    /*  parse can't tell a "not" from a varname w/o following return	*/
  if (strcmp(s,"not") == 0) return ILLEGAL_VARNUM;

  if (selection_varnames_list == NULL)
      err("varnames_in_sel: internal error: NULL selection_varnames_list",
	  "");

    /*  Doing logic w/indices rather than pointer avoids issues due to	*/
    /*  realloc								*/
  index = 0;
  while (selection_varnames_list[index] != NULL) {
    if (strcmp(s,selection_varnames_list[index]) == 0) break;
    index++;
  }

  if (selection_varnames_list[index] == NULL) {
    n_selection_varnames++;
    strdupl (&selection_varnames_list[index++],s,
				"varnames_from_selection: adding name");
    nbytes = (index + 1) * (sizeof(char **));
    selection_varnames_list = (char **)realloc(selection_varnames_list,nbytes);
    if (selection_varnames_list == NULL)
      errn("varnames_in_sel: mem alloc failure. Bytes wanted: ",nbytes);
    selection_varnames_list[index] = NULL;
  }
  return index;	/*  This return is to parse - nothing to do w/us - but	*/
		/*  might as well make it "real" in some sense		*/
}


int varnames_in_sel 
		(var_array_ptr,selection,user_err,user_errn,user_malloc_err)
char ***var_array_ptr;
char *selection;
void (*user_err)();
void (*user_errn)();
void (*user_malloc_err)();
{
  struct parse_functions parse_functions;
  struct parse_data parse_data;

    /*  Set up for parsing						*/
  parse_data.tstcnt = 0;
  parse_data.tstproccnt = 0;
  parse_functions.varname_lookup = &add_varnames_from_selection;
  parse_functions.iovarlevel = &dummy_iovarlevel;
  parse_functions.err = (user_err == NULL) ? &err : user_err;
  parse_functions.errn = (user_errn == NULL) ? &errn : user_errn;
  parse_functions.malloc_err = (user_malloc_err == NULL) ?
					&malloc_err_stub : user_malloc_err;

  selection_varnames_list = *var_array_ptr;
  if (selection_varnames_list == NULL) {
    selection_varnames_list = (char **)malloc(sizeof (char **));
    if (selection_varnames_list == NULL)
      err("varnames_in_sel: could not allocate 1 (char **) pointer","");
    *selection_varnames_list = NULL;
  }
  n_selection_varnames = 0;

    /*  Next line causes a list of vars used in selection to be		*/
    /*  placed in selection_varnames_list.  See comments before		*/
    /*  add_varnames_from_selection (above).  Apologies for the		*/
    /*  indirectness of this action					*/
  jgofs_selection_parser (selection,&parse_data,&parse_functions);

  *var_array_ptr = selection_varnames_list;
  return n_selection_varnames;
}
