//-----------------------------------------------------------------------------
// Copyright © 2003 - Philip Howard - All rights reserved
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//-----------------------------------------------------------------------------
// package	libh/list
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 120 columns wide.
//-----------------------------------------------------------------------------

#include "list_lib.h"

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_initialize
//
// purpose	Initialize a list anchor to initial state of empty.
//
// arguments	1 (list_anchor) lvalue of list anchor
//
// returns	(void)
//-----------------------------------------------------------------------------
#define list_initialize(a) ((a)?((int)!((a)=(list_anchor)NULL)):1)
__FMACRO_END__

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_insert_front
//
// purpose	Insert a list node at the front of the specified list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//		2 (list_member_p) pointer to node to add
//
// returns	(list_member_p) pointer to node added
//		(list_member_p) NULL if no node given nor can be allocated
//-----------------------------------------------------------------------------
// macro	list_insert_front_new
//
// purpose	Allocate and insert a list node at the front of the
//		specified list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//
// returns	(list_member_p) pointer to node added
//		(list_member_p) NULL if no node can be allocated
//-----------------------------------------------------------------------------
// function	(list_insert_front)
//
// purpose	Insert a list node at the front of the specified list.
//
// arguments	1 (list_anchor_p) pointer to list anchor
//		2 (list_member_p) pointer to node to add
//
// returns	(list_member_p) pointer to node added
//		(list_member_p) NULL if no node given nor can be allocated
//-----------------------------------------------------------------------------
#define list_insert_front(a,e) ((list_insert_front)(sizeof(*(a)),&(a),&((e)->link)))
#define list_insert_front_new(a) ((list_insert_front)(sizeof(*(a)),&(a),((list_member_p)(NULL))))
list_member_p
(list_insert_front) (
    size_t		arg_size
    ,
    list_anchor_p	arg_list
    ,
    list_member_p	arg_node
    )
