Explorar o código

Merge remote branch 'sebastian/continuation'

Nick Mathewson %!s(int64=14) %!d(string=hai) anos
pai
achega
c8e1538a0b
Modificáronse 4 ficheiros con 140 adicións e 6 borrados
  1. 6 0
      changes/torrc_continuation
  2. 4 1
      doc/tor.1.txt
  3. 74 5
      src/common/util.c
  4. 56 0
      src/test/test_util.c

+ 6 - 0
changes/torrc_continuation

@@ -0,0 +1,6 @@
+  o Minor features:
+    - Support line continuations in torrc.  If a line ends with a
+      single backslash character, the newline is ignored, and the
+      configuration value is treated as continuing on the next line.
+      Resolves bug 1929.
+

+ 4 - 1
doc/tor.1.txt

@@ -65,7 +65,10 @@ Other options can be specified either on the command-line (--option
     value), or in the configuration file (option value or option "value").
     Options are case-insensitive. C-style escaped characters are allowed inside
     quoted values.   Options on the command line take precedence over
-    options found in the configuration file.
+    options found in the configuration file, except indicated otherwise.  To
+    split one configuration entry into multiple lines, use a single \ before
+    the end of the line.  Comments can be used in such multiline entries, but
+    they must start at the beginning of a line.
 
 **BandwidthRate** __N__ **bytes**|**KB**|**MB**|**GB**::
     A token bucket limits the average incoming bandwidth usage on this node to

+ 74 - 5
src/common/util.c

@@ -2284,7 +2284,40 @@ unescape_string(const char *s, char **result, size_t *size_out)
 const char *
 parse_config_line_from_str(const char *line, char **key_out, char **value_out)
 {
+  /* I believe the file format here is supposed to be:
+     FILE = (EMPTYLINE | LINE)* (EMPTYLASTLINE | LASTLINE)?
+
+     EMPTYLASTLINE = SPACE* | COMMENT
+     EMPTYLINE = EMPTYLASTLINE NL
+     SPACE = ' ' | '\r' | '\t'
+     COMMENT = '#' NOT-NL*
+     NOT-NL = Any character except '\n'
+     NL = '\n'
+
+     LASTLINE = SPACE* KEY SPACE* VALUES
+     LINE = LASTLINE NL
+     KEY = KEYCHAR+
+     KEYCHAR = Any character except ' ', '\r', '\n', '\t', '#', "\"
+
+     VALUES = QUOTEDVALUE | NORMALVALUE
+     QUOTEDVALUE = QUOTE QVITEM* QUOTE EOLSPACE?
+     QUOTE = '"'
+     QVCHAR = KEYCHAR | ESC ('n' | 't' | 'r' | '"' | ESC |'\'' | OCTAL | HEX)
+     ESC = "\\"
+     OCTAL = ODIGIT (ODIGIT ODIGIT?)?
+     HEX = ('x' | 'X') HEXDIGIT HEXDIGIT
+     ODIGIT = '0' .. '7'
+     HEXDIGIT = '0'..'9' | 'a' .. 'f' | 'A' .. 'F'
+     EOLSPACE = SPACE* COMMENT?
+
+     NORMALVALUE = (VALCHAR | ESC ESC_IGNORE | CONTINUATION)* EOLSPACE?
+     VALCHAR = Any character except ESC, '#', and '\n'
+     ESC_IGNORE = Any character except '#' or '\n'
+     CONTINUATION = ESC NL ( COMMENT NL )*
+   */
+
   const char *key, *val, *cp;
+  int continuation = 0;
 
   tor_assert(key_out);
   tor_assert(value_out);
@@ -2308,9 +2341,10 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out)
     return line;
   }
 
-  /* Skip until the next space. */
+  /* Skip until the next space or \ followed by newline. */
   key = line;
-  while (*line && !TOR_ISSPACE(*line) && *line != '#')
+  while (*line && !TOR_ISSPACE(*line) && *line != '#' &&
+         ! (line[0] == '\\' && line[1] == '\n'))
     ++line;
   *key_out = tor_strndup(key, line-key);
 
