+/*
+
+ silclist.h
+
+ Author: Timo Sirainen <tss@iki.fi>
+
+ Copyright (C) 2002 Timo Sirainen
+
+ 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.
+
+*/
+
+/****h* silcutil/SilcList
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the SilcList interface. This interface provides
+ * simple linked list.
+ *
+ ***/
+
+#ifndef SILCLIST_H
+#define SILCLIST_H
+
+/****s* silcutil/SilcList/SilcList
+ *
+ * NAME
+ *
+ * typedef struct { ... } SilcList;
+ *
+ * DESCRIPTION
+ *
+ * This is the SilcList context, and is initialized by calling the
+ * function silc_list_init.
+ *
+ * EXAMPLE
+ *
+ * SilcList list;
+ * silc_list_init(list, struct SilcInternalEntryStruct, next);
+ *
+ ***/
+typedef struct {
+ void *head, *tail;
+ void *current;
+ int offset;
+} SilcList;
+
+/****d* silcutil/SilcList/SILC_LIST_END
+ *
+ * NAME
+ *
+ * #define SILC_LIST_END ...
+ *
+ * DESCRIPTION
+ *
+ * Functions return this when the list is invalid or when traversing
+ * the list there is no more entires in the list.
+ *
+ * SOURCE
+ */
+#define SILC_LIST_END NULL
+/***/
+
+/* Initializes SilcList object. Example:
+
+ SilcList list;
+
+ silc_list_init(list, struct SilcInternalEntryStruct, next);
+
+ Where `list' is the defined list, and second argument is the structure
+ of the entries in the list and last argument is the pointer in the entry
+ structure that is used as list member. SilcInternalEntry might be as
+ follows:
+
+ struct SilcInternalEntryStruct {
+ char *dummy;
+ struct SilcInternalEntryStruct *next; // The list member pointer
+ };
+
+ The `next' pointer in the structure (or some other pointer for that matter)
+ is given for the silc_list_init as the last argument. This pointer is used
+ by the list routines to link the entries together in the list. Your code
+ should not touch the member pointer manually.
+*/
+
+/****f* silcutil/SilcList/silc_list_init
+ *
+ * SYNOPSIS
+ *
+ * #define silc_list_init(list, type, field) ...
+ *
+ * DESCRIPTION
+ *
+ * This macro initializes the SilcList list. The `list' is the defined
+ * list, second argument is the structure of the entries in the list,
+ * and last argument is the pointer in the entry structure that is used
+ * as list member. When using SilcList, you should not touch the
+ * structure member pointer (the `next' for example) manually.
+ *
+ * EXAMPLE
+ *
+ * struct SilcInternalEntryStruct {
+ * char *dummy;
+ * struct SilcInternalEntryStruct *next; // The list member pointer
+ * };
+ *
+ * SilcList list;
+ * silc_list_init(list, struct SilcInternalEntryStruct, next);
+ *
+ ***/
+#define silc_list_init(list, type, field) \
+ __silc_list_init(&(list), offsetof(type, field))
+
+static inline void __silc_list_init(SilcList *list, int offset)
+{
+ list->head = list->tail = list->current = SILC_LIST_END;
+ list->offset = offset;
+}
+
+#define list_next(list, pos) ((void **) ((char *) (pos) + (list)->offset))
+
+/****f* silcutil/SilcList/silc_list_count
+ *
+ * SYNOPSIS
+ *
+ * #define silc_list_count(list) ...
+ *
+ * DESCRIPTION
+ *
+ * Returns the number of entries in the list indicated by `list'.
+ *
+ ***/
+#define silc_list_count(list) __silc_list_count(&(list))
+static inline int __silc_list_count(SilcList *list)
+{
+ int count = 0;
+ void *pos;
+
+ for (pos = list->head; pos != NULL; pos = *list_next(list, pos))
+ count++;
+
+ return count;
+}
+
+/****f* silcutil/SilcList/silc_list_start
+ *
+ * SYNOPSIS
+ *
+ * #define silc_list_start(list) ...
+ *
+ * DESCRIPTION
+ *
+ * Sets the start of the list. This prepares the list for traversing
+ * the entries from the start of the list.
+ *
+ ***/
+#define silc_list_start(list) (list).current = (list).head;
+
+/****f* silcutil/SilcList/silc_list_add
+ *
+ * SYNOPSIS
+ *
+ * #define silc_list_add(list, entry) ...
+ *
+ * DESCRIPTION
+ *
+ * Adds new entry indicated by `entry' to the end of the list indicated
+ * by `list'.
+ *
+ ***/
+#define silc_list_add(list, entry) __silc_list_add(&(list), entry)
+static inline void __silc_list_add(SilcList *list, void *data)
+{
+ if (list->head == NULL)
+ list->head = data;
+ else
+ *list_next(list, list->tail) = data;
+
+ list->tail = data;
+ *list_next(list, data) = NULL;
+}
+
+/****f* silcutil/SilcList/silc_list_del
+ *
+ * SYNOPSIS
+ *
+ * #define silc_list_del(list, entry) ...
+ *
+ * DESCRIPTION
+ *
+ * Remove entry indicated by `entry' from the list indicated by `list'.
+ *
+ ***/
+#define silc_list_del(list, data) __silc_list_del(&(list), data)
+static inline void __silc_list_del(SilcList *list, void *data)
+{
+ void **pos, *prev;
+
+ prev = NULL;
+ for (pos = &list->head; *pos != NULL; pos = list_next(list, *pos)) {
+ if (*pos == data) {
+ *pos = *list_next(list, data);
+ if (list->current == data)
+ list->current = *pos;
+ break;
+ }
+
+ prev = *pos;
+ }
+
+ if (data == list->tail)
+ list->tail = prev;
+}
+
+/****f* silcutil/SilcList/silc_list_get
+ *
+ * SYNOPSIS
+ *
+ * #define silc_list_get(list, entry) ...
+ *
+ * DESCRIPTION
+ *
+ * Returns the current entry from the list indicated by `list' and
+ * moves the list pointer forward so that calling this next time will
+ * return the next entry from the list. This can be used to traverse
+ * the list. Returns SILC_LIST_END when the entire list has been
+ * tarversed and no additional entries exist in the list. Later,
+ * silc_list_start must be called again when re-starting the list
+ * tarversing.
+ *
+ * EXAMPLE
+ *
+ * // Traverse the list from the beginning to the end
+ * silc_list_start(list)
+ * while ((entry = silc_list_get(list)) != SILC_LIST_END) {
+ * ...
+ * }
+ *
+ ***/
+#define silc_list_get(x) __silc_list_get(&(x))
+
+static inline
+void *__silc_list_get(SilcList *list)
+{
+ void *pos;
+
+ pos = list->current;
+ if (pos != NULL)
+ list->current = *list_next(list, pos);
+ return pos;
+}
+
+#endif