conf: throw on error
authorMax Kellermann <max@musicpd.org>
Sun, 8 Sep 2019 19:49:27 +0000 (21:49 +0200)
committerMax Kellermann <max@musicpd.org>
Sun, 8 Sep 2019 19:49:27 +0000 (21:49 +0200)
src/Styles.cxx
src/Styles.hxx
src/conf.cxx
src/conf.hxx
src/util/RuntimeError.hxx [new file with mode: 0644]

index ea7236c..a025e12 100644 (file)
@@ -21,6 +21,7 @@
 #include "BasicColors.hxx"
 #include "CustomColors.hxx"
 #include "i18n.h"
+#include "util/RuntimeError.hxx"
 #include "util/StringStrip.hxx"
 #include "util/Compiler.h"
 
@@ -241,7 +242,9 @@ colors_update_pair(Style style)
        init_pair(short(style), fg, bg);
 }
 
-gcc_pure
+/**
+ * Throws on error.
+ */
 static short
 ParseBackgroundColor(const char *s)
 {
@@ -252,10 +255,13 @@ ParseBackgroundColor(const char *s)
        if (!strcasecmp(s, "none"))
                return COLOR_NONE;
 
-       return COLOR_ERROR;
+       throw FormatRuntimeError("%s: %s", _("Unknown color"), s);
 }
 
-static bool
+/**
+ * Throws on error.
+ */
+static void
 ParseStyle(StyleData &d, const char *str)
 {
        std::string copy(str);
@@ -266,14 +272,7 @@ ParseStyle(StyleData &d, const char *str)
                char *slash = strchr(cur, '/');
                if (slash != nullptr) {
                        const char *name = slash + 1;
-                       short color = ParseBackgroundColor(name);
-                       if (color == COLOR_ERROR) {
-                               fprintf(stderr, "%s: %s\n",
-                                       _("Unknown color"), name);
-                               return false;
-                       }
-
-                       d.bg_color = color;
+                       d.bg_color = ParseBackgroundColor(name);
 
                        *slash = 0;
 
@@ -315,26 +314,19 @@ ParseStyle(StyleData &d, const char *str)
                        d.attr |= A_DIM;
                else if (!strcasecmp(cur, "bold"))
                        d.attr |= A_BOLD;
-               else {
-                       fprintf(stderr, "%s: %s\n",
-                               _("Unknown color"), str);
-                       return false;
-               }
-
+               else
+                       throw FormatRuntimeError("%s: %s",
+                                                _("Unknown color"), str);
        }
-
-       return true;
 }
 
-bool
+void
 ModifyStyle(const char *name, const char *value)
 {
        const auto style = StyleByName(name);
-       if (style == Style::END) {
-               fprintf(stderr, "%s: %s\n",
-                       _("Unknown color field"), name);
-               return false;
-       }
+       if (style == Style::END)
+               throw FormatRuntimeError("%s: %s",
+                                        _("Unknown color field"), name);
 
        auto &data = GetStyle(style);
 
@@ -343,15 +335,8 @@ ModifyStyle(const char *name, const char *value)
                   styles inherit their background color from; if the
                   user configures a color, it will be the background
                   color, but no attributes */
-               short color = ParseBackgroundColor(value);
-               if (color != COLOR_ERROR) {
-                       data.bg_color = color;
-                       return true;
-               } else {
-                       fprintf(stderr, "%s: %s\n",
-                               _("Unknown color"), value);
-                       return false;
-               }
+               data.bg_color = ParseBackgroundColor(value);
+               return;
        }
 
        return ParseStyle(data, value);
index 86fdcb7..b1a7287 100644 (file)
@@ -52,7 +52,11 @@ enum class Style : unsigned {
 };
 
 #ifdef ENABLE_COLORS
-bool
+
+/**
+ * Throws on error.
+ */
+void
 ModifyStyle(const char *name, const char *value);
 
 void
index 31418cb..40d8644 100644 (file)
@@ -32,6 +32,8 @@
 #include "Options.hxx"
 #include "io/Path.hxx"
 #include "util/CharUtil.hxx"
+#include "util/PrintException.hxx"
+#include "util/RuntimeError.hxx"
 #include "util/StringStrip.hxx"
 
 #include <assert.h>
