Logo Search packages:      
Sourcecode: wireshark version File versions  Download package

proto.c

/* proto.c
 * Routines for protocol tree
 *
 * $Id: proto.c 33659 2010-07-28 23:19:34Z gerald $
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <glib.h>
#include <float.h>

#include "packet.h"
#include "ptvcursor.h"
#include "strutil.h"
#include "addr_resolv.h"
#include "oids.h"
#include "plugins.h"
#include "proto.h"
#include "epan_dissect.h"
#include "slab.h"
#include "tvbuff.h"
#include "emem.h"
#include "charsets.h"
#include "asm_utils.h"
#include "column-utils.h"
#include "to_str.h"

#include "wspython/wspy_register.h"

#define SUBTREE_ONCE_ALLOCATION_NUMBER 8
#define SUBTREE_MAX_LEVELS 256
/* Throw an exception if we exceed this many tree items. */
/* XXX - This should probably be a preference */
#define MAX_TREE_ITEMS (1 * 1000 * 1000)


typedef struct __subtree_lvl {
  gint cursor_offset;
  proto_item * it;
  proto_tree * tree;
}subtree_lvl;

struct ptvcursor {
      subtree_lvl *pushed_tree;
      guint8            pushed_tree_index;
      guint8            pushed_tree_max;
      proto_tree  *tree;
      tvbuff_t    *tvb;
      gint        offset;
};

/* Candidates for assembler */
static int
wrs_count_bitshift(const guint32 bitmask)
{
      int bitshift = 0;

      while ((bitmask & (1 << bitshift)) == 0)
            bitshift++;
      return bitshift;
}

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

/** See inlined comments.
 @param tree the tree to append this item to
 @param hfindex field index
 @param hfinfo header_field
 @return the header field matching 'hfinfo' */
#define TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo) \
      /* If this item is not referenced we dont have to do much work    \
         at all but we should still return a node so that         \
         field items below this node ( think proto_item_add_subtree() )\
         will still have somewhere to attach to             \
         or else filtering will not work (they would be ignored since tree\
         would be NULL).                                    \
         DONT try to fake a node where PTREE_FINFO(tree) is NULL  \
         since dissectors that want to do proto_item_set_len() or \
         other operations that dereference this would crash.            \
         We fake FT_PROTOCOL unless some clients have requested us      \
         not to do so. \
      */                                              \
      if (!tree)                                      \
            return(NULL);                                   \
      PTREE_DATA(tree)->count++;                            \
      if (PTREE_DATA(tree)->count > MAX_TREE_ITEMS) {             \
            /* Let the exception handler add items to the tree */ \
            PTREE_DATA(tree)->count = 0;                    \
            THROW_MESSAGE(DissectorError,                   \
                  ep_strdup_printf("More than %d items in the tree -- possible infinite loop", MAX_TREE_ITEMS)); \
      }                                               \
      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);             \
      if(!(PTREE_DATA(tree)->visible)){                     \
            if(PTREE_FINFO(tree)){                          \
                  if((hfinfo->ref_type != HF_REF_TYPE_DIRECT)     \
                  && (hfinfo->type!=FT_PROTOCOL ||          \
                        PTREE_DATA(tree)->fake_protocols)){ \
                        /* just return tree back to the caller */\
                        return tree;                        \
                  }                                   \
            }                                         \
      }

/** See inlined comments.
 @param tree the tree to append this item to
 @param pi the created protocol item we're about to return */
#if 1
#define TRY_TO_FAKE_THIS_REPR(tree, pi) \
      DISSECTOR_ASSERT(tree); \
      if(!(PTREE_DATA(tree)->visible)) { \
            /* If the tree (GUI) isn't visible it's pointless for us to generate the protocol \
             * items string representation */ \
            return pi; \
      }
#else
#define TRY_TO_FAKE_THIS_REPR(tree, pi)
#endif

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_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 const char* hfinfo_uint_vals_format(const header_field_info *hfinfo);
static const char* hfinfo_uint_format(const header_field_info *hfinfo);
static const char* hfinfo_uint_value_format(const header_field_info *hfinfo);
static const char* hfinfo_uint64_format(const header_field_info *hfinfo);
static const char* hfinfo_int_vals_format(const header_field_info *hfinfo);
static const char* hfinfo_int_format(const header_field_info *hfinfo);
static const char* hfinfo_int_value_format(const header_field_info *hfinfo);
static const char* hfinfo_int64_format(const header_field_info *hfinfo);
static const char* hfinfo_numeric_value_format(const header_field_info *hfinfo);

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

static header_field_info *
get_hfi_and_length(int hfindex, tvbuff_t *tvb, const gint start, gint *length,
               gint *item_length);

static field_info *
new_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb,
             const gint start, const gint item_length);

static field_info *
alloc_field_info(proto_tree *tree, int hfindex, tvbuff_t *tvb,
             const 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_value(proto_item *pi, const char *format, va_list ap);
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);
static void
proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length);
static void
proto_tree_set_ebcdic_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, gint length);
static void
proto_tree_set_guid(field_info *fi, const e_guid_t *value_ptr);
static void
proto_tree_set_guid_tvb(field_info *fi, tvbuff_t *tvb, gint start, const guint encoding);
static void
proto_tree_set_oid(field_info *fi, const guint8* value_ptr, gint length);
static void
proto_tree_set_oid_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length);
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, guint length, const guint encoding);
static gboolean
proto_item_add_bitmask_tree(proto_item *item, tvbuff_t *tvb, const int offset,
                      const int len, const gint ett, const gint **fields,
                      const gboolean little_endian, const int flags,
                      gboolean first);

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

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

/* Structure for information about a protocol */
struct _protocol {
      const char *name;       /* long description */
      const char *short_name;       /* short description */
      const 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 */
      gboolean is_private;          /* TRUE is protocol is private */
};

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

#define INITIAL_NUM_PROTOCOL_HFINFO       1500

/* Contains information about protocols and header fields. Used when
 * dissectors register their data */
#if GLIB_CHECK_VERSION(2,10,0)
#else
static GMemChunk *gmc_hfinfo = NULL;
#endif

/* 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)

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

/* 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;
static header_field_info *same_name_hfinfo;

static void save_same_name_hfinfo(gpointer data)
{
  same_name_hfinfo = (header_field_info*)data;
}

/* 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_ascii_strcasecmp(p1->short_name, p2->short_name);
}


/* initialize data structures and register protocols and fields */
void
proto_init(void (register_all_protocols_func)(register_cb cb, gpointer client_data),
         void (register_all_handoffs_func)(register_cb cb, gpointer client_data),
         register_cb cb,
         gpointer client_data)
{
      static hf_register_info hf[] = {
            { &hf_text_only,
            { "Text item",    "text", FT_NONE, BASE_NONE, NULL, 0x0,
              NULL, HFILL }},
      };

      proto_cleanup();

      proto_names = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL);
      proto_short_names = g_hash_table_new(wrs_str_hash, g_str_equal);
      proto_filter_names = g_hash_table_new(wrs_str_hash, g_str_equal);

#if GLIB_CHECK_VERSION(2,10,0)
#else
      gmc_hfinfo = g_mem_chunk_new("gmc_hfinfo",
            sizeof(header_field_info),
                  INITIAL_NUM_PROTOCOL_HFINFO * sizeof(header_field_info),
                  G_ALLOC_ONLY);
#endif

      gpa_hfinfo.len=0;
      gpa_hfinfo.allocated_len=0;
      gpa_hfinfo.hfi=NULL;
      gpa_name_tree = g_tree_new_full(wrs_strcmp_with_data, NULL, NULL, save_same_name_hfinfo);

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

      /* Register one special-case FT_TEXT_ONLY field for use when
         converting wireshark 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_func(cb, client_data);
#ifdef HAVE_PYTHON
            /* Now scan for python protocols */
            register_all_py_protocols_func(cb, client_data);
#endif

#ifdef HAVE_PLUGINS
      /* Now scan for plugins and load all the ones we find, calling
         their register routines to do the stuff described above. */
      if(cb)
        (*cb)(RA_PLUGIN_REGISTER, NULL, client_data);
      init_plugins();
      register_all_plugin_registrations();
#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_handoffs_func(cb, client_data);

#ifdef HAVE_PYTHON
            /* Now do the same with python dissectors */
            register_all_py_handoffs_func(cb, client_data);
#endif

#ifdef HAVE_PLUGINS
      /* Now do the same with plugins. */
      if(cb)
        (*cb)(RA_PLUGIN_HANDOFF, NULL, client_data);
00418       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_new0(gboolean, num_tree_types);
}

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

      while (protocols) {
            protocol_t *protocol = protocols->data;
            header_field_info *hfinfo;
            PROTO_REGISTRAR_GET_NTH(protocol->proto_id, hfinfo);
            DISSECTOR_ASSERT(protocol->proto_id == hfinfo->id);

#if GLIB_CHECK_VERSION(2,10,0)
            g_slice_free(header_field_info, hfinfo);
#else
            g_mem_chunk_free(gmc_hfinfo, hfinfo);
#endif

            g_list_free(protocol->fields);
            protocols = g_list_remove(protocols, protocol);
            g_free(protocol);
      }

      if (proto_names) {
            g_hash_table_destroy(proto_names);
            proto_names = NULL;
      }

      if (proto_short_names) {
            g_hash_table_destroy(proto_short_names);
            proto_short_names = NULL;
      }

      if (proto_filter_names) {
            g_hash_table_destroy(proto_filter_names);
            proto_filter_names = NULL;
      }

#if GLIB_CHECK_VERSION(2,10,0)
#else
      if (gmc_hfinfo) {
            g_mem_chunk_destroy(gmc_hfinfo);
            gmc_hfinfo = NULL;
      }
#endif

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

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;
}

gboolean
proto_tree_traverse_post_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;
      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_post_order((proto_tree *)current, func,
                  data))
                  return TRUE;
      }
      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;
00548       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
proto_tree_free(proto_tree *tree)
{
      proto_tree_traverse_post_order(tree, proto_tree_free_node, NULL);
}

static void
free_GPtrArray_value(gpointer key, gpointer value, gpointer user_data _U_)
{
      GPtrArray   *ptrs = value;
      gint hfid = (gint)(long)key;
      header_field_info *hfinfo;

      PROTO_REGISTRAR_GET_NTH(hfid, hfinfo);
      if(hfinfo->ref_type != HF_REF_TYPE_NONE) {
            /* when a field is referenced by a filter this also
               affects the refcount for the parent protocol so we need
               to adjust the refcount for the parent as well
            */
            if( hfinfo->parent != -1 ) {
                  header_field_info *parent_hfinfo;
                  PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo);
                  parent_hfinfo->ref_type = HF_REF_TYPE_NONE;
            }
            hfinfo->ref_type = HF_REF_TYPE_NONE;
      }

      g_ptr_array_free(ptrs, TRUE);
}

static void
free_node_tree_data(tree_data_t *tree_data)
{
      if (tree_data->interesting_hfids) {
            /* 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 = PNODE_FINFO(node);
#if 0
      proto_node *parent = node->parent;
#endif

      if (finfo == NULL) {
            /* This is the root node. Destroy the per-tree data.
             * There is no field_info to destroy. */
            if (PTREE_DATA(node)) 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);
      }

#if 0
      /* NOTE: This code is required when this function is used to free individual
       * nodes only. Current use is for the destruction of complete trees, so the
       * inconsistancies have no ill effect.
       */
      /* Remove node from parent */
      if (parent) {
            proto_item *prev_item = NULL;
            if (parent->first_child == node) {
                  parent->first_child = node->next;
            } else {
                  /* find previous and change its next */
                  for (prev_item = parent->first_child; prev_item; prev_item = prev_item->next) {
                        if (prev_item->next == node) {
                              break;
                        }
                  }
                  DISSECTOR_ASSERT(prev_item);
                  prev_item->next = node->next;
            }
            /* fix last_child if required */
            if (parent->last_child == node) {
                  parent->last_child = prev_item;
            }
      }
      DISSECTOR_ASSERT(node->first_child == NULL && node->last_child == NULL);
00657 #endif
      /* 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 g_vsnprintf and copy strings around.
00667  */
gboolean
proto_tree_set_visible(proto_tree *tree, gboolean visible)
{
      gboolean old_visible = PTREE_DATA(tree)->visible;

      PTREE_DATA(tree)->visible = visible;

      return old_visible;
}

void
proto_tree_set_fake_protocols(proto_tree *tree, gboolean fake_protocols)
00680 {
      PTREE_DATA(tree)->fake_protocols = fake_protocols;
}

/* Assume dissector set only its protocol fields.
   This function is called by dissectors and allows the speeding up of filtering
   in wireshark; if this function returns FALSE it is safe to reset tree to NULL
   and thus skip calling most of the expensive proto_tree_add_...()
   functions.
   If the tree is visible we implicitly assume the field is referenced.
*/
gboolean
proto_field_is_referenced(proto_tree *tree, int proto_id)
{
      register header_field_info *hfinfo;


      if (!tree)
            return FALSE;

      if (PTREE_DATA(tree)->visible)
            return TRUE;

      PROTO_REGISTRAR_GET_NTH(proto_id, hfinfo);
00704       if (hfinfo->ref_type != HF_REF_TYPE_NONE)
            return TRUE;

      if (hfinfo->type == FT_PROTOCOL && !PTREE_DATA(tree)->fake_protocols)
            return TRUE;

      return FALSE;
}


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

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);
      return hfinfo;
}


/*    Prefix initialization
 *      this allows for a dissector to register a display filter name prefix
 *      so that it can delay the initialization of the hf array as long as
 *      possible.
 */

/* compute a hash for the part before the dot of a display filter */
static guint
prefix_hash (gconstpointer key) {
      /* end the string at the dot and compute its hash */
      gchar* copy = ep_strdup(key);
      gchar* c = copy;

      for (;*c ;c++) {
            if (*c == '.') {
                  *c = 0;
                  break;
            }
      }

      return g_str_hash(copy);
}

/* are both strings equal up to the end or the dot? */
static gboolean
prefix_equal (gconstpointer ap,gconstpointer bp) {
      const gchar* a = ap;
      const gchar* b = bp;

      do {
            gchar ac = *a++;
            gchar bc = *b++;

            if ((ac == '.' || ac == '\0') && (bc == '.' || bc == '\0')) return TRUE;

            if ( (ac == '.' || ac == '\0') && ! (bc == '.' || bc == '\0') ) return FALSE;
            if ( (bc == '.' || bc == '\0') && ! (ac == '.' || ac == '\0') ) return FALSE;

            if (ac != bc) return FALSE;
00764       } while(1);

      return FALSE;
}


/* indexed by prefix, contains initializers */
static GHashTable* prefixes = NULL;


/* Register a new prefix for "delayed" initialization of field arrays */
void
proto_register_prefix(const char *prefix, prefix_initializer_t pi ) {
      if (! prefixes ) {
            prefixes = g_hash_table_new(prefix_hash,prefix_equal);
      }

00781       g_hash_table_insert(prefixes,(gpointer)prefix,pi);
}

/* helper to call all prefix initializers */
static gboolean
initialize_prefix(gpointer k, gpointer v, gpointer u _U_) {
      ((prefix_initializer_t)v)(k);
      return TRUE;
}

00791 /** Initialize every remaining uninitialized prefix. */
void
proto_initialize_all_prefixes(void) {
      g_hash_table_foreach_remove(prefixes, initialize_prefix, NULL);
}

/* Finds a record in the hf_info_records array by name.
 * If it fails to find it in the already registered fields,
 * it tries to find and call an initializer in the prefixes
 * table and if so it looks again.
 */
header_field_info *
proto_registrar_get_byname(const char *field_name)
{
      header_field_info* hfinfo;
      prefix_initializer_t pi;

      if (!field_name)
            return NULL;

      hfinfo = g_tree_lookup(gpa_name_tree, field_name);

      if (hfinfo) return hfinfo;

      if    (!prefixes) return NULL;

      if(( pi = g_hash_table_lookup(prefixes,field_name) )) {
            pi(field_name);
            g_hash_table_remove(prefixes,field_name);
      } else {
            return NULL;
      }

      return g_tree_lookup(gpa_name_tree, field_name);
}


