Logo Search packages:      
Sourcecode: ethereal version File versions

proto.c

/* proto.c
 * Routines for protocol tree
 *
 * $Id: proto.c 13180 2005-01-26 23:17:00Z ulfl $
 *
 * 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 <stdio.h>
#include <string.h>
#include <ctype.h>
#include <glib.h>
#include <float.h>

#ifdef NEED_SNPRINTF_H
# include "snprintf.h"
#endif

#include "packet.h"
#include "strutil.h"
#include "addr_resolv.h"
#include "plugins.h"
#include "ipv6-utils.h"
#include "proto.h"
#include "epan_dissect.h"
#include "slab.h"
#include "tvbuff.h"

#define cVALS(x) (const value_string*)(x)

static gboolean
proto_tree_free_node(proto_node *node, gpointer data);

static void fill_label_boolean(field_info *fi, gchar *label_str);
static void fill_label_uint(field_info *fi, gchar *label_str);
static void fill_label_uint64(field_info *fi, gchar *label_str);
static void fill_label_enumerated_uint(field_info *fi, gchar *label_str);
static void fill_label_enumerated_bitfield(field_info *fi, gchar *label_str);
static void fill_label_numeric_bitfield(field_info *fi, gchar *label_str);
static void fill_label_int(field_info *fi, gchar *label_str);
static void fill_label_int64(field_info *fi, gchar *label_str);
static void fill_label_enumerated_int(field_info *fi, gchar *label_str);

int hfinfo_bitwidth(header_field_info *hfinfo);
static char* hfinfo_uint_vals_format(header_field_info *hfinfo);
static char* hfinfo_uint_format(header_field_info *hfinfo);
static char* hfinfo_uint64_format(header_field_info *hfinfo);
static char* hfinfo_int_vals_format(header_field_info *hfinfo);
static char* hfinfo_int_format(header_field_info *hfinfo);
static char* hfinfo_int64_format(header_field_info *hfinfo);

static proto_item*
proto_tree_add_node(proto_tree *tree, field_info *fi);

static field_info *
alloc_field_info(proto_tree *tree, int hfindex, tvbuff_t *tvb,
        gint start, gint *length);

static proto_item *
proto_tree_add_pi(proto_tree *tree, int hfindex, tvbuff_t *tvb,
        gint start, gint *length, field_info **pfi);

static void
proto_tree_set_representation(proto_item *pi, const char *format, va_list ap);

static void
proto_tree_set_protocol_tvb(field_info *fi, tvbuff_t *tvb);
static void
proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length);
static void
proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length);
static void
proto_tree_set_time(field_info *fi, nstime_t *value_ptr);
static void
proto_tree_set_string(field_info *fi, const char* value, gboolean);
static void
proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length);
static void
proto_tree_set_ether(field_info *fi, const guint8* value);
static void
proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start);
static void
proto_tree_set_ipxnet(field_info *fi, guint32 value);
static void
proto_tree_set_ipv4(field_info *fi, guint32 value);
static void
proto_tree_set_ipv6(field_info *fi, const guint8* value_ptr);
static void
proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start);
static void
proto_tree_set_boolean(field_info *fi, guint32 value);
static void
proto_tree_set_float(field_info *fi, float value);
static void
proto_tree_set_double(field_info *fi, double value);
static void
proto_tree_set_uint(field_info *fi, guint32 value);
static void
proto_tree_set_int(field_info *fi, gint32 value);
static void
proto_tree_set_uint64(field_info *fi, guint64 value);
static void
proto_tree_set_uint64_tvb(field_info *fi, tvbuff_t *tvb, gint start, gboolean little_endian);

static int proto_register_field_init(header_field_info *hfinfo, int parent);

/* Comparision function for tree insertion. A wrapper around strcmp() */
static int g_strcmp(gconstpointer a, gconstpointer b);

/* special-case header field used within proto.c */
int hf_text_only = -1;

/* Structure for information about a protocol */
struct _protocol {
      char  *name;            /* long description */
      char  *short_name;      /* short description */
      char  *filter_name;     /* name of this protocol in filters */
      int   proto_id;   /* field ID for this protocol */
      GList *fields;    /* fields for this protocol */
      GList *last_field;      /* pointer to end of list of fields */
      gboolean is_enabled;    /* TRUE if protocol is enabled */
      gboolean can_toggle;    /* TRUE if is_enabled can be changed */
};

/* List of all protocols */
static GList *protocols = NULL;

#define INITIAL_NUM_PROTOCOL_HFINFO     200


/* Contains information about protocols and header fields. Used when
 * dissectors register their data */
static GMemChunk *gmc_hfinfo = NULL;

/* Contains information about a field when a dissector calls
 * proto_tree_add_item.  */
SLAB_ITEM_TYPE_DEFINE(field_info)
static SLAB_FREE_LIST_DEFINE(field_info)
static field_info *field_info_tmp=NULL;
#define FIELD_INFO_NEW(fi)                            \
      SLAB_ALLOC(fi, field_info)
#define FIELD_INFO_FREE(fi)                           \
      SLAB_FREE(fi, field_info)



/* Contains the space for proto_nodes. */
SLAB_ITEM_TYPE_DEFINE(proto_node)
static SLAB_FREE_LIST_DEFINE(proto_node)
#define PROTO_NODE_NEW(node)                    \
      SLAB_ALLOC(node, proto_node)              \
      node->first_child = NULL;                 \
      node->last_child = NULL;                  \
      node->next = NULL;

#define PROTO_NODE_FREE(node)                   \
      SLAB_FREE(node, proto_node)



/* String space for protocol and field items for the GUI */
SLAB_ITEM_TYPE_DEFINE(item_label_t)
static SLAB_FREE_LIST_DEFINE(item_label_t)
#define ITEM_LABEL_NEW(il)                      \
      SLAB_ALLOC(il, item_label_t)
#define ITEM_LABEL_FREE(il)                     \
      SLAB_FREE(il, item_label_t)



/* List which stores protocols and fields that have been registered */
typedef struct _gpa_hfinfo_t {
      guint32 len;
      guint32 allocated_len;
      header_field_info **hfi;
} gpa_hfinfo_t;
gpa_hfinfo_t gpa_hfinfo;

/* Balanced tree of abbreviations and IDs */
static GTree *gpa_name_tree = NULL;

/* Points to the first element of an array of Booleans, indexed by
   a subtree item type; that array element is TRUE if subtrees of
   an item of that type are to be expanded. */
gboolean    *tree_is_expanded;

/* Number of elements in that array. */
int         num_tree_types;

/* Name hashtables for fast detection of duplicate names */
static GHashTable* proto_names = NULL;
static GHashTable* proto_short_names = NULL;
static GHashTable* proto_filter_names = NULL;

static gint
proto_compare_name(gconstpointer p1_arg, gconstpointer p2_arg)
{
      const protocol_t *p1 = p1_arg;
      const protocol_t *p2 = p2_arg;

      return g_strcasecmp(p1->short_name, p2->short_name);
}


/* initialize data structures and register protocols and fields */
void
proto_init(const char *plugin_dir
#ifndef HAVE_PLUGINS
                         _U_
#endif
         ,
         void (register_all_protocols)(void),
         void (register_all_protocol_handoffs)(void))
{
      static hf_register_info hf[] = {
            { &hf_text_only,
            { "", "", FT_NONE, BASE_NONE, NULL, 0x0,
                  NULL, HFILL }},
      };


    proto_names = g_hash_table_new(g_int_hash, g_int_equal);
    proto_short_names = g_hash_table_new(g_int_hash, g_int_equal);
    proto_filter_names = g_hash_table_new(g_int_hash, g_int_equal);

      proto_cleanup();

      gmc_hfinfo = g_mem_chunk_new("gmc_hfinfo",
            sizeof(header_field_info),
        INITIAL_NUM_PROTOCOL_HFINFO * sizeof(header_field_info),
        G_ALLOC_ONLY);

      gpa_hfinfo.len=0;
      gpa_hfinfo.allocated_len=0;
      gpa_hfinfo.hfi=NULL;
      gpa_name_tree = g_tree_new(g_strcmp);

      /* Initialize the ftype subsystem */
      ftypes_initialize();

      /* Register one special-case FT_TEXT_ONLY field for use when
         converting ethereal to new-style proto_tree. These fields
         are merely strings on the GUI tree; they are not filterable */
      proto_register_field_array(-1, hf, array_length(hf));

      /* Have each built-in dissector register its protocols, fields,
         dissector tables, and dissectors to be called through a
         handle, and do whatever one-time initialization it needs to
         do. */
      register_all_protocols();

#ifdef HAVE_PLUGINS
      /* Now scan for plugins and load all the ones we find, calling
         their register routines to do the stuff described above. */
      init_plugins(plugin_dir);
#endif

      /* Now call the "handoff registration" routines of all built-in
         dissectors; those routines register the dissector in other
         dissectors' handoff tables, and fetch any dissector handles
         they need. */
      register_all_protocol_handoffs();

#ifdef HAVE_PLUGINS
      /* Now do the same with plugins. */
      register_all_plugin_handoffs();
#endif

    /* sort the protocols by protocol name */
    protocols = g_list_sort(protocols, proto_compare_name);

      /* We've assigned all the subtree type values; allocate the array
         for them, and zero it out. */
      tree_is_expanded = g_malloc(num_tree_types*sizeof (gint *));
      memset(tree_is_expanded, 0, num_tree_types*sizeof (gint *));
}

/* String comparison func for dfilter_token GTree */
static int
g_strcmp(gconstpointer a, gconstpointer b)
{
      return strcmp((const char*)a, (const char*)b);
}

void
00306 proto_cleanup(void)
{
      /* Free the abbrev/ID GTree */
      if (gpa_name_tree) {
            g_tree_destroy(gpa_name_tree);
            gpa_name_tree = NULL;
      }

      if (gmc_hfinfo)
            g_mem_chunk_destroy(gmc_hfinfo);

      if(gpa_hfinfo.allocated_len){
            gpa_hfinfo.len=0;
            gpa_hfinfo.allocated_len=0;
            g_free(gpa_hfinfo.hfi);
            gpa_hfinfo.hfi=NULL;
      }
      if (tree_is_expanded != NULL)
            g_free(tree_is_expanded);

}

typedef gboolean (*proto_tree_traverse_func)(proto_node *, gpointer);

static gboolean
proto_tree_traverse_pre_order(proto_tree *tree, proto_tree_traverse_func func,
    gpointer data)
{
      proto_node *pnode = tree;
      proto_node *child;
      proto_node *current;

      if (func(pnode, data))
            return TRUE;

      child = pnode->first_child;
      while (child != NULL) {
            /*
             * The routine we call might modify the child, e.g. by
             * freeing it, so we get the child's successor before
             * calling that routine.
             */
            current = child;
            child = current->next;
            if (proto_tree_traverse_pre_order((proto_tree *)current, func,
                data))
                  return TRUE;
      }

      return FALSE;
}

static gboolean
proto_tree_traverse_in_order(proto_tree *tree, proto_tree_traverse_func func,
    gpointer data)
{
      proto_node *pnode = tree;
      proto_node *child;
      proto_node *current;

      child = pnode->first_child;
      if (child != NULL) {
            /*
             * The routine we call might modify the child, e.g. by
             * freeing it, so we get the child's successor before
             * calling that routine.
             */
            current = child;
            child = current->next;

            if (proto_tree_traverse_in_order((proto_tree *)current, func,
                data))
                  return TRUE;

            if (func(pnode, data))
                  return TRUE;

            while (child != NULL) {
                  /*
                   * The routine we call might modify the child, e.g. by
                   * freeing it, so we get the child's successor before
                   * calling that routine.
                   */
                  current = child;
                  child = current->next;
                  if (proto_tree_traverse_in_order((proto_tree *)current,
                      func, data))
                        return TRUE;
            }
      } else {
            if (func(pnode, data))
                  return TRUE;
      }

      return FALSE;
}

void
proto_tree_children_foreach(proto_tree *tree, proto_tree_foreach_func func,
    gpointer data)
{
      proto_node *node = tree;
      proto_node *current;

      node = node->first_child;
      while (node != NULL) {
            current = node;
            node = current->next;
            func((proto_tree *)current, data);
      }
}

/* frees the resources that the dissection a proto_tree uses */
void
00420 proto_tree_free(proto_tree *tree)
{
      proto_tree_traverse_in_order(tree, proto_tree_free_node, NULL);
}

static void
free_GPtrArray_value(gpointer key _U_, gpointer value, gpointer user_data _U_)
{
      GPtrArray   *ptrs = value;

      g_ptr_array_free(ptrs, TRUE);
}

static void
free_node_tree_data(tree_data_t *tree_data)
{
        /* Free all the GPtrArray's in the interesting_hfids hash. */
        g_hash_table_foreach(tree_data->interesting_hfids,
            free_GPtrArray_value, NULL);

        /* And then destroy the hash. */
        g_hash_table_destroy(tree_data->interesting_hfids);

        /* And finally the tree_data_t itself. */
        g_free(tree_data);
}

