2 * Dialog boxes for handling files
4 * $Id: file_dlg.c,v 1.47 2002/01/13 20:35:11 guy Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
41 #include <epan/filesystem.h>
44 #include "gtkglobals.h"
47 #include "filter_prefs.h"
49 #include "simple_dialog.h"
52 #include "dlg_utils.h"
55 static void file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs);
56 static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
57 static void select_file_type_cb(GtkWidget *w, gpointer data);
58 static void file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs);
59 static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
61 #define E_FILE_M_RESOLVE_KEY "file_dlg_mac_resolve_key"
62 #define E_FILE_N_RESOLVE_KEY "file_dlg_network_resolve_key"
63 #define E_FILE_T_RESOLVE_KEY "file_dlg_transport_resolve_key"
66 * Keep a static pointer to the current "Open Capture File" window, if
67 * any, so that if somebody tries to do "File:Open" while there's already
68 * an "Open Capture File" window up, we just pop up the existing one,
69 * rather than creating a new one.
71 static GtkWidget *file_open_w;
75 file_open_cmd_cb(GtkWidget *w, gpointer data)
77 GtkWidget *main_vb, *filter_hbox, *filter_bt, *filter_te,
78 *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
79 GtkAccelGroup *accel_group;
80 /* No Apply button, and "OK" just sets our text widget, it doesn't
81 activate it (i.e., it doesn't cause us to try to open the file). */
82 static construct_args_t args = {
83 "Ethereal: Read Filter",
88 if (file_open_w != NULL) {
89 /* There's already an "Open Capture File" dialog box; reactivate it. */
90 reactivate_window(file_open_w);
94 file_open_w = gtk_file_selection_new ("Ethereal: Open Capture File");
95 gtk_signal_connect(GTK_OBJECT(file_open_w), "destroy",
96 GTK_SIGNAL_FUNC(file_open_destroy_cb), NULL);
98 /* Accelerator group for the accelerators (or, as they're called in
99 Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
100 Ctrl+<key> is an accelerator). */
101 accel_group = gtk_accel_group_new();
102 gtk_window_add_accel_group(GTK_WINDOW(file_open_w), accel_group);
104 /* If we've opened a file, start out by showing the files in the directory
105 in which that file resided. */
107 gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_open_w), last_open_dir);
109 /* Container for each row of widgets */
110 main_vb = gtk_vbox_new(FALSE, 3);
111 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
112 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_open_w)->action_area),
113 main_vb, FALSE, FALSE, 0);
114 gtk_widget_show(main_vb);
116 filter_hbox = gtk_hbox_new(FALSE, 1);
117 gtk_container_border_width(GTK_CONTAINER(filter_hbox), 0);
118 gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
119 gtk_widget_show(filter_hbox);
121 filter_bt = gtk_button_new_with_label("Filter:");
122 gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
123 GTK_SIGNAL_FUNC(display_filter_construct_cb), &args);
124 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
125 gtk_widget_show(filter_bt);
127 filter_te = gtk_entry_new();
128 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
129 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
130 gtk_widget_show(filter_te);
132 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
133 E_RFILTER_TE_KEY, filter_te);
135 m_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
136 "Enable _MAC name resolution", accel_group);
137 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(m_resolv_cb),
138 g_resolv_flags & RESOLV_MAC);
139 gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
140 gtk_widget_show(m_resolv_cb);
141 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
142 E_FILE_M_RESOLVE_KEY, m_resolv_cb);
144 n_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
145 "Enable _network name resolution", accel_group);
146 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(n_resolv_cb),
147 g_resolv_flags & RESOLV_NETWORK);
148 gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
149 gtk_widget_show(n_resolv_cb);
150 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
151 E_FILE_N_RESOLVE_KEY, n_resolv_cb);
153 t_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
154 "Enable _transport name resolution", accel_group);
155 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(t_resolv_cb),
156 g_resolv_flags & RESOLV_TRANSPORT);
157 gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
158 gtk_widget_show(t_resolv_cb);
159 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
160 E_FILE_T_RESOLVE_KEY, t_resolv_cb);
162 /* Connect the ok_button to file_open_ok_cb function and pass along a
163 pointer to the file selection box widget */
164 gtk_signal_connect(GTK_OBJECT (GTK_FILE_SELECTION(file_open_w)->ok_button),
165 "clicked", (GtkSignalFunc) file_open_ok_cb, file_open_w);
167 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
168 E_DFILTER_TE_KEY, gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY));
170 /* Connect the cancel_button to destroy the widget */
171 gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
172 (file_open_w)->cancel_button), "clicked", (GtkSignalFunc)
173 gtk_widget_destroy, GTK_OBJECT (file_open_w));
175 /* Catch the "key_press_event" signal in the window, so that we can catch
176 the ESC key being pressed and act as if the "Cancel" button had
178 dlg_set_cancel(file_open_w, GTK_FILE_SELECTION(file_open_w)->cancel_button);
180 gtk_widget_show(file_open_w);
184 file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
185 gchar *cf_name, *rfilter, *s;
186 GtkWidget *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
187 dfilter_t *rfcode = NULL;
190 cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
191 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_RFILTER_TE_KEY);
192 rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
193 if (!dfilter_compile(rfilter, &rfcode)) {
194 simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
198 /* Perhaps the user specified a directory instead of a file.
199 Check whether they did. */
200 if (test_for_directory(cf_name) == EISDIR) {
201 /* It's a directory - set the file selection box to display that
202 directory, don't try to open the directory as a capture file. */
203 set_last_open_dir(cf_name);
204 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
208 /* Try to open the capture file. */
209 if ((err = open_cap_file(cf_name, FALSE, &cfile)) != 0) {
210 /* We couldn't open it; don't dismiss the open dialog box,
211 just leave it around so that the user can, after they
212 dismiss the alert box popped up for the open error,
215 dfilter_free(rfcode);
220 /* Attach the new read filter to "cf" ("open_cap_file()" succeeded, so
221 it closed the previous capture file, and thus destroyed any
222 previous read filter attached to "cf"). */
223 cfile.rfcode = rfcode;
225 /* Set the global resolving variable */
227 m_resolv_cb = gtk_object_get_data(GTK_OBJECT(w), E_FILE_M_RESOLVE_KEY);
228 g_resolv_flags |= GTK_TOGGLE_BUTTON (m_resolv_cb)->active ? RESOLV_MAC : RESOLV_NONE;
229 n_resolv_cb = gtk_object_get_data(GTK_OBJECT(w), E_FILE_N_RESOLVE_KEY);
230 g_resolv_flags |= GTK_TOGGLE_BUTTON (n_resolv_cb)->active ? RESOLV_NETWORK : RESOLV_NONE;
231 t_resolv_cb = gtk_object_get_data(GTK_OBJECT(w), E_FILE_T_RESOLVE_KEY);
232 g_resolv_flags |= GTK_TOGGLE_BUTTON (t_resolv_cb)->active ? RESOLV_TRANSPORT : RESOLV_NONE;
234 /* We've crossed the Rubicon; get rid of the file selection box. */
235 gtk_widget_hide(GTK_WIDGET (fs));
236 gtk_widget_destroy(GTK_WIDGET (fs));
238 switch (read_cap_file(&cfile, &err)) {
242 /* Just because we got an error, that doesn't mean we were unable
243 to read any of the file; we handle what we could get from the
248 /* The user bailed out of re-reading the capture file; the
249 capture file has been closed - just free the capture file name
250 string and return (without changing the last containing
256 /* Save the name of the containing directory specified in the path name,
257 if any; we can write over cf_name, which is a good thing, given that
258 "get_dirname()" does write over its argument. */
259 s = get_dirname(cf_name);
260 set_last_open_dir(s);
266 file_open_destroy_cb(GtkWidget *win, gpointer user_data)
268 GtkWidget *file_open_filter_w;
270 /* Is there a filter edit/selection dialog associated with this
271 Open Capture File dialog? */
272 file_open_filter_w = gtk_object_get_data(GTK_OBJECT(win), E_FILT_DIALOG_PTR_KEY);
274 if (file_open_filter_w != NULL) {
275 /* Yes. Destroy it. */
276 gtk_widget_destroy(file_open_filter_w);
279 /* Note that we no longer have a "Open Capture File" dialog box. */
285 file_close_cmd_cb(GtkWidget *widget, gpointer data) {
286 close_cap_file(&cfile);
290 file_save_cmd_cb(GtkWidget *w, gpointer data) {
291 /* If the file's already been saved, do nothing. */
292 if (cfile.user_saved)
295 /* Do a "Save As". */
296 file_save_as_cmd_cb(w, data);
299 /* XXX - can we make these not be static? */
300 static gboolean filtered;
301 static gboolean marked;
303 static GtkWidget *filter_cb;
304 static GtkWidget *mark_cb;
305 static GtkWidget *ft_om;
308 can_save_with_wiretap(int ft)
310 /* To save a file with Wiretap, Wiretap has to handle that format,
311 and its code to handle that format must be able to write a file
312 with this file's encapsulation type. */
313 return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cfile.lnk_t);
316 /* Generate a list of the file types we can save this file as.
318 "filetype" is the type it has now.
320 "encap" is the encapsulation for its packets (which could be
321 "unknown" or "per-packet").
323 "filtered" is TRUE if we're to save only the packets that passed
324 the display filter (in which case we have to save it using Wiretap)
325 and FALSE if we're to save the entire file (in which case, if we're
326 saving it in the type it has already, we can just copy it).
328 "marked" is TRUE if we have to save only the marked packets,
329 the same remark as "filtered" applies.
332 set_file_type_list(GtkWidget *option_menu)
334 GtkWidget *ft_menu, *ft_menu_item;
337 guint item_to_select;
339 /* Default to the first supported file type, if the file's current
340 type isn't supported. */
343 ft_menu = gtk_menu_new();
345 /* Check all file types. */
347 for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
348 if (filtered || marked || ft != cfile.cd_t) {
349 /* Filtered, marked or a different file type. We have to use Wiretap. */
350 if (!can_save_with_wiretap(ft))
351 continue; /* We can't. */
354 /* OK, we can write it out in this type. */
355 ft_menu_item = gtk_menu_item_new_with_label(wtap_file_type_string(ft));
356 if (ft == filetype) {
357 /* Default to the same format as the file, if it's supported. */
358 item_to_select = index;
360 gtk_signal_connect(GTK_OBJECT(ft_menu_item), "activate",
361 GTK_SIGNAL_FUNC(select_file_type_cb), (gpointer)ft);
362 gtk_menu_append(GTK_MENU(ft_menu), ft_menu_item);
363 gtk_widget_show(ft_menu_item);
367 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), ft_menu);
368 gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), item_to_select);
372 select_file_type_cb(GtkWidget *w, gpointer data)
374 int new_filetype = (int)data;
376 if (filetype != new_filetype) {
377 /* We can select only the filtered or marked packets to be saved if we can
378 use Wiretap to save the file. */
379 gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(new_filetype));
380 filetype = new_filetype;
381 file_set_save_marked_sensitive();
386 toggle_filtered_cb(GtkWidget *widget, gpointer data)
388 gboolean new_filtered;
390 new_filtered = GTK_TOGGLE_BUTTON (widget)->active;
392 if (filtered != new_filtered) {
393 /* They changed the state of the "filtered" button. */
394 filtered = new_filtered;
395 set_file_type_list(ft_om);
400 toggle_marked_cb(GtkWidget *widget, gpointer data)
404 new_marked = GTK_TOGGLE_BUTTON (widget)->active;
406 if (marked != new_marked) {
407 /* They changed the state of the "marked" button. */
409 set_file_type_list(ft_om);
414 * Keep a static pointer to the current "Save Capture File As" window, if
415 * any, so that if somebody tries to do "File:Save" or "File:Save As"
416 * while there's already a "Save Capture File As" window up, we just pop
417 * up the existing one, rather than creating a new one.
419 static GtkWidget *file_save_as_w;
422 file_save_as_cmd_cb(GtkWidget *w, gpointer data)
424 GtkWidget *ok_bt, *main_vb, *ft_hb, *ft_lb;
426 if (file_save_as_w != NULL) {
427 /* There's already an "Save Capture File As" dialog box; reactivate it. */
428 reactivate_window(file_save_as_w);
432 /* Default to saving all packets, in the file's current format. */
435 filetype = cfile.cd_t;
437 file_save_as_w = gtk_file_selection_new ("Ethereal: Save Capture File As");
438 gtk_signal_connect(GTK_OBJECT(file_save_as_w), "destroy",
439 GTK_SIGNAL_FUNC(file_save_as_destroy_cb), NULL);
441 /* If we've opened a file, start out by showing the files in the directory
442 in which that file resided. */
444 gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_save_as_w), last_open_dir);
446 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
447 pointer to the file selection box widget */
448 ok_bt = GTK_FILE_SELECTION (file_save_as_w)->ok_button;
449 gtk_signal_connect(GTK_OBJECT (ok_bt), "clicked",
450 (GtkSignalFunc) file_save_as_ok_cb, file_save_as_w);
452 /* Container for each row of widgets */
453 main_vb = gtk_vbox_new(FALSE, 3);
454 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
455 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_save_as_w)->action_area),
456 main_vb, FALSE, FALSE, 0);
457 gtk_widget_show(main_vb);
460 * XXX - should this be sensitive only if the current display filter
461 * has rejected some packets, so that not all packets are currently
462 * being displayed, and if it has accepted some packets, so that some
463 * packets are currently being displayed?
465 * I'd say "no", as that complicates the UI code, and as one could,
466 * I guess, argue that the user may want to "save all the displayed
467 * packets" even if there aren't any, i.e. save an empty file.
469 filter_cb = gtk_check_button_new_with_label("Save only packets currently being displayed");
470 gtk_container_add(GTK_CONTAINER(main_vb), filter_cb);
471 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
472 gtk_signal_connect(GTK_OBJECT(filter_cb), "toggled",
473 GTK_SIGNAL_FUNC(toggle_filtered_cb), NULL);
474 gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(filetype));
475 gtk_widget_show(filter_cb);
478 * The argument above could, I guess, be applied to the marked packets,
479 * except that you can't easily tell whether there are any marked
480 * packets, so I could imagine users doing "Save only marked packets"
481 * when there aren't any marked packets, not knowing that they'd
482 * failed to mark them, so I'm more inclined to have the "Save only
483 * marked packets" toggle button enabled only if there are marked
486 mark_cb = gtk_check_button_new_with_label("Save only marked packets");
487 gtk_container_add(GTK_CONTAINER(main_vb), mark_cb);
489 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(mark_cb), FALSE);
490 gtk_signal_connect(GTK_OBJECT(mark_cb), "toggled",
491 GTK_SIGNAL_FUNC(toggle_marked_cb), NULL);
492 gtk_widget_show(mark_cb);
495 ft_hb = gtk_hbox_new(FALSE, 3);
496 gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
497 gtk_widget_show(ft_hb);
499 ft_lb = gtk_label_new("File type:");
500 gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
501 gtk_widget_show(ft_lb);
503 ft_om = gtk_option_menu_new();
505 /* Generate the list of file types we can save. */
506 set_file_type_list(ft_om);
507 gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
508 gtk_widget_show(ft_om);
511 * Set the sensitivity of the "Save only marked packets" toggle
514 * This has to be done after we create the file type menu option,
515 * as the routine that sets it also sets that menu.
517 file_set_save_marked_sensitive();
519 /* Connect the cancel_button to destroy the widget */
520 gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
521 (file_save_as_w)->cancel_button), "clicked", (GtkSignalFunc)
522 gtk_widget_destroy, GTK_OBJECT (file_save_as_w));
524 /* Catch the "key_press_event" signal in the window, so that we can catch
525 the ESC key being pressed and act as if the "Cancel" button had
527 dlg_set_cancel(file_save_as_w, GTK_FILE_SELECTION(file_save_as_w)->cancel_button);
529 gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_save_as_w), "");
531 gtk_widget_show(file_save_as_w);
535 * Set the "Save only marked packets" toggle button as appropriate for
536 * the current output file type and count of marked packets.
538 * Called when the "Save As..." dialog box is created and when either
539 * the file type or the marked count changes.
542 file_set_save_marked_sensitive(void)
544 if (file_save_as_w == NULL) {
545 /* We don't currently have a "Save As..." dialog box up. */
549 /* We can request that only the marked packets be saved only if we
550 can use Wiretap to save the file and if there *are* marked packets. */
551 if (can_save_with_wiretap(filetype) && cfile.marked_count != 0)
552 gtk_widget_set_sensitive(mark_cb, TRUE);
554 /* Force the "Save only marked packets" toggle to "false", turn
555 off the flag it controls, and update the list of types we can
558 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(mark_cb), FALSE);
559 set_file_type_list(ft_om);
560 gtk_widget_set_sensitive(mark_cb, FALSE);
565 file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
568 cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
569 gtk_widget_hide(GTK_WIDGET (fs));
570 gtk_widget_destroy(GTK_WIDGET (fs));
572 /* Write out the packets (all, or only the ones that are currently
573 displayed or marked) to the file with the specified name. */
574 save_cap_file(cf_name, &cfile, filtered, marked, filetype);
576 /* If "save_cap_file()" saved the file name we handed it, it saved
577 a copy, so we should free up our copy. */
582 file_save_as_destroy_cb(GtkWidget *win, gpointer user_data)
584 /* Note that we no longer have a "Save Capture File As" dialog box. */
585 file_save_as_w = NULL;
588 /* Reload a file using the current read and display filters */
590 file_reload_cmd_cb(GtkWidget *w, gpointer data) {
591 /*GtkWidget *filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);*/
592 GtkWidget *filter_te;
594 gboolean is_tempfile;
597 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
600 g_free(cfile.dfilter);
601 cfile.dfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
603 /* If the file could be opened, "open_cap_file()" calls "close_cap_file()"
604 to get rid of state for the old capture file before filling in state
605 for the new capture file. "close_cap_file()" will remove the file if
606 it's a temporary file; we don't want that to happen (for one thing,
607 it'd prevent subsequent reopens from working). Remember whether it's
608 a temporary file, mark it as not being a temporary file, and then
609 reopen it as the type of file it was.
611 Also, "close_cap_file()" will free "cfile.filename", so we must make
612 a copy of it first. */
613 filename = g_strdup(cfile.filename);
614 is_tempfile = cfile.is_tempfile;
615 cfile.is_tempfile = FALSE;
616 if (open_cap_file(filename, is_tempfile, &cfile) == 0) {
617 switch (read_cap_file(&cfile, &err)) {
621 /* Just because we got an error, that doesn't mean we were unable
622 to read any of the file; we handle what we could get from the
627 /* The user bailed out of re-reading the capture file; the
628 capture file has been closed - just free the capture file name
629 string and return (without changing the last containing
635 /* The open failed, so "cfile.is_tempfile" wasn't set to "is_tempfile".
636 Instead, the file was left open, so we should restore "cfile.is_tempfile"
639 XXX - change the menu? Presumably "open_cap_file()" will do that;
640 make sure it does! */
641 cfile.is_tempfile = is_tempfile;
643 /* "open_cap_file()" made a copy of the file name we handed it, so
644 we should free up our copy. */