void
ptvcursor_new_subtree_levels(ptvcursor_t * ptvc)
{
      subtree_lvl * pushed_tree;

      DISSECTOR_ASSERT(ptvc->pushed_tree_max <= SUBTREE_MAX_LEVELS-SUBTREE_ONCE_ALLOCATION_NUMBER);
      ptvc->pushed_tree_max += SUBTREE_ONCE_ALLOCATION_NUMBER;

      pushed_tree = ep_alloc(sizeof(subtree_lvl) * ptvc->pushed_tree_max);
      DISSECTOR_ASSERT(pushed_tree != NULL);
      if (ptvc->pushed_tree)
            memcpy(pushed_tree, ptvc->pushed_tree, ptvc->pushed_tree_max - SUBTREE_ONCE_ALLOCATION_NUMBER);
      ptvc->pushed_tree = pushed_tree;
}

void
ptvcursor_free_subtree_levels(ptvcursor_t * ptvc)
{
      ptvc->pushed_tree = NULL;
      ptvc->pushed_tree_max = 0;
      DISSECTOR_ASSERT(ptvc->pushed_tree_index ==0);
      ptvc->pushed_tree_index = 0;
}

/* Allocates an initializes a ptvcursor_t with 3 variables:
 *    proto_tree, tvbuff, and offset. */
ptvcursor_t *
ptvcursor_new(proto_tree *tree, tvbuff_t *tvb, gint offset)
{
      ptvcursor_t *ptvc;

      ptvc = ep_alloc(sizeof(ptvcursor_t));
      ptvc->tree  = tree;
      ptvc->tvb   = tvb;
      ptvc->offset      = offset;
      ptvc->pushed_tree= NULL;
      ptvc->pushed_tree_max= 0;
      ptvc->pushed_tree_index= 0;
      return ptvc;
}


/* Frees memory for ptvcursor_t, but nothing deeper than that. */
void
ptvcursor_free(ptvcursor_t *ptvc)
{
      ptvcursor_free_subtree_levels(ptvc);
      /*g_free(ptvc);*/
}

/* Returns tvbuff. */
tvbuff_t *
ptvcursor_tvbuff(ptvcursor_t* ptvc)
{
      return ptvc->tvb;
}

/* Returns current offset. */
gint
ptvcursor_current_offset(ptvcursor_t* ptvc)
{
      return ptvc->offset;
}

proto_tree *
ptvcursor_tree(ptvcursor_t* ptvc)
{
      if (!ptvc)
            return NULL;

      return ptvc->tree;
}

void
ptvcursor_set_tree(ptvcursor_t* ptvc, proto_tree *tree)
{
      ptvc->tree = tree;
}

/* creates a subtree, sets it as the working tree and pushes the old working tree */
proto_tree *
ptvcursor_push_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree)
{
      subtree_lvl * subtree;
      if (ptvc->pushed_tree_index >= ptvc->pushed_tree_max)
            ptvcursor_new_subtree_levels(ptvc);

      subtree = ptvc->pushed_tree+ptvc->pushed_tree_index;
      subtree->tree = ptvc->tree;
      subtree->it= NULL;
      ptvc->pushed_tree_index++;
      return ptvcursor_set_subtree(ptvc, it, ett_subtree);
}

/* pops a subtree */
void
ptvcursor_pop_subtree(ptvcursor_t *ptvc)
{
      subtree_lvl * subtree;
      if (ptvc->pushed_tree_index <= 0)
            return;

      ptvc->pushed_tree_index--;
      subtree = ptvc->pushed_tree+ptvc->pushed_tree_index;
      if (subtree->it != NULL)
            proto_item_set_len(subtree->it, ptvcursor_current_offset(ptvc) - subtree->cursor_offset);

      ptvc->tree = subtree->tree;
}

/* saves the current tvb offset and the item in the current subtree level */
static void
ptvcursor_subtree_set_item(ptvcursor_t * ptvc, proto_item * it)
{
      subtree_lvl * subtree;

      DISSECTOR_ASSERT(ptvc->pushed_tree_index > 0);

      subtree = ptvc->pushed_tree+ptvc->pushed_tree_index-1;
      subtree->it = it;
      subtree->cursor_offset = ptvcursor_current_offset(ptvc);
}

/* Creates a subtree and adds it to the cursor as the working tree but does not
 * save the old working tree */
proto_tree *
ptvcursor_set_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree)
{
      ptvc->tree = proto_item_add_subtree(it, ett_subtree);
      return ptvc->tree;
}

proto_tree *
ptvcursor_add_subtree_item(ptvcursor_t * ptvc, proto_item * it, gint ett_subtree, gint length)
{
      ptvcursor_push_subtree(ptvc, it, ett_subtree);
      if (length == SUBTREE_UNDEFINED_LENGTH)
            ptvcursor_subtree_set_item(ptvc, it);
      return ptvcursor_tree(ptvc);
}

/* Add an item to the tree and create a subtree
 * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH.
 * In this case, when the subtree will be closed, the parent item length will
 * be equal to the advancement of the cursor since the creation of the subtree.
 */
proto_tree *
ptvcursor_add_with_subtree(ptvcursor_t * ptvc, int hfindex, gint length,
                     const guint encoding, gint ett_subtree)
{
      proto_item * it;

      it = ptvcursor_add_no_advance(ptvc, hfindex, length, encoding);
      return ptvcursor_add_subtree_item(ptvc, it, ett_subtree, length);
}

static proto_item *
proto_tree_add_text_node(proto_tree *tree, tvbuff_t *tvb, gint start, gint length);

/* Add a text node to the tree and create a subtree
 * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH.
 * In this case, when the subtree will be closed, the item length will be equal
 * to the advancement of the cursor since the creation of the subtree.
 */
proto_tree *
ptvcursor_add_text_with_subtree(ptvcursor_t * ptvc, gint length,
                        gint ett_subtree, const char *format, ...)
{
      proto_item *      it;
      va_list     ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(ptvcursor_tree(ptvc), hf_text_only, hfinfo);

      it = proto_tree_add_text_node(ptvcursor_tree(ptvc), ptvcursor_tvbuff(ptvc),
                              ptvcursor_current_offset(ptvc), length);

      if (it == NULL)
            return(NULL);

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

      return ptvcursor_add_subtree_item(ptvc, it, ett_subtree, length);
}

/* 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 *
proto_tree_add_text(proto_tree *tree, tvbuff_t *tvb, gint start, gint length,
                const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hf_text_only, hfinfo);

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

      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 *
proto_tree_add_text_valist(proto_tree *tree, tvbuff_t *tvb, gint start,
                     gint length, const char *format, va_list ap)
{
      proto_item  *pi;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hf_text_only, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

      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 *
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) {
            va_start(ap, format);
            proto_tree_set_representation(pi, format, ap);
            va_end(ap);
      }
      va_start(ap, format);
      vprintf(format, ap);
      va_end(ap);
      printf("\n");

      return pi;
}

/*
 * NOTE: to support code written when proto_tree_add_item() took a
 * gboolean as its last argument, with FALSE meaning "big-endian"
 * and TRUE meaning "little-endian", we treat any non-zero value of
 * "encoding" as meaning "little-endian".
 */
static guint32
get_uint_value(tvbuff_t *tvb, gint offset, gint length, const guint encoding)
{
      guint32 value;

      switch (length) {

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

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

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

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

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

/*
 * NOTE: to support code written when proto_tree_add_item() took a
 * gboolean as its last argument, with FALSE meaning "big-endian"
 * and TRUE meaning "little-endian", we treat any non-zero value of
 * "encoding" as meaning "little-endian".
 */
static gint32
get_int_value(tvbuff_t *tvb, gint offset, gint length, const guint encoding)
{
      gint32 value;

      switch (length) {

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

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

      case 3:
            value = encoding ? 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 = encoding ? tvb_get_letohl(tvb, offset)
                         : tvb_get_ntohl(tvb, offset);
            break;

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

static GPtrArray *
proto_lookup_or_create_interesting_hfids(proto_tree *tree,
                               header_field_info *hfinfo)
{
      GPtrArray *ptrs = NULL;

      DISSECTOR_ASSERT(tree);
      DISSECTOR_ASSERT(hfinfo);

      if (hfinfo->ref_type == HF_REF_TYPE_DIRECT) {
            if (PTREE_DATA(tree)->interesting_hfids == NULL) {
                  /* Initialize the hash because we now know that it is needed */
                  PTREE_DATA(tree)->interesting_hfids =
                        g_hash_table_new(g_direct_hash, NULL /* g_direct_equal */);
            }

            ptrs = g_hash_table_lookup(PTREE_DATA(tree)->interesting_hfids,
                                 GINT_TO_POINTER(hfinfo->id));
            if (!ptrs) {
                  /* First element triggers the creation of pointer array */
                  ptrs = g_ptr_array_new();
                  g_hash_table_insert(PTREE_DATA(tree)->interesting_hfids,
                                  GINT_TO_POINTER(hfinfo->id), ptrs);
            }
      }

      return ptrs;
}

/* 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. */
static proto_item *
proto_tree_new_item(field_info *new_fi, proto_tree *tree,
                tvbuff_t *tvb, gint start, gint length,
                const guint encoding)
{
      proto_item  *pi;
      guint32           value, n;
      float       floatval;
      double            doubleval;
      char        *string;
      nstime_t    time_stamp;
      GPtrArray   *ptrs;

      /* 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, encoding);
                  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, encoding));
                  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, encoding));
                  break;

            case FT_INT64:
            case FT_UINT64:
                  DISSECTOR_ASSERT( length <= 8 && length >= 1);
                  proto_tree_set_uint64_tvb(new_fi, tvb, start, length, encoding);
                  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, encoding));
                  break;

            case FT_IPv4:
                  DISSECTOR_ASSERT(length == FT_IPv4_LEN);
                  value = tvb_get_ipv4(tvb, start);
                  /*
                   * NOTE: to support code written when
                   * proto_tree_add_item() took a gboolean as its
                   * last argument, with FALSE meaning "big-endian"
                   * and TRUE meaning "little-endian", we treat any
                   * non-zero value of "encoding" as meaning
                   * "little-endian".
                   */
                  proto_tree_set_ipv4(new_fi, encoding ? GUINT32_SWAP_LE_BE(value) : value);
                  break;

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

            case FT_IPv6:
                  DISSECTOR_ASSERT(length >=0 && length <= FT_IPv6_LEN);
                  proto_tree_set_ipv6_tvb(new_fi, tvb, start, length);
                  break;

            case FT_ETHER:
                  DISSECTOR_ASSERT(length == FT_ETHER_LEN);
                  proto_tree_set_ether_tvb(new_fi, tvb, start);
                  break;

            case FT_GUID:
                  DISSECTOR_ASSERT(length == FT_GUID_LEN);
                  proto_tree_set_guid_tvb(new_fi, tvb, start, encoding);
                  break;

            case FT_OID:
                  proto_tree_set_oid_tvb(new_fi, tvb, start, length);
                  break;

            case FT_FLOAT:
                  DISSECTOR_ASSERT(length == 4);
                  /*
                   * NOTE: to support code written when
                   * proto_tree_add_item() took a gboolean as its
                   * last argument, with FALSE meaning "big-endian"
                   * and TRUE meaning "little-endian", we treat any
                   * non-zero value of "encoding" as meaning
                   * "little-endian".
                   *
                   * At some point in the future, we might
                   * support non-IEEE-binary floating-point
                   * formats in the encoding as well
                   * (IEEE decimal, System/3x0, VAX).
                   */
                  if (encoding)
                        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:
                  /*
                   * NOTE: to support code written when
                   * proto_tree_add_item() took a gboolean as its
                   * last argument, with FALSE meaning "big-endian"
                   * and TRUE meaning "little-endian", we treat any
                   * non-zero value of "encoding" as meaning
                   * "little-endian".
                   *
                   * At some point in the future, we might
                   * support non-IEEE-binary floating-point
                   * formats in the encoding as well
                   * (IEEE decimal, System/3x0, VAX).
                   */
                  DISSECTOR_ASSERT(length == 8);
                  if (encoding)
                        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:
                  proto_tree_set_string_tvb(new_fi, tvb, start, length);
                  break;

            case FT_STRINGZ:
                  DISSECTOR_ASSERT(length >= -1);
                  /* 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);

                        string = ep_alloc(length);

                        tvb_memcpy(tvb, string, start, length);
                  } else if (length == 0) {
                        string = "[Empty]";
                  } 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.)
                         */
                        string = tvb_get_ephemeral_string(tvb,
                                                        start,
                                                        length);
                  }
                  new_fi->length = length;
                  proto_tree_set_string(new_fi, string);
                  break;

                  case FT_EBCDIC:
                  proto_tree_set_ebcdic_string_tvb(new_fi, tvb, start, length);
                  break;

            case FT_UINT_STRING:
                  n = get_uint_value(tvb, start, length, encoding);
                  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;

            case FT_ABSOLUTE_TIME:
            case FT_RELATIVE_TIME:
                  DISSECTOR_ASSERT(length == 8);
                  /*
                   * NOTE: to support code written when
                   * proto_tree_add_item() took a gboolean as its
                   * last argument, with FALSE meaning "big-endian"
                   * and TRUE meaning "little-endian", we treat any
                   * non-zero value of "encoding" as meaning
                   * "little-endian".
                   *
                   * At some point in the future, we might
                   * support non-struct timespec formats in
                   * the encoding as well (struct timeval,
                   * NTP time, Windows NT FILETIME, time_t, etc.).
                   */
                  if (encoding) {
                        time_stamp.secs  = tvb_get_letohl(tvb, start);
                        time_stamp.nsecs = tvb_get_letohl(tvb, start+4);
                  } else {
                        time_stamp.secs  = tvb_get_ntohl(tvb, start);
                        time_stamp.nsecs = tvb_get_ntohl(tvb, start+4);
                  }
                  proto_tree_set_time(new_fi, &time_stamp);
                  break;

            default:
                  g_error("new_fi->hfinfo->type %d (%s) not handled\n",
                              new_fi->hfinfo->type,
                              ftype_name(new_fi->hfinfo->type));
                  DISSECTOR_ASSERT_NOT_REACHED();
                  break;
      }
      /*
       * XXX - this should just check the ENC_*_ENDIAN bit, with
       * those fields for which we treat any non-zero value of
       * "encoding" checking the rest of the bits.
       */
      FI_SET_FLAG(new_fi, (encoding) ? FI_LITTLE_ENDIAN : FI_BIG_ENDIAN);

      /* 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. */
      ptrs = proto_lookup_or_create_interesting_hfids(tree, new_fi->hfinfo);
      if (ptrs)
            g_ptr_array_add(ptrs, new_fi);

      return pi;
}

/* Gets data from tvbuff, adds it to proto_tree, increments offset,
   and returns proto_item* */
proto_item *
ptvcursor_add(ptvcursor_t *ptvc, int hfindex, gint length,
            const guint encoding)
{
      field_info        *new_fi;
      header_field_info *hfinfo;
      gint              item_length;
      guint32                 n;
      int               offset;

      /* We can't fake it just yet. We have to advance the cursor
      TRY_TO_FAKE_THIS_ITEM(ptvc->tree, hfindex, hfinfo); */

      offset = ptvc->offset;
      hfinfo = get_hfi_and_length(hfindex, ptvc->tvb, offset, &length,
            &item_length);
      ptvc->offset += length;
      if (hfinfo->type == FT_UINT_BYTES || hfinfo->type == FT_UINT_STRING) {
            /*
             * The length of the rest of the item is in the first N
             * bytes of the item.
             */
            n = get_uint_value(ptvc->tvb, offset, length, encoding);
            ptvc->offset += n;
      }

      /* Coast clear. Try and fake it */
      TRY_TO_FAKE_THIS_ITEM(ptvc->tree, hfindex, hfinfo);
01553 
      new_fi = new_field_info(ptvc->tree, hfinfo, ptvc->tvb, offset, item_length);
      if (new_fi == NULL)
            return NULL;

      return proto_tree_new_item(new_fi, ptvc->tree, ptvc->tvb,
            offset, length, encoding);
}

/* 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 *
proto_tree_add_item(proto_tree *tree, const int hfindex, tvbuff_t *tvb,
                const gint start, gint length, const guint encoding)
{
      field_info  *new_fi;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

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

      return proto_tree_new_item(new_fi, tree, tvb, start,
            length, encoding);
}

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

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_ASSERT(hfinfo->type == FT_NONE);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

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

/* Gets data from tvbuff, adds it to proto_tree, *DOES NOT* increment
 * offset, and returns proto_item* */
proto_item *
ptvcursor_add_no_advance(ptvcursor_t* ptvc, int hf, gint length,
                   const guint encoding)
{
      proto_item  *item;

      item = proto_tree_add_item(ptvc->tree, hf, ptvc->tvb, ptvc->offset,
                           length, encoding);

      return item;
}

/* Advance the ptvcursor's offset within its tvbuff without
 * adding anything to the proto_tree. */
void
ptvcursor_advance(ptvcursor_t* ptvc, gint length)
{
      ptvc->offset += length;
}


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 *
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;
      field_info        *new_fi;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_ASSERT(hfinfo->type == FT_PROTOCOL);

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

      proto_tree_set_protocol_tvb(new_fi, (start == 0 ? tvb : tvb_new_subset(tvb, start, length, length)));

01655       TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}


/* Add a FT_BYTES to a proto_tree */
proto_item *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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 *
proto_tree_add_bytes_format_value(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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

      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);
      }
