actions: use unique_ptr for storing actions
[ncmpcpp.git] / src / configuration.cpp
blobb5ef96e1e6c3af2e32fc506342d15e706227ffb9
1 /***************************************************************************
2 * Copyright (C) 2008-2016 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 <iostream>
27 #include "bindings.h"
28 #include "configuration.h"
29 #include "config.h"
30 #include "mpdpp.h"
31 #include "settings.h"
32 #include "utility/string.h"
34 namespace po = boost::program_options;
36 using std::cerr;
37 using std::cout;
39 namespace {
41 const char *env_home;
43 std::string xdg_config_home()
45 std::string result;
46 const char *env_xdg_config_home = getenv("XDG_CONFIG_HOME");
47 if (env_xdg_config_home == nullptr)
48 result = "~/.config/";
49 else
51 result = env_xdg_config_home;
52 if (!result.empty() && result.back() != '/')
53 result += "/";
55 return result;
60 void expand_home(std::string &path)
62 assert(env_home != nullptr);
63 if (!path.empty() && path[0] == '~')
64 path.replace(0, 1, env_home);
67 bool configure(int argc, char **argv)
69 const std::vector<std::string> default_config_paths = {
70 "~/.ncmpcpp/config",
71 xdg_config_home() + "ncmpcpp/config"
74 std::string bindings_path;
75 std::vector<std::string> config_paths;
77 po::options_description options("Options");
78 options.add_options()
79 ("host,h", po::value<std::string>()->default_value("localhost"), "connect to server at host")
80 ("port,p", po::value<int>()->default_value(6600), "connect to server at port")
81 ("current-song", po::value<std::string>()->implicit_value("{{{(%l) }{{%a - }%t}}|{%f}}"), "print current song using given format and exit")
82 ("config,c", po::value<std::vector<std::string>>(&config_paths)->default_value(default_config_paths, join<std::string>(default_config_paths, " AND ")), "specify configuration file(s)")
83 ("ignore-config-errors", "ignore unknown and invalid options in configuration files")
84 ("bindings,b", po::value<std::string>(&bindings_path)->default_value("~/.ncmpcpp/bindings"), "specify bindings file")
85 ("screen,s", po::value<std::string>(), "specify the startup screen")
86 ("slave-screen,S", po::value<std::string>(), "specify the startup slave screen")
87 ("help,?", "show help message")
88 ("version,v", "display version information")
91 po::variables_map vm;
92 try
94 po::store(po::parse_command_line(argc, argv, options), vm);
96 if (vm.count("help"))
98 cout << "Usage: " << argv[0] << " [options]...\n" << options << "\n";
99 return false;
101 if (vm.count("version"))
103 std::cout << "ncmpcpp " << VERSION << "\n\n"
104 << "optional screens compiled-in:\n"
105 # ifdef HAVE_TAGLIB_H
106 << " - tag editor\n"
107 << " - tiny tag editor\n"
108 # endif
109 # ifdef ENABLE_OUTPUTS
110 << " - outputs\n"
111 # endif
112 # ifdef ENABLE_VISUALIZER
113 << " - visualizer\n"
114 # endif
115 # ifdef ENABLE_CLOCK
116 << " - clock\n"
117 # endif
118 << "\nencoding detection: "
119 # ifdef HAVE_LANGINFO_H
120 << "enabled"
121 # else
122 << "disabled"
123 # endif // HAVE_LANGINFO_H
124 << "\nbuilt with support for:"
125 # ifdef HAVE_FFTW3_H
126 << " fftw"
127 # endif
128 << " ncurses"
129 # ifdef HAVE_TAGLIB_H
130 << " taglib"
131 # endif
132 # ifdef NCMPCPP_UNICODE
133 << " unicode"
134 # endif
135 << "\n";
136 return false;
139 po::notify(vm);
140 // get home directory
141 env_home = getenv("HOME");
142 if (env_home == nullptr)
144 cerr << "Fatal error: HOME environment variable is not defined\n";
145 return false;
148 // read configuration
149 std::for_each(config_paths.begin(), config_paths.end(), expand_home);
150 if (Config.read(config_paths, vm.count("ignore-config-errors")) == false)
151 exit(1);
153 // if bindings file was not specified, use the one from main directory.
154 if (vm["bindings"].defaulted())
155 bindings_path = Config.ncmpcpp_directory + "bindings";
156 else
157 expand_home(bindings_path);
159 // read bindings
160 if (Bindings.read(bindings_path) == false)
161 exit(1);
162 Bindings.generateDefaults();
164 // create directories
165 boost::filesystem::create_directory(Config.ncmpcpp_directory);
166 boost::filesystem::create_directory(Config.lyrics_directory);
168 // try to get MPD connection details from environment variables
169 // as they take precedence over these from the configuration.
170 auto env_host = getenv("MPD_HOST");
171 auto env_port = getenv("MPD_PORT");
172 if (env_host != nullptr)
173 Mpd.SetHostname(env_host);
174 if (env_port != nullptr)
176 auto trimmed_env_port = boost::trim_copy<std::string>(env_port);
177 try {
178 Mpd.SetPort(boost::lexical_cast<int>(trimmed_env_port));
179 } catch (boost::bad_lexical_cast &) {
180 throw std::runtime_error("MPD_PORT environment variable ("
181 + std::string(env_port)
182 + ") is not a number");
186 // if MPD connection details are provided as command line
187 // parameters, use them as their priority is the highest.
188 if (!vm["host"].defaulted())
189 Mpd.SetHostname(vm["host"].as<std::string>());
190 if (!vm["port"].defaulted())
191 Mpd.SetPort(vm["port"].as<int>());
192 Mpd.SetTimeout(Config.mpd_connection_timeout);
194 // print current song
195 if (vm.count("current-song"))
197 Mpd.Connect();
198 auto s = Mpd.GetCurrentSong();
199 if (!s.empty())
201 auto format = Format::parse(vm["current-song"].as<std::string>(), Format::Flags::Tag);
202 std::cout << Format::stringify<char>(format, &s);
204 return false;
207 // custom startup screen
208 if (vm.count("screen"))
210 auto screen = vm["screen"].as<std::string>();
211 Config.startup_screen_type = stringtoStartupScreenType(screen);
212 if (Config.startup_screen_type == ScreenType::Unknown)
214 std::cerr << "Unknown screen: " << screen << "\n";
215 exit(1);
219 // custom startup slave screen
220 if (vm.count("slave-screen"))
222 auto screen = vm["slave-screen"].as<std::string>();
223 Config.startup_slave_screen_type = stringtoStartupScreenType(screen);
224 if (Config.startup_slave_screen_type == ScreenType::Unknown)
226 std::cerr << "Unknown slave screen: " << screen << "\n";
227 exit(1);
231 catch (std::exception &e)
233 cerr << "Error while processing configuration: " << e.what() << "\n";
234 exit(1);
236 return true;