Coverage Report

Created: 2026-05-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/stats_tree.c
Line
Count
Source
1
/* stats_tree.c
2
 * API for a counter tree for Wireshark
3
 * 2004, Luis E. G. Ontanon
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
 /* stats_tree modifications by Deon van der Westhuysen, November 2013
13
  * support for
14
  *  - sorting by column,
15
  *  - calculation of average values
16
  *  - calculation of burst rate
17
  *  - export to text, CSV or XML file
18
  */
19
20
#include "config.h"
21
22
#include <glib.h>
23
24
#include <stdlib.h>
25
26
#include <epan/stats_tree_priv.h>
27
#include <epan/prefs.h>
28
#include <math.h>
29
#include <string.h>
30
31
#include "strutil.h"
32
#include "stats_tree.h"
33
#include <wsutil/ws_assert.h>
34
35
enum _stat_tree_columns {
36
    COL_NAME,
37
    COL_COUNT,
38
    COL_AVERAGE,
39
    COL_MIN,
40
    COL_MAX,
41
    COL_RATE,
42
    COL_PERCENT,
43
    COL_BURSTRATE,
44
    COL_BURSTTIME,
45
    N_COLUMNS
46
};
47
48
/* used to contain the registered stat trees */
49
static GHashTable *registry;
50
51
/* a text representation of a node
52
if buffer is NULL returns a newly allocated string */
53
char*
54
stats_tree_node_to_str(const stat_node *node, char *buffer, unsigned len)
55
0
{
56
0
    if (buffer) {
57
0
        snprintf(buffer,len,"%s: %i",node->name, node->counter);
58
0
        return buffer;
59
0
    } else {
60
0
        return ws_strdup_printf("%s: %i",node->name, node->counter);
61
0
    }
62
0
}
63
64
unsigned
65
// NOLINTNEXTLINE(misc-no-recursion)
66
stats_tree_branch_max_namelen(const stat_node *node, unsigned indent)
67
0
{
68
0
    stat_node *child;
69
0
    unsigned maxlen = 0;
70
0
    unsigned len;
71
72
0
    indent = indent > INDENT_MAX ? INDENT_MAX : indent;
73
74
0
    if (node->children) {
75
0
        for (child = node->children; child; child = child->next ) {
76
            // Recursion is limited by proto.c checks
77
0
            len = stats_tree_branch_max_namelen(child,indent+1);
78
0
            maxlen = len > maxlen ? len : maxlen;
79
0
        }
80
0
    }
81
82
0
    if (node->st_flags&ST_FLG_ROOTCHILD) {
83
0
        char *display_name = stats_tree_get_displayname(node->name);
84
0
        len = (unsigned) strlen(display_name) + indent;
85
0
        g_free(display_name);
86
0
    }
87
0
    else {
88
0
    len = (unsigned) strlen(node->name) + indent;
89
0
    }
90
0
    maxlen = len > maxlen ? len : maxlen;
91
92
0
    return maxlen;
93
0
}
94
95
/* frees the resources allocated by a stat_tree node */
96
static void
97
// NOLINTNEXTLINE(misc-no-recursion)
98
free_stat_node(stat_node *node)
99
0
{
100
0
    stat_node *child;
101
0
    stat_node *next;
102
0
    burst_bucket *bucket;
103
104
0
    if (node->children) {
105
0
    for (child = node->children; child; child = next ) {
106
        /* child->next will be gone after free_stat_node, so cache it here */
107
0
        next = child->next;
108
        // Recursion is limited by proto.c checks
109
0
        free_stat_node(child);
110
0
    }
111
0
    }
112
113
0
    if (node->hash) g_hash_table_destroy(node->hash);
114
115
0
    while (node->bh) {
116
0
        bucket = node->bh;
117
0
        node->bh = bucket->next;
118
0
        g_free(bucket);
119
0
    }
120
121
0
    g_free(node->rng);
122
0
    g_free(node->name);
123
0
    g_free(node);
124
0
}
125
126
/* destroys the whole tree instance */
127
void
128
stats_tree_free(stats_tree *st)
129
0
{
130
0
    stat_node *child;
131
0
    stat_node *next;
132
133
0
    if (!st) return;
134
135
0
    g_free(st->filter);
136
0
    g_hash_table_destroy(st->names);
137
0
    g_ptr_array_free(st->parents,true);
138
0
    g_free(st->display_name);
139
140
    /* st->root is not allocated with malloc, so we have to call
141
     * free_stat_node on each child and free the root's dynamically
142
     * allocated members instead of calling free_stat_node(&str->root)
143
     */
144
0
    for (child = st->root.children; child; child = next ) {
145
        /* child->next will be gone after free_stat_node, so cache it here */
146
0
        next = child->next;
147
0
        free_stat_node(child);
148
0
    }
149
0
    g_free(st->root.name);
150
0
    g_free(st->root.bh);
151
152
0
    if (st->cfg->free_tree_pr)
153
0
        st->cfg->free_tree_pr(st);
154
155
0
    if (st->cfg->cleanup)
156
0
        st->cfg->cleanup(st);
157
158
0
    g_free(st);
159
0
}
160
161
162
/* reset a node to its original state */
163
static void
164
// NOLINTNEXTLINE(misc-no-recursion)
165
reset_stat_node(stat_node *node)
166
0
{
167
0
    stat_node *child;
168
0
    burst_bucket *bucket;
169
170
0
    node->counter = 0;
171
0
    switch (node->datatype)
172
0
    {
173
0
    case STAT_DT_INT:
174
0
        node->total.int_total = 0;
175
0
        node->minvalue.int_min = INT_MAX;
176
0
        node->maxvalue.int_max = INT_MIN;
177
0
        break;
178
0
    case STAT_DT_FLOAT:
179
0
        node->total.float_total = 0;
180
0
        node->minvalue.float_min = FLT_MAX;
181
0
        node->maxvalue.float_max = -FLT_MAX;
182
0
        break;
183
0
    }
184
0
    node->st_flags = 0;
185
186
0
    while (node->bh) {
187
0
        bucket = node->bh;
188
0
        node->bh = bucket->next;
189
0
        g_free(bucket);
190
0
    }
191
0
    node->bh = g_new0(burst_bucket, 1);
192
0
    node->bt = node->bh;
193
0
    node->bcount = 0;
194
0
    node->max_burst = 0;
195
0
    node->burst_time = -1.0;
196
197
0
    if (node->children) {
198
0
        for (child = node->children; child; child = child->next )
199
            // Recursion is limited by proto.c checks
200
0
            reset_stat_node(child);
201
0
    }
202
0
}
203
204
/* reset the whole stats_tree */
205
void
206
stats_tree_reset(void *p)
207
0
{
208
0
    stats_tree *st = (stats_tree *)p;
209
210
0
    st->start = -1.0;
211
0
    st->elapsed = 0.0;
212
0
    st->now = - 1.0;
213
214
0
    reset_stat_node(&st->root);
215
0
}
216
217
void
218
stats_tree_reinit(void *p)
219
0
{
220
0
    stats_tree *st = (stats_tree *)p;
221
0
    stat_node *child;
222
0
    stat_node *next;
223
224
0
    for (child = st->root.children; child; child = next) {
225
        /* child->next will be gone after free_stat_node, so cache it here */
226
0
        next = child->next;
227
0
        free_stat_node(child);
228
0
    }
229
230
0
    st->root.children = NULL;
231
0
    st->root.counter = 0;
232
0
    switch (st->root.datatype)
233
0
    {
234
0
    case STAT_DT_INT:
235
0
        st->root.total.int_total = 0;
236
0
        st->root.minvalue.int_min = INT_MAX;
237
0
        st->root.maxvalue.int_max = INT_MIN;
238
0
        break;
239
0
    case STAT_DT_FLOAT:
240
0
        st->root.total.float_total = 0;
241
0
        st->root.minvalue.float_min = FLT_MAX;
242
0
        st->root.maxvalue.float_max = -FLT_MAX;
243
0
        break;
244
0
    }
245
0
    st->root.st_flags = 0;
246
247
0
    g_free(st->root.bh);
248
0
    st->root.bh = g_new0(burst_bucket, 1);
249
0
    st->root.bt = st->root.bh;
250
0
    st->root.bcount = 0;
251
0
    st->root.max_burst = 0;
252
0
    st->root.burst_time = -1.0;
253
254
    /* No more stat_nodes left in tree - clean out hash, array */
255
0
    g_hash_table_remove_all(st->names);
256
0
    if (st->parents->len>1) {
257
0
        g_ptr_array_remove_range(st->parents, 1, st->parents->len-1);
258
0
    }
259
260
    /* Do not update st_flags for the tree (sorting) - leave as was */
261
0
    st->num_columns = N_COLUMNS;
262
    /* XXX - Do we really need to recreate the display name? */
263
0
    g_free(st->display_name);
264
0
    st->display_name = stats_tree_get_displayname(st->cfg->path);
265
266
0
    if (st->cfg->init) {
267
0
        st->cfg->init(st);
268
0
    }
269
0
}
270
271
static void
272
stats_tree_free_configuration(void *p)
273
0
{
274
0
    stats_tree_cfg* cfg = (stats_tree_cfg*)p;
275
0
    g_free(cfg->tapname);
276
0
    g_free(cfg->abbr);
277
0
    g_free(cfg->path);
278
0
    g_free(cfg->title);
279
0
    g_free(cfg->first_column_name);
280
0
    g_free(cfg);
281
0
}
282
283
void
284
stats_tree_init(void)
285
15
{
286
15
    registry = g_hash_table_new_full(g_str_hash,g_str_equal,NULL,stats_tree_free_configuration);
287
15
}
288
289
/* register a new stats_tree */
290
stats_tree_cfg *
291
stats_tree_register(const char *tapname, const char *abbr, const char *path,
292
            unsigned flags,
293
            stat_tree_packet_cb packet, stat_tree_init_cb init,
294
            stat_tree_cleanup_cb cleanup)
