It was silly of me to require that "forget_scrolled_window()" be called
[metze/wireshark/wip.git] / gtk / main.c
1 /* main.c
2  *
3  * $Id: main.c,v 1.136 2000/08/17 07:56:37 guy Exp $
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@zing.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * Richard Sharpe, 13-Feb-1999, added support for initializing structures
10  *                              needed by dissect routines
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  *
26  *
27  * To do:
28  * - Graphs
29  * - Check for end of packet in dissect_* routines.
30  * - Playback window
31  * - Multiple window support
32  * - Add cut/copy/paste
33  * - Create header parsing routines
34  * - Make byte view selections more fancy?
35  *
36  */
37
38 #ifdef HAVE_CONFIG_H
39 # include "config.h"
40 #endif
41
42 #include <gtk/gtk.h>
43
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51
52 #include <errno.h>
53
54 #ifdef HAVE_SYS_TYPES_H
55 #include <sys/types.h>
56 #endif
57
58 #ifdef HAVE_SYS_STAT_H
59 #include <sys/stat.h>
60 #endif
61
62 #ifdef HAVE_IO_H
63 #include <io.h> /* open/close on win32 */
64 #endif
65
66 #ifdef HAVE_DIRECT_H
67 #include <direct.h>
68 #endif
69
70 #ifdef HAVE_NETINET_IN_H
71 #include <netinet/in.h>
72 #endif
73
74 #include <signal.h>
75
76 #ifdef NEED_SNPRINTF_H
77 # include "snprintf.h"
78 #endif
79
80 #if defined(HAVE_UCD_SNMP_SNMP_H)
81 #ifdef HAVE_UCD_SNMP_VERSION_H
82 #include <ucd-snmp/version.h>
83 #endif /* HAVE_UCD_SNMP_VERSION_H */
84 #elif defined(HAVE_SNMP_SNMP_H)
85 #ifdef HAVE_SNMP_VERSION_H
86 #include <snmp/version.h>
87 #endif /* HAVE_SNMP_VERSION_H */
88 #endif /* SNMP */
89
90 #ifdef NEED_STRERROR_H
91 #include "strerror.h"
92 #endif
93
94 #ifdef NEED_GETOPT_H
95 #include "getopt.h"
96 #endif
97
98 #include "main.h"
99 #include "timestamp.h"
100 #include "packet.h"
101 #include "capture.h"
102 #include "summary.h"
103 #include "file.h"
104 #include "menu.h"
105 #include "../menu.h"
106 #include "filter_prefs.h"
107 #include "prefs_dlg.h"
108 #include "column.h"
109 #include "print.h"
110 #include "resolv.h"
111 #include "util.h"
112 #include "simple_dialog.h"
113 #include "proto_draw.h"
114 #include "dfilter.h"
115 #include "keys.h"
116 #include "packet_win.h"
117 #include "gtkglobals.h"
118 #include "plugins.h"
119
120 packet_info  pi;
121 capture_file cfile;
122 GtkWidget   *top_level, *packet_list, *tree_view, *byte_view,
123             *info_bar, *tv_scrollw, *pkt_scrollw;
124 static GtkWidget        *bv_scrollw;
125 GdkFont     *m_r_font, *m_b_font;
126 guint        main_ctx, file_ctx;
127 gchar        comp_info_str[256];
128 gchar       *ethereal_path = NULL;
129 gchar       *medium_font = MONO_MEDIUM_FONT;
130 gchar       *bold_font = MONO_BOLD_FONT;
131 gchar       *last_open_dir = NULL;
132
133 ts_type timestamp_type = RELATIVE;
134
135 GtkStyle *item_style;
136
137 /* Specifies the field currently selected in the GUI protocol tree */
138 field_info *finfo_selected = NULL;
139
140 static char* hfinfo_numeric_format(header_field_info *hfinfo);
141 static void create_main_window(gint, gint, gint, e_prefs*);
142
143 /* About Ethereal window */
144 void
145 about_ethereal( GtkWidget *w, gpointer data ) {
146   simple_dialog(ESD_TYPE_INFO, NULL,
147                 "Ethereal - Network Protocol Analyzer\n"
148                 "Version " VERSION " (C) 1998-2000 Gerald Combs <gerald@zing.org>\n"
149                 "Compiled with %s\n\n"
150
151                 "Check the man page for complete documentation and\n"
152                 "for the list of contributors.\n"
153
154                 "\nSee http://ethereal.zing.org/ for more information.",
155                  comp_info_str);
156 }
157
158
159 /* Match selected byte pattern */
160 void
161 match_selected_cb(GtkWidget *w, gpointer data)
162 {
163     char                *buf;
164     GtkWidget           *filter_te;
165     char                *ptr, *format, *stringified;
166     int                 i, dfilter_len, abbrev_len;
167     guint8              *c;
168     header_field_info   *hfinfo;
169
170     filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
171
172     if (!finfo_selected) {
173         simple_dialog(ESD_TYPE_CRIT, NULL,
174                       "Error determining selected bytes.  Please make\n"
175                       "sure you have selected a field within the tree\n"
176                       "view to be matched.");
177         return;
178     }
179
180     hfinfo = finfo_selected->hfinfo;
181     g_assert(hfinfo);
182     abbrev_len = strlen(hfinfo->abbrev);
183
184         switch(hfinfo->type) {
185
186                 case FT_BOOLEAN:
187                         dfilter_len = abbrev_len + 2;
188                         buf = g_malloc0(dfilter_len);
189                         snprintf(buf, dfilter_len, "%s%s", finfo_selected->value.numeric ? "" : "!",
190                                         hfinfo->abbrev);
191                         break;
192
193                 case FT_UINT8:
194                 case FT_UINT16:
195                 case FT_UINT24:
196                 case FT_UINT32:
197                 case FT_INT8:
198                 case FT_INT16:
199                 case FT_INT24:
200                 case FT_INT32:
201                         dfilter_len = abbrev_len + 20;
202                         buf = g_malloc0(dfilter_len);
203                         format = hfinfo_numeric_format(hfinfo);
204                         snprintf(buf, dfilter_len, format, hfinfo->abbrev, finfo_selected->value.numeric);
205                         break;
206
207                 case FT_IPv4:
208                         dfilter_len = abbrev_len + 4 + 15 + 1;
209                         buf = g_malloc0(dfilter_len);
210                         snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
211                                         ipv4_addr_str(&(finfo_selected->value.ipv4)));
212                         break;
213
214                 case FT_IPXNET:
215                         dfilter_len = abbrev_len + 15;
216                         buf = g_malloc0(dfilter_len);
217                         snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev,
218                                         finfo_selected->value.numeric);
219                         break;
220
221                 case FT_IPv6:
222                         stringified = ip6_to_str((struct e_in6_addr*) &(finfo_selected->value.ipv6));
223                         dfilter_len = abbrev_len + 4 + strlen(stringified) + 1;
224                         buf = g_malloc0(dfilter_len);
225                         snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
226                                         stringified);
227                         break;
228
229                 case FT_DOUBLE:
230                         dfilter_len = abbrev_len + 30;
231                         buf = g_malloc0(dfilter_len);
232                         snprintf(buf, dfilter_len, "%s == %f", hfinfo->abbrev,
233                                         finfo_selected->value.floating);
234                         break;
235
236                 case FT_ETHER:
237                         dfilter_len = abbrev_len + 22;
238                         buf = g_malloc0(dfilter_len);
239                         snprintf(buf, dfilter_len, "%s == %s",
240                                         hfinfo->abbrev,
241                                         ether_to_str(finfo_selected->value.ether));
242                         break;
243 #if 0
244
245                 case FT_ABSOLUTE_TIME:
246                 case FT_RELATIVE_TIME:
247                         memcpy(&fi->value.time, va_arg(ap, struct timeval*),
248                                 sizeof(struct timeval));
249                         break;
250
251                 case FT_STRING:
252                         /* This g_strdup'ed memory is freed in proto_tree_free_node() */
253                         fi->value.string = g_strdup(va_arg(ap, char*));
254                         break;
255
256                 case FT_TEXT_ONLY:
257                         ; /* nothing */
258                         break;
259 #endif
260                 default:
261                     c = cfile.pd + finfo_selected->start;
262                     buf = g_malloc0(32 + finfo_selected->length * 3);
263                     ptr = buf;
264
265                     sprintf(ptr, "frame[%d] == ", finfo_selected->start);
266                     ptr = buf+strlen(buf);
267
268                     if (finfo_selected->length == 1) {
269                         sprintf(ptr, "0x%02x", *c++);
270                     }
271                     else {
272                             for (i=0;i<finfo_selected->length; i++) {
273                                 if (i == 0 ) {
274                                         sprintf(ptr, "%02x", *c++);
275                                 }
276                                 else {
277                                         sprintf(ptr, ":%02x", *c++);
278                                 }
279                                 ptr = buf+strlen(buf);
280                             }
281                     }
282                     break;
283         }
284
285     /* create a new one and set the display filter entry accordingly */
286     gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
287
288     /* Run the display filter so it goes in effect. */
289     filter_packets(&cfile, buf);
290
291     /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
292 }
293
294 static char*
295 hfinfo_numeric_format(header_field_info *hfinfo)
296 {
297         char *format = NULL;
298
299         /* Pick the proper format string */
300         switch(hfinfo->display) {
301                 case BASE_DEC:
302                 case BASE_NONE:
303                 case BASE_OCT: /* I'm lazy */
304                 case BASE_BIN: /* I'm lazy */
305                         switch(hfinfo->type) {
306                                 case FT_UINT8:
307                                 case FT_UINT16:
308                                 case FT_UINT24:
309                                 case FT_UINT32:
310                                         format = "%s == %u";
311                                         break;
312                                 case FT_INT8:
313                                 case FT_INT16:
314                                 case FT_INT24:
315                                 case FT_INT32:
316                                         format = "%s == %d";
317                                         break;
318                                 default:
319                                         g_assert_not_reached();
320                                         ;
321                         }
322                         break;
323                 case BASE_HEX:
324                         switch(hfinfo->type) {
325                                 case FT_UINT8:
326                                         format = "%s == 0x%02x";
327                                         break;
328                                 case FT_UINT16:
329                                         format = "%s == 0x%04x";
330                                         break;
331                                 case FT_UINT24:
332                                         format = "%s == 0x%06x";
333                                         break;
334                                 case FT_UINT32:
335                                         format = "%s == 0x%08x";
336                                         break;
337                                 default:
338                                         g_assert_not_reached();
339                                         ;
340                         }
341                         break;
342                 default:
343                         g_assert_not_reached();
344                         ;
345         }
346         return format;
347 }
348
349
350 /* Run the current display filter on the current packet set, and
351    redisplay. */
352 static void
353 filter_activate_cb(GtkWidget *w, gpointer data)
354 {
355   GtkCombo  *filter_cm = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_CM_KEY);
356   GList     *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY);
357   GList     *li, *nl = NULL;
358   gboolean   add_filter = TRUE;
359   
360   char *s = gtk_entry_get_text(GTK_ENTRY(w));
361   
362   /* GtkCombos don't let us get at their list contents easily, so we maintain
363      our own filter list, and feed it to gtk_combo_set_popdown_strings when
364      a new filter is added. */
365   if (filter_packets(&cfile, g_strdup(s))) {
366     li = g_list_first(filter_list);
367     while (li) {
368       if (li->data && strcmp(s, li->data) == 0)
369         add_filter = FALSE;
370       li = li->next;
371     }
372
373     if (add_filter) {
374       filter_list = g_list_append(filter_list, g_strdup(s));
375       li = g_list_first(filter_list);
376       while (li) {
377         nl = g_list_append(nl, strdup(li->data));
378         li = li->next;
379       }
380       gtk_combo_set_popdown_strings(filter_cm, nl);
381       gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
382     }
383   }
384 }
385
386 /* redisplay with no display filter */
387 static void
388 filter_reset_cb(GtkWidget *w, gpointer data)
389 {
390   GtkWidget *filter_te = NULL;
391
392   if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
393     gtk_entry_set_text(GTK_ENTRY(filter_te), "");
394   }
395
396   filter_packets(&cfile, NULL);
397 }
398
399 /* GTKClist compare routine, overrides default to allow numeric comparison */
400 static gint
401 packet_list_compare(GtkCList *clist, gconstpointer  ptr1, gconstpointer  ptr2)
402 {
403   /* Get row text strings */
404   char *text1 = GTK_CELL_TEXT (((GtkCListRow *)ptr1)->cell[clist->sort_column])->text;
405   char *text2 = GTK_CELL_TEXT (((GtkCListRow *)ptr2)->cell[clist->sort_column])->text;
406
407   /* Attempt to convert to numbers */
408   double  num1 = atof(text1);
409   double  num2 = atof(text2);
410   
411   gint  col_fmt = cfile.cinfo.col_fmt[clist->sort_column];
412   
413   if ((col_fmt == COL_NUMBER) || (col_fmt == COL_REL_TIME) || (col_fmt == COL_DELTA_TIME) ||
414       ((col_fmt == COL_CLS_TIME) && (timestamp_type == RELATIVE)) ||
415       ((col_fmt == COL_CLS_TIME) && (timestamp_type == DELTA))    ||
416       (col_fmt == COL_UNRES_SRC_PORT) || (col_fmt == COL_UNRES_DST_PORT) ||
417       ((num1 != 0) && (num2 != 0) && ((col_fmt == COL_DEF_SRC_PORT) || (col_fmt == COL_RES_SRC_PORT) ||
418                                       (col_fmt == COL_DEF_DST_PORT) || (col_fmt == COL_RES_DST_PORT))) ||
419       (col_fmt == COL_PACKET_LENGTH)) {
420
421     /* Compare numeric column */
422
423     if (num1 < num2)
424       return -1;
425     else if (num1 > num2)
426       return 1;
427     else
428       return 0;
429   }
430   
431   else {
432     
433     /* Compare text column */
434     if (!text2)
435       return (text1 != NULL);
436
437     if (!text1)
438       return -1;
439
440     return strcmp(text1, text2);
441   }
442 }
443
444 /* What to do when a column is clicked */
445 static void 
446 packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data)
447 {
448   if (column == clist->sort_column) {
449     if (clist->sort_type == GTK_SORT_ASCENDING)
450       clist->sort_type = GTK_SORT_DESCENDING;
451     else
452       clist->sort_type = GTK_SORT_ASCENDING;
453   }
454   else {
455     clist->sort_type = GTK_SORT_ASCENDING;
456     gtk_clist_set_sort_column(clist, column);
457   }
458
459   gtk_clist_sort(clist);
460 }
461
462 /* What to do when a list item is selected/unselected */
463 static void
464 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
465
466   blank_packetinfo();
467   select_packet(&cfile, row);
468 }
469
470 static void
471 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
472   unselect_packet(&cfile);
473 }
474
475 static void
476 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
477 {
478         field_info      *finfo;
479
480         g_assert(node);
481         finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
482         if (!finfo) return;
483
484         finfo_selected = finfo;
485
486         set_menus_for_selected_tree_row(TRUE);
487
488         packet_hex_print(GTK_TEXT(byte_view), cfile.pd, cfile.current_frame->cap_len, 
489                 finfo->start, finfo->length, cfile.current_frame->flags.encoding);
490 }
491
492 static void
493 tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
494 {
495         finfo_selected = NULL;
496         set_menus_for_selected_tree_row(FALSE);
497         packet_hex_print(GTK_TEXT(byte_view), cfile.pd, cfile.current_frame->cap_len, 
498                 -1, -1, cfile.current_frame->flags.encoding);
499 }
500
501 void collapse_all_cb(GtkWidget *widget, gpointer data) {
502   if (cfile.protocol_tree)
503     collapse_all_tree(cfile.protocol_tree, tree_view);
504 }
505
506 void expand_all_cb(GtkWidget *widget, gpointer data) {
507   if (cfile.protocol_tree)
508     expand_all_tree(cfile.protocol_tree, tree_view);
509 }
510
511 void resolve_name_cb(GtkWidget *widget, gpointer data) {
512   if (cfile.protocol_tree) {
513     int tmp = g_resolving_actif;
514     g_resolving_actif = 1;
515     gtk_clist_clear ( GTK_CLIST(tree_view) );
516     proto_tree_draw(cfile.protocol_tree, tree_view);
517     g_resolving_actif = tmp;
518   }
519 }
520
521 /* Set the scrollbar placement of a scrolled window based upon pos value:
522    0 = left, 1 = right */
523 void
524 set_scrollbar_placement_scrollw(GtkWidget *scrollw, int pos) /* 0=left, 1=right */
525 {
526         if (pos) {
527                 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
528                                 GTK_CORNER_TOP_LEFT);
529         } else {
530                 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
531                                 GTK_CORNER_TOP_RIGHT);
532         }
533 }
534
535 /* List of all scrolled windows, so we can globally set the scrollbar
536    placement of them. */
537 static GList *scrolled_windows;
538
539 /* Add a scrolled window to the list of scrolled windows. */
540 static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
541
542 void
543 remember_scrolled_window(GtkWidget *scrollw)
544 {
545   scrolled_windows = g_list_append(scrolled_windows, scrollw);
546
547   /* Catch the "destroy" event on the widget, so that we remove it from
548      the list when it's destroyed. */
549   gtk_signal_connect(GTK_OBJECT(scrollw), "destroy",
550                      GTK_SIGNAL_FUNC(forget_scrolled_window), NULL);
551 }
552
553 /* Remove a scrolled window from the list of scrolled windows. */
554 static void
555 forget_scrolled_window(GtkWidget *scrollw, gpointer data)
556 {
557   scrolled_windows = g_list_remove(scrolled_windows, scrollw);
558 }
559
560 static void
561 set_scrollbar_placement_cb(gpointer data, gpointer user_data)
562 {
563         set_scrollbar_placement_scrollw((GtkWidget *)data,
564             *(int *)user_data);
565 }
566
567 /* Set the scrollbar placement of all scrolled windows based on pos value:
568    0 = left, 1 = right */
569 void
570 set_scrollbar_placement_all(int pos)
571 {
572         g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, &pos);
573 }
574
575 /* Set the selection mode of the packet list window. */
576 void
577 set_plist_sel_browse(gboolean val)
578 {
579         gboolean old_val;
580
581         old_val =
582             (GTK_CLIST(packet_list)->selection_mode == GTK_SELECTION_SINGLE);
583
584         if (val == old_val) {
585                 /*
586                  * The mode isn't changing, so don't do anything.
587                  * In particular, don't gratuitiously unselect the
588                  * current packet.
589                  *
590                  * XXX - why do we have to unselect the current packet
591                  * ourselves?  The documentation for the GtkCList at
592                  *
593                  *      http://developer.gnome.org/doc/API/gtk/gtkclist.html
594                  *
595                  * says "Note that setting the widget's selection mode to
596                  * one of GTK_SELECTION_BROWSE or GTK_SELECTION_SINGLE will
597                  * cause all the items in the GtkCList to become deselected."
598                  */
599                 return;
600         }
601
602         if (finfo_selected)
603                 unselect_packet(&cfile);
604
605         /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
606          * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
607         if (val) {
608                 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_SINGLE);
609         }
610         else {
611                 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_BROWSE);
612         }
613 }
614
615 /* Set the selection mode of a given packet tree window. */
616 void
617 set_ptree_sel_browse(GtkWidget *tv, gboolean val)
618 {
619         /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
620          * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
621         if (val) {
622                 gtk_clist_set_selection_mode(GTK_CLIST(tv), GTK_SELECTION_SINGLE);
623         }
624         else {
625                 gtk_clist_set_selection_mode(GTK_CLIST(tv), GTK_SELECTION_BROWSE);
626         }
627 }
628
629 /* Set the selection mode of all packet tree windows. */
630 void
631 set_ptree_sel_browse_all(gboolean val)
632 {
633         set_ptree_sel_browse(tree_view, val);
634         set_ptree_sel_browse_packet_wins(val);
635 }
636
637 /* Set the line style of a given packet tree window. */
638 void
639 set_ptree_line_style(GtkWidget *tv, gint style)
640 {
641         /* I'm using an assert here since the preferences code limits
642          * the user input, both in the GUI and when reading the preferences file.
643          * If the value is incorrect, it's a program error, not a user-initiated error.
644          */
645         g_assert(style >= GTK_CTREE_LINES_NONE && style <= GTK_CTREE_LINES_TABBED);
646         gtk_ctree_set_line_style( GTK_CTREE(tv), style );
647 }
648
649 /* Set the line style of all packet tree window. */
650 void
651 set_ptree_line_style_all(gint style)
652 {
653         set_ptree_line_style(tree_view, style);
654         set_ptree_line_style_packet_wins(style);
655 }
656
657 /* Set the expander style of a given packet tree window. */
658 void
659 set_ptree_expander_style(GtkWidget *tv, gint style)
660 {
661         /* I'm using an assert here since the preferences code limits
662          * the user input, both in the GUI and when reading the preferences file.
663          * If the value is incorrect, it's a program error, not a user-initiated error.
664          */
665         g_assert(style >= GTK_CTREE_EXPANDER_NONE && style <= GTK_CTREE_EXPANDER_CIRCULAR);
666         gtk_ctree_set_expander_style( GTK_CTREE(tv), style );
667 }
668         
669 void
670 set_ptree_expander_style_all(gint style)
671 {
672         set_ptree_expander_style(tree_view, style);
673         set_ptree_expander_style_packet_wins(style);
674 }
675
676 static gboolean
677 main_window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
678 {
679         file_quit_cmd_cb(widget, data);
680
681         /* Say that the window should be deleted. */
682         return FALSE;
683 }
684
685 void
686 file_quit_cmd_cb (GtkWidget *widget, gpointer data)
687 {
688         /* XXX - should we check whether the capture file is an
689            unsaved temporary file for a live capture and, if so,
690            pop up a "do you want to exit without saving the capture
691            file?" dialog, and then just return, leaving said dialog
692            box to forcibly quit if the user clicks "OK"?
693
694            If so, note that this should be done in a subroutine that
695            returns TRUE if we do so, and FALSE otherwise, and that
696            "main_window_delete_event_cb()" should return its
697            return value. */
698
699         /* Are we in the middle of reading a capture? */
700         if (cfile.state == FILE_READ_IN_PROGRESS) {
701                 /* Yes, so we can't just close the file and quit, as
702                    that may yank the rug out from under the read in
703                    progress; instead, just set the state to
704                    "FILE_READ_ABORTED" and return - the code doing the read
705                    will check for that and, if it sees that, will clean
706                    up and quit. */
707                 cfile.state = FILE_READ_ABORTED;
708         } else {
709                 /* Close any capture file we have open; on some OSes, you
710                    can't unlink a temporary capture file if you have it
711                    open.
712                    "close_cap_file()" will unlink it after closing it if
713                    it's a temporary file.
714
715                    We do this here, rather than after the main loop returns,
716                    as, after the main loop returns, the main window may have
717                    been destroyed (if this is called due to a "destroy"
718                    even on the main window rather than due to the user
719                    selecting a menu item), and there may be a crash
720                    or other problem when "close_cap_file()" tries to
721                    clean up stuff in the main window.
722
723                    XXX - is there a better place to put this?
724                    Or should we have a routine that *just* closes the
725                    capture file, and doesn't do anything with the UI,
726                    which we'd call here, and another routine that
727                    calls that routine and also cleans up the UI, which
728                    we'd call elsewhere? */
729                 close_cap_file(&cfile, info_bar);
730
731                 /* Exit by leaving the main loop, so that any quit functions
732                    we registered get called. */
733                 gtk_main_quit();
734         }
735 }
736
737 static void 
738 print_usage(void) {
739
740   fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled with %s\n",
741           comp_info_str);
742 #ifdef HAVE_LIBPCAP
743   fprintf(stderr, "%s [ -vh ] [ -kQS ] [ -b <bold font> ] [ -B <byte view height> ]\n",
744           PACKAGE);
745   fprintf(stderr, "\t[ -c count ] [ -D ] [ -f <capture filter> ] [ -i interface ]\n");
746   fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -o <preference setting> ] ...\n");
747   fprintf(stderr, "\t[ -P <packet list height> ] [ -r infile ] [ -R <read filter> ]\n");
748   fprintf(stderr, "\t[ -s snaplen ] [ -t <time stamp format> ] [ -T <tree view height> ]\n");
749   fprintf(stderr, "\t[ -w savefile ]\n");
750 #else
751   fprintf(stderr, "%s [ -vh ] [ -b <bold font> ] [ -B <byte view height> ]\n",
752           PACKAGE);
753   fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -o <preference setting ] ...\n");
754   fprintf(stderr, "\t[ -P <packet list height> ] [ -r infile ] [ -R <read filter> ]\n");
755   fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
756 #endif
757 }
758
759 /* And now our feature presentation... [ fade to music ] */
760 int
761 main(int argc, char *argv[])
762 {
763 #ifdef HAVE_LIBPCAP
764   char                *command_name;
765 #endif
766   char                *s;
767   int                  i;
768   int                  opt;
769   extern char         *optarg;
770   gboolean             arg_error = FALSE;
771 #ifdef HAVE_LIBPCAP
772 #ifdef WIN32
773   char pcap_version[] = "0.4a6";
774 #else
775   extern char          pcap_version[];
776 #endif
777 #endif
778   char                *gpf_path, *pf_path;
779   int                  gpf_open_errno, pf_open_errno;
780   int                  err;
781 #ifdef HAVE_LIBPCAP
782   gboolean             start_capture = FALSE;
783   gchar               *save_file = NULL;
784   GList               *if_list;
785   gchar                err_str[PCAP_ERRBUF_SIZE];
786 #else
787   gboolean             capture_option_specified = FALSE;
788 #endif
789   gint                 pl_size = 280, tv_size = 95, bv_size = 75;
790   gchar               *rc_file, *cf_name = NULL, *rfilter = NULL;
791   dfilter             *rfcode = NULL;
792   gboolean             rfilter_parse_failed = FALSE;
793   e_prefs             *prefs;
794
795   ethereal_path = argv[0];
796
797 #ifdef HAVE_LIBPCAP
798   command_name = get_basename(ethereal_path);
799   /* Set "capture_child" to indicate whether this is going to be a child
800      process for a "-S" capture. */
801   capture_child = (strcmp(command_name, CHILD_NAME) == 0);
802 #endif
803
804   /* Register all dissectors; we must do this before checking for the
805      "-G" flag, as the "-G" flag dumps a list of fields registered
806      by the dissectors, and we must do it before we read the preferences,
807      in case any dissectors register preferences. */
808   dissect_init();
809
810   /* Now register the preferences for any non-dissector modules.
811      We must do that before we read the preferences as well. */
812   prefs_register_modules();
813
814   /* If invoked with the "-G" flag, we dump out a glossary of
815      display filter symbols.
816
817      We must do this before calling "gtk_init()", because "gtk_init()"
818      tries to open an X display, and we don't want to have to do any X
819      stuff just to do a build.
820
821      Given that we call "gtk_init()" before doing the regular argument
822      list processing, so that it can handle X and GTK+ arguments and
823      remove them from the list at which we look, this means we must do
824      this before doing the regular argument list processing, as well.
825
826      This means that:
827
828         you must give the "-G" flag as the first flag on the command line;
829
830         you must give it as "-G", nothing more, nothing less;
831
832         any arguments after the "-G" flag will not be used. */
833   if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
834     proto_registrar_dump();
835     exit(0);
836   }
837
838   /* Set the current locale according to the program environment. 
839    * We haven't localized anything, but some GTK widgets are localized
840    * (the file selection dialogue, for example).
841    * This also sets the C-language locale to the native environment. */
842   gtk_set_locale();
843
844   /* Let GTK get its args */
845   gtk_init (&argc, &argv);
846   
847   prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
848
849   /* Initialize the capture file struct */
850   cfile.plist           = NULL;
851   cfile.plist_end               = NULL;
852   cfile.wth             = NULL;
853   cfile.filename                = NULL;
854   cfile.user_saved              = FALSE;
855   cfile.is_tempfile     = FALSE;
856   cfile.rfcode          = NULL;
857   cfile.dfilter         = NULL;
858   cfile.dfcode          = NULL;
859 #ifdef HAVE_LIBPCAP
860   cfile.cfilter         = g_strdup(EMPTY_FILTER);
861 #endif
862   cfile.iface           = NULL;
863   cfile.save_file               = NULL;
864   cfile.save_file_fd    = -1;
865   cfile.snap            = WTAP_MAX_PACKET_SIZE;
866   cfile.count           = 0;
867   cfile.cinfo.num_cols  = prefs->num_cols;
868   cfile.cinfo.col_fmt      = (gint *) g_malloc(sizeof(gint) * cfile.cinfo.num_cols);
869   cfile.cinfo.fmt_matx  = (gboolean **) g_malloc(sizeof(gboolean *) * cfile.cinfo.num_cols);
870   cfile.cinfo.col_width = (gint *) g_malloc(sizeof(gint) * cfile.cinfo.num_cols);
871   cfile.cinfo.col_title    = (gchar **) g_malloc(sizeof(gchar *) * cfile.cinfo.num_cols);
872   cfile.cinfo.col_data  = (gchar **) g_malloc(sizeof(gchar *) * cfile.cinfo.num_cols);
873
874   /* Assemble the compile-time options */
875   snprintf(comp_info_str, 256,
876 #ifdef GTK_MAJOR_VERSION
877     "GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
878     GTK_MICRO_VERSION,
879 #else
880     "GTK+ (version unknown), %s%s, %s%s, %s%s",
881 #endif
882
883 #ifdef HAVE_LIBPCAP
884    "with libpcap ", pcap_version,
885 #else
886    "without libpcap", "",
887 #endif
888
889 #ifdef HAVE_LIBZ
890 #ifdef ZLIB_VERSION
891    "with libz ", ZLIB_VERSION,
892 #else /* ZLIB_VERSION */
893    "with libz ", "(version unknown)",
894 #endif /* ZLIB_VERSION */
895 #else /* HAVE_LIBZ */
896    "without libz", "",
897 #endif /* HAVE_LIBZ */
898
899 /* Oh, this is pretty */
900 #if defined(HAVE_UCD_SNMP_SNMP_H)
901 #ifdef HAVE_UCD_SNMP_VERSION_H
902    "with UCD SNMP ", VersionInfo
903 #else /* HAVE_UCD_SNMP_VERSION_H */
904    "with UCD SNMP ", "(version unknown)"
905 #endif /* HAVE_UCD_SNMP_VERSION_H */
906 #elif defined(HAVE_SNMP_SNMP_H)
907 #ifdef HAVE_SNMP_VERSION_H
908    "with CMU SNMP ", snmp_Version()
909 #else /* HAVE_SNMP_VERSION_H */
910    "with CMU SNMP ", "(version unknown)"
911 #endif /* HAVE_SNMP_VERSION_H */
912 #else /* no SNMP */
913    "without SNMP", ""
914 #endif
915    );
916
917   /* Now get our args */
918   while ((opt = getopt(argc, argv, "b:B:c:Df:hi:km:no:P:Qr:R:Ss:t:T:w:W:vZ:")) != EOF) {
919     switch (opt) {
920       case 'b':        /* Bold font */
921         bold_font = g_strdup(optarg);
922         break;
923       case 'B':        /* Byte view pane height */
924         bv_size = atoi(optarg);
925         break;
926       case 'c':        /* Capture xxx packets */
927 #ifdef HAVE_LIBPCAP
928         cfile.count = atoi(optarg);
929 #else
930         capture_option_specified = TRUE;
931         arg_error = TRUE;
932 #endif
933         break;
934       case 'D':        /* Turn off DSCP printing */
935         g_ip_dscp_actif = FALSE;
936         break;
937       case 'f':
938 #ifdef HAVE_LIBPCAP
939         if (cfile.cfilter)
940                 g_free(cfile.cfilter);
941         cfile.cfilter = g_strdup(optarg);
942 #else
943         capture_option_specified = TRUE;
944         arg_error = TRUE;
945 #endif
946         break;
947       case 'h':        /* Print help and exit */
948         print_usage();
949         exit(0);
950         break;
951       case 'i':        /* Use interface xxx */
952 #ifdef HAVE_LIBPCAP
953         cfile.iface = g_strdup(optarg);
954 #else
955         capture_option_specified = TRUE;
956         arg_error = TRUE;
957 #endif
958         break;
959       case 'k':        /* Start capture immediately */
960 #ifdef HAVE_LIBPCAP
961         start_capture = TRUE;
962 #else
963         capture_option_specified = TRUE;
964         arg_error = TRUE;
965 #endif
966         break;
967       case 'm':        /* Medium font */
968         medium_font = g_strdup(optarg);
969         break;
970       case 'n':        /* No name resolution */
971         g_resolving_actif = 0;
972         break;
973       case 'o':        /* Override preference from command line */
974         switch (prefs_set_pref(optarg)) {
975
976         case PREFS_SET_SYNTAX_ERR:
977           fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
978           exit(1);
979           break;
980
981         case PREFS_SET_NO_SUCH_PREF:
982           fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
983                         optarg);
984           exit(1);
985           break;
986         }
987         break;
988       case 'P':        /* Packet list pane height */
989         pl_size = atoi(optarg);
990         break;
991       case 'Q':        /* Quit after capture (just capture to file) */
992 #ifdef HAVE_LIBPCAP
993         quit_after_cap = 1;
994         start_capture = TRUE;  /*** -Q implies -k !! ***/
995 #else
996         capture_option_specified = TRUE;
997         arg_error = TRUE;
998 #endif
999         break;
1000       case 'r':        /* Read capture file xxx */
1001         /* We may set "last_open_dir" to "cf_name", and if we change
1002            "last_open_dir" later, we free the old value, so we have to
1003            set "cf_name" to something that's been allocated. */
1004         cf_name = g_strdup(optarg);
1005         break;
1006       case 'R':        /* Read file filter */
1007         rfilter = optarg;
1008         break;
1009       case 's':        /* Set the snapshot (capture) length */
1010 #ifdef HAVE_LIBPCAP
1011         cfile.snap = atoi(optarg);
1012 #else
1013         capture_option_specified = TRUE;
1014         arg_error = TRUE;
1015 #endif
1016         break;
1017       case 'S':        /* "Sync" mode: used for following file ala tail -f */
1018 #ifdef HAVE_LIBPCAP
1019         sync_mode = TRUE;
1020 #else
1021         capture_option_specified = TRUE;
1022         arg_error = TRUE;
1023 #endif
1024         break;
1025       case 't':        /* Time stamp type */
1026         if (strcmp(optarg, "r") == 0)
1027           timestamp_type = RELATIVE;
1028         else if (strcmp(optarg, "a") == 0)
1029           timestamp_type = ABSOLUTE;
1030         else if (strcmp(optarg, "d") == 0)
1031           timestamp_type = DELTA;
1032         else {
1033           fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
1034             optarg);
1035           fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
1036           fprintf(stderr, "or \"d\" for delta.\n");
1037           exit(1);
1038         }
1039         break;
1040       case 'T':        /* Tree view pane height */
1041         tv_size = atoi(optarg);
1042         break;
1043       case 'v':        /* Show version and exit */
1044         printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
1045         exit(0);
1046         break;
1047       case 'w':        /* Write to capture file xxx */
1048 #ifdef HAVE_LIBPCAP
1049         save_file = g_strdup(optarg);
1050 #else
1051         capture_option_specified = TRUE;
1052         arg_error = TRUE;
1053 #endif
1054         break;
1055       case 'W':        /* Write to capture file FD xxx */
1056 #ifdef HAVE_LIBPCAP
1057         cfile.save_file_fd = atoi(optarg);
1058 #else
1059         capture_option_specified = TRUE;
1060         arg_error = TRUE;
1061 #endif
1062         break;
1063
1064 #ifdef _WIN32
1065       case 'Z':        /* Write to pipe FD XXX */
1066 #ifdef HAVE_LIBPCAP
1067         /* associate stdout with pipe */
1068         i = atoi(optarg);
1069         if (dup2(i, 1) < 0) {
1070           fprintf(stderr, "Unable to dup pipe handle\n");
1071           exit(1);
1072         }
1073 #else
1074         capture_option_specified = TRUE;
1075         arg_error = TRUE;
1076 #endif /* HAVE_LIBPCAP */
1077         break;
1078 #endif /* _WIN32 */
1079
1080       default:
1081       case '?':        /* Bad flag - print usage message */
1082         arg_error = TRUE;
1083         break;
1084     }
1085   }
1086
1087   /* Notify all registered modules that have had any of their preferences
1088      changed either from one of the preferences file or from the command
1089      line that its preferences have changed. */
1090   prefs_apply_all();
1091
1092 #ifndef HAVE_LIBPCAP
1093   if (capture_option_specified)
1094     fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
1095 #endif
1096   if (arg_error)
1097     print_usage();
1098 #ifdef HAVE_LIBPCAP
1099   if (start_capture) {
1100     /* We're supposed to do a live capture; did the user specify an interface
1101        to use? */
1102     if (cfile.iface == NULL) {
1103       /* No - pick the first one from the list of interfaces. */
1104       if_list = get_interface_list(&err, err_str);
1105       if (if_list == NULL) {
1106         switch (err) {
1107
1108         case CANT_GET_INTERFACE_LIST:
1109             fprintf(stderr, "ethereal: Can't get list of interfaces: %s\n",
1110                         err_str);
1111             break;
1112
1113         case NO_INTERFACES_FOUND:
1114             fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
1115             break;
1116         }
1117         exit(2);
1118       }
1119       cfile.iface = g_strdup(if_list->data);    /* first interface */
1120       free_interface_list(if_list);
1121     }
1122   }
1123   if (capture_child) {
1124     if (cfile.save_file_fd == -1) {
1125       /* XXX - send this to the standard output as something our parent
1126          should put in an error message box? */
1127       fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
1128       exit(1);
1129     }
1130   }
1131 #endif
1132
1133   /* Build the column format array */  
1134   for (i = 0; i < cfile.cinfo.num_cols; i++) {
1135     cfile.cinfo.col_fmt[i] = get_column_format(i);
1136     cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
1137     cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
1138       NUM_COL_FMTS);
1139     get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
1140     if (cfile.cinfo.col_fmt[i] == COL_INFO)
1141       cfile.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
1142     else
1143       cfile.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
1144   }
1145
1146   if (cfile.snap < 1)
1147     cfile.snap = WTAP_MAX_PACKET_SIZE;
1148   else if (cfile.snap < MIN_PACKET_SIZE)
1149     cfile.snap = MIN_PACKET_SIZE;
1150   
1151   rc_file = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(RC_FILE) + 4);
1152   sprintf(rc_file, "%s/%s", get_home_dir(), RC_FILE);
1153   gtk_rc_parse(rc_file);
1154
1155   if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
1156     fprintf(stderr, "ethereal: Error font %s not found (use -m option)\n", medium_font);
1157     exit(1);
1158   }
1159
1160   if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
1161     fprintf(stderr, "ethereal: Error font %s not found (use -b option)\n", bold_font);
1162     exit(1);
1163   }
1164
1165   create_main_window(pl_size, tv_size, bv_size, prefs);
1166
1167 #ifdef HAVE_LIBPCAP
1168   /* Is this a "child" ethereal, which is only supposed to pop up a
1169      capture box to let us stop the capture, and run a capture
1170      to a file that our parent will read? */
1171   if (!capture_child) {
1172 #endif
1173     /* No.  Pop up the main window, and read in a capture file if
1174        we were told to. */
1175
1176     gtk_widget_show(top_level);
1177     set_menus_for_capture_file(FALSE);
1178
1179     cfile.colors = colfilter_new();
1180
1181     /* If we were given the name of a capture file, read it in now;
1182        we defer it until now, so that, if we can't open it, and pop
1183        up an alert box, the alert box is more likely to come up on
1184        top of the main window - but before the preference-file-error
1185        alert box, so, if we get one of those, it's more likely to come
1186        up on top of us. */
1187     if (cf_name) {
1188       if (rfilter != NULL) {
1189         if (dfilter_compile(rfilter, &rfcode) != 0) {
1190           simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
1191           rfilter_parse_failed = TRUE;
1192         }
1193       }
1194       if (!rfilter_parse_failed) {
1195         if ((err = open_cap_file(cf_name, FALSE, &cfile)) == 0) {
1196           /* "open_cap_file()" succeeded, so it closed the previous
1197              capture file, and thus destroyed any previous read filter
1198              attached to "cf". */
1199           cfile.rfcode = rfcode;
1200           switch (read_cap_file(&cfile, &err)) {
1201
1202           case READ_SUCCESS:
1203           case READ_ERROR:
1204             /* Just because we got an error, that doesn't mean we were unable
1205                to read any of the file; we handle what we could get from the
1206                file. */
1207             break;
1208
1209           case READ_ABORTED:
1210             /* Exit now. */
1211             gtk_exit(0);
1212             break;
1213           }
1214           /* Save the name of the containing directory specified in the
1215              path name, if any; we can write over cf_name, which is a
1216              good thing, given that "get_dirname()" does write over its
1217              argument. */
1218           s = get_dirname(cf_name);
1219           if (s != NULL)
1220             last_open_dir = s;
1221         } else {
1222           dfilter_destroy(rfcode);
1223           cfile.rfcode = NULL;
1224         }
1225       }
1226     }
1227 #ifdef HAVE_LIBPCAP
1228   }
1229 #endif
1230
1231   /* If the global preferences file exists but we failed to open it,
1232      pop up an alert box; we defer that until now, so that the alert
1233      box is more likely to come up on top of the main window. */
1234   if (gpf_path != NULL) {
1235       simple_dialog(ESD_TYPE_WARN, NULL,
1236         "Could not open global preferences file\n\"%s\": %s.", gpf_path,
1237         strerror(gpf_open_errno));
1238   }
1239
1240   /* If the user's preferences file exists but we failed to open it,
1241      pop up an alert box; we defer that until now, so that the alert
1242      box is more likely to come up on top of the main window. */
1243   if (pf_path != NULL) {
1244       simple_dialog(ESD_TYPE_WARN, NULL,
1245         "Could not open your preferences file\n\"%s\": %s.", pf_path,
1246         strerror(pf_open_errno));
1247   }
1248
1249 #ifdef HAVE_LIBPCAP
1250   if (capture_child) {
1251     /* This is the child process for a sync mode or fork mode capture,
1252        so just do the low-level work of a capture - don't create
1253        a temporary file and fork off *another* child process (so don't
1254        call "do_capture()"). */
1255
1256        capture();
1257
1258        /* The capture is done; there's nothing more for us to do. */
1259        gtk_exit(0);
1260   } else {
1261     if (start_capture) {
1262       /* "-k" was specified; start a capture. */
1263       do_capture(save_file);
1264     }
1265     else {
1266       set_menus_for_capture_in_progress(FALSE);
1267     }
1268   }
1269 #else
1270   set_menus_for_capture_in_progress(FALSE);
1271 #endif
1272
1273   gtk_main();
1274
1275   dissect_cleanup();
1276   g_free(rc_file);
1277
1278   gtk_exit(0);
1279
1280   /* This isn't reached, but we need it to keep GCC from complaining
1281      that "main()" returns without returning a value - it knows that
1282      "exit()" never returns, but it doesn't know that "gtk_exit()"
1283      doesn't, as GTK+ doesn't declare it with the attribute
1284      "noreturn". */
1285   return 0;     /* not reached */
1286 }
1287
1288 #ifdef WIN32
1289
1290 /* We build this as a GUI subsystem application on Win32, so
1291    "WinMain()", not "main()", gets called.
1292
1293    Hack shamelessly stolen from the Win32 port of the GIMP. */
1294 #ifdef __GNUC__
1295 #define _stdcall  __attribute__((stdcall))
1296 #endif
1297
1298 int _stdcall
1299 WinMain (struct HINSTANCE__ *hInstance,
1300          struct HINSTANCE__ *hPrevInstance,
1301          char               *lpszCmdLine,
1302          int                 nCmdShow)
1303 {
1304   return main (__argc, __argv);
1305 }
1306
1307 #endif
1308
1309 static void
1310 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
1311 {
1312   GtkWidget           *main_vbox, *menubar, *u_pane, *l_pane,
1313                       *stat_hbox,
1314                       *filter_bt, *filter_cm, *filter_te,
1315                       *filter_reset;
1316   GList               *filter_list = NULL;
1317   GtkStyle            *pl_style;
1318   GtkAccelGroup       *accel;
1319   int                   i;
1320
1321   /* Main window */  
1322   top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1323   gtk_widget_set_name(top_level, "main window");
1324   gtk_signal_connect(GTK_OBJECT(top_level), "delete_event", 
1325     GTK_SIGNAL_FUNC(main_window_delete_event_cb), NULL);
1326   gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
1327   gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
1328   gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
1329
1330   /* Container for menu bar, paned windows and progress/info box */
1331   main_vbox = gtk_vbox_new(FALSE, 1);
1332   gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
1333   gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
1334   gtk_widget_show(main_vbox);
1335
1336   /* Menu bar */
1337   get_main_menu(&menubar, &accel);
1338   gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
1339   gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
1340   gtk_widget_show(menubar);
1341
1342   /* Panes for the packet list, tree, and byte view */
1343   u_pane = gtk_vpaned_new();
1344   gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
1345   l_pane = gtk_vpaned_new();
1346   gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
1347   gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
1348   gtk_widget_show(l_pane);
1349   gtk_paned_add2(GTK_PANED(u_pane), l_pane);
1350   gtk_widget_show(u_pane);
1351
1352   /* Packet list */
1353   pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
1354   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
1355     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1356   set_scrollbar_placement_scrollw(pkt_scrollw, prefs->gui_scrollbar_on_right);
1357   remember_scrolled_window(pkt_scrollw);
1358   gtk_widget_show(pkt_scrollw);
1359   gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
1360
1361   packet_list = gtk_clist_new_with_titles(cfile.cinfo.num_cols, cfile.cinfo.col_title);
1362   gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
1363   
1364   set_plist_sel_browse(prefs->gui_plist_sel_browse);
1365   pl_style = gtk_style_new();
1366   gdk_font_unref(pl_style->font);
1367   pl_style->font = m_r_font;
1368   gtk_widget_set_style(packet_list, pl_style);
1369   gtk_widget_set_name(packet_list, "packet list");
1370   gtk_signal_connect (GTK_OBJECT (packet_list), "click_column",
1371     GTK_SIGNAL_FUNC(packet_list_click_column_cb), NULL);
1372   gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
1373     GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
1374   gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
1375     GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
1376   for (i = 0; i < cfile.cinfo.num_cols; i++) {
1377     if (get_column_resize_type(cfile.cinfo.col_fmt[i]) != RESIZE_MANUAL)
1378       gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
1379
1380     /* Right-justify the packet number column. */
1381     if (cfile.cinfo.col_fmt[i] == COL_NUMBER)
1382       gtk_clist_set_column_justification(GTK_CLIST(packet_list), i, 
1383         GTK_JUSTIFY_RIGHT);
1384
1385     /* Save static column sizes to use during a "-S" capture, so that
1386        the columns don't resize during a live capture. */
1387     cfile.cinfo.col_width[i] = gdk_string_width(pl_style->font,
1388                                 get_column_longest_string(get_column_format(i)));
1389   }
1390   gtk_widget_set_usize(packet_list, -1, pl_size);
1391   gtk_signal_connect_object(GTK_OBJECT(packet_list), "button_press_event",
1392     GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
1393   gtk_clist_set_compare_func(GTK_CLIST(packet_list), packet_list_compare);
1394   gtk_widget_show(packet_list);
1395
1396   /* Tree view */
1397   create_tree_view(tv_size, prefs, l_pane, &tv_scrollw, &tree_view,
1398                         prefs->gui_scrollbar_on_right);
1399   gtk_signal_connect(GTK_OBJECT(tree_view), "tree-select-row",
1400     GTK_SIGNAL_FUNC(tree_view_select_row_cb), NULL);
1401   gtk_signal_connect(GTK_OBJECT(tree_view), "tree-unselect-row",
1402     GTK_SIGNAL_FUNC(tree_view_unselect_row_cb), NULL);
1403   gtk_signal_connect_object(GTK_OBJECT(tree_view), "button_press_event",
1404     GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY));
1405   gtk_widget_show(tree_view);
1406
1407   item_style = gtk_style_new();
1408   gdk_font_unref(item_style->font);
1409   item_style->font = m_r_font;
1410
1411   /* Byte view. */
1412   create_byte_view(bv_size, l_pane, &byte_view, &bv_scrollw,
1413                         prefs->gui_scrollbar_on_right);
1414
1415   /* Filter/info box */
1416   stat_hbox = gtk_hbox_new(FALSE, 1);
1417   gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
1418   gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
1419   gtk_widget_show(stat_hbox);
1420
1421   filter_bt = gtk_button_new_with_label("Filter:");
1422   /* A non-null pointer passed to "filter_browse_cb()" causes it to
1423      give the dialog box it pops up an "Apply" button. */
1424   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
1425     GTK_SIGNAL_FUNC(filter_browse_cb), "");
1426   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
1427   gtk_widget_show(filter_bt);
1428   
1429   filter_cm = gtk_combo_new();
1430   filter_list = g_list_append (filter_list, "");
1431   gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
1432   gtk_combo_disable_activate(GTK_COMBO(filter_cm));
1433   filter_te = GTK_COMBO(filter_cm)->entry;
1434   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
1435   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
1436   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
1437   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
1438   gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
1439     GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
1440   gtk_widget_show(filter_cm);
1441
1442   filter_reset = gtk_button_new_with_label("Reset");
1443   gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
1444   gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
1445                      GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
1446   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
1447   gtk_widget_show(filter_reset);
1448
1449   /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
1450    * of any widget that ends up calling a callback which needs
1451    * that text entry pointer */
1452   set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
1453   set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
1454   set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
1455   set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
1456   set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
1457
1458   info_bar = gtk_statusbar_new();
1459   main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
1460   file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
1461   gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
1462   gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
1463   gtk_widget_show(info_bar);
1464 }