diff options
Diffstat (limited to '')
-rw-r--r-- | src/config/ConfigData.cxx | 159 | ||||
-rw-r--r-- | src/config/ConfigData.hxx | 134 | ||||
-rw-r--r-- | src/config/ConfigDefaults.hxx | 26 | ||||
-rw-r--r-- | src/config/ConfigError.cxx | 23 | ||||
-rw-r--r-- | src/config/ConfigError.hxx | 25 | ||||
-rw-r--r-- | src/config/ConfigFile.cxx | 269 | ||||
-rw-r--r-- | src/config/ConfigFile.hxx | 30 | ||||
-rw-r--r-- | src/config/ConfigGlobal.cxx | 192 | ||||
-rw-r--r-- | src/config/ConfigGlobal.hxx | 106 | ||||
-rw-r--r-- | src/config/ConfigOption.hxx | 91 | ||||
-rw-r--r-- | src/config/ConfigParser.cxx | 40 | ||||
-rw-r--r-- | src/config/ConfigParser.hxx | 26 | ||||
-rw-r--r-- | src/config/ConfigPath.cxx | 131 | ||||
-rw-r--r-- | src/config/ConfigPath.hxx | 29 | ||||
-rw-r--r-- | src/config/ConfigTemplates.cxx | 97 | ||||
-rw-r--r-- | src/config/ConfigTemplates.hxx | 31 |
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 |