Add a "--enable-threads" option, which allows to build the gtk+ v2
[obnox/wireshark/wip.git] / gtk / main.c
1 /* main.c
2  *
3  * $Id: main.c,v 1.296 2003/06/08 09:12:23 oabad 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  * - Playback window
32  * - Multiple window support
33  * - Add cut/copy/paste
34  * - Create header parsing routines
35  * - Make byte view selections more fancy?
36  */
37
38 #ifdef HAVE_CONFIG_H
39 # include "config.h"
40 #endif
41
42 #include <gtk/gtk.h>
43
44 #include <string.h>
45 #include <ctype.h>
46
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50
51 #ifdef HAVE_IO_H
52 #include <io.h> /* open/close on win32 */
53 #endif
54
55 #ifdef HAVE_LIBPCAP
56 #include <pcap.h>
57 #endif
58
59 #ifdef NEED_SNPRINTF_H
60 # include "snprintf.h"
61 #endif
62
63 #ifdef NEED_STRERROR_H
64 #include "strerror.h"
65 #endif
66
67 #ifdef NEED_GETOPT_H
68 #include "getopt.h"
69 #endif
70
71 #ifdef WIN32 /* Needed for console I/O */
72 #include <fcntl.h>
73 #include <conio.h>
74 #endif
75
76 #include <epan/epan.h>
77 #include <epan/filesystem.h>
78 #include <epan/epan_dissect.h>
79
80 #include "main.h"
81 #include <epan/timestamp.h>
82 #include <epan/packet.h>
83 #include "capture.h"
84 #include "summary.h"
85 #include "file.h"
86 #include "filters.h"
87 #include "prefs.h"
88 #include "menu.h"
89 #include "../menu.h"
90 #include "color.h"
91 #include "color_filters.h"
92 #include "color_utils.h"
93 #include "filter_prefs.h"
94 #include "file_dlg.h"
95 #include "column.h"
96 #include "print.h"
97 #include <epan/resolv.h>
98 #ifdef HAVE_LIBPCAP
99 #include "pcap-util.h"
100 #endif
101 #include "statusbar.h"
102 #include "simple_dialog.h"
103 #include "proto_draw.h"
104 #include <epan/dfilter/dfilter.h>
105 #include "keys.h"
106 #include "packet_win.h"
107 #include "gtkglobals.h"
108 #include <epan/plugins.h>
109 #include "colors.h"
110 #include <epan/strutil.h>
111 #include "register.h"
112 #include "ringbuffer.h"
113 #include "ui_util.h"
114 #include "image/clist_ascend.xpm"
115 #include "image/clist_descend.xpm"
116 #include "../tap.h"
117 #include "../util.h"
118 #include "compat_macros.h"
119
120 #ifdef WIN32
121 #include "capture-wpcap.h"
122 #endif
123
124 typedef struct column_arrows {
125   GtkWidget *table;
126   GtkWidget *ascend_pm;
127   GtkWidget *descend_pm;
128 } column_arrows;
129
130 capture_file cfile;
131 GtkWidget   *top_level, *packet_list, *tree_view, *byte_nb_ptr,
132             *tv_scrollw, *pkt_scrollw;
133 static GtkWidget        *info_bar;
134 #if GTK_MAJOR_VERSION < 2
135 GdkFont     *m_r_font, *m_b_font;
136 guint        m_font_height, m_font_width;
137 #else
138 PangoFontDescription *m_r_font, *m_b_font;
139 #endif
140 static guint    main_ctx, file_ctx, help_ctx;
141 static GString *comp_info_str, *runtime_info_str;
142 gchar       *ethereal_path = NULL;
143 gchar       *last_open_dir = NULL;
144 gint   root_x = G_MAXINT, root_y = G_MAXINT, top_width, top_height;
145
146 ts_type timestamp_type = RELATIVE;
147
148 #if GTK_MAJOR_VERSION < 2
149 GtkStyle *item_style;
150 #endif
151
152 /* Specifies the field currently selected in the GUI protocol tree */
153 field_info *finfo_selected = NULL;
154
155 #ifdef WIN32
156 static gboolean has_no_console; /* TRUE if app has no console */
157 static gboolean console_was_created; /* TRUE if console was created */
158 static void create_console(void);
159 static void destroy_console(void);
160 static void console_log_handler(const char *log_domain,
161     GLogLevelFlags log_level, const char *message, gpointer user_data);
162 #endif
163
164 static void create_main_window(gint, gint, gint, e_prefs*);
165
166 #define E_DFILTER_CM_KEY          "display_filter_combo"
167 #define E_DFILTER_FL_KEY          "display_filter_list"
168
169 /* About Ethereal window */
170 void
171 about_ethereal( GtkWidget *w _U_, gpointer data _U_ ) {
172   simple_dialog(ESD_TYPE_INFO, NULL,
173                 "Ethereal - Network Protocol Analyzer\n"
174                 "Version " VERSION " (C) 1998-2003 Gerald Combs <gerald@ethereal.com>\n"
175                 "%s\n%s\n\n"
176
177                 "Check the man page for complete documentation and\n"
178                 "for the list of contributors.\n"
179
180                 "\nSee http://www.ethereal.com/ for more information.",
181                  comp_info_str->str, runtime_info_str->str);
182 }
183
184 #if GTK_MAJOR_VERSION < 2
185 void
186 set_fonts(GdkFont *regular, GdkFont *bold)
187 #else
188 void
189 set_fonts(PangoFontDescription *regular, PangoFontDescription *bold)
190 #endif
191 {
192         /* Yes, assert. The code that loads the font should check
193          * for NULL and provide its own error message. */
194         g_assert(m_r_font && m_b_font);
195         m_r_font = regular;
196         m_b_font = bold;
197
198 #if GTK_MAJOR_VERSION < 2
199         m_font_height = m_r_font->ascent + m_r_font->descent;
200         m_font_width = gdk_string_width(m_r_font, "0");
201 #endif
202 }
203
204 /*
205  * Go to frame specified by currently selected protocol tree item.
206  */
207 void
208 goto_framenum_cb(GtkWidget *w _U_, gpointer data _U_)
209 {
210     if (finfo_selected) {
211         header_field_info       *hfinfo;
212         guint32                 framenum;
213
214         hfinfo = finfo_selected->hfinfo;
215         g_assert(hfinfo);
216         if (hfinfo->type == FT_FRAMENUM) {
217             framenum = fvalue_get_integer(finfo_selected->value);
218             if (framenum != 0)
219                 goto_frame(&cfile, framenum);
220         }
221     }
222 }
223
224 /* Match selected byte pattern */
225 static void
226 match_selected_cb_do(gpointer data, int action, gchar *text)
227 {
228     GtkWidget           *filter_te;
229     char                *cur_filter, *new_filter;
230
231     if (!text)
232         return;
233     g_assert(data);
234     filter_te = OBJECT_GET_DATA(data, E_DFILTER_TE_KEY);
235     g_assert(filter_te);
236
237     cur_filter = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, -1);
238
239     switch (action&MATCH_SELECTED_MASK) {
240
241     case MATCH_SELECTED_REPLACE:
242         new_filter = g_strdup(text);
243         break;
244
245     case MATCH_SELECTED_AND:
246         if ((!cur_filter) || (0 == strlen(cur_filter)))
247             new_filter = g_strdup(text);
248         else
249             new_filter = g_strconcat("(", cur_filter, ") && (", text, ")", NULL);
250         break;
251
252     case MATCH_SELECTED_OR:
253         if ((!cur_filter) || (0 == strlen(cur_filter)))
254             new_filter = g_strdup(text);
255         else
256             new_filter = g_strconcat("(", cur_filter, ") || (", text, ")", NULL);
257         break;
258
259     case MATCH_SELECTED_NOT:
260         new_filter = g_strconcat("!(", text, ")", NULL);
261         break;
262
263     case MATCH_SELECTED_AND_NOT:
264         if ((!cur_filter) || (0 == strlen(cur_filter)))
265             new_filter = g_strconcat("!(", text, ")", NULL);
266         else
267             new_filter = g_strconcat("(", cur_filter, ") && !(", text, ")", NULL);
268         break;
269
270     case MATCH_SELECTED_OR_NOT:
271         if ((!cur_filter) || (0 == strlen(cur_filter)))
272             new_filter = g_strconcat("!(", text, ")", NULL);
273         else
274             new_filter = g_strconcat("(", cur_filter, ") || !(", text, ")", NULL);
275         break;
276
277     default:
278         g_assert_not_reached();
279         new_filter = NULL;
280         break;
281     }
282
283     /* Free up the copy we got of the old filter text. */
284     g_free(cur_filter);
285
286     /* create a new one and set the display filter entry accordingly */
287     gtk_entry_set_text(GTK_ENTRY(filter_te), new_filter);
288
289     /* Run the display filter so it goes in effect. */
290     if (action&MATCH_SELECTED_APPLY_NOW)
291         filter_packets(&cfile, new_filter);
292
293     /* Free up the new filter text. */
294     g_free(new_filter);
295
296     /* Free up the generated text we were handed. */
297     g_free(text);
298 }
299
300 void
301 match_selected_cb_replace_ptree(GtkWidget *w, gpointer data)
302 {
303     if (finfo_selected)
304         match_selected_cb_do((data ? data : w),
305             MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW,
306             proto_construct_dfilter_string(finfo_selected, cfile.edt));
307 }
308
309 void
310 match_selected_cb_and_ptree(GtkWidget *w, gpointer data)
311 {
312     if (finfo_selected)
313         match_selected_cb_do((data ? data : w),
314             MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW,
315             proto_construct_dfilter_string(finfo_selected, cfile.edt));
316 }
317
318 void
319 match_selected_cb_or_ptree(GtkWidget *w, gpointer data)
320 {
321     if (finfo_selected)
322         match_selected_cb_do((data ? data : w),
323             MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW,
324             proto_construct_dfilter_string(finfo_selected, cfile.edt));
325 }
326
327 void
328 match_selected_cb_not_ptree(GtkWidget *w, gpointer data)
329 {
330     if (finfo_selected)
331         match_selected_cb_do((data ? data : w),
332             MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW,
333             proto_construct_dfilter_string(finfo_selected, cfile.edt));
334 }
335
336 void
337 match_selected_cb_and_ptree_not(GtkWidget *w, gpointer data)
338 {
339     if (finfo_selected)
340         match_selected_cb_do((data ? data : w),
341             MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW,
342             proto_construct_dfilter_string(finfo_selected, cfile.edt));
343 }
344
345 void
346 match_selected_cb_or_ptree_not(GtkWidget *w, gpointer data)
347 {
348     if (finfo_selected)
349         match_selected_cb_do((data ? data : w),
350             MATCH_SELECTED_OR_NOT,
351             proto_construct_dfilter_string(finfo_selected, cfile.edt));
352 }
353
354 void
355 prepare_selected_cb_replace_ptree(GtkWidget *w, gpointer data)
356 {
357     if (finfo_selected)
358         match_selected_cb_do((data ? data : w),
359             MATCH_SELECTED_REPLACE,
360             proto_construct_dfilter_string(finfo_selected, cfile.edt));
361 }
362
363 void
364 prepare_selected_cb_and_ptree(GtkWidget *w, gpointer data)
365 {
366     if (finfo_selected)
367         match_selected_cb_do((data ? data : w),
368             MATCH_SELECTED_AND,
369             proto_construct_dfilter_string(finfo_selected, cfile.edt));
370 }
371
372 void
373 prepare_selected_cb_or_ptree(GtkWidget *w, gpointer data)
374 {
375     if (finfo_selected)
376         match_selected_cb_do((data ? data : w),
377             MATCH_SELECTED_OR,
378             proto_construct_dfilter_string(finfo_selected, cfile.edt));
379 }
380
381 void
382 prepare_selected_cb_not_ptree(GtkWidget *w, gpointer data)
383 {
384     if (finfo_selected)
385         match_selected_cb_do((data ? data : w),
386             MATCH_SELECTED_NOT,
387             proto_construct_dfilter_string(finfo_selected, cfile.edt));
388 }
389
390 void
391 prepare_selected_cb_and_ptree_not(GtkWidget *w, gpointer data)
392 {
393     if (finfo_selected)
394         match_selected_cb_do((data ? data : w),
395             MATCH_SELECTED_AND_NOT,
396             proto_construct_dfilter_string(finfo_selected, cfile.edt));
397 }
398
399 void
400 prepare_selected_cb_or_ptree_not(GtkWidget *w, gpointer data)
401 {
402     if (finfo_selected)
403         match_selected_cb_do((data ? data : w),
404             MATCH_SELECTED_OR_NOT,
405             proto_construct_dfilter_string(finfo_selected, cfile.edt));
406 }
407
408 static gchar *
409 get_text_from_packet_list(gpointer data)
410 {
411     gint        row = (gint)OBJECT_GET_DATA(data, E_MPACKET_LIST_ROW_KEY);
412     gint        column = (gint)OBJECT_GET_DATA(data, E_MPACKET_LIST_COL_KEY);
413     frame_data *fdata = (frame_data *)gtk_clist_get_row_data(GTK_CLIST(packet_list), row);
414     epan_dissect_t *edt;
415     gchar      *buf=NULL;
416     int         len;
417     int         err;
418
419     if (fdata != NULL) {
420         /* XXX - do something with "err" */
421         wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header,
422                        cfile.pd, fdata->cap_len, &err);
423
424         edt = epan_dissect_new(FALSE, FALSE);
425         epan_dissect_run(edt, &cfile.pseudo_header, cfile.pd, fdata,
426                          &cfile.cinfo);
427         epan_dissect_fill_in_columns(edt);
428
429         if (strlen(cfile.cinfo.col_expr[column]) != 0 &&
430             strlen(cfile.cinfo.col_expr_val[column]) != 0) {
431             len = strlen(cfile.cinfo.col_expr[column]) +
432                   strlen(cfile.cinfo.col_expr_val[column]) + 5;
433             buf = g_malloc0(len);
434             snprintf(buf, len, "%s == %s", cfile.cinfo.col_expr[column],
435                      cfile.cinfo.col_expr_val[column]);
436         }
437
438         epan_dissect_free(edt);
439     }
440
441     return buf;
442 }
443
444 void
445 match_selected_cb_replace_plist(GtkWidget *w _U_, gpointer data)
446 {
447     match_selected_cb_do(data,
448         MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW,
449         get_text_from_packet_list(data));
450 }
451
452 void
453 match_selected_cb_and_plist(GtkWidget *w _U_, gpointer data)
454 {
455     match_selected_cb_do(data,
456         MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW,
457         get_text_from_packet_list(data));
458 }
459
460 void
461 match_selected_cb_or_plist(GtkWidget *w _U_, gpointer data)
462 {
463     match_selected_cb_do(data,
464         MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW,
465         get_text_from_packet_list(data));
466 }
467
468 void
469 match_selected_cb_not_plist(GtkWidget *w _U_, gpointer data)
470 {
471     match_selected_cb_do(data,
472         MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW,
473         get_text_from_packet_list(data));
474 }
475
476 void
477 match_selected_cb_and_plist_not(GtkWidget *w _U_, gpointer data)
478 {
479     match_selected_cb_do(data,
480         MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW,
481         get_text_from_packet_list(data));
482 }
483
484 void
485 match_selected_cb_or_plist_not(GtkWidget *w _U_, gpointer data)
486 {
487     match_selected_cb_do(data,
488         MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW,
489         get_text_from_packet_list(data));
490 }
491
492 void
493 prepare_selected_cb_replace_plist(GtkWidget *w _U_, gpointer data)
494 {
495     match_selected_cb_do(data,
496         MATCH_SELECTED_REPLACE,
497         get_text_from_packet_list(data));
498 }
499
500 void
501 prepare_selected_cb_and_plist(GtkWidget *w _U_, gpointer data)
502 {
503     match_selected_cb_do(data,
504         MATCH_SELECTED_AND,
505         get_text_from_packet_list(data));
506 }
507
508 void
509 prepare_selected_cb_or_plist(GtkWidget *w _U_, gpointer data)
510 {
511     match_selected_cb_do(data,
512         MATCH_SELECTED_OR,
513         get_text_from_packet_list(data));
514 }
515
516 void
517 prepare_selected_cb_not_plist(GtkWidget *w _U_, gpointer data)
518 {
519     match_selected_cb_do(data,
520         MATCH_SELECTED_NOT,
521         get_text_from_packet_list(data));
522 }
523
524 void
525 prepare_selected_cb_and_plist_not(GtkWidget *w _U_, gpointer data)
526 {
527     match_selected_cb_do(data,
528         MATCH_SELECTED_AND_NOT,
529         get_text_from_packet_list(data));
530 }
531
532 void
533 prepare_selected_cb_or_plist_not(GtkWidget *w _U_, gpointer data)
534 {
535     match_selected_cb_do(data,
536         MATCH_SELECTED_OR_NOT,
537         get_text_from_packet_list(data));
538 }
539
540 /* Run the current display filter on the current packet set, and
541    redisplay. */
542 static void
543 filter_activate_cb(GtkWidget *w, gpointer data)
544 {
545   GtkCombo  *filter_cm = OBJECT_GET_DATA(w, E_DFILTER_CM_KEY);
546   GList     *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY);
547   GList     *li;
548   gboolean   add_filter = TRUE;
549   gboolean   free_filter = TRUE;
550   char      *s;
551
552   g_assert(data);
553   s = g_strdup(gtk_entry_get_text(GTK_ENTRY(data)));
554
555   /* GtkCombos don't let us get at their list contents easily, so we maintain
556      our own filter list, and feed it to gtk_combo_set_popdown_strings when
557      a new filter is added. */
558   if (filter_packets(&cfile, s)) {
559     li = g_list_first(filter_list);
560     while (li) {
561       if (li->data && strcmp(s, li->data) == 0)
562         add_filter = FALSE;
563       li = li->next;
564     }
565
566     if (add_filter) {
567       free_filter = FALSE;
568       filter_list = g_list_append(filter_list, s);
569       OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
570       gtk_combo_set_popdown_strings(filter_cm, filter_list);
571       gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
572     }
573   }
574   if (free_filter)
575     g_free(s);
576 }
577
578 /* redisplay with no display filter */
579 static void
580 filter_reset_cb(GtkWidget *w, gpointer data _U_)
581 {
582   GtkWidget *filter_te = NULL;
583
584   if ((filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY))) {
585     gtk_entry_set_text(GTK_ENTRY(filter_te), "");
586   }
587   filter_packets(&cfile, NULL);
588 }
589
590 /* GTKClist compare routine, overrides default to allow numeric comparison */
591 static gint
592 packet_list_compare(GtkCList *clist, gconstpointer  ptr1, gconstpointer  ptr2)
593 {
594   /* Get row text strings */
595   char *text1 = GTK_CELL_TEXT (((const GtkCListRow *)ptr1)->cell[clist->sort_column])->text;
596   char *text2 = GTK_CELL_TEXT (((const GtkCListRow *)ptr2)->cell[clist->sort_column])->text;
597
598   /* Attempt to convert to numbers */
599   double  num1 = atof(text1);
600   double  num2 = atof(text2);
601
602   gint  col_fmt = cfile.cinfo.col_fmt[clist->sort_column];
603
604   if ((col_fmt == COL_NUMBER) || (col_fmt == COL_REL_TIME) || (col_fmt == COL_DELTA_TIME) ||
605       ((col_fmt == COL_CLS_TIME) && (timestamp_type == RELATIVE)) ||
606       ((col_fmt == COL_CLS_TIME) && (timestamp_type == DELTA))    ||
607       (col_fmt == COL_UNRES_SRC_PORT) || (col_fmt == COL_UNRES_DST_PORT) ||
608       ((num1 != 0) && (num2 != 0) && ((col_fmt == COL_DEF_SRC_PORT) || (col_fmt == COL_RES_SRC_PORT) ||
609                                       (col_fmt == COL_DEF_DST_PORT) || (col_fmt == COL_RES_DST_PORT))) ||
610       (col_fmt == COL_PACKET_LENGTH)) {
611
612     /* Compare numeric column */
613
614     if (num1 < num2)
615       return -1;
616     else if (num1 > num2)
617       return 1;
618     else
619       return 0;
620   }
621
622   else {
623
624     /* Compare text column */
625     if (!text2)
626       return (text1 != NULL);
627
628     if (!text1)
629       return -1;
630
631     return strcmp(text1, text2);
632   }
633 }
634
635 /* What to do when a column is clicked */
636 static void
637 packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data)
638 {
639   column_arrows *col_arrows = (column_arrows *) data;
640   int i;
641
642   gtk_clist_freeze(clist);
643
644   for (i = 0; i < cfile.cinfo.num_cols; i++) {
645     gtk_widget_hide(col_arrows[i].ascend_pm);
646     gtk_widget_hide(col_arrows[i].descend_pm);
647   }
648
649   if (column == clist->sort_column) {
650     if (clist->sort_type == GTK_SORT_ASCENDING) {
651       clist->sort_type = GTK_SORT_DESCENDING;
652       gtk_widget_show(col_arrows[column].descend_pm);
653     } else {
654       clist->sort_type = GTK_SORT_ASCENDING;
655       gtk_widget_show(col_arrows[column].ascend_pm);
656     }
657   }
658   else {
659     clist->sort_type = GTK_SORT_ASCENDING;
660     gtk_widget_show(col_arrows[column].ascend_pm);
661     gtk_clist_set_sort_column(clist, column);
662   }
663   gtk_clist_thaw(clist);
664
665   gtk_clist_sort(clist);
666 }
667
668 /* mark packets */
669 static void
670 set_frame_mark(gboolean set, frame_data *frame, gint row) {
671   GdkColor fg, bg;
672
673   if (row == -1)
674     return;
675   if (set) {
676     mark_frame(&cfile, frame);
677     color_t_to_gdkcolor(&fg, &prefs.gui_marked_fg);
678     color_t_to_gdkcolor(&bg, &prefs.gui_marked_bg);
679     gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
680     gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
681   } else {
682     unmark_frame(&cfile, frame);
683     gtk_clist_set_background(GTK_CLIST(packet_list), row, NULL);
684     gtk_clist_set_foreground(GTK_CLIST(packet_list), row, NULL);
685   }
686   file_set_save_marked_sensitive();
687 }
688
689 #if GTK_MAJOR_VERSION < 2
690 static void
691 packet_list_button_pressed_cb(GtkWidget *w, GdkEvent *event, gpointer data _U_)
692 {
693     GdkEventButton *event_button = (GdkEventButton *)event;
694     gint row, column;
695
696     if (w == NULL || event == NULL)
697         return;
698
699     if (event->type == GDK_BUTTON_PRESS && event_button->button == 2 &&
700         gtk_clist_get_selection_info(GTK_CLIST(w), event_button->x,
701                                      event_button->y, &row, &column)) {
702         frame_data *fdata = (frame_data *) gtk_clist_get_row_data(GTK_CLIST(w),
703                                                                   row);
704         set_frame_mark(!fdata->flags.marked, fdata, row);
705     }
706 }
707 #else
708 static gint
709 packet_list_button_pressed_cb(GtkWidget *w, GdkEvent *event, gpointer data _U_)
710 {
711     GdkEventButton *event_button = (GdkEventButton *)event;
712     gint row, column;
713
714     if (w == NULL || event == NULL)
715         return FALSE;
716
717     if (event->type == GDK_BUTTON_PRESS &&
718         gtk_clist_get_selection_info(GTK_CLIST(w), event_button->x,
719                                      event_button->y, &row, &column)) {
720         if (event_button->button == 2)
721         {
722             frame_data *fdata = (frame_data *)gtk_clist_get_row_data(GTK_CLIST(w), row);
723             set_frame_mark(!fdata->flags.marked, fdata, row);
724             return TRUE;
725         }
726         else if (event_button->button == 1) {
727             gtk_clist_select_row(GTK_CLIST(w), row, column);
728             return TRUE;
729         }
730     }
731     return FALSE;
732 }
733 #endif
734
735 void mark_frame_cb(GtkWidget *w _U_, gpointer data _U_) {
736   if (cfile.current_frame) {
737     /* XXX hum, should better have a "cfile->current_row" here ... */
738     set_frame_mark(!cfile.current_frame->flags.marked,
739                    cfile.current_frame,
740                    gtk_clist_find_row_from_data(GTK_CLIST(packet_list),
741                                                 cfile.current_frame));
742   }
743 }
744
745 static void mark_all_frames(gboolean set) {
746   frame_data *fdata;
747   for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
748     set_frame_mark(set,
749                    fdata,
750                    gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata));
751   }
752 }
753
754 void update_marked_frames(void) {
755   frame_data *fdata;
756   if (cfile.plist == NULL) return;
757   for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
758     if (fdata->flags.marked)
759       set_frame_mark(TRUE,
760                      fdata,
761                      gtk_clist_find_row_from_data(GTK_CLIST(packet_list),
762                                                   fdata));
763   }
764 }
765
766 void mark_all_frames_cb(GtkWidget *w _U_, gpointer data _U_) {
767   mark_all_frames(TRUE);
768 }
769
770 void unmark_all_frames_cb(GtkWidget *w _U_, gpointer data _U_) {
771   mark_all_frames(FALSE);
772 }
773
774 /* What to do when a list item is selected/unselected */
775 static void
776 packet_list_select_cb(GtkWidget *w _U_, gint row, gint col _U_, gpointer evt _U_) {
777
778 /* Remove the hex display tabbed pages */
779   while( (gtk_notebook_get_nth_page( GTK_NOTEBOOK(byte_nb_ptr), 0)))
780     gtk_notebook_remove_page( GTK_NOTEBOOK(byte_nb_ptr), 0);
781
782   select_packet(&cfile, row);
783 }
784
785
786 static void
787 packet_list_unselect_cb(GtkWidget *w _U_, gint row _U_, gint col _U_, gpointer evt _U_) {
788
789   unselect_packet(&cfile);
790 }
791
792
793 #if GTK_MAJOR_VERSION < 2
794 static void
795 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column _U_,
796                         gpointer user_data _U_)
797 #else
798 static void
799 tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data _U_)
800 #endif
801 {
802     field_info   *finfo;
803     gchar        *help_str = NULL;
804     gchar         len_str[2+10+1+5+1]; /* ", {N} bytes\0",
805                                           N < 4294967296 */
806     gboolean      has_blurb = FALSE;
807     guint         length = 0, byte_len;
808     GtkWidget    *byte_view;
809     const guint8 *byte_data;
810 #if GTK_MAJOR_VERSION >= 2
811     GtkTreeModel *model;
812     GtkTreeIter   iter;
813 #endif
814
815 #if GTK_MAJOR_VERSION >= 2
816     /* if nothing is selected */
817     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
818     {
819         /*
820          * Which byte view is displaying the current protocol tree
821          * row's data?
822          */
823         byte_view = get_notebook_bv_ptr(byte_nb_ptr);
824         if (byte_view == NULL)
825             return;     /* none */
826
827         byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
828         if (byte_data == NULL)
829             return;     /* none */
830
831         unselect_field();
832         packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data,
833                          cfile.current_frame, NULL, byte_len);
834         return;
835     }
836     gtk_tree_model_get(model, &iter, 1, &finfo, -1);
837 #else
838     g_assert(node);
839     finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
840 #endif
841     if (!finfo) return;
842
843     set_notebook_page(byte_nb_ptr, finfo->ds_tvb);
844
845     byte_view = get_notebook_bv_ptr(byte_nb_ptr);
846     byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
847     g_assert(byte_data != NULL);
848
849     finfo_selected = finfo;
850     set_menus_for_selected_tree_row(TRUE);
851
852     if (finfo->hfinfo) {
853         if (finfo->hfinfo->blurb != NULL &&
854             finfo->hfinfo->blurb[0] != '\0') {
855             has_blurb = TRUE;
856             length = strlen(finfo->hfinfo->blurb);
857         } else {
858             length = strlen(finfo->hfinfo->name);
859         }
860         if (finfo->length == 0) {
861             len_str[0] = '\0';
862         } else if (finfo->length == 1) {
863             strcpy (len_str, ", 1 byte");
864         } else {
865             snprintf (len_str, sizeof len_str, ", %d bytes", finfo->length);
866         }
867         statusbar_pop_field_msg();      /* get rid of current help msg */
868         if (length) {
869             length += strlen(finfo->hfinfo->abbrev) + strlen(len_str) + 10;
870             help_str = g_malloc(sizeof(gchar) * length);
871             sprintf(help_str, "%s (%s)%s",
872                     (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name,
873                     finfo->hfinfo->abbrev, len_str);
874             statusbar_push_field_msg(help_str);
875             g_free(help_str);
876         } else {
877             /*
878              * Don't show anything if the field name is zero-length;
879              * the pseudo-field for "proto_tree_add_text()" is such
880              * a field, and we don't want "Text (text)" showing up
881              * on the status line if you've selected such a field.
882              *
883              * XXX - there are zero-length fields for which we *do*
884              * want to show the field name.
885              *
886              * XXX - perhaps the name and abbrev field should be null
887              * pointers rather than null strings for that pseudo-field,
888              * but we'd have to add checks for null pointers in some
889              * places if we did that.
890              *
891              * Or perhaps protocol tree items added with
892              * "proto_tree_add_text()" should have -1 as the field index,
893              * with no pseudo-field being used, but that might also
894              * require special checks for -1 to be added.
895              */
896             statusbar_push_field_msg("");
897         }
898     }
899
900 #if GTK_MAJOR_VERSION < 2
901     packet_hex_print(GTK_TEXT(byte_view), byte_data, cfile.current_frame,
902                      finfo, byte_len);
903 #else
904     packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data, cfile.current_frame,
905                      finfo, byte_len);
906 #endif
907 }
908
909 #if GTK_MAJOR_VERSION < 2
910 static void
911 tree_view_unselect_row_cb(GtkCTree *ctree _U_, GList *node _U_, gint column _U_,
912                           gpointer user_data _U_)
913 {
914         GtkWidget       *byte_view;
915         const guint8    *data;
916         guint           len;
917
918         /*
919          * Which byte view is displaying the current protocol tree
920          * row's data?
921          */
922         byte_view = get_notebook_bv_ptr(byte_nb_ptr);
923         if (byte_view == NULL)
924                 return; /* none */
925
926         data = get_byte_view_data_and_length(byte_view, &len);
927         if (data == NULL)
928                 return; /* none */
929
930         unselect_field();
931         packet_hex_print(GTK_TEXT(byte_view), data, cfile.current_frame,
932                 NULL, len);
933 }
934 #endif
935
936 void collapse_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
937   if (cfile.edt->tree)
938     collapse_all_tree(cfile.edt->tree, tree_view);
939 }
940
941 void expand_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
942   if (cfile.edt->tree)
943     expand_all_tree(cfile.edt->tree, tree_view);
944 }
945
946 void resolve_name_cb(GtkWidget *widget _U_, gpointer data _U_) {
947   if (cfile.edt->tree) {
948     guint32 tmp = g_resolv_flags;
949     g_resolv_flags = RESOLV_ALL;
950     proto_tree_draw(cfile.edt->tree, tree_view);
951     g_resolv_flags = tmp;
952   }
953 }
954
955 /* Set the selection mode of the packet list window. */
956 void
957 set_plist_sel_browse(gboolean val)
958 {
959         gboolean old_val;
960
961         old_val =
962             (GTK_CLIST(packet_list)->selection_mode == GTK_SELECTION_SINGLE);
963
964         if (val == old_val) {
965                 /*
966                  * The mode isn't changing, so don't do anything.
967                  * In particular, don't gratuitiously unselect the
968                  * current packet.
969                  *
970                  * XXX - why do we have to unselect the current packet
971                  * ourselves?  The documentation for the GtkCList at
972                  *
973                  *      http://developer.gnome.org/doc/API/gtk/gtkclist.html
974                  *
975                  * says "Note that setting the widget's selection mode to
976                  * one of GTK_SELECTION_BROWSE or GTK_SELECTION_SINGLE will
977                  * cause all the items in the GtkCList to become deselected."
978                  */
979                 return;
980         }
981
982         if (finfo_selected)
983                 unselect_packet(&cfile);
984
985         /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
986          * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
987         if (val) {
988                 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_SINGLE);
989         }
990         else {
991                 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_BROWSE);
992         }
993 }
994
995 /* Set the font of the packet list window. */
996 #if GTK_MAJOR_VERSION < 2
997 void
998 set_plist_font(GdkFont *font)
999 #else
1000 void
1001 set_plist_font(PangoFontDescription *font)
1002 #endif
1003 {
1004         int i;
1005 #if GTK_MAJOR_VERSION < 2
1006         GtkStyle *style;
1007
1008         style = gtk_style_new();
1009         gdk_font_unref(style->font);
1010         style->font = font;
1011         gdk_font_ref(font);
1012
1013         gtk_widget_set_style(packet_list, style);
1014 #else
1015         PangoLayout *layout;
1016
1017         gtk_widget_modify_font(packet_list, font);
1018 #endif
1019
1020         /* Compute static column sizes to use during a "-S" capture, so that
1021            the columns don't resize during a live capture. */
1022         for (i = 0; i < cfile.cinfo.num_cols; i++) {
1023 #if GTK_MAJOR_VERSION < 2
1024                 cfile.cinfo.col_width[i] = gdk_string_width(font,
1025                         get_column_longest_string(get_column_format(i)));
1026 #else
1027                 layout = gtk_widget_create_pango_layout(packet_list,
1028                     get_column_longest_string(get_column_format(i)));
1029                 pango_layout_get_pixel_size(layout, &cfile.cinfo.col_width[i],
1030                                             NULL);
1031                 g_object_unref(G_OBJECT(layout));
1032 #endif
1033         }
1034 }
1035
1036 /*
1037  * Push a message referring to file access onto the statusbar.
1038  */
1039 void
1040 statusbar_push_file_msg(gchar *msg)
1041 {
1042         gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg);
1043 }
1044
1045 /*
1046  * Pop a message referring to file access off the statusbar.
1047  */
1048 void
1049 statusbar_pop_file_msg(void)
1050 {
1051         gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
1052 }
1053
1054 /*
1055  * XXX - do we need multiple statusbar contexts?
1056  */
1057
1058 /*
1059  * Push a message referring to the currently-selected field onto the statusbar.
1060  */
1061 void
1062 statusbar_push_field_msg(gchar *msg)
1063 {
1064         gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg);
1065 }
1066
1067 /*
1068  * Pop a message referring to the currently-selected field off the statusbar.
1069  */
1070 void
1071 statusbar_pop_field_msg(void)
1072 {
1073         gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
1074 }
1075
1076 static gboolean
1077 do_quit(void)
1078 {
1079         /* XXX - should we check whether the capture file is an
1080            unsaved temporary file for a live capture and, if so,
1081            pop up a "do you want to exit without saving the capture
1082            file?" dialog, and then just return, leaving said dialog
1083            box to forcibly quit if the user clicks "OK"?
1084
1085            If so, note that this should be done in a subroutine that
1086            returns TRUE if we do so, and FALSE otherwise, and if it
1087            returns TRUE we should return TRUE without nuking anything.
1088
1089            Note that, if we do that, we might also want to check if
1090            an "Update list of packets in real time" capture is in
1091            progress and, if so, ask whether they want to terminate
1092            the capture and discard it, and return TRUE, before nuking
1093            any child capture, if they say they don't want to do so. */
1094
1095 #ifdef HAVE_LIBPCAP
1096         /* Nuke any child capture in progress. */
1097         kill_capture_child();
1098 #endif
1099
1100         /* Are we in the middle of reading a capture? */
1101         if (cfile.state == FILE_READ_IN_PROGRESS) {
1102                 /* Yes, so we can't just close the file and quit, as
1103                    that may yank the rug out from under the read in
1104                    progress; instead, just set the state to
1105                    "FILE_READ_ABORTED" and return - the code doing the read
1106                    will check for that and, if it sees that, will clean
1107                    up and quit. */
1108                 cfile.state = FILE_READ_ABORTED;
1109
1110                 /* Say that the window should *not* be deleted;
1111                    that'll be done by the code that cleans up. */
1112                 return TRUE;
1113         } else {
1114                 /* Close any capture file we have open; on some OSes, you
1115                    can't unlink a temporary capture file if you have it
1116                    open.
1117                    "close_cap_file()" will unlink it after closing it if
1118                    it's a temporary file.
1119
1120                    We do this here, rather than after the main loop returns,
1121                    as, after the main loop returns, the main window may have
1122                    been destroyed (if this is called due to a "destroy"
1123                    even on the main window rather than due to the user
1124                    selecting a menu item), and there may be a crash
1125                    or other problem when "close_cap_file()" tries to
1126                    clean up stuff in the main window.
1127
1128                    XXX - is there a better place to put this?
1129                    Or should we have a routine that *just* closes the
1130                    capture file, and doesn't do anything with the UI,
1131                    which we'd call here, and another routine that
1132                    calls that routine and also cleans up the UI, which
1133                    we'd call elsewhere? */
1134                 close_cap_file(&cfile);
1135
1136                 /* Exit by leaving the main loop, so that any quit functions
1137                    we registered get called. */
1138                 gtk_main_quit();
1139
1140                 /* Say that the window should be deleted. */
1141                 return FALSE;
1142         }
1143 }
1144
1145 static gboolean
1146 main_window_delete_event_cb(GtkWidget *widget _U_, GdkEvent *event _U_, gpointer data _U_)
1147 {
1148         gint desk_x, desk_y;
1149
1150         /* Try to grab our geometry */
1151         gdk_window_get_root_origin(top_level->window, &root_x, &root_y);
1152         if (gdk_window_get_deskrelative_origin(top_level->window,
1153                                 &desk_x, &desk_y)) {
1154                 if (desk_x <= root_x && desk_y <= root_y) {
1155                         root_x = desk_x;
1156                         root_y = desk_y;
1157                 }
1158         }
1159
1160         /* XXX - Is this the "approved" method? */
1161         gdk_window_get_size(top_level->window, &top_width, &top_height);
1162
1163         /* "do_quit()" indicates whether the main window should be deleted. */
1164         return do_quit();
1165 }
1166
1167 void
1168 file_quit_cmd_cb (GtkWidget *widget _U_, gpointer data _U_)
1169 {
1170         do_quit();
1171 }
1172
1173 static void
1174 print_usage(gboolean print_ver) {
1175
1176   if (print_ver) {
1177     fprintf(stderr, "This is GNU " PACKAGE " " VERSION "\n%s\n%s\n",
1178           comp_info_str->str, runtime_info_str->str);
1179   }
1180 #ifdef HAVE_LIBPCAP
1181   fprintf(stderr, "\n%s [ -vh ] [ -klpQS ] [ -a <capture autostop condition> ] ...\n",
1182           PACKAGE);
1183   fprintf(stderr, "\t[ -b <number of ringbuffer files>[:<duration>] ]\n");
1184   fprintf(stderr, "\t[ -B <byte view height> ]\n");
1185   fprintf(stderr, "\t[ -c <count> ] [ -f <capture filter> ] [ -i <interface> ]\n");
1186   fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -N <resolving> ]\n");
1187   fprintf(stderr, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
1188   fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
1189   fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
1190   fprintf(stderr, "\t[ -w <savefile> ] [ <infile> ]\n");
1191 #else
1192   fprintf(stderr, "\n%s [ -vh ] [ -B <byte view height> ] [ -m <medium font> ]\n",
1193           PACKAGE);
1194   fprintf(stderr, "\t[ -n ] [ -N <resolving> ]\n");
1195   fprintf(stderr, "\t[ -o <preference setting> ... [ -P <packet list height> ]\n");
1196   fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -t <time stamp format> ]\n");
1197   fprintf(stderr, "\t[ -T <tree view height> ] [ <infile> ]\n");
1198 #endif
1199 }
1200
1201 static void
1202 show_version(void)
1203 {
1204 #ifdef WIN32
1205   create_console();
1206 #endif
1207
1208   printf("%s %s\n%s\n%s\n", PACKAGE, VERSION, comp_info_str->str,
1209          runtime_info_str->str);
1210 }
1211
1212 static int
1213 get_positive_int(const char *string, const char *name)
1214 {
1215   long number;
1216   char *p;
1217
1218   number = strtol(string, &p, 10);
1219   if (p == string || *p != '\0') {
1220     fprintf(stderr, "ethereal: The specified %s \"%s\" is not a decimal number\n",
1221             name, string);
1222     exit(1);
1223   }
1224   if (number < 0) {
1225     fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
1226             name, string);
1227     exit(1);
1228   }
1229   if (number == 0) {
1230     fprintf(stderr, "ethereal: The specified %s \"%s\" is zero\n",
1231             name, string);
1232     exit(1);
1233   }
1234   if (number > INT_MAX) {
1235     fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
1236             name, string, INT_MAX);
1237     exit(1);
1238   }
1239   return number;
1240 }
1241
1242 #ifdef HAVE_LIBPCAP
1243 /*
1244  * Given a string of the form "<autostop criterion>:<value>", as might appear
1245  * as an argument to a "-a" option, parse it and set the criterion in
1246  * question.  Return an indication of whether it succeeded or failed
1247  * in some fashion.
1248  */
1249 static gboolean
1250 set_autostop_criterion(const char *autostoparg)
1251 {
1252   guchar *p, *colonp;
1253
1254   colonp = strchr(autostoparg, ':');
1255   if (colonp == NULL)
1256     return FALSE;
1257
1258   p = colonp;
1259   *p++ = '\0';
1260
1261   /*
1262    * Skip over any white space (there probably won't be any, but
1263    * as we allow it in the preferences file, we might as well
1264    * allow it here).
1265    */
1266   while (isspace(*p))
1267     p++;
1268   if (*p == '\0') {
1269     /*
1270      * Put the colon back, so if our caller uses, in an
1271      * error message, the string they passed us, the message
1272      * looks correct.
1273      */
1274     *colonp = ':';
1275     return FALSE;
1276   }
1277   if (strcmp(autostoparg,"duration") == 0) {
1278     capture_opts.has_autostop_duration = TRUE;
1279     capture_opts.autostop_duration = get_positive_int(p,"autostop duration");
1280   } else if (strcmp(autostoparg,"filesize") == 0) {
1281     capture_opts.has_autostop_filesize = TRUE;
1282     capture_opts.autostop_filesize = get_positive_int(p,"autostop filesize");
1283   } else {
1284     return FALSE;
1285   }
1286   *colonp = ':'; /* put the colon back */
1287   return TRUE;
1288 }
1289
1290 /*
1291  * Given a string of the form "<ring buffer file>:<duration>", as might appear
1292  * as an argument to a "-b" option, parse it and set the arguments in
1293  * question.  Return an indication of whether it succeeded or failed
1294  * in some fashion.
1295  */
1296 static gboolean
1297 get_ring_arguments(const char *arg)
1298 {
1299   guchar *p = NULL, *colonp;
1300
1301   colonp = strchr(arg, ':');
1302
1303   if (colonp != NULL) {
1304     p = colonp;
1305     *p++ = '\0';
1306   }
1307
1308   capture_opts.ringbuffer_num_files = 
1309     get_positive_int(arg, "number of ring buffer files");
1310
1311   if (colonp == NULL)
1312     return TRUE;
1313
1314   /*
1315    * Skip over any white space (there probably won't be any, but
1316    * as we allow it in the preferences file, we might as well
1317    * allow it here).
1318    */
1319   while (isspace(*p))
1320     p++;
1321   if (*p == '\0') {
1322     /*
1323      * Put the colon back, so if our caller uses, in an
1324      * error message, the string they passed us, the message
1325      * looks correct.
1326      */
1327     *colonp = ':';
1328     return FALSE;
1329   }
1330
1331   capture_opts.has_ring_duration = TRUE;
1332   capture_opts.ringbuffer_duration = get_positive_int(p,
1333                                                       "ring buffer duration");
1334
1335   *colonp = ':';        /* put the colon back */
1336   return TRUE;
1337 }
1338 #endif
1339
1340 #if defined WIN32 || GTK_MAJOR_VERSION < 2 || ! defined USE_THREADS
1341 /* 
1342    Once every 3 seconds we get a callback here which we use to update
1343    the tap extensions. Since Gtk1 is single threaded we dont have to
1344    worry about any locking or critical regions.
1345  */
1346 static gint
1347 update_cb(gpointer data _U_)
1348 {
1349         draw_tap_listeners(FALSE);
1350         return 1;
1351 }
1352 #else
1353
1354 /* if these three functions are copied to gtk1 ethereal, since gtk1 does not
1355    use threads all updte_thread_mutex can be dropped and protect/unprotect 
1356    would just be empty functions.
1357
1358    This allows gtk2-rpcstat.c and friends to be copied unmodified to 
1359    gtk1-ethereal and it will just work.
1360  */
1361 static GStaticMutex update_thread_mutex = G_STATIC_MUTEX_INIT;
1362 gpointer
1363 update_thread(gpointer data _U_)
1364 {
1365     while(1){
1366         GTimeVal tv1, tv2;
1367         g_get_current_time(&tv1);
1368         g_static_mutex_lock(&update_thread_mutex);
1369         gdk_threads_enter();
1370         draw_tap_listeners(FALSE);
1371         gdk_threads_leave();
1372         g_static_mutex_unlock(&update_thread_mutex);
1373         g_thread_yield();
1374         g_get_current_time(&tv2);
1375         if( ((tv1.tv_sec + 2) * 1000000 + tv1.tv_usec) >
1376             (tv2.tv_sec * 1000000 + tv2.tv_usec) ){
1377             g_usleep(((tv1.tv_sec + 2) * 1000000 + tv1.tv_usec) -
1378                      (tv2.tv_sec * 1000000 + tv2.tv_usec));
1379         }
1380     }
1381     return NULL;
1382 }
1383 #endif
1384 void
1385 protect_thread_critical_region(void)
1386 {
1387 #if ! defined WIN32 && GTK_MAJOR_VERSION >= 2 && defined USE_THREADS
1388     g_static_mutex_lock(&update_thread_mutex);
1389 #endif
1390 }
1391 void
1392 unprotect_thread_critical_region(void)
1393 {
1394 #if ! defined WIN32 && GTK_MAJOR_VERSION >= 2 && defined USE_THREADS
1395     g_static_mutex_unlock(&update_thread_mutex);
1396 #endif
1397 }
1398
1399 /* structure to keep track of what tap listeners have been registered.
1400  */
1401 typedef struct _ethereal_tap_list {
1402         struct _ethereal_tap_list *next;
1403         char *cmd;
1404         void (*func)(char *arg);
1405 } ethereal_tap_list;
1406 static ethereal_tap_list *tap_list=NULL;
1407
1408 void
1409 register_ethereal_tap(char *cmd, void (*func)(char *arg))
1410 {
1411         ethereal_tap_list *newtl;
1412
1413         newtl=malloc(sizeof(ethereal_tap_list));
1414         newtl->next=tap_list;
1415         tap_list=newtl;
1416         newtl->cmd=cmd;
1417         newtl->func=func;
1418
1419 }
1420
1421 /* And now our feature presentation... [ fade to music ] */
1422 int
1423 main(int argc, char *argv[])
1424 {
1425 #ifdef HAVE_LIBPCAP
1426   char                *command_name;
1427 #endif
1428   char                *s;
1429   int                  i;
1430   int                  opt;
1431   extern char         *optarg;
1432   gboolean             arg_error = FALSE;
1433
1434 #ifdef WIN32
1435   WSADATA              wsaData;
1436 #endif  /* WIN32 */
1437
1438   char                *gpf_path, *cf_path, *df_path;
1439   char                *pf_path;
1440   int                  gpf_open_errno, pf_open_errno, cf_open_errno, df_open_errno;
1441   int                  err;
1442 #ifdef HAVE_LIBPCAP
1443   gboolean             start_capture = FALSE;
1444   gchar               *save_file = NULL;
1445   GList               *if_list;
1446   gchar                err_str[PCAP_ERRBUF_SIZE];
1447   gboolean             stats_known;
1448   struct pcap_stat     stats;
1449 #else
1450   gboolean             capture_option_specified = FALSE;
1451 #endif
1452   gint                 pl_size = 280, tv_size = 95, bv_size = 75;
1453   gchar               *rc_file, *cf_name = NULL, *rfilter = NULL;
1454   dfilter_t           *rfcode = NULL;
1455   gboolean             rfilter_parse_failed = FALSE;
1456   e_prefs             *prefs;
1457   char                 badopt;
1458 #if GTK_MAJOR_VERSION < 2
1459   char                *bold_font_name;
1460 #endif
1461   gint                 desk_x, desk_y;
1462   gboolean             prefs_write_needed = FALSE;
1463   ethereal_tap_list   *tli = NULL;
1464   gchar               *tap_opt = NULL;
1465
1466 #define OPTSTRING_INIT "a:b:B:c:f:hi:klm:nN:o:pP:Qr:R:Ss:t:T:w:vz:"
1467
1468 #ifdef HAVE_LIBPCAP
1469 #ifdef WIN32
1470 #define OPTSTRING_CHILD "W:Z:"
1471 #else
1472 #define OPTSTRING_CHILD "W:"
1473 #endif  /* WIN32 */
1474 #else
1475 #define OPTSTRING_CHILD ""
1476 #endif  /* HAVE_LIBPCAP */
1477
1478   char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_CHILD) - 1] =
1479     OPTSTRING_INIT;
1480
1481   ethereal_path = argv[0];
1482
1483 #ifdef WIN32
1484   /* Arrange that if we have no console window, and a GLib message logging
1485      routine is called to log a message, we pop up a console window.
1486
1487      We do that by inserting our own handler for all messages logged
1488      to the default domain; that handler pops up a console if necessary,
1489      and then calls the default handler. */
1490   g_log_set_handler(NULL,
1491                     G_LOG_LEVEL_ERROR|
1492                     G_LOG_LEVEL_CRITICAL|
1493                     G_LOG_LEVEL_WARNING|
1494                     G_LOG_LEVEL_MESSAGE|
1495                     G_LOG_LEVEL_INFO|
1496                     G_LOG_LEVEL_DEBUG|
1497                     G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION,
1498                     console_log_handler, NULL);
1499 #endif
1500
1501 #ifdef HAVE_LIBPCAP
1502   command_name = get_basename(ethereal_path);
1503   /* Set "capture_child" to indicate whether this is going to be a child
1504      process for a "-S" capture. */
1505   capture_child = (strcmp(command_name, CHILD_NAME) == 0);
1506   if (capture_child)
1507     strcat(optstring, OPTSTRING_CHILD);
1508 #endif
1509
1510   /* Register all dissectors; we must do this before checking for the
1511      "-G" flag, as the "-G" flag dumps information registered by the
1512      dissectors, and we must do it before we read the preferences, in
1513      case any dissectors register preferences. */
1514   epan_init(PLUGIN_DIR,register_all_protocols,register_all_protocol_handoffs);
1515
1516   /* Register all tap listeners; we do this before we parse the arguments,
1517      as the "-z" argument can specify a registered tap. */
1518   register_all_tap_listeners();
1519
1520   /* Now register the preferences for any non-dissector modules.
1521      We must do that before we read the preferences as well. */
1522   prefs_register_modules();
1523
1524   /* If invoked with the "-G" flag, we dump out information based on
1525      the argument to the "-G" flag; if no argument is specified,
1526      for backwards compatibility we dump out a glossary of display
1527      filter symbols.
1528
1529      We must do this before calling "gtk_init()", because "gtk_init()"
1530      tries to open an X display, and we don't want to have to do any X
1531      stuff just to do a build.
1532
1533      Given that we call "gtk_init()" before doing the regular argument
1534      list processing, so that it can handle X and GTK+ arguments and
1535      remove them from the list at which we look, this means we must do
1536      this before doing the regular argument list processing, as well.
1537
1538      This means that:
1539
1540         you must give the "-G" flag as the first flag on the command line;
1541
1542         you must give it as "-G", nothing more, nothing less;
1543
1544         the first argument after the "-G" flag, if present, will be used
1545         to specify the information to dump;
1546
1547         arguments after that will not be used. */
1548   if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
1549     if (argc == 2)
1550       proto_registrar_dump_fields();
1551     else {
1552       if (strcmp(argv[2], "fields") == 0)
1553         proto_registrar_dump_fields();
1554       else if (strcmp(argv[2], "protocols") == 0)
1555         proto_registrar_dump_protocols();
1556       else {
1557         fprintf(stderr, "ethereal: Invalid \"%s\" option for -G flag\n",
1558                 argv[2]);
1559         exit(1);
1560       }
1561     }
1562     exit(0);
1563   }
1564
1565   /* multithread support currently doesn't seem to work in win32 gtk2.0.6 */
1566 #if ! defined WIN32 && GTK_MAJOR_VERSION >= 2 && defined G_THREADS_ENABLED && defined USE_THREADS
1567   {
1568       GThread *ut;
1569       g_thread_init(NULL);
1570       gdk_threads_init();
1571       ut=g_thread_create(update_thread, NULL, FALSE, NULL);
1572       g_thread_set_priority(ut, G_THREAD_PRIORITY_LOW);
1573   }
1574 #else  /* WIN32 || GTK1.2 || !G_THREADS_ENABLED || !USE_THREADS */
1575   /* this is to keep tap extensions updating once every 3 seconds */
1576   gtk_timeout_add(3000, (GtkFunction)update_cb,(gpointer)NULL);
1577 #endif /* !WIN32 && GTK2 && G_THREADS_ENABLED */
1578
1579 #if HAVE_GNU_ADNS
1580   gtk_timeout_add(750, (GtkFunction) host_name_lookup_process, NULL);
1581 #endif
1582
1583
1584   /* Set the current locale according to the program environment.
1585    * We haven't localized anything, but some GTK widgets are localized
1586    * (the file selection dialogue, for example).
1587    * This also sets the C-language locale to the native environment. */
1588   gtk_set_locale();
1589
1590   /* Let GTK get its args */
1591   gtk_init (&argc, &argv);
1592
1593   /* Read the preference files. */
1594   prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
1595
1596 #ifdef HAVE_LIBPCAP
1597   capture_opts.has_snaplen = FALSE;
1598   capture_opts.snaplen = MIN_PACKET_SIZE;
1599   capture_opts.has_autostop_count = FALSE;
1600   capture_opts.autostop_count = 1;
1601   capture_opts.has_autostop_duration = FALSE;
1602   capture_opts.autostop_duration = 1;
1603   capture_opts.has_autostop_filesize = FALSE;
1604   capture_opts.autostop_filesize = 1;
1605   capture_opts.ringbuffer_on = FALSE;
1606   capture_opts.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
1607   capture_opts.has_ring_duration = FALSE;
1608   capture_opts.ringbuffer_duration = 1;
1609
1610   /* If this is a capture child process, it should pay no attention
1611      to the "prefs.capture_prom_mode" setting in the preferences file;
1612      it should do what the parent process tells it to do, and if
1613      the parent process wants it not to run in promiscuous mode, it'll
1614      tell it so with a "-p" flag.
1615
1616      Otherwise, set promiscuous mode from the preferences setting. */
1617   if (capture_child)
1618     capture_opts.promisc_mode = TRUE;
1619   else
1620     capture_opts.promisc_mode = prefs->capture_prom_mode;
1621
1622   /* Set "Update list of packets in real time" mode from the preferences
1623      setting. */
1624   capture_opts.sync_mode = prefs->capture_real_time;
1625
1626   /* And do the same for "Automatic scrolling in live capture" mode. */
1627   auto_scroll_live = prefs->capture_auto_scroll;
1628 #endif
1629
1630   /* Set the name resolution code's flags from the preferences. */
1631   g_resolv_flags = prefs->name_resolve;
1632
1633   /* Read the capture filter file. */
1634   read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
1635
1636   /* Read the display filter file. */
1637   read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
1638
1639   init_cap_file(&cfile);
1640
1641   /* Assemble the compile-time version information string */
1642   comp_info_str = g_string_new("Compiled ");
1643   g_string_append(comp_info_str, "with ");
1644   g_string_sprintfa(comp_info_str,
1645 #ifdef GTK_MAJOR_VERSION
1646                     "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
1647                     GTK_MICRO_VERSION);
1648 #else
1649                     "GTK+ (version unknown)");
1650 #endif
1651
1652   g_string_append(comp_info_str, ", ");
1653   get_compiled_version_info(comp_info_str);
1654
1655   /* Assemble the run-time version information string */
1656   runtime_info_str = g_string_new("Running ");
1657   get_runtime_version_info(runtime_info_str);
1658
1659   /* Now get our args */
1660   while ((opt = getopt(argc, argv, optstring)) != -1) {
1661     switch (opt) {
1662       case 'a':        /* autostop criteria */
1663 #ifdef HAVE_LIBPCAP
1664         if (set_autostop_criterion(optarg) == FALSE) {
1665           fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
1666           exit(1);
1667         }
1668 #else
1669         capture_option_specified = TRUE;
1670         arg_error = TRUE;
1671 #endif
1672         break;
1673       case 'b':        /* Ringbuffer option */
1674 #ifdef HAVE_LIBPCAP
1675         capture_opts.ringbuffer_on = TRUE;
1676         if (get_ring_arguments(optarg) == FALSE) {
1677           fprintf(stderr, "ethereal: Invalid or unknown -b arg \"%s\"\n", optarg);
1678           exit(1);
1679         }
1680 #else
1681         capture_option_specified = TRUE;
1682         arg_error = TRUE;
1683 #endif
1684         break;
1685       case 'B':        /* Byte view pane height */
1686         bv_size = get_positive_int(optarg, "byte view pane height");
1687         break;
1688       case 'c':        /* Capture xxx packets */
1689 #ifdef HAVE_LIBPCAP
1690         capture_opts.has_autostop_count = TRUE;
1691         capture_opts.autostop_count = get_positive_int(optarg, "packet count");
1692 #else
1693         capture_option_specified = TRUE;
1694         arg_error = TRUE;
1695 #endif
1696         break;
1697       case 'f':
1698 #ifdef HAVE_LIBPCAP
1699         if (cfile.cfilter)
1700                 g_free(cfile.cfilter);
1701         cfile.cfilter = g_strdup(optarg);
1702 #else
1703         capture_option_specified = TRUE;
1704         arg_error = TRUE;
1705 #endif
1706         break;
1707       case 'h':        /* Print help and exit */
1708         print_usage(TRUE);
1709         exit(0);
1710         break;
1711       case 'i':        /* Use interface xxx */
1712 #ifdef HAVE_LIBPCAP
1713         cfile.iface = g_strdup(optarg);
1714 #else
1715         capture_option_specified = TRUE;
1716         arg_error = TRUE;
1717 #endif
1718         break;
1719       case 'k':        /* Start capture immediately */
1720 #ifdef HAVE_LIBPCAP
1721         start_capture = TRUE;
1722 #else
1723         capture_option_specified = TRUE;
1724         arg_error = TRUE;
1725 #endif
1726         break;
1727       case 'l':        /* Automatic scrolling in live capture mode */
1728 #ifdef HAVE_LIBPCAP
1729         auto_scroll_live = TRUE;
1730 #else
1731         capture_option_specified = TRUE;
1732         arg_error = TRUE;
1733 #endif
1734         break;
1735       case 'm':        /* Fixed-width font for the display */
1736         if (prefs->gui_font_name != NULL)
1737           g_free(prefs->gui_font_name);
1738         prefs->gui_font_name = g_strdup(optarg);
1739         break;
1740       case 'n':        /* No name resolution */
1741         g_resolv_flags = RESOLV_NONE;
1742         break;
1743       case 'N':        /* Select what types of addresses/port #s to resolve */
1744         if (g_resolv_flags == RESOLV_ALL)
1745           g_resolv_flags = RESOLV_NONE;
1746         badopt = string_to_name_resolve(optarg, &g_resolv_flags);
1747         if (badopt != '\0') {
1748           fprintf(stderr, "ethereal: -N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'\n",
1749                         badopt);
1750           exit(1);
1751         }
1752         break;
1753       case 'o':        /* Override preference from command line */
1754         switch (prefs_set_pref(optarg)) {
1755
1756         case PREFS_SET_SYNTAX_ERR:
1757           fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
1758           exit(1);
1759           break;
1760
1761         case PREFS_SET_NO_SUCH_PREF:
1762         case PREFS_SET_OBSOLETE:
1763           fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
1764                         optarg);
1765           exit(1);
1766           break;
1767         }
1768         break;
1769       case 'p':        /* Don't capture in promiscuous mode */
1770 #ifdef HAVE_LIBPCAP
1771         capture_opts.promisc_mode = FALSE;
1772 #else
1773         capture_option_specified = TRUE;
1774         arg_error = TRUE;
1775 #endif
1776         break;
1777       case 'P':        /* Packet list pane height */
1778         pl_size = get_positive_int(optarg, "packet list pane height");
1779         break;
1780       case 'Q':        /* Quit after capture (just capture to file) */
1781 #ifdef HAVE_LIBPCAP
1782         quit_after_cap = 1;
1783         start_capture = TRUE;  /*** -Q implies -k !! ***/
1784 #else
1785         capture_option_specified = TRUE;
1786         arg_error = TRUE;
1787 #endif
1788         break;
1789       case 'r':        /* Read capture file xxx */
1790         /* We may set "last_open_dir" to "cf_name", and if we change
1791            "last_open_dir" later, we free the old value, so we have to
1792            set "cf_name" to something that's been allocated. */
1793         cf_name = g_strdup(optarg);
1794         break;
1795       case 'R':        /* Read file filter */
1796         rfilter = optarg;
1797         break;
1798       case 's':        /* Set the snapshot (capture) length */
1799 #ifdef HAVE_LIBPCAP
1800         capture_opts.has_snaplen = TRUE;
1801         capture_opts.snaplen = get_positive_int(optarg, "snapshot length");
1802 #else
1803         capture_option_specified = TRUE;
1804         arg_error = TRUE;
1805 #endif
1806         break;
1807       case 'S':        /* "Sync" mode: used for following file ala tail -f */
1808 #ifdef HAVE_LIBPCAP
1809         capture_opts.sync_mode = TRUE;
1810 #else
1811         capture_option_specified = TRUE;
1812         arg_error = TRUE;
1813 #endif
1814         break;
1815       case 't':        /* Time stamp type */
1816         if (strcmp(optarg, "r") == 0)
1817           timestamp_type = RELATIVE;
1818         else if (strcmp(optarg, "a") == 0)
1819           timestamp_type = ABSOLUTE;
1820         else if (strcmp(optarg, "ad") == 0)
1821           timestamp_type = ABSOLUTE_WITH_DATE;
1822         else if (strcmp(optarg, "d") == 0)
1823           timestamp_type = DELTA;
1824         else {
1825           fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
1826             optarg);
1827           fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
1828           fprintf(stderr, "\"ad\" for absolute with date, or \"d\" for delta.\n");
1829           exit(1);
1830         }
1831         break;
1832       case 'T':        /* Tree view pane height */
1833         tv_size = get_positive_int(optarg, "tree view pane height");
1834         break;
1835       case 'v':        /* Show version and exit */
1836         show_version();
1837 #ifdef WIN32
1838         if (console_was_created)
1839           destroy_console();
1840 #endif
1841         exit(0);
1842         break;
1843       case 'w':        /* Write to capture file xxx */
1844 #ifdef HAVE_LIBPCAP
1845         save_file = g_strdup(optarg);
1846 #else
1847         capture_option_specified = TRUE;
1848         arg_error = TRUE;
1849 #endif
1850         break;
1851 #ifdef HAVE_LIBPCAP
1852       /* This is a hidden option supporting Sync mode, so we don't set
1853        * the error flags for the user in the non-libpcap case.
1854        */
1855       case 'W':        /* Write to capture file FD xxx */
1856         cfile.save_file_fd = atoi(optarg);
1857         break;
1858 #endif
1859       case 'z':
1860         for(tli=tap_list;tli;tli=tli->next){
1861           if(!strncmp(tli->cmd,optarg,strlen(tli->cmd))){
1862             tap_opt = g_strdup(optarg);
1863             break;
1864           }
1865         }
1866         if(!tli){
1867           fprintf(stderr,"ethereal: invalid -z argument.\n");
1868           fprintf(stderr,"  -z argument must be one of :\n");
1869           for(tli=tap_list;tli;tli=tli->next){
1870             fprintf(stderr,"     %s\n",tli->cmd);
1871           }
1872           exit(1);
1873         }
1874         break;
1875
1876 #ifdef _WIN32
1877 #ifdef HAVE_LIBPCAP
1878       /* Hidden option supporting Sync mode */
1879       case 'Z':        /* Write to pipe FD XXX */
1880         /* associate stdout with pipe */
1881         i = atoi(optarg);
1882         if (dup2(i, 1) < 0) {
1883           fprintf(stderr, "Unable to dup pipe handle\n");
1884           exit(1);
1885         }
1886         break;
1887 #endif /* HAVE_LIBPCAP */
1888 #endif /* _WIN32 */
1889
1890       default:
1891       case '?':        /* Bad flag - print usage message */
1892         arg_error = TRUE;
1893         break;
1894     }
1895   }
1896   argc -= optind;
1897   argv += optind;
1898   if (argc >= 1) {
1899     if (cf_name != NULL) {
1900       /*
1901        * Input file name specified with "-r" *and* specified as a regular
1902        * command-line argument.
1903        */
1904       arg_error = TRUE;
1905     } else {
1906       /*
1907        * Input file name not specified with "-r", and a command-line argument
1908        * was specified; treat it as the input file name.
1909        *
1910        * Yes, this is different from tethereal, where non-flag command-line
1911        * arguments are a filter, but this works better on GUI desktops
1912        * where a command can be specified to be run to open a particular
1913        * file - yes, you could have "-r" as the last part of the command,
1914        * but that's a bit ugly.
1915        */
1916       cf_name = g_strdup(argv[0]);
1917     }
1918     argc--;
1919     argv++;
1920   }
1921
1922   if (argc != 0) {
1923     /*
1924      * Extra command line arguments were specified; complain.
1925      */
1926     fprintf(stderr, "Invalid argument: %s\n", argv[0]);
1927     arg_error = TRUE;
1928   }
1929   if (arg_error) {
1930     print_usage(FALSE);
1931     exit(1);
1932   }
1933
1934 #ifdef HAVE_LIBPCAP
1935   if (capture_opts.ringbuffer_on) {
1936     /* Ring buffer works only under certain conditions:
1937        a) ring buffer does not work with temporary files;
1938        b) sync_mode and capture_opts.ringbuffer_on are mutually exclusive -
1939           sync_mode takes precedence;
1940        c) it makes no sense to enable the ring buffer if the maximum
1941           file size is set to "infinite". */
1942     if (save_file == NULL) {
1943       fprintf(stderr, "ethereal: Ring buffer requested, but capture isn't being saved to a permanent file.\n");
1944       capture_opts.ringbuffer_on = FALSE;
1945     }
1946     if (capture_opts.sync_mode) {
1947       fprintf(stderr, "ethereal: Ring buffer requested, but an \"Update list of packets in real time\" capture is being done.\n");
1948       capture_opts.ringbuffer_on = FALSE;
1949     }
1950     if (!capture_opts.has_autostop_filesize) {
1951       fprintf(stderr, "ethereal: Ring buffer requested, but no maximum capture file size was specified.\n");
1952       capture_opts.ringbuffer_on = FALSE;
1953     }
1954   }
1955 #endif
1956
1957 #ifdef WIN32
1958   /* Load wpcap if possible */
1959   load_wpcap();
1960
1961   /* Start windows sockets */
1962   WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
1963 #endif  /* WIN32 */
1964
1965   /* Notify all registered modules that have had any of their preferences
1966      changed either from one of the preferences file or from the command
1967      line that their preferences have changed. */
1968   prefs_apply_all();
1969
1970 #ifndef HAVE_LIBPCAP
1971   if (capture_option_specified)
1972     fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
1973 #endif
1974 #ifdef HAVE_LIBPCAP
1975   if (start_capture) {
1976     /* We're supposed to do a live capture; did the user specify an interface
1977        to use? */
1978     if (cfile.iface == NULL) {
1979       /* No - is a default specified in the preferences file? */
1980       if (prefs->capture_device != NULL) {
1981           /* Yes - use it. */
1982           cfile.iface = g_strdup(prefs->capture_device);
1983       } else {
1984         /* No - pick the first one from the list of interfaces. */
1985         if_list = get_interface_list(&err, err_str);
1986         if (if_list == NULL) {
1987           switch (err) {
1988
1989           case CANT_GET_INTERFACE_LIST:
1990               fprintf(stderr, "ethereal: Can't get list of interfaces: %s\n",
1991                         err_str);
1992               break;
1993
1994           case NO_INTERFACES_FOUND:
1995               fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
1996               break;
1997           }
1998           exit(2);
1999         }
2000         cfile.iface = g_strdup(if_list->data);  /* first interface */
2001         free_interface_list(if_list);
2002       }
2003     }
2004   }
2005   if (capture_child) {
2006     if (cfile.save_file_fd == -1) {
2007       /* XXX - send this to the standard output as something our parent
2008          should put in an error message box? */
2009       fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
2010       exit(1);
2011     }
2012   }
2013 #endif
2014
2015   /* Build the column format array */
2016   col_setup(&cfile.cinfo, prefs->num_cols);
2017   for (i = 0; i < cfile.cinfo.num_cols; i++) {
2018     cfile.cinfo.col_fmt[i] = get_column_format(i);
2019     cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
2020     cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
2021       NUM_COL_FMTS);
2022     get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
2023     cfile.cinfo.col_data[i] = NULL;
2024     if (cfile.cinfo.col_fmt[i] == COL_INFO)
2025       cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
2026     else
2027       cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2028     cfile.cinfo.col_fence[i] = 0;
2029     cfile.cinfo.col_expr[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2030     cfile.cinfo.col_expr_val[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
2031   }
2032
2033 #ifdef HAVE_LIBPCAP
2034   if (capture_opts.has_snaplen) {
2035     if (capture_opts.snaplen < 1)
2036       capture_opts.snaplen = WTAP_MAX_PACKET_SIZE;
2037     else if (capture_opts.snaplen < MIN_PACKET_SIZE)
2038       capture_opts.snaplen = MIN_PACKET_SIZE;
2039   }
2040
2041   /* Check the value range of the ringbuffer_num_files parameter */
2042   if (capture_opts.ringbuffer_num_files < RINGBUFFER_MIN_NUM_FILES)
2043     capture_opts.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
2044   else if (capture_opts.ringbuffer_num_files > RINGBUFFER_MAX_NUM_FILES)
2045     capture_opts.ringbuffer_num_files = RINGBUFFER_MAX_NUM_FILES;
2046 #endif
2047
2048   rc_file = get_persconffile_path(RC_FILE, FALSE);
2049   gtk_rc_parse(rc_file);
2050
2051   /* Try to load the regular and boldface fixed-width fonts */
2052 #if GTK_MAJOR_VERSION < 2
2053   bold_font_name = boldify(prefs->gui_font_name);
2054   m_r_font = gdk_font_load(prefs->gui_font_name);
2055   m_b_font = gdk_font_load(bold_font_name);
2056 #else
2057   m_r_font = pango_font_description_from_string(prefs->gui_font_name);
2058   m_b_font = pango_font_description_copy(m_r_font);
2059   pango_font_description_set_weight(m_b_font, PANGO_WEIGHT_BOLD);
2060 #endif
2061   if (m_r_font == NULL || m_b_font == NULL) {
2062     /* XXX - pop this up as a dialog box? no */
2063     if (m_r_font == NULL) {
2064 #ifdef HAVE_LIBPCAP
2065       if (!capture_child)
2066 #endif
2067 #if GTK_MAJOR_VERSION < 2
2068         fprintf(stderr, "ethereal: Warning: font %s not found - defaulting to 6x13 and 6x13bold\n",
2069 #else
2070         fprintf(stderr, "ethereal: Warning: font %s not found - defaulting to Monospace 9\n",
2071 #endif
2072                 prefs->gui_font_name);
2073     } else {
2074 #if GTK_MAJOR_VERSION < 2
2075       gdk_font_unref(m_r_font);
2076 #else
2077       pango_font_description_free(m_r_font);
2078 #endif
2079     }
2080     if (m_b_font == NULL) {
2081 #ifdef HAVE_LIBPCAP
2082       if (!capture_child)
2083 #endif
2084 #if GTK_MAJOR_VERSION < 2
2085         fprintf(stderr, "ethereal: Warning: font %s not found - defaulting to 6x13 and 6x13bold\n",
2086                 bold_font_name);
2087 #else
2088         fprintf(stderr, "ethereal: Warning: bold font %s not found - defaulting"
2089                         " to Monospace 9\n", prefs->gui_font_name);
2090 #endif
2091     } else {
2092 #if GTK_MAJOR_VERSION < 2
2093       gdk_font_unref(m_b_font);
2094 #else
2095       pango_font_description_free(m_b_font);
2096 #endif
2097     }
2098 #if GTK_MAJOR_VERSION < 2
2099     g_free(bold_font_name);
2100     if ((m_r_font = gdk_font_load("6x13")) == NULL) {
2101       fprintf(stderr, "ethereal: Error: font 6x13 not found\n");
2102 #else
2103     if ((m_r_font = pango_font_description_from_string("Monospace 9")) == NULL)
2104     {
2105             fprintf(stderr, "ethereal: Error: font Monospace 9 not found\n");
2106 #endif
2107       exit(1);
2108     }
2109 #if GTK_MAJOR_VERSION < 2
2110     if ((m_b_font = gdk_font_load("6x13bold")) == NULL) {
2111       fprintf(stderr, "ethereal: Error: font 6x13bold not found\n");
2112 #else
2113     if ((m_b_font = pango_font_description_copy(m_r_font)) == NULL) {
2114             fprintf(stderr, "ethereal: Error: font Monospace 9 bold not found\n");
2115 #endif
2116       exit(1);
2117     }
2118     g_free(prefs->gui_font_name);
2119 #if GTK_MAJOR_VERSION < 2
2120     prefs->gui_font_name = g_strdup("6x13");
2121 #else
2122     pango_font_description_set_weight(m_b_font, PANGO_WEIGHT_BOLD);
2123     prefs->gui_font_name = g_strdup("Monospace 9");
2124 #endif
2125   }
2126
2127   /* Call this for the side-effects that set_fonts() produces */
2128   set_fonts(m_r_font, m_b_font);
2129
2130
2131 #ifdef HAVE_LIBPCAP
2132   /* Is this a "child" ethereal, which is only supposed to pop up a
2133      capture box to let us stop the capture, and run a capture
2134      to a file that our parent will read? */
2135   if (!capture_child) {
2136 #endif
2137     /* No.  Pop up the main window, register menus for taps (which we
2138        must do after creating the main window, so that we can add
2139        menu items to the main menu), and read in a capture file if
2140        we were told to. */
2141     create_main_window(pl_size, tv_size, bv_size, prefs);
2142     register_all_tap_menus();
2143     set_menus_for_capture_file(FALSE);
2144
2145     /* open tap windows after creating the main window to avoid GTK warnings */
2146     if (tap_opt && tli) {
2147       (*tli->func)(tap_opt);
2148       g_free(tap_opt);
2149     }
2150
2151     colors_init();
2152     colfilter_init();
2153
2154     /* If we were given the name of a capture file, read it in now;
2155        we defer it until now, so that, if we can't open it, and pop
2156        up an alert box, the alert box is more likely to come up on
2157        top of the main window - but before the preference-file-error
2158        alert box, so, if we get one of those, it's more likely to come
2159        up on top of us. */
2160     if (cf_name) {
2161       if (rfilter != NULL) {
2162         if (!dfilter_compile(rfilter, &rfcode)) {
2163           simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
2164           rfilter_parse_failed = TRUE;
2165         }
2166       }
2167       if (!rfilter_parse_failed) {
2168         if ((err = open_cap_file(cf_name, FALSE, &cfile)) == 0) {
2169           /* "open_cap_file()" succeeded, so it closed the previous
2170              capture file, and thus destroyed any previous read filter
2171              attached to "cf". */
2172           cfile.rfcode = rfcode;
2173           switch (read_cap_file(&cfile, &err)) {
2174
2175           case READ_SUCCESS:
2176           case READ_ERROR:
2177             /* Just because we got an error, that doesn't mean we were unable
2178                to read any of the file; we handle what we could get from the
2179                file. */
2180             break;
2181
2182           case READ_ABORTED:
2183             /* Exit now. */
2184             gtk_exit(0);
2185             break;
2186           }
2187           /* Save the name of the containing directory specified in the
2188              path name, if any; we can write over cf_name, which is a
2189              good thing, given that "get_dirname()" does write over its
2190              argument. */
2191           s = get_dirname(cf_name);
2192           set_last_open_dir(s);
2193           g_free(cf_name);
2194           cf_name = NULL;
2195         } else {
2196           if (rfcode != NULL)
2197             dfilter_free(rfcode);
2198           cfile.rfcode = NULL;
2199         }
2200       }
2201     }
2202 #ifdef HAVE_LIBPCAP
2203   }
2204 #endif
2205
2206   /* If the global preferences file exists but we failed to open it,
2207      pop up an alert box; we defer that until now, so that the alert
2208      box is more likely to come up on top of the main window. */
2209   if (gpf_path != NULL) {
2210       simple_dialog(ESD_TYPE_WARN, NULL,
2211         "Could not open global preferences file\n\"%s\": %s.", gpf_path,
2212         strerror(gpf_open_errno));
2213   }
2214
2215   /* If the user's preferences file exists but we failed to open it,
2216      pop up an alert box; we defer that until now, so that the alert
2217      box is more likely to come up on top of the main window. */
2218   if (pf_path != NULL) {
2219       simple_dialog(ESD_TYPE_WARN, NULL,
2220         "Could not open your preferences file\n\"%s\": %s.", pf_path,
2221         strerror(pf_open_errno));
2222       g_free(pf_path);
2223       pf_path = NULL;
2224   }
2225
2226   /* If the user's capture filter file exists but we failed to open it,
2227      pop up an alert box; we defer that until now, so that the alert
2228      box is more likely to come up on top of the main window. */
2229   if (cf_path != NULL) {
2230       simple_dialog(ESD_TYPE_WARN, NULL,
2231         "Could not open your capture filter file\n\"%s\": %s.", cf_path,
2232         strerror(cf_open_errno));
2233       g_free(cf_path);
2234   }
2235
2236   /* If the user's display filter file exists but we failed to open it,
2237      pop up an alert box; we defer that until now, so that the alert
2238      box is more likely to come up on top of the main window. */
2239   if (df_path != NULL) {
2240       simple_dialog(ESD_TYPE_WARN, NULL,
2241         "Could not open your display filter file\n\"%s\": %s.", df_path,
2242         strerror(df_open_errno));
2243       g_free(df_path);
2244   }
2245
2246 #ifdef HAVE_LIBPCAP
2247   if (capture_child) {
2248     /* This is the child process for a sync mode or fork mode capture,
2249        so just do the low-level work of a capture - don't create
2250        a temporary file and fork off *another* child process (so don't
2251        call "do_capture()"). */
2252
2253        /* XXX - hand these stats to the parent process */
2254        capture(&stats_known, &stats);
2255
2256        /* The capture is done; there's nothing more for us to do. */
2257        gtk_exit(0);
2258   } else {
2259     if (start_capture) {
2260       /* "-k" was specified; start a capture. */
2261       do_capture(save_file);
2262       if (save_file != NULL) {
2263         /* Save the directory name for future file dialogs. */
2264         s = get_dirname(save_file);  /* Overwrites save_file */
2265         set_last_open_dir(s);
2266         g_free(save_file);
2267         save_file = NULL;
2268       }
2269     }
2270     else {
2271       set_menus_for_capture_in_progress(FALSE);
2272     }
2273   }
2274 #else
2275   set_menus_for_capture_in_progress(FALSE);
2276 #endif
2277
2278   gtk_main();
2279
2280         /* Try to save our geometry.  GTK+ provides two routines to get a
2281                  window's position relative to the X root window.  If I understand the
2282                  documentation correctly, gdk_window_get_deskrelative_origin applies
2283                  mainly to Enlightenment and gdk_window_get_root_origin applies for
2284                  all other WMs.
2285
2286            The code below tries both routines, and picks the one that returns
2287            the upper-left-most coordinates.
2288
2289            More info at:
2290
2291            http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html
2292            http://www.gtk.org/faq/#AEN600 */
2293
2294         /* Re-read our saved preferences. */
2295         /* XXX - Move all of this into a separate function? */
2296         prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
2297
2298         if (pf_path == NULL) {
2299                 if (prefs->gui_geometry_save_position) {
2300                         if (top_level->window != NULL) {
2301                                 gdk_window_get_root_origin(top_level->window, &root_x, &root_y);
2302                                 if (gdk_window_get_deskrelative_origin(top_level->window,
2303                                                         &desk_x, &desk_y)) {
2304                                         if (desk_x <= root_x && desk_y <= root_y) {
2305                                                 root_x = desk_x;
2306                                                 root_y = desk_y;
2307                                         }
2308                                 }
2309                         }
2310                         if (prefs->gui_geometry_main_x != root_x) {
2311                                 prefs->gui_geometry_main_x = root_x;
2312                                 prefs_write_needed = TRUE;
2313                         }
2314                         if (prefs->gui_geometry_main_y != root_y) {
2315                                 prefs->gui_geometry_main_y = root_y;
2316                                 prefs_write_needed = TRUE;
2317                         }
2318                 }
2319
2320                 if (prefs->gui_geometry_save_size) {
2321                         if (top_level->window != NULL) {
2322                                 /* XXX - Is this the "approved" method? */
2323                                 gdk_window_get_size(top_level->window, &top_width, &top_height);
2324                         }
2325                         if (prefs->gui_geometry_main_width != top_width) {
2326                                 prefs->gui_geometry_main_width = top_width;
2327                                 prefs_write_needed = TRUE;
2328                         }
2329                         if (prefs->gui_geometry_main_height != top_height) {
2330                                 prefs->gui_geometry_main_height = top_height;
2331                                 prefs_write_needed = TRUE;
2332                         }
2333                 }
2334
2335                 if (prefs_write_needed) {
2336                         write_prefs(&pf_path);
2337                 }
2338         } else {
2339                 /* Ignore errors silently */
2340                 g_free(pf_path);
2341         }
2342
2343   epan_cleanup();
2344   g_free(rc_file);
2345
2346 #ifdef WIN32
2347   /* Shutdown windows sockets */
2348   WSACleanup();
2349
2350   /* For some unknown reason, the "atexit()" call in "create_console()"
2351      doesn't arrange that "destroy_console()" be called when we exit,
2352      so we call it here if a console was created. */
2353   if (console_was_created)
2354     destroy_console();
2355 #endif
2356
2357   gtk_exit(0);
2358
2359   /* This isn't reached, but we need it to keep GCC from complaining
2360      that "main()" returns without returning a value - it knows that
2361      "exit()" never returns, but it doesn't know that "gtk_exit()"
2362      doesn't, as GTK+ doesn't declare it with the attribute
2363      "noreturn". */
2364   return 0;     /* not reached */
2365 }
2366
2367 #ifdef WIN32
2368
2369 /* We build this as a GUI subsystem application on Win32, so
2370    "WinMain()", not "main()", gets called.
2371
2372    Hack shamelessly stolen from the Win32 port of the GIMP. */
2373 #ifdef __GNUC__
2374 #define _stdcall  __attribute__((stdcall))
2375 #endif
2376
2377 int _stdcall
2378 WinMain (struct HINSTANCE__ *hInstance,
2379          struct HINSTANCE__ *hPrevInstance,
2380          char               *lpszCmdLine,
2381          int                 nCmdShow)
2382 {
2383   has_no_console = TRUE;
2384   return main (__argc, __argv);
2385 }
2386
2387 /*
2388  * If this application has no console window to which its standard output
2389  * would go, create one.
2390  */
2391 static void
2392 create_console(void)
2393 {
2394   if (has_no_console) {
2395     /* We have no console to which to print the version string, so
2396        create one and make it the standard input, output, and error. */
2397     if (!AllocConsole())
2398       return;   /* couldn't create console */
2399     freopen("CONIN$", "r", stdin);
2400     freopen("CONOUT$", "w", stdout);
2401     freopen("CONOUT$", "w", stderr);
2402
2403     /* Well, we have a console now. */
2404     has_no_console = FALSE;
2405     console_was_created = TRUE;
2406
2407     /* Now register "destroy_console()" as a routine to be called just
2408        before the application exits, so that we can destroy the console
2409        after the user has typed a key (so that the console doesn't just
2410        disappear out from under them, giving the user no chance to see
2411        the message(s) we put in there). */
2412     atexit(destroy_console);
2413   }
2414 }
2415
2416 static void
2417 destroy_console(void)
2418 {
2419   printf("\n\nPress any key to exit\n");
2420   _getch();
2421   FreeConsole();
2422 }
2423
2424 /* This routine should not be necessary, at least as I read the GLib
2425    source code, as it looks as if GLib is, on Win32, *supposed* to
2426    create a console window into which to display its output.
2427
2428    That doesn't happen, however.  I suspect there's something completely
2429    broken about that code in GLib-for-Win32, and that it may be related
2430    to the breakage that forces us to just call "printf()" on the message
2431    rather than passing the message on to "g_log_default_handler()"
2432    (which is the routine that does the aforementioned non-functional
2433    console window creation). */
2434 static void
2435 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
2436                     const char *message, gpointer user_data)
2437 {
2438   create_console();
2439   if (console_was_created) {
2440     /* For some unknown reason, the above doesn't appear to actually cause
2441        anything to be sent to the standard output, so we'll just splat the
2442        message out directly, just to make sure it gets out. */
2443     printf("%s\n", message);
2444   } else
2445     g_log_default_handler(log_domain, log_level, message, user_data);
2446 }
2447 #endif
2448
2449 #if GTK_MAJOR_VERSION < 2
2450 /* Given a font name, construct the name of the next heavier version of
2451    that font. */
2452
2453 #define XLFD_WEIGHT     3       /* index of the "weight" field */
2454
2455 /* Map from a given weight to the appropriate weight for the "bold"
2456    version of a font.
2457    XXX - the XLFD says these strings shouldn't be used for font matching;
2458    can we get the weight, as a number, from GDK, and ask GDK to find us
2459    a font just like the given font, but with the appropriate higher
2460    weight? */
2461 static const struct {
2462         char    *light;
2463         char    *heavier;
2464 } weight_map[] = {
2465         { "ultralight", "light" },
2466         { "extralight", "semilight" },
2467         { "light",      "medium" },
2468         { "semilight",  "semibold" },
2469         { "medium",     "bold" },
2470         { "normal",     "bold" },
2471         { "semibold",   "extrabold" },
2472         { "bold",       "ultrabold" }
2473 };
2474 #define N_WEIGHTS       (sizeof weight_map / sizeof weight_map[0])
2475
2476 char *
2477 boldify(const char *font_name)
2478 {
2479         char *bold_font_name;
2480         gchar **xlfd_tokens;
2481         unsigned int i;
2482
2483         /* Is this an XLFD font?  If it begins with "-", yes, otherwise no. */
2484         if (font_name[0] == '-') {
2485                 xlfd_tokens = g_strsplit(font_name, "-", XLFD_WEIGHT+1);
2486
2487                 /*
2488                  * Make sure we *have* a weight (this might not be a valid
2489                  * XLFD font name).
2490                  */
2491                 for (i = 0; i < XLFD_WEIGHT+1; i++) {
2492                         if (xlfd_tokens[i] == NULL) {
2493                                 /*
2494                                  * We don't, so treat this as a non-XLFD
2495                                  * font name.
2496                                  */
2497                                 goto not_xlfd;
2498                         }
2499                 }
2500                 for (i = 0; i < N_WEIGHTS; i++) {
2501                         if (strcmp(xlfd_tokens[XLFD_WEIGHT],
2502                             weight_map[i].light) == 0) {
2503                                 g_free(xlfd_tokens[XLFD_WEIGHT]);
2504                                 xlfd_tokens[XLFD_WEIGHT] =
2505                                     g_strdup(weight_map[i].heavier);
2506                                 break;
2507                         }
2508                 }
2509                 bold_font_name = g_strjoinv("-", xlfd_tokens);
2510                 g_strfreev(xlfd_tokens);
2511                 return bold_font_name;
2512         }
2513
2514 not_xlfd:
2515         /*
2516          * This isn't an XLFD font name; just append "bold" to the name
2517          * of the font.
2518          */
2519         bold_font_name = g_strconcat(font_name, "bold", NULL);
2520         return bold_font_name;
2521 }
2522 #endif
2523
2524 static void
2525 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
2526 {
2527     GtkWidget     *main_vbox, *menubar, *u_pane, *l_pane,
2528                   *stat_hbox, *column_lb,
2529                   *filter_bt, *filter_cm, *filter_te,
2530                   *filter_apply,
2531                   *filter_reset;
2532     GList         *filter_list = NULL;
2533     GtkAccelGroup *accel;
2534     GtkStyle      *win_style;
2535     GdkBitmap     *ascend_bm, *descend_bm;
2536     GdkPixmap     *ascend_pm, *descend_pm;
2537     column_arrows *col_arrows;
2538     int            i;
2539     /* Display filter construct dialog has an Apply button, and "OK" not
2540        only sets our text widget, it activates it (i.e., it causes us to
2541        filter the capture). */
2542     static construct_args_t args = {
2543         "Ethereal: Display Filter",
2544         TRUE,
2545         TRUE
2546     };
2547
2548     /* Main window */
2549     top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2550     gtk_widget_set_name(top_level, "main window");
2551     SIGNAL_CONNECT(top_level, "delete_event", main_window_delete_event_cb,
2552                    NULL);
2553     SIGNAL_CONNECT(top_level, "realize", window_icon_realize_cb, NULL);
2554     gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
2555     if (prefs->gui_geometry_save_position) {
2556         gtk_widget_set_uposition(GTK_WIDGET(top_level),
2557                                  prefs->gui_geometry_main_x,
2558                                  prefs->gui_geometry_main_y);
2559     }
2560     if (prefs->gui_geometry_save_size) {
2561         WIDGET_SET_SIZE(top_level, prefs->gui_geometry_main_width,
2562                         prefs->gui_geometry_main_height);
2563     } else {
2564         WIDGET_SET_SIZE(top_level, DEF_WIDTH, -1);
2565     }
2566     gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
2567
2568     /* Container for menu bar, paned windows and progress/info box */
2569     main_vbox = gtk_vbox_new(FALSE, 1);
2570     gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
2571     gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
2572     gtk_widget_show(main_vbox);
2573
2574     /* Menu bar */
2575     get_main_menu(&menubar, &accel);
2576     gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
2577     gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
2578     gtk_widget_show(menubar);
2579
2580     /* Panes for the packet list, tree, and byte view */
2581     u_pane = gtk_vpaned_new();
2582     gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
2583     l_pane = gtk_vpaned_new();
2584     gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
2585     gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
2586     gtk_widget_show(l_pane);
2587     gtk_paned_add2(GTK_PANED(u_pane), l_pane);
2588     gtk_widget_show(u_pane);
2589
2590     /* Packet list */
2591     pkt_scrollw = scrolled_window_new(NULL, NULL);
2592     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
2593                                     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2594     gtk_widget_show(pkt_scrollw);
2595     gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
2596
2597     packet_list = gtk_clist_new(cfile.cinfo.num_cols);
2598     /* Column titles are filled in below */
2599     gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
2600
2601     col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) *
2602                                             cfile.cinfo.num_cols);
2603
2604     set_plist_sel_browse(prefs->gui_plist_sel_browse);
2605     set_plist_font(m_r_font);
2606     gtk_widget_set_name(packet_list, "packet list");
2607     SIGNAL_CONNECT(packet_list, "click-column", packet_list_click_column_cb,
2608                    col_arrows);
2609     SIGNAL_CONNECT(packet_list, "select-row", packet_list_select_cb, NULL);
2610     SIGNAL_CONNECT(packet_list, "unselect-row", packet_list_unselect_cb, NULL);
2611     for (i = 0; i < cfile.cinfo.num_cols; i++) {
2612         if (get_column_resize_type(cfile.cinfo.col_fmt[i]) != RESIZE_MANUAL)
2613             gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
2614
2615         /* Right-justify the packet number column. */
2616         if (cfile.cinfo.col_fmt[i] == COL_NUMBER)
2617             gtk_clist_set_column_justification(GTK_CLIST(packet_list), i,
2618                                                GTK_JUSTIFY_RIGHT);
2619     }
2620     WIDGET_SET_SIZE(packet_list, -1, pl_size);
2621     SIGNAL_CONNECT(packet_list, "button_press_event", popup_menu_handler,
2622                    OBJECT_GET_DATA(popup_menu_object, PM_PACKET_LIST_KEY));
2623     SIGNAL_CONNECT(packet_list, "button_press_event",
2624                    packet_list_button_pressed_cb, NULL);
2625     gtk_clist_set_compare_func(GTK_CLIST(packet_list), packet_list_compare);
2626     gtk_widget_show(packet_list);
2627
2628     /* Tree view */
2629 #if GTK_MAJOR_VERSION < 2
2630     item_style = gtk_style_new();
2631     gdk_font_unref(item_style->font);
2632     item_style->font = m_r_font;
2633 #endif
2634     create_tree_view(tv_size, prefs, l_pane, &tv_scrollw, &tree_view);
2635 #if GTK_MAJOR_VERSION < 2
2636     SIGNAL_CONNECT(tree_view, "tree-select-row", tree_view_select_row_cb, NULL);
2637     SIGNAL_CONNECT(tree_view, "tree-unselect-row", tree_view_unselect_row_cb,
2638                    NULL);
2639 #else
2640     SIGNAL_CONNECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)),
2641                    "changed", tree_view_selection_changed_cb, NULL);
2642 #endif
2643     SIGNAL_CONNECT(tree_view, "button_press_event", popup_menu_handler,
2644                    OBJECT_GET_DATA(popup_menu_object, PM_TREE_VIEW_KEY));
2645     gtk_widget_show(tree_view);
2646
2647     /* Byte view. */
2648     byte_nb_ptr = create_byte_view(bv_size, l_pane);
2649
2650     SIGNAL_CONNECT(byte_nb_ptr, "button_press_event", popup_menu_handler,
2651                    OBJECT_GET_DATA(popup_menu_object, PM_HEXDUMP_KEY));
2652
2653     /* Filter/info box */
2654     stat_hbox = gtk_hbox_new(FALSE, 1);
2655     gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
2656     gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
2657     gtk_widget_show(stat_hbox);
2658
2659     filter_bt = gtk_button_new_with_label("Filter:");
2660     SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
2661     gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
2662     gtk_widget_show(filter_bt);
2663
2664     filter_cm = gtk_combo_new();
2665     filter_list = g_list_append (filter_list, "");
2666     gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
2667     gtk_combo_disable_activate(GTK_COMBO(filter_cm));
2668     gtk_combo_set_case_sensitive(GTK_COMBO(filter_cm), TRUE);
2669     OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list);
2670     filter_te = GTK_COMBO(filter_cm)->entry;
2671     OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_te);
2672     OBJECT_SET_DATA(filter_te, E_DFILTER_CM_KEY, filter_cm);
2673     gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
2674     SIGNAL_CONNECT(filter_te, "activate", filter_activate_cb, filter_te);
2675     gtk_widget_show(filter_cm);
2676
2677 #if GTK_MAJOR_VERSION < 2
2678     filter_reset = gtk_button_new_with_label("Reset");
2679 #else
2680     filter_reset = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
2681 #endif    
2682     OBJECT_SET_DATA(filter_reset, E_DFILTER_TE_KEY, filter_te);
2683     SIGNAL_CONNECT(filter_reset, "clicked", filter_reset_cb, NULL);
2684     gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
2685     gtk_widget_show(filter_reset);
2686
2687 #if GTK_MAJOR_VERSION < 2
2688     filter_apply = gtk_button_new_with_label("Apply");
2689 #else
2690     filter_apply = gtk_button_new_from_stock(GTK_STOCK_APPLY);
2691 #endif
2692     OBJECT_SET_DATA(filter_apply, E_DFILTER_CM_KEY, filter_cm);
2693     SIGNAL_CONNECT(filter_apply, "clicked", filter_activate_cb, filter_te);
2694     gtk_box_pack_start(GTK_BOX(stat_hbox), filter_apply, FALSE, TRUE, 1);
2695     gtk_widget_show(filter_apply);
2696
2697     /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
2698      * of any widget that ends up calling a callback which needs
2699      * that text entry pointer */
2700     set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
2701     set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
2702     set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
2703     set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY,
2704                          filter_te);
2705     set_menu_object_data("/Display/Match/Selected", E_DFILTER_TE_KEY,
2706                          filter_te);
2707     set_menu_object_data("/Display/Match/Not Selected", E_DFILTER_TE_KEY,
2708                          filter_te);
2709     set_menu_object_data("/Display/Match/And Selected", E_DFILTER_TE_KEY,
2710                          filter_te);
2711     set_menu_object_data("/Display/Match/Or Selected", E_DFILTER_TE_KEY,
2712                          filter_te);
2713     set_menu_object_data("/Display/Match/And Not Selected", E_DFILTER_TE_KEY,
2714                          filter_te);
2715     set_menu_object_data("/Display/Match/Or Not Selected", E_DFILTER_TE_KEY,
2716                          filter_te);
2717     set_menu_object_data("/Display/Prepare/Selected", E_DFILTER_TE_KEY,
2718                          filter_te);
2719     set_menu_object_data("/Display/Prepare/Not Selected", E_DFILTER_TE_KEY,
2720                          filter_te);
2721     set_menu_object_data("/Display/Prepare/And Selected", E_DFILTER_TE_KEY,
2722                          filter_te);
2723     set_menu_object_data("/Display/Prepare/Or Selected", E_DFILTER_TE_KEY,
2724                          filter_te);
2725     set_menu_object_data("/Display/Prepare/And Not Selected", E_DFILTER_TE_KEY,
2726                          filter_te);
2727     set_menu_object_data("/Display/Prepare/Or Not Selected", E_DFILTER_TE_KEY,
2728                          filter_te);
2729     OBJECT_SET_DATA(popup_menu_object, E_DFILTER_TE_KEY, filter_te);
2730     OBJECT_SET_DATA(popup_menu_object, E_MPACKET_LIST_KEY, packet_list);
2731
2732     info_bar = gtk_statusbar_new();
2733     main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
2734     file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
2735     help_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "help");
2736     gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
2737     gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
2738     gtk_widget_show(info_bar);
2739
2740     gtk_widget_show(top_level);
2741
2742     /* Fill in column titles.  This must be done after the top level window
2743        is displayed. */
2744     win_style = gtk_widget_get_style(top_level);
2745     ascend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &ascend_bm,
2746                                              &win_style->bg[GTK_STATE_NORMAL],
2747                                              (gchar **)clist_ascend_xpm);
2748     descend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &descend_bm,
2749                                               &win_style->bg[GTK_STATE_NORMAL],
2750                                               (gchar **)clist_descend_xpm);
2751     for (i = 0; i < cfile.cinfo.num_cols; i++) {
2752         col_arrows[i].table = gtk_table_new(2, 2, FALSE);
2753         gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
2754         column_lb = gtk_label_new(cfile.cinfo.col_title[i]);
2755         gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2,
2756                          GTK_SHRINK, GTK_SHRINK, 0, 0);
2757         gtk_widget_show(column_lb);
2758         col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
2759         gtk_table_attach(GTK_TABLE(col_arrows[i].table),
2760                          col_arrows[i].ascend_pm,
2761                          1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
2762         if (i == 0) {
2763             gtk_widget_show(col_arrows[i].ascend_pm);
2764         }
2765         col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
2766         gtk_table_attach(GTK_TABLE(col_arrows[i].table),
2767                          col_arrows[i].descend_pm,
2768                          1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
2769         gtk_clist_set_column_widget(GTK_CLIST(packet_list), i,
2770                                     col_arrows[i].table);
2771         gtk_widget_show(col_arrows[i].table);
2772     }
2773     gtk_clist_column_titles_show(GTK_CLIST(packet_list));
2774 }
2775
2776
2777 void
2778 set_last_open_dir(char *dirname)
2779 {
2780         int len;
2781
2782         if (last_open_dir) {
2783                 g_free(last_open_dir);
2784         }
2785
2786         if (dirname) {
2787                 len = strlen(dirname);
2788                 if (dirname[len-1] == G_DIR_SEPARATOR) {
2789                         last_open_dir = g_strconcat(dirname, NULL);
2790                 }
2791                 else {
2792                         last_open_dir = g_strconcat(dirname, G_DIR_SEPARATOR_S,
2793                                 NULL);
2794                 }
2795         }
2796         else {
2797                 last_open_dir = NULL;
2798         }
2799 }