/*
 * avrdude - A Downloader/Uploader for AVR device programmers
 * Copyright (C) 1990-2004 Brian S. Dean <bsd@bdmicro.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/*------------------------------------------------------------------------
  lists.c

  General purpose linked list routines.  These routines implement a
  generic doubly linked list.  Any data type may be placed in the
  lists.  Stacking and Queuing routines are provided via #defines
  declared in 'lists.h'.

  Author : Brian Dean
  Date   : 10 January, 1990
  ------------------------------------------------------------------------*/

#include <ac_cfg.h>

#include <stdio.h>
#include <stdlib.h>

#include "avrdude.h"
#include "libavrdude.h"

#define MAGIC 0xb05b05b0

#define CHECK_MAGIC 0           // Set to 1 to enable memory overwrite detection

#define MALLOC(size, x) mmt_malloc(size)
#define FREE            mmt_free

/*------------------------------------------------------------
|  required private data structures
 ------------------------------------------------------------*/
typedef struct LISTNODE {

#if CHECK_MAGIC
  unsigned int magic1;
#endif

  struct LISTNODE *next;        // Chain to next item in the list
  struct LISTNODE *prev;        // Chain to previous item in the list
  void *data;                   // Pointer to user data

#if CHECK_MAGIC
  unsigned int magic2;
#endif
} LISTNODE;

typedef struct NODEPOOL {

#if CHECK_MAGIC
  unsigned int magic1;
#endif

  struct NODEPOOL *chain_next;  // Chain to next node pool
  struct NODEPOOL *chain_prev;  // Chain to previous node pool

#if CHECK_MAGIC
  unsigned int magic2;
#endif
} NODEPOOL;

typedef struct LIST {

#if CHECK_MAGIC
  unsigned int magic1;
#endif

  int num;                      // Number of elements in the list
  short int free_on_close;      // Free the LIST memory on close T/F
  short int poolsize;           // List node allocation size
  int n_ln_pool;                // Number of listnodes in a pool
  LISTNODE *top;                // Top of the list
  LISTNODE *bottom;             // Bottom of the list
  LISTNODE *next_ln;            // Next available list node
  NODEPOOL *np_top;             // Top of the node pool chain
  NODEPOOL *np_bottom;          // Bottom of the node pool chain

#if CHECK_MAGIC
  unsigned int magic2;
#endif
} LIST;

// Allocate list nodes in 512 byte chunks, giving 42 elements
#define DEFAULT_POOLSIZE 512

#if CHECK_MAGIC
#define CKMAGIC(p) { if(p->magic1 != MAGIC) breakpoint(); \
                     if(p->magic2 != MAGIC) breakpoint(); }

#define CKNPMAGIC(p) cknpmagic(p)

#define CKLNMAGIC(p) cklnmagic(p)

#define CKLMAGIC(p)  cklmagic(p)
#else
#define CKMAGIC(p)

#define CKNPMAGIC(p)

#define CKLNMAGIC(p)

#define CKLMAGIC(p)
#endif

static int insert_ln(LIST *l, LISTNODE *ln, void *data_ptr);

#if CHECK_MAGIC
static int cknpmagic(LIST *l) {
  NODEPOOL *np;
  int i;

  i = 0;
  np = l->np_top;
  while(np) {
    i++;
    CKMAGIC(np);
    np = np->chain_next;
  }

  i = 0;
  np = l->np_bottom;
  while(np) {
    i++;
    CKMAGIC(np);
    np = np->chain_prev;
  }

  return 0;
}

static int cklnmagic(LIST *l) {
  LISTNODE *ln;
  int i;

  i = 0;
  ln = l->top;
  while(ln) {
    i++;
    CKMAGIC(ln);
    ln = ln->next;
  }

  i = 0;
  ln = l->bottom;
  while(ln) {
    i++;
    CKMAGIC(ln);
    ln = ln->prev;
  }

  return 0;
}

