Clean up indentation.
[metze/wireshark/wip.git] / ui / gtk / capture_file_dlg.c
1 /* capture_file_dlg.c
2  * Dialog boxes for handling capture files
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
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.
14  *
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.
19  *
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.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32
33 #include <string.h>
34
35 #include <gtk/gtk.h>
36
37 #include "packet-range.h"
38 #include <epan/filesystem.h>
39 #include <epan/addr_resolv.h>
40 #include <epan/prefs.h>
41
42 #include "../globals.h"
43 #include "../color.h"
44 #include "../color_filters.h"
45 #include "../merge.h"
46 #include "ui/util.h"
47 #include <wsutil/file_util.h>
48
49 #include "ui/alert_box.h"
50 #include "ui/last_open_dir.h"
51 #include "ui/recent.h"
52 #include "ui/simple_dialog.h"
53 #include "ui/ui_util.h"
54
55 #include "ui/gtk/gtkglobals.h"
56 #include "ui/gtk/keys.h"
57 #include "ui/gtk/filter_dlg.h"
58 #include "ui/gtk/gui_utils.h"
59 #include "ui/gtk/dlg_utils.h"
60 #include "ui/gtk/file_dlg.h"
61 #include "ui/gtk/capture_file_dlg.h"
62 #include "ui/gtk/drag_and_drop.h"
63 #include "ui/gtk/main.h"
64 #include "ui/gtk/color_dlg.h"
65 #include "ui/gtk/new_packet_list.h"
66 #ifdef HAVE_LIBPCAP
67 #include "ui/gtk/capture_dlg.h"
68 #endif
69 #include "ui/gtk/stock_icons.h"
70 #include "ui/gtk/range_utils.h"
71 #include "ui/gtk/filter_autocomplete.h"
72
73 #if _WIN32
74 #include <gdk/gdkwin32.h>
75 #include <windows.h>
76 #include "ui/win32/file_dlg_win32.h"
77 #endif
78
79
80 static void file_open_ok_cb(GtkWidget *w, gpointer fs);
81 static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
82 static void file_merge_ok_cb(GtkWidget *w, gpointer fs);
83 static void file_merge_destroy_cb(GtkWidget *win, gpointer user_data);
84 static void do_file_save(capture_file *cf, gboolean dont_reopen);
85 static void do_file_save_as(capture_file *cf);
86 static void file_save_as_cb(GtkWidget *fs);
87 static void file_save_as_select_file_type_cb(GtkWidget *w, gpointer data);
88 static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
89 static void file_export_specified_packets_cb(GtkWidget *w, gpointer fs);
90 static void file_export_specified_packets_select_file_type_cb(GtkWidget *w, gpointer data);
91 static void file_export_specified_packets_ok_cb(GtkWidget *w, gpointer fs);
92 static void file_export_specified_packets_destroy_cb(GtkWidget *win, gpointer user_data);
93 static void file_color_import_ok_cb(GtkWidget *w, gpointer filter_list);
94 static void file_color_import_destroy_cb(GtkWidget *win, gpointer user_data);
95 static void file_color_export_ok_cb(GtkWidget *w, gpointer filter_list);
96 static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
97 static void set_file_type_list(GtkWidget *combo_box, capture_file *cf);
98
99 #define E_FILE_TYPE_COMBO_BOX_KEY "file_type_combo_box"
100 #define E_COMPRESSED_CB_KEY       "compressed_cb"
101
102 #define E_FILE_M_RESOLVE_KEY      "file_dlg_mac_resolve_key"
103 #define E_FILE_N_RESOLVE_KEY      "file_dlg_network_resolve_key"
104 #define E_FILE_T_RESOLVE_KEY      "file_dlg_transport_resolve_key"
105
106 #define E_MERGE_PREPEND_KEY       "merge_dlg_prepend_key"
107 #define E_MERGE_CHRONO_KEY            "merge_dlg_chrono_key"
108 #define E_MERGE_APPEND_KEY            "merge_dlg_append_key"
109
110
111 #define PREVIEW_TABLE_KEY       "preview_table_key"
112 #define PREVIEW_FILENAME_KEY    "preview_filename_key"
113 #define PREVIEW_FORMAT_KEY      "preview_format_key"
114 #define PREVIEW_SIZE_KEY        "preview_size_key"
115 #define PREVIEW_ELAPSED_KEY     "preview_elapsed_key"
116 #define PREVIEW_PACKETS_KEY     "preview_packets_key"
117 #define PREVIEW_FIRST_KEY       "preview_first_key"
118
119
120 /*
121  * Keep a static pointer to the current "Save Capture File As" window, if
122  * any, so that if somebody tries to do "File:Save" or "File:Save As"
123  * while there's already a "Save Capture File As" window up, we just pop
124  * up the existing one, rather than creating a new one.
125  */
126 static GtkWidget *file_save_as_w;
127
128 /*
129  * Keep a static pointer to the current "Export Specified Packets" window, if
130  * any, so that if somebody tries to do "File:Export Specified Packets"
131  * while there's already a "Export Specified Packets" window up, we just pop
132  * up the existing one, rather than creating a new one.
133  */
134 static GtkWidget *file_export_specified_packets_w;
135
136 /* XXX - can we make these not be static? */
137 static packet_range_t  range;
138 static gboolean        color_selected;
139 static GtkWidget      *range_tb;
140
141 #define PREVIEW_STR_MAX         200
142
143
144 /* set a new filename for the preview widget */
145 static wtap *
146 preview_set_filename(GtkWidget *prev, const gchar *cf_name)
147 {
148     GtkWidget  *label;
149     gchar      *display_basename;
150     wtap       *wth;
151     int         err = 0;
152     gchar      *err_info;
153     gchar       string_buff[PREVIEW_STR_MAX];
154     gint64      filesize;
155
156
157     /* init preview labels */
158     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
159     gtk_label_set_text(GTK_LABEL(label), "-");
160     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
161     gtk_label_set_text(GTK_LABEL(label), "-");
162     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
163     gtk_label_set_text(GTK_LABEL(label), "-");
164     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
165     gtk_label_set_text(GTK_LABEL(label), "-");
166     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
167     gtk_label_set_text(GTK_LABEL(label), "-");
168     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
169     gtk_label_set_text(GTK_LABEL(label), "-");
170
171     if(!cf_name) {
172         return NULL;
173     }
174
175     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
176     display_basename = g_filename_display_basename(cf_name);
177     gtk_label_set_text(GTK_LABEL(label), display_basename);
178     g_free(display_basename);
179
180     if (test_for_directory(cf_name) == EISDIR) {
181         label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
182         gtk_label_set_text(GTK_LABEL(label), "directory");
183         return NULL;
184     }
185
186     wth = wtap_open_offline(cf_name, &err, &err_info, TRUE);
187     if (wth == NULL) {
188         label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
189         if(err == WTAP_ERR_FILE_UNKNOWN_FORMAT) {
190             gtk_label_set_text(GTK_LABEL(label), "unknown file format");
191         } else {
192             gtk_label_set_text(GTK_LABEL(label), "error opening file");
193         }
194         return NULL;
195     }
196
197     /* Find the size of the file. */
198     filesize = wtap_file_size(wth, &err);
199     if (filesize == -1) {
200         gtk_label_set_text(GTK_LABEL(label), "error getting file size");
201         wtap_close(wth);
202         return NULL;
203     }
204     g_snprintf(string_buff, PREVIEW_STR_MAX, "%" G_GINT64_MODIFIER "d bytes", filesize);
205     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
206     gtk_label_set_text(GTK_LABEL(label), string_buff);
207
208     /* type */
209     g_strlcpy(string_buff, wtap_file_type_string(wtap_file_type(wth)), PREVIEW_STR_MAX);
210     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
211     gtk_label_set_text(GTK_LABEL(label), string_buff);
212
213     return wth;
214 }
215
216
217 /* do a preview run on the currently selected capture file */
218 static void
219 preview_do(GtkWidget *prev, wtap *wth)
220 {
221     GtkWidget  *label;
222     unsigned int elapsed_time;
223     time_t      time_preview;
224     time_t      time_current;
225     int         err = 0;
226     gchar      *err_info;
227     gint64      data_offset;
228     const struct wtap_pkthdr *phdr;
229     double      start_time = 0; /* seconds, with nsec resolution */
230     double      stop_time = 0;  /* seconds, with nsec resolution */
231     double      cur_time;
232     unsigned int packets = 0;
233     gboolean    is_breaked = FALSE;
234     gchar       string_buff[PREVIEW_STR_MAX];
235     time_t      ti_time;
236     struct tm  *ti_tm;
237
238
239     time(&time_preview);
240     while ( (wtap_read(wth, &err, &err_info, &data_offset)) ) {
241         phdr = wtap_phdr(wth);
242         cur_time = wtap_nstime_to_sec(&phdr->ts);
243         if(packets == 0) {
244             start_time  = cur_time;
245             stop_time = cur_time;
246         }
247         if (cur_time < start_time) {
248             start_time = cur_time;
249         }
250         if (cur_time > stop_time){
251             stop_time = cur_time;
252         }
253
254         packets++;
255         if(packets%1000 == 0) {
256             /* do we have a timeout? */
257             time(&time_current);
258             if(time_current-time_preview >= (time_t) prefs.gui_fileopen_preview) {
259                 is_breaked = TRUE;
260                 break;
261             }
262         }
263     }
264
265     if(err != 0) {
266         g_snprintf(string_buff, PREVIEW_STR_MAX, "error after reading %u packets", packets);
267         label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
268         gtk_label_set_text(GTK_LABEL(label), string_buff);
269         wtap_close(wth);
270         return;
271     }
272
273     /* packet count */
274     if(is_breaked) {
275         g_snprintf(string_buff, PREVIEW_STR_MAX, "more than %u packets (preview timeout)", packets);
276     } else {
277         g_snprintf(string_buff, PREVIEW_STR_MAX, "%u", packets);
278     }
279     label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
280     gtk_label_set_text(GTK_LABEL(label), string_buff);
281
282     /* first packet */
283     ti_time = (long)start_time;
284     ti_tm = localtime( &ti_time );
285         if(ti_tm) {
286                 g_snprintf(string_buff, PREVIEW_STR_MAX,
287                                  "%04d-%02d-%02d %02d:%02d:%02d",
288                                  ti_tm->tm_year + 1900,
289                                  ti_tm->tm_mon + 1,
290                                  ti_tm->tm_mday,
291                                  ti_tm->tm_hour,
292                                  ti_tm->tm_min,
293                                  ti_tm->tm_sec);
294         } else {
295                 g_snprintf(string_buff, PREVIEW_STR_MAX, "?");
296         }
297         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
298     gtk_label_set_text(GTK_LABEL(label), string_buff);
299
300     /* elapsed time */
301     elapsed_time = (unsigned int)(stop_time-start_time);
302     if(elapsed_time/86400) {
303       g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u days %02u:%02u:%02u",
304         elapsed_time/86400, elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
305     } else {
306       g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u:%02u:%02u",
307         elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
308     }
309     if(is_breaked) {
310       g_snprintf(string_buff, PREVIEW_STR_MAX, "unknown");
311     }
312     label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
313     gtk_label_set_text(GTK_LABEL(label), string_buff);
314
315     wtap_close(wth);
316 }
317
318 #if 0
319 /* as the dialog layout will look very ugly when using the file chooser preview mechanism,
320    simply use the same layout as in GTK2.0 */
321 static void
322 update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
323 {
324     GtkWidget *prev = GTK_WIDGET (data);
325     char *cf_name;
326     gboolean have_preview;
327
328     cf_name = gtk_file_chooser_get_preview_filename (file_chooser);
329
330     have_preview = preview_set_filename(prev, cf_name);
331
332     g_free (cf_name);
333
334     have_preview = TRUE;
335     gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
336 }
337 #endif
338
339
340 /* the filename text entry changed */
341 static void
342 file_open_entry_changed(GtkWidget *w _U_, gpointer file_sel)
343 {
344     GtkWidget *prev = (GtkWidget *)g_object_get_data(G_OBJECT(file_sel), PREVIEW_TABLE_KEY);
345     gchar *cf_name;
346     gboolean have_preview;
347     wtap       *wth;
348
349     /* get the filename */
350     cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_sel));
351
352     /* set the filename to the preview */
353     wth = preview_set_filename(prev, cf_name);
354     have_preview = (wth != NULL);
355
356     g_free(cf_name);
357
358     /* make the preview widget sensitive */
359     gtk_widget_set_sensitive(prev, have_preview);
360
361     /*
362      * XXX - if the Open button isn't sensitive, you can't type into
363      * the location bar and select the file or directory you've typed.
364      * See
365      *
366      *  https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1791
367      *
368      * It's not as if allowing users to click Open when they've
369      * selected a file that's not a valid capture file will cause
370      * anything worse than an error dialog, so we'll leave the Open
371      * button sensitive for now.  Perhaps making it sensitive if
372      * cf_name is NULL would also work, although I don't know whether
373      * there are any cases where it would be non-null when you've
374      * typed in the location bar.
375      *
376      * XXX - Bug 1791 also notes that, with the line removed, Bill
377      * Meier "somehow managed to get the file chooser window somewhat
378      * wedged in that neither the cancel or open buttons were responsive".
379      * That seems a bit odd, given that, without this line, we're not
380      * monkeying with the Open button's sensitivity, but...
381      */
382 #if 0
383     /* make the open/save/... dialog button sensitive */
384
385     gtk_dialog_set_response_sensitive(file_sel, GTK_RESPONSE_ACCEPT, have_preview);
386 #endif
387
388     /* do the actual preview */
389     if(have_preview)
390         preview_do(prev, wth);
391 }
392
393
394 /* copied from summary_dlg.c */
395 static GtkWidget *
396 add_string_to_table_sensitive(GtkWidget *list, guint *row, const gchar *title, const gchar *value, gboolean sensitive)
397 {
398     GtkWidget *label;
399     gchar     *indent;
400
401     if(strlen(value) != 0) {
402         indent = g_strdup_printf("   %s", title);
403     } else {
404         indent = g_strdup(title);
405     }
406     label = gtk_label_new(indent);
407     g_free(indent);
408     gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
409     gtk_widget_set_sensitive(label, sensitive);
410     gtk_table_attach_defaults(GTK_TABLE(list), label, 0, 1, *row, *row+1);
411
412     label = gtk_label_new(value);
413     gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
414     gtk_widget_set_sensitive(label, sensitive);
415     gtk_table_attach_defaults(GTK_TABLE(list), label, 1, 2, *row, *row+1);
416
417     *row = *row + 1;
418
419     return label;
420 }
421
422 static GtkWidget *
423 add_string_to_table(GtkWidget *list, guint *row, const gchar *title, const gchar *value)
424 {
425     return add_string_to_table_sensitive(list, row, title, value, TRUE);
426 }
427
428
429
430 static GtkWidget *
431 preview_new(void)
432 {
433     GtkWidget *table, *label;
434     guint         row;
435
436     table = gtk_table_new(1, 2, FALSE);
437     gtk_table_set_col_spacings(GTK_TABLE(table), 6);
438     gtk_table_set_row_spacings(GTK_TABLE(table), 3);
439     row = 0;
440
441     label = add_string_to_table(table, &row, "Filename:", "-");
442     gtk_widget_set_size_request(label, DEF_WIDTH/3, -1);
443     g_object_set_data(G_OBJECT(table), PREVIEW_FILENAME_KEY, label);
444     label = add_string_to_table(table, &row, "Format:", "-");
445     g_object_set_data(G_OBJECT(table), PREVIEW_FORMAT_KEY, label);
446     label = add_string_to_table(table, &row, "Size:", "-");
447     g_object_set_data(G_OBJECT(table), PREVIEW_SIZE_KEY, label);
448     label = add_string_to_table(table, &row, "Packets:", "-");
449     g_object_set_data(G_OBJECT(table), PREVIEW_PACKETS_KEY, label);
450     label = add_string_to_table(table, &row, "First Packet:", "-");
451     g_object_set_data(G_OBJECT(table), PREVIEW_FIRST_KEY, label);
452     label = add_string_to_table(table, &row, "Elapsed time:", "-");
453     g_object_set_data(G_OBJECT(table), PREVIEW_ELAPSED_KEY, label);
454
455     return table;
456 }
457
458 /*
459  * Keep a static pointer to the current "Open Capture File" window, if
460  * any, so that if somebody tries to do "File:Open" while there's already
461  * an "Open Capture File" window up, we just pop up the existing one,
462  * rather than creating a new one.
463  */
464 static GtkWidget *file_open_w;
465
466 /* Open a file */
467 static void
468 file_open_cmd(GtkWidget *w)
469 {
470 #if _WIN32
471   win32_open_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
472 #else /* _WIN32 */
473   GtkWidget     *main_hb, *main_vb, *filter_hbox, *filter_bt, *filter_te,
474                 *m_resolv_cb, *n_resolv_cb, *t_resolv_cb, *prev;
475   /* No Apply button, and "OK" just sets our text widget, it doesn't
476      activate it (i.e., it doesn't cause us to try to open the file). */
477   static construct_args_t args = {
478         "Wireshark: Read Filter",
479         FALSE,
480         FALSE,
481     TRUE
482   };
483
484   if (file_open_w != NULL) {
485     /* There's already an "Open Capture File" dialog box; reactivate it. */
486     reactivate_window(file_open_w);
487     return;
488   }
489
490   file_open_w = file_selection_new("Wireshark: Open Capture File",
491                                    FILE_SELECTION_OPEN);
492   /* it's annoying, that the file chooser dialog is already shown here,
493      so we cannot use the correct gtk_window_set_default_size() to resize it */
494   gtk_widget_set_size_request(file_open_w, DEF_WIDTH, DEF_HEIGHT);
495
496   switch (prefs.gui_fileopen_style) {
497
498   case FO_STYLE_LAST_OPENED:
499     /* The user has specified that we should start out in the last directory
500        we looked in.  If we've already opened a file, use its containing
501        directory, if we could determine it, as the directory, otherwise
502        use the "last opened" directory saved in the preferences file if
503        there was one. */
504     /* This is now the default behaviour in file_selection_new() */
505     break;
506
507   case FO_STYLE_SPECIFIED:
508     /* The user has specified that we should always start out in a
509        specified directory; if they've specified that directory,
510        start out by showing the files in that dir. */
511     if (prefs.gui_fileopen_dir[0] != '\0')
512       file_selection_set_current_folder(file_open_w, prefs.gui_fileopen_dir);
513     break;
514   }
515
516
517   main_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
518   file_selection_set_extra_widget(file_open_w, main_hb);
519   gtk_widget_show(main_hb);
520
521   /* Container for each row of widgets */
522   main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
523   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
524   gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
525   gtk_widget_show(main_vb);
526
527   /* filter row */
528   filter_hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1, FALSE);
529   gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
530   gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
531   gtk_widget_show(filter_hbox);
532
533   filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
534   g_signal_connect(filter_bt, "clicked",
535                    G_CALLBACK(display_filter_construct_cb), &args);
536   g_signal_connect(filter_bt, "destroy",
537                    G_CALLBACK(filter_button_destroy_cb), NULL);
538   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
539   gtk_widget_show(filter_bt);
540   gtk_widget_set_tooltip_text(filter_bt, "Open the \"Display Filter\" dialog, to edit/apply filters");
541
542   filter_te = gtk_entry_new();
543   g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
544   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
545   g_signal_connect(filter_te, "changed",
546                    G_CALLBACK(filter_te_syntax_check_cb), NULL);
547   g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
548   g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
549   g_signal_connect(file_open_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
550   colorize_filter_te_as_empty(filter_te);
551   gtk_widget_show(filter_te);
552   gtk_widget_set_tooltip_text(filter_te, "Enter a display filter.");
553
554   g_object_set_data(G_OBJECT(file_open_w), E_RFILTER_TE_KEY, filter_te);
555
556   /* resolve buttons */
557   m_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _MAC name resolution");
558   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_resolv_cb),
559         gbl_resolv_flags & RESOLV_MAC);
560   gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
561   g_object_set_data(G_OBJECT(file_open_w),
562                   E_FILE_M_RESOLVE_KEY, m_resolv_cb);
563   gtk_widget_show(m_resolv_cb);
564
565   n_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _network name resolution");
566   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(n_resolv_cb),
567         gbl_resolv_flags & RESOLV_NETWORK);
568   gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
569   gtk_widget_show(n_resolv_cb);
570   g_object_set_data(G_OBJECT(file_open_w), E_FILE_N_RESOLVE_KEY, n_resolv_cb);
571   t_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _transport name resolution");
572   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(t_resolv_cb),
573         gbl_resolv_flags & RESOLV_TRANSPORT);
574   gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
575   gtk_widget_show(t_resolv_cb);
576   g_object_set_data(G_OBJECT(file_open_w), E_FILE_T_RESOLVE_KEY, t_resolv_cb);
577
578   g_signal_connect(file_open_w, "destroy",
579                    G_CALLBACK(file_open_destroy_cb), NULL);
580
581   /* preview widget */
582   prev = preview_new();
583   g_object_set_data(G_OBJECT(file_open_w), PREVIEW_TABLE_KEY, prev);
584   gtk_widget_show_all(prev);
585   gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
586
587   g_signal_connect(GTK_FILE_CHOOSER(file_open_w), "selection-changed",
588                    G_CALLBACK(file_open_entry_changed), file_open_w);
589   file_open_entry_changed(file_open_w, file_open_w);
590
591   g_object_set_data(G_OBJECT(file_open_w), E_DFILTER_TE_KEY,
592                     g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
593   if (gtk_dialog_run(GTK_DIALOG(file_open_w)) == GTK_RESPONSE_ACCEPT)
594   {
595     file_open_ok_cb(file_open_w, file_open_w);
596   }
597   else window_destroy(file_open_w);
598 #endif /* _WIN32 */
599 }
600
601 void
602 file_open_cmd_cb(GtkWidget *widget, gpointer data _U_) {
603   /* If there's unsaved data, let the user save it first.
604      If they cancel out of it, don't quit. */
605   if (do_file_close(&cfile, FALSE, " before opening a new capture file"))
606     file_open_cmd(widget);
607 }
608
609 /* user pressed "open" button */
610 static void
611 file_open_ok_cb(GtkWidget *w, gpointer fs) {
612   gchar       *cf_name, *s;
613   const gchar *rfilter;
614   GtkWidget   *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
615   dfilter_t   *rfcode = NULL;
616   int          err;
617
618   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
619   filter_te = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
620   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
621   if (!dfilter_compile(rfilter, &rfcode)) {
622     bad_dfilter_alert_box(rfilter);
623     g_free(cf_name);
624     return;
625   }
626
627   /* Perhaps the user specified a directory instead of a file.
628      Check whether they did. */
629   if (test_for_directory(cf_name) == EISDIR) {
630     /* It's a directory - set the file selection box to display that
631        directory, don't try to open the directory as a capture file. */
632     set_last_open_dir(cf_name);
633     g_free(cf_name);
634     file_selection_set_current_folder(fs, get_last_open_dir());
635     return;
636   }
637
638   /* Try to open the capture file. */
639   if (cf_open(&cfile, cf_name, FALSE, &err) != CF_OK) {
640     /* We couldn't open it; don't dismiss the open dialog box,
641        just leave it around so that the user can, after they
642        dismiss the alert box popped up for the open error,
643        try again. */
644     if (rfcode != NULL)
645       dfilter_free(rfcode);
646     g_free(cf_name);
647
648     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
649      * as this will prevent the user from closing the now existing error
650      * message, simply close the dialog (this is the best we can do here). */
651     if (file_open_w)
652       window_destroy(file_open_w);
653
654     return;
655   }
656
657   /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
658      it closed the previous capture file, and thus destroyed any
659      previous read filter attached to "cf"). */
660   cfile.rfcode = rfcode;
661
662   /* Set the global resolving variable */
663   gbl_resolv_flags = prefs.name_resolve;
664   m_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_FILE_M_RESOLVE_KEY);
665   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (m_resolv_cb)))
666     gbl_resolv_flags |= RESOLV_MAC;
667   else
668     gbl_resolv_flags &= ~RESOLV_MAC;
669   n_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_FILE_N_RESOLVE_KEY);
670   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (n_resolv_cb)))
671     gbl_resolv_flags |= RESOLV_NETWORK;
672   else
673     gbl_resolv_flags &= ~RESOLV_NETWORK;
674   t_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_FILE_T_RESOLVE_KEY);
675   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (t_resolv_cb)))
676     gbl_resolv_flags |= RESOLV_TRANSPORT;
677   else
678     gbl_resolv_flags &= ~RESOLV_TRANSPORT;
679
680   /* We've crossed the Rubicon; get rid of the file selection box. */
681   window_destroy(GTK_WIDGET (fs));
682
683   switch (cf_read(&cfile, FALSE)) {
684
685   case CF_READ_OK:
686   case CF_READ_ERROR:
687     /* Just because we got an error, that doesn't mean we were unable
688        to read any of the file; we handle what we could get from the
689        file. */
690     break;
691
692   case CF_READ_ABORTED:
693     /* The user bailed out of re-reading the capture file; the
694        capture file has been closed - just free the capture file name
695        string and return (without changing the last containing
696        directory). */
697     g_free(cf_name);
698     return;
699   }
700
701   /* Save the name of the containing directory specified in the path name,
702      if any; we can write over cf_name, which is a good thing, given that
703      "get_dirname()" does write over its argument. */
704   s = get_dirname(cf_name);
705   set_last_open_dir(s);
706
707   g_free(cf_name);
708 }
709
710 static void
711 file_open_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
712 {
713   /* Note that we no longer have a "Open Capture File" dialog box. */
714   file_open_w = NULL;
715 }
716
717 /*
718  * Keep a static pointer to the current "Merge Capture File" window, if
719  * any, so that if somebody tries to do "File:Merge" while there's already
720  * an "Merge Capture File" window up, we just pop up the existing one,
721  * rather than creating a new one.
722  */
723 static GtkWidget *file_merge_w;
724
725 /* Merge existing with another file */
726 static void
727 file_merge_cmd(GtkWidget *w)
728 {
729 #if _WIN32
730   win32_merge_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
731   new_packet_list_freeze();
732   new_packet_list_thaw();
733 #else /* _WIN32 */
734   GtkWidget     *main_hb, *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *filter_hbox,
735                 *filter_bt, *filter_te, *prepend_rb, *chrono_rb,
736                 *append_rb, *prev;
737
738   /* No Apply button, and "OK" just sets our text widget, it doesn't
739      activate it (i.e., it doesn't cause us to try to open the file). */
740   static construct_args_t args = {
741     "Wireshark: Read Filter",
742     FALSE,
743     FALSE,
744     TRUE
745   };
746
747   if (file_merge_w != NULL) {
748     /* There's already an "Merge Capture File" dialog box; reactivate it. */
749     reactivate_window(file_merge_w);
750     return;
751   }
752
753   /* Default to saving all packets, in the file's current format. */
754
755   file_merge_w = file_selection_new("Wireshark: Merge with Capture File",
756                                    FILE_SELECTION_OPEN);
757   /* it's annoying, that the file chooser dialog is already shown here,
758      so we cannot use the correct gtk_window_set_default_size() to resize it */
759   gtk_widget_set_size_request(file_merge_w, DEF_WIDTH, DEF_HEIGHT);
760
761   switch (prefs.gui_fileopen_style) {
762
763   case FO_STYLE_LAST_OPENED:
764     /* The user has specified that we should start out in the last directory
765        we looked in.  If we've already opened a file, use its containing
766        directory, if we could determine it, as the directory, otherwise
767        use the "last opened" directory saved in the preferences file if
768        there was one. */
769     /* This is now the default behaviour in file_selection_new() */
770     break;
771
772   case FO_STYLE_SPECIFIED:
773     /* The user has specified that we should always start out in a
774        specified directory; if they've specified that directory,
775        start out by showing the files in that dir. */
776     if (prefs.gui_fileopen_dir[0] != '\0')
777       file_selection_set_current_folder(file_merge_w, prefs.gui_fileopen_dir);
778     break;
779   }
780
781   main_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
782   file_selection_set_extra_widget(file_merge_w, main_hb);
783   gtk_widget_show(main_hb);
784
785   /* Container for each row of widgets */
786   main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
787   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
788   gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
789   gtk_widget_show(main_vb);
790
791   /* File type row */
792   range_tb = NULL;
793   ft_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
794   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
795   gtk_widget_show(ft_hb);
796
797   ft_lb = gtk_label_new("Merged output file type:");
798   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
799   gtk_widget_show(ft_lb);
800
801   ft_combo_box = ws_combo_box_new_text_and_pointer();
802
803   /* Generate the list of file types we can save. */
804   set_file_type_list(ft_combo_box, &cfile);
805   gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
806   gtk_widget_show(ft_combo_box);
807   g_object_set_data(G_OBJECT(file_merge_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
808   ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0); /* No callback */
809
810   filter_hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1, FALSE);
811   gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
812   gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
813   gtk_widget_show(filter_hbox);
814
815   filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
816   g_signal_connect(filter_bt, "clicked",
817                    G_CALLBACK(display_filter_construct_cb), &args);
818   g_signal_connect(filter_bt, "destroy",
819                    G_CALLBACK(filter_button_destroy_cb), NULL);
820   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
821   gtk_widget_show(filter_bt);
822   gtk_widget_set_tooltip_text(filter_bt, "Open the \"Display Filter\" dialog, to edit/apply filters");
823
824   filter_te = gtk_entry_new();
825   g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
826   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
827   g_signal_connect(filter_te, "changed",
828                    G_CALLBACK(filter_te_syntax_check_cb), NULL);
829   g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
830   g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
831   g_signal_connect(file_merge_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
832   colorize_filter_te_as_empty(filter_te);
833   gtk_widget_show(filter_te);
834   gtk_widget_set_tooltip_text(filter_te, "Enter a display filter.");
835
836   g_object_set_data(G_OBJECT(file_merge_w), E_RFILTER_TE_KEY, filter_te);
837
838   prepend_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL,
839       "Prepend packets to existing file");
840   gtk_widget_set_tooltip_text(prepend_rb, "The resulting file contains the packets from the selected, followed by the packets from the currently loaded file, the packet timestamps will be ignored.");
841   gtk_box_pack_start(GTK_BOX(main_vb), prepend_rb, FALSE, FALSE, 0);
842   g_object_set_data(G_OBJECT(file_merge_w),
843                   E_MERGE_PREPEND_KEY, prepend_rb);
844   gtk_widget_show(prepend_rb);
845
846   chrono_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Merge packets chronologically");
847   gtk_widget_set_tooltip_text(chrono_rb, "The resulting file contains all the packets from the currently loaded and the selected file, sorted by the packet timestamps.");
848   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chrono_rb), TRUE);
849   gtk_box_pack_start(GTK_BOX(main_vb), chrono_rb, FALSE, FALSE, 0);
850   gtk_widget_show(chrono_rb);
851   g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_CHRONO_KEY, chrono_rb);
852
853   append_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Append packets to existing file");
854   gtk_widget_set_tooltip_text(append_rb, "The resulting file contains the packets from the currently loaded, followed by the packets from the selected file, the packet timestamps will be ignored.");
855   gtk_box_pack_start(GTK_BOX(main_vb), append_rb, FALSE, FALSE, 0);
856   gtk_widget_show(append_rb);
857   g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_APPEND_KEY, append_rb);
858
859   g_signal_connect(file_merge_w, "destroy",
860                    G_CALLBACK(file_merge_destroy_cb), NULL);
861
862   /* preview widget */
863   prev = preview_new();
864   g_object_set_data(G_OBJECT(file_merge_w), PREVIEW_TABLE_KEY, prev);
865   gtk_widget_show_all(prev);
866   gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
867
868   g_signal_connect(GTK_FILE_CHOOSER(file_merge_w), "selection-changed",
869                    G_CALLBACK(file_open_entry_changed), file_merge_w);
870   file_open_entry_changed(file_merge_w, file_merge_w);
871
872   g_object_set_data(G_OBJECT(file_merge_w), E_DFILTER_TE_KEY,
873                     g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
874   if (gtk_dialog_run(GTK_DIALOG(file_merge_w)) == GTK_RESPONSE_ACCEPT)
875   {
876     file_merge_ok_cb(file_merge_w, file_merge_w);
877   }
878   else window_destroy(file_merge_w);
879 #endif /* _WIN32 */
880 }
881
882 void
883 file_merge_cmd_cb(GtkWidget *widget, gpointer data _U_) {
884   /* If there's unsaved data, let the user save it first.
885      If they cancel out of it, don't merge. */
886   GtkWidget *msg_dialog;
887   gchar     *display_basename;
888   gint       response;
889
890   if (prefs.gui_ask_unsaved) {
891     if (cfile.is_tempfile || cfile.unsaved_changes) {
892       /* This is a temporary capture file or has unsaved changes; ask the
893          user whether to save the capture. */
894       if (cfile.is_tempfile) {
895         msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
896                                             GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
897                                             GTK_MESSAGE_QUESTION,
898                                             GTK_BUTTONS_NONE,
899                                             "Do you want to save the captured packets before merging another capture file into it?");
900
901         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
902              "A temporary capture file can't be merged.");
903       } else {
904         /*
905          * Format the message.
906          */
907         display_basename = g_filename_display_basename(cfile.filename);
908         msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
909                                             GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
910                                             GTK_MESSAGE_QUESTION,
911                                             GTK_BUTTONS_NONE,
912                                             "Do you want to save the changes you've made "
913                                             "to the capture file \"%s\" before merging another capture file into it?",
914                                             display_basename);
915         g_free(display_basename);
916         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
917              "The changes must be saved before the files are merged.");
918       }
919
920 #ifndef _WIN32
921       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
922                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
923       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
924                             GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT);
925 #else
926       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
927                             GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT);
928       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
929                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
930 #endif
931       gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog), GTK_RESPONSE_ACCEPT);
932
933       response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
934       gtk_widget_destroy(msg_dialog);
935
936       switch (response) {
937
938       case GTK_RESPONSE_ACCEPT:
939         /* Save the file but don't close it */
940         do_file_save(&cfile, FALSE);
941         break;
942
943       case GTK_RESPONSE_CANCEL:
944       case GTK_RESPONSE_NONE:
945       case GTK_RESPONSE_DELETE_EVENT:
946       default:
947         /* Don't do the merge. */
948         return;
949       }
950     }
951   }
952
953   /* Do the merge. */
954   file_merge_cmd(widget);
955 }
956
957
958 static void
959 file_merge_ok_cb(GtkWidget *w, gpointer fs) {
960   gchar       *cf_name, *s;
961   const gchar *rfilter;
962   GtkWidget   *ft_combo_box, *filter_te, *rb;
963   dfilter_t   *rfcode = NULL;
964   int          err;
965   cf_status_t  merge_status;
966   char        *in_filenames[2];
967   char        *tmpname;
968   gpointer     ptr;
969   int          file_type;
970
971
972   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
973   filter_te = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
974   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
975   if (!dfilter_compile(rfilter, &rfcode)) {
976     bad_dfilter_alert_box(rfilter);
977     g_free(cf_name);
978     return;
979   }
980
981   ft_combo_box  = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_FILE_TYPE_COMBO_BOX_KEY);
982   if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
983       g_assert_not_reached();  /* Programming error: somehow nothing is active */
984   }
985   file_type = GPOINTER_TO_INT(ptr);
986
987   /* Perhaps the user specified a directory instead of a file.
988      Check whether they did. */
989   if (test_for_directory(cf_name) == EISDIR) {
990     /* It's a directory - set the file selection box to display that
991        directory, don't try to open the directory as a capture file. */
992     set_last_open_dir(cf_name);
993     g_free(cf_name);
994     file_selection_set_current_folder(fs, get_last_open_dir());
995     return;
996   }
997
998   /* merge or append the two files */
999   rb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_MERGE_CHRONO_KEY);
1000   tmpname = NULL;
1001   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
1002       /* chronological order */
1003       in_filenames[0] = cfile.filename;
1004       in_filenames[1] = cf_name;
1005       merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
1006   } else {
1007       rb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_MERGE_PREPEND_KEY);
1008       if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
1009           /* prepend file */
1010           in_filenames[0] = cf_name;
1011           in_filenames[1] = cfile.filename;
1012           merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type,
1013                                         TRUE);
1014       } else {
1015           /* append file */
1016           in_filenames[0] = cfile.filename;
1017           in_filenames[1] = cf_name;
1018           merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type,
1019                                         TRUE);
1020       }
1021   }
1022
1023   g_free(cf_name);
1024
1025   if (merge_status != CF_OK) {
1026     if (rfcode != NULL)
1027       dfilter_free(rfcode);
1028     g_free(tmpname);
1029     return;
1030   }
1031
1032   cf_close(&cfile);
1033
1034   /* We've crossed the Rubicon; get rid of the file selection box. */
1035   window_destroy(GTK_WIDGET (fs));
1036
1037   /* Try to open the merged capture file. */
1038   if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
1039     /* We couldn't open it; don't dismiss the open dialog box,
1040        just leave it around so that the user can, after they
1041        dismiss the alert box popped up for the open error,
1042        try again. */
1043     if (rfcode != NULL)
1044       dfilter_free(rfcode);
1045     g_free(tmpname);
1046     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1047      * as this will prevent the user from closing the now existing error
1048      * message, simply close the dialog (this is the best we can do here). */
1049     if (file_open_w)
1050       window_destroy(file_open_w);
1051     return;
1052   }
1053   g_free(tmpname);
1054
1055   /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1056      it closed the previous capture file, and thus destroyed any
1057      previous read filter attached to "cf"). */
1058   cfile.rfcode = rfcode;
1059
1060   switch (cf_read(&cfile, FALSE)) {
1061
1062   case CF_READ_OK:
1063   case CF_READ_ERROR:
1064     /* Just because we got an error, that doesn't mean we were unable
1065        to read any of the file; we handle what we could get from the
1066        file. */
1067     break;
1068
1069   case CF_READ_ABORTED:
1070     /* The user bailed out of re-reading the capture file; the
1071        capture file has been closed - just free the capture file name
1072        string and return (without changing the last containing
1073        directory). */
1074     return;
1075   }
1076
1077   /* Save the name of the containing directory specified in the path name,
1078      if any; we can write over cf_merged_name, which is a good thing, given that
1079      "get_dirname()" does write over its argument. */
1080   s = get_dirname(tmpname);
1081   set_last_open_dir(s);
1082 }
1083
1084 static void
1085 file_merge_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1086 {
1087   /* Note that we no longer have a "Merge Capture File" dialog box. */
1088   file_merge_w = NULL;
1089 }
1090
1091 gboolean
1092 do_file_close(capture_file *cf, gboolean from_quit, const char *before_what)
1093 {
1094   GtkWidget *msg_dialog;
1095   gchar     *display_basename;
1096   gint       response;
1097   gboolean   capture_in_progress;
1098
1099   if (cf->state == FILE_CLOSED)
1100     return TRUE; /* already closed, nothing to do */
1101
1102 #ifdef HAVE_LIBPCAP
1103   if (cf->state == FILE_READ_IN_PROGRESS) {
1104     /* This is true if we're reading a capture file *or* if we're doing
1105        a live capture.  If we're reading a capture file, the main loop
1106        is busy reading packets, and only accepting input from the
1107        progress dialog, so we can't get here, so this means we're
1108        doing a capture. */
1109     capture_in_progress = TRUE;
1110   } else
1111 #endif
1112     capture_in_progress = FALSE;
1113        
1114   if (prefs.gui_ask_unsaved) {
1115     if (cf->is_tempfile || capture_in_progress || cf->unsaved_changes) {
1116       /* This is a temporary capture file, or there's a capture in
1117          progress, or the file has unsaved changes; ask the user whether
1118          to save the data. */
1119       if (cf->is_tempfile) {
1120         msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1121                                             GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
1122                                             GTK_MESSAGE_QUESTION,
1123                                             GTK_BUTTONS_NONE,
1124                                             capture_in_progress ? 
1125                                                 "Do you want to stop the capture and save the captured packets%s?" :
1126                                                 "Do you want to save the captured packets%s?",
1127                                             before_what);
1128
1129         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
1130              "Your captured packets will be lost if you don't save them.");
1131       } else {
1132         /*
1133          * Format the message.
1134          */
1135         display_basename = g_filename_display_basename(cf->filename);
1136         if (capture_in_progress) {
1137           msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1138                                               GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
1139                                               GTK_MESSAGE_QUESTION,
1140                                               GTK_BUTTONS_NONE,
1141                                               "Do you want to stop the capture and save the captured packets%s?",
1142                                               before_what);
1143         } else {
1144           msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
1145                                               GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
1146                                               GTK_MESSAGE_QUESTION,
1147                                               GTK_BUTTONS_NONE,
1148                                               "Do you want to save the changes you've made "
1149                                               "to the capture file \"%s\"%s?",
1150                                               display_basename, before_what);
1151         }
1152         g_free(display_basename);
1153         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
1154                                                  capture_in_progress ?
1155              "Your captured packets will be lost if you don't save them." :
1156              "Your changes will be lost if you don't save them.");
1157       }
1158
1159 #ifndef _WIN32
1160       /* If this is from a Quit operation, use "quit and don't save"
1161          rather than just "don't save". */
1162       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1163                             (from_quit ?
1164                                 (cf->state == FILE_READ_IN_PROGRESS ?
1165                                     WIRESHARK_STOCK_STOP_QUIT_DONT_SAVE :
1166                                     WIRESHARK_STOCK_QUIT_DONT_SAVE) :
1167                                 (capture_in_progress ?
1168                                     WIRESHARK_STOCK_STOP_DONT_SAVE :
1169                                     WIRESHARK_STOCK_DONT_SAVE)),
1170                             GTK_RESPONSE_REJECT);
1171       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1172                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1173       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1174                             (capture_in_progress ?
1175                                 WIRESHARK_STOCK_STOP_SAVE :
1176                                 GTK_STOCK_SAVE),
1177                             GTK_RESPONSE_ACCEPT);
1178 #else
1179       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1180                             (capture_in_progress ?
1181                                 WIRESHARK_STOCK_STOP_SAVE :
1182                                 GTK_STOCK_SAVE),
1183                             GTK_RESPONSE_ACCEPT);
1184       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1185                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1186       gtk_dialog_add_button(GTK_DIALOG(msg_dialog),
1187                             (from_quit ?
1188                                 (capture_in_progress ?
1189                                     WIRESHARK_STOCK_STOP_QUIT_DONT_SAVE :
1190                                     WIRESHARK_STOCK_QUIT_DONT_SAVE) :
1191                                 (capture_in_progress ?
1192                                     WIRESHARK_STOCK_STOP_DONT_SAVE :
1193                                     WIRESHARK_STOCK_DONT_SAVE)),
1194                             GTK_RESPONSE_REJECT);
1195 #endif
1196       gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog), GTK_RESPONSE_ACCEPT);
1197
1198       response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
1199       gtk_widget_destroy(msg_dialog);
1200
1201       switch (response) {
1202
1203       case GTK_RESPONSE_ACCEPT:
1204 #ifdef HAVE_LIBPCAP
1205         /* If there's a capture in progress, we have to stop the capture
1206            and then do the save. */
1207         if (capture_in_progress)
1208           capture_stop_cb(NULL, NULL);
1209 #endif
1210         /* Save the file and close it */
1211         do_file_save(cf, TRUE);
1212         break;
1213
1214       case GTK_RESPONSE_REJECT:
1215 #ifdef HAVE_LIBPCAP
1216         /* If there's a capture in progress; we have to stop the capture
1217            and then do the close. */
1218         if (capture_in_progress)
1219           capture_stop_cb(NULL, NULL);
1220 #endif
1221         /* Just close the file, discarding changes */
1222         cf_close(cf);
1223         break;
1224
1225       case GTK_RESPONSE_CANCEL:
1226       case GTK_RESPONSE_NONE:
1227       case GTK_RESPONSE_DELETE_EVENT:
1228       default:
1229         /* Don't close the file (and don't stop any capture in progress). */
1230         return FALSE; /* file not closed */
1231         break;
1232       }
1233     } else {
1234       /* unchanged file, just close it */
1235       cf_close(cf);
1236     }
1237   } else {
1238     /* User asked not to be bothered by those prompts, just close it.
1239        XXX - should that apply only to saving temporary files? */
1240 #ifdef HAVE_LIBPCAP
1241       /* If there's a capture in progress, we have to stop the capture
1242          and then do the close. */
1243     if (capture_in_progress)
1244       capture_stop_cb(NULL, NULL);
1245 #endif
1246     cf_close(cf);
1247   }
1248   return TRUE; /* file closed */
1249 }
1250
1251 /* Close a file */
1252 void
1253 file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
1254   do_file_close(&cfile, FALSE, "");
1255 }
1256
1257 /*
1258  * Save the capture file in question, prompting the user for a file
1259  * name to save to if necessary.
1260  */
1261 static void
1262 do_file_save(capture_file *cf, gboolean dont_reopen)
1263 {
1264   char *fname;
1265
1266   if (cf->is_tempfile) {
1267     /* This is a temporary capture file, so saving it means saving
1268        it to a permanent file.  Prompt the user for a location
1269        to which to save it. */
1270     do_file_save_as(cf);
1271   } else {
1272     if (cf->unsaved_changes) {
1273       /* This is not a temporary capture file, but it has unsaved
1274          changes, so saving it means doing a "safe save" on top
1275          of the existing file, in the same format - no UI needed. */
1276       /* XXX - cf->filename might get freed out from under us, because
1277          the code path through which cf_save_packets() goes currently
1278          closes the current file and then opens and reloads the saved file,
1279          so make a copy and free it later. */
1280       fname = g_strdup(cf->filename);
1281       cf_save_packets(cf, fname, cf->cd_t, cf->iscompressed, dont_reopen);
1282       g_free(fname);
1283     }
1284     /* Otherwise just do nothing. */
1285   }
1286 }
1287
1288 void
1289 file_save_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
1290   do_file_save(&cfile, FALSE);
1291 }
1292
1293 /* Attach a list of the valid 'save as' file types to a combo_box by
1294    checking what Wiretap supports.  Make the default type the first
1295    in the list.
1296  */
1297 static void
1298 set_file_type_list(GtkWidget *combo_box, capture_file *cf)
1299 {
1300   GArray *savable_file_types;
1301   guint i;
1302   int ft;
1303
1304   savable_file_types = wtap_get_savable_file_types(cf->cd_t, cf->lnk_t);
1305
1306   if (savable_file_types != NULL) {
1307     /* OK, we have at least one file type we can save this file as.
1308        (If we didn't, we shouldn't have gotten here in the first
1309        place.)  Add them all to the combo box.  */
1310     for (i = 0; i < savable_file_types->len; i++) {
1311       ft = g_array_index(savable_file_types, int, i);
1312       ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box),
1313                                            wtap_file_type_string(ft),
1314                                            GINT_TO_POINTER(ft));
1315     }
1316     g_array_free(savable_file_types, TRUE);
1317   }
1318 }
1319
1320 static void
1321 file_save_as_select_file_type_cb(GtkWidget *w, gpointer data _U_)
1322 {
1323   int new_file_type;
1324   gpointer ptr;
1325   GtkWidget *compressed_cb;
1326
1327   if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(w), &ptr)) {
1328       g_assert_not_reached();  /* Programming error: somehow nothing is active */
1329   }
1330   new_file_type = GPOINTER_TO_INT(ptr);
1331
1332   compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(file_save_as_w), E_COMPRESSED_CB_KEY);
1333   if (!wtap_dump_can_compress(new_file_type)) {
1334     /* Can't compress this file type; turn off compression and make
1335        the compression checkbox insensitive. */
1336     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compressed_cb), FALSE);
1337     gtk_widget_set_sensitive(compressed_cb, FALSE);
1338   } else
1339     gtk_widget_set_sensitive(compressed_cb, TRUE);
1340 }
1341
1342
1343 static void
1344 do_file_save_as(capture_file *cf)
1345 {
1346 #if _WIN32
1347   win32_save_as_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
1348 #else /* _WIN32 */
1349   GtkWidget     *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *compressed_cb;
1350   GtkWidget     *msg_dialog;
1351   gchar         *display_basename;
1352   gint           response;
1353   char          *cf_name;
1354   ws_statb64     statbuf;
1355
1356   if (file_save_as_w != NULL) {
1357     /* There's already an "Save Capture File As" dialog box; reactivate it. */
1358     reactivate_window(file_save_as_w);
1359     return;
1360   }
1361
1362   /* Default to saving in the file's current format. */
1363
1364   /* build the file selection */
1365   file_save_as_w = file_selection_new ("Wireshark: Save Capture File As",
1366                                        FILE_SELECTION_SAVE);
1367
1368   /* Container for each row of widgets */
1369
1370   main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
1371   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1372   file_selection_set_extra_widget(file_save_as_w, main_vb);
1373   gtk_widget_show(main_vb);
1374
1375   /* File type row */
1376   range_tb = NULL;
1377   ft_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1378   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
1379   gtk_widget_show(ft_hb);
1380
1381   ft_lb = gtk_label_new("File type:");
1382   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
1383   gtk_widget_show(ft_lb);
1384
1385   ft_combo_box = ws_combo_box_new_text_and_pointer();
1386
1387   /* Generate the list of file types we can save. */
1388   set_file_type_list(ft_combo_box, cf);
1389   gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
1390   gtk_widget_show(ft_combo_box);
1391   g_object_set_data(G_OBJECT(file_save_as_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
1392
1393   /* compressed */
1394   compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
1395   gtk_container_add(GTK_CONTAINER(ft_hb), compressed_cb);
1396   gtk_widget_show(compressed_cb);
1397   g_object_set_data(G_OBJECT(file_save_as_w), E_COMPRESSED_CB_KEY, compressed_cb);
1398   /* Ok: now "select" the default filetype which invokes file_save_as_select_file_type_cb */
1399   g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_save_as_select_file_type_cb), NULL);
1400   ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0);
1401
1402   g_signal_connect(file_save_as_w, "destroy",
1403                    G_CALLBACK(file_save_as_destroy_cb), NULL);
1404
1405   /*
1406    * Loop until the user either selects a file or gives up.
1407    */
1408   for (;;) {
1409     if (gtk_dialog_run(GTK_DIALOG(file_save_as_w)) != GTK_RESPONSE_ACCEPT) {
1410       /* They clicked "Cancel" or closed the dialog or.... */
1411       window_destroy(file_save_as_w);
1412       return;
1413     }
1414
1415     cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_save_as_w));
1416
1417     /* Perhaps the user specified a directory instead of a file.
1418        Check whether they did. */
1419     if (test_for_directory(cf_name) == EISDIR) {
1420       /* It's a directory - set the file selection box to display that
1421          directory, and go back and re-run it. */
1422       set_last_open_dir(cf_name);
1423       g_free(cf_name);
1424       file_selection_set_current_folder(file_save_as_w, get_last_open_dir());
1425       continue;
1426     }
1427
1428     /*
1429      * Check that the from file is not the same as to file
1430      * We do it here so we catch all cases ...
1431      * Unfortunately, the file requester gives us an absolute file
1432      * name and the read file name may be relative (if supplied on
1433      * the command line). From Joerg Mayer.
1434      */
1435     if (files_identical(cf->filename, cf_name)) {
1436       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1437         "%sCapture file: \"%s\" identical to loaded file!%s\n\n"
1438         "Please choose a different filename.",
1439         simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
1440       g_free(cf_name);
1441
1442       /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1443        * as this will prevent the user from closing the now existing error
1444        * message, simply close the dialog (this is the best we can do here). */
1445       if (file_save_as_w)
1446         window_destroy(GTK_WIDGET(file_save_as_w));
1447
1448       return;
1449     }
1450
1451     /* it the file doesn't exist, simply try to save it */
1452     if (!file_exists(cf_name)) {
1453       gtk_widget_hide(GTK_WIDGET(file_save_as_w));
1454       g_free(cf_name);
1455       file_save_as_cb(file_save_as_w);
1456       return;
1457     }
1458
1459     /*
1460      * The file exists.  Ask the user if they want to overwrite it.
1461      *
1462      * XXX - what if it's a symlink?  TextEdit just blindly replaces
1463      * symlinks with files if you save over them, but gedit appears
1464      * to replace the *target* of the symlink.  (To find the target,
1465      * use realpath() - it dates back to the SUSv2 and may be even
1466      * older.)
1467      */
1468     display_basename = g_filename_display_basename(cf_name);
1469     msg_dialog = gtk_message_dialog_new(GTK_WINDOW(file_save_as_w),
1470                                         GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
1471                                         GTK_MESSAGE_QUESTION,
1472                                         GTK_BUTTONS_NONE,
1473                                         "A file named \"%s\" already exists. Do you want to replace it?",
1474                                         display_basename);
1475     g_free(display_basename);
1476     gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
1477         "A file or folder with the same name already exists in that folder. "
1478         "Replacing it will overwrite its current contents.");
1479
1480     gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
1481 #ifndef _WIN32
1482                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1483                            "Replace", GTK_RESPONSE_ACCEPT,
1484 #else
1485                            "Replace", GTK_RESPONSE_ACCEPT,
1486                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1487 #endif
1488                            NULL);
1489     gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog), GTK_RESPONSE_CANCEL);
1490
1491     response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
1492     gtk_widget_destroy(msg_dialog);
1493
1494     if (response != GTK_RESPONSE_ACCEPT) {
1495       /* The user doesn't want to overwrite this file; let them choose
1496          another one */
1497       g_free(cf_name);
1498       continue;
1499     }
1500
1501     /* Check whether the file has all the write permission bits clear
1502        and, on systems that have the 4.4-Lite file flags, whether it
1503        has the "user immutable" flag set.  Treat both of those as an
1504        indication that the user wants to protect the file from
1505        casual overwriting, and ask the user if they want to override
1506        that.
1507
1508        (Linux's "immutable" flag, as fetched and set by the appropriate
1509        ioctls (FS_IOC_GETFLAGS/FS_IOC_SETFLAGS in newer kernels,
1510        EXT2_IOC_GETFLAGS/EXT2_IOC_SETFLAGS in older kernels - non-ext2
1511        file systems that support those ioctls use the same values as ext2
1512        does), appears to be more like the *BSD/OS X "system immutable"
1513        flag, as it can be set only by the superuser or by processes with
1514        CAP_LINUX_IMMUTABLE, so it sounds as if it's not intended for
1515        arbitrary users to set or clear. */
1516     if (ws_stat64(cf_name, &statbuf) != -1) {
1517       /* OK, we have the permission bits and, if HAVE_ST_FLAGS is defined,
1518          the flags.  (If we don't, we don't worry about it.) */
1519 #ifdef HAVE_ST_FLAGS
1520       if (statbuf.st_flags & UF_IMMUTABLE) {
1521         display_basename = g_filename_display_basename(cf_name);
1522         msg_dialog = gtk_message_dialog_new(GTK_WINDOW(file_save_as_w),
1523                                             GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
1524                                             GTK_MESSAGE_QUESTION,
1525                                             GTK_BUTTONS_NONE,
1526 #ifdef __APPLE__
1527         /* Stuff in the OS X UI calls files with the "user immutable" bit
1528            "locked"; pre-OS X Mac software might have had that notion and
1529            called it "locked". */
1530                                             "The file \"%s\" is locked.",
1531 #else /* __APPLE__ */
1532         /* Just call it "immutable" in *BSD. */
1533                                             "The file \"%s\" is immutable.",
1534 #endif /* __APPLE__ */
1535                                             display_basename);
1536         g_free(display_basename);
1537       } else
1538 #endif /* HAVE_ST_FLAGS */
1539       if ((statbuf.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0) {
1540         display_basename = g_filename_display_basename(cf_name);
1541         msg_dialog = gtk_message_dialog_new(GTK_WINDOW(file_save_as_w),
1542                                             GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
1543                                             GTK_MESSAGE_QUESTION,
1544                                             GTK_BUTTONS_NONE,
1545                                             "The file \"%s\" is read-only.",
1546                                             display_basename);
1547         g_free(display_basename);
1548       } else {
1549         /* No problem, just drive on. */
1550         msg_dialog = NULL;
1551       }
1552       if (msg_dialog != NULL) {
1553         /* OK, ask the user if they want to overwrite the file. */
1554         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
1555             "Do you want to overwrite it anyway?");
1556
1557         gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
1558                                "Overwrite", GTK_RESPONSE_ACCEPT,
1559                                "Don't overwrite", GTK_RESPONSE_REJECT,
1560                                NULL);
1561         gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog), GTK_RESPONSE_REJECT);
1562
1563         response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
1564         gtk_widget_destroy(msg_dialog);
1565
1566         if (response != GTK_RESPONSE_ACCEPT) {
1567           /* The user doesn't want to overwrite this file; let them choose
1568              another one */
1569           g_free(cf_name);
1570           continue;
1571         }
1572
1573 #ifdef HAVE_ST_FLAGS
1574         /* OK, they want to overwrite the file.  If it has the "user
1575            immutable" flag, we have to turn that off first, so we
1576            can move the file. */
1577         if (statbuf.st_flags & UF_IMMUTABLE) {
1578           /* If this fails, the attempt to save will fail, so just
1579              let that happen and pop up a "you lose" dialog. */
1580           chflags(cf_name, statbuf.st_flags & ~UF_IMMUTABLE);
1581         }
1582 #endif
1583       }
1584     }
1585
1586     /* save file */
1587     gtk_widget_hide(GTK_WIDGET(file_save_as_w));
1588     file_save_as_cb(file_save_as_w);
1589     return;
1590   }
1591 #endif /* _WIN32 */
1592 }
1593
1594 void
1595 file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
1596 {
1597   do_file_save_as(&cfile);
1598 }
1599
1600 /* all tests ok, we only have to save the file */
1601 /* (and probably continue with a pending operation) */
1602 static void
1603 file_save_as_cb(GtkWidget *fs) {
1604   GtkWidget *ft_combo_box;
1605   GtkWidget *compressed_cb;
1606   gchar     *cf_name;
1607   gchar     *dirname;
1608   gpointer   ptr;
1609   int        file_type;
1610   gboolean   compressed;
1611
1612   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1613
1614   compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_COMPRESSED_CB_KEY);
1615   ft_combo_box  = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_FILE_TYPE_COMBO_BOX_KEY);
1616
1617   if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
1618       g_assert_not_reached();  /* Programming error: somehow nothing is active */
1619   }
1620   file_type = GPOINTER_TO_INT(ptr);
1621   compressed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb));
1622
1623   /* Write out all the packets to the file with the specified name. */
1624   if (cf_save_packets(&cfile, cf_name, file_type, compressed, FALSE) != CF_OK) {
1625     /* The write failed; don't dismiss the open dialog box,
1626        just leave it around so that the user can, after they
1627        dismiss the alert box popped up for the error, try again. */
1628     g_free(cf_name);
1629     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1630      * as this will prevent the user from closing the now existing error
1631      * message, simply close the dialog (this is the best we can do here). */
1632     if (file_save_as_w)
1633       window_destroy(GTK_WIDGET (fs));
1634     return;
1635   }
1636
1637   /* The write succeeded; get rid of the file selection box. */
1638   /* cf_save_packets() might already closed our dialog! */
1639   if (file_save_as_w)
1640     window_destroy(GTK_WIDGET (fs));
1641
1642   /* Save the directory name for future file dialogs. */
1643   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1644   set_last_open_dir(dirname);
1645   g_free(cf_name);
1646 }
1647
1648
1649 void
1650 file_save_as_destroy(void)
1651 {
1652   if (file_save_as_w)
1653     window_destroy(file_save_as_w);
1654 }
1655
1656 static void
1657 file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1658 {
1659   /* Note that we no longer have a "Save Capture File As" dialog box. */
1660   file_save_as_w = NULL;
1661 }
1662
1663 /*
1664  * Update various dynamic parts of the range controls; called from outside
1665  * the file dialog code whenever the packet counts change.
1666  */
1667 void
1668 file_export_specified_packets_update_dynamics(void)
1669 {
1670   if (file_export_specified_packets_w == NULL) {
1671     /* We don't currently have a "Export Specified Packets..." dialog box up. */
1672     return;
1673   }
1674
1675   range_update_dynamics(range_tb);
1676 }
1677
1678 void
1679 file_export_specified_packets_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
1680 {
1681 #if _WIN32
1682   win32_export_specified_packets_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
1683 #else /* _WIN32 */
1684   GtkWidget     *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *range_fr, *compressed_cb;
1685
1686   if (file_export_specified_packets_w != NULL) {
1687     /* There's already an "Export Specified Packets" dialog box; reactivate it. */
1688     reactivate_window(file_export_specified_packets_w);
1689     return;
1690   }
1691
1692   /* Default to writing out all displayed packets, in the file's current format. */
1693
1694   /* init the packet range */
1695   packet_range_init(&range);
1696   range.process_filtered = TRUE;
1697   range.include_dependents = TRUE;
1698
1699   /* build the file selection */
1700   file_export_specified_packets_w = file_selection_new ("Wireshark: Export Specified Packets",
1701                                                 FILE_SELECTION_SAVE);
1702
1703   /* Container for each row of widgets */
1704
1705   main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
1706   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1707   file_selection_set_extra_widget(file_export_specified_packets_w, main_vb);
1708   gtk_widget_show(main_vb);
1709
1710   /*** Packet Range frame ***/
1711   range_fr = gtk_frame_new("Packet Range");
1712   gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
1713   gtk_widget_show(range_fr);
1714
1715   /* range table */
1716   range_tb = range_new(&range, TRUE);
1717   gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
1718   gtk_widget_show(range_tb);
1719
1720   /* File type row */
1721   ft_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1722   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
1723   gtk_widget_show(ft_hb);
1724
1725   ft_lb = gtk_label_new("File type:");
1726   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
1727   gtk_widget_show(ft_lb);
1728
1729   ft_combo_box = ws_combo_box_new_text_and_pointer();
1730
1731   /* Generate the list of file types we can save. */
1732   set_file_type_list(ft_combo_box, &cfile);
1733   gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
1734   gtk_widget_show(ft_combo_box);
1735   g_object_set_data(G_OBJECT(file_export_specified_packets_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
1736
1737   /* dynamic values in the range frame */
1738   range_update_dynamics(range_tb);
1739
1740   /* compressed */
1741   compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
1742   gtk_container_add(GTK_CONTAINER(ft_hb), compressed_cb);
1743   gtk_widget_show(compressed_cb);
1744   g_object_set_data(G_OBJECT(file_export_specified_packets_w), E_COMPRESSED_CB_KEY, compressed_cb);
1745
1746   /* Ok: now "select" the default filetype which invokes file_export_specified_packets_select_file_type_cb */
1747   g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_export_specified_packets_select_file_type_cb), NULL);
1748   ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0);
1749
1750   g_signal_connect(file_export_specified_packets_w, "destroy",
1751                    G_CALLBACK(file_export_specified_packets_destroy_cb), NULL);
1752
1753   if (gtk_dialog_run(GTK_DIALOG(file_export_specified_packets_w)) == GTK_RESPONSE_ACCEPT) {
1754     file_export_specified_packets_ok_cb(file_export_specified_packets_w, file_export_specified_packets_w);
1755   } else {
1756     window_destroy(file_export_specified_packets_w);
1757   }
1758 #endif /* _WIN32 */
1759 }
1760
1761 static void
1762 file_export_specified_packets_select_file_type_cb(GtkWidget *w, gpointer data _U_)
1763 {
1764   int new_file_type;
1765   gpointer ptr;
1766   GtkWidget *compressed_cb;
1767
1768   if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(w), &ptr)) {
1769       g_assert_not_reached();  /* Programming error: somehow nothing is active */
1770   }
1771   new_file_type = GPOINTER_TO_INT(ptr);
1772
1773   compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(file_export_specified_packets_w), E_COMPRESSED_CB_KEY);
1774   if (!wtap_dump_can_compress(new_file_type)) {
1775     /* Can't compress this file type; turn off compression and make
1776        the compression checkbox insensitive. */
1777     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compressed_cb), FALSE);
1778     gtk_widget_set_sensitive(compressed_cb, FALSE);
1779   } else
1780     gtk_widget_set_sensitive(compressed_cb, TRUE);
1781 }
1782
1783 static void
1784 file_export_specified_packets_exists_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
1785 {
1786     gchar       *cf_name;
1787
1788     cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data));
1789
1790     switch(btn) {
1791     case(ESD_BTN_OK):
1792         /* save file */
1793         file_export_specified_packets_cb(NULL, data);
1794         break;
1795     case(ESD_BTN_CANCEL):
1796         /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1797          * as this will prevent the user from closing the now existing error
1798          * message, simply close the dialog (this is the best we can do here). */
1799         if (file_export_specified_packets_w)
1800             window_destroy(file_export_specified_packets_w);
1801         break;
1802     default:
1803         g_assert_not_reached();
1804     }
1805     g_free(cf_name);
1806 }
1807
1808 /* user pressed "Export Specified Packets" dialog "Ok" button */
1809 static void
1810 file_export_specified_packets_ok_cb(GtkWidget *w _U_, gpointer fs) {
1811   gchar *cf_name;
1812   gpointer  dialog;
1813
1814   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1815
1816   /* Perhaps the user specified a directory instead of a file.
1817      Check whether they did. */
1818   if (test_for_directory(cf_name) == EISDIR) {
1819     /* It's a directory - set the file selection box to display that
1820        directory, and leave the selection box displayed. */
1821     set_last_open_dir(cf_name);
1822     g_free(cf_name);
1823     file_selection_set_current_folder(fs, get_last_open_dir());
1824     return;
1825   }
1826
1827   /* Check whether the range is valid. */
1828   if (!range_check_validity(&range)) {
1829     /* The range isn't valid; don't dismiss the open dialog box,
1830        just leave it around so that the user can, after they
1831        dismiss the alert box popped up for the error, try again. */
1832     g_free(cf_name);
1833     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1834      * as this will prevent the user from closing the now existing error
1835      * message, simply close the dialog (this is the best we can do here). */
1836     if (file_save_as_w)
1837       window_destroy(GTK_WIDGET (fs));
1838
1839     return;
1840   }
1841
1842   /*
1843    * Check that the from file is not the same as to file
1844    * We do it here so we catch all cases ...
1845    * Unfortunately, the file requester gives us an absolute file
1846    * name and the read file name may be relative (if supplied on
1847    * the command line). From Joerg Mayer.
1848    */
1849   if (files_identical(cfile.filename, cf_name)) {
1850     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1851       "%sCapture file: \"%s\" identical to loaded file!%s\n\n"
1852       "Please choose a different filename.",
1853       simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
1854     g_free(cf_name);
1855     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1856      * as this will prevent the user from closing the now existing error
1857      * message, simply close the dialog (this is the best we can do here). */
1858     if (file_export_specified_packets_w)
1859       window_destroy(GTK_WIDGET (fs));
1860
1861     return;
1862   }
1863
1864   /* don't show the dialog while saving (or asking) */
1865   gtk_widget_hide(GTK_WIDGET (fs));
1866
1867   /* it the file doesn't exist, simply try to write the packets to it */
1868   if (!file_exists(cf_name)) {
1869     file_export_specified_packets_cb(NULL, fs);
1870     g_free(cf_name);
1871     return;
1872   }
1873
1874   /* The file exists.  Ask the user if they want to overwrite it. */
1875   dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
1876       "%s"
1877       "A file named \"%s\" already exists. Do you want to replace it?"
1878       "%s\n\n"
1879       "A file or folder with the same name already exists in that folder. "
1880       "Replacing it will overwrite its current contents.",
1881       simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
1882   simple_dialog_set_cb(dialog, file_export_specified_packets_exists_answered_cb, fs);
1883
1884   g_free(cf_name);
1885 }
1886
1887 /* all tests ok, we only have to write out the packets */
1888 /* (and probably continue with a pending operation) */
1889 static void
1890 file_export_specified_packets_cb(GtkWidget *w _U_, gpointer fs) {
1891   GtkWidget *ft_combo_box;
1892   GtkWidget *compressed_cb;
1893   gchar     *cf_name;
1894   gchar     *dirname;
1895   gpointer   ptr;
1896   int        file_type;
1897   gboolean   compressed;
1898
1899   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1900
1901   compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_COMPRESSED_CB_KEY);
1902   ft_combo_box  = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_FILE_TYPE_COMBO_BOX_KEY);
1903
1904   if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
1905       g_assert_not_reached();  /* Programming error: somehow nothing is active */
1906   }
1907   file_type = GPOINTER_TO_INT(ptr);
1908   compressed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb));
1909
1910   /* Write out the specified packets to the file with the specified name. */
1911   if (cf_export_specified_packets(&cfile, cf_name, &range, file_type,
1912                                   compressed) != CF_OK) {
1913     /* The write failed; don't dismiss the open dialog box,
1914        just leave it around so that the user can, after they
1915        dismiss the alert box popped up for the error, try again. */
1916     g_free(cf_name);
1917     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1918      * as this will prevent the user from closing the now existing error
1919      * message, simply close the dialog (this is the best we can do here). */
1920     if (file_export_specified_packets_w)
1921       window_destroy(GTK_WIDGET (fs));
1922     return;
1923   }
1924
1925   /* The write succeeded; get rid of the file selection box. */
1926   /* cf_export_specified_packets() might already closed our dialog! */
1927   if (file_export_specified_packets_w)
1928     window_destroy(GTK_WIDGET (fs));
1929
1930   /* Save the directory name for future file dialogs.
1931      XXX - should there be separate ones for "Save As" and
1932      "Export Specified Packets"? */
1933   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1934   set_last_open_dir(dirname);
1935   g_free(cf_name);
1936 }
1937
1938 void
1939 file_export_specified_packets_destroy(void)
1940 {
1941   if (file_export_specified_packets_w)
1942     window_destroy(file_export_specified_packets_w);
1943 }
1944
1945 static void
1946 file_export_specified_packets_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1947 {
1948   /* Note that we no longer have a "Export Specified Packets" dialog box. */
1949   file_export_specified_packets_w = NULL;
1950 }
1951
1952 /* Reload a file using the current read and display filters */
1953 void
1954 file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
1955   cf_reload(&cfile);
1956 }
1957
1958 /******************** Color Filters *********************************/
1959 /*
1960  * Keep a static pointer to the current "Color Export" window, if
1961  * any, so that if somebody tries to do "Export"
1962  * while there's already a "Color Export" window up, we just pop
1963  * up the existing one, rather than creating a new one.
1964  */
1965 static GtkWidget *file_color_import_w;
1966
1967 /* sets the file path to the global color filter file.
1968    WARNING: called by both the import and the export dialog.
1969 */
1970 static void
1971 color_global_cb(GtkWidget *widget _U_, gpointer data)
1972 {
1973   GtkWidget *fs_widget = (GtkWidget *)data;
1974   gchar *path;
1975
1976   /* decide what file to open (from dfilter code) */
1977   path = get_datafile_path("colorfilters");
1978
1979   gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(fs_widget), path);
1980
1981   g_free(path);
1982 }
1983
1984 /* Import color filters */
1985 void
1986 file_color_import_cmd_cb(GtkWidget *color_filters, gpointer filter_list _U_)
1987 {
1988 #if _WIN32
1989   win32_import_color_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), color_filters);
1990 #else /* _WIN32 */
1991   GtkWidget     *main_vb, *cfglobal_but;
1992
1993   /* No Apply button, and "OK" just sets our text widget, it doesn't
1994      activate it (i.e., it doesn't cause us to try to open the file). */
1995
1996   if (file_color_import_w != NULL) {
1997     /* There's already an "Import Color Filters" dialog box; reactivate it. */
1998     reactivate_window(file_color_import_w);
1999     return;
2000   }
2001
2002   file_color_import_w = file_selection_new("Wireshark: Import Color Filters",
2003                                            FILE_SELECTION_OPEN);
2004
2005   /* Container for each row of widgets */
2006   main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
2007   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
2008   file_selection_set_extra_widget(file_color_import_w, main_vb);
2009   gtk_widget_show(main_vb);
2010
2011
2012   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
2013   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
2014   g_signal_connect(cfglobal_but, "clicked",
2015                    G_CALLBACK(color_global_cb), file_color_import_w);
2016   gtk_widget_show(cfglobal_but);
2017
2018   g_signal_connect(file_color_import_w, "destroy",
2019                    G_CALLBACK(file_color_import_destroy_cb), NULL);
2020
2021
2022   if (gtk_dialog_run(GTK_DIALOG(file_color_import_w)) == GTK_RESPONSE_ACCEPT)
2023   {
2024       file_color_import_ok_cb(file_color_import_w, color_filters);
2025   }
2026   else window_destroy(file_color_import_w);
2027 #endif /* _WIN32 */
2028 }
2029
2030 static void
2031 file_color_import_ok_cb(GtkWidget *w, gpointer color_filters) {
2032   gchar     *cf_name, *s;
2033   GtkWidget *fs = gtk_widget_get_toplevel(w);
2034
2035   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
2036
2037   /* Perhaps the user specified a directory instead of a file.
2038      Check whether they did. */
2039   if (test_for_directory(cf_name) == EISDIR) {
2040     /* It's a directory - set the file selection box to display that
2041        directory, don't try to open the directory as a color filter file. */
2042     set_last_open_dir(cf_name);
2043     g_free(cf_name);
2044     file_selection_set_current_folder(fs, get_last_open_dir());
2045     return;
2046   }
2047
2048   /* Try to open the color filter file. */
2049
2050   if (!color_filters_import(cf_name, color_filters)) {
2051     /* We couldn't open it; don't dismiss the open dialog box,
2052        just leave it around so that the user can, after they
2053        dismiss the alert box popped up for the open error,
2054        try again. */
2055     g_free(cf_name);
2056     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
2057      * as this will prevent the user from closing the now existing error
2058      * message, simply close the dialog (this is the best we can do here). */
2059     window_destroy(GTK_WIDGET (fs));
2060
2061     return;
2062   }
2063
2064   /* We've crossed the Rubicon; get rid of the file selection box. */
2065   window_destroy(GTK_WIDGET (fs));
2066
2067   /* Save the name of the containing directory specified in the path name,
2068      if any; we can write over cf_name, which is a good thing, given that
2069      "get_dirname()" does write over its argument. */
2070   s = get_dirname(cf_name);
2071   set_last_open_dir(s);
2072
2073   g_free(cf_name);
2074 }
2075
2076 static void
2077 file_color_import_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
2078 {
2079   /* Note that we no longer have a "Open Capture File" dialog box. */
2080   file_color_import_w = NULL;
2081 }
2082
2083 static GtkWidget *file_color_export_w;
2084 /*
2085  * Set the "Export only selected filters" toggle button as appropriate for
2086  * the current output file type and count of selected filters.
2087  *
2088  * Called when the "Export" dialog box is created and when the selected
2089  * count changes.
2090  */
2091 static void
2092 color_set_export_selected_sensitive(GtkWidget * cfselect_cb)
2093 {
2094   if (file_color_export_w == NULL) {
2095     /* We don't currently have an "Export" dialog box up. */
2096     return;
2097   }
2098
2099   /* We can request that only the selected filters be saved only if
2100         there *are* selected filters. */
2101   if (color_selected_count() != 0)
2102     gtk_widget_set_sensitive(cfselect_cb, TRUE);
2103   else {
2104     /* Force the "Export only selected filters" toggle to "false", turn
2105        off the flag it controls. */
2106     color_selected = FALSE;
2107     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
2108     gtk_widget_set_sensitive(cfselect_cb, FALSE);
2109   }
2110 }
2111
2112 static void
2113 color_toggle_selected_cb(GtkWidget *widget, gpointer data _U_)
2114 {
2115   color_selected = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
2116 }
2117
2118 void
2119 file_color_export_cmd_cb(GtkWidget *w _U_, gpointer filter_list)
2120 {
2121 #if _WIN32
2122   win32_export_color_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), filter_list);
2123 #else /* _WIN32 */
2124   GtkWidget *main_vb, *cfglobal_but;
2125   GtkWidget *cfselect_cb;
2126
2127   if (file_color_export_w != NULL) {
2128     /* There's already an "Color Filter Export" dialog box; reactivate it. */
2129     reactivate_window(file_color_export_w);
2130     return;
2131   }
2132
2133   color_selected   = FALSE;
2134
2135   file_color_export_w = file_selection_new("Wireshark: Export Color Filters",
2136                                            FILE_SELECTION_SAVE);
2137
2138   /* Container for each row of widgets */
2139   main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
2140   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
2141   file_selection_set_extra_widget(file_color_export_w, main_vb);
2142   gtk_widget_show(main_vb);
2143
2144   cfselect_cb = gtk_check_button_new_with_label("Export only selected filters");
2145   gtk_container_add(GTK_CONTAINER(main_vb), cfselect_cb);
2146   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
2147   g_signal_connect(cfselect_cb, "toggled",
2148                    G_CALLBACK(color_toggle_selected_cb), NULL);
2149   gtk_widget_show(cfselect_cb);
2150   color_set_export_selected_sensitive(cfselect_cb);
2151
2152   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
2153   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
2154   g_signal_connect(cfglobal_but, "clicked",
2155                    G_CALLBACK(color_global_cb), file_color_export_w);
2156   gtk_widget_show(cfglobal_but);
2157
2158   g_signal_connect(file_color_export_w, "destroy",
2159                    G_CALLBACK(file_color_export_destroy_cb), NULL);
2160
2161   if (gtk_dialog_run(GTK_DIALOG(file_color_export_w)) == GTK_RESPONSE_ACCEPT)
2162   {
2163     file_color_export_ok_cb(file_color_export_w, filter_list);
2164   }
2165   else window_destroy(file_color_export_w);
2166 #endif /* _WIN32 */
2167 }
2168
2169 static void
2170 file_color_export_ok_cb(GtkWidget *w, gpointer filter_list) {
2171   gchar *cf_name;
2172   gchar *dirname;
2173   GtkWidget *fs = gtk_widget_get_toplevel(w);
2174
2175   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
2176
2177   /* Perhaps the user specified a directory instead of a file.
2178      Check whether they did. */
2179   if (test_for_directory(cf_name) == EISDIR) {
2180     /* It's a directory - set the file selection box to display that
2181        directory, and leave the selection box displayed. */
2182     set_last_open_dir(cf_name);
2183     g_free(cf_name);
2184     file_selection_set_current_folder(fs, get_last_open_dir());
2185     return;
2186   }
2187
2188   /* Write out the filters (all, or only the ones that are currently
2189      displayed or selected) to the file with the specified name. */
2190
2191    if (!color_filters_export(cf_name, filter_list, color_selected))
2192    {
2193     /* The write failed; don't dismiss the open dialog box,
2194        just leave it around so that the user can, after they
2195        dismiss the alert box popped up for the error, try again. */
2196        g_free(cf_name);
2197
2198     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
2199      * as this will prevent the user from closing the now existing error
2200      * message, simply close the dialog (this is the best we can do here). */
2201      window_destroy(GTK_WIDGET (fs));
2202
2203      return;
2204    }
2205
2206   /* The write succeeded; get rid of the file selection box. */
2207   window_destroy(GTK_WIDGET (fs));
2208
2209   /* Save the directory name for future file dialogs. */
2210   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
2211   set_last_open_dir(dirname);
2212   g_free(cf_name);
2213 }
2214
2215 static void
2216 file_color_export_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
2217 {
2218   file_color_export_w = NULL;
2219 }