diff options
Diffstat (limited to '')
-rw-r--r-- | src/config/ConfigFile.cxx | 262 |
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); } |