Ethereal-dev: Re: [Ethereal-dev] New feature for ethereal

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: David Hampton <dhampton@xxxxxxx>
Date: Thu, 18 Jan 2001 18:33:12 -0800
Jeff Foster wrote:
> 
> You should also look at the code that does sub-dissector registration
> (epan/packet.c) and consider what changes would be need to integrate
> your changes into that code. The additions you purpose are a logical
> extension to that code. You have to consider
> 
> 1) Finding the protocols that support sub-dissector tables.
>         - check the dissector_tables variable in packet.c
> 2) Adding the user defined values after a 'redissect'; check file.c
> including ...
> 3) Saving sub-dissector information if there was a dissector definition
> when you add the user values.
> 4) Restoring old values if user deletes a special definition.

I have integrated my dissector changes into the code in packet.c,
instead of keeping a separate table in my source file.

Guy Harris wrote:
> Yes - for example, the list of protocols to decode a given {TCP,UDP}
> conversation as should probably include all protocols that run atop
> {TCP,UDP} and that don't have heuristic dissectors (if the heuristic
> dissector fails to recognize the packet as one of its own, it's not
> going to do so just because you try to force it to do so).
> 
> That could be done, in part, by going through the {"tcp","udp"}
> dissector table, but you'd also have to handle protocols that don't have
> a port assigned to them at all, e.g. RTP.

I have implemented iterators for the dissector tables, and am using
these now to create the menus.  This means that I can include all items
in a dissector table without any changes to the packet-xxx.c files.

I have added panels to my menu so that you can select whether to change
decoding of an ethertype, an IP protocol number, or a TCP/UDP port
number.  The menu choices for TCP/UDP include all items in the normal
TCP and UDP dissectors, and also entries from the TCP/UDP conversation
dissector tables.  Note that only the changes specified in the frontmost
panel are saved.  This means that while you can change dissectors for
both the IP protocol number and the TCP port number of a packet, you
can't request both changes at the same time.

This code is admittedly Ethernet and IP centric at this point, but its a
start.  The problem is that I can't find a way to determine the data
link layer format of frames (which would determine whether an ethernet
or, say, frame relay panel was presented), and if non-IP network layers
are used there is no easy way to find their protocol numbers without
knowledge of the network layer packet formats.  If DECnet, VINES, etc.
left their protocol numbers in a common location then these panels would
be trivial to add.

David
Index: packet-ethertype.c
===================================================================
RCS file: /cvsroot/ethereal/packet-ethertype.c,v
retrieving revision 1.11
diff -u -r1.11 packet-ethertype.c
--- packet-ethertype.c	2001/01/18 08:38:10	1.11
+++ packet-ethertype.c	2001/01/19 02:11:46
@@ -117,6 +117,8 @@
 	/* Tvbuff for the payload after the Ethernet type. */
 	next_tvb = tvb_new_subset(tvb, offset_after_etype, -1, -1);
 
+	pinfo->ethertype = etype;
+
 	/* Remember how much data there is in it. */
 	length_before = tvb_reported_length(next_tvb);
 
Index: epan/packet.c
===================================================================
RCS file: /cvsroot/ethereal/epan/packet.c,v
retrieving revision 1.18
diff -u -r1.18 packet.c
--- packet.c	2001/01/13 06:34:33	1.18
+++ packet.c	2001/01/19 02:11:47
@@ -83,6 +83,12 @@
 #include "tvbuff.h"
 #include "plugins.h"
 
+#ifdef DEBUG
+#define dprintf(...) printf(...)
+#else
+#define dprintf(...)
+#endif
+
 static void display_signed_time(gchar *, int, gint32, gint32);
 
 
@@ -1009,6 +1015,7 @@
   pi.net_dst.type = AT_NONE;
   pi.src.type = AT_NONE;
   pi.dst.type = AT_NONE;
+  pi.ethertype  = 0;
   pi.ipproto  = 0;
   pi.ptype = PT_NONE;
   pi.srcport  = 0;
@@ -1173,8 +1180,18 @@
 		dissector_t	new;
 	} dissector;
 	int	proto_index;
-} dtbl_entry_t;
+} dissector_entry_t;
+
+struct dtbl_entry {
+	dissector_entry_t initial;
+	dissector_entry_t current;
+};
 
+static void
+dissect_null(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+}
+
 /* Finds a dissector table by field name. */
 static dissector_table_t
 find_dissector_table(const char *name)
@@ -1196,9 +1213,11 @@
 	g_assert( sub_dissectors);
 
 	dtbl_entry = g_malloc(sizeof (dtbl_entry_t));
-	dtbl_entry->is_old_dissector = TRUE;
-	dtbl_entry->dissector.old = dissector;
-	dtbl_entry->proto_index = proto;
+	dtbl_entry->current.is_old_dissector = TRUE;
+	dtbl_entry->current.dissector.old = dissector;
+	dtbl_entry->current.proto_index = proto;
+	dtbl_entry->initial = dtbl_entry->current;
+	proto_set_protocol_dissector(proto, dissector);
 
 /* do the table insertion */
     	g_hash_table_insert( sub_dissectors, GUINT_TO_POINTER( pattern),
@@ -1216,9 +1235,11 @@
 	g_assert( sub_dissectors);
 
 	dtbl_entry = g_malloc(sizeof (dtbl_entry_t));
-	dtbl_entry->is_old_dissector = FALSE;
-	dtbl_entry->dissector.new = dissector;
-	dtbl_entry->proto_index = proto;
+	dtbl_entry->current.is_old_dissector = FALSE;
+	dtbl_entry->current.dissector.new = dissector;
+	dtbl_entry->current.proto_index = proto;
+	dtbl_entry->initial = dtbl_entry->current;
+	proto_set_protocol_dissector(proto, dissector);
 
 /* do the table insertion */
     	g_hash_table_insert( sub_dissectors, GUINT_TO_POINTER( pattern),
@@ -1288,6 +1309,78 @@
 	}
 }
 
+void
+dissector_change(const char *name, guint32 pattern, dissector_t dissector,
+		 gboolean old, int proto)
+{
+	dissector_table_t sub_dissectors = find_dissector_table( name);
+	dtbl_entry_t *dtbl_entry;
+
+/* sanity check */
+	g_assert( sub_dissectors);
+
+	/*
+	 * See if the entry already exists. If so, reuse it.
+	 */
+	dtbl_entry = g_hash_table_lookup(sub_dissectors,
+	    GUINT_TO_POINTER(pattern));
+	if (dtbl_entry != NULL) {
+	  dtbl_entry->current.is_old_dissector = old;
+	  dtbl_entry->current.dissector.new = dissector ? dissector : dissect_null;
+	  dtbl_entry->current.proto_index = proto;
+	  return;
+	}
+
+	/*
+	 * Don't create an entry if there is no dissector - I.E. the
+	 * user said not to decode something that wasn't being decoded
+	 * in the first place.
+	 */
+	if (dissector == NULL)
+	  return;
+
+	dtbl_entry = g_malloc(sizeof (dtbl_entry_t));
+	dtbl_entry->initial.is_old_dissector = FALSE;
+	dtbl_entry->initial.dissector.old = NULL;
+	dtbl_entry->initial.proto_index = -1;
+	dtbl_entry->current.is_old_dissector = old;
+	dtbl_entry->current.dissector.new = dissector;
+	dtbl_entry->current.proto_index = proto;
+
+/* do the table insertion */
+    	g_hash_table_insert( sub_dissectors, GUINT_TO_POINTER( pattern),
+    	 (gpointer)dtbl_entry);
+}
+
+void
+dissector_reset(const char *name, guint32 pattern)
+{
+	dissector_table_t sub_dissectors = find_dissector_table( name);
+	dtbl_entry_t *dtbl_entry;
+
+/* sanity check */
+	g_assert( sub_dissectors);
+
+	/*
+	 * Find the entry.
+	 */
+	dtbl_entry = g_hash_table_lookup(sub_dissectors,
+	    GUINT_TO_POINTER(pattern));
+
+	if (dtbl_entry == NULL)
+		return;
+
+	/*
+	 * Found - is there an initial value?
+	 */
+	if (dtbl_entry->initial.dissector.new != NULL) {
+		dtbl_entry->current = dtbl_entry->initial;
+	} else {
+		g_hash_table_remove(sub_dissectors, GUINT_TO_POINTER(pattern));
+		g_free(dtbl_entry);
+	}
+}
+
 /* Look for a given port in a given dissector table and, if found, call
    the dissector with the arguments supplied, and return TRUE, otherwise
    return FALSE.
@@ -1309,8 +1402,8 @@
 		/*
 		 * Is this protocol enabled?
 		 */
