6 #include "play_queue.h"
18 #include "ui_curses.h"
20 #include <sys/types.h>
28 static pthread_mutex_t track_db_mutex
= CMUS_MUTEX_INITIALIZER
;
29 static struct track_db
*track_db
;
30 static char **playable_exts
;
31 static const char * const playlist_exts
[] = { "m3u", "pl", "pls", NULL
};
33 #define track_db_lock() cmus_mutex_lock(&track_db_mutex)
34 #define track_db_unlock() cmus_mutex_unlock(&track_db_mutex)
36 /* add (worker job) {{{ */
44 static struct track_info
*track_info_url_new(const char *url
)
46 struct track_info
*ti
= track_info_new(url
);
47 ti
->comments
= xnew0(struct keyval
, 1);
53 static void add_url(add_ti_cb add
, const char *filename
)
55 struct track_info
*ti
;
57 ti
= track_info_url_new(filename
);
64 /* add file to the playlist
66 * @filename: absolute filename with extraneous slashes stripped
68 static void add_file(add_ti_cb add
, const char *filename
)
70 struct track_info
*ti
;
73 ti
= track_db_get_track(track_db
, filename
);
85 static int dir_entry_cmp(const void *ap
, const void *bp
)
87 struct dir_entry
*a
= *(struct dir_entry
**)ap
;
88 struct dir_entry
*b
= *(struct dir_entry
**)bp
;
90 return strcmp(a
->name
, b
->name
);
93 static int dir_entry_cmp_reverse(const void *ap
, const void *bp
)
95 struct dir_entry
*a
= *(struct dir_entry
**)ap
;
96 struct dir_entry
*b
= *(struct dir_entry
**)bp
;
98 return strcmp(b
->name
, a
->name
);
101 static int points_within(const char *target
, const char *root
)
103 int tlen
= strlen(target
);
104 int rlen
= strlen(root
);
108 if (strncmp(target
, root
, rlen
))
110 return target
[rlen
] == '/' || !target
[rlen
];
113 static void do_add_dir(add_ti_cb add
, const char *dirname
, const char *root
)
115 struct directory dir
;
116 struct dir_entry
**ents
;
121 if (dir_open(&dir
, dirname
)) {
122 d_print("error: opening %s: %s\n", dirname
, strerror(errno
));
125 while ((name
= dir_read(&dir
))) {
126 struct dir_entry
*ent
;
135 int rc
= readlink(dir
.path
, buf
, sizeof(buf
));
137 if (rc
< 0 || rc
== sizeof(buf
))
140 target
= path_absolute_cwd(buf
, dirname
);
141 if (points_within(target
, root
)) {
142 /* symlink points withing the root */
143 d_print("%s -> %s points within %s. ignoring\n",
144 dir
.path
, target
, root
);
151 size
= strlen(name
) + 1;
152 ent
= xmalloc(sizeof(struct dir_entry
) + size
);
153 ent
->mode
= dir
.st
.st_mode
;
154 memcpy(ent
->name
, name
, size
);
155 ptr_array_add(&array
, ent
);
159 if (add
== play_queue_prepend
) {
160 ptr_array_sort(&array
, dir_entry_cmp_reverse
);
162 ptr_array_sort(&array
, dir_entry_cmp
);
165 for (i
= 0; i
< array
.count
; i
++) {
166 if (!worker_cancelling()) {
167 /* abuse dir.path because
168 * - it already contains dirname + '/'
169 * - it is guaranteed to be large enough
171 int len
= strlen(ents
[i
]->name
);
173 memcpy(dir
.path
+ dir
.len
, ents
[i
]->name
, len
+ 1);
174 if (S_ISDIR(ents
[i
]->mode
)) {
175 do_add_dir(add
, dir
.path
, root
);
177 add_file(add
, dir
.path
);
185 static void add_dir(add_ti_cb add
, const char *dirname
)
187 do_add_dir(add
, dirname
, dirname
);
190 static int handle_line(void *data
, const char *line
)
192 add_ti_cb add
= data
;
194 if (worker_cancelling())
205 static void add_pl(add_ti_cb add
, const char *filename
)
210 buf
= mmap_file(filename
, &size
);
216 reverse
= add
== play_queue_prepend
;
218 cmus_playlist_for_each(buf
, size
, reverse
, handle_line
, add
);
223 static void do_add_job(void *data
)
225 struct add_data
*jd
= data
;
229 add_url(jd
->add
, jd
->name
);
232 add_pl(jd
->add
, jd
->name
);
235 add_dir(jd
->add
, jd
->name
);
238 add_file(jd
->add
, jd
->name
);
240 case FILE_TYPE_INVALID
:
245 static void free_add_job(void *data
)
247 struct add_data
*jd
= data
;
255 /* update (worker job) {{{ */
260 struct track_info
**ti
;
263 static void do_update_job(void *data
)
265 struct update_data
*d
= data
;
268 for (i
= 0; i
< d
->used
; i
++) {
269 struct track_info
*ti
= d
->ti
[i
];
272 /* stat follows symlinks, lstat does not */
273 if (stat(ti
->filename
, &s
) == -1) {
274 d_print("removing dead file %s\n", ti
->filename
);
278 } else if (ti
->mtime
!= s
.st_mtime
) {
279 d_print("mtime changed: %s\n", ti
->filename
);
284 cmus_add(lib_add_track
, ti
->filename
, FILE_TYPE_FILE
, JOB_TYPE_LIB
);
286 track_info_unref(ti
);
290 static void free_update_job(void *data
)
292 struct update_data
*d
= data
;
302 char *db_filename_base
;
304 playable_exts
= ip_get_supported_extensions();
306 db_filename_base
= xstrjoin(cmus_config_dir
, "/trackdb");
307 track_db
= track_db_new(db_filename_base
);
308 free(db_filename_base
);
318 worker_remove_jobs(JOB_TYPE_ANY
);
320 if (track_db_close(track_db
))
321 d_print("error: %s\n", strerror(errno
));
326 struct track_info
*info
;
329 info
= play_queue_remove();
332 info
= lib_set_next();
334 info
= pl_set_next();
340 player_set_file(info
);
345 struct track_info
*info
;
349 info
= lib_set_prev();
351 info
= pl_set_prev();
356 player_set_file(info
);
359 void cmus_play_file(const char *filename
)
361 struct track_info
*ti
;
363 if (is_url(filename
)) {
364 ti
= track_info_url_new(filename
);
367 ti
= track_db_get_track(track_db
, filename
);
370 error_msg("Couldn't get file information for %s\n", filename
);
374 player_play_file(ti
);
377 enum file_type
cmus_detect_ft(const char *name
, char **ret
)
383 *ret
= xstrdup(name
);
384 return FILE_TYPE_URL
;
388 absolute
= path_absolute(name
);
389 if (absolute
== NULL
)
390 return FILE_TYPE_INVALID
;
392 /* stat follows symlinks, lstat does not */
393 if (stat(absolute
, &st
) == -1) {
395 return FILE_TYPE_INVALID
;
398 if (S_ISDIR(st
.st_mode
)) {
400 return FILE_TYPE_DIR
;
402 if (!S_ISREG(st
.st_mode
)) {
405 return FILE_TYPE_INVALID
;
409 if (cmus_is_playlist(absolute
))
412 /* NOTE: it could be FILE_TYPE_PL too! */
413 return FILE_TYPE_FILE
;
416 void cmus_add(add_ti_cb add
, const char *name
, enum file_type ft
, int jt
)
418 struct add_data
*data
= xnew(struct add_data
, 1);
421 data
->name
= xstrdup(name
);
423 worker_add_job(jt
, do_add_job
, free_add_job
, data
);
426 static int save_playlist_cb(void *data
, struct track_info
*ti
)
428 int fd
= *(int *)data
;
429 const char nl
= '\n';
432 rc
= write_all(fd
, ti
->filename
, strlen(ti
->filename
));
435 rc
= write_all(fd
, &nl
, 1);
441 int cmus_save(for_each_ti_cb for_each_ti
, const char *filename
)
445 fd
= open(filename
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
448 rc
= for_each_ti(save_playlist_cb
, &fd
);
453 static int update_cb(void *data
, struct track_info
*ti
)
455 struct update_data
*d
= data
;
457 if (is_url(ti
->filename
))
460 if (d
->size
== d
->used
) {
464 d
->ti
= xrealloc(d
->ti
, d
->size
* sizeof(struct track_info
*));
467 d
->ti
[d
->used
++] = ti
;
471 void cmus_update_lib(void)
473 struct update_data
*data
;
475 data
= xnew(struct update_data
, 1);
481 lib_for_each(update_cb
, data
);
484 worker_add_job(JOB_TYPE_LIB
, do_update_job
, free_update_job
, data
);
487 void cmus_update_tis(struct track_info
**tis
, int nr
)
489 struct update_data
*data
;
491 data
= xnew(struct update_data
, 1);
495 worker_add_job(JOB_TYPE_LIB
, do_update_job
, free_update_job
, data
);
498 struct track_info
*cmus_get_track_info(const char *name
)
500 struct track_info
*ti
;
503 return track_info_url_new(name
);
506 ti
= track_db_get_track(track_db
, name
);
511 static const char *get_ext(const char *filename
)
513 const char *ext
= strrchr(filename
, '.');
520 static int str_in_array(const char *str
, const char * const * array
)
524 for (i
= 0; array
[i
]; i
++) {
525 if (strcasecmp(str
, array
[i
]) == 0)
531 int cmus_is_playlist(const char *filename
)
533 const char *ext
= get_ext(filename
);
535 return ext
&& str_in_array(ext
, playlist_exts
);
538 int cmus_is_playable(const char *filename
)
540 const char *ext
= get_ext(filename
);
542 return ext
&& str_in_array(ext
, (const char * const *)playable_exts
);
545 int cmus_is_supported(const char *filename
)
547 const char *ext
= get_ext(filename
);
549 return ext
&& (str_in_array(ext
, (const char * const *)playable_exts
) ||
550 str_in_array(ext
, playlist_exts
));
554 int (*cb
)(void *data
, const char *line
);
558 static int pl_handle_line(void *data
, const char *line
)
560 struct pl_data
*d
= data
;
563 while (isspace(line
[i
]))
571 return d
->cb(d
->data
, line
);
574 static int pls_handle_line(void *data
, const char *line
)
576 struct pl_data
*d
= data
;
578 if (strncasecmp(line
, "file", 4))
580 line
= strchr(line
, '=');
583 return d
->cb(d
->data
, line
+ 1);
586 int cmus_playlist_for_each(const char *buf
, int size
, int reverse
,
587 int (*cb
)(void *data
, const char *line
),
590 struct pl_data d
= { cb
, data
};
591 int (*handler
)(void *, const char *);
593 handler
= pl_handle_line
;
594 if (size
>= 10 && strncasecmp(buf
, "[playlist]", 10) == 0)
595 handler
= pls_handle_line
;
598 buffer_for_each_line_reverse(buf
, size
, handler
, &d
);
600 buffer_for_each_line(buf
, size
, handler
, &d
);