1 /* cclive
2 * Copyright (C) 2010-2013 Toni Gundogdu <legatvs@gmail.com>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <ccinternal>
19
20 #include <iostream>
21 #include <fstream>
22 #include <cstring>
23
24 #include <boost/filesystem.hpp>
25 #include <boost/foreach.hpp>
26
27 #ifndef foreach
28 #define foreach BOOST_FOREACH
29 #endif
30
31 #include <ccre>
32 #include <ccoptions>
33
34 namespace cc
35 {
36
37 options opts;
38
39 namespace po = boost::program_options;
40 namespace fs = boost::filesystem;
41
42 typedef std::vector<std::string> vst;
43
parse(int argc,char ** argv)44 void options::parse(int argc, char **argv)
45 {
46 memset(&flags, 0, sizeof(struct flags_s));
47
48 // Path to ccliverc.
49
50 #if BOOST_FILESYSTEM_VERSION > 2
51 fs::path conf_path(fs::current_path());
52 #else
53 fs::path conf_path(fs::current_path<fs::path>());
54 #endif
55
56 const char *home = getenv("HOME");
57
58 if (home && strlen(home) > 0)
59 conf_path = fs::system_complete(fs::path(home));
60
61 conf_path /=
62 #ifndef _WIN32
63 std::string(".") +
64 #endif
65 std::string("ccliverc");
66
67 // Construct options.
68
69 po::options_description generic;
70 std::string conf_file;
71
72 generic.add_options()
73 ("print-config,D",
74 po::value(&flags.print_config)->zero_tokens(),
75 "Print value of defined config options")
76 ("version,v",
77 po::value(&flags.version)->zero_tokens(),
78 "Print version and exit")
79 ("help,h",
80 po::value(&flags.help)->zero_tokens(),
81 "Print help and exit")
82 ("license",
83 po::value(&flags.license)->zero_tokens(),
84 "Print license and exit")
85 ("support",
86 po::value(&flags.support)->zero_tokens(),
87 "Print supported websites and exit")
88 ("verbose-libcurl,B",
89 po::value(&flags.verbose_libcurl)->zero_tokens()->default_value(false),
90 "Turn on libcurl verbose output")
91 ("quiet,q",
92 po::value(&flags.quiet)->zero_tokens()->default_value(false),
93 "Turn off all output, excl. errors")
94 #ifdef HAVE_FORK
95 ("background,b",
96 po::value(&flags.background)->zero_tokens()->default_value(false),
97 "Go to background")
98 #endif
99 ("print-streams,S",
100 po::value(&flags.print_streams)->zero_tokens(),
101 "Print available media streams")
102 ("stream,s",
103 po::value<std::string>(),
104 "Select media stream")
105 ("query-formats,F",
106 po::value(&flags.query_formats)->zero_tokens(),
107 "Query available formats to URL (depr.)")
108 ("format,f",
109 po::value<std::string>(),
110 "Download media format (depr.)")
111 ("overwrite,W",
112 po::value(&flags.overwrite)->zero_tokens()->default_value(false),
113 "Overwrite existing media")
114 ("output-file,O",
115 po::value<std::string>(),
116 "Write media to arg")
117 ("no-download,n",
118 po::value(&flags.no_download)->zero_tokens()->default_value(false),
119 "Do not download media, print details")
120 ("no-proxy",
121 po::value(&flags.no_proxy)->zero_tokens()->default_value(false),
122 "Do not use HTTP proxy")
123 ("log-file",
124 po::value<std::string>()->default_value("cclive_log"),
125 "Write log output to arg")
126 ("config-file",
127 po::value<std::string>(&conf_file)->default_value(conf_path.string()),
128 "Read args from arg")
129 ;
130
131 // Config.
132
133 po::options_description config("Configuration");
134
135 config.add_options()
136 ("no-resolve,r",
137 po::value(&flags.no_resolve)->zero_tokens()->default_value(false),
138 "Do not resolve URL redirections")
139 ("continue,c",
140 po::value(&flags.cont)->zero_tokens()->default_value(false),
141 "Resume partially downloaded media")
142 ("timestamp,N",
143 po::value(&flags.timestamp)->zero_tokens()->default_value(false),
144 "Try to preserve modification time")
145 ("prefer-format,p",
146 po::value<std::vector<std::string> >()->composing(),
147 "Preferred format [domain:format[,...]]")
148 ("progressbar",
149 po::value<std::string>()->default_value("normal"),
150 "Use progressbar arg")
151 ("update-interval",
152 po::value<double>()->default_value(1.0),
153 "Update interval of progressbar")
154 ("filename-format",
155 po::value<std::string>()->default_value("%t.%s"),
156 "Downloaded media filename format")
157 ("output-dir",
158 po::value<std::string>(),
159 "Write downloaded media to arg directory")
160 ("tr,t",
161 po::value<vst>()->composing(),
162 "Translate characters in media title")
163 ("regexp",
164 po::value<std::string>(),
165 "Regexp to cleanup media title (depr.)")
166 ("subst", po::value<std::string>(),
167 "Replace matched occurences in filename (depr.)")
168 ("exec", po::value<vst>()->composing(),
169 "Invoke arg after each finished download")
170 ("agent",
171 po::value<std::string>()->default_value("Mozilla/5.0"),
172 "Identify as arg to HTTP servers")
173 ("proxy", po::value<std::string>(),
174 "Use proxy for HTTP connections")
175 ("throttle", po::value<int>()->default_value(0),
176 "Do not exceed transfer rate arg KB/s")
177 ("connect-timeout", po::value<int>()->default_value(30),
178 "Seconds connecting allowed to take")
179 ("transfer-timeout", po::value<int>()->default_value(0),
180 "Seconds transfer allowed to take")
181 ("dns-cache-timeout", po::value<int>()->default_value(60),
182 "Seconds DNS resolves kept in memory")
183 ("max-retries", po::value<int>()->default_value(5),
184 "Max download attempts before giving up")
185 ("retry-wait", po::value<int>()->default_value(5),
186 "Time to wait before retrying")
187 ;
188
189 // Hidden.
190
191 po::options_description hidden;
192
193 hidden.add_options()
194 ("url", po::value<vst>(), "url");
195
196 // Visible.
197
198 _visible.add(generic).add(config);
199
200 // Command line options.
201
202 po::options_description cmdline_options;
203 cmdline_options.add(generic).add(config).add(hidden);
204
205 // Config file options.
206
207 po::options_description config_file_options;
208 config_file_options.add(config);
209
210 // Positional.
211
212 po::positional_options_description p;
213 p.add("url", -1);
214
215 // Parse.
216
217 store(po::command_line_parser(argc,argv)
218 .options(cmdline_options).positional(p).run(), _map);
219 notify(_map);
220
221 // Read config.
222
223 std::ifstream ifs(conf_file.c_str());
224
225 if (ifs)
226 {
227 store(parse_config_file(ifs, config_file_options), _map);
228 notify(_map);
229 }
230
231 _validate();
232 }
233
warn_depr(const std::string & w,const std::string & n)234 static void warn_depr(const std::string& w, const std::string& n)
235 {
236 std::clog << "[WARNING] '--" << w << "' is deprecated and will be removed "
237 << "in later versions\n[WARNING] Use '--" << n << "' instead"
238 << std::endl;
239 }
240
_validate()241 void options::_validate()
242 {
243 std::string empty;
244
245 if (_map.count("format"))
246 warn_depr("format", "stream");
247
248 if (_map.count("query-formats"))
249 warn_depr("query-formats", "print-streams");
250
251 if (_map.count("tr"))
252 {
253 vst v = _map["tr"].as<vst>();
254 foreach (const std::string s, v)
255 {
256 re::tr(s, empty);
257 }
258 }
259
260 if (_map.count("regexp")) // Deprecated.
261 {
262 warn_depr("regexp", "tr");
263
264 std::string s = _map["regexp"].as<std::string>();
265 if (!cc::re::capture(s, empty))
266 {
267 std::stringstream b;
268 b << "--regexp: expects "
269 << "`/pattern/flags', for example: \"/(\\w|\\s)/g\"";
270 throw std::runtime_error(b.str());
271 }
272 }
273
274 if (_map.count("subst")) // Deprecated.
275 {
276 warn_depr("subst", "tr");
277
278 std::istringstream iss( _map["subst"].as<std::string>());
279 vst v;
280
281 std::copy(
282 std::istream_iterator<std::string >(iss),
283 std::istream_iterator<std::string >(),
284 std::back_inserter<vst>(v)
285 );
286
287 foreach (const std::string s, v)
288 {
289 if (!cc::re::subst(s,empty))
290 {
291 std::stringstream b;
292 b << "--subst: expects " << "`s{old}{new}flags'";
293 throw std::runtime_error(b.str());
294 }
295 }
296 }
297 }
298
dump()299 void options::dump()
300 {
301 for (po::variables_map::iterator i = _map.begin(); i != _map.end(); ++i)
302 {
303 const po::variable_value &v = i->second;
304
305 if (v.empty())
306 continue;
307
308 const std::type_info &t = v.value().type();
309 bool nl = true;
310
311 if (t == typeid(bool))
312 std::cout << i->first << " is " << (v.as<bool>() ? "set" : "unset");
313 else if (t == typeid(vst))
314 {
315 const vst r = v.as<vst>();
316 foreach (const std::string s, r)
317 {
318 std::cout << i->first << "=" << s << "\n";
319 }
320 nl = false;
321 }
322 else
323 {
324 std::cout << i->first << "=";
325 if (t == typeid(std::string))
326 std::cout << v.as<std::string>();
327 else if (t == typeid(double))
328 std::cout << v.as<double>();
329 else if (t == typeid(int))
330 std::cout << v.as<int>();
331 else
332 std::cout << "<unsupported type error>";
333 }
334
335 if (!nl)
336 std::cout << std::flush;
337 else
338 std::cout << std::endl;
339 }
340 }
341
342 } // namespace cc
343
344 // vim: set ts=2 sw=2 tw=72 expandtab:
345