/**********************************************************************
 * File:        varable.c  (Formerly variable.c)
 * Description: Initialization and setting of VARIABLEs.
 * Author:					Ray Smith
 * Created:					Fri Feb 22 16:22:34 GMT 1991
 *
 * (C) Copyright 1991, Hewlett-Packard Ltd.
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 ** http://www.apache.org/licenses/LICENSE-2.0
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 *
 **********************************************************************/

#include          "mfcpch.h"     //precompiled headers
#include          <stdio.h>
#include          <string.h>
#include          <stdlib.h>
#include          "tprintf.h"
//#include                                      "ipeerr.h"
#include          "varable.h"
#include          "scanutils.h"

#define PLUS          '+'        //flag states
#define MINUS         '-'
#define EQUAL         '='

CLISTIZE (INT_VARIABLE)
CLISTIZE (BOOL_VARIABLE) CLISTIZE (STRING_VARIABLE) CLISTIZE (double_VARIABLE)
INT_VAR_FROM
INT_VARIABLE::copy;
INT_VARIABLE_CLIST
INT_VARIABLE::head;              //global definition
INT_VAR_TO
INT_VARIABLE::replace;
BOOL_VAR_FROM
BOOL_VARIABLE::copy;
BOOL_VARIABLE_CLIST
BOOL_VARIABLE::head;             //global definition
BOOL_VAR_TO
BOOL_VARIABLE::replace;
STRING_VAR_FROM
STRING_VARIABLE::copy;
STRING_VARIABLE_CLIST
STRING_VARIABLE::head;           //global definition
STRING_VAR_TO
STRING_VARIABLE::replace;
double_VAR_FROM
double_VARIABLE::copy;
double_VARIABLE_CLIST
double_VARIABLE::head;           //global definition
double_VAR_TO
double_VARIABLE::replace;

/**********************************************************************
 * INT_VAR_FROM::INT_VAR_FROM
 *
 * Constructor to copy the list to a temporary location while the
 * list head gets constructed.
 **********************************************************************/

INT_VAR_FROM::INT_VAR_FROM() {  //constructor
  INT_VARIABLE_C_IT start_it = &INT_VARIABLE::head;
  INT_VARIABLE_C_IT end_it = &INT_VARIABLE::head;

  if (!start_it.empty ()) {
    while (!end_it.at_last ())
      end_it.forward ();
                                 //move to copy
    list.assign_to_sublist (&start_it, &end_it);
  }
}


/**********************************************************************
 * INT_VAR_TO::INT_VAR_TO
 *
 * Constructor to copy the list back to its rightful place.
 **********************************************************************/

INT_VAR_TO::INT_VAR_TO() {  //constructor
  INT_VARIABLE_C_IT start_it = &INT_VARIABLE::copy.list;
  INT_VARIABLE_C_IT end_it = &INT_VARIABLE::copy.list;

  if (!start_it.empty ()) {
    while (!end_it.at_last ())
      end_it.forward ();
    INT_VARIABLE::head.assign_to_sublist (&start_it, &end_it);
  }
}


/**********************************************************************
 * INT_VARIABLE::INT_VARIABLE
 *
 * Constructor for INT_VARIABLE. Add the variable to the static list.
 **********************************************************************/

INT_VARIABLE::INT_VARIABLE(                     //constructor
                           INT32 v,             //the variable
                           const char *vname,   //of variable
                           const char *comment  //info on variable
                          ) {
  INT_VARIABLE_C_IT it = &head;  //list iterator

  //tprintf("Constructing %s\n",vname);
  set_value(v);  //set the value
  name = vname;                  //strings must be static
  info = comment;
  it.add_before_stay_put (this); //add it to stack
}


INT_VARIABLE::~INT_VARIABLE (    //constructor
) {
  INT_VARIABLE_C_IT it = &head;  //list iterator

  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
    if (it.data () == this)
      it.extract ();
}


/**********************************************************************
 * INT_VARIABLE::get_head
 *
 * Get the head of the list of the variables.
 **********************************************************************/

INT_VARIABLE_CLIST *INT_VARIABLE::get_head() {  //access to static
  return &head;
}


/**********************************************************************
 * INT_VARIABLE::print
 *
 * Print the entire list of INT_VARIABLEs.
 **********************************************************************/

