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