Add 'status' parameter to spawn() and close stdin
[cmus.git] / cmus / input.c
blob6523fd5a6d56b00aba673fee4b7dff20f5677959
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include <input.h>
21 #include <ip.h>
22 #include <pcm.h>
23 #include <http.h>
24 #include <xmalloc.h>
25 #include <file.h>
26 #include <utils.h>
27 #include <symbol.h>
28 #include <pls.h>
29 #include <list.h>
30 #include <debug.h>
31 #include <config.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <stdarg.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <dirent.h>
41 #include <dlfcn.h>
43 struct input_plugin {
44 const struct input_plugin_ops *ops;
45 struct input_plugin_data data;
46 unsigned int open : 1;
47 unsigned int eof : 1;
48 int http_code;
49 char *http_reason;
52 * pcm is converted to 16-bit signed little-endian stereo
53 * NOTE: no conversion is done if channels > 2 or bits > 16
55 void (*pcm_convert)(char *, const char *, int);
56 void (*pcm_convert_in_place)(char *, int);
58 * 4 if 8-bit mono
59 * 2 if 8-bit stereo or 16-bit mono
60 * 1 otherwise
62 int pcm_convert_scale;
65 struct ip {
66 struct list_head node;
67 char *name;
68 void *handle;
70 const char * const *extensions;
71 const char * const *mime_types;
72 const struct input_plugin_ops *ops;
75 static const char * const plugin_dir = LIBDIR "/" PACKAGE "/ip";
76 static LIST_HEAD(ip_head);
78 /* timeouts (ms) */
79 static int http_connection_timeout = 5e3;
80 static int http_read_timeout = 5e3;
82 static const char *get_extension(const char *filename)
84 const char *ext;
86 ext = filename + strlen(filename) - 1;
87 while (ext >= filename && *ext != '/') {
88 if (*ext == '.') {
89 ext++;
90 return ext;
92 ext--;
94 return NULL;
97 static const struct input_plugin_ops *get_ops_by_filename(const char *filename)
99 struct ip *ip;
100 const char *ext;
102 ext = get_extension(filename);
103 if (ext == NULL)
104 return NULL;
105 list_for_each_entry(ip, &ip_head, node) {
106 const char * const *exts = ip->extensions;
107 int i;
109 for (i = 0; exts[i]; i++) {
110 if (strcasecmp(ext, exts[i]) == 0)
111 return ip->ops;
114 return NULL;
117 static const struct input_plugin_ops *get_ops_by_mime_type(const char *mime_type)
119 struct ip *ip;
121 list_for_each_entry(ip, &ip_head, node) {
122 const char * const *types = ip->mime_types;
123 int i;
125 for (i = 0; types[i]; i++) {
126 if (strcasecmp(mime_type, types[i]) == 0)
127 return ip->ops;
130 return NULL;
133 static int do_http_get(const char *uri, struct http_header **headersp, int *codep, char **reasonp)
135 char *user, *pass, *host, *path, *reason;
136 int port, sock, i, rc, code;
137 struct http_header *h;
139 *headersp = NULL;
140 *codep = -1;
141 *reasonp = NULL;
143 if (http_parse_uri(uri, &user, &pass, &host, &port, &path))
144 return -IP_ERROR_INVALID_URI;
146 /* d_print("%s -> '%s':'%s'@'%s':%d'%s'\n", uri, user, pass, host, port, path); */
148 sock = http_open(host, port, http_connection_timeout);
149 if (sock == -1) {
150 free(user);
151 free(pass);
152 free(host);
153 free(path);
154 return -IP_ERROR_ERRNO;
157 h = xnew(struct http_header, 5);
158 i = 0;
159 h[i].key = xstrdup("Host");
160 h[i].val = xstrdup(host);
161 i++;
162 h[i].key = xstrdup("User-Agent");
163 h[i].val = xstrdup(PACKAGE "/" VERSION);
164 i++;
165 h[i].key = xstrdup("Icy-MetaData");
166 h[i].val = xstrdup("1");
167 i++;
168 if (user && pass) {
169 char buf[256];
170 char *encoded;
172 snprintf(buf, sizeof(buf), "%s:%s", user, pass);
173 encoded = base64_encode(buf);
174 if (encoded == NULL) {
175 d_print("couldn't base64 encode '%s'\n", buf);
176 } else {
177 snprintf(buf, sizeof(buf), "Basic %s", encoded);
178 free(encoded);
179 h[i].key = xstrdup("Authorization");
180 h[i].val = xstrdup(buf);
181 i++;
184 h[i].key = NULL;
185 h[i].val = NULL;
186 i++;
188 rc = http_get(sock, path, h, &code, &reason, headersp, http_read_timeout);
189 http_headers_free(h);
190 free(user);
191 free(pass);
192 free(host);
193 free(path);
194 switch (rc) {
195 case -1:
196 d_print("error: %s\n", strerror(errno));
197 close(sock);
198 return -IP_ERROR_ERRNO;
199 case -2:
200 d_print("error parsing HTTP response\n");
201 close(sock);
202 return -IP_ERROR_HTTP_RESPONSE;
204 d_print("HTTP response: %d %s\n", code, reason);
205 if (code != 200) {
206 *codep = code;
207 *reasonp = reason;
208 close(sock);
209 return -IP_ERROR_HTTP_STATUS;
211 free(reason);
212 return sock;
215 static int setup_remote(struct input_plugin *ip, const struct http_header *headers, int sock)
217 const char *val;
219 val = http_headers_get_value(headers, "Content-Type");
220 if (val) {
221 ip->ops = get_ops_by_mime_type(val);
222 if (ip->ops == NULL) {
223 d_print("unsupported content type: %s\n", val);
224 close(sock);
225 return -IP_ERROR_FILE_FORMAT;
227 } else {
228 const char *type = "audio/mpeg";
230 d_print("assuming %s content type\n", type);
231 ip->ops = get_ops_by_mime_type(type);
232 if (ip->ops == NULL) {
233 d_print("unsupported content type: %s\n", type);
234 close(sock);
235 return -IP_ERROR_FILE_FORMAT;
239 ip->data.fd = sock;
240 ip->data.metadata = (char *)xmalloc(16 * 255 + 1);
242 val = http_headers_get_value(headers, "icy-metaint");
243 if (val) {
244 long int lint;
246 if (str_to_int(val, &lint) == 0 && lint >= 0) {
247 ip->data.metaint = lint;
248 d_print("metaint: %d\n", ip->data.metaint);
251 return 0;
254 static void dump_lines(char **lines)
256 int i;
258 for (i = 0; lines[i]; i++)
259 d_print("%d='%s'\n", i, lines[i]);
262 static int read_pls(struct input_plugin *ip, int sock)
264 struct http_header *headers;
265 char *body, *reason;
266 char **lines;
267 int rc, code;
269 rc = http_read_body(sock, &body, http_read_timeout);
270 close(sock);
271 if (rc)
272 return -IP_ERROR_ERRNO;
274 lines = pls_get_files(body);
275 free(body);
277 if (lines == NULL) {
278 d_print("error parsing playlist\n");
279 return -IP_ERROR_HTTP_RESPONSE;
281 dump_lines(lines);
282 if (lines[0] == NULL) {
283 free(lines);
284 d_print("empty playlist\n");
285 return -IP_ERROR_HTTP_RESPONSE;
288 sock = do_http_get(lines[0], &headers, &code, &reason);
289 free_str_array(lines);
290 if (sock < 0) {
291 ip->http_code = code;
292 ip->http_reason = reason;
293 return sock;
296 rc = setup_remote(ip, headers, sock);
297 http_headers_free(headers);
298 return rc;
301 static int read_m3u(struct input_plugin *ip, int sock)
303 struct http_header *headers;
304 char *body, *reason;
305 char **lines;
306 int rc, code, i;
308 rc = http_read_body(sock, &body, http_read_timeout);
309 close(sock);
310 if (rc)
311 return -IP_ERROR_ERRNO;
313 lines = bsplit(body, strlen(body), '\n', 0);
314 free(body);
316 for (i = 0; lines[i]; i++) {
317 char *ptr = strchr(lines[i], '\r');
319 if (ptr)
320 *ptr = 0;
322 if (i > 0 && lines[i - 1][0] == 0) {
323 free(lines[i - 1]);
324 lines[i - 1] = NULL;
326 dump_lines(lines);
328 if (lines[0] == NULL) {
329 free(lines);
330 d_print("empty playlist\n");
331 return -IP_ERROR_HTTP_RESPONSE;
334 sock = do_http_get(lines[0], &headers, &code, &reason);
335 free_str_array(lines);
336 if (sock < 0) {
337 ip->http_code = code;
338 ip->http_reason = reason;
340 if (sock == -IP_ERROR_INVALID_URI)
341 sock = -IP_ERROR_HTTP_RESPONSE;
342 return sock;
345 rc = setup_remote(ip, headers, sock);
346 http_headers_free(headers);
347 return rc;
350 static int open_remote(struct input_plugin *ip)
352 struct input_plugin_data *d = &ip->data;
353 char *reason;
354 int sock, rc, code;
355 struct http_header *headers;
356 const char *val;
358 sock = do_http_get(d->filename, &headers, &code, &reason);
359 if (sock < 0) {
360 ip->http_code = code;
361 ip->http_reason = reason;
362 return sock;
365 val = http_headers_get_value(headers, "Content-Type");
366 if (val) {
367 d_print("Content-Type: %s\n", val);
368 if (strcasecmp(val, "audio/x-scpls") == 0) {
369 http_headers_free(headers);
370 return read_pls(ip, sock);
371 } else if (strcasecmp(val, "audio/m3u") == 0) {
372 http_headers_free(headers);
373 return read_m3u(ip, sock);
377 rc = setup_remote(ip, headers, sock);
378 http_headers_free(headers);
379 return rc;
382 static int open_file(struct input_plugin *ip)
384 ip->ops = get_ops_by_filename(ip->data.filename);
385 if (ip->ops == NULL)
386 return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
387 ip->data.fd = open(ip->data.filename, O_RDONLY);
388 if (ip->data.fd == -1) {
389 ip->ops = NULL;
390 return -IP_ERROR_ERRNO;
392 return 0;
395 void ip_init_plugins(void)
397 DIR *dir;
398 struct dirent *d;
400 dir = opendir(plugin_dir);
401 if (dir == NULL) {
402 fprintf(stderr, "couldn't open directory `%s': %s\n", plugin_dir, strerror(errno));
403 return;
405 while ((d = readdir(dir)) != NULL) {
406 char filename[256];
407 struct ip *ip;
408 void *so;
409 char *ext;
411 if (d->d_name[0] == '.')
412 continue;
413 ext = strrchr(d->d_name, '.');
414 if (ext == NULL)
415 continue;
416 if (strcmp(ext, ".so"))
417 continue;
419 snprintf(filename, sizeof(filename), "%s/%s", plugin_dir, d->d_name);
421 so = dlopen(filename, RTLD_NOW);
422 if (so == NULL) {
423 fprintf(stderr, "%s\n", dlerror());
424 continue;
427 ip = xnew(struct ip, 1);
429 if (!get_symbol(so, "ip_extensions", filename, (void **)&ip->extensions, 0)) {
430 free(ip);
431 dlclose(so);
432 continue;
435 if (!get_symbol(so, "ip_mime_types", filename, (void **)&ip->mime_types, 0)) {
436 free(ip);
437 dlclose(so);
438 continue;
441 if (!get_symbol(so, "ip_ops", filename, (void **)&ip->ops, 0)) {
442 free(ip);
443 dlclose(so);
444 continue;
447 ip->name = xstrndup(d->d_name, ext - d->d_name);
448 ip->handle = so;
450 list_add_tail(&ip->node, &ip_head);
452 closedir(dir);
455 struct input_plugin *ip_new(const char *filename)
457 struct input_plugin *ip = xnew(struct input_plugin, 1);
459 ip->ops = NULL;
460 ip->open = 0;
461 ip->eof = 0;
462 ip->http_code = -1;
463 ip->http_reason = NULL;
465 ip->data.filename = xstrdup(filename);
466 ip->data.fd = -1;
468 ip->data.remote = is_url(filename);
469 ip->data.metadata_changed = 0;
470 ip->data.counter = 0;
471 ip->data.metaint = 0;
472 ip->data.metadata = NULL;
474 ip->data.private = NULL;
475 return ip;
478 void ip_delete(struct input_plugin *ip)
480 if (ip->open)
481 ip_close(ip);
482 free(ip->data.filename);
483 free(ip);
486 int ip_open(struct input_plugin *ip)
488 int rc, bits, is_signed, channels;
490 BUG_ON(ip->open);
491 BUG_ON(ip->eof);
492 BUG_ON(ip->ops);
493 BUG_ON(ip->data.filename == NULL);
494 BUG_ON(ip->data.fd != -1);
496 /* set fd and ops */
497 if (ip->data.remote) {
498 rc = open_remote(ip);
499 } else {
500 rc = open_file(ip);
503 if (rc) {
504 d_print("opening `%s' failed: %d %s\n", ip->data.filename, rc, rc == -1 ? strerror(errno) : "");
505 return rc;
508 BUG_ON(ip->data.fd == -1);
509 BUG_ON(ip->ops == NULL);
511 BUG_ON(ip->ops->open == NULL);
512 BUG_ON(ip->ops->close == NULL);
513 BUG_ON(ip->ops->read == NULL);
514 BUG_ON(ip->ops->seek == NULL);
515 BUG_ON(ip->ops->read_comments == NULL);
516 BUG_ON(ip->ops->duration == NULL);
518 rc = ip->ops->open(&ip->data);
519 if (rc) {
520 d_print("opening file `%s' failed: %d %s\n", ip->data.filename, rc, rc == -1 ? strerror(errno) : "");
521 if (ip->data.fd != -1)
522 close(ip->data.fd);
523 ip->data.fd = -1;
524 ip->ops = NULL;
525 free(ip->data.metadata);
526 ip->data.metadata = NULL;
527 return rc;
530 ip->pcm_convert_scale = 1;
531 ip->pcm_convert = NULL;
532 ip->pcm_convert_in_place = NULL;
533 bits = sf_get_bits(ip->data.sf);
534 is_signed = sf_get_signed(ip->data.sf);
535 channels = sf_get_channels(ip->data.sf);
536 if (bits == 8) {
537 if (channels == 1) {
538 ip->pcm_convert_scale = 4;
539 if (is_signed) {
540 ip->pcm_convert = convert_s8_1ch_to_s16_2ch;
541 } else {
542 ip->pcm_convert = convert_u8_1ch_to_s16_2ch;
544 } else if (channels == 2) {
545 ip->pcm_convert_scale = 2;
546 if (is_signed) {
547 ip->pcm_convert = convert_s8_2ch_to_s16_2ch;
548 } else {
549 ip->pcm_convert = convert_u8_2ch_to_s16_2ch;
552 } else if (bits == 16) {
553 if (channels == 1) {
554 ip->pcm_convert_scale = 2;
555 ip->pcm_convert = convert_16_1ch_to_16_2ch;
557 if (channels <= 2) {
558 int bigendian = sf_get_bigendian(ip->data.sf);
560 if (is_signed) {
561 if (bigendian)
562 ip->pcm_convert_in_place = convert_s16_be_to_s16_le;
563 } else {
564 if (bigendian) {
565 ip->pcm_convert_in_place = convert_u16_be_to_s16_le;
566 } else {
567 ip->pcm_convert_in_place = convert_u16_le_to_s16_le;
572 d_print("pcm convert: scale=%d convert=%d convert_in_place=%d\n",
573 ip->pcm_convert_scale,
574 ip->pcm_convert != NULL,
575 ip->pcm_convert_in_place != NULL);
577 ip->open = 1;
578 return 0;
581 int ip_close(struct input_plugin *ip)
583 int rc;
585 BUG_ON(!ip->open);
587 rc = ip->ops->close(&ip->data);
588 BUG_ON(ip->data.private);
589 if (ip->data.fd != -1)
590 close(ip->data.fd);
591 free(ip->data.metadata);
592 free(ip->http_reason);
593 ip->data.fd = -1;
594 ip->data.metadata = NULL;
595 ip->http_reason = NULL;
596 ip->ops = NULL;
597 ip->open = 0;
598 ip->eof = 0;
600 ip->pcm_convert_scale = -1;
601 ip->pcm_convert = NULL;
602 ip->pcm_convert_in_place = NULL;
603 return rc;
606 int ip_read(struct input_plugin *ip, char *buffer, int count)
608 struct timeval tv;
609 fd_set readfds;
610 /* 4608 seems to be optimal for mp3s, 4096 for oggs */
611 char tmp[8 * 1024];
612 char *buf;
613 int rc;
615 BUG_ON(!ip->open);
616 BUG_ON(count <= 0);
618 FD_ZERO(&readfds);
619 FD_SET(ip->data.fd, &readfds);
620 /* zero timeout -> return immediately */
621 tv.tv_sec = 0;
622 tv.tv_usec = 50e3;
623 rc = select(ip->data.fd + 1, &readfds, NULL, NULL, &tv);
624 if (rc == -1) {
625 d_print("select: error: %s\n", strerror(errno));
626 if (errno == EINTR)
627 errno = EAGAIN;
628 return -1;
630 if (rc == 0) {
631 errno = EAGAIN;
632 return -1;
635 buf = buffer;
636 if (ip->pcm_convert_scale > 1) {
637 /* use tmp buffer for 16-bit mono and 8-bit */
638 buf = tmp;
639 count /= ip->pcm_convert_scale;
640 if (count > sizeof(tmp))
641 count = sizeof(tmp);
644 rc = ip->ops->read(&ip->data, buf, count);
645 if (rc == 0)
646 ip->eof = 1;
647 if (rc == -1)
648 d_print("error: %s\n", strerror(errno));
650 if (rc > 0) {
651 int sample_size = sf_get_sample_size(ip->data.sf);
653 if (ip->pcm_convert_in_place != NULL)
654 ip->pcm_convert_in_place(buf, rc / sample_size);
655 if (ip->pcm_convert != NULL)
656 ip->pcm_convert(buffer, tmp, rc / sample_size);
657 rc *= ip->pcm_convert_scale;
659 return rc;
662 int ip_seek(struct input_plugin *ip, double offset)
664 int rc;
666 BUG_ON(!ip->open);
668 if (ip->data.remote)
669 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
670 rc = ip->ops->seek(&ip->data, offset);
671 if (rc == 0)
672 ip->eof = 0;
673 return rc;
676 int ip_read_comments(struct input_plugin *ip, struct keyval **comments)
678 int rc;
680 BUG_ON(!ip->open);
682 rc = ip->ops->read_comments(&ip->data, comments);
683 return rc;
686 int ip_duration(struct input_plugin *ip)
688 int rc;
690 BUG_ON(!ip->open);
692 rc = ip->ops->duration(&ip->data);
693 return rc;
696 sample_format_t ip_get_sf(struct input_plugin *ip)
698 BUG_ON(!ip->open);
699 return ip->data.sf;
702 const char *ip_get_filename(struct input_plugin *ip)
704 return ip->data.filename;
707 const char *ip_get_metadata(struct input_plugin *ip)
709 BUG_ON(!ip->open);
710 return ip->data.metadata;
713 int ip_is_remote(struct input_plugin *ip)
715 return ip->data.remote;
718 int ip_metadata_changed(struct input_plugin *ip)
720 int ret = ip->data.metadata_changed;
722 BUG_ON(!ip->open);
723 ip->data.metadata_changed = 0;
724 return ret;
727 int ip_eof(struct input_plugin *ip)
729 BUG_ON(!ip->open);
730 return ip->eof;
733 char *ip_get_error_msg(struct input_plugin *ip, int rc, const char *arg)
735 char buffer[1024];
737 switch (-rc) {
738 case IP_ERROR_ERRNO:
739 snprintf(buffer, sizeof(buffer), "%s: %s", arg, strerror(errno));
740 break;
741 case IP_ERROR_UNRECOGNIZED_FILE_TYPE:
742 snprintf(buffer, sizeof(buffer),
743 "%s: unrecognized filename extension", arg);
744 break;
745 case IP_ERROR_FUNCTION_NOT_SUPPORTED:
746 snprintf(buffer, sizeof(buffer),
747 "%s: function not supported", arg);
748 break;
749 case IP_ERROR_FILE_FORMAT:
750 snprintf(buffer, sizeof(buffer),
751 "%s: file format not supported or corrupted file",
752 arg);
753 break;
754 case IP_ERROR_INVALID_URI:
755 snprintf(buffer, sizeof(buffer), "%s: invalid URI", arg);
756 break;
757 case IP_ERROR_SAMPLE_FORMAT:
758 snprintf(buffer, sizeof(buffer),
759 "%s: input plugin doesn't support the sample format",
760 arg);
761 break;
762 case IP_ERROR_HTTP_RESPONSE:
763 snprintf(buffer, sizeof(buffer), "%s: invalid HTTP response", arg);
764 break;
765 case IP_ERROR_HTTP_STATUS:
766 snprintf(buffer, sizeof(buffer), "%s: %d %s", arg, ip->http_code, ip->http_reason);
767 free(ip->http_reason);
768 ip->http_reason = NULL;
769 ip->http_code = -1;
770 break;
771 case IP_ERROR_INTERNAL:
772 snprintf(buffer, sizeof(buffer), "%s: internal error", arg);
773 break;
774 case IP_ERROR_SUCCESS:
775 default:
776 snprintf(buffer, sizeof(buffer),
777 "%s: this is not an error (%d), this is a bug",
778 arg, rc);
779 break;
781 return xstrdup(buffer);
784 static int strptrcmp(const void *a, const void *b)
786 const char *as = *(char **)a;
787 const char *bs = *(char **)b;
789 return strcmp(as, bs);
792 char **ip_get_supported_extensions(void)
794 struct ip *ip;
795 char **exts;
796 int i, size;
797 int count = 0;
799 size = 8;
800 exts = xnew(char *, size);
801 list_for_each_entry(ip, &ip_head, node) {
802 const char * const *e = ip->extensions;
804 for (i = 0; e[i]; i++) {
805 if (count == size - 1) {
806 size *= 2;
807 exts = xrenew(char *, exts, size);
809 exts[count++] = xstrdup(e[i]);
812 exts[count] = NULL;
813 qsort(exts, count, sizeof(char *), strptrcmp);
814 return exts;
817 void ip_dump_plugins(void)
819 struct ip *ip;
820 int i;
822 printf("Input Plugins: %s\n", plugin_dir);
823 list_for_each_entry(ip, &ip_head, node) {
824 printf(" %s:\n File Types:", ip->name);
825 for (i = 0; ip->extensions[i]; i++)
826 printf(" %s", ip->extensions[i]);
827 printf("\n MIME Types:");
828 for (i = 0; ip->mime_types[i]; i++)
829 printf(" %s", ip->mime_types[i]);
830 printf("\n");