/* set.c	8/19/1995
 * Database of runtime-cunfigurable options
 */

/* SPDX-FileCopyrightText: 1995,1996 Sascha Demetrio
 * SPDX-FileCopyrightText: 2009, 2015 Peter Pentchev
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <ctype.h>

#include "buffer.h"
#include "hexer.h"
#include "set.h"
#include "util.h"

struct option_s {
  char option[256];
  union {
    long ival; /* integer value */
    char sval[1024]; /* string value */
    int bval; /* boolean value */
  } u;
  enum s_option_e type;
  struct option_s *next;
  set_fn action;
};

static const char *true_strings[] = { "true", "t", "yes", "y", "on", 0 };
static const char *false_string = "false";

static struct option_s *option_first = 0;

  long
s_get_option_integer(const char *option)
{
  struct option_s *i;

  for (i = option_first; i; i = i->next)
    if (!strcmp(option, i->option)) {
      assert(i->type == S_INTEGER);
      return i->u.ival;
    }
  return -1;
}
/* s_get_option_integer */

  const char *
s_get_option_string(const char * const option)
{
  struct option_s *i;

  for (i = option_first; i; i = i->next)
    if (!strcmp(option, i->option)) {
      assert(i->type == S_STRING);
      return i->u.sval;
    }
  return "";
}
/* s_get_option_string */

  int
s_get_option_bool(const char *option)
{
  struct option_s *i;

  for (i = option_first; i; i = i->next)
    if (!strcmp(option, i->option)) {
      assert(i->type == S_BOOL);
      return i->u.bval;
    }
  return 0;
}
/* s_get_option_bool */

  int
s_delete_option(const char *option)
{
  struct option_s *i, *j;

  for (i = option_first, j = 0; i; j = i, i = i->next)
    if (!strcmp(option, i->option)) {
      if (j) j->next = i->next; else option_first = i->next;
      free((char *)i);
      return 0;
    }
  return -1;
}
/* s_delete_option */

  static void
s_append(struct option_s * const last,
         const char * const option,
         const enum s_option_e type, ...)
{
  va_list ap;
  struct option_s *i;

  va_start(ap, type);
  i = (struct option_s *)malloc_fatal(sizeof(struct option_s));
  i->type = type;
  i->next = 0;
  strcpy(i->option, option);
  if (last) last->next = i; else option_first = i;
  i->action = 0;
  switch (type) {
    case S_STRING:
      strcpy(i->u.sval, va_arg(ap, char *));
      break;
    case S_INTEGER:
      i->u.ival = va_arg(ap, long);
      break;
    case S_BOOL:
      i->u.bval = !!va_arg(ap, int);
      break;
    default:
      abort();
  }
  va_end(ap);
}
/* s_append */

  void
s_set_option_string(const char * const option, const char * const value)
{
  struct option_s *i, *j;

  for (i = option_first, j = 0; i; j = i, i = i->next)
    if (!strcmp(option, i->option)) {
      strcpy(i->u.sval, value);
      i->action = 0;
      i->type = S_STRING;
      return;
    }
  s_append(j, option, S_STRING, value);
}
/* s_set_option_string */

  void
s_set_option_integer(const char * const option, const long value)
{
  struct option_s *i, *j;

  for (i = option_first, j = 0; i; j = i, i = i->next)
    if (!strcmp(option, i->option)) {
      i->u.ival = value;
      i->type = S_INTEGER;
      i->action = 0;
      return;
    }
  s_append(j, option, S_INTEGER, value);
}
/* s_set_option_integer */

  void
s_set_option_bool(const char * const option, const int value)
{
  struct option_s *i, *j;

  for (i = option_first, j = 0; i; j = i, i = i->next)
    if (!strcmp(option, i->option)) {
      i->u.bval = value;
      i->type = S_BOOL;
      i->action = 0;
      return;
    }
  s_append(j, option, S_BOOL, value);
}
/* s_set_option_bool */

  enum s_option_e
s_get_type(const char *option)
{
  struct option_s *i;

  for (i = option_first; i; i = i->next)
    if (!strcmp(option, i->option)) return i->type;
  return (enum s_option_e)0;
}
/* s_get_type */

  const char *