void INT_VARIABLE::print(          //print full list
                         FILE *fp  //file to print on
                        ) {
  INT_VARIABLE_C_IT it = &head;  //list iterator
  INT_VARIABLE *elt;             //current element

  if (fp == stdout) {
    tprintf ("#Variables of type INT_VARIABLE:\n");
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
      elt = it.data ();
      tprintf ("%s %d #%s\n", elt->name, elt->value, elt->info);
    }
  }
  else {
    fprintf (fp, "#Variables of type INT_VARIABLE:\n");
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
      elt = it.data ();
      fprintf (fp, "%s " INT32FORMAT " #%s\n", elt->name, elt->value,
        elt->info);
    }
  }
}


/**********************************************************************
 * BOOL_VAR_FROM::BOOL_VAR_FROM
 *
 * Constructor to copy the list to a temporary location while the
 * list head gets constructed.
 **********************************************************************/

BOOL_VAR_FROM::BOOL_VAR_FROM() {  //constructor
  BOOL_VARIABLE_C_IT start_it = &BOOL_VARIABLE::head;
  BOOL_VARIABLE_C_IT end_it = &BOOL_VARIABLE::head;

  if (!start_it.empty ()) {
    while (!end_it.at_last ())
      end_it.forward ();
                                 //move to copy
    list.assign_to_sublist (&start_it, &end_it);
  }
}


/**********************************************************************
 * BOOL_VAR_TO::BOOL_VAR_TO
 *
 * Constructor to copy the list back to its rightful place.
 **********************************************************************/

BOOL_VAR_TO::BOOL_VAR_TO() {  //constructor
  BOOL_VARIABLE_C_IT start_it = &BOOL_VARIABLE::copy.list;
  BOOL_VARIABLE_C_IT end_it = &BOOL_VARIABLE::copy.list;

  if (!start_it.empty ()) {
    while (!end_it.at_last ())
      end_it.forward ();
    BOOL_VARIABLE::head.assign_to_sublist (&start_it, &end_it);
  }
}


/**********************************************************************
 * BOOL_VARIABLE::BOOL_VARIABLE
 *
 * Constructor for BOOL_VARIABLE. Add the variable to the static list.
 **********************************************************************/

BOOL_VARIABLE::BOOL_VARIABLE(                     //constructor
                             BOOL8 v,             //the variable
                             const char *vname,   //of variable
                             const char *comment  //info on variable
                            ) {
  BOOL_VARIABLE_C_IT it = &head; //list iterator

  //tprintf("Constructing %s\n",vname);
  set_value(v);  //set the value
  name = vname;                  //strings must be static
  info = comment;
  it.add_before_stay_put (this); //add it to stack

}


/**********************************************************************
 * BOOL_VARIABLE::BOOL_VARIABLE
 *
 * Constructor for BOOL_VARIABLE. Add the variable to the static list.
 **********************************************************************/

BOOL_VARIABLE::~BOOL_VARIABLE () {
  BOOL_VARIABLE_C_IT it = &head; //list iterator

  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
    if (it.data () == this)
      it.extract ();
}


/**********************************************************************
 * BOOL_VARIABLE::get_head
 *
 * Get the head of the list of the variables.
 **********************************************************************/

BOOL_VARIABLE_CLIST *BOOL_VARIABLE::get_head() {  //access to static
  return &head;
}


/**********************************************************************
 * BOOL_VARIABLE::print
 *
 * Print the entire list of BOOL_VARIABLEs.
 **********************************************************************/

void BOOL_VARIABLE::print(          //print full list
                          FILE *fp  //file to print on
                         ) {
  BOOL_VARIABLE_C_IT it = &head; //list iterator
  BOOL_VARIABLE *elt;            //current element

  if (fp == stdout) {
    tprintf ("#Variables of type BOOL_VARIABLE:\n");
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
      elt = it.data ();
      tprintf ("%s %c #%s\n",
        elt->name, elt->value ? 'T' : 'F', elt->info);
    }
  }
  else {
    fprintf (fp, "#Variables of type BOOL_VARIABLE:\n");
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
      elt = it.data ();
      fprintf (fp, "%s %c #%s\n",
        elt->name, elt->value ? 'T' : 'F', elt->info);
    }
  }
}


