From 14e2a790f9b63cca70d9bc60006d649d968dba28 Mon Sep 17 00:00:00 2001 From: Ulf Lamping Date: Mon, 29 Dec 2003 20:05:59 +0000 Subject: [PATCH] From Dick Gooris (and me :-) more ways to choose which packets can be saved, in the save(as) dialog box svn path=/trunk/; revision=9476 --- file.c | 85 +++++----- file.h | 5 +- gtk/Makefile.am | 5 +- gtk/Makefile.nmake | 3 +- gtk/file_dlg.c | 323 ++++++++++++++++++++++++++++++------- gtk/main.c | 4 +- gtk/range.c | 385 +++++++++++++++++++++++++++++++++++++++++++++ range.h | 73 +++++++++ 8 files changed, 782 insertions(+), 101 deletions(-) create mode 100644 gtk/range.c create mode 100644 range.h diff --git a/file.c b/file.c index 18456d6fb2..e078495ca3 100644 --- a/file.c +++ b/file.c @@ -1,7 +1,7 @@ /* file.c * File I/O routines * - * $Id: file.c,v 1.331 2003/12/19 23:39:53 guy Exp $ + * $Id: file.c,v 1.332 2003/12/29 20:03:38 ulfl Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs @@ -116,7 +116,7 @@ static gboolean find_packet(capture_file *cf, static char *file_rename_error_message(int err); static char *file_close_error_message(int err); -static gboolean copy_binary_file(char *from_filename, char *to_filename); +static gboolean copy_binary_file(char *from_filename, char *to_filename); /* Update the progress bar this many times when reading a file. */ #define N_PROGBAR_UPDATES 100 @@ -2242,8 +2242,7 @@ unmark_frame(capture_file *cf, frame_data *frame) * up a message box for the failure. */ gboolean -cf_save(char *fname, capture_file *cf, gboolean save_filtered, - gboolean save_marked, guint save_format) +cf_save(char *fname, capture_file *cf, packet_range_t *range, guint save_format) { gchar *from_filename; gchar *name_ptr, *save_msg, *save_fmt = " Saving: %s..."; @@ -2256,6 +2255,7 @@ cf_save(char *fname, capture_file *cf, gboolean save_filtered, union wtap_pseudo_header pseudo_header; guint8 pd[65536]; struct stat infile, outfile; + range_process_e process_this; name_ptr = get_basename(fname); msg_len = strlen(name_ptr) + strlen(save_fmt) + 2; @@ -2282,7 +2282,12 @@ cf_save(char *fname, capture_file *cf, gboolean save_filtered, goto fail; } - if (!save_filtered && !save_marked && save_format == cf->cd_t) { + /* Used to be : + * if (!save_filtered && !save_marked && !save_manual_range && + * !save_marked_range && !save_curr && save_format == cf->cd_t) { + */ + + if (packet_range_process_all(range) && save_format == cf->cd_t) { /* We're not filtering packets, and we're saving it in the format it's already in, so we can just move or copy the raw data. */ @@ -2349,49 +2354,53 @@ cf_save(char *fname, capture_file *cf, gboolean save_filtered, NetMon do? */ for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) { /* XXX - do a progress bar */ - if ((!save_filtered && !save_marked) || - (save_filtered && fdata->flags.passed_dfilter && !save_marked) || - (save_marked && fdata->flags.marked && !save_filtered) || - (save_filtered && save_marked && fdata->flags.passed_dfilter && - fdata->flags.marked)) { - /* Either : - - we're saving all frames, or - - we're saving filtered frames and this one passed the display filter or - - we're saving marked frames (and it has been marked) or - - we're saving filtered _and_ marked frames, - save it. */ - hdr.ts.tv_sec = fdata->abs_secs; - hdr.ts.tv_usec = fdata->abs_usecs; - hdr.caplen = fdata->cap_len; - hdr.len = fdata->pkt_len; - hdr.pkt_encap = fdata->lnk_t; - if (!wtap_seek_read(cf->wth, fdata->file_off, &pseudo_header, - pd, fdata->cap_len, &err)) { - simple_dialog(ESD_TYPE_CRIT, NULL, - file_read_error_message(err), cf->filename); - wtap_dump_close(pdh, &err); - goto fail; - } + process_this = packet_range_process(range, fdata); - if (!wtap_dump(pdh, &hdr, &pseudo_header, pd, &err)) { - simple_dialog(ESD_TYPE_CRIT, NULL, - file_write_error_message(err), fname); - wtap_dump_close(pdh, &err); - goto fail; - } - } - } + if (process_this == range_process_next) { + continue; + } else if (process_this == range_processing_finished) { + break; + } else { + + /* init the wtap header for saving */ + hdr.ts.tv_sec = fdata->abs_secs; + hdr.ts.tv_usec = fdata->abs_usecs; + hdr.caplen = fdata->cap_len; + hdr.len = fdata->pkt_len; + hdr.pkt_encap = fdata->lnk_t; + + /* Get the packet */ + if (!wtap_seek_read(cf->wth, fdata->file_off, &pseudo_header, + pd, fdata->cap_len, &err)) { + simple_dialog(ESD_TYPE_CRIT, NULL, + file_read_error_message(err), cf->filename); + wtap_dump_close(pdh, &err); + goto fail; + } + + /* and save the packet */ + if (!wtap_dump(pdh, &hdr, &pseudo_header, pd, &err)) { + simple_dialog(ESD_TYPE_CRIT, NULL, + file_write_error_message(err), fname); + wtap_dump_close(pdh, &err); + goto fail; + } + } + } /* for */ if (!wtap_dump_close(pdh, &err)) { simple_dialog(ESD_TYPE_WARN, NULL, file_close_error_message(err), fname); goto fail; } - } + } /* save_all */ /* Pop the "Saving:" message off the status bar. */ statusbar_pop_file_msg(); - if (!save_filtered && !save_marked) { + + /* XXX: I'm not sure how this should look like! */ + if (packet_range_process_all(range)) { + /*if (!save_filtered && !save_marked) {*/ /* We saved the entire capture, not just some packets from it. Open and read the file we saved it to. diff --git a/file.h b/file.h index ac60b0d664..0342446b00 100644 --- a/file.h +++ b/file.h @@ -1,7 +1,7 @@ /* file.h * Definitions for file structures and routines * - * $Id: file.h,v 1.108 2003/09/24 00:47:36 guy Exp $ + * $Id: file.h,v 1.109 2003/12/29 20:03:38 ulfl Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs @@ -25,6 +25,7 @@ #ifndef __FILE_H__ #define __FILE_H__ +#include "range.h" #include "wiretap/wtap.h" #include #include "print.h" @@ -48,7 +49,7 @@ int cf_start_tail(char *, gboolean, capture_file *); read_status_t cf_continue_tail(capture_file *, int, int *); read_status_t cf_finish_tail(capture_file *, int *); /* size_t read_frame_header(capture_file *); */ -gboolean cf_save(char *, capture_file *, gboolean, gboolean, guint); +gboolean cf_save(char *fname, capture_file * cf, packet_range_t *range, guint save_format); gchar *cf_get_display_name(capture_file *); int filter_packets(capture_file *cf, gchar *dfilter); diff --git a/gtk/Makefile.am b/gtk/Makefile.am index c4f542a785..b9c38af821 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -1,7 +1,7 @@ # Makefile.am # Automake file for the GTK interface routines for Ethereal # -# $Id: Makefile.am,v 1.81 2003/12/17 22:13:05 guy Exp $ +# $Id: Makefile.am,v 1.82 2003/12/29 20:05:59 ulfl Exp $ # # Ethereal - Network traffic analyzer # By Gerald Combs @@ -131,6 +131,7 @@ libui_a_SOURCES = \ proto_draw.h \ proto_hier_stats_dlg.h \ proto_hier_stats_dlg.c \ + range.c \ rtp_analysis.h \ rtp_stream.c \ rtp_stream.h \ @@ -225,6 +226,8 @@ libui_a_SOURCES = \ proto_draw.h \ proto_hier_stats_dlg.h \ proto_hier_stats_dlg.c \ + range.c \ + range.h \ rtp_analysis.h \ rtp_stream.c \ rtp_stream.h \ diff --git a/gtk/Makefile.nmake b/gtk/Makefile.nmake index 54c2c30b5b..709bda80d6 100644 --- a/gtk/Makefile.nmake +++ b/gtk/Makefile.nmake @@ -1,7 +1,7 @@ ## Makefile for building ethereal.exe with Microsoft C and nmake ## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake # -# $Id: Makefile.nmake,v 1.66 2003/12/17 22:13:05 guy Exp $ +# $Id: Makefile.nmake,v 1.67 2003/12/29 20:05:59 ulfl Exp $ include ..\config.nmake @@ -95,6 +95,7 @@ OBJECTS = \ proto_dlg.obj \ proto_draw.obj \ proto_hier_stats_dlg.obj \ + range.obj \ rtp_stream.obj \ service_response_time_table.obj \ simple_dialog.obj \ diff --git a/gtk/file_dlg.c b/gtk/file_dlg.c index 5799a9fea1..a27ac8c409 100644 --- a/gtk/file_dlg.c +++ b/gtk/file_dlg.c @@ -1,7 +1,7 @@ /* file_dlg.c * Dialog boxes for handling files * - * $Id: file_dlg.c,v 1.66 2003/12/01 02:01:55 guy Exp $ + * $Id: file_dlg.c,v 1.67 2003/12/29 20:05:59 ulfl Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs @@ -28,6 +28,7 @@ #include +#include "range.h" #include #include "globals.h" @@ -66,6 +67,14 @@ static void file_select_destroy_cb(GtkWidget *win, GtkWidget* file_te); #define ARGUMENT_CL "argument_cl" +/* + * Keep a static pointer to the current "Save Capture File As" window, if + * any, so that if somebody tries to do "File:Save" or "File:Save As" + * while there's already a "Save Capture File As" window up, we just pop + * up the existing one, rather than creating a new one. + */ +static GtkWidget *file_save_as_w; + /* * A generic select_file_cb routine that is intended to be connected to * a Browse button on other dialog boxes. This allows the user to browse @@ -450,12 +459,16 @@ file_save_cmd_cb(GtkWidget *w, gpointer data) { } /* XXX - can we make these not be static? */ -static gboolean filtered; -static gboolean marked; +static packet_range_t range; static gboolean color_marked; static int filetype; static GtkWidget *filter_cb; -static GtkWidget *mark_cb; +static GtkWidget *select_all; +static GtkWidget *select_curr; +static GtkWidget *select_marked_only; +static GtkWidget *select_marked_range; +static GtkWidget *select_manual_range; +static GtkWidget *range_specs; static GtkWidget *cfmark_cb; static GtkWidget *ft_om; @@ -480,8 +493,7 @@ can_save_with_wiretap(int ft) and FALSE if we're to save the entire file (in which case, if we're saving it in the type it has already, we can just copy it). - "marked" is TRUE if we have to save only the marked packets, - the same remark as "filtered" applies. + The same applies for sel_curr, sel_all, sel_m_only, sel_m_range and sel_man_range */ static void set_file_type_list(GtkWidget *option_menu) @@ -500,8 +512,8 @@ set_file_type_list(GtkWidget *option_menu) /* Check all file types. */ index = 0; for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) { - if (filtered || marked || ft != cfile.cd_t) { - /* Filtered, marked or a different file type. We have to use Wiretap. */ + if (!packet_range_process_all(&range) || ft != cfile.cd_t) { + /* not all unfiltered packets or a different file type. We have to use Wiretap. */ if (!can_save_with_wiretap(ft)) continue; /* We can't. */ } @@ -541,43 +553,114 @@ static void toggle_filtered_cb(GtkWidget *widget, gpointer data _U_) { gboolean new_filtered; - + new_filtered = GTK_TOGGLE_BUTTON (widget)->active; - - if (filtered != new_filtered) { + + if (range.process_filtered != new_filtered) { /* They changed the state of the "filtered" button. */ - filtered = new_filtered; + range.process_filtered = new_filtered; set_file_type_list(ft_om); } } static void -toggle_marked_cb(GtkWidget *widget, gpointer data _U_) +toggle_select_all(GtkWidget *widget, gpointer data _U_) +{ + gboolean new_all; + + new_all = GTK_TOGGLE_BUTTON (widget)->active; + + if (range.process_all != new_all) { + /* They changed the state of the "select-all" button. */ + range.process_all = new_all; + set_file_type_list(ft_om); + } +} + +static void +toggle_select_curr(GtkWidget *widget, gpointer data _U_) +{ + gboolean new_curr; + + new_curr = GTK_TOGGLE_BUTTON (widget)->active; + + if (range.process_curr != new_curr) { + /* They changed the state of the "select-current" button. */ + range.process_curr = new_curr; + set_file_type_list(ft_om); + } +} + +static void +toggle_select_marked_only(GtkWidget *widget, gpointer data _U_) { gboolean new_marked; new_marked = GTK_TOGGLE_BUTTON (widget)->active; - if (marked != new_marked) { - /* They changed the state of the "marked" button. */ - marked = new_marked; + if (range.process_marked != new_marked) { + /* They changed the state of the "marked-only" button. */ + range.process_marked = new_marked; set_file_type_list(ft_om); } } -/* - * Keep a static pointer to the current "Save Capture File As" window, if - * any, so that if somebody tries to do "File:Save" or "File:Save As" - * while there's already a "Save Capture File As" window up, we just pop - * up the existing one, rather than creating a new one. - */ -static GtkWidget *file_save_as_w; +static void +toggle_select_marked_range(GtkWidget *widget, gpointer data _U_) +{ + gboolean new_marked_range; + + new_marked_range = GTK_TOGGLE_BUTTON (widget)->active; + + if (range.process_marked_range != new_marked_range) { + /* They changed the state of the "marked-range" button. */ + range.process_marked_range = new_marked_range; + set_file_type_list(ft_om); + } +} + +static void +toggle_select_manual_range(GtkWidget *widget, gpointer data _U_) +{ + gboolean new_manual_range; + + new_manual_range = GTK_TOGGLE_BUTTON (widget)->active; + + if (range.process_manual_range != new_manual_range) { + /* They changed the state of the "manual-range" button. */ + range.process_manual_range = new_manual_range; + set_file_type_list(ft_om); + } + + /* Make the entry widget sensitive or insensitive */ + gtk_widget_set_sensitive(range_specs, range.process_manual_range); + + /* When selecting manual range, then focus on the entry */ + if (range.process_manual_range) + gtk_widget_grab_focus(range_specs); + +} + +static void +range_entry(GtkWidget *entry) +{ + gchar *entry_text; + entry_text = gtk_entry_get_text (GTK_ENTRY (entry)); + packet_range_convert_str(entry_text); +} void file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_) { - GtkWidget *ok_bt, *main_vb, *ft_hb, *ft_lb; - + GtkWidget *ok_bt, *main_vb, *ft_hb, *ft_lb, *range_fr, *range_vb, *range_tb; + GtkTooltips *tooltips; + gchar label_text[100]; + frame_data *packet; + +#if GTK_MAJOR_VERSION < 2 + GtkAccelGroup *accel_group; +#endif + if (file_save_as_w != NULL) { /* There's already an "Save Capture File As" dialog box; reactivate it. */ reactivate_window(file_save_as_w); @@ -585,13 +668,28 @@ file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_) } /* Default to saving all packets, in the file's current format. */ - filtered = FALSE; - marked = FALSE; - filetype = cfile.cd_t; + range.process_all = TRUE; + range.process_curr = FALSE; + range.process_marked = FALSE; + range.process_marked_range = FALSE; + range.process_manual_range = FALSE; + range.process_filtered = FALSE; + filetype = cfile.cd_t; + /* init the packet range */ + packet_range_init(&range); + + /* Enable tooltips */ + tooltips = gtk_tooltips_new(); + file_save_as_w = file_selection_new ("Ethereal: Save Capture File As"); SIGNAL_CONNECT(file_save_as_w, "destroy", file_save_as_destroy_cb, NULL); +#if GTK_MAJOR_VERSION < 2 + accel_group = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(file_save_as_w), accel_group); +#endif + /* If we've opened a file, start out by showing the files in the directory in which that file resided. */ if (last_open_dir) @@ -603,11 +701,123 @@ file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_) SIGNAL_CONNECT(ok_bt, "clicked", file_save_as_ok_cb, file_save_as_w); /* Container for each row of widgets */ - main_vb = gtk_vbox_new(FALSE, 3); + + main_vb = gtk_vbox_new(FALSE, 5); gtk_container_border_width(GTK_CONTAINER(main_vb), 5); gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_save_as_w)->action_area), main_vb, FALSE, FALSE, 0); - gtk_widget_show(main_vb); + gtk_widget_show(main_vb); + + /*** Save Range frame ***/ + range_fr = gtk_frame_new("Save Range"); + gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0); + gtk_widget_show(range_fr); + range_vb = gtk_vbox_new(FALSE,6); + gtk_container_border_width(GTK_CONTAINER(range_vb), 5); + gtk_container_add(GTK_CONTAINER(range_fr), range_vb); + gtk_widget_show(range_vb); + + + /* + * The argument above could, I guess, be applied to the marked packets, + * except that you can't easily tell whether there are any marked + * packets, so I could imagine users doing "Save only marked packets" + * when there aren't any marked packets, not knowing that they'd + * failed to mark them, so I'm more inclined to have the "Save only + * marked packets" toggle button enabled only if there are marked + * packets to save. + */ + + /* Save all packets */ + g_snprintf(label_text, sizeof(label_text), "All _captured %s (%u %s)", + plurality(cfile.count, "packet", "packets"), cfile.count, plurality(cfile.count, "packet", "packets")); +#if GTK_MAJOR_VERSION < 2 + select_all = dlg_radio_button_new_with_label_with_mnemonic(NULL, label_text ,accel_group); +#else + select_all = gtk_radio_button_new_with_mnemonic(NULL, label_text); +#endif + gtk_container_add(GTK_CONTAINER(range_vb), select_all); + gtk_tooltips_set_tip (tooltips,select_all,("Save all captured packets"), NULL); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(select_all), FALSE); + SIGNAL_CONNECT(select_all, "toggled", toggle_select_all, NULL); + gtk_widget_show(select_all); + + /* Save currently selected */ + g_snprintf(label_text, sizeof(label_text), "_Selected packet #%u only", cfile.current_frame->num); +#if GTK_MAJOR_VERSION < 2 + select_curr = dlg_radio_button_new_with_label_with_mnemonic(gtk_radio_button_group (GTK_RADIO_BUTTON (select_all)), + label_text,accel_group); +#else + select_curr = gtk_radio_button_new_with_mnemonic(gtk_radio_button_group (GTK_RADIO_BUTTON (select_all)), + label_text); +#endif + gtk_container_add(GTK_CONTAINER(range_vb), select_curr); + gtk_tooltips_set_tip (tooltips,select_curr,("Save the currently selected packet only"), NULL); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(select_curr), FALSE); + SIGNAL_CONNECT(select_curr, "toggled", toggle_select_curr, NULL); + gtk_widget_show(select_curr); + + /* Save marked packets */ + g_snprintf(label_text, sizeof(label_text), "_Marked %s only (%u %s)", + plurality(cfile.marked_count, "packet", "packets"), cfile.marked_count, plurality(cfile.marked_count, "packet", "packets")); +#if GTK_MAJOR_VERSION < 2 + select_marked_only = dlg_radio_button_new_with_label_with_mnemonic(gtk_radio_button_group (GTK_RADIO_BUTTON (select_all)), + label_text,accel_group); +#else + select_marked_only = gtk_radio_button_new_with_mnemonic(gtk_radio_button_group (GTK_RADIO_BUTTON (select_all)), + label_text); +#endif + gtk_container_add(GTK_CONTAINER(range_vb), select_marked_only); + gtk_tooltips_set_tip (tooltips,select_marked_only,("Save marked packets only"), NULL); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(select_marked_only), FALSE); + SIGNAL_CONNECT(select_marked_only, "toggled", toggle_select_marked_only, NULL); + gtk_widget_show(select_marked_only); + + /* Save packet range between first and last packet */ + g_snprintf(label_text, sizeof(label_text), "From first _to last marked packet (%u %s)", + range.mark_range, plurality(range.mark_range, "packet", "packets")); +#if GTK_MAJOR_VERSION < 2 + select_marked_range = dlg_radio_button_new_with_label_with_mnemonic(gtk_radio_button_group (GTK_RADIO_BUTTON (select_all)), + label_text,accel_group); +#else + select_marked_range = gtk_radio_button_new_with_mnemonic(gtk_radio_button_group (GTK_RADIO_BUTTON (select_all)), + label_text); +#endif + gtk_container_add(GTK_CONTAINER(range_vb), select_marked_range); + gtk_tooltips_set_tip (tooltips,select_marked_range,("Save all packets between the first and last marker"), NULL); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(select_marked_range), FALSE); + SIGNAL_CONNECT(select_marked_range, "toggled", toggle_select_marked_range, NULL); + gtk_widget_show(select_marked_range); + + /* Range table */ + range_tb = gtk_table_new(2, 2, FALSE); + gtk_box_pack_start(GTK_BOX(range_vb), range_tb, FALSE, FALSE, 0); + gtk_widget_show(range_tb); + + /* Save a manually provided packet range : -10,30,40-70,80- */ + g_snprintf(label_text, sizeof(label_text), "Specify a packet _range :"); +#if GTK_MAJOR_VERSION < 2 + select_manual_range = dlg_radio_button_new_with_label_with_mnemonic(gtk_radio_button_group (GTK_RADIO_BUTTON (select_all)), + label_text,accel_group); +#else + select_manual_range = gtk_radio_button_new_with_mnemonic(gtk_radio_button_group (GTK_RADIO_BUTTON (select_all)), + label_text); +#endif + gtk_table_attach_defaults(GTK_TABLE(range_tb), select_manual_range, 0, 1, 1, 2); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(select_manual_range), FALSE); + gtk_tooltips_set_tip (tooltips,select_manual_range,("Save a specified packet range"), NULL); + SIGNAL_CONNECT(select_manual_range, "toggled", toggle_select_manual_range, NULL); + gtk_widget_show(select_manual_range); + + /* The entry part */ + range_specs = gtk_entry_new(); + gtk_entry_set_max_length (GTK_ENTRY (range_specs), 254); + gtk_table_attach_defaults(GTK_TABLE(range_tb), range_specs, 1, 2, 1, 2); + gtk_tooltips_set_tip (tooltips,range_specs, + ("Specify a range of packet numbers : \nExample : 1-10,18,25-100,332-"), NULL); + SIGNAL_CONNECT(range_specs,"activate", range_entry, range_specs); + gtk_widget_set_sensitive(range_specs, FALSE); + gtk_widget_show(range_specs); /* * XXX - should this be sensitive only if the current display filter @@ -619,29 +829,18 @@ file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_) * I guess, argue that the user may want to "save all the displayed * packets" even if there aren't any, i.e. save an empty file. */ - filter_cb = gtk_check_button_new_with_label("Save only packets currently being displayed"); - gtk_container_add(GTK_CONTAINER(main_vb), filter_cb); +#if GTK_MAJOR_VERSION < 2 + filter_cb = dlg_check_button_new_with_label_with_mnemonic("Apply _display filter",accel_group); +#else + filter_cb = gtk_check_button_new_with_mnemonic("Apply _display filter"); +#endif + gtk_container_add(GTK_CONTAINER(range_vb), filter_cb); gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(filter_cb), FALSE); SIGNAL_CONNECT(filter_cb, "toggled", toggle_filtered_cb, NULL); gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(filetype)); - gtk_widget_show(filter_cb); - - /* - * The argument above could, I guess, be applied to the marked packets, - * except that you can't easily tell whether there are any marked - * packets, so I could imagine users doing "Save only marked packets" - * when there aren't any marked packets, not knowing that they'd - * failed to mark them, so I'm more inclined to have the "Save only - * marked packets" toggle button enabled only if there are marked - * packets to save. - */ - mark_cb = gtk_check_button_new_with_label("Save only marked packets"); - gtk_container_add(GTK_CONTAINER(main_vb), mark_cb); - marked = FALSE; - gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(mark_cb), FALSE); - SIGNAL_CONNECT(mark_cb, "toggled", toggle_marked_cb, NULL); - gtk_widget_show(mark_cb); + gtk_widget_show(filter_cb); + /* File type row */ ft_hb = gtk_hbox_new(FALSE, 3); gtk_container_add(GTK_CONTAINER(main_vb), ft_hb); @@ -666,7 +865,7 @@ file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_) * as the routine that sets it also sets that menu. */ file_set_save_marked_sensitive(); - + /* Connect the cancel_button to destroy the widget */ SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(file_save_as_w)->cancel_button, "clicked", (GtkSignalFunc)gtk_widget_destroy, @@ -696,19 +895,23 @@ file_set_save_marked_sensitive(void) /* We don't currently have a "Save As..." dialog box up. */ return; } - + /* We can request that only the marked packets be saved only if we can use Wiretap to save the file and if there *are* marked packets. */ - if (can_save_with_wiretap(filetype) && cfile.marked_count != 0) - gtk_widget_set_sensitive(mark_cb, TRUE); + if (can_save_with_wiretap(filetype) && cfile.marked_count != 0) { + gtk_widget_set_sensitive(select_marked_only, TRUE); + gtk_widget_set_sensitive(select_marked_range, TRUE); + } else { /* Force the "Save only marked packets" toggle to "false", turn off the flag it controls, and update the list of types we can save the file as. */ - marked = FALSE; - gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(mark_cb), FALSE); + range.process_marked = FALSE; + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(select_marked_only), FALSE); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(select_marked_range), FALSE); set_file_type_list(ft_om); - gtk_widget_set_sensitive(mark_cb, FALSE); + gtk_widget_set_sensitive(select_marked_only, FALSE); + gtk_widget_set_sensitive(select_marked_range, FALSE); } } @@ -717,6 +920,11 @@ file_save_as_ok_cb(GtkWidget *w _U_, GtkFileSelection *fs) { gchar *cf_name; gchar *dirname; + /* obtain the range specifications in case we selected manual range */ + if (range.process_manual_range) { + range_entry(range_specs); + } + cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs))); /* Perhaps the user specified a directory instead of a file. @@ -733,7 +941,7 @@ file_save_as_ok_cb(GtkWidget *w _U_, GtkFileSelection *fs) { /* Write out the packets (all, or only the ones that are currently displayed or marked) to the file with the specified name. */ - if (! cf_save(cf_name, &cfile, filtered, marked, filetype)) { + if (! cf_save(cf_name, &cfile, &range, filetype)) { /* The write failed; don't dismiss the open dialog box, just leave it around so that the user can, after they dismiss the alert box popped up for the error, try again. */ @@ -1002,7 +1210,6 @@ file_color_export_cmd_cb(GtkWidget *w _U_, gpointer data _U_) } /* Default to saving all packets, in the file's current format. */ - filtered = FALSE; color_marked = FALSE; filetype = cfile.cd_t; diff --git a/gtk/main.c b/gtk/main.c index bc44a07f14..740bf245fa 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -1,6 +1,6 @@ /* main.c * - * $Id: main.c,v 1.346 2003/12/21 12:17:59 ulfl Exp $ + * $Id: main.c,v 1.347 2003/12/29 20:05:59 ulfl Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs @@ -1022,6 +1022,8 @@ void mark_frame_cb(GtkWidget *w _U_, gpointer data _U_) { static void mark_all_frames(gboolean set) { frame_data *fdata; + + cfile.marked_count = 0; for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) { set_frame_mark(set, fdata, diff --git a/gtk/range.c b/gtk/range.c new file mode 100644 index 0000000000..8fb3254699 --- /dev/null +++ b/gtk/range.c @@ -0,0 +1,385 @@ +/* range.c + * Packet range routines (save, print, ...) + * + * Dick Gooris + * Ulf Lamping + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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 + +#include + + +#include "range.h" + +#include "globals.h" + + +static gboolean packet_is_in_range(guint32 val); + + +/* Range parser variables */ +#define MaxRange 30 + +struct range_admin { + guint32 low; + guint32 high; +}; + +static guint GLnrange=0; +struct range_admin GLrange[MaxRange]; +static guint32 max_packets; + +void packet_range_init(packet_range_t *range) { + guint32 current_count; + guint32 displayed_count; + guint32 mark_low; + guint32 mark_high; + frame_data *packet; + + /* This is needed to calculate the number of packets for each variation + * like the total amount of packets, the number of marked packets, and + * the number between the packets. This is for information only + */ + + /* "enumeration" values */ + range->markers = cfile.marked_count; + range->range_active = FALSE; + range->process_curr_done = FALSE; + + displayed_count = 0L; + mark_low = 0L; + mark_high = 0L; + range->mark_range = 0L; + + current_count = 0; + for(packet = cfile.plist; packet != NULL; packet = packet->next) { + current_count++; + if (cfile.current_frame == packet) { + range->selected_packet = current_count; + } + if (packet->flags.passed_dfilter) { + displayed_count++; + } + if (packet->flags.marked) { + if (mark_low == 0) { + mark_low = current_count; + } + if (current_count > mark_high) { + mark_high = current_count; + } + } + } + /* in case we marked just one packet, we add 1. */ + if (cfile.marked_count != 0) { + range->mark_range = mark_high - mark_low + 1; + } + /* make this global, to be used in function conv_str_range() */ + max_packets = cfile.count; +} + + +/* do we have to process all packets? */ +gboolean packet_range_process_all(packet_range_t *range) { + return range->process_all && !range->process_filtered; +} + +/* do we have to process this packet? */ +range_process_e packet_range_process(packet_range_t *range, frame_data *fdata) { + + /* do we have to process this packet at all? */ + if ( + (!range->process_filtered && !range->process_marked) || + (range->process_filtered && fdata->flags.passed_dfilter && !range->process_marked) || + (range->process_marked && fdata->flags.marked && !range->process_filtered) || + (range->process_filtered && range->process_marked && fdata->flags.passed_dfilter && fdata->flags.marked) || + (range->process_curr) || + (range->process_marked_range) || + (range->process_manual_range) || + (range->range_active) + ) { + /* yes, we have to */ + } else { + return range_process_next; + } + + /* In case we process a manual range, we check the packet number + * with the range as defined in the array GLrange, see file_dlg.c + * If a match is found, we process it, otherwise we simply go to check + * the next packet. + */ + if (range->process_manual_range) { + if (range->process_filtered) { + if (fdata->flags.passed_dfilter == FALSE) { + return range_process_next; + } + } + if (packet_is_in_range(fdata->num) == FALSE) { + return range_process_next; + } + } + + /* For processing a marked range, skip the frames not marked in the first place + * until the first marked frame comes by. Then continue processing until we found the + * last marked frame. We set the range_active to FALSE in the first place until + * a marked frame is found (fdata->flags.marked == TRUE) From now on range_active + * is TRUE, and the large 'if' statement will pass by any frame. It will stop doing + * so once the markers count got 0. process_marked_range got set in gtk/file_dlg.c + */ + if (range->process_marked_range) { + if (range->markers == 0) { + return range_processing_finished; + } + if (fdata->flags.marked == TRUE) { + range->range_active = TRUE; + range->markers--; + } + if (range->process_filtered) { + if (fdata->flags.passed_dfilter == FALSE) { + return range_process_next; + } + } + if (range->range_active == FALSE ) { + return range_process_next; + } + } + + /* Only process the selected frame. If accomplished, finish */ + if (range->process_curr) { + if (range->process_curr_done) { + return range_processing_finished; + } + if (fdata->num != cfile.current_frame->num) { + return range_process_next; + } + range->process_curr_done = TRUE; + } + + return range_process_this; +} + + + + +/******************** Range Entry Parser *********************************/ + +/* Convert the entry range string in a fast comparable array of ranges. + * In the first place get rid of spaces, and any other characters than + * commas, digits, and hyphens. The parameter es points to the string to be processed + * + * This function is only called once when a range string is provided in the Save/Print As + * widget. This function fills an array of low and high values indexed by a global + * varaiable GLnrange. After having called this function, the function isin(val) + * determines whether the value is with the range or not. + */ + +void packet_range_convert_str(gchar *es) +{ + gchar EntryStr[255], OrgStr[255], value[255], p; + guint i, j=0; + guint32 tmp, val; + gboolean hyphenseen; + + /* Reset the number of ranges we are going to find */ + GLnrange = 0; + GLrange[GLnrange].low = 0L; + GLrange[GLnrange].high = 0L; + + /* Make a copy of the string, and check the validity of the input */ + strcpy(OrgStr,es); + if (strlen(OrgStr) == 0 ) { + return; + } + + /* only keep digits, comma's, and hyphens. */ + for (i=0; i<=strlen(OrgStr); i++) { + if ( isdigit(OrgStr[i]) || OrgStr[i] == '-' || OrgStr[i] == ',' ) { + EntryStr[j++] = OrgStr[i]; + } + } + EntryStr[j] = '\0'; + + /* remove any starting commas */ + strcpy(OrgStr,EntryStr); + i = 0; + while (OrgStr[i] == ',') { + i++; + } + strcpy(EntryStr,OrgStr+i); + + /* remove any double commas within the entry string */ + strcpy(OrgStr,EntryStr); + p = ','; + j = 0; + for (i=0; i<=strlen(OrgStr); i++) { + if ( OrgStr[i] != ',' || p != ',') { + EntryStr[j++] = OrgStr[i]; + } + p = OrgStr[i]; + } + EntryStr[j] = '\0'; + + /* remove any double hyphens within the entry string */ + strcpy(OrgStr,EntryStr); + p = '-'; + j = 0; + for (i=0; i<=strlen(OrgStr); i++) { + if (OrgStr[i] != '-' || p != '-' || i == 0) { + EntryStr[j++] = OrgStr[i]; + } + p = OrgStr[i]; + } + EntryStr[j] = '\0'; + + /* remove any trailing commas */ + i = strlen(EntryStr) - 1; + while (EntryStr[i] == ',') { + EntryStr[i] = '\0'; + i--; + } + + /* The entry string is now filtered, and ready for further parsing */ + /* printf("str=%s\n",EntryStr); */ + + /* Now we are going to process the ranges separately until we get a comma, + * or end of string. The following input are interpreted all right : + * + * 0-20,30-40 -=> Range from 0 to 20, and packets 30 to 40 + * -20,30 -=> Range from 0 to 20, and packet 30 + * 20,30,40- -=> Packet number 20, 30, and the range from 40 to the end + * 20-10,30-25 -=> Range from 10 to 20, and from 25 to 30 + * - -=> All packets + * + * We build a structure array called GLrange of high and low values. After the + * following loop, we have the GLnrange variable which tells how many ranges + * are found. + * The number of different ranges is limited to 'MaxRanges' + */ + + j = 0; + hyphenseen = FALSE; + for (i=0; i<=strlen(EntryStr);i++) { + + /* copy the digit string until a no-digit character is seen */ + if (isdigit(EntryStr[i])) { + value[j++] = EntryStr[i]; + continue; + } + + /* Terminate the digit string, and convert it */ + value[j] = '\0'; + val=atol(value); + j=0; + + /* treatment in case we see a hyphen */ + if (EntryStr[i] == '-') { + /* if this is a trailer hyphen, then treat it in a different + * way, then the high value is the maximum number of packets counted + * and we are ready */ + if (i == strlen(EntryStr)-1) { + GLrange[GLnrange].low = val; + GLrange[GLnrange].high = max_packets; + GLnrange++; + break; + } else { + /* if no digits were actually seen, the outcome of + * a zeroed string conversion to interger is also 0. */ + GLrange[GLnrange].low = val; + } + hyphenseen=TRUE; + continue; + } + + /* treatment in case we see a comma, or end of string */ + if (EntryStr[i] == ',' || i == strlen(EntryStr)) { + if (hyphenseen) { + GLrange[GLnrange].high = val; + } else { + /* in this case we got a single packet number */ + GLrange[GLnrange].low = val; + GLrange[GLnrange].high = val; + } + hyphenseen=FALSE; + } + + /* Increase the index for ranges found, and protect + * against wildly outside array bounds */ + GLnrange++; + if (GLnrange > MaxRange) { + GLnrange--; + } + } + GLnrange--; + + /* Now we are going through the low and high values, and check + * whether they are in a proper order. Low should be equal or lower + * than high. So, go through the loop and swap if needed + */ + for (i=0; i <= GLnrange; i++) { + if (GLrange[i].low > GLrange[i].high) { + tmp = GLrange[i].low; + GLrange[i].low = GLrange[i].high; + GLrange[i].high = tmp; + } + } + + /* In case we want to know what the result ranges are : + * + * for (i=0; i <= GLnrange; i++) { + * printf("L=%u\tH=%u\n",GLrange[i].low,GLrange[i].high); + * } + * + */ + + /* End of conv_str_range() */ + return; +} + +/* This function returns TRUE is the given value is within the range + * of the input range entered via (Save/Print As). This is supposed to + * be a tiny and quick procedure since this is called for every packet + * to be potentially saved. + */ +static gboolean packet_is_in_range(guint32 val) +{ + guint i; + + for (i=0; i <= GLnrange; i++) { + if (val >= GLrange[i].low && val <= GLrange[i].high) + return TRUE; + } + return(FALSE); +} + +static void packet_is_in_range_check(guint32 val) +{ + printf("Checking %d\t",val); + if (packet_is_in_range(val)) { + printf("TRUE\n"); + } else { + printf("FALSE\n"); + } +} + diff --git a/range.h b/range.h new file mode 100644 index 0000000000..86497e5175 --- /dev/null +++ b/range.h @@ -0,0 +1,73 @@ +/* range.h + * Packet range routines (save, print, ...) + * + * Dick Gooris + * Ulf Lamping + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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 __PRINT_RANGE_H__ +#define __PRINT_RANGE_H__ + +#include + +#include + +extern guint32 curr_selected_frame; + +typedef struct packet_range_tag { + /* values coming from the UI */ + /* XXX: use an enum instead? */ + gboolean process_all; + gboolean process_curr; + gboolean process_marked; + gboolean process_marked_range; + gboolean process_manual_range; + gboolean process_filtered; + + /* calculated values */ + guint32 mark_range; + guint32 selected_packet; + + /* "enumeration" values */ + gboolean range_active; + guint32 markers; + gboolean process_curr_done; +} packet_range_t; + +typedef enum { + range_process_next, + range_processing_finished, + range_process_this +} range_process_e; + +/* init the range structure */ +extern void packet_range_init(packet_range_t *range); + +/* do we have to process all packets? */ +extern gboolean packet_range_process_all(packet_range_t *range); + +/* do we have to process this packet? */ +extern range_process_e packet_range_process(packet_range_t *range, frame_data *fdata); + +extern void packet_range_convert_str(gchar *es); + + +#endif /* __PRINT_RANGE_H__ */ \ No newline at end of file