@@ -111,14 +113,6 @@ str2bool(char *str) noexcept
                strcasecmp(str, "on") == 0 || strcasecmp(str, "1") == 0;
 }
 
-static void
-print_error(const char *msg, const char *input) noexcept
-{
-       fprintf(stderr, "%s: %s ('%s')\n",
-               /* To translators: prefix for error messages */
-               _("Error"), msg, input);
-}
-
 static constexpr bool
 is_word_char(char ch) noexcept
 {
@@ -128,10 +122,9 @@ is_word_char(char ch) noexcept
 static char *
 after_unquoted_word(char *p)
 {
-       if (!is_word_char(*p)) {
-               print_error(_("Word expected"), p);
-               return nullptr;
-       }
+       if (!is_word_char(*p))
+               throw FormatRuntimeError("%s: %s",
+                                        _("Word expected"), p);
 
        ++p;
 
@@ -141,6 +134,9 @@ after_unquoted_word(char *p)
        return p;
 }
 
+/**
+ * Throws on error.
+ */
 static int
 parse_key_value(char *str, char **end)
 {
@@ -152,34 +148,37 @@ parse_key_value(char *str, char **end)
                        return str[2];
                }
 
-               if (str[1] == '\'' || str[2] != '\'') {
-                       print_error(_("Malformed hotkey definition"), str);
-                       return -1;
-               }
+               if (str[1] == '\'' || str[2] != '\'')
+                       throw FormatRuntimeError("%s: %s",
+                                                _("Malformed hotkey definition"),
+                                                str);
 
                *end = str + 3;
                return str[1];
        } else {
                long value = strtol(str, end, 0);
-               if (*end == str) {
-                       print_error(_("Malformed hotkey definition"), str);
-                       return -1;
-               }
+               if (*end == str)
+                       throw FormatRuntimeError("%s: %s",
+                                                _("Malformed hotkey definition"),
+                                                str);
 
                return (int)value;
        }
 }
 
-static bool
+/**
+ * Throws on error.
+ */
+static void
 parse_key_definition(char *str)
 {
        /* get the command name */
        char *eq = strchr(str, '=');
-       if (eq == nullptr) {
+       if (eq == nullptr)
                /* the hotkey configuration line is incomplete */
-               print_error(_("Incomplete hotkey configuration"), str);
-               return false;
-       }
+               throw FormatRuntimeError("%s: %s",
+                                        _("Incomplete hotkey configuration"),
+                                        str);
 
        char *command_name = str;
        str = StripLeft(eq + 1);
@@ -187,33 +186,29 @@ parse_key_definition(char *str)
        *eq = '\0';
        StripRight(command_name);
        const auto cmd = get_key_command_from_name(command_name);
-       if(cmd == Command::NONE) {
+       if (cmd == Command::NONE)
                /* the hotkey configuration contains an unknown
                   command */
-               print_error(_("Unknown command"), command_name);
-               return false;
-       }
+               throw FormatRuntimeError("%s: %s",
+                                        _("Unknown command"), command_name);
 
        /* parse key values */
        size_t i = 0;
-       int key = 0;
        char *p = str;
 
        std::array<int, MAX_COMMAND_KEYS> keys{0};
-       while (i < MAX_COMMAND_KEYS && *p != 0 &&
-              (key = parse_key_value(p, &p)) >= 0) {
-               keys[i++] = key;
+       while (i < MAX_COMMAND_KEYS && *p != 0) {
+               keys[i++] = parse_key_value(p, &p);
                while (*p==',' || *p==' ' || *p=='\t')
                        p++;
        }
 
-       if (key < 0)
-               return false;
-
        GetGlobalKeyBindings().SetKey(cmd, keys);
-       return true;
 }
 
+/**
+ * Throws on error.
+ */
 static bool
 parse_timedisplay_type(const char *str)
 {
@@ -226,36 +221,38 @@ parse_timedisplay_type(const char *str)
                   "elapsed" or "remaining" time of a song being
                   played; in this case, the configuration file
                   contained an invalid setting */
-               print_error(_("Bad time display type"), str);
+               throw FormatRuntimeError("%s: %s",
+                                        _("Bad time display type"), str);
                return false;
        }
 }
 
 #ifdef ENABLE_COLORS
