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"
89 #include "filter_dlg.h"
90 #include "layout_prefs.h"
92 #include "color_filters.h"
94 #include "simple_dialog.h"
96 #include "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"
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 "decode_as_dlg.h"
140 * File under personal preferences directory in which GTK settings for
141 * Ethereal are stored.
143 #define RC_FILE "gtkrc"
146 #define DEF_READY_MESSAGE " Ready to load or capture"
148 #define DEF_READY_MESSAGE " Ready to load file"
152 GtkWidget *main_display_filter_widget=NULL;
153 GtkWidget *top_level = NULL, *tree_view, *byte_nb_ptr, *tv_scrollw;
154 static GtkWidget *main_pane_v1, *main_pane_v2, *main_pane_h1, *main_pane_h2;
155 static GtkWidget *main_first_pane, *main_second_pane;
156 static GtkWidget *status_pane;
157 static GtkWidget *menubar, *main_vbox, *main_tb, *pkt_scrollw, *stat_hbox, *filter_tb;
158 static GtkWidget *info_bar;
159 static GtkWidget *packets_bar = NULL;
160 static guint main_ctx, file_ctx, help_ctx;
161 static guint packets_ctx;
162 static gchar *packets_str = NULL;
163 GString *comp_info_str, *runtime_info_str;
164 gchar *ethereal_path = NULL;
167 static gboolean has_console; /* TRUE if app has console */
168 /*static void create_console(void);*/
169 static void destroy_console(void);
170 static void console_log_handler(const char *log_domain,
171 GLogLevelFlags log_level, const char *message, gpointer user_data);
175 static gboolean list_link_layer_types;
178 static void create_main_window(gint, gint, gint, e_prefs*);
179 static void file_quit_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_);
180 static void main_save_window_geometry(GtkWidget *widget);
182 #define E_DFILTER_CM_KEY "display_filter_combo"
183 #define E_DFILTER_FL_KEY "display_filter_list"
187 /* Match selected byte pattern */
189 match_selected_cb_do(gpointer data, int action, gchar *text)
191 GtkWidget *filter_te;
192 char *cur_filter, *new_filter;
197 filter_te = OBJECT_GET_DATA(data, E_DFILTER_TE_KEY);
200 cur_filter = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, -1);
202 switch (action&MATCH_SELECTED_MASK) {
204 case MATCH_SELECTED_REPLACE:
205 new_filter = g_strdup(text);
208 case MATCH_SELECTED_AND:
209 if ((!cur_filter) || (0 == strlen(cur_filter)))
210 new_filter = g_strdup(text);
212 new_filter = g_strconcat("(", cur_filter, ") && (", text, ")", NULL);
215 case MATCH_SELECTED_OR:
216 if ((!cur_filter) || (0 == strlen(cur_filter)))
217 new_filter = g_strdup(text);
219 new_filter = g_strconcat("(", cur_filter, ") || (", text, ")", NULL);
222 case MATCH_SELECTED_NOT:
223 new_filter = g_strconcat("!(", text, ")", NULL);
226 case MATCH_SELECTED_AND_NOT:
227 if ((!cur_filter) || (0 == strlen(cur_filter)))
228 new_filter = g_strconcat("!(", text, ")", NULL);
230 new_filter = g_strconcat("(", cur_filter, ") && !(", text, ")", NULL);
233 case MATCH_SELECTED_OR_NOT:
234 if ((!cur_filter) || (0 == strlen(cur_filter)))
235 new_filter = g_strconcat("!(", text, ")", NULL);
237 new_filter = g_strconcat("(", cur_filter, ") || !(", text, ")", NULL);
241 g_assert_not_reached();
246 /* Free up the copy we got of the old filter text. */
249 /* create a new one and set the display filter entry accordingly */
250 gtk_entry_set_text(GTK_ENTRY(filter_te), new_filter);
252 /* Run the display filter so it goes in effect. */
253 if (action&MATCH_SELECTED_APPLY_NOW)
254 main_filter_packets(&cfile, new_filter, FALSE);
256 /* Free up the new filter text. */
259 /* Free up the generated text we were handed. */
264 match_selected_ptree_cb(GtkWidget *w, gpointer data, MATCH_SELECTED_E action)
266 if (cfile.finfo_selected)
267 match_selected_cb_do((data ? data : w),
269 proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt));
274 get_text_from_packet_list(gpointer data)
276 gint row = GPOINTER_TO_INT(OBJECT_GET_DATA(data, E_MPACKET_LIST_ROW_KEY));
277 gint column = GPOINTER_TO_INT(OBJECT_GET_DATA(data, E_MPACKET_LIST_COL_KEY));
278 frame_data *fdata = (frame_data *)packet_list_get_row_data(row);
286 if (!wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header,
287 cfile.pd, fdata->cap_len, &err, &err_info)) {
288 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
289 cf_read_error_message(err, err_info), cfile.filename);
293 edt = epan_dissect_new(FALSE, FALSE);
294 epan_dissect_run(edt, &cfile.pseudo_header, cfile.pd, fdata,
296 epan_dissect_fill_in_columns(edt);
298 if (strlen(cfile.cinfo.col_expr[column]) != 0 &&
299 strlen(cfile.cinfo.col_expr_val[column]) != 0) {
300 len = strlen(cfile.cinfo.col_expr[column]) +
301 strlen(cfile.cinfo.col_expr_val[column]) + 5;
302 buf = g_malloc0(len);
303 g_snprintf(buf, len, "%s == %s", cfile.cinfo.col_expr[column],
304 cfile.cinfo.col_expr_val[column]);
307 epan_dissect_free(edt);
314 match_selected_plist_cb(GtkWidget *w _U_, gpointer data, MATCH_SELECTED_E action)
316 match_selected_cb_do(data,
318 get_text_from_packet_list(data));
323 /* XXX: use a preference for this setting! */
324 static guint dfilter_combo_max_recent = 10;
326 /* add a display filter to the combo box */
327 /* Note: a new filter string will replace an old identical one */
329 dfilter_combo_add(GtkWidget *filter_cm, char *s) {
331 GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
334 /* GtkCombos don't let us get at their list contents easily, so we maintain
335 our own filter list, and feed it to gtk_combo_set_popdown_strings when
336 a new filter is added. */
337 li = g_list_first(filter_list);
339 /* If the filter is already in the list, remove the old one and
340 * append the new one at the latest position (at g_list_append() below) */
341 if (li->data && strcmp(s, li->data) == 0) {
342 filter_list = g_list_remove(filter_list, li->data);
348 filter_list = g_list_append(filter_list, s);
349 OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
350 gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
351 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(filter_cm)->entry), g_list_last(filter_list)->data);
357 /* write all non empty display filters (until maximum count)
358 * of the combo box GList to the user's recent file */
360 dfilter_recent_combo_write_all(FILE *rf) {
361 GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
362 GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
367 /* write all non empty display filter strings to the recent file (until max count) */
368 li = g_list_first(filter_list);
369 while ( li && (max_count++ <= dfilter_combo_max_recent) ) {
370 if (strlen(li->data)) {
371 fprintf (rf, RECENT_KEY_DISPLAY_FILTER ": %s\n", (char *)li->data);
377 /* empty the combobox entry field */
379 dfilter_combo_add_empty(void) {
380 GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
382 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(filter_cm)->entry), "");
386 /* add a display filter coming from the user's recent file to the dfilter combo box */
388 dfilter_combo_add_recent(gchar *s) {
389 GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
393 if (!dfilter_combo_add(filter_cm, dup)) {
402 /* call filter_packets() and add this filter string to the recent filter list */
404 main_filter_packets(capture_file *cf, const gchar *dftext, gboolean force)
406 GtkCombo *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
407 GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
409 gboolean add_filter = TRUE;
410 gboolean free_filter = TRUE;
412 gboolean filter_packets_ret;
414 s = g_strdup(dftext);
416 /* GtkCombos don't let us get at their list contents easily, so we maintain
417 our own filter list, and feed it to gtk_combo_set_popdown_strings when
418 a new filter is added. */
419 if ((filter_packets_ret = filter_packets(cf, s, force))) {
420 li = g_list_first(filter_list);
422 if (li->data && strcmp(s, li->data) == 0)
428 /* trim list size first */
429 while (g_list_length(filter_list) >= dfilter_combo_max_recent) {
430 filter_list = g_list_remove(filter_list, g_list_first(filter_list)->data);
434 filter_list = g_list_append(filter_list, s);
435 OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
436 gtk_combo_set_popdown_strings(filter_cm, filter_list);
437 gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
443 return filter_packets_ret;
447 /* Run the current display filter on the current packet set, and
450 filter_activate_cb(GtkWidget *w _U_, gpointer data)
454 s = gtk_entry_get_text(GTK_ENTRY(data));
456 main_filter_packets(&cfile, s, FALSE);
459 /* redisplay with no display filter */
461 filter_reset_cb(GtkWidget *w, gpointer data _U_)
463 GtkWidget *filter_te = NULL;
465 if ((filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY))) {
466 gtk_entry_set_text(GTK_ENTRY(filter_te), "");
468 main_filter_packets(&cfile, "", FALSE);
471 /* mark as reference time frame */
473 set_frame_reftime(gboolean set, frame_data *frame, gint row) {
477 frame->flags.ref_time=1;
479 frame->flags.ref_time=0;
481 reftime_packets(&cfile);
485 reftime_frame_cb(GtkWidget *w _U_, gpointer data _U_, REFTIME_ACTION_E action)
490 if (cfile.current_frame) {
491 /* XXX hum, should better have a "cfile->current_row" here ... */
492 set_frame_reftime(!cfile.current_frame->flags.ref_time,
494 packet_list_find_row_from_data(cfile.current_frame));
497 case REFTIME_FIND_NEXT:
498 find_previous_next_frame_with_filter("frame.ref_time", FALSE);
500 case REFTIME_FIND_PREV:
501 find_previous_next_frame_with_filter("frame.ref_time", TRUE);
506 #if GTK_MAJOR_VERSION < 2
508 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column _U_,
509 gpointer user_data _U_)
512 tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data _U_)
516 gchar *help_str = NULL;
517 gchar len_str[2+10+1+5+1]; /* ", {N} bytes\0",
519 gboolean has_blurb = FALSE;
520 guint length = 0, byte_len;
521 GtkWidget *byte_view;
522 const guint8 *byte_data;
523 #if GTK_MAJOR_VERSION >= 2
528 #if GTK_MAJOR_VERSION >= 2
529 /* if nothing is selected */
530 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
533 * Which byte view is displaying the current protocol tree
536 byte_view = get_notebook_bv_ptr(byte_nb_ptr);
537 if (byte_view == NULL)
540 byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
541 if (byte_data == NULL)
544 unselect_field(&cfile);
545 packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data,
546 cfile.current_frame, NULL, byte_len);
549 gtk_tree_model_get(model, &iter, 1, &finfo, -1);
552 finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
556 set_notebook_page(byte_nb_ptr, finfo->ds_tvb);
558 byte_view = get_notebook_bv_ptr(byte_nb_ptr);
559 byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
560 g_assert(byte_data != NULL);
562 cfile.finfo_selected = finfo;
563 set_menus_for_selected_tree_row(&cfile);
566 if (finfo->hfinfo->blurb != NULL &&
567 finfo->hfinfo->blurb[0] != '\0') {
569 length = strlen(finfo->hfinfo->blurb);
571 length = strlen(finfo->hfinfo->name);
573 if (finfo->length == 0) {
575 } else if (finfo->length == 1) {
576 strcpy (len_str, ", 1 byte");
578 g_snprintf (len_str, sizeof len_str, ", %d bytes", finfo->length);
580 statusbar_pop_field_msg(); /* get rid of current help msg */
582 help_str = g_strdup_printf("%s (%s)%s",
583 (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name,
584 finfo->hfinfo->abbrev, len_str);
585 statusbar_push_field_msg(help_str);
589 * Don't show anything if the field name is zero-length;
590 * the pseudo-field for "proto_tree_add_text()" is such
591 * a field, and we don't want "Text (text)" showing up
592 * on the status line if you've selected such a field.
594 * XXX - there are zero-length fields for which we *do*
595 * want to show the field name.
597 * XXX - perhaps the name and abbrev field should be null
598 * pointers rather than null strings for that pseudo-field,
599 * but we'd have to add checks for null pointers in some
600 * places if we did that.
602 * Or perhaps protocol tree items added with
603 * "proto_tree_add_text()" should have -1 as the field index,
604 * with no pseudo-field being used, but that might also
605 * require special checks for -1 to be added.
607 statusbar_push_field_msg("");
611 #if GTK_MAJOR_VERSION < 2
612 packet_hex_print(GTK_TEXT(byte_view), byte_data, cfile.current_frame,
615 packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data, cfile.current_frame,
620 #if GTK_MAJOR_VERSION < 2
622 tree_view_unselect_row_cb(GtkCTree *ctree _U_, GList *node _U_, gint column _U_,
623 gpointer user_data _U_)
625 GtkWidget *byte_view;
630 * Which byte view is displaying the current protocol tree
633 byte_view = get_notebook_bv_ptr(byte_nb_ptr);
634 if (byte_view == NULL)
637 data = get_byte_view_data_and_length(byte_view, &len);
641 unselect_field(&cfile);
642 packet_hex_print(GTK_TEXT(byte_view), data, cfile.current_frame,
647 void collapse_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
649 collapse_all_tree(cfile.edt->tree, tree_view);
652 void expand_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
654 expand_all_tree(cfile.edt->tree, tree_view);
657 void expand_tree_cb(GtkWidget *widget _U_, gpointer data _U_) {
658 #if GTK_MAJOR_VERSION < 2
664 #if GTK_MAJOR_VERSION < 2
665 node = gtk_ctree_find_by_row_data(GTK_CTREE(tree_view), NULL, cfile.finfo_selected);
667 gtk_ctree_expand_recursive(GTK_CTREE(tree_view), node);
669 path = tree_find_by_field_info(GTK_TREE_VIEW(tree_view), cfile.finfo_selected);
671 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view), path, TRUE);
672 gtk_tree_path_free(path);
676 void resolve_name_cb(GtkWidget *widget _U_, gpointer data _U_) {
677 if (cfile.edt->tree) {
678 guint32 tmp = g_resolv_flags;
679 g_resolv_flags = RESOLV_ALL;
680 proto_tree_draw(cfile.edt->tree, tree_view);
681 g_resolv_flags = tmp;
686 * Push a message referring to file access onto the statusbar.
689 statusbar_push_file_msg(gchar *msg)
691 gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg);
695 * Pop a message referring to file access off the statusbar.
698 statusbar_pop_file_msg(void)
700 gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
704 * XXX - do we need multiple statusbar contexts?
708 * Push a message referring to the currently-selected field onto the statusbar.
711 statusbar_push_field_msg(gchar *msg)
713 gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg);
717 * Pop a message referring to the currently-selected field off the statusbar.
720 statusbar_pop_field_msg(void)
722 gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
726 * update the packets statusbar to the current values
728 void packets_bar_update(void)
732 /* remove old status */
735 gtk_statusbar_pop(GTK_STATUSBAR(packets_bar), packets_ctx);
738 /* do we have any packets? */
740 packets_str = g_strdup_printf(" P: %u D: %u M: %u",
741 cfile.count, cfile.displayed_count, cfile.marked_count);
743 packets_str = g_strdup(" No Packets");
745 gtk_statusbar_push(GTK_STATUSBAR(packets_bar), packets_ctx, packets_str);
756 /* get the current geometry, before writing it to disk */
757 main_save_window_geometry(top_level);
759 /* write user's recent file to disk
760 * It is no problem to write this file, even if we do not quit */
761 write_recent(&rec_path);
763 /* XXX - should we check whether the capture file is an
764 unsaved temporary file for a live capture and, if so,
765 pop up a "do you want to exit without saving the capture
766 file?" dialog, and then just return, leaving said dialog
767 box to forcibly quit if the user clicks "OK"?
769 If so, note that this should be done in a subroutine that
770 returns TRUE if we do so, and FALSE otherwise, and if it
771 returns TRUE we should return TRUE without nuking anything.
773 Note that, if we do that, we might also want to check if
774 an "Update list of packets in real time" capture is in
775 progress and, if so, ask whether they want to terminate
776 the capture and discard it, and return TRUE, before nuking
777 any child capture, if they say they don't want to do so. */
780 /* Nuke any child capture in progress. */
781 kill_capture_child();
784 /* Are we in the middle of reading a capture? */
785 if (cfile.state == FILE_READ_IN_PROGRESS) {
786 /* Yes, so we can't just close the file and quit, as
787 that may yank the rug out from under the read in
788 progress; instead, just set the state to
789 "FILE_READ_ABORTED" and return - the code doing the read
790 will check for that and, if it sees that, will clean
792 cfile.state = FILE_READ_ABORTED;
794 /* Say that the window should *not* be deleted;
795 that'll be done by the code that cleans up. */
798 /* Close any capture file we have open; on some OSes, you
799 can't unlink a temporary capture file if you have it
801 "cf_close()" will unlink it after closing it if
802 it's a temporary file.
804 We do this here, rather than after the main loop returns,
805 as, after the main loop returns, the main window may have
806 been destroyed (if this is called due to a "destroy"
807 even on the main window rather than due to the user
808 selecting a menu item), and there may be a crash
809 or other problem when "cf_close()" tries to
810 clean up stuff in the main window.
812 XXX - is there a better place to put this?
813 Or should we have a routine that *just* closes the
814 capture file, and doesn't do anything with the UI,
815 which we'd call here, and another routine that
816 calls that routine and also cleans up the UI, which
817 we'd call elsewhere? */
820 /* Exit by leaving the main loop, so that any quit functions
821 we registered get called. */
824 /* Say that the window should be deleted. */
830 main_window_delete_event_cb(GtkWidget *widget _U_, GdkEvent *event _U_, gpointer data _U_)
834 if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
835 #if GTK_MAJOR_VERSION >= 2
836 gtk_window_present(GTK_WINDOW(top_level));
838 /* user didn't saved his current file, ask him */
839 dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
840 PRIMARY_TEXT_START "Save capture file before program quit?" PRIMARY_TEXT_END "\n\n"
841 "If you quit the program without saving, your capture data will be discarded.");
842 simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL);
845 /* unchanged file, just exit */
846 /* "main_do_quit()" indicates whether the main window should be deleted. */
847 return main_do_quit();
854 main_load_window_geometry(GtkWidget *widget)
856 window_geometry_t geom;
858 geom.set_pos = prefs.gui_geometry_save_position;
859 geom.x = recent.gui_geometry_main_x;
860 geom.y = recent.gui_geometry_main_y;
861 geom.set_size = prefs.gui_geometry_save_size;
862 if (recent.gui_geometry_main_width > 0 &&
863 recent.gui_geometry_main_height > 0) {
864 geom.width = recent.gui_geometry_main_width;
865 geom.height = recent.gui_geometry_main_height;
866 geom.set_maximized = prefs.gui_geometry_save_maximized;
868 /* We assume this means the width and height weren't set in
869 the "recent" file (or that there is no "recent" file),
870 and weren't set to a default value, so we don't set the
871 size. (The "recent" file code rejects non-positive width
872 and height values.) */
873 geom.set_size = FALSE;
875 geom.maximized = recent.gui_geometry_main_maximized;
877 window_set_geometry(widget, &geom);
879 #if GTK_MAJOR_VERSION >= 2
880 /* XXX - rename recent settings? */
881 if (recent.gui_geometry_main_upper_pane)
882 gtk_paned_set_position(GTK_PANED(main_first_pane), recent.gui_geometry_main_upper_pane);
883 if (recent.gui_geometry_main_lower_pane)
884 gtk_paned_set_position(GTK_PANED(main_second_pane), recent.gui_geometry_main_lower_pane);
885 if (recent.gui_geometry_status_pane)
886 gtk_paned_set_position(GTK_PANED(status_pane), recent.gui_geometry_status_pane);
892 main_save_window_geometry(GtkWidget *widget)
894 window_geometry_t geom;
896 window_get_geometry(widget, &geom);
898 if (prefs.gui_geometry_save_position) {
899 recent.gui_geometry_main_x = geom.x;
900 recent.gui_geometry_main_y = geom.y;
903 if (prefs.gui_geometry_save_size) {
904 recent.gui_geometry_main_width = geom.width,
905 recent.gui_geometry_main_height = geom.height;
908 #if GTK_MAJOR_VERSION >= 2
909 if(prefs.gui_geometry_save_maximized) {
910 recent.gui_geometry_main_maximized = geom.maximized;
913 recent.gui_geometry_main_upper_pane = gtk_paned_get_position(GTK_PANED(main_first_pane));
914 recent.gui_geometry_main_lower_pane = gtk_paned_get_position(GTK_PANED(main_second_pane));
915 recent.gui_geometry_status_pane = gtk_paned_get_position(GTK_PANED(status_pane));
919 static void file_quit_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
923 /* save file first */
924 file_save_as_cmd(after_save_exit, NULL);
926 case(ESD_BTN_DONT_SAVE):
929 case(ESD_BTN_CANCEL):
932 g_assert_not_reached();
937 file_quit_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
941 if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
942 /* user didn't saved his current file, ask him */
943 dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
944 PRIMARY_TEXT_START "Save capture file before program quit?" PRIMARY_TEXT_END "\n\n"
945 "If you quit the program without saving, your capture data will be discarded.");
946 simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL);
948 /* unchanged file, just exit */
954 print_usage(gboolean print_ver) {
960 fprintf(output, "This is GNU " PACKAGE " " VERSION
965 comp_info_str->str, runtime_info_str->str);
970 fprintf(output, "\n%s [ -vh ] [ -klLnpQS ] [ -a <capture autostop condition> ] ...\n",
972 fprintf(output, "\t[ -b <number of ringbuffer files>[:<duration>] ]\n");
973 fprintf(output, "\t[ -B <byte view height> ] [ -c <count> ] [ -f <capture filter> ]\n");
974 fprintf(output, "\t[ -i <interface> ] [ -m <medium font> ] [ -N <resolving> ]\n");
975 fprintf(output, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
976 fprintf(output, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
977 fprintf(output, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
978 fprintf(output, "\t[ -w <savefile> ] [ -y <link type> ] [ -z <statistics string> ]\n");
979 fprintf(output, "\t[ <infile> ]\n");
981 fprintf(output, "\n%s [ -vh ] [ -n ] [ -B <byte view height> ] [ -m <medium font> ]\n",
983 fprintf(output, "\t[ -N <resolving> ] [ -o <preference setting> ...\n");
984 fprintf(output, "\t[ -P <packet list height> ] [ -r <infile> ] [ -R <read filter> ]\n");
985 fprintf(output, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
986 fprintf(output, "\t[ -z <statistics string> ] [ <infile> ]\n");
997 printf(PACKAGE " " VERSION
1002 comp_info_str->str, runtime_info_str->str);
1006 get_natural_int(const char *string, const char *name)
1011 number = strtol(string, &p, 10);
1012 if (p == string || *p != '\0') {
1013 fprintf(stderr, "ethereal: The specified %s \"%s\" is not a decimal number\n",
1018 fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
1022 if (number > INT_MAX) {
1023 fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
1024 name, string, INT_MAX);
1031 get_positive_int(const char *string, const char *name)
1035 number = get_natural_int(string, name);
1038 fprintf(stderr, "ethereal: The specified %s is zero\n",
1048 * Given a string of the form "<autostop criterion>:<value>", as might appear
1049 * as an argument to a "-a" option, parse it and set the criterion in
1050 * question. Return an indication of whether it succeeded or failed
1054 set_autostop_criterion(const char *autostoparg)
1058 colonp = strchr(autostoparg, ':');
1066 * Skip over any white space (there probably won't be any, but
1067 * as we allow it in the preferences file, we might as well
1070 while (isspace((guchar)*p))
1074 * Put the colon back, so if our caller uses, in an
1075 * error message, the string they passed us, the message
1081 if (strcmp(autostoparg,"duration") == 0) {
1082 capture_opts.has_autostop_duration = TRUE;
1083 capture_opts.autostop_duration = get_positive_int(p,"autostop duration");
1084 } else if (strcmp(autostoparg,"filesize") == 0) {
1085 capture_opts.has_autostop_filesize = TRUE;
1086 capture_opts.autostop_filesize = get_positive_int(p,"autostop filesize");
1090 *colonp = ':'; /* put the colon back */
1095 * Given a string of the form "<ring buffer file>:<duration>", as might appear
1096 * as an argument to a "-b" option, parse it and set the arguments in
1097 * question. Return an indication of whether it succeeded or failed
1101 get_ring_arguments(const char *arg)
1103 gchar *p = NULL, *colonp;
1105 colonp = strchr(arg, ':');
1107 if (colonp != NULL) {
1112 capture_opts.ring_num_files =
1113 get_natural_int(arg, "number of ring buffer files");
1119 * Skip over any white space (there probably won't be any, but
1120 * as we allow it in the preferences file, we might as well
1123 while (isspace((guchar)*p))
1127 * Put the colon back, so if our caller uses, in an
1128 * error message, the string they passed us, the message
1135 capture_opts.has_file_duration = TRUE;
1136 capture_opts.file_duration = get_positive_int(p,
1137 "ring buffer duration");
1139 *colonp = ':'; /* put the colon back */
1144 #if defined(_WIN32) || GTK_MAJOR_VERSION < 2 || ! defined USE_THREADS
1146 Once every 3 seconds we get a callback here which we use to update
1147 the tap extensions. Since Gtk1 is single threaded we dont have to
1148 worry about any locking or critical regions.
1151 update_cb(gpointer data _U_)
1153 draw_tap_listeners(FALSE);
1158 /* if these three functions are copied to gtk1 ethereal, since gtk1 does not
1159 use threads all updte_thread_mutex can be dropped and protect/unprotect
1160 would just be empty functions.
1162 This allows gtk2-rpcstat.c and friends to be copied unmodified to
1163 gtk1-ethereal and it will just work.
1165 static GStaticMutex update_thread_mutex = G_STATIC_MUTEX_INIT;
1167 update_thread(gpointer data _U_)
1171 g_get_current_time(&tv1);
1172 g_static_mutex_lock(&update_thread_mutex);
1173 gdk_threads_enter();
1174 draw_tap_listeners(FALSE);
1175 gdk_threads_leave();
1176 g_static_mutex_unlock(&update_thread_mutex);
1178 g_get_current_time(&tv2);
1179 if( ((tv1.tv_sec + 2) * 1000000 + tv1.tv_usec) >
1180 (tv2.tv_sec * 1000000 + tv2.tv_usec) ){
1181 g_usleep(((tv1.tv_sec + 2) * 1000000 + tv1.tv_usec) -
1182 (tv2.tv_sec * 1000000 + tv2.tv_usec));
1189 protect_thread_critical_region(void)
1191 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2 && defined USE_THREADS
1192 g_static_mutex_lock(&update_thread_mutex);
1196 unprotect_thread_critical_region(void)
1198 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2 && defined USE_THREADS
1199 g_static_mutex_unlock(&update_thread_mutex);
1203 /* structure to keep track of what tap listeners have been registered.
1205 typedef struct _ethereal_tap_list {
1206 struct _ethereal_tap_list *next;
1208 void (*func)(char *arg);
1209 } ethereal_tap_list;
1210 static ethereal_tap_list *tap_list=NULL;
1213 register_ethereal_tap(char *cmd, void (*func)(char *arg))
1215 ethereal_tap_list *newtl;
1217 newtl=malloc(sizeof(ethereal_tap_list));
1218 newtl->next=tap_list;
1226 enum { DND_TARGET_STRING, DND_TARGET_ROOTWIN, DND_TARGET_URL };
1228 /* convert drag and drop URI to a local filename */
1230 dnd_uri2filename(gchar *cf_name)
1239 * Remove URI header.
1240 * On win32 (at least WinXP), this string looks like (UNC or local filename):
1241 * file:////servername/sharename/dir1/dir2/capture-file.cap
1243 * file:///d:/dir1/dir2/capture-file.cap
1244 * we have to remove the prefix to get a valid filename.
1246 * On UNIX (at least KDE 3.0 Konqueror), this string looks like:
1247 * file:/dir1/dir2/capture-file.cap
1248 * we have to remove the file: to get a valid filename.
1250 if (strncmp("file:////", cf_name, 9) == 0) {
1251 /* win32 UNC: now becoming: //servername/sharename/dir1/dir2/capture-file.cap */
1253 } else if (strncmp("file:///", cf_name, 8) == 0) {
1254 /* win32 local: now becoming: d:/dir1/dir2/capture-file.cap */
1256 } else if (strncmp("file:", cf_name, 5) == 0) {
1257 /* unix local: now becoming: /dir1/dir2/capture-file.cap */
1262 * unescape the escaped URI characters (spaces, ...)
1264 * we have to replace escaped chars to their equivalents,
1265 * e.g. %20 (always a two digit hexstring) -> ' '
1266 * the percent character '%' is escaped be a double one "%%"
1268 * we do this conversation "in place" as the result is always
1269 * equal or smaller in size.
1277 /* this is an escaped '%' char (was: "%%") */
1282 /* convert escaped hexnumber to unscaped character */
1286 ret = sscanf(esc, "%x", &i);
1292 /* somethings wrong, just jump over that char
1293 * this will result in a wrong string, but we might get
1294 * user feedback and can fix it later ;-) */
1310 dnd_merge_files(int in_file_count, char **in_filenames)
1315 char tmpname[128+1];
1318 out_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
1320 /* merge the files in chonological order */
1321 merge_ok = merge_n_files(out_fd, in_file_count, in_filenames, FALSE, &err);
1325 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1326 "An error occurred while merging the files: \"%s\".",
1327 wtap_strerror(err));
1334 /* Try to open the merged capture file. */
1335 if ((err = cf_open(tmpname, TRUE /* temporary file */, &cfile)) != 0) {
1336 /* We couldn't open it; don't dismiss the open dialog box,
1337 just leave it around so that the user can, after they
1338 dismiss the alert box popped up for the open error,
1343 switch (cf_read(&cfile)) {
1347 /* Just because we got an error, that doesn't mean we were unable
1348 to read any of the file; we handle what we could get from the
1353 /* The user bailed out of re-reading the capture file; the
1354 capture file has been closed - just free the capture file name
1355 string and return (without changing the last containing
1360 gtk_widget_grab_focus(packet_list);
1363 /* open/merge the dnd file */
1365 dnd_open_file_cmd(GtkSelectionData *selection_data)
1368 gchar *cf_name, *cf_name_freeme;
1371 GString *dialog_text;
1373 char **in_filenames;
1376 /* DND_TARGET_URL on Win32:
1377 * The selection_data->data is a single string, containing one or more URI's,
1378 * seperated by CR/NL chars. The length of the whole field can be found
1379 * in the selection_data->length field. If it contains one file, simply open it,
1380 * If it contains more than one file, ask to merge these files. */
1382 /* the data string is not zero terminated -> make a zero terminated "copy" of it */
1383 cf_name_freeme = g_malloc(selection_data->length + 1);
1384 memcpy(cf_name_freeme, selection_data->data, selection_data->length);
1385 cf_name_freeme[selection_data->length] = '\0';
1387 /* count the number of input files */
1388 cf_name = cf_name_freeme;
1389 for(in_files = 0; (cf_name = strstr(cf_name, "\r\n")) != NULL; ) {
1394 in_filenames = g_malloc(sizeof(char*) * in_files);
1396 /* store the starts of the file entries in a gchar array */
1397 cf_name = cf_name_freeme;
1398 in_filenames[0] = cf_name;
1399 for(files_work = 1; (cf_name = strstr(cf_name, "\r\n")) != NULL && files_work < in_files; ) {
1401 in_filenames[files_work] = cf_name;
1405 /* replace trailing CR NL simply with zeroes (in place), so we get valid terminated strings */
1406 cf_name = cf_name_freeme;
1407 g_strdelimit(cf_name, "\r\n", '\0');
1409 /* convert all filenames from URI to local filename (in place) */
1410 for(files_work = 0; files_work < in_files; files_work++) {
1411 in_filenames[files_work] = dnd_uri2filename(in_filenames[files_work]);
1416 /* shouldn't happen */
1419 /* open and read the capture file (this will close an existing file) */
1420 if ((err = cf_open(in_filenames[0], FALSE, &cfile)) == 0) {
1422 add_menu_recent_capture_file(in_filenames[0]);
1424 /* the capture file couldn't be read (doesn't exist, file format unknown, ...) */
1428 /* build and show the info dialog */
1429 dialog_text = g_string_sized_new(200);
1430 g_string_append(dialog_text, PRIMARY_TEXT_START
1431 "Merging the following files:" PRIMARY_TEXT_END "\n\n");
1432 for(files_work = 0; files_work < in_files; files_work++) {
1433 g_string_append(dialog_text, in_filenames[files_work]);
1434 g_string_append(dialog_text, "\n");
1436 g_string_append(dialog_text, "\nThe packets in these files will be merged chronologically into a new temporary file.");
1437 dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
1440 g_string_free(dialog_text, TRUE);
1442 /* actually merge the files now */
1443 dnd_merge_files(in_files, in_filenames);
1446 g_free(in_filenames);
1447 g_free(cf_name_freeme);
1450 /* ask the user to save current unsaved file, before opening the dnd file */
1452 dnd_save_file_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
1456 /* save file first */
1457 file_save_as_cmd(after_save_open_dnd_file, data);
1459 case(ESD_BTN_DONT_SAVE):
1461 dnd_open_file_cmd(data);
1463 case(ESD_BTN_CANCEL):
1466 g_assert_not_reached();
1471 /* we have received some drag and drop data */
1472 /* (as we only registered to "text/uri-list", we will only get a file list here) */
1474 dnd_data_received(GtkWidget *widget _U_, GdkDragContext *dc _U_, gint x _U_, gint y _U_,
1475 GtkSelectionData *selection_data, guint info, guint t _U_, gpointer data _U_)
1479 if (info == DND_TARGET_URL) {
1480 /* ask the user to save it's current capture file first */
1481 if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
1482 /* user didn't saved his current file, ask him */
1483 dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
1484 ESD_BTNS_SAVE_DONTSAVE_CANCEL,
1485 PRIMARY_TEXT_START "Save capture file before opening a new one?" PRIMARY_TEXT_END "\n\n"
1486 "If you open a new capture file without saving, your current capture data will be discarded.");
1487 simple_dialog_set_cb(dialog, dnd_save_file_answered_cb, selection_data);
1489 /* unchanged file */
1490 dnd_open_file_cmd(selection_data);
1495 /* init the drag and drop functionality */
1497 dnd_init(GtkWidget *w)
1499 /* we are only interested in the URI list containing filenames */
1500 static GtkTargetEntry target_entry[] = {
1501 /*{"STRING", 0, DND_TARGET_STRING},*/
1502 /*{"text/plain", 0, DND_TARGET_STRING},*/
1503 {"text/uri-list", 0, DND_TARGET_URL}
1506 /* set this window as a dnd destination */
1508 w, GTK_DEST_DEFAULT_ALL, target_entry,
1509 sizeof(target_entry) / sizeof(GtkTargetEntry),
1510 (GdkDragAction)(GDK_ACTION_MOVE | GDK_ACTION_COPY) );
1512 /* get notified, if some dnd coming in */
1513 gtk_signal_connect(GTK_OBJECT(w), "drag_data_received",
1514 GTK_SIGNAL_FUNC(dnd_data_received), NULL);
1518 /* And now our feature presentation... [ fade to music ] */
1520 main(int argc, char *argv[])
1528 extern char *optarg;
1529 gboolean arg_error = FALSE;
1537 char *gpf_path, *pf_path;
1538 char *cf_path, *df_path;
1539 char *gdp_path, *dp_path;
1540 int gpf_open_errno, gpf_read_errno;
1541 int pf_open_errno, pf_read_errno;
1542 int cf_open_errno, df_open_errno;
1543 int gdp_open_errno, gdp_read_errno;
1544 int dp_open_errno, dp_read_errno;
1547 gboolean start_capture = FALSE;
1548 gchar *save_file = NULL;
1551 GList *lt_list, *lt_entry;
1552 data_link_info_t *data_link_info;
1553 gchar err_str[PCAP_ERRBUF_SIZE];
1554 gchar *cant_get_if_list_errstr;
1555 gboolean stats_known;
1556 struct pcap_stat stats;
1558 gboolean capture_option_specified = FALSE;
1560 gint pl_size = 280, tv_size = 95, bv_size = 75;
1561 gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
1562 dfilter_t *rfcode = NULL;
1563 gboolean rfilter_parse_failed = FALSE;
1566 ethereal_tap_list *tli = NULL;
1567 gchar *tap_opt = NULL;
1568 GtkWidget *splash_win = NULL;
1570 #define OPTSTRING_INIT "a:b:B:c:f:Hhi:klLm:nN:o:pP:Qr:R:Ss:t:T:w:vy:z:"
1574 #define OPTSTRING_CHILD "W:Z:"
1576 #define OPTSTRING_CHILD "W:"
1579 #define OPTSTRING_CHILD ""
1580 #endif /* HAVE_LIBPCAP */
1582 char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_CHILD) - 1] =
1586 /* Set the current locale according to the program environment.
1587 * We haven't localized anything, but some GTK widgets are localized
1588 * (the file selection dialogue, for example).
1589 * This also sets the C-language locale to the native environment. */
1592 /* Let GTK get its args */
1593 gtk_init (&argc, &argv);
1596 ethereal_path = argv[0];
1599 /* Arrange that if we have no console window, and a GLib message logging
1600 routine is called to log a message, we pop up a console window.
1602 We do that by inserting our own handler for all messages logged
1603 to the default domain; that handler pops up a console if necessary,
1604 and then calls the default handler. */
1605 g_log_set_handler(NULL,
1607 G_LOG_LEVEL_CRITICAL|
1608 G_LOG_LEVEL_WARNING|
1609 G_LOG_LEVEL_MESSAGE|
1612 G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION,
1613 console_log_handler, NULL);
1616 command_name = get_basename(ethereal_path);
1617 /* Set "capture_child" to indicate whether this is going to be a child
1618 process for a "-S" capture. */
1619 capture_child = (strcmp(command_name, CHILD_NAME) == 0);
1620 if (capture_child) {
1621 strcat(optstring, OPTSTRING_CHILD);
1622 } else if (argc < 2 || strcmp(argv[1], "-G") != 0) {
1623 /* We want a splash screen only if we're not a child process */
1624 splash_win = splash_new("Loading Ethereal ...");
1628 splash_update(splash_win, "Registering dissectors ...");
1630 /* Register all dissectors; we must do this before checking for the
1631 "-G" flag, as the "-G" flag dumps information registered by the
1632 dissectors, and we must do it before we read the preferences, in
1633 case any dissectors register preferences. */
1634 epan_init(PLUGIN_DIR,register_all_protocols,register_all_protocol_handoffs,
1635 failure_alert_box,open_failure_alert_box,read_failure_alert_box);
1637 splash_update(splash_win, "Registering tap listeners ...");
1639 /* Register all tap listeners; we do this before we parse the arguments,
1640 as the "-z" argument can specify a registered tap. */
1641 register_all_tap_listeners();
1643 splash_update(splash_win, "Loading module preferences ...");
1645 /* Now register the preferences for any non-dissector modules.
1646 We must do that before we read the preferences as well. */
1647 prefs_register_modules();
1649 /* If invoked with the "-G" flag, we dump out information based on
1650 the argument to the "-G" flag; if no argument is specified,
1651 for backwards compatibility we dump out a glossary of display
1654 We must do this before calling "gtk_init()", because "gtk_init()"
1655 tries to open an X display, and we don't want to have to do any X
1656 stuff just to do a build.
1658 Given that we call "gtk_init()" before doing the regular argument
1659 list processing, so that it can handle X and GTK+ arguments and
1660 remove them from the list at which we look, this means we must do
1661 this before doing the regular argument list processing, as well.
1665 you must give the "-G" flag as the first flag on the command line;
1667 you must give it as "-G", nothing more, nothing less;
1669 the first argument after the "-G" flag, if present, will be used
1670 to specify the information to dump;
1672 arguments after that will not be used. */
1673 handle_dashG_option(argc, argv, "ethereal");
1675 /* multithread support currently doesn't seem to work in win32 gtk2.0.6 */
1676 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2 && defined(G_THREADS_ENABLED) && defined USE_THREADS
1679 g_thread_init(NULL);
1681 ut=g_thread_create(update_thread, NULL, FALSE, NULL);
1682 g_thread_set_priority(ut, G_THREAD_PRIORITY_LOW);
1684 #else /* _WIN32 || GTK1.2 || !G_THREADS_ENABLED || !USE_THREADS */
1685 /* this is to keep tap extensions updating once every 3 seconds */
1686 gtk_timeout_add(3000, (GtkFunction)update_cb,(gpointer)NULL);
1687 #endif /* !_WIN32 && GTK2 && G_THREADS_ENABLED */
1690 gtk_timeout_add(750, (GtkFunction) host_name_lookup_process, NULL);
1693 splash_update(splash_win, "Loading configuration files ...");
1695 /* Read the preference files. */
1696 prefs = read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path,
1697 &pf_open_errno, &pf_read_errno, &pf_path);
1698 if (gpf_path != NULL) {
1699 if (gpf_open_errno != 0) {
1700 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1701 "Could not open global preferences file\n\"%s\": %s.", gpf_path,
1702 strerror(gpf_open_errno));
1704 if (gpf_read_errno != 0) {
1705 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1706 "I/O error reading global preferences file\n\"%s\": %s.", gpf_path,
1707 strerror(gpf_read_errno));
1710 if (pf_path != NULL) {
1711 if (pf_open_errno != 0) {
1712 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1713 "Could not open your preferences file\n\"%s\": %s.", pf_path,
1714 strerror(pf_open_errno));
1716 if (pf_read_errno != 0) {
1717 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1718 "I/O error reading your preferences file\n\"%s\": %s.", pf_path,
1719 strerror(pf_read_errno));
1726 if (prefs->gui_console_open == console_open_always) {
1732 capture_opts.has_snaplen = FALSE;
1733 capture_opts.snaplen = MIN_PACKET_SIZE;
1734 capture_opts.linktype = -1;
1736 capture_opts.buffer_size = 1;
1739 capture_opts.has_autostop_packets = FALSE;
1740 capture_opts.autostop_packets = 1;
1741 capture_opts.has_autostop_duration = FALSE;
1742 capture_opts.autostop_duration = 60 /* 1 min */;
1743 capture_opts.has_autostop_filesize = FALSE;
1744 capture_opts.autostop_filesize = 1024 * 1024 /* 1 MB */;
1745 capture_opts.has_autostop_files = FALSE;
1746 capture_opts.autostop_files = 1;
1748 capture_opts.multi_files_on = FALSE;
1749 capture_opts.has_ring_num_files = TRUE;
1750 capture_opts.ring_num_files = 2;
1751 capture_opts.has_file_duration = FALSE;
1752 capture_opts.file_duration = 60 /* 1 min */;
1754 /* If this is a capture child process, it should pay no attention
1755 to the "prefs.capture_prom_mode" setting in the preferences file;
1756 it should do what the parent process tells it to do, and if
1757 the parent process wants it not to run in promiscuous mode, it'll
1758 tell it so with a "-p" flag.
1760 Otherwise, set promiscuous mode from the preferences setting. */
1761 /* the same applies to other preferences settings as well. */
1762 if (capture_child) {
1763 capture_opts.promisc_mode = TRUE; /* maybe changed by command line below */
1764 capture_opts.show_info = TRUE; /* maybe changed by command line below */
1765 capture_opts.sync_mode = TRUE; /* always true in child process */
1766 auto_scroll_live = FALSE; /* doesn't matter in child process */
1769 capture_opts.promisc_mode = prefs->capture_prom_mode;
1770 capture_opts.show_info = prefs->capture_show_info;
1771 capture_opts.sync_mode = prefs->capture_real_time;
1772 auto_scroll_live = prefs->capture_auto_scroll;
1775 #endif /* HAVE_LIBPCAP */
1777 /* Set the name resolution code's flags from the preferences. */
1778 g_resolv_flags = prefs->name_resolve;
1780 /* Read the capture filter file. */
1781 read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
1782 if (cf_path != NULL) {
1783 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1784 "Could not open your capture filter file\n\"%s\": %s.", cf_path,
1785 strerror(cf_open_errno));
1789 /* Read the display filter file. */
1790 read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
1791 if (df_path != NULL) {
1792 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1793 "Could not open your display filter file\n\"%s\": %s.", df_path,
1794 strerror(df_open_errno));
1798 /* Read the disabled protocols file. */
1799 read_disabled_protos_list(&gdp_path, &gdp_open_errno, &gdp_read_errno,
1800 &dp_path, &dp_open_errno, &dp_read_errno);
1801 if (gdp_path != NULL) {
1802 if (gdp_open_errno != 0) {
1803 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1804 "Could not open global disabled protocols file\n\"%s\": %s.",
1805 gdp_path, strerror(gdp_open_errno));
1807 if (gdp_read_errno != 0) {
1808 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1809 "I/O error reading global disabled protocols file\n\"%s\": %s.",
1810 gdp_path, strerror(gdp_read_errno));
1814 if (dp_path != NULL) {
1815 if (dp_open_errno != 0) {
1816 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1817 "Could not open your disabled protocols file\n\"%s\": %s.", dp_path,
1818 strerror(dp_open_errno));
1820 if (dp_read_errno != 0) {
1821 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1822 "I/O error reading your disabled protocols file\n\"%s\": %s.", dp_path,
1823 strerror(dp_read_errno));
1828 init_cap_file(&cfile);
1831 /* Load wpcap if possible. Do this before collecting the run-time version information */
1834 /* Start windows sockets */
1835 WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
1838 /* Assemble the compile-time version information string */
1839 comp_info_str = g_string_new("Compiled ");
1840 g_string_append(comp_info_str, "with ");
1841 g_string_sprintfa(comp_info_str,
1842 #ifdef GTK_MAJOR_VERSION
1843 "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
1846 "GTK+ (version unknown)");
1849 g_string_append(comp_info_str, ", ");
1850 get_compiled_version_info(comp_info_str);
1852 /* Assemble the run-time version information string */
1853 runtime_info_str = g_string_new("Running ");
1854 get_runtime_version_info(runtime_info_str);
1856 /* Now get our args */
1857 while ((opt = getopt(argc, argv, optstring)) != -1) {
1859 case 'a': /* autostop criteria */
1861 if (set_autostop_criterion(optarg) == FALSE) {
1862 fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
1866 capture_option_specified = TRUE;
1870 case 'b': /* Ringbuffer option */
1872 capture_opts.multi_files_on = TRUE;
1873 capture_opts.has_ring_num_files = TRUE;
1874 if (get_ring_arguments(optarg) == FALSE) {
1875 fprintf(stderr, "ethereal: Invalid or unknown -b arg \"%s\"\n", optarg);
1879 capture_option_specified = TRUE;
1883 case 'B': /* Byte view pane height */
1884 bv_size = get_positive_int(optarg, "byte view pane height");
1886 case 'c': /* Capture xxx packets */
1888 capture_opts.has_autostop_packets = TRUE;
1889 capture_opts.autostop_packets = get_positive_int(optarg, "packet count");
1891 capture_option_specified = TRUE;
1898 g_free(cfile.cfilter);
1899 cfile.cfilter = g_strdup(optarg);
1901 capture_option_specified = TRUE;
1905 case 'h': /* Print help and exit */
1909 case 'i': /* Use interface xxx */
1911 cfile.iface = g_strdup(optarg);
1913 capture_option_specified = TRUE;
1917 case 'k': /* Start capture immediately */
1919 start_capture = TRUE;
1921 capture_option_specified = TRUE;
1925 case 'l': /* Automatic scrolling in live capture mode */
1927 auto_scroll_live = TRUE;
1929 capture_option_specified = TRUE;
1933 case 'H': /* Hide capture info dialog box */
1935 capture_opts.show_info = FALSE;
1937 capture_option_specified = TRUE;
1941 case 'L': /* Print list of link-layer types and exit */
1943 list_link_layer_types = TRUE;
1945 capture_option_specified = TRUE;
1949 case 'm': /* Fixed-width font for the display */
1950 if (prefs->PREFS_GUI_FONT_NAME != NULL)
1951 g_free(prefs->PREFS_GUI_FONT_NAME);
1952 prefs->PREFS_GUI_FONT_NAME = g_strdup(optarg);
1954 case 'n': /* No name resolution */
1955 g_resolv_flags = RESOLV_NONE;
1957 case 'N': /* Select what types of addresses/port #s to resolve */
1958 if (g_resolv_flags == RESOLV_ALL)
1959 g_resolv_flags = RESOLV_NONE;
1960 badopt = string_to_name_resolve(optarg, &g_resolv_flags);
1961 if (badopt != '\0') {
1962 fprintf(stderr, "ethereal: -N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'\n",
1967 case 'o': /* Override preference from command line */
1968 switch (prefs_set_pref(optarg)) {
1970 case PREFS_SET_SYNTAX_ERR:
1971 fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
1975 case PREFS_SET_NO_SUCH_PREF:
1976 case PREFS_SET_OBSOLETE:
1977 fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
1983 case 'p': /* Don't capture in promiscuous mode */
1985 capture_opts.promisc_mode = FALSE;
1987 capture_option_specified = TRUE;
1991 case 'P': /* Packet list pane height */
1992 pl_size = get_positive_int(optarg, "packet list pane height");
1994 case 'Q': /* Quit after capture (just capture to file) */
1996 quit_after_cap = TRUE;
1997 start_capture = TRUE; /*** -Q implies -k !! ***/
1999 capture_option_specified = TRUE;
2003 case 'r': /* Read capture file xxx */
2004 /* We may set "last_open_dir" to "cf_name", and if we change
2005 "last_open_dir" later, we free the old value, so we have to
2006 set "cf_name" to something that's been allocated. */
2007 cf_name = g_strdup(optarg);
2009 case 'R': /* Read file filter */
2012 case 's': /* Set the snapshot (capture) length */
2014 capture_opts.has_snaplen = TRUE;
2015 capture_opts.snaplen = get_positive_int(optarg, "snapshot length");
2017 capture_option_specified = TRUE;
2021 case 'S': /* "Sync" mode: used for following file ala tail -f */
2023 capture_opts.sync_mode = TRUE;
2025 capture_option_specified = TRUE;
2029 case 't': /* Time stamp type */
2030 if (strcmp(optarg, "r") == 0)
2031 set_timestamp_setting(TS_RELATIVE);
2032 else if (strcmp(optarg, "a") == 0)
2033 set_timestamp_setting(TS_ABSOLUTE);
2034 else if (strcmp(optarg, "ad") == 0)
2035 set_timestamp_setting(TS_ABSOLUTE_WITH_DATE);
2036 else if (strcmp(optarg, "d") == 0)
2037 set_timestamp_setting(TS_DELTA);
2039 fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
2041 fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
2042 fprintf(stderr, "\"ad\" for absolute with date, or \"d\" for delta.\n");
2046 case 'T': /* Tree view pane height */
2047 tv_size = get_positive_int(optarg, "tree view pane height");
2049 case 'v': /* Show version and exit */
2056 case 'w': /* Write to capture file xxx */
2058 save_file = g_strdup(optarg);
2060 capture_option_specified = TRUE;
2064 case 'y': /* Set the pcap data link type */
2066 #ifdef HAVE_PCAP_DATALINK_NAME_TO_VAL
2067 capture_opts.linktype = pcap_datalink_name_to_val(optarg);
2068 if (capture_opts.linktype == -1) {
2069 fprintf(stderr, "ethereal: The specified data link type \"%s\" is not valid\n",
2073 #else /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
2074 /* XXX - just treat it as a number */
2075 capture_opts.linktype = get_natural_int(optarg, "data link type");
2076 #endif /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
2077 #else /* HAVE_LIBPCAP */
2078 capture_option_specified = TRUE;
2080 #endif /* HAVE_LIBPCAP */
2083 /* This is a hidden option supporting Sync mode, so we don't set
2084 * the error flags for the user in the non-libpcap case.
2086 case 'W': /* Write to capture file FD xxx */
2087 cfile.save_file_fd = atoi(optarg);
2091 for(tli=tap_list;tli;tli=tli->next){
2092 if(!strncmp(tli->cmd,optarg,strlen(tli->cmd))){
2093 tap_opt = g_strdup(optarg);
2098 fprintf(stderr,"ethereal: invalid -z argument.\n");
2099 fprintf(stderr," -z argument must be one of :\n");
2100 for(tli=tap_list;tli;tli=tli->next){
2101 fprintf(stderr," %s\n",tli->cmd);
2109 /* Hidden option supporting Sync mode */
2110 case 'Z': /* Write to pipe FD XXX */
2111 /* associate stdout with pipe */
2113 if (dup2(i, 1) < 0) {
2114 fprintf(stderr, "Unable to dup pipe handle\n");
2118 #endif /* HAVE_LIBPCAP */
2122 case '?': /* Bad flag - print usage message */
2130 if (cf_name != NULL) {
2132 * Input file name specified with "-r" *and* specified as a regular
2133 * command-line argument.
2138 * Input file name not specified with "-r", and a command-line argument
2139 * was specified; treat it as the input file name.
2141 * Yes, this is different from tethereal, where non-flag command-line
2142 * arguments are a filter, but this works better on GUI desktops
2143 * where a command can be specified to be run to open a particular
2144 * file - yes, you could have "-r" as the last part of the command,
2145 * but that's a bit ugly.
2147 cf_name = g_strdup(argv[0]);
2155 * Extra command line arguments were specified; complain.
2157 fprintf(stderr, "Invalid argument: %s\n", argv[0]);
2161 #ifndef HAVE_LIBPCAP
2162 if (capture_option_specified)
2163 fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
2171 if (start_capture && list_link_layer_types) {
2172 /* Specifying *both* is bogus. */
2173 fprintf(stderr, "ethereal: You cannot specify both -L and a live capture.\n");
2177 if (list_link_layer_types) {
2178 /* We're supposed to list the link-layer types for an interface;
2179 did the user also specify a capture file to be read? */
2181 /* Yes - that's bogus. */
2182 fprintf(stderr, "ethereal: You cannot specify -L and a capture file to be read.\n");
2185 /* No - did they specify a ring buffer option? */
2186 if (capture_opts.multi_files_on) {
2187 fprintf(stderr, "ethereal: Ring buffer requested, but a capture is not being done.\n");
2191 /* We're supposed to do a live capture; did the user also specify
2192 a capture file to be read? */
2193 if (start_capture && cf_name) {
2194 /* Yes - that's bogus. */
2195 fprintf(stderr, "ethereal: You cannot specify both a live capture and a capture file to be read.\n");
2199 /* No - was the ring buffer option specified and, if so, does it make
2201 if (capture_opts.multi_files_on) {
2202 /* Ring buffer works only under certain conditions:
2203 a) ring buffer does not work with temporary files;
2204 b) sync_mode and capture_opts.ringbuffer_on are mutually exclusive -
2205 sync_mode takes precedence;
2206 c) it makes no sense to enable the ring buffer if the maximum
2207 file size is set to "infinite". */
2208 if (save_file == NULL) {
2209 fprintf(stderr, "ethereal: Ring buffer requested, but capture isn't being saved to a permanent file.\n");
2210 capture_opts.multi_files_on = FALSE;
2212 if (capture_opts.sync_mode) {
2213 fprintf(stderr, "ethereal: Ring buffer requested, but an \"Update list of packets in real time\" capture is being done.\n");
2214 capture_opts.multi_files_on = FALSE;
2216 if (!capture_opts.has_autostop_filesize) {
2217 fprintf(stderr, "ethereal: Ring buffer requested, but no maximum capture file size was specified.\n");
2218 capture_opts.multi_files_on = FALSE;
2223 if (start_capture || list_link_layer_types) {
2224 /* Did the user specify an interface to use? */
2225 if (cfile.iface == NULL) {
2226 /* No - is a default specified in the preferences file? */
2227 if (prefs->capture_device != NULL) {
2229 cfile.iface = g_strdup(prefs->capture_device);
2231 /* No - pick the first one from the list of interfaces. */
2232 if_list = get_interface_list(&err, err_str);
2233 if (if_list == NULL) {
2236 case CANT_GET_INTERFACE_LIST:
2237 cant_get_if_list_errstr = cant_get_if_list_error_message(err_str);
2238 fprintf(stderr, "%s\n", cant_get_if_list_errstr);
2239 g_free(cant_get_if_list_errstr);
2242 case NO_INTERFACES_FOUND:
2243 fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
2248 if_info = if_list->data; /* first interface */
2249 cfile.iface = g_strdup(if_info->name);
2250 free_interface_list(if_list);
2255 if (capture_child) {
2256 if (cfile.save_file_fd == -1) {
2257 /* XXX - send this to the standard output as something our parent
2258 should put in an error message box? */
2259 fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
2264 if (list_link_layer_types) {
2265 /* Get the list of link-layer types for the capture device. */
2266 lt_list = get_pcap_linktype_list(cfile.iface, err_str);
2267 if (lt_list == NULL) {
2268 if (err_str[0] != '\0') {
2269 fprintf(stderr, "ethereal: The list of data link types for the capture device could not be obtained (%s).\n"
2270 "Please check to make sure you have sufficient permissions, and that\n"
2271 "you have the proper interface or pipe specified.\n", err_str);
2273 fprintf(stderr, "ethereal: The capture device has no data link types.\n");
2276 fprintf(stderr, "Data link types (use option -y to set):\n");
2277 for (lt_entry = lt_list; lt_entry != NULL;
2278 lt_entry = g_list_next(lt_entry)) {
2279 data_link_info = lt_entry->data;
2280 fprintf(stderr, " %s", data_link_info->name);
2281 if (data_link_info->description != NULL)
2282 fprintf(stderr, " (%s)", data_link_info->description);
2284 fprintf(stderr, " (not supported)");
2287 free_pcap_linktype_list(lt_list);
2293 /* Notify all registered modules that have had any of their preferences
2294 changed either from one of the preferences file or from the command
2295 line that their preferences have changed. */
2298 /* disabled protocols as per configuration file */
2299 if (gdp_path == NULL && dp_path == NULL) {
2300 set_disabled_protos_list();
2303 /* Build the column format array */
2304 col_setup(&cfile.cinfo, prefs->num_cols);
2305 for (i = 0; i < cfile.cinfo.num_cols; i++) {
2306 cfile.cinfo.col_fmt[i] = get_column_format(i);
2307 cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
2308 cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
2310 get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
2311 cfile.cinfo.col_data[i] = NULL;
2312 if (cfile.cinfo.col_fmt[i] == COL_INFO)
2313 cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
2315 cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2316 cfile.cinfo.col_fence[i] = 0;
2317 cfile.cinfo.col_expr[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2318 cfile.cinfo.col_expr_val[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2321 for (i = 0; i < cfile.cinfo.num_cols; i++) {
2324 for (j = 0; j < NUM_COL_FMTS; j++) {
2325 if (!cfile.cinfo.fmt_matx[i][j])
2328 if (cfile.cinfo.col_first[j] == -1)
2329 cfile.cinfo.col_first[j] = i;
2330 cfile.cinfo.col_last[j] = i;
2335 if (capture_opts.has_snaplen) {
2336 if (capture_opts.snaplen < 1)
2337 capture_opts.snaplen = WTAP_MAX_PACKET_SIZE;
2338 else if (capture_opts.snaplen < MIN_PACKET_SIZE)
2339 capture_opts.snaplen = MIN_PACKET_SIZE;
2342 /* Check the value range of the ringbuffer_num_files parameter */
2343 if (capture_opts.ring_num_files > RINGBUFFER_MAX_NUM_FILES)
2344 capture_opts.ring_num_files = RINGBUFFER_MAX_NUM_FILES;
2345 #if RINGBUFFER_MIN_NUM_FILES > 0
2346 else if (capture_opts.num_files < RINGBUFFER_MIN_NUM_FILES)
2347 capture_opts.ring_num_files = RINGBUFFER_MIN_NUM_FILES;
2351 /* read in rc file from global and personal configuration paths. */
2352 /* XXX - is this a good idea? */
2353 gtk_rc_parse(RC_FILE);
2354 rc_file = get_persconffile_path(RC_FILE, FALSE);
2355 gtk_rc_parse(rc_file);
2359 /* close the splash screen, as we are going to open the main window now */
2360 splash_destroy(splash_win);
2363 /* Is this a "child" ethereal, which is only supposed to pop up a
2364 capture box to let us stop the capture, and run a capture
2365 to a file that our parent will read? */
2366 if (!capture_child) {
2368 /* No. Pop up the main window, and read in a capture file if
2370 create_main_window(pl_size, tv_size, bv_size, prefs);
2372 /* Read the recent file, as we have the gui now ready for it. */
2373 read_recent(&rf_path, &rf_open_errno);
2375 /* rearrange all the widgets as we now have the recent settings for this */
2376 main_widgets_rearrange();
2378 /* Fill in column titles. This must be done after the top level window
2381 XXX - is that still true, with fixed-width columns? */
2382 packet_list_set_column_titles();
2384 menu_recent_read_finished();
2386 switch (user_font_apply()) {
2389 case FA_FONT_NOT_RESIZEABLE:
2390 /* "user_font_apply()" popped up an alert box. */
2391 /* turn off zooming - font can't be resized */
2392 case FA_FONT_NOT_AVAILABLE:
2393 /* XXX - did we successfully load the un-zoomed version earlier?
2394 If so, this *probably* means the font is available, but not at
2395 this particular zoom level, but perhaps some other failure
2396 occurred; I'm not sure you can determine which is the case,
2398 /* turn off zooming - zoom level is unavailable */
2400 /* in any other case than FA_SUCCESS, turn off zooming */
2401 recent.gui_zoom_level = 0;
2402 /* XXX: would it be a good idea to disable zooming (insensitive GUI)? */
2405 dnd_init(top_level);
2411 /* the window can be sized only, if it's not already shown, so do it now! */
2412 main_load_window_geometry(top_level);
2414 /*** we have finished all init things, show the main window ***/
2415 gtk_widget_show(top_level);
2417 /* the window can be maximized only, if it's visible, so do it after show! */
2418 main_load_window_geometry(top_level);
2420 /* process all pending GUI events before continue */
2421 while (gtk_events_pending()) gtk_main_iteration();
2423 /* Pop up any queued-up alert boxes. */
2424 display_queued_messages();
2426 /* If we were given the name of a capture file, read it in now;
2427 we defer it until now, so that, if we can't open it, and pop
2428 up an alert box, the alert box is more likely to come up on
2429 top of the main window - but before the preference-file-error
2430 alert box, so, if we get one of those, it's more likely to come
2433 if (rfilter != NULL) {
2434 if (!dfilter_compile(rfilter, &rfcode)) {
2435 bad_dfilter_alert_box(rfilter);
2436 rfilter_parse_failed = TRUE;
2439 if (!rfilter_parse_failed) {
2440 if ((err = cf_open(cf_name, FALSE, &cfile)) == 0) {
2441 /* "cf_open()" succeeded, so it closed the previous
2442 capture file, and thus destroyed any previous read filter
2443 attached to "cf". */
2444 cfile.rfcode = rfcode;
2446 /* Open tap windows; we do so after creating the main window,
2447 to avoid GTK warnings, and after successfully opening the
2448 capture file, so we know we have something to tap. */
2449 if (tap_opt && tli) {
2450 (*tli->func)(tap_opt);
2454 /* Read the capture file. */
2455 switch (cf_read(&cfile)) {
2459 /* Just because we got an error, that doesn't mean we were unable
2460 to read any of the file; we handle what we could get from the
2469 /* Save the name of the containing directory specified in the
2470 path name, if any; we can write over cf_name, which is a
2471 good thing, given that "get_dirname()" does write over its
2473 s = get_dirname(cf_name);
2474 /* we might already set this from the recent file, don't overwrite this */
2475 if(get_last_open_dir() == NULL)
2476 set_last_open_dir(s);
2481 dfilter_free(rfcode);
2482 cfile.rfcode = NULL;
2487 if (start_capture) {
2488 /* "-k" was specified; start a capture. */
2489 if (do_capture(save_file)) {
2490 /* The capture started. Open tap windows; we do so after creating
2491 the main window, to avoid GTK warnings, and after starting the
2492 capture, so we know we have something to tap. */
2493 if (tap_opt && tli) {
2494 (*tli->func)(tap_opt);
2498 if (save_file != NULL) {
2499 /* Save the directory name for future file dialogs. */
2500 s = get_dirname(save_file); /* Overwrites save_file */
2501 set_last_open_dir(s);
2507 set_menus_for_capture_in_progress(FALSE);
2510 /* This is the child process for a sync mode or fork mode capture,
2511 so just do the low-level work of a capture - don't create
2512 a temporary file and fork off *another* child process (so don't
2513 call "do_capture()"). */
2515 /* Pop up any queued-up alert boxes. */
2516 display_queued_messages();
2518 /* XXX - hand these stats to the parent process */
2519 capture(&stats_known, &stats);
2521 /* The capture is done; there's nothing more for us to do. */
2524 if (!start_capture && (cfile.cfilter == NULL || strlen(cfile.cfilter) == 0)) {
2525 if (cfile.cfilter) {
2526 g_free(cfile.cfilter);
2528 cfile.cfilter = g_strdup(get_conn_cfilter());
2530 #else /* HAVE_LIBPCAP */
2531 set_menus_for_capture_in_progress(FALSE);
2532 #endif /* HAVE_LIBPCAP */
2540 /* Shutdown windows sockets */
2543 /* For some unknown reason, the "atexit()" call in "create_console()"
2544 doesn't arrange that "destroy_console()" be called when we exit,
2545 so we call it here if a console was created. */
2551 /* This isn't reached, but we need it to keep GCC from complaining
2552 that "main()" returns without returning a value - it knows that
2553 "exit()" never returns, but it doesn't know that "gtk_exit()"
2554 doesn't, as GTK+ doesn't declare it with the attribute
2556 return 0; /* not reached */
2561 /* We build this as a GUI subsystem application on Win32, so
2562 "WinMain()", not "main()", gets called.
2564 Hack shamelessly stolen from the Win32 port of the GIMP. */
2566 #define _stdcall __attribute__((stdcall))
2570 WinMain (struct HINSTANCE__ *hInstance,
2571 struct HINSTANCE__ *hPrevInstance,
2575 has_console = FALSE;
2576 return main (__argc, __argv);
2580 * If this application has no console window to which its standard output
2581 * would go, create one.
2584 create_console(void)
2586 if (!has_console && prefs.gui_console_open != console_open_never) {
2587 /* We have no console to which to print the version string, so
2588 create one and make it the standard input, output, and error. */
2589 if (!AllocConsole())
2590 return; /* couldn't create console */
2591 freopen("CONIN$", "r", stdin);
2592 freopen("CONOUT$", "w", stdout);
2593 freopen("CONOUT$", "w", stderr);
2595 /* Well, we have a console now. */
2598 /* Now register "destroy_console()" as a routine to be called just
2599 before the application exits, so that we can destroy the console
2600 after the user has typed a key (so that the console doesn't just
2601 disappear out from under them, giving the user no chance to see
2602 the message(s) we put in there). */
2603 atexit(destroy_console);
2608 destroy_console(void)
2611 printf("\n\nPress any key to exit\n");
2617 /* This routine should not be necessary, at least as I read the GLib
2618 source code, as it looks as if GLib is, on Win32, *supposed* to
2619 create a console window into which to display its output.
2621 That doesn't happen, however. I suspect there's something completely
2622 broken about that code in GLib-for-Win32, and that it may be related
2623 to the breakage that forces us to just call "printf()" on the message
2624 rather than passing the message on to "g_log_default_handler()"
2625 (which is the routine that does the aforementioned non-functional
2626 console window creation). */
2628 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
2629 const char *message, gpointer user_data)
2633 /* For some unknown reason, the above doesn't appear to actually cause
2634 anything to be sent to the standard output, so we'll just splat the
2635 message out directly, just to make sure it gets out. */
2636 printf("%s\n", message);
2638 g_log_default_handler(log_domain, log_level, message, user_data);
2643 GtkWidget *info_bar_new(void)
2645 /* tip: tooltips don't work on statusbars! */
2646 info_bar = gtk_statusbar_new();
2647 main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
2648 file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
2649 help_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "help");
2650 #if GTK_MAJOR_VERSION >= 2
2651 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), FALSE);
2653 gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
2658 GtkWidget *packets_bar_new(void)
2660 /* tip: tooltips don't work on statusbars! */
2661 packets_bar = gtk_statusbar_new();
2662 packets_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(packets_bar), "packets");
2663 packets_bar_update();
2670 * Helper for main_widgets_rearrange()
2672 void foreach_remove_a_child(GtkWidget *widget, gpointer data) {
2673 gtk_container_remove(GTK_CONTAINER(data), widget);
2676 GtkWidget *main_widget_layout(gint layout_content)
2678 switch(layout_content) {
2679 case(layout_pane_content_none):
2682 case(layout_pane_content_plist):
2685 case(layout_pane_content_pdetails):
2688 case(layout_pane_content_pbytes):
2692 g_assert_not_reached();
2699 * Rearrange the main window widgets
2701 void main_widgets_rearrange(void) {
2702 GtkWidget *first_pane_widget1, *first_pane_widget2;
2703 GtkWidget *second_pane_widget1, *second_pane_widget2;
2704 gboolean split_top_left;
2706 /* be a bit faster */
2707 gtk_widget_hide(main_vbox);
2709 /* be sure, we don't loose a widget while rearranging */
2710 gtk_widget_ref(menubar);
2711 gtk_widget_ref(main_tb);
2712 gtk_widget_ref(filter_tb);
2713 gtk_widget_ref(pkt_scrollw);
2714 gtk_widget_ref(tv_scrollw);
2715 gtk_widget_ref(byte_nb_ptr);
2716 gtk_widget_ref(stat_hbox);
2717 gtk_widget_ref(info_bar);
2718 gtk_widget_ref(packets_bar);
2719 gtk_widget_ref(status_pane);
2720 gtk_widget_ref(main_pane_v1);
2721 gtk_widget_ref(main_pane_v2);
2722 gtk_widget_ref(main_pane_h1);
2723 gtk_widget_ref(main_pane_h2);
2725 /* empty all containers participating */
2726 gtk_container_foreach(GTK_CONTAINER(main_vbox), foreach_remove_a_child, main_vbox);
2727 gtk_container_foreach(GTK_CONTAINER(stat_hbox), foreach_remove_a_child, stat_hbox);
2728 gtk_container_foreach(GTK_CONTAINER(status_pane), foreach_remove_a_child, status_pane);
2729 gtk_container_foreach(GTK_CONTAINER(main_pane_v1), foreach_remove_a_child, main_pane_v1);
2730 gtk_container_foreach(GTK_CONTAINER(main_pane_v2), foreach_remove_a_child, main_pane_v2);
2731 gtk_container_foreach(GTK_CONTAINER(main_pane_h1), foreach_remove_a_child, main_pane_h1);
2732 gtk_container_foreach(GTK_CONTAINER(main_pane_h2), foreach_remove_a_child, main_pane_h2);
2734 /* add the menubar always at the top */
2735 gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
2738 gtk_box_pack_start(GTK_BOX(main_vbox), main_tb, FALSE, TRUE, 0);
2740 /* filter toolbar in toolbar area */
2741 if (!prefs.filter_toolbar_show_in_statusbar) {
2742 gtk_box_pack_start(GTK_BOX(main_vbox), filter_tb, FALSE, TRUE, 1);
2745 /* fill the main layout panes */
2746 switch(prefs.gui_layout_type) {
2747 case(layout_type_5):
2748 main_first_pane = main_pane_v1;
2749 main_second_pane = main_pane_v2;
2750 split_top_left = FALSE;
2752 case(layout_type_2):
2753 main_first_pane = main_pane_v1;
2754 main_second_pane = main_pane_h1;
2755 split_top_left = FALSE;
2757 case(layout_type_1):
2758 main_first_pane = main_pane_v1;
2759 main_second_pane = main_pane_h1;
2760 split_top_left = TRUE;
2762 case(layout_type_4):
2763 main_first_pane = main_pane_h1;
2764 main_second_pane = main_pane_v1;
2765 split_top_left = FALSE;
2767 case(layout_type_3):
2768 main_first_pane = main_pane_h1;
2769 main_second_pane = main_pane_v1;
2770 split_top_left = TRUE;
2772 case(layout_type_6):
2773 main_first_pane = main_pane_h1;
2774 main_second_pane = main_pane_h2;
2775 split_top_left = FALSE;
2778 main_first_pane = NULL;
2779 main_second_pane = NULL;
2780 split_top_left = FALSE;
2781 g_assert_not_reached();
2783 if (split_top_left) {
2784 first_pane_widget1 = main_second_pane;
2785 second_pane_widget1 = main_widget_layout(prefs.gui_layout_content_1);
2786 second_pane_widget2 = main_widget_layout(prefs.gui_layout_content_2);
2787 first_pane_widget2 = main_widget_layout(prefs.gui_layout_content_3);
2789 first_pane_widget1 = main_widget_layout(prefs.gui_layout_content_1);
2790 first_pane_widget2 = main_second_pane;
2791 second_pane_widget1 = main_widget_layout(prefs.gui_layout_content_2);
2792 second_pane_widget2 = main_widget_layout(prefs.gui_layout_content_3);
2794 if (first_pane_widget1 != NULL)
2795 gtk_paned_add1(GTK_PANED(main_first_pane), first_pane_widget1);
2796 if (first_pane_widget2 != NULL)
2797 gtk_paned_add2(GTK_PANED(main_first_pane), first_pane_widget2);
2798 if (second_pane_widget1 != NULL)
2799 gtk_paned_pack1(GTK_PANED(main_second_pane), second_pane_widget1, TRUE, TRUE);
2800 if (second_pane_widget2 != NULL)
2801 gtk_paned_pack2(GTK_PANED(main_second_pane), second_pane_widget2, FALSE, FALSE);
2803 gtk_container_add(GTK_CONTAINER(main_vbox), main_first_pane);
2805 /* statusbar hbox */
2806 gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
2808 /* filter toolbar in statusbar hbox */
2809 if (prefs.filter_toolbar_show_in_statusbar) {
2810 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_tb, FALSE, TRUE, 1);
2814 gtk_box_pack_start(GTK_BOX(stat_hbox), status_pane, TRUE, TRUE, 0);
2815 gtk_paned_pack1(GTK_PANED(status_pane), info_bar, FALSE, FALSE);
2816 gtk_paned_pack2(GTK_PANED(status_pane), packets_bar, FALSE, FALSE);
2818 /* hide widgets on users recent settings */
2819 main_widgets_show_or_hide();
2821 gtk_widget_show(main_vbox);
2825 is_widget_visible(GtkWidget *widget, gpointer data)
2827 gboolean *is_visible = data;
2830 if (GTK_WIDGET_VISIBLE(widget))
2836 * XXX - this doesn't appear to work with the paned widgets in
2837 * GTK+ 1.2[.x]; if you hide one of the panes, the splitter remains
2838 * and the other pane doesn't grow to take up the rest of the pane.
2839 * It does appear to work with GTK+ 2.x.
2842 main_widgets_show_or_hide(void)
2844 gboolean main_second_pane_show;
2846 if (recent.main_toolbar_show) {
2847 gtk_widget_show(main_tb);
2849 gtk_widget_hide(main_tb);
2853 * Show the status hbox if either:
2855 * 1) we're showing the filter toolbar and we want it in the status
2860 * 2) we're showing the status bar.
2862 if ((recent.filter_toolbar_show && prefs.filter_toolbar_show_in_statusbar) ||
2863 recent.statusbar_show) {
2864 gtk_widget_show(stat_hbox);
2866 gtk_widget_hide(stat_hbox);
2869 if (recent.statusbar_show) {
2870 gtk_widget_show(status_pane);
2872 gtk_widget_hide(status_pane);
2875 if (recent.filter_toolbar_show) {
2876 gtk_widget_show(filter_tb);
2878 gtk_widget_hide(filter_tb);
2881 if (recent.packet_list_show) {
2882 gtk_widget_show(pkt_scrollw);
2884 gtk_widget_hide(pkt_scrollw);
2887 if (recent.tree_view_show) {
2888 gtk_widget_show(tv_scrollw);
2890 gtk_widget_hide(tv_scrollw);
2893 if (recent.byte_view_show) {
2894 gtk_widget_show(byte_nb_ptr);
2896 gtk_widget_hide(byte_nb_ptr);
2900 * Is anything in "main_second_pane" visible?
2901 * If so, show it, otherwise hide it.
2903 main_second_pane_show = FALSE;
2904 gtk_container_foreach(GTK_CONTAINER(main_second_pane), is_widget_visible,
2905 &main_second_pane_show);
2906 if (main_second_pane_show) {
2907 gtk_widget_show(main_second_pane);
2909 gtk_widget_hide(main_second_pane);
2914 #if GTK_MAJOR_VERSION >= 2
2915 /* called, when the window state changes (minimized, maximized, ...) */
2917 window_state_event_cb (GtkWidget *widget _U_,
2921 GdkWindowState new_window_state = ((GdkEventWindowState*)event)->new_window_state;
2923 if( (event->type) == (GDK_WINDOW_STATE)) {
2924 if(!(new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
2925 /* we might have dialogs popped up while we where iconified,
2927 display_queued_messages();
2936 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
2939 *filter_bt, *filter_cm, *filter_te,
2940 *filter_add_expr_bt,
2943 GList *filter_list = NULL;
2944 GtkTooltips *tooltips;
2945 GtkAccelGroup *accel;
2946 /* Display filter construct dialog has an Apply button, and "OK" not
2947 only sets our text widget, it activates it (i.e., it causes us to
2948 filter the capture). */
2949 static construct_args_t args = {
2950 "Ethereal: Display Filter",
2956 top_level = window_new(GTK_WINDOW_TOPLEVEL, "The Ethereal Network Analyzer");
2958 tooltips = gtk_tooltips_new();
2961 #if GTK_MAJOR_VERSION < 2
2962 /* has to be done, after top_level window is created */
2963 app_font_gtk1_init(top_level);
2967 gtk_widget_set_name(top_level, "main window");
2968 SIGNAL_CONNECT(top_level, "delete_event", main_window_delete_event_cb,
2970 #if GTK_MAJOR_VERSION >= 2
2971 SIGNAL_CONNECT(GTK_OBJECT(top_level), "window_state_event",
2972 G_CALLBACK (window_state_event_cb), NULL);
2975 gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
2977 /* Container for menu bar, toolbar(s), paned windows and progress/info box */
2978 main_vbox = gtk_vbox_new(FALSE, 1);
2979 gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
2980 gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
2981 gtk_widget_show(main_vbox);
2984 menubar = main_menu_new(&accel);
2985 gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
2986 gtk_widget_show(menubar);
2989 main_tb = toolbar_new();
2990 gtk_widget_show (main_tb);
2993 pkt_scrollw = packet_list_new(prefs);
2994 WIDGET_SET_SIZE(packet_list, -1, pl_size);
2995 gtk_widget_show(pkt_scrollw);
2998 tv_scrollw = main_tree_view_new(prefs, &tree_view);
2999 WIDGET_SET_SIZE(tv_scrollw, -1, tv_size);
3000 gtk_widget_show(tv_scrollw);
3002 #if GTK_MAJOR_VERSION < 2
3003 SIGNAL_CONNECT(tree_view, "tree-select-row", tree_view_select_row_cb, NULL);
3004 SIGNAL_CONNECT(tree_view, "tree-unselect-row", tree_view_unselect_row_cb,
3007 SIGNAL_CONNECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)),
3008 "changed", tree_view_selection_changed_cb, NULL);
3010 SIGNAL_CONNECT(tree_view, "button_press_event", popup_menu_handler,
3011 OBJECT_GET_DATA(popup_menu_object, PM_TREE_VIEW_KEY));
3012 gtk_widget_show(tree_view);
3015 byte_nb_ptr = byte_view_new();
3016 WIDGET_SET_SIZE(byte_nb_ptr, -1, bv_size);
3017 gtk_widget_show(byte_nb_ptr);
3019 SIGNAL_CONNECT(byte_nb_ptr, "button_press_event", popup_menu_handler,
3020 OBJECT_GET_DATA(popup_menu_object, PM_HEXDUMP_KEY));
3023 /* Panes for the packet list, tree, and byte view */
3024 main_pane_v1 = gtk_vpaned_new();
3025 gtk_widget_show(main_pane_v1);
3026 main_pane_v2 = gtk_vpaned_new();
3027 gtk_widget_show(main_pane_v2);
3028 main_pane_h1 = gtk_hpaned_new();
3029 gtk_widget_show(main_pane_h1);
3030 main_pane_h2 = gtk_hpaned_new();
3031 gtk_widget_show(main_pane_h2);
3033 /* filter toolbar */
3034 #if GTK_MAJOR_VERSION < 2
3035 filter_tb = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
3038 filter_tb = gtk_toolbar_new();
3039 gtk_toolbar_set_orientation(GTK_TOOLBAR(filter_tb),
3040 GTK_ORIENTATION_HORIZONTAL);
3041 #endif /* GTK_MAJOR_VERSION */
3042 gtk_widget_show(filter_tb);
3044 /* Create the "Filter:" button */
3045 filter_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_DISPLAY_FILTER_ENTRY);
3046 SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
3047 gtk_widget_show(filter_bt);
3048 OBJECT_SET_DATA(top_level, E_FILT_BT_PTR_KEY, filter_bt);
3050 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_bt,
3051 "Open the \"Display Filter\" dialog, to edit/apply filters", "Private");
3053 /* Create the filter combobox */
3054 filter_cm = gtk_combo_new();
3056 gtk_combo_disable_activate(GTK_COMBO(filter_cm));
3057 gtk_combo_set_case_sensitive(GTK_COMBO(filter_cm), TRUE);
3058 OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
3059 filter_te = GTK_COMBO(filter_cm)->entry;
3060 main_display_filter_widget=filter_te;
3061 OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_te);
3062 OBJECT_SET_DATA(filter_te, E_DFILTER_CM_KEY, filter_cm);
3063 OBJECT_SET_DATA(top_level, E_DFILTER_CM_KEY, filter_cm);
3064 SIGNAL_CONNECT(filter_te, "activate", filter_activate_cb, filter_te);
3065 SIGNAL_CONNECT(filter_te, "changed", filter_te_syntax_check_cb, NULL);
3066 WIDGET_SET_SIZE(filter_cm, 400, -1);
3067 gtk_widget_show(filter_cm);
3068 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_cm,
3070 /* setting a tooltip for a combobox will do nothing, so add it to the corresponding text entry */
3071 gtk_tooltips_set_tip(tooltips, filter_te,
3072 "Enter a display filter, or choose one of your recently used filters. "
3073 "The background color of this field is changed by a continuous syntax check (green is valid, red is invalid).",
3076 /* Create the "Add Expression..." button, to pop up a dialog
3077 for constructing filter comparison expressions. */
3078 filter_add_expr_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_ADD_EXPRESSION);
3079 OBJECT_SET_DATA(filter_tb, E_FILT_FILTER_TE_KEY, filter_te);
3080 SIGNAL_CONNECT(filter_add_expr_bt, "clicked", filter_add_expr_bt_cb, filter_tb);
3081 gtk_widget_show(filter_add_expr_bt);
3082 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_add_expr_bt,
3083 "Add an expression to this filter string", "Private");
3085 /* Create the "Clear" button */
3086 filter_reset = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLEAR);
3087 OBJECT_SET_DATA(filter_reset, E_DFILTER_TE_KEY, filter_te);
3088 SIGNAL_CONNECT(filter_reset, "clicked", filter_reset_cb, NULL);
3089 gtk_widget_show(filter_reset);
3090 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_reset,
3091 "Clear this filter string and update the display", "Private");
3093 /* Create the "Apply" button */
3094 filter_apply = BUTTON_NEW_FROM_STOCK(GTK_STOCK_APPLY);
3095 OBJECT_SET_DATA(filter_apply, E_DFILTER_CM_KEY, filter_cm);
3096 SIGNAL_CONNECT(filter_apply, "clicked", filter_activate_cb, filter_te);
3097 gtk_widget_show(filter_apply);
3098 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_apply,
3099 "Apply this filter string to the display", "Private");
3101 /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
3102 * of any widget that ends up calling a callback which needs
3103 * that text entry pointer */
3104 set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
3105 set_menu_object_data("/Analyze/Display Filters...", E_FILT_TE_PTR_KEY,
3107 set_menu_object_data("/Analyze/Follow TCP Stream", E_DFILTER_TE_KEY,
3109 set_menu_object_data("/Analyze/Apply as Filter/Selected", E_DFILTER_TE_KEY,
3111 set_menu_object_data("/Analyze/Apply as Filter/Not Selected", E_DFILTER_TE_KEY,
3113 set_menu_object_data("/Analyze/Apply as Filter/... and Selected", E_DFILTER_TE_KEY,
3115 set_menu_object_data("/Analyze/Apply as Filter/... or Selected", E_DFILTER_TE_KEY,
3117 set_menu_object_data("/Analyze/Apply as Filter/... and not Selected", E_DFILTER_TE_KEY,
3119 set_menu_object_data("/Analyze/Apply as Filter/... or not Selected", E_DFILTER_TE_KEY,
3121 set_menu_object_data("/Analyze/Prepare a Filter/Selected", E_DFILTER_TE_KEY,
3123 set_menu_object_data("/Analyze/Prepare a Filter/Not Selected", E_DFILTER_TE_KEY,
3125 set_menu_object_data("/Analyze/Prepare a Filter/... and Selected", E_DFILTER_TE_KEY,
3127 set_menu_object_data("/Analyze/Prepare a Filter/... or Selected", E_DFILTER_TE_KEY,
3129 set_menu_object_data("/Analyze/Prepare a Filter/... and not Selected", E_DFILTER_TE_KEY,
3131 set_menu_object_data("/Analyze/Prepare a Filter/... or not Selected", E_DFILTER_TE_KEY,
3133 set_toolbar_object_data(E_DFILTER_TE_KEY, filter_te);
3134 OBJECT_SET_DATA(popup_menu_object, E_DFILTER_TE_KEY, filter_te);
3135 OBJECT_SET_DATA(popup_menu_object, E_MPACKET_LIST_KEY, packet_list);
3137 /* info (main) statusbar */
3138 info_bar = info_bar_new();
3139 gtk_widget_show(info_bar);
3141 /* packets statusbar */
3142 packets_bar = packets_bar_new();
3143 gtk_widget_show(packets_bar);
3145 /* Filter/status hbox */
3146 stat_hbox = gtk_hbox_new(FALSE, 1);
3147 gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
3148 gtk_widget_show(stat_hbox);
3150 /* Pane for the statusbar */
3151 status_pane = gtk_hpaned_new();
3152 gtk_widget_show(status_pane);