295
720
{
296
720
    stats_tree_cfg *cfg = g_new0(stats_tree_cfg, 1);
297
298
    /* at the very least the abbrev and the packet function should be given */
299
720
    ws_assert( tapname && abbr && packet );
300
301
720
    cfg->tapname = g_strdup(tapname);
302
720
    cfg->abbr = g_strdup(abbr);
303
720
    cfg->path = path ? g_strdup(path) : g_strdup(abbr);
304
720
    cfg->stat_group = REGISTER_PACKET_STAT_GROUP_UNSORTED;
305
306
720
    GString *title_str = g_string_new("");
307
720
    char **split = g_strsplit(path, STATS_TREE_MENU_SEPARATOR, 0);
308
720
    const char *sep = "";
309
1.77k
    for (size_t idx = 0; split[idx]; idx++) {
310
1.05k
        g_string_append_printf(title_str, "%s%s", sep, g_strstrip(split[idx]));
311
1.05k
        sep = " / ";
312
1.05k
    }
313
720
    g_strfreev(split);
314
720
    cfg->title = g_string_free(title_str, FALSE);
315
316
720
    cfg->packet = packet;
317
720
    cfg->init = init;
318
720
    cfg->cleanup = cleanup;
319
320
720
    cfg->flags = flags&~ST_FLG_MASK;
321
720
    cfg->st_flags = flags&ST_FLG_MASK;
322
323
720
    if (!registry) registry = g_hash_table_new_full(g_str_hash,g_str_equal,NULL,stats_tree_free_configuration);
324
325
720
    g_hash_table_insert(registry,cfg->abbr,cfg);
326
327
720
    return cfg;
328
720
}
329
330
/* register a new stat_tree with default group REGISTER_PACKET_STAT_GROUP_UNSORTED from a plugin */
331
stats_tree_cfg *
332
stats_tree_register_plugin(const char *tapname, const char *abbr, const char *path,
333
            unsigned flags,
334
            stat_tree_packet_cb packet, stat_tree_init_cb init,
335
            stat_tree_cleanup_cb cleanup)
336
135
{
337
135
    stats_tree_cfg *cfg = stats_tree_register(tapname, abbr, path,
338
135
            flags, packet, init, cleanup);
339
135
    cfg->plugin = true;
340
341
135
    return cfg;
342
135
}
343
344
void
345
45
stats_tree_set_group(stats_tree_cfg *st_config, register_stat_group_t stat_group) {
346
45
    if (st_config) {
347
45
        st_config->stat_group = stat_group;
348
45
    }
349
45
}
350
351
void
352
75
stats_tree_set_first_column_name(stats_tree_cfg *st_config, const char *column_name) {
353
75
    if (st_config) {
354
75
        st_config->first_column_name = g_strdup(column_name);
355
75
    }
356
75
}
357
358
stats_tree*
359
stats_tree_new(stats_tree_cfg *cfg, tree_pres *pr, const char *filter)
360
0
{
361
0
    stats_tree *st = g_new0(stats_tree, 1);
362
363
0
    st->cfg = cfg;
364
0
    st->pr = pr;
365
366
0
    st->names = g_hash_table_new(g_str_hash,g_str_equal);
367
0
    st->parents = g_ptr_array_new();
368
0
    st->filter = g_strdup(filter);
369
370
0
    st->start = -1.0;
371
0
    st->elapsed = 0.0;
372
373
0
    switch (st->root.datatype)
374
0
    {
375
0
    case STAT_DT_INT:
376
0
        st->root.minvalue.int_min = INT_MAX;
377
0
        st->root.maxvalue.int_max = INT_MIN;
378
0
        break;
379
0
    case STAT_DT_FLOAT:
380
0
        st->root.minvalue.float_min = FLT_MAX;
381
0
        st->root.maxvalue.float_max = -FLT_MAX;
382
0
        break;
383
0
    }
384
385
0
    st->root.bh = g_new0(burst_bucket, 1);
386
0
    st->root.bt = st->root.bh;
387
0
    st->root.burst_time = -1.0;
388
389
0
    st->root.name = stats_tree_get_displayname(cfg->path);
390
0
    st->root.st = st;
391
392
0
    st->st_flags = st->cfg->st_flags;
393
394
0
    if (!(st->st_flags&ST_FLG_SRTCOL_MASK)) {
395
        /* No default sort specified - use preferences */
396
0
        st->st_flags |= prefs.st_sort_defcolflag<<ST_FLG_SRTCOL_SHIFT;
397
0
        if (prefs.st_sort_defdescending) {
398
0
            st->st_flags |= ST_FLG_SORT_DESC;
399
0
        }
400
0
    }
401
0
    st->num_columns = N_COLUMNS;
402
0
    st->display_name = stats_tree_get_displayname(st->cfg->path);
403
404
0
    g_ptr_array_add(st->parents,&st->root);
405
406
0
    return st;
407
0
}
408
409
/* will be the tap packet cb */
410
tap_packet_status
411
stats_tree_packet(void *p, packet_info *pinfo, epan_dissect_t *edt, const void *pri, tap_flags_t flags)
412
0
{
413
0
    stats_tree *st = (stats_tree *)p;
414
415
0
    st->now = nstime_to_msec(&pinfo->rel_ts);
416
0
    if (st->start < 0.0) st->start = st->now;
417
418
0
    st->elapsed = st->now - st->start;
419
420
0
    if (st->cfg->packet)
421
0
        return st->cfg->packet(st,pinfo,edt,pri, flags);
422
0
    else
423
0
        return TAP_PACKET_DONT_REDRAW;
424
0
}
425
426
stats_tree_cfg*
427
stats_tree_get_cfg_by_abbr(const char *abbr)
428
0
{
429
0
    if (!abbr) return NULL;
430
0
    return (stats_tree_cfg *)g_hash_table_lookup(registry,abbr);
431
0
}
432
433
static int
434
compare_stat_menu_item(const void *stat_a, const void *stat_b)
435
0
{
436
0
    const stats_tree_cfg* stat_cfg_a = (const stats_tree_cfg*)stat_a;
437
0
    const stats_tree_cfg* stat_cfg_b = (const stats_tree_cfg*)stat_b;
438
439
0
    return strcmp(stat_cfg_a->path, stat_cfg_b->path);
440
0
}
441
442
GList*
443
stats_tree_get_cfg_list(void)
444
0
{
445
0
    GList* registry_list = g_hash_table_get_values(registry);
446
    /* Now sort the list so they can show up in the
447
       menu alphabetically */
448
0
    return g_list_sort(registry_list, compare_stat_menu_item);
449
450
0
}
451
452
struct _stats_tree_pres_cbs {
453
    void (*setup_node_pr)(stat_node*);
454
    void (*free_tree_pr)(stats_tree*);
455
};
456
457
static void
458
setup_tree_presentation(void *k _U_, void *v, void *p)
459
0
{
460
0
    stats_tree_cfg *cfg = (stats_tree_cfg *)v;
461
0
    struct _stats_tree_pres_cbs *d = (struct _stats_tree_pres_cbs *)p;
462
463
0
    cfg->setup_node_pr = d->setup_node_pr;
464
0
    cfg->free_tree_pr = d->free_tree_pr;
465
466
0
}
467
468
void
469
stats_tree_presentation(void (*registry_iterator)(void *,void *,void *),
470
            void (*setup_node_pr)(stat_node*),
471
            void (*free_tree_pr)(stats_tree*),
472
            void *data)
