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, + >K_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, + >K_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, + >K_WIDGET(hex_text)->style->text[GTK_STATE_INSENSITIVE], + >K_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, + >K_WIDGET(hex_text)->style->text[GTK_STATE_INSENSITIVE], + >K_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, + >K_WIDGET(hex_text)->style->text[GTK_STATE_INSENSITIVE], + >K_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 (), >k_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),
- Prev by Date: [Ethereal-dev] Update to packet-m3ua.c
- Next by Date: Re: [Ethereal-dev] [PATCH] Wish list item 17 (packet generator)
- Previous by thread: Re: [Ethereal-dev] Update to packet-m3ua.c
- Next by thread: Re: [Ethereal-dev] [PATCH] Wish list item 17 (packet generator)
- Index(es):