summaryrefslogtreecommitdiff
path: root/subversion/libsvn_subr/config_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_subr/config_file.c')
-rw-r--r--subversion/libsvn_subr/config_file.c321
1 files changed, 261 insertions, 60 deletions
diff --git a/subversion/libsvn_subr/config_file.c b/subversion/libsvn_subr/config_file.c
index e4a5936d78342..bd2ec828a5425 100644
--- a/subversion/libsvn_subr/config_file.c
+++ b/subversion/libsvn_subr/config_file.c
@@ -25,6 +25,8 @@
#include <apr_lib.h>
#include <apr_env.h>
+#include <apr_tables.h>
+
#include "config_impl.h"
#include "svn_io.h"
#include "svn_types.h"
@@ -37,8 +39,9 @@
#include "svn_user.h"
#include "svn_ctype.h"
-#include "svn_private_config.h"
+#include "private/svn_config_private.h"
#include "private/svn_subr_private.h"
+#include "svn_private_config.h"
#ifdef __HAIKU__
# include <FindDirectory.h>
@@ -52,8 +55,9 @@
/* File parsing context */
typedef struct parse_context_t
{
- /* This config struct */
- svn_config_t *cfg;
+ /* The configuration constructor. */
+ svn_config__constructor_t *constructor;
+ void *constructor_baton;
/* The stream struct */
svn_stream_t *stream;
@@ -64,10 +68,14 @@ typedef struct parse_context_t
/* Emulate an ungetc */
int ungotten_char;
+ /* We're currently parsing a section */
+ svn_boolean_t in_section;
+
/* Temporary strings */
svn_stringbuf_t *section;
svn_stringbuf_t *option;
svn_stringbuf_t *value;
+ svn_stringbuf_t *line_read;
/* Parser buffer for getc() to avoid call overhead into several libraries
for every character */
@@ -80,6 +88,109 @@ typedef struct parse_context_t
} parse_context_t;
+/* Config representation constructor */
+struct svn_config__constructor_t
+{
+ /* Constructor callbacks; see docs for svn_config__constructor_create. */
+ svn_config__open_section_fn open_section;
+ svn_config__close_section_fn close_section;
+ svn_config__add_value_fn add_value;
+};
+
+svn_config__constructor_t *
+svn_config__constructor_create(
+ svn_config__open_section_fn open_section_callback,
+ svn_config__close_section_fn close_section_callback,
+ svn_config__add_value_fn add_value_callback,
+ apr_pool_t *result_pool)
+{
+ svn_config__constructor_t *ctor = apr_palloc(result_pool, sizeof(*ctor));
+ ctor->open_section = open_section_callback;
+ ctor->close_section = close_section_callback;
+ ctor->add_value = add_value_callback;
+ return ctor;
+}
+
+/* Called after we've parsed a section name and before we start
+ parsing any options within that section. */
+static APR_INLINE svn_error_t *
+open_section(parse_context_t *ctx, svn_boolean_t *stop)
+{
+ if (ctx->constructor->open_section)
+ {
+ svn_error_t *err = ctx->constructor->open_section(
+ ctx->constructor_baton, ctx->section);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
+ {
+ *stop = TRUE;
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ else
+ return svn_error_trace(err);
+ }
+ }
+
+ *stop = FALSE;
+ ctx->in_section = TRUE;
+ return SVN_NO_ERROR;
+}
+
+/* Called after we've parsed all options within a section and before
+ we start parsing the next section. */
+static APR_INLINE svn_error_t *
+close_section(parse_context_t *ctx, svn_boolean_t *stop)
+{
+ ctx->in_section = FALSE;
+ if (ctx->constructor->close_section)
+ {
+ svn_error_t *err = ctx->constructor->close_section(
+ ctx->constructor_baton, ctx->section);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
+ {
+ *stop = TRUE;
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ else
+ return svn_error_trace(err);
+ }
+ }
+
+ *stop = FALSE;
+ return SVN_NO_ERROR;
+}
+
+/* Called every tyme we've parsed a complete (option, value) pair. */
+static APR_INLINE svn_error_t *
+add_value(parse_context_t *ctx, svn_boolean_t *stop)
+{
+ if (ctx->constructor->add_value)
+ {
+ svn_error_t *err = ctx->constructor->add_value(
+ ctx->constructor_baton, ctx->section,
+ ctx->option, ctx->value);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
+ {
+ *stop = TRUE;
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ else
+ return svn_error_trace(err);
+ }
+ }
+
+ *stop = FALSE;
+ return SVN_NO_ERROR;
+}
+
/* Emulate getc() because streams don't support it.
*
@@ -161,6 +272,57 @@ parser_ungetc(parse_context_t *ctx, int c)
return SVN_NO_ERROR;
}
+/* Read from CTX to the end of the line (or file) and write the data
+ into LINE. Previous contents of *LINE will be overwritten.
+ Returns the char that ended the line in *C; the char is either
+ EOF or newline. */
+static svn_error_t *
+parser_get_line(parse_context_t *ctx, svn_stringbuf_t *line, int *c)
+{
+ int ch;
+ svn_stringbuf_setempty(line);
+
+ /* Loop until EOF of newline. There is one extra iteration per
+ * parser buffer refill. The initial parser_getc() also ensures
+ * that the unget buffer is emptied. */
+ SVN_ERR(parser_getc(ctx, &ch));
+ while (ch != '\n' && ch != EOF)
+ {
+ const char *start, *newline;
+
+ /* Save the char we just read. */
+ svn_stringbuf_appendbyte(line, ch);
+
+ /* Scan the parser buffer for NL. */
+ start = ctx->parser_buffer + ctx->buffer_pos;
+ newline = memchr(start, '\n', ctx->buffer_size - ctx->buffer_pos);
+ if (newline)
+ {
+ /* NL found before the end of the of the buffer. */
+ svn_stringbuf_appendbytes(line, start, newline - start);
+ ch = '\n';
+ ctx->buffer_pos = newline - ctx->parser_buffer + 1;
+ break;
+ }
+ else
+ {
+ /* Hit the end of the buffer. This may be either due to
+ * buffer size or EOF. In any case, all (remaining) current
+ * buffer data is part of the line to read. */
+ const char *end = ctx->parser_buffer + ctx->buffer_size;
+ ctx->buffer_pos = ctx->buffer_size;
+
+ svn_stringbuf_appendbytes(line, start, end - start);
+ }
+
+ /* refill buffer, check for EOF */
+ SVN_ERR(parser_getc_plain(ctx, &ch));
+ }
+
+ *c = ch;
+ return SVN_NO_ERROR;
+}
+
/* Eat chars from STREAM until encounter non-whitespace, newline, or EOF.
Set *PCOUNT to the number of characters eaten, not counting the
last one, and return the last char read (the one that caused the
@@ -245,23 +407,16 @@ skip_bom(parse_context_t *ctx)
return SVN_NO_ERROR;
}
-/* Parse a single option value */
+/* Parse continuation lines of single option value if they exist. Append
+ * their contents, including *PCH, to CTX->VALUE. Return the first char
+ * of the next line or EOF in *PCH. */
static svn_error_t *
-parse_value(int *pch, parse_context_t *ctx)
+parse_value_continuation_lines(int *pch, parse_context_t *ctx)
{
svn_boolean_t end_of_val = FALSE;
- int ch;
+ svn_boolean_t stop;
+ int ch = *pch;
- /* Read the first line of the value */
- svn_stringbuf_setempty(ctx->value);
- SVN_ERR(parser_getc(ctx, &ch));
- while (ch != EOF && ch != '\n')
- /* last ch seen was ':' or '=' in parse_option. */
- {
- const char char_from_int = (char)ch;
- svn_stringbuf_appendbyte(ctx->value, char_from_int);
- SVN_ERR(parser_getc(ctx, &ch));
- }
/* Leading and trailing whitespace is ignored. */
svn_stringbuf_strip_whitespace(ctx->value);
@@ -273,8 +428,9 @@ parse_value(int *pch, parse_context_t *ctx)
{
/* At end of file. The value is complete, there can't be
any continuation lines. */
- svn_config_set(ctx->cfg, ctx->section->data,
- ctx->option->data, ctx->value->data);
+ SVN_ERR(add_value(ctx, &stop));
+ if (stop)
+ return SVN_NO_ERROR;
break;
}
else
@@ -310,16 +466,15 @@ parse_value(int *pch, parse_context_t *ctx)
else
{
/* This is a continuation line. Read it. */
- svn_stringbuf_appendbyte(ctx->value, ' ');
+ SVN_ERR(parser_ungetc(ctx, ch));
+ SVN_ERR(parser_get_line(ctx, ctx->line_read, &ch));
- while (ch != EOF && ch != '\n')
- {
- const char char_from_int = (char)ch;
- svn_stringbuf_appendbyte(ctx->value, char_from_int);
- SVN_ERR(parser_getc(ctx, &ch));
- }
/* Trailing whitespace is ignored. */
- svn_stringbuf_strip_whitespace(ctx->value);
+ svn_stringbuf_strip_whitespace(ctx->line_read);
+
+ svn_stringbuf_appendbyte(ctx->value, ' ');
+ svn_stringbuf_appendbytes(ctx->value, ctx->line_read->data,
+ ctx->line_read->len);
}
}
}
@@ -336,17 +491,33 @@ parse_option(int *pch, parse_context_t *ctx, apr_pool_t *scratch_pool)
{
svn_error_t *err = SVN_NO_ERROR;
int ch;
+ char *equals, *separator;
+
+ /* Read the first line. */
+ parser_ungetc(ctx, *pch); /* Yes, the first char is relevant. */
+ SVN_ERR(parser_get_line(ctx, ctx->line_read, &ch));
- svn_stringbuf_setempty(ctx->option);
- ch = *pch; /* Yes, the first char is relevant. */
- while (ch != EOF && ch != ':' && ch != '=' && ch != '\n')
+ /* Extract the option name from it. */
+ equals = strchr(ctx->line_read->data, '=');
+ if (equals)
+ {
+ /* Efficiently look for a colon separator prior to the equals sign.
+ * Since there is no strnchr, we limit the search by temporarily
+ * limiting the string. */
+ char *colon;
+ *equals = 0;
+ colon = strchr(ctx->line_read->data, ':');
+ *equals = '=';
+
+ separator = colon ? colon : equals;
+ }
+ else
{
- const char char_from_int = (char)ch;
- svn_stringbuf_appendbyte(ctx->option, char_from_int);
- SVN_ERR(parser_getc(ctx, &ch));
+ /* No '=' separator so far. Look for colon. */
+ separator = strchr(ctx->line_read->data, ':');
}
- if (ch != ':' && ch != '=')
+ if (!separator)
{
ch = EOF;
err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
@@ -356,8 +527,22 @@ parse_option(int *pch, parse_context_t *ctx, apr_pool_t *scratch_pool)
else
{
/* Whitespace around the name separator is ignored. */
- svn_stringbuf_strip_whitespace(ctx->option);
- err = parse_value(&ch, ctx);
+ const char *end = separator;
+ while (svn_ctype_isspace(*--end))
+ ;
+ while (svn_ctype_isspace(*++separator))
+ ;
+
+ /* Extract the option. It can't contain whitespace chars. */
+ svn_stringbuf_setempty(ctx->option);
+ svn_stringbuf_appendbytes(ctx->option, ctx->line_read->data,
+ end + 1 - ctx->line_read->data);
+
+ /* Extract the first line of the value. */
+ end = ctx->line_read->data + ctx->line_read->len;
+ svn_stringbuf_setempty(ctx->value);
+ svn_stringbuf_appendbytes(ctx->value, separator, end - separator);
+ err = parse_value_continuation_lines(&ch, ctx);
}
*pch = ch;
@@ -379,17 +564,12 @@ parse_section_name(int *pch, parse_context_t *ctx,
{
svn_error_t *err = SVN_NO_ERROR;
int ch;
+ char *terminal;
- svn_stringbuf_setempty(ctx->section);
- SVN_ERR(parser_getc(ctx, &ch));
- while (ch != EOF && ch != ']' && ch != '\n')
- {
- const char char_from_int = (char)ch;
- svn_stringbuf_appendbyte(ctx->section, char_from_int);
- SVN_ERR(parser_getc(ctx, &ch));
- }
+ SVN_ERR(parser_get_line(ctx, ctx->section, &ch));
+ terminal = strchr(ctx->section->data, ']');
- if (ch != ']')
+ if (!terminal)
{
ch = EOF;
err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
@@ -399,9 +579,8 @@ parse_section_name(int *pch, parse_context_t *ctx,
else
{
/* Everything from the ']' to the end of the line is ignored. */
- SVN_ERR(skip_to_eoln(ctx, &ch));
- if (ch != EOF)
- ++ctx->line;
+ *terminal = 0;
+ ctx->section->len = terminal - ctx->section->data;
}
*pch = ch;
@@ -531,6 +710,7 @@ svn_config__shallow_replace_section(svn_config_t *target,
}
+
svn_error_t *
svn_config__parse_file(svn_config_t *cfg, const char *file,
svn_boolean_t must_exist, apr_pool_t *result_pool)
@@ -554,7 +734,12 @@ svn_config__parse_file(svn_config_t *cfg, const char *file,
SVN_ERR(err);
stream = svn_stream_from_aprfile2(apr_file, FALSE, scratch_pool);
- err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
+ err = svn_config__parse_stream(stream,
+ svn_config__constructor_create(
+ NULL, NULL,
+ svn_config__default_add_value_fn,
+ scratch_pool),
+ cfg, scratch_pool);
if (err != SVN_NO_ERROR)
{
@@ -571,21 +756,27 @@ svn_config__parse_file(svn_config_t *cfg, const char *file,
}
svn_error_t *
-svn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream,
- apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+svn_config__parse_stream(svn_stream_t *stream,
+ svn_config__constructor_t *constructor,
+ void *constructor_baton,
+ apr_pool_t *scratch_pool)
{
parse_context_t *ctx;
+ svn_boolean_t stop;
int ch, count;
ctx = apr_palloc(scratch_pool, sizeof(*ctx));
- ctx->cfg = cfg;
+ ctx->constructor = constructor;
+ ctx->constructor_baton = constructor_baton;
ctx->stream = stream;
ctx->line = 1;
ctx->ungotten_char = EOF;
+ ctx->in_section = FALSE;
ctx->section = svn_stringbuf_create_empty(scratch_pool);
ctx->option = svn_stringbuf_create_empty(scratch_pool);
ctx->value = svn_stringbuf_create_empty(scratch_pool);
+ ctx->line_read = svn_stringbuf_create_empty(scratch_pool);
ctx->buffer_pos = 0;
ctx->buffer_size = 0;
ctx->hit_stream_eof = FALSE;
@@ -599,13 +790,23 @@ svn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream,
switch (ch)
{
case '[': /* Start of section header */
- if (count == 0)
- SVN_ERR(parse_section_name(&ch, ctx, scratch_pool));
- else
+ if (count != 0)
return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
_("line %d: Section header"
" must start in the first column"),
ctx->line);
+
+ /* Close the previous section before starting a new one. */
+ if (ctx->in_section)
+ {
+ SVN_ERR(close_section(ctx, &stop));
+ if (stop)
+ return SVN_NO_ERROR;
+ }
+ SVN_ERR(parse_section_name(&ch, ctx, scratch_pool));
+ SVN_ERR(open_section(ctx, &stop));
+ if (stop)
+ return SVN_NO_ERROR;
break;
case '#': /* Comment */
@@ -644,6 +845,9 @@ svn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream,
}
while (ch != EOF);
+ /* Emit the last close-section call to wrap up. */
+ if (ctx->in_section)
+ SVN_ERR(close_section(ctx, &stop));
return SVN_NO_ERROR;
}
@@ -945,12 +1149,12 @@ svn_config_ensure(const char *config_dir, apr_pool_t *pool)
"### http-timeout Timeout for HTTP requests in seconds"
NL
"### http-compression Whether to compress HTTP requests" NL
+ "### (yes/no/auto)." NL
"### http-max-connections Maximum number of parallel server" NL
"### connections to use for any given" NL
"### HTTP operation." NL
"### http-chunked-requests Whether to use chunked transfer" NL
"### encoding for HTTP requests body." NL
- "### neon-debug-mask Debug mask for Neon HTTP library" NL
"### ssl-authority-files List of files, each of a trusted CA"
NL
"### ssl-trust-default-ca Trust the system 'default' CAs" NL
@@ -1043,7 +1247,6 @@ svn_config_ensure(const char *config_dir, apr_pool_t *pool)
"### Most users will not need to explicitly set the http-library" NL
"### option, but valid values for the option include:" NL
"### 'serf': Serf-based module (Subversion 1.5 - present)" NL
- "### 'neon': Neon-based module (Subversion 1.0 - 1.7)" NL
"### Availability of these modules may depend on your specific" NL
"### Subversion distribution." NL
"###" NL
@@ -1068,7 +1271,6 @@ svn_config_ensure(const char *config_dir, apr_pool_t *pool)
"# http-proxy-username = blah" NL
"# http-proxy-password = doubleblah" NL
"# http-timeout = 60" NL
- "# neon-debug-mask = 130" NL
#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
"# store-plaintext-passwords = no" NL
#endif
@@ -1107,9 +1309,8 @@ svn_config_ensure(const char *config_dir, apr_pool_t *pool)
"# http-proxy-port = 7000" NL
"# http-proxy-username = defaultusername" NL
"# http-proxy-password = defaultpassword" NL
- "# http-compression = no" NL
+ "# http-compression = auto" NL
"# No http-timeout, so just use the builtin default." NL
- "# No neon-debug-mask, so neon debugging is disabled." NL
"# ssl-authority-files = /path/to/CAcert.pem;/path/to/CAcert2.pem" NL
"#" NL
"# Password / passphrase caching parameters:" NL
@@ -1421,7 +1622,7 @@ svn_config_get_user_config_path(const char **path,
if (! homedir)
return SVN_NO_ERROR;
*path = svn_dirent_join_many(pool,
- svn_dirent_canonicalize(homedir, pool),
+ homedir,
SVN_CONFIG__USR_DIRECTORY, fname, SVN_VA_NULL);
}
#endif /* WIN32 */