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