/************************************************************************** * File: lists.c Part of tbaMUD * * Usage: Handling of in-game lists * * * * By Vatiken. Copyright 2012 by Joseph Arnusch * **************************************************************************/ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "db.h" #include "dg_event.h" /* Global lists */ struct list_data * global_lists = NULL; struct list_data * create_list(void) { struct list_data *pNewList; static bool first_list = TRUE; CREATE(pNewList, struct list_data, 1); pNewList->pFirstItem = NULL; pNewList->pLastItem = NULL; pNewList->iIterators = 0; pNewList->iSize = 0; /* Add to global lists, primarily for debugging purposes */ if (first_list == FALSE) add_to_list(pNewList, global_lists); else first_list = FALSE; return (pNewList); } struct item_data * create_item(void) { struct item_data *pNewItem; CREATE(pNewItem, struct item_data, 1); pNewItem->pNextItem = NULL; pNewItem->pPrevItem = NULL; pNewItem->pContent = NULL; return (pNewItem); } void free_list(struct list_data * pList) { void * pContent; simple_list(NULL); if (pList->iSize) while ((pContent = simple_list(pList))) remove_from_list(pContent, pList); if (pList->iSize > 0) mudlog(CMP, LVL_GOD, TRUE, "List being freed while not empty."); /* Global List for debugging */ if (pList != global_lists) remove_from_list(pList, global_lists); free(pList); } void add_to_list(void * pContent, struct list_data * pList) { struct item_data * pNewItem; struct item_data * pLastItem; /* Allocate our memory */ pNewItem = create_item(); /* Place the contents in the item */ pNewItem->pContent = pContent; pNewItem->pNextItem = NULL; /* If we are the first entry in the list, mark us as such */ if (pList->pFirstItem == NULL) pList->pFirstItem = pNewItem; /* Grab our last item from the list and attach it to our new item */ if (pList->pLastItem) { pLastItem = pList->pLastItem; pLastItem->pNextItem = pNewItem; pNewItem->pPrevItem = pLastItem; } /* Make our new item our last item in the list */ pList->pLastItem = pNewItem; pList->iSize++; } void remove_from_list(void * pContent, struct list_data * pList) { struct item_data *pRemovedItem; if ((pRemovedItem = find_in_list(pContent, pList)) == NULL) { mudlog(CMP, LVL_GOD, TRUE, "SYSERR: Attempting to remove contents that don't exist in list."); return; } if (pRemovedItem == pList->pFirstItem) pList->pFirstItem = pRemovedItem->pNextItem; if (pRemovedItem == pList->pLastItem) pList->pLastItem = pRemovedItem->pPrevItem; if (pRemovedItem->pPrevItem) pRemovedItem->pPrevItem->pNextItem = pRemovedItem->pNextItem; if (pRemovedItem->pNextItem) pRemovedItem->pNextItem->pPrevItem = pRemovedItem->pPrevItem; pList->iSize--; if (pList->iSize == 0) { pList->pFirstItem = NULL; pList->pLastItem = NULL; } free(pRemovedItem); } /** Merges an iterator with a list * @post Don't forget to remove the iterator with remove_iterator(). * */ void * merge_iterator(struct iterator_data * pIterator, struct list_data * pList) { void * pContent; if (pList == NULL) { mudlog(NRM, LVL_GOD, TRUE, "SYSERR: Attempting to merge iterator to NULL list."); pIterator->pList = NULL; pIterator->pItem = NULL; return NULL; } if (pList->pFirstItem == NULL) { mudlog(NRM, LVL_GOD, TRUE, "SYSERR: Attempting to merge iterator to empty list."); pIterator->pList = NULL; pIterator->pItem = NULL; return NULL; } pList->iIterators++; pIterator->pList = pList; pIterator->pItem = pList->pFirstItem; pContent = pIterator->pItem ? pIterator->pItem->pContent : NULL; return (pContent); } void remove_iterator(struct iterator_data * pIterator) { if (pIterator->pList == NULL) { mudlog(NRM, LVL_GOD, TRUE, "SYSERR: Attempting to remove iterator from NULL list."); return; } pIterator->pList->iIterators--; pIterator->pList = NULL; pIterator->pItem = NULL; } /** Spits out an item and cycles down the list * @return Returns the content of the list * */ void * next_in_list(struct iterator_data * pIterator) { void * pContent; struct item_data * pTempItem; if (pIterator->pList == NULL) { mudlog(NRM, LVL_GOD, TRUE, "SYSERR: Attempting to get content from iterator with NULL list."); return NULL; } /* Cycle down the list */ pTempItem = pIterator->pItem->pNextItem; pIterator->pItem = pTempItem; /* Grab the content */ pContent = pIterator->pItem ? pIterator->pItem->pContent : NULL; return (pContent); } /** Searches through the a list and returns the item block that holds pContent * @return Returns the actual item block and not the pContent itself, since * it is assumed you already have the pContent. * */ struct item_data * find_in_list(void * pContent, struct list_data * pList) { struct iterator_data Iterator; void * pFoundItem; struct item_data *pItem = NULL; bool found; pFoundItem = merge_iterator(&Iterator, pList); for (found = FALSE; pFoundItem != NULL; pFoundItem = next_in_list(&Iterator)) { if (pFoundItem == pContent) { found = TRUE; break; } } if (found) pItem = Iterator.pItem; remove_iterator(&Iterator); if (found) return (pItem); else return NULL; } /** This is the "For Dummies" function, as although it's not as flexible, * it is even easier applied for list searches then using your own iterators * and next_in_list() * @usage Common usage would be as follows: * * while ((var = (struct XXX_data *) simple_list(XXX_list))) { * blah blah.... * } * @return Will return the next list content until it hits the end, in which * will detach itself from the list. * */ void * simple_list(struct list_data * pList) { static struct iterator_data Iterator; static bool loop = FALSE; static struct list_data *pLastList = NULL; void * pContent; /* Reset List */ if (pList == NULL) { loop = FALSE; pLastList = NULL; return NULL; } if (!loop || pLastList != pList) { if (loop && pLastList != pList) mudlog(CMP, LVL_GRGOD, TRUE, "SYSERR: simple_list() forced to reset itself."); pContent = merge_iterator(&Iterator, pList); if (pContent != NULL) { pLastList = pList; loop = TRUE; return (pContent); } else return NULL; } if ((pContent = next_in_list(&Iterator)) != NULL) return (pContent); remove_iterator(&Iterator); loop = FALSE; return NULL; } void * random_from_list(struct list_data * pList) { struct iterator_data Iterator; void * pFoundItem; bool found; int number; int count = 1; if (pList->iSize <= 0) return NULL; else number = rand_number(1, pList->iSize); pFoundItem = merge_iterator(&Iterator, pList); for (found = FALSE; pFoundItem != NULL; pFoundItem = next_in_list(&Iterator), count++) { if (count == number) { found = TRUE; break; } } remove_iterator(&Iterator); if (found) return (pFoundItem); else return NULL; } struct list_data * randomize_list(struct list_data * pList) { struct list_data * newList; void * pContent; if (pList->iSize == 0) return NULL; newList = create_list(); while ((pContent = random_from_list(pList)) != NULL) { remove_from_list(pContent, pList); add_to_list(pContent, newList); } free_list(pList); return (newList); }