Added the ethereal capture preferences to the preference file.
[obnox/wireshark/wip.git] / prefs.c
diff --git a/prefs.c b/prefs.c
index 13de43215e476c8cc1131151a9faff2b7ca9bb48..ce77fe262579fa20e1074e51beda537203ccdbb3 100644 (file)
--- a/prefs.c
+++ b/prefs.c
@@ -1,7 +1,7 @@
 /* prefs.c
  * Routines for handling preferences
  *
- * $Id: prefs.c,v 1.36 2000/08/15 20:53:30 deniel Exp $
+ * $Id: prefs.c,v 1.49 2001/04/13 14:59:28 jfoster Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
 #include <sys/stat.h>
 #endif
 
+#include <epan.h>
+#include <filesystem.h>
 #include "globals.h"
 #include "packet.h"
 #include "file.h"
 #include "prefs.h"
+#include "proto.h"
 #include "column.h"
 #include "print.h"
-#include "util.h"
 
 #include "prefs-int.h"
 
@@ -62,6 +64,7 @@
 static int    set_pref(gchar*, gchar*);
 static GList *get_string_list(gchar *);
 static void   clear_string_list(GList *);
+static void   free_col_info(e_prefs *);
 
 #define PF_NAME "preferences"
 
@@ -70,6 +73,15 @@ static void   clear_string_list(GList *);
 static gboolean init_prefs = TRUE;
 static gchar *pf_path = NULL;
 
+/*
+ * XXX - variables to allow us to attempt to interpret the first
+ * "mgcp.{tcp,udp}.port" in a preferences file as
+ * "mgcp.{tcp,udp}.gateway_port" and the second as
+ * "mgcp.{tcp,udp}.callagent_port".
+ */
+static int mgcp_tcp_port_count;
+static int mgcp_udp_port_count;
+
 e_prefs prefs;
 
 gchar  *gui_ptree_line_style_text[] =
@@ -78,6 +90,8 @@ gchar *gui_ptree_line_style_text[] =
 gchar  *gui_ptree_expander_style_text[] =
        { "NONE", "SQUARE", "TRIANGLE", "CIRCULAR", NULL };
 
+gchar  *gui_hex_dump_highlight_style_text[] =
+       { "BOLD", "INVERSE", NULL };
 
 /*
  * List of modules with preference settings.
@@ -109,6 +123,17 @@ prefs_register_module(const char *name, const char *title,
        return module;
 }
 
+/*
+ * Register that a protocol has preferences.
+ */
+module_t *
+prefs_register_protocol(int id, void (*apply_cb)(void))
+{
+       return prefs_register_module(proto_get_protocol_filter_name(id),
+                                    proto_get_protocol_short_name(id),
+                                    apply_cb);
+}
+
 /*
  * Find a module, given its name.
  */
