1 /*******************************************************************/
2 
3 /* The following sort functions are trivial rewrites of merge-sort
4  * implementation by Simon Tatham
5  * copyright 2001 Simon Tatham.
6  *
7  * Permission is hereby granted, free of charge, to any person
8  * obtaining a copy of this software and associated documentation
9  * files (the "Software"), to deal in the Software without
10  * restriction, including without limitation the rights to use,
11  * copy, modify, merge, publish, distribute, sublicense, and/or
12  * sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following
14  * conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  */
28 
29 #include <cf3.defs.h>
30 
31 #include <rlist.h>
32 #include <item_lib.h>
33 #include <ip_address.h>
34 
35 typedef bool (*LessFn)(void *lhs, void *rhs, void *ctx);
36 typedef void * (*GetNextElementFn)(void *element);
37 typedef void (*PutNextElementFn)(void *element, void *next);
38 
Sort(void * list,LessFn less,GetNextElementFn next,PutNextElementFn putnext,void * ctx)39 static void *Sort(void *list, LessFn less, GetNextElementFn next, PutNextElementFn putnext, void *ctx)
40 {
41     void *p, *q, *e, *tail;
42     int insize, nmerges, psize, qsize, i;
43 
44     if (list == NULL)
45     {
46         return NULL;
47     }
48 
49     insize = 1;
50 
51     while (true)
52     {
53         p = list;
54         list = NULL;
55         tail = NULL;
56 
57         nmerges = 0;            /* count number of merges we do in this pass */
58 
59         while (p)
60         {
61             nmerges++;          /* there exists a merge to be done */
62             /* step `insize' places along from p */
63             q = p;
64             psize = 0;
65 
66             for (i = 0; i < insize; i++)
67             {
68                 psize++;
69 
70                 q = next(q);
71 
72                 if (!q)
73                 {
74                     break;
75                 }
76             }
77 
78             /* if q hasn't fallen off end, we have two lists to merge */
79             qsize = insize;
80 
81             /* now we have two lists; merge them */
82             while ((psize > 0) || ((qsize > 0) && q))
83             {
84                 /* decide whether next element of merge comes from p or q */
85                 if (psize == 0)
86                 {
87                     /* p is empty; e must come from q. */
88                     e = q;
89                     q = next(q);
90                     qsize--;
91                 }
92                 else if ((qsize == 0) || (!q))
93                 {
94                     /* q is empty; e must come from p. */
95                     e = p;
96                     p = next(p);
97                     psize--;
98                 }
99                 else if (less(p, q, ctx))
100                 {
101                     /* First element of p is lower (or same);
102                      * e must come from p. */
103                     e = p;
104                     p = next(p);
105                     psize--;
106                 }
107                 else
108                 {
109                     /* First element of q is lower; e must come from q. */
110                     e = q;
111                     q = next(q);
112                     qsize--;
113                 }
114 
115                 /* add the next element to the merged list */
116                 if (tail)
117                 {
118                     putnext(tail, e);
119                 }
120                 else
121                 {
122                     list = e;
123                 }
124 
125                 tail = e;
126             }
127 
128             /* now p has stepped `insize' places along, and q has too */
129             p = q;
130         }
131 
132         putnext(tail, NULL);
133 
134         /* If we have done only one merge, we're finished. */
135 
136         if (nmerges <= 1)       /* allow for nmerges==0, the empty list case */
137         {
138             return list;
139         }
140 
141         /* Otherwise repeat, merging lists twice the size */
142         insize *= 2;
143     }
144 }
145 
146 /* Item* callbacks */
147 
ItemNameLess(void * lhs,void * rhs,ARG_UNUSED void * ctx)148 static bool ItemNameLess(void *lhs, void *rhs, ARG_UNUSED void *ctx)
149 {
150     return strcmp(((Item*)lhs)->name, ((Item*)rhs)->name) < 0;
151 }
152 
ItemClassesLess(void * lhs,void * rhs,ARG_UNUSED void * ctx)153 static bool ItemClassesLess(void *lhs, void *rhs, ARG_UNUSED void *ctx)
154 {
155     return strcmp(((Item*)lhs)->classes, ((Item*)rhs)->classes) < 0;
156 }
157 
ItemCounterMore(void * lhs,void * rhs,ARG_UNUSED void * ctx)158 static bool ItemCounterMore(void *lhs, void *rhs, ARG_UNUSED void *ctx)
159 {
160     return ((Item*)lhs)->counter > ((Item*)rhs)->counter;
161 }
162 
ItemTimeMore(void * lhs,void * rhs,ARG_UNUSED void * ctx)163 static bool ItemTimeMore(void *lhs, void *rhs, ARG_UNUSED void *ctx)
164 {
165     return ((Item*)lhs)->time > ((Item*)rhs)->time;
166 }
167 
ItemGetNext(void * element)168 static void *ItemGetNext(void *element)
169 {
170     return ((Item*)element)->next;
171 }
172 
ItemPutNext(void * element,void * next)173 static void ItemPutNext(void *element, void *next)
174 {
175     ((Item*)element)->next = (Item *)next;
176 }
177 
178 /* Item* sorting */
179 
SortItemListNames(Item * list)180 Item *SortItemListNames(Item *list)
181 {
182     return Sort(list, &ItemNameLess, &ItemGetNext, &ItemPutNext, NULL);
183 }
184 
SortItemListClasses(Item * list)185 Item *SortItemListClasses(Item *list)
186 {
187     return Sort(list, &ItemClassesLess, &ItemGetNext, &ItemPutNext, NULL);
188 }
189 
SortItemListCounters(Item * list)190 Item *SortItemListCounters(Item *list)
191 {
192     return Sort(list, &ItemCounterMore, &ItemGetNext, &ItemPutNext, NULL);
193 }
194 
SortItemListTimes(Item * list)195 Item *SortItemListTimes(Item *list)
196 {
197     return Sort(list, &ItemTimeMore, &ItemGetNext, &ItemPutNext, NULL);
198 }
199 
200 /* Rlist* and String* callbacks */
201 
RlistCustomItemLess(void * lhs_,void * rhs_,void * ctx)202 static bool RlistCustomItemLess(void *lhs_, void *rhs_, void *ctx)
203 {
204     Rlist *lhs = lhs_;
205     Rlist *rhs = rhs_;
206     bool (*cmp)() = ctx;
207 
208     return (*cmp)(lhs->val.item, rhs->val.item);
209 }
210 
RlistItemLess(void * lhs,void * rhs,ARG_UNUSED void * ctx)211 static bool RlistItemLess(void *lhs, void *rhs, ARG_UNUSED void *ctx)
212 {
213     return strcmp(((Rlist*)lhs)->val.item, ((Rlist*)rhs)->val.item) < 0;
214 }
215 
StringItemLess(const char * lhs,const char * rhs,ARG_UNUSED void * ctx)216 static bool StringItemLess(const char *lhs, const char *rhs, ARG_UNUSED void *ctx)
217 {
218     return strcmp(lhs, rhs) < 0;
219 }
220 
StringItemNumberLess(const char * lhs,const char * rhs,ARG_UNUSED void * ctx,bool int_mode)221 static bool StringItemNumberLess(const char *lhs, const char *rhs, ARG_UNUSED void *ctx, bool int_mode)
222 {
223     char remainder[4096];
224     double left;
225     double right;
226 
227     bool matched_left = sscanf(lhs, "%lf", &left) > 0;
228     bool matched_right = sscanf(rhs, "%lf", &right) > 0;
229 
230     if (!matched_left)
231     {
232         matched_left = sscanf(lhs, "%lf%4095s", &left, remainder) > 0;
233     }
234 
235     if (!matched_right)
236     {
237         matched_right = sscanf(rhs, "%lf%4095s", &right, remainder) > 0;
238     }
239 
240     if (matched_left && matched_right)
241     {
242         if (int_mode)
243         {
244             return ((long int)left) - ((long int)right) < 0;
245         }
246         else
247         {
248             return left - right < 0;
249         }
250     }
251 
252     if (matched_left)
253     {
254         return false;
255     }
256 
257     if (matched_right)
258     {
259         return true;
260     }
261 
262     // neither item matched
263     return StringItemLess(lhs, rhs, ctx);
264 }
265 
RlistItemNumberLess(void * lhs,void * rhs,ARG_UNUSED void * ctx,bool int_mode)266 static bool RlistItemNumberLess(void *lhs, void *rhs, ARG_UNUSED void *ctx, bool int_mode)
267 {
268     return StringItemNumberLess(RlistScalarValue((Rlist*)lhs), RlistScalarValue((Rlist*)rhs), ctx, int_mode);
269 }
270 
RlistItemIntLess(void * lhs,void * rhs,ARG_UNUSED void * ctx)271 static bool RlistItemIntLess(void *lhs, void *rhs, ARG_UNUSED void *ctx)
272 {
273     return RlistItemNumberLess(lhs, rhs, ctx, true);
274 }
275 
RlistItemRealLess(void * lhs,void * rhs,ARG_UNUSED void * ctx)276 static bool RlistItemRealLess(void *lhs, void *rhs, ARG_UNUSED void *ctx)
277 {
278     return RlistItemNumberLess(lhs, rhs, ctx, false);
279 }
280 
StringItemIPLess(const char * left_item,const char * right_item,ARG_UNUSED void * ctx)281 static bool StringItemIPLess(const char *left_item, const char *right_item, ARG_UNUSED void *ctx)
282 {
283     Buffer *left_buffer = BufferNewFrom(left_item, strlen(left_item));
284     Buffer *right_buffer = BufferNewFrom(right_item, strlen(right_item));
285 
286     IPAddress *left = IPAddressNew(left_buffer);
287     IPAddress *right = IPAddressNew(right_buffer);
288 
289     bool matched_left = (left != NULL);
290     bool matched_right = (right != NULL);
291 
292     BufferDestroy(left_buffer);
293     BufferDestroy(right_buffer);
294 
295     if (matched_left && matched_right)
296     {
297         bool less = IPAddressCompareLess(left, right);
298         IPAddressDestroy(&left);
299         IPAddressDestroy(&right);
300         return less;
301     }
302 
303     IPAddressDestroy(&left);
304     IPAddressDestroy(&right);
305 
306     if (matched_left)
307     {
308         return false;
309     }
310 
311     if (matched_right)
312     {
313         return true;
314     }
315 
316     // neither item matched
317     return StringItemLess(left_item, right_item, ctx);
318 }
319 
RlistItemIPLess(void * lhs,void * rhs,ARG_UNUSED void * ctx)320 static bool RlistItemIPLess(void *lhs, void *rhs, ARG_UNUSED void *ctx)
321 {
322     return StringItemIPLess(RlistScalarValue((Rlist*)lhs), RlistScalarValue((Rlist*)rhs), ctx);
323 }
324 
ParseEtherAddress(const char * input,unsigned char * addr)325 static long ParseEtherAddress(const char* input, unsigned char *addr)
326 {
327     if (strlen(input) > 12)
328     {
329         return sscanf(input, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
330                       &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]);
331     }
332 
333     return sscanf(input, "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
334                   &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]);
335 }
336 
StringItemMACLess(const char * lhs,const char * rhs,ARG_UNUSED void * ctx)337 static bool StringItemMACLess(const char *lhs, const char *rhs, ARG_UNUSED void *ctx)
338 {
339     int bytes = 6;
340     unsigned char left[bytes], right[bytes];
341     int matched_left  = (ParseEtherAddress(lhs, left)  == 6);
342     int matched_right = (ParseEtherAddress(rhs, right) == 6);;
343 
344     if (matched_left && matched_right)
345     {
346         int difference = memcmp(left, right, bytes);
347         if (difference != 0) return difference < 0;
348     }
349 
350     if (matched_left)
351     {
352         return false;
353     }
354 
355     if (matched_right)
356     {
357         return true;
358     }
359 
360     // neither item matched
361     return StringItemLess(lhs, rhs, ctx);
362 }
363 
RlistItemMACLess(void * lhs,void * rhs,ARG_UNUSED void * ctx)364 static bool RlistItemMACLess(void *lhs, void *rhs, ARG_UNUSED void *ctx)
365 {
366     return StringItemMACLess(RlistScalarValue((Rlist*)lhs), RlistScalarValue((Rlist*)rhs), ctx);
367 }
368 
RlistGetNext(void * element)369 static void *RlistGetNext(void *element)
370 {
371     return ((Rlist*)element)->next;
372 }
373 
RlistPutNext(void * element,void * next)374 static void RlistPutNext(void *element, void *next)
375 {
376     ((Rlist*)element)->next = (Rlist *)next;
377 }
378 
379 /* Rlist* sorting */
380 
SortRlist(Rlist * list,bool (* CompareItems)())381 Rlist *SortRlist(Rlist *list, bool (*CompareItems) ())
382 {
383     return Sort(list, &RlistCustomItemLess, &RlistGetNext, &RlistPutNext, CompareItems);
384 }
385 
AlphaSortRListNames(Rlist * list)386 Rlist *AlphaSortRListNames(Rlist *list)
387 {
388     return Sort(list, &RlistItemLess, &RlistGetNext, &RlistPutNext, NULL);
389 }
390 
IntSortRListNames(Rlist * list)391 Rlist *IntSortRListNames(Rlist *list)
392 {
393     return Sort(list, &RlistItemIntLess, &RlistGetNext, &RlistPutNext, NULL);
394 }
395 
RealSortRListNames(Rlist * list)396 Rlist *RealSortRListNames(Rlist *list)
397 {
398     return Sort(list, &RlistItemRealLess, &RlistGetNext, &RlistPutNext, NULL);
399 }
400 
IPSortRListNames(Rlist * list)401 Rlist *IPSortRListNames(Rlist *list)
402 {
403     return Sort(list, &RlistItemIPLess, &RlistGetNext, &RlistPutNext, NULL);
404 }
405 
MACSortRListNames(Rlist * list)406 Rlist *MACSortRListNames(Rlist *list)
407 {
408     return Sort(list, &RlistItemMACLess, &RlistGetNext, &RlistPutNext, NULL);
409 }
410 
GenericItemLess(const char * sort_type,void * lhs,void * rhs)411 bool GenericItemLess(const char *sort_type, void *lhs, void *rhs)
412 {
413     if (strcmp(sort_type, "int") == 0)
414     {
415         return RlistItemNumberLess(lhs, rhs, NULL, true);
416     }
417     else if (strcmp(sort_type, "real") == 0)
418     {
419         return RlistItemNumberLess(lhs, rhs, NULL, false);
420     }
421     else if (strcasecmp(sort_type, "IP") == 0)
422     {
423         return RlistItemIPLess(lhs, rhs, NULL);
424     }
425     else if (strcasecmp(sort_type, "MAC") == 0)
426     {
427         return RlistItemMACLess(lhs, rhs, NULL);
428     }
429 
430     // "lex"
431     return RlistItemLess(lhs, rhs, NULL);
432 }
433 
GenericStringItemLess(const char * sort_type,const char * lhs,const char * rhs)434 bool GenericStringItemLess(const char *sort_type, const char *lhs, const char *rhs)
435 {
436     if (strcmp(sort_type, "int") == 0)
437     {
438         return StringItemNumberLess(lhs, rhs, NULL, true);
439     }
440     else if (strcmp(sort_type, "real") == 0)
441     {
442         return StringItemNumberLess(lhs, rhs, NULL, false);
443     }
444     else if (strcasecmp(sort_type, "IP") == 0)
445     {
446         return StringItemIPLess(lhs, rhs, NULL);
447     }
448     else if (strcasecmp(sort_type, "MAC") == 0)
449     {
450         return StringItemMACLess(lhs, rhs, NULL);
451     }
452 
453     // "lex"
454     return StringItemLess(lhs, rhs, NULL);
455 }
456