1998-03-28 02:44:41 +00:00
|
|
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Netscape Public License
|
|
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
|
|
* http://www.mozilla.org/NPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* NPL.
|
|
|
|
*
|
|
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
|
|
* Communications Corporation. Portions created by Netscape are
|
|
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
|
|
* Reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
inked list manipulation routines
|
|
|
|
|
|
|
|
this is a very standard linked list structure
|
|
|
|
used by many many programmers all over the world
|
|
|
|
|
|
|
|
The lists have been modified to be doubly linked. The
|
|
|
|
first element in a list is always the header. The 'next'
|
|
|
|
pointer of the header is the first element in the list.
|
|
|
|
The 'prev' pointer of the header is the last element in
|
|
|
|
the list.
|
|
|
|
|
|
|
|
The 'prev' pointer of the first real element in the list
|
|
|
|
is NULL as is the 'next' pointer of the last real element
|
|
|
|
in the list
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "xp.h"
|
|
|
|
|
|
|
|
#ifdef PROFILE
|
|
|
|
#pragma profile on
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
Delete all of the elements in the list.
|
|
|
|
This function does *NOT* free the objects in the list, just the
|
|
|
|
list structure itself (seems like a bug...)
|
|
|
|
*/
|
|
|
|
PUBLIC void
|
|
|
|
XP_ListDestroy (XP_List *list)
|
|
|
|
{
|
|
|
|
XP_List *list_ptr;
|
|
|
|
|
|
|
|
while (list)
|
|
|
|
{
|
|
|
|
list_ptr = list;
|
|
|
|
list = list->next;
|
|
|
|
XP_FREE(list_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Create a new list
|
|
|
|
*/
|
|
|
|
PUBLIC XP_List *
|
|
|
|
XP_ListNew (void)
|
|
|
|
{
|
|
|
|
XP_List *new_struct = XP_NEW(XP_List);
|
|
|
|
|
|
|
|
if (!new_struct)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
new_struct->object = NULL;
|
|
|
|
new_struct->next = NULL;
|
|
|
|
new_struct->prev = NULL;
|
|
|
|
return new_struct;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Add an object to the front of the list (which could also be the end)
|
|
|
|
*/
|
|
|
|
PUBLIC void
|
|
|
|
XP_ListAddObject (XP_List *list, void *object)
|
|
|
|
{
|
|
|
|
|
|
|
|
XP_ASSERT (list);
|
|
|
|
|
|
|
|
if (list) {
|
|
|
|
XP_List *new_struct = XP_NEW(XP_List);
|
|
|
|
|
|
|
|
if (!new_struct)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* set back pointer of old first element */
|
|
|
|
if(list->next) {
|
|
|
|
list->next->prev = new_struct;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we might have a next but since we are the first we WONT have a previous */
|
|
|
|
new_struct->object = object;
|
|
|
|
new_struct->next = list->next;
|
|
|
|
new_struct->prev = NULL;
|
|
|
|
|
|
|
|
/* the new element is the first one */
|
|
|
|
list->next = new_struct;
|
|
|
|
|
|
|
|
/* if this is the only element it is the end too */
|
|
|
|
if(NULL == list->prev)
|
|
|
|
list->prev = new_struct;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Add an object to the end of the list
|
|
|
|
*/
|
|
|
|
PUBLIC void
|
|
|
|
XP_ListAddObjectToEnd (XP_List *list, void *object)
|
|
|
|
{
|
|
|
|
XP_List * new_struct;
|
|
|
|
|
|
|
|
if(NULL == list)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* handle the case where the list is empty */
|
|
|
|
if(NULL == list->prev) {
|
|
|
|
XP_ListAddObject(list, object);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
XP_ASSERT(list->prev);
|
|
|
|
|
|
|
|
/* check for out of memory */
|
|
|
|
new_struct = XP_NEW(XP_List);
|
|
|
|
if (!new_struct)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* initialize our pointers */
|
|
|
|
new_struct->prev = list->prev;
|
|
|
|
new_struct->next = NULL;
|
|
|
|
new_struct->object = object;
|
|
|
|
|
|
|
|
/* make old last element point to us */
|
|
|
|
list->prev->next = new_struct;
|
|
|
|
|
|
|
|
/* update end of list pointer */
|
|
|
|
list->prev = new_struct;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Return the 'object' pointer of the last list entry
|
|
|
|
*/
|
|
|
|
PUBLIC void *
|
|
|
|
XP_ListGetEndObject (XP_List *list)
|
|
|
|
{
|
|
|
|
if (list && list->prev)
|
|
|
|
return(list->prev->object);
|
|
|
|
else
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Dunno what this is doing since it doesn't need to be rewritten
|
|
|
|
*/
|
|
|
|
PUBLIC void *
|
|
|
|
XP_ListGetObjectNum (XP_List *list, int num)
|
|
|
|
{
|
|
|
|
if(list == NULL)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
if(num < 1)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
list = list->next; /* the first list never has data */
|
|
|
|
|
|
|
|
for(; list && num > 1; num--)
|
|
|
|
{
|
|
|
|
list = list->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(list)
|
|
|
|
return(list->object);
|
|
|
|
else
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* move the top object to the bottom of the list
|
|
|
|
* this function is useful for reordering the list
|
|
|
|
* so that a round robin ordering can occur
|
|
|
|
*/
|
|
|
|
PUBLIC void
|
|
|
|
XP_ListMoveTopToBottom (XP_List *list)
|
|
|
|
{
|
|
|
|
XP_List *obj1;
|
|
|
|
XP_List *prev_end;
|
|
|
|
XP_List *next_obj;
|
|
|
|
|
|
|
|
if(!list->next || list->next == list->prev)
|
|
|
|
return; /* no action required */
|
|
|
|
|
|
|
|
/* move obj1 to the end */
|
|
|
|
obj1 = list->next;
|
|
|
|
next_obj = obj1->next;
|
|
|
|
prev_end = list->prev;
|
|
|
|
|
|
|
|
list->next = next_obj;
|
|
|
|
list->prev = obj1;
|
|
|
|
obj1->prev = prev_end;
|
|
|
|
obj1->next = NULL;
|
|
|
|
prev_end->next = obj1;
|
|
|
|
|
|
|
|
if(next_obj)
|
|
|
|
next_obj->prev = list;
|
|
|
|
}
|
|
|
|
|
1999-06-16 01:00:29 +00:00
|
|
|
#if 0
|
1998-03-28 02:44:41 +00:00
|
|
|
/*
|
|
|
|
Comment goes here
|
|
|
|
*/
|
|
|
|
PUBLIC int
|
|
|
|
XP_ListGetNumFromObject (XP_List *list, void * object)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
void * obj2;
|
|
|
|
|
|
|
|
while((obj2 = XP_ListNextObject(list)) != NULL)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
if(obj2 == object)
|
|
|
|
return(count);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0); /* not found */
|
|
|
|
}
|
1999-06-16 01:00:29 +00:00
|
|
|
|
1998-03-28 02:44:41 +00:00
|
|
|
/* returns the list node of the specified object if it was
|
|
|
|
* in the list
|
|
|
|
*/
|
|
|
|
PUBLIC XP_List *
|
|
|
|
XP_ListFindObject (XP_List *list, void * obj)
|
|
|
|
{
|
|
|
|
if(!list)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
list = list->next; /* the first list item never has data */
|
|
|
|
|
|
|
|
while(list)
|
|
|
|
{
|
|
|
|
if(list->object == obj)
|
|
|
|
return(list);
|
|
|
|
list = list->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
1999-06-16 01:00:29 +00:00
|
|
|
#endif
|
1998-03-28 02:44:41 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Count the number of items in the list
|
|
|
|
Should we cache this in the header? maybe in header->object
|
|
|
|
*/
|
|
|
|
PUBLIC int
|
|
|
|
XP_ListCount (XP_List *list)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
if (list)
|
|
|
|
while ((list = list->next) != NULL)
|
|
|
|
count++;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Insert an object somewhere into the list.
|
|
|
|
If insert_before is not NULL find the element whose 'object' is equal to
|
|
|
|
insert_before and add a new element in front of that element.
|
|
|
|
*/
|
|
|
|
PUBLIC void
|
|
|
|
XP_ListInsertObject (XP_List *list, void *insert_before, void *object)
|
|
|
|
{
|
|
|
|
XP_List *temp;
|
|
|
|
XP_List *new_struct;
|
|
|
|
|
|
|
|
XP_ASSERT(list);
|
|
|
|
|
|
|
|
if (list) {
|
|
|
|
if(NULL == insert_before) {
|
|
|
|
XP_ListAddObjectToEnd(list, object);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find the prev pointer so we can add the forward pointer to
|
|
|
|
* the new object
|
|
|
|
*/
|
|
|
|
temp = list;
|
|
|
|
|
|
|
|
/* walk down the list till we find the element we want */
|
|
|
|
for(temp = list; temp; temp = temp->next)
|
|
|
|
if(temp->object == insert_before)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
We got all the way though and didn't find the element we wanted
|
|
|
|
so just stick the new element on the end of the list
|
|
|
|
*/
|
|
|
|
if(!temp) {
|
|
|
|
XP_ListAddObjectToEnd(list, object);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_struct = XP_NEW(XP_List);
|
|
|
|
if (!new_struct)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* set our values */
|
|
|
|
new_struct->object = object;
|
|
|
|
new_struct->next = temp;
|
|
|
|
new_struct->prev = temp->prev;
|
|
|
|
|
|
|
|
/* there is either a previous element or we are the new first element */
|
|
|
|
if(temp->prev)
|
|
|
|
temp->prev->next = new_struct;
|
|
|
|
else
|
|
|
|
list->next = new_struct;
|
|
|
|
|
|
|
|
temp->prev = new_struct;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-06-16 01:00:29 +00:00
|
|
|
#if 0
|
1998-03-28 02:44:41 +00:00
|
|
|
/*
|
|
|
|
Insert an object somewhere into the list.
|
|
|
|
If insert_after is not NULL find the element whose 'object' is equal to
|
|
|
|
insert_after and add a new element in back of that element.
|
|
|
|
*/
|
|
|
|
PUBLIC void
|
|
|
|
XP_ListInsertObjectAfter (XP_List *list, void *insert_after, void *object)
|
|
|
|
{
|
|
|
|
XP_List *tmp;
|
|
|
|
XP_List *new_struct;
|
|
|
|
|
|
|
|
if(list) {
|
|
|
|
|
|
|
|
/* if no element to insert after just add to the end of the list */
|
|
|
|
if(NULL == insert_after) {
|
|
|
|
XP_ListAddObjectToEnd(list, object);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check that insert_after is a valid list entry */
|
|
|
|
for(tmp = list; tmp; tmp = tmp->next)
|
|
|
|
if(tmp->object == insert_after)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* the object we were asked to insert after is not actually in the
|
|
|
|
list. Add the new item to the end
|
|
|
|
*/
|
|
|
|
if(!tmp) {
|
|
|
|
XP_ListAddObjectToEnd(list, object);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* creat a new list struct and insert it */
|
|
|
|
new_struct = XP_NEW(XP_List);
|
|
|
|
if (!new_struct)
|
|
|
|
return;
|
|
|
|
|
|
|
|
new_struct->object = object;
|
|
|
|
new_struct->next = tmp->next;
|
|
|
|
new_struct->prev = tmp;
|
|
|
|
|
|
|
|
/* we either have a next element or we are the last one */
|
|
|
|
if(tmp->next)
|
|
|
|
tmp->next->prev = new_struct;
|
|
|
|
else
|
|
|
|
list->prev = new_struct;
|
|
|
|
|
|
|
|
tmp->next = new_struct;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
1999-06-16 01:00:29 +00:00
|
|
|
#endif
|
1998-03-28 02:44:41 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Remove the element whose 'object' field is equal to remove_me
|
|
|
|
Return TRUE if such an element was remove else FALSE
|
|
|
|
*/
|
|
|
|
PUBLIC Bool
|
|
|
|
XP_ListRemoveObject (XP_List *list, void *remove_me)
|
|
|
|
{
|
|
|
|
XP_List * tmp;
|
|
|
|
|
|
|
|
/* check if list is empty */
|
|
|
|
if(NULL == list)
|
|
|
|
return(FALSE);
|
|
|
|
|
|
|
|
/* walk down list looking for object we want */
|
|
|
|
for(tmp = list; tmp; tmp = tmp->next)
|
|
|
|
if(tmp->object == remove_me)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* element was not found */
|
|
|
|
if(NULL == tmp)
|
|
|
|
return(FALSE);
|
|
|
|
|
|
|
|
/* we either have a prev or we were the first element */
|
|
|
|
if(tmp->prev)
|
|
|
|
tmp->prev->next = tmp->next;
|
|
|
|
else
|
|
|
|
list->next = tmp->next;
|
|
|
|
|
|
|
|
/* we either have a next or we were the last element */
|
|
|
|
if(tmp->next)
|
|
|
|
tmp->next->prev = tmp->prev;
|
|
|
|
else
|
|
|
|
list->prev = tmp->prev;
|
|
|
|
|
|
|
|
/* OK we have updated all of the pointers now free the element */
|
|
|
|
XP_FREE (tmp);
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Remove the last element in the list and return it
|
|
|
|
*/
|
|
|
|
PUBLIC void *
|
|
|
|
XP_ListRemoveEndObject (XP_List *list)
|
|
|
|
{
|
|
|
|
XP_List * last;
|
|
|
|
void * data;
|
|
|
|
|
|
|
|
/* check if bogus list */
|
|
|
|
if(NULL == list)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
/* check if no last element (bug or empty list) */
|
|
|
|
if(NULL == list->prev)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
last = list->prev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
there is either a previous element or we were the first and
|
|
|
|
only element
|
|
|
|
*/
|
|
|
|
if(last->prev)
|
|
|
|
last->prev->next = NULL;
|
|
|
|
else
|
|
|
|
list->next = NULL;
|
|
|
|
|
|
|
|
/* always a new last element */
|
|
|
|
list->prev = last->prev;
|
|
|
|
|
|
|
|
/* remember the data pointer so we can return it */
|
|
|
|
data = last->object;
|
|
|
|
XP_FREE(last);
|
|
|
|
return(data);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Remove the first element in the list and return its 'object'
|
|
|
|
*/
|
|
|
|
PUBLIC void *
|
|
|
|
XP_ListRemoveTopObject (XP_List *list)
|
|
|
|
{
|
|
|
|
XP_List * first;
|
|
|
|
void * data;
|
|
|
|
|
|
|
|
/* check if bogus list */
|
|
|
|
if(NULL == list)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
/* check if no first element (bug or empty list) */
|
|
|
|
if(NULL == list->next)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
first = list->next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
There is either a next element or we were the last element too
|
|
|
|
*/
|
|
|
|
if(first->next)
|
|
|
|
first->next->prev = NULL;
|
|
|
|
else
|
|
|
|
list->prev = NULL;
|
|
|
|
|
|
|
|
/* new first element */
|
|
|
|
list->next = first->next;
|
|
|
|
|
|
|
|
/* remember the data pointer so we can return it */
|
|
|
|
data = first->object;
|
|
|
|
XP_FREE(first);
|
|
|
|
return(data);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
PUBLIC void *
|
|
|
|
XP_ListPeekTopObject (XP_List *list)
|
|
|
|
{
|
|
|
|
if (list && list->next)
|
|
|
|
{
|
|
|
|
return list->next->object;
|
|
|
|
}
|
|
|
|
else /* Empty list */
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef PROFILE
|
|
|
|
#pragma profile off
|
|
|
|
#endif
|
|
|
|
|