3 * $Id: main.c,v 1.80 2000/01/03 06:59:21 guy Exp $
5 * Ethereal - Network traffic analyzer
6 * By Gerald Combs <gerald@zing.org>
7 * Copyright 1998 Gerald Combs
9 * Richard Sharpe, 13-Feb-1999, added support for initializing structures
10 * needed by dissect routines
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 * - Check for end of packet in dissect_* routines.
31 * - Multiple window support
32 * - Add cut/copy/paste
33 * - Create header parsing routines
34 * - Make byte view selections more fancy?
53 #include <sys/types.h>
60 #ifdef HAVE_NETINET_IN_H
61 #include <netinet/in.h>
66 #ifdef NEED_SNPRINTF_H
72 # include "snprintf.h"
75 #if defined(HAVE_UCD_SNMP_SNMP_H)
76 #ifdef HAVE_UCD_SNMP_VERSION_H
77 #include <ucd-snmp/version.h>
78 #endif /* HAVE_UCD_SNMP_VERSION_H */
79 #elif defined(HAVE_SNMP_SNMP_H)
80 #ifdef HAVE_SNMP_VERSION_H
81 #include <snmp/version.h>
82 #endif /* HAVE_SNMP_VERSION_H */
85 #ifdef NEED_STRERROR_H
90 #include "timestamp.h"
96 #include "prefs_dlg.h"
102 #include "simple_dialog.h"
103 #include "proto_draw.h"
106 #include "gtkglobals.h"
108 FILE *data_out_file = NULL;
111 GtkWidget *top_level, *file_sel, *packet_list, *tree_view, *byte_view,
112 *prog_bar, *info_bar, *tv_scrollw, *pkt_scrollw,
113 *bv_vscroll_left, *bv_vscroll_right;
114 GdkFont *m_r_font, *m_b_font;
115 guint main_ctx, file_ctx;
116 gchar comp_info_str[256];
117 gchar *ethereal_path = NULL;
118 gchar *medium_font = MONO_MEDIUM_FONT;
119 gchar *bold_font = MONO_BOLD_FONT;
120 gchar *last_open_dir = NULL;
122 ts_type timestamp_type = RELATIVE;
124 GtkStyle *item_style;
126 /* Specifies the field currently selected in the GUI protocol tree */
127 field_info *finfo_selected = NULL;
129 static void follow_destroy_cb(GtkWidget *win, gpointer data);
130 static void follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w);
131 static void follow_load_text(GtkWidget *text, char *filename, gboolean show_ascii);
132 static void follow_print_stream(GtkWidget *w, gpointer parent_w);
133 static char* hfinfo_numeric_format(header_field_info *hfinfo);
135 /* About Ethereal window */
137 about_ethereal( GtkWidget *w, gpointer data ) {
138 simple_dialog(ESD_TYPE_INFO, NULL,
139 "Ethereal - Network Protocol Analyzer\n"
140 "Version %s (C) 1998-2000 Gerald Combs <gerald@zing.org>\n"
141 "Compiled with %s\n\n"
143 "Check the man page for complete documentation and\n"
144 "for the list of contributors.\n"
146 "\nSee http://ethereal.zing.org/ for more information.",
147 VERSION, comp_info_str);
150 /* Follow the TCP stream, if any, to which the last packet that we called
151 a dissection routine on belongs (this might be the most recently
152 selected packet, or it might be the last packet in the file). */
154 follow_stream_cb( GtkWidget *w, gpointer data ) {
155 char filename1[128+1];
156 GtkWidget *streamwindow, *box, *text, *vscrollbar, *table,
158 GtkWidget *hbox, *close_bt, *print_bt, *button;
160 gchar *follow_filter;
162 if( pi.ipproto == 6 ) {
163 /* we got tcp so we can follow */
164 /* Create a temporary file into which to dump the reassembled data
165 from the TCP stream, and set "data_out_file" to refer to it, so
166 that the TCP code will write to it.
168 XXX - it might be nicer to just have the TCP code directly
169 append stuff to the text widget for the TCP stream window,
170 if we can arrange that said window not pop up until we're
172 tmp_fd = create_tempfile( filename1, sizeof filename1, "follow");
174 simple_dialog(ESD_TYPE_WARN, NULL,
175 "Could not create temporary file %s: %s", filename1, strerror(errno));
178 data_out_file = fdopen( tmp_fd, "w" );
179 if( data_out_file == NULL ) {
180 simple_dialog(ESD_TYPE_WARN, NULL,
181 "Could not create temporary file %s: %s", filename1, strerror(errno));
187 /* Create a new filter that matches all packets in the TCP stream,
188 and set the display filter entry accordingly */
189 reset_tcp_reassembly();
190 follow_filter = build_follow_filter( &pi );
192 /* set the display filter entry accordingly */
193 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
194 gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
196 /* Run the display filter so it goes in effect. */
197 filter_packets(&cf, follow_filter);
199 /* the data_out_file should now be full of the streams information */
200 fclose( data_out_file );
202 /* the filename1 file now has all the text that was in the session */
203 streamwindow = gtk_window_new( GTK_WINDOW_TOPLEVEL);
204 gtk_widget_set_name( streamwindow, "TCP stream window" );
206 gtk_signal_connect( GTK_OBJECT(streamwindow), "delete_event",
207 GTK_SIGNAL_FUNC(follow_destroy_cb), NULL);
208 gtk_signal_connect( GTK_OBJECT(streamwindow), "destroy",
209 GTK_SIGNAL_FUNC(follow_destroy_cb), NULL);
211 if( incomplete_tcp_stream ) {
212 gtk_window_set_title( GTK_WINDOW(streamwindow),
213 "Contents of TCP stream (incomplete)" );
215 gtk_window_set_title( GTK_WINDOW(streamwindow),
216 "Contents of TCP stream" );
218 gtk_widget_set_usize( GTK_WIDGET(streamwindow), DEF_WIDTH, DEF_HEIGHT );
219 gtk_container_border_width( GTK_CONTAINER(streamwindow), 2 );
221 /* setup the container */
222 box = gtk_vbox_new( FALSE, 0 );
223 gtk_container_add( GTK_CONTAINER(streamwindow), box );
224 gtk_widget_show( box );
226 /* set up the table we attach to */
227 table = gtk_table_new( 1, 2, FALSE );
228 gtk_table_set_col_spacing( GTK_TABLE(table), 0, 2);
229 gtk_box_pack_start( GTK_BOX(box), table, TRUE, TRUE, 0 );
230 gtk_widget_show( table );
232 /* create a text box */
233 text = gtk_text_new( NULL, NULL );
234 gtk_text_set_editable( GTK_TEXT(text), FALSE);
235 gtk_table_attach( GTK_TABLE(table), text, 0, 1, 0, 1,
236 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
237 GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
238 gtk_widget_show(text);
241 hbox = gtk_hbox_new( FALSE, 1 );
242 gtk_box_pack_end( GTK_BOX(box), hbox, FALSE, FALSE, 0);
243 gtk_widget_show(hbox);
245 #define E_FOLLOW_ASCII_KEY "follow_ascii_key"
247 /* Create Radio Buttons */
248 button = gtk_radio_button_new_with_label(NULL, "ASCII");
249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
250 gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_ASCII_KEY, button);
251 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
252 gtk_signal_connect(GTK_OBJECT(button), "toggled",
253 GTK_SIGNAL_FUNC(follow_charset_toggle_cb),
254 GTK_OBJECT(streamwindow));
255 gtk_widget_show(button);
257 button = gtk_radio_button_new_with_label(
258 gtk_radio_button_group(GTK_RADIO_BUTTON(button)),
260 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
261 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
262 gtk_widget_show(button);
264 /* Create Close Button */
265 close_bt = gtk_button_new_with_label("Close");
266 gtk_signal_connect_object(GTK_OBJECT(close_bt), "clicked",
267 GTK_SIGNAL_FUNC(gtk_widget_destroy),
268 GTK_OBJECT(streamwindow));
269 gtk_box_pack_end( GTK_BOX(hbox), close_bt, FALSE, FALSE, 0);
270 gtk_widget_show( close_bt );
272 /* Create Print Button */
273 print_bt = gtk_button_new_with_label("Print");
274 gtk_signal_connect(GTK_OBJECT(print_bt), "clicked",
275 GTK_SIGNAL_FUNC(follow_print_stream),
276 GTK_OBJECT(streamwindow));
277 gtk_box_pack_end( GTK_BOX(hbox), print_bt, FALSE, FALSE, 0);
278 gtk_widget_show( print_bt );
280 /* create the scrollbar */
281 vscrollbar = gtk_vscrollbar_new( GTK_TEXT(text)->vadj );
282 gtk_table_attach( GTK_TABLE(table), vscrollbar, 1, 2, 0, 1,
283 GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
284 gtk_widget_show( vscrollbar );
285 gtk_widget_realize( text );
287 /* Tuck away the filename and textbox into streamwindow */
288 #define E_FOLLOW_FILENAME_KEY "follow_filename_key"
289 #define E_FOLLOW_TEXT_KEY "follow_text_key"
291 gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_FILENAME_KEY,
292 g_strdup(filename1));
293 gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_TEXT_KEY, text);
295 follow_load_text(text, filename1, TRUE);
297 data_out_file = NULL;
298 gtk_widget_show( streamwindow );
300 simple_dialog(ESD_TYPE_WARN, NULL,
301 "Error following stream. Please make\n"
302 "sure you have a TCP packet selected.");
306 /* The destroy call back has the responsibility of
307 * unlinking the temporary file */
309 follow_destroy_cb(GtkWidget *win, gpointer data)
313 filename = (char*) gtk_object_get_data(GTK_OBJECT(win),
314 E_FOLLOW_FILENAME_KEY);
318 gtk_widget_destroy(win);
321 /* Handles the ASCII/EBCDIC toggling */
323 follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w)
325 gboolean show_ascii = FALSE;
326 GtkWidget *button, *text;
330 button = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
332 text = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
334 filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w),
335 E_FOLLOW_FILENAME_KEY);
341 if (GTK_TOGGLE_BUTTON(button)->active)
344 follow_load_text(text, filename, show_ascii);
347 static void follow_print_stream(GtkWidget *w, gpointer parent_w)
351 char* print_dest = NULL;
354 switch (prefs.pr_dest) {
356 print_dest = prefs.pr_cmd;
361 print_dest = prefs.pr_file;
366 if (print_dest != NULL) {
367 fh = open_print_dest(to_file, print_dest);
373 simple_dialog(ESD_TYPE_WARN, NULL,
374 "Couldn't figure out where to send the print "
375 "job. Check your preferences.");
379 simple_dialog(ESD_TYPE_WARN, NULL,
380 "Couldn't run print command %s.", prefs.pr_cmd);
384 simple_dialog(ESD_TYPE_WARN, NULL,
385 file_write_error_message(errno),
392 filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w),
393 E_FOLLOW_FILENAME_KEY);
395 if (filename != NULL) {
397 print_file(fh, filename);
399 close_print_dest(to_file, fh);
402 simple_dialog(ESD_TYPE_WARN, NULL, "Could not find data to print.");
406 #define FLT_BUF_SIZE 1024
408 follow_load_text(GtkWidget *text, char *filename, gboolean show_ascii)
410 int bytes_already, bcount;
412 guint32 client_addr = 0;
413 guint16 client_port = 0;
415 /* Delete any info already in text box */
416 bytes_already = gtk_text_get_length(GTK_TEXT(text));
417 if (bytes_already > 0) {
418 gtk_text_set_point(GTK_TEXT(text), 0);
419 gtk_text_forward_delete(GTK_TEXT(text), bytes_already);
422 /* stop the updates while we fill the text box */
423 gtk_text_freeze( GTK_TEXT(text) );
424 data_out_file = fopen( filename, "r" );
425 if( data_out_file ) {
426 char buffer[FLT_BUF_SIZE];
428 while(fread(&sc.src_addr, 1, sizeof(sc), data_out_file)) {
429 if (client_addr == 0) {
430 client_addr = sc.src_addr;
431 client_port = sc.src_port;
434 while (sc.dlen > 0) {
435 bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
436 nchars = fread( buffer, 1, bcount, data_out_file );
441 /* If our native arch is EBCDIC, call:
442 * ASCII_TO_EBCDIC(buffer, nchars);
446 /* If our native arch is ASCII, call: */
447 EBCDIC_to_ASCII(buffer, nchars);
449 if (client_addr == sc.src_addr && client_port == sc.src_port)
450 gtk_text_insert( GTK_TEXT(text), m_r_font, &prefs.st_client_fg,
451 &prefs.st_client_bg, buffer, nchars );
453 gtk_text_insert( GTK_TEXT(text), m_r_font, &prefs.st_server_fg,
454 &prefs.st_server_bg, buffer, nchars );
457 if( ferror( data_out_file ) ) {
458 simple_dialog(ESD_TYPE_WARN, NULL,
459 "Error reading temporary file %s: %s", filename, strerror(errno));
461 fclose( data_out_file );
463 simple_dialog(ESD_TYPE_WARN, NULL,
464 "Could not open temporary file %s: %s", filename, strerror(errno));
466 gtk_text_thaw( GTK_TEXT(text) );
469 /* Match selected byte pattern */
471 match_selected_cb(GtkWidget *w, gpointer data)
474 GtkWidget *filter_te;
475 char *ptr, *format, *stringified;
476 int i, dfilter_len, abbrev_len;
478 header_field_info *hfinfo;
480 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
482 if (!finfo_selected) {
483 simple_dialog(ESD_TYPE_WARN, NULL,
484 "Error determining selected bytes. Please make\n"
485 "sure you have selected a field within the tree\n"
486 "view to be matched.");
490 hfinfo = finfo_selected->hfinfo;
492 abbrev_len = strlen(hfinfo->abbrev);
494 switch(hfinfo->type) {
497 dfilter_len = abbrev_len + 2;
498 buf = g_malloc0(dfilter_len);
499 snprintf(buf, dfilter_len, "%s%s", finfo_selected->value.numeric ? "" : "!",
511 dfilter_len = abbrev_len + 20;
512 buf = g_malloc0(dfilter_len);
513 format = hfinfo_numeric_format(hfinfo);
514 snprintf(buf, dfilter_len, format, hfinfo->abbrev, finfo_selected->value.numeric);
518 dfilter_len = abbrev_len + 4 + 15 + 1;
519 buf = g_malloc0(dfilter_len);
520 snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
521 ipv4_addr_str(&(finfo_selected->value.ipv4)));
525 dfilter_len = abbrev_len + 15;
526 buf = g_malloc0(dfilter_len);
527 snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev,
528 finfo_selected->value.numeric);
532 stringified = ip6_to_str((struct e_in6_addr*) &(finfo_selected->value.ipv6));
533 dfilter_len = abbrev_len + 4 + strlen(stringified) + 1;
534 buf = g_malloc0(dfilter_len);
535 snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
540 dfilter_len = abbrev_len + 30;
541 buf = g_malloc0(dfilter_len);
542 snprintf(buf, dfilter_len, "%s == %f", hfinfo->abbrev,
543 finfo_selected->value.floating);
547 dfilter_len = abbrev_len + 22;
548 buf = g_malloc0(dfilter_len);
549 snprintf(buf, dfilter_len, "%s == %s",
551 ether_to_str(finfo_selected->value.ether));
555 case FT_ABSOLUTE_TIME:
556 case FT_RELATIVE_TIME:
557 memcpy(&fi->value.time, va_arg(ap, struct timeval*),
558 sizeof(struct timeval));
562 /* This g_strdup'ed memory is freed in proto_tree_free_node() */
563 fi->value.string = g_strdup(va_arg(ap, char*));
571 c = cf.pd + finfo_selected->start;
572 buf = g_malloc0(32 + finfo_selected->length * 3);
575 sprintf(ptr, "frame[%d] == ", finfo_selected->start);
576 ptr = buf+strlen(buf);
578 if (finfo_selected->length == 1) {
579 sprintf(ptr, "0x%02x", *c++);
582 for (i=0;i<finfo_selected->length; i++) {
584 sprintf(ptr, "%02x", *c++);
587 sprintf(ptr, ":%02x", *c++);
589 ptr = buf+strlen(buf);
595 /* create a new one and set the display filter entry accordingly */
596 gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
598 /* Run the display filter so it goes in effect. */
599 filter_packets(&cf, buf);
601 /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
605 hfinfo_numeric_format(header_field_info *hfinfo)
609 /* Pick the proper format string */
610 switch(hfinfo->display) {
613 case BASE_OCT: /* I'm lazy */
614 case BASE_BIN: /* I'm lazy */
615 switch(hfinfo->type) {
629 g_assert_not_reached();
634 switch(hfinfo->type) {
636 format = "%s == 0x%02x";
639 format = "%s == 0x%04x";
642 format = "%s == 0x%06x";
645 format = "%s == 0x%08x";
648 g_assert_not_reached();
653 g_assert_not_reached();
660 /* Run the current display filter on the current packet set, and
663 filter_activate_cb(GtkWidget *w, gpointer data)
665 GtkCombo *filter_cm = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_CM_KEY);
666 GList *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY);
667 GList *li, *nl = NULL;
668 gboolean add_filter = TRUE;
670 char *s = gtk_entry_get_text(GTK_ENTRY(w));
672 /* GtkCombos don't let us get at their list contents easily, so we maintain
673 our own filter list, and feed it to gtk_combo_set_popdown_strings when
674 a new filter is added. */
675 if (filter_packets(&cf, g_strdup(s))) {
676 li = g_list_first(filter_list);
678 if (li->data && strcmp(s, li->data) == 0)
684 filter_list = g_list_append(filter_list, g_strdup(s));
685 li = g_list_first(filter_list);
687 nl = g_list_append(nl, strdup(li->data));
690 gtk_combo_set_popdown_strings(filter_cm, nl);
691 gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
696 /* redisplay with no display filter */
698 filter_reset_cb(GtkWidget *w, gpointer data)
700 GtkWidget *filter_te = NULL;
702 if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
703 gtk_entry_set_text(GTK_ENTRY(filter_te), "");
706 filter_packets(&cf, NULL);
709 /* What to do when a list item is selected/unselected */
711 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
722 select_packet(&cf, row);
726 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
727 unselect_packet(&cf);
731 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
734 int tree_selected_start = -1;
735 int tree_selected_len = -1;
738 finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
741 finfo_selected = finfo;
742 tree_selected_start = finfo->start;
743 tree_selected_len = finfo->length;
745 packet_hex_print(GTK_TEXT(byte_view), cf.pd, cf.current_frame->cap_len,
746 tree_selected_start, tree_selected_len, cf.current_frame->encoding);
750 tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
752 finfo_selected = NULL;
753 packet_hex_print(GTK_TEXT(byte_view), cf.pd, cf.current_frame->cap_len,
754 -1, -1, cf.current_frame->encoding);
757 void collapse_all_cb(GtkWidget *widget, gpointer data) {
758 if (cf.protocol_tree)
759 collapse_all_tree(cf.protocol_tree, tree_view);
762 void expand_all_cb(GtkWidget *widget, gpointer data) {
763 if (cf.protocol_tree)
764 expand_all_tree(cf.protocol_tree, tree_view);
768 set_scrollbar_placement(int pos) /* 0=left, 1=right */
771 gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(pkt_scrollw),
772 GTK_CORNER_TOP_LEFT );
773 gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(tv_scrollw),
774 GTK_CORNER_TOP_LEFT );
775 gtk_widget_hide(bv_vscroll_left);
776 gtk_widget_show(bv_vscroll_right);
779 gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(pkt_scrollw),
780 GTK_CORNER_TOP_RIGHT );
781 gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(tv_scrollw),
782 GTK_CORNER_TOP_RIGHT );
783 gtk_widget_hide(bv_vscroll_right);
784 gtk_widget_show(bv_vscroll_left);
789 set_plist_sel_browse(gboolean val)
792 unselect_packet(&cf);
794 /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
795 * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
797 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_SINGLE);
800 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_BROWSE);
805 set_ptree_sel_browse(gboolean val)
807 /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
808 * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
810 gtk_clist_set_selection_mode(GTK_CLIST(tree_view), GTK_SELECTION_SINGLE);
813 gtk_clist_set_selection_mode(GTK_CLIST(tree_view), GTK_SELECTION_BROWSE);
818 set_ptree_line_style(gint style)
820 /* I'm using an assert here since the preferences code limits
821 * the user input, both in the GUI and when reading the preferences file.
822 * If the value is incorrect, it's a program error, not a user-initiated error.
824 g_assert(style >= GTK_CTREE_LINES_NONE && style <= GTK_CTREE_LINES_TABBED);
825 gtk_ctree_set_line_style( GTK_CTREE(tree_view), style );
829 set_ptree_expander_style(gint style)
831 /* I'm using an assert here since the preferences code limits
832 * the user input, both in the GUI and when reading the preferences file.
833 * If the value is incorrect, it's a program error, not a user-initiated error.
835 g_assert(style >= GTK_CTREE_EXPANDER_NONE && style <= GTK_CTREE_EXPANDER_CIRCULAR);
836 gtk_ctree_set_expander_style( GTK_CTREE(tree_view), style );
841 file_quit_cmd_cb (GtkWidget *widget, gpointer data) {
842 /* If we have a capture file open, and it's a temporary file,
844 if (cf.filename != NULL && cf.is_tempfile)
849 /* call initialization routines at program startup time */
851 ethereal_proto_init(void) {
859 ethereal_proto_cleanup(void) {
867 fprintf(stderr, "This is GNU %s %s, compiled with %s\n", PACKAGE,
868 VERSION, comp_info_str);
870 fprintf(stderr, "%s [-vh] [-kQS] [-b <bold font>] [-B <byte view height>] [-c count]\n",
872 fprintf(stderr, " [-f <capture filter>] [-i interface] [-m <medium font>] [-n]\n");
873 fprintf(stderr, " [-P <packet list height>] [-r infile] [-R <read filter>]\n");
874 fprintf(stderr, " [-s snaplen] [-t <time stamp format>] [-T <tree view height>]\n");
875 fprintf(stderr, " [-w savefile]\n");
877 fprintf(stderr, "%s [-vh] [-b <bold font>] [-B <byte view height>] [-m <medium font>]\n",
879 fprintf(stderr, " [-n] [-P <packet list height>] [-r infile] [-R <read filter>]\n");
880 fprintf(stderr, " [-t <time stamp format>] [-T <tree view height>]\n");
884 /* And now our feature presentation... [ fade to music ] */
886 main(int argc, char *argv[])
896 gboolean arg_error = FALSE;
899 extern char pcap_version[];
902 int pf_open_errno = 0;
905 gboolean start_capture = FALSE;
906 gchar *save_file = NULL;
908 gboolean capture_option_specified = FALSE;
910 GtkWidget *main_vbox, *menubar, *u_pane, *l_pane,
911 *bv_table, *stat_hbox,
912 *filter_bt, *filter_cm, *filter_te,
914 GList *filter_list = NULL;
916 GtkAccelGroup *accel;
917 gint pl_size = 280, tv_size = 95, bv_size = 75;
918 gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
919 dfilter *rfcode = NULL;
920 gboolean rfilter_parse_failed = FALSE;
923 ethereal_path = argv[0];
926 command_name = strrchr(ethereal_path, '/');
927 if (command_name == NULL)
928 command_name = ethereal_path;
931 /* Set "capture_child" to indicate whether this is going to be a child
932 process for a "-S" capture. */
933 capture_child = (strcmp(command_name, CHILD_NAME) == 0);
936 /* If invoked with the "-G" flag, we dump out a glossary of
937 display filter symbols.
939 We must do this before calling "gtk_init()", because "gtk_init()"
940 tries to open an X display, and we don't want to have to do any X
941 stuff just to do a build.
943 Given that we call "gtk_init()" before doing the regular argument
944 list processing, so that it can handle X and GTK+ arguments and
945 remove them from the list at which we look, this means we must do
946 this before doing the regular argument list processing, as well.
950 you must give the "-G" flag as the first flag on the command line;
952 you must give it as "-G", nothing more, nothing less;
954 any arguments after the "-G" flag will not be used. */
955 if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
956 ethereal_proto_init();
957 proto_registrar_dump();
961 /* Let GTK get its args */
962 gtk_init (&argc, &argv);
964 prefs = read_prefs(&pf_path);
965 if (pf_path != NULL) {
966 /* The preferences file exists, but couldn't be opened; "pf_path" is
967 its pathname. Remember "errno", as that says why the attempt
969 pf_open_errno = errno;
972 /* Initialize the capture file struct */
978 cf.user_saved = FALSE;
979 cf.is_tempfile = FALSE;
988 cf.save_file_fd = -1;
989 cf.snap = WTAP_MAX_PACKET_SIZE;
991 cf.cinfo.num_cols = prefs->num_cols;
992 cf.cinfo.col_fmt = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
993 cf.cinfo.fmt_matx = (gboolean **) g_malloc(sizeof(gboolean *) * cf.cinfo.num_cols);
994 cf.cinfo.col_width = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
995 cf.cinfo.col_title = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
996 cf.cinfo.col_data = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
998 /* Assemble the compile-time options */
999 snprintf(comp_info_str, 256,
1000 #ifdef GTK_MAJOR_VERSION
1001 "GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
1004 "GTK+ (version unknown), %s%s, %s%s, %s%s",
1008 "with libpcap ", pcap_version,
1010 "without libpcap", "",
1015 "with libz ", ZLIB_VERSION,
1016 #else /* ZLIB_VERSION */
1017 "with libz ", "(version unknown)",
1018 #endif /* ZLIB_VERSION */
1019 #else /* HAVE_LIBZ */
1021 #endif /* HAVE_LIBZ */
1023 /* Oh, this is pretty */
1024 #if defined(HAVE_UCD_SNMP_SNMP_H)
1025 #ifdef HAVE_UCD_SNMP_VERSION_H
1026 "with UCD SNMP ", VersionInfo
1027 #else /* HAVE_UCD_SNMP_VERSION_H */
1028 "with UCD SNMP ", "(version unknown)"
1029 #endif /* HAVE_UCD_SNMP_VERSION_H */
1030 #elif defined(HAVE_SNMP_SNMP_H)
1031 #ifdef HAVE_SNMP_VERSION_H
1032 "with CMU SNMP ", snmp_Version()
1033 #else /* HAVE_SNMP_VERSION_H */
1034 "with CMU SNMP ", "(version unknown)"
1035 #endif /* HAVE_SNMP_VERSION_H */
1042 /* Now get our args */
1043 while ((opt = getopt(argc, argv, "b:B:c:f:hi:km:nP:Qr:R:Ss:t:T:w:W:v")) != EOF) {
1045 case 'b': /* Bold font */
1046 bold_font = g_strdup(optarg);
1048 case 'B': /* Byte view pane height */
1049 bv_size = atoi(optarg);
1051 case 'c': /* Capture xxx packets */
1053 cf.count = atoi(optarg);
1055 capture_option_specified = TRUE;
1061 cf.cfilter = g_strdup(optarg);
1063 capture_option_specified = TRUE;
1067 case 'h': /* Print help and exit */
1071 case 'i': /* Use interface xxx */
1073 cf.iface = g_strdup(optarg);
1075 capture_option_specified = TRUE;
1079 case 'k': /* Start capture immediately */
1081 start_capture = TRUE;
1083 capture_option_specified = TRUE;
1087 case 'm': /* Medium font */
1088 medium_font = g_strdup(optarg);
1090 case 'n': /* No name resolution */
1091 g_resolving_actif = 0;
1093 case 'P': /* Packet list pane height */
1094 pl_size = atoi(optarg);
1096 case 'Q': /* Quit after capture (just capture to file) */
1099 start_capture = TRUE; /*** -Q implies -k !! ***/
1101 capture_option_specified = TRUE;
1105 case 'r': /* Read capture file xxx */
1106 /* We may set "last_open_dir" to "cf_name", and if we change
1107 "last_open_dir" later, we free the old value, so we have to
1108 set "cf_name" to something that's been allocated. */
1109 cf_name = g_strdup(optarg);
1111 case 'R': /* Read file filter */
1114 case 's': /* Set the snapshot (capture) length */
1116 cf.snap = atoi(optarg);
1118 capture_option_specified = TRUE;
1122 case 'S': /* "Sync" mode: used for following file ala tail -f */
1126 capture_option_specified = TRUE;
1130 case 't': /* Time stamp type */
1131 if (strcmp(optarg, "r") == 0)
1132 timestamp_type = RELATIVE;
1133 else if (strcmp(optarg, "a") == 0)
1134 timestamp_type = ABSOLUTE;
1135 else if (strcmp(optarg, "d") == 0)
1136 timestamp_type = DELTA;
1138 fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
1140 fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
1141 fprintf(stderr, "or \"d\" for delta.\n");
1145 case 'T': /* Tree view pane height */
1146 tv_size = atoi(optarg);
1148 case 'v': /* Show version and exit */
1149 printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
1152 case 'w': /* Write to capture file xxx */
1154 save_file = g_strdup(optarg);
1156 capture_option_specified = TRUE;
1160 case 'W': /* Write to capture file FD xxx */
1162 cf.save_file_fd = atoi(optarg);
1164 capture_option_specified = TRUE;
1169 case '?': /* Bad flag - print usage message */
1176 #ifndef HAVE_LIBPCAP
1177 if (capture_option_specified)
1178 fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
1183 if (start_capture) {
1184 if (cf.iface == NULL) {
1185 fprintf(stderr, "ethereal: \"-k\" flag was specified without \"-i\" flag\n");
1189 if (capture_child) {
1190 if (cf.save_file_fd == -1) {
1191 /* XXX - send this to the standard output as something our parent
1192 should put in an error message box? */
1193 fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
1199 /* Build the column format array */
1200 for (i = 0; i < cf.cinfo.num_cols; i++) {
1201 cf.cinfo.col_fmt[i] = get_column_format(i);
1202 cf.cinfo.col_title[i] = g_strdup(get_column_title(i));
1203 cf.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
1205 get_column_format_matches(cf.cinfo.fmt_matx[i], cf.cinfo.col_fmt[i]);
1206 if (cf.cinfo.col_fmt[i] == COL_INFO)
1207 cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
1209 cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
1213 cf.snap = WTAP_MAX_PACKET_SIZE;
1214 else if (cf.snap < MIN_PACKET_SIZE)
1215 cf.snap = MIN_PACKET_SIZE;
1217 rc_file = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(RC_FILE) + 4);
1218 sprintf(rc_file, "%s/%s", getenv("HOME"), RC_FILE);
1219 gtk_rc_parse(rc_file);
1221 if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
1222 fprintf(stderr, "ethereal: Error font %s not found (use -m option)\n", medium_font);
1226 if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
1227 fprintf(stderr, "ethereal: Error font %s not found (use -b option)\n", bold_font);
1232 top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1233 gtk_widget_set_name(top_level, "main window");
1234 gtk_signal_connect(GTK_OBJECT(top_level), "delete_event",
1235 GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
1236 gtk_signal_connect(GTK_OBJECT(top_level), "destroy",
1237 GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
1238 gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
1239 gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
1240 gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
1242 /* Container for menu bar, paned windows and progress/info box */
1243 main_vbox = gtk_vbox_new(FALSE, 1);
1244 gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
1245 gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
1246 gtk_widget_show(main_vbox);
1249 get_main_menu(&menubar, &accel);
1250 gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
1251 gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
1252 gtk_widget_show(menubar);
1254 /* Panes for the packet list, tree, and byte view */
1255 u_pane = gtk_vpaned_new();
1256 gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
1257 l_pane = gtk_vpaned_new();
1258 gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
1259 gtk_container_add(GTK_CONTAINER(main_vbox), l_pane);
1260 gtk_widget_show(u_pane);
1261 gtk_paned_add1 (GTK_PANED(l_pane), u_pane);
1262 gtk_widget_show(l_pane);
1265 pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
1266 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
1267 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1268 gtk_widget_show(pkt_scrollw);
1269 gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
1271 packet_list = gtk_clist_new_with_titles(cf.cinfo.num_cols, cf.cinfo.col_title);
1272 gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
1273 gtk_clist_column_titles_passive(GTK_CLIST(packet_list));
1274 set_plist_sel_browse(prefs->gui_plist_sel_browse);
1275 pl_style = gtk_style_new();
1276 gdk_font_unref(pl_style->font);
1277 pl_style->font = m_r_font;
1278 gtk_widget_set_style(packet_list, pl_style);
1279 gtk_widget_set_name(packet_list, "packet list");
1280 gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
1281 GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
1282 gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
1283 GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
1284 for (i = 0; i < cf.cinfo.num_cols; i++) {
1285 if (get_column_resize_type(cf.cinfo.col_fmt[i]) != RESIZE_MANUAL)
1286 gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
1288 /* Right-justify the packet number column. */
1289 if (cf.cinfo.col_fmt[i] == COL_NUMBER)
1290 gtk_clist_set_column_justification(GTK_CLIST(packet_list), i,
1293 /* Save static column sizes to use during a "-S" capture, so that
1294 the columns don't resize during a live capture. */
1295 cf.cinfo.col_width[i] = get_column_width(get_column_format(i),
1298 gtk_widget_set_usize(packet_list, -1, pl_size);
1299 gtk_widget_show(packet_list);
1302 tv_scrollw = gtk_scrolled_window_new(NULL, NULL);
1303 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(tv_scrollw),
1304 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1305 gtk_paned_add2(GTK_PANED(u_pane), tv_scrollw);
1306 gtk_widget_set_usize(tv_scrollw, -1, tv_size);
1307 gtk_widget_show(tv_scrollw);
1309 tree_view = gtk_ctree_new(1, 0);
1310 /* I need this next line to make the widget work correctly with hidden
1311 * column titles and GTK_SELECTION_BROWSE */
1312 gtk_clist_set_column_auto_resize( GTK_CLIST(tree_view), 0, TRUE );
1313 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1314 set_ptree_sel_browse(prefs->gui_ptree_sel_browse);
1315 set_ptree_line_style(prefs->gui_ptree_line_style);
1316 set_ptree_expander_style(prefs->gui_ptree_expander_style);
1318 gtk_signal_connect(GTK_OBJECT(tree_view), "tree-select-row",
1319 GTK_SIGNAL_FUNC(tree_view_select_row_cb), NULL);
1320 gtk_signal_connect(GTK_OBJECT(tree_view), "tree-unselect-row",
1321 GTK_SIGNAL_FUNC(tree_view_unselect_row_cb), NULL);
1322 gtk_widget_show(tree_view);
1324 item_style = gtk_style_new();
1325 gdk_font_unref(item_style->font);
1326 item_style->font = m_r_font;
1328 /* Byte view. The table is only one row high, but 3 columns
1329 * wide. The middle column contains the GtkText with the hex dump.
1330 * The left and right columns contain vertical scrollbars. They both
1331 * do the same thing, but only one will be shown at a time, in accordance
1332 * with where the user wants the other vertical scrollbars places
1333 * (on the left or the right).
1335 bv_table = gtk_table_new (1, 3, FALSE);
1336 gtk_paned_pack2(GTK_PANED(l_pane), bv_table, FALSE, FALSE);
1337 gtk_widget_set_usize(bv_table, -1, bv_size);
1338 gtk_widget_show(bv_table);
1340 byte_view = gtk_text_new(NULL, NULL);
1341 gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
1342 gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
1343 gtk_table_attach (GTK_TABLE (bv_table), byte_view, 1, 2, 0, 1,
1344 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
1345 gtk_widget_show(byte_view);
1347 /* The gtk_text widget doesn't scroll horizontally (see gtktext.c)
1348 * in the GTK+ distribution, so I removed the horizontal scroll bar
1349 * that used to be here. I tried the gtk_text widget with a
1350 * gtk_scrolled_window w/ viewport, but the vertical scrollowing
1351 * did not work well, and sometimes a few pixels were cut off on
1354 bv_vscroll_left = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
1355 gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll_left, 0, 1, 0, 1,
1356 GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
1358 bv_vscroll_right = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
1359 gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll_right, 2, 3, 0, 1,
1360 GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
1362 /* Now that the 3 panes are created, set the vertical scrollbar
1363 * on the left or right according to the user's preference */
1364 set_scrollbar_placement(prefs->gui_scrollbar_on_right);
1366 /* Progress/filter/info box */
1367 stat_hbox = gtk_hbox_new(FALSE, 1);
1368 gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
1369 gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
1370 gtk_widget_show(stat_hbox);
1372 prog_bar = gtk_progress_bar_new();
1373 gtk_box_pack_start(GTK_BOX(stat_hbox), prog_bar, FALSE, TRUE, 3);
1374 gtk_widget_show(prog_bar);
1376 filter_bt = gtk_button_new_with_label("Filter:");
1377 gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
1378 GTK_SIGNAL_FUNC(filter_dialog_cb), NULL);
1379 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
1380 gtk_widget_show(filter_bt);
1382 filter_cm = gtk_combo_new();
1383 filter_list = g_list_append (filter_list, "");
1384 gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
1385 gtk_combo_disable_activate(GTK_COMBO(filter_cm));
1386 filter_te = GTK_COMBO(filter_cm)->entry;
1387 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
1388 gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
1389 gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
1390 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
1391 gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
1392 GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
1393 gtk_widget_show(filter_cm);
1395 filter_reset = gtk_button_new_with_label("Reset");
1396 gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
1397 gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
1398 GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
1399 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
1400 gtk_widget_show(filter_reset);
1402 /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
1403 * of any widget that ends up calling a callback which needs
1404 * that text entry pointer */
1405 set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
1406 set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
1407 set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
1408 set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
1409 set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
1411 info_bar = gtk_statusbar_new();
1412 main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
1413 file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
1414 gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
1415 gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
1416 gtk_widget_show(info_bar);
1419 Hmmm should we do it here
1422 ethereal_proto_init(); /* Init anything that needs initializing */
1425 /* Is this a "child" ethereal, which is only supposed to pop up a
1426 capture box to let us stop the capture, and run a capture
1427 to a file that our parent will read? */
1428 if (!capture_child) {
1430 /* No. Pop up the main window, and read in a capture file if
1433 gtk_widget_show(top_level);
1435 cf.colors = colfilter_new();
1437 /* If we were given the name of a capture file, read it in now;
1438 we defer it until now, so that, if we can't open it, and pop
1439 up an alert box, the alert box is more likely to come up on
1440 top of the main window - but before the preference-file-error
1441 alert box, so, if we get one of those, it's more likely to come
1444 if (rfilter != NULL) {
1445 if (dfilter_compile(rfilter, &rfcode) != 0) {
1446 simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
1447 rfilter_parse_failed = TRUE;
1450 if (!rfilter_parse_failed) {
1451 if ((err = open_cap_file(cf_name, FALSE, &cf)) == 0) {
1452 /* "open_cap_file()" succeeded, so it closed the previous
1453 capture file, and thus destroyed any previous read filter
1454 attached to "cf". */
1456 err = read_cap_file(&cf);
1457 s = strrchr(cf_name, '/');
1459 last_open_dir = cf_name;
1463 dfilter_destroy(rfcode);
1472 /* If we failed to open the preferences file, pop up an alert box;
1473 we defer it until now, so that the alert box is more likely to
1474 come up on top of the main window. */
1475 if (pf_path != NULL) {
1476 simple_dialog(ESD_TYPE_WARN, NULL,
1477 "Could not open preferences file\n\"%s\": %s.", pf_path,
1478 strerror(pf_open_errno));
1482 if (capture_child) {
1483 /* This is the child process for a sync mode or fork mode capture,
1484 so just do the low-level work of a capture - don't create
1485 a temporary file and fork off *another* child process (so don't
1486 call "do_capture()"). */
1490 /* The capture is done; there's nothing more for us to do. */
1493 if (start_capture) {
1494 /* "-k" was specified; start a capture. */
1495 do_capture(save_file);
1502 ethereal_proto_cleanup();