473
0
{
474
0
    static struct _stats_tree_pres_cbs d;
475
476
0
    d.setup_node_pr = setup_node_pr;
477
0
    d.free_tree_pr = free_tree_pr;
478
479
0
    if (registry) g_hash_table_foreach(registry,setup_tree_presentation,&d);
480
481
0
    if (registry_iterator && registry)
482
0
        g_hash_table_foreach(registry,registry_iterator,data);
483
484
0
}
485
486
487
/* creates a stat_tree node
488
*    name: the name of the stats_tree node
489
*    parent_name: the name of the ALREADY REGISTERED parent
490
*    with_hash: whether or not it should keep a hash with its children names
491
*    as_named_node: whether or not it has to be registered in the root namespace
492
*/
493
static stat_node*
494
new_stat_node(stats_tree *st, const char *name, int parent_id, stat_node_datatype datatype,
495
          bool with_hash, bool as_parent_node)
496
0
{
497
498
0
    stat_node *node = g_new0(stat_node, 1);
499
0
    stat_node *last_chld = NULL;
500
501
0
    node->datatype = datatype;
502
0
    switch (datatype)
503
0
    {
504
0
    case STAT_DT_INT:
505
0
        node->minvalue.int_min = INT_MAX;
506
0
        node->maxvalue.int_max = INT_MIN;
507
0
        break;
508
0
    case STAT_DT_FLOAT:
509
0
        node->minvalue.float_min = FLT_MAX;
510
0
        node->maxvalue.float_max = -FLT_MAX;
511
0
        break;
512
0
    }
513
0
    node->st_flags = parent_id?0:ST_FLG_ROOTCHILD;
514
515
0
    node->bh = g_new0(burst_bucket, 1);
516
0
    node->bt = node->bh;
517
0
    node->burst_time = -1.0;
518
519
0
    node->name = g_strdup(name);
520
0
    node->st = st;
521
0
    node->hash = with_hash ? g_hash_table_new(g_str_hash,g_str_equal) : NULL;
522
523
0
    if (as_parent_node) {
524
0
        g_hash_table_insert(st->names,
525
0
                            node->name,
526
0
                            node);
527
528
0
        g_ptr_array_add(st->parents,node);
529
530
0
        node->id = st->parents->len - 1;
531
0
    } else {
532
0
        node->id = -1;
533
0
    }
534
535
0
    if (parent_id >= 0 && parent_id < (int) st->parents->len ) {
536
0
        node->parent = (stat_node *)g_ptr_array_index(st->parents,parent_id);
537
0
    } else {
538
        /* ??? should we set the parent to be root ??? */
539
0
        ws_assert_not_reached();
540
0
    }
541
542
0
    if (node->parent->children) {
543
        /* insert as last child */
544
545
0
        for (last_chld = node->parent->children;
546
0
             last_chld->next;
547
0
             last_chld = last_chld->next ) ;
548
549
0
        last_chld->next = node;
550
551
0
    } else {
552
        /* insert as first child */
553
0
        node->parent->children = node;
554
0
    }
555
556
0
    if(node->parent->hash) {
557
0
        g_hash_table_replace(node->parent->hash,node->name,node);
558
0
    }
559
560
0
    if (st->cfg->setup_node_pr) {
561
0
        st->cfg->setup_node_pr(node);
562
0
    } else {
563
0
        node->pr = NULL;
564
0
    }
565
566
0
    return node;
567
0
}
568
/***/
569
570
int
571
stats_tree_create_node(stats_tree *st, const char *name, int parent_id, stat_node_datatype datatype, bool with_hash)
572
0
{
573
0
    stat_node *node = new_stat_node(st,name,parent_id,datatype,with_hash,true);
574
575
0
    if (node)
576
0
        return node->id;
577
0
    else
578
0
        return 0;
579
0
}
580
581
/* XXX: should this be a macro? */
582
int
583
stats_tree_create_node_by_pname(stats_tree *st, const char *name,
584
                const char *parent_name, stat_node_datatype datatype, bool with_children)
