Include files from the "epan" directory and subdirectories thereof with
[obnox/wireshark/wip.git] / gtk / main.c
index bdf08f9670c94b06b6ee2351d7978cfdbc43aa82..68d0fabbab76b509ecc7ea8555438801488b26ae 100644 (file)
@@ -1,6 +1,6 @@
 /* main.c
  *
- * $Id: main.c,v 1.182 2001/03/02 23:10:12 gram Exp $
+ * $Id: main.c,v 1.231 2002/01/21 07:37:41 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -8,6 +8,8 @@
  *
  * Richard Sharpe, 13-Feb-1999, added support for initializing structures
  *                              needed by dissect routines
+ * Jeff Foster,    2001/03/12,  added support tabbed hex display windowss
+ * 
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -44,6 +46,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #include <pcap.h>
 #endif
 
+#ifdef HAVE_LIBZ
+#include <zlib.h>      /* to get the libz version number */
+#endif
+
 #ifdef NEED_SNPRINTF_H
 # include "snprintf.h"
 #endif
 #include <conio.h>
 #endif
 
-#include <epan.h>
+#include <epan/epan.h>
+#include <epan/filesystem.h>
+#include <epan/epan_dissect.h>
 
 #include "main.h"
-#include "timestamp.h"
-#include "packet.h"
+#include <epan/timestamp.h>
+#include <epan/packet.h>
 #include "capture.h"
 #include "summary.h"
 #include "file.h"
 #include "filters.h"
+#include "prefs.h"
 #include "menu.h"
 #include "../menu.h"
 #include "color.h"
 #include "color_utils.h"
 #include "filter_prefs.h"
-#include "prefs_dlg.h"
+#include "file_dlg.h"
 #include "column.h"
 #include "print.h"
-#include "resolv.h"
-#include "util.h"
+#include <epan/resolv.h>
+#ifdef HAVE_LIBPCAP
+#include "pcap-util.h"
+#endif
+#include "statusbar.h"
 #include "simple_dialog.h"
 #include "proto_draw.h"
-#include "dfilter/dfilter.h"
+#include <epan/dfilter/dfilter.h>
 #include "keys.h"
 #include "packet_win.h"
 #include "gtkglobals.h"
-#include "plugins.h"
+#include <epan/plugins.h>
 #include "colors.h"
-#include "strutil.h"
+#include <epan/strutil.h>
+#include "register.h"
+#include "ringbuffer.h"
+#include "ui_util.h"
+#include "image/clist_ascend.xpm"
+#include "image/clist_descend.xpm"
+
+#ifdef WIN32
+#include "capture-wpcap.h"
+#endif
+
+typedef struct column_arrows {
+  GtkWidget *table;
+  GtkWidget *ascend_pm;
+  GtkWidget *descend_pm;
+} column_arrows;
 
-packet_info  pi;
 capture_file cfile;
-GtkWidget   *top_level, *packet_list, *tree_view, *byte_view,
-            *info_bar, *tv_scrollw, *pkt_scrollw;
-static GtkWidget       *bv_scrollw;
+GtkWidget   *top_level, *packet_list, *tree_view, *byte_nb_ptr,
+            *tv_scrollw, *pkt_scrollw;
+static GtkWidget       *info_bar, *bv_scrollw;
 GdkFont     *m_r_font, *m_b_font;
 guint          m_font_height, m_font_width;
-guint        main_ctx, file_ctx, help_ctx;
-gchar        comp_info_str[256];
+static guint    main_ctx, file_ctx, help_ctx;
+static GString *comp_info_str;
 gchar       *ethereal_path = NULL;
 gchar       *last_open_dir = NULL;
+gint   root_x = G_MAXINT, root_y = G_MAXINT, top_width, top_height;
 
 ts_type timestamp_type = RELATIVE;
 
@@ -169,13 +197,13 @@ about_ethereal( GtkWidget *w, gpointer data ) {
   simple_dialog(ESD_TYPE_INFO, NULL,
                "Ethereal - Network Protocol Analyzer\n"
                "Version " VERSION " (C) 1998-2000 Gerald Combs <gerald@ethereal.com>\n"
-                "Compiled with %s\n\n"
+                "Compiled %s\n\n"
 
                "Check the man page for complete documentation and\n"
                "for the list of contributors.\n"
 
                "\nSee http://www.ethereal.com/ for more information.",
-                 comp_info_str);
+                 comp_info_str->str);
 }
 
 void
@@ -194,33 +222,310 @@ set_fonts(GdkFont *regular, GdkFont *bold)
 
 /* Match selected byte pattern */
 void
