Move the toolkit-independent code to create a temporary capture file,
[obnox/wireshark/wip.git] / gtk / main.c
1 /* main.c
2  *
3  * $Id: main.c,v 1.7 1999/09/23 06:27:27 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  * - Get AIX to work
30  * - Check for end of packet in dissect_* routines.
31  * - Playback window
32  * - Multiple window support
33  * - Add cut/copy/paste
34  * - Create header parsing routines
35  * - Make byte view scrollbars automatic?
36  * - Make byte view selections more fancy?
37  *
38  */
39
40 #ifdef HAVE_CONFIG_H
41 # include "config.h"
42 #endif
43
44 #include <gtk/gtk.h>
45
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53
54 #include <errno.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <fcntl.h>
58
59 #ifdef HAVE_DIRECT_H
60 #include <direct.h>
61 #endif
62
63 #ifdef HAVE_NETINET_IN_H
64 #include <netinet/in.h>
65 #endif
66
67 #include <signal.h>
68
69 #ifdef NEED_SNPRINTF_H
70 # ifdef HAVE_STDARG_H
71 #  include <stdarg.h>
72 # else
73 #  include <varargs.h>
74 # endif
75 # include "snprintf.h"
76 #endif
77
78 #ifdef NEED_STRERROR_H
79 #include "strerror.h"
80 #endif
81
82 #include "main.h"
83 #include "timestamp.h"
84 #include "packet.h"
85 #include "capture.h"
86 #include "summary.h"
87 #include "file.h"
88 #include "menu.h"
89 #include "prefs_dlg.h"
90 #include "column.h"
91 #include "print.h"
92 #include "resolv.h"
93 #include "follow.h"
94 #include "util.h"
95 #include "proto_draw.h"
96 #include "dfilter.h"
97 #include "keys.h"
98
99 FILE        *data_out_file = NULL;
100 packet_info  pi;
101 capture_file cf;
102 GtkWidget   *file_sel, *packet_list, *tree_view, *byte_view, *prog_bar,
103             *info_bar;
104 GdkFont     *m_r_font, *m_b_font;
105 guint        main_ctx, file_ctx;
106 gint         start_capture = 0;
107 gchar        comp_info_str[256];
108 gchar       *ethereal_path = NULL;
109 gchar       *medium_font = MONO_MEDIUM_FONT;
110 gchar       *bold_font = MONO_BOLD_FONT;
111 gchar       *last_open_dir = NULL;
112
113 ts_type timestamp_type = RELATIVE;
114
115 GtkStyle *item_style;
116
117 #ifdef HAVE_LIBPCAP
118 int sync_mode;  /* allow sync */
119 int sync_pipe[2]; /* used to sync father */
120 int fork_mode;  /* fork a child to do the capture */
121 int quit_after_cap; /* Makes a "capture only mode". Implies -k */
122 #endif
123
124 /* Specifies byte offsets for object selected in tree */
125 static gint tree_selected_start=-1, tree_selected_len=-1; 
126
127
128 /* About Ethereal window */
129 void
130 about_ethereal( GtkWidget *w, gpointer data ) {
131   simple_dialog(ESD_TYPE_INFO, NULL,
132                 "GNU Ethereal - network protocol analyzer\n"
133                 "Version %s (C) 1998 Gerald Combs <gerald@zing.org>\n"
134                 "Compiled with %s\n\n"
135                 "Contributors:\n"
136
137                 "Gilbert Ramirez          <gramirez@tivoli.com>\n"
138                 "Hannes R. Boehm          <hannes@boehm.org>\n"
139                 "Mike Hall                <mlh@io.com>\n"
140                 "Bobo Rajec               <bobo@bsp-consulting.sk>\n"
141                 "Laurent Deniel           <deniel@worldnet.fr>\n"
142                 "Don Lafontaine           <lafont02@cn.ca>\n"
143                 "Guy Harris               <guy@netapp.com>\n"
144                 "Simon Wilkinson          <sxw@dcs.ed.ac.uk>\n"
145                 "Joerg Mayer              <jmayer@telemation.de>\n"
146                 "Martin Maciaszek         <fastjack@i-s-o.net>\n"
147                 "Didier Jorand            <Didier.Jorand@alcatel.fr>\n"
148                 "Jun-ichiro itojun Hagino <itojun@iijlab.net>\n"
149                 "Richard Sharpe           <sharpe@ns.aus.com>\n"
150                 "John McDermott           <jjm@jkintl.com>\n"
151                 "Jeff Jahr                <jjahr@shastanets.com>\n"
152                 "Brad Robel-Forrest       <bradr@watchguard.com>\n"
153                 "Ashok Narayanan          <ashokn@cisco.com>\n"
154                 "Aaron Hillegass          <aaron@classmax.com>\n"
155                 "Jason Lango              <jal@netapp.com>\n"
156                 "Johan Feyaerts           <Johan.Feyaerts@siemens.atea.be>\n"
157                 "Olivier Abad             <abad@daba.dhis.org>\n"
158                 "Thierry Andry            <Thierry.Andry@advalvas.be>\n"
159                 "Jeff Foster              <jfoste@woodward.com>\n"
160                 "Peter Torvals            <petertv@xoommail.com>\n"
161
162                 "\nSee http://ethereal.zing.org for more information",
163                 VERSION, comp_info_str);
164 }
165
166 /* Follow the TCP stream, if any, to which the last packet that we called
167    a dissection routine on belongs (this might be the most recently
168    selected packet, or it might be the last packet in the file). */
169 void
170 follow_stream_cb( GtkWidget *w, gpointer data ) {
171   char      filename1[128+1];
172   GtkWidget *streamwindow, *box, *text, *vscrollbar, *table;
173   int        tmp_fd;
174
175   if( pi.ipproto == 6 ) {
176     /* we got tcp so we can follow */
177     /* Create a temporary file into which to dump the reassembled data
178        from the TCP stream, and set "data_out_file" to refer to it, so
179        that the TCP code will write to it.
180
181        XXX - it might be nicer to just have the TCP code directly
182        append stuff to the text widget for the TCP stream window,
183        if we can arrange that said window not pop up until we're
184        done. */
185     tmp_fd = create_tempfile( filename1, sizeof filename1, "follow");
186     if (tmp_fd == -1) {
187       simple_dialog(ESD_TYPE_WARN, NULL,
188         "Could not create temporary file %s: %s", filename1, strerror(errno));
189       return;
190     }
191     data_out_file = fdopen( tmp_fd, "w" );
192     if( data_out_file == NULL ) {
193       simple_dialog(ESD_TYPE_WARN, NULL,
194         "Could not create temporary file %s: %s", filename1, strerror(errno));
195       close(tmp_fd);
196       unlink(filename1);
197       return;
198     }
199
200     /* Create a new filter that matches all packets in the TCP stream,
201        and set the display filter entry accordingly */
202     reset_tcp_reassembly();
203     cf.dfilter = build_follow_filter( &pi );
204
205     /* Run the display filter so it goes in effect. */
206     filter_packets(&cf);
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     gtk_signal_connect( GTK_OBJECT(streamwindow), "delete_event",
215                         NULL, "WM destroy" );
216     gtk_signal_connect( GTK_OBJECT(streamwindow), "destroy",
217                         NULL, "WM destroy" );
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 the scrollbar */
248     vscrollbar = gtk_vscrollbar_new( GTK_TEXT(text)->vadj );
249     gtk_table_attach( GTK_TABLE(table), vscrollbar, 1, 2, 0, 1,
250                       GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
251     gtk_widget_show( vscrollbar );
252     gtk_widget_realize( text );
253
254     /* stop the updates while we fill the text box */
255     gtk_text_freeze( GTK_TEXT(text) );
256     data_out_file = fopen( filename1, "r" );
257     if( data_out_file ) {
258       char buffer[1024];
259       int nchars;
260       while( 1 ) {
261         nchars = fread( buffer, 1, 1024, data_out_file );
262         gtk_text_insert( GTK_TEXT(text), m_r_font, NULL, NULL, buffer, nchars );
263         if( nchars < 1024 ) {
264           break;
265         }
266       }
267       if( ferror( data_out_file ) ) {
268         simple_dialog(ESD_TYPE_WARN, NULL,
269           "Error reading temporary file %s: %s", filename1, strerror(errno));
270       }
271       fclose( data_out_file );
272     } else {
273       simple_dialog(ESD_TYPE_WARN, NULL,
274         "Could not open temporary file %s: %s", filename1, strerror(errno));
275     }
276     gtk_text_thaw( GTK_TEXT(text) );
277     unlink( filename1 );
278     data_out_file = NULL;
279     gtk_widget_show( streamwindow );
280   } else {
281     simple_dialog(ESD_TYPE_WARN, NULL,
282       "Error following stream.  Please make\n"
283       "sure you have a TCP packet selected.");
284   }
285 }
286
287 /* Match selected byte pattern */
288 void
289 match_selected_cb(GtkWidget *w, gpointer data)
290 {
291     char *buf = g_malloc(1024);
292     GtkWidget *filter_te = NULL;
293     char *ptr;
294     int i;
295     guint8 *c;
296
297     filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
298
299     if (tree_selected_start<0) {
300         simple_dialog(ESD_TYPE_WARN, NULL,
301                       "Error determining selected bytes.  Please make\n"
302                       "sure you have selected a field within the tree\n"
303                       "view to be matched.");
304         return;
305     }
306
307     c = cf.pd + tree_selected_start;
308     ptr = buf;
309
310     sprintf(ptr, "frame[%d : %d] == ", tree_selected_start, tree_selected_len);
311     ptr = buf+strlen(buf);
312
313     if (tree_selected_len == 1) {
314         sprintf(ptr, "0x%02x", *c++);
315     }
316     else {
317             for (i=0;i<tree_selected_len; i++) {
318                 if (i == 0 ) {
319                         sprintf(ptr, "%02x", *c++);
320                 }
321                 else {
322                         sprintf(ptr, ":%02x", *c++);
323                 }
324                 ptr = buf+strlen(buf);
325             }
326     }
327
328     if( cf.dfilter != NULL ) {
329       /* get rid of this one */
330       g_free( cf.dfilter );
331     }
332     /* create a new one and set the display filter entry accordingly */
333     cf.dfilter = buf;
334     if (filter_te) {
335         gtk_entry_set_text(GTK_ENTRY(filter_te), cf.dfilter);
336     }
337     /* Run the display filter so it goes in effect. */
338     filter_packets(&cf);
339 }
340
341 /* Run the current display filter on the current packet set, and
342    redisplay. */
343 static void
344 filter_activate_cb(GtkWidget *w, gpointer data)
345 {
346   char *s = gtk_entry_get_text(GTK_ENTRY(w));
347
348   if (cf.dfilter)
349         g_free(cf.dfilter);
350
351   /* simple check for empty string. XXX - need to modify to search for /^\s+$/ */
352   if (s[0] == '\0' ) {
353         cf.dfilter = NULL;
354   }
355   else {
356         cf.dfilter = g_strdup(s);
357   }
358
359   filter_packets(&cf);
360 }
361
362 /* What to do when a list item is selected/unselected */
363 void
364 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
365
366 #ifdef HAVE_LIBPCAP
367   if (!sync_mode) {
368 #endif
369     if (cf.wth)
370       return; 
371 #ifdef HAVE_LIBPCAP
372   }
373 #endif
374   blank_packetinfo();
375   select_packet(&cf, row);
376 }
377
378 void
379 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
380   unselect_packet(&cf);
381 }
382
383 void
384 tree_view_cb(GtkWidget *w, gpointer data) {
385
386   tree_selected_start = -1;
387   tree_selected_len = -1;
388
389   if (GTK_TREE(w)->selection) {
390     tree_selected_start = 
391         (gint) gtk_object_get_data(GTK_OBJECT(GTK_TREE(w)->selection->data),
392                                    E_TREEINFO_START_KEY);
393     tree_selected_len   = 
394         (gint) gtk_object_get_data(GTK_OBJECT(GTK_TREE(w)->selection->data),
395                                    E_TREEINFO_LEN_KEY);
396   }
397
398   gtk_text_freeze(GTK_TEXT(byte_view));
399   gtk_text_set_point(GTK_TEXT(byte_view), 0);
400   gtk_text_forward_delete(GTK_TEXT(byte_view),
401     gtk_text_get_length(GTK_TEXT(byte_view)));
402   packet_hex_print(GTK_TEXT(byte_view), cf.pd, cf.fd->cap_len, 
403                    tree_selected_start, 
404                    tree_selected_len);
405   
406   gtk_text_thaw(GTK_TEXT(byte_view));
407 }
408
409 void collapse_all_cb(GtkWidget *widget, gpointer data) {
410   if (cf.protocol_tree)
411     collapse_all_tree(cf.protocol_tree, tree_view);
412 }
413
414 void expand_all_cb(GtkWidget *widget, gpointer data) {
415   if (cf.protocol_tree)
416     expand_all_tree(cf.protocol_tree, tree_view);
417 }
418
419 void
420 file_quit_cmd_cb (GtkWidget *widget, gpointer data) {
421   if (cf.save_file && !cf.user_saved) {
422         unlink(cf.save_file);
423   }
424   gtk_exit(0);
425 }
426
427 void blank_packetinfo() {
428   pi.ip_src   = 0;
429   pi.ip_dst   = 0;
430   pi.ipproto  = 0;
431   pi.srcport  = 0;
432   pi.destport = 0;
433 }
434
435 /* Things to do when the main window is realized */
436 void
437 main_realize_cb(GtkWidget *w, gpointer data) {
438 #ifdef HAVE_LIBPCAP
439   if (start_capture) {
440     capture();
441     start_capture = 0;
442   }
443 #endif
444 }
445
446 /* call initialization routines at program startup time */
447 static void
448 ethereal_proto_init(void) {
449   proto_init();
450   init_dissect_udp();
451   dfilter_init();
452 }
453
454 static void
455 ethereal_proto_cleanup(void) {
456         proto_cleanup();
457         dfilter_cleanup();
458 }
459
460 static void 
461 print_usage(void) {
462
463   fprintf(stderr, "This is GNU %s %s, compiled with %s\n", PACKAGE,
464           VERSION, comp_info_str);
465   fprintf(stderr, "%s [-vh] [-FkQS] [-b bold font] [-B byte view height] [-c count]\n",
466           PACKAGE);
467   fprintf(stderr, "         [-f \"filter expression\"] [-i interface] [-m medium font] [-n]\n");
468   fprintf(stderr, "         [-P packet list height] [-r infile] [-s snaplen]\n");
469   fprintf(stderr, "         [-t <time stamp format>] [-T tree view height] [-w savefile] \n");
470 }
471
472 /* And now our feature presentation... [ fade to music ] */
473 int
474 main(int argc, char *argv[])
475 {
476   char               *command_name, *s;
477   int                  i;
478 #ifndef WIN32
479   int                  opt;
480   extern char         *optarg;
481 #endif
482   char                *pf_path;
483   int                 pf_open_errno = 0;
484   int                 err;
485   GtkWidget           *window, *main_vbox, *menubar, *u_pane, *l_pane,
486                       *bv_table, *bv_hscroll, *bv_vscroll, *stat_hbox, 
487                       *tv_scrollw, *filter_bt, *filter_te;
488   GtkStyle            *pl_style;
489   GtkAccelGroup       *accel;
490   GtkWidget           *packet_sw;
491   gint                 pl_size = 280, tv_size = 95, bv_size = 75;
492   gchar               *rc_file, *cf_name = NULL, *rfilter = NULL;
493   dfilter             *rfcode = NULL;
494   gboolean             rfilter_parse_failed = FALSE;
495   e_prefs             *prefs;
496
497   ethereal_path = argv[0];
498
499   /* If invoked as "ethereal-dump-fields", we dump out a glossary of
500      display filter symbols; we specify that by checking the name,
501      so that we can do so before looking at the argument list -
502      we don't want to look at the argument list, because we don't
503      want to call "gtk_init()", because we don't want to have to
504      do any X stuff just to do a build. */
505   command_name = strrchr(ethereal_path, '/');
506   if (command_name == NULL)
507     command_name = ethereal_path;
508   else
509     command_name++;
510   if (strcmp(command_name, "ethereal-dump-fields") == 0) {
511     ethereal_proto_init();
512     proto_registrar_dump();
513     exit(0);
514   }
515   
516   /* Let GTK get its args */
517   gtk_init (&argc, &argv);
518   
519   prefs = read_prefs(&pf_path);
520   if (pf_path != NULL) {
521     /* The preferences file exists, but couldn't be opened; "pf_path" is
522        its pathname.  Remember "errno", as that says why the attempt
523        failed. */
524     pf_open_errno = errno;
525   }
526
527   /* Initialize the capture file struct */
528   cf.plist              = NULL;
529   cf.plist_end          = NULL;
530   cf.wth                = NULL;
531   cf.fh                 = NULL;
532   cf.rfcode             = NULL;
533   cf.dfilter            = NULL;
534   cf.dfcode             = dfilter_new();
535 #ifdef HAVE_LIBPCAP
536   cf.cfilter            = NULL;
537 #endif
538   cf.iface              = NULL;
539   cf.save_file          = NULL;
540   cf.save_file_fd       = -1;
541   cf.user_saved         = 0;
542   cf.snap               = WTAP_MAX_PACKET_SIZE;
543   cf.count              = 0;
544   cf.cinfo.num_cols     = prefs->num_cols;
545   cf.cinfo.col_fmt      = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
546   cf.cinfo.fmt_matx     = (gboolean **) g_malloc(sizeof(gboolean *) * cf.cinfo.num_cols);
547   cf.cinfo.col_width    = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
548   cf.cinfo.col_title    = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
549   cf.cinfo.col_data     = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
550
551   /* Assemble the compile-time options */
552   snprintf(comp_info_str, 256,
553 #ifdef GTK_MAJOR_VERSION
554     "GTK+ %d.%d.%d, %s libpcap", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
555     GTK_MICRO_VERSION,
556 #else
557     "GTK+ (version unknown), %s libpcap",
558 #endif
559
560 #ifdef HAVE_LIBPCAP
561    "with"
562 #else
563    "without"
564 #endif
565    );
566
567 #ifndef WIN32
568   /* Now get our args */
569   while ((opt = getopt(argc, argv, "b:B:c:f:Fhi:km:nP:Qr:R:Ss:t:T:w:W:v")) != EOF) {
570     switch (opt) {
571       case 'b':        /* Bold font */
572         bold_font = g_strdup(optarg);
573         break;
574       case 'B':        /* Byte view pane height */
575         bv_size = atoi(optarg);
576         break;
577       case 'c':        /* Capture xxx packets */
578         cf.count = atoi(optarg);
579         break;
580 #ifdef HAVE_LIBPCAP
581       case 'f':
582         cf.cfilter = g_strdup(optarg);
583         break;
584       case 'F':        /* Fork to capture */
585         fork_mode = 1;
586         break;
587 #endif
588       case 'h':        /* Print help and exit */
589         print_usage();
590         exit(0);
591         break;
592       case 'i':        /* Use interface xxx */
593         cf.iface = g_strdup(optarg);
594         break;
595       case 'm':        /* Medium font */
596         medium_font = g_strdup(optarg);
597         break;
598       case 'n':        /* No name resolution */
599         g_resolving_actif = 0;
600         break;
601 #ifdef HAVE_LIBPCAP
602       case 'k':        /* Start capture immediately */
603         start_capture = 1;
604         break;
605 #endif
606       case 'P':        /* Packet list pane height */
607         pl_size = atoi(optarg);
608         break;
609 #ifdef HAVE_LIBPCAP
610       case 'Q':        /* Quit after capture (just capture to file) */
611         quit_after_cap = 1;
612         start_capture = 1;  /*** -Q implies -k !! ***/
613         break;
614 #endif
615       case 'r':        /* Read capture file xxx */
616         cf_name = g_strdup(optarg);
617         break;
618       case 'R':        /* Read file filter */
619         rfilter = optarg;
620         break;
621 #ifdef HAVE_LIBPCAP
622       case 's':        /* Set the snapshot (capture) length */
623         cf.snap = atoi(optarg);
624         break;
625       case 'S':        /* "Sync" mode: used for following file ala tail -f */
626         sync_mode = 1;
627         fork_mode = 1; /* -S implies -F */
628         break;
629 #endif
630       case 't':        /* Time stamp type */
631         if (strcmp(optarg, "r") == 0)
632           timestamp_type = RELATIVE;
633         else if (strcmp(optarg, "a") == 0)
634           timestamp_type = ABSOLUTE;
635         else if (strcmp(optarg, "d") == 0)
636           timestamp_type = DELTA;
637         else {
638           fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
639             optarg);
640           fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
641           fprintf(stderr, "or \"d\" for delta.\n");
642           exit(1);
643         }
644         break;
645       case 'T':        /* Tree view pane height */
646         tv_size = atoi(optarg);
647         break;
648       case 'v':        /* Show version and exit */
649         printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
650         exit(0);
651         break;
652 #ifdef HAVE_LIBPCAP
653       case 'w':        /* Write to capture file xxx */
654         cf.save_file = g_strdup(optarg);
655         break;
656       case 'W':        /* Write to capture file FD xxx */
657         cf.save_file_fd = atoi(optarg);
658         break;
659 #endif
660     }
661   }
662 #endif
663
664   if (start_capture) {
665     if (cf.iface == NULL) {
666       fprintf(stderr, "ethereal: \"-k\" flag was specified without \"-i\" flag\n");
667       exit(1);
668     }
669     if (cf.save_file == NULL) {
670       fprintf(stderr, "ethereal: \"-k\" flag was specified without \"-w\" flag\n");
671       exit(1);
672     }
673 #ifdef HAVE_LIBPCAP
674     if (fork_mode) {
675       if (cf.save_file_fd == -1) {
676         fprintf(stderr, "ethereal: \"-k\" flag was specified with \"-%c\" flag but without \"-W\" flag\n",
677             (sync_mode ? 'S' : 'F'));
678         exit(1);
679       }
680     }
681 #endif
682   }
683
684   /* Build the column format array */  
685   for (i = 0; i < cf.cinfo.num_cols; i++) {
686     cf.cinfo.col_fmt[i] = get_column_format(i);
687     cf.cinfo.col_title[i] = g_strdup(get_column_title(i));
688     cf.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
689       NUM_COL_FMTS);
690     get_column_format_matches(cf.cinfo.fmt_matx[i], cf.cinfo.col_fmt[i]);
691     cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
692   }
693
694   if (cf.snap < 1)
695     cf.snap = WTAP_MAX_PACKET_SIZE;
696   else if (cf.snap < MIN_PACKET_SIZE)
697     cf.snap = MIN_PACKET_SIZE;
698   
699   rc_file = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(RC_FILE) + 4);
700   sprintf(rc_file, "%s/%s", getenv("HOME"), RC_FILE);
701   gtk_rc_parse(rc_file);
702
703   if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
704     fprintf(stderr, "ethereal: Error font %s not found (use -m option)\n", medium_font);
705     exit(1);
706   }
707
708   if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
709     fprintf(stderr, "ethereal: Error font %s not found (use -b option)\n", bold_font);
710     exit(1);
711   }
712
713   /* Main window */  
714   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
715   gtk_widget_set_name(window, "main window");
716   gtk_signal_connect(GTK_OBJECT(window), "delete_event",
717     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
718   gtk_signal_connect(GTK_OBJECT(window), "destroy", 
719     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
720   gtk_signal_connect(GTK_OBJECT (window), "realize",
721     GTK_SIGNAL_FUNC(main_realize_cb), NULL);
722   gtk_window_set_title(GTK_WINDOW(window), "The Ethereal Network Analyzer");
723   gtk_widget_set_usize(GTK_WIDGET(window), DEF_WIDTH, -1);
724   gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
725
726   /* Container for menu bar, paned windows and progress/info box */
727   main_vbox = gtk_vbox_new(FALSE, 1);
728   gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
729   gtk_container_add(GTK_CONTAINER(window), main_vbox);
730   gtk_widget_show(main_vbox);
731
732   /* Menu bar */
733   get_main_menu(&menubar, &accel);
734   gtk_window_add_accel_group(GTK_WINDOW(window), accel);
735   gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
736   gtk_widget_show(menubar);
737
738   /* Panes for the packet list, tree, and byte view */
739   u_pane = gtk_vpaned_new();
740   gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
741   l_pane = gtk_vpaned_new();
742   gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
743   gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
744   gtk_widget_show(u_pane);
745   gtk_paned_add2 (GTK_PANED(u_pane), l_pane);
746   gtk_widget_show(l_pane);
747
748   /* Packet list */
749   packet_list = gtk_clist_new_with_titles(cf.cinfo.num_cols, cf.cinfo.col_title);
750   gtk_clist_column_titles_passive(GTK_CLIST(packet_list));
751   packet_sw = gtk_scrolled_window_new(NULL, NULL);
752   gtk_widget_show(packet_sw);
753   gtk_container_add(GTK_CONTAINER(packet_sw), packet_list);
754   pl_style = gtk_style_new();
755   gdk_font_unref(pl_style->font);
756   pl_style->font = m_r_font;
757   gtk_widget_set_style(packet_list, pl_style);
758   gtk_widget_set_name(packet_list, "packet list");
759   gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
760     GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
761   gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
762     GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
763   for (i = 0; i < cf.cinfo.num_cols; i++) {
764     if (get_column_resize_type(cf.cinfo.col_fmt[i]) != RESIZE_MANUAL)
765       gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
766
767     /* Right-justify the packet number column. */
768     if (cf.cinfo.col_fmt[i] == COL_NUMBER)
769       gtk_clist_set_column_justification(GTK_CLIST(packet_list), i, 
770         GTK_JUSTIFY_RIGHT);
771
772     /* Save static column sizes to use during a "-S" capture, so that
773        the columns don't resize during a live capture. */
774     cf.cinfo.col_width[i] = get_column_width(get_column_format(i),
775                                                 pl_style->font);
776   }
777   gtk_widget_set_usize(packet_list, -1, pl_size);
778   gtk_paned_add1(GTK_PANED(u_pane), packet_sw);
779   gtk_widget_show(packet_list);
780   
781   /* Tree view */
782   tv_scrollw = gtk_scrolled_window_new(NULL, NULL);
783   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(tv_scrollw),
784     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
785   gtk_paned_add1(GTK_PANED(l_pane), tv_scrollw);
786   gtk_widget_set_usize(tv_scrollw, -1, tv_size);
787   gtk_widget_show(tv_scrollw);
788   
789   tree_view = gtk_tree_new();
790   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(tv_scrollw),
791                   tree_view);
792   gtk_tree_set_selection_mode(GTK_TREE(tree_view), GTK_SELECTION_SINGLE);
793
794   /* XXX - what's the difference between the next two lines? */
795   gtk_tree_set_view_lines(GTK_TREE(tree_view), FALSE);
796   gtk_tree_set_view_mode(GTK_TREE(tree_view), GTK_TREE_VIEW_ITEM);
797
798   gtk_signal_connect(GTK_OBJECT(tree_view), "selection_changed",
799     GTK_SIGNAL_FUNC(tree_view_cb), NULL);
800   gtk_widget_show(tree_view);
801
802   item_style = gtk_style_new();
803   gdk_font_unref(item_style->font);
804   item_style->font = m_r_font;
805
806   /* Byte view */
807   bv_table = gtk_table_new (2, 2, FALSE);
808   gtk_paned_add2(GTK_PANED(l_pane), bv_table);
809   gtk_widget_set_usize(bv_table, -1, bv_size);
810   gtk_widget_show(bv_table);
811
812   byte_view = gtk_text_new(NULL, NULL);
813   gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
814   gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
815   gtk_table_attach (GTK_TABLE (bv_table), byte_view, 0, 1, 0, 1,
816     GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
817   gtk_widget_show(byte_view);
818
819   bv_hscroll = gtk_hscrollbar_new(GTK_TEXT(byte_view)->hadj);
820   gtk_table_attach(GTK_TABLE(bv_table), bv_hscroll, 0, 1, 1, 2,
821     GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
822   gtk_widget_show (bv_hscroll);
823
824   bv_vscroll = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
825   gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll, 1, 2, 0, 1,
826     GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
827   gtk_widget_show(bv_vscroll);
828   
829   /* Progress/filter/info box */
830   stat_hbox = gtk_hbox_new(FALSE, 1);
831   gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
832   gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
833   gtk_widget_show(stat_hbox);
834
835   prog_bar = gtk_progress_bar_new();
836   gtk_box_pack_start(GTK_BOX(stat_hbox), prog_bar, FALSE, TRUE, 3);
837   gtk_widget_show(prog_bar);
838
839   filter_bt = gtk_button_new_with_label("Filter:");
840   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
841     GTK_SIGNAL_FUNC(prefs_cb), (gpointer) E_PR_PG_FILTER);
842   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
843   gtk_widget_show(filter_bt);
844   
845   filter_te = gtk_entry_new();
846   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
847   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_te, TRUE, TRUE, 3);
848   gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
849     GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
850   gtk_widget_show(filter_te);
851
852   /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
853    * of any widget that ends up calling a callback which needs
854    * that text entry pointer */
855   set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
856   set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
857   set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY,
858     filter_te);
859
860   info_bar = gtk_statusbar_new();
861   main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
862   file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
863   gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
864   gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
865   gtk_widget_show(info_bar);
866
867 /* 
868    Hmmm should we do it here
869 */
870
871   ethereal_proto_init();   /* Init anything that needs initializing */
872
873   gtk_widget_show(window);
874
875   colors_init(&cf);
876
877   /* If we were given the name of a capture file, read it in now;
878      we defer it until now, so that, if we can't open it, and pop
879      up an alert box, the alert box is more likely to come up on
880      top of the main window - but before the preference-file-error
881      alert box, so, if we get one of those, it's more likely to come
882      up on top of us. */
883   if (cf_name) {
884     if (rfilter != NULL) {
885       rfcode = dfilter_new();
886       if (dfilter_compile(rfcode, rfilter) != 0) {
887         simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
888         dfilter_destroy(rfcode);
889         rfilter_parse_failed = TRUE;
890       }
891     }
892     if (!rfilter_parse_failed) {
893       if ((err = open_cap_file(cf_name, &cf)) == 0) {
894         cf.rfcode = rfcode;
895         err = read_cap_file(&cf);
896         s = strrchr(cf_name, '/');
897         if (s) {
898           last_open_dir = cf_name;
899           *s = '\0';
900         }
901         set_menu_sensitivity("/File/Save As...", TRUE);
902       }
903     }
904   }
905
906   /* If we failed to open the preferences file, pop up an alert box;
907      we defer it until now, so that the alert box is more likely to
908      come up on top of the main window. */
909   if (pf_path != NULL) {
910       simple_dialog(ESD_TYPE_WARN, NULL,
911         "Could not open preferences file\n\"%s\": %s.", pf_path,
912         strerror(pf_open_errno));
913   }
914
915   gtk_main();
916
917   ethereal_proto_cleanup();
918
919   exit(0);
920 }