aboutsummaryrefslogtreecommitdiffstats
path: root/src/config/ConfigFile.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/config/ConfigFile.cxx')
-rw-r--r--src/config/ConfigFile.cxx262
1 files changed, 151 insertions, 111 deletions
diff --git a/src/config/ConfigFile.cxx b/src/config/ConfigFile.cxx
index 1329c4cd4..b6164b8bc 100644
--- a/src/config/ConfigFile.cxx
+++ b/src/config/ConfigFile.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,28 +19,27 @@
#include "config.h"
#include "ConfigFile.hxx"
-#include "ConfigData.hxx"
+#include "Data.hxx"
+#include "Param.hxx"
+#include "Block.hxx"
#include "ConfigTemplates.hxx"
#include "util/Tokenizer.hxx"
#include "util/StringUtil.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
-#include "fs/Limits.hxx"
#include "fs/Path.hxx"
-#include "fs/FileSystem.hxx"
+#include "fs/io/FileReader.hxx"
+#include "fs/io/BufferedReader.hxx"
#include "Log.hxx"
#include <assert.h>
-#include <stdio.h>
-#define MAX_STRING_SIZE MPD_PATH_MAX+80
-
-#define CONF_COMMENT '#'
+static constexpr char CONF_COMMENT = '#';
static constexpr Domain config_file_domain("config_file");
static bool
-config_read_name_value(struct config_param *param, char *input, unsigned line,
+config_read_name_value(ConfigBlock &block, char *input, unsigned line,
Error &error)
{
Tokenizer tokenizer(input);
@@ -67,7 +66,7 @@ config_read_name_value(struct config_param *param, char *input, unsigned line,
return false;
}
- const struct block_param *bp = param->GetBlockParam(name);
+ const BlockParam *bp = block.GetBlockParam(name);
if (bp != nullptr) {
error.Format(config_file_domain,
"\"%s\" is duplicate, first defined on line %i",
@@ -75,27 +74,26 @@ config_read_name_value(struct config_param *param, char *input, unsigned line,
return false;
}
- param->AddBlockParam(name, value, line);
+ block.AddBlockParam(name, value, line);
return true;
}
-static struct config_param *
-config_read_block(FILE *fp, int *count, char *string, Error &error)
+static ConfigBlock *
+config_read_block(BufferedReader &reader, Error &error)
{
- struct config_param *ret = new config_param(*count);
+ auto *ret = new ConfigBlock(reader.GetLineNumber());
while (true) {
- char *line;
-
- line = fgets(string, MAX_STRING_SIZE, fp);
+ char *line = reader.ReadLine();
if (line == nullptr) {
delete ret;
- error.Set(config_file_domain,
- "Expected '}' before end-of-file");
+
+ if (reader.Check(error))
+ error.Set(config_file_domain,
+ "Expected '}' before end-of-file");
return nullptr;
}
- (*count)++;
line = StripLeft(line);
if (*line == 0 || *line == CONF_COMMENT)
continue;
@@ -108,8 +106,8 @@ config_read_block(FILE *fp, int *count, char *string, Error &error)
if (*line != 0 && *line != CONF_COMMENT) {
delete ret;
error.Format(config_file_domain,
- "line %i: Unknown tokens after '}'",
- *count);
+ "line %y: Unknown tokens after '}'",
+ reader.GetLineNumber());
return nullptr;
}
@@ -118,10 +116,11 @@ config_read_block(FILE *fp, int *count, char *string, Error &error)
/* parse name and value */
- if (!config_read_name_value(ret, line, *count, error)) {
+ if (!config_read_name_value(*ret, line, reader.GetLineNumber(),
+ error)) {
assert(*line != 0);
delete ret;
- error.FormatPrefix("line %i: ", *count);
+ error.FormatPrefix("line %u: ", reader.GetLineNumber());
return nullptr;
}
}
@@ -129,6 +128,64 @@ config_read_block(FILE *fp, int *count, char *string, Error &error)
gcc_nonnull_all
static void
+Append(ConfigBlock *&head, ConfigBlock *p)
+{
+ assert(p->next == nullptr);
+
+ auto **i = &head;
+ while (*i != nullptr)
+ i = &(*i)->next;
+
+ *i = p;
+}
+
+static bool
+ReadConfigBlock(ConfigData &config_data, BufferedReader &reader,
+ const char *name, ConfigBlockOption o,
+ Tokenizer &tokenizer,
+ Error &error)
+{
+ const unsigned i = unsigned(o);
+ const ConfigTemplate &option = config_block_templates[i];
+ ConfigBlock *&head = config_data.blocks[i];
+
+ if (head != nullptr && !option.repeatable) {
+ ConfigBlock *block = head;
+ error.Format(config_file_domain,
+ "config parameter \"%s\" is first defined "
+ "on line %d and redefined on line %u\n",
+ name, block->line,
+ reader.GetLineNumber());
+ return false;
+ }
+
+ /* now parse the block or the value */
+
+ if (tokenizer.CurrentChar() != '{') {
+ error.Format(config_file_domain,
+ "line %u: '{' expected",
+ reader.GetLineNumber());
+ return false;
+ }
+
+ char *line = StripLeft(tokenizer.Rest() + 1);
+ if (*line != 0 && *line != CONF_COMMENT) {
+ error.Format(config_file_domain,
+ "line %u: Unknown tokens after '{'",
+ reader.GetLineNumber());
+ return false;
+ }
+
+ auto *param = config_read_block(reader, error);
+ if (param == nullptr)
+ return false;
+
+ Append(head, param);
+ return true;
+}
+
+gcc_nonnull_all
+static void
Append(config_param *&head, config_param *p)
{
assert(p->next == nullptr);
@@ -141,21 +198,62 @@ Append(config_param *&head, config_param *p)
}
static bool
-ReadConfigFile(ConfigData &config_data, FILE *fp, Error &error)
+ReadConfigParam(ConfigData &config_data, BufferedReader &reader,
+ const char *name, ConfigOption o,
+ Tokenizer &tokenizer,
+ Error &error)
{
- assert(fp != nullptr);
+ const unsigned i = unsigned(o);
+ const ConfigTemplate &option = config_param_templates[i];
+ config_param *&head = config_data.params[i];
- char string[MAX_STRING_SIZE + 1];
- int count = 0;
- struct config_param *param;
+ if (head != nullptr && !option.repeatable) {
+ struct config_param *param = head;
+ error.Format(config_file_domain,
+ "config parameter \"%s\" is first defined "
+ "on line %d and redefined on line %u\n",
+ name, param->line,
+ reader.GetLineNumber());
+ return false;
+ }
- while (fgets(string, MAX_STRING_SIZE, fp)) {
- char *line;
- const char *name, *value;
+ /* now parse the block or the value */
- count++;
+ const char *value = tokenizer.NextString(error);
+ if (value == nullptr) {
+ if (tokenizer.IsEnd())
+ error.Format(config_file_domain,
+ "line %u: Value missing",
+ reader.GetLineNumber());
+ else
+ error.FormatPrefix("line %u: ",
+ reader.GetLineNumber());
- line = StripLeft(string);
+ return false;
+ }
+
+ if (!tokenizer.IsEnd() &&
+ tokenizer.CurrentChar() != CONF_COMMENT) {
+ error.Format(config_file_domain,
+ "line %u: Unknown tokens after value",
+ reader.GetLineNumber());
+ return false;
+ }
+
+ auto *param = new config_param(value, reader.GetLineNumber());
+ Append(head, param);
+ return true;
+}
+
+static bool
+ReadConfigFile(ConfigData &config_data, BufferedReader &reader, Error &error)
+{
+ while (true) {
+ char *line = reader.ReadLine();
+ if (line == nullptr)
+ return true;
+
+ line = StripLeft(line);
if (*line == 0 || *line == CONF_COMMENT)
continue;
@@ -163,10 +261,10 @@ ReadConfigFile(ConfigData &config_data, FILE *fp, Error &error)
by either the value or '{' */
Tokenizer tokenizer(line);
- name = tokenizer.NextWord(error);
+ const char *name = tokenizer.NextWord(error);
if (name == nullptr) {
assert(!tokenizer.IsEnd());
- error.FormatPrefix("line %i: ", count);
+ error.FormatPrefix("line %u: ", reader.GetLineNumber());
return false;
}
@@ -174,79 +272,23 @@ ReadConfigFile(ConfigData &config_data, FILE *fp, Error &error)
"repeatable" flag */
const ConfigOption o = ParseConfigOptionName(name);
- if (o == CONF_MAX) {
- error.Format(config_file_domain,
- "unrecognized parameter in config file at "
- "line %i: %s\n", count, name);
- return false;
- }
-
- const unsigned i = unsigned(o);
- const ConfigTemplate &option = config_templates[i];
- config_param *&head = config_data.params[i];
-
- if (head != nullptr && !option.repeatable) {
- param = head;
- error.Format(config_file_domain,
- "config parameter \"%s\" is first defined "
- "on line %i and redefined on line %i\n",
- name, param->line, count);
- return false;
- }
-
- /* now parse the block or the value */
-
- if (option.block) {
- /* it's a block, call config_read_block() */
-
- if (tokenizer.CurrentChar() != '{') {
- error.Format(config_file_domain,
- "line %i: '{' expected", count);
- return false;
- }
-
- line = StripLeft(tokenizer.Rest() + 1);
- if (*line != 0 && *line != CONF_COMMENT) {
- error.Format(config_file_domain,
- "line %i: Unknown tokens after '{'",
- count);
+ ConfigBlockOption bo;
+ if (o != ConfigOption::MAX) {
+ if (!ReadConfigParam(config_data, reader, name, o,
+ tokenizer, error))
return false;
- }
-
- param = config_read_block(fp, &count, string, error);
- if (param == nullptr) {
+ } else if ((bo = ParseConfigBlockOptionName(name)) != ConfigBlockOption::MAX) {
+ if (!ReadConfigBlock(config_data, reader, name, bo,
+ tokenizer, error))
return false;
- }
} else {
- /* a string value */
-
- value = tokenizer.NextString(error);
- if (value == nullptr) {
- if (tokenizer.IsEnd())
- error.Format(config_file_domain,
- "line %i: Value missing",
- count);
- else
- error.FormatPrefix("line %i: ", count);
-
- return false;
- }
-
- if (!tokenizer.IsEnd() &&
- tokenizer.CurrentChar() != CONF_COMMENT) {
- error.Format(config_file_domain,
- "line %i: Unknown tokens after value",
- count);
- return false;
- }
-
- param = new config_param(value, count);
+ error.Format(config_file_domain,
+ "unrecognized parameter in config file at "
+ "line %u: %s\n",
+ reader.GetLineNumber(), name);
+ return false;
}
-
- Append(head, param);
}
-
- return true;
}
bool
@@ -257,13 +299,11 @@ ReadConfigFile(ConfigData &config_data, Path path, Error &error)
FormatDebug(config_file_domain, "loading file %s", path_utf8.c_str());
- FILE *fp = FOpen(path, FOpenMode::ReadText);
- if (fp == nullptr) {
- error.FormatErrno("Failed to open %s", path_utf8.c_str());
+ FileReader file(path, error);
+ if (!file.IsDefined())
return false;
- }
- bool result = ReadConfigFile(config_data, fp, error);
- fclose(fp);
- return result;
+ BufferedReader reader(file);
+ return ReadConfigFile(config_data, reader, error) &&
+ reader.Check(error);
}