Simplify option_parser

This commit is contained in:
Andrzej Rybczak
2016-11-19 14:41:32 +01:00
parent 6d313a282b
commit 180c2d26fc
9 changed files with 480 additions and 690 deletions

View File

@@ -390,6 +390,8 @@
##
#lastfm_preferred_language = en
#
#space_add_mode = add_remove
#
#show_hidden_files_in_local_browser = no
#
##

View File

@@ -257,7 +257,7 @@ If enabled, each time song changes lyrics fetcher will be automatically run in b
If enabled, lyrics will be saved in song's directory, otherwise in ~/.lyrics. Note that it needs properly set mpd_music_dir.
.TP
.B generate_win32_compatible_filenames = yes/no
If set to yes, filenames generated by ncmpcpp (with tag editor, for lyrics, artists etc.) will not contain the following characters: \\?*:|"<> - otherwise only slash (/) will not be used.
If set to yes, filenames generated by ncmpcpp (with tag editor, for lyrics, artists etc.) will not contain the following characters: \\?*:|\"<> - otherwise only slash (/) will not be used.
.TP
.B allow_for_physical_item_deletion = yes/no
If set to yes, it will be possible to physically delete files and directories from the disk in the browser.
@@ -265,6 +265,9 @@ If set to yes, it will be possible to physically delete files and directories fr
.B lastfm_preferred_language = ISO 639 alpha-2 language code
If set, ncmpcpp will try to get info from last.fm in language you set and if it fails, it will fall back to english. Otherwise it will use english the first time.
.TP
.B space_add_mode = add_remove/always_add
If set to add_remove, attepting to add files that are already in playlist will remove them. Otherwise they can be added multiple times.
.TP
.B show_hidden_files_in_local_browser = yes/no
Trigger for displaying in local browser files and directories that begin with '.'
.TP

View File

@@ -36,27 +36,31 @@
#include "utility/html.h"
#include "utility/string.h"
std::unique_ptr<LyricsFetcher> toLyricsFetcher(const std::string &s)
std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
{
std::string s;
is >> s;
if (s == "lyricwiki")
return std::make_unique<LyricwikiFetcher>();
fetcher = std::make_unique<LyricwikiFetcher>();
else if (s == "azlyrics")
return std::make_unique<AzLyricsFetcher>();
fetcher = std::make_unique<AzLyricsFetcher>();
else if (s == "genius")
return std::make_unique<GeniusFetcher>();
fetcher = std::make_unique<GeniusFetcher>();
else if (s == "sing365")
return std::make_unique<Sing365Fetcher>();
fetcher = std::make_unique<Sing365Fetcher>();
else if (s == "lyricsmania")
return std::make_unique<LyricsmaniaFetcher>();
fetcher = std::make_unique<LyricsmaniaFetcher>();
else if (s == "metrolyrics")
return std::make_unique<MetrolyricsFetcher>();
fetcher = std::make_unique<MetrolyricsFetcher>();
else if (s == "justsomelyrics")
return std::make_unique<JustSomeLyricsFetcher>();
fetcher = std::make_unique<JustSomeLyricsFetcher>();
else if (s == "tekstowo")
return std::make_unique<TekstowoFetcher>();
fetcher = std::make_unique<TekstowoFetcher>();
else if (s == "internet")
return std::make_unique<InternetLyricsFetcher>();
throw std::runtime_error("no lyrics fetcher named '" + s + "'");
fetcher = std::make_unique<InternetLyricsFetcher>();
else
is.setstate(std::ios::failbit);
return is;
}
const char LyricsFetcher::msgNotFound[] = "Not found";

View File

@@ -47,9 +47,11 @@ protected:
static const char msgNotFound[];
};
typedef std::vector<std::unique_ptr<LyricsFetcher>> LyricsFetchers;
typedef std::unique_ptr<LyricsFetcher> LyricsFetcher_;
std::unique_ptr<LyricsFetcher> toLyricsFetcher(const std::string &s);
typedef std::vector<LyricsFetcher_> LyricsFetchers;
std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher);
/**********************************************************************/

View File

