flac: Saner EOF handling
[cmus.git] / job.c
blobc16133a74eabf73b0b339ab15bafe3483f542544
1 #include "job.h"
2 #include "worker.h"
3 #include "cache.h"
4 #include "xmalloc.h"
5 #include "debug.h"
6 #include "load_dir.h"
7 #include "path.h"
8 #include "editable.h"
9 #include "play_queue.h"
10 #include "lib.h"
11 #include "utils.h"
12 #include "file.h"
13 #include "cache.h"
15 #include <string.h>
16 #include <unistd.h>
17 #include <errno.h>
19 static struct track_info *ti_buffer[32];
20 static int ti_buffer_fill;
21 static struct add_data *jd;
23 static void flush_ti_buffer(void)
25 int i;
27 editable_lock();
28 for (i = 0; i < ti_buffer_fill; i++) {
29 jd->add(ti_buffer[i]);
30 track_info_unref(ti_buffer[i]);
32 editable_unlock();
33 ti_buffer_fill = 0;
36 static void add_ti(struct track_info *ti)
38 if (ti_buffer_fill == sizeof(ti_buffer) / sizeof(ti_buffer[0]))
39 flush_ti_buffer();
40 ti_buffer[ti_buffer_fill++] = ti;
43 static void add_url(const char *filename)
45 add_ti(track_info_url_new(filename));
48 /* add file to the playlist
50 * @filename: absolute filename with extraneous slashes stripped
52 static void add_file(const char *filename)
54 struct track_info *ti;
56 cache_lock();
57 ti = cache_get_ti(filename);
58 cache_unlock();
60 if (ti)
61 add_ti(ti);
64 static int dir_entry_cmp(const void *ap, const void *bp)
66 struct dir_entry *a = *(struct dir_entry **)ap;
67 struct dir_entry *b = *(struct dir_entry **)bp;
69 return strcmp(a->name, b->name);
72 static int dir_entry_cmp_reverse(const void *ap, const void *bp)
74 struct dir_entry *a = *(struct dir_entry **)ap;
75 struct dir_entry *b = *(struct dir_entry **)bp;
77 return strcmp(b->name, a->name);
80 static int points_within(const char *target, const char *root)
82 int tlen = strlen(target);
83 int rlen = strlen(root);
85 if (rlen > tlen)
86 return 0;
87 if (strncmp(target, root, rlen))
88 return 0;
89 return target[rlen] == '/' || !target[rlen];
92 static void add_dir(const char *dirname, const char *root)
94 struct directory dir;
95 struct dir_entry **ents;
96 const char *name;
97 PTR_ARRAY(array);
98 int i;
100 if (dir_open(&dir, dirname)) {
101 d_print("error: opening %s: %s\n", dirname, strerror(errno));
102 return;
104 while ((name = dir_read(&dir))) {
105 struct dir_entry *ent;
106 int size;
108 if (name[0] == '.')
109 continue;
111 if (dir.is_link) {
112 char buf[1024];
113 char *target;
114 int rc = readlink(dir.path, buf, sizeof(buf));
116 if (rc < 0 || rc == sizeof(buf))
117 continue;
118 buf[rc] = 0;
119 target = path_absolute_cwd(buf, dirname);
120 if (points_within(target, root)) {
121 /* symlink points withing the root */
122 d_print("%s -> %s points within %s. ignoring\n",
123 dir.path, target, root);
124 free(target);
125 continue;
127 free(target);
130 size = strlen(name) + 1;
131 ent = xmalloc(sizeof(struct dir_entry) + size);
132 ent->mode = dir.st.st_mode;
133 memcpy(ent->name, name, size);
134 ptr_array_add(&array, ent);
136 dir_close(&dir);
138 if (jd->add == play_queue_prepend) {
139 ptr_array_sort(&array, dir_entry_cmp_reverse);
140 } else {
141 ptr_array_sort(&array, dir_entry_cmp);
143 ents = array.ptrs;
144 for (i = 0; i < array.count; i++) {
145 if (!worker_cancelling()) {
146 /* abuse dir.path because
147 * - it already contains dirname + '/'
148 * - it is guaranteed to be large enough
150 int len = strlen(ents[i]->name);
152 memcpy(dir.path + dir.len, ents[i]->name, len + 1);
153 if (S_ISDIR(ents[i]->mode)) {
154 add_dir(dir.path, root);
155 } else {
156 add_file(dir.path);
159 free(ents[i]);
161 free(ents);
164 static int handle_line(void *data, const char *line)
166 if (worker_cancelling())
167 return 1;
169 if (is_url(line)) {
170 add_url(line);
171 } else {
172 add_file(line);
174 return 0;
177 static void add_pl(const char *filename)
179 char *buf;
180 int size, reverse;
182 buf = mmap_file(filename, &size);
183 if (size == -1)
184 return;
186 if (buf) {
187 /* beautiful hack */
188 reverse = jd->add == play_queue_prepend;
190 cmus_playlist_for_each(buf, size, reverse, handle_line, NULL);
191 munmap(buf, size);
195 void do_add_job(void *data)
197 jd = data;
198 switch (jd->type) {
199 case FILE_TYPE_URL:
200 add_url(jd->name);
201 break;
202 case FILE_TYPE_PL:
203 add_pl(jd->name);
204 break;
205 case FILE_TYPE_DIR:
206 add_dir(jd->name, jd->name);
207 break;
208 case FILE_TYPE_FILE:
209 add_file(jd->name);
210 break;
211 case FILE_TYPE_INVALID:
212 break;
214 if (ti_buffer_fill)
215 flush_ti_buffer();
216 jd = NULL;
219 void free_add_job(void *data)
221 struct add_data *d = data;
222 free(d->name);
223 free(d);
226 void do_update_job(void *data)
228 struct update_data *d = data;
229 int i;
231 for (i = 0; i < d->used; i++) {
232 struct track_info *ti = d->ti[i];
233 struct stat s;
234 int rc;
236 /* stat follows symlinks, lstat does not */
237 rc = stat(ti->filename, &s);
238 if (rc || ti->mtime != s.st_mtime) {
239 editable_lock();
240 lib_remove(ti);
241 editable_unlock();
243 cache_lock();
244 cache_remove_ti(ti);
245 cache_unlock();
247 if (rc) {
248 d_print("removing dead file %s\n", ti->filename);
249 } else {
250 d_print("mtime changed: %s\n", ti->filename);
251 cmus_add(lib_add_track, ti->filename, FILE_TYPE_FILE, JOB_TYPE_LIB);
254 track_info_unref(ti);
258 void free_update_job(void *data)
260 struct update_data *d = data;
262 free(d->ti);
263 free(d);
266 void do_update_cache_job(void *data)
268 struct track_info **tis;
269 int i, count;
271 cache_lock();
272 tis = cache_refresh(&count);
273 editable_lock();
274 for (i = 0; i < count; i++) {
275 struct track_info *new, *old = tis[i];
277 if (!old)
278 continue;
280 new = old->next;
281 if (lib_remove(old) && new)
282 lib_add_track(new);
283 // FIXME: other views
285 track_info_unref(old);
286 if (new)
287 track_info_unref(new);
289 editable_unlock();
290 cache_unlock();
291 free(tis);
294 void free_update_cache_job(void *data)