-match_selected_cb(GtkWidget *w, gpointer data)
+match_selected_cb_do(gpointer data, int action, gchar *text)
 {
-    char               *buf;
+    char               *ptr;
     GtkWidget          *filter_te;
 
-    filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
-
-    if (!finfo_selected) {
-       simple_dialog(ESD_TYPE_CRIT, NULL,
-                     "Error determining selected bytes.  Please make\n"
-                     "sure you have selected a field within the tree\n"
-                     "view to be matched.");
+    if (!text)
        return;
-    }
+    g_assert(data);
+    filter_te = gtk_object_get_data(GTK_OBJECT(data), E_DFILTER_TE_KEY);
+    g_assert(filter_te);
+
+    ptr = gtk_editable_get_chars(GTK_EDITABLE(filter_te),0,-1);
+
+    switch (action&MATCH_SELECTED_MASK) {
+
+    case MATCH_SELECTED_REPLACE:
+       ptr = g_strdup(text);
+       break;
+
+    case MATCH_SELECTED_AND:
+       if ((!ptr) || (0 == strlen(ptr)))
+           ptr = g_strdup(text);
+       else
+           ptr = g_strconcat("(", ptr, ") && (", text, ")", NULL);
+       break;
+
+    case MATCH_SELECTED_OR:
+       if ((!ptr) || (0 == strlen(ptr)))
+           ptr = g_strdup(text);
+       else
+           ptr = g_strconcat("(", ptr, ") || (", text, ")", NULL);
+       break;
+
+    case MATCH_SELECTED_NOT:
+       ptr = g_strconcat("!(", text, ")", NULL);
+       break;
+
+    case MATCH_SELECTED_AND_NOT:
+       if ((!ptr) || (0 == strlen(ptr)))
+           ptr = g_strconcat("!(", text, ")", NULL);
+       else
+           ptr = g_strconcat("(", ptr, ") && !(", text, ")", NULL);
+       break;
 
-    buf = proto_alloc_dfilter_string(finfo_selected, cfile.pd);
+    case MATCH_SELECTED_OR_NOT:
+       if ((!ptr) || (0 == strlen(ptr)))
+           ptr = g_strconcat("!(", text, ")", NULL);
+       else
+           ptr = g_strconcat("(", ptr, ") || !(", text, ")", NULL);
+       break;
+
+    default:
+       break;
+    }
 
     /* create a new one and set the display filter entry accordingly */
-    gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
+    gtk_entry_set_text(GTK_ENTRY(filter_te), ptr);
 
     /* Run the display filter so it goes in effect. */
-    filter_packets(&cfile, buf);
+    if (action&MATCH_SELECTED_APPLY_NOW)
+       filter_packets(&cfile, ptr);
+
+    /* Don't g_free(ptr) here. filter_packets() will do it the next time
+       it's called. */
+    g_free(text);
+}
+
+void
+match_selected_cb_replace(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+match_selected_cb_and(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+match_selected_cb_or(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
 
-    /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
+void
+match_selected_cb_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+match_selected_cb_and_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+match_selected_cb_or_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_OR_NOT,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_replace(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_REPLACE,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_and(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_AND,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_or(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_OR,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
 }
 
+void
+prepare_selected_cb_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_NOT,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
 
+void
+prepare_selected_cb_and_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_AND_NOT,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_or_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_OR_NOT,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+static gchar *
+get_text_from_packet_list(gpointer data)
+{
+    gint       row = (gint)gtk_object_get_data(GTK_OBJECT(data), E_MPACKET_LIST_ROW_KEY);
+    gint       column = (gint)gtk_object_get_data(GTK_OBJECT(data), E_MPACKET_LIST_COL_KEY);
+    frame_data *fdata = (frame_data *)gtk_clist_get_row_data(GTK_CLIST(packet_list), row);
+    epan_dissect_t *edt;
+    gchar      *buf=NULL;
+    int         len;
+
+    if (fdata != NULL) {
+       wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header,
+                      cfile.pd, fdata->cap_len);
+
+       edt = epan_dissect_new(FALSE, FALSE);
+       epan_dissect_run(edt, &cfile.pseudo_header, cfile.pd, fdata,
+                        &cfile.cinfo);
+       epan_dissect_fill_in_columns(edt);
+
+       if (strlen(cfile.cinfo.col_expr[column]) != 0 &&
+           strlen(cfile.cinfo.col_expr_val[column]) != 0) {
+           len = strlen(cfile.cinfo.col_expr[column]) +
+                 strlen(cfile.cinfo.col_expr_val[column]) + 5;
+           buf = g_malloc0(len);
+           snprintf(buf, len, "%s == %s", cfile.cinfo.col_expr[column],
+                    cfile.cinfo.col_expr_val[column]);
+       }
+
+       epan_dissect_free(edt);
+    }
+           
+    return buf;
+}
+
+void
+match_selected_cb_replace2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_and2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_or2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_and_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_or_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_replace2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_REPLACE,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_and2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_AND,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_or2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_OR,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_NOT,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_and_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_AND_NOT,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_or_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_OR_NOT,
+        get_text_from_packet_list(data));
+}
 
 /* Run the current display filter on the current packet set, and
    redisplay. */
@@ -231,8 +536,10 @@ filter_activate_cb(GtkWidget *w, gpointer data)
   GList     *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY);
   GList     *li, *nl = NULL;
   gboolean   add_filter = TRUE;
+  char *s = NULL;
   
-  char *s = gtk_entry_get_text(GTK_ENTRY(w));
+  g_assert(data);
+  s = gtk_entry_get_text(GTK_ENTRY(data));
   
   /* GtkCombos don't let us get at their list contents easily, so we maintain
      our own filter list, and feed it to gtk_combo_set_popdown_strings when
@@ -267,7 +574,6 @@ filter_reset_cb(GtkWidget *w, gpointer data)
   if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
     gtk_entry_set_text(GTK_ENTRY(filter_te), "");
   }
-
   filter_packets(&cfile, NULL);
 }
 
@@ -320,16 +626,31 @@ packet_list_compare(GtkCList *clist, gconstpointer  ptr1, gconstpointer  ptr2)
 static void 
 packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data)
 {
+  column_arrows *col_arrows = (column_arrows *) data;
+  int i;
+  
+  gtk_clist_freeze(clist);
+  
+  for (i = 0; i < cfile.cinfo.num_cols; i++) {
+    gtk_widget_hide(col_arrows[i].ascend_pm);
+    gtk_widget_hide(col_arrows[i].descend_pm);
+  }
+  
   if (column == clist->sort_column) {
-    if (clist->sort_type == GTK_SORT_ASCENDING)
+    if (clist->sort_type == GTK_SORT_ASCENDING) {
       clist->sort_type = GTK_SORT_DESCENDING;
-    else
+      gtk_widget_show(col_arrows[column].descend_pm);
+    } else {
       clist->sort_type = GTK_SORT_ASCENDING;
+      gtk_widget_show(col_arrows[column].ascend_pm);
+    }
   }
   else {
     clist->sort_type = GTK_SORT_ASCENDING;
+    gtk_widget_show(col_arrows[column].ascend_pm);
     gtk_clist_set_sort_column(clist, column);
   }
+  gtk_clist_thaw(clist);
 
   gtk_clist_sort(clist);
 }
@@ -339,15 +660,18 @@ static void
 set_frame_mark(gboolean set, frame_data *frame, gint row) {
   GdkColor fg, bg;
 
-  if (frame == NULL || row == -1) return;
-  frame->flags.marked = set;
+  if (row == -1)
+    return;
   if (set) {
+    mark_frame(&cfile, frame);
     color_t_to_gdkcolor(&fg, &prefs.gui_marked_fg);
     color_t_to_gdkcolor(&bg, &prefs.gui_marked_bg);
   } else {
+    unmark_frame(&cfile, frame);
     fg = BLACK;
     bg = WHITE;
   }
+  file_set_save_marked_sensitive();
   gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
   gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
 }
@@ -381,7 +705,6 @@ void mark_frame_cb(GtkWidget *w, gpointer data) {
 
 static void mark_all_frames(gboolean set) {
   frame_data *fdata;
-  if (cfile.plist == NULL) return;
   for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
     set_frame_mark(set,
                   fdata,
@@ -413,32 +736,47 @@ void unmark_all_frames_cb(GtkWidget *w, gpointer data) {
 static void
 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
 
-  blank_packetinfo();
+/* Remove the hex display tabbed pages */
+  while( (gtk_notebook_get_nth_page( GTK_NOTEBOOK(byte_nb_ptr), 0)))
+    gtk_notebook_remove_page( GTK_NOTEBOOK(byte_nb_ptr), 0);
+
   select_packet(&cfile, row);
 }
 
+
 static void
 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
+
   unselect_packet(&cfile);
 }
 
