Address warnings spewed by splint
[quvi-tool.git] / src / quvi / quvi.c
blobe1c3d3803f58bcb78869e42eb9108593b5802b60
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 <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <assert.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 /* strepl.c */
43 extern char *strepl(const char *s, const char *what, const char *with);
45 static int verbose_flag = 1;
46 /*@null@*/
47 static quvi_t quvi = NULL;
48 /*@null@*/
49 static CURL *curl = NULL;
51 typedef struct gengetopt_args_info *opts_t;
52 /*@null@*/
53 static opts_t opts = NULL;
55 /*@null@*/
56 static quvi_llst_node_t inputs = NULL;
58 /* prints to std(e)rr. */
59 static void spew_e(const char *fmt, ...)
61 va_list ap;
62 va_start(ap, fmt);
63 vfprintf(stderr, fmt, ap);
64 va_end(ap);
67 /* respects (q)uiet, prints to std(e)rr. */
68 static void spew_qe(const char *fmt, ...)
70 va_list ap;
71 if (verbose_flag == 0)
72 return;
73 va_start(ap, fmt);
74 vfprintf(stderr, fmt, ap);
75 va_end(ap);
78 /* glorified printf. */
79 static void spew(const char *fmt, ...)
81 va_list ap;
82 va_start(ap, fmt);
83 vfprintf(stdout, fmt, ap);
84 va_end(ap);
87 static void dump_error_json(quvi_t quvi, QUVIcode rc)
89 fprintf(stderr,
90 "{\n"
91 " \"error\": [\n"
92 " {\n"
93 " \"message\": \"%s\"\n"
94 " }\n"
95 " ]\n"
96 "}\n", quvi_strerror(quvi, rc));
99 static void dump_error_xml(quvi_t quvi, QUVIcode rc)
101 fprintf(stderr,
102 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
103 "<error>\n"
104 " <message>%s</message>\n"
105 "</error>\n",
106 quvi_strerror(quvi, rc));
109 static void dump_error(quvi_t quvi, QUVIcode rc)
111 if (opts->export_errors_given == 1)
113 if (opts->xml_given == 1)
114 dump_error_xml(quvi, rc);
115 else
116 dump_error_json(quvi, rc);
118 else
119 fprintf(stderr, "error: %s\n", quvi_strerror(quvi, rc));
122 static void handle_resolve_status(quvi_word type)
124 if (type == QUVISTATUSTYPE_DONE)
125 spew_qe("done.\n");
126 else
127 spew_qe(":: Check for URL redirection ...");
130 static void handle_fetch_status(quvi_word type, void *p)
132 switch (type)
134 default:
135 spew_qe(":: Fetch %s ...", (char *)p);
136 break;
137 case QUVISTATUSTYPE_CONFIG:
138 spew_qe(":: Fetch config ...");
139 break;
140 case QUVISTATUSTYPE_PLAYLIST:
141 spew_qe(":: Fetch playlist ...");
142 break;
143 case QUVISTATUSTYPE_DONE:
144 spew_qe("done.\n");
145 break;
149 static void handle_verify_status(quvi_word type)
151 switch (type)
153 default:
154 spew_qe(":: Verify media URL ...");
155 break;
156 case QUVISTATUSTYPE_DONE:
157 spew_qe("done.\n");
158 break;
162 static int status_callback(long param, void *data)
164 quvi_word status, type;
166 status = quvi_loword(param);
167 type = quvi_hiword(param);
169 switch (status)
171 case QUVISTATUS_RESOLVE:
172 handle_resolve_status(type);
173 break;
175 case QUVISTATUS_FETCH:
176 handle_fetch_status(type, data);
177 break;
179 case QUVISTATUS_VERIFY:
180 handle_verify_status(type);
181 break;
184 fflush(stderr);
186 return (0);
189 /* Divided into smaller blocks. Otherwise -pedantic objects. */
191 #define LICENSE_1 \
192 "/* quvi\n" \
193 " * Copyright (C) 2009-2011 Toni Gundogdu <legatvs@gmail.com>\n"
195 #define LICENSE_2 \
196 " * This library is free software; you can redistribute it and/or\n" \
197 " * modify it under the terms of the GNU Lesser General Public\n" \
198 " * License as published by the Free Software Foundation; either\n" \
199 " * version 2.1 of the License, or (at your option) any later version.\n"
201 #define LICENSE_3 \
202 " * This library is distributed in the hope that it will be useful,\n" \
203 " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
204 " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" \
205 " * Lesser General Public License for more details.\n"
207 #define LICENSE_4 \
208 " * You should have received a copy of the GNU Lesser General Public\n" \
209 " * License along with this library; if not, write to the Free Software\n" \
210 " * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n" \
211 " * 02110-1301 USA\n" " */"
213 static void license()
215 printf("%s *\n%s *\n%s *\n%s\n",
216 LICENSE_1, LICENSE_2, LICENSE_3, LICENSE_4);
217 exit(0);
220 #undef LICENSE_4
221 #undef LICENSE_3
222 #undef LICENSE_2
223 #undef LICENSE_1
225 static void print_version()
227 static const char version[] =
228 #ifdef VN
230 #else
231 PACKAGE_VERSION
232 #endif
233 " for " CANONICAL_TARGET;
234 printf("quvi %s\n libquvi %s\n libquvi-scripts %s\n",
235 version, quvi_version(QUVI_VERSION_LONG),
236 quvi_version(QUVI_VERSION_SCRIPTS));
237 exit(0);
240 static void dump_host(char *domain, char *formats)
242 printf("%s\t%s\n", domain, formats);
243 quvi_free(domain);
244 quvi_free(formats);
247 /* Wraps quvi_supported_ident. */
248 static void supported(quvi_t quvi)
250 quvi_ident_t ident;
251 unsigned int i;
252 char *formats;
253 QUVIcode rc;
255 rc = QUVI_NOSUPPORT;
257 for (i=0; i<opts->inputs_num; ++i)
259 rc = quvi_supported_ident(quvi, (char*)opts->inputs[i], &ident);
260 if (rc == QUVI_OK)
262 quvi_ident_getprop(ident,
263 QUVI_IDENT_PROPERTY_FORMATS, &formats);
264 spew("%10s : %s\n", formats, (char *)opts->inputs[i]);
265 quvi_supported_ident_close(&ident);
267 else
268 dump_error(quvi,rc);
271 exit(rc);
274 /* Query which formats are available for the URL */
275 static void query_formats(quvi_t quvi)
277 unsigned int i;
278 QUVIcode rc;
280 if (opts->inputs_num < 1)
282 spew_qe("error: no input URLs\n");
283 exit (QUVI_INVARG);
286 for (i=0,rc=0; i<opts->inputs_num; ++i)
288 char *formats = NULL;
290 rc = quvi_query_formats(quvi, (char*)opts->inputs[i], &formats);
291 if (rc == QUVI_OK)
293 spew("%10s : %s\n", formats, opts->inputs[i]);
294 quvi_free(formats);
296 else
297 dump_error(quvi,rc);
300 exit(rc);
303 /* dumps all supported hosts to stdout. */
304 static void support(quvi_t quvi)
306 int done = 0;
308 if (opts->inputs_num >0)
309 supported(quvi);
311 while (done == 0)
313 char *domain, *formats;
314 QUVIcode rc;
316 rc = quvi_next_supported_website(quvi, &domain, &formats);
318 switch (rc)
320 case QUVI_OK:
321 dump_host(domain, formats);
322 break;
323 case QUVI_LAST:
324 done = 1;
325 break;
326 default:
327 spew_e("%s\n", quvi_strerror(quvi, rc));
328 break;
332 exit(0);
335 static void invoke_exec(quvi_media_t media)
337 char *cmd, *media_url, *q_media_url;
338 char *page_title, *q_page_title, *t;
339 int rc;
341 quvi_getprop(media, QUVIPROP_PAGETITLE, &page_title);
342 t = strdup(page_title);
343 t = strepl(t, "\"", "\\\""); /* Escape existing double quotation marks */
344 asprintf(&q_page_title, "\"%s\"", t); /* Put inside quotation marks */
345 _free(t);
347 quvi_getprop(media, QUVIPROP_MEDIAURL, &media_url);
348 asprintf(&q_media_url, "\"%s\"", media_url);
350 cmd = strdup(opts->exec_arg);
351 cmd = strepl(cmd, "%t", q_page_title);
352 cmd = strepl(cmd, "%u", q_media_url);
354 _free(q_page_title);
355 _free(q_media_url);
357 rc = system(cmd);
359 switch (rc)
361 case 0:
362 break;
363 case -1:
364 spew_e("error: failed to execute `%s'\n", cmd);
365 break;
366 default:
367 spew_e("error: child exited with: %d\n", rc >> 8);
368 break;
371 _free(cmd);
374 struct parsed_url_s
376 char *media_url;
377 char *content_type;
378 double content_length;
379 char *file_suffix;
382 typedef struct parsed_url_s *parsed_url_t;
384 static void dump_media_url_xml(parsed_url_t p, int i)
386 char *media_url = curl_easy_escape(curl, p->media_url, 0);
388 spew(" <link id=\"%d\">\n", i);
390 if (p->content_length >0)
391 spew(" <length_bytes>%.0f</length_bytes>\n", p->content_length);
393 if (strlen(p->content_type) >0)
394 spew(" <content_type>%s</content_type>\n", p->content_type);
396 if (strlen(p->file_suffix) >0)
397 spew(" <file_suffix>%s</file_suffix>\n", p->file_suffix);
399 spew(" <url>%s</url>\n"
400 " </link>\n",
401 (media_url != NULL)
402 ? media_url
403 : p->media_url);
405 _free(media_url);
408 static void dump_media_url_json(parsed_url_t p, int i, int prepend_newln)
410 if (prepend_newln != 0)
411 spew(",\n");
413 spew(" {\n"
414 " \"id\": \"%d\",\n", i);
416 if (p->content_length >0)
417 spew(" \"length_bytes\": \"%.0f\",\n", p->content_length);
419 if (strlen(p->content_type) >0)
420 spew(" \"content_type\": \"%s\",\n", p->content_type);
422 if (strlen(p->file_suffix) >0)
423 spew(" \"file_suffix\": \"%s\",\n", p->file_suffix);
425 spew(" \"url\": \"%s\"\n"
426 " }",
427 p->media_url);
430 static void dump_media_urls(quvi_media_t media)
432 int json_flag=0, i=1;
435 struct parsed_url_s p;
437 memset(&p, 0, sizeof(&p));
438 quvi_getprop(media, QUVIPROP_MEDIAURL, &p.media_url);
439 quvi_getprop(media, QUVIPROP_MEDIACONTENTTYPE, &p.content_type);
440 quvi_getprop(media, QUVIPROP_MEDIACONTENTLENGTH, &p.content_length);
441 quvi_getprop(media, QUVIPROP_FILESUFFIX, &p.file_suffix);
443 if (opts->xml_given == 1)
444 dump_media_url_xml(&p,i);
445 else
447 dump_media_url_json(&p, i, (int)(i>1));
448 json_flag = 1;
450 ++i;
452 while (quvi_next_media_url(media) == QUVI_OK);
454 if (json_flag == 1)
455 spew("\n");
458 struct parsed_s
460 char *page_url;
461 char *page_title;
462 char *media_id;
463 char *format;
464 char *host;
465 char *start_time;
466 char *thumb_url;
467 double duration;
470 typedef struct parsed_s *parsed_t;
472 static void dump_media_xml(parsed_t p)
474 char *e_page_url, *e_thumb_url;
476 e_page_url = curl_easy_escape(curl, p->page_url, 0);
477 e_thumb_url = curl_easy_escape(curl, p->thumb_url, 0);
479 spew("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
480 "<media id=\"%s\" host=\"%s\">\n"
481 " <format_requested>%s</format_requested>\n"
482 " <page_title>%s</page_title>\n"
483 " <page_url>%s</page_url>\n",
484 p->media_id,
485 p->host,
486 p->format,
487 p->page_title,
488 e_page_url ? e_page_url : "");
490 if (strlen(p->start_time) >0)
491 spew(" <start_time>%s</start_time>\n", p->start_time);
493 if (e_thumb_url != NULL && strlen(e_thumb_url) >0)
494 spew(" <thumbnail_url>%s</thumbnail_url>\n", e_thumb_url);
496 if (p->duration >0)
497 spew(" <duration>%.0f</duration>\n", p->duration);
499 if (e_page_url != NULL)
501 curl_free(e_page_url);
502 e_page_url = NULL;
505 if (e_thumb_url != NULL)
507 curl_free(e_thumb_url);
508 e_thumb_url = NULL;
512 static void dump_media_json(parsed_t p)
514 char *t;
516 t = strdup(p->page_title);
517 t = strepl(t, "\"", "\\\"");
519 spew("{\n"
520 " \"host\": \"%s\",\n"
521 " \"page_title\": \"%s\",\n"
522 " \"page_url\": \"%s\",\n"
523 " \"id\": \"%s\",\n"
524 " \"format_requested\": \"%s\",\n",
525 p->host,
527 p->page_url,
528 p->media_id,
529 p->format);
531 if (strlen(p->start_time) >0)
532 spew(" \"start_time\": \"%s\",\n", p->start_time);
534 if (strlen(p->thumb_url) >0)
535 spew(" \"thumbnail_url\": \"%s\",\n", p->thumb_url);
537 if (p->duration >0)
538 spew(" \"duration\": \"%.0f\",\n", p->duration);
540 spew(" \"link\": [\n");
542 _free(t);
545 static void dump_media(quvi_media_t media)
547 struct parsed_s p;
549 memset(&p, 0, sizeof(p));
551 quvi_getprop(media, QUVIPROP_HOSTID, &p.host);
552 quvi_getprop(media, QUVIPROP_PAGEURL, &p.page_url);
553 quvi_getprop(media, QUVIPROP_PAGETITLE, &p.page_title);
554 quvi_getprop(media, QUVIPROP_MEDIAID, &p.media_id);
555 quvi_getprop(media, QUVIPROP_FORMAT, &p.format);
556 quvi_getprop(media, QUVIPROP_STARTTIME, &p.start_time);
557 quvi_getprop(media, QUVIPROP_MEDIATHUMBNAILURL, &p.thumb_url);
558 quvi_getprop(media, QUVIPROP_MEDIADURATION, &p.duration);
560 if (opts->xml_given == 1)
561 dump_media_xml(&p);
562 else
563 dump_media_json(&p);
565 dump_media_urls(media);
567 if (opts->xml_given == 1)
568 spew("</media>\n");
569 else
570 spew(" ]\n}\n");
573 static void dump_category_help()
575 fprintf(stderr,
576 "Usage:\n"
577 " quvi --category <value[,value,...]>\n"
578 "Where the values may be:\n"
579 " http, rtmp, rtsp, mms, all\n"
580 "Examples:\n"
581 " quvi --category all ;# program default\n"
582 " quvi --category rtmp,mms ;# multiple categories\n");
583 exit (0);
586 static void check_category(const char *s, QUVIcategory *n)
588 if (strlen(s) == 0)
589 return;
591 if (strcmp(s, "all") == 0)
592 *n = QUVIPROTO_ALL;
593 else if (strcmp(s, "help") == 0)
594 dump_category_help();
595 else
597 if (strcmp(s, "http") == 0)
598 *n |= QUVIPROTO_HTTP;
599 else if (strcmp(s, "rtmp") == 0)
600 *n |= QUVIPROTO_RTMP;
601 else if (strcmp(s, "rtsp") == 0)
602 *n |= QUVIPROTO_RTSP;
603 else if (strcmp(s, "mms") == 0)
604 *n |= QUVIPROTO_MMS;
605 else
607 fprintf(stderr, "warning: --category: %s: invalid value, "
608 "try \"help\"\n", s);
613 static QUVIcategory parse_categories(char *s)
615 QUVIcategory n=0;
616 char b[4], *p=s;
617 size_t i;
619 while (*p != '\0')
621 for (i=0; i<sizeof(b) && *p!='\0'; ++i,++p)
623 if (*p == ',')
625 ++p;
626 break;
628 b[i] = *p;
630 b[i] = '\0';
631 check_category(b, &n);
634 return (n);
637 static void depr_category(const char *o)
639 fprintf(stderr, "warning: %s: deprecated, use --category instead\n", o);
642 static void init_quvi()
644 QUVIcategory category;
645 QUVIcode rc;
647 if ((rc = quvi_init(&quvi)) != QUVI_OK)
649 dump_error(quvi, rc);
650 exit(rc);
652 assert(quvi != NULL);
654 /* Set quvi options. */
656 if (opts->format_given == 1)
657 quvi_setopt(quvi, QUVIOPT_FORMAT, opts->format_arg);
659 quvi_setopt(quvi, QUVIOPT_NORESOLVE, opts->no_resolve_given);
660 quvi_setopt(quvi, QUVIOPT_NOVERIFY, opts->no_verify_given);
662 /* Category. */
664 category = 0;
666 if (opts->category_given == 1)
667 category = parse_categories(opts->category_arg);
669 /* Category: deprecated. To be removed in later versions. */
671 if (opts->category_http_given == 1)
673 depr_category("--category-http");
674 category |= QUVIPROTO_HTTP;
676 if (opts->category_rtmp_given == 1)
678 depr_category("--category-rtmp");
679 category |= QUVIPROTO_RTMP;
681 if (opts->category_rtsp_given == 1)
683 depr_category("--category-rtsp");
684 category |= QUVIPROTO_RTSP;
686 if (opts->category_mms_given == 1)
688 depr_category("--category-mms");
689 category |= QUVIPROTO_MMS;
691 if (opts->category_all_given == 1)
692 depr_category("--category-all");
694 if (category == 0)
695 category = QUVIPROTO_ALL;
697 quvi_setopt(quvi, QUVIOPT_CATEGORY, category);
699 /* Status callback */
701 quvi_setopt(quvi, QUVIOPT_STATUSFUNCTION, status_callback);
703 /* Use the quvi created cURL handle. */
705 quvi_getinfo(quvi, QUVIINFO_CURL, &curl);
706 assert(curl != NULL);
708 if (opts->agent_given == 1)
709 curl_easy_setopt(curl, CURLOPT_USERAGENT, opts->agent_arg);
711 if (opts->proxy_given == 1)
712 curl_easy_setopt(curl, CURLOPT_PROXY, opts->proxy_arg);
714 if (opts->no_proxy_given == 1)
715 curl_easy_setopt(curl, CURLOPT_PROXY, "");
717 curl_easy_setopt(curl, CURLOPT_VERBOSE, opts->verbose_libcurl_given);
718 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, opts->connect_timeout_arg);
721 static void cleanup()
723 if (inputs)
724 quvi_llst_free(&inputs);
725 assert(inputs == NULL);
727 if (quvi)
728 quvi_close(&quvi);
729 assert(quvi == NULL);
731 if (opts)
733 cmdline_parser_free(opts);
734 _free(opts);
736 assert(opts == NULL);
739 static void read_from(FILE *f)
741 char b[256];
743 if (!f)
744 return;
746 while (fgets(b, (int)sizeof(b), f))
748 if (strlen(b) > 16)
750 const size_t n = strlen(b)-1;
752 if (b[n] == '\n')
753 b[n] = '\0';
755 quvi_llst_append(&inputs, strdup(b));
760 /*@null@*/ static char *parse_url_scheme(const char *url)
762 char *p, *r;
764 p = strstr(url, ":/");
765 if (!p)
766 return (NULL);
768 asprintf(&r, "%.*s", (int)(p - url), url);
770 return (r);
773 static int is_url(const char *s)
775 char *p = parse_url_scheme(s);
776 if (p)
778 _free(p);
779 return (1);
781 return (0);
784 static void read_file(const char *path)
786 FILE *f = fopen(path, "rt");
787 if (f == NULL)
788 #ifdef HAVE_STRERROR
789 spew_e("error: %s: %s\n", path, strerror(errno));
790 #else
791 perror("fopen");
792 #endif
793 else
795 read_from(f);
796 fclose(f);
797 f = NULL;
801 static unsigned int read_input()
803 if (opts->inputs_num == 0)
804 read_from(stdin);
805 else
807 unsigned int i;
808 for (i=0; i<opts->inputs_num; ++i)
810 if (is_url(opts->inputs[i]) == 0)
811 read_file(opts->inputs[i]);
812 else /* Must be an URL. */
813 quvi_llst_append(&inputs, strdup(opts->inputs[i]));
816 return ((unsigned int)quvi_llst_size(inputs));
819 int main(int argc, char *argv[])
821 const char *home, *no_config, *fname;
822 QUVIcode rc, last_failure;
823 unsigned int inputs_num;
824 quvi_llst_node_t curr;
825 quvi_media_t media;
826 int no_config_flag;
827 int i, errors;
829 assert(quvi == NULL);
830 assert(curl == NULL);
831 assert(opts == NULL);
832 assert(inputs == NULL);
834 no_config = getenv("QUVI_NO_CONFIG");
835 no_config_flag = 1;
837 home = getenv("QUVI_HOME");
838 if (!home)
839 home = getenv("HOME");
841 #ifndef HOST_W32
842 fname = "/.quvirc";
843 #else
844 fname = "\\quvirc";
845 #endif
847 atexit(cleanup);
849 opts = calloc(1, sizeof(struct gengetopt_args_info));
850 if (!opts)
851 return(QUVI_MEM);
853 /* Init cmdline parser. */
855 if (home != NULL && no_config == 0)
857 char *path;
858 FILE *f;
860 asprintf(&path, "%s%s", home, fname);
861 f = fopen(path, "r");
863 if (f != NULL)
865 struct cmdline_parser_params *pp;
867 fclose(f);
868 f = NULL;
870 pp = cmdline_parser_params_create();
871 pp->check_required = 0;
873 if (cmdline_parser_config_file(path, opts, pp) == 0)
875 pp->initialize = 0;
876 pp->override = 1;
877 pp->check_required = 1;
879 if (cmdline_parser_ext(argc, argv, opts, pp) == 0)
880 no_config_flag = 0;
882 _free(pp);
884 _free(path);
887 if (no_config_flag == 1)
889 if (cmdline_parser(argc, argv, opts) != 0)
890 return (QUVI_INVARG);
893 if (opts->version_given == 1)
894 print_version(opts);
896 if (opts->license_given == 1)
897 license(opts);
899 verbose_flag = (int)(opts->quiet_given == 0);
900 init_quvi();
902 if (opts->query_formats_given == 1)
903 query_formats(quvi);
905 if (opts->support_given == 1)
906 support(quvi);
908 /* User input */
910 inputs_num = read_input();
912 if (inputs_num == 0)
914 spew_qe("error: no input URLs\n");
915 return (QUVI_INVARG);
918 last_failure = QUVI_OK;
919 errors = 0;
921 for (i=0, curr=inputs; curr; ++i)
923 char *url = quvi_llst_data(curr);
924 rc = quvi_parse(quvi, url, &media);
925 if (rc == QUVI_OK)
927 assert(media != NULL);
928 dump_media(media);
930 if (opts->exec_given == 1)
934 invoke_exec(media);
936 while (quvi_next_media_url(media) == QUVI_OK);
939 else
941 dump_error(quvi,rc);
942 last_failure = rc;
943 ++errors;
945 quvi_parse_close(&media);
946 assert(media == NULL);
947 curr = quvi_llst_next(curr);
950 if (inputs_num > 1)
952 spew_qe("Results: %d OK, %d failed (last 0x%02x), exit with 0x%02x\n",
953 inputs_num - errors, errors, last_failure, rc);
956 return (rc);
959 /* vim: set ts=2 sw=2 tw=72 expandtab: */