diff -urN ethereal-0.10.0a/epan/Makefile.am ethereal/epan/Makefile.am
--- ethereal-0.10.0a/epan/Makefile.am	2003-12-06 13:46:55.000000000 -0500
+++ ethereal/epan/Makefile.am	2004-02-12 11:40:39.000000000 -0500
@@ -85,7 +85,9 @@
 	tvbuff.c		\
 	tvbuff.h		\
 	value_string.c		\
-	value_string.h		
+	value_string.h		\
+	bitfield_string.c	\
+	bitfield_string.h	
 
 EXTRA_libethereal_a_SOURCES =	\
         inet_aton.c		\
diff -urN ethereal-0.10.0a/epan/Makefile.in ethereal/epan/Makefile.in
--- ethereal-0.10.0a/epan/Makefile.in	2003-12-13 12:12:59.000000000 -0500
+++ ethereal/epan/Makefile.in	2004-02-12 13:17:41.000000000 -0500
@@ -191,7 +191,9 @@
 	tvbuff.c		\
 	tvbuff.h		\
 	value_string.c		\
-	value_string.h		
+	value_string.h		\
+	bitfield_string.c	\
+	bitfield_string.h	
 
 
 EXTRA_libethereal_a_SOURCES = \
@@ -233,7 +235,7 @@
 	osi-utils.$(OBJEXT) packet.$(OBJEXT) plugins.$(OBJEXT) \
 	proto.$(OBJEXT) resolv.$(OBJEXT) sna-utils.$(OBJEXT) \
 	strutil.$(OBJEXT) to_str.$(OBJEXT) tvbuff.$(OBJEXT) \
-	value_string.$(OBJEXT)
+	value_string.$(OBJEXT) bitfield_string.$(OBJEXT)
 libethereal_a_OBJECTS = $(am_libethereal_a_OBJECTS)
 
 DEFS = @DEFS@
@@ -255,7 +257,8 @@
 @AMDEP_TRUE@	./$(DEPDIR)/plugins.Po ./$(DEPDIR)/proto.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/resolv.Po ./$(DEPDIR)/sna-utils.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/strutil.Po ./$(DEPDIR)/to_str.Po \
-@AMDEP_TRUE@	./$(DEPDIR)/tvbuff.Po ./$(DEPDIR)/value_string.Po
+@AMDEP_TRUE@	./$(DEPDIR)/tvbuff.Po ./$(DEPDIR)/value_string.Po \
+@AMDEP_TRUE@	./$(DEPDIR)/bitfield_string.Po
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
 LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \
@@ -355,6 +358,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/to_str.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tvbuff.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/value_string.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitfield_string.Po@am__quote@
 
 distclean-depend:
 	-rm -rf ./$(DEPDIR)
diff -urN ethereal-0.10.0a/epan/Makefile.nmake ethereal/epan/Makefile.nmake
--- ethereal-0.10.0a/epan/Makefile.nmake	2003-12-06 22:30:30.000000000 -0500
+++ ethereal/epan/Makefile.nmake	2004-02-12 11:42:14.000000000 -0500
@@ -45,6 +45,7 @@
 	to_str.obj	\
 	tvbuff.obj       \
 	value_string.obj \
+	bitfield_string.obj \
 
 
 all: ftypes dfilter ethereal.lib