+
 static void
 tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
 {
        field_info      *finfo;
        gchar           *help_str = NULL;
        gboolean        has_blurb = FALSE;
-       guint           length = 0;
+       guint           length = 0, byte_len;
+       GtkWidget       *byte_view;
+       guint8          *byte_data;
 
        g_assert(node);
        finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
        if (!finfo) return;
 
-       finfo_selected = finfo;
+       if (finfo->ds_name != NULL)
+               set_notebook_page(  byte_nb_ptr, find_notebook_page( byte_nb_ptr, finfo->ds_name));
 
+        byte_view = gtk_object_get_data(GTK_OBJECT(byte_nb_ptr), E_BYTE_VIEW_TEXT_INFO_KEY);
+        byte_data = gtk_object_get_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_DATA_PTR_KEY);
+        byte_len = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_DATA_LEN_KEY));
+
+       g_assert(byte_data);
+
+       finfo_selected = finfo;
        set_menus_for_selected_tree_row(TRUE);
 
-       /*if (finfo->hfinfo && finfo->hfinfo->type != FT_TEXT_ONLY) {*/
        if (finfo->hfinfo) {
          if (finfo->hfinfo->blurb != NULL && 
              finfo->hfinfo->blurb[0] != '\0') {
@@ -447,103 +785,83 @@ tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user
          } else {
            length = strlen(finfo->hfinfo->name);
          }
-         length += strlen(finfo->hfinfo->abbrev) + 10;
-         help_str = g_malloc(sizeof(gchar) * length);
-         sprintf(help_str, "%s (%s)", 
-                 (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name,
-                 finfo->hfinfo->abbrev);
-         gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, help_str);
-         g_free(help_str);
+         statusbar_pop_field_msg();    /* get rid of current help msg */
+          if (length) {
+           length += strlen(finfo->hfinfo->abbrev) + 10;
+           help_str = g_malloc(sizeof(gchar) * length);
+           sprintf(help_str, "%s (%s)", 
+              (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name,
+              finfo->hfinfo->abbrev);
+           statusbar_push_field_msg(help_str);
+           g_free(help_str);
+          } else {
+            /*
+            * Don't show anything if the field name is zero-length;
+            * the pseudo-field for "proto_tree_add_text()" is such
+            * a field, and we don't want "Text (text)" showing up
+            * on the status line if you've selected such a field.
+            *
+            * XXX - there are zero-length fields for which we *do*
+            * want to show the field name.
+            *
+            * XXX - perhaps the name and abbrev field should be null
+            * pointers rather than null strings for that pseudo-field,
+            * but we'd have to add checks for null pointers in some
+            * places if we did that.
+            *
+            * Or perhaps protocol tree items added with
+            * "proto_tree_add_text()" should have -1 as the field index,
+            * with no pseudo-field being used, but that might also
+            * require special checks for -1 to be added.
+            */
+           statusbar_push_field_msg("");
+          }
        }
 
-       packet_hex_print(GTK_TEXT(byte_view), cfile.pd, cfile.current_frame,
-               finfo);
+       packet_hex_print(GTK_TEXT(byte_view), byte_data, cfile.current_frame,
+               finfo, byte_len);
 }
 
 static void
 tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
 {
-       gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
-       finfo_selected = NULL;
-       set_menus_for_selected_tree_row(FALSE);
-       packet_hex_print(GTK_TEXT(byte_view), cfile.pd, cfile.current_frame,
-               NULL);
+       GtkWidget       *byte_view;
+       guint8  *data;
+       gint    len;    
+
+       /*
+        * Which byte view is displaying the current protocol tree
+        * row's data?
+        */
+       len = get_byte_view_and_data( byte_nb_ptr, &byte_view, &data);
+       if ( len < 0)
+               return; /* none */
+
+       unselect_field();
+       packet_hex_print(GTK_TEXT(byte_view), data, cfile.current_frame,
+               NULL, len);
 }
 
 void collapse_all_cb(GtkWidget *widget, gpointer data) {
-  if (cfile.protocol_tree)
-    collapse_all_tree(cfile.protocol_tree, tree_view);
+  if (cfile.edt->tree)
+    collapse_all_tree(cfile.edt->tree, tree_view);
 }
 
 void expand_all_cb(GtkWidget *widget, gpointer data) {
-  if (cfile.protocol_tree)
-    expand_all_tree(cfile.protocol_tree, tree_view);
+  if (cfile.edt->tree)
+    expand_all_tree(cfile.edt->tree, tree_view);
 }
 
 void resolve_name_cb(GtkWidget *widget, gpointer data) {
-  if (cfile.protocol_tree) {
-    int tmp = g_resolving_actif;
-    g_resolving_actif = 1;
+  if (cfile.edt->tree) {
+    guint32 tmp = g_resolv_flags;
+    g_resolv_flags = RESOLV_ALL;
     gtk_clist_clear ( GTK_CLIST(tree_view) );
-    proto_tree_draw(cfile.protocol_tree, tree_view);
-    g_resolving_actif = tmp;
+    proto_tree_draw(cfile.edt->tree, tree_view);
+    g_resolv_flags = tmp;
   }
 }
 
-/* Set the scrollbar placement of a scrolled window based upon pos value:
-   0 = left, 1 = right */
-void
-set_scrollbar_placement_scrollw(GtkWidget *scrollw, int pos) /* 0=left, 1=right */
-{
-       if (pos) {
-               gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
-                               GTK_CORNER_TOP_LEFT);
-       } else {
-               gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
-                               GTK_CORNER_TOP_RIGHT);
-       }
-}
-
-/* List of all scrolled windows, so we can globally set the scrollbar
-   placement of them. */
-static GList *scrolled_windows;
-
-/* Add a scrolled window to the list of scrolled windows. */
-static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
-
-void
-remember_scrolled_window(GtkWidget *scrollw)
-{
-  scrolled_windows = g_list_append(scrolled_windows, scrollw);
-
-  /* Catch the "destroy" event on the widget, so that we remove it from
-     the list when it's destroyed. */
-  gtk_signal_connect(GTK_OBJECT(scrollw), "destroy",
-                    GTK_SIGNAL_FUNC(forget_scrolled_window), NULL);
-}
-
-/* Remove a scrolled window from the list of scrolled windows. */
-static void
-forget_scrolled_window(GtkWidget *scrollw, gpointer data)
-{
-  scrolled_windows = g_list_remove(scrolled_windows, scrollw);
-}
-
-static void
-set_scrollbar_placement_cb(gpointer data, gpointer user_data)
-{
-       set_scrollbar_placement_scrollw((GtkWidget *)data,
-           *(int *)user_data);
-}
-
-/* Set the scrollbar placement of all scrolled windows based on pos value:
-   0 = left, 1 = right */
-void
-set_scrollbar_placement_all(int pos)
-{
-       g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, &pos);
-}
-
 /* Set the selection mode of the packet list window. */
 void
 set_plist_sel_browse(gboolean val)
