Code

38d5089540e49369a25c97cca9c0030d11e95a63
[ncmpc.git] / src / conf.c
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2009 The Music Player Daemon Project
3  * Project homepage: http://musicpd.org
4  
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         }
113 static int
114 parse_key_definition(char *str)
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);
167 static const char *
168 parse_timedisplay_type(const char *str)
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         }
182 #ifdef ENABLE_COLORS
183 static char *
184 separate_value(char *p)
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);
202 static int
203 parse_color(char *str)
205         char *value;
207         value = separate_value(str);
208         if (value == NULL)
209                 return -1;
211         return colors_assign(str, value);
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)
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;
234 static int
235 parse_color_definition(char *str)
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]);
277 #endif
279 static char *
280 get_format(char *str)
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);
292 static char **
293 check_screen_list(char *value)
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;
322 static bool
323 parse_line(char *line)
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;
461 static int
462 read_rc_file(char *filename)
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;
487 int
488 check_user_conf_dir(void)
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;
503 char *
504 get_user_key_binding_filename(void)
506         return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
509 int
510 read_configuration(void)
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;