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