@@ -606,17 +924,48 @@ set_plist_font(GdkFont *font)
        }
 }
 
-static gboolean
-main_window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
+/*
+ * Push a message referring to file access onto the statusbar.
+ */
+void
+statusbar_push_file_msg(gchar *msg)
 {
-       file_quit_cmd_cb(widget, data);
+       gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg);
+}
 
-       /* Say that the window should be deleted. */
-       return FALSE;
+/*
+ * Pop a message referring to file access off the statusbar.
+ */
+void
+statusbar_pop_file_msg(void)
+{
+       gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
 }
 
+/*
+ * XXX - do we need multiple statusbar contexts?
+ */
+
+/*
+ * Push a message referring to the currently-selected field onto the statusbar.
+ */
 void
-file_quit_cmd_cb (GtkWidget *widget, gpointer data)
+statusbar_push_field_msg(gchar *msg)
+{
+       gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg);
+}
+
+/*
+ * Pop a message referring to the currently-selected field off the statusbar.
+ */
+void
+statusbar_pop_field_msg(void)
+{
+       gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
+}
+
+static gboolean
+do_quit(void)
 {
        /* XXX - should we check whether the capture file is an
           unsaved temporary file for a live capture and, if so,
@@ -625,9 +974,19 @@ file_quit_cmd_cb (GtkWidget *widget, gpointer data)
           box to forcibly quit if the user clicks "OK"?
 
           If so, note that this should be done in a subroutine that
-          returns TRUE if we do so, and FALSE otherwise, and that
-          "main_window_delete_event_cb()" should return its
-          return value. */
+          returns TRUE if we do so, and FALSE otherwise, and if it
+          returns TRUE we should return TRUE without nuking anything.
+
+          Note that, if we do that, we might also want to check if
+          an "Update list of packets in real time" capture is in
+          progress and, if so, ask whether they want to terminate
+          the capture and discard it, and return TRUE, before nuking
+          any child capture, if they say they don't want to do so. */
+
+#ifdef HAVE_LIBPCAP
+       /* Nuke any child capture in progress. */
+       kill_capture_child();
+#endif
 
        /* Are we in the middle of reading a capture? */
        if (cfile.state == FILE_READ_IN_PROGRESS) {
@@ -638,6 +997,10 @@ file_quit_cmd_cb (GtkWidget *widget, gpointer data)
                   will check for that and, if it sees that, will clean
                   up and quit. */
                cfile.state = FILE_READ_ABORTED;
+
+               /* Say that the window should *not* be deleted;
+                  that'll be done by the code that cleans up. */
+               return TRUE;
        } else {
                /* Close any capture file we have open; on some OSes, you
                   can't unlink a temporary capture file if you have it
@@ -659,32 +1022,67 @@ file_quit_cmd_cb (GtkWidget *widget, gpointer data)
                   which we'd call here, and another routine that
                   calls that routine and also cleans up the UI, which
                   we'd call elsewhere? */
-               close_cap_file(&cfile, info_bar);
+               close_cap_file(&cfile);
 
                /* Exit by leaving the main loop, so that any quit functions
                   we registered get called. */
                gtk_main_quit();
+
+               /* Say that the window should be deleted. */
+               return FALSE;
        }
 }
 
+static gboolean
+main_window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+       gint desk_x, desk_y;
+
+       /* Try to grab our geometry */
+       gdk_window_get_root_origin(top_level->window, &root_x, &root_y);
+       if (gdk_window_get_deskrelative_origin(top_level->window,
+                               &desk_x, &desk_y)) {
+               if (desk_x <= root_x && desk_y <= root_y) {
+                       root_x = desk_x;
+                       root_y = desk_y;
+               }
+       }
+
+       /* XXX - Is this the "approved" method? */
+       gdk_window_get_size(top_level->window, &top_width, &top_height);
+
+       /* "do_quit()" indicates whether the main window should be deleted. */
+       return do_quit();
+}
+
+void
+file_quit_cmd_cb (GtkWidget *widget, gpointer data)
+{
+       do_quit();
+}
+
 static void 
 print_usage(void) {
 
-  fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled with %s\n",
-         comp_info_str);
+  fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled %s\n",
+         comp_info_str->str);
 #ifdef HAVE_LIBPCAP
-  fprintf(stderr, "%s [ -vh ] [ -kpQS ] [ -B <byte view height> ] [ -c count ]\n",
+  fprintf(stderr, "%s [ -vh ] [ -klpQS ] [ -a <capture autostop condition> ] ...\n",
          PACKAGE);
-  fprintf(stderr, "\t[ -f <capture filter> ] [ -i interface ] [ -m <medium font> ] \n");
-  fprintf(stderr, "\t[ -n ] [ -o <preference setting> ] ... [ -P <packet list height> ]\n");
-  fprintf(stderr, "\t[ -r infile ] [ -R <read filter> ] [ -s snaplen ] \n");
-  fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ] [ -w savefile ]\n");
+  fprintf(stderr, "\t[ -b <number of ringbuffer files> ] [ -B <byte view height> ]\n");
+  fprintf(stderr, "\t[ -c <count> ] [ -f <capture filter> ] [ -i <interface> ]\n");
+  fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -N <resolving> ]\n");
+  fprintf(stderr, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
+  fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
+  fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
+  fprintf(stderr, "\t[ -w <savefile> ] [ <infile> ]\n");
 #else
-  fprintf(stderr, "%s [ -vh ] [ -B <byte view height> ] [ -m <medium font> ] [ -n ]\n",
+  fprintf(stderr, "%s [ -vh ] [ -B <byte view height> ] [ -m <medium font> ]\n",
          PACKAGE);
+  fprintf(stderr, "\t[ -n ] [ -N <resolving> ]\n");
   fprintf(stderr, "\t[ -o <preference setting> ... [ -P <packet list height> ]\n");
-  fprintf(stderr, "\t[ -r infile ] [ -R <read filter> ] [ -t <time stamp format> ]\n");
-  fprintf(stderr, "\t[ -T <tree view height> ]\n");
+  fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -t <time stamp format> ]\n");
+  fprintf(stderr, "\t[ -T <tree view height> ] [ <infile> ]\n");
 #endif
 }
 
@@ -695,9 +1093,81 @@ show_version(void)
   create_console();
 #endif
 
-  printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
+  printf("%s %s, %s\n", PACKAGE, VERSION, comp_info_str->str);
 }
 
