1cb66a3c431d8e183a26873bac21a3d39a0c34ff
[obnox/wireshark/wip.git] / ethereal.c
1 /* ethereal.c
2  *
3  * $Id: ethereal.c,v 1.4 1998/09/27 22:12:21 gerald Exp $
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@zing.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * 
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  *
24  *
25  * To do:
26  * - Add time stamps to packet list?
27  * - Live browser/capture display
28  * - Graphs
29  * - Prefs dialog
30  * - Get AIX to work
31  * - Fix PPP support.
32  * - Check for end of packet in dissect_* routines.
33  * - Playback window
34  * - Multiple window support
35  * - Add cut/copy/paste
36  * - Handle snoop files
37  * - Fix progress/status bar glitches?  (GTK+ bug?)
38  * - Create header parsing routines
39  * - Check fopens, freads, fwrites
40  * - Make byte view scrollbars automatic?
41  * - Make byte view selections more fancy?
42  *
43  */
44
45 #ifdef HAVE_CONFIG_H
46 # include "config.h"
47 #endif
48
49 #include <gtk/gtk.h>
50 #include <pcap.h>
51
52 #include <stdio.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <fcntl.h>
58 #include <netinet/in.h>
59
60 #include "ethereal.h"
61 #include "capture.h"
62 #include "packet.h"
63 #include "file.h"
64 #include "menu.h"
65 #include "etypes.h"
66 #include "print.h"
67 #include "resolv.h"
68 #include "follow.h"
69 #include "util.h"
70
71 FILE        *data_out_file = NULL;
72 packet_info  pi;
73 capture_file cf;
74 GtkWidget   *file_sel, *packet_list, *tree_view, *byte_view, *prog_bar,
75   *info_bar;
76 GdkFont     *m_r_font, *m_b_font;
77 guint        main_ctx, file_ctx;
78 frame_data  *fd;
79 gint         start_capture = 0;
80
81 const gchar *list_item_data_key = "list_item_data";
82
83 extern pr_opts printer_opts;
84
85 ts_type timestamp_type = RELATIVE;
86
87 /* Things to do when the OK button is pressed */
88 void
89 file_sel_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
90   gchar  *cf_name;
91   int     err;
92   
93   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
94   gtk_widget_hide(GTK_WIDGET (fs));
95   gtk_widget_destroy(GTK_WIDGET (fs));
96
97   if ((err = load_cap_file(cf_name, &cf)) == 0)
98     chdir(cf_name);
99   g_free(cf_name);
100 }
101
102 /* Update the progress bar */
103 gint
104 file_progress_cb(gpointer p) {
105   gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar),
106     (gfloat) ftell(cf.fh) / (gfloat) cf.f_len);
107   return TRUE;
108 }
109
110 /* Follow a TCP stream */
111 void
112 follow_stream_cb( GtkWidget *widget, gpointer data ) {
113   char filename1[128];
114   GtkWidget *streamwindow, *box, *text, *vscrollbar, *table;
115   if( pi.ipproto == 6 ) {
116     /* we got tcp so we can follow */
117     /* check to see if we are using a filter */
118     if( cf.filter != NULL ) {
119       /* get rid of this one */
120       g_free( cf.filter );
121       cf.filter = NULL;
122     }
123     /* create a new one */
124     cf.filter = build_follow_filter( &pi );
125     /* reload so it goes in effect. Also we set data_out_file which 
126        tells the tcp code to output the data */
127     close_cap_file( &cf, info_bar, file_ctx);
128     strcpy( filename1, tmpnam(NULL) );
129     data_out_file = fopen( filename1, "a" );
130     if( data_out_file == NULL ) {
131       fprintf( stderr, "Could not open tmp file %s\n", filename1 );
132     }
133     reset_tcp_reassembly();
134     load_cap_file( cf.filename, &cf );
135     /* the data_out_file should now be full of the streams information */
136     fclose( data_out_file );
137     /* the filename1 file now has all the text that was in the session */
138     streamwindow = gtk_window_new( GTK_WINDOW_TOPLEVEL);
139     gtk_widget_set_name( streamwindow, "TCP stream window" );
140     gtk_signal_connect( GTK_OBJECT(streamwindow), "delete_event",
141                         NULL, "WM destroy" );
142     gtk_signal_connect( GTK_OBJECT(streamwindow), "destroy",
143                         NULL, "WM destroy" );
144     gtk_window_set_title( GTK_WINDOW(streamwindow), "Contents of TCP stream" );
145     gtk_widget_set_usize( GTK_WIDGET(streamwindow), DEF_WIDTH, DEF_HEIGHT );
146     gtk_container_border_width( GTK_CONTAINER(streamwindow), 2 );
147     /* setup the container */
148     box = gtk_vbox_new( FALSE, 0 );
149     gtk_container_add( GTK_CONTAINER(streamwindow), box );
150     gtk_widget_show( box );
151     /* set up the table we attach to */
152     table = gtk_table_new( 1, 2, FALSE );
153     gtk_table_set_col_spacing( GTK_TABLE(table), 0, 2);
154     gtk_box_pack_start( GTK_BOX(box), table, TRUE, TRUE, 0 );
155     gtk_widget_show( table );
156     /* create a text box */
157     text = gtk_text_new( NULL, NULL );
158     gtk_text_set_editable( GTK_TEXT(text), FALSE);
159     gtk_table_attach( GTK_TABLE(table), text, 0, 1, 0, 1,
160                       GTK_EXPAND | GTK_SHRINK | GTK_FILL,
161                       GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
162     gtk_widget_show(text);
163     /* create the scrollbar */
164     vscrollbar = gtk_vscrollbar_new( GTK_TEXT(text)->vadj );
165     gtk_table_attach( GTK_TABLE(table), vscrollbar, 1, 2, 0, 1,
166                       GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
167     gtk_widget_show( vscrollbar );
168     gtk_widget_realize( text );
169     /* stop the updates while we fill the text box */
170     gtk_text_freeze( GTK_TEXT(text) );
171     data_out_file = NULL;
172     data_out_file = fopen( filename1, "r" );
173     if( data_out_file ) {
174       char buffer[1024];
175       int nchars;
176       while( 1 ) {
177         nchars = fread( buffer, 1, 1024, data_out_file );
178         gtk_text_insert( GTK_TEXT(text), m_r_font, NULL, NULL, buffer, nchars );
179         if( nchars < 1024 ) {
180           break;
181         }
182       }
183       fclose( data_out_file );
184       unlink( filename1 );
185     }
186     gtk_text_thaw( GTK_TEXT(text) );
187     data_out_file = NULL;
188     gtk_widget_show( streamwindow );
189     if( cf.filter != NULL ) {
190       g_free( cf.filter );
191       cf.filter = NULL;
192     }
193   } else {
194     simple_dialog(ESD_TYPE_WARN, NULL,
195       "Error following stream.  Please make\n"
196       "sure you have a TCP packet selected.");
197   }
198 }
199
200 /* Open a file */
201 void
202 file_open_cmd_cb(GtkWidget *widget, gpointer data) {
203   file_sel = gtk_file_selection_new ("Ethereal: Open Capture File");
204   
205   /* Connect the ok_button to file_ok_sel_cb function */
206   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->ok_button),
207     "clicked", (GtkSignalFunc) file_sel_ok_cb, file_sel );
208
209   /* Connect the cancel_button to destroy the widget */
210   gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
211     (file_sel)->cancel_button), "clicked", (GtkSignalFunc)
212     gtk_widget_destroy, GTK_OBJECT (file_sel));
213
214   gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_sel), "");
215
216   gtk_widget_show(file_sel);
217 }
218
219 /* Close a file */
220 void
221 file_close_cmd_cb(GtkWidget *widget, gpointer data) {
222   close_cap_file(&cf, info_bar, file_ctx);
223   set_menu_sensitivity("<Main>/File/Close", FALSE);
224 }
225
226 /* Print a packet */
227 void
228 file_print_cmd_cb(GtkWidget *widget, gpointer data) {
229     print_tree(cf.pd, fd, GTK_TREE(tree_view));
230 }
231
232 /* What to do when a list item is selected/unselected */
233 void
234 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
235   GList      *l;
236   
237   blank_packetinfo();
238   gtk_text_freeze(GTK_TEXT(byte_view));
239   gtk_text_set_point(GTK_TEXT(byte_view), 0);
240   gtk_text_forward_delete(GTK_TEXT(byte_view),
241     gtk_text_get_length(GTK_TEXT(byte_view)));
242   l = g_list_nth(cf.plist, row);
243   if (l) {
244     fd = (frame_data *) l->data;
245     fseek(cf.fh, fd->file_off, SEEK_SET);
246     fread(cf.pd, sizeof(guint8), fd->cap_len, cf.fh);
247     dissect_packet(cf.pd, 0, 0, fd, GTK_TREE(tree_view));
248     packet_hex_print(GTK_TEXT(byte_view), cf.pd, fd->cap_len, -1, -1);
249   }
250   gtk_text_thaw(GTK_TEXT(byte_view));
251 }
252
253 void
254 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
255   gtk_text_freeze(GTK_TEXT(byte_view));
256   gtk_text_set_point(GTK_TEXT(byte_view), 0);
257   gtk_text_forward_delete(GTK_TEXT(byte_view),
258     gtk_text_get_length(GTK_TEXT(byte_view)));
259   gtk_text_thaw(GTK_TEXT(byte_view));
260   gtk_tree_clear_items(GTK_TREE(tree_view), 0,
261     g_list_length(GTK_TREE(tree_view)->children));
262 }
263
264 void
265 tree_view_cb(GtkWidget *w) {
266   gint       start = -1, len = -1;
267   guint32    tinfo = 0;
268
269   if (GTK_TREE(w)->selection) {
270     tinfo = (guint32) gtk_object_get_user_data(GTK_TREE(w)->selection->data);
271     start = (tinfo >> 16) & 0xffff;
272     len   = tinfo & 0xffff;
273   }
274
275   gtk_text_freeze(GTK_TEXT(byte_view));
276   gtk_text_set_point(GTK_TEXT(byte_view), 0);
277   gtk_text_forward_delete(GTK_TEXT(byte_view),
278     gtk_text_get_length(GTK_TEXT(byte_view)));
279   packet_hex_print(GTK_TEXT(byte_view), cf.pd, fd->cap_len, start, len);
280   gtk_text_thaw(GTK_TEXT(byte_view));
281 }
282
283 void
284 file_quit_cmd_cb (GtkWidget *widget, gpointer data) {
285   gtk_exit(0);
286 }
287
288 void blank_packetinfo() {
289   pi.srcip    = 0;
290   pi.destip   = 0;
291   pi.ipproto  = 0;
292   pi.srcport  = 0;
293   pi.destport = 0;
294 }
295
296 /* Things to do when the OK button is pressed */
297 void
298 main_realize_cb(GtkWidget *w, gpointer data) {
299   gchar  *cf_name = (gchar *) data;
300   int     err;
301   
302   if (cf_name) {
303     err = load_cap_file(cf_name, &cf);
304     cf_name[0] = '\0';
305   }
306   if (start_capture) {
307     if (cf.save_file)
308       capture(1);
309     else
310       capture(0);
311     start_capture = 0;
312   }
313 }
314
315 void 
316 print_usage(void) {
317
318   fprintf(stderr, "This is GNU %s %s\n", PACKAGE, VERSION);
319   fprintf(stderr, "%s [-v] [-b bold font] [-B byte view height] [-c count] [-h]\n",
320           PACKAGE);
321   fprintf(stderr, "         [-i interface] [-m medium font] [-n] [-P packet list height]\n");
322   fprintf(stderr, "         [-r infile] [-s snaplen] [-t <time stamp format>]\n");
323   fprintf(stderr, "         [-T tree view height] [-w savefile] \n");
324 }
325
326 int
327 main(int argc, char *argv[])
328 {
329   int                  opt;
330   extern char         *optarg;
331   GtkWidget           *window, *main_vbox, *menubar, *u_pane, *l_pane,
332                       *bv_table, *bv_hscroll, *bv_vscroll, *stat_hbox, 
333                       *tv_scrollw;
334   GtkStyle            *pl_style;
335   GtkAcceleratorTable *accel;
336   gint                 col_width, pl_size = 280, tv_size = 95, bv_size = 75;
337   gchar               *rc_file, *cf_name = NULL;
338   gchar               *cl_title[] = {"No.", "Time", "Source", "Destination",
339                       "Protocol", "Info"};
340   gchar               *medium_font = MONO_MEDIUM_FONT;
341   gchar               *bold_font = MONO_BOLD_FONT;
342
343   /* Initialize the capture file struct */
344   cf.plist     = NULL;
345   cf.pfh       = NULL;
346   cf.fh        = NULL;
347   cf.filter    = NULL;
348   cf.iface     = NULL;
349   cf.save_file = NULL;
350   cf.snap      = 68;
351   cf.count     = 0;
352     
353   /* Let GTK get its args */
354   gtk_init (&argc, &argv);
355
356   /* Now get our args */
357   while ((opt = getopt(argc, argv, "b:B:c:hi:m:nP:r:s:t:T:w:v")) != EOF) {
358     switch (opt) {
359       case 'b':        /* Bold font */
360         bold_font = g_strdup(optarg);
361         break;
362       case 'B':        /* Byte view pane height */
363         bv_size = atoi(optarg);
364         break;
365       case 'c':        /* Capture xxx packets */
366         cf.count = atoi(optarg);
367         break;
368       case 'h':        /* Print help and exit */
369         print_usage();
370         exit(0);
371         break;
372       case 'i':        /* Use interface xxx */
373         cf.iface = g_strdup(optarg);
374         break;
375       case 'm':        /* Medium font */
376         medium_font = g_strdup(optarg);
377         break;
378       case 'n':        /* No name resolution */
379         g_resolving_actif = 0;
380         break;
381       case 'k':        /* Start capture immediately */
382         start_capture = 1;
383         break;
384       case 'P':        /* Packet list pane height */
385         pl_size = atoi(optarg);
386         break;
387       case 'r':        /* Read capture file xxx */
388         cf_name = g_strdup(optarg);
389         break;
390       case 's':        /* Set the snapshot (capture) length */
391         cf.snap = atoi(optarg);
392         break;
393       case 't':        /* Time stamp type */
394         if (strcmp(optarg, "r") == 0)
395           timestamp_type = RELATIVE;
396         else if (strcmp(optarg, "a") == 0)
397           timestamp_type = ABSOLUTE;
398         else if (strcmp(optarg, "d") == 0)
399           timestamp_type = DELTA;
400         else {
401           fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
402             optarg);
403           fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
404           fprintf(stderr, "or \"d\" for delta.\n");
405           exit(1);
406         }
407         break;
408       case 'T':        /* Tree view pane height */
409         tv_size = atoi(optarg);
410         break;
411       case 'v':        /* Show version and exit */
412         printf("%s %s\n", PACKAGE, VERSION);
413         exit(0);
414         break;
415       case 'w':        /* Write capture file xxx */
416         cf.save_file = g_strdup(optarg);
417         break;
418     }
419   }
420   
421   if (cf.snap < 1)
422     cf.snap = 4096;
423   else if (cf.snap < 68)
424     cf.snap = 68;
425   
426   rc_file = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(RC_FILE) + 4);
427   sprintf(rc_file, "%s/%s", getenv("HOME"), RC_FILE);
428   gtk_rc_parse(rc_file);
429
430   /* initialize printer options. temporary! we should only initialize
431    * if the options are not set in some ethereal initialization file */
432   printer_opts.output_format = 0;
433   printer_opts.output_dest = 0;
434   printer_opts.file = g_strdup("ethereal.out");
435   printer_opts.cmd = g_strdup("lpr");
436
437   if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
438     fprintf(stderr, "Error font %s not found (use -m option)\n", medium_font);
439     exit(1);
440   }
441
442   if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
443     fprintf(stderr, "Error font %s not found (use -b option)\n", bold_font);
444     exit(1);
445   }
446
447   /* Main window */  
448   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
449   gtk_widget_set_name(window, "main window");
450   gtk_signal_connect(GTK_OBJECT(window), "delete_event",
451     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
452   gtk_signal_connect(GTK_OBJECT(window), "destroy", 
453     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
454   gtk_signal_connect(GTK_OBJECT (window), "realize",
455     GTK_SIGNAL_FUNC(main_realize_cb), cf_name);
456   gtk_window_set_title(GTK_WINDOW(window), "The Ethereal Network Analyzer");
457   gtk_widget_set_usize(GTK_WIDGET(window), DEF_WIDTH, -1);
458
459   /* Container for menu bar, paned windows and progress/info box */
460   main_vbox = gtk_vbox_new(FALSE, 1);
461   gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
462   gtk_container_add(GTK_CONTAINER(window), main_vbox);
463   gtk_widget_show(main_vbox);
464
465   /* Menu bar */
466   get_main_menu(&menubar, &accel);
467   gtk_window_add_accelerator_table(GTK_WINDOW(window), accel);
468   gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
469   gtk_widget_show(menubar);
470
471   /* Panes for the packet list, tree, and byte view */
472   u_pane = gtk_vpaned_new();
473   l_pane = gtk_vpaned_new();
474   gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
475   gtk_widget_show(u_pane);
476   gtk_paned_add2 (GTK_PANED(u_pane), l_pane);
477   gtk_widget_show(l_pane);
478
479   /* Packet list */
480   packet_list = gtk_clist_new_with_titles(NUM_COLS, cl_title);
481   pl_style = gtk_style_new();
482   gdk_font_unref(pl_style->font);
483   pl_style->font = m_r_font;
484   gtk_widget_set_style(packet_list, pl_style);
485   gtk_widget_set_name(packet_list, "packet list");
486   gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
487     GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
488   gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
489     GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
490   gtk_clist_set_column_justification(GTK_CLIST(packet_list), 0, 
491     GTK_JUSTIFY_RIGHT);
492   col_width = (gdk_string_width(pl_style->font, "0") * 7) + 2;
493   gtk_clist_set_column_width(GTK_CLIST(packet_list), COL_NUM, col_width);
494   if (timestamp_type == ABSOLUTE)
495     col_width = gdk_string_width(pl_style->font, "00:00:00.000000");
496   else
497     col_width = gdk_string_width(pl_style->font, "0000.000000");
498   gtk_clist_set_column_width(GTK_CLIST(packet_list), COL_TIME, col_width);
499   col_width = gdk_string_width(pl_style->font, "00:00:00:00:00:00") + 2;
500   gtk_clist_set_column_width(GTK_CLIST(packet_list), COL_SOURCE, col_width);
501   gtk_clist_set_column_width(GTK_CLIST(packet_list), COL_DESTINATION, col_width);
502   col_width = gdk_string_width(pl_style->font, "AppleTalk") + 2;
503   gtk_clist_set_column_width(GTK_CLIST(packet_list), COL_PROTOCOL, col_width);
504   gtk_widget_set_usize(packet_list, -1, pl_size);
505   gtk_paned_add1(GTK_PANED(u_pane), packet_list);
506   gtk_widget_show(packet_list);
507   
508   /* Tree view */
509   tv_scrollw = gtk_scrolled_window_new(NULL, NULL);
510   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(tv_scrollw),
511     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
512   gtk_paned_add1(GTK_PANED(l_pane), tv_scrollw);
513   gtk_widget_set_usize(tv_scrollw, -1, tv_size);
514   gtk_widget_show(tv_scrollw);
515   
516   tree_view = gtk_tree_new();
517   gtk_container_add(GTK_CONTAINER(tv_scrollw), tree_view);
518   gtk_tree_set_selection_mode(GTK_TREE(tree_view), GTK_SELECTION_SINGLE);
519   gtk_tree_set_view_lines(GTK_TREE(tree_view), FALSE);
520   gtk_tree_set_view_mode(GTK_TREE(tree_view), TRUE);
521   gtk_signal_connect(GTK_OBJECT(tree_view), "selection_changed",
522     GTK_SIGNAL_FUNC(tree_view_cb), NULL);
523   gtk_widget_show(tree_view);
524
525   /* Byte view */
526   bv_table = gtk_table_new (2, 2, FALSE);
527   gtk_paned_add2(GTK_PANED(l_pane), bv_table);
528   gtk_widget_set_usize(bv_table, -1, bv_size);
529   gtk_widget_show(bv_table);
530
531   byte_view = gtk_text_new(NULL, NULL);
532   gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
533   gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
534   gtk_table_attach (GTK_TABLE (bv_table), byte_view, 0, 1, 0, 1,
535     GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
536   gtk_widget_show(byte_view);
537
538   bv_hscroll = gtk_hscrollbar_new(GTK_TEXT(byte_view)->hadj);
539   gtk_table_attach(GTK_TABLE(bv_table), bv_hscroll, 0, 1, 1, 2,
540     GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
541   gtk_widget_show (bv_hscroll);
542
543   bv_vscroll = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
544   gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll, 1, 2, 0, 1,
545     GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
546   gtk_widget_show(bv_vscroll);
547   
548   /* Progress/info box */
549   stat_hbox = gtk_hbox_new(FALSE, 1);
550   gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
551   gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
552   gtk_widget_show(stat_hbox);
553
554   prog_bar = gtk_progress_bar_new();  
555   gtk_box_pack_start(GTK_BOX(stat_hbox), prog_bar, FALSE, TRUE, 0);
556   gtk_widget_show(prog_bar);
557
558   info_bar = gtk_statusbar_new();
559   main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
560   file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
561   gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
562   gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
563   gtk_widget_show(info_bar);
564
565   gtk_widget_show(window);
566   gtk_main();
567
568   exit(0);
569 }