01742       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 *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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 *
proto_tree_add_time_format_value(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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}
01819 
/* Set the FT_*TIME value */
static void
proto_tree_set_time(field_info *fi, nstime_t *value_ptr)
{
      DISSECTOR_ASSERT(value_ptr != NULL);

      fvalue_set(&fi->value, value_ptr, FALSE);
}

/* Add a FT_IPXNET to a proto_tree */
proto_item *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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 *
proto_tree_add_ipxnet_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb,
                           gint start, gint length, guint32 value,
                           const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

01893       return pi;
}

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

/* Add a FT_IPv4 to a proto_tree */
proto_item *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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 *
proto_tree_add_ipv4_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb,
                         gint start, gint length, guint32 value,
                         const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

01967       return pi;
}

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

/* Add a FT_IPv6 to a proto_tree */
proto_item *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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 *
proto_tree_add_ipv6_format_value(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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

      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)
{
02049       DISSECTOR_ASSERT(value_ptr != NULL);
      fvalue_set(&fi->value, (gpointer) value_ptr, FALSE);
}

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

/* Add a FT_GUID to a proto_tree */
proto_item *
proto_tree_add_guid(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
                gint length, const e_guid_t *value_ptr)
{
      proto_item        *pi;
      field_info        *new_fi;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_ASSERT(hfinfo->type == FT_GUID);

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

      return pi;
}

proto_item *
proto_tree_add_guid_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb,
                         gint start, gint length,
                         const e_guid_t *value_ptr,
                         const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
proto_tree_add_guid_format(proto_tree *tree, int hfindex, tvbuff_t *tvb,
                     gint start, gint length, const e_guid_t *value_ptr,
                     const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

/* Set the FT_GUID value */
static void
proto_tree_set_guid(field_info *fi, const e_guid_t *value_ptr)
{
      DISSECTOR_ASSERT(value_ptr != NULL);
      fvalue_set(&fi->value, (gpointer) value_ptr, FALSE);
}

02135 static void
proto_tree_set_guid_tvb(field_info *fi, tvbuff_t *tvb, gint start,
                  const guint encoding)
{
      e_guid_t guid;

      tvb_get_guid(tvb, start, &guid, encoding);
      proto_tree_set_guid(fi, &guid);
}

/* Add a FT_OID to a proto_tree */
proto_item *
proto_tree_add_oid(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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_ASSERT(hfinfo->type == FT_OID);

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

      return pi;
}

proto_item *
proto_tree_add_oid_format_value(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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
proto_tree_add_oid_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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

/* Set the FT_OID value */
static void
proto_tree_set_oid(field_info *fi, const guint8* value_ptr, gint length)
{
      GByteArray        *bytes;

      DISSECTOR_ASSERT(value_ptr != NULL);

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

static void
proto_tree_set_oid_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length)
{
      proto_tree_set_oid(fi, tvb_get_ptr(tvb, start, length), length);
}

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

/*
 * NOTE: to support code written when proto_tree_add_item() took a
 * gboolean as its last argument, with FALSE meaning "big-endian"
 * and TRUE meaning "little-endian", we treat any non-zero value of
 * "encoding" as meaning "little-endian".
 */
static void
proto_tree_set_uint64_tvb(field_info *fi, tvbuff_t *tvb, gint start,
                    guint length, const guint encoding)
{
      guint64 value = 0;
      guint8* b = ep_tvb_memdup(tvb,start,length);

      if(encoding) {
            b += length;
            switch(length) {
                  default: DISSECTOR_ASSERT_NOT_REACHED();
                  case 8: value <<= 8; value += *--b;
                  case 7: value <<= 8; value += *--b;
                  case 6: value <<= 8; value += *--b;
                  case 5: value <<= 8; value += *--b;
                  case 4: value <<= 8; value += *--b;
                  case 3: value <<= 8; value += *--b;
                  case 2: value <<= 8; value += *--b;
                  case 1: value <<= 8; value += *--b;
                        break;
            }
      } else {
            switch(length) {
                  default: DISSECTOR_ASSERT_NOT_REACHED();
                  case 8: value <<= 8; value += *b++;
                  case 7: value <<= 8; value += *b++;
                  case 6: value <<= 8; value += *b++;
                  case 5: value <<= 8; value += *b++;
                  case 4: value <<= 8; value += *b++;
                  case 3: value <<= 8; value += *b++;
02276                   case 2: value <<= 8; value += *b++;
                  case 1: value <<= 8; value += *b++;
                        break;
            }
      }

      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 *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_ASSERT(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ);

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

      return pi;
}

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

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

      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()
 */
/* NOTE: this function will break with the TRY_TO_FAKE_THIS_ITEM()
02360  * speed optimization.
 * Currently only WSP use this function so it is not that bad but try to
 * avoid using this one if possible.
 * IF you must use this function you MUST also disable the
 * TRY_TO_FAKE_THIS_ITEM() optimization for your dissector/function
 * using proto_item_append_string().
 * Do that by faking that the tree is visible by calling
 * proto_tree_set_visible(tree, TRUE) (see packet-wsp.c)
 * BEFORE you create the item you are later going to use
 * proto_item_append_string() on.
 */
void
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);
      DISSECTOR_ASSERT(fi && "proto_tree_set_visible(tree, TRUE) should have been called previously");

      hfinfo = fi->hfinfo;
      if (hfinfo->type == FT_PROTOCOL) {
            /* TRY_TO_FAKE_THIS_ITEM() speed optimization: silently skip */
            return;
      }
      DISSECTOR_ASSERT(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ);
      old_str = fvalue_get(&fi->value);
      new_str = ep_strdup_printf("%s%s", old_str, str);
      fvalue_set(&fi->value, new_str, FALSE);
}

/* Set the FT_STRING value */
static void
proto_tree_set_string(field_info *fi, const char* value)
{
      if (value) {
            fvalue_set(&fi->value, (gpointer) value, FALSE);
      } else {
            fvalue_set(&fi->value, (gpointer) "[ Null ]", FALSE);
      }
}

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);
      }

      string = tvb_get_ephemeral_string(tvb, start, length);
      proto_tree_set_string(fi, string);
}

static void
proto_tree_set_ebcdic_string_tvb(field_info *fi, tvbuff_t *tvb, gint start,
                         gint length)
{
      gchar *string;
02426 
      if (length == -1) {
            length = tvb_ensure_length_remaining(tvb, start);
      }

      string = tvb_get_ephemeral_string(tvb, start, length);
      EBCDIC_to_ASCII(string, length);
      proto_tree_set_string(fi, string);
}

/* Add a FT_ETHER to a proto_tree */
proto_item *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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 *
proto_tree_add_ether_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb,
                          gint start, gint length, const guint8* value,
                          const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

      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)
02506 {
      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, FT_ETHER_LEN));
}

/* Add a FT_BOOLEAN to a proto_tree */
proto_item *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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 *
proto_tree_add_boolean_format_value(proto_tree *tree, int hfindex,
                            tvbuff_t *tvb, gint start, gint length,
                            guint32 value, const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

02580       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 *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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 *
proto_tree_add_float_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb,
                          gint start, gint length, float value,
                          const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

02654       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 *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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 *
proto_tree_add_double_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb,
                           gint start, gint length, double value,
                           const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

02728       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 *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, 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:
                  DISSECTOR_ASSERT_NOT_REACHED();
      }

      return pi;
}

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

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

      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;
02828 
            /* Shift bits */
            if (hfinfo->bitshift > 0) {
                  integer >>= hfinfo->bitshift;
            }
      }

      fvalue_set_uinteger(&fi->value, integer);
}

/* Add FT_UINT64 to a proto_tree */
proto_item *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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_value(proto_tree *tree, int hfindex, tvbuff_t *tvb,
                           gint start, gint length, guint64 value,
                           const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      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 *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, 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:
                  DISSECTOR_ASSERT_NOT_REACHED();
      }

      return pi;
}

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

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      return pi;
}

proto_item *
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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

      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;
02994 
            /* Shift bits */
            if (hfinfo->bitshift > 0) {
                  integer >>= hfinfo->bitshift;
            }
      }

      fvalue_set_sinteger(&fi->value, integer);
}

/* Add FT_INT64 to a proto_tree */
proto_item *
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;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

      DISSECTOR_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_value(proto_tree *tree, int hfindex, tvbuff_t *tvb,
                          gint start, gint length, gint64 value,
                          const char *format, ...)
{
      proto_item        *pi;
      va_list                 ap;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

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

      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;
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo);

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

      TRY_TO_FAKE_THIS_REPR(tree, pi);

      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.
       *
       * "PNODE_FINFO(tnode)" 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 = PNODE_FINFO(tnode);
      if (tfi != NULL && (tfi->tree_type < 0 || tfi->tree_type >= num_tree_types)) {
            REPORT_DISSECTOR_BUG(ep_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? */
      }

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

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

      if (tnode->last_child != NULL) {
            sibling = tnode->last_child;
            DISSECTOR_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;
      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. */
      ptrs = proto_lookup_or_create_interesting_hfids(tree, fi->hfinfo);
      if (ptrs)
            g_ptr_array_add(ptrs, fi);

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

      return pi;
}


static header_field_info *
get_hfi_and_length(int hfindex, tvbuff_t *tvb, const gint start, gint *length,
               gint *item_length)
{
      header_field_info *hfinfo;
      gint              length_remaining;

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

      PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);

      /*
       * XXX - in some protocols, there are 32-bit unsigned length
       * fields, so lengths in protocol tree and tvbuff routines
       * should really be unsigned.  We should have, for those
       * field types for which "to the end of the tvbuff" makes sense,
       * additional routines that take no length argument and
       * add fields that run to the end of the tvbuff.
       */
      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);
                  }
                  DISSECTOR_ASSERT(*length >= 0);
                  break;

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

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

            default:
                  DISSECTOR_ASSERT_NOT_REACHED();
            }
            *item_length = *length;
      } else {
            *item_length = *length;
            if (hfinfo->type == FT_PROTOCOL || hfinfo->type == FT_NONE) {
                  /*
                   * These types are for interior nodes of the
                   * tree, and don't have data associated with
                   * them; if the length is negative (XXX - see
                   * above) or goes past the end of the tvbuff,
                   * cut it short at the end of the tvbuff.
                   * That way, if this field is selected in
                   * Wireshark, we don't highlight stuff past
                   * the end of the data.
                   */
                  /* XXX - what to do, if we don't have a tvb? */
                  if (tvb) {
                        length_remaining = tvb_length_remaining(tvb, start);
                        if (*item_length < 0 ||
                              (*item_length > 0 &&
                                (length_remaining < *item_length)))
                              *item_length = length_remaining;
                  }
            }
            if (*item_length < 0) {
                  THROW(ReportedBoundsError);
            }
      }

      return hfinfo;
}

static field_info *
new_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb,
             const gint start, const gint item_length)
{
      field_info        *fi;

      FIELD_INFO_NEW(fi);

      fi->hfinfo = hfinfo;
      fi->start = start;
      fi->start+=(tvb)?TVB_RAW_OFFSET(tvb):0;
      fi->length = item_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;

      fi->appendix_start = 0;
      fi->appendix_length = 0;

      return fi;
}

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

      hfinfo = get_hfi_and_length(hfindex, tvb, start, length, &item_length);
      return new_field_info(tree, hfinfo, tvb, start, item_length);
}

/* If the protocol tree is to be visible, set the representation of a
   proto_tree entry with the name of the field for the item and with
   the value formatted with the supplied printf-style format and
   argument list. */
static void
proto_tree_set_representation_value(proto_item *pi, const char *format, va_list ap)
{
      int   ret;  /*tmp return value */
      field_info *fi = PITEM_FINFO(pi);
      header_field_info *hf;

      DISSECTOR_ASSERT(fi);

      hf = fi->hfinfo;

      if (!PROTO_ITEM_IS_HIDDEN(pi)) {
            ITEM_LABEL_NEW(fi->rep);
            if (hf->bitmask && (hf->type == FT_BOOLEAN || IS_FT_UINT(hf->type))) {
                  char tmpbuf[64];
                  guint32 val;

                  val = fvalue_get_uinteger(&fi->value);
                  if (hf->bitshift > 0) {
                        val <<= hf->bitshift;
                  }
                  decode_bitfield_value(tmpbuf, val, hf->bitmask, hfinfo_bitwidth(hf));
                  /* put in the hf name */
                  ret = g_snprintf(fi->rep->representation, ITEM_LABEL_LENGTH,
                               "%s%s: ", tmpbuf, fi->hfinfo->name);
            } else {
                  /* put in the hf name */
                  ret = g_snprintf(fi->rep->representation, ITEM_LABEL_LENGTH,
                               "%s: ", fi->hfinfo->name);
            }

            /* If possible, Put in the value of the string */
            if (ret < ITEM_LABEL_LENGTH) {
                  ret += g_vsnprintf(fi->rep->representation + ret,
                                ITEM_LABEL_LENGTH - ret, format, ap);
            }
            if (ret >= ITEM_LABEL_LENGTH) {
                  /* Uh oh, we don't have enough room.  Tell the user
                   * that the field is truncated.
                   */
                  char *oldrep;

                  /*    Argh, we cannot reuse 'ap' here.  So make a copy
                   *    of what we formatted for (re)use below.
                   */
                  oldrep = g_strdup(fi->rep->representation);

                  g_snprintf(fi->rep->representation,
                           ITEM_LABEL_LENGTH,
                           "[truncated] %s",
                           oldrep);
                  g_free(oldrep);
            }
      }
}

/* If the protocol tree is to be visible, set the representation of a
   proto_tree entry with the representation formatted with the supplied
   printf-style format and argument list. */
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);

      DISSECTOR_ASSERT(fi);

      if (!PROTO_ITEM_IS_HIDDEN(pi)) {
            ITEM_LABEL_NEW(fi->rep);
            ret = g_vsnprintf(fi->rep->representation, ITEM_LABEL_LENGTH,
                          format, ap);
            if (ret >= ITEM_LABEL_LENGTH) {
                  /* Uh oh, we don't have enough room.  Tell the user
                   * that the field is truncated.
                   */
                  char *oldrep;

                  /*    Argh, we cannot reuse 'ap' here.  So make a copy
                   *    of what we formatted for (re)use below.
03403                    */
                  oldrep = g_strdup(fi->rep->representation);

                  g_snprintf(fi->rep->representation, ITEM_LABEL_LENGTH,
                           "[truncated] %s", oldrep);
                  g_free(oldrep);
            }
      }
}

