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