+/**
+ * Throws on error.
+ */
 static char *
 separate_value(char *p)
 {
        char *value = strchr(p, '=');
-       if (value == nullptr) {
+       if (value == nullptr)
                /* an equals sign '=' was expected while parsing a
                   configuration file line */
-               fprintf(stderr, "%s\n", _("Missing '='"));
-               return nullptr;
-       }
+               throw std::runtime_error(_("Missing '='"));
 
        *StripRight(p, value++) = '\0';
 
        return Strip(value);
 }
 
-static bool
+/**
+ * Throws on error.
+ */
+static void
 parse_color(char *str)
 {
        char *value = separate_value(str);
-       if (value == nullptr)
-               return false;
-
-       return ModifyStyle(str, value);
+       ModifyStyle(str, value);
 }
 
 /**
@@ -278,47 +275,43 @@ after_comma(char *p) noexcept
        return comma;
 }
 
-static bool
+/**
+ * Throws on error.
+ */
+static void
 parse_color_definition(char *str)
 {
        char *value = separate_value(str);
-       if (value == nullptr)
-               return false;
 
        /* get the command name */
        short color = ParseColorNameOrNumber(str);
-       if (color < 0) {
-               char buf[MAX_LINE_LENGTH];
-               print_error(_("Bad color name"), buf);
-               return false;
-       }
+       if (color < 0)
+               throw FormatRuntimeError("%s: %s",
+                                        _("Bad color name"), str);
 
        /* parse r,g,b values */
 
        short rgb[3];
        for (unsigned i = 0; i < 3; ++i) {
                char *next = after_comma(value), *endptr;
-               if (*value == 0) {
-                       print_error(_("Incomplete color definition"), str);
-                       return false;
-               }
+               if (*value == 0)
+                       throw FormatRuntimeError("%s: %s",
+                                                _("Incomplete color definition"),
+                                                str);
 
                rgb[i] = strtol(value, &endptr, 0);
-               if (endptr == value || *endptr != 0) {
-                       print_error(_("Invalid number"), value);
-                       return false;
-               }
+               if (endptr == value || *endptr != 0)
+                       throw FormatRuntimeError("%s: %s",
+                                                _("Invalid number"), value);
 
                value = next;
        }
 
-       if (*value != 0) {
-               print_error(_("Malformed color definition"), str);
-               return false;
-       }
+       if (*value != 0)
+               throw FormatRuntimeError("%s: %s",
+                                        _("Malformed color definition"), str);
 
        colors_define(color, rgb[0], rgb[1], rgb[2]);
-       return true;
 }
 #endif
 
@@ -361,6 +354,9 @@ NextItem(char *&src) noexcept
        return value;
 }
 
+/**
+ * Throws on error.
+ */
 static std::vector<std::string>
 check_screen_list(char *value)
 {
@@ -370,16 +366,17 @@ check_screen_list(char *value)
                std::transform(name, name + strlen(name), name, tolower);
 
                const auto *page_meta = screen_lookup_name(name);
-               if (page_meta == nullptr) {
+               if (page_meta == nullptr)
                        /* an unknown screen name was specified in the
                           configuration file */
-                       print_error(_("Unknown screen name"), name);
-               } else {
-                       /* use PageMeta::name because
-                          screen_lookup_name() may have translated a
-                          deprecated name */
-                       screen.emplace_back(page_meta->name);
-               }
+                       throw FormatRuntimeError("%s: %s",
+                                                _("Unknown screen name"),
+                                                name);
+
+               /* use PageMeta::name because
+                  screen_lookup_name() may have translated a
+                  deprecated name */
+               screen.emplace_back(page_meta->name);
        }
 
        if (screen.empty())
@@ -390,6 +387,9 @@ check_screen_list(char *value)
 
 #ifdef ENABLE_LIBRARY_PAGE
 
+/**
+ * Throws on error.
+ */
 static auto
 ParseTagList(char *value)
 {
@@ -397,23 +397,24 @@ ParseTagList(char *value)
 
        while (char *name = NextItem(value)) {
                auto type = mpd_tag_name_iparse(name);
-               if (type == MPD_TAG_UNKNOWN) {
-                       print_error(_("Unknown MPD tag"), name);
-                       result.clear();
-                       return result;
-               }
+               if (type == MPD_TAG_UNKNOWN)
+                       throw FormatRuntimeError("%s: %s",
+                                                _("Unknown MPD tag"), name);
 
                result.emplace_back(type);
        }
 
        if (result.empty())
-               fprintf(stderr, "Empty tag list\n");
+               throw std::runtime_error("Empty tag list");
 
        return result;
 }
 
 #endif
 
