1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2017 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "screen_list.h"
36 #include <glib/gstdio.h>
38 #define MAX_LINE_LENGTH 1024
39 #define COMMENT_TOKEN '#'
41 /* configuration field names */
42 #define CONF_ENABLE_COLORS "enable-colors"
43 #define CONF_SCROLL_OFFSET "scroll-offset"
44 #define CONF_AUTO_CENTER "auto-center"
45 #define CONF_WIDE_CURSOR "wide-cursor"
46 #define CONF_KEY_DEFINITION "key"
47 #define CONF_COLOR "color"
48 #define CONF_COLOR_DEFINITION "colordef"
49 #define CONF_LIST_FORMAT "list-format"
50 #define CONF_SEARCH_FORMAT "search-format"
51 #define CONF_STATUS_FORMAT "status-format"
52 #define CONF_XTERM_TITLE_FORMAT "xterm-title-format"
53 #define CONF_LIST_WRAP "wrap-around"
54 #define CONF_FIND_WRAP "find-wrap"
55 #define CONF_FIND_SHOW_LAST "find-show-last"
56 #define CONF_AUDIBLE_BELL "audible-bell"
57 #define CONF_VISIBLE_BELL "visible-bell"
58 #define CONF_BELL_ON_WRAP "bell-on-wrap"
59 #define CONF_STATUS_MESSAGE_TIME "status-message-time"
60 #define CONF_XTERM_TITLE "set-xterm-title"
61 #define CONF_ENABLE_MOUSE "enable-mouse"
62 #define CONF_CROSSFADE_TIME "crossfade-time"
63 #define CONF_SEARCH_MODE "search-mode"
64 #define CONF_HIDE_CURSOR "hide-cursor"
65 #define CONF_SEEK_TIME "seek-time"
66 #define CONF_SCREEN_LIST "screen-list"
67 #define CONF_TIMEDISPLAY_TYPE "timedisplay-type"
68 #define CONF_HOST "host"
69 #define CONF_PORT "port"
70 #define CONF_PASSWORD "password"
71 #define CONF_TIMEOUT "timeout"
72 #define CONF_LYRICS_TIMEOUT "lyrics-timeout"
73 #define CONF_SCROLL "scroll"
74 #define CONF_SCROLL_SEP "scroll-sep"
75 #define CONF_VISIBLE_BITRATE "visible-bitrate"
76 #define CONF_HARDWARE_CURSOR "hardware-cursor"
77 #define CONF_WELCOME_SCREEN_LIST "welcome-screen-list"
78 #define CONF_DISPLAY_TIME "display-time"
79 #define CONF_JUMP_PREFIX_ONLY "jump-prefix-only"
80 #define CONF_LYRICS_AUTOSAVE "lyrics-autosave"
81 #define CONF_LYRICS_SHOW_PLUGIN "lyrics-show-plugin"
82 #define CONF_TEXT_EDITOR "text-editor"
83 #define CONF_TEXT_EDITOR_ASK "text-editor-ask"
84 #define CONF_CHAT_PREFIX "chat-prefix"
85 #define CONF_SECOND_COLUMN "second-column"
89 is_space_not_null(char ch
)
91 unsigned char uch
= (unsigned char)ch
;
92 return uch
<= 0x20 && uch
> 0;
96 * Returns the first non-space character (or a pointer to the null
97 * terminator). Similar to g_strchug(), but just moves the pointer
98 * forward without modifying the string contents.
104 while (is_space_not_null(*p
))
112 return strcasecmp(str
, "yes") == 0 || strcasecmp(str
, "true") == 0 ||
113 strcasecmp(str
, "on") == 0 || strcasecmp(str
, "1") == 0;
117 print_error(const char *msg
, const char *input
)
119 fprintf(stderr
, "%s: %s ('%s')\n",
120 /* To translators: prefix for error messages */
121 _("Error"), msg
, input
);
125 parse_key_value(char *str
, char **end
)
128 if (str
[1] == '\'' || str
[2] != '\'') {
129 print_error(_("Malformed hotkey definition"), str
);
136 long value
= strtol(str
, end
, 0);
138 print_error(_("Malformed hotkey definition"), str
);
147 parse_key_definition(char *str
)
149 /* get the command name */
150 const size_t len
= strlen(str
);
153 char buf
[MAX_LINE_LENGTH
];
154 memset(buf
, 0, MAX_LINE_LENGTH
);
155 while (i
< len
&& str
[i
] != '=' && !g_ascii_isspace(str
[i
]))
158 command_t cmd
= get_key_command_from_name(buf
);
159 if(cmd
== CMD_NONE
) {
160 /* the hotkey configuration contains an unknown
162 print_error(_("Unknown command"), buf
);
166 /* skip whitespace */
167 while (i
< len
&& (str
[i
] == '=' || g_ascii_isspace(str
[i
])))
170 /* get the value part */
171 memset(buf
, 0, MAX_LINE_LENGTH
);
172 g_strlcpy(buf
, str
+i
, MAX_LINE_LENGTH
);
174 /* the hotkey configuration line is incomplete */
175 print_error(_("Incomplete hotkey configuration"), str
);
179 /* parse key values */
184 int keys
[MAX_COMMAND_KEYS
];
185 memset(keys
, 0, sizeof(int)*MAX_COMMAND_KEYS
);
186 while (i
< MAX_COMMAND_KEYS
&& *p
!= 0 &&
187 (key
= parse_key_value(p
, &p
)) >= 0) {
189 while (*p
==',' || *p
==' ' || *p
=='\t')
196 return assign_keys(cmd
, keys
);
200 parse_timedisplay_type(const char *str
)
202 if (strcmp(str
, "elapsed") == 0)
204 else if (strcmp(str
, "remaining") == 0)
207 /* translators: ncmpc supports displaying the
208 "elapsed" or "remaining" time of a song being
209 played; in this case, the configuration file
210 contained an invalid setting */
211 print_error(_("Bad time display type"), str
);
218 separate_value(char *p
)
220 char *value
= strchr(p
, '=');
222 /* an equals sign '=' was expected while parsing a
223 configuration file line */
224 fprintf(stderr
, "%s\n", _("Missing '='"));
231 return skip_spaces(value
);
235 parse_color(char *str
)
237 char *value
= separate_value(str
);
241 return colors_assign(str
, value
);
245 * Returns the first non-whitespace character after the next comma
246 * character, or the end of the string. This is used to parse comma
252 char *comma
= strchr(p
, ',');
256 comma
= skip_spaces(comma
);
258 comma
= p
+ strlen(p
);
265 parse_color_definition(char *str
)
267 char *value
= separate_value(str
);
271 /* get the command name */
272 short color
= colors_str2color(str
);
274 char buf
[MAX_LINE_LENGTH
];
275 print_error(_("Bad color name"), buf
);
279 /* parse r,g,b values */
282 for (unsigned i
= 0; i
< 3; ++i
) {
283 char *next
= after_comma(value
), *endptr
;
285 print_error(_("Incomplete color definition"), str
);
289 rgb
[i
] = strtol(value
, &endptr
, 0);
290 if (endptr
== value
|| *endptr
!= 0) {
291 print_error(_("Invalid number"), value
);
299 print_error(_("Malformed color definition"), str
);
303 return colors_define(str
, rgb
[0], rgb
[1], rgb
[2]);
308 get_format(char *str
)
310 gsize len
= strlen(str
);
312 if (str
&& str
[0]=='\"' && str
[len
-1] == '\"') {
317 return g_strdup(str
);
321 check_screen_list(char *value
)
323 char **tmp
= g_strsplit_set(value
, " \t,", 100);
324 char **screen
= NULL
;
327 while( tmp
&& tmp
[i
] ) {
328 char *name
= g_ascii_strdown(tmp
[i
], -1);
330 if (screen_lookup_name(name
) == NULL
) {
331 /* an unknown screen name was specified in the
332 configuration file */
333 print_error(_("Unknown screen name"), name
);
336 screen
= g_realloc(screen
, (j
+2)*sizeof(char *));
345 return g_strsplit_set(DEFAULT_SCREEN_LIST
, " ", 0);
351 get_search_mode(char *value
)
354 const int mode
= strtol(value
, &test
, 10);
357 if (0 <= mode
&& mode
<= 4)
361 print_error(_("Invalid search mode"),value
);
367 for (int i
= 0; value
[i
] != '\0'; i
++)
368 value
[i
] = tolower(value
[i
]);
370 // TODO: modify screen_search so that its own list of modes can be used
371 // for comparison instead of specifying them here
372 if (strcmp(value
, "title") == 0)
374 else if (strcmp(value
, "artist") == 0)
376 else if (strcmp(value
, "album") == 0)
378 else if (strcmp(value
, "filename") == 0)
380 else if (strcmp(value
, "artist+album") == 0)
384 print_error(_("Unknown search mode"),value
);
391 parse_line(char *line
)
393 size_t len
= strlen(line
), i
= 0, j
= 0;
395 /* get the name part */
396 char name
[MAX_LINE_LENGTH
];
397 while (i
< len
&& line
[i
] != '=' && !g_ascii_isspace(line
[i
]))
398 name
[j
++] = line
[i
++];
402 /* skip '=' and whitespace */
403 while (i
< len
&& (line
[i
] == '=' || g_ascii_isspace(line
[i
])))
406 /* get the value part */
407 char value
[MAX_LINE_LENGTH
];
410 value
[j
++] = line
[i
++];
414 if (!strcasecmp(CONF_KEY_DEFINITION
, name
))
415 parse_key_definition(value
);
417 else if(!strcasecmp(CONF_ENABLE_COLORS
, name
))
419 options
.enable_colors
= str2bool(value
);
423 else if (!strcasecmp(CONF_SCROLL_OFFSET
, name
))
424 options
.scroll_offset
= atoi(value
);
426 else if (!strcasecmp(CONF_AUTO_CENTER
, name
))
427 options
.auto_center
= str2bool(value
);
428 /* color assignment */
429 else if (!strcasecmp(CONF_COLOR
, name
))
436 else if (!strcasecmp(CONF_WIDE_CURSOR
, name
))
437 options
.wide_cursor
= str2bool(value
);
438 else if (strcasecmp(name
, CONF_HARDWARE_CURSOR
) == 0)
439 options
.hardware_cursor
= str2bool(value
);
440 /* welcome screen list */
441 else if (!strcasecmp(CONF_WELCOME_SCREEN_LIST
, name
))
442 options
.welcome_screen_list
= str2bool(value
);
443 /* visible bitrate */
444 else if (!strcasecmp(CONF_VISIBLE_BITRATE
, name
))
445 options
.visible_bitrate
= str2bool(value
);
446 /* timer display type */
447 else if (!strcasecmp(CONF_TIMEDISPLAY_TYPE
, name
))
448 options
.display_remaining_time
= parse_timedisplay_type(value
);
449 /* color definition */
450 else if (!strcasecmp(CONF_COLOR_DEFINITION
, name
))
452 parse_color_definition(value
);
456 /* list format string */
457 else if (!strcasecmp(CONF_LIST_FORMAT
, name
)) {
458 g_free(options
.list_format
);
459 options
.list_format
= get_format(value
);
460 /* search format string */
461 } else if (!strcasecmp(CONF_SEARCH_FORMAT
, name
)) {
462 g_free(options
.search_format
);
463 options
.search_format
= get_format(value
);
464 /* status format string */
465 } else if (!strcasecmp(CONF_STATUS_FORMAT
, name
)) {
466 g_free(options
.status_format
);
467 options
.status_format
= get_format(value
);
468 /* xterm title format string */
469 } else if (!strcasecmp(CONF_XTERM_TITLE_FORMAT
, name
)) {
470 g_free(options
.xterm_title_format
);
471 options
.xterm_title_format
= get_format(value
);
472 } else if (!strcasecmp(CONF_LIST_WRAP
, name
))
473 options
.list_wrap
= str2bool(value
);
474 else if (!strcasecmp(CONF_FIND_WRAP
, name
))
475 options
.find_wrap
= str2bool(value
);
476 else if (!strcasecmp(CONF_FIND_SHOW_LAST
,name
))
477 options
.find_show_last_pattern
= str2bool(value
);
478 else if (!strcasecmp(CONF_AUDIBLE_BELL
, name
))
479 options
.audible_bell
= str2bool(value
);
480 else if (!strcasecmp(CONF_VISIBLE_BELL
, name
))
481 options
.visible_bell
= str2bool(value
);
482 else if (!strcasecmp(CONF_BELL_ON_WRAP
, name
))
483 options
.bell_on_wrap
= str2bool(value
);
484 else if (!strcasecmp(CONF_STATUS_MESSAGE_TIME
, name
))
485 options
.status_message_time
= atoi(value
);
486 else if (!strcasecmp(CONF_XTERM_TITLE
, name
))
487 options
.enable_xterm_title
= str2bool(value
);
488 else if (!strcasecmp(CONF_ENABLE_MOUSE
, name
))
490 options
.enable_mouse
= str2bool(value
);
494 else if (!strcasecmp(CONF_CROSSFADE_TIME
, name
))
495 options
.crossfade_time
= atoi(value
);
496 else if (!strcasecmp(CONF_SEARCH_MODE
, name
))
497 options
.search_mode
= get_search_mode(value
);
498 else if (!strcasecmp(CONF_HIDE_CURSOR
, name
))
499 options
.hide_cursor
= atoi(value
);
500 else if (!strcasecmp(CONF_SEEK_TIME
, name
))
501 options
.seek_time
= atoi(value
);
502 else if (!strcasecmp(CONF_SCREEN_LIST
, name
)) {
503 g_strfreev(options
.screen_list
);
504 options
.screen_list
= check_screen_list(value
);
505 } else if (!strcasecmp(CONF_HOST
, name
))
506 options
.host
= get_format(value
);
507 else if (!strcasecmp(CONF_PORT
, name
))
508 options
.port
= atoi(get_format(value
));
509 else if (!strcasecmp(CONF_PASSWORD
, name
))
510 options
.password
= get_format(value
);
511 else if (!strcasecmp(CONF_TIMEOUT
, name
))
512 options
.timeout_ms
= atoi(get_format(value
))
513 * 1000 /* seconds -> milliseconds */;
514 else if (!strcasecmp(CONF_LYRICS_TIMEOUT
, name
))
515 #ifdef ENABLE_LYRICS_SCREEN
516 options
.lyrics_timeout
= atoi(get_format(value
));
520 else if (!strcasecmp(CONF_SCROLL
, name
))
521 options
.scroll
= str2bool(value
);
522 else if (!strcasecmp(CONF_SCROLL_SEP
, name
)) {
523 g_free(options
.scroll_sep
);
524 options
.scroll_sep
= get_format(value
);
525 } else if (!strcasecmp(CONF_DISPLAY_TIME
, name
))
526 /* obsolete, ignore */
528 else if (!strcasecmp(CONF_JUMP_PREFIX_ONLY
, name
))
532 options
.jump_prefix_only
= str2bool(value
);
534 else if (!strcasecmp(CONF_LYRICS_AUTOSAVE
, name
))
535 #ifdef ENABLE_LYRICS_SCREEN
536 options
.lyrics_autosave
= str2bool(value
);
540 else if (!strcasecmp(CONF_LYRICS_SHOW_PLUGIN
, name
))
541 #ifdef ENABLE_LYRICS_SCREEN
542 options
.lyrics_show_plugin
= str2bool(value
);
546 else if (!strcasecmp(name
, CONF_TEXT_EDITOR
))
547 #ifdef ENABLE_LYRICS_SCREEN
549 g_free(options
.text_editor
);
550 options
.text_editor
= get_format(value
);
555 else if (!strcasecmp(name
, CONF_TEXT_EDITOR_ASK
))
556 #ifdef ENABLE_LYRICS_SCREEN
557 options
.text_editor_ask
= str2bool(value
);
561 else if (!strcasecmp(name
, CONF_CHAT_PREFIX
))
562 #ifdef ENABLE_CHAT_SCREEN
563 options
.chat_prefix
= get_format(value
);
567 else if (!strcasecmp(CONF_SECOND_COLUMN
, name
))
571 options
.second_column
= str2bool(value
);
574 print_error(_("Unknown configuration parameter"), name
);
582 read_rc_file(char *filename
)
584 assert(filename
!= NULL
);
586 FILE *file
= fopen(filename
, "r");
592 char line
[MAX_LINE_LENGTH
];
593 while (fgets(line
, sizeof(line
), file
) != NULL
) {
594 char *p
= skip_spaces(line
);
596 if (*p
!= 0 && *p
!= COMMENT_TOKEN
)
597 parse_line(g_strchomp(p
));
605 check_user_conf_dir(void)
607 char *directory
= g_build_filename(g_get_home_dir(), "." PACKAGE
, NULL
);
609 if (g_file_test(directory
, G_FILE_TEST_IS_DIR
)) {
614 bool success
= g_mkdir(directory
, 0755) == 0;
620 build_user_conf_filename(void)
623 return g_build_filename(g_get_user_config_dir(), PACKAGE
, "ncmpc.conf", NULL
);
625 return g_build_filename(g_get_home_dir(), "." PACKAGE
, "config", NULL
);
630 build_system_conf_filename(void)
633 const gchar
* const *system_data_dirs
;
634 gchar
*pathname
= NULL
;
636 for (system_data_dirs
= g_get_system_config_dirs (); *system_data_dirs
!= NULL
; system_data_dirs
++)
638 pathname
= g_build_filename(*system_data_dirs
, PACKAGE
, "ncmpc.conf", NULL
);
639 if (g_file_test(pathname
, G_FILE_TEST_EXISTS
))
651 return g_build_filename(SYSCONFDIR
, PACKAGE
, "config", NULL
);
656 build_user_key_binding_filename(void)
659 return g_build_filename(g_get_user_config_dir(), PACKAGE
, "keys.conf", NULL
);
661 return g_build_filename(g_get_home_dir(), "." PACKAGE
, "keys", NULL
);
666 g_build_system_key_binding_filename(void)
669 const gchar
* const *system_data_dirs
;
670 gchar
*pathname
= NULL
;
672 for (system_data_dirs
= g_get_system_config_dirs (); *system_data_dirs
!= NULL
; system_data_dirs
++)
674 pathname
= g_build_filename(*system_data_dirs
, PACKAGE
, "keys.conf", NULL
);
675 if (g_file_test(pathname
, G_FILE_TEST_EXISTS
))
687 return g_build_filename(SYSCONFDIR
, PACKAGE
, "keys", NULL
);
692 find_config_file(void)
694 /* check for command line configuration file */
695 if (options
.config_file
!= NULL
)
696 return g_strdup(options
.config_file
);
698 /* check for user configuration ~/.ncmpc/config */
699 char *filename
= build_user_conf_filename();
700 if (g_file_test(filename
, G_FILE_TEST_IS_REGULAR
))
705 /* check for global configuration SYSCONFDIR/ncmpc/config */
706 filename
= build_system_conf_filename();
707 if (g_file_test(filename
, G_FILE_TEST_IS_REGULAR
))
717 /* check for command line key binding file */
718 if (options
.key_file
!= NULL
)
719 return g_strdup(options
.key_file
);
721 /* check for user key bindings ~/.ncmpc/keys */
722 char *filename
= build_user_key_binding_filename();
723 if (g_file_test(filename
, G_FILE_TEST_IS_REGULAR
))
728 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
729 filename
= g_build_system_key_binding_filename();
730 if (g_file_test(filename
, G_FILE_TEST_IS_REGULAR
))
738 read_configuration(void)
740 /* load configuration */
741 char *filename
= find_config_file();
742 if (filename
!= NULL
) {
743 read_rc_file(filename
);
747 /* load key bindings */
748 filename
= find_keys_file();
749 if (filename
!= NULL
) {
750 read_rc_file(filename
);