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 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;
} }

View File

@ -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))

View File

@ -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();
} }

View File

@ -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