list_intro

	LIST_INTRO

	The list library functions provide an easy way to manipulate
	linked lists. These routines use the VAX queue instructions so they
	execute quickly.

	The list's header is simply an instance of list_link_t. Before using
	the list, however, you should call list_init().

	Example 1 (creates a list header, i.e. empty list):
	   |
	   | list_link_t l_header;
	   |
	   | ...
	   |     list_init(&l_header);
	   | ...
	   |

	To allow your structures to be part of a list, make one of the data
	members a list_link_t data type. The address to this member is what
	gets passed to the list functions.

	Example 2 (Insert a node into the list):
	   |
	   | typedef struct {
	   |     list_link_t node;
	   |     char name[20];
	   |     int employee_id;
	   | } my_data_t;
	   |
	   | ...
	   |     my_data_t *new_employee;
	   |
	   |     new_employee = (my_data_t *) malloc(sizeof (my_data_t));
	   |     list_insert(&l_header, &new_employee->node);
	   | ...
	   |

	To traverse a list, start with the header structure and loop through
	each entry by using the 'next' pointer in the list_link_t member.

	Example 3 (Traverse a list):

	   |
	   | list_link_t *ptr;
	   | my_data_t *data;
	   |
	   | ptr = root.next;
	   | while (ptr != &root) {
	   |     data = (my_data_t *) ptr;
	   |     ...
	   |     ptr = ptr->next;
	   | }
	   |

	Example 4 (Traverse a list with better typecasting):

	   |
	   | list_link_t *ptr;
	   | my_data_t *data;
	   |
	   | ptr = root.next;
	   | while (ptr != &root) {
	   |     data = CAST_LLINK_TO (my_data_t, node, ptr);
	   |     ...
	   |     ptr = ptr->next;
	   | }
	   |

	In the above examples, Example 4 is slightly better because the
	link_link_t pointer is typecast using the CAST_LLINK_TO macro.
	Example 3 only works if no data members are added before the
	'node' field. Example 4 works even when someone adds a data
	member before the 'node' field.

	An alternative to looping through the list is to use list_for_each().
	In example 5, process_node() is called once for each node in the list.
	This method is slightly better than either of the above examples
	mainly because this method is portable.

	Example 5 :

	   |
	   | void process_node (list_link_t *ptr)
	   | {
	   |     my_data_t *data = CAST_LLINK_TO(my_data_t, node, ptr);
	   |     ...
	   | }
	   |
	   | ...
	   |     list_for_each(&l_header, process_node);
	   | ...
	   |

	A similar approach is used to perform searches on a list. In this
	case, the function passed to the list_find() routine returns TRUE
	or FALSE, depending whether the current node matches the search
	specification.

	Example 6 :

	   |
	   | int test_node (list_link_t *ptr, void *arg)
	   | {
	   |     my_data_t *data = CAST_LLINK_TO(my_data_t, node, ptr);
	   |     ...
	   |     return TRUE;    // (or FALSE)
	   | }
	   |
	   | ...
	   |     list_link_t *matched_node = list_find(
	   |         &l_header, LIST_DIR_FORWARD, process_node, NULL);
	   | ...
	   |

	If multiple list_link_t fields are placed in a structure, the
	data can then reside in multiple lists simulatenously! However,
	since only one of the list_link_t fields can be placed at the
	beginning of the structure, you'll have to use CAST_LLINK_TO to
	properly convert pointers.

	To use these functions, you need the following include files:

	cbslib_h, acnet_errors_h

	Related functions:

	list_init, list_insert, LIST_INSERT_HEAD, LIST_INSERT_TAIL,
	list_delete, list_remove, list_find, list_for_each, LIST_IS_EMPTY
	CAST_LLINK_TO