util/Tokenizer: use std::runtime_error on syntax error

This commit is contained in:
Max Kellermann 2015-12-16 11:34:26 +01:00
parent d256a0e98f
commit 51168169e7
4 changed files with 80 additions and 141 deletions

View File

@ -361,7 +361,7 @@ command_checked_lookup(Response &r, unsigned permission,
CommandResult
command_process(Client &client, unsigned num, char *line)
{
try {
Response r(client, num);
Error error;
@ -371,13 +371,17 @@ command_process(Client &client, unsigned num, char *line)
Tokenizer tokenizer(line);
const char *const cmd_name = tokenizer.NextWord(error);
if (cmd_name == nullptr) {
if (tokenizer.IsEnd())
r.FormatError(ACK_ERROR_UNKNOWN, "No command given");
else
r.Error(ACK_ERROR_UNKNOWN, error.GetMessage());
const char *cmd_name;
try {
cmd_name = tokenizer.NextWord();
if (cmd_name == nullptr) {
r.Error(ACK_ERROR_UNKNOWN, "No command given");
/* this client does not speak the MPD
protocol; kick the connection */
return CommandResult::FINISH;
}
} catch (const std::exception &e) {
r.Error(ACK_ERROR_UNKNOWN, e.what());
/* this client does not speak the MPD protocol; kick
the connection */
return CommandResult::FINISH;
@ -394,14 +398,9 @@ command_process(Client &client, unsigned num, char *line)
return CommandResult::ERROR;
}
char *a = tokenizer.NextParam(error);
if (a == nullptr) {
if (tokenizer.IsEnd())
break;
r.Error(ACK_ERROR_UNKNOWN, error.GetMessage());
return CommandResult::ERROR;
}
char *a = tokenizer.NextParam();
if (a == nullptr)
break;
argv[args.size++] = a;
}
@ -412,14 +411,13 @@ command_process(Client &client, unsigned num, char *line)
command_checked_lookup(r, client.GetPermission(),
cmd_name, args);
try {
CommandResult ret = cmd
? cmd->handler(client, args, r)
: CommandResult::ERROR;
CommandResult ret = cmd
? cmd->handler(client, args, r)
: CommandResult::ERROR;
return ret;
} catch (const std::exception &e) {
PrintError(r, e);
return CommandResult::ERROR;
}
return ret;
} catch (const std::exception &e) {
Response r(client, num);
PrintError(r, e);
return CommandResult::ERROR;
}

View File

@ -41,26 +41,17 @@ static constexpr char CONF_COMMENT = '#';
static constexpr Domain config_file_domain("config_file");
static bool
config_read_name_value(ConfigBlock &block, char *input, unsigned line,
Error &error)
static void
config_read_name_value(ConfigBlock &block, char *input, unsigned line)
{
Tokenizer tokenizer(input);
const char *name = tokenizer.NextWord(error);
if (name == nullptr) {
assert(!tokenizer.IsEnd());
return false;
}
const char *name = tokenizer.NextWord();
assert(name != nullptr);
const char *value = tokenizer.NextString(error);
if (value == nullptr) {
if (tokenizer.IsEnd())
throw std::runtime_error("Value missing");
assert(error.IsDefined());
return false;
}
const char *value = tokenizer.NextString();
if (value == nullptr)
throw std::runtime_error("Value missing");
if (!tokenizer.IsEnd() && tokenizer.CurrentChar() != CONF_COMMENT)
throw std::runtime_error("Unknown tokens after value");
@ -71,7 +62,6 @@ config_read_name_value(ConfigBlock &block, char *input, unsigned line,
name, bp->line);
block.AddBlockParam(name, value, line);
return true;
}
static ConfigBlock *
@ -104,13 +94,8 @@ try {
/* parse name and value */
if (!config_read_name_value(*block, line,
reader.GetLineNumber(),
error)) {
assert(*line != 0);
error.FormatPrefix("line %u: ", reader.GetLineNumber());
return nullptr;
}
config_read_name_value(*block, line,
reader.GetLineNumber());
}
} catch (...) {
std::throw_with_nested(FormatRuntimeError("Error in line %u", reader.GetLineNumber()));
@ -179,11 +164,10 @@ Append(config_param *&head, config_param *p)
*i = p;
}
static bool
static void
ReadConfigParam(ConfigData &config_data, BufferedReader &reader,
const char *name, ConfigOption o,
Tokenizer &tokenizer,
Error &error)
Tokenizer &tokenizer)
{
const unsigned i = unsigned(o);
const ConfigTemplate &option = config_param_templates[i];
@ -199,15 +183,10 @@ ReadConfigParam(ConfigData &config_data, BufferedReader &reader,
/* now parse the block or the value */
const char *value = tokenizer.NextString(error);
if (value == nullptr) {
if (tokenizer.IsEnd())
throw FormatRuntimeError("line %u: Value missing",
reader.GetLineNumber());
error.FormatPrefix("line %u: ", reader.GetLineNumber());
return false;
}
const char *value = tokenizer.NextString();
if (value == nullptr)
throw FormatRuntimeError("line %u: Value missing",
reader.GetLineNumber());
if (!tokenizer.IsEnd() && tokenizer.CurrentChar() != CONF_COMMENT)
throw FormatRuntimeError("line %u: Unknown tokens after value",
@ -215,7 +194,6 @@ ReadConfigParam(ConfigData &config_data, BufferedReader &reader,
auto *param = new config_param(value, reader.GetLineNumber());
Append(head, param);
return true;
}
static bool
@ -234,12 +212,8 @@ ReadConfigFile(ConfigData &config_data, BufferedReader &reader, Error &error)
by either the value or '{' */
Tokenizer tokenizer(line);
const char *name = tokenizer.NextWord(error);
if (name == nullptr) {
assert(!tokenizer.IsEnd());
error.FormatPrefix("line %u: ", reader.GetLineNumber());
return false;
}
const char *name = tokenizer.NextWord();
assert(name != nullptr);
/* get the definition of that option, and check the
"repeatable" flag */
@ -247,9 +221,8 @@ ReadConfigFile(ConfigData &config_data, BufferedReader &reader, Error &error)
const ConfigOption o = ParseConfigOptionName(name);
ConfigBlockOption bo;
if (o != ConfigOption::MAX) {
if (!ReadConfigParam(config_data, reader, name, o,
tokenizer, error))
return false;
ReadConfigParam(config_data, reader, name, o,
tokenizer);
} else if ((bo = ParseConfigBlockOptionName(name)) != ConfigBlockOption::MAX) {
if (!ReadConfigBlock(config_data, reader, name, bo,
tokenizer, error))

View File

@ -31,10 +31,8 @@
#include "Tokenizer.hxx"
#include "CharUtil.hxx"
#include "StringUtil.hxx"
#include "Error.hxx"
#include "Domain.hxx"
static constexpr Domain tokenizer_domain("tokenizer");
#include <stdexcept>
static inline bool
valid_word_first_char(char ch)
@ -49,7 +47,7 @@ valid_word_char(char ch)
}
char *
Tokenizer::NextWord(Error &error)
Tokenizer::NextWord()
{
char *const word = input;
@ -58,10 +56,8 @@ Tokenizer::NextWord(Error &error)
/* check the first character */
if (!valid_word_first_char(*input)) {
error.Set(tokenizer_domain, "Letter expected");
return nullptr;
}
if (!valid_word_first_char(*input))
throw std::runtime_error("Letter expected");
/* now iterate over the other characters until we find a
whitespace or end-of-string */
@ -75,10 +71,8 @@ Tokenizer::NextWord(Error &error)
break;
}
if (!valid_word_char(*input)) {
error.Set(tokenizer_domain, "Invalid word character");
return nullptr;
}
if (!valid_word_char(*input))
throw std::runtime_error("Invalid word character");
}
/* end of string: the string is already null-terminated
@ -94,7 +88,7 @@ valid_unquoted_char(char ch)
}
char *
Tokenizer::NextUnquoted(Error &error)
Tokenizer::NextUnquoted()
{
char *const word = input;
@ -103,10 +97,8 @@ Tokenizer::NextUnquoted(Error &error)
/* check the first character */
if (!valid_unquoted_char(*input)) {
error.Set(tokenizer_domain, "Invalid unquoted character");
return nullptr;
}
if (!valid_unquoted_char(*input))
throw std::runtime_error("Invalid unquoted character");
/* now iterate over the other characters until we find a
whitespace or end-of-string */
@ -120,11 +112,8 @@ Tokenizer::NextUnquoted(Error &error)
break;
}
if (!valid_unquoted_char(*input)) {
error.Set(tokenizer_domain,
"Invalid unquoted character");
return nullptr;
}
if (!valid_unquoted_char(*input))
throw std::runtime_error("Invalid unquoted character");
}
/* end of string: the string is already null-terminated
@ -134,7 +123,7 @@ Tokenizer::NextUnquoted(Error &error)
}
char *
Tokenizer::NextString(Error &error)
Tokenizer::NextString()
{
char *const word = input, *dest = input;
@ -144,10 +133,8 @@ Tokenizer::NextString(Error &error)
/* check for the opening " */
if (*input != '"') {
error.Set(tokenizer_domain, "'\"' expected");
return nullptr;
}
if (*input != '"')
throw std::runtime_error("'\"' expected");
++input;
@ -159,14 +146,8 @@ Tokenizer::NextString(Error &error)
character */
++input;
if (*input == 0) {
/* return input-1 so the caller can see the
difference between "end of line" and
"error" */
--input;
error.Set(tokenizer_domain, "Missing closing '\"'");
return nullptr;
}
if (*input == 0)
throw std::runtime_error("Missing closing '\"'");
/* copy one character */
*dest++ = *input++;
@ -176,11 +157,8 @@ Tokenizer::NextString(Error &error)
line) */
++input;
if (!IsWhitespaceFast(*input)) {
error.Set(tokenizer_domain,
"Space expected after closing '\"'");
return nullptr;
}
if (!IsWhitespaceFast(*input))
throw std::runtime_error("Space expected after closing '\"'");
/* finish the string and return it */
@ -190,10 +168,10 @@ Tokenizer::NextString(Error &error)
}
char *
Tokenizer::NextParam(Error &error)
Tokenizer::NextParam()
{
if (*input == '"')
return NextString(error);
return NextString();
else
return NextUnquoted(error);
return NextUnquoted();
}

