/*
 * AnyOption 1.3
 *
 * kishan at hackorama dot com  www.hackorama.com JULY 2001
 *
 * + Acts as a common facade class for reading
 *   command line options as well as options from
 *   an option file with delimited type value pairs
 *
 * + Handles the POSIX style single character options ( -w )
 *   as well as the newer GNU long options ( --width )
 *
 * + The option file assumes the traditional format of
 *   first character based comment lines and type value
 *   pairs with a delimiter , and flags which are not pairs
 *
 *  	# this is a comment
 *  	# next line is an option value pair
 *  	width : 100
 *     	# next line is a flag
 *      noimages
 *
 * + Supports printing out Help and Usage
 *
 * + Why not just use getopt() ?
 *
 *   getopt() Its a POSIX standard not part of ANSI-C.
 *   So it may not be available on platforms like Windows.
 *
 * + Why it is so long ?
 *
 *   The actual code which does command line parsing
 *   and option file parsing are done in  few methods.
 *   Most of the extra code are for providing a flexible
 *   common public interface to both a resource file
 *   and command line supporting POSIX style and
 *   GNU long option as well as mixing of both.
 *
 * + Please see "anyoption.h" for public method descriptions
 *
 */

/* Updated August 2004
 * Fix from  Michael D Peters (mpeters at sandia.gov)
 * to remove static local variables, allowing multiple instantiations
 * of the reader (for using multiple configuration files).  There is
 * an error in the destructor when using multiple instances, so you
 * cannot delete your objects (it will crash), but not calling the
 * destructor only introduces a small memory leak, so I
 * have not bothered tracking it down.
 *
 * Also updated to use modern C++ style headers, rather than
 * deprecated iostream.h (it was causing my compiler problems)
 */

/*
 * Updated September 2006
 * Fix from Boyan Asenov for a bug in mixing up option indexes
 * leading to exception when mixing different options types
 */

#include "anyoption.h"

AnyOption::AnyOption() { init(); }

AnyOption::AnyOption(int maxopt) { init(maxopt, maxopt); }

AnyOption::AnyOption(int maxopt, int maxcharopt) { init(maxopt, maxcharopt); }

AnyOption::~AnyOption() {
  if (mem_allocated)
    cleanup();
}

void AnyOption::init() { init(DEFAULT_MAXOPTS, DEFAULT_MAXOPTS); }

void AnyOption::init(int maxopt, int maxcharopt) {

  max_options = maxopt;
  max_char_options = maxcharopt;
  max_usage_lines = DEFAULT_MAXUSAGE;
  usage_lines = 0;
  argc = 0;
  argv = NULL;
  posix_style = true;
  verbose = false;
  filename = NULL;
  appname = NULL;
  option_counter = 0;
  optchar_counter = 0;
  new_argv = NULL;
  new_argc = 0;
  max_legal_args = 0;
  command_set = false;
  file_set = false;
  values = NULL;
  g_value_counter = 0;
  mem_allocated = false;
  opt_prefix_char = '-';
  file_delimiter_char = ':';
  file_comment_char = '#';
  equalsign = '=';
  comment = '#';
  delimiter = ':';
  endofline = '\n';
  whitespace = ' ';
  nullterminate = '\0';
  set = false;
  once = true;
  hasoptions = false;
  autousage = false;
  print_usage = false;
  print_help = false;

  strcpy(long_opt_prefix, "--");

  if (alloc() == false) {
    cout << endl << "OPTIONS ERROR : Failed allocating memory";
    cout << endl;
    cout << "Exiting." << endl;
    exit(0);
  }
}

