/* @configure_input@ */

/*
**  Copyright 1998-2002 University of Illinois Board of Trustees
**  Copyright 1998-2002 Mark D. Roth
**  All rights reserved.
**
**  @LISTHASH_PREFIX@_list.c - linked list routines
**
**  Mark D. Roth <roth@uiuc.edu>
**  Campus Information Technologies and Educational Services
**  University of Illinois at Urbana-Champaign
*/

#include <@LISTHASH_PREFIX@/config.h>
#include <@LISTHASH_PREFIX@/compat.h>

#include <@LISTHASH_PREFIX@/@LISTHASH_PREFIX@_listhash.h>

#include <stdio.h>
#include <errno.h>

#ifdef STDC_HEADERS
# include <string.h>
# include <stdlib.h>
#endif


/*
** @LISTHASH_PREFIX@_listptr_reset() - reset a list pointer
*/
void
@LISTHASH_PREFIX@_listptr_reset(@LISTHASH_PREFIX@_listptr_t *lp)
{
  *lp = NULL;
}


/*
** @LISTHASH_PREFIX@_listptr_data() - retrieve the data pointed to by lp
*/
void *
@LISTHASH_PREFIX@_listptr_data(@LISTHASH_PREFIX@_listptr_t *lp)
{
  return (*lp)->data;
}


/*
** @LISTHASH_PREFIX@_list_new() - create a new, empty list
*/
@LISTHASH_PREFIX@_list_t *
@LISTHASH_PREFIX@_list_new(int flags, @LISTHASH_PREFIX@_cmpfunc_t cmpfunc)
{
  @LISTHASH_PREFIX@_list_t *newlist;

#ifdef DS_DEBUG
  printf("in @LISTHASH_PREFIX@_list_new(%d, 0x%lx)\n", flags, cmpfunc);
#endif

  if (flags != LIST_USERFUNC
      && flags != LIST_STACK
      && flags != LIST_QUEUE)
  {
    errno = EINVAL;
    return NULL;
  }

  newlist = (@LISTHASH_PREFIX@_list_t *)calloc(1, sizeof(@LISTHASH_PREFIX@_list_t));
  if (cmpfunc != NULL)
    newlist->cmpfunc = cmpfunc;
  else
    newlist->cmpfunc = (@LISTHASH_PREFIX@_cmpfunc_t)strcmp;
  newlist->flags = flags;

  return newlist;
}


/*
** @LISTHASH_PREFIX@_list_iterate() - call a function for every element
**              in a list
*/
int
@LISTHASH_PREFIX@_list_iterate(@LISTHASH_PREFIX@_list_t *l,
             @LISTHASH_PREFIX@_iterate_func_t plugin,
             void *state)
{
  @LISTHASH_PREFIX@_listptr_t n;

  if (l == NULL)
    return -1;

  for (n = l->first; n != NULL; n = n->next)
  {
    if ((*plugin)(n->data, state) == -1)
      return -1;
  }

  return 0;
}


/*
** @LISTHASH_PREFIX@_list_empty() - empty the list
*/
void
@LISTHASH_PREFIX@_list_empty(@LISTHASH_PREFIX@_list_t *l, @LISTHASH_PREFIX@_freefunc_t freefunc)
{
  @LISTHASH_PREFIX@_listptr_t n;

  for (n = l->first; n != NULL; n = l->first)
  {
    l->first = n->next;
    if (freefunc != NULL)
      (*freefunc)(n->data);
    free(n);
  }

  l->nents = 0;
}


/*
** @LISTHASH_PREFIX@_list_free() - remove and free() the whole list
*/
void
@LISTHASH_PREFIX@_list_free(@LISTHASH_PREFIX@_list_t *l, @LISTHASH_PREFIX@_freefunc_t freefunc)
{
  @LISTHASH_PREFIX@_list_empty(l, freefunc);
  free(l);
}


/*
** @LISTHASH_PREFIX@_list_nents() - return number of elements in the list
*/
unsigned int
@LISTHASH_PREFIX@_list_nents(@LISTHASH_PREFIX@_list_t *l)
{
  return l->nents;
}


