CommandLine.cxx 7.9 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2013 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13
 * http://www.musicpd.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
14 15 16 17
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 19
 */

20
#include "config.h"
Max Kellermann's avatar
Max Kellermann committed
21
#include "CommandLine.hxx"
Max Kellermann's avatar
Max Kellermann committed
22
#include "ls.hxx"
23
#include "LogInit.hxx"
24
#include "Log.hxx"
25
#include "ConfigGlobal.hxx"
26
#include "DecoderList.hxx"
27
#include "DecoderPlugin.hxx"
28
#include "OutputList.hxx"
Max Kellermann's avatar
Max Kellermann committed
29
#include "OutputPlugin.hxx"
30
#include "InputRegistry.hxx"
31
#include "InputPlugin.hxx"
32
#include "PlaylistRegistry.hxx"
33
#include "PlaylistPlugin.hxx"
34
#include "fs/AllocatedPath.hxx"
35
#include "fs/Traits.hxx"
36
#include "fs/FileSystem.hxx"
37
#include "fs/StandardDirectory.hxx"
38 39
#include "util/Error.hxx"
#include "util/Domain.hxx"
40 41
#include "util/OptionDef.hxx"
#include "util/OptionParser.hxx"
42

43
#ifdef ENABLE_ENCODER
44
#include "EncoderList.hxx"
45
#include "EncoderPlugin.hxx"
46 47
#endif

48
#ifdef ENABLE_ARCHIVE
49 50
#include "ArchiveList.hxx"
#include "ArchivePlugin.hxx"
51 52 53
#endif

#include <stdio.h>
Max Kellermann's avatar
Max Kellermann committed
54
#include <stdlib.h>
55

56
#ifdef WIN32
57 58
#define CONFIG_FILE_LOCATION		"mpd\\mpd.conf"
#define APP_CONFIG_FILE_LOCATION	"conf\\mpd.conf"
59
#else
60 61
#define USER_CONFIG_FILE_LOCATION1	".mpdconf"
#define USER_CONFIG_FILE_LOCATION2	".mpd/mpd.conf"
62
#define USER_CONFIG_FILE_LOCATION_XDG	"mpd/mpd.conf"
63
#endif
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
static const OptionDef opt_kill(
	"kill", "kill the currently running mpd session");
static const OptionDef opt_no_config(
	"no-config", "don't read from config");
static const OptionDef opt_no_daemon(
	"no-daemon", "don't detach from console");
static const OptionDef opt_stdout(
	"stdout", nullptr); // hidden, compatibility with old versions
static const OptionDef opt_stderr(
	"stderr", "print messages to stderr");
static const OptionDef opt_verbose(
	"verbose", 'v', "verbose logging");
static const OptionDef opt_version(
	"version", 'V', "print version number");
static const OptionDef opt_help(
	"help", 'h', "show help options");
static const OptionDef opt_help_alt(
	nullptr, '?', nullptr); // hidden, standard alias for --help

84
static constexpr Domain cmdline_domain("cmdline");
85

86
gcc_noreturn
87 88
static void version(void)
{
89
	puts("Music Player Daemon " VERSION "\n"
90 91
	     "\n"
	     "Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
92
	     "Copyright (C) 2008-2013 Max Kellermann <max@duempel.org>\n"
93 94 95
	     "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"
96 97
	     "Decoders plugins:");

98 99
	decoder_plugins_for_each([](const DecoderPlugin &plugin){
			printf(" [%s]", plugin.name);
100

101 102 103 104
			const char *const*suffixes = plugin.suffixes;
			if (suffixes != nullptr)
				for (; *suffixes != nullptr; ++suffixes)
					printf(" %s", *suffixes);
105

106 107
			puts("");
		});
108

109
	puts("\n"
110 111 112 113
	     "Output plugins:");
	audio_output_plugins_for_each(plugin)
		printf(" %s", plugin->name);
	puts("");
114

115 116
#ifdef ENABLE_ENCODER
	puts("\n"
117 118 119 120
	     "Encoder plugins:");
	encoder_plugins_for_each(plugin)
		printf(" %s", plugin->name);
	puts("");
121 122
#endif

123 124
#ifdef ENABLE_ARCHIVE
	puts("\n"
125 126 127 128 129
	     "Archive plugins:");
	archive_plugins_for_each(plugin) {
		printf(" [%s]", plugin->name);

		const char *const*suffixes = plugin->suffixes;
130 131
		if (suffixes != nullptr)
			for (; *suffixes != nullptr; ++suffixes)
132 133 134 135
				printf(" %s", *suffixes);

		puts("");
	}
136
#endif
137

138
	puts("\n"
139 140 141 142 143
	     "Input plugins:");
	input_plugins_for_each(plugin)
		printf(" %s", plugin->name);

	puts("\n\n"
144 145 146 147 148
	     "Playlist plugins:");
	playlist_plugins_for_each(plugin)
		printf(" %s", plugin->name);

	puts("\n\n"
149
	     "Protocols:");
150 151
	print_supported_uri_schemes_to_fp(stdout);

152
	exit(EXIT_SUCCESS);
153 154
}

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
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_noreturn
static void help(void)
{
	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);

	exit(EXIT_SUCCESS);
}
188

189
class ConfigLoader
190
{
191 192 193 194
	Error &error;
	bool result;
public:
	ConfigLoader(Error &_error) : error(_error), result(false) { }
195

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
	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);
219 220
}

221 222
bool
parse_cmdline(int argc, char **argv, struct options *options,
223
	      Error &error)
224
{
225
	bool use_config_file = true;
226 227
	options->kill = false;
	options->daemon = true;
228
	options->log_stderr = false;
229
	options->verbose = false;
230

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
	// 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();
260

261 262 263 264
		error.Format(cmdline_domain, "invalid option: %s",
			     parser.GetOption());
		return false;
	}
265

266 267 268 269
	/* initialize the logging library, so the configuration file
	   parser can use it already */
	log_early_init(options->verbose);

270
	if (!use_config_file) {
271 272
		LogDebug(cmdline_domain,
			 "Ignoring config, using daemon defaults");
273
		return true;
274
	}
275

276 277 278 279 280 281 282 283
	// 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;
284
		}
285 286 287
		error.Set(cmdline_domain, "too many arguments");
		return false;
	}
288

289 290 291 292
	if (config_file != nullptr) {
		/* use specified configuration file */
		return ReadConfigFile(Path::FromFS(config_file), error);
	}
293

294
	/* use default configuration file path */
295 296 297 298

	ConfigLoader loader(error);

	bool found =
299
#ifdef WIN32
300 301 302
		loader.TryFile(GetUserConfigDir(), CONFIG_FILE_LOCATION) ||
		loader.TryFile(GetSystemConfigDir(), CONFIG_FILE_LOCATION) ||
		loader.TryFile(GetAppBaseDir(), APP_CONFIG_FILE_LOCATION);
303
#else
304 305 306 307 308
		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));
309
#endif
310 311 312 313 314 315
	if (!found) {
		error.Set(cmdline_domain, "No configuration file found");
		return false;
	}

	return loader.GetResult();
316
}