077646e647f1c99df239e9456e781f24843cc7f6
[jlayton/wireshark.git] / gtk / capture_file_dlg.c
1 /* capture_file_dlg.c
2  * Dialog boxes for handling capture files
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32
33 #include <string.h>
34
35 #include <gtk/gtk.h>
36
37 #include "packet-range.h"
38 #include <epan/filesystem.h>
39 #include <epan/addr_resolv.h>
40 #include <epan/prefs.h>
41
42 #include "../globals.h"
43 #include "../alert_box.h"
44 #include "../simple_dialog.h"
45 #include "../color.h"
46 #include "../ui_util.h"
47 #include "../color_filters.h"
48 #include "../merge.h"
49 #include "../util.h"
50 #include <wsutil/file_util.h>
51
52 #include "gtk/gtkglobals.h"
53 #include "gtk/keys.h"
54 #include "gtk/filter_dlg.h"
55 #include "gtk/gui_utils.h"
56 #include "gtk/dlg_utils.h"
57 #include "gtk/file_dlg.h"
58 #include "gtk/capture_file_dlg.h"
59 #include "gtk/drag_and_drop.h"
60 #include "gtk/main.h"
61 #include "gtk/menus.h"
62 #include "gtk/recent.h"
63 #include "gtk/color_dlg.h"
64 #ifdef NEW_PACKET_LIST
65 #include "gtk/new_packet_list.h"
66 #endif
67 #ifdef HAVE_LIBPCAP
68 #include "gtk/capture_dlg.h"
69 #endif
70 #include "gtk/stock_icons.h"
71 #include "gtk/range_utils.h"
72 #include "gtk/filter_autocomplete.h"
73
74 #if _WIN32
75 #include <gdk/gdkwin32.h>
76 #include <windows.h>
77 #include "gtk/file_dlg_win32.h"
78 #endif
79
80
81 static void file_open_ok_cb(GtkWidget *w, gpointer fs);
82 static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
83 static void file_merge_ok_cb(GtkWidget *w, gpointer fs);
84 static void file_merge_destroy_cb(GtkWidget *win, gpointer user_data);
85 static void select_file_type_cb(GtkWidget *w, gpointer data);
86 static void file_save_as_ok_cb(GtkWidget *w, gpointer fs);
87 static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
88 static void file_color_import_ok_cb(GtkWidget *w, gpointer filter_list);
89 static void file_color_import_destroy_cb(GtkWidget *win, gpointer user_data);
90 static void file_color_export_ok_cb(GtkWidget *w, gpointer filter_list);
91 static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
92 static void set_file_type_list(GtkWidget *option_menu);
93
94 #define E_FILE_M_RESOLVE_KEY      "file_dlg_mac_resolve_key"
95 #define E_FILE_N_RESOLVE_KEY      "file_dlg_network_resolve_key"
96 #define E_FILE_T_RESOLVE_KEY      "file_dlg_transport_resolve_key"
97
98 #define E_MERGE_PREPEND_KEY       "merge_dlg_prepend_key"
99 #define E_MERGE_CHRONO_KEY            "merge_dlg_chrono_key"
100 #define E_MERGE_APPEND_KEY            "merge_dlg_append_key"
101
102
103 #define PREVIEW_TABLE_KEY       "preview_table_key"
104 #define PREVIEW_FILENAME_KEY    "preview_filename_key"
105 #define PREVIEW_FORMAT_KEY      "preview_format_key"
106 #define PREVIEW_SIZE_KEY        "preview_size_key"
107 #define PREVIEW_ELAPSED_KEY     "preview_elapsed_key"
108 #define PREVIEW_PACKETS_KEY     "preview_packets_key"
109 #define PREVIEW_FIRST_KEY       "preview_first_key"
110
111
112 /*
113  * Keep a static pointer to the current "Save Capture File As" window, if
114  * any, so that if somebody tries to do "File:Save" or "File:Save As"
115  * while there's already a "Save Capture File As" window up, we just pop
116  * up the existing one, rather than creating a new one.
117  */
118 static GtkWidget *file_save_as_w;
119
120 /* XXX - can we make these not be static? */
121 static packet_range_t range;
122 static gboolean color_selected;
123 static int filetype;
124 static GtkWidget *ft_om;
125 static GtkWidget *range_tb;
126
127 #define PREVIEW_STR_MAX         200
128
129
130 /* set a new filename for the preview widget */
131 static wtap *
132 preview_set_filename(GtkWidget *prev, const gchar *cf_name)
133 {
134     GtkWidget  *label;
135     wtap       *wth;
136     int         err = 0;
137     gchar      *err_info;
138     gchar       string_buff[PREVIEW_STR_MAX];
139     gint64      filesize;
140
141
142     /* init preview labels */
143     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
144     gtk_label_set_text(GTK_LABEL(label), "-");
145     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
146     gtk_label_set_text(GTK_LABEL(label), "-");
147     label = g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
148     gtk_label_set_text(GTK_LABEL(label), "-");
149     label = g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
150     gtk_label_set_text(GTK_LABEL(label), "-");
151     label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
152     gtk_label_set_text(GTK_LABEL(label), "-");
153     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
154     gtk_label_set_text(GTK_LABEL(label), "-");
155
156     if(!cf_name) {
157         return NULL;
158     }
159
160     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
161     gtk_label_set_text(GTK_LABEL(label), get_basename(cf_name));
162
163     if (test_for_directory(cf_name) == EISDIR) {
164         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
165         gtk_label_set_text(GTK_LABEL(label), "directory");
166         return NULL;
167     }
168
169     wth = wtap_open_offline(cf_name, &err, &err_info, TRUE);
170     if (wth == NULL) {
171         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
172         if(err == WTAP_ERR_FILE_UNKNOWN_FORMAT) {
173             gtk_label_set_text(GTK_LABEL(label), "unknown file format");
174         } else {
175             gtk_label_set_text(GTK_LABEL(label), "error opening file");
176         }
177         return NULL;
178     }
179
180     /* Find the size of the file. */
181     filesize = wtap_file_size(wth, &err);
182     if (filesize == -1) {
183         gtk_label_set_text(GTK_LABEL(label), "error getting file size");
184         wtap_close(wth);
185         return NULL;
186     }
187     g_snprintf(string_buff, PREVIEW_STR_MAX, "%" G_GINT64_MODIFIER "d bytes", filesize);
188     label = g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
189     gtk_label_set_text(GTK_LABEL(label), string_buff);
190
191     /* type */
192     g_strlcpy(string_buff, wtap_file_type_string(wtap_file_type(wth)), PREVIEW_STR_MAX);
193     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
194     gtk_label_set_text(GTK_LABEL(label), string_buff);
195
196     return wth;
197 }
198
199
200 /* do a preview run on the currently selected capture file */
201 static void
202 preview_do(GtkWidget *prev, wtap *wth)
203 {
204     GtkWidget  *label;
205     unsigned int elapsed_time;
206     time_t      time_preview;
207     time_t      time_current;
208     int         err = 0;
209     gchar      *err_info;
210     gint64      data_offset;
211     const struct wtap_pkthdr *phdr;
212     double      start_time = 0; /* seconds, with nsec resolution */
213     double      stop_time = 0;  /* seconds, with nsec resolution */
214     double      cur_time;
215     unsigned int packets = 0;
216     gboolean    is_breaked = FALSE;
217     gchar       string_buff[PREVIEW_STR_MAX];
218     time_t      ti_time;
219     struct tm  *ti_tm;
220
221
222     time(&time_preview);
223     while ( (wtap_read(wth, &err, &err_info, &data_offset)) ) {
224         phdr = wtap_phdr(wth);
225         cur_time = wtap_nstime_to_sec(&phdr->ts);
226         if(packets == 0) {
227             start_time  = cur_time;
228             stop_time = cur_time;
229         }
230         if (cur_time < start_time) {
231             start_time = cur_time;
232         }
233         if (cur_time > stop_time){
234             stop_time = cur_time;
235         }
236
237         packets++;
238         if(packets%1000 == 0) {
239             /* do we have a timeout? */
240             time(&time_current);
241             if(time_current-time_preview >= (time_t) prefs.gui_fileopen_preview) {
242                 is_breaked = TRUE;
243                 break;
244             }
245         }
246     }
247
248     if(err != 0) {
249         g_snprintf(string_buff, PREVIEW_STR_MAX, "error after reading %u packets", packets);
250         label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
251         gtk_label_set_text(GTK_LABEL(label), string_buff);
252         wtap_close(wth);
253         return;
254     }
255
256     /* packet count */
257     if(is_breaked) {
258         g_snprintf(string_buff, PREVIEW_STR_MAX, "more than %u packets (preview timeout)", packets);
259     } else {
260         g_snprintf(string_buff, PREVIEW_STR_MAX, "%u", packets);
261     }
262     label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
263     gtk_label_set_text(GTK_LABEL(label), string_buff);
264
265     /* first packet */
266     ti_time = (long)start_time;
267     ti_tm = localtime( &ti_time );
268         if(ti_tm) {
269                 g_snprintf(string_buff, PREVIEW_STR_MAX,
270                                  "%04d-%02d-%02d %02d:%02d:%02d",
271                                  ti_tm->tm_year + 1900,
272                                  ti_tm->tm_mon + 1,
273                                  ti_tm->tm_mday,
274                                  ti_tm->tm_hour,
275                                  ti_tm->tm_min,
276                                  ti_tm->tm_sec);
277         } else {
278                 g_snprintf(string_buff, PREVIEW_STR_MAX, "?");
279         }
280         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
281     gtk_label_set_text(GTK_LABEL(label), string_buff);
282
283     /* elapsed time */
284     elapsed_time = (unsigned int)(stop_time-start_time);
285     if(elapsed_time/86400) {
286       g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u days %02u:%02u:%02u",
287         elapsed_time/86400, elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
288     } else {
289       g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u:%02u:%02u",
290         elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
291     }
292     if(is_breaked) {
293       g_snprintf(string_buff, PREVIEW_STR_MAX, "unknown");
294     }
295     label = g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
296     gtk_label_set_text(GTK_LABEL(label), string_buff);
297
298     wtap_close(wth);
299 }
300
301 #if 0
302 /* as the dialog layout will look very ugly when using the file chooser preview mechanism,
303    simply use the same layout as in GTK2.0 */
304 static void
305 update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
306 {
307     GtkWidget *prev = GTK_WIDGET (data);
308     char *cf_name;
309     gboolean have_preview;
310
311     cf_name = gtk_file_chooser_get_preview_filename (file_chooser);
312
313     have_preview = preview_set_filename(prev, cf_name);
314
315     g_free (cf_name);
316
317     have_preview = TRUE;
318     gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
319 }
320 #endif
321
322
323 /* the filename text entry changed */
324 static void
325 file_open_entry_changed(GtkWidget *w _U_, gpointer file_sel)
326 {
327     GtkWidget *prev = g_object_get_data(G_OBJECT(file_sel), PREVIEW_TABLE_KEY);
328     gchar *cf_name;
329     gboolean have_preview;
330     wtap       *wth;
331
332     /* get the filename */
333     cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_sel));
334
335     /* set the filename to the preview */
336     wth = preview_set_filename(prev, cf_name);
337     have_preview = (wth != NULL);
338
339     g_free(cf_name);
340
341     /* make the preview widget sensitive */
342     gtk_widget_set_sensitive(prev, have_preview);
343
344     /* make the open/save/... dialog button sensitive */
345
346     gtk_dialog_set_response_sensitive(file_sel, GTK_RESPONSE_ACCEPT, have_preview);
347
348     /* do the actual preview */
349     if(have_preview)
350         preview_do(prev, wth);
351 }
352
353
354 /* copied from summary_dlg.c */
355 static GtkWidget *
356 add_string_to_table_sensitive(GtkWidget *list, guint *row, const gchar *title, const gchar *value, gboolean sensitive)
357 {
358     GtkWidget *label;
359     gchar     *indent;
360
361     if(strlen(value) != 0) {
362         indent = g_strdup_printf("   %s", title);
363     } else {
364         indent = g_strdup(title);
365     }
366     label = gtk_label_new(indent);
367     g_free(indent);
368     gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
369     gtk_widget_set_sensitive(label, sensitive);
370     gtk_table_attach_defaults(GTK_TABLE(list), label, 0, 1, *row, *row+1);
371
372     label = gtk_label_new(value);
373     gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
374     gtk_widget_set_sensitive(label, sensitive);
375     gtk_table_attach_defaults(GTK_TABLE(list), label, 1, 2, *row, *row+1);
376
377     *row = *row + 1;
378
379     return label;
380 }
381
382 static GtkWidget *
383 add_string_to_table(GtkWidget *list, guint *row, const gchar *title, const gchar *value)
384 {
385     return add_string_to_table_sensitive(list, row, title, value, TRUE);
386 }
387
388
389
390 static GtkWidget *
391 preview_new(void)
392 {
393     GtkWidget *table, *label;
394     guint         row;
395
396     table = gtk_table_new(1, 2, FALSE);
397     gtk_table_set_col_spacings(GTK_TABLE(table), 6);
398     gtk_table_set_row_spacings(GTK_TABLE(table), 3);
399     row = 0;
400
401     label = add_string_to_table(table, &row, "Filename:", "-");
402     gtk_widget_set_size_request(label, DEF_WIDTH/3, -1);
403     g_object_set_data(G_OBJECT(table), PREVIEW_FILENAME_KEY, label);
404     label = add_string_to_table(table, &row, "Format:", "-");
405     g_object_set_data(G_OBJECT(table), PREVIEW_FORMAT_KEY, label);
406     label = add_string_to_table(table, &row, "Size:", "-");
407     g_object_set_data(G_OBJECT(table), PREVIEW_SIZE_KEY, label);
408     label = add_string_to_table(table, &row, "Packets:", "-");
409     g_object_set_data(G_OBJECT(table), PREVIEW_PACKETS_KEY, label);
410     label = add_string_to_table(table, &row, "First Packet:", "-");
411     g_object_set_data(G_OBJECT(table), PREVIEW_FIRST_KEY, label);
412     label = add_string_to_table(table, &row, "Elapsed time:", "-");
413     g_object_set_data(G_OBJECT(table), PREVIEW_ELAPSED_KEY, label);
414
415     return table;
416 }
417
418 /*
419  * Keep a static pointer to the current "Open Capture File" window, if
420  * any, so that if somebody tries to do "File:Open" while there's already
421  * an "Open Capture File" window up, we just pop up the existing one,
422  * rather than creating a new one.
423  */
424 static GtkWidget *file_open_w;
425
426 /* Open a file */
427 static void
428 file_open_cmd(GtkWidget *w)
429 {
430 #if _WIN32
431   win32_open_file(GDK_WINDOW_HWND(top_level->window));
432 #else /* _WIN32 */
433   GtkWidget     *main_hb, *main_vb, *filter_hbox, *filter_bt, *filter_te,
434                 *m_resolv_cb, *n_resolv_cb, *t_resolv_cb, *prev;
435   GtkTooltips *tooltips = gtk_tooltips_new();
436   /* No Apply button, and "OK" just sets our text widget, it doesn't
437      activate it (i.e., it doesn't cause us to try to open the file). */
438   static construct_args_t args = {
439         "Wireshark: Read Filter",
440         FALSE,
441         FALSE,
442     TRUE
443   };
444
445   if (file_open_w != NULL) {
446     /* There's already an "Open Capture File" dialog box; reactivate it. */
447     reactivate_window(file_open_w);
448     return;
449   }
450
451   file_open_w = file_selection_new("Wireshark: Open Capture File",
452                                    FILE_SELECTION_OPEN);
453   /* it's annoying, that the file chooser dialog is already shown here,
454      so we cannot use the correct gtk_window_set_default_size() to resize it */
455   gtk_widget_set_size_request(file_open_w, DEF_WIDTH, DEF_HEIGHT);
456
457   switch (prefs.gui_fileopen_style) {
458
459   case FO_STYLE_LAST_OPENED:
460     /* The user has specified that we should start out in the last directory
461        we looked in.  If we've already opened a file, use its containing
462        directory, if we could determine it, as the directory, otherwise
463        use the "last opened" directory saved in the preferences file if
464        there was one. */
465     /* This is now the default behaviour in file_selection_new() */
466     break;
467
468   case FO_STYLE_SPECIFIED:
469     /* The user has specified that we should always start out in a
470        specified directory; if they've specified that directory,
471        start out by showing the files in that dir. */
472     if (prefs.gui_fileopen_dir[0] != '\0')
473       file_selection_set_current_folder(file_open_w, prefs.gui_fileopen_dir);
474     break;
475   }
476
477
478   main_hb = gtk_hbox_new(FALSE, 3);
479   file_selection_set_extra_widget(file_open_w, main_hb);
480   gtk_widget_show(main_hb);
481
482   /* Container for each row of widgets */
483   main_vb = gtk_vbox_new(FALSE, 3);
484   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
485   gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
486   gtk_widget_show(main_vb);
487
488   /* filter row */
489   filter_hbox = gtk_hbox_new(FALSE, 1);
490   gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
491   gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
492   gtk_widget_show(filter_hbox);
493
494   filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
495   g_signal_connect(filter_bt, "clicked",
496                    G_CALLBACK(display_filter_construct_cb), &args);
497   g_signal_connect(filter_bt, "destroy",
498                    G_CALLBACK(filter_button_destroy_cb), NULL);
499   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
500   gtk_widget_show(filter_bt);
501   gtk_tooltips_set_tip(tooltips, filter_bt,
502     "Open the \"Display Filter\" dialog, to edit/apply filters", NULL);
503
504   filter_te = gtk_entry_new();
505   g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
506   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
507   g_signal_connect(filter_te, "changed",
508                    G_CALLBACK(filter_te_syntax_check_cb), NULL);
509   g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
510   g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
511   g_signal_connect(file_open_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
512   colorize_filter_te_as_empty(filter_te);
513   gtk_widget_show(filter_te);
514   gtk_tooltips_set_tip(tooltips, filter_te, "Enter a display filter.", NULL);
515
516   g_object_set_data(G_OBJECT(file_open_w), E_RFILTER_TE_KEY, filter_te);
517
518   /* resolve buttons */
519   m_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _MAC name resolution");
520   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_resolv_cb),
521         g_resolv_flags & RESOLV_MAC);
522   gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
523   g_object_set_data(G_OBJECT(file_open_w),
524                   E_FILE_M_RESOLVE_KEY, m_resolv_cb);
525   gtk_widget_show(m_resolv_cb);
526
527   n_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _network name resolution");
528   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(n_resolv_cb),
529         g_resolv_flags & RESOLV_NETWORK);
530   gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
531   gtk_widget_show(n_resolv_cb);
532   g_object_set_data(G_OBJECT(file_open_w), E_FILE_N_RESOLVE_KEY, n_resolv_cb);
533   t_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _transport name resolution");
534   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(t_resolv_cb),
535         g_resolv_flags & RESOLV_TRANSPORT);
536   gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
537   gtk_widget_show(t_resolv_cb);
538   g_object_set_data(G_OBJECT(file_open_w), E_FILE_T_RESOLVE_KEY, t_resolv_cb);
539
540   g_signal_connect(file_open_w, "destroy",
541                    G_CALLBACK(file_open_destroy_cb), NULL);
542
543   /* preview widget */
544   prev = preview_new();
545   g_object_set_data(G_OBJECT(file_open_w), PREVIEW_TABLE_KEY, prev);
546   gtk_widget_show_all(prev);
547   gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
548
549   g_signal_connect(GTK_FILE_CHOOSER(file_open_w), "selection-changed",
550                    G_CALLBACK(file_open_entry_changed), file_open_w);
551   file_open_entry_changed(file_open_w, file_open_w);
552
553   g_object_set_data(G_OBJECT(file_open_w), E_DFILTER_TE_KEY,
554                     g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
555   if (gtk_dialog_run(GTK_DIALOG(file_open_w)) == GTK_RESPONSE_ACCEPT)
556   {
557     file_open_ok_cb(file_open_w, file_open_w);
558   }
559   else window_destroy(file_open_w);
560 #endif /* _WIN32 */
561 }
562
563 static void file_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
564 {
565     switch(btn) {
566     case(ESD_BTN_SAVE):
567         /* save file first */
568         file_save_as_cmd(after_save_open_dialog, data);
569         break;
570     case(ESD_BTN_DONT_SAVE):
571         cf_close(&cfile);
572         file_open_cmd(data);
573         break;
574     case(ESD_BTN_CANCEL):
575         break;
576     default:
577         g_assert_not_reached();
578     }
579 }
580
581 void
582 file_open_cmd_cb(GtkWidget *widget, gpointer data _U_) {
583   gpointer  dialog;
584
585   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
586     /* user didn't save his current file, ask him */
587     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
588                 "%sSave capture file before opening a new one?%s\n\n"
589                 "If you open a new capture file without saving, your capture data will be discarded.",
590                 simple_dialog_primary_start(), simple_dialog_primary_end());
591     simple_dialog_set_cb(dialog, file_open_answered_cb, widget);
592   } else {
593     /* unchanged file, just open a new one */
594     file_open_cmd(widget);
595   }
596 }
597
598 /* user pressed "open" button */
599 static void
600 file_open_ok_cb(GtkWidget *w, gpointer fs) {
601   gchar       *cf_name, *s;
602   const gchar *rfilter;
603   GtkWidget   *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
604   dfilter_t   *rfcode = NULL;
605   int          err;
606
607   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
608   filter_te = g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
609   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
610   if (!dfilter_compile(rfilter, &rfcode)) {
611     bad_dfilter_alert_box(rfilter);
612     g_free(cf_name);
613     return;
614   }
615
616   /* Perhaps the user specified a directory instead of a file.
617      Check whether they did. */
618   if (test_for_directory(cf_name) == EISDIR) {
619         /* It's a directory - set the file selection box to display that
620            directory, don't try to open the directory as a capture file. */
621         set_last_open_dir(cf_name);
622         g_free(cf_name);
623         file_selection_set_current_folder(fs, get_last_open_dir());
624         return;
625   }
626
627   /* Try to open the capture file. */
628   if (cf_open(&cfile, cf_name, FALSE, &err) != CF_OK) {
629     /* We couldn't open it; don't dismiss the open dialog box,
630        just leave it around so that the user can, after they
631        dismiss the alert box popped up for the open error,
632        try again. */
633     if (rfcode != NULL)
634       dfilter_free(rfcode);
635     g_free(cf_name);
636
637     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
638      * as this will prevent the user from closing the now existing error
639      * message, simply close the dialog (this is the best we can do here). */
640     if (file_open_w)
641       window_destroy(file_open_w);
642
643     return;
644   }
645
646   /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
647      it closed the previous capture file, and thus destroyed any
648      previous read filter attached to "cf"). */
649   cfile.rfcode = rfcode;
650
651   /* Set the global resolving variable */
652   g_resolv_flags = prefs.name_resolve;
653   m_resolv_cb = g_object_get_data(G_OBJECT(w), E_FILE_M_RESOLVE_KEY);
654   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (m_resolv_cb)))
655     g_resolv_flags |= RESOLV_MAC;
656   else
657     g_resolv_flags &= ~RESOLV_MAC;
658   n_resolv_cb = g_object_get_data(G_OBJECT(w), E_FILE_N_RESOLVE_KEY);
659   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (n_resolv_cb)))
660     g_resolv_flags |= RESOLV_NETWORK;
661   else
662     g_resolv_flags &= ~RESOLV_NETWORK;
663   t_resolv_cb = g_object_get_data(G_OBJECT(w), E_FILE_T_RESOLVE_KEY);
664   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (t_resolv_cb)))
665     g_resolv_flags |= RESOLV_TRANSPORT;
666   else
667     g_resolv_flags &= ~RESOLV_TRANSPORT;
668
669   /* We've crossed the Rubicon; get rid of the file selection box. */
670   window_destroy(GTK_WIDGET (fs));
671
672   switch (cf_read(&cfile)) {
673
674   case CF_READ_OK:
675   case CF_READ_ERROR:
676     /* Just because we got an error, that doesn't mean we were unable
677        to read any of the file; we handle what we could get from the
678        file. */
679     break;
680
681   case CF_READ_ABORTED:
682     /* The user bailed out of re-reading the capture file; the
683        capture file has been closed - just free the capture file name
684        string and return (without changing the last containing
685        directory). */
686     g_free(cf_name);
687     return;
688   }
689
690   /* Save the name of the containing directory specified in the path name,
691      if any; we can write over cf_name, which is a good thing, given that
692      "get_dirname()" does write over its argument. */
693   s = get_dirname(cf_name);
694   set_last_open_dir(s);
695 #ifndef NEW_PACKET_LIST
696   gtk_widget_grab_focus(packet_list);
697 #endif
698
699   g_free(cf_name);
700 }
701
702 static void
703 file_open_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
704 {
705   /* Note that we no longer have a "Open Capture File" dialog box. */
706   file_open_w = NULL;
707 }
708
709 /*
710  * Keep a static pointer to the current "Merge Capture File" window, if
711  * any, so that if somebody tries to do "File:Merge" while there's already
712  * an "Merge Capture File" window up, we just pop up the existing one,
713  * rather than creating a new one.
714  */
715 static GtkWidget *file_merge_w;
716
717 /* Merge existing with another file */
718 static void
719 file_merge_cmd(GtkWidget *w)
720 {
721 #if _WIN32
722   win32_merge_file(GDK_WINDOW_HWND(top_level->window));
723 #ifdef NEW_PACKET_LIST
724   new_packet_list_freeze();
725   new_packet_list_thaw();
726 #else
727   packet_list_freeze();
728   packet_list_thaw();
729 #endif /* NEW_PACKET_LIST */
730 #else /* _WIN32 */
731   GtkWidget     *main_hb, *main_vb, *ft_hb, *ft_lb, *filter_hbox,
732                 *filter_bt, *filter_te, *prepend_rb, *chrono_rb,
733                 *append_rb, *prev;
734   GtkTooltips *tooltips = gtk_tooltips_new();
735   /* No Apply button, and "OK" just sets our text widget, it doesn't
736      activate it (i.e., it doesn't cause us to try to open the file). */
737   static construct_args_t args = {
738         "Wireshark: Read Filter",
739         FALSE,
740         FALSE,
741     TRUE
742   };
743
744   if (file_merge_w != NULL) {
745     /* There's already an "Merge Capture File" dialog box; reactivate it. */
746     reactivate_window(file_merge_w);
747     return;
748   }
749
750   /* Default to saving all packets, in the file's current format. */
751   filetype = cfile.cd_t;
752
753   file_merge_w = file_selection_new("Wireshark: Merge with Capture File",
754                                    FILE_SELECTION_OPEN);
755   /* it's annoying, that the file chooser dialog is already shown here,
756      so we cannot use the correct gtk_window_set_default_size() to resize it */
757   gtk_widget_set_size_request(file_merge_w, DEF_WIDTH, DEF_HEIGHT);
758
759   switch (prefs.gui_fileopen_style) {
760
761   case FO_STYLE_LAST_OPENED:
762     /* The user has specified that we should start out in the last directory
763        we looked in.  If we've already opened a file, use its containing
764        directory, if we could determine it, as the directory, otherwise
765        use the "last opened" directory saved in the preferences file if
766        there was one. */
767     /* This is now the default behaviour in file_selection_new() */
768     break;
769
770   case FO_STYLE_SPECIFIED:
771     /* The user has specified that we should always start out in a
772        specified directory; if they've specified that directory,
773        start out by showing the files in that dir. */
774     if (prefs.gui_fileopen_dir[0] != '\0')
775       file_selection_set_current_folder(file_merge_w, prefs.gui_fileopen_dir);
776     break;
777   }
778
779   main_hb = gtk_hbox_new(FALSE, 3);
780   file_selection_set_extra_widget(file_merge_w, main_hb);
781   gtk_widget_show(main_hb);
782
783   /* Container for each row of widgets */
784   main_vb = gtk_vbox_new(FALSE, 3);
785   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
786   gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
787   gtk_widget_show(main_vb);
788
789   /* File type row */
790   range_tb = NULL;
791   ft_hb = gtk_hbox_new(FALSE, 3);
792   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
793   gtk_widget_show(ft_hb);
794
795   ft_lb = gtk_label_new("Merged output file type:");
796   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
797   gtk_widget_show(ft_lb);
798
799   ft_om = gtk_option_menu_new();
800
801   /* Generate the list of file types we can save. */
802   set_file_type_list(ft_om);
803   gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
804   gtk_widget_show(ft_om);
805
806   filter_hbox = gtk_hbox_new(FALSE, 1);
807   gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
808   gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
809   gtk_widget_show(filter_hbox);
810
811   filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
812   g_signal_connect(filter_bt, "clicked",
813                    G_CALLBACK(display_filter_construct_cb), &args);
814   g_signal_connect(filter_bt, "destroy",
815                    G_CALLBACK(filter_button_destroy_cb), NULL);
816   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
817   gtk_widget_show(filter_bt);
818   gtk_tooltips_set_tip(tooltips, filter_bt,
819     "Open the \"Display Filter\" dialog, to edit/apply filters", NULL);
820
821   filter_te = gtk_entry_new();
822   g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
823   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
824   g_signal_connect(filter_te, "changed",
825                    G_CALLBACK(filter_te_syntax_check_cb), NULL);
826   g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
827   g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
828   g_signal_connect(file_merge_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
829   colorize_filter_te_as_empty(filter_te);
830   gtk_widget_show(filter_te);
831   gtk_tooltips_set_tip(tooltips, filter_te, "Enter a display filter.", NULL);
832
833   g_object_set_data(G_OBJECT(file_merge_w), E_RFILTER_TE_KEY, filter_te);
834
835   prepend_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL,
836       "Prepend packets to existing file");
837   gtk_tooltips_set_tip(tooltips, prepend_rb,
838       "The resulting file contains the packets from the selected, followed by the packets from the currently loaded file,"
839       " the packet timestamps will be ignored.", NULL);
840   gtk_box_pack_start(GTK_BOX(main_vb), prepend_rb, FALSE, FALSE, 0);
841   g_object_set_data(G_OBJECT(file_merge_w),
842                   E_MERGE_PREPEND_KEY, prepend_rb);
843   gtk_widget_show(prepend_rb);
844
845   chrono_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Merge packets chronologically");
846   gtk_tooltips_set_tip(tooltips, chrono_rb,
847       "The resulting file contains all the packets from the currently loaded and the selected file,"
848       " sorted by the packet timestamps.", NULL);
849   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chrono_rb), TRUE);
850   gtk_box_pack_start(GTK_BOX(main_vb), chrono_rb, FALSE, FALSE, 0);
851   gtk_widget_show(chrono_rb);
852   g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_CHRONO_KEY, chrono_rb);
853
854   append_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Append packets to existing file");
855   gtk_tooltips_set_tip(tooltips, append_rb,
856       "The resulting file contains the packets from the currently loaded, followed by the packets from the selected file,"
857       " the packet timestamps will be ignored.", NULL);
858   gtk_box_pack_start(GTK_BOX(main_vb), append_rb, FALSE, FALSE, 0);
859   gtk_widget_show(append_rb);
860   g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_APPEND_KEY, append_rb);
861
862   g_signal_connect(file_merge_w, "destroy",
863                    G_CALLBACK(file_merge_destroy_cb), NULL);
864
865   /* preview widget */
866   prev = preview_new();
867   g_object_set_data(G_OBJECT(file_merge_w), PREVIEW_TABLE_KEY, prev);
868   gtk_widget_show_all(prev);
869   gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
870
871   g_signal_connect(GTK_FILE_CHOOSER(file_merge_w), "selection-changed",
872                    G_CALLBACK(file_open_entry_changed), file_merge_w);
873   file_open_entry_changed(file_merge_w, file_merge_w);
874
875   g_object_set_data(G_OBJECT(file_merge_w), E_DFILTER_TE_KEY,
876                     g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
877   if (gtk_dialog_run(GTK_DIALOG(file_merge_w)) == GTK_RESPONSE_ACCEPT)
878   {
879     file_merge_ok_cb(file_merge_w, file_merge_w);
880   }
881   else window_destroy(file_merge_w);
882 #endif /* _WIN32 */
883 }
884
885 static void file_merge_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
886 {
887     switch(btn) {
888     case(ESD_BTN_OK):
889         /* save file first */
890         file_save_as_cmd(after_save_merge_dialog, data);
891         break;
892     case(ESD_BTN_CANCEL):
893         break;
894     default:
895         g_assert_not_reached();
896     }
897 }
898
899 void
900 file_merge_cmd_cb(GtkWidget *widget, gpointer data _U_) {
901   gpointer  dialog;
902
903   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
904     /* user didn't saved his current file, ask him */
905     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
906                 "%sSave the capture file before merging to another one?%s\n\n"
907                 "A temporary capture file can't be merged.",
908                 simple_dialog_primary_start(), simple_dialog_primary_end());
909     simple_dialog_set_cb(dialog, file_merge_answered_cb, widget);
910   } else {
911     /* unchanged file, just start to merge */
912     file_merge_cmd(widget);
913   }
914 }
915
916
917 static void
918 file_merge_ok_cb(GtkWidget *w, gpointer fs) {
919   gchar       *cf_name, *s;
920   const gchar *rfilter;
921   GtkWidget   *filter_te, *rb;
922   dfilter_t   *rfcode = NULL;
923   int          err;
924   cf_status_t  merge_status;
925   char        *in_filenames[2];
926   char        *tmpname;
927
928   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
929   filter_te = g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
930   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
931   if (!dfilter_compile(rfilter, &rfcode)) {
932     bad_dfilter_alert_box(rfilter);
933     g_free(cf_name);
934     return;
935   }
936
937   /* Perhaps the user specified a directory instead of a file.
938      Check whether they did. */
939   if (test_for_directory(cf_name) == EISDIR) {
940         /* It's a directory - set the file selection box to display that
941            directory, don't try to open the directory as a capture file. */
942         set_last_open_dir(cf_name);
943         g_free(cf_name);
944         file_selection_set_current_folder(fs, get_last_open_dir());
945         return;
946   }
947
948   /* merge or append the two files */
949   rb = g_object_get_data(G_OBJECT(w), E_MERGE_CHRONO_KEY);
950   tmpname = NULL;
951   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
952       /* chronological order */
953       in_filenames[0] = cfile.filename;
954       in_filenames[1] = cf_name;
955       merge_status = cf_merge_files(&tmpname, 2, in_filenames, filetype, FALSE);
956   } else {
957       rb = g_object_get_data(G_OBJECT(w), E_MERGE_PREPEND_KEY);
958       if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
959           /* prepend file */
960           in_filenames[0] = cf_name;
961           in_filenames[1] = cfile.filename;
962           merge_status = cf_merge_files(&tmpname, 2, in_filenames, filetype,
963                                         TRUE);
964       } else {
965           /* append file */
966           in_filenames[0] = cfile.filename;
967           in_filenames[1] = cf_name;
968           merge_status = cf_merge_files(&tmpname, 2, in_filenames, filetype,
969                                         TRUE);
970       }
971   }
972
973   g_free(cf_name);
974
975   if (merge_status != CF_OK) {
976     if (rfcode != NULL)
977       dfilter_free(rfcode);
978     g_free(tmpname);
979     return;
980   }
981
982   cf_close(&cfile);
983
984   /* We've crossed the Rubicon; get rid of the file selection box. */
985   window_destroy(GTK_WIDGET (fs));
986
987   /* Try to open the merged capture file. */
988   if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
989     /* We couldn't open it; don't dismiss the open dialog box,
990        just leave it around so that the user can, after they
991        dismiss the alert box popped up for the open error,
992        try again. */
993     if (rfcode != NULL)
994       dfilter_free(rfcode);
995     g_free(tmpname);
996     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
997      * as this will prevent the user from closing the now existing error
998      * message, simply close the dialog (this is the best we can do here). */
999     if (file_open_w)
1000       window_destroy(file_open_w);
1001     return;
1002   }
1003   g_free(tmpname);
1004
1005   /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1006      it closed the previous capture file, and thus destroyed any
1007      previous read filter attached to "cf"). */
1008   cfile.rfcode = rfcode;
1009
1010   switch (cf_read(&cfile)) {
1011
1012   case CF_READ_OK:
1013   case CF_READ_ERROR:
1014     /* Just because we got an error, that doesn't mean we were unable
1015        to read any of the file; we handle what we could get from the
1016        file. */
1017     break;
1018
1019   case CF_READ_ABORTED:
1020     /* The user bailed out of re-reading the capture file; the
1021        capture file has been closed - just free the capture file name
1022        string and return (without changing the last containing
1023        directory). */
1024     return;
1025   }
1026
1027   /* Save the name of the containing directory specified in the path name,
1028      if any; we can write over cf_merged_name, which is a good thing, given that
1029      "get_dirname()" does write over its argument. */
1030   s = get_dirname(tmpname);
1031   set_last_open_dir(s);
1032 #ifndef NEW_PACKET_LIST
1033   gtk_widget_grab_focus(packet_list);
1034 #endif
1035 }
1036
1037 static void
1038 file_merge_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1039 {
1040   /* Note that we no longer have a "Merge Capture File" dialog box. */
1041   file_merge_w = NULL;
1042 }
1043
1044
1045 static void file_close_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
1046 {
1047     switch(btn) {
1048     case(ESD_BTN_SAVE):
1049         /* save file first */
1050         file_save_as_cmd(after_save_close_file, NULL);
1051         break;
1052     case(ESD_BTN_DONT_SAVE):
1053         cf_close(&cfile);
1054         break;
1055     case(ESD_BTN_CANCEL):
1056         break;
1057     default:
1058         g_assert_not_reached();
1059     }
1060 }
1061
1062 /* Close a file */
1063 void
1064 file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
1065   gpointer  dialog;
1066
1067   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
1068     /* user didn't saved his current file, ask him */
1069     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
1070                 "%sSave capture file before closing it?%s\n\n"
1071                 "If you close without saving, your capture data will be discarded.",
1072                 simple_dialog_primary_start(), simple_dialog_primary_end());
1073
1074     simple_dialog_set_cb(dialog, file_close_answered_cb, NULL);
1075   } else {
1076     /* unchanged file, just close it */
1077     cf_close(&cfile);
1078   }
1079 }
1080
1081 void
1082 file_save_cmd_cb(GtkWidget *w, gpointer data) {
1083   /* If the file's already been saved, do nothing.  */
1084   if (cfile.user_saved)
1085     return;
1086
1087   /* Do a "Save As". */
1088   file_save_as_cmd_cb(w, data);
1089 }
1090
1091 static gboolean
1092 can_save_with_wiretap(int ft)
1093 {
1094   /* To save a file with Wiretap, Wiretap has to handle that format,
1095      and its code to handle that format must be able to write a file
1096      with this file's encapsulation type. */
1097   return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cfile.lnk_t);
1098 }
1099
1100
1101 /* Generate a list of the file types we can save this file as, by
1102    checking what Wiretap supports. */
1103 static void
1104 set_file_type_list(GtkWidget *option_menu)
1105 {
1106   GtkWidget *ft_menu, *ft_menu_item;
1107   int ft;
1108   guint index;
1109   gint item_to_select;
1110
1111   /* Default to the first supported file type, if the file's current
1112      type isn't supported. */
1113   item_to_select = -1;
1114
1115   ft_menu = gtk_menu_new();
1116
1117   /* Check all file types. */
1118   index = 0;
1119   for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
1120     if (can_save_with_wiretap(ft)) {
1121       /* OK, we can write it out in this type. */
1122       ft_menu_item = gtk_menu_item_new_with_label(wtap_file_type_string(ft));
1123       if (ft == filetype) {
1124         /* Default to the same format as the file, if it's supported. */
1125         item_to_select = index;
1126       }
1127       g_signal_connect(ft_menu_item, "activate", G_CALLBACK(select_file_type_cb),
1128                      GINT_TO_POINTER(ft));
1129       gtk_menu_shell_append(GTK_MENU_SHELL(ft_menu), ft_menu_item);
1130       gtk_widget_show(ft_menu_item);
1131       index++;
1132     }
1133   }
1134
1135   gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), ft_menu);
1136   if (item_to_select >= 0) {
1137           /* Select the current File format in the menu */
1138           gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), item_to_select);
1139           select_file_type_cb(NULL, GINT_TO_POINTER(filetype));
1140   } else {
1141
1142           /*
1143            * Manually call the signal handler to activate the first menu item
1144            * since gtk_option_menu_set_history() doesn't do it for us. The first two
1145            * entries in the wiretap file types are placeholders so we start at #2, which
1146            * is the normal libpcap format.
1147            */
1148           gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), 0);
1149           select_file_type_cb(NULL, GINT_TO_POINTER(WTAP_FILE_PCAP));
1150   }
1151 }
1152
1153 static void
1154 select_file_type_cb(GtkWidget *w _U_, gpointer data)
1155 {
1156   int new_filetype = GPOINTER_TO_INT(data);
1157   GtkWidget *compressed_cb;
1158
1159   if (filetype != new_filetype) {
1160     filetype = new_filetype;
1161     compressed_cb = g_object_get_data(G_OBJECT(file_save_as_w), "compressed");
1162     if(compressed_cb)
1163             gtk_widget_set_sensitive(compressed_cb, wtap_dump_can_compress(new_filetype));
1164   }
1165 }
1166
1167
1168 /*
1169  * Update various dynamic parts of the range controls; called from outside
1170  * the file dialog code whenever the packet counts change.
1171  */
1172 void
1173 file_save_update_dynamics(void)
1174 {
1175   if (file_save_as_w == NULL) {
1176     /* We don't currently have a "Save As..." dialog box up. */
1177     return;
1178   }
1179
1180   range_update_dynamics(range_tb);
1181 }
1182
1183
1184 action_after_save_e action_after_save_g;
1185 gpointer            action_after_save_data_g;
1186
1187
1188 void
1189 file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data)
1190 {
1191 #if _WIN32
1192   win32_save_as_file(GDK_WINDOW_HWND(top_level->window), action_after_save, action_after_save_data);
1193 #else /* _WIN32 */
1194   GtkWidget     *main_vb, *ft_hb, *ft_lb, *range_fr, *compressed_cb;
1195
1196   if (file_save_as_w != NULL) {
1197     /* There's already an "Save Capture File As" dialog box; reactivate it. */
1198     reactivate_window(file_save_as_w);
1199     return;
1200   }
1201
1202   /* Default to saving all packets, in the file's current format. */
1203   filetype = cfile.cd_t;
1204
1205   /* init the packet range */
1206   packet_range_init(&range);
1207
1208   /* build the file selection */
1209   file_save_as_w = file_selection_new ("Wireshark: Save Capture File As",
1210                                        FILE_SELECTION_SAVE);
1211
1212   /* as the dialog might already be gone, when using this values, we cannot
1213    * set data to the dialog object, but keep global values */
1214   action_after_save_g       = action_after_save;
1215   action_after_save_data_g  = action_after_save_data;
1216
1217   /* Container for each row of widgets */
1218
1219   main_vb = gtk_vbox_new(FALSE, 5);
1220   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1221   file_selection_set_extra_widget(file_save_as_w, main_vb);
1222   gtk_widget_show(main_vb);
1223
1224   /*** Packet Range frame ***/
1225   range_fr = gtk_frame_new("Packet Range");
1226   gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
1227   gtk_widget_show(range_fr);
1228
1229   /* range table */
1230   range_tb = range_new(&range);
1231   gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
1232   gtk_widget_show(range_tb);
1233
1234   /* File type row */
1235   ft_hb = gtk_hbox_new(FALSE, 3);
1236   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
1237   gtk_widget_show(ft_hb);
1238
1239   ft_lb = gtk_label_new("File type:");
1240   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
1241   gtk_widget_show(ft_lb);
1242
1243   ft_om = gtk_option_menu_new();
1244
1245   /* Generate the list of file types we can save. */
1246   set_file_type_list(ft_om);
1247   gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
1248   gtk_widget_show(ft_om);
1249
1250   /* dynamic values in the range frame */
1251   range_update_dynamics(range_tb);
1252
1253   /* compressed */
1254   compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
1255   gtk_container_add(GTK_CONTAINER(ft_hb), compressed_cb);
1256   /* XXX - disable output compression for now, as this doesn't work with the
1257    * current optimization to simply copy a capture file if it's using the same
1258    * encapsulation ... */
1259   /* the rest of the implementation is just working fine :-( */
1260   /*gtk_widget_show(compressed_cb);*/
1261   g_object_set_data(G_OBJECT(file_save_as_w), "compressed", compressed_cb);
1262   gtk_widget_set_sensitive(compressed_cb, wtap_dump_can_compress(cfile.cd_t));
1263
1264   g_signal_connect(file_save_as_w, "destroy",
1265                    G_CALLBACK(file_save_as_destroy_cb), NULL);
1266
1267   if (gtk_dialog_run(GTK_DIALOG(file_save_as_w)) == GTK_RESPONSE_ACCEPT) {
1268     file_save_as_ok_cb(file_save_as_w, file_save_as_w);
1269   } else {
1270     window_destroy(file_save_as_w);
1271   }
1272 #endif /* _WIN32 */
1273 }
1274
1275 void
1276 file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
1277 {
1278   file_save_as_cmd(after_save_no_action, NULL);
1279 }
1280
1281
1282 /* all tests ok, we only have to save the file */
1283 /* (and probably continue with a pending operation) */
1284 static void
1285 file_save_as_cb(GtkWidget *w _U_, gpointer fs) {
1286   gchar *cf_name;
1287   gchar *dirname;
1288   GtkWidget *compressed_cb;
1289
1290
1291   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1292
1293   compressed_cb = g_object_get_data(G_OBJECT(file_save_as_w), "compressed");
1294
1295   /* XXX - if the user requests to save to an already existing filename, */
1296   /* ask in a dialog if that's intended */
1297   /* currently, cf_save() will simply deny it */
1298
1299   /* Write out the packets (all, or only the ones from the current
1300      range) to the file with the specified name. */
1301   if (cf_save(&cfile, cf_name, &range, filetype,
1302           gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb))) != CF_OK) {
1303     /* The write failed; don't dismiss the open dialog box,
1304        just leave it around so that the user can, after they
1305        dismiss the alert box popped up for the error, try again. */
1306     g_free(cf_name);
1307     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1308      * as this will prevent the user from closing the now existing error
1309      * message, simply close the dialog (this is the best we can do here). */
1310     if (file_save_as_w)
1311       window_destroy(GTK_WIDGET (fs));
1312     return;
1313   }
1314
1315   /* The write succeeded; get rid of the file selection box. */
1316   /* cf_save() might already closed our dialog! */
1317   if (file_save_as_w)
1318     window_destroy(GTK_WIDGET (fs));
1319
1320   /* Save the directory name for future file dialogs. */
1321   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1322   set_last_open_dir(dirname);
1323   g_free(cf_name);
1324
1325   /* we have finished saving, do we have pending things to do? */
1326   switch(action_after_save_g) {
1327   case(after_save_no_action):
1328       break;
1329   case(after_save_open_dialog):
1330       file_open_cmd(action_after_save_data_g);
1331       break;
1332   case(after_save_open_recent_file):
1333       menu_open_recent_file_cmd(action_after_save_data_g);
1334       break;
1335   case(after_save_open_dnd_file):
1336       dnd_open_file_cmd(action_after_save_data_g);
1337       break;
1338   case(after_save_merge_dialog):
1339       file_merge_cmd(action_after_save_data_g);
1340       break;
1341 #ifdef HAVE_LIBPCAP
1342   case(after_save_capture_dialog):
1343       capture_start_confirmed();
1344       break;
1345 #endif
1346   case(after_save_close_file):
1347       cf_close(&cfile);
1348       break;
1349   case(after_save_exit):
1350       main_do_quit();
1351       break;
1352   default:
1353       g_assert_not_reached();
1354   }
1355
1356   action_after_save_g = after_save_no_action;
1357 }
1358
1359
1360 static void file_save_as_exists_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
1361 {
1362     gchar       *cf_name;
1363
1364     cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data));
1365
1366     switch(btn) {
1367     case(ESD_BTN_OK):
1368         /* save file */
1369         ws_unlink(cf_name);
1370         file_save_as_cb(NULL, data);
1371         break;
1372     case(ESD_BTN_CANCEL):
1373         /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1374          * as this will prevent the user from closing the now existing error
1375          * message, simply close the dialog (this is the best we can do here). */
1376         if (file_save_as_w)
1377             window_destroy(file_save_as_w);
1378         break;
1379     default:
1380         g_assert_not_reached();
1381     }
1382     g_free(cf_name);
1383 }
1384
1385
1386 /* user pressed "Save" dialog "Ok" button */
1387 static void
1388 file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
1389   gchar *cf_name;
1390   gpointer  dialog;
1391
1392   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1393
1394   /* Perhaps the user specified a directory instead of a file.
1395      Check whether they did. */
1396   if (test_for_directory(cf_name) == EISDIR) {
1397         /* It's a directory - set the file selection box to display that
1398            directory, and leave the selection box displayed. */
1399         set_last_open_dir(cf_name);
1400         g_free(cf_name);
1401         file_selection_set_current_folder(fs, get_last_open_dir());
1402         return;
1403   }
1404
1405   /* Check whether the range is valid. */
1406   if (!range_check_validity(&range)) {
1407     /* The range isn't valid; don't dismiss the open dialog box,
1408        just leave it around so that the user can, after they
1409        dismiss the alert box popped up for the error, try again. */
1410     g_free(cf_name);
1411     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1412      * as this will prevent the user from closing the now existing error
1413      * message, simply close the dialog (this is the best we can do here). */
1414     if (file_save_as_w)
1415       window_destroy(GTK_WIDGET (fs));
1416
1417     return;
1418   }
1419
1420   /*
1421    * Check that the from file is not the same as to file
1422    * We do it here so we catch all cases ...
1423    * Unfortunately, the file requester gives us an absolute file
1424    * name and the read file name may be relative (if supplied on
1425    * the command line). From Joerg Mayer.
1426    */
1427   if (files_identical(cfile.filename, cf_name)) {
1428     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1429       "%sCapture file: \"%s\" identical to loaded file!%s\n\n"
1430       "Please choose a different filename.",
1431       simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
1432     g_free(cf_name);
1433     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1434      * as this will prevent the user from closing the now existing error
1435      * message, simply close the dialog (this is the best we can do here). */
1436     if (file_save_as_w)
1437       window_destroy(GTK_WIDGET (fs));
1438
1439     return;
1440   }
1441
1442   /* don't show the dialog while saving (or asking) */
1443   gtk_widget_hide(GTK_WIDGET (fs));
1444
1445   /* it the file doesn't exist, simply try to save it */
1446   if (!file_exists(cf_name)) {
1447     file_save_as_cb(NULL, fs);
1448     g_free(cf_name);
1449     return;
1450   }
1451
1452   /* the file exists, ask the user to remove it first */
1453   dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
1454       "%sA file named \"%s\" already exists.%s\n\n"
1455       "Do you want to replace it with the capture you are saving?",
1456       simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
1457   simple_dialog_set_cb(dialog, file_save_as_exists_answered_cb, fs);
1458
1459   g_free(cf_name);
1460 }
1461
1462 void
1463 file_save_as_destroy(void)
1464 {
1465   if (file_save_as_w)
1466     window_destroy(file_save_as_w);
1467 }
1468
1469 static void
1470 file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1471 {
1472   /* Note that we no longer have a "Save Capture File As" dialog box. */
1473   file_save_as_w = NULL;
1474 }
1475
1476 /* Reload a file using the current read and display filters */
1477 void
1478 file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
1479   cf_reload(&cfile);
1480 }
1481
1482 /******************** Color Filters *********************************/
1483 /*
1484  * Keep a static pointer to the current "Color Export" window, if
1485  * any, so that if somebody tries to do "Export"
1486  * while there's already a "Color Export" window up, we just pop
1487  * up the existing one, rather than creating a new one.
1488  */
1489 static GtkWidget *file_color_import_w;
1490
1491 /* sets the file path to the global color filter file.
1492    WARNING: called by both the import and the export dialog.
1493 */
1494 static void
1495 color_global_cb(GtkWidget *widget _U_, gpointer data)
1496 {
1497   GtkWidget *fs_widget = data;
1498   gchar *path;
1499
1500   /* decide what file to open (from dfilter code) */
1501   path = get_datafile_path("colorfilters");
1502
1503   gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(fs_widget), path);
1504
1505   g_free(path);
1506 }
1507
1508 /* Import color filters */
1509 void
1510 file_color_import_cmd_cb(GtkWidget *color_filters, gpointer filter_list _U_)
1511 {
1512 #if _WIN32
1513   win32_import_color_file(GDK_WINDOW_HWND(top_level->window), color_filters);
1514 #else /* _WIN32 */
1515   GtkWidget     *main_vb, *cfglobal_but;
1516
1517   /* No Apply button, and "OK" just sets our text widget, it doesn't
1518      activate it (i.e., it doesn't cause us to try to open the file). */
1519
1520   if (file_color_import_w != NULL) {
1521     /* There's already an "Import Color Filters" dialog box; reactivate it. */
1522     reactivate_window(file_color_import_w);
1523     return;
1524   }
1525
1526   file_color_import_w = file_selection_new("Wireshark: Import Color Filters",
1527                                            FILE_SELECTION_OPEN);
1528
1529   /* Container for each row of widgets */
1530   main_vb = gtk_vbox_new(FALSE, 3);
1531   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1532   file_selection_set_extra_widget(file_color_import_w, main_vb);
1533   gtk_widget_show(main_vb);
1534
1535
1536   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
1537   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
1538   g_signal_connect(cfglobal_but, "clicked",
1539                    G_CALLBACK(color_global_cb), file_color_import_w);
1540   gtk_widget_show(cfglobal_but);
1541
1542   g_signal_connect(file_color_import_w, "destroy",
1543                    G_CALLBACK(file_color_import_destroy_cb), NULL);
1544
1545
1546   if (gtk_dialog_run(GTK_DIALOG(file_color_import_w)) == GTK_RESPONSE_ACCEPT)
1547   {
1548       file_color_import_ok_cb(file_color_import_w, color_filters);
1549   }
1550   else window_destroy(file_color_import_w);
1551 #endif /* _WIN32 */
1552 }
1553
1554 static void
1555 file_color_import_ok_cb(GtkWidget *w, gpointer color_filters) {
1556   gchar     *cf_name, *s;
1557   GtkWidget *fs = gtk_widget_get_toplevel(w);
1558
1559   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1560
1561   /* Perhaps the user specified a directory instead of a file.
1562      Check whether they did. */
1563   if (test_for_directory(cf_name) == EISDIR) {
1564         /* It's a directory - set the file selection box to display that
1565            directory, don't try to open the directory as a color filter file. */
1566         set_last_open_dir(cf_name);
1567         g_free(cf_name);
1568         file_selection_set_current_folder(fs, get_last_open_dir());
1569         return;
1570   }
1571
1572   /* Try to open the color filter file. */
1573
1574   if (!color_filters_import(cf_name, color_filters)) {
1575     /* We couldn't open it; don't dismiss the open dialog box,
1576        just leave it around so that the user can, after they
1577        dismiss the alert box popped up for the open error,
1578        try again. */
1579     g_free(cf_name);
1580     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1581      * as this will prevent the user from closing the now existing error
1582      * message, simply close the dialog (this is the best we can do here). */
1583     window_destroy(GTK_WIDGET (fs));
1584
1585     return;
1586   }
1587
1588   /* We've crossed the Rubicon; get rid of the file selection box. */
1589   window_destroy(GTK_WIDGET (fs));
1590
1591   /* Save the name of the containing directory specified in the path name,
1592      if any; we can write over cf_name, which is a good thing, given that
1593      "get_dirname()" does write over its argument. */
1594   s = get_dirname(cf_name);
1595   set_last_open_dir(s);
1596 #ifndef NEW_PACKET_LIST
1597   gtk_widget_grab_focus(packet_list);
1598 #endif
1599
1600   g_free(cf_name);
1601 }
1602
1603 static void
1604 file_color_import_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1605 {
1606   /* Note that we no longer have a "Open Capture File" dialog box. */
1607   file_color_import_w = NULL;
1608 }
1609
1610 static GtkWidget *file_color_export_w;
1611 /*
1612  * Set the "Export only selected filters" toggle button as appropriate for
1613  * the current output file type and count of selected filters.
1614  *
1615  * Called when the "Export" dialog box is created and when the selected
1616  * count changes.
1617  */
1618 static void
1619 color_set_export_selected_sensitive(GtkWidget * cfselect_cb)
1620 {
1621   if (file_color_export_w == NULL) {
1622     /* We don't currently have an "Export" dialog box up. */
1623     return;
1624   }
1625
1626   /* We can request that only the selected filters be saved only if
1627         there *are* selected filters. */
1628   if (color_selected_count() != 0)
1629     gtk_widget_set_sensitive(cfselect_cb, TRUE);
1630   else {
1631     /* Force the "Export only selected filters" toggle to "false", turn
1632        off the flag it controls. */
1633     color_selected = FALSE;
1634     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
1635     gtk_widget_set_sensitive(cfselect_cb, FALSE);
1636   }
1637 }
1638
1639 static void
1640 color_toggle_selected_cb(GtkWidget *widget, gpointer data _U_)
1641 {
1642   color_selected = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
1643 }
1644
1645 void
1646 file_color_export_cmd_cb(GtkWidget *w _U_, gpointer filter_list)
1647 {
1648 #if _WIN32
1649   win32_export_color_file(GDK_WINDOW_HWND(top_level->window), filter_list);
1650 #else /* _WIN32 */
1651   GtkWidget *main_vb, *cfglobal_but;
1652   GtkWidget *cfselect_cb;
1653
1654   if (file_color_export_w != NULL) {
1655     /* There's already an "Color Filter Export" dialog box; reactivate it. */
1656     reactivate_window(file_color_export_w);
1657     return;
1658   }
1659
1660   color_selected   = FALSE;
1661
1662   file_color_export_w = file_selection_new("Wireshark: Export Color Filters",
1663                                            FILE_SELECTION_SAVE);
1664
1665   /* Container for each row of widgets */
1666   main_vb = gtk_vbox_new(FALSE, 3);
1667   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1668   file_selection_set_extra_widget(file_color_export_w, main_vb);
1669   gtk_widget_show(main_vb);
1670
1671   cfselect_cb = gtk_check_button_new_with_label("Export only selected filters");
1672   gtk_container_add(GTK_CONTAINER(main_vb), cfselect_cb);
1673   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
1674   g_signal_connect(cfselect_cb, "toggled",
1675                    G_CALLBACK(color_toggle_selected_cb), NULL);
1676   gtk_widget_show(cfselect_cb);
1677   color_set_export_selected_sensitive(cfselect_cb);
1678
1679   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
1680   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
1681   g_signal_connect(cfglobal_but, "clicked",
1682                    G_CALLBACK(color_global_cb), file_color_export_w);
1683   gtk_widget_show(cfglobal_but);
1684
1685   g_signal_connect(file_color_export_w, "destroy",
1686                    G_CALLBACK(file_color_export_destroy_cb), NULL);
1687
1688   if (gtk_dialog_run(GTK_DIALOG(file_color_export_w)) == GTK_RESPONSE_ACCEPT)
1689   {
1690       file_color_export_ok_cb(file_color_export_w, filter_list);
1691   }
1692   else window_destroy(file_color_export_w);
1693 #endif /* _WIN32 */
1694 }
1695
1696 static void
1697 file_color_export_ok_cb(GtkWidget *w, gpointer filter_list) {
1698   gchar *cf_name;
1699   gchar *dirname;
1700   GtkWidget *fs = gtk_widget_get_toplevel(w);
1701
1702   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1703
1704   /* Perhaps the user specified a directory instead of a file.
1705      Check whether they did. */
1706   if (test_for_directory(cf_name) == EISDIR) {
1707         /* It's a directory - set the file selection box to display that
1708            directory, and leave the selection box displayed. */
1709         set_last_open_dir(cf_name);
1710         g_free(cf_name);
1711         file_selection_set_current_folder(fs, get_last_open_dir());
1712         return;
1713   }
1714
1715   /* Write out the filters (all, or only the ones that are currently
1716      displayed or selected) to the file with the specified name. */
1717
1718    if (!color_filters_export(cf_name, filter_list, color_selected))
1719    {
1720     /* The write failed; don't dismiss the open dialog box,
1721        just leave it around so that the user can, after they
1722        dismiss the alert box popped up for the error, try again. */
1723        g_free(cf_name);
1724
1725       /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1726        * as this will prevent the user from closing the now existing error
1727        * message, simply close the dialog (this is the best we can do here). */
1728        window_destroy(GTK_WIDGET (fs));
1729
1730        return;
1731    }
1732
1733   /* The write succeeded; get rid of the file selection box. */
1734   window_destroy(GTK_WIDGET (fs));
1735
1736   /* Save the directory name for future file dialogs. */
1737   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1738   set_last_open_dir(dirname);
1739   g_free(cf_name);
1740 }
1741
1742 static void
1743 file_color_export_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1744 {
1745   file_color_export_w = NULL;
1746 }