__PROTO_END__
{
    if ( ! arg_node ) {
	arg_node = malloc( arg_size );
	if ( ! arg_node ) return NULL;
    }
    if ( * arg_list ) {
	arg_node->list_next = ( * arg_list );
	arg_node->list_prev = ( * arg_list )->list_prev;
	( * arg_list )->list_prev->list_next = arg_node;
	( * arg_list )->list_prev = arg_node;
	* arg_list = arg_node;
    } else {
	arg_node->list_next = arg_node->list_prev = arg_node;
    }
    return arg_node;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_insert_back
//
// purpose	Insert a list node at the back of the specified list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//		2 (list_member_p) pointer to node to add
//
// returns	(list_member_p) pointer to node added
//		(list_member_p) NULL if no node given nor can be allocated
//-----------------------------------------------------------------------------
// macro	list_insert_back_new
//
// purpose	Allocate and insert a list node at the back of the
//		specified list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//
// returns	(list_member_p) pointer to node added
//		(list_member_p) NULL if no node can be allocated
//-----------------------------------------------------------------------------
// function	(list_insert_back)
//
// purpose	Insert a list node at the back of the specified list.
//
// arguments	1 (list_anchor_p) pointer to list anchor
//		2 (list_member_p) pointer to node to add
//
// returns	(list_member_p) pointer to node added
//		(list_member_p) NULL if no node given nor can be allocated
//-----------------------------------------------------------------------------
#define list_insert_back(a,e) ((list_insert_back)(sizeof(*(a)),&(a),&((e)->link)))
#define list_insert_back_new(a) ((list_insert_back)(sizeof(*(a)),&(a),((list_member_p)(NULL))))
list_member_p
(list_insert_back) (
    size_t		arg_size
    ,
    list_anchor_p	arg_list
    ,
    list_member_p	arg_node
    )
__PROTO_END__
{
    if ( ! arg_node ) {
	arg_node = malloc( arg_size );
	if ( ! arg_node ) return NULL;
    }
    if ( * arg_list ) {
	arg_node->list_next = ( * arg_list );
	arg_node->list_prev = ( * arg_list )->list_prev;
	( * arg_list )->list_prev->list_next = arg_node;
	( * arg_list )->list_prev = arg_node;
    } else {
	arg_node->list_next = arg_node->list_prev = arg_node;
    }
    return arg_node;
}

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_point_first
//
// purpose	Get the pointer to the first node from the specified list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//
// returns	(list_member_p) pointer to first node
//-----------------------------------------------------------------------------
#define list_point_first(a) ((a)?((void*)(&((a)->link))):(void*)NULL)
__FMACRO_END__

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_point_last
//
// purpose	Get the pointer to the last node from the specified list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//
// returns	(list_member_p) pointer to last node
//-----------------------------------------------------------------------------
#define list_point_last(a) ((a)?((void*)(&((a)->link.list_prev))):(void*)NULL)
__FMACRO_END__

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_next
//
// purpose	Get the next node from the given node.
//
// arguments	1 (list_member_p) pointer to node to start with
//
// returns	(list_member_p) next node (same if only one)
//-----------------------------------------------------------------------------
#define list_next(e) ((void*)((e)->link.list_next))
__FMACRO_END__

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_prev
//
// purpose	Get the previous node from the given node.
//
// arguments	1 (list_member_p) pointer to node to start with
//
// returns	(list_member_p) prev node (same if only one)
//-----------------------------------------------------------------------------
#define list_prev(e) ((void*)((e)->link.list_prev))
__FMACRO_END__

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_is_first
//
// purpose	Determine if a given node is the first in the list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//		2 (list_member_p) pointer to node to check
//
// returns	(int) 0 : node is not the first in the list
//		(int) 1 : node is the first in the list
//-----------------------------------------------------------------------------
// macro	list_is_not_first
//
// purpose	Determine if a given node is not the first in the list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//		2 (list_member_p) pointer to node to check
//
// returns	(int) 0 : node is not the first in the list
//		(int) 1 : node is the first in the list
//-----------------------------------------------------------------------------
#define list_is_first(a,e) (((a)&&(e))?(((void*)(&((a)->link)))==((void*)(e))):0)
#define list_is_not_first(a,e) (((a)&&(e))?(((void*)(&((a)->link)))!=((void*)(e))):1)
__FMACRO_END__

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_is_last
//
// purpose	Determine if a given node is the last in the list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//		2 (list_member_p) pointer to node to check
//
// returns	(int) 0 : node is not the last in the list
//		(int) 1 : node is the last in the list
//-----------------------------------------------------------------------------
// macro	list_is_not_last
//
// purpose	Determine if a given node is not the last in the list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//		2 (list_member_p) pointer to node to check
//
// returns	(int) 0 : node is not the last in the list
//		(int) 1 : node is the last in the list
//-----------------------------------------------------------------------------
#define list_is_last(a,e) (((a)&&(e))?(((void*)(&((a)->link.list_prev)))==((void*)(e))):0)
#define list_is_not_last(a,e) (((a)&&(e))?(((void*)(&((a)->link.list_prev)))!=((void*)(e))):1)
__FMACRO_END__

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_remove_node
//
// purpose	Remove the specified list node from the specified list.
//		No test is performed to verify the node is on this list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//		2 (list_member_p) pointer to node to remove
//
// returns	(list_member_p) removed node
//		(list_member_p) NULL if no node given
//-----------------------------------------------------------------------------
// macro	list_remove_front
//
// purpose	Remove the list node at the front of the specified list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//
// returns	(list_member_p) pointer to removed list node
//-----------------------------------------------------------------------------
// macro	list_remove_back
//
// purpose	Remove the list node at the back of the specified list.
//
// arguments	1 (list_anchor) lvalue of list anchor
//
// returns	(list_member_p) pointer to removed list node
//-----------------------------------------------------------------------------
// function	(list_remove_node)
//
// purpose	Remove the specified list node from the specified list.
//		No test is performed to verify the node is on this list.
//
// arguments	1 (list_anchor_p) pointer to list anchor
//		2 (list_member_p) pointer to node to remove
//
// returns	(list_member_p) removed node
//		(list_member_p) NULL if no node given
//
// warning	Attempting to remove a node not on the list specified may
//		result in invalid pointer operations during this function
//		call or later on, likely causing the crash of the program.
//
// warning	If the node being removed is on a different list than the
//		list specified, this WILL cause corruption of the list the
//		node is on if it is the first or last node of that list.
//
// note		Verification of a node being on a list is not practical
//		since to do so would result in a full scan of the list.
//-----------------------------------------------------------------------------
#define list_remove_node(a,e) (((void*)(list_remove_node)(&(a),&((e)->link))))
#define list_remove_front(a) (((a)?((void*)(list_remove_node)(&(a),((a)->link))):((list_member_p)(NULL))))
#define list_remove_back(a) (((a)?((void*)(list_remove_node)(&(a),((a)->link.list_prev))):((list_member_p)(NULL))))
list_member_p
(list_remove_node) (
    list_anchor_p	arg_list
    ,
    list_member_p	arg_node
    )
__PROTO_END__
{
    if ( arg_node == arg_node->list_next && arg_node == * arg_list ) {
	* arg_list = NULL;
    } else {
	arg_node->list_prev->list_next = arg_node->list_next;
	arg_node->list_next->list_prev = arg_node->list_prev;
	if ( arg_node == * arg_list ) * arg_list = arg_node->list_next;
    }
    return arg_node;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_rotate_forward
//
// purpose	Rotate a list so the first node becomes the last node and the
//		next node after the first becomes the first.
//
// arguments	1 (list_anchor) lvalue of list anchor
//
// returns	(int) 0
//-----------------------------------------------------------------------------
// function	(list_rotate_forward)
//
// purpose	Rotate a list so the first node becomes the last node and the
//		next node after the first becomes the first.
//
// arguments	1 (list_anchor_p) pointer to list anchor
//
// returns	(int) 0
//-----------------------------------------------------------------------------
#define list_rotate_forward(a) ((list_rotate_forward)(&(a)))
int
(list_rotate_forward) (
    list_anchor_p	arg_list
    )
__PROTO_END__
{
    if ( * arg_list ) * arg_list = ( * arg_list )->list_next;
    return 0;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// macro	list_rotate_backward
//
// purpose	Rotate a list so the last node becomes the first node and the
//		first node becomes the next node after the first.
//
// arguments	1 (list_anchor) lvalue of list anchor
//
// returns	(int) 0
//-----------------------------------------------------------------------------
// function	list_rotate_backward
//
// purpose	Rotate a list so the last node becomes the first node and the
//		first node becomes the next node after the first.
//
// arguments	1 (list_anchor_p) pointer to list anchor
//
// returns	(int) 0
//-----------------------------------------------------------------------------
#define list_rotate_backward(a) ((list_rotate_backward)(&(a)))
int
(list_rotate_backward) (
    list_anchor_p	arg_list
    )
__PROTO_END__
{
    if ( * arg_list ) * arg_list = ( * arg_list )->list_next;
    return 0;
}

