Ethereal-dev: [Ethereal-dev] [PATCH] Wish list item 17 (packet generator)

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

From: Lanfranco Salinari <lanfranco.salinari@xxxxxxxxxxxxx>
Date: Tue, 02 Sep 2003 21:54:03 +0200
Hello,
I post this patch again, hoping that someone notices it.
I think that a packet generator would be a useful feature for ethereal,
so I hope someone will give me his opinion about the patch and the
work that remains to be done, as I have no experience with GTK.
Best regards,

Lanfranco
diff -urN --exclude='*~' ethereal-0.9.14-orig/gtk/gen_packet.c ethereal-0.9.14/gtk/gen_packet.c
--- ethereal-0.9.14-orig/gtk/gen_packet.c	1970-01-01 01:00:00.000000000 +0100
+++ ethereal-0.9.14/gtk/gen_packet.c	2003-08-25 21:35:46.000000000 +0200
@@ -0,0 +1,358 @@
+/* gen_packet.c
+ * Routines for packet generation
+ * Copyright 2003, L. Salinari <lanfranco.salinari@xxxxxxxxxxxxx>
+ * Network-related code written with L.Rossi
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <pcap.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <linux/if.h>
+#include <linux/if_packet.h>
+
+#include "dlg_utils.h"
+#include "ui_util.h"
+#include "main.h"
+#include "compat_macros.h"
+#include "gtkglobals.h"
+
+#include <string.h>
+#include "prefs.h"
+#include "simple_dialog.h"
+#include "proto_draw.h"
+#include "keys.h"
+#include "gtk_hex.h"
+#include "pcap-util.h"
+#include <epan/epan.h>
+#include <epan/packet.h>
+#include <epan/plugins.h>
+#include <epan/epan_dissect.h>
+#include "gen_packet.h"
+
+/* Data structure holding information about a packet-detail window. */
+struct PacketGenData {
+    frame_data *frame;	   /* The frame being displayed */
+    union wtap_pseudo_header pseudo_header; /* Pseudo-header for packet */
+    guint8     *pd;		   /* Data for packet */
+    GtkWidget  *main;
+    GtkWidget  *tv_scrollw;
+    GtkWidget  *tree_view;
+    field_info *finfo_selected;
+    epan_dissect_t	*edt;
+    GtkWidget  *hex;
+    GtkWidget  *interf;
+};
+
+#define E_BYTE_VIEW_TREE_PTR      "byte_view_tree_ptr"
+#define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
+#define E_BYTE_VIEW_NDIGITS_KEY   "byte_view_ndigits"
+#define E_BYTE_VIEW_TVBUFF_KEY    "byte_view_tvbuff"
+#define E_BYTE_VIEW_START_KEY     "byte_view_start"
+#define E_BYTE_VIEW_END_KEY       "byte_view_end"
+#define E_BYTE_VIEW_ENCODE_KEY    "byte_view_encode"
+
+
+static void
+byte_view_show_cb(GtkWidget *bv, gpointer data _U_)
+{
+    guint8 *byte_data;
+    guint byte_len;
+    GtkHex  *hex = GTK_HEX(bv);
+    
+    byte_data = get_byte_view_data_and_length(bv, &byte_len);
+    if (byte_data == NULL) {
+        /* This must be the dummy byte view if no packet is selected. */
+        return;
+    }
+    hex->hex_font = m_r_font;
+    gtk_hex_set_content(hex, byte_data, byte_len);
+}
+
+static gboolean
+on_hex_changed(GtkWidget *widget,
+	       int index,
+	       int len,
+	       gpointer data)
+{
+    struct PacketGenData *DataPtr = (struct PacketGenData *) data;
+    GtkHex *byte_view = GTK_HEX(DataPtr->hex);
+    
+    /* TBD: update of protocol view */
+    DataPtr->edt = epan_dissect_new(TRUE, TRUE);
+    epan_dissect_run(DataPtr->edt,
+		     &DataPtr->pseudo_header,
+		     byte_view->data,
+		     DataPtr->frame,
+		     &cfile.cinfo);
+    epan_dissect_fill_in_columns(DataPtr->edt);
+    proto_tree_draw(DataPtr->edt->tree, DataPtr->tree_view);
+
+    return TRUE;
+    TOUCH(widget);
+    TOUCH(index);
+    TOUCH(len);
+}
+
+static GtkWidget *
+create_hex_view(epan_dissect_t *edt, GtkWidget *tree_view,
+				   GtkWidget *byte_nb_ptr, struct PacketGenData *DataPtr)
+{
+    data_source *src;
+    GtkWidget *byte_view;
+
+    src = edt->pi.data_src->data;
+    byte_view = gtk_hex_new();
+
+    OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY, src->tvb);
+    gtk_container_add(GTK_CONTAINER(byte_nb_ptr), byte_view);
+    
+    SIGNAL_CONNECT(byte_view, "show", byte_view_show_cb, NULL);
+    SIGNAL_CONNECT(byte_view, "changed", on_hex_changed, (gpointer) DataPtr);
+
+    OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_PTR, edt->tree);
+    OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
+
+    gtk_widget_show(byte_view);
+
+    return byte_view;
+}
+
+static void
+send_packet (gchar *packet, guint len, gchar *ifname)
+{
+    int sockfd;                                        /* Socket descriptor */
+    int err;
+    struct sockaddr_ll	sll;
+    struct ifreq	ifr;
+
+    
+    /* We want only Ethernet frames containing IP data */
+    sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+    if (sockfd < 0) {
+	simple_dialog(ESD_TYPE_WARN, NULL, "ERROR: socket not created");	
+	return;
+    }
+
+    memset(&ifr, 0, sizeof(ifr));
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+    if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1) {
+	simple_dialog(ESD_TYPE_WARN, NULL, "ERROR: could not get if index");	
+	return;
+    }    
+    memset(&sll, 0, sizeof(sll));
+    sll.sll_family		= AF_PACKET;
+    sll.sll_ifindex		= ifr.ifr_ifindex;
+    sll.sll_protocol	        = htons(ETH_P_ALL);
+
+#if 0
+    /* Give the socket a name. */
+    if (bind (sockfd, (struct sockaddr *) &sll, sizeof (sll)) < 0) {
+	simple_dialog(ESD_TYPE_WARN, NULL, "ERROR: could not bind socket");
+	return;
+    }
+#endif
+
+    /* Read one packet */
+    err = sendto (sockfd, packet, len, 0, (struct sockaddr *) &sll, sizeof (sll));
+    if (err < 0) {
+	simple_dialog(ESD_TYPE_WARN, NULL, "ERROR: could not write on socket");	
+	return;
+    }
+
+    close(sockfd);
+    
+    return;
+}
+
+static void
+print_buffer(GtkWidget *widget _U_, gpointer data)
+{
+    struct PacketGenData *PGdata = (struct PacketGenData *)data;
+    GtkHex *byte_view = GTK_HEX(PGdata->hex);
+    GtkCombo *if_combo = GTK_COMBO(PGdata->interf);
+    GtkEntry *if_entry = GTK_ENTRY(if_combo->entry);
+    gchar *if_name;
+    unsigned int i;
+
+#ifdef DEBUG
+    for (i=0; i<byte_view->data_size; i++) {
+	printf ("%02x ", (byte_view->data)[i]);
+	if (i%16 == 15)
+	    printf ("\n");
+    }
+    printf ("\n");
+#endif
+    if_name = gtk_entry_get_text(if_entry);
+#ifdef DEBUG
+    printf ("Interface = %s\n", if_name);
+#endif
+    if (strcmp(if_name,"")) {
+	send_packet (byte_view->data, byte_view->data_size, if_name);
+    }
+    else {
+	simple_dialog(ESD_TYPE_WARN, NULL, "No interface selected");
+    }
+    return;
+}
+
+static void
+main_win_destroy(GtkWidget *widget _U_, gpointer data)
+{
+    struct PacketGenData *DataPtr = (struct PacketGenData *)data;
+
+    g_free(DataPtr->pd);
+    g_free(DataPtr);
+
+    return;
+}
+
+
+/* Callback for menu */
+void
+packet_generator(GtkWidget *w _U_, gpointer d _U_)
+{
+    GtkWidget *main_w, *main_vbox, *pane,
+	*tree_view, *tv_scrollw, 
+	*send_button, *if_lb, *if_hbox,
+	*if_cb, *hex_view;
+    struct PacketGenData *DataPtr;
+    gint tv_size = 95;
+    GList         *if_list;
+    int           err;
+    char          err_str[PCAP_ERRBUF_SIZE];
+
+
+    /* Allocate data structure to represent this window. */
+    DataPtr = (struct PacketGenData *) g_malloc(sizeof(struct PacketGenData));
+
+    /* Nothing was selected: TBD later */
+    if (cfile.current_frame == NULL) {
+	simple_dialog(ESD_TYPE_WARN, NULL, "No packet selected");
+	return;
+    }
+    else {
+	DataPtr->frame = cfile.current_frame;
+	memcpy(&DataPtr->pseudo_header, &cfile.pseudo_header, sizeof DataPtr->pseudo_header);
+	DataPtr->pd = (guint8 *) g_malloc(DataPtr->frame->cap_len);
+	memcpy(DataPtr->pd, cfile.pd, DataPtr->frame->cap_len);
+	DataPtr->edt = epan_dissect_new(TRUE, TRUE);
+	epan_dissect_run(DataPtr->edt,
+			 &DataPtr->pseudo_header,
+			 DataPtr->pd,
+			 DataPtr->frame,
+			 &cfile.cinfo);
+	epan_dissect_fill_in_columns(DataPtr->edt);
+    }
+
+    main_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(main_w), "Ethereal: Packet Generator");
+    gtk_window_set_default_size(GTK_WINDOW(main_w), DEF_WIDTH, -1);
+
+    /* Container for paned windows  */
+    main_vbox = gtk_vbox_new(FALSE, 1);
+    gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
+    gtk_container_add(GTK_CONTAINER(main_w), main_vbox);
+    gtk_widget_show(main_vbox);
+    
+    /* Panes for the tree and byte view */
+    pane = gtk_vpaned_new();
+    gtk_paned_gutter_size(GTK_PANED(pane), (GTK_PANED(pane))->handle_size);
+    gtk_container_add(GTK_CONTAINER(main_vbox), pane);
+    gtk_widget_show(pane);
+
+    /* Tree view */
+    create_tree_view(tv_size, &prefs, pane, &tv_scrollw, &tree_view);
+
+    gtk_widget_show(tree_view);
+   
+    DataPtr->main = main_w;
+    DataPtr->tv_scrollw = tv_scrollw;
+    DataPtr->tree_view = tree_view;
+
+    /* draw the protocol tree & print hex data */
+    hex_view = create_hex_view(DataPtr->edt, tree_view, pane, DataPtr);
+    proto_tree_draw(DataPtr->edt->tree, tree_view);
+
+    DataPtr->hex = hex_view;
+    DataPtr->finfo_selected = NULL;
+    
+    /* Interface and buttons box */
+    if_hbox = gtk_hbox_new(FALSE, 1);
+    gtk_container_border_width(GTK_CONTAINER(if_hbox), 0);
+    gtk_box_pack_start(GTK_BOX(main_vbox), if_hbox, FALSE, TRUE, 0);
+    gtk_widget_show(if_hbox);
+
+    /* Send button */
+    send_button = gtk_button_new_with_label("Send");
+    gtk_box_pack_start(GTK_BOX(if_hbox), send_button, FALSE, TRUE, 1);
+    gtk_widget_show(send_button);
+
+    /* Get interfaces'names */
+    if_list = get_interface_list(&err, err_str);
+    if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
+	simple_dialog(ESD_TYPE_WARN, NULL, "Can't get list of interfaces: %s",
+		      err_str);
+    }
+    
+    if_lb = gtk_label_new("Interface:");
+    gtk_box_pack_start(GTK_BOX(if_hbox), if_lb, FALSE, FALSE, 6);
+    gtk_widget_show(if_lb);
+
+
+    if_cb = gtk_combo_new();
+    if (if_list != NULL)
+	gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
+    if (cfile.iface == NULL && prefs.capture_device != NULL) {
+	/* No interface was specified on the command line or in a previous
+	   capture, but there is one specified in the preferences file;
+	   make the one from the preferences file the default */
+	cfile.iface	= g_strdup(prefs.capture_device);
+    }
+    if (cfile.iface != NULL)
+	gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cfile.iface);
+    else if (if_list != NULL)
+	gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
+    gtk_box_pack_start(GTK_BOX(if_hbox), if_cb, TRUE, TRUE, 6);
+    gtk_widget_show(if_cb);
+    
+    free_interface_list(if_list);
+
+    DataPtr->interf = if_cb;
+
+    SIGNAL_CONNECT(send_button, "clicked", print_buffer, DataPtr);
+    SIGNAL_CONNECT(main_w, "destroy", main_win_destroy, DataPtr);
+  
+    gtk_widget_show_all(main_w);
+
+    return;
+}
diff -urN --exclude='*~' ethereal-0.9.14-orig/gtk/gen_packet.h ethereal-0.9.14/gtk/gen_packet.h
--- ethereal-0.9.14-orig/gtk/gen_packet.h	1970-01-01 01:00:00.000000000 +0100
+++ ethereal-0.9.14/gtk/gen_packet.h	2003-07-26 14:44:50.000000000 +0200
@@ -0,0 +1,29 @@
+/* gen_packet.h
+ * Definitions for packet generation code
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GEN_PACKET_H__
+#define __GEN_PACKET_H__
+
+void
+packet_generator(GtkWidget *w _U_, gpointer d _U_);
+
+#endif
diff -urN --exclude='*~' ethereal-0.9.14-orig/gtk/gtk_hex.c ethereal-0.9.14/gtk/gtk_hex.c
--- ethereal-0.9.14-orig/gtk/gtk_hex.c	1970-01-01 01:00:00.000000000 +0100
+++ ethereal-0.9.14/gtk/gtk_hex.c	2003-08-25 21:28:57.000000000 +0200
@@ -0,0 +1,1527 @@
+
+/*
+
+Copyright (C) 2000, 2001, 2002 Christian Kreibich <christian@xxxxxxxxx>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies of the Software and its documentation and acknowledgment shall be
+given in the documentation and software packages that this Software was
+used.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+#define GTK_ENABLE_BROKEN
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+
+#include "gtk_hex.h"
+
+enum {
+  CHANGED,
+  MOVE_CURSOR,
+  LAST_SIGNAL
+};
+
+static GtkVBoxClass *parent_class = NULL;
+static guint hex_signals[LAST_SIGNAL] = { 0 };
+
+struct _GtkHexPrivate
+{
+    GtkText *null_map;
+};
+
+/* The width of a line of text in the hex-mode view, consisting
+ * of offset, hex view and ascii view:
+ *
+ * 32 +     16 characters per 8 Bytes, twice
+ * (2*7) +  Single space between bytes, twice
+ * 4 +      Two spaces between 8-byte sets and ascii
+ * 1 +      For newline
+ * 17 +     For ascii display, with spacer column
+ * 6        For 5-digit offset counter, including spacer
+ */ 
+#define HEX_LINE_WIDTH               74
+
+#define HEX_LINE_START                6
+#define HEX_LINE_END                 53
+#define HEX_LINE_START_ASCII         56
+#define HEX_LINE_START_RIGHT_ASCII   65
+#define HEX_LINE_LEFT_MIDDLE         28
+#define HEX_LINE_RIGHT_MIDDLE        31
+#define HEX_BLOCK_LEN                23
+
+#define NULL_CHAR                    0xF8
+#define NONPRINT_CHAR                0xB7
+
+static int       hex_get_nowhite_index(GtkHex *hex, int index);
+static gboolean  hex_cursor_over_hex(GtkHex *hex);
+static gboolean  hex_cursor_over_hex_ascii(GtkHex *hex);
+static gboolean  hex_cursor_over_high_nibble(GtkHex *hex);
+static int       hex_cursor_index_to_byte_index(GtkHex *hex);
+static void      hex_byte_index_to_cursor_indices(int byte_index, int *i1, int *i2);
+static void      hex_sync_data_to_ascii(GtkHex *hex);
+static void      hex_text_ascii_colorify(GtkHex *hex, guint start, int length);
+static void      hex_text_set_cursor(GtkHex *hex, guint index);
+static gboolean  hex_is_char_unprintable(guchar val);
+
+
+/* Sets the editor into hex mode
+ */
+static void
+on_mode_hex_toggled             (GtkToggleButton *togglebutton,
+				 gpointer         user_data)
+{
+  if (gtk_toggle_button_get_active(togglebutton))
+    gtk_hex_set_mode((GtkHex*) user_data, GTK_RAW_HEX);
+}
+
+
+/* Sets the editor into ascii mode
+ */
+static void
+on_mode_ascii_toggled           (GtkToggleButton *togglebutton,
+				 gpointer         user_data)
+{
+  if (gtk_toggle_button_get_active(togglebutton))
+    gtk_hex_set_mode((GtkHex*) user_data, GTK_RAW_ASCII);
+}
+
+
+/* We use the motion notify callback only to kill the object's
+ * handling of text selection -- we use the selection as our
+ * cursor, thus we want to disable it. This works through setting
+ * the pressed button to zero, making the object's handler think
+ * no button was pressed and changing nothing. This handler
+ * thus must be called *before* the object's handler
+ */
+static gboolean
+on_hex_text_motion_notify_event        (GtkWidget       *widget,
+                                        GdkEventMotion  *event,
+                                        gpointer         user_data)
+{
+  GtkHex *hex = GTK_HEX(user_data);
+
+  if (hex->mode == GTK_RAW_HEX)
+    GTK_TEXT(hex->hex_text)->button = 0;
+
+  return FALSE;
+  TOUCH(event);
+  TOUCH(widget);
+}
+
+
+/* This handler takes care of updating the null
+ * map when text is deleted.
+ */
+static gboolean
+on_hex_text_deleted(GtkWidget       *widget,
+		    gint             start,
+		    gint             end,
+		    gpointer         user_data)
+{
+  GtkHex *hex = GTK_HEX(user_data);
+
+  if (hex->mode == GTK_RAW_ASCII)
+    {
+      gtk_editable_delete_text(GTK_EDITABLE(hex->priv->null_map),
+			       start, end);
+      hex_text_ascii_colorify(hex, start, -ABS(end - start));
+      hex_sync_data_to_ascii(hex);
+
+      gtk_signal_emit(GTK_OBJECT(hex), hex_signals[CHANGED],
+		      start, ABS(end - start));
+    }
+
+  return FALSE;
+  TOUCH(widget);
+}
+
+
+/* This handles text insertions *BEFORE* the object's own
+ * handler. We use it to update the contents of the null map.
+ */
+static gboolean
+on_hex_text_inserted(GtkWidget       *widget,
+		     const gchar     *text,
+		     gint             length,
+		     gint            *position,
+		     gpointer         user_data)
+{
+  GtkHex  *hex = GTK_HEX(user_data);
+  guint    pos;
+  gint     i;
+
+  if (hex->mode == GTK_RAW_ASCII)
+    {
+      gchar *whitespace;
+      pos = *position;
+
+      /* Update null_map to reflect changes: */
+
+      if ( (whitespace = g_new0(gchar, length + 1)))
+	{
+	  memset(whitespace, ' ', length);
+
+	  for (i = 0; i < length + 1; i++)
+	    {
+	      if (hex_is_char_unprintable(text[i]))
+		{
+		  switch (text[i])
+		    {
+		    case '\n':
+		      whitespace[i] = ' ';
+		      break;
+
+		    case 0:
+		      whitespace[i] = '0';
+		      break;
+
+		    default:
+		      whitespace[i] = text[i];
+		    }
+		}
+	    }
+
+	  gtk_editable_insert_text(GTK_EDITABLE(hex->priv->null_map),
+				   whitespace, length, &pos);
+	  g_free(whitespace);
+	}
+    }
+
+  return FALSE;
+  TOUCH(widget);
+  TOUCH(text);
+}
+
+
+/* This is the text insertion handler that runs *AFTER*
+ * the object's own handler. We use it to cut excess data
+ * at the end of the text buffers and to reflect the changes
+ * in the packet's payload.
+ */
+static gboolean
+on_hex_text_inserted_after(GtkWidget       *widget,
+			   const gchar     *text,
+			   gint             length,
+			   gint            *position,
+			   gpointer         user_data)
+{
+  GtkHex  *hex = GTK_HEX(user_data);
+
+  if (hex->mode == GTK_RAW_ASCII)
+    {
+
+      /* Make sure text gets no longer than packet payload,
+	 and reflect changes in the null map: */
+
+      hex_text_ascii_colorify(hex, *position - length, ABS(length));
+      hex_sync_data_to_ascii(hex);
+
+      gtk_signal_emit(GTK_OBJECT(hex), hex_signals[CHANGED],
+		      *position - length, length);
+    }
+
+
+  return FALSE;
+  TOUCH(widget);
+  TOUCH(text);
+  TOUCH(length);
+  TOUCH(position);
+}
+
+
+static gboolean
+hex_is_char_unprintable(guchar val)
+{
+  return (val < 0x20 || (val >= 0x7f && val < 0xa0));
+}
+
+
+/* This function selects a character in the hex editor (thus placing
+ * the cursor) and also takes care of hilighting the corresponding
+ * character in the ascii or hex field
+ */
+static void
+hex_text_set_cursor(GtkHex *hex, guint index)
+{
+  static guint old_index = 0, old_len = 0;
+
+  GtkEditable *ed;
+  gchar       *text;
+  int          byte_offset, i1, i2;
+
+  if (!hex || hex->mode != GTK_RAW_HEX)
+    return;
+
+  ed = GTK_EDITABLE(hex->hex_text);
+
+  gtk_editable_select_region(ed, index, index + 1);
+
+  gtk_text_freeze(GTK_TEXT(ed));
+  gtk_signal_handler_block_by_data(GTK_OBJECT(hex->hex_text), hex);
+
+  if (old_len)
+    {
+      text = gtk_editable_get_chars(ed, old_index, old_index + old_len);
+      gtk_editable_delete_text(ed, old_index, old_index + old_len);
+      gtk_text_set_point(GTK_TEXT(hex->hex_text), old_index);
+      gtk_text_insert(GTK_TEXT(hex->hex_text), hex->hex_font, NULL, NULL, text, old_len);
+      g_free(text);
+    }
+
+  gtk_signal_handler_unblock_by_data(GTK_OBJECT(hex->hex_text), hex);
+  gtk_text_thaw(GTK_TEXT(ed));
+
+  byte_offset = hex_cursor_index_to_byte_index(hex);
+  hex_byte_index_to_cursor_indices(byte_offset, &i1, &i2);
+
+  gtk_text_freeze(GTK_TEXT(ed));
+  gtk_signal_handler_block_by_data(GTK_OBJECT(hex->hex_text), hex);
+
+  if (hex_cursor_over_hex(hex))
+    {
+      text = gtk_editable_get_chars(ed, i2, i2 + 1);
+      gtk_editable_delete_text(ed, i2, i2 + 1);
+      gtk_text_set_point(GTK_TEXT(hex->hex_text), i2);
+      gtk_text_insert(GTK_TEXT(hex->hex_text), hex->hex_font, NULL,
+		      &GTK_WIDGET(hex->hex_text)->style->base[GTK_STATE_INSENSITIVE],
+		      text, 1);
+      g_free(text);      
+      old_index = i2;
+      old_len = 1;
+    }
+  else
+    {
+      text = gtk_editable_get_chars(ed, i1, i1 + 2);
+      gtk_editable_delete_text(ed, i1, i1 + 2);
+      gtk_text_set_point(GTK_TEXT(hex->hex_text), i1);
+      gtk_text_insert(GTK_TEXT(hex->hex_text), hex->hex_font, NULL,
+		      &GTK_WIDGET(hex->hex_text)->style->base[GTK_STATE_INSENSITIVE],
+		      text, 2);
+      g_free(text);      
+      old_index = i1;
+      old_len = 2;
+    }
+
+  gtk_signal_handler_unblock_by_data(GTK_OBJECT(hex->hex_text), hex);
+  gtk_text_thaw(GTK_TEXT(ed));
+
+  gtk_editable_select_region(ed, index, index + 1);
+}
+
+
+static void
+hex_text_ascii_colorify(GtkHex *hex, guint start, int length)
+{
+  GtkText *hex_text;
+  char *text;
+  guint len, min_len, point;
+
+  if (!hex || hex->mode != GTK_RAW_ASCII)
+    return;
+
+  hex_text = GTK_TEXT(hex->hex_text);
+
+  gtk_signal_handler_block_by_data(GTK_OBJECT(hex->hex_text), hex);
+  gtk_text_freeze(hex_text);
+
+  len = gtk_text_get_length(hex_text);
+  min_len = MIN(hex->data_size - 1, len);
+  text = gtk_editable_get_chars(GTK_EDITABLE(hex_text), 0, -1);
+  point = gtk_editable_get_position(GTK_EDITABLE(hex_text));
+
+  if (length > 0)
+    {
+      if (len > hex->data_size)
+	{
+	  gtk_editable_delete_text(GTK_EDITABLE(hex_text), hex->data_size, -1);
+	  gtk_text_set_point(hex_text, hex->data_size);
+	  gtk_text_insert(hex_text, hex->hex_font,
+			  &GTK_WIDGET(hex_text)->style->text[GTK_STATE_INSENSITIVE],
+			  &GTK_WIDGET(hex_text)->style->base[GTK_STATE_NORMAL],
+			  &text[hex->data_size],
+			  len - hex->data_size);
+	}
+    }
+  else
+    {
+      if (start >= hex->data_size)
+	{
+	  gtk_editable_delete_text(GTK_EDITABLE(hex_text), hex->data_size, -1);
+	  gtk_text_set_point(hex_text, hex->data_size);
+	  gtk_text_insert(hex_text, hex->hex_font,
+			  &GTK_WIDGET(hex_text)->style->text[GTK_STATE_INSENSITIVE],
+			  &GTK_WIDGET(hex_text)->style->base[GTK_STATE_NORMAL],
+			  &text[hex->data_size],
+			  len - hex->data_size);
+	}
+      else
+	{
+	  gtk_editable_delete_text(GTK_EDITABLE(hex_text), point, -1);
+	  gtk_text_insert(hex_text, hex->hex_font, NULL, NULL, &text[point],
+			  MIN(hex->data_size - point, len - point));
+	  
+	  if (len > hex->data_size)
+	    {
+	      gtk_text_set_point(hex_text, hex->data_size);
+	      gtk_text_insert(hex_text, hex->hex_font,
+			      &GTK_WIDGET(hex_text)->style->text[GTK_STATE_INSENSITIVE],
+			      &GTK_WIDGET(hex_text)->style->base[GTK_STATE_NORMAL],
+			      &text[hex->data_size],
+			      len - hex->data_size);
+	    }
+	}
+    }
+  
+  gtk_text_thaw(hex_text);
+  gtk_signal_handler_unblock_by_data(GTK_OBJECT(hex->hex_text), hex);
+  gtk_editable_set_position(GTK_EDITABLE(hex_text), point);
+
+  g_free(text);
+}
+
+
+/* This function handles the case of cursor-left being
+ * pressed while hex mode is active.
+ */
+static void
+hex_handle_hex_left(GtkHex *hex)
+{
+  int line_offset, line_num;
+  GtkEditable *ed;
+
+  ed = GTK_EDITABLE(hex->hex_text);
+  line_offset = ed->selection_start_pos % HEX_LINE_WIDTH;
+  line_num = ed->selection_start_pos / HEX_LINE_WIDTH;
+
+  /* If we're at the beginning of either hex or ascii data,
+     don't do anything. */
+  if ((ed->selection_start_pos == HEX_LINE_START_ASCII) ||
+      (ed->selection_start_pos == HEX_LINE_START))
+    return;
+
+  /* Check for intervals in the hex display: */
+  
+  if (line_offset >= HEX_LINE_START && line_offset <= HEX_LINE_LEFT_MIDDLE)
+    {
+      if (line_offset == HEX_LINE_START)
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos - 27);
+	  return;
+	}
+
+      if ((line_offset - HEX_LINE_START) % 3 == 1)
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos - 1);
+	}
+      else
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos - 2);
+	}
+      return;
+    }
+
+  if (line_offset >= HEX_LINE_RIGHT_MIDDLE && line_offset <= HEX_LINE_END)
+    {
+      if (line_offset == HEX_LINE_RIGHT_MIDDLE)
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos - 3);
+	  return;
+	}
+
+      if ((line_offset - HEX_LINE_LEFT_MIDDLE) % 3 == 1)
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos - 1);
+	}
+      else
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos - 2);
+	}
+      return;
+    }
+
+  /* Still here? Ok, check for intervals in the ascii display */
+
+  if (line_offset == HEX_LINE_START_RIGHT_ASCII)
+    {
+      hex_text_set_cursor(hex, ed->selection_start_pos - 2);
+      return;
+    }
+
+  if (line_offset > HEX_LINE_START_ASCII && line_offset <= HEX_LINE_START_RIGHT_ASCII)
+    {
+      hex_text_set_cursor(hex, ed->selection_start_pos - 1);
+      return;
+    }
+
+  if (line_offset >= HEX_LINE_START_RIGHT_ASCII && line_offset <= HEX_LINE_START_RIGHT_ASCII + 7)
+    {
+      hex_text_set_cursor(hex, ed->selection_start_pos - 1);
+      return;
+    }
+
+  if (line_offset == HEX_LINE_START_ASCII)
+    {
+      hex_text_set_cursor(hex, ed->selection_start_pos - HEX_LINE_START_ASCII - 2);
+      return;
+    }
+}
+
+
+/* This function handles the case of cursor-right being
+ * pressed while hex mode is active.
+ */
+static void
+hex_handle_hex_right(GtkHex *hex)
+{
+  int line_offset, line_num;
+  int i;
+  guint byte_index;
+  GtkEditable *ed;
+  guchar s[HEX_LINE_WIDTH+1];
+  
+  ed = GTK_EDITABLE(hex->hex_text);
+
+  line_offset = ed->selection_start_pos % HEX_LINE_WIDTH;
+  line_num = ed->selection_start_pos / HEX_LINE_WIDTH;
+  byte_index = hex_cursor_index_to_byte_index(hex);
+
+#if 0
+  /* If we're at the last byte and are using hex display,
+     it depends on whether we've selected the low or high nibble
+     whether we can move any further to the right. */
+  if (byte_index == hex->data_size - 1)
+    {
+      /* We're in hex area */
+      if (line_offset < HEX_LINE_RIGHT_MIDDLE)
+      {
+	  if ((line_offset - HEX_LINE_START) % 3 == 0)
+	    hex_text_set_cursor(hex, ed->selection_start_pos + 1);
+      }
+      /* We're in ASCII area */
+      else if ((line_offset - HEX_LINE_RIGHT_MIDDLE) % 3 == 0)
+	hex_text_set_cursor(hex, ed->selection_start_pos + 1);
+      return;
+    }
+#endif
+
+  /* First we handle the case of last byte in the last column */
+  if ((byte_index == hex->data_size - 1) &&
+      line_offset == HEX_LINE_END) 
+    {
+	gtk_text_freeze(GTK_TEXT(hex->hex_text));
+
+	sprintf (s, "\n%.4x", hex->data_size);
+	for (i = HEX_LINE_START-1; i <= HEX_LINE_WIDTH; i++)
+	    {
+		s[i] = ' ';
+	    }
+	
+	gtk_text_insert(GTK_TEXT(hex->hex_text), hex->hex_font, NULL, NULL,
+			s, sizeof(s)-1);
+	gtk_text_thaw(GTK_TEXT(hex->hex_text));
+    }
+	
+
+  /* Check for intervals in the hex display: */
+
+  if (line_offset >= HEX_LINE_START && line_offset <= HEX_LINE_LEFT_MIDDLE)
+    {
+      if (line_offset == HEX_LINE_LEFT_MIDDLE)
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos + 3);
+	  return;
+	}
+
+      if ((line_offset - HEX_LINE_START) % 3 == 1)
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos + 2);
+	}
+      else
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos + 1);
+	}
+
+      return;
+    }
+
+  if (line_offset >= HEX_LINE_RIGHT_MIDDLE && line_offset <= HEX_LINE_END)
+    {
+      if (line_offset == HEX_LINE_END)
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos + 27);
+	  return;
+	}
+
+      if ((line_offset - HEX_LINE_LEFT_MIDDLE) % 3 == 1)
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos + 2);
+	}
+      else
+	{
+	  hex_text_set_cursor(hex, ed->selection_start_pos + 1);
+	}
+
+      return;
+    }
+
+  /* Check for intervals in the ascii display: */
+
+  if (line_offset == HEX_LINE_START_RIGHT_ASCII - 2)
+    {
+      hex_text_set_cursor(hex, ed->selection_start_pos + 2);
+      return;
+    }
+
+  if (line_offset >= HEX_LINE_START_ASCII && line_offset < HEX_LINE_START_RIGHT_ASCII)
+    {
+      hex_text_set_cursor(hex, ed->selection_start_pos + 1);
+      return;
+    }
+
+  if (line_offset >= HEX_LINE_START_RIGHT_ASCII && line_offset < HEX_LINE_START_RIGHT_ASCII + 7)
+    {
+      hex_text_set_cursor(hex, ed->selection_start_pos + 1);
+      return;
+    }
+
+  if (line_offset == HEX_LINE_START_RIGHT_ASCII + 7)
+    {
+      hex_text_set_cursor(hex, ed->selection_start_pos + HEX_LINE_START_ASCII + 2);
+      return;
+    }
+}
+
+
+static void
+hex_update_current(GtkHex *hex, int byte_offset)
+{
+  GtkEditable *ed;
+  int  old, old2, i1, i2;
+  guchar s[16];
+
+  if (!hex || hex->mode != GTK_RAW_HEX)
+    return;
+
+  ed = GTK_EDITABLE(hex->hex_text);
+
+  hex_byte_index_to_cursor_indices(byte_offset, &i1, &i2);
+  gtk_text_freeze(GTK_TEXT(hex->hex_text));
+  old = old2 = ed->selection_start_pos;
+
+  s[0] = hex->data[byte_offset];
+  s[1] = 0;
+
+  if (hex_is_char_unprintable(s[0]))
+    {
+      if (s[0] == 0)
+	s[0] = NULL_CHAR;
+      else
+	s[0] = NONPRINT_CHAR;
+    }
+
+  gtk_editable_delete_text(ed, i2, i2 + 1);
+  gtk_editable_insert_text(ed, s, 1, &i2);
+
+  g_snprintf(s, 16, "%.2x", hex->data[byte_offset]);
+  gtk_editable_delete_text(ed, i1, i1 + 2);
+  gtk_editable_insert_text(ed, s, 2, &i1);
+
+  gtk_text_thaw(GTK_TEXT(hex->hex_text));
+  hex_text_set_cursor(hex, old);
+}
+
+
+static gboolean
+hex_text_cursor_key_press(GtkHex *hex, GdkEventKey *event, guint byte_offset)
+{
+  GtkEditable *ed;
+
+  ed = GTK_EDITABLE(hex->hex_text);
+
+  if (hex->mode == GTK_RAW_HEX)
+    {
+      switch(event->keyval)
+	{
+	case GDK_Up:
+	  if ((int) ed->selection_start_pos - HEX_LINE_WIDTH > 0)
+	    hex_text_set_cursor(hex, ed->selection_start_pos - HEX_LINE_WIDTH);
+	  return TRUE;
+	  break;
+
+	case GDK_Down:
+	  if (byte_offset + 16 < hex->data_size)
+	    hex_text_set_cursor(hex, ed->selection_start_pos + HEX_LINE_WIDTH);
+	  return TRUE;
+	  break;
+
+	case GDK_Left:
+	  hex_handle_hex_left(hex);
+	  return TRUE;
+	  break;
+
+	case GDK_Right:
+	  hex_handle_hex_right(hex);
+	  return TRUE;
+	  break;
+
+	case GDK_Return:
+	  if (event->string)
+	    event->string[0] = '\n';
+	  break;
+	}
+    }
+
+  return FALSE;
+}
+
+static gboolean
+check_buf_limit (GtkHex *hex, int byte_offset)
+{
+    guchar *new_data;
+
+    /*
+     * First case: byte_offset is less than data_size.
+     * Nothing has to be done, since data in that space
+     * was already written
+     */
+    if (byte_offset < hex->data_size)
+        return TRUE;
+
+    /*
+     * Second case: data_size <= byte_offset < buf_size.
+     * Adjust data_size, but don't allocate space
+     */
+    if (byte_offset < hex->buf_size)
+    {
+        hex->data_size = byte_offset + 1;
+	return TRUE;
+    }
+    /*
+     * Third case: byte_offset >= buf_size.
+     * Space has to be reallocated, since there is no room for the new byte
+     */
+    new_data = g_realloc (hex->data, hex->buf_size+16);
+    if (new_data)
+    {
+        hex->data       = new_data;
+	hex->data_size  = byte_offset + 1;
+	hex->buf_size  += 16;
+	return TRUE;
+    }
+    else
+        return FALSE;
+
+}
+
+/* LS: handle the case in which byte_offset goes past the end of the buffer */
+static gboolean
+hex_text_data_key_press(GtkHex *hex, GdkEventKey *event, int byte_offset)
+{
+  char val = 0;
+
+  if (!hex)
+    return FALSE;
+
+  if (hex->mode == GTK_RAW_HEX && event->string && strlen(event->string) == 1)
+    {
+      if (hex_cursor_over_hex(hex))
+	{
+	  /* Accept only hex characters when editing hex data */
+	  if ((event->string[0] >= 'a' && event->string[0] <= 'f') ||
+	      (event->string[0] >= 'A' && event->string[0] <= 'F'))
+	    {
+	      val = tolower(event->string[0]) - 87;
+	    }
+	  else if (event->string[0] >= '0' && event->string[0] <= '9')
+	    {
+	      val = event->string[0] - 48;
+	    }
+	  else
+	    {
+	      return FALSE;
+	    }
+
+	  if (!check_buf_limit (hex, byte_offset))
+	      return FALSE;
+
+	  if (hex_cursor_over_high_nibble(hex))
+	    {
+	      hex->data[byte_offset] &= 0x0F;
+	      hex->data[byte_offset] |= (val << 4);
+	    }
+	  else
+	    {
+	      hex->data[byte_offset] &= 0xF0;
+	      hex->data[byte_offset] |= val;
+	    }
+
+	  hex_update_current(hex, byte_offset);
+	  hex_handle_hex_right(hex);
+	}
+      else if (hex_cursor_over_hex_ascii(hex))
+	{
+	  if (!check_buf_limit (hex, byte_offset))
+	      return FALSE;
+
+	  hex->data[byte_offset] = event->string[0];
+
+	  hex_update_current(hex, byte_offset);
+	  hex_handle_hex_right(hex);
+	}
+
+      gtk_signal_emit(GTK_OBJECT(hex), hex_signals[CHANGED],
+		      byte_offset, 1);
+    }
+
+  return TRUE;
+}
+
+
+static gboolean
+on_hex_text_key_press_event        (GtkWidget       *widget,
+				    GdkEventKey     *event,
+				    gpointer         user_data)
+{
+  gboolean done;
+  int byte_offset;
+  GtkHex *hex;
+  GtkEditable *ed;
+
+  if (!user_data)
+    return FALSE;
+
+  hex = GTK_HEX(user_data);
+  ed = GTK_EDITABLE(widget);
+
+  if (hex->mode != GTK_RAW_HEX)
+    return FALSE;
+
+  if (!ed->has_selection)
+    return FALSE;
+
+  byte_offset = hex_cursor_index_to_byte_index(hex);
+
+  /* We'll first check for cursor navigation and delegate handling
+   * to the appropriate handler routines.
+   */
+  done = hex_text_cursor_key_press(hex, event, byte_offset);
+
+  if (!done)
+    hex_text_data_key_press(hex, event, byte_offset);
+
+  byte_offset = hex_cursor_index_to_byte_index(hex);
+  gtk_signal_emit(GTK_OBJECT(hex), hex_signals[MOVE_CURSOR], byte_offset);
+
+  return FALSE;
+}
+
+
+/* This is our callback to indicate cursor placement.
+ * This is called after the object's handler, which
+ * even in non-editable mode sets GtkText->current_pos */
+gboolean
+on_hex_button_press_event     (GtkWidget       *widget,
+			       GdkEventButton  *event,
+			       gpointer         user_data)
+{
+  int index;
+  GtkHex *hex;
+  GtkEditable *ed;
+
+  ed = GTK_EDITABLE(widget);
+  hex = GTK_HEX(user_data);
+  
+  if (hex->mode == GTK_RAW_ASCII)
+    return FALSE;
+
+  index = hex_get_nowhite_index(hex, ed->current_pos);
+  hex_text_set_cursor(hex, index);
+
+  return FALSE;
+  TOUCH(event);
+}
+
+
+/* Assuming the cursor would be placed at INDEX, is that
+ * a feasible (i.e. hex or ascii, but not whitespace) index?
+ * If so, the same index is returned,
+ * otherwise the nearest one that actually contains
+ * non-whitespace.
+ */
+static int
+hex_get_nowhite_index(GtkHex *hex, int index_orig)
+{
+  GtkEditable *ed;
+  int line_offset;
+  int index;
+
+  if (!hex)
+    return FALSE;
+
+  ed = GTK_EDITABLE(hex->hex_text);
+
+  if (hex->mode == GTK_RAW_ASCII)
+    return index_orig;
+
+  line_offset = index_orig % HEX_LINE_WIDTH;
+  index = (index_orig / HEX_LINE_WIDTH) * HEX_LINE_WIDTH + line_offset;
+
+  if (line_offset < HEX_LINE_START)
+    {
+      index += HEX_LINE_START - (line_offset % HEX_LINE_START);
+      return index;
+    }
+
+  if (line_offset >= HEX_LINE_START && line_offset <= HEX_LINE_END)
+    {
+      if (line_offset <= HEX_LINE_LEFT_MIDDLE)
+	{
+	  if ((line_offset - HEX_LINE_START) % 3 == 2)
+	    index--;
+
+	  return index;
+	}
+
+      if (line_offset >= HEX_LINE_RIGHT_MIDDLE)
+	{
+	  if ((line_offset - HEX_LINE_RIGHT_MIDDLE) % 3 == 2)
+	    index--;
+
+	  return index;
+	}
+
+      return index - ((line_offset - HEX_LINE_START) % (HEX_BLOCK_LEN-1));
+    }
+  
+  if (line_offset > HEX_LINE_END && line_offset < HEX_LINE_START_ASCII)
+    {
+      index -= (line_offset % HEX_LINE_END);
+      return index;
+    }
+
+  return index;
+}
+
+
+/* Returns TRUE when the editor is in hex mode and the
+ * currently selected character is in the hex area.
+ */
+static gboolean
+hex_cursor_over_hex(GtkHex *hex)
+{
+  GtkEditable *ed;
+  int line_offset;
+
+  if (!hex || hex->mode == GTK_RAW_ASCII)
+    return FALSE;
+
+  ed = GTK_EDITABLE(hex->hex_text);
+  line_offset = ed->selection_start_pos % HEX_LINE_WIDTH;
+
+  if (line_offset >= HEX_LINE_START && line_offset <= HEX_LINE_END)
+    return TRUE;
+
+  return FALSE;
+}
+
+
+/* Returns TRUE when the editor is in hex mode and the
+ * currently selected character is in the ascii area.
+ */
+static gboolean
+hex_cursor_over_hex_ascii(GtkHex *hex)
+{
+  GtkEditable *ed;
+  int line_offset;
+
+  if (!hex || hex->mode == GTK_RAW_ASCII)
+    return FALSE;
+
+  ed = GTK_EDITABLE(hex->hex_text);
+  line_offset = ed->selection_start_pos % HEX_LINE_WIDTH;
+
+  if (line_offset >= HEX_LINE_START_ASCII)
+    return TRUE;
+  
+  return FALSE;
+}
+
+
+/* Returns TRUE when the editor is in hex mode and the
+ * high nibble (i.e. left character) of a byte is selected,
+ * FALSE otherwise.
+ */
+static gboolean  
+hex_cursor_over_high_nibble(GtkHex *hex)
+{
+  GtkEditable *ed;
+  int line_offset;
+
+  if (!hex || hex->mode == GTK_RAW_ASCII)
+    return FALSE;
+
+  ed = GTK_EDITABLE(hex->hex_text);
+  line_offset = ed->selection_start_pos % HEX_LINE_WIDTH;
+
+  if (line_offset >= HEX_LINE_START && line_offset <= HEX_LINE_LEFT_MIDDLE)
+    {
+      if ((line_offset - HEX_LINE_START) % 3 == 0)
+	return TRUE;
+    }
+
+  if (line_offset >= HEX_LINE_RIGHT_MIDDLE && line_offset <= HEX_LINE_END)
+    {
+      if ((line_offset - HEX_LINE_RIGHT_MIDDLE) % 3 == 0)
+	return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+/* Calculates and returns the currently selected byte in the
+ * data chunk being displayed from the selected character in
+ * either hex or ascii mode. Blood, sweat & tears.
+ */
+static int
+hex_cursor_index_to_byte_index(GtkHex *hex)
+{
+  GtkEditable *ed = GTK_EDITABLE(hex->hex_text);
+  int line_num;
+  int line_offset;
+
+  if (!hex || hex->mode == GTK_RAW_ASCII)
+    return -1;
+
+  ed = GTK_EDITABLE(hex->hex_text);
+
+  line_num = ed->selection_start_pos / HEX_LINE_WIDTH;
+  line_offset = ed->selection_start_pos % HEX_LINE_WIDTH;
+  
+  if (line_offset >= HEX_LINE_START && line_offset <= HEX_LINE_END)
+    {
+      if (line_offset <= HEX_LINE_LEFT_MIDDLE)
+	return line_num * 16 + (line_offset - HEX_LINE_START) / 3;
+      else
+	return line_num * 16 + 8 + (line_offset - HEX_LINE_RIGHT_MIDDLE) / 3;
+    }
+
+  if (line_offset >= HEX_LINE_START_RIGHT_ASCII)
+    return line_num * 16 + 8 + (line_offset - HEX_LINE_START_RIGHT_ASCII);
+
+  if (line_offset >= HEX_LINE_START_ASCII)
+    return line_num * 16 + (line_offset - HEX_LINE_START_ASCII);
+  
+  return -1;
+}
+
+
+static void
+hex_byte_index_to_cursor_indices(int byte_index, int *i1, int *i2)
+{
+  int line_num, line_offset;
+
+  if (!i1 || !i2 || byte_index < 0) 
+    return;
+
+  line_num = byte_index / 16;
+  line_offset = byte_index % 16;
+
+  (*i1) =
+    line_num * HEX_LINE_WIDTH +
+    HEX_LINE_START + line_offset * 3 + (line_offset >= 8 ? 1 : 0);
+
+  (*i2) =
+    line_num * HEX_LINE_WIDTH +
+    HEX_LINE_START_ASCII + line_offset + (line_offset >= 8 ? 1 : 0);
+}
+
+
+/* The heart of the editor -- creates the hex editor hex-mode
+ * string from the input data and returns it. The string must
+ * be freed when it's not needed any more.
+ */
+static guchar *
+hex_get_hex_text(const guchar *data, int data_size)
+{
+  int x, y, num_lines, len;
+  guchar      *hex_data;
+  const guchar *data_ptr;
+  guchar      *hex_data_ptr;
+  guchar      *ascii_ptr;
+  guchar       hex_byte[3];
+  guchar       offset[5];
+  guchar       ascii_byte;
+
+  if (!data)
+    return NULL;
+
+  num_lines = (data_size / 16) + 1;
+  len = num_lines * HEX_LINE_WIDTH;
+  if (! (hex_data = g_new0(guchar, len + 1)))
+    return NULL;
+
+  memset(hex_data, ' ', len);
+
+  hex_data_ptr = hex_data;
+  ascii_ptr = hex_data_ptr + 50;
+  x = y = 0;
+
+  for (data_ptr = data; data_ptr < data + data_size; data_ptr++)
+    {
+      if (x == 0)
+	{
+	  g_snprintf(offset, 5, "%.4x", data_ptr - data);
+	  memcpy(hex_data_ptr, offset, 4);
+	  hex_data_ptr += 6;
+	  ascii_ptr = hex_data_ptr + 50;
+	}
+
+      g_snprintf(hex_byte, 3, "%.2x", (guchar) *data_ptr);
+
+      /* Workaround for a nonsense gcc warning -- char's range is
+	 obviously not limited to 128 ... */
+
+      {
+	int val = (guchar) *data_ptr;
+
+	if (hex_is_char_unprintable(val))
+	  {
+	    if (val == 0)
+	      ascii_byte = NULL_CHAR;
+	    else
+	      ascii_byte = NONPRINT_CHAR;
+	  }
+	else
+	  {
+	    ascii_byte = val;
+	  }
+      }
+
+      *hex_data_ptr++ = hex_byte[0];
+      *hex_data_ptr++ = hex_byte[1];
+      *hex_data_ptr++ = ' ';
+      *ascii_ptr++    = ascii_byte;
+
+      if (x == 7)
+	{
+	  *hex_data_ptr++ = ' ';
+	  *ascii_ptr++ = ' ';
+	}
+
+      x++;
+
+      if (x == 16)
+	{
+	  x = 0;
+	  *ascii_ptr++ = '\n';
+	  hex_data_ptr = ascii_ptr;
+	}
+    }
+
+  return hex_data;
+}
+
+
+/* This one creates and returns the ascii version of
+ * the data being displayed. Zeroes and other non-displayable
+ * characters in the input are substituted with dots.
+ *
+ * The string is returned through the result pointer.
+ * An additional string will be returned through null_map,
+ * that contains a null character "0" wherever the original text
+'* contained a null byte, and whitespace everywhere else.
+ * The strings must be freed when it's no longer needed.
+ */
+void
+hex_get_ascii_text(const guchar *data, int data_size,
+		   guchar **result, guchar **null_map)
+{
+  guchar *result_ptr;
+  const guchar *data_ptr;
+  guchar *null_map_ptr;
+
+  if (!data || !result || !null_map)
+    return;
+
+  *result = result_ptr = g_new0(guchar, data_size + 1);
+  *null_map = null_map_ptr = g_new0(guchar, data_size + 1);
+
+  if (!result || !null_map)
+    {
+      g_free(result);
+      g_free(null_map);
+      return;
+    }
+
+  for (data_ptr = data; data_ptr < data + data_size; result_ptr++, data_ptr++, null_map_ptr++)
+    {
+      int val = *data_ptr;
+
+      if (hex_is_char_unprintable(val))
+	{
+	  switch (val)
+	    {
+	    case '\n':
+	      *result_ptr = *data_ptr;
+	      *null_map_ptr = ' ';
+	      break;
+
+	    case 0:
+	      *result_ptr = NULL_CHAR;
+	      *null_map_ptr = '0';
+	      break;
+
+	    default:
+	      *result_ptr = NONPRINT_CHAR;
+	      *null_map_ptr = val;
+	    }
+	}
+      else
+	{
+	  *result_ptr = *data_ptr;
+	  *null_map_ptr = ' ';
+	}
+    }
+}
+
+
+static void      
+hex_sync_data_to_ascii(GtkHex *hex)
+{
+  guint len, i;
+  gchar *text;
+
+  if (!hex || hex->mode != GTK_RAW_ASCII)
+    return;
+
+  text = gtk_editable_get_chars(GTK_EDITABLE(hex->hex_text), 0, -1);
+  len = strlen(text);
+
+  memset(hex->data, 0, hex->data_size);
+  memcpy(hex->data, text, MIN(len, hex->data_size));
+  g_free(text);
+
+  text = gtk_editable_get_chars(GTK_EDITABLE(hex->priv->null_map), 0, -1);
+
+  for (i = 0; i < MIN(len, hex->data_size); i++)
+    {
+      if (text[i] != ' ')
+	{
+	  if (text[i] == '0')
+	    {
+	      hex->data[i] = 0;
+	    }
+	  else
+	    {
+	      hex->data[i] = text[i];
+	    }
+	}
+    }
+
+  g_free(text);
+}
+
+
+static void
+gtk_hex_destroy(GtkObject *object)
+{
+  GtkHex *hex;
+
+  g_return_if_fail(object != NULL);
+  g_return_if_fail(GTK_IS_HEX(object));
+
+  hex = GTK_HEX(object);
+
+  gtk_widget_unref(GTK_WIDGET(hex->priv->null_map));
+  g_free(hex->priv);
+  
+  if (GTK_OBJECT_CLASS(parent_class)->destroy)
+    (* GTK_OBJECT_CLASS(parent_class)->destroy) (object);
+}
+
+
+static void
+gtk_hex_class_init (GtkHexClass *class)
+{
+  GtkObjectClass *object_class;
+  GtkVBoxClass   *vbox_class;
+
+  object_class = (GtkObjectClass *) class;
+  vbox_class   = (GtkVBoxClass *) class;
+
+  parent_class = gtk_type_class(gtk_vbox_get_type());
+
+  hex_signals[CHANGED] =
+    gtk_signal_new ("changed",
+		    GTK_RUN_LAST,
+		    object_class->type,
+		    GTK_SIGNAL_OFFSET (GtkHexClass, changed),
+		    gtk_marshal_NONE__INT_INT,
+		    GTK_TYPE_NONE, 2,
+		    GTK_TYPE_INT,
+		    GTK_TYPE_INT);
+
+  hex_signals[MOVE_CURSOR] =
+    gtk_signal_new ("move_cursor",
+		    GTK_RUN_LAST | GTK_RUN_ACTION,
+		    object_class->type,
+		    GTK_SIGNAL_OFFSET (GtkHexClass, move_cursor),
+		    gtk_marshal_NONE__INT,
+		    GTK_TYPE_NONE, 1,
+		    GTK_TYPE_INT);
+
+  gtk_object_class_add_signals(object_class, hex_signals, LAST_SIGNAL);
+
+  object_class->destroy = gtk_hex_destroy;
+
+  class->changed = NULL;
+  class->move_cursor = NULL;
+}
+
+
+static void
+gtk_hex_init (GtkHex *hex)
+{
+  GtkHexPrivate *priv;
+  GtkWidget *mode_hbox, *hex_vbox;
+  GtkWidget *scrolledwin;
+  GSList    *mode_hbox_group = NULL;
+
+  if (!hex)
+    return;
+
+  hex_vbox = gtk_vbox_new (FALSE, 0);
+  gtk_widget_ref (hex_vbox);
+  gtk_object_set_data_full (GTK_OBJECT (hex), "hex_vbox", hex_vbox,
+			    (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (hex_vbox);
+  gtk_container_add (GTK_CONTAINER (hex), hex_vbox);
+
+  mode_hbox = gtk_hbox_new (FALSE, 0);
+  gtk_widget_ref (mode_hbox);
+  gtk_object_set_data_full (GTK_OBJECT (hex_vbox), "mode_hbox", mode_hbox,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (mode_hbox);
+  gtk_box_pack_start (GTK_BOX (hex_vbox), mode_hbox, FALSE, FALSE, 0);
+
+  hex->mode_button_hex = gtk_radio_button_new_with_label (mode_hbox_group, ("Hex/ASCII"));
+  mode_hbox_group = gtk_radio_button_group (GTK_RADIO_BUTTON (hex->mode_button_hex));
+  gtk_widget_ref (hex->mode_button_hex);
+  gtk_object_set_data_full (GTK_OBJECT (hex_vbox), "mode_button_hex", hex->mode_button_hex,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (hex->mode_button_hex);
+  gtk_box_pack_start (GTK_BOX (mode_hbox), hex->mode_button_hex, FALSE, FALSE, 0);
+
+  hex->mode_button_ascii = gtk_radio_button_new_with_label (mode_hbox_group, ("ASCII only"));
+  mode_hbox_group = gtk_radio_button_group (GTK_RADIO_BUTTON (hex->mode_button_ascii));
+  gtk_widget_ref (hex->mode_button_ascii);
+  gtk_object_set_data_full (GTK_OBJECT (hex_vbox), "mode_button_ascii", hex->mode_button_ascii,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (hex->mode_button_ascii);
+  gtk_box_pack_start (GTK_BOX (mode_hbox), hex->mode_button_ascii, FALSE, FALSE, 0);
+
+  scrolledwin = gtk_scrolled_window_new (NULL, NULL);
+  gtk_widget_ref (scrolledwin);
+  gtk_object_set_data_full (GTK_OBJECT (hex_vbox), "scrolledwin", scrolledwin,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (scrolledwin);
+  gtk_box_pack_start (GTK_BOX (hex_vbox), scrolledwin, TRUE, TRUE, 0);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+
+  hex->hex_text = gtk_text_new (NULL, NULL);
+  gtk_widget_ref (hex->hex_text);
+  gtk_object_set_data_full (GTK_OBJECT (hex_vbox), "hex_text", hex->hex_text,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (hex->hex_text);
+  gtk_container_add (GTK_CONTAINER (scrolledwin), hex->hex_text);
+
+  gtk_text_set_editable(GTK_TEXT(hex->hex_text), FALSE);
+  gtk_text_set_word_wrap(GTK_TEXT(hex->hex_text), FALSE);
+  gtk_text_set_line_wrap(GTK_TEXT(hex->hex_text), FALSE);
+
+  hex->mode = GTK_RAW_HEX;
+
+  priv = g_new0(GtkHexPrivate, 1);
+  priv->null_map = GTK_TEXT(gtk_text_new(NULL, NULL));
+  gtk_widget_ref(GTK_WIDGET(priv->null_map));
+  gtk_object_set_data_full (GTK_OBJECT (hex_vbox), "null_map", priv->null_map,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  hex->priv = priv;
+
+  gtk_signal_connect (GTK_OBJECT (hex->mode_button_ascii), "toggled",
+                      GTK_SIGNAL_FUNC (on_mode_ascii_toggled),
+                      hex);
+
+  gtk_signal_connect (GTK_OBJECT (hex->mode_button_hex), "toggled",
+                      GTK_SIGNAL_FUNC (on_mode_hex_toggled),
+                      hex);
+
+  gtk_signal_connect (GTK_OBJECT (hex->hex_text), "key_press_event",
+                      GTK_SIGNAL_FUNC (on_hex_text_key_press_event),
+                      hex);
+
+  gtk_signal_connect (GTK_OBJECT (hex->hex_text), "motion_notify_event",
+		      GTK_SIGNAL_FUNC (on_hex_text_motion_notify_event),
+		      hex);
+
+  gtk_signal_connect_after (GTK_OBJECT (hex->hex_text), "button_press_event",
+			    GTK_SIGNAL_FUNC (on_hex_button_press_event),
+			    hex);
+
+  gtk_signal_connect(GTK_OBJECT (hex->hex_text), "insert_text",
+			    GTK_SIGNAL_FUNC (on_hex_text_inserted),
+			    hex);
+
+  gtk_signal_connect_after (GTK_OBJECT (hex->hex_text), "insert_text",
+			    GTK_SIGNAL_FUNC (on_hex_text_inserted_after),
+			    hex);
+
+  gtk_signal_connect_after (GTK_OBJECT (hex->hex_text), "delete_text",
+			    GTK_SIGNAL_FUNC (on_hex_text_deleted),
+			    hex);
+}
+
+
+guint
+gtk_hex_get_type ()
+{
+  static guint gtk_hex_type = 0;
+
+  if (!gtk_hex_type)
+    {
+      GtkTypeInfo gtk_hex_info =
+      {
+        "GtkHex",
+        sizeof (GtkHex),
+        sizeof (GtkHexClass),
+        (GtkClassInitFunc) gtk_hex_class_init,
+        (GtkObjectInitFunc) gtk_hex_init,
+	/* reserved_1 */ NULL,
+        /* reserved_2 */ NULL,
+        (GtkClassInitFunc) NULL,
+      };
+
+      gtk_hex_type = gtk_type_unique (gtk_vbox_get_type (), &gtk_hex_info);
+    }
+
+  return gtk_hex_type;
+}
+
+
+GtkWidget *
+gtk_hex_new(void)
+{
+    return GTK_WIDGET(gtk_type_new(gtk_hex_get_type()));
+}
+
+
+void
+gtk_hex_set_content(GtkHex *hex, char *arg_data, int data_size)
+{
+  guchar     *hex_text = NULL;
+  guchar     *null_map_text = NULL;
+  guchar     *data = NULL;
+
+  if (!hex || !arg_data || !data_size)
+    return;
+
+  /* Make a private copy of the data */
+  data = g_malloc (data_size);
+  if (!data)
+    return;
+  memcpy (data, arg_data, data_size);
+
+  switch (hex->mode)
+    {
+    case GTK_RAW_HEX:
+      hex_text = hex_get_hex_text(data, data_size);
+      if (!hex_text)
+	goto cleanup;
+      break;
+
+    case GTK_RAW_ASCII:
+      hex_get_ascii_text(data, data_size, &hex_text, &null_map_text);
+      if (!hex_text || !null_map_text)
+	goto cleanup;
+
+      gtk_editable_delete_text(GTK_EDITABLE(hex->priv->null_map), 0, -1);
+      gtk_text_insert(GTK_TEXT(hex->priv->null_map), hex->hex_font, NULL, NULL,
+		      null_map_text, strlen(null_map_text));
+      break;
+
+    default:
+      goto cleanup;
+    }
+
+  gtk_text_freeze(GTK_TEXT(hex->hex_text));
+  gtk_signal_handler_block_by_data(GTK_OBJECT(hex->hex_text), hex);
+
+  gtk_editable_delete_text(GTK_EDITABLE(hex->hex_text), 0, -1);
+  gtk_text_insert(GTK_TEXT(hex->hex_text), hex->hex_font, NULL, NULL,
+		  hex_text, strlen(hex_text));
+
+  gtk_signal_handler_unblock_by_data(GTK_OBJECT(hex->hex_text), hex);
+  gtk_text_thaw(GTK_TEXT(hex->hex_text));
+
+  /* Old data isn't freed -- we don't possess it! */
+  /* No longer true */
+  g_free(hex->data);
+  hex->data = data;
+  hex->data_size = hex->buf_size = data_size;
+
+ cleanup:
+  g_free(hex_text);
+  g_free(null_map_text);
+}
+
+
+void
+gtk_hex_set_mode(GtkHex *hex, GtkRawMode mode)
+{
+  if (!hex)
+    return;
+
+  hex->mode = mode;
+
+  switch(mode)
+    {
+    case GTK_RAW_ASCII:
+      gtk_text_set_line_wrap(GTK_TEXT(hex->hex_text), TRUE);
+      gtk_text_set_editable(GTK_TEXT(hex->hex_text), TRUE);
+      break;
+
+    case GTK_RAW_HEX:
+      gtk_text_set_line_wrap(GTK_TEXT(hex->hex_text), FALSE);
+      gtk_text_set_editable(GTK_TEXT(hex->hex_text), FALSE);
+      break;
+
+    default:
+      g_assert_not_reached();
+    }
+
+  gtk_hex_set_content(hex, hex->data, hex->data_size);
+}
diff -urN --exclude='*~' ethereal-0.9.14-orig/gtk/gtk_hex.h ethereal-0.9.14/gtk/gtk_hex.h
--- ethereal-0.9.14-orig/gtk/gtk_hex.h	1970-01-01 01:00:00.000000000 +0100
+++ ethereal-0.9.14/gtk/gtk_hex.h	2003-08-22 00:23:02.000000000 +0200
@@ -0,0 +1,96 @@
+/*
+
+Copyright (C) 2000, 2001, 2002 Christian Kreibich <christian@xxxxxxxxx>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies of the Software and its documentation and acknowledgment shall be
+given in the documentation and software packages that this Software was
+used.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+#ifndef __gtk_hex_h
+#define __gtk_hex_h
+
+#include <gdk/gdk.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkradiobutton.h>
+#include <gtk/gtktext.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define TOUCH(x)  { x = 0; }
+
+    
+#define GTK_TYPE_HEX          (gtk_hex_get_type())
+#define GTK_HEX(obj)          GTK_CHECK_CAST (obj, gtk_hex_get_type (), GtkHex)
+#define GTK_HEX_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, gtk_hex_get_type (), GtkHexClass)
+#define GTK_IS_HEX(obj)       GTK_CHECK_TYPE (obj, gtk_hex_get_type ())
+
+
+typedef enum {
+  GTK_RAW_ASCII,
+  GTK_RAW_HEX
+} GtkRawMode;
+
+
+typedef struct _GtkHex         GtkHex;
+typedef struct _GtkHexClass    GtkHexClass;
+typedef struct _GtkHexPrivate  GtkHexPrivate;
+
+struct _GtkHex
+{
+  GtkVBox         vbox;
+
+  GtkWidget      *mode_button_hex;
+  GtkWidget      *mode_button_ascii;
+  GtkWidget      *hex_text;
+
+  GtkRawMode      mode;
+
+  guchar         *data;
+  guint           data_size;
+  guint           buf_size;
+
+  GtkHexPrivate  *priv;
+  GdkFont        *hex_font;
+};
+
+struct _GtkHexClass
+{
+  GtkVBoxClass parent_class;
+
+  void (* changed)      (GtkHex *hex,
+			 gint         index,
+			 gint         value);
+  void (* move_cursor)  (GtkEditable *editable,
+			 gint         index);
+};
+
+
+guint      gtk_hex_get_type(void);
+GtkWidget *gtk_hex_new(void);
+void       gtk_hex_set_content(GtkHex *hex, char *data, int data_size);
+void       gtk_hex_set_mode(GtkHex *hex, GtkRawMode mode);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif
diff -urN --exclude='*~' ethereal-0.9.14-orig/gtk/Makefile.in ethereal-0.9.14/gtk/Makefile.in
--- ethereal-0.9.14-orig/gtk/Makefile.in	2003-07-24 00:37:28.000000000 +0200
+++ ethereal-0.9.14/gtk/Makefile.in	2003-07-26 00:42:38.000000000 +0200
@@ -250,6 +250,10 @@
 @USE_GTK2_TRUE@	proto_draw.h	\
 @USE_GTK2_TRUE@	proto_hier_stats_dlg.h	\
 @USE_GTK2_TRUE@	proto_hier_stats_dlg.c	\
+@USE_GTK2_TRUE@ gtk_hex.c       \
+@USE_GTK2_TRUE@ gtk_hex.h       \
+@USE_GTK2_TRUE@ gen_packet.c    \
+@USE_GTK2_TRUE@ gen_packet.h    \
 @USE_GTK2_TRUE@	simple_dialog.c	\
 @USE_GTK2_TRUE@	service_response_time_table.c	\
 @USE_GTK2_TRUE@	service_response_time_table.h	\
@@ -329,6 +333,10 @@
 @USE_GTK2_FALSE@	proto_draw.h	\
 @USE_GTK2_FALSE@	proto_hier_stats_dlg.h	\
 @USE_GTK2_FALSE@	proto_hier_stats_dlg.c	\
+@USE_GTK2_FALSE@        gtk_hex.c       \
+@USE_GTK2_FALSE@        gtk_hex.h       \
+@USE_GTK2_FALSE@        gen_packet.c    \
+@USE_GTK2_FALSE@        gen_packet.h    \
 @USE_GTK2_FALSE@	service_response_time_table.c	\
 @USE_GTK2_FALSE@	service_response_time_table.h	\
 @USE_GTK2_FALSE@	simple_dialog.c	\
@@ -379,6 +387,8 @@
 @USE_GTK2_TRUE@	print_prefs.$(OBJEXT) progress_dlg.$(OBJEXT) \
 @USE_GTK2_TRUE@	proto_dlg.$(OBJEXT) proto_draw.$(OBJEXT) \
 @USE_GTK2_TRUE@	proto_hier_stats_dlg.$(OBJEXT) \
+@USE_GTK2_TRUE@ gtk_hex.$(OBJEXT) \
+@USE_GTK2_TRUE@ gen_packet.$(OBJEXT) \
 @USE_GTK2_TRUE@	simple_dialog.$(OBJEXT) \
 @USE_GTK2_TRUE@	service_response_time_table.$(OBJEXT) \
 @USE_GTK2_TRUE@	stream_prefs.$(OBJEXT) summary_dlg.$(OBJEXT) \
@@ -404,6 +414,8 @@
 @USE_GTK2_FALSE@	progress_dlg.$(OBJEXT) proto_dlg.$(OBJEXT) \
 @USE_GTK2_FALSE@	proto_draw.$(OBJEXT) \
 @USE_GTK2_FALSE@	proto_hier_stats_dlg.$(OBJEXT) \
+@USE_GTK2_FALSE@        gtk_hex.$(OBJEXT) \
+@USE_GTK2_FALSE@        gen_packet.$(OBJEXT) \
 @USE_GTK2_FALSE@	service_response_time_table.$(OBJEXT) \
 @USE_GTK2_FALSE@	simple_dialog.$(OBJEXT) stream_prefs.$(OBJEXT) \
 @USE_GTK2_FALSE@	summary_dlg.$(OBJEXT) tcp_graph.$(OBJEXT) \
@@ -445,6 +457,8 @@
 @AMDEP_TRUE@	./$(DEPDIR)/progress_dlg.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/proto_dlg.Po ./$(DEPDIR)/proto_draw.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/proto_hier_stats_dlg.Po \
+@AMDEP_TRUE@    ./$(DEPDIR)/gtk_hex.Po \
+@AMDEP_TRUE@    ./$(DEPDIR)/gen_packet.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/rpc_progs.Po ./$(DEPDIR)/rpc_stat.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/service_response_time_table.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/simple_dialog.Po \
@@ -526,6 +540,8 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto_dlg.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto_draw.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto_hier_stats_dlg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_hex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gen_packet.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc_progs.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc_stat.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service_response_time_table.Po@am__quote@
diff -urN --exclude='*~' ethereal-0.9.14-orig/gtk/menu.c ethereal-0.9.14/gtk/menu.c
--- ethereal-0.9.14-orig/gtk/menu.c	2003-05-04 04:24:55.000000000 +0200
+++ ethereal-0.9.14/gtk/menu.c	2003-07-26 17:29:09.000000000 +0200
@@ -53,6 +53,7 @@
 #include "help_dlg.h"
 #include "proto_dlg.h"
 #include "proto_hier_stats_dlg.h"
+#include "gen_packet.h"
 #include "keys.h"
 #include <epan/plugins.h>
 #include "tcp_graph.h"
@@ -231,6 +232,8 @@
     ITEM_FACTORY_ENTRY("/Tools/Protocol Hierarchy Statistics", NULL,
                        proto_hier_stats_cb, 0, NULL, NULL),
     ITEM_FACTORY_ENTRY("/Tools/Statistics", NULL, NULL, 0, "<Branch>", NULL),
+    ITEM_FACTORY_ENTRY("/Tools/Packet Generator", NULL,  
+                       packet_generator, 0, NULL, NULL),
     ITEM_FACTORY_ENTRY("/_Help", NULL, NULL, 0, "<LastBranch>", NULL),
     ITEM_FACTORY_STOCK_ENTRY("/Help/_Help", NULL, help_cb, 0, GTK_STOCK_HELP),
     ITEM_FACTORY_ENTRY("/Help/<separator>", NULL, NULL, 0, "<Separator>", NULL),