+static int
+get_positive_int(const char *string, const char *name)
+{
+  long number;
+  char *p;
+
+  number = strtol(string, &p, 10);
+  if (p == string || *p != '\0') {
+    fprintf(stderr, "ethereal: The specified %s \"%s\" is not a decimal number\n",
+           name, string);
+    exit(1);
+  }
+  if (number < 0) {
+    fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
+           name, string);
+    exit(1);
+  }
+  if (number > INT_MAX) {
+    fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
+           name, string, INT_MAX);
+    exit(1);
+  }
+  return number;
+}
+
+#ifdef HAVE_LIBPCAP
+/*
+ * Given a string of the form "<autostop criterion>:<value>", as might appear
+ * as an argument to a "-a" option, parse it and set the criterion in
+ * question.  Return an indication of whether it succeeded or failed
+ * in some fashion.
+ */
+static gboolean
+set_autostop_criterion(const char *autostoparg)
+{
+  u_char *p, *colonp;
+
+  colonp = strchr(autostoparg, ':');
+  if (colonp == NULL)
+    return FALSE;
+
+  p = colonp;
+  *p++ = '\0';
+
+  /*
+   * Skip over any white space (there probably won't be any, but
+   * as we allow it in the preferences file, we might as well
+   * allow it here).
+   */
+  while (isspace(*p))
+    p++;
+  if (*p == '\0') {
+    /*
+     * Put the colon back, so if our caller uses, in an
+     * error message, the string they passed us, the message
+     * looks correct.
+     */
+    *colonp = ':';
+    return FALSE;
+  }
+  if (strcmp(autostoparg,"duration") == 0) {
+    cfile.autostop_duration = get_positive_int(p,"autostop duration");
+  } else if (strcmp(autostoparg,"filesize") == 0) {
+    cfile.autostop_filesize = get_positive_int(p,"autostop filesize");
+  } else {
+    return FALSE;
+  }
+  *colonp = ':'; /* put the colon back */
+  return TRUE;
+}
+#endif
+
 /* And now our feature presentation... [ fade to music ] */
 int
 main(int argc, char *argv[])
@@ -710,20 +1180,18 @@ main(int argc, char *argv[])
   int                  opt;
   extern char         *optarg;
   gboolean             arg_error = FALSE;
-
 #ifdef HAVE_LIBPCAP
-#ifdef WIN32
-  char pcap_version[] = "0.4a6";
-#else
+#ifdef HAVE_PCAP_VERSION
   extern char          pcap_version[];
-#endif
-#endif
+#endif /* HAVE_PCAP_VERSION */
+#endif /* HAVE_LIBPCAP */
   
 #ifdef WIN32
   WSADATA             wsaData; 
 #endif
 
-  char                *gpf_path, *pf_path, *cf_path, *df_path;
+  char                *gpf_path, *cf_path, *df_path;
+  const char          *pf_path;
   int                  gpf_open_errno, pf_open_errno, cf_open_errno, df_open_errno;
   int                  err;
 #ifdef HAVE_LIBPCAP
@@ -741,7 +1209,11 @@ main(int argc, char *argv[])
   dfilter_t           *rfcode = NULL;
   gboolean             rfilter_parse_failed = FALSE;
   e_prefs             *prefs;
+  char                 badopt;
   char                *bold_font_name;
+  gint                 desk_x, desk_y;
+  gboolean             prefs_write_needed = FALSE;
+
 
   ethereal_path = argv[0];
 
@@ -774,7 +1246,7 @@ main(int argc, char *argv[])
      "-G" flag, as the "-G" flag dumps a list of fields registered
      by the dissectors, and we must do it before we read the preferences,
      in case any dissectors register preferences. */
-  epan_init(PLUGIN_DIR);
+  epan_init(PLUGIN_DIR,register_all_protocols,register_all_protocol_handoffs);
 
   /* Now register the preferences for any non-dissector modules.
      We must do that before we read the preferences as well. */
@@ -816,6 +1288,30 @@ main(int argc, char *argv[])
   /* Read the preference files. */
   prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
 
+#ifdef HAVE_LIBPCAP
+  /* If this is a capture child process, it should pay no attention
+     to the "prefs.capture_prom_mode" setting in the preferences file;
+     it should do what the parent process tells it to do, and if
+     the parent process wants it not to run in promiscuous mode, it'll
+     tell it so with a "-p" flag.
+
+     Otherwise, set promiscuous mode from the preferences setting. */
+  if (capture_child)
+    promisc_mode = TRUE;
+  else
+    promisc_mode = prefs->capture_prom_mode;
+
+  /* Set "Update list of packets in real time" mode from the preferences
+     setting. */
+  sync_mode = prefs->capture_real_time;
+
+  /* And do the same for "Automatic scrolling in live capture" mode. */
+  auto_scroll_live = prefs->capture_auto_scroll;
+#endif
+
+  /* Set the name resolution code's flags from the preferences. */
+  g_resolv_flags = prefs->name_resolve;
+
   /* Read the capture filter file. */
   read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
 
@@ -840,60 +1336,105 @@ main(int argc, char *argv[])
   cfile.save_file_fd   = -1;
   cfile.snap           = WTAP_MAX_PACKET_SIZE;
   cfile.count          = 0;
+#ifdef HAVE_LIBPCAP
+  cfile.autostop_duration = 0;
+  cfile.autostop_filesize = 0;
+  cfile.ringbuffer_on = FALSE;
+  cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
+#endif
   col_init(&cfile.cinfo, prefs->num_cols);
 
   /* Assemble the compile-time options */
-  snprintf(comp_info_str, 256,
+  comp_info_str = g_string_new("");
+
+  g_string_append(comp_info_str, "with ");
+  g_string_sprintfa(comp_info_str,
 #ifdef GTK_MAJOR_VERSION
-    "GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
-    GTK_MICRO_VERSION,
+    "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
+    GTK_MICRO_VERSION);
 #else
-    "GTK+ (version unknown), %s%s, %s%s, %s%s",
+    "GTK+ (version unknown)");
 #endif
 
-#ifdef HAVE_LIBPCAP
-   "with libpcap ", pcap_version,
+  g_string_append(comp_info_str, ", with ");
+  g_string_sprintfa(comp_info_str,
+#ifdef GLIB_MAJOR_VERSION
+    "GLib %d.%d.%d", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION,
+    GLIB_MICRO_VERSION);
 #else
-   "without libpcap", "",
+    "GLib (version unknown)");
 #endif
 
+#ifdef HAVE_LIBPCAP
+  g_string_append(comp_info_str, ", with libpcap ");
+#ifdef HAVE_PCAP_VERSION
+  g_string_append(comp_info_str, pcap_version);
+#else /* HAVE_PCAP_VERSION */
+  g_string_append(comp_info_str, "(version unknown)");
+#endif /* HAVE_PCAP_VERSION */
+#else /* HAVE_LIBPCAP */
+  g_string_append(comp_info_str, ", without libpcap");
+#endif /* HAVE_LIBPCAP */
+
 #ifdef HAVE_LIBZ