-		if (dtbl_entry->proto_index != -1 &&
-		    !proto_is_protocol_enabled(dtbl_entry->proto_index)) {
+		if (dtbl_entry->current.proto_index != -1 &&
+		    !proto_is_protocol_enabled(dtbl_entry->current.proto_index)) {
 			/*
 			 * No - pretend this dissector didn't exist,
 			 * so that other dissectors might have a chance
@@ -1325,8 +1418,8 @@
 		saved_proto = pi.current_proto;
 		saved_match_port = pi.match_port;
 		pi.match_port = port;
-		if (dtbl_entry->is_old_dissector)
-			(*dtbl_entry->dissector.old)(pd, offset, fd, tree);
+		if (dtbl_entry->current.is_old_dissector)
+			(*dtbl_entry->current.dissector.old)(pd, offset, fd, tree);
 		else {
 			/*
 			 * Old dissector calling new dissector; use
@@ -1337,12 +1430,12 @@
 			 * let the "offset" argument handle stepping
 			 * through the packet?
 			 */
-			if (dtbl_entry->proto_index != -1) {
+			if (dtbl_entry->current.proto_index != -1) {
 				pi.current_proto =
-				    proto_get_protocol_short_name(dtbl_entry->proto_index);
+				    proto_get_protocol_short_name(dtbl_entry->current.proto_index);
 			}
 			tvb = tvb_create_from_top(offset);
-			(*dtbl_entry->dissector.new)(tvb, &pi, tree);
+			(*dtbl_entry->current.dissector.new)(tvb, &pi, tree);
 		}
 		pi.current_proto = saved_proto;
 		pi.match_port = saved_match_port;
@@ -1367,8 +1460,8 @@
 		/*
 		 * Is this protocol enabled?
 		 */
-		if (dtbl_entry->proto_index != -1 &&
-		    !proto_is_protocol_enabled(dtbl_entry->proto_index)) {
+		if (dtbl_entry->current.proto_index != -1 &&
+		    !proto_is_protocol_enabled(dtbl_entry->current.proto_index)) {
 			/*
 			 * No - pretend this dissector didn't exist,
 			 * so that other dissectors might have a chance
@@ -1383,20 +1476,20 @@
 		saved_proto = pinfo->current_proto;
 		saved_match_port = pinfo->match_port;
 		pinfo->match_port = port;
-		if (dtbl_entry->is_old_dissector) {
+		if (dtbl_entry->current.is_old_dissector) {
 			/*
 			 * New dissector calling old dissector; use
 			 * "tvb_compat()" to remap.
 			 */
 			tvb_compat(tvb, &pd, &offset);
-			(*dtbl_entry->dissector.old)(pd, offset, pinfo->fd,
+			(*dtbl_entry->current.dissector.old)(pd, offset, pinfo->fd,
 			    tree);
 		} else {
-			if (dtbl_entry->proto_index != -1) {
+			if (dtbl_entry->current.proto_index != -1) {
 				pinfo->current_proto =
-				    proto_get_protocol_short_name(dtbl_entry->proto_index);
+				    proto_get_protocol_short_name(dtbl_entry->current.proto_index);
 			}
-			(*dtbl_entry->dissector.new)(tvb, pinfo, tree);
+			(*dtbl_entry->current.dissector.new)(tvb, pinfo, tree);
 		}
 		pinfo->current_proto = saved_proto;
 		pinfo->match_port = saved_match_port;
@@ -1405,6 +1498,175 @@
 		return FALSE;
 }
 
+gboolean
+dissector_get_old_flag (dtbl_entry_t *dtbl_entry)
+{
+	g_assert(dtbl_entry);
+	return(dtbl_entry->current.is_old_dissector);
+}
+
+gint
+dissector_get_proto (dtbl_entry_t *dtbl_entry)
+{
+	g_assert(dtbl_entry);
+	return(dtbl_entry->current.proto_index);
+}
+
+gint
+dissector_get_initial_proto (dtbl_entry_t *dtbl_entry)
+{
+	g_assert(dtbl_entry);
+	return(dtbl_entry->initial.proto_index);
+}
+
+/**************************************************/
+/*                                                */
+/*       Routines to walk dissector tables        */
+/*                                                */
+/**************************************************/
+
+typedef struct dissector_foreach_info {
+  gpointer     caller_data;
+  DATFunc      caller_func;
+  GHFunc       next_func;
+  gchar       *table_name;
+} dissector_foreach_info_t;
+
+/*
+ * Walk all dissector tables calling a user supplied function on each
+ * entry.  These three routines handle traversing the hash of hashes
+ * that is the dissector tables.
+ */
+static void
+dissector_all_tables_foreach_func2 (gpointer key, gpointer value, gpointer user_data)
+{
+	dissector_foreach_info_t *info;
+	dtbl_entry_t *dtbl_entry;
+
+	g_assert(value);
+	g_assert(user_data);
+
+	dtbl_entry = value;
+	if (dtbl_entry->current.proto_index == -1) {
+	  return;
+	}
+
+	info = user_data;
+	dprintf("         Entry %x...", (gint)key);
+	info->caller_func(info->table_name, key, value, info->caller_data);
+	dprintf("\n");
+}
+
+static void
+dissector_all_tables_foreach_func1 (gpointer key, gpointer value, gpointer user_data)
+{
+	GHashTable   *hash_table;
+	dissector_foreach_info_t *info;
+
+	g_assert(value);
+	g_assert(user_data);
+
+	hash_table = value;
+	info = user_data;
+	info->table_name = (gchar*) key;
+	dprintf("Walking table %s...\n", info->table_name);
+	g_hash_table_foreach(hash_table, info->next_func, info);
+}
+
+void
+dissector_all_tables_foreach (DATFunc func,
+			      gpointer user_data)
+{
+	dissector_foreach_info_t info;
+
+	info.caller_data = user_data;
+	info.caller_func = func;
+	info.next_func = dissector_all_tables_foreach_func2;
+	g_hash_table_foreach(dissector_tables, dissector_all_tables_foreach_func1, &info);
+}
+
+/*
+ * Walk one dissector table calling a user supplied function on each
+ * entry.
+ */
+void
+dissector_table_foreach (char *name,
+			 DATFunc func,
+			 gpointer user_data)
+{
+	dissector_foreach_info_t info;
+	GHashTable *hash_table;
+
+	hash_table = find_dissector_table(name);
+	g_assert(hash_table);
+
+	info.table_name = name;
+	info.caller_func = func;
+	info.caller_data = user_data;
+	dprintf("Walking table %s...\n", info->table_name);
+	g_hash_table_foreach(hash_table, dissector_all_tables_foreach_func2, &info);
+}
+
+/*
+ * Walk all dissector tables calling a user supplied function only on
+ * any entry that has been changed from its original state.  These two
+ * routines (plus one above) handle traversing the hash of hashes that
+ * is the dissector tables.
+ */
+static void
+dissector_all_tables_foreach_changed_func2 (gpointer key, gpointer value, gpointer user_data)
+{
+	dtbl_entry_t *dtbl_entry;
+	dissector_foreach_info_t *info;
+
+	g_assert(value);
+	g_assert(user_data);
+
+	dtbl_entry = value;
+	if (dtbl_entry->initial.proto_index == dtbl_entry->current.proto_index) {
+	    dprintf("         Entry %x... (not changed)\n", (gint)key);
+	    return;
+	}
+
+	info = user_data;
+	dprintf("         Entry %x...", (gint)key);
+	info->caller_func(info->table_name, key, value, info->caller_data);
+	dprintf("\n");
+}
+
+void
+dissector_all_tables_foreach_changed (DATFunc func,
+				      gpointer user_data)
+{
+	dissector_foreach_info_t info;
+
+	info.caller_data = user_data;
+	info.caller_func = func;
+	info.next_func = dissector_all_tables_foreach_changed_func2;
+	g_hash_table_foreach(dissector_tables, dissector_all_tables_foreach_func1, &info);
+}
+
+/*
+ * Walk one dissector table calling a user supplied function only on
+ * any entry that has been changed from its original state.
+ */
+void
+dissector_table_foreach_changed (char *name,
+				 DATFunc func,
+				 gpointer user_data)
+{
+	dissector_foreach_info_t info;
+	GHashTable *hash_table;
+
+	hash_table = find_dissector_table(name);
+	g_assert(hash_table);
+
+	info.table_name = name;
+	info.caller_func = func;
+	info.caller_data = user_data;
+	g_hash_table_foreach(hash_table, dissector_all_tables_foreach_changed_func2, &info);
+}
+
 dissector_table_t
 register_dissector_table(const char *name)
 {
@@ -1569,14 +1831,14 @@
  * Nuke this and go back to storing a pointer to the dissector when
  * the last old-style dissector is gone.
  */
-typedef struct {
+struct conv_dtbl_entry {
 	gboolean is_old_dissector;
 	union {
 		old_dissector_t	old;
 		dissector_t	new;
 	} dissector;
 	int	proto_index;
-} conv_dtbl_entry_t;
+};
 
 /* Finds a conversation dissector table by table name. */
 static conv_dissector_list_t *
@@ -1600,6 +1862,7 @@
 	dtbl_entry->is_old_dissector = TRUE;
 	dtbl_entry->dissector.old = dissector;
 	dtbl_entry->proto_index = proto;
+	proto_set_protocol_dissector(proto, dissector);
 
 	/* do the table insertion */
 	*sub_dissectors = g_slist_append(*sub_dissectors, (gpointer)dtbl_entry);
@@ -1618,6 +1881,7 @@
 	dtbl_entry->is_old_dissector = FALSE;
 	dtbl_entry->dissector.new = dissector;
 	dtbl_entry->proto_index = proto;
+	proto_set_protocol_dissector(proto, dissector);
 
 	/* do the table insertion */
 	*sub_dissectors = g_slist_append(*sub_dissectors, (gpointer)dtbl_entry);
@@ -1638,6 +1902,66 @@
 	*sub_dissectors = NULL;	/* initially empty */
 	g_hash_table_insert(conv_dissector_lists, (gpointer)name,
 	    (gpointer) sub_dissectors);
+}
+
+gboolean
+conv_dissector_get_old_flag (conv_dtbl_entry_t *dtbl_entry)
+{
+	g_assert(dtbl_entry);
+	return(dtbl_entry->is_old_dissector);
+}
+
+gint
+conv_dissector_get_proto (conv_dtbl_entry_t *dtbl_entry)
+{
+	g_assert(dtbl_entry);
+	return(dtbl_entry->proto_index);
+}
+
+void
+dissector_conv_foreach (char *name,
+			DATFunc func,
+			gpointer user_data)
+{
+	conv_dissector_list_t *sub_dissectors = find_conv_dissector_list(name);
+	GSList *tmp;
+
+	/* sanity check */
+	g_assert(sub_dissectors != NULL);
+
+	for (tmp = *sub_dissectors; tmp; tmp = g_slist_next(tmp)) {
+		func(name, 0, tmp->data, user_data);
+	}
+}
+
+static void
+dissector_all_conv_foreach_func1 (gpointer key, gpointer value, gpointer user_data)
+{
+	conv_dissector_list_t *sub_dissectors;
+	GSList *tmp;
+	dissector_foreach_info_t *info;
+
+	g_assert(value);
+	g_assert(user_data);
+
+	sub_dissectors = value;
+	for (tmp = *sub_dissectors; tmp; tmp = g_slist_next(tmp)) {
+	  info = user_data;
+	  dprintf("         Entry %x...", (gint)key);
+	  info->caller_func(key, 0, tmp->data, info->caller_data);
+	  dprintf("\n");
+	}
+}
+
+void
+dissector_all_conv_foreach (DATFunc func,
+			    gpointer user_data)
+{
+	dissector_foreach_info_t info;
+
+	info.caller_data = user_data;
+	info.caller_func = func;
+	g_hash_table_foreach(conv_dissector_lists, dissector_all_conv_foreach_func1, &info);
 }
 
 /*
Index: epan/packet.h
===================================================================
RCS file: /cvsroot/ethereal/epan/packet.h,v
retrieving revision 1.20
diff -u -r1.20 packet.h
--- packet.h	2001/01/18 07:44:41	1.20
+++ packet.h	2001/01/19 02:11:48
@@ -170,6 +170,7 @@
   address net_dst;		/* network-layer destination address */
   address src;			/* source address (net if present, DL otherwise )*/
   address dst;			/* destination address (net if present, DL otherwise )*/
+  guint32 ethertype;		/* Ethernet Type Code, if this is an Ethernet packet */
   guint32 ipproto;		/* IP protocol, if this is an IP packet */
   gboolean fragmented;		/* TRUE if the protocol is only a fragment */
   port_type ptype;		/* type of the following two port numbers */
@@ -206,6 +207,18 @@
 typedef void (*old_dissector_t)(const u_char *, int, frame_data *, proto_tree *);
 typedef void (*dissector_t)(tvbuff_t *, packet_info *, proto_tree *);
 
+typedef void (*DATFunc) (gchar *table_name, gpointer key, gpointer value, gpointer user_data);
+
+/* Opaque structure - provides type checking but no access to components */
+typedef struct dtbl_entry dtbl_entry_t;
+
+gboolean dissector_get_old_flag (dtbl_entry_t *entry);
+gint dissector_get_proto (dtbl_entry_t * entry);
+gint dissector_get_initial_proto (dtbl_entry_t * entry);
+void dissector_table_foreach_changed (char *name, DATFunc func, gpointer user_data);
+void dissector_table_foreach (char *name, DATFunc func, gpointer user_data);
+void dissector_all_tables_foreach_changed (DATFunc func, gpointer user_data);
+
 /* a protocol uses the function to register a sub-dissector table */
 dissector_table_t register_dissector_table(const char *name);
 
@@ -221,6 +234,11 @@
 void old_dissector_delete(const char *name, guint32 pattern, old_dissector_t dissector);
 void dissector_delete(const char *name, guint32 pattern, dissector_t dissector);
 
+/* Reset a dissector in a sub-dissector table to its initial value. */
+void dissector_change(const char *abbrev, guint32 pattern,
+    dissector_t dissector, gboolean old, int proto);
+void dissector_reset(const char *name, guint32 pattern);
+
 /* Look for a given port in a given dissector table and, if found, call
    the dissector with the arguments supplied, and return TRUE, otherwise
    return FALSE. */
@@ -280,6 +298,14 @@
     int proto);
 void conv_dissector_add(const char *name, dissector_t dissector,
     int proto);
+
+/* Opaque structure - provides type checking but no access to components */
+typedef struct conv_dtbl_entry conv_dtbl_entry_t;
+
+gboolean conv_dissector_get_old_flag (conv_dtbl_entry_t *entry);
+gint conv_dissector_get_proto (conv_dtbl_entry_t * entry);
+void dissector_conv_foreach(char *name, DATFunc func, gpointer user_data);
+void dissector_all_conv_foreach(DATFunc func, gpointer user_data);
 
 /* Handle for dissectors you call directly.
    This handle is opaque outside of "packet.c". */
Index: epan/proto.c
===================================================================
RCS file: /cvsroot/ethereal/epan/proto.c,v
retrieving revision 1.4
diff -u -r1.4 proto.c
--- proto.c	2001/01/03 06:55:58	1.4
+++ proto.c	2001/01/19 02:11:50
@@ -125,6 +125,7 @@
 	GList	*last_field;	/* pointer to end of list of fields */
 	gboolean is_enabled;	/* TRUE if protocol is enabled */
 	gboolean can_disable;	/* TRUE if protocol can be disabled */
+	gpointer dissector;
 } protocol_t;
 
 /* List of all protocols */
@@ -1506,6 +1507,7 @@
 	protocol->fields = NULL;
 	protocol->is_enabled = TRUE; /* protocol is enabled by default */
 	protocol->can_disable = TRUE;
+	protocol->dissector = NULL;
 	protocols = g_list_insert_sorted(protocols, protocol,
 	    proto_compare_name);
 
@@ -1595,6 +1597,8 @@
 {
 	protocol_t *protocol;
 
+	if (proto_id == -1)
+		return "(none)";
 	protocol = find_protocol_by_id(proto_id);
 	return protocol->short_name;
 }
@@ -1643,6 +1647,32 @@
 
 	protocol = find_protocol_by_id(proto_id);
 	protocol->can_disable = FALSE;
+}
+
+gpointer
+proto_get_protocol_dissector(int proto_id)
+{
+	protocol_t *protocol;
+
+	protocol = find_protocol_by_id(proto_id);
+	if (protocol == NULL)
+		return(NULL);
+	return protocol->dissector;
+}
+
+void
+proto_set_protocol_dissector(int proto_id, gpointer dissector)
+{
+	protocol_t *protocol;
+
+	protocol = find_protocol_by_id(proto_id);
+	if (protocol != NULL) {
+		if (protocol->dissector != NULL) {
+			/* Already set */
+			return;
+		}
+		protocol->dissector = dissector;
+	}
 }
 
 /* for use with static arrays only, since we don't allocate our own copies
Index: epan/proto.h
===================================================================
RCS file: /cvsroot/ethereal/epan/proto.h,v
retrieving revision 1.3
diff -u -r1.3 proto.h
--- proto.h	2001/01/03 06:55:59	1.3
+++ proto.h	2001/01/19 02:11:52
@@ -525,6 +525,9 @@
 /* Disable disabling of protocol */
 void proto_set_cant_disable(int proto_id);
 
+gpointer proto_get_protocol_dissector(int proto_id);
+void proto_set_protocol_dissector(int proto_id, gpointer dissector);
+
 /* Get length of registered field according to field type.
  * 0 means undeterminable at registration time.
  * -1 means unknown field */
Index: gtk/Makefile.am
===================================================================
RCS file: /cvsroot/ethereal/gtk/Makefile.am,v
retrieving revision 1.32
diff -u -r1.32 Makefile.am
--- Makefile.am	2001/01/02 01:32:21	1.32
+++ Makefile.am	2001/01/19 02:11:52
@@ -39,6 +39,8 @@
 	color_utils.h	\
 	column_prefs.c	\
 	column_prefs.h	\
+ 	decode_as_dlg.c	\
+ 	decode_as_dlg.h	\
 	dfilter_expr_dlg.c \
 	dfilter_expr_dlg.h \
 	display_opts.c	\
Index: gtk/decode_as_dlg.c
===================================================================
RCS file: decode_as_dlg.c
diff -N decode_as_dlg.c
--- /dev/null	Sat Oct  7 15:19:25 2000
+++ decode_as_dlg.c	Thu Jan 18 20:11:53 2001
@@ -0,0 +1,1436 @@
+/* decode_as_dlg.c
+ *
+ * $Id: $
+ *
+ * Routines to modify dissector tables on the fly.
+ *
+ * By David Hampton <dhampton@xxxxxxx>
+ * Copyright 2001 David Hampton
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "decode_as_dlg.h"
+#include "dlg_utils.h"
+#include "globals.h"
+#include "simple_dialog.h"
+#include "packet.h"
+#include "packet-ip.h"
+#include "ui_util.h"
+
+#undef DEBUG
+
+/**************************************************/
+/*                Typedefs & Enums                */
+/**************************************************/
+
+/*
+ * Enum used to track which radio button is currently selected in the
+ * dialog. These buttons are labeled "Decode" and "Do not decode".
+ */
+enum action_type {
+    /* The "Decode" button is currently selected. */
+    E_DECODE_YES,
+
+    /* The "Do not decode" button is currently selected. */
+    E_DECODE_NO
+};
+
+/*
+ * Enum used to track which transport layer protocol menu item is
+ * currently selected in the dialog.  These items are labeled "TCP",
+ * "UDP", and "TCP/UDP".
+ */
+enum tcpudp_type {
+    /* The "TCP" menu item is currently selected. */
+    E_DECODE_TCP,
+
+    /* The "TCP" menu item is currently selected. */
+    E_DECODE_UDP,
+
+    /* The "TCP/UDP" menu item is currently selected. */
+    E_DECODE_TCPUDP
+};
+
+/*
+ * Enum used to track which transport layer port menu item is
+ * currently selected in the dialog.  These items are labeled "source",
+ * "destination", and "source/destination".
+ */
+enum srcdst_type {
+    /* The "source port" menu item is currently selected. */
+    E_DECODE_SPORT,
+    /* The "destination port" menu item is currently selected. */
+    E_DECODE_DPORT,
+    /* The "source/destination port" menu item is currently selected. */
+    E_DECODE_BPORT
+};
+
+#define E_DECODE_MIN_HEIGHT 100
+#define E_NOTEBOOK "notebook"
+
+#define E_MENU_TCPUDP "menu_tcp_udp"
+#define E_MENU_SRCDST "menu_src_dst"
+
+#define E_PAGE_ACTION "notebook_page_action"
+#define E_PAGE_CLIST  "notebook_page_clist"
+#define E_PAGE_TABLE  "notebook_page_table_name"
+#define E_PAGE_TITLE  "notebook_page_title"
+#define E_PAGE_VALUE  "notebook_page_value"
+
+/*
+ * Clist columns for a "Select" clist
+ */
+#define E_CLIST_S_PROTO_NAME 0
+#define E_CLIST_S_TABLE	     1
+#define E_CLIST_S_ISOLD	     2
+/* The following is for debugging in decode_add_to_clist */
+#define E_CLIST_S_ISCONV     3
+#define E_CLIST_S_MAX	     E_CLIST_S_ISCONV
+#define E_CLIST_S_COLUMNS   (E_CLIST_S_MAX + 1)
+
+/*
+ * Clist columns for a "Display" clist
+ */
+#define E_CLIST_D_TABLE	     0
+#define E_CLIST_D_PORT	     1
+#define E_CLIST_D_INITIAL    2
+#define E_CLIST_D_CURRENT    3
+#define E_CLIST_D_MAX	     E_CLIST_D_CURRENT
+#define E_CLIST_D_COLUMNS   (E_CLIST_D_MAX + 1)
+
+/**************************************************/
+/*             File Global Variables              */
+/**************************************************/
+
+/*
+ * Keep a static pointer to the current "Decode As" window.  This is
+ * kept so that if somebody tries to do "Tools:Decode As" while
+ * there's already a "Decode As" window up, we just pop up the
+ * existing one, rather than creating a new one.
+ */
+static GtkWidget *decode_w = NULL;
+
+/*
+ * A static pointer to the current "Decode As:Show" window.  This is
+ * kept so that if somebody tries to do clock the "Show Current"
+ * button or slect the "Display:User Specified Decodes" menu item
+ * while there's already a "Decode As:Show" window up, we just pop up
+ * the existing one, rather than creating a new one.
+ */
+static GtkWidget *decode_show_w = NULL;
+
+/*
+ * A list of the dialog items that only have meaning when the user has
+ * selected the "Decode" radio button.  When the "Do not decode"
+ * button is selected these items should be dimmed.
+ */
+static GSList *decode_dimmable = NULL;
+
+/*
+ * A list of additional IP port numbers that are currently being
+ * decodes as either TCP or UDP.  This is used to determine whether or
+ * not to include a "transport" page in the dialog notebook.  This
+ * list never includes values for the standard TCP or UDP protocol
+ * numbers.
+ */
+static GSList *decode_as_tcpudp = NULL;
+
+/*
+ * Remember the "action" radio button that is currently selected in
+ * the dialog.  This value is initialized when the dialog is created,
+ * modified in a callback routine, and read in the routine that
+ * handles a click in the "OK" button for the dialog.
+ */
+static enum action_type	requested_action = -1;
+
+/**************************************************/
+/*            Resett Changed Dissectors           */
+/**************************************************/
+
+/*
+ * Data structure for tracking which dissector need to be reset.  This
+ * structure is necessary as a hash table entry cannot be removed
+ * while a g_hash_table_foreach walk is in progress.
+ */
+struct dissector_delete_item {
+    /* The name of the dissector table */
+    const gchar *ddi_table_name;
+    /* The port number in the dissector table */
+    gint   ddi_port;
+};
+
+/*
+ * A typedef for the data structure to track the original dissector
+ * used for any given port on any given protocol.
+ */
+typedef struct dissector_delete_item dissector_delete_item_t;
+
+/*
+ * A list of dissectors that need to be reset.
+ */
+GSList *dissector_reset_list = NULL;
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that need to be reset. It is called by the g_hash_table_foreach
+ * routine once for each changed entry in a dissector table.
+ * Unfortunately it cannot delete the entry immediately as this screws
+ * up the foreach function, so it builds a list of dissectors to be
+ * reset once the foreach routine finishes.
+ *
+ * @param table_name The table name in which this dissector is found.
+ *
+ * @param key A pointer to the key for this entry in the dissector
+ * hash table.  This is generally the numeric selector of the
+ * protocol, i.e. the ethernet type code, IP port number, TCP port
+ * number, etc.
+ *
+ * @param value A pointer to the value for this entry in the dissector
+ * hash table.  This is an opaque pointer that can only be handed back
+ * to routine in the file packet.c
+ *
+ * @param user_data Unused.
+ */
+void
+decode_build_reset_list (gchar *table_name, gpointer key,
+			 gpointer value, gpointer user_data)
+{
+    dissector_delete_item_t *item;
+
+    item = g_malloc(sizeof(dissector_delete_item_t));
+    item->ddi_table_name = table_name;
+    item->ddi_port = GPOINTER_TO_INT(key);
+    dissector_reset_list = g_slist_prepend(dissector_reset_list, item);
+}
+
+/*
+ * This routine resets any changed dissectors.  it is called from the
+ * "Decode As" dialog box when the "reset changed" button is pressed.
+ *
+ * This routine uses auxiliary functions to perform the bulk of its
+ * work.  These functions walk the dissector tables and build a list
+ * of dissectors that should be deleted.
+ *
+ * @param w Unknown
+ * @param data Unknown
+ */
+void
+decode_reset_cb (GtkWidget * w, gpointer data)
+{
+    dissector_delete_item_t *item;
+    GSList *tmp;
+    
+    dissector_all_tables_foreach_changed(decode_build_reset_list, NULL);
+
+    for (tmp = dissector_reset_list; tmp; tmp = g_slist_next(tmp)) {
+	item = tmp->data;
+	dissector_reset(item->ddi_table_name, item->ddi_port);
+	g_free(item);
+    }
+    g_slist_free(dissector_reset_list);
+    dissector_reset_list = NULL;
+
+    simple_dialog(ESD_TYPE_INFO, NULL,
+		  "All dissectors have been reset to their default values.");
+
+    redissect_packets(&cfile);
+}
+
+
+/**************************************************/
+/*             Show Changed Dissectors            */
+/**************************************************/
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that have been changed.  It is called by the g_hash_foreach routine
+ * once for each changed entry in a dissector table.
+ *
+ * @param table_name The table name in which this dissector is found.
+ *
+ * @param key A pointer to the key for this entry in the dissector
+ * hash table.  This is generally the numeric selector of the
+ * protocol, i.e. the ethernet type code, IP port number, TCP port
+ * number, etc.
+ *
+ * @param value A pointer to the value for this entry in the dissector
+ * hash table.  This is an opaque pointer that can only be handed back
+ * to routine in the file packet.c
+ *
+ * @param user_data A pointer to the clist in which this information
+ * should be stored.
+ */
+void
+decode_build_show_list (gchar *table_name, gpointer key,
+			gpointer value, gpointer user_data)
+{
+    GtkCList  *clist;
+    gchar     *current_proto_name, *initial_proto_name, *text[E_CLIST_D_COLUMNS];
+    gchar      string1[20];
+    gint       current_proto, initial_proto, row;
+
+    g_assert(user_data);
+    g_assert(value);
+
+    clist = (GtkCList *)user_data;
+    current_proto = dissector_get_proto(value);
+    current_proto_name = proto_get_protocol_short_name(current_proto);
+    initial_proto = dissector_get_initial_proto(value);
+    initial_proto_name = proto_get_protocol_short_name(initial_proto);
+
+    text[E_CLIST_D_TABLE] = table_name;
+    sprintf(string1, "%d", GPOINTER_TO_INT(key));
+    text[E_CLIST_D_PORT] = string1;
+    text[E_CLIST_D_INITIAL] = initial_proto_name;
+    text[E_CLIST_D_CURRENT] = current_proto_name;
+    row = gtk_clist_prepend(clist, text);
+}
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in
+ * the "Decode Show:Show" dialog window.  This routine then destroys the
+ * dialog box and performs other housekeeping functions.
+ *
+ * @param GtkWidget * A pointer to the "OK" button.
+ *
+ * @param gpointer A pointer to the dialog window.
+ */
+static void
+decode_show_ok_cb (GtkWidget *ok_bt, gpointer parent_w)
+{
+    gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+
+/*
+ * This routine is called when the user clicks the "Close" button in
+ * the "Decode As:Show" dialog window.  This routine simply calls the
+ * cancel routine as if the user had clicked the cancel button instead
+ * of the close button.
+ *
+ * @param GtkWidget * A pointer to the dialog box.
+ *
+ * @param gpointer Unknown
+ */
+static gboolean
+decode_show_delete_cb (GtkWidget *decode_w, gpointer dummy)
+{
+    decode_show_ok_cb(NULL, decode_show_w);
+    return FALSE;
+}
+
+
+/*
+ * This routine is called at the destruction of the "Decode As:Show"
+ * dialog box.  It clears the pointer maintained by this file, so that
+ * the next time the user clicks the "Decode As:Show" button a new
+ * dialog box will be created.
+ *
+ * @param GtkWidget * A pointer to the dialog box.
+ *
+ * @param gpointer Unknown
+ */
+static void
+decode_show_destroy_cb (GtkWidget *win, gpointer user_data)
+{
+    /* Note that we no longer have a "Decode As:Show" dialog box. */
+    decode_show_w = NULL;
+}
+
+
+/*
+ * This routine creates the "Decode As:Show" dialog box. This dialog box
+ * shows the user which protocols have had their dissectors changed.
+ *
+ * @param w Unknown
+ * @param data Unknown
+ */
+void
+decode_show_cb (GtkWidget * w, gpointer data)
+{
+    GtkWidget *main_vb, *bbox, *ok_bt, *scrolled_window;
+    GtkCList  *clist;
+    gchar     *titles[E_CLIST_D_COLUMNS] = {"Table", "Port", "Initial", "Current"};
+    gint       column;
+
+    if (decode_show_w != NULL) {
+	/* There's already a "Decode As" dialog box; reactivate it. */
+	reactivate_window(decode_show_w);
+	return;
+    }
+
+    decode_show_w = dlg_window_new("Ethereal: Decode As: Show");
+    gtk_signal_connect(GTK_OBJECT(decode_show_w), "delete_event",
+		       GTK_SIGNAL_FUNC(decode_show_delete_cb), NULL);
+    gtk_signal_connect(GTK_OBJECT(decode_show_w), "destroy",
+		       GTK_SIGNAL_FUNC(decode_show_destroy_cb), NULL);
+  
+    /* Container for each row of widgets */
+    main_vb = gtk_vbox_new(FALSE, 2);
+    gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+    gtk_container_add(GTK_CONTAINER(decode_show_w), main_vb);
+
+    {
+	/* Initialize clist */
+	clist = GTK_CLIST(gtk_clist_new_with_titles(E_CLIST_D_COLUMNS, titles));
+	gtk_clist_column_titles_passive(clist);
+	for (column = 0; column < E_CLIST_D_COLUMNS; column++)
+	    gtk_clist_set_column_auto_resize(clist, column, TRUE);
+	gtk_clist_set_selection_mode(clist, GTK_SELECTION_EXTENDED);
+
+	/* Add data */
+	dissector_all_tables_foreach_changed(decode_build_show_list, clist);
+	gtk_clist_sort(clist);
+
+	/* Put clist into a scrolled window */
+	scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+				       GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
+					      GTK_WIDGET(clist));
+	gtk_box_pack_start(GTK_BOX(main_vb), scrolled_window, TRUE, TRUE, 0);
+	/* Provide a minimum of a couple of rows worth of data */
+	gtk_widget_set_usize(scrolled_window, 0, E_DECODE_MIN_HEIGHT);
+    }
+
+    /* Button row: OK and cancel buttons */
+    bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+    gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 10);
+
+    ok_bt = gtk_button_new_with_label("OK");
+    gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
+		       GTK_SIGNAL_FUNC(decode_show_ok_cb),
+		       GTK_OBJECT(decode_show_w));
+    GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(bbox), ok_bt, FALSE, FALSE, 0);
+    gtk_widget_grab_default(ok_bt);
+    dlg_set_cancel(decode_show_w, ok_bt);
+
+    gtk_widget_show_all(decode_show_w);
+}
+
+
+/**************************************************/
+/*         Modify the dissector routines          */
+/**************************************************/
+
+/*
+ * Modify a single dissector.  This routine first takes care of
+ * updating the internal table of original protocol/port/dissector
+ * combinations by adding a new entry (or removing an existing entry
+ * if the value is being set back to its default).  This routine then
+ * performs the actual modification to the packet dissector tables.
+ *
+ * @param s Pointer to a string buffer.  This buffer is used to build
+ * up a message indicating which ports have had their dissector
+ * changed. This output will be displayed all at once after all
+ * dissectors have been modified.
+ *
+ * @param table_name The table name in which the dissector should be
+ * modified.
+ *
+ * @param selector An enum value indication which selector value
+ * (i.e. IP protocol number, TCP port number, etc.)is to be changed.
+ *
+ * @param clist The CList in which all the selection information can
+ * be found.
+ *
+ * @return gchar * Pointer to the next free location in the string
+ * buffer.
+ */
+static gchar *
+decode_change_one_dissector (gchar *s, gchar *table_name, gint selector,
+			     GtkCList *clist)
+{
+    dissector_t  dissector;
+    gboolean     old;
+    gchar       *abbrev, *oldstring;
+    gint         row, proto_num;
+
+    if (!clist->selection) {
+	proto_num = -1;
+	abbrev = "(NULL)";
+	old = FALSE;
+	dissector = NULL;
+    } else {
+	row = GPOINTER_TO_INT(clist->selection->data);
+	proto_num = GPOINTER_TO_INT(gtk_clist_get_row_data(clist, row));
+	gtk_clist_get_text(clist, row, E_CLIST_S_PROTO_NAME, &abbrev);
+	gtk_clist_get_text(clist, row, E_CLIST_S_ISOLD, &oldstring);
+	old = (strcmp(oldstring, "TRUE") == 0);
+	dissector = proto_get_protocol_dissector(proto_num);
+	if ((proto_num != -1) && (dissector == NULL)) {
+	    simple_dialog(ESD_TYPE_CRIT, NULL,
+			  "Protocol dissector structure disappeared");
+	    return(s);
+	}
+    }
+
+    if (strcmp(abbrev, "(default)") == 0) {
+	dissector_reset(table_name, selector);
+	s += sprintf(s, "Reset %s port %5d.\n", table_name, selector);
+    } else {
+	dissector_change(table_name, selector, dissector, old, proto_num);
+	if (dissector != NULL) {
+	    s += sprintf(s, "Decoding %s table entry %5d as %s.\n",
+			 table_name, selector, abbrev);
+	} else {
+	    s += sprintf(s, "Not decoding %s table entry %5d.\n",
+			 table_name, selector);
+	}
+    }
+    return(s);
+}
+
+
+
+/**************************************************/
+/* Action routines for the "Decode As..." dialog  */
+/*   - called when the OK button pressed          */
+/*   - one per notebook page                      */
+/**************************************************/
+
+
+#ifdef DEBUG
+/*
+ * Print debugging information about clist selection.  Extract all
+ * information from the clist entry that was selected and print it to
+ * a dialog window.
+ *
+ * @param clist The clist to dump.
+ *
+ * @param leadin A string to print at the start of each line.
+ */
+void
+decode_debug (GtkCList *clist, gchar *leadin)
+{
+    gchar *string, *text[E_CLIST_S_MAX];
+    gint row, proto_num;
+
+    string = g_malloc(1024);
+    if (clist->selection) {
+	row = GPOINTER_TO_INT(clist->selection->data);
+	gtk_clist_get_text(clist, row, E_CLIST_S_PROTO_NAME, &text[E_CLIST_S_PROTO_NAME]);
+	gtk_clist_get_text(clist, row, E_CLIST_S_TABLE, &text[E_CLIST_S_TABLE]);
+	gtk_clist_get_text(clist, row, E_CLIST_S_ISOLD, &text[E_CLIST_S_ISOLD]);
+	proto_num = GPOINTER_TO_INT(gtk_clist_get_row_data(clist, row));
+	sprintf(string, "%s clist row %d: proto %d, name %s, table %s, old %s",
+		leadin, row, proto_num, text[E_CLIST_S_PROTO_NAME],
+		text[E_CLIST_S_TABLE], text[E_CLIST_S_ISOLD]);
+    } else {
+	sprintf(string, "%s clist row (none), aka do not decode", leadin);
+    }
+    simple_dialog(ESD_TYPE_INFO, NULL, string);
+    g_free(string);
+}
+#endif
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window and a 'simple' page is foremost.
+ * This routine takes care of making any changes requested to the
+ * dissector tables.  This routine is currently used for IP and
+ * Ethertypes.  Any 'single change' notebook page can use this
+ * routine.
+ *
+ * @param notebook_pg A pointer to the "network" notebook page.
+ */
+static void
+decode_simple (GtkWidget *notebook_pg)
+{
+    GtkCList *clist;
+    gchar *string, *table_name;
+    gint value;
+
+    clist = GTK_CLIST(gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_CLIST));
+    if (requested_action == E_DECODE_NO)
+	gtk_clist_unselect_all(clist);
+
+#ifdef DEBUG
+    string = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TITLE);
+    decode_debug(clist, string);
+#endif
+
+    string = g_malloc(1024);
+    table_name = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TABLE);
+    value = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(notebook_pg),
+						E_PAGE_VALUE));
+    decode_change_one_dissector(string, table_name, value, clist);
+    simple_dialog(ESD_TYPE_INFO, NULL, string);
+    g_free(string);
+}
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window and the network page is foremost.
+ * This routine takes care of making any changes requested to the
+ * dissector tables.  This routine uses the decode_simple() routine to
+ * perform the heavy lifting, and then updates a list of protocol that
+ * are being decoded as TCP/UDP. *
+ *
+ * @param notebook_pg A pointer to the "network" notebook page.
+ */
+static void
+decode_network (GtkWidget *notebook_pg)
+{
+    GtkCList *clist;
+    GSList *item;
+    gint row, assigned, port_num;
+
+    /* Do the real work */
+    decode_simple(notebook_pg);
+
+    /* Now tweak a local table of protocol ids currently decoded as TCP/UDP */
+    port_num = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(notebook_pg),
+						E_PAGE_VALUE));
+    clist = GTK_CLIST(gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_CLIST));
+    row = GPOINTER_TO_INT(clist->selection->data);
+    assigned = GPOINTER_TO_INT(gtk_clist_get_row_data(clist, row));
+
+    /* Ignore changes to the normal TCP and UDP protocol numbers */
+    if ((port_num == IP_PROTO_TCP) || (port_num == IP_PROTO_UDP))
+	return;
+
+    /* Not decoding - remove any entry for this IP protocol number */
+    if (requested_action == E_DECODE_NO) {
+	decode_as_tcpudp =
+	    g_slist_remove(decode_as_tcpudp, GINT_TO_POINTER(port_num));
+	return;
+    }
+
+    /* Not assigning TCP or UDP - remove any entry for this IP protocol number */
+    if ((assigned != IP_PROTO_TCP) && (assigned != IP_PROTO_UDP)) {
+	decode_as_tcpudp =
+	    g_slist_remove(decode_as_tcpudp, GINT_TO_POINTER(port_num));
+	return;
+    }
+
+    /* Assigning TCP or UDP - add if not already present */
+    item = g_slist_find(decode_as_tcpudp, GINT_TO_POINTER(port_num));
+    if (!item) {
+	decode_as_tcpudp =
+	    g_slist_prepend(decode_as_tcpudp, GINT_TO_POINTER(port_num));
+    }
+}
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window and the transport page is foremost.
+ * This routine takes care of making any changes requested to the TCP
+ * and UDP dissector tables.
+ *
+ * Note: The negative tests catch multiple cases.  For example, if the
+ * user didn't select UDP, then they either selected TCP or TCP/UDP.
+ * Either way they *did* select TCP.
+ *
+ * @param notebook_pg A pointer to the "transport" notebook page.
+ */
+static void
+decode_transport (GtkObject *notebook_pg)
+{
+    GtkWidget *menu, *menuitem;
+    GtkCList *clist;
+    gint requested_tcpudp, requested_srcdst;
+    gchar *s, *string;
+
+    clist = GTK_CLIST(gtk_object_get_data(notebook_pg, E_PAGE_CLIST));
+    if (requested_action == E_DECODE_NO)
+	gtk_clist_unselect_all(clist);
+
+    menu = gtk_object_get_data(notebook_pg, E_MENU_TCPUDP);
+    menuitem = gtk_menu_get_active(GTK_MENU(menu));
+    requested_tcpudp = GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(menuitem)));
+
+    menu = gtk_object_get_data(notebook_pg, E_MENU_SRCDST);
+    menuitem = gtk_menu_get_active(GTK_MENU(menu));
+    requested_srcdst = GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(menuitem)));
+
+#ifdef DEBUG
+    string = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TITLE);
+    decode_debug(clist, string);
+#endif
+
+    string = s = g_malloc(1024);
+    if (requested_tcpudp != E_DECODE_UDP) {
+	if (requested_srcdst != E_DECODE_DPORT)
+	    s = decode_change_one_dissector(s, "tcp.port", pi.srcport, clist);
+	if (requested_srcdst != E_DECODE_SPORT)
+	    s = decode_change_one_dissector(s, "tcp.port", pi.destport, clist);
+    }
+    if (requested_tcpudp != E_DECODE_TCP) {
+	if (requested_srcdst != E_DECODE_DPORT)
+	    s = decode_change_one_dissector(s, "udp.port", pi.srcport, clist);
+	if (requested_srcdst != E_DECODE_SPORT)
+	    s = decode_change_one_dissector(s, "udp.port", pi.destport, clist);
+    }
+    simple_dialog(ESD_TYPE_INFO, NULL, string);
+    g_free(string);
+}
+
+/**************************************************/
+/*      Signals from the "Decode As..." dialog    */
+/**************************************************/
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window.  This routine calls various helper
+ * routines to set/clear dissector values as requested by the user.
+ * These routines accumulate information on what actions they have
+ * taken, and this summary information is printed by this routine.
+ * This routine then destroys the dialog box and performs other
+ * housekeeping functions.
+ *
+ * @param ok_bt A pointer to the "OK" button.
+ *
+ * @param parent_w A pointer to the dialog window.
+ */
+static void
+decode_ok_cb (GtkWidget *ok_bt, gpointer parent_w)
+{
+    GtkWidget *notebook, *notebook_pg;
+    GtkSignalFunc func;
+    gint page_num;
+
+    /* Call the right routine for the page that was currently in front. */
+    notebook =  gtk_object_get_data(GTK_OBJECT(parent_w), E_NOTEBOOK);
+    page_num = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
+    notebook_pg = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);
+
+    func = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_ACTION);
+    func(notebook_pg);
+
+    /* Now destroy the "Decode As" dialog. */
+    gtk_widget_destroy(GTK_WIDGET(parent_w));
+    g_slist_free(decode_dimmable);
+    decode_dimmable = NULL;
+
+    redissect_packets(&cfile);
+}
+
+
+/*
+ * This routine is called when the user clicks the "Cancel" button in
+ * the "Decode As..." dialog window.  This routine then destroys the
+ * dialog box and performs other housekeeping functions.
+ *
+ * @param cancel_bt A pointer to the "Cancel" button.
+ *
+ * @param parent_w A pointer to the dialog window.
+ */
+static void
+decode_cancel_cb (GtkWidget *cancel_bt, gpointer parent_w)
+{
+    gtk_widget_destroy(GTK_WIDGET(parent_w));
+    g_slist_free(decode_dimmable);
+    decode_dimmable = NULL;
+}
+
+
+/*
+ * This routine is called when the user clicks the "Close" button in
+ * the "Decode As..." dialog window.  This routine simply calls the
+ * cancel routine as if the user had clicked the cancel button instead
+ * of the close button.
+ *
+ * @param decode_w A pointer to the dialog box.
+ *
+ * @param dummy Unknown
+ */
+static gboolean
+decode_delete_cb (GtkWidget *decode_w, gpointer dummy)
+{
+    decode_cancel_cb(NULL, decode_w);
+    return FALSE;
+}
+
+
+/*
+ * This routine is called at the destruction of the "Decode As..."
+ * dialog box.  It clears the pointer maintained by this file, so that
+ * the next time the user selects the "Decode As..." menu item a new
+ * dialog box will be created.
+ *
+ * @param decode_w A pointer to the dialog box.
+ *
+ * @param user_data Unknown
+ *
+ * @return void
+ */
+static void
+decode_destroy_cb (GtkWidget *win, gpointer user_data)
+{
+    /* Note that we no longer have a "Decode As" dialog box. */
+    decode_w = NULL;
+}
+
+
+/**************************************************/
+/*          Dialog setup - radio buttons          */
+/**************************************************/
+
+/*
+ * Update the requested action field of the dialog.  This routine is
+ * called by GTK when either of the two radio buttons in the dialog is
+ * clicked.
+ *
+ * @param w The radio button that was clicked.
+ *
+ * @param data The enum value assigned to this radio button.  This
+ * will be either E_DECODE_YES or E_DECODE_NO
+ */
+static void
+decode_update_action (GtkWidget *w, gpointer data)
+{
+    GSList *tmp;
+    gboolean enable;
+
+    requested_action = GPOINTER_TO_INT(data);
+    enable = (requested_action == E_DECODE_YES);
+    for (tmp = decode_dimmable; tmp; tmp = g_slist_next(tmp)) {
+	gtk_widget_set_sensitive(tmp->data, enable);
+    }
+}
+
+/*
+ * This routine is called to create the "Decode" and "No not decode"
+ * radio buttons.  These buttons are installed into a vbox, and set up
+ * as a format group.
+ *
+ * @return GtkWidget * A pointer to the vbox containing the buttons
+ */
+static GtkWidget *
+decode_add_yes_no (void)
+{
+    GtkWidget	*format_vb, *radio_button;
+    GSList	*format_grp;
+
+    format_vb = gtk_vbox_new(FALSE, 2);
+
+    radio_button = gtk_radio_button_new_with_label(NULL, "Decode");
+    format_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(radio_button));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button), TRUE);
+    gtk_signal_connect(GTK_OBJECT(radio_button), "clicked",
+		       GTK_SIGNAL_FUNC(decode_update_action),
+		       GINT_TO_POINTER(E_DECODE_YES));
+    gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
+
+    radio_button = gtk_radio_button_new_with_label(format_grp, "No not decode");
+    format_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(radio_button));
+    gtk_signal_connect(GTK_OBJECT(radio_button), "clicked",
+		       GTK_SIGNAL_FUNC(decode_update_action),
+		       GINT_TO_POINTER(E_DECODE_NO));
+    gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
+
+    return(format_vb);
+}
+
+/**************************************************/
+/*          Dialog setup - simple menus           */
+/**************************************************/
+
+/*
+ * This routine is called to add the transport protocol selection menu
+ * to the dialog box.  This is a three choice menu: TCP, UDP, and
+ * TCP/UDP.  The default choice for the menu is set to the transport
+ * layer protocol of the currently selected packet.
+ *
+ * @param page A pointer notebook page that will contain all
+ * widgets created by this routine.
+ *
+ * @return GtkWidget * A pointer to the newly created option menu.
+ */
+static GtkWidget *
+decode_add_tcpudp_menu (GtkWidget *page)
+{
+    GtkWidget *optmenu, *menu, *menuitem;
+    gint requested_tcpudp;
+
+    optmenu = gtk_option_menu_new();
+    menu = gtk_menu_new();
+    menuitem = gtk_menu_item_new_with_label("TCP");
+    gtk_object_set_user_data(GTK_OBJECT(menuitem),
+			     GINT_TO_POINTER(E_DECODE_TCP));
+    gtk_menu_append(GTK_MENU(menu), menuitem);
+    gtk_widget_show(menuitem);	/* gtk_widget_show_all() doesn't show this */
+
+    menuitem = gtk_menu_item_new_with_label("UDP");
+    gtk_object_set_user_data(GTK_OBJECT(menuitem),
+			     GINT_TO_POINTER(E_DECODE_UDP));
+    gtk_menu_append(GTK_MENU(menu), menuitem);
+    gtk_widget_show(menuitem);	/* gtk_widget_show_all() doesn't show this */
+
+    menuitem = gtk_menu_item_new_with_label("TCP/UDP");
+    gtk_object_set_user_data(GTK_OBJECT(menuitem),
+			     GINT_TO_POINTER(E_DECODE_TCPUDP));
+    gtk_menu_append(GTK_MENU(menu), menuitem);
+    gtk_widget_show(menuitem);	/* gtk_widget_show_all() doesn't show this */
+
+    requested_tcpudp = (pi.ipproto == IP_PROTO_TCP) ? E_DECODE_TCP : E_DECODE_UDP;
+    gtk_menu_set_active(GTK_MENU(menu), requested_tcpudp == E_DECODE_UDP);
+    gtk_object_set_data(GTK_OBJECT(page), E_MENU_TCPUDP, menu);
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
+
+    return(optmenu);
+}
+
+
+/*
+ * This routine is called to add the transport port selection menu to
+ * the dialog box.  This is a three choice menu: source, destination
+ * and both.  The default choice for the menu is set to the source
+ * port number of the currently selected packet.
+ *
+ * @param page A pointer notebook page that will contain all
+ * widgets created by this routine.
+ *
+ * @return GtkWidget * A pointer to the newly created option menu.
+ */
+static GtkWidget *
+decode_add_srcdst_menu (GtkWidget *page)
+{
+    GtkWidget *optmenu, *menu, *menuitem;
+    gchar      tmp[100];
+
+    optmenu = gtk_option_menu_new();
+    menu = gtk_menu_new();
+    sprintf(tmp, "source (%d)", pi.srcport);
+    menuitem = gtk_menu_item_new_with_label(tmp);
+    gtk_object_set_user_data(GTK_OBJECT(menuitem),
+			     GINT_TO_POINTER(E_DECODE_SPORT));
+    gtk_menu_append(GTK_MENU(menu), menuitem);
+    gtk_widget_show(menuitem);	/* gtk_widget_show_all() doesn't show this */
+
+    sprintf(tmp, "destination (%d)", pi.destport);
+    menuitem = gtk_menu_item_new_with_label(tmp);
+    gtk_object_set_user_data(GTK_OBJECT(menuitem),
+			     GINT_TO_POINTER(E_DECODE_DPORT));
+    gtk_menu_append(GTK_MENU(menu), menuitem);
+    gtk_widget_show(menuitem);	/* gtk_widget_show_all() doesn't show this */
+
+    menuitem = gtk_menu_item_new_with_label("both");
+    gtk_object_set_user_data(GTK_OBJECT(menuitem),
+			     GINT_TO_POINTER(E_DECODE_BPORT));
+    gtk_menu_append(GTK_MENU(menu), menuitem);
+    gtk_widget_show(menuitem);	/* gtk_widget_show_all() doesn't show this */
+
+    gtk_object_set_data(GTK_OBJECT(page), E_MENU_SRCDST, menu);
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
+
+    return(optmenu);
+}
+
+/**************************************************/
+/*        Dialog setup - clist based menus        */
+/**************************************************/
+
+
+typedef struct decode_build_clist_info {
+    GtkCList *clist;
+    gboolean conv;
+} decode_build_clist_info_t;
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that can be used.  It is called by the g_hash_foreach routine once
+ * for each entry in a dissector table.  It guarantees unique entries
+ * by iterating over the list of entries build up to this point,
+ * looking for a duplicate name.  If there is no duplicate, then this
+ * entry is added to the list of possible dissectors.
+ *
+ * @param table_name The name of the dissector hash table currently
+ * being walked.
+ *
+ * @param key A pointer to the key for this entry in the
+ * dissector hash table.  This is generally the numeric selector of
+ * the protocol, i.e. the ethernet type code, IP port number, TCP port
+ * number, etc.
+ *
+ * @param value A pointer to the value for this entry in the
+ * dissector hash table.  This is an opaque pointer that can only be
+ * handed back to routines in the file packet.c
+ *
+ * @param user_data A data block passed into each instance of this
+ * routine.  It contains information from the caller of the foreach
+ * routine, specifying information about the dissector table and where
+ * to store any information generated by this routine.
+ */
+void decode_add_to_clist (gchar *table_name, gpointer key,
+			  gpointer value, gpointer user_data)
+{
+    GtkCList  *clist;
+    gchar     *proto_name, *isold, *isconv;
+    gchar     *text[E_CLIST_S_MAX];
+    gint proto, row;
+    decode_build_clist_info_t *info;
+
+    g_assert(user_data);
+    g_assert(value);
+
+    info = user_data;
+    clist = info->clist;
+    if (info->conv) {
+	proto = conv_dissector_get_proto(value);
+	isold = conv_dissector_get_old_flag(value) ? "TRUE" : "FALSE";
+	isconv = "TRUE";
+    } else {
+	proto = dissector_get_proto(value);
+	isold = dissector_get_old_flag(value) ? "TRUE" : "FALSE";
+	isconv = "FALSE";
+    }
+    proto_name = proto_get_protocol_short_name(proto);
+
+    row = gtk_clist_find_row_from_data(clist, GINT_TO_POINTER(proto));
+    if (row != -1) {
+	return;
+    }
+
+    text[E_CLIST_S_PROTO_NAME] = proto_name;
+    text[E_CLIST_S_TABLE] = table_name;
+    text[E_CLIST_S_ISOLD] = isold;
+    text[E_CLIST_S_ISCONV] = isconv;
+    row = gtk_clist_prepend(clist, text);
+    gtk_clist_set_row_data(clist, row, GINT_TO_POINTER(proto));
+}
+
+
+/*
+ * This routine starts the creation of a CList on a notebook page.  It
+ * creates both a scrolled window and a clist, adds the clist to the
+ * window, and attaches the clist as a data object on the page.
+ *
+ * @param page A pointer to the notebook page being created.
+ *
+ * @param clist_p Will be filled in with the address of a newly
+ * created CList.
+ *
+ * @param scrolled_win_p Will be filled in with the address of a newly
+ * created GtkScrolledWindow.
+ */
+void
+decode_clist_menu_start (GtkWidget *page, GtkCList **clist_p,
+			 GtkWidget **scrolled_win_p)
+{
+    gchar *titles[E_CLIST_S_COLUMNS] = {"Short Name", "Table Name",
+					"Is Old", "Is Conversation"};
+    GtkCList  *clist;
+    GtkWidget *window;
+    gint column;
+
+    *clist_p = clist =
+	GTK_CLIST(gtk_clist_new_with_titles(E_CLIST_S_COLUMNS, titles));
+    gtk_clist_column_titles_passive(clist);
+#ifndef DEBUG
+    gtk_clist_column_titles_hide(clist);
+    for (column = 1; column < E_CLIST_S_COLUMNS; column++)
+	gtk_clist_set_column_visibility (clist, column, FALSE);
+#endif
+    for (column = 0; column < E_CLIST_S_COLUMNS; column++)
+	gtk_clist_set_column_auto_resize(clist, column, TRUE);
+    gtk_object_set_data(GTK_OBJECT(page), E_PAGE_CLIST, clist);
+
+    *scrolled_win_p = window = gtk_scrolled_window_new(NULL, NULL);
+    /* Provide a minimum of a couple of rows worth of data */
+    gtk_widget_set_usize(window, 0, E_DECODE_MIN_HEIGHT);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(window),
+				   GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(window),
+					  GTK_WIDGET(clist));
+}
+
+/*
+ * This routine finishes the creation of a CList on a notebook page.
+ * It adds the default entry, sets the default entry as the
+ * highlighted entry, and sorts the CList.
+ *
+ * @param clist A pointer the the CList to finish.
+ */
+void
+decode_clist_menu_finish (GtkCList *clist)
+{
+    gchar *text[E_CLIST_S_COLUMNS];
+    gint row;
+
+    text[E_CLIST_S_PROTO_NAME] = "(default)";
+    text[E_CLIST_S_TABLE] = "(none)";
+    text[E_CLIST_S_ISOLD] = "(who cares)";
+    text[E_CLIST_S_ISCONV] = "(who cares)";
+    row = gtk_clist_prepend(clist, text);
+    gtk_clist_set_row_data(clist, row, GINT_TO_POINTER(-1));
+
+    gtk_clist_select_row(clist, 0, -1);
+    gtk_clist_sort(clist);
+}
+
+/*
+ * This routine is called to add the dissector selection list to a
+ * notebook page.  This scrolled list contains an entry labeled
+ * "default", and an entry for each protocol that has had a dissector
+ * registered.  The default choice for the list is set to the
+ * "default" choice, which will return the protocol/port selections to
+ * their original dissector(s).
+ *
+ * @param page A pointer to the notebook page currently being created.
+ *
+ * @param table_name The name of the dissector hash table to use to
+ * build this (clist) menu.
+ *
+ * @return GtkWidget * A pointer to the newly created clist within a
+ * scrolled window.
+ */
+static GtkWidget *
+decode_add_simple_menu (GtkWidget *page, gchar *table_name)
+{
+    GtkWidget *scrolled_window;
+    GtkCList  *clist;
+    decode_build_clist_info_t info;
+
+    decode_clist_menu_start(page, &clist, &scrolled_window);
+    {
+	info.clist = clist;
+	info.conv = FALSE;
+	dissector_table_foreach(table_name, decode_add_to_clist, &info);
+    }
+    decode_clist_menu_finish(clist);
+    return(scrolled_window);
+}
+
+/*
+ * This routine is called to add the dissector selection list to 
+ * notebook page.  This scrolled list contains an entry labeled
+ * "default", and an entry for each protocol that has had a dissector
+ * registered.  The default choice for the list is set to the
+ * "default" choice, which will return the protocol/port selections to
+ * their original dissector(s).
+ *
+ * @param page A pointer to the notebook page currently being created.
+ *
+ * @return GtkWidget * A pointer to the newly created option menu.
+ */
+static GtkWidget *
+decode_add_transport_menu (GtkWidget *page)
+{
+    GtkWidget *scrolled_window;
+    GtkCList  *clist;
+    decode_build_clist_info_t info;
+
+    decode_clist_menu_start(page, &clist, &scrolled_window);
+    {
+	info.clist = clist;
+	info.conv = FALSE;
+	dissector_table_foreach("tcp.port", decode_add_to_clist, &info);
+	dissector_table_foreach("udp.port", decode_add_to_clist, &info);
+
+	info.conv = TRUE;
+	dissector_conv_foreach("udp", decode_add_to_clist, &info);
+    }
+    decode_clist_menu_finish(clist);
+    return(scrolled_window);
+}
+
+/**************************************************/
+/*                  Dialog setup                  */
+/**************************************************/
+
+/*
+ * This routine creates a somple notebook page ni the dialog box.
+ * This notebook page provides a promp specifying what is being
+ * changed and its current value (e.g. "IP Protocol number (17)"), and
+ * a clist specifying all the available choices.  The list of choices
+ * is conditionally enabled, based upon the setting of the
+ * "decode"/"do not decode" radio buttons.
+ *
+ * @param prompt The prompt for this notebook page
+ *
+ * @param title A table name from which all dissector names will
+ * be extracted.
+ *
+ * @param table_name The name of the dissector hash table to use to
+ * build this page.
+ *
+ * @param value The protocol/port value that is to be changed.
+ *
+ * @return GtkWidget * A pointer to the notebook page created by this
+ * routine.
+ */
+static GtkWidget *
+decode_add_simple_page (gchar *prompt, gchar *title, gchar *table_name,
+			gint value)
+{
+    GtkWidget	*page, *label, *scrolled_window;
+
+    page = gtk_hbox_new(FALSE, 5);
+    gtk_object_set_data(GTK_OBJECT(page), E_PAGE_ACTION, decode_simple);
+    gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TABLE, table_name);
+    gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TITLE, title);
+    gtk_object_set_data(GTK_OBJECT(page), E_PAGE_VALUE, GINT_TO_POINTER(value));
+
+    /* Always enabled */
+    label = gtk_label_new(prompt);
+    gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+
+    /* Conditionally enabled - only when decoding packets */
+    label = gtk_label_new("as");
+    gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+    decode_dimmable = g_slist_prepend(decode_dimmable, label);
+    scrolled_window = decode_add_simple_menu(page, table_name);
+    gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+    decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
+
+    return(page);
+}
+
+
+/*
+ * This routine creates the TCP/UDP notebook page in the dialog box.
+ * All items created by this routine are packed into a single
+ * horizontal box.  First is a menu allowing the user to select the
+ * TCP or UDP transport layer protocol.  Second is a menu allowing the
+ * user to select whether the source port, destination port, or both
+ * ports will have dissectors added for them.  Last is a
+ * (conditionally enabled) popup menu listing all possible dissectors
+ * that can be used to decode the packets, and the choice or returning
+ * to the default dissector for these ports.
+ *
+ * The defaults for these items are the transport layer protocol of
+ * the currently selected packet, the source port of the currently
+ * selected packet, and the "default dissector".
+ *
+ * @return GtkWidget * A pointer to the notebook page created by
+ * this routine.
+ */
+static GtkWidget *
+decode_add_tcpudp_page (void)
+{
+    GtkWidget	*page, *label, *scrolled_window, *optmenu;
+
+    page = gtk_hbox_new(FALSE, 5);
+    gtk_object_set_data(GTK_OBJECT(page), E_PAGE_ACTION, decode_transport);
+    gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TITLE, "Transport");
+
+    /* Always enabled */
+    optmenu = decode_add_tcpudp_menu(page);
+    gtk_box_pack_start(GTK_BOX(page), optmenu, TRUE, TRUE, 0);
+    optmenu = decode_add_srcdst_menu(page);
+    gtk_box_pack_start(GTK_BOX(page), optmenu, TRUE, TRUE, 0);
+    label = gtk_label_new("port(s)");
+    gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+
+    /* Conditionally enabled - only when decoding packets */
+    label = gtk_label_new("as");
+    gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+    decode_dimmable = g_slist_prepend(decode_dimmable, label);
+    scrolled_window = decode_add_transport_menu(page);
+    gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+    decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
+
+    return(page);
+}
+
+/*
+ * Indicate if a transport page should be included, based upon the iP
+ * protocol number.
+ *
+ * @param ip_protocol The IP protocol number in question.
+ *
+ * @return gboolean TRUE if this protocol is being decoded as TCP or
+ * UDP.
+ */
+gboolean
+decode_as_transport_ok (gint ip_protocol)
+{
+    if ((ip_protocol == IP_PROTO_TCP) || (ip_protocol == IP_PROTO_UDP))
+	return(TRUE);
+
+    if (g_slist_find(decode_as_tcpudp, GINT_TO_POINTER(ip_protocol)))
+	return(TRUE);
+    return(FALSE);
+}
+
+
+/*
+ * This routine creates the bulk of the "Decode As" dialog box.  All
+ * items created by this routine are packed as pages into a notebook.
+ * There will be a page for each protocol layer that can be change.
+ *
+ * @param GtkWidget * A pointer to the widget in which the notebook
+ * should be installed.
+ */
+void
+decode_add_notebook (GtkWidget *format_hb)
+{
+    GtkWidget *notebook, *page, *label;
+    gchar buffer[40];
+
+    /* Start a nootbook for flipping between sets of changes */
+    notebook = gtk_notebook_new();
+    gtk_container_add(GTK_CONTAINER(format_hb), notebook);
+    gtk_object_set_data(GTK_OBJECT(decode_w), E_NOTEBOOK, notebook);
+
+    /* Add link level selection page */
+    if (pi.ethertype) {
+	sprintf(buffer, "Ethertype %d", pi.ethertype);
+	page = decode_add_simple_page(buffer, "Link", "ethertype", pi.ethertype);
+	label = gtk_label_new("Link");
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+    }
+
+    /* Add network selection page */
+    if (pi.ipproto) {
+	sprintf(buffer, "IP protocol %d", pi.ipproto);
+	page = decode_add_simple_page(buffer, "Network", "ip.proto", pi.ipproto);
+	gtk_object_set_data(GTK_OBJECT(page), E_PAGE_ACTION, decode_network);
+	label = gtk_label_new("Network");
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+    }
+
+    /* Add transport selection page */
+    if (decode_as_transport_ok(pi.ipproto)) {
+	page = decode_add_tcpudp_page();
+	label = gtk_label_new("Transport");
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+    }
+
+    /* Select the last added page (selects first by default) */
+    /* Notebook must be visible for set_page to work. */
+    gtk_widget_show_all(notebook);
+    gtk_notebook_set_page(GTK_NOTEBOOK(notebook), -1);
+}
+
+
+/*
+ * This routine creates the "Decode As" dialog box. This dialog box
+ * asks the user which protocol to use for decoding the currently
+ * selected packet.  This will affect the last packet that we called a
+ * dissection routine on belongs (this might be the most recently
+ * selected packet, or it might be the last packet in the file).
+ *
+ * This routine uses an auxiliary function to create the bulk of the
+ * dialog box, and then hand crafts the button box at the bottom of
+ * the dialog.
+ *
+ * @param w Unknown
+ * @param data Unknown
+ */
+void
+decode_as_cb (GtkWidget * w, gpointer data)
+{
+    GtkWidget	*main_vb, *format_hb, *bbox, *ok_bt, *cancel_bt, *button;
+    GtkWidget   *button_vb;
+
+    if (decode_w != NULL) {
+	/* There's already a "Decode As" dialog box; reactivate it. */
+	reactivate_window(decode_w);
+	return;
+    }
+
+    requested_action = E_DECODE_YES;
+    decode_w = dlg_window_new("Ethereal: Decode As");
+    gtk_signal_connect(GTK_OBJECT(decode_w), "delete_event",
+		       GTK_SIGNAL_FUNC(decode_delete_cb), NULL);
+    gtk_signal_connect(GTK_OBJECT(decode_w), "destroy",
+		       GTK_SIGNAL_FUNC(decode_destroy_cb), NULL);
+  
+    /* Container for each row of widgets */
+    main_vb = gtk_vbox_new(FALSE, 2);
+    gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+    gtk_container_add(GTK_CONTAINER(decode_w), main_vb);
+
+    /* First row - Buttons and Notebook */
+    {
+	format_hb = gtk_hbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(main_vb), format_hb, TRUE, TRUE, 10);
+
+	button_vb = decode_add_yes_no();
+	gtk_box_pack_start(GTK_BOX(format_hb), button_vb, TRUE, TRUE, 10);
+
+	decode_add_notebook(format_hb);
+    }
+
+    /* Button row: OK and cancel buttons */
+    bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+    gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 10);
+
+    ok_bt = gtk_button_new_with_label("OK");
+    gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
+		       GTK_SIGNAL_FUNC(decode_ok_cb),
+		       GTK_OBJECT(decode_w));
+    GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(bbox), ok_bt, FALSE, FALSE, 0);
+    gtk_widget_grab_default(ok_bt);
+
+    button = gtk_button_new_with_label("Show Current");
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       GTK_SIGNAL_FUNC(decode_show_cb),
+		       GTK_OBJECT(decode_w));
+    GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+
+    button = gtk_button_new_with_label("Reset Changes");
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       GTK_SIGNAL_FUNC(decode_reset_cb),
+		       GTK_OBJECT(decode_w));
+    GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+
+    cancel_bt = gtk_button_new_with_label("Cancel");
+    gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
+		       GTK_SIGNAL_FUNC(decode_cancel_cb),
+		       GTK_OBJECT(decode_w));
+    GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(bbox), cancel_bt, FALSE, FALSE, 0);
+
+    /*
+     * Catch the "key_press_event" signal in the window, so that
+     * we can catch the ESC key being pressed and act as if the
+     * "Cancel" button had been selected.
+     */
+    dlg_set_cancel(decode_w, cancel_bt);
+
+    gtk_widget_show_all(decode_w);
+}
+
+
+/*
+ * Local Variables:
+ * mode:c
+ * c-basic-offset: 4
+ * End:
+ */
Index: gtk/decode_as_dlg.h
===================================================================
RCS file: decode_as_dlg.h
diff -N decode_as_dlg.h
--- /dev/null	Sat Oct  7 15:19:25 2000
+++ decode_as_dlg.h	Thu Jan 18 20:11:53 2001
@@ -0,0 +1,33 @@
+/* decode_as_dlg.c
+ *
+ * $Id: $
+ *
+ * Routines to modify dissector tables on the fly.
+ *
+ * By David Hampton <dhampton@xxxxxxx>
+ * Copyright 2001 David Hampton
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef __DECODE_AS_DLG_H__
+#define __DECODE_AS_DLG_H__
+
+void decode_as_cb(GtkWidget *, gpointer);
+void decode_show_cb(GtkWidget *, gpointer);
+void decode_as_register_tcpudp(gint);
+
+#endif
Index: gtk/menu.c
===================================================================
RCS file: /cvsroot/ethereal/gtk/menu.c,v
retrieving revision 1.46
diff -u -r1.46 menu.c
--- menu.c	2000/10/19 22:59:24	1.46
+++ menu.c	2001/01/19 02:11:53
@@ -55,6 +55,7 @@
 #include "packet_win.h"
 #include "print.h"
 #include "follow_dlg.h"
+#include "decode_as_dlg.h"
 #include "help_dlg.h"
 #include "proto_dlg.h"
 #include "keys.h"
@@ -139,11 +140,13 @@
   {"/Display/Collapse _All", NULL, GTK_MENU_FUNC(collapse_all_cb), 0, NULL},
   {"/Display/_Expand All", NULL, GTK_MENU_FUNC(expand_all_cb), 0, NULL},
   {"/Display/_Show Packet In New Window", NULL, GTK_MENU_FUNC(new_window_cb), 0, NULL},
+  {"/Display/User Specified Decodes...", NULL, GTK_MENU_FUNC(decode_show_cb), 0, NULL},
   {"/_Tools", NULL, NULL, 0, "<Branch>" },
 #ifdef HAVE_PLUGINS
   {"/Tools/_Plugins...", NULL, GTK_MENU_FUNC(tools_plugins_cmd_cb), 0, NULL},
 #endif
   {"/Tools/_Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL},
+  {"/Tools/_Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL},
 /*  {"/Tools/Graph", NULL, NULL, 0, NULL}, future use */
   {"/Tools/_Summary", NULL, GTK_MENU_FUNC(summary_open_cb), 0, NULL},
   {"/_Help", NULL, NULL, 0, "<LastBranch>" },