#define FREE_NODE_FIELD_INFO(finfo) \
      if(finfo->rep){               \
            ITEM_LABEL_FREE(finfo->rep);  \
      }                       \
      FVALUE_CLEANUP(&finfo->value);      \
      FIELD_INFO_FREE(finfo);

static gboolean
proto_tree_free_node(proto_node *node, gpointer data _U_)
{
      field_info *finfo = PITEM_FINFO(node);

      if (finfo == NULL) {
            /* This is the root node. Destroy the per-tree data.
             * There is no field_info to destroy. */
            free_node_tree_data(PTREE_DATA(node));
      }
      else {
            /* This is a child node. Don't free the per-tree data, but
             * do free the field_info data. */
            FREE_NODE_FIELD_INFO(finfo);
      }

      /* Free the proto_node. */
      PROTO_NODE_FREE(node);

      return FALSE; /* FALSE = do not end traversal of protocol tree */
}

/* Is the parsing being done for a visible proto_tree or an invisible one?
 * By setting this correctly, the proto_tree creation is sped up by not
 * having to call vsnprintf and copy strings around.
 */
void
00481 proto_tree_set_visible(proto_tree *tree, gboolean visible)
{
      PTREE_DATA(tree)->visible = visible;
}

#define PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo) \
      g_assert((guint)hfindex < gpa_hfinfo.len); \
      hfinfo=gpa_hfinfo.hfi[hfindex];           

/* Finds a record in the hf_info_records array by id. */
header_field_info*
00492 proto_registrar_get_nth(guint hfindex)
{
      register header_field_info    *hfinfo;

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      return hfinfo;
}

/* Finds a record in the hf_info_records array by name.
 */
header_field_info*
00503 proto_registrar_get_byname(const char *field_name)
{
      g_assert(field_name != NULL);
      return g_tree_lookup(gpa_name_tree, (gpointer)field_name);
}

/* Add a text-only node, leaving it to our caller to fill the text in */
static proto_item *
proto_tree_add_text_node(proto_tree *tree, tvbuff_t *tvb, gint start, gint length)
{
      proto_item  *pi;

      pi = proto_tree_add_pi(tree, hf_text_only, tvb, start, &length, NULL);
      if (pi == NULL)
            return(NULL);

      return pi;
}

/* Add a text-only node to the proto_tree */
proto_item *
00524 proto_tree_add_text(proto_tree *tree, tvbuff_t *tvb, gint start, gint length,
      const char *format, ...)
{
      proto_item  *pi;
      va_list           ap;

      pi = proto_tree_add_text_node(tree, tvb, start, length);
      if (pi == NULL)
            return(NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Add a text-only node to the proto_tree (va_list version) */
proto_item *
00543 proto_tree_add_text_valist(proto_tree *tree, tvbuff_t *tvb, gint start,
      gint length, const char *format, va_list ap)
{
      proto_item  *pi;

      pi = proto_tree_add_text_node(tree, tvb, start, length);
      if (pi == NULL)
            return(NULL);

      proto_tree_set_representation(pi, format, ap);

      return pi;
}

/* Add a text-only node for debugging purposes. The caller doesn't need
 * to worry about tvbuff, start, or length. Debug message gets sent to
 * STDOUT, too */
proto_item *
00561 proto_tree_add_debug_text(proto_tree *tree, const char *format, ...)
{
      proto_item  *pi;
      va_list           ap;

      pi = proto_tree_add_text_node(tree, NULL, 0, 0);
      if (pi == NULL)
            return(NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      vprintf(format, ap);
      va_end(ap);
      printf("\n");

      return pi;
}


static guint32
get_uint_value(tvbuff_t *tvb, gint offset, gint length, gboolean little_endian)
{
      guint32 value;

      switch (length) {

      case 1:
            value = tvb_get_guint8(tvb, offset);
            break;

      case 2:
            value = little_endian ? tvb_get_letohs(tvb, offset)
                              : tvb_get_ntohs(tvb, offset);
            break;

      case 3:
            value = little_endian ? tvb_get_letoh24(tvb, offset)
                              : tvb_get_ntoh24(tvb, offset);
            break;

      case 4:
            value = little_endian ? tvb_get_letohl(tvb, offset)
                              : tvb_get_ntohl(tvb, offset);
            break;

      default:
            g_assert_not_reached();
            value = 0;
            break;
      }
      return value;
}

static gint32
get_int_value(tvbuff_t *tvb, gint offset, gint length, gboolean little_endian)
{
      gint32 value;

      switch (length) {

      case 1:
            value = (gint8)tvb_get_guint8(tvb, offset);
            break;

      case 2:
            value = (gint16) (little_endian ? tvb_get_letohs(tvb, offset)
                                    : tvb_get_ntohs(tvb, offset));
            break;

      case 3:
            value = little_endian ? tvb_get_letoh24(tvb, offset)
                              : tvb_get_ntoh24(tvb, offset);
            if (value & 0x00800000) {
                  /* Sign bit is set; sign-extend it. */
                  value |= 0xFF000000;
            }
            break;

      case 4:
            value = little_endian ? tvb_get_letohl(tvb, offset)
                              : tvb_get_ntohl(tvb, offset);
            break;

      default:
            g_assert_not_reached();
            value = 0;
            break;
      }
      return value;
}

/* Add an item to a proto_tree, using the text label registered to that item;
   the item is extracted from the tvbuff handed to it. */
proto_item *
00655 proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb,
    gint start, gint length, gboolean little_endian)
{
      field_info  *new_fi;
      proto_item  *pi;
      guint32           value, n;
      float       floatval;
      double            doubleval;
      char        *string;
      GHashTable  *hash;
      GPtrArray   *ptrs;

      if (!tree)
            return(NULL);

      new_fi = alloc_field_info(tree, hfindex, tvb, start, &length);

      if (new_fi == NULL)
            return(NULL);

      /* there is a possibility here that we might raise an exception
       * and thus would lose track of the field_info.
       * store it in a temp so that if we come here again we can reclaim
       * the field_info without leaking memory.
       */
      /* XXX this only keeps track of one field_info struct,
         if we ever go multithreaded for calls to this function
         we have to change this code to use per thread variable.
      */
      if(field_info_tmp){
            /* oops, last one we got must have been lost due
             * to an exception.
             * good thing we saved it, now we can reverse the
             * memory leak and reclaim it.
             */
            SLAB_FREE(field_info_tmp, field_info);
      }
      /* we might throw an exception, keep track of this one
       * across the "dangerous" section below.
      */
      field_info_tmp=new_fi;

      switch(new_fi->hfinfo->type) {
            case FT_NONE:
                  /* no value to set for FT_NONE */
                  break;

            case FT_PROTOCOL:
                  proto_tree_set_protocol_tvb(new_fi, tvb);
                  break;

            case FT_BYTES:
                  proto_tree_set_bytes_tvb(new_fi, tvb, start, length);
                  break;

            case FT_UINT_BYTES:
                  n = get_uint_value(tvb, start, length, little_endian);
                  proto_tree_set_bytes_tvb(new_fi, tvb, start + length, n);

                  /* Instead of calling proto_item_set_len(), since we don't yet
                   * have a proto_item, we set the field_info's length ourselves. */
                  new_fi->length = n + length;
                  break;

            case FT_BOOLEAN:
                  proto_tree_set_boolean(new_fi,
                      get_uint_value(tvb, start, length, little_endian));
                  break;

            /* XXX - make these just FT_UINT? */
            case FT_UINT8:
            case FT_UINT16:
            case FT_UINT24:
            case FT_UINT32:
                  proto_tree_set_uint(new_fi,
                      get_uint_value(tvb, start, length, little_endian));
                  break;

            case FT_INT64:
            case FT_UINT64:
                  g_assert(length == 8);
                  proto_tree_set_uint64_tvb(new_fi, tvb, start, little_endian);
                  break;

            /* XXX - make these just FT_INT? */
            case FT_INT8:
            case FT_INT16:
            case FT_INT24:
            case FT_INT32:
                  proto_tree_set_int(new_fi,
                      get_int_value(tvb, start, length, little_endian));
                  break;

            case FT_IPv4:
                  g_assert(length == 4);
                  tvb_memcpy(tvb, (guint8 *)&value, start, 4);
                  proto_tree_set_ipv4(new_fi, little_endian ? GUINT32_SWAP_LE_BE(value) : value);
                  break;

            case FT_IPXNET:
                  g_assert(length == 4);
                  proto_tree_set_ipxnet(new_fi,
                      get_uint_value(tvb, start, 4, FALSE));
                  break;

            case FT_IPv6:
                  g_assert(length == 16);
                  proto_tree_set_ipv6_tvb(new_fi, tvb, start);
                  break;

            case FT_ETHER:
                  g_assert(length == 6);
                  proto_tree_set_ether_tvb(new_fi, tvb, start);
                  break;

            case FT_FLOAT:
                  g_assert(length == 4);
                  if (little_endian)
                        floatval = tvb_get_letohieee_float(tvb, start);
                  else
                        floatval = tvb_get_ntohieee_float(tvb, start);
                  proto_tree_set_float(new_fi, floatval);
                  break;

            case FT_DOUBLE:
                  g_assert(length == 8);
                  if (little_endian)
                        doubleval = tvb_get_letohieee_double(tvb, start);
                  else
                        doubleval = tvb_get_ntohieee_double(tvb, start);
                  proto_tree_set_double(new_fi, doubleval);
                  break;

            case FT_STRING:
                  /* This g_strdup'ed memory is freed in proto_tree_free_node() */
                  proto_tree_set_string_tvb(new_fi, tvb, start, length);
                  break;

            case FT_STRINGZ:
                  if (length != 0) {  /* XXX - Should we throw an exception instead? */
                        /* Instead of calling proto_item_set_len(),
                         * since we don't yet have a proto_item, we
                         * set the field_info's length ourselves.
                         *
                         * XXX - our caller can't use that length to
                         * advance an offset unless they arrange that
                         * there always be a protocol tree into which
                         * we're putting this item.
                         */
                        if (length == -1) {
                              /* This can throw an exception */
                              length = tvb_strsize(tvb, start);

                              /* This g_malloc'ed memory is freed
                                 in proto_tree_free_node() */
                              string = g_malloc(length);

                              tvb_memcpy(tvb, string, start, length);
                              new_fi->length = length;
                        }
                        else {
                              /* In this case, length signifies
                               * the length of the string.
                               *
                               * This could either be a null-padded
                               * string, which doesn't necessarily
                               * have a '\0' at the end, or a
                               * null-terminated string, with a
                               * trailing '\0'.  (Yes, there are
                               * cases where you have a string
                               * that's both counted and null-
                               * terminated.)
                               *
                               * In the first case, we must
                               * allocate a buffer of length
                               * "length+1", to make room for
                               * a trailing '\0'.
                               *
                               * In the second case, we don't
                               * assume that there is a trailing
                               * '\0' there, as the packet might
                               * be malformed.  (XXX - should we
                               * throw an exception if there's no
                               * trailing '\0'?)  Therefore, we
                               * allocate a buffer of length
                               * "length+1", and put in a trailing
                               * '\0', just to be safe.
                               *
                               * (XXX - this would change if
                               * we made string values counted
                               * rather than null-terminated.)
                               */

                              /* This g_malloc'ed memory is freed
                               * in proto_tree_free_node() */
                              string = tvb_get_string(tvb, start,
                                  length);
                              new_fi->length = length;
                        }
                        proto_tree_set_string(new_fi, string, TRUE);
                  }
                  break;

            case FT_UINT_STRING:
                  /* This g_strdup'ed memory is freed in proto_tree_free_node() */
                  n = get_uint_value(tvb, start, length, little_endian);
                  proto_tree_set_string_tvb(new_fi, tvb, start + length, n);

                  /* Instead of calling proto_item_set_len(), since we
                   * don't yet have a proto_item, we set the
                   * field_info's length ourselves.
                   *
                   * XXX - our caller can't use that length to
                   * advance an offset unless they arrange that
                   * there always be a protocol tree into which
                   * we're putting this item.
                   */
                  new_fi->length = n + length;
                  break;

            default:
                  g_error("new_fi->hfinfo->type %d (%s) not handled\n",
                              new_fi->hfinfo->type,
                              ftype_name(new_fi->hfinfo->type));
                  g_assert_not_reached();
                  break;
      }

      /* Don't add new node to proto_tree until now so that any exceptions
       * raised by a tvbuff access method doesn't leave junk in the proto_tree. */
      pi = proto_tree_add_node(tree, new_fi);

      /* we did not raise an exception so we dont have to remember this
       * field_info struct any more.
       */
      field_info_tmp=NULL;

      /* If the proto_tree wants to keep a record of this finfo
       * for quick lookup, then record it. */
      hash = PTREE_DATA(tree)->interesting_hfids;
      ptrs = g_hash_table_lookup(hash, GINT_TO_POINTER(hfindex));
      if (ptrs) {
            g_ptr_array_add(ptrs, new_fi);
      }

      return pi;
}

proto_item *
00904 proto_tree_add_item_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb,
    gint start, gint length, gboolean little_endian)
{
      proto_item  *pi;

      pi = proto_tree_add_item(tree, hfindex, tvb, start, length, little_endian);
      if (pi == NULL)
            return(NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}


/* Add a FT_NONE to a proto_tree */
proto_item *
00921 proto_tree_add_none_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
            gint length, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_NONE);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      /* no value to set for FT_NONE */
      return pi;
}


