"col_format_to_pref_str()" is used only in "prefs.c", and knows about
[obnox/wireshark/wip.git] / prefs.c
diff --git a/prefs.c b/prefs.c
index ce77fe262579fa20e1074e51beda537203ccdbb3..e1631c82f2cf3aaff4f4611fc36d78bc25c5ecd9 100644 (file)
--- a/prefs.c
+++ b/prefs.c
@@ -1,12 +1,11 @@
 /* prefs.c
  * Routines for handling preferences
  *
- * $Id: prefs.c,v 1.49 2001/04/13 14:59:28 jfoster Exp $
+ * $Id: prefs.c,v 1.56 2001/07/22 21:56:25 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998 Gerald Combs
- *
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -369,53 +368,125 @@ prefs_register_modules(void)
 {
 }
 
-/* Parse through a list of comma-separated, quoted strings.  Return a
-   list of the string data */
+/* Parse through a list of comma-separated, possibly quoted strings.
+   Return a list of the string data. */
 static GList *
-get_string_list(gchar *str) {
-  enum { PRE_QUOT, IN_QUOT, POST_QUOT };
-
-  gint      state = PRE_QUOT, i = 0, j = 0;
-  gboolean  backslash = FALSE;
+get_string_list(gchar *str)
+{
+  gint      i = 0, j = 0;
+  gboolean  in_quot = FALSE, backslash = FALSE;
   gchar     cur_c, *slstr = NULL;
   GList    *sl = NULL;
-  
-  while ((cur_c = str[i]) != '\0') {
+
+  /* Allocate a buffer for the first string.   */
+  slstr = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
+  j = 0;
+
+  for (;;) {
+    cur_c = str[i];
+    if (cur_c == '\0') {
+      /* It's the end of the input, so it's the end of the string we
+         were working on, and there's no more input. */
+      if (in_quot || backslash) {
+        /* We were in the middle of a quoted string or backslash escape,
+           and ran out of characters; that's an error.  */
+        g_free(slstr);
+        clear_string_list(sl);
+        return NULL;
+      }
+      slstr[j] = '\0';
+      sl = g_list_append(sl, slstr);
+      break;
+    }
     if (cur_c == '"' && ! backslash) {
-      switch (state) {
-        case PRE_QUOT:
-          state = IN_QUOT;
-          slstr = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
-          j = 0;
-          break;
-        case IN_QUOT:
-          state  = POST_QUOT;
-          slstr[j] = '\0';
-          sl = g_list_append(sl, slstr);
-          break;
-        case POST_QUOT:
-          clear_string_list(sl);
-          return NULL;
-          break;
-        default:
-          break;
+      if (!in_quot) {
+        /* We're not in the middle of a quoted string, and we saw a
+           quotation mark; we're now quoting.  */
+        in_quot = TRUE;
+      } else {
+        /* We're in the middle of a quoted string, and we saw a quotation
+           mark; we're no longer quoting.   */
+        in_quot = FALSE;
       }
     } else if (cur_c == '\\' && ! backslash) {
+      /* We saw a backslash, and the previous character wasn't a
+         backslash; escape the next character.  */
       backslash = TRUE;
-    } else if (cur_c == ',' && state == POST_QUOT) {
-      state = PRE_QUOT;
-    } else if (state == IN_QUOT && j < COL_MAX_LEN) {
-      slstr[j] = str[i];
-      j++;
+    } else if (cur_c == ',' && ! in_quot && ! backslash) {
+      /* We saw a comma, and we're not in the middle of a quoted string
+         and it wasn't preceded by a backslash; it's the end of
+         the string we were working on...  */
+      slstr[j] = '\0';
+      sl = g_list_append(sl, slstr);
+
+      /* ...and the beginning of a new string.  */
+      slstr = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
+      j = 0;
+    } else {
+      /* It's a character to be put into a string; do so if there's room.  */
+      if (j < COL_MAX_LEN) {
+        slstr[j] = cur_c;
+        j++;
+      }
+
+      /* If it was backslash-escaped, we're done with the backslash escape.  */
+      backslash = FALSE;
     }
     i++;
   }
-  if (state != POST_QUOT) {
-    clear_string_list(sl);
-  }
   return(sl);
 }
 
+/* XXX - needs to handle quote marks inside the quoted string, by
+   backslash-escaping them.  */
+#define MAX_FMT_PREF_LEN      1024
+#define MAX_FMT_PREF_LINE_LEN   60
+static gchar *
+put_string_list(GList *sl)
+{
+  static gchar  pref_str[MAX_FMT_PREF_LEN] = "";
+  GList        *clp = g_list_first(sl);
+  fmt_data     *cfmt;
+  int           cur_pos = 0, cur_len = 0, fmt_len;
+  
+  while (clp) {
+    cfmt = (fmt_data *) clp->data;
+    
+    fmt_len = strlen(cfmt->title) + 4;
+    if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
+      if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
+        cur_len--;
+        cur_pos = 0;
+               pref_str[cur_len] = '\n'; cur_len++;
+        pref_str[cur_len] = '\t'; cur_len++;
+      }
+      sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->title);
+      cur_len += fmt_len;
+      cur_pos += fmt_len;
+    }
+
+    fmt_len = strlen(cfmt->fmt) + 4;
+    if ((fmt_len + cur_len) < (MAX_FMT_PREF_LEN - 1)) {
+      if ((fmt_len + cur_pos) > MAX_FMT_PREF_LINE_LEN) {
+        cur_len--;
+        cur_pos = 0;
+        pref_str[cur_len] = '\n'; cur_len++;
+        pref_str[cur_len] = '\t'; cur_len++;
+      }
+      sprintf(&pref_str[cur_len], "\"%s\", ", cfmt->fmt);
+      cur_len += fmt_len;
+      cur_pos += fmt_len;
+    }
+    
+    clp = clp->next;
+  }
+  
+  if (cur_len > 2)
+    pref_str[cur_len - 2] = '\0';
+
+  return(pref_str);
+}    
+
 void
 clear_string_list(GList *sl) {
   GList *l = sl;
@@ -612,11 +683,10 @@ read_prefs(int *gpf_errno_return, char **gpf_path_return,
     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;
-
+    prefs.capture_prom_mode   =  TRUE;
+    prefs.capture_real_time   = FALSE;
+    prefs.capture_auto_scroll = FALSE;
+    prefs.name_resolve        = PREFS_RESOLV_ALL;
   }
 
   /* Read the global preferences file, if it exists. */
@@ -851,11 +921,20 @@ prefs_set_pref(char *prefarg)
 #define PRS_GUI_MARKED_FG "gui.marked_frame.fg"
 #define PRS_GUI_MARKED_BG "gui.marked_frame.bg"
 
+/*
+ * This applies to more than just captures, so it's not "capture.name_resolve";
+ * "capture.name_resolve" is supported on input for backwards compatibility.
+ *
+ * It's not a preference for a particular part of Ethereal, it's used all
+ * over the place, so its name doesn't have two components.
+ */
+#define PRS_NAME_RESOLVE "name_resolve"
+#define PRS_CAP_NAME_RESOLVE "capture.name_resolve"
+
 /*  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)
@@ -864,10 +943,70 @@ prefs_set_pref(char *prefarg)
 static gchar *pr_formats[] = { "text", "postscript" };
 static gchar *pr_dests[]   = { "command", "file" };
 
+typedef struct {
+  char    letter;
+  guint32 value;
+} name_resolve_opt_t;
+
+static name_resolve_opt_t name_resolve_opt[] = {
+  { 'm', PREFS_RESOLV_MAC },
+  { 'n', PREFS_RESOLV_NETWORK },
+  { 't', PREFS_RESOLV_TRANSPORT },
+};
+
+#define N_NAME_RESOLVE_OPT     (sizeof name_resolve_opt / sizeof name_resolve_opt[0])
+
+static char *
+name_resolve_to_string(guint32 name_resolve)
+{
+  static char string[N_NAME_RESOLVE_OPT+1];
+  char *p;
+  unsigned int i;
+  gboolean all_opts_set = TRUE;
+
+  if (name_resolve == PREFS_RESOLV_NONE)
+    return "FALSE";
+  p = &string[0];
+  for (i = 0; i < N_NAME_RESOLVE_OPT; i++) {
+    if (name_resolve & name_resolve_opt[i].value)
+      *p++ =  name_resolve_opt[i].letter;
+    else
+      all_opts_set = FALSE;
+  }
+  *p = '\0';
+  if (all_opts_set)
+    return "TRUE";
+  return string;
+}
+
+char
+string_to_name_resolve(char *string, guint32 *name_resolve)
+{
+  char c;
+  unsigned int i;
+
+  *name_resolve = 0;
+  while ((c = *string++) != '\0') {
+    for (i = 0; i < N_NAME_RESOLVE_OPT; i++) {
+      if (c == name_resolve_opt[i].letter) {
+        *name_resolve |= name_resolve_opt[i].value;
+        break;
+      }
+    }
+    if (i == N_NAME_RESOLVE_OPT) {
+      /*
+       * Unrecognized letter.
+       */
+      return c;
+    }
+  }
+  return '\0';
+}
+
 static int
 set_pref(gchar *pref_name, gchar *value)
 {
-  GList    *col_l;
+  GList    *col_l, *col_l_elt;
   gint      llen;
   fmt_data *cfmt;
   unsigned long int cval;
@@ -902,21 +1041,49 @@ set_pref(gchar *pref_name, gchar *value)
     if (prefs.pr_cmd) g_free(prefs.pr_cmd);
     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) {
-      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);
-      while(col_l) {
-        cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
-        cfmt->title    = g_strdup(col_l->data);
-        col_l          = col_l->next;
-        cfmt->fmt      = g_strdup(col_l->data);
-        col_l          = col_l->next;
-        prefs.col_list = g_list_append(prefs.col_list, cfmt);
+    col_l = get_string_list(value);
+    if (col_l == NULL)
+      return PREFS_SET_SYNTAX_ERR;
+    if ((g_list_length(col_l) % 2) != 0) {
+      /* A title didn't have a matching format.  */
+      clear_string_list(col_l);
+      return PREFS_SET_SYNTAX_ERR;
+    }
+    /* Check to make sure all column formats are valid.  */
+    col_l_elt = g_list_first(col_l);
+    while(col_l_elt) {
+      /* Make sure the title isn't empty.  */
+      if (strcmp(col_l_elt->data, "") == 0) {
+       /* It is.  */
+        clear_string_list(col_l);
+        return PREFS_SET_SYNTAX_ERR;
       }
-      /* To do: else print some sort of error? */
+
+      /* Go past the title.  */
+      col_l_elt = col_l_elt->next;
+
+      /* Check the format.  */
+      if (get_column_format_from_str(col_l_elt->data) == -1) {
+        /* It's not a valid column format.  */
+        clear_string_list(col_l);
+        return PREFS_SET_SYNTAX_ERR;
+      }
+
+      /* Go past the format.  */
+      col_l_elt = col_l_elt->next;
+    }
+    free_col_info(&prefs);
+    prefs.col_list = NULL;
+    llen             = g_list_length(col_l);
+    prefs.num_cols   = llen / 2;
+    col_l_elt = g_list_first(col_l);
+    while(col_l_elt) {
+      cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
+      cfmt->title    = g_strdup(col_l_elt->data);
+      col_l_elt      = col_l_elt->next;
+      cfmt->fmt      = g_strdup(col_l_elt->data);
+      col_l_elt      = col_l_elt->next;
+      prefs.col_list = g_list_append(prefs.col_list, cfmt);
     }
     clear_string_list(col_l);
   } else if (strcmp(pref_name, PRS_STREAM_CL_FG) == 0) {
@@ -944,21 +1111,21 @@ set_pref(gchar *pref_name, gchar *value)
     prefs.st_server_bg.green = GREEN_COMPONENT(cval);
     prefs.st_server_bg.blue  = BLUE_COMPONENT(cval);
   } else if (strcmp(pref_name, PRS_GUI_SCROLLBAR_ON_RIGHT) == 0) {
-    if (strcmp(value, "TRUE") == 0) {
+    if (strcasecmp(value, "true") == 0) {
            prefs.gui_scrollbar_on_right = TRUE;
     }
     else {
            prefs.gui_scrollbar_on_right = FALSE;
     }
   } else if (strcmp(pref_name, PRS_GUI_PLIST_SEL_BROWSE) == 0) {
-    if (strcmp(value, "TRUE") == 0) {
+    if (strcasecmp(value, "true") == 0) {
            prefs.gui_plist_sel_browse = TRUE;
     }
     else {
            prefs.gui_plist_sel_browse = FALSE;
     }
   } else if (strcmp(pref_name, PRS_GUI_PTREE_SEL_BROWSE) == 0) {
-    if (strcmp(value, "TRUE") == 0) {
+    if (strcasecmp(value, "true") == 0) {
            prefs.gui_ptree_sel_browse = TRUE;
     }
     else {
@@ -992,17 +1159,32 @@ set_pref(gchar *pref_name, gchar *value)
 
 /* handle the capture options */ 
   } else if (strcmp(pref_name, PRS_CAP_PROM_MODE) == 0) {
-    prefs.capture_prom_mode = ((strcmp(value, "TRUE") == 0)?TRUE:FALSE); 
+    prefs.capture_prom_mode = ((strcasecmp(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); 
+    prefs.capture_real_time = ((strcasecmp(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); 
+    prefs.capture_auto_scroll = ((strcasecmp(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); 
-
+/* handle the global options */
+  } else if (strcmp(pref_name, PRS_NAME_RESOLVE) == 0 ||
+            strcmp(pref_name, PRS_CAP_NAME_RESOLVE) == 0) {
+    /*
+     * "TRUE" and "FALSE", for backwards compatibility, are synonyms for
+     * PREFS_RESOLV_ALL and PREFS_RESOLV_NONE.
+     *
+     * Otherwise, we treat it as a list of name types we want to resolve.
+     */
+    if (strcasecmp(value, "true") == 0)
+      prefs.name_resolve = PREFS_RESOLV_ALL;
+    else if (strcasecmp(value, "false") == 0)
+      prefs.name_resolve = PREFS_RESOLV_NONE;
+    else {
+      prefs.name_resolve = PREFS_RESOLV_NONE;  /* start out with none set */
+      if (string_to_name_resolve(value, &prefs.name_resolve) != '\0')
+        return PREFS_SET_SYNTAX_ERR;
+    }
   } else {
     /* To which module does this preference belong? */
     dotp = strchr(pref_name, '.');
@@ -1268,7 +1450,7 @@ write_prefs(char **pf_path_return)
 
   fprintf (pf, "# Packet list column format.  Each pair of strings consists "
     "of a column title \n# and its format.\n"
-    "%s: %s\n\n", PRS_COL_FMT, col_format_to_pref_str());
+    "%s: %s\n\n", PRS_COL_FMT, put_string_list(prefs.col_list));
 
   fprintf (pf, "# TCP stream window color preferences.  Each value is a six "
     "digit hexadecimal value in the form rrggbb.\n");
@@ -1327,6 +1509,10 @@ write_prefs(char **pf_path_return)
     (prefs.gui_marked_bg.green * 255 / 65535),
     (prefs.gui_marked_bg.blue * 255 / 65535));
 
+  fprintf(pf, "\n# Resolve addresses to names? TRUE/FALSE/{list of address types to resolve}\n");
+  fprintf(pf, PRS_NAME_RESOLVE ": %s\n",
+                 name_resolve_to_string(prefs.name_resolve));
+
 /* write the capture options */
   fprintf(pf, "\n# Capture in promiscuous mode? TRUE/FALSE\n");
   fprintf(pf, PRS_CAP_PROM_MODE ": %s\n",
@@ -1340,10 +1526,6 @@ write_prefs(char **pf_path_return)
   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);
@@ -1392,7 +1574,7 @@ copy_prefs(e_prefs *dest, e_prefs *src)
   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;
+  dest->name_resolve = src->name_resolve;
 
 }