1 /*
2  *  alpm_list.c
3  *
4  *  Copyright (c) 2006-2018 Pacman Development Team <pacman-dev@archlinux.org>
5  *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 /* Note: alpm_list.{c,h} are intended to be standalone files. Do not include
25  * any other libalpm headers.
26  */
27 
28 /* libalpm */
29 #include "alpm_list.h"
30 
31 /* check exported library symbols with: nm -C -D <lib> */
32 #define SYMEXPORT __attribute__((visibility("default")))
33 #define SYMHIDDEN __attribute__((visibility("internal")))
34 
35 /**
36  * @addtogroup alpm_list List Functions
37  * @brief Functions to manipulate alpm_list_t lists.
38  *
39  * These functions are designed to create, destroy, and modify lists of
40  * type alpm_list_t. This is an internal list type used by libalpm that is
41  * publicly exposed for use by frontends if desired.
42  *
43  * @{ */
44 
45 /* Allocation */
46 
47 /**
48  * @brief Free a list, but not the contained data.
49  *
50  * @param list the list to free
51  */
alpm_list_free(alpm_list_t * list)52 void SYMEXPORT alpm_list_free(alpm_list_t *list)
53 {
54 	alpm_list_t *it = list;
55 
56 	while(it) {
57 		alpm_list_t *tmp = it->next;
58 		free(it);
59 		it = tmp;
60 	}
61 }
62 
63 /**
64  * @brief Free the internal data of a list structure.
65  *
66  * @param list the list to free
67  * @param fn   a free function for the internal data
68  */
alpm_list_free_inner(alpm_list_t * list,alpm_list_fn_free fn)69 void SYMEXPORT alpm_list_free_inner(alpm_list_t *list, alpm_list_fn_free fn)
70 {
71 	alpm_list_t *it = list;
72 
73 	if(fn) {
74 		while(it) {
75 			if(it->data) {
76 				fn(it->data);
77 			}
78 			it = it->next;
79 		}
80 	}
81 }
82 
83 
84 /* Mutators */
85 
86 /**
87  * @brief Add a new item to the end of the list.
88  *
89  * @param list the list to add to
90  * @param data the new item to be added to the list
91  *
92  * @return the resultant list
93  */
alpm_list_add(alpm_list_t * list,void * data)94 alpm_list_t SYMEXPORT *alpm_list_add(alpm_list_t *list, void *data)
95 {
96 	alpm_list_append(&list, data);
97 	return list;
98 }
99 
100 /**
101  * @brief Add a new item to the end of the list.
102  *
103  * @param list the list to add to
104  * @param data the new item to be added to the list
105  *
106  * @return the newly added item
107  */
alpm_list_append(alpm_list_t ** list,void * data)108 alpm_list_t SYMEXPORT *alpm_list_append(alpm_list_t **list, void *data)
109 {
110 	alpm_list_t *ptr;
111 
112 	ptr = malloc(sizeof(alpm_list_t));
113 	if(ptr == NULL) {
114 		return NULL;
115 	}
116 
117 	ptr->data = data;
118 	ptr->next = NULL;
119 
120 	/* Special case: the input list is empty */
121 	if(*list == NULL) {
122 		*list = ptr;
123 		ptr->prev = ptr;
124 	} else {
125 		alpm_list_t *lp = alpm_list_last(*list);
126 		lp->next = ptr;
127 		ptr->prev = lp;
128 		(*list)->prev = ptr;
129 	}
130 
131 	return ptr;
132 }
133 
134 /**
135  * @brief Duplicate and append a string to a list.
136  *
137  * @param list the list to append to
138  * @param data the string to duplicate and append
139  *
140  * @return the newly added item
141  */
alpm_list_append_strdup(alpm_list_t ** list,const char * data)142 alpm_list_t SYMEXPORT *alpm_list_append_strdup(alpm_list_t **list, const char *data)
143 {
144 	alpm_list_t *ret;
145 	char *dup;
146 	if((dup = strdup(data)) && (ret = alpm_list_append(list, dup))) {
147 		return ret;
148 	} else {
149 		free(dup);
150 		return NULL;
151 	}
152 }
153 
154 /**
155  * @brief Add items to a list in sorted order.
156  *
157  * @param list the list to add to
158  * @param data the new item to be added to the list
159  * @param fn   the comparison function to use to determine order
160  *
161  * @return the resultant list
162  */
alpm_list_add_sorted(alpm_list_t * list,void * data,alpm_list_fn_cmp fn)163 alpm_list_t SYMEXPORT *alpm_list_add_sorted(alpm_list_t *list, void *data, alpm_list_fn_cmp fn)
164 {
165 	if(!fn || !list) {
166 		return alpm_list_add(list, data);
167 	} else {
168 		alpm_list_t *add = NULL, *prev = NULL, *next = list;
169 
170 		add = malloc(sizeof(alpm_list_t));
171 		if(add == NULL) {
172 			return list;
173 		}
174 		add->data = data;
175 
176 		/* Find insertion point. */
177 		while(next) {
178 			if(fn(add->data, next->data) <= 0) break;
179 			prev = next;
180 			next = next->next;
181 		}
182 
183 		/* Insert the add node to the list */
184 		if(prev == NULL) { /* special case: we insert add as the first element */
185 			add->prev = list->prev; /* list != NULL */
186 			add->next = list;
187 			list->prev = add;
188 			return add;
189 		} else if(next == NULL) { /* another special case: add last element */
190 			add->prev = prev;
191 			add->next = NULL;
192 			prev->next = add;
193 			list->prev = add;
194 			return list;
195 		} else {
196 			add->prev = prev;
197 			add->next = next;
198 			next->prev = add;
199 			prev->next = add;
200 			return list;
201 		}
202 	}
203 }
204 
205 /**
206  * @brief Join two lists.
207  * The two lists must be independent. Do not free the original lists after
208  * calling this function, as this is not a copy operation. The list pointers
209  * passed in should be considered invalid after calling this function.
210  *
211  * @param first  the first list
212  * @param second the second list
213  *
214  * @return the resultant joined list
215  */
alpm_list_join(alpm_list_t * first,alpm_list_t * second)216 alpm_list_t SYMEXPORT *alpm_list_join(alpm_list_t *first, alpm_list_t *second)
217 {
218 	alpm_list_t *tmp;
219 
220 	if(first == NULL) {
221 		return second;
222 	}
223 	if(second == NULL) {
224 		return first;
225 	}
226 	/* tmp is the last element of the first list */
227 	tmp = first->prev;
228 	/* link the first list to the second */
229 	tmp->next = second;
230 	/* link the second list to the first */
231 	first->prev = second->prev;
232 	/* set the back reference to the tail */
233 	second->prev = tmp;
234 
235 	return first;
236 }
237 
238 /**
239  * @brief Merge the two sorted sublists into one sorted list.
240  *
241  * @param left  the first list
242  * @param right the second list
243  * @param fn    comparison function for determining merge order
244  *
245  * @return the resultant list
246  */
alpm_list_mmerge(alpm_list_t * left,alpm_list_t * right,alpm_list_fn_cmp fn)247 alpm_list_t SYMEXPORT *alpm_list_mmerge(alpm_list_t *left, alpm_list_t *right,
248 		alpm_list_fn_cmp fn)
249 {
250 	alpm_list_t *newlist, *lp, *tail_ptr, *left_tail_ptr, *right_tail_ptr;
251 
252 	if(left == NULL) {
253 		return right;
254 	}
255 	if(right == NULL) {
256 		return left;
257 	}
258 
259 	/* Save tail node pointers for future use */
260 	left_tail_ptr = left->prev;
261 	right_tail_ptr = right->prev;
262 
263 	if(fn(left->data, right->data) <= 0) {
264 		newlist = left;
265 		left = left->next;
266 	}
267 	else {
268 		newlist = right;
269 		right = right->next;
270 	}
271 	newlist->prev = NULL;
272 	newlist->next = NULL;
273 	lp = newlist;
274 
275 	while((left != NULL) && (right != NULL)) {
276 		if(fn(left->data, right->data) <= 0) {
277 			lp->next = left;
278 			left->prev = lp;
279 			left = left->next;
280 		}
281 		else {
282 			lp->next = right;
283 			right->prev = lp;
284 			right = right->next;
285 		}
286 		lp = lp->next;
287 		lp->next = NULL;
288 	}
289 	if(left != NULL) {
290 		lp->next = left;
291 		left->prev = lp;
292 		tail_ptr = left_tail_ptr;
293 	}
294 	else if(right != NULL) {
295 		lp->next = right;
296 		right->prev = lp;
297 		tail_ptr = right_tail_ptr;
298 	}
299 	else {
300 		tail_ptr = lp;
301 	}
302 
303 	newlist->prev = tail_ptr;
304 
305 	return newlist;
306 }
307 
308 /**
309  * @brief Sort a list of size `n` using mergesort algorithm.
310  *
311  * @param list the list to sort
312  * @param n    the size of the list
313  * @param fn   the comparison function for determining order
314  *
315  * @return the resultant list
316  */
alpm_list_msort(alpm_list_t * list,size_t n,alpm_list_fn_cmp fn)317 alpm_list_t SYMEXPORT *alpm_list_msort(alpm_list_t *list, size_t n,
318 		alpm_list_fn_cmp fn)
319 {
320 	if(n > 1) {
321 		size_t half = n / 2;
322 		size_t i = half - 1;
323 		alpm_list_t *left = list, *lastleft = list, *right;
324 
325 		while(i--) {
326 			lastleft = lastleft->next;
327 		}
328 		right = lastleft->next;
329 
330 		/* tidy new lists */
331 		lastleft->next = NULL;
332 		right->prev = left->prev;
333 		left->prev = lastleft;
334 
335 		left = alpm_list_msort(left, half, fn);
336 		right = alpm_list_msort(right, n - half, fn);
337 		list = alpm_list_mmerge(left, right, fn);
338 	}
339 	return list;
340 }
341 
342 /**
343  * @brief Remove an item from the list.
344  * item is not freed; this is the responsibility of the caller.
345  *
346  * @param haystack the list to remove the item from
347  * @param item the item to remove from the list
348  *
349  * @return the resultant list
350  */
alpm_list_remove_item(alpm_list_t * haystack,alpm_list_t * item)351 alpm_list_t SYMEXPORT *alpm_list_remove_item(alpm_list_t *haystack,
352 		alpm_list_t *item)
353 {
354 	if(haystack == NULL || item == NULL) {
355 		return haystack;
356 	}
357 
358 	if(item == haystack) {
359 		/* Special case: removing the head node which has a back reference to
360 		 * the tail node */
361 		haystack = item->next;
362 		if(haystack) {
363 			haystack->prev = item->prev;
364 		}
365 		item->prev = NULL;
366 	} else if(item == haystack->prev) {
367 		/* Special case: removing the tail node, so we need to fix the back
368 		 * reference on the head node. We also know tail != head. */
369 		if(item->prev) {
370 			/* i->next should always be null */
371 			item->prev->next = item->next;
372 			haystack->prev = item->prev;
373 			item->prev = NULL;
374 		}
375 	} else {
376 		/* Normal case, non-head and non-tail node */
377 		if(item->next) {
378 			item->next->prev = item->prev;
379 		}
380 		if(item->prev) {
381 			item->prev->next = item->next;
382 		}
383 	}
384 
385 	return haystack;
386 }
387 
388 
389 /**
390  * @brief Remove an item from the list.
391  *
392  * @param haystack the list to remove the item from
393  * @param needle   the data member of the item we're removing
394  * @param fn       the comparison function for searching
395  * @param data     output parameter containing data of the removed item
396  *
397  * @return the resultant list
398  */
alpm_list_remove(alpm_list_t * haystack,const void * needle,alpm_list_fn_cmp fn,void ** data)399 alpm_list_t SYMEXPORT *alpm_list_remove(alpm_list_t *haystack,
400 		const void *needle, alpm_list_fn_cmp fn, void **data)
401 {
402 	alpm_list_t *i = haystack;
403 
404 	if(data) {
405 		*data = NULL;
406 	}
407 
408 	if(needle == NULL) {
409 		return haystack;
410 	}
411 
412 	while(i) {
413 		if(i->data == NULL) {
414 			i = i->next;
415 			continue;
416 		}
417 		if(fn(i->data, needle) == 0) {
418 			haystack = alpm_list_remove_item(haystack, i);
419 
420 			if(data) {
421 				*data = i->data;
422 			}
423 			free(i);
424 			break;
425 		} else {
426 			i = i->next;
427 		}
428 	}
429 
430 	return haystack;
431 }
432 
433 /**
434  * @brief Remove a string from a list.
435  *
436  * @param haystack the list to remove the item from
437  * @param needle   the data member of the item we're removing
438  * @param data     output parameter containing data of the removed item
439  *
440  * @return the resultant list
441  */
alpm_list_remove_str(alpm_list_t * haystack,const char * needle,char ** data)442 alpm_list_t SYMEXPORT *alpm_list_remove_str(alpm_list_t *haystack,
443 		const char *needle, char **data)
444 {
445 	return alpm_list_remove(haystack, (const void *)needle,
446 			(alpm_list_fn_cmp)strcmp, (void **)data);
447 }
448 
449 /**
450  * @brief Create a new list without any duplicates.
451  *
452  * This does NOT copy data members.
453  *
454  * @param list the list to copy
455  *
456  * @return a new list containing non-duplicate items
457  */
alpm_list_remove_dupes(const alpm_list_t * list)458 alpm_list_t SYMEXPORT *alpm_list_remove_dupes(const alpm_list_t *list)
459 {
460 	const alpm_list_t *lp = list;
461 	alpm_list_t *newlist = NULL;
462 	while(lp) {
463 		if(!alpm_list_find_ptr(newlist, lp->data)) {
464 			if(alpm_list_append(&newlist, lp->data) == NULL) {
465 				alpm_list_free(newlist);
466 				return NULL;
467 			}
468 		}
469 		lp = lp->next;
470 	}
471 	return newlist;
472 }
473 
474 /**
475  * @brief Copy a string list, including data.
476  *
477  * @param list the list to copy
478  *
479  * @return a copy of the original list
480  */
alpm_list_strdup(const alpm_list_t * list)481 alpm_list_t SYMEXPORT *alpm_list_strdup(const alpm_list_t *list)
482 {
483 	const alpm_list_t *lp = list;
484 	alpm_list_t *newlist = NULL;
485 	while(lp) {
486 		if(alpm_list_append_strdup(&newlist, lp->data) == NULL) {
487 			FREELIST(newlist);
488 			return NULL;
489 		}
490 		lp = lp->next;
491 	}
492 	return newlist;
493 }
494 
495 /**
496  * @brief Copy a list, without copying data.
497  *
498  * @param list the list to copy
499  *
500  * @return a copy of the original list
501  */
alpm_list_copy(const alpm_list_t * list)502 alpm_list_t SYMEXPORT *alpm_list_copy(const alpm_list_t *list)
503 {
504 	const alpm_list_t *lp = list;
505 	alpm_list_t *newlist = NULL;
506 	while(lp) {
507 		if(alpm_list_append(&newlist, lp->data) == NULL) {
508 			alpm_list_free(newlist);
509 			return NULL;
510 		}
511 		lp = lp->next;
512 	}
513 	return newlist;
514 }
515 
516 /**
517  * @brief Copy a list and copy the data.
518  * Note that the data elements to be copied should not contain pointers
519  * and should also be of constant size.
520  *
521  * @param list the list to copy
522  * @param size the size of each data element
523  *
524  * @return a copy of the original list, data copied as well
525  */
alpm_list_copy_data(const alpm_list_t * list,size_t size)526 alpm_list_t SYMEXPORT *alpm_list_copy_data(const alpm_list_t *list,
527 		size_t size)
528 {
529 	const alpm_list_t *lp = list;
530 	alpm_list_t *newlist = NULL;
531 	while(lp) {
532 		void *newdata = malloc(size);
533 		if(newdata) {
534 			memcpy(newdata, lp->data, size);
535 			if(alpm_list_append(&newlist, newdata) == NULL) {
536 				free(newdata);
537 				FREELIST(newlist);
538 				return NULL;
539 			}
540 			lp = lp->next;
541 		} else {
542 			FREELIST(newlist);
543 			return NULL;
544 		}
545 	}
546 	return newlist;
547 }
548 
549 /**
550  * @brief Create a new list in reverse order.
551  *
552  * @param list the list to copy
553  *
554  * @return a new list in reverse order
555  */
alpm_list_reverse(alpm_list_t * list)556 alpm_list_t SYMEXPORT *alpm_list_reverse(alpm_list_t *list)
557 {
558 	const alpm_list_t *lp;
559 	alpm_list_t *newlist = NULL, *backup;
560 
561 	if(list == NULL) {
562 		return NULL;
563 	}
564 
565 	lp = alpm_list_last(list);
566 	/* break our reverse circular list */
567 	backup = list->prev;
568 	list->prev = NULL;
569 
570 	while(lp) {
571 		if(alpm_list_append(&newlist, lp->data) == NULL) {
572 			alpm_list_free(newlist);
573 			return NULL;
574 		}
575 		lp = lp->prev;
576 	}
577 	list->prev = backup; /* restore tail pointer */
578 	return newlist;
579 }
580 
581 /* Accessors */
582 
583 /**
584  * @brief Return nth element from list (starting from 0).
585  *
586  * @param list the list
587  * @param n    the index of the item to find (n < alpm_list_count(list) IS needed)
588  *
589  * @return an alpm_list_t node for index `n`
590  */
alpm_list_nth(const alpm_list_t * list,size_t n)591 alpm_list_t SYMEXPORT *alpm_list_nth(const alpm_list_t *list, size_t n)
592 {
593 	const alpm_list_t *i = list;
594 	while(n--) {
595 		i = i->next;
596 	}
597 	return (alpm_list_t *)i;
598 }
599 
600 /**
601  * @brief Get the next element of a list.
602  *
603  * @param node the list node
604  *
605  * @return the next element, or NULL when no more elements exist
606  */
alpm_list_next(const alpm_list_t * node)607 inline alpm_list_t SYMEXPORT *alpm_list_next(const alpm_list_t *node)
608 {
609 	if(node) {
610 		return node->next;
611 	} else {
612 		return NULL;
613 	}
614 }
615 
616 /**
617  * @brief Get the previous element of a list.
618  *
619  * @param list the list head
620  *
621  * @return the previous element, or NULL when no previous element exist
622  */
alpm_list_previous(const alpm_list_t * list)623 inline alpm_list_t SYMEXPORT *alpm_list_previous(const alpm_list_t *list)
624 {
625 	if(list && list->prev->next) {
626 		return list->prev;
627 	} else {
628 		return NULL;
629 	}
630 }
631 
632 /**
633  * @brief Get the last item in the list.
634  *
635  * @param list the list
636  *
637  * @return the last element in the list
638  */
alpm_list_last(const alpm_list_t * list)639 alpm_list_t SYMEXPORT *alpm_list_last(const alpm_list_t *list)
640 {
641 	if(list) {
642 		return list->prev;
643 	} else {
644 		return NULL;
645 	}
646 }
647 
648 /* Misc */
649 
650 /**
651  * @brief Get the number of items in a list.
652  *
653  * @param list the list
654  *
655  * @return the number of list items
656  */
alpm_list_count(const alpm_list_t * list)657 size_t SYMEXPORT alpm_list_count(const alpm_list_t *list)
658 {
659 	size_t i = 0;
660 	const alpm_list_t *lp = list;
661 	while(lp) {
662 		++i;
663 		lp = lp->next;
664 	}
665 	return i;
666 }
667 
668 /**
669  * @brief Find an item in a list.
670  *
671  * @param needle   the item to search
672  * @param haystack the list
673  * @param fn       the comparison function for searching (!= NULL)
674  *
675  * @return `needle` if found, NULL otherwise
676  */
alpm_list_find(const alpm_list_t * haystack,const void * needle,alpm_list_fn_cmp fn)677 void SYMEXPORT *alpm_list_find(const alpm_list_t *haystack, const void *needle,
678 		alpm_list_fn_cmp fn)
679 {
680 	const alpm_list_t *lp = haystack;
681 	while(lp) {
682 		if(lp->data && fn(lp->data, needle) == 0) {
683 			return lp->data;
684 		}
685 		lp = lp->next;
686 	}
687 	return NULL;
688 }
689 
690 /* trivial helper function for alpm_list_find_ptr */
ptr_cmp(const void * p,const void * q)691 static int ptr_cmp(const void *p, const void *q)
692 {
693 	return (p != q);
694 }
695 
696 /**
697  * @brief Find an item in a list.
698  *
699  * Search for the item whose data matches that of the `needle`.
700  *
701  * @param needle   the data to search for (== comparison)
702  * @param haystack the list
703  *
704  * @return `needle` if found, NULL otherwise
705  */
alpm_list_find_ptr(const alpm_list_t * haystack,const void * needle)706 void SYMEXPORT *alpm_list_find_ptr(const alpm_list_t *haystack,
707 		const void *needle)
708 {
709 	return alpm_list_find(haystack, needle, ptr_cmp);
710 }
711 
712 /**
713  * @brief Find a string in a list.
714  *
715  * @param needle   the string to search for
716  * @param haystack the list
717  *
718  * @return `needle` if found, NULL otherwise
719  */
alpm_list_find_str(const alpm_list_t * haystack,const char * needle)720 char SYMEXPORT *alpm_list_find_str(const alpm_list_t *haystack,
721 		const char *needle)
722 {
723 	return (char *)alpm_list_find(haystack, (const void *)needle,
724 			(alpm_list_fn_cmp)strcmp);
725 }
726 
727 /**
728  * @brief Find the differences between list `left` and list `right`
729  *
730  * The two lists must be sorted. Items only in list `left` are added to the
731  * `onlyleft` list. Items only in list `right` are added to the `onlyright`
732  * list.
733  *
734  * @param left      the first list
735  * @param right     the second list
736  * @param fn        the comparison function
737  * @param onlyleft  pointer to the first result list
738  * @param onlyright pointer to the second result list
739  *
740  */
alpm_list_diff_sorted(const alpm_list_t * left,const alpm_list_t * right,alpm_list_fn_cmp fn,alpm_list_t ** onlyleft,alpm_list_t ** onlyright)741 void SYMEXPORT alpm_list_diff_sorted(const alpm_list_t *left,
742 		const alpm_list_t *right, alpm_list_fn_cmp fn,
743 		alpm_list_t **onlyleft, alpm_list_t **onlyright)
744 {
745 	const alpm_list_t *l = left;
746 	const alpm_list_t *r = right;
747 
748 	if(!onlyleft && !onlyright) {
749 		return;
750 	}
751 
752 	while(l != NULL && r != NULL) {
753 		int cmp = fn(l->data, r->data);
754 		if(cmp < 0) {
755 			if(onlyleft) {
756 				*onlyleft = alpm_list_add(*onlyleft, l->data);
757 			}
758 			l = l->next;
759 		}
760 		else if(cmp > 0) {
761 			if(onlyright) {
762 				*onlyright = alpm_list_add(*onlyright, r->data);
763 			}
764 			r = r->next;
765 		} else {
766 			l = l->next;
767 			r = r->next;
768 		}
769 	}
770 	while(l != NULL) {
771 		if(onlyleft) {
772 			*onlyleft = alpm_list_add(*onlyleft, l->data);
773 		}
774 		l = l->next;
775 	}
776 	while(r != NULL) {
777 		if(onlyright) {
778 			*onlyright = alpm_list_add(*onlyright, r->data);
779 		}
780 		r = r->next;
781 	}
782 }
783 
784 
785 /**
786  * @brief Find the items in list `lhs` that are not present in list `rhs`.
787  *
788  * @param lhs the first list
789  * @param rhs the second list
790  * @param fn  the comparison function
791  *
792  * @return a list containing all items in `lhs` not present in `rhs`
793  */
alpm_list_diff(const alpm_list_t * lhs,const alpm_list_t * rhs,alpm_list_fn_cmp fn)794 alpm_list_t SYMEXPORT *alpm_list_diff(const alpm_list_t *lhs,
795 		const alpm_list_t *rhs, alpm_list_fn_cmp fn)
796 {
797 	alpm_list_t *left, *right;
798 	alpm_list_t *ret = NULL;
799 
800 	left = alpm_list_copy(lhs);
801 	left = alpm_list_msort(left, alpm_list_count(left), fn);
802 	right = alpm_list_copy(rhs);
803 	right = alpm_list_msort(right, alpm_list_count(right), fn);
804 
805 	alpm_list_diff_sorted(left, right, fn, &ret, NULL);
806 
807 	alpm_list_free(left);
808 	alpm_list_free(right);
809 	return ret;
810 }
811 
812 /**
813  * @brief Copy a list and data into a standard C array of fixed length.
814  * Note that the data elements are shallow copied so any contained pointers
815  * will point to the original data.
816  *
817  * @param list the list to copy
818  * @param n    the size of the list
819  * @param size the size of each data element
820  *
821  * @return an array version of the original list, data copied as well
822  */
alpm_list_to_array(const alpm_list_t * list,size_t n,size_t size)823 void SYMEXPORT *alpm_list_to_array(const alpm_list_t *list, size_t n,
824 		size_t size)
825 {
826 	size_t i;
827 	const alpm_list_t *item;
828 	char *array;
829 
830 	if(n == 0) {
831 		return NULL;
832 	}
833 
834 	array = malloc(n * size);
835 	if(array == NULL) {
836 		return NULL;
837 	}
838 	for(i = 0, item = list; i < n && item; i++, item = item->next) {
839 		memcpy(array + i * size, item->data, size);
840 	}
841 	return array;
842 }
843 
844 /** @} */
845