Commit 58de695f authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

r15787@tombo: nickm | 2008-01-02 01:59:07 -0500

 Allow config values in quotes to contain special characters, with full C escape syntax.  With tests.  Addresses bug 557.


svn:r13021
parent 86f51808
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -6,6 +6,11 @@ Changes in version 0.2.0.16-alpha - 2008-01-??
      implementation also avoids realloc();realloc(); patterns that
      can contribute to memory fragmentation.

  o Minor features:
    - Configuration files now accept C-style strings as values.  This
      helps encode characters not allowed in the current configuration
      file format, such as newline or #.  Addresses bug 557.

  o Minor performance improvements:
    - Reference-count and share copies of address policy entries; only
      5% of them were actually distinct.
+3 −2
Original line number Diff line number Diff line
@@ -53,8 +53,9 @@ Display Tor version.
.LP
.TP
Other options can be specified either on the command-line (\fI--option
value\fR), or in the configuration file (\fIoption value\fR).
Options are case-insensitive.
value\fR), or in the configuration file (\fIoption value\fR or
\fIoption "value"\fR).  Options are case-insensitive.  C-style escaped
characters are allowed inside quoted values.
.LP
.TP
\fBBandwidthRate \fR\fIN\fR \fBbytes\fR|\fBKB\fR|\fBMB\fR|\fBGB\fR|\fBTB\fP
+109 −13
Original line number Diff line number Diff line
@@ -1924,6 +1924,95 @@ read_file_to_str(const char *filename, int flags, struct stat *stat_out)
  return string;
}

#define TOR_ISODIGIT(c) ('0' <= (c) && (c) <= '7')

/* DOCDOC */
static const char *
unescape_string(const char *s, char **result, size_t *size_out)
{
  const char *cp;
  char *out;
  tor_assert(s[0] == '\"');
  cp = s+1;
  while (1) {
    switch (*cp) {
      case '\0':
      case '\n':
        return NULL;
      case '\"':
        goto end_of_loop;
      case '\\':
        if ((cp[1] == 'x' || cp[1] == 'X')
            && TOR_ISXDIGIT(cp[2]) && TOR_ISXDIGIT(cp[3])) {
          cp += 4;
        } else if (TOR_ISODIGIT(cp[1])) {
          cp += 2;
          if (TOR_ISODIGIT(*cp)) ++cp;
          if (TOR_ISODIGIT(*cp)) ++cp;
        } else if (cp[1]) {
          cp += 2;
        } else {
          return NULL;
        }
        break;
      default:
        ++cp;
        break;
    }
  }
 end_of_loop:
  out = *result = tor_malloc(cp-s + 1);
  cp = s+1;
  while (1) {
    switch (*cp)
      {
      case '\"':
        *out = '\0';
        if (size_out) *size_out = out - *result;
        return cp+1;
      case '\0':
        tor_fragile_assert();
        tor_free(*result);
        return NULL;
      case '\\':
        switch (cp[1])
          {
          case 'n': *out++ = '\n'; cp += 2; break;
          case 'r': *out++ = '\r'; cp += 2; break;
          case 't': *out++ = '\t'; cp += 2; break;
          case 'x': case 'X':
            *out++ = ((hex_decode_digit(cp[2])<<4) +
                      hex_decode_digit(cp[3]));
            cp += 4;
            break;
          case '0': case '1': case '2': case '3': case '4': case '5':
          case '6': case '7':
            {
              int n = cp[1]-'0';
              cp += 2;
              if (TOR_ISODIGIT(*cp)) { n = n*8 + *cp-'0'; cp++; }
              if (TOR_ISODIGIT(*cp)) { n = n*8 + *cp-'0'; cp++; }
              if (n > 255) { tor_free(*result); return NULL; }
              *out++ = (char)n;
            }
            break;
          case '\'':
          case '\"':
          case '\\':
          case '\?':
            *out++ = cp[1];
            cp += 2;
            break;
          default:
            tor_free(*result); return NULL;
          }
        break;
      default:
        *out++ = *cp++;
      }
  }
}

/** Given a string containing part of a configuration file or similar format,
 * advance past comments and whitespace and try to parse a single line.  If we
 * parse a line successfully, set *<b>key_out</b> to a new string holding the
@@ -1965,13 +2054,20 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out)
    ++line;
  *key_out = tor_strndup(key, line-key);

  /* Skip until the value, writing nuls so key will be nul-terminated */
  /* Skip until the value. */
  while (*line == ' ' || *line == '\t')
    ++line;

  val = line;

  /* Find the end of the line. */
  if (*line == '\"') {
    line = unescape_string(line, value_out, NULL);
    while (*line == ' ' || *line == '\t')
      ++line;
    if (*line && *line != '#' && *line != '\n')
      return NULL;
  } else {
    while (*line && *line != '\n' && *line != '#')
      ++line;
    if (*line == '\n') {
@@ -1984,14 +2080,14 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out)

    tor_assert(cp >= val);
    *value_out = tor_strndup(val, cp-val);
  }

  if (*line == '#') {
    do {
      ++line;
    } while (*line && *line != '\n');
    if (*line == '\n')
      ++line;
  }
  while(TOR_ISSPACE(*line)) ++line;

  return line;
}
+18 −3
Original line number Diff line number Diff line
@@ -826,7 +826,10 @@ test_util(void)
  strlcpy(buf, "k v\n" " key    value with spaces   \n" "keykey val\n"
          "k2\n"
          "k3 \n" "\n" "   \n" "#comment\n"
          "k4#a\n" "k5#abc\n" "k6 val #with comment\n", sizeof(buf));
          "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"
          , sizeof(buf));
  str = buf;

  str = parse_config_line_from_str(str, &k, &v);
@@ -857,7 +860,7 @@ test_util(void)
  test_streq(k, "k3");
  test_streq(v, "");
  tor_free(k); tor_free(v);
  test_assert(!strcmpstart(str, "\n   \n"));
  test_assert(!strcmpstart(str, "#comment"));

  str = parse_config_line_from_str(str, &k, &v);
  test_streq(k, "k4");
@@ -875,6 +878,18 @@ test_util(void)
  test_streq(k, "k6");
  test_streq(v, "val");
  tor_free(k); tor_free(v);
  test_assert(!strcmpstart(str, "kseven"));

  str = parse_config_line_from_str(str, &k, &v);
  test_streq(k, "kseven");
  test_streq(v, "a quoted 'string");
  tor_free(k); tor_free(v);
  test_assert(!strcmpstart(str, "k8 "));

  str = parse_config_line_from_str(str, &k, &v);
  test_streq(k, "k8");
  test_streq(v, "a quoted\n\"str\\ing\t\x01\x01\x01\"");
  tor_free(k); tor_free(v);
  test_streq(str, "");

  /* Test for strcmpstart and strcmpend. */