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