@@ -19,6 +19,7 @@
***************************************************************************/
#include <boost/tuple/tuple.hpp>
#include <boost/tokenizer.hpp>
#include <fstream>
#include <stdexcept>
@@ -166,567 +167,346 @@ std::string adjust_path(std::string s)
return s;
}
// parser worker for buffer
template <typename ValueT, typename TransformT>
option_parser::worker buffer(NC::Buffer &arg, ValueT &&value, TransformT &&map)
NC::Buffer buffer(const std::string &v)
{
return option_parser::worker(assign<std::string>(arg, [&arg, map](std::string s) {
NC::Buffer result;
auto ast = Format::parse(s, Format::Flags::Color | Format::Flags::Format);
Format::print(ast, result, nullptr);
return map(std::move(result));
}), defaults_to(arg, map(std::forward<ValueT>(value))));
}
option_parser::worker border(NC::Border &arg, NC::Border value)
{
return option_parser::worker(assign<std::string>(arg, [&arg](std::string s) {
NC::Border result;
if (!s.empty())
{
try {
result = boost::lexical_cast<NC::Color>(s);
} catch (boost::bad_lexical_cast &) {
throw std::runtime_error("invalid border: " + s);
}
}
Format::print(
Format::parse(v, Format::Flags::Color | Format::Flags::Format),
result,
nullptr);
return result;
}), defaults_to(arg, std::move(value)));
}
option_parser::worker deprecated(const char *option, double version_removal)
void deprecated(const char *option, double version_removal)
{
return option_parser::worker([option, version_removal](std::string) {
std::cerr << "WARNING: " << option << " is deprecated and will be removed in " << version_removal << ".\n";
}, [] { }
);
std::cerr << "WARNING: " << option
<< " is deprecated and will be removed in " << version_removal << "\n";
}
}
bool Configuration::read(const std::vector<std::string> &config_paths, bool ignore_errors)
{
std::string mpd_host;
unsigned mpd_port;
std::string columns_format;
option_parser p;
// deprecation warnings
p.add("default_space_mode", deprecated("default_space_mode", 0.8));
p.add<void>("default_space_mode", nullptr, "", [](std::string) {
deprecated("default_space_mode", 0.8);
});
// keep the same order of variables as in configuration file
p.add("ncmpcpp_directory", assign_default<std::string>(
ncmpcpp_directory, "~/.ncmpcpp/", adjust_directory
));
p.add("lyrics_directory", assign_default<std::string>(
lyrics_directory, "~/.lyrics/", adjust_directory
));
p.add("mpd_host", assign_default<std::string>(
mpd_host, "localhost", [](std::string host) {
// host can be a path to ipc socket, relative to home directory
p.add("ncmpcpp_directory", &ncmpcpp_directory, "~/.ncmpcpp/", adjust_directory);
p.add("lyrics_directory", &lyrics_directory, "~/.lyrics/", adjust_directory);
p.add<void>("mpd_host", nullptr, "localhost", [](std::string host) {
expand_home(host);
Mpd.SetHostname(host);
return host;
}));
p.add("mpd_port", assign_default<unsigned>(
mpd_port, 6600, [](unsigned port) {
Mpd.SetPort(port);
return port;
}));
p.add("mpd_music_dir", assign_default<std::string>(
mpd_music_dir, "~/music", adjust_directory
));
p.add("mpd_connection_timeout", assign_default(
mpd_connection_timeout, 5
));
p.add("mpd_crossfade_time", assign_default(
crossfade_time, 5
));
p.add("visualizer_fifo_path", assign_default<std::string>(
visualizer_fifo_path, "/tmp/mpd.fifo", adjust_path
));
p.add("visualizer_output_name", assign_default(
visualizer_output_name, "Visualizer feed"
));
p.add("visualizer_in_stereo", yes_no(
visualizer_in_stereo, true
));
p.add("visualizer_sample_multiplier", assign_default<double>(
visualizer_sample_multiplier, 1.0, [](double v) {
lowerBoundCheck(v, 0.0);
return v;
}));
p.add("visualizer_sync_interval", assign_default<unsigned>(
visualizer_sync_interval, 30, [](unsigned v) {
lowerBoundCheck(v, 10u);
return boost::posix_time::seconds(v);
}));
p.add("visualizer_type", assign_default(
visualizer_type, VisualizerType::Wave
));
p.add("visualizer_look", assign_default<std::string>(
visualizer_chars, "●▮", [](std::string s) {
});
p.add<void>("mpd_port", nullptr, "6600", [](std::string port) {
Mpd.SetPort(verbose_lexical_cast<unsigned>(port));
});
p.add("mpd_music_dir", &mpd_music_dir, "~/music", adjust_directory);
p.add("mpd_connection_timeout", &mpd_connection_timeout, "5");
p.add("mpd_crossfade_time", &crossfade_time, "5");
p.add("visualizer_fifo_path", &visualizer_fifo_path, "/tmp/mpd.fifo", adjust_path);
p.add("visualizer_output_name", &visualizer_output_name, "Visualizer feed");
p.add("visualizer_in_stereo", &visualizer_in_stereo, "yes", yes_no);
p.add("visualizer_sample_multiplier", &visualizer_sample_multiplier, "1",
[](std::string v) {
double multiplier = verbose_lexical_cast<double>(v);
lowerBoundCheck(multiplier, 0.0);
return multiplier;
});
p.add("visualizer_sync_interval", &visualizer_sync_interval, "30",
[](std::string v) {
unsigned sync_interval = verbose_lexical_cast<unsigned>(v);
lowerBoundCheck<unsigned>(sync_interval, 10);
return boost::posix_time::seconds(sync_interval);
});
p.add("visualizer_type", &visualizer_type, "wave");
p.add("visualizer_look", &visualizer_chars, "●▮", [](std::string s) {
auto result = ToWString(std::move(s));
typedef std::wstring::size_type size_type;
boundsCheck(result.size(), size_type(2), size_type(2));
boundsCheck<std::wstring::size_type>(result.size(), 2, 2);
return result;
}));
p.add("visualizer_color", option_parser::worker([this](std::string v) {
boost::sregex_token_iterator color(v.begin(), v.end(), boost::regex("\\w+")), end;
for (; color != end; ++color)
{
try {
visualizer_colors.push_back(boost::lexical_cast<NC::Color>(*color));
} catch (boost::bad_lexical_cast &) {
throw std::runtime_error("invalid color: " + *color);
}
}
if (visualizer_colors.empty())
throw std::runtime_error("empty list");
}, [this] {
visualizer_colors = { NC::Color::Blue, NC::Color::Cyan, NC::Color::Green, NC::Color::Yellow, NC::Color::Magenta, NC::Color::Red };
}));
p.add("system_encoding", assign_default<std::string>(
system_encoding, "", [](std::string enc) {
# ifdef HAVE_LANGINFO_H
});
p.add("visualizer_color", &visualizer_colors,
"blue, cyan, green, yellow, magenta, red", list_of<NC::Color>);
p.add("system_encoding", &system_encoding, "", [](std::string encoding) {
#ifdef HAVE_LANGINFO_H
// try to autodetect system encoding
if (enc.empty())
if (encoding.empty())
{
enc = nl_langinfo(CODESET);
if (enc == "UTF-8") // mpd uses utf-8 by default so no need to convert
enc.clear();
encoding = nl_langinfo(CODESET);
if (encoding == "UTF-8") // mpd uses utf-8 by default so no need to convert
encoding.clear();
}
# endif // HAVE_LANGINFO_H
return enc;
}));
p.add("playlist_disable_highlight_delay", assign_default<unsigned>(
playlist_disable_highlight_delay, 5, [](unsigned v) {
return boost::posix_time::seconds(v);
}));
p.add("message_delay_time", assign_default(
message_delay_time, 5
));
p.add("song_list_format", assign_default<std::string>(
song_list_format, "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [](std::string v) {
#endif // HAVE_LANGINFO_H
return encoding;
});
p.add("playlist_disable_highlight_delay", &playlist_disable_highlight_delay,
"5", [](std::string v) {
return boost::posix_time::seconds(verbose_lexical_cast<unsigned>(v));
});
p.add("message_delay_time", &message_delay_time, "5");
p.add("song_list_format", &song_list_format,
"{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [](std::string v) {
return Format::parse(v);
}));
p.add("song_status_format", assign_default<std::string>(
song_status_format, "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) {
const unsigned flags = Format::Flags::All ^ Format::Flags::OutputSwitch;
});
p.add("song_status_format", &song_status_format,
"{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) {
auto flags = Format::Flags::All ^ Format::Flags::OutputSwitch;
// precompute wide format for status display
song_status_wformat = Format::parse(ToWString(v), flags);
return Format::parse(v, flags);
}));
p.add("song_library_format", assign_default<std::string>(
song_library_format, "{%n - }{%t}|{%f}", [](std::string v) {
});
p.add("song_library_format", &song_library_format,
"{%n - }{%t}|{%f}", [](std::string v) {
return Format::parse(v);
}));
p.add("alternative_header_first_line_format", assign_default<std::string>(
new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [](std::string v) {
});
p.add("alternative_header_first_line_format", &new_header_first_line,
"$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [](std::string v) {
return Format::parse(ToWString(std::move(v)),
Format::Flags::All ^ Format::Flags::OutputSwitch
);
}));
p.add("alternative_header_second_line_format", assign_default<std::string>(
new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [](std::string v) {
Format::Flags::All ^ Format::Flags::OutputSwitch);
});
p.add("alternative_header_second_line_format", &new_header_second_line,
"{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [](std::string v) {
return Format::parse(ToWString(std::move(v)),
Format::Flags::All ^ Format::Flags::OutputSwitch
);
}));
p.add("now_playing_prefix", buffer(
now_playing_prefix, NC::Buffer::init(NC::Format::Bold), [this](NC::Buffer buf) {
now_playing_prefix_length = wideLength(ToWString(buf.str()));
return buf;
}));
p.add("now_playing_suffix", buffer(
now_playing_suffix, NC::Buffer::init(NC::Format::NoBold), [this](NC::Buffer buf) {
now_playing_suffix_length = wideLength(ToWString(buf.str()));
return buf;
}));
p.add("browser_playlist_prefix", buffer(
browser_playlist_prefix, NC::Buffer::init(NC::Color::Red, "playlist", NC::Color::End, ' '), id_()
));
p.add("selected_item_prefix", buffer(
selected_item_prefix, NC::Buffer::init(NC::Color::Magenta), [this](NC::Buffer buf) {
selected_item_prefix_length = wideLength(ToWString(buf.str()));
return buf;
}));
p.add("selected_item_suffix", buffer(
selected_item_suffix, NC::Buffer::init(NC::Color::End), [this](NC::Buffer buf) {
selected_item_suffix_length = wideLength(ToWString(buf.str()));
return buf;
}));
p.add("modified_item_prefix", buffer(
modified_item_prefix, NC::Buffer::init(NC::Color::Green, "> ", NC::Color::End), id_()
));
p.add("browser_sort_mode", assign_default(
browser_sort_mode, SortMode::Name
));
p.add("browser_sort_format", assign_default<std::string>(
browser_sort_format, "{%a - }{%t}|{%f} {(%l)}", [](std::string v) {
Format::Flags::All ^ Format::Flags::OutputSwitch);
});
p.add("now_playing_prefix", &now_playing_prefix,
"$b", [this](std::string v) {
NC::Buffer result = buffer(v);
now_playing_prefix_length = wideLength(ToWString(result.str()));
return result;
});
p.add("now_playing_suffix", &now_playing_suffix,
"$/b", [this](std::string v) {
NC::Buffer result = buffer(v);
now_playing_suffix_length = wideLength(ToWString(result.str()));
return result;
});
p.add("browser_playlist_prefix", &browser_playlist_prefix, "$2playlist$9 ", buffer);
p.add("selected_item_prefix", &selected_item_prefix,
"$6", [this](std::string v) {
NC::Buffer result = buffer(v);
selected_item_prefix_length = wideLength(ToWString(result.str()));
return result;
});
p.add("selected_item_suffix", &selected_item_suffix,
"$9", [this](std::string v) {
NC::Buffer result = buffer(v);
selected_item_suffix_length = wideLength(ToWString(result.str()));
return result;
});
p.add("modified_item_prefix", &modified_item_prefix, "$3>$9 ", buffer);
p.add("song_window_title_format", &song_window_title_format,
"{%a - }{%t}|{%f}", [](std::string v) {
return Format::parse(v, Format::Flags::Tag);
}));
p.add("song_window_title_format", assign_default<std::string>(
song_window_title_format, "{%a - }{%t}|{%f}", [](std::string v) {
});
p.add("browser_sort_mode", &browser_sort_mode, "name");
p.add("browser_sort_format", &browser_sort_format,
"{%a - }{%t}|{%f} {(%l)}", [](std::string v) {
return Format::parse(v, Format::Flags::Tag);
}));
p.add("song_columns_list_format", assign_default<std::string>(
columns_format, "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}",
});
p.add("song_columns_list_format", &song_columns_mode_format,
"(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}",
[this](std::string v) {
columns = generate_columns(v);
song_columns_mode_format = columns_to_format(columns);
return v;
}));
p.add("execute_on_song_change", assign_default<std::string>(
execute_on_song_change, "", adjust_path
));
p.add("execute_on_player_state_change", assign_default<std::string>(
execute_on_player_state_change, "", adjust_path
));
p.add("playlist_show_mpd_host", yes_no(
playlist_show_mpd_host, false
));
p.add("playlist_show_remaining_time", yes_no(
playlist_show_remaining_time, false
));
p.add("playlist_shorten_total_times", yes_no(
playlist_shorten_total_times, false
));
p.add("playlist_separate_albums", yes_no(
playlist_separate_albums, false
));
p.add("playlist_display_mode", assign_default(
playlist_display_mode, DisplayMode::Columns
));
p.add("browser_display_mode", assign_default(
browser_display_mode, DisplayMode::Classic
));
p.add("search_engine_display_mode", assign_default(
search_engine_display_mode, DisplayMode::Classic
));
p.add("playlist_editor_display_mode", assign_default(
playlist_editor_display_mode, DisplayMode::Classic
));
p.add("discard_colors_if_item_is_selected", yes_no(
discard_colors_if_item_is_selected, true
));
p.add("show_duplicate_tags", yes_no(
MPD::Song::ShowDuplicateTags, true
));
p.add("incremental_seeking", yes_no(
incremental_seeking, true
));
p.add("seek_time", assign_default(
seek_time, 1
));
p.add("volume_change_step", assign_default(
volume_change_step, 2
));
p.add("autocenter_mode", yes_no(
autocenter_mode, false
));
p.add("centered_cursor", yes_no(
centered_cursor, false
));
p.add("progressbar_look", assign_default<std::string>(
progressbar, "=>", [](std::string s) {
auto result = ToWString(std::move(s));
typedef std::wstring::size_type size_type;
boundsCheck(result.size(), size_type(2), size_type(3));
// if two characters were specified, add third one (null)
return columns_to_format(columns);
});
p.add("execute_on_song_change", &execute_on_song_change, "", adjust_path);
p.add("execute_on_player_state_change", &execute_on_player_state_change,
"", adjust_path);
p.add("playlist_show_mpd_host", &playlist_show_mpd_host, "no", yes_no);
p.add("playlist_show_remaining_time", &playlist_show_remaining_time, "no", yes_no);
p.add("playlist_shorten_total_times", &playlist_shorten_total_times, "no", yes_no);
p.add("playlist_separate_albums", &playlist_separate_albums, "no", yes_no);
p.add("playlist_display_mode", &playlist_display_mode, "columns");
p.add("browser_display_mode", &browser_display_mode, "classic");
p.add("search_engine_display_mode", &search_engine_display_mode, "classic");
p.add("playlist_editor_display_mode", &playlist_editor_display_mode, "classic");
p.add("discard_colors_if_item_is_selected", &discard_colors_if_item_is_selected,
"yes", yes_no);
p.add("show_duplicate_tags", &MPD::Song::ShowDuplicateTags, "yes", yes_no);
p.add("incremental_seeking", &incremental_seeking, "yes", yes_no);
p.add("seek_time", &seek_time, "1");
p.add("volume_change_step", &volume_change_step, "2");
p.add("autocenter_mode", &autocenter_mode, "no", yes_no);
p.add("centered_cursor", &centered_cursor, "no", yes_no);
p.add("progressbar_look", &progressbar, "=>", [](std::string v) {
auto result = ToWString(std::move(v));
boundsCheck<std::wstring::size_type>(result.size(), 2, 3);
// If two characters were specified, fill \0 as the third one.
result.resize(3);
return result;
}));
p.add("progressbar_boldness", yes_no(
progressbar_boldness, true
));
p.add("default_place_to_search_in", option_parser::worker([this](std::string v) {
});
p.add("progressbar_boldness", &progressbar_boldness, "yes", yes_no);
p.add("default_place_to_search_in", &search_in_db, "database", [](std::string v) {
if (v == "database")
search_in_db = true;
return true;
else if (v == "playlist")
search_in_db = true;
return false;
else
throw std::runtime_error("invalid argument: " + v);
}, defaults_to(search_in_db, true)
));
p.add("user_interface", assign_default(
design, Design::Classic
));
p.add("data_fetching_delay", yes_no(
data_fetching_delay, true
));
p.add("media_library_primary_tag", option_parser::worker([this](std::string v) {
invalid_value(v);
});
p.add("user_interface", &design, "classic");
p.add("data_fetching_delay", &data_fetching_delay, "yes", yes_no);
p.add("media_library_primary_tag", &media_lib_primary_tag, "artist", [](std::string v) {
if (v == "artist")
media_lib_primary_tag = MPD_TAG_ARTIST;
return MPD_TAG_ARTIST;
else if (v == "album_artist")
media_lib_primary_tag = MPD_TAG_ALBUM_ARTIST;
return MPD_TAG_ALBUM_ARTIST;
else if (v == "date")
media_lib_primary_tag = MPD_TAG_DATE;
return MPD_TAG_DATE;
else if (v == "genre")
media_lib_primary_tag = MPD_TAG_GENRE;
return MPD_TAG_GENRE;
else if (v == "composer")
media_lib_primary_tag = MPD_TAG_COMPOSER;
return MPD_TAG_COMPOSER;
else if (v == "performer")
media_lib_primary_tag = MPD_TAG_PERFORMER;
return MPD_TAG_PERFORMER;
else
throw std::runtime_error("invalid argument: " + v);
}, defaults_to(media_lib_primary_tag, MPD_TAG_ARTIST)
));
p.add("default_find_mode", option_parser::worker([this](std::string v) {
invalid_value(v);
});
p.add("default_find_mode", &wrapped_search, "wrapped", [](std::string v) {
if (v == "wrapped")
wrapped_search = true;
return true;
else if (v == "normal")
wrapped_search = false;
return false;
else
throw std::runtime_error("invalid argument: " + v);
}, defaults_to(wrapped_search, true)
));
p.add("default_tag_editor_pattern", assign_default(
pattern, "%n - %t"
));
p.add("header_visibility", yes_no(
header_visibility, true
));
p.add("statusbar_visibility", yes_no(
statusbar_visibility, true
));
p.add("titles_visibility", yes_no(
titles_visibility, true
));
p.add("header_text_scrolling", yes_no(
header_text_scrolling, true
));
p.add("cyclic_scrolling", yes_no(
use_cyclic_scrolling, false
));
p.add("lines_scrolled", assign_default(
lines_scrolled, 2
));
p.add("lyrics_fetchers", option_parser::worker([this](std::string v) {
boost::sregex_token_iterator fetcher(v.begin(), v.end(), boost::regex("\\w+")), end;
for (; fetcher != end; ++fetcher)
lyrics_fetchers.push_back(toLyricsFetcher(*fetcher));
if (lyrics_fetchers.empty())
throw std::runtime_error("empty list");
}, [this] {
lyrics_fetchers.push_back(std::make_unique<LyricwikiFetcher>());
lyrics_fetchers.push_back(std::make_unique<AzLyricsFetcher>());
lyrics_fetchers.push_back(std::make_unique<GeniusFetcher>());
lyrics_fetchers.push_back(std::make_unique<Sing365Fetcher>());
lyrics_fetchers.push_back(std::make_unique<LyricsmaniaFetcher>());
lyrics_fetchers.push_back(std::make_unique<MetrolyricsFetcher>());
lyrics_fetchers.push_back(std::make_unique<JustSomeLyricsFetcher>());
lyrics_fetchers.push_back(std::make_unique<TekstowoFetcher>());
lyrics_fetchers.push_back(std::make_unique<InternetLyricsFetcher>());
}));
p.add("follow_now_playing_lyrics", yes_no(
now_playing_lyrics, false
));
p.add("fetch_lyrics_for_current_song_in_background", yes_no(
fetch_lyrics_in_background, false
));
p.add("store_lyrics_in_song_dir", yes_no(
store_lyrics_in_song_dir, false
));
p.add("generate_win32_compatible_filenames", yes_no(
generate_win32_compatible_filenames, true
));
p.add("allow_for_physical_item_deletion", yes_no(
allow_for_physical_item_deletion, false
));
p.add("lastfm_preferred_language", assign_default(
lastfm_preferred_language, "en"
));
p.add("space_add_mode", assign_default(
space_add_mode, SpaceAddMode::AlwaysAdd
));
p.add("show_hidden_files_in_local_browser", yes_no(
local_browser_show_hidden_files, false
));
p.add("screen_switcher_mode", option_parser::worker([this](std::string v) {
invalid_value(v);
});
p.add("default_tag_editor_pattern", &pattern, "%n - %t");
p.add("header_visibility", &header_visibility, "yes", yes_no);
p.add("statusbar_visibility", &statusbar_visibility, "yes", yes_no);
p.add("titles_visibility", &titles_visibility, "yes", yes_no);
p.add("header_text_scrolling", &header_text_scrolling, "yes", yes_no);
p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no);
p.add("lines_scrolled", &lines_scrolled, "2");
p.add("lyrics_fetchers", &lyrics_fetchers,
"lyricwiki, azlyrics, genius, sing365, lyricsmania, metrolyrics, justsomelyrics, tekstowo, internet",
list_of<LyricsFetcher_>);
p.add("follow_now_playing_lyrics", &now_playing_lyrics, "no", yes_no);
p.add("fetch_lyrics_for_current_song_in_background", &fetch_lyrics_in_background,
"no", yes_no);
p.add("store_lyrics_in_song_dir", &store_lyrics_in_song_dir, "no", yes_no);
p.add("generate_win32_compatible_filenames", &generate_win32_compatible_filenames,
"yes", yes_no);
p.add("allow_for_physical_item_deletion", &allow_for_physical_item_deletion,
"no", yes_no);
p.add("lastfm_preferred_language", &lastfm_preferred_language, "en");
p.add("space_add_mode", &space_add_mode, "add_remove");
p.add("show_hidden_files_in_local_browser", &local_browser_show_hidden_files,
"no", yes_no);
p.add<void>(
"screen_switcher_mode", nullptr, "playlist, browser",
[this](std::string v) {
if (v == "previous")
screen_switcher_previous = true;
else
{
screen_switcher_previous = false;
boost::sregex_token_iterator i(v.begin(), v.end(), boost::regex("\\w+")), j;
for (; i != j; ++i)
{
auto screen = stringtoStartupScreenType(*i);
if (screen != ScreenType::Unknown)
screen_sequence.push_back(screen);
else
throw std::runtime_error("unknown screen: " + *i);
screen_sequence = list_of<ScreenType>(v, [](std::string s) {
auto screen = stringtoStartupScreenType(s);
if (screen == ScreenType::Unknown)
invalid_value(s);
return screen;
});
}
}
}, [this] {
screen_switcher_previous = false;
screen_sequence = { ScreenType::Playlist, ScreenType::Browser };
}));
p.add("startup_screen", option_parser::worker([this](std::string v) {
startup_screen_type = stringtoStartupScreenType(v);
if (startup_screen_type == ScreenType::Unknown)
throw std::runtime_error("unknown screen: " + v);
}, defaults_to(startup_screen_type, ScreenType::Playlist)
));
p.add("startup_slave_screen", option_parser::worker([this](std::string v) {
});
p.add("startup_screen", &startup_screen_type, "playlist", [](std::string v) {
auto screen = stringtoStartupScreenType(v);
if (screen == ScreenType::Unknown)
invalid_value(v);
return screen;
});
p.add("startup_slave_screen", &startup_slave_screen_type, "", [](std::string v) {
boost::optional<ScreenType> screen;
if (!v.empty())
{
startup_slave_screen_type = stringtoStartupScreenType(v);
if (startup_slave_screen_type == ScreenType::Unknown)
throw std::runtime_error("unknown slave screen: " + v);
screen = stringtoStartupScreenType(v);
if (screen == ScreenType::Unknown)
invalid_value(v);
}
}, defaults_to(startup_slave_screen_type, boost::none)
));
p.add("startup_slave_screen_focus", yes_no(
startup_slave_screen_focus, false
));
p.add("locked_screen_width_part", assign_default<double>(
locked_screen_width_part, 50.0, [](double v) {
return v / 100;
}));
p.add("ask_for_locked_screen_width_part", yes_no(
ask_for_locked_screen_width_part, true
));
p.add("jump_to_now_playing_song_at_start", yes_no(
jump_to_now_playing_song_at_start, true
));
p.add("ask_before_clearing_playlists", yes_no(
ask_before_clearing_playlists, true
));
p.add("ask_before_shuffling_playlists", yes_no(
ask_before_shuffling_playlists, true
));
p.add("clock_display_seconds", yes_no(
clock_display_seconds, false
));
p.add("display_volume_level", yes_no(
display_volume_level, true
));
p.add("display_bitrate", yes_no(
display_bitrate, false
));
p.add("display_remaining_time", yes_no(
display_remaining_time, false
));
p.add("regular_expressions", option_parser::worker([this](std::string v) {
return screen;
});
p.add("startup_slave_screen_focus", &startup_slave_screen_focus, "no", yes_no);
p.add("locked_screen_width_part", &locked_screen_width_part,
"50", [](std::string v) {
return verbose_lexical_cast<double>(v) / 100;
});
p.add("ask_for_locked_screen_width_part", &ask_for_locked_screen_width_part,
"yes", yes_no);
p.add("jump_to_now_playing_song_at_start", &jump_to_now_playing_song_at_start,
"yes", yes_no);
p.add("ask_before_clearing_playlists", &ask_before_clearing_playlists,
"yes", yes_no);
p.add("ask_before_shuffling_playlists", &ask_before_clearing_playlists,
"yes", yes_no);
p.add("clock_display_seconds", &clock_display_seconds, "no", yes_no);
p.add("display_volume_level", &display_volume_level, "yes", yes_no);
p.add("display_bitrate", &display_bitrate, "no", yes_no);
p.add("display_remaining_time", &display_remaining_time, "no", yes_no);
p.add("regular_expressions", &regex_type, "perl", [](std::string v) {
if (v == "none")
regex_type = boost::regex::literal;
return boost::regex::icase | boost::regex::literal;
else if (v == "basic")
regex_type = boost::regex::basic;
return boost::regex::icase | boost::regex::basic;
else if (v == "extended")
regex_type = boost::regex::extended;
return boost::regex::icase | boost::regex::extended;
else if (v == "perl")
regex_type = boost::regex::perl;
return boost::regex::icase | boost::regex::perl;
else
throw std::runtime_error("invalid argument: " + v);
regex_type |= boost::regex::icase;
}, defaults_to(regex_type, boost::regex::perl | boost::regex::icase)
));
p.add("ignore_leading_the", yes_no(
ignore_leading_the, false
));
p.add("block_search_constraints_change_if_items_found", yes_no(
block_search_constraints_change, true
));
p.add("mouse_support", yes_no(
mouse_support, true
));
p.add("mouse_list_scroll_whole_page", yes_no(
mouse_list_scroll_whole_page, true
));
p.add("empty_tag_marker", assign_default(
empty_tag, "<empty>"
));
p.add("tags_separator", assign_default(
MPD::Song::TagsSeparator, " | "
));
p.add("tag_editor_extended_numeration", yes_no(
tag_editor_extended_numeration, false
));
p.add("media_library_sort_by_mtime", yes_no(
media_library_sort_by_mtime, false
));
p.add("enable_window_title", [this]() {
// Consider this variable only if TERM variable is available
// and we're not in emacs terminal nor tty (through any wrapper
// like screen).
invalid_value(v);
});
p.add("ignore_leading_the", &ignore_leading_the, "no", yes_no);
p.add("block_search_constraints_change_if_items_found",
&block_search_constraints_change, "yes", yes_no);
p.add("mouse_support", &mouse_support, "yes", yes_no);
p.add("mouse_list_scroll_whole_page", &mouse_list_scroll_whole_page, "yes", yes_no);
p.add("empty_tag_marker", &empty_tag, "<empty>");
p.add("tags_separator", &MPD::Song::TagsSeparator, " | ");
p.add("tag_editor_extended_numeration", &tag_editor_extended_numeration, "no", yes_no);
p.add("media_library_sort_by_mtime", &media_library_sort_by_mtime, "no", yes_no);
p.add("enable_window_title", &set_window_title, "yes", [](std::string v) {
// Consider this variable only if TERM variable is available and we're not
// in emacs terminal nor tty (through any wrapper like screen).
auto term = getenv("TERM");
if (term != nullptr
&& strstr(term, "linux") == nullptr
&& strncmp(term, "eterm", const_strlen("eterm")))
return yes_no(set_window_title, true);
return yes_no(v);
else
{
set_window_title = false;
return option_parser::worker([](std::string) {}, [] {
std::clog << "Terminal doesn't support window title, skipping 'enable_window_title'.\n";
});
return false;
}
}());
p.add("search_engine_default_search_mode", assign_default<unsigned>(
search_engine_default_search_mode, 1, [](unsigned v) {
boundsCheck(v, 1u, 3u);
return --v;
}));
p.add("external_editor", assign_default<std::string>(
external_editor, "nano", adjust_path
));
p.add("use_console_editor", yes_no(
use_console_editor, true
));
p.add("colors_enabled", yes_no(
colors_enabled, true
));
p.add("empty_tag_color", assign_default(
empty_tags_color, NC::Color::Cyan
));
p.add("header_window_color", assign_default(
header_color, NC::Color::Default
));
p.add("volume_color", assign_default(
volume_color, NC::Color::Default
));
p.add("state_line_color", assign_default(
state_line_color, NC::Color::Default
));
p.add("state_flags_color", assign_default(
state_flags_color, NC::Color::Default
));
p.add("main_window_color", assign_default(
main_color, NC::Color::Yellow
));
p.add("color1", assign_default(
color1, NC::Color::White
));
p.add("color2", assign_default(
color2, NC::Color::Green
));
p.add("main_window_highlight_color", assign_default(
main_highlight_color, NC::Color::Yellow
));
p.add("progressbar_color", assign_default(
progressbar_color, NC::Color::Black
));
p.add("progressbar_elapsed_color", assign_default(
progressbar_elapsed_color, NC::Color::Green
));
p.add("statusbar_color", assign_default(
statusbar_color, NC::Color::Default
));
p.add("alternative_ui_separator_color", assign_default(
alternative_ui_separator_color, NC::Color::Black
));
p.add("active_column_color", assign_default(
active_column_color, NC::Color::Red
));
p.add("window_border_color", border(
window_border, NC::Color::Green
));
p.add("active_window_border", border(
active_window_border, NC::Color::Red
));
});
p.add("search_engine_default_search_mode", &search_engine_default_search_mode,
"1", [](std::string v) {
auto mode = verbose_lexical_cast<unsigned>(v);
boundsCheck<unsigned>(mode, 1, 3);
return --mode;
});
p.add("external_editor", &external_editor, "nano", adjust_path);
p.add("use_console_editor", &use_console_editor, "yes", yes_no);
p.add("colors_enabled", &colors_enabled, "yes", yes_no);
p.add("empty_tag_color", &empty_tags_color, "cyan");
p.add("header_window_color", &header_color, "default");
p.add("volume_color", &volume_color, "default");
p.add("state_line_color", &state_line_color, "default");
p.add("state_flags_color", &state_flags_color, "default");
p.add("main_window_color", &main_color, "yellow");
p.add("color1", &color1, "white");
p.add("color2", &color2, "green");
p.add("main_window_highlight_color", &main_highlight_color, "yellow");
p.add("progressbar_color", &progressbar_color, "black");
p.add("progressbar_elapsed_color", &progressbar_elapsed_color, "green");
p.add("statusbar_color", &statusbar_color, "default");
p.add("alternative_ui_separator_color", &alternative_ui_separator_color, "black");
p.add("active_column_color", &active_column_color, "red");
p.add("window_border_color", &window_border, "green", verbose_lexical_cast<NC::Color>);
p.add("active_window_border", &active_window_border, "red",
verbose_lexical_cast<NC::Color>);
return std::all_of(
config_paths.begin(),
@@ -739,5 +519,3 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
}
) && p.initialize_undefined(ignore_errors);
}
/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */

