Fix buffer overflow when completing variable value
[cmus.git] / lib.c
blob4206b46cd9f6f5d66e769d3d94146fb5ec6c08a7
1 /*
2 * Copyright 2004-2006 Timo Hirvonen
3 */
5 #include "lib.h"
6 #include "editable.h"
7 #include "options.h"
8 #include "xmalloc.h"
9 #include "debug.h"
11 #include <pthread.h>
12 #include <string.h>
14 struct editable lib_editable;
15 LIST_HEAD(lib_shuffle_head);
16 struct tree_track *lib_cur_track = NULL;
17 unsigned int play_sorted = 0;
18 enum aaa_mode aaa_mode = AAA_MODE_ALL;
20 static struct expr *filter = NULL;
21 static int remove_from_hash = 1;
23 static inline struct tree_track *to_sorted(const struct list_head *item)
25 return (struct tree_track *)container_of(item, struct simple_track, node);
28 static inline void sorted_track_to_iter(struct tree_track *track, struct iter *iter)
30 iter->data0 = &lib_editable.head;
31 iter->data1 = track;
32 iter->data2 = NULL;
35 static void all_wins_changed(void)
37 lib_tree_win->changed = 1;
38 lib_track_win->changed = 1;
39 lib_editable.win->changed = 1;
42 static void shuffle_add(struct tree_track *track)
44 shuffle_list_add_track(&lib_shuffle_head, &track->shuffle_track.node, lib_editable.nr_tracks);
47 static void views_add_track(struct track_info *ti)
49 struct tree_track *track = xnew(struct tree_track, 1);
51 /* NOTE: does not ref ti */
52 simple_track_init((struct simple_track *)track, ti);
54 /* both the hash table and views have refs */
55 track_info_ref(ti);
57 tree_add_track(track);
58 shuffle_add(track);
59 editable_add(&lib_editable, (struct simple_track *)track);
62 struct fh_entry {
63 struct fh_entry *next;
65 /* ref count is increased when added to this hash */
66 struct track_info *ti;
69 #define FH_SIZE (1024)
70 static struct fh_entry *ti_hash[FH_SIZE] = { NULL, };
72 /* this is from glib */
73 static unsigned int str_hash(const char *str)
75 unsigned int hash = 0;
76 int i;
78 for (i = 0; str[i]; i++)
79 hash = (hash << 5) - hash + str[i];
80 return hash;
83 static int hash_insert(struct track_info *ti)
85 const char *filename = ti->filename;
86 unsigned int pos = str_hash(filename) % FH_SIZE;
87 struct fh_entry **entryp;
88 struct fh_entry *e;
90 entryp = &ti_hash[pos];
91 e = *entryp;
92 while (e) {
93 if (strcmp(e->ti->filename, filename) == 0) {
94 /* found, don't insert */
95 return 0;
97 e = e->next;
100 e = xnew(struct fh_entry, 1);
101 track_info_ref(ti);
102 e->ti = ti;
103 e->next = *entryp;
104 *entryp = e;
105 return 1;
108 static void hash_remove(struct track_info *ti)
110 const char *filename = ti->filename;
111 unsigned int pos = str_hash(filename) % FH_SIZE;
112 struct fh_entry **entryp;
114 entryp = &ti_hash[pos];
115 while (1) {
116 struct fh_entry *e = *entryp;
118 BUG_ON(e == NULL);
119 if (strcmp(e->ti->filename, filename) == 0) {
120 *entryp = e->next;
121 track_info_unref(e->ti);
122 free(e);
123 break;
125 entryp = &e->next;
129 void lib_add_track(struct track_info *ti)
131 if (!hash_insert(ti)) {
132 /* duplicate files not allowed */
133 return;
135 if (filter == NULL || expr_eval(filter, ti))
136 views_add_track(ti);
139 static struct tree_track *album_first_track(const struct album *album)
141 return to_tree_track(album->track_head.next);
144 static struct tree_track *artist_first_track(const struct artist *artist)
146 return album_first_track(to_album(artist->album_head.next));
149 static struct tree_track *normal_get_first(void)
151 return artist_first_track(to_artist(lib_artist_head.next));
154 static struct tree_track *album_last_track(const struct album *album)
156 return to_tree_track(album->track_head.prev);
159 static struct tree_track *artist_last_track(const struct artist *artist)
161 return album_last_track(to_album(artist->album_head.prev));
164 static int aaa_mode_filter(const struct simple_track *track)
166 const struct album *album = ((struct tree_track *)track)->album;
168 if (aaa_mode == AAA_MODE_ALBUM)
169 return CUR_ALBUM == album;
171 if (aaa_mode == AAA_MODE_ARTIST)
172 return CUR_ARTIST == album->artist;
174 /* AAA_MODE_ALL */
175 return 1;
178 /* set next/prev (tree) {{{ */
180 static struct tree_track *normal_get_next(void)
182 if (lib_cur_track == NULL)
183 return normal_get_first();
185 /* not last track of the album? */
186 if (lib_cur_track->node.next != &CUR_ALBUM->track_head) {
187 /* next track of the current album */
188 return to_tree_track(lib_cur_track->node.next);
191 if (aaa_mode == AAA_MODE_ALBUM) {
192 if (!repeat)
193 return NULL;
194 /* first track of the current album */
195 return album_first_track(CUR_ALBUM);
198 /* not last album of the artist? */
199 if (CUR_ALBUM->node.next != &CUR_ARTIST->album_head) {
200 /* first track of the next album */
201 return album_first_track(to_album(CUR_ALBUM->node.next));
204 if (aaa_mode == AAA_MODE_ARTIST) {
205 if (!repeat)
206 return NULL;
207 /* first track of the first album of the current artist */
208 return artist_first_track(CUR_ARTIST);
211 /* not last artist of the library? */
212 if (CUR_ARTIST->node.next != &lib_artist_head) {
213 /* first track of the next artist */
214 return artist_first_track(to_artist(CUR_ARTIST->node.next));
217 if (!repeat)
218 return NULL;
220 /* first track */
221 return normal_get_first();
224 static struct tree_track *normal_get_prev(void)
226 if (lib_cur_track == NULL)
227 return normal_get_first();
229 /* not first track of the album? */
230 if (lib_cur_track->node.prev != &CUR_ALBUM->track_head) {
231 /* prev track of the album */
232 return to_tree_track(lib_cur_track->node.prev);
235 if (aaa_mode == AAA_MODE_ALBUM) {
236 if (!repeat)
237 return NULL;
238 /* last track of the album */
239 return to_tree_track(CUR_ALBUM->track_head.prev);
242 /* not first album of the artist? */
243 if (CUR_ALBUM->node.prev != &CUR_ARTIST->album_head) {
244 /* last track of the prev album of the artist */
245 return album_last_track(to_album(CUR_ALBUM->node.prev));
248 if (aaa_mode == AAA_MODE_ARTIST) {
249 if (!repeat)
250 return NULL;
251 /* last track of the last album of the artist */
252 return album_last_track(to_album(CUR_ARTIST->album_head.prev));
255 /* not first artist of the library? */
256 if (CUR_ARTIST->node.prev != &lib_artist_head) {
257 /* last track of the last album of the prev artist */
258 return artist_last_track(to_artist(CUR_ARTIST->node.prev));
261 if (!repeat)
262 return NULL;
264 /* last track */
265 return artist_last_track(to_artist(lib_artist_head.prev));
268 /* set next/prev (tree) }}} */
270 void lib_reshuffle(void)
272 reshuffle(&lib_shuffle_head);
275 static void free_lib_track(struct list_head *item)
277 struct tree_track *track = (struct tree_track *)to_simple_track(item);
278 struct track_info *ti = tree_track_info(track);
280 if (track == lib_cur_track)
281 lib_cur_track = NULL;
283 if (remove_from_hash)
284 hash_remove(ti);
286 list_del(&track->shuffle_track.node);
287 tree_remove(track);
289 track_info_unref(ti);
290 free(track);
293 void lib_init(void)
295 editable_init(&lib_editable, free_lib_track);
296 tree_init();
297 srand(time(NULL));
300 static struct track_info *lib_set_track(struct tree_track *track)
302 struct track_info *ti = NULL;
304 if (track) {
305 lib_cur_track = track;
306 ti = tree_track_info(track);
307 track_info_ref(ti);
308 all_wins_changed();
310 return ti;
313 struct track_info *lib_set_next(void)
315 struct tree_track *track;
317 if (list_empty(&lib_artist_head)) {
318 BUG_ON(lib_cur_track != NULL);
319 return NULL;
321 if (shuffle) {
322 track = (struct tree_track *)shuffle_list_get_next(&lib_shuffle_head,
323 (struct shuffle_track *)lib_cur_track, aaa_mode_filter);
324 } else if (play_sorted) {
325 track = (struct tree_track *)simple_list_get_next(&lib_editable.head,
326 (struct simple_track *)lib_cur_track, aaa_mode_filter);
327 } else {
328 track = normal_get_next();
330 return lib_set_track(track);
333 struct track_info *lib_set_prev(void)
335 struct tree_track *track;
337 if (list_empty(&lib_artist_head)) {
338 BUG_ON(lib_cur_track != NULL);
339 return NULL;
341 if (shuffle) {
342 track = (struct tree_track *)shuffle_list_get_prev(&lib_shuffle_head,
343 (struct shuffle_track *)lib_cur_track, aaa_mode_filter);
344 } else if (play_sorted) {
345 track = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
346 (struct simple_track *)lib_cur_track, aaa_mode_filter);
347 } else {
348 track = normal_get_prev();
350 return lib_set_track(track);
353 struct track_info *sorted_set_selected(void)
355 struct iter sel;
357 if (list_empty(&lib_editable.head))
358 return NULL;
360 window_get_sel(lib_editable.win, &sel);
361 return lib_set_track(iter_to_sorted_track(&sel));
364 void lib_set_filter(struct expr *expr)
366 static const char *tmp_keys[1] = { NULL };
367 struct track_info *cur_ti = NULL;
368 const char **sort_keys;
369 int i;
371 /* try to save cur_track */
372 if (lib_cur_track) {
373 cur_ti = tree_track_info(lib_cur_track);
374 track_info_ref(cur_ti);
377 remove_from_hash = 0;
378 editable_clear(&lib_editable);
379 remove_from_hash = 1;
381 if (filter)
382 expr_free(filter);
383 filter = expr;
385 /* disable sorting temporarily */
386 sort_keys = lib_editable.sort_keys;
387 lib_editable.sort_keys = tmp_keys;
389 for (i = 0; i < FH_SIZE; i++) {
390 struct fh_entry *e;
392 e = ti_hash[i];
393 while (e) {
394 struct track_info *ti = e->ti;
396 if (filter == NULL || expr_eval(filter, ti))
397 views_add_track(ti);
398 e = e->next;
402 /* enable sorting */
403 lib_editable.sort_keys = sort_keys;
404 editable_sort(&lib_editable);
406 lib_cur_win = lib_tree_win;
407 window_goto_top(lib_tree_win);
409 /* restore cur_track */
410 if (cur_ti) {
411 struct simple_track *track;
413 list_for_each_entry(track, &lib_editable.head, node) {
414 if (strcmp(track->info->filename, cur_ti->filename) == 0) {
415 struct tree_track *tt = (struct tree_track *)track;
417 lib_cur_track = tt;
418 break;
421 track_info_unref(cur_ti);
425 void lib_remove(struct track_info *ti)
427 struct simple_track *track;
429 list_for_each_entry(track, &lib_editable.head, node) {
430 if (track->info == ti) {
431 editable_remove_track(&lib_editable, track);
432 break;
437 void lib_clear_store(void)
439 int i;
441 for (i = 0; i < FH_SIZE; i++) {
442 struct fh_entry *e, *next;
444 e = ti_hash[i];
445 while (e) {
446 next = e->next;
447 track_info_unref(e->ti);
448 free(e);
449 e = next;
451 ti_hash[i] = NULL;
455 void sorted_sel_current(void)
457 if (lib_cur_track) {
458 struct iter iter;
460 sorted_track_to_iter(lib_cur_track, &iter);
461 window_set_sel(lib_editable.win, &iter);
465 static int ti_filename_cmp(const void *a, const void *b)
467 const struct track_info *tia = *(const struct track_info **)a;
468 const struct track_info *tib = *(const struct track_info **)b;
470 return strcmp(tia->filename, tib->filename);
473 int lib_for_each(int (*cb)(void *data, struct track_info *ti), void *data)
475 int i, rc = 0, count = 0, size = 1024;
476 struct track_info **tis;
478 tis = xnew(struct track_info *, size);
480 /* collect all track_infos */
481 for (i = 0; i < FH_SIZE; i++) {
482 struct fh_entry *e;
484 e = ti_hash[i];
485 while (e) {
486 if (count == size) {
487 size *= 2;
488 tis = xrenew(struct track_info *, tis, size);
490 tis[count++] = e->ti;
491 e = e->next;
495 /* sort them by filename and call cb for each */
496 qsort(tis, count, sizeof(struct track_info *), ti_filename_cmp);
497 for (i = 0; i < count; i++) {
498 rc = cb(data, tis[i]);
499 if (rc)
500 break;
503 free(tis);
504 return rc;