+  g_string_append(comp_info_str, ", with libz ");
 #ifdef ZLIB_VERSION
-   "with libz ", ZLIB_VERSION,
+  g_string_append(comp_info_str, ZLIB_VERSION);
 #else /* ZLIB_VERSION */
-   "with libz ", "(version unknown)",
+  g_string_append(comp_info_str, "(version unknown)");
 #endif /* ZLIB_VERSION */
 #else /* HAVE_LIBZ */
-   "without libz", "",
+  g_string_append(comp_info_str, ", without libz");
 #endif /* HAVE_LIBZ */
 
 /* Oh, this is pretty */
 #if defined(HAVE_UCD_SNMP_SNMP_H)
+  g_string_append(comp_info_str, ", with UCD SNMP ");
 #ifdef HAVE_UCD_SNMP_VERSION_H
-   "with UCD SNMP ", VersionInfo
+  g_string_append(comp_info_str, VersionInfo);
 #else /* HAVE_UCD_SNMP_VERSION_H */
-   "with UCD SNMP ", "(version unknown)"
+  g_string_append(comp_info_str, "(version unknown)");
 #endif /* HAVE_UCD_SNMP_VERSION_H */
 #elif defined(HAVE_SNMP_SNMP_H)
+  g_string_append(comp_info_str, ", with CMU SNMP ");
 #ifdef HAVE_SNMP_VERSION_H
-   "with CMU SNMP ", snmp_Version()
+  g_string_append(comp_info_str, snmp_Version());
 #else /* HAVE_SNMP_VERSION_H */
-   "with CMU SNMP ", "(version unknown)"
+  g_string_append(comp_info_str, "(version unknown)");
 #endif /* HAVE_SNMP_VERSION_H */
 #else /* no SNMP */
-   "without SNMP", ""
+  g_string_append(comp_info_str, ", without SNMP");
 #endif
-   );
 
   /* Now get our args */
-  while ((opt = getopt(argc, argv, "B:c:Df:hi:km:no:pP:Qr:R:Ss:t:T:w:W:vZ:")) != EOF) {
+  while ((opt = getopt(argc, argv, "a:b:B:c:f:hi:klm:nN:o:pP:Qr:R:Ss:t:T:w:W:vZ:")) !=  EOF) {
     switch (opt) {
+      case 'a':        /* autostop criteria */
+#ifdef HAVE_LIBPCAP
+        if (set_autostop_criterion(optarg) == FALSE) {
+          fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
+          exit(1);          
+        }
+#else
+        capture_option_specified = TRUE;
+        arg_error = TRUE;
+#endif
+        break;
+      case 'b':        /* Ringbuffer option */
+#ifdef HAVE_LIBPCAP
+        cfile.ringbuffer_on = TRUE;
+        cfile.ringbuffer_num_files = get_positive_int(optarg, "number of ring buffer files");
+#else
+        capture_option_specified = TRUE;
+        arg_error = TRUE;
+#endif
+        break;
       case 'B':        /* Byte view pane height */
-        bv_size = atoi(optarg);
+        bv_size = get_positive_int(optarg, "byte view pane height");
         break;
       case 'c':        /* Capture xxx packets */
 #ifdef HAVE_LIBPCAP
-        cfile.count = atoi(optarg);
+        cfile.count = get_positive_int(optarg, "packet count");
 #else
         capture_option_specified = TRUE;
         arg_error = TRUE;
@@ -927,16 +1468,34 @@ main(int argc, char *argv[])
 #else
         capture_option_specified = TRUE;
         arg_error = TRUE;
+#endif
+        break;
+      case 'l':        /* Automatic scrolling in live capture mode */
+#ifdef HAVE_LIBPCAP
+        auto_scroll_live = TRUE;
+#else
+        capture_option_specified = TRUE;
+        arg_error = TRUE;
 #endif
         break;
       case 'm':        /* Fixed-width font for the display */
         if (prefs->gui_font_name != NULL)
           g_free(prefs->gui_font_name);
-       prefs->gui_font_name = g_strdup(optarg);
-       break;
+        prefs->gui_font_name = g_strdup(optarg);
+        break;
       case 'n':        /* No name resolution */
-       g_resolving_actif = 0;
-       break;
+        g_resolv_flags = RESOLV_NONE;
+        break;
+      case 'N':        /* Select what types of addresses/port #s to resolve */
+        if (g_resolv_flags == RESOLV_ALL)
+          g_resolv_flags = RESOLV_NONE;
+        badopt = string_to_name_resolve(optarg, &g_resolv_flags);
+        if (badopt != '\0') {
+          fprintf(stderr, "ethereal: -N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'\n",
+                       badopt);
+          exit(1);
+        }
+        break;
       case 'o':        /* Override preference from command line */
         switch (prefs_set_pref(optarg)) {
 
@@ -946,6 +1505,7 @@ main(int argc, char *argv[])
           break;
 
         case PREFS_SET_NO_SUCH_PREF:
+        case PREFS_SET_OBSOLETE:
           fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
                        optarg);
           exit(1);
@@ -954,14 +1514,14 @@ main(int argc, char *argv[])
         break;
       case 'p':        /* Don't capture in promiscuous mode */
 #ifdef HAVE_LIBPCAP
-       promisc_mode = 0;
+       promisc_mode = FALSE;
 #else
         capture_option_specified = TRUE;
         arg_error = TRUE;
 #endif
        break;
       case 'P':        /* Packet list pane height */
-        pl_size = atoi(optarg);
+        pl_size = get_positive_int(optarg, "packet list pane height");
         break;
       case 'Q':        /* Quit after capture (just capture to file) */
 #ifdef HAVE_LIBPCAP
@@ -983,7 +1543,7 @@ main(int argc, char *argv[])
         break;
       case 's':        /* Set the snapshot (capture) length */
 #ifdef HAVE_LIBPCAP
-        cfile.snap = atoi(optarg);
+        cfile.snap = get_positive_int(optarg, "snapshot length");
 #else
         capture_option_specified = TRUE;
         arg_error = TRUE;
@@ -1015,7 +1575,7 @@ main(int argc, char *argv[])
         }
         break;
       case 'T':        /* Tree view pane height */
-        tv_size = atoi(optarg);
+        tv_size = get_positive_int(optarg, "tree view pane height");
         break;
       case 'v':        /* Show version and exit */
         show_version();
@@ -1064,8 +1624,66 @@ main(int argc, char *argv[])
         break;
     }
   }