static void
proto_tree_set_protocol_tvb(field_info *fi, tvbuff_t *tvb)
{
      fvalue_set(&fi->value, tvb, TRUE);
}

/* Add a FT_PROTOCOL to a proto_tree */
proto_item *
00953 proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
            gint length, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;
      field_info        *new_fi;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_PROTOCOL);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      if (start == 0) {
            proto_tree_set_protocol_tvb(new_fi, tvb);
      }
      else {
            proto_tree_set_protocol_tvb(new_fi, NULL);
      }
      return pi;
}


/* Add a FT_BYTES to a proto_tree */
proto_item *
00985 proto_tree_add_bytes(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
            gint length, const guint8 *start_ptr)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_BYTES);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_bytes(new_fi, start_ptr, length);

      return pi;
}

proto_item *
01005 proto_tree_add_bytes_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
            gint length, const guint8 *start_ptr)
{
      proto_item        *pi;

      pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, start_ptr);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01020 proto_tree_add_bytes_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
            gint length, const guint8 *start_ptr, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, start_ptr);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

static void
proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length)
{
      GByteArray        *bytes;

      bytes = g_byte_array_new();
      if (length > 0) {
            g_byte_array_append(bytes, start_ptr, length);
      }
      fvalue_set(&fi->value, bytes, TRUE);
}


static void
proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length)
{
      proto_tree_set_bytes(fi, tvb_get_ptr(tvb, offset, length), length);
}

/* Add a FT_*TIME to a proto_tree */
proto_item *
01058 proto_tree_add_time(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            nstime_t *value_ptr)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_ABSOLUTE_TIME ||
                        hfinfo->type == FT_RELATIVE_TIME);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_time(new_fi, value_ptr);

      return pi;
}

proto_item *
01079 proto_tree_add_time_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            nstime_t *value_ptr)
{
      proto_item        *pi;

      pi = proto_tree_add_time(tree, hfindex, tvb, start, length, value_ptr);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01094 proto_tree_add_time_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            nstime_t *value_ptr, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_time(tree, hfindex, tvb, start, length, value_ptr);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_*TIME value */
static void
proto_tree_set_time(field_info *fi, nstime_t *value_ptr)
{
      fvalue_set(&fi->value, value_ptr, FALSE);
}

/* Add a FT_IPXNET to a proto_tree */
proto_item *
01120 proto_tree_add_ipxnet(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_IPXNET);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_ipxnet(new_fi, value);

      return pi;
}

proto_item *
01140 proto_tree_add_ipxnet_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value)
{
      proto_item        *pi;

      pi = proto_tree_add_ipxnet(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01155 proto_tree_add_ipxnet_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_ipxnet(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_IPXNET value */
static void
proto_tree_set_ipxnet(field_info *fi, guint32 value)
{
      fvalue_set_integer(&fi->value, value);
}

/* Add a FT_IPv4 to a proto_tree */
proto_item *
01181 proto_tree_add_ipv4(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_IPv4);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_ipv4(new_fi, value);

      return pi;
}

proto_item *
01201 proto_tree_add_ipv4_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value)
{
      proto_item        *pi;

      pi = proto_tree_add_ipv4(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01216 proto_tree_add_ipv4_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_ipv4(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_IPv4 value */
static void
proto_tree_set_ipv4(field_info *fi, guint32 value)
{
      fvalue_set_integer(&fi->value, value);
}

/* Add a FT_IPv6 to a proto_tree */
proto_item *
01242 proto_tree_add_ipv6(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            const guint8* value_ptr)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_IPv6);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_ipv6(new_fi, value_ptr);

      return pi;
}

proto_item *
01262 proto_tree_add_ipv6_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            const guint8* value_ptr)
{
      proto_item        *pi;

      pi = proto_tree_add_ipv6(tree, hfindex, tvb, start, length, value_ptr);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01277 proto_tree_add_ipv6_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            const guint8* value_ptr, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_ipv6(tree, hfindex, tvb, start, length, value_ptr);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_IPv6 value */
static void
proto_tree_set_ipv6(field_info *fi, const guint8* value_ptr)
{
      fvalue_set(&fi->value, (gpointer) value_ptr, FALSE);
}

static void
proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start)
{
      proto_tree_set_ipv6(fi, tvb_get_ptr(tvb, start, 16));
}

static void
proto_tree_set_uint64(field_info *fi, guint64 value)
{
      fvalue_set_integer64(&fi->value, value);
}

static void
proto_tree_set_uint64_tvb(field_info *fi, tvbuff_t *tvb, gint start, gboolean little_endian)
{
      guint64 value;

      value = little_endian ? tvb_get_letoh64(tvb, start)
                        : tvb_get_ntoh64(tvb, start);

      proto_tree_set_uint64(fi, value);
}

/* Add a FT_STRING or FT_STRINGZ to a proto_tree. Creates own copy of string,
 * and frees it when the proto_tree is destroyed. */
proto_item *
01327 proto_tree_add_string(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
            gint length, const char* value)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      g_assert(length >= 0);
      proto_tree_set_string(new_fi, value, FALSE);

      return pi;
}

proto_item *
01348 proto_tree_add_string_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
            gint length, const char* value)
{
      proto_item        *pi;

      pi = proto_tree_add_string(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01363 proto_tree_add_string_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
            gint length, const char* value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_string(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Appends string data to a FT_STRING or FT_STRINGZ, allowing progressive
 * field info update instead of only updating the representation as does
 * proto_item_append_text()
 */
void
01385 proto_item_append_string(proto_item *pi, const char *str)
{
      field_info *fi;
      header_field_info *hfinfo;
      gchar *old_str, *new_str;

      if (!pi)
            return;
      if (!*str)
            return;

      fi = PITEM_FINFO(pi);
      hfinfo = fi->hfinfo;
      g_assert(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ);
      old_str = fvalue_get(&fi->value);
      new_str = g_malloc(strlen(old_str) + strlen(str) + 1);
      sprintf(new_str, "%s%s", old_str, str);
      fvalue_set(&fi->value, new_str, TRUE);
}

/* Set the FT_STRING value */
static void
proto_tree_set_string(field_info *fi, const char* value,
            gboolean already_allocated)
{
      fvalue_set(&fi->value, (gpointer) value, already_allocated);
}

static void
proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length)
{
      gchar *string;

      if (length == -1) {
            length = tvb_ensure_length_remaining(tvb, start);
      }

      /* This memory is freed in proto_tree_free_node() */
      string = tvb_get_string(tvb, start, length);
      proto_tree_set_string(fi, string, TRUE);
}

/* Add a FT_ETHER to a proto_tree */
proto_item *
01429 proto_tree_add_ether(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            const guint8* value)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_ETHER);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_ether(new_fi, value);

      return pi;
}

proto_item *
01449 proto_tree_add_ether_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            const guint8* value)
{
      proto_item        *pi;

      pi = proto_tree_add_ether(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01464 proto_tree_add_ether_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            const guint8* value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_ether(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_ETHER value */
static void
proto_tree_set_ether(field_info *fi, const guint8* value)
{
      fvalue_set(&fi->value, (gpointer) value, FALSE);
}

static void
proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start)
{
      proto_tree_set_ether(fi, tvb_get_ptr(tvb, start, 6));
}

/* Add a FT_BOOLEAN to a proto_tree */
proto_item *
01496 proto_tree_add_boolean(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_BOOLEAN);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_boolean(new_fi, value);

      return pi;
}

proto_item *
01516 proto_tree_add_boolean_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value)
{
      proto_item        *pi;

      pi = proto_tree_add_boolean(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01531 proto_tree_add_boolean_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_boolean(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_BOOLEAN value */
static void
proto_tree_set_boolean(field_info *fi, guint32 value)
{
      proto_tree_set_uint(fi, value);
}

/* Add a FT_FLOAT to a proto_tree */
proto_item *
01557 proto_tree_add_float(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            float value)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_FLOAT);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_float(new_fi, value);

      return pi;
}

proto_item *
01577 proto_tree_add_float_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            float value)
{
      proto_item        *pi;

      pi = proto_tree_add_float(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01592 proto_tree_add_float_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            float value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_float(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_FLOAT value */
static void
proto_tree_set_float(field_info *fi, float value)
{
      fvalue_set_floating(&fi->value, value);
}

/* Add a FT_DOUBLE to a proto_tree */
proto_item *
01618 proto_tree_add_double(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            double value)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_DOUBLE);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_double(new_fi, value);

      return pi;
}

proto_item *
01638 proto_tree_add_double_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            double value)
{
      proto_item        *pi;

      pi = proto_tree_add_double(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01653 proto_tree_add_double_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            double value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_double(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_DOUBLE value */
static void
proto_tree_set_double(field_info *fi, double value)
{
      fvalue_set_floating(&fi->value, value);
}

/* Add FT_UINT{8,16,24,32} to a proto_tree */
proto_item *
01679 proto_tree_add_uint(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value)
{
      proto_item        *pi = NULL;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      switch(hfinfo->type) {
            case FT_UINT8:
            case FT_UINT16:
            case FT_UINT24:
            case FT_UINT32:
            case FT_FRAMENUM:
                  pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length,
                              &new_fi);
                  proto_tree_set_uint(new_fi, value);
                  break;

            default:
                  g_assert_not_reached();
      }

      return pi;
}

proto_item *
01709 proto_tree_add_uint_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value)
{
      proto_item        *pi;

      pi = proto_tree_add_uint(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01724 proto_tree_add_uint_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint32 value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_uint(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_UINT{8,16,24,32} value */
static void
proto_tree_set_uint(field_info *fi, guint32 value)
{
      header_field_info *hfinfo;
      guint32                 integer;

      hfinfo = fi->hfinfo;
      integer = value;

      if (hfinfo->bitmask) {
            /* Mask out irrelevant portions */
            integer &= hfinfo->bitmask;

            /* Shift bits */
            if (hfinfo->bitshift > 0) {
                  integer >>= hfinfo->bitshift;
            }
      }
      fvalue_set_integer(&fi->value, integer);
}

/* Add FT_UINT64 to a proto_tree */
proto_item *
01765 proto_tree_add_uint64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint64 value)
{
      proto_item        *pi = NULL;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_UINT64);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_uint64(new_fi, value);

      return pi;
}

proto_item *
proto_tree_add_uint64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            guint64 value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_uint64(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Add FT_INT{8,16,24,32} to a proto_tree */
proto_item *
01804 proto_tree_add_int(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            gint32 value)
{
      proto_item        *pi = NULL;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      switch(hfinfo->type) {
            case FT_INT8:
            case FT_INT16:
            case FT_INT24:
            case FT_INT32:
                  pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length,
                              &new_fi);
                  proto_tree_set_int(new_fi, value);
                  break;

            default:
                  g_assert_not_reached();
      }

      return pi;
}

proto_item *
01833 proto_tree_add_int_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            gint32 value)
{
      proto_item        *pi;

      pi = proto_tree_add_int(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      PROTO_ITEM_SET_HIDDEN(pi);

      return pi;
}

proto_item *
01848 proto_tree_add_int_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            gint32 value, const char *format, ...)
{
      proto_item        *pi = NULL;
      va_list                 ap;

      pi = proto_tree_add_int(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}

/* Set the FT_INT{8,16,24,32} value */
static void
proto_tree_set_int(field_info *fi, gint32 value)
{
      header_field_info *hfinfo;
      guint32                 integer;

      hfinfo = fi->hfinfo;
      integer = (guint32) value;

      if (hfinfo->bitmask) {
            /* Mask out irrelevant portions */
            integer &= hfinfo->bitmask;

            /* Shift bits */
            if (hfinfo->bitshift > 0) {
                  integer >>= hfinfo->bitshift;
            }
      }
      fvalue_set_integer(&fi->value, integer);
}

/* Add FT_INT64 to a proto_tree */
proto_item *
01889 proto_tree_add_int64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            gint64 value)
{
      proto_item        *pi = NULL;
      field_info        *new_fi;
      header_field_info *hfinfo;

      if (!tree)
            return (NULL);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      g_assert(hfinfo->type == FT_INT64);

      pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi);
      proto_tree_set_uint64(new_fi, (guint64)value);

      return pi;
}

proto_item *
proto_tree_add_int64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length,
            gint64 value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;

      pi = proto_tree_add_int64(tree, hfindex, tvb, start, length, value);
      if (pi == NULL)
            return (NULL);

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);

      return pi;
}


