062386e4d18683785ace5030b18bd39234780a3c
[obnox/wireshark/wip.git] / gtk / file_dlg.c
1 /* file_dlg.c
2  * Dialog boxes for handling files
3  *
4  * $Id: file_dlg.c,v 1.18 2000/01/25 05:48:46 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33
34 #ifdef HAVE_DIRECT_H
35 #include <direct.h>
36 #endif
37
38 #include <string.h>
39
40 #ifndef __GLOBALS_H__
41 #include "globals.h"
42 #endif
43 #ifndef __GTKGLOBALS_H__
44 #include "gtkglobals.h"
45 #endif
46
47 #ifndef __KEYS_H__
48 #include "keys.h"
49 #endif
50
51 #ifndef __PREFS_DLG_H__
52 #include "prefs_dlg.h"
53 #endif
54
55 #ifndef __DIALOG_H__
56 #include "simple_dialog.h"
57 #endif
58
59 #ifndef __MENU_H__
60 #include "menu.h"
61 #endif
62
63 #ifndef __MAIN_H__
64 #include "main.h"
65 #endif
66
67 #ifndef __UTIL_H__
68 #include "util.h"
69 #endif
70
71 static void file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs);
72 static void select_file_type_cb(GtkWidget *w, gpointer data);
73 static void file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs);
74
75 /* Open a file */
76 void
77 file_open_cmd_cb(GtkWidget *w, gpointer data) {
78   GtkWidget *filter_hbox, *filter_bt, *filter_te;
79
80   /* XXX - GTK+'s file selection dialog box doesn't let you set the
81      initial directory it should show; it always uses the current
82      directory.  We want to start out by showing the user the files
83      in the last directory in which they looked, so we have to "chdir()"
84      there.
85
86      This means means that if Ethereal dumps core, the core file will be
87      dumped in whatever directory it last "chdir()"red to, rather
88      than in the directory in which you started it.
89
90      It also means, for better or worse, that *all* file selection
91      dialog boxes will start in that directory. */
92   if (last_open_dir)
93           chdir(last_open_dir);
94
95   file_sel = gtk_file_selection_new ("Ethereal: Open Capture File");
96   
97   /* Connect the ok_button to file_open_ok_cb function and pass along a
98      pointer to the file selection box widget */
99   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->ok_button),
100     "clicked", (GtkSignalFunc) file_open_ok_cb, file_sel );
101
102   gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_sel)->ok_button),
103       E_DFILTER_TE_KEY, gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY));
104
105   filter_hbox = gtk_hbox_new(FALSE, 1);
106   gtk_container_border_width(GTK_CONTAINER(filter_hbox), 0);
107   gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_sel)->action_area),
108     filter_hbox, FALSE, FALSE, 0);
109   gtk_widget_show(filter_hbox);
110
111   filter_bt = gtk_button_new_with_label("Filter:");
112   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
113     GTK_SIGNAL_FUNC(filter_dialog_cb), NULL);
114   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
115   gtk_widget_show(filter_bt);
116   
117   filter_te = gtk_entry_new();
118   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
119   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
120   gtk_widget_show(filter_te);
121
122   gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_sel)->ok_button),
123     E_RFILTER_TE_KEY, filter_te);
124
125   /* Connect the cancel_button to destroy the widget */
126   gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
127     (file_sel)->cancel_button), "clicked", (GtkSignalFunc)
128     gtk_widget_destroy, GTK_OBJECT (file_sel));
129
130   gtk_widget_show(file_sel);
131 }
132
133 static void
134 file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
135   gchar     *cf_name, *rfilter, *s;
136   GtkWidget *filter_te;
137   dfilter   *rfcode = NULL;
138   int        err;
139
140   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
141   filter_te = gtk_object_get_data(GTK_OBJECT(w), E_RFILTER_TE_KEY);
142   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
143   if (dfilter_compile(rfilter, &rfcode) != 0) {
144     simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
145     return;
146   }
147
148   /* Perhaps the user specified a directory instead of a file.
149    * See if we can chdir to it. */
150   if (chdir(cf_name) == 0) {
151         int     cf_name_len;
152
153         g_free(last_open_dir);
154
155         /* Append a slash, even if not needed */
156         cf_name_len = strlen(cf_name);
157         last_open_dir = g_malloc(cf_name_len + 2);
158         strcpy(last_open_dir, cf_name);
159         strcat(last_open_dir, "/");
160         g_free(cf_name);
161         gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
162         return;
163   }
164
165   /* Try to open the capture file. */
166   if ((err = open_cap_file(cf_name, FALSE, &cf)) != 0) {
167     /* We couldn't open it; don't dismiss the open dialog box,
168        just leave it around so that the user can, after they
169        dismiss the alert box popped up for the open error,
170        try again. */
171     if (rfcode != NULL)
172       dfilter_destroy(rfcode);
173     g_free(cf_name);
174     return;
175   }
176
177   /* Attach the new read filter to "cf" ("open_cap_file()" succeeded, so
178      it closed the previous capture file, and thus destroyed any
179      previous read filter attached to "cf"). */
180   cf.rfcode = rfcode;
181
182   /* We've crossed the Rubicon; get rid of the file selection box. */
183   gtk_widget_hide(GTK_WIDGET (fs));
184   gtk_widget_destroy(GTK_WIDGET (fs));
185
186   err = read_cap_file(&cf);
187   /* Save the name of the containing directory specified in the path name,
188      if any; we can write over cf_name, which is a good thing, given that
189      "get_dirname()" does write over its argument. */
190   s = get_dirname(cf_name);
191   if (s != NULL) {
192     /* Well, there is a directory in there... */
193     if (last_open_dir != NULL) {
194       /* ...and we already have one saved... */
195       if (strcmp(last_open_dir, s) != 0) {
196         /* ...and it's not the same as this one, so free the old one
197            and assign a copy of the new one to it. */
198         g_free(last_open_dir);
199         last_open_dir = g_strdup(s);
200       }
201     } else {
202       /* ...and we don't already have one saved, so just save this one. */
203       last_open_dir = g_strdup(s);
204     }
205   } else {
206     /* There was no directory in there. */
207     last_open_dir = NULL;
208   }
209   g_free(cf_name);
210 }
211
212 /* Close a file */
213 void
214 file_close_cmd_cb(GtkWidget *widget, gpointer data) {
215   close_cap_file(&cf, info_bar);
216 }
217
218 void
219 file_save_cmd_cb(GtkWidget *w, gpointer data) {
220   file_sel = gtk_file_selection_new ("Ethereal: Save Capture File");
221
222   /* If the file's already been saved, do nothing.  */
223   if (cf.user_saved)
224     return;
225
226   /* Do a "Save As". */
227   file_save_as_cmd_cb(w, data);
228 }
229
230 /* XXX - can we make these not be static? */
231 static gboolean filtered;
232 static int filetype;
233 static GtkWidget *filter_cb;
234 static GtkWidget *ft_om;
235
236 static gboolean
237 can_save_with_wiretap(int ft)
238 {
239   /* To save a file with Wiretap, Wiretap has to handle that format,
240      and its code to handle that format must be able to write a file
241      with this file's encapsulation type. */
242   return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cf.lnk_t);
243 }
244
245 /* Generate a list of the file types we can save this file as.
246
247    "filetype" is the type it has now.
248
249    "encap" is the encapsulation for its packets (which could be
250    "unknown" or "per-packet").
251
252    "filtered" is TRUE if we're to save only the packets that passed
253    the display filter (in which case we have to save it using Wiretap)
254    and FALSE if we're to save the entire file (in which case, if we're
255    saving it in the type it has already, we can just copy it). */
256 static void
257 set_file_type_list(GtkWidget *option_menu)
258 {
259   GtkWidget *ft_menu, *ft_menu_item;
260   int ft;
261   guint index;
262   guint item_to_select;
263
264   /* Default to the first supported file type, if the file's current
265      type isn't supported. */
266   item_to_select = 0;
267
268   ft_menu = gtk_menu_new();
269
270   /* Check all file types. */
271   index = 0;
272   for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
273     if (filtered || ft != cf.cd_t) {
274       /* Filtered, or a different file type.  We have to use Wiretap. */
275       if (!can_save_with_wiretap(ft))
276         continue;       /* We can't. */
277     }
278
279     /* OK, we can write it out in this type. */
280     ft_menu_item = gtk_menu_item_new_with_label(wtap_file_type_string(ft));
281     if (ft == filetype) {
282       /* Default to the same format as the file, if it's supported. */
283       item_to_select = index;
284     }
285     gtk_signal_connect(GTK_OBJECT(ft_menu_item), "activate",
286       GTK_SIGNAL_FUNC(select_file_type_cb), (gpointer)ft);
287     gtk_menu_append(GTK_MENU(ft_menu), ft_menu_item);
288     gtk_widget_show(ft_menu_item);
289     index++;
290   }
291
292   gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), ft_menu);
293   gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), item_to_select);
294 }
295
296 static void
297 select_file_type_cb(GtkWidget *w, gpointer data)
298 {
299   int new_filetype = (int)data;
300
301   if (filetype != new_filetype) {
302     /* We can select only the filtered packets to be saved iff we can
303        use Wiretap to save the file. */
304     gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(new_filetype));
305     filetype = new_filetype;
306   }
307 }
308
309 static void
310 toggle_filtered_cb(GtkWidget *widget, gpointer data)
311 {
312   gboolean new_filtered;
313
314   new_filtered = GTK_TOGGLE_BUTTON (widget)->active;
315
316   if (filtered != new_filtered) {
317     /* They changed the state of the "filtered" button. */
318     filtered = new_filtered;
319     set_file_type_list(ft_om);
320   }
321 }
322
323 void
324 file_save_as_cmd_cb(GtkWidget *w, gpointer data)
325 {
326   GtkWidget *ok_bt, *main_vb, *ft_hb, *ft_lb;
327
328   /* Default to saving all packets, in the file's current format. */
329   filtered = FALSE;
330   filetype = cf.cd_t;
331
332   file_sel = gtk_file_selection_new ("Ethereal: Save Capture File As");
333
334   /* Connect the ok_button to file_save_as_ok_cb function and pass along a
335      pointer to the file selection box widget */
336   ok_bt = GTK_FILE_SELECTION (file_sel)->ok_button;
337   gtk_signal_connect (GTK_OBJECT (ok_bt), "clicked",
338     (GtkSignalFunc) file_save_as_ok_cb, file_sel );
339
340   /* Container for each row of widgets */
341   main_vb = gtk_vbox_new(FALSE, 3);
342   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
343   gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_sel)->action_area),
344     main_vb, FALSE, FALSE, 0);
345   gtk_widget_show(main_vb);
346   
347   filter_cb = gtk_check_button_new_with_label("Save only packets currently being displayed");
348   gtk_container_add(GTK_CONTAINER(main_vb), filter_cb);
349   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
350   gtk_signal_connect(GTK_OBJECT(filter_cb), "toggled",
351                         GTK_SIGNAL_FUNC(toggle_filtered_cb), NULL);
352   gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(filetype));
353   gtk_widget_show(filter_cb);
354
355   /* File type row */
356   ft_hb = gtk_hbox_new(FALSE, 3);
357   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
358   gtk_widget_show(ft_hb);
359   
360   ft_lb = gtk_label_new("File type:");
361   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
362   gtk_widget_show(ft_lb);
363
364   ft_om = gtk_option_menu_new();
365
366   /* Generate the list of file types we can save. */
367   set_file_type_list(ft_om);
368   gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
369   gtk_widget_show(ft_om);
370
371   /* Connect the cancel_button to destroy the widget */
372   gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
373     (file_sel)->cancel_button), "clicked", (GtkSignalFunc)
374     gtk_widget_destroy, GTK_OBJECT (file_sel));
375
376   gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_sel), "");
377   gtk_widget_show(file_sel);
378 }
379
380 static void
381 file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
382   gchar *cf_name;
383
384   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
385   gtk_widget_hide(GTK_WIDGET (fs));
386   gtk_widget_destroy(GTK_WIDGET (fs));
387
388   /* Write out the packets (all, or only the ones that are currently
389      displayed) to the file with the specified name. */
390   save_cap_file(cf_name, &cf, filtered, filetype);
391
392   /* If "save_cap_file()" saved the file name we handed it, it saved
393      a copy, so we should free up our copy. */
394   g_free(cf_name);
395 }
396
397 /* Reload a file using the current read and display filters */
398 void
399 file_reload_cmd_cb(GtkWidget *w, gpointer data) {
400   /*GtkWidget *filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);*/
401   GtkWidget *filter_te;
402   gchar *filename;
403   gboolean is_tempfile;
404
405   filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
406
407   if (cf.dfilter)
408     g_free(cf.dfilter);
409   cf.dfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
410
411   /* If the file could be opened, "open_cap_file()" calls "close_cap_file()"
412      to get rid of state for the old capture file before filling in state
413      for the new capture file.  "close_cap_file()" will remove the file if
414      it's a temporary file; we don't want that to happen (for one thing,
415      it'd prevent subsequent reopens from working).  Remember whether it's
416      a temporary file, mark it as not being a temporary file, and then
417      reopen it as the type of file it was.
418
419      Also, "close_cap_file()" will free "cf.filename", so we must make
420      a copy of it first. */
421   filename = strdup(cf.filename);
422   is_tempfile = cf.is_tempfile;
423   cf.is_tempfile = FALSE;
424   if (open_cap_file(filename, is_tempfile, &cf) == 0)
425     read_cap_file(&cf);
426   else {
427     /* The open failed, so "cf.is_tempfile" wasn't set to "is_tempfile".
428        Instead, the file was left open, so we should restore "cf.is_tempfile"
429        ourselves.
430
431        XXX - change the menu?  Presumably "open_cap_file()" will do that;
432        make sure it does! */
433     cf.is_tempfile = is_tempfile;
434   }
435   /* "open_cap_file()" made a copy of the file name we handed it, so
436      we should free up our copy. */
437   g_free(filename);
438 }