+  argc -= optind;
+  argv += optind;
+  if (argc >= 1) {
+    if (cf_name != NULL) {
+      /*
+       * Input file name specified with "-r" *and* specified as a regular
+       * command-line argument.
+       */
+      arg_error = TRUE;
+    } else {
+      /*
+       * Input file name not specified with "-r", and a command-line argument
+       * was specified; treat it as the input file name.
+       *
+       * Yes, this is different from tethereal, where non-flag command-line
+       * arguments are a filter, but this works better on GUI desktops
+       * where a command can be specified to be run to open a particular
+       * file - yes, you could have "-r" as the last part of the command,
+       * but that's a bit ugly.
+       */
+      cf_name = g_strdup(argv[0]);
+    }
+    argc--;
+    argv++;
+  }
+
+  if (argc != 0) {
+    /*
+     * Extra command line arguments were specified; complain.
+     */
+    arg_error = TRUE;
+  }
+
+#ifdef HAVE_LIBPCAP
+  if (cfile.ringbuffer_on) {
+    /* Ring buffer works only under certain conditions:
+       a) ring buffer does not work with temporary files;
+       b) sync_mode and cfile.ringbuffer_on are mutually exclusive -
+          sync_mode takes precedence;
+       c) it makes no sense to enable the ring buffer if the maximum
+          file size is set to "infinite". */
+    if (cfile.save_file == NULL) {
+      fprintf(stderr, "ethereal: Ring buffer requested, but capture isn't being saved to a permanent file.\n");
+      cfile.ringbuffer_on = FALSE;
+    }
+    if (sync_mode) {
+      fprintf(stderr, "ethereal: Ring buffer requested, but an \"Update list of packets in real time\" capture is being done.\n");
+      cfile.ringbuffer_on = FALSE;
+    }
+    if (cfile.autostop_filesize == 0) {
+      fprintf(stderr, "ethereal: Ring buffer requested, but no maximum capture file size was specified.\n");
+      cfile.ringbuffer_on = FALSE;
+    }
+  }
+#endif
 
 #ifdef WIN32
+  /* Load wpcap if possible */
+  load_wpcap();
+
   /* Start windows sockets */
   WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
 #endif
@@ -1128,15 +1746,24 @@ main(int argc, char *argv[])
       cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
     else
       cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
+    cfile.cinfo.col_expr[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
+    cfile.cinfo.col_expr_val[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
   }
 
+#ifdef HAVE_LIBPCAP
   if (cfile.snap < 1)
     cfile.snap = WTAP_MAX_PACKET_SIZE;
   else if (cfile.snap < MIN_PACKET_SIZE)
     cfile.snap = MIN_PACKET_SIZE;
   
-  rc_file = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(RC_FILE) + 4);
-  sprintf(rc_file, "%s/%s", get_home_dir(), RC_FILE);
+  /* Check the value range of the ringbuffer_num_files parameter */
+  if (cfile.ringbuffer_num_files < RINGBUFFER_MIN_NUM_FILES)
+    cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
+  else if (cfile.ringbuffer_num_files > RINGBUFFER_MAX_NUM_FILES)
+    cfile.ringbuffer_num_files = RINGBUFFER_MAX_NUM_FILES;
+#endif
+  
+  rc_file = get_persconffile_path(RC_FILE, FALSE);
   gtk_rc_parse(rc_file);
 
   /* Try to load the regular and boldface fixed-width fonts */
@@ -1232,8 +1859,7 @@ main(int argc, char *argv[])
              good thing, given that "get_dirname()" does write over its
              argument. */
           s = get_dirname(cf_name);