@@ -159,6 +162,7 @@
 static GtkItemFactoryEntry packet_list_menu_items[] =
 {
 	{"/Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL},
+	{"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL},
 	{"/Filters...", NULL, GTK_MENU_FUNC(filter_dialog_cb), 0, NULL},
 	{"/<separator>", NULL, NULL, 0, "<Separator>"},
 	{"/Colorize Display...", NULL, GTK_MENU_FUNC(color_display_cb), 0, NULL},
@@ -170,6 +174,7 @@
 static GtkItemFactoryEntry tree_view_menu_items[] =
 {
 	{"/Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL},
+	{"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL},
 	{"/Filters...", NULL, GTK_MENU_FUNC(filter_dialog_cb), 0, NULL},
 	{"/<separator>", NULL, NULL, 0, "<Separator>"},
 	{"/Resolve Name", NULL, GTK_MENU_FUNC(resolve_name_cb), 0, NULL},
@@ -183,6 +188,7 @@
 static GtkItemFactoryEntry hexdump_menu_items[] =
 {
 	{"/Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL},
+	{"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL},
 	{"/Filters...", NULL, GTK_MENU_FUNC(filter_dialog_cb), 0, NULL}
 };
 
@@ -387,6 +393,7 @@
   set_menu_sensitivity("/Display/Show Packet In New Window", have_selected_packet);
   set_menu_sensitivity("/Tools/Follow TCP Stream",
       have_selected_packet ? (pi.ipproto == 6) : FALSE);
+  set_menu_sensitivity("/Tools/Decode As...", have_selected_packet);
   set_menu_sensitivity("/Resolve Name", 
       have_selected_packet && !g_resolving_actif);  
 }