/* -------------------------- */
const gchar *
proto_custom_set(proto_tree* tree, const int field_id, gchar *result,
             gchar *expr, const int size)
{
      guint32           u_integer;
      gint32            integer;
      guint8            *bytes;
      ipv4_addr   *ipv4;
      struct e_in6_addr *ipv6;
      address           addr;
      guint32           n_addr; /* network-order IPv4 address */

      const true_false_string  *tfstring;
      int         len;
      GPtrArray   *finfos;
      field_info  *finfo;
      header_field_info* hfinfo;

      g_assert(field_id >= 0);

      hfinfo = proto_registrar_get_nth((guint)field_id);

      /* do we need to rewind ? */
      if (!hfinfo)
            return "";

      while (hfinfo) {
            finfos = proto_get_finfo_ptr_array(tree, hfinfo->id);

            if (!finfos || !(len = g_ptr_array_len(finfos))) {
                  hfinfo = hfinfo->same_name_next;
                  continue;
            }
            /* get the last one  */
            finfo = g_ptr_array_index(finfos, len -1);

            switch(hfinfo->type) {

            case FT_NONE: /* Nothing to add */
                  result[0] = '\0';
                  break;

            case FT_PROTOCOL:
                  g_strlcpy(result, "Yes", size);
                  break;

            case FT_UINT_BYTES:
            case FT_BYTES:
                  bytes = fvalue_get(&finfo->value);
                  g_strlcpy(result, bytes_to_str(bytes, fvalue_length(&finfo->value)), size);
                  break;

            case FT_ABSOLUTE_TIME:
                  g_strlcpy(result,
                        abs_time_to_str(fvalue_get(&finfo->value), hfinfo->display, TRUE),
                        size);
                  break;

            case FT_RELATIVE_TIME:
                  g_strlcpy(result, rel_time_to_secs_str(fvalue_get(&finfo->value)), size);
                  break;

            case FT_BOOLEAN:
                  u_integer = fvalue_get_uinteger(&finfo->value);
                  tfstring = (const true_false_string *)&tfs_true_false;
                  if (hfinfo->strings) {
                        tfstring = (const struct true_false_string*) hfinfo->strings;
                  }
                  g_strlcpy(result, u_integer ? tfstring->true_string : tfstring->false_string, size);
                  break;

            case FT_UINT8:
            case FT_UINT16:
            case FT_UINT24:
            case FT_UINT32:
            case FT_FRAMENUM:
                  u_integer = fvalue_get_uinteger(&finfo->value);
                  if (hfinfo->strings) {
                        if (hfinfo->display & BASE_RANGE_STRING) {
                              g_strlcpy(result, rval_to_str(u_integer, hfinfo->strings, "%u"), size);
                        } else if (hfinfo->display & BASE_EXT_STRING) {
                              g_strlcpy(result, val_to_str_ext(u_integer, (value_string_ext *) (hfinfo->strings), "%u"), size);
                        } else {
                              g_strlcpy(result, val_to_str(u_integer, cVALS(hfinfo->strings), "%u"), size);
                        }
                  } else if (IS_BASE_DUAL(hfinfo->display)) {
                        g_snprintf(result, size, hfinfo_uint_value_format(hfinfo), u_integer, u_integer);
                  } else {
                        g_snprintf(result, size, hfinfo_uint_value_format(hfinfo), u_integer);
                  }
                  break;

            case FT_INT64:
            case FT_UINT64:
                  g_snprintf(result, size, "%" G_GINT64_MODIFIER "u", fvalue_get_integer64(&finfo->value));
                  break;

            /* XXX - make these just FT_INT? */
            case FT_INT8:
            case FT_INT16:
            case FT_INT24:
            case FT_INT32:
                  integer = fvalue_get_sinteger(&finfo->value);
                  if (hfinfo->strings) {
                        if (hfinfo->display & BASE_RANGE_STRING) {
                              g_strlcpy(result, rval_to_str(integer, hfinfo->strings, "%d"), size);
                        } else if (hfinfo->display & BASE_EXT_STRING) {
                              g_strlcpy(result, val_to_str_ext(integer, (value_string_ext *) (hfinfo->strings), "%d"), size);
                        } else {
                              g_strlcpy(result, val_to_str(integer, cVALS(hfinfo->strings), "%d"), size);
                  }
                  } else if (IS_BASE_DUAL(hfinfo->display)) {
                        g_snprintf(result, size, hfinfo_int_value_format(hfinfo), integer, integer);
                  } else {
                        g_snprintf(result, size, hfinfo_int_value_format(hfinfo), integer);
                  }
                  break;

            case FT_IPv4:
                  ipv4 = fvalue_get(&finfo->value);
                  n_addr = ipv4_get_net_order_addr(ipv4);
                  g_strlcpy(result, ip_to_str((guint8 *)&n_addr), size);
                  break;

            case FT_IPv6:
                  ipv6 = fvalue_get(&finfo->value);
                  SET_ADDRESS (&addr, AT_IPv6, sizeof(struct e_in6_addr), ipv6);
                  address_to_str_buf(&addr, result, size);
                  break;

            case FT_ETHER:
                  g_strlcpy(result, bytes_to_str_punct(fvalue_get(&finfo->value), 6, ':'), size);
                  break;

            case FT_GUID:
                  g_strlcpy(result, guid_to_str((e_guid_t *)fvalue_get(&finfo->value)), size);
                  break;

            case FT_OID:
                  bytes = fvalue_get(&finfo->value);
                  g_strlcpy(result, oid_resolved_from_encoded(bytes, fvalue_length(&finfo->value)), size);
                  break;

            case FT_FLOAT:
                  g_snprintf(result, size, "%." STRINGIFY(FLT_DIG) "f", fvalue_get_floating(&finfo->value));
                  break;

            case FT_DOUBLE:
                  g_snprintf(result, size, "%." STRINGIFY(DBL_DIG) "g", fvalue_get_floating(&finfo->value));
                  break;

            case FT_EBCDIC:
            case FT_STRING:
            case FT_STRINGZ:
            case FT_UINT_STRING:
                  bytes = fvalue_get(&finfo->value);
                  g_strlcpy(result, format_text(bytes, strlen(bytes)), size);
                  break;

            case FT_IPXNET: /*XXX really No column custom ?*/
            case FT_PCRE:
            default:
                  g_error("hfinfo->type %d (%s) not handled\n",
                              hfinfo->type,
                              ftype_name(hfinfo->type));
                  DISSECTOR_ASSERT_NOT_REACHED();
                  break;
            }

            switch(hfinfo->type) {
            case FT_BOOLEAN:
                  g_snprintf(expr, size, "%u", fvalue_get_uinteger(&finfo->value) ? 1 : 0);
                  break;

            case FT_UINT8:
            case FT_UINT16:
            case FT_UINT24:
            case FT_UINT32:
            case FT_FRAMENUM:
                  g_snprintf(expr, size, hfinfo_numeric_value_format(hfinfo), fvalue_get_uinteger(&finfo->value));
                  break;
            case FT_INT8:
            case FT_INT16:
            case FT_INT24:
            case FT_INT32:
                  g_snprintf(expr, size, hfinfo_numeric_value_format(hfinfo), fvalue_get_sinteger(&finfo->value));
                  break;
            default:
                  g_strlcpy(expr, result, size);
                  break;
            }
            return hfinfo->abbrev;
      }
      return "";
}


/* Set text of proto_item after having already been created. */
void
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==NULL)
            return;

      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
proto_item_append_text(proto_item *pi, const char *format, ...)
{
      field_info *fi = NULL;
      size_t curlen;
      va_list     ap;

      if (pi==NULL) {
            return;
      }

      fi = PITEM_FINFO(pi);
      if (fi==NULL) {
            return;
      }

      if (!PROTO_ITEM_IS_HIDDEN(pi)) {
            /*
             * 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);
            }
03661 
            curlen = strlen(fi->rep->representation);
            if (ITEM_LABEL_LENGTH > curlen) {
                  va_start(ap, format);
                  g_vsnprintf(fi->rep->representation + curlen,
                        ITEM_LABEL_LENGTH - (gulong) curlen, format, ap);
                  va_end(ap);
            }
      }
}

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

      if (pi == NULL)
            return;

      fi = PITEM_FINFO(pi);
      if (fi == NULL)
            return;

      DISSECTOR_ASSERT(length >= 0);
      fi->length = length;

03687       if (fi->value.ftype->ftype == FT_BYTES)
            fi->value.value.bytes->len = 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
proto_item_set_end(proto_item *pi, tvbuff_t *tvb, gint end)
{
      field_info *fi;

      if (pi == NULL)
03704             return;

      fi = PITEM_FINFO(pi);
      if (fi == NULL)
            return;

      end += TVB_RAW_OFFSET(tvb);
      DISSECTOR_ASSERT(end >= fi->start);
      fi->length = end - fi->start;
}

int
proto_item_get_len(const proto_item *pi)
{
03718       field_info *fi = PITEM_FINFO(pi);
      return fi ? fi->length : -1;
}


/** clear flags according to the mask and set new flag values */
#define FI_REPLACE_FLAGS(fi, mask, flags_in) { \
      (fi->flags = (fi)->flags & ~(mask)); \
      (fi->flags = (fi)->flags | (flags_in)); \
}

gboolean
proto_item_set_expert_flags(proto_item *pi, const int group, const guint severity)
{
      if(pi == NULL || PITEM_FINFO(pi) == NULL)
            return FALSE;

03735       /* only change things if severity is worse or at least equal than before */
      if(severity >= FI_GET_FLAG(PITEM_FINFO(pi), PI_SEVERITY_MASK)) {
            FI_REPLACE_FLAGS(PITEM_FINFO(pi), PI_GROUP_MASK, group);
            FI_REPLACE_FLAGS(PITEM_FINFO(pi), PI_SEVERITY_MASK, severity);

            return TRUE;
      }

      return FALSE;
}

proto_tree *
proto_tree_create_root(void)
{
      proto_node  *pnode;

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

      /* Don't initialize the tree_data_t. Wait until we know we need it */
      pnode->tree_data->interesting_hfids = NULL;

      /* 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;

      /* Make sure that we fake protocols (if possible) */
      pnode->tree_data->fake_protocols = TRUE;

      /* Keep track of the number of children */
      pnode->tree_data->count = 0;

      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 _U_, const gint hfid)
{
      header_field_info *hfinfo;

      PROTO_REGISTRAR_GET_NTH(hfid, hfinfo);
      /* this field is referenced by a filter so increase the refcount.
         also increase the refcount for the parent, i.e the protocol.
      */
      hfinfo->ref_type = HF_REF_TYPE_DIRECT;
      /* only increase the refcount if there is a parent.
         if this is a protocol and not a field then parent will be -1
         and there is no parent to add any refcounting for.
      */
      if (hfinfo->parent != -1) {
03793             header_field_info *parent_hfinfo;
            PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo);

            /* Mark parent as indirectly referenced unless it is already directly
             * referenced, i.e. the user has specified the parent in a filter.
             */
            if (parent_hfinfo->ref_type != HF_REF_TYPE_DIRECT)
                  parent_hfinfo->ref_type = HF_REF_TYPE_INDIRECT;
      }
}

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

      if (!pi)
            return(NULL);

03811       DISSECTOR_ASSERT(idx >= 0 && idx < num_tree_types);

      fi = PITEM_FINFO(pi);
      if (!fi)
            return (proto_tree*) pi;

      fi->tree_type = idx;

      return (proto_tree*) pi;
}

proto_tree *
03823 proto_item_get_subtree(const proto_item *pi) {
      field_info *fi;

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

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

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


proto_item *
proto_tree_get_parent(const proto_tree *tree) {
      if (!tree)
            return (NULL);
      return (proto_item*) tree;
}
03860 
proto_tree *
proto_tree_get_root(proto_tree *tree) {
      if (!tree)
            return (NULL);
      while (tree->parent) {
            tree = tree->parent;
      }
      return tree;
}

void
proto_tree_move_item(proto_tree *tree, proto_item *fixed_item,
                 proto_item *item_to_move)
{
      DISSECTOR_ASSERT(item_to_move->parent == tree);
      DISSECTOR_ASSERT(fixed_item->parent == tree);

      /*** cut item_to_move out ***/

      /* is item_to_move the first? */
      if(tree->first_child == item_to_move) {
            /* simply change first child to next */
            tree->first_child = item_to_move->next;

            DISSECTOR_ASSERT(tree->last_child != item_to_move);
      } else {
            proto_item *curr_item;
            /* find previous and change it's next */
            for(curr_item = tree->first_child; curr_item != NULL; curr_item = curr_item->next) {
                  if(curr_item->next == item_to_move) {
                        break;
                  }
            }

            DISSECTOR_ASSERT(curr_item);

            curr_item->next = item_to_move->next;

            /* fix last_child if required */
            if(tree->last_child == item_to_move) {
                  tree->last_child = curr_item;
03902             }
      }

      /*** insert to_move after fixed ***/
      item_to_move->next = fixed_item->next;
      fixed_item->next = item_to_move;
      if(tree->last_child == fixed_item) {
            tree->last_child = item_to_move;
      }
}

void
proto_tree_set_appendix(proto_tree *tree, tvbuff_t *tvb, gint start,
                  const gint length)
{
      field_info *fi;

      if (tree == NULL)
            return;

      fi = PTREE_FINFO(tree);
03923       if (fi == NULL)
            return;

      start += TVB_RAW_OFFSET(tvb);
      DISSECTOR_ASSERT(start >= 0);
      DISSECTOR_ASSERT(length >= 0);

      fi->appendix_start = start;
      fi->appendix_length = length;
}

int
proto_register_protocol(const char *name, const char *short_name,
                  const char *filter_name)
{
      protocol_t *protocol;
      header_field_info *hfinfo;
      int proto_id;
      char *existing_name;
      gint *key;
      guint i;
      guchar c;
      gboolean found_invalid;

      /*
       * 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 = wrs_str_hash(name);
      existing_name = g_hash_table_lookup(proto_names, key);
      if (existing_name != NULL) {
            /* g_error will terminate the program */
            g_error("Duplicate protocol name \"%s\"!"
                  " This might be caused by an inappropriate plugin or a development error.", name);
      }
      g_hash_table_insert(proto_names, key, (gpointer)name);

      existing_name = g_hash_table_lookup(proto_short_names, (gpointer)short_name);
      if (existing_name != NULL) {
            g_error("Duplicate protocol short_name \"%s\"!"
                  " This might be caused by an inappropriate plugin or a development error.", short_name);
      }
      g_hash_table_insert(proto_short_names, (gpointer)short_name, (gpointer)short_name);

      found_invalid = FALSE;
      for (i = 0; i < strlen(filter_name); i++) {
            c = filter_name[i];
            if (!(islower(c) || isdigit(c) || c == '-' || c == '_' || c == '.')) {
                  found_invalid = TRUE;
            }
      }
      if (found_invalid) {
            g_error("Protocol filter name \"%s\" has one or more invalid characters."
                  " Allowed are lower characters, digits, '-', '_' and '.'."
                  " This might be caused by an inappropriate plugin or a development error.", filter_name);
      }
      existing_name = g_hash_table_lookup(proto_filter_names, (gpointer)filter_name);
      if (existing_name != NULL) {
            g_error("Duplicate protocol filter_name \"%s\"!"
                  " This might be caused by an inappropriate plugin or a development error.", filter_name);
      }
      g_hash_table_insert(proto_filter_names, (gpointer)filter_name, (gpointer)filter_name);

      /* Add this protocol to the list of known protocols; the list
         is sorted by protocol short name. */
      protocol = g_new(protocol_t, 1);
      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;
      protocol->is_private = FALSE;
      /* list will be sorted later by name, when all protocols completed registering */
      protocols = g_list_prepend(protocols, protocol);

      /* Here we do allocate a new header_field_info struct */
#if GLIB_CHECK_VERSION(2,10,0)
      hfinfo = g_slice_new(header_field_info);
#else
      hfinfo = g_mem_chunk_alloc(gmc_hfinfo);
#endif
      hfinfo->name = name;
      hfinfo->abbrev = filter_name;
      hfinfo->type = FT_PROTOCOL;
      hfinfo->display = BASE_NONE;
      hfinfo->strings = protocol;
04023       hfinfo->bitmask = 0;
      hfinfo->bitshift = 0;
      hfinfo->ref_type = HF_REF_TYPE_NONE;
      hfinfo->blurb = NULL;
      hfinfo->parent = -1; /* this field differentiates protos and fields */

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

void
proto_mark_private(const int proto_id)
{
      protocol_t *protocol = find_protocol_by_id(proto_id);
      if (protocol)
            protocol->is_private = TRUE;
}

gboolean
proto_is_private(const int proto_id)
{
      protocol_t *protocol = find_protocol_by_id(proto_id);
      if (protocol)
            return protocol->is_private;
04048       else
            return FALSE;
}

/*
 * 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(const 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;
04103       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;
}

protocol_t *
find_protocol_by_id(const int proto_id)
{
      header_field_info *hfinfo;

      if(proto_id<0)
            return NULL;

      PROTO_REGISTRAR_GET_NTH(proto_id, hfinfo);
      DISSECTOR_ASSERT(hfinfo->type==FT_PROTOCOL);
      return (protocol_t *)hfinfo->strings;
04125 }

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

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

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

int proto_get_id_by_filter_name(const gchar* filter_name)
{
      GList *list_entry;
04145       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;
04154 }

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

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

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

const char *
proto_get_protocol_long_name(const protocol_t *protocol)
{
      if (protocol == NULL)
            return "(none)";
      return protocol->name;
}

04181 const char *
proto_get_protocol_filter_name(const int proto_id)
{
      protocol_t *protocol;

      protocol = find_protocol_by_id(proto_id);
04187       if (protocol == NULL)
            return "(none)";
      return protocol->filter_name;
}

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

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

      protocol = find_protocol_by_id(proto_id);
      return protocol->can_toggle;
}
04206 
void
proto_set_decoding(const int proto_id, const gboolean enabled)
{
      protocol_t *protocol;

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

void
proto_enable_all(void)
{
      protocol_t *protocol;
      GList *list_item = protocols;

04223       if (protocols == NULL)
            return;

      while (list_item) {
            protocol = list_item->data;
            if (protocol->can_toggle)
                  protocol->is_enabled = TRUE;
            list_item = g_list_next(list_item);
      }
}

04234 void
proto_set_cant_toggle(const 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
proto_register_field_array(const int parent, hf_register_info *hf, const 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()").
             */
            if (*ptr->p_id != -1 && *ptr->p_id != 0) {
                  fprintf(stderr,
                        "Duplicate field detected in call to proto_register_field_array: %s is already registered\n",
                        ptr->hfinfo.abbrev);
                  return;
            }

            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;
      }
}