View File

@ -30,8 +30,6 @@
#ifndef TOKENIZER_HXX
#define TOKENIZER_HXX
class Error;
class Tokenizer {
char *input;
@ -58,50 +56,42 @@ public:
}
/**
* Reads the next word.
* Reads the next word. Throws std::runtime_error on error.
*
* @param error if this function returns nullptr and
* **input_p!=0, it provides an #Error object in
* this argument
* @return a pointer to the null-terminated word, or nullptr
* on error or end of line
* on end of line
*/
char *NextWord(Error &error);
char *NextWord();
/**
* Reads the next unquoted word from the input string.
* Reads the next unquoted word from the input string. Throws
* std::runtime_error on error.
*
* @param error if this function returns nullptr and **input_p!=0, it
* provides an #Error object in this argument
* @return a pointer to the null-terminated word, or nullptr
* on error or end of line
* on end of line
*/
char *NextUnquoted(Error &error);
char *NextUnquoted();
/**
* Reads the next quoted string from the input string. A backslash
* escapes the following character. This function modifies the input
* string.
* Reads the next quoted string from the input string. A
* backslash escapes the following character. This function
* modifies the input string. Throws std::runtime_error on
* error.
*
* @param error if this function returns nullptr and **input_p!=0, it
* provides an #Error object in this argument
* @return a pointer to the null-terminated string, or nullptr on error
* or end of line
* @return a pointer to the null-terminated string, or nullptr
* end of line
*/
char *NextString(Error &error);
char *NextString();
/**
* Reads the next unquoted word or quoted string from the
* input. This is a wrapper for NextUnquoted() and
* NextString().
* NextString(). Throws std::runtime_error on error.
*
* @param error if this function returns nullptr and
* **input_p!=0, it provides an #Error object in
* this argument
* @return a pointer to the null-terminated string, or nullptr
* on error or end of line
* on end of line
*/
char *NextParam(Error &error);
char *NextParam();
};
#endif