Add an additional "title" attribute for UAT fields; that's what's
[obnox/wireshark/wip.git] / epan / stats_tree.c
1 /* stats_tree.c
2  * API for a counter tree for Wireshark
3  * 2004, Luis E. G. Ontanon
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <glib.h>
31 #include <epan/stats_tree_priv.h>
32 #include <string.h>
33
34 #include "stats_tree.h"
35
36 /*
37 TODO: 
38    - sort out the sorting issue
39  
40  */
41
42 /* used to contain the registered stat trees */
43 static GHashTable *registry = NULL;
44
45 /* writes into the buffers pointed by value, rate and percent
46    the string representations of a node*/
47 extern void
48 stats_tree_get_strs_from_node(const stat_node *node, gchar *value, gchar *rate, gchar *percent)
49 {
50         float f;
51         
52         if (value) g_snprintf(value,NUM_BUF_SIZE,"%u",node->counter);
53         
54         if (rate) {
55                 *rate = '\0';
56                 if (node->st->elapsed > 0.0) {
57                         f = ((float)node->counter) / (float)node->st->elapsed;
58                         g_snprintf(rate,NUM_BUF_SIZE,"%f",f);
59                 }
60         }
61         
62         if (percent) {
63                 *percent = '\0';
64                 if (node->parent->counter > 0) {
65                         f = (float)(((float)node->counter * 100.0) / node->parent->counter);
66                         g_snprintf(percent,NUM_BUF_SIZE,"%.2f%%",f);
67                 }
68         }
69 }
70
71
72 /* a text representation of a node
73 if buffer is NULL returns a newly allocated string */
74 extern gchar*
75 stats_tree_node_to_str(const stat_node *node, gchar *buffer, guint len)
76 {
77         if (buffer) {
78                 g_snprintf(buffer,len,"%s: %i",node->name, node->counter);
79                 return buffer;
80         } else {
81                 return g_strdup_printf("%s: %i",node->name, node->counter);
82         }
83 }
84
85 extern guint
86 stats_tree_branch_max_namelen(const stat_node *node, guint indent)
87 {
88         stat_node *child;
89         guint maxlen = 0;
90         guint len;
91         
92         indent = indent > INDENT_MAX ? INDENT_MAX : indent;
93
94         if (node->children) {
95                 for (child = node->children; child; child = child->next ) {
96                         len = stats_tree_branch_max_namelen(child,indent+1); 
97                         maxlen = len > maxlen ? len : maxlen;
98                 }
99         }
100         
101         len = strlen(node->name) + indent;
102         maxlen = len > maxlen ? len : maxlen;
103         
104         return maxlen;
105 }
106
107 static gchar *format;
108
109 /* populates the given GString with a tree representation of a branch given by node,
110 using indent spaces as initial indentation */
111 extern void
112 stats_tree_branch_to_str(const stat_node *node, GString *s, guint indent)
113 {
114         stat_node *child;
115         static gchar indentation[INDENT_MAX+1];
116         static gchar value[NUM_BUF_SIZE];
117         static gchar rate[NUM_BUF_SIZE];
118         static gchar percent[NUM_BUF_SIZE];
119         
120         guint i = 0;
121         
122         if (indent == 0) {
123                 format = g_strdup_printf(" %%s%%-%us%%12s   %%12s    %%12s\n",stats_tree_branch_max_namelen(node,0));
124         }
125         
126         stats_tree_get_strs_from_node(node, value, rate, percent);
127         
128         indent = indent > INDENT_MAX ? INDENT_MAX : indent;
129         
130         /* fill indentation with indent spaces */
131         if (indent > 0) {
132                 while(i<indent)
133                         indentation[i++] = ' ';
134         }
135         
136         indentation[i++] = '\0';
137         
138         g_string_append_printf(s,format,
139                                           indentation,node->name,value,rate,percent);
140                 
141         if (node->children) {
142                 for (child = node->children; child; child = child->next ) {
143                         stats_tree_branch_to_str(child,s,indent+1);
144                 }
145         }
146         
147         if (indent == 0) {
148                 g_free(format);
149         }
150 }
151
152
153 /* frees the resources allocated by a stat_tree node */
154 static void
155 free_stat_node(stat_node *node)
156 {
157         stat_node *child;
158         stat_node *next;
159         
160         if (node->children) {
161         for (child = node->children; child; child = next ) {
162             /* child->next will be gone after free_stat_node, so cache it here */
163             next = child->next;
164                         free_stat_node(child);
165         }
166         }
167         
168         if(node->st->cfg->free_node_pr) node->st->cfg->free_node_pr(node);
169         
170         if (node->hash) g_hash_table_destroy(node->hash);
171
172         if (node->rng) g_free(node->rng);
173         
174         if (node->name) g_free(node->name);
175         
176         g_free(node);
177 }
178
179 /* destroys the whole tree instance */
180 extern void
181 stats_tree_free(stats_tree *st)
182 {
183         stat_node *child;
184         stat_node *next;
185         
186         g_free(st->filter);
187         g_hash_table_destroy(st->names);
188         g_ptr_array_free(st->parents,TRUE);
189         
190         for (child = st->root.children; child; child = next ) {
191                 /* child->next will be gone after free_stat_node, so cache it here */
192                 next = child->next;
193                 free_stat_node(child);
194         }
195         
196         if (st->cfg->free_tree_pr)
197                 st->cfg->free_tree_pr(st);
198         
199         if (st->cfg->cleanup)
200                 st->cfg->cleanup(st);
201         
202         g_free(st);
203 }
204
205
206 /* reset a node to its original state */
207 static void
208 reset_stat_node(stat_node *node)
209 {
210         stat_node *child;
211         
212         if (node->children) {
213                 for (child = node->children; child; child = child->next ) 
214                         reset_stat_node(child);
215         }
216         
217         node->counter = 0;
218         
219         if(node->st->cfg->reset_node) {
220                 node->st->cfg->reset_node(node);
221         }
222         
223 }
224
225 /* reset the whole stats_tree */
226 extern void
227 stats_tree_reset(void *p)
228 {
229         stats_tree *st = p;
230     
231         st->start = -1.0;
232         st->elapsed = 0.0;
233     
234         reset_stat_node(&st->root);
235         
236         if (st->cfg->reset_tree) {
237                 st->cfg->reset_tree(st);
238         }
239 }
240
241 extern void
242 stats_tree_reinit(void *p)
243 {
244         stats_tree *st = p;
245         stat_node *child;
246         stat_node *next;
247         
248         for (child = st->root.children; child; child = next) {
249         /* child->next will be gone after free_stat_node, so cache it here */
250         next = child->next;
251                 free_stat_node(child);
252         }
253         
254         st->root.children = NULL;
255         st->root.counter = 0;
256         
257         if (st->cfg->init) {
258                 st->cfg->init(st);
259         }
260 }
261
262 /* register a new stats_tree */
263 extern void
264 stats_tree_register_with_group(const char *tapname, const char *abbr, const char *name,
265                     stat_tree_packet_cb packet, stat_tree_init_cb init,
266                     stat_tree_cleanup_cb cleanup, register_stat_group_t stat_group)
267 {
268         
269         stats_tree_cfg *cfg = g_malloc( sizeof(stats_tree_cfg) );
270
271         /* at the very least the abbrev and the packet function should be given */ 
272         g_assert( tapname && abbr && packet );
273
274         cfg->tapname = g_strdup(tapname);
275         cfg->abbr = g_strdup(abbr);
276         cfg->name = name ? g_strdup(name) : g_strdup(abbr);
277     cfg->stat_group = stat_group;
278         
279         cfg->packet = packet;
280         cfg->init = init;
281         cfg->cleanup = cleanup;
282         
283         /* these have to be filled in by implementations */
284         cfg->setup_node_pr = NULL;
285         cfg->new_tree_pr = NULL;
286         cfg->free_node_pr = NULL;
287         cfg->free_tree_pr = NULL;
288         cfg->draw_node = NULL;
289         cfg->draw_tree = NULL;
290         cfg->reset_node = NULL;
291         cfg->reset_tree = NULL;
292
293         if (!registry) registry = g_hash_table_new(g_str_hash,g_str_equal);
294
295         g_hash_table_insert(registry,cfg->abbr,cfg);
296         
297 }
298
299 /* register a new stats_tree with default group REGISTER_STAT_GROUP_UNSORTED */
300 extern void
301 stats_tree_register(const char *tapname, const char *abbr, const char *name,
302                     stat_tree_packet_cb packet, stat_tree_init_cb init,
303                     stat_tree_cleanup_cb cleanup)
304 {
305     stats_tree_register_with_group(tapname, abbr, name,
306                     packet, init,
307                     cleanup, REGISTER_STAT_GROUP_UNSORTED);
308 }
309
310 extern stats_tree*
311 stats_tree_new(stats_tree_cfg *cfg, tree_pres *pr, char *filter)
312 {
313         stats_tree *st = g_malloc(sizeof(stats_tree));
314
315         st->cfg = cfg;
316         st->pr = pr;
317
318         st->names = g_hash_table_new(g_str_hash,g_str_equal);
319         st->parents = g_ptr_array_new();
320         st->filter = g_strdup(filter);
321         
322         st->start = -1.0;
323         st->elapsed = 0.0;
324
325         st->root.counter = 0;
326         st->root.name = g_strdup(cfg->name);
327         st->root.st = st;
328         st->root.parent = NULL;
329         st->root.children = NULL;
330         st->root.next = NULL;
331         st->root.hash = NULL;
332         st->root.pr = NULL;
333         
334         g_ptr_array_add(st->parents,&st->root);
335         
336         return st;
337 }       
338
339 /* will be the tap packet cb */
340 extern int
341 stats_tree_packet(void *p, packet_info *pinfo, epan_dissect_t *edt, const void *pri)
342 {
343         stats_tree *st = p;
344         double now = nstime_to_msec(&pinfo->fd->rel_ts);
345         
346         if (st->start < 0.0) st->start = now;
347         
348         st->elapsed = now - st->start;
349         
350         if (st->cfg->packet)
351                 return st->cfg->packet(st,pinfo,edt,pri);
352         else
353                 return 0;
354 }
355
356 extern stats_tree_cfg*
357 stats_tree_get_cfg_by_abbr(char *abbr)
358 {
359         return g_hash_table_lookup(registry,abbr);
360 }
361
362
363 struct _stats_tree_pres_cbs {
364         void (*setup_node_pr)(stat_node*);
365         void (*free_node_pr)(stat_node*);
366         void (*draw_node)(stat_node*);
367         void (*reset_node)(stat_node*);
368         tree_pres *(*new_tree_pr)(stats_tree*);
369         void (*free_tree_pr)(stats_tree*);
370         void (*draw_tree)(stats_tree*);
371         void (*reset_tree)(stats_tree*);
372 };
373
374 static void
375 setup_tree_presentation(gpointer k _U_, gpointer v, gpointer p)
376 {
377         stats_tree_cfg *cfg = v;
378         struct _stats_tree_pres_cbs *d = p;
379         
380         cfg->in_use = FALSE;
381         cfg->setup_node_pr = d->setup_node_pr;
382         cfg->new_tree_pr = d->new_tree_pr;
383         cfg->free_node_pr = d->free_node_pr;
384         cfg->free_tree_pr = d->free_tree_pr;
385         cfg->draw_node = d->draw_node;
386         cfg->draw_tree = d->draw_tree;
387         cfg->reset_node = d->reset_node;
388         cfg->reset_tree = d->reset_tree;
389         
390 }
391
392 extern void
393 stats_tree_presentation(void (*registry_iterator)(gpointer,gpointer,gpointer),
394                         void (*setup_node_pr)(stat_node*),
395                         void (*free_node_pr)(stat_node*),
396                         void (*draw_node)(stat_node*),
397                         void (*reset_node)(stat_node*),
398                         tree_pres *(*new_tree_pr)(stats_tree*),
399                         void (*free_tree_pr)(stats_tree*),
400                         void (*draw_tree)(stats_tree*),
401                         void (*reset_tree)(stats_tree*),
402                         void *data)
403 {
404         static struct _stats_tree_pres_cbs d;
405         
406         d.setup_node_pr = setup_node_pr;
407         d.new_tree_pr = new_tree_pr;
408         d.free_node_pr = free_node_pr;
409         d.free_tree_pr = free_tree_pr;
410         d.draw_node = draw_node;
411         d.draw_tree = draw_tree;
412         d.reset_node = reset_node;
413         d.reset_tree = reset_tree;
414         
415         if (registry) g_hash_table_foreach(registry,setup_tree_presentation,&d);
416         
417         if (registry_iterator && registry)
418                 g_hash_table_foreach(registry,registry_iterator,data);
419         
420 }
421
422
423 /* creates a stat_tree node
424 *    name: the name of the stats_tree node
425 *    parent_name: the name of the ALREADY REGISTERED parent
426 *    with_hash: whether or not it should keep a hash with it's children names
427 *    as_named_node: whether or not it has to be registered in the root namespace
428 */
429 static stat_node*
430 new_stat_node(stats_tree *st, const gchar *name, int parent_id,
431               gboolean with_hash, gboolean as_parent_node)
432 {
433
434         stat_node *node = g_malloc (sizeof(stat_node));
435         stat_node *last_chld = NULL;
436         
437         node->counter = 0;
438         node->name = g_strdup(name);
439         node->children = NULL;
440         node->next = NULL;
441         node->st = (stats_tree*) st;
442         node->hash = with_hash ? g_hash_table_new(g_str_hash,g_str_equal) : NULL;
443         node->parent = NULL;
444         node->rng  =  NULL;
445
446         if (as_parent_node) {
447                 g_hash_table_insert(st->names,
448                                                         node->name,
449                                                         node);
450                 
451                 g_ptr_array_add(st->parents,node);
452                 
453                 node->id = st->parents->len - 1;
454         } else {
455                 node->id = -1;
456         }
457         
458         if (parent_id >= 0 && parent_id < (int) st->parents->len ) {
459                 node->parent = g_ptr_array_index(st->parents,parent_id);
460         } else {
461                 /* ??? should we set the parent to be root ??? */
462                 g_assert_not_reached();
463         }
464         
465         if (node->parent->children) {
466                 /* insert as last child */
467                 
468                 for (last_chld = node->parent->children;
469                          last_chld->next;
470                          last_chld = last_chld->next ) ;
471                 
472                 last_chld->next = node;
473                 
474         } else {
475                 /* insert as first child */
476                 node->parent->children = node;
477         }
478         
479         if(node->parent->hash) {
480                 g_hash_table_insert(node->parent->hash,node->name,node);
481         }
482         
483         if (st->cfg->setup_node_pr) {
484                 st->cfg->setup_node_pr(node);
485         } else {
486                 node->pr = NULL;
487         }
488         
489         return node;
490 }
491 /***/
492
493 extern int
494 stats_tree_create_node(stats_tree *st, const gchar *name, int parent_id, gboolean with_hash)
495 {
496         stat_node *node = new_stat_node(st,name,parent_id,with_hash,TRUE);
497         
498         if (node) 
499                 return node->id;
500         else
501                 return 0;
502 }
503
504 /* XXX: should this be a macro? */
505 extern int
506 stats_tree_create_node_by_pname(stats_tree *st, const gchar *name,
507                                 const gchar *parent_name, gboolean with_children)
508 {
509         return stats_tree_create_node(st,name,stats_tree_parent_id_by_name(st,parent_name),with_children);
510 }
511
512
513
514 /*
515  * Increases by delta the counter of the node whose name is given
516  * if the node does not exist yet it's created (with counter=1)
517  * using parent_name as parent node.
518  * with_hash=TRUE to indicate that the created node will have a parent
519  */
520 extern int
521 stats_tree_manip_node(manip_node_mode mode, stats_tree *st, const char *name,
522                       int parent_id, gboolean with_hash, gint value)
523 {
524         stat_node *node = NULL;
525         stat_node *parent = NULL;
526         
527         g_assert( parent_id >= 0 && parent_id < (int) st->parents->len );
528         
529         parent = g_ptr_array_index(st->parents,parent_id);
530         
531         if( parent->hash ) {
532                 node = g_hash_table_lookup(parent->hash,name);
533         } else {
534                 node = g_hash_table_lookup(st->names,name);
535         }
536         
537         if ( node == NULL ) 
538                 node = new_stat_node(st,name,parent_id,with_hash,with_hash);
539         
540         switch (mode) {
541                 case MN_INCREASE: node->counter += value; break;
542                 case MN_SET: node->counter = value; break;
543         }
544         
545         if (node) 
546                 return node->id;
547         else
548                 return -1;
549 }
550
551
552 extern char*
553 stats_tree_get_abbr(const char *optarg)
554 {
555         guint i;
556
557         /* XXX: this fails when tshark is given any options
558            after the -z */
559         g_assert(optarg != NULL);
560         
561         for (i=0; optarg[i] && optarg[i] != ','; i++);
562         
563         if (optarg[i] == ',') {
564                 return g_strndup(optarg,i);
565         } else {
566                 return NULL;
567         }
568 }
569
570
571 /*
572  * This function accepts an input string which should define a long integer range.
573  * The normal result is a struct containing the floor and ceil value of this
574  * range.
575  *
576  * It is allowed to define a range string in the following ways :
577  *
578  * "0-10" -> { 0, 10 }
579  * "-0" -> { G_MININT, 0 }
580  * "0-" -> { 0, G_MAXINT }
581  * "-" -> { G_MININT, G_MAXINT }
582  *
583  * Note that this function is robust to buggy input string. If in some cases it
584  * returns NULL, it but may also return a pair with undefined values.
585  *
586  */
587 static range_pair_t*
588 get_range(char *rngstr)
589 {
590         gchar **split;
591         range_pair_t *rng;
592
593         split = g_strsplit((gchar*)rngstr,"-",2);
594
595         /* empty string */
596         if (split[0] == NULL) {
597           g_strfreev(split);
598           return NULL;
599         }
600
601         /* means we have a non empty string 
602          * which does not contain a delimiter */
603         if (split[1] == NULL) {
604           g_strfreev(split);
605           return NULL;
606         }
607
608         rng = g_malloc(sizeof(range_pair_t));
609
610         /* string == "X-?" */
611         if (*(split[0]) != '\0') {
612             rng->floor = strtol(split[0],NULL,10);
613         } else
614           /* string == "-?" */
615           rng->floor = G_MININT;
616
617         /* string != "?-" */
618         if (*(split[1]) != '\0') {
619           rng->ceil  = strtol(split[1],NULL,10);
620         } else
621           /* string == "?-" */
622           rng->ceil = G_MAXINT;
623
624         g_strfreev(split);
625
626         return rng;
627 }
628
629
630 extern int
631 stats_tree_create_range_node(stats_tree *st, const gchar *name, int parent_id, ...)
632 {
633         va_list list;
634         gchar *curr_range;
635         stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
636         stat_node *range_node = NULL;
637
638         va_start( list, parent_id );
639         while (( curr_range = va_arg(list, gchar*) )) {
640                 range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
641                 range_node->rng = get_range(curr_range);
642         }
643         va_end( list );
644
645         return rng_root->id;
646 }
647
648 /****/
649 extern int
650 stats_tree_parent_id_by_name(stats_tree *st, const gchar *parent_name)
651 {
652         stat_node *node = g_hash_table_lookup(st->names,parent_name);
653         
654         if (node)
655                 return node->id;
656         else
657                 return 0; /* XXX: this is the root shoud we return -1 instead?*/
658 }
659
660
661 extern int
662 stats_tree_range_node_with_pname(stats_tree *st, const gchar *name,
663                                  const gchar *parent_name, ...)
664 {
665         va_list list;
666         gchar *curr_range;
667         stat_node *range_node = NULL;
668         int parent_id = stats_tree_parent_id_by_name(st,parent_name);
669         stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
670
671         va_start( list, parent_name );
672         while (( curr_range = va_arg(list, gchar*) )) {
673                 range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
674                 range_node->rng = get_range(curr_range);
675         }
676         va_end( list );
677         
678         return rng_root->id;
679 }       
680
681
682 extern int
683 stats_tree_tick_range(stats_tree *st, const gchar *name, int parent_id,
684                       int value_in_range)
685 {
686         
687         stat_node *node = NULL;
688         stat_node *parent = NULL;
689         stat_node *child = NULL;
690         gint floor, ceil;
691         
692         if (parent_id >= 0 && parent_id < (int) st->parents->len) {
693                 parent = g_ptr_array_index(st->parents,parent_id);
694         } else {
695                 g_assert_not_reached();
696         }
697         
698         if( parent->hash ) {
699                 node = g_hash_table_lookup(parent->hash,name);
700         } else {
701                 node = g_hash_table_lookup(st->names,name);
702         }
703         
704         if ( node == NULL ) 
705                 g_assert_not_reached();
706         
707         for ( child = node->children; child; child = child->next) {
708                 floor =  child->rng->floor;
709                 ceil = child->rng->ceil;
710                 
711                 if ( value_in_range >= floor && value_in_range <= ceil ) {
712                         child->counter++;
713                         return node->id;
714                 }
715         }
716         
717         return node->id;
718 }
719
720 extern int
721 stats_tree_create_pivot(stats_tree *st, const gchar *name, int parent_id)
722 {
723         stat_node *node = new_stat_node(st,name,parent_id,TRUE,TRUE);
724         
725         if (node) 
726                 return node->id;
727         else
728                 return 0;
729 }
730
731 extern int
732 stats_tree_create_pivot_by_pname(stats_tree *st, const gchar *name,
733                                  const gchar *parent_name)
734 {
735         int parent_id = stats_tree_parent_id_by_name(st,parent_name);
736         stat_node *node;
737         
738         node = new_stat_node(st,name,parent_id,TRUE,TRUE);
739         
740         if (node) 
741                 return node->id;
742         else
743                 return 0;
744 }
745
746 extern int
747 stats_tree_tick_pivot(stats_tree *st, int pivot_id, const gchar *pivot_value)
748 {
749         
750         stat_node *parent = g_ptr_array_index(st->parents,pivot_id);
751         
752         parent->counter++;
753         stats_tree_manip_node( MN_INCREASE, st, pivot_value, pivot_id, FALSE, 1);
754         
755         return pivot_id;
756 }
757