/* Copyright (C) 2018 by John Schember <john@nachtimwald.com>
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in
 * advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 */

#include "ares_setup.h"
#include "ares_strsplit.h"
#include "ares.h"
#include "ares_private.h"

static int list_contains(char * const *list, size_t num_elem, const char *str, int insensitive)
{
  size_t len;
  size_t i;

  len = strlen(str);
  for (i=0; i<num_elem; i++)
  {
    if (insensitive)
    {
#ifdef WIN32
      if (strnicmp(list[i], str, len) == 0)
#else
      if (strncasecmp(list[i], str, len) == 0)
#endif
        return 1;
    }
    else
    {
      if (strncmp(list[i], str, len) == 0)
        return 1;
    }
  }

  return 0;
}

static int is_delim(char c, const char *delims, size_t num_delims)
{
  size_t i;

  for (i=0; i<num_delims; i++)
  {
    if (c == delims[i])
      return 1;
  }
  return 0;
}


void ares_strsplit_free(char **elms, size_t num_elm)
{
  size_t i;

  if (elms == NULL)
    return;

  for (i=0; i<num_elm; i++)
    ares_free(elms[i]);
  ares_free(elms);
}


char **ares_strsplit(const char *in, const char *delms, int make_set, size_t *num_elm)
{
  char *parsestr;
  char **temp;
  char **out;
  size_t cnt;
  size_t nelms;
  size_t in_len;
  size_t num_delims;
  size_t i;

  if (in == NULL || delms == NULL || num_elm == NULL)
    return NULL;

  *num_elm = 0;

  in_len = strlen(in);
  num_delims = strlen(delms);

  /* Figure out how many elements. */
  nelms = 1;
  for (i=0; i<in_len; i++)
  {
    if (is_delim(in[i], delms, num_delims))
    {
      nelms++;
    }
  }

  /* Copy of input so we can cut it up. */
  parsestr = ares_strdup(in);
  if (parsestr == NULL)
    return NULL;

  /* Temporary array to store locations of start of each element
   * within parsestr. */
  temp = ares_malloc(nelms * sizeof(*temp));
  if (temp == NULL)
  {
    ares_free(parsestr);
    return NULL;
  }
  temp[0] = parsestr;
  cnt = 1;
  for (i=0; i<in_len && cnt<nelms; i++)
  {
    if (!is_delim(parsestr[i], delms, num_delims))
      continue;

    /* Replace sep with NULL. */
    parsestr[i] = '\0';
    /* Add the pointer to the array of elements */
    temp[cnt] = parsestr+i+1;
    cnt++;
  }

  /* Copy each element to our output array. */
  out = ares_malloc(nelms * sizeof(*out));
  if (out == NULL)
  {
    ares_free(parsestr);
    ares_free(temp);
    return NULL;
  }

  nelms = 0;
  for (i=0; i<cnt; i++)
  {
    if (temp[i][0] == '\0')
      continue;

    if (make_set && list_contains(out, nelms, temp[i], 1))
      continue;

    out[nelms] = ares_strdup(temp[i]);
    if (out[nelms] == NULL)
    {
      ares_strsplit_free(out, nelms);
      ares_free(parsestr);
      ares_free(temp);
      return NULL;
    }
    nelms++;
  }


  /* If there are no elements don't return an empty allocated
   * array. */
  if (nelms == 0)
  {
    ares_strsplit_free(out, nelms);
    out = NULL;
  }

  /* Get the true number of elements (recalculated because of make_set) */
  *num_elm = nelms;

  ares_free(parsestr);
  ares_free(temp);
  return out;
}