@@ -282,7 +307,7 @@ prefs_register_bool_preference(module_t *module, const char *name,
 void
 prefs_register_enum_preference(module_t *module, const char *name,
     const char *title, const char *description, gint *var,
-    const enum_val *enumvals, gboolean radio_buttons)
+    const enum_val_t *enumvals, gboolean radio_buttons)
 {
        pref_t *preference;
 
@@ -402,7 +427,7 @@ clear_string_list(GList *sl) {
 }
 
 /*
- * Takes a string, a pointer to an array of "enum_val"s, and a default gint
+ * Takes a string, a pointer to an array of "enum_val_t"s, and a default gint
  * value.
  * The array must be terminated by an entry with a null "name" string.
  * If the string matches a "name" strings in an entry, the value from that
@@ -410,7 +435,7 @@ clear_string_list(GList *sl) {
  * third argument is returned.
  */
 gint
-find_val_for_string(const char *needle, const enum_val *haystack,
+find_val_for_string(const char *needle, const enum_val_t *haystack,
     gint default_value)
 {
        int i = 0;
@@ -465,6 +490,17 @@ print.file: /a/very/long/path/
 
 static void read_prefs_file(const char *pf_path, FILE *pf);
 
+/* Read the preferences file, fill in "prefs", and return a pointer to it.
+
+   If we got an error (other than "it doesn't exist") trying to read
+   the global preferences file, stuff the errno into "*gpf_errno_return"
+   and a pointer to the path of the file into "*gpf_path_return", and
+   return NULL.
+
+   If we got an error (other than "it doesn't exist") trying to read
+   the user's preferences file, stuff the errno into "*pf_errno_return"
+   and a pointer to the path of the file into "*pf_path_return", and
+   return NULL. */
 e_prefs *
 read_prefs(int *gpf_errno_return, char **gpf_path_return,
           int *pf_errno_return, char **pf_path_return)
@@ -515,6 +551,72 @@ read_prefs(int *gpf_errno_return, char **gpf_path_return,
     prefs.gui_ptree_sel_browse = FALSE;
     prefs.gui_ptree_line_style = 0;
     prefs.gui_ptree_expander_style = 1;
+    prefs.gui_hex_dump_highlight_style = 1;
+#ifdef WIN32
+    prefs.gui_font_name = g_strdup("-*-lucida console-medium-r-*-*-*-100-*-*-*-*-*-*");
+#else
+    /*
+     * XXX - for now, we make the initial font name a pattern that matches
+     * only ISO 8859/1 fonts, so that we don't match 2-byte fonts such
+     * as ISO 10646 fonts.
+     *
+     * Users in locales using other one-byte fonts will have to choose
+     * a different font from the preferences dialog - or put the font
+     * selection in the global preferences file to make that font the
+     * default for all users who don't explicitly specify a different
+     * font.
+     *
+     * Making this a font set rather than a font has two problems:
+     *
+     * 1) as far as I know, you can't select font sets with the
+     *    font selection dialog;
+     *
+     *  2) if you use a font set, the text to be drawn must be a
+     *    multi-byte string in the appropriate locale, but
+     *    Ethereal does *NOT* guarantee that's the case - in
+     *    the hex-dump window, each character in the text portion
+     *    of the display must be a *single* byte, and in the
+     *    packet-list and protocol-tree windows, text extracted
+     *    from the packet is not necessarily in the right format.
+     *
+     * "Doing this right" may, for the packet-list and protocol-tree
+     * windows, require that dissectors know what the locale is
+     * *AND* know what locale and text representation is used in
+     * the packets they're dissecting, and may be impossible in
+     * the hex-dump window (except by punting and displaying only
+     * ASCII characters).
+     *
+     * GTK+ 2.0 may simplify part of the problem, as it will, as I
+     * understand it, use UTF-8-encoded Unicode as its internal
+     * character set; however, we'd still have to know whatever
+     * character set and encoding is used in the packet (which
+     * may differ for different protocols, e.g. SMB might use
+     * PC code pages for some strings and Unicode for others, whilst
+     * NFS might use some UNIX character set encoding, e.g. ISO 8859/x,
+     * or one of the EUC character sets for Asian languages, or one
+     * of the other multi-byte character sets, or UTF-8, or...).
+     *
+     * I.e., as far as I can tell, "internationalizing" the packet-list,
+     * protocol-tree, and hex-dump windows involves a lot more than, say,
+     * just using font sets rather than fonts.
+     */
+    prefs.gui_font_name = g_strdup("-*-fixed-medium-r-semicondensed-*-*-120-*-*-*-*-iso8859-1");
+#endif
+    prefs.gui_marked_fg.pixel = 65535;
+    prefs.gui_marked_fg.red   = 65535;
+    prefs.gui_marked_fg.green = 65535;
+    prefs.gui_marked_fg.blue  = 65535;
+    prefs.gui_marked_bg.pixel =     0;
+    prefs.gui_marked_bg.red   =     0;
+    prefs.gui_marked_bg.green =     0;
+    prefs.gui_marked_bg.blue  =     0;
+
+/* set the default values for the capture dialog box */
+    prefs.capture_prom_mode   =     0;
+    prefs.capture_real_time   =     0;
+    prefs.capture_auto_scroll =     0;
+    prefs.capture_name_resolve=     1;
+
   }
 
   /* Read the global preferences file, if it exists. */
@@ -568,6 +670,13 @@ read_prefs_file(const char *pf_path, FILE *pf)
   gboolean  got_val = FALSE;
   gint      var_len = 0, val_len = 0, fline = 1, pline = 1;
 
+  /*
+   * Start out the counters of "mgcp.{tcp,udp}.port" entries we've
+   * seen.
+   */
+  mgcp_tcp_port_count = 0;
+  mgcp_udp_port_count = 0;
+
   while ((got_c = getc(pf)) != EOF) {
     if (got_c == '\n') {
       state = START;
@@ -684,6 +793,16 @@ prefs_set_pref(char *prefarg)
        u_char *p, *colonp;
        int ret;
 
+       /*
+        * Set the counters of "mgcp.{tcp,udp}.port" entries we've
+        * seen to values that keep us from trying to interpret tham
+        * as "mgcp.{tcp,udp}.gateway_port" or "mgcp.{tcp,udp}.callagent_port",
+        * as, from the command line, we have no way of guessing which
+        * the user had in mind.
+        */
+       mgcp_tcp_port_count = -1;
+       mgcp_udp_port_count = -1;
+
        colonp = strchr(prefarg, ':');
        if (colonp == NULL)
                return PREFS_SET_SYNTAX_ERR;
@@ -727,6 +846,16 @@ prefs_set_pref(char *prefarg)
 #define PRS_GUI_PTREE_SEL_BROWSE "gui.protocol_tree_sel_browse"
 #define PRS_GUI_PTREE_LINE_STYLE "gui.protocol_tree_line_style"
 #define PRS_GUI_PTREE_EXPANDER_STYLE "gui.protocol_tree_expander_style"
+#define PRS_GUI_HEX_DUMP_HIGHLIGHT_STYLE "gui.hex_dump_highlight_style"
+#define PRS_GUI_FONT_NAME "gui.font_name"
+#define PRS_GUI_MARKED_FG "gui.marked_frame.fg"
+#define PRS_GUI_MARKED_BG "gui.marked_frame.bg"
+
+/*  values for the capture dialog box */
+#define PRS_CAP_REAL_TIME "capture.real_time_update"
+#define PRS_CAP_PROM_MODE "capture.prom_mode"
+#define PRS_CAP_AUTO_SCROLL "capture.auto_scroll"
+#define PRS_CAP_NAME_RESOLVE "capture.name_resolve"
 
 #define RED_COMPONENT(x)   ((((x) >> 16) & 0xff) * 65535 / 255)
 #define GREEN_COMPONENT(x) ((((x) >>  8) & 0xff) * 65535 / 255)
@@ -774,13 +903,8 @@ set_pref(gchar *pref_name, gchar *value)
     prefs.pr_cmd = g_strdup(value);
   } else if (strcmp(pref_name, PRS_COL_FMT) == 0) {
     if ((col_l = get_string_list(value)) && (g_list_length(col_l) % 2) == 0) {
-      while (prefs.col_list) {
-        cfmt = prefs.col_list->data;
-        g_free(cfmt->title);
-        g_free(cfmt->fmt);
-        g_free(cfmt);
-        prefs.col_list = g_list_remove_link(prefs.col_list, prefs.col_list);
-      }
+      free_col_info(&prefs);
+      prefs.col_list = NULL;
       llen             = g_list_length(col_l);
       prefs.num_cols   = llen / 2;
       col_l = g_list_first(col_l);
@@ -846,6 +970,39 @@ set_pref(gchar *pref_name, gchar *value)
   } else if (strcmp(pref_name, PRS_GUI_PTREE_EXPANDER_STYLE) == 0) {
          prefs.gui_ptree_expander_style =
                  find_index_from_string_array(value, gui_ptree_expander_style_text, 1);
+  } else if (strcmp(pref_name, PRS_GUI_HEX_DUMP_HIGHLIGHT_STYLE) == 0) {
+         prefs.gui_hex_dump_highlight_style =
+                 find_index_from_string_array(value, gui_hex_dump_highlight_style_text, 1);
+  } else if (strcmp(pref_name, PRS_GUI_FONT_NAME) == 0) {
+         if (prefs.gui_font_name != NULL)
+               g_free(prefs.gui_font_name);
+         prefs.gui_font_name = g_strdup(value);
+  } else if (strcmp(pref_name, PRS_GUI_MARKED_FG) == 0) {
+    cval = strtoul(value, NULL, 16);
+    prefs.gui_marked_fg.pixel = 0;
+    prefs.gui_marked_fg.red   = RED_COMPONENT(cval);
+    prefs.gui_marked_fg.green = GREEN_COMPONENT(cval);
+    prefs.gui_marked_fg.blue  = BLUE_COMPONENT(cval);
+  } else if (strcmp(pref_name, PRS_GUI_MARKED_BG) == 0) {
+    cval = strtoul(value, NULL, 16);
+    prefs.gui_marked_bg.pixel = 0;
+    prefs.gui_marked_bg.red   = RED_COMPONENT(cval);
+    prefs.gui_marked_bg.green = GREEN_COMPONENT(cval);
+    prefs.gui_marked_bg.blue  = BLUE_COMPONENT(cval);
+
+/* handle the capture options */ 
+  } else if (strcmp(pref_name, PRS_CAP_PROM_MODE) == 0) {
+    prefs.capture_prom_mode = ((strcmp(value, "TRUE") == 0)?TRUE:FALSE); 
+  } else if (strcmp(pref_name, PRS_CAP_REAL_TIME) == 0) {
+    prefs.capture_real_time = ((strcmp(value, "TRUE") == 0)?TRUE:FALSE); 
+
+  } else if (strcmp(pref_name, PRS_CAP_AUTO_SCROLL) == 0) {
+    prefs.capture_auto_scroll = ((strcmp(value, "TRUE") == 0)?TRUE:FALSE); 
+  } else if (strcmp(pref_name, PRS_CAP_NAME_RESOLVE) == 0) {
+    prefs.capture_name_resolve = ((strcmp(value, "TRUE") == 0)?TRUE:FALSE); 
+
   } else {
     /* To which module does this preference belong? */
     dotp = strchr(pref_name, '.');
@@ -853,11 +1010,68 @@ set_pref(gchar *pref_name, gchar *value)
       return PREFS_SET_SYNTAX_ERR;     /* no ".", so no module/name separator */
     *dotp = '\0';              /* separate module and preference name */
     module = find_module(pref_name);
+
+    /*
+     * XXX - "Diameter" rather than "diameter" was used in earlier
+     * versions of Ethereal; if we didn't find the module, and its name
+     * was "Diameter", look for "diameter" instead.
+     */
+    if (module == NULL && strcmp(pref_name, "Diameter") == 0)
+      module = find_module("diameter");
     *dotp = '.';               /* put the preference string back */
     if (module == NULL)
       return PREFS_SET_NO_SUCH_PREF;   /* no such module */
     dotp++;                    /* skip past separator to preference name */
     pref = find_preference(module, dotp);
+
+    /*
+     * XXX - "mgcp.display raw text toggle" and "mgcp.display dissect tree"
+     * rather than "mgcp.display_raw_text" and "mgcp.display_dissect_tree"
+     * were used in earlier versions of Ethereal; if we didn't find the
+     * preference, it was an MGCP preference, and its name was
+     * "display raw text toggle" or "display dissect tree", look for
+     * "display_raw_text" or "display_dissect_tree" instead.
+     *
+     * "mgcp.tcp.port" and "mgcp.udp.port" are harder to handle, as both
+     * the gateway and callagent ports were given those names; we interpret
+     * the first as "mgcp.{tcp,udp}.gateway_port" and the second as
+     * "mgcp.{tcp,udp}.callagent_port", as that's the order in which
+     * they were registered by the MCCP dissector and thus that's the
+     * order in which they were written to the preferences file.  (If
+     * we're not reading the preferences file, but are handling stuff
+     * from a "-o" command-line option, we have no clue which the user
+     * had in mind - they should have used "mgcp.{tcp,udp}.gateway_port"
+     * or "mgcp.{tcp,udp}.callagent_port" instead.)
+     */
+    if (pref == NULL && strncmp(pref_name, "mgcp.", 5) == 0) {
+      if (strcmp(dotp, "display raw text toggle") == 0)
+        pref = find_preference(module, "display_raw_text");
+      else if (strcmp(dotp, "display dissect tree") == 0)
+        pref = find_preference(module, "display_dissect_tree");
+      else if (strcmp(dotp, "tcp.port") == 0) {
+       mgcp_tcp_port_count++;
+       if (mgcp_tcp_port_count == 1) {
+         /* It's the first one */
+         pref = find_preference(module, "tcp.gateway_port");
+       } else if (mgcp_tcp_port_count == 2) {
+         /* It's the second one */
+         pref = find_preference(module, "tcp.callagent_port");
+       }
+       /* Otherwise it's from the command line, and we don't bother
+          mapping it. */
+      } else if (strcmp(dotp, "udp.port") == 0) {
+       mgcp_udp_port_count++;
+       if (mgcp_udp_port_count == 1) {
+         /* It's the first one */
+         pref = find_preference(module, "udp.gateway_port");
+       } else if (mgcp_udp_port_count == 2) {
+         /* It's the second one */
+         pref = find_preference(module, "udp.callagent_port");
+       }
+       /* Otherwise it's from the command line, and we don't bother
+          mapping it. */
+      }
+    }
     if (pref == NULL)
       return PREFS_SET_NO_SUCH_PREF;   /* no such preference */
 
@@ -880,8 +1094,8 @@ set_pref(gchar *pref_name, gchar *value)
       else
         bval = FALSE;
       if (*pref->varp.bool != bval) {
-       module->prefs_changed = TRUE;
-       *pref->varp.bool = bval;
+       module->prefs_changed = TRUE;
+       *pref->varp.bool = bval;
       }
       break;
 
@@ -890,8 +1104,8 @@ set_pref(gchar *pref_name, gchar *value)
       enum_val = find_val_for_string(value,
                                        pref->info.enum_info.enumvals, 1);
       if (*pref->varp.enump != enum_val) {
-       module->prefs_changed = TRUE;
-       *pref->varp.enump = enum_val;
+       module->prefs_changed = TRUE;
+       *pref->varp.enump = enum_val;
       }
       break;
 
@@ -922,7 +1136,7 @@ write_pref(gpointer data, gpointer user_data)
 {
        pref_t *pref = data;
        write_pref_arg_t *arg = user_data;
-       const enum_val *enum_valp;
+       const enum_val_t *enum_valp;
        const char *val_string;
 
        fprintf(arg->pf, "\n# %s\n", pref->description);
@@ -995,6 +1209,10 @@ write_module_prefs(gpointer data, gpointer user_data)
        g_list_foreach(arg.module->prefs, write_pref, &arg);
 }
 
+/* Write out "prefs" to the user's preferences file, and return 0.
+
+   If we got an error, stuff a pointer to the path of the preferences file
+   into "*pf_path_return", and return the errno. */
 int
 write_prefs(char **pf_path_return)
 {
@@ -1091,6 +1309,41 @@ write_prefs(char **pf_path_return)
   fprintf(pf, PRS_GUI_PTREE_EXPANDER_STYLE ": %s\n",
                  gui_ptree_expander_style_text[prefs.gui_ptree_expander_style]);
 
+  fprintf(pf, "\n# Hex dump highlight style. One of: BOLD, INVERSE\n");
+  fprintf(pf, PRS_GUI_HEX_DUMP_HIGHLIGHT_STYLE ": %s\n",
+                 gui_hex_dump_highlight_style_text[prefs.gui_hex_dump_highlight_style]);
+
+  fprintf(pf, "\n# Font name for packet list, protocol tree, and hex dump panes.\n");
+  fprintf(pf, PRS_GUI_FONT_NAME ": %s\n", prefs.gui_font_name);
+
+  fprintf (pf, "\n# Color preferences for a marked frame.  Each value is a six "
+    "digit hexadecimal value in the form rrggbb.\n");
+  fprintf (pf, "%s: %02x%02x%02x\n", PRS_GUI_MARKED_FG,
+    (prefs.gui_marked_fg.red * 255 / 65535),
+    (prefs.gui_marked_fg.green * 255 / 65535),
+    (prefs.gui_marked_fg.blue * 255 / 65535));
+  fprintf (pf, "%s: %02x%02x%02x\n", PRS_GUI_MARKED_BG,
+    (prefs.gui_marked_bg.red * 255 / 65535),
+    (prefs.gui_marked_bg.green * 255 / 65535),
+    (prefs.gui_marked_bg.blue * 255 / 65535));
+
+/* write the capture options */
+  fprintf(pf, "\n# Capture in promiscuous mode? TRUE/FALSE\n");
+  fprintf(pf, PRS_CAP_PROM_MODE ": %s\n",
+                 prefs.capture_prom_mode == TRUE ? "TRUE" : "FALSE");
+
+  fprintf(pf, "\n# Update packet list in real time during capture? TRUE/FALSE\n");
+  fprintf(pf, PRS_CAP_REAL_TIME ": %s\n",
+                 prefs.capture_real_time == TRUE ? "TRUE" : "FALSE");
+
+  fprintf(pf, "\n# scroll packet list during capture? TRUE/FALSE\n");
+  fprintf(pf, PRS_CAP_AUTO_SCROLL ": %s\n",
+                 prefs.capture_auto_scroll == TRUE ? "TRUE" : "FALSE");
+
+  fprintf(pf, "\n# resolve names  during capture? TRUE/FALSE\n");
+  fprintf(pf, PRS_CAP_NAME_RESOLVE ": %s\n",
+                 prefs.capture_name_resolve == TRUE ? "TRUE" : "FALSE");
+
   g_list_foreach(modules, write_module_prefs, pf);
 
   fclose(pf);
@@ -1101,3 +1354,79 @@ write_prefs(char **pf_path_return)
      errors. */
   return 0;
 }
+
+/* Copy a set of preferences. */
+void
+copy_prefs(e_prefs *dest, e_prefs *src)
+{
+  fmt_data *src_cfmt, *dest_cfmt;
+  GList *entry;
+
+  dest->pr_format = src->pr_format;
+  dest->pr_dest = src->pr_dest;
+  dest->pr_file = g_strdup(src->pr_file);
+  dest->pr_cmd = g_strdup(src->pr_cmd);
+  dest->col_list = NULL;
+  for (entry = src->col_list; entry != NULL; entry = g_list_next(entry)) {
+    src_cfmt = entry->data;
+    dest_cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
+    dest_cfmt->title = g_strdup(src_cfmt->title);
+    dest_cfmt->fmt = g_strdup(src_cfmt->fmt);
+    dest->col_list = g_list_append(dest->col_list, dest_cfmt);
+  }
+  dest->num_cols = src->num_cols;
+  dest->st_client_fg = src->st_client_fg;
+  dest->st_client_bg = src->st_client_bg;
+  dest->st_server_fg = src->st_server_fg;
+  dest->st_server_bg = src->st_server_bg;
+  dest->gui_scrollbar_on_right = src->gui_scrollbar_on_right;
+  dest->gui_plist_sel_browse = src->gui_plist_sel_browse;
+  dest->gui_ptree_sel_browse = src->gui_ptree_sel_browse;
+  dest->gui_ptree_line_style = src->gui_ptree_line_style;
+  dest->gui_ptree_expander_style = src->gui_ptree_expander_style;
+  dest->gui_hex_dump_highlight_style = src->gui_hex_dump_highlight_style;
+  dest->gui_font_name = g_strdup(src->gui_font_name);
+  dest->gui_marked_fg = src->gui_marked_fg;
+  dest->gui_marked_bg = src->gui_marked_bg;
+/*  values for the capture dialog box */
+  dest->capture_prom_mode = src->capture_prom_mode;
+  dest->capture_real_time = src->capture_real_time;
+  dest->capture_auto_scroll = src->capture_auto_scroll;
+  dest->capture_name_resolve = src->capture_name_resolve;
+
+}
+
+/* Free a set of preferences. */
+void
+free_prefs(e_prefs *pr)
+{
+  if (pr->pr_file != NULL) {
+    g_free(pr->pr_file);
+    pr->pr_file = NULL;
+  }
+  if (pr->pr_cmd != NULL) {
+    g_free(pr->pr_cmd);
+    pr->pr_cmd = NULL;
+  }
+  free_col_info(pr);
+  if (pr->gui_font_name != NULL) {
+    g_free(pr->gui_font_name);
+    pr->gui_font_name = NULL;
+  }
+}
+
+static void
+free_col_info(e_prefs *pr)
+{
+  fmt_data *cfmt;
+
+  while (pr->col_list != NULL) {
+    cfmt = pr->col_list->data;
+    g_free(cfmt->title);
+    g_free(cfmt->fmt);
+    g_free(cfmt);
+    pr->col_list = g_list_remove_link(pr->col_list, pr->col_list);
+  }
+  g_list_free(pr->col_list);
+  pr->col_list = NULL;
+}