"Save As" always saves everything and, when the save is done, makes the

new file the current file, as is the case in most if not all other GUI
applications.

A new "Export Specified Packets" menu option allows you to specify which
packets to write out, with the default being the displayed packets (and
those on which the displayed packets depend for, e.g.  reassembly), and
never makes the resulting file the current file.

The two operations are conceptually distinct.  Lumping them into one
menu item, with the default for "Save As" being "displayed packets only"
and thus making it behave like the latter operation, was causing some
confusion; see, for example, bug 6640.

Make the dialog popped up if you try to "Save As" or "Export Specified
Packets" on top of an existing file ask the "do you want to do this?"
question in the main part of the message, and note in the secondary text
that doing that will overwrite what's in the file; that matches what
TextEdit on OS X and the GNOME text editor say.

svn path=/trunk/; revision=42792
This commit is contained in:
Guy Harris 2012-05-22 22:17:57 +00:00
parent 4e7d87caa2
commit c2bb7956c2
10 changed files with 506 additions and 160 deletions

209
file.c
View File

@ -3806,13 +3806,14 @@ cf_can_save_as(capture_file *cf)
}
static cf_status_t
cf_save_packets(capture_file *cf, const char *fname, packet_range_t *range,
guint save_format, gboolean compressed, gboolean do_overwrite)
cf_save_packets(capture_file *cf, const char *fname, guint save_format,
gboolean compressed, gboolean do_overwrite)
{
gchar *fname_new = NULL;
int err;
gboolean do_copy;
wtap_dumper *pdh;
packet_range_t range;
save_callback_args_t callback_args;
cf_callback_invoke(cf_cb_file_save_started, (gpointer)fname);
@ -3843,14 +3844,10 @@ cf_save_packets(capture_file *cf, const char *fname, packet_range_t *range,
}
}
packet_range_process_init(range);
if (packet_range_process_all(range) && save_format == cf->cd_t
&& !cf->unsaved_changes) {
/* We're not filtering packets, and we're saving it in the format
it's already in, and there are no changes we have in memory
that aren't saved to the file, so we can just move or copy the
raw data. */
if (save_format == cf->cd_t && !cf->unsaved_changes) {
/* We're saving in the format it's already in, and there are no
changes we have in memory that aren't saved to the file, so
we can just move or copy the raw data. */
if (cf->is_tempfile) {
/* The file being saved is a temporary file from a live
@ -3905,12 +3902,11 @@ cf_save_packets(capture_file *cf, const char *fname, packet_range_t *range,
goto fail;
}
} else {
/* Either we're filtering packets, or we're saving in a different
format, or we're saving changes, such as added, modified, or
removed comments, that haven't yet been written to the
underlying file; we can't do that by copying or moving the
capture file, we have to do it by writing the packets out in
Wiretap. */
/* Either we're saving in a different format or we're saving changes,
such as added, modified, or removed comments, that haven't yet
been written to the underlying file; we can't do that by copying
or moving the capture file, we have to do it by writing the packets
out in Wiretap. */
wtapng_section_t *shb_hdr = NULL;
wtapng_iface_descriptions_t *idb_inf = NULL;
@ -3943,23 +3939,15 @@ cf_save_packets(capture_file *cf, const char *fname, packet_range_t *range,
/* Add address resolution */
wtap_dump_set_addrinfo_list(pdh, get_addrinfo_list());
/* XXX - we let the user save a subset of the packets.
/* Create a packet range that's set to the default "save everything"
state. */
packet_range_init(&range);
If we do that, should we make that file the current file? If so,
it means we can no longer get at the other packets. What does
NetMon do? */
/* Iterate through the list of packets, processing the packets we were
told to process.
XXX - we've already called "packet_range_process_init(range)", but
"process_specified_packets()" will do it again. Fortunately,
that's harmless in this case, as we haven't done anything to
"range" since we initialized it. */
/* Iterate through the list of packets, processing all the packets. */
callback_args.pdh = pdh;
callback_args.fname = fname;
callback_args.file_type = save_format;
switch (process_specified_packets(cf, range, "Saving", "selected packets",
switch (process_specified_packets(cf, &range, "Saving", "selected packets",
TRUE, save_packet, &callback_args)) {
case PSP_FINISHED:
@ -4006,44 +3994,41 @@ cf_save_packets(capture_file *cf, const char *fname, packet_range_t *range,
cf_callback_invoke(cf_cb_file_save_finished, NULL);
if (packet_range_process_all(range)) {
/* We saved the entire capture, not just some packets from it.
Open and read the file we saved it to.
/* Open and read the file we saved to.
XXX - this is somewhat of a waste; we already have the
packets, all this gets us is updated file type information
(which we could just stuff into "cf"), and having the new
file be the one we have opened and from which we're reading
the data, and it means we have to spend time opening and
reading the file, which could be a significant amount of
time if the file is large.
XXX - this is somewhat of a waste; we already have the
packets, all this gets us is updated file type information
(which we could just stuff into "cf"), and having the new
file be the one we have opened and from which we're reading
the data, and it means we have to spend time opening and
reading the file, which could be a significant amount of
time if the file is large.
If the capture-file-writing code were to return the
seek offset of each packet it writes, we could save that
in the frame_data structure for the frame, and just open
the file without reading it again. */
cf->unsaved_changes = FALSE;
If the capture-file-writing code were to return the
seek offset of each packet it writes, we could save that
in the frame_data structure for the frame, and just open
the file without reading it again. */
cf->unsaved_changes = FALSE;
if ((cf_open(cf, fname, FALSE, &err)) == CF_OK) {
/* XXX - report errors if this fails?
What should we return if it fails or is aborted? */
if ((cf_open(cf, fname, FALSE, &err)) == CF_OK) {
/* XXX - report errors if this fails?
What should we return if it fails or is aborted? */
switch (cf_read(cf, TRUE)) {
switch (cf_read(cf, TRUE)) {
case CF_READ_OK:
case CF_READ_ERROR:
/* Just because we got an error, that doesn't mean we were unable
to read any of the file; we handle what we could get from the
file. */
break;
case CF_READ_OK:
case CF_READ_ERROR:
/* Just because we got an error, that doesn't mean we were unable
to read any of the file; we handle what we could get from the
file. */
break;
case CF_READ_ABORTED:
/* The user bailed out of re-reading the capture file; the
capture file has been closed - just return (without
changing any menu settings; "cf_close()" set them
correctly for the "no capture file open" state). */
break;
}
case CF_READ_ABORTED:
/* The user bailed out of re-reading the capture file; the
capture file has been closed - just return (without
changing any menu settings; "cf_close()" set them
correctly for the "no capture file open" state). */
break;
}
}
return CF_OK;
@ -4056,18 +4041,104 @@ fail:
cf_status_t
cf_save(capture_file *cf, const char *fname, guint save_format, gboolean compressed)
{
packet_range_t range;
/* This only does a "save all", so we have our own packet_range_t
structure, which is set to the default "save everything" state. */
packet_range_init(&range);
return cf_save_packets(cf, fname, &range, save_format, compressed, TRUE);
return cf_save_packets(cf, fname, save_format, compressed, TRUE);
}
cf_status_t
cf_save_as(capture_file *cf, const char *fname, packet_range_t *range, guint save_format, gboolean compressed)
cf_save_as(capture_file *cf, const char *fname, guint save_format, gboolean compressed)
{
return cf_save_packets(cf, fname, range, save_format, compressed, FALSE);
return cf_save_packets(cf, fname, save_format, compressed, FALSE);
}
cf_status_t
cf_export_specified_packets(capture_file *cf, const char *fname,
packet_range_t *range, guint save_format,
gboolean compressed)
{
int err;
wtap_dumper *pdh;
save_callback_args_t callback_args;
wtapng_section_t *shb_hdr = NULL;
wtapng_iface_descriptions_t *idb_inf = NULL;
cf_callback_invoke(cf_cb_file_save_started, (gpointer)fname);
if (file_exists(fname)) {
/* don't write over an existing file. */
/* this should've been already checked by our caller, just to be sure... */
if (file_exists(fname)) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"%sCapture file: \"%s\" already exists!%s\n\n"
"Please choose a different filename.",
simple_dialog_primary_start(),
fname,
simple_dialog_primary_end());
goto fail;
}
}
packet_range_process_init(range);
/* We're writing out specified packets from the specified capture
file to another file. Even if all captured packets are to be
written, don't special-case the operation - read each packet
and then write it out if it's one of the specified ones. */
shb_hdr = wtap_file_get_shb_info(cf->wth);
idb_inf = wtap_file_get_idb_info(cf->wth);
pdh = wtap_dump_open_ng(fname, save_format, cf->lnk_t, cf->snap,
compressed, shb_hdr, idb_inf, &err);
g_free(idb_inf);
idb_inf = NULL;
if (pdh == NULL) {
cf_open_failure_alert_box(fname, err, NULL, TRUE, save_format);
goto fail;
}
/* Add address resolution */
wtap_dump_set_addrinfo_list(pdh, get_addrinfo_list());
/* Iterate through the list of packets, processing the packets we were
told to process.
XXX - we've already called "packet_range_process_init(range)", but
"process_specified_packets()" will do it again. Fortunately,
that's harmless in this case, as we haven't done anything to
"range" since we initialized it. */
callback_args.pdh = pdh;
callback_args.fname = fname;
callback_args.file_type = save_format;
switch (process_specified_packets(cf, range, "Writing", "specified packets",
TRUE, save_packet, &callback_args)) {
case PSP_FINISHED:
/* Completed successfully. */
break;
case PSP_STOPPED:
/* The user decided to abort the writing.
XXX - remove the output file? */
break;
case PSP_FAILED:
/* Error while writing. */
wtap_dump_close(pdh, &err);
goto fail;
}
if (!wtap_dump_close(pdh, &err)) {
cf_close_failure_alert_box(fname, err);
goto fail;
}
cf_callback_invoke(cf_cb_file_save_finished, NULL);
return CF_OK;
fail:
cf_callback_invoke(cf_cb_file_save_failed, NULL);
return CF_ERROR;
}
static void

24
file.h
View File

@ -208,17 +208,33 @@ gboolean cf_can_save_as(capture_file *cf);
cf_status_t cf_save(capture_file * cf, const char *fname, guint save_format, gboolean compressed);
/**
* Save a capture file (or a range of it). Fails if the specified
* pathname already exists.
* Save all packets in a capture file to a new file, and, if that succeeds,
* make that file the current capture file. Fails if the specified
* target file already exists.
*
* @param cf the capture file to save to
* @param fname the filename to save to
* @param range the range of packets to save
* @param save_format the format of the file to save (libpcap, ...)
* @param compressed whether to gzip compress the file
* @return one of cf_status_t
*/
cf_status_t cf_save_as(capture_file * cf, const char *fname, packet_range_t *range, guint save_format, gboolean compressed);
cf_status_t cf_save_as(capture_file * cf, const char *fname, guint save_format, gboolean compressed);
/**
* Export some or all packets from a capture file to a new file. Fails if
* the specified target file already exists.
*
* @param cf the capture file to write to
* @param fname the filename to write to
* @param range the range of packets to write
* @param save_format the format of the file to write (libpcap, ...)
* @param compressed whether to gzip compress the file
* @return one of cf_status_t
*/
cf_status_t cf_export_specified_packets(capture_file *cf, const char *fname,
packet_range_t *range,
guint save_format,
gboolean compressed);
/**
* Get a displayable name of the capture file.

View File

@ -4201,7 +4201,7 @@ capture_start_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
switch(btn) {
case(ESD_BTN_SAVE):
/* save file first */
file_save_as_cmd(after_save_capture_dialog, data, FALSE);
file_save_as_cmd(after_save_capture_dialog, data);
break;
case(ESD_BTN_DONT_SAVE):
/* XXX - unlink old file? */

View File

@ -85,6 +85,9 @@ static void file_merge_destroy_cb(GtkWidget *win, gpointer user_data);
static void file_save_as_select_file_type_cb(GtkWidget *w, gpointer data);
static void file_save_as_ok_cb(GtkWidget *w, gpointer fs);
static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
static void file_export_specified_packets_cb(GtkWidget *w, gpointer fs);
static void file_export_specified_packets_ok_cb(GtkWidget *w, gpointer fs);
static void file_export_specified_packets_destroy_cb(GtkWidget *win, gpointer user_data);
static void file_color_import_ok_cb(GtkWidget *w, gpointer filter_list);
static void file_color_import_destroy_cb(GtkWidget *win, gpointer user_data);
static void file_color_export_ok_cb(GtkWidget *w, gpointer filter_list);
@ -120,6 +123,14 @@ static void set_file_type_list(GtkWidget *combo_box, int default_file_type);
*/
static GtkWidget *file_save_as_w;
/*
* Keep a static pointer to the current "Export Specified Packets" window, if
* any, so that if somebody tries to do "File:Export Specified Packets"
* while there's already a "Export Specified Packets" window up, we just pop
* up the existing one, rather than creating a new one.
*/
static GtkWidget *file_export_specified_packets_w;
/* XXX - can we make these not be static? */
static packet_range_t range;
static gboolean color_selected;
@ -587,7 +598,7 @@ static void file_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
switch(btn) {
case(ESD_BTN_SAVE):
/* save file first */
file_save_as_cmd(after_save_open_dialog, data, FALSE);
file_save_as_cmd(after_save_open_dialog, data);
break;
case(ESD_BTN_DONT_SAVE):
cf_close(&cfile);
@ -897,7 +908,7 @@ static void file_merge_answered_cb(gpointer dialog _U_, gint btn, gpointer data
switch(btn) {
case(ESD_BTN_OK):
/* save file first */
file_save_as_cmd(after_save_merge_dialog, data, FALSE);
file_save_as_cmd(after_save_merge_dialog, data);
break;
case(ESD_BTN_CANCEL):
break;
@ -1065,7 +1076,7 @@ static void file_close_answered_cb(gpointer dialog _U_, gint btn, gpointer data
switch(btn) {
case(ESD_BTN_SAVE):
/* save file first */
file_save_as_cmd(after_save_close_file, NULL, FALSE);
file_save_as_cmd(after_save_close_file, NULL);
break;
case(ESD_BTN_DONT_SAVE):
cf_close(&cfile);
@ -1103,7 +1114,7 @@ file_save_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
if (cfile.is_tempfile) {
/* This is a temporary capture file, so saving it means saving
it to a permanent file. */
file_save_as_cmd(after_save_no_action, NULL, FALSE);
file_save_as_cmd(after_save_no_action, NULL);
} else {
if (cfile.unsaved_changes) {
/* This is not a temporary capture file, but it has unsaved
@ -1159,21 +1170,6 @@ file_save_as_select_file_type_cb(GtkWidget *w, gpointer data _U_)
gtk_widget_set_sensitive(compressed_cb, wtap_dump_can_compress(new_file_type));
}
/*
* Update various dynamic parts of the range controls; called from outside
* the file dialog code whenever the packet counts change.
*/
void
file_save_update_dynamics(void)
{
if (file_save_as_w == NULL) {
/* We don't currently have a "Save As..." dialog box up. */
return;
}
range_update_dynamics(range_tb);
}
action_after_save_e action_after_save_g;
gpointer action_after_save_data_g;
@ -1197,12 +1193,12 @@ file_save_cmd(action_after_save_e action_after_save, gpointer action_after_save_
}
void
file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data, gboolean save_only_displayed)
file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data)
{
#if _WIN32
win32_save_as_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), action_after_save, action_after_save_data);
#else /* _WIN32 */
GtkWidget *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *range_fr, *compressed_cb;
GtkWidget *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *compressed_cb;
if (file_save_as_w != NULL) {
/* There's already an "Save Capture File As" dialog box; reactivate it. */
@ -1210,12 +1206,7 @@ file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_sa
return;
}
/* Default to saving all packets, in the file's current format. */
/* init the packet range */
packet_range_init(&range);
range.process_filtered = save_only_displayed;
range.include_dependents = TRUE;
/* Default to saving in the file's current format. */
/* build the file selection */
file_save_as_w = file_selection_new ("Wireshark: Save Capture File As",
@ -1233,17 +1224,8 @@ file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_sa
file_selection_set_extra_widget(file_save_as_w, main_vb);
gtk_widget_show(main_vb);
/*** Packet Range frame ***/
range_fr = gtk_frame_new("Packet Range");
gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
gtk_widget_show(range_fr);
/* range table */
range_tb = range_new(&range, TRUE);
gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
gtk_widget_show(range_tb);
/* File type row */
range_tb = NULL;
ft_hb = gtk_hbox_new(FALSE, 3);
gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
gtk_widget_show(ft_hb);
@ -1260,9 +1242,6 @@ file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_sa
gtk_widget_show(ft_combo_box);
g_object_set_data(G_OBJECT(file_save_as_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
/* dynamic values in the range frame */
range_update_dynamics(range_tb);
/* compressed */
compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
gtk_container_add(GTK_CONTAINER(ft_hb), compressed_cb);
@ -1292,10 +1271,9 @@ file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_sa
void
file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
{
file_save_as_cmd(after_save_no_action, NULL, TRUE);
file_save_as_cmd(after_save_no_action, NULL);
}
/* all tests ok, we only have to save the file */
/* (and probably continue with a pending operation) */
static void
@ -1321,9 +1299,8 @@ file_save_as_cb(GtkWidget *w _U_, gpointer fs) {
/* ask in a dialog if that's intended */
/* currently, cf_save_as() will simply deny it */
/* Write out the packets (all, or only the ones from the current
range) to the file with the specified name. */
if (cf_save_as(&cfile, cf_name, &range, file_type,
/* Write out all the packets to the file with the specified name. */
if (cf_save_as(&cfile, cf_name, file_type,
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb))) != CF_OK) {
/* The write failed; don't dismiss the open dialog box,
just leave it around so that the user can, after they
@ -1408,7 +1385,7 @@ static void file_save_as_exists_answered_cb(gpointer dialog _U_, gint btn, gpoin
}
/* user pressed "Save" dialog "Ok" button */
/* user pressed "Save As" dialog "Ok" button */
static void
file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
gchar *cf_name;
@ -1427,21 +1404,6 @@ file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
return;
}
/* Check whether the range is valid. */
if (!range_check_validity(&range)) {
/* The range isn't valid; 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. */
g_free(cf_name);
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_save_as_w)
window_destroy(GTK_WIDGET (fs));
return;
}
/*
* Check that the from file is not the same as to file
* We do it here so we catch all cases ...
@ -1474,10 +1436,13 @@ file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
return;
}
/* the file exists, ask the user to remove it first */
/* The file exists. Ask the user if they want to overwrite it. */
dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
"%sA file named \"%s\" already exists.%s\n\n"
"Do you want to replace it with the capture you are saving?",
"%s"
"A file named \"%s\" already exists. Do you want to replace it?"
"%s\n\n"
"A file or folder with the same name already exists in that folder. "
"Replacing it will overwrite its current contents.",
simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
simple_dialog_set_cb(dialog, file_save_as_exists_answered_cb, fs);
@ -1498,6 +1463,281 @@ file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
file_save_as_w = NULL;
}
/*
* Update various dynamic parts of the range controls; called from outside
* the file dialog code whenever the packet counts change.
*/
void
file_export_specified_packets_update_dynamics(void)
{
if (file_export_specified_packets_w == NULL) {
/* We don't currently have a "Export Specified Packets..." dialog box up. */
return;
}
range_update_dynamics(range_tb);
}
void
file_export_specified_packets_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
{
#if _WIN32
win32_save_as_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), action_after_save, action_after_save_data);
#else /* _WIN32 */
GtkWidget *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *range_fr, *compressed_cb;
if (file_export_specified_packets_w != NULL) {
/* There's already an "Export Specified Packets" dialog box; reactivate it. */
reactivate_window(file_export_specified_packets_w);
return;
}
/* Default to writing out all displayed packets, in the file's current format. */
/* init the packet range */
packet_range_init(&range);
range.process_filtered = TRUE;
range.include_dependents = TRUE;
/* build the file selection */
file_export_specified_packets_w = file_selection_new ("Wireshark: Export Specified Packets",
FILE_SELECTION_SAVE);
/* Container for each row of widgets */
main_vb = gtk_vbox_new(FALSE, 5);
gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
file_selection_set_extra_widget(file_export_specified_packets_w, main_vb);
gtk_widget_show(main_vb);
/*** Packet Range frame ***/
range_fr = gtk_frame_new("Packet Range");
gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
gtk_widget_show(range_fr);
/* range table */
range_tb = range_new(&range, TRUE);
gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
gtk_widget_show(range_tb);
/* File type row */
ft_hb = gtk_hbox_new(FALSE, 3);
gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
gtk_widget_show(ft_hb);
ft_lb = gtk_label_new("File type:");
gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
gtk_widget_show(ft_lb);
ft_combo_box = ws_combo_box_new_text_and_pointer();
/* Generate the list of file types we can save. */
set_file_type_list(ft_combo_box, cfile.cd_t);
gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
gtk_widget_show(ft_combo_box);
g_object_set_data(G_OBJECT(file_export_specified_packets_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
/* dynamic values in the range frame */
range_update_dynamics(range_tb);
/* compressed */
compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
gtk_container_add(GTK_CONTAINER(ft_hb), compressed_cb);
/* XXX - disable output compression for now, as this doesn't work with the
* current optimization to simply copy a capture file if it's using the same
* encapsulation ... */
/* the rest of the implementation is just working fine :-( */
#if 0
gtk_widget_show(compressed_cb);
#endif
g_object_set_data(G_OBJECT(file_export_specified_packets_w), E_COMPRESSED_CB_KEY, compressed_cb);
/* Ok: now "select" the default filetype which invokes file_save_as_select_file_type_cb */
g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_save_as_select_file_type_cb), NULL);
ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0);
g_signal_connect(file_export_specified_packets_w, "destroy",
G_CALLBACK(file_export_specified_packets_destroy_cb), NULL);
if (gtk_dialog_run(GTK_DIALOG(file_export_specified_packets_w)) == GTK_RESPONSE_ACCEPT) {
file_export_specified_packets_ok_cb(file_export_specified_packets_w, file_export_specified_packets_w);
} else {
window_destroy(file_export_specified_packets_w);
}
#endif /* _WIN32 */
}
static void
file_export_specified_packets_exists_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
{
gchar *cf_name;
cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data));
switch(btn) {
case(ESD_BTN_OK):
/* save file */
ws_unlink(cf_name);
file_export_specified_packets_cb(NULL, data);
break;
case(ESD_BTN_CANCEL):
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_export_specified_packets_w)
window_destroy(file_export_specified_packets_w);
break;
default:
g_assert_not_reached();
}
g_free(cf_name);
}
/* user pressed "Export Specified Packets" dialog "Ok" button */
static void
file_export_specified_packets_ok_cb(GtkWidget *w _U_, gpointer fs) {
gchar *cf_name;
gpointer dialog;
cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
/* Perhaps the user specified a directory instead of a file.
Check whether they did. */
if (test_for_directory(cf_name) == EISDIR) {
/* It's a directory - set the file selection box to display that
directory, and leave the selection box displayed. */
set_last_open_dir(cf_name);
g_free(cf_name);
file_selection_set_current_folder(fs, get_last_open_dir());
return;
}
/* Check whether the range is valid. */
if (!range_check_validity(&range)) {
/* The range isn't valid; 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. */
g_free(cf_name);
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_save_as_w)
window_destroy(GTK_WIDGET (fs));
return;
}
/*
* Check that the from file is not the same as to file
* We do it here so we catch all cases ...
* Unfortunately, the file requester gives us an absolute file
* name and the read file name may be relative (if supplied on
* the command line). From Joerg Mayer.
*/
if (files_identical(cfile.filename, cf_name)) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"%sCapture file: \"%s\" identical to loaded file!%s\n\n"
"Please choose a different filename.",
simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
g_free(cf_name);
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_export_specified_packets_w)
window_destroy(GTK_WIDGET (fs));
return;
}
/* don't show the dialog while saving (or asking) */
gtk_widget_hide(GTK_WIDGET (fs));
/* it the file doesn't exist, simply try to write the packets to it */
if (!file_exists(cf_name)) {
file_export_specified_packets_cb(NULL, fs);
g_free(cf_name);
return;
}
/* The file exists. Ask the user if they want to overwrite it. */
dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
"%s"
"A file named \"%s\" already exists. Do you want to replace it?"
"%s\n\n"
"A file or folder with the same name already exists in that folder. "
"Replacing it will overwrite its current contents.",
simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
simple_dialog_set_cb(dialog, file_export_specified_packets_exists_answered_cb, fs);
g_free(cf_name);
}
/* all tests ok, we only have to write out the packets */
/* (and probably continue with a pending operation) */
static void
file_export_specified_packets_cb(GtkWidget *w _U_, gpointer fs) {
GtkWidget *ft_combo_box;
GtkWidget *compressed_cb;
gchar *cf_name;
gchar *dirname;
gpointer ptr;
int file_type;
cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_COMPRESSED_CB_KEY);
ft_combo_box = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_FILE_TYPE_COMBO_BOX_KEY);
if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
g_assert_not_reached(); /* Programming error: somehow nothing is active */
}
file_type = GPOINTER_TO_INT(ptr);
/* XXX - if the user requests to save to an already existing filename, */
/* ask in a dialog if that's intended */
/* currently, cf_export_specified_packets() will simply deny it */
/* Write out the specified packets to the file with the specified name. */
if (cf_export_specified_packets(&cfile, cf_name, &range, file_type,
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb))) != CF_OK) {
/* 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. */
g_free(cf_name);
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_export_specified_packets_w)
window_destroy(GTK_WIDGET (fs));
return;
}
/* The write succeeded; get rid of the file selection box. */
/* cf_export_specified_packets() might already closed our dialog! */
if (file_export_specified_packets_w)
window_destroy(GTK_WIDGET (fs));
/* Save the directory name for future file dialogs.
XXX - should there be separate ones for "Save As" and
"Export Specified Packets"? */
dirname = get_dirname(cf_name); /* Overwrites cf_name */
set_last_open_dir(dirname);
g_free(cf_name);
}
void
file_export_specified_packets_destroy(void)
{
if (file_export_specified_packets_w)
window_destroy(file_export_specified_packets_w);
}
static void
file_export_specified_packets_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
{
/* Note that we no longer have a "Export Specified Packets" dialog box. */
file_export_specified_packets_w = NULL;
}
/* Reload a file using the current read and display filters */
void
file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {

View File

@ -53,14 +53,17 @@ void file_save_cmd(action_after_save_e action_after_save, gpointer action_after_
*
* @param action_after_save the action to take, when save completed
* @param action_after_save_data data for action_after_save
* @param save_only_displayed Save only the displayed packets
*/
void file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data, gboolean save_only_displayed);
void file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data);
/** Destroy the save as dialog.
/** Destroy the "Save As" dialog.
*/
void file_save_as_destroy(void);
/** Destroy the "Export Specified Packets" dialog.
*/
void file_export_specified_packets_destroy(void);
/** User requested the "Open" dialog box.
*
* @param widget parent widget
@ -96,6 +99,21 @@ void file_save_as_cmd_cb(GtkWidget *widget, gpointer data);
*/
void file_close_cmd_cb(GtkWidget *widget, gpointer data);
/** User requested the "Export Specified Packets" dialog box.
*
* @param widget parent widget
* @param data unused
*/
void file_export_specified_packets_cmd_cb(GtkWidget *widget, gpointer data);
/*
* Set the "Save only marked packets" toggle button as appropriate for
* the current output file type and count of marked packets.
* Called when the "Export Specified Packets..." dialog box is created
* and when either the file type or the marked count changes.
*/
void file_export_specified_packets_update_dynamics(void);
/** User requested "Reload".
*
* @param widget parent widget
@ -117,12 +135,4 @@ void file_color_import_cmd_cb(GtkWidget *widget, gpointer data);
*/
void file_color_export_cmd_cb(GtkWidget *widget, gpointer data);
/*
* Set the "Save only marked packets" toggle button as appropriate for
* the current output file type and count of marked packets.
* Called when the "Save As..." dialog box is created and when either
* the file type or the marked count changes.
*/
void file_save_update_dynamics(void);
#endif /* capture_file_dlg.h */

