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