alpm_list.c

Go to the documentation of this file.
00001 /*
00002  *  alpm_list.c
00003  *
00004  *  Copyright (c) 2002-2007 by Judd Vinet <jvinet@zeroflux.org>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include "config.h"
00021 
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <stdio.h>
00025 
00026 /* libalpm */
00027 #include "alpm_list.h"
00028 #include "util.h"
00029 
00030 /**
00031  * @addtogroup alpm_list List Functions
00032  * @brief Functions to manipulate alpm_list_t lists.
00033  *
00034  * These functions are designed to create, destroy, and modify lists of
00035  * type alpm_list_t. This is an internal list type used by libalpm that is
00036  * publicly exposed for use by frontends if desired.
00037  *
00038  * @{ */
00039 
00040 /* Allocation */
00041 
00042 /**
00043  * @brief Allocate a new alpm_list_t.
00044  *
00045  * @return a new alpm_list_t item, or NULL on failure
00046  */
00047 alpm_list_t SYMEXPORT *alpm_list_new()
00048 {
00049     alpm_list_t *list = NULL;
00050 
00051     list = malloc(sizeof(alpm_list_t));
00052     if(list) {
00053         list->data = NULL;
00054         list->prev = list; /* maintain a back reference to the tail pointer */
00055         list->next = NULL;
00056     }
00057 
00058     return(list);
00059 }
00060 
00061 /**
00062  * @brief Free a list, but not the contained data.
00063  *
00064  * @param list the list to free
00065  */
00066 void SYMEXPORT alpm_list_free(alpm_list_t *list)
00067 {
00068     alpm_list_t *it = list;
00069 
00070     while(it) {
00071         alpm_list_t *tmp = it->next;
00072         free(it);
00073         it = tmp;
00074     }
00075 }
00076 
00077 /**
00078  * @brief Free the internal data of a list structure.
00079  *
00080  * @param list the list to free
00081  * @param fn   a free function for the internal data
00082  */
00083 void SYMEXPORT alpm_list_free_inner(alpm_list_t *list, alpm_list_fn_free fn)
00084 {
00085     alpm_list_t *it = list;
00086 
00087     while(it) {
00088         if(fn && it->data) {
00089             fn(it->data);
00090         }
00091         it = it->next;
00092     }
00093 }
00094 
00095 
00096 /* Mutators */
00097 
00098 /**
00099  * @brief Add a new item to the end of the list.
00100  *
00101  * @param list the list to add to
00102  * @param data the new item to be added to the list
00103  *
00104  * @return the resultant list
00105  */
00106 alpm_list_t SYMEXPORT *alpm_list_add(alpm_list_t *list, void *data)
00107 {
00108     alpm_list_t *ptr, *lp;
00109 
00110     ptr = list;
00111     if(ptr == NULL) {
00112         ptr = alpm_list_new();
00113         if(ptr == NULL) {
00114             return(NULL);
00115         }
00116     }
00117 
00118     lp = alpm_list_last(ptr);
00119     if(lp == ptr && lp->data == NULL) {
00120         /* nada */
00121     } else {
00122         lp->next = alpm_list_new();
00123         if(lp->next == NULL) {
00124             return(NULL);
00125         }
00126         lp->next->prev = lp;
00127         lp = lp->next;
00128         list->prev = lp;
00129     }
00130 
00131     lp->data = data;
00132 
00133     return(ptr);
00134 }
00135 
00136 /**
00137  * @brief Add items to a list in sorted order.
00138  *
00139  * @param list the list to add to
00140  * @param data the new item to be added to the list
00141  * @param fn   the comparison function to use to determine order
00142  *
00143  * @return the resultant list
00144  */
00145 alpm_list_t SYMEXPORT *alpm_list_add_sorted(alpm_list_t *list, void *data, alpm_list_fn_cmp fn)
00146 {
00147     if(!fn) {
00148         return alpm_list_add(list, data);
00149     } else {
00150         alpm_list_t *add = NULL, *prev = NULL, *next = list;
00151 
00152         add = alpm_list_new();
00153         add->data = data;
00154 
00155         /* Find insertion point. */
00156         while(next) {
00157             if(fn(add->data, next->data) <= 0) break;
00158             prev = next;
00159             next = next->next;
00160         }
00161 
00162         /*  Insert node before insertion point. */
00163         add->prev = prev;
00164         add->next = next;
00165 
00166         if(next != NULL) {
00167             next->prev = add;   /*  Not at end.  */
00168         }
00169 
00170         if(prev != NULL) {
00171             prev->next = add;       /*  In middle.  */
00172         } else {
00173             list = add; /* At beginning, or new list */
00174         }
00175 
00176         if(next == NULL) {
00177             /* At end, adjust tail pointer on head node */
00178             list->prev = add;
00179         }
00180 
00181         return(list);
00182     }
00183 }
00184 
00185 /**
00186  * @brief Join two lists.
00187  * The two lists must be independent. Do not free the original lists after
00188  * calling this function, as this is not a copy operation. The list pointers
00189  * passed in should be considered invalid after calling this function.
00190  *
00191  * @param first  the first list
00192  * @param second the second list
00193  *
00194  * @return the resultant joined list
00195  */
00196 alpm_list_t SYMEXPORT *alpm_list_join(alpm_list_t *first, alpm_list_t *second)
00197 {
00198     alpm_list_t *tmp;
00199 
00200     if (first == NULL) {
00201         return second;
00202     }
00203     if (second == NULL) {
00204         return first;
00205     }
00206     /* tmp is the last element of the first list */
00207     tmp = first->prev;
00208     /* link the first list to the second */
00209     tmp->next = second;
00210     /* link the second list to the first */
00211     first->prev = second->prev;
00212     /* set the back reference to the tail */
00213     second->prev = tmp;
00214 
00215     return(first);
00216 }
00217 
00218 /**
00219  * @brief Merge the two sorted sublists into one sorted list.
00220  *
00221  * @param left  the first list
00222  * @param right the second list
00223  * @param fn    comparison function for determining merge order
00224  *
00225  * @return the resultant list
00226  */
00227 alpm_list_t SYMEXPORT *alpm_list_mmerge(alpm_list_t *left, alpm_list_t *right, alpm_list_fn_cmp fn)
00228 {
00229     alpm_list_t *newlist, *lp;
00230 
00231     if (left == NULL)
00232         return right;
00233     if (right == NULL)
00234         return left;
00235 
00236     if (fn(left->data, right->data) <= 0) {
00237         newlist = left;
00238         left = left->next;
00239     }
00240     else {
00241         newlist = right;
00242         right = right->next;
00243     }
00244     newlist->prev = NULL;
00245     newlist->next = NULL;
00246     lp = newlist;
00247 
00248     while ((left != NULL) && (right != NULL)) {
00249         if (fn(left->data, right->data) <= 0) {
00250             lp->next = left;
00251             left->prev = lp;
00252             left = left->next;
00253         }
00254         else {
00255             lp->next = right;
00256             right->prev = lp;
00257             right = right->next;
00258         }
00259         lp = lp->next;
00260         lp->next = NULL;
00261     }
00262     if (left != NULL) {
00263         lp->next = left;
00264         left->prev = lp;
00265     }
00266     else if (right != NULL) {
00267         lp->next = right;
00268         right->prev = lp;
00269     }
00270 
00271     /* Find our tail pointer
00272      * TODO maintain this in the algorithm itself */
00273     lp = newlist;
00274     while(lp && lp->next) {
00275         lp = lp->next;
00276     }
00277     newlist->prev = lp;
00278 
00279     return(newlist);
00280 }
00281 
00282 /**
00283  * @brief Sort a list of size `n` using mergesort algorithm.
00284  *
00285  * @param list the list to sort
00286  * @param n    the size of the list
00287  * @param fn   the comparison function for determining order
00288  *
00289  * @return the resultant list
00290  */
00291 alpm_list_t SYMEXPORT *alpm_list_msort(alpm_list_t *list, int n, alpm_list_fn_cmp fn)
00292 {
00293     if (n > 1) {
00294         alpm_list_t *left = list;
00295         alpm_list_t *lastleft = alpm_list_nth(list, n/2 - 1);
00296         alpm_list_t *right = lastleft->next;
00297         /* terminate first list */
00298         lastleft->next = NULL;
00299 
00300         left = alpm_list_msort(left, n/2, fn);
00301         right = alpm_list_msort(right, n - (n/2), fn);
00302         list = alpm_list_mmerge(left, right, fn);
00303     }
00304     return(list);
00305 }
00306 
00307 /**
00308  * @brief Remove an item from the list.
00309  *
00310  * @param haystack the list to remove the item from
00311  * @param needle   the data member of the item we're removing
00312  * @param fn       the comparison function for searching
00313  * @param data     output parameter containing data of the removed item
00314  *
00315  * @return the resultant list
00316  */
00317 alpm_list_t SYMEXPORT *alpm_list_remove(alpm_list_t *haystack, const void *needle, alpm_list_fn_cmp fn, void **data)
00318 {
00319     alpm_list_t *i = haystack, *tmp = NULL;
00320 
00321     if(data) {
00322         *data = NULL;
00323     }
00324 
00325     while(i) {
00326         if(i->data == NULL) {
00327             continue;
00328         }
00329         tmp = i->next;
00330         if(fn(needle, i->data) == 0) {
00331             /* we found a matching item */
00332             if(i == haystack) {
00333                 /* Special case: removing the head node which has a back reference to
00334                  * the tail node */
00335                 haystack = i->next;
00336                 if(haystack) {
00337                     haystack->prev = i->prev;
00338                 }
00339                 i->prev = NULL;
00340             } else if(i == haystack->prev) {
00341                 /* Special case: removing the tail node, so we need to fix the back
00342                  * reference on the head node. We also know tail != head. */
00343                 if(i->prev) {
00344                     /* i->next should always be null */
00345                     i->prev->next = i->next;
00346                     haystack->prev = i->prev;
00347                     i->prev = NULL;
00348                 }
00349             } else {
00350                 /* Normal case, non-head and non-tail node */
00351                 if(i->next) {
00352                     i->next->prev = i->prev;
00353                 }
00354                 if(i->prev) {
00355                     i->prev->next = i->next;
00356                 }
00357             }
00358 
00359             if(data) {
00360                 *data = i->data;
00361             }
00362             i->data = NULL;
00363             free(i);
00364             i = NULL;
00365         } else {
00366             i = tmp;
00367         }
00368     }
00369 
00370     return(haystack);
00371 }
00372 
00373 /**
00374  * @brief Create a new list without any duplicates.
00375  *
00376  * This does NOT copy data members.
00377  *
00378  * @param list the list to copy
00379  *
00380  * @return a new list containing non-duplicate items
00381  */
00382 alpm_list_t SYMEXPORT *alpm_list_remove_dupes(const alpm_list_t *list)
00383 {
00384     const alpm_list_t *lp = list;
00385     alpm_list_t *newlist = NULL;
00386     while(lp) {
00387         if(!alpm_list_find_ptr(newlist, lp->data)) {
00388             newlist = alpm_list_add(newlist, lp->data);
00389         }
00390         lp = lp->next;
00391     }
00392     return(newlist);
00393 }
00394 
00395 /**
00396  * @brief Copy a string list, including data.
00397  *
00398  * @param list the list to copy
00399  *
00400  * @return a copy of the original list
00401  */
00402 alpm_list_t SYMEXPORT *alpm_list_strdup(const alpm_list_t *list)
00403 {
00404     const alpm_list_t *lp = list;
00405     alpm_list_t *newlist = NULL;
00406     while(lp) {
00407         newlist = alpm_list_add(newlist, strdup(lp->data));
00408         lp = lp->next;
00409     }
00410     return(newlist);
00411 }
00412 
00413 /**
00414  * @brief Copy a list, without copying data.
00415  *
00416  * @param list the list to copy
00417  *
00418  * @return a copy of the original list
00419  */
00420 alpm_list_t SYMEXPORT *alpm_list_copy(const alpm_list_t *list)
00421 {
00422     const alpm_list_t *lp = list;
00423     alpm_list_t *newlist = NULL;
00424     while(lp) {
00425         newlist = alpm_list_add(newlist, lp->data);
00426         lp = lp->next;
00427     }
00428     return(newlist);
00429 }
00430 
00431 /**
00432  * @brief Copy a list and copy the data.
00433  * Note that the data elements to be copied should not contain pointers
00434  * and should also be of constant size.
00435  *
00436  * @param list the list to copy
00437  * @param size the size of each data element
00438  *
00439  * @return a copy of the original list, data copied as well
00440  */
00441 alpm_list_t SYMEXPORT *alpm_list_copy_data(const alpm_list_t *list,
00442         size_t size)
00443 {
00444     const alpm_list_t *lp = list;
00445     alpm_list_t *newlist = NULL;
00446     while(lp) {
00447         void *newdata = calloc(1, size);
00448         if(newdata) {
00449             memcpy(newdata, lp->data, size);
00450             newlist = alpm_list_add(newlist, newdata);
00451             lp = lp->next;
00452         }
00453     }
00454     return(newlist);
00455 }
00456 
00457 /**
00458  * @brief Create a new list in reverse order.
00459  *
00460  * @param list the list to copy
00461  *
00462  * @return a new list in reverse order
00463  */
00464 alpm_list_t SYMEXPORT *alpm_list_reverse(alpm_list_t *list)
00465 {
00466     const alpm_list_t *lp;
00467     alpm_list_t *newlist = NULL;
00468 
00469     lp = alpm_list_last(list);
00470     if(list) {
00471         /* break our reverse circular list */
00472         list->prev = NULL;
00473     }
00474 
00475     while(lp) {
00476         newlist = alpm_list_add(newlist, lp->data);
00477         lp = lp->prev;
00478     }
00479     return(newlist);
00480 }
00481 
00482 /* Accessors */
00483 
00484 /**
00485  * @brief Get the first element of a list.
00486  *
00487  * @param list the list
00488  *
00489  * @return the first element in the list
00490  */
00491 inline alpm_list_t SYMEXPORT *alpm_list_first(const alpm_list_t *list)
00492 {
00493     return((alpm_list_t*)list);
00494 }
00495 
00496 /**
00497  * @brief Return nth element from list (starting from 0).
00498  *
00499  * @param list the list
00500  * @param n    the index of the item to find
00501  *
00502  * @return an alpm_list_t node for index `n`
00503  */
00504 alpm_list_t SYMEXPORT *alpm_list_nth(const alpm_list_t *list, int n)
00505 {
00506     const alpm_list_t *i = list;
00507     while(n--) {
00508         i = i->next;
00509     }
00510     return((alpm_list_t*)i);
00511 }
00512 
00513 /**
00514  * @brief Get the next element of a list.
00515  *
00516  * @param node the list node
00517  *
00518  * @return the next element, or NULL when no more elements exist
00519  */
00520 inline alpm_list_t SYMEXPORT *alpm_list_next(const alpm_list_t *node)
00521 {
00522     return(node->next);
00523 }
00524 
00525 /**
00526  * @brief Get the last item in the list.
00527  *
00528  * @param list the list
00529  *
00530  * @return the last element in the list
00531  */
00532 alpm_list_t SYMEXPORT *alpm_list_last(const alpm_list_t *list)
00533 {
00534     if(list) {
00535         return(list->prev);
00536     } else {
00537         return(NULL);
00538     }
00539 }
00540 
00541 /**
00542  * @brief Get the data member of a list node.
00543  *
00544  * @param node the list node
00545  *
00546  * @return the contained data, or NULL if none
00547  */
00548 void SYMEXPORT *alpm_list_getdata(const alpm_list_t *node)
00549 {
00550     if(node == NULL) return(NULL);
00551     return(node->data);
00552 }
00553 
00554 /* Misc */
00555 
00556 /**
00557  * @brief Get the number of items in a list.
00558  *
00559  * @param list the list
00560  *
00561  * @return the number of list items
00562  */
00563 int SYMEXPORT alpm_list_count(const alpm_list_t *list)
00564 {
00565     unsigned int i = 0;
00566     const alpm_list_t *lp = list;
00567     while(lp) {
00568         ++i;
00569         lp = lp->next;
00570     }
00571     return(i);
00572 }
00573 
00574 /**
00575  * @brief Find an item in a list.
00576  *
00577  * @param needle   the item to search
00578  * @param haystack the list
00579  * @param fn       the comparison function for searching (!= NULL)
00580  *
00581  * @return `needle` if found, NULL otherwise
00582  */
00583 void SYMEXPORT *alpm_list_find(const alpm_list_t *haystack, const void *needle,
00584         alpm_list_fn_cmp fn)
00585 {
00586     const alpm_list_t *lp = haystack;
00587     while(lp) {
00588         if(lp->data && fn(lp->data, needle) == 0) {
00589             return(lp->data);
00590         }
00591         lp = lp->next;
00592     }
00593     return(NULL);
00594 }
00595 
00596 /* trivial helper function for alpm_list_find_ptr */
00597 static int ptrcmp(const void *p, const void *q)
00598 {
00599     return(p != q);
00600 }
00601 
00602 /**
00603  * @brief Find an item in a list.
00604  *
00605  * Search for the item whos data matches that of the `needle`.
00606  *
00607  * @param needle   the data to search for (== comparison)
00608  * @param haystack the list
00609  *
00610  * @return `needle` if found, NULL otherwise
00611  */
00612 void SYMEXPORT *alpm_list_find_ptr(const alpm_list_t *haystack, const void *needle)
00613 {
00614     return(alpm_list_find(haystack, needle, ptrcmp));
00615 }
00616 
00617 /**
00618  * @brief Find a string in a list.
00619  *
00620  * @param needle   the string to search for
00621  * @param haystack the list
00622  *
00623  * @return `needle` if found, NULL otherwise
00624  */
00625 char SYMEXPORT *alpm_list_find_str(const alpm_list_t *haystack, const char *needle)
00626 {
00627     return((char *)alpm_list_find(haystack, (const void*)needle, (alpm_list_fn_cmp)strcmp));
00628 }
00629 
00630 /**
00631  * @brief Find the items in list `lhs` that are not present in list `rhs`.
00632  *
00633  * Entries are not duplicated. Operation is O(m*n). The first list is stepped
00634  * through one node at a time, and for each node in the first list, each node
00635  * in the second list is compared to it.
00636  *
00637  * @param lhs the first list
00638  * @param rhs the second list
00639  * @param fn  the comparison function
00640  *
00641  * @return a list containing all items in `lhs` not present in `rhs`
00642  */
00643 alpm_list_t SYMEXPORT *alpm_list_diff(const alpm_list_t *lhs,
00644         const alpm_list_t *rhs, alpm_list_fn_cmp fn)
00645 {
00646     const alpm_list_t *i, *j;
00647     alpm_list_t *ret = NULL;
00648     for(i = lhs; i; i = i->next) {
00649         int found = 0;
00650         for(j = rhs; j; j = j->next) {
00651             if(fn(i->data, j->data) == 0) {
00652                 found = 1;
00653                 break;
00654             }
00655         }
00656         if(!found) {
00657             ret = alpm_list_add(ret, i->data);
00658         }
00659     }
00660 
00661     return(ret);
00662 }
00663 
00664 /** @} */
00665 
00666 /* vim: set ts=2 sw=2 noet: */

Generated on Mon Jan 14 23:53:39 2008 for libalpm by  doxygen 1.5.4