static int cklmagic(LIST *l) {
  CKMAGIC(l);
  CKNPMAGIC(l);
  CKLNMAGIC(l);
  CKMAGIC(l);
  return 0;
}
#endif

/*------------------------------------------------------------
|  new_node_pool
|
|  Create and initialize a new pool of list nodes.  This is
|  just a big block of memory with the first sizeof(NODEPOOL)
|  bytes reserved.  The first available list node resides at
|  offset sizeof(NODEPOOL).
 ------------------------------------------------------------*/
static NODEPOOL *new_nodepool(LIST *l) {
  NODEPOOL *np;
  LISTNODE *ln;
  int i;

  CKLMAGIC(l);

  /*--------------------------------------------------
  |  get a block of memory for the new pool
   --------------------------------------------------*/
  np = (NODEPOOL *) MALLOC(l->poolsize, "list node pool");
  if(np == NULL) {
    return NULL;
  }

  /*--------------------------------------------------
  |  initialize the chaining information at the
  |  beginning of the pool.
   --------------------------------------------------*/

#if CHECK_MAGIC
  np->magic1 = MAGIC;
#endif

  np->chain_next = NULL;
  np->chain_prev = NULL;

#if CHECK_MAGIC
  np->magic2 = MAGIC;
#endif

  /*--------------------------------------------------
  |  initialize all the list nodes within the node
  |  pool, which begin just after the NODEPOOL
  |  structure at the beginning of the memory block
   --------------------------------------------------*/
  ln = (LISTNODE *) (&np[1]);

#if CHECK_MAGIC
  ln[0].magic1 = MAGIC;
#endif

  ln[0].data = NULL;
  ln[0].next = &ln[1];
  ln[0].prev = NULL;

#if CHECK_MAGIC
  ln[0].magic2 = MAGIC;
#endif

  for(i = 1; i < l->n_ln_pool - 1; i++) {

#if CHECK_MAGIC
    ln[i].magic1 = MAGIC;
#endif

    ln[i].data = NULL;
    ln[i].next = &ln[i + 1];
    ln[i].prev = &ln[i - 1];

#if CHECK_MAGIC
    ln[i].magic2 = MAGIC;
#endif
  }

#if CHECK_MAGIC
  ln[l->n_ln_pool - 1].magic1 = MAGIC;
#endif

  ln[l->n_ln_pool - 1].data = NULL;
  ln[l->n_ln_pool - 1].next = NULL;
  ln[l->n_ln_pool - 1].prev = &ln[l->n_ln_pool - 2];

#if CHECK_MAGIC
  ln[l->n_ln_pool - 1].magic2 = MAGIC;
#endif

  CKMAGIC(np);

  CKLMAGIC(l);

  return np;
}

/*------------------------------------------------------------
|  get_listnode
|
|  Get the next available list node.  If there are no more
|  list nodes, another pool of list nodes is allocated.  If
|  that fails, NULL is returned.
 ------------------------------------------------------------*/
static LISTNODE *get_listnode(LIST *l) {
  LISTNODE *ln;
  NODEPOOL *np;

  CKLMAGIC(l);

  if(l->next_ln == NULL) {

    /*--------------------------------------------------
    | allocate a new node pool and chain to the others
     --------------------------------------------------*/
    np = new_nodepool(l);
    if(np == NULL) {
      CKLMAGIC(l);
      return NULL;
    }

    if(l->np_top == NULL) {

      /*--------------------------------------------------
      |  this is the first node pool for this list,
      |  directly assign to the top and bottom.
       --------------------------------------------------*/
      l->np_top = np;
      l->np_bottom = np;
      np->chain_next = NULL;
      np->chain_prev = NULL;
    } else {

      /*--------------------------------------------------
      |  this is an additional node pool, add it to the
      |  chain.
       --------------------------------------------------*/
      np->chain_next = NULL;
      np->chain_prev = l->np_bottom;
      l->np_bottom->chain_next = np;
      l->np_bottom = np;
    }

    /*--------------------------------------------------
    |  set the list's pointer to the next available
    |  list node to the first list node in this new
    |  pool.
     --------------------------------------------------*/
    l->next_ln = (LISTNODE *) & np[1];

    CKMAGIC(np);
  }

  /*--------------------------------------------------
  |  get the next available list node, set the list's
  |  next available list node to the next one in the
  |  list.
   --------------------------------------------------*/
  ln = l->next_ln;
  l->next_ln = ln->next;

  CKMAGIC(ln);

  /*--------------------------------------------------
  |  initialize the new list node and return
   --------------------------------------------------*/
  ln->next = NULL;
  ln->prev = NULL;
  ln->data = NULL;

  CKLMAGIC(l);

  return ln;
}