View File

@@ -193,7 +193,7 @@ struct Configuration
ScreenType startup_screen_type;
boost::optional<ScreenType> startup_slave_screen_type;
std::list<ScreenType> screen_sequence;
std::vector<ScreenType> screen_sequence;
SortMode browser_sort_mode;

View File

@@ -35,6 +35,18 @@
#include "utility/option_parser.h"
bool yes_no(std::string v)
{
if (v == "yes")
return true;
else if (v == "no")
return false;
else
invalid_value(v);
}
////////////////////////////////////////////////////////////////////////////////
bool option_parser::run(std::istream &is, bool ignore_errors)
{
// quoted value. leftmost and rightmost quotation marks are the delimiters.
@@ -52,10 +64,14 @@ bool option_parser::run(std::istream &is, bool ignore_errors)
auto it = m_parsers.find(option);
if (it != m_parsers.end())
{
try {
try
{
it->second.parse(match[2]);
} catch (std::exception &e) {
std::cerr << "Error while processing option \"" << option << "\": " << e.what() << "\n";
}
catch (std::exception &e)
{
std::cerr << "Error while processing option \"" << option
<< "\": " << e.what() << "\n";
if (!ignore_errors)
return false;
}
@@ -73,14 +89,19 @@ bool option_parser::run(std::istream &is, bool ignore_errors)
bool option_parser::initialize_undefined(bool ignore_errors)
{
for (auto &p : m_parsers)
for (auto &pp : m_parsers)
{
if (!p.second.defined())
auto &p = pp.second;
if (!p.used())
{
try {
p.second.run_default();
} catch (std::exception &e) {
std::cerr << "Error while initializing option \"" << p.first << "\": " << e.what() << "\n";
try
{
p.parse_default();
}
catch (std::exception &e)
{
std::cerr << "Error while initializing option \"" << pp.first
<< "\": " << e.what() << "\n";
if (ignore_errors)
return false;
}
@@ -88,15 +109,3 @@ bool option_parser::initialize_undefined(bool ignore_errors)
}
return true;
}
option_parser::worker yes_no(bool &arg, bool value)
{
return option_parser::worker([&arg](std::string &&v) {
if (v == "yes")
arg = true;
else if (v == "no")
arg = false;
else
throw std::runtime_error("invalid argument: " + v);
}, defaults_to(arg, value));
}

View File

@@ -33,152 +33,144 @@
#ifndef NCMPCPP_UTILITY_OPTION_PARSER_H
#define NCMPCPP_UTILITY_OPTION_PARSER_H
#include <boost/algorithm/string/trim.hpp>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>
#include <cassert>
#include <stdexcept>
#include <unordered_map>
#include "utility/functional.h"
struct option_parser
[[noreturn]] inline void invalid_value(const std::string &v)
{
typedef std::function<void(std::string &&)> parser_t;
typedef std::function<void()> default_t;
template <typename DestT, typename SourceT>
struct assign_value_once
{
typedef DestT dest_type;
typedef typename std::decay<SourceT>::type source_type;
template <typename ArgT>
assign_value_once(DestT &dest, ArgT &&value)
: m_dest(dest), m_source(std::make_shared<source_type>(std::forward<ArgT>(value))) { }
void operator()()
{
assert(m_source.get() != nullptr);
m_dest = std::move(*m_source);
m_source.reset();
}
private:
DestT &m_dest;
std::shared_ptr<source_type> m_source;
};
template <typename IntermediateT, typename DestT, typename TransformT>
struct parse_and_transform
{
template <typename ArgT>
parse_and_transform(DestT &dest, ArgT &&map)
: m_dest(dest), m_map(std::forward<ArgT>(map)) { }
void operator()(std::string &&v)
{
try {
m_dest = m_map(boost::lexical_cast<IntermediateT>(v));
} catch (boost::bad_lexical_cast &) {
throw std::runtime_error("invalid value: " + v);
}
}
}
private:
DestT &m_dest;
TransformT m_map;
};
template <typename DestT>
DestT verbose_lexical_cast(std::string v)
{
try {
return boost::lexical_cast<DestT>(std::move(v));
} catch (boost::bad_lexical_cast &) {
invalid_value(v);
}
}
template <typename ValueT, typename ConvertT>
std::vector<ValueT> list_of(const std::string &v, ConvertT convert)
{
std::vector<ValueT> result;
boost::tokenizer<boost::escaped_list_separator<char>> elems(v);
for (auto &value : elems)
result.push_back(convert(boost::trim_copy(value)));
if (result.empty())
throw std::runtime_error("empty list");
return result;
}
template <typename ValueT>
std::vector<ValueT> list_of(const std::string &v)
{
return list_of<ValueT>(v, verbose_lexical_cast<ValueT>);
}
bool yes_no(std::string v);
////////////////////////////////////////////////////////////////////////////////
class option_parser
{
template <typename DestT>
struct worker
{
worker() { }
template <typename MapT>
worker(DestT *dest, MapT &&map)
: m_dest(dest), m_map(std::forward<MapT>(map)), m_dest_set(false)
{ }
template <typename ParserT, typename DefaultT>
worker(ParserT &&p, DefaultT &&d)
: m_defined(false), m_parser(std::forward<ParserT>(p))
, m_default(std::forward<DefaultT>(d)) { }
template <typename ValueT>
void parse(ValueT &&value)
void operator()(std::string value)
{
if (m_defined)
throw std::runtime_error("option already defined");
m_parser(std::forward<ValueT>(value));
m_defined = true;
}
bool defined() const
{
return m_defined;
}
void run_default()
{
if (m_defined)
throw std::runtime_error("option already defined");
m_default();
m_defined = true;
if (m_dest_set)
throw std::runtime_error("option already set");
assign<DestT, void>::apply(m_dest, m_map, value);
m_dest_set = true;
}
private:
bool m_defined;
parser_t m_parser;
default_t m_default;
template <typename ValueT, typename VoidT>
struct assign {
static void apply(ValueT *dest,
std::function<DestT(std::string)> &map,
std::string &value) {
*dest = map(std::move(value));
}
};
template <typename VoidT>
struct assign<void, VoidT> {
static void apply(void *,
std::function<void(std::string)> &map,
std::string &value) {
map(std::move(value));
}
};
template <typename ParserT, typename DefaultT>
void add(const std::string &option, ParserT &&p, DefaultT &&d)
DestT *m_dest;
std::function<DestT(std::string)> m_map;
bool m_dest_set;
};
struct parser {
template <typename StringT, typename SetterT>
parser(StringT &&default_, SetterT &&setter_)
: m_used(false)
, m_default_value(std::forward<StringT>(default_))
, m_worker(std::forward<SetterT>(setter_))
{ }
bool used() const
{
assert(m_parsers.count(option) == 0);
m_parsers[option] = worker(std::forward<ParserT>(p), std::forward<DefaultT>(d));
return m_used;
}
template <typename WorkerT>
void add(const std::string &option, WorkerT &&w)
void parse(std::string v)
{
assert(m_parsers.count(option) == 0);
m_parsers[option] = std::forward<WorkerT>(w);
m_worker(std::move(v));
m_used = true;
}
bool run(std::istream &is, bool ignore_errors);
bool initialize_undefined(bool ignore_errors);
void parse_default() const
{
assert(!m_used);
m_worker(m_default_value);
}
private:
std::unordered_map<std::string, worker> m_parsers;
private:
bool m_used;
std::string m_default_value;
std::function<void(std::string)> m_worker;
};
std::unordered_map<std::string, parser> m_parsers;
public:
template <typename DestT, typename MapT>
void add(std::string option, DestT *dest, std::string default_, MapT &&map)
{
assert(m_parsers.count(option) == 0);
m_parsers.emplace(
std::move(option),
parser(
std::move(default_),
worker<DestT>(dest, std::forward<MapT>(map))));
}
template <typename DestT>
void add(std::string option, DestT *dest, std::string default_)
{
add(std::move(option), dest, std::move(default_), verbose_lexical_cast<DestT>);
}
bool run(std::istream &is, bool warn_on_errors);
bool initialize_undefined(bool warn_on_errors);
};
template <typename IntermediateT, typename ArgT, typename TransformT>
option_parser::parser_t assign(ArgT &arg, TransformT &&map = id_())
{
return option_parser::parse_and_transform<IntermediateT, ArgT, TransformT>(
arg, std::forward<TransformT>(map)
);
}
template <typename ArgT, typename ValueT>
option_parser::default_t defaults_to(ArgT &arg, ValueT &&value)
{
return option_parser::assign_value_once<ArgT, ValueT>(
arg, std::forward<ValueT>(value)
);
}
template <typename IntermediateT, typename ArgT, typename ValueT, typename TransformT>
option_parser::worker assign_default(ArgT &arg, ValueT &&value, TransformT &&map)
{
return option_parser::worker(
assign<IntermediateT>(arg, std::forward<TransformT>(map)),
defaults_to(arg, map(std::forward<ValueT>(value)))
);
}
template <typename ArgT, typename ValueT>
option_parser::worker assign_default(ArgT &arg, ValueT &&value)
{
return assign_default<ArgT>(arg, std::forward<ValueT>(value), id_());
}
// workers for specific types
option_parser::worker yes_no(bool &arg, bool value);
#endif // NCMPCPP_UTILITY_OPTION_PARSER_H