+/*********************************************************************
+ *
+ * Function : list_init
+ *
+ * Description : Create a new, empty list in user-allocated memory.
+ * Caller should allocate a "struct list" variable,
+ * then pass it to this function.
+ * (Implementation note: Rather than calling this
+ * function, you can also just memset the memory to
+ * zero, e.g. if you have a larger structure you
+ * want to initialize quickly. However, that isn't
+ * really good design.)
+ *
+ * Parameters :
+ * 1 : the_list = pointer to list
+ *
+ * Returns : N/A
+ *
+ *********************************************************************/
+void init_list(struct list *the_list)
+{
+ memset(the_list, '\0', sizeof(*the_list));
+}
+
+
+/*********************************************************************
+ *
+ * Function : destroy_list
+ *
+ * Description : Destroy a string list (opposite of list_init).
+ * On return, the memory used by the list entries has
+ * been freed, but not the memory used by the_list
+ * itself. You should not re-use the_list without
+ * calling list_init().
+ *
+ * (Implementation note: You *can* reuse the_list
+ * without calling list_init(), but please don't.
+ * If you want to remove all entries from a list
+ * and still have a usable list, then use
+ * list_remove_all().)
+ *
+ * Parameters :
+ * 1 : the_list = pointer to list
+ *
+ * Returns : N/A
+ *
+ *********************************************************************/
+void destroy_list (struct list *the_list)
+{
+ struct list_entry *cur_entry, *next_entry;
+
+ assert(the_list);
+
+ for (cur_entry = the_list->first; cur_entry ; cur_entry = next_entry)
+ {
+ next_entry = cur_entry->next;
+ freez((char *)cur_entry->str);
+ free(cur_entry);
+ }
+
+ the_list->first = NULL;
+ the_list->last = NULL;
+}
+
+
+/*********************************************************************
+ *
+ * Function : list_is_valid
+ *
+ * Description : Check that a string list is valid. The intended
+ * usage is "assert(list_is_valid(the_list))".
+ * Currently this checks that "the_list->last"
+ * is correct, and that the list dosn't contain
+ * circular references. It is likely to crash if
+ * it's passed complete garbage.
+ *
+ * Parameters :
+ * 1 : the_list = pointer to list. Must be non-null.
+ *
+ * Returns : 1 if list is valid, 0 otherwise.
+ *
+ *********************************************************************/
+static int list_is_valid (const struct list *the_list)
+{
+ /*
+ * If you don't want this check, just change the line below
+ * from "#if 1" to "#if 0".
+ */
+#if 1
+ const struct list_entry *cur_entry;
+ const struct list_entry *last_entry = NULL;
+ int length = 0;
+
+ assert(the_list);
+
+ for (cur_entry = the_list->first; cur_entry ; cur_entry = cur_entry->next)
+ {
+ last_entry = cur_entry;
+
+ if (cur_entry->str)
+ {
+ /*
+ * Just check that this string can be accessed - i.e. it's a valid
+ * pointer.
+ */
+ strlen(cur_entry->str);
+ }
+
+ /*
+ * Check for looping back to first
+ */
+ if ((length != 0) && (cur_entry == the_list->first))
+ {
+ return 0;
+ }
+
+ /*
+ * Arbitrarily limit length to prevent infinite loops.
+ */
+ if (++length > 1000)
+ {
+ return 0;
+ }
+
+ /*
+ * Check this isn't marked as the last entry, unless of course it's
+ * *really* the last entry.
+ */
+ if ((the_list->last == cur_entry) && (cur_entry->next != NULL))
+ {
+ /* This is the last entry, but there's data after it !!?? */
+ return 0;
+ }
+ }
+
+ return (the_list->last == last_entry);
+#else
+ return 1;
+#endif
+}
+