/*------------------------------------------------------------
|  free_listnode
|
|  Return a list node to the pool of list nodes.  This puts
|  the node at the head of the free list, so that the next
|  call to 'get_listnode', with return the most recently
|  freed one.
 ------------------------------------------------------------*/
static int free_listnode(LIST *l, LISTNODE *ln) {
  CKLMAGIC(l);

  /*--------------------------------------------------
  |  insert the list node at the head of the list of
  |  free list nodes.
   --------------------------------------------------*/
  ln->prev = NULL;
  ln->data = NULL;
  ln->next = l->next_ln;
  l->next_ln = ln;

  CKLMAGIC(l);

  return 0;
}

/*----------------------------------------------------------------------
  lcreat

  Create a new list data structure.

  If liststruct is not NULL, it is used to provide the memory space
  for the list structure instance, otherwise, the necessary memory is
  mmt_malloc'd.

  If elements is zero, the default poolsize is used, otherwise,
  poolsizes of 'elements' elements are mmt_malloc'd to obtain the memory
  for list nodes.  Minimum element count is 5.

  The first node pool is not preallocated; instead it is mmt_malloc'd at
  the time of the first use.
  ----------------------------------------------------------------------*/
LISTID lcreat(void *liststruct, int elements) {
  LIST *l;

  if(liststruct == NULL) {

    /*--------------------------------------------------
      allocate memory for the list itself
      --------------------------------------------------*/
    l = (LIST *) MALLOC(sizeof(LIST), "list struct");
    if(l == NULL) {
      return NULL;
    }
    l->free_on_close = 1;
  } else {

    /*-----------------------------------------------------------------
      use the memory given to us for the list structure
      -----------------------------------------------------------------*/
    l = liststruct;
    l->free_on_close = 0;
  }

  /*--------------------------------------------------
  |  initialize the list
   --------------------------------------------------*/

#if CHECK_MAGIC
  l->magic1 = MAGIC;
  l->magic2 = MAGIC;
#endif

  l->top = NULL;
  l->bottom = NULL;
  l->num = 0;

  if(elements == 0) {
    l->poolsize = DEFAULT_POOLSIZE;
  } else {
    l->poolsize = elements*sizeof(LISTNODE) + sizeof(NODEPOOL);
  }

  l->n_ln_pool = (l->poolsize - sizeof(NODEPOOL))/sizeof(LISTNODE);

  if(l->n_ln_pool < 5) {
    if(!liststruct) {
      FREE(l);
    }
    return NULL;
  }

  l->np_top = NULL;
  l->np_bottom = NULL;
  l->next_ln = NULL;

  CKLMAGIC(l);

  return (LISTID) l;
}

/*--------------------------------------------------
|  ldestroy_cb
|
|  destroy an existing list data structure, calling
|  the user routine 'ucleanup' on the data pointer
|  of each list element.  Allows the user to free
|  up a list data structure and have this routine
|  call their function to free up each list element
|  at the same time.
 --------------------------------------------------*/
void ldestroy_cb(LISTID lid, void (*ucleanup)(void *data_ptr)) {
  LIST *l;
  LISTNODE *ln;

  l = (LIST *) lid;

  CKLMAGIC(l);

  ln = l->top;
  while(ln != NULL) {
    ucleanup(ln->data);
    ln = ln->next;
  }

  ldestroy(l);
}

