added list handling functions (from glib)
authorHolger Schemel <info@artsoft.org>
Fri, 23 Feb 2024 17:19:08 +0000 (18:19 +0100)
committerHolger Schemel <info@artsoft.org>
Fri, 23 Feb 2024 17:22:25 +0000 (18:22 +0100)
src/libgame/Makefile
src/libgame/libgame.h
src/libgame/list.c [new file with mode: 0644]
src/libgame/list.h [new file with mode: 0644]

index 2718b459b7e31b7b007b8c4c69caa19690815df2..54e92ed803b44b201d80c1ef6e4ed807bc97cc2f 100644 (file)
@@ -22,6 +22,7 @@ SRCS =        system.c        \
        image.c         \
        random.c        \
        hash.c          \
+       list.c          \
        http.c          \
        base64.c        \
        setup.c         \
@@ -41,6 +42,7 @@ OBJS =        system.o        \
        image.o         \
        random.o        \
        hash.o          \
+       list.o          \
        http.o          \
        base64.o        \
        setup.o         \
index 2573a635b249052532d0101c8414a65378c2d803..390480d4933e6c6a3a36d09900612b30aedd51a1 100644 (file)
@@ -27,6 +27,8 @@
 #include "setup.h"
 #include "misc.h"
 #include "http.h"
+#include "hash.h"
+#include "list.h"
 #include "base64.h"
 #include "zip/miniunz.h"
 
