/*
 * -------------------------------------------------------
 * Copyright (C) 2004-2007 Tommi Saviranta <wnd@iki.fi>
 * -------------------------------------------------------
 * 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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* ifdef HAVE_CONFIG_H */

#include "list.h"
#include "common.h"
#include "error.h"



#ifdef USE_MOVE_TO
static list_type *add_head_priv(list_type *list, list_type *node);
static list_type *add_tail_priv(list_type *list, list_type *node);
static list_type *delete_priv(list_type *list, list_type *node);
static list_type *insert_at_priv(list_type *list, list_type *pos,
		list_type *node);
#endif



/*
 * Create node and add it to the beginning of list.
 * @list:	List to add data to
 * @data:	Stuff to add
 *
 * Returns: Pointer to list.
 */
list_type *
list_add_head(list_type *list, void *data)
{
	list_type *new;
	
	new = (list_type *) xmalloc(sizeof(list_type));
	new->data = data;
	
	new->prev = NULL;
	new->next = list;
	if (list == NULL) {
		new->last = new;
	} else {
		new->last = list->last;
		list->prev = new;
	}

	return new;
} /* list_type *list_add_head(list_type *list, void *data) */



/*
 * Create node and add it to the end of list.
 * @list:	List to add data to
 * @data:	Stuff to add
 *
 * Returns: Pointer to list.
 */
list_type *
list_add_tail(list_type *list, void *data)
{
	list_type *new;
	
	new = (list_type *) xmalloc(sizeof(list_type));
	new->data = data;
	
	new->next = NULL;
	
	if (list == NULL) {
		new->prev = NULL;
		new->last = new;
		return new;
	} else {
		new->prev = list->last;
		list->last->next = new;
		list->last = new;
		return list;
	}
} /* list_type *list_add_tail(list_type *list, void *data) */



/*
 * Inserts a node before specified position.
 * @list:	List to add data to
 * @dest:	Location where to insert
 * @data:	What to insert
 *
 * Returns: Pointer to list.
 *
 * If dest == NULL, node will be inserted as last.
 */
list_type *
list_insert_at(list_type *list, list_type *dest, void *data)
{
	list_type *new;

	if (dest == list) {
		return list_add_head(list, data);
	} else if (dest == NULL) {
		return list_add_tail(list, data);
	}
	
	new = (list_type *) xmalloc(sizeof(list_type));
	new->data = data;
	
	new->prev = dest->prev;
	new->next = dest;
	dest->prev->next = new;
	dest->prev = new;

	return list;
} /* list_type *list_insert_at(list_type *list, list_type *dest, void *data) */



#ifdef USE_MOVE_TO
/*
 * Insert a node to node in list.
 * @list: 	List to add data to
 * @node:	Location where to insert. If node == list, node is added as
 * 		the first element of the list. If node == NULL, node will
 * 		be added to the end of list. First rule implies that new data
 * 		will be inserted before pos.
 * @data: 	Stuff to add
 *
 * Returns: Pointer to list
 */
list_type *
list_insert_at(list_type *list, list_type *pos, void *data)
{
	list_type *new;

	/* Adding to head/tail? */
	if (pos == list) {
		return llist_add_head(list, data);
	} else if (pos == NULL) {
		return llist_add_tail(list, data);
	}

	/* Inserting for real. Proceed. */
	new = (list_type *) xmalloc(sizeof(llist_t));
	new->data = data;
	
	new->prev = pos->prev;
	new->next = pos;
	pos->prev->next = new;
	pos->prev = new;

	return list;
} /* list_type *list_insert_at(list_type *list, list_type *pos, void *data) */



/*
 * Move src to dest.
 * @list:	List where to move
 * @src:	Node to move
 * @dest:	Target location
 *
 * Returns: Pointer to list.
 *
 * If src == list, item will be moved as first item. If dest == NULL, item will
 * be moved as last timem. First rule implies that item is moved to location
 * just before dest.
 */
list_type *
list_move_to(list_type *list, list_type *src, list_type *dest)
{
	list_type *first;
	list_type *ptr;

	ptr = src;
	first = delete_priv(list, src);
	first = insert_at_priv(list, dest, ptr);

	return first;
} /* list_type *list_move_to(list_type *list, list_type *src,
		list_type *dest) */



