2 * Dialog boxes for handling files
4 * $Id: file_dlg.c,v 1.3 2002/09/05 18:48:51 jmayer 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.
31 #include <epan/filesystem.h>
34 #include "gtkglobals.h"
35 #include <epan/resolv.h>
37 #include "filter_prefs.h"
39 #include "simple_dialog.h"
42 #include "dlg_utils.h"
45 static void file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs);
46 static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
47 static void select_file_type_cb(GtkWidget *w, gpointer data);
48 static void file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs);
49 static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
51 #define E_FILE_M_RESOLVE_KEY "file_dlg_mac_resolve_key"
52 #define E_FILE_N_RESOLVE_KEY "file_dlg_network_resolve_key"
53 #define E_FILE_T_RESOLVE_KEY "file_dlg_transport_resolve_key"
56 * Keep a static pointer to the current "Open Capture File" window, if
57 * any, so that if somebody tries to do "File:Open" while there's already
58 * an "Open Capture File" window up, we just pop up the existing one,
59 * rather than creating a new one.
61 static GtkWidget *file_open_w;
65 file_open_cmd_cb(GtkWidget *w, gpointer data _U_)
67 GtkWidget *main_vb, *filter_hbox, *filter_bt, *filter_te,
68 *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
69 /* No Apply button, and "OK" just sets our text widget, it doesn't
70 activate it (i.e., it doesn't cause us to try to open the file). */
71 static construct_args_t args = {
72 "Ethereal: Read Filter",
77 if (file_open_w != NULL) {
78 /* There's already an "Open Capture File" dialog box; reactivate it. */
79 reactivate_window(file_open_w);
83 file_open_w = gtk_file_selection_new ("Ethereal: Open Capture File");
84 g_signal_connect(G_OBJECT(file_open_w), "destroy",
85 G_CALLBACK(file_open_destroy_cb), NULL);
87 /* If we've opened a file, start out by showing the files in the directory
88 in which that file resided. */
90 gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_open_w), last_open_dir);
92 /* Container for each row of widgets */
93 main_vb = gtk_vbox_new(FALSE, 3);
94 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
95 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_open_w)->action_area),
96 main_vb, FALSE, FALSE, 0);
97 gtk_widget_show(main_vb);
99 filter_hbox = gtk_hbox_new(FALSE, 1);
100 gtk_container_border_width(GTK_CONTAINER(filter_hbox), 0);
101 gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
102 gtk_widget_show(filter_hbox);
104 filter_bt = gtk_button_new_with_label("Filter:");
105 g_signal_connect(G_OBJECT(filter_bt), "clicked",
106 G_CALLBACK(display_filter_construct_cb), &args);
107 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
108 gtk_widget_show(filter_bt);
110 filter_te = gtk_entry_new();
111 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
112 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
113 gtk_widget_show(filter_te);
115 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
116 E_RFILTER_TE_KEY, filter_te);
118 m_resolv_cb = gtk_check_button_new_with_mnemonic(
119 "Enable _MAC name resolution");
120 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(m_resolv_cb),
121 g_resolv_flags & RESOLV_MAC);
122 gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
123 gtk_widget_show(m_resolv_cb);
124 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
125 E_FILE_M_RESOLVE_KEY, m_resolv_cb);
127 n_resolv_cb = gtk_check_button_new_with_mnemonic(
128 "Enable _network name resolution");
129 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(n_resolv_cb),
130 g_resolv_flags & RESOLV_NETWORK);
131 gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
132 gtk_widget_show(n_resolv_cb);
133 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
134 E_FILE_N_RESOLVE_KEY, n_resolv_cb);
136 t_resolv_cb = gtk_check_button_new_with_mnemonic(
137 "Enable _transport name resolution");
138 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(t_resolv_cb),
139 g_resolv_flags & RESOLV_TRANSPORT);
140 gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
141 gtk_widget_show(t_resolv_cb);
142 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
143 E_FILE_T_RESOLVE_KEY, t_resolv_cb);
145 /* Connect the ok_button to file_open_ok_cb function and pass along a
146 pointer to the file selection box widget */
147 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
148 "clicked", G_CALLBACK(file_open_ok_cb), file_open_w);
150 gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
151 E_DFILTER_TE_KEY, gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY));
153 /* Connect the cancel_button to destroy the widget */
154 gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->cancel_button),
155 "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
156 GTK_OBJECT(file_open_w));
158 /* Catch the "key_press_event" signal in the window, so that we can catch
159 the ESC key being pressed and act as if the "Cancel" button had
161 dlg_set_cancel(file_open_w, GTK_FILE_SELECTION(file_open_w)->cancel_button);
163 gtk_widget_show(file_open_w);
167 file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
169 GtkWidget *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
170 dfilter_t *rfcode = NULL;
172 const gchar *rfilter;
174 cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
175 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_RFILTER_TE_KEY);
176 rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
177 if (!dfilter_compile(rfilter, &rfcode)) {
178 simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
182 /* Perhaps the user specified a directory instead of a file.
183 Check whether they did. */
184 if (test_for_directory(cf_name) == EISDIR) {
185 /* It's a directory - set the file selection box to display that
186 directory, don't try to open the directory as a capture file. */
187 set_last_open_dir(cf_name);
188 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
192 /* Try to open the capture file. */
193 if ((err = open_cap_file(cf_name, FALSE, &cfile)) != 0) {
194 /* We couldn't open it; don't dismiss the open dialog box,
195 just leave it around so that the user can, after they
196 dismiss the alert box popped up for the open error,
199 dfilter_free(rfcode);
204 /* Attach the new read filter to "cf" ("open_cap_file()" succeeded, so
205 it closed the previous capture file, and thus destroyed any
206 previous read filter attached to "cf"). */
207 cfile.rfcode = rfcode;
209 /* Set the global resolving variable */
211 m_resolv_cb = gtk_object_get_data(GTK_OBJECT(w), E_FILE_M_RESOLVE_KEY);
212 g_resolv_flags |= GTK_TOGGLE_BUTTON (m_resolv_cb)->active ? RESOLV_MAC : RESOLV_NONE;
213 n_resolv_cb = gtk_object_get_data(GTK_OBJECT(w), E_FILE_N_RESOLVE_KEY);
214 g_resolv_flags |= GTK_TOGGLE_BUTTON (n_resolv_cb)->active ? RESOLV_NETWORK : RESOLV_NONE;
215 t_resolv_cb = gtk_object_get_data(GTK_OBJECT(w), E_FILE_T_RESOLVE_KEY);
216 g_resolv_flags |= GTK_TOGGLE_BUTTON (t_resolv_cb)->active ? RESOLV_TRANSPORT : RESOLV_NONE;
218 /* We've crossed the Rubicon; get rid of the file selection box. */
219 gtk_widget_hide(GTK_WIDGET (fs));
220 gtk_widget_destroy(GTK_WIDGET (fs));
222 switch (read_cap_file(&cfile, &err)) {
226 /* Just because we got an error, that doesn't mean we were unable
227 to read any of the file; we handle what we could get from the
232 /* The user bailed out of re-reading the capture file; the
233 capture file has been closed - just free the capture file name
234 string and return (without changing the last containing
240 /* Save the name of the containing directory specified in the path name,
241 if any; we can write over cf_name, which is a good thing, given that
242 "get_dirname()" does write over its argument. */
243 s = get_dirname(cf_name);
244 set_last_open_dir(s);
250 file_open_destroy_cb(GtkWidget *win, gpointer user_data _U_)
252 GtkWidget *file_open_filter_w;
254 /* Is there a filter edit/selection dialog associated with this
255 Open Capture File dialog? */
256 file_open_filter_w = gtk_object_get_data(GTK_OBJECT(win), E_FILT_DIALOG_PTR_KEY);
258 if (file_open_filter_w != NULL) {
259 /* Yes. Destroy it. */
260 gtk_widget_destroy(file_open_filter_w);
263 /* Note that we no longer have a "Open Capture File" dialog box. */
269 file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
270 close_cap_file(&cfile);
274 file_save_cmd_cb(GtkWidget *w, gpointer data) {
275 /* If the file's already been saved, do nothing. */
276 if (cfile.user_saved)
279 /* Do a "Save As". */
280 file_save_as_cmd_cb(w, data);
283 /* XXX - can we make these not be static? */
284 static gboolean filtered;
285 static gboolean marked;
287 static GtkWidget *filter_cb;
288 static GtkWidget *mark_cb;
289 static GtkWidget *ft_om;
292 can_save_with_wiretap(int ft)
294 /* To save a file with Wiretap, Wiretap has to handle that format,
295 and its code to handle that format must be able to write a file
296 with this file's encapsulation type. */
297 return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cfile.lnk_t);
300 /* Generate a list of the file types we can save this file as.
302 "filetype" is the type it has now.
304 "encap" is the encapsulation for its packets (which could be
305 "unknown" or "per-packet").
307 "filtered" is TRUE if we're to save only the packets that passed
308 the display filter (in which case we have to save it using Wiretap)
309 and FALSE if we're to save the entire file (in which case, if we're
310 saving it in the type it has already, we can just copy it).
312 "marked" is TRUE if we have to save only the marked packets,
313 the same remark as "filtered" applies.
316 set_file_type_list(GtkWidget *option_menu)
318 GtkWidget *ft_menu, *ft_menu_item;
321 guint item_to_select;
323 /* Default to the first supported file type, if the file's current
324 type isn't supported. */
327 ft_menu = gtk_menu_new();
329 /* Check all file types. */
331 for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
332 if (filtered || marked || ft != cfile.cd_t) {
333 /* Filtered, marked or a different file type. We have to use Wiretap. */
334 if (!can_save_with_wiretap(ft))
335 continue; /* We can't. */
338 /* OK, we can write it out in this type. */
339 ft_menu_item = gtk_menu_item_new_with_label(wtap_file_type_string(ft));
340 if (ft == filetype) {
341 /* Default to the same format as the file, if it's supported. */
342 item_to_select = index;
344 g_signal_connect(G_OBJECT(ft_menu_item), "activate",
345 G_CALLBACK(select_file_type_cb), (gpointer)ft);
346 gtk_menu_append(GTK_MENU(ft_menu), ft_menu_item);
347 gtk_widget_show(ft_menu_item);
351 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), ft_menu);
352 gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), item_to_select);
356 select_file_type_cb(GtkWidget *w _U_, gpointer data)
358 int new_filetype = (int)data;
360 if (filetype != new_filetype) {
361 /* We can select only the filtered or marked packets to be saved if we can
362 use Wiretap to save the file. */
363 gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(new_filetype));
364 filetype = new_filetype;
365 file_set_save_marked_sensitive();
370 toggle_filtered_cb(GtkWidget *widget, gpointer data _U_)
372 gboolean new_filtered;
374 new_filtered = GTK_TOGGLE_BUTTON (widget)->active;
376 if (filtered != new_filtered) {
377 /* They changed the state of the "filtered" button. */
378 filtered = new_filtered;
379 set_file_type_list(ft_om);
384 toggle_marked_cb(GtkWidget *widget, gpointer data _U_)
388 new_marked = GTK_TOGGLE_BUTTON (widget)->active;
390 if (marked != new_marked) {
391 /* They changed the state of the "marked" button. */
393 set_file_type_list(ft_om);
398 * Keep a static pointer to the current "Save Capture File As" window, if
399 * any, so that if somebody tries to do "File:Save" or "File:Save As"
400 * while there's already a "Save Capture File As" window up, we just pop
401 * up the existing one, rather than creating a new one.
403 static GtkWidget *file_save_as_w;
406 file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
408 GtkWidget *ok_bt, *main_vb, *ft_hb, *ft_lb;
410 if (file_save_as_w != NULL) {
411 /* There's already an "Save Capture File As" dialog box; reactivate it. */
412 reactivate_window(file_save_as_w);
416 /* Default to saving all packets, in the file's current format. */
419 filetype = cfile.cd_t;
421 file_save_as_w = gtk_file_selection_new ("Ethereal: Save Capture File As");
422 g_signal_connect(G_OBJECT(file_save_as_w), "destroy",
423 G_CALLBACK(file_save_as_destroy_cb), NULL);
425 /* If we've opened a file, start out by showing the files in the directory
426 in which that file resided. */
428 gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_save_as_w), last_open_dir);
430 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
431 pointer to the file selection box widget */
432 ok_bt = GTK_FILE_SELECTION (file_save_as_w)->ok_button;
433 g_signal_connect(G_OBJECT(ok_bt), "clicked",
434 G_CALLBACK(file_save_as_ok_cb), file_save_as_w);
436 /* Container for each row of widgets */
437 main_vb = gtk_vbox_new(FALSE, 3);
438 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
439 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_save_as_w)->action_area),
440 main_vb, FALSE, FALSE, 0);
441 gtk_widget_show(main_vb);
444 * XXX - should this be sensitive only if the current display filter
445 * has rejected some packets, so that not all packets are currently
446 * being displayed, and if it has accepted some packets, so that some
447 * packets are currently being displayed?
449 * I'd say "no", as that complicates the UI code, and as one could,
450 * I guess, argue that the user may want to "save all the displayed
451 * packets" even if there aren't any, i.e. save an empty file.
453 filter_cb = gtk_check_button_new_with_label("Save only packets currently being displayed");
454 gtk_container_add(GTK_CONTAINER(main_vb), filter_cb);
455 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
456 g_signal_connect(G_OBJECT(filter_cb), "toggled",
457 G_CALLBACK(toggle_filtered_cb), NULL);
458 gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(filetype));
459 gtk_widget_show(filter_cb);
462 * The argument above could, I guess, be applied to the marked packets,
463 * except that you can't easily tell whether there are any marked
464 * packets, so I could imagine users doing "Save only marked packets"
465 * when there aren't any marked packets, not knowing that they'd
466 * failed to mark them, so I'm more inclined to have the "Save only
467 * marked packets" toggle button enabled only if there are marked
470 mark_cb = gtk_check_button_new_with_label("Save only marked packets");
471 gtk_container_add(GTK_CONTAINER(main_vb), mark_cb);
473 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(mark_cb), FALSE);
474 g_signal_connect(G_OBJECT(mark_cb), "toggled",
475 G_CALLBACK(toggle_marked_cb), NULL);
476 gtk_widget_show(mark_cb);
479 ft_hb = gtk_hbox_new(FALSE, 3);
480 gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
481 gtk_widget_show(ft_hb);
483 ft_lb = gtk_label_new("File type:");
484 gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
485 gtk_widget_show(ft_lb);
487 ft_om = gtk_option_menu_new();
489 /* Generate the list of file types we can save. */
490 set_file_type_list(ft_om);
491 gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
492 gtk_widget_show(ft_om);
495 * Set the sensitivity of the "Save only marked packets" toggle
498 * This has to be done after we create the file type menu option,
499 * as the routine that sets it also sets that menu.
501 file_set_save_marked_sensitive();
503 /* Connect the cancel_button to destroy the widget */
504 gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(file_save_as_w)->cancel_button),
505 "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
506 GTK_OBJECT(file_save_as_w));
508 /* Catch the "key_press_event" signal in the window, so that we can catch
509 the ESC key being pressed and act as if the "Cancel" button had
511 dlg_set_cancel(file_save_as_w, GTK_FILE_SELECTION(file_save_as_w)->cancel_button);
513 gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_save_as_w), "");
515 gtk_widget_show(file_save_as_w);
519 * Set the "Save only marked packets" toggle button as appropriate for
520 * the current output file type and count of marked packets.
522 * Called when the "Save As..." dialog box is created and when either
523 * the file type or the marked count changes.
526 file_set_save_marked_sensitive(void)
528 if (file_save_as_w == NULL) {
529 /* We don't currently have a "Save As..." dialog box up. */
533 /* We can request that only the marked packets be saved only if we
534 can use Wiretap to save the file and if there *are* marked packets. */
535 if (can_save_with_wiretap(filetype) && cfile.marked_count != 0)
536 gtk_widget_set_sensitive(mark_cb, TRUE);
538 /* Force the "Save only marked packets" toggle to "false", turn
539 off the flag it controls, and update the list of types we can
542 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(mark_cb), FALSE);
543 set_file_type_list(ft_om);
544 gtk_widget_set_sensitive(mark_cb, FALSE);
549 file_save_as_ok_cb(GtkWidget *w _U_, GtkFileSelection *fs) {
552 cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
553 gtk_widget_hide(GTK_WIDGET (fs));
554 gtk_widget_destroy(GTK_WIDGET (fs));
556 /* Write out the packets (all, or only the ones that are currently
557 displayed or marked) to the file with the specified name. */
558 save_cap_file(cf_name, &cfile, filtered, marked, filetype);
560 /* If "save_cap_file()" saved the file name we handed it, it saved
561 a copy, so we should free up our copy. */
566 file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
568 /* Note that we no longer have a "Save Capture File As" dialog box. */
569 file_save_as_w = NULL;
572 /* Reload a file using the current read and display filters */
574 file_reload_cmd_cb(GtkWidget *w, gpointer data _U_) {
575 /*GtkWidget *filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);*/
576 GtkWidget *filter_te;
578 gboolean is_tempfile;
581 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
584 g_free(cfile.dfilter);
585 cfile.dfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
587 /* If the file could be opened, "open_cap_file()" calls "close_cap_file()"
588 to get rid of state for the old capture file before filling in state
589 for the new capture file. "close_cap_file()" will remove the file if
590 it's a temporary file; we don't want that to happen (for one thing,
591 it'd prevent subsequent reopens from working). Remember whether it's
592 a temporary file, mark it as not being a temporary file, and then
593 reopen it as the type of file it was.
595 Also, "close_cap_file()" will free "cfile.filename", so we must make
596 a copy of it first. */
597 filename = g_strdup(cfile.filename);
598 is_tempfile = cfile.is_tempfile;
599 cfile.is_tempfile = FALSE;
600 if (open_cap_file(filename, is_tempfile, &cfile) == 0) {
601 switch (read_cap_file(&cfile, &err)) {
605 /* Just because we got an error, that doesn't mean we were unable
606 to read any of the file; we handle what we could get from the
611 /* The user bailed out of re-reading the capture file; the
612 capture file has been closed - just free the capture file name
613 string and return (without changing the last containing
619 /* The open failed, so "cfile.is_tempfile" wasn't set to "is_tempfile".
620 Instead, the file was left open, so we should restore "cfile.is_tempfile"
623 XXX - change the menu? Presumably "open_cap_file()" will do that;
624 make sure it does! */
625 cfile.is_tempfile = is_tempfile;
627 /* "open_cap_file()" made a copy of the file name we handed it, so
628 we should free up our copy. */