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"
111 #if GTK_MAJOR_VERSION < 2 && GTK_MINOR_VERSION < 3
112 #include "ethclist.h"
116 #include "statusbar.h"
117 #include "alert_box.h"
118 #include "dlg_utils.h"
119 #include "gtkglobals.h"
121 #include "ui_util.h" /* beware: ui_util.h exists twice! */
122 #include "compat_macros.h"
127 #include "file_dlg.h"
128 #include <epan/column.h>
129 #include "proto_draw.h"
131 #include "packet_win.h"
133 #include "find_dlg.h"
134 #include "packet_list.h"
136 #include "follow_dlg.h"
137 #include "font_utils.h"
138 #include "about_dlg.h"
139 #include "help_dlg.h"
140 #include "decode_as_dlg.h"
141 #include "webbrowser.h"
145 * File under personal preferences directory in which GTK settings for
146 * Ethereal are stored.
148 #define RC_FILE "gtkrc"
151 #define DEF_READY_MESSAGE " Ready to load or capture"
153 #define DEF_READY_MESSAGE " Ready to load file"
157 GtkWidget *main_display_filter_widget=NULL;
158 GtkWidget *top_level = NULL, *tree_view, *byte_nb_ptr, *tv_scrollw;
159 static GtkWidget *main_pane_v1, *main_pane_v2, *main_pane_h1, *main_pane_h2;
160 static GtkWidget *main_first_pane, *main_second_pane;
161 static GtkWidget *status_pane;
162 static GtkWidget *menubar, *main_vbox, *main_tb, *pkt_scrollw, *stat_hbox, *filter_tb;
163 static GtkWidget *info_bar;
164 static GtkWidget *packets_bar = NULL;
165 static guint main_ctx, file_ctx, help_ctx;
166 static guint packets_ctx;
167 static gchar *packets_str = NULL;
168 GString *comp_info_str, *runtime_info_str;
169 gchar *ethereal_path = NULL;
172 static gboolean has_console; /* TRUE if app has console */
173 /*static void create_console(void);*/
174 static void destroy_console(void);
175 static void console_log_handler(const char *log_domain,
176 GLogLevelFlags log_level, const char *message, gpointer user_data);
180 static gboolean list_link_layer_types;
181 capture_options global_capture_opts;
182 capture_options *capture_opts = &global_capture_opts;
186 static void create_main_window(gint, gint, gint, e_prefs*);
187 static void file_quit_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_);
188 static void main_save_window_geometry(GtkWidget *widget);
190 #define E_DFILTER_CM_KEY "display_filter_combo"
191 #define E_DFILTER_FL_KEY "display_filter_list"
195 /* Match selected byte pattern */
197 match_selected_cb_do(gpointer data, int action, gchar *text)
199 GtkWidget *filter_te;
200 char *cur_filter, *new_filter;
205 filter_te = OBJECT_GET_DATA(data, E_DFILTER_TE_KEY);
208 cur_filter = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, -1);
210 switch (action&MATCH_SELECTED_MASK) {
212 case MATCH_SELECTED_REPLACE:
213 new_filter = g_strdup(text);
216 case MATCH_SELECTED_AND:
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_OR:
224 if ((!cur_filter) || (0 == strlen(cur_filter)))
225 new_filter = g_strdup(text);
227 new_filter = g_strconcat("(", cur_filter, ") || (", text, ")", NULL);
230 case MATCH_SELECTED_NOT:
231 new_filter = g_strconcat("!(", text, ")", NULL);
234 case MATCH_SELECTED_AND_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);
241 case MATCH_SELECTED_OR_NOT:
242 if ((!cur_filter) || (0 == strlen(cur_filter)))
243 new_filter = g_strconcat("!(", text, ")", NULL);
245 new_filter = g_strconcat("(", cur_filter, ") || !(", text, ")", NULL);
249 g_assert_not_reached();
254 /* Free up the copy we got of the old filter text. */
257 /* create a new one and set the display filter entry accordingly */
258 gtk_entry_set_text(GTK_ENTRY(filter_te), new_filter);
260 /* Run the display filter so it goes in effect. */
261 if (action&MATCH_SELECTED_APPLY_NOW)
262 main_filter_packets(&cfile, new_filter, FALSE);
264 /* Free up the new filter text. */
267 /* Free up the generated text we were handed. */
272 match_selected_ptree_cb(GtkWidget *w, gpointer data, MATCH_SELECTED_E action)
274 if (cfile.finfo_selected)
275 match_selected_cb_do((data ? data : w),
277 proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt));
281 static void selected_ptree_info_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
283 gchar *selected_proto_url;
284 gchar *proto_abbrev = data;
289 if (cfile.finfo_selected) {
290 /* open wiki page using the protocol abbreviation */
291 selected_proto_url = g_strdup_printf("http://wiki.ethereal.com/Protocols/%s", proto_abbrev);
292 browser_open_url(selected_proto_url);
293 g_free(selected_proto_url);
296 case(ESD_BTN_CANCEL):
299 g_assert_not_reached();
305 selected_ptree_info_cb(GtkWidget *widget _U_, gpointer data _U_)
312 if (cfile.finfo_selected) {
313 /* convert selected field to protocol abbreviation */
314 /* XXX - could this conversion be simplified? */
315 field_id = cfile.finfo_selected->hfinfo->id;
316 /* if the selected field isn't a protocol, get it's parent */
317 if(!proto_registrar_is_protocol(field_id)) {
318 field_id = proto_registrar_get_parent(cfile.finfo_selected->hfinfo->id);
321 proto_abbrev = proto_registrar_get_abbrev(field_id);
323 /* ask the user if the wiki page really should be opened */
324 dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
325 PRIMARY_TEXT_START "Open Ethereal Wiki page of protocol \"%s\"?" PRIMARY_TEXT_END "\n"
327 "This will open the \"%s\" related Ethereal Wiki page in your Web browser.\n"
329 "The Ethereal Wiki is a collaborative approach to provide information\n"
330 "about Ethereal in several ways (not limited to protocol specifics).\n"
332 "This Wiki is new, so the page of the selected protocol\n"
333 "may not exist and/or may not contain valuable information.\n"
335 "As everyone can edit the Wiki and add new content (or extend existing),\n"
336 "you are encouraged to add information if you can.\n"
338 "Hint 1: If you are new to wiki editing, try out editing the Sandbox first!\n"
340 "Hint 2: If you want to add a new protocol page, you should use the ProtocolTemplate,\n"
341 "which will save you a lot of editing and will give a consistent look over the pages.",
342 proto_abbrev, proto_abbrev);
343 simple_dialog_set_cb(dialog, selected_ptree_info_answered_cb, proto_abbrev);
349 selected_ptree_ref_cb(GtkWidget *widget _U_, gpointer data _U_)
353 gchar *selected_proto_url;
356 if (cfile.finfo_selected) {
357 /* convert selected field to protocol abbreviation */
358 /* XXX - could this conversion be simplified? */
359 field_id = cfile.finfo_selected->hfinfo->id;
360 /* if the selected field isn't a protocol, get it's parent */
361 if(!proto_registrar_is_protocol(field_id)) {
362 field_id = proto_registrar_get_parent(cfile.finfo_selected->hfinfo->id);
365 proto_abbrev = proto_registrar_get_abbrev(field_id);
367 /* open reference page using the protocol abbreviation */
368 selected_proto_url = g_strdup_printf("http://www.ethereal.com/docs/dfref/%c/%s", proto_abbrev[0], proto_abbrev);
369 browser_open_url(selected_proto_url);
370 g_free(selected_proto_url);
376 get_text_from_packet_list(gpointer data)
378 gint row = GPOINTER_TO_INT(OBJECT_GET_DATA(data, E_MPACKET_LIST_ROW_KEY));
379 gint column = GPOINTER_TO_INT(OBJECT_GET_DATA(data, E_MPACKET_LIST_COL_KEY));
380 frame_data *fdata = (frame_data *)packet_list_get_row_data(row);
388 if (!wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header,
389 cfile.pd, fdata->cap_len, &err, &err_info)) {
390 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
391 cf_read_error_message(err, err_info), cfile.filename);
395 edt = epan_dissect_new(FALSE, FALSE);
396 epan_dissect_run(edt, &cfile.pseudo_header, cfile.pd, fdata,
398 epan_dissect_fill_in_columns(edt);
400 if (strlen(cfile.cinfo.col_expr[column]) != 0 &&
401 strlen(cfile.cinfo.col_expr_val[column]) != 0) {
402 len = strlen(cfile.cinfo.col_expr[column]) +
403 strlen(cfile.cinfo.col_expr_val[column]) + 5;
404 buf = g_malloc0(len);
405 g_snprintf(buf, len, "%s == %s", cfile.cinfo.col_expr[column],
406 cfile.cinfo.col_expr_val[column]);
409 epan_dissect_free(edt);
416 match_selected_plist_cb(GtkWidget *w _U_, gpointer data, MATCH_SELECTED_E action)
418 match_selected_cb_do(data,
420 get_text_from_packet_list(data));
425 /* XXX: use a preference for this setting! */
426 static guint dfilter_combo_max_recent = 10;
428 /* add a display filter to the combo box */
429 /* Note: a new filter string will replace an old identical one */
431 dfilter_combo_add(GtkWidget *filter_cm, char *s) {
433 GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
436 /* GtkCombos don't let us get at their list contents easily, so we maintain
437 our own filter list, and feed it to gtk_combo_set_popdown_strings when
438 a new filter is added. */
439 li = g_list_first(filter_list);
441 /* If the filter is already in the list, remove the old one and
442 * append the new one at the latest position (at g_list_append() below) */
443 if (li->data && strcmp(s, li->data) == 0) {
444 filter_list = g_list_remove(filter_list, li->data);
450 filter_list = g_list_append(filter_list, s);
451 OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
452 gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
453 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(filter_cm)->entry), g_list_last(filter_list)->data);
459 /* write all non empty display filters (until maximum count)
460 * of the combo box GList to the user's recent file */
462 dfilter_recent_combo_write_all(FILE *rf) {
463 GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
464 GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
469 /* write all non empty display filter strings to the recent file (until max count) */
470 li = g_list_first(filter_list);
471 while ( li && (max_count++ <= dfilter_combo_max_recent) ) {
472 if (strlen(li->data)) {
473 fprintf (rf, RECENT_KEY_DISPLAY_FILTER ": %s\n", (char *)li->data);
479 /* empty the combobox entry field */
481 dfilter_combo_add_empty(void) {
482 GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
484 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(filter_cm)->entry), "");
488 /* add a display filter coming from the user's recent file to the dfilter combo box */
490 dfilter_combo_add_recent(gchar *s) {
491 GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
495 if (!dfilter_combo_add(filter_cm, dup)) {
504 /* call filter_packets() and add this filter string to the recent filter list */
506 main_filter_packets(capture_file *cf, const gchar *dftext, gboolean force)
508 GtkCombo *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY);
509 GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
511 gboolean add_filter = TRUE;
512 gboolean free_filter = TRUE;
514 gboolean filter_packets_ret;
516 s = g_strdup(dftext);
518 /* GtkCombos don't let us get at their list contents easily, so we maintain
519 our own filter list, and feed it to gtk_combo_set_popdown_strings when
520 a new filter is added. */
521 if ((filter_packets_ret = filter_packets(cf, s, force))) {
522 li = g_list_first(filter_list);
524 if (li->data && strcmp(s, li->data) == 0)
530 /* trim list size first */
531 while (g_list_length(filter_list) >= dfilter_combo_max_recent) {
532 filter_list = g_list_remove(filter_list, g_list_first(filter_list)->data);
536 filter_list = g_list_append(filter_list, s);
537 OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
538 gtk_combo_set_popdown_strings(filter_cm, filter_list);
539 gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
545 return filter_packets_ret;
549 /* Run the current display filter on the current packet set, and
552 filter_activate_cb(GtkWidget *w _U_, gpointer data)
556 s = gtk_entry_get_text(GTK_ENTRY(data));
558 main_filter_packets(&cfile, s, FALSE);
561 /* redisplay with no display filter */
563 filter_reset_cb(GtkWidget *w, gpointer data _U_)
565 GtkWidget *filter_te = NULL;
567 if ((filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY))) {
568 gtk_entry_set_text(GTK_ENTRY(filter_te), "");
570 main_filter_packets(&cfile, "", FALSE);
573 /* mark as reference time frame */
575 set_frame_reftime(gboolean set, frame_data *frame, gint row) {
579 frame->flags.ref_time=1;
581 frame->flags.ref_time=0;
583 reftime_packets(&cfile);
587 reftime_frame_cb(GtkWidget *w _U_, gpointer data _U_, REFTIME_ACTION_E action)
592 if (cfile.current_frame) {
593 /* XXX hum, should better have a "cfile->current_row" here ... */
594 set_frame_reftime(!cfile.current_frame->flags.ref_time,
596 packet_list_find_row_from_data(cfile.current_frame));
599 case REFTIME_FIND_NEXT:
600 find_previous_next_frame_with_filter("frame.ref_time", FALSE);
602 case REFTIME_FIND_PREV:
603 find_previous_next_frame_with_filter("frame.ref_time", TRUE);
608 #if GTK_MAJOR_VERSION < 2
610 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column _U_,
611 gpointer user_data _U_)
614 tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data _U_)
618 gchar *help_str = NULL;
619 gchar len_str[2+10+1+5+1]; /* ", {N} bytes\0",
621 gboolean has_blurb = FALSE;
622 guint length = 0, byte_len;
623 GtkWidget *byte_view;
624 const guint8 *byte_data;
625 #if GTK_MAJOR_VERSION >= 2
630 #if GTK_MAJOR_VERSION >= 2
631 /* if nothing is selected */
632 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
635 * Which byte view is displaying the current protocol tree
638 byte_view = get_notebook_bv_ptr(byte_nb_ptr);
639 if (byte_view == NULL)
642 byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
643 if (byte_data == NULL)
646 unselect_field(&cfile);
647 packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data,
648 cfile.current_frame, NULL, byte_len);
651 gtk_tree_model_get(model, &iter, 1, &finfo, -1);
654 finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
658 set_notebook_page(byte_nb_ptr, finfo->ds_tvb);
660 byte_view = get_notebook_bv_ptr(byte_nb_ptr);
661 byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
662 g_assert(byte_data != NULL);
664 cfile.finfo_selected = finfo;
665 set_menus_for_selected_tree_row(&cfile);
668 if (finfo->hfinfo->blurb != NULL &&
669 finfo->hfinfo->blurb[0] != '\0') {
671 length = strlen(finfo->hfinfo->blurb);
673 length = strlen(finfo->hfinfo->name);
675 if (finfo->length == 0) {
677 } else if (finfo->length == 1) {
678 strcpy (len_str, ", 1 byte");
680 g_snprintf (len_str, sizeof len_str, ", %d bytes", finfo->length);
682 statusbar_pop_field_msg(); /* get rid of current help msg */
684 help_str = g_strdup_printf("%s (%s)%s",
685 (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name,
686 finfo->hfinfo->abbrev, len_str);
687 statusbar_push_field_msg(help_str);
691 * Don't show anything if the field name is zero-length;
692 * the pseudo-field for "proto_tree_add_text()" is such
693 * a field, and we don't want "Text (text)" showing up
694 * on the status line if you've selected such a field.
696 * XXX - there are zero-length fields for which we *do*
697 * want to show the field name.
699 * XXX - perhaps the name and abbrev field should be null
700 * pointers rather than null strings for that pseudo-field,
701 * but we'd have to add checks for null pointers in some
702 * places if we did that.
704 * Or perhaps protocol tree items added with
705 * "proto_tree_add_text()" should have -1 as the field index,
706 * with no pseudo-field being used, but that might also
707 * require special checks for -1 to be added.
709 statusbar_push_field_msg("");
713 #if GTK_MAJOR_VERSION < 2
714 packet_hex_print(GTK_TEXT(byte_view), byte_data, cfile.current_frame,
717 packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data, cfile.current_frame,
722 #if GTK_MAJOR_VERSION < 2
724 tree_view_unselect_row_cb(GtkCTree *ctree _U_, GList *node _U_, gint column _U_,
725 gpointer user_data _U_)
727 GtkWidget *byte_view;
732 * Which byte view is displaying the current protocol tree
735 byte_view = get_notebook_bv_ptr(byte_nb_ptr);
736 if (byte_view == NULL)
739 data = get_byte_view_data_and_length(byte_view, &len);
743 unselect_field(&cfile);
744 packet_hex_print(GTK_TEXT(byte_view), data, cfile.current_frame,
749 void collapse_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
751 collapse_all_tree(cfile.edt->tree, tree_view);
754 void expand_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
756 expand_all_tree(cfile.edt->tree, tree_view);
759 void expand_tree_cb(GtkWidget *widget _U_, gpointer data _U_) {
760 #if GTK_MAJOR_VERSION < 2
766 #if GTK_MAJOR_VERSION < 2
767 node = gtk_ctree_find_by_row_data(GTK_CTREE(tree_view), NULL, cfile.finfo_selected);
769 gtk_ctree_expand_recursive(GTK_CTREE(tree_view), node);
771 path = tree_find_by_field_info(GTK_TREE_VIEW(tree_view), cfile.finfo_selected);
773 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view), path, TRUE);
774 gtk_tree_path_free(path);
778 void resolve_name_cb(GtkWidget *widget _U_, gpointer data _U_) {
779 if (cfile.edt->tree) {
780 guint32 tmp = g_resolv_flags;
781 g_resolv_flags = RESOLV_ALL;
782 proto_tree_draw(cfile.edt->tree, tree_view);
783 g_resolv_flags = tmp;
788 * Push a message referring to file access onto the statusbar.
791 statusbar_push_file_msg(gchar *msg)
793 gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg);
797 * Pop a message referring to file access off the statusbar.
800 statusbar_pop_file_msg(void)
802 gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
806 * XXX - do we need multiple statusbar contexts?
810 * Push a message referring to the currently-selected field onto the statusbar.
813 statusbar_push_field_msg(gchar *msg)
815 gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg);
819 * Pop a message referring to the currently-selected field off the statusbar.
822 statusbar_pop_field_msg(void)
824 gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
828 * update the packets statusbar to the current values
830 void packets_bar_update(void)
834 /* remove old status */
837 gtk_statusbar_pop(GTK_STATUSBAR(packets_bar), packets_ctx);
840 /* do we have any packets? */
842 packets_str = g_strdup_printf(" P: %u D: %u M: %u",
843 cfile.count, cfile.displayed_count, cfile.marked_count);
845 packets_str = g_strdup(" No Packets");
847 gtk_statusbar_push(GTK_STATUSBAR(packets_bar), packets_ctx, packets_str);
858 /* get the current geometry, before writing it to disk */
859 main_save_window_geometry(top_level);
861 /* write user's recent file to disk
862 * It is no problem to write this file, even if we do not quit */
863 write_recent(&rec_path);
865 /* XXX - should we check whether the capture file is an
866 unsaved temporary file for a live capture and, if so,
867 pop up a "do you want to exit without saving the capture
868 file?" dialog, and then just return, leaving said dialog
869 box to forcibly quit if the user clicks "OK"?
871 If so, note that this should be done in a subroutine that
872 returns TRUE if we do so, and FALSE otherwise, and if it
873 returns TRUE we should return TRUE without nuking anything.
875 Note that, if we do that, we might also want to check if
876 an "Update list of packets in real time" capture is in
877 progress and, if so, ask whether they want to terminate
878 the capture and discard it, and return TRUE, before nuking
879 any child capture, if they say they don't want to do so. */
882 /* Nuke any child capture in progress. */
883 kill_capture_child(capture_opts->sync_mode);
886 /* Are we in the middle of reading a capture? */
887 if (cfile.state == FILE_READ_IN_PROGRESS) {
888 /* Yes, so we can't just close the file and quit, as
889 that may yank the rug out from under the read in
890 progress; instead, just set the state to
891 "FILE_READ_ABORTED" and return - the code doing the read
892 will check for that and, if it sees that, will clean
894 cfile.state = FILE_READ_ABORTED;
896 /* Say that the window should *not* be deleted;
897 that'll be done by the code that cleans up. */
900 /* Close any capture file we have open; on some OSes, you
901 can't unlink a temporary capture file if you have it
903 "cf_close()" will unlink it after closing it if
904 it's a temporary file.
906 We do this here, rather than after the main loop returns,
907 as, after the main loop returns, the main window may have
908 been destroyed (if this is called due to a "destroy"
909 even on the main window rather than due to the user
910 selecting a menu item), and there may be a crash
911 or other problem when "cf_close()" tries to
912 clean up stuff in the main window.
914 XXX - is there a better place to put this?
915 Or should we have a routine that *just* closes the
916 capture file, and doesn't do anything with the UI,
917 which we'd call here, and another routine that
918 calls that routine and also cleans up the UI, which
919 we'd call elsewhere? */
922 /* Exit by leaving the main loop, so that any quit functions
923 we registered get called. */
926 /* Say that the window should be deleted. */
932 main_window_delete_event_cb(GtkWidget *widget _U_, GdkEvent *event _U_, gpointer data _U_)
936 if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
937 #if GTK_MAJOR_VERSION >= 2
938 gtk_window_present(GTK_WINDOW(top_level));
940 /* user didn't saved his current file, ask him */
941 dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
942 PRIMARY_TEXT_START "Save capture file before program quit?" PRIMARY_TEXT_END "\n\n"
943 "If you quit the program without saving, your capture data will be discarded.");
944 simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL);
947 /* unchanged file, just exit */
948 /* "main_do_quit()" indicates whether the main window should be deleted. */
949 return main_do_quit();
956 main_load_window_geometry(GtkWidget *widget)
958 window_geometry_t geom;
960 geom.set_pos = prefs.gui_geometry_save_position;
961 geom.x = recent.gui_geometry_main_x;
962 geom.y = recent.gui_geometry_main_y;
963 geom.set_size = prefs.gui_geometry_save_size;
964 if (recent.gui_geometry_main_width > 0 &&
965 recent.gui_geometry_main_height > 0) {
966 geom.width = recent.gui_geometry_main_width;
967 geom.height = recent.gui_geometry_main_height;
968 geom.set_maximized = prefs.gui_geometry_save_maximized;
970 /* We assume this means the width and height weren't set in
971 the "recent" file (or that there is no "recent" file),
972 and weren't set to a default value, so we don't set the
973 size. (The "recent" file code rejects non-positive width
974 and height values.) */
975 geom.set_size = FALSE;
977 geom.maximized = recent.gui_geometry_main_maximized;
979 window_set_geometry(widget, &geom);
981 #if GTK_MAJOR_VERSION >= 2
982 /* XXX - rename recent settings? */
983 if (recent.gui_geometry_main_upper_pane)
984 gtk_paned_set_position(GTK_PANED(main_first_pane), recent.gui_geometry_main_upper_pane);
985 if (recent.gui_geometry_main_lower_pane)
986 gtk_paned_set_position(GTK_PANED(main_second_pane), recent.gui_geometry_main_lower_pane);
987 if (recent.gui_geometry_status_pane)
988 gtk_paned_set_position(GTK_PANED(status_pane), recent.gui_geometry_status_pane);
994 main_save_window_geometry(GtkWidget *widget)
996 window_geometry_t geom;
998 window_get_geometry(widget, &geom);
1000 if (prefs.gui_geometry_save_position) {
1001 recent.gui_geometry_main_x = geom.x;
1002 recent.gui_geometry_main_y = geom.y;
1005 if (prefs.gui_geometry_save_size) {
1006 recent.gui_geometry_main_width = geom.width,
1007 recent.gui_geometry_main_height = geom.height;
1010 #if GTK_MAJOR_VERSION >= 2
1011 if(prefs.gui_geometry_save_maximized) {
1012 recent.gui_geometry_main_maximized = geom.maximized;
1015 recent.gui_geometry_main_upper_pane = gtk_paned_get_position(GTK_PANED(main_first_pane));
1016 recent.gui_geometry_main_lower_pane = gtk_paned_get_position(GTK_PANED(main_second_pane));
1017 recent.gui_geometry_status_pane = gtk_paned_get_position(GTK_PANED(status_pane));
1021 static void file_quit_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
1025 /* save file first */
1026 file_save_as_cmd(after_save_exit, NULL);
1028 case(ESD_BTN_DONT_SAVE):
1031 case(ESD_BTN_CANCEL):
1034 g_assert_not_reached();
1039 file_quit_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
1043 if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
1044 /* user didn't saved his current file, ask him */
1045 dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
1046 PRIMARY_TEXT_START "Save capture file before program quit?" PRIMARY_TEXT_END "\n\n"
1047 "If you quit the program without saving, your capture data will be discarded.");
1048 simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL);
1050 /* unchanged file, just exit */
1056 print_usage(gboolean print_ver) {
1062 fprintf(output, "This is GNU " PACKAGE " " VERSION
1067 comp_info_str->str, runtime_info_str->str);
1072 fprintf(output, "\n%s [ -vh ] [ -klLnpQS ] [ -a <capture autostop condition> ] ...\n",
1074 fprintf(output, "\t[ -b <number of ringbuffer files>[:<duration>] ]\n");
1075 fprintf(output, "\t[ -B <byte view height> ] [ -c <count> ] [ -f <capture filter> ]\n");
1076 fprintf(output, "\t[ -i <interface> ] [ -m <medium font> ] [ -N <resolving> ]\n");
1077 fprintf(output, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
1078 fprintf(output, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
1079 fprintf(output, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
1080 fprintf(output, "\t[ -w <savefile> ] [ -y <link type> ] [ -z <statistics string> ]\n");
1081 fprintf(output, "\t[ <infile> ]\n");
1083 fprintf(output, "\n%s [ -vh ] [ -n ] [ -B <byte view height> ] [ -m <medium font> ]\n",
1085 fprintf(output, "\t[ -N <resolving> ] [ -o <preference setting> ...\n");
1086 fprintf(output, "\t[ -P <packet list height> ] [ -r <infile> ] [ -R <read filter> ]\n");
1087 fprintf(output, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
1088 fprintf(output, "\t[ -z <statistics string> ] [ <infile> ]\n");
1099 printf(PACKAGE " " VERSION
1104 comp_info_str->str, runtime_info_str->str);
1108 get_natural_int(const char *string, const char *name)
1113 number = strtol(string, &p, 10);
1114 if (p == string || *p != '\0') {
1115 fprintf(stderr, "ethereal: The specified %s \"%s\" isn't a decimal number\n",
1120 fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
1124 if (number > INT_MAX) {
1125 fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
1126 name, string, INT_MAX);
1133 get_positive_int(const char *string, const char *name)
1137 number = get_natural_int(string, name);
1140 fprintf(stderr, "ethereal: The specified %s is zero\n",
1150 * Given a string of the form "<autostop criterion>:<value>", as might appear
1151 * as an argument to a "-a" option, parse it and set the criterion in
1152 * question. Return an indication of whether it succeeded or failed
1156 set_autostop_criterion(const char *autostoparg)
1160 colonp = strchr(autostoparg, ':');
1168 * Skip over any white space (there probably won't be any, but
1169 * as we allow it in the preferences file, we might as well
1172 while (isspace((guchar)*p))
1176 * Put the colon back, so if our caller uses, in an
1177 * error message, the string they passed us, the message
1183 if (strcmp(autostoparg,"duration") == 0) {
1184 capture_opts->has_autostop_duration = TRUE;
1185 capture_opts->autostop_duration = get_positive_int(p,"autostop duration");
1186 } else if (strcmp(autostoparg,"filesize") == 0) {
1187 capture_opts->has_autostop_filesize = TRUE;
1188 capture_opts->autostop_filesize = get_positive_int(p,"autostop filesize");
1192 *colonp = ':'; /* put the colon back */
1197 * Given a string of the form "<ring buffer file>:<duration>", as might appear
1198 * as an argument to a "-b" option, parse it and set the arguments in
1199 * question. Return an indication of whether it succeeded or failed
1203 get_ring_arguments(const char *arg)
1205 gchar *p = NULL, *colonp;
1207 colonp = strchr(arg, ':');
1209 if (colonp != NULL) {
1214 capture_opts->ring_num_files =
1215 get_natural_int(arg, "number of ring buffer files");
1221 * Skip over any white space (there probably won't be any, but
1222 * as we allow it in the preferences file, we might as well
1225 while (isspace((guchar)*p))
1229 * Put the colon back, so if our caller uses, in an
1230 * error message, the string they passed us, the message
1237 capture_opts->has_file_duration = TRUE;
1238 capture_opts->file_duration = get_positive_int(p,
1239 "ring buffer duration");
1241 *colonp = ':'; /* put the colon back */
1246 #if defined(_WIN32) || GTK_MAJOR_VERSION < 2 || ! defined USE_THREADS
1248 Once every 3 seconds we get a callback here which we use to update
1249 the tap extensions. Since Gtk1 is single threaded we dont have to
1250 worry about any locking or critical regions.
1253 update_cb(gpointer data _U_)
1255 draw_tap_listeners(FALSE);
1260 /* if these three functions are copied to gtk1 ethereal, since gtk1 does not
1261 use threads all updte_thread_mutex can be dropped and protect/unprotect
1262 would just be empty functions.
1264 This allows gtk2-rpcstat.c and friends to be copied unmodified to
1265 gtk1-ethereal and it will just work.
1267 static GStaticMutex update_thread_mutex = G_STATIC_MUTEX_INIT;
1269 update_thread(gpointer data _U_)
1273 g_get_current_time(&tv1);
1274 g_static_mutex_lock(&update_thread_mutex);
1275 gdk_threads_enter();
1276 draw_tap_listeners(FALSE);
1277 gdk_threads_leave();
1278 g_static_mutex_unlock(&update_thread_mutex);
1280 g_get_current_time(&tv2);
1281 if( ((tv1.tv_sec + 2) * 1000000 + tv1.tv_usec) >
1282 (tv2.tv_sec * 1000000 + tv2.tv_usec) ){
1283 g_usleep(((tv1.tv_sec + 2) * 1000000 + tv1.tv_usec) -
1284 (tv2.tv_sec * 1000000 + tv2.tv_usec));
1291 protect_thread_critical_region(void)
1293 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2 && defined USE_THREADS
1294 g_static_mutex_lock(&update_thread_mutex);
1298 unprotect_thread_critical_region(void)
1300 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2 && defined USE_THREADS
1301 g_static_mutex_unlock(&update_thread_mutex);
1305 /* structure to keep track of what tap listeners have been registered.
1307 typedef struct _ethereal_tap_list {
1308 struct _ethereal_tap_list *next;
1310 void (*func)(char *arg);
1311 } ethereal_tap_list;
1312 static ethereal_tap_list *tap_list=NULL;
1315 register_ethereal_tap(char *cmd, void (*func)(char *arg))
1317 ethereal_tap_list *newtl;
1319 newtl=malloc(sizeof(ethereal_tap_list));
1320 newtl->next=tap_list;
1328 enum { DND_TARGET_STRING, DND_TARGET_ROOTWIN, DND_TARGET_URL };
1330 /* convert drag and drop URI to a local filename */
1332 dnd_uri2filename(gchar *cf_name)
1341 * Remove URI header.
1342 * On win32 (at least WinXP), this string looks like (UNC or local filename):
1343 * file:////servername/sharename/dir1/dir2/capture-file.cap
1345 * file:///d:/dir1/dir2/capture-file.cap
1346 * we have to remove the prefix to get a valid filename.
1348 * On UNIX (at least KDE 3.0 Konqueror), this string looks like:
1349 * file:/dir1/dir2/capture-file.cap
1350 * we have to remove the file: to get a valid filename.
1352 if (strncmp("file:////", cf_name, 9) == 0) {
1353 /* win32 UNC: now becoming: //servername/sharename/dir1/dir2/capture-file.cap */
1355 } else if (strncmp("file:///", cf_name, 8) == 0) {
1356 /* win32 local: now becoming: d:/dir1/dir2/capture-file.cap */
1358 } else if (strncmp("file:", cf_name, 5) == 0) {
1359 /* unix local: now becoming: /dir1/dir2/capture-file.cap */
1364 * unescape the escaped URI characters (spaces, ...)
1366 * we have to replace escaped chars to their equivalents,
1367 * e.g. %20 (always a two digit hexstring) -> ' '
1368 * the percent character '%' is escaped be a double one "%%"
1370 * we do this conversation "in place" as the result is always
1371 * equal or smaller in size.
1379 /* this is an escaped '%' char (was: "%%") */
1384 /* convert escaped hexnumber to unscaped character */
1388 ret = sscanf(esc, "%x", &i);
1394 /* somethings wrong, just jump over that char
1395 * this will result in a wrong string, but we might get
1396 * user feedback and can fix it later ;-) */
1412 dnd_merge_files(int in_file_count, char **in_filenames)
1417 char tmpname[128+1];
1420 out_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
1422 /* merge the files in chonological order */
1423 merge_ok = cf_merge_files(tmpname, out_fd, in_file_count, in_filenames,
1424 WTAP_FILE_PCAP, FALSE);
1428 close(out_fd); /* XXX - isn't it already closed? */
1434 /* Try to open the merged capture file. */
1435 if ((err = cf_open(tmpname, TRUE /* temporary file */, &cfile)) != 0) {
1436 /* We couldn't open it; don't dismiss the open dialog box,
1437 just leave it around so that the user can, after they
1438 dismiss the alert box popped up for the open error,
1443 switch (cf_read(&cfile)) {
1447 /* Just because we got an error, that doesn't mean we were unable
1448 to read any of the file; we handle what we could get from the
1453 /* The user bailed out of re-reading the capture file; the
1454 capture file has been closed - just free the capture file name
1455 string and return (without changing the last containing
1460 gtk_widget_grab_focus(packet_list);
1463 /* open/merge the dnd file */
1465 dnd_open_file_cmd(GtkSelectionData *selection_data)
1468 gchar *cf_name, *cf_name_freeme;
1471 GString *dialog_text;
1473 char **in_filenames;
1476 /* DND_TARGET_URL on Win32:
1477 * The selection_data->data is a single string, containing one or more URI's,
1478 * seperated by CR/NL chars. The length of the whole field can be found
1479 * in the selection_data->length field. If it contains one file, simply open it,
1480 * If it contains more than one file, ask to merge these files. */
1482 /* the data string is not zero terminated -> make a zero terminated "copy" of it */
1483 cf_name_freeme = g_malloc(selection_data->length + 1);
1484 memcpy(cf_name_freeme, selection_data->data, selection_data->length);
1485 cf_name_freeme[selection_data->length] = '\0';
1487 /* count the number of input files */
1488 cf_name = cf_name_freeme;
1489 for(in_files = 0; (cf_name = strstr(cf_name, "\r\n")) != NULL; ) {
1494 in_filenames = g_malloc(sizeof(char*) * in_files);
1496 /* store the starts of the file entries in a gchar array */
1497 cf_name = cf_name_freeme;
1498 in_filenames[0] = cf_name;
1499 for(files_work = 1; (cf_name = strstr(cf_name, "\r\n")) != NULL && files_work < in_files; ) {
1501 in_filenames[files_work] = cf_name;
1505 /* replace trailing CR NL simply with zeroes (in place), so we get valid terminated strings */
1506 cf_name = cf_name_freeme;
1507 g_strdelimit(cf_name, "\r\n", '\0');
1509 /* convert all filenames from URI to local filename (in place) */
1510 for(files_work = 0; files_work < in_files; files_work++) {
1511 in_filenames[files_work] = dnd_uri2filename(in_filenames[files_work]);
1516 /* shouldn't happen */
1519 /* open and read the capture file (this will close an existing file) */
1520 if ((err = cf_open(in_filenames[0], FALSE, &cfile)) == 0) {
1522 add_menu_recent_capture_file(in_filenames[0]);
1524 /* the capture file couldn't be read (doesn't exist, file format unknown, ...) */
1528 /* build and show the info dialog */
1529 dialog_text = g_string_sized_new(200);
1530 g_string_append(dialog_text, PRIMARY_TEXT_START
1531 "Merging the following files:" PRIMARY_TEXT_END "\n\n");
1532 for(files_work = 0; files_work < in_files; files_work++) {
1533 g_string_append(dialog_text, in_filenames[files_work]);
1534 g_string_append(dialog_text, "\n");
1536 g_string_append(dialog_text, "\nThe packets in these files will be merged chronologically into a new temporary file.");
1537 dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
1540 g_string_free(dialog_text, TRUE);
1542 /* actually merge the files now */
1543 dnd_merge_files(in_files, in_filenames);
1546 g_free(in_filenames);
1547 g_free(cf_name_freeme);
1550 /* ask the user to save current unsaved file, before opening the dnd file */
1552 dnd_save_file_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
1556 /* save file first */
1557 file_save_as_cmd(after_save_open_dnd_file, data);
1559 case(ESD_BTN_DONT_SAVE):
1561 dnd_open_file_cmd(data);
1563 case(ESD_BTN_CANCEL):
1566 g_assert_not_reached();
1571 /* we have received some drag and drop data */
1572 /* (as we only registered to "text/uri-list", we will only get a file list here) */
1574 dnd_data_received(GtkWidget *widget _U_, GdkDragContext *dc _U_, gint x _U_, gint y _U_,
1575 GtkSelectionData *selection_data, guint info, guint t _U_, gpointer data _U_)
1579 if (info == DND_TARGET_URL) {
1580 /* ask the user to save it's current capture file first */
1581 if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
1582 /* user didn't saved his current file, ask him */
1583 dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
1584 ESD_BTNS_SAVE_DONTSAVE_CANCEL,
1585 PRIMARY_TEXT_START "Save capture file before opening a new one?" PRIMARY_TEXT_END "\n\n"
1586 "If you open a new capture file without saving, your current capture data will be discarded.");
1587 simple_dialog_set_cb(dialog, dnd_save_file_answered_cb, selection_data);
1589 /* unchanged file */
1590 dnd_open_file_cmd(selection_data);
1595 /* init the drag and drop functionality */
1597 dnd_init(GtkWidget *w)
1599 /* we are only interested in the URI list containing filenames */
1600 static GtkTargetEntry target_entry[] = {
1601 /*{"STRING", 0, DND_TARGET_STRING},*/
1602 /*{"text/plain", 0, DND_TARGET_STRING},*/
1603 {"text/uri-list", 0, DND_TARGET_URL}
1606 /* set this window as a dnd destination */
1608 w, GTK_DEST_DEFAULT_ALL, target_entry,
1609 sizeof(target_entry) / sizeof(GtkTargetEntry),
1610 (GdkDragAction)(GDK_ACTION_MOVE | GDK_ACTION_COPY) );
1612 /* get notified, if some dnd coming in */
1613 gtk_signal_connect(GTK_OBJECT(w), "drag_data_received",
1614 GTK_SIGNAL_FUNC(dnd_data_received), NULL);
1618 /* And now our feature presentation... [ fade to music ] */
1620 main(int argc, char *argv[])
1623 const char *command_name;
1628 extern char *optarg;
1629 gboolean arg_error = FALSE;
1637 char *gpf_path, *pf_path;
1638 char *cf_path, *df_path;
1639 char *gdp_path, *dp_path;
1640 int gpf_open_errno, gpf_read_errno;
1641 int pf_open_errno, pf_read_errno;
1642 int cf_open_errno, df_open_errno;
1643 int gdp_open_errno, gdp_read_errno;
1644 int dp_open_errno, dp_read_errno;
1647 gboolean start_capture = FALSE;
1648 gchar *save_file = NULL;
1651 GList *lt_list, *lt_entry;
1652 data_link_info_t *data_link_info;
1653 gchar err_str[PCAP_ERRBUF_SIZE];
1654 gchar *cant_get_if_list_errstr;
1655 gboolean stats_known;
1656 struct pcap_stat stats;
1658 gboolean capture_option_specified = FALSE;
1660 gint pl_size = 280, tv_size = 95, bv_size = 75;
1661 gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
1662 dfilter_t *rfcode = NULL;
1663 gboolean rfilter_parse_failed = FALSE;
1666 ethereal_tap_list *tli = NULL;
1667 gchar *tap_opt = NULL;
1668 GtkWidget *splash_win = NULL;
1670 #define OPTSTRING_INIT "a:b:B:c:f:Hhi:klLm:nN:o:pP:Qr:R:Ss:t:T:w:vy:z:"
1674 #define OPTSTRING_CHILD "W:Z:"
1676 #define OPTSTRING_CHILD "W:"
1679 #define OPTSTRING_CHILD ""
1680 #endif /* HAVE_LIBPCAP */
1682 char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_CHILD) - 1] =
1686 /* Set the current locale according to the program environment.
1687 * We haven't localized anything, but some GTK widgets are localized
1688 * (the file selection dialogue, for example).
1689 * This also sets the C-language locale to the native environment. */
1692 /* Let GTK get its args */
1693 gtk_init (&argc, &argv);
1695 #if GTK_MAJOR_VERSION < 2 && GTK_MINOR_VERSION < 3
1696 /* initialize our GTK eth_clist_type */
1697 init_eth_clist_type();
1700 ethereal_path = argv[0];
1703 /* Arrange that if we have no console window, and a GLib message logging
1704 routine is called to log a message, we pop up a console window.
1706 We do that by inserting our own handler for all messages logged
1707 to the default domain; that handler pops up a console if necessary,
1708 and then calls the default handler. */
1709 g_log_set_handler(NULL,
1711 G_LOG_LEVEL_CRITICAL|
1712 G_LOG_LEVEL_WARNING|
1713 G_LOG_LEVEL_MESSAGE|
1716 G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION,
1717 console_log_handler, NULL);
1721 command_name = get_basename(ethereal_path);
1722 /* Set "capture_child" to indicate whether this is going to be a child
1723 process for a "-S" capture. */
1724 capture_opts->capture_child = (strcmp(command_name, CHILD_NAME) == 0);
1725 /* We want a splash screen only if we're not a child process */
1726 if (capture_opts->capture_child) {
1727 strcat(optstring, OPTSTRING_CHILD);
1731 /* We also want it only if we're not being run with "-G".
1732 XXX - we also don't want it if we're being run with
1733 "-h" or "-v", as those are options to run Ethereal and just
1734 have it print stuff to the command line. That would require
1735 that we parse the argument list before putting up the splash
1736 screen, which means we'd need to do so before reading the
1737 preference files, as that could take enough time that we'd
1738 want the splash screen up while we're doing that. Unfortunately,
1739 that means we'd have to queue up, for example, "-o" options,
1740 so that we apply them *after* reading the preferences, as
1741 they're supposed to override saved preferences. */
1742 if (argc < 2 || strcmp(argv[1], "-G") != 0) {
1743 splash_win = splash_new("Loading Ethereal ...");
1747 splash_update(splash_win, "Registering dissectors ...");
1749 /* Register all dissectors; we must do this before checking for the
1750 "-G" flag, as the "-G" flag dumps information registered by the
1751 dissectors, and we must do it before we read the preferences, in
1752 case any dissectors register preferences. */
1753 epan_init(PLUGIN_DIR,register_all_protocols,register_all_protocol_handoffs,
1754 failure_alert_box,open_failure_alert_box,read_failure_alert_box);
1756 splash_update(splash_win, "Registering tap listeners ...");
1758 /* Register all tap listeners; we do this before we parse the arguments,
1759 as the "-z" argument can specify a registered tap. */
1760 register_all_tap_listeners();
1762 splash_update(splash_win, "Loading module preferences ...");
1764 /* Now register the preferences for any non-dissector modules.
1765 We must do that before we read the preferences as well. */
1766 prefs_register_modules();
1768 /* If invoked with the "-G" flag, we dump out information based on
1769 the argument to the "-G" flag; if no argument is specified,
1770 for backwards compatibility we dump out a glossary of display
1773 We must do this before calling "gtk_init()", because "gtk_init()"
1774 tries to open an X display, and we don't want to have to do any X
1775 stuff just to do a build.
1777 Given that we call "gtk_init()" before doing the regular argument
1778 list processing, so that it can handle X and GTK+ arguments and
1779 remove them from the list at which we look, this means we must do
1780 this before doing the regular argument list processing, as well.
1784 you must give the "-G" flag as the first flag on the command line;
1786 you must give it as "-G", nothing more, nothing less;
1788 the first argument after the "-G" flag, if present, will be used
1789 to specify the information to dump;
1791 arguments after that will not be used. */
1792 handle_dashG_option(argc, argv, "ethereal");
1794 /* multithread support currently doesn't seem to work in win32 gtk2.0.6 */
1795 #if !defined(_WIN32) && GTK_MAJOR_VERSION >= 2 && defined(G_THREADS_ENABLED) && defined USE_THREADS
1798 g_thread_init(NULL);
1800 ut=g_thread_create(update_thread, NULL, FALSE, NULL);
1801 g_thread_set_priority(ut, G_THREAD_PRIORITY_LOW);
1803 #else /* _WIN32 || GTK1.2 || !G_THREADS_ENABLED || !USE_THREADS */
1804 /* this is to keep tap extensions updating once every 3 seconds */
1805 gtk_timeout_add(3000, (GtkFunction)update_cb,(gpointer)NULL);
1806 #endif /* !_WIN32 && GTK2 && G_THREADS_ENABLED */
1809 gtk_timeout_add(750, (GtkFunction) host_name_lookup_process, NULL);
1812 splash_update(splash_win, "Loading configuration files ...");
1814 /* Read the preference files. */
1815 prefs = read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path,
1816 &pf_open_errno, &pf_read_errno, &pf_path);
1817 if (gpf_path != NULL) {
1818 if (gpf_open_errno != 0) {
1819 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1820 "Could not open global preferences file\n\"%s\": %s.", gpf_path,
1821 strerror(gpf_open_errno));
1823 if (gpf_read_errno != 0) {
1824 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1825 "I/O error reading global preferences file\n\"%s\": %s.", gpf_path,
1826 strerror(gpf_read_errno));
1829 if (pf_path != NULL) {
1830 if (pf_open_errno != 0) {
1831 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1832 "Could not open your preferences file\n\"%s\": %s.", pf_path,
1833 strerror(pf_open_errno));
1835 if (pf_read_errno != 0) {
1836 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1837 "I/O error reading your preferences file\n\"%s\": %s.", pf_path,
1838 strerror(pf_read_errno));
1845 if (prefs->gui_console_open == console_open_always) {
1851 capture_opts->has_snaplen = FALSE;
1852 capture_opts->snaplen = MIN_PACKET_SIZE;
1853 capture_opts->linktype = -1;
1855 capture_opts->buffer_size = 1;
1857 capture_opts->quit_after_cap = FALSE;
1859 capture_opts->has_autostop_packets = FALSE;
1860 capture_opts->autostop_packets = 1;
1861 capture_opts->has_autostop_duration = FALSE;
1862 capture_opts->autostop_duration = 60 /* 1 min */;
1863 capture_opts->has_autostop_filesize = FALSE;
1864 capture_opts->autostop_filesize = 1024 * 1024 /* 1 MB */;
1865 capture_opts->has_autostop_files = FALSE;
1866 capture_opts->autostop_files = 1;
1868 capture_opts->multi_files_on = FALSE;
1869 capture_opts->has_ring_num_files = TRUE;
1870 capture_opts->ring_num_files = 2;
1871 capture_opts->has_file_duration = FALSE;
1872 capture_opts->file_duration = 60 /* 1 min */;
1874 /* If this is a capture child process, it should pay no attention
1875 to the "prefs.capture_prom_mode" setting in the preferences file;
1876 it should do what the parent process tells it to do, and if
1877 the parent process wants it not to run in promiscuous mode, it'll
1878 tell it so with a "-p" flag.
1880 Otherwise, set promiscuous mode from the preferences setting. */
1881 /* the same applies to other preferences settings as well. */
1882 if (capture_opts->capture_child) {
1883 capture_opts->promisc_mode = TRUE; /* maybe changed by command line below */
1884 capture_opts->show_info = TRUE; /* maybe changed by command line below */
1885 capture_opts->sync_mode = TRUE; /* always true in child process */
1886 auto_scroll_live = FALSE; /* doesn't matter in child process */
1889 capture_opts->promisc_mode = prefs->capture_prom_mode;
1890 capture_opts->show_info = prefs->capture_show_info;
1891 capture_opts->sync_mode = prefs->capture_real_time;
1892 auto_scroll_live = prefs->capture_auto_scroll;
1895 #endif /* HAVE_LIBPCAP */
1897 /* Set the name resolution code's flags from the preferences. */
1898 g_resolv_flags = prefs->name_resolve;
1900 /* Read the capture filter file. */
1901 read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
1902 if (cf_path != NULL) {
1903 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1904 "Could not open your capture filter file\n\"%s\": %s.", cf_path,
1905 strerror(cf_open_errno));
1909 /* Read the display filter file. */
1910 read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
1911 if (df_path != NULL) {
1912 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1913 "Could not open your display filter file\n\"%s\": %s.", df_path,
1914 strerror(df_open_errno));
1918 /* Read the disabled protocols file. */
1919 read_disabled_protos_list(&gdp_path, &gdp_open_errno, &gdp_read_errno,
1920 &dp_path, &dp_open_errno, &dp_read_errno);
1921 if (gdp_path != NULL) {
1922 if (gdp_open_errno != 0) {
1923 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1924 "Could not open global disabled protocols file\n\"%s\": %s.",
1925 gdp_path, strerror(gdp_open_errno));
1927 if (gdp_read_errno != 0) {
1928 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1929 "I/O error reading global disabled protocols file\n\"%s\": %s.",
1930 gdp_path, strerror(gdp_read_errno));
1934 if (dp_path != NULL) {
1935 if (dp_open_errno != 0) {
1936 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1937 "Could not open your disabled protocols file\n\"%s\": %s.", dp_path,
1938 strerror(dp_open_errno));
1940 if (dp_read_errno != 0) {
1941 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1942 "I/O error reading your disabled protocols file\n\"%s\": %s.", dp_path,
1943 strerror(dp_read_errno));
1948 init_cap_file(&cfile);
1951 /* Load wpcap if possible. Do this before collecting the run-time version information */
1954 /* Start windows sockets */
1955 WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
1958 /* Assemble the compile-time version information string */
1959 comp_info_str = g_string_new("Compiled ");
1960 g_string_append(comp_info_str, "with ");
1961 g_string_sprintfa(comp_info_str,
1962 #ifdef GTK_MAJOR_VERSION
1963 "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
1966 "GTK+ (version unknown)");
1969 g_string_append(comp_info_str, ", ");
1970 get_compiled_version_info(comp_info_str);
1972 /* Assemble the run-time version information string */
1973 runtime_info_str = g_string_new("Running ");
1974 get_runtime_version_info(runtime_info_str);
1976 /* Now get our args */
1977 while ((opt = getopt(argc, argv, optstring)) != -1) {
1979 case 'a': /* autostop criteria */
1981 if (set_autostop_criterion(optarg) == FALSE) {
1982 fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
1986 capture_option_specified = TRUE;
1990 case 'b': /* Ringbuffer option */
1992 capture_opts->multi_files_on = TRUE;
1993 capture_opts->has_ring_num_files = TRUE;
1994 if (get_ring_arguments(optarg) == FALSE) {
1995 fprintf(stderr, "ethereal: Invalid or unknown -b arg \"%s\"\n", optarg);
1999 capture_option_specified = TRUE;
2003 case 'B': /* Byte view pane height */
2004 bv_size = get_positive_int(optarg, "byte view pane height");
2006 case 'c': /* Capture xxx packets */
2008 capture_opts->has_autostop_packets = TRUE;
2009 capture_opts->autostop_packets = get_positive_int(optarg, "packet count");
2011 capture_option_specified = TRUE;
2018 g_free(cfile.cfilter);
2019 cfile.cfilter = g_strdup(optarg);
2021 capture_option_specified = TRUE;
2025 case 'h': /* Print help and exit */
2029 case 'i': /* Use interface xxx */
2031 cfile.iface = g_strdup(optarg);
2033 capture_option_specified = TRUE;
2037 case 'k': /* Start capture immediately */
2039 start_capture = TRUE;
2041 capture_option_specified = TRUE;
2045 case 'l': /* Automatic scrolling in live capture mode */
2047 auto_scroll_live = TRUE;
2049 capture_option_specified = TRUE;
2053 case 'H': /* Hide capture info dialog box */
2055 capture_opts->show_info = FALSE;
2057 capture_option_specified = TRUE;
2061 case 'L': /* Print list of link-layer types and exit */
2063 list_link_layer_types = TRUE;
2065 capture_option_specified = TRUE;
2069 case 'm': /* Fixed-width font for the display */
2070 if (prefs->PREFS_GUI_FONT_NAME != NULL)
2071 g_free(prefs->PREFS_GUI_FONT_NAME);
2072 prefs->PREFS_GUI_FONT_NAME = g_strdup(optarg);
2074 case 'n': /* No name resolution */
2075 g_resolv_flags = RESOLV_NONE;
2077 case 'N': /* Select what types of addresses/port #s to resolve */
2078 if (g_resolv_flags == RESOLV_ALL)
2079 g_resolv_flags = RESOLV_NONE;
2080 badopt = string_to_name_resolve(optarg, &g_resolv_flags);
2081 if (badopt != '\0') {
2082 fprintf(stderr, "ethereal: -N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'\n",
2087 case 'o': /* Override preference from command line */
2088 switch (prefs_set_pref(optarg)) {
2090 case PREFS_SET_SYNTAX_ERR:
2091 fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
2095 case PREFS_SET_NO_SUCH_PREF:
2096 case PREFS_SET_OBSOLETE:
2097 fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
2103 case 'p': /* Don't capture in promiscuous mode */
2105 capture_opts->promisc_mode = FALSE;
2107 capture_option_specified = TRUE;
2111 case 'P': /* Packet list pane height */
2112 pl_size = get_positive_int(optarg, "packet list pane height");
2114 case 'Q': /* Quit after capture (just capture to file) */
2116 capture_opts->quit_after_cap = TRUE;
2117 start_capture = TRUE; /*** -Q implies -k !! ***/
2119 capture_option_specified = TRUE;
2123 case 'r': /* Read capture file xxx */
2124 /* We may set "last_open_dir" to "cf_name", and if we change
2125 "last_open_dir" later, we free the old value, so we have to
2126 set "cf_name" to something that's been allocated. */
2127 cf_name = g_strdup(optarg);
2129 case 'R': /* Read file filter */
2132 case 's': /* Set the snapshot (capture) length */
2134 capture_opts->has_snaplen = TRUE;
2135 capture_opts->snaplen = get_positive_int(optarg, "snapshot length");
2137 capture_option_specified = TRUE;
2141 case 'S': /* "Sync" mode: used for following file ala tail -f */
2143 capture_opts->sync_mode = TRUE;
2145 capture_option_specified = TRUE;
2149 case 't': /* Time stamp type */
2150 if (strcmp(optarg, "r") == 0)
2151 set_timestamp_setting(TS_RELATIVE);
2152 else if (strcmp(optarg, "a") == 0)
2153 set_timestamp_setting(TS_ABSOLUTE);
2154 else if (strcmp(optarg, "ad") == 0)
2155 set_timestamp_setting(TS_ABSOLUTE_WITH_DATE);
2156 else if (strcmp(optarg, "d") == 0)
2157 set_timestamp_setting(TS_DELTA);
2159 fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
2161 fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
2162 fprintf(stderr, "\"ad\" for absolute with date, or \"d\" for delta.\n");
2166 case 'T': /* Tree view pane height */
2167 tv_size = get_positive_int(optarg, "tree view pane height");
2169 case 'v': /* Show version and exit */
2176 case 'w': /* Write to capture file xxx */
2178 save_file = g_strdup(optarg);
2180 capture_option_specified = TRUE;
2184 case 'y': /* Set the pcap data link type */
2186 #ifdef HAVE_PCAP_DATALINK_NAME_TO_VAL
2187 capture_opts->linktype = pcap_datalink_name_to_val(optarg);
2188 if (capture_opts->linktype == -1) {
2189 fprintf(stderr, "ethereal: The specified data link type \"%s\" isn't valid\n",
2193 #else /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
2194 /* XXX - just treat it as a number */
2195 capture_opts->linktype = get_natural_int(optarg, "data link type");
2196 #endif /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
2197 #else /* HAVE_LIBPCAP */
2198 capture_option_specified = TRUE;
2200 #endif /* HAVE_LIBPCAP */
2203 /* This is a hidden option supporting Sync mode, so we don't set
2204 * the error flags for the user in the non-libpcap case.
2206 case 'W': /* Write to capture file FD xxx */
2207 cfile.save_file_fd = atoi(optarg);
2211 for(tli=tap_list;tli;tli=tli->next){
2212 if(!strncmp(tli->cmd,optarg,strlen(tli->cmd))){
2213 tap_opt = g_strdup(optarg);
2218 fprintf(stderr,"ethereal: invalid -z argument.\n");
2219 fprintf(stderr," -z argument must be one of :\n");
2220 for(tli=tap_list;tli;tli=tli->next){
2221 fprintf(stderr," %s\n",tli->cmd);
2229 /* Hidden option supporting Sync mode */
2230 case 'Z': /* Write to pipe FD XXX */
2231 /* associate stdout with pipe */
2233 if (dup2(i, 1) < 0) {
2234 fprintf(stderr, "Unable to dup pipe handle\n");
2238 #endif /* HAVE_LIBPCAP */
2242 case '?': /* Bad flag - print usage message */
2250 if (cf_name != NULL) {
2252 * Input file name specified with "-r" *and* specified as a regular
2253 * command-line argument.
2258 * Input file name not specified with "-r", and a command-line argument
2259 * was specified; treat it as the input file name.
2261 * Yes, this is different from tethereal, where non-flag command-line
2262 * arguments are a filter, but this works better on GUI desktops
2263 * where a command can be specified to be run to open a particular
2264 * file - yes, you could have "-r" as the last part of the command,
2265 * but that's a bit ugly.
2267 cf_name = g_strdup(argv[0]);
2275 * Extra command line arguments were specified; complain.
2277 fprintf(stderr, "Invalid argument: %s\n", argv[0]);
2281 #ifndef HAVE_LIBPCAP
2282 if (capture_option_specified)
2283 fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
2291 if (start_capture && list_link_layer_types) {
2292 /* Specifying *both* is bogus. */
2293 fprintf(stderr, "ethereal: You can't specify both -L and a live capture.\n");
2297 if (list_link_layer_types) {
2298 /* We're supposed to list the link-layer types for an interface;
2299 did the user also specify a capture file to be read? */
2301 /* Yes - that's bogus. */
2302 fprintf(stderr, "ethereal: You can't specify -L and a capture file to be read.\n");
2305 /* No - did they specify a ring buffer option? */
2306 if (capture_opts->multi_files_on) {
2307 fprintf(stderr, "ethereal: Ring buffer requested, but a capture isn't being done.\n");
2311 /* We're supposed to do a live capture; did the user also specify
2312 a capture file to be read? */
2313 if (start_capture && cf_name) {
2314 /* Yes - that's bogus. */
2315 fprintf(stderr, "ethereal: You can't specify both a live capture and a capture file to be read.\n");
2319 /* No - was the ring buffer option specified and, if so, does it make
2321 if (capture_opts->multi_files_on) {
2322 /* Ring buffer works only under certain conditions:
2323 a) ring buffer does not work with temporary files;
2324 b) sync_mode and capture_opts->ringbuffer_on are mutually exclusive -
2325 sync_mode takes precedence;
2326 c) it makes no sense to enable the ring buffer if the maximum
2327 file size is set to "infinite". */
2328 if (save_file == NULL) {
2329 fprintf(stderr, "ethereal: Ring buffer requested, but capture isn't being saved to a permanent file.\n");
2330 capture_opts->multi_files_on = FALSE;
2332 if (capture_opts->sync_mode) {
2333 fprintf(stderr, "ethereal: Ring buffer requested, but an \"Update list of packets in real time\" capture is being done.\n");
2334 capture_opts->multi_files_on = FALSE;
2336 if (!capture_opts->has_autostop_filesize) {
2337 fprintf(stderr, "ethereal: Ring buffer requested, but no maximum capture file size was specified.\n");
2338 capture_opts->multi_files_on = FALSE;
2343 if (start_capture || list_link_layer_types) {
2344 /* Did the user specify an interface to use? */
2345 if (cfile.iface == NULL) {
2346 /* No - is a default specified in the preferences file? */
2347 if (prefs->capture_device != NULL) {
2349 cfile.iface = g_strdup(prefs->capture_device);
2351 /* No - pick the first one from the list of interfaces. */
2352 if_list = get_interface_list(&err, err_str);
2353 if (if_list == NULL) {
2356 case CANT_GET_INTERFACE_LIST:
2357 cant_get_if_list_errstr = cant_get_if_list_error_message(err_str);
2358 fprintf(stderr, "%s\n", cant_get_if_list_errstr);
2359 g_free(cant_get_if_list_errstr);
2362 case NO_INTERFACES_FOUND:
2363 fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
2368 if_info = if_list->data; /* first interface */
2369 cfile.iface = g_strdup(if_info->name);
2370 free_interface_list(if_list);
2375 if (capture_opts->capture_child) {
2376 if (cfile.save_file_fd == -1) {
2377 /* XXX - send this to the standard output as something our parent
2378 should put in an error message box? */
2379 fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
2384 if (list_link_layer_types) {
2385 /* Get the list of link-layer types for the capture device. */
2386 lt_list = get_pcap_linktype_list(cfile.iface, err_str);
2387 if (lt_list == NULL) {
2388 if (err_str[0] != '\0') {
2389 fprintf(stderr, "ethereal: The list of data link types for the capture device could not be obtained (%s).\n"
2390 "Please check to make sure you have sufficient permissions, and that\n"
2391 "you have the proper interface or pipe specified.\n", err_str);
2393 fprintf(stderr, "ethereal: The capture device has no data link types.\n");
2396 fprintf(stderr, "Data link types (use option -y to set):\n");
2397 for (lt_entry = lt_list; lt_entry != NULL;
2398 lt_entry = g_list_next(lt_entry)) {
2399 data_link_info = lt_entry->data;
2400 fprintf(stderr, " %s", data_link_info->name);
2401 if (data_link_info->description != NULL)
2402 fprintf(stderr, " (%s)", data_link_info->description);
2404 fprintf(stderr, " (not supported)");
2407 free_pcap_linktype_list(lt_list);
2413 /* Notify all registered modules that have had any of their preferences
2414 changed either from one of the preferences file or from the command
2415 line that their preferences have changed. */
2418 /* disabled protocols as per configuration file */
2419 if (gdp_path == NULL && dp_path == NULL) {
2420 set_disabled_protos_list();
2423 /* Build the column format array */
2424 col_setup(&cfile.cinfo, prefs->num_cols);
2425 for (i = 0; i < cfile.cinfo.num_cols; i++) {
2426 cfile.cinfo.col_fmt[i] = get_column_format(i);
2427 cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
2428 cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
2430 get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
2431 cfile.cinfo.col_data[i] = NULL;
2432 if (cfile.cinfo.col_fmt[i] == COL_INFO)
2433 cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
2435 cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2436 cfile.cinfo.col_fence[i] = 0;
2437 cfile.cinfo.col_expr[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2438 cfile.cinfo.col_expr_val[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2441 for (i = 0; i < cfile.cinfo.num_cols; i++) {
2444 for (j = 0; j < NUM_COL_FMTS; j++) {
2445 if (!cfile.cinfo.fmt_matx[i][j])
2448 if (cfile.cinfo.col_first[j] == -1)
2449 cfile.cinfo.col_first[j] = i;
2450 cfile.cinfo.col_last[j] = i;
2455 if (capture_opts->has_snaplen) {
2456 if (capture_opts->snaplen < 1)
2457 capture_opts->snaplen = WTAP_MAX_PACKET_SIZE;
2458 else if (capture_opts->snaplen < MIN_PACKET_SIZE)
2459 capture_opts->snaplen = MIN_PACKET_SIZE;
2462 /* Check the value range of the ringbuffer_num_files parameter */
2463 if (capture_opts->ring_num_files > RINGBUFFER_MAX_NUM_FILES)
2464 capture_opts->ring_num_files = RINGBUFFER_MAX_NUM_FILES;
2465 #if RINGBUFFER_MIN_NUM_FILES > 0
2466 else if (capture_opts->num_files < RINGBUFFER_MIN_NUM_FILES)
2467 capture_opts->ring_num_files = RINGBUFFER_MIN_NUM_FILES;
2471 /* read in rc file from global and personal configuration paths. */
2472 /* XXX - is this a good idea? */
2473 gtk_rc_parse(RC_FILE);
2474 rc_file = get_persconffile_path(RC_FILE, FALSE);
2475 gtk_rc_parse(rc_file);
2478 font_init(capture_opts->capture_child);
2483 /* close the splash screen, as we are going to open the main window now */
2484 splash_destroy(splash_win);
2487 /* Is this a "child" ethereal, which is only supposed to pop up a
2488 capture box to let us stop the capture, and run a capture
2489 to a file that our parent will read? */
2490 if (!capture_opts->capture_child) {
2492 /* No. Pop up the main window, and read in a capture file if
2494 create_main_window(pl_size, tv_size, bv_size, prefs);
2496 /* Read the recent file, as we have the gui now ready for it. */
2497 read_recent(&rf_path, &rf_open_errno);
2499 /* rearrange all the widgets as we now have the recent settings for this */
2500 main_widgets_rearrange();
2502 /* Fill in column titles. This must be done after the top level window
2505 XXX - is that still true, with fixed-width columns? */
2506 packet_list_set_column_titles();
2508 menu_recent_read_finished();
2510 switch (user_font_apply()) {
2513 case FA_FONT_NOT_RESIZEABLE:
2514 /* "user_font_apply()" popped up an alert box. */
2515 /* turn off zooming - font can't be resized */
2516 case FA_FONT_NOT_AVAILABLE:
2517 /* XXX - did we successfully load the un-zoomed version earlier?
2518 If so, this *probably* means the font is available, but not at
2519 this particular zoom level, but perhaps some other failure
2520 occurred; I'm not sure you can determine which is the case,
2522 /* turn off zooming - zoom level is unavailable */
2524 /* in any other case than FA_SUCCESS, turn off zooming */
2525 recent.gui_zoom_level = 0;
2526 /* XXX: would it be a good idea to disable zooming (insensitive GUI)? */
2529 dnd_init(top_level);
2535 /* the window can be sized only, if it's not already shown, so do it now! */
2536 main_load_window_geometry(top_level);
2538 /*** we have finished all init things, show the main window ***/
2539 gtk_widget_show(top_level);
2541 /* the window can be maximized only, if it's visible, so do it after show! */
2542 main_load_window_geometry(top_level);
2544 /* process all pending GUI events before continue */
2545 while (gtk_events_pending()) gtk_main_iteration();
2547 /* Pop up any queued-up alert boxes. */
2548 display_queued_messages();
2550 /* If we were given the name of a capture file, read it in now;
2551 we defer it until now, so that, if we can't open it, and pop
2552 up an alert box, the alert box is more likely to come up on
2553 top of the main window - but before the preference-file-error
2554 alert box, so, if we get one of those, it's more likely to come
2557 if (rfilter != NULL) {
2558 if (!dfilter_compile(rfilter, &rfcode)) {
2559 bad_dfilter_alert_box(rfilter);
2560 rfilter_parse_failed = TRUE;
2563 if (!rfilter_parse_failed) {
2564 if ((err = cf_open(cf_name, FALSE, &cfile)) == 0) {
2565 /* "cf_open()" succeeded, so it closed the previous
2566 capture file, and thus destroyed any previous read filter
2567 attached to "cf". */
2568 cfile.rfcode = rfcode;
2570 /* Open tap windows; we do so after creating the main window,
2571 to avoid GTK warnings, and after successfully opening the
2572 capture file, so we know we have something to tap. */
2573 if (tap_opt && tli) {
2574 (*tli->func)(tap_opt);
2578 /* Read the capture file. */
2579 switch (cf_read(&cfile)) {
2583 /* Just because we got an error, that doesn't mean we were unable
2584 to read any of the file; we handle what we could get from the
2593 /* Save the name of the containing directory specified in the
2594 path name, if any; we can write over cf_name, which is a
2595 good thing, given that "get_dirname()" does write over its
2597 s = get_dirname(cf_name);
2598 /* we might already set this from the recent file, don't overwrite this */
2599 if(get_last_open_dir() == NULL)
2600 set_last_open_dir(s);
2605 dfilter_free(rfcode);
2606 cfile.rfcode = NULL;
2611 if (start_capture) {
2612 /* "-k" was specified; start a capture. */
2613 if (do_capture(capture_opts, save_file)) {
2614 /* The capture started. Open tap windows; we do so after creating
2615 the main window, to avoid GTK warnings, and after starting the
2616 capture, so we know we have something to tap. */
2617 if (tap_opt && tli) {
2618 (*tli->func)(tap_opt);
2622 if (save_file != NULL) {
2623 /* Save the directory name for future file dialogs. */
2624 s = get_dirname(save_file); /* Overwrites save_file */
2625 set_last_open_dir(s);
2631 set_menus_for_capture_in_progress(FALSE);
2634 /* This is the child process for a sync mode or fork mode capture,
2635 so just do the low-level work of a capture - don't create
2636 a temporary file and fork off *another* child process (so don't
2637 call "do_capture()"). */
2639 /* Pop up any queued-up alert boxes. */
2640 display_queued_messages();
2642 /* XXX - hand these stats to the parent process */
2643 capture_start(capture_opts, &stats_known, &stats);
2645 /* The capture is done; there's nothing more for us to do. */
2648 if (!start_capture && (cfile.cfilter == NULL || strlen(cfile.cfilter) == 0)) {
2649 if (cfile.cfilter) {
2650 g_free(cfile.cfilter);
2652 cfile.cfilter = g_strdup(get_conn_cfilter());
2654 #else /* HAVE_LIBPCAP */
2655 set_menus_for_capture_in_progress(FALSE);
2656 #endif /* HAVE_LIBPCAP */
2664 /* Shutdown windows sockets */
2667 /* For some unknown reason, the "atexit()" call in "create_console()"
2668 doesn't arrange that "destroy_console()" be called when we exit,
2669 so we call it here if a console was created. */
2675 /* This isn't reached, but we need it to keep GCC from complaining
2676 that "main()" returns without returning a value - it knows that
2677 "exit()" never returns, but it doesn't know that "gtk_exit()"
2678 doesn't, as GTK+ doesn't declare it with the attribute
2680 return 0; /* not reached */
2685 /* We build this as a GUI subsystem application on Win32, so
2686 "WinMain()", not "main()", gets called.
2688 Hack shamelessly stolen from the Win32 port of the GIMP. */
2690 #define _stdcall __attribute__((stdcall))
2694 WinMain (struct HINSTANCE__ *hInstance,
2695 struct HINSTANCE__ *hPrevInstance,
2699 has_console = FALSE;
2700 return main (__argc, __argv);
2704 * If this application has no console window to which its standard output
2705 * would go, create one.
2708 create_console(void)
2710 if (!has_console && prefs.gui_console_open != console_open_never) {
2711 /* We have no console to which to print the version string, so
2712 create one and make it the standard input, output, and error. */
2713 if (!AllocConsole())
2714 return; /* couldn't create console */
2715 freopen("CONIN$", "r", stdin);
2716 freopen("CONOUT$", "w", stdout);
2717 freopen("CONOUT$", "w", stderr);
2719 /* Well, we have a console now. */
2722 /* Now register "destroy_console()" as a routine to be called just
2723 before the application exits, so that we can destroy the console
2724 after the user has typed a key (so that the console doesn't just
2725 disappear out from under them, giving the user no chance to see
2726 the message(s) we put in there). */
2727 atexit(destroy_console);
2732 destroy_console(void)
2735 printf("\n\nPress any key to exit\n");
2741 /* This routine should not be necessary, at least as I read the GLib
2742 source code, as it looks as if GLib is, on Win32, *supposed* to
2743 create a console window into which to display its output.
2745 That doesn't happen, however. I suspect there's something completely
2746 broken about that code in GLib-for-Win32, and that it may be related
2747 to the breakage that forces us to just call "printf()" on the message
2748 rather than passing the message on to "g_log_default_handler()"
2749 (which is the routine that does the aforementioned non-functional
2750 console window creation). */
2752 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
2753 const char *message, gpointer user_data)
2757 /* For some unknown reason, the above doesn't appear to actually cause
2758 anything to be sent to the standard output, so we'll just splat the
2759 message out directly, just to make sure it gets out. */
2760 printf("%s\n", message);
2762 g_log_default_handler(log_domain, log_level, message, user_data);
2767 GtkWidget *info_bar_new(void)
2769 /* tip: tooltips don't work on statusbars! */
2770 info_bar = gtk_statusbar_new();
2771 main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
2772 file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
2773 help_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "help");
2774 #if GTK_MAJOR_VERSION >= 2
2775 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), FALSE);
2777 gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
2782 GtkWidget *packets_bar_new(void)
2784 /* tip: tooltips don't work on statusbars! */
2785 packets_bar = gtk_statusbar_new();
2786 packets_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(packets_bar), "packets");
2787 packets_bar_update();
2794 * Helper for main_widgets_rearrange()
2796 void foreach_remove_a_child(GtkWidget *widget, gpointer data) {
2797 gtk_container_remove(GTK_CONTAINER(data), widget);
2800 GtkWidget *main_widget_layout(gint layout_content)
2802 switch(layout_content) {
2803 case(layout_pane_content_none):
2806 case(layout_pane_content_plist):
2809 case(layout_pane_content_pdetails):
2812 case(layout_pane_content_pbytes):
2816 g_assert_not_reached();
2823 * Rearrange the main window widgets
2825 void main_widgets_rearrange(void) {
2826 GtkWidget *first_pane_widget1, *first_pane_widget2;
2827 GtkWidget *second_pane_widget1, *second_pane_widget2;
2828 gboolean split_top_left;
2830 /* be a bit faster */
2831 gtk_widget_hide(main_vbox);
2833 /* be sure, we don't loose a widget while rearranging */
2834 gtk_widget_ref(menubar);
2835 gtk_widget_ref(main_tb);
2836 gtk_widget_ref(filter_tb);
2837 gtk_widget_ref(pkt_scrollw);
2838 gtk_widget_ref(tv_scrollw);
2839 gtk_widget_ref(byte_nb_ptr);
2840 gtk_widget_ref(stat_hbox);
2841 gtk_widget_ref(info_bar);
2842 gtk_widget_ref(packets_bar);
2843 gtk_widget_ref(status_pane);
2844 gtk_widget_ref(main_pane_v1);
2845 gtk_widget_ref(main_pane_v2);
2846 gtk_widget_ref(main_pane_h1);
2847 gtk_widget_ref(main_pane_h2);
2849 /* empty all containers participating */
2850 gtk_container_foreach(GTK_CONTAINER(main_vbox), foreach_remove_a_child, main_vbox);
2851 gtk_container_foreach(GTK_CONTAINER(stat_hbox), foreach_remove_a_child, stat_hbox);
2852 gtk_container_foreach(GTK_CONTAINER(status_pane), foreach_remove_a_child, status_pane);
2853 gtk_container_foreach(GTK_CONTAINER(main_pane_v1), foreach_remove_a_child, main_pane_v1);
2854 gtk_container_foreach(GTK_CONTAINER(main_pane_v2), foreach_remove_a_child, main_pane_v2);
2855 gtk_container_foreach(GTK_CONTAINER(main_pane_h1), foreach_remove_a_child, main_pane_h1);
2856 gtk_container_foreach(GTK_CONTAINER(main_pane_h2), foreach_remove_a_child, main_pane_h2);
2858 /* add the menubar always at the top */
2859 gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
2862 gtk_box_pack_start(GTK_BOX(main_vbox), main_tb, FALSE, TRUE, 0);
2864 /* filter toolbar in toolbar area */
2865 if (!prefs.filter_toolbar_show_in_statusbar) {
2866 gtk_box_pack_start(GTK_BOX(main_vbox), filter_tb, FALSE, TRUE, 1);
2869 /* fill the main layout panes */
2870 switch(prefs.gui_layout_type) {
2871 case(layout_type_5):
2872 main_first_pane = main_pane_v1;
2873 main_second_pane = main_pane_v2;
2874 split_top_left = FALSE;
2876 case(layout_type_2):
2877 main_first_pane = main_pane_v1;
2878 main_second_pane = main_pane_h1;
2879 split_top_left = FALSE;
2881 case(layout_type_1):
2882 main_first_pane = main_pane_v1;
2883 main_second_pane = main_pane_h1;
2884 split_top_left = TRUE;
2886 case(layout_type_4):
2887 main_first_pane = main_pane_h1;
2888 main_second_pane = main_pane_v1;
2889 split_top_left = FALSE;
2891 case(layout_type_3):
2892 main_first_pane = main_pane_h1;
2893 main_second_pane = main_pane_v1;
2894 split_top_left = TRUE;
2896 case(layout_type_6):
2897 main_first_pane = main_pane_h1;
2898 main_second_pane = main_pane_h2;
2899 split_top_left = FALSE;
2902 main_first_pane = NULL;
2903 main_second_pane = NULL;
2904 split_top_left = FALSE;
2905 g_assert_not_reached();
2907 if (split_top_left) {
2908 first_pane_widget1 = main_second_pane;
2909 second_pane_widget1 = main_widget_layout(prefs.gui_layout_content_1);
2910 second_pane_widget2 = main_widget_layout(prefs.gui_layout_content_2);
2911 first_pane_widget2 = main_widget_layout(prefs.gui_layout_content_3);
2913 first_pane_widget1 = main_widget_layout(prefs.gui_layout_content_1);
2914 first_pane_widget2 = main_second_pane;
2915 second_pane_widget1 = main_widget_layout(prefs.gui_layout_content_2);
2916 second_pane_widget2 = main_widget_layout(prefs.gui_layout_content_3);
2918 if (first_pane_widget1 != NULL)
2919 gtk_paned_add1(GTK_PANED(main_first_pane), first_pane_widget1);
2920 if (first_pane_widget2 != NULL)
2921 gtk_paned_add2(GTK_PANED(main_first_pane), first_pane_widget2);
2922 if (second_pane_widget1 != NULL)
2923 gtk_paned_pack1(GTK_PANED(main_second_pane), second_pane_widget1, TRUE, TRUE);
2924 if (second_pane_widget2 != NULL)
2925 gtk_paned_pack2(GTK_PANED(main_second_pane), second_pane_widget2, FALSE, FALSE);
2927 gtk_container_add(GTK_CONTAINER(main_vbox), main_first_pane);
2929 /* statusbar hbox */
2930 gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
2932 /* filter toolbar in statusbar hbox */
2933 if (prefs.filter_toolbar_show_in_statusbar) {
2934 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_tb, FALSE, TRUE, 1);
2938 gtk_box_pack_start(GTK_BOX(stat_hbox), status_pane, TRUE, TRUE, 0);
2939 gtk_paned_pack1(GTK_PANED(status_pane), info_bar, FALSE, FALSE);
2940 gtk_paned_pack2(GTK_PANED(status_pane), packets_bar, FALSE, FALSE);
2942 /* hide widgets on users recent settings */
2943 main_widgets_show_or_hide();
2945 gtk_widget_show(main_vbox);
2949 is_widget_visible(GtkWidget *widget, gpointer data)
2951 gboolean *is_visible = data;
2954 if (GTK_WIDGET_VISIBLE(widget))
2960 * XXX - this doesn't appear to work with the paned widgets in
2961 * GTK+ 1.2[.x]; if you hide one of the panes, the splitter remains
2962 * and the other pane doesn't grow to take up the rest of the pane.
2963 * It does appear to work with GTK+ 2.x.
2966 main_widgets_show_or_hide(void)
2968 gboolean main_second_pane_show;
2970 if (recent.main_toolbar_show) {
2971 gtk_widget_show(main_tb);
2973 gtk_widget_hide(main_tb);
2977 * Show the status hbox if either:
2979 * 1) we're showing the filter toolbar and we want it in the status
2984 * 2) we're showing the status bar.
2986 if ((recent.filter_toolbar_show && prefs.filter_toolbar_show_in_statusbar) ||
2987 recent.statusbar_show) {
2988 gtk_widget_show(stat_hbox);
2990 gtk_widget_hide(stat_hbox);
2993 if (recent.statusbar_show) {
2994 gtk_widget_show(status_pane);
2996 gtk_widget_hide(status_pane);
2999 if (recent.filter_toolbar_show) {
3000 gtk_widget_show(filter_tb);
3002 gtk_widget_hide(filter_tb);
3005 if (recent.packet_list_show) {
3006 gtk_widget_show(pkt_scrollw);
3008 gtk_widget_hide(pkt_scrollw);
3011 if (recent.tree_view_show) {
3012 gtk_widget_show(tv_scrollw);
3014 gtk_widget_hide(tv_scrollw);
3017 if (recent.byte_view_show) {
3018 gtk_widget_show(byte_nb_ptr);
3020 gtk_widget_hide(byte_nb_ptr);
3024 * Is anything in "main_second_pane" visible?
3025 * If so, show it, otherwise hide it.
3027 main_second_pane_show = FALSE;
3028 gtk_container_foreach(GTK_CONTAINER(main_second_pane), is_widget_visible,
3029 &main_second_pane_show);
3030 if (main_second_pane_show) {
3031 gtk_widget_show(main_second_pane);
3033 gtk_widget_hide(main_second_pane);
3038 #if GTK_MAJOR_VERSION >= 2
3039 /* called, when the window state changes (minimized, maximized, ...) */
3041 window_state_event_cb (GtkWidget *widget _U_,
3045 GdkWindowState new_window_state = ((GdkEventWindowState*)event)->new_window_state;
3047 if( (event->type) == (GDK_WINDOW_STATE)) {
3048 if(!(new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
3049 /* we might have dialogs popped up while we where iconified,
3051 display_queued_messages();
3060 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
3063 *filter_bt, *filter_cm, *filter_te,
3064 *filter_add_expr_bt,
3067 GList *filter_list = NULL;
3068 GtkTooltips *tooltips;
3069 GtkAccelGroup *accel;
3071 /* Display filter construct dialog has an Apply button, and "OK" not
3072 only sets our text widget, it activates it (i.e., it causes us to
3073 filter the capture). */
3074 static construct_args_t args = {
3075 "Ethereal: Display Filter",
3080 /* use user-defined title if preference is set */
3081 title = create_user_window_title("The Ethereal Network Analyzer");
3084 top_level = window_new(GTK_WINDOW_TOPLEVEL, title);
3087 tooltips = gtk_tooltips_new();
3090 #if GTK_MAJOR_VERSION < 2
3091 /* has to be done, after top_level window is created */
3092 app_font_gtk1_init(top_level);
3096 gtk_widget_set_name(top_level, "main window");
3097 SIGNAL_CONNECT(top_level, "delete_event", main_window_delete_event_cb,
3099 #if GTK_MAJOR_VERSION >= 2
3100 SIGNAL_CONNECT(GTK_OBJECT(top_level), "window_state_event",
3101 G_CALLBACK (window_state_event_cb), NULL);
3104 gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
3106 /* Container for menu bar, toolbar(s), paned windows and progress/info box */
3107 main_vbox = gtk_vbox_new(FALSE, 1);
3108 gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
3109 gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
3110 gtk_widget_show(main_vbox);
3113 menubar = main_menu_new(&accel);
3114 gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
3115 gtk_widget_show(menubar);
3118 main_tb = toolbar_new();
3119 gtk_widget_show (main_tb);
3122 pkt_scrollw = packet_list_new(prefs);
3123 WIDGET_SET_SIZE(packet_list, -1, pl_size);
3124 gtk_widget_show(pkt_scrollw);
3127 tv_scrollw = main_tree_view_new(prefs, &tree_view);
3128 WIDGET_SET_SIZE(tv_scrollw, -1, tv_size);
3129 gtk_widget_show(tv_scrollw);
3131 #if GTK_MAJOR_VERSION < 2
3132 SIGNAL_CONNECT(tree_view, "tree-select-row", tree_view_select_row_cb, NULL);
3133 SIGNAL_CONNECT(tree_view, "tree-unselect-row", tree_view_unselect_row_cb,
3136 SIGNAL_CONNECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)),
3137 "changed", tree_view_selection_changed_cb, NULL);
3139 SIGNAL_CONNECT(tree_view, "button_press_event", popup_menu_handler,
3140 OBJECT_GET_DATA(popup_menu_object, PM_TREE_VIEW_KEY));
3141 gtk_widget_show(tree_view);
3144 byte_nb_ptr = byte_view_new();
3145 WIDGET_SET_SIZE(byte_nb_ptr, -1, bv_size);
3146 gtk_widget_show(byte_nb_ptr);
3148 SIGNAL_CONNECT(byte_nb_ptr, "button_press_event", popup_menu_handler,
3149 OBJECT_GET_DATA(popup_menu_object, PM_HEXDUMP_KEY));
3152 /* Panes for the packet list, tree, and byte view */
3153 main_pane_v1 = gtk_vpaned_new();
3154 gtk_widget_show(main_pane_v1);
3155 main_pane_v2 = gtk_vpaned_new();
3156 gtk_widget_show(main_pane_v2);
3157 main_pane_h1 = gtk_hpaned_new();
3158 gtk_widget_show(main_pane_h1);
3159 main_pane_h2 = gtk_hpaned_new();
3160 gtk_widget_show(main_pane_h2);
3162 /* filter toolbar */
3163 #if GTK_MAJOR_VERSION < 2
3164 filter_tb = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
3167 filter_tb = gtk_toolbar_new();
3168 gtk_toolbar_set_orientation(GTK_TOOLBAR(filter_tb),
3169 GTK_ORIENTATION_HORIZONTAL);
3170 #endif /* GTK_MAJOR_VERSION */
3171 gtk_widget_show(filter_tb);
3173 /* Create the "Filter:" button */
3174 filter_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_DISPLAY_FILTER_ENTRY);
3175 SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
3176 gtk_widget_show(filter_bt);
3177 OBJECT_SET_DATA(top_level, E_FILT_BT_PTR_KEY, filter_bt);
3179 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_bt,
3180 "Open the \"Display Filter\" dialog, to edit/apply filters", "Private");
3182 /* Create the filter combobox */
3183 filter_cm = gtk_combo_new();
3185 gtk_combo_disable_activate(GTK_COMBO(filter_cm));
3186 gtk_combo_set_case_sensitive(GTK_COMBO(filter_cm), TRUE);
3187 OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
3188 filter_te = GTK_COMBO(filter_cm)->entry;
3189 main_display_filter_widget=filter_te;
3190 OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_te);
3191 OBJECT_SET_DATA(filter_te, E_DFILTER_CM_KEY, filter_cm);
3192 OBJECT_SET_DATA(top_level, E_DFILTER_CM_KEY, filter_cm);
3193 SIGNAL_CONNECT(filter_te, "activate", filter_activate_cb, filter_te);
3194 SIGNAL_CONNECT(filter_te, "changed", filter_te_syntax_check_cb, NULL);
3195 WIDGET_SET_SIZE(filter_cm, 400, -1);
3196 gtk_widget_show(filter_cm);
3197 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_cm,
3199 /* setting a tooltip for a combobox will do nothing, so add it to the corresponding text entry */
3200 gtk_tooltips_set_tip(tooltips, filter_te,
3201 "Enter a display filter, or choose one of your recently used filters. "
3202 "The background color of this field is changed by a continuous syntax check (green is valid, red is invalid).",
3205 /* Create the "Add Expression..." button, to pop up a dialog
3206 for constructing filter comparison expressions. */
3207 filter_add_expr_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_ADD_EXPRESSION);
3208 OBJECT_SET_DATA(filter_tb, E_FILT_FILTER_TE_KEY, filter_te);
3209 SIGNAL_CONNECT(filter_add_expr_bt, "clicked", filter_add_expr_bt_cb, filter_tb);
3210 gtk_widget_show(filter_add_expr_bt);
3211 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_add_expr_bt,
3212 "Add an expression to this filter string", "Private");
3214 /* Create the "Clear" button */
3215 filter_reset = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLEAR);
3216 OBJECT_SET_DATA(filter_reset, E_DFILTER_TE_KEY, filter_te);
3217 SIGNAL_CONNECT(filter_reset, "clicked", filter_reset_cb, NULL);
3218 gtk_widget_show(filter_reset);
3219 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_reset,
3220 "Clear this filter string and update the display", "Private");
3222 /* Create the "Apply" button */
3223 filter_apply = BUTTON_NEW_FROM_STOCK(GTK_STOCK_APPLY);
3224 OBJECT_SET_DATA(filter_apply, E_DFILTER_CM_KEY, filter_cm);
3225 SIGNAL_CONNECT(filter_apply, "clicked", filter_activate_cb, filter_te);
3226 gtk_widget_show(filter_apply);
3227 gtk_toolbar_append_widget(GTK_TOOLBAR(filter_tb), filter_apply,
3228 "Apply this filter string to the display", "Private");
3230 /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
3231 * of any widget that ends up calling a callback which needs
3232 * that text entry pointer */
3233 set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
3234 set_menu_object_data("/Analyze/Display Filters...", E_FILT_TE_PTR_KEY,
3236 set_menu_object_data("/Analyze/Follow TCP Stream", E_DFILTER_TE_KEY,
3238 set_menu_object_data("/Analyze/Apply as Filter/Selected", E_DFILTER_TE_KEY,
3240 set_menu_object_data("/Analyze/Apply as Filter/Not Selected", E_DFILTER_TE_KEY,
3242 set_menu_object_data("/Analyze/Apply as Filter/... and Selected", E_DFILTER_TE_KEY,
3244 set_menu_object_data("/Analyze/Apply as Filter/... or Selected", E_DFILTER_TE_KEY,
3246 set_menu_object_data("/Analyze/Apply as Filter/... and not Selected", E_DFILTER_TE_KEY,
3248 set_menu_object_data("/Analyze/Apply as Filter/... or not Selected", E_DFILTER_TE_KEY,
3250 set_menu_object_data("/Analyze/Prepare a Filter/Selected", E_DFILTER_TE_KEY,
3252 set_menu_object_data("/Analyze/Prepare a Filter/Not Selected", E_DFILTER_TE_KEY,
3254 set_menu_object_data("/Analyze/Prepare a Filter/... and Selected", E_DFILTER_TE_KEY,
3256 set_menu_object_data("/Analyze/Prepare a Filter/... or Selected", E_DFILTER_TE_KEY,
3258 set_menu_object_data("/Analyze/Prepare a Filter/... and not Selected", E_DFILTER_TE_KEY,
3260 set_menu_object_data("/Analyze/Prepare a Filter/... or not Selected", E_DFILTER_TE_KEY,
3262 set_toolbar_object_data(E_DFILTER_TE_KEY, filter_te);
3263 OBJECT_SET_DATA(popup_menu_object, E_DFILTER_TE_KEY, filter_te);
3264 OBJECT_SET_DATA(popup_menu_object, E_MPACKET_LIST_KEY, packet_list);
3266 /* info (main) statusbar */
3267 info_bar = info_bar_new();
3268 gtk_widget_show(info_bar);
3270 /* packets statusbar */
3271 packets_bar = packets_bar_new();
3272 gtk_widget_show(packets_bar);
3274 /* Filter/status hbox */
3275 stat_hbox = gtk_hbox_new(FALSE, 1);
3276 gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
3277 gtk_widget_show(stat_hbox);
3279 /* Pane for the statusbar */
3280 status_pane = gtk_hpaned_new();
3281 gtk_widget_show(status_pane);