/* Add a field_info struct to the proto_tree, encapsulating it in a proto_node */
static proto_item *
proto_tree_add_node(proto_tree *tree, field_info *fi)
{
      proto_node *pnode, *tnode, *sibling;
      field_info *tfi;

      /*
       * Make sure "tree" is ready to have subtrees under it, by
       * checking whether it's been given an ett_ value.
       *
       * "tnode->finfo" may be null; that's the case for the root
       * node of the protocol tree.  That node is not displayed,
       * so it doesn't need an ett_ value to remember whether it
       * was expanded.
       */
      tnode = tree;
      tfi = tnode->finfo;
      if (tfi != NULL && (tfi->tree_type < 0 || tfi->tree_type >= num_tree_types)) {
            REPORT_DISSECTOR_BUG(g_strdup_printf("\"%s\" - \"%s\" tfi->tree_type: %u invalid (%s:%u)",
                  fi->hfinfo->name, fi->hfinfo->abbrev, tfi->tree_type, __FILE__, __LINE__));
        /* XXX - is it safe to continue here? */
    }

      g_assert(tfi == NULL ||
          (tfi->tree_type >= 0 && tfi->tree_type < num_tree_types));

      PROTO_NODE_NEW(pnode);
      pnode->parent = tnode;
      pnode->finfo = fi;
      pnode->tree_data = PTREE_DATA(tree);

      if (tnode->last_child != NULL) {
            sibling = tnode->last_child;
            g_assert(sibling->next == NULL);
            sibling->next = pnode;
      } else
            tnode->first_child = pnode;
      tnode->last_child = pnode;

      return (proto_item*)pnode;
}


/* Generic way to allocate field_info and add to proto_tree.
 * Sets *pfi to address of newly-allocated field_info struct, if pfi is
 * non-NULL. */
static proto_item *
proto_tree_add_pi(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
    gint *length, field_info **pfi)
{
      proto_item  *pi;
      field_info  *fi;
      GHashTable  *hash;
      GPtrArray   *ptrs;

      if (!tree)
            return(NULL);

      fi = alloc_field_info(tree, hfindex, tvb, start, length);
      pi = proto_tree_add_node(tree, fi);

      /* If the proto_tree wants to keep a record of this finfo
       * for quick lookup, then record it. */
      hash = PTREE_DATA(tree)->interesting_hfids;
      ptrs = g_hash_table_lookup(hash, GINT_TO_POINTER(hfindex));
      if (ptrs) {
            g_ptr_array_add(ptrs, fi);
      }

      /* Does the caller want to know the fi pointer? */
      if (pfi) {
            *pfi = fi;
      }

      return pi;
}

static field_info *
alloc_field_info(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
    gint *length)
{
      header_field_info *hfinfo;
      field_info        *fi;

      /*
       * We only allow a null tvbuff if the item has a zero length,
       * i.e. if there's no data backing it.
       */
      g_assert(tvb != NULL || *length == 0);

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);

      if (*length == -1) {
            /*
             * For FT_NONE, FT_PROTOCOL, FT_BYTES, and FT_STRING fields,
             * a length of -1 means "set the length to what remains in
             * the tvbuff".
             *
             * The assumption is either that
             *
             *    1) the length of the item can only be determined
             *       by dissection (typically true of items with
             *       subitems, which are probably FT_NONE or
             *       FT_PROTOCOL)
             *
             * or
             *
             *    2) if the tvbuff is "short" (either due to a short
             *       snapshot length or due to lack of reassembly of
             *       fragments/segments/whatever), we want to display
             *       what's available in the field (probably FT_BYTES
             *       or FT_STRING) and then throw an exception later
             *
             * or
             *
             *    3) the field is defined to be "what's left in the
             *       packet"
             *
             * so we set the length to what remains in the tvbuff so
             * that, if we throw an exception while dissecting, it
             * has what is probably the right value.
             *
             * For FT_STRINGZ, it means "the string is null-terminated,
             * not null-padded; set the length to the actual length
             * of the string", and if the tvbuff if short, we just
             * throw an exception.
             *
             * It's not valid for any other type of field.
             */
            switch (hfinfo->type) {

            case FT_PROTOCOL:
                  /*
                   * We allow this to be zero-length - for
                   * example, an ONC RPC NULL procedure has
                   * neither arguments nor reply, so the
                   * payload for that protocol is empty.
                   *
                   * However, if the length is negative, the
                   * start offset is *past* the byte past the
                   * end of the tvbuff, so we throw an
                   * exception.
                   */
                  *length = tvb_length_remaining(tvb, start);
                  if (*length < 0) {
                        /*
                         * Use "tvb_ensure_bytes_exist()"
                         * to force the appropriate exception
                         * to be thrown.
                         */
                        tvb_ensure_bytes_exist(tvb, start, 0);
                  }
                  g_assert(*length >= 0);
                  break;

            case FT_NONE:
            case FT_BYTES:
            case FT_STRING:
                  *length = tvb_ensure_length_remaining(tvb, start);
                  g_assert(*length >= 0);
                  break;

            case FT_STRINGZ:
                  /*
                   * Leave the length as -1, so our caller knows
                   * it was -1.
                   */
                  break;

            default:
                  g_assert_not_reached();
            }
      } else {
            if (*length < 0) {
                  REPORT_DISSECTOR_BUG(g_strdup_printf("\"%s\" - \"%s\" invalid length: %d (%s:%u)",
                      hfinfo->name, hfinfo->abbrev, *length,
                      __FILE__, __LINE__));
            }
      }

      FIELD_INFO_NEW(fi);

      fi->hfinfo = hfinfo;
      fi->start = start;
      fi->start+=(tvb)?TVB_RAW_OFFSET(tvb):0;
      fi->length = *length;
      fi->tree_type = -1;
      fi->flags = 0;
      if (!PTREE_DATA(tree)->visible)
            FI_SET_FLAG(fi, FI_HIDDEN);
      fvalue_init(&fi->value, fi->hfinfo->type);
      fi->rep = NULL;

      /* add the data source tvbuff */
      fi->ds_tvb=tvb?TVB_GET_DS_TVB(tvb):NULL;

      return fi;
}

/* Set representation of a proto_tree entry, if the protocol tree is to
   be visible. */
static void
proto_tree_set_representation(proto_item *pi, const char *format, va_list ap)
{
      int                           ret;  /*tmp return value */
      field_info *fi = PITEM_FINFO(pi);

      if (!PROTO_ITEM_IS_HIDDEN(pi)) {
            ITEM_LABEL_NEW(fi->rep);
            ret = vsnprintf(fi->rep->representation, ITEM_LABEL_LENGTH, format, ap);
            if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                  fi->rep->representation[ITEM_LABEL_LENGTH - 1] = '\0';
      }
}

/* Set text of proto_item after having already been created. */
void
02145 proto_item_set_text(proto_item *pi, const char *format, ...)
{
      field_info *fi = NULL;
      va_list     ap;

      if (pi==NULL) {
            return;
      }

      fi = PITEM_FINFO(pi);

      if(fi->rep){
            ITEM_LABEL_FREE(fi->rep);
      }

      va_start(ap, format);
      proto_tree_set_representation(pi, format, ap);
      va_end(ap);
}

/* Append to text of proto_item after having already been created. */
void
02167 proto_item_append_text(proto_item *pi, const char *format, ...)
{
      field_info *fi = NULL;
      size_t curlen;
      va_list     ap;
      int                           ret;  /*tmp return value */

      if (pi==NULL) {
            return;
      }

      fi = PITEM_FINFO(pi);

    if (!PROTO_ITEM_IS_HIDDEN(pi)) {
            va_start(ap, format);

            /*
             * If we don't already have a representation,
             * generate the default representation.
             */
            if (fi->rep == NULL) {
                  ITEM_LABEL_NEW(fi->rep);
                  proto_item_fill_label(fi, fi->rep->representation);
            }

            curlen = strlen(fi->rep->representation);
            if (ITEM_LABEL_LENGTH > curlen) {
                  ret = vsnprintf(fi->rep->representation + curlen,
                      ITEM_LABEL_LENGTH - curlen, format, ap);
                  if ((ret == -1) || (ret >= (int)(ITEM_LABEL_LENGTH - curlen)))
                        fi->rep->representation[ITEM_LABEL_LENGTH - 1] = '\0';
            }
            va_end(ap);
      }
}

void
02204 proto_item_set_len(proto_item *pi, gint length)
{
      field_info *fi;

      if (pi == NULL)
            return;
      fi = PITEM_FINFO(pi);
      g_assert(length >= 0);
      fi->length = length;
}

/*
 * Sets the length of the item based on its start and on the specified
 * offset, which is the offset past the end of the item; as the start
 * in the item is relative to the beginning of the data source tvbuff,
 * we need to pass in a tvbuff - the end offset is relative to the beginning
 * of that tvbuff.
 */
void
02223 proto_item_set_end(proto_item *pi, tvbuff_t *tvb, gint end)
{
      field_info *fi;

      if (pi == NULL)
            return;
      fi = PITEM_FINFO(pi);
      end += TVB_RAW_OFFSET(tvb);
      g_assert(end >= fi->start);
      fi->length = end - fi->start;
}

int
02236 proto_item_get_len(proto_item *pi)
{
      field_info *fi = PITEM_FINFO(pi);
      return fi->length;
}

proto_tree*
02243 proto_tree_create_root(void)
{
      proto_node  *pnode;

      /* Initialize the proto_node */
      PROTO_NODE_NEW(pnode);
      pnode->parent = NULL;
      pnode->finfo = NULL;
      pnode->tree_data = g_new(tree_data_t, 1);

      /* Initialize the tree_data_t */
      pnode->tree_data->interesting_hfids =
          g_hash_table_new(g_direct_hash, g_direct_equal);

      /* Set the default to FALSE so it's easier to
       * find errors; if we expect to see the protocol tree
       * but for some reason the default 'visible' is not
       * changed, then we'll find out very quickly. */
      pnode->tree_data->visible = FALSE;

      return (proto_tree*) pnode;
}


/* "prime" a proto_tree with a single hfid that a dfilter
 * is interested in. */
void
proto_tree_prime_hfid(proto_tree *tree, gint hfid)
{
      g_hash_table_insert(PTREE_DATA(tree)->interesting_hfids,
            GINT_TO_POINTER(hfid), g_ptr_array_new());
}


proto_tree*
02278 proto_item_add_subtree(proto_item *pi,  gint idx) {
      field_info *fi;

      if (!pi)
            return(NULL);

      fi = PITEM_FINFO(pi);
      g_assert(idx >= 0 && idx < num_tree_types);
      fi->tree_type = idx;
      return (proto_tree*) pi;
}

proto_tree*
02291 proto_item_get_subtree(proto_item *pi) {
      field_info *fi;

      if (!pi)
            return(NULL);
      fi = PITEM_FINFO(pi);
      if (fi->tree_type == -1)
            return(NULL);
      return (proto_tree*) pi;
}

proto_item*
02303 proto_item_get_parent(proto_item *ti) {
      if (!ti)
            return (NULL);
      return ti->parent;
}

proto_item*
02310 proto_item_get_parent_nth(proto_item *ti, int gen) {
      if (!ti)
            return (NULL);
      while (gen--) {
            ti = ti->parent;
            if (!ti)
                  return (NULL);
      }
      return ti;
}


proto_item* 
02323 proto_tree_get_parent(proto_tree *tree) {
      if (!tree)
            return (NULL);
      return (proto_item*) tree;
}


int
02331 proto_register_protocol(char *name, char *short_name, char *filter_name)
{
      protocol_t *protocol;
      header_field_info *hfinfo;
      int proto_id;
    char *existing_name;
    gint *key;

      /*
       * Make sure there's not already a protocol with any of those
       * names.  Crash if there is, as that's an error in the code
     * or an inappropriate plugin.
     * This situation has to be fixed to not register more than one
       * protocol with the same name.
       *
     * This is done by reducing the number of strcmp (and alike) calls as much as possible,
     * as this significally slows down startup time.
     *
     * Drawback: As a hash value is used to reduce insert time, 
     * this might lead to a hash collision.
     * However, as we have around 500+ protocols and we're using a 32 bit int this is very,
     * very unlikely.
     */

    key = g_malloc (sizeof(gint));
    *key = g_str_hash(name);
    existing_name = g_hash_table_lookup(proto_names, key);
    if (existing_name != NULL) {
        /* g_error will terminate the program */
        g_error("The protocol name \"%s\" is existing more than one time!"
            " This might be caused by an inappropriate plugin or a development error.", name);
    }
    g_hash_table_insert(proto_names, key, name);

    key = g_malloc (sizeof(gint));
    *key = g_str_hash(short_name);
    existing_name = g_hash_table_lookup(proto_short_names, key);
    if (existing_name != NULL) {
        g_error("The protocol short_name \"%s\" is existing more than one time!"
            " This might be caused by an inappropriate plugin or a development error.", short_name);
    }
    g_hash_table_insert(proto_short_names, key, short_name);

    key = g_malloc (sizeof(gint));
    *key = g_str_hash(filter_name);
    existing_name = g_hash_table_lookup(proto_filter_names, key);
    if (existing_name != NULL) {
        g_error("The protocol filter_name \"%s\" is existing more than one time!"
            " This might be caused by an inappropriate plugin or a development error.", filter_name);
    }
    g_hash_table_insert(proto_filter_names, key, filter_name);

      /* Add this protocol to the list of known protocols; the list
         is sorted by protocol short name. */
      protocol = g_malloc(sizeof (protocol_t));
      protocol->name = name;
      protocol->short_name = short_name;
      protocol->filter_name = filter_name;
      protocol->fields = NULL;
      protocol->is_enabled = TRUE; /* protocol is enabled by default */
      protocol->can_toggle = TRUE;
    /* list will be sorted later by name, when all protocols completed registering */
    protocols = g_list_append(protocols, protocol);

      /* Here we do allocate a new header_field_info struct */
      hfinfo = g_mem_chunk_alloc(gmc_hfinfo);
      hfinfo->name = name;
      hfinfo->abbrev = filter_name;
      hfinfo->type = FT_PROTOCOL;
      hfinfo->strings = NULL;
      hfinfo->bitmask = 0;
      hfinfo->bitshift = 0;
      hfinfo->blurb = "";
      hfinfo->parent = -1; /* this field differentiates protos and fields */

      proto_id = proto_register_field_init(hfinfo, hfinfo->parent);
      protocol->proto_id = proto_id;
      return proto_id;
}

