1 /*
2  * Copyright (c) 2011 Tim van der Molen <tim@kariliq.nl>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /* For FreeBSD. */
18 #define _WITH_GETLINE
19 
20 #include <errno.h>
21 #include <limits.h>
22 #include <pthread.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 
28 #include "siren.h"
29 
getrandom_inner(dest: &mut [u8]) -> Result<(), Error>30 static int		 library_search_entry(const void *, const char *);
31 
32 static pthread_mutex_t	 library_menu_mtx = PTHREAD_MUTEX_INITIALIZER;
33 static struct format	*library_altformat;
34 static struct format	*library_format;
35 static struct menu	*library_menu;
36 static unsigned int	 library_duration;
37 static int		 library_modified;
38 
39 void
40 library_activate_entry(void)
41 {
42 	struct menu_entry	*e;
43 	struct track		*t;
44 
45 	t = NULL;
46 
47 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
48 	if ((e = menu_get_selected_entry(library_menu)) != NULL) {
49 		menu_activate_entry(library_menu, e);
50 		t = menu_get_entry_data(e);
51 	}
52 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
53 
54 	if (t != NULL) {
55 		player_set_source(PLAYER_SOURCE_LIBRARY);
56 		player_play_track(t);
57 		library_print();
58 	}
getrandom_init() -> Result<RngSource, Error>59 }
60 
61 void
62 library_add_dir(const char *path)
63 {
64 	struct dir		*d;
65 	struct dir_entry	*de;
66 	struct track		*t;
67 
68 	if ((d = dir_open(path)) == NULL) {
69 		msg_err("%s", path);
70 		return;
71 	}
72 
73 	while ((de = dir_get_entry(d)) != NULL) {
74 		switch (de->type) {
75 		case FILE_TYPE_DIRECTORY:
76 			if (strcmp(de->name, ".") && strcmp(de->name, ".."))
77 				library_add_dir(de->path);
78 			break;
79 		case FILE_TYPE_REGULAR:
80 			if ((t = track_get(de->path, NULL)) != NULL)
81 				library_add_track(t);
82 			break;
83 		default:
84 			msg_errx("%s: Unsupported file type", de->path);
85 			break;
86 		}
87 	}
88 
89 	dir_close(d);
90 }
91 
92 void
93 library_add_track(struct track *t)
94 {
95 	struct track		*et;
96 	struct menu_entry	*entry;
97 
ms_crypto(this: &Global) -> BrowserCrypto98 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
99 	MENU_FOR_EACH_ENTRY(library_menu, entry) {
crypto(this: &Global) -> BrowserCrypto100 		et = menu_get_entry_data(entry);
101 		if (track_cmp(t, et) < 0) {
102 			menu_insert_before(library_menu, entry, t);
get_random_values(this: &BrowserCrypto, buf: &Uint8Array) -> Result<(), JsValue>103 			break;
104 		}
105 	}
106 
require(s: &str) -> Result<NodeCrypto, JsValue>107 	if (entry == NULL)
108 		menu_insert_tail(library_menu, t);
109 
random_fill_sync(this: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>110 	library_duration += t->duration;
111 	library_modified = 1;
112 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
113 	library_print();
process(this: &Global) -> Process114 }
115 
116 void
117 library_copy_entry(enum view_id view)
118 {
119 	struct track *t;
120 
121 	if (view == VIEW_ID_LIBRARY)
122 		return;
123 
124 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
125 	if ((t = menu_get_selected_entry_data(library_menu)) != NULL)
126 		view_add_track(view, t);
127 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
128 }
129 
130 void
131 library_delete_all_entries(void)
132 {
133 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
134 	menu_remove_all_entries(library_menu);
135 	library_duration = 0;
136 	library_modified = 1;
137 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
138 	library_print();
139 }
140 
141 void
142 library_delete_entry(void)
143 {
144 	struct menu_entry	*e;
145 	struct track		*t;
146 
147 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
148 	if ((e = menu_get_selected_entry(library_menu)) != NULL) {
149 		t = menu_get_entry_data(e);
150 		menu_remove_selected_entry(library_menu);
151 		library_duration -= t->duration;
152 		library_modified = 1;
153 	}
154 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
155 	library_print();
156 }
157 
158 void
159 library_end(void)
160 {
161 	if (library_modified)
162 		library_write_file();
163 
164 	menu_free(library_menu);
165 }
166 
167 static void
168 library_get_entry_text(const void *e, char *buf, size_t bufsize)
169 {
170 	const struct track *t;
171 
172 	t = e;
173 	format_track_snprintf(buf, bufsize, library_format, library_altformat,
174 	    t);
175 }
176 
177 struct track *
178 library_get_next_track(void)
179 {
180 	struct menu_entry	*me;
181 	struct track		*t;
182 
183 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
184 	if ((me = menu_get_active_entry(library_menu)) == NULL)
185 		t = NULL;
186 	else {
187 		if ((me = menu_get_next_entry(me)) == NULL &&
188 		    option_get_boolean("repeat-all"))
189 			me = menu_get_first_entry(library_menu);
190 
191 		if (me == NULL)
192 			t = NULL;
193 		else {
194 			menu_activate_entry(library_menu, me);
195 			t = menu_get_entry_data(me);
196 		}
197 	}
198 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
199 	library_print();
200 	return t;
201 }
202 
203 struct track *
204 library_get_prev_track(void)
205 {
206 	struct menu_entry	*me;
207 	struct track		*t;
208 
209 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
210 	if ((me = menu_get_active_entry(library_menu)) == NULL)
211 		t = NULL;
212 	else {
213 		if ((me = menu_get_prev_entry(me)) == NULL &&
214 		    option_get_boolean("repeat-all"))
215 			me = menu_get_last_entry(library_menu);
216 
217 		if (me == NULL)
218 			t = NULL;
219 		else {
220 			menu_activate_entry(library_menu, me);
221 			t = menu_get_entry_data(me);
222 		}
223 	}
224 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
225 	library_print();
226 	return t;
227 }
228 
229 void
230 library_init(void)
231 {
232 	library_menu = menu_init(NULL, library_get_entry_text,
233 	    library_search_entry);
234 }
235 
236 void
237 library_print(void)
238 {
239 	if (view_get_id() != VIEW_ID_LIBRARY)
240 		return;
241 
242 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
243 	screen_view_title_printf("Library: %u track%s (%u:%02u:%02u)",
244 	    menu_get_nentries(library_menu),
245 	    menu_get_nentries(library_menu) == 1 ? "" : "s",
246 	    HOURS(library_duration),
247 	    HMINS(library_duration),
248 	    MSECS(library_duration));
249 	option_lock();
250 	library_format = option_get_format("library-format");
251 	library_altformat = option_get_format("library-format-alt");
252 	menu_print(library_menu);
253 	option_unlock();
254 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
255 }
256 
257 void
258 library_read_file(void)
259 {
260 	struct menu_entry	*e;
261 	struct track		*et, *t;
262 	FILE			*fp;
263 	size_t			 size;
264 	ssize_t			 len;
265 	time_t			 lasttime;
266 	char			*line, *file;
267 
268 	file = conf_get_path(LIBRARY_FILE);
269 	if ((fp = fopen(file, "r")) == NULL) {
270 		if (errno != ENOENT) {
271 			LOG_ERR("fopen: %s", file);
272 			msg_err("Cannot read library file");
273 		}
274 		free(file);
275 		return;
276 	}
277 
278 	lasttime = time(NULL);
279 	line = NULL;
280 	size = 0;
281 	while ((len = getline(&line, &size, fp)) != -1) {
282 		if (len > 0 && line[len - 1] == '\n')
283 			line[len - 1] = '\0';
284 
285 		if (line[0] != '/') {
286 			LOG_ERRX("%s: %s: invalid entry", file, line);
287 			continue;
288 		}
289 
290 		if ((t = track_require(line)) == NULL)
291 			continue;
292 
293 		MENU_FOR_EACH_ENTRY_REVERSE(library_menu, e) {
294 			et = menu_get_entry_data(e);
295 			if (track_cmp(t, et) > 0) {
296 				menu_insert_after(library_menu, e, t);
297 				break;
298 			}
299 		}
300 		if (e == NULL)
301 			menu_insert_head(library_menu, t);
302 		library_duration += t->duration;
303 
304 		if (time(NULL) > lasttime) {
305 			library_print();
306 			lasttime = time(NULL);
307 		}
308 	}
309 	if (ferror(fp)) {
310 		LOG_ERR("getline: %s", file);
311 		msg_err("Cannot read library");
312 	}
313 	free(line);
314 	free(file);
315 
316 	fclose(fp);
317 
318 	library_print();
319 }
320 
321 void
322 library_scroll_down(enum menu_scroll scroll)
323 {
324 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
325 	menu_scroll_down(library_menu, scroll);
326 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
327 	library_print();
328 }
329 
330 void
331 library_scroll_up(enum menu_scroll scroll)
332 {
333 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
334 	menu_scroll_up(library_menu, scroll);
335 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
336 	library_print();
337 }
338 
339 static int
340 library_search_entry(const void *e, const char *search)
341 {
342 	const struct track *t;
343 
344 	t = e;
345 	return track_search(t, search);
346 }
347 
348 void
349 library_search_next(const char *search)
350 {
351 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
352 	menu_search_next(library_menu, search);
353 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
354 	library_print();
355 }
356 
357 void
358 library_search_prev(const char *search)
359 {
360 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
361 	menu_search_prev(library_menu, search);
362 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
363 	library_print();
364 }
365 
366 void
367 library_select_active_entry(void)
368 {
369 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
370 	menu_select_active_entry(library_menu);
371 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
372 	library_print();
373 }
374 
375 void
376 library_select_first_entry(void)
377 {
378 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
379 	menu_select_first_entry(library_menu);
380 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
381 	library_print();
382 }
383 
384 void
385 library_select_last_entry(void)
386 {
387 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
388 	menu_select_last_entry(library_menu);
389 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
390 	library_print();
391 }
392 
393 void
394 library_select_next_entry(void)
395 {
396 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
397 	menu_select_next_entry(library_menu);
398 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
399 	library_print();
400 }
401 
402 void
403 library_select_prev_entry(void)
404 {
405 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
406 	menu_select_prev_entry(library_menu);
407 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
408 	library_print();
409 }
410 
411 /* Resort the tracks and recalculate the duration. */
412 void
413 library_update(void)
414 {
415 	struct menu_entry	*be, *e, *ne, *pe;
416 	struct track		*pt, *t;
417 
418 	XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
419 
420 	library_duration = 0;
421 
422 	e = menu_get_first_entry(library_menu);
423 	while (e != NULL) {
424 		t = menu_get_entry_data(e);
425 		library_duration += t->duration;
426 
427 		ne = menu_get_next_entry(e);
428 
429 		be = NULL;
430 		pe = e;
431 		while ((pe = menu_get_prev_entry(pe)) != NULL) {
432 			pt = menu_get_entry_data(pe);
433 			if (track_cmp(t, pt) < 0)
434 				be = pe;
435 			else
436 				break;
437 		}
438 		if (be != NULL)
439 			menu_move_entry_before(library_menu, be, e);
440 
441 		e = ne;
442 	}
443 
444 	XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
445 }
446 
447 int
448 library_write_file(void)
449 {
450 	struct menu_entry	*entry;
451 	struct track		*t;
452 	FILE			*fp;
453 	int			 ret;
454 	char			*file;
455 
456 	file = conf_get_path(LIBRARY_FILE);
457 	if ((fp = fopen(file, "w")) == NULL) {
458 		LOG_ERR("fopen: %s", file);
459 		msg_err("Cannot save library");
460 		ret = -1;
461 	} else {
462 		XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
463 		MENU_FOR_EACH_ENTRY(library_menu, entry) {
464 			t = menu_get_entry_data(entry);
465 			fprintf(fp, "%s\n", t->path);
466 		}
467 		library_modified = 0;
468 		XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
469 
470 		fclose(fp);
471 		ret = 0;
472 	}
473 
474 	free(file);
475 	return ret;
476 }
477