-          if (s != NULL)
-            last_open_dir = s;
+         set_last_open_dir(s);
         } else {
           if (rfcode != NULL)
             dfilter_free(rfcode);
@@ -1310,6 +1936,66 @@ main(int argc, char *argv[])
 
   gtk_main();
 
+       /* Try to save our geometry.  GTK+ provides two routines to get a
+                window's position relative to the X root window.  If I understand the
+                documentation correctly, gdk_window_get_deskrelative_origin applies
+                mainly to Enlightenment and gdk_window_get_root_origin applies for
+                all other WMs.
+          
+          The code below tries both routines, and picks the one that returns
+          the upper-left-most coordinates.
+          
+          More info at:
+
+          http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html
+          http://www.gtk.org/faq/#AEN600 */
+
+       /* Re-read our saved preferences. */
+       /* XXX - Move all of this into a separate function? */
+       prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
+
+       if (pf_path == NULL) {
+               if (prefs->gui_geometry_save_position) {
+                       if (top_level->window != NULL) {
+                               gdk_window_get_root_origin(top_level->window, &root_x, &root_y);
+                               if (gdk_window_get_deskrelative_origin(top_level->window,
+                                                       &desk_x, &desk_y)) {
+                                       if (desk_x <= root_x && desk_y <= root_y) {
+                                               root_x = desk_x;
+                                               root_y = desk_y;
+                                       }
+                               }
+                       }
+                       if (prefs->gui_geometry_main_x != root_x) {
+                               prefs->gui_geometry_main_x = root_x;
+                               prefs_write_needed = TRUE;
+                       }
+                       if (prefs->gui_geometry_main_y != root_y) {
+                               prefs->gui_geometry_main_y = root_y;
+                               prefs_write_needed = TRUE;
+                       }
+               }
+               
+               if (prefs->gui_geometry_save_size) {
+                       if (top_level->window != NULL) {
+                               /* XXX - Is this the "approved" method? */
+                               gdk_window_get_size(top_level->window, &top_width, &top_height);
+                       }
+                       if (prefs->gui_geometry_main_width != top_width) {
+                               prefs->gui_geometry_main_width = top_width;
+                               prefs_write_needed = TRUE;
+                       }
+                       if (prefs->gui_geometry_main_height != top_height) {
+                               prefs->gui_geometry_main_height = top_height;
+                               prefs_write_needed = TRUE;
+                       }
+               }
+               
+               if (prefs_write_needed) {
+                       write_prefs(&pf_path);
+               }
+       }
+       
   epan_cleanup();
   g_free(rc_file);
 
@@ -1447,7 +2133,7 @@ boldify(const char *font_name)
 {
        char *bold_font_name;
        gchar **xlfd_tokens;
-       int i;
+       unsigned int i;
 
        /* Is this an XLFD font?  If it begins with "-", yes, otherwise no. */
        if (font_name[0] == '-') {
@@ -1475,11 +2161,16 @@ static void
 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
 {
   GtkWidget           *main_vbox, *menubar, *u_pane, *l_pane,
-                      *stat_hbox,
+                      *stat_hbox, *column_lb,
                       *filter_bt, *filter_cm, *filter_te,
+                      *filter_apply,
                       *filter_reset;
   GList               *filter_list = NULL;
   GtkAccelGroup       *accel;
+  GtkStyle            *win_style;
+  GdkBitmap           *ascend_bm, *descend_bm;
+  GdkPixmap           *ascend_pm, *descend_pm;
+  column_arrows       *col_arrows;
   int                  i;
   /* Display filter construct dialog has an Apply button, and "OK" not
      only sets our text widget, it activates it (i.e., it causes us to
@@ -1489,14 +2180,25 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
        TRUE,
        TRUE
   };
-
+  
   /* Main window */  
   top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   gtk_widget_set_name(top_level, "main window");
   gtk_signal_connect(GTK_OBJECT(top_level), "delete_event", 
     GTK_SIGNAL_FUNC(main_window_delete_event_cb), NULL);
+  gtk_signal_connect (GTK_OBJECT (top_level), "realize",
+    GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
   gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
-  gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
+  if (prefs->gui_geometry_save_position) {
+    gtk_widget_set_uposition(GTK_WIDGET(top_level),
+      prefs->gui_geometry_main_x, prefs->gui_geometry_main_y);
+  }
+  if (prefs->gui_geometry_save_size) {
+    gtk_widget_set_usize(GTK_WIDGET(top_level),
+      prefs->gui_geometry_main_width, prefs->gui_geometry_main_height);
+  } else {
+    gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
+  }
   gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
 
   /* Container for menu bar, paned windows and progress/info box */
@@ -1522,22 +2224,23 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
   gtk_widget_show(u_pane);
 
   /* Packet list */
-  pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
+  pkt_scrollw = scrolled_window_new(NULL, NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-  set_scrollbar_placement_scrollw(pkt_scrollw, prefs->gui_scrollbar_on_right);
-  remember_scrolled_window(pkt_scrollw);
   gtk_widget_show(pkt_scrollw);
   gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
 
-  packet_list = gtk_clist_new_with_titles(cfile.cinfo.num_cols, cfile.cinfo.col_title);
+  packet_list = gtk_clist_new(cfile.cinfo.num_cols);
+  /* Column titles are filled in below */
   gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
+
+  col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * cfile.cinfo.num_cols);
   
   set_plist_sel_browse(prefs->gui_plist_sel_browse);
   set_plist_font(m_r_font);
   gtk_widget_set_name(packet_list, "packet list");
   gtk_signal_connect (GTK_OBJECT (packet_list), "click_column",
-    GTK_SIGNAL_FUNC(packet_list_click_column_cb), NULL);
+    GTK_SIGNAL_FUNC(packet_list_click_column_cb), col_arrows);
   gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
     GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
   gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
@@ -1576,9 +2279,13 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
   gtk_widget_show(tree_view);
 
   /* Byte view. */
-  create_byte_view(bv_size, l_pane, &byte_view, &bv_scrollw,
+  create_byte_view(bv_size, l_pane, &byte_nb_ptr, &bv_scrollw,
                        prefs->gui_scrollbar_on_right);
 
+  gtk_signal_connect(GTK_OBJECT(byte_nb_ptr), "button_press_event",
+                    GTK_SIGNAL_FUNC(popup_menu_handler),
+                    gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_HEXDUMP_KEY));
+
   /* Filter/info box */
   stat_hbox = gtk_hbox_new(FALSE, 1);
   gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
@@ -1601,24 +2308,45 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
   gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
-    GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
+    GTK_SIGNAL_FUNC(filter_activate_cb), filter_te);
   gtk_widget_show(filter_cm);
 
   filter_reset = gtk_button_new_with_label("Reset");
   gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
   gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
-                    GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
+                    GTK_SIGNAL_FUNC(filter_reset_cb), NULL);
   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
   gtk_widget_show(filter_reset);
 
+  filter_apply = gtk_button_new_with_label("Apply");
+  gtk_object_set_data(GTK_OBJECT(filter_apply), E_DFILTER_CM_KEY, filter_cm);
+  gtk_object_set_data(GTK_OBJECT(filter_apply), E_DFILTER_FL_KEY, filter_list);
+  gtk_signal_connect(GTK_OBJECT(filter_apply), "clicked",
+                     GTK_SIGNAL_FUNC(filter_activate_cb), filter_te);
+  gtk_box_pack_start(GTK_BOX(stat_hbox), filter_apply, FALSE, TRUE, 1);
+  gtk_widget_show(filter_apply);
+
   /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
    * of any widget that ends up calling a callback which needs
    * that text entry pointer */
   set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
   set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
   set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
-  set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
   set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/And Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/Or Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/And Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/Or Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/And Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/Or Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/And Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/Or Not Selected", E_DFILTER_TE_KEY, filter_te);
+  gtk_object_set_data(GTK_OBJECT(popup_menu_object), E_DFILTER_TE_KEY, filter_te);
+  gtk_object_set_data(GTK_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY, packet_list);
 
   info_bar = gtk_statusbar_new();
   main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
@@ -1629,6 +2357,54 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
   gtk_widget_show(info_bar);
 
   gtk_widget_show(top_level);
+
+  /* Fill in column titles.  This must be done after the top level window
+     is displayed. */
+  win_style = gtk_widget_get_style(top_level);
+  ascend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &ascend_bm,
+       &win_style->bg[GTK_STATE_NORMAL], (gchar **)clist_ascend_xpm);
+  descend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &descend_bm,
+       &win_style->bg[GTK_STATE_NORMAL], (gchar **)clist_descend_xpm);
+  for (i = 0; i < cfile.cinfo.num_cols; i++) {
+    col_arrows[i].table = gtk_table_new(2, 2, FALSE);
+    gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
+    column_lb = gtk_label_new(cfile.cinfo.col_title[i]);
+    gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2,
+       GTK_SHRINK, GTK_SHRINK, 0, 0);
+    gtk_widget_show(column_lb);
+    col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
+    gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 
+       1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
+    if (i == 0) {
+      gtk_widget_show(col_arrows[i].ascend_pm);
+    }
+    col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
+    gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm,
+       1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
+    gtk_clist_set_column_widget(GTK_CLIST(packet_list), i, col_arrows[i].table);
+    gtk_widget_show(col_arrows[i].table);
+  }
+  gtk_clist_column_titles_show(GTK_CLIST(packet_list));
 }
 
-       
+
+void
+set_last_open_dir(char *dirname)
+{
+       int len;
+
+       if (last_open_dir) {
+               g_free(last_open_dir);
+       }
+
+       if (dirname) {
+               len = strlen(dirname);
+               if (dirname[len-1] != G_DIR_SEPARATOR) {
+                       last_open_dir = g_strconcat(dirname, G_DIR_SEPARATOR_S,
+                               NULL);
+               }
+       }
+       else {
+               last_open_dir = NULL;
+       }
+}