This repository has been archived on 2020-04-11. You can view files and clone it, but cannot push or open issues or pull requests.
eilduscd/src/list.c

451 lines
8.8 KiB
C

/* $Id: list.c,v 1.13 2003/06/15 10:05:55 sjoerd Exp $ */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "common.h"
#include "list.h"
/*
* Simple refcounted linked list implementation.
* Copyright (C) 2002-2006 Sjoerd Simons
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
static void
list_node_ref(List * list, List_node * item) {
item->refcount++;
}
static void
list_node_unref(List * list, List_node * item) {
item->refcount--;
if (item->refcount <= 0) {
if (item->next != NULL) {
item->next->prev = item->prev;
}
if (item->prev != NULL) {
item->prev->next = item->next;
}
if (list->head == item) {
list->head = item->next;
}
if (list->tail == item) {
list->tail = item->prev;
}
free(item);
}
}
static List_node *
list_new_node(void *data) {
List_node *result = malloc(sizeof(List_node));
assert(data != NULL);
assert(result != NULL);
memset(result, 0, sizeof(List_node));
result->data = data;
return result;
}
static void
list_del_node(List * list, List_node * item) {
item->data = NULL;
list->length--;
list_node_unref(list, item);
}
List *
new_list(void) {
List *result;
result = malloc(sizeof(List));
assert(result != NULL);
memset(result, 0, sizeof(List));
return result;
}
void
del_list(List * list) {
List_ptr *ptr;
ptr = new_list_ptr(list);
if (list_ptr_first(ptr)) {
do {
list_ptr_del_current(ptr);
} while (list_ptr_next(ptr));
}
del_list_ptr(ptr);
free(list);
}
int
list_prepend(List * list, void *data) {
List_node *n;
n = list_new_node(data);
list_node_ref(list, n);
if (list->tail == NULL) {
list->tail = n;
} else {
/* list->tail != NULL so list->head is also !NULL */
n->next = list->head;
list->head->prev = n;
}
list->head = n;
list->length++;
return TRUE;
}
int
list_append(List * list, void *data) {
List_node *n;
n = list_new_node(data);
list_node_ref(list, n);
if (list->head == NULL) {
list->head = n;
} else {
/* list->head != NULL so list->tail is also !NULL */
n->prev = list->tail;
list->tail->next = n;
}
list->tail = n;
list->length++;
return TRUE;
}
int
list_cmp_data(void *data, void *user_data) {
return data == user_data;
}
int
list_del(List * list, void *data) {
List_ptr *ptr;
ptr = new_list_ptr(list);
if (!list_ptr_first(ptr)) {
del_list_ptr(ptr);
return FALSE;
}
do {
if (list_ptr_get_data(ptr) == data) {
list_ptr_del_current(ptr);
del_list_ptr(ptr);
return TRUE;
}
} while (list_ptr_next(ptr));
del_list_ptr(ptr);
return FALSE;
}
void *
list_search(List * list, ListFunc cmp_func, void *user_data) {
List_ptr *ptr;
void *data;
ptr = new_list_ptr(list);
if (!list_ptr_first(ptr)) {
del_list_ptr(ptr);
return NULL;
}
do {
data = list_ptr_get_data(ptr);
if (cmp_func(data, user_data)) {
del_list_ptr(ptr);
return data;
}
} while (list_ptr_next(ptr));
del_list_ptr(ptr);
return NULL;
}
int
list_foreach(List * list, ListFunc do_func, void *user_data) {
List_ptr *ptr;
void *data;
ptr = new_list_ptr(list);
if (!list_ptr_first(ptr)) {
del_list_ptr(ptr);
return TRUE;
}
do {
data = list_ptr_get_data(ptr);
if (!do_func(data, user_data)) {
del_list_ptr(ptr);
return FALSE;
}
} while (list_ptr_next(ptr));
del_list_ptr(ptr);
return TRUE;
}
List_ptr *
new_list_ptr(List * list) {
List_ptr *result;
result = malloc(sizeof(List_ptr));
assert(result != NULL);
result->list = list;
result->current = NULL;
return result;
}
void
del_list_ptr(List_ptr * ptr) {
if (ptr->current != NULL) {
list_node_unref(ptr->list, ptr->current);
}
free(ptr);
}
List_ptr *
list_ptr_dup(List_ptr * ptr) {
List_ptr *result;
result = malloc(sizeof(List_ptr));
assert(result != NULL);
memcpy(result, ptr, sizeof(List_ptr));
list_node_ref(result->list, result->current);
return result;
}
void *
list_ptr_get_data(List_ptr * ptr) {
return ptr->current == NULL ? NULL : ptr->current->data;
}
void
list_ptr_del_current(List_ptr * ptr) {
if (ptr->current != NULL && ptr->current->data != NULL) {
list_del_node(ptr->list, ptr->current);
}
}
static int
list_ptr_getend(List_ptr * ptr, int head) {
List_node *next;
if (head) {
next = ptr->list->head;
} else {
next = ptr->list->tail;
}
while (next != NULL && next->data == NULL) {
if (head) {
next = next->next;
} else {
next = next->prev;
}
}
if (next == NULL) {
return FALSE;
}
if (ptr->current != NULL) {
list_node_unref(ptr->list, ptr->current);
}
ptr->current = next;
list_node_ref(ptr->list, ptr->current);
return TRUE;
}
int
list_ptr_first(List_ptr * ptr) {
return list_ptr_getend(ptr, TRUE);
}
int
list_ptr_last(List_ptr * ptr) {
return list_ptr_getend(ptr, FALSE);
}
static int
list_ptr_step(List_ptr * ptr, int forward) {
List_node *next;
if (ptr->current == NULL) {
return FALSE;
}
next = ptr->current->next;
while (next != NULL && next->data == NULL) {
if (forward) {
next = next->next;
} else {
next = next->prev;
}
}
if (next == NULL) {
return FALSE;
}
list_node_unref(ptr->list, ptr->current);
ptr->current = next;
list_node_ref(ptr->list, ptr->current);
return TRUE;
}
int
list_ptr_next(List_ptr * ptr) {
return list_ptr_step(ptr, TRUE);
}
int
list_ptr_prev(List_ptr * ptr) {
return list_ptr_step(ptr, FALSE);
}
void *
list_pop(List *list) {
void *result;
List_node *node;
for (node = list->head; node != NULL && node->data == NULL;
node = node->next);
if (node == NULL)
return NULL;
result = node->data;
list_del_node(list,node);
return result;
}
int
list_length(List *list) {
return list->length;
}
void
list_swapnodes(List_node *node0,List_node *node1) {
List_node *tmp;
if (node0->next == node1) {
node0->next = node1->next;
node1->next = node0;
tmp = node0->prev;
node0->prev = node1;
node1->prev = tmp;
} else if (node1->next == node0) {
node1->next = node0->next;
node0->next = node1;
tmp = node1->prev;
node1->prev = node0;
node0->prev = tmp;
} else {
tmp = node1->next;
node1->next = node0->next;
node0->next = tmp;
tmp = node1->prev;
node1->prev = node0->prev;
node0->prev = tmp;
}
if (node1->next != NULL) {
node1->next->prev = node1;
}
if (node0->next != NULL) {
node0->next->prev = node0;
}
if (node0->prev != NULL) {
node0->prev->next = node0;
}
if (node1->prev != NULL) {
node1->prev->next = node1;
}
}
List_node *
list_partition(ListSortFunc func,List_node **snode,
List_node **enode,void *user_data) {
List_node *node;
List_node *tmp;
List_node *pivot = *enode;
List_node *pivotpoint = *snode;;
for (node = *snode ; node != *enode; node = node->next) {
if (func(node->data,pivot->data,user_data) <= 0) {
if (node != pivotpoint) {
list_swapnodes(node,pivotpoint);
if (*snode == pivotpoint) {
*snode = node;
} else if (*snode == node) {
*snode = pivotpoint;
}
}
tmp = pivotpoint;
pivotpoint = node->next;
node = tmp;
}
}
list_swapnodes(pivot,pivotpoint);
if (*snode == pivotpoint) {
*snode = pivot;
}
*enode = pivotpoint;
return pivot;
}
int
list_do_sort(ListSortFunc func,
List_node *snode, List_node *enode,void *user_data) {
List_node *node;
if (snode != NULL && enode != NULL &&
snode->prev != enode && snode != enode) {
node = list_partition(func,&snode,&enode,user_data);
list_do_sort(func,snode,node->prev,user_data);
list_do_sort(func,node->next,enode,user_data);
}
return TRUE;
}
int
list_sort(List *list,ListSortFunc func,void *user_data) {
List_node *node;
if (list->length == 0)
return TRUE;
list_do_sort(func,list->head,list->tail,user_data);
/* fix -> head pointer */
for (node = list->head; node->prev != NULL; node = node->prev)
;
list->head = node;
/* fix ->tail pointer */
for (node = list->tail; node->next != NULL; node = node->next)
;
list->tail = node;
return TRUE;
}