Commit bd55279a bd55279a9af52b1e6d88b6d8cb483e17174db106 by Sergey Poznyakoff

Implement list sorting.

* include/mailutils/list.h (mu_list_sort): New proto.
* libmailutils/list/sort.c: New file.
* libmailutils/list/Makefile.am (liblist_la_SOURCES): Add sort.c
* libmailutils/tests/listop.c: Implement the sort command.
* libmailutils/tests/list.at: Test list sorting.
1 parent 98ca43a0
...@@ -274,7 +274,9 @@ int mu_list_map (mu_list_t _list, mu_list_mapper_t _map, ...@@ -274,7 +274,9 @@ int mu_list_map (mu_list_t _list, mu_list_mapper_t _map,
274 void *_data, size_t _nelem, 274 void *_data, size_t _nelem,
275 mu_list_t *_res); 275 mu_list_t *_res);
276 276
277 /* List fold */ 277 /* ************************************************* */
278 /* List folding */
279 /* ************************************************* */
278 280
279 typedef int (*mu_list_folder_t) (void *_item, void *_data, 281 typedef int (*mu_list_folder_t) (void *_item, void *_data,
280 void *_prev, void **_ret); 282 void *_prev, void **_ret);
...@@ -316,6 +318,18 @@ int mu_list_fold (mu_list_t _list, mu_list_folder_t _fold, void *_data, ...@@ -316,6 +318,18 @@ int mu_list_fold (mu_list_t _list, mu_list_folder_t _fold, void *_data,
316 int mu_list_rfold (mu_list_t _list, mu_list_folder_t _fold, void *_data, 318 int mu_list_rfold (mu_list_t _list, mu_list_folder_t _fold, void *_data,
317 void *_init, void *_return_value); 319 void *_init, void *_return_value);
318 320
321 /* ************************************************* */
322 /* Sorting */
323 /* ************************************************* */
324
325 /* Sort _list using quicksort algorithm. Use _comp to compare elements.
326 If it is NULL, use the comparator method of _list.
327
328 Comparator must return 0 if the two elements are equal, -1 if
329 first of them is less than the second, and +1 otherwise.
330 */
331 void mu_list_sort (mu_list_t _list, mu_list_comparator_t _comp);
332
319 #ifdef __cplusplus 333 #ifdef __cplusplus
320 } 334 }
321 #endif 335 #endif
......
...@@ -45,6 +45,7 @@ liblist_la_SOURCES = \ ...@@ -45,6 +45,7 @@ liblist_la_SOURCES = \
45 setdestr.c\ 45 setdestr.c\
46 slice.c\ 46 slice.c\
47 slice2.c\ 47 slice2.c\
48 sort.c\
48 tail.c 49 tail.c
49 50
50 INCLUDES = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils 51 INCLUDES = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2011 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <stdlib.h>
22 #include <string.h>
23 #include <mailutils/errno.h>
24 #include <mailutils/sys/list.h>
25
26 static void
27 _list_append_entry (struct _mu_list *list, struct list_data *ent)
28 {
29 ent->prev = list->head.prev ? list->head.prev : &list->head;
30 ent->next = &list->head;
31 if (list->head.prev)
32 list->head.prev->next = ent;
33 else
34 list->head.next = ent;
35 list->head.prev = ent;
36 list->count++;
37 }
38
39 static void
40 _list_qsort (mu_list_t list, mu_list_comparator_t cmp)
41 {
42 struct list_data *cur, *middle;
43 struct _mu_list high_list, low_list;
44 int rc;
45
46 if (list->count < 2)
47 return;
48 if (list->count == 2)
49 {
50 if (cmp (list->head.prev->item, list->head.next->item) < 0)
51 {
52 cur = list->head.prev;
53 list->head.prev = list->head.next;
54 list->head.next = cur;
55
56 list->head.next->prev = &list->head;
57 list->head.next->next = list->head.prev;
58
59 list->head.prev->next = &list->head;
60 list->head.prev->prev = list->head.next;
61 }
62 return;
63 }
64
65 cur = list->head.next;
66 do {
67 cur = cur->next;
68 if (!cur)
69 return;
70 } while ((rc = cmp (list->head.next->item, cur->item)) == 0);
71
72 /* Select the lower of the two as the middle value */
73 middle = (rc > 0) ? cur : list->head.next;
74
75 /* Split into two sublists */
76 memset (&high_list, 0, sizeof (high_list));
77 memset (&low_list, 0, sizeof (low_list));
78
79 for (cur = list->head.next; cur != &list->head; )
80 {
81 struct list_data *next = cur->next;
82 cur->next = NULL;
83
84 if (cmp (middle->item, cur->item) < 0)
85 _list_append_entry (&high_list, cur);
86 else
87 _list_append_entry (&low_list, cur);
88 cur = next;
89 }
90
91 /* Sort both sublists recursively */
92 _list_qsort (&low_list, cmp);
93 _list_qsort (&high_list, cmp);
94
95 /* Join both lists in order */
96 if (low_list.head.prev)
97 cur = low_list.head.prev;
98 else
99 cur = &low_list.head;
100 cur->next = high_list.head.next;
101 if (high_list.head.next)
102 high_list.head.next->prev = cur;
103
104 low_list.head.prev = high_list.head.prev;
105 high_list.head.prev = &low_list.head;
106 low_list.count += high_list.count;
107
108 /* Return the resulting list */
109 list->head = low_list.head;
110 if (list->head.next)
111 list->head.next->prev = &list->head;
112 if (list->head.prev)
113 list->head.prev->next = &list->head;
114 }
115
116 void
117 mu_list_sort (mu_list_t list, mu_list_comparator_t comp)
118 {
119 if (list)
120 _list_qsort (list, comp ? comp : list->comp);
121 }
...@@ -445,6 +445,68 @@ rfold ...@@ -445,6 +445,68 @@ rfold
445 445
446 m4_popdef([MU_TEST_KEYWORDS]) 446 m4_popdef([MU_TEST_KEYWORDS])
447 447
448 # ------------------------------------------------------------
449 # Sort
450 # ------------------------------------------------------------
451
452 m4_define([MU_TEST_GROUP],[Sort])
453 m4_pushdef([MU_TEST_KEYWORDS],MU_TEST_KEYWORDS[ sort])
454
455 TESTLIST([empty list],[],
456 [sort
457 print
458 ],
459 [# items: 0
460 ])
461
462 TESTLIST([sorted list asc],[],
463 [add a b c d e f
464 sort
465 print
466 ],
467 [# items: 6
468 a
469 b
470 c
471 d
472 e
473 f
474 ])
475
476 TESTLIST([sorted list desc],[],
477 [add f e d c b a
478 sort
479 print
480 ],
481 [# items: 6
482 a
483 b
484 c
485 d
486 e
487 f
488 ])
489
490 TESTLIST([unsorted list],[],
491 [add en to tre fire fem seks syv atte ni ti
492 sort
493 print
494 ],
495 [# items: 10
496 atte
497 en
498 fem
499 fire
500 ni
501 seks
502 syv
503 ti
504 to
505 tre
506 ])
507
508 m4_popdef([MU_TEST_KEYWORDS])
509
448 dnl ------------------------------------------------------------ 510 dnl ------------------------------------------------------------
449 dnl Cleanup 511 dnl Cleanup
450 m4_popdef([TESTLIST]) 512 m4_popdef([TESTLIST])
......
...@@ -775,6 +775,12 @@ rfold (mu_list_t list) ...@@ -775,6 +775,12 @@ rfold (mu_list_t list)
775 } 775 }
776 776
777 void 777 void
778 sort (mu_list_t list)
779 {
780 mu_list_sort (list, NULL);
781 }
782
783 void
778 help () 784 help ()
779 { 785 {
780 printf ("count\n"); 786 printf ("count\n");
...@@ -802,6 +808,7 @@ help () ...@@ -802,6 +808,7 @@ help ()
802 printf ("help\n"); 808 printf ("help\n");
803 printf ("head\n"); 809 printf ("head\n");
804 printf ("tail\n"); 810 printf ("tail\n");
811 printf ("sort\n");
805 printf ("NUMBER\n"); 812 printf ("NUMBER\n");
806 } 813 }
807 814
...@@ -921,6 +928,14 @@ shell (mu_list_t list) ...@@ -921,6 +928,14 @@ shell (mu_list_t list)
921 head (ws.ws_wordc, list); 928 head (ws.ws_wordc, list);
922 else if (strcmp (ws.ws_wordv[0], "tail") == 0) 929 else if (strcmp (ws.ws_wordv[0], "tail") == 0)
923 tail (ws.ws_wordc, list); 930 tail (ws.ws_wordc, list);
931 else if (strcmp (ws.ws_wordv[0], "sort") == 0)
932 {
933 int i;
934 sort (list);
935
936 for (i = 0; i < NITR; i++)
937 mu_iterator_destroy (&itr[i]);
938 }
924 else if (ws.ws_wordc == 1) 939 else if (ws.ws_wordc == 1)
925 { 940 {
926 char *p; 941 char *p;
......