/*
 * configuration parser unit
 *
 * This file is part of the backconn project. http://backconn.sourceforge.net
 * Copyright (C) 2017, 2018 Mateusz Viste
 *
 * ===================== PUBLISHED UNDER THE MIT LICENSE =====================
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * ===========================================================================
 */

#include <stdio.h>   /* rewind(), fgetc()... */
#include <stdlib.h>  /* calloc() */
#include <string.h>  /* memmove() */

#include "config.h"


/* reads a single line from file, returns length of line on success, or -1 on EOF, or -2 on other errors */
static int freadline(char *buf, int bufsize, FILE *fp) {
  int c, reslen = 0;
  if (bufsize < 1) return(0);
  bufsize -= 1; /* just so it's easier to check later */
  for (;;) {
    c = fgetc(fp);
    if (c == EOF) {
      if (reslen == 0) return(-1);
      return(reslen);
    }
    if (c == '\n') {
      buf[reslen] = 0;
      return(reslen);
    }
    buf[reslen] = c;
    if (reslen < bufsize) reslen++;
  }
}

static void line2ptrs(char *line, char **res, int resz) {
  int i, r;
  /* init res array to all NULLs */
  for (i = 0; i < resz; i++) res[i] = NULL;
  /* */
  i = 0;
  r = 0;
  for (;;) {
    /* do I have some PTRs left? */
    if (r >= resz) break;
    /* skip to next non-whitespace char */
    while ((line[i] == ' ') || (line[i] == '\t')) i++;
    /* end of line? */
    if (line[i] == 0) break;  /* EOL */
    /* save ptr */
    res[r++] = line+i;
    /* skip to next white-space char */
    while ((line[i] != ' ') && (line[i] != '\t') && (line[i] != 0)) i++;
    /* EOL? */
    if (line[i] == 0) break;  /* EOL */
    /* insert a string terminator */
    line[i++] = 0;
  }
}

void config_free(struct scfg *cfg) {
  struct scfg *victim;
  while (cfg != NULL) {
    victim = cfg;
    cfg = cfg->next;
    free(victim);
  }
}

struct scfg *config_read(FILE *fp) {
  struct scfg *res = NULL;
  struct scfg *node;
  char buf[1024];
  rewind(fp);

  for (;;) {
    int linelen, i;
    char *ptrs[8];
    /* read line to buffer */
    linelen = freadline(buf, sizeof(buf), fp);
    if (linelen < 0) return(res);
    /* if comment or empty - skip it */
    if ((linelen == 0) || (buf[0] == '#')) continue;
    /* alloc and fill node */
    node = calloc(sizeof(struct scfg) + linelen, 1);
    if (node == NULL) {  /* out of memory */
      config_free(res);
      return(NULL);
    }
    memmove(node->data, buf, linelen + 1);
    /* tokenize the line */
    line2ptrs(node->data, ptrs, 6);
    if ((ptrs[0] == NULL) || (ptrs[0][0] == '#')) { /* empty line of comment */
      free(node);
      continue;
    }

    node->token = ptrs[0];
    for (i = 0; i < 4; i++) node->val[i] = ptrs[i+1];
    /* append node to queue */
    node->next = res;
    res = node;
  }
}

struct scfg *config_find(struct scfg *cfg, char *token) {
  while (cfg != NULL) {
    if (strcasecmp(cfg->token, token) == 0) return(cfg);
    cfg = cfg->next;
  }
  return(NULL);
}