585
0
{
586
0
    return stats_tree_create_node(st,name,stats_tree_parent_id_by_name(st,parent_name),datatype,with_children);
587
0
}
588
589
/* Internal function to update the burst calculation data - add entry to bucket */
590
static void
591
update_burst_calc(stat_node *node, int value)
592
0
{
593
0
    double current_bucket;
594
0
    double burstwin;
595
596
0
    burst_bucket *bn;
597
598
0
    if (!prefs.st_enable_burstinfo) {
599
0
        return;
600
0
    }
601
602
    /* NB thebucket list should always contain at least one node - even if it is */
603
    /* the dummy created at init time. Head and tail should never be NULL!       */
604
0
    current_bucket = floor(node->st->now/prefs.st_burst_resolution);
605
0
    burstwin = prefs.st_burst_windowlen/prefs.st_burst_resolution;
606
0
    if (current_bucket>node->bt->bucket_no) {
607
        /* Must add a new bucket at the burst list tail */
608
0
        bn = g_new0(burst_bucket, 1);
609
0
        bn->count = value;
610
0
        bn->bucket_no = current_bucket;
611
0
        bn->start_time = node->st->now;
612
0
        bn->prev = node->bt;
613
0
        node->bt->next = bn;
614
0
        node->bt = bn;
615
        /* And add value to the current burst count for node */
616
0
        node->bcount += value;
617
        /* Check if bucket list head is now too old and must be removed */
618
0
        while (current_bucket>=(node->bh->bucket_no+burstwin)) {
619
            /* off with its head! */
620
0
            bn = node->bh;
621
0
            node->bh = bn->next;
622
0
            node->bh->prev = NULL;
623
0
            node->bcount -= bn->count;
624
0
            g_free(bn);
625
0
        }
626
0
    }
627
0
    else if (current_bucket<node->bh->bucket_no) {
628
        /* Packet must be added at head of burst list - check if not too old */
629
0
        if ((current_bucket+burstwin)>node->bt->bucket_no) {
630
            /* packet still within the window */
631
0
            bn = g_new0(burst_bucket, 1);
632
0
            bn->count = value;
633
0
            bn->bucket_no = current_bucket;
634
0
            bn->start_time = node->st->now;
635
0
            bn->next = node->bh;
636
0
            node->bh->prev = bn;
637
0
            node->bh = bn;
638
            /* And add value to the current burst count for node */
639
0
            node->bcount += value;
640
0
        }
641
0
    }
642
0
    else
643
0
    {
644
        /* Somewhere in the middle... */
645
0
        burst_bucket *search = node->bt;
646
0
        while (current_bucket<search->bucket_no) {
647
0
            search = search->prev;
648
0
        }
649
0
        if (current_bucket==search->bucket_no) {
650
            /* found existing bucket, increase value */
651
0
            search->count += value;
652
0
            if (search->start_time>node->st->now) {
653
0
                search->start_time = node->st->now;
654
0
            }
655
0
        }
656
0
        else {
657
            /* must add a new bucket after bn. */
658
0
            bn = g_new0(burst_bucket, 1);
659
0
            bn->count = value;
660
0
            bn->bucket_no = current_bucket;
661
0
            bn->start_time = node->st->now;
662
0
            bn->prev = search;
663
0
            bn->next = search->next;
664
0
            search->next = bn;
665
0
            bn->next->prev = bn;
666
0
        }
667
0
        node->bcount += value;
668
0
    }
669
0
    if (node->bcount>node->max_burst) {
670
        /* new record burst */
671
0
        node->max_burst = node->bcount;
672
0
        node->burst_time = node->bh->start_time;
673
0
    }
674
0
}
675
676
/*
677
 * Increases by delta the counter of the node whose name is given
678
 * if the node does not exist yet it's created (with counter=1)
679
 * using parent_name as parent node.
680
 * with_hash=true to indicate that the created node will have a parent
681
 */
682
int
683
stats_tree_manip_node_int(manip_node_mode mode, stats_tree *st, const char *name,
684
              int parent_id, bool with_hash, int value)
685
0
{
686
0
    stat_node *node = NULL;
687
0
    stat_node *parent = NULL;
688
689
0
    ws_assert( parent_id >= 0 && parent_id < (int) st->parents->len );
690
691
0
    parent = (stat_node *)g_ptr_array_index(st->parents,parent_id);
692
693
0
    if( parent->hash ) {
694
0
        node = (stat_node *)g_hash_table_lookup(parent->hash,name);
695
0
    } else {
696
0
        node = (stat_node *)g_hash_table_lookup(st->names,name);
697
0
    }
698
699
0
    if ( node == NULL )
700
0
        node = new_stat_node(st,name,parent_id,STAT_DT_INT,with_hash,with_hash);
701
702
0
    switch (mode) {
703
0
        case MN_INCREASE:
704
0
            node->counter += value;
705
0
            update_burst_calc(node, value);
706
0
            break;
707
0
        case MN_SET:
708
0
            node->counter = value;
709
0
            break;
710
0
        case MN_AVERAGE:
711
0
            node->counter++;
712
0
            update_burst_calc(node, 1);
713
            /* fall through */ /*to average code */
714
0
        case MN_AVERAGE_NOTICK:
715
0
            node->total.int_total += value;
716
0
            if (node->minvalue.int_min > value) {
717
0
                node->minvalue.int_min = value;
718
0
            }
719
0
            if (node->maxvalue.int_max < value) {
720
0
                node->maxvalue.int_max = value;
721
0
            }
722
0
            node->st_flags |= ST_FLG_AVERAGE;
723
0
            break;
724
0
        case MN_SET_FLAGS:
725
0
            node->st_flags |= value;
726
0
            break;
727
0
        case MN_CLEAR_FLAGS:
728
0
            node->st_flags &= ~value;
729
0
            break;
730
0
    }
731
732
0
    if (node)
733
0
        return node->id;
734
0
    else
735
0
        return -1;
736
0
}
737
738
/*
739
* Increases by delta the counter of the node whose name is given
740
* if the node does not exist yet it's created (with counter=1)
741
* using parent_name as parent node.
742
* with_hash=true to indicate that the created node will have a parent
743
*/
744
int
745
stats_tree_manip_node_float(manip_node_mode mode, stats_tree *st, const char *name,
746
    int parent_id, bool with_hash, float value)
747
0
{
748
0
    stat_node *node = NULL;
749
0
    stat_node *parent = NULL;
750
751
0
    ws_assert(parent_id >= 0 && parent_id < (int)st->parents->len);
752
753
0
    parent = (stat_node *)g_ptr_array_index(st->parents, parent_id);
754
755
0
    if (parent->hash) {
756
0
        node = (stat_node *)g_hash_table_lookup(parent->hash, name);
757
0
    }
758
0
    else {
759
0
        node = (stat_node *)g_hash_table_lookup(st->names, name);
760
0
    }
761
762
0
    if (node == NULL)
763
0
        node = new_stat_node(st, name, parent_id, STAT_DT_FLOAT, with_hash, with_hash);
764
765
0
    switch (mode) {
766
0
    case MN_AVERAGE:
767
0
        node->counter++;
768
0
        update_burst_calc(node, 1);
769
        /* fall through */ /*to average code */
770
0
    case MN_AVERAGE_NOTICK:
771
0
        node->total.float_total += value;
772
0
        if (node->minvalue.float_min > value) {
773
0
            node->minvalue.float_min = value;
774
0
        }
775
0
        if (node->maxvalue.float_max < value) {
776
0
            node->maxvalue.float_max = value;
777
0
        }
778
0
        node->st_flags |= ST_FLG_AVERAGE;
779
0
        break;
780
0
    default:
781
        //only average is currently supported
782
0
        ws_assert_not_reached();
783
0
        break;
784
0
    }
785
786
0
    if (node)
787
0
        return node->id;
788
0
    else
789
0
        return -1;
790
0
}
791
792
char*
793
stats_tree_get_abbr(const char *opt_arg)
794
0
{
795
0
    unsigned i;
796
797
    /* XXX: this fails when tshark is given any options
798
       after the -z */
799
0
    ws_assert(opt_arg != NULL);
800
801
0
    for (i=0; opt_arg[i] && opt_arg[i] != ','; i++);
802
803
0
    if (opt_arg[i] == ',') {
804
0
        return g_strndup(opt_arg,i);
805
0
    } else {
806
0
        return NULL;
807
0
    }
808
0
}
809
810
811
/*
812
 * This function accepts an input string which should define a long integer range.
813
 * The normal result is a struct containing the floor and ceil value of this
814
 * range.
815
 *
816
 * It is allowed to define a range string in the following ways :
817
 *
818
 * "0-10" -> { 0, 10 }
819
 * "-0" -> { INT_MIN, 0 }
820
 * "0-" -> { 0, INT_MAX }
821
 * "-" -> { INT_MIN, INT_MAX }
822
 *
823
 * Note that this function is robust to buggy input string. If in some cases it
824
 * returns NULL, it but may also return a pair with undefined values.
825
 *
826
 */
