nextcloud-desktop/src/std/c_list.c
2009-06-05 11:22:06 +02:00

461 lines
8.3 KiB
C

/*
* csync list -- a doubly-linked list
*
* This code is based on glist.{h,c} from glib
* ftp://ftp.gtk.org/pub/gtk/
* Copyright (c) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
* Copyright (c) 2006-2008 Andreas Schneider <mail@csyncapses.org>
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* vim: ts=2 sw=2 et cindent
*/
#include <stdio.h>
#include <stdlib.h>
#include "c_alloc.h"
#include "c_list.h"
/*
* Adds a new element on to the end of the list.
*/
c_list_t *c_list_append(c_list_t *list, void *data) {
c_list_t *new;
c_list_t *last;
new = c_list_alloc();
if (new == NULL) {
return NULL;
}
new->data = data;
if (list == NULL) {
return new;
}
last = c_list_last(list);
last->next = new;
new->prev = last;
return list;
}
/*
* Adds a new element on at the beginning of the list.
*/
c_list_t *c_list_prepend(c_list_t *list, void *data) {
c_list_t *new;
c_list_t *first;
new = c_list_alloc();
if (new == NULL) {
return NULL;
}
new->data = data;
if (list != NULL) {
first = c_list_first(list);
first->prev = new;
new->next = first;
}
return new;
}
/*
* Inserts a new element into the list at the given position.
*/
c_list_t *c_list_insert(c_list_t *list, void *data, long position) {
c_list_t *new;
c_list_t *temp;
/* Handle wrong values for position */
if (position < 0) {
return c_list_append (list, data);
} else if (position == 0) {
return c_list_prepend (list, data);
}
temp = c_list_position(list, position);
if (temp == NULL) {
return c_list_append(list, data);
}
new = c_list_alloc();
if (new == NULL) {
return NULL;
}
new->data = data;
/* List is not empty */
if (temp->prev) {
temp->prev->next = new;
new->prev = temp->prev;
}
new->next = temp;
temp->prev = new;
/* */
if (temp == list) {
return new;
}
return list;
}
/*
* Inserts a new element into the list, using the given comparison function to
* determine its position.
*/
c_list_t *c_list_insert_sorted(c_list_t *list, void *data,
c_list_compare_fn fn) {
c_list_t *new;
c_list_t *temp;
int cmp;
new = c_list_alloc();
if (new == NULL) {
return NULL;
}
new->data = data;
/* list is empty */
if (list == NULL) {
return new;
}
temp = list;
cmp = (fn)(data, temp->data);
while ((temp->next) && (cmp > 0)) {
temp = temp->next;
cmp = (fn)(data, temp->data);
}
/* last element */
if ((temp->next == NULL) && (cmp > 0)) {
temp->next = new;
new->prev = temp;
return list;
}
/* first element */
if (temp->prev) {
temp->prev->next = new;
new->prev = temp->prev;
}
new->next = temp;
temp->prev = new;
/* inserted before first */
if (temp == list) {
return new;
}
return list;
}
/*
* Allocates space for one c_list_t element.
*/
c_list_t *c_list_alloc(void) {
c_list_t *list = NULL;
list = c_malloc(sizeof(c_list_t));
if (list == NULL) {
return NULL;
}
list->data = NULL;
list->prev = NULL;
list->next = NULL;
return list;
}
/*
* Removes an element from a c_list. If two elements contain the same data,
* only the first is removed.
*/
c_list_t *c_list_remove(c_list_t *list, void *data) {
c_list_t *temp;
if (list == NULL || data == NULL) {
return NULL;
}
temp = list;
while (temp != NULL) {
if (temp->data != data) {
temp = temp->next;
} else {
/* not at first element */
if (temp->prev) {
temp->prev->next = temp->next;
}
/* not at last element */
if (temp->next) {
temp->next->prev = temp->prev;
}
/* first element */
if (list == temp) {
list = list->next;
}
SAFE_FREE(temp);
break;
}
}
return list;
}
/*
* Frees all elements from a c_list.
*/
void c_list_free(c_list_t *list) {
c_list_t *temp = NULL;
if (list == NULL) {
return;
}
list = c_list_last(list);
while (list->prev != NULL) {
temp = list;
list = list->prev;
SAFE_FREE(temp);
}
SAFE_FREE(list);
}
/*
* Gets the next element in a c_list.
*/
c_list_t *c_list_next(c_list_t *list) {
if (list == NULL) {
return NULL;
}
return list->next;
}
/*
* Gets the previous element in a c_list.
*/
c_list_t *c_list_prev(c_list_t *list) {
if (list == NULL) {
return NULL;
}
return list->prev;
}
/*
* Gets the number of elements in a c_list
*/
unsigned long c_list_length(c_list_t *list) {
unsigned long length = 1;
if (list == NULL) {
return 0;
}
while (list->next) {
length++;
list = list->next;
}
return length;
}
/*
* Gets the first element in a c_list
*/
c_list_t *c_list_first(c_list_t *list) {
if (list != NULL) {
while (list->prev) {
list = list->prev;
}
}
return list;
}
/*
* Gets the last element in a c_list
*/
c_list_t *c_list_last(c_list_t *list) {
if (list != NULL) {
while (list->next) {
list = list->next;
}
}
return list;
}
/*
* Gets the element at the given positon in a c_list
*/
c_list_t *c_list_position(c_list_t *list, long position) {
if (list == NULL) {
return NULL;
}
while ((position-- > 0) && list != NULL) {
list = list->next;
}
return list;
}
/*
* Finds the element in a c_list_t which contains the given data.
*/
c_list_t *c_list_find(c_list_t *list, const void *data) {
if (list == NULL) {
return NULL;
}
while (list != NULL) {
if (list->data == data) {
break;
}
list = list->next;
}
return list;
}
/*
* Finds an element, using a supplied function to find the desired
* element.
*/
c_list_t *c_list_find_custom(c_list_t *list, const void *data,
c_list_compare_fn fn) {
int cmp;
if (list != NULL && fn != NULL) {
while (list != NULL) {
cmp = (*fn)(list->data, data);
if (cmp == 0) {
return list;
}
list = list->next;
}
}
return NULL;
}
/*
* Internal used function to merge 2 lists using a compare function
*/
static c_list_t *_c_list_merge(c_list_t *list1, c_list_t *list2,
c_list_compare_fn func) {
int cmp;
/* lists are emty */
if (list1 == NULL) {
return list2;
} else if (list2 == NULL) {
return list1;
}
cmp = ((c_list_compare_fn) func)(list1->data, list2->data);
/* compare if it is smaller */
if (cmp <= 0) {
list1->next = _c_list_merge(list1->next, list2, func);
if (list1->next) {
list1->next->prev = list1;
}return list1;
} else {
list2->next = _c_list_merge(list1, list2->next, func);
if (list2->next) {
list2->next->prev = list2;
}
return list2;
}
}
/*
* Internally used function to split 2 lists.
*/
static c_list_t *_c_list_split(c_list_t *list) {
c_list_t *second = NULL;
/* list is empty */
if (list == NULL) {
return NULL;
} else if (list->next == NULL) {
/* list has only 1 element */
return NULL;
} else {
/* split */
second = list->next;
list->next = second->next;
/* is last element */
if (list->next) {
list->next->prev = list;
}
second->prev = NULL;
second->next = _c_list_split(second->next);
/* is last element */
if (second->next) {
second->next->prev = second;
}
return second;
}
/* never reached */
return NULL;
}
/*
* Sorts the elements of a c_list. This is a merge sort.
*/
c_list_t *c_list_sort(c_list_t *list, c_list_compare_fn func) {
c_list_t *second;
/* list is empty */
if (list == NULL) {
return NULL;
} else if (list->next == NULL) {
/* list has only one element */
return list;
} else {
/* split list */
second = _c_list_split(list);
}
return _c_list_merge(c_list_sort(list, func), c_list_sort(second, func),
func);
}