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