/*--------------------------------------------------
|  ldestroy
|
|  destroy an existing list data structure.
|
|  assumes that each data element does not need to
|  be freed.
 --------------------------------------------------*/
void ldestroy(LISTID lid) {
  LIST *l;
  NODEPOOL *p1, *p2;

  l = (LIST *) lid;

  CKLMAGIC(l);

  /*--------------------------------------------------
  |  free each node pool - start at the first node
  |  pool and free each successive until there are
  |  no more.
   --------------------------------------------------*/
  p1 = l->np_top;
  while(p1 != NULL) {
    p2 = p1->chain_next;
    FREE(p1);
    p1 = p2;
  }

  /*--------------------------------------------------
  |  now free the memory occupied by the list itself
   --------------------------------------------------*/
  if(l->free_on_close) {
    FREE(l);
  }
}

/*------------------------------------------------------------
|  ladd
|
|  add list - add item p to the list
 ------------------------------------------------------------*/
int ladd(LISTID lid, void *p) {
  LIST *l;
  LISTNODE *lnptr;

  l = (LIST *) lid;

  CKLMAGIC(l);

  lnptr = get_listnode(l);
  if(lnptr == NULL) {

#ifdef BOS
    breakpoint();
#endif

    return -1;
  }

  CKMAGIC(lnptr);

  lnptr->data = p;

  if(l->top == NULL) {
    l->top = lnptr;
    l->bottom = lnptr;
    lnptr->next = NULL;
    lnptr->prev = NULL;
  } else {
    lnptr->prev = l->bottom;
    lnptr->next = NULL;
    l->bottom->next = lnptr;
    l->bottom = lnptr;
  }
  l->num++;

  CKLMAGIC(l);

  return 0;
}

/*------------------------------------------------------------
|  laddo
|
|  add list, ordered - add item p to the list, use 'compare'
|  function to place 'p' when the comparison 'p' is less than
|  the next item.  Return 0 if this was a unique entry,
|  else return 1 indicating a duplicate entry, i.e., the
|  compare function returned 0 while inserting the element.
 ------------------------------------------------------------*/
int laddo(LISTID lid, void *p, int (*compare)(const void *p1, const void *p2), LNODEID *firstdup) {
  LIST *l;
  LISTNODE *ln;
  int dup, cmp;

  l = (LIST *) lid;

  CKLMAGIC(l);

  dup = 0;
  ln = l->top;

  while(ln != NULL) {
    CKMAGIC(ln);
    cmp = compare(p, ln->data);
    if(cmp == 0) {
      dup = 1;
      if(firstdup)
        *firstdup = ln;
    }
    if(cmp < 0) {
      insert_ln(l, ln, p);
      CKLMAGIC(l);
      return dup;
    } else {
      ln = ln->next;
    }
  }

  ladd(l, p);

  CKLMAGIC(l);

  return dup;
}

/*---------------------------------------------------------------------------
|  laddu
|
|  add list, ordered, unique - add item p to the list, use 'compare'
|  function to place 'p' when the comparison 'p' is less than the next
|  item.  Return 1 if the item was added, 0 if not.
 --------------------------------------------------------------------------*/
int laddu(LISTID lid, void *p, int (*compare)(const void *p1, const void *p2)) {
  LIST *l;
  LISTNODE *ln;
  int cmp;

  l = (LIST *) lid;

  CKLMAGIC(l);

  ln = l->top;

  while(ln != NULL) {
    CKMAGIC(ln);
    cmp = compare(p, ln->data);
    if(cmp == 0) {
      CKLMAGIC(l);
      return 0;
    }
    if(cmp < 0) {
      insert_ln(l, ln, p);
      CKLMAGIC(l);
      return 1;
    } else {
      ln = ln->next;
    }
  }

  ladd(l, p);

  CKLMAGIC(l);

  return 1;
}