bool AnyOption::alloc() {
  int i = 0;
  int size = 0;

  if (mem_allocated)
    return true;

  size = (max_options + 1) * sizeof(const char *);
  options = (const char **)malloc(size);
  optiontype = (int *)malloc((max_options + 1) * sizeof(int));
  optionindex = (int *)malloc((max_options + 1) * sizeof(int));
  if (options == NULL || optiontype == NULL || optionindex == NULL)
    return false;
  else
    mem_allocated = true;
  for (i = 0; i < max_options; i++) {
    options[i] = NULL;
    optiontype[i] = 0;
    optionindex[i] = -1;
  }
  optionchars = (char *)malloc((max_char_options + 1) * sizeof(char));
  optchartype = (int *)malloc((max_char_options + 1) * sizeof(int));
  optcharindex = (int *)malloc((max_char_options + 1) * sizeof(int));
  if (optionchars == NULL || optchartype == NULL || optcharindex == NULL) {
    mem_allocated = false;
    return false;
  }
  for (i = 0; i < max_char_options; i++) {
    optionchars[i] = '0';
    optchartype[i] = 0;
    optcharindex[i] = -1;
  }

  size = (max_usage_lines + 1) * sizeof(const char *);
  usage = (const char **)malloc(size);

  if (usage == NULL) {
    mem_allocated = false;
    return false;
  }
  for (i = 0; i < max_usage_lines; i++)
    usage[i] = NULL;

  return true;
}

void AnyOption::allocValues(int index, size_t length) {
  if (values[index] == NULL) {
    values[index] = (char *)malloc(length);
  } else {
    free(values[index]);
    values[index] = (char *)malloc(length);
  }
}

bool AnyOption::doubleOptStorage() {
  const char **options_saved = options;
  options = (const char **)realloc(options, ((2 * max_options) + 1) *
                                                sizeof(const char *));
  if (options == NULL) {
    free(options_saved);
    return false;
  }
  int *optiontype_saved = optiontype;
  optiontype =
      (int *)realloc(optiontype, ((2 * max_options) + 1) * sizeof(int));
  if (optiontype == NULL) {
    free(optiontype_saved);
    return false;
  }
  int *optionindex_saved = optionindex;
  optionindex =
      (int *)realloc(optionindex, ((2 * max_options) + 1) * sizeof(int));
  if (optionindex == NULL) {
    free(optionindex_saved);
    return false;
  }
  /* init new storage */
  for (int i = max_options; i < 2 * max_options; i++) {
    options[i] = NULL;
    optiontype[i] = 0;
    optionindex[i] = -1;
  }
  max_options = 2 * max_options;
  return true;
}

bool AnyOption::doubleCharStorage() {
  char *optionchars_saved = optionchars;
  optionchars =
      (char *)realloc(optionchars, ((2 * max_char_options) + 1) * sizeof(char));
  if (optionchars == NULL) {
    free(optionchars_saved);
    return false;
  }
  int *optchartype_saved = optchartype;
  optchartype =
      (int *)realloc(optchartype, ((2 * max_char_options) + 1) * sizeof(int));
  if (optchartype == NULL) {
    free(optchartype_saved);
    return false;
  }
  int *optcharindex_saved = optcharindex;
  optcharindex =
      (int *)realloc(optcharindex, ((2 * max_char_options) + 1) * sizeof(int));
  if (optcharindex == NULL) {
    free(optcharindex_saved);
    return false;
  }
  /* init new storage */
  for (int i = max_char_options; i < 2 * max_char_options; i++) {
    optionchars[i] = '0';
    optchartype[i] = 0;
    optcharindex[i] = -1;
  }
  max_char_options = 2 * max_char_options;
  return true;
}

bool AnyOption::doubleUsageStorage() {
  const char **usage_saved = usage;
  usage = (const char **)realloc(usage, ((2 * max_usage_lines) + 1) *
                                            sizeof(const char *));
  if (usage == NULL) {
    free(usage_saved);
    return false;
  }
  for (int i = max_usage_lines; i < 2 * max_usage_lines; i++)
    usage[i] = NULL;
  max_usage_lines = 2 * max_usage_lines;
  return true;
}

void AnyOption::cleanup() {
  free(options);
  free(optiontype);
  free(optionindex);
  free(optionchars);
  free(optchartype);
  free(optcharindex);
  free(usage);
  if (values != NULL) {
    for (int i = 0; i < g_value_counter; i++) {
      free(values[i]);
      values[i] = NULL;
    }
    free(values);
  }
  if (new_argv != NULL)
    free(new_argv);
}

void AnyOption::setCommandPrefixChar(char _prefix) {
  opt_prefix_char = _prefix;
}