s_get_option(const char *option)
{
  struct option_s *i;
  static char rval[1024];

  for (i = option_first; i; i = i->next)
    if (!strcmp(option, i->option)) break;
  if (!i) return "";
  switch (i->type) {
    case S_STRING:
      strcpy(rval, i->u.sval);
      break;
    case S_INTEGER:
      sprintf(rval, "%li", i->u.ival);
      break;
    case S_BOOL:
      if (i->u.bval)
	strcpy(rval, true_strings[0]);
      else
	strcpy(rval, false_string);
      break;
    default:
      abort();
  }
  return rval;
}
/* s_get_option */

  void
s_set_type(const char * const option, const enum s_option_e type)
{
  switch (type) {
    case S_STRING:
      s_set_option_string(option, "");
      break;
    case S_INTEGER:
      s_set_option_integer(option, 0);
      break;
    case S_BOOL:
      s_set_option_bool(option, 0);
      break;
    default:
      abort();
  }
}
/* s_set_type */

  void
s_set_option(const char * const option, const char * const value_string, const int no_action)
{
  struct option_s *i;
  int k;

  for (i = option_first; i; i = i->next)
    if (!strcmp(option, i->option)) break;
  assert(i);
  switch (i->type) {
    case S_STRING:
      strcpy(i->u.sval, value_string);
      if (i->action && !no_action) i->action(value_string);
      break;
    case S_INTEGER:
      i->u.ival = atol(value_string);
      if (i->action && !no_action) i->action(i->u.ival);
      break;
    case S_BOOL:
      i->u.bval = 0;
      for (k = 0; true_strings[k]; ++k)
	if (!strcasecmp(true_strings[k], value_string)) {
	  i->u.bval = 1;
	  break;
	}
      if (i->action && !no_action) i->action(i->u.bval);
      break;
    default:
      abort();
  }
}
/* s_set_option */

  void
s_set_action(const char * const option, const set_fn action)
{
  struct option_s *i;

  for (i = option_first; i; i = i->next)
    if (!strcmp(i->option, option)) {
      i->action = action;
      return;
    }
  abort();
}
/* s_set_action */

  char **
s_option_list(const char *prefix, int bool_only)
{
  struct option_s *i;
  char **list;
  int n;

  for (i = option_first, n = 0; i; i = i->next)
    if (!strncmp(i->option, prefix, strlen(prefix))) ++n;
  list = (char **)malloc_fatal((n + 1) * sizeof(char *));
  for (i = option_first, n = 0; i; i = i->next)
    if (!strncmp(i->option, prefix, strlen(prefix)))
      if (!bool_only || i->type == S_BOOL) {
        list[n] = (char *)malloc_fatal(strlen(i->option) + 1);
        strcpy(list[n], i->option);
        ++n;
      }
  list[n] = 0;
  return list;
}
/* s_option_list */

  char **
s_option_value_list(void)
{
  struct option_s *i;
  char **list;
  size_t n, option_maxlen = 0;

  for (i = option_first, n = 0; i; i = i->next, ++n)
    if (strlen(i->option) > option_maxlen) option_maxlen = strlen(i->option);
  list = (char **)malloc_fatal((n + 1) * sizeof(char *));
  for (i = option_first, n = 0; i; i = i->next, ++n) {
    list[n] = (char *)malloc_fatal(option_maxlen + 4
                             + strlen(s_get_option(i->option)));
    memset(list[n], ' ', option_maxlen + 2);
    strcpy(list[n], i->option);
    list[n][strlen(list[n])] = ' ';
    strcpy(list[n] + option_maxlen + 2, s_get_option(i->option));
    strcpy(list[n] + strlen(list[n]), "\n");
  }
  list[n] = 0;
  return list;
}
/* s_option_value_list */
    
  void
s_clear_all(void)
{
  struct option_s *i, *j;

  if (option_first) {
    for (j = option_first, i = j->next; i; j = i, i = i->next) free((char *)j);
    free((char *)j);
    option_first = 0;
  }
}
/* s_clear_all */

/* end of set.c */


/* VIM configuration: (do not delete this line)
 *
 * vim:bk:nodg:efm=%f\:%l\:%m:hid:icon:
 * vim:sw=2:sm:textwidth=79:ul=1024:wrap:
 */
