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 int
91 parse_key_value(char *str, size_t len, char **end)
92 {
93 size_t i;
94 int value;
95 key_parser_state_t state;
97 i=0;
98 value=0;
99 state=KEY_PARSER_UNKNOWN;
100 *end = str;
102 while (i < len && state != KEY_PARSER_DONE) {
103 int next = 0;
104 int c = str[i];
106 if( i+1<len )
107 next = str[i+1];
109 switch(state) {
110 case KEY_PARSER_UNKNOWN:
111 if( c=='\'' )
112 state = KEY_PARSER_CHAR;
113 else if( c=='0' && next=='x' )
114 state = KEY_PARSER_HEX;
115 else if( isdigit(c) )
116 state = KEY_PARSER_DEC;
117 else {
118 fprintf(stderr,
119 _("Error: Unsupported key definition - %s\n"),
120 str);
121 return -1;
122 }
123 break;
124 case KEY_PARSER_CHAR:
125 if( next!='\'' ) {
126 fprintf(stderr,
127 _("Error: Unsupported key definition - %s\n"),
128 str);
129 return -1;
130 }
131 value = c;
132 *end = str+i+2;
133 state = KEY_PARSER_DONE;
134 break;
135 case KEY_PARSER_DEC:
136 value = (int) strtol(str+(i-1), end, 10);
137 state = KEY_PARSER_DONE;
138 break;
139 case KEY_PARSER_HEX:
140 if( !isdigit(next) ) {
141 fprintf(stderr,_("Error: Digit expected after 0x - %s\n"), str);
142 return -1;
143 }
144 value = (int) strtol(str+(i+1), end, 16);
145 state = KEY_PARSER_DONE;
146 break;
147 case KEY_PARSER_DONE:
148 break;
149 }
150 i++;
151 }
153 if( *end> str+len )
154 *end = str+len;
156 return value;
157 }
159 static int
160 parse_key_definition(char *str)
161 {
162 char buf[MAX_LINE_LENGTH];
163 char *p, *end;
164 size_t len = strlen(str), i;
165 int j,key;
166 int keys[MAX_COMMAND_KEYS];
167 command_t cmd;
169 /* get the command name */
170 i=0;
171 j=0;
172 memset(buf, 0, MAX_LINE_LENGTH);
173 while (i < len && str[i] != '=' && !g_ascii_isspace(str[i]))
174 buf[j++] = str[i++];
175 if( (cmd=get_key_command_from_name(buf)) == CMD_NONE ) {
176 fprintf(stderr, _("Error: Unknown key command %s\n"), buf);
177 return -1;
178 }
180 /* skip whitespace */
181 while (i < len && (str[i] == '=' || g_ascii_isspace(str[i])))
182 i++;
184 /* get the value part */
185 memset(buf, 0, MAX_LINE_LENGTH);
186 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
187 len = strlen(buf);
188 if (len == 0) {
189 fprintf(stderr,_("Error: Incomplete key definition - %s\n"), str);
190 return -1;
191 }
193 /* parse key values */
194 i = 0;
195 key = 0;
196 len = strlen(buf);
197 p = buf;
198 end = buf+len;
199 memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
200 while (i < MAX_COMMAND_KEYS && p < end &&
201 (key = parse_key_value(p, len + 1, &p)) >= 0) {
202 keys[i++] = key;
203 while (p < end && (*p==',' || *p==' ' || *p=='\t'))
204 p++;
205 len = strlen(p);
206 }
208 if (key < 0) {
209 fprintf(stderr,_("Error: Bad key definition - %s\n"), str);
210 return -1;
211 }
213 return assign_keys(cmd, keys);
214 }
216 static const char *
217 parse_timedisplay_type(const char *str)
218 {
219 if (!strcmp(str,"elapsed") || !strcmp(str,"remaining"))
220 return str;
221 else {
222 fprintf(stderr,_("Error: Bad time display type - %s\n"), str);
223 return DEFAULT_TIMEDISPLAY_TYPE;
224 }
225 }
227 #ifdef ENABLE_COLORS
228 static int
229 parse_color(char *str)
230 {
231 const char *name = str;
232 const char *value = NULL;
233 int len,i;
235 i=0;
236 len=strlen(str);
237 /* get the color name */
238 while (i < len && str[i] != '=' && !g_ascii_isspace(str[i]))
239 i++;
241 /* skip whitespace */
242 while (i < len && (str[i] == '=' || g_ascii_isspace(str[i]))) {
243 str[i]='\0';
244 i++;
245 }
247 if (i < len)
248 value = str+i;
250 return colors_assign(name, value);
251 }
253 static int
254 parse_color_definition(char *str)
255 {
256 char buf[MAX_LINE_LENGTH];
257 char *p, *end, *name;
258 size_t len = strlen(str), i;
259 int j,value;
260 short color, rgb[3];
262 /* get the command name */
263 i=0;
264 j=0;
265 memset(buf, 0, MAX_LINE_LENGTH);
266 while (i < len && str[i] != '=' && !g_ascii_isspace(str[i]))
267 buf[j++] = str[i++];
268 color = colors_str2color(buf);
269 if (color < 0) {
270 fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
271 return -1;
272 }
273 name = g_strdup(buf);
275 /* skip whitespace */
276 while (i < len && (str[i] == '=' || g_ascii_isspace(str[i])))
277 i++;
279 /* get the value part */
280 memset(buf, 0, MAX_LINE_LENGTH);
281 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
282 len = strlen(buf);
283 if (len == 0) {
284 fprintf(stderr, _("Error: Incomplete color definition - %s\n"),
285 str);
286 g_free(name);
287 return -1;
288 }
290 /* parse r,g.b values with the key definition parser */
291 i = 0;
292 value = 0;
293 len = strlen(buf);
294 p = buf;
295 end = buf + len;
296 memset(rgb, 0, sizeof(short)*3);
297 while (i < 3 && p < end &&
298 (value = parse_key_value(p,len+1,&p)) >= 0) {
299 rgb[i++] = value;
300 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
301 p++;
302 len = strlen(p);
303 }
305 if (value < 0 || i != 3) {
306 fprintf(stderr, _("Error: Bad color definition - %s\n"), str);
307 g_free(name);
308 return -1;
309 }
311 value = colors_define(name, rgb[0], rgb[1], rgb[2]);
312 g_free(name);
313 return value;
314 }
315 #endif
317 static char *
318 get_format(char *str)
319 {
320 gsize len = strlen(str);
322 if (str && str[0]=='\"' && str[len-1] == '\"') {
323 str[len - 1] = '\0';
324 str++;
325 }
327 return g_strdup(str);
328 }
330 static char **
331 check_screen_list(char *value)
332 {
333 char **tmp = g_strsplit_set(value, " \t,", 100);
334 char **screen = NULL;
335 int i,j;
337 i=0;
338 j=0;
339 while( tmp && tmp[i] ) {
340 char *name = g_ascii_strdown(tmp[i], -1);
341 if (screen_lookup_name(name) == NULL) {
342 fprintf(stderr,
343 _("Error: Unsupported screen \"%s\"\n"),
344 name);
345 free(name);
346 } else {
347 screen = g_realloc(screen, (j+2)*sizeof(char *));
348 screen[j++] = name;
349 screen[j] = NULL;
350 }
351 i++;
352 }
353 g_strfreev(tmp);
354 if( screen == NULL )
355 return g_strsplit_set(DEFAULT_SCREEN_LIST, " ", 0);
357 return screen;
358 }
360 static int
361 read_rc_file(char *filename)
362 {
363 int fd;
364 int quit = 0;
365 int free_filename = 0;
367 if (filename == NULL)
368 return -1;
370 if ((fd = open(filename,O_RDONLY)) < 0) {
371 perror(filename);
372 if (free_filename)
373 g_free(filename);
374 return -1;
375 }
377 while (!quit) {
378 int i,j;
379 int len;
380 int match_found;
381 char line[MAX_LINE_LENGTH];
382 char name[MAX_LINE_LENGTH];
383 char value[MAX_LINE_LENGTH];
385 line[0] = '\0';
386 value[0] = '\0';
388 i = 0;
389 /* read a line ending with '\n' */
390 do {
391 len = read(fd, &line[i], 1);
392 if (len == 1)
393 i++;
394 else
395 quit = 1;
396 } while (!quit && i < MAX_LINE_LENGTH && line[i-1] != '\n');
399 /* remove trailing whitespace */
400 line[i] = '\0';
401 i--;
402 while (i >= 0 && g_ascii_isspace(line[i])) {
403 line[i] = '\0';
404 i--;
405 }
407 len = i + 1;
408 if (len > 0) {
409 i = 0;
410 /* skip whitespace */
411 while (i < len && g_ascii_isspace(line[i]))
412 i++;
414 /* continue if this line is not a comment */
415 if (line[i] != COMMENT_TOKEN) {
416 /* get the name part */
417 j = 0;
418 while (i < len && line[i] != '=' &&
419 !g_ascii_isspace(line[i])) {
420 name[j++] = line[i++];
421 }
423 name[j] = '\0';
425 /* skip '=' and whitespace */
426 while (i < len && (line[i] == '=' || g_ascii_isspace(line[i])))
427 i++;
429 /* get the value part */
430 j = 0;
431 while (i < len)
432 value[j++] = line[i++];
433 value[j] = '\0';
435 match_found = 1;
437 /* key definition */
438 if (!strcasecmp(CONF_KEY_DEFINITION, name))
439 parse_key_definition(value);
440 /* enable colors */
441 else if(!strcasecmp(CONF_ENABLE_COLORS, name))
442 #ifdef ENABLE_COLORS
443 options.enable_colors = str2bool(value);
444 #else
445 {}
446 #endif
447 /* auto center */
448 else if (!strcasecmp(CONF_AUTO_CENTER, name))
449 options.auto_center = str2bool(value);
450 /* color assignment */
451 else if (!strcasecmp(CONF_COLOR, name))
452 #ifdef ENABLE_COLORS
453 parse_color(value);
454 #else
455 {}
456 #endif
457 /* wide cursor */
458 else if (!strcasecmp(CONF_WIDE_CURSOR, name))
459 options.wide_cursor = str2bool(value);
460 /* welcome screen list */
461 else if (!strcasecmp(CONF_WELCOME_SCREEN_LIST, name))
462 options.welcome_screen_list = str2bool(value);
463 /* visible bitrate */
464 else if (!strcasecmp(CONF_VISIBLE_BITRATE, name))
465 options.visible_bitrate = str2bool(value);
466 /* timer display type */
467 else if (!strcasecmp(CONF_TIMEDISPLAY_TYPE, name)) {
468 g_free(options.timedisplay_type);
469 options.timedisplay_type=g_strdup(parse_timedisplay_type(value));
470 /* color definition */
471 } else if (!strcasecmp(CONF_COLOR_DEFINITION, name))
472 #ifdef ENABLE_COLORS
473 parse_color_definition(value);
474 #else
475 {}
476 #endif
477 /* list format string */
478 else if (!strcasecmp(CONF_LIST_FORMAT, name)) {
479 g_free(options.list_format);
480 options.list_format = get_format(value);
481 /* status format string */
482 } else if (!strcasecmp(CONF_STATUS_FORMAT, name)) {
483 g_free(options.status_format);
484 options.status_format = get_format(value);
485 /* xterm title format string */
486 } else if (!strcasecmp(CONF_XTERM_TITLE_FORMAT, name)) {
487 g_free(options.xterm_title_format);
488 options.xterm_title_format = get_format(value);
489 } else if (!strcasecmp(CONF_LIST_WRAP, name))
490 options.list_wrap = str2bool(value);
491 else if (!strcasecmp(CONF_FIND_WRAP, name))
492 options.find_wrap = str2bool(value);
493 else if (!strcasecmp(CONF_FIND_SHOW_LAST,name))
494 options.find_show_last_pattern = str2bool(value);
495 else if (!strcasecmp(CONF_AUDIBLE_BELL, name))
496 options.audible_bell = str2bool(value);
497 else if (!strcasecmp(CONF_VISIBLE_BELL, name))
498 options.visible_bell = str2bool(value);
499 else if (!strcasecmp(CONF_XTERM_TITLE, name))
500 options.enable_xterm_title = str2bool(value);
501 else if (!strcasecmp(CONF_ENABLE_MOUSE, name))
502 options.enable_mouse = str2bool(value);
503 else if (!strcasecmp(CONF_CROSSFADE_TIME, name))
504 options.crossfade_time = atoi(value);
505 else if (!strcasecmp(CONF_SEARCH_MODE, name))
506 options.search_mode = atoi(value);
507 else if (!strcasecmp(CONF_HIDE_CURSOR, name))
508 options.hide_cursor = atoi(value);
509 else if (!strcasecmp(CONF_SEEK_TIME, name))
510 options.seek_time = atoi(value);
511 else if (!strcasecmp(CONF_SCREEN_LIST, name)) {
512 g_strfreev(options.screen_list);
513 options.screen_list = check_screen_list(value);
514 } else if (!strcasecmp(CONF_SHOW_SPLASH, name)) {
515 /* the splash screen was removed */
516 } else if (!strcasecmp(CONF_HOST, name))
517 options.host = get_format(value);
518 else if (!strcasecmp(CONF_PORT, name))
519 options.port = atoi(get_format(value));
520 else if (!strcasecmp(CONF_PASSWORD, name))
521 options.password = get_format(value);
522 else if (!strcasecmp(CONF_LYRICS_TIMEOUT, name))
523 options.lyrics_timeout = atoi(get_format(value));
524 else if (!strcasecmp(CONF_SCROLL, name))
525 options.scroll = str2bool(value);
526 else if (!strcasecmp(CONF_SCROLL_SEP, name)) {
527 g_free(options.scroll_sep);
528 options.scroll_sep = get_format(value);
529 } else
530 match_found = 0;
532 if (!match_found)
533 fprintf(stderr,
534 _("Unknown configuration parameter: %s\n"),
535 name);
536 }
537 }
538 }
540 if (free_filename)
541 g_free(filename);
543 return 0;
544 }
546 int
547 check_user_conf_dir(void)
548 {
549 int retval;
550 char *directory = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
552 if (g_file_test(directory, G_FILE_TEST_IS_DIR)) {
553 g_free(directory);
554 return 0;
555 }
557 retval = mkdir(directory, 0755);
558 g_free(directory);
559 return retval;
560 }
562 char *
563 get_user_key_binding_filename(void)
564 {
565 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
566 }
568 int
569 read_configuration(void)
570 {
571 char *filename = NULL;
573 /* check for command line configuration file */
574 if (options.config_file)
575 filename = g_strdup(options.config_file);
577 /* check for user configuration ~/.ncmpc/config */
578 if (filename == NULL) {
579 filename = g_build_filename(g_get_home_dir(),
580 "." PACKAGE, "config", NULL);
581 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
582 g_free(filename);
583 filename = NULL;
584 }
585 }
587 /* check for global configuration SYSCONFDIR/ncmpc/config */
588 if (filename == NULL) {
589 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
590 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
591 g_free(filename);
592 filename = NULL;
593 }
594 }
596 /* load configuration */
597 if (filename) {
598 read_rc_file(filename);
599 g_free(filename);
600 filename = NULL;
601 }
603 /* check for command line key binding file */
604 if (options.key_file)
605 filename = g_strdup(options.key_file);
607 /* check for user key bindings ~/.ncmpc/keys */
608 if (filename == NULL) {
609 filename = get_user_key_binding_filename();
610 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
611 g_free(filename);
612 filename = NULL;
613 }
614 }
616 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
617 if (filename == NULL) {
618 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
619 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
620 g_free(filename);
621 filename = NULL;
622 }
623 }
625 /* load key bindings */
626 if (filename) {
627 read_rc_file(filename);
628 g_free(filename);
629 filename = NULL;
630 }
632 return 0;
633 }