When exiting Ethereal, don't just unlink any temporary capture file,
[obnox/wireshark/wip.git] / gtk / main.c
1 /* main.c
2  *
3  * $Id: main.c,v 1.105 2000/02/13 10:36:06 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 {
965         /* Close any capture file we have open; on some OSes, you can't
966            unlink a temporary capture file if you have it open.
967            "close_cap_file()" will unlink it after closing it if
968            it's a temporary file. */
969         close_cap_file(&cf, info_bar);
970         gtk_exit(0);
971 }
972
973 /* call initialization routines at program startup time */
974 static void
975 ethereal_proto_init(void) {
976   init_dissect_rpc();
977   proto_init();
978   init_dissect_udp();
979   dfilter_init();
980 #ifdef HAVE_PLUGINS
981   init_plugins();
982 #endif
983 }
984
985 static void
986 ethereal_proto_cleanup(void) {
987         proto_cleanup();
988         dfilter_cleanup();
989 }
990
991 static void 
992 print_usage(void) {
993
994   fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled with %s\n",
995           comp_info_str);
996 #ifdef HAVE_LIBPCAP
997   fprintf(stderr, "%s [ -vh ] [ -kQS ] [ -b <bold font> ] [ -B <byte view height> ]\n",
998           PACKAGE);
999   fprintf(stderr, "\t[ -c count ] [ -D ] [ -f <capture filter> ] [ -i interface ]\n");
1000   fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -P <packet list height> ] [ -r infile ]\n");
1001   fprintf(stderr, "\t[ -R <read filter> ] [ -s snaplen ] [ -t <time stamp format> ]\n");
1002   fprintf(stderr, "\t[ -T <tree view height> ] [ -w savefile ]\n");
1003 #else
1004   fprintf(stderr, "%s [ -vh ] [ -b <bold font> ] [ -B <byte view height> ]\n",
1005           PACKAGE);
1006   fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -P <packet list height> ] [ -r infile ]\n");
1007   fprintf(stderr, "\t[ -R <read filter> ] [ -t <time stamp format> ]\n");
1008   fprintf(stderr, "\t[ -T <tree view height> ]\n");
1009 #endif
1010 }
1011
1012 /* And now our feature presentation... [ fade to music ] */
1013 int
1014 main(int argc, char *argv[])
1015 {
1016 #ifdef HAVE_LIBPCAP
1017   char                *command_name;
1018 #endif
1019   char                *s;
1020   int                  i;
1021 #ifndef WIN32
1022   int                  opt;
1023   extern char         *optarg;
1024   gboolean             arg_error = FALSE;
1025 #endif
1026 #ifdef HAVE_LIBPCAP
1027 #ifdef WIN32
1028   char pcap_version[] = "0.4a6";
1029 #else
1030   extern char          pcap_version[];
1031 #endif
1032 #endif
1033   char                *pf_path;
1034   int                  pf_open_errno = 0;
1035   int                  err;
1036 #ifdef HAVE_LIBPCAP
1037   gboolean             start_capture = FALSE;
1038   gchar               *save_file = NULL;
1039   GList               *if_list;
1040   gchar                err_str[PCAP_ERRBUF_SIZE];
1041 #else
1042   gboolean             capture_option_specified = FALSE;
1043 #endif
1044   gint                 pl_size = 280, tv_size = 95, bv_size = 75;
1045   gchar               *rc_file, *cf_name = NULL, *rfilter = NULL;
1046   dfilter             *rfcode = NULL;
1047   gboolean             rfilter_parse_failed = FALSE;
1048   e_prefs             *prefs;
1049
1050   ethereal_path = argv[0];
1051
1052 #ifdef HAVE_LIBPCAP
1053   command_name = get_basename(ethereal_path);
1054   /* Set "capture_child" to indicate whether this is going to be a child
1055      process for a "-S" capture. */
1056   capture_child = (strcmp(command_name, CHILD_NAME) == 0);
1057 #endif
1058
1059   /* If invoked with the "-G" flag, we dump out a glossary of
1060      display filter symbols.
1061
1062      We must do this before calling "gtk_init()", because "gtk_init()"
1063      tries to open an X display, and we don't want to have to do any X
1064      stuff just to do a build.
1065
1066      Given that we call "gtk_init()" before doing the regular argument
1067      list processing, so that it can handle X and GTK+ arguments and
1068      remove them from the list at which we look, this means we must do
1069      this before doing the regular argument list processing, as well.
1070
1071      This means that:
1072
1073         you must give the "-G" flag as the first flag on the command line;
1074
1075         you must give it as "-G", nothing more, nothing less;
1076
1077         any arguments after the "-G" flag will not be used. */
1078   if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
1079     ethereal_proto_init();
1080     proto_registrar_dump();
1081     exit(0);
1082   }
1083
1084   /* Let GTK get its args */
1085   gtk_init (&argc, &argv);
1086   
1087   prefs = read_prefs(&pf_path);
1088   if (pf_path != NULL) {
1089     /* The preferences file exists, but couldn't be opened; "pf_path" is
1090        its pathname.  Remember "errno", as that says why the attempt
1091        failed. */
1092     pf_open_errno = errno;
1093   }
1094
1095   /* Initialize the capture file struct */
1096   cf.plist              = NULL;
1097   cf.plist_end          = NULL;
1098   cf.wth                = NULL;
1099   cf.fh                 = NULL;
1100   cf.filename           = NULL;
1101   cf.user_saved         = FALSE;
1102   cf.is_tempfile        = FALSE;
1103   cf.rfcode             = NULL;
1104   cf.dfilter            = NULL;
1105   cf.dfcode             = NULL;
1106 #ifdef HAVE_LIBPCAP
1107   cf.cfilter            = g_strdup(EMPTY_FILTER);
1108 #endif
1109   cf.iface              = NULL;
1110   cf.save_file          = NULL;
1111   cf.save_file_fd       = -1;
1112   cf.snap               = WTAP_MAX_PACKET_SIZE;
1113   cf.count              = 0;
1114   cf.cinfo.num_cols     = prefs->num_cols;
1115   cf.cinfo.col_fmt      = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
1116   cf.cinfo.fmt_matx     = (gboolean **) g_malloc(sizeof(gboolean *) * cf.cinfo.num_cols);
1117   cf.cinfo.col_width    = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
1118   cf.cinfo.col_title    = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
1119   cf.cinfo.col_data     = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
1120
1121   /* Assemble the compile-time options */
1122   snprintf(comp_info_str, 256,
1123 #ifdef GTK_MAJOR_VERSION
1124     "GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
1125     GTK_MICRO_VERSION,
1126 #else
1127     "GTK+ (version unknown), %s%s, %s%s, %s%s",
1128 #endif
1129
1130 #ifdef HAVE_LIBPCAP
1131    "with libpcap ", pcap_version,
1132 #else
1133    "without libpcap", "",
1134 #endif
1135
1136 #ifdef HAVE_LIBZ
1137 #ifdef ZLIB_VERSION
1138    "with libz ", ZLIB_VERSION,
1139 #else /* ZLIB_VERSION */
1140    "with libz ", "(version unknown)",
1141 #endif /* ZLIB_VERSION */
1142 #else /* HAVE_LIBZ */
1143    "without libz", "",
1144 #endif /* HAVE_LIBZ */
1145
1146 /* Oh, this is pretty */
1147 #if defined(HAVE_UCD_SNMP_SNMP_H)
1148 #ifdef HAVE_UCD_SNMP_VERSION_H
1149    "with UCD SNMP ", VersionInfo
1150 #else /* HAVE_UCD_SNMP_VERSION_H */
1151    "with UCD SNMP ", "(version unknown)"
1152 #endif /* HAVE_UCD_SNMP_VERSION_H */
1153 #elif defined(HAVE_SNMP_SNMP_H)
1154 #ifdef HAVE_SNMP_VERSION_H
1155    "with CMU SNMP ", snmp_Version()
1156 #else /* HAVE_SNMP_VERSION_H */
1157    "with CMU SNMP ", "(version unknown)"
1158 #endif /* HAVE_SNMP_VERSION_H */
1159 #else /* no SNMP */
1160    "without SNMP", ""
1161 #endif
1162    );
1163
1164 #ifndef WIN32
1165   /* Now get our args */
1166   while ((opt = getopt(argc, argv, "b:B:c:Df:hi:km:nP:Qr:R:Ss:t:T:w:W:v")) != EOF) {
1167     switch (opt) {
1168       case 'b':        /* Bold font */
1169         bold_font = g_strdup(optarg);
1170         break;
1171       case 'B':        /* Byte view pane height */
1172         bv_size = atoi(optarg);
1173         break;
1174       case 'c':        /* Capture xxx packets */
1175 #ifdef HAVE_LIBPCAP
1176         cf.count = atoi(optarg);
1177 #else
1178         capture_option_specified = TRUE;
1179         arg_error = TRUE;
1180 #endif
1181         break;
1182       case 'D':        /* Turn off DSCP printing */
1183         g_ip_dscp_actif = FALSE;
1184         break;
1185       case 'f':
1186 #ifdef HAVE_LIBPCAP
1187         if (cf.cfilter)
1188                 g_free(cf.cfilter);
1189         cf.cfilter = g_strdup(optarg);
1190 #else
1191         capture_option_specified = TRUE;
1192         arg_error = TRUE;
1193 #endif
1194         break;
1195       case 'h':        /* Print help and exit */
1196         print_usage();
1197         exit(0);
1198         break;
1199       case 'i':        /* Use interface xxx */
1200 #ifdef HAVE_LIBPCAP
1201         cf.iface = g_strdup(optarg);
1202 #else
1203         capture_option_specified = TRUE;
1204         arg_error = TRUE;
1205 #endif
1206         break;
1207       case 'k':        /* Start capture immediately */
1208 #ifdef HAVE_LIBPCAP
1209         start_capture = TRUE;
1210 #else
1211         capture_option_specified = TRUE;
1212         arg_error = TRUE;
1213 #endif
1214         break;
1215       case 'm':        /* Medium font */
1216         medium_font = g_strdup(optarg);
1217         break;
1218       case 'n':        /* No name resolution */
1219         g_resolving_actif = 0;
1220         break;
1221       case 'P':        /* Packet list pane height */
1222         pl_size = atoi(optarg);
1223         break;
1224       case 'Q':        /* Quit after capture (just capture to file) */
1225 #ifdef HAVE_LIBPCAP
1226         quit_after_cap = 1;
1227         start_capture = TRUE;  /*** -Q implies -k !! ***/
1228 #else
1229         capture_option_specified = TRUE;
1230         arg_error = TRUE;
1231 #endif
1232         break;
1233       case 'r':        /* Read capture file xxx */
1234         /* We may set "last_open_dir" to "cf_name", and if we change
1235            "last_open_dir" later, we free the old value, so we have to
1236            set "cf_name" to something that's been allocated. */
1237         cf_name = g_strdup(optarg);
1238         break;
1239       case 'R':        /* Read file filter */
1240         rfilter = optarg;
1241         break;
1242       case 's':        /* Set the snapshot (capture) length */
1243 #ifdef HAVE_LIBPCAP
1244         cf.snap = atoi(optarg);
1245 #else
1246         capture_option_specified = TRUE;
1247         arg_error = TRUE;
1248 #endif
1249         break;
1250       case 'S':        /* "Sync" mode: used for following file ala tail -f */
1251 #ifdef HAVE_LIBPCAP
1252         sync_mode = TRUE;
1253 #else
1254         capture_option_specified = TRUE;
1255         arg_error = TRUE;
1256 #endif
1257         break;
1258       case 't':        /* Time stamp type */
1259         if (strcmp(optarg, "r") == 0)
1260           timestamp_type = RELATIVE;
1261         else if (strcmp(optarg, "a") == 0)
1262           timestamp_type = ABSOLUTE;
1263         else if (strcmp(optarg, "d") == 0)
1264           timestamp_type = DELTA;
1265         else {
1266           fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
1267             optarg);
1268           fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
1269           fprintf(stderr, "or \"d\" for delta.\n");
1270           exit(1);
1271         }
1272         break;
1273       case 'T':        /* Tree view pane height */
1274         tv_size = atoi(optarg);
1275         break;
1276       case 'v':        /* Show version and exit */
1277         printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
1278         exit(0);
1279         break;
1280       case 'w':        /* Write to capture file xxx */
1281 #ifdef HAVE_LIBPCAP
1282         save_file = g_strdup(optarg);
1283 #else
1284         capture_option_specified = TRUE;
1285         arg_error = TRUE;
1286 #endif
1287         break;
1288       case 'W':        /* Write to capture file FD xxx */
1289 #ifdef HAVE_LIBPCAP
1290         cf.save_file_fd = atoi(optarg);
1291 #else
1292         capture_option_specified = TRUE;
1293         arg_error = TRUE;
1294 #endif
1295         break;
1296       default:
1297       case '?':        /* Bad flag - print usage message */
1298         arg_error = TRUE;
1299         break;
1300     }
1301   }
1302 #endif
1303
1304 #ifndef HAVE_LIBPCAP
1305   if (capture_option_specified)
1306     fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
1307 #endif
1308 #ifndef WIN32
1309   if (arg_error)
1310     print_usage();
1311 #endif
1312 #ifdef HAVE_LIBPCAP
1313   if (start_capture) {
1314     /* We're supposed to do a live capture; did the user specify an interface
1315        to use? */
1316     if (cf.iface == NULL) {
1317       /* No - pick the first one from the list of interfaces. */
1318       if_list = get_interface_list(&err, err_str);
1319       if (if_list == NULL) {
1320         switch (err) {
1321
1322         case CANT_GET_INTERFACE_LIST:
1323             fprintf(stderr, "ethereal: Can't get list of interfaces: %s\n",
1324                         err_str);
1325             break;
1326
1327         case NO_INTERFACES_FOUND:
1328             fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
1329             break;
1330         }
1331         exit(2);
1332       }
1333       cf.iface = g_strdup(if_list->data);       /* first interface */
1334       free_interface_list(if_list);
1335     }
1336   }
1337   if (capture_child) {
1338     if (cf.save_file_fd == -1) {
1339       /* XXX - send this to the standard output as something our parent
1340          should put in an error message box? */
1341       fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
1342       exit(1);
1343     }
1344   }
1345 #endif
1346
1347   /* Build the column format array */  
1348   for (i = 0; i < cf.cinfo.num_cols; i++) {
1349     cf.cinfo.col_fmt[i] = get_column_format(i);
1350     cf.cinfo.col_title[i] = g_strdup(get_column_title(i));
1351     cf.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
1352       NUM_COL_FMTS);
1353     get_column_format_matches(cf.cinfo.fmt_matx[i], cf.cinfo.col_fmt[i]);
1354     if (cf.cinfo.col_fmt[i] == COL_INFO)
1355       cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
1356     else
1357       cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
1358   }
1359
1360   if (cf.snap < 1)
1361     cf.snap = WTAP_MAX_PACKET_SIZE;
1362   else if (cf.snap < MIN_PACKET_SIZE)
1363     cf.snap = MIN_PACKET_SIZE;
1364   
1365   rc_file = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(RC_FILE) + 4);
1366   sprintf(rc_file, "%s/%s", get_home_dir(), RC_FILE);
1367   gtk_rc_parse(rc_file);
1368
1369   if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
1370     fprintf(stderr, "ethereal: Error font %s not found (use -m option)\n", medium_font);
1371     exit(1);
1372   }
1373
1374   if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
1375     fprintf(stderr, "ethereal: Error font %s not found (use -b option)\n", bold_font);
1376     exit(1);
1377   }
1378
1379   create_main_window(pl_size, tv_size, bv_size, prefs);
1380
1381 /* 
1382    Hmmm should we do it here
1383 */
1384
1385   ethereal_proto_init();   /* Init anything that needs initializing */
1386
1387 #ifdef HAVE_LIBPCAP
1388   /* Is this a "child" ethereal, which is only supposed to pop up a
1389      capture box to let us stop the capture, and run a capture
1390      to a file that our parent will read? */
1391   if (!capture_child) {
1392 #endif
1393     /* No.  Pop up the main window, and read in a capture file if
1394        we were told to. */
1395
1396     gtk_widget_show(top_level);
1397     set_menus_for_capture_file(FALSE);
1398
1399     cf.colors = colfilter_new();
1400
1401     /* If we were given the name of a capture file, read it in now;
1402        we defer it until now, so that, if we can't open it, and pop
1403        up an alert box, the alert box is more likely to come up on
1404        top of the main window - but before the preference-file-error
1405        alert box, so, if we get one of those, it's more likely to come
1406        up on top of us. */
1407     if (cf_name) {
1408       if (rfilter != NULL) {
1409         if (dfilter_compile(rfilter, &rfcode) != 0) {
1410           simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
1411           rfilter_parse_failed = TRUE;
1412         }
1413       }
1414       if (!rfilter_parse_failed) {
1415         if ((err = open_cap_file(cf_name, FALSE, &cf)) == 0) {
1416           /* "open_cap_file()" succeeded, so it closed the previous
1417              capture file, and thus destroyed any previous read filter
1418              attached to "cf". */
1419           cf.rfcode = rfcode;
1420           err = read_cap_file(&cf);
1421           /* Save the name of the containing directory specified in the
1422              path name, if any; we can write over cf_name, which is a
1423              good thing, given that "get_dirname()" does write over its
1424              argument. */
1425           s = get_dirname(cf_name);
1426           if (s != NULL)
1427             last_open_dir = s;
1428         } else {
1429           dfilter_destroy(rfcode);
1430           cf.rfcode = NULL;
1431         }
1432       }
1433     }
1434 #ifdef HAVE_LIBPCAP
1435   }
1436 #endif
1437
1438   /* If we failed to open the preferences file, pop up an alert box;
1439      we defer it until now, so that the alert box is more likely to
1440      come up on top of the main window. */
1441   if (pf_path != NULL) {
1442       simple_dialog(ESD_TYPE_WARN, NULL,
1443         "Could not open preferences file\n\"%s\": %s.", pf_path,
1444         strerror(pf_open_errno));
1445   }
1446
1447 #ifdef HAVE_LIBPCAP
1448   if (capture_child) {
1449     /* This is the child process for a sync mode or fork mode capture,
1450        so just do the low-level work of a capture - don't create
1451        a temporary file and fork off *another* child process (so don't
1452        call "do_capture()"). */
1453
1454        capture();
1455
1456        /* The capture is done; there's nothing more for us to do. */
1457        gtk_exit(0);
1458   } else {
1459     if (start_capture) {
1460       /* "-k" was specified; start a capture. */
1461       do_capture(save_file);
1462     }
1463     else {
1464             set_menus_for_capture_in_progress(FALSE);
1465     }
1466   }
1467 #else
1468   set_menus_for_capture_in_progress(FALSE);
1469 #endif
1470
1471   gtk_main();
1472
1473   ethereal_proto_cleanup();
1474   g_free(rc_file);
1475
1476   exit(0);
1477 }
1478
1479 static void
1480 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
1481 {
1482   GtkWidget           *main_vbox, *menubar, *u_pane, *l_pane,
1483                       *bv_table, *stat_hbox,
1484                       *filter_bt, *filter_cm, *filter_te,
1485                       *filter_reset;
1486   GList               *filter_list = NULL;
1487   GtkStyle            *pl_style;
1488   GtkAccelGroup       *accel;
1489   int                   i;
1490
1491   /* Main window */  
1492   top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1493   gtk_widget_set_name(top_level, "main window");
1494   gtk_signal_connect(GTK_OBJECT(top_level), "delete_event",
1495     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
1496   gtk_signal_connect(GTK_OBJECT(top_level), "destroy", 
1497     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
1498   gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
1499   gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
1500   gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
1501
1502   /* Container for menu bar, paned windows and progress/info box */
1503   main_vbox = gtk_vbox_new(FALSE, 1);
1504   gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
1505   gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
1506   gtk_widget_show(main_vbox);
1507
1508   /* Menu bar */
1509   get_main_menu(&menubar, &accel);
1510   gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
1511   gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
1512   gtk_widget_show(menubar);
1513
1514   /* Panes for the packet list, tree, and byte view */
1515   u_pane = gtk_vpaned_new();
1516   gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
1517   l_pane = gtk_vpaned_new();
1518   gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
1519   gtk_container_add(GTK_CONTAINER(main_vbox), l_pane);
1520   gtk_widget_show(u_pane);
1521   gtk_paned_add1 (GTK_PANED(l_pane), u_pane);
1522   gtk_widget_show(l_pane);
1523
1524   /* Packet list */
1525   pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
1526   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
1527     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1528   gtk_widget_show(pkt_scrollw);
1529   gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
1530
1531   packet_list = gtk_clist_new_with_titles(cf.cinfo.num_cols, cf.cinfo.col_title);
1532   gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
1533   gtk_clist_column_titles_passive(GTK_CLIST(packet_list));
1534   set_plist_sel_browse(prefs->gui_plist_sel_browse);
1535   pl_style = gtk_style_new();
1536   gdk_font_unref(pl_style->font);
1537   pl_style->font = m_r_font;
1538   gtk_widget_set_style(packet_list, pl_style);
1539   gtk_widget_set_name(packet_list, "packet list");
1540   gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
1541     GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
1542   gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
1543     GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
1544   for (i = 0; i < cf.cinfo.num_cols; i++) {
1545     if (get_column_resize_type(cf.cinfo.col_fmt[i]) != RESIZE_MANUAL)
1546       gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
1547
1548     /* Right-justify the packet number column. */
1549     if (cf.cinfo.col_fmt[i] == COL_NUMBER)
1550       gtk_clist_set_column_justification(GTK_CLIST(packet_list), i, 
1551         GTK_JUSTIFY_RIGHT);
1552
1553     /* Save static column sizes to use during a "-S" capture, so that
1554        the columns don't resize during a live capture. */
1555     cf.cinfo.col_width[i] = gdk_string_width(pl_style->font,
1556                                 get_column_longest_string(get_column_format(i)));
1557   }
1558   gtk_widget_set_usize(packet_list, -1, pl_size);
1559   gtk_signal_connect_object(GTK_OBJECT(packet_list), "button_press_event",
1560     GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
1561   gtk_widget_show(packet_list);
1562   
1563   /* Tree view */
1564   tv_scrollw = gtk_scrolled_window_new(NULL, NULL);
1565   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(tv_scrollw),
1566     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1567   gtk_paned_add2(GTK_PANED(u_pane), tv_scrollw);
1568   gtk_widget_set_usize(tv_scrollw, -1, tv_size);
1569   gtk_widget_show(tv_scrollw);
1570   
1571   tree_view = gtk_ctree_new(1, 0);
1572   /* I need this next line to make the widget work correctly with hidden
1573    * column titles and GTK_SELECTION_BROWSE */
1574   gtk_clist_set_column_auto_resize( GTK_CLIST(tree_view), 0, TRUE );
1575   gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1576   set_ptree_sel_browse(prefs->gui_ptree_sel_browse);
1577   set_ptree_line_style(prefs->gui_ptree_line_style);
1578   set_ptree_expander_style(prefs->gui_ptree_expander_style);
1579
1580   gtk_signal_connect(GTK_OBJECT(tree_view), "tree-select-row",
1581     GTK_SIGNAL_FUNC(tree_view_select_row_cb), NULL);
1582   gtk_signal_connect(GTK_OBJECT(tree_view), "tree-unselect-row",
1583     GTK_SIGNAL_FUNC(tree_view_unselect_row_cb), NULL);
1584   gtk_signal_connect_object(GTK_OBJECT(tree_view), "button_press_event",
1585     GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY));
1586   gtk_widget_show(tree_view);
1587
1588   item_style = gtk_style_new();
1589   gdk_font_unref(item_style->font);
1590   item_style->font = m_r_font;
1591
1592   /* Byte view. The table is only one row high, but 3 columns
1593    * wide. The middle column contains the GtkText with the hex dump.
1594    * The left and right columns contain vertical scrollbars. They both
1595    * do the same thing, but only one will be shown at a time, in accordance
1596    * with where the user wants the other vertical scrollbars places
1597    * (on the left or the right).
1598    */
1599   bv_table = gtk_table_new (1, 3, FALSE);
1600   gtk_paned_pack2(GTK_PANED(l_pane), bv_table, FALSE, FALSE);
1601   gtk_widget_set_usize(bv_table, -1, bv_size);
1602   gtk_widget_show(bv_table);
1603
1604   byte_view = gtk_text_new(NULL, NULL);
1605   gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
1606   gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
1607   gtk_table_attach (GTK_TABLE (bv_table), byte_view, 1, 2, 0, 1,
1608     GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
1609   gtk_widget_show(byte_view);
1610
1611   /* The gtk_text widget doesn't scroll horizontally (see gtktext.c)
1612    * in the GTK+ distribution, so I removed the horizontal scroll bar
1613    * that used to be here. I tried the gtk_text widget with a 
1614    * gtk_scrolled_window w/ viewport, but the vertical scrollowing
1615    * did not work well, and sometimes a few pixels were cut off on
1616    * the bottom. */
1617
1618   bv_vscroll_left = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
1619   gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll_left, 0, 1, 0, 1,
1620     GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
1621
1622   bv_vscroll_right = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
1623   gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll_right, 2, 3, 0, 1,
1624     GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
1625  
1626   /* Now that the 3 panes are created, set the vertical scrollbar
1627    * on the left or right according to the user's preference */
1628   set_scrollbar_placement(prefs->gui_scrollbar_on_right);
1629
1630   /* Progress/filter/info box */
1631   stat_hbox = gtk_hbox_new(FALSE, 1);
1632   gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
1633   gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
1634   gtk_widget_show(stat_hbox);
1635
1636   prog_bar = gtk_progress_bar_new();
1637   gtk_box_pack_start(GTK_BOX(stat_hbox), prog_bar, FALSE, TRUE, 3);
1638   gtk_widget_show(prog_bar);
1639
1640   filter_bt = gtk_button_new_with_label("Filter:");
1641   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
1642     GTK_SIGNAL_FUNC(filter_dialog_cb), NULL);
1643   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
1644   gtk_widget_show(filter_bt);
1645   
1646   filter_cm = gtk_combo_new();
1647   filter_list = g_list_append (filter_list, "");
1648   gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
1649   gtk_combo_disable_activate(GTK_COMBO(filter_cm));
1650   filter_te = GTK_COMBO(filter_cm)->entry;
1651   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
1652   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
1653   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
1654   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
1655   gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
1656     GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
1657   gtk_widget_show(filter_cm);
1658
1659   filter_reset = gtk_button_new_with_label("Reset");
1660   gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
1661   gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
1662                      GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
1663   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
1664   gtk_widget_show(filter_reset);
1665
1666   /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
1667    * of any widget that ends up calling a callback which needs
1668    * that text entry pointer */
1669   set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
1670   set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
1671   set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
1672   set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
1673   set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
1674
1675   info_bar = gtk_statusbar_new();
1676   main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
1677   file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
1678   gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
1679   gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
1680   gtk_widget_show(info_bar);
1681 }