Accept multiple occurences of --exec
[quvi-tool.git] / src / quvi / quvi.c
blob9b0c179ff03467d0e32bb32b24ea3c338e02855f
1 /* quvi
2 * Copyright (C) 2009-2011 Toni Gundogdu <legatvs@gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library 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 GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA
20 /* quvi.c - query media tool. */
22 #include "config.h"
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <errno.h>
31 #include <quvi/quvi.h>
32 #include <quvi/llst.h>
34 #include <curl/curl.h>
36 #include "platform.h"
37 #include "cmdline.h"
39 #define _free(p) \
40 do { if (p) { free(p); p=0; } } while (0)
42 static const char *config_fname = ".quvirc";
44 /* strepl.c */
45 extern char *strepl(const char *s, const char *what, const char *with);
47 /*@null@*/
48 static quvi_t quvi = NULL;
49 /*@null@*/
50 static CURL *curl = NULL;
52 typedef struct gengetopt_args_info *opts_t;
53 /*@null@*/
54 static opts_t opts = NULL;
56 /*@null@*/
57 static quvi_llst_node_t input = NULL;
59 /* Check whether a message should be printed. */
60 static int check_verbosity(int threshold)
62 if (opts->verbosity_arg >= threshold)
63 return (0);
64 return (1);
67 /* Prints to std(e)rr. 'mute' disables. */
68 static void spew_e(const char *fmt, ...)
70 va_list ap;
71 if (check_verbosity(verbosity_arg_mute) == 0)
72 return;
73 va_start(ap, fmt);
74 vfprintf(stderr, fmt, ap);
75 va_end(ap);
78 /* Prints to std(e)rr. 'quiet' disables. */
79 static void spew_qe(const char *fmt, ...)
81 va_list ap;
82 if (check_verbosity(verbosity_arg_quiet) == 0)
83 return;
84 va_start(ap, fmt);
85 vfprintf(stderr, fmt, ap);
86 va_end(ap);
89 /* Glorified printf. 'mute' disables. */
90 static void spew(const char *fmt, ...)
92 va_list ap;
93 if (check_verbosity(verbosity_arg_mute) == 0)
94 return;
95 va_start(ap, fmt);
96 vfprintf(stdout, fmt, ap);
97 va_end(ap);
100 static void dump_error_json(quvi_t quvi, QUVIcode rc)
102 spew_e("{\n"
103 " \"error\": [\n"
104 " {\n"
105 " \"message\": \"%s\"\n"
106 " }\n"
107 " ]\n"
108 "}\n", quvi_strerror(quvi, rc));
111 static void dump_error_xml(quvi_t quvi, QUVIcode rc)
113 spew_e("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
114 "<error>\n"
115 " <message>%s</message>\n"
116 "</error>\n",
117 quvi_strerror(quvi, rc));
120 static void dump_error(quvi_t quvi, QUVIcode rc)
122 switch (opts->export_level_arg)
124 case export_level_arg_media:
125 case export_level__NULL:
126 default:
127 spew_e("error: %s\n", quvi_strerror(quvi, rc));
128 break;
129 case export_level_arg_PLUS_errors:
130 switch (opts->export_format_arg)
132 case export_format_arg_json:
133 case export_format__NULL:
134 default:
135 dump_error_json(quvi, rc);
136 break;
137 case export_format_arg_xml:
138 dump_error_xml(quvi, rc);
139 break;
144 static void handle_resolve_status(quvi_word type)
146 if (type == QUVISTATUSTYPE_DONE)
147 spew_qe("done.\n");
148 else
149 spew_qe(":: Check for URL redirection ...");
152 static void handle_fetch_status(quvi_word type, void *p)
154 switch (type)
156 default:
157 spew_qe(":: Fetch %s ...", (char *)p);
158 break;
159 case QUVISTATUSTYPE_CONFIG:
160 spew_qe(":: Fetch config ...");
161 break;
162 case QUVISTATUSTYPE_PLAYLIST:
163 spew_qe(":: Fetch playlist ...");
164 break;
165 case QUVISTATUSTYPE_DONE:
166 spew_qe("done.\n");
167 break;
171 static void handle_verify_status(quvi_word type)
173 switch (type)
175 default:
176 spew_qe(":: Verify media URL ...");
177 break;
178 case QUVISTATUSTYPE_DONE:
179 spew_qe("done.\n");
180 break;
184 static int status_callback(long param, void *data)
186 quvi_word status, type;
188 status = quvi_loword(param);
189 type = quvi_hiword(param);
191 switch (status)
193 case QUVISTATUS_RESOLVE:
194 handle_resolve_status(type);
195 break;
197 case QUVISTATUS_FETCH:
198 handle_fetch_status(type, data);
199 break;
201 case QUVISTATUS_VERIFY:
202 handle_verify_status(type);
203 break;
206 fflush(stderr);
208 return (0);
211 /* Divided into smaller blocks. Otherwise -pedantic objects. */
213 #define LICENSE_1 \
214 "/* quvi\n" \
215 " * Copyright (C) 2009-2011 Toni Gundogdu <legatvs@gmail.com>\n"
217 #define LICENSE_2 \
218 " * This library is free software; you can redistribute it and/or\n" \
219 " * modify it under the terms of the GNU Lesser General Public\n" \
220 " * License as published by the Free Software Foundation; either\n" \
221 " * version 2.1 of the License, or (at your option) any later version.\n"
223 #define LICENSE_3 \
224 " * This library is distributed in the hope that it will be useful,\n" \
225 " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
226 " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" \
227 " * Lesser General Public License for more details.\n"
229 #define LICENSE_4 \
230 " * You should have received a copy of the GNU Lesser General Public\n" \
231 " * License along with this library; if not, write to the Free Software\n" \
232 " * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n" \
233 " * 02110-1301 USA\n" " */"
235 static void license()
237 printf("%s *\n%s *\n%s *\n%s\n",
238 LICENSE_1, LICENSE_2, LICENSE_3, LICENSE_4);
239 exit(0);
242 #undef LICENSE_4
243 #undef LICENSE_3
244 #undef LICENSE_2
245 #undef LICENSE_1
247 static void print_version()
249 static const char version[] =
250 #ifdef VN
252 #else
253 PACKAGE_VERSION
254 #endif
255 " for " CANONICAL_TARGET;
256 printf("quvi %s\n libquvi %s\n libquvi-scripts %s\n",
257 version, quvi_version(QUVI_VERSION_LONG),
258 quvi_version(QUVI_VERSION_SCRIPTS));
259 exit(0);
262 static void dump_host(char *domain, char *formats)
264 printf("%s\t%s\n", domain, formats);
265 quvi_free(domain);
266 quvi_free(formats);
269 /* Wraps quvi_supported_ident. */
270 static void supported(quvi_t quvi)
272 quvi_ident_t ident;
273 unsigned int i;
274 char *formats;
275 QUVIcode rc;
277 rc = QUVI_NOSUPPORT;
279 for (i=0; i<opts->inputs_num; ++i)
281 rc = quvi_supported_ident(quvi, (char*)opts->inputs[i], &ident);
282 if (rc == QUVI_OK)
284 quvi_ident_getprop(ident,
285 QUVI_IDENT_PROPERTY_FORMATS, &formats);
286 spew("%10s : %s\n", formats, (char *)opts->inputs[i]);
287 quvi_supported_ident_close(&ident);
289 else
290 dump_error(quvi,rc);
293 exit(rc);
296 /* Query which formats are available for the URL */
297 static void query_formats(quvi_t quvi)
299 unsigned int i;
300 QUVIcode rc;
302 if (opts->inputs_num <1)
304 spew_qe("error: no input URLs\n");
305 exit (QUVI_INVARG);
308 for (i=0,rc=0; i<opts->inputs_num; ++i)
310 char *formats = NULL;
312 rc = quvi_query_formats(quvi, (char*)opts->inputs[i], &formats);
313 if (rc == QUVI_OK)
315 spew("%10s : %s\n", formats, opts->inputs[i]);
316 quvi_free(formats);
318 else
319 dump_error(quvi,rc);
322 exit(rc);
325 /* dumps all supported hosts to stdout. */
326 static void support(quvi_t quvi)
328 int done = 0;
330 if (opts->inputs_num >0)
331 supported(quvi);
333 while (done == 0)
335 char *domain, *formats;
336 QUVIcode rc;
338 rc = quvi_next_supported_website(quvi, &domain, &formats);
340 switch (rc)
342 case QUVI_OK:
343 dump_host(domain, formats);
344 break;
345 case QUVI_LAST:
346 done = 1;
347 break;
348 default:
349 spew_e("%s\n", quvi_strerror(quvi, rc));
350 break;
354 exit(0);
357 static char * shell_escape( char *str )
359 size_t len = strlen( str );
360 int i;
361 str = realloc( str, 2 * len + 1 );
362 if ( !str ) abort();
363 for ( i = len - 1; i >= 0; --i )
365 str[ 2 * i + 1 ] = str[ i ];
366 str[ 2 * i ] = '\\';
368 str[ 2 * len ] = 0;
369 return str;
372 static void invoke_exec(quvi_media_t media, const char *exec_arg)
374 char *cmd, *media_url, *q_media_url;
375 char *page_title, *q_page_title, *t;
376 int rc;
378 quvi_getprop(media, QUVIPROP_PAGETITLE, &page_title);
379 t = strdup(page_title);
380 t = shell_escape(t);
381 asprintf(&q_page_title, "%s", t);
382 _free(t);
384 quvi_getprop(media, QUVIPROP_MEDIAURL, &media_url);
385 t = strdup(media_url);
386 t = shell_escape(t);
387 asprintf(&q_media_url, "%s", t);
388 _free(t);
390 cmd = strdup(exec_arg);
391 cmd = strepl(cmd, "%t", q_page_title);
392 cmd = strepl(cmd, "%u", q_media_url);
394 _free(q_page_title);
395 _free(q_media_url);
397 rc = system(cmd);
399 switch (rc)
401 case 0:
402 break;
403 case -1:
404 spew_e("error: failed to execute `%s'\n", cmd);
405 break;
406 default:
407 spew_e("error: child exited with: %d\n", rc >> 8);
408 break;
411 _free(cmd);
414 struct parsed_url_s
416 char *media_url;
417 char *content_type;
418 double content_length;
419 char *file_suffix;
422 typedef struct parsed_url_s *parsed_url_t;
424 static void dump_media_url_xml(parsed_url_t p, int i)
426 char *media_url = curl_easy_escape(curl, p->media_url, 0);
428 spew(" <link id=\"%d\">\n", i);
430 if (p->content_length >0)
431 spew(" <length_bytes>%.0f</length_bytes>\n", p->content_length);
433 if (strlen(p->content_type) >0)
434 spew(" <content_type>%s</content_type>\n", p->content_type);
436 if (strlen(p->file_suffix) >0)
437 spew(" <file_suffix>%s</file_suffix>\n", p->file_suffix);
439 spew(" <url>%s</url>\n"
440 " </link>\n",
441 (media_url != NULL)
442 ? media_url
443 : p->media_url);
445 _free(media_url);
448 static void dump_media_url_json(parsed_url_t p, int i, int prepend_newln)
450 if (prepend_newln != 0)
451 spew(",\n");
453 spew(" {\n"
454 " \"id\": \"%d\",\n", i);
456 if (p->content_length >0)
457 spew(" \"length_bytes\": \"%.0f\",\n", p->content_length);
459 if (strlen(p->content_type) >0)
460 spew(" \"content_type\": \"%s\",\n", p->content_type);
462 if (strlen(p->file_suffix) >0)
463 spew(" \"file_suffix\": \"%s\",\n", p->file_suffix);
465 spew(" \"url\": \"%s\"\n"
466 " }",
467 p->media_url);
470 static void dump_media_urls(quvi_media_t media)
472 int json_flag=0, i=1;
475 struct parsed_url_s p;
477 memset(&p, 0, sizeof(&p));
478 quvi_getprop(media, QUVIPROP_MEDIAURL, &p.media_url);
479 quvi_getprop(media, QUVIPROP_MEDIACONTENTTYPE, &p.content_type);
480 quvi_getprop(media, QUVIPROP_MEDIACONTENTLENGTH, &p.content_length);
481 quvi_getprop(media, QUVIPROP_FILESUFFIX, &p.file_suffix);
483 if (opts->xml_given == 1)
484 dump_media_url_xml(&p,i);
485 else
487 dump_media_url_json(&p, i, (int)(i>1));
488 json_flag = 1;
490 ++i;
492 while (quvi_next_media_url(media) == QUVI_OK);
494 if (json_flag == 1)
495 spew("\n");
498 struct parsed_s
500 char *page_url;
501 char *page_title;
502 char *media_id;
503 char *format;
504 char *host;
505 char *start_time;
506 char *thumb_url;
507 double duration;
510 typedef struct parsed_s *parsed_t;
512 static void dump_media_xml(parsed_t p)
514 char *e_page_url, *e_thumb_url;
516 e_page_url = curl_easy_escape(curl, p->page_url, 0);
517 e_thumb_url = curl_easy_escape(curl, p->thumb_url, 0);
519 spew("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
520 "<media id=\"%s\" host=\"%s\">\n"
521 " <format_requested>%s</format_requested>\n"
522 " <page_title>%s</page_title>\n"
523 " <page_url>%s</page_url>\n",
524 p->media_id,
525 p->host,
526 p->format,
527 p->page_title,
528 e_page_url ? e_page_url : "");
530 if (strlen(p->start_time) >0)
531 spew(" <start_time>%s</start_time>\n", p->start_time);
533 if (e_thumb_url != NULL && strlen(e_thumb_url) >0)
534 spew(" <thumbnail_url>%s</thumbnail_url>\n", e_thumb_url);
536 if (p->duration >0)
537 spew(" <duration>%.0f</duration>\n", p->duration);
539 if (e_page_url != NULL)
541 curl_free(e_page_url);
542 e_page_url = NULL;
545 if (e_thumb_url != NULL)
547 curl_free(e_thumb_url);
548 e_thumb_url = NULL;
552 static void dump_media_json(parsed_t p)
554 char *t;
556 t = strdup(p->page_title);
557 t = strepl(t, "\"", "\\\"");
559 spew("{\n"
560 " \"host\": \"%s\",\n"
561 " \"page_title\": \"%s\",\n"
562 " \"page_url\": \"%s\",\n"
563 " \"id\": \"%s\",\n"
564 " \"format_requested\": \"%s\",\n",
565 p->host,
567 p->page_url,
568 p->media_id,
569 p->format);
571 if (strlen(p->start_time) >0)
572 spew(" \"start_time\": \"%s\",\n", p->start_time);
574 if (strlen(p->thumb_url) >0)
575 spew(" \"thumbnail_url\": \"%s\",\n", p->thumb_url);
577 if (p->duration >0)
578 spew(" \"duration\": \"%.0f\",\n", p->duration);
580 spew(" \"link\": [\n");
582 _free(t);
585 static void dump_media(quvi_media_t media)
587 struct parsed_s p;
589 memset(&p, 0, sizeof(p));
591 quvi_getprop(media, QUVIPROP_HOSTID, &p.host);
592 quvi_getprop(media, QUVIPROP_PAGEURL, &p.page_url);
593 quvi_getprop(media, QUVIPROP_PAGETITLE, &p.page_title);
594 quvi_getprop(media, QUVIPROP_MEDIAID, &p.media_id);
595 quvi_getprop(media, QUVIPROP_FORMAT, &p.format);
596 quvi_getprop(media, QUVIPROP_STARTTIME, &p.start_time);
597 quvi_getprop(media, QUVIPROP_MEDIATHUMBNAILURL, &p.thumb_url);
598 quvi_getprop(media, QUVIPROP_MEDIADURATION, &p.duration);
600 if (opts->xml_given == 1)
601 dump_media_xml(&p);
602 else
603 dump_media_json(&p);
605 dump_media_urls(media);
607 if (opts->xml_given == 1)
608 spew("</media>\n");
609 else
610 spew(" ]\n}\n");
613 static void check_categories(QUVIcategory *n)
615 int i;
616 for (i=0, *n=0; i<opts->category_given; ++i)
618 switch (opts->category_arg[i])
620 case category_arg_all:
621 case category__NULL:
622 default:
623 *n |= QUVIPROTO_ALL;
624 break;
625 case category_arg_http:
626 *n |= QUVIPROTO_HTTP;
627 break;
628 case category_arg_mms:
629 *n |= QUVIPROTO_MMS;
630 break;
631 case category_arg_rtmp:
632 *n |= QUVIPROTO_RTMP;
633 break;
634 case category_arg_rtsp:
635 *n |= QUVIPROTO_RTSP;
636 break;
641 static void depr_category(const char *o)
643 spew_qe("warning: %s: deprecated, use --category instead\n", o);
646 static void depr_export_format(const char *o)
648 spew_qe("warning: %s: deprecated, use --export-format instead\n", o);
651 static void depr_verbosity(const char *o)
653 spew_qe("warning: %s: deprecated, use --verbosity instead\n", o);
656 static void depr_feat(const char *o)
658 spew_e("warning: %s: deprecated, use --feature instead\n", o);
661 static int enabled(enum enum_feature on)
663 const enum enum_feature off = on+1;
664 int i,r=1;
665 for (i=0; i<opts->feature_given; ++i)
667 if (opts->feature_arg[i] == on)
668 r = 1;
669 else if (opts->feature_arg[i] == on+1)
670 r = 0;
672 return (r);
675 static void init_quvi()
677 int resolve, verify, proxy;
678 QUVIcategory categories;
679 QUVIcode rc;
681 if ((rc = quvi_init(&quvi)) != QUVI_OK)
683 dump_error(quvi, rc);
684 exit(rc);
686 assert(quvi != NULL);
688 /* Set quvi options. */
690 quvi_setopt(quvi, QUVIOPT_FORMAT, opts->format_arg);
692 resolve = enabled(feature_arg_resolve);
693 verify = enabled(feature_arg_verify);
695 /* Deprecated. */
697 if (opts->no_resolve_given)
699 depr_feat("--no-resolve");
700 resolve = 0;
703 if (opts->no_verify_given)
705 depr_feat("--no-verify");
706 verify = 0;
709 quvi_setopt(quvi, QUVIOPT_NORESOLVE, resolve == 0);
710 quvi_setopt(quvi, QUVIOPT_NOVERIFY, verify == 0);
712 /* Category. */
714 categories = 0;
716 if (opts->category_arg != NULL)
717 check_categories(&categories);
719 /* Category: deprecated. To be removed in later versions. */
721 if (opts->category_http_given == 1)
723 depr_category("--category-http");
724 categories |= QUVIPROTO_HTTP;
726 if (opts->category_rtmp_given == 1)
728 depr_category("--category-rtmp");
729 categories |= QUVIPROTO_RTMP;
731 if (opts->category_rtsp_given == 1)
733 depr_category("--category-rtsp");
734 categories |= QUVIPROTO_RTSP;
736 if (opts->category_mms_given == 1)
738 depr_category("--category-mms");
739 categories |= QUVIPROTO_MMS;
741 if (opts->category_all_given == 1)
743 depr_category("--category-all");
744 categories = QUVIPROTO_ALL;
747 if (categories == 0)
748 categories = QUVIPROTO_ALL;
750 quvi_setopt(quvi, QUVIOPT_CATEGORY, categories);
752 /* Status callback */
754 quvi_setopt(quvi, QUVIOPT_STATUSFUNCTION, status_callback);
756 /* Use the quvi created cURL handle. */
758 quvi_getinfo(quvi, QUVIINFO_CURL, &curl);
759 assert(curl != NULL);
761 curl_easy_setopt(curl, CURLOPT_USERAGENT, opts->agent_arg);
762 curl_easy_setopt(curl, CURLOPT_PROXY, opts->proxy_arg);
764 proxy = enabled(feature_arg_proxy);
766 /* Deprecated. */
767 if (opts->no_proxy_given == 1)
769 depr_feat("--no-proxy");
770 proxy = 0;
773 #ifdef _1
774 printf("resolve=%d, verify=%d, proxy=%d\n", resolve, verify, proxy);
775 #endif
777 /* -- */
779 if (proxy == 0)
780 curl_easy_setopt(curl, CURLOPT_PROXY, "");
782 if (opts->verbose_libcurl_given)
784 opts->verbosity_arg = verbosity_arg_debug;
785 depr_verbosity("--verbose-libcurl");
788 curl_easy_setopt(curl, CURLOPT_VERBOSE,
789 opts->verbosity_arg == verbosity_arg_debug);
791 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, opts->connect_timeout_arg);
794 static void cleanup()
796 if (input)
797 quvi_llst_free(&input);
799 if (quvi)
800 quvi_close(&quvi);
802 if (opts)
804 cmdline_parser_free(opts);
805 _free(opts);
808 assert(input == NULL);
809 assert(quvi == NULL);
810 assert(opts == NULL);
813 static void read_from(FILE *f)
815 char b[256];
817 if (!f)
818 return;
820 while (fgets(b, (int)sizeof(b), f))
822 if (strlen(b) >16)
824 const size_t n = strlen(b)-1;
826 if (b[n] == '\n')
827 b[n] = '\0';
829 quvi_llst_append(&input, strdup(b));
834 /*@null@*/ static char *parse_url_scheme(const char *url)
836 char *p, *r;
838 p = strstr(url, ":/");
839 if (!p)
840 return (NULL);
842 asprintf(&r, "%.*s", (int)(p - url), url);
844 return (r);
847 static int is_url(const char *s)
849 char *p = parse_url_scheme(s);
850 if (p)
852 _free(p);
853 return (1);
855 return (0);
858 static void read_file(const char *path)
860 FILE *f = fopen(path, "rt");
861 if (f == NULL)
863 #ifdef HAVE_STRERROR
864 spew_e("error: %s: %s\n", path, strerror(errno));
865 #else
866 perror("fopen");
867 #endif
869 else
871 read_from(f);
872 fclose(f);
873 f = NULL;
877 static unsigned int read_input()
879 if (opts->inputs_num == 0)
880 read_from(stdin);
881 else
883 unsigned int i;
884 for (i=0; i<opts->inputs_num; ++i)
886 if (is_url(opts->inputs[i]) == 0)
887 read_file(opts->inputs[i]);
888 else /* Must be an URL. */
889 quvi_llst_append(&input, strdup(opts->inputs[i]));
892 return ((unsigned int)quvi_llst_size(input));
895 static int config_exists(const char *fpath)
897 FILE *f = fopen(fpath, "r");
898 int have_config = 0;
899 if (f != NULL)
901 have_config = 1;
902 fclose(f);
903 f = NULL;
905 return (have_config);
908 static int parse_config(int argc, char **argv, char *home)
910 struct cmdline_parser_params *pp = NULL;
911 int have_config = 0;
912 char *fpath = NULL;
914 asprintf(&fpath, "%s/%s", home, config_fname);
915 if (fpath == NULL)
916 exit (QUVI_MEM);
918 have_config = config_exists(fpath);
919 if (have_config == 0)
921 _free(fpath);
922 return (have_config);
925 pp = cmdline_parser_params_create();
926 pp->check_required = 0;
928 have_config = (int)(cmdline_parser_config_file(fpath, opts, pp) == 0);
929 if (have_config == 1)
931 _free(fpath); /* cmdline_parser_ext may exit early. */
933 /* Config parsed at this point. Next, apply the cmdline options
934 * and allow overriding the values defined in the config.
936 * This step resets the $opt_given flags, e.g. if the config
937 * contains the "proxy", the parser sets proxy_given to 1,
938 * --proxy is used to override the config value, the flag
939 * is reset to 0. */
940 pp->check_required = 1;
941 pp->initialize = 0;
942 pp->override = 1;
943 have_config = (int)(cmdline_parser_ext(argc, argv, opts, pp) == 0);
946 _free(fpath);
947 _free(pp);
949 return (have_config);
952 static void parse_cmdline(int argc, char **argv)
954 int have_config = 0;
955 char *home = NULL;
957 opts = calloc(1, sizeof(struct gengetopt_args_info));
958 if (opts == NULL)
959 exit (QUVI_MEM);
961 home = getenv("QUVI_HOME");
962 if (home == NULL)
963 home = getenv("HOME");
965 if (home != NULL && getenv("QUVI_NO_CONFIG") == NULL)
966 have_config = parse_config(argc, argv, home);
968 if (have_config == 0)
970 if (cmdline_parser(argc, argv, opts) != 0)
971 exit (QUVI_INVARG);
975 static int process_queue()
977 QUVIcode rc, last_error;
978 unsigned int input_num;
979 quvi_llst_node_t curr;
980 quvi_media_t media;
981 int i, errors;
983 input_num = read_input();
984 last_error = QUVI_OK;
985 errors = 0;
987 if (input_num == 0)
989 spew_qe("error: no input URLs\n");
990 return (QUVI_INVARG);
993 for (i=0, curr=input; curr; ++i)
995 char *url = quvi_llst_data(curr);
996 rc = quvi_parse(quvi, url, &media);
997 if (rc == QUVI_OK)
999 int i;
1001 assert(media != NULL);
1002 dump_media(media);
1004 for (i=0; i<opts->exec_given; ++i)
1008 invoke_exec(media, opts->exec_arg[i]);
1010 while (quvi_next_media_url(media) == QUVI_OK);
1013 else
1015 dump_error(quvi, rc);
1016 last_error = rc;
1017 ++errors;
1019 quvi_parse_close(&media);
1020 assert(media == NULL);
1021 curr = quvi_llst_next(curr);
1024 if (input_num >1)
1026 spew_qe("Results: %d OK, %d failed (last 0x%02x), exit with 0x%02x\n",
1027 input_num - errors, errors, last_error, rc);
1029 return (rc);
1032 int main(int argc, char *argv[])
1034 assert(quvi == NULL);
1035 assert(curl == NULL);
1036 assert(opts == NULL);
1037 assert(input == NULL);
1039 atexit(cleanup);
1040 parse_cmdline(argc, argv);
1042 if (opts->version_given == 1)
1043 print_version();
1045 if (opts->license_given == 1)
1046 license();
1048 init_quvi();
1050 /* Deprecated. */
1052 if (opts->xml_given)
1054 opts->export_format_arg = export_format_arg_xml;
1055 depr_export_format("--xml");
1058 if (opts->quiet_given == 1)
1060 depr_verbosity("--quiet"); /* Must come before the next line */
1061 opts->verbosity_arg = verbosity_arg_quiet;
1064 if (opts->query_formats_given == 1)
1065 query_formats(quvi);
1067 if (opts->support_given == 1)
1068 support(quvi);
1070 return (process_queue());
1073 /* vim: set ts=2 sw=2 tw=72 expandtab: */