/*
 * Insert a node to node in list (doesn't allocate memory).
 * @list: 	List to add data to
 * @node:	Location where to insert. If node == list, node is added as
 * 		the first element of the list. If node == NULL, node will
 * 		be added to the end of list. First rule implies that new data
 * 		will be inserted before pos.
 *
 * Returns: Pointer to list
 */
static list_type *
insert_at_priv(list_type *list, list_type *pos, list_type *node)
{
	/* Adding to head/tail? */
	if (pos == list) {
		return add_head_priv(list, node);
	} else if (pos == NULL) {
		return add_tail_priv(list, node);
	}

	
	node->prev = pos->prev;
	node->next = pos;
	pos->prev->next = node;
	pos->prev = node;

	return list;
} /* static list_type *insert_at_priv(list_type *list, list_type *pos) */
#endif



/*
 * Delete a node from list.
 * @list: list to remove from
 * @node: node to remove
 *
 * Returns: pointer to list
 *
 * Data should be freed before calling this function.
 */
list_type *
list_delete(list_type *list, list_type *node)
{
	list_type *first;

#ifdef ENDUSERDEBUG
	if (list == NULL) {
		enduserdebug("Trying to delete stuff from empty list");
		return NULL;
	}
#endif

	/* Update next-link of previous item. */
	if (node->prev != NULL) {
		node->prev->next = node->next;
	}

	/* Update prev-link of next item. */
	if (node->next != NULL) {
		node->next->prev = node->prev;
	}

	/* If deleted item was the first one, update new first item. */
	if (node == list) {
		if (node->next != NULL) {
			node->next->last = node->last;
			first = node->next;
		} else {
			first = NULL;
		}
	} else {
		first = list;
	}

	/* If deleted item was the last one, update new item. */
	if (node == list->last) {
		list->last = node->prev;
	}

	xfree(node);

	return first;
} /* list_type *list_delete(list_type *list, list_type *node) */



/*
 * Find node that points to data.
 * @list: list to look from
 * @data: data to look for
 *
 * Returns: node or NULL if data was not found.
 */
list_type *
list_find(list_type *list, void *data)
{
	list_type *ptr;

	for (ptr = list; ptr != NULL; ptr = ptr->next) {
		if (ptr->data == data) {
			return ptr;
		}
	}

	return NULL;
} /* list_type *list_find(list_type *list, void *data) */



list_type *
list_move_first_to(list_type *list, list_type *dest)
{
	list_type *new;
	list_type *moved;

/* printf("list=%p dest=%p\n", (void *) list, (void *) dest); */

	if (dest == list) {
		/* Moving item as item before the second item. */
		return list;
	} else if (list->next == dest) {
		/* Moving single item in list doesn't do anything. */
		return list;
	}
	
	moved = list;
	new = list->next;
	new->prev = NULL;

	if (dest != NULL) {
		new->last = moved->last;
		moved->prev = dest->prev;
		moved->next = dest;
		dest->prev->next = moved;
		dest->prev = moved;
	} else {
		new->last = moved;
		moved->last->next = moved;
		moved->prev = moved->last;
		moved->next = NULL;
	}

	return new;
} /* list_type *list_move_first_to(list_type *list, list_type *dest) */



#ifdef DUMPSTATUS
#define LLIST_BUFSIZE	65536
const char *
list_dump(list_type *list)
{
	static char buf[LLIST_BUFSIZE];
	/*
	char *bufptr;
	list_type *ptr;

	printf("%p -- %p -----\n", (void *) buf, (void *) 0);
	bufptr = buf + snprintf(buf, LLIST_BUFSIZE, "list:\n");
	bufptr = buf;
	for (ptr = list; ptr != NULL; ptr = ptr->next) {
		bufptr += snprintf(bufptr, LLIST_BUFSIZE - (int) (buf - bufptr),
				"<-%p/%p/%p-> %p-->\n",
				(void *) ptr->prev, (void *) ptr,
				(void *) ptr->next, (void *) ptr->last);
	}
	*/
	buf[0] = '\0';
	return buf;
} /* const char *list_dump(list_type *list) */
#endif /* ifdef DUMPSTATUS */


syntax highlighted by Code2HTML, v. 0.9.1