+/**
+ * Throws on error.
+ */
 static int
 get_search_mode(char *value)
 {
@@ -424,10 +425,8 @@ get_search_mode(char *value)
                if (0 <= mode && mode <= 4)
                        return mode;
                else
-               {
-                       print_error(_("Invalid search mode"),value);
-                       return 0;
-               }
+                       throw FormatRuntimeError("%s: %s",
+                                                _("Invalid search mode"),value);
        }
        else
        {
@@ -444,29 +443,29 @@ get_search_mode(char *value)
                else if (strcasecmp(value, "artist+album") == 0)
                        return 4;
                else
-               {
-                       print_error(_("Unknown search mode"),value);
-                       return 0;
-               }
+                       throw FormatRuntimeError("%s: %s",
+                                                _("Unknown search mode"),
+                                                value);
        }
 }
 
-static bool
+/**
+ * Throws on error.
+ */
+static void
 parse_line(char *line)
 {
        /* get the name part */
        char *const name = line;
        char *const name_end = after_unquoted_word(line);
-       if (name_end == nullptr)
-               return false;
 
        line = StripLeft(name_end);
        if (*line == '=') {
                ++line;
                line = StripLeft(line);
        } else if (line == name_end) {
-               print_error(_("Missing '='"), name_end);
-               return false;
+               throw FormatRuntimeError("%s: %s",
+                                        _("Missing '='"), name_end);
        }
 
        *name_end = 0;
@@ -562,8 +561,6 @@ parse_line(char *line)
        else if (!strcasecmp(CONF_LIBRARY_PAGE_TAGS, name)) {
 #ifdef ENABLE_LIBRARY_PAGE
                options.library_page_tags = ParseTagList(value);
-               if (options.library_page_tags.empty())
-                       return false;
 #endif
        } else if (!strcasecmp(CONF_SCREEN_LIST, name)) {
                options.screen_list = check_screen_list(value);
@@ -633,12 +630,10 @@ parse_line(char *line)
 #else
                options.second_column = str2bool(value);
 #endif
-       else {
-               print_error(_("Unknown configuration parameter"), name);
-               return false;
-       }
-
-       return true;
+       else
+               throw FormatRuntimeError("%s: %s",
+                                        _("Unknown configuration parameter"),
+                                        name);
 }
 
 static bool
@@ -658,7 +653,12 @@ read_rc_file(const char *filename)
 
                if (*p != 0 && *p != COMMENT_TOKEN) {
                        StripRight(p);
-                       parse_line(p);
+
+                       try {
+                               parse_line(p);
+                       } catch (...) {
+                               PrintException(std::current_exception());
+                       }
                }
        }
 
index 20fe40f..e36e9ad 100644 (file)
@@ -33,5 +33,6 @@ GetUserConfigPath() noexcept;
 std::string
 GetSystemConfigPath() noexcept;
 
-void read_configuration();
+void
+read_configuration();
 
diff --git a/src/util/RuntimeError.hxx b/src/util/RuntimeError.hxx
new file mode 100644 (file)
index 0000000..268c66a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013-2017 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RUNTIME_ERROR_HXX
+#define RUNTIME_ERROR_HXX
+
+#include <stdexcept>
+#include <utility>
+
+#include <stdio.h>
+
+template<typename... Args>
+static inline std::runtime_error
+FormatRuntimeError(const char *fmt, Args&&... args) noexcept
+{
+       char buffer[1024];
+       snprintf(buffer, sizeof(buffer), fmt, std::forward<Args>(args)...);
+       return std::runtime_error(buffer);
+}
+
+template<typename... Args>
+inline std::invalid_argument
+FormatInvalidArgument(const char *fmt, Args&&... args) noexcept
+{
+       char buffer[1024];
+       snprintf(buffer, sizeof(buffer), fmt, std::forward<Args>(args)...);
+       return std::invalid_argument(buffer);
+}
+
+#endif