/* chars allowed in field abbrev */
static
const guchar fld_abbrev_chars[256] = {
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x0F */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x1F */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, /* 0x20-0x2F '-', '.'     */
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30-0x3F '0'-'9'      */
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40-0x4F 'A'-'O'      */
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x50-0x5F 'P'-'Z', '_' */
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60-0x6F 'a'-'o'      */
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x70-0x7F 'p'-'z'      */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80-0x8F */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x9F */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0-0xAF */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0-0xBF */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0-0xCF */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0-0xDF */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE0-0xEF */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xF0-0xFF */
};

/* temporary function containing assert part for easier profiling */
static void tmp_fld_check_assert(header_field_info *hfinfo) {
      static const value_string hf_types[] = {
          { FT_NONE,          "FT_NONE" },
          { FT_PROTOCOL,      "FT_PROTOCOL" },
          { FT_BOOLEAN, "FT_BOOLEAN" },
          { FT_UINT8,         "FT_UINT8" },
          { FT_UINT16,  "FT_UINT16" },
          { FT_UINT24,  "FT_UINT24" },
          { FT_UINT32,  "FT_UINT32" },
          { FT_UINT64,  "FT_UINT64" },
          { FT_INT8,          "FT_INT8" },
          { FT_INT16,         "FT_INT16" },
          { FT_INT24,         "FT_INT24" },
          { FT_INT32,         "FT_INT32" },
          { FT_INT64,         "FT_INT64" },
          { FT_FLOAT,         "FT_FLOAT" },
          { FT_DOUBLE,  "FT_DOUBLE" },
          { FT_ABSOLUTE_TIME, "FT_ABSOLUTE_TIME" },
          { FT_RELATIVE_TIME, "FT_RELATIVE_TIME" },
          { FT_STRING,  "FT_STRING" },
          { FT_STRINGZ, "FT_STRINGZ" },
          { FT_EBCDIC,  "FT_EBCDIC" },
          { FT_UINT_STRING,   "FT_UINT_STRING" },
          { FT_ETHER,         "FT_ETHER" },
          { FT_BYTES,         "FT_BYTES" },
          { FT_UINT_BYTES,    "FT_UINT_BYTES" },
          { FT_IPv4,          "FT_IPv4" },
          { FT_IPv6,          "FT_IPv6" },
          { FT_IPXNET,  "FT_IPXNET" },
          { FT_FRAMENUM,      "FT_FRAMENUM" },
          { FT_PCRE,          "FT_PCR" },
          { FT_GUID,          "FT_GUID" },
          { FT_OID,           "FT_OID" },
          { 0,          NULL } };

      static const value_string hf_display[] = {
          { BASE_NONE,              "BASE_NONE" },
          { BASE_DEC,                     "BASE_DEC" },
          { BASE_HEX,                     "BASE_HEX" },
          { BASE_OCT,                     "BASE_OCT" },
          { BASE_DEC_HEX,                 "BASE_DEC_HEX" },
          { BASE_HEX_DEC,                 "BASE_HEX_DEC" },
          { BASE_CUSTOM,                  "BASE_CUSTOM" },
          { BASE_NONE|BASE_RANGE_STRING,  "BASE_NONE|BASE_RANGE_STRING" },
          { BASE_DEC|BASE_RANGE_STRING,   "BASE_DEC|BASE_RANGE_STRING" },
          { BASE_HEX|BASE_RANGE_STRING,   "BASE_HEX|BASE_RANGE_STRING" },
          { BASE_OCT|BASE_RANGE_STRING,   "BASE_OCT|BASE_RANGE_STRING" },
          { BASE_DEC_HEX|BASE_RANGE_STRING,     "BASE_DEC_HEX|BASE_RANGE_STRING" },
          { BASE_HEX_DEC|BASE_RANGE_STRING,     "BASE_HEX_DEC|BASE_RANGE_STRING" },
          { BASE_CUSTOM|BASE_RANGE_STRING,      "BASE_CUSTOM|BASE_RANGE_STRING" },
          { ABSOLUTE_TIME_LOCAL,          "ABSOLUTE_TIME_LOCAL" },
          { ABSOLUTE_TIME_UTC,            "ABSOLUTE_TIME_LOCAL" },
          { ABSOLUTE_TIME_DOY_UTC,        "ABSOLUTE_TIME_LOCAL" },
          { 0,                      NULL } };

      /* The field must have a name (with length > 0) */
      if (!hfinfo->name || !hfinfo->name[0]) {
            if (hfinfo->abbrev)
                  /* Try to identify the field */
                  g_error("Field (abbrev='%s') does not have a name\n",
                        hfinfo->abbrev);
            else
                  /* Hum, no luck */
                  g_error("Field does not have a name (nor an abbreviation)\n");
      }

      /* fields with an empty string for an abbreviation aren't filterable */
      /* XXX - so why aren't we checking for an empty string here?? */
      if (!hfinfo->abbrev)
            g_error("Field '%s' does not have an abbreviation\n", hfinfo->name);
      /* Okay, let's at least put out a warning and see if anyone complains */
      if (!hfinfo->abbrev[0])
            g_warning("Field '%s' does not have an abbreviation", hfinfo->name);

      /*  These types of fields are allowed to have value_strings,
       *  true_false_strings or a protocol_t struct
       */
      if (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_PROTOCOL) ||
          (hfinfo->type == FT_FRAMENUM) ))
            g_error("Field '%s' (%s) has a 'strings' value but is of type %s (which is not allowed to have strings)\n",
                  hfinfo->name, hfinfo->abbrev,
                  val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"));

      switch (hfinfo->type) {

      case FT_INT8:
      case FT_INT16:
      case FT_INT24:
      case FT_INT32:
      case FT_INT64:
            /*    Hexadecimal and octal are, in printf() and everywhere
             *    else, unsigned so don't allow dissectors to register a
             *    signed field to be displayed unsigned.  (Else how would
             *    we display negative values?)
             *
             *    If you want to take out this check, be sure to fix
             *    hfinfo_numeric_format() so that it does not assert out
             *    when trying to construct a hexadecimal representation of
             *    FT_INT*.
             */
            if (hfinfo->display == BASE_HEX ||
                hfinfo->display == BASE_HEX_DEC ||
                hfinfo->display == BASE_DEC_HEX ||
                hfinfo->display == BASE_OCT)
                  g_error("Field '%s' (%s) is signed (%s) but is being displayed unsigned (%s)\n",
                        hfinfo->name, hfinfo->abbrev,
                        val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"),
                        val_to_str(hfinfo->display, hf_display, "(Bit count: %d)"));
            /* FALL THROUGH */
      case FT_UINT8:
      case FT_UINT16:
      case FT_UINT24:
      case FT_UINT32:
            if (hfinfo->strings == NULL) {
                  /*  Require integral types (other than frame number,
                   *  which is always displayed in decimal) to have a
                   *  number base */
                  if (hfinfo->display == BASE_NONE)
                        g_error("Field '%s' (%s) is an integral value (%s) without strings but is being displayed as BASE_NONE\n",
                              hfinfo->name, hfinfo->abbrev,
                              val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"));
            }
            break;

      case FT_UINT64:
            if (hfinfo->display == BASE_NONE)
                  g_error("Field '%s' (%s) is an integral value (%s) but is being displayed as BASE_NONE\n",
                        hfinfo->name, hfinfo->abbrev,
                        val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"));
            break;

      case FT_PROTOCOL:
      case FT_FRAMENUM:
            if (hfinfo->display != BASE_NONE)
                  g_error("Field '%s' (%s) is an %s but is being displayed as %s instead of BASE_NONE\n",
                        hfinfo->name, hfinfo->abbrev,
                        val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"),
                        val_to_str(hfinfo->display, hf_display, "(Bit count: %d)"));
            if (hfinfo->bitmask != 0)
                  g_error("Field '%s' (%s) is an %s but has a bitmask\n",
                        hfinfo->name, hfinfo->abbrev,
                        val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"));
            break;

      case FT_BOOLEAN:
            break;

      case FT_ABSOLUTE_TIME:
            if (!(hfinfo->display == ABSOLUTE_TIME_LOCAL ||
                hfinfo->display == ABSOLUTE_TIME_UTC ||
                hfinfo->display == ABSOLUTE_TIME_DOY_UTC))
                  g_error("Field '%s' (%s) is a %s but is being displayed as %s instead of as a time\n",
                        hfinfo->name, hfinfo->abbrev,
                        val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"),
                        val_to_str(hfinfo->display, hf_display, "(Bit count: %d)"));
            if (hfinfo->bitmask != 0)
                  g_error("Field '%s' (%s) is an %s but has a bitmask\n",
                        hfinfo->name, hfinfo->abbrev,
                        val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"));
            break;

      default:
            if (hfinfo->display != BASE_NONE)
                  g_error("Field '%s' (%s) is an %s but is being displayed as %s instead of BASE_NONE\n",
                        hfinfo->name, hfinfo->abbrev,
                        val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"),
                        val_to_str(hfinfo->display, hf_display, "(Bit count: %d)"));
            if (hfinfo->bitmask != 0)
                  g_error("Field '%s' (%s) is an %s but has a bitmask\n",
                        hfinfo->name, hfinfo->abbrev,
                        val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"));
            if (hfinfo->strings != NULL)
                  g_error("Field '%s' (%s) is an %s but has a strings value\n",
                        hfinfo->name, hfinfo->abbrev,
                        val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"));
            break;
      }
}

static int
proto_register_field_init(header_field_info *hfinfo, const int parent)
{

      tmp_fld_check_assert(hfinfo);

      /* if this is a bitfield, compute bitshift */
      if (hfinfo->bitmask) {
            hfinfo->bitshift = wrs_count_bitshift(hfinfo->bitmask);
      }

      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=100000;
                  gpa_hfinfo.hfi=g_malloc(sizeof(header_field_info *)*100000);
            } 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_next_hfinfo;
            guchar c;

            /* Check that the filter name (abbreviation) is legal;
             * it must contain only alphanumerics, '-', "_", and ".". */
            c = wrs_check_charset(fld_abbrev_chars, hfinfo->abbrev);
            if (c) {
                  fprintf(stderr, "OOPS: '%c' in '%s'\n", c, hfinfo->abbrev);
                  DISSECTOR_ASSERT(!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 = NULL;

            g_tree_insert(gpa_name_tree, (gpointer) (hfinfo->abbrev), hfinfo);
            /* GLIB 2.x - if it is already present
             * the previous hfinfo with the same name is saved
             * to same_name_hfinfo by value destroy callback */
            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;
04563                   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;
            }
      }

      return hfinfo->id;
}

void
proto_register_subtree_array(gint *const *indices, const 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 (gboolean));
            memset(tree_is_expanded + num_tree_types, 0,
                   num_indices*sizeof (gboolean));
      }

      /*
       * 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++) {
04606             if (**ptr != -1) {
                  /* g_error will terminate the program */
                  g_error("register_subtree_array: subtree item type (ett_...) not -1 !"
                        " This is a development error:"
                        " Either the subtree item type has already been assigned or"
                        " was not initialized to -1.");
            }
            **ptr = num_tree_types;
      }
}

void
proto_item_fill_label(field_info *fi, gchar *label_str)
{
      header_field_info       *hfinfo;

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

      if (!fi) {
            if (label_str)
                  label_str[0]= '\0';
            /* XXX: Check validity of hfinfo->type */
            return;
      }

      hfinfo = fi->hfinfo;

      switch(hfinfo->type) {
            case FT_NONE:
            case FT_PROTOCOL:
                  g_strlcpy(label_str, hfinfo->name, ITEM_LABEL_LENGTH);
                  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 = g_snprintf(label_str, ITEM_LABEL_LENGTH,
                              "%s: %s", hfinfo->name,
                               bytes_to_str(bytes, fvalue_length(&fi->value)));
                        if (ret >= ITEM_LABEL_LENGTH) {
                              /* Uh oh, we don't have enough room.  Tell the
                               *    user that the field is truncated.
                               */
                              g_snprintf(label_str, ITEM_LABEL_LENGTH,
                                       "%s [truncated]: %s",
                                       hfinfo->name,
                                       bytes_to_str(bytes, fvalue_length(&fi->value)));
                        }
                  }
                  else {
                        g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s: <MISSING>", hfinfo->name);
                  }
                  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:
                  if (hfinfo->bitmask) {
                        fill_label_bitfield(fi, label_str);
                  } else {
                        fill_label_uint(fi, label_str);
                  }
                  break;

            case FT_FRAMENUM:
                  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:
                  DISSECTOR_ASSERT(!hfinfo->bitmask);
                  fill_label_int(fi, label_str);
                  break;

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

            case FT_FLOAT:
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           "%s: %." STRINGIFY(FLT_DIG) "f",
                           hfinfo->name, fvalue_get_floating(&fi->value));
                  break;

            case FT_DOUBLE:
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           "%s: %." STRINGIFY(DBL_DIG) "g",
                           hfinfo->name, fvalue_get_floating(&fi->value));
                  break;

            case FT_ABSOLUTE_TIME:
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           "%s: %s", hfinfo->name,
                           abs_time_to_str(fvalue_get(&fi->value), hfinfo->display, TRUE));
                  break;

            case FT_RELATIVE_TIME:
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           "%s: %s seconds", hfinfo->name,
                           rel_time_to_secs_str(fvalue_get(&fi->value)));
                  break;

            case FT_IPXNET:
                  integer = fvalue_get_uinteger(&fi->value);
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           "%s: %s (0x%08X)", hfinfo->name,
                           get_ipxnet_name(integer), integer);
                  break;

            case FT_ETHER:
                  bytes = fvalue_get(&fi->value);
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           "%s: %s (%s)", hfinfo->name,
                           get_ether_name(bytes),
                           ether_to_str(bytes));
                  break;

            case FT_IPv4:
                  ipv4 = fvalue_get(&fi->value);
                  n_addr = ipv4_get_net_order_addr(ipv4);
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           "%s: %s (%s)", hfinfo->name,
                           get_hostname(n_addr),
                           ip_to_str((guint8*)&n_addr));
                  break;

            case FT_IPv6:
                  bytes = fvalue_get(&fi->value);
                  g_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));
                  break;

            case FT_GUID:
                  guid = fvalue_get(&fi->value);
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           "%s: %s", hfinfo->name,
                            guid_to_str(guid));
                  break;

            case FT_OID:
                  bytes = fvalue_get(&fi->value);
                  name = oid_resolved_from_encoded(bytes, fvalue_length(&fi->value));
                  if (name) {
                        g_snprintf(label_str, ITEM_LABEL_LENGTH,
                              "%s: %s (%s)", hfinfo->name,
                               oid_encoded2string(bytes, fvalue_length(&fi->value)), name);
                  } else {
                        g_snprintf(label_str, ITEM_LABEL_LENGTH,
                              "%s: %s", hfinfo->name,
                               oid_encoded2string(bytes, fvalue_length(&fi->value)));
                  }
                  break;

            case FT_STRING:
            case FT_STRINGZ:
            case FT_EBCDIC:
            case FT_UINT_STRING:
                  bytes = fvalue_get(&fi->value);
                  ret = g_snprintf(label_str, ITEM_LABEL_LENGTH,
                               "%s: %s", hfinfo->name,
                               format_text(bytes, strlen(bytes)));
                  if (ret >= ITEM_LABEL_LENGTH) {
                        /* Uh oh, we don't have enough room.  Tell the
                         *    user that the field is truncated.
                         */
                        g_snprintf(label_str, ITEM_LABEL_LENGTH,
                                 "%s [truncated]: %s", hfinfo->name,
                                 format_text(bytes, strlen(bytes)));
                  }
                  break;

            default:
                  g_error("hfinfo->type %d (%s) not handled\n",
                        hfinfo->type, ftype_name(hfinfo->type));
                  DISSECTOR_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;

      header_field_info       *hfinfo = fi->hfinfo;
      const true_false_string       *tfstring = (const true_false_string *)&tfs_true_false;

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

      value = fvalue_get_uinteger(&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 = (int) (p - label_str);
      }

      /* Fill in the textual info */
      g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
               "%s: %s",  hfinfo->name,
               value ? tfstring->true_string : tfstring->false_string);
}

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

      header_field_info *hfinfo = fi->hfinfo;

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

      /* Un-shift bits */
      unshifted_value = fvalue_get_uinteger(&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 = (int) (p - label_str);

      /* Fill in the textual info using stored (shifted) value */
      if (hfinfo->display == BASE_CUSTOM) {
            gchar tmp[ITEM_LABEL_LENGTH];
            custom_fmt_func_t fmtfunc = (custom_fmt_func_t)hfinfo->strings;

            DISSECTOR_ASSERT(fmtfunc);
            fmtfunc(tmp, value);
            g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
                     "%s: %s", hfinfo->name, tmp);
      }
      else if (hfinfo->strings) {
            format = hfinfo_uint_vals_format(hfinfo);
            if (hfinfo->display & BASE_RANGE_STRING) {
                  g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
                           format,  hfinfo->name,
                           rval_to_str(value, hfinfo->strings, "Unknown"), value);
            } else if (hfinfo->display & BASE_EXT_STRING) {
                  g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
                         format,  hfinfo->name,
                         val_to_str_ext(value, (value_string_ext *) hfinfo->strings, "Unknown"), value);
            } else {
                  g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
                           format,  hfinfo->name,
                           val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value);
            }
      }
      else {
            format = hfinfo_uint_format(hfinfo);
            if (IS_BASE_DUAL(hfinfo->display)) {
                  g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
                           format,  hfinfo->name, value, value);
            } else {
                  g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
                           format,  hfinfo->name, value);
            }
      }
}