827
static range_pair_t*
828
get_range(char *rngstr)
829
0
{
830
0
    char **split;
831
0
    range_pair_t *rng;
832
833
0
    split = g_strsplit((char*)rngstr,"-",2);
834
835
    /* empty string */
836
0
    if (split[0] == NULL) {
837
0
        g_strfreev(split);
838
0
        return NULL;
839
0
    }
840
841
0
    rng = g_new(range_pair_t, 1);
842
843
0
    if (split[1] == NULL) {
844
        /* means we have a non empty string with no delimiter
845
         * so it must be a single number */
846
0
        rng->floor = (int)strtol(split[0],NULL,10);
847
0
        rng->ceil = rng->floor;
848
0
    } else {
849
      /* string == "X-?" */
850
0
        if (*(split[0]) != '\0') {
851
0
            rng->floor = (int)strtol(split[0],NULL,10);
852
0
        } else {
853
            /* string == "-?" */
854
0
            rng->floor = INT_MIN;
855
0
        }
856
857
        /* string != "?-" */
858
0
        if (*(split[1]) != '\0') {
859
0
            rng->ceil  = (int)strtol(split[1],NULL,10);
860
0
        } else {
861
            /* string == "?-" */
862
0
            rng->ceil = INT_MAX;
863
0
        }
864
0
    }
865
0
    g_strfreev(split);
866
867
0
    return rng;
868
0
}
869
870
871
int
872
stats_tree_create_range_node(stats_tree *st, const char *name, int parent_id, ...)
873
0
{
874
0
    va_list list;
875
0
    char *curr_range;
876
0
    stat_node *rng_root = new_stat_node(st, name, parent_id, STAT_DT_INT, false, true);
877
0
    stat_node *range_node = NULL;
878
879
0
    va_start( list, parent_id );
880
0
    while (( curr_range = va_arg(list, char*) )) {
881
0
        range_node = new_stat_node(st, curr_range, rng_root->id, STAT_DT_INT, false, false);
882
0
        range_node->rng = get_range(curr_range);
883
0
    }
884
0
    va_end( list );
885
886
0
    return rng_root->id;
887
0
}
888
889
int
890
stats_tree_create_range_node_string(stats_tree *st, const char *name,
891
                    int parent_id, int num_str_ranges,
892
                    char** str_ranges)
893
0
{
894
0
    int i;
895
0
    stat_node *rng_root = new_stat_node(st, name, parent_id, STAT_DT_INT, false, true);
896
0
    stat_node *range_node = NULL;
897
898
0
    for (i = 0; i < num_str_ranges - 1; i++) {
899
0
        range_node = new_stat_node(st, str_ranges[i], rng_root->id, STAT_DT_INT, false, false);
900
0
        range_node->rng = get_range(str_ranges[i]);
901
0
    }
902
0
    range_node = new_stat_node(st, str_ranges[i], rng_root->id, STAT_DT_INT, false, false);
903
0
    range_node->rng = get_range(str_ranges[i]);
904
0
    if (range_node->rng->floor == range_node->rng->ceil) {
905
0
        range_node->rng->ceil = INT_MAX;
906
0
    }
907
908
0
    return rng_root->id;
909
0
}
910
911
/****/
912
int
913
stats_tree_parent_id_by_name(stats_tree *st, const char *parent_name)
914
0
{
915
0
    stat_node *node = (stat_node *)g_hash_table_lookup(st->names,parent_name);
916
917
0
    if (node)
918
0
        return node->id;
919
0
    else
920
0
        return 0; /* XXX: this is the root should we return -1 instead?*/
921
0
}
922
923
924
int
925
stats_tree_range_node_with_pname(stats_tree *st, const char *name,
926
                 const char *parent_name, ...)
927
0
{
928
0
    va_list list;
929
0
    char *curr_range;
930
0
    stat_node *range_node = NULL;
931
0
    int parent_id = stats_tree_parent_id_by_name(st,parent_name);
932
0
    stat_node *rng_root = new_stat_node(st, name, parent_id, STAT_DT_INT, false, true);
933
934
0
    va_start( list, parent_name );
935
0
    while (( curr_range = va_arg(list, char*) )) {
936
0
        range_node = new_stat_node(st, curr_range, rng_root->id, STAT_DT_INT, false, false);
937
0
        range_node->rng = get_range(curr_range);
938
0
    }
939
0
    va_end( list );
940
941
0
    return rng_root->id;
942
0
}
943
944
945
int
946
stats_tree_tick_range(stats_tree *st, const char *name, int parent_id,
947
              int value_in_range)
948
0
{
949
950
0
    stat_node *node = NULL;
951
0
    stat_node *parent = NULL;
952
0
    stat_node *child = NULL;
953
0
    int stat_floor, stat_ceil;
954
955
0
    if (parent_id >= 0 && parent_id < (int) st->parents->len) {
956
0
        parent = (stat_node *)g_ptr_array_index(st->parents,parent_id);
957
0
    } else {
958
0
        ws_assert_not_reached();
959
0
    }
960
961
0
    if( parent->hash ) {
962
0
        node = (stat_node *)g_hash_table_lookup(parent->hash,name);
963
0
    } else {
964
0
        node = (stat_node *)g_hash_table_lookup(st->names,name);
965
0
    }
966
967
0
    if ( node == NULL )
968
0
        ws_assert_not_reached();
969
970
    /* update stats for container node. counter should already be ticked so we only update total and min/max */
971
0
    node->total.int_total += value_in_range;
972
0
    if (node->minvalue.int_min > value_in_range) {
973
0
        node->minvalue.int_min = value_in_range;
974
0
    }
975
0
    if (node->maxvalue.int_max < value_in_range) {
976
0
        node->maxvalue.int_max = value_in_range;
977
0
    }
978
0
    node->st_flags |= ST_FLG_AVERAGE;
979
980
0
    for ( child = node->children; child; child = child->next) {
981
0
        stat_floor =  child->rng->floor;
982
0
        stat_ceil = child->rng->ceil;
983
984
0
        if ( value_in_range >= stat_floor && value_in_range <= stat_ceil ) {
985
0
            child->counter++;
986
0
            child->total.int_total += value_in_range;
987
0
            if (child->minvalue.int_min > value_in_range) {
988
0
                child->minvalue.int_min = value_in_range;
989
0
            }
990
0
            if (child->maxvalue.int_max < value_in_range) {
991
0
                child->maxvalue.int_max = value_in_range;
992
0
            }
993
0
            child->st_flags |= ST_FLG_AVERAGE;
994
0
            update_burst_calc(child, 1);
995
0
            return node->id;
996
0
        }
997
0
    }
998
999
0
    return node->id;
1000
0
}
1001
1002
int
1003
stats_tree_create_pivot(stats_tree *st, const char *name, int parent_id)
1004
0
{
1005
0
    stat_node *node = new_stat_node(st,name,parent_id,STAT_DT_INT,true,true);
1006
1007
0
    if (node)
1008
0
        return node->id;
1009
0
    else
1010
0
        return 0;
1011
0
}
1012
1013
int
1014
stats_tree_create_pivot_by_pname(stats_tree *st, const char *name,
1015
                 const char *parent_name)
