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