void AnyOption::setCommandLongPrefix(const char *_prefix) {
  if (strlen(_prefix) > MAX_LONG_PREFIX_LENGTH) {
    strncpy(long_opt_prefix, _prefix, MAX_LONG_PREFIX_LENGTH);
    long_opt_prefix[MAX_LONG_PREFIX_LENGTH] = nullterminate;
  } else {
    strcpy(long_opt_prefix, _prefix);
  }
}

void AnyOption::setFileCommentChar(char _comment) {
  file_delimiter_char = _comment;
}

void AnyOption::setFileDelimiterChar(char _delimiter) {
  file_comment_char = _delimiter;
}

bool AnyOption::CommandSet() const { return (command_set); }

bool AnyOption::FileSet() const { return (file_set); }

void AnyOption::noPOSIX() { posix_style = false; }

bool AnyOption::POSIX() const { return posix_style; }

void AnyOption::setVerbose() { verbose = true; }

void AnyOption::printVerbose() const {
  if (verbose)
    cout << endl;
}
void AnyOption::printVerbose(const char *msg) const {
  if (verbose)
    cout << msg;
}

void AnyOption::printVerbose(char *msg) const {
  if (verbose)
    cout << msg;
}

void AnyOption::printVerbose(char ch) const {
  if (verbose)
    cout << ch;
}

bool AnyOption::hasOptions() const { return hasoptions; }

void AnyOption::autoUsagePrint(bool _autousage) { autousage = _autousage; }

void AnyOption::useCommandArgs(int _argc, char **_argv) {
  argc = _argc;
  argv = _argv;
  command_set = true;
  appname = argv[0];
  if (argc > 1)
    hasoptions = true;
}

void AnyOption::useFiileName(const char *_filename) {
  filename = _filename;
  file_set = true;
}

/*
 * set methods for options
 */

void AnyOption::setCommandOption(const char *opt) {
  addOption(opt, COMMAND_OPT);
  g_value_counter++;
}

void AnyOption::setCommandOption(char opt) {
  addOption(opt, COMMAND_OPT);
  g_value_counter++;
}

void AnyOption::setCommandOption(const char *opt, char optchar) {
  addOption(opt, COMMAND_OPT);
  addOption(optchar, COMMAND_OPT);
  g_value_counter++;
}

void AnyOption::setCommandFlag(const char *opt) {
  addOption(opt, COMMAND_FLAG);
  g_value_counter++;
}

void AnyOption::setCommandFlag(char opt) {
  addOption(opt, COMMAND_FLAG);
  g_value_counter++;
}

void AnyOption::setCommandFlag(const char *opt, char optchar) {
  addOption(opt, COMMAND_FLAG);
  addOption(optchar, COMMAND_FLAG);
  g_value_counter++;
}

void AnyOption::setFileOption(const char *opt) {
  addOption(opt, FILE_OPT);
  g_value_counter++;
}

void AnyOption::setFileOption(char opt) {
  addOption(opt, FILE_OPT);
  g_value_counter++;
}

void AnyOption::setFileOption(const char *opt, char optchar) {
  addOption(opt, FILE_OPT);
  addOption(optchar, FILE_OPT);
  g_value_counter++;
}

void AnyOption::setFileFlag(const char *opt) {
  addOption(opt, FILE_FLAG);
  g_value_counter++;
}

void AnyOption::setFileFlag(char opt) {
  addOption(opt, FILE_FLAG);
  g_value_counter++;
}

void AnyOption::setFileFlag(const char *opt, char optchar) {
  addOption(opt, FILE_FLAG);
  addOption(optchar, FILE_FLAG);
  g_value_counter++;
}

void AnyOption::setOption(const char *opt) {
  addOption(opt, COMMON_OPT);
  g_value_counter++;
}

void AnyOption::setOption(char opt) {
  addOption(opt, COMMON_OPT);
  g_value_counter++;
}

void AnyOption::setOption(const char *opt, char optchar) {
  addOption(opt, COMMON_OPT);
  addOption(optchar, COMMON_OPT);
  g_value_counter++;
}

void AnyOption::setFlag(const char *opt) {
  addOption(opt, COMMON_FLAG);
  g_value_counter++;
}

