diff options
Diffstat (limited to 'src/CommandLine.cxx')
-rw-r--r-- | src/CommandLine.cxx | 317 |
1 files changed, 209 insertions, 108 deletions
diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx index 4bed5d531..c6e9c69c5 100644 --- a/src/CommandLine.cxx +++ b/src/CommandLine.cxx @@ -22,57 +22,115 @@ #include "ls.hxx" #include "LogInit.hxx" #include "Log.hxx" -#include "ConfigGlobal.hxx" -#include "DecoderList.hxx" -#include "DecoderPlugin.hxx" -#include "OutputList.hxx" -#include "OutputPlugin.hxx" -#include "InputRegistry.hxx" -#include "InputPlugin.hxx" -#include "PlaylistRegistry.hxx" -#include "PlaylistPlugin.hxx" +#include "config/ConfigGlobal.hxx" +#include "decoder/DecoderList.hxx" +#include "decoder/DecoderPlugin.hxx" +#include "output/Registry.hxx" +#include "output/OutputPlugin.hxx" +#include "input/Registry.hxx" +#include "input/InputPlugin.hxx" +#include "playlist/PlaylistRegistry.hxx" +#include "playlist/PlaylistPlugin.hxx" #include "fs/AllocatedPath.hxx" #include "fs/Traits.hxx" #include "fs/FileSystem.hxx" +#include "fs/StandardDirectory.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" -#include "system/FatalError.hxx" +#include "util/OptionDef.hxx" +#include "util/OptionParser.hxx" + +#ifdef ENABLE_DATABASE +#include "db/Registry.hxx" +#include "db/DatabasePlugin.hxx" +#include "storage/Registry.hxx" +#include "storage/StoragePlugin.hxx" +#endif + +#ifdef ENABLE_NEIGHBOR_PLUGINS +#include "neighbor/Registry.hxx" +#include "neighbor/NeighborPlugin.hxx" +#endif #ifdef ENABLE_ENCODER -#include "EncoderList.hxx" -#include "EncoderPlugin.hxx" +#include "encoder/EncoderList.hxx" +#include "encoder/EncoderPlugin.hxx" #endif #ifdef ENABLE_ARCHIVE -#include "ArchiveList.hxx" -#include "ArchivePlugin.hxx" +#include "archive/ArchiveList.hxx" +#include "archive/ArchivePlugin.hxx" #endif -#include <glib.h> - #include <stdio.h> #include <stdlib.h> #ifdef WIN32 -#define CONFIG_FILE_LOCATION "\\mpd\\mpd.conf" +#define CONFIG_FILE_LOCATION "mpd\\mpd.conf" +#define APP_CONFIG_FILE_LOCATION "conf\\mpd.conf" #else #define USER_CONFIG_FILE_LOCATION1 ".mpdconf" #define USER_CONFIG_FILE_LOCATION2 ".mpd/mpd.conf" #define USER_CONFIG_FILE_LOCATION_XDG "mpd/mpd.conf" #endif +static constexpr OptionDef opt_kill( + "kill", "kill the currently running mpd session"); +static constexpr OptionDef opt_no_config( + "no-config", "don't read from config"); +static constexpr OptionDef opt_no_daemon( + "no-daemon", "don't detach from console"); +static constexpr OptionDef opt_stdout( + "stdout", nullptr); // hidden, compatibility with old versions +static constexpr OptionDef opt_stderr( + "stderr", "print messages to stderr"); +static constexpr OptionDef opt_verbose( + "verbose", 'v', "verbose logging"); +static constexpr OptionDef opt_version( + "version", 'V', "print version number"); +static constexpr OptionDef opt_help( + "help", 'h', "show help options"); +static constexpr OptionDef opt_help_alt( + nullptr, '?', nullptr); // hidden, standard alias for --help + static constexpr Domain cmdline_domain("cmdline"); gcc_noreturn static void version(void) { - puts("Music Player Daemon " VERSION "\n" + puts("Music Player Daemon " VERSION +#ifdef GIT_COMMIT + " (" GIT_COMMIT ")" +#endif + "\n" "\n" "Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n" "Copyright (C) 2008-2014 Max Kellermann <max@duempel.org>\n" "This is free software; see the source for copying conditions. There is NO\n" - "warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" - "\n" + "warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); + +#ifdef ENABLE_DATABASE + puts("\n" + "Database plugins:"); + + for (auto i = database_plugins; *i != nullptr; ++i) + printf(" %s", (*i)->name); + + puts("\n\n" + "Storage plugins:"); + + for (auto i = storage_plugins; *i != nullptr; ++i) + printf(" %s", (*i)->name); +#endif + +#ifdef ENABLE_NEIGHBOR_PLUGINS + puts("\n\n" + "Neighbor plugins:"); + for (auto i = neighbor_plugins; *i != nullptr; ++i) + printf(" %s", (*i)->name); +#endif + + puts("\n\n" "Decoders plugins:"); decoder_plugins_for_each([](const DecoderPlugin &plugin){ @@ -132,122 +190,165 @@ static void version(void) exit(EXIT_SUCCESS); } -static const char *summary = - "Music Player Daemon - a daemon for playing music."; +static void PrintOption(const OptionDef &opt) +{ + if (opt.HasShortOption()) + printf(" -%c, --%-12s%s\n", + opt.GetShortOption(), + opt.GetLongOption(), + opt.GetDescription()); + else + printf(" --%-16s%s\n", + opt.GetLongOption(), + opt.GetDescription()); +} -gcc_pure -static AllocatedPath -PathBuildChecked(const AllocatedPath &a, PathTraits::const_pointer b) +gcc_noreturn +static void help(void) { - if (a.IsNull()) - return AllocatedPath::Null(); + puts("Usage:\n" + " mpd [OPTION...] [path/to/mpd.conf]\n" + "\n" + "Music Player Daemon - a daemon for playing music.\n" + "\n" + "Options:"); + + PrintOption(opt_help); + PrintOption(opt_kill); + PrintOption(opt_no_config); + PrintOption(opt_no_daemon); + PrintOption(opt_stderr); + PrintOption(opt_verbose); + PrintOption(opt_version); - return AllocatedPath::Build(a, b); + exit(EXIT_SUCCESS); +} + +class ConfigLoader +{ + Error &error; + bool result; +public: + ConfigLoader(Error &_error) : error(_error), result(false) { } + + bool GetResult() const { return result; } + + bool TryFile(const Path path); + bool TryFile(const AllocatedPath &base_path, + PathTraitsFS::const_pointer path); +}; + +bool ConfigLoader::TryFile(Path path) +{ + if (FileExists(path)) { + result = ReadConfigFile(path, error); + return true; + } + return false; +} + +bool ConfigLoader::TryFile(const AllocatedPath &base_path, + PathTraitsFS::const_pointer path) +{ + if (base_path.IsNull()) + return false; + auto full_path = AllocatedPath::Build(base_path, path); + return TryFile(full_path); } bool parse_cmdline(int argc, char **argv, struct options *options, Error &error) { - GOptionContext *context; - bool ret; - static gboolean option_version, - option_no_daemon, - option_no_config; - const GOptionEntry entries[] = { - { "kill", 0, 0, G_OPTION_ARG_NONE, &options->kill, - "kill the currently running mpd session", nullptr }, - { "no-config", 0, 0, G_OPTION_ARG_NONE, &option_no_config, - "don't read from config", nullptr }, - { "no-daemon", 0, 0, G_OPTION_ARG_NONE, &option_no_daemon, - "don't detach from console", nullptr }, - { "stdout", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr, - nullptr, nullptr }, - { "stderr", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr, - "print messages to stderr", nullptr }, - { "verbose", 'v', 0, G_OPTION_ARG_NONE, &options->verbose, - "verbose logging", nullptr }, - { "version", 'V', 0, G_OPTION_ARG_NONE, &option_version, - "print version number", nullptr }, - { nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr } - }; - + bool use_config_file = true; options->kill = false; options->daemon = true; options->log_stderr = false; options->verbose = false; - context = g_option_context_new("[path/to/mpd.conf]"); - g_option_context_add_main_entries(context, entries, nullptr); - - g_option_context_set_summary(context, summary); - - GError *gerror = nullptr; - ret = g_option_context_parse(context, &argc, &argv, &gerror); - g_option_context_free(context); - - if (!ret) - FatalError("option parsing failed", gerror); + // First pass: handle command line options + OptionParser parser(argc, argv); + while (parser.HasEntries()) { + if (!parser.ParseNext()) + continue; + if (parser.CheckOption(opt_kill)) { + options->kill = true; + continue; + } + if (parser.CheckOption(opt_no_config)) { + use_config_file = false; + continue; + } + if (parser.CheckOption(opt_no_daemon)) { + options->daemon = false; + continue; + } + if (parser.CheckOption(opt_stderr, opt_stdout)) { + options->log_stderr = true; + continue; + } + if (parser.CheckOption(opt_verbose)) { + options->verbose = true; + continue; + } + if (parser.CheckOption(opt_version)) + version(); + if (parser.CheckOption(opt_help, opt_help_alt)) + help(); - if (option_version) - version(); + error.Format(cmdline_domain, "invalid option: %s", + parser.GetOption()); + return false; + } /* initialize the logging library, so the configuration file parser can use it already */ log_early_init(options->verbose); - options->daemon = !option_no_daemon; - - if (option_no_config) { + if (!use_config_file) { LogDebug(cmdline_domain, "Ignoring config, using daemon defaults"); return true; - } else if (argc <= 1) { - /* default configuration file path */ + } -#ifdef WIN32 - AllocatedPath path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_user_config_dir()), - CONFIG_FILE_LOCATION); - if (!path.IsNull() && FileExists(path)) - return ReadConfigFile(path, error); - - const char *const*system_config_dirs = - g_get_system_config_dirs(); - - for (unsigned i = 0; system_config_dirs[i] != nullptr; ++i) { - path = PathBuildChecked(AllocatedPath::FromUTF8(system_config_dirs[i]), - CONFIG_FILE_LOCATION); - if (!path.IsNull() && FileExists(path)) - return ReadConfigFile(path, error); + // Second pass: find non-option parameters (i.e. config file) + const char *config_file = nullptr; + for (int i = 1; i < argc; ++i) { + if (OptionParser::IsOption(argv[i])) + continue; + if (config_file == nullptr) { + config_file = argv[i]; + continue; } + error.Set(cmdline_domain, "too many arguments"); + return false; + } + + if (config_file != nullptr) { + /* use specified configuration file */ + return ReadConfigFile(Path::FromFS(config_file), error); + } + + /* use default configuration file path */ + + ConfigLoader loader(error); + + bool found = +#ifdef WIN32 + loader.TryFile(GetUserConfigDir(), CONFIG_FILE_LOCATION) || + loader.TryFile(GetSystemConfigDir(), CONFIG_FILE_LOCATION) || + loader.TryFile(GetAppBaseDir(), APP_CONFIG_FILE_LOCATION); #else - AllocatedPath path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_user_config_dir()), - USER_CONFIG_FILE_LOCATION_XDG); - if (!path.IsNull() && FileExists(path)) - return ReadConfigFile(path, error); - - path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_home_dir()), - USER_CONFIG_FILE_LOCATION1); - if (!path.IsNull() && FileExists(path)) - return ReadConfigFile(path, error); - - path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_home_dir()), - USER_CONFIG_FILE_LOCATION2); - if (!path.IsNull() && FileExists(path)) - return ReadConfigFile(path, error); - - path = AllocatedPath::FromUTF8(SYSTEM_CONFIG_FILE_LOCATION); - if (!path.IsNull() && FileExists(path)) - return ReadConfigFile(path, error); + loader.TryFile(GetUserConfigDir(), + USER_CONFIG_FILE_LOCATION_XDG) || + loader.TryFile(GetHomeDir(), USER_CONFIG_FILE_LOCATION1) || + loader.TryFile(GetHomeDir(), USER_CONFIG_FILE_LOCATION2) || + loader.TryFile(Path::FromFS(SYSTEM_CONFIG_FILE_LOCATION)); #endif - + if (!found) { error.Set(cmdline_domain, "No configuration file found"); return false; - } else if (argc == 2) { - /* specified configuration file */ - return ReadConfigFile(Path::FromFS(argv[1]), error); - } else { - error.Set(cmdline_domain, "too many arguments"); - return false; } + + return loader.GetResult(); } |