diff --git a/src/libgame/list.c b/src/libgame/list.c
new file mode 100644 (file)
index 0000000..55c5b35
--- /dev/null
@@ -0,0 +1,775 @@
+// ============================================================================
+// list.c
+// ============================================================================
+
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+
+#include "list.h"
+
+
+/**
+ * List:
+ * @data: holds the element's data, which can be a pointer to any kind
+ *        of data, or any integer value using the
+ *        [Type Conversion Macros][glib-Type-Conversion-Macros]
+ * @next: contains the link to the next element in the list
+ * @prev: contains the link to the previous element in the list
+ *
+ * The #List struct is used for each element in a doubly-linked list.
+ **/
+
+/**
+ * list_previous:
+ * @list: an element in a #List
+ *
+ * A convenience macro to get the previous element in a #List.
+ * Note that it is considered perfectly acceptable to access
+ * @list->prev directly.
+ *
+ * Returns: the previous element, or %NULL if there are no previous
+ *          elements
+ **/
+
+/**
+ * list_next:
+ * @list: an element in a #List
+ *
+ * A convenience macro to get the next element in a #List.
+ * Note that it is considered perfectly acceptable to access
+ * @list->next directly.
+ *
+ * Returns: the next element, or %NULL if there are no more elements
+ **/
+
+/**
+ * list_alloc:
+ *
+ * Allocates space for one #List element. It is called by
+ * list_append(), list_prepend(), list_insert() and
+ * list_insert_sorted() and so is rarely used on its own.
+ *
+ * Returns: a pointer to the newly-allocated #List element
+ **/
+List *
+list_alloc (void)
+{
+  return calloc(1, sizeof(List));
+}
+
+/**
+ * list_free:
+ * @list: the first link of a #List
+ *
+ * Frees all of the memory used by a #List.
+ * The freed elements are returned to the slice allocator.
+ *
+ * If list elements contain dynamically-allocated memory, you should
+ * either use list_free_full() or free them manually first.
+ *
+ * It can be combined with steal_pointer() to ensure the list head pointer
+ * is not left dangling:
+ * |[<!-- language="C" -->
+ * List *list_of_borrowed_things = …;  /<!-- -->* (transfer container) *<!-- -->/
+ * list_free (steal_pointer (&list_of_borrowed_things));
+ * ]|
+ */
+void
+list_free (List *list)
+{
+  void *slice = list;
+  size_t next_offset = offsetof(List, next);
+
+  while (slice)
+  {
+    void *current = slice;
+    slice = *(void**) (current + next_offset);
+    free(current);
+  }
+}
+
+/**
+ * list_free_1:
+ * @list: a #List element
+ *
+ * Frees one #List element, but does not update links from the next and
+ * previous elements in the list, so you should not call this function on an
+ * element that is currently part of a list.
+ *
+ * It is usually used after list_remove_link().
+ */
+/**
+ * list_free1:
+ *
+ * Another name for list_free_1().
+ **/
+void
+list_free_1 (List *list)
+{
+  free(list);
+}
+
+/**
+ * list_append:
+ * @list: a pointer to a #List
+ * @data: the data for the new element
+ *
+ * Adds a new element on to the end of the list.
+ *
+ * Note that the return value is the new start of the list,
+ * if @list was empty; make sure you store the new value.
+ *
+ * list_append() has to traverse the entire list to find the end,
+ * which is inefficient when adding multiple elements. A common idiom
+ * to avoid the inefficiency is to use list_prepend() and reverse
+ * the list with list_reverse() when all elements have been added.
+ *
+ * |[<!-- language="C" -->
+ * // Notice that these are initialized to the empty list.
+ * List *strinlist = NULL, *number_list = NULL;
+ *
+ * // This is a list of strings.
+ * strinlist = list_append (strinlist, "first");
+ * strinlist = list_append (strinlist, "second");
+ *
+ * // This is a list of integers.
+ * number_list = list_append (number_list, INT_TO_PTR (27));
+ * number_list = list_append (number_list, INT_TO_PTR (14));
+ * ]|
+ *
+ * Returns: either @list or the new start of the #List if @list was %NULL
+ */
+List *
+list_append (List *list, void *data)
+{
+  List *new_list;
+  List *last;
+
+  new_list = malloc(sizeof(List));
+  new_list->data = data;
+  new_list->next = NULL;
+
+  if (list)
+  {
+    last = list_last(list);
+    last->next = new_list;
+    new_list->prev = last;
+
+    return list;
+  }
+  else
+  {
+    new_list->prev = NULL;
+
+    return new_list;
+  }
+}
+
+/**
+ * list_prepend:
+ * @list: a pointer to a #List, this must point to the top of the list
+ * @data: the data for the new element
+ *
+ * Prepends a new element on to the start of the list.
+ *
+ * Note that the return value is the new start of the list,
+ * which will have changed, so make sure you store the new value.
+ *
+ * |[<!-- language="C" -->
+ * // Notice that it is initialized to the empty list.
+ * List *list = NULL;
+ *
+ * list = list_prepend (list, "last");
+ * list = list_prepend (list, "first");
+ * ]|
+ *
+ * Do not use this function to prepend a new element to a different
+ * element than the start of the list. Use list_insert_before() instead.
+ *
+ * Returns: a pointer to the newly prepended element, which is the new
+ *     start of the #List
+ */
+List *
+list_prepend (List *list, void *data)
+{
+  List *new_list;
+
+  new_list = malloc(sizeof(List));
+  new_list->data = data;
+  new_list->next = list;
+
+  if (list)
+  {
+    new_list->prev = list->prev;
+    if (list->prev)
+      list->prev->next = new_list;
+    list->prev = new_list;
+  }
+  else
+  {
+    new_list->prev = NULL;
+  }
+
+  return new_list;
+}
+
+/**
+ * list_insert:
+ * @list: a pointer to a #List, this must point to the top of the list
+ * @data: the data for the new element
+ * @position: the position to insert the element. If this is
+ *     negative, or is larger than the number of elements in the
+ *     list, the new element is added on to the end of the list.
+ *
+ * Inserts a new element into the list at the given position.
+ *
+ * Returns: the (possibly changed) start of the #List
+ */
+List *
+list_insert (List *list, void *data, int position)
+{
+  List *new_list;
+  List *tmp_list;
+
+  if (position < 0)
+    return list_append(list, data);
+  else if (position == 0)
+    return list_prepend(list, data);
+
+  tmp_list = list_nth(list, position);
+  if (!tmp_list)
+    return list_append(list, data);
+
+  new_list = malloc(sizeof(List));
+  new_list->data = data;
+  new_list->prev = tmp_list->prev;
+  tmp_list->prev->next = new_list;
+  new_list->next = tmp_list;
+  tmp_list->prev = new_list;
+
+  return list;
+}
+
+static inline List *
+_list_remove_link (List *list, List *link)
+{
+  if (link == NULL)
+    return list;
+
+  if (link->prev)
+  {
+    if (link->prev->next == link)
+      link->prev->next = link->next;
+    else
+      Warn("corrupted double-linked list detected");
+  }
+
+  if (link->next)
+  {
+    if (link->next->prev == link)
+      link->next->prev = link->prev;
+    else
+      Warn("corrupted double-linked list detected");
+  }
+
+  if (link == list)
+    list = list->next;
+
+  link->next = NULL;
+  link->prev = NULL;
+
+  return list;
+}
+
+/**
+ * list_remove:
+ * @list: a #List, this must point to the top of the list
+ * @data: the data of the element to remove
+ *
+ * Removes an element from a #List.
+ * If two elements contain the same data, only the first is removed.
+ * If none of the elements contain the data, the #List is unchanged.
+ *
+ * Returns: the (possibly changed) start of the #List
+ */
+List *
+list_remove (List *list, const void *data)
+{
+  List *tmp;
+
+  tmp = list;
+  while (tmp)
+  {
+    if (tmp->data != data)
+    {
+      tmp = tmp->next;
+    }
+    else
+    {
+      list = _list_remove_link (list, tmp);
+      free (tmp);
+
+      break;
+    }
+  }
+
+  return list;
+}
+
+/**
+ * list_remove_all:
+ * @list: a #List, this must point to the top of the list
+ * @data: data to remove
+ *
+ * Removes all list nodes with data equal to @data.
+ * Returns the new head of the list. Contrast with
+ * list_remove() which removes only the first node
+ * matching the given data.
+ *
+ * Returns: the (possibly changed) start of the #List
+ */
+List *
+list_remove_all (List *list, const void *data)
+{
+  List *tmp = list;
+
+  while (tmp)
+  {
+    if (tmp->data != data)
+    {
+      tmp = tmp->next;
+    }
+    else
+    {
+      List *next = tmp->next;
+
+      if (tmp->prev)
+       tmp->prev->next = next;
+      else
+       list = next;
+
+      if (next)
+       next->prev = tmp->prev;
+
+      free (tmp);
+      tmp = next;
+    }
+  }
+
+  return list;
+}
+
+/**
+ * list_remove_link:
+ * @list: a #List, this must point to the top of the list
+ * @llink: an element in the #List
+ *
+ * Removes an element from a #List, without freeing the element.
+ * The removed element's prev and next links are set to %NULL, so
+ * that it becomes a self-contained list with one element.
+ *
+ * This function is for example used to move an element in the list
+ * (see the example for list_concat()) or to remove an element in
+ * the list before freeing its data:
+ * |[<!-- language="C" -->
+ * list = list_remove_link (list, llink);
+ * free_some_data_that_may_access_the_list_again (llink->data);
+ * list_free (llink);
+ * ]|
+ *
+ * Returns: the (possibly changed) start of the #List
+ */
+List *
+list_remove_link (List *list, List *llink)
+{
+  return _list_remove_link(list, llink);
+}
+
+/**
+ * list_delete_link:
+ * @list: a #List, this must point to the top of the list
+ * @link_: node to delete from @list
+ *
+ * Removes the node link_ from the list and frees it.
+ * Compare this to list_remove_link() which removes the node
+ * without freeing it.
+ *
+ * Returns: the (possibly changed) start of the #List
+ */
+List *
+list_delete_link (List *list, List *link_)
+{
+  list = _list_remove_link(list, link_);
+  free(link_);
+
+  return list;
+}
+
+/**
+ * list_copy:
+ * @list: a #List, this must point to the top of the list
+ *
+ * Copies a #List.
+ *
+ * Note that this is a "shallow" copy. If the list elements
+ * consist of pointers to data, the pointers are copied but
+ * the actual data is not. See list_copy_deep() if you need
+ * to copy the data as well.
+ *
+ * Returns: the start of the new list that holds the same data as @list
+ */
+List *
+list_copy (List *list)
+{
+  return list_copy_deep(list, NULL, NULL);
+}
+
+/**
+ * g_list_copy_deep:
+ * @list: a #List, this must point to the top of the list
+ * @func: (scope call): a copy function used to copy every element in the list
+ * @user_data: user data passed to the copy function @func, or %NULL
+ *
+ * Makes a full (deep) copy of a #List.
+ *
+ * In contrast with g_list_copy(), this function uses @func to make
+ * a copy of each list element, in addition to copying the list
+ * container itself.
+ *
+ * @func, as a #GCopyFunc, takes two arguments, the data to be copied
+ * and a @user_data pointer. On common processor architectures, it's safe to
+ * pass %NULL as @user_data if the copy function takes only one argument. You
+ * may get compiler warnings from this though if compiling with GCC’s
+ * `-Wcast-function-type` warning.
+ *
+ * For instance, if @list holds a list of GObjects, you can do:
+ * |[<!-- language="C" -->
+ * another_list = g_list_copy_deep (list, (GCopyFunc) g_object_ref, NULL);
+ * ]|
+ *
+ * And, to entirely free the new list, you could do:
+ * |[<!-- language="C" -->
+ * g_list_free_full (another_list, g_object_unref);
+ * ]|
+ *
+ * Returns: the start of the new list that holds a full copy of @list,
+ *     use g_list_free_full() to free it
+ *
+ * Since: 2.34
+ */
+List *
+list_copy_deep (List *list, list_copy_fn func, void *user_data)
+{
+  List *new_list = NULL;
+
+  if (list)
+  {
+    List *last;
+
+    new_list = malloc(sizeof(List));
+
+    if (func)
+      new_list->data = func (list->data, user_data);
+    else
+      new_list->data = list->data;
+
+    new_list->prev = NULL;
+    last = new_list;
+    list = list->next;
+
+    while (list)
+    {
+      last->next = malloc(sizeof(List));
+      last->next->prev = last;
+      last = last->next;
+
+      if (func)
+       last->data = func (list->data, user_data);
+      else
+       last->data = list->data;
+
+      list = list->next;
+    }
+
+    last->next = NULL;
+  }
+
+  return new_list;
+}
+
+/**
+ * list_reverse:
+ * @list: a #List, this must point to the top of the list
+ *
+ * Reverses a #List.
+ * It simply switches the next and prev pointers of each element.
+ *
+ * Returns: the start of the reversed #List
+ */
+List *
+list_reverse (List *list)
+{
+  List *last;
+
+  last = NULL;
+
+  while (list)
+  {
+    last = list;
+    list = last->next;
+    last->next = last->prev;
+    last->prev = list;
+  }
+
+  return last;
+}
+
+/**
+ * list_nth:
+ * @list: a #List, this must point to the top of the list
+ * @n: the position of the element, counting from 0
+ *
+ * Gets the element at the given position in a #List.
+ *
+ * This iterates over the list until it reaches the @n-th position. If you
+ * intend to iterate over every element, it is better to use a for-loop as
+ * described in the #List introduction.
+ *
+ * Returns: the element, or %NULL if the position is off
+ *     the end of the #List
+ */
+List *
+list_nth (List *list, unsigned int  n)
+{
+  while ((n-- > 0) && list)
+    list = list->next;
+
+  return list;
+}
+
+/**
+ * list_nth_prev:
+ * @list: a #List
+ * @n: the position of the element, counting from 0
+ *
+ * Gets the element @n places before @list.
+ *
+ * Returns: the element, or %NULL if the position is
+ *     off the end of the #List
+ */
+List *
+list_nth_prev (List *list, unsigned int n)
+{
+  while ((n-- > 0) && list)
+    list = list->prev;
+
+  return list;
+}
+
+/**
+ * list_nth_data:
+ * @list: a #List, this must point to the top of the list
+ * @n: the position of the element
+ *
+ * Gets the data of the element at the given position.
+ *
+ * This iterates over the list until it reaches the @n-th position. If you
+ * intend to iterate over every element, it is better to use a for-loop as
+ * described in the #List introduction.
+ *
+ * Returns: the element's data, or %NULL if the position
+ *     is off the end of the #List
+ */
+void *
+list_nth_data (List *list, unsigned int n)
+{
+  while ((n-- > 0) && list)
+    list = list->next;
+
+  return list ? list->data : NULL;
+}
+
+/**
+ * list_position:
+ * @list: a #List, this must point to the top of the list
+ * @llink: an element in the #List
+ *
+ * Gets the position of the given element
+ * in the #List (starting from 0).
+ *
+ * Returns: the position of the element in the #List,
+ *     or -1 if the element is not found
+ */
+int
+list_position (List *list, List *llink)
+{
+  int i;
+
+  i = 0;
+  while (list)
+  {
+    if (list == llink)
+      return i;
+
+    i++;
+    list = list->next;
+  }
+
+  return -1;
+}
+
+/**
+ * list_index:
+ * @list: a #List, this must point to the top of the list
+ * @data: the data to find
+ *
+ * Gets the position of the element containing
+ * the given data (starting from 0).
+ *
+ * Returns: the index of the element containing the data,
+ *     or -1 if the data is not found
+ */
+int
+list_index (List *list, const void *data)
+{
+  int i;
+
+  i = 0;
+  while (list)
+  {
+    if (list->data == data)
+      return i;
+
+    i++;
+    list = list->next;
+  }
+
+  return -1;
+}
+
+/**
+ * list_last:
+ * @list: any #List element
+ *
+ * Gets the last element in a #List.
+ *
+ * Returns: the last element in the #List,
+ *     or %NULL if the #List has no elements
+ */
+List *
+list_last (List *list)
+{
+  if (list)
+  {
+    while (list->next)
+      list = list->next;
+  }
+
+  return list;
+}
+
+/**
+ * list_first:
+ * @list: any #List element
+ *
+ * Gets the first element in a #List.
+ *
+ * Returns: the first element in the #List,
+ *     or %NULL if the #List has no elements
+ */
+List *
+list_first (List *list)
+{
+  if (list)
+  {
+    while (list->prev)
+      list = list->prev;
+  }
+
+  return list;
+}
+
+/**
+ * list_length:
+ * @list: a #List, this must point to the top of the list
+ *
+ * Gets the number of elements in a #List.
+ *
+ * This function iterates over the whole list to count its elements.
+ * Use a #GQueue instead of a List if you regularly need the number
+ * of items. To check whether the list is non-empty, it is faster to check
+ * @list against %NULL.
+ *
+ * Returns: the number of elements in the #List
+ */
+unsigned int
+list_length (List *list)
+{
+  unsigned int length;
+
+  length = 0;
+  while (list)
+  {
+    length++;
+    list = list->next;
+  }
+
+  return length;
+}
+
+/**
+ * list_foreach:
+ * @list: a #List, this must point to the top of the list
+ * @func: (scope call): the function to call with each element's data
+ * @user_data: user data to pass to the function
+ *
+ * Calls a function for each element of a #List.
+ *
+ * It is safe for @func to remove the element from @list, but it must
+ * not modify any part of the list after that element.
+ */
+/**
+ * list_fn:
+ * @data: the element's data
+ * @user_data: user data passed to list_foreach() or slist_foreach()
+ *
+ * Specifies the type of functions passed to list_foreach() and
+ * slist_foreach().
+ */
+void
+list_foreach (List *list, list_fn func, void *user_data)
+{
+  while (list)
+  {
+    List *next = list->next;
+
+    (*func) (list->data, user_data);
+    list = next;
+  }
+}
diff --git a/src/libgame/list.h b/src/libgame/list.h
new file mode 100644 (file)
index 0000000..2ad694b
--- /dev/null
@@ -0,0 +1,78 @@
+// ============================================================================
+// list.h
+// ============================================================================
+
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+#include "misc.h"
+
+
+typedef struct _List List;
+
+struct _List
+{
+  void *data;
+  List *next;
+  List *prev;
+};
+
+
+typedef void (*list_fn) (void *data, void *userdata);
+typedef void *(*list_copy_fn) (const void *data, void *userdata);
+
+/* Doubly linked lists */
+List *list_alloc(void);
+void  list_free(List *list);
+void  list_free_1(List *list);
+
+List *list_append(List *list, void *data);
+List *list_prepend(List *list, void *data);
+List *list_insert(List *list, void *data, int position);
+List *list_remove(List *list, const void *data);
+List *list_remove_all(List *list, const void *data);
+List *list_remove_link(List *list, List *llink);
+List *list_delete_link(List *list, List *link_);
+List *list_reverse(List *list);
+List *list_copy(List *list);
+List *list_copy_deep(List *list, list_copy_fn func, void *user_data);
+List *list_nth(List *list, unsigned int n);
+List *list_nth_prev(List *list, unsigned int n);
+int   list_position(List *list, List *llink);
+int   list_index(List *list, const void *data);
+List *list_last(List *list);
+List *list_first(List *list);
+unsigned int list_length(List *list);
+void  list_foreach(List *list, list_fn func, void *user_data);
+void *list_nth_data(List *list, unsigned int n);
+
+#define list_previous(list)    ((list) ? (((List *)(list))->prev) : NULL)
+#define list_next(list)                ((list) ? (((List *)(list))->next) : NULL)
+
+#endif