nextcloud-desktop/src/std/c_list.c

462 lines
8.3 KiB
C
Raw Normal View History

2008-05-19 20:09:39 +04:00
/*
* 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;
2008-05-19 20:09:39 +04:00
new = c_list_alloc();
2008-05-20 12:06:02 +04:00
if (new == NULL) {
return NULL;
}
2008-05-19 20:09:39 +04:00
new->data = data;
2008-05-20 12:06:02 +04:00
if (list == NULL) {
return new;
2009-06-05 12:51:22 +04:00
}
2008-05-19 20:09:39 +04:00
2009-06-05 12:51:22 +04:00
last = c_list_last(list);
2008-05-19 20:09:39 +04:00
2009-06-05 12:51:22 +04:00
last->next = new;
new->prev = last;
return list;
2008-05-19 20:09:39 +04:00
}
/*
* 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;
2008-05-19 20:09:39 +04:00
new = c_list_alloc();
2008-05-20 12:06:02 +04:00
if (new == NULL) {
return NULL;
}
2008-05-19 20:09:39 +04:00
new->data = data;
2008-05-20 12:06:02 +04:00
if (list != NULL) {
2008-05-19 20:09:39 +04:00
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;
2008-05-19 20:09:39 +04:00
/* 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) {
2008-05-19 20:09:39 +04:00
return c_list_append(list, data);
}
new = c_list_alloc();
2008-05-20 12:06:02 +04:00
if (new == NULL) {
return NULL;
}
2008-05-19 20:09:39 +04:00
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;
}
2009-06-05 12:51:22 +04:00
return list;
2008-05-19 20:09:39 +04:00
}
/*
* 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,
2009-05-23 13:57:15 +04:00
c_list_compare_fn fn) {
c_list_t *new;
c_list_t *temp;
2008-05-19 20:09:39 +04:00
int cmp;
new = c_list_alloc();
2008-05-20 12:06:02 +04:00
if (new == NULL) {
return NULL;
}
2008-05-19 20:09:39 +04:00
new->data = data;
/* list is empty */
2008-05-20 12:06:02 +04:00
if (list == NULL) {
2008-05-19 20:09:39 +04:00
return new;
}
temp = list;
2009-05-23 13:57:15 +04:00
cmp = (fn)(data, temp->data);
2008-05-19 20:09:39 +04:00
while ((temp->next) && (cmp > 0)) {
temp = temp->next;
2009-05-23 13:57:15 +04:00
cmp = (fn)(data, temp->data);
2008-05-19 20:09:39 +04:00
}
/* last element */
if ((temp->next == NULL) && (cmp > 0)) {
2008-05-19 20:09:39 +04:00
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;
}
2009-06-05 12:51:22 +04:00
return list;
2008-05-19 20:09:39 +04:00
}
/*
* Allocates space for one c_list_t element.
*/
c_list_t *c_list_alloc(void) {
c_list_t *list = NULL;
2008-05-19 20:09:39 +04:00
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;
2008-05-19 20:09:39 +04:00
2008-05-20 13:14:16 +04:00
if (list == NULL || data == NULL) {
return NULL;
}
2008-05-19 20:09:39 +04:00
temp = list;
2008-05-20 12:06:02 +04:00
while (temp != NULL) {
2008-05-19 20:09:39 +04:00
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;
}
2008-05-20 12:06:02 +04:00
SAFE_FREE(temp);
2008-05-19 20:09:39 +04:00
break;
}
}
return list;
}
/*
* Frees all elements from a c_list.
*/
void c_list_free(c_list_t *list) {
c_list_t *temp = NULL;
2008-05-19 20:09:39 +04:00
if (list == NULL) {
return;
}
2008-05-19 20:09:39 +04:00
list = c_list_last(list);
while (list->prev != NULL) {
temp = list;
list = list->prev;
SAFE_FREE(temp);
2008-05-19 20:09:39 +04:00
}
SAFE_FREE(list);
2008-05-19 20:09:39 +04:00
}
/*
* Gets the next element in a c_list.
*/
c_list_t *c_list_next(c_list_t *list) {
2008-05-20 12:06:02 +04:00
if (list == NULL) {
2008-05-19 20:09:39 +04:00
return NULL;
}
2009-06-05 12:51:22 +04:00
return list->next;
2008-05-19 20:09:39 +04:00
}
/*
* Gets the previous element in a c_list.
*/
2008-05-20 13:24:09 +04:00
c_list_t *c_list_prev(c_list_t *list) {
2008-05-20 12:06:02 +04:00
if (list == NULL) {
2008-05-19 20:09:39 +04:00
return NULL;
}
2009-06-05 12:51:22 +04:00
return list->prev;
2008-05-19 20:09:39 +04:00
}
/*
* Gets the number of elements in a c_list
*/
unsigned long c_list_length(c_list_t *list) {
2008-05-20 13:14:16 +04:00
unsigned long length = 1;
2008-05-19 20:09:39 +04:00
2008-05-20 13:14:16 +04:00
if (list == NULL) {
return 0;
}
while (list->next) {
length++;
list = list->next;
2008-05-19 20:09:39 +04:00
}
return length;
}
/*
* Gets the first element in a c_list
*/
c_list_t *c_list_first(c_list_t *list) {
2008-05-20 12:06:02 +04:00
if (list != NULL) {
2008-05-19 20:09:39 +04:00
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) {
2008-05-20 12:06:02 +04:00
if (list != NULL) {
2008-05-19 20:09:39 +04:00
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) {
2008-05-20 14:05:27 +04:00
if (list == NULL) {
return NULL;
}
2008-05-20 12:06:02 +04:00
while ((position-- > 0) && list != NULL) {
2008-05-19 20:09:39 +04:00
list = list->next;
}
return list;
}
/*
* Finds the element in a c_list_t which contains the given data.
*/
2009-06-05 13:22:06 +04:00
c_list_t *c_list_find(c_list_t *list, const void *data) {
2008-05-20 12:06:02 +04:00
if (list == NULL) {
return NULL;
2008-05-19 20:09:39 +04:00
}
2008-05-20 12:06:02 +04:00
while (list != NULL) {
if (list->data == data) {
break;
}
list = list->next;
}
return list;
2008-05-19 20:09:39 +04:00
}
/*
* Finds an element, using a supplied function to find the desired
* element.
*/
2009-06-05 13:22:06 +04:00
c_list_t *c_list_find_custom(c_list_t *list, const void *data,
c_list_compare_fn fn) {
2008-05-19 20:09:39 +04:00
int cmp;
2009-06-05 13:22:06 +04:00
if (list != NULL && fn != NULL) {
2008-05-20 12:06:02 +04:00
while (list != NULL) {
2009-06-05 13:22:06 +04:00
cmp = (*fn)(list->data, data);
2008-05-19 20:09:39 +04:00
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) {
2008-05-19 20:09:39 +04:00
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);
2008-05-19 20:09:39 +04:00
if (list1->next) {
list1->next->prev = list1;
}return list1;
} else {
list2->next = _c_list_merge(list1, list2->next, func);
2008-05-19 20:09:39 +04:00
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;
2008-05-19 20:09:39 +04:00
/* list is empty */
if (list == NULL) {
return NULL;
} else if (list->next == NULL) {
/* list has only 1 element */
2008-05-19 20:09:39 +04:00
return NULL;
} else {
/* split */
2008-05-19 20:09:39 +04:00
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);
2008-05-19 20:09:39 +04:00
/* is last element */
if (second->next) {
second->next->prev = second;
}
2008-05-19 20:09:39 +04:00
return second;
}
/* never reached */
return NULL;
2008-05-19 20:09:39 +04:00
}
2008-05-20 12:06:02 +04:00
/*
* Sorts the elements of a c_list. This is a merge sort.
2008-05-20 12:06:02 +04:00
*/
c_list_t *c_list_sort(c_list_t *list, c_list_compare_fn func) {
c_list_t *second;
2008-05-20 12:06:02 +04:00
/* list is empty */
if (list == NULL) {
return NULL;
} else if (list->next == NULL) {
/* list has only one element */
2008-05-20 12:06:02 +04:00
return list;
} else {
/* split list */
second = _c_list_split(list);
2008-05-20 12:06:02 +04:00
}
return _c_list_merge(c_list_sort(list, func), c_list_sort(second, func),
func);
2008-05-20 12:06:02 +04:00
}