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