@@ -2321,7 +2355,7 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out)
   val = line;
 
   /* Find the end of the line. */
-  if (*line == '\"') {
+  if (*line == '\"') { // XXX No continuation handling is done here
     if (!(line = unescape_string(line, value_out, NULL)))
        return NULL;
     while (*line == ' ' || *line == '\t')
@@ -2329,18 +2363,53 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out)
     if (*line && *line != '#' && *line != '\n')
       return NULL;
   } else {
-    while (*line && *line != '\n' && *line != '#')
-      ++line;
+    /* Look for the end of the line. */
+    while (*line && *line != '\n' && (*line != '#' || continuation)) {
+      if (*line == '\\' && line[1] == '\n') {
+        continuation = 1;
+        line += 2;
+      } else if (*line == '#') {
+        do {
+          ++line;
+        } while (*line && *line != '\n');
+        if (*line == '\n')
+          ++line;
+      } else {
+        ++line;
+      }
+    }
+
     if (*line == '\n') {
       cp = line++;
     } else {
       cp = line;
     }
+    /* Now back cp up to be the last nonspace character */
     while (cp>val && TOR_ISSPACE(*(cp-1)))
       --cp;
 
     tor_assert(cp >= val);
+
+    /* Now copy out and decode the value. */
     *value_out = tor_strndup(val, cp-val);
+    if (continuation) {
+      char *v_out, *v_in;
+      v_out = v_in = *value_out;
+      while (*v_in) {
+        if (*v_in == '#') {
+          do {
+            ++v_in;
+          } while (*v_in && *v_in != '\n');
+          if (*v_in == '\n')
+            ++v_in;
+        } else if (v_in[0] == '\\' && v_in[1] == '\n') {
+          v_in += 2;
+        } else {
+          *v_out++ = *v_in++;
+        }
+      }
+      *v_out = '\0';
+    }
   }
 
   if (*line == '#') {

+ 56 - 0
src/test/test_util.c

@@ -100,6 +100,15 @@ test_util_config_line(void)
           "k4#a\n" "k5#abc\n" "k6 val #with comment\n"
           "kseven   \"a quoted 'string\"\n"
           "k8 \"a \\x71uoted\\n\\\"str\\\\ing\\t\\001\\01\\1\\\"\"\n"
+          "k9 a line that\\\n spans two lines.\n\n"
+          "k10 more than\\\n one contin\\\nuation\n"
+          "k11  \\\ncontinuation at the start\n"
+          "k12 line with a\\\n#comment\n embedded\n"
+          "k13\\\ncontinuation at the very start\n"
+          "k14 a line that has a comment and # ends with a slash \\\n"
+          "k15 this should be the next new line\n"
+          "k16 a line that has a comment and # ends without a slash \n"
+          "k17 this should be the next new line\n"
           , sizeof(buf));
   str = buf;
 
@@ -161,7 +170,54 @@ test_util_config_line(void)
   test_streq(k, "k8");
   test_streq(v, "a quoted\n\"str\\ing\t\x01\x01\x01\"");
   tor_free(k); tor_free(v);
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k9");
+  test_streq(v, "a line that spans two lines.");
+  tor_free(k); tor_free(v);
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k10");
+  test_streq(v, "more than one continuation");
+  tor_free(k); tor_free(v);
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k11");
+  test_streq(v, "continuation at the start");
+  tor_free(k); tor_free(v);
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k12");
+  test_streq(v, "line with a embedded");
+  tor_free(k); tor_free(v);
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k13");
+  test_streq(v, "continuation at the very start");
+  tor_free(k); tor_free(v);
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k14");
+  test_streq(v, "a line that has a comment and" );
+  tor_free(k); tor_free(v);
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k15");
+  test_streq(v, "this should be the next new line");
+  tor_free(k); tor_free(v);
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k16");
+  test_streq(v, "a line that has a comment and" );
+  tor_free(k); tor_free(v);
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k17");
+  test_streq(v, "this should be the next new line");
+  tor_free(k); tor_free(v);
+
   test_streq(str, "");
+
  done:
   tor_free(k);
   tor_free(v);