1016
0
{
1017
0
    int parent_id = stats_tree_parent_id_by_name(st,parent_name);
1018
0
    stat_node *node;
1019
1020
0
    node = new_stat_node(st,name,parent_id,STAT_DT_INT,true,true);
1021
1022
0
    if (node)
1023
0
        return node->id;
1024
0
    else
1025
0
        return 0;
1026
0
}
1027
1028
int
1029
stats_tree_tick_pivot(stats_tree *st, int pivot_id, const char *pivot_value)
1030
0
{
1031
0
    stat_node *parent = (stat_node *)g_ptr_array_index(st->parents,pivot_id);
1032
1033
0
    parent->counter++;
1034
0
    update_burst_calc(parent, 1);
1035
0
    stats_tree_manip_node_int( MN_INCREASE, st, pivot_value, pivot_id, false, 1);
1036
1037
0
    return pivot_id;
1038
0
}
1039
1040
char*
1041
stats_tree_get_displayname (const char* fullname)
1042
0
{
1043
0
    char *buf = g_strdup(fullname);
1044
0
    char *sep;
1045
1046
0
    if (prefs.st_sort_showfullname) {
1047
0
        return buf; /* unmodified */
1048
0
    }
1049
1050
0
    sep = buf;
1051
0
    while ((sep = strchr(sep,'/')) != NULL) {
1052
0
        if (*(++sep)=='/') {  /* escaped slash - two slash characters after each other */
1053
0
            memmove(sep,sep+1,strlen(sep));
1054
0
        }
1055
0
        else {
1056
            /* we got a new path separator */
1057
0
            memmove(buf,sep,strlen(sep)+1);
1058
0
            sep = buf;
1059
0
        }
1060
0
    }
1061
1062
0
    return buf;
1063
0
}
1064
1065
int
1066
stats_tree_get_default_sort_col (stats_tree *st)
1067
0
{
1068
0
    switch ((st->st_flags&ST_FLG_SRTCOL_MASK)>>ST_FLG_SRTCOL_SHIFT) {
1069
0
        case ST_SORT_COL_NAME:
1070
0
            return COL_NAME;
1071
0
        case ST_SORT_COL_COUNT:
1072
0
            return COL_COUNT;
1073
0
        case ST_SORT_COL_AVG:
1074
0
            return COL_AVERAGE;
1075
0
        case ST_SORT_COL_MIN:
1076
0
            return COL_MIN;
1077
0
        case ST_SORT_COL_MAX:
1078
0
            return COL_MAX;
1079
0
        case ST_SORT_COL_BURSTRATE:
1080
0
            return COL_BURSTRATE;
1081
0
    }
1082
0
    return COL_COUNT;   /* nothing specific set */
1083
0
}
1084
1085
bool
1086
stats_tree_is_default_sort_DESC (stats_tree *st)
1087
0
{
1088
0
    return st->st_flags&ST_FLG_SORT_DESC;
1089
0
}
1090
1091
const char*
1092
stats_tree_get_column_name (stats_tree_cfg *st_config, int col_index)
1093
0
{
1094
0
    switch (col_index) {
1095
0
        case COL_NAME:
1096
0
            if (st_config->first_column_name) {
1097
0
                return st_config->first_column_name;
1098
0
            }
1099
0
            return "Topic / Item";
1100
0
        case COL_COUNT:
1101
0
            return "Count";
1102
0
        case COL_AVERAGE:
1103
0
            return "Average";
1104
0
        case COL_MIN:
1105
0
            return "Min Val";
1106
0
        case COL_MAX:
1107
0
            return "Max Val";
1108
0
        case COL_RATE:
1109
0
            return "Rate (ms)";
1110
0
        case COL_PERCENT:
1111
0
            return "Percent";
1112
0
        case COL_BURSTRATE:
1113
0
            return prefs.st_burst_showcount ? "Burst Count" : "Burst Rate";
1114
0
        case COL_BURSTTIME:
1115
0
            return "Burst Start";
1116
0
        default:
1117
0
            return "(Unknown)";
1118
0
    }
1119
0
}
1120
1121
int
1122
stats_tree_get_column_size (int col_index)
1123
0
{
1124
0
    if (col_index==COL_NAME) {
1125
0
        return 36;      /* but caller should really call stats_tree_branch_max_namelen() */
1126
0
    }
1127
0
    if (col_index<N_COLUMNS) {
1128
0
        return 12;      /* all numerical values are this size */
1129
0
    }
1130
0
    return 0;           /* invalid column */
1131
0
}
1132
1133
char**
1134
stats_tree_get_values_from_node (const stat_node* node)
1135
0
{
1136
0
    char **values = (char**) g_malloc0(sizeof(char*)*(node->st->num_columns));
1137
1138
0
    values[COL_NAME] = (node->st_flags&ST_FLG_ROOTCHILD)?stats_tree_get_displayname(node->name):g_strdup(node->name);
1139
0
    values[COL_COUNT] = ws_strdup_printf("%u",node->counter);
1140
0
    if (((node->st_flags&ST_FLG_AVERAGE) || node->rng)) {
1141
0
        if (node->counter) {
1142
0
            switch (node->datatype)
1143
0
            {
1144
0
            case STAT_DT_INT:
1145
0
                values[COL_AVERAGE] = ws_strdup_printf("%.2f", ((float)node->total.int_total) / node->counter);
1146
0
                break;
1147
0
            case STAT_DT_FLOAT:
1148
0
                values[COL_AVERAGE] = ws_strdup_printf("%.2f", node->total.float_total / node->counter);
1149
0
                break;
1150
0
            }
1151
0
        } else {
1152
0
            values[COL_AVERAGE] = g_strdup("-");
1153
0
        }
1154
0
    } else {
1155
0
        values[COL_AVERAGE] = g_strdup("");
1156
0
    }
1157
1158
0
    if (((node->st_flags&ST_FLG_AVERAGE) || node->rng)) {
1159
0
        if (node->counter) {
1160
0
            switch (node->datatype)
1161
0
            {
1162
0
            case STAT_DT_INT:
1163
0
                values[COL_MIN] = ws_strdup_printf("%d", node->minvalue.int_min);
1164
0
                break;
1165
0
            case STAT_DT_FLOAT:
1166
0
                values[COL_MIN] = ws_strdup_printf("%f", node->minvalue.float_min);
1167
0
                break;
1168
0
            }
1169
0
        }
1170
0
        else {
1171
0
            values[COL_MIN] = g_strdup("-");
1172
0
        }
1173
0
    }
1174
0
    else {
1175
0
        values[COL_MIN] = g_strdup("");
1176
0
    }
1177
1178
0
    if (((node->st_flags&ST_FLG_AVERAGE) || node->rng)) {
1179
0
        if (node->counter) {
1180
0
            switch (node->datatype)
1181
0
            {
1182
0
            case STAT_DT_INT:
1183
0
                values[COL_MAX] = ws_strdup_printf("%d", node->maxvalue.int_max);
1184
0
                break;
1185
0
            case STAT_DT_FLOAT:
1186
0
                values[COL_MAX] = ws_strdup_printf("%f", node->maxvalue.float_max);
1187
0
                break;
1188
0
            }
1189
0
        }
1190
0
        else {
1191
0
            values[COL_MAX] = g_strdup("-");
1192
0
        }
1193
0
    }
1194
0
    else {
1195
0
        values[COL_MAX] = g_strdup("");
1196
0
    }
1197
1198
0
    values[COL_RATE] = (node->st->elapsed)?ws_strdup_printf("%.4f",((float)node->counter)/node->st->elapsed):g_strdup("");
1199
0
    values[COL_PERCENT] = ((node->parent)&&(node->parent->counter))?
1200
0
                ws_strdup_printf("%.2f%%",(node->counter*100.0)/node->parent->counter):
1201
0
                (node->parent==&(node->st->root)?g_strdup("100%"):g_strdup(""));
1202
0
    if (node->st->num_columns>COL_BURSTTIME) {
1203
0
        values[COL_BURSTRATE] = (!prefs.st_enable_burstinfo)?g_strdup(""):
1204
0
                (node->max_burst?(prefs.st_burst_showcount?
1205
0
                                ws_strdup_printf("%d",node->max_burst):
1206
0
                                ws_strdup_printf("%.4f",((double)node->max_burst)/prefs.st_burst_windowlen)):
1207
0
                g_strdup("-"));
1208
0
        values[COL_BURSTTIME] = (!prefs.st_enable_burstinfo)?g_strdup(""):
1209
0
                (node->max_burst?ws_strdup_printf("%.3f",(node->burst_time/1000.0)):g_strdup("-"));
1210
0
    }
1211
0
    return values;
1212
0
}
1213
1214
int
1215
stats_tree_sort_compare (const stat_node *a, const stat_node *b, int sort_column,
1216
                    bool sort_descending)
