/*
 *  rc.c -- routines for processing the configuration file
 *
 *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef HAVE_NCURSES

#include "dialog.h"
#include "colors.h"
#include "rc.h"


static char *attr_to_str(int fg, int bg, int hl);
static int str_to_attr(char *str, int *fg, int *bg, int *hl);
static int parse_line(char *line, char **var, char **value);


/*
 * Create the configuration file
 */
void create_rc(char *filename)
{
  int i;
  FILE *rc_file;

  if ((rc_file = fopen(filename, "wt")) == NULL) {
    fprintf(stderr, "\nError opening file for writing in create_rc().\n");
    exit(-1);
  }

  fprintf(rc_file, "#\
\n# Run-time configuration file for dialog\
\n#\
\n# Automatically generated by \"dialog --create-rc <file>\"\
\n#\
\n#\
\n# Types of values:\
\n#\
\n# Number     -  <number>\
\n# String     -  \"string\"\
\n# Boolean    -  <ON|OFF>\
\n# Attribute  -  (foreground,background,highlight?)\
\n#\n\n");

  /* Print an entry for each configuration variable */
  for (i = 0; i < VAR_COUNT; i++) {
    fprintf(rc_file, "\n# %s\n", vars[i].comment);    /* print comment */
    switch (vars[i].type) {
      case VAL_INT:
        fprintf(rc_file, "%s = %d\n", vars[i].name, *((int *) vars[i].var));
        break;
      case VAL_STR:
        fprintf(rc_file, "%s = \"%s\"\n", vars[i].name, (char *) vars[i].var);
        break;
      case VAL_BOOL:
        fprintf(rc_file, "%s = %s\n", vars[i].name, *((bool *) vars[i].var) ? "ON" : "OFF");
        break;
      case VAL_ATTR:
        fprintf(rc_file, "%s = %s\n", vars[i].name, attr_to_str(((int *) vars[i].var)[0], ((int *) vars[i].var)[1], ((int *) vars[i].var)[2]));
        break;
    }
  }

  fclose(rc_file);
}
/* End of create_rc() */


/*
 * Parse the configuration file and set up variables
 */
int parse_rc(void)
{
  int i, l = 1, parse, fg, bg, hl;
  char str[MAX_LEN+1], *var, *value, *tempptr;
  FILE *rc_file;

  /*
   *
   *  At start, 'dialog' determines the settings to use as follows:
   *
   *  a) if environment variable DIALOGRC is set, it's value determines the
   *     name of the configuration file.
   *
   *  b) if the file in (a) can't be found, use the file $HOME/.dialogrc
   *     as the configuration file.
   *
   *  c) if the file in (b) can't be found, use compiled in defaults.
   *
   */

  if ((tempptr = getenv("DIALOGRC")) != NULL)
    rc_file = fopen(tempptr, "rt");

  if (tempptr == NULL || rc_file == NULL) {    /* step (a) failed? */
    /* try step (b) */
    if ((tempptr = getenv("HOME")) == NULL)
      return 0;    /* step (b) failed, use default values */

    if (tempptr[0] == '\0' || lastch(tempptr) == '/')
      sprintf(str, "%s%s", tempptr, DIALOGRC);
    else
      sprintf(str, "%s/%s", tempptr, DIALOGRC);

    if ((rc_file = fopen(str, "rt")) == NULL)
      return 0;    /* step (b) failed, use default values */
  }

  /* Scan each line and set variables */
  while (fgets(str, MAX_LEN, rc_file) != NULL) {
    if (lastch(str) != '\n') {    /* ignore rest of file if line too long */
      fprintf(stderr, "\nParse error: line %d of configuration file too long.\n", l);
      fclose(rc_file);
      return -1;    /* parse aborted */
    }
    else {
      lastch(str) = '\0';
      parse = parse_line(str, &var, &value);    /* parse current line */

      switch (parse) {
	case LINE_BLANK:    /* ignore blank lines and comments */
        case LINE_COMMENT:
          break;
        case LINE_OK:
          /* search table for matching config variable name */
          for (i = 0; i < VAR_COUNT && strcmp(vars[i].name, var); i++);

          if (i == VAR_COUNT) {    /* no match */
            fprintf(stderr, "\nParse error: unknown variable at line %d of configuration file.\n", l);
            return -1;    /* parse aborted */
          }
          else {    /* variable found in table, set run time variables */
            switch (vars[i].type) {
              case VAL_INT:
                *((int *) vars[i].var) = atoi(value);
                break;
              case VAL_STR:
                if (!isquote(value[0]) || !isquote(lastch(value)) || strlen(value) < 2) {
                  fprintf(stderr, "\nParse error: string value expected at line %d of configuration file.\n", l);
                  return -1;    /* parse aborted */
                }
                else {
                  /* remove the (") quotes */
                  value++;
                  lastch(value) = '\0';
                  strcpy((char *) vars[i].var, value);
		}
                break;
              case VAL_BOOL:
                if (!strcasecmp(value, "ON"))
                  *((bool *) vars[i].var) = TRUE;
                else if (!strcasecmp(value, "OFF"))
                  *((bool *) vars[i].var) = FALSE;
                else {
                  fprintf(stderr, "\nParse error: boolean value expected at line %d of configuration file.\n", l);
                  return -1;    /* parse aborted */
                }
                break;
              case VAL_ATTR:
                if (str_to_attr(value, &fg, &bg, &hl) == -1) {
                  fprintf(stderr, "\nParse error: attribute value expected at line %d of configuration file.\n", l);
                  return -1;    /* parse aborted */
                }
                ((int *) vars[i].var)[0] = fg;
                ((int *) vars[i].var)[1] = bg;
                ((int *) vars[i].var)[2] = hl;
                break;
            }
          }
          break;
        case LINE_ERROR:
          fprintf(stderr, "\nParse error: syntax error at line %d of configuration file.\n", l);
          return -1;    /* parse aborted */
      }
    }

    l++;    /* next line */
  }

  fclose(rc_file);
  return 0;    /* parse successful */
}
/* End of parse_rc() */


