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

      OPTION.C


      This is a set of routines for processing a list of
      program options read from a control file.  The first
      option in the list should be the terminator for that
      set of options; that is, when it's name is
      encountered, a final routine will be executed and the
      processing of that list will end.

      Fri  11-18-1988
      1) Modified so that each word read by
      execute_options and execute_one_option is echoed to
      the screen.  This should make it much easier to figure
      out what is wrong when the program fails in reading a
      control file. -- see note of 2/25/91 below
      2) Added function "list_options()", to be used when an
      error is found.  It prints to the screen a list of the
      available options.

            Eric Firing
            University of Hawaii

      Mon  02-25-91 JR
      Modified so execute_options() and execute_one_option()
      echo only any invalid option encountered, and then
      call list_options() to list valid options.

*/



#include <stdio.h>
#include <string.h>  /* strcmp() */
#include "misc.h"    /* NAME_LIST_ENTRY_TYPE, get_name() */
#include "ioserv.h"  /* OPTION_TYPE, report_msg(), check_error() */

#if PROTOTYPE_ALLOWED
int get_option_index(OPTION_TYPE *list, char *word)
#else
int get_option_index(list, word)
OPTION_TYPE *list;
char *word;
#endif
{
   int i;

   for (i=0; list[i].name; i++)
      if (!strcmp( list[i].name, word))
         return(i);                       /* match */
   return(-1);                            /* no match */
}                       /* get_option_index */

int echo = 0;

#if PROTOTYPE_ALLOWED
int execute_options(FILE *fp, OPTION_TYPE *list, int echo_flag)
#else
int execute_options(fp, list, echo_flag)
FILE *fp;
OPTION_TYPE *list;
int echo_flag;
#endif
{
   char word[80];
   int index = 0;
   int end = 0;
   int match = 0;   /* count of the number of words matched */

   echo = echo_flag;
   if (echo == 1) report_msg("% OPTIONS:\n% =======");
   while (!end)
   {
      if (fscanf(fp, " %79s", word) != 1) return(-1);    /* scanf error exit */
      index = get_option_index(list, word);
      if (index < 0)
      {
         report_msg("\n% ERROR: Unrecognized option => ");
         report_msg(word);
         list_options(list);
         return(0);             /* error exit: word not found */
      }
      if (index == 0) 
      {
         end = 1;
         if (echo == 1) report_msg("\n% =======\n");
      }
      else
      {
         if (echo == 1) 
         {
	    report_msg("\n% ");
	    report_msg(word);
         }
         if (list[index].function != NULL)
            (*(list[index].function))(fp, list[index].argument, list[index].code);
      }
      match++;
   }
   return(match);       /* normal exit */
}                       /* execute_options */

#if PROTOTYPE_ALLOWED
int execute_one_option(FILE *fp, OPTION_TYPE *list)
#else
int execute_one_option(fp, list)
FILE *fp;
OPTION_TYPE *list;
#endif
{
   char word[80];
   int index = 0;

   if (fscanf(fp, " %79s", word) != 1) return(-1);    /* scanf error exit */
   index = get_option_index(list, word);
   if (index < 0)
   {
      report_msg("\n ERROR: Unrecognized option => ");
      report_msg(word);
      list_options(list);
      return(0);             /* error exit: word not found */
   }
   if (list[index].function != NULL)
      (*(list[index].function))(fp, list[index].argument, list[index].code);
   return(1);       /* normal exit */
}                       /* execute_one_option */

#if PROTOTYPE_ALLOWED
char *get_option_name(OPTION_TYPE *list, int number)
#else
char *get_option_name(list, number)
OPTION_TYPE *list;
int number;
#endif
{
   int i;

   for (i=0; list[i].name; i++)
      if ( list[i].code == number )
         return (list[i].name);

   return(NULL);
}                       /* get_option_name() */