1217
0
{
1218
0
    int result = 0;
1219
0
    float avg_a = 0, avg_b = 0;
1220
1221
0
    if  (prefs.st_sort_rng_nameonly&&(a->rng&&b->rng)) {
1222
        /* always sort ranges by range name */
1223
0
        result = a->rng->floor - b->rng->floor;
1224
0
        if (sort_descending&&(!prefs.st_sort_rng_fixorder)) {
1225
0
            result = -result;
1226
0
        }
1227
0
        return result;
1228
0
    }
1229
1230
0
    switch (sort_column) {
1231
0
        case COL_NAME:
1232
0
            if  (a->rng&&b->rng) {
1233
0
                result = a->rng->floor - b->rng->floor;
1234
0
            }
1235
0
            else if (prefs.st_sort_casesensitve) {
1236
0
                result = strcmp(a->name,b->name);
1237
0
            }
1238
0
            else {
1239
0
                result = g_ascii_strcasecmp(a->name,b->name);
1240
0
            }
1241
0
            break;
1242
1243
0
        case COL_RATE:
1244
0
        case COL_PERCENT:
1245
0
        case COL_COUNT:
1246
0
            result = a->counter - b->counter;
1247
0
            break;
1248
1249
0
        case COL_AVERAGE:
1250
0
            switch (a->datatype)
1251
0
            {
1252
0
            case STAT_DT_INT:
1253
0
                avg_a = a->counter ? ((float)a->total.int_total)/a->counter : 0;
1254
0
                avg_b = b->counter ? ((float)b->total.int_total)/b->counter : 0;
1255
0
                break;
1256
0
            case STAT_DT_FLOAT:
1257
0
                avg_a = a->counter ? ((float)a->total.float_total) / a->counter : 0;
1258
0
                avg_b = b->counter ? ((float)b->total.float_total) / b->counter : 0;
1259
0
                break;
1260
0
            }
1261
0
            result = (avg_a>avg_b) ? 1 : ( (avg_a<avg_b) ? -1 : 0);
1262
0
            break;
1263
1264
0
        case COL_MIN:
1265
0
            switch (a->datatype)
1266
0
            {
1267
0
            case STAT_DT_INT:
1268
0
                result = a->minvalue.int_min - b->minvalue.int_min;
1269
0
                break;
1270
0
            case STAT_DT_FLOAT:
1271
0
                result = (a->minvalue.float_min>b->minvalue.int_min) ? 1 : ((a->minvalue.float_min<b->minvalue.int_min) ? -1 : 0);
1272
0
                break;
1273
0
            }
1274
0
            break;
1275
1276
0
        case COL_MAX:
1277
0
            switch (a->datatype)
1278
0
            {
1279
0
            case STAT_DT_INT:
1280
0
                result = a->maxvalue.int_max - b->maxvalue.int_max;
1281
0
                break;
1282
0
            case STAT_DT_FLOAT:
1283
0
                result = (a->maxvalue.float_max>b->maxvalue.float_max) ? 1 : ((a->maxvalue.float_max<b->maxvalue.float_max) ? -1 : 0);
1284
0
                break;
1285
0
            }
1286
0
            break;
1287
1288
0
        case COL_BURSTRATE:
1289
0
            result = a->max_burst - b->max_burst;
1290
0
            break;
1291
1292
0
        case COL_BURSTTIME:
1293
0
            result = (a->burst_time>b->burst_time)?1:((a->burst_time<b->burst_time)?-1:0);
1294
0
            break;
1295
1296
0
        default:
1297
            /* no sort comparison found for column - must update this switch statement */
1298
0
            ws_assert_not_reached();
1299
0
    }
1300
1301
    /* break tie between items with same primary search result */
1302
0
    if (!result) {
1303
0
        if (sort_column==COL_NAME) {
1304
0
            result = a->counter - b->counter;
1305
0
        }
1306
0
        else {
1307
0
            if  (a->rng&&b->rng) {
1308
0
                result = a->rng->floor - b->rng->floor;
1309
0
            }
1310
0
            else if (prefs.st_sort_casesensitve) {
1311
0
                result = strcmp(a->name,b->name);
1312
0
            }
1313
0
            else {
1314
0
                result = g_ascii_strcasecmp(a->name,b->name);
1315
0
            }
1316
0
        }
1317
0
    }
1318
1319
    /* take into account sort order */
1320
0
    if (sort_descending) {
1321
0
        result = -result;
1322
0
    }
1323
1324
0
    if ((a->st_flags&ST_FLG_SORT_TOP)!=(b->st_flags&ST_FLG_SORT_TOP)) {
1325
        /* different sort groups top vs non-top */
1326
0
        result = (a->st_flags&ST_FLG_SORT_TOP)?-1:1;
1327
0
    }
1328
0
    return result;
1329
0
}
1330
1331
GString*
1332
stats_tree_format_as_str(const stats_tree* st, st_format_type format_type,
1333
                    int sort_column, bool sort_descending)