/**********************************************************************
 * STRING_VAR_FROM::STRING_VAR_FROM
 *
 * Constructor to copy the list to a temporary location while the
 * list head gets constructed.
 **********************************************************************/

STRING_VAR_FROM::STRING_VAR_FROM() {  //constructor
  STRING_VARIABLE_C_IT start_it = &STRING_VARIABLE::head;
  STRING_VARIABLE_C_IT end_it = &STRING_VARIABLE::head;

  if (!start_it.empty ()) {
    while (!end_it.at_last ())
      end_it.forward ();
                                 //move to copy
    list.assign_to_sublist (&start_it, &end_it);
  }
}


/**********************************************************************
 * STRING_VAR_TO::STRING_VAR_TO
 *
 * Constructor to copy the list back to its rightful place.
 **********************************************************************/

STRING_VAR_TO::STRING_VAR_TO() {  //constructor
  STRING_VARIABLE_C_IT start_it = &STRING_VARIABLE::copy.list;
  STRING_VARIABLE_C_IT end_it = &STRING_VARIABLE::copy.list;

  if (!start_it.empty ()) {
    while (!end_it.at_last ())
      end_it.forward ();
    STRING_VARIABLE::head.assign_to_sublist (&start_it, &end_it);
  }
}


/**********************************************************************
 * STRING_VARIABLE::STRING_VARIABLE
 *
 * Constructor for STRING_VARIABLE. Add the variable to the static list.
 **********************************************************************/

STRING_VARIABLE::STRING_VARIABLE (
                                 //constructor
const char *v,                   //the variable
const char *vname,               //of variable
const char *comment              //info on variable
):
value(v) { 
                                 //list iterator
  STRING_VARIABLE_C_IT it = &head;

  //tprintf("Constructing %s\n",vname);
  name = vname;                  //strings must be static
  info = comment;
  it.add_before_stay_put (this); //add it to stack
}


/**********************************************************************
 * STRING_VARIABLE::~STRING_VARIABLE
 *
 * Destructor for STRING_VARIABLE. Add the variable to the static list.
 **********************************************************************/

                                 //constructor
STRING_VARIABLE::~STRING_VARIABLE (
) {
                                 //list iterator
  STRING_VARIABLE_C_IT it = &head;

  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
    if (it.data () == this)
      it.extract ();
}


/**********************************************************************
 * STRING_VARIABLE::get_head
 *
 * Get the head of the list of the variables.
 **********************************************************************/

STRING_VARIABLE_CLIST *STRING_VARIABLE::get_head() {  //access to static
  return &head;
}


/**********************************************************************
 * STRING_VARIABLE::print
 *
 * Print the entire list of STRING_VARIABLEs.
 **********************************************************************/

void STRING_VARIABLE::print(          //print full list
                            FILE *fp  //file to print on
                           ) {
                                 //list iterator
  STRING_VARIABLE_C_IT it = &head;
  STRING_VARIABLE *elt;          //current element

  if (fp == stdout) {
    tprintf ("#Variables of type STRING_VARIABLE:\n");
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
      elt = it.data ();
      tprintf ("%s #%s %s\n", elt->name, elt->value.string (), elt->info);
    }
  }
  else {
    fprintf (fp, "#Variables of type STRING_VARIABLE:\n");
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
      elt = it.data ();
      fprintf (fp, "%s #%s %s\n",
        elt->name, elt->value.string (), elt->info);
    }
  }
}


/**********************************************************************
 * double_VAR_FROM::double_VAR_FROM
 *
 * Constructor to copy the list to a temporary location while the
 * list head gets constructed.
 **********************************************************************/

double_VAR_FROM::double_VAR_FROM() {  //constructor
  double_VARIABLE_C_IT start_it = &double_VARIABLE::head;
  double_VARIABLE_C_IT end_it = &double_VARIABLE::head;

  if (!start_it.empty ()) {
    while (!end_it.at_last ())
      end_it.forward ();
                                 //move to copy
    list.assign_to_sublist (&start_it, &end_it);
  }
}


/**********************************************************************
 * double_VAR_TO::double_VAR_TO
 *
 * Constructor to copy the list back to its rightful place.
 **********************************************************************/