LNODEID lfirst(LISTID lid) {
  CKLMAGIC(((LIST *) lid));
  return ((LIST *) lid)->top;
}

LNODEID llast(LISTID lid) {
  CKLMAGIC(((LIST *) lid));
  return ((LIST *) lid)->bottom;
}

LNODEID lnext(LNODEID lnid) {
  CKMAGIC(((LISTNODE *) lnid));
  return ((LISTNODE *) lnid)->next;
}

LNODEID lprev(LNODEID lnid) {
  CKMAGIC(((LISTNODE *) lnid));
  return ((LISTNODE *) lnid)->prev;
}

void *ldata(LNODEID lnid) {
  CKMAGIC(((LISTNODE *) lnid));
  return ((LISTNODE *) lnid)->data;
}

int lsize(LISTID lid) {
  CKLMAGIC(((LIST *) lid));
  return ((LIST *) lid)->num;
}

/*------------------------------------------------------------
|  lcat
|
|  catenate - catenate l2 to l1, return pointer to l1.
 ------------------------------------------------------------*/
LISTID lcat(LISTID lid1, LISTID lid2) {
  CKLMAGIC(((LIST *) lid1));
  CKLMAGIC(((LIST *) lid2));
  while(lsize(lid2)) {
    ladd(lid1, lrmv_n(lid2, 1));
  }

  CKLMAGIC(((LIST *) lid1));
  CKLMAGIC(((LIST *) lid2));

  return lid1;
}

/*----------------------------------------------------------------------
|  lget
|
|  get from list, last item - return pointer to the data of the last
|  item in the list, non-destructive
 ----------------------------------------------------------------------*/
void *lget(LISTID lid) {
  LIST *l;
  LISTNODE *p;

  l = (LIST *) lid;

  CKLMAGIC(l);

  p = l->bottom;

  if(p == NULL) {
    CKLMAGIC(l);
    return NULL;
  } else {
    CKLMAGIC(l);
    return p->data;
  }
}

/*---------------------------------------------------------------
|  lget_n
|
|  get from list, index - return the nth list item,
|  non-destructive
 ---------------------------------------------------------------*/
void *lget_n(LISTID lid, unsigned int n) {
  unsigned int i;
  LIST *l;
  LISTNODE *ln;

  l = (LIST *) lid;

  CKLMAGIC(l);

  if(n < 1 || n > (unsigned int) lsize(l)) {
    return NULL;
  }

  ln = l->top;
  i = 1;
  while(ln && (i != n)) {
    CKMAGIC(ln);
    ln = ln->next;
    i++;
  }

  if(ln) {
    CKLMAGIC(l);
    return ln->data;
  } else {
    CKLMAGIC(l);
    return NULL;
  }
}

/*---------------------------------------------------------------
|  lget_ln
|
|  get from list, listnode - return the nth list item, the
|  listnode is returned instead of the data, non-destructive
 ---------------------------------------------------------------*/
LNODEID lget_ln(LISTID lid, unsigned int n) {
  unsigned int i;
  LIST *l;
  LISTNODE *ln;

  l = (LIST *) lid;

  CKLMAGIC(l);

  if(n < 1 || n > (unsigned int) lsize(l)) {
    return NULL;
  }

  ln = l->top;
  i = 1;
  while(i != n) {
    CKMAGIC(ln);
    ln = ln->next;
    i++;
  }

  CKLMAGIC(l);
  return (LNODEID) ln;
}

/*----------------------------------------------------------------------
|  insert_ln
|
|  insert data, listnode - insert data just before the list item
|  pointed to by 'ln'.
|
|  This routine is not intended to be called directly by the user
|  because the validity of ln is not checked.  This routine is called
|  by list manipulation routines within this module, in which ln is
|  known to point to a valid list node.
 ----------------------------------------------------------------------*/