/*
 * Routines to use to iterate over the protocols.
 * The argument passed to the iterator routines is an opaque cookie to
 * their callers; it's the GList pointer for the current element in
 * the list.
 * The ID of the protocol is returned, or -1 if there is no protocol.
 */
int
proto_get_first_protocol(void **cookie)
{
      protocol_t *protocol;

      if (protocols == NULL)
            return -1;
      *cookie = protocols;
      protocol = protocols->data;
      return protocol->proto_id;
}

int
proto_get_next_protocol(void **cookie)
{
      GList *list_item = *cookie;
      protocol_t *protocol;

      list_item = g_list_next(list_item);
      if (list_item == NULL)
            return -1;
      *cookie = list_item;
      protocol = list_item->data;
      return protocol->proto_id;
}

header_field_info *
proto_get_first_protocol_field(int proto_id, void **cookie)
{
      protocol_t *protocol = find_protocol_by_id(proto_id);
      hf_register_info *ptr;

      if ((protocol == NULL) || (protocol->fields == NULL))
            return NULL;

      *cookie = protocol->fields;
      ptr = protocol->fields->data;
      return &ptr->hfinfo;
}

header_field_info *
proto_get_next_protocol_field(void **cookie)
{
      GList *list_item = *cookie;
      hf_register_info *ptr;

      list_item = g_list_next(list_item);
      if (list_item == NULL)
            return NULL;

      *cookie = list_item;
      ptr = list_item->data;
      return &ptr->hfinfo;
}

/*
 * Find the protocol list entry for a protocol given its field ID.
 */
static gint
compare_proto_id(gconstpointer proto_arg, gconstpointer id_arg)
{
      const protocol_t *protocol = proto_arg;
      const int *id_ptr = id_arg;

      return (protocol->proto_id == *id_ptr) ? 0 : 1;
}

protocol_t *
02486 find_protocol_by_id(int proto_id)
{
      GList *list_entry;

      list_entry = g_list_find_custom(protocols, &proto_id, compare_proto_id);
      if (list_entry == NULL)
            return NULL;
      return list_entry->data;
}

static gint compare_filter_name(gconstpointer proto_arg,
                        gconstpointer filter_name)
{
      const protocol_t *protocol = proto_arg;
      const gchar* f_name = filter_name;

      return (strcmp(protocol->filter_name, f_name));
}

int
02506 proto_get_id(protocol_t *protocol)
{
      return protocol->proto_id;
}

02511 int proto_get_id_by_filter_name(gchar* filter_name)
{
      GList *list_entry;
      protocol_t *protocol;

      list_entry = g_list_find_custom(protocols, filter_name,
          compare_filter_name);
      if (list_entry == NULL)
            return -1;
      protocol = list_entry->data;
      return protocol->proto_id;
}

char *
02525 proto_get_protocol_name(int proto_id)
{
      protocol_t *protocol;

      protocol = find_protocol_by_id(proto_id);
      return protocol->name;
}

char *
02534 proto_get_protocol_short_name(protocol_t *protocol)
{
      if (protocol == NULL)
            return "(none)";
      return protocol->short_name;
}

char *
02542 proto_get_protocol_filter_name(int proto_id)
{
      protocol_t *protocol;

      protocol = find_protocol_by_id(proto_id);
      return protocol->filter_name;
}

gboolean
02551 proto_is_protocol_enabled(protocol_t *protocol)
{
      return protocol->is_enabled;
}

gboolean
02557 proto_can_toggle_protocol(int proto_id)
{
      protocol_t *protocol;

      protocol = find_protocol_by_id(proto_id);
      return protocol->can_toggle;
}

void
02566 proto_set_decoding(int proto_id, gboolean enabled)
{
      protocol_t *protocol;

      protocol = find_protocol_by_id(proto_id);
      g_assert(protocol->can_toggle);
      protocol->is_enabled = enabled;
}

void
02576 proto_set_cant_toggle(int proto_id)
{
      protocol_t *protocol;

      protocol = find_protocol_by_id(proto_id);
      protocol->can_toggle = FALSE;
}

/* for use with static arrays only, since we don't allocate our own copies
of the header_field_info struct contained within the hf_register_info struct */
void
02587 proto_register_field_array(int parent, hf_register_info *hf, int num_records)
{
      int               field_id, i;
      hf_register_info  *ptr = hf;
      protocol_t        *proto;

      proto = find_protocol_by_id(parent);
      for (i = 0; i < num_records; i++, ptr++) {
            /*
             * Make sure we haven't registered this yet.
             * Most fields have variables associated with them
             * that are initialized to -1; some have array elements,
             * or possibly uninitialized variables, so we also allow
             * 0 (which is unlikely to be the field ID we get back
             * from "proto_register_field_init()").
             */
            g_assert(*ptr->p_id == -1 || *ptr->p_id == 0);

            if (proto != NULL) {
                  if (proto->fields == NULL) {
                        proto->fields = g_list_append(NULL, ptr);
                        proto->last_field = proto->fields;
                  } else {
                        proto->last_field =
                            g_list_append(proto->last_field, ptr)->next;
                  }
            }
            field_id = proto_register_field_init(&ptr->hfinfo, parent);
            *ptr->p_id = field_id;
      }
}

static int
proto_register_field_init(header_field_info *hfinfo, int parent)
{
      /* The field must have names */
      g_assert(hfinfo->name);
      g_assert(hfinfo->abbrev);

      /* These types of fields are allowed to have value_strings or true_false_strings */
      g_assert((hfinfo->strings == NULL) || (
                  (hfinfo->type == FT_UINT8) ||
                  (hfinfo->type == FT_UINT16) ||
                  (hfinfo->type == FT_UINT24) ||
                  (hfinfo->type == FT_UINT32) ||
                  (hfinfo->type == FT_INT8) ||
                  (hfinfo->type == FT_INT16) ||
                  (hfinfo->type == FT_INT24) ||
                  (hfinfo->type == FT_INT32) ||
                  (hfinfo->type == FT_BOOLEAN) ||
                  (hfinfo->type == FT_FRAMENUM) ));

      switch (hfinfo->type) {

      case FT_UINT8:
      case FT_UINT16:
      case FT_UINT24:
      case FT_UINT32:
      case FT_INT8:
      case FT_INT16:
      case FT_INT24:
      case FT_INT32:
            /* Require integral types (other than frame number, which is
               always displayed in decimal) to have a number base */
            g_assert(hfinfo->display != BASE_NONE);
            break;

      case FT_FRAMENUM:
            /* Don't allow bitfields or value strings for frame numbers */
            g_assert(hfinfo->bitmask == 0);
            g_assert(hfinfo->strings == NULL);
            break;

      default:
            break;
      }
      /* if this is a bitfield, compute bitshift */
      if (hfinfo->bitmask) {
            while ((hfinfo->bitmask & (1 << hfinfo->bitshift)) == 0)
                  hfinfo->bitshift++;
      }

      hfinfo->parent = parent;
      hfinfo->same_name_next = NULL;
      hfinfo->same_name_prev = NULL;

      /* if we always add and never delete, then id == len - 1 is correct */
      if(gpa_hfinfo.len>=gpa_hfinfo.allocated_len){
            if(!gpa_hfinfo.hfi){
                  gpa_hfinfo.allocated_len=1000;
                  gpa_hfinfo.hfi=g_malloc(sizeof(header_field_info *)*1000);
            } else {
                  gpa_hfinfo.allocated_len+=1000;
                  gpa_hfinfo.hfi=g_realloc(gpa_hfinfo.hfi, sizeof(header_field_info *)*gpa_hfinfo.allocated_len);
            }
      }
      gpa_hfinfo.hfi[gpa_hfinfo.len]=hfinfo;
      gpa_hfinfo.len++;
      hfinfo->id = gpa_hfinfo.len - 1;

      /* if we have real names, enter this field in the name tree */
      if ((hfinfo->name[0] != 0) && (hfinfo->abbrev[0] != 0 )) {

            header_field_info *same_name_hfinfo, *same_name_next_hfinfo;
            char *p;
            guchar c;

            /* Check that the filter name (abbreviation) is legal;
             * it must contain only alphanumerics, '-', "_", and ".". */
            for (p = hfinfo->abbrev; (c = *p) != '\0'; p++)
                  g_assert(isalnum(c) || c == '-' || c == '_' ||
                      c == '.');

            /* We allow multiple hfinfo's to be registered under the same
             * abbreviation. This was done for X.25, as, depending
             * on whether it's modulo-8 or modulo-128 operation,
             * some bitfield fields may be in different bits of
             * a byte, and we want to be able to refer to that field
             * with one name regardless of whether the packets
             * are modulo-8 or modulo-128 packets. */
            same_name_hfinfo = g_tree_lookup(gpa_name_tree, hfinfo->abbrev);
            if (same_name_hfinfo) {
                  /* There's already a field with this name.
                   * Put it after that field in the list of
                   * fields with this name, then allow the code
                   * after this if{} block to replace the old
                   * hfinfo with the new hfinfo in the GTree. Thus,
                   * we end up with a linked-list of same-named hfinfo's,
                   * with the root of the list being the hfinfo in the GTree */
                  same_name_next_hfinfo =
                      same_name_hfinfo->same_name_next;

                  hfinfo->same_name_next = same_name_next_hfinfo;
                  if (same_name_next_hfinfo)
                        same_name_next_hfinfo->same_name_prev = hfinfo;

                  same_name_hfinfo->same_name_next = hfinfo;
                  hfinfo->same_name_prev = same_name_hfinfo;
            }
            g_tree_insert(gpa_name_tree, hfinfo->abbrev, hfinfo);
      }

      return hfinfo->id;
}

void
02733 proto_register_subtree_array(gint *const *indices, int num_indices)
{
      int   i;
      gint  *const *ptr = indices;

      /*
       * If we've already allocated the array of tree types, expand
       * it; this lets plugins such as mate add tree types after
       * the initial startup.  (If we haven't already allocated it,
       * we don't allocate it; on the first pass, we just assign
       * ett values and keep track of how many we've assigned, and
       * when we're finished registering all dissectors we allocate
       * the array, so that we do only one allocation rather than
       * wasting CPU time and memory by growing the array for each
       * dissector that registers ett values.)
       */
      if (tree_is_expanded != NULL) {
            tree_is_expanded =
                g_realloc(tree_is_expanded,
                    (num_tree_types+num_indices)*sizeof (gint *));
            memset(tree_is_expanded + num_tree_types, 0,
                num_indices*sizeof (gint *));
      }

      /*
       * Assign "num_indices" subtree numbers starting at "num_tree_types",
       * returning the indices through the pointers in the array whose
       * first element is pointed to by "indices", and update
       * "num_tree_types" appropriately.
       */
      for (i = 0; i < num_indices; i++, ptr++, num_tree_types++)
            **ptr = num_tree_types;
}