diff -urN ethereal-0.10.0a/epan/bitfield_string.c ethereal/epan/bitfield_string.c
--- ethereal-0.10.0a/epan/bitfield_string.c	1969-12-31 19:00:00.000000000 -0500
+++ ethereal/epan/bitfield_string.c	2004-02-12 11:59:52.000000000 -0500
@@ -0,0 +1,239 @@
+/* bitfield_string.c
+ * Routines for bitfield_strings
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "bitfield_string.h"
+
+
+#define ANY_VALUE ((guint32) -1)
+#define LEFTOVER_BIT_MASK ((guint32) -1)
+
+/* If bs.value is -1, that means _any_ bits set in the masked val
+   constitute a hit.  (This lets you represent integer bitfields or
+   lazily represent single-bit flags.)
+     e.g.: { NULL, 0x03, -1, "value=%d", NULL }
+ 
+   If bs.value is any other value, the masked val must equal bs.value
+   to be considered a hit.  Note that this lets you match a value of
+   0.  (This lets you represent enum constants in a bitfield.)
+     e.g.: { NULL, 0x03, 0x00, "up", NULL}
+           { NULL, 0x03, 0x01, "down", NULL }
+  
+   -1 is available for this purpose because you should always have a
+   mask with _some_ zero bits in it (and thus -1 will never itself
+   match), otherwise you should just use an integer (and value_string).
+  
+   Additionally, a mask with no zero bits in it is reserved to mark a
+   leftover-bits mask.  This is useful for showing any set bits
+   which didn't otherwise match masks/values in the bitfield array.
+     e.g.: { NULL, -1, -1, "unknown(%02X)", NULL }  */
+
+#define TEST_BS(x, y) \
+    ( \
+     ((x).value == ANY_VALUE) \
+       ? (((x).mask & (y)) != 0) \
+       : (((x).mask & (y)) == (x).value) \
+    )
+
+
+static guint32
+tree_add_bitfield(proto_tree *tree,
+		  const bitfield_string *bs, gint i,
+		  tvbuff_t *tvb, gint start, gint length,
+		  guint32 val, gint width);
+
+static gint
+bitfield_shifted_value(guint32 val, guint32 mask)
+{
+  int shift = 0;
+
+  /* Compute the number of bits we have to shift the bitfield right
+     to extract its value. */
+  for (shift = 0; shift < 32; shift++)
+    if (mask & (1<<shift))
+      break;
+
+  return ((val & mask) >> shift);
+}
+
+
+/* Generate a single string from the bitfield, each bit/match delimited */
+extern gchar*
+bitfield_to_string(guint32 val, const bitfield_string* bs, const char* delim)
+{
+  gint i = 0;
+  gchar *strptr;
+  gchar tmp_str[1024];
+  static gchar out_str[1024];
+  gboolean matched;
+
+  out_str[0] = '\0';
+  while (bs[i].set_strptr || bs[i].clr_strptr)
+    {
+      matched = TEST_BS(bs[i], val);
+      if (matched)
+	strptr = bs[i].set_strptr;
+      else
+	strptr = bs[i].clr_strptr;
+
+      /* if we're supposed to display this bit, */
+      if (strptr != NULL)
+	{
+	  /* if we've already displayed some bits, insert "|" between */
+	  if (out_str[0] != '\0')
+	    strcat(out_str, delim);
+
+	  /* add the verbal form of the bit to the string */
+	  sprintf(tmp_str, strptr, bitfield_shifted_value(val, bs[i].mask));
+	  strcat(out_str, tmp_str);
+	}
+
+      /* ignore subsequent matches on this mask, to allow for printing
+       * of leftover bits -- just make sure zero enums come first */
+      if (matched)
+	val &= ~(bs[i].mask);
+
+      i++;
+    }
+
+  return out_str;
+}
+
+
+/* Generate a "..01 ...." representation of a value */
+static gchar *
+bitfield_bits_to_str(guint32 value, guint32 mask, gint width)
+{
+  static gchar str[256];
+  const gchar* ch;
+  const gchar* zero;
+  gint i;
+
+  /* For full masks (used only for leftover bit display), don't display
+   * the zero bits. */
+  if (mask == LEFTOVER_BIT_MASK)
+    zero = ".";
+  else
+    zero = "0";
+
+  str[0] = '\0';
+  for (i = width - 1; i >= 0; i--)
+    {
+      /* add the bit to the string */
+      if (mask & (1 << i))
+	ch = (value & (1 << i)) ? "1" : zero;
+      else
+	ch = ".";
+      strcat(str, ch);
+
+      /* insert a space between every nibble */
+      if (i && ((i & 3) == 0))
+	strcat(str, " ");
+    }
+
+  return str;
+}
+
+
+/* Add a list of items to the proto_tree from the bitfield, each bit/match
+   a separate item. */
+extern void
+proto_tree_add_bitfield(proto_tree *tree,
+			const bitfield_string *bs,
+			tvbuff_t *tvb, gint start, gint length,
+			guint32 val, gint width)
+{
+  gint i = 0;
+
+  /* build the tree from highest to lowest bits */
+  while (bs[i].set_strptr || bs[i].clr_strptr)
+    {
+      val = tree_add_bitfield(tree, bs, i, tvb, start, length, val, width);
+      i++;
+    }
+}
+
+
+/* Add a list of items to the proto_tree from the bitfield, each bit/match
+   (searched from back-to-front) a separate item.  This is useful for
+   those simple bitfields which you want to display low-to-high-bit on
+   a single line but high-to-low-bit in the proto_tree.  For complicated
+   bitfields, you can't always go backwards like this, and you'll need to
+   have a separate bitfield_string. */
+extern void
+proto_tree_add_bitfield_inv(proto_tree *tree,
+			    const bitfield_string *bs,
+			    tvbuff_t *tvb, gint start, gint length,
+			    guint32 val, gint width)
+{
+  gint i = 0;
+
+  /* build the tree from back to front */
+  while (bs[i].set_strptr || bs[i].clr_strptr)
+    i++;
+
+  for (i--; i >= 0; i--)
+    {
+      val = tree_add_bitfield(tree, bs, i, tvb, start, length, val, width);
+    }
+}
+
+
+/* This does the real work of adding proto_tree items from a bitfield. 
+   It handles a single entry in the bitfield_value array. */
+static guint32
+tree_add_bitfield(proto_tree *tree,
+		  const bitfield_string *bs, gint i,
+		  tvbuff_t *tvb, gint start, gint length,
+		  guint32 val, gint width)
+{
+  gchar tmp_str[4096];
+  gchar out_str[4096];
+  gchar *strptr;
+  gboolean matched;
+
+  matched = TEST_BS(bs[i], val);
+
+  /* only bother for items which are registered for display */
+  if (bs[i].p_id != NULL)
+    {
+      if (matched)
+	strptr = bs[i].set_strptr;
+      else
+	strptr = bs[i].clr_strptr;
+      
+      /* if we're supposed to display this bit, */
+      if (strptr != NULL)
+	{
+	  /* convert it to its verbal form */
+	  sprintf(tmp_str, strptr,
+		  bitfield_shifted_value(val, bs[i].mask));
+	  
+	  /* and attach the bit display */
+	  sprintf(out_str, "%s = %s",
+		  bitfield_bits_to_str(val, bs[i].mask, width),
+		  tmp_str);
+	  
+	  /* add to tree */
+	  proto_tree_add_uint_format(tree,
+				     *(bs[i].p_id),
+				     tvb, start, length,
+				     val,
+				     out_str);
+	}
+    }
+  
+  /* ignore subsequent matches on this mask, to allow for printing
+     of leftover bits -- just make sure zero enums come first */
+  if (matched)
+    val &= ~(bs[i].mask);
+
+  return val;
+}
diff -urN ethereal-0.10.0a/epan/bitfield_string.h ethereal/epan/bitfield_string.h
--- ethereal-0.10.0a/epan/bitfield_string.h	1969-12-31 19:00:00.000000000 -0500
+++ ethereal/epan/bitfield_string.h	2004-02-12 11:54:23.000000000 -0500
@@ -0,0 +1,96 @@
+#ifndef __BITFIELD_STRING_H__
+#define __BITFIELD_STRING_H__
+
+#include <glib.h>
+#include "proto.h"
+
+/* When you construct an array of bitfield_string[], ordering matters.
+   Generally you'll want to order them low-to-high.  This makes the
+   strings start with the low bits on the left.  Subtrees start with
+   the low bits on the top by default, but you can also use
+   proto_tree_add_bitfield_inv() to reverse that for simple arrays.
+  
+   Ordering is most subtly important when dealing with bitfield enums.
+   After a particular value is matched, its bits subsequently ignored.
+   This allows you to display leftover bits by adding a subsequent
+   entry with a broad mask.  e.g.: { NULL, -1, -1, "%04x", NULL }
+  
+   This means you should always place the zero enum value in a given
+   mask first, or else it will match any time you've matched a prior
+   value in that mask.  This also means that for certain complicated
+   bitfield patterns, proto_tree_add_bitfield_inv() will have
+   unexpected behavior.  */
+
+
+typedef struct _bitfield_string {
+  int     *p_id;        /* a pointer to bit's hf_foo for adding to tree */
+  guint32 mask;         /* the mask for this particular bit */
+  guint32 value;        /* the value (or -1 for any set) for this bit */
+  gchar   *set_strptr;  /* the format string to print when mask/value match */
+  gchar   *clr_strptr;  /* the format string to print when they don't match */
+} bitfield_string;
+
+
+
+/* If p_id is NULL, this item won't show up in a proto_tree, but it will
+   be added to the single-line string.
+
+   Either set_strptr or clr_strptr may be NULL.  Both NULL marks the end
+   of the array.  The value passed to the format string is shifted and
+   masked.
+
+   If value is -1, that means _any_ bits set in the masked integer
+   constitute a hit.  (This lets you represent integer bitfields or
+   lazily represent single-bit flags.)
+     e.g.: { NULL, 0x03, -1, "value=%d", NULL }
+ 
+   If value is not -1, the masked integer must equal bs.value
+   to be considered a hit.  Note that this lets you match a value of
+   0.  (This lets you represent enum constants in a bitfield.)
+     e.g.: { NULL, 0x03, 0x00, "up", NULL}
+           { NULL, 0x03, 0x01, "down", NULL }
+ 
+   -1 is available for this purpose because you should always have a
+   mask with _some_ zero bits in it (and thus -1 will never itself
+   match), otherwise you should just use an integer (and value_string).
+  
+   Additionally, a mask with no zero bits in it is reserved to mark a
+   leftover-bits mask.  This is useful for showing any set bits
+   which didn't otherwise match masks/values in the bitfield array.
+     e.g.: { NULL, -1, -1, "unknown(%02X)", NULL }  */
+
+
+/* Convenience macros for defining simple single-bit lists */
+
+#define BFS_DEF(x) { NULL, x, -1, #x, NULL }
+#define BFS_UNK(x) { NULL, -1, -1, x, NULL }
+#define BFS_END    { NULL, -1, -1, NULL, NULL }
+
+
+/* Generate a single string from the bitfield, each bit/match delimited */
+extern gchar*
+bitfield_to_string(guint32 val, const bitfield_string* bs, const char *delim);
+
+
+/* Add a list of items to the proto_tree from the bitfield, each bit/match
+   a separate item. */
+extern void
+proto_tree_add_bitfield(proto_tree *tree,
+			const bitfield_string *bs,
+			tvbuff_t *tvb, gint start, gint length,
+			guint32 value, gint width);
+
+
+/* Add a list of items to the proto_tree from the bitfield, each bit/match
+   (searched from back-to-front) a separate item.  This is useful for
+   those simple bitfields which you want to display low-to-high-bit on
+   a single line but high-to-low-bit in the proto_tree.  For complicated
+   bitfields, you can't always go backwards like this, and you'll need to
+   have a separate bitfield_string. */
+extern void
+proto_tree_add_bitfield_inv(proto_tree *tree,
+			    const bitfield_string *bs,
+			    tvbuff_t *tvb, gint start, gint length,
+			    guint32 value, gint width);
+
+#endif
diff -urN ethereal-0.10.0a/epan/packet.h ethereal/epan/packet.h
--- ethereal-0.10.0a/epan/packet.h	2003-10-03 23:42:39.000000000 -0400
+++ ethereal/epan/packet.h	2004-02-12 11:48:55.000000000 -0500
@@ -31,6 +31,7 @@
 #include "pint.h"
 #include "to_str.h"
 #include "value_string.h"
