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