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.
32 * - Multiple window support
33 * - Add cut/copy/paste
34 * - Create header parsing routines
35 * - Make byte view selections more fancy?
56 #include <io.h> /* open/close on win32 */
59 #ifdef NEED_STRERROR_H
67 #ifdef _WIN32 /* Needed for console I/O */
72 #include <epan/epan.h>
73 #include <epan/filesystem.h>
74 #include <epan/epan_dissect.h>
75 #include <epan/timestamp.h>
76 #include <epan/packet.h>
77 #include <epan/plugins.h>
78 #include <epan/dfilter/dfilter.h>
79 #include <epan/strutil.h>
80 #include <epan/addr_resolv.h>
82 /* general (not GTK specific) */
83 #include "svnversion.h"
87 #include "disabled_protos.h"
88 #include <epan/prefs.h>
89 #include "filter_dlg.h"
90 #include "layout_prefs.h"
92 #include "color_filters.h"
94 #include "simple_dialog.h"
96 #include <epan/prefs-int.h>
97 #include "ringbuffer.h"
98 #include "../ui_util.h" /* beware: ui_util.h exists twice! */
101 #include "clopts_common.h"
102 #include "version_info.h"
106 #include "pcap-util.h"
109 #include "capture-wpcap.h"
113 #include "statusbar.h"
114 #include "alert_box.h"
115 #include "dlg_utils.h"
116 #include "gtkglobals.h"
118 #include "ui_util.h" /* beware: ui_util.h exists twice! */
119 #include "compat_macros.h"
124 #include "file_dlg.h"
125 #include <epan/column.h>
126 #include "proto_draw.h"
128 #include "packet_win.h"
130 #include "find_dlg.h"
131 #include "packet_list.h"
133 #include "follow_dlg.h"
134 #include "font_utils.h"
135 #include "about_dlg.h"
136 #include "help_dlg.h"
137 #include "decode_as_dlg.h"
141 * File under personal preferences directory in which GTK settings for
142 * Ethereal are stored.
144 #define RC_FILE "gtkrc"
147 #define DEF_READY_MESSAGE " Ready to load or capture"
149 #define DEF_READY_MESSAGE " Ready to load file"
153 GtkWidget *main_display_filter_widget=NULL;
154 GtkWidget *top_level = NULL, *tree_view, *byte_nb_ptr, *tv_scrollw;
155 static GtkWidget *main_pane_v1, *main_pane_v2, *main_pane_h1, *main_pane_h2;
156 static GtkWidget *main_first_pane, *main_second_pane;
157 static GtkWidget *status_pane;
158 static GtkWidget *menubar, *main_vbox, *main_tb, *pkt_scrollw, *stat_hbox, *filter_tb;
159 static GtkWidget *info_bar;
160 static GtkWidget *packets_bar = NULL;
161 static guint main_ctx, file_ctx, help_ctx;
162 static guint packets_ctx;
163 static gchar *packets_str = NULL;
164 GString *comp_info_str, *runtime_info_str;
165 gchar *ethereal_path = NULL;
168 static gboolean has_console; /* TRUE if app has console */
169 /*static void create_console(void);*/
170 static void destroy_console(void);
171 static void console_log_handler(const char *log_domain,
172 GLogLevelFlags log_level, const char *message, gpointer user_data);
176 static gboolean list_link_layer_types;
179 static void create_main_window(gint, gint, gint, e_prefs*);
180 static void file_quit_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_);
181 static void main_save_window_geometry(GtkWidget *widget);
183 #define E_DFILTER_CM_KEY "display_filter_combo"
184 #define E_DFILTER_FL_KEY "display_filter_list"
188 /* Match selected byte pattern */
190 match_selected_cb_do(gpointer data, int action, gchar *text)
192 GtkWidget *filter_te;
193 char *cur_filter, *new_filter;
198 filter_te = OBJECT_GET_DATA(data, E_DFILTER_TE_KEY);
201 cur_filter = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, -1);
203 switch (action&MATCH_SELECTED_MASK) {
205 case MATCH_SELECTED_REPLACE:
206 new_filter = g_strdup(text);
209 case MATCH_SELECTED_AND:
210 if ((!cur_filter) || (0 == strlen(cur_filter)))
211 new_filter = g_strdup(text);
213 new_filter = g_strconcat("(", cur_filter, ") && (", text, ")", NULL);
216 case MATCH_SELECTED_OR:
217 if ((!cur_filter) || (0 == strlen(cur_filter)))
218 new_filter = g_strdup(text);
220 new_filter = g_strconcat("(", cur_filter, ") || (", text, ")", NULL);
223 case MATCH_SELECTED_NOT:
224 new_filter = g_strconcat("!(", text, ")", NULL);
227 case MATCH_SELECTED_AND_NOT:
228 if ((!cur_filter) || (0 == strlen(cur_filter)))
229 new_filter = g_strconcat("!(", text, ")", NULL);
231 new_filter = g_strconcat("(", cur_filter, ") && !(", text, ")", NULL);
234 case MATCH_SELECTED_OR_NOT:
235 if ((!cur_filter) || (0 == strlen(cur_filter)))
236 new_filter = g_strconcat("!(", text, ")", NULL);
238 new_filter = g_strconcat("(", cur_filter, ") || !(", text, ")", NULL);
242 g_assert_not_reached();
247 /* Free up the copy we got of the old filter text. */
250 /* create a new one and set the display filter entry accordingly */
251 gtk_entry_set_text(GTK_ENTRY(filter_te), new_filter);
253 /* Run the display filter so it goes in effect. */
254 if (action&MATCH_SELECTED_APPLY_NOW)
255 main_filter_packets(&cfile, new_filter, FALSE);
257 /* Free up the new filter text. */
260 /* Free up the generated text we were handed. */
265 match_selected_ptree_cb(GtkWidget *w, gpointer data, MATCH_SELECTED_E action)
267 if (cfile.finfo_selected)
268 match_selected_cb_do((data ? data : w),
270 proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt));
275 get_text_from_packet_list(gpointer data)
277 gint row = GPOINTER_TO_INT(OBJECT_GET_DATA(data, E_MPACKET_LIST_ROW_KEY));
278 gint column = GPOINTER_TO_INT(OBJECT_GET_DATA(data, E_MPACKET_LIST_COL_KEY));
279 frame_data *fdata = (frame_data *)packet_list_get_row_data(row);
287 if (!wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header,
288 cfile.pd, fdata->cap_len, &err, &err_info)) {
289 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
290 cf_read_error_message(err, err_info), cfile.filename);
294 edt = epan_dissect_new(FALSE, FALSE);
295 epan_dissect_run(edt, &cfile.pseudo_header, cfile.pd, fdata,
297 epan_dissect_fill_in_columns(edt);
299 if (strlen(cfile.cinfo.col_expr[column]) != 0 &&
300 strlen(cfile.cinfo.col_expr_val[column]) != 0) {
301 len = strlen(cfile.cinfo.col_expr[column]) +
302 strlen(cfile.cinfo.col_expr_val[column]) + 5;
303 buf = g_malloc0(len);
304 g_snprintf(buf, len, "%s == %s", cfile.cinfo.col_expr[column],
305 cfile.cinfo.col_expr_val[column]);
308 epan_dissect_free(edt);
315 match_selected_plist_cb(GtkWidget *w _U_, gpointer data, MATCH_SELECTED_E action)
317 match_selected_cb_do(data,
319 get_text_from_packet_list(data));
324 /* XXX: use a preference for this setting! */
325 static guint dfilter_combo_max_recent = 10;
327 /* add a display filter to the combo box */
328 /* Note: a new filter string will replace an old identical one */
330 dfilter_combo_add(GtkWidget *filter_cm, char *s) {
332 GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
335 /* GtkCombos don't let us get at their list contents easily, so we maintain
336 our own filter list, and feed it to gtk_combo_set_popdown_strings when
337 a new filter is added. */
338 li = g_list_first(filter_list);
340 /* If the filter is already in the list, remove the old one and
341 * append the new one at the latest position (at g_list_append() below) */
342 if (li->data && strcmp(s, li->data) == 0) {
343 filter_list = g_list_remove(filter_list, li->data);
349 filter_list = g_list_append(filter_list, s);
350 OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
351 gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
352 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(filter_cm)->entry), g_list_last(filter_list)->data);
358 /* write all non empty display filters (until maximum count)
359 * of the combo box GList to the user's recent file */
361 dfilter_recent_combo_write_all(FILE *rf) {
362 GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
363 GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
368 /* write all non empty display filter strings to the recent file (until max count) */
369 li = g_list_first(filter_list);
370 while ( li && (max_count++ <= dfilter_combo_max_recent) ) {
371 if (strlen(li->data)) {
372 fprintf (rf, RECENT_KEY_DISPLAY_FILTER ": %s\n", (char *)li->data);
378 /* empty the combobox entry field */
380 dfilter_combo_add_empty(void) {
381 GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
383 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(filter_cm)->entry), "");
387 /* add a display filter coming from the user's recent file to the dfilter combo box */
389 dfilter_combo_add_recent(gchar *s) {
390 GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
394 if (!dfilter_combo_add(filter_cm, dup)) {
403 /* call filter_packets() and add this filter string to the recent filter list */
405 main_filter_packets(capture_file *cf, const gchar *dftext, gboolean force)
407 GtkCombo *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
408 GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
410 gboolean add_filter = TRUE;
411 gboolean free_filter = TRUE;
413 gboolean filter_packets_ret;
415 s = g_strdup(dftext);
417 /* GtkCombos don't let us get at their list contents easily, so we maintain
418 our own filter list, and feed it to gtk_combo_set_popdown_strings when
419 a new filter is added. */
420 if ((filter_packets_ret = filter_packets(cf, s, force))) {
421 li = g_list_first(filter_list);
423 if (li->data && strcmp(s, li->data) == 0)
429 /* trim list size first */
430 while (g_list_length(filter_list) >= dfilter_combo_max_recent) {
431 filter_list = g_list_remove(filter_list, g_list_first(filter_list)->data);
435 filter_list = g_list_append(filter_list, s);
436 OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
437 gtk_combo_set_popdown_strings(filter_cm, filter_list);
438 gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
444 return filter_packets_ret;
448 /* Run the current display filter on the current packet set, and
451 filter_activate_cb(GtkWidget *w _U_, gpointer data)
455 s = gtk_entry_get_text(GTK_ENTRY(data));
457 main_filter_packets(&cfile, s, FALSE);
460 /* redisplay with no display filter */
462 filter_reset_cb(GtkWidget *w, gpointer data _U_)
464 GtkWidget *filter_te = NULL;
466 if ((filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY))) {
467 gtk_entry_set_text(GTK_ENTRY(filter_te), "");
469 main_filter_packets(&cfile, "", FALSE);
472 /* mark as reference time frame */
474 set_frame_reftime(gboolean set, frame_data *frame, gint row) {
478 frame->flags.ref_time=1;
480 frame->flags.ref_time=0;
482 reftime_packets(&cfile);
486 reftime_frame_cb(GtkWidget *w _U_, gpointer data _U_, REFTIME_ACTION_E action)
491 if (cfile.current_frame) {
492 /* XXX hum, should better have a "cfile->current_row" here ... */
493 set_frame_reftime(!cfile.current_frame->flags.ref_time,
495 packet_list_find_row_from_data(cfile.current_frame));
498 case REFTIME_FIND_NEXT:
499 find_previous_next_frame_with_filter("frame.ref_time", FALSE);
501 case REFTIME_FIND_PREV:
502 find_previous_next_frame_with_filter("frame.ref_time", TRUE);
507 #if GTK_MAJOR_VERSION < 2
509 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column _U_,
510 gpointer user_data _U_)
513 tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data _U_)
517 gchar *help_str = NULL;
518 gchar len_str[2+10+1+5+1]; /* ", {N} bytes\0",
520 gboolean has_blurb = FALSE;
521 guint length = 0, byte_len;
522 GtkWidget *byte_view;
523 const guint8 *byte_data;
524 #if GTK_MAJOR_VERSION >= 2
529 #if GTK_MAJOR_VERSION >= 2
530 /* if nothing is selected */
531 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
534 * Which byte view is displaying the current protocol tree
537 byte_view = get_notebook_bv_ptr(byte_nb_ptr);
538 if (byte_view == NULL)
541 byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
542 if (byte_data == NULL)
545 unselect_field(&cfile);
546 packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data,
547 cfile.current_frame, NULL, byte_len);
550 gtk_tree_model_get(model, &iter, 1, &finfo, -1);
553 finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
557 set_notebook_page(byte_nb_ptr, finfo->ds_tvb);
559 byte_view = get_notebook_bv_ptr(byte_nb_ptr);
560 byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
561 g_assert(byte_data != NULL);
563 cfile.finfo_selected = finfo;
564 set_menus_for_selected_tree_row(&cfile);
567 if (finfo->hfinfo->blurb != NULL &&
568 finfo->hfinfo->blurb[0] != '\0') {
570 length = strlen(finfo->hfinfo->blurb);
572 length = strlen(finfo->hfinfo->name);
574 if (finfo->length == 0) {
576 } else if (finfo->length == 1) {
577 strcpy (len_str, ", 1 byte");
579 g_snprintf (len_str, sizeof len_str, ", %d bytes", finfo->length);
581 statusbar_pop_field_msg(); /* get rid of current help msg */
583 help_str = g_strdup_printf("%s (%s)%s",
584 (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name,
585 finfo->hfinfo->abbrev, len_str);
586 statusbar_push_field_msg(help_str);
590 * Don't show anything if the field name is zero-length;
591 * the pseudo-field for "proto_tree_add_text()" is such
592 * a field, and we don't want "Text (text)" showing up
593 * on the status line if you've selected such a field.
595 * XXX - there are zero-length fields for which we *do*
596 * want to show the field name.
598 * XXX - perhaps the name and abbrev field should be null
599 * pointers rather than null strings for that pseudo-field,
600 * but we'd have to add checks for null pointers in some
601 * places if we did that.
603 * Or perhaps protocol tree items added with
604 * "proto_tree_add_text()" should have -1 as the field index,
605 * with no pseudo-field being used, but that might also
606 * require special checks for -1 to be added.
608 statusbar_push_field_msg("");
612 #if GTK_MAJOR_VERSION < 2
613 packet_hex_print(GTK_TEXT(byte_view), byte_data, cfile.current_frame,
616 packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data, cfile.current_frame,
621 #if GTK_MAJOR_VERSION < 2
623 tree_view_unselect_row_cb(GtkCTree *ctree _U_, GList *node _U_, gint column _U_,
624 gpointer user_data _U_)
626 GtkWidget *byte_view;
631 * Which byte view is displaying the current protocol tree
634 byte_view = get_notebook_bv_ptr(byte_nb_ptr);
635 if (byte_view == NULL)
638 data = get_byte_view_data_and_length(byte_view, &len);
642 unselect_field(&cfile);
643 packet_hex_print(GTK_TEXT(byte_view), data, cfile.current_frame,
648 void collapse_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
650 collapse_all_tree(cfile.edt->tree, tree_view);
653 void expand_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
655 expand_all_tree(cfile.edt->tree, tree_view);
658 void expand_tree_cb(GtkWidget *widget _U_, gpointer data _U_) {
659 #if GTK_MAJOR_VERSION < 2
665 #if GTK_MAJOR_VERSION < 2
666 node = gtk_ctree_find_by_row_data(GTK_CTREE(tree_view), NULL, cfile.finfo_selected);
668 gtk_ctree_expand_recursive(GTK_CTREE(tree_view), node);
670 path = tree_find_by_field_info(GTK_TREE_VIEW(tree_view), cfile.finfo_selected);
672 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view), path, TRUE);
673 gtk_tree_path_free(path);
677 void resolve_name_cb(GtkWidget *widget _U_, gpointer data _U_) {
678 if (cfile.edt->tree) {
679 guint32 tmp = g_resolv_flags;
680 g_resolv_flags = RESOLV_ALL;
681 proto_tree_draw(cfile.edt->tree, tree_view);
682 g_resolv_flags = tmp;
687 * Push a message referring to file access onto the statusbar.
690 statusbar_push_file_msg(gchar *msg)
692 gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg);
696 * Pop a message referring to file access off the statusbar.
699 statusbar_pop_file_msg(void)
701 gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
705 * XXX - do we need multiple statusbar contexts?
709 * Push a message referring to the currently-selected field onto the statusbar.
712 statusbar_push_field_msg(gchar *msg)
714 gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg);
718 * Pop a message referring to the currently-selected field off the statusbar.
721 statusbar_pop_field_msg(void)
723 gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
727 * update the packets statusbar to the current values
729 void packets_bar_update(void)
733 /* remove old status */
736 gtk_statusbar_pop(GTK_STATUSBAR(packets_bar), packets_ctx);
739 /* do we have any packets? */
741 packets_str = g_strdup_printf(" P: %u D: %u M: %u",
742 cfile.count, cfile.displayed_count, cfile.marked_count);
744 packets_str = g_strdup(" No Packets");
746 gtk_statusbar_push(GTK_STATUSBAR(packets_bar), packets_ctx, packets_str);
757 /* get the current geometry, before writing it to disk */
758 main_save_window_geometry(top_level);
760 /* write user's recent file to disk
761 * It is no problem to write this file, even if we do not quit */
762 write_recent(&rec_path);
764 /* XXX - should we check whether the capture file is an
765 unsaved temporary file for a live capture and, if so,
766 pop up a "do you want to exit without saving the capture
767 file?" dialog, and then just return, leaving said dialog
768 box to forcibly quit if the user clicks "OK"?
770 If so, note that this should be done in a subroutine that
771 returns TRUE if we do so, and FALSE otherwise, and if it
772 returns TRUE we should return TRUE without nuking anything.
774 Note that, if we do that, we might also want to check if
775 an "Update list of packets in real time" capture is in
776 progress and, if so, ask whether they want to terminate
777 the capture and discard it, and return TRUE, before nuking
778 any child capture, if they say they don't want to do so. */
781 /* Nuke any child capture in progress. */
782 kill_capture_child();
785 /* Are we in the middle of reading a capture? */
786 if (cfile.state == FILE_READ_IN_PROGRESS) {
787 /* Yes, so we can't just close the file and quit, as
788 that may yank the rug out from under the read in
789 progress; instead, just set the state to
790 "FILE_READ_ABORTED" and return - the code doing the read
791 will check for that and, if it sees that, will clean
793 cfile.state = FILE_READ_ABORTED;
795 /* Say that the window should *not* be deleted;
796 that'll be done by the code that cleans up. */
799 /* Close any capture file we have open; on some OSes, you
800 can't unlink a temporary capture file if you have it
802 "cf_close()" will unlink it after closing it if
803 it's a temporary file.
805 We do this here, rather than after the main loop returns,
806 as, after the main loop returns, the main window may have
807 been destroyed (if this is called due to a "destroy"
808 even on the main window rather than due to the user
809 selecting a menu item), and there may be a crash
810 or other problem when "cf_close()" tries to
811 clean up stuff in the main window.
813 XXX - is there a better place to put this?
814 Or should we have a routine that *just* closes the
815 capture file, and doesn't do anything with the UI,
816 which we'd call here, and another routine that
817 calls that routine and also cleans up the UI, which
818 we'd call elsewhere? */
821 /* Exit by leaving the main loop, so that any quit functions
822 we registered get called. */
825 /* Say that the window should be deleted. */
831 main_window_delete_event_cb(GtkWidget *widget _U_, GdkEvent *event _U_, gpointer data _U_)
835 if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
836 #if GTK_MAJOR_VERSION >= 2
837 gtk_window_present(GTK_WINDOW(top_level));
839 /* user didn't saved his current file, ask him */
840 dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
841 PRIMARY_TEXT_START "Save capture file before program quit?" PRIMARY_TEXT_END "\n\n"
842 "If you quit the program without saving, your capture data will be discarded.");
843 simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL);
846 /* unchanged file, just exit */
847 /* "main_do_quit()" indicates whether the main window should be deleted. */
848 return main_do_quit();
855 main_load_window_geometry(GtkWidget *widget)
857 window_geometry_t geom;
859 geom.set_pos = prefs.gui_geometry_save_position;
860 geom.x = recent.gui_geometry_main_x;
861 geom.y = recent.gui_geometry_main_y;
862 geom.set_size = prefs.gui_geometry_save_size;
863 if (recent.gui_geometry_main_width > 0 &&
864 recent.gui_geometry_main_height > 0) {
865 geom.width = recent.gui_geometry_main_width;
866 geom.height = recent.gui_geometry_main_height;
867 geom.set_maximized = prefs.gui_geometry_save_maximized;
869 /* We assume this means the width and height weren't set in
870 the "recent" file (or that there is no "recent" file),
871 and weren't set to a default value, so we don't set the
872 size. (The "recent" file code rejects non-positive width
873 and height values.) */
874 geom.set_size = FALSE;
876 geom.maximized = recent.gui_geometry_main_maximized;
878 window_set_geometry(widget, &geom);
880 #if GTK_MAJOR_VERSION >= 2
881 /* XXX - rename recent settings? */
882 if (recent.gui_geometry_main_upper_pane)
883 gtk_paned_set_position(GTK_PANED(main_first_pane), recent.gui_geometry_main_upper_pane);
884 if (recent.gui_geometry_main_lower_pane)
885 gtk_paned_set_position(GTK_PANED(main_second_pane), recent.gui_geometry_main_lower_pane);
886 if (recent.gui_geometry_status_pane)
887 gtk_paned_set_position(GTK_PANED(status_pane), recent.gui_geometry_status_pane);
893 main_save_window_geometry(GtkWidget *widget)
895 window_geometry_t geom;
897 window_get_geometry(widget, &geom);
899 if (prefs.gui_geometry_save_position) {
900 recent.gui_geometry_main_x = geom.x;
901 recent.gui_geometry_main_y = geom.y;
904 if (prefs.gui_geometry_save_size) {
905 recent.gui_geometry_main_width = geom.width,
906 recent.gui_geometry_main_height = geom.height;
909 #if GTK_MAJOR_VERSION >= 2
910 if(prefs.gui_geometry_save_maximized) {
911 recent.gui_geometry_main_maximized = geom.maximized;
914 recent.gui_geometry_main_upper_pane = gtk_paned_get_position(GTK_PANED(main_first_pane));
915 recent.gui_geometry_main_lower_pane = gtk_paned_get_position(GTK_PANED(main_second_pane));
916 recent.gui_geometry_status_pane = gtk_paned_get_position(GTK_PANED(status_pane));
920 static void file_quit_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
924 /* save file first */
925 file_save_as_cmd(after_save_exit, NULL);
927 case(ESD_BTN_DONT_SAVE):
930 case(ESD_BTN_CANCEL):
933 g_assert_not_reached();
938 file_quit_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
942 if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
943 /* user didn't saved his current file, ask him */
944 dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
945 PRIMARY_TEXT_START "Save capture file before program quit?" PRIMARY_TEXT_END "\n\n"
946 "If you quit the program without saving, your capture data will be discarded.");
947 simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL);
949 /* unchanged file, just exit */
955 print_usage(gboolean print_ver) {
961 fprintf(output, "This is GNU " PACKAGE " " VERSION
966 comp_info_str->str, runtime_info_str->str);
971 fprintf(output, "\n%s [ -vh ] [ -klLnpQS ] [ -a <capture autostop condition> ] ...\n",
973 fprintf(output, "\t[ -b <number of ringbuffer files>[:<duration>] ]\n");
974 fprintf(output, "\t[ -B <byte view height> ] [ -c <count> ] [ -f <capture filter> ]\n");
975 fprintf(output, "\t[ -i <interface> ] [ -m <medium font> ] [ -N <resolving> ]\n");
976 fprintf(output, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
977 fprintf(output, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
978 fprintf(output, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
979 fprintf(output, "\t[ -w <savefile> ] [ -y <link type> ] [ -z <statistics string> ]\n");
980 fprintf(output, "\t[ <infile> ]\n");
982 fprintf(output, "\n%s [ -vh ] [ -n ] [ -B <byte view height> ] [ -m <medium font> ]\n",
984 fprintf(output, "\t[ -N <resolving> ] [ -o <preference setting> ...\n");
985 fprintf(output, "\t[ -P <packet list height> ] [ -r <infile> ] [ -R <read filter> ]\n");
986 fprintf(output, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
987 fprintf(output, "\t[ -z <statistics string> ] [ <infile> ]\n");
998 printf(PACKAGE " " VERSION
1003 comp_info_str->str, runtime_info_str->str);
1007 get_natural_int(const char *string, const char *name)
1012 number = strtol(string, &p, 10);
1013 if (p == string || *p != '\0') {
1014 fprintf(stderr, "ethereal: The specified %s \"%s\" is not a decimal number\n",
1019 fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
1023 if (number > INT_MAX) {
1024 fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
1025 name, string, INT_MAX);
1032 get_positive_int(const char *string, const char *name)
1036 number = get_natural_int(string, name);
1039 fprintf(stderr, "ethereal: The specified %s is zero\n",
1049 * Given a string of the form "<autostop criterion>:<value>", as might appear
1050 * as an argument to a "-a" option, parse it and set the criterion in
1051 * question. Return an indication of whether it succeeded or failed
1055 set_autostop_criterion(const char *autostoparg)
1059 colonp = strchr(autostoparg, ':');
1067 * Skip over any white space (there probably won't be any, but
1068 * as we allow it in the preferences file, we might as well
1071 while (isspace((guchar)*p))
1075 * Put the colon back, so if our caller uses, in an
1076 * error message, the string they passed us, the message
1082 if (strcmp(autostoparg,"duration") == 0) {
1083 capture_opts.has_autostop_duration = TRUE;
1084 capture_opts.autostop_duration = get_positive_int(p,"autostop duration");
1085 } else if (strcmp(autostoparg,"filesize") == 0) {
1086 capture_opts.has_autostop_filesize = TRUE;
1087 capture_opts.autostop_filesize = get_positive_int(p,"autostop filesize");
1091 *colonp = ':'; /* put the colon back */
1096 * Given a string of the form "<ring buffer file>:<duration>", as might appear
1097 * as an argument to a "-b" option, parse it and set the arguments in
1098 * question. Return an indication of whether it succeeded or failed
1102 get_ring_arguments(const char *arg)
1104 gchar *p = NULL, *colonp;
1106 colonp = strchr(arg, ':');
1108 if (colonp != NULL) {
1113 capture_opts.ring_num_files =
1114 get_natural_int(arg, "number of ring buffer files");
1120 * Skip over any white space (there probably won't be any, but
1121 * as we allow it in the preferences file, we might as well
1124 while (isspace((guchar)*p))
1128 * Put the colon back, so if our caller uses, in an
1129 * error message, the string they passed us, the message
1136 capture_opts.has_file_duration = TRUE;
1137 capture_opts.file_duration = get_positive_int(p,
1138 "ring buffer duration");
1140 *colonp = ':'; /* put the colon back */
1145 #if defined(_WIN32) || GTK_MAJOR_VERSION < 2 || ! defined USE_THREADS
1147 Once every 3 seconds we get a callback here which we use to update
1148 the tap extensions. Since Gtk1 is single threaded we dont have to
1149 worry about any locking or critical regions.
1152 update_cb(gpointer data _U_)
1154 draw_tap_listeners(FALSE);
1159 /* if these three functions are copied to gtk1 ethereal, since gtk1 does not
1160 use threads all updte_thread_mutex can be dropped and protect/unprotect
1161 would just be empty functions.
1163 This allows gtk2-rpcstat.c and friends to be copied unmodified to
1164 gtk1-ethereal and it will just work.
1166 static GStaticMutex update_thread_mutex = G_STATIC_MUTEX_INIT;
1168 update_thread(gpointer data _U_)
1172 g_get_current_time(&tv1);
1173 g_static_mutex_lock(&update_thread_mutex);
1174 gdk_threads_enter();
1175 draw_tap_listeners(FALSE);
1176 gdk_threads_leave();
1177 g_static_mutex_unlock(&update_thread_mutex);
1179 g_get_current_time(&tv2);
1180 if( ((tv1.tv_sec + 2) * 1000000 + tv1.tv_usec) >
1181 (tv2.tv_sec * 1000000 + tv2.tv_usec) ){
1182 g_usleep(((tv1.tv_sec + 2) * 1000000 + tv1.tv_usec) -
1183 (tv2.tv_sec * 1000000 + tv2.tv_usec));
1190 protect_thread_critical_region(void)
1192 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2 && defined USE_THREADS
1193 g_static_mutex_lock(&update_thread_mutex);
1197 unprotect_thread_critical_region(void)
1199 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2 && defined USE_THREADS
1200 g_static_mutex_unlock(&update_thread_mutex);
1204 /* structure to keep track of what tap listeners have been registered.
1206 typedef struct _ethereal_tap_list {
1207 struct _ethereal_tap_list *next;
1209 void (*func)(char *arg);
1210 } ethereal_tap_list;
1211 static ethereal_tap_list *tap_list=NULL;
1214 register_ethereal_tap(char *cmd, void (*func)(char *arg))
1216 ethereal_tap_list *newtl;
1218 newtl=malloc(sizeof(ethereal_tap_list));
1219 newtl->next=tap_list;
1227 enum { DND_TARGET_STRING, DND_TARGET_ROOTWIN, DND_TARGET_URL };
1229 /* convert drag and drop URI to a local filename */
1231 dnd_uri2filename(gchar *cf_name)
1240 * Remove URI header.
1241 * On win32 (at least WinXP), this string looks like (UNC or local filename):
1242 * file:////servername/sharename/dir1/dir2/capture-file.cap
1244 * file:///d:/dir1/dir2/capture-file.cap
1245 * we have to remove the prefix to get a valid filename.
1247 * On UNIX (at least KDE 3.0 Konqueror), this string looks like:
1248 * file:/dir1/dir2/capture-file.cap
1249 * we have to remove the file: to get a valid filename.
1251 if (strncmp("file:////", cf_name, 9) == 0) {
1252 /* win32 UNC: now becoming: //servername/sharename/dir1/dir2/capture-file.cap */
1254 } else if (strncmp("file:///", cf_name, 8) == 0) {
1255 /* win32 local: now becoming: d:/dir1/dir2/capture-file.cap */
1257 } else if (strncmp("file:", cf_name, 5) == 0) {
1258 /* unix local: now becoming: /dir1/dir2/capture-file.cap */
1263 * unescape the escaped URI characters (spaces, ...)
1265 * we have to replace escaped chars to their equivalents,
1266 * e.g. %20 (always a two digit hexstring) -> ' '
1267 * the percent character '%' is escaped be a double one "%%"
1269 * we do this conversation "in place" as the result is always
1270 * equal or smaller in size.
1278 /* this is an escaped '%' char (was: "%%") */
1283 /* convert escaped hexnumber to unscaped character */
1287 ret = sscanf(esc, "%x", &i);
1293 /* somethings wrong, just jump over that char
1294 * this will result in a wrong string, but we might get
1295 * user feedback and can fix it later ;-) */
1311 dnd_merge_files(int in_file_count, char **in_filenames)
1316 char tmpname[128+1];
1319 out_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
1321 /* merge the files in chonological order */
1322 merge_ok = merge_n_files(out_fd, in_file_count, in_filenames, FALSE, &err);
1326 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1327 "An error occurred while merging the files: \"%s\".",
1328 wtap_strerror(err));
1335 /* Try to open the merged capture file. */
1336 if ((err = cf_open(tmpname, TRUE /* temporary file */, &cfile)) != 0) {
1337 /* We couldn't open it; don't dismiss the open dialog box,
1338 just leave it around so that the user can, after they
1339 dismiss the alert box popped up for the open error,
1344 switch (cf_read(&cfile)) {
1348 /* Just because we got an error, that doesn't mean we were unable
1349 to read any of the file; we handle what we could get from the
1354 /* The user bailed out of re-reading the capture file; the
1355 capture file has been closed - just free the capture file name
1356 string and return (without changing the last containing
1361 gtk_widget_grab_focus(packet_list);
1364 /* open/merge the dnd file */
1366 dnd_open_file_cmd(GtkSelectionData *selection_data)
1369 gchar *cf_name, *cf_name_freeme;
1372 GString *dialog_text;
1374 char **in_filenames;
1377 /* DND_TARGET_URL on Win32:
1378 * The selection_data->data is a single string, containing one or more URI's,
1379 * seperated by CR/NL chars. The length of the whole field can be found
1380 * in the selection_data->length field. If it contains one file, simply open it,
1381 * If it contains more than one file, ask to merge these files. */
1383 /* the data string is not zero terminated -> make a zero terminated "copy" of it */
1384 cf_name_freeme = g_malloc(selection_data->length + 1);
1385 memcpy(cf_name_freeme, selection_data->data, selection_data->length);
1386 cf_name_freeme[selection_data->length] = '\0';
1388 /* count the number of input files */
1389 cf_name = cf_name_freeme;
1390 for(in_files = 0; (cf_name = strstr(cf_name, "\r\n")) != NULL; ) {
1395 in_filenames = g_malloc(sizeof(char*) * in_files);
1397 /* store the starts of the file entries in a gchar array */
1398 cf_name = cf_name_freeme;
1399 in_filenames[0] = cf_name;
1400 for(files_work = 1; (cf_name = strstr(cf_name, "\r\n")) != NULL && files_work < in_files; ) {
1402 in_filenames[files_work] = cf_name;
1406 /* replace trailing CR NL simply with zeroes (in place), so we get valid terminated strings */
1407 cf_name = cf_name_freeme;
1408 g_strdelimit(cf_name, "\r\n", '\0');
1410 /* convert all filenames from URI to local filename (in place) */
1411 for(files_work = 0; files_work < in_files; files_work++) {
1412 in_filenames[files_work] = dnd_uri2filename(in_filenames[files_work]);
1417 /* shouldn't happen */
1420 /* open and read the capture file (this will close an existing file) */
1421 if ((err = cf_open(in_filenames[0], FALSE, &cfile)) == 0) {
1423 add_menu_recent_capture_file(in_filenames[0]);
1425 /* the capture file couldn't be read (doesn't exist, file format unknown, ...) */
1429 /* build and show the info dialog */
1430 dialog_text = g_string_sized_new(200);
1431 g_string_append(dialog_text, PRIMARY_TEXT_START
1432 "Merging the following files:" PRIMARY_TEXT_END "\n\n");
1433 for(files_work = 0; files_work < in_files; files_work++) {
1434 g_string_append(dialog_text, in_filenames[files_work]);
1435 g_string_append(dialog_text, "\n");
1437 g_string_append(dialog_text, "\nThe packets in these files will be merged chronologically into a new temporary file.");
1438 dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
1441 g_string_free(dialog_text, TRUE);
1443 /* actually merge the files now */
1444 dnd_merge_files(in_files, in_filenames);
1447 g_free(in_filenames);
1448 g_free(cf_name_freeme);
1451 /* ask the user to save current unsaved file, before opening the dnd file */
1453 dnd_save_file_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
1457 /* save file first */
1458 file_save_as_cmd(after_save_open_dnd_file, data);
1460 case(ESD_BTN_DONT_SAVE):
1462 dnd_open_file_cmd(data);
1464 case(ESD_BTN_CANCEL):
1467 g_assert_not_reached();
1472 /* we have received some drag and drop data */
1473 /* (as we only registered to "text/uri-list", we will only get a file list here) */
1475 dnd_data_received(GtkWidget *widget _U_, GdkDragContext *dc _U_, gint x _U_, gint y _U_,
1476 GtkSelectionData *selection_data, guint info, guint t _U_, gpointer data _U_)
1480 if (info == DND_TARGET_URL) {
1481 /* ask the user to save it's current capture file first */
1482 if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
1483 /* user didn't saved his current file, ask him */
1484 dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
1485 ESD_BTNS_SAVE_DONTSAVE_CANCEL,
1486 PRIMARY_TEXT_START "Save capture file before opening a new one?" PRIMARY_TEXT_END "\n\n"
1487 "If you open a new capture file without saving, your current capture data will be discarded.");
1488 simple_dialog_set_cb(dialog, dnd_save_file_answered_cb, selection_data);
1490 /* unchanged file */
1491 dnd_open_file_cmd(selection_data);
1496 /* init the drag and drop functionality */
1498 dnd_init(GtkWidget *w)
1500 /* we are only interested in the URI list containing filenames */
1501 static GtkTargetEntry target_entry[] = {
1502 /*{"STRING", 0, DND_TARGET_STRING},*/
1503 /*{"text/plain", 0, DND_TARGET_STRING},*/
1504 {"text/uri-list", 0, DND_TARGET_URL}
1507 /* set this window as a dnd destination */
1509 w, GTK_DEST_DEFAULT_ALL, target_entry,
1510 sizeof(target_entry) / sizeof(GtkTargetEntry),
1511 (GdkDragAction)(GDK_ACTION_MOVE | GDK_ACTION_COPY) );
1513 /* get notified, if some dnd coming in */
1514 gtk_signal_connect(GTK_OBJECT(w), "drag_data_received",
1515 GTK_SIGNAL_FUNC(dnd_data_received), NULL);
1519 /* And now our feature presentation... [ fade to music ] */
1521 main(int argc, char *argv[])
1529 extern char *optarg;
1530 gboolean arg_error = FALSE;
1538 char *gpf_path, *pf_path;
1539 char *cf_path, *df_path;
1540 char *gdp_path, *dp_path;
1541 int gpf_open_errno, gpf_read_errno;
1542 int pf_open_errno, pf_read_errno;
1543 int cf_open_errno, df_open_errno;
1544 int gdp_open_errno, gdp_read_errno;
1545 int dp_open_errno, dp_read_errno;
1548 gboolean start_capture = FALSE;
1549 gchar *save_file = NULL;
1552 GList *lt_list, *lt_entry;
1553 data_link_info_t *data_link_info;
1554 gchar err_str[PCAP_ERRBUF_SIZE];
1555 gchar *cant_get_if_list_errstr;
1556 gboolean stats_known;
1557 struct pcap_stat stats;
1559 gboolean capture_option_specified = FALSE;
1561 gint pl_size = 280, tv_size = 95, bv_size = 75;
1562 gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
1563 dfilter_t *rfcode = NULL;
1564 gboolean rfilter_parse_failed = FALSE;
1567 ethereal_tap_list *tli = NULL;
1568 gchar *tap_opt = NULL;
1569 GtkWidget *splash_win = NULL;
1571 #define OPTSTRING_INIT "a:b:B:c:f:Hhi:klLm:nN:o:pP:Qr:R:Ss:t:T:w:vy:z:"
1575 #define OPTSTRING_CHILD "W:Z:"
1577 #define OPTSTRING_CHILD "W:"
1580 #define OPTSTRING_CHILD ""
1581 #endif /* HAVE_LIBPCAP */
1583 char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_CHILD) - 1] =
1587 /* Set the current locale according to the program environment.
1588 * We haven't localized anything, but some GTK widgets are localized
1589 * (the file selection dialogue, for example).
1590 * This also sets the C-language locale to the native environment. */
1593 /* Let GTK get its args */
1594 gtk_init (&argc, &argv);
1597 ethereal_path = argv[0];
1600 /* Arrange that if we have no console window, and a GLib message logging
1601 routine is called to log a message, we pop up a console window.
1603 We do that by inserting our own handler for all messages logged
1604 to the default domain; that handler pops up a console if necessary,
1605 and then calls the default handler. */
1606 g_log_set_handler(NULL,
1608 G_LOG_LEVEL_CRITICAL|
1609 G_LOG_LEVEL_WARNING|
1610 G_LOG_LEVEL_MESSAGE|
1613 G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION,
1614 console_log_handler, NULL);
1617 command_name = get_basename(ethereal_path);
1618 /* Set "capture_child" to indicate whether this is going to be a child
1619 process for a "-S" capture. */
1620 capture_child = (strcmp(command_name, CHILD_NAME) == 0);
1621 if (capture_child) {
1622 strcat(optstring, OPTSTRING_CHILD);
1623 } else if (argc < 2 || strcmp(argv[1], "-G") != 0) {
1624 /* We want a splash screen only if we're not a child process */
1625 splash_win = splash_new("Loading Ethereal ...");
1629 splash_update(splash_win, "Registering dissectors ...");
1631 /* Register all dissectors; we must do this before checking for the
1632 "-G" flag, as the "-G" flag dumps information registered by the
1633 dissectors, and we must do it before we read the preferences, in
1634 case any dissectors register preferences. */
1635 epan_init(PLUGIN_DIR,register_all_protocols,register_all_protocol_handoffs,
1636 failure_alert_box,open_failure_alert_box,read_failure_alert_box);
1638 splash_update(splash_win, "Registering tap listeners ...");
1640 /* Register all tap listeners; we do this before we parse the arguments,
1641 as the "-z" argument can specify a registered tap. */
1642 register_all_tap_listeners();
1644 splash_update(splash_win, "Loading module preferences ...");
1646 /* Now register the preferences for any non-dissector modules.
1647 We must do that before we read the preferences as well. */
1648 prefs_register_modules();
1650 /* If invoked with the "-G" flag, we dump out information based on
1651 the argument to the "-G" flag; if no argument is specified,
1652 for backwards compatibility we dump out a glossary of display
1655 We must do this before calling "gtk_init()", because "gtk_init()"
1656 tries to open an X display, and we don't want to have to do any X
1657 stuff just to do a build.
1659 Given that we call "gtk_init()" before doing the regular argument
1660 list processing, so that it can handle X and GTK+ arguments and
1661 remove them from the list at which we look, this means we must do
1662 this before doing the regular argument list processing, as well.
1666 you must give the "-G" flag as the first flag on the command line;
1668 you must give it as "-G", nothing more, nothing less;
1670 the first argument after the "-G" flag, if present, will be used
1671 to specify the information to dump;
1673 arguments after that will not be used. */
1674 handle_dashG_option(argc, argv, "ethereal");
1676 /* multithread support currently doesn't seem to work in win32 gtk2.0.6 */
1677 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2 && defined(G_THREADS_ENABLED) && defined USE_THREADS
1680 g_thread_init(NULL);
1682 ut=g_thread_create(update_thread, NULL, FALSE, NULL);
1683 g_thread_set_priority(ut, G_THREAD_PRIORITY_LOW);
1685 #else /* _WIN32 || GTK1.2 || !G_THREADS_ENABLED || !USE_THREADS */
1686 /* this is to keep tap extensions updating once every 3 seconds */
1687 gtk_timeout_add(3000, (GtkFunction)update_cb,(gpointer)NULL);
1688 #endif /* !_WIN32 && GTK2 && G_THREADS_ENABLED */
1691 gtk_timeout_add(750, (GtkFunction) host_name_lookup_process, NULL);
1694 splash_update(splash_win, "Loading configuration files ...");
1696 /* Read the preference files. */
1697 prefs = read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path,
1698 &pf_open_errno, &pf_read_errno, &pf_path);
1699 if (gpf_path != NULL) {
1700 if (gpf_open_errno != 0) {
1701 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1702 "Could not open global preferences file\n\"%s\": %s.", gpf_path,
1703 strerror(gpf_open_errno));
1705 if (gpf_read_errno != 0) {
1706 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1707 "I/O error reading global preferences file\n\"%s\": %s.", gpf_path,
1708 strerror(gpf_read_errno));
1711 if (pf_path != NULL) {
1712 if (pf_open_errno != 0) {
1713 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1714 "Could not open your preferences file\n\"%s\": %s.", pf_path,
1715 strerror(pf_open_errno));
1717 if (pf_read_errno != 0) {
1718 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1719 "I/O error reading your preferences file\n\"%s\": %s.", pf_path,
1720 strerror(pf_read_errno));
1727 if (prefs->gui_console_open == console_open_always) {
1733 capture_opts.has_snaplen = FALSE;
1734 capture_opts.snaplen = MIN_PACKET_SIZE;
1735 capture_opts.linktype = -1;
1737 capture_opts.buffer_size = 1;
1740 capture_opts.has_autostop_packets = FALSE;
1741 capture_opts.autostop_packets = 1;
1742 capture_opts.has_autostop_duration = FALSE;
1743 capture_opts.autostop_duration = 60 /* 1 min */;
1744 capture_opts.has_autostop_filesize = FALSE;
1745 capture_opts.autostop_filesize = 1024 * 1024 /* 1 MB */;
1746 capture_opts.has_autostop_files = FALSE;
1747 capture_opts.autostop_files = 1;
1749 capture_opts.multi_files_on = FALSE;
1750 capture_opts.has_ring_num_files = TRUE;
1751 capture_opts.ring_num_files = 2;
1752 capture_opts.has_file_duration = FALSE;
1753 capture_opts.file_duration = 60 /* 1 min */;
1755 /* If this is a capture child process, it should pay no attention
1756 to the "prefs.capture_prom_mode" setting in the preferences file;
1757 it should do what the parent process tells it to do, and if
1758 the parent process wants it not to run in promiscuous mode, it'll
1759 tell it so with a "-p" flag.
1761 Otherwise, set promiscuous mode from the preferences setting. */
1762 /* the same applies to other preferences settings as well. */
1763 if (capture_child) {
1764 capture_opts.promisc_mode = TRUE; /* maybe changed by command line below */
1765 capture_opts.show_info = TRUE; /* maybe changed by command line below */
1766 capture_opts.sync_mode = TRUE; /* always true in child process */
1767 auto_scroll_live = FALSE; /* doesn't matter in child process */
1770 capture_opts.promisc_mode = prefs->capture_prom_mode;
1771 capture_opts.show_info = prefs->capture_show_info;
1772 capture_opts.sync_mode = prefs->capture_real_time;
1773 auto_scroll_live = prefs->capture_auto_scroll;
1776 #endif /* HAVE_LIBPCAP */
1778 /* Set the name resolution code's flags from the preferences. */
1779 g_resolv_flags = prefs->name_resolve;
1781 /* Read the capture filter file. */
1782 read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
1783 if (cf_path != NULL) {
1784 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1785 "Could not open your capture filter file\n\"%s\": %s.", cf_path,
1786 strerror(cf_open_errno));
1790 /* Read the display filter file. */
1791 read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
1792 if (df_path != NULL) {
1793 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1794 "Could not open your display filter file\n\"%s\": %s.", df_path,
1795 strerror(df_open_errno));
1799 /* Read the disabled protocols file. */
1800 read_disabled_protos_list(&gdp_path, &gdp_open_errno, &gdp_read_errno,
1801 &dp_path, &dp_open_errno, &dp_read_errno);
1802 if (gdp_path != NULL) {
1803 if (gdp_open_errno != 0) {
1804 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1805 "Could not open global disabled protocols file\n\"%s\": %s.",
1806 gdp_path, strerror(gdp_open_errno));
1808 if (gdp_read_errno != 0) {
1809 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1810 "I/O error reading global disabled protocols file\n\"%s\": %s.",
1811 gdp_path, strerror(gdp_read_errno));
1815 if (dp_path != NULL) {
1816 if (dp_open_errno != 0) {
1817 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1818 "Could not open your disabled protocols file\n\"%s\": %s.", dp_path,
1819 strerror(dp_open_errno));
1821 if (dp_read_errno != 0) {
1822 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1823 "I/O error reading your disabled protocols file\n\"%s\": %s.", dp_path,
1824 strerror(dp_read_errno));
1829 init_cap_file(&cfile);
1832 /* Load wpcap if possible. Do this before collecting the run-time version information */
1835 /* Start windows sockets */
1836 WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
1839 /* Assemble the compile-time version information string */
1840 comp_info_str = g_string_new("Compiled ");
1841 g_string_append(comp_info_str, "with ");
1842 g_string_sprintfa(comp_info_str,
1843 #ifdef GTK_MAJOR_VERSION
1844 "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
1847 "GTK+ (version unknown)");
1850 g_string_append(comp_info_str, ", ");
1851 get_compiled_version_info(comp_info_str);
1853 /* Assemble the run-time version information string */
1854 runtime_info_str = g_string_new("Running ");
1855 get_runtime_version_info(runtime_info_str);
1857 /* Now get our args */
1858 while ((opt = getopt(argc, argv, optstring)) != -1) {
1860 case 'a': /* autostop criteria */
1862 if (set_autostop_criterion(optarg) == FALSE) {
1863 fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
1867 capture_option_specified = TRUE;
1871 case 'b': /* Ringbuffer option */
1873 capture_opts.multi_files_on = TRUE;
1874 capture_opts.has_ring_num_files = TRUE;
1875 if (get_ring_arguments(optarg) == FALSE) {
1876 fprintf(stderr, "ethereal: Invalid or unknown -b arg \"%s\"\n", optarg);
1880 capture_option_specified = TRUE;
1884 case 'B': /* Byte view pane height */
1885 bv_size = get_positive_int(optarg, "byte view pane height");
1887 case 'c': /* Capture xxx packets */
1889 capture_opts.has_autostop_packets = TRUE;
1890 capture_opts.autostop_packets = get_positive_int(optarg, "packet count");
1892 capture_option_specified = TRUE;
1899 g_free(cfile.cfilter);
1900 cfile.cfilter = g_strdup(optarg);
1902 capture_option_specified = TRUE;
1906 case 'h': /* Print help and exit */
1910 case 'i': /* Use interface xxx */
1912 cfile.iface = g_strdup(optarg);
1914 capture_option_specified = TRUE;
1918 case 'k': /* Start capture immediately */
1920 start_capture = TRUE;
1922 capture_option_specified = TRUE;
1926 case 'l': /* Automatic scrolling in live capture mode */
1928 auto_scroll_live = TRUE;
1930 capture_option_specified = TRUE;
1934 case 'H': /* Hide capture info dialog box */
1936 capture_opts.show_info = FALSE;
1938 capture_option_specified = TRUE;
1942 case 'L': /* Print list of link-layer types and exit */
1944 list_link_layer_types = TRUE;
1946 capture_option_specified = TRUE;
1950 case 'm': /* Fixed-width font for the display */
1951 if (prefs->PREFS_GUI_FONT_NAME != NULL)
1952 g_free(prefs->PREFS_GUI_FONT_NAME);
1953 prefs->PREFS_GUI_FONT_NAME = g_strdup(optarg);
1955 case 'n': /* No name resolution */
1956 g_resolv_flags = RESOLV_NONE;
1958 case 'N': /* Select what types of addresses/port #s to resolve */
1959 if (g_resolv_flags == RESOLV_ALL)
1960 g_resolv_flags = RESOLV_NONE;
1961 badopt = string_to_name_resolve(optarg, &g_resolv_flags);
1962 if (badopt != '\0') {
1963 fprintf(stderr, "ethereal: -N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'\n",
1968 case 'o': /* Override preference from command line */
1969 switch (prefs_set_pref(optarg)) {
1971 case PREFS_SET_SYNTAX_ERR:
1972 fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
1976 case PREFS_SET_NO_SUCH_PREF:
1977 case PREFS_SET_OBSOLETE:
1978 fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
1984 case 'p': /* Don't capture in promiscuous mode */
1986 capture_opts.promisc_mode = FALSE;
1988 capture_option_specified = TRUE;
1992 case 'P': /* Packet list pane height */
1993 pl_size = get_positive_int(optarg, "packet list pane height");
1995 case 'Q': /* Quit after capture (just capture to file) */
1997 quit_after_cap = TRUE;
1998 start_capture = TRUE; /*** -Q implies -k !! ***/
2000 capture_option_specified = TRUE;
2004 case 'r': /* Read capture file xxx */
2005 /* We may set "last_open_dir" to "cf_name", and if we change
2006 "last_open_dir" later, we free the old value, so we have to
2007 set "cf_name" to something that's been allocated. */
2008 cf_name = g_strdup(optarg);
2010 case 'R': /* Read file filter */
2013 case 's': /* Set the snapshot (capture) length */
2015 capture_opts.has_snaplen = TRUE;
2016 capture_opts.snaplen = get_positive_int(optarg, "snapshot length");
2018 capture_option_specified = TRUE;
2022 case 'S': /* "Sync" mode: used for following file ala tail -f */
2024 capture_opts.sync_mode = TRUE;
2026 capture_option_specified = TRUE;
2030 case 't': /* Time stamp type */
2031 if (strcmp(optarg, "r") == 0)
2032 set_timestamp_setting(TS_RELATIVE);
2033 else if (strcmp(optarg, "a") == 0)
2034 set_timestamp_setting(TS_ABSOLUTE);
2035 else if (strcmp(optarg, "ad") == 0)
2036 set_timestamp_setting(TS_ABSOLUTE_WITH_DATE);
2037 else if (strcmp(optarg, "d") == 0)
2038 set_timestamp_setting(TS_DELTA);
2040 fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
2042 fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
2043 fprintf(stderr, "\"ad\" for absolute with date, or \"d\" for delta.\n");
2047 case 'T': /* Tree view pane height */
2048 tv_size = get_positive_int(optarg, "tree view pane height");
2050 case 'v': /* Show version and exit */
2057 case 'w': /* Write to capture file xxx */
2059 save_file = g_strdup(optarg);
2061 capture_option_specified = TRUE;
2065 case 'y': /* Set the pcap data link type */
2067 #ifdef HAVE_PCAP_DATALINK_NAME_TO_VAL
2068 capture_opts.linktype = pcap_datalink_name_to_val(optarg);
2069 if (capture_opts.linktype == -1) {
2070 fprintf(stderr, "ethereal: The specified data link type \"%s\" is not valid\n",
2074 #else /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
2075 /* XXX - just treat it as a number */
2076 capture_opts.linktype = get_natural_int(optarg, "data link type");
2077 #endif /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
2078 #else /* HAVE_LIBPCAP */
2079 capture_option_specified = TRUE;
2081 #endif /* HAVE_LIBPCAP */
2084 /* This is a hidden option supporting Sync mode, so we don't set
2085 * the error flags for the user in the non-libpcap case.
2087 case 'W': /* Write to capture file FD xxx */
2088 cfile.save_file_fd = atoi(optarg);
2092 for(tli=tap_list;tli;tli=tli->next){
2093 if(!strncmp(tli->cmd,optarg,strlen(tli->cmd))){
2094 tap_opt = g_strdup(optarg);
2099 fprintf(stderr,"ethereal: invalid -z argument.\n");
2100 fprintf(stderr," -z argument must be one of :\n");
2101 for(tli=tap_list;tli;tli=tli->next){
2102 fprintf(stderr," %s\n",tli->cmd);
2110 /* Hidden option supporting Sync mode */
2111 case 'Z': /* Write to pipe FD XXX */
2112 /* associate stdout with pipe */
2114 if (dup2(i, 1) < 0) {
2115 fprintf(stderr, "Unable to dup pipe handle\n");
2119 #endif /* HAVE_LIBPCAP */
2123 case '?': /* Bad flag - print usage message */
2131 if (cf_name != NULL) {
2133 * Input file name specified with "-r" *and* specified as a regular
2134 * command-line argument.
2139 * Input file name not specified with "-r", and a command-line argument
2140 * was specified; treat it as the input file name.
2142 * Yes, this is different from tethereal, where non-flag command-line
2143 * arguments are a filter, but this works better on GUI desktops
2144 * where a command can be specified to be run to open a particular
2145 * file - yes, you could have "-r" as the last part of the command,
2146 * but that's a bit ugly.
2148 cf_name = g_strdup(argv[0]);
2156 * Extra command line arguments were specified; complain.
2158 fprintf(stderr, "Invalid argument: %s\n", argv[0]);
2162 #ifndef HAVE_LIBPCAP
2163 if (capture_option_specified)
2164 fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
2172 if (start_capture && list_link_layer_types) {
2173 /* Specifying *both* is bogus. */
2174 fprintf(stderr, "ethereal: You cannot specify both -L and a live capture.\n");
2178 if (list_link_layer_types) {
2179 /* We're supposed to list the link-layer types for an interface;
2180 did the user also specify a capture file to be read? */
2182 /* Yes - that's bogus. */
2183 fprintf(stderr, "ethereal: You cannot specify -L and a capture file to be read.\n");
2186 /* No - did they specify a ring buffer option? */
2187 if (capture_opts.multi_files_on) {
2188 fprintf(stderr, "ethereal: Ring buffer requested, but a capture is not being done.\n");
2192 /* We're supposed to do a live capture; did the user also specify
2193 a capture file to be read? */
2194 if (start_capture && cf_name) {
2195 /* Yes - that's bogus. */
2196 fprintf(stderr, "ethereal: You cannot specify both a live capture and a capture file to be read.\n");
2200 /* No - was the ring buffer option specified and, if so, does it make
2202 if (capture_opts.multi_files_on) {
2203 /* Ring buffer works only under certain conditions:
2204 a) ring buffer does not work with temporary files;
2205 b) sync_mode and capture_opts.ringbuffer_on are mutually exclusive -
2206 sync_mode takes precedence;
2207 c) it makes no sense to enable the ring buffer if the maximum
2208 file size is set to "infinite". */
2209 if (save_file == NULL) {
2210 fprintf(stderr, "ethereal: Ring buffer requested, but capture isn't being saved to a permanent file.\n");
2211 capture_opts.multi_files_on = FALSE;
2213 if (capture_opts.sync_mode) {
2214 fprintf(stderr, "ethereal: Ring buffer requested, but an \"Update list of packets in real time\" capture is being done.\n");
2215 capture_opts.multi_files_on = FALSE;
2217 if (!capture_opts.has_autostop_filesize) {
2218 fprintf(stderr, "ethereal: Ring buffer requested, but no maximum capture file size was specified.\n");
2219 capture_opts.multi_files_on = FALSE;
2224 if (start_capture || list_link_layer_types) {
2225 /* Did the user specify an interface to use? */
2226 if (cfile.iface == NULL) {
2227 /* No - is a default specified in the preferences file? */
2228 if (prefs->capture_device != NULL) {
2230 cfile.iface = g_strdup(prefs->capture_device);
2232 /* No - pick the first one from the list of interfaces. */
2233 if_list = get_interface_list(&err, err_str);
2234 if (if_list == NULL) {
2237 case CANT_GET_INTERFACE_LIST:
2238 cant_get_if_list_errstr = cant_get_if_list_error_message(err_str);
2239 fprintf(stderr, "%s\n", cant_get_if_list_errstr);
2240 g_free(cant_get_if_list_errstr);
2243 case NO_INTERFACES_FOUND:
2244 fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
2249 if_info = if_list->data; /* first interface */
2250 cfile.iface = g_strdup(if_info->name);
2251 free_interface_list(if_list);
2256 if (capture_child) {
2257 if (cfile.save_file_fd == -1) {
2258 /* XXX - send this to the standard output as something our parent
2259 should put in an error message box? */
2260 fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
2265 if (list_link_layer_types) {
2266 /* Get the list of link-layer types for the capture device. */
2267 lt_list = get_pcap_linktype_list(cfile.iface, err_str);
2268 if (lt_list == NULL) {
2269 if (err_str[0] != '\0') {
2270 fprintf(stderr, "ethereal: The list of data link types for the capture device could not be obtained (%s).\n"
2271 "Please check to make sure you have sufficient permissions, and that\n"
2272 "you have the proper interface or pipe specified.\n", err_str);
2274 fprintf(stderr, "ethereal: The capture device has no data link types.\n");
2277 fprintf(stderr, "Data link types (use option -y to set):\n");
2278 for (lt_entry = lt_list; lt_entry != NULL;
2279 lt_entry = g_list_next(lt_entry)) {
2280 data_link_info = lt_entry->data;
2281 fprintf(stderr, " %s", data_link_info->name);
2282 if (data_link_info->description != NULL)
2283 fprintf(stderr, " (%s)", data_link_info->description);
2285 fprintf(stderr, " (not supported)");
2288 free_pcap_linktype_list(lt_list);
2294 /* Notify all registered modules that have had any of their preferences
2295 changed either from one of the preferences file or from the command
2296 line that their preferences have changed. */
2299 /* disabled protocols as per configuration file */
2300 if (gdp_path == NULL && dp_path == NULL) {
2301 set_disabled_protos_list();
2304 /* Build the column format array */
2305 col_setup(&cfile.cinfo, prefs->num_cols);
2306 for (i = 0; i < cfile.cinfo.num_cols; i++) {
2307 cfile.cinfo.col_fmt[i] = get_column_format(i);
2308 cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
2309 cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
2311 get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
2312 cfile.cinfo.col_data[i] = NULL;
2313 if (cfile.cinfo.col_fmt[i] == COL_INFO)
2314 cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
2316 cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2317 cfile.cinfo.col_fence[i] = 0;
2318 cfile.cinfo.col_expr[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2319 cfile.cinfo.col_expr_val[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2322 for (i = 0; i < cfile.cinfo.num_cols; i++) {
2325 for (j = 0; j < NUM_COL_FMTS; j++) {
2326 if (!cfile.cinfo.fmt_matx[i][j])
2329 if (cfile.cinfo.col_first[j] == -1)
2330 cfile.cinfo.col_first[j] = i;
2331 cfile.cinfo.col_last[j] = i;
2336 if (capture_opts.has_snaplen) {
2337 if (capture_opts.snaplen < 1)
2338 capture_opts.snaplen = WTAP_MAX_PACKET_SIZE;
2339 else if (capture_opts.snaplen < MIN_PACKET_SIZE)
2340 capture_opts.snaplen = MIN_PACKET_SIZE;
2343 /* Check the value range of the ringbuffer_num_files parameter */
2344 if (capture_opts.ring_num_files > RINGBUFFER_MAX_NUM_FILES)
2345 capture_opts.ring_num_files = RINGBUFFER_MAX_NUM_FILES;
2346 #if RINGBUFFER_MIN_NUM_FILES > 0
2347 else if (capture_opts.num_files < RINGBUFFER_MIN_NUM_FILES)
2348 capture_opts.ring_num_files = RINGBUFFER_MIN_NUM_FILES;
2352 /* read in rc file from global and personal configuration paths. */
2353 /* XXX - is this a good idea? */
2354 gtk_rc_parse(RC_FILE);
2355 rc_file = get_persconffile_path(RC_FILE, FALSE);
2356 gtk_rc_parse(rc_file);
2360 /* close the splash screen, as we are going to open the main window now */
2361 splash_destroy(splash_win);
2364 /* Is this a "child" ethereal, which is only supposed to pop up a
2365 capture box to let us stop the capture, and run a capture
2366 to a file that our parent will read? */
2367 if (!capture_child) {
2369 /* No. Pop up the main window, and read in a capture file if
2371 create_main_window(pl_size, tv_size, bv_size, prefs);
2373 /* Read the recent file, as we have the gui now ready for it. */
2374 read_recent(&rf_path, &rf_open_errno);
2376 /* rearrange all the widgets as we now have the recent settings for this */
2377 main_widgets_rearrange();
2379 /* Fill in column titles. This must be done after the top level window
2382 XXX - is that still true, with fixed-width columns? */
2383 packet_list_set_column_titles();
2385 menu_recent_read_finished();
2387 switch (user_font_apply()) {
2390 case FA_FONT_NOT_RESIZEABLE:
2391 /* "user_font_apply()" popped up an alert box. */
2392 /* turn off zooming - font can't be resized */
2393 case FA_FONT_NOT_AVAILABLE:
2394 /* XXX - did we successfully load the un-zoomed version earlier?
2395 If so, this *probably* means the font is available, but not at
2396 this particular zoom level, but perhaps some other failure
2397 occurred; I'm not sure you can determine which is the case,
2399 /* turn off zooming - zoom level is unavailable */
2401 /* in any other case than FA_SUCCESS, turn off zooming */
2402 recent.gui_zoom_level = 0;
2403 /* XXX: would it be a good idea to disable zooming (insensitive GUI)? */
2406 dnd_init(top_level);
2412 /* the window can be sized only, if it's not already shown, so do it now! */
2413 main_load_window_geometry(top_level);
2415 /*** we have finished all init things, show the main window ***/
2416 gtk_widget_show(top_level);
2418 /* the window can be maximized only, if it's visible, so do it after show! */
2419 main_load_window_geometry(top_level);
2421 /* process all pending GUI events before continue */
2422 while (gtk_events_pending()) gtk_main_iteration();
2424 /* Pop up any queued-up alert boxes. */
2425 display_queued_messages();
2427 /* If we were given the name of a capture file, read it in now;
2428 we defer it until now, so that, if we can't open it, and pop
2429 up an alert box, the alert box is more likely to come up on
2430 top of the main window - but before the preference-file-error
2431 alert box, so, if we get one of those, it's more likely to come
2434 if (rfilter != NULL) {
2435 if (!dfilter_compile(rfilter, &rfcode)) {
2436 bad_dfilter_alert_box(rfilter);
2437 rfilter_parse_failed = TRUE;
2440 if (!rfilter_parse_failed) {
2441 if ((err = cf_open(cf_name, FALSE, &cfile)) == 0) {
2442 /* "cf_open()" succeeded, so it closed the previous
2443 capture file, and thus destroyed any previous read filter
2444 attached to "cf". */
2445 cfile.rfcode = rfcode;
2447 /* Open tap windows; we do so after creating the main window,
2448 to avoid GTK warnings, and after successfully opening the
2449 capture file, so we know we have something to tap. */
2450 if (tap_opt && tli) {
2451 (*tli->func)(tap_opt);
2455 /* Read the capture file. */
2456 switch (cf_read(&cfile)) {
2460 /* Just because we got an error, that doesn't mean we were unable
2461 to read any of the file; we handle what we could get from the
2470 /* Save the name of the containing directory specified in the
2471 path name, if any; we can write over cf_name, which is a
2472 good thing, given that "get_dirname()" does write over its
2474 s = get_dirname(cf_name);
2475 /* we might already set this from the recent file, don't overwrite this */
2476 if(get_last_open_dir() == NULL)
2477 set_last_open_dir(s);
2482 dfilter_free(rfcode);
2483 cfile.rfcode = NULL;
2488 if (start_capture) {
2489 /* "-k" was specified; start a capture. */
2490 if (do_capture(save_file)) {
2491 /* The capture started. Open tap windows; we do so after creating
2492 the main window, to avoid GTK warnings, and after starting the
2493 capture, so we know we have something to tap. */
2494 if (tap_opt && tli) {
2495 (*tli->func)(tap_opt);
2499 if (save_file != NULL) {
2500 /* Save the directory name for future file dialogs. */
2501 s = get_dirname(save_file); /* Overwrites save_file */
2502 set_last_open_dir(s);
2508 set_menus_for_capture_in_progress(FALSE);
2511 /* This is the child process for a sync mode or fork mode capture,
2512 so just do the low-level work of a capture - don't create
2513 a temporary file and fork off *another* child process (so don't
2514 call "do_capture()"). */
2516 /* Pop up any queued-up alert boxes. */
2517 display_queued_messages();
2519 /* XXX - hand these stats to the parent process */
2520 capture(&stats_known, &stats);
2522 /* The capture is done; there's nothing more for us to do. */
2525 if (!start_capture && (cfile.cfilter == NULL || strlen(cfile.cfilter) == 0)) {
2526 if (cfile.cfilter) {
2527 g_free(cfile.cfilter);
2529 cfile.cfilter = g_strdup(get_conn_cfilter());
2531 #else /* HAVE_LIBPCAP */
2532 set_menus_for_capture_in_progress(FALSE);
2533 #endif /* HAVE_LIBPCAP */
2541 /* Shutdown windows sockets */
2544 /* For some unknown reason, the "atexit()" call in "create_console()"
2545 doesn't arrange that "destroy_console()" be called when we exit,
2546 so we call it here if a console was created. */
2552 /* This isn't reached, but we need it to keep GCC from complaining
2553 that "main()" returns without returning a value - it knows that
2554 "exit()" never returns, but it doesn't know that "gtk_exit()"
2555 doesn't, as GTK+ doesn't declare it with the attribute
2557 return 0; /* not reached */
2562 /* We build this as a GUI subsystem application on Win32, so
2563 "WinMain()", not "main()", gets called.
2565 Hack shamelessly stolen from the Win32 port of the GIMP. */
2567 #define _stdcall __attribute__((stdcall))
2571 WinMain (struct HINSTANCE__ *hInstance,
2572 struct HINSTANCE__ *hPrevInstance,
2576 has_console = FALSE;
2577 return main (__argc, __argv);
2581 * If this application has no console window to which its standard output
2582 * would go, create one.
2585 create_console(void)
2587 if (!has_console && prefs.gui_console_open != console_open_never) {
2588 /* We have no console to which to print the version string, so
2589 create one and make it the standard input, output, and error. */
2590 if (!AllocConsole())
2591 return; /* couldn't create console */
2592 freopen("CONIN$", "r", stdin);
2593 freopen("CONOUT$", "w", stdout);
2594 freopen("CONOUT$", "w", stderr);
2596 /* Well, we have a console now. */
2599 /* Now register "destroy_console()" as a routine to be called just
2600 before the application exits, so that we can destroy the console
2601 after the user has typed a key (so that the console doesn't just
2602 disappear out from under them, giving the user no chance to see
2603 the message(s) we put in there). */
2604 atexit(destroy_console);
2609 destroy_console(void)
2612 printf("\n\nPress any key to exit\n");
2618 /* This routine should not be necessary, at least as I read the GLib
2619 source code, as it looks as if GLib is, on Win32, *supposed* to
2620 create a console window into which to display its output.
2622 That doesn't happen, however. I suspect there's something completely
2623 broken about that code in GLib-for-Win32, and that it may be related
2624 to the breakage that forces us to just call "printf()" on the message
2625 rather than passing the message on to "g_log_default_handler()"
2626 (which is the routine that does the aforementioned non-functional
2627 console window creation). */
2629 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
2630 const char *message, gpointer user_data)
2634 /* For some unknown reason, the above doesn't appear to actually cause
2635 anything to be sent to the standard output, so we'll just splat the
2636 message out directly, just to make sure it gets out. */
2637 printf("%s\n", message);
2639 g_log_default_handler(log_domain, log_level, message, user_data);
2644 GtkWidget *info_bar_new(void)
2646 /* tip: tooltips don't work on statusbars! */
2647 info_bar = gtk_statusbar_new();
2648 main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
2649 file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
2650 help_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "help");
2651 #if GTK_MAJOR_VERSION >= 2
2652 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), FALSE);
2654 gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
2659 GtkWidget *packets_bar_new(void)
2661 /* tip: tooltips don't work on statusbars! */
2662 packets_bar = gtk_statusbar_new();
2663 packets_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(packets_bar), "packets");
2664 packets_bar_update();
2671 * Helper for main_widgets_rearrange()
2673 void foreach_remove_a_child(GtkWidget *widget, gpointer data) {
2674 gtk_container_remove(GTK_CONTAINER(data), widget);
2677 GtkWidget *main_widget_layout(gint layout_content)
2679 switch(layout_content) {
2680 case(layout_pane_content_none):
2683 case(layout_pane_content_plist):
2686 case(layout_pane_content_pdetails):
2689 case(layout_pane_content_pbytes):
2693 g_assert_not_reached();
2700 * Rearrange the main window widgets
2702 void main_widgets_rearrange(void) {
2703 GtkWidget *first_pane_widget1, *first_pane_widget2;
2704 GtkWidget *second_pane_widget1, *second_pane_widget2;
2705 gboolean split_top_left;
2707 /* be a bit faster */
2708 gtk_widget_hide(main_vbox);
2710 /* be sure, we don't loose a widget while rearranging */
2711 gtk_widget_ref(menubar);
2712 gtk_widget_ref(main_tb);
2713 gtk_widget_ref(filter_tb);
2714 gtk_widget_ref(pkt_scrollw);
2715 gtk_widget_ref(tv_scrollw);
2716 gtk_widget_ref(byte_nb_ptr);
2717 gtk_widget_ref(stat_hbox);
2718 gtk_widget_ref(info_bar);
2719 gtk_widget_ref(packets_bar);
2720 gtk_widget_ref(status_pane);
2721 gtk_widget_ref(main_pane_v1);
2722 gtk_widget_ref(main_pane_v2);
2723 gtk_widget_ref(main_pane_h1);
2724 gtk_widget_ref(main_pane_h2);
2726 /* empty all containers participating */
2727 gtk_container_foreach(GTK_CONTAINER(main_vbox), foreach_remove_a_child, main_vbox);
2728 gtk_container_foreach(GTK_CONTAINER(stat_hbox), foreach_remove_a_child, stat_hbox);
2729 gtk_container_foreach(GTK_CONTAINER(status_pane), foreach_remove_a_child, status_pane);
2730 gtk_container_foreach(GTK_CONTAINER(main_pane_v1), foreach_remove_a_child, main_pane_v1);
2731 gtk_container_foreach(GTK_CONTAINER(main_pane_v2), foreach_remove_a_child, main_pane_v2);
2732 gtk_container_foreach(GTK_CONTAINER(main_pane_h1), foreach_remove_a_child, main_pane_h1);
2733 gtk_container_foreach(GTK_CONTAINER(main_pane_h2), foreach_remove_a_child, main_pane_h2);
2735 /* add the menubar always at the top */
2736 gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
2739 gtk_box_pack_start(GTK_BOX(main_vbox), main_tb, FALSE, TRUE, 0);
2741 /* filter toolbar in toolbar area */
2742 if (!prefs.filter_toolbar_show_in_statusbar) {
2743 gtk_box_pack_start(GTK_BOX(main_vbox), filter_tb, FALSE, TRUE, 1);
2746 /* fill the main layout panes */
2747 switch(prefs.gui_layout_type) {
2748 case(layout_type_5):
2749 main_first_pane = main_pane_v1;
2750 main_second_pane = main_pane_v2;
2751 split_top_left = FALSE;
2753 case(layout_type_2):
2754 main_first_pane = main_pane_v1;
2755 main_second_pane = main_pane_h1;
2756 split_top_left = FALSE;
2758 case(layout_type_1):
2759 main_first_pane = main_pane_v1;
2760 main_second_pane = main_pane_h1;
2761 split_top_left = TRUE;
2763 case(layout_type_4):
2764 main_first_pane = main_pane_h1;
2765 main_second_pane = main_pane_v1;
2766 split_top_left = FALSE;
2768 case(layout_type_3):
2769 main_first_pane = main_pane_h1;
2770 main_second_pane = main_pane_v1;
2771 split_top_left = TRUE;
2773 case(layout_type_6):
2774 main_first_pane = main_pane_h1;
2775 main_second_pane = main_pane_h2;
2776 split_top_left = FALSE;
2779 main_first_pane = NULL;
2780 main_second_pane = NULL;
2781 split_top_left = FALSE;
2782 g_assert_not_reached();
2784 if (split_top_left) {
2785 first_pane_widget1 = main_second_pane;
2786 second_pane_widget1 = main_widget_layout(prefs.gui_layout_content_1);
2787 second_pane_widget2 = main_widget_layout(prefs.gui_layout_content_2);
2788 first_pane_widget2 = main_widget_layout(prefs.gui_layout_content_3);
2790 first_pane_widget1 = main_widget_layout(prefs.gui_layout_content_1);
2791 first_pane_widget2 = main_second_pane;
2792 second_pane_widget1 = main_widget_layout(prefs.gui_layout_content_2);
2793 second_pane_widget2 = main_widget_layout(prefs.gui_layout_content_3);
2795 if (first_pane_widget1 != NULL)
2796 gtk_paned_add1(GTK_PANED(main_first_pane), first_pane_widget1);
2797 if (first_pane_widget2 != NULL)
2798 gtk_paned_add2(GTK_PANED(main_first_pane), first_pane_widget2);
2799 if (second_pane_widget1 != NULL)
2800 gtk_paned_pack1(GTK_PANED(main_second_pane), second_pane_widget1, TRUE, TRUE);
2801 if (second_pane_widget2 != NULL)
2802 gtk_paned_pack2(GTK_PANED(main_second_pane), second_pane_widget2, FALSE, FALSE);
2804 gtk_container_add(GTK_CONTAINER(main_vbox), main_first_pane);
2806 /* statusbar hbox */
2807 gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
2809 /* filter toolbar in statusbar hbox */
2810 if (prefs.filter_toolbar_show_in_statusbar) {
2811 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_tb, FALSE, TRUE, 1);
2815 gtk_box_pack_start(GTK_BOX(stat_hbox), status_pane, TRUE, TRUE, 0);
2816 gtk_paned_pack1(GTK_PANED(status_pane), info_bar, FALSE, FALSE);
2817 gtk_paned_pack2(GTK_PANED(status_pane), packets_bar, FALSE, FALSE);
2819 /* hide widgets on users recent settings */
2820 main_widgets_show_or_hide();
2822 gtk_widget_show(main_vbox);
2826 is_widget_visible(GtkWidget *widget, gpointer data)
2828 gboolean *is_visible = data;
2831 if (GTK_WIDGET_VISIBLE(widget))
2837 * XXX - this doesn't appear to work with the paned widgets in
2838 * GTK+ 1.2[.x]; if you hide one of the panes, the splitter remains
2839 * and the other pane doesn't grow to take up the rest of the pane.
2840 * It does appear to work with GTK+ 2.x.
2843 main_widgets_show_or_hide(void)
2845 gboolean main_second_pane_show;
2847 if (recent.main_toolbar_show) {
2848 gtk_widget_show(main_tb);
2850 gtk_widget_hide(main_tb);
2854 * Show the status hbox if either:
2856 * 1) we're showing the filter toolbar and we want it in the status
2861 * 2) we're showing the status bar.
2863 if ((recent.filter_toolbar_show && prefs.filter_toolbar_show_in_statusbar) ||
2864 recent.statusbar_show) {
2865 gtk_widget_show(stat_hbox);
2867 gtk_widget_hide(stat_hbox);
2870 if (recent.statusbar_show) {
2871 gtk_widget_show(status_pane);
2873 gtk_widget_hide(status_pane);
2876 if (recent.filter_toolbar_show) {
2877 gtk_widget_show(filter_tb);
2879 gtk_widget_hide(filter_tb);
2882 if (recent.packet_list_show) {
2883 gtk_widget_show(pkt_scrollw);
2885 gtk_widget_hide(pkt_scrollw);
2888 if (recent.tree_view_show) {
2889 gtk_widget_show(tv_scrollw);
2891 gtk_widget_hide(tv_scrollw);
2894 if (recent.byte_view_show) {
2895 gtk_widget_show(byte_nb_ptr);
2897 gtk_widget_hide(byte_nb_ptr);
2901 * Is anything in "main_second_pane" visible?
2902 * If so, show it, otherwise hide it.
2904 main_second_pane_show = FALSE;
2905 gtk_container_foreach(GTK_CONTAINER(main_second_pane), is_widget_visible,
2906 &main_second_pane_show);
2907 if (main_second_pane_show) {
2908 gtk_widget_show(main_second_pane);
2910 gtk_widget_hide(main_second_pane);
2915 #if GTK_MAJOR_VERSION >= 2
2916 /* called, when the window state changes (minimized, maximized, ...) */
2918 window_state_event_cb (GtkWidget *widget _U_,
2922 GdkWindowState new_window_state = ((GdkEventWindowState*)event)->new_window_state;
2924 if( (event->type) == (GDK_WINDOW_STATE)) {
2925 if(!(new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
2926 /* we might have dialogs popped up while we where iconified,
2928 display_queued_messages();
2937 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
2940 *filter_bt, *filter_cm, *filter_te,
2941 *filter_add_expr_bt,
2944 GList *filter_list = NULL;
2945 GtkTooltips *tooltips;
2946 GtkAccelGroup *accel;
2947 /* Display filter construct dialog has an Apply button, and "OK" not
2948 only sets our text widget, it activates it (i.e., it causes us to
2949 filter the capture). */
2950 static construct_args_t args = {
2951 "Ethereal: Display Filter",
2957 top_level = window_new(GTK_WINDOW_TOPLEVEL, "The Ethereal Network Analyzer");
2959 tooltips = gtk_tooltips_new();
2962 #if GTK_MAJOR_VERSION < 2
2963 /* has to be done, after top_level window is created */
2964 app_font_gtk1_init(top_level);
2968 gtk_widget_set_name(top_level, "main window");
2969 SIGNAL_CONNECT(top_level, "delete_event", main_window_delete_event_cb,
2971 #if GTK_MAJOR_VERSION >= 2
2972 SIGNAL_CONNECT(GTK_OBJECT(top_level), "window_state_event",
2973 G_CALLBACK (window_state_event_cb), NULL);
2976 gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
2978 /* Container for menu bar, toolbar(s), paned windows and progress/info box */
2979 main_vbox = gtk_vbox_new(FALSE, 1);
2980 gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
2981 gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
2982 gtk_widget_show(main_vbox);
2985 menubar = main_menu_new(&accel);
2986 gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
2987 gtk_widget_show(menubar);
2990 main_tb = toolbar_new();
2991 gtk_widget_show (main_tb);
2994 pkt_scrollw = packet_list_new(prefs);
2995 WIDGET_SET_SIZE(packet_list, -1, pl_size);
2996 gtk_widget_show(pkt_scrollw);
2999 tv_scrollw = main_tree_view_new(prefs, &tree_view);
3000 WIDGET_SET_SIZE(tv_scrollw, -1, tv_size);
3001 gtk_widget_show(tv_scrollw);
3003 #if GTK_MAJOR_VERSION < 2
3004 SIGNAL_CONNECT(tree_view, "tree-select-row", tree_view_select_row_cb, NULL);
3005 SIGNAL_CONNECT(tree_view, "tree-unselect-row", tree_view_unselect_row_cb,
3008 SIGNAL_CONNECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)),
3009 "changed", tree_view_selection_changed_cb, NULL);
3011 SIGNAL_CONNECT(tree_view, "button_press_event", popup_menu_handler,
3012 OBJECT_GET_DATA(popup_menu_object, PM_TREE_VIEW_KEY));
3013 gtk_widget_show(tree_view);
3016 byte_nb_ptr = byte_view_new();
3017 WIDGET_SET_SIZE(byte_nb_ptr, -1, bv_size);
3018 gtk_widget_show(byte_nb_ptr);
3020 SIGNAL_CONNECT(byte_nb_ptr, "button_press_event", popup_menu_handler,
3021 OBJECT_GET_DATA(popup_menu_object, PM_HEXDUMP_KEY));
3024 /* Panes for the packet list, tree, and byte view */
3025 main_pane_v1 = gtk_vpaned_new();
3026 gtk_widget_show(main_pane_v1);
3027 main_pane_v2 = gtk_vpaned_new();
3028 gtk_widget_show(main_pane_v2);
3029 main_pane_h1 = gtk_hpaned_new();
3030 gtk_widget_show(main_pane_h1);
3031 main_pane_h2 = gtk_hpaned_new();
3032 gtk_widget_show(main_pane_h2);
3034 /* filter toolbar */
3035 #if GTK_MAJOR_VERSION < 2
3036 filter_tb = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
3039 filter_tb = gtk_toolbar_new();
3040 gtk_toolbar_set_orientation(GTK_TOOLBAR(filter_tb),
3041 GTK_ORIENTATION_HORIZONTAL);
3042 #endif /* GTK_MAJOR_VERSION */
3043 gtk_widget_show(filter_tb);
3045 /* Create the "Filter:" button */
3046 filter_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_DISPLAY_FILTER_ENTRY);
3047 SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
3048 gtk_widget_show(filter_bt);
3049 OBJECT_SET_DATA(top_level, E_FILT_BT_PTR_KEY, filter_bt);
3051 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_bt,
3052 "Open the \"Display Filter\" dialog, to edit/apply filters", "Private");
3054 /* Create the filter combobox */
3055 filter_cm = gtk_combo_new();
3057 gtk_combo_disable_activate(GTK_COMBO(filter_cm));
3058 gtk_combo_set_case_sensitive(GTK_COMBO(filter_cm), TRUE);
3059 OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
3060 filter_te = GTK_COMBO(filter_cm)->entry;
3061 main_display_filter_widget=filter_te;
3062 OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_te);
3063 OBJECT_SET_DATA(filter_te, E_DFILTER_CM_KEY, filter_cm);
3064 OBJECT_SET_DATA(top_level, E_DFILTER_CM_KEY, filter_cm);
3065 SIGNAL_CONNECT(filter_te, "activate", filter_activate_cb, filter_te);
3066 SIGNAL_CONNECT(filter_te, "changed", filter_te_syntax_check_cb, NULL);
3067 WIDGET_SET_SIZE(filter_cm, 400, -1);
3068 gtk_widget_show(filter_cm);
3069 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_cm,
3071 /* setting a tooltip for a combobox will do nothing, so add it to the corresponding text entry */
3072 gtk_tooltips_set_tip(tooltips, filter_te,
3073 "Enter a display filter, or choose one of your recently used filters. "
3074 "The background color of this field is changed by a continuous syntax check (green is valid, red is invalid).",
3077 /* Create the "Add Expression..." button, to pop up a dialog
3078 for constructing filter comparison expressions. */
3079 filter_add_expr_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_ADD_EXPRESSION);
3080 OBJECT_SET_DATA(filter_tb, E_FILT_FILTER_TE_KEY, filter_te);
3081 SIGNAL_CONNECT(filter_add_expr_bt, "clicked", filter_add_expr_bt_cb, filter_tb);
3082 gtk_widget_show(filter_add_expr_bt);
3083 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_add_expr_bt,
3084 "Add an expression to this filter string", "Private");
3086 /* Create the "Clear" button */
3087 filter_reset = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLEAR);
3088 OBJECT_SET_DATA(filter_reset, E_DFILTER_TE_KEY, filter_te);
3089 SIGNAL_CONNECT(filter_reset, "clicked", filter_reset_cb, NULL);
3090 gtk_widget_show(filter_reset);
3091 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_reset,
3092 "Clear this filter string and update the display", "Private");
3094 /* Create the "Apply" button */
3095 filter_apply = BUTTON_NEW_FROM_STOCK(GTK_STOCK_APPLY);
3096 OBJECT_SET_DATA(filter_apply, E_DFILTER_CM_KEY, filter_cm);
3097 SIGNAL_CONNECT(filter_apply, "clicked", filter_activate_cb, filter_te);
3098 gtk_widget_show(filter_apply);
3099 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_apply,
3100 "Apply this filter string to the display", "Private");
3102 /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
3103 * of any widget that ends up calling a callback which needs
3104 * that text entry pointer */
3105 set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
3106 set_menu_object_data("/Analyze/Display Filters...", E_FILT_TE_PTR_KEY,
3108 set_menu_object_data("/Analyze/Follow TCP Stream", E_DFILTER_TE_KEY,
3110 set_menu_object_data("/Analyze/Apply as Filter/Selected", E_DFILTER_TE_KEY,
3112 set_menu_object_data("/Analyze/Apply as Filter/Not Selected", E_DFILTER_TE_KEY,
3114 set_menu_object_data("/Analyze/Apply as Filter/... and Selected", E_DFILTER_TE_KEY,
3116 set_menu_object_data("/Analyze/Apply as Filter/... or Selected", E_DFILTER_TE_KEY,
3118 set_menu_object_data("/Analyze/Apply as Filter/... and not Selected", E_DFILTER_TE_KEY,
3120 set_menu_object_data("/Analyze/Apply as Filter/... or not Selected", E_DFILTER_TE_KEY,
3122 set_menu_object_data("/Analyze/Prepare a Filter/Selected", E_DFILTER_TE_KEY,
3124 set_menu_object_data("/Analyze/Prepare a Filter/Not Selected", E_DFILTER_TE_KEY,
3126 set_menu_object_data("/Analyze/Prepare a Filter/... and Selected", E_DFILTER_TE_KEY,
3128 set_menu_object_data("/Analyze/Prepare a Filter/... or Selected", E_DFILTER_TE_KEY,
3130 set_menu_object_data("/Analyze/Prepare a Filter/... and not Selected", E_DFILTER_TE_KEY,
3132 set_menu_object_data("/Analyze/Prepare a Filter/... or not Selected", E_DFILTER_TE_KEY,
3134 set_toolbar_object_data(E_DFILTER_TE_KEY, filter_te);
3135 OBJECT_SET_DATA(popup_menu_object, E_DFILTER_TE_KEY, filter_te);
3136 OBJECT_SET_DATA(popup_menu_object, E_MPACKET_LIST_KEY, packet_list);
3138 /* info (main) statusbar */
3139 info_bar = info_bar_new();
3140 gtk_widget_show(info_bar);
3142 /* packets statusbar */
3143 packets_bar = packets_bar_new();
3144 gtk_widget_show(packets_bar);
3146 /* Filter/status hbox */
3147 stat_hbox = gtk_hbox_new(FALSE, 1);
3148 gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
3149 gtk_widget_show(stat_hbox);
3151 /* Pane for the statusbar */
3152 status_pane = gtk_hpaned_new();
3153 gtk_widget_show(status_pane);