Files
wget2/libwget/list.c
2018-04-30 20:52:11 +02:00

266 lines
6.8 KiB
C

/*
* Copyright(c) 2012 Tim Ruehsen
* Copyright(c) 2015-2018 Free Software Foundation, Inc.
*
* This file is part of libwget.
*
* Libwget is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libwget 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libwget. If not, see <https://www.gnu.org/licenses/>.
*
*
* Double linked list routines
*
* Changelog
* 25.04.2012 Tim Ruehsen created
*
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <wget.h>
#include "private.h"
/**
* \file
* \brief Double linked list routines
* \defgroup libwget-list Double linked list
* @{
*
* Double linked lists provide fast insertion, removal and
* iteration in either direction.
*
* Each element has pointers to the next and the previous element.<br>
* Iteration can be done by calling the wget_list_browse() function,
* so the list structure doesn't need to be exposed.
*
* This datatype is used by the Wget tool to implement the job queue (append and remove).
*
* See wget_list_append() for an example on how to use lists.
*/
struct _wget_list_st {
wget_list_t
*next,
*prev;
};
/**
* \param[in] list Pointer to Pointer to a double linked list
* \param[in] data Pointer to data to be inserted
* \param[in] size Size of data in bytes
* \return Pointer to the new element
*
* Append an element to the end of the list.<br>
* \p size bytes at \p data will be copied and appended to the list.
*
* A pointer to the new element will be returned.
*
* \note The returned pointer must be freed by wget_list_remove() or implicitly by wget_list_free().
*
* Example:
*
* \code{.c}
* wget_list_t *list = NULL;
* struct mystruct mydata1 = { .x = 1, .y = 25 };
* struct mystruct mydata2 = { .x = 5, .y = 99 };
* struct mystruct *data;
*
* wget_list_append(&list, &mydata1, sizeof(mydata1)); // append mydata1 to list
* wget_list_append(&list, &mydata2, sizeof(mydata2)); // append mydata2 to list
*
* data = wget_list_getfirst(list);
* printf("data=(%d,%d)\n", data->x, data->y); // prints 'data=(1,25)'
*
* wget_list_remove(&list, data);
*
* data = wget_list_getfirst(list);
* printf("data=(%d,%d)\n", data->x, data->y); // prints 'data=(5,99)'
*
* wget_list_free(&list);
* \endcode
*/
void *
wget_list_append(wget_list_t **list, const void *data, size_t size)
{
// allocate space for node and data in one row
wget_list_t *node = wget_malloc(sizeof(wget_list_t) + size);
memcpy(node + 1, data, size);
if (!*list) {
// <*list> is an empty list
*list = node;
node->next = node->prev = node;
} else {
node->next = *list;
node->prev = (*list)->prev;
(*list)->prev->next = node;
(*list)->prev = node;
}
return node + 1;
}
/**
* \param[in] list Pointer to Pointer to a double linked list
* \param[in] data Pointer to data to be inserted
* \param[in] size Size of data in bytes
* \return Pointer to the new element
*
* Insert an entry at the beginning of the list.
* \p size bytes at \p data will be copied and prepended to the list.
*
* A pointer to the new element will be returned.
* It must be freed by wget_list_remove() or implicitely by wget_list_free().
*/
void *wget_list_prepend(wget_list_t **list, const void *data, size_t size)
{
if (!*list) {
return wget_list_append(list, data, size);
} else {
return wget_list_append(&(*list)->prev, data, size);
}
}
/**
* \param[in] list Pointer to Pointer to a double linked list
* \param[in] elem Pointer to a list element returned by wget_list_append() or wget_list_prepend()
*
* Remove an element from the list.
*/
void wget_list_remove(wget_list_t **list, void *elem)
{
wget_list_t *node = ((wget_list_t *)elem) - 1;
if (node->prev == node->next && node == node->prev) {
// removing the last node in the list
if (*list && node == *list)
*list = NULL;
} else {
node->prev->next = node->next;
node->next->prev = node->prev;
if (*list && node == *list)
*list = node->next;
}
xfree(node);
}
/**
* \param[in] list Pointer to a double linked list
* \return Pointer to the first element of the list or %NULL if the list is empty
*
* Get the first element of a list.
*/
void *wget_list_getfirst(const wget_list_t *list)
{
return (void *)(list ? list + 1 : list);
}
/**
* \param[in] list Pointer to a double linked list
* \return Pointer to the last element of the list or %NULL if the list is empty
*
* Get the last element of a list.
*/
void *wget_list_getlast(const wget_list_t *list)
{
return (void *)(list ? list->prev + 1 : list);
}
/**
* \param[in] elem Pointer to an element of a linked list
* \return Pointer to the next element of the list or %NULL if the list is empty
*
* Get the next element of a list.
*/
void *wget_list_getnext(const void *elem)
{
if (elem) {
wget_list_t *node = ((wget_list_t *)elem) - 1;
return node->next + 1;
}
return NULL;
}
/**
* \param[in] list Pointer to a double linked list
* \param[in] browse Pointer to callback function which is called for every element in the list.
* If the callback functions returns a value not equal to zero, browsing is stopped and
* this value will be returned by wget_list_browse.
* \param[in] context The context handle that will be passed to the callback function
* \return The return value of the last call to the browse function or -1 if \p list is NULL (empty)
*
* Iterate through all entries of the \p list and call the function \p browse for each.
*
*
* \code{.c}
* // assume that list contains C strings.
* wget_list_t *list = NULL;
*
* static int print_elem(void *context, const char *elem)
* {
* printf("%s\n",elem);
* return 0;
* }
*
* void dump(WGET_LIST *list)
* {
* wget_list_browse(list, (wget_list_browse_t)print_elem, NULL);
* }
* \endcode
*/
int wget_list_browse(const wget_list_t *list, wget_list_browse_t browse, void *context)
{
if (!list)
return -1;
int ret;
const wget_list_t *end = list->prev, *cur = list;
while ((ret = browse(context, (void *)(cur + 1))) == 0 && cur != end)
cur = cur->next;
return ret;
}
/**
* \param[in] list Pointer to Pointer to a double linked list
*
* Freeing the list and it's entry.
*/
void wget_list_free(wget_list_t **list)
{
while (*list)
wget_list_remove(list, *list + 1);
}
/*
void wget_list_dump(const WGET_LIST *list)
{
if (list) {
const wget_list_t *cur = list;
do {
debug_printf("%p: next %p prev %p\n", cur, cur->next, cur->prev);
cur = cur->next;
} while (cur != list);
} else
debug_printf("empty\n");
}
*/
/**@}*/