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