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