static int insert_ln(LIST *l, LISTNODE *ln, void *data_ptr) {
  LISTNODE *lnptr;

  CKLMAGIC(l);

  if(ln == NULL) {
    ladd(l, data_ptr);
    CKLMAGIC(l);
    return 0;
  }

  lnptr = get_listnode(l);
  if(lnptr == NULL) {

#ifdef BOS
    breakpoint();
#endif

    return -1;
  }

  CKMAGIC(lnptr);

  lnptr->data = data_ptr;

  if(ln == l->top) {

    /*------------------------------
    |  insert before the list head
     ------------------------------*/
    lnptr->next = ln;
    lnptr->prev = NULL;
    ln->prev = lnptr;
    l->top = lnptr;
  } else if(ln == NULL) {

    /*-----------------
    |  list was empty
     -----------------*/
    lnptr->next = NULL;
    lnptr->prev = l->bottom;
    l->bottom->next = lnptr;
    l->bottom = lnptr;
  } else {

    /*-----------------------------------
    |  insert in the middle of the list
     -----------------------------------*/
    lnptr->next = ln;
    lnptr->prev = ln->prev;
    lnptr->next->prev = lnptr;
    lnptr->prev->next = lnptr;
  }

  l->num++;

  CKLMAGIC(l);

  return 0;
}

/*-----------------------------------------------------------------
|  lins_n
|
|  Insert data before the nth item in the list.
 -----------------------------------------------------------------*/
int lins_n(LISTID lid, void *data_ptr, unsigned int n) {
  unsigned int i;
  LIST *l;
  LISTNODE *ln;

  l = (LIST *) lid;

  CKLMAGIC(l);

  if(n < 1 || n > (unsigned int) (l->num + 1)) {
    return -1;
  }

  if(l->num == 0) {
    return ladd(lid, data_ptr);
  }

  /*----------------------------------
  |  locate the nth item in the list
   ----------------------------------*/
  ln = l->top;
  i = 1;
  while(ln && (i != n)) {
    CKMAGIC(ln);
    ln = ln->next;
    i++;
  }

  if(!ln) {
    CKLMAGIC(l);
    return -1;
  }

  CKLMAGIC(l);

  /*-----------------------------------------
  |  insert before the nth item in the list
   -----------------------------------------*/
  return insert_ln(l, ln, data_ptr);
}

/*-----------------------------------------------------------------
|  lins_ln
|
|  Insert data before the list node pointed to by ln.
 -----------------------------------------------------------------*/
int lins_ln(LISTID lid, LNODEID lnid, void *data_ptr) {
  LIST *l;
  LISTNODE *ln;
  LISTNODE *ln_ptr;

  l = (LIST *) lid;
  ln = (LISTNODE *) lnid;

  CKLMAGIC(l);

  CKMAGIC(ln);

  /*-----------------------------------------
  |  validate that ln is indeed in the list
   -----------------------------------------*/
  ln_ptr = l->top;
  while((ln_ptr != NULL) && (ln_ptr != ln)) {
    CKMAGIC(ln_ptr);
    ln_ptr = ln_ptr->next;
  }

  if(ln_ptr == NULL) {
    CKLMAGIC(l);
    return -1;
  }

  CKLMAGIC(l);

  /*--------------------------------
  |  insert the data into the list
   --------------------------------*/
  return insert_ln(l, ln, data_ptr);
}

/*----------------------------------------------------------------------
|  remove_ln
|
|  Remove the item in the list pointed to by ln.  This routine is not
|  intended to be called directly by the user because the validity
|  of ln is not checked.  This routine is called by list manipulation
|  routines within this module, in which ln is known to point to a
|  valid list node.
 ----------------------------------------------------------------------*/
