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