cea03affc8116f6d99541d586931bf32b713505c
[obnox/wireshark/wip.git] / gtk / main.c
1 /* main.c
2  *
3  * $Id: main.c,v 1.103 2000/02/09 19:18:04 gram Exp $
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@zing.org>
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 #include <sys/types.h>
54 #include <sys/stat.h>
55
56 #ifdef HAVE_IO_H
57 #include <io.h> /* open/close on win32 */
58 #endif
59
60 #ifdef HAVE_DIRECT_H
61 #include <direct.h>
62 #endif
63
64 #ifdef HAVE_NETINET_IN_H
65 #include <netinet/in.h>
66 #endif
67
68 #include <signal.h>
69
70 #ifdef NEED_SNPRINTF_H
71 # ifdef HAVE_STDARG_H
72 #  include <stdarg.h>
73 # else
74 #  include <varargs.h>
75 # endif
76 # include "snprintf.h"
77 #endif
78
79 #if defined(HAVE_UCD_SNMP_SNMP_H)
80 #ifdef HAVE_UCD_SNMP_VERSION_H
81 #include <ucd-snmp/version.h>
82 #endif /* HAVE_UCD_SNMP_VERSION_H */
83 #elif defined(HAVE_SNMP_SNMP_H)
84 #ifdef HAVE_SNMP_VERSION_H
85 #include <snmp/version.h>
86 #endif /* HAVE_SNMP_VERSION_H */
87 #endif /* SNMP */
88
89 #ifdef NEED_STRERROR_H
90 #include "strerror.h"
91 #endif
92
93 #include "main.h"
94 #include "timestamp.h"
95 #include "packet.h"
96 #include "capture.h"
97 #include "summary.h"
98 #include "file.h"
99 #include "menu.h"
100 #include "../menu.h"
101 #include "prefs_dlg.h"
102 #include "column.h"
103 #include "print.h"
104 #include "resolv.h"
105 #include "follow.h"
106 #include "util.h"
107 #include "simple_dialog.h"
108 #include "proto_draw.h"
109 #include "dfilter.h"
110 #include "keys.h"
111 #include "gtkglobals.h"
112 #include "plugins.h"
113
114 FILE        *data_out_file = NULL;
115 packet_info  pi;
116 capture_file cf;
117 GtkWidget   *top_level, *file_sel, *packet_list, *tree_view, *byte_view,
118             *prog_bar, *info_bar, *tv_scrollw, *pkt_scrollw;
119 static GtkWidget        *bv_vscroll_left, *bv_vscroll_right;
120 GdkFont     *m_r_font, *m_b_font;
121 guint        main_ctx, file_ctx;
122 gchar        comp_info_str[256];
123 gchar       *ethereal_path = NULL;
124 gchar       *medium_font = MONO_MEDIUM_FONT;
125 gchar       *bold_font = MONO_BOLD_FONT;
126 gchar       *last_open_dir = NULL;
127
128 ts_type timestamp_type = RELATIVE;
129
130 GtkStyle *item_style;
131
132 /* Specifies the field currently selected in the GUI protocol tree */
133 field_info *finfo_selected = NULL;
134
135 static void follow_destroy_cb(GtkWidget *win, gpointer data);
136 static void follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w);
137 static void follow_load_text(GtkWidget *text, char *filename, guint8 show_type);
138 static void follow_print_stream(GtkWidget *w, gpointer parent_w);
139 static char* hfinfo_numeric_format(header_field_info *hfinfo);
140 static void create_main_window(gint, gint, gint, e_prefs*);
141
142 /* About Ethereal window */
143 void
144 about_ethereal( GtkWidget *w, gpointer data ) {
145   simple_dialog(ESD_TYPE_INFO, NULL,
146                 "Ethereal - Network Protocol Analyzer\n"
147                 "Version " VERSION " (C) 1998-2000 Gerald Combs <gerald@zing.org>\n"
148                 "Compiled with %s\n\n"
149
150                 "Check the man page for complete documentation and\n"
151                 "for the list of contributors.\n"
152
153                 "\nSee http://ethereal.zing.org/ for more information.",
154                  comp_info_str);
155 }
156
157 /* Follow the TCP stream, if any, to which the last packet that we called
158    a dissection routine on belongs (this might be the most recently
159    selected packet, or it might be the last packet in the file). */
160 void
161 follow_stream_cb( GtkWidget *w, gpointer data ) {
162   char      filename1[128+1];
163   GtkWidget *streamwindow, *box, *text, *vscrollbar, *table,
164                 *filter_te;
165   GtkWidget *hbox, *close_bt, *print_bt;
166   GtkWidget *b_ascii, *b_ebcdic, *b_hexdump;
167   int        tmp_fd;
168   gchar     *follow_filter;
169
170   if( pi.ipproto == 6 ) {
171     /* we got tcp so we can follow */
172     /* Create a temporary file into which to dump the reassembled data
173        from the TCP stream, and set "data_out_file" to refer to it, so
174        that the TCP code will write to it.
175
176        XXX - it might be nicer to just have the TCP code directly
177        append stuff to the text widget for the TCP stream window,
178        if we can arrange that said window not pop up until we're
179        done. */
180     tmp_fd = create_tempfile( filename1, sizeof filename1, "follow");
181     if (tmp_fd == -1) {
182       simple_dialog(ESD_TYPE_WARN, NULL,
183         "Could not create temporary file %s: %s", filename1, strerror(errno));
184       return;
185     }
186     data_out_file = fdopen( tmp_fd, "w" );
187     if( data_out_file == NULL ) {
188       simple_dialog(ESD_TYPE_WARN, NULL,
189         "Could not create temporary file %s: %s", filename1, strerror(errno));
190       close(tmp_fd);
191       unlink(filename1);
192       return;
193     }
194
195     /* Create a new filter that matches all packets in the TCP stream,
196        and set the display filter entry accordingly */
197     reset_tcp_reassembly();
198     follow_filter = build_follow_filter( &pi );
199
200     /* set the display filter entry accordingly */
201     filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
202     gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
203
204     /* Run the display filter so it goes in effect. */
205     filter_packets(&cf, follow_filter);
206
207     /* the data_out_file should now be full of the streams information */
208     fclose( data_out_file );
209
210     /* the filename1 file now has all the text that was in the session */
211     streamwindow = gtk_window_new( GTK_WINDOW_TOPLEVEL);
212     gtk_widget_set_name( streamwindow, "TCP stream window" );
213
214     gtk_signal_connect( GTK_OBJECT(streamwindow), "delete_event",
215                         GTK_SIGNAL_FUNC(follow_destroy_cb), NULL);
216     gtk_signal_connect( GTK_OBJECT(streamwindow), "destroy",
217                         GTK_SIGNAL_FUNC(follow_destroy_cb), NULL);
218                         
219     if( incomplete_tcp_stream ) {
220       gtk_window_set_title( GTK_WINDOW(streamwindow), 
221                             "Contents of TCP stream (incomplete)" );
222     } else {
223       gtk_window_set_title( GTK_WINDOW(streamwindow),
224                             "Contents of TCP stream" );
225     }
226     gtk_widget_set_usize( GTK_WIDGET(streamwindow), DEF_WIDTH, DEF_HEIGHT );
227     gtk_container_border_width( GTK_CONTAINER(streamwindow), 2 );
228
229     /* setup the container */
230     box = gtk_vbox_new( FALSE, 0 );
231     gtk_container_add( GTK_CONTAINER(streamwindow), box );
232     gtk_widget_show( box );
233
234     /* set up the table we attach to */
235     table = gtk_table_new( 1, 2, FALSE );
236     gtk_table_set_col_spacing( GTK_TABLE(table), 0, 2);
237     gtk_box_pack_start( GTK_BOX(box), table, TRUE, TRUE, 0 );
238     gtk_widget_show( table );
239
240     /* create a text box */
241     text = gtk_text_new( NULL, NULL );
242     gtk_text_set_editable( GTK_TEXT(text), FALSE);
243     gtk_table_attach( GTK_TABLE(table), text, 0, 1, 0, 1,
244                       GTK_EXPAND | GTK_SHRINK | GTK_FILL,
245                       GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
246     gtk_widget_show(text);
247
248     /* Create hbox */
249     hbox = gtk_hbox_new( FALSE, 1 );
250     gtk_box_pack_end( GTK_BOX(box), hbox, FALSE, FALSE, 0);
251     gtk_widget_show(hbox);
252
253 #define E_FOLLOW_ASCII_KEY "follow_ascii_key"
254 #define E_FOLLOW_EBCDIC_KEY "follow_ebcdic_key"
255 #define E_FOLLOW_HEXDUMP_KEY "follow_hexdump_key"
256
257     /* Create Radio Buttons */
258     b_ascii = gtk_radio_button_new_with_label(NULL, "ASCII");
259     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_ascii), TRUE);
260     gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_ASCII_KEY, b_ascii);
261     gtk_box_pack_start(GTK_BOX(hbox), b_ascii, FALSE, FALSE, 0);
262     gtk_signal_connect(GTK_OBJECT(b_ascii), "toggled",
263                     GTK_SIGNAL_FUNC(follow_charset_toggle_cb),
264                     GTK_OBJECT(streamwindow));
265     gtk_widget_show(b_ascii);
266
267     b_ebcdic = gtk_radio_button_new_with_label(
268                     gtk_radio_button_group(GTK_RADIO_BUTTON(b_ascii)),
269                     "EBCDIC");
270     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_ebcdic), FALSE);
271     gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_EBCDIC_KEY, b_ebcdic);
272     gtk_box_pack_start(GTK_BOX(hbox), b_ebcdic, FALSE, FALSE, 0);
273     gtk_signal_connect(GTK_OBJECT(b_ebcdic), "toggled",
274                     GTK_SIGNAL_FUNC(follow_charset_toggle_cb),
275                     GTK_OBJECT(streamwindow));
276     gtk_widget_show(b_ebcdic);
277
278     b_hexdump = gtk_radio_button_new_with_label(
279                     gtk_radio_button_group(GTK_RADIO_BUTTON(b_ascii)),
280                     "Hex. Dump");
281     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_hexdump), FALSE);
282     gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_HEXDUMP_KEY, b_hexdump);
283     gtk_box_pack_start(GTK_BOX(hbox), b_hexdump, FALSE, FALSE, 0);
284     gtk_signal_connect(GTK_OBJECT(b_hexdump), "toggled",
285                     GTK_SIGNAL_FUNC(follow_charset_toggle_cb),
286                     GTK_OBJECT(streamwindow));
287     gtk_widget_show(b_hexdump);
288
289     /* Create Close Button */
290     close_bt = gtk_button_new_with_label("Close");
291     gtk_signal_connect_object(GTK_OBJECT(close_bt), "clicked",
292                     GTK_SIGNAL_FUNC(gtk_widget_destroy),
293                     GTK_OBJECT(streamwindow));
294     gtk_box_pack_end( GTK_BOX(hbox), close_bt, FALSE, FALSE, 0);
295     gtk_widget_show( close_bt );
296
297     /* Create Print Button */
298     print_bt = gtk_button_new_with_label("Print");
299     gtk_signal_connect(GTK_OBJECT(print_bt), "clicked",
300                    GTK_SIGNAL_FUNC(follow_print_stream),
301                    GTK_OBJECT(streamwindow));
302     gtk_box_pack_end( GTK_BOX(hbox), print_bt, FALSE, FALSE, 0);
303     gtk_widget_show( print_bt );
304
305     /* create the scrollbar */
306     vscrollbar = gtk_vscrollbar_new( GTK_TEXT(text)->vadj );
307     gtk_table_attach( GTK_TABLE(table), vscrollbar, 1, 2, 0, 1,
308                       GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
309     gtk_widget_show( vscrollbar );
310     gtk_widget_realize( text );
311
312     /* Tuck away the filename and textbox into streamwindow */
313 #define E_FOLLOW_FILENAME_KEY "follow_filename_key"
314 #define E_FOLLOW_TEXT_KEY "follow_text_key"
315
316     gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_FILENAME_KEY,
317                     g_strdup(filename1));
318     gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_TEXT_KEY, text);
319
320     follow_load_text(text, filename1, 0);
321
322     data_out_file = NULL;
323     gtk_widget_show( streamwindow );
324   } else {
325     simple_dialog(ESD_TYPE_WARN, NULL,
326       "Error following stream.  Please make\n"
327       "sure you have a TCP packet selected.");
328   }
329 }
330
331 /* The destroy call back has the responsibility of
332  * unlinking the temporary file */
333 static void
334 follow_destroy_cb(GtkWidget *win, gpointer data)
335 {
336         char    *filename;
337
338         filename = (char*) gtk_object_get_data(GTK_OBJECT(win),
339                                                 E_FOLLOW_FILENAME_KEY);
340         g_assert(filename);
341
342         unlink(filename);
343         gtk_widget_destroy(win);
344 }
345
346 #define E_FOLLOW_ASCII_TYPE     0
347 #define E_FOLLOW_EBCDIC_TYPE    1
348 #define E_FOLLOW_HEXDUMP_TYPE   2
349
350 /* Handles the ASCII/EBCDIC toggling */
351 static void
352 follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w)
353 {
354         guint8          show_type = E_FOLLOW_ASCII_TYPE;
355         GtkWidget       *b_ascii, *b_ebcdic, *b_hexdump, *text;
356         char            *filename;
357
358         b_ascii = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
359                                                    E_FOLLOW_ASCII_KEY);
360         b_ebcdic = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
361                                                     E_FOLLOW_EBCDIC_KEY);
362         b_hexdump = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
363                                                      E_FOLLOW_HEXDUMP_KEY);
364         text = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
365                                                 E_FOLLOW_TEXT_KEY);
366         filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w),
367                                                 E_FOLLOW_FILENAME_KEY);
368
369         g_assert(b_ascii);
370         g_assert(b_ebcdic);
371         g_assert(b_hexdump);
372         g_assert(text);
373         g_assert(filename);
374
375         if (GTK_TOGGLE_BUTTON(b_ebcdic)->active)
376                 show_type = E_FOLLOW_EBCDIC_TYPE;
377         else if (GTK_TOGGLE_BUTTON(b_hexdump)->active)
378                 show_type = E_FOLLOW_HEXDUMP_TYPE;
379
380         follow_load_text(text, filename, show_type);
381 }
382
383 #define FLT_BUF_SIZE 1024
384 static void
385 follow_read_stream(char *filename, guint8 show_type,
386    void (*print_line)(char *, int, gboolean, void *), void *arg)
387 {
388   tcp_stream_chunk sc;
389   int bcount;
390   guint32 client_addr = 0;
391   guint16 client_port = 0;
392   gboolean is_server;
393   guint16 current_pos, global_client_pos = 0, global_server_pos = 0;
394   guint16 *global_pos;
395
396   data_out_file = fopen( filename, "r" );
397   if( data_out_file ) {
398     char buffer[FLT_BUF_SIZE];
399     int nchars;
400     while(fread(&sc.src_addr, 1, sizeof(sc), data_out_file)) {
401       if (client_addr == 0) {
402         client_addr = sc.src_addr;
403         client_port = sc.src_port;
404       }
405       if (client_addr == sc.src_addr && client_port == sc.src_port) {
406         is_server = FALSE;
407         global_pos = &global_client_pos;
408       }
409       else {
410         is_server = TRUE;
411         global_pos = &global_server_pos;
412       }
413         
414       while (sc.dlen > 0) {
415         bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
416         nchars = fread( buffer, 1, bcount, data_out_file );
417         if (nchars == 0)
418           break;
419         sc.dlen -= bcount;
420         switch (show_type) {
421         case E_FOLLOW_EBCDIC_TYPE:
422                 /* If our native arch is ASCII, call: */
423                 EBCDIC_to_ASCII(buffer, nchars);
424         case E_FOLLOW_ASCII_TYPE:
425                 /* If our native arch is EBCDIC, call:
426                  * ASCII_TO_EBCDIC(buffer, nchars);
427                  */
428                 (*print_line)( buffer, nchars, is_server, arg );
429                 break;
430         case E_FOLLOW_HEXDUMP_TYPE:
431                 current_pos = 0;
432                 while (current_pos < nchars)
433                 {
434                     gchar hexbuf[256];
435                     gchar hexchars[] = "0123456789abcdef";
436                     int i, cur;
437                     /* is_server indentation : put 63 spaces at the begenning
438                      * of the string */
439                     sprintf(hexbuf, is_server ?
440                             "                                 "
441                             "                              %08X  " :
442                             "%08X  ", *global_pos);
443                     cur = strlen(hexbuf);
444                     for (i=0; i < 16 && current_pos+i < nchars; i++) {
445                         hexbuf[cur++] = hexchars[(buffer[current_pos+i] & 0xf0) >> 4];
446                         hexbuf[cur++] = hexchars[buffer[current_pos+i] & 0x0f];
447                         if (i == 7) {
448                             hexbuf[cur++] = ' '; hexbuf[cur++] = ' ';
449                         }
450                         else if (i != 15)
451                             hexbuf[cur++] = ' ';
452                     }
453                     current_pos += i;
454                     (*global_pos) += i;
455                     hexbuf[cur++] = '\n';
456                     hexbuf[cur] = 0;
457                     (*print_line)( hexbuf, strlen(hexbuf), is_server, arg );
458                 }
459                 break;
460         }
461       }
462     }
463     if( ferror( data_out_file ) ) {
464       simple_dialog(ESD_TYPE_WARN, NULL,
465         "Error reading temporary file %s: %s", filename, strerror(errno));
466     }
467     fclose( data_out_file );
468     data_out_file = NULL;
469   } else {
470     simple_dialog(ESD_TYPE_WARN, NULL,
471       "Could not open temporary file %s: %s", filename, strerror(errno));
472   }
473 }
474
475 /*
476  * XXX - for text printing, we probably want to wrap lines at 80 characters;
477  * for PostScript printing, we probably want to wrap them at the appropriate
478  * width, and perhaps put some kind of dingbat (to use the technical term)
479  * to indicate a wrapped line, along the lines of what's done when displaying
480  * this in a window, as per Warren Young's suggestion.
481  *
482  * For now, we support only text printing.
483  */
484 static void
485 follow_print_text(char *buffer, int nchars, gboolean is_server, void *arg)
486 {
487   FILE *fh = arg;
488
489   fwrite(buffer, nchars, 1, fh);
490 }
491
492 static void
493 follow_print_stream(GtkWidget *w, gpointer parent_w)
494 {
495        FILE *fh;
496        gboolean to_file;
497        char* print_dest;
498        char* filename;
499        guint8 show_type = E_FOLLOW_ASCII_TYPE;
500        GtkWidget *button;
501
502        switch (prefs.pr_dest) {
503                case PR_DEST_CMD:
504                        print_dest = prefs.pr_cmd;
505                        to_file = FALSE;
506                        break;
507
508                case PR_DEST_FILE:
509                        print_dest = prefs.pr_file;
510                        to_file = TRUE;
511                        break;
512                default: /* "Can't happen" */
513                        simple_dialog(ESD_TYPE_WARN, NULL,
514                                "Couldn't figure out where to send the print "
515                                "job. Check your preferences.");
516                        return;
517        }
518
519        fh = open_print_dest(to_file, print_dest);
520        if (fh == NULL) {
521                switch (to_file) {
522                        case FALSE:
523                                simple_dialog(ESD_TYPE_WARN, NULL,
524                                                "Couldn't run print command %s.", prefs.pr_cmd);
525                                break;
526
527                        case TRUE:
528                                simple_dialog(ESD_TYPE_WARN, NULL, 
529                                                file_write_error_message(errno),
530                                                prefs.pr_file);
531                                break;
532                }
533                return;
534        }
535
536        button = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
537                        E_FOLLOW_EBCDIC_KEY);
538        if (GTK_TOGGLE_BUTTON(button)->active)
539                show_type = E_FOLLOW_EBCDIC_TYPE;
540        button = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
541                        E_FOLLOW_HEXDUMP_KEY);
542        if (GTK_TOGGLE_BUTTON(button)->active)
543                show_type = E_FOLLOW_HEXDUMP_TYPE;
544
545        filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w),
546                        E_FOLLOW_FILENAME_KEY);
547
548        if (filename != NULL) {
549                print_preamble(fh, PR_FMT_TEXT);
550                follow_read_stream(filename, show_type, follow_print_text, fh);
551                print_finale(fh, PR_FMT_TEXT);
552                close_print_dest(to_file, fh);
553        }
554        else {
555                simple_dialog(ESD_TYPE_WARN, NULL, "Could not find data to print.");
556        }
557 }
558
559 static void
560 follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server, void *arg)
561 {
562   GtkWidget *text = arg;
563
564   if (is_server)
565     gtk_text_insert( GTK_TEXT(text), m_r_font, &prefs.st_server_fg,
566             &prefs.st_server_bg, buffer, nchars );
567   else
568     gtk_text_insert( GTK_TEXT(text), m_r_font, &prefs.st_client_fg,
569             &prefs.st_client_bg, buffer, nchars );
570 }
571
572 static void
573 follow_load_text(GtkWidget *text, char *filename, guint8 show_type)
574 {
575   int bytes_already;
576
577   /* Delete any info already in text box */
578   bytes_already = gtk_text_get_length(GTK_TEXT(text));
579   if (bytes_already > 0) {
580     gtk_text_set_point(GTK_TEXT(text), 0);
581     gtk_text_forward_delete(GTK_TEXT(text), bytes_already);
582   }
583
584   /* stop the updates while we fill the text box */
585   gtk_text_freeze( GTK_TEXT(text) );
586   follow_read_stream(filename, show_type, follow_add_to_gtk_text, text);
587   gtk_text_thaw( GTK_TEXT(text) );
588 }
589
590 /* Match selected byte pattern */
591 void
592 match_selected_cb(GtkWidget *w, gpointer data)
593 {
594     char                *buf;
595     GtkWidget           *filter_te;
596     char                *ptr, *format, *stringified;
597     int                 i, dfilter_len, abbrev_len;
598     guint8              *c;
599     header_field_info   *hfinfo;
600
601     filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
602
603     if (!finfo_selected) {
604         simple_dialog(ESD_TYPE_WARN, NULL,
605                       "Error determining selected bytes.  Please make\n"
606                       "sure you have selected a field within the tree\n"
607                       "view to be matched.");
608         return;
609     }
610
611     hfinfo = finfo_selected->hfinfo;
612     g_assert(hfinfo);
613     abbrev_len = strlen(hfinfo->abbrev);
614
615         switch(hfinfo->type) {
616
617                 case FT_BOOLEAN:
618                         dfilter_len = abbrev_len + 2;
619                         buf = g_malloc0(dfilter_len);
620                         snprintf(buf, dfilter_len, "%s%s", finfo_selected->value.numeric ? "" : "!",
621                                         hfinfo->abbrev);
622                         break;
623
624                 case FT_UINT8:
625                 case FT_UINT16:
626                 case FT_UINT24:
627                 case FT_UINT32:
628                 case FT_INT8:
629                 case FT_INT16:
630                 case FT_INT24:
631                 case FT_INT32:
632                         dfilter_len = abbrev_len + 20;
633                         buf = g_malloc0(dfilter_len);
634                         format = hfinfo_numeric_format(hfinfo);
635                         snprintf(buf, dfilter_len, format, hfinfo->abbrev, finfo_selected->value.numeric);
636                         break;
637
638                 case FT_IPv4:
639                         dfilter_len = abbrev_len + 4 + 15 + 1;
640                         buf = g_malloc0(dfilter_len);
641                         snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
642                                         ipv4_addr_str(&(finfo_selected->value.ipv4)));
643                         break;
644
645                 case FT_IPXNET:
646                         dfilter_len = abbrev_len + 15;
647                         buf = g_malloc0(dfilter_len);
648                         snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev,
649                                         finfo_selected->value.numeric);
650                         break;
651
652                 case FT_IPv6:
653                         stringified = ip6_to_str((struct e_in6_addr*) &(finfo_selected->value.ipv6));
654                         dfilter_len = abbrev_len + 4 + strlen(stringified) + 1;
655                         buf = g_malloc0(dfilter_len);
656                         snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
657                                         stringified);
658                         break;
659
660                 case FT_DOUBLE:
661                         dfilter_len = abbrev_len + 30;
662                         buf = g_malloc0(dfilter_len);
663                         snprintf(buf, dfilter_len, "%s == %f", hfinfo->abbrev,
664                                         finfo_selected->value.floating);
665                         break;
666
667                 case FT_ETHER:
668                         dfilter_len = abbrev_len + 22;
669                         buf = g_malloc0(dfilter_len);
670                         snprintf(buf, dfilter_len, "%s == %s",
671                                         hfinfo->abbrev,
672                                         ether_to_str(finfo_selected->value.ether));
673                         break;
674 #if 0
675
676                 case FT_ABSOLUTE_TIME:
677                 case FT_RELATIVE_TIME:
678                         memcpy(&fi->value.time, va_arg(ap, struct timeval*),
679                                 sizeof(struct timeval));
680                         break;
681
682                 case FT_STRING:
683                         /* This g_strdup'ed memory is freed in proto_tree_free_node() */
684                         fi->value.string = g_strdup(va_arg(ap, char*));
685                         break;
686
687                 case FT_TEXT_ONLY:
688                         ; /* nothing */
689                         break;
690 #endif
691                 default:
692                     c = cf.pd + finfo_selected->start;
693                     buf = g_malloc0(32 + finfo_selected->length * 3);
694                     ptr = buf;
695
696                     sprintf(ptr, "frame[%d] == ", finfo_selected->start);
697                     ptr = buf+strlen(buf);
698
699                     if (finfo_selected->length == 1) {
700                         sprintf(ptr, "0x%02x", *c++);
701                     }
702                     else {
703                             for (i=0;i<finfo_selected->length; i++) {
704                                 if (i == 0 ) {
705                                         sprintf(ptr, "%02x", *c++);
706                                 }
707                                 else {
708                                         sprintf(ptr, ":%02x", *c++);
709                                 }
710                                 ptr = buf+strlen(buf);
711                             }
712                     }
713                     break;
714         }
715
716     /* create a new one and set the display filter entry accordingly */
717     gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
718
719     /* Run the display filter so it goes in effect. */
720     filter_packets(&cf, buf);
721
722     /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
723 }
724
725 static char*
726 hfinfo_numeric_format(header_field_info *hfinfo)
727 {
728         char *format = NULL;
729
730         /* Pick the proper format string */
731         switch(hfinfo->display) {
732                 case BASE_DEC:
733                 case BASE_NONE:
734                 case BASE_OCT: /* I'm lazy */
735                 case BASE_BIN: /* I'm lazy */
736                         switch(hfinfo->type) {
737                                 case FT_UINT8:
738                                 case FT_UINT16:
739                                 case FT_UINT24:
740                                 case FT_UINT32:
741                                         format = "%s == %u";
742                                         break;
743                                 case FT_INT8:
744                                 case FT_INT16:
745                                 case FT_INT24:
746                                 case FT_INT32:
747                                         format = "%s == %d";
748                                         break;
749                                 default:
750                                         g_assert_not_reached();
751                                         ;
752                         }
753                         break;
754                 case BASE_HEX:
755                         switch(hfinfo->type) {
756                                 case FT_UINT8:
757                                         format = "%s == 0x%02x";
758                                         break;
759                                 case FT_UINT16:
760                                         format = "%s == 0x%04x";
761                                         break;
762                                 case FT_UINT24:
763                                         format = "%s == 0x%06x";
764                                         break;
765                                 case FT_UINT32:
766                                         format = "%s == 0x%08x";
767                                         break;
768                                 default:
769                                         g_assert_not_reached();
770                                         ;
771                         }
772                         break;
773                 default:
774                         g_assert_not_reached();
775                         ;
776         }
777         return format;
778 }
779
780
781 /* Run the current display filter on the current packet set, and
782    redisplay. */
783 static void
784 filter_activate_cb(GtkWidget *w, gpointer data)
785 {
786   GtkCombo  *filter_cm = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_CM_KEY);
787   GList     *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY);
788   GList     *li, *nl = NULL;
789   gboolean   add_filter = TRUE;
790   
791   char *s = gtk_entry_get_text(GTK_ENTRY(w));
792   
793   /* GtkCombos don't let us get at their list contents easily, so we maintain
794      our own filter list, and feed it to gtk_combo_set_popdown_strings when
795      a new filter is added. */
796   if (filter_packets(&cf, g_strdup(s))) {
797     li = g_list_first(filter_list);
798     while (li) {
799       if (li->data && strcmp(s, li->data) == 0)
800         add_filter = FALSE;
801       li = li->next;
802     }
803
804     if (add_filter) {
805       filter_list = g_list_append(filter_list, g_strdup(s));
806       li = g_list_first(filter_list);
807       while (li) {
808         nl = g_list_append(nl, strdup(li->data));
809         li = li->next;
810       }
811       gtk_combo_set_popdown_strings(filter_cm, nl);
812       gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
813     }
814   }
815 }
816
817 /* redisplay with no display filter */
818 static void
819 filter_reset_cb(GtkWidget *w, gpointer data)
820 {
821   GtkWidget *filter_te = NULL;
822
823   if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
824     gtk_entry_set_text(GTK_ENTRY(filter_te), "");
825   }
826
827   filter_packets(&cf, NULL);
828 }
829
830 /* What to do when a list item is selected/unselected */
831 static void
832 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
833
834 #ifdef HAVE_LIBPCAP
835   if (!sync_mode) {
836 #endif
837     if (cf.wth)
838       return; 
839 #ifdef HAVE_LIBPCAP
840   }
841 #endif
842   blank_packetinfo();
843   select_packet(&cf, row);
844 }
845
846 static void
847 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
848   unselect_packet(&cf);
849 }
850
851 static void
852 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
853 {
854         field_info      *finfo;
855         int             tree_selected_start = -1;
856         int             tree_selected_len = -1;
857
858         g_assert(node);
859         finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
860         if (!finfo) return;
861
862         finfo_selected = finfo;
863         tree_selected_start = finfo->start;
864         tree_selected_len   = finfo->length;
865
866         packet_hex_print(GTK_TEXT(byte_view), cf.pd, cf.current_frame->cap_len, 
867                 tree_selected_start, tree_selected_len, cf.current_frame->encoding);
868 }
869
870 static void
871 tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
872 {
873         finfo_selected = NULL;
874         packet_hex_print(GTK_TEXT(byte_view), cf.pd, cf.current_frame->cap_len, 
875                 -1, -1, cf.current_frame->encoding);
876 }
877
878 void collapse_all_cb(GtkWidget *widget, gpointer data) {
879   if (cf.protocol_tree)
880     collapse_all_tree(cf.protocol_tree, tree_view);
881 }
882
883 void expand_all_cb(GtkWidget *widget, gpointer data) {
884   if (cf.protocol_tree)
885     expand_all_tree(cf.protocol_tree, tree_view);
886 }
887
888 void
889 set_scrollbar_placement(int pos) /* 0=left, 1=right */
890 {
891         if (pos) {
892                 gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(pkt_scrollw),
893                                 GTK_CORNER_TOP_LEFT );
894                 gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(tv_scrollw),
895                                 GTK_CORNER_TOP_LEFT );
896                 gtk_widget_hide(bv_vscroll_left);
897                 gtk_widget_show(bv_vscroll_right);
898         }
899         else {
900                 gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(pkt_scrollw),
901                                 GTK_CORNER_TOP_RIGHT );
902                 gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(tv_scrollw),
903                                 GTK_CORNER_TOP_RIGHT );
904                 gtk_widget_hide(bv_vscroll_right);
905                 gtk_widget_show(bv_vscroll_left);
906         }
907 }
908
909 void
910 set_plist_sel_browse(gboolean val)
911 {
912         if (finfo_selected)
913                 unselect_packet(&cf);
914
915         /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
916          * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
917         if (val) {
918                 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_SINGLE);
919         }
920         else {
921                 gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_BROWSE);
922         }
923 }
924
925 void
926 set_ptree_sel_browse(gboolean val)
927 {
928         /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
929          * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
930         if (val) {
931                 gtk_clist_set_selection_mode(GTK_CLIST(tree_view), GTK_SELECTION_SINGLE);
932         }
933         else {
934                 gtk_clist_set_selection_mode(GTK_CLIST(tree_view), GTK_SELECTION_BROWSE);
935         }
936 }
937
938 void
939 set_ptree_line_style(gint style)
940 {
941         /* I'm using an assert here since the preferences code limits
942          * the user input, both in the GUI and when reading the preferences file.
943          * If the value is incorrect, it's a program error, not a user-initiated error.
944          */
945         g_assert(style >= GTK_CTREE_LINES_NONE && style <= GTK_CTREE_LINES_TABBED);
946         gtk_ctree_set_line_style( GTK_CTREE(tree_view), style );
947 }
948
949 void
950 set_ptree_expander_style(gint style)
951 {
952         /* I'm using an assert here since the preferences code limits
953          * the user input, both in the GUI and when reading the preferences file.
954          * If the value is incorrect, it's a program error, not a user-initiated error.
955          */
956         g_assert(style >= GTK_CTREE_EXPANDER_NONE && style <= GTK_CTREE_EXPANDER_CIRCULAR);
957         gtk_ctree_set_expander_style( GTK_CTREE(tree_view), style );
958 }
959         
960
961 void
962 file_quit_cmd_cb (GtkWidget *widget, gpointer data) {
963   /* If we have a capture file open, and it's a temporary file,
964      unlink it. */
965   if (cf.filename != NULL && cf.is_tempfile)
966         unlink(cf.filename);
967   gtk_exit(0);
968 }
969
970 /* call initialization routines at program startup time */
971 static void
972 ethereal_proto_init(void) {
973   init_dissect_rpc();
974   proto_init();
975   init_dissect_udp();
976   dfilter_init();
977 #ifdef HAVE_PLUGINS
978   init_plugins();
979 #endif
980 }
981
982 static void
983 ethereal_proto_cleanup(void) {
984         proto_cleanup();
985         dfilter_cleanup();
986 }
987
988 static void 
989 print_usage(void) {
990
991   fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled with %s\n",
992           comp_info_str);
993 #ifdef HAVE_LIBPCAP
994   fprintf(stderr, "%s [ -vh ] [ -kQS ] [ -b <bold font> ] [ -B <byte view height> ]\n",
995           PACKAGE);
996   fprintf(stderr, "\t[ -c count ] [ -D ] [ -f <capture filter> ] [ -i interface ]\n");
997   fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -P <packet list height> ] [ -r infile ]\n");
998   fprintf(stderr, "\t[ -R <read filter> ] [ -s snaplen ] [ -t <time stamp format> ]\n");
999   fprintf(stderr, "\t[ -T <tree view height> ] [ -w savefile ]\n");
1000 #else
1001   fprintf(stderr, "%s [ -vh ] [ -b <bold font> ] [ -B <byte view height> ]\n",
1002           PACKAGE);
1003   fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -P <packet list height> ] [ -r infile ]\n");
1004   fprintf(stderr, "\t[ -R <read filter> ] [ -t <time stamp format> ]\n");
1005   fprintf(stderr, "\t[ -T <tree view height> ]\n");
1006 #endif
1007 }
1008
1009 /* And now our feature presentation... [ fade to music ] */
1010 int
1011 main(int argc, char *argv[])
1012 {
1013 #ifdef HAVE_LIBPCAP
1014   char                *command_name;
1015 #endif
1016   char                *s;
1017   int                  i;
1018 #ifndef WIN32
1019   int                  opt;
1020   extern char         *optarg;
1021   gboolean             arg_error = FALSE;
1022 #endif
1023 #ifdef HAVE_LIBPCAP
1024 #ifdef WIN32
1025   char pcap_version[] = "0.4a6";
1026 #else
1027   extern char          pcap_version[];
1028 #endif
1029 #endif
1030   char                *pf_path;
1031   int                  pf_open_errno = 0;
1032   int                  err;
1033 #ifdef HAVE_LIBPCAP
1034   gboolean             start_capture = FALSE;
1035   gchar               *save_file = NULL;
1036   GList               *if_list;
1037   gchar                err_str[PCAP_ERRBUF_SIZE];
1038 #else
1039   gboolean             capture_option_specified = FALSE;
1040 #endif
1041   gint                 pl_size = 280, tv_size = 95, bv_size = 75;
1042   gchar               *rc_file, *cf_name = NULL, *rfilter = NULL;
1043   dfilter             *rfcode = NULL;
1044   gboolean             rfilter_parse_failed = FALSE;
1045   e_prefs             *prefs;
1046
1047   ethereal_path = argv[0];
1048
1049 #ifdef HAVE_LIBPCAP
1050   command_name = get_basename(ethereal_path);
1051   /* Set "capture_child" to indicate whether this is going to be a child
1052      process for a "-S" capture. */
1053   capture_child = (strcmp(command_name, CHILD_NAME) == 0);
1054 #endif
1055
1056   /* If invoked with the "-G" flag, we dump out a glossary of
1057      display filter symbols.
1058
1059      We must do this before calling "gtk_init()", because "gtk_init()"
1060      tries to open an X display, and we don't want to have to do any X
1061      stuff just to do a build.
1062
1063      Given that we call "gtk_init()" before doing the regular argument
1064      list processing, so that it can handle X and GTK+ arguments and
1065      remove them from the list at which we look, this means we must do
1066      this before doing the regular argument list processing, as well.
1067
1068      This means that:
1069
1070         you must give the "-G" flag as the first flag on the command line;
1071
1072         you must give it as "-G", nothing more, nothing less;
1073
1074         any arguments after the "-G" flag will not be used. */
1075   if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
1076     ethereal_proto_init();
1077     proto_registrar_dump();
1078     exit(0);
1079   }
1080
1081   /* Let GTK get its args */
1082   gtk_init (&argc, &argv);
1083   
1084   prefs = read_prefs(&pf_path);
1085   if (pf_path != NULL) {
1086     /* The preferences file exists, but couldn't be opened; "pf_path" is
1087        its pathname.  Remember "errno", as that says why the attempt
1088        failed. */
1089     pf_open_errno = errno;
1090   }
1091
1092   /* Initialize the capture file struct */
1093   cf.plist              = NULL;
1094   cf.plist_end          = NULL;
1095   cf.wth                = NULL;
1096   cf.fh                 = NULL;
1097   cf.filename           = NULL;
1098   cf.user_saved         = FALSE;
1099   cf.is_tempfile        = FALSE;
1100   cf.rfcode             = NULL;
1101   cf.dfilter            = NULL;
1102   cf.dfcode             = NULL;
1103 #ifdef HAVE_LIBPCAP
1104   cf.cfilter            = g_strdup(EMPTY_FILTER);
1105 #endif
1106   cf.iface              = NULL;
1107   cf.save_file          = NULL;
1108   cf.save_file_fd       = -1;
1109   cf.snap               = WTAP_MAX_PACKET_SIZE;
1110   cf.count              = 0;
1111   cf.cinfo.num_cols     = prefs->num_cols;
1112   cf.cinfo.col_fmt      = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
1113   cf.cinfo.fmt_matx     = (gboolean **) g_malloc(sizeof(gboolean *) * cf.cinfo.num_cols);
1114   cf.cinfo.col_width    = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
1115   cf.cinfo.col_title    = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
1116   cf.cinfo.col_data     = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
1117
1118   /* Assemble the compile-time options */
1119   snprintf(comp_info_str, 256,
1120 #ifdef GTK_MAJOR_VERSION
1121     "GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
1122     GTK_MICRO_VERSION,
1123 #else
1124     "GTK+ (version unknown), %s%s, %s%s, %s%s",
1125 #endif
1126
1127 #ifdef HAVE_LIBPCAP
1128    "with libpcap ", pcap_version,
1129 #else
1130    "without libpcap", "",
1131 #endif
1132
1133 #ifdef HAVE_LIBZ
1134 #ifdef ZLIB_VERSION
1135    "with libz ", ZLIB_VERSION,
1136 #else /* ZLIB_VERSION */
1137    "with libz ", "(version unknown)",
1138 #endif /* ZLIB_VERSION */
1139 #else /* HAVE_LIBZ */
1140    "without libz", "",
1141 #endif /* HAVE_LIBZ */
1142
1143 /* Oh, this is pretty */
1144 #if defined(HAVE_UCD_SNMP_SNMP_H)
1145 #ifdef HAVE_UCD_SNMP_VERSION_H
1146    "with UCD SNMP ", VersionInfo
1147 #else /* HAVE_UCD_SNMP_VERSION_H */
1148    "with UCD SNMP ", "(version unknown)"
1149 #endif /* HAVE_UCD_SNMP_VERSION_H */
1150 #elif defined(HAVE_SNMP_SNMP_H)
1151 #ifdef HAVE_SNMP_VERSION_H
1152    "with CMU SNMP ", snmp_Version()
1153 #else /* HAVE_SNMP_VERSION_H */
1154    "with CMU SNMP ", "(version unknown)"
1155 #endif /* HAVE_SNMP_VERSION_H */
1156 #else /* no SNMP */
1157    "without SNMP", ""
1158 #endif
1159    );
1160
1161 #ifndef WIN32
1162   /* Now get our args */
1163   while ((opt = getopt(argc, argv, "b:B:c:Df:hi:km:nP:Qr:R:Ss:t:T:w:W:v")) != EOF) {
1164     switch (opt) {
1165       case 'b':        /* Bold font */
1166         bold_font = g_strdup(optarg);
1167         break;
1168       case 'B':        /* Byte view pane height */
1169         bv_size = atoi(optarg);
1170         break;
1171       case 'c':        /* Capture xxx packets */
1172 #ifdef HAVE_LIBPCAP
1173         cf.count = atoi(optarg);
1174 #else
1175         capture_option_specified = TRUE;
1176         arg_error = TRUE;
1177 #endif
1178         break;
1179       case 'D':        /* Turn off DSCP printing */
1180         g_ip_dscp_actif = FALSE;
1181         break;
1182       case 'f':
1183 #ifdef HAVE_LIBPCAP
1184         if (cf.cfilter)
1185                 g_free(cf.cfilter);
1186         cf.cfilter = g_strdup(optarg);
1187 #else
1188         capture_option_specified = TRUE;
1189         arg_error = TRUE;
1190 #endif
1191         break;
1192       case 'h':        /* Print help and exit */
1193         print_usage();
1194         exit(0);
1195         break;
1196       case 'i':        /* Use interface xxx */
1197 #ifdef HAVE_LIBPCAP
1198         cf.iface = g_strdup(optarg);
1199 #else
1200         capture_option_specified = TRUE;
1201         arg_error = TRUE;
1202 #endif
1203         break;
1204       case 'k':        /* Start capture immediately */
1205 #ifdef HAVE_LIBPCAP
1206         start_capture = TRUE;
1207 #else
1208         capture_option_specified = TRUE;
1209         arg_error = TRUE;
1210 #endif
1211         break;
1212       case 'm':        /* Medium font */
1213         medium_font = g_strdup(optarg);
1214         break;
1215       case 'n':        /* No name resolution */
1216         g_resolving_actif = 0;
1217         break;
1218       case 'P':        /* Packet list pane height */
1219         pl_size = atoi(optarg);
1220         break;
1221       case 'Q':        /* Quit after capture (just capture to file) */
1222 #ifdef HAVE_LIBPCAP
1223         quit_after_cap = 1;
1224         start_capture = TRUE;  /*** -Q implies -k !! ***/
1225 #else
1226         capture_option_specified = TRUE;
1227         arg_error = TRUE;
1228 #endif
1229         break;
1230       case 'r':        /* Read capture file xxx */
1231         /* We may set "last_open_dir" to "cf_name", and if we change
1232            "last_open_dir" later, we free the old value, so we have to
1233            set "cf_name" to something that's been allocated. */
1234         cf_name = g_strdup(optarg);
1235         break;
1236       case 'R':        /* Read file filter */
1237         rfilter = optarg;
1238         break;
1239       case 's':        /* Set the snapshot (capture) length */
1240 #ifdef HAVE_LIBPCAP
1241         cf.snap = atoi(optarg);
1242 #else
1243         capture_option_specified = TRUE;
1244         arg_error = TRUE;
1245 #endif
1246         break;
1247       case 'S':        /* "Sync" mode: used for following file ala tail -f */
1248 #ifdef HAVE_LIBPCAP
1249         sync_mode = TRUE;
1250 #else
1251         capture_option_specified = TRUE;
1252         arg_error = TRUE;
1253 #endif
1254         break;
1255       case 't':        /* Time stamp type */
1256         if (strcmp(optarg, "r") == 0)
1257           timestamp_type = RELATIVE;
1258         else if (strcmp(optarg, "a") == 0)
1259           timestamp_type = ABSOLUTE;
1260         else if (strcmp(optarg, "d") == 0)
1261           timestamp_type = DELTA;
1262         else {
1263           fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
1264             optarg);
1265           fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
1266           fprintf(stderr, "or \"d\" for delta.\n");
1267           exit(1);
1268         }
1269         break;
1270       case 'T':        /* Tree view pane height */
1271         tv_size = atoi(optarg);
1272         break;
1273       case 'v':        /* Show version and exit */
1274         printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
1275         exit(0);
1276         break;
1277       case 'w':        /* Write to capture file xxx */
1278 #ifdef HAVE_LIBPCAP
1279         save_file = g_strdup(optarg);
1280 #else
1281         capture_option_specified = TRUE;
1282         arg_error = TRUE;
1283 #endif
1284         break;
1285       case 'W':        /* Write to capture file FD xxx */
1286 #ifdef HAVE_LIBPCAP
1287         cf.save_file_fd = atoi(optarg);
1288 #else
1289         capture_option_specified = TRUE;
1290         arg_error = TRUE;
1291 #endif
1292         break;
1293       default:
1294       case '?':        /* Bad flag - print usage message */
1295         arg_error = TRUE;
1296         break;
1297     }
1298   }
1299 #endif
1300
1301 #ifndef HAVE_LIBPCAP
1302   if (capture_option_specified)
1303     fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
1304 #endif
1305 #ifndef WIN32
1306   if (arg_error)
1307     print_usage();
1308 #endif
1309 #ifdef HAVE_LIBPCAP
1310   if (start_capture) {
1311     /* We're supposed to do a live capture; did the user specify an interface
1312        to use? */
1313     if (cf.iface == NULL) {
1314       /* No - pick the first one from the list of interfaces. */
1315       if_list = get_interface_list(&err, err_str);
1316       if (if_list == NULL) {
1317         switch (err) {
1318
1319         case CANT_GET_INTERFACE_LIST:
1320             fprintf(stderr, "ethereal: Can't get list of interfaces: %s\n",
1321                         err_str);
1322             break;
1323
1324         case NO_INTERFACES_FOUND:
1325             fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
1326             break;
1327         }
1328         exit(2);
1329       }
1330       cf.iface = g_strdup(if_list->data);       /* first interface */
1331       free_interface_list(if_list);
1332     }
1333   }
1334   if (capture_child) {
1335     if (cf.save_file_fd == -1) {
1336       /* XXX - send this to the standard output as something our parent
1337          should put in an error message box? */
1338       fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
1339       exit(1);
1340     }
1341   }
1342 #endif
1343
1344   /* Build the column format array */  
1345   for (i = 0; i < cf.cinfo.num_cols; i++) {
1346     cf.cinfo.col_fmt[i] = get_column_format(i);
1347     cf.cinfo.col_title[i] = g_strdup(get_column_title(i));
1348     cf.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
1349       NUM_COL_FMTS);
1350     get_column_format_matches(cf.cinfo.fmt_matx[i], cf.cinfo.col_fmt[i]);
1351     if (cf.cinfo.col_fmt[i] == COL_INFO)
1352       cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
1353     else
1354       cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
1355   }
1356
1357   if (cf.snap < 1)
1358     cf.snap = WTAP_MAX_PACKET_SIZE;
1359   else if (cf.snap < MIN_PACKET_SIZE)
1360     cf.snap = MIN_PACKET_SIZE;
1361   
1362   rc_file = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(RC_FILE) + 4);
1363   sprintf(rc_file, "%s/%s", get_home_dir(), RC_FILE);
1364   gtk_rc_parse(rc_file);
1365
1366   if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
1367     fprintf(stderr, "ethereal: Error font %s not found (use -m option)\n", medium_font);
1368     exit(1);
1369   }
1370
1371   if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
1372     fprintf(stderr, "ethereal: Error font %s not found (use -b option)\n", bold_font);
1373     exit(1);
1374   }
1375
1376   create_main_window(pl_size, tv_size, bv_size, prefs);
1377
1378 /* 
1379    Hmmm should we do it here
1380 */
1381
1382   ethereal_proto_init();   /* Init anything that needs initializing */
1383
1384 #ifdef HAVE_LIBPCAP
1385   /* Is this a "child" ethereal, which is only supposed to pop up a
1386      capture box to let us stop the capture, and run a capture
1387      to a file that our parent will read? */
1388   if (!capture_child) {
1389 #endif
1390     /* No.  Pop up the main window, and read in a capture file if
1391        we were told to. */
1392
1393     gtk_widget_show(top_level);
1394     set_menus_for_capture_file(FALSE);
1395
1396     cf.colors = colfilter_new();
1397
1398     /* If we were given the name of a capture file, read it in now;
1399        we defer it until now, so that, if we can't open it, and pop
1400        up an alert box, the alert box is more likely to come up on
1401        top of the main window - but before the preference-file-error
1402        alert box, so, if we get one of those, it's more likely to come
1403        up on top of us. */
1404     if (cf_name) {
1405       if (rfilter != NULL) {
1406         if (dfilter_compile(rfilter, &rfcode) != 0) {
1407           simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
1408           rfilter_parse_failed = TRUE;
1409         }
1410       }
1411       if (!rfilter_parse_failed) {
1412         if ((err = open_cap_file(cf_name, FALSE, &cf)) == 0) {
1413           /* "open_cap_file()" succeeded, so it closed the previous
1414              capture file, and thus destroyed any previous read filter
1415              attached to "cf". */
1416           cf.rfcode = rfcode;
1417           err = read_cap_file(&cf);
1418           /* Save the name of the containing directory specified in the
1419              path name, if any; we can write over cf_name, which is a
1420              good thing, given that "get_dirname()" does write over its
1421              argument. */
1422           s = get_dirname(cf_name);
1423           if (s != NULL)
1424             last_open_dir = s;
1425         } else {
1426           dfilter_destroy(rfcode);
1427           cf.rfcode = NULL;
1428         }
1429       }
1430     }
1431 #ifdef HAVE_LIBPCAP
1432   }
1433 #endif
1434
1435   /* If we failed to open the preferences file, pop up an alert box;
1436      we defer it until now, so that the alert box is more likely to
1437      come up on top of the main window. */
1438   if (pf_path != NULL) {
1439       simple_dialog(ESD_TYPE_WARN, NULL,
1440         "Could not open preferences file\n\"%s\": %s.", pf_path,
1441         strerror(pf_open_errno));
1442   }
1443
1444 #ifdef HAVE_LIBPCAP
1445   if (capture_child) {
1446     /* This is the child process for a sync mode or fork mode capture,
1447        so just do the low-level work of a capture - don't create
1448        a temporary file and fork off *another* child process (so don't
1449        call "do_capture()"). */
1450
1451        capture();
1452
1453        /* The capture is done; there's nothing more for us to do. */
1454        gtk_exit(0);
1455   } else {
1456     if (start_capture) {
1457       /* "-k" was specified; start a capture. */
1458       do_capture(save_file);
1459     }
1460     else {
1461             set_menus_for_capture_in_progress(FALSE);
1462     }
1463   }
1464 #else
1465   set_menus_for_capture_in_progress(FALSE);
1466 #endif
1467
1468   gtk_main();
1469
1470   ethereal_proto_cleanup();
1471   g_free(rc_file);
1472
1473   exit(0);
1474 }
1475
1476 static void
1477 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
1478 {
1479   GtkWidget           *main_vbox, *menubar, *u_pane, *l_pane,
1480                       *bv_table, *stat_hbox,
1481                       *filter_bt, *filter_cm, *filter_te,
1482                       *filter_reset;
1483   GList               *filter_list = NULL;
1484   GtkStyle            *pl_style;
1485   GtkAccelGroup       *accel;
1486   int                   i;
1487
1488   /* Main window */  
1489   top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1490   gtk_widget_set_name(top_level, "main window");
1491   gtk_signal_connect(GTK_OBJECT(top_level), "delete_event",
1492     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
1493   gtk_signal_connect(GTK_OBJECT(top_level), "destroy", 
1494     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
1495   gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
1496   gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
1497   gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
1498
1499   /* Container for menu bar, paned windows and progress/info box */
1500   main_vbox = gtk_vbox_new(FALSE, 1);
1501   gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
1502   gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
1503   gtk_widget_show(main_vbox);
1504
1505   /* Menu bar */
1506   get_main_menu(&menubar, &accel);
1507   gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
1508   gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
1509   gtk_widget_show(menubar);
1510
1511   /* Panes for the packet list, tree, and byte view */
1512   u_pane = gtk_vpaned_new();
1513   gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
1514   l_pane = gtk_vpaned_new();
1515   gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
1516   gtk_container_add(GTK_CONTAINER(main_vbox), l_pane);
1517   gtk_widget_show(u_pane);
1518   gtk_paned_add1 (GTK_PANED(l_pane), u_pane);
1519   gtk_widget_show(l_pane);
1520
1521   /* Packet list */
1522   pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
1523   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
1524     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1525   gtk_widget_show(pkt_scrollw);
1526   gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
1527
1528   packet_list = gtk_clist_new_with_titles(cf.cinfo.num_cols, cf.cinfo.col_title);
1529   gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
1530   gtk_clist_column_titles_passive(GTK_CLIST(packet_list));
1531   set_plist_sel_browse(prefs->gui_plist_sel_browse);
1532   pl_style = gtk_style_new();
1533   gdk_font_unref(pl_style->font);
1534   pl_style->font = m_r_font;
1535   gtk_widget_set_style(packet_list, pl_style);
1536   gtk_widget_set_name(packet_list, "packet list");
1537   gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
1538     GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
1539   gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
1540     GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
1541   for (i = 0; i < cf.cinfo.num_cols; i++) {
1542     if (get_column_resize_type(cf.cinfo.col_fmt[i]) != RESIZE_MANUAL)
1543       gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
1544
1545     /* Right-justify the packet number column. */
1546     if (cf.cinfo.col_fmt[i] == COL_NUMBER)
1547       gtk_clist_set_column_justification(GTK_CLIST(packet_list), i, 
1548         GTK_JUSTIFY_RIGHT);
1549
1550     /* Save static column sizes to use during a "-S" capture, so that
1551        the columns don't resize during a live capture. */
1552     cf.cinfo.col_width[i] = gdk_string_width(pl_style->font,
1553                                 get_column_longest_string(get_column_format(i)));
1554   }
1555   gtk_widget_set_usize(packet_list, -1, pl_size);
1556   gtk_signal_connect_object(GTK_OBJECT(packet_list), "button_press_event",
1557     GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
1558   gtk_widget_show(packet_list);
1559   
1560   /* Tree view */
1561   tv_scrollw = gtk_scrolled_window_new(NULL, NULL);
1562   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(tv_scrollw),
1563     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1564   gtk_paned_add2(GTK_PANED(u_pane), tv_scrollw);
1565   gtk_widget_set_usize(tv_scrollw, -1, tv_size);
1566   gtk_widget_show(tv_scrollw);
1567   
1568   tree_view = gtk_ctree_new(1, 0);
1569   /* I need this next line to make the widget work correctly with hidden
1570    * column titles and GTK_SELECTION_BROWSE */
1571   gtk_clist_set_column_auto_resize( GTK_CLIST(tree_view), 0, TRUE );
1572   gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1573   set_ptree_sel_browse(prefs->gui_ptree_sel_browse);
1574   set_ptree_line_style(prefs->gui_ptree_line_style);
1575   set_ptree_expander_style(prefs->gui_ptree_expander_style);
1576
1577   gtk_signal_connect(GTK_OBJECT(tree_view), "tree-select-row",
1578     GTK_SIGNAL_FUNC(tree_view_select_row_cb), NULL);
1579   gtk_signal_connect(GTK_OBJECT(tree_view), "tree-unselect-row",
1580     GTK_SIGNAL_FUNC(tree_view_unselect_row_cb), NULL);
1581   gtk_signal_connect_object(GTK_OBJECT(tree_view), "button_press_event",
1582     GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY));
1583   gtk_widget_show(tree_view);
1584
1585   item_style = gtk_style_new();
1586   gdk_font_unref(item_style->font);
1587   item_style->font = m_r_font;
1588
1589   /* Byte view. The table is only one row high, but 3 columns
1590    * wide. The middle column contains the GtkText with the hex dump.
1591    * The left and right columns contain vertical scrollbars. They both
1592    * do the same thing, but only one will be shown at a time, in accordance
1593    * with where the user wants the other vertical scrollbars places
1594    * (on the left or the right).
1595    */
1596   bv_table = gtk_table_new (1, 3, FALSE);
1597   gtk_paned_pack2(GTK_PANED(l_pane), bv_table, FALSE, FALSE);
1598   gtk_widget_set_usize(bv_table, -1, bv_size);
1599   gtk_widget_show(bv_table);
1600
1601   byte_view = gtk_text_new(NULL, NULL);
1602   gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
1603   gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
1604   gtk_table_attach (GTK_TABLE (bv_table), byte_view, 1, 2, 0, 1,
1605     GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
1606   gtk_widget_show(byte_view);
1607
1608   /* The gtk_text widget doesn't scroll horizontally (see gtktext.c)
1609    * in the GTK+ distribution, so I removed the horizontal scroll bar
1610    * that used to be here. I tried the gtk_text widget with a 
1611    * gtk_scrolled_window w/ viewport, but the vertical scrollowing
1612    * did not work well, and sometimes a few pixels were cut off on
1613    * the bottom. */
1614
1615   bv_vscroll_left = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
1616   gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll_left, 0, 1, 0, 1,
1617     GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
1618
1619   bv_vscroll_right = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
1620   gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll_right, 2, 3, 0, 1,
1621     GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
1622  
1623   /* Now that the 3 panes are created, set the vertical scrollbar
1624    * on the left or right according to the user's preference */
1625   set_scrollbar_placement(prefs->gui_scrollbar_on_right);
1626
1627   /* Progress/filter/info box */
1628   stat_hbox = gtk_hbox_new(FALSE, 1);
1629   gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
1630   gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
1631   gtk_widget_show(stat_hbox);
1632
1633   prog_bar = gtk_progress_bar_new();
1634   gtk_box_pack_start(GTK_BOX(stat_hbox), prog_bar, FALSE, TRUE, 3);
1635   gtk_widget_show(prog_bar);
1636
1637   filter_bt = gtk_button_new_with_label("Filter:");
1638   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
1639     GTK_SIGNAL_FUNC(filter_dialog_cb), NULL);
1640   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
1641   gtk_widget_show(filter_bt);
1642   
1643   filter_cm = gtk_combo_new();
1644   filter_list = g_list_append (filter_list, "");
1645   gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
1646   gtk_combo_disable_activate(GTK_COMBO(filter_cm));
1647   filter_te = GTK_COMBO(filter_cm)->entry;
1648   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
1649   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
1650   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
1651   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
1652   gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
1653     GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
1654   gtk_widget_show(filter_cm);
1655
1656   filter_reset = gtk_button_new_with_label("Reset");
1657   gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
1658   gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
1659                      GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
1660   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
1661   gtk_widget_show(filter_reset);
1662
1663   /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
1664    * of any widget that ends up calling a callback which needs
1665    * that text entry pointer */
1666   set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
1667   set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
1668   set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
1669   set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
1670   set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
1671
1672   info_bar = gtk_statusbar_new();
1673   main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
1674   file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
1675   gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
1676   gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
1677   gtk_widget_show(info_bar);
1678 }