void AnyOption::setFlag(const char opt) {
  addOption(opt, COMMON_FLAG);
  g_value_counter++;
}

void AnyOption::setFlag(const char *opt, char optchar) {
  addOption(opt, COMMON_FLAG);
  addOption(optchar, COMMON_FLAG);
  g_value_counter++;
}

void AnyOption::addOption(const char *opt, int type) {
  if (option_counter >= max_options) {
    if (doubleOptStorage() == false) {
      addOptionError(opt);
      return;
    }
  }
  options[option_counter] = opt;
  optiontype[option_counter] = type;
  optionindex[option_counter] = g_value_counter;
  option_counter++;
}

void AnyOption::addOption(char opt, int type) {
  if (!POSIX()) {
    printVerbose("Ignoring the option character \"");
    printVerbose(opt);
    printVerbose("\" ( POSIX options are turned off )");
    printVerbose();
    return;
  }

  if (optchar_counter >= max_char_options) {
    if (doubleCharStorage() == false) {
      addOptionError(opt);
      return;
    }
  }
  optionchars[optchar_counter] = opt;
  optchartype[optchar_counter] = type;
  optcharindex[optchar_counter] = g_value_counter;
  optchar_counter++;
}

void AnyOption::addOptionError(const char *opt) const {
  cout << endl;
  cout << "OPTIONS ERROR : Failed allocating extra memory " << endl;
  cout << "While adding the option : \"" << opt << "\"" << endl;
  cout << "Exiting." << endl;
  cout << endl;
  exit(0);
}

void AnyOption::addOptionError(char opt) const {
  cout << endl;
  cout << "OPTIONS ERROR : Failed allocating extra memory " << endl;
  cout << "While adding the option: \"" << opt << "\"" << endl;
  cout << "Exiting." << endl;
  cout << endl;
  exit(0);
}

void AnyOption::processOptions() {
  if (!valueStoreOK())
    return;
}

void AnyOption::processCommandArgs(int max_args) {
  max_legal_args = max_args;
  processCommandArgs();
}

void AnyOption::processCommandArgs(int _argc, char **_argv, int max_args) {
  max_legal_args = max_args;
  processCommandArgs(_argc, _argv);
}

void AnyOption::processCommandArgs(int _argc, char **_argv) {
  useCommandArgs(_argc, _argv);
  processCommandArgs();
}

void AnyOption::processCommandArgs() {
  if (!(valueStoreOK() && CommandSet()))
    return;

  if (max_legal_args == 0)
    max_legal_args = argc;
  new_argv = (int *)malloc((max_legal_args + 1) * sizeof(int));
  for (int i = 1; i < argc; i++) { /* ignore first argv */
    if (argv[i][0] == long_opt_prefix[0] &&
        argv[i][1] == long_opt_prefix[1]) { /* long GNU option */
      int match_at = parseGNU(argv[i] + 2); /* skip -- */
      if (match_at >= 0 && i < argc - 1)    /* found match */
        setValue(options[match_at], argv[++i]);
    } else if (argv[i][0] == opt_prefix_char) { /* POSIX char */
      if (POSIX()) {
        char ch = parsePOSIX(argv[i] + 1); /* skip - */
        if (ch != '0' && i < argc - 1)     /* matching char */
          setValue(ch, argv[++i]);
      } else { /* treat it as GNU option with a - */
        int match_at = parseGNU(argv[i] + 1); /* skip - */
        if (match_at >= 0 && i < argc - 1)    /* found match */
          setValue(options[match_at], argv[++i]);
      }
    } else { /* not option but an argument keep index */
      if (new_argc < max_legal_args) {
        new_argv[new_argc] = i;
        new_argc++;
      } else { /* ignore extra arguments */
        printVerbose("Ignoring extra argument: ");
        printVerbose(argv[i]);
        printVerbose();
        printAutoUsage();
      }
      printVerbose("Unknown command argument option : ");
      printVerbose(argv[i]);
      printVerbose();
      printAutoUsage();
    }
  }
}

