3 * $Id: main.c,v 1.136 2000/08/17 07:56:37 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?
54 #ifdef HAVE_SYS_TYPES_H
55 #include <sys/types.h>
58 #ifdef HAVE_SYS_STAT_H
63 #include <io.h> /* open/close on win32 */
70 #ifdef HAVE_NETINET_IN_H
71 #include <netinet/in.h>
76 #ifdef NEED_SNPRINTF_H
77 # include "snprintf.h"
80 #if defined(HAVE_UCD_SNMP_SNMP_H)
81 #ifdef HAVE_UCD_SNMP_VERSION_H
82 #include <ucd-snmp/version.h>
83 #endif /* HAVE_UCD_SNMP_VERSION_H */
84 #elif defined(HAVE_SNMP_SNMP_H)
85 #ifdef HAVE_SNMP_VERSION_H
86 #include <snmp/version.h>
87 #endif /* HAVE_SNMP_VERSION_H */
90 #ifdef NEED_STRERROR_H
99 #include "timestamp.h"
106 #include "filter_prefs.h"
107 #include "prefs_dlg.h"
112 #include "simple_dialog.h"
113 #include "proto_draw.h"
116 #include "packet_win.h"
117 #include "gtkglobals.h"
122 GtkWidget *top_level, *packet_list, *tree_view, *byte_view,
123 *info_bar, *tv_scrollw, *pkt_scrollw;
124 static GtkWidget *bv_scrollw;
125 GdkFont *m_r_font, *m_b_font;
126 guint main_ctx, file_ctx;
127 gchar comp_info_str[256];
128 gchar *ethereal_path = NULL;
129 gchar *medium_font = MONO_MEDIUM_FONT;
130 gchar *bold_font = MONO_BOLD_FONT;
131 gchar *last_open_dir = NULL;
133 ts_type timestamp_type = RELATIVE;
135 GtkStyle *item_style;
137 /* Specifies the field currently selected in the GUI protocol tree */
138 field_info *finfo_selected = NULL;
140 static char* hfinfo_numeric_format(header_field_info *hfinfo);
141 static void create_main_window(gint, gint, gint, e_prefs*);
143 /* About Ethereal window */
145 about_ethereal( GtkWidget *w, gpointer data ) {
146 simple_dialog(ESD_TYPE_INFO, NULL,
147 "Ethereal - Network Protocol Analyzer\n"
148 "Version " VERSION " (C) 1998-2000 Gerald Combs <gerald@zing.org>\n"
149 "Compiled with %s\n\n"
151 "Check the man page for complete documentation and\n"
152 "for the list of contributors.\n"
154 "\nSee http://ethereal.zing.org/ for more information.",
159 /* Match selected byte pattern */
161 match_selected_cb(GtkWidget *w, gpointer data)
164 GtkWidget *filter_te;
165 char *ptr, *format, *stringified;
166 int i, dfilter_len, abbrev_len;
168 header_field_info *hfinfo;
170 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
172 if (!finfo_selected) {
173 simple_dialog(ESD_TYPE_CRIT, NULL,
174 "Error determining selected bytes. Please make\n"
175 "sure you have selected a field within the tree\n"
176 "view to be matched.");
180 hfinfo = finfo_selected->hfinfo;
182 abbrev_len = strlen(hfinfo->abbrev);
184 switch(hfinfo->type) {
187 dfilter_len = abbrev_len + 2;
188 buf = g_malloc0(dfilter_len);
189 snprintf(buf, dfilter_len, "%s%s", finfo_selected->value.numeric ? "" : "!",
201 dfilter_len = abbrev_len + 20;
202 buf = g_malloc0(dfilter_len);
203 format = hfinfo_numeric_format(hfinfo);
204 snprintf(buf, dfilter_len, format, hfinfo->abbrev, finfo_selected->value.numeric);
208 dfilter_len = abbrev_len + 4 + 15 + 1;
209 buf = g_malloc0(dfilter_len);
210 snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
211 ipv4_addr_str(&(finfo_selected->value.ipv4)));
215 dfilter_len = abbrev_len + 15;
216 buf = g_malloc0(dfilter_len);
217 snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev,
218 finfo_selected->value.numeric);
222 stringified = ip6_to_str((struct e_in6_addr*) &(finfo_selected->value.ipv6));
223 dfilter_len = abbrev_len + 4 + strlen(stringified) + 1;
224 buf = g_malloc0(dfilter_len);
225 snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
230 dfilter_len = abbrev_len + 30;
231 buf = g_malloc0(dfilter_len);
232 snprintf(buf, dfilter_len, "%s == %f", hfinfo->abbrev,
233 finfo_selected->value.floating);
237 dfilter_len = abbrev_len + 22;
238 buf = g_malloc0(dfilter_len);
239 snprintf(buf, dfilter_len, "%s == %s",
241 ether_to_str(finfo_selected->value.ether));
245 case FT_ABSOLUTE_TIME:
246 case FT_RELATIVE_TIME:
247 memcpy(&fi->value.time, va_arg(ap, struct timeval*),
248 sizeof(struct timeval));
252 /* This g_strdup'ed memory is freed in proto_tree_free_node() */
253 fi->value.string = g_strdup(va_arg(ap, char*));
261 c = cfile.pd + finfo_selected->start;
262 buf = g_malloc0(32 + finfo_selected->length * 3);
265 sprintf(ptr, "frame[%d] == ", finfo_selected->start);
266 ptr = buf+strlen(buf);
268 if (finfo_selected->length == 1) {
269 sprintf(ptr, "0x%02x", *c++);
272 for (i=0;i<finfo_selected->length; i++) {
274 sprintf(ptr, "%02x", *c++);
277 sprintf(ptr, ":%02x", *c++);
279 ptr = buf+strlen(buf);
285 /* create a new one and set the display filter entry accordingly */
286 gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
288 /* Run the display filter so it goes in effect. */
289 filter_packets(&cfile, buf);
291 /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
295 hfinfo_numeric_format(header_field_info *hfinfo)
299 /* Pick the proper format string */
300 switch(hfinfo->display) {
303 case BASE_OCT: /* I'm lazy */
304 case BASE_BIN: /* I'm lazy */
305 switch(hfinfo->type) {
319 g_assert_not_reached();
324 switch(hfinfo->type) {
326 format = "%s == 0x%02x";
329 format = "%s == 0x%04x";
332 format = "%s == 0x%06x";
335 format = "%s == 0x%08x";
338 g_assert_not_reached();
343 g_assert_not_reached();
350 /* Run the current display filter on the current packet set, and
353 filter_activate_cb(GtkWidget *w, gpointer data)
355 GtkCombo *filter_cm = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_CM_KEY);
356 GList *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY);
357 GList *li, *nl = NULL;
358 gboolean add_filter = TRUE;
360 char *s = gtk_entry_get_text(GTK_ENTRY(w));
362 /* GtkCombos don't let us get at their list contents easily, so we maintain
363 our own filter list, and feed it to gtk_combo_set_popdown_strings when
364 a new filter is added. */
365 if (filter_packets(&cfile, g_strdup(s))) {
366 li = g_list_first(filter_list);
368 if (li->data && strcmp(s, li->data) == 0)
374 filter_list = g_list_append(filter_list, g_strdup(s));
375 li = g_list_first(filter_list);
377 nl = g_list_append(nl, strdup(li->data));
380 gtk_combo_set_popdown_strings(filter_cm, nl);
381 gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
386 /* redisplay with no display filter */
388 filter_reset_cb(GtkWidget *w, gpointer data)
390 GtkWidget *filter_te = NULL;
392 if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
393 gtk_entry_set_text(GTK_ENTRY(filter_te), "");
396 filter_packets(&cfile, NULL);
399 /* GTKClist compare routine, overrides default to allow numeric comparison */
401 packet_list_compare(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
403 /* Get row text strings */
404 char *text1 = GTK_CELL_TEXT (((GtkCListRow *)ptr1)->cell[clist->sort_column])->text;
405 char *text2 = GTK_CELL_TEXT (((GtkCListRow *)ptr2)->cell[clist->sort_column])->text;
407 /* Attempt to convert to numbers */
408 double num1 = atof(text1);
409 double num2 = atof(text2);
411 gint col_fmt = cfile.cinfo.col_fmt[clist->sort_column];
413 if ((col_fmt == COL_NUMBER) || (col_fmt == COL_REL_TIME) || (col_fmt == COL_DELTA_TIME) ||
414 ((col_fmt == COL_CLS_TIME) && (timestamp_type == RELATIVE)) ||
415 ((col_fmt == COL_CLS_TIME) && (timestamp_type == DELTA)) ||
416 (col_fmt == COL_UNRES_SRC_PORT) || (col_fmt == COL_UNRES_DST_PORT) ||
417 ((num1 != 0) && (num2 != 0) && ((col_fmt == COL_DEF_SRC_PORT) || (col_fmt == COL_RES_SRC_PORT) ||
418 (col_fmt == COL_DEF_DST_PORT) || (col_fmt == COL_RES_DST_PORT))) ||
419 (col_fmt == COL_PACKET_LENGTH)) {
421 /* Compare numeric column */
425 else if (num1 > num2)
433 /* Compare text column */
435 return (text1 != NULL);
440 return strcmp(text1, text2);
444 /* What to do when a column is clicked */
446 packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data)
448 if (column == clist->sort_column) {
449 if (clist->sort_type == GTK_SORT_ASCENDING)
450 clist->sort_type = GTK_SORT_DESCENDING;
452 clist->sort_type = GTK_SORT_ASCENDING;
455 clist->sort_type = GTK_SORT_ASCENDING;
456 gtk_clist_set_sort_column(clist, column);
459 gtk_clist_sort(clist);
462 /* What to do when a list item is selected/unselected */
464 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
467 select_packet(&cfile, row);
471 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
472 unselect_packet(&cfile);
476 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
481 finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
484 finfo_selected = finfo;
486 set_menus_for_selected_tree_row(TRUE);
488 packet_hex_print(GTK_TEXT(byte_view), cfile.pd, cfile.current_frame->cap_len,
489 finfo->start, finfo->length, cfile.current_frame->flags.encoding);
493 tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
495 finfo_selected = NULL;
496 set_menus_for_selected_tree_row(FALSE);
497 packet_hex_print(GTK_TEXT(byte_view), cfile.pd, cfile.current_frame->cap_len,
498 -1, -1, cfile.current_frame->flags.encoding);
501 void collapse_all_cb(GtkWidget *widget, gpointer data) {
502 if (cfile.protocol_tree)
503 collapse_all_tree(cfile.protocol_tree, tree_view);
506 void expand_all_cb(GtkWidget *widget, gpointer data) {
507 if (cfile.protocol_tree)
508 expand_all_tree(cfile.protocol_tree, tree_view);
511 void resolve_name_cb(GtkWidget *widget, gpointer data) {
512 if (cfile.protocol_tree) {
513 int tmp = g_resolving_actif;
514 g_resolving_actif = 1;
515 gtk_clist_clear ( GTK_CLIST(tree_view) );
516 proto_tree_draw(cfile.protocol_tree, tree_view);
517 g_resolving_actif = tmp;
521 /* Set the scrollbar placement of a scrolled window based upon pos value:
522 0 = left, 1 = right */
524 set_scrollbar_placement_scrollw(GtkWidget *scrollw, int pos) /* 0=left, 1=right */
527 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
528 GTK_CORNER_TOP_LEFT);
530 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
531 GTK_CORNER_TOP_RIGHT);
535 /* List of all scrolled windows, so we can globally set the scrollbar
536 placement of them. */
537 static GList *scrolled_windows;
539 /* Add a scrolled window to the list of scrolled windows. */
540 static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
543 remember_scrolled_window(GtkWidget *scrollw)
545 scrolled_windows = g_list_append(scrolled_windows, scrollw);
547 /* Catch the "destroy" event on the widget, so that we remove it from
548 the list when it's destroyed. */
549 gtk_signal_connect(GTK_OBJECT(scrollw), "destroy",
550 GTK_SIGNAL_FUNC(forget_scrolled_window), NULL);
553 /* Remove a scrolled window from the list of scrolled windows. */
555 forget_scrolled_window(GtkWidget *scrollw, gpointer data)
557 scrolled_windows = g_list_remove(scrolled_windows, scrollw);
561 set_scrollbar_placement_cb(gpointer data, gpointer user_data)
563 set_scrollbar_placement_scrollw((GtkWidget *)data,
567 /* Set the scrollbar placement of all scrolled windows based on pos value:
568 0 = left, 1 = right */
570 set_scrollbar_placement_all(int pos)
572 g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, &pos);
575 /* Set the selection mode of the packet list window. */
577 set_plist_sel_browse(gboolean val)
582 (GTK_CLIST(packet_list)->selection_mode == GTK_SELECTION_SINGLE);
584 if (val == old_val) {
586 * The mode isn't changing, so don't do anything.
587 * In particular, don't gratuitiously unselect the
590 * XXX - why do we have to unselect the current packet
591 * ourselves? The documentation for the GtkCList at
593 * http://developer.gnome.org/doc/API/gtk/gtkclist.html
595 * says "Note that setting the widget's selection mode to
596 * one of GTK_SELECTION_BROWSE or GTK_SELECTION_SINGLE will
597 * cause all the items in the GtkCList to become deselected."
603 unselect_packet(&cfile);
605 /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
606 * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
608 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_SINGLE);
611 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_BROWSE);
615 /* Set the selection mode of a given packet tree window. */
617 set_ptree_sel_browse(GtkWidget *tv, gboolean val)
619 /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
620 * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
622 gtk_clist_set_selection_mode(GTK_CLIST(tv), GTK_SELECTION_SINGLE);
625 gtk_clist_set_selection_mode(GTK_CLIST(tv), GTK_SELECTION_BROWSE);
629 /* Set the selection mode of all packet tree windows. */
631 set_ptree_sel_browse_all(gboolean val)
633 set_ptree_sel_browse(tree_view, val);
634 set_ptree_sel_browse_packet_wins(val);
637 /* Set the line style of a given packet tree window. */
639 set_ptree_line_style(GtkWidget *tv, gint style)
641 /* I'm using an assert here since the preferences code limits
642 * the user input, both in the GUI and when reading the preferences file.
643 * If the value is incorrect, it's a program error, not a user-initiated error.
645 g_assert(style >= GTK_CTREE_LINES_NONE && style <= GTK_CTREE_LINES_TABBED);
646 gtk_ctree_set_line_style( GTK_CTREE(tv), style );
649 /* Set the line style of all packet tree window. */
651 set_ptree_line_style_all(gint style)
653 set_ptree_line_style(tree_view, style);
654 set_ptree_line_style_packet_wins(style);
657 /* Set the expander style of a given packet tree window. */
659 set_ptree_expander_style(GtkWidget *tv, gint style)
661 /* I'm using an assert here since the preferences code limits
662 * the user input, both in the GUI and when reading the preferences file.
663 * If the value is incorrect, it's a program error, not a user-initiated error.
665 g_assert(style >= GTK_CTREE_EXPANDER_NONE && style <= GTK_CTREE_EXPANDER_CIRCULAR);
666 gtk_ctree_set_expander_style( GTK_CTREE(tv), style );
670 set_ptree_expander_style_all(gint style)
672 set_ptree_expander_style(tree_view, style);
673 set_ptree_expander_style_packet_wins(style);
677 main_window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
679 file_quit_cmd_cb(widget, data);
681 /* Say that the window should be deleted. */
686 file_quit_cmd_cb (GtkWidget *widget, gpointer data)
688 /* XXX - should we check whether the capture file is an
689 unsaved temporary file for a live capture and, if so,
690 pop up a "do you want to exit without saving the capture
691 file?" dialog, and then just return, leaving said dialog
692 box to forcibly quit if the user clicks "OK"?
694 If so, note that this should be done in a subroutine that
695 returns TRUE if we do so, and FALSE otherwise, and that
696 "main_window_delete_event_cb()" should return its
699 /* Are we in the middle of reading a capture? */
700 if (cfile.state == FILE_READ_IN_PROGRESS) {
701 /* Yes, so we can't just close the file and quit, as
702 that may yank the rug out from under the read in
703 progress; instead, just set the state to
704 "FILE_READ_ABORTED" and return - the code doing the read
705 will check for that and, if it sees that, will clean
707 cfile.state = FILE_READ_ABORTED;
709 /* Close any capture file we have open; on some OSes, you
710 can't unlink a temporary capture file if you have it
712 "close_cap_file()" will unlink it after closing it if
713 it's a temporary file.
715 We do this here, rather than after the main loop returns,
716 as, after the main loop returns, the main window may have
717 been destroyed (if this is called due to a "destroy"
718 even on the main window rather than due to the user
719 selecting a menu item), and there may be a crash
720 or other problem when "close_cap_file()" tries to
721 clean up stuff in the main window.
723 XXX - is there a better place to put this?
724 Or should we have a routine that *just* closes the
725 capture file, and doesn't do anything with the UI,
726 which we'd call here, and another routine that
727 calls that routine and also cleans up the UI, which
728 we'd call elsewhere? */
729 close_cap_file(&cfile, info_bar);
731 /* Exit by leaving the main loop, so that any quit functions
732 we registered get called. */
740 fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled with %s\n",
743 fprintf(stderr, "%s [ -vh ] [ -kQS ] [ -b <bold font> ] [ -B <byte view height> ]\n",
745 fprintf(stderr, "\t[ -c count ] [ -D ] [ -f <capture filter> ] [ -i interface ]\n");
746 fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -o <preference setting> ] ...\n");
747 fprintf(stderr, "\t[ -P <packet list height> ] [ -r infile ] [ -R <read filter> ]\n");
748 fprintf(stderr, "\t[ -s snaplen ] [ -t <time stamp format> ] [ -T <tree view height> ]\n");
749 fprintf(stderr, "\t[ -w savefile ]\n");
751 fprintf(stderr, "%s [ -vh ] [ -b <bold font> ] [ -B <byte view height> ]\n",
753 fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -o <preference setting ] ...\n");
754 fprintf(stderr, "\t[ -P <packet list height> ] [ -r infile ] [ -R <read filter> ]\n");
755 fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
759 /* And now our feature presentation... [ fade to music ] */
761 main(int argc, char *argv[])
770 gboolean arg_error = FALSE;
773 char pcap_version[] = "0.4a6";
775 extern char pcap_version[];
778 char *gpf_path, *pf_path;
779 int gpf_open_errno, pf_open_errno;
782 gboolean start_capture = FALSE;
783 gchar *save_file = NULL;
785 gchar err_str[PCAP_ERRBUF_SIZE];
787 gboolean capture_option_specified = FALSE;
789 gint pl_size = 280, tv_size = 95, bv_size = 75;
790 gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
791 dfilter *rfcode = NULL;
792 gboolean rfilter_parse_failed = FALSE;
795 ethereal_path = argv[0];
798 command_name = get_basename(ethereal_path);
799 /* Set "capture_child" to indicate whether this is going to be a child
800 process for a "-S" capture. */
801 capture_child = (strcmp(command_name, CHILD_NAME) == 0);
804 /* Register all dissectors; we must do this before checking for the
805 "-G" flag, as the "-G" flag dumps a list of fields registered
806 by the dissectors, and we must do it before we read the preferences,
807 in case any dissectors register preferences. */
810 /* Now register the preferences for any non-dissector modules.
811 We must do that before we read the preferences as well. */
812 prefs_register_modules();
814 /* If invoked with the "-G" flag, we dump out a glossary of
815 display filter symbols.
817 We must do this before calling "gtk_init()", because "gtk_init()"
818 tries to open an X display, and we don't want to have to do any X
819 stuff just to do a build.
821 Given that we call "gtk_init()" before doing the regular argument
822 list processing, so that it can handle X and GTK+ arguments and
823 remove them from the list at which we look, this means we must do
824 this before doing the regular argument list processing, as well.
828 you must give the "-G" flag as the first flag on the command line;
830 you must give it as "-G", nothing more, nothing less;
832 any arguments after the "-G" flag will not be used. */
833 if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
834 proto_registrar_dump();
838 /* Set the current locale according to the program environment.
839 * We haven't localized anything, but some GTK widgets are localized
840 * (the file selection dialogue, for example).
841 * This also sets the C-language locale to the native environment. */
844 /* Let GTK get its args */
845 gtk_init (&argc, &argv);
847 prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
849 /* Initialize the capture file struct */
851 cfile.plist_end = NULL;
853 cfile.filename = NULL;
854 cfile.user_saved = FALSE;
855 cfile.is_tempfile = FALSE;
857 cfile.dfilter = NULL;
860 cfile.cfilter = g_strdup(EMPTY_FILTER);
863 cfile.save_file = NULL;
864 cfile.save_file_fd = -1;
865 cfile.snap = WTAP_MAX_PACKET_SIZE;
867 cfile.cinfo.num_cols = prefs->num_cols;
868 cfile.cinfo.col_fmt = (gint *) g_malloc(sizeof(gint) * cfile.cinfo.num_cols);
869 cfile.cinfo.fmt_matx = (gboolean **) g_malloc(sizeof(gboolean *) * cfile.cinfo.num_cols);
870 cfile.cinfo.col_width = (gint *) g_malloc(sizeof(gint) * cfile.cinfo.num_cols);
871 cfile.cinfo.col_title = (gchar **) g_malloc(sizeof(gchar *) * cfile.cinfo.num_cols);
872 cfile.cinfo.col_data = (gchar **) g_malloc(sizeof(gchar *) * cfile.cinfo.num_cols);
874 /* Assemble the compile-time options */
875 snprintf(comp_info_str, 256,
876 #ifdef GTK_MAJOR_VERSION
877 "GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
880 "GTK+ (version unknown), %s%s, %s%s, %s%s",
884 "with libpcap ", pcap_version,
886 "without libpcap", "",
891 "with libz ", ZLIB_VERSION,
892 #else /* ZLIB_VERSION */
893 "with libz ", "(version unknown)",
894 #endif /* ZLIB_VERSION */
895 #else /* HAVE_LIBZ */
897 #endif /* HAVE_LIBZ */
899 /* Oh, this is pretty */
900 #if defined(HAVE_UCD_SNMP_SNMP_H)
901 #ifdef HAVE_UCD_SNMP_VERSION_H
902 "with UCD SNMP ", VersionInfo
903 #else /* HAVE_UCD_SNMP_VERSION_H */
904 "with UCD SNMP ", "(version unknown)"
905 #endif /* HAVE_UCD_SNMP_VERSION_H */
906 #elif defined(HAVE_SNMP_SNMP_H)
907 #ifdef HAVE_SNMP_VERSION_H
908 "with CMU SNMP ", snmp_Version()
909 #else /* HAVE_SNMP_VERSION_H */
910 "with CMU SNMP ", "(version unknown)"
911 #endif /* HAVE_SNMP_VERSION_H */
917 /* Now get our args */
918 while ((opt = getopt(argc, argv, "b:B:c:Df:hi:km:no:P:Qr:R:Ss:t:T:w:W:vZ:")) != EOF) {
920 case 'b': /* Bold font */
921 bold_font = g_strdup(optarg);
923 case 'B': /* Byte view pane height */
924 bv_size = atoi(optarg);
926 case 'c': /* Capture xxx packets */
928 cfile.count = atoi(optarg);
930 capture_option_specified = TRUE;
934 case 'D': /* Turn off DSCP printing */
935 g_ip_dscp_actif = FALSE;
940 g_free(cfile.cfilter);
941 cfile.cfilter = g_strdup(optarg);
943 capture_option_specified = TRUE;
947 case 'h': /* Print help and exit */
951 case 'i': /* Use interface xxx */
953 cfile.iface = g_strdup(optarg);
955 capture_option_specified = TRUE;
959 case 'k': /* Start capture immediately */
961 start_capture = TRUE;
963 capture_option_specified = TRUE;
967 case 'm': /* Medium font */
968 medium_font = g_strdup(optarg);
970 case 'n': /* No name resolution */
971 g_resolving_actif = 0;
973 case 'o': /* Override preference from command line */
974 switch (prefs_set_pref(optarg)) {
976 case PREFS_SET_SYNTAX_ERR:
977 fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
981 case PREFS_SET_NO_SUCH_PREF:
982 fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
988 case 'P': /* Packet list pane height */
989 pl_size = atoi(optarg);
991 case 'Q': /* Quit after capture (just capture to file) */
994 start_capture = TRUE; /*** -Q implies -k !! ***/
996 capture_option_specified = TRUE;
1000 case 'r': /* Read capture file xxx */
1001 /* We may set "last_open_dir" to "cf_name", and if we change
1002 "last_open_dir" later, we free the old value, so we have to
1003 set "cf_name" to something that's been allocated. */
1004 cf_name = g_strdup(optarg);
1006 case 'R': /* Read file filter */
1009 case 's': /* Set the snapshot (capture) length */
1011 cfile.snap = atoi(optarg);
1013 capture_option_specified = TRUE;
1017 case 'S': /* "Sync" mode: used for following file ala tail -f */
1021 capture_option_specified = TRUE;
1025 case 't': /* Time stamp type */
1026 if (strcmp(optarg, "r") == 0)
1027 timestamp_type = RELATIVE;
1028 else if (strcmp(optarg, "a") == 0)
1029 timestamp_type = ABSOLUTE;
1030 else if (strcmp(optarg, "d") == 0)
1031 timestamp_type = DELTA;
1033 fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
1035 fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
1036 fprintf(stderr, "or \"d\" for delta.\n");
1040 case 'T': /* Tree view pane height */
1041 tv_size = atoi(optarg);
1043 case 'v': /* Show version and exit */
1044 printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
1047 case 'w': /* Write to capture file xxx */
1049 save_file = g_strdup(optarg);
1051 capture_option_specified = TRUE;
1055 case 'W': /* Write to capture file FD xxx */
1057 cfile.save_file_fd = atoi(optarg);
1059 capture_option_specified = TRUE;
1065 case 'Z': /* Write to pipe FD XXX */
1067 /* associate stdout with pipe */
1069 if (dup2(i, 1) < 0) {
1070 fprintf(stderr, "Unable to dup pipe handle\n");
1074 capture_option_specified = TRUE;
1076 #endif /* HAVE_LIBPCAP */
1081 case '?': /* Bad flag - print usage message */
1087 /* Notify all registered modules that have had any of their preferences
1088 changed either from one of the preferences file or from the command
1089 line that its preferences have changed. */
1092 #ifndef HAVE_LIBPCAP
1093 if (capture_option_specified)
1094 fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
1099 if (start_capture) {
1100 /* We're supposed to do a live capture; did the user specify an interface
1102 if (cfile.iface == NULL) {
1103 /* No - pick the first one from the list of interfaces. */
1104 if_list = get_interface_list(&err, err_str);
1105 if (if_list == NULL) {
1108 case CANT_GET_INTERFACE_LIST:
1109 fprintf(stderr, "ethereal: Can't get list of interfaces: %s\n",
1113 case NO_INTERFACES_FOUND:
1114 fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
1119 cfile.iface = g_strdup(if_list->data); /* first interface */
1120 free_interface_list(if_list);
1123 if (capture_child) {
1124 if (cfile.save_file_fd == -1) {
1125 /* XXX - send this to the standard output as something our parent
1126 should put in an error message box? */
1127 fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
1133 /* Build the column format array */
1134 for (i = 0; i < cfile.cinfo.num_cols; i++) {
1135 cfile.cinfo.col_fmt[i] = get_column_format(i);
1136 cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
1137 cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
1139 get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
1140 if (cfile.cinfo.col_fmt[i] == COL_INFO)
1141 cfile.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
1143 cfile.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
1147 cfile.snap = WTAP_MAX_PACKET_SIZE;
1148 else if (cfile.snap < MIN_PACKET_SIZE)
1149 cfile.snap = MIN_PACKET_SIZE;
1151 rc_file = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(RC_FILE) + 4);
1152 sprintf(rc_file, "%s/%s", get_home_dir(), RC_FILE);
1153 gtk_rc_parse(rc_file);
1155 if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
1156 fprintf(stderr, "ethereal: Error font %s not found (use -m option)\n", medium_font);
1160 if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
1161 fprintf(stderr, "ethereal: Error font %s not found (use -b option)\n", bold_font);
1165 create_main_window(pl_size, tv_size, bv_size, prefs);
1168 /* Is this a "child" ethereal, which is only supposed to pop up a
1169 capture box to let us stop the capture, and run a capture
1170 to a file that our parent will read? */
1171 if (!capture_child) {
1173 /* No. Pop up the main window, and read in a capture file if
1176 gtk_widget_show(top_level);
1177 set_menus_for_capture_file(FALSE);
1179 cfile.colors = colfilter_new();
1181 /* If we were given the name of a capture file, read it in now;
1182 we defer it until now, so that, if we can't open it, and pop
1183 up an alert box, the alert box is more likely to come up on
1184 top of the main window - but before the preference-file-error
1185 alert box, so, if we get one of those, it's more likely to come
1188 if (rfilter != NULL) {
1189 if (dfilter_compile(rfilter, &rfcode) != 0) {
1190 simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
1191 rfilter_parse_failed = TRUE;
1194 if (!rfilter_parse_failed) {
1195 if ((err = open_cap_file(cf_name, FALSE, &cfile)) == 0) {
1196 /* "open_cap_file()" succeeded, so it closed the previous
1197 capture file, and thus destroyed any previous read filter
1198 attached to "cf". */
1199 cfile.rfcode = rfcode;
1200 switch (read_cap_file(&cfile, &err)) {
1204 /* Just because we got an error, that doesn't mean we were unable
1205 to read any of the file; we handle what we could get from the
1214 /* Save the name of the containing directory specified in the
1215 path name, if any; we can write over cf_name, which is a
1216 good thing, given that "get_dirname()" does write over its
1218 s = get_dirname(cf_name);
1222 dfilter_destroy(rfcode);
1223 cfile.rfcode = NULL;
1231 /* If the global preferences file exists but we failed to open it,
1232 pop up an alert box; we defer that until now, so that the alert
1233 box is more likely to come up on top of the main window. */
1234 if (gpf_path != NULL) {
1235 simple_dialog(ESD_TYPE_WARN, NULL,
1236 "Could not open global preferences file\n\"%s\": %s.", gpf_path,
1237 strerror(gpf_open_errno));
1240 /* If the user's preferences file exists but we failed to open it,
1241 pop up an alert box; we defer that until now, so that the alert
1242 box is more likely to come up on top of the main window. */
1243 if (pf_path != NULL) {
1244 simple_dialog(ESD_TYPE_WARN, NULL,
1245 "Could not open your preferences file\n\"%s\": %s.", pf_path,
1246 strerror(pf_open_errno));
1250 if (capture_child) {
1251 /* This is the child process for a sync mode or fork mode capture,
1252 so just do the low-level work of a capture - don't create
1253 a temporary file and fork off *another* child process (so don't
1254 call "do_capture()"). */
1258 /* The capture is done; there's nothing more for us to do. */
1261 if (start_capture) {
1262 /* "-k" was specified; start a capture. */
1263 do_capture(save_file);
1266 set_menus_for_capture_in_progress(FALSE);
1270 set_menus_for_capture_in_progress(FALSE);
1280 /* This isn't reached, but we need it to keep GCC from complaining
1281 that "main()" returns without returning a value - it knows that
1282 "exit()" never returns, but it doesn't know that "gtk_exit()"
1283 doesn't, as GTK+ doesn't declare it with the attribute
1285 return 0; /* not reached */
1290 /* We build this as a GUI subsystem application on Win32, so
1291 "WinMain()", not "main()", gets called.
1293 Hack shamelessly stolen from the Win32 port of the GIMP. */
1295 #define _stdcall __attribute__((stdcall))
1299 WinMain (struct HINSTANCE__ *hInstance,
1300 struct HINSTANCE__ *hPrevInstance,
1304 return main (__argc, __argv);
1310 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
1312 GtkWidget *main_vbox, *menubar, *u_pane, *l_pane,
1314 *filter_bt, *filter_cm, *filter_te,
1316 GList *filter_list = NULL;
1318 GtkAccelGroup *accel;
1322 top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1323 gtk_widget_set_name(top_level, "main window");
1324 gtk_signal_connect(GTK_OBJECT(top_level), "delete_event",
1325 GTK_SIGNAL_FUNC(main_window_delete_event_cb), NULL);
1326 gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
1327 gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
1328 gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
1330 /* Container for menu bar, paned windows and progress/info box */
1331 main_vbox = gtk_vbox_new(FALSE, 1);
1332 gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
1333 gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
1334 gtk_widget_show(main_vbox);
1337 get_main_menu(&menubar, &accel);
1338 gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
1339 gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
1340 gtk_widget_show(menubar);
1342 /* Panes for the packet list, tree, and byte view */
1343 u_pane = gtk_vpaned_new();
1344 gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
1345 l_pane = gtk_vpaned_new();
1346 gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
1347 gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
1348 gtk_widget_show(l_pane);
1349 gtk_paned_add2(GTK_PANED(u_pane), l_pane);
1350 gtk_widget_show(u_pane);
1353 pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
1354 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
1355 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1356 set_scrollbar_placement_scrollw(pkt_scrollw, prefs->gui_scrollbar_on_right);
1357 remember_scrolled_window(pkt_scrollw);
1358 gtk_widget_show(pkt_scrollw);
1359 gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
1361 packet_list = gtk_clist_new_with_titles(cfile.cinfo.num_cols, cfile.cinfo.col_title);
1362 gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
1364 set_plist_sel_browse(prefs->gui_plist_sel_browse);
1365 pl_style = gtk_style_new();
1366 gdk_font_unref(pl_style->font);
1367 pl_style->font = m_r_font;
1368 gtk_widget_set_style(packet_list, pl_style);
1369 gtk_widget_set_name(packet_list, "packet list");
1370 gtk_signal_connect (GTK_OBJECT (packet_list), "click_column",
1371 GTK_SIGNAL_FUNC(packet_list_click_column_cb), NULL);
1372 gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
1373 GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
1374 gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
1375 GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
1376 for (i = 0; i < cfile.cinfo.num_cols; i++) {
1377 if (get_column_resize_type(cfile.cinfo.col_fmt[i]) != RESIZE_MANUAL)
1378 gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
1380 /* Right-justify the packet number column. */
1381 if (cfile.cinfo.col_fmt[i] == COL_NUMBER)
1382 gtk_clist_set_column_justification(GTK_CLIST(packet_list), i,
1385 /* Save static column sizes to use during a "-S" capture, so that
1386 the columns don't resize during a live capture. */
1387 cfile.cinfo.col_width[i] = gdk_string_width(pl_style->font,
1388 get_column_longest_string(get_column_format(i)));
1390 gtk_widget_set_usize(packet_list, -1, pl_size);
1391 gtk_signal_connect_object(GTK_OBJECT(packet_list), "button_press_event",
1392 GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
1393 gtk_clist_set_compare_func(GTK_CLIST(packet_list), packet_list_compare);
1394 gtk_widget_show(packet_list);
1397 create_tree_view(tv_size, prefs, l_pane, &tv_scrollw, &tree_view,
1398 prefs->gui_scrollbar_on_right);
1399 gtk_signal_connect(GTK_OBJECT(tree_view), "tree-select-row",
1400 GTK_SIGNAL_FUNC(tree_view_select_row_cb), NULL);
1401 gtk_signal_connect(GTK_OBJECT(tree_view), "tree-unselect-row",
1402 GTK_SIGNAL_FUNC(tree_view_unselect_row_cb), NULL);
1403 gtk_signal_connect_object(GTK_OBJECT(tree_view), "button_press_event",
1404 GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY));
1405 gtk_widget_show(tree_view);
1407 item_style = gtk_style_new();
1408 gdk_font_unref(item_style->font);
1409 item_style->font = m_r_font;
1412 create_byte_view(bv_size, l_pane, &byte_view, &bv_scrollw,
1413 prefs->gui_scrollbar_on_right);
1415 /* Filter/info box */
1416 stat_hbox = gtk_hbox_new(FALSE, 1);
1417 gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
1418 gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
1419 gtk_widget_show(stat_hbox);
1421 filter_bt = gtk_button_new_with_label("Filter:");
1422 /* A non-null pointer passed to "filter_browse_cb()" causes it to
1423 give the dialog box it pops up an "Apply" button. */
1424 gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
1425 GTK_SIGNAL_FUNC(filter_browse_cb), "");
1426 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
1427 gtk_widget_show(filter_bt);
1429 filter_cm = gtk_combo_new();
1430 filter_list = g_list_append (filter_list, "");
1431 gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
1432 gtk_combo_disable_activate(GTK_COMBO(filter_cm));
1433 filter_te = GTK_COMBO(filter_cm)->entry;
1434 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
1435 gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
1436 gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
1437 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
1438 gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
1439 GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
1440 gtk_widget_show(filter_cm);
1442 filter_reset = gtk_button_new_with_label("Reset");
1443 gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
1444 gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
1445 GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
1446 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
1447 gtk_widget_show(filter_reset);
1449 /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
1450 * of any widget that ends up calling a callback which needs
1451 * that text entry pointer */
1452 set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
1453 set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
1454 set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
1455 set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
1456 set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
1458 info_bar = gtk_statusbar_new();
1459 main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
1460 file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
1461 gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
1462 gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
1463 gtk_widget_show(info_bar);