+#include "bitfield_string.h"
 #include "column_info.h"
 #include "frame_data.h"
 #include "packet_info.h"
diff -urN ethereal-0.10.0a/epan/value_string.h ethereal/epan/value_string.h
--- ethereal-0.10.0a/epan/value_string.h	2003-12-06 13:46:55.000000000 -0500
+++ ethereal/epan/value_string.h	2004-02-12 13:36:09.000000000 -0500
@@ -35,6 +35,9 @@
   gchar   *strptr;
 } value_string;
 
+#define VS_DEF(x) { x, #x }
+#define VS_END    { 0, NULL }
+
 extern gchar*     match_strval(guint32, const value_string*);
 
 extern gchar*     val_to_str(guint32, const value_string *, const char *);
diff -urN ethereal-0.10.0a/plugins/plugin_api_list.c ethereal/plugins/plugin_api_list.c
--- ethereal-0.10.0a/plugins/plugin_api_list.c	2003-12-06 13:46:57.000000000 -0500
+++ ethereal/plugins/plugin_api_list.c	2004-02-12 12:27:11.000000000 -0500
@@ -393,3 +393,8 @@
 
 const char *decode_enumerated_bitfield_shifted(guint32, guint32, int,
     const value_string *, const char *);
+
+gchar* bitfield_to_string(guint32 val, const bitfield_string* bs, const char* delim);
+void proto_tree_add_bitfield(proto_tree *tree, const bitfield_string *bs, tvbuff_t *tvb, gint start, gint length, guint32 value, gint width);
+void proto_tree_add_bitfield_inv(proto_tree *tree, const bitfield_string *bs, tvbuff_t *tvb, gint start, gint length, guint32 value, gint width);
+
diff -urN ethereal-0.10.0a/util.c ethereal/util.c
--- ethereal-0.10.0a/util.c	2003-12-06 13:46:55.000000000 -0500
+++ ethereal/util.c	2004-02-12 13:00:01.000000000 -0500
@@ -56,6 +56,14 @@
 #endif /* __MINGW32__ */
 #endif /* HAVE_IO_H */
 
+/* This is a slight hack to ensure that the bitfield_string gets linked
+ * into the ethereal binaries, in the event that only plugins use it.
+ */
+#include "epan/bitfield_string.h"
+void* addr_bitfield_to_string = (void*) bitfield_to_string;
+void* addr_proto_tree_add_bitfield = (void*) proto_tree_add_bitfield;
+void* addr_proto_tree_add_bitfield_inv = (void*) proto_tree_add_bitfield_inv;
+
 #ifdef HAVE_LIBZ
 #include <zlib.h>	/* to get the libz version number */
 #endif