char AnyOption::parsePOSIX(char *arg) {

  for (unsigned int i = 0; i < strlen(arg); i++) {
    char ch = arg[i];
    if (matchChar(ch)) { /* keep matching flags till an option */
      /*if last char argv[++i] is the value */
      if (i == strlen(arg) - 1) {
        return ch;
      } else { /* else the rest of arg is the value */
        i++;   /* skip any '=' and ' ' */
        while (arg[i] == whitespace || arg[i] == equalsign)
          i++;
        setValue(ch, arg + i);
        return '0';
      }
    }
  }
  printVerbose("Unknown command argument option : ");
  printVerbose(arg);
  printVerbose();
  printAutoUsage();
  return '0';
}

int AnyOption::parseGNU(char *arg) {
  size_t split_at = 0;
  /* if has a '=' sign get value */
  for (size_t i = 0; i < strlen(arg); i++) {
    if (arg[i] == equalsign) {
      split_at = i;    /* store index */
      i = strlen(arg); /* get out of loop */
    }
  }
  if (split_at > 0) { /* it is an option value pair */
    char *tmp = (char *)malloc((split_at + 1) * sizeof(char));
    for (size_t i = 0; i < split_at; i++)
      tmp[i] = arg[i];
    tmp[split_at] = '\0';

    if (matchOpt(tmp) >= 0) {
      setValue(options[matchOpt(tmp)], arg + split_at + 1);
      free(tmp);
    } else {
      printVerbose("Unknown command argument option : ");
      printVerbose(arg);
      printVerbose();
      printAutoUsage();
      free(tmp);
      return -1;
    }
  } else { /* regular options with no '=' sign  */
    return matchOpt(arg);
  }
  return -1;
}

int AnyOption::matchOpt(char *opt) {
  for (int i = 0; i < option_counter; i++) {
    if (strcmp(options[i], opt) == 0) {
      if (optiontype[i] == COMMON_OPT ||
          optiontype[i] == COMMAND_OPT) { /* found option return index */
        return i;
      } else if (optiontype[i] == COMMON_FLAG ||
                 optiontype[i] == COMMAND_FLAG) { /* found flag, set it */
        setFlagOn(opt);
        return -1;
      }
    }
  }
  printVerbose("Unknown command argument option : ");
  printVerbose(opt);
  printVerbose();
  printAutoUsage();
  return -1;
}
bool AnyOption::matchChar(char c) {
  for (int i = 0; i < optchar_counter; i++) {
    if (optionchars[i] == c) { /* found match */
      if (optchartype[i] == COMMON_OPT ||
          optchartype[i] ==
              COMMAND_OPT) { /* an option store and stop scanning */
        return true;
      } else if (optchartype[i] == COMMON_FLAG ||
                 optchartype[i] ==
                     COMMAND_FLAG) { /* a flag store and keep scanning */
        setFlagOn(c);
        return false;
      }
    }
  }
  printVerbose("Unknown command argument option : ");
  printVerbose(c);
  printVerbose();
  printAutoUsage();
  return false;
}

bool AnyOption::valueStoreOK() {
  if (!set) {
    if (g_value_counter > 0) {
      const int size = g_value_counter * sizeof(char *);
      values = (char **)malloc(size);
      for (int i = 0; i < g_value_counter; i++)
        values[i] = NULL;
      set = true;
    }
  }
  return set;
}

/*
 * public get methods
 */
char *AnyOption::getValue(const char *option) {
  if (!valueStoreOK())
    return NULL;

  for (int i = 0; i < option_counter; i++) {
    if (strcmp(options[i], option) == 0)
      return values[optionindex[i]];
  }
  return NULL;
}

bool AnyOption::getFlag(const char *option) {
  if (!valueStoreOK())
    return false;
  for (int i = 0; i < option_counter; i++) {
    if (strcmp(options[i], option) == 0)
      return findFlag(values[optionindex[i]]);
  }
  return false;
}

char *AnyOption::getValue(char option) {
  if (!valueStoreOK())
    return NULL;
  for (int i = 0; i < optchar_counter; i++) {
    if (optionchars[i] == option)
      return values[optcharindex[i]];
  }
  return NULL;
}

