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