Initial checkin of the stats-tree tap API
authorlego <lego@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 16 Feb 2005 14:16:40 +0000 (14:16 +0000)
committerlego <lego@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 16 Feb 2005 14:16:40 +0000 (14:16 +0000)
Makefiles have not been modified yet, there's still work to do.

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@13414 f5534014-38df-0310-8fa8-9805f1628bb7

epan/stats_tree.c [new file with mode: 0644]
epan/stats_tree.h [new file with mode: 0644]
epan/stats_tree_priv.h [new file with mode: 0644]
gtk/stats_tree_stat.c [new file with mode: 0644]
tap-stats_tree.c [new file with mode: 0644]

diff --git a/epan/stats_tree.c b/epan/stats_tree.c
new file mode 100644 (file)
index 0000000..3efd11f
--- /dev/null
@@ -0,0 +1,532 @@
+/* stats_tree.c
+ * API for a counter tree for ethereal
+ * 2004, Luis E. G. Ontanon
+ *
+ * $Id: $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <epan/stats_tree_priv.h>
+
+/* used to contain the registered stat trees */
+static GHashTable* registry = NULL;
+
+/* writes into the buffers pointed by value, rate and percent
+   the string representations of a node*/
+extern void get_strings_from_node(const stat_node* node, guint8* value, guint8* rate, guint8* percent) {
+       float f;
+       
+       if (value) g_snprintf(value,NUM_BUF_SIZE,"%u",node->counter);
+       
+       if (rate) {
+               *rate = '\0';
+               if (node->st->elapsed > 0.0) {
+                       f = ((float)node->counter) / node->st->elapsed;
+                       g_snprintf(rate,NUM_BUF_SIZE,"%f",f);
+               }
+       }
+       
+       if (percent) {
+               *percent = '\0';
+               if (node->parent->counter > 0) {
+                       f = ((float)node->counter * 100.0) / node->parent->counter;
+                       g_snprintf(percent,NUM_BUF_SIZE,"%.2f%%",f);
+               }
+       }
+}
+
+
+/* a text representation of a node
+if buffer is NULL returns a newly allocated string */
+extern guint8* stat_node_to_str(const stat_node* node,
+                                                               guint8* buffer, guint len) {
+       if (buffer) {
+               g_snprintf(buffer,len,"%s: %i",node->name, node->counter);
+               return buffer;
+       } else {
+               return g_strdup_printf("%s: %i",node->name, node->counter);
+       }
+}
+
+/* populates the given GString with a tree representation of a branch given by node,
+using indent spaces as initial indentation */
+extern void stat_branch_to_str(const stat_node* node, GString* s, guint indent) {
+       stat_node* child;
+       static gchar indentation[INDENT_MAX];
+       static gchar value[NUM_BUF_SIZE];
+       static gchar rate[NUM_BUF_SIZE];
+       static gchar percent[NUM_BUF_SIZE];
+       guint i;
+       
+       get_strings_from_node(node, value, rate, percent);
+       
+       indent = indent > INDENT_MAX ? INDENT_MAX : indent;
+       
+       /* fill indentation with indent spaces */
+       for ( i = 0 ; i<(indent-1); i++) indentation[i] = ' ';
+       indentation[i] = '\0';
+       
+       g_string_sprintfa(s,"%s%s = %s (%s/s) (%s)\n",
+                                         indentation,node->name,value,rate,percent);
+       
+       if (node->children) {
+               for (child = node->children; child; child = child->next ) {
+                       stat_branch_to_str(child,s,indent+1);
+               }
+       }
+}
+
+/* frees the resources allocated by a stat_tree node */
+static void free_stat_node( stat_node* node ) {
+       stat_node* child;
+       
+       if (node->children) {
+               for (child = node->children; child; child = child->next ) 
+                       free_stat_node(child);
+       }
+       
+       if(node->st->free_node_pr) node->st->free_node_pr(node);
+       
+       if (node->hash) g_hash_table_destroy(node->hash);
+       if (node->rng) g_free(node->rng);
+       
+       if (node->name) g_free(node->name);
+       
+       g_free(node);
+}
+
+/* destroys the whole tree */
+extern void free_stats_tree(stats_tree* st) {
+       stat_node* child;
+       
+       g_free(st->abbr);
+       g_free(st->filter);
+       
+       for (child = st->root.children; child; child = child->next ) 
+               free_stat_node(child);
+       
+       if (st->free_tree_pr)
+               st->free_tree_pr(st);
+                       
+       g_free(st);
+}
+
+
+/* reset a node to its original state */
+static void reset_stat_node(stat_node* node) {
+       stat_node* child;
+       
+       if (node->children) {
+               for (child = node->children; child; child = child->next ) 
+                       reset_stat_node(child);
+       }
+       
+       node->counter = 0;
+       
+       if(node->st->reset_node) {
+               node->st->reset_node(node);
+       }
+       
+}
+
+/* reset the whole stats_tree */
+extern void reset_stats_tree(void *psp  ) {
+       stats_tree *st=psp;
+       
+       if (st) {
+               reset_stat_node(&st->root);
+       }
+       
+       if (st->reset_tree) {
+               st->reset_tree(st);
+       }
+}
+
+/* register a new stats_tree */
+extern void register_stats_tree(guint8* abbr, 
+                                                               guint8* name,
+                                                               stat_tree_packet_cb packet,
+                                                               stat_tree_init_cb init ) {
+       
+       stats_tree* st = g_malloc( sizeof(stats_tree) );
+
+       /* at the very least the abbrev and the packet function should be given */ 
+       g_assert( abbr && packet );
+
+       st->abbr = g_strdup(abbr);
+       st->name = name ? g_strdup(name) : g_strdup(abbr);
+       st->filter = NULL;
+       
+       st->root.counter = 0;
+       st->root.name = STAT_TREE_ROOT;
+       st->root.st = st;
+       st->root.parent = NULL;
+       st->root.children = NULL;
+       st->root.next = NULL;
+       st->root.hash = NULL;
+       st->root.pr = NULL;
+       
+       st->names = g_hash_table_new(g_str_hash,g_str_equal);
+       st->parents = g_ptr_array_new();
+       
+       g_ptr_array_add(st->parents,&st->root);
+       
+       st->start = -1.0;
+       st->elapsed = 0.0;
+       
+       st->packet = packet;
+       st->init = init;
+       
+       /* these have to be filled in by implementations */
+       st->setup_node_pr = NULL;
+       st->new_tree_pr = NULL;
+       st->free_node_pr = NULL;
+       st->free_tree_pr = NULL;
+       st->draw_node = NULL;
+       st->draw_tree = NULL;
+       st->reset_node = NULL;
+       st->reset_tree = NULL;
+
+       if (!registry) registry = g_hash_table_new(g_str_hash,g_str_equal);
+
+       g_hash_table_insert(registry,st->abbr,st);
+       
+}
+
+/* will be the tap packet cb */
+extern int stats_tree_packet(void* p, packet_info* pinfo, epan_dissect_t *edt, const void *pri) {
+       stats_tree* st = p;
+       
+       float now = (((float)pinfo->fd->rel_secs) + (((float)pinfo->fd->rel_usecs)/1000000) );
+       
+       if (st->start < 0.0) st->start = now;
+       
+       st->elapsed = now - st->start;
+       
+       if (st->packet)
+               return st->packet(st,pinfo,edt,pri);
+       else
+               return 0;
+}
+
+extern GHashTable* stat_tree_registry(void) {
+       return registry;
+}
+
+extern stats_tree* get_stats_tree_by_abbr(guint8* abbr) {
+       return g_hash_table_lookup(registry,abbr);
+}
+
+
+struct _stats_tree_pres_stuff {
+       void (*setup_node_pr)(stat_node*);
+       void (*free_node_pr)(stat_node*);
+       void (*draw_node)(stat_node*);
+       void (*reset_node)(stat_node*);
+       tree_pres* (*new_tree_pr)(stats_tree*);
+       void (*free_tree_pr)(stats_tree*);
+       void (*draw_tree)(stats_tree*);
+       void (*reset_tree)(stats_tree*);
+};
+
+static void setup_tree_presentation(gpointer k _U_, gpointer v, gpointer p) {
+       stats_tree* st = v;
+       struct _stats_tree_pres_stuff *d = p;
+       
+       st->setup_node_pr = d->setup_node_pr;
+       st->new_tree_pr = d->new_tree_pr;
+       st->free_node_pr = d->free_node_pr;
+       st->free_tree_pr = d->free_tree_pr;
+       st->draw_node = d->draw_node;
+       st->draw_tree = d->draw_tree;
+       st->reset_node = d->reset_node;
+       st->reset_tree = d->reset_tree;
+       
+}
+
+extern void stats_tree_presentation(void (*registry_iterator)(gpointer,gpointer,gpointer),
+                                                                       void (*setup_node_pr)(stat_node*),
+                                                                       void (*free_node_pr)(stat_node*),
+                                                                       void (*draw_node)(stat_node*),
+                                                                       void (*reset_node)(stat_node*),
+                                                                       tree_pres* (*new_tree_pr)(stats_tree*),
+                                                                       void (*free_tree_pr)(stats_tree*),
+                                                                       void (*draw_tree)(stats_tree*),
+                                                                       void (*reset_tree)(stats_tree*),
+                                                                       void* data) {
+       struct _stats_tree_pres_stuff d = {setup_node_pr,free_node_pr,draw_node,reset_node,new_tree_pr,free_tree_pr,draw_tree,reset_tree};
+       
+       if (registry) g_hash_table_foreach(registry,setup_tree_presentation,&d);
+       
+       if (registry_iterator && registry)
+               g_hash_table_foreach(registry,registry_iterator,data);
+       
+}
+
+
+/* creates a stat_tree node
+*    name: the name of the stats_tree node
+*    parent_name: the name of the ALREADY REGISTERED parent
+*    with_hash: whether or not it should keep a hash with it's children names
+*    as_named_node: whether or not it has to be registered in the root namespace
+*/
+static stat_node*  new_stat_node(stats_tree* st,
+                                                                const gchar* name,
+                                                                int parent_id,
+                                                                gboolean with_hash,
+                                                                gboolean as_parent_node) {
+       
+       stat_node *node = g_malloc (sizeof(stat_node));
+       stat_node* last_chld = NULL;
+       
+       node->counter = 0;
+       node->name = g_strdup(name);
+       node->children = NULL;
+       node->next = NULL;
+       node->st = (stats_tree*) st;
+       node->hash = with_hash ? g_hash_table_new(g_str_hash,g_str_equal) : NULL;
+       node->parent = NULL;
+       
+       if (as_parent_node) {
+               g_hash_table_insert(st->names,
+                                                       node->name,
+                                                       node);
+               
+               g_ptr_array_add(st->parents,node);
+               
+               node->id = st->parents->len - 1;
+       } else {
+               node->id = -1;
+       }
+       
+       if (parent_id >= 0 && parent_id < (int) st->parents->len ) {
+               node->parent = g_ptr_array_index(st->parents,parent_id);
+       } else {
+               /* ??? should we set the parent to be root ??? */
+               g_assert_not_reached();
+       }
+       
+       if (node->parent->children) {
+               /* insert as last child */
+               
+               for (last_chld = node->parent->children;
+                        last_chld->next;
+                        last_chld = last_chld->next ) ;
+               
+               last_chld->next = node;
+               
+       } else {
+               /* insert as first child */
+               node->parent->children = node;
+       }
+       
+       if(node->parent->hash) {
+               g_hash_table_insert(node->parent->hash,node->name,node);
+       }
+       
+       if (st->setup_node_pr) {
+               st->setup_node_pr(node);
+       } else {
+               node->pr = NULL;
+       }
+       
+       return node;
+}
+
+
+extern int create_node(stats_tree* st, const gchar* name, int parent_id, gboolean with_hash) {
+       stat_node* node = new_stat_node(st,name,parent_id,with_hash,TRUE);
+       
+       if (node) 
+               return node->id;
+       else
+               return 0;
+}
+
+/* XXX: should this be a macro? */
+extern int create_node_with_parent_name(stats_tree* st,
+                                                                                  const gchar* name,
+                                                                                  const gchar* parent_name,
+                                                                                  gboolean with_children) {
+       return create_node(st,name,get_parent_id_by_name(st,parent_name),with_children);
+}
+
+
+
+/*
+ * Increases by delta the counter of the node whose name is given
+ * if the node does not exist yet it's created (with counter=1)
+ * using parent_name as parent node.
+ * with_hash=TRUE to indicate that the created node will have a parent
+ */
+extern guint8* manip_stat_node(manip_node_mode mode, stats_tree* st, const guint8* name, int parent_id, gboolean with_hash, gint value) {
+       stat_node* node = NULL;
+       stat_node* parent = NULL;
+       
+       if (parent_id >= 0 && parent_id < (int) st->parents->len ) {
+               parent = g_ptr_array_index(st->parents,parent_id);
+       } else {
+               g_assert_not_reached();
+       }
+       
+       if( parent->hash ) {
+               node = g_hash_table_lookup(parent->hash,name);
+       } else {
+               node = g_hash_table_lookup(st->names,name);
+       }
+       
+       if ( node == NULL ) 
+               node = new_stat_node(st,name,parent_id,with_hash,with_hash);
+       
+       switch (mode) {
+               case MN_INCREASE: node->counter += value; break;
+               case MN_SET: node->counter = value; break;
+       }
+       
+       if (node) 
+               return node->name;
+       else
+               return NULL;
+}
+
+
+extern guint8* get_st_abbr(const guint8* optarg) {
+       guint i;
+       
+       for (i=0; optarg[i] && optarg[i] != ','; i++);
+       
+       if (optarg[i] == ',') {
+               return g_strndup(optarg,i);
+       } else {
+               return NULL;
+       }
+}
+
+
+static range_pair_t* get_range(guint8* rngstr) {
+       gchar** split;
+       range_pair_t* rng = g_malloc(sizeof(range_pair_t));
+       
+       split =  g_strsplit(rngstr,"-",2);
+
+       rng->floor = strtol(split[0],NULL,10);
+       rng->ceil  = strtol(split[1],NULL,10);
+       
+       if (rng->ceil == 0) rng->ceil = G_MAXINT;
+       if (rng->floor == 0) rng->floor = G_MININT;
+
+       g_strfreev(split);
+       
+       return rng;
+}
+
+
+extern int create_range_node(stats_tree* st,
+                                                               const gchar* name,
+                                                               int parent_id,
+                                                               ...) {
+       va_list list;
+       guint8* curr_range;
+       stat_node* rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
+       stat_node* range_node = NULL;
+       
+       va_start( list, parent_id );
+       while (( curr_range = va_arg(list, guint8*) )) {
+               range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
+               range_node->rng = get_range(curr_range);
+       }
+       va_end( list );
+
+       return rng_root->id;
+}
+
+extern int get_parent_id_by_name(stats_tree* st, const gchar* parent_name) {
+       stat_node* node = g_hash_table_lookup(st->names,parent_name);
+       
+       if (node)
+               return node->id;
+       else
+               return 0; /* ??? -1 ??? */
+}
+
+
+extern int create_range_node_with_parent_name(stats_tree* st,
+                                                                                         const gchar* name,
+                                                                                         const gchar* parent_name,
+                                                                                         ...) {
+       va_list list;
+       guint8* curr_range;
+       stat_node* range_node = NULL;
+       int parent_id = get_parent_id_by_name(st,parent_name);
+       stat_node* rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
+
+       va_start( list, parent_name );
+       while (( curr_range = va_arg(list, guint8*) )) {
+               range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
+               range_node->rng = get_range(curr_range);
+       }
+       va_end( list );
+       
+       return rng_root->id;
+}      
+
+
+extern int tick_range(stats_tree* st,
+                                                const gchar* name,
+                                                int parent_id,
+                                                int value_in_range) {
+       
+       stat_node* node = NULL;
+       stat_node* parent = NULL;
+       stat_node* child = NULL;
+       gint floor, ceil;
+       
+       if (parent_id >= 0 && parent_id < (int) st->parents->len) {
+               parent = g_ptr_array_index(st->parents,parent_id);
+       } else {
+               g_assert_not_reached();
+       }
+       
+       if( parent->hash ) {
+               node = g_hash_table_lookup(parent->hash,name);
+       } else {
+               node = g_hash_table_lookup(st->names,name);
+       }
+       
+       if ( node == NULL ) 
+               return node->id;
+       
+       for ( child = node->children; child; child = child->next) {
+               floor =  child->rng->floor;
+               ceil = child->rng->ceil;
+               
+               if ( value_in_range >= floor && value_in_range <= ceil ) {
+                       child->counter++;
+                       return node->id;
+               }
+       }
+       
+       return node->id;
+}
diff --git a/epan/stats_tree.h b/epan/stats_tree.h
new file mode 100644 (file)
index 0000000..16a1333
--- /dev/null
@@ -0,0 +1,131 @@
+/* stats_tree.h
+ * A counter tree API for ethereal dissectors
+ * 2005, Luis E. G. Ontanon
+ *
+ * $Id: $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#ifndef __STATS_TREE_H
+#define __STATS_TREE_H
+
+#include <glib.h>
+#include <epan/epan.h>
+#include <epan/packet_info.h>
+#include <epan/to_str.h>
+#include <epan/tap.h>
+#include "../register.h"
+
+#define STAT_TREE_ROOT "root"
+
+/* obscure information regarding the stats_tree */ 
+typedef struct _stats_tree stats_tree;
+
+/* tap packet callback for stats_tree */
+typedef int  (*stat_tree_packet_cb)(stats_tree*,
+                                                                       packet_info*,
+                                                                       epan_dissect_t*,
+                                                                       const void *);
+
+/* stats_tree initilaization callback */
+typedef void  (*stat_tree_init_cb)(stats_tree*);
+
+/* registers a new stats tree 
+ * abbr: protocol abbr
+ * name: protocol name
+ * packet: per packet callback
+ * init: tree initialization callback
+ */
+extern void register_stats_tree(guint8* abbr, 
+                                                               guint8* name,
+                                                               stat_tree_packet_cb packet,
+                                                               stat_tree_init_cb init );
+
+extern int get_parent_id_by_name(stats_tree* st, const gchar* parent_name);
+
+/* Creates a node in the tree (to be used in the in init_cb)
+* st: the stats_tree in which to create it
+* name: the name of the new node
+* parent_name: the name of the parent_node (NULL for root)
+* with_children: TRUE if this node will have "dynamically created" children
+*/
+extern int create_node(stats_tree* st,
+                                               const gchar* name,
+                                               int parent_id,
+                                               gboolean with_children);
+
+extern int create_node_with_parent_name(stats_tree* st,
+                                                 const gchar* name,
+                                                 const gchar* parent_name,
+                                                 gboolean with_children);
+
+/* creates a node in the tree, that will contain a ranges list.
+ example:
+ create_range_node(st,name,parent,
+                                  "-99","100-199","200-299","300-399","400-", NULL);
+*/
+extern int create_range_node(stats_tree* st,
+                                                               const gchar* name,
+                                                               int parent_id,
+                                                               ...);
+
+extern int create_range_node_with_parent_name(stats_tree* st,
+                                                                                         const gchar* name,
+                                                                                         const gchar* parent_name,
+                                                                                         ...);
+/* */
+
+extern int tick_range(stats_tree* st,
+                                                const gchar* name,
+                                                int parent_id,
+                                                int value_in_range);
+
+extern int tick_range_with_parent_name(stats_tree* st,
+                                                const gchar* name,
+                                                const gchar* parent_name,
+                                                int value_in_range);
+
+
+/*
+ * manipulates the value of the node whose name is given
+ * if the node does not exist yet it's created (with counter=1)
+ * using parent_name as parent node (NULL for root).
+ * with_children=TRUE to indicate that the created node will be a parent
+ */
+typedef enum _manip_node_mode { MN_INCREASE, MN_SET } manip_node_mode;
+extern guint8* manip_stat_node(manip_node_mode mode,
+                                                          stats_tree* st,
+                                                          const guint8* name,
+                                                          int parent_id,
+                                                          gboolean with_children,
+                                                          gint value);
+
+#define increase_stat_node(st,name,parent_id,with_children,value) \
+(manip_stat_node(MN_INCREASE,(st),(name),(parent_id),(with_children),(value)))
+
+#define tick_stat_node(st,name,parent_id,with_children) \
+(manip_stat_node(MN_INCREASE,(st),(name),(parent_id),(with_children),1))
+
+#define set_stat_node(st,name,parent_id,with_children,value) \
+(manip_stat_node(MN_SET,(st),(name),(parent_id),(with_children),value))
+
+#define zero_stat_node(st,name,parent_id,with_children) \
+(manip_stat_node(MN_SET,(st),(name),(parent_id),(with_children),0))
+
+#endif /* __STATS_TREE_H */
diff --git a/epan/stats_tree_priv.h b/epan/stats_tree_priv.h
new file mode 100644 (file)
index 0000000..893269c
--- /dev/null
@@ -0,0 +1,179 @@
+/* stats_tree_priv.h
+ * implementor's API for stats_tree
+ * 2005, Luis E. G. Ontanon
+ *
+ * $Id: $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __STATS_TREE_PRIV_H
+#define  __STATS_TREE_PRIV_H
+
+#include "stats_tree.h"
+
+#define INDENT_MAX 32
+#define NUM_BUF_SIZE 32
+
+/* implementations should define this to contain its own node related data
+ * as well as some operations on it */
+typedef struct _st_node_pres st_node_pres;
+
+/* implementations should define this to contain its own tree related data
+* as well as some operations on it */
+typedef struct _tree_pres tree_pres;
+
+typedef struct _stat_node stat_node;
+
+
+typedef struct _range_pair {
+       gint floor;
+       gint ceil;
+} range_pair_t;
+
+struct _stat_node {
+       gchar*                  name;
+       int                             id;
+       
+       /* the counter it keeps */
+       gint                    counter;
+
+       /* children nodes by name */
+       GHashTable*             hash;
+       
+       /* the owner of this node */
+       stats_tree*             st;
+       
+       /* relatives */
+       stat_node*      parent;
+       stat_node*      children;
+       stat_node*      next;
+
+       /* used to check if value is within range */
+       range_pair_t* rng;
+       
+       /* node presentation data */
+       st_node_pres* pr;
+} ;
+
+struct _stats_tree {
+       guint8*                 abbr;
+       guint8*                 name;
+       
+       /* is this realy needed? */
+       char*                   filter;
+       
+       /* times */
+       float                   start;
+       float                   elapsed;
+               
+
+          /* used to lookup named parents:
+               *    key: parent node name
+               *  value: parent node
+               */
+       GHashTable*                     names;
+       
+          /* used for quicker lookups of parent nodes */
+       GPtrArray*                      parents;
+       
+       /* every tree in nature has one */
+       stat_node       root;
+       
+       /* dissector defined callbacks */
+       stat_tree_packet_cb packet;
+       stat_tree_init_cb init;
+
+       /**** tree representation
+        *      to be defined (if needed) by the implementations
+        */
+       tree_pres* pr;
+       
+       /*  node presentation callbacks
+        */
+               
+       /* last to be called at node creation */
+       void (*setup_node_pr)(stat_node*);
+       
+       /* last to be called at node destruction */
+       void (*free_node_pr)(stat_node*);
+       
+       /* to be called for every node in the tree */
+       void (*draw_node)(stat_node*);
+       void (*reset_node)(stat_node*);
+       
+       /* tree presentation callbacks */
+       
+       tree_pres* (*new_tree_pr)(stats_tree*);
+       void (*free_tree_pr)(stats_tree*);
+       void (*draw_tree)(stats_tree*);
+       void (*reset_tree)(stats_tree*);
+};
+
+/* guess what, this is it! */
+extern void stats_tree_presentation(void (*registry_iterator)(gpointer,gpointer,gpointer),
+                                                                       void (*setup_node_pr)(stat_node*),
+                                                                       void (*free_node_pr)(stat_node*),
+                                                                       void (*draw_node)(stat_node*),
+                                                                       void (*reset_node)(stat_node*),
+                                                                       tree_pres* (*new_tree_pr)(stats_tree*),
+                                                                       void (*free_tree_pr)(stats_tree*),
+                                                                       void (*draw_tree)(stats_tree*),
+                                                                       void (*reset_tree)(stats_tree*),
+                                                                       void* data);
+
+/* callback for taps */
+extern int  stats_tree_packet(void*, packet_info*, epan_dissect_t*, const void *);
+
+/* callback for reset */
+extern void reset_stats_tree(void*);
+
+/* callback for destoy */
+extern void free_stats_tree(stats_tree* st);
+
+/* given an optarg splits the abbr part
+   and returns a newly allocated buffer containing it */
+extern guint8* get_st_abbr(const guint8* optarg);
+
+/* obtains a stats tree from the registry given its abbr */
+extern stats_tree* get_stats_tree_by_abbr(guint8* abbr);
+
+/* extracts node data as strings from a stat_node into
+   the buffers given by value, rate and precent
+   if NULL they are ignored */
+extern void get_strings_from_node(const stat_node* node,
+                                                                 guint8* value,
+                                                                 guint8* rate,
+                                                                 guint8* percent);
+
+/* populates the given GString with a tree representation of a branch given by node,
+   using indent spaces as indentation */
+extern void stat_branch_to_str(const stat_node* node,
+                                                          GString* s,
+                                                          guint indent);
+
+/* a text representation of a node,
+   if buffer is NULL returns a newly allocated string */
+extern guint8* stat_node_to_str(const stat_node* node,
+                                                               guint8* buffer, guint len);
+
+/* destroys the stats_tree */
+extern void free_stats_tree(stats_tree* st);
+
+#endif /* __STATS_TREE_PRIV_H */
diff --git a/gtk/stats_tree_stat.c b/gtk/stats_tree_stat.c
new file mode 100644 (file)
index 0000000..2cedd67
--- /dev/null
@@ -0,0 +1,327 @@
+/* stats_tree_stat.c
+ * GTK Tap implementation of stats_tree
+ * 2005, Luis E. G. Ontanon
+ *
+ * $Id: $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include <epan/stats_tree_priv.h>
+
+#include "simple_dialog.h"
+#include "globals.h"
+#include "tap_menu.h"
+#include "ui_util.h"
+#include "dlg_utils.h"
+#include "compat_macros.h"
+#include "tap_dfilter_dlg.h"
+#include "../tap_dfilter_dlg.h"
+
+struct _st_node_pres {
+#if GTK_MAJOR_VERSION >= 2
+       GtkTreeIter*    iter;
+#else
+       /* g_malloc(0) ??? */
+       void*           dummy;
+#endif
+};
+
+
+struct _tree_pres {
+       tap_dfilter_dlg* stat_dlg;
+       GString*                text;
+       GtkWidget*              win;
+       
+#if GTK_MAJOR_VERSION >= 2
+       GtkTreeStore*   store;
+       GtkWidget*              tree;
+#else
+       GtkText*                textbox;
+#endif
+};
+
+/* the columns of the tree pane */
+enum _stat_tree_columns {
+       COUNT_COLUMN,
+       RATE_COLUMN,
+       TITLE_COLUMN,
+       PERCENT_COLUMN,
+       N_COLUMNS
+};
+
+/* used for converting numbers */
+#define NUM_BUF_SIZE  32
+
+/* creates the gtk representation for a stat_node
+ * node: the node
+ */
+static void setup_gtk_node_pr(stat_node* node) {
+       node->pr = g_malloc(sizeof(st_node_pres));
+       
+
+#if GTK_MAJOR_VERSION >= 2
+       GtkTreeIter* parent =  NULL;
+       
+       if ( node->parent && node->parent->pr ) 
+               parent = node->parent->pr->iter;
+
+       node->pr->iter = g_malloc(sizeof(GtkTreeIter));
+
+       if (node->st->pr->store) {
+               gtk_tree_store_append (node->st->pr->store, node->pr->iter, parent);
+               gtk_tree_store_set(node->st->pr->store, node->pr->iter, TITLE_COLUMN, node->name, RATE_COLUMN, "", COUNT_COLUMN, "", -1);
+       }
+#else
+       node->pr->dummy = NULL;
+#endif
+}
+
+
+#if GTK_MAJOR_VERSION >= 2
+static void draw_gtk_node(stat_node* node) {
+       static gchar value[NUM_BUF_SIZE];
+       static gchar rate[NUM_BUF_SIZE];
+       static gchar percent[NUM_BUF_SIZE];
+       stat_node* child;
+       
+       get_strings_from_node(node, value, rate, percent);
+       
+       if (node->st->pr->store)
+               gtk_tree_store_set(node->st->pr->store, node->pr->iter,
+                                                  RATE_COLUMN, rate,
+                                                  COUNT_COLUMN, value,
+                                                  PERCENT_COLUMN, percent,
+                                                  -1);
+       
+       if (node->children) {
+               for (child = node->children; child; child = child->next )
+                       draw_gtk_node(child);
+       }
+}
+#endif
+
+static void draw_gtk_tree( void *psp  ) {
+       stats_tree *st = psp;
+       stat_node* child;
+
+#if GTK_MAJOR_VERSION >= 2
+       for (child = st->root.children; child; child = child->next )
+               draw_gtk_node(child);
+       
+       gtk_tree_view_set_model(GTK_TREE_VIEW(st->pr->tree),GTK_TREE_MODEL(st->pr->store));
+#else
+       GString* text = g_string_new("");
+       
+       for (child = st->root.children; child; child = child->next ) {
+               stat_node_to_str(child,text,0);
+       }
+       
+       gtk_text_freeze(st->textbox);
+       gtk_text_set_point(st->textbox,0);
+       gtk_text_forward_delete(st->textbox,gtk_text_get_length(st->textbox));
+       gtk_text_insert(st->textbox,NULL,st->textbox->style->black,NULL,text->str,-1);
+       gtk_text_thaw(st->textbox);
+       
+       g_string_free(text,TRUE);
+#endif 
+}
+
+void protect_thread_critical_region(void);
+void unprotect_thread_critical_region(void);
+
+static void free_gtk_tree(GtkWindow *win _U_, stats_tree *st)
+{
+       
+       protect_thread_critical_region();
+       remove_tap_listener(st);
+       unprotect_thread_critical_region();
+               
+       free_stats_tree(st);
+}
+
+
+/* initializes the stats_tree window */
+static void init_gtk_tree(char* optarg) {
+       guint8* abbr = get_st_abbr(optarg);
+       stats_tree* st = NULL;
+       guint8* title = NULL;
+       guint8* window_name = NULL;
+       GString* error_string;
+#if GTK_MAJOR_VERSION >= 2
+       GtkTreeViewColumn* column;
+       GtkCellRenderer* renderer;
+       GtkWidget *scr_win;
+#endif
+       
+       if (abbr) {
+               st = get_stats_tree_by_abbr(abbr);
+               
+               if (st != NULL) {
+                       if (strncmp (optarg, st->pr->stat_dlg->init_string, strlen(st->pr->stat_dlg->init_string)) == 0){
+                               st->filter=((guint8*)optarg)+strlen(st->pr->stat_dlg->init_string);
+                       } else {
+                               st->filter=NULL;
+                       }
+               } else {
+                       g_error("no such stats_tree (%s) found in stats_tree registry",abbr);
+               }
+       } else {
+               g_error("could not obtain stats_tree abbr from optarg");                
+       }
+       
+       window_name = g_strdup_printf("%s_stat", st->abbr);
+       
+       st->pr->win = window_new_with_geom(GTK_WINDOW_TOPLEVEL,window_name,window_name);
+       g_free(window_name);
+       
+       if(st->filter){
+               title=g_strdup_printf("%s with filter: %s",st->name,st->filter);
+       } else {
+               st->filter=NULL;
+               title=g_strdup_printf("%s", st->name);
+       }
+       
+    gtk_window_set_title(GTK_WINDOW(st->pr->win), title);
+       g_free(title);
+       
+#if GTK_MAJOR_VERSION >= 2
+       scr_win = scrolled_window_new(NULL, NULL);
+       
+       st->pr->store = gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
+                                                                       G_TYPE_STRING, G_TYPE_STRING);
+       
+       st->pr->tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (st->pr->store));
+       
+       gtk_container_add( GTK_CONTAINER(scr_win), st->pr->tree);
+       gtk_container_add( GTK_CONTAINER(st->pr->win), scr_win);
+       
+       /* the columns */
+       renderer = gtk_cell_renderer_text_new ();
+       column = gtk_tree_view_column_new_with_attributes ("What", renderer,
+                                                                                                          "text", TITLE_COLUMN,
+                                                                                                          NULL);
+       gtk_tree_view_column_set_resizable (column,TRUE);
+       gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column);
+       
+       renderer = gtk_cell_renderer_text_new ();
+       column = gtk_tree_view_column_new_with_attributes ("count", renderer,
+                                                                                                          "text", COUNT_COLUMN,
+                                                                                                          NULL);
+       
+       gtk_tree_view_column_set_resizable (column,TRUE);
+       gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column);
+       
+       renderer = gtk_cell_renderer_text_new ();
+       column = gtk_tree_view_column_new_with_attributes ("rate", renderer,
+                                                                                                          "text", RATE_COLUMN,
+                                                                                                          NULL);
+       gtk_tree_view_column_set_resizable (column,TRUE);
+       gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column);
+       
+       renderer = gtk_cell_renderer_text_new ();
+       column = gtk_tree_view_column_new_with_attributes ("percent", renderer,
+                                                                                                          "text", PERCENT_COLUMN,
+                                                                                                          NULL);
+       gtk_tree_view_column_set_resizable(column,TRUE);
+       gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column);
+#else
+       pr->textbox = gtk_text_new(NULL,NULL);
+       gtk_container_add( GTK_CONTAINER(scr_win), st->pr->textbox);
+       gtk_container_add( GTK_CONTAINER(st->pr->win), scr_win);
+#endif
+       
+       error_string = register_tap_listener( st->abbr,
+                                                                                 st,
+                                                                                 st->filter,
+                                                                                 reset_stats_tree,
+                                                                                 stats_tree_packet,
+                                                                                 draw_gtk_tree);
+       
+       if (error_string) {
+               /* error, we failed to attach to the tap. clean up */
+               simple_dialog( ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str );
+               /* destroy_stat_tree_window(st); */
+               g_string_free(error_string, TRUE);
+               g_error("stats_tree for: %s failed to attach to the tap: %s",st->name,error_string->str);
+       }
+               
+       SIGNAL_CONNECT(GTK_WINDOW(st->pr->win), "delete_event", window_delete_event_cb, NULL);
+       SIGNAL_CONNECT(GTK_WINDOW(st->pr->win), "destroy", free_gtk_tree, st);
+       
+       gtk_widget_show_all(st->pr->win);
+       window_present(st->pr->win);
+       
+       if (st->init) st->init(st);
+
+       cf_retap_packets(&cfile);
+       
+}
+
+
+void register_gtk_stats_tree_tap (gpointer k _U_, gpointer v, gpointer p _U_) {
+       stats_tree* st = v;
+       guint8* s;
+
+       s = g_strdup_printf("%s,stat",st->abbr);
+       
+       register_ethereal_tap(s, init_gtk_tree);
+       g_free(s);
+       
+       st->pr = g_malloc(sizeof(tree_pres));
+       st->pr->text = NULL;
+       st->pr->win = NULL;
+       
+#if GTK_MAJOR_VERSION >= 2
+       st->pr->store = NULL;
+       st->pr->tree = NULL;
+#else
+       st->pr->textbox = NULL;
+#endif
+       
+       st->pr->stat_dlg = g_malloc(sizeof(tap_dfilter_dlg));
+       
+       st->pr->stat_dlg->win_title = g_strdup_printf("%s Packet Counter",st->name);
+       st->pr->stat_dlg->init_string = g_strdup_printf("%s,stat",st->abbr);
+       st->pr->stat_dlg->tap_init_cb = init_gtk_tree;
+       st->pr->stat_dlg->index = -1;
+       
+       register_tap_menu_item(st->name, REGISTER_TAP_GROUP_NONE,
+                                                  gtk_tap_dfilter_dlg_cb, NULL, NULL, st->pr->stat_dlg);
+}
+
+void
+register_tap_listener_stats_tree_stat(void)
+{      
+       stats_tree_presentation(register_gtk_stats_tree_tap,
+                                                       setup_gtk_node_pr, NULL,
+                                                       draw_gtk_node,
+                                                       NULL, NULL, NULL, NULL, NULL, NULL);
+}
diff --git a/tap-stats_tree.c b/tap-stats_tree.c
new file mode 100644 (file)
index 0000000..8150260
--- /dev/null
@@ -0,0 +1,118 @@
+/* tap-stats_tree.c
+ * tethereal's tap implememntation of stats_tree
+ * 2005, Luis E. G. Ontanon
+ *
+ * $Id: $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <glib.h>
+#include <epan/stats_tree_priv.h>
+
+/* actually unused */
+struct _st_node_pres {
+       void* dummy;
+};
+
+
+struct _tree_pres {
+       guint8* init_string;    
+};
+
+static void draw_stats_tree(void *psp) {
+       stats_tree *st = psp;
+       GString* s;
+       stat_node* child;
+       
+       s = g_string_new("\n===================================================================\n");
+       
+       g_string_sprintfa(s,"Statistics for %s\n===================================================================\n",
+                                        st->name);
+       
+       for (child = st->root.children; child; child = child->next ) {
+               stat_branch_to_str(child,s,1);
+       }
+       
+       s = g_string_append(s,"\n===================================================================\n");
+       
+       printf("%s",s->str);
+       
+}
+
+static void  init_stats_tree(char *optarg) {
+       guint8* abbr = get_st_abbr(optarg);
+       GString *error_string;
+       stats_tree *st = NULL;
+
+       if (abbr) {
+               st = get_stats_tree_by_abbr(abbr);
+               
+               if (st != NULL) {
+                       if (strncmp (optarg, st->pr->init_string, strlen(st->pr->init_string)) == 0){
+                               st->filter=((guint8*)optarg)+strlen(st->pr->init_string);
+                       } else {
+                               st->filter=NULL;
+                       }
+               } else {
+                       g_error("no such stats_tree (%s) found in stats_tree registry",abbr);
+               }
+       } else {
+               g_error("could not obtain stats_tree abbr from optarg");                
+       }
+       
+       error_string = register_tap_listener( st->abbr,
+                                                                                 st,
+                                                                                 st->filter,
+                                                                                 reset_stats_tree,
+                                                                                 stats_tree_packet,
+                                                                                 draw_stats_tree);
+       
+       if (error_string) {
+               g_error("stats_tree for: %s failed to attach to the tap: %s",st->name,error_string->str);
+       }
+
+       if (st->init) st->init(st);
+
+}
+
+void register_stats_tree_tap (gpointer k _U_, gpointer v, gpointer p _U_) {
+       stats_tree* st = v;
+       
+       st->pr = g_malloc(sizeof(tree_pres));
+       st->pr->init_string = g_strdup_printf("%s,stat",st->abbr);
+
+       register_ethereal_tap(st->pr->init_string, init_stats_tree);
+       
+}
+
+
+void
+register_tap_listener_stats_tree_stat(void)
+{
+       stats_tree_presentation(register_stats_tree_tap,
+                                                       NULL, NULL, NULL, NULL, NULL,
+                                                       NULL, NULL, NULL, NULL);
+}