static void
fill_label_uint(field_info *fi, gchar *label_str)
{
      const char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint32 value;

      value = fvalue_get_uinteger(&fi->value);

      /* Fill in the textual info */
      if (hfinfo->display == BASE_CUSTOM) {
            gchar tmp[ITEM_LABEL_LENGTH];
            custom_fmt_func_t fmtfunc = (custom_fmt_func_t)hfinfo->strings;

            DISSECTOR_ASSERT(fmtfunc);
            fmtfunc(tmp, value);
            g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s", hfinfo->name, tmp);
      }
      else if (hfinfo->strings) {
            format = hfinfo_uint_vals_format(hfinfo);
            if (hfinfo->display & BASE_RANGE_STRING) {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           format,  hfinfo->name,
                           rval_to_str(value, hfinfo->strings, "Unknown"), value);
            } else if (hfinfo->display & BASE_EXT_STRING) {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                         format,  hfinfo->name,
                         val_to_str_ext(value, (value_string_ext *) hfinfo->strings, "Unknown"), value);
            } else {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           format,  hfinfo->name,
                           val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value);
            }
      }
      else {
            format = hfinfo_uint_format(hfinfo);
            if (IS_BASE_DUAL(hfinfo->display)) {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           format,  hfinfo->name, value, value);
            } else {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           format,  hfinfo->name, value);
            }
      }
}

static void
fill_label_uint64(field_info *fi, gchar *label_str)
{
      const char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint64 value;

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

      /* Fill in the textual info */
      if (IS_BASE_DUAL(hfinfo->display)) {
            g_snprintf(label_str, ITEM_LABEL_LENGTH,
                     format,  hfinfo->name, value, value);
      } else {
            g_snprintf(label_str, ITEM_LABEL_LENGTH,
                     format,  hfinfo->name, value);
      }
}

static void
fill_label_int(field_info *fi, gchar *label_str)
{
      const char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint32 value;

      value = fvalue_get_sinteger(&fi->value);

      /* Fill in the textual info */
      if (hfinfo->display == BASE_CUSTOM) {
            gchar tmp[ITEM_LABEL_LENGTH];
            custom_fmt_func_t fmtfunc = (custom_fmt_func_t)hfinfo->strings;

            DISSECTOR_ASSERT(fmtfunc);
            fmtfunc(tmp, value);
            g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s", hfinfo->name, tmp);
      }
      else if (hfinfo->strings) {
            format = hfinfo_int_vals_format(hfinfo);
            if (hfinfo->display & BASE_RANGE_STRING) {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           format,  hfinfo->name,
                           rval_to_str(value, hfinfo->strings, "Unknown"), value);
            } else if (hfinfo->display & BASE_EXT_STRING) {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                         format,  hfinfo->name,
                         val_to_str_ext(value, (value_string_ext *) hfinfo->strings, "Unknown"), value);
            } else {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           format,  hfinfo->name,
                           val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value);
            }
      }
      else {
            format = hfinfo_int_format(hfinfo);
            if (IS_BASE_DUAL(hfinfo->display)) {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           format,  hfinfo->name, value, value);
            } else {
                  g_snprintf(label_str, ITEM_LABEL_LENGTH,
                           format,  hfinfo->name, value);
            }
      }
}

static void
fill_label_int64(field_info *fi, gchar *label_str)
{
      const char *format = NULL;
      header_field_info *hfinfo = fi->hfinfo;
      guint64 value;

      /* Pick the proper format string */
      format = hfinfo_int64_format(hfinfo);
      value = fvalue_get_integer64(&fi->value);
05035 
      /* Fill in the textual info */
      if (IS_BASE_DUAL(hfinfo->display)) {
            g_snprintf(label_str, ITEM_LABEL_LENGTH,
                     format,  hfinfo->name, value, value);
      } else {
            g_snprintf(label_str, ITEM_LABEL_LENGTH,
                     format,  hfinfo->name, value);
      }
}

int
hfinfo_bitwidth(const 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:
                  DISSECTOR_ASSERT_NOT_REACHED();
                  ;
      }
      return bitwidth;
}

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

      /* Get the underlying BASE_ value */
      switch(hfinfo->display & BASE_DISPLAY_E_MASK) {
            case BASE_NONE:
                  format = "%s: %s";
                  break;
            case BASE_DEC:
            case BASE_DEC_HEX:
                  format = "%s: %s (%u)";
                  break;
            case BASE_OCT: /* I'm lazy */
                  format = "%s: %s (%#o)";
                  break;
            case BASE_HEX:
            case BASE_HEX_DEC:
                  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:
                              DISSECTOR_ASSERT_NOT_REACHED();
                              ;
                  }
                  break;
            default:
                  DISSECTOR_ASSERT_NOT_REACHED();
                  ;
      }
      return format;
}

static const char *
hfinfo_uint_format(const header_field_info *hfinfo)
{
      const 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_DEC_HEX:
                        switch(hfinfo->type) {
                              case FT_UINT8:
                                    format = "%s: %u (0x%02x)";
                                    break;
                              case FT_UINT16:
                                    format = "%s: %u (0x%04x)";
                                    break;
                              case FT_UINT24:
                                    format = "%s: %u (0x%06x)";
                                    break;
                              case FT_UINT32:
                                    format = "%s: %u (0x%08x)";
                                    break;
                              default:
                                    DISSECTOR_ASSERT_NOT_REACHED();
                                    ;
                        }
                        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:
                                    DISSECTOR_ASSERT_NOT_REACHED();
                                    ;
                        }
                        break;
                  case BASE_HEX_DEC:
                        switch(hfinfo->type) {
                              case FT_UINT8:
                                    format = "%s: 0x%02x (%u)";
                                    break;
                              case FT_UINT16:
                                    format = "%s: 0x%04x (%u)";
                                    break;
                              case FT_UINT24:
                                    format = "%s: 0x%06x (%u)";
                                    break;
                              case FT_UINT32:
                                    format = "%s: 0x%08x (%u)";
                                    break;
                              default:
                                    DISSECTOR_ASSERT_NOT_REACHED();
                                    ;
                        }
                        break;
                  default:
                        DISSECTOR_ASSERT_NOT_REACHED();
                        ;
            }
      }
      return format;
}

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

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

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

      /* Get the underlying BASE_ value */
      switch(hfinfo->display & BASE_DISPLAY_E_MASK) {
            case BASE_NONE:
                  format = "%s: %s";
                  break;
            case BASE_DEC:
            case BASE_DEC_HEX:
                  format = "%s: %s (%d)";
                  break;
            case BASE_OCT: /* I'm lazy */
                  format = "%s: %s (%#o)";
                  break;
            case BASE_HEX:
            case BASE_HEX_DEC:
                  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:
                              DISSECTOR_ASSERT_NOT_REACHED();
                              ;
                  }
                  break;
            default:
                  DISSECTOR_ASSERT_NOT_REACHED();
                  ;
      }
      return format;
}

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

      /* Pick the proper format string */
      switch(hfinfo->display) {
            case BASE_DEC:
                  format = "%s: %" G_GINT64_MODIFIER "u";
                  break;
            case BASE_DEC_HEX:
                  format = "%s: %" G_GINT64_MODIFIER "u (%" G_GINT64_MODIFIER "x)";
                  break;
            case BASE_OCT: /* I'm lazy */
                  format = "%s: %#" G_GINT64_MODIFIER "o";
                  break;
            case BASE_HEX:
                  format = "%s: 0x%016" G_GINT64_MODIFIER "x";
                  break;
            case BASE_HEX_DEC:
                  format = "%s: 0x%016" G_GINT64_MODIFIER "x (%" G_GINT64_MODIFIER "u)";
                  break;
            default:
                  DISSECTOR_ASSERT_NOT_REACHED();
                  ;
      }
      return format;
}

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

      /* Pick the proper format string */
      switch(hfinfo->display) {
            case BASE_DEC:
                  format = "%s: %d";
                  break;
            case BASE_DEC_HEX:
                  switch(hfinfo->type) {
                        case FT_INT8:
                              format = "%s: %d (0x%02x)";
                              break;
                        case FT_INT16:
                              format = "%s: %d (0x%04x)";
                              break;
                        case FT_INT24:
                              format = "%s: %d (0x%06x)";
                              break;
                        case FT_INT32:
                              format = "%s: %d (0x%08x)";
                              break;
                        default:
                              DISSECTOR_ASSERT_NOT_REACHED();
                              ;
                  }
                  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:
                              DISSECTOR_ASSERT_NOT_REACHED();
                              ;
                  }
                  break;
            case BASE_HEX_DEC:
                  switch(hfinfo->type) {
                        case FT_INT8:
                              format = "%s: 0x%02x (%d)";
                              break;
                        case FT_INT16:
                              format = "%s: 0x%04x (%d)";
                              break;
                        case FT_INT24:
                              format = "%s: 0x%06x (%d)";
                              break;
                        case FT_INT32:
                              format = "%s: 0x%08x (%d)";
                              break;
                        default:
                              DISSECTOR_ASSERT_NOT_REACHED();
                              ;
                  }
                  break;
            default:
                  DISSECTOR_ASSERT_NOT_REACHED();
                  ;
      }
      return format;
}

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

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

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

      /* Pick the proper format string */
      switch(hfinfo->display) {
            case BASE_DEC:
                  format = "%s: %" G_GINT64_MODIFIER "d";
                  break;
            case BASE_DEC_HEX:
                  format = "%s: %" G_GINT64_MODIFIER "d (%" G_GINT64_MODIFIER "x)";
                  break;
            case BASE_OCT: /* I'm lazy */
                  format = "%s: %#" G_GINT64_MODIFIER "o";
                  break;
            case BASE_HEX:
                  format = "%s: 0x%016" G_GINT64_MODIFIER "x";
05539                   break;
            case BASE_HEX_DEC:
                  format = "%s: 0x%016" G_GINT64_MODIFIER "x (%" G_GINT64_MODIFIER "d)";
                  break;
            default:
                  DISSECTOR_ASSERT_NOT_REACHED();
05545                   ;
      }
      return format;
}

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

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

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

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

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

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

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

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

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