void
02768 proto_item_fill_label(field_info *fi, gchar *label_str)
{
      header_field_info       *hfinfo = fi->hfinfo;

      guint8                        *bytes;
      guint32                       integer;
      ipv4_addr               *ipv4;
      guint32                       n_addr; /* network-order IPv4 address */
      int                           ret;  /*tmp return value */

      switch(hfinfo->type) {
            case FT_NONE:
            case FT_PROTOCOL:
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s", hfinfo->name);
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            case FT_BOOLEAN:
                  fill_label_boolean(fi, label_str);
                  break;

            case FT_BYTES:
            case FT_UINT_BYTES:
                  bytes = fvalue_get(&fi->value);
                  if (bytes) {
                        ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                              "%s: %s", hfinfo->name,
                               bytes_to_str(bytes, fvalue_length(&fi->value)));
                        if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                              label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  }
                  else {
                        ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                              "%s: <MISSING>", hfinfo->name);
                        if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                              label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  }
                  break;

            /* Four types of integers to take care of:
             *    Bitfield, with val_string
             *    Bitfield, w/o val_string
             *    Non-bitfield, with val_string
             *    Non-bitfield, w/o val_string
             */
            case FT_UINT8:
            case FT_UINT16:
            case FT_UINT24:
            case FT_UINT32:
            case FT_FRAMENUM:
                  if (hfinfo->bitmask) {
                        if (hfinfo->strings) {
                              fill_label_enumerated_bitfield(fi, label_str);
                        }
                        else {
                              fill_label_numeric_bitfield(fi, label_str);
                        }
                  }
                  else {
                        if (hfinfo->strings) {
                              fill_label_enumerated_uint(fi, label_str);
                        }
                        else {
                              fill_label_uint(fi, label_str);
                        }
                  }
                  break;

            case FT_UINT64:
                  fill_label_uint64(fi, label_str);
                  break;

            case FT_INT8:
            case FT_INT16:
            case FT_INT24:
            case FT_INT32:
                  g_assert(!hfinfo->bitmask);
                  if (hfinfo->strings) {
                        fill_label_enumerated_int(fi, label_str);
                  }
                  else {
                        fill_label_int(fi, label_str);
                  }
                  break;

            case FT_INT64:
                  fill_label_int64(fi, label_str);
                  break;

            case FT_FLOAT:
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s: %." STRINGIFY(FLT_DIG) "f",
                        hfinfo->name, fvalue_get_floating(&fi->value));
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            case FT_DOUBLE:
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s: %." STRINGIFY(DBL_DIG) "g",
                        hfinfo->name, fvalue_get_floating(&fi->value));
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            case FT_ABSOLUTE_TIME:
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s: %s", hfinfo->name,
                        abs_time_to_str(fvalue_get(&fi->value)));
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            case FT_RELATIVE_TIME:
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s: %s seconds", hfinfo->name,
                        rel_time_to_secs_str(fvalue_get(&fi->value)));
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            case FT_IPXNET:
                  integer = fvalue_get_integer(&fi->value);
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s: 0x%08X (%s)", hfinfo->name,
                        integer, get_ipxnet_name(integer));
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            case FT_ETHER:
                  bytes = fvalue_get(&fi->value);
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s: %s (%s)", hfinfo->name,
                        ether_to_str(bytes),
                        get_ether_name(bytes));
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            case FT_IPv4:
                  ipv4 = fvalue_get(&fi->value);
                  n_addr = ipv4_get_net_order_addr(ipv4);
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s: %s (%s)", hfinfo->name,
                        get_hostname(n_addr),
                        ip_to_str((guint8*)&n_addr));
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            case FT_IPv6:
                  bytes = fvalue_get(&fi->value);
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s: %s (%s)", hfinfo->name,
                        get_hostname6((struct e_in6_addr *)bytes),
                        ip6_to_str((struct e_in6_addr*)bytes));
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            case FT_STRING:
            case FT_STRINGZ:
            case FT_UINT_STRING:
                  bytes = fvalue_get(&fi->value);
                  ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                        "%s: %s", hfinfo->name,
                        format_text(bytes, strlen(bytes)));
                  if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
                        label_str[ITEM_LABEL_LENGTH - 1] = '\0';
                  break;

            default:
                  g_error("hfinfo->type %d (%s) not handled\n",
                              hfinfo->type,
                              ftype_name(hfinfo->type));
                  g_assert_not_reached();
                  break;
      }
}

static void
fill_label_boolean(field_info *fi, gchar *label_str)
{
      char  *p = label_str;
      int   bitfield_byte_length = 0, bitwidth;
      guint32     unshifted_value;
      guint32     value;
      int                           ret;  /*tmp return value */

      header_field_info       *hfinfo = fi->hfinfo;
      static const true_false_string      default_tf = { "True", "False" };
      const true_false_string       *tfstring = &default_tf;

      if (hfinfo->strings) {
            tfstring = (const struct true_false_string*) hfinfo->strings;
      }

      value = fvalue_get_integer(&fi->value);
      if (hfinfo->bitmask) {
            /* Figure out the bit width */
            bitwidth = hfinfo_bitwidth(hfinfo);

            /* Un-shift bits */
            unshifted_value = value;
            if (hfinfo->bitshift > 0) {
                  unshifted_value <<= hfinfo->bitshift;
            }

            /* Create the bitfield first */
            p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
            bitfield_byte_length = p - label_str;
      }

      /* Fill in the textual info */
      ret = snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
            "%s: %s",  hfinfo->name,
            value ? tfstring->true_string : tfstring->false_string);
      if ((ret == -1) || (ret >= (ITEM_LABEL_LENGTH - bitfield_byte_length)))
            label_str[ITEM_LABEL_LENGTH - 1] = '\0';
}


/* Fills data for bitfield ints with val_strings */
static void
fill_label_enumerated_bitfield(field_info *fi, gchar *label_str)
{
      char *format = NULL, *p;
      int bitfield_byte_length, bitwidth;
      guint32 unshifted_value;
      guint32 value;
      int                           ret;  /*tmp return value */

      header_field_info *hfinfo = fi->hfinfo;

      /* Figure out the bit width */
      bitwidth = hfinfo_bitwidth(hfinfo);

      /* Pick the proper format string */
      format = hfinfo_uint_vals_format(hfinfo);

      /* Un-shift bits */
      unshifted_value = fvalue_get_integer(&fi->value);
      value = unshifted_value;
      if (hfinfo->bitshift > 0) {
            unshifted_value <<= hfinfo->bitshift;
      }

      /* Create the bitfield first */
      p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
      bitfield_byte_length = p - label_str;

      /* Fill in the textual info using stored (shifted) value */
      ret = snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
                  format,  hfinfo->name,
                  val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value);
      if ((ret == -1) || (ret >= (ITEM_LABEL_LENGTH - bitfield_byte_length)))
            label_str[ITEM_LABEL_LENGTH - 1] = '\0';
}

static void
fill_label_numeric_bitfield(field_info *fi, gchar *label_str)
{
      char *format = NULL, *p;
      int bitfield_byte_length, bitwidth;
      guint32 unshifted_value;
      guint32 value;
      int                           ret;  /*tmp return value */

      header_field_info *hfinfo = fi->hfinfo;

      /* Figure out the bit width */
      bitwidth = hfinfo_bitwidth(hfinfo);

      /* Pick the proper format string */
      format = hfinfo_uint_format(hfinfo);

      /* Un-shift bits */
      unshifted_value = fvalue_get_integer(&fi->value);
      value = unshifted_value;
      if (hfinfo->bitshift > 0) {
            unshifted_value <<= hfinfo->bitshift;
      }

      /* Create the bitfield using */
      p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
      bitfield_byte_length = p - label_str;

      /* Fill in the textual info using stored (shifted) value */
      ret = snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
                  format,  hfinfo->name, value);
      if ((ret == -1) || (ret >= (ITEM_LABEL_LENGTH - bitfield_byte_length)))
            label_str[ITEM_LABEL_LENGTH - 1] = '\0';

}

static void
fill_label_enumerated_uint(field_info *fi, gchar *label_str)
{
      char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint32 value;
      int                           ret;  /*tmp return value */

      /* Pick the proper format string */
      format = hfinfo_uint_vals_format(hfinfo);

      value = fvalue_get_integer(&fi->value);

      /* Fill in the textual info */
      ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                  format,  hfinfo->name,
                  val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value);
      if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
            label_str[ITEM_LABEL_LENGTH - 1] = '\0';
}

static void
fill_label_uint(field_info *fi, gchar *label_str)
{
      char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint32 value;
      int                           ret;  /*tmp return value */

      /* Pick the proper format string */
      format = hfinfo_uint_format(hfinfo);
      value = fvalue_get_integer(&fi->value);

      /* Fill in the textual info */
      ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                  format,  hfinfo->name, value);
      if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
            label_str[ITEM_LABEL_LENGTH - 1] = '\0';
}

static void
fill_label_uint64(field_info *fi, gchar *label_str)
{
      char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint64 value;
      int                           ret;  /*tmp return value */

      /* Pick the proper format string */
      format = hfinfo_uint64_format(hfinfo);
      value = fvalue_get_integer64(&fi->value);

      /* Fill in the textual info */
      ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                  format,  hfinfo->name, value);
      if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
            label_str[ITEM_LABEL_LENGTH - 1] = '\0';
}

static void
fill_label_enumerated_int(field_info *fi, gchar *label_str)
{
      char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint32 value;
      int                           ret;  /*tmp return value */

      /* Pick the proper format string */
      format = hfinfo_int_vals_format(hfinfo);
      value = fvalue_get_integer(&fi->value);

      /* Fill in the textual info */
      ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                  format,  hfinfo->name,
                  val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value);
      if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
            label_str[ITEM_LABEL_LENGTH - 1] = '\0';
}

static void
fill_label_int(field_info *fi, gchar *label_str)
{
      char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint32 value;
      int                           ret;  /*tmp return value */

      /* Pick the proper format string */
      format = hfinfo_int_format(hfinfo);
      value = fvalue_get_integer(&fi->value);

      /* Fill in the textual info */
      ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                  format,  hfinfo->name, value);
      if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
            label_str[ITEM_LABEL_LENGTH - 1] = '\0';
}

static void
fill_label_int64(field_info *fi, gchar *label_str)
{
      char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint64 value;
      int                           ret;  /*tmp return value */

      /* Pick the proper format string */
      format = hfinfo_int64_format(hfinfo);
      value = fvalue_get_integer64(&fi->value);

      /* Fill in the textual info */
      ret = snprintf(label_str, ITEM_LABEL_LENGTH,
                  format,  hfinfo->name, value);
      if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH))
            label_str[ITEM_LABEL_LENGTH - 1] = '\0';
}

int
hfinfo_bitwidth(header_field_info *hfinfo)
{
      int bitwidth = 0;

      if (!hfinfo->bitmask) {
            return 0;
      }

      switch(hfinfo->type) {
            case FT_UINT8:
            case FT_INT8:
                  bitwidth = 8;
                  break;
            case FT_UINT16:
            case FT_INT16:
                  bitwidth = 16;
                  break;
            case FT_UINT24:
            case FT_INT24:
                  bitwidth = 24;
                  break;
            case FT_UINT32:
            case FT_INT32:
                  bitwidth = 32;
                  break;
            case FT_BOOLEAN:
                  bitwidth = hfinfo->display; /* hacky? :) */
                  break;
            default:
                  g_assert_not_reached();
                  ;
      }
      return bitwidth;
}

static char*
hfinfo_uint_vals_format(header_field_info *hfinfo)
{
      char *format = NULL;

      switch(hfinfo->display) {
            case BASE_DEC:
                  format = "%s: %s (%u)";
                  break;
            case BASE_OCT: /* I'm lazy */
                  format = "%s: %s (%o)";
                  break;
            case BASE_HEX:
                  switch(hfinfo->type) {
                        case FT_UINT8:
                              format = "%s: %s (0x%02x)";
                              break;
                        case FT_UINT16:
                              format = "%s: %s (0x%04x)";
                              break;
                        case FT_UINT24:
                              format = "%s: %s (0x%06x)";
                              break;
                        case FT_UINT32:
                              format = "%s: %s (0x%08x)";
                              break;
                        default:
                              g_assert_not_reached();
                              ;
                  }
                  break;
            default:
                  g_assert_not_reached();
                  ;
      }
      return format;
}

static char*
hfinfo_uint_format(header_field_info *hfinfo)
{
      char *format = NULL;

      /* Pick the proper format string */
      if (hfinfo->type == FT_FRAMENUM) {
            /*
             * Frame numbers are always displayed in decimal.
             */
            format = "%s: %u";
      } else {
            switch(hfinfo->display) {
                  case BASE_DEC:
                        format = "%s: %u";
                        break;
                  case BASE_OCT: /* I'm lazy */
                        format = "%s: %o";
                        break;
                  case BASE_HEX:
                        switch(hfinfo->type) {
                              case FT_UINT8:
                                    format = "%s: 0x%02x";
                                    break;
                              case FT_UINT16:
                                    format = "%s: 0x%04x";
                                    break;
                              case FT_UINT24:
                                    format = "%s: 0x%06x";
                                    break;
                              case FT_UINT32:
                                    format = "%s: 0x%08x";
                                    break;
                              default:
                                    g_assert_not_reached();
                                    ;
                        }
                        break;
                  default:
                        g_assert_not_reached();
                        ;
            }
      }
      return format;
}

static char*
hfinfo_int_vals_format(header_field_info *hfinfo)
{
      char *format = NULL;

      switch(hfinfo->display) {
            case BASE_DEC:
                  format = "%s: %s (%d)";
                  break;
            case BASE_OCT: /* I'm lazy */
                  format = "%s: %s (%o)";
                  break;
            case BASE_HEX:
                  switch(hfinfo->type) {
                        case FT_INT8:
                              format = "%s: %s (0x%02x)";
                              break;
                        case FT_INT16:
                              format = "%s: %s (0x%04x)";
                              break;
                        case FT_INT24:
                              format = "%s: %s (0x%06x)";
                              break;
                        case FT_INT32:
                              format = "%s: %s (0x%08x)";
                              break;
                        default:
                              g_assert_not_reached();
                              ;
                  }
                  break;
            default:
                  g_assert_not_reached();
                  ;
      }
      return format;
}

static char*
hfinfo_uint64_format(header_field_info *hfinfo)
{
      char *format = NULL;

      /* Pick the proper format string */
      switch(hfinfo->display) {
            case BASE_DEC:
                  format = "%s: %" PRIu64;
                  break;
            case BASE_OCT: /* I'm lazy */
                  format = "%s: %" PRIo64;
                  break;
            case BASE_HEX:
                  format = "%s: 0x%016" PRIx64;
                  break;
            default:
                  g_assert_not_reached();
                  ;
      }
      return format;
}

