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
|
CommandResult
|
||||||
command_process(Client &client, unsigned num, char *line)
|
command_process(Client &client, unsigned num, char *line)
|
||||||
{
|
try {
|
||||||
Response r(client, num);
|
Response r(client, num);
|
||||||
Error error;
|
Error error;
|
||||||
|
|
||||||
@ -371,13 +371,17 @@ command_process(Client &client, unsigned num, char *line)
|
|||||||
|
|
||||||
Tokenizer tokenizer(line);
|
Tokenizer tokenizer(line);
|
||||||
|
|
||||||
const char *const cmd_name = tokenizer.NextWord(error);
|
const char *cmd_name;
|
||||||
if (cmd_name == nullptr) {
|
try {
|
||||||
if (tokenizer.IsEnd())
|
cmd_name = tokenizer.NextWord();
|
||||||
r.FormatError(ACK_ERROR_UNKNOWN, "No command given");
|
if (cmd_name == nullptr) {
|
||||||
else
|
r.Error(ACK_ERROR_UNKNOWN, "No command given");
|
||||||
r.Error(ACK_ERROR_UNKNOWN, error.GetMessage());
|
/* 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
|
/* this client does not speak the MPD protocol; kick
|
||||||
the connection */
|
the connection */
|
||||||
return CommandResult::FINISH;
|
return CommandResult::FINISH;
|
||||||
@ -394,14 +398,9 @@ command_process(Client &client, unsigned num, char *line)
|
|||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *a = tokenizer.NextParam(error);
|
char *a = tokenizer.NextParam();
|
||||||
if (a == nullptr) {
|
if (a == nullptr)
|
||||||
if (tokenizer.IsEnd())
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
r.Error(ACK_ERROR_UNKNOWN, error.GetMessage());
|
|
||||||
return CommandResult::ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
argv[args.size++] = a;
|
argv[args.size++] = a;
|
||||||
}
|
}
|
||||||
@ -412,14 +411,13 @@ command_process(Client &client, unsigned num, char *line)
|
|||||||
command_checked_lookup(r, client.GetPermission(),
|
command_checked_lookup(r, client.GetPermission(),
|
||||||
cmd_name, args);
|
cmd_name, args);
|
||||||
|
|
||||||
try {
|
CommandResult ret = cmd
|
||||||
CommandResult ret = cmd
|
? cmd->handler(client, args, r)
|
||||||
? cmd->handler(client, args, r)
|
: CommandResult::ERROR;
|
||||||
: CommandResult::ERROR;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
PrintError(r, e);
|
Response r(client, num);
|
||||||
return CommandResult::ERROR;
|
PrintError(r, e);
|
||||||
}
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
@ -41,26 +41,17 @@ static constexpr char CONF_COMMENT = '#';
|
|||||||
|
|
||||||
static constexpr Domain config_file_domain("config_file");
|
static constexpr Domain config_file_domain("config_file");
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
config_read_name_value(ConfigBlock &block, char *input, unsigned line,
|
config_read_name_value(ConfigBlock &block, char *input, unsigned line)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
Tokenizer tokenizer(input);
|
Tokenizer tokenizer(input);
|
||||||
|
|
||||||
const char *name = tokenizer.NextWord(error);
|
const char *name = tokenizer.NextWord();
|
||||||
if (name == nullptr) {
|
assert(name != nullptr);
|
||||||
assert(!tokenizer.IsEnd());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *value = tokenizer.NextString(error);
|
const char *value = tokenizer.NextString();
|
||||||
if (value == nullptr) {
|
if (value == nullptr)
|
||||||
if (tokenizer.IsEnd())
|
throw std::runtime_error("Value missing");
|
||||||
throw std::runtime_error("Value missing");
|
|
||||||
|
|
||||||
assert(error.IsDefined());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tokenizer.IsEnd() && tokenizer.CurrentChar() != CONF_COMMENT)
|
if (!tokenizer.IsEnd() && tokenizer.CurrentChar() != CONF_COMMENT)
|
||||||
throw std::runtime_error("Unknown tokens after value");
|
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);
|
name, bp->line);
|
||||||
|
|
||||||
block.AddBlockParam(name, value, line);
|
block.AddBlockParam(name, value, line);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ConfigBlock *
|
static ConfigBlock *
|
||||||
@ -104,13 +94,8 @@ try {
|
|||||||
|
|
||||||
/* parse name and value */
|
/* parse name and value */
|
||||||
|
|
||||||
if (!config_read_name_value(*block, line,
|
config_read_name_value(*block, line,
|
||||||
reader.GetLineNumber(),
|
reader.GetLineNumber());
|
||||||
error)) {
|
|
||||||
assert(*line != 0);
|
|
||||||
error.FormatPrefix("line %u: ", reader.GetLineNumber());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
std::throw_with_nested(FormatRuntimeError("Error in line %u", reader.GetLineNumber()));
|
std::throw_with_nested(FormatRuntimeError("Error in line %u", reader.GetLineNumber()));
|
||||||
@ -179,11 +164,10 @@ Append(config_param *&head, config_param *p)
|
|||||||
*i = p;
|
*i = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
ReadConfigParam(ConfigData &config_data, BufferedReader &reader,
|
ReadConfigParam(ConfigData &config_data, BufferedReader &reader,
|
||||||
const char *name, ConfigOption o,
|
const char *name, ConfigOption o,
|
||||||
Tokenizer &tokenizer,
|
Tokenizer &tokenizer)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
const unsigned i = unsigned(o);
|
const unsigned i = unsigned(o);
|
||||||
const ConfigTemplate &option = config_param_templates[i];
|
const ConfigTemplate &option = config_param_templates[i];
|
||||||
@ -199,15 +183,10 @@ ReadConfigParam(ConfigData &config_data, BufferedReader &reader,
|
|||||||
|
|
||||||
/* now parse the block or the value */
|
/* now parse the block or the value */
|
||||||
|
|
||||||
const char *value = tokenizer.NextString(error);
|
const char *value = tokenizer.NextString();
|
||||||
if (value == nullptr) {
|
if (value == nullptr)
|
||||||
if (tokenizer.IsEnd())
|
throw FormatRuntimeError("line %u: Value missing",
|
||||||
throw FormatRuntimeError("line %u: Value missing",
|
reader.GetLineNumber());
|
||||||
reader.GetLineNumber());
|
|
||||||
|
|
||||||
error.FormatPrefix("line %u: ", reader.GetLineNumber());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tokenizer.IsEnd() && tokenizer.CurrentChar() != CONF_COMMENT)
|
if (!tokenizer.IsEnd() && tokenizer.CurrentChar() != CONF_COMMENT)
|
||||||
throw FormatRuntimeError("line %u: Unknown tokens after value",
|
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());
|
auto *param = new config_param(value, reader.GetLineNumber());
|
||||||
Append(head, param);
|
Append(head, param);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -234,12 +212,8 @@ ReadConfigFile(ConfigData &config_data, BufferedReader &reader, Error &error)
|
|||||||
by either the value or '{' */
|
by either the value or '{' */
|
||||||
|
|
||||||
Tokenizer tokenizer(line);
|
Tokenizer tokenizer(line);
|
||||||
const char *name = tokenizer.NextWord(error);
|
const char *name = tokenizer.NextWord();
|
||||||
if (name == nullptr) {
|
assert(name != nullptr);
|
||||||
assert(!tokenizer.IsEnd());
|
|
||||||
error.FormatPrefix("line %u: ", reader.GetLineNumber());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get the definition of that option, and check the
|
/* get the definition of that option, and check the
|
||||||
"repeatable" flag */
|
"repeatable" flag */
|
||||||
@ -247,9 +221,8 @@ ReadConfigFile(ConfigData &config_data, BufferedReader &reader, Error &error)
|
|||||||
const ConfigOption o = ParseConfigOptionName(name);
|
const ConfigOption o = ParseConfigOptionName(name);
|
||||||
ConfigBlockOption bo;
|
ConfigBlockOption bo;
|
||||||
if (o != ConfigOption::MAX) {
|
if (o != ConfigOption::MAX) {
|
||||||
if (!ReadConfigParam(config_data, reader, name, o,
|
ReadConfigParam(config_data, reader, name, o,
|
||||||
tokenizer, error))
|
tokenizer);
|
||||||
return false;
|
|
||||||
} else if ((bo = ParseConfigBlockOptionName(name)) != ConfigBlockOption::MAX) {
|
} else if ((bo = ParseConfigBlockOptionName(name)) != ConfigBlockOption::MAX) {
|
||||||
if (!ReadConfigBlock(config_data, reader, name, bo,
|
if (!ReadConfigBlock(config_data, reader, name, bo,
|
||||||
tokenizer, error))
|
tokenizer, error))
|
||||||
|
@ -31,10 +31,8 @@
|
|||||||
#include "Tokenizer.hxx"
|
#include "Tokenizer.hxx"
|
||||||
#include "CharUtil.hxx"
|
#include "CharUtil.hxx"
|
||||||
#include "StringUtil.hxx"
|
#include "StringUtil.hxx"
|
||||||
#include "Error.hxx"
|
|
||||||
#include "Domain.hxx"
|
|
||||||
|
|
||||||
static constexpr Domain tokenizer_domain("tokenizer");
|
#include <stdexcept>
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
valid_word_first_char(char ch)
|
valid_word_first_char(char ch)
|
||||||
@ -49,7 +47,7 @@ valid_word_char(char ch)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
Tokenizer::NextWord(Error &error)
|
Tokenizer::NextWord()
|
||||||
{
|
{
|
||||||
char *const word = input;
|
char *const word = input;
|
||||||
|
|
||||||
@ -58,10 +56,8 @@ Tokenizer::NextWord(Error &error)
|
|||||||
|
|
||||||
/* check the first character */
|
/* check the first character */
|
||||||
|
|
||||||
if (!valid_word_first_char(*input)) {
|
if (!valid_word_first_char(*input))
|
||||||
error.Set(tokenizer_domain, "Letter expected");
|
throw std::runtime_error("Letter expected");
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now iterate over the other characters until we find a
|
/* now iterate over the other characters until we find a
|
||||||
whitespace or end-of-string */
|
whitespace or end-of-string */
|
||||||
@ -75,10 +71,8 @@ Tokenizer::NextWord(Error &error)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid_word_char(*input)) {
|
if (!valid_word_char(*input))
|
||||||
error.Set(tokenizer_domain, "Invalid word character");
|
throw std::runtime_error("Invalid word character");
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* end of string: the string is already null-terminated
|
/* end of string: the string is already null-terminated
|
||||||
@ -94,7 +88,7 @@ valid_unquoted_char(char ch)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
Tokenizer::NextUnquoted(Error &error)
|
Tokenizer::NextUnquoted()
|
||||||
{
|
{
|
||||||
char *const word = input;
|
char *const word = input;
|
||||||
|
|
||||||
@ -103,10 +97,8 @@ Tokenizer::NextUnquoted(Error &error)
|
|||||||
|
|
||||||
/* check the first character */
|
/* check the first character */
|
||||||
|
|
||||||
if (!valid_unquoted_char(*input)) {
|
if (!valid_unquoted_char(*input))
|
||||||
error.Set(tokenizer_domain, "Invalid unquoted character");
|
throw std::runtime_error("Invalid unquoted character");
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now iterate over the other characters until we find a
|
/* now iterate over the other characters until we find a
|
||||||
whitespace or end-of-string */
|
whitespace or end-of-string */
|
||||||
@ -120,11 +112,8 @@ Tokenizer::NextUnquoted(Error &error)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid_unquoted_char(*input)) {
|
if (!valid_unquoted_char(*input))
|
||||||
error.Set(tokenizer_domain,
|
throw std::runtime_error("Invalid unquoted character");
|
||||||
"Invalid unquoted character");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* end of string: the string is already null-terminated
|
/* end of string: the string is already null-terminated
|
||||||
@ -134,7 +123,7 @@ Tokenizer::NextUnquoted(Error &error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
Tokenizer::NextString(Error &error)
|
Tokenizer::NextString()
|
||||||
{
|
{
|
||||||
char *const word = input, *dest = input;
|
char *const word = input, *dest = input;
|
||||||
|
|
||||||
@ -144,10 +133,8 @@ Tokenizer::NextString(Error &error)
|
|||||||
|
|
||||||
/* check for the opening " */
|
/* check for the opening " */
|
||||||
|
|
||||||
if (*input != '"') {
|
if (*input != '"')
|
||||||
error.Set(tokenizer_domain, "'\"' expected");
|
throw std::runtime_error("'\"' expected");
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
++input;
|
++input;
|
||||||
|
|
||||||
@ -159,14 +146,8 @@ Tokenizer::NextString(Error &error)
|
|||||||
character */
|
character */
|
||||||
++input;
|
++input;
|
||||||
|
|
||||||
if (*input == 0) {
|
if (*input == 0)
|
||||||
/* return input-1 so the caller can see the
|
throw std::runtime_error("Missing closing '\"'");
|
||||||
difference between "end of line" and
|
|
||||||
"error" */
|
|
||||||
--input;
|
|
||||||
error.Set(tokenizer_domain, "Missing closing '\"'");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy one character */
|
/* copy one character */
|
||||||
*dest++ = *input++;
|
*dest++ = *input++;
|
||||||
@ -176,11 +157,8 @@ Tokenizer::NextString(Error &error)
|
|||||||
line) */
|
line) */
|
||||||
|
|
||||||
++input;
|
++input;
|
||||||
if (!IsWhitespaceFast(*input)) {
|
if (!IsWhitespaceFast(*input))
|
||||||
error.Set(tokenizer_domain,
|
throw std::runtime_error("Space expected after closing '\"'");
|
||||||
"Space expected after closing '\"'");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* finish the string and return it */
|
/* finish the string and return it */
|
||||||
|
|
||||||
@ -190,10 +168,10 @@ Tokenizer::NextString(Error &error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
Tokenizer::NextParam(Error &error)
|
Tokenizer::NextParam()
|
||||||
{
|
{
|
||||||
if (*input == '"')
|
if (*input == '"')
|
||||||
return NextString(error);
|
return NextString();
|
||||||
else
|
else
|
||||||
return NextUnquoted(error);
|
return NextUnquoted();
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,6 @@
|
|||||||
#ifndef TOKENIZER_HXX
|
#ifndef TOKENIZER_HXX
|
||||||
#define TOKENIZER_HXX
|
#define TOKENIZER_HXX
|
||||||
|
|
||||||
class Error;
|
|
||||||
|
|
||||||
class Tokenizer {
|
class Tokenizer {
|
||||||
char *input;
|
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
|
* @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
|
* @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
|
* Reads the next quoted string from the input string. A
|
||||||
* escapes the following character. This function modifies the input
|
* backslash escapes the following character. This function
|
||||||
* string.
|
* modifies the input string. Throws std::runtime_error on
|
||||||
|
* error.
|
||||||
*
|
*
|
||||||
* @param error if this function returns nullptr and **input_p!=0, it
|
* @return a pointer to the null-terminated string, or nullptr
|
||||||
* provides an #Error object in this argument
|
* end of line
|
||||||
* @return a pointer to the null-terminated string, or nullptr on error
|
|
||||||
* or end of line
|
|
||||||
*/
|
*/
|
||||||
char *NextString(Error &error);
|
char *NextString();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the next unquoted word or quoted string from the
|
* Reads the next unquoted word or quoted string from the
|
||||||
* input. This is a wrapper for NextUnquoted() and
|
* 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
|
* @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
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user