1 /*
2  * Copyright 2008-2013 Various Authors
3  * Copyright 2006 Timo Hirvonen
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "options.h"
20 #include "list.h"
21 #include "utils.h"
22 #include "xmalloc.h"
23 #include "player.h"
24 #include "buffer.h"
25 #include "ui_curses.h"
26 #include "cmus.h"
27 #include "misc.h"
28 #include "lib.h"
29 #include "pl.h"
30 #include "browser.h"
31 #include "keys.h"
32 #include "filters.h"
33 #include "command_mode.h"
34 #include "file.h"
35 #include "prog.h"
36 #include "output.h"
37 #include "input.h"
38 #include "xstrjoin.h"
39 #include "track_info.h"
40 #include "cache.h"
41 #include "debug.h"
42 #include "discid.h"
43 #include "mpris.h"
44 
45 #include <stdio.h>
46 #include <errno.h>
47 #include <strings.h>
48 
49 #if defined(__sun__)
50 #include <ncurses.h>
51 #else
52 #include <curses.h>
53 #endif
54 
55 /* initialized option variables */
56 
57 char *cdda_device = NULL;
58 char *output_plugin = NULL;
59 char *status_display_program = NULL;
60 char *server_password;
61 int auto_reshuffle = 1;
62 int confirm_run = 1;
63 int resume_cmus = 0;
64 int show_hidden = 0;
65 int show_current_bitrate = 0;
66 int show_playback_position = 1;
67 int show_remaining_time = 0;
68 int set_term_title = 1;
69 int wrap_search = 1;
70 int play_library = 1;
71 int repeat = 0;
72 int shuffle = 0;
73 int follow = 0;
74 int display_artist_sort_name;
75 int smart_artist_sort = 1;
76 int scroll_offset = 2;
77 int rewind_offset = 5;
78 int skip_track_info = 0;
79 int auto_expand_albums_follow = 1;
80 int auto_expand_albums_search = 1;
81 int auto_expand_albums_selcur = 1;
82 int show_all_tracks = 1;
83 int mouse = 0;
84 int mpris = 1;
85 int time_show_leading_zero = 1;
86 int start_view = TREE_VIEW;
87 int stop_after_queue = 0;
88 int tree_width_percent = 33;
89 int tree_width_max = 0;
90 
91 int colors[NR_COLORS] = {
92 	-1,
93 	-1,
94 	COLOR_RED | BRIGHT,
95 	COLOR_YELLOW | BRIGHT,
96 
97 	COLOR_BLUE,
98 	COLOR_WHITE,
99 	COLOR_BLACK,
100 	COLOR_BLUE,
101 
102 	COLOR_WHITE | BRIGHT,
103 	-1,
104 	COLOR_YELLOW | BRIGHT,
105 	COLOR_BLUE,
106 
107 	COLOR_YELLOW | BRIGHT,
108 	COLOR_BLUE | BRIGHT,
109 	-1,
110 	COLOR_WHITE,
111 
112 	COLOR_YELLOW | BRIGHT,
113 	COLOR_WHITE,
114 	COLOR_BLACK,
115 	COLOR_BLUE,
116 
117 	COLOR_WHITE | BRIGHT,
118 	COLOR_BLUE,
119 	COLOR_WHITE | BRIGHT,
120 	-1,
121 
122 	-1,
123 };
124 
125 int attrs[NR_ATTRS] = {
126 	A_NORMAL,
127 	A_NORMAL,
128 	A_NORMAL,
129 	A_NORMAL,
130 	A_NORMAL,
131 	A_NORMAL,
132 	A_NORMAL,
133 	A_NORMAL,
134 	A_NORMAL,
135 	A_NORMAL,
136 	A_BOLD,
137 	A_NORMAL,
138 };
139 
140 /* uninitialized option variables */
141 char *tree_win_format = NULL;
142 char *tree_win_artist_format = NULL;
143 char *track_win_album_format = NULL;
144 char *track_win_format = NULL;
145 char *track_win_format_va = NULL;
146 char *track_win_alt_format = NULL;
147 char *list_win_format = NULL;
148 char *list_win_format_va = NULL;
149 char *list_win_alt_format = NULL;
150 char *current_format = NULL;
151 char *current_alt_format = NULL;
152 char *statusline_format = NULL;
153 char *window_title_format = NULL;
154 char *window_title_alt_format = NULL;
155 char *id3_default_charset = NULL;
156 char *icecast_default_charset = NULL;
157 char *lib_add_filter = NULL;
158 
buf_int(char * buf,int val,size_t size)159 static void buf_int(char *buf, int val, size_t size)
160 {
161 	snprintf(buf, size, "%d", val);
162 }
163 
parse_int(const char * buf,int minval,int maxval,int * val)164 static int parse_int(const char *buf, int minval, int maxval, int *val)
165 {
166 	long int tmp;
167 
168 	if (str_to_int(buf, &tmp) == -1 || tmp < minval || tmp > maxval) {
169 		error_msg("integer in range %d..%d expected", minval, maxval);
170 		return 0;
171 	}
172 	*val = tmp;
173 	return 1;
174 }
175 
parse_enum(const char * buf,int minval,int maxval,const char * const names[],int * val)176 int parse_enum(const char *buf, int minval, int maxval, const char * const names[], int *val)
177 {
178 	long int tmp;
179 	int i;
180 
181 	if (str_to_int(buf, &tmp) == 0) {
182 		if (tmp < minval || tmp > maxval)
183 			goto err;
184 		*val = tmp;
185 		return 1;
186 	}
187 
188 	for (i = 0; names[i]; i++) {
189 		if (strcasecmp(buf, names[i]) == 0) {
190 			*val = i + minval;
191 			return 1;
192 		}
193 	}
194 err:
195 	error_msg("name or integer in range %d..%d expected", minval, maxval);
196 	return 0;
197 }
198 
199 static const char * const bool_names[] = {
200 	"false", "true", NULL
201 };
202 
parse_bool(const char * buf,int * val)203 static int parse_bool(const char *buf, int *val)
204 {
205 	return parse_enum(buf, 0, 1, bool_names, val);
206 }
207 
208 /* this is used as id in struct cmus_opt */
209 enum format_id {
210 	FMT_CURRENT,
211 	FMT_CURRENT_ALT,
212 	FMT_STATUSLINE,
213 	FMT_PLAYLIST,
214 	FMT_PLAYLIST_ALT,
215 	FMT_PLAYLIST_VA,
216 	FMT_TITLE,
217 	FMT_TITLE_ALT,
218 	FMT_TRACKWIN,
219 	FMT_TRACKWIN_ALBUM,
220 	FMT_TRACKWIN_ALT,
221 	FMT_TRACKWIN_VA,
222 	FMT_TREEWIN,
223 	FMT_TREEWIN_ARTIST,
224 
225 	NR_FMTS
226 };
227 
228 /* default values for the variables which we must initialize but
229  * can't do it statically */
230 static const struct {
231 	const char *name;
232 	const char *value;
233 } str_defaults[] = {
234 	[FMT_CURRENT_ALT]	= { "altformat_current"		, " %F "						},
235 	[FMT_CURRENT]		= { "format_current"		, " %a - %l -%3n. %t%= %y "				},
236 	[FMT_STATUSLINE]	= { "format_statusline"		,
237 		" %{status} %{?show_playback_position?%{position} %{?duration?/ %{duration} }?%{?duration?%{duration} }}"
238 		"- %{total} %{?bpm>0?at %{bpm} BPM }"
239 		"%{?volume>=0?vol: %{?lvolume!=rvolume?%{lvolume},%{rvolume} ?%{volume} }}"
240 		"%{?stream?buf: %{buffer} }"
241 		"%{?show_current_bitrate & bitrate>=0? %{bitrate} kbps }"
242 		"%="
243 		"%{?repeat_current?repeat current?%{?play_library?%{playlist_mode} from %{?play_sorted?sorted }library?playlist}}"
244 		" | %1{continue}%1{follow}%1{repeat}%1{shuffle} "
245 	},
246 	[FMT_PLAYLIST_ALT]	= { "altformat_playlist"	, " %f%= %d "						},
247 	[FMT_PLAYLIST]		= { "format_playlist"		, " %-21%a %3n. %t%= %y %d %{?X!=0?%3X ?    }"		},
248 	[FMT_PLAYLIST_VA]	= { "format_playlist_va"	, " %-21%A %3n. %t (%a)%= %y %d %{?X!=0?%3X ?    }"	},
249 	[FMT_TITLE_ALT]		= { "altformat_title"		, "%f"							},
250 	[FMT_TITLE]		= { "format_title"		, "%a - %l - %t (%y)"					},
251 	[FMT_TRACKWIN_ALBUM]	= { "format_trackwin_album"	, " %l %= %{albumduration} "				},
252 	[FMT_TRACKWIN_ALT]	= { "altformat_trackwin"	, " %f%= %d "						},
253 	[FMT_TRACKWIN]		= { "format_trackwin"		, "%3n. %t%= %y %d "					},
254 	[FMT_TRACKWIN_VA]	= { "format_trackwin_va"	, "%3n. %t (%a)%= %y %d "				},
255 	[FMT_TREEWIN]		= { "format_treewin"		, "  %l"						},
256 	[FMT_TREEWIN_ARTIST]	= { "format_treewin_artist"	, "%a"							},
257 
258 	[NR_FMTS] =
259 
260 	{ "lib_sort", "albumartist date album discnumber tracknumber title filename play_count" },
261 	{ "pl_sort", "" },
262 	{ "id3_default_charset", "ISO-8859-1" },
263 	{ "icecast_default_charset", "ISO-8859-1" },
264 	{ NULL, NULL }
265 };
266 
267 /* callbacks for normal options {{{ */
268 
get_device(void * data,char * buf,size_t size)269 static void get_device(void *data, char *buf, size_t size)
270 {
271 	strscpy(buf, cdda_device, size);
272 }
273 
set_device(void * data,const char * buf)274 static void set_device(void *data, const char *buf)
275 {
276 	free(cdda_device);
277 	cdda_device = expand_filename(buf);
278 }
279 
280 #define SECOND_SIZE (44100 * 16 / 8 * 2)
get_buffer_seconds(void * data,char * buf,size_t size)281 static void get_buffer_seconds(void *data, char *buf, size_t size)
282 {
283 	int val = (player_get_buffer_chunks() * CHUNK_SIZE + SECOND_SIZE / 2) /
284 		SECOND_SIZE;
285 	buf_int(buf, val, size);
286 }
287 
set_buffer_seconds(void * data,const char * buf)288 static void set_buffer_seconds(void *data, const char *buf)
289 {
290 	int sec;
291 
292 	if (parse_int(buf, 1, 300, &sec))
293 		player_set_buffer_chunks((sec * SECOND_SIZE + CHUNK_SIZE / 2) / CHUNK_SIZE);
294 }
295 
get_scroll_offset(void * data,char * buf,size_t size)296 static void get_scroll_offset(void *data, char *buf, size_t size)
297 {
298 	buf_int(buf, scroll_offset, size);
299 }
300 
set_scroll_offset(void * data,const char * buf)301 static void set_scroll_offset(void *data, const char *buf)
302 {
303 	int offset;
304 
305 	if (parse_int(buf, 0, 9999, &offset))
306 		scroll_offset = offset;
307 }
308 
get_rewind_offset(void * data,char * buf,size_t size)309 static void get_rewind_offset(void *data, char *buf, size_t size)
310 {
311 	buf_int(buf, rewind_offset, size);
312 }
313 
set_rewind_offset(void * data,const char * buf)314 static void set_rewind_offset(void *data, const char *buf)
315 {
316 	int offset;
317 
318 	if (parse_int(buf, -1, 9999, &offset))
319 		rewind_offset = offset;
320 }
321 
get_id3_default_charset(void * data,char * buf,size_t size)322 static void get_id3_default_charset(void *data, char *buf, size_t size)
323 {
324 	strscpy(buf, id3_default_charset, size);
325 }
326 
get_icecast_default_charset(void * data,char * buf,size_t size)327 static void get_icecast_default_charset(void *data, char *buf, size_t size)
328 {
329 	strscpy(buf, icecast_default_charset, size);
330 }
331 
set_id3_default_charset(void * data,const char * buf)332 static void set_id3_default_charset(void *data, const char *buf)
333 {
334 	free(id3_default_charset);
335 	id3_default_charset = xstrdup(buf);
336 }
337 
set_icecast_default_charset(void * data,const char * buf)338 static void set_icecast_default_charset(void *data, const char *buf)
339 {
340 	free(icecast_default_charset);
341 	icecast_default_charset = xstrdup(buf);
342 }
343 
get_lib_sort(void * data,char * buf,size_t size)344 static void get_lib_sort(void *data, char *buf, size_t size)
345 {
346 	strscpy(buf, lib_editable.shared->sort_str, size);
347 }
348 
set_lib_sort(void * data,const char * buf)349 static void set_lib_sort(void *data, const char *buf)
350 {
351 	sort_key_t *keys = parse_sort_keys(buf);
352 
353 	if (keys) {
354 		editable_shared_set_sort_keys(lib_editable.shared, keys);
355 		editable_sort(&lib_editable);
356 		sort_keys_to_str(keys, lib_editable.shared->sort_str,
357 				sizeof(lib_editable.shared->sort_str));
358 	}
359 }
360 
get_pl_sort(void * data,char * buf,size_t size)361 static void get_pl_sort(void *data, char *buf, size_t size)
362 {
363 	pl_get_sort_str(buf, size);
364 }
365 
set_pl_sort(void * data,const char * buf)366 static void set_pl_sort(void *data, const char *buf)
367 {
368 	pl_set_sort_str(buf);
369 }
370 
get_output_plugin(void * data,char * buf,size_t size)371 static void get_output_plugin(void *data, char *buf, size_t size)
372 {
373 	const char *value = op_get_current();
374 
375 	if (value)
376 		strscpy(buf, value, size);
377 }
378 
set_output_plugin(void * data,const char * buf)379 static void set_output_plugin(void *data, const char *buf)
380 {
381 	if (ui_initialized) {
382 		if (!soft_vol)
383 			mixer_close();
384 		player_set_op(buf);
385 		if (!soft_vol)
386 			mixer_open();
387 	} else {
388 		/* must set it later manually */
389 		output_plugin = xstrdup(buf);
390 	}
391 }
392 
get_passwd(void * data,char * buf,size_t size)393 static void get_passwd(void *data, char *buf, size_t size)
394 {
395 	if (server_password)
396 		strscpy(buf, server_password, size);
397 }
398 
set_passwd(void * data,const char * buf)399 static void set_passwd(void *data, const char *buf)
400 {
401 	int len = strlen(buf);
402 
403 	if (len == 0) {
404 		free(server_password);
405 		server_password = NULL;
406 	} else if (len < 6) {
407 		error_msg("unsafe password");
408 	} else {
409 		free(server_password);
410 		server_password = xstrdup(buf);
411 	}
412 }
413 
get_replaygain_preamp(void * data,char * buf,size_t size)414 static void get_replaygain_preamp(void *data, char *buf, size_t size)
415 {
416 	snprintf(buf, size, "%f", replaygain_preamp);
417 }
418 
set_replaygain_preamp(void * data,const char * buf)419 static void set_replaygain_preamp(void *data, const char *buf)
420 {
421 	double val;
422 	char *end;
423 
424 	val = strtod(buf, &end);
425 	if (end == buf) {
426 		error_msg("floating point number expected (dB)");
427 		return;
428 	}
429 	player_set_rg_preamp(val);
430 }
431 
get_softvol_state(void * data,char * buf,size_t size)432 static void get_softvol_state(void *data, char *buf, size_t size)
433 {
434 	snprintf(buf, size, "%d %d", soft_vol_l, soft_vol_r);
435 }
436 
set_softvol_state(void * data,const char * buf)437 static void set_softvol_state(void *data, const char *buf)
438 {
439 	char buffer[OPTION_MAX_SIZE];
440 	char *ptr;
441 	long int l, r;
442 
443 	strscpy(buffer, buf, sizeof(buffer));
444 	ptr = strchr(buffer, ' ');
445 	if (!ptr)
446 		goto err;
447 	while (*ptr == ' ')
448 		*ptr++ = 0;
449 
450 	if (str_to_int(buffer, &l) == -1 || l < 0 || l > 100)
451 		goto err;
452 	if (str_to_int(ptr, &r) == -1 || r < 0 || r > 100)
453 		goto err;
454 
455 	player_set_soft_volume(l, r);
456 	return;
457 err:
458 	error_msg("two integers in range 0..100 expected");
459 }
460 
get_status_display_program(void * data,char * buf,size_t size)461 static void get_status_display_program(void *data, char *buf, size_t size)
462 {
463 	if (status_display_program)
464 		strscpy(buf, status_display_program, size);
465 }
466 
set_status_display_program(void * data,const char * buf)467 static void set_status_display_program(void *data, const char *buf)
468 {
469 	free(status_display_program);
470 	status_display_program = NULL;
471 	if (buf[0])
472 		status_display_program = expand_filename(buf);
473 }
474 
get_tree_width_percent(void * data,char * buf,size_t size)475 static void get_tree_width_percent(void *data, char *buf, size_t size)
476 {
477 	buf_int(buf, tree_width_percent, size);
478 }
479 
set_tree_width_percent(void * data,const char * buf)480 static void set_tree_width_percent(void *data, const char *buf)
481 {
482 	int percent;
483 
484 	if (parse_int(buf, 1, 100, &percent))
485 		tree_width_percent = percent;
486 	update_size();
487 }
488 
get_tree_width_max(void * data,char * buf,size_t size)489 static void get_tree_width_max(void *data, char *buf, size_t size)
490 {
491 	buf_int(buf, tree_width_max, size);
492 }
493 
set_tree_width_max(void * data,const char * buf)494 static void set_tree_width_max(void *data, const char *buf)
495 {
496 	int cols;
497 
498 	if (parse_int(buf, 0, 9999, &cols))
499 		tree_width_max = cols;
500 	update_size();
501 }
502 
503 /* }}} */
504 
505 /* callbacks for toggle options {{{ */
506 
get_auto_reshuffle(void * data,char * buf,size_t size)507 static void get_auto_reshuffle(void *data, char *buf, size_t size)
508 {
509 	strscpy(buf, bool_names[auto_reshuffle], size);
510 }
511 
set_auto_reshuffle(void * data,const char * buf)512 static void set_auto_reshuffle(void *data, const char *buf)
513 {
514 	parse_bool(buf, &auto_reshuffle);
515 }
516 
toggle_auto_reshuffle(void * data)517 static void toggle_auto_reshuffle(void *data)
518 {
519 	auto_reshuffle ^= 1;
520 }
521 
get_follow(void * data,char * buf,size_t size)522 static void get_follow(void *data, char *buf, size_t size)
523 {
524 	strscpy(buf, bool_names[follow], size);
525 }
526 
set_follow(void * data,const char * buf)527 static void set_follow(void *data, const char *buf)
528 {
529 	if (!parse_bool(buf, &follow))
530 		return;
531 	update_statusline();
532 }
533 
toggle_follow(void * data)534 static void toggle_follow(void *data)
535 {
536 	follow ^= 1;
537 	update_statusline();
538 }
539 
get_continue(void * data,char * buf,size_t size)540 static void get_continue(void *data, char *buf, size_t size)
541 {
542 	strscpy(buf, bool_names[player_cont], size);
543 }
544 
set_continue(void * data,const char * buf)545 static void set_continue(void *data, const char *buf)
546 {
547 	if (!parse_bool(buf, &player_cont))
548 		return;
549 	update_statusline();
550 }
551 
toggle_continue(void * data)552 static void toggle_continue(void *data)
553 {
554 	player_cont ^= 1;
555 	update_statusline();
556 }
557 
get_continue_album(void * data,char * buf,size_t size)558 static void get_continue_album(void *data, char *buf, size_t size)
559 {
560 	strscpy(buf, bool_names[player_cont_album], size);
561 }
562 
set_continue_album(void * data,const char * buf)563 static void set_continue_album(void *data, const char *buf)
564 {
565 	if (!parse_bool(buf, &player_cont_album))
566 		return;
567 	update_statusline();
568 }
569 
toggle_continue_album(void * data)570 static void toggle_continue_album(void *data)
571 {
572 	player_cont_album ^= 1;
573 	update_statusline();
574 }
575 
get_repeat_current(void * data,char * buf,size_t size)576 static void get_repeat_current(void *data, char *buf, size_t size)
577 {
578 	strscpy(buf, bool_names[player_repeat_current], size);
579 }
580 
set_repeat_current(void * data,const char * buf)581 static void set_repeat_current(void *data, const char *buf)
582 {
583 	int old = player_repeat_current;
584 	if (!parse_bool(buf, &player_repeat_current))
585 		return;
586 	if (old != player_repeat_current)
587 		mpris_loop_status_changed();
588 	update_statusline();
589 }
590 
toggle_repeat_current(void * data)591 static void toggle_repeat_current(void *data)
592 {
593 	player_repeat_current ^= 1;
594 	mpris_loop_status_changed();
595 	update_statusline();
596 }
597 
get_confirm_run(void * data,char * buf,size_t size)598 static void get_confirm_run(void *data, char *buf, size_t size)
599 {
600 	strscpy(buf, bool_names[confirm_run], size);
601 }
602 
set_confirm_run(void * data,const char * buf)603 static void set_confirm_run(void *data, const char *buf)
604 {
605 	parse_bool(buf, &confirm_run);
606 }
607 
toggle_confirm_run(void * data)608 static void toggle_confirm_run(void *data)
609 {
610 	confirm_run ^= 1;
611 }
612 
613 const char * const view_names[NR_VIEWS + 1] = {
614 	"tree", "sorted", "playlist", "queue", "browser", "filters", "settings", NULL
615 };
616 
get_play_library(void * data,char * buf,size_t size)617 static void get_play_library(void *data, char *buf, size_t size)
618 {
619 	strscpy(buf, bool_names[play_library], size);
620 }
621 
set_play_library(void * data,const char * buf)622 static void set_play_library(void *data, const char *buf)
623 {
624 	if (!parse_bool(buf, &play_library))
625 		return;
626 	update_statusline();
627 }
628 
toggle_play_library(void * data)629 static void toggle_play_library(void *data)
630 {
631 	play_library ^= 1;
632 	update_statusline();
633 }
634 
get_play_sorted(void * data,char * buf,size_t size)635 static void get_play_sorted(void *data, char *buf, size_t size)
636 {
637 	strscpy(buf, bool_names[play_sorted], size);
638 }
639 
set_play_sorted(void * data,const char * buf)640 static void set_play_sorted(void *data, const char *buf)
641 {
642 	int tmp;
643 
644 	if (!parse_bool(buf, &tmp))
645 		return;
646 
647 	play_sorted = tmp;
648 
649 	update_statusline();
650 }
651 
toggle_play_sorted(void * data)652 static void toggle_play_sorted(void *data)
653 {
654 	play_sorted = play_sorted ^ 1;
655 
656 	/* shuffle would override play_sorted... */
657 	if (play_sorted) {
658 		/* play_sorted makes no sense in playlist */
659 		play_library = 1;
660 		shuffle = 0;
661 	}
662 
663 	update_statusline();
664 }
665 
get_smart_artist_sort(void * data,char * buf,size_t size)666 static void get_smart_artist_sort(void *data, char *buf, size_t size)
667 {
668 	strscpy(buf, bool_names[smart_artist_sort], size);
669 }
670 
set_smart_artist_sort(void * data,const char * buf)671 static void set_smart_artist_sort(void *data, const char *buf)
672 {
673 	if (parse_bool(buf, &smart_artist_sort))
674 		tree_sort_artists();
675 }
676 
toggle_smart_artist_sort(void * data)677 static void toggle_smart_artist_sort(void *data)
678 {
679 	smart_artist_sort ^= 1;
680 	tree_sort_artists();
681 }
682 
get_display_artist_sort_name(void * data,char * buf,size_t size)683 static void get_display_artist_sort_name(void *data, char *buf, size_t size)
684 {
685 	strscpy(buf, bool_names[display_artist_sort_name], size);
686 }
687 
set_display_artist_sort_name(void * data,const char * buf)688 static void set_display_artist_sort_name(void *data, const char *buf)
689 {
690 	parse_bool(buf, &display_artist_sort_name);
691 	lib_tree_win->changed = 1;
692 }
693 
toggle_display_artist_sort_name(void * data)694 static void toggle_display_artist_sort_name(void *data)
695 {
696 	display_artist_sort_name ^= 1;
697 	lib_tree_win->changed = 1;
698 }
699 
700 const char * const aaa_mode_names[] = {
701 	"all", "artist", "album", NULL
702 };
703 
get_aaa_mode(void * data,char * buf,size_t size)704 static void get_aaa_mode(void *data, char *buf, size_t size)
705 {
706 	strscpy(buf, aaa_mode_names[aaa_mode], size);
707 }
708 
set_aaa_mode(void * data,const char * buf)709 static void set_aaa_mode(void *data, const char *buf)
710 {
711 	int tmp;
712 
713 	if (!parse_enum(buf, 0, 2, aaa_mode_names, &tmp))
714 		return;
715 
716 	aaa_mode = tmp;
717 	update_statusline();
718 }
719 
toggle_aaa_mode(void * data)720 static void toggle_aaa_mode(void *data)
721 {
722 	/* aaa mode makes no sense in playlist */
723 	play_library = 1;
724 
725 	aaa_mode++;
726 	aaa_mode %= 3;
727 	update_statusline();
728 }
729 
get_repeat(void * data,char * buf,size_t size)730 static void get_repeat(void *data, char *buf, size_t size)
731 {
732 	strscpy(buf, bool_names[repeat], size);
733 }
734 
set_repeat(void * data,const char * buf)735 static void set_repeat(void *data, const char *buf)
736 {
737 	int old = repeat;
738 	if (!parse_bool(buf, &repeat))
739 		return;
740 	if (!player_repeat_current && old != repeat)
741 		mpris_loop_status_changed();
742 	update_statusline();
743 }
744 
toggle_repeat(void * data)745 static void toggle_repeat(void *data)
746 {
747 	repeat ^= 1;
748 	if (!player_repeat_current)
749 		mpris_loop_status_changed();
750 	update_statusline();
751 }
752 
753 static const char * const replaygain_names[] = {
754 	"disabled", "track", "album", "track-preferred", "album-preferred", NULL
755 };
756 
get_replaygain(void * data,char * buf,size_t size)757 static void get_replaygain(void *data, char *buf, size_t size)
758 {
759 	strscpy(buf, replaygain_names[replaygain], size);
760 }
761 
set_replaygain(void * data,const char * buf)762 static void set_replaygain(void *data, const char *buf)
763 {
764 	int tmp;
765 
766 	if (!parse_enum(buf, 0, 4, replaygain_names, &tmp))
767 		return;
768 	player_set_rg(tmp);
769 }
770 
toggle_replaygain(void * data)771 static void toggle_replaygain(void *data)
772 {
773 	player_set_rg((replaygain + 1) % 5);
774 }
775 
get_replaygain_limit(void * data,char * buf,size_t size)776 static void get_replaygain_limit(void *data, char *buf, size_t size)
777 {
778 	strscpy(buf, bool_names[replaygain_limit], size);
779 }
780 
set_replaygain_limit(void * data,const char * buf)781 static void set_replaygain_limit(void *data, const char *buf)
782 {
783 	int tmp;
784 
785 	if (!parse_bool(buf, &tmp))
786 		return;
787 	player_set_rg_limit(tmp);
788 }
789 
toggle_replaygain_limit(void * data)790 static void toggle_replaygain_limit(void *data)
791 {
792 	player_set_rg_limit(replaygain_limit ^ 1);
793 }
794 
get_resume(void * data,char * buf,size_t size)795 static void get_resume(void *data, char *buf, size_t size)
796 {
797 	strscpy(buf, bool_names[resume_cmus], size);
798 }
799 
set_resume(void * data,const char * buf)800 static void set_resume(void *data, const char *buf)
801 {
802 	parse_bool(buf, &resume_cmus);
803 }
804 
toggle_resume(void * data)805 static void toggle_resume(void *data)
806 {
807 	resume_cmus ^= 1;
808 }
809 
get_show_hidden(void * data,char * buf,size_t size)810 static void get_show_hidden(void *data, char *buf, size_t size)
811 {
812 	strscpy(buf, bool_names[show_hidden], size);
813 }
814 
set_show_hidden(void * data,const char * buf)815 static void set_show_hidden(void *data, const char *buf)
816 {
817 	if (!parse_bool(buf, &show_hidden))
818 		return;
819 	browser_reload();
820 }
821 
toggle_show_hidden(void * data)822 static void toggle_show_hidden(void *data)
823 {
824 	show_hidden ^= 1;
825 	browser_reload();
826 }
827 
828 static void set_show_all_tracks_int(int); /* defined below */
829 
get_auto_expand_albums_follow(void * data,char * buf,size_t size)830 static void get_auto_expand_albums_follow(void *data, char *buf, size_t size)
831 {
832 	strscpy(buf, bool_names[auto_expand_albums_follow], size);
833 }
834 
set_auto_expand_albums_follow_int(int value)835 static void set_auto_expand_albums_follow_int(int value)
836 {
837 	auto_expand_albums_follow = !!value;
838 	if (!auto_expand_albums_follow && !show_all_tracks)
839 		set_show_all_tracks_int(1);
840 }
841 
set_auto_expand_albums_follow(void * data,const char * buf)842 static void set_auto_expand_albums_follow(void *data, const char *buf)
843 {
844 	int tmp = 0;
845 	parse_bool(buf, &tmp);
846 	set_auto_expand_albums_follow_int(tmp);
847 }
848 
toggle_auto_expand_albums_follow(void * data)849 static void toggle_auto_expand_albums_follow(void *data)
850 {
851 	set_auto_expand_albums_follow_int(!auto_expand_albums_follow);
852 }
853 
get_auto_expand_albums_search(void * data,char * buf,size_t size)854 static void get_auto_expand_albums_search(void *data, char *buf, size_t size)
855 {
856 	strscpy(buf, bool_names[auto_expand_albums_search], size);
857 }
858 
set_auto_expand_albums_search_int(int value)859 static void set_auto_expand_albums_search_int(int value)
860 {
861 	auto_expand_albums_search = !!value;
862 	if (!auto_expand_albums_search && !show_all_tracks)
863 		set_show_all_tracks_int(1);
864 }
865 
set_auto_expand_albums_search(void * data,const char * buf)866 static void set_auto_expand_albums_search(void *data, const char *buf)
867 {
868 	int tmp = 0;
869 	parse_bool(buf, &tmp);
870 	set_auto_expand_albums_search_int(tmp);
871 }
872 
toggle_auto_expand_albums_search(void * data)873 static void toggle_auto_expand_albums_search(void *data)
874 {
875 	set_auto_expand_albums_search_int(!auto_expand_albums_search);
876 }
877 
get_auto_expand_albums_selcur(void * data,char * buf,size_t size)878 static void get_auto_expand_albums_selcur(void *data, char *buf, size_t size)
879 {
880 	strscpy(buf, bool_names[auto_expand_albums_selcur], size);
881 }
882 
set_auto_expand_albums_selcur_int(int value)883 static void set_auto_expand_albums_selcur_int(int value)
884 {
885 	auto_expand_albums_selcur = !!value;
886 	if (!auto_expand_albums_selcur && !show_all_tracks)
887 		set_show_all_tracks_int(1);
888 }
889 
set_auto_expand_albums_selcur(void * data,const char * buf)890 static void set_auto_expand_albums_selcur(void *data, const char *buf)
891 {
892 	int tmp = 0;
893 	parse_bool(buf, &tmp);
894 	set_auto_expand_albums_selcur_int(tmp);
895 }
896 
toggle_auto_expand_albums_selcur(void * data)897 static void toggle_auto_expand_albums_selcur(void *data)
898 {
899 	set_auto_expand_albums_selcur_int(!auto_expand_albums_selcur);
900 }
901 
902 
get_show_all_tracks(void * data,char * buf,size_t size)903 static void get_show_all_tracks(void *data, char *buf, size_t size)
904 {
905 	strscpy(buf, bool_names[show_all_tracks], size);
906 }
907 
set_show_all_tracks_int(int value)908 static void set_show_all_tracks_int(int value)
909 {
910 	value = !!value;
911 	if (show_all_tracks == value)
912 		return;
913 	show_all_tracks = value;
914 	if (!show_all_tracks) {
915 		if  (!auto_expand_albums_follow)
916 			set_auto_expand_albums_follow_int(1);
917 		if  (!auto_expand_albums_search)
918 			set_auto_expand_albums_search_int(1);
919 		if  (!auto_expand_albums_selcur)
920 			set_auto_expand_albums_selcur_int(1);
921 	}
922 	tree_sel_update(0);
923 }
924 
set_show_all_tracks(void * data,const char * buf)925 static void set_show_all_tracks(void *data, const char *buf)
926 {
927 	int tmp = 0;
928 	parse_bool(buf, &tmp);
929 	set_show_all_tracks_int(tmp);
930 }
931 
toggle_show_all_tracks(void * data)932 static void toggle_show_all_tracks(void *data)
933 {
934 	set_show_all_tracks_int(!show_all_tracks);
935 }
936 
get_show_current_bitrate(void * data,char * buf,size_t size)937 static void get_show_current_bitrate(void *data, char *buf, size_t size)
938 {
939 	strscpy(buf, bool_names[show_current_bitrate], size);
940 }
941 
set_show_current_bitrate(void * data,const char * buf)942 static void set_show_current_bitrate(void *data, const char *buf)
943 {
944 	if (parse_bool(buf, &show_current_bitrate))
945 		update_statusline();
946 }
947 
toggle_show_current_bitrate(void * data)948 static void toggle_show_current_bitrate(void *data)
949 {
950 	show_current_bitrate ^= 1;
951 	update_statusline();
952 }
953 
get_show_playback_position(void * data,char * buf,size_t size)954 static void get_show_playback_position(void *data, char *buf, size_t size)
955 {
956 	strscpy(buf, bool_names[show_playback_position], size);
957 }
958 
set_show_playback_position(void * data,const char * buf)959 static void set_show_playback_position(void *data, const char *buf)
960 {
961 	if (!parse_bool(buf, &show_playback_position))
962 		return;
963 	update_statusline();
964 }
965 
toggle_show_playback_position(void * data)966 static void toggle_show_playback_position(void *data)
967 {
968 	show_playback_position ^= 1;
969 	update_statusline();
970 }
971 
get_show_remaining_time(void * data,char * buf,size_t size)972 static void get_show_remaining_time(void *data, char *buf, size_t size)
973 {
974 	strscpy(buf, bool_names[show_remaining_time], size);
975 }
976 
set_show_remaining_time(void * data,const char * buf)977 static void set_show_remaining_time(void *data, const char *buf)
978 {
979 	if (!parse_bool(buf, &show_remaining_time))
980 		return;
981 	update_statusline();
982 }
983 
toggle_show_remaining_time(void * data)984 static void toggle_show_remaining_time(void *data)
985 {
986 	show_remaining_time ^= 1;
987 	update_statusline();
988 }
989 
get_set_term_title(void * data,char * buf,size_t size)990 static void get_set_term_title(void *data, char *buf, size_t size)
991 {
992 	strscpy(buf, bool_names[set_term_title], size);
993 }
994 
set_set_term_title(void * data,const char * buf)995 static void set_set_term_title(void *data, const char *buf)
996 {
997 	parse_bool(buf, &set_term_title);
998 }
999 
toggle_set_term_title(void * data)1000 static void toggle_set_term_title(void *data)
1001 {
1002 	set_term_title ^= 1;
1003 }
1004 
get_shuffle(void * data,char * buf,size_t size)1005 static void get_shuffle(void *data, char *buf, size_t size)
1006 {
1007 	strscpy(buf, bool_names[shuffle], size);
1008 }
1009 
set_shuffle(void * data,const char * buf)1010 static void set_shuffle(void *data, const char *buf)
1011 {
1012 	int old = shuffle;
1013 	if (!parse_bool(buf, &shuffle))
1014 		return;
1015 	if (old != shuffle)
1016 		mpris_shuffle_changed();
1017 	update_statusline();
1018 }
1019 
toggle_shuffle(void * data)1020 static void toggle_shuffle(void *data)
1021 {
1022 	shuffle ^= 1;
1023 	mpris_shuffle_changed();
1024 	update_statusline();
1025 }
1026 
get_softvol(void * data,char * buf,size_t size)1027 static void get_softvol(void *data, char *buf, size_t size)
1028 {
1029 	strscpy(buf, bool_names[soft_vol], size);
1030 }
1031 
do_set_softvol(int soft)1032 static void do_set_softvol(int soft)
1033 {
1034 	if (!soft_vol)
1035 		mixer_close();
1036 	player_set_soft_vol(soft);
1037 	if (!soft_vol)
1038 		mixer_open();
1039 	update_statusline();
1040 }
1041 
set_softvol(void * data,const char * buf)1042 static void set_softvol(void *data, const char *buf)
1043 {
1044 	int soft;
1045 
1046 	if (!parse_bool(buf, &soft))
1047 		return;
1048 	do_set_softvol(soft);
1049 }
1050 
toggle_softvol(void * data)1051 static void toggle_softvol(void *data)
1052 {
1053 	do_set_softvol(soft_vol ^ 1);
1054 }
1055 
get_wrap_search(void * data,char * buf,size_t size)1056 static void get_wrap_search(void *data, char *buf, size_t size)
1057 {
1058 	strscpy(buf, bool_names[wrap_search], size);
1059 }
1060 
set_wrap_search(void * data,const char * buf)1061 static void set_wrap_search(void *data, const char *buf)
1062 {
1063 	parse_bool(buf, &wrap_search);
1064 }
1065 
toggle_wrap_search(void * data)1066 static void toggle_wrap_search(void *data)
1067 {
1068 	wrap_search ^= 1;
1069 }
1070 
get_skip_track_info(void * data,char * buf,size_t size)1071 static void get_skip_track_info(void *data, char *buf, size_t size)
1072 {
1073 	strscpy(buf, bool_names[skip_track_info], size);
1074 }
1075 
set_skip_track_info(void * data,const char * buf)1076 static void set_skip_track_info(void *data, const char *buf)
1077 {
1078 	parse_bool(buf, &skip_track_info);
1079 }
1080 
toggle_skip_track_info(void * data)1081 static void toggle_skip_track_info(void *data)
1082 {
1083 	skip_track_info ^= 1;
1084 }
1085 
update_mouse(void)1086 void update_mouse(void)
1087 {
1088 	if (mouse) {
1089 		mouseinterval(25);
1090 		mousemask(BUTTON_CTRL | BUTTON_ALT
1091 		  | BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_CLICKED
1092 		  | BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED
1093 		  | BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_CLICKED
1094 		  | BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_CLICKED
1095 		  | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED
1096 		  | BUTTON4_PRESSED | BUTTON4_RELEASED | BUTTON4_CLICKED
1097 		  | BUTTON4_DOUBLE_CLICKED | BUTTON4_TRIPLE_CLICKED
1098 #if NCURSES_MOUSE_VERSION >= 2
1099 		  | BUTTON5_PRESSED | BUTTON5_RELEASED | BUTTON5_CLICKED
1100 		  | BUTTON5_DOUBLE_CLICKED | BUTTON5_TRIPLE_CLICKED
1101 #endif
1102 		  , NULL);
1103 	} else {
1104 		mousemask(0, NULL);
1105 	}
1106 }
1107 
get_mouse(void * data,char * buf,size_t size)1108 static void get_mouse(void *data, char *buf, size_t size)
1109 {
1110 	strscpy(buf, bool_names[mouse], size);
1111 }
1112 
set_mouse(void * data,const char * buf)1113 static void set_mouse(void *data, const char *buf)
1114 {
1115 	parse_bool(buf, &mouse);
1116 	update_mouse();
1117 }
1118 
toggle_mouse(void * data)1119 static void toggle_mouse(void *data)
1120 {
1121 	mouse ^= 1;
1122 	update_mouse();
1123 }
1124 
get_mpris(void * data,char * buf,size_t size)1125 static void get_mpris(void *data, char *buf, size_t size)
1126 {
1127 	strscpy(buf, bool_names[mpris], size);
1128 }
1129 
set_mpris(void * data,const char * buf)1130 static void set_mpris(void *data, const char *buf)
1131 {
1132 	parse_bool(buf, &mpris);
1133 }
1134 
toggle_mpris(void * data)1135 static void toggle_mpris(void *data)
1136 {
1137 	mpris ^= 1;
1138 }
1139 
get_time_show_leading_zero(void * data,char * buf,size_t size)1140 static void get_time_show_leading_zero(void *data, char *buf, size_t size)
1141 {
1142 	strscpy(buf, bool_names[time_show_leading_zero], size);
1143 }
1144 
set_time_show_leading_zero(void * data,const char * buf)1145 static void set_time_show_leading_zero(void *data, const char *buf)
1146 {
1147 	if (!parse_bool(buf, &time_show_leading_zero))
1148 		return;
1149 	update_statusline();
1150 }
1151 
toggle_time_show_leading_zero(void * data)1152 static void toggle_time_show_leading_zero(void *data)
1153 {
1154 	time_show_leading_zero ^= 1;
1155 	update_statusline();
1156 }
1157 
get_lib_add_filter(void * data,char * buf,size_t size)1158 static void get_lib_add_filter(void *data, char *buf, size_t size)
1159 {
1160 	strscpy(buf, lib_add_filter ? lib_add_filter : "", size);
1161 }
1162 
set_lib_add_filter(void * data,const char * buf)1163 static void set_lib_add_filter(void *data, const char *buf)
1164 {
1165 	struct expr *expr = NULL;
1166 
1167 	if (strlen(buf) != 0) {
1168 		/* parse expression if non-empty string given */
1169 		expr = expr_parse(buf);
1170 
1171 		if (!expr)
1172 			return;
1173 	}
1174 
1175 	free(lib_add_filter);
1176 	lib_add_filter = xstrdup(buf);
1177 
1178 	lib_set_add_filter(expr);
1179 }
1180 
get_stop_after_queue(void * data,char * buf,size_t size)1181 static void get_stop_after_queue(void *data, char *buf, size_t size)
1182 {
1183 	strscpy(buf, bool_names[stop_after_queue], size);
1184 }
1185 
set_stop_after_queue(void * data,const char * buf)1186 static void set_stop_after_queue(void *data, const char *buf)
1187 {
1188 	parse_bool(buf, &stop_after_queue);
1189 }
1190 
toggle_stop_after_queue(void * data)1191 static void toggle_stop_after_queue(void *data)
1192 {
1193 	stop_after_queue ^= 1;
1194 }
1195 
1196 /* }}} */
1197 
1198 /* special callbacks (id set) {{{ */
1199 
1200 static const char * const color_enum_names[1 + 8 * 2 + 1] = {
1201 	"default",
1202 	"black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray",
1203 	"darkgray", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "white",
1204 	NULL
1205 };
1206 
get_color(void * data,char * buf,size_t size)1207 static void get_color(void *data, char *buf, size_t size)
1208 {
1209 	int val = *(int *)data;
1210 	if (val < 16) {
1211 		strscpy(buf, color_enum_names[val + 1], size);
1212 	} else {
1213 		buf_int(buf, val, size);
1214 	}
1215 }
1216 
set_color(void * data,const char * buf)1217 static void set_color(void *data, const char *buf)
1218 {
1219 	int color;
1220 
1221 	if (!parse_enum(buf, -1, 255, color_enum_names, &color))
1222 		return;
1223 
1224 	*(int *)data = color;
1225 	update_colors();
1226 	update_full();
1227 }
1228 
get_start_view(void * data,char * buf,size_t size)1229 static void get_start_view(void *data, char *buf, size_t size)
1230 {
1231 	strscpy(buf, view_names[start_view], size);
1232 }
1233 
set_start_view(void * data,const char * buf)1234 static void set_start_view(void *data, const char *buf)
1235 {
1236 	int view;
1237 
1238 	if (parse_enum(buf, 0, NR_VIEWS - 1, view_names, &view)) {
1239 		start_view = view;
1240 	}
1241 }
1242 
get_attr(void * data,char * buf,size_t size)1243 static void get_attr(void *data, char *buf, size_t size)
1244 {
1245 	int attr = *(int *)data;
1246 
1247 	if (attr == 0) {
1248 		strscpy(buf, "default", size);
1249 		return;
1250 	}
1251 
1252 	const char *standout = "";
1253 	const char *underline = "";
1254 	const char *reverse = "";
1255 	const char *blink = "";
1256 	const char *bold = "";
1257 
1258 	if (attr & A_STANDOUT)
1259 		standout = "standout|";
1260 	if (attr & A_UNDERLINE)
1261 		underline = "underline|";
1262 	if (attr & A_REVERSE)
1263 		reverse = "reverse|";
1264 	if (attr & A_BLINK)
1265 		blink = "blink|";
1266 	if (attr & A_BOLD)
1267 		bold = "bold|";
1268 
1269 	size_t len = snprintf(buf, size, "%s%s%s%s%s", standout, underline, reverse,
1270 			blink, bold);
1271 
1272 	if (0 < len && len < size)
1273 		buf[len - 1] = 0;
1274 }
1275 
set_attr(void * data,const char * buf)1276 static void set_attr(void *data, const char *buf)
1277 {
1278 	int attr = 0;
1279 	size_t i = 0;
1280 	size_t offset = 0;
1281 	size_t length = 0;
1282 	char*  current;
1283 
1284 	do {
1285 		if (buf[i] == '|' || buf[i] == '\0') {
1286 			current = xstrndup(&buf[offset], length);
1287 
1288 			if (strcmp(current, "default") == 0)
1289 				attr |= A_NORMAL;
1290 			else if (strcmp(current, "standout") == 0)
1291 				attr |= A_STANDOUT;
1292 			else if (strcmp(current, "underline") == 0)
1293 				attr |= A_UNDERLINE;
1294 			else if (strcmp(current, "reverse") == 0)
1295 				attr |= A_REVERSE;
1296 			else if (strcmp(current, "blink") == 0)
1297 				attr |= A_BLINK;
1298 			else if (strcmp(current, "bold") == 0)
1299 				attr |= A_BOLD;
1300 
1301 			free(current);
1302 
1303 			offset = i;
1304 			length = -1;
1305 		}
1306 
1307 		i++;
1308 		length++;
1309 	} while (buf[i - 1] != '\0');
1310 
1311 	*(int *)data = attr;
1312 	update_colors();
1313 	update_full();
1314 }
1315 
id_to_fmt(enum format_id id)1316 static char **id_to_fmt(enum format_id id)
1317 {
1318 	switch (id) {
1319 	case FMT_CURRENT_ALT:
1320 		return &current_alt_format;
1321 	case FMT_PLAYLIST_ALT:
1322 		return &list_win_alt_format;
1323 	case FMT_TITLE_ALT:
1324 		return &window_title_alt_format;
1325 	case FMT_TRACKWIN_ALT:
1326 		return &track_win_alt_format;
1327 	case FMT_CURRENT:
1328 		return &current_format;
1329 	case FMT_PLAYLIST:
1330 		return &list_win_format;
1331 	case FMT_PLAYLIST_VA:
1332 		return &list_win_format_va;
1333 	case FMT_TITLE:
1334 		return &window_title_format;
1335 	case FMT_TRACKWIN:
1336 		return &track_win_format;
1337 	case FMT_TRACKWIN_ALBUM:
1338 		return &track_win_album_format;
1339 	case FMT_TRACKWIN_VA:
1340 		return &track_win_format_va;
1341 	case FMT_TREEWIN:
1342 		return &tree_win_format;
1343 	case FMT_TREEWIN_ARTIST:
1344 		return &tree_win_artist_format;
1345 	case FMT_STATUSLINE:
1346 		return &statusline_format;
1347 	default:
1348 		die("unhandled format code: %d\n", id);
1349 	}
1350 	return NULL;
1351 }
1352 
get_format(void * data,char * buf,size_t size)1353 static void get_format(void *data, char *buf, size_t size)
1354 {
1355 	char **fmtp = data;
1356 
1357 	strscpy(buf, *fmtp, size);
1358 }
1359 
set_format(void * data,const char * buf)1360 static void set_format(void *data, const char *buf)
1361 {
1362 	char **fmtp = data;
1363 
1364 	if (!track_format_valid(buf)) {
1365 		error_msg("invalid format string");
1366 		return;
1367 	}
1368 	free(*fmtp);
1369 	*fmtp = xstrdup(buf);
1370 
1371 	update_full();
1372 }
1373 
1374 /* }}} */
1375 
1376 #define DN(name) { #name, get_ ## name, set_ ## name, NULL, 0 },
1377 #define DN_FLAGS(name, flags) { #name, get_ ## name, set_ ## name, NULL, flags },
1378 #define DT(name) { #name, get_ ## name, set_ ## name, toggle_ ## name, 0 },
1379 
1380 static const struct {
1381 	const char *name;
1382 	opt_get_cb get;
1383 	opt_set_cb set;
1384 	opt_toggle_cb toggle;
1385 	unsigned int flags;
1386 } simple_options[] = {
1387 	DT(aaa_mode)
1388 	DT(auto_reshuffle)
1389 	DN_FLAGS(device, OPT_PROGRAM_PATH)
1390 	DN(buffer_seconds)
1391 	DN(scroll_offset)
1392 	DN(rewind_offset)
1393 	DT(confirm_run)
1394 	DT(continue)
1395 	DT(continue_album)
1396 	DT(smart_artist_sort)
1397 	DN(id3_default_charset)
1398 	DN(icecast_default_charset)
1399 	DN(lib_sort)
1400 	DN(output_plugin)
1401 	DN(passwd)
1402 	DN(pl_sort)
1403 	DT(play_library)
1404 	DT(play_sorted)
1405 	DT(display_artist_sort_name)
1406 	DT(repeat)
1407 	DT(repeat_current)
1408 	DT(replaygain)
1409 	DT(replaygain_limit)
1410 	DN(replaygain_preamp)
1411 	DT(resume)
1412 	DT(show_hidden)
1413 	DT(auto_expand_albums_follow)
1414 	DT(auto_expand_albums_search)
1415 	DT(auto_expand_albums_selcur)
1416 	DT(show_all_tracks)
1417 	DT(show_current_bitrate)
1418 	DT(show_playback_position)
1419 	DT(show_remaining_time)
1420 	DT(set_term_title)
1421 	DT(shuffle)
1422 	DT(follow)
1423 	DT(softvol)
1424 	DN(softvol_state)
1425 	DN_FLAGS(status_display_program, OPT_PROGRAM_PATH)
1426 	DT(wrap_search)
1427 	DT(skip_track_info)
1428 	DT(mouse)
1429 	DT(mpris)
1430 	DT(time_show_leading_zero)
1431 	DN(lib_add_filter)
1432 	DN(start_view)
1433 	DT(stop_after_queue)
1434 	DN(tree_width_percent)
1435 	DN(tree_width_max)
1436 	{ NULL, NULL, NULL, NULL, 0 }
1437 };
1438 
1439 static const char * const color_names[NR_COLORS] = {
1440 	"color_cmdline_bg",
1441 	"color_cmdline_fg",
1442 	"color_error",
1443 	"color_info",
1444 	"color_separator",
1445 	"color_statusline_bg",
1446 	"color_statusline_fg",
1447 	"color_titleline_bg",
1448 	"color_titleline_fg",
1449 	"color_win_bg",
1450 	"color_win_cur",
1451 	"color_win_cur_sel_bg",
1452 	"color_win_cur_sel_fg",
1453 	"color_win_dir",
1454 	"color_win_fg",
1455 	"color_win_inactive_cur_sel_bg",
1456 	"color_win_inactive_cur_sel_fg",
1457 	"color_win_inactive_sel_bg",
1458 	"color_win_inactive_sel_fg",
1459 	"color_win_sel_bg",
1460 	"color_win_sel_fg",
1461 	"color_win_title_bg",
1462 	"color_win_title_fg",
1463 	"color_trackwin_album_bg",
1464 	"color_trackwin_album_fg",
1465 };
1466 
1467 static const char * const attr_names[NR_ATTRS] = {
1468 	"color_cmdline_attr",
1469 	"color_statusline_attr",
1470 	"color_titleline_attr",
1471 	"color_win_attr",
1472 	"color_win_cur_sel_attr",
1473 	"color_cur_sel_attr",
1474 	"color_win_inactive_cur_sel_attr",
1475 	"color_win_inactive_sel_attr",
1476 	"color_win_sel_attr",
1477 	"color_win_title_attr",
1478 	"color_trackwin_album_attr",
1479 	"color_win_cur_attr",
1480 };
1481 
1482 LIST_HEAD(option_head);
1483 int nr_options = 0;
1484 
option_add(const char * name,const void * data,opt_get_cb get,opt_set_cb set,opt_toggle_cb toggle,unsigned int flags)1485 void option_add(const char *name, const void *data, opt_get_cb get,
1486 		opt_set_cb set, opt_toggle_cb toggle, unsigned int flags)
1487 {
1488 	struct cmus_opt *opt = xnew(struct cmus_opt, 1);
1489 	struct list_head *item;
1490 
1491 	opt->name = name;
1492 	opt->data = (void *)data;
1493 	opt->get = get;
1494 	opt->set = set;
1495 	opt->toggle = toggle;
1496 	opt->flags = flags;
1497 
1498 	item = option_head.next;
1499 	while (item != &option_head) {
1500 		struct cmus_opt *o = container_of(item, struct cmus_opt, node);
1501 
1502 		if (strcmp(name, o->name) < 0)
1503 			break;
1504 		item = item->next;
1505 	}
1506 	/* add before item */
1507 	list_add_tail(&opt->node, item);
1508 	nr_options++;
1509 }
1510 
option_find(const char * name)1511 struct cmus_opt *option_find(const char *name)
1512 {
1513 	struct cmus_opt *opt = option_find_silent(name);
1514 	if (opt == NULL)
1515 		error_msg("no such option %s", name);
1516 	return opt;
1517 }
1518 
option_find_silent(const char * name)1519 struct cmus_opt *option_find_silent(const char *name)
1520 {
1521 	struct cmus_opt *opt;
1522 
1523 	list_for_each_entry(opt, &option_head, node) {
1524 		if (strcmp(name, opt->name) == 0)
1525 			return opt;
1526 	}
1527 	return NULL;
1528 }
1529 
option_set(const char * name,const char * value)1530 void option_set(const char *name, const char *value)
1531 {
1532 	struct cmus_opt *opt = option_find(name);
1533 
1534 	if (opt)
1535 		opt->set(opt->data, value);
1536 }
1537 
options_add(void)1538 void options_add(void)
1539 {
1540 	int i;
1541 
1542 	for (i = 0; simple_options[i].name; i++)
1543 		option_add(simple_options[i].name, NULL, simple_options[i].get,
1544 				simple_options[i].set, simple_options[i].toggle,
1545 				simple_options[i].flags);
1546 
1547 	for (i = 0; i < NR_FMTS; i++)
1548 		option_add(str_defaults[i].name, id_to_fmt(i), get_format,
1549 				set_format, NULL, 0);
1550 
1551 	for (i = 0; i < NR_COLORS; i++)
1552 		option_add(color_names[i], &colors[i], get_color, set_color,
1553 				NULL, 0);
1554 
1555 	for (i = 0; i < NR_ATTRS; i++)
1556 		option_add(attr_names[i], &attrs[i], get_attr, set_attr, NULL,
1557 				0);
1558 
1559 	ip_add_options();
1560 	op_add_options();
1561 }
1562 
handle_line(void * data,const char * line)1563 static int handle_line(void *data, const char *line)
1564 {
1565 	run_command(line);
1566 	return 0;
1567 }
1568 
source_file(const char * filename)1569 int source_file(const char *filename)
1570 {
1571 	return file_for_each_line(filename, handle_line, NULL);
1572 }
1573 
options_load(void)1574 void options_load(void)
1575 {
1576 	char filename[512];
1577 	int i;
1578 
1579 	/* initialize those that can't be statically initialized */
1580 	cdda_device = get_default_cdda_device();
1581 	for (i = 0; str_defaults[i].name; i++)
1582 		option_set(str_defaults[i].name, str_defaults[i].value);
1583 
1584 	/* load autosave config */
1585 	snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
1586 	if (source_file(filename) == -1) {
1587 		char *def = xstrjoin(cmus_data_dir, "/rc");
1588 
1589 		if (errno != ENOENT)
1590 			error_msg("loading %s: %s", filename, strerror(errno));
1591 
1592 		/* load defaults */
1593 		if (source_file(def) == -1)
1594 			die_errno("loading %s", def);
1595 
1596 		free(def);
1597 	}
1598 
1599 	/* load optional static config */
1600 	snprintf(filename, sizeof(filename), "%s/rc", cmus_config_dir);
1601 	if (source_file(filename) == -1) {
1602 		if (errno != ENOENT)
1603 			error_msg("loading %s: %s", filename, strerror(errno));
1604 	}
1605 }
1606 
options_exit(void)1607 void options_exit(void)
1608 {
1609 	struct cmus_opt *opt;
1610 	struct filter_entry *filt;
1611 	char filename_tmp[512];
1612 	char filename[512];
1613 	FILE *f;
1614 	int i;
1615 
1616 	snprintf(filename_tmp, sizeof(filename_tmp), "%s/autosave.tmp", cmus_config_dir);
1617 	f = fopen(filename_tmp, "w");
1618 	if (f == NULL) {
1619 		warn_errno("creating %s", filename_tmp);
1620 		return;
1621 	}
1622 
1623 	/* save options */
1624 	list_for_each_entry(opt, &option_head, node) {
1625 		char buf[OPTION_MAX_SIZE];
1626 
1627 		buf[0] = 0;
1628 		opt->get(opt->data, buf, OPTION_MAX_SIZE);
1629 		fprintf(f, "set %s=%s\n", opt->name, buf);
1630 	}
1631 
1632 	/* save key bindings */
1633 	for (i = 0; i < NR_CTXS; i++) {
1634 		struct binding *b = key_bindings[i];
1635 
1636 		while (b) {
1637 			fprintf(f, "bind %s %s %s\n", key_context_names[i], b->key->name, b->cmd);
1638 			b = b->next;
1639 		}
1640 	}
1641 
1642 	/* save filters */
1643 	list_for_each_entry(filt, &filters_head, node)
1644 		fprintf(f, "fset %s=%s\n", filt->name, filt->filter);
1645 	fprintf(f, "factivate");
1646 	list_for_each_entry(filt, &filters_head, node) {
1647 		switch (filt->act_stat) {
1648 		case FS_YES:
1649 			fprintf(f, " %s", filt->name);
1650 			break;
1651 		case FS_NO:
1652 			fprintf(f, " !%s", filt->name);
1653 			break;
1654 		}
1655 	}
1656 	fprintf(f, "\n");
1657 
1658 	fclose(f);
1659 
1660 	snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
1661 	i = rename(filename_tmp, filename);
1662 	if (i)
1663 		warn_errno("renaming %s to %s", filename_tmp, filename);
1664 }
1665 
1666 struct resume {
1667 	enum player_status status;
1668 	char *filename;
1669 	long int position;
1670 	char *lib_filename;
1671 	int view;
1672 	char *live_filter;
1673 	char *browser_dir;
1674 	char *marked_pl;
1675 };
1676 
handle_resume_line(void * data,const char * line)1677 static int handle_resume_line(void *data, const char *line)
1678 {
1679 	struct resume *resume = data;
1680 	char *cmd, *arg;
1681 
1682 	if (!parse_command(line, &cmd, &arg))
1683 		return 0;
1684 	if (!arg)
1685 		goto out;
1686 
1687 	if (strcmp(cmd, "status") == 0) {
1688 		parse_enum(arg, 0, NR_PLAYER_STATUS, player_status_names, (int *) &resume->status);
1689 	} else if (strcmp(cmd, "file") == 0) {
1690 		free(resume->filename);
1691 		resume->filename = xstrdup(unescape(arg));
1692 	} else if (strcmp(cmd, "position") == 0) {
1693 		str_to_int(arg, &resume->position);
1694 	} else if (strcmp(cmd, "lib_file") == 0) {
1695 		free(resume->lib_filename);
1696 		resume->lib_filename = xstrdup(unescape(arg));
1697 	} else if (strcmp(cmd, "view") == 0) {
1698 		parse_enum(arg, 0, NR_VIEWS, view_names, &resume->view);
1699 	} else if (strcmp(cmd, "live-filter") == 0) {
1700 		free(resume->live_filter);
1701 		resume->live_filter = xstrdup(unescape(arg));
1702 	} else if (strcmp(cmd, "browser-dir") == 0) {
1703 		free(resume->browser_dir);
1704 		resume->browser_dir = xstrdup(unescape(arg));
1705 	} else if (strcmp(cmd, "marked-pl") == 0) {
1706 		free(resume->marked_pl);
1707 		resume->marked_pl = xstrdup(unescape(arg));
1708 	}
1709 
1710 	free(arg);
1711 out:
1712 	free(cmd);
1713 	return 0;
1714 }
1715 
resume_load(void)1716 void resume_load(void)
1717 {
1718 	char filename[512];
1719 	struct track_info *ti, *old;
1720 	struct resume resume = { .status = PLAYER_STATUS_STOPPED, .view = -1 };
1721 
1722 	snprintf(filename, sizeof(filename), "%s/resume", cmus_config_dir);
1723 	if (file_for_each_line(filename, handle_resume_line, &resume) == -1) {
1724 		if (errno != ENOENT)
1725 			error_msg("loading %s: %s", filename, strerror(errno));
1726 		return;
1727 	}
1728 	if (resume.view >= 0 && resume.view != cur_view)
1729 		set_view(resume.view);
1730 	if (resume.lib_filename) {
1731 		cache_lock();
1732 		ti = old = cache_get_ti(resume.lib_filename, 0);
1733 		cache_unlock();
1734 		if (ti) {
1735 			lib_add_track(ti, NULL);
1736 			track_info_unref(ti);
1737 			lib_store_cur_track(ti);
1738 			track_info_unref(ti);
1739 			ti = lib_set_track(lib_find_track(ti));
1740 			if (ti) {
1741 				BUG_ON(ti != old);
1742 				track_info_unref(ti);
1743 				tree_sel_current(auto_expand_albums_follow);
1744 				sorted_sel_current();
1745 			}
1746 		}
1747 		free(resume.lib_filename);
1748 	}
1749 	if (resume.filename) {
1750 		cache_lock();
1751 		ti = cache_get_ti(resume.filename, 0);
1752 		cache_unlock();
1753 		if (ti) {
1754 			player_set_file(ti);
1755 			if (resume.status != PLAYER_STATUS_STOPPED)
1756 				player_seek(resume.position, 0, resume.status == PLAYER_STATUS_PLAYING);
1757 		}
1758 		free(resume.filename);
1759 	}
1760 	if (resume.live_filter) {
1761 		filters_set_live(resume.live_filter);
1762 		free(resume.live_filter);
1763 	}
1764 	if (resume.browser_dir) {
1765 		browser_chdir(resume.browser_dir);
1766 		free(resume.browser_dir);
1767 	}
1768 	if (resume.marked_pl) {
1769 		pl_set_marked_pl_by_name(resume.marked_pl);
1770 		free(resume.marked_pl);
1771 	}
1772 }
1773 
resume_exit(void)1774 void resume_exit(void)
1775 {
1776 	char filename_tmp[512];
1777 	char filename[512];
1778 	struct track_info *ti;
1779 	FILE *f;
1780 	int rc;
1781 
1782 	snprintf(filename_tmp, sizeof(filename_tmp), "%s/resume.tmp", cmus_config_dir);
1783 	f = fopen(filename_tmp, "w");
1784 	if (!f) {
1785 		warn_errno("creating %s", filename_tmp);
1786 		return;
1787 	}
1788 
1789 	fprintf(f, "status %s\n", player_status_names[player_info.status]);
1790 	ti = player_info.ti;
1791 	if (ti) {
1792 		fprintf(f, "file %s\n", escape(ti->filename));
1793 		fprintf(f, "position %d\n", player_info.pos);
1794 	}
1795 	if (lib_cur_track)
1796 		ti = tree_track_info(lib_cur_track);
1797 	else
1798 		ti = lib_get_cur_stored_track();
1799 	if (ti)
1800 		fprintf(f, "lib_file %s\n", escape(ti->filename));
1801 	fprintf(f, "view %s\n", view_names[cur_view]);
1802 	if (lib_live_filter)
1803 		fprintf(f, "live-filter %s\n", escape(lib_live_filter));
1804 	fprintf(f, "browser-dir %s\n", escape(browser_dir));
1805 
1806 	fprintf(f, "marked-pl %s\n", escape(pl_marked_pl_name()));
1807 
1808 	fclose(f);
1809 
1810 	snprintf(filename, sizeof(filename), "%s/resume", cmus_config_dir);
1811 	rc = rename(filename_tmp, filename);
1812 	if (rc)
1813 		warn_errno("renaming %s to %s", filename_tmp, filename);
1814 }
1815