Commit c8e1538a authored by Nick Mathewson's avatar Nick Mathewson 🐚
Browse files

Merge remote branch 'sebastian/continuation'

parents 9b49a894 85125517
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
  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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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 != '#')
    /* 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
Original line number Diff line number Diff line
@@ -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);