static void *remove_ln(LIST *l, LISTNODE *ln) {
  void *r;

  CKLMAGIC(l);

  CKMAGIC(ln);

  if(ln == l->top) {

    /*------------------------------
    |  remove the head of the list
     ------------------------------*/
    l->top = ln->next;
    if(l->top != NULL) {
      l->top->prev = NULL;
    } else {

      /*----------------------------------------
      |  this was the only item in the list
       ----------------------------------------*/
      l->bottom = NULL;
    }
  } else if(ln == l->bottom) {

    /*------------------------------
    |  remove the tail of the list
     ------------------------------*/
    l->bottom = ln->prev;
    if(l->bottom != NULL) {
      l->bottom->next = NULL;
    }
  } else {

    /*-------------------------------------
    |  remove from the middle of the list
     -------------------------------------*/
    ln->prev->next = ln->next;
    ln->next->prev = ln->prev;
  }

  /*-----------------------------
  |  prepare to return the data
   -----------------------------*/
  r = ln->data;

  /*-----------------------------------------------
  |  free the listnode for re-use
   -----------------------------------------------*/
  free_listnode(l, ln);

  /*------------------------------------
  |  adjust the item count of the list
   ------------------------------------*/
  l->num--;

  CKLMAGIC(l);

  return r;
}

/*-------------------------------------------------------------------------
|  lrmv_d
|
|  remove from list, data - removes the data element from the list,
|  destructive
 -------------------------------------------------------------------------*/
void *lrmv_d(LISTID lid, void *data_ptr) {
  LIST *l;
  LISTNODE *ln;

  l = (LIST *) lid;

  CKLMAGIC(l);

  ln = l->top;
  while(ln && (ln->data != data_ptr)) {
    CKMAGIC(ln);
    ln = ln->next;
  }

  if(ln == NULL) {
    CKLMAGIC(l);
    return NULL;
  } else {
    CKLMAGIC(l);
    return remove_ln(l, ln);
  }
}

/*-------------------------------------------------------------------------
|  lrmv_ln
|
|  remove from list, by list node - remove the data element pointed to
|  by 'ln' from the list, destructive
 -------------------------------------------------------------------------*/
void *lrmv_ln(LISTID lid, LNODEID lnid) {
  LIST *l;
  LISTNODE *ln;
  LISTNODE *p;

  l = (LIST *) lid;
  ln = (LISTNODE *) lnid;

  CKLMAGIC(l);

  CKMAGIC(ln);

  p = l->top;
  while((p != NULL) && (p != ln)) {
    CKMAGIC(p);
    p = p->next;
  }

  if(p == NULL) {
    CKLMAGIC(l);
    return NULL;
  } else {
    CKLMAGIC(l);
    return remove_ln(l, p);
  }
}

/*----------------------------------------------------------------------
|  lrmv_n
|
|  remove from list, by item number - remove the nth element from
|  the list.
 ----------------------------------------------------------------------*/
void *lrmv_n(LISTID lid, unsigned int n) {
  unsigned int i;
  LIST *l;
  LISTNODE *ln;

  l = (LIST *) lid;

  CKLMAGIC(l);

  if(n < 1 || n > (unsigned int) l->num) {
    return NULL;
  }

  ln = l->top;
  i = 1;
  while(ln && (i != n)) {
    CKMAGIC(ln);
    ln = ln->next;
    i++;
  }

  if(ln) {
    CKLMAGIC(l);
    return remove_ln(l, ln);
  } else {
    CKLMAGIC(l);
    return NULL;
  }
}

/*----------------------------------------------------------------------
|  lrmv
|
|  remove from list, last item - remove the last item from the list,
|  destructive
 ----------------------------------------------------------------------*/
void *lrmv(LISTID lid) {
  LIST *l;
  LISTNODE *p;

  l = (LIST *) lid;

  CKLMAGIC(l);

  p = l->bottom;

  if(p == NULL) {
    CKLMAGIC(l);
    return NULL;
  } else {
    CKLMAGIC(l);
    return remove_ln(l, p);
  }
}

/*----------------------------------------------------------------------
|  lsrch
|
|  search list - return data element pointed to by 'p', NULL if not
|  found
 ----------------------------------------------------------------------*/
