Add albumartist to valid sort keys
[cmus.git] / options.c
blobf3a27a79e772d2c440b3f2edd77a4b3df2ad2a2d
1 /*
2 * Copyright 2006 Timo Hirvonen
3 */
5 #include "options.h"
6 #include "list.h"
7 #include "utils.h"
8 #include "xmalloc.h"
9 #include "player.h"
10 #include "buffer.h"
11 #include "ui_curses.h"
12 #include "format_print.h"
13 #include "cmus.h"
14 #include "misc.h"
15 #include "lib.h"
16 #include "pl.h"
17 #include "browser.h"
18 #include "keys.h"
19 #include "filters.h"
20 #include "command_mode.h"
21 #include "file.h"
22 #include "prog.h"
23 #include "output.h"
24 #include "config/datadir.h"
26 #include <stdio.h>
27 #include <errno.h>
29 #if defined(__sun__)
30 #include <ncurses.h>
31 #else
32 #include <curses.h>
33 #endif
35 /* initialized option variables */
37 char *output_plugin = NULL;
38 char *status_display_program = NULL;
39 char *server_password;
40 int auto_reshuffle = 0;
41 int confirm_run = 1;
42 int show_hidden = 0;
43 int show_remaining_time = 0;
44 int set_term_title = 1;
45 int play_library = 1;
46 int repeat = 0;
47 int shuffle = 0;
49 int colors[NR_COLORS] = {
50 -1,
51 -1,
52 COLOR_RED | BRIGHT,
53 COLOR_YELLOW | BRIGHT,
55 COLOR_BLUE,
56 COLOR_WHITE,
57 COLOR_BLACK,
58 COLOR_BLUE,
60 COLOR_WHITE | BRIGHT,
61 -1,
62 COLOR_YELLOW | BRIGHT,
63 COLOR_BLUE,
65 COLOR_YELLOW | BRIGHT,
66 COLOR_BLUE | BRIGHT,
67 -1,
68 COLOR_WHITE,
70 COLOR_YELLOW | BRIGHT,
71 COLOR_WHITE,
72 COLOR_BLACK,
73 COLOR_BLUE,
75 COLOR_WHITE | BRIGHT,
76 COLOR_BLUE,
77 COLOR_WHITE | BRIGHT
80 /* uninitialized option variables */
81 char *track_win_format = NULL;
82 char *track_win_alt_format = NULL;
83 char *list_win_format = NULL;
84 char *list_win_alt_format = NULL;
85 char *current_format = NULL;
86 char *current_alt_format = NULL;
87 char *window_title_format = NULL;
88 char *window_title_alt_format = NULL;
89 char *id3_default_charset = NULL;
91 static void buf_int(char *buf, int val)
93 snprintf(buf, OPTION_MAX_SIZE, "%d", val);
96 static int parse_int(const char *buf, int minval, int maxval, int *val)
98 long int tmp;
100 if (str_to_int(buf, &tmp) == -1 || tmp < minval || tmp > maxval) {
101 error_msg("integer in range %d..%d expected", minval, maxval);
102 return 0;
104 *val = tmp;
105 return 1;
108 int parse_enum(const char *buf, int minval, int maxval, const char * const names[], int *val)
110 long int tmp;
111 int i;
113 if (str_to_int(buf, &tmp) == 0) {
114 if (tmp < minval || tmp > maxval)
115 goto err;
116 *val = tmp;
117 return 1;
120 for (i = 0; names[i]; i++) {
121 if (strcasecmp(buf, names[i]) == 0) {
122 *val = i + minval;
123 return 1;
126 err:
127 error_msg("name or integer in range %d..%d expected", minval, maxval);
128 return 0;
131 static const char * const bool_names[] = {
132 "false", "true", NULL
135 static int parse_bool(const char *buf, int *val)
137 return parse_enum(buf, 0, 1, bool_names, val);
140 /* this is used as id in struct cmus_opt */
141 enum format_id {
142 FMT_CURRENT_ALT,
143 FMT_PLAYLIST_ALT,
144 FMT_TITLE_ALT,
145 FMT_TRACKWIN_ALT,
146 FMT_CURRENT,
147 FMT_PLAYLIST,
148 FMT_TITLE,
149 FMT_TRACKWIN
151 #define NR_FMTS 8
153 /* callbacks for normal options {{{ */
155 #define SECOND_SIZE (44100 * 16 / 8 * 2)
156 static void get_buffer_seconds(unsigned int id, char *buf)
158 buf_int(buf, (player_get_buffer_chunks() * CHUNK_SIZE + SECOND_SIZE / 2) / SECOND_SIZE);
161 static void set_buffer_seconds(unsigned int id, const char *buf)
163 int sec;
165 if (parse_int(buf, 1, 20, &sec))
166 player_set_buffer_chunks((sec * SECOND_SIZE + CHUNK_SIZE / 2) / CHUNK_SIZE);
169 static void get_id3_default_charset(unsigned int id, char *buf)
171 strcpy(buf, id3_default_charset);
174 static void set_id3_default_charset(unsigned int id, const char *buf)
176 free(id3_default_charset);
177 id3_default_charset = xstrdup(buf);
180 static const char * const valid_sort_keys[] = {
181 "artist",
182 "album",
183 "title",
184 "tracknumber",
185 "discnumber",
186 "date",
187 "genre",
188 "filename",
189 "albumartist",
190 NULL
193 static const char **parse_sort_keys(const char *value)
195 const char **keys;
196 const char *s, *e;
197 int size = 4;
198 int pos = 0;
200 size = 4;
201 keys = xnew(const char *, size);
203 s = value;
204 while (1) {
205 char buf[32];
206 int i, len;
208 while (*s == ' ')
209 s++;
211 e = s;
212 while (*e && *e != ' ')
213 e++;
215 len = e - s;
216 if (len == 0)
217 break;
218 if (len > 31)
219 len = 31;
221 memcpy(buf, s, len);
222 buf[len] = 0;
223 s = e;
225 for (i = 0; ; i++) {
226 if (valid_sort_keys[i] == NULL) {
227 error_msg("invalid sort key '%s'", buf);
228 free(keys);
229 return NULL;
232 if (strcmp(buf, valid_sort_keys[i]) == 0)
233 break;
236 if (pos == size - 1) {
237 size *= 2;
238 keys = xrenew(const char *, keys, size);
240 keys[pos++] = valid_sort_keys[i];
242 keys[pos] = NULL;
243 return keys;
246 static void get_lib_sort(unsigned int id, char *buf)
248 strcpy(buf, lib_editable.sort_str);
251 static void set_lib_sort(unsigned int id, const char *buf)
253 const char **keys = parse_sort_keys(buf);
255 if (keys)
256 editable_set_sort_keys(&lib_editable, keys);
259 static void get_pl_sort(unsigned int id, char *buf)
261 strcpy(buf, pl_editable.sort_str);
264 static void set_pl_sort(unsigned int id, const char *buf)
266 const char **keys = parse_sort_keys(buf);
268 if (keys)
269 editable_set_sort_keys(&pl_editable, keys);
272 static void get_output_plugin(unsigned int id, char *buf)
274 char *value = player_get_op();
276 if (value)
277 strcpy(buf, value);
278 free(value);
281 static void set_output_plugin(unsigned int id, const char *buf)
283 if (ui_initialized) {
284 player_set_op(buf);
285 } else {
286 /* must set it later manually */
287 output_plugin = xstrdup(buf);
291 static void get_passwd(unsigned int id, char *buf)
293 if (server_password)
294 strcpy(buf, server_password);
297 static void set_passwd(unsigned int id, const char *buf)
299 int len = strlen(buf);
301 if (len == 0) {
302 free(server_password);
303 server_password = NULL;
304 } else if (len < 6) {
305 error_msg("unsafe password");
306 } else {
307 free(server_password);
308 server_password = xstrdup(buf);
312 static void get_softvol_state(unsigned int id, char *buf)
314 sprintf(buf, "%d %d", soft_vol_l, soft_vol_r);
317 static void set_softvol_state(unsigned int id, const char *buf)
319 char buffer[OPTION_MAX_SIZE];
320 char *ptr;
321 long int l, r;
323 strcpy(buffer, buf);
324 ptr = strchr(buffer, ' ');
325 if (!ptr)
326 goto err;
327 while (*ptr == ' ')
328 *ptr++ = 0;
330 if (str_to_int(buffer, &l) == -1 || l < 0 || l > 100)
331 goto err;
332 if (str_to_int(ptr, &r) == -1 || r < 0 || r > 100)
333 goto err;
335 if (soft_vol) {
336 /* avoid race condition */
337 player_set_volume(l, r);
338 } else {
339 /* can't use player_set_volume(), no race condition */
340 soft_vol_l = l;
341 soft_vol_r = r;
343 return;
344 err:
345 error_msg("two integers in range 0..100 expected");
348 static void get_status_display_program(unsigned int id, char *buf)
350 if (status_display_program)
351 strcpy(buf, status_display_program);
354 static void set_status_display_program(unsigned int id, const char *buf)
356 free(status_display_program);
357 status_display_program = NULL;
358 if (buf[0])
359 status_display_program = xstrdup(buf);
362 /* }}} */
364 /* callbacks for toggle options {{{ */
366 static void get_auto_reshuffle(unsigned int id, char *buf)
368 strcpy(buf, bool_names[auto_reshuffle]);
371 static void set_auto_reshuffle(unsigned int id, const char *buf)
373 parse_bool(buf, &auto_reshuffle);
376 static void toggle_auto_reshuffle(unsigned int id)
378 auto_reshuffle ^= 1;
381 static void get_continue(unsigned int id, char *buf)
383 strcpy(buf, bool_names[player_cont]);
386 static void set_continue(unsigned int id, const char *buf)
388 if (!parse_bool(buf, &player_cont))
389 return;
390 update_statusline();
393 static void toggle_continue(unsigned int id)
395 player_cont ^= 1;
396 update_statusline();
399 static void get_confirm_run(unsigned int id, char *buf)
401 strcpy(buf, bool_names[confirm_run]);
404 static void set_confirm_run(unsigned int id, const char *buf)
406 parse_bool(buf, &confirm_run);
409 static void toggle_confirm_run(unsigned int id)
411 confirm_run ^= 1;
414 const char * const view_names[NR_VIEWS + 1] = {
415 "tree", "sorted", "playlist", "queue", "browser", "filters", "settings", NULL
418 static void get_play_library(unsigned int id, char *buf)
420 strcpy(buf, bool_names[play_library]);
423 static void set_play_library(unsigned int id, const char *buf)
425 if (!parse_bool(buf, &play_library))
426 return;
427 update_statusline();
430 static void toggle_play_library(unsigned int id)
432 play_library ^= 1;
433 update_statusline();
436 static void get_play_sorted(unsigned int id, char *buf)
438 strcpy(buf, bool_names[play_sorted]);
441 static void set_play_sorted(unsigned int id, const char *buf)
443 int tmp;
445 if (!parse_bool(buf, &tmp))
446 return;
448 play_sorted = tmp;
449 update_statusline();
452 static void toggle_play_sorted(unsigned int id)
454 editable_lock();
455 play_sorted = play_sorted ^ 1;
457 /* shuffle would override play_sorted... */
458 if (play_sorted) {
459 /* play_sorted makes no sense in playlist */
460 play_library = 1;
461 shuffle = 0;
464 editable_unlock();
465 update_statusline();
468 const char * const aaa_mode_names[] = {
469 "all", "artist", "album", NULL
472 static void get_aaa_mode(unsigned int id, char *buf)
474 strcpy(buf, aaa_mode_names[aaa_mode]);
477 static void set_aaa_mode(unsigned int id, const char *buf)
479 int tmp;
481 if (!parse_enum(buf, 0, 2, aaa_mode_names, &tmp))
482 return;
484 aaa_mode = tmp;
485 update_statusline();
488 static void toggle_aaa_mode(unsigned int id)
490 editable_lock();
492 /* aaa mode makes no sense in playlist */
493 play_library = 1;
495 aaa_mode++;
496 aaa_mode %= 3;
497 editable_unlock();
498 update_statusline();
501 static void get_repeat(unsigned int id, char *buf)
503 strcpy(buf, bool_names[repeat]);
506 static void set_repeat(unsigned int id, const char *buf)
508 if (!parse_bool(buf, &repeat))
509 return;
510 update_statusline();
513 static void toggle_repeat(unsigned int id)
515 repeat ^= 1;
516 update_statusline();
519 static void get_show_hidden(unsigned int id, char *buf)
521 strcpy(buf, bool_names[show_hidden]);
524 static void set_show_hidden(unsigned int id, const char *buf)
526 if (!parse_bool(buf, &show_hidden))
527 return;
528 browser_reload();
531 static void toggle_show_hidden(unsigned int id)
533 show_hidden ^= 1;
534 browser_reload();
537 static void get_show_remaining_time(unsigned int id, char *buf)
539 strcpy(buf, bool_names[show_remaining_time]);
542 static void set_show_remaining_time(unsigned int id, const char *buf)
544 if (!parse_bool(buf, &show_remaining_time))
545 return;
546 update_statusline();
549 static void toggle_show_remaining_time(unsigned int id)
551 show_remaining_time ^= 1;
552 update_statusline();
555 static void get_set_term_title(unsigned int id, char *buf)
557 strcpy(buf, bool_names[set_term_title]);
560 static void set_set_term_title(unsigned int id, const char *buf)
562 parse_bool(buf, &set_term_title);
565 static void toggle_set_term_title(unsigned int id)
567 set_term_title ^= 1;
570 static void get_shuffle(unsigned int id, char *buf)
572 strcpy(buf, bool_names[shuffle]);
575 static void set_shuffle(unsigned int id, const char *buf)
577 if (!parse_bool(buf, &shuffle))
578 return;
579 update_statusline();
582 static void toggle_shuffle(unsigned int id)
584 shuffle ^= 1;
585 update_statusline();
588 static void get_softvol(unsigned int id, char *buf)
590 strcpy(buf, bool_names[soft_vol]);
593 static void set_softvol(unsigned int id, const char *buf)
595 int soft;
597 if (!parse_bool(buf, &soft))
598 return;
599 player_set_soft_vol(soft);
602 static void toggle_softvol(unsigned int id)
604 player_set_soft_vol(soft_vol ^ 1);
607 /* }}} */
609 /* special callbacks (id set) {{{ */
611 static const char * const color_enum_names[1 + 8 * 2 + 1] = {
612 "default",
613 "black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray",
614 "darkgray", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "white",
615 NULL
618 static void get_color(unsigned int id, char *buf)
620 int val;
622 val = colors[id];
623 if (val < 16) {
624 strcpy(buf, color_enum_names[val + 1]);
625 } else {
626 buf_int(buf, val);
630 static void set_color(unsigned int id, const char *buf)
632 int color;
634 if (!parse_enum(buf, -1, 255, color_enum_names, &color))
635 return;
637 colors[id] = color;
638 update_colors();
639 update_full();
642 static char **id_to_fmt(enum format_id id)
644 switch (id) {
645 case FMT_CURRENT_ALT:
646 return &current_alt_format;
647 case FMT_PLAYLIST_ALT:
648 return &list_win_alt_format;
649 case FMT_TITLE_ALT:
650 return &window_title_alt_format;
651 case FMT_TRACKWIN_ALT:
652 return &track_win_alt_format;
653 case FMT_CURRENT:
654 return &current_format;
655 case FMT_PLAYLIST:
656 return &list_win_format;
657 case FMT_TITLE:
658 return &window_title_format;
659 case FMT_TRACKWIN:
660 return &track_win_format;
662 return NULL;
665 static void get_format(unsigned int id, char *buf)
667 char **fmtp = id_to_fmt(id);
669 strcpy(buf, *fmtp);
672 static void set_format(unsigned int id, const char *buf)
674 char **fmtp = id_to_fmt(id);
676 if (!format_valid(buf)) {
677 error_msg("invalid format string");
678 return;
680 free(*fmtp);
681 *fmtp = xstrdup(buf);
683 update_full();
686 /* }}} */
688 #define DN(name) { #name, get_ ## name, set_ ## name, NULL },
689 #define DT(name) { #name, get_ ## name, set_ ## name, toggle_ ## name },
691 static const struct {
692 const char *name;
693 opt_get_cb get;
694 opt_set_cb set;
695 opt_toggle_cb toggle;
696 } simple_options[] = {
697 DT(aaa_mode)
698 DT(auto_reshuffle)
699 DN(buffer_seconds)
700 DT(confirm_run)
701 DT(continue)
702 DN(id3_default_charset)
703 DN(lib_sort)
704 DN(output_plugin)
705 DN(passwd)
706 DN(pl_sort)
707 DT(play_library)
708 DT(play_sorted)
709 DT(repeat)
710 DT(show_hidden)
711 DT(show_remaining_time)
712 DT(set_term_title)
713 DT(shuffle)
714 DT(softvol)
715 DN(softvol_state)
716 DN(status_display_program)
717 { NULL, NULL, NULL, NULL }
720 static const char * const color_names[NR_COLORS] = {
721 "color_cmdline_bg",
722 "color_cmdline_fg",
723 "color_error",
724 "color_info",
725 "color_separator",
726 "color_statusline_bg",
727 "color_statusline_fg",
728 "color_titleline_bg",
729 "color_titleline_fg",
730 "color_win_bg",
731 "color_win_cur",
732 "color_win_cur_sel_bg",
733 "color_win_cur_sel_fg",
734 "color_win_dir",
735 "color_win_fg",
736 "color_win_inactive_cur_sel_bg",
737 "color_win_inactive_cur_sel_fg",
738 "color_win_inactive_sel_bg",
739 "color_win_inactive_sel_fg",
740 "color_win_sel_bg",
741 "color_win_sel_fg",
742 "color_win_title_bg",
743 "color_win_title_fg"
746 /* default values for the variables which we must initialize but
747 * can't do it statically */
748 static const struct {
749 const char *name;
750 const char *value;
751 } str_defaults[] = {
752 { "altformat_current", " %F " },
753 { "altformat_playlist", " %f%= %d " },
754 { "altformat_title", "%f" },
755 { "altformat_trackwin", " %f%= %d " },
756 { "format_current", " %a - %l - %02n. %t%= %y " },
757 { "format_playlist", " %-20a %02n. %t%= %y %d " },
758 { "format_title", "%a - %l - %t (%y)" },
759 { "format_trackwin", " %02n. %t%= %y %d " },
761 { "lib_sort" , "artist album discnumber tracknumber title filename" },
762 { "pl_sort", "" },
763 { "id3_default_charset","ISO-8859-1" },
764 { NULL, NULL }
767 LIST_HEAD(option_head);
768 int nr_options = 0;
770 void option_add(const char *name, unsigned int id, opt_get_cb get,
771 opt_set_cb set, opt_toggle_cb toggle)
773 struct cmus_opt *opt = xnew(struct cmus_opt, 1);
774 struct list_head *item;
776 opt->name = name;
777 opt->id = id;
778 opt->get = get;
779 opt->set = set;
780 opt->toggle = toggle;
782 item = option_head.next;
783 while (item != &option_head) {
784 struct cmus_opt *o = container_of(item, struct cmus_opt, node);
786 if (strcmp(name, o->name) < 0)
787 break;
788 item = item->next;
790 /* add before item */
791 list_add_tail(&opt->node, item);
792 nr_options++;
795 struct cmus_opt *option_find(const char *name)
797 struct cmus_opt *opt;
799 list_for_each_entry(opt, &option_head, node) {
800 if (strcmp(name, opt->name) == 0)
801 return opt;
803 error_msg("no such option %s", name);
804 return NULL;
807 void option_set(const char *name, const char *value)
809 struct cmus_opt *opt = option_find(name);
811 if (opt)
812 opt->set(opt->id, value);
815 static void get_op_option(unsigned int id, char *buf)
817 char *val = NULL;
819 player_get_op_option(id, &val);
820 if (val) {
821 strcpy(buf, val);
822 free(val);
826 static void set_op_option(unsigned int id, const char *buf)
828 int rc = player_set_op_option(id, buf);
830 if (rc) {
831 char *msg = op_get_error_msg(rc, "setting option");
832 error_msg("%s", msg);
833 free(msg);
837 /* id is ((plugin_index << 16) | option_index) */
838 static void add_op_option(unsigned int id, const char *name)
840 option_add(xstrdup(name), id, get_op_option, set_op_option, NULL);
843 void options_add(void)
845 int i;
847 /* add options */
849 for (i = 0; simple_options[i].name; i++)
850 option_add(simple_options[i].name, 0, simple_options[i].get,
851 simple_options[i].set, simple_options[i].toggle);
853 for (i = 0; i < NR_FMTS; i++)
854 option_add(str_defaults[i].name, i, get_format, set_format, NULL);
856 for (i = 0; i < NR_COLORS; i++)
857 option_add(color_names[i], i, get_color, set_color, NULL);
859 player_for_each_op_option(add_op_option);
862 static int handle_line(void *data, const char *line)
864 run_command(line);
865 return 0;
868 int source_file(const char *filename)
870 return file_for_each_line(filename, handle_line, NULL);
873 void options_load(void)
875 char filename[512];
876 int i;
878 display_errors = 1;
880 /* initialize those that can't be statically initialized */
881 for (i = 0; str_defaults[i].name; i++)
882 option_set(str_defaults[i].name, str_defaults[i].value);
884 /* load autosave config */
885 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
886 if (source_file(filename) == -1) {
887 const char *def = DATADIR "/cmus/rc";
889 if (errno != ENOENT)
890 warn_errno("loading %s", filename);
892 /* load defaults */
893 if (source_file(def) == -1)
894 die_errno("loading %s", def);
897 /* load optional static config */
898 snprintf(filename, sizeof(filename), "%s/rc", cmus_config_dir);
899 if (source_file(filename) == -1) {
900 if (errno != ENOENT)
901 warn_errno("loading %s", filename);
905 void options_exit(void)
907 struct cmus_opt *opt;
908 struct filter_entry *filt;
909 char filename[512];
910 FILE *f;
911 int i;
913 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
914 f = fopen(filename, "w");
915 if (f == NULL) {
916 warn_errno("creating %s", filename);
917 return;
920 /* save options */
921 list_for_each_entry(opt, &option_head, node) {
922 char buf[OPTION_MAX_SIZE];
924 buf[0] = 0;
925 opt->get(opt->id, buf);
926 fprintf(f, "set %s=%s\n", opt->name, buf);
929 /* save key bindings */
930 for (i = 0; i < NR_CTXS; i++) {
931 struct binding *b = key_bindings[i];
933 while (b) {
934 fprintf(f, "bind %s %s %s\n", key_context_names[i], b->key->name, b->cmd);
935 b = b->next;
939 /* save filters */
940 list_for_each_entry(filt, &filters_head, node)
941 fprintf(f, "fset %s=%s\n", filt->name, filt->filter);
942 fprintf(f, "factivate");
943 list_for_each_entry(filt, &filters_head, node) {
944 switch (filt->act_stat) {
945 case FS_YES:
946 fprintf(f, " %s", filt->name);
947 break;
948 case FS_NO:
949 fprintf(f, " !%s", filt->name);
950 break;
953 fprintf(f, "\n");
955 fclose(f);