#if PROTOTYPE_ALLOWED
void list_options(OPTION_TYPE *list)
#else
void list_options(list)
OPTION_TYPE *list;
#endif
{
   int i;

   report_msg("\n Valid options are:\n  ");
   for (i=0; list[i].name; i++)
   {
      report_msg(list[i].name);
      report_msg("\n  ");
   }
}                       /* list_options() */

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

   The following functions are likely to be useful in option
   processing.

   function: skip_to()
         This allows flexible nested commenting out of
         blocks of a control file.  For example:
            skip_to next1
            do_this
            do_that
            next1
         in the control file will cause the commands do_this
         and do_that to be skipped, provided skip_to() is
         mapped to "skip_to" in the option structure.

   function: logical_or()
         This is used for building up a flag in which each
         option sets one or more bits.  The option code is
         an integer with the appropriate bits set for that
         option. The logical_or operation then makes sure
         that these bits are also set in the argument,
         without disturbing any other bits.

   function: set_code()
         This is used when the option code completely
         determines the value of a flag word.

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

#if PROTOTYPE_ALLOWED
int skip_to(FILE *fp, char *arg, int code)
#else
int skip_to(fp, arg, code)
FILE *fp;
char *arg;
int code;
#endif
{
   char word[80], next_word[80];

   if (fscanf(fp, " %79s", word) != 1) return(-1);    /* scanf error exit */
   while (1)
   {
      if (fscanf(fp, " %79s", next_word) != 1) return(-1);
      if (strcmp(word, next_word) == 0) return(0);
   }
}

#if PROTOTYPE_ALLOWED
int logical_or(FILE *fp, char *arg, int code)
#else
int logical_or(fp, arg, code)
FILE *fp;
char *arg;
int code;
#endif
{
   *((int *)arg) |= code;
   return(0);
}


#if PROTOTYPE_ALLOWED
int set_code(FILE *fp, char *arg, int code)
#else
int set_code(fp, arg, code)
FILE *fp;
char *arg;
int code;
#endif
{
   *((int *)arg) = code;
   return(0);
}

/*-----------------------------------------------------------------
   The following sets bit n (counting from 0) in a long word, or,
   if n>32, it resets bit n-100.
*/

#if PROTOTYPE_ALLOWED
int set_long_bit(FILE *fp, char *arg, int code)
#else
int set_long_bit(fp, arg, code)
FILE *fp;
char *arg;
int code;
#endif
{
   long bit_mask;
   int off = 0;

   if (code >31)   /* turn off a bit */
   {
      code -= 100;
      off = 1;
   }
   bit_mask = 1;
   bit_mask <<= code;
   if (off)
      *((long *)arg) &= ~bit_mask;
   else
      *((long *)arg) |= bit_mask;
   return(0);
}

/* The following is for getting simple options, that is,
   numbers or strings, without translations.
*/

static NAME_LIST_ENTRY_TYPE op_format_list[] =
{
   {" %c"   , TYPE_CHAR},
   {" %ld"  , TYPE_LONG},
   {" %f"   , TYPE_FLOAT},
   {" %lf"  , TYPE_DOUBLE},
   {" %s"   , TYPE_STRING},
   {" %d"   , TYPE_INT},
   {NULL    , 0}
};

#if PROTOTYPE_ALLOWED
int op_get_param(FILE *fp, char *arg, int code)
#else
int op_get_param(fp, arg, code)
FILE *fp;
char *arg;
int code;
#endif
{
   char buf[80];

   check_error( (fscanf(fp, get_name(op_format_list, code), arg) != 1),
      "op_get_param");
   if (echo) 
   {
      switch (code)
      {
	 case TYPE_INT:
	    sprintf(buf, " %d", *((int *) arg));
	    break;
	 case TYPE_LONG:
	    sprintf(buf, " %ld", *((long *) arg));
	    break;
	 case TYPE_FLOAT: 
	    sprintf(buf, " %f", *((float *) arg));
	    break;
	 case TYPE_DOUBLE:
	    sprintf(buf, " %f", *((double *) arg));
	    break;
	 case TYPE_CHAR:
	    sprintf(buf, " %c", *arg);
	    break;
	 case TYPE_STRING:
	    sprintf(buf, " %s", arg);
	    break;
      }
      report_msg(buf);
   }
   return(1);
}

/* 1999/07/30 Pierre Jaccard */
#include "../gfi/src/plugs/option.c" 
