Provide for per-protocol-tree data in the proto_tree code.
[obnox/wireshark/wip.git] / gtk / main.c
1 /* main.c
2  *
3  * $Id: main.c,v 1.219 2001/12/18 19:09:08 gram Exp $
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@ethereal.com>
7  * Copyright 1998 Gerald Combs
8  *
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
12  * 
13  * 
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.
18  * 
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.
23  * 
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.
27  *
28  *
29  * To do:
30  * - Graphs
31  * - Check for end of packet in dissect_* routines.
32  * - Playback window
33  * - Multiple window support
34  * - Add cut/copy/paste
35  * - Create header parsing routines
36  * - Make byte view selections more fancy?
37  *
38  */
39
40 #ifdef HAVE_CONFIG_H
41 # include "config.h"
42 #endif
43
44 #include <gtk/gtk.h>
45
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <ctype.h>
50
51 #ifdef HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54
55 #include <errno.h>
56
57 #ifdef HAVE_SYS_TYPES_H
58 #include <sys/types.h>
59 #endif
60
61 #ifdef HAVE_SYS_STAT_H
62 #include <sys/stat.h>
63 #endif
64
65 #ifdef HAVE_IO_H
66 #include <io.h> /* open/close on win32 */
67 #endif
68
69 #ifdef HAVE_DIRECT_H
70 #include <direct.h>
71 #endif
72
73 #ifdef HAVE_NETINET_IN_H
74 #include <netinet/in.h>
75 #endif
76
77 #include <signal.h>
78
79 #ifdef HAVE_LIBPCAP
80 #include <pcap.h>
81 #endif
82
83 #ifdef HAVE_LIBZ
84 #include <zlib.h>       /* to get the libz version number */
85 #endif
86
87 #ifdef NEED_SNPRINTF_H
88 # include "snprintf.h"
89 #endif
90
91 #if defined(HAVE_UCD_SNMP_SNMP_H)
92 #ifdef HAVE_UCD_SNMP_VERSION_H
93 #include <ucd-snmp/version.h>
94 #endif /* HAVE_UCD_SNMP_VERSION_H */
95 #elif defined(HAVE_SNMP_SNMP_H)
96 #ifdef HAVE_SNMP_VERSION_H
97 #include <snmp/version.h>
98 #endif /* HAVE_SNMP_VERSION_H */
99 #endif /* SNMP */
100
101 #ifdef NEED_STRERROR_H
102 #include "strerror.h"
103 #endif
104
105 #ifdef NEED_GETOPT_H
106 #include "getopt.h"
107 #endif
108
109 #ifdef WIN32 /* Needed for console I/O */
110 #include <fcntl.h>
111 #include <conio.h>
112 #endif
113
114 #include <epan.h>
115 #include <epan/filesystem.h>
116 #include <epan_dissect.h>
117
118 #include "main.h"
119 #include "timestamp.h"
120 #include "packet.h"
121 #include "capture.h"
122 #include "summary.h"
123 #include "file.h"
124 #include "filters.h"
125 #include "menu.h"
126 #include "../menu.h"
127 #include "color.h"
128 #include "color_utils.h"
129 #include "filter_prefs.h"
130 #include "prefs_dlg.h"
131 #include "file_dlg.h"
132 #include "column.h"
133 #include "print.h"
134 #include "resolv.h"
135 #ifdef HAVE_LIBPCAP
136 #include "pcap-util.h"
137 #endif
138 #include "statusbar.h"
139 #include "simple_dialog.h"
140 #include "proto_draw.h"
141 #include "dfilter/dfilter.h"
142 #include "keys.h"
143 #include "packet_win.h"
144 #include "gtkglobals.h"
145 #include "plugins.h"
146 #include "colors.h"
147 #include "strutil.h"
148 #include "register.h"
149 #include "prefs.h"
150 #include "ringbuffer.h"
151 #include "ui_util.h"
152 #include "image/clist_ascend.xpm"
153 #include "image/clist_descend.xpm"
154
155 #ifdef WIN32
156 #include "capture-wpcap.h"
157 #endif
158
159 typedef struct column_arrows {
160   GtkWidget *table;
161   GtkWidget *ascend_pm;
162   GtkWidget *descend_pm;
163 } column_arrows;
164
165 capture_file cfile;
166 GtkWidget   *top_level, *packet_list, *tree_view, *byte_nb_ptr,
167             *tv_scrollw, *pkt_scrollw;
168 static GtkWidget        *info_bar, *bv_scrollw;
169 GdkFont     *m_r_font, *m_b_font;
170 guint           m_font_height, m_font_width;
171 static guint    main_ctx, file_ctx, help_ctx;
172 static GString *comp_info_str;
173 gchar       *ethereal_path = NULL;
174 gchar       *last_open_dir = NULL;
175
176 ts_type timestamp_type = RELATIVE;
177
178 GtkStyle *item_style;
179
180 /* Specifies the field currently selected in the GUI protocol tree */
181 field_info *finfo_selected = NULL;
182
183 #ifdef WIN32
184 static gboolean has_no_console; /* TRUE if app has no console */
185 static gboolean console_was_created; /* TRUE if console was created */
186 static void create_console(void);
187 static void destroy_console(void);
188 static void console_log_handler(const char *log_domain,
189     GLogLevelFlags log_level, const char *message, gpointer user_data);
190 #endif
191
192 static void create_main_window(gint, gint, gint, e_prefs*);
193
194 /* About Ethereal window */
195 void
196 about_ethereal( GtkWidget *w, gpointer data ) {
197   simple_dialog(ESD_TYPE_INFO, NULL,
198                 "Ethereal - Network Protocol Analyzer\n"
199                 "Version " VERSION " (C) 1998-2000 Gerald Combs <gerald@ethereal.com>\n"
200                 "Compiled %s\n\n"
201
202                 "Check the man page for complete documentation and\n"
203                 "for the list of contributors.\n"
204
205                 "\nSee http://www.ethereal.com/ for more information.",
206                  comp_info_str->str);
207 }
208
209 void
210 set_fonts(GdkFont *regular, GdkFont *bold)
211 {
212         /* Yes, assert. The code that loads the font should check
213          * for NULL and provide its own error message. */
214         g_assert(m_r_font && m_b_font);
215         m_r_font = regular;
216         m_b_font = bold;
217
218         m_font_height = m_r_font->ascent + m_r_font->descent;
219         m_font_width = gdk_string_width(m_r_font, "0");
220 }
221
222
223 /* Match selected byte pattern */
224 void
225 match_selected_cb(GtkWidget *w, gpointer data)
226 {
227     char                *buf;
228     GtkWidget           *filter_te;
229
230     filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
231
232     buf = proto_alloc_dfilter_string(finfo_selected, cfile.pd);
233
234     /* create a new one and set the display filter entry accordingly */
235     gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
236
237     /* Run the display filter so it goes in effect. */
238     filter_packets(&cfile, buf);
239
240     /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
241 }
242
243
244
245 /* Run the current display filter on the current packet set, and
246    redisplay. */
247 static void
248 filter_activate_cb(GtkWidget *w, gpointer data)
249 {
250   GtkCombo  *filter_cm = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_CM_KEY);
251   GList     *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY);
252   GList     *li, *nl = NULL;
253   gboolean   add_filter = TRUE;
254   
255   char *s = gtk_entry_get_text(GTK_ENTRY(w));
256   
257   /* GtkCombos don't let us get at their list contents easily, so we maintain
258      our own filter list, and feed it to gtk_combo_set_popdown_strings when
259      a new filter is added. */
260   if (filter_packets(&cfile, g_strdup(s))) {
261     li = g_list_first(filter_list);
262     while (li) {
263       if (li->data && strcmp(s, li->data) == 0)
264         add_filter = FALSE;
265       li = li->next;
266     }
267
268     if (add_filter) {
269       filter_list = g_list_append(filter_list, g_strdup(s));
270       li = g_list_first(filter_list);
271       while (li) {
272         nl = g_list_append(nl, strdup(li->data));
273         li = li->next;
274       }
275       gtk_combo_set_popdown_strings(filter_cm, nl);
276       gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
277     }
278   }
279 }
280
281 /* redisplay with no display filter */
282 static void
283 filter_reset_cb(GtkWidget *w, gpointer data)
284 {
285   GtkWidget *filter_te = NULL;
286
287   if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
288     gtk_entry_set_text(GTK_ENTRY(filter_te), "");
289   }
290
291   filter_packets(&cfile, NULL);
292 }
293
294 /* GTKClist compare routine, overrides default to allow numeric comparison */
295 static gint
296 packet_list_compare(GtkCList *clist, gconstpointer  ptr1, gconstpointer  ptr2)
297 {
298   /* Get row text strings */
299   char *text1 = GTK_CELL_TEXT (((GtkCListRow *)ptr1)->cell[clist->sort_column])->text;
300   char *text2 = GTK_CELL_TEXT (((GtkCListRow *)ptr2)->cell[clist->sort_column])->text;
301
302   /* Attempt to convert to numbers */
303   double  num1 = atof(text1);
304   double  num2 = atof(text2);
305   
306   gint  col_fmt = cfile.cinfo.col_fmt[clist->sort_column];
307   
308   if ((col_fmt == COL_NUMBER) || (col_fmt == COL_REL_TIME) || (col_fmt == COL_DELTA_TIME) ||
309       ((col_fmt == COL_CLS_TIME) && (timestamp_type == RELATIVE)) ||
310       ((col_fmt == COL_CLS_TIME) && (timestamp_type == DELTA))    ||
311       (col_fmt == COL_UNRES_SRC_PORT) || (col_fmt == COL_UNRES_DST_PORT) ||
312       ((num1 != 0) && (num2 != 0) && ((col_fmt == COL_DEF_SRC_PORT) || (col_fmt == COL_RES_SRC_PORT) ||
313                                       (col_fmt == COL_DEF_DST_PORT) || (col_fmt == COL_RES_DST_PORT))) ||
314       (col_fmt == COL_PACKET_LENGTH)) {
315
316     /* Compare numeric column */
317
318     if (num1 < num2)
319       return -1;
320     else if (num1 > num2)
321       return 1;
322     else
323       return 0;
324   }
325   
326   else {
327     
328     /* Compare text column */
329     if (!text2)
330       return (text1 != NULL);
331
332     if (!text1)
333       return -1;
334
335     return strcmp(text1, text2);
336   }
337 }
338
339 /* What to do when a column is clicked */
340 static void 
341 packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data)
342 {
343   column_arrows *col_arrows = (column_arrows *) data;
344   int i;
345   
346   gtk_clist_freeze(clist);
347   
348   for (i = 0; i < cfile.cinfo.num_cols; i++) {
349     gtk_widget_hide(col_arrows[i].ascend_pm);
350     gtk_widget_hide(col_arrows[i].descend_pm);
351   }
352   
353   if (column == clist->sort_column) {
354     if (clist->sort_type == GTK_SORT_ASCENDING) {
355       clist->sort_type = GTK_SORT_DESCENDING;
356       gtk_widget_show(col_arrows[column].descend_pm);
357     } else {
358       clist->sort_type = GTK_SORT_ASCENDING;
359       gtk_widget_show(col_arrows[column].ascend_pm);
360     }
361   }
362   else {
363     clist->sort_type = GTK_SORT_ASCENDING;
364     gtk_widget_show(col_arrows[column].ascend_pm);
365     gtk_clist_set_sort_column(clist, column);
366   }
367   gtk_clist_thaw(clist);
368
369   gtk_clist_sort(clist);
370 }
371
372 /* mark packets */
373 static void 
374 set_frame_mark(gboolean set, frame_data *frame, gint row) {
375   GdkColor fg, bg;
376
377   if (row == -1)
378     return;
379   if (set) {
380     mark_frame(&cfile, frame);
381     color_t_to_gdkcolor(&fg, &prefs.gui_marked_fg);
382     color_t_to_gdkcolor(&bg, &prefs.gui_marked_bg);
383   } else {
384     unmark_frame(&cfile, frame);
385     fg = BLACK;
386     bg = WHITE;
387   }
388   file_set_save_marked_sensitive();
389   gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
390   gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
391 }
392
393 static void
394 packet_list_button_pressed_cb(GtkWidget *w, GdkEvent *event, gpointer data) {
395   
396   GdkEventButton *event_button = (GdkEventButton *)event;
397   gint row, column;
398
399   if (w == NULL || event == NULL)
400     return;
401
402   if (event->type == GDK_BUTTON_PRESS && event_button->button == 2 &&
403       gtk_clist_get_selection_info(GTK_CLIST(w), event_button->x, event_button->y,
404                                    &row, &column)) {
405     frame_data *fdata = (frame_data *) gtk_clist_get_row_data(GTK_CLIST(w), row);
406     set_frame_mark(!fdata->flags.marked, fdata, row);
407   }
408 }
409
410 void mark_frame_cb(GtkWidget *w, gpointer data) {
411   if (cfile.current_frame) {
412     /* XXX hum, should better have a "cfile->current_row" here ... */
413     set_frame_mark(!cfile.current_frame->flags.marked,
414                    cfile.current_frame, 
415                    gtk_clist_find_row_from_data(GTK_CLIST(packet_list), 
416                                                 cfile.current_frame));
417   }
418 }
419
420 static void mark_all_frames(gboolean set) {
421   frame_data *fdata;
422   for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
423     set_frame_mark(set,
424                    fdata,
425                    gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata));    
426   }
427 }
428
429 void update_marked_frames(void) {
430   frame_data *fdata;
431   if (cfile.plist == NULL) return;
432   for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
433     if (fdata->flags.marked)
434       set_frame_mark(TRUE,
435                      fdata,
436                      gtk_clist_find_row_from_data(GTK_CLIST(packet_list),
437                                                   fdata));
438   }
439 }
440
441 void mark_all_frames_cb(GtkWidget *w, gpointer data) {
442   mark_all_frames(TRUE);
443 }
444
445 void unmark_all_frames_cb(GtkWidget *w, gpointer data) {
446   mark_all_frames(FALSE);
447 }
448
449 /* What to do when a list item is selected/unselected */
450 static void
451 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
452
453 /* Remove the hex display tabbed pages */
454   while( (gtk_notebook_get_nth_page( GTK_NOTEBOOK(byte_nb_ptr), 0)))
455     gtk_notebook_remove_page( GTK_NOTEBOOK(byte_nb_ptr), 0);
456
457   select_packet(&cfile, row);
458 }
459
460
461 static void
462 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
463
464   unselect_packet(&cfile);
465 }
466
467
468 static void
469 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
470 {
471         field_info      *finfo;
472         gchar           *help_str = NULL;
473         gboolean        has_blurb = FALSE;
474         guint           length = 0, byte_len;
475         GtkWidget       *byte_view;
476         guint8          *byte_data;
477
478         g_assert(node);
479         finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
480         if (!finfo) return;
481
482         if (finfo->ds_name != NULL)
483                 set_notebook_page(  byte_nb_ptr, find_notebook_page( byte_nb_ptr, finfo->ds_name));
484
485         byte_view = gtk_object_get_data(GTK_OBJECT(byte_nb_ptr), E_BYTE_VIEW_TEXT_INFO_KEY);
486         byte_data = gtk_object_get_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_DATA_PTR_KEY);
487         byte_len = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_DATA_LEN_KEY));
488
489         g_assert(byte_data);
490
491         finfo_selected = finfo;
492         set_menus_for_selected_tree_row(TRUE);
493
494         if (finfo->hfinfo) {
495           if (finfo->hfinfo->blurb != NULL && 
496               finfo->hfinfo->blurb[0] != '\0') {
497             has_blurb = TRUE;
498             length = strlen(finfo->hfinfo->blurb);
499           } else {
500             length = strlen(finfo->hfinfo->name);
501           }
502           statusbar_pop_field_msg();    /* get rid of current help msg */
503           if (length) {
504             length += strlen(finfo->hfinfo->abbrev) + 10;
505             help_str = g_malloc(sizeof(gchar) * length);
506             sprintf(help_str, "%s (%s)", 
507                (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name,
508                finfo->hfinfo->abbrev);
509             statusbar_push_field_msg(help_str);
510             g_free(help_str);
511           } else {
512             /*
513              * Don't show anything if the field name is zero-length;
514              * the pseudo-field for "proto_tree_add_text()" is such
515              * a field, and we don't want "Text (text)" showing up
516              * on the status line if you've selected such a field.
517              *
518              * XXX - there are zero-length fields for which we *do*
519              * want to show the field name.
520              *
521              * XXX - perhaps the name and abbrev field should be null
522              * pointers rather than null strings for that pseudo-field,
523              * but we'd have to add checks for null pointers in some
524              * places if we did that.
525              *
526              * Or perhaps protocol tree items added with
527              * "proto_tree_add_text()" should have -1 as the field index,
528              * with no pseudo-field being used, but that might also
529              * require special checks for -1 to be added.
530              */
531             statusbar_push_field_msg("");
532           }
533         }
534
535         packet_hex_print(GTK_TEXT(byte_view), byte_data, cfile.current_frame,
536                 finfo, byte_len);
537 }
538
539 static void
540 tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
541 {
542         GtkWidget       *byte_view;
543         guint8  *data;
544         gint    len;    
545
546         /*
547          * Which byte view is displaying the current protocol tree
548          * row's data?
549          */
550         len = get_byte_view_and_data( byte_nb_ptr, &byte_view, &data);
551         if ( len < 0)
552                 return; /* none */
553
554         unselect_field();
555         packet_hex_print(GTK_TEXT(byte_view), data, cfile.current_frame,
556                 NULL, len);
557 }
558
559 void collapse_all_cb(GtkWidget *widget, gpointer data) {
560   if (cfile.edt->tree)
561     collapse_all_tree(cfile.edt->tree, tree_view);
562 }
563
564 void expand_all_cb(GtkWidget *widget, gpointer data) {
565   if (cfile.edt->tree)
566     expand_all_tree(cfile.edt->tree, tree_view);
567 }
568
569 void resolve_name_cb(GtkWidget *widget, gpointer data) {
570   if (cfile.edt->tree) {
571     gint tmp = prefs.name_resolve;
572     prefs.name_resolve = PREFS_RESOLV_ALL;
573     gtk_clist_clear ( GTK_CLIST(tree_view) );
574     proto_tree_draw(cfile.edt->tree, tree_view);
575     prefs.name_resolve = tmp;
576   }
577 }
578
579 /* Set the scrollbar placement of a scrolled window based upon pos value:
580    0 = left, 1 = right */
581 void
582 set_scrollbar_placement_scrollw(GtkWidget *scrollw, int pos) /* 0=left, 1=right */
583 {
584         if (pos) {
585                 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
586                                 GTK_CORNER_TOP_LEFT);
587         } else {
588                 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
589                                 GTK_CORNER_TOP_RIGHT);
590         }
591 }
592
593 /* List of all scrolled windows, so we can globally set the scrollbar
594    placement of them. */
595 static GList *scrolled_windows;
596
597 /* Add a scrolled window to the list of scrolled windows. */
598 static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
599
600 void
601 remember_scrolled_window(GtkWidget *scrollw)
602 {
603   scrolled_windows = g_list_append(scrolled_windows, scrollw);
604
605   /* Catch the "destroy" event on the widget, so that we remove it from
606      the list when it's destroyed. */
607   gtk_signal_connect(GTK_OBJECT(scrollw), "destroy",
608                      GTK_SIGNAL_FUNC(forget_scrolled_window), NULL);
609 }
610
611 /* Remove a scrolled window from the list of scrolled windows. */
612 static void
613 forget_scrolled_window(GtkWidget *scrollw, gpointer data)
614 {
615   scrolled_windows = g_list_remove(scrolled_windows, scrollw);
616 }
617
618 static void
619 set_scrollbar_placement_cb(gpointer data, gpointer user_data)
620 {
621         set_scrollbar_placement_scrollw((GtkWidget *)data,
622             *(int *)user_data);
623 }
624
625 /* Set the scrollbar placement of all scrolled windows based on pos value:
626    0 = left, 1 = right */
627 void
628 set_scrollbar_placement_all(int pos)
629 {
630         g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, &pos);
631 }
632
633 /* Set the selection mode of the packet list window. */
634 void
635 set_plist_sel_browse(gboolean val)
636 {
637         gboolean old_val;
638
639         old_val =
640             (GTK_CLIST(packet_list)->selection_mode == GTK_SELECTION_SINGLE);
641
642         if (val == old_val) {
643                 /*
644                  * The mode isn't changing, so don't do anything.
645                  * In particular, don't gratuitiously unselect the
646                  * current packet.
647                  *
648                  * XXX - why do we have to unselect the current packet
649                  * ourselves?  The documentation for the GtkCList at
650                  *
651                  *      http://developer.gnome.org/doc/API/gtk/gtkclist.html
652                  *
653                  * says "Note that setting the widget's selection mode to
654                  * one of GTK_SELECTION_BROWSE or GTK_SELECTION_SINGLE will
655                  * cause all the items in the GtkCList to become deselected."
656                  */
657                 return;
658         }
659
660         if (finfo_selected)
661                 unselect_packet(&cfile);
662
663         /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
664          * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
665         if (val) {
666                 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_SINGLE);
667         }
668         else {
669                 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_BROWSE);
670         }
671 }
672         
673 /* Set the font of the packet list window. */
674 void
675 set_plist_font(GdkFont *font)
676 {
677         GtkStyle *style;
678         int i;
679
680         style = gtk_style_new();
681         gdk_font_unref(style->font);
682         style->font = font;
683         gdk_font_ref(font);
684
685         gtk_widget_set_style(packet_list, style);
686
687         /* Compute static column sizes to use during a "-S" capture, so that
688            the columns don't resize during a live capture. */
689         for (i = 0; i < cfile.cinfo.num_cols; i++) {
690                 cfile.cinfo.col_width[i] = gdk_string_width(font,
691                         get_column_longest_string(get_column_format(i)));
692         }
693 }
694
695 /*
696  * Push a message referring to file access onto the statusbar.
697  */
698 void
699 statusbar_push_file_msg(gchar *msg)
700 {
701         gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg);
702 }
703
704 /*
705  * Pop a message referring to file access off the statusbar.
706  */
707 void
708 statusbar_pop_file_msg(void)
709 {
710         gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
711 }
712
713 /*
714  * XXX - do we need multiple statusbar contexts?
715  */
716
717 /*
718  * Push a message referring to the currently-selected field onto the statusbar.
719  */
720 void
721 statusbar_push_field_msg(gchar *msg)
722 {
723         gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg);
724 }
725
726 /*
727  * Pop a message referring to the currently-selected field off the statusbar.
728  */
729 void
730 statusbar_pop_field_msg(void)
731 {
732         gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
733 }
734
735 static gboolean
736 main_window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
737 {
738         file_quit_cmd_cb(widget, data);
739
740         /* Say that the window should be deleted. */
741         return FALSE;
742 }
743
744 void
745 file_quit_cmd_cb (GtkWidget *widget, gpointer data)
746 {
747         /* XXX - should we check whether the capture file is an
748            unsaved temporary file for a live capture and, if so,
749            pop up a "do you want to exit without saving the capture
750            file?" dialog, and then just return, leaving said dialog
751            box to forcibly quit if the user clicks "OK"?
752
753            If so, note that this should be done in a subroutine that
754            returns TRUE if we do so, and FALSE otherwise, and that
755            "main_window_delete_event_cb()" should return its
756            return value. */
757
758         /* Are we in the middle of reading a capture? */
759         if (cfile.state == FILE_READ_IN_PROGRESS) {
760                 /* Yes, so we can't just close the file and quit, as
761                    that may yank the rug out from under the read in
762                    progress; instead, just set the state to
763                    "FILE_READ_ABORTED" and return - the code doing the read
764                    will check for that and, if it sees that, will clean
765                    up and quit. */
766                 cfile.state = FILE_READ_ABORTED;
767         } else {
768                 /* Close any capture file we have open; on some OSes, you
769                    can't unlink a temporary capture file if you have it
770                    open.
771                    "close_cap_file()" will unlink it after closing it if
772                    it's a temporary file.
773
774                    We do this here, rather than after the main loop returns,
775                    as, after the main loop returns, the main window may have
776                    been destroyed (if this is called due to a "destroy"
777                    even on the main window rather than due to the user
778                    selecting a menu item), and there may be a crash
779                    or other problem when "close_cap_file()" tries to
780                    clean up stuff in the main window.
781
782                    XXX - is there a better place to put this?
783                    Or should we have a routine that *just* closes the
784                    capture file, and doesn't do anything with the UI,
785                    which we'd call here, and another routine that
786                    calls that routine and also cleans up the UI, which
787                    we'd call elsewhere? */
788                 close_cap_file(&cfile);
789
790                 /* Exit by leaving the main loop, so that any quit functions
791                    we registered get called. */
792                 gtk_main_quit();
793         }
794 }
795
796 static void 
797 print_usage(void) {
798
799   fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled %s\n",
800           comp_info_str->str);
801 #ifdef HAVE_LIBPCAP
802   fprintf(stderr, "%s [ -vh ] [ -klpQS ] [ -a <capture autostop condition> ] ...\n",
803           PACKAGE);
804   fprintf(stderr, "\t[ -b <number of ringbuffer files> ] [ -B <byte view height> ]\n");
805   fprintf(stderr, "\t[ -c <count> ] [ -f <capture filter> ] [ -i <interface> ]\n");
806   fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -N <resolving> ]\n");
807   fprintf(stderr, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
808   fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
809   fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
810   fprintf(stderr, "\t[ -w <savefile> ] [ <infile> ]\n");
811 #else
812   fprintf(stderr, "%s [ -vh ] [ -B <byte view height> ] [ -m <medium font> ]\n",
813           PACKAGE);
814   fprintf(stderr, "\t[ -n ] [ -N <resolving> ]\n");
815   fprintf(stderr, "\t[ -o <preference setting> ... [ -P <packet list height> ]\n");
816   fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -t <time stamp format> ]\n");
817   fprintf(stderr, "\t[ -T <tree view height> ] [ <infile> ]\n");
818 #endif
819 }
820
821 static void 
822 show_version(void)
823 {
824 #ifdef WIN32
825   create_console();
826 #endif
827
828   printf("%s %s, %s\n", PACKAGE, VERSION, comp_info_str->str);
829 }
830
831 static int
832 get_positive_int(const char *string, const char *name)
833 {
834   long number;
835   char *p;
836
837   number = strtol(string, &p, 10);
838   if (p == string || *p != '\0') {
839     fprintf(stderr, "ethereal: The specified %s \"%s\" is not a decimal number\n",
840             name, string);
841     exit(1);
842   }
843   if (number < 0) {
844     fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
845             name, string);
846     exit(1);
847   }
848   if (number > INT_MAX) {
849     fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
850             name, string, INT_MAX);
851     exit(1);
852   }
853   return number;
854 }
855
856 /*
857  * Given a string of the form "<autostop criterion>:<value>", as might appear
858  * as an argument to a "-a" option, parse it and set the criterion in
859  * question.  Return an indication of whether it succeeded or failed
860  * in some fashion.
861  */
862 static gboolean
863 set_autostop_criterion(const char *autostoparg)
864 {
865   u_char *p, *colonp;
866
867   colonp = strchr(autostoparg, ':');
868   if (colonp == NULL)
869     return FALSE;
870
871   p = colonp;
872   *p++ = '\0';
873
874   /*
875    * Skip over any white space (there probably won't be any, but
876    * as we allow it in the preferences file, we might as well
877    * allow it here).
878    */
879   while (isspace(*p))
880     p++;
881   if (*p == '\0') {
882     /*
883      * Put the colon back, so if our caller uses, in an
884      * error message, the string they passed us, the message
885      * looks correct.
886      */
887     *colonp = ':';
888     return FALSE;
889   }
890   if (strcmp(autostoparg,"duration") == 0) {
891     cfile.autostop_duration = get_positive_int(p,"autostop duration");
892   } else if (strcmp(autostoparg,"filesize") == 0) {
893     cfile.autostop_filesize = get_positive_int(p,"autostop filesize");
894   } else {
895     return FALSE;
896   }
897   *colonp = ':'; /* put the colon back */
898   return TRUE;
899 }
900
901 /* And now our feature presentation... [ fade to music ] */
902 int
903 main(int argc, char *argv[])
904 {
905 #ifdef HAVE_LIBPCAP
906   char                *command_name;
907 #endif
908   char                *s;
909   int                  i;
910   int                  opt;
911   extern char         *optarg;
912   gboolean             arg_error = FALSE;
913 #ifdef HAVE_LIBPCAP
914 #ifdef HAVE_PCAP_VERSION
915   extern char          pcap_version[];
916 #endif /* HAVE_PCAP_VERSION */
917 #endif /* HAVE_LIBPCAP */
918   
919 #ifdef WIN32
920   WSADATA              wsaData; 
921 #endif
922
923   char                *gpf_path, *cf_path, *df_path;
924   const char          *pf_path;
925   int                  gpf_open_errno, pf_open_errno, cf_open_errno, df_open_errno;
926   int                  err;
927 #ifdef HAVE_LIBPCAP
928   gboolean             start_capture = FALSE;
929   gchar               *save_file = NULL;
930   GList               *if_list;
931   gchar                err_str[PCAP_ERRBUF_SIZE];
932   gboolean             stats_known;
933   struct pcap_stat     stats;
934 #else
935   gboolean             capture_option_specified = FALSE;
936 #endif
937   gint                 pl_size = 280, tv_size = 95, bv_size = 75;
938   gchar               *rc_file, *cf_name = NULL, *rfilter = NULL;
939   dfilter_t           *rfcode = NULL;
940   gboolean             rfilter_parse_failed = FALSE;
941   e_prefs             *prefs;
942   char                 badopt;
943   char                *bold_font_name;
944
945   ethereal_path = argv[0];
946
947 #ifdef WIN32
948   /* Arrange that if we have no console window, and a GLib message logging
949      routine is called to log a message, we pop up a console window.
950
951      We do that by inserting our own handler for all messages logged
952      to the default domain; that handler pops up a console if necessary,
953      and then calls the default handler. */
954   g_log_set_handler(NULL,
955                     G_LOG_LEVEL_ERROR|
956                     G_LOG_LEVEL_CRITICAL|
957                     G_LOG_LEVEL_WARNING|
958                     G_LOG_LEVEL_MESSAGE|
959                     G_LOG_LEVEL_INFO|
960                     G_LOG_LEVEL_DEBUG|
961                     G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION,
962                     console_log_handler, NULL);
963 #endif
964
965 #ifdef HAVE_LIBPCAP
966   command_name = get_basename(ethereal_path);
967   /* Set "capture_child" to indicate whether this is going to be a child
968      process for a "-S" capture. */
969   capture_child = (strcmp(command_name, CHILD_NAME) == 0);
970 #endif
971
972   /* Register all dissectors; we must do this before checking for the
973      "-G" flag, as the "-G" flag dumps a list of fields registered
974      by the dissectors, and we must do it before we read the preferences,
975      in case any dissectors register preferences. */
976   epan_init(PLUGIN_DIR,register_all_protocols,register_all_protocol_handoffs);
977
978   /* Now register the preferences for any non-dissector modules.
979      We must do that before we read the preferences as well. */
980   prefs_register_modules();
981
982   /* If invoked with the "-G" flag, we dump out a glossary of
983      display filter symbols.
984
985      We must do this before calling "gtk_init()", because "gtk_init()"
986      tries to open an X display, and we don't want to have to do any X
987      stuff just to do a build.
988
989      Given that we call "gtk_init()" before doing the regular argument
990      list processing, so that it can handle X and GTK+ arguments and
991      remove them from the list at which we look, this means we must do
992      this before doing the regular argument list processing, as well.
993
994      This means that:
995
996         you must give the "-G" flag as the first flag on the command line;
997
998         you must give it as "-G", nothing more, nothing less;
999
1000         any arguments after the "-G" flag will not be used. */
1001   if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
1002     proto_registrar_dump();
1003     exit(0);
1004   }
1005
1006   /* Set the current locale according to the program environment. 
1007    * We haven't localized anything, but some GTK widgets are localized
1008    * (the file selection dialogue, for example).
1009    * This also sets the C-language locale to the native environment. */
1010   gtk_set_locale();
1011
1012   /* Let GTK get its args */
1013   gtk_init (&argc, &argv);
1014   
1015   /* Read the preference files. */
1016   prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
1017
1018 #ifdef HAVE_LIBPCAP
1019   /* If this is a capture child process, it should pay no attention
1020      to the "prefs.capture_prom_mode" setting in the preferences file;
1021      it should do what the parent process tells it to do, and if
1022      the parent process wants it not to run in promiscuous mode, it'll
1023      tell it so with a "-p" flag. */
1024   if (capture_child)
1025     prefs->capture_prom_mode = TRUE;
1026 #endif
1027
1028   /* Read the capture filter file. */
1029   read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
1030
1031   /* Read the display filter file. */
1032   read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
1033
1034   /* Initialize the capture file struct */
1035   cfile.plist           = NULL;
1036   cfile.plist_end       = NULL;
1037   cfile.wth             = NULL;
1038   cfile.filename        = NULL;
1039   cfile.user_saved      = FALSE;
1040   cfile.is_tempfile     = FALSE;
1041   cfile.rfcode          = NULL;
1042   cfile.dfilter         = NULL;
1043   cfile.dfcode          = NULL;
1044 #ifdef HAVE_LIBPCAP
1045   cfile.cfilter         = g_strdup(EMPTY_FILTER);
1046 #endif
1047   cfile.iface           = NULL;
1048   cfile.save_file       = NULL;
1049   cfile.save_file_fd    = -1;
1050   cfile.snap            = WTAP_MAX_PACKET_SIZE;
1051   cfile.count           = 0;
1052 #ifdef HAVE_LIBPCAP
1053   cfile.autostop_duration = 0;
1054   cfile.autostop_filesize = 0;
1055   cfile.ringbuffer_on = FALSE;
1056   cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
1057 #endif
1058   col_init(&cfile.cinfo, prefs->num_cols);
1059
1060   /* Assemble the compile-time options */
1061   comp_info_str = g_string_new("");
1062
1063   g_string_append(comp_info_str, "with ");
1064   g_string_sprintfa(comp_info_str,
1065 #ifdef GTK_MAJOR_VERSION
1066     "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
1067     GTK_MICRO_VERSION);
1068 #else
1069     "GTK+ (version unknown)");
1070 #endif
1071
1072   g_string_append(comp_info_str, ", with ");
1073   g_string_sprintfa(comp_info_str,
1074 #ifdef GLIB_MAJOR_VERSION
1075     "GLib %d.%d.%d", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION,
1076     GLIB_MICRO_VERSION);
1077 #else
1078     "GLib (version unknown)");
1079 #endif
1080
1081 #ifdef HAVE_LIBPCAP
1082   g_string_append(comp_info_str, ", with libpcap ");
1083 #ifdef HAVE_PCAP_VERSION
1084   g_string_append(comp_info_str, pcap_version);
1085 #else /* HAVE_PCAP_VERSION */
1086   g_string_append(comp_info_str, "(version unknown)");
1087 #endif /* HAVE_PCAP_VERSION */
1088 #else /* HAVE_LIBPCAP */
1089   g_string_append(comp_info_str, ", without libpcap");
1090 #endif /* HAVE_LIBPCAP */
1091
1092 #ifdef HAVE_LIBZ
1093   g_string_append(comp_info_str, ", with libz ");
1094 #ifdef ZLIB_VERSION
1095   g_string_append(comp_info_str, ZLIB_VERSION);
1096 #else /* ZLIB_VERSION */
1097   g_string_append(comp_info_str, "(version unknown)");
1098 #endif /* ZLIB_VERSION */
1099 #else /* HAVE_LIBZ */
1100   g_string_append(comp_info_str, ", without libz");
1101 #endif /* HAVE_LIBZ */
1102
1103 /* Oh, this is pretty */
1104 #if defined(HAVE_UCD_SNMP_SNMP_H)
1105   g_string_append(comp_info_str, ", with UCD SNMP ");
1106 #ifdef HAVE_UCD_SNMP_VERSION_H
1107   g_string_append(comp_info_str, VersionInfo);
1108 #else /* HAVE_UCD_SNMP_VERSION_H */
1109   g_string_append(comp_info_str, "(version unknown)");
1110 #endif /* HAVE_UCD_SNMP_VERSION_H */
1111 #elif defined(HAVE_SNMP_SNMP_H)
1112   g_string_append(comp_info_str, ", with CMU SNMP ");
1113 #ifdef HAVE_SNMP_VERSION_H
1114   g_string_append(comp_info_str, snmp_Version());
1115 #else /* HAVE_SNMP_VERSION_H */
1116   g_string_append(comp_info_str, "(version unknown)");
1117 #endif /* HAVE_SNMP_VERSION_H */
1118 #else /* no SNMP */
1119   g_string_append(comp_info_str, ", without SNMP");
1120 #endif
1121
1122   /* Now get our args */
1123   while ((opt = getopt(argc, argv, "a:b:B:c:f:hi:klm:nN:o:pP:Qr:R:Ss:t:T:w:W:vZ:")) !=  EOF) {
1124     switch (opt) {
1125       case 'a':        /* autostop criteria */
1126 #ifdef HAVE_LIBPCAP
1127         if (set_autostop_criterion(optarg) == FALSE) {
1128           fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
1129           exit(1);          
1130         }
1131 #else
1132         capture_option_specified = TRUE;
1133         arg_error = TRUE;
1134 #endif
1135         break;
1136       case 'b':        /* Ringbuffer option */
1137 #ifdef HAVE_LIBPCAP
1138         cfile.ringbuffer_on = TRUE;
1139         /* get optional ringbuffer number of files parameter */
1140         if (optarg[0] != '-') {
1141           cfile.ringbuffer_num_files = get_positive_int(optarg, "ringbuffer number of files");
1142         } else {
1143           cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
1144           optind--;
1145         }
1146 #else
1147         capture_option_specified = TRUE;
1148         arg_error = TRUE;
1149 #endif
1150         break;
1151       case 'B':        /* Byte view pane height */
1152         bv_size = get_positive_int(optarg, "byte view pane height");
1153         break;
1154       case 'c':        /* Capture xxx packets */
1155 #ifdef HAVE_LIBPCAP
1156         cfile.count = get_positive_int(optarg, "packet count");
1157 #else
1158         capture_option_specified = TRUE;
1159         arg_error = TRUE;
1160 #endif
1161         break;
1162       case 'f':
1163 #ifdef HAVE_LIBPCAP
1164         if (cfile.cfilter)
1165                 g_free(cfile.cfilter);
1166         cfile.cfilter = g_strdup(optarg);
1167 #else
1168         capture_option_specified = TRUE;
1169         arg_error = TRUE;
1170 #endif
1171         break;
1172       case 'h':        /* Print help and exit */
1173         print_usage();
1174         exit(0);
1175         break;
1176       case 'i':        /* Use interface xxx */
1177 #ifdef HAVE_LIBPCAP
1178         cfile.iface = g_strdup(optarg);
1179 #else
1180         capture_option_specified = TRUE;
1181         arg_error = TRUE;
1182 #endif
1183         break;
1184       case 'k':        /* Start capture immediately */
1185 #ifdef HAVE_LIBPCAP
1186         start_capture = TRUE;
1187 #else
1188         capture_option_specified = TRUE;
1189         arg_error = TRUE;
1190 #endif
1191         break;
1192       case 'l':        /* Automatic scrolling in live capture mode */
1193         prefs->capture_auto_scroll = TRUE;
1194         break;
1195       case 'm':        /* Fixed-width font for the display */
1196         if (prefs->gui_font_name != NULL)
1197           g_free(prefs->gui_font_name);
1198         prefs->gui_font_name = g_strdup(optarg);
1199         break;
1200       case 'n':        /* No name resolution */
1201         prefs->name_resolve = PREFS_RESOLV_NONE;
1202         break;
1203       case 'N':        /* Select what types of addresses/port #s to resolve */
1204         if (prefs->name_resolve == PREFS_RESOLV_ALL)
1205           prefs->name_resolve = PREFS_RESOLV_NONE;
1206         badopt = string_to_name_resolve(optarg, &prefs->name_resolve);
1207         if (badopt != '\0') {
1208           fprintf(stderr, "ethereal: -N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'\n",
1209                         badopt);
1210           exit(1);
1211         }
1212         break;
1213       case 'o':        /* Override preference from command line */
1214         switch (prefs_set_pref(optarg)) {
1215
1216         case PREFS_SET_SYNTAX_ERR:
1217           fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
1218           exit(1);
1219           break;
1220
1221         case PREFS_SET_NO_SUCH_PREF:
1222         case PREFS_SET_OBSOLETE:
1223           fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
1224                         optarg);
1225           exit(1);
1226           break;
1227         }
1228         break;
1229       case 'p':        /* Don't capture in promiscuous mode */
1230 #ifdef HAVE_LIBPCAP
1231         prefs->capture_prom_mode = FALSE;
1232 #else
1233         capture_option_specified = TRUE;
1234         arg_error = TRUE;
1235 #endif
1236         break;
1237       case 'P':        /* Packet list pane height */
1238         pl_size = get_positive_int(optarg, "packet list pane height");
1239         break;
1240       case 'Q':        /* Quit after capture (just capture to file) */
1241 #ifdef HAVE_LIBPCAP
1242         quit_after_cap = 1;
1243         start_capture = TRUE;  /*** -Q implies -k !! ***/
1244 #else
1245         capture_option_specified = TRUE;
1246         arg_error = TRUE;
1247 #endif
1248         break;
1249       case 'r':        /* Read capture file xxx */
1250         /* We may set "last_open_dir" to "cf_name", and if we change
1251            "last_open_dir" later, we free the old value, so we have to
1252            set "cf_name" to something that's been allocated. */
1253         cf_name = g_strdup(optarg);
1254         break;
1255       case 'R':        /* Read file filter */
1256         rfilter = optarg;
1257         break;
1258       case 's':        /* Set the snapshot (capture) length */
1259 #ifdef HAVE_LIBPCAP
1260         cfile.snap = get_positive_int(optarg, "snapshot length");
1261 #else
1262         capture_option_specified = TRUE;
1263         arg_error = TRUE;
1264 #endif
1265         break;
1266       case 'S':        /* "Sync" mode: used for following file ala tail -f */
1267 #ifdef HAVE_LIBPCAP
1268         prefs->capture_real_time = TRUE;
1269 #else
1270         capture_option_specified = TRUE;
1271         arg_error = TRUE;
1272 #endif
1273         break;
1274       case 't':        /* Time stamp type */
1275         if (strcmp(optarg, "r") == 0)
1276           timestamp_type = RELATIVE;
1277         else if (strcmp(optarg, "a") == 0)
1278           timestamp_type = ABSOLUTE;
1279         else if (strcmp(optarg, "ad") == 0)
1280           timestamp_type = ABSOLUTE_WITH_DATE;
1281         else if (strcmp(optarg, "d") == 0)
1282           timestamp_type = DELTA;
1283         else {
1284           fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
1285             optarg);
1286           fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
1287           fprintf(stderr, "\"ad\" for absolute with date, or \"d\" for delta.\n");
1288           exit(1);
1289         }
1290         break;
1291       case 'T':        /* Tree view pane height */
1292         tv_size = get_positive_int(optarg, "tree view pane height");
1293         break;
1294       case 'v':        /* Show version and exit */
1295         show_version();
1296 #ifdef WIN32
1297         if (console_was_created)
1298           destroy_console();
1299 #endif
1300         exit(0);
1301         break;
1302       case 'w':        /* Write to capture file xxx */
1303 #ifdef HAVE_LIBPCAP
1304         save_file = g_strdup(optarg);
1305 #else
1306         capture_option_specified = TRUE;
1307         arg_error = TRUE;
1308 #endif
1309         break;
1310       case 'W':        /* Write to capture file FD xxx */
1311 #ifdef HAVE_LIBPCAP
1312         cfile.save_file_fd = atoi(optarg);
1313 #else
1314         capture_option_specified = TRUE;
1315         arg_error = TRUE;
1316 #endif
1317         break;
1318
1319 #ifdef _WIN32
1320       case 'Z':        /* Write to pipe FD XXX */
1321 #ifdef HAVE_LIBPCAP
1322         /* associate stdout with pipe */
1323         i = atoi(optarg);
1324         if (dup2(i, 1) < 0) {
1325           fprintf(stderr, "Unable to dup pipe handle\n");
1326           exit(1);
1327         }
1328 #else
1329         capture_option_specified = TRUE;
1330         arg_error = TRUE;
1331 #endif /* HAVE_LIBPCAP */
1332         break;
1333 #endif /* _WIN32 */
1334
1335       default:
1336       case '?':        /* Bad flag - print usage message */
1337         arg_error = TRUE;
1338         break;
1339     }
1340   }
1341   argc -= optind;
1342   argv += optind;
1343   if (argc >= 1) {
1344     if (cf_name != NULL) {
1345       /*
1346        * Input file name specified with "-r" *and* specified as a regular
1347        * command-line argument.
1348        */
1349       arg_error = TRUE;
1350     } else {
1351       /*
1352        * Input file name not specified with "-r", and a command-line argument
1353        * was specified; treat it as the input file name.
1354        *
1355        * Yes, this is different from tethereal, where non-flag command-line
1356        * arguments are a filter, but this works better on GUI desktops
1357        * where a command can be specified to be run to open a particular
1358        * file - yes, you could have "-r" as the last part of the command,
1359        * but that's a bit ugly.
1360        */
1361       cf_name = g_strdup(argv[0]);
1362     }
1363     argc--;
1364     argv++;
1365   }
1366
1367   if (argc != 0) {
1368     /*
1369      * Extra command line arguments were specified; complain.
1370      */
1371     arg_error = TRUE;
1372   }
1373
1374 #ifdef WIN32
1375   /* Load wpcap if possible */
1376   load_wpcap();
1377
1378   /* Start windows sockets */
1379   WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
1380 #endif
1381
1382   /* Notify all registered modules that have had any of their preferences
1383      changed either from one of the preferences file or from the command
1384      line that its preferences have changed. */
1385   prefs_apply_all();
1386
1387 #ifndef HAVE_LIBPCAP
1388   if (capture_option_specified)
1389     fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
1390 #endif
1391   if (arg_error)
1392     print_usage();
1393 #ifdef HAVE_LIBPCAP
1394   if (start_capture) {
1395     /* We're supposed to do a live capture; did the user specify an interface
1396        to use? */
1397     if (cfile.iface == NULL) {
1398       /* No - pick the first one from the list of interfaces. */
1399       if_list = get_interface_list(&err, err_str);
1400       if (if_list == NULL) {
1401         switch (err) {
1402
1403         case CANT_GET_INTERFACE_LIST:
1404             fprintf(stderr, "ethereal: Can't get list of interfaces: %s\n",
1405                         err_str);
1406             break;
1407
1408         case NO_INTERFACES_FOUND:
1409             fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
1410             break;
1411         }
1412         exit(2);
1413       }
1414       cfile.iface = g_strdup(if_list->data);    /* first interface */
1415       free_interface_list(if_list);
1416     }
1417   }
1418   if (capture_child) {
1419     if (cfile.save_file_fd == -1) {
1420       /* XXX - send this to the standard output as something our parent
1421          should put in an error message box? */
1422       fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
1423       exit(1);
1424     }
1425   }
1426 #endif
1427
1428   /* Build the column format array */  
1429   for (i = 0; i < cfile.cinfo.num_cols; i++) {
1430     cfile.cinfo.col_fmt[i] = get_column_format(i);
1431     cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
1432     cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
1433       NUM_COL_FMTS);
1434     get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
1435     cfile.cinfo.col_data[i] = NULL;
1436     if (cfile.cinfo.col_fmt[i] == COL_INFO)
1437       cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
1438     else
1439       cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
1440   }
1441
1442   if (cfile.snap < 1)
1443     cfile.snap = WTAP_MAX_PACKET_SIZE;
1444   else if (cfile.snap < MIN_PACKET_SIZE)
1445     cfile.snap = MIN_PACKET_SIZE;
1446   
1447   if (cfile.ringbuffer_on == TRUE) {
1448     /* Ringbuffer works just under certain conditions: 
1449        a) prefs->capture_real_time and cfile.ringbuffer_on are mutially 
1450           exclusive. prefs->capture_real_time takes precedence. 
1451        b) Ringbuffer does not work with temporary files
1452        c) It makes no sense to enable the ringbuffer if the maximum
1453            file size is set to infinite */
1454     if (prefs->capture_real_time == TRUE ||
1455         cfile.save_file == NULL ||
1456         cfile.autostop_filesize == 0) {
1457       /* turn ringbuffer off */
1458       cfile.ringbuffer_on = FALSE;
1459     }
1460   }
1461
1462   /* Check the value range of the ringbuffer_num_files parameter */
1463   if (cfile.ringbuffer_num_files < RINGBUFFER_MIN_NUM_FILES)
1464     cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
1465   else if (cfile.ringbuffer_num_files > RINGBUFFER_MAX_NUM_FILES)
1466     cfile.ringbuffer_num_files = RINGBUFFER_MAX_NUM_FILES;
1467   
1468   rc_file = get_persconffile_path(RC_FILE, FALSE);
1469   gtk_rc_parse(rc_file);
1470
1471   /* Try to load the regular and boldface fixed-width fonts */
1472   bold_font_name = boldify(prefs->gui_font_name);
1473   m_r_font = gdk_font_load(prefs->gui_font_name);
1474   m_b_font = gdk_font_load(bold_font_name);
1475   if (m_r_font == NULL || m_b_font == NULL) {
1476     /* XXX - pop this up as a dialog box? no */
1477     if (m_r_font == NULL) {
1478 #ifdef HAVE_LIBPCAP
1479       if (!capture_child)
1480 #endif
1481         fprintf(stderr, "ethereal: Warning: font %s not found - defaulting to 6x13 and 6x13bold\n",
1482                 prefs->gui_font_name);
1483     } else {
1484       gdk_font_unref(m_r_font);
1485     }
1486     if (m_b_font == NULL) {
1487 #ifdef HAVE_LIBPCAP
1488       if (!capture_child)
1489 #endif
1490         fprintf(stderr, "ethereal: Warning: font %s not found - defaulting to 6x13 and 6x13bold\n",
1491                 bold_font_name);
1492     } else {
1493       gdk_font_unref(m_b_font);
1494     }
1495     g_free(bold_font_name);
1496     if ((m_r_font = gdk_font_load("6x13")) == NULL) {
1497       fprintf(stderr, "ethereal: Error: font 6x13 not found\n");
1498       exit(1);
1499     }
1500     if ((m_b_font = gdk_font_load("6x13bold")) == NULL) {
1501       fprintf(stderr, "ethereal: Error: font 6x13bold not found\n");
1502       exit(1);
1503     }
1504     g_free(prefs->gui_font_name);
1505     prefs->gui_font_name = g_strdup("6x13");
1506   }
1507
1508   /* Call this for the side-effects that set_fonts() produces */
1509   set_fonts(m_r_font, m_b_font);
1510
1511
1512 #ifdef HAVE_LIBPCAP
1513   /* Is this a "child" ethereal, which is only supposed to pop up a
1514      capture box to let us stop the capture, and run a capture
1515      to a file that our parent will read? */
1516   if (!capture_child) {
1517 #endif
1518     /* No.  Pop up the main window, and read in a capture file if
1519        we were told to. */
1520
1521     create_main_window(pl_size, tv_size, bv_size, prefs);
1522     set_menus_for_capture_file(FALSE);
1523
1524     cfile.colors = colfilter_new();
1525
1526     /* If we were given the name of a capture file, read it in now;
1527        we defer it until now, so that, if we can't open it, and pop
1528        up an alert box, the alert box is more likely to come up on
1529        top of the main window - but before the preference-file-error
1530        alert box, so, if we get one of those, it's more likely to come
1531        up on top of us. */
1532     if (cf_name) {
1533       if (rfilter != NULL) {
1534         if (!dfilter_compile(rfilter, &rfcode)) {
1535           simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
1536           rfilter_parse_failed = TRUE;
1537         }
1538       }
1539       if (!rfilter_parse_failed) {
1540         if ((err = open_cap_file(cf_name, FALSE, &cfile)) == 0) {
1541           /* "open_cap_file()" succeeded, so it closed the previous
1542              capture file, and thus destroyed any previous read filter
1543              attached to "cf". */
1544           cfile.rfcode = rfcode;
1545           switch (read_cap_file(&cfile, &err)) {
1546
1547           case READ_SUCCESS:
1548           case READ_ERROR:
1549             /* Just because we got an error, that doesn't mean we were unable
1550                to read any of the file; we handle what we could get from the
1551                file. */
1552             break;
1553
1554           case READ_ABORTED:
1555             /* Exit now. */
1556             gtk_exit(0);
1557             break;
1558           }
1559           /* Save the name of the containing directory specified in the
1560              path name, if any; we can write over cf_name, which is a
1561              good thing, given that "get_dirname()" does write over its
1562              argument. */
1563           s = get_dirname(cf_name);
1564           set_last_open_dir(s);
1565         } else {
1566           if (rfcode != NULL)
1567             dfilter_free(rfcode);
1568           cfile.rfcode = NULL;
1569         }
1570       }
1571     }
1572 #ifdef HAVE_LIBPCAP
1573   }
1574 #endif
1575
1576   /* If the global preferences file exists but we failed to open it,
1577      pop up an alert box; we defer that until now, so that the alert
1578      box is more likely to come up on top of the main window. */
1579   if (gpf_path != NULL) {
1580       simple_dialog(ESD_TYPE_WARN, NULL,
1581         "Could not open global preferences file\n\"%s\": %s.", gpf_path,
1582         strerror(gpf_open_errno));
1583   }
1584
1585   /* If the user's preferences file exists but we failed to open it,
1586      pop up an alert box; we defer that until now, so that the alert
1587      box is more likely to come up on top of the main window. */
1588   if (pf_path != NULL) {
1589       simple_dialog(ESD_TYPE_WARN, NULL,
1590         "Could not open your preferences file\n\"%s\": %s.", pf_path,
1591         strerror(pf_open_errno));
1592   }
1593
1594   /* If the user's capture filter file exists but we failed to open it,
1595      pop up an alert box; we defer that until now, so that the alert
1596      box is more likely to come up on top of the main window. */
1597   if (cf_path != NULL) {
1598       simple_dialog(ESD_TYPE_WARN, NULL,
1599         "Could not open your capture filter file\n\"%s\": %s.", cf_path,
1600         strerror(cf_open_errno));
1601       g_free(cf_path);
1602   }
1603
1604   /* If the user's display filter file exists but we failed to open it,
1605      pop up an alert box; we defer that until now, so that the alert
1606      box is more likely to come up on top of the main window. */
1607   if (df_path != NULL) {
1608       simple_dialog(ESD_TYPE_WARN, NULL,
1609         "Could not open your display filter file\n\"%s\": %s.", df_path,
1610         strerror(df_open_errno));
1611       g_free(df_path);
1612   }
1613
1614 #ifdef HAVE_LIBPCAP
1615   if (capture_child) {
1616     /* This is the child process for a sync mode or fork mode capture,
1617        so just do the low-level work of a capture - don't create
1618        a temporary file and fork off *another* child process (so don't
1619        call "do_capture()"). */
1620
1621        /* XXX - hand these stats to the parent process */
1622        capture(&stats_known, &stats);
1623
1624        /* The capture is done; there's nothing more for us to do. */
1625        gtk_exit(0);
1626   } else {
1627     if (start_capture) {
1628       /* "-k" was specified; start a capture. */
1629       do_capture(save_file);
1630     }
1631     else {
1632       set_menus_for_capture_in_progress(FALSE);
1633     }
1634   }
1635 #else
1636   set_menus_for_capture_in_progress(FALSE);
1637 #endif
1638
1639   gtk_main();
1640
1641   epan_cleanup();
1642   g_free(rc_file);
1643
1644 #ifdef WIN32
1645   /* Shutdown windows sockets */
1646   WSACleanup();
1647
1648   /* For some unknown reason, the "atexit()" call in "create_console()"
1649      doesn't arrange that "destroy_console()" be called when we exit,
1650      so we call it here if a console was created. */
1651   if (console_was_created)
1652     destroy_console();
1653 #endif
1654
1655   gtk_exit(0);
1656
1657   /* This isn't reached, but we need it to keep GCC from complaining
1658      that "main()" returns without returning a value - it knows that
1659      "exit()" never returns, but it doesn't know that "gtk_exit()"
1660      doesn't, as GTK+ doesn't declare it with the attribute
1661      "noreturn". */
1662   return 0;     /* not reached */
1663 }
1664
1665 #ifdef WIN32
1666
1667 /* We build this as a GUI subsystem application on Win32, so
1668    "WinMain()", not "main()", gets called.
1669
1670    Hack shamelessly stolen from the Win32 port of the GIMP. */
1671 #ifdef __GNUC__
1672 #define _stdcall  __attribute__((stdcall))
1673 #endif
1674
1675 int _stdcall
1676 WinMain (struct HINSTANCE__ *hInstance,
1677          struct HINSTANCE__ *hPrevInstance,
1678          char               *lpszCmdLine,
1679          int                 nCmdShow)
1680 {
1681   has_no_console = TRUE;
1682   return main (__argc, __argv);
1683 }
1684
1685 /*
1686  * If this application has no console window to which its standard output
1687  * would go, create one.
1688  */
1689 static void
1690 create_console(void)
1691 {
1692   if (has_no_console) {
1693     /* We have no console to which to print the version string, so
1694        create one and make it the standard input, output, and error. */
1695     if (!AllocConsole())
1696       return;   /* couldn't create console */
1697     freopen("CONIN$", "r", stdin);
1698     freopen("CONOUT$", "w", stdout);
1699     freopen("CONOUT$", "w", stderr);
1700
1701     /* Well, we have a console now. */
1702     has_no_console = FALSE;
1703     console_was_created = TRUE;
1704
1705     /* Now register "destroy_console()" as a routine to be called just
1706        before the application exits, so that we can destroy the console
1707        after the user has typed a key (so that the console doesn't just
1708        disappear out from under them, giving the user no chance to see
1709        the message(s) we put in there). */
1710     atexit(destroy_console);
1711   }
1712 }
1713
1714 static void
1715 destroy_console(void)
1716 {
1717   printf("\n\nPress any key to exit\n");
1718   _getch();
1719   FreeConsole();
1720 }
1721
1722 /* This routine should not be necessary, at least as I read the GLib
1723    source code, as it looks as if GLib is, on Win32, *supposed* to
1724    create a console window into which to display its output.
1725
1726    That doesn't happen, however.  I suspect there's something completely
1727    broken about that code in GLib-for-Win32, and that it may be related
1728    to the breakage that forces us to just call "printf()" on the message
1729    rather than passing the message on to "g_log_default_handler()"
1730    (which is the routine that does the aforementioned non-functional
1731    console window creation). */
1732 static void
1733 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
1734                     const char *message, gpointer user_data)
1735 {
1736   create_console();
1737   if (console_was_created) {
1738     /* For some unknown reason, the above doesn't appear to actually cause
1739        anything to be sent to the standard output, so we'll just splat the
1740        message out directly, just to make sure it gets out. */
1741     printf("%s\n", message);
1742   } else
1743     g_log_default_handler(log_domain, log_level, message, user_data);
1744 }
1745 #endif
1746
1747 /* Given a font name, construct the name of the next heavier version of
1748    that font. */
1749
1750 #define XLFD_WEIGHT     3       /* index of the "weight" field */
1751
1752 /* Map from a given weight to the appropriate weight for the "bold"
1753    version of a font.
1754    XXX - the XLFD says these strings shouldn't be used for font matching;
1755    can we get the weight, as a number, from GDK, and ask GDK to find us
1756    a font just like the given font, but with the appropriate higher
1757    weight? */
1758 static const struct {
1759         char    *light;
1760         char    *heavier;
1761 } weight_map[] = {
1762         { "ultralight", "light" },
1763         { "extralight", "semilight" },
1764         { "light",      "medium" },
1765         { "semilight",  "semibold" },
1766         { "medium",     "bold" },
1767         { "normal",     "bold" },
1768         { "semibold",   "extrabold" },
1769         { "bold",       "ultrabold" }
1770 };
1771 #define N_WEIGHTS       (sizeof weight_map / sizeof weight_map[0])
1772         
1773 char *
1774 boldify(const char *font_name)
1775 {
1776         char *bold_font_name;
1777         gchar **xlfd_tokens;
1778         unsigned int i;
1779
1780         /* Is this an XLFD font?  If it begins with "-", yes, otherwise no. */
1781         if (font_name[0] == '-') {
1782                 xlfd_tokens = g_strsplit(font_name, "-", XLFD_WEIGHT+1);
1783                 for (i = 0; i < N_WEIGHTS; i++) {
1784                         if (strcmp(xlfd_tokens[XLFD_WEIGHT],
1785                             weight_map[i].light) == 0) {
1786                                 g_free(xlfd_tokens[XLFD_WEIGHT]);
1787                                 xlfd_tokens[XLFD_WEIGHT] =
1788                                     g_strdup(weight_map[i].heavier);
1789                                 break;
1790                         }
1791                 }
1792                 bold_font_name = g_strjoinv("-", xlfd_tokens);
1793                 g_strfreev(xlfd_tokens);
1794         } else {
1795                 /* Append "bold" to the name of the font. */
1796                 bold_font_name = g_strconcat(font_name, "bold", NULL);
1797         }
1798         return bold_font_name;
1799 }
1800
1801
1802 static void
1803 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
1804 {
1805   GtkWidget           *main_vbox, *menubar, *u_pane, *l_pane,
1806                       *stat_hbox, *column_lb,
1807                       *filter_bt, *filter_cm, *filter_te,
1808                       *filter_reset;
1809   GList               *filter_list = NULL;
1810   GtkAccelGroup       *accel;
1811   GtkStyle            *win_style;
1812   GdkBitmap           *ascend_bm, *descend_bm;
1813   GdkPixmap           *ascend_pm, *descend_pm;
1814   column_arrows       *col_arrows;
1815   int                   i;
1816   /* Display filter construct dialog has an Apply button, and "OK" not
1817      only sets our text widget, it activates it (i.e., it causes us to
1818      filter the capture). */
1819   static construct_args_t args = {
1820         "Ethereal: Display Filter",
1821         TRUE,
1822         TRUE
1823   };
1824
1825   /* Main window */  
1826   top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1827   gtk_widget_set_name(top_level, "main window");
1828   gtk_signal_connect(GTK_OBJECT(top_level), "delete_event", 
1829     GTK_SIGNAL_FUNC(main_window_delete_event_cb), NULL);
1830   gtk_signal_connect (GTK_OBJECT (top_level), "realize",
1831     GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
1832   gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
1833   gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
1834   gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
1835
1836   /* Container for menu bar, paned windows and progress/info box */
1837   main_vbox = gtk_vbox_new(FALSE, 1);
1838   gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
1839   gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
1840   gtk_widget_show(main_vbox);
1841
1842   /* Menu bar */
1843   get_main_menu(&menubar, &accel);
1844   gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
1845   gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
1846   gtk_widget_show(menubar);
1847
1848   /* Panes for the packet list, tree, and byte view */
1849   u_pane = gtk_vpaned_new();
1850   gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
1851   l_pane = gtk_vpaned_new();
1852   gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
1853   gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
1854   gtk_widget_show(l_pane);
1855   gtk_paned_add2(GTK_PANED(u_pane), l_pane);
1856   gtk_widget_show(u_pane);
1857
1858   /* Packet list */
1859   pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
1860   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
1861     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1862   set_scrollbar_placement_scrollw(pkt_scrollw, prefs->gui_scrollbar_on_right);
1863   remember_scrolled_window(pkt_scrollw);
1864   gtk_widget_show(pkt_scrollw);
1865   gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
1866
1867   packet_list = gtk_clist_new(cfile.cinfo.num_cols);
1868   /* Column titles are filled in below */
1869   gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
1870
1871   col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * cfile.cinfo.num_cols);
1872   
1873   set_plist_sel_browse(prefs->gui_plist_sel_browse);
1874   set_plist_font(m_r_font);
1875   gtk_widget_set_name(packet_list, "packet list");
1876   gtk_signal_connect (GTK_OBJECT (packet_list), "click_column",
1877     GTK_SIGNAL_FUNC(packet_list_click_column_cb), col_arrows);
1878   gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
1879     GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
1880   gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
1881     GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
1882   for (i = 0; i < cfile.cinfo.num_cols; i++) {
1883     if (get_column_resize_type(cfile.cinfo.col_fmt[i]) != RESIZE_MANUAL)
1884       gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
1885
1886     /* Right-justify the packet number column. */
1887     if (cfile.cinfo.col_fmt[i] == COL_NUMBER)
1888       gtk_clist_set_column_justification(GTK_CLIST(packet_list), i, 
1889         GTK_JUSTIFY_RIGHT);
1890   }
1891   gtk_widget_set_usize(packet_list, -1, pl_size);
1892   gtk_signal_connect(GTK_OBJECT(packet_list), "button_press_event",
1893                      GTK_SIGNAL_FUNC(popup_menu_handler), 
1894                      gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
1895   gtk_signal_connect(GTK_OBJECT(packet_list), "button_press_event",
1896                      GTK_SIGNAL_FUNC(packet_list_button_pressed_cb), NULL);
1897   gtk_clist_set_compare_func(GTK_CLIST(packet_list), packet_list_compare);
1898   gtk_widget_show(packet_list);
1899
1900   /* Tree view */
1901   item_style = gtk_style_new();
1902   gdk_font_unref(item_style->font);
1903   item_style->font = m_r_font;
1904   create_tree_view(tv_size, prefs, l_pane, &tv_scrollw, &tree_view,
1905                         prefs->gui_scrollbar_on_right);
1906   gtk_signal_connect(GTK_OBJECT(tree_view), "tree-select-row",
1907     GTK_SIGNAL_FUNC(tree_view_select_row_cb), NULL);
1908   gtk_signal_connect(GTK_OBJECT(tree_view), "tree-unselect-row",
1909     GTK_SIGNAL_FUNC(tree_view_unselect_row_cb), NULL);
1910   gtk_signal_connect(GTK_OBJECT(tree_view), "button_press_event",
1911                      GTK_SIGNAL_FUNC(popup_menu_handler),
1912                      gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY));
1913   gtk_widget_show(tree_view);
1914
1915   /* Byte view. */
1916   create_byte_view(bv_size, l_pane, &byte_nb_ptr, &bv_scrollw,
1917                         prefs->gui_scrollbar_on_right);
1918
1919   gtk_signal_connect(GTK_OBJECT(byte_nb_ptr), "button_press_event",
1920                      GTK_SIGNAL_FUNC(popup_menu_handler),
1921                      gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_HEXDUMP_KEY));
1922
1923   /* Filter/info box */
1924   stat_hbox = gtk_hbox_new(FALSE, 1);
1925   gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
1926   gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
1927   gtk_widget_show(stat_hbox);
1928
1929   filter_bt = gtk_button_new_with_label("Filter:");
1930   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
1931     GTK_SIGNAL_FUNC(display_filter_construct_cb), &args);
1932   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
1933   gtk_widget_show(filter_bt);
1934   
1935   filter_cm = gtk_combo_new();
1936   filter_list = g_list_append (filter_list, "");
1937   gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
1938   gtk_combo_disable_activate(GTK_COMBO(filter_cm));
1939   filter_te = GTK_COMBO(filter_cm)->entry;
1940   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
1941   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
1942   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
1943   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
1944   gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
1945     GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
1946   gtk_widget_show(filter_cm);
1947
1948   filter_reset = gtk_button_new_with_label("Reset");
1949   gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
1950   gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
1951                      GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
1952   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
1953   gtk_widget_show(filter_reset);
1954
1955   /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
1956    * of any widget that ends up calling a callback which needs
1957    * that text entry pointer */
1958   set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
1959   set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
1960   set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
1961   set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
1962   set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
1963
1964   info_bar = gtk_statusbar_new();
1965   main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
1966   file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
1967   help_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "help");
1968   gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
1969   gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
1970   gtk_widget_show(info_bar);
1971
1972   gtk_widget_show(top_level);
1973
1974   /* Fill in column titles.  This must be done after the top level window
1975      is displayed. */
1976   win_style = gtk_widget_get_style(top_level);
1977   ascend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &ascend_bm,
1978         &win_style->bg[GTK_STATE_NORMAL], (gchar **)clist_ascend_xpm);
1979   descend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &descend_bm,
1980         &win_style->bg[GTK_STATE_NORMAL], (gchar **)clist_descend_xpm);
1981   for (i = 0; i < cfile.cinfo.num_cols; i++) {
1982     col_arrows[i].table = gtk_table_new(2, 2, FALSE);
1983     gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
1984     column_lb = gtk_label_new(cfile.cinfo.col_title[i]);
1985     gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2,
1986         GTK_SHRINK, GTK_SHRINK, 0, 0);
1987     gtk_widget_show(column_lb);
1988     col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
1989     gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 
1990         1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1991     if (i == 0) {
1992       gtk_widget_show(col_arrows[i].ascend_pm);
1993     }
1994     col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
1995     gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm,
1996         1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
1997     gtk_clist_set_column_widget(GTK_CLIST(packet_list), i, col_arrows[i].table);
1998     gtk_widget_show(col_arrows[i].table);
1999   }
2000   gtk_clist_column_titles_show(GTK_CLIST(packet_list));
2001 }
2002
2003
2004 void
2005 set_last_open_dir(char *dirname)
2006 {
2007         int len;
2008
2009         if (last_open_dir) {
2010                 g_free(last_open_dir);
2011         }
2012
2013         if (dirname) {
2014                 len = strlen(dirname);
2015                 if (dirname[len-1] != G_DIR_SEPARATOR) {
2016                         last_open_dir = g_strconcat(dirname, G_DIR_SEPARATOR_S,
2017                                 NULL);
2018                 }
2019         }
2020         else {
2021                 last_open_dir = NULL;
2022         }
2023 }