/*
** @LISTHASH_PREFIX@_list_add() - adds an element to the list
** returns:
**  0      success
**  -1 (and sets errno)  failure
*/
int
@LISTHASH_PREFIX@_list_add(@LISTHASH_PREFIX@_list_t *l, void *data)
{
  @LISTHASH_PREFIX@_listptr_t n, m;

#ifdef DS_DEBUG
  printf("==> @LISTHASH_PREFIX@_list_add(\"%s\")\n", (char *)data);
#endif

  n = (@LISTHASH_PREFIX@_listptr_t)malloc(sizeof(struct @LISTHASH_PREFIX@_node));
  if (n == NULL)
    return -1;
  n->data = data;
  l->nents++;

#ifdef DS_DEBUG
  printf("    @LISTHASH_PREFIX@_list_add(): allocated data\n");
#endif

  /* if the list is empty */
  if (l->first == NULL)
  {
    l->last = l->first = n;
    n->next = n->prev = NULL;
#ifdef DS_DEBUG
    printf("<== @LISTHASH_PREFIX@_list_add(): list was empty; "
           "added first element and returning 0\n");
#endif
    return 0;
  }

#ifdef DS_DEBUG
  printf("    @LISTHASH_PREFIX@_list_add(): list not empty\n");
#endif

  if (l->flags == LIST_STACK)
  {
    n->prev = NULL;
    n->next = l->first;
    if (l->first != NULL)
      l->first->prev = n;
    l->first = n;
#ifdef DS_DEBUG
    printf("<== @LISTHASH_PREFIX@_list_add(): LIST_STACK set; "
           "added in front\n");
#endif
    return 0;
  }

  if (l->flags == LIST_QUEUE)
  {
    n->prev = l->last;
    n->next = NULL;
    if (l->last != NULL)
      l->last->next = n;
    l->last = n;
#ifdef DS_DEBUG
    printf("<== @LISTHASH_PREFIX@_list_add(): LIST_QUEUE set; "
           "added at end\n");
#endif
    return 0;
  }

  for (m = l->first; m != NULL; m = m->next)
    if ((*(l->cmpfunc))(data, m->data) < 0)
    {
      /*
      ** if we find one that's bigger,
      ** insert data before it
      */
#ifdef DS_DEBUG
      printf("    @LISTHASH_PREFIX@_list_add(): gotcha..."
             "inserting data\n");
#endif
      if (m == l->first)
      {
        l->first = n;
        n->prev = NULL;
        m->prev = n;
        n->next = m;
#ifdef DS_DEBUG
        printf("<== @LISTHASH_PREFIX@_list_add(): "
               "added first, returning 0\n");
#endif
        return 0;
      }
      m->prev->next = n;
      n->prev = m->prev;
      m->prev = n;
      n->next = m;
#ifdef DS_DEBUG
      printf("<== @LISTHASH_PREFIX@_list_add(): added middle,"
             " returning 0\n");
#endif
      return 0;
    }

#ifdef DS_DEBUG
  printf("    @LISTHASH_PREFIX@_list_add(): new data larger than current "
         "list elements\n");
#endif

  /* if we get here, data is bigger than everything in the list */
  l->last->next = n;
  n->prev = l->last;
  l->last = n;
  n->next = NULL;
#ifdef DS_DEBUG
  printf("<== @LISTHASH_PREFIX@_list_add(): added end, returning 0\n");
#endif
  return 0;
}


/*
** @LISTHASH_PREFIX@_list_del() - remove the element pointed to by n
**          from the list l
*/
void
@LISTHASH_PREFIX@_list_del(@LISTHASH_PREFIX@_list_t *l, @LISTHASH_PREFIX@_listptr_t *n)
{
  @LISTHASH_PREFIX@_listptr_t m;

#ifdef DS_DEBUG
  printf("==> @LISTHASH_PREFIX@_list_del()\n");
#endif

  l->nents--;

  m = (*n)->next;

  if ((*n)->prev)
    (*n)->prev->next = (*n)->next;
  else
    l->first = (*n)->next;
  if ((*n)->next)
    (*n)->next->prev = (*n)->prev;
  else
    l->last = (*n)->prev;

  free(*n);
  *n = m;
}


/*
** @LISTHASH_PREFIX@_list_next() - get the next element in the list
** returns:
**  1      success
**  0      end of list
*/
int
@LISTHASH_PREFIX@_list_next(@LISTHASH_PREFIX@_list_t *l,
          @LISTHASH_PREFIX@_listptr_t *n)
{
  if (*n == NULL)
    *n = l->first;
  else
    *n = (*n)->next;

  return (*n != NULL ? 1 : 0);
}


