1 /**
2  * sort.c -- functions related to sort functionality
3  *    ______      ___
4  *   / ____/___  /   | _____________  __________
5  *  / / __/ __ \/ /| |/ ___/ ___/ _ \/ ___/ ___/
6  * / /_/ / /_/ / ___ / /__/ /__/  __(__  |__  )
7  * \____/\____/_/  |_\___/\___/\___/____/____/
8  *
9  * The MIT License (MIT)
10  * Copyright (c) 2009-2020 Gerardo Orellana <hello @ goaccess.io>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy
13  * of this software and associated documentation files (the "Software"), to deal
14  * in the Software without restriction, including without limitation the rights
15  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16  * copies of the Software, and to permit persons to whom the Software is
17  * furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in all
20  * copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28  * SOFTWARE.
29  */
30 
31 #if HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <getopt.h>
39 #include <errno.h>
40 
41 #include "error.h"
42 #include "settings.h"
43 #include "util.h"
44 
45 #include "sort.h"
46 
47 /* *INDENT-OFF* */
48 const int sort_choices[][SORT_MAX_OPTS] = {
49   /* VISITORS */
50   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
51   /* REQUESTS */
52   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, SORT_BY_PROT, SORT_BY_MTHD, -1},
53   /* REQUESTS_STATIC */
54   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, SORT_BY_PROT, SORT_BY_MTHD, -1},
55   /* NOT_FOUND */
56   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, SORT_BY_PROT, SORT_BY_MTHD, -1},
57   /* HOSTS */
58   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
59   /* OS */
60   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
61   /* BROWSERS */
62   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
63   /* VISIT_TIMES */
64   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
65   /* VIRTUAL_HOSTS */
66   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
67   /* REFERRERS */
68   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
69   /* REFERRING_SITES */
70   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
71   /* KEYPHRASES */
72   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
73   /* STATUS_CODES */
74   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
75   /* REMOTE_USER */
76   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
77   /* CACHE_STATUS */
78   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
79 #ifdef HAVE_GEOLOCATION
80   /* GEO_LOCATION */
81   {SORT_BY_HITS, SORT_BY_VISITORS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
82 #endif
83   /* MIME_TYPE */
84   {SORT_BY_HITS, SORT_BY_DATA, SORT_BY_BW, SORT_BY_AVGTS, SORT_BY_CUMTS, SORT_BY_MAXTS, -1},
85   /* TLS_TYPE */
86   {SORT_BY_HITS, SORT_BY_DATA, SORT_BY_VISITORS, SORT_BY_BW, -1},
87 };
88 
89 static GEnum FIELD[] = {
90   {"BY_HITS"     , SORT_BY_HITS     } ,
91   {"BY_VISITORS" , SORT_BY_VISITORS } ,
92   {"BY_DATA"     , SORT_BY_DATA     } ,
93   {"BY_BW"       , SORT_BY_BW       } ,
94   {"BY_AVGTS"    , SORT_BY_AVGTS    } ,
95   {"BY_CUMTS"    , SORT_BY_CUMTS    } ,
96   {"BY_MAXTS"    , SORT_BY_MAXTS    } ,
97   {"BY_PROT"     , SORT_BY_PROT     } ,
98   {"BY_MTHD"     , SORT_BY_MTHD     } ,
99 };
100 
101 static GEnum ORDER[] = {
102   {"ASC"  , SORT_ASC  } ,
103   {"DESC" , SORT_DESC } ,
104 };
105 
106 GSort module_sort[TOTAL_MODULES] = {
107   {VISITORS            , SORT_BY_DATA , SORT_DESC } ,
108   {REQUESTS            , SORT_BY_HITS , SORT_DESC } ,
109   {REQUESTS_STATIC     , SORT_BY_HITS , SORT_DESC } ,
110   {NOT_FOUND           , SORT_BY_HITS , SORT_DESC } ,
111   {HOSTS               , SORT_BY_HITS , SORT_DESC } ,
112   {OS                  , SORT_BY_HITS , SORT_DESC } ,
113   {BROWSERS            , SORT_BY_HITS , SORT_DESC } ,
114   {VISIT_TIMES         , SORT_BY_DATA , SORT_ASC  } ,
115   {VIRTUAL_HOSTS       , SORT_BY_HITS , SORT_DESC } ,
116   {REFERRERS           , SORT_BY_HITS , SORT_DESC } ,
117   {REFERRING_SITES     , SORT_BY_HITS , SORT_DESC } ,
118   {KEYPHRASES          , SORT_BY_HITS , SORT_DESC } ,
119   {STATUS_CODES        , SORT_BY_HITS , SORT_DESC } ,
120   {REMOTE_USER         , SORT_BY_HITS , SORT_DESC } ,
121   {CACHE_STATUS        , SORT_BY_HITS , SORT_DESC } ,
122 #ifdef HAVE_GEOLOCATION
123   {GEO_LOCATION        , SORT_BY_HITS , SORT_DESC } ,
124 #endif
125   {MIME_TYPE           , SORT_BY_HITS , SORT_DESC } ,
126   {TLS_TYPE            , SORT_BY_VISITORS , SORT_DESC } ,
127 };
128 /* *INDENT-ON* */
129 
130 /* Sort an array of strings ascending */
131 int
strcmp_asc(const void * a,const void * b)132 strcmp_asc (const void *a, const void *b) {
133   return strcmp (*((char *const *) a), *((char *const *) b));
134 }
135 
136 /* Sort 'data' metric ascending */
137 static int
cmp_data_asc(const void * a,const void * b)138 cmp_data_asc (const void *a, const void *b) {
139   const GHolderItem *ia = a;
140   const GHolderItem *ib = b;
141   return strcmp (ia->metrics->data, ib->metrics->data);
142 }
143 
144 /* Sort 'data' metric descending */
145 static int
cmp_data_desc(const void * a,const void * b)146 cmp_data_desc (const void *a, const void *b) {
147   const GHolderItem *ia = a;
148   const GHolderItem *ib = b;
149   return strcmp (ib->metrics->data, ia->metrics->data);
150 }
151 
152 /* Sort 'hits' metric descending */
153 static int
cmp_num_desc(const void * a,const void * b)154 cmp_num_desc (const void *a, const void *b) {
155   const GHolderItem *ia = a;
156   const GHolderItem *ib = b;
157 
158   uint64_t va = ia->metrics->hits;
159   uint64_t vb = ib->metrics->hits;
160 
161   return (va < vb) - (va > vb);
162 }
163 
164 /* Sort 'hits' metric ascending */
165 static int
cmp_num_asc(const void * a,const void * b)166 cmp_num_asc (const void *a, const void *b) {
167   const GHolderItem *ia = a;
168   const GHolderItem *ib = b;
169 
170   uint64_t va = ia->metrics->hits;
171   uint64_t vb = ib->metrics->hits;
172 
173   return (va > vb) - (va < vb);
174 }
175 
176 /* Sort 'visitors' metric descending */
177 static int
cmp_vis_desc(const void * a,const void * b)178 cmp_vis_desc (const void *a, const void *b) {
179   const GHolderItem *ia = a;
180   const GHolderItem *ib = b;
181 
182   uint64_t va = ia->metrics->visitors;
183   uint64_t vb = ib->metrics->visitors;
184 
185   return (va < vb) - (va > vb);
186 }
187 
188 /* Sort 'visitors' metric ascending */
189 static int
cmp_vis_asc(const void * a,const void * b)190 cmp_vis_asc (const void *a, const void *b) {
191   const GHolderItem *ia = a;
192   const GHolderItem *ib = b;
193 
194   uint64_t va = ia->metrics->visitors;
195   uint64_t vb = ib->metrics->visitors;
196 
197   return (va > vb) - (va < vb);
198 }
199 
200 /* Sort GRawDataItem value descending */
201 static int
cmp_raw_num_desc(const void * a,const void * b)202 cmp_raw_num_desc (const void *a, const void *b) {
203   const GRawDataItem *ia = a;
204   const GRawDataItem *ib = b;
205 
206   uint64_t va = ia->hits;
207   uint64_t vb = ib->hits;
208 
209   return (va < vb) - (va > vb);
210 }
211 
212 /* Sort GRawDataItem value descending */
213 static int
cmp_raw_str_desc(const void * a,const void * b)214 cmp_raw_str_desc (const void *a, const void *b) {
215   const GRawDataItem *ia = a;
216   const GRawDataItem *ib = b;
217 
218   return strcmp (ib->data, ia->data);
219 }
220 
221 /* Sort 'bandwidth' metric descending */
222 static int
cmp_bw_desc(const void * a,const void * b)223 cmp_bw_desc (const void *a, const void *b) {
224   const GHolderItem *ia = a;
225   const GHolderItem *ib = b;
226 
227   uint64_t va = ia->metrics->bw.nbw;
228   uint64_t vb = ib->metrics->bw.nbw;
229 
230   return (va < vb) - (va > vb);
231 }
232 
233 /* Sort 'bandwidth' metric ascending */
234 static int
cmp_bw_asc(const void * a,const void * b)235 cmp_bw_asc (const void *a, const void *b) {
236   const GHolderItem *ia = a;
237   const GHolderItem *ib = b;
238 
239   uint64_t va = ia->metrics->bw.nbw;
240   uint64_t vb = ib->metrics->bw.nbw;
241 
242   return (va > vb) - (va < vb);
243 }
244 
245 /* Sort 'avgts' metric descending */
246 static int
cmp_avgts_desc(const void * a,const void * b)247 cmp_avgts_desc (const void *a, const void *b) {
248   const GHolderItem *ia = a;
249   const GHolderItem *ib = b;
250 
251   uint64_t va = ia->metrics->avgts.nts;
252   uint64_t vb = ib->metrics->avgts.nts;
253 
254   return (va < vb) - (va > vb);
255 }
256 
257 /* Sort 'avgts' metric ascending */
258 static int
cmp_avgts_asc(const void * a,const void * b)259 cmp_avgts_asc (const void *a, const void *b) {
260   const GHolderItem *ia = a;
261   const GHolderItem *ib = b;
262 
263   uint64_t va = ia->metrics->avgts.nts;
264   uint64_t vb = ib->metrics->avgts.nts;
265 
266   return (va > vb) - (va < vb);
267 }
268 
269 /* Sort 'cumts' metric descending */
270 static int
cmp_cumts_desc(const void * a,const void * b)271 cmp_cumts_desc (const void *a, const void *b) {
272   const GHolderItem *ia = a;
273   const GHolderItem *ib = b;
274 
275   uint64_t va = ia->metrics->cumts.nts;
276   uint64_t vb = ib->metrics->cumts.nts;
277 
278   return (va < vb) - (va > vb);
279 }
280 
281 /* Sort 'cumts' metric ascending */
282 static int
cmp_cumts_asc(const void * a,const void * b)283 cmp_cumts_asc (const void *a, const void *b) {
284   const GHolderItem *ia = a;
285   const GHolderItem *ib = b;
286 
287   uint64_t va = ia->metrics->cumts.nts;
288   uint64_t vb = ib->metrics->cumts.nts;
289 
290   return (va > vb) - (va < vb);
291 }
292 
293 /* Sort 'maxts' metric descending */
294 static int
cmp_maxts_desc(const void * a,const void * b)295 cmp_maxts_desc (const void *a, const void *b) {
296   const GHolderItem *ia = a;
297   const GHolderItem *ib = b;
298 
299   uint64_t va = ia->metrics->maxts.nts;
300   uint64_t vb = ib->metrics->maxts.nts;
301 
302   return (va < vb) - (va > vb);
303 }
304 
305 /* Sort 'maxts' metric ascending */
306 static int
cmp_maxts_asc(const void * a,const void * b)307 cmp_maxts_asc (const void *a, const void *b) {
308   const GHolderItem *ia = a;
309   const GHolderItem *ib = b;
310 
311   uint64_t va = ia->metrics->maxts.nts;
312   uint64_t vb = ib->metrics->maxts.nts;
313 
314   return (va > vb) - (va < vb);
315 }
316 
317 /* Sort 'protocol' metric ascending */
318 static int
cmp_proto_asc(const void * a,const void * b)319 cmp_proto_asc (const void *a, const void *b) {
320   const GHolderItem *ia = a;
321   const GHolderItem *ib = b;
322   return strcmp (ia->metrics->protocol, ib->metrics->protocol);
323 }
324 
325 /* Sort 'protocol' metric descending */
326 static int
cmp_proto_desc(const void * a,const void * b)327 cmp_proto_desc (const void *a, const void *b) {
328   const GHolderItem *ia = a;
329   const GHolderItem *ib = b;
330   return strcmp (ib->metrics->protocol, ia->metrics->protocol);
331 }
332 
333 /* Sort 'method' metric ascending */
334 static int
cmp_mthd_asc(const void * a,const void * b)335 cmp_mthd_asc (const void *a, const void *b) {
336   const GHolderItem *ia = a;
337   const GHolderItem *ib = b;
338   return strcmp (ia->metrics->method, ib->metrics->method);
339 }
340 
341 /* Sort 'method' metric descending */
342 static int
cmp_mthd_desc(const void * a,const void * b)343 cmp_mthd_desc (const void *a, const void *b) {
344   const GHolderItem *ia = a;
345   const GHolderItem *ib = b;
346   return strcmp (ib->metrics->method, ia->metrics->method);
347 }
348 
349 /* Sort ascending */
350 #if defined(__clang__) && defined(__clang_major__) && (__clang_major__ >= 4)
351 __attribute__((no_sanitize ("implicit-conversion", "unsigned-integer-overflow")))
352 #endif
cmp_ui32_asc(const void * a,const void * b)353   int cmp_ui32_asc (const void *a, const void *b) {
354   const uint32_t *ia = (const uint32_t *) a;    // casting pointer types
355   const uint32_t *ib = (const uint32_t *) b;
356   return *ia - *ib;
357   }
358 
359 int
cmp_ui32_desc(const void * a,const void * b)360 cmp_ui32_desc (const void *a, const void *b) {
361   const uint32_t *ia = (const uint32_t *) a;    // casting pointer types
362   const uint32_t *ib = (const uint32_t *) b;
363   return *ib - *ia;
364 }
365 
366 /* Given a string sort field, get the enum field value.
367  *
368  * On error, -1 is returned.
369  * On success, the enumerated field value is returned. */
370 int
get_sort_field_enum(const char * str)371 get_sort_field_enum (const char *str) {
372   return str2enum (FIELD, ARRAY_SIZE (FIELD), str);
373 }
374 
375 /* Given a string sort order, get the enum order value.
376  *
377  * On error, -1 is returned.
378  * On success, the enumerated order value is returned. */
379 int
get_sort_order_enum(const char * str)380 get_sort_order_enum (const char *str) {
381   return str2enum (ORDER, ARRAY_SIZE (ORDER), str);
382 }
383 
384 /* Given a GSortOrder enum value, return the corresponding string.
385  *
386  * The string corresponding to the enumerated order value is returned. */
387 const char *
get_sort_order_str(GSortOrder order)388 get_sort_order_str (GSortOrder order) {
389   return ORDER[order].str;
390 }
391 
392 /* Given a GSortField enum value, return the corresponding string.
393  *
394  * The string corresponding to the enumerated field value is returned. */
395 const char *
get_sort_field_str(GSortField field)396 get_sort_field_str (GSortField field) {
397   return FIELD[field].str;
398 }
399 
400 /* Given a GSortField enum value, return the corresponding key.
401  *
402  * The key corresponding to the enumerated field value is returned. */
403 const char *
get_sort_field_key(GSortField field)404 get_sort_field_key (GSortField field) {
405   static const char *field2key[][2] = {
406     {"BY_HITS", "hits"},
407     {"BY_VISITORS", "visitors"},
408     {"BY_DATA", "data"},
409     {"BY_BW", "bytes"},
410     {"BY_AVGTS", "avgts"},
411     {"BY_CUMTS", "cumts"},
412     {"BY_MAXTS", "maxts"},
413     {"BY_PROT", "protocol"},
414     {"BY_MTHD", "method"},
415   };
416 
417   return field2key[field][1];
418 }
419 
420 /* Set the initial metric sort per module/panel.
421  *
422  * On error, function returns.
423  * On success, panel metrics are sorted. */
424 void
set_initial_sort(const char * smod,const char * sfield,const char * ssort)425 set_initial_sort (const char *smod, const char *sfield, const char *ssort) {
426   int module, field, order;
427   if ((module = get_module_enum (smod)) == -1)
428     return;
429 
430   if ((field = get_sort_field_enum (sfield)) == -1)
431     return;
432   if ((order = get_sort_order_enum (ssort)) == -1)
433     return;
434   if (!can_sort_module (module, field))
435     return;
436 
437   module_sort[module].field = field;
438   module_sort[module].sort = order;
439 }
440 
441 /* Determine if module/panel metric can be sorted.
442  *
443  * On error or if metric can't be sorted, 0 is returned.
444  * On success, 1 is returned. */
445 int
can_sort_module(GModule module,int field)446 can_sort_module (GModule module, int field) {
447   int i, can_sort = 0;
448   for (i = 0; -1 != sort_choices[module][i]; i++) {
449     if (sort_choices[module][i] != field)
450       continue;
451     if (SORT_BY_AVGTS == field && !conf.serve_usecs)
452       continue;
453     if (SORT_BY_CUMTS == field && !conf.serve_usecs)
454       continue;
455     if (SORT_BY_MAXTS == field && !conf.serve_usecs)
456       continue;
457     else if (SORT_BY_BW == field && !conf.bandwidth)
458       continue;
459     else if (SORT_BY_PROT == field && !conf.append_protocol)
460       continue;
461     else if (SORT_BY_MTHD == field && !conf.append_method)
462       continue;
463 
464     can_sort = 1;
465     break;
466   }
467 
468   return can_sort;
469 }
470 
471 /* Parse all initial sort options from the config file.
472  *
473  * On error, function returns.
474  * On success, panel metrics are sorted. */
475 void
parse_initial_sort(void)476 parse_initial_sort (void) {
477   int i;
478   char module[SORT_MODULE_LEN], field[SORT_FIELD_LEN], order[SORT_ORDER_LEN];
479   for (i = 0; i < conf.sort_panel_idx; ++i) {
480     if (sscanf (conf.sort_panels[i], "%15[^','],%11[^','],%4s", module, field, order) != 3)
481       continue;
482     set_initial_sort (module, field, order);
483   }
484 }
485 
486 /* Apply user defined sort */
487 void
sort_holder_items(GHolderItem * items,int size,GSort sort)488 sort_holder_items (GHolderItem * items, int size, GSort sort) {
489   switch (sort.field) {
490   case SORT_BY_HITS:
491     if (sort.sort == SORT_DESC)
492       qsort (items, size, sizeof (GHolderItem), cmp_num_desc);
493     else
494       qsort (items, size, sizeof (GHolderItem), cmp_num_asc);
495     break;
496   case SORT_BY_VISITORS:
497     if (sort.sort == SORT_DESC)
498       qsort (items, size, sizeof (GHolderItem), cmp_vis_desc);
499     else
500       qsort (items, size, sizeof (GHolderItem), cmp_vis_asc);
501     break;
502   case SORT_BY_DATA:
503     if (sort.sort == SORT_DESC)
504       qsort (items, size, sizeof (GHolderItem), cmp_data_desc);
505     else
506       qsort (items, size, sizeof (GHolderItem), cmp_data_asc);
507     break;
508   case SORT_BY_BW:
509     if (sort.sort == SORT_DESC)
510       qsort (items, size, sizeof (GHolderItem), cmp_bw_desc);
511     else
512       qsort (items, size, sizeof (GHolderItem), cmp_bw_asc);
513     break;
514   case SORT_BY_AVGTS:
515     if (sort.sort == SORT_DESC)
516       qsort (items, size, sizeof (GHolderItem), cmp_avgts_desc);
517     else
518       qsort (items, size, sizeof (GHolderItem), cmp_avgts_asc);
519     break;
520   case SORT_BY_CUMTS:
521     if (sort.sort == SORT_DESC)
522       qsort (items, size, sizeof (GHolderItem), cmp_cumts_desc);
523     else
524       qsort (items, size, sizeof (GHolderItem), cmp_cumts_asc);
525     break;
526   case SORT_BY_MAXTS:
527     if (sort.sort == SORT_DESC)
528       qsort (items, size, sizeof (GHolderItem), cmp_maxts_desc);
529     else
530       qsort (items, size, sizeof (GHolderItem), cmp_maxts_asc);
531     break;
532   case SORT_BY_PROT:
533     if (sort.sort == SORT_DESC)
534       qsort (items, size, sizeof (GHolderItem), cmp_proto_desc);
535     else
536       qsort (items, size, sizeof (GHolderItem), cmp_proto_asc);
537     break;
538   case SORT_BY_MTHD:
539     if (sort.sort == SORT_DESC)
540       qsort (items, size, sizeof (GHolderItem), cmp_mthd_desc);
541     else
542       qsort (items, size, sizeof (GHolderItem), cmp_mthd_asc);
543     break;
544   }
545 }
546 
547 /* Sort raw numeric data in a descending order for the first run
548  * (default sort)
549  *
550  * On success, raw data sorted in a descending order. */
551 GRawData *
sort_raw_num_data(GRawData * raw_data,int ht_size)552 sort_raw_num_data (GRawData * raw_data, int ht_size) {
553   qsort (raw_data->items, ht_size, sizeof *(raw_data->items), cmp_raw_num_desc);
554   return raw_data;
555 }
556 
557 /* Sort raw string data in a descending order for the first run.
558  *
559  * On success, raw data sorted in a descending order. */
560 GRawData *
sort_raw_str_data(GRawData * raw_data,int ht_size)561 sort_raw_str_data (GRawData * raw_data, int ht_size) {
562   qsort (raw_data->items, ht_size, sizeof *(raw_data->items), cmp_raw_str_desc);
563   return raw_data;
564 }
565