static char*
hfinfo_int_format(header_field_info *hfinfo)
{
      char *format = NULL;

      /* Pick the proper format string */
      switch(hfinfo->display) {
            case BASE_DEC:
                  format = "%s: %d";
                  break;
            case BASE_OCT: /* I'm lazy */
                  format = "%s: %o";
                  break;
            case BASE_HEX:
                  switch(hfinfo->type) {
                        case FT_INT8:
                              format = "%s: 0x%02x";
                              break;
                        case FT_INT16:
                              format = "%s: 0x%04x";
                              break;
                        case FT_INT24:
                              format = "%s: 0x%06x";
                              break;
                        case FT_INT32:
                              format = "%s: 0x%08x";
                              break;
                        default:
                              g_assert_not_reached();
                              ;
                  }
                  break;
            default:
                  g_assert_not_reached();
                  ;
      }
      return format;
}

static char*
hfinfo_int64_format(header_field_info *hfinfo)
{
      char *format = NULL;

      /* Pick the proper format string */
      switch(hfinfo->display) {
            case BASE_DEC:
                  format = "%s: %" PRId64;
                  break;
            case BASE_OCT: /* I'm lazy */
                  format = "%s: %" PRIo64;
                  break;
            case BASE_HEX:
                  format = "%s: 0x%016" PRIx64;
                  break;
            default:
                  g_assert_not_reached();
                  ;
      }
      return format;
}



int
03429 proto_registrar_n(void)
{
      return gpa_hfinfo.len;
}

char*
03435 proto_registrar_get_name(int n)
{
      header_field_info *hfinfo;

      PROTO_REGISTRAR_GET_NTH(n, hfinfo);
      return hfinfo->name;
}

char*
03444 proto_registrar_get_abbrev(int n)
{
      header_field_info *hfinfo;

      PROTO_REGISTRAR_GET_NTH(n, hfinfo);
      return hfinfo->abbrev;
}

int
03453 proto_registrar_get_ftype(int n)
{
      header_field_info *hfinfo;

      PROTO_REGISTRAR_GET_NTH(n, hfinfo);
      return hfinfo->type;
}

int
03462 proto_registrar_get_parent(int n)
{
      header_field_info *hfinfo;

      PROTO_REGISTRAR_GET_NTH(n, hfinfo);
      return hfinfo->parent;
}

gboolean
03471 proto_registrar_is_protocol(int n)
{
      header_field_info *hfinfo;

      PROTO_REGISTRAR_GET_NTH(n, hfinfo);
      return (hfinfo->parent == -1 ? TRUE : FALSE);
}

/* Returns length of field in packet (not necessarily the length
 * in our internal representation, as in the case of IPv4).
 * 0 means undeterminable at time of registration
 * -1 means the field is not registered. */
gint
proto_registrar_get_length(int n)
{
      header_field_info *hfinfo;

      PROTO_REGISTRAR_GET_NTH(n, hfinfo);
      return ftype_length(hfinfo->type);
}



/* Looks for a protocol or a field in a proto_tree. Returns TRUE if
 * it exists anywhere, or FALSE if it exists nowhere. */
gboolean
03497 proto_check_for_protocol_or_field(proto_tree* tree, int id)
{
      GPtrArray *ptrs = proto_get_finfo_ptr_array(tree, id);

      if (!ptrs) {
            return FALSE;
      }
      else if (g_ptr_array_len(ptrs) > 0) {
            return TRUE;
      }
      else {
            return FALSE;
      }
}

/* Return GPtrArray* of field_info pointers for all hfindex that appear in tree.
 * This only works if the hfindex was "primed" before the dissection
 * took place, as we just pass back the already-created GPtrArray*.
 * The caller should *not* free the GPtrArray*; proto_tree_free_node()
 * handles that. */
GPtrArray*
proto_get_finfo_ptr_array(proto_tree *tree, int id)
{
      return g_hash_table_lookup(PTREE_DATA(tree)->interesting_hfids,
          GINT_TO_POINTER(id));
}


/* Helper struct and function for proto_find_info() */
typedef struct {
      GPtrArray   *array;
      int         id;
} ffdata_t;

static gboolean
find_finfo(proto_node *node, gpointer data)
{
      field_info *fi = PITEM_FINFO(node);
      if (fi && fi->hfinfo) {
            if (fi->hfinfo->id == ((ffdata_t*)data)->id) {
                  g_ptr_array_add(((ffdata_t*)data)->array, fi);
            }
      }

      /* Don't stop traversing. */
      return FALSE;
}

/* Return GPtrArray* of field_info pointers for all hfindex that appear in a tree.
 * This works on any proto_tree, primed or unprimed, but actually searches
 * the tree, so it is slower than using proto_get_finfo_ptr_array on a primed tree.
 * The caller does need to free the returned GPtrArray with
 * g_ptr_array_free(<array>, FALSE).
 */
GPtrArray*
proto_find_finfo(proto_tree *tree, int id)
{
      ffdata_t    ffdata;

      ffdata.array = g_ptr_array_new();
      ffdata.id = id;

      proto_tree_traverse_pre_order(tree, find_finfo, &ffdata);

      return ffdata.array;
}     


typedef struct {
      guint       offset;
      field_info  *finfo;
      tvbuff_t    *tvb;
} offset_search_t;

static gboolean
check_for_offset(proto_node *node, gpointer data)
{
      field_info          *fi = PITEM_FINFO(node);
      offset_search_t         *offsearch = data;

      /* !fi == the top most container node which holds nothing */
      if (fi && !PROTO_ITEM_IS_HIDDEN(node) && fi->ds_tvb && offsearch->tvb == fi->ds_tvb) {
            if (offsearch->offset >= (guint) fi->start &&
                        offsearch->offset < (guint) (fi->start + fi->length)) {

                  offsearch->finfo = fi;
                  return FALSE; /* keep traversing */
            }
      }
      return FALSE; /* keep traversing */
}

/* Search a proto_tree backwards (from leaves to root) looking for the field
 * whose start/length occupies 'offset' */
/* XXX - I couldn't find an easy way to search backwards, so I search
 * forwards, w/o stopping. Therefore, the last finfo I find will the be
 * the one I want to return to the user. This algorithm is inefficient
 * and could be re-done, but I'd have to handle all the children and
 * siblings of each node myself. When I have more time I'll do that.
 * (yeah right) */
field_info*
03598 proto_find_field_from_offset(proto_tree *tree, guint offset, tvbuff_t *tvb)
{
      offset_search_t         offsearch;

      offsearch.offset = offset;
      offsearch.finfo = NULL;
      offsearch.tvb = tvb;

      proto_tree_traverse_pre_order(tree, check_for_offset, &offsearch);

      return offsearch.finfo;
}

/* Dumps the protocols in the registration database to stdout.  An independent
 * program can take this output and format it into nice tables or HTML or
 * whatever.
 *
 * There is one record per line. The fields are tab-delimited.
 *
 * Field 1 = protocol name
 * Field 2 = protocol short name
 * Field 3 = protocol filter name
 */
void
03622 proto_registrar_dump_protocols(void)
{
      protocol_t        *protocol;
      int               i;
      void              *cookie;

      for (i = proto_get_first_protocol(&cookie); i != -1;
          i = proto_get_next_protocol(&cookie)) {
            protocol = find_protocol_by_id(i);
            printf("%s\t%s\t%s\n", protocol->name, protocol->short_name,
                protocol->filter_name);
      }
}

/* Dumps the value_string and true/false strings for fields that have
 * them. There is one record per line. Fields are tab-delimited.
 * There are two types of records, Value String records and True/False
 * String records. The first field, 'V' or 'T', indicates the type
 * of record.
 *
 * Value Strings
 * -------------
 * Field 1 = 'V'
 * Field 2 = field abbreviation to which this value string corresponds
 * Field 3 = Integer value
 * Field 4 = String
 *
 * True/False Strings
 * ------------------
 * Field 1 = 'T'
 * Field 2 = field abbreviation to which this true/false string corresponds
 * Field 3 = True String
 * Field 4 = False String
 */
void
03657 proto_registrar_dump_values(void)
{
      header_field_info *hfinfo, *parent_hfinfo;
      int               i, len, vi;
      const value_string      *vals;
      const true_false_string *tfs;

      len = gpa_hfinfo.len;
      for (i = 0; i < len ; i++) {
            PROTO_REGISTRAR_GET_NTH(i, hfinfo);

             if (hfinfo->id == hf_text_only) {
                   continue;
             }

            /* ignore protocols */
            if (proto_registrar_is_protocol(i)) {
                  continue;
            }
            /* process header fields */
            else {
                  /*
                   * If this field isn't at the head of the list of
                   * fields with this name, skip this field - all
                   * fields with the same name are really just versions
                   * of the same field stored in different bits, and
                   * should have the same type/radix/value list, and
                   * just differ in their bit masks.  (If a field isn't
                   * a bitfield, but can be, say, 1 or 2 bytes long,
                   * it can just be made FT_UINT16, meaning the
                   * *maximum* length is 2 bytes, and be used
                   * for all lengths.)
                   */
                  if (hfinfo->same_name_prev != NULL)
                        continue;

                  PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo);

                  vals = NULL;
                  tfs = NULL;

                  if (hfinfo->type == FT_UINT8 ||
                        hfinfo->type == FT_UINT16 ||
                        hfinfo->type == FT_UINT24 ||
                        hfinfo->type == FT_UINT32 ||
                        hfinfo->type == FT_UINT64 ||
                        hfinfo->type == FT_INT8 ||
                        hfinfo->type == FT_INT16 ||
                        hfinfo->type == FT_INT24 ||
                        hfinfo->type == FT_INT32 ||
                        hfinfo->type == FT_INT64) {

                        vals = hfinfo->strings;
                  }
                  else if (hfinfo->type == FT_BOOLEAN) {
                        tfs = hfinfo->strings;
                  }

                  /* Print value strings? */
                  if (vals) {
                        vi = 0;
                        while (vals[vi].strptr) {
                              /* Print in the proper base */
                              if (hfinfo->display == BASE_HEX) {
                                    printf("V\t%s\t0x%x\t%s\n",
                                                hfinfo->abbrev,
                                                vals[vi].value,
                                                vals[vi].strptr);
                              }
                              else {
                                    printf("V\t%s\t%u\t%s\n",
                                                hfinfo->abbrev,
                                                vals[vi].value,
                                                vals[vi].strptr);
                              }
                              vi++;
                        }
                  }

                  /* Print true/false strings? */
                  else if (tfs) {
                        printf("T\t%s\t%s\t%s\n", hfinfo->abbrev,
                                    tfs->true_string, tfs->false_string);
                  }
            }
      }
}

/* Dumps the contents of the registration database to stdout. An indepedent
 * program can take this output and format it into nice tables or HTML or
 * whatever.
 *
 * There is one record per line. Each record is either a protocol or a header
 * field, differentiated by the first field. The fields are tab-delimited.
 *
 * Protocols
 * ---------
 * Field 1 = 'P'
 * Field 2 = descriptive protocol name
 * Field 3 = protocol abbreviation
 *
 * Header Fields
 * -------------
 * (format 1)
 * Field 1 = 'F'
 * Field 2 = descriptive field name
 * Field 3 = field abbreviation
 * Field 4 = type ( textual representation of the the ftenum type )
 * Field 5 = parent protocol abbreviation
 *
 * (format 2 adds these fields:)
 * Field 6 = base for display (for integer types)
 * Field 7 = blurb describing field
 */
