3 * $Id: main.c,v 1.219 2001/12/18 19:09:08 gram Exp $
5 * Ethereal - Network traffic analyzer
6 * By Gerald Combs <gerald@ethereal.com>
7 * Copyright 1998 Gerald Combs
9 * Richard Sharpe, 13-Feb-1999, added support for initializing structures
10 * needed by dissect routines
11 * Jeff Foster, 2001/03/12, added support tabbed hex display windowss
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 * - Check for end of packet in dissect_* routines.
33 * - Multiple window support
34 * - Add cut/copy/paste
35 * - Create header parsing routines
36 * - Make byte view selections more fancy?
57 #ifdef HAVE_SYS_TYPES_H
58 #include <sys/types.h>
61 #ifdef HAVE_SYS_STAT_H
66 #include <io.h> /* open/close on win32 */
73 #ifdef HAVE_NETINET_IN_H
74 #include <netinet/in.h>
84 #include <zlib.h> /* to get the libz version number */
87 #ifdef NEED_SNPRINTF_H
88 # include "snprintf.h"
91 #if defined(HAVE_UCD_SNMP_SNMP_H)
92 #ifdef HAVE_UCD_SNMP_VERSION_H
93 #include <ucd-snmp/version.h>
94 #endif /* HAVE_UCD_SNMP_VERSION_H */
95 #elif defined(HAVE_SNMP_SNMP_H)
96 #ifdef HAVE_SNMP_VERSION_H
97 #include <snmp/version.h>
98 #endif /* HAVE_SNMP_VERSION_H */
101 #ifdef NEED_STRERROR_H
102 #include "strerror.h"
109 #ifdef WIN32 /* Needed for console I/O */
115 #include <epan/filesystem.h>
116 #include <epan_dissect.h>
119 #include "timestamp.h"
128 #include "color_utils.h"
129 #include "filter_prefs.h"
130 #include "prefs_dlg.h"
131 #include "file_dlg.h"
136 #include "pcap-util.h"
138 #include "statusbar.h"
139 #include "simple_dialog.h"
140 #include "proto_draw.h"
141 #include "dfilter/dfilter.h"
143 #include "packet_win.h"
144 #include "gtkglobals.h"
148 #include "register.h"
150 #include "ringbuffer.h"
152 #include "image/clist_ascend.xpm"
153 #include "image/clist_descend.xpm"
156 #include "capture-wpcap.h"
159 typedef struct column_arrows {
161 GtkWidget *ascend_pm;
162 GtkWidget *descend_pm;
166 GtkWidget *top_level, *packet_list, *tree_view, *byte_nb_ptr,
167 *tv_scrollw, *pkt_scrollw;
168 static GtkWidget *info_bar, *bv_scrollw;
169 GdkFont *m_r_font, *m_b_font;
170 guint m_font_height, m_font_width;
171 static guint main_ctx, file_ctx, help_ctx;
172 static GString *comp_info_str;
173 gchar *ethereal_path = NULL;
174 gchar *last_open_dir = NULL;
176 ts_type timestamp_type = RELATIVE;
178 GtkStyle *item_style;
180 /* Specifies the field currently selected in the GUI protocol tree */
181 field_info *finfo_selected = NULL;
184 static gboolean has_no_console; /* TRUE if app has no console */
185 static gboolean console_was_created; /* TRUE if console was created */
186 static void create_console(void);
187 static void destroy_console(void);
188 static void console_log_handler(const char *log_domain,
189 GLogLevelFlags log_level, const char *message, gpointer user_data);
192 static void create_main_window(gint, gint, gint, e_prefs*);
194 /* About Ethereal window */
196 about_ethereal( GtkWidget *w, gpointer data ) {
197 simple_dialog(ESD_TYPE_INFO, NULL,
198 "Ethereal - Network Protocol Analyzer\n"
199 "Version " VERSION " (C) 1998-2000 Gerald Combs <gerald@ethereal.com>\n"
202 "Check the man page for complete documentation and\n"
203 "for the list of contributors.\n"
205 "\nSee http://www.ethereal.com/ for more information.",
210 set_fonts(GdkFont *regular, GdkFont *bold)
212 /* Yes, assert. The code that loads the font should check
213 * for NULL and provide its own error message. */
214 g_assert(m_r_font && m_b_font);
218 m_font_height = m_r_font->ascent + m_r_font->descent;
219 m_font_width = gdk_string_width(m_r_font, "0");
223 /* Match selected byte pattern */
225 match_selected_cb(GtkWidget *w, gpointer data)
228 GtkWidget *filter_te;
230 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
232 buf = proto_alloc_dfilter_string(finfo_selected, cfile.pd);
234 /* create a new one and set the display filter entry accordingly */
235 gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
237 /* Run the display filter so it goes in effect. */
238 filter_packets(&cfile, buf);
240 /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
245 /* Run the current display filter on the current packet set, and
248 filter_activate_cb(GtkWidget *w, gpointer data)
250 GtkCombo *filter_cm = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_CM_KEY);
251 GList *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY);
252 GList *li, *nl = NULL;
253 gboolean add_filter = TRUE;
255 char *s = gtk_entry_get_text(GTK_ENTRY(w));
257 /* GtkCombos don't let us get at their list contents easily, so we maintain
258 our own filter list, and feed it to gtk_combo_set_popdown_strings when
259 a new filter is added. */
260 if (filter_packets(&cfile, g_strdup(s))) {
261 li = g_list_first(filter_list);
263 if (li->data && strcmp(s, li->data) == 0)
269 filter_list = g_list_append(filter_list, g_strdup(s));
270 li = g_list_first(filter_list);
272 nl = g_list_append(nl, strdup(li->data));
275 gtk_combo_set_popdown_strings(filter_cm, nl);
276 gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
281 /* redisplay with no display filter */
283 filter_reset_cb(GtkWidget *w, gpointer data)
285 GtkWidget *filter_te = NULL;
287 if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
288 gtk_entry_set_text(GTK_ENTRY(filter_te), "");
291 filter_packets(&cfile, NULL);
294 /* GTKClist compare routine, overrides default to allow numeric comparison */
296 packet_list_compare(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
298 /* Get row text strings */
299 char *text1 = GTK_CELL_TEXT (((GtkCListRow *)ptr1)->cell[clist->sort_column])->text;
300 char *text2 = GTK_CELL_TEXT (((GtkCListRow *)ptr2)->cell[clist->sort_column])->text;
302 /* Attempt to convert to numbers */
303 double num1 = atof(text1);
304 double num2 = atof(text2);
306 gint col_fmt = cfile.cinfo.col_fmt[clist->sort_column];
308 if ((col_fmt == COL_NUMBER) || (col_fmt == COL_REL_TIME) || (col_fmt == COL_DELTA_TIME) ||
309 ((col_fmt == COL_CLS_TIME) && (timestamp_type == RELATIVE)) ||
310 ((col_fmt == COL_CLS_TIME) && (timestamp_type == DELTA)) ||
311 (col_fmt == COL_UNRES_SRC_PORT) || (col_fmt == COL_UNRES_DST_PORT) ||
312 ((num1 != 0) && (num2 != 0) && ((col_fmt == COL_DEF_SRC_PORT) || (col_fmt == COL_RES_SRC_PORT) ||
313 (col_fmt == COL_DEF_DST_PORT) || (col_fmt == COL_RES_DST_PORT))) ||
314 (col_fmt == COL_PACKET_LENGTH)) {
316 /* Compare numeric column */
320 else if (num1 > num2)
328 /* Compare text column */
330 return (text1 != NULL);
335 return strcmp(text1, text2);
339 /* What to do when a column is clicked */
341 packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data)
343 column_arrows *col_arrows = (column_arrows *) data;
346 gtk_clist_freeze(clist);
348 for (i = 0; i < cfile.cinfo.num_cols; i++) {
349 gtk_widget_hide(col_arrows[i].ascend_pm);
350 gtk_widget_hide(col_arrows[i].descend_pm);
353 if (column == clist->sort_column) {
354 if (clist->sort_type == GTK_SORT_ASCENDING) {
355 clist->sort_type = GTK_SORT_DESCENDING;
356 gtk_widget_show(col_arrows[column].descend_pm);
358 clist->sort_type = GTK_SORT_ASCENDING;
359 gtk_widget_show(col_arrows[column].ascend_pm);
363 clist->sort_type = GTK_SORT_ASCENDING;
364 gtk_widget_show(col_arrows[column].ascend_pm);
365 gtk_clist_set_sort_column(clist, column);
367 gtk_clist_thaw(clist);
369 gtk_clist_sort(clist);
374 set_frame_mark(gboolean set, frame_data *frame, gint row) {
380 mark_frame(&cfile, frame);
381 color_t_to_gdkcolor(&fg, &prefs.gui_marked_fg);
382 color_t_to_gdkcolor(&bg, &prefs.gui_marked_bg);
384 unmark_frame(&cfile, frame);
388 file_set_save_marked_sensitive();
389 gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
390 gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
394 packet_list_button_pressed_cb(GtkWidget *w, GdkEvent *event, gpointer data) {
396 GdkEventButton *event_button = (GdkEventButton *)event;
399 if (w == NULL || event == NULL)
402 if (event->type == GDK_BUTTON_PRESS && event_button->button == 2 &&
403 gtk_clist_get_selection_info(GTK_CLIST(w), event_button->x, event_button->y,
405 frame_data *fdata = (frame_data *) gtk_clist_get_row_data(GTK_CLIST(w), row);
406 set_frame_mark(!fdata->flags.marked, fdata, row);
410 void mark_frame_cb(GtkWidget *w, gpointer data) {
411 if (cfile.current_frame) {
412 /* XXX hum, should better have a "cfile->current_row" here ... */
413 set_frame_mark(!cfile.current_frame->flags.marked,
415 gtk_clist_find_row_from_data(GTK_CLIST(packet_list),
416 cfile.current_frame));
420 static void mark_all_frames(gboolean set) {
422 for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
425 gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata));
429 void update_marked_frames(void) {
431 if (cfile.plist == NULL) return;
432 for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
433 if (fdata->flags.marked)
436 gtk_clist_find_row_from_data(GTK_CLIST(packet_list),
441 void mark_all_frames_cb(GtkWidget *w, gpointer data) {
442 mark_all_frames(TRUE);
445 void unmark_all_frames_cb(GtkWidget *w, gpointer data) {
446 mark_all_frames(FALSE);
449 /* What to do when a list item is selected/unselected */
451 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
453 /* Remove the hex display tabbed pages */
454 while( (gtk_notebook_get_nth_page( GTK_NOTEBOOK(byte_nb_ptr), 0)))
455 gtk_notebook_remove_page( GTK_NOTEBOOK(byte_nb_ptr), 0);
457 select_packet(&cfile, row);
462 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
464 unselect_packet(&cfile);
469 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
472 gchar *help_str = NULL;
473 gboolean has_blurb = FALSE;
474 guint length = 0, byte_len;
475 GtkWidget *byte_view;
479 finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
482 if (finfo->ds_name != NULL)
483 set_notebook_page( byte_nb_ptr, find_notebook_page( byte_nb_ptr, finfo->ds_name));
485 byte_view = gtk_object_get_data(GTK_OBJECT(byte_nb_ptr), E_BYTE_VIEW_TEXT_INFO_KEY);
486 byte_data = gtk_object_get_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_DATA_PTR_KEY);
487 byte_len = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_DATA_LEN_KEY));
491 finfo_selected = finfo;
492 set_menus_for_selected_tree_row(TRUE);
495 if (finfo->hfinfo->blurb != NULL &&
496 finfo->hfinfo->blurb[0] != '\0') {
498 length = strlen(finfo->hfinfo->blurb);
500 length = strlen(finfo->hfinfo->name);
502 statusbar_pop_field_msg(); /* get rid of current help msg */
504 length += strlen(finfo->hfinfo->abbrev) + 10;
505 help_str = g_malloc(sizeof(gchar) * length);
506 sprintf(help_str, "%s (%s)",
507 (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name,
508 finfo->hfinfo->abbrev);
509 statusbar_push_field_msg(help_str);
513 * Don't show anything if the field name is zero-length;
514 * the pseudo-field for "proto_tree_add_text()" is such
515 * a field, and we don't want "Text (text)" showing up
516 * on the status line if you've selected such a field.
518 * XXX - there are zero-length fields for which we *do*
519 * want to show the field name.
521 * XXX - perhaps the name and abbrev field should be null
522 * pointers rather than null strings for that pseudo-field,
523 * but we'd have to add checks for null pointers in some
524 * places if we did that.
526 * Or perhaps protocol tree items added with
527 * "proto_tree_add_text()" should have -1 as the field index,
528 * with no pseudo-field being used, but that might also
529 * require special checks for -1 to be added.
531 statusbar_push_field_msg("");
535 packet_hex_print(GTK_TEXT(byte_view), byte_data, cfile.current_frame,
540 tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
542 GtkWidget *byte_view;
547 * Which byte view is displaying the current protocol tree
550 len = get_byte_view_and_data( byte_nb_ptr, &byte_view, &data);
555 packet_hex_print(GTK_TEXT(byte_view), data, cfile.current_frame,
559 void collapse_all_cb(GtkWidget *widget, gpointer data) {
561 collapse_all_tree(cfile.edt->tree, tree_view);
564 void expand_all_cb(GtkWidget *widget, gpointer data) {
566 expand_all_tree(cfile.edt->tree, tree_view);
569 void resolve_name_cb(GtkWidget *widget, gpointer data) {
570 if (cfile.edt->tree) {
571 gint tmp = prefs.name_resolve;
572 prefs.name_resolve = PREFS_RESOLV_ALL;
573 gtk_clist_clear ( GTK_CLIST(tree_view) );
574 proto_tree_draw(cfile.edt->tree, tree_view);
575 prefs.name_resolve = tmp;
579 /* Set the scrollbar placement of a scrolled window based upon pos value:
580 0 = left, 1 = right */
582 set_scrollbar_placement_scrollw(GtkWidget *scrollw, int pos) /* 0=left, 1=right */
585 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
586 GTK_CORNER_TOP_LEFT);
588 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
589 GTK_CORNER_TOP_RIGHT);
593 /* List of all scrolled windows, so we can globally set the scrollbar
594 placement of them. */
595 static GList *scrolled_windows;
597 /* Add a scrolled window to the list of scrolled windows. */
598 static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
601 remember_scrolled_window(GtkWidget *scrollw)
603 scrolled_windows = g_list_append(scrolled_windows, scrollw);
605 /* Catch the "destroy" event on the widget, so that we remove it from
606 the list when it's destroyed. */
607 gtk_signal_connect(GTK_OBJECT(scrollw), "destroy",
608 GTK_SIGNAL_FUNC(forget_scrolled_window), NULL);
611 /* Remove a scrolled window from the list of scrolled windows. */
613 forget_scrolled_window(GtkWidget *scrollw, gpointer data)
615 scrolled_windows = g_list_remove(scrolled_windows, scrollw);
619 set_scrollbar_placement_cb(gpointer data, gpointer user_data)
621 set_scrollbar_placement_scrollw((GtkWidget *)data,
625 /* Set the scrollbar placement of all scrolled windows based on pos value:
626 0 = left, 1 = right */
628 set_scrollbar_placement_all(int pos)
630 g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, &pos);
633 /* Set the selection mode of the packet list window. */
635 set_plist_sel_browse(gboolean val)
640 (GTK_CLIST(packet_list)->selection_mode == GTK_SELECTION_SINGLE);
642 if (val == old_val) {
644 * The mode isn't changing, so don't do anything.
645 * In particular, don't gratuitiously unselect the
648 * XXX - why do we have to unselect the current packet
649 * ourselves? The documentation for the GtkCList at
651 * http://developer.gnome.org/doc/API/gtk/gtkclist.html
653 * says "Note that setting the widget's selection mode to
654 * one of GTK_SELECTION_BROWSE or GTK_SELECTION_SINGLE will
655 * cause all the items in the GtkCList to become deselected."
661 unselect_packet(&cfile);
663 /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
664 * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
666 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_SINGLE);
669 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_BROWSE);
673 /* Set the font of the packet list window. */
675 set_plist_font(GdkFont *font)
680 style = gtk_style_new();
681 gdk_font_unref(style->font);
685 gtk_widget_set_style(packet_list, style);
687 /* Compute static column sizes to use during a "-S" capture, so that
688 the columns don't resize during a live capture. */
689 for (i = 0; i < cfile.cinfo.num_cols; i++) {
690 cfile.cinfo.col_width[i] = gdk_string_width(font,
691 get_column_longest_string(get_column_format(i)));
696 * Push a message referring to file access onto the statusbar.
699 statusbar_push_file_msg(gchar *msg)
701 gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg);
705 * Pop a message referring to file access off the statusbar.
708 statusbar_pop_file_msg(void)
710 gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
714 * XXX - do we need multiple statusbar contexts?
718 * Push a message referring to the currently-selected field onto the statusbar.
721 statusbar_push_field_msg(gchar *msg)
723 gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg);
727 * Pop a message referring to the currently-selected field off the statusbar.
730 statusbar_pop_field_msg(void)
732 gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
736 main_window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
738 file_quit_cmd_cb(widget, data);
740 /* Say that the window should be deleted. */
745 file_quit_cmd_cb (GtkWidget *widget, gpointer data)
747 /* XXX - should we check whether the capture file is an
748 unsaved temporary file for a live capture and, if so,
749 pop up a "do you want to exit without saving the capture
750 file?" dialog, and then just return, leaving said dialog
751 box to forcibly quit if the user clicks "OK"?
753 If so, note that this should be done in a subroutine that
754 returns TRUE if we do so, and FALSE otherwise, and that
755 "main_window_delete_event_cb()" should return its
758 /* Are we in the middle of reading a capture? */
759 if (cfile.state == FILE_READ_IN_PROGRESS) {
760 /* Yes, so we can't just close the file and quit, as
761 that may yank the rug out from under the read in
762 progress; instead, just set the state to
763 "FILE_READ_ABORTED" and return - the code doing the read
764 will check for that and, if it sees that, will clean
766 cfile.state = FILE_READ_ABORTED;
768 /* Close any capture file we have open; on some OSes, you
769 can't unlink a temporary capture file if you have it
771 "close_cap_file()" will unlink it after closing it if
772 it's a temporary file.
774 We do this here, rather than after the main loop returns,
775 as, after the main loop returns, the main window may have
776 been destroyed (if this is called due to a "destroy"
777 even on the main window rather than due to the user
778 selecting a menu item), and there may be a crash
779 or other problem when "close_cap_file()" tries to
780 clean up stuff in the main window.
782 XXX - is there a better place to put this?
783 Or should we have a routine that *just* closes the
784 capture file, and doesn't do anything with the UI,
785 which we'd call here, and another routine that
786 calls that routine and also cleans up the UI, which
787 we'd call elsewhere? */
788 close_cap_file(&cfile);
790 /* Exit by leaving the main loop, so that any quit functions
791 we registered get called. */
799 fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled %s\n",
802 fprintf(stderr, "%s [ -vh ] [ -klpQS ] [ -a <capture autostop condition> ] ...\n",
804 fprintf(stderr, "\t[ -b <number of ringbuffer files> ] [ -B <byte view height> ]\n");
805 fprintf(stderr, "\t[ -c <count> ] [ -f <capture filter> ] [ -i <interface> ]\n");
806 fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -N <resolving> ]\n");
807 fprintf(stderr, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
808 fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
809 fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
810 fprintf(stderr, "\t[ -w <savefile> ] [ <infile> ]\n");
812 fprintf(stderr, "%s [ -vh ] [ -B <byte view height> ] [ -m <medium font> ]\n",
814 fprintf(stderr, "\t[ -n ] [ -N <resolving> ]\n");
815 fprintf(stderr, "\t[ -o <preference setting> ... [ -P <packet list height> ]\n");
816 fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -t <time stamp format> ]\n");
817 fprintf(stderr, "\t[ -T <tree view height> ] [ <infile> ]\n");
828 printf("%s %s, %s\n", PACKAGE, VERSION, comp_info_str->str);
832 get_positive_int(const char *string, const char *name)
837 number = strtol(string, &p, 10);
838 if (p == string || *p != '\0') {
839 fprintf(stderr, "ethereal: The specified %s \"%s\" is not a decimal number\n",
844 fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
848 if (number > INT_MAX) {
849 fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
850 name, string, INT_MAX);
857 * Given a string of the form "<autostop criterion>:<value>", as might appear
858 * as an argument to a "-a" option, parse it and set the criterion in
859 * question. Return an indication of whether it succeeded or failed
863 set_autostop_criterion(const char *autostoparg)
867 colonp = strchr(autostoparg, ':');
875 * Skip over any white space (there probably won't be any, but
876 * as we allow it in the preferences file, we might as well
883 * Put the colon back, so if our caller uses, in an
884 * error message, the string they passed us, the message
890 if (strcmp(autostoparg,"duration") == 0) {
891 cfile.autostop_duration = get_positive_int(p,"autostop duration");
892 } else if (strcmp(autostoparg,"filesize") == 0) {
893 cfile.autostop_filesize = get_positive_int(p,"autostop filesize");
897 *colonp = ':'; /* put the colon back */
901 /* And now our feature presentation... [ fade to music ] */
903 main(int argc, char *argv[])
912 gboolean arg_error = FALSE;
914 #ifdef HAVE_PCAP_VERSION
915 extern char pcap_version[];
916 #endif /* HAVE_PCAP_VERSION */
917 #endif /* HAVE_LIBPCAP */
923 char *gpf_path, *cf_path, *df_path;
925 int gpf_open_errno, pf_open_errno, cf_open_errno, df_open_errno;
928 gboolean start_capture = FALSE;
929 gchar *save_file = NULL;
931 gchar err_str[PCAP_ERRBUF_SIZE];
932 gboolean stats_known;
933 struct pcap_stat stats;
935 gboolean capture_option_specified = FALSE;
937 gint pl_size = 280, tv_size = 95, bv_size = 75;
938 gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
939 dfilter_t *rfcode = NULL;
940 gboolean rfilter_parse_failed = FALSE;
943 char *bold_font_name;
945 ethereal_path = argv[0];
948 /* Arrange that if we have no console window, and a GLib message logging
949 routine is called to log a message, we pop up a console window.
951 We do that by inserting our own handler for all messages logged
952 to the default domain; that handler pops up a console if necessary,
953 and then calls the default handler. */
954 g_log_set_handler(NULL,
956 G_LOG_LEVEL_CRITICAL|
961 G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION,
962 console_log_handler, NULL);
966 command_name = get_basename(ethereal_path);
967 /* Set "capture_child" to indicate whether this is going to be a child
968 process for a "-S" capture. */
969 capture_child = (strcmp(command_name, CHILD_NAME) == 0);
972 /* Register all dissectors; we must do this before checking for the
973 "-G" flag, as the "-G" flag dumps a list of fields registered
974 by the dissectors, and we must do it before we read the preferences,
975 in case any dissectors register preferences. */
976 epan_init(PLUGIN_DIR,register_all_protocols,register_all_protocol_handoffs);
978 /* Now register the preferences for any non-dissector modules.
979 We must do that before we read the preferences as well. */
980 prefs_register_modules();
982 /* If invoked with the "-G" flag, we dump out a glossary of
983 display filter symbols.
985 We must do this before calling "gtk_init()", because "gtk_init()"
986 tries to open an X display, and we don't want to have to do any X
987 stuff just to do a build.
989 Given that we call "gtk_init()" before doing the regular argument
990 list processing, so that it can handle X and GTK+ arguments and
991 remove them from the list at which we look, this means we must do
992 this before doing the regular argument list processing, as well.
996 you must give the "-G" flag as the first flag on the command line;
998 you must give it as "-G", nothing more, nothing less;
1000 any arguments after the "-G" flag will not be used. */
1001 if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
1002 proto_registrar_dump();
1006 /* Set the current locale according to the program environment.
1007 * We haven't localized anything, but some GTK widgets are localized
1008 * (the file selection dialogue, for example).
1009 * This also sets the C-language locale to the native environment. */
1012 /* Let GTK get its args */
1013 gtk_init (&argc, &argv);
1015 /* Read the preference files. */
1016 prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
1019 /* If this is a capture child process, it should pay no attention
1020 to the "prefs.capture_prom_mode" setting in the preferences file;
1021 it should do what the parent process tells it to do, and if
1022 the parent process wants it not to run in promiscuous mode, it'll
1023 tell it so with a "-p" flag. */
1025 prefs->capture_prom_mode = TRUE;
1028 /* Read the capture filter file. */
1029 read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
1031 /* Read the display filter file. */
1032 read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
1034 /* Initialize the capture file struct */
1036 cfile.plist_end = NULL;
1038 cfile.filename = NULL;
1039 cfile.user_saved = FALSE;
1040 cfile.is_tempfile = FALSE;
1041 cfile.rfcode = NULL;
1042 cfile.dfilter = NULL;
1043 cfile.dfcode = NULL;
1045 cfile.cfilter = g_strdup(EMPTY_FILTER);
1048 cfile.save_file = NULL;
1049 cfile.save_file_fd = -1;
1050 cfile.snap = WTAP_MAX_PACKET_SIZE;
1053 cfile.autostop_duration = 0;
1054 cfile.autostop_filesize = 0;
1055 cfile.ringbuffer_on = FALSE;
1056 cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
1058 col_init(&cfile.cinfo, prefs->num_cols);
1060 /* Assemble the compile-time options */
1061 comp_info_str = g_string_new("");
1063 g_string_append(comp_info_str, "with ");
1064 g_string_sprintfa(comp_info_str,
1065 #ifdef GTK_MAJOR_VERSION
1066 "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
1069 "GTK+ (version unknown)");
1072 g_string_append(comp_info_str, ", with ");
1073 g_string_sprintfa(comp_info_str,
1074 #ifdef GLIB_MAJOR_VERSION
1075 "GLib %d.%d.%d", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION,
1076 GLIB_MICRO_VERSION);
1078 "GLib (version unknown)");
1082 g_string_append(comp_info_str, ", with libpcap ");
1083 #ifdef HAVE_PCAP_VERSION
1084 g_string_append(comp_info_str, pcap_version);
1085 #else /* HAVE_PCAP_VERSION */
1086 g_string_append(comp_info_str, "(version unknown)");
1087 #endif /* HAVE_PCAP_VERSION */
1088 #else /* HAVE_LIBPCAP */
1089 g_string_append(comp_info_str, ", without libpcap");
1090 #endif /* HAVE_LIBPCAP */
1093 g_string_append(comp_info_str, ", with libz ");
1095 g_string_append(comp_info_str, ZLIB_VERSION);
1096 #else /* ZLIB_VERSION */
1097 g_string_append(comp_info_str, "(version unknown)");
1098 #endif /* ZLIB_VERSION */
1099 #else /* HAVE_LIBZ */
1100 g_string_append(comp_info_str, ", without libz");
1101 #endif /* HAVE_LIBZ */
1103 /* Oh, this is pretty */
1104 #if defined(HAVE_UCD_SNMP_SNMP_H)
1105 g_string_append(comp_info_str, ", with UCD SNMP ");
1106 #ifdef HAVE_UCD_SNMP_VERSION_H
1107 g_string_append(comp_info_str, VersionInfo);
1108 #else /* HAVE_UCD_SNMP_VERSION_H */
1109 g_string_append(comp_info_str, "(version unknown)");
1110 #endif /* HAVE_UCD_SNMP_VERSION_H */
1111 #elif defined(HAVE_SNMP_SNMP_H)
1112 g_string_append(comp_info_str, ", with CMU SNMP ");
1113 #ifdef HAVE_SNMP_VERSION_H
1114 g_string_append(comp_info_str, snmp_Version());
1115 #else /* HAVE_SNMP_VERSION_H */
1116 g_string_append(comp_info_str, "(version unknown)");
1117 #endif /* HAVE_SNMP_VERSION_H */
1119 g_string_append(comp_info_str, ", without SNMP");
1122 /* Now get our args */
1123 while ((opt = getopt(argc, argv, "a:b:B:c:f:hi:klm:nN:o:pP:Qr:R:Ss:t:T:w:W:vZ:")) != EOF) {
1125 case 'a': /* autostop criteria */
1127 if (set_autostop_criterion(optarg) == FALSE) {
1128 fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
1132 capture_option_specified = TRUE;
1136 case 'b': /* Ringbuffer option */
1138 cfile.ringbuffer_on = TRUE;
1139 /* get optional ringbuffer number of files parameter */
1140 if (optarg[0] != '-') {
1141 cfile.ringbuffer_num_files = get_positive_int(optarg, "ringbuffer number of files");
1143 cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
1147 capture_option_specified = TRUE;
1151 case 'B': /* Byte view pane height */
1152 bv_size = get_positive_int(optarg, "byte view pane height");
1154 case 'c': /* Capture xxx packets */
1156 cfile.count = get_positive_int(optarg, "packet count");
1158 capture_option_specified = TRUE;
1165 g_free(cfile.cfilter);
1166 cfile.cfilter = g_strdup(optarg);
1168 capture_option_specified = TRUE;
1172 case 'h': /* Print help and exit */
1176 case 'i': /* Use interface xxx */
1178 cfile.iface = g_strdup(optarg);
1180 capture_option_specified = TRUE;
1184 case 'k': /* Start capture immediately */
1186 start_capture = TRUE;
1188 capture_option_specified = TRUE;
1192 case 'l': /* Automatic scrolling in live capture mode */
1193 prefs->capture_auto_scroll = TRUE;
1195 case 'm': /* Fixed-width font for the display */
1196 if (prefs->gui_font_name != NULL)
1197 g_free(prefs->gui_font_name);
1198 prefs->gui_font_name = g_strdup(optarg);
1200 case 'n': /* No name resolution */
1201 prefs->name_resolve = PREFS_RESOLV_NONE;
1203 case 'N': /* Select what types of addresses/port #s to resolve */
1204 if (prefs->name_resolve == PREFS_RESOLV_ALL)
1205 prefs->name_resolve = PREFS_RESOLV_NONE;
1206 badopt = string_to_name_resolve(optarg, &prefs->name_resolve);
1207 if (badopt != '\0') {
1208 fprintf(stderr, "ethereal: -N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'\n",
1213 case 'o': /* Override preference from command line */
1214 switch (prefs_set_pref(optarg)) {
1216 case PREFS_SET_SYNTAX_ERR:
1217 fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
1221 case PREFS_SET_NO_SUCH_PREF:
1222 case PREFS_SET_OBSOLETE:
1223 fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
1229 case 'p': /* Don't capture in promiscuous mode */
1231 prefs->capture_prom_mode = FALSE;
1233 capture_option_specified = TRUE;
1237 case 'P': /* Packet list pane height */
1238 pl_size = get_positive_int(optarg, "packet list pane height");
1240 case 'Q': /* Quit after capture (just capture to file) */
1243 start_capture = TRUE; /*** -Q implies -k !! ***/
1245 capture_option_specified = TRUE;
1249 case 'r': /* Read capture file xxx */
1250 /* We may set "last_open_dir" to "cf_name", and if we change
1251 "last_open_dir" later, we free the old value, so we have to
1252 set "cf_name" to something that's been allocated. */
1253 cf_name = g_strdup(optarg);
1255 case 'R': /* Read file filter */
1258 case 's': /* Set the snapshot (capture) length */
1260 cfile.snap = get_positive_int(optarg, "snapshot length");
1262 capture_option_specified = TRUE;
1266 case 'S': /* "Sync" mode: used for following file ala tail -f */
1268 prefs->capture_real_time = TRUE;
1270 capture_option_specified = TRUE;
1274 case 't': /* Time stamp type */
1275 if (strcmp(optarg, "r") == 0)
1276 timestamp_type = RELATIVE;
1277 else if (strcmp(optarg, "a") == 0)
1278 timestamp_type = ABSOLUTE;
1279 else if (strcmp(optarg, "ad") == 0)
1280 timestamp_type = ABSOLUTE_WITH_DATE;
1281 else if (strcmp(optarg, "d") == 0)
1282 timestamp_type = DELTA;
1284 fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
1286 fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
1287 fprintf(stderr, "\"ad\" for absolute with date, or \"d\" for delta.\n");
1291 case 'T': /* Tree view pane height */
1292 tv_size = get_positive_int(optarg, "tree view pane height");
1294 case 'v': /* Show version and exit */
1297 if (console_was_created)
1302 case 'w': /* Write to capture file xxx */
1304 save_file = g_strdup(optarg);
1306 capture_option_specified = TRUE;
1310 case 'W': /* Write to capture file FD xxx */
1312 cfile.save_file_fd = atoi(optarg);
1314 capture_option_specified = TRUE;
1320 case 'Z': /* Write to pipe FD XXX */
1322 /* associate stdout with pipe */
1324 if (dup2(i, 1) < 0) {
1325 fprintf(stderr, "Unable to dup pipe handle\n");
1329 capture_option_specified = TRUE;
1331 #endif /* HAVE_LIBPCAP */
1336 case '?': /* Bad flag - print usage message */
1344 if (cf_name != NULL) {
1346 * Input file name specified with "-r" *and* specified as a regular
1347 * command-line argument.
1352 * Input file name not specified with "-r", and a command-line argument
1353 * was specified; treat it as the input file name.
1355 * Yes, this is different from tethereal, where non-flag command-line
1356 * arguments are a filter, but this works better on GUI desktops
1357 * where a command can be specified to be run to open a particular
1358 * file - yes, you could have "-r" as the last part of the command,
1359 * but that's a bit ugly.
1361 cf_name = g_strdup(argv[0]);
1369 * Extra command line arguments were specified; complain.
1375 /* Load wpcap if possible */
1378 /* Start windows sockets */
1379 WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
1382 /* Notify all registered modules that have had any of their preferences
1383 changed either from one of the preferences file or from the command
1384 line that its preferences have changed. */
1387 #ifndef HAVE_LIBPCAP
1388 if (capture_option_specified)
1389 fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
1394 if (start_capture) {
1395 /* We're supposed to do a live capture; did the user specify an interface
1397 if (cfile.iface == NULL) {
1398 /* No - pick the first one from the list of interfaces. */
1399 if_list = get_interface_list(&err, err_str);
1400 if (if_list == NULL) {
1403 case CANT_GET_INTERFACE_LIST:
1404 fprintf(stderr, "ethereal: Can't get list of interfaces: %s\n",
1408 case NO_INTERFACES_FOUND:
1409 fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
1414 cfile.iface = g_strdup(if_list->data); /* first interface */
1415 free_interface_list(if_list);
1418 if (capture_child) {
1419 if (cfile.save_file_fd == -1) {
1420 /* XXX - send this to the standard output as something our parent
1421 should put in an error message box? */
1422 fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
1428 /* Build the column format array */
1429 for (i = 0; i < cfile.cinfo.num_cols; i++) {
1430 cfile.cinfo.col_fmt[i] = get_column_format(i);
1431 cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
1432 cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
1434 get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
1435 cfile.cinfo.col_data[i] = NULL;
1436 if (cfile.cinfo.col_fmt[i] == COL_INFO)
1437 cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
1439 cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
1443 cfile.snap = WTAP_MAX_PACKET_SIZE;
1444 else if (cfile.snap < MIN_PACKET_SIZE)
1445 cfile.snap = MIN_PACKET_SIZE;
1447 if (cfile.ringbuffer_on == TRUE) {
1448 /* Ringbuffer works just under certain conditions:
1449 a) prefs->capture_real_time and cfile.ringbuffer_on are mutially
1450 exclusive. prefs->capture_real_time takes precedence.
1451 b) Ringbuffer does not work with temporary files
1452 c) It makes no sense to enable the ringbuffer if the maximum
1453 file size is set to infinite */
1454 if (prefs->capture_real_time == TRUE ||
1455 cfile.save_file == NULL ||
1456 cfile.autostop_filesize == 0) {
1457 /* turn ringbuffer off */
1458 cfile.ringbuffer_on = FALSE;
1462 /* Check the value range of the ringbuffer_num_files parameter */
1463 if (cfile.ringbuffer_num_files < RINGBUFFER_MIN_NUM_FILES)
1464 cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
1465 else if (cfile.ringbuffer_num_files > RINGBUFFER_MAX_NUM_FILES)
1466 cfile.ringbuffer_num_files = RINGBUFFER_MAX_NUM_FILES;
1468 rc_file = get_persconffile_path(RC_FILE, FALSE);
1469 gtk_rc_parse(rc_file);
1471 /* Try to load the regular and boldface fixed-width fonts */
1472 bold_font_name = boldify(prefs->gui_font_name);
1473 m_r_font = gdk_font_load(prefs->gui_font_name);
1474 m_b_font = gdk_font_load(bold_font_name);
1475 if (m_r_font == NULL || m_b_font == NULL) {
1476 /* XXX - pop this up as a dialog box? no */
1477 if (m_r_font == NULL) {
1481 fprintf(stderr, "ethereal: Warning: font %s not found - defaulting to 6x13 and 6x13bold\n",
1482 prefs->gui_font_name);
1484 gdk_font_unref(m_r_font);
1486 if (m_b_font == NULL) {
1490 fprintf(stderr, "ethereal: Warning: font %s not found - defaulting to 6x13 and 6x13bold\n",
1493 gdk_font_unref(m_b_font);
1495 g_free(bold_font_name);
1496 if ((m_r_font = gdk_font_load("6x13")) == NULL) {
1497 fprintf(stderr, "ethereal: Error: font 6x13 not found\n");
1500 if ((m_b_font = gdk_font_load("6x13bold")) == NULL) {
1501 fprintf(stderr, "ethereal: Error: font 6x13bold not found\n");
1504 g_free(prefs->gui_font_name);
1505 prefs->gui_font_name = g_strdup("6x13");
1508 /* Call this for the side-effects that set_fonts() produces */
1509 set_fonts(m_r_font, m_b_font);
1513 /* Is this a "child" ethereal, which is only supposed to pop up a
1514 capture box to let us stop the capture, and run a capture
1515 to a file that our parent will read? */
1516 if (!capture_child) {
1518 /* No. Pop up the main window, and read in a capture file if
1521 create_main_window(pl_size, tv_size, bv_size, prefs);
1522 set_menus_for_capture_file(FALSE);
1524 cfile.colors = colfilter_new();
1526 /* If we were given the name of a capture file, read it in now;
1527 we defer it until now, so that, if we can't open it, and pop
1528 up an alert box, the alert box is more likely to come up on
1529 top of the main window - but before the preference-file-error
1530 alert box, so, if we get one of those, it's more likely to come
1533 if (rfilter != NULL) {
1534 if (!dfilter_compile(rfilter, &rfcode)) {
1535 simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
1536 rfilter_parse_failed = TRUE;
1539 if (!rfilter_parse_failed) {
1540 if ((err = open_cap_file(cf_name, FALSE, &cfile)) == 0) {
1541 /* "open_cap_file()" succeeded, so it closed the previous
1542 capture file, and thus destroyed any previous read filter
1543 attached to "cf". */
1544 cfile.rfcode = rfcode;
1545 switch (read_cap_file(&cfile, &err)) {
1549 /* Just because we got an error, that doesn't mean we were unable
1550 to read any of the file; we handle what we could get from the
1559 /* Save the name of the containing directory specified in the
1560 path name, if any; we can write over cf_name, which is a
1561 good thing, given that "get_dirname()" does write over its
1563 s = get_dirname(cf_name);
1564 set_last_open_dir(s);
1567 dfilter_free(rfcode);
1568 cfile.rfcode = NULL;
1576 /* If the global preferences file exists but we failed to open it,
1577 pop up an alert box; we defer that until now, so that the alert
1578 box is more likely to come up on top of the main window. */
1579 if (gpf_path != NULL) {
1580 simple_dialog(ESD_TYPE_WARN, NULL,
1581 "Could not open global preferences file\n\"%s\": %s.", gpf_path,
1582 strerror(gpf_open_errno));
1585 /* If the user's preferences file exists but we failed to open it,
1586 pop up an alert box; we defer that until now, so that the alert
1587 box is more likely to come up on top of the main window. */
1588 if (pf_path != NULL) {
1589 simple_dialog(ESD_TYPE_WARN, NULL,
1590 "Could not open your preferences file\n\"%s\": %s.", pf_path,
1591 strerror(pf_open_errno));
1594 /* If the user's capture filter file exists but we failed to open it,
1595 pop up an alert box; we defer that until now, so that the alert
1596 box is more likely to come up on top of the main window. */
1597 if (cf_path != NULL) {
1598 simple_dialog(ESD_TYPE_WARN, NULL,
1599 "Could not open your capture filter file\n\"%s\": %s.", cf_path,
1600 strerror(cf_open_errno));
1604 /* If the user's display filter file exists but we failed to open it,
1605 pop up an alert box; we defer that until now, so that the alert
1606 box is more likely to come up on top of the main window. */
1607 if (df_path != NULL) {
1608 simple_dialog(ESD_TYPE_WARN, NULL,
1609 "Could not open your display filter file\n\"%s\": %s.", df_path,
1610 strerror(df_open_errno));
1615 if (capture_child) {
1616 /* This is the child process for a sync mode or fork mode capture,
1617 so just do the low-level work of a capture - don't create
1618 a temporary file and fork off *another* child process (so don't
1619 call "do_capture()"). */
1621 /* XXX - hand these stats to the parent process */
1622 capture(&stats_known, &stats);
1624 /* The capture is done; there's nothing more for us to do. */
1627 if (start_capture) {
1628 /* "-k" was specified; start a capture. */
1629 do_capture(save_file);
1632 set_menus_for_capture_in_progress(FALSE);
1636 set_menus_for_capture_in_progress(FALSE);
1645 /* Shutdown windows sockets */
1648 /* For some unknown reason, the "atexit()" call in "create_console()"
1649 doesn't arrange that "destroy_console()" be called when we exit,
1650 so we call it here if a console was created. */
1651 if (console_was_created)
1657 /* This isn't reached, but we need it to keep GCC from complaining
1658 that "main()" returns without returning a value - it knows that
1659 "exit()" never returns, but it doesn't know that "gtk_exit()"
1660 doesn't, as GTK+ doesn't declare it with the attribute
1662 return 0; /* not reached */
1667 /* We build this as a GUI subsystem application on Win32, so
1668 "WinMain()", not "main()", gets called.
1670 Hack shamelessly stolen from the Win32 port of the GIMP. */
1672 #define _stdcall __attribute__((stdcall))
1676 WinMain (struct HINSTANCE__ *hInstance,
1677 struct HINSTANCE__ *hPrevInstance,
1681 has_no_console = TRUE;
1682 return main (__argc, __argv);
1686 * If this application has no console window to which its standard output
1687 * would go, create one.
1690 create_console(void)
1692 if (has_no_console) {
1693 /* We have no console to which to print the version string, so
1694 create one and make it the standard input, output, and error. */
1695 if (!AllocConsole())
1696 return; /* couldn't create console */
1697 freopen("CONIN$", "r", stdin);
1698 freopen("CONOUT$", "w", stdout);
1699 freopen("CONOUT$", "w", stderr);
1701 /* Well, we have a console now. */
1702 has_no_console = FALSE;
1703 console_was_created = TRUE;
1705 /* Now register "destroy_console()" as a routine to be called just
1706 before the application exits, so that we can destroy the console
1707 after the user has typed a key (so that the console doesn't just
1708 disappear out from under them, giving the user no chance to see
1709 the message(s) we put in there). */
1710 atexit(destroy_console);
1715 destroy_console(void)
1717 printf("\n\nPress any key to exit\n");
1722 /* This routine should not be necessary, at least as I read the GLib
1723 source code, as it looks as if GLib is, on Win32, *supposed* to
1724 create a console window into which to display its output.
1726 That doesn't happen, however. I suspect there's something completely
1727 broken about that code in GLib-for-Win32, and that it may be related
1728 to the breakage that forces us to just call "printf()" on the message
1729 rather than passing the message on to "g_log_default_handler()"
1730 (which is the routine that does the aforementioned non-functional
1731 console window creation). */
1733 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
1734 const char *message, gpointer user_data)
1737 if (console_was_created) {
1738 /* For some unknown reason, the above doesn't appear to actually cause
1739 anything to be sent to the standard output, so we'll just splat the
1740 message out directly, just to make sure it gets out. */
1741 printf("%s\n", message);
1743 g_log_default_handler(log_domain, log_level, message, user_data);
1747 /* Given a font name, construct the name of the next heavier version of
1750 #define XLFD_WEIGHT 3 /* index of the "weight" field */
1752 /* Map from a given weight to the appropriate weight for the "bold"
1754 XXX - the XLFD says these strings shouldn't be used for font matching;
1755 can we get the weight, as a number, from GDK, and ask GDK to find us
1756 a font just like the given font, but with the appropriate higher
1758 static const struct {
1762 { "ultralight", "light" },
1763 { "extralight", "semilight" },
1764 { "light", "medium" },
1765 { "semilight", "semibold" },
1766 { "medium", "bold" },
1767 { "normal", "bold" },
1768 { "semibold", "extrabold" },
1769 { "bold", "ultrabold" }
1771 #define N_WEIGHTS (sizeof weight_map / sizeof weight_map[0])
1774 boldify(const char *font_name)
1776 char *bold_font_name;
1777 gchar **xlfd_tokens;
1780 /* Is this an XLFD font? If it begins with "-", yes, otherwise no. */
1781 if (font_name[0] == '-') {
1782 xlfd_tokens = g_strsplit(font_name, "-", XLFD_WEIGHT+1);
1783 for (i = 0; i < N_WEIGHTS; i++) {
1784 if (strcmp(xlfd_tokens[XLFD_WEIGHT],
1785 weight_map[i].light) == 0) {
1786 g_free(xlfd_tokens[XLFD_WEIGHT]);
1787 xlfd_tokens[XLFD_WEIGHT] =
1788 g_strdup(weight_map[i].heavier);
1792 bold_font_name = g_strjoinv("-", xlfd_tokens);
1793 g_strfreev(xlfd_tokens);
1795 /* Append "bold" to the name of the font. */
1796 bold_font_name = g_strconcat(font_name, "bold", NULL);
1798 return bold_font_name;
1803 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
1805 GtkWidget *main_vbox, *menubar, *u_pane, *l_pane,
1806 *stat_hbox, *column_lb,
1807 *filter_bt, *filter_cm, *filter_te,
1809 GList *filter_list = NULL;
1810 GtkAccelGroup *accel;
1811 GtkStyle *win_style;
1812 GdkBitmap *ascend_bm, *descend_bm;
1813 GdkPixmap *ascend_pm, *descend_pm;
1814 column_arrows *col_arrows;
1816 /* Display filter construct dialog has an Apply button, and "OK" not
1817 only sets our text widget, it activates it (i.e., it causes us to
1818 filter the capture). */
1819 static construct_args_t args = {
1820 "Ethereal: Display Filter",
1826 top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1827 gtk_widget_set_name(top_level, "main window");
1828 gtk_signal_connect(GTK_OBJECT(top_level), "delete_event",
1829 GTK_SIGNAL_FUNC(main_window_delete_event_cb), NULL);
1830 gtk_signal_connect (GTK_OBJECT (top_level), "realize",
1831 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
1832 gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
1833 gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
1834 gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
1836 /* Container for menu bar, paned windows and progress/info box */
1837 main_vbox = gtk_vbox_new(FALSE, 1);
1838 gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
1839 gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
1840 gtk_widget_show(main_vbox);
1843 get_main_menu(&menubar, &accel);
1844 gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
1845 gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
1846 gtk_widget_show(menubar);
1848 /* Panes for the packet list, tree, and byte view */
1849 u_pane = gtk_vpaned_new();
1850 gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
1851 l_pane = gtk_vpaned_new();
1852 gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
1853 gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
1854 gtk_widget_show(l_pane);
1855 gtk_paned_add2(GTK_PANED(u_pane), l_pane);
1856 gtk_widget_show(u_pane);
1859 pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
1860 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
1861 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1862 set_scrollbar_placement_scrollw(pkt_scrollw, prefs->gui_scrollbar_on_right);
1863 remember_scrolled_window(pkt_scrollw);
1864 gtk_widget_show(pkt_scrollw);
1865 gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
1867 packet_list = gtk_clist_new(cfile.cinfo.num_cols);
1868 /* Column titles are filled in below */
1869 gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
1871 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * cfile.cinfo.num_cols);
1873 set_plist_sel_browse(prefs->gui_plist_sel_browse);
1874 set_plist_font(m_r_font);
1875 gtk_widget_set_name(packet_list, "packet list");
1876 gtk_signal_connect (GTK_OBJECT (packet_list), "click_column",
1877 GTK_SIGNAL_FUNC(packet_list_click_column_cb), col_arrows);
1878 gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
1879 GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
1880 gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
1881 GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
1882 for (i = 0; i < cfile.cinfo.num_cols; i++) {
1883 if (get_column_resize_type(cfile.cinfo.col_fmt[i]) != RESIZE_MANUAL)
1884 gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
1886 /* Right-justify the packet number column. */
1887 if (cfile.cinfo.col_fmt[i] == COL_NUMBER)
1888 gtk_clist_set_column_justification(GTK_CLIST(packet_list), i,
1891 gtk_widget_set_usize(packet_list, -1, pl_size);
1892 gtk_signal_connect(GTK_OBJECT(packet_list), "button_press_event",
1893 GTK_SIGNAL_FUNC(popup_menu_handler),
1894 gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
1895 gtk_signal_connect(GTK_OBJECT(packet_list), "button_press_event",
1896 GTK_SIGNAL_FUNC(packet_list_button_pressed_cb), NULL);
1897 gtk_clist_set_compare_func(GTK_CLIST(packet_list), packet_list_compare);
1898 gtk_widget_show(packet_list);
1901 item_style = gtk_style_new();
1902 gdk_font_unref(item_style->font);
1903 item_style->font = m_r_font;
1904 create_tree_view(tv_size, prefs, l_pane, &tv_scrollw, &tree_view,
1905 prefs->gui_scrollbar_on_right);
1906 gtk_signal_connect(GTK_OBJECT(tree_view), "tree-select-row",
1907 GTK_SIGNAL_FUNC(tree_view_select_row_cb), NULL);
1908 gtk_signal_connect(GTK_OBJECT(tree_view), "tree-unselect-row",
1909 GTK_SIGNAL_FUNC(tree_view_unselect_row_cb), NULL);
1910 gtk_signal_connect(GTK_OBJECT(tree_view), "button_press_event",
1911 GTK_SIGNAL_FUNC(popup_menu_handler),
1912 gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY));
1913 gtk_widget_show(tree_view);
1916 create_byte_view(bv_size, l_pane, &byte_nb_ptr, &bv_scrollw,
1917 prefs->gui_scrollbar_on_right);
1919 gtk_signal_connect(GTK_OBJECT(byte_nb_ptr), "button_press_event",
1920 GTK_SIGNAL_FUNC(popup_menu_handler),
1921 gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_HEXDUMP_KEY));
1923 /* Filter/info box */
1924 stat_hbox = gtk_hbox_new(FALSE, 1);
1925 gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
1926 gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
1927 gtk_widget_show(stat_hbox);
1929 filter_bt = gtk_button_new_with_label("Filter:");
1930 gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
1931 GTK_SIGNAL_FUNC(display_filter_construct_cb), &args);
1932 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
1933 gtk_widget_show(filter_bt);
1935 filter_cm = gtk_combo_new();
1936 filter_list = g_list_append (filter_list, "");
1937 gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
1938 gtk_combo_disable_activate(GTK_COMBO(filter_cm));
1939 filter_te = GTK_COMBO(filter_cm)->entry;
1940 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
1941 gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
1942 gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
1943 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
1944 gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
1945 GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
1946 gtk_widget_show(filter_cm);
1948 filter_reset = gtk_button_new_with_label("Reset");
1949 gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
1950 gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
1951 GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
1952 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
1953 gtk_widget_show(filter_reset);
1955 /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
1956 * of any widget that ends up calling a callback which needs
1957 * that text entry pointer */
1958 set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
1959 set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
1960 set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
1961 set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
1962 set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
1964 info_bar = gtk_statusbar_new();
1965 main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
1966 file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
1967 help_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "help");
1968 gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
1969 gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
1970 gtk_widget_show(info_bar);
1972 gtk_widget_show(top_level);
1974 /* Fill in column titles. This must be done after the top level window
1976 win_style = gtk_widget_get_style(top_level);
1977 ascend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &ascend_bm,
1978 &win_style->bg[GTK_STATE_NORMAL], (gchar **)clist_ascend_xpm);
1979 descend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &descend_bm,
1980 &win_style->bg[GTK_STATE_NORMAL], (gchar **)clist_descend_xpm);
1981 for (i = 0; i < cfile.cinfo.num_cols; i++) {
1982 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
1983 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
1984 column_lb = gtk_label_new(cfile.cinfo.col_title[i]);
1985 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2,
1986 GTK_SHRINK, GTK_SHRINK, 0, 0);
1987 gtk_widget_show(column_lb);
1988 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
1989 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm,
1990 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1992 gtk_widget_show(col_arrows[i].ascend_pm);
1994 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
1995 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm,
1996 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
1997 gtk_clist_set_column_widget(GTK_CLIST(packet_list), i, col_arrows[i].table);
1998 gtk_widget_show(col_arrows[i].table);
2000 gtk_clist_column_titles_show(GTK_CLIST(packet_list));
2005 set_last_open_dir(char *dirname)
2009 if (last_open_dir) {
2010 g_free(last_open_dir);
2014 len = strlen(dirname);
2015 if (dirname[len-1] != G_DIR_SEPARATOR) {
2016 last_open_dir = g_strconcat(dirname, G_DIR_SEPARATOR_S,
2021 last_open_dir = NULL;