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: Mon, 22 Jan 2001 09:07:51 -0800
Guy Harris wrote:
> 
> On Sat, Jan 20, 2001 at 09:29:35PM -0800, David Hampton wrote:
> > You're absolutely correct.  Do you want me to correct it and resubmit
> > the diffs?
> 
> Yes, and change "No not decode" to "Do not decode" (which is what the
> comments in the code say it should be) while you're at it.

Done.  Here are the new diffs.

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/22 17:04:27
@@ -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/22 17:04:29
@@ -1009,6 +1009,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 +1174,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 +1207,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 +1229,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 +1303,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 +1396,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 +1412,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 +1424,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 +1454,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 +1470,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 +1492,168 @@
 		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;
+	info->caller_func(info->table_name, key, value, info->caller_data);
+}
+
+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;
+	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;
+	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) {
+	    return;
+	}
+
+	info = user_data;
+	info->caller_func(info->table_name, key, value, info->caller_data);
+}
+
+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 +1818,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 +1849,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 +1868,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 +1889,64 @@
 	*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;
+	  info->caller_func(key, 0, tmp->data, info->caller_data);
+	}
+}
+
+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/22 17:04:29
@@ -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/22 17:04:31
@@ -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/22 17:04:32
@@ -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/22 17:04:32
@@ -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	Mon Jan 22 11:04:33 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_COLUMNS];
+    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 "Do 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, "Do 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_COLUMNS];
+    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	Mon Jan 22 11:04:33 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/22 17:04:33
@@ -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);  
 }