void *lsrch(LISTID lid, void *p, int (*compare)(void *p1, void *p2)) {
  LIST *l;
  LISTNODE *ln;

  l = (LIST *) lid;

  CKLMAGIC(l);

  ln = l->top;

  while(ln != NULL) {
    CKMAGIC(ln);
    if(compare(p, ln->data) == 0) {
      CKLMAGIC(l);
      return ln->data;
    } else {
      ln = ln->next;
    }
  }

  CKLMAGIC(l);
  return NULL;
}

/*----------------------------------------------------------------------
|  lsort
|
|  sort list - sorts list inplace (using bubble sort)
|
 ----------------------------------------------------------------------*/
void lsort(LISTID lid, int (*compare)(void *p1, void *p2)) {
  LIST *l;
  LISTNODE *lt;                 // This
  LISTNODE *ln;                 // Next
  int unsorted = 1;

  l = (LIST *) lid;

  CKLMAGIC(l);

  while(unsorted) {
    lt = l->top;
    unsorted = 0;
    while(lt != NULL) {
      CKMAGIC(lt);
      ln = lt->next;
      if(ln != NULL && compare(lt->data, ln->data) > 0) {
        void *p = ln->data;

        ln->data = lt->data;
        lt->data = p;
        unsorted = 1;
      }
      lt = ln;
    }
  }

  CKLMAGIC(l);
}

int lprint(FILE *f, LISTID lid) {
  LIST *l;
  LISTNODE *ln;
  NODEPOOL *np;
  int count;

  l = (LIST *) lid;

  fprintf(f, "list id %p internal data structures:\n", lid);

#if CHECK_MAGIC
  if((l->magic1 != MAGIC) || (l->magic2 != MAGIC)) {
    fprintf(f, "  *** WARNING: LIST MAGIC IS CORRUPT ***\n");
  }
  fprintf(f, "  magic1=0x%08x\n" "  magic2=0x%08x\n", l->magic1, l->magic2);
#endif

  fprintf(f, "   num f pool n_ln        top     bottom    next_ln     np_top  np_bottom\n");
  fprintf(f, "  ---- - ---- ---- ---------- ---------- ---------- ---------- ----------\n");
  fprintf(f, "  %4d %1d %4d %4d %10p %10p %10p %10p %10p\n",
    l->num, l->free_on_close, l->poolsize, l->n_ln_pool, l->top, l->bottom, l->next_ln, l->np_top, l->np_bottom);

  fprintf(f,
    "  node pools:\n"
    "     idx         np     magic1       next       prev     magic2\n"
    "    ---- ---------- ---------- ---------- ---------- ----------\n");
  count = 0;
  np = l->np_top;
  while(np != NULL) {
    count++;
    fprintf(f, "    %4d %10p 0x%08x %10p %10p 0x%08x\n", count, np,

#if CHECK_MAGIC
      np->magic1,
#else
      0,
#endif

      np->chain_next, np->chain_prev,

#if CHECK_MAGIC
      np->magic2
#else
      0
#endif

      );
    np = np->chain_next;
  }

  if(f) {
    fprintf(f,
      "  list elements:\n"
      "       n         ln     magic1       next       prev       data     magic2\n"
      "    ---- ---------- ---------- ---------- ---------- ---------- ----------\n");
    count = 0;
    ln = l->top;
    while(ln != NULL) {
      count++;
      fprintf(f, "    %4d %10p %10x %10p %10p %10p %10x\n", count, ln,
#if CHECK_MAGIC
        ln->magic1,
#else
        0,
#endif
        ln->next, ln->prev, ln->data,
#if CHECK_MAGIC
        ln->magic2
#else
        0
#endif
        );
      ln = lnext(ln);
    }
    if(count != l->num)
      fprintf(f, "  *** list count is not correct\n" "  *** list id indicates %d, counted items = %d\n",
        l->num, count);
  }

  return 0;
}
