Config: reformat all descriptions to fill 80 columns
[ncmpcpp.git] / src / configuration.cpp
blob565c50d4b260c786a9204783fe41c4b453044d54
1 /***************************************************************************
2 * Copyright (C) 2008-2017 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <algorithm>
22 #include <boost/algorithm/string/trim.hpp>
23 #include <boost/filesystem/operations.hpp>
24 #include <boost/program_options.hpp>
25 #include <iomanip>
26 #include <iostream>
27 #include <fstream>
29 #include "bindings.h"
30 #include "configuration.h"
31 #include "config.h"
32 #include "mpdpp.h"
33 #include "settings.h"
34 #include "utility/string.h"
36 namespace po = boost::program_options;
38 using std::cerr;
39 using std::cout;
41 namespace {
43 const char *env_home;
45 std::string xdg_config_home()
47 std::string result;
48 const char *env_xdg_config_home = getenv("XDG_CONFIG_HOME");
49 if (env_xdg_config_home == nullptr)
50 result = "~/.config/";
51 else
53 result = env_xdg_config_home;
54 if (!result.empty() && result.back() != '/')
55 result += "/";
57 return result;
62 void expand_home(std::string &path)
64 assert(env_home != nullptr);
65 if (!path.empty() && path[0] == '~')
66 path.replace(0, 1, env_home);
69 bool configure(int argc, char **argv)
71 const std::vector<std::string> default_config_paths = {
72 "~/.ncmpcpp/config",
73 xdg_config_home() + "ncmpcpp/config"
76 std::string bindings_path;
77 std::vector<std::string> config_paths;
79 po::options_description options("Options");
80 options.add_options()
81 ("host,h", po::value<std::string>()->value_name("HOST")->default_value("localhost"), "connect to server at host")
82 ("port,p", po::value<int>()->value_name("PORT")->default_value(6600), "connect to server at port")
83 ("current-song", po::value<std::string>()->value_name("FORMAT")->implicit_value("{{{(%l) }{{%a - }%t}}|{%f}}"), "print current song using given format and exit")
84 ("config,c", po::value<std::vector<std::string>>(&config_paths)->value_name("PATH")->default_value(default_config_paths, join<std::string>(default_config_paths, " AND ")), "specify configuration file(s)")
85 ("ignore-config-errors", "ignore unknown and invalid options in configuration files")
86 ("test-lyrics-fetchers", "check if lyrics fetchers work")
87 ("bindings,b", po::value<std::string>(&bindings_path)->value_name("PATH")->default_value("~/.ncmpcpp/bindings"), "specify bindings file")
88 ("screen,s", po::value<std::string>()->value_name("SCREEN"), "specify the startup screen")
89 ("slave-screen,S", po::value<std::string>()->value_name("SCREEN"), "specify the startup slave screen")
90 ("help,?", "show help message")
91 ("version,v", "display version information")
92 ("quiet,q", "suppress logs and excess output")
95 po::variables_map vm;
96 try
98 po::store(po::parse_command_line(argc, argv, options), vm);
100 // suppress messages from std::clog
101 if (vm.count("quiet"))
103 std::ofstream null_stream;
104 null_stream.open("/dev/null");
105 std::clog.rdbuf(null_stream.rdbuf());
108 if (vm.count("help"))
110 cout << "Usage: " << argv[0] << " [options]...\n" << options << "\n";
111 return false;
113 if (vm.count("version"))
115 std::cout << "ncmpcpp " << VERSION << "\n\n"
116 << "optional screens compiled-in:\n"
117 # ifdef HAVE_TAGLIB_H
118 << " - tag editor\n"
119 << " - tiny tag editor\n"
120 # endif
121 # ifdef ENABLE_OUTPUTS
122 << " - outputs\n"
123 # endif
124 # ifdef ENABLE_VISUALIZER
125 << " - visualizer\n"
126 # endif
127 # ifdef ENABLE_CLOCK
128 << " - clock\n"
129 # endif
130 << "\nencoding detection: "
131 # ifdef HAVE_LANGINFO_H
132 << "enabled"
133 # else
134 << "disabled"
135 # endif // HAVE_LANGINFO_H
136 << "\nbuilt with support for:"
137 # ifdef HAVE_FFTW3_H
138 << " fftw"
139 # endif
140 << " ncurses"
141 # ifdef HAVE_TAGLIB_H
142 << " taglib"
143 # endif
144 << "\n";
145 return false;
148 po::notify(vm);
150 if (vm.count("test-lyrics-fetchers"))
152 std::vector<std::tuple<std::string, std::string, std::string>> fetcher_data = {
153 std::make_tuple("lyricwiki", "rihanna", "umbrella"),
154 std::make_tuple("azlyrics", "rihanna", "umbrella"),
155 std::make_tuple("genius", "rihanna", "umbrella"),
156 std::make_tuple("sing365", "rihanna", "umbrella"),
157 std::make_tuple("lyricsmania", "rihanna", "umbrella"),
158 std::make_tuple("metrolyrics", "rihanna", "umbrella"),
159 std::make_tuple("justsomelyrics", "rihanna", "umbrella"),
160 std::make_tuple("jahlyrics", "sean kingston", "dry your eyes"),
161 std::make_tuple("plyrics", "offspring", "genocide"),
162 std::make_tuple("tekstowo", "rihanna", "umbrella"),
164 for (auto &data : fetcher_data)
166 auto fetcher = boost::lexical_cast<LyricsFetcher_>(std::get<0>(data));
167 std::cout << std::setw(20)
168 << std::left
169 << fetcher->name()
170 << " : "
171 << std::flush;
172 auto result = fetcher->fetch(std::get<1>(data), std::get<2>(data));
173 std::cout << (result.first ? "ok" : "failed")
174 << "\n";
176 exit(0);
179 // get home directory
180 env_home = getenv("HOME");
181 if (env_home == nullptr)
183 cerr << "Fatal error: HOME environment variable is not defined\n";
184 return false;
187 // read configuration
188 std::for_each(config_paths.begin(), config_paths.end(), expand_home);
189 if (Config.read(config_paths, vm.count("ignore-config-errors")) == false)
190 exit(1);
192 // if bindings file was not specified, use the one from main directory.
193 if (vm["bindings"].defaulted())
194 bindings_path = Config.ncmpcpp_directory + "bindings";
195 else
196 expand_home(bindings_path);
198 // read bindings
199 if (Bindings.read(bindings_path) == false)
200 exit(1);
201 Bindings.generateDefaults();
203 // create directories
204 boost::filesystem::create_directory(Config.ncmpcpp_directory);
205 boost::filesystem::create_directory(Config.lyrics_directory);
207 // try to get MPD connection details from environment variables
208 // as they take precedence over these from the configuration.
209 auto env_host = getenv("MPD_HOST");
210 auto env_port = getenv("MPD_PORT");
211 if (env_host != nullptr)
212 Mpd.SetHostname(env_host);
213 if (env_port != nullptr)
215 auto trimmed_env_port = boost::trim_copy<std::string>(env_port);
216 try {
217 Mpd.SetPort(boost::lexical_cast<int>(trimmed_env_port));
218 } catch (boost::bad_lexical_cast &) {
219 throw std::runtime_error("MPD_PORT environment variable ("
220 + std::string(env_port)
221 + ") is not a number");
225 // if MPD connection details are provided as command line
226 // parameters, use them as their priority is the highest.
227 if (!vm["host"].defaulted())
228 Mpd.SetHostname(vm["host"].as<std::string>());
229 if (!vm["port"].defaulted())
230 Mpd.SetPort(vm["port"].as<int>());
231 Mpd.SetTimeout(Config.mpd_connection_timeout);
233 // print current song
234 if (vm.count("current-song"))
236 Mpd.Connect();
237 auto s = Mpd.GetCurrentSong();
238 if (!s.empty())
240 auto format = Format::parse(vm["current-song"].as<std::string>(), Format::Flags::Tag);
241 std::cout << Format::stringify<char>(format, &s);
243 return false;
246 // custom startup screen
247 if (vm.count("screen"))
249 auto screen = vm["screen"].as<std::string>();
250 Config.startup_screen_type = stringtoStartupScreenType(screen);
251 if (Config.startup_screen_type == ScreenType::Unknown)
253 std::cerr << "Unknown screen: " << screen << "\n";
254 exit(1);
258 // custom startup slave screen
259 if (vm.count("slave-screen"))
261 auto screen = vm["slave-screen"].as<std::string>();
262 Config.startup_slave_screen_type = stringtoStartupScreenType(screen);
263 if (Config.startup_slave_screen_type == ScreenType::Unknown)
265 std::cerr << "Unknown slave screen: " << screen << "\n";
266 exit(1);
270 catch (std::exception &e)
272 cerr << "Error while processing configuration: " << e.what() << "\n";
273 exit(1);
275 return true;