View File

@ -294,7 +294,7 @@ dnd_save_file_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
switch(btn) {
case(ESD_BTN_SAVE):
/* save file first */
file_save_as_cmd(after_save_open_dnd_file, data, FALSE);
file_save_as_cmd(after_save_open_dnd_file, data);
break;
case(ESD_BTN_DONT_SAVE):
cf_close(&cfile);

View File

@ -779,7 +779,7 @@ file_import_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
switch (btn) {
case ESD_BTN_SAVE:
/* save file first */
file_save_as_cmd(after_save_no_action, NULL, FALSE);
file_save_as_cmd(after_save_no_action, NULL);
break;
case ESD_BTN_DONT_SAVE:
cf_close(&cfile);

View File

@ -1102,7 +1102,7 @@ static void file_quit_answered_cb(gpointer dialog _U_, gint btn, gpointer data _
switch(btn) {
case(ESD_BTN_SAVE):
/* save file first */
file_save_as_cmd(after_save_exit, NULL, FALSE);
file_save_as_cmd(after_save_exit, NULL);
break;
case(ESD_BTN_QUIT_DONT_SAVE):
main_do_quit();
@ -1419,10 +1419,11 @@ main_cf_cb_file_closing(capture_file *cf)
gtk_window_set_position(GTK_WINDOW(close_dlg), GTK_WIN_POS_CENTER_ON_PARENT);
}
/* Destroy all windows, which refer to the
/* Destroy all windows that refer to the
capture file we're closing. */
destroy_packet_wins();
file_save_as_destroy();
file_export_specified_packets_destroy();
/* Restore the standard title bar message. */
set_main_window_name("The Wireshark Network Analyzer");

View File

@ -1003,6 +1003,7 @@ static const char *ui_desc_menubar =
" <menuitem name='PreviousFile' action='/File/Set/PreviousFile'/>\n"
" </menu>\n"
" <separator/>\n"
" <menuitem name='ExportSpecifiedPackets' action='/File/ExportSpecifiedPackets'/>\n"
" <menu name= 'ExportPacketDissections' action='/File/ExportPacketDissections'>\n"
" <menuitem name='AsTxt' action='/File/ExportPacketDissections/Text'/>\n"
" <menuitem name='AsPostScript' action='/File/ExportPacketDissections/PostScript'/>\n"
@ -1445,6 +1446,7 @@ static const GtkActionEntry main_menu_bar_entries[] = {
{ "/File/SaveAs", GTK_STOCK_SAVE_AS, "Save _As...", "<shift><control>S", NULL, G_CALLBACK(file_save_as_cmd_cb) },
{ "/File/Set", NULL, "File Set", NULL, NULL, NULL },
{ "/File/ExportSpecifiedPackets", NULL, "Export Specified Packets...", NULL, NULL, G_CALLBACK(file_export_specified_packets_cmd_cb) },
{ "/File/ExportPacketDissections", NULL, "Export Packet Dissections", NULL, NULL, NULL },
{ "/File/ExportSelectedPacketBytes", NULL, "Export Selected Packet _Bytes...", "<control>H", NULL, G_CALLBACK(savehex_cb) },
{ "/File/ExportSSLSessionKeys", NULL, "Export SSL Session Keys...", NULL, NULL, G_CALLBACK(savesslkeys_cb) },
@ -4176,7 +4178,7 @@ static void menu_open_recent_file_answered_cb(gpointer dialog _U_, gint btn, gpo
switch(btn) {
case(ESD_BTN_YES):
/* save file first */
file_save_as_cmd(after_save_open_recent_file, data, FALSE);
file_save_as_cmd(after_save_open_recent_file, data);
break;
case(ESD_BTN_NO):
cf_close(&cfile);
@ -4635,6 +4637,7 @@ set_menus_for_capture_file(capture_file *cf)
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Close", FALSE);
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Save", FALSE);
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/SaveAs", FALSE);
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSpecifiedPackets", FALSE);
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportPacketDissections", FALSE);
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSelectedPacketBytes", FALSE);
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSSLSessionKeys", FALSE);
@ -4650,13 +4653,18 @@ set_menus_for_capture_file(capture_file *cf)
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Save",
(cf->is_tempfile || cf->unsaved_changes));
/*
* "Save As..." works only if we can write the file out in at least
* one format (so we can save the whole file or just a subset) or
* if the file is a temporary file (so writing the whole file out
* with a raw data copy makes sense).
* "Save As..." should be available only if we have no unsaved
* changes (so saving just involves copying the raw file) or if
* we can write the file out in at least one format.
*/
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/SaveAs",
cf_can_save_as(cf) || cf->is_tempfile);
(!cf->unsaved_changes || cf_can_save_as(cf)));
/*
* "Export Specified Packets..." should be available only if
* we can write the file out in at least one format.
*/
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSpecifiedPackets",
cf_can_save_as(cf));
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportPacketDissections", TRUE);
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSelectedPacketBytes", TRUE);
set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSSLSessionKeys", TRUE);

View File

@ -1450,7 +1450,7 @@ new_packet_list_set_font(PangoFontDescription *font)
static void
mark_frames_ready(void)
{
file_save_update_dynamics();
file_export_specified_packets_update_dynamics();
packets_bar_update();
new_packet_list_queue_draw();
}