gboolean
proto_registrar_is_protocol(const int n)
05594 {
      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. */
05605 gint
proto_registrar_get_length(const 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
proto_check_for_protocol_or_field(const proto_tree* tree, const 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;
05626       }
      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(const proto_tree *tree, const int id)
05639 {
      if (!tree)
            return NULL;

      if (PTREE_DATA(tree)->interesting_hfids != NULL)
            return g_hash_table_lookup(PTREE_DATA(tree)->interesting_hfids,
                                 GINT_TO_POINTER(id));
      else
            return NULL;
}

gboolean
proto_tracking_interesting_fields(const proto_tree *tree)
{
      if (!tree)
            return FALSE;

      return (PTREE_DATA(tree)->interesting_hfids != NULL);
}

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

/* Helper function for proto_find_info() */
static gboolean
find_finfo(proto_node *node, gpointer data)
{
      field_info *fi = PNODE_FINFO(node);
      if (fi && fi->hfinfo) {
            if (fi->hfinfo->id == ((ffdata_t*)data)->id) {
                  g_ptr_array_add(((ffdata_t*)data)->array, fi);
            }
      }
05675 
      /* 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>, TRUE).
*/
GPtrArray *
proto_find_finfo(proto_tree *tree, const 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;
}

/* Helper function for proto_all_finfos() */
static gboolean
every_finfo(proto_node *node, gpointer data)
05702 {
      field_info *fi = PNODE_FINFO(node);
      if (fi && fi->hfinfo) {
            g_ptr_array_add(((ffdata_t*)data)->array, fi);
      }

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

/* Return GPtrArray* of field_info pointers containing all hfindexes that appear in a tree. */
GPtrArray *
proto_all_finfos(proto_tree *tree)
{
      ffdata_t    ffdata;

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

      proto_tree_traverse_pre_order(tree, every_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, const gpointer data)
{
      field_info              *fi = PNODE_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 */
            }
      }
05748       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 *
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;
}
05772 
/* 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
proto_registrar_dump_protocols(void)
{
      protocol_t        *protocol;
      int               i;
      void              *cookie = NULL;

      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_strings, range_strings or true/false strings for fields
 * that have them. There is one record per line. Fields are tab-delimited.
 * There are three types of records: Value String, Range String
 * and True/False String. The first field, 'V', 'R' 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
 *
 * Range Strings
 * -------------
 * Field 1 = 'R'
 * Field 2 = field abbreviation to which this range string corresponds
05815  * Field 3 = Integer value: lower bound
 * Field 4 = Integer value: upper bound
 * Field 5 = 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
proto_registrar_dump_values(void)
{
      header_field_info *hfinfo, *parent_hfinfo;
      int               i, len, vi;
      const value_string      *vals;
      const range_string      *range;
      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;
                  range = NULL;
                  tfs   = NULL;

                  if ((hfinfo->display & BASE_DISPLAY_E_MASK) != BASE_CUSTOM &&
                        (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)) {

                        if ((hfinfo->display & BASE_EXT_STRING)) {
                              vals = ((value_string_ext *) hfinfo->strings)->vals;
                        } if ((hfinfo->display & BASE_RANGE_STRING) == 0) {
                              vals = hfinfo->strings;
                        } else {
                              range = 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 range strings? */
                  else if (range) {
                        vi = 0;
                        while (range[vi].strptr) {
                              /* Print in the proper base */
                              if ((hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_HEX) {
                                    printf("R\t%s\t0x%x\t0x%x\t%s\n",
                                           hfinfo->abbrev,
                                           range[vi].value_min,
                                           range[vi].value_max,
                                           range[vi].strptr);
                              }
                              else {
                                    printf("R\t%s\t%u\t%u\t%s\n",
                                           hfinfo->abbrev,
                                           range[vi].value_min,
                                           range[vi].value_max,
                                           range[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 independent
 * 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
 * Field 6 = blurb describing field
 *
 * (format 2)
 * 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
 * Field 6 = blurb describing field
 * Field 7 = base for display (for integer types); "parent bitfield width" for FT_BOOLEAN
 * Field 8 = blurb describing field (yes, apparently we repeated this accidentally)
05979  *
 * (format 3)
 * 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
 * Field 6 = blurb describing field
 * Field 7 = base for display (for integer types); "parent bitfield width" for FT_BOOLEAN
 * Field 8 = bitmask: format: hex: 0x....
 */
void
proto_registrar_dump_fields(const int format)
{
      header_field_info *hfinfo, *parent_hfinfo;
      int               i, len;
      const char        *enum_name;
      const char        *base_name;
      const char        *blurb;
      char              width[5];

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

            /*
             * Skip the pseudo-field for "proto_tree_add_text()" since
             * we don't want it in the list of filterable fields.
             */
            if (hfinfo->id == hf_text_only)
                  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 & BASE_DISPLAY_E_MASK) {
                                    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;
                                    case BASE_DEC_HEX:
                                          base_name = "BASE_DEC_HEX";
                                          break;
                                    case BASE_HEX_DEC:
                                          base_name = "BASE_HEX_DEC";
                                          break;
                                    case BASE_CUSTOM:
                                          base_name = "BASE_CUSTOM";
                                          break;
                                    default:
                                          base_name = "????";
                                          break;
                              }
                        } else if (hfinfo->type == FT_BOOLEAN) {
                              /* For FT_BOOLEAN: 'display' can be "parent bitfield width" */
                              g_snprintf(width, sizeof(width), "%d", hfinfo->display);
                              base_name = width;
                        }
                  }

                  blurb = hfinfo->blurb;
                  if (blurb == NULL)
                        blurb = "";
                  else if (strlen(blurb) == 0)
                        blurb = "\"\"";
                  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, 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, blurb,
                              base_name, blurb);
                  }
                  else if (format == 3) {
                        printf("F\t%s\t%s\t%s\t%s\t%s\t%s\t0x%x\n",
                              hfinfo->name, hfinfo->abbrev, enum_name,
                              parent_hfinfo->abbrev, blurb,
                              base_name, hfinfo->bitmask);
                  }
                  else {
                        g_assert_not_reached();
                  }
            }
      }
}

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

      /* Pick the proper format string */
      if (hfinfo->type == FT_FRAMENUM) {
            /*
             * Frame numbers are always displayed in decimal.
             */
            format = "%s == %u";
      } else {
            /* Get the underlying BASE_ value */
            switch(hfinfo->display & BASE_DISPLAY_E_MASK) {
                  case BASE_DEC:
                  case BASE_DEC_HEX:
                  case BASE_OCT: /* I'm lazy */
                  case BASE_CUSTOM:
                        switch(hfinfo->type) {
                              case FT_UINT8:
                              case FT_UINT16:
                              case FT_UINT24:
                              case FT_UINT32:
                                    format = "%s == %u";
                                    break;
                              case FT_UINT64:
                                    format = "%s == %" G_GINT64_MODIFIER "u";
                                    break;
                              case FT_INT8:
                              case FT_INT16:
                              case FT_INT24:
                              case FT_INT32:
                                    format = "%s == %d";
                                    break;
                              case FT_INT64:
                                    format = "%s == %" G_GINT64_MODIFIER "d";
                                    break;
                              default:
                                    DISSECTOR_ASSERT_NOT_REACHED();
                                    ;
                        }
                        break;
                  case BASE_HEX:
                  case BASE_HEX_DEC:
                        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" G_GINT64_MODIFIER "x";
                                    break;
                              default:
                                    DISSECTOR_ASSERT_NOT_REACHED();
                                    ;
                        }
                        break;
                  default:
                        DISSECTOR_ASSERT_NOT_REACHED();
                        ;
            }
      }
      return format;
}

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

      /* Pick the proper format string */
      if (hfinfo->type == FT_FRAMENUM) {
            /*
             * Frame numbers are always displayed in decimal.
             */
            format = "%u";
      } else {
            /* Get the underlying BASE_ value */
            switch(hfinfo->display & BASE_DISPLAY_E_MASK) {
                  case BASE_DEC:
                  case BASE_DEC_HEX:
                  case BASE_OCT: /* I'm lazy */
                  case BASE_CUSTOM:
                        switch(hfinfo->type) {
                              case FT_UINT8:
                              case FT_UINT16:
                              case FT_UINT24:
                              case FT_UINT32:
                                    format = "%u";
                                    break;
                              case FT_UINT64:
                                    format = "%" G_GINT64_MODIFIER "u";
                                    break;
                              case FT_INT8:
                              case FT_INT16:
                              case FT_INT24:
                              case FT_INT32:
                                    format = "%d";
                                    break;
                              case FT_INT64:
                                    format = "%" G_GINT64_MODIFIER "d";
                                    break;
                              default:
                                    DISSECTOR_ASSERT_NOT_REACHED();
                                    ;
                        }
                        break;
                  case BASE_HEX:
                  case BASE_HEX_DEC:
                        switch(hfinfo->type) {
                              case FT_UINT8:
                                    format = "0x%02x";
                                    break;
                              case FT_UINT16:
                                    format = "0x%04x";
                                    break;
                              case FT_UINT24:
                                    format = "0x%06x";
                                    break;
                              case FT_UINT32:
                                    format = "0x%08x";
                                    break;
                              case FT_UINT64:
                                    format = "0x%016" G_GINT64_MODIFIER "x";
                                    break;
                              default:
                                    DISSECTOR_ASSERT_NOT_REACHED();
                                    ;
                        }
                        break;
                  default:
                        DISSECTOR_ASSERT_NOT_REACHED();
                        ;
            }
      }
      return format;
}

/* This function indicates whether it's possible to construct a
 * "match selected" display filter string for the specified field,
 * returns an indication of whether it's possible, and, if it's
 * possible and "filter" is non-null, constructs the filter and
 * sets "*filter" to point to it.
 * You do not need to [g_]free() this string since it will be automatically
 * freed once the next packet is dissected.
 */
