e3875e3af314cfb13d5c0ca7de77f8f49ca973e3
1 /*
2 * (c) 2004 by Kalle Wallin <kaw@linux.se>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 */
19 #include "conf.h"
20 #include "config.h"
21 #include "defaults.h"
22 #include "i18n.h"
23 #include "command.h"
24 #include "colors.h"
25 #include "screen_list.h"
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <glib.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_AUTO_CENTER "auto-center"
44 #define CONF_WIDE_CURSOR "wide-cursor"
45 #define CONF_ENABLE_BELL "enable-bell"
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_STATUS_FORMAT "status-format"
51 #define CONF_XTERM_TITLE_FORMAT "xterm-title-format"
52 #define CONF_LIST_WRAP "wrap-around"
53 #define CONF_FIND_WRAP "find-wrap"
54 #define CONF_FIND_SHOW_LAST "find-show-last"
55 #define CONF_AUDIBLE_BELL "audible-bell"
56 #define CONF_VISIBLE_BELL "visible-bell"
57 #define CONF_XTERM_TITLE "set-xterm-title"
58 #define CONF_ENABLE_MOUSE "enable-mouse"
59 #define CONF_CROSSFADE_TIME "crossfade-time"
60 #define CONF_SEARCH_MODE "search-mode"
61 #define CONF_HIDE_CURSOR "hide-cursor"
62 #define CONF_SEEK_TIME "seek-time"
63 #define CONF_SCREEN_LIST "screen-list"
64 #define CONF_TIMEDISPLAY_TYPE "timedisplay-type"
65 #define CONF_HOST "host"
66 #define CONF_PORT "port"
67 #define CONF_PASSWORD "password"
68 #define CONF_LYRICS_TIMEOUT "lyrics-timeout"
69 #define CONF_SHOW_SPLASH "show-splash"
70 #define CONF_SCROLL "scroll"
71 #define CONF_SCROLL_SEP "scroll-sep"
72 #define CONF_VISIBLE_BITRATE "visible-bitrate"
73 #define CONF_WELCOME_SCREEN_LIST "welcome-screen-list"
75 typedef enum {
76 KEY_PARSER_UNKNOWN,
77 KEY_PARSER_CHAR,
78 KEY_PARSER_DEC,
79 KEY_PARSER_HEX,
80 KEY_PARSER_DONE
81 } key_parser_state_t;
83 static bool
84 str2bool(char *str)
85 {
86 return strcasecmp(str, "yes") == 0 || strcasecmp(str, "true") == 0 ||
87 strcasecmp(str, "on") == 0 || strcasecmp(str, "1") == 0;
88 }
90 static void
91 print_error(const char *msg, const char *input)
92 {
93 fprintf(stderr, "%s: %s ('%s')\n",
94 /* To translators: prefix for error messages */
95 _("Error"), msg, input);
96 }
98 static int
99 parse_key_value(char *str, char **end)
100 {
101 size_t len = strlen(str);
102 size_t i;
103 int value;
104 key_parser_state_t state;
106 i=0;
107 value=0;
108 state=KEY_PARSER_UNKNOWN;
109 *end = str;
111 while (i < len && state != KEY_PARSER_DONE) {
112 int next = 0;
113 int c = str[i];
115 if( i+1<len )
116 next = str[i+1];
118 switch(state) {
119 case KEY_PARSER_UNKNOWN:
120 if( c=='\'' )
121 state = KEY_PARSER_CHAR;
122 else if( c=='0' && next=='x' )
123 state = KEY_PARSER_HEX;
124 else if( isdigit(c) )
125 state = KEY_PARSER_DEC;
126 else {
127 print_error(_("Unsupported key definition"),
128 str);
129 return -1;
130 }
131 break;
132 case KEY_PARSER_CHAR:
133 if( next!='\'' ) {
134 print_error(_("Unsupported key definition"),
135 str);
136 return -1;
137 }
138 value = c;
139 *end = str+i+2;
140 state = KEY_PARSER_DONE;
141 break;
142 case KEY_PARSER_DEC:
143 value = (int) strtol(str+(i-1), end, 10);
144 state = KEY_PARSER_DONE;
145 break;
146 case KEY_PARSER_HEX:
147 if( !isdigit(next) ) {
148 print_error(_("Digit expected after 0x"), str);
149 return -1;
150 }
151 value = (int) strtol(str+(i+1), end, 16);
152 state = KEY_PARSER_DONE;
153 break;
154 case KEY_PARSER_DONE:
155 break;
156 }
157 i++;
158 }
160 if( *end> str+len )
161 *end = str+len;
163 return value;
164 }
166 static int
167 parse_key_definition(char *str)
168 {
169 char buf[MAX_LINE_LENGTH];
170 char *p;
171 size_t len = strlen(str), i;
172 int j,key;
173 int keys[MAX_COMMAND_KEYS];
174 command_t cmd;
176 /* get the command name */
177 i=0;
178 j=0;
179 memset(buf, 0, MAX_LINE_LENGTH);
180 while (i < len && str[i] != '=' && !g_ascii_isspace(str[i]))
181 buf[j++] = str[i++];
182 if( (cmd=get_key_command_from_name(buf)) == CMD_NONE ) {
183 print_error(_("Unknown key command"), buf);
184 return -1;
185 }
187 /* skip whitespace */
188 while (i < len && (str[i] == '=' || g_ascii_isspace(str[i])))
189 i++;
191 /* get the value part */
192 memset(buf, 0, MAX_LINE_LENGTH);
193 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
194 if (*buf == 0) {
195 print_error(_("Incomplete key definition"), str);
196 return -1;
197 }
199 /* parse key values */
200 i = 0;
201 key = 0;
202 p = buf;
203 memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
204 while (i < MAX_COMMAND_KEYS && *p != 0 &&
205 (key = parse_key_value(p, &p)) >= 0) {
206 keys[i++] = key;
207 while (*p==',' || *p==' ' || *p=='\t')
208 p++;
209 }
211 if (key < 0) {
212 print_error(_("Bad key definition"), str);
213 return -1;
214 }
216 return assign_keys(cmd, keys);
217 }
219 static const char *
220 parse_timedisplay_type(const char *str)
221 {
222 if (!strcmp(str,"elapsed") || !strcmp(str,"remaining"))
223 return str;
224 else {
225 print_error(_("Bad time display type"), str);
226 return DEFAULT_TIMEDISPLAY_TYPE;
227 }
228 }
230 #ifdef ENABLE_COLORS
231 static char *
232 separate_value(char *p)
233 {
234 char *value;
236 value = strchr(p, '=');
237 if (value == NULL) {
238 fprintf(stderr, "%s\n", _("Missing '='"));
239 return NULL;
240 }
242 *value++ = 0;
244 g_strchomp(p);
245 return g_strchug(value);
246 }
248 static int
249 parse_color(char *str)
250 {
251 char *value;
253 value = separate_value(str);
254 if (value == NULL)
255 return -1;
257 return colors_assign(str, value);
258 }
260 /**
261 * Returns the first non-whitespace character after the next comma
262 * character, or the end of the string. This is used to parse comma
263 * separated values.
264 */
265 static char *
266 after_comma(char *p)
267 {
268 char *comma = strchr(p, ',');
270 if (comma != NULL) {
271 *comma++ = 0;
272 comma = g_strchug(comma);
273 } else
274 comma = p + strlen(p);
276 g_strchomp(p);
277 return comma;
278 }
280 static int
281 parse_color_definition(char *str)
282 {
283 char buf[MAX_LINE_LENGTH];
284 char *value;
285 short color, rgb[3];
287 value = separate_value(str);
288 if (value == NULL)
289 return -1;
291 /* get the command name */
292 color = colors_str2color(str);
293 if (color < 0) {
294 print_error(_("Bad color"), buf);
295 return -1;
296 }
298 /* parse r,g,b values */
300 for (unsigned i = 0; i < 3; ++i) {
301 char *next = after_comma(value), *endptr;
302 if (*value == 0) {
303 print_error(_("Incomplete color definition"), str);
304 return -1;
305 }
307 rgb[i] = strtol(value, &endptr, 0);
308 if (endptr == value || *endptr != 0) {
309 print_error(_("Invalid number"), value);
310 return -1;
311 }
313 value = next;
314 }
316 if (*value != 0) {
317 print_error(_("Bad color definition"), str);
318 return -1;
319 }
321 return colors_define(str, rgb[0], rgb[1], rgb[2]);
322 }
323 #endif
325 static char *
326 get_format(char *str)
327 {
328 gsize len = strlen(str);
330 if (str && str[0]=='\"' && str[len-1] == '\"') {
331 str[len - 1] = '\0';
332 str++;
333 }
335 return g_strdup(str);
336 }
338 static char **
339 check_screen_list(char *value)
340 {
341 char **tmp = g_strsplit_set(value, " \t,", 100);
342 char **screen = NULL;
343 int i,j;
345 i=0;
346 j=0;
347 while( tmp && tmp[i] ) {
348 char *name = g_ascii_strdown(tmp[i], -1);
349 if (screen_lookup_name(name) == NULL) {
350 print_error(_("Unsupported screen"), name);
351 free(name);
352 } else {
353 screen = g_realloc(screen, (j+2)*sizeof(char *));
354 screen[j++] = name;
355 screen[j] = NULL;
356 }
357 i++;
358 }
359 g_strfreev(tmp);
360 if( screen == NULL )
361 return g_strsplit_set(DEFAULT_SCREEN_LIST, " ", 0);
363 return screen;
364 }
366 static bool
367 parse_line(char *line)
368 {
369 size_t len = strlen(line), i = 0, j;
370 char name[MAX_LINE_LENGTH];
371 char value[MAX_LINE_LENGTH];
372 bool match_found;
374 /* get the name part */
375 j = 0;
376 while (i < len && line[i] != '=' &&
377 !g_ascii_isspace(line[i])) {
378 name[j++] = line[i++];
379 }
381 name[j] = '\0';
383 /* skip '=' and whitespace */
384 while (i < len && (line[i] == '=' || g_ascii_isspace(line[i])))
385 i++;
387 /* get the value part */
388 j = 0;
389 while (i < len)
390 value[j++] = line[i++];
391 value[j] = '\0';
393 match_found = true;
395 /* key definition */
396 if (!strcasecmp(CONF_KEY_DEFINITION, name))
397 parse_key_definition(value);
398 /* enable colors */
399 else if(!strcasecmp(CONF_ENABLE_COLORS, name))
400 #ifdef ENABLE_COLORS
401 options.enable_colors = str2bool(value);
402 #else
403 {}
404 #endif
405 /* auto center */
406 else if (!strcasecmp(CONF_AUTO_CENTER, name))
407 options.auto_center = str2bool(value);
408 /* color assignment */
409 else if (!strcasecmp(CONF_COLOR, name))
410 #ifdef ENABLE_COLORS
411 parse_color(value);
412 #else
413 {}
414 #endif
415 /* wide cursor */
416 else if (!strcasecmp(CONF_WIDE_CURSOR, name))
417 options.wide_cursor = str2bool(value);
418 /* welcome screen list */
419 else if (!strcasecmp(CONF_WELCOME_SCREEN_LIST, name))
420 options.welcome_screen_list = str2bool(value);
421 /* visible bitrate */
422 else if (!strcasecmp(CONF_VISIBLE_BITRATE, name))
423 options.visible_bitrate = str2bool(value);
424 /* timer display type */
425 else if (!strcasecmp(CONF_TIMEDISPLAY_TYPE, name)) {
426 g_free(options.timedisplay_type);
427 options.timedisplay_type=g_strdup(parse_timedisplay_type(value));
428 /* color definition */
429 } else if (!strcasecmp(CONF_COLOR_DEFINITION, name))
430 #ifdef ENABLE_COLORS
431 parse_color_definition(value);
432 #else
433 {}
434 #endif
435 /* list format string */
436 else if (!strcasecmp(CONF_LIST_FORMAT, name)) {
437 g_free(options.list_format);
438 options.list_format = get_format(value);
439 /* status format string */
440 } else if (!strcasecmp(CONF_STATUS_FORMAT, name)) {
441 g_free(options.status_format);
442 options.status_format = get_format(value);
443 /* xterm title format string */
444 } else if (!strcasecmp(CONF_XTERM_TITLE_FORMAT, name)) {
445 g_free(options.xterm_title_format);
446 options.xterm_title_format = get_format(value);
447 } else if (!strcasecmp(CONF_LIST_WRAP, name))
448 options.list_wrap = str2bool(value);
449 else if (!strcasecmp(CONF_FIND_WRAP, name))
450 options.find_wrap = str2bool(value);
451 else if (!strcasecmp(CONF_FIND_SHOW_LAST,name))
452 options.find_show_last_pattern = str2bool(value);
453 else if (!strcasecmp(CONF_AUDIBLE_BELL, name))
454 options.audible_bell = str2bool(value);
455 else if (!strcasecmp(CONF_VISIBLE_BELL, name))
456 options.visible_bell = str2bool(value);
457 else if (!strcasecmp(CONF_XTERM_TITLE, name))
458 options.enable_xterm_title = str2bool(value);
459 else if (!strcasecmp(CONF_ENABLE_MOUSE, name))
460 #ifdef HAVE_GETMOUSE
461 options.enable_mouse = str2bool(value);
462 #else
463 {}
464 #endif
465 else if (!strcasecmp(CONF_CROSSFADE_TIME, name))
466 options.crossfade_time = atoi(value);
467 else if (!strcasecmp(CONF_SEARCH_MODE, name))
468 options.search_mode = atoi(value);
469 else if (!strcasecmp(CONF_HIDE_CURSOR, name))
470 options.hide_cursor = atoi(value);
471 else if (!strcasecmp(CONF_SEEK_TIME, name))
472 options.seek_time = atoi(value);
473 else if (!strcasecmp(CONF_SCREEN_LIST, name)) {
474 g_strfreev(options.screen_list);
475 options.screen_list = check_screen_list(value);
476 } else if (!strcasecmp(CONF_SHOW_SPLASH, name)) {
477 /* the splash screen was removed */
478 } else if (!strcasecmp(CONF_HOST, name))
479 options.host = get_format(value);
480 else if (!strcasecmp(CONF_PORT, name))
481 options.port = atoi(get_format(value));
482 else if (!strcasecmp(CONF_PASSWORD, name))
483 options.password = get_format(value);
484 else if (!strcasecmp(CONF_LYRICS_TIMEOUT, name))
485 #ifdef ENABLE_LYRICS_SCREEN
486 options.lyrics_timeout = atoi(get_format(value));
487 #else
488 {}
489 #endif
490 else if (!strcasecmp(CONF_SCROLL, name))
491 options.scroll = str2bool(value);
492 else if (!strcasecmp(CONF_SCROLL_SEP, name)) {
493 g_free(options.scroll_sep);
494 options.scroll_sep = get_format(value);
495 } else
496 match_found = false;
498 if (!match_found)
499 print_error(_("Unknown configuration parameter"),
500 name);
502 return match_found;
503 }
505 static int
506 read_rc_file(char *filename)
507 {
508 FILE *file;
509 char line[MAX_LINE_LENGTH];
511 if (filename == NULL)
512 return -1;
514 file = fopen(filename, "r");
515 if (file == NULL) {
516 perror(filename);
517 return -1;
518 }
520 while (fgets(line, sizeof(line), file) != NULL) {
521 char *p = g_strchug(line);
523 if (*p != 0 && *p != COMMENT_TOKEN)
524 parse_line(g_strchomp(p));
525 }
527 fclose(file);
528 return 0;
529 }
531 int
532 check_user_conf_dir(void)
533 {
534 int retval;
535 char *directory = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
537 if (g_file_test(directory, G_FILE_TEST_IS_DIR)) {
538 g_free(directory);
539 return 0;
540 }
542 retval = mkdir(directory, 0755);
543 g_free(directory);
544 return retval;
545 }
547 char *
548 get_user_key_binding_filename(void)
549 {
550 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
551 }
553 int
554 read_configuration(void)
555 {
556 char *filename = NULL;
558 /* check for command line configuration file */
559 if (options.config_file)
560 filename = g_strdup(options.config_file);
562 /* check for user configuration ~/.ncmpc/config */
563 if (filename == NULL) {
564 filename = g_build_filename(g_get_home_dir(),
565 "." PACKAGE, "config", NULL);
566 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
567 g_free(filename);
568 filename = NULL;
569 }
570 }
572 /* check for global configuration SYSCONFDIR/ncmpc/config */
573 if (filename == NULL) {
574 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
575 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
576 g_free(filename);
577 filename = NULL;
578 }
579 }
581 /* load configuration */
582 if (filename) {
583 read_rc_file(filename);
584 g_free(filename);
585 filename = NULL;
586 }
588 /* check for command line key binding file */
589 if (options.key_file)
590 filename = g_strdup(options.key_file);
592 /* check for user key bindings ~/.ncmpc/keys */
593 if (filename == NULL) {
594 filename = get_user_key_binding_filename();
595 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
596 g_free(filename);
597 filename = NULL;
598 }
599 }
601 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
602 if (filename == NULL) {
603 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
604 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
605 g_free(filename);
606 filename = NULL;
607 }
608 }
610 /* load key bindings */
611 if (filename) {
612 read_rc_file(filename);
613 g_free(filename);
614 filename = NULL;
615 }
617 return 0;
618 }