/*
 * Convert an attribute to a string representation like this:
 *
 * "(foreground,background,highlight)"
 */
static char *attr_to_str(int fg, int bg, int hl)
{
  int i;
  static char str[MAX_LEN+1];

  strcpy(str, "(");
  /* foreground */
  for (i = 0; fg != color_names[i].value; i++);
  strcat(str, color_names[i].name);
  strcat(str, ",");

  /* background */
  for (i = 0; bg != color_names[i].value; i++);
  strcat(str, color_names[i].name);

  /* highlight */
  strcat(str, hl ? ",ON)" : ",OFF)");

  return str;
}
/* End of attr_to_str() */


/*
 * Extract the foreground, background and highlight values from an attribute
 * represented as a string in this form:
 *
 * "(foreground,background,highlight)"
 */
static int str_to_attr(char *str, int *fg, int *bg, int *hl)
{
  int i = 0, j, get_fg = 1;
  char tempstr[MAX_LEN+1], *part;

  if (str[0] != '(' || lastch(str) != ')')
    return -1;    /* invalid representation */

  /* remove the parenthesis */
  strcpy(tempstr, str + 1);
  lastch(tempstr) = '\0';


  /* get foreground and background */

  while (1) {
    /* skip white space before fg/bg string */
    while (whitespace(tempstr[i]) && tempstr[i] != '\0') i++;
    if (tempstr[i] == '\0')
      return -1;    /* invalid representation */
    part = tempstr + i;    /* set 'part' to start of fg/bg string */

    /* find end of fg/bg string */
    while(!whitespace(tempstr[i]) && tempstr[i] != ',' && tempstr[i] != '\0') i++;

    if (tempstr[i] == '\0')
      return -1;    /* invalid representation */
    else if (whitespace(tempstr[i])) {   /* not yet ',' */
      tempstr[i++] = '\0';

      /* skip white space before ',' */
      while(whitespace(tempstr[i]) && tempstr[i] != '\0') i++;

      if (tempstr[i] != ',')
        return -1;    /* invalid representation */
    }

    tempstr[i++] = '\0';    /* skip the ',' */
    for (j = 0; j < COLOR_COUNT && strcasecmp(part, color_names[j].name); j++);
    if (j == COLOR_COUNT)    /* invalid color name */
      return -1;
    if (get_fg) {
      *fg = color_names[j].value;
      get_fg = 0;    /* next we have to get the background */
    }
    else {
      *bg = color_names[j].value;
      break;
    }
  }   /* got foreground and background */


  /* get highlight */

  /* skip white space before highlight string */
  while (whitespace(tempstr[i]) && tempstr[i] != '\0') i++;
  if (tempstr[i] == '\0')
    return -1;    /* invalid representation */
  part = tempstr + i;    /* set 'part' to start of highlight string */

  /* trim trailing white space from highlight string */
  i = strlen(part) - 1;
  while(whitespace(part[i])) i--;
  part[i+1] = '\0';

  if (!strcasecmp(part, "ON"))
    *hl = TRUE;
  else if (!strcasecmp(part, "OFF"))
    *hl = FALSE;
  else
    return -1;    /* invalid highlight value */

  return 0;
}
/* End of str_to_attr() */


/*
 * Parse a line in the configuration file
 *
 * Each line is of the form:  "variable = value". On exit, 'var' will contain
 * the variable name, and 'value' will contain the value string.
 *
 * Return values:
 *
 * LINE_BLANK   - line is blank
 * LINE_COMMENT - line is comment
 * LINE_OK      - line is ok
 * LINE_ERROR   - syntax error in line
 */
static int parse_line(char *line, char **var, char **value)
{
  int i = 0;

  /* ignore white space at beginning of line */
  while(whitespace(line[i]) && line[i] != '\0') i++;

  if (line[i] == '\0')    /* line is blank */
    return LINE_BLANK;
  else if (line[i] == '#')    /* line is comment */
    return LINE_COMMENT;
  else if (line[i] == '=')    /* variables names can't strart with a '=' */
    return LINE_ERROR;

  /* set 'var' to variable name */
  *var = line + i++;    /* skip to next character */

  /* find end of variable name */
  while(!whitespace(line[i]) && line[i] != '=' && line[i] != '\0') i++;

  if (line[i] == '\0')    /* syntax error */
    return LINE_ERROR;
  else if (line[i] == '=')
    line[i++] = '\0';
  else {
    line[i++] = '\0';

    /* skip white space before '=' */
    while(whitespace(line[i]) && line[i] != '\0') i++;

    if (line[i] != '=')    /* syntax error */
      return LINE_ERROR;
    else
      i++;    /* skip the '=' */
  }

  /* skip white space after '=' */
  while(whitespace(line[i]) && line[i] != '\0') i++;

  if (line[i] == '\0')
    return LINE_ERROR;
  else
    *value = line + i;    /* set 'value' to value string */

  /* trim trailing white space from 'value' */
  i = strlen(*value) - 1;
  while(whitespace((*value)[i])) i--;
  (*value)[i+1] = '\0';

  return LINE_OK;    /* no syntax error in line */
}
/* End of parse_line() */
#endif