1334
0
{
1335
0
    int maxnamelen = stats_tree_branch_max_namelen(&st->root,0);
1336
0
    stat_node *child;
1337
0
    GString *s;
1338
0
    int count;
1339
0
    char *separator = NULL;
1340
1341
0
    switch(format_type) {
1342
0
        case ST_FORMAT_YAML:
1343
0
            s = g_string_new("---\n");
1344
0
            break;
1345
0
        case ST_FORMAT_XML:
1346
0
            s = g_string_new("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1347
0
            break;
1348
0
        case ST_FORMAT_CSV:
1349
0
            s = g_string_new("\"level\",\"parent\",");
1350
0
            for (count = 0; count<st->num_columns; count++) {
1351
0
                g_string_append_printf(s,"\"%s\",",stats_tree_get_column_name(st->cfg, count));
1352
0
            }
1353
0
            g_string_append (s,"\n");
1354
0
            break;
1355
0
        case ST_FORMAT_PLAIN:
1356
0
        {
1357
0
            char fmt[16];
1358
0
            int sep_length;
1359
1360
0
            sep_length = maxnamelen;
1361
0
            for (count = 1; count<st->num_columns; count++) {
1362
0
                sep_length += stats_tree_get_column_size(count)+2;
1363
0
            }
1364
0
            separator = (char *)g_malloc(sep_length+1);
1365
0
            memset (separator, '=', sep_length);
1366
0
            separator[sep_length] = 0;
1367
1368
0
            s = g_string_new("\n");
1369
0
            g_string_append(s,separator);
1370
0
            g_string_append_printf(s,"\n%s:\n",st->cfg->title);
1371
0
            snprintf (fmt,sizeof(fmt),"%%-%us",maxnamelen);
1372
0
            g_string_append_printf(s,fmt,stats_tree_get_column_name(st->cfg, 0));
1373
0
            for (count = 1; count<st->num_columns; count++) {
1374
0
                snprintf (fmt,sizeof(fmt)," %%-%ds",stats_tree_get_column_size(count)+1);
1375
0
                g_string_append_printf(s,fmt,stats_tree_get_column_name(st->cfg, count));
1376
0
            }
1377
0
            memset (separator, '-', sep_length);
1378
0
            g_string_append_printf(s,"\n%s\n",separator);
1379
0
            break;
1380
0
        }
1381
0
        default:
1382
0
            return g_string_new("unknown format for stats_tree\n");
1383
0
    }
1384
1385
0
    for (child = st->root.children; child; child = child->next ) {
1386
0
        stats_tree_format_node_as_str(child,s,format_type,0,"",maxnamelen,sort_column,sort_descending);
1387
1388
0
    }
1389
1390
0
    if (format_type==ST_FORMAT_PLAIN) {
1391
0
        g_string_append_printf(s,"\n%s\n",separator);
1392
0
        g_free(separator);
1393
0
    }
1394
1395
0
    return s;
1396
0
}
1397
1398
typedef struct {
1399
    int sort_column;
1400
    bool sort_descending;
1401
}   sortinfo;
1402
1403
/* Function to compare elements for child array sort. a and b are children, user_data
1404
points to a st_flags value */
1405
int
1406
stat_node_array_sortcmp (const void *a, const void *b, void *user_data)
1407
0
{
1408
    /* user_data is *unsigned value to st_flags */
1409
0
    return stats_tree_sort_compare (*(const stat_node*const*)a,*(const stat_node*const*)b,
1410
0
                    ((sortinfo*)user_data)->sort_column,((sortinfo*)user_data)->sort_descending);
1411
0
}
1412
1413
static char*
1414
clean_for_xml_tag (char *str)
1415
0
{
1416
0
    char *s = str;
1417
0
    while ((s=strpbrk(s,"!\"#$%%&'()*+,/;<=>?@[\\]^`{|}~ ")) != NULL) {
1418
0
        *(s++) = '-';
1419
0
    }
1420
0
    return str;
1421
0
}
1422
1423
/** helper function to add note to formatted stats_tree */
1424
// NOLINTNEXTLINE(misc-no-recursion)
1425
void stats_tree_format_node_as_str(const stat_node *node,
1426
                         GString *s,
1427
                         st_format_type format_type,
1428
                         unsigned indent,
1429
                         const char *path,
1430
                         int maxnamelen,
1431
                         int sort_column,
1432
                         bool sort_descending)
1433
0
{
1434
0
    int count;
1435
0
    int num_columns = node->st->num_columns;
1436
0
    char **values = stats_tree_get_values_from_node(node);
1437
0
    stat_node *child;
1438
0
    sortinfo si;
1439
0
    char *full_path;
1440
0
    char fmt[16] = "%s%s%s";
1441
1442
0
    switch(format_type) {
1443
0
        case ST_FORMAT_YAML:
1444
0
            if (indent) {
1445
0
                snprintf(fmt, sizeof(fmt), "%%%ds%%s%%s", indent*4-2);
1446
0
            }
1447
0
            g_string_append_printf(s, fmt, "", indent?"- ":"", "Description");
1448
0
            g_string_append_printf(s, ": \"%s\"\n", values[0]);
1449
1450
0
            for (count = 1; count<num_columns; count++) {
1451
0
                if (*values[count]) {
1452
0
                    g_string_append_printf(s, fmt, "", indent?"  ":"",
1453
0
                                            stats_tree_get_column_name(node->st->cfg, count));
1454
0
                    g_string_append_printf(s, ": %s\n", values[count]);
1455
0
                }
1456
0
            }
1457
0
            if (node->children) {
1458
0
                g_string_append_printf(s, fmt, "", indent?"  ":"", "Items:\n");
1459
0
            }
1460
0
            break;
1461
0
        case ST_FORMAT_XML:
1462
0
        {
1463
0
            char *itemname = xml_escape(values[0]);
1464
0
            g_string_append_printf(s,"<stat-node name=\"%s\"%s>\n",itemname,
1465
0
                    node->rng?" isrange=\"true\"":"");
1466
0
            g_free(itemname);
1467
0
            for (count = 1; count<num_columns; count++) {
1468
0
                char *colname = g_strdup(stats_tree_get_column_name(node->st->cfg, count));
1469
0
                g_string_append_printf(s,"<%s>",clean_for_xml_tag(colname));
1470
0
                g_string_append_printf(s,"%s</%s>\n",values[count],colname);
1471
0
                g_free(colname);
1472
0
            }
1473
0
            break;
1474
0
        }
1475
0
        case ST_FORMAT_CSV:
1476
0
            g_string_append_printf(s,"%d,\"%s\",\"%s\"",indent,path,values[0]);
1477
0
            for (count = 1; count<num_columns; count++) {
1478
0
                g_string_append_printf(s,",%s",values[count]);
1479
0
            }
1480
0
            g_string_append (s,"\n");
1481
0
            break;
1482
0
        case ST_FORMAT_PLAIN:
1483
0
            snprintf (fmt,sizeof(fmt),"%%%ds%%-%us",indent,maxnamelen-indent);
1484
0
            g_string_append_printf(s,fmt,"",values[0]);
1485
0
            for (count = 1; count<num_columns; count++) {
1486
0
                snprintf (fmt,sizeof(fmt)," %%-%us",stats_tree_get_column_size(count)+1);
1487
0
                g_string_append_printf(s,fmt,values[count]);
1488
0
            }
1489
0
            g_string_append (s,"\n");
1490
0
            break;
1491
0
    }
1492
1493
0
    indent++;
1494
0
    indent = indent > INDENT_MAX ? INDENT_MAX : indent;
1495
0
    full_path = ws_strdup_printf ("%s/%s",path,values[0]);
1496
1497
0
    for (count = 0; count<num_columns; count++) {
1498
0
        g_free(values[count]);
1499
0
    }
1500
0
    g_free(values);
1501
1502
0
    if (node->children) {
1503
0
        GArray *Children = g_array_new(false,false,sizeof(child));
1504
0
        for (child = node->children; child; child = child->next ) {
1505
0
            g_array_append_val(Children,child);
1506
0
        }
1507
0
        si.sort_column = sort_column;
1508
0
        si.sort_descending = sort_descending;
1509
0
        g_array_sort_with_data(Children,stat_node_array_sortcmp,&si);
1510
0
        for (count = 0; count<((int)Children->len); count++) {
1511
0
            stats_tree_format_node_as_str(g_array_index(Children,stat_node*,count), s, format_type,
1512
0
                    indent, full_path, maxnamelen, sort_column, sort_descending);
1513
0
        }
1514
0
        g_array_free(Children, true);
1515
0
    }
1516
0
    g_free(full_path);
1517
1518
0
    if (format_type==ST_FORMAT_XML) {
1519
0
        g_string_append(s,"</stat-node>\n");
1520
0
    }
1521
0
}
1522
1523
void stats_tree_cleanup(void)
1524
0
{
1525
0
    g_hash_table_destroy(registry);
1526
0
}
1527
1528
/*
1529
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1530
 *
1531
 * Local variables:
1532
 * c-basic-offset: 4
1533
 * tab-width: 8
1534
 * indent-tabs-mode: nil
1535
 * End:
1536
 *
1537
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1538
 * :indentSize=4:tabSize=8:noTabs=true:
1539
 */