bool AnyOption::getFlag(char option) {
  if (!valueStoreOK())
    return false;
  for (int i = 0; i < optchar_counter; i++) {
    if (optionchars[i] == option)
      return findFlag(values[optcharindex[i]]);
  }
  return false;
}

bool AnyOption::findFlag(char *val) {
  if (val == NULL)
    return false;

  if (strcmp(TRUE_FLAG, val) == 0)
    return true;

  return false;
}

/*
 * private set methods
 */
bool AnyOption::setValue(const char *option, char *value) {
  if (!valueStoreOK())
    return false;
  for (int i = 0; i < option_counter; i++) {
    if (strcmp(options[i], option) == 0) {
      size_t length = (strlen(value) + 1) * sizeof(char);
      allocValues(optionindex[i], length);
      strncpy(values[optionindex[i]], value, length);
      return true;
    }
  }
  return false;
}

bool AnyOption::setFlagOn(const char *option) {
  if (!valueStoreOK())
    return false;
  for (int i = 0; i < option_counter; i++) {
    if (strcmp(options[i], option) == 0) {
      size_t length = (strlen(TRUE_FLAG) + 1) * sizeof(char);
      allocValues(optionindex[i], length);
      strncpy(values[optionindex[i]], TRUE_FLAG, length);
      return true;
    }
  }
  return false;
}

bool AnyOption::setValue(char option, char *value) {
  if (!valueStoreOK())
    return false;
  for (int i = 0; i < optchar_counter; i++) {
    if (optionchars[i] == option) {
      size_t length = (strlen(value) + 1) * sizeof(char);
      allocValues(optcharindex[i], length);
      strncpy(values[optcharindex[i]], value, length);
      return true;
    }
  }
  return false;
}

bool AnyOption::setFlagOn(char option) {
  if (!valueStoreOK())
    return false;
  for (int i = 0; i < optchar_counter; i++) {
    if (optionchars[i] == option) {
      size_t length = (strlen(TRUE_FLAG) + 1) * sizeof(char);
      allocValues(optcharindex[i], length);
      strncpy(values[optcharindex[i]], TRUE_FLAG, length);
      return true;
    }
  }
  return false;
}

int AnyOption::getArgc() const { return new_argc; }

char *AnyOption::getArgv(int index) const {
  if (index < new_argc) {
    return (argv[new_argv[index]]);
  }
  return NULL;
}

/* option file sub routines */

bool AnyOption::processFile() {
  if (!(valueStoreOK() && FileSet()))
    return false;
  return hasoptions = (consumeFile(readFile()));
}

bool AnyOption::processFile(const char *_filename) {
  useFiileName(_filename);
  return (processFile());
}

char *AnyOption::readFile() { return (readFile(filename)); }

/*
 * read the file contents to a character buffer
 */

char *AnyOption::readFile(const char *fname) {
  char *buffer;
  ifstream is;
  is.open(fname, ifstream::in);
  if (!is.good()) {
    is.close();
    return NULL;
  }
  is.seekg(0, ios::end);
  size_t length = (size_t)is.tellg();
  is.seekg(0, ios::beg);
  buffer = (char *)malloc((length + 1) * sizeof(char));
  is.read(buffer, length);
  is.close();
  buffer[length] = nullterminate;
  return buffer;
}

/*
 * scans a char* buffer for lines that does not
 * start with the specified comment character.
 */
bool AnyOption::consumeFile(char *buffer) {

  if (buffer == NULL)
    return false;

  char *cursor = buffer; /* preserve the ptr */
  char *pline = NULL;
  int linelength = 0;
  bool newline = true;
  for (unsigned int i = 0; i < strlen(buffer); i++) {
    if (*cursor == endofline) { /* end of line */
      if (pline != NULL)        /* valid line */
        processLine(pline, linelength);
      pline = NULL;
      newline = true;
    } else if (newline) { /* start of line */
      newline = false;
      if ((*cursor != comment)) { /* not a comment */
        pline = cursor;
        linelength = 0;
      }
    }
    cursor++; /* keep moving */
    linelength++;
  }
  free(buffer);
  return true;
}

