aboutsummaryrefslogtreecommitdiffstats
path: root/src/config
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/config/ConfigData.cxx159
-rw-r--r--src/config/ConfigData.hxx134
-rw-r--r--src/config/ConfigDefaults.hxx26
-rw-r--r--src/config/ConfigError.cxx23
-rw-r--r--src/config/ConfigError.hxx25
-rw-r--r--src/config/ConfigFile.cxx269
-rw-r--r--src/config/ConfigFile.hxx30
-rw-r--r--src/config/ConfigGlobal.cxx192
-rw-r--r--src/config/ConfigGlobal.hxx106
-rw-r--r--src/config/ConfigOption.hxx91
-rw-r--r--src/config/ConfigParser.cxx40
-rw-r--r--src/config/ConfigParser.hxx26
-rw-r--r--src/config/ConfigPath.cxx131
-rw-r--r--src/config/ConfigPath.hxx29
-rw-r--r--src/config/ConfigTemplates.cxx97
-rw-r--r--src/config/ConfigTemplates.hxx31
16 files changed, 1409 insertions, 0 deletions
diff --git a/src/config/ConfigData.cxx b/src/config/ConfigData.cxx
new file mode 100644
index 000000000..70e1e55ed
--- /dev/null
+++ b/src/config/ConfigData.cxx
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ConfigData.hxx"
+#include "ConfigParser.hxx"
+#include "ConfigPath.hxx"
+#include "util/Error.hxx"
+#include "fs/AllocatedPath.hxx"
+#include "system/FatalError.hxx"
+
+#include <assert.h>
+#include <stdlib.h>
+
+int
+block_param::GetIntValue() const
+{
+ char *endptr;
+ long value2 = strtol(value.c_str(), &endptr, 0);
+ if (*endptr != 0)
+ FormatFatalError("Not a valid number in line %i", line);
+
+ return value2;
+}
+
+unsigned
+block_param::GetUnsignedValue() const
+{
+ char *endptr;
+ unsigned long value2 = strtoul(value.c_str(), &endptr, 0);
+ if (*endptr != 0)
+ FormatFatalError("Not a valid number in line %i", line);
+
+ return (unsigned)value2;
+}
+
+bool
+block_param::GetBoolValue() const
+{
+ bool value2;
+ if (!get_bool(value.c_str(), &value2))
+ FormatFatalError("%s is not a boolean value (yes, true, 1) or "
+ "(no, false, 0) on line %i\n",
+ name.c_str(), line);
+
+ return value2;
+}
+
+config_param::config_param(const char *_value, int _line)
+ :next(nullptr), value(_value), line(_line), used(false) {}
+
+config_param::~config_param()
+{
+ delete next;
+}
+
+const block_param *
+config_param::GetBlockParam(const char *name) const
+{
+ for (const auto &i : block_params) {
+ if (i.name == name) {
+ i.used = true;
+ return &i;
+ }
+ }
+
+ return NULL;
+}
+
+const char *
+config_param::GetBlockValue(const char *name, const char *default_value) const
+{
+ const block_param *bp = GetBlockParam(name);
+ if (bp == nullptr)
+ return default_value;
+
+ return bp->value.c_str();
+}
+
+AllocatedPath
+config_param::GetBlockPath(const char *name, const char *default_value,
+ Error &error) const
+{
+ assert(!error.IsDefined());
+
+ int line2 = line;
+ const char *s;
+
+ const block_param *bp = GetBlockParam(name);
+ if (bp != nullptr) {
+ line2 = bp->line;
+ s = bp->value.c_str();
+ } else {
+ if (default_value == nullptr)
+ return AllocatedPath::Null();
+
+ s = default_value;
+ }
+
+ AllocatedPath path = ParsePath(s, error);
+ if (gcc_unlikely(path.IsNull()))
+ error.FormatPrefix("Invalid path in \"%s\" at line %i: ",
+ name, line2);
+
+ return path;
+}
+
+AllocatedPath
+config_param::GetBlockPath(const char *name, Error &error) const
+{
+ return GetBlockPath(name, nullptr, error);
+}
+
+int
+config_param::GetBlockValue(const char *name, int default_value) const
+{
+ const block_param *bp = GetBlockParam(name);
+ if (bp == nullptr)
+ return default_value;
+
+ return bp->GetIntValue();
+}
+
+unsigned
+config_param::GetBlockValue(const char *name, unsigned default_value) const
+{
+ const block_param *bp = GetBlockParam(name);
+ if (bp == nullptr)
+ return default_value;
+
+ return bp->GetUnsignedValue();
+}
+
+gcc_pure
+bool
+config_param::GetBlockValue(const char *name, bool default_value) const
+{
+ const block_param *bp = GetBlockParam(name);
+ if (bp == NULL)
+ return default_value;
+
+ return bp->GetBoolValue();
+}
diff --git a/src/config/ConfigData.hxx b/src/config/ConfigData.hxx
new file mode 100644
index 000000000..e42d674ba
--- /dev/null
+++ b/src/config/ConfigData.hxx
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_DATA_HXX
+#define MPD_CONFIG_DATA_HXX
+
+#include "ConfigOption.hxx"
+#include "Compiler.h"
+
+#include <string>
+#include <array>
+#include <vector>
+
+class AllocatedPath;
+class Error;
+
+struct block_param {
+ std::string name;
+ std::string value;
+ int line;
+
+ /**
+ * This flag is false when nobody has queried the value of
+ * this option yet.
+ */
+ mutable bool used;
+
+ gcc_nonnull_all
+ block_param(const char *_name, const char *_value, int _line=-1)
+ :name(_name), value(_value), line(_line), used(false) {}
+
+ gcc_pure
+ int GetIntValue() const;
+
+ gcc_pure
+ unsigned GetUnsignedValue() const;
+
+ gcc_pure
+ bool GetBoolValue() const;
+};
+
+struct config_param {
+ /**
+ * The next config_param with the same name. The destructor
+ * deletes the whole chain.
+ */
+ struct config_param *next;
+
+ std::string value;
+
+ unsigned int line;
+
+ std::vector<block_param> block_params;
+
+ /**
+ * This flag is false when nobody has queried the value of
+ * this option yet.
+ */
+ bool used;
+
+ config_param(int _line=-1)
+ :next(nullptr), line(_line), used(false) {}
+
+ gcc_nonnull_all
+ config_param(const char *_value, int _line=-1);
+
+ config_param(const config_param &) = delete;
+
+ ~config_param();
+
+ config_param &operator=(const config_param &) = delete;
+
+ /**
+ * Determine if this is a "null" instance, i.e. an empty
+ * object that was synthesized and not loaded from a
+ * configuration file.
+ */
+ bool IsNull() const {
+ return line == unsigned(-1);
+ }
+
+ gcc_nonnull_all
+ void AddBlockParam(const char *_name, const char *_value,
+ int _line=-1) {
+ block_params.emplace_back(_name, _value, _line);
+ }
+
+ gcc_nonnull_all gcc_pure
+ const block_param *GetBlockParam(const char *_name) const;
+
+ gcc_pure
+ const char *GetBlockValue(const char *name,
+ const char *default_value=nullptr) const;
+
+ /**
+ * Same as config_dup_path(), but looks up the setting in the
+ * specified block.
+ */
+ AllocatedPath GetBlockPath(const char *name, const char *default_value,
+ Error &error) const;
+
+ AllocatedPath GetBlockPath(const char *name, Error &error) const;
+
+ gcc_pure
+ int GetBlockValue(const char *name, int default_value) const;
+
+ gcc_pure
+ unsigned GetBlockValue(const char *name, unsigned default_value) const;
+
+ gcc_pure
+ bool GetBlockValue(const char *name, bool default_value) const;
+};
+
+struct ConfigData {
+ std::array<config_param *, std::size_t(CONF_MAX)> params;
+};
+
+#endif
diff --git a/src/config/ConfigDefaults.hxx b/src/config/ConfigDefaults.hxx
new file mode 100644
index 000000000..c50f28c91
--- /dev/null
+++ b/src/config/ConfigDefaults.hxx
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_DEFAULTS_HXX
+#define MPD_CONFIG_DEFAULTS_HXX
+
+static constexpr unsigned DEFAULT_PLAYLIST_MAX_LENGTH = 16 * 1024;
+static constexpr bool DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS = false;
+
+#endif
diff --git a/src/config/ConfigError.cxx b/src/config/ConfigError.cxx
new file mode 100644
index 000000000..70aff7175
--- /dev/null
+++ b/src/config/ConfigError.cxx
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ConfigError.hxx"
+#include "util/Domain.hxx"
+
+const Domain config_domain("config");
diff --git a/src/config/ConfigError.hxx b/src/config/ConfigError.hxx
new file mode 100644
index 000000000..cbfa79df3
--- /dev/null
+++ b/src/config/ConfigError.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_ERROR_HXX
+#define MPD_CONFIG_ERROR_HXX
+
+extern const class Domain config_domain;
+
+#endif
diff --git a/src/config/ConfigFile.cxx b/src/config/ConfigFile.cxx
new file mode 100644
index 000000000..f045213a4
--- /dev/null
+++ b/src/config/ConfigFile.cxx
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ConfigFile.hxx"
+#include "ConfigData.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 "Log.hxx"
+
+#include <assert.h>
+#include <stdio.h>
+
+#define MAX_STRING_SIZE MPD_PATH_MAX+80
+
+#define CONF_COMMENT '#'
+
+static constexpr Domain config_file_domain("config_file");
+
+static bool
+config_read_name_value(struct config_param *param, char *input, unsigned line,
+ Error &error)
+{
+ Tokenizer tokenizer(input);
+
+ const char *name = tokenizer.NextWord(error);
+ if (name == nullptr) {
+ assert(!tokenizer.IsEnd());
+ return false;
+ }
+
+ const char *value = tokenizer.NextString(error);
+ if (value == nullptr) {
+ if (tokenizer.IsEnd()) {
+ error.Set(config_file_domain, "Value missing");
+ } else {
+ assert(error.IsDefined());
+ }
+
+ return false;
+ }
+
+ if (!tokenizer.IsEnd() && tokenizer.CurrentChar() != CONF_COMMENT) {
+ error.Set(config_file_domain, "Unknown tokens after value");
+ return false;
+ }
+
+ const struct block_param *bp = param->GetBlockParam(name);
+ if (bp != nullptr) {
+ error.Format(config_file_domain,
+ "\"%s\" is duplicate, first defined on line %i",
+ name, bp->line);
+ return false;
+ }
+
+ param->AddBlockParam(name, value, line);
+ return true;
+}
+
+static struct config_param *
+config_read_block(FILE *fp, int *count, char *string, Error &error)
+{
+ struct config_param *ret = new config_param(*count);
+
+ while (true) {
+ char *line;
+
+ line = fgets(string, MAX_STRING_SIZE, fp);
+ if (line == nullptr) {
+ delete ret;
+ error.Set(config_file_domain,
+ "Expected '}' before end-of-file");
+ return nullptr;
+ }
+
+ (*count)++;
+ line = strchug_fast(line);
+ if (*line == 0 || *line == CONF_COMMENT)
+ continue;
+
+ if (*line == '}') {
+ /* end of this block; return from the function
+ (and from this "while" loop) */
+
+ line = strchug_fast(line + 1);
+ if (*line != 0 && *line != CONF_COMMENT) {
+ delete ret;
+ error.Format(config_file_domain,
+ "line %i: Unknown tokens after '}'",
+ *count);
+ return nullptr;
+ }
+
+ return ret;
+ }
+
+ /* parse name and value */
+
+ if (!config_read_name_value(ret, line, *count, error)) {
+ assert(*line != 0);
+ delete ret;
+ error.FormatPrefix("line %i: ", *count);
+ return nullptr;
+ }
+ }
+}
+
+gcc_nonnull_all
+static void
+Append(config_param *&head, config_param *p)
+{
+ assert(p->next == nullptr);
+
+ config_param **i = &head;
+ while (*i != nullptr)
+ i = &(*i)->next;
+
+ *i = p;
+}
+
+static bool
+ReadConfigFile(ConfigData &config_data, FILE *fp, Error &error)
+{
+ assert(fp != nullptr);
+
+ char string[MAX_STRING_SIZE + 1];
+ int count = 0;
+ struct config_param *param;
+
+ while (fgets(string, MAX_STRING_SIZE, fp)) {
+ char *line;
+ const char *name, *value;
+
+ count++;
+
+ line = strchug_fast(string);
+ if (*line == 0 || *line == CONF_COMMENT)
+ continue;
+
+ /* the first token in each line is the name, followed
+ by either the value or '{' */
+
+ Tokenizer tokenizer(line);
+ name = tokenizer.NextWord(error);
+ if (name == nullptr) {
+ assert(!tokenizer.IsEnd());
+ error.FormatPrefix("line %i: ", count);
+ return false;
+ }
+
+ /* get the definition of that option, and check the
+ "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 = strchug_fast(tokenizer.Rest() + 1);
+ if (*line != 0 && *line != CONF_COMMENT) {
+ error.Format(config_file_domain,
+ "line %i: Unknown tokens after '{'",
+ count);
+ return false;
+ }
+
+ param = config_read_block(fp, &count, string, error);
+ if (param == nullptr) {
+ 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);
+ }
+
+ Append(head, param);
+ }
+
+ return true;
+}
+
+bool
+ReadConfigFile(ConfigData &config_data, Path path, Error &error)
+{
+ assert(!path.IsNull());
+ const std::string path_utf8 = path.ToUTF8();
+
+ 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());
+ return false;
+ }
+
+ bool result = ReadConfigFile(config_data, fp, error);
+ fclose(fp);
+ return result;
+}
diff --git a/src/config/ConfigFile.hxx b/src/config/ConfigFile.hxx
new file mode 100644
index 000000000..b87182c6a
--- /dev/null
+++ b/src/config/ConfigFile.hxx
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_FILE_HXX
+#define MPD_CONFIG_FILE_HXX
+
+class Error;
+class Path;
+struct ConfigData;
+
+bool
+ReadConfigFile(ConfigData &data, Path path, Error &error);
+
+#endif
diff --git a/src/config/ConfigGlobal.cxx b/src/config/ConfigGlobal.cxx
new file mode 100644
index 000000000..dd76e3ca3
--- /dev/null
+++ b/src/config/ConfigGlobal.cxx
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ConfigGlobal.hxx"
+#include "ConfigParser.hxx"
+#include "ConfigData.hxx"
+#include "ConfigFile.hxx"
+#include "ConfigPath.hxx"
+#include "ConfigError.hxx"
+#include "fs/Path.hxx"
+#include "fs/AllocatedPath.hxx"
+#include "util/Error.hxx"
+#include "system/FatalError.hxx"
+#include "Log.hxx"
+
+#include <stdlib.h>
+
+static ConfigData config_data;
+
+void config_global_finish(void)
+{
+ for (auto i : config_data.params)
+ delete i;
+}
+
+void config_global_init(void)
+{
+}
+
+bool
+ReadConfigFile(Path path, Error &error)
+{
+ return ReadConfigFile(config_data, path, error);
+}
+
+static void
+Check(const config_param *param)
+{
+ if (!param->used)
+ /* this whole config_param was not queried at all -
+ the feature might be disabled at compile time?
+ Silently ignore it here. */
+ return;
+
+ for (const auto &i : param->block_params) {
+ if (!i.used)
+ FormatWarning(config_domain,
+ "option '%s' on line %i was not recognized",
+ i.name.c_str(), i.line);
+ }
+}
+
+void config_global_check(void)
+{
+ for (auto i : config_data.params)
+ for (const config_param *p = i; p != nullptr; p = p->next)
+ Check(p);
+}
+
+const struct config_param *
+config_get_next_param(ConfigOption option, const struct config_param * last)
+{
+ config_param *param = last != nullptr
+ ? last->next
+ : config_data.params[unsigned(option)];
+ if (param != nullptr)
+ param->used = true;
+ return param;
+}
+
+const config_param *
+config_find_block(ConfigOption option, const char *key, const char *value)
+{
+ const config_param *param = nullptr;
+ while ((param = config_get_next_param(option, param)) != nullptr) {
+ const char *value2 = param->GetBlockValue(key);
+ if (value2 == nullptr)
+ FormatFatalError("block without '%s' name in line %d",
+ key, param->line);
+
+ if (strcmp(value2, value) == 0)
+ return param;
+ }
+
+ return nullptr;
+}
+
+const char *
+config_get_string(ConfigOption option, const char *default_value)
+{
+ const struct config_param *param = config_get_param(option);
+
+ if (param == nullptr)
+ return default_value;
+
+ return param->value.c_str();
+}
+
+AllocatedPath
+config_get_path(ConfigOption option, Error &error)
+{
+ const struct config_param *param = config_get_param(option);
+ if (param == nullptr)
+ return AllocatedPath::Null();
+
+ return config_parse_path(param, error);
+}
+
+AllocatedPath
+config_parse_path(const struct config_param *param, Error & error)
+{
+ AllocatedPath path = ParsePath(param->value.c_str(), error);
+ if (gcc_unlikely(path.IsNull()))
+ error.FormatPrefix("Invalid path at line %i: ",
+ param->line);
+
+ return path;
+}
+
+unsigned
+config_get_unsigned(ConfigOption option, unsigned default_value)
+{
+ const struct config_param *param = config_get_param(option);
+ long value;
+ char *endptr;
+
+ if (param == nullptr)
+ return default_value;
+
+ value = strtol(param->value.c_str(), &endptr, 0);
+ if (*endptr != 0 || value < 0)
+ FormatFatalError("Not a valid non-negative number in line %i",
+ param->line);
+
+ return (unsigned)value;
+}
+
+unsigned
+config_get_positive(ConfigOption option, unsigned default_value)
+{
+ const struct config_param *param = config_get_param(option);
+ long value;
+ char *endptr;
+
+ if (param == nullptr)
+ return default_value;
+
+ value = strtol(param->value.c_str(), &endptr, 0);
+ if (*endptr != 0)
+ FormatFatalError("Not a valid number in line %i", param->line);
+
+ if (value <= 0)
+ FormatFatalError("Not a positive number in line %i",
+ param->line);
+
+ return (unsigned)value;
+}
+
+bool
+config_get_bool(ConfigOption option, bool default_value)
+{
+ const struct config_param *param = config_get_param(option);
+ bool success, value;
+
+ if (param == nullptr)
+ return default_value;
+
+ success = get_bool(param->value.c_str(), &value);
+ if (!success)
+ FormatFatalError("Expected boolean value (yes, true, 1) or "
+ "(no, false, 0) on line %i\n",
+ param->line);
+
+ return value;
+}
diff --git a/src/config/ConfigGlobal.hxx b/src/config/ConfigGlobal.hxx
new file mode 100644
index 000000000..84ef7dd5f
--- /dev/null
+++ b/src/config/ConfigGlobal.hxx
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_GLOBAL_HXX
+#define MPD_CONFIG_GLOBAL_HXX
+
+#include "ConfigOption.hxx"
+#include "Compiler.h"
+
+class Error;
+class Path;
+class AllocatedPath;
+
+void config_global_init(void);
+void config_global_finish(void);
+
+/**
+ * Call this function after all configuration has been evaluated. It
+ * checks for unused parameters, and logs warnings.
+ */
+void config_global_check(void);
+
+bool
+ReadConfigFile(Path path, Error &error);
+
+/* don't free the returned value
+ set _last_ to nullptr to get first entry */
+gcc_pure
+const struct config_param *
+config_get_next_param(enum ConfigOption option,
+ const struct config_param *last);
+
+gcc_pure
+static inline const struct config_param *
+config_get_param(enum ConfigOption option)
+{
+ return config_get_next_param(option, nullptr);
+}
+
+/**
+ * Find a block with a matching attribute.
+ *
+ * @param option the blocks to search
+ * @param key the attribute name
+ * @param value the expected attribute value
+ */
+gcc_pure
+const config_param *
+config_find_block(ConfigOption option, const char *key, const char *value);
+
+/* Note on gcc_pure: Some of the functions declared pure are not
+ really pure in strict sense. They have side effect such that they
+ validate parameter's value and signal an error if it's invalid.
+ However, if the argument was already validated or we don't care
+ about the argument at all, this may be ignored so in the end, we
+ should be fine with calling those functions pure. */
+
+gcc_pure
+const char *
+config_get_string(enum ConfigOption option, const char *default_value);
+
+/**
+ * Returns an optional configuration variable which contains an
+ * absolute path. If there is a tilde prefix, it is expanded.
+ * Returns AllocatedPath::Null() if the value is not present. If the path
+ * could not be parsed, returns AllocatedPath::Null() and sets the error.
+ */
+AllocatedPath
+config_get_path(enum ConfigOption option, Error &error);
+
+/**
+ * Parse a configuration parameter as a path.
+ * If there is a tilde prefix, it is expanded. If the path could
+ * not be parsed, returns AllocatedPath::Null() and sets the error.
+ */
+AllocatedPath
+config_parse_path(const struct config_param *param, Error & error_r);
+
+gcc_pure
+unsigned
+config_get_unsigned(enum ConfigOption option, unsigned default_value);
+
+gcc_pure
+unsigned
+config_get_positive(enum ConfigOption option, unsigned default_value);
+
+gcc_pure
+bool config_get_bool(enum ConfigOption option, bool default_value);
+
+#endif
diff --git a/src/config/ConfigOption.hxx b/src/config/ConfigOption.hxx
new file mode 100644
index 000000000..506c9e9dc
--- /dev/null
+++ b/src/config/ConfigOption.hxx
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_OPTION_HXX
+#define MPD_CONFIG_OPTION_HXX
+
+#include "Compiler.h"
+
+enum ConfigOption {
+ CONF_MUSIC_DIR,
+ CONF_PLAYLIST_DIR,
+ CONF_FOLLOW_INSIDE_SYMLINKS,
+ CONF_FOLLOW_OUTSIDE_SYMLINKS,
+ CONF_DB_FILE,
+ CONF_STICKER_FILE,
+ CONF_LOG_FILE,
+ CONF_PID_FILE,
+ CONF_STATE_FILE,
+ CONF_RESTORE_PAUSED,
+ CONF_USER,
+ CONF_GROUP,
+ CONF_BIND_TO_ADDRESS,
+ CONF_PORT,
+ CONF_LOG_LEVEL,
+ CONF_ZEROCONF_NAME,
+ CONF_ZEROCONF_ENABLED,
+ CONF_PASSWORD,
+ CONF_DEFAULT_PERMS,
+ CONF_AUDIO_OUTPUT,
+ CONF_AUDIO_OUTPUT_FORMAT,
+ CONF_MIXER_TYPE,
+ CONF_REPLAYGAIN,
+ CONF_REPLAYGAIN_PREAMP,
+ CONF_REPLAYGAIN_MISSING_PREAMP,
+ CONF_REPLAYGAIN_LIMIT,
+ CONF_VOLUME_NORMALIZATION,
+ CONF_SAMPLERATE_CONVERTER,
+ CONF_AUDIO_BUFFER_SIZE,
+ CONF_BUFFER_BEFORE_PLAY,
+ CONF_HTTP_PROXY_HOST,
+ CONF_HTTP_PROXY_PORT,
+ CONF_HTTP_PROXY_USER,
+ CONF_HTTP_PROXY_PASSWORD,
+ CONF_CONN_TIMEOUT,
+ CONF_MAX_CONN,
+ CONF_MAX_PLAYLIST_LENGTH,
+ CONF_MAX_COMMAND_LIST_SIZE,
+ CONF_MAX_OUTPUT_BUFFER_SIZE,
+ CONF_FS_CHARSET,
+ CONF_ID3V1_ENCODING,
+ CONF_METADATA_TO_USE,
+ CONF_SAVE_ABSOLUTE_PATHS,
+ CONF_DECODER,
+ CONF_INPUT,
+ CONF_GAPLESS_MP3_PLAYBACK,
+ CONF_PLAYLIST_PLUGIN,
+ CONF_AUTO_UPDATE,
+ CONF_AUTO_UPDATE_DEPTH,
+ CONF_DESPOTIFY_USER,
+ CONF_DESPOTIFY_PASSWORD,
+ CONF_DESPOTIFY_HIGH_BITRATE,
+ CONF_AUDIO_FILTER,
+ CONF_DATABASE,
+ CONF_NEIGHBORS,
+ CONF_MAX
+};
+
+/**
+ * @return #CONF_MAX if not found
+ */
+gcc_pure
+enum ConfigOption
+ParseConfigOptionName(const char *name);
+
+#endif
diff --git a/src/config/ConfigParser.cxx b/src/config/ConfigParser.cxx
new file mode 100644
index 000000000..3535c9a13
--- /dev/null
+++ b/src/config/ConfigParser.cxx
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ConfigParser.hxx"
+#include "util/StringUtil.hxx"
+
+bool
+get_bool(const char *value, bool *value_r)
+{
+ static const char *t[] = { "yes", "true", "1", nullptr };
+ static const char *f[] = { "no", "false", "0", nullptr };
+
+ if (string_array_contains(t, value)) {
+ *value_r = true;
+ return true;
+ }
+
+ if (string_array_contains(f, value)) {
+ *value_r = false;
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/config/ConfigParser.hxx b/src/config/ConfigParser.hxx
new file mode 100644
index 000000000..06151b0bd
--- /dev/null
+++ b/src/config/ConfigParser.hxx
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_PARSER_HXX
+#define MPD_CONFIG_PARSER_HXX
+
+bool
+get_bool(const char *value, bool *value_r);
+
+#endif
diff --git a/src/config/ConfigPath.cxx b/src/config/ConfigPath.cxx
new file mode 100644
index 000000000..a3b3f83a5
--- /dev/null
+++ b/src/config/ConfigPath.cxx
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ConfigPath.hxx"
+#include "fs/AllocatedPath.hxx"
+#include "fs/Traits.hxx"
+#include "fs/Domain.hxx"
+#include "fs/StandardDirectory.hxx"
+#include "util/Error.hxx"
+#include "ConfigGlobal.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+#ifndef WIN32
+#include <pwd.h>
+
+/**
+ * Determine a given user's home directory.
+ */
+static AllocatedPath
+GetHome(const char *user, Error &error)
+{
+ AllocatedPath result = GetHomeDir(user);
+ if (result.IsNull()) {
+ error.Format(path_domain,
+ "no such user: %s", user);
+ return AllocatedPath::Null();
+ }
+
+ return result;
+}
+
+/**
+ * Determine the current user's home directory.
+ */
+static AllocatedPath
+GetHome(Error &error)
+{
+ AllocatedPath result = GetHomeDir();
+ if (result.IsNull()) {
+ error.Set(path_domain,
+ "problems getting home for current user");
+ return AllocatedPath::Null();
+ }
+
+ return result;
+}
+
+/**
+ * Determine the configured user's home directory.
+ */
+static AllocatedPath
+GetConfiguredHome(Error &error)
+{
+ const char *user = config_get_string(CONF_USER, nullptr);
+ return user != nullptr
+ ? GetHome(user, error)
+ : GetHome(error);
+}
+
+#endif
+
+AllocatedPath
+ParsePath(const char *path, Error &error)
+{
+ assert(path != nullptr);
+
+#ifndef WIN32
+ if (path[0] == '~') {
+ ++path;
+
+ if (*path == '\0')
+ return GetConfiguredHome(error);
+
+ AllocatedPath home = AllocatedPath::Null();
+
+ if (*path == '/') {
+ home = GetConfiguredHome(error);
+
+ ++path;
+ } else {
+ const char *slash = strchr(path, '/');
+ const char *end = slash == nullptr
+ ? path + strlen(path)
+ : slash;
+ const std::string user(path, end);
+ home = GetHome(user.c_str(), error);
+
+ if (slash == nullptr)
+ return home;
+
+ path = slash + 1;
+ }
+
+ if (home.IsNull())
+ return AllocatedPath::Null();
+
+ AllocatedPath path2 = AllocatedPath::FromUTF8(path, error);
+ if (path2.IsNull())
+ return AllocatedPath::Null();
+
+ return AllocatedPath::Build(home, path2);
+ } else if (!PathTraitsUTF8::IsAbsolute(path)) {
+ error.Format(path_domain,
+ "not an absolute path: %s", path);
+ return AllocatedPath::Null();
+ } else {
+#endif
+ return AllocatedPath::FromUTF8(path, error);
+#ifndef WIN32
+ }
+#endif
+}
diff --git a/src/config/ConfigPath.hxx b/src/config/ConfigPath.hxx
new file mode 100644
index 000000000..a5518a497
--- /dev/null
+++ b/src/config/ConfigPath.hxx
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_PATH_HXX
+#define MPD_CONFIG_PATH_HXX
+
+class AllocatedPath;
+class Error;
+
+AllocatedPath
+ParsePath(const char *path, Error &error);
+
+#endif
diff --git a/src/config/ConfigTemplates.cxx b/src/config/ConfigTemplates.cxx
new file mode 100644
index 000000000..8eaa22bdd
--- /dev/null
+++ b/src/config/ConfigTemplates.cxx
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ConfigTemplates.hxx"
+#include "ConfigOption.hxx"
+
+#include <string.h>
+
+const ConfigTemplate config_templates[] = {
+ { "music_directory", false, false },
+ { "playlist_directory", false, false },
+ { "follow_inside_symlinks", false, false },
+ { "follow_outside_symlinks", false, false },
+ { "db_file", false, false },
+ { "sticker_file", false, false },
+ { "log_file", false, false },
+ { "pid_file", false, false },
+ { "state_file", false, false },
+ { "restore_paused", false, false },
+ { "user", false, false },
+ { "group", false, false },
+ { "bind_to_address", true, false },
+ { "port", false, false },
+ { "log_level", false, false },
+ { "zeroconf_name", false, false },
+ { "zeroconf_enabled", false, false },
+ { "password", true, false },
+ { "default_permissions", false, false },
+ { "audio_output", true, true },
+ { "audio_output_format", false, false },
+ { "mixer_type", false, false },
+ { "replaygain", false, false },
+ { "replaygain_preamp", false, false },
+ { "replaygain_missing_preamp", false, false },
+ { "replaygain_limit", false, false },
+ { "volume_normalization", false, false },
+ { "samplerate_converter", false, false },
+ { "audio_buffer_size", false, false },
+ { "buffer_before_play", false, false },
+ { "http_proxy_host", false, false },
+ { "http_proxy_port", false, false },
+ { "http_proxy_user", false, false },
+ { "http_proxy_password", false, false },
+ { "connection_timeout", false, false },
+ { "max_connections", false, false },
+ { "max_playlist_length", false, false },
+ { "max_command_list_size", false, false },
+ { "max_output_buffer_size", false, false },
+ { "filesystem_charset", false, false },
+ { "id3v1_encoding", false, false },
+ { "metadata_to_use", false, false },
+ { "save_absolute_paths_in_playlists", false, false },
+ { "decoder", true, true },
+ { "input", true, true },
+ { "gapless_mp3_playback", false, false },
+ { "playlist_plugin", true, true },
+ { "auto_update", false, false },
+ { "auto_update_depth", false, false },
+ { "despotify_user", false, false },
+ { "despotify_password", false, false},
+ { "despotify_high_bitrate", false, false },
+ { "filter", true, true },
+ { "database", false, true },
+ { "neighbors", true, true },
+};
+
+static constexpr unsigned n_config_templates =
+ sizeof(config_templates) / sizeof(config_templates[0]);
+
+static_assert(n_config_templates == unsigned(CONF_MAX),
+ "Wrong number of config_templates");
+
+ConfigOption
+ParseConfigOptionName(const char *name)
+{
+ for (unsigned i = 0; i < n_config_templates; ++i)
+ if (strcmp(config_templates[i].name, name) == 0)
+ return ConfigOption(i);
+
+ return CONF_MAX;
+}
diff --git a/src/config/ConfigTemplates.hxx b/src/config/ConfigTemplates.hxx
new file mode 100644
index 000000000..90d098dc0
--- /dev/null
+++ b/src/config/ConfigTemplates.hxx
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_TEMPLATES_HXX
+#define MPD_CONFIG_TEMPLATES_HXX
+
+struct ConfigTemplate {
+ const char *const name;
+ const bool repeatable;
+ const bool block;
+};
+
+extern const ConfigTemplate config_templates[];
+
+#endif