static gboolean
construct_match_selected_string(field_info *finfo, epan_dissect_t *edt,
                        char **filter)
{
      header_field_info *hfinfo;
      int               abbrev_len;
      char              *ptr;
      int               buf_len;
      const char        *format;
      int               dfilter_len, i;
      gint              start, length, length_remaining;
      guint8                  c;
      gchar             is_signed_num = FALSE;

      hfinfo = finfo->hfinfo;
      DISSECTOR_ASSERT(hfinfo);
      abbrev_len = (int) strlen(hfinfo->abbrev);

      if (hfinfo->strings && (hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_NONE) {
            const gchar *str = NULL;

            switch(hfinfo->type) {

            case FT_INT8:
            case FT_INT16:
            case FT_INT24:
            case FT_INT32:
                  if (hfinfo->display & BASE_RANGE_STRING) {
                        str = match_strrval(fvalue_get_sinteger(&finfo->value), hfinfo->strings);
                  } else if (hfinfo->display & BASE_EXT_STRING) {
                        str = match_strval_ext(fvalue_get_sinteger(&finfo->value), hfinfo->strings);
                  } else {
                        str = match_strval(fvalue_get_sinteger(&finfo->value), hfinfo->strings);
                  }
                  break;

            case FT_UINT8:
            case FT_UINT16:
            case FT_UINT24:
            case FT_UINT32:
                  if (hfinfo->display & BASE_RANGE_STRING) {
                        str = match_strrval(fvalue_get_uinteger(&finfo->value), hfinfo->strings);
                  } else if (hfinfo->display & BASE_EXT_STRING) {
                        str = match_strval_ext(fvalue_get_uinteger(&finfo->value), hfinfo->strings);
                  } else {
                        str = match_strval(fvalue_get_uinteger(&finfo->value), hfinfo->strings);
                  }
                  break;

            default:
                  break;
            }

            if (str != NULL && filter != NULL) {
                  *filter = ep_strdup_printf("%s == \"%s\"", hfinfo->abbrev, str);
                  return TRUE;
            }
      }

      /*
       * XXX - we can't use the "val_to_string_repr" and "string_repr_len"
       * functions for FT_UINT and FT_INT types, 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 them 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_INT8:
            case FT_INT16:
            case FT_INT24:
            case FT_INT32:
                  is_signed_num = TRUE;
            case FT_UINT8:
            case FT_UINT16:
            case FT_UINT24:
            case FT_UINT32:
                  if (filter != NULL) {
                        format = hfinfo_numeric_format(hfinfo);
                        if(is_signed_num) {
                              *filter = ep_strdup_printf(format,
                                       hfinfo->abbrev,
                                       fvalue_get_sinteger(&finfo->value));
                        } else {
                              *filter = ep_strdup_printf(format,
                                       hfinfo->abbrev,
                                             fvalue_get_uinteger(&finfo->value));
                        }
                  }
                  break;

            case FT_FRAMENUM:
                  DISSECTOR_ASSERT(!is_signed_num);
                  if (filter != NULL) {
                        format = hfinfo_numeric_format(hfinfo);
                        *filter = ep_strdup_printf(format,
                                 hfinfo->abbrev,
                                       fvalue_get_uinteger(&finfo->value));
                  }
                  break;

            case FT_INT64:
            case FT_UINT64:
                  if (filter != NULL) {
                        format = hfinfo_numeric_format(hfinfo);
                        *filter = ep_strdup_printf(format,
                              hfinfo->abbrev,
                              fvalue_get_integer64(&finfo->value));
                  }
                  break;

            case FT_PROTOCOL:
                  if (filter != NULL)
                        *filter = ep_strdup(finfo->hfinfo->abbrev);
                  break;

            case FT_NONE:
                  /*
                   * If the length is 0, just match the name of the
                   * field.
                   *
                   * (Also check for negative values, just in case,
                   * as we'll cast it to an unsigned value later.)
                   */
                  length = finfo->length;
                  if (length == 0) {
                        if (filter != NULL)
                              *filter = ep_strdup(finfo->hfinfo->abbrev);
                        break;
                  }
                  if (length < 0)
                        return FALSE;

                  /*
                   * 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;     /* you lose */

                  /*
                   * 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 FALSE;

                  if (filter != NULL) {
                        start = finfo->start;
                        buf_len = 32 + length * 3;
                        *filter = ep_alloc0(buf_len);
                        ptr = *filter;

                        ptr += g_snprintf(ptr, (gulong) (buf_len-(ptr-*filter)),
                              "frame[%d:%d] == ", finfo->start, length);
                        for (i=0;i<length; i++) {
                              c = tvb_get_guint8(finfo->ds_tvb, start);
                              start++;
                              if (i == 0 ) {
                                    ptr += g_snprintf(ptr, (gulong) (buf_len-(ptr-*filter)), "%02x", c);
                              }
                              else {
                                    ptr += g_snprintf(ptr, (gulong) (buf_len-(ptr-*filter)), ":%02x", c);
                              }
                        }
                  }
                  break;

            case FT_PCRE:
                  /* FT_PCRE never appears as a type for a registered field. It is
                   * only used internally. */
                  DISSECTOR_ASSERT_NOT_REACHED();
                  break;

            /* By default, use the fvalue's "to_string_repr" method. */
            default:
                  /* Figure out the string length needed.
                   *    The ft_repr length.
                   *    4 bytes for " == ".
                   *    1 byte for trailing NUL.
                   */
                  if (filter != NULL) {
                        dfilter_len = fvalue_string_repr_len(&finfo->value,
                                    FTREPR_DFILTER);
                        dfilter_len += abbrev_len + 4 + 1;
                        *filter = ep_alloc0(dfilter_len);

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

      return TRUE;
}

/*
 * Returns TRUE if we can do a "match selected" on the field, FALSE
 * otherwise.
 */
gboolean
proto_can_match_selected(field_info *finfo, epan_dissect_t *edt)
{
06505       return construct_match_selected_string(finfo, edt, NULL);
}

/* This function attempts to construct a "match selected" display filter
 * string for the specified field; if it can do so, it returns a pointer
 * to the string, otherwise it returns NULL.
 *
 * The string is allocated with packet lifetime scope.
 * You do not need to [g_]free() this string since it will be automatically
 * freed once the next packet is dissected.
 */
char *
proto_construct_match_selected_string(field_info *finfo, epan_dissect_t *edt)
{
      char *filter;

      if (!construct_match_selected_string(finfo, edt, &filter))
            return NULL;
      return filter;
}

/* This function is common code for both proto_tree_add_bitmask() and
 *    proto_tree_add_bitmask_text() functions.
 */
static gboolean
proto_item_add_bitmask_tree(proto_item *item, tvbuff_t *tvb, const int offset,
                      const int len, const gint ett, const int **fields,
                      const gboolean little_endian, const int flags,
                      gboolean first)
{
      guint32 value = 0, tmpval;
      proto_tree *tree = NULL;
      header_field_info *hf;
      const char *fmt;

      switch (len) {
      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();
      }

      tree = proto_item_add_subtree(item, ett);
      while (*fields) {
            proto_tree_add_item(tree, **fields, tvb, offset, len, little_endian);
            if (flags & BMT_NO_APPEND) {
                  fields++;
                  continue;
            }
            hf = proto_registrar_get_nth(**fields);
            DISSECTOR_ASSERT(hf->bitmask != 0);
            tmpval = (value & hf->bitmask) >> hf->bitshift;

            switch (hf->type) {
            case FT_INT8:
            case FT_UINT8:
            case FT_INT16:
            case FT_UINT16:
            case FT_INT24:
            case FT_UINT24:
            case FT_INT32:
            case FT_UINT32:
                  DISSECTOR_ASSERT(len == ftype_length(hf->type));

                  if (hf->display == BASE_CUSTOM) {
                        gchar lbl[ITEM_LABEL_LENGTH];
                        custom_fmt_func_t fmtfunc = (custom_fmt_func_t)hf->strings;

                        DISSECTOR_ASSERT(fmtfunc);
                        fmtfunc(lbl, tmpval);
                        proto_item_append_text(item, "%s%s: %s", first ? "" : ", ",
                                    hf->name, lbl);
                        first = FALSE;
                  }
                  else if (hf->strings) {
                        if (hf->display & BASE_RANGE_STRING) {
                              proto_item_append_text(item, "%s%s: %s", first ? "" : ", ",
                                                 hf->name, rval_to_str(tmpval, hf->strings, "Unknown"));
                        } else if (hf->display & BASE_EXT_STRING) {
                              proto_item_append_text(item, "%s%s: %s", first ? "" : ", ",
                                                 hf->name, val_to_str_ext(tmpval, (value_string_ext *) (hf->strings), "Unknown"));
                        } else {
                              proto_item_append_text(item, "%s%s: %s", first ? "" : ", ",
                                                 hf->name, val_to_str(tmpval, cVALS(hf->strings), "Unknown"));
                        }
                        first = FALSE;
                  }
                  else if (!(flags & BMT_NO_INT)) {
                        if (!first) {
                              proto_item_append_text(item, ", ");
                        }

                        fmt = IS_FT_INT(hf->type) ? hfinfo_int_format(hf) : hfinfo_uint_format(hf);
                        if (IS_BASE_DUAL(hf->display)) {
                              proto_item_append_text(item, fmt, hf->name, tmpval, tmpval);
                        } else {
                              proto_item_append_text(item, fmt, hf->name, tmpval);
                        }
                        first = FALSE;
                  }

                  break;
            case FT_BOOLEAN:
                  DISSECTOR_ASSERT(len * 8 == hf->display);

                  if (hf->strings && !(flags & BMT_NO_TFS)) {
                        /* If we have true/false strings, emit full - otherwise messages
                           might look weird */
                        const struct true_false_string *tfs =
                              (const struct true_false_string *)hf->strings;

                        if (tmpval) {
                              proto_item_append_text(item, "%s%s: %s", first ? "" : ", ",
                                          hf->name, tfs->true_string);
                              first = FALSE;
                        } else if (!(flags & BMT_NO_FALSE)) {
                              proto_item_append_text(item, "%s%s: %s", first ? "" : ", ",
                                          hf->name, tfs->false_string);
                              first = FALSE;
                        }
                  } else if (hf->bitmask & value) {
                        /* If the flag is set, show the name */
                        proto_item_append_text(item, "%s%s", first ? "" : ", ", hf->name);
                        first = FALSE;
                  }
                  break;
            default:
                  g_assert_not_reached();
            }

            fields++;
      }

      return first;
}

/* This function will dissect a sequence of bytes that describe a
 * bitmask.
 * hf_hdr is a 8/16/24/32 bit integer that describes the bitmask to be dissected.
 * This field will form an expansion under which the individual fields of the
 * bitmask is dissected and displayed.
06659  * This field must be of the type FT_[U]INT{8|16|24|32}.
 *
 * fields is an array of pointers to int that lists all the fields of the
 * bitmask. These fields can be either of the type FT_BOOLEAN for flags
 * or another integer of the same type/size as hf_hdr with a mask specified.
 * This array is terminated by a NULL entry.
 *
 * FT_BOOLEAN bits that are set to 1 will have the name added to the expansion.
 * FT_integer fields that have a value_string attached will have the
 * matched string displayed on the expansion line.
 */
proto_item *
proto_tree_add_bitmask(proto_tree *parent_tree, tvbuff_t *tvb,
                   const guint offset, const int hf_hdr,
                   const gint ett, const int **fields,
                   const gboolean little_endian)
{
      proto_item *item = NULL;
      header_field_info *hf;
      int len;

      hf = proto_registrar_get_nth(hf_hdr);
      DISSECTOR_ASSERT(IS_FT_INT(hf->type) || IS_FT_UINT(hf->type));
      len = ftype_length(hf->type);
06683 
      if (parent_tree) {
            item = proto_tree_add_item(parent_tree, hf_hdr, tvb, offset, len, little_endian);
            proto_item_add_bitmask_tree(item, tvb, offset, len, ett, fields, little_endian,
                                  BMT_NO_INT|BMT_NO_TFS, FALSE);
      }

      return item;
}

/* The same as proto_tree_add_bitmask(), but using an arbitrary text as a top-level item */
proto_item *
proto_tree_add_bitmask_text(proto_tree *parent_tree, tvbuff_t *tvb,
                      const guint offset, const guint len,
                      const char *name, const char *fallback,
                      const gint ett, const int **fields,
                      const gboolean little_endian, const int flags)
{
      proto_item *item = NULL;

      if (parent_tree) {
06704             item = proto_tree_add_text(parent_tree, tvb, offset, len, "%s", name ? name : "");
            if (proto_item_add_bitmask_tree(item, tvb, offset, len, ett, fields, little_endian,
                              flags, TRUE) && fallback) {
                  /* Still at first item - append 'fallback' text if any */
                  proto_item_append_text(item, "%s", fallback);
            }
      }

      return item;
}

proto_item *
proto_tree_add_bits_item(proto_tree *tree, const int hf_index, tvbuff_t *tvb,
                   const gint bit_offset, const gint no_of_bits,
                   const gboolean little_endian)
{
      header_field_info *hfinfo;

      TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hfinfo);

      return proto_tree_add_bits_ret_val(tree, hf_index, tvb, bit_offset, no_of_bits, NULL, little_endian);
}

/*
 * This function will dissect a sequence of bits that does not need to be byte aligned; the bits
 * set will be shown in the tree as ..10 10.. and the integer value returned if return_value is set.
 * Offset should be given in bits from the start of the tvb.
 */

static proto_item *
_proto_tree_add_bits_ret_val(proto_tree *tree, const int hf_index, tvbuff_t *tvb,
                      const gint bit_offset, const gint no_of_bits,
                      guint64 *return_value, const gboolean little_endian)
{
      const char *format = NULL;
      gint  offset;
      guint length;
      guint8      tot_no_bits;
      char *str;
      header_field_info *hf_field;
      guint64 value = 0;
      const true_false_string *tfstring;

      /* We can't fake it just yet. We have to fill in the 'return_value' parameter */
      PROTO_REGISTRAR_GET_NTH(hf_index, hf_field);

      if(hf_field -> bitmask != 0) {
            REPORT_DISSECTOR_BUG(ep_strdup_printf("Incompatible use of proto_tree_add_bits_ret_val with field '%s' (%s) with bitmask != 0",
                             hf_field->abbrev, hf_field->name));
      }

      DISSECTOR_ASSERT(bit_offset >= 0);
      DISSECTOR_ASSERT(no_of_bits > 0);

      /* Byte align offset */
      offset = bit_offset>>3;

      /*
       * Calculate the number of octets used to hold the bits
       */
      tot_no_bits = ((bit_offset&0x7)+no_of_bits);
      length = tot_no_bits>>3;
      /* If we are using part of the next octet, increase length by 1 */
      if (tot_no_bits & 0x07)
            length++;

      if (no_of_bits < 9){
            value = tvb_get_bits8(tvb, bit_offset, no_of_bits);
      }else if(no_of_bits < 17){
            value = tvb_get_bits16(tvb, bit_offset, no_of_bits, little_endian);
      }else if(no_of_bits < 33){
            value = tvb_get_bits32(tvb, bit_offset, no_of_bits, little_endian);
      }else if(no_of_bits < 65){
            value = tvb_get_bits64(tvb, bit_offset, no_of_bits, little_endian);
      }else{
            DISSECTOR_ASSERT_NOT_REACHED();
            return NULL;
      }

      if(return_value){
            *return_value=value;
      }

      /* Coast clear. Try and fake it */
      TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field);

      str = decode_bits_in_field(bit_offset, no_of_bits, value);

      strcat(str," = ");
      strcat(str,hf_field->name);

      switch(hf_field->type){
      case FT_BOOLEAN:
            /* Boolean field */
            tfstring = (const true_false_string *) &tfs_true_false;
            if (hf_field->strings)
                  tfstring = (const true_false_string *) hf_field->strings;
            return proto_tree_add_boolean_format(tree, hf_index, tvb, offset, length, (guint32)value,
                  "%s: %s",
                  str,
                  (guint32)value ? tfstring->true_string : tfstring->false_string);
            break;

      case FT_UINT8:
      case FT_UINT16:
      case FT_UINT24:
      case FT_UINT32:
            /* 1 - 32 bits field */
            if (hf_field->strings) {
                  return proto_tree_add_uint_format(tree, hf_index, tvb, offset, length, (guint32)value,
                        "%s: %s (%u)",
                        str,  (hf_field->display & BASE_RANGE_STRING) ?
                              rval_to_str((guint32)value, hf_field->strings, "Unknown ") :
                              (hf_field->display & BASE_EXT_STRING) ?
                              val_to_str_ext((guint32)value, (value_string_ext *) (hf_field->strings), "Unknown ") :
                              val_to_str((guint32)value, cVALS(hf_field->strings), "Unknown "),
                        (guint32)value);
                  break;
            }
            /* Pick the proper format string */
            format = hfinfo_uint_format(hf_field);
            if (IS_BASE_DUAL(hf_field->display)) {
                  return proto_tree_add_uint_format(tree, hf_index, tvb, offset, length, (guint32)value,
                        format, str, (guint32)value, (guint32)value);
            } else {
                  return proto_tree_add_uint_format(tree, hf_index, tvb, offset, length, (guint32)value,
                        format, str, (guint32)value);
            }
            break;

      case FT_UINT64:
            /* Pick the proper format string */
            format = hfinfo_uint64_format(hf_field);
            if (IS_BASE_DUAL(hf_field->display)) {
                  return proto_tree_add_uint64_format(tree, hf_index, tvb, offset, length, value,
                        format, str, value, value);
            } else {
                  return proto_tree_add_uint64_format(tree, hf_index, tvb, offset, length, value,
06842                         format, str, value);
            }
            break;

      default:
            DISSECTOR_ASSERT_NOT_REACHED();
            return NULL;
            break;
      }
}

proto_item *
proto_tree_add_bits_ret_val(proto_tree *tree, const int hf_index, tvbuff_t *tvb,
                      const gint bit_offset, const gint no_of_bits,
                      guint64 *return_value, const gboolean little_endian)
{
      proto_item *item;

      if ((item = _proto_tree_add_bits_ret_val(tree, hf_index, tvb, bit_offset, no_of_bits, return_value, little_endian))) {
            FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_OFFSET(bit_offset));
            FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_SIZE(no_of_bits));
      }
      return item;
}

static proto_item *
_proto_tree_add_bits_format_value(proto_tree *tree, const int hf_index,
                         tvbuff_t *tvb, const gint bit_offset,
                         const gint no_of_bits, void *value_ptr,
                         gchar *value_str)
{
      gint  offset;
      guint length;
      guint8      tot_no_bits;
      char *str;
      header_field_info *hf_field;
      guint64 value = 0;

      /* We do not have to return a value, try to fake it as soon as possible */
      TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field);

      if(hf_field -> bitmask != 0) {
            REPORT_DISSECTOR_BUG(ep_strdup_printf("Incompatible use of proto_tree_add_bits_ret_val with field '%s' (%s) with bitmask != 0",
                             hf_field->abbrev, hf_field->name));
      }

      DISSECTOR_ASSERT(bit_offset >= 0);
      DISSECTOR_ASSERT(no_of_bits > 0);

      /* Byte align offset */
      offset = bit_offset>>3;

      /*
       * Calculate the number of octets used to hold the bits
       */
      tot_no_bits = ((bit_offset&0x7)+no_of_bits);
      length = tot_no_bits>>3;
      /* If we are using part of the next octet, increase length by 1 */
      if (tot_no_bits & 0x07)
            length++;

      if (no_of_bits < 9){
            value = tvb_get_bits8(tvb, bit_offset, no_of_bits);
      }else if(no_of_bits < 17){
            value = tvb_get_bits16(tvb, bit_offset, no_of_bits, FALSE);
      }else if(no_of_bits < 33){
            value = tvb_get_bits32(tvb, bit_offset, no_of_bits, FALSE);
      }else if(no_of_bits < 65){
            value = tvb_get_bits64(tvb, bit_offset, no_of_bits, FALSE);
      }else{
            DISSECTOR_ASSERT_NOT_REACHED();
            return NULL;
      }

      str = decode_bits_in_field(bit_offset, no_of_bits, value);

      strcat(str," = ");
      strcat(str,hf_field->name);

      /*
       * This function does not receive an actual value but a dimensionless pointer to that value.
       * For this reason, the type of the header field is examined in order to determine
       * what kind of value we should read from this address.
       * The caller of this function must make sure that for the specific header field type the address of
       * a compatible value is provided.
       */
      switch(hf_field->type){
      case FT_BOOLEAN:
            return proto_tree_add_boolean_format(tree, hf_index, tvb, offset, length, *(guint32 *)value_ptr,
                  "%s: %s", str, value_str);
            break;

      case FT_UINT8:
      case FT_UINT16:
      case FT_UINT24:
      case FT_UINT32:
            return proto_tree_add_uint_format(tree, hf_index, tvb, offset, length, *(guint32 *)value_ptr,
                        "%s: %s", str, value_str);
            break;

      case FT_UINT64:
            return proto_tree_add_uint64_format(tree, hf_index, tvb, offset, length, *(guint64 *)value_ptr,
                        "%s: %s", str, value_str);
            break;

      case FT_INT8:
      case FT_INT16:
      case FT_INT24:
      case FT_INT32:
            return proto_tree_add_int_format(tree, hf_index, tvb, offset, length, *(gint32 *)value_ptr,
                        "%s: %s", str, value_str);
            break;

      case FT_FLOAT:
            return proto_tree_add_float_format(tree, hf_index, tvb, offset, length, *(float *)value_ptr,
                        "%s: %s", str, value_str);
            break;

      default:
            DISSECTOR_ASSERT_NOT_REACHED();
            return NULL;
            break;
      }
}

proto_item *
proto_tree_add_bits_format_value(proto_tree *tree, const int hf_index,
                         tvbuff_t *tvb, const gint bit_offset,
                         const gint no_of_bits, void *value_ptr,
                         gchar *value_str)
{
      proto_item *item;

      if ((item = _proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, value_ptr, value_str))) {
            FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_OFFSET(bit_offset));
            FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_SIZE(no_of_bits));
      }
      return item;
}

#define CREATE_VALUE_STRING(dst,format,ap) \
      va_start(ap,format); \
      dst = ep_strdup_vprintf(format, ap); \
      va_end(ap);

proto_item *
proto_tree_add_uint_bits_format_value(proto_tree *tree, const int hf_index,
                              tvbuff_t *tvb, const gint bit_offset,
                              const gint no_of_bits, guint32 value,
                              const char *format, ...)
{
      va_list ap;
      gchar* dst;
      header_field_info *hf_field;

      TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field);

      switch(hf_field->type){
      case FT_UINT8:
      case FT_UINT16:
      case FT_UINT24:
      case FT_UINT32:
            break;

      default:
            DISSECTOR_ASSERT_NOT_REACHED();
            return NULL;
            break;
      }

      CREATE_VALUE_STRING(dst,format,ap);

      return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst);
}

proto_item *
proto_tree_add_float_bits_format_value(proto_tree *tree, const int hf_index,
                               tvbuff_t *tvb, const gint bit_offset,
                               const gint no_of_bits, float value,
                               const char *format, ...)
{
      va_list ap;
      gchar* dst;
      header_field_info *hf_field;

      TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field);

      DISSECTOR_ASSERT(hf_field->type == FT_FLOAT);
      
      CREATE_VALUE_STRING(dst,format,ap);

      return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst);
}

proto_item *
proto_tree_add_int_bits_format_value(proto_tree *tree, const int hf_index,
                             tvbuff_t *tvb, const gint bit_offset,
                             const gint no_of_bits, gint32 value,
                             const char *format, ...)
{
      va_list ap;
      gchar* dst;
      header_field_info *hf_field;

      TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field);

      switch(hf_field->type){
      case FT_INT8:
      case FT_INT16:
      case FT_INT24:
      case FT_INT32:
            break;

      default:
            DISSECTOR_ASSERT_NOT_REACHED();
            return NULL;
            break;
      }

      CREATE_VALUE_STRING(dst,format,ap);

      return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst);
}

proto_item *
proto_tree_add_boolean_bits_format_value(proto_tree *tree, const int hf_index,
                               tvbuff_t *tvb, const gint bit_offset,
                               const gint no_of_bits, guint32 value,
                               const char *format, ...)
{
      va_list ap;
      gchar* dst;
07074       header_field_info *hf_field;

      TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field);

      DISSECTOR_ASSERT(hf_field->type == FT_BOOLEAN);
      
      CREATE_VALUE_STRING(dst,format,ap);

      return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst);
}

guchar
proto_check_field_name(const gchar *field_name)
{
  return wrs_check_charset(fld_abbrev_chars, field_name);
}

Generated by  Doxygen 1.6.0   Back to index