2 * Dialog boxes for handling capture files
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35 #include <epan/filesystem.h>
36 #include <epan/addr_resolv.h>
37 #include <epan/prefs.h>
41 #include "color_filters.h"
43 #include <wsutil/file_util.h>
45 #include <wiretap/merge.h>
48 #include "ui/alert_box.h"
49 #include "ui/file_dialog.h"
50 #include "ui/recent.h"
51 #include "ui/ui_util.h"
53 #include "ui/gtk/gtkglobals.h"
54 #include "ui/gtk/keys.h"
55 #include "ui/gtk/filter_dlg.h"
56 #include "ui/gtk/gui_utils.h"
57 #include "ui/gtk/dlg_utils.h"
58 #include "ui/gtk/file_dlg.h"
59 #include "ui/gtk/capture_file_dlg.h"
60 #include "ui/gtk/drag_and_drop.h"
61 #include "ui/gtk/main.h"
62 #include "ui/gtk/color_dlg.h"
63 #include "ui/gtk/packet_list.h"
65 #include "ui/gtk/capture_dlg.h"
67 #include "ui/gtk/stock_icons.h"
68 #include "ui/gtk/range_utils.h"
69 #include "ui/gtk/filter_autocomplete.h"
72 #define USE_WIN32_FILE_DIALOGS
75 #ifdef USE_WIN32_FILE_DIALOGS
76 #include <gdk/gdkwin32.h>
78 #include "ui/win32/file_dlg_win32.h"
81 static void do_file_save(capture_file *cf, gboolean dont_reopen);
82 static void file_save_as_cmd(capture_file *cf,
83 gboolean must_support_all_comments,
84 gboolean dont_reopen);
85 static void file_select_file_type_cb(GtkWidget *w, gpointer data);
86 static int set_file_type_list(GtkWidget *combo_box, capture_file *cf,
87 gboolean must_support_all_comments);
88 static gboolean test_file_close(capture_file *cf, gboolean from_quit,
89 const char *before_what);
91 #define E_FILE_TYPE_COMBO_BOX_KEY "file_type_combo_box"
92 #define E_COMPRESSED_CB_KEY "compressed_cb"
94 #define PREVIEW_TABLE_KEY "preview_table_key"
95 #define PREVIEW_FORMAT_KEY "preview_format_key"
96 #define PREVIEW_SIZE_KEY "preview_size_key"
97 #define PREVIEW_ELAPSED_KEY "preview_elapsed_key"
98 #define PREVIEW_PACKETS_KEY "preview_packets_key"
99 #define PREVIEW_FIRST_KEY "preview_first_key"
101 /* XXX - can we make these not be static? */
102 static gboolean color_selected;
104 #define PREVIEW_STR_MAX 200
107 /* set a new filename for the preview widget */
109 preview_set_filename(GtkWidget *prev, const gchar *cf_name)
115 gchar string_buff[PREVIEW_STR_MAX];
119 /* init preview labels */
120 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
121 gtk_label_set_text(GTK_LABEL(label), "-");
122 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
123 gtk_label_set_text(GTK_LABEL(label), "-");
124 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
125 gtk_label_set_text(GTK_LABEL(label), "-");
126 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
127 gtk_label_set_text(GTK_LABEL(label), "-");
128 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
129 gtk_label_set_text(GTK_LABEL(label), "-");
135 if (test_for_directory(cf_name) == EISDIR) {
136 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
137 gtk_label_set_text(GTK_LABEL(label), "directory");
141 wth = wtap_open_offline(cf_name, &err, &err_info, TRUE);
143 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
144 if (err == WTAP_ERR_FILE_UNKNOWN_FORMAT) {
145 gtk_label_set_text(GTK_LABEL(label), "unknown file format");
147 gtk_label_set_text(GTK_LABEL(label), "error opening file");
152 /* Find the size of the file. */
153 filesize = wtap_file_size(wth, &err);
154 if (filesize == -1) {
155 gtk_label_set_text(GTK_LABEL(label), "error getting file size");
159 g_snprintf(string_buff, PREVIEW_STR_MAX, "%" G_GINT64_MODIFIER "d bytes", filesize);
160 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
161 gtk_label_set_text(GTK_LABEL(label), string_buff);
164 g_strlcpy(string_buff, wtap_file_type_string(wtap_file_type(wth)), PREVIEW_STR_MAX);
165 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
166 gtk_label_set_text(GTK_LABEL(label), string_buff);
172 /* do a preview run on the currently selected capture file */
174 preview_do(GtkWidget *prev, wtap *wth)
177 unsigned int elapsed_time;
183 double start_time = 0; /* seconds, with nsec resolution */
184 double stop_time = 0; /* seconds, with nsec resolution */
186 unsigned int packets = 0;
187 gboolean is_breaked = FALSE;
188 gchar string_buff[PREVIEW_STR_MAX];
191 const struct wtap_pkthdr *phdr;
195 while ( (wtap_read(wth, &err, &err_info, &data_offset)) ) {
196 phdr = wtap_phdr(wth);
197 cur_time = wtap_nstime_to_sec(&phdr->ts);
199 start_time = cur_time;
200 stop_time = cur_time;
202 if (cur_time < start_time) {
203 start_time = cur_time;
205 if (cur_time > stop_time) {
206 stop_time = cur_time;
210 if (packets%1000 == 0) {
211 /* do we have a timeout? */
213 if (time_current-time_preview >= (time_t) prefs.gui_fileopen_preview) {
221 g_snprintf(string_buff, PREVIEW_STR_MAX, "error after reading %u packets", packets);
222 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
223 gtk_label_set_text(GTK_LABEL(label), string_buff);
230 g_snprintf(string_buff, PREVIEW_STR_MAX, "more than %u packets (preview timeout)", packets);
232 g_snprintf(string_buff, PREVIEW_STR_MAX, "%u", packets);
234 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
235 gtk_label_set_text(GTK_LABEL(label), string_buff);
238 ti_time = (long)start_time;
239 ti_tm = localtime( &ti_time );
241 g_snprintf(string_buff, PREVIEW_STR_MAX,
242 "%04d-%02d-%02d %02d:%02d:%02d",
243 ti_tm->tm_year + 1900,
250 g_snprintf(string_buff, PREVIEW_STR_MAX, "?");
252 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
253 gtk_label_set_text(GTK_LABEL(label), string_buff);
256 elapsed_time = (unsigned int)(stop_time-start_time);
257 if (elapsed_time/86400) {
258 g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u days %02u:%02u:%02u",
259 elapsed_time/86400, elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
261 g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u:%02u:%02u",
262 elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
265 g_snprintf(string_buff, PREVIEW_STR_MAX, "unknown");
267 label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
268 gtk_label_set_text(GTK_LABEL(label), string_buff);
274 /* as the dialog layout will look very ugly when using the file chooser preview mechanism,
275 simply use the same layout as in GTK2.0 */
277 update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
279 GtkWidget *prev = GTK_WIDGET(data);
281 gboolean have_preview;
283 cf_name = gtk_file_chooser_get_preview_filename(file_chooser);
285 have_preview = preview_set_filename(prev, cf_name);
290 gtk_file_chooser_set_preview_widget_active(file_chooser, have_preview);
295 /* the filename text entry changed */
297 file_open_entry_changed(GtkWidget *w _U_, gpointer file_sel)
299 GtkWidget *prev = (GtkWidget *)g_object_get_data(G_OBJECT(file_sel), PREVIEW_TABLE_KEY);
301 gboolean have_preview;
304 /* get the filename */
305 cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_sel));
307 /* set the filename to the preview */
308 wth = preview_set_filename(prev, cf_name);
309 have_preview = (wth != NULL);
313 /* make the preview widget sensitive */
314 gtk_widget_set_sensitive(prev, have_preview);
317 * XXX - if the Open button isn't sensitive, you can't type into
318 * the location bar and select the file or directory you've typed.
321 * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1791
323 * It's not as if allowing users to click Open when they've
324 * selected a file that's not a valid capture file will cause
325 * anything worse than an error dialog, so we'll leave the Open
326 * button sensitive for now. Perhaps making it sensitive if
327 * cf_name is NULL would also work, although I don't know whether
328 * there are any cases where it would be non-null when you've
329 * typed in the location bar.
331 * XXX - Bug 1791 also notes that, with the line removed, Bill
332 * Meier "somehow managed to get the file chooser window somewhat
333 * wedged in that neither the cancel or open buttons were responsive".
334 * That seems a bit odd, given that, without this line, we're not
335 * monkeying with the Open button's sensitivity, but...
338 /* make the open/save/... dialog button sensitive */
340 gtk_dialog_set_response_sensitive(file_sel, GTK_RESPONSE_ACCEPT, have_preview);
343 /* do the actual preview */
345 preview_do(prev, wth);
349 /* copied from summary_dlg.c */
351 add_string_to_grid_sensitive(GtkWidget *grid, guint *row, const gchar *title, const gchar *value, gboolean sensitive)
356 if (strlen(value) != 0) {
357 indent = g_strdup_printf(" %s", title);
359 indent = g_strdup(title);
361 label = gtk_label_new(indent);
363 gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
364 gtk_widget_set_sensitive(label, sensitive);
365 ws_gtk_grid_attach_defaults(GTK_GRID(grid), label, 0, *row, 1, 1);
367 label = gtk_label_new(value);
368 gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
369 gtk_widget_set_sensitive(label, sensitive);
370 ws_gtk_grid_attach_defaults(GTK_GRID(grid), label, 1, *row, 1, 1);
378 add_string_to_grid(GtkWidget *grid, guint *row, const gchar *title, const gchar *value)
380 return add_string_to_grid_sensitive(grid, row, title, value, TRUE);
388 GtkWidget *grid, *label;
391 grid = ws_gtk_grid_new();
392 ws_gtk_grid_set_column_spacing(GTK_GRID(grid), 6);
393 ws_gtk_grid_set_row_spacing(GTK_GRID(grid), 3);
396 label = add_string_to_grid(grid, &row, "Format:", "-");
397 g_object_set_data(G_OBJECT(grid), PREVIEW_FORMAT_KEY, label);
398 label = add_string_to_grid(grid, &row, "Size:", "-");
399 g_object_set_data(G_OBJECT(grid), PREVIEW_SIZE_KEY, label);
400 label = add_string_to_grid(grid, &row, "Packets:", "-");
401 g_object_set_data(G_OBJECT(grid), PREVIEW_PACKETS_KEY, label);
402 label = add_string_to_grid(grid, &row, "First Packet:", "-");
403 g_object_set_data(G_OBJECT(grid), PREVIEW_FIRST_KEY, label);
404 label = add_string_to_grid(grid, &row, "Elapsed time:", "-");
405 g_object_set_data(G_OBJECT(grid), PREVIEW_ELAPSED_KEY, label);
410 #ifndef USE_WIN32_FILE_DIALOGS
413 ------------------------------------------------
415 | ------------------------------------------- |
416 | |file_chooser_dialog [vbox] | |
417 | | -------------------------------------- | |
418 | | |[file_browser] T/T | | |
422 | | -------------------------------------- | |
424 | | -------------------------------------- | |
425 | | |[alignment] F/F | | |
426 | | | -------------------------------- | | |
427 | | | |main_hb [extra_widget] | | | |
428 | | | | ------------- ---------- | | | |
429 | | | | |main_vb F/F| |grid F/F| | | | |
430 | | | | | item | | row | | | | |
431 | | | | | item | | row | | | | |
432 | | | | | item | | row | | | | |
433 | | | | | item | | row | | | | |
434 | | | | | item | | row | | | | |
435 | | | | ------------- ---------- | | | |
436 | | | -------------------------------- | | |
437 | | -------------------------------------- | |
439 | | -------------------------------------- | |
440 | | |btn_row F/F | | |
441 | | -------------------------------------- | |
442 | ------------------------------------------- |
443 ------------------------------------------------
446 1. T/T & F/F above refer to the
447 gtk_box_pack_start() 'expand'/'fill' args
448 used to pack the widget into the enclosing GtkBox.
449 2. The 'Alignment' widget is actually internal to
450 the GtkFileChooser widget.
452 Gtk3: Expand/Fill effect
455 (file_chooser): expand/fills.
456 main_hb does not expand/fill [explicitly set via vexpand = FALSE].
457 btn_row does not expand/fill.
458 So: vertical resize (drog lower edge down).
459 expands (file_chooser) but leaves others as is.
460 Also: grid rows are "vexpand' so grid
461 vertically epands/fills in main_hb.
462 That is: it will be the same height as
464 Since vexpand is "inherited upwards", main_hb vexpand set to FALSE
465 to prevent vertical expansion of same.
468 (file_chooser) & btn_row: expand/fill.
469 main_vb & grid do not expand.
470 So: horizontal resize (drag right edge to the right)
471 expands all but the "extra widget".
476 gtk_open_file(GtkWidget *w, GString *file_name, GString *display_filter)
478 GtkWidget *file_open_w;
479 GtkWidget *main_hb, *main_vb, *filter_hbox, *filter_bt, *filter_te;
480 GtkWidget *m_resolv_cb, *n_resolv_cb, *t_resolv_cb, *e_resolv_cb, *prev;
482 /* No Apply button, and "OK" just sets our text widget, it doesn't
483 activate it (i.e., it doesn't cause us to try to open the file). */
484 static construct_args_t args = {
485 "Wireshark: Display Filter",
492 if (!file_name || !display_filter)
495 file_open_w = file_selection_new("Wireshark: Open Capture File",
496 GTK_WINDOW(top_level),
497 FILE_SELECTION_OPEN);
498 /* it's annoying, that the file chooser dialog is already shown here,
499 so we cannot use the correct gtk_window_set_default_size() to resize it */
500 gtk_widget_set_size_request(file_open_w, DEF_WIDTH, DEF_HEIGHT);
502 if (file_name->len > 0) {
503 gchar *dirname = g_path_get_dirname(file_name->str);
505 file_selection_set_current_folder(file_open_w, dirname);
508 switch (prefs.gui_fileopen_style) {
510 case FO_STYLE_LAST_OPENED:
511 /* The user has specified that we should start out in the last directory
512 we looked in. If we've already opened a file, use its containing
513 directory, if we could determine it, as the directory, otherwise
514 use the "last opened" directory saved in the preferences file if
516 /* This is now the default behaviour in file_selection_new() */
519 case FO_STYLE_SPECIFIED:
520 /* The user has specified that we should always start out in a
521 specified directory; if they've specified that directory,
522 start out by showing the files in that dir. */
523 if (prefs.gui_fileopen_dir[0] != '\0')
524 file_selection_set_current_folder(file_open_w, prefs.gui_fileopen_dir);
529 main_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
530 #if GTK_CHECK_VERSION(3,0,0)
531 gtk_widget_set_vexpand(main_hb, FALSE); /* prevents "inheritance" from child VEXPAND */
532 /* so hbox doesn't expand vertically even */
533 /* tho grid rows have VEXPAND. */
535 file_selection_set_extra_widget(file_open_w, main_hb);
536 gtk_widget_show(main_hb);
538 /* Container for each row of widgets */
539 main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
540 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
541 gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
542 gtk_widget_show(main_vb);
545 filter_hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1, FALSE);
546 gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
547 gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
548 gtk_widget_show(filter_hbox);
550 filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
551 g_signal_connect(filter_bt, "clicked",
552 G_CALLBACK(display_filter_construct_cb), &args);
553 g_signal_connect(filter_bt, "destroy",
554 G_CALLBACK(filter_button_destroy_cb), NULL);
555 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, FALSE, 0);
556 gtk_widget_show(filter_bt);
557 gtk_widget_set_tooltip_text(filter_bt, "Open the \"Display Filter\" dialog to edit/apply filters");
559 filter_te = gtk_entry_new();
560 g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
561 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
562 g_signal_connect(filter_te, "changed",
563 G_CALLBACK(filter_te_syntax_check_cb), NULL);
564 g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
565 g_signal_connect(filter_te, "key-press-event", G_CALLBACK(filter_string_te_key_pressed_cb), NULL);
566 g_signal_connect(file_open_w, "key-press-event", G_CALLBACK(filter_parent_dlg_key_pressed_cb), NULL);
567 colorize_filter_te_as_empty(filter_te);
568 gtk_entry_set_text(GTK_ENTRY(filter_te), display_filter->str);
569 gtk_widget_show(filter_te);
570 gtk_widget_set_tooltip_text(filter_te, "Enter a display filter.");
572 g_object_set_data(G_OBJECT(file_open_w), E_RFILTER_TE_KEY, filter_te);
574 /* Resolve buttons */
575 m_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _MAC name resolution");
576 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_resolv_cb),
577 gbl_resolv_flags.mac_name);
578 gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
579 gtk_widget_show(m_resolv_cb);
581 t_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _transport name resolution");
582 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(t_resolv_cb),
583 gbl_resolv_flags.transport_name);
584 gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
585 gtk_widget_show(t_resolv_cb);
587 n_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _network name resolution");
588 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(n_resolv_cb),
589 gbl_resolv_flags.network_name);
590 gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
591 gtk_widget_show(n_resolv_cb);
593 e_resolv_cb = gtk_check_button_new_with_mnemonic("Use _external network name resolver");
594 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(e_resolv_cb),
595 gbl_resolv_flags.use_external_net_name_resolver);
596 gtk_box_pack_start(GTK_BOX(main_vb), e_resolv_cb, FALSE, FALSE, 0);
597 gtk_widget_show(e_resolv_cb);
600 prev = preview_new();
601 g_object_set_data(G_OBJECT(file_open_w), PREVIEW_TABLE_KEY, prev);
602 gtk_widget_show_all(prev);
603 gtk_box_pack_start(GTK_BOX(main_hb), prev, FALSE, FALSE, 0);
605 g_signal_connect(GTK_FILE_CHOOSER(file_open_w), "selection-changed",
606 G_CALLBACK(file_open_entry_changed), file_open_w);
607 file_open_entry_changed(file_open_w, file_open_w);
609 g_object_set_data(G_OBJECT(file_open_w), E_DFILTER_TE_KEY,
610 g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
612 cf_name = file_selection_run(file_open_w);
614 if (cf_name == NULL) {
615 /* User cancelled or closed the dialog. */
619 g_string_printf(file_name, "%s", cf_name);
621 g_string_printf(display_filter, "%s", gtk_entry_get_text(GTK_ENTRY(filter_te)));
623 /* Set the global resolving variable */
624 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_resolv_cb)))
625 gbl_resolv_flags.mac_name = TRUE;
627 gbl_resolv_flags.mac_name = FALSE;
628 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(n_resolv_cb)))
629 gbl_resolv_flags.network_name = TRUE;
631 gbl_resolv_flags.network_name = FALSE;
632 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t_resolv_cb)))
633 gbl_resolv_flags.transport_name = TRUE;
635 gbl_resolv_flags.transport_name = FALSE;
636 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(e_resolv_cb)))
637 gbl_resolv_flags.use_external_net_name_resolver = TRUE;
639 gbl_resolv_flags.use_external_net_name_resolver = FALSE;
641 /* We've crossed the Rubicon; get rid of the file selection box. */
642 window_destroy(GTK_WIDGET(file_open_w));
646 #endif /* USE_WIN32_FILE_DIALOGS */
651 * <platform/>_open_file routines should upon entry...
652 * Set the path and fill in the filename if the path+filename is provided.
653 * Set the display filter if provided. Filter syntax should be checked.
654 * Set the name resolution check boxes to match the global settings.
655 * ...and upon exit...
656 * Return TRUE on "OK" and "FALSE" on "Cancel".
657 * Set the global name resolution preferences on "OK".
662 file_open_cmd(capture_file *cf, GtkWidget *w _U_)
664 GString *file_name = g_string_new("");
665 GString *display_filter = g_string_new("");
666 dfilter_t *rfcode = NULL;
670 * Loop until the user either selects a file or gives up.
673 #ifdef USE_WIN32_FILE_DIALOGS
674 if (win32_open_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), file_name, display_filter)) {
675 #else /* USE_WIN32_FILE_DIALOGS */
676 if (gtk_open_file(top_level, file_name, display_filter)) {
677 #endif /* USE_WIN32_FILE_DIALOGS */
679 /* Only close the old file now that we know we want to open another one. */
681 /* apply our filter */
682 if (dfilter_compile(display_filter->str, &rfcode)) {
683 cf_set_rfcode(&cfile, rfcode);
685 /* Not valid. Tell the user, and go back and run the file
686 selection box again once they dismiss the alert. */
687 bad_dfilter_alert_box(top_level, display_filter->str);
691 /* Try to open the capture file. */
692 if (cf_open(&cfile, file_name->str, FALSE, &err) != CF_OK) {
693 /* We couldn't open it; don't dismiss the open dialog box,
694 just leave it around so that the user can, after they
695 dismiss the alert box popped up for the open error,
698 dfilter_free(rfcode);
703 switch (cf_read(&cfile, FALSE)) {
707 /* Just because we got an error, that doesn't mean we were unable
708 to read any of the file; we handle what we could get from the
712 case CF_READ_ABORTED:
713 /* The user bailed out of re-reading the capture file; the
714 capture file has been closed - just free the capture file name
715 string and return (without changing the last containing
717 g_string_free(file_name, TRUE);
718 g_string_free(display_filter, TRUE);
721 /* Save the name of the containing directory specified in the path name,
722 if any; we can write over cf_name, which is a good thing, given that
723 "get_dirname()" does write over its argument. */
724 set_last_open_dir(get_dirname(file_name->str));
726 g_string_free(file_name, TRUE);
727 g_string_free(display_filter, TRUE);
733 file_open_cmd_cb(GtkWidget *widget, gpointer data _U_) {
734 /* If there's unsaved data, let the user save it first.
735 If they cancel out of it, don't quit. */
736 if (test_file_close(&cfile, FALSE, " before opening a new capture file"))
737 file_open_cmd(&cfile, widget);
740 #ifndef USE_WIN32_FILE_DIALOGS
742 gtk_merge_file(GtkWidget *w, GString *file_name, GString *display_filter, int *merge_type)
744 GtkWidget *file_merge_w;
745 GtkWidget *main_hb, *main_vb, *filter_hbox, *filter_bt, *filter_te;
746 GtkWidget *prepend_rb, *chrono_rb, *append_rb, *prev;
748 /* No Apply button, and "OK" just sets our text widget, it doesn't
749 activate it (i.e., it doesn't cause us to try to open the file). */
750 static construct_args_t args = {
751 "Wireshark: Display Filter",
758 if (!file_name || !display_filter || !merge_type)
761 /* Default to saving all packets, in the file's current format. */
763 file_merge_w = file_selection_new("Wireshark: Merge with Capture File",
764 GTK_WINDOW(top_level),
765 FILE_SELECTION_OPEN);
766 /* it's annoying, that the file chooser dialog is already shown here,
767 so we cannot use the correct gtk_window_set_default_size() to resize it */
768 gtk_widget_set_size_request(file_merge_w, DEF_WIDTH, DEF_HEIGHT);
770 if (file_name->len > 0) {
771 gchar *dirname = g_path_get_dirname(file_name->str);
773 file_selection_set_current_folder(file_merge_w, dirname);
776 switch (prefs.gui_fileopen_style) {
778 case FO_STYLE_LAST_OPENED:
779 /* The user has specified that we should start out in the last directory
780 we looked in. If we've already opened a file, use its containing
781 directory, if we could determine it, as the directory, otherwise
782 use the "last opened" directory saved in the preferences file if
784 /* This is now the default behaviour in file_selection_new() */
787 case FO_STYLE_SPECIFIED:
788 /* The user has specified that we should always start out in a
789 specified directory; if they've specified that directory,
790 start out by showing the files in that dir. */
791 if (prefs.gui_fileopen_dir[0] != '\0')
792 file_selection_set_current_folder(file_merge_w, prefs.gui_fileopen_dir);
797 main_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
798 #if GTK_CHECK_VERSION(3,0,0)
799 gtk_widget_set_vexpand(main_hb, FALSE); /* prevents "inheritance" from child VEXPAND */
800 /* so hbox doesn't expand vertically even */
801 /* tho grid rows have VEXPAND. */
803 file_selection_set_extra_widget(file_merge_w, main_hb);
804 gtk_widget_show(main_hb);
806 /* Container for each row of widgets */
807 main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
808 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
809 gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
810 gtk_widget_show(main_vb);
813 filter_hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1, FALSE);
814 gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
815 gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
816 gtk_widget_show(filter_hbox);
818 filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
819 g_signal_connect(filter_bt, "clicked",
820 G_CALLBACK(display_filter_construct_cb), &args);
821 g_signal_connect(filter_bt, "destroy",
822 G_CALLBACK(filter_button_destroy_cb), NULL);
823 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
824 gtk_widget_show(filter_bt);
825 gtk_widget_set_tooltip_text(filter_bt, "Open the \"Display Filter\" dialog to edit/apply filters");
827 filter_te = gtk_entry_new();
828 g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
829 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
830 g_signal_connect(filter_te, "changed",
831 G_CALLBACK(filter_te_syntax_check_cb), NULL);
832 g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
833 g_signal_connect(filter_te, "key-press-event", G_CALLBACK(filter_string_te_key_pressed_cb), NULL);
834 g_signal_connect(file_merge_w, "key-press-event", G_CALLBACK(filter_parent_dlg_key_pressed_cb), NULL);
835 colorize_filter_te_as_empty(filter_te);
836 gtk_entry_set_text(GTK_ENTRY(filter_te), display_filter->str);
837 gtk_widget_show(filter_te);
838 gtk_widget_set_tooltip_text(filter_te, "Enter a display filter.");
840 g_object_set_data(G_OBJECT(file_merge_w), E_RFILTER_TE_KEY, filter_te);
843 prepend_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL,
845 gtk_widget_set_tooltip_text(prepend_rb, "Insert packets from the selected file before the current file. Packet timestamps will be ignored.");
846 gtk_box_pack_start(GTK_BOX(main_vb), prepend_rb, FALSE, FALSE, 0);
847 gtk_widget_show(prepend_rb);
849 chrono_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Merge chronologically");
850 gtk_widget_set_tooltip_text(chrono_rb, "Insert packets in chronological order.");
851 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chrono_rb), TRUE);
852 gtk_box_pack_start(GTK_BOX(main_vb), chrono_rb, FALSE, FALSE, 0);
853 gtk_widget_show(chrono_rb);
855 append_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Append packets");
856 gtk_widget_set_tooltip_text(append_rb, "Insert packets from the selected file after the current file. Packet timestamps will be ignored.");
857 gtk_box_pack_start(GTK_BOX(main_vb), append_rb, FALSE, FALSE, 0);
858 gtk_widget_show(append_rb);
861 prev = preview_new();
862 g_object_set_data(G_OBJECT(file_merge_w), PREVIEW_TABLE_KEY, prev);
863 gtk_widget_show_all(prev);
864 gtk_box_pack_start(GTK_BOX(main_hb), prev, FALSE, FALSE, 0);
866 g_signal_connect(GTK_FILE_CHOOSER(file_merge_w), "selection-changed",
867 G_CALLBACK(file_open_entry_changed), file_merge_w);
868 file_open_entry_changed(file_merge_w, file_merge_w);
870 g_object_set_data(G_OBJECT(file_merge_w), E_DFILTER_TE_KEY,
871 g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
873 cf_name = file_selection_run(file_merge_w);
874 if (cf_name == NULL) {
875 /* User cancelled or closed the dialog. */
879 g_string_printf(file_name, "%s", cf_name);
881 g_string_printf(display_filter, "%s", gtk_entry_get_text(GTK_ENTRY(filter_te)));
883 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chrono_rb))) {
885 } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(prepend_rb))) {
891 /* We've crossed the Rubicon; get rid of the file selection box. */
892 window_destroy(GTK_WIDGET(file_merge_w));
896 #endif /* USE_WIN32_FILE_DIALOGS */
898 /* Merge existing with another file */
901 * <platform/>_merge_file routines should upon entry...
902 * Set the path and fill in the filename if the path+filename is provided.
903 * Set the display filter if provided. Filter syntax should be checked.
904 * ...and upon exit...
905 * Return TRUE on "OK" and "FALSE" on "Cancel".
910 file_merge_cmd(GtkWidget *w _U_)
912 GString *file_name = g_string_new("");
913 GString *display_filter = g_string_new("");
915 dfilter_t *rfcode = NULL;
918 cf_status_t merge_status;
919 char *in_filenames[2];
920 char *tmpname = NULL;
923 * Loop until the user either selects a file or gives up.
926 g_string_truncate(file_name, 0);
927 g_string_truncate(display_filter, 0);
928 #ifdef USE_WIN32_FILE_DIALOGS
929 if (win32_merge_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), file_name, display_filter, &merge_type)) {
930 #else /* USE_WIN32_FILE_DIALOGS */
931 if (gtk_merge_file(top_level, file_name, display_filter, &merge_type)) {
932 #endif /* USE_WIN32_FILE_DIALOGS */
934 /* Get the specified read filter and try to compile it. */
935 if (!dfilter_compile(display_filter->str, &rfcode)) {
936 /* Not valid. Tell the user, and go back and run the file
937 selection box again once they dismiss the alert. */
938 bad_dfilter_alert_box(top_level, display_filter->str);
942 file_type = cfile.cd_t;
944 /* Try to merge or append the two files */
946 if (merge_type == 0) {
947 /* chronological order */
948 in_filenames[0] = cfile.filename;
949 in_filenames[1] = file_name->str;
950 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
951 } else if (merge_type < 0) {
953 in_filenames[0] = file_name->str;
954 in_filenames[1] = cfile.filename;
955 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
958 in_filenames[0] = cfile.filename;
959 in_filenames[1] = file_name->str;
960 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
963 if (merge_status != CF_OK) {
965 dfilter_free(rfcode);
972 /* Try to open the merged capture file. */
973 if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
974 /* We couldn't open it; fail. */
976 dfilter_free(rfcode);
978 g_string_free(file_name, TRUE);
979 g_string_free(display_filter, TRUE);
983 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
984 it closed the previous capture file, and thus destroyed any
985 previous read filter attached to "cf"). */
986 cfile.rfcode = rfcode;
988 switch (cf_read(&cfile, FALSE)) {
992 /* Just because we got an error, that doesn't mean we were unable
993 to read any of the file; we handle what we could get from the
997 case CF_READ_ABORTED:
998 /* The user bailed out of re-reading the capture file; the
999 capture file has been closed - just free the capture file name
1000 string and return (without changing the last containing
1003 g_string_free(file_name, TRUE);
1004 g_string_free(display_filter, TRUE);
1009 /* Save the name of the containing directory specified in the path name,
1010 if any; we can write over cf_merged_name, which is a good thing, given that
1011 "get_dirname()" does write over its argument. */
1013 set_last_open_dir(get_dirname(tmpname));
1016 g_string_free(file_name, TRUE);
1017 g_string_free(display_filter, TRUE);
1023 file_merge_cmd_cb(GtkWidget *widget, gpointer data _U_) {
1024 /* If there's unsaved data, let the user save it first.
1025 If they cancel out of it, don't merge. */
1026 GtkWidget *msg_dialog;
1027 gchar *display_basename;
1030 if (prefs.gui_ask_unsaved) {
1031 if (cf_has_unsaved_data(&cfile)) {
1032 /* This file has unsaved data; ask the user whether to save the
1034 if (cfile.is_tempfile) {
1035 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1036 (GtkDialogFlags)(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
1037 GTK_MESSAGE_QUESTION,
1039 "Do you want to save the captured packets before merging another capture file into it?");
1041 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
1042 "A temporary capture file can't be merged.");
1045 * Format the message.
1047 display_basename = g_filename_display_basename(cfile.filename);
1048 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1049 (GtkDialogFlags)(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
1050 GTK_MESSAGE_QUESTION,
1052 "Do you want to save the changes you've made "
1053 "to the capture file \"%s\" before merging another capture file into it?",
1055 g_free(display_basename);
1056 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
1057 "The changes must be saved before the files are merged.");
1060 gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1061 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1062 gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1063 WIRESHARK_STOCK_SAVE, GTK_RESPONSE_ACCEPT);
1064 gtk_dialog_set_alternative_button_order(GTK_DIALOG(msg_dialog),
1065 GTK_RESPONSE_ACCEPT,
1066 GTK_RESPONSE_CANCEL,
1068 gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog),
1069 GTK_RESPONSE_ACCEPT);
1071 response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
1072 gtk_widget_destroy(msg_dialog);
1076 case GTK_RESPONSE_ACCEPT:
1077 /* Save the file but don't close it */
1078 do_file_save(&cfile, FALSE);
1081 case GTK_RESPONSE_CANCEL:
1082 case GTK_RESPONSE_NONE:
1083 case GTK_RESPONSE_DELETE_EVENT:
1085 /* Don't do the merge. */
1092 file_merge_cmd(widget);
1097 do_capture_stop(capture_file *cf)
1099 /* Stop the capture (complete with UI updates). */
1100 capture_stop_cb(NULL, NULL);
1102 /* Now run the main loop until the capture stops and we finish
1103 reading it; we need to run the main loop so we respond to
1104 messages on the sync pipe and the sync pipe being closed. */
1105 while (cf->state == FILE_READ_IN_PROGRESS)
1106 gtk_main_iteration();
1110 /* Returns true if the current file has been saved or if the user has chosen
1111 * to discard it, ie if it is safe to continue with the call to close, and
1112 * false otherwise. */
1114 test_file_close(capture_file *cf, gboolean from_quit, const char *before_what)
1116 GtkWidget *msg_dialog;
1117 gchar *display_basename;
1119 gboolean capture_in_progress;
1121 if (cf->state == FILE_CLOSED)
1122 return TRUE; /* already closed, nothing to do */
1125 if (cf->state == FILE_READ_IN_PROGRESS) {
1126 /* This is true if we're reading a capture file *or* if we're doing
1127 a live capture. If we're reading a capture file, the main loop
1128 is busy reading packets, and only accepting input from the
1129 progress dialog, so we can't get here, so this means we're
1131 capture_in_progress = TRUE;
1134 capture_in_progress = FALSE;
1136 if (prefs.gui_ask_unsaved) {
1137 if (cf_has_unsaved_data(cf) || capture_in_progress) {
1138 /* This file has unsaved data or there's a capture in progress;
1139 ask the user whether to save the data. */
1140 if (cf->is_tempfile) {
1141 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1142 (GtkDialogFlags)(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
1143 GTK_MESSAGE_QUESTION,
1145 capture_in_progress ?
1146 "Do you want to stop the capture and save the captured packets%s?" :
1147 "Do you want to save the captured packets%s?",
1150 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
1151 "Your captured packets will be lost if you don't save them.");
1154 * Format the message.
1156 display_basename = g_filename_display_basename(cf->filename);
1157 if (capture_in_progress) {
1158 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1159 (GtkDialogFlags)(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
1160 GTK_MESSAGE_QUESTION,
1162 "Do you want to stop the capture and save the captured packets%s?",
1165 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1166 (GtkDialogFlags)(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
1167 GTK_MESSAGE_QUESTION,
1169 "Do you want to save the changes you've made "
1170 "to the capture file \"%s\"%s?",
1171 display_basename, before_what);
1173 g_free(display_basename);
1174 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
1175 capture_in_progress ?
1176 "Your captured packets will be lost if you don't save them." :
1177 "Your changes will be lost if you don't save them.");
1180 /* If this is from a Quit operation, use "quit and don't save"
1181 rather than just "don't save". */
1182 gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1184 (cf->state == FILE_READ_IN_PROGRESS ?
1185 WIRESHARK_STOCK_STOP_QUIT_DONT_SAVE :
1186 WIRESHARK_STOCK_QUIT_DONT_SAVE) :
1187 (capture_in_progress ?
1188 WIRESHARK_STOCK_STOP_DONT_SAVE :
1189 WIRESHARK_STOCK_DONT_SAVE)),
1190 GTK_RESPONSE_REJECT);
1191 gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1192 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1193 gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1194 (capture_in_progress ?
1195 WIRESHARK_STOCK_STOP_SAVE :
1196 WIRESHARK_STOCK_SAVE),
1197 GTK_RESPONSE_ACCEPT);
1198 gtk_dialog_set_alternative_button_order(GTK_DIALOG(msg_dialog),
1199 GTK_RESPONSE_ACCEPT,
1200 GTK_RESPONSE_CANCEL,
1201 GTK_RESPONSE_REJECT,
1203 gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog),
1204 GTK_RESPONSE_ACCEPT);
1206 response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
1207 gtk_widget_destroy(msg_dialog);
1211 case GTK_RESPONSE_ACCEPT:
1213 /* If there's a capture in progress, we have to stop the capture
1214 and then do the save. */
1215 if (capture_in_progress)
1216 do_capture_stop(cf);
1218 /* Save the file and close it */
1219 do_file_save(cf, TRUE);
1222 case GTK_RESPONSE_REJECT:
1224 /* If there's a capture in progress; we have to stop the capture
1225 and then do the close. */
1226 if (capture_in_progress)
1227 do_capture_stop(cf);
1231 case GTK_RESPONSE_CANCEL:
1232 case GTK_RESPONSE_NONE:
1233 case GTK_RESPONSE_DELETE_EVENT:
1235 /* Don't close the file (and don't stop any capture in progress). */
1236 return FALSE; /* file not closed */
1239 /* unchanged file, safe to close */
1243 /* User asked not to be bothered by those prompts, just close it.
1244 XXX - should that apply only to saving temporary files? */
1246 /* If there's a capture in progress, we have to stop the capture
1247 and then do the close. */
1248 if (capture_in_progress)
1249 do_capture_stop(cf);
1253 return TRUE; /* shouldn't get here? */
1257 do_file_close(capture_file *cf, gboolean from_quit, const char *before_what)
1259 if (test_file_close(cf, from_quit, before_what)) {
1268 file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
1269 do_file_close(&cfile, FALSE, "");
1272 #define RESPONSE_DISCARD_COMMENTS_AND_SAVE 1
1273 #define RESPONSE_SAVE_IN_ANOTHER_FORMAT 2
1275 static check_savability_t
1276 check_save_with_comments(capture_file *cf)
1278 guint32 comment_types;
1279 GtkWidget *msg_dialog;
1282 /* What types of comments do we have? */
1283 comment_types = cf_comment_types(cf);
1285 /* Does the file's format support all the comments we have? */
1286 if (wtap_dump_supports_comment_types(cf->cd_t, comment_types)) {
1287 /* Yes. Let the save happen; we can save all the comments, so
1288 there's no need to delete them. */
1292 /* No. Are there formats in which we can write this file that
1293 supports all the comments in this file? */
1294 if (wtap_dump_can_write(cf->linktypes, comment_types)) {
1295 /* Yes. Offer the user a choice of "Save in a format that
1296 supports comments", "Discard comments and save in the
1297 file's own format", or "Cancel", meaning "don't bother
1298 saving the file at all". */
1299 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1300 GTK_DIALOG_DESTROY_WITH_PARENT,
1301 GTK_MESSAGE_QUESTION,
1303 "The capture has comments, but the file's format "
1304 "doesn't support comments. Do you want to save the capture "
1305 "in a format that supports comments, or discard the comments "
1306 "and save in the file's format?");
1307 gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
1308 "Discard comments and save",
1309 RESPONSE_DISCARD_COMMENTS_AND_SAVE,
1311 GTK_RESPONSE_CANCEL,
1312 "Save in another format",
1313 RESPONSE_SAVE_IN_ANOTHER_FORMAT,
1315 gtk_dialog_set_alternative_button_order(GTK_DIALOG(msg_dialog),
1316 RESPONSE_SAVE_IN_ANOTHER_FORMAT,
1317 GTK_RESPONSE_CANCEL,
1318 RESPONSE_DISCARD_COMMENTS_AND_SAVE,
1320 gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog),
1321 RESPONSE_SAVE_IN_ANOTHER_FORMAT);
1323 /* No. Offer the user a choice of "Discard comments and
1324 save in the file's format" or "Cancel". */
1325 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1326 GTK_DIALOG_DESTROY_WITH_PARENT,
1327 GTK_MESSAGE_QUESTION,
1329 "The capture has comments, but no file format in which it "
1330 "can be saved supports comments. Do you want to discard "
1331 "the comments and save in the file's format?");
1332 gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
1334 GTK_RESPONSE_CANCEL,
1335 "Discard comments and save",
1336 RESPONSE_DISCARD_COMMENTS_AND_SAVE,
1338 gtk_dialog_set_alternative_button_order(GTK_DIALOG(msg_dialog),
1339 RESPONSE_DISCARD_COMMENTS_AND_SAVE,
1340 GTK_RESPONSE_CANCEL,
1342 gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog),
1343 GTK_RESPONSE_CANCEL);
1346 response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
1347 gtk_widget_destroy(msg_dialog);
1351 case RESPONSE_SAVE_IN_ANOTHER_FORMAT:
1352 /* Let the user select another format. */
1353 return SAVE_IN_ANOTHER_FORMAT;
1355 case RESPONSE_DISCARD_COMMENTS_AND_SAVE:
1356 /* Save without the comments and, if that succeeds, delete the
1358 return SAVE_WITHOUT_COMMENTS;
1360 case GTK_RESPONSE_CANCEL:
1361 case GTK_RESPONSE_NONE:
1362 case GTK_RESPONSE_DELETE_EVENT:
1370 * Save the capture file in question, prompting the user for a file
1371 * name to save to if necessary.
1374 do_file_save(capture_file *cf, gboolean dont_reopen)
1377 gboolean discard_comments;
1378 cf_write_status_t status;
1380 if (cf->is_tempfile) {
1381 /* This is a temporary capture file, so saving it means saving
1382 it to a permanent file. Prompt the user for a location
1383 to which to save it. Don't require that the file format
1384 support comments - if it's a temporary capture file, it's
1385 probably pcap-ng, which supports comments and, if it's
1386 not pcap-ng, let the user decide what they want to do
1387 if they've added comments. */
1388 file_save_as_cmd(cf, FALSE, dont_reopen);
1390 if (cf->unsaved_changes) {
1391 /* This is not a temporary capture file, but it has unsaved
1392 changes, so saving it means doing a "safe save" on top
1393 of the existing file, in the same format - no UI needed
1394 unless the file has comments and the file's format doesn't
1397 If the file has comments, does the file's format support them?
1398 If not, ask the user whether they want to discard the comments
1399 or choose a different format. */
1400 switch (check_save_with_comments(cf)) {
1403 /* The file can be saved in the specified format as is;
1404 just drive on and save in the format they selected. */
1405 discard_comments = FALSE;
1408 case SAVE_WITHOUT_COMMENTS:
1409 /* The file can't be saved in the specified format as is,
1410 but it can be saved without the comments, and the user
1411 said "OK, discard the comments", so save it in the
1412 format they specified without the comments. */
1413 discard_comments = TRUE;
1416 case SAVE_IN_ANOTHER_FORMAT:
1417 /* There are file formats in which we can save this that
1418 support comments, and the user said not to delete the
1419 comments. Do a "Save As" so the user can select
1420 one of those formats and choose a file name. */
1421 file_save_as_cmd(cf, TRUE, dont_reopen);
1425 /* The user said "forget it". Just return. */
1429 /* Squelch warnings that discard_comments is being used
1431 g_assert_not_reached();
1435 /* XXX - cf->filename might get freed out from under us, because
1436 the code path through which cf_save_packets() goes currently
1437 closes the current file and then opens and reloads the saved file,
1438 so make a copy and free it later. */
1439 fname = g_strdup(cf->filename);
1440 status = cf_save_packets(cf, fname, cf->cd_t, cf->iscompressed,
1441 discard_comments, dont_reopen);
1445 /* The save succeeded; we're done.
1446 If we discarded comments, redraw the packet list to reflect
1447 any packets that no longer have comments. */
1448 if (discard_comments)
1449 packet_list_queue_draw();
1452 case CF_WRITE_ERROR:
1453 /* The write failed.
1454 XXX - OK, what do we do now? Let them try a
1455 "Save As", in case they want to try to save to a
1456 different directory r file system? */
1459 case CF_WRITE_ABORTED:
1460 /* The write was aborted; just drive on. */
1465 /* Otherwise just do nothing. */
1470 file_save_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
1471 do_file_save(&cfile, FALSE);
1474 /* Attach a list of the valid 'save as' file types to a combo_box by
1475 checking what Wiretap supports. Make the default type the first
1476 in the list. If must_supprt_comments is true, restrict the list
1477 to those formats that support comments (currently, just pcap-ng).
1479 Returns the default file type. */
1481 set_file_type_list(GtkWidget *combo_box, capture_file *cf,
1482 gboolean must_support_all_comments)
1484 guint32 required_comment_types;
1485 GArray *savable_file_types;
1488 int default_ft = -1;
1490 /* What types of comments do we have to support? */
1491 if (must_support_all_comments)
1492 required_comment_types = cf_comment_types(cf); /* all the ones the file has */
1494 required_comment_types = 0; /* none of them */
1496 /* What types of file can we save this file as? */
1497 savable_file_types = wtap_get_savable_file_types(cf->cd_t, cf->linktypes,
1498 required_comment_types);
1500 if (savable_file_types != NULL) {
1501 /* OK, we have at least one file type we can save this file as.
1502 (If we didn't, we shouldn't have gotten here in the first
1503 place.) Add them all to the combo box. */
1504 for (i = 0; i < savable_file_types->len; i++) {
1505 ft = g_array_index(savable_file_types, int, i);
1506 if (default_ft == -1)
1507 default_ft = ft; /* first file type is the default */
1508 ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box),
1509 wtap_file_type_string(ft),
1510 GINT_TO_POINTER(ft));
1512 g_array_free(savable_file_types, TRUE);
1519 file_select_file_type_cb(GtkWidget *w, gpointer parent_arg)
1521 GtkWidget *parent = (GtkWidget *)parent_arg;
1524 GtkWidget *compressed_cb;
1526 compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(parent), E_COMPRESSED_CB_KEY);
1527 if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(w), &ptr)) {
1528 /* XXX - this can happen when we clear the list of file types
1529 and then reconstruct it. */
1530 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compressed_cb), FALSE);
1531 gtk_widget_set_sensitive(compressed_cb, FALSE);
1534 new_file_type = GPOINTER_TO_INT(ptr);
1536 if (!wtap_dump_can_compress(new_file_type)) {
1537 /* Can't compress this file type; turn off compression and make
1538 the compression checkbox insensitive. */
1539 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compressed_cb), FALSE);
1540 gtk_widget_set_sensitive(compressed_cb, FALSE);
1542 gtk_widget_set_sensitive(compressed_cb, TRUE);
1545 static check_savability_t
1546 gtk_check_save_as_with_comments(GtkWidget *w, capture_file *cf, int file_type)
1548 guint32 comment_types;
1549 GtkWidget *msg_dialog;
1552 /* What types of comments do we have? */
1553 comment_types = cf_comment_types(cf);
1555 /* Does the file's format support all the comments we have? */
1556 if (wtap_dump_supports_comment_types(file_type, comment_types)) {
1557 /* Yes. Let the save happen; we can save all the comments, so
1558 there's no need to delete them. */
1562 /* No. Are there formats in which we can write this file that
1563 supports all the comments in this file? */
1564 if (wtap_dump_can_write(cf->linktypes, comment_types)) {
1565 /* Yes. Offer the user a choice of "Save in a format that
1566 supports comments", "Discard comments and save in the
1567 format you selected", or "Cancel", meaning "don't bother
1568 saving the file at all". */
1569 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(w),
1570 GTK_DIALOG_DESTROY_WITH_PARENT,
1571 GTK_MESSAGE_QUESTION,
1573 "The capture has comments, but the file format you chose "
1574 "doesn't support comments. Do you want to save the capture "
1575 "in a format that supports comments, or discard the comments "
1576 "and save in the format you chose?");
1577 gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
1578 "Discard comments and save",
1579 RESPONSE_DISCARD_COMMENTS_AND_SAVE,
1581 GTK_RESPONSE_CANCEL,
1582 "Save in another format",
1583 RESPONSE_SAVE_IN_ANOTHER_FORMAT,
1585 gtk_dialog_set_alternative_button_order(GTK_DIALOG(msg_dialog),
1586 RESPONSE_SAVE_IN_ANOTHER_FORMAT,
1587 GTK_RESPONSE_CANCEL,
1588 RESPONSE_DISCARD_COMMENTS_AND_SAVE,
1590 gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog),
1591 RESPONSE_SAVE_IN_ANOTHER_FORMAT);
1593 /* No. Offer the user a choice of "Discard comments and
1594 save in the format you selected" or "Cancel". */
1595 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(w),
1596 GTK_DIALOG_DESTROY_WITH_PARENT,
1597 GTK_MESSAGE_QUESTION,
1599 "The capture has comments, but no file format in which it "
1600 "can be saved supports comments. Do you want to discard "
1601 "the comments and save in the format you chose?");
1602 gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
1604 GTK_RESPONSE_CANCEL,
1605 "Discard comments and save",
1606 RESPONSE_DISCARD_COMMENTS_AND_SAVE,
1608 gtk_dialog_set_alternative_button_order(GTK_DIALOG(msg_dialog),
1609 RESPONSE_DISCARD_COMMENTS_AND_SAVE,
1610 GTK_RESPONSE_CANCEL,
1612 gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog),
1613 GTK_RESPONSE_CANCEL);
1616 response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
1617 gtk_widget_destroy(msg_dialog);
1621 case RESPONSE_SAVE_IN_ANOTHER_FORMAT:
1622 /* OK, the only other format we support is pcap-ng. Make that
1623 the one and only format in the combo box, and return to
1624 let the user continue with the dialog.
1626 XXX - removing all the formats from the combo box will clear
1627 the compressed checkbox; get the current value and restore
1630 XXX - we know pcap-ng can be compressed; if we ever end up
1631 supporting saving comments in a format that *can't* be
1632 compressed, such as NetMon format, we must check this. */
1633 /* XXX - need a compressed checkbox here! */
1634 return SAVE_IN_ANOTHER_FORMAT;
1636 case RESPONSE_DISCARD_COMMENTS_AND_SAVE:
1637 /* Save without the comments and, if that succeeds, delete the
1639 return SAVE_WITHOUT_COMMENTS;
1641 case GTK_RESPONSE_CANCEL:
1642 case GTK_RESPONSE_NONE:
1643 case GTK_RESPONSE_DELETE_EVENT:
1650 #ifndef USE_WIN32_FILE_DIALOGS
1653 gtk_save_as_file(GtkWidget *w _U_, capture_file *cf, GString *file_name, int *file_type,
1654 gboolean *compressed, gboolean must_support_all_comments)
1656 GtkWidget *file_save_as_w;
1657 GtkWidget *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *compressed_cb;
1662 if (!file_name || !file_type || !compressed)
1665 /* Default to saving in the file's current format. */
1667 /* build the file selection */
1668 file_save_as_w = file_selection_new("Wireshark: Save Capture File As",
1669 GTK_WINDOW(top_level),
1670 FILE_SELECTION_SAVE);
1672 /* Container for each row of widgets */
1674 main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
1675 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1676 file_selection_set_extra_widget(file_save_as_w, main_vb);
1677 gtk_widget_show(main_vb);
1680 ft_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1681 gtk_box_pack_start(GTK_BOX(main_vb), ft_hb, FALSE, FALSE, 0);
1682 gtk_widget_show(ft_hb);
1684 ft_lb = gtk_label_new("File type:");
1685 gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
1686 gtk_widget_show(ft_lb);
1688 ft_combo_box = ws_combo_box_new_text_and_pointer();
1690 /* Generate the list of file types we can save. */
1691 default_ft = set_file_type_list(ft_combo_box, cf, must_support_all_comments);
1692 gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
1693 gtk_widget_show(ft_combo_box);
1694 g_object_set_data(G_OBJECT(file_save_as_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
1696 /* compressed - if the file is currently compressed, and the default
1697 file type supports compression, turn the checkbox on */
1698 compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
1699 gtk_box_pack_start(GTK_BOX(ft_hb), compressed_cb, FALSE, FALSE, 0);
1700 if (cf->iscompressed && wtap_dump_can_compress(default_ft))
1701 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compressed_cb), TRUE);
1702 gtk_widget_show(compressed_cb);
1703 g_object_set_data(G_OBJECT(file_save_as_w), E_COMPRESSED_CB_KEY, compressed_cb);
1705 /* Ok: now "select" the default filetype which invokes file_select_file_type_cb */
1706 g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_select_file_type_cb), file_save_as_w);
1707 ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0);
1709 cf_name = file_selection_run(file_save_as_w);
1711 if (cf_name == NULL) {
1712 /* User cancelled or closed the dialog. */
1716 if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
1717 g_assert_not_reached(); /* Programming error: somehow nothing is active */
1719 *file_type = GPOINTER_TO_INT(ptr);
1720 *compressed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb));
1722 /* We've crossed the Rubicon; get rid of the file selection box. */
1723 window_destroy(GTK_WIDGET(file_save_as_w));
1725 g_string_printf(file_name, "%s", cf_name);
1729 #endif /* USE_WIN32_FILE_DIALOGS */
1732 file_add_extension(GString *file_name, int file_type, gboolean compressed) {
1733 gchar *file_name_lower;
1734 GString *file_suffix;
1735 GSList *extensions_list, *extension;
1736 gboolean add_extension;
1739 * Append the default file extension if there's none given by
1740 * the user or if they gave one that's not one of the valid
1741 * extensions for the file type.
1743 file_name_lower = g_utf8_strdown(file_name->str, -1);
1744 file_suffix = g_string_new("");
1745 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1746 if (extensions_list != NULL) {
1747 /* We have one or more extensions for this file type.
1748 Start out assuming we need to add the default one. */
1749 add_extension = TRUE;
1751 /* OK, see if the file has one of those extensions. */
1752 for (extension = extensions_list; extension != NULL;
1753 extension = g_slist_next(extension)) {
1754 g_string_printf(file_suffix, ".%s", (char *)extension->data);
1755 if (g_str_has_suffix(file_name_lower, file_suffix->str)) {
1757 * The file name has one of the extensions for
1760 add_extension = FALSE;
1763 g_string_append(file_suffix, ".gz");
1764 if (g_str_has_suffix(file_name_lower, file_suffix->str)) {
1766 * The file name has one of the extensions for
1769 add_extension = FALSE;
1774 /* We have no extensions for this file type. Don't add one. */
1775 add_extension = FALSE;
1777 g_free(file_name_lower);
1778 g_string_free(file_suffix, TRUE);
1779 if (add_extension) {
1780 if (wtap_default_file_extension(file_type) != NULL) {
1781 g_string_append_printf(file_name, ".%s",
1782 wtap_default_file_extension(file_type));
1784 g_string_append(file_name, ".gz");
1790 /* Save a file with a user-specified name */
1793 * <platform/>_save_as_file routines should upon entry...
1794 * Set the path and fill in the filename if the path+filename is provided.
1795 * ...and upon exit...
1796 * Return TRUE on "OK" and "FALSE" on "Cancel".
1801 file_save_as_cmd(capture_file *cf, gboolean must_support_all_comments,
1802 gboolean dont_reopen)
1804 GString *file_name = g_string_new("");
1806 gboolean compressed;
1808 gboolean discard_comments = FALSE;
1809 cf_write_status_t status;
1812 * Loop until the user either selects a file or gives up.
1815 #ifdef USE_WIN32_FILE_DIALOGS
1816 if (win32_save_as_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), cf,
1817 file_name, &file_type, &compressed, must_support_all_comments)) {
1818 /* They discarded comments, so redraw the packet details window
1819 to reflect any packets that no longer have comments. */
1820 packet_list_queue_draw();
1821 #else /* USE_WIN32_FILE_DIALOGS */
1822 if (gtk_save_as_file(top_level, cf, file_name, &file_type, &compressed, must_support_all_comments)) {
1823 #endif /* USE_WIN32_FILE_DIALOGS */
1825 /* If the file has comments, does the format the user selected
1826 support them? If not, ask the user whether they want to
1827 discard the comments or choose a different format. */
1828 #ifdef USE_WIN32_FILE_DIALOGS
1829 switch (win32_check_save_as_with_comments(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), cf, file_type)) {
1830 #else /* USE_WIN32_FILE_DIALOGS */
1831 switch (gtk_check_save_as_with_comments(top_level, cf, file_type)) {
1832 #endif /* USE_WIN32_FILE_DIALOGS */
1835 /* The file can be saved in the specified format as is;
1836 just drive on and save in the format they selected. */
1837 discard_comments = FALSE;
1840 case SAVE_WITHOUT_COMMENTS:
1841 /* The file can't be saved in the specified format as is,
1842 but it can be saved without the comments, and the user
1843 said "OK, discard the comments", so save it in the
1844 format they specified without the comments. */
1845 discard_comments = TRUE;
1848 case SAVE_IN_ANOTHER_FORMAT:
1849 /* There are file formats in which we can save this that
1850 support comments, and the user said not to delete the
1851 comments. The combo box of file formats has had the
1852 formats that don't support comments trimmed from it,
1853 so run the dialog again, to let the user decide
1854 whether to save in one of those formats or give up. */
1855 discard_comments = FALSE;
1856 must_support_all_comments = TRUE;
1860 /* The user said "forget it". Just get rid of the dialog box
1862 g_string_free(file_name, TRUE);
1866 file_add_extension(file_name, file_type, compressed);
1869 /* If the file exists and it's user-immutable or not writable,
1870 ask the user whether they want to override that. */
1871 if (!file_target_unwritable_ui(top_level, file_name->str)) {
1872 /* They don't. Let them try another file name or cancel. */
1877 /* Attempt to save the file */
1878 status = cf_save_packets(&cfile, file_name->str, file_type, compressed,
1879 discard_comments, dont_reopen);
1883 /* The save succeeded; we're done. */
1884 /* Save the directory name for future file dialogs. */
1885 dirname = get_dirname(file_name->str); /* Overwrites file_name->str */
1886 set_last_open_dir(dirname);
1887 /* If we discarded comments, redraw the packet list to reflect
1888 any packets that no longer have comments. */
1889 if (discard_comments)
1890 packet_list_queue_draw();
1893 case CF_WRITE_ERROR:
1894 /* The save failed; let the user try again. */
1897 case CF_WRITE_ABORTED:
1898 /* The user aborted the save; just return. */
1899 g_string_free(file_name, TRUE);
1903 g_string_free(file_name, TRUE);
1909 file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
1911 file_save_as_cmd(&cfile, FALSE, FALSE);
1914 #ifndef USE_WIN32_FILE_DIALOGS
1916 gtk_export_specified_packets_file(GtkWidget *w _U_, GString *file_name, int *file_type,
1917 gboolean *compressed, packet_range_t *range)
1919 GtkWidget *file_export_specified_packets_w;
1920 GtkWidget *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *range_fr, *range_grid,
1925 if (!file_name || !file_type || !compressed || !range)
1928 /* Default to writing out all displayed packets, in the file's current format. */
1930 /* build the file selection */
1931 file_export_specified_packets_w = file_selection_new("Wireshark: Export Specified Packets",
1932 GTK_WINDOW(top_level),
1933 FILE_SELECTION_SAVE);
1935 /* Container for each row of widgets */
1937 main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
1938 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1939 file_selection_set_extra_widget(file_export_specified_packets_w, main_vb);
1940 gtk_widget_show(main_vb);
1942 /*** Packet Range frame ***/
1943 range_fr = gtk_frame_new("Packet Range");
1944 gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
1945 gtk_widget_show(range_fr);
1948 range_grid = range_new(range, TRUE);
1949 gtk_container_add(GTK_CONTAINER(range_fr), range_grid);
1950 gtk_widget_show(range_grid);
1953 ft_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1954 gtk_box_pack_start(GTK_BOX(main_vb), ft_hb, FALSE, FALSE, 0);
1956 gtk_widget_show(ft_hb);
1958 ft_lb = gtk_label_new("File type:");
1959 gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
1960 gtk_widget_show(ft_lb);
1962 ft_combo_box = ws_combo_box_new_text_and_pointer();
1964 /* Generate the list of file types we can save. */
1965 set_file_type_list(ft_combo_box, &cfile, FALSE);
1966 gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
1967 gtk_widget_show(ft_combo_box);
1968 g_object_set_data(G_OBJECT(file_export_specified_packets_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
1970 /* dynamic values in the range frame */
1971 range_update_dynamics(range_grid);
1974 compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
1975 gtk_box_pack_start(GTK_BOX(ft_hb), compressed_cb, FALSE, FALSE, 0);
1976 gtk_widget_show(compressed_cb);
1977 g_object_set_data(G_OBJECT(file_export_specified_packets_w), E_COMPRESSED_CB_KEY, compressed_cb);
1979 /* Ok: now "select" the default filetype which invokes file_select_file_type_cb */
1980 g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_select_file_type_cb), file_export_specified_packets_w);
1981 ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0);
1983 cf_name = file_selection_run(file_export_specified_packets_w);
1984 if (cf_name == NULL) {
1985 /* User cancelled or closed the dialog. */
1989 if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
1990 g_assert_not_reached(); /* Programming error: somehow nothing is active */
1992 *file_type = GPOINTER_TO_INT(ptr);
1993 *compressed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb));
1995 /* We've crossed the Rubicon; get rid of the file export box. */
1996 window_destroy(GTK_WIDGET(file_export_specified_packets_w));
1998 g_string_printf(file_name, "%s", cf_name);
2002 #endif /* USE_WIN32_FILE_DIALOGS */
2005 * <platform/>_export_specified_packets_file routines should upon entry...
2006 * Set the path and fill in the filename if the path+filename is provided.
2007 * ...and upon exit...
2008 * Not initialize range.
2009 * Return TRUE on "OK" and "FALSE" on "Cancel".
2014 file_export_specified_packets_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
2015 GString *file_name = g_string_new("");
2017 gboolean compressed;
2018 packet_range_t range;
2019 cf_write_status_t status;
2021 gchar *display_basename;
2022 GtkWidget *msg_dialog;
2024 /* init the packet range */
2025 packet_range_init(&range, &cfile);
2026 range.process_filtered = TRUE;
2027 range.include_dependents = TRUE;
2030 * Loop until the user either selects a file or gives up.
2033 #ifdef USE_WIN32_FILE_DIALOGS
2034 if (win32_export_specified_packets_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)),
2035 file_name, &file_type, &compressed, &range)) {
2036 #else /* USE_WIN32_FILE_DIALOGS */
2037 if (gtk_export_specified_packets_file(w, file_name, &file_type, &compressed, &range)) {
2038 #endif /* USE_WIN32_FILE_DIALOGS */
2040 /* Check whether the range is valid. */
2041 if (!range_check_validity_modal(top_level, &range)) {
2042 /* The range isn't valid; the user was told that, and dismissed
2043 the dialog telling them that, so let them fix the range
2044 and try again, or cancel. */
2048 /* XXX - Check for comments? */
2051 * Check that we're not going to save on top of the current
2053 * We do it here so we catch all cases ...
2054 * Unfortunately, the file requester gives us an absolute file
2055 * name and the read file name may be relative (if supplied on
2056 * the command line). From Joerg Mayer.
2058 if (files_identical(cfile.filename, file_name->str)) {
2059 display_basename = g_filename_display_basename(file_name->str);
2060 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
2061 GTK_DIALOG_DESTROY_WITH_PARENT,
2064 "The file \"%s\" is the capture file from which you're exporting the packets.",
2066 g_free(display_basename);
2067 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
2068 "You cannot export packets on top of the current capture file.");
2069 gtk_dialog_run(GTK_DIALOG(msg_dialog));
2070 gtk_widget_destroy(msg_dialog);
2074 file_add_extension(file_name, file_type, compressed);
2077 /* If the file exists and it's user-immutable or not writable,
2078 ask the user whether they want to override that. */
2079 if (!file_target_unwritable_ui(top_level, file_name->str)) {
2080 /* They don't. Let them try another file name or cancel. */
2085 /* Attempt to export the file */
2086 status = cf_export_specified_packets(&cfile, file_name->str, &range, file_type,
2091 /* The write succeeded; get rid of the file selection box. */
2092 /* cf_export_specified_packets() might already closed our dialog! */
2094 /* Save the directory name for future file dialogs.
2095 XXX - should there be separate ones for "Save As" and
2096 "Export Specified Packets"? */
2097 dirname = get_dirname(file_name->str); /* Overwrites file_name->str */
2098 set_last_open_dir(dirname);
2101 case CF_WRITE_ERROR:
2102 /* The save failed; let the user try again. */
2105 case CF_WRITE_ABORTED:
2106 /* The user aborted the save; just return. */
2107 g_string_free(file_name, TRUE);
2111 g_string_free(file_name, TRUE);
2117 /* Reload a file using the current read and display filters */
2119 file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
2123 /******************** Color Filters *********************************/
2125 * Keep a static pointer to the current "Color Export" window, if
2126 * any, so that if somebody tries to do "Export"
2127 * while there's already a "Color Export" window up, we just pop
2128 * up the existing one, rather than creating a new one.
2130 static GtkWidget *file_color_import_w;
2132 /* sets the file path to the global color filter file.
2133 WARNING: called by both the import and the export dialog.
2136 color_global_cb(GtkWidget *widget _U_, gpointer data)
2138 GtkWidget *fs_widget = (GtkWidget *)data;
2141 /* decide what file to open (from dfilter code) */
2142 path = get_datafile_path("colorfilters");
2144 gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(fs_widget), path);
2149 /* Import color filters */
2151 file_color_import_cmd_cb(GtkWidget *color_filters, gpointer filter_list _U_)
2153 #ifdef USE_WIN32_FILE_DIALOGS
2154 win32_import_color_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), color_filters);
2155 #else /* USE_WIN32_FILE_DIALOGS */
2156 GtkWidget *main_vb, *cfglobal_but;
2159 /* No Apply button, and "OK" just sets our text widget, it doesn't
2160 activate it (i.e., it doesn't cause us to try to open the file). */
2162 file_color_import_w = file_selection_new("Wireshark: Import Color Filters",
2163 GTK_WINDOW(top_level),
2164 FILE_SELECTION_OPEN);
2166 /* Container for each row of widgets */
2167 main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
2168 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
2169 file_selection_set_extra_widget(file_color_import_w, main_vb);
2170 gtk_widget_show(main_vb);
2172 cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
2173 gtk_box_pack_start(GTK_BOX(main_vb), cfglobal_but, FALSE, FALSE, 0);
2174 g_signal_connect(cfglobal_but, "clicked",
2175 G_CALLBACK(color_global_cb), file_color_import_w);
2176 gtk_widget_show(cfglobal_but);
2179 * Loop until the user either selects a file or gives up.
2182 cf_name = file_selection_run(file_color_import_w);
2183 if (cf_name == NULL) {
2184 /* User cancelled or closed the dialog. */
2188 /* Try to open the color filter file. */
2189 if (!color_filters_import(cf_name, color_filters)) {
2190 /* We couldn't open it; don't dismiss the open dialog box,
2191 just leave it around so that the user can, after they
2192 dismiss the alert box popped up for the open error,
2198 /* We've crossed the Rubicon; get rid of the file selection box. */
2199 window_destroy(GTK_WIDGET(file_color_import_w));
2201 /* Save the name of the containing directory specified in the path name,
2202 if any; we can write over cf_name, which is a good thing, given that
2203 "get_dirname()" does write over its argument. */
2204 s = get_dirname(cf_name);
2205 set_last_open_dir(s);
2210 #endif /* USE_WIN32_FILE_DIALOGS */
2214 * Set the "Export only selected filters" toggle button as appropriate for
2215 * the current output file type and count of selected filters.
2217 * Called when the "Export" dialog box is created and when the selected
2221 color_set_export_selected_sensitive(GtkWidget * cfselect_cb)
2223 /* We can request that only the selected filters be saved only if
2224 there *are* selected filters. */
2225 if (color_selected_count() != 0)
2226 gtk_widget_set_sensitive(cfselect_cb, TRUE);
2228 /* Force the "Export only selected filters" toggle to "false", turn
2229 off the flag it controls. */
2230 color_selected = FALSE;
2231 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
2232 gtk_widget_set_sensitive(cfselect_cb, FALSE);
2237 color_toggle_selected_cb(GtkWidget *widget, gpointer data _U_)
2239 color_selected = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
2243 file_color_export_cmd_cb(GtkWidget *w _U_, gpointer filter_list)
2245 #ifdef USE_WIN32_FILE_DIALOGS
2246 win32_export_color_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), filter_list);
2247 #else /* USE_WIN32_FILE_DIALOGS */
2248 GtkWidget *file_color_export_w;
2249 GtkWidget *main_vb, *cfglobal_but;
2250 GtkWidget *cfselect_cb;
2254 color_selected = FALSE;
2256 file_color_export_w = file_selection_new("Wireshark: Export Color Filters",
2257 GTK_WINDOW(top_level),
2258 FILE_SELECTION_SAVE);
2260 /* Container for each row of widgets */
2261 main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
2262 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
2263 file_selection_set_extra_widget(file_color_export_w, main_vb);
2264 gtk_widget_show(main_vb);
2266 cfselect_cb = gtk_check_button_new_with_label("Export only selected filters");
2267 gtk_box_pack_start(GTK_BOX(main_vb), cfselect_cb, FALSE, FALSE, 0);
2268 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
2269 g_signal_connect(cfselect_cb, "toggled",
2270 G_CALLBACK(color_toggle_selected_cb), NULL);
2271 gtk_widget_show(cfselect_cb);
2272 color_set_export_selected_sensitive(cfselect_cb);
2274 cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
2275 gtk_box_pack_start(GTK_BOX(main_vb), cfglobal_but, FALSE, FALSE, 0);
2276 g_signal_connect(cfglobal_but, "clicked",
2277 G_CALLBACK(color_global_cb), file_color_export_w);
2278 gtk_widget_show(cfglobal_but);
2281 * Loop until the user either selects a file or gives up.
2284 cf_name = file_selection_run(file_color_export_w);
2285 if (cf_name == NULL) {
2286 /* User cancelled or closed the dialog. */
2291 /* If the file exists and it's user-immutable or not writable,
2292 ask the user whether they want to override that. */
2293 if (!file_target_unwritable_ui(file_color_export_w, cf_name)) {
2294 /* They don't. Let them try another file name or cancel. */
2300 /* Write out the filters (all, or only the ones that are currently
2301 displayed or selected) to the file with the specified name. */
2302 if (!color_filters_export(cf_name, (GSList *)filter_list, color_selected)) {
2303 /* The write failed; don't dismiss the open dialog box,
2304 just leave it around so that the user can, after they
2305 dismiss the alert box popped up for the error, try again. */
2310 /* The write succeeded; get rid of the file selection box. */
2311 window_destroy(GTK_WIDGET(file_color_export_w));
2313 /* Save the directory name for future file dialogs. */
2314 dirname = get_dirname(cf_name); /* Overwrites cf_name */
2315 set_last_open_dir(dirname);
2318 #endif /* USE_WIN32_FILE_DIALOGS */
2327 * indent-tabs-mode: nil
2330 * ex: set shiftwidth=2 tabstop=8 expandtab:
2331 * :indentSize=2:tabSize=8:noTabs=true: