38d5089540e49369a25c97cca9c0030d11e95a63
1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2009 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.
18 */
20 #include "conf.h"
21 #include "config.h"
22 #include "defaults.h"
23 #include "i18n.h"
24 #include "command.h"
25 #include "colors.h"
26 #include "screen_list.h"
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <glib.h>
39 #define MAX_LINE_LENGTH 1024
40 #define COMMENT_TOKEN '#'
42 /* configuration field names */
43 #define CONF_ENABLE_COLORS "enable-colors"
44 #define CONF_AUTO_CENTER "auto-center"
45 #define CONF_WIDE_CURSOR "wide-cursor"
46 #define CONF_ENABLE_BELL "enable-bell"
47 #define CONF_KEY_DEFINITION "key"
48 #define CONF_COLOR "color"
49 #define CONF_COLOR_DEFINITION "colordef"
50 #define CONF_LIST_FORMAT "list-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_XTERM_TITLE "set-xterm-title"
59 #define CONF_ENABLE_MOUSE "enable-mouse"
60 #define CONF_CROSSFADE_TIME "crossfade-time"
61 #define CONF_SEARCH_MODE "search-mode"
62 #define CONF_HIDE_CURSOR "hide-cursor"
63 #define CONF_SEEK_TIME "seek-time"
64 #define CONF_SCREEN_LIST "screen-list"
65 #define CONF_TIMEDISPLAY_TYPE "timedisplay-type"
66 #define CONF_HOST "host"
67 #define CONF_PORT "port"
68 #define CONF_PASSWORD "password"
69 #define CONF_LYRICS_TIMEOUT "lyrics-timeout"
70 #define CONF_SHOW_SPLASH "show-splash"
71 #define CONF_SCROLL "scroll"
72 #define CONF_SCROLL_SEP "scroll-sep"
73 #define CONF_VISIBLE_BITRATE "visible-bitrate"
74 #define CONF_WELCOME_SCREEN_LIST "welcome-screen-list"
76 static bool
77 str2bool(char *str)
78 {
79 return strcasecmp(str, "yes") == 0 || strcasecmp(str, "true") == 0 ||
80 strcasecmp(str, "on") == 0 || strcasecmp(str, "1") == 0;
81 }
83 static void
84 print_error(const char *msg, const char *input)
85 {
86 fprintf(stderr, "%s: %s ('%s')\n",
87 /* To translators: prefix for error messages */
88 _("Error"), msg, input);
89 }
91 static int
92 parse_key_value(char *str, char **end)
93 {
94 if (*str == '\'') {
95 if (str[1] == '\'' || str[2] != '\'') {
96 print_error(_("Malformed hotkey definition"), str);
97 return -1;
98 }
100 *end = str + 3;
101 return str[1];
102 } else {
103 long value = strtol(str, end, 0);
104 if (*end == str) {
105 print_error(_("Malformed hotkey definition"), str);
106 return -1;
107 }
109 return (int)value;
110 }
111 }
113 static int
114 parse_key_definition(char *str)
115 {
116 char buf[MAX_LINE_LENGTH];
117 char *p;
118 size_t len = strlen(str), i;
119 int j,key;
120 int keys[MAX_COMMAND_KEYS];
121 command_t cmd;
123 /* get the command name */
124 i=0;
125 j=0;
126 memset(buf, 0, MAX_LINE_LENGTH);
127 while (i < len && str[i] != '=' && !g_ascii_isspace(str[i]))
128 buf[j++] = str[i++];
129 if( (cmd=get_key_command_from_name(buf)) == CMD_NONE ) {
130 /* the hotkey configuration contains an unknown
131 command */
132 print_error(_("Unknown command"), buf);
133 return -1;
134 }
136 /* skip whitespace */
137 while (i < len && (str[i] == '=' || g_ascii_isspace(str[i])))
138 i++;
140 /* get the value part */
141 memset(buf, 0, MAX_LINE_LENGTH);
142 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
143 if (*buf == 0) {
144 /* the hotkey configuration line is incomplete */
145 print_error(_("Incomplete hotkey configuration"), str);
146 return -1;
147 }
149 /* parse key values */
150 i = 0;
151 key = 0;
152 p = buf;
153 memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
154 while (i < MAX_COMMAND_KEYS && *p != 0 &&
155 (key = parse_key_value(p, &p)) >= 0) {
156 keys[i++] = key;
157 while (*p==',' || *p==' ' || *p=='\t')
158 p++;
159 }
161 if (key < 0)
162 return -1;
164 return assign_keys(cmd, keys);
165 }
167 static const char *
168 parse_timedisplay_type(const char *str)
169 {
170 if (!strcmp(str,"elapsed") || !strcmp(str,"remaining"))
171 return str;
172 else {
173 /* translators: ncmpc supports displaying the
174 "elapsed" or "remaining" time of a song being
175 played; in this case, the configuration file
176 contained an invalid setting */
177 print_error(_("Bad time display type"), str);
178 return DEFAULT_TIMEDISPLAY_TYPE;
179 }
180 }
182 #ifdef ENABLE_COLORS
183 static char *
184 separate_value(char *p)
185 {
186 char *value;
188 value = strchr(p, '=');
189 if (value == NULL) {
190 /* an equals sign '=' was expected while parsing a
191 configuration file line */
192 fprintf(stderr, "%s\n", _("Missing '='"));
193 return NULL;
194 }
196 *value++ = 0;
198 g_strchomp(p);
199 return g_strchug(value);
200 }
202 static int
203 parse_color(char *str)
204 {
205 char *value;
207 value = separate_value(str);
208 if (value == NULL)
209 return -1;
211 return colors_assign(str, value);
212 }
214 /**
215 * Returns the first non-whitespace character after the next comma
216 * character, or the end of the string. This is used to parse comma
217 * separated values.
218 */
219 static char *
220 after_comma(char *p)
221 {
222 char *comma = strchr(p, ',');
224 if (comma != NULL) {
225 *comma++ = 0;
226 comma = g_strchug(comma);
227 } else
228 comma = p + strlen(p);
230 g_strchomp(p);
231 return comma;
232 }
234 static int
235 parse_color_definition(char *str)
236 {
237 char buf[MAX_LINE_LENGTH];
238 char *value;
239 short color, rgb[3];
241 value = separate_value(str);
242 if (value == NULL)
243 return -1;
245 /* get the command name */
246 color = colors_str2color(str);
247 if (color < 0) {
248 print_error(_("Bad color name"), buf);
249 return -1;
250 }
252 /* parse r,g,b values */
254 for (unsigned i = 0; i < 3; ++i) {
255 char *next = after_comma(value), *endptr;
256 if (*value == 0) {
257 print_error(_("Incomplete color definition"), str);
258 return -1;
259 }
261 rgb[i] = strtol(value, &endptr, 0);
262 if (endptr == value || *endptr != 0) {
263 print_error(_("Invalid number"), value);
264 return -1;
265 }
267 value = next;
268 }
270 if (*value != 0) {
271 print_error(_("Malformed color definition"), str);
272 return -1;
273 }
275 return colors_define(str, rgb[0], rgb[1], rgb[2]);
276 }
277 #endif
279 static char *
280 get_format(char *str)
281 {
282 gsize len = strlen(str);
284 if (str && str[0]=='\"' && str[len-1] == '\"') {
285 str[len - 1] = '\0';
286 str++;
287 }
289 return g_strdup(str);
290 }
292 static char **
293 check_screen_list(char *value)
294 {
295 char **tmp = g_strsplit_set(value, " \t,", 100);
296 char **screen = NULL;
297 int i,j;
299 i=0;
300 j=0;
301 while( tmp && tmp[i] ) {
302 char *name = g_ascii_strdown(tmp[i], -1);
303 if (screen_lookup_name(name) == NULL) {
304 /* an unknown screen name was specified in the
305 configuration file */
306 print_error(_("Unknown screen name"), name);
307 free(name);
308 } else {
309 screen = g_realloc(screen, (j+2)*sizeof(char *));
310 screen[j++] = name;
311 screen[j] = NULL;
312 }
313 i++;
314 }
315 g_strfreev(tmp);
316 if( screen == NULL )
317 return g_strsplit_set(DEFAULT_SCREEN_LIST, " ", 0);
319 return screen;
320 }
322 static bool
323 parse_line(char *line)
324 {
325 size_t len = strlen(line), i = 0, j;
326 char name[MAX_LINE_LENGTH];
327 char value[MAX_LINE_LENGTH];
328 bool match_found;
330 /* get the name part */
331 j = 0;
332 while (i < len && line[i] != '=' &&
333 !g_ascii_isspace(line[i])) {
334 name[j++] = line[i++];
335 }
337 name[j] = '\0';
339 /* skip '=' and whitespace */
340 while (i < len && (line[i] == '=' || g_ascii_isspace(line[i])))
341 i++;
343 /* get the value part */
344 j = 0;
345 while (i < len)
346 value[j++] = line[i++];
347 value[j] = '\0';
349 match_found = true;
351 /* key definition */
352 if (!strcasecmp(CONF_KEY_DEFINITION, name))
353 parse_key_definition(value);
354 /* enable colors */
355 else if(!strcasecmp(CONF_ENABLE_COLORS, name))
356 #ifdef ENABLE_COLORS
357 options.enable_colors = str2bool(value);
358 #else
359 {}
360 #endif
361 /* auto center */
362 else if (!strcasecmp(CONF_AUTO_CENTER, name))
363 options.auto_center = str2bool(value);
364 /* color assignment */
365 else if (!strcasecmp(CONF_COLOR, name))
366 #ifdef ENABLE_COLORS
367 parse_color(value);
368 #else
369 {}
370 #endif
371 /* wide cursor */
372 else if (!strcasecmp(CONF_WIDE_CURSOR, name))
373 options.wide_cursor = str2bool(value);
374 /* welcome screen list */
375 else if (!strcasecmp(CONF_WELCOME_SCREEN_LIST, name))
376 options.welcome_screen_list = str2bool(value);
377 /* visible bitrate */
378 else if (!strcasecmp(CONF_VISIBLE_BITRATE, name))
379 options.visible_bitrate = str2bool(value);
380 /* timer display type */
381 else if (!strcasecmp(CONF_TIMEDISPLAY_TYPE, name)) {
382 g_free(options.timedisplay_type);
383 options.timedisplay_type=g_strdup(parse_timedisplay_type(value));
384 /* color definition */
385 } else if (!strcasecmp(CONF_COLOR_DEFINITION, name))
386 #ifdef ENABLE_COLORS
387 parse_color_definition(value);
388 #else
389 {}
390 #endif
391 /* list format string */
392 else if (!strcasecmp(CONF_LIST_FORMAT, name)) {
393 g_free(options.list_format);
394 options.list_format = get_format(value);
395 /* status format string */
396 } else if (!strcasecmp(CONF_STATUS_FORMAT, name)) {
397 g_free(options.status_format);
398 options.status_format = get_format(value);
399 /* xterm title format string */
400 } else if (!strcasecmp(CONF_XTERM_TITLE_FORMAT, name)) {
401 g_free(options.xterm_title_format);
402 options.xterm_title_format = get_format(value);
403 } else if (!strcasecmp(CONF_LIST_WRAP, name))
404 options.list_wrap = str2bool(value);
405 else if (!strcasecmp(CONF_FIND_WRAP, name))
406 options.find_wrap = str2bool(value);
407 else if (!strcasecmp(CONF_FIND_SHOW_LAST,name))
408 options.find_show_last_pattern = str2bool(value);
409 else if (!strcasecmp(CONF_AUDIBLE_BELL, name))
410 options.audible_bell = str2bool(value);
411 else if (!strcasecmp(CONF_VISIBLE_BELL, name))
412 options.visible_bell = str2bool(value);
413 else if (!strcasecmp(CONF_XTERM_TITLE, name))
414 options.enable_xterm_title = str2bool(value);
415 else if (!strcasecmp(CONF_ENABLE_MOUSE, name))
416 #ifdef HAVE_GETMOUSE
417 options.enable_mouse = str2bool(value);
418 #else
419 {}
420 #endif
421 else if (!strcasecmp(CONF_CROSSFADE_TIME, name))
422 options.crossfade_time = atoi(value);
423 else if (!strcasecmp(CONF_SEARCH_MODE, name))
424 options.search_mode = atoi(value);
425 else if (!strcasecmp(CONF_HIDE_CURSOR, name))
426 options.hide_cursor = atoi(value);
427 else if (!strcasecmp(CONF_SEEK_TIME, name))
428 options.seek_time = atoi(value);
429 else if (!strcasecmp(CONF_SCREEN_LIST, name)) {
430 g_strfreev(options.screen_list);
431 options.screen_list = check_screen_list(value);
432 } else if (!strcasecmp(CONF_SHOW_SPLASH, name)) {
433 /* the splash screen was removed */
434 } else if (!strcasecmp(CONF_HOST, name))
435 options.host = get_format(value);
436 else if (!strcasecmp(CONF_PORT, name))
437 options.port = atoi(get_format(value));
438 else if (!strcasecmp(CONF_PASSWORD, name))
439 options.password = get_format(value);
440 else if (!strcasecmp(CONF_LYRICS_TIMEOUT, name))
441 #ifdef ENABLE_LYRICS_SCREEN
442 options.lyrics_timeout = atoi(get_format(value));
443 #else
444 {}
445 #endif
446 else if (!strcasecmp(CONF_SCROLL, name))
447 options.scroll = str2bool(value);
448 else if (!strcasecmp(CONF_SCROLL_SEP, name)) {
449 g_free(options.scroll_sep);
450 options.scroll_sep = get_format(value);
451 } else
452 match_found = false;
454 if (!match_found)
455 print_error(_("Unknown configuration parameter"),
456 name);
458 return match_found;
459 }
461 static int
462 read_rc_file(char *filename)
463 {
464 FILE *file;
465 char line[MAX_LINE_LENGTH];
467 if (filename == NULL)
468 return -1;
470 file = fopen(filename, "r");
471 if (file == NULL) {
472 perror(filename);
473 return -1;
474 }
476 while (fgets(line, sizeof(line), file) != NULL) {
477 char *p = g_strchug(line);
479 if (*p != 0 && *p != COMMENT_TOKEN)
480 parse_line(g_strchomp(p));
481 }
483 fclose(file);
484 return 0;
485 }
487 int
488 check_user_conf_dir(void)
489 {
490 int retval;
491 char *directory = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
493 if (g_file_test(directory, G_FILE_TEST_IS_DIR)) {
494 g_free(directory);
495 return 0;
496 }
498 retval = mkdir(directory, 0755);
499 g_free(directory);
500 return retval;
501 }
503 char *
504 get_user_key_binding_filename(void)
505 {
506 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
507 }
509 int
510 read_configuration(void)
511 {
512 char *filename = NULL;
514 /* check for command line configuration file */
515 if (options.config_file)
516 filename = g_strdup(options.config_file);
518 /* check for user configuration ~/.ncmpc/config */
519 if (filename == NULL) {
520 filename = g_build_filename(g_get_home_dir(),
521 "." PACKAGE, "config", NULL);
522 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
523 g_free(filename);
524 filename = NULL;
525 }
526 }
528 /* check for global configuration SYSCONFDIR/ncmpc/config */
529 if (filename == NULL) {
530 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
531 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
532 g_free(filename);
533 filename = NULL;
534 }
535 }
537 /* load configuration */
538 if (filename) {
539 read_rc_file(filename);
540 g_free(filename);
541 filename = NULL;
542 }
544 /* check for command line key binding file */
545 if (options.key_file)
546 filename = g_strdup(options.key_file);
548 /* check for user key bindings ~/.ncmpc/keys */
549 if (filename == NULL) {
550 filename = get_user_key_binding_filename();
551 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
552 g_free(filename);
553 filename = NULL;
554 }
555 }
557 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
558 if (filename == NULL) {
559 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
560 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
561 g_free(filename);
562 filename = NULL;
563 }
564 }
566 /* load key bindings */
567 if (filename) {
568 read_rc_file(filename);
569 g_free(filename);
570 filename = NULL;
571 }
573 return 0;
574 }