mirror of
https://github.com/nextcloud/desktop.git
synced 2024-12-14 01:34:22 +03:00
461 lines
8.3 KiB
C
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);
|
|
}
|
|
|