double_VAR_TO::double_VAR_TO() {  //constructor
  double_VARIABLE_C_IT start_it = &double_VARIABLE::copy.list;
  double_VARIABLE_C_IT end_it = &double_VARIABLE::copy.list;

  if (!start_it.empty ()) {
    while (!end_it.at_last ())
      end_it.forward ();
    double_VARIABLE::head.assign_to_sublist (&start_it, &end_it);
  }
}


/**********************************************************************
 * double_VARIABLE::double_VARIABLE
 *
 * Constructor for double_VARIABLE. Add the variable to the static list.
 **********************************************************************/

double_VARIABLE::double_VARIABLE(                     //constructor
                                 double v,            //the variable
                                 const char *vname,   //of variable
                                 const char *comment  //info on variable
                                ) {
                                 //list iterator
  double_VARIABLE_C_IT it = &head;

  //tprintf("Constructing %s\n",vname);
  set_value(v);  //set the value
  name = vname;                  //strings must be static
  info = comment;
  it.add_before_stay_put (this); //add it to stack
}


double_VARIABLE::~double_VARIABLE () {
                                 //list iterator
  double_VARIABLE_C_IT it = &head;

  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
    if (it.data () == this)
      it.extract ();
}


/**********************************************************************
 * double_VARIABLE::get_head
 *
 * Get the head of the list of the variables.
 **********************************************************************/

double_VARIABLE_CLIST *double_VARIABLE::get_head() {  //access to static
  return &head;
}


/**********************************************************************
 * double_VARIABLE::print
 *
 * Print the entire list of double_VARIABLEs.
 **********************************************************************/

void double_VARIABLE::print(          //print full list
                            FILE *fp  //file to print on
                           ) {
                                 //list iterator
  double_VARIABLE_C_IT it = &head;
  double_VARIABLE *elt;          //current element

  if (fp == stdout) {
    tprintf ("#Variables of type double_VARIABLE:\n");
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
      elt = it.data ();
      tprintf ("%s %lg #%s\n", elt->name, elt->value, elt->info);
    }
  }
  else {
    fprintf (fp, "#Variables of type double_VARIABLE:\n");
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
      elt = it.data ();
      fprintf (fp, "%s %g #%s\n", elt->name, elt->value, elt->info);
    }
  }
}


/**********************************************************************
 * read_variables_file
 *
 * Read a file of variables definitions and set/modify the values therein.
 * If the filename begins with a + or -, the BOOL_VARIABLEs will be
 * ORed or ANDed with any current values.
 * Blank lines and lines beginning # are ignored.
 * Values may have any whitespace after the name and are the rest of line.
 **********************************************************************/