/*
** @LISTHASH_PREFIX@_list_prev() - get the previous element in the list
** returns:
**  1      success
**  0      end of list
*/
int
@LISTHASH_PREFIX@_list_prev(@LISTHASH_PREFIX@_list_t *l,
          @LISTHASH_PREFIX@_listptr_t *n)
{
  if (*n == NULL)
    *n = l->last;
  else
    *n = (*n)->prev;

  return (*n != NULL ? 1 : 0);
}


/*
** @LISTHASH_PREFIX@_str_match() - string matching function
** returns:
**  1      match
**  0      no match
*/
int
@LISTHASH_PREFIX@_str_match(char *check, char *data)
{
  return !strcmp(check, data);
}


/*
** @LISTHASH_PREFIX@_list_add_str() - splits string str into delim-delimited
**              elements and adds them to list l
** returns:
**  0      success
**  -1 (and sets errno)  failure
*/
int
@LISTHASH_PREFIX@_list_add_str(@LISTHASH_PREFIX@_list_t *l,
             char *str, char *delim)
{
  char tmp[10240];
  char *tokp, *nextp = tmp;

  strlcpy(tmp, str, sizeof(tmp));
  while ((tokp = strsep(&nextp, delim)) != NULL)
  {
    if (*tokp == '\0')
      continue;
    if (@LISTHASH_PREFIX@_list_add(l, strdup(tokp)))
      return -1;
  }

  return 0;
}


/*
** @LISTHASH_PREFIX@_list_search() - find an entry in a list
** returns:
**  1      match found
**  0      no match
*/
int
@LISTHASH_PREFIX@_list_search(@LISTHASH_PREFIX@_list_t *l,
            @LISTHASH_PREFIX@_listptr_t *n, void *data,
            @LISTHASH_PREFIX@_matchfunc_t matchfunc)
{
#ifdef DS_DEBUG
  printf("==> @LISTHASH_PREFIX@_list_search(l=0x%lx, n=0x%lx, \"%s\")\n",
         l, n, (char *)data);
#endif

  if (matchfunc == NULL)
    matchfunc = (@LISTHASH_PREFIX@_matchfunc_t)@LISTHASH_PREFIX@_str_match;

  if (*n == NULL)
    *n = l->first;
  else
    *n = (*n)->next;

  for (; *n != NULL; *n = (*n)->next)
  {
#ifdef DS_DEBUG
    printf("checking against \"%s\"\n", (char *)(*n)->data);
#endif
    if ((*(matchfunc))(data, (*n)->data) != 0)
      return 1;
  }

#ifdef DS_DEBUG
  printf("no matches found\n");
#endif
  return 0;
}


/*
** @LISTHASH_PREFIX@_list_dup() - copy an existing list
*/
/*
static @LISTHASH_PREFIX@_list_t *
@LISTHASH_PREFIX@_list_dup(@LISTHASH_PREFIX@_list_t *l)
{
  @LISTHASH_PREFIX@_list_t *newlist;
  @LISTHASH_PREFIX@_listptr_t n;

  newlist = @LISTHASH_PREFIX@_list_new(l->flags, l->cmpfunc);
  for (n = l->first; n != NULL; n = n->next)
    @LISTHASH_PREFIX@_list_add(newlist, n->data);

#ifdef DS_DEBUG
  printf("returning from @LISTHASH_PREFIX@_list_dup()\n");
#endif
  return newlist;
}
*/


/*
** @LISTHASH_PREFIX@_list_merge() - merge two lists into a new list
*/
/*
static @LISTHASH_PREFIX@_list_t *
@LISTHASH_PREFIX@_list_merge(@LISTHASH_PREFIX@_cmpfunc_t cmpfunc, int flags,
           @LISTHASH_PREFIX@_list_t *list1,
           @LISTHASH_PREFIX@_list_t *list2)
{
  @LISTHASH_PREFIX@_list_t *newlist;
  @LISTHASH_PREFIX@_listptr_t n;

  newlist = @LISTHASH_PREFIX@_list_new(flags, cmpfunc);

  n = NULL;
  while (@LISTHASH_PREFIX@_list_next(list1, &n) != 0)
    @LISTHASH_PREFIX@_list_add(newlist, n->data);
  n = NULL;
  while (@LISTHASH_PREFIX@_list_next(list2, &n) != 0)
    @LISTHASH_PREFIX@_list_add(newlist, n->data);

  return newlist;
}
*/