void
03772 proto_registrar_dump_fields(int format)
{
      header_field_info *hfinfo, *parent_hfinfo;
      int               i, len;
      const char        *enum_name;
      const char        *base_name;

      len = gpa_hfinfo.len;
      for (i = 0; i < len ; i++) {
            PROTO_REGISTRAR_GET_NTH(i, hfinfo);

            /*
             * Skip fields with zero-length names or abbreviations;
             * the pseudo-field for "proto_tree_add_text()" is such
             * a field, and we don't want it in the list of filterable
             * fields.
             *
             *
             * XXX - perhaps the name and abbrev field should be null
             * pointers rather than null strings for that pseudo-field,
             * but we'd have to add checks for null pointers in some
             * places if we did that.
             *
             * Or perhaps protocol tree items added with
             * "proto_tree_add_text()" should have -1 as the field index,
             * with no pseudo-field being used, but that might also
             * require special checks for -1 to be added.
             */
            /* XXX - we could just skip the special text
             * pseudo-field by testing: if (hfinfo->id == hf_text_only)
             * */
            if (hfinfo->name[0] == 0 || hfinfo->abbrev[0] == 0)
                  continue;

            /* format for protocols */
            if (proto_registrar_is_protocol(i)) {
                  printf("P\t%s\t%s\n", hfinfo->name, hfinfo->abbrev);
            }
            /* format for header fields */
            else {
                  /*
                   * If this field isn't at the head of the list of
                   * fields with this name, skip this field - all
                   * fields with the same name are really just versions
                   * of the same field stored in different bits, and
                   * should have the same type/radix/value list, and
                   * just differ in their bit masks.  (If a field isn't
                   * a bitfield, but can be, say, 1 or 2 bytes long,
                   * it can just be made FT_UINT16, meaning the
                   * *maximum* length is 2 bytes, and be used
                   * for all lengths.)
                   */
                  if (hfinfo->same_name_prev != NULL)
                        continue;

                  PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo);

                  enum_name = ftype_name(hfinfo->type);
                  base_name = "";

                  if (format > 1) {
                        if (hfinfo->type == FT_UINT8 ||
                              hfinfo->type == FT_UINT16 ||
                              hfinfo->type == FT_UINT24 ||
                              hfinfo->type == FT_UINT32 ||
                              hfinfo->type == FT_UINT64 ||
                              hfinfo->type == FT_INT8 ||
                              hfinfo->type == FT_INT16 ||
                              hfinfo->type == FT_INT24 ||
                              hfinfo->type == FT_INT32 ||
                              hfinfo->type == FT_INT64) {

                              
                              switch(hfinfo->display) {
                                    case BASE_NONE:
                                          base_name = "BASE_NONE";
                                          break;
                                    case BASE_DEC:
                                          base_name = "BASE_DEC";
                                          break;
                                    case BASE_HEX:
                                          base_name = "BASE_HEX";
                                          break;
                                    case BASE_OCT:
                                          base_name = "BASE_OCT";
                                          break;
                              }
                        }
                  }

                  if (format == 1) {
                        printf("F\t%s\t%s\t%s\t%s\t%s\n", hfinfo->name, hfinfo->abbrev,
                              enum_name,parent_hfinfo->abbrev, hfinfo->blurb);
                  }
                  else if (format == 2) {
                        printf("F\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
                              hfinfo->name, hfinfo->abbrev,
                              enum_name,parent_hfinfo->abbrev, hfinfo->blurb,
                              base_name, hfinfo->blurb);
                  }
            }
      }
}

static char*
hfinfo_numeric_format(header_field_info *hfinfo)
{
      char *format = NULL;

      /* Pick the proper format string */
      if (hfinfo->type == FT_FRAMENUM) {
            /*
             * Frame numbers are always displayed in decimal.
             */
            format = "%s == %u";
      } else {
            /* Pick the proper format string */
            switch(hfinfo->display) {
                  case BASE_DEC:
                  case BASE_OCT: /* I'm lazy */
                        switch(hfinfo->type) {
                              case FT_UINT8:
                              case FT_UINT16:
                              case FT_UINT24:
                              case FT_UINT32:
                                    format = "%s == %u";
                                    break;
                              case FT_UINT64:
                                    format = "%s == %" PRIu64;
                                    break;
                              case FT_INT8:
                              case FT_INT16:
                              case FT_INT24:
                              case FT_INT32:
                                    format = "%s == %d";
                                    break;
                              case FT_INT64:
                                    format = "%s == %" PRId64;
                                    break;
                              default:
                                    g_assert_not_reached();
                                    ;
                        }
                        break;
                  case BASE_HEX:
                        switch(hfinfo->type) {
                              case FT_UINT8:
                                    format = "%s == 0x%02x";
                                    break;
                              case FT_UINT16:
                                    format = "%s == 0x%04x";
                                    break;
                              case FT_UINT24:
                                    format = "%s == 0x%06x";
                                    break;
                              case FT_UINT32:
                                    format = "%s == 0x%08x";
                                    break;
                              case FT_UINT64:
                                    format = "%s == 0x%016" PRIx64;
                                    break;
                              default:
                                    g_assert_not_reached();
                                    ;
                        }
                        break;
                  default:
                        g_assert_not_reached();
                        ;
            }
      }
      return format;
}

/*
 * Returns TRUE if we can do a "match selected" on the field, FALSE
 * otherwise.
 */
gboolean
03951 proto_can_match_selected(field_info *finfo, epan_dissect_t *edt)
{
      header_field_info *hfinfo;
      gint              length;

      hfinfo = finfo->hfinfo;
      g_assert(hfinfo);

      switch(hfinfo->type) {

            case FT_BOOLEAN:
            case FT_UINT8:
            case FT_UINT16:
            case FT_UINT24:
            case FT_UINT32:
            case FT_INT8:
            case FT_INT16:
            case FT_INT24:
            case FT_INT32:
            case FT_FRAMENUM:
            case FT_UINT64:
            case FT_INT64:
            case FT_IPv4:
            case FT_IPXNET:
            case FT_IPv6:
            case FT_FLOAT:
            case FT_DOUBLE:
            case FT_ABSOLUTE_TIME:
            case FT_RELATIVE_TIME:
            case FT_STRING:
            case FT_STRINGZ:
            case FT_UINT_STRING:
            case FT_ETHER:
            case FT_BYTES:
            case FT_UINT_BYTES:
            case FT_PROTOCOL:
                  /*
                   * These all have values, so we can match.
                   */
                  return TRUE;

            default:
                  /*
                   * This doesn't have a value, so we'd match
                   * on the raw bytes at this address.
                   *
                   * Should we be allowed to access to the raw bytes?
                   * If "edt" is NULL, the answer is "no".
                   */
                  if (edt == NULL)
                        return FALSE;

                  /*
                   * Is this field part of the raw frame tvbuff?
                   * If not, we can't use "frame[N:M]" to match
                   * it.
                   *
                   * XXX - should this be frame-relative, or
                   * protocol-relative?
                   *
                   * XXX - does this fallback for non-registered
                   * fields even make sense?
                   */
                  if (finfo->ds_tvb != edt->tvb)
                        return FALSE;

                  /*
                   * If the length is 0, there's nothing to match, so
                   * we can't match.  (Also check for negative values,
                   * just in case, as we'll cast it to an unsigned
                   * value later.)
                   */
                  length = finfo->length;
                  if (length <= 0)
                        return FALSE;

                  /*
                   * Don't go past the end of that tvbuff.
                   */
                  if ((guint)length > tvb_length(finfo->ds_tvb))
                        length = tvb_length(finfo->ds_tvb);
                  if (length <= 0)
                        return FALSE;
                  return TRUE;
      }
}

char*
04039 proto_construct_dfilter_string(field_info *finfo, epan_dissect_t *edt)
{
      header_field_info *hfinfo;
      int               abbrev_len;
      char              *buf, *stringified, *format, *ptr;
      int               dfilter_len, i;
      gint              start, length, length_remaining;
      guint8                  c;

      hfinfo = finfo->hfinfo;
      g_assert(hfinfo);
      abbrev_len = strlen(hfinfo->abbrev);

      /*
       * XXX - we should add "val_to_string_repr" and "string_repr_len"
       * functions for more types, and use them whenever possible.
       *
       * The FT_UINT and FT_INT types are the only tricky ones, as
       * we choose the base in the string expression based on the
       * display base of the field.
       *
       * Note that the base does matter, as this is also used for
       * the protocolinfo tap.
       *
       * It might be nice to use that in "proto_item_fill_label()"
       * as well, although, there, you'd have to deal with the base
       * *and* with resolved values for addresses.
       *
       * Perhaps we need two different val_to_string routines, one
       * to generate items for display filters and one to generate
       * strings for display, and pass to both of them the
       * "display" and "strings" values in the header_field_info
       * structure for the field, so they can get the base and,
       * if the field is Boolean or an enumerated integer type,
       * the tables used to generate human-readable values.
       */
      switch(hfinfo->type) {

            case FT_UINT8:
            case FT_UINT16:
            case FT_UINT24:
            case FT_UINT32:
            case FT_INT8:
            case FT_INT16:
            case FT_INT24:
            case FT_INT32:
            case FT_FRAMENUM:
                  /*
                   * 4 bytes for " == ".
                   * 11 bytes for:
                   *
                   *    a sign + up to 10 digits of 32-bit integer,
                   *    in decimal;
                   *
                   *    "0x" + 8 digits of 32-bit integer, in hex;
                   *
                   *    11 digits of 32-bit integer, in octal.
                   *    (No, we don't do octal, but this way,
                   *    we know that if we do, this will still
                   *    work.)
                   *
                   * 1 byte for the trailing '\0'.
                   */
                  dfilter_len = abbrev_len + 4 + 11 + 1;
                  buf = g_malloc0(dfilter_len);
                  format = hfinfo_numeric_format(hfinfo);
                  snprintf(buf, dfilter_len, format, hfinfo->abbrev, fvalue_get_integer(&finfo->value));
                  break;

            case FT_INT64:
            case FT_UINT64:
                  /*
                   * 4 bytes for " == ".
                   * 22 bytes for:
                   *
                   *    a sign + up to 20 digits of 32-bit integer,
                   *    in decimal;
                   *
                   *    "0x" + 16 digits of 32-bit integer, in hex;
                   *
                   *    22 digits of 32-bit integer, in octal.
                   *    (No, we don't do octal, but this way,
                   *    we know that if we do, this will still
                   *    work.)
                   *
                   * 1 byte for the trailing '\0'.
                   */
                  dfilter_len = abbrev_len + 4 + 22 + 1;
                  buf = g_malloc0(dfilter_len);
                  format = hfinfo_numeric_format(hfinfo);
                  snprintf(buf, dfilter_len, format, hfinfo->abbrev, fvalue_get_integer64(&finfo->value));
                  break;

            case FT_IPXNET:
                  /*
                   * 4 bytes for " == ".
                   * 2 bytes for "0x".
                   * 8 bytes for 8 digits of 32-bit hex number.
                   * 1 byte for the trailing '\0'.
                   */
                  dfilter_len = abbrev_len + 4 + 2 + 8 + 1;
                  buf = g_malloc0(dfilter_len);
                  snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev,
                              fvalue_get_integer(&finfo->value));
                  break;

            case FT_IPv6:
                  /*
                   * 4 bytes for " == ".
                   * N bytes for the string for the address.
                   * 1 byte for the trailing '\0'.
                   */
                  stringified = ip6_to_str((struct e_in6_addr*) fvalue_get(&finfo->value));
                  dfilter_len = abbrev_len + 4 + strlen(stringified) + 1;
                  buf = g_malloc0(dfilter_len);
                  snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
                              stringified);
                  break;

            /* These use the fvalue's "to_string_repr" method. */
            case FT_BOOLEAN:
            case FT_STRING:
            case FT_ETHER:
            case FT_BYTES:
            case FT_UINT_BYTES:
            case FT_FLOAT:
            case FT_DOUBLE:
            case FT_ABSOLUTE_TIME:
            case FT_RELATIVE_TIME:
            case FT_IPv4:
                  /* Figure out the string length needed.
                   *    The ft_repr length.
                   *    4 bytes for " == ".
                   *    1 byte for trailing NUL.
                   */
                  dfilter_len = fvalue_string_repr_len(&finfo->value,
                              FTREPR_DFILTER);
                  dfilter_len += abbrev_len + 4 + 1;
                  buf = g_malloc0(dfilter_len);

                  /* Create the string */
                  snprintf(buf, dfilter_len, "%s == ", hfinfo->abbrev);
                  fvalue_to_string_repr(&finfo->value,
                              FTREPR_DFILTER,
                              &buf[abbrev_len + 4]);
                  break;

            case FT_PROTOCOL:
                  buf = g_strdup(finfo->hfinfo->abbrev);
                  break;

            default:
                  /*
                   * This doesn't have a value, so we'd match
                   * on the raw bytes at this address.
                   *
                   * Should we be allowed to access to the raw bytes?
                   * If "edt" is NULL, the answer is "no".
                   */
                  if (edt == NULL)
                        return FALSE;

                  /*
                   * Is this field part of the raw frame tvbuff?
                   * If not, we can't use "frame[N:M]" to match
                   * it.
                   *
                   * XXX - should this be frame-relative, or
                   * protocol-relative?
                   *
                   * XXX - does this fallback for non-registered
                   * fields even make sense?
                   */
                  if (finfo->ds_tvb != edt->tvb)
                        return NULL;      /* you lose */

                  /*
                   * If the length is 0, there's nothing to match, so
                   * we can't match.  (Also check for negative values,
                   * just in case, as we'll cast it to an unsigned
                   * value later.)
                   */
                  length = finfo->length;
                  if (length <= 0)
                        return NULL;

                  /*
                   * Don't go past the end of that tvbuff.
                   */
                  length_remaining = tvb_length_remaining(finfo->ds_tvb, finfo->start);
                  if (length > length_remaining)
                        length = length_remaining;
                  if (length <= 0)
                        return NULL;
                  
                  start = finfo->start;
                  buf = g_malloc0(32 + length * 3);
                  ptr = buf;

                  sprintf(ptr, "frame[%d:%d] == ", finfo->start, length);
                  ptr = buf+strlen(buf);

                  for (i=0;i<length; i++) {
                        c = tvb_get_guint8(finfo->ds_tvb, start);
                        start++;
                        if (i == 0 ) {
                              sprintf(ptr, "%02x", c);
                        }
                        else {
                              sprintf(ptr, ":%02x", c);
                        }
                        ptr = buf+strlen(buf);
                  }
                  break;
      }

      return buf;
}

Generated by  Doxygen 1.6.0   Back to index