util/Tokenizer: use std::runtime_error on syntax error
This commit is contained in:
parent
d256a0e98f
commit
51168169e7
@ -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;
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user