/*
 *  find a valid type value pair separated by a delimiter
 *  character and pass it to valuePairs()
 *  any line which is not valid will be considered a value
 *  and will get passed on to justValue()
 *
 *  assuming delimiter is ':' the behaviour will be,
 *
 *  width:10    - valid pair valuePairs( width, 10 );
 *  width : 10  - valid pair valuepairs( width, 10 );
 *
 *  ::::        - not valid
 *  width       - not valid
 *  :10         - not valid
 *  width:      - not valid
 *  ::          - not valid
 *  :           - not valid
 *
 */

void AnyOption::processLine(char *theline, int length) {
  char *pline = (char *)malloc((length + 1) * sizeof(char));
  for (int i = 0; i < length; i++)
    pline[i] = *(theline++);
  pline[length] = nullterminate;
  char *cursor = pline; /* preserve the ptr */
  if (*cursor == delimiter || *(cursor + length - 1) == delimiter) {
    justValue(pline); /* line with start/end delimiter */
  } else {
    bool found = false;
    for (int i = 1; i < length - 1 && !found; i++) { /* delimiter */
      if (*cursor == delimiter) {
        *(cursor - 1) = nullterminate; /* two strings */
        found = true;
        valuePairs(pline, cursor + 1);
      }
      cursor++;
    }
    cursor++;
    if (!found) /* not a pair */
      justValue(pline);
  }
  free(pline);
}

/*
 * removes trailing and preceding white spaces from a string
 */
char *AnyOption::chomp(char *str) {
  while (*str == whitespace)
    str++;
  char *end = str + strlen(str) - 1;
  while (*end == whitespace)
    end--;
  *(end + 1) = nullterminate;
  return str;
}

void AnyOption::valuePairs(char *type, char *value) {
  if (strlen(chomp(type)) == 1) { /* this is a char option */
    for (int i = 0; i < optchar_counter; i++) {
      if (optionchars[i] == type[0]) { /* match */
        if (optchartype[i] == COMMON_OPT || optchartype[i] == FILE_OPT) {
          setValue(type[0], chomp(value));
          return;
        }
      }
    }
  }
  /* if no char options matched */
  for (int i = 0; i < option_counter; i++) {
    if (strcmp(options[i], type) == 0) { /* match */
      if (optiontype[i] == COMMON_OPT || optiontype[i] == FILE_OPT) {
        setValue(type, chomp(value));
        return;
      }
    }
  }
  printVerbose("Unknown option in resource file : ");
  printVerbose(type);
  printVerbose();
}

void AnyOption::justValue(char *type) {

  if (strlen(chomp(type)) == 1) { /* this is a char option */
    for (int i = 0; i < optchar_counter; i++) {
      if (optionchars[i] == type[0]) { /* match */
        if (optchartype[i] == COMMON_FLAG || optchartype[i] == FILE_FLAG) {
          setFlagOn(type[0]);
          return;
        }
      }
    }
  }
  /* if no char options matched */
  for (int i = 0; i < option_counter; i++) {
    if (strcmp(options[i], type) == 0) { /* match */
      if (optiontype[i] == COMMON_FLAG || optiontype[i] == FILE_FLAG) {
        setFlagOn(type);
        return;
      }
    }
  }
  printVerbose("Unknown option in resource file : ");
  printVerbose(type);
  printVerbose();
}

/*
 * usage and help
 */

void AnyOption::printAutoUsage() {
  if (autousage)
    printUsage();
}

void AnyOption::printUsage() {

  if (once) {
    once = false;
    cout << endl;
    for (int i = 0; i < usage_lines; i++)
      cout << usage[i] << endl;
    cout << endl;
  }
}

void AnyOption::addUsage(const char *line) {
  if (usage_lines >= max_usage_lines) {
    if (doubleUsageStorage() == false) {
      addUsageError(line);
      exit(1);
    }
  }
  usage[usage_lines] = line;
  usage_lines++;
}

void AnyOption::addUsageError(const char *line) {
  cout << endl;
  cout << "OPTIONS ERROR : Failed allocating extra memory " << endl;
  cout << "While adding the usage/help  : \"" << line << "\"" << endl;
  cout << "Exiting." << endl;
  cout << endl;
  exit(0);
}