/*
 * (C) Copyright 1991 UCAR/Unidata
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose without fee is hereby granted, provided that
 * the above copyright notice appear in all copies, that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of UCAR/Unidata not be used in advertising or publicity
 * pertaining to distribution of the software without specific, written prior
 * permission.  UCAR makes no representations about the suitability of this
 * software for any purpose. It is provided "as is" without express or
 * implied warranty.  It is provided with no support and without obligation
 * on the part of UCAR or Unidata, to assist in its use, correction,
 * modification, or enhancement.
 * 
 * $Id: linklist.c,v 1.1 2000/08/07 23:15:03 emmerson Exp $
 */

/*
 * This file implements a linked-list capability.
 */

/* LINTLIBRARY */

#include "udposix.h"		/* for a near-POSIX environment */
#include <assert.h>		/* for assert() */
#include <string.h>		/* for memcpy() */
#include <stdio.h>		/* needed by <assert.h> */
#include "udalloc.h"		/* for Unidata memory-allocation routines */
#include "linklist.h"		/* for myself */

typedef struct Member {
    struct Member  *next;
    char            data[1];
}               Member;

struct Linklist {
    Member         *head;
    Member         *current;
};


/*
 * Return a pointer to a new linked-list.
 */
    Linklist*
llnew()
{
    Linklist       *new;

    if ((new = UD_ALLOC(1, Linklist)) != NULL) {
	new->head = NULL;
	new->current = NULL;
    }
    return new;
}


/*
 * Add a member to a linked-list.  Return a pointer to the added data.
 * 
 * This routine can handle 0-sized data, but it cannot handle a NULL
 * data-pointer.
 */
    voidp
lladd(list, data, size)
    Linklist       *list;
    const voidp     data;
    size_t          size;
{
    Member         *memb;

    assert(list != NULL);

    if (data == NULL)
	return NULL;

    if ((memb = (Member *) udmalloc(sizeof(Member) - 1 + size)) == NULL)
	return NULL;

    /*
     * The following line might cause lint(1) to emit a "possible 
     * pointer alignment problem" message.  This may safely be ignored.
     */
    (void) memcpy((voidp)memb->data, data, size);
    memb->next = list->head;
    list->head = memb;

    /*
     * The following line might cause lint(1) to emit a "possible 
     * pointer alignment problem" message.  This may safely be ignored.
     */
    return (voidp) memb->data;
}


/*
 * Return a pointer to the first piece of data in a linked-list.  NB: A
 * pointer is returned, not the datum itself.
 */
    voidp
llfirst(list)
    Linklist       *list;
{
    if (list == NULL)
	return NULL;

    return list->head == NULL
	? NULL
	/*
	 * The following line might cause lint(1) to emit a "possible 
	 * pointer alignment problem" message.  This may safely be ignored.
	 */
	: (voidp) (list->current = (Member*)list->head)->data;
}


/*
 * Return a pointer to the next piece of data in a linked-list.
 */
    voidp
llnext(list)
    Linklist       *list;
{
    if (list == NULL)
	return NULL;

    assert(list->current != NULL);

    return list->current->next == NULL
	? NULL
	/*
	 * The following line might cause lint(1) to emit a "possible 
	 * pointer alignment problem" message.  This may safely be ignored.
	 */
	: (voidp) (list->current = (Member*)list->current->next)->data;
}


/*
 * Free the resources associated with a linked-list.
 */
    Linklist*
llfree(list)
    Linklist       *list;
{
    if (list != NULL) {
	Member         *memb, *next;

	for (memb = list->head; memb != NULL; memb = next) {
	    next = memb->next;
	    free((voidp)memb);
	}

	free((voidp)list);
    }

    return NULL;
}
