1 /***************************************************************************
2 * Copyright (C) 2008-2016 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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. *
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. *
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 ***************************************************************************/
22 #include <boost/algorithm/string/trim.hpp>
23 #include <boost/filesystem/operations.hpp>
24 #include <boost/program_options.hpp>
29 #include "configuration.h"
33 #include "utility/string.h"
35 namespace po
= boost::program_options
;
44 std::string
xdg_config_home()
47 const char *env_xdg_config_home
= getenv("XDG_CONFIG_HOME");
48 if (env_xdg_config_home
== nullptr)
49 result
= "~/.config/";
52 result
= env_xdg_config_home
;
53 if (!result
.empty() && result
.back() != '/')
61 void expand_home(std::string
&path
)
63 assert(env_home
!= nullptr);
64 if (!path
.empty() && path
[0] == '~')
65 path
.replace(0, 1, env_home
);
68 bool configure(int argc
, char **argv
)
70 const std::vector
<std::string
> default_config_paths
= {
72 xdg_config_home() + "ncmpcpp/config"
75 std::string bindings_path
;
76 std::vector
<std::string
> config_paths
;
78 po::options_description
options("Options");
80 ("host,h", po::value
<std::string
>()->value_name("HOST")->default_value("localhost"), "connect to server at host")
81 ("port,p", po::value
<int>()->value_name("PORT")->default_value(6600), "connect to server at port")
82 ("current-song", po::value
<std::string
>()->value_name("FORMAT")->implicit_value("{{{(%l) }{{%a - }%t}}|{%f}}"), "print current song using given format and exit")
83 ("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)")
84 ("ignore-config-errors", "ignore unknown and invalid options in configuration files")
85 ("test-lyrics-fetchers", "check if lyrics fetchers work")
86 ("bindings,b", po::value
<std::string
>(&bindings_path
)->value_name("PATH")->default_value("~/.ncmpcpp/bindings"), "specify bindings file")
87 ("screen,s", po::value
<std::string
>()->value_name("SCREEN"), "specify the startup screen")
88 ("slave-screen,S", po::value
<std::string
>()->value_name("SCREEN"), "specify the startup slave screen")
89 ("help,?", "show help message")
90 ("version,v", "display version information")
96 po::store(po::parse_command_line(argc
, argv
, options
), vm
);
100 cout
<< "Usage: " << argv
[0] << " [options]...\n" << options
<< "\n";
103 if (vm
.count("version"))
105 std::cout
<< "ncmpcpp " << VERSION
<< "\n\n"
106 << "optional screens compiled-in:\n"
107 # ifdef HAVE_TAGLIB_H
109 << " - tiny tag editor\n"
111 # ifdef ENABLE_OUTPUTS
114 # ifdef ENABLE_VISUALIZER
120 << "\nencoding detection: "
121 # ifdef HAVE_LANGINFO_H
125 # endif // HAVE_LANGINFO_H
126 << "\nbuilt with support for:"
131 # ifdef HAVE_TAGLIB_H
140 if (vm
.count("test-lyrics-fetchers"))
142 std::vector
<std::tuple
<std::string
, std::string
, std::string
>> fetcher_data
= {
143 std::make_tuple("lyricwiki", "rihanna", "umbrella"),
144 std::make_tuple("azlyrics", "rihanna", "umbrella"),
145 std::make_tuple("genius", "rihanna", "umbrella"),
146 std::make_tuple("sing365", "rihanna", "umbrella"),
147 std::make_tuple("lyricsmania", "rihanna", "umbrella"),
148 std::make_tuple("metrolyrics", "rihanna", "umbrella"),
149 std::make_tuple("justsomelyrics", "rihanna", "umbrella"),
150 std::make_tuple("jahlyrics", "sean kingston", "dry your eyes"),
151 std::make_tuple("plyrics", "offspring", "genocide"),
152 std::make_tuple("tekstowo", "rihanna", "umbrella"),
154 for (auto &data
: fetcher_data
)
156 auto fetcher
= boost::lexical_cast
<LyricsFetcher_
>(std::get
<0>(data
));
157 std::cout
<< std::setw(20)
162 auto result
= fetcher
->fetch(std::get
<1>(data
), std::get
<2>(data
));
163 std::cout
<< (result
.first
? "ok" : "failed")
169 // get home directory
170 env_home
= getenv("HOME");
171 if (env_home
== nullptr)
173 cerr
<< "Fatal error: HOME environment variable is not defined\n";
177 // read configuration
178 std::for_each(config_paths
.begin(), config_paths
.end(), expand_home
);
179 if (Config
.read(config_paths
, vm
.count("ignore-config-errors")) == false)
182 // if bindings file was not specified, use the one from main directory.
183 if (vm
["bindings"].defaulted())
184 bindings_path
= Config
.ncmpcpp_directory
+ "bindings";
186 expand_home(bindings_path
);
189 if (Bindings
.read(bindings_path
) == false)
191 Bindings
.generateDefaults();
193 // create directories
194 boost::filesystem::create_directory(Config
.ncmpcpp_directory
);
195 boost::filesystem::create_directory(Config
.lyrics_directory
);
197 // try to get MPD connection details from environment variables
198 // as they take precedence over these from the configuration.
199 auto env_host
= getenv("MPD_HOST");
200 auto env_port
= getenv("MPD_PORT");
201 if (env_host
!= nullptr)
202 Mpd
.SetHostname(env_host
);
203 if (env_port
!= nullptr)
205 auto trimmed_env_port
= boost::trim_copy
<std::string
>(env_port
);
207 Mpd
.SetPort(boost::lexical_cast
<int>(trimmed_env_port
));
208 } catch (boost::bad_lexical_cast
&) {
209 throw std::runtime_error("MPD_PORT environment variable ("
210 + std::string(env_port
)
211 + ") is not a number");
215 // if MPD connection details are provided as command line
216 // parameters, use them as their priority is the highest.
217 if (!vm
["host"].defaulted())
218 Mpd
.SetHostname(vm
["host"].as
<std::string
>());
219 if (!vm
["port"].defaulted())
220 Mpd
.SetPort(vm
["port"].as
<int>());
221 Mpd
.SetTimeout(Config
.mpd_connection_timeout
);
223 // print current song
224 if (vm
.count("current-song"))
227 auto s
= Mpd
.GetCurrentSong();
230 auto format
= Format::parse(vm
["current-song"].as
<std::string
>(), Format::Flags::Tag
);
231 std::cout
<< Format::stringify
<char>(format
, &s
);
236 // custom startup screen
237 if (vm
.count("screen"))
239 auto screen
= vm
["screen"].as
<std::string
>();
240 Config
.startup_screen_type
= stringtoStartupScreenType(screen
);
241 if (Config
.startup_screen_type
== ScreenType::Unknown
)
243 std::cerr
<< "Unknown screen: " << screen
<< "\n";
248 // custom startup slave screen
249 if (vm
.count("slave-screen"))
251 auto screen
= vm
["slave-screen"].as
<std::string
>();
252 Config
.startup_slave_screen_type
= stringtoStartupScreenType(screen
);
253 if (Config
.startup_slave_screen_type
== ScreenType::Unknown
)
255 std::cerr
<< "Unknown slave screen: " << screen
<< "\n";
260 catch (std::exception
&e
)
262 cerr
<< "Error while processing configuration: " << e
.what() << "\n";