DLLSYM BOOL8 read_variables_file(                  //read the file
                                 const char *file  //name to read
                                ) {
  BOOL8 anyerr;                  //true if any error
  char flag;                     //file flag
  BOOL8 foundit;                 //found variable
  INT16 length;                  //length of line
  INT16 nameoffset;              //offset for real name
  char *valptr;                  //value field
  char *stringend;               //end of string value
  FILE *fp;                      //file pointer
  INT32 intval;                  //value from file
  double doubleval;              //value form file
                                 //iterators
  INT_VARIABLE_C_IT int_it = &INT_VARIABLE::head;
  BOOL_VARIABLE_C_IT BOOL_it = &BOOL_VARIABLE::head;
  STRING_VARIABLE_C_IT STRING_it = &STRING_VARIABLE::head;
  double_VARIABLE_C_IT double_it = &double_VARIABLE::head;
  char line[MAX_PATH];           //input line

  anyerr = FALSE;
  if (*file == PLUS) {
    flag = PLUS;                 //file has flag
    nameoffset = 1;
  }
  else if (*file == MINUS) {
    flag = MINUS;
    nameoffset = 1;
  }
  else {
    flag = EQUAL;
    nameoffset = 0;
  }

  fp = fopen (file + nameoffset, "r");
  if (fp == NULL) {
    tprintf ("read_variables_file:Can't open %s", file + nameoffset);
    return TRUE;                 //can't open it
  }
  while (fgets (line, MAX_PATH, fp)) {
    if (line[0] != '\n' && line[0] != '#') {
      length = strlen (line);
      if (line[length - 1] == '\n')
        line[length - 1] = '\0'; //cut newline
      for (valptr = line; *valptr && *valptr != ' ' && *valptr != '\t';
        valptr++);
      if (*valptr) {             //found blank
        *valptr = '\0';          //make name a string
        do

        valptr++;              //find end of blanks
        while (*valptr == ' ' || *valptr == '\t');

        if (*valptr && *valptr != '#') {
                                 //last char in string
          stringend = valptr + strlen (valptr) - 1;
          while (stringend != valptr) {
            while (stringend != valptr
              && (*stringend == ' ' || *stringend == '\t'))
              //cut trailing blanks
              stringend--;
            stringend[1] = '\0'; //terminate string

            while (stringend != valptr
              && (*stringend != ' ' && *stringend != '\t'
              || stringend[1] != '#'))
              stringend--;       //find word start
          }
        }
      }
      foundit = FALSE;

                                 //find name
      for (STRING_it.mark_cycle_pt (); !STRING_it.cycled_list () && strcmp (line, STRING_it.data ()->name); STRING_it.forward ());
                                 //found it
      if (!STRING_it.cycled_list ()) {
        foundit = TRUE;          //found the varaible
        if (*valptr == '\0' || *valptr == '#')
          STRING_it.data ()->set_value ((char *) NULL);
        //no value
        else
                                 //set its value
          STRING_it.data ()->set_value (valptr);
      }

      if (*valptr) {
                                 //find name
        for (int_it.mark_cycle_pt (); !int_it.cycled_list () && strcmp (line, int_it.data ()->name); int_it.forward ());
                                 //found it
        if (!int_it.cycled_list ()
        && sscanf (valptr, INT32FORMAT, &intval) == 1) {
          foundit = TRUE;        //found the varaible
                                 //set its value
          int_it.data ()->set_value (intval);
        }

                                 //find name
        for (BOOL_it.mark_cycle_pt (); !BOOL_it.cycled_list () && strcmp (line, BOOL_it.data ()->name); BOOL_it.forward ());
                                 //found it
        if (!BOOL_it.cycled_list ()) {
          if (*valptr == 'T' || *valptr == 't'
          || *valptr == 'Y' || *valptr == 'y' || *valptr == '1') {
            foundit = TRUE;
            if (flag == MINUS)
              BOOL_it.data ()->set_value (FALSE);
            //set to false
            else
              BOOL_it.data ()->set_value (TRUE);
            //set to true
          }
          else if (*valptr == 'F' || *valptr == 'f'
            || *valptr == 'N' || *valptr == 'n'
          || *valptr == '0') {
            foundit = TRUE;
            if (flag == EQUAL)
              BOOL_it.data ()->set_value (FALSE);
            //set to false
          }
        }

                                 //find name
        for (double_it.mark_cycle_pt (); !double_it.cycled_list () && strcmp (line, double_it.data ()->name); double_it.forward ());
                                 //found it
        
        #ifdef EMBEDDED
        if (!double_it.cycled_list ()) {
          doubleval = strtofloat(valptr);
        #else
        if (!double_it.cycled_list ()
        && sscanf (valptr, "%lf", &doubleval) == 1) {
        #endif
          foundit = TRUE;        //found the varaible
          double_it.data ()->set_value (doubleval);
          //set its value
        }

        if (!foundit) {
          anyerr = TRUE;         //had an error
          tprintf ("read_variables_file:variable not found: %s",
            line);
        }
      }
      else if (!foundit) {
        anyerr = TRUE;           //had an error
        tprintf ("read_variables_file:No value for variable %s", line);
      }
    }
  }
  fclose(fp);  //close file
  return anyerr;
}


/**********************************************************************
 * print_variables
 *
 * Print all variable types to the given file
 **********************************************************************/

DLLSYM void print_variables(          //print all vars
                            FILE *fp  //file to print on
                           ) {
  INT_VARIABLE::print(fp);  //print INTs
  BOOL_VARIABLE::print(fp);  //print BOOLs
  STRING_VARIABLE::print(fp);  //print STRINGs
  double_VARIABLE::print(fp);  //print doubles
}
