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