c02df52cf42f79e19273c0b8ca96aef27076f8d9
1 /*
2 * $Id$
3 *
4 * (c) 2004 by Kalle Wallin <kaw@linux.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
21 #define NO_GLOBAL_OPTIONS
22 #include "conf.h"
23 #include "config.h"
24 #include "ncmpc.h"
25 #include "support.h"
26 #include "command.h"
27 #include "colors.h"
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.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 typedef enum {
77 KEY_PARSER_UNKNOWN,
78 KEY_PARSER_CHAR,
79 KEY_PARSER_DEC,
80 KEY_PARSER_HEX,
81 KEY_PARSER_DONE
82 } key_parser_state_t;
85 extern gint screen_get_id(char *name);
88 static gboolean
89 str2bool(char *str)
90 {
91 if (!strcasecmp(str, "yes") || !strcasecmp(str, "true") ||
92 !strcasecmp(str, "on") || !strcasecmp(str, "1"))
93 return TRUE;
94 return FALSE;
95 }
97 static int
98 parse_key_value(char *str, size_t len, char **end)
99 {
100 size_t i;
101 int value;
102 key_parser_state_t state;
104 i=0;
105 value=0;
106 state=KEY_PARSER_UNKNOWN;
107 *end = str;
109 while (i < len && state != KEY_PARSER_DONE) {
110 int next = 0;
111 int c = str[i];
113 if( i+1<len )
114 next = str[i+1];
116 switch(state) {
117 case KEY_PARSER_UNKNOWN:
118 if( c=='\'' )
119 state = KEY_PARSER_CHAR;
120 else if( c=='0' && next=='x' )
121 state = KEY_PARSER_HEX;
122 else if( isdigit(c) )
123 state = KEY_PARSER_DEC;
124 else {
125 fprintf(stderr,
126 _("Error: Unsupported key definition - %s\n"),
127 str);
128 return -1;
129 }
130 break;
131 case KEY_PARSER_CHAR:
132 if( next!='\'' ) {
133 fprintf(stderr,
134 _("Error: Unsupported key definition - %s\n"),
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 fprintf(stderr,_("Error: Digit expected after 0x - %s\n"), 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, *end;
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]!='=' && !IS_WHITESPACE(str[i]) )
181 buf[j++] = str[i++];
182 if( (cmd=get_key_command_from_name(buf)) == CMD_NONE ) {
183 fprintf(stderr, _("Error: Unknown key command %s\n"), buf);
184 return -1;
185 }
187 /* skip whitespace */
188 while( i<len && (str[i]=='=' || IS_WHITESPACE(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 len = strlen(buf);
195 if( len==0 ) {
196 fprintf(stderr,_("Error: Incomplete key definition - %s\n"), str);
197 return -1;
198 }
200 /* parse key values */
201 i = 0;
202 key = 0;
203 len = strlen(buf);
204 p = buf;
205 end = buf+len;
206 memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
207 while( i<MAX_COMMAND_KEYS && p<end &&
208 (key=parse_key_value(p,len+1,&p))>=0 ) {
209 keys[i++] = key;
210 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
211 p++;
212 len = strlen(p);
213 }
214 if( key<0 ) {
215 fprintf(stderr,_("Error: Bad key definition - %s\n"), str);
216 return -1;
217 }
219 return assign_keys(cmd, keys);
220 }
222 static const char *
223 parse_timedisplay_type(const char *str)
224 {
225 if((!strcmp(str,"elapsed")) || (!strcmp(str,"remaining"))){
226 return str;
227 } else {
228 fprintf(stderr,_("Error: Bad time display type - %s\n"), str);
229 return DEFAULT_TIMEDISPLAY_TYPE;
230 }
231 }
233 static int
234 parse_color(char *str)
235 {
236 const char *name = str;
237 const char *value = NULL;
238 int len,i;
240 i=0;
241 len=strlen(str);
242 /* get the color name */
243 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
244 i++;
246 /* skip whitespace */
247 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) ) {
248 str[i]='\0';
249 i++;
250 }
252 if( i<len )
253 value = str+i;
255 return colors_assign(name, value);
256 }
258 static int
259 parse_color_definition(char *str)
260 {
261 char buf[MAX_LINE_LENGTH];
262 char *p, *end, *name;
263 size_t len = strlen(str), i;
264 int j,value;
265 short color, rgb[3];
267 /* get the command name */
268 i=0;
269 j=0;
270 memset(buf, 0, MAX_LINE_LENGTH);
271 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
272 buf[j++] = str[i++];
273 color=colors_str2color(buf);
274 if( color<0 ) {
275 fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
276 return -1;
277 }
278 name = g_strdup(buf);
280 /* skip whitespace */
281 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
282 i++;
284 /* get the value part */
285 memset(buf, 0, MAX_LINE_LENGTH);
286 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
287 len = strlen(buf);
288 if( len==0 ) {
289 fprintf(stderr,_("Error: Incomplete color definition - %s\n"), str);
290 g_free(name);
291 return -1;
292 }
294 /* parse r,g.b values with the key definition parser */
295 i = 0;
296 value = 0;
297 len = strlen(buf);
298 p = buf;
299 end = buf+len;
300 memset(rgb, 0, sizeof(short)*3);
301 while( i<3 && p<end && (value=parse_key_value(p,len+1,&p))>=0 ) {
302 rgb[i++] = value;
303 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
304 p++;
305 len = strlen(p);
306 }
307 if( value<0 || i!=3) {
308 fprintf(stderr,_("Error: Bad color definition - %s\n"), str);
309 g_free(name);
310 return -1;
311 }
312 value = colors_define(name, rgb[0], rgb[1], rgb[2]);
313 g_free(name);
314 return value;
315 }
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 }
326 return g_strdup(str);
327 }
329 static char **
330 check_screen_list(char *value)
331 {
332 char **tmp = g_strsplit_set(value, " \t,", 100);
333 char **screen = NULL;
334 int i,j;
336 i=0;
337 j=0;
338 while( tmp && tmp[i] ) {
339 tmp[i] = lowerstr(tmp[i]);
340 if( screen_get_id(tmp[i]) == -1 )
341 fprintf(stderr,
342 _("Error: Unsupported screen \"%s\"\n"),
343 tmp[i]);
344 else {
345 screen = g_realloc(screen, (j+2)*sizeof(char *));
346 screen[j++] = g_strdup(tmp[i]);
347 screen[j] = NULL;
348 }
349 i++;
350 }
351 g_strfreev(tmp);
352 if( screen == NULL )
353 return g_strsplit_set(DEFAULT_SCREEN_LIST, " ", 0);
355 return screen;
356 }
358 static int
359 read_rc_file(char *filename, options_t *options)
360 {
361 int fd;
362 int quit = 0;
363 int free_filename = 0;
365 if( filename==NULL )
366 return -1;
368 D("Reading configuration file %s\n", filename);
369 if( (fd=open(filename,O_RDONLY)) <0 )
370 {
371 perror(filename);
372 if( free_filename )
373 g_free(filename);
374 return -1;
375 }
377 while( !quit )
378 {
379 int i,j;
380 int len;
381 int match_found;
382 char line[MAX_LINE_LENGTH];
383 char name[MAX_LINE_LENGTH];
384 char value[MAX_LINE_LENGTH];
386 line[0] = '\0';
387 value[0] = '\0';
389 i = 0;
390 /* read a line ending with '\n' */
391 do {
392 len = read( fd, &line[i], 1 );
393 if( len == 1 )
394 i++;
395 else
396 quit = 1;
397 } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
400 /* remove trailing whitespace */
401 line[i] = '\0';
402 i--;
403 while( i>=0 && IS_WHITESPACE(line[i]) )
404 {
405 line[i] = '\0';
406 i--;
407 }
408 len = i+1;
410 if( len>0 )
411 {
412 i = 0;
413 /* skip whitespace */
414 while( i<len && IS_WHITESPACE(line[i]) )
415 i++;
417 /* continue if this line is not a comment */
418 if( line[i] != COMMENT_TOKEN )
419 {
420 /* get the name part */
421 j=0;
422 while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
423 {
424 name[j++] = line[i++];
425 }
426 name[j] = '\0';
428 /* skip '=' and whitespace */
429 while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
430 i++;
432 /* get the value part */
433 j=0;
434 while( i<len )
435 {
436 value[j++] = line[i++];
437 }
438 value[j] = '\0';
440 match_found = 1;
442 /* key definition */
443 if( !strcasecmp(CONF_KEY_DEFINITION, name) )
444 {
445 parse_key_definition(value);
446 }
447 /* enable colors */
448 else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
449 {
450 options->enable_colors = str2bool(value);
451 }
452 /* auto center */
453 else if( !strcasecmp(CONF_AUTO_CENTER, name) )
454 {
455 options->auto_center = str2bool(value);
456 }
457 /* color assignment */
458 else if( !strcasecmp(CONF_COLOR, name) )
459 {
460 parse_color(value);
461 }
462 /* wide cursor */
463 else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
464 {
465 options->wide_cursor = str2bool(value);
466 }
467 /* welcome screen list */
468 else if (!strcasecmp(CONF_WELCOME_SCREEN_LIST, name)) {
469 options->welcome_screen_list = str2bool(value);
470 }
471 /* visible bitrate */
472 else if (!strcasecmp(CONF_VISIBLE_BITRATE, name)) {
473 options->visible_bitrate = str2bool(value);
474 }
475 /* timer display type */
476 else if( !strcasecmp(CONF_TIMEDISPLAY_TYPE, name) )
477 {
478 g_free(options->timedisplay_type);
479 options->timedisplay_type=g_strdup(parse_timedisplay_type(value));
480 D("deb");
481 D(options->timedisplay_type);
482 }
483 /* color definition */
484 else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
485 {
486 parse_color_definition(value);
487 }
488 /* list format string */
489 else if( !strcasecmp(CONF_LIST_FORMAT, name) )
490 {
491 g_free(options->list_format);
492 options->list_format = get_format(value);
493 }
494 /* status format string */
495 else if( !strcasecmp(CONF_STATUS_FORMAT, name) )
496 {
497 g_free(options->status_format);
498 options->status_format = get_format(value);
499 }
500 /* xterm title format string */
501 else if( !strcasecmp(CONF_XTERM_TITLE_FORMAT, name) )
502 {
503 g_free(options->xterm_title_format);
504 options->xterm_title_format = get_format(value);
505 }
506 else if( !strcasecmp(CONF_LIST_WRAP, name) )
507 {
508 options->list_wrap = str2bool(value);
509 }
510 else if( !strcasecmp(CONF_FIND_WRAP, name) )
511 {
512 options->find_wrap = str2bool(value);
513 }
514 else if( !strcasecmp(CONF_FIND_SHOW_LAST,name) )
515 {
516 options->find_show_last_pattern = str2bool(value);
517 }
518 else if( !strcasecmp(CONF_AUDIBLE_BELL, name) )
519 {
520 options->audible_bell = str2bool(value);
521 }
522 else if( !strcasecmp(CONF_VISIBLE_BELL, name) )
523 {
524 options->visible_bell = str2bool(value);
525 }
526 else if( !strcasecmp(CONF_XTERM_TITLE, name) )
527 {
528 options->enable_xterm_title = str2bool(value);
529 }
530 else if( !strcasecmp(CONF_ENABLE_MOUSE, name) )
531 {
532 options->enable_mouse = str2bool(value);
533 }
534 else if( !strcasecmp(CONF_CROSSFADE_TIME, name) )
535 {
536 options->crossfade_time = atoi(value);
537 }
538 else if( !strcasecmp(CONF_SEARCH_MODE, name) )
539 {
540 options->search_mode = atoi(value);
541 }
542 else if( !strcasecmp(CONF_HIDE_CURSOR, name) )
543 {
544 options->hide_cursor = atoi(value);
545 }
546 else if( !strcasecmp(CONF_SEEK_TIME, name) )
547 {
548 options->seek_time = atoi(value);
549 }
550 else if( !strcasecmp(CONF_SCREEN_LIST, name) )
551 {
552 g_strfreev(options->screen_list);
553 options->screen_list = check_screen_list(value);
554 }
555 else if( !strcasecmp(CONF_SHOW_SPLASH, name) )
556 {
557 /* the splash screen was removed */
558 }
559 else if( !strcasecmp(CONF_HOST, name))
560 {
561 options->host = get_format(value);
562 }
563 else if( !strcasecmp(CONF_PORT, name))
564 {
565 options->port = atoi(get_format(value));
566 }
567 else if( !strcasecmp(CONF_PASSWORD, name))
568 {
569 options->password = get_format(value);
570 }
571 else if( !strcasecmp(CONF_LYRICS_TIMEOUT, name))
572 {
573 options->lyrics_timeout = atoi(get_format(value));
574 }
575 else if( !strcasecmp(CONF_SCROLL, name))
576 {
577 options->scroll = str2bool(value);
578 }
579 else if( !strcasecmp(CONF_SCROLL_SEP, name))
580 {
581 g_free(options->scroll_sep);
582 options->scroll_sep = get_format(value);
583 }
584 else
585 {
586 match_found = 0;
587 }
589 if( !match_found )
590 fprintf(stderr,
591 _("Unknown configuration parameter: %s\n"),
592 name);
593 D("conf> %s = %s %s\n", name, value,
594 match_found ? "" : "- UNKNOWN SETTING!" );
595 }
596 }
597 }
599 D("--\n\n");
601 if( free_filename )
602 g_free(filename);
604 return 0;
605 }
607 int
608 check_user_conf_dir(void)
609 {
610 int retval;
611 char *directory = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
613 if (g_file_test(directory, G_FILE_TEST_IS_DIR)) {
614 g_free(directory);
615 return 0;
616 }
618 retval = mkdir(directory, 0755);
619 g_free(directory);
620 return retval;
621 }
623 char *
624 get_user_key_binding_filename(void)
625 {
626 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
627 }
630 int
631 read_configuration(options_t *options)
632 {
633 char *filename = NULL;
635 /* check for command line configuration file */
636 if( options->config_file )
637 filename = g_strdup(options->config_file);
639 /* check for user configuration ~/.ncmpc/config */
640 if( filename == NULL )
641 {
642 filename = g_build_filename(g_get_home_dir(),
643 "." PACKAGE, "config", NULL);
644 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
645 {
646 g_free(filename);
647 filename = NULL;
648 }
649 }
651 /* check for global configuration SYSCONFDIR/ncmpc/config */
652 if( filename == NULL )
653 {
654 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
655 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
656 {
657 g_free(filename);
658 filename = NULL;
659 }
660 }
662 /* load configuration */
663 if( filename )
664 {
665 read_rc_file(filename, options);
666 g_free(filename);
667 filename = NULL;
668 }
670 /* check for command line key binding file */
671 if( options->key_file )
672 filename = g_strdup(options->key_file);
674 /* check for user key bindings ~/.ncmpc/keys */
675 if( filename == NULL )
676 {
677 filename = get_user_key_binding_filename();
678 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
679 {
680 g_free(filename);
681 filename = NULL;
682 }
683 }
685 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
686 if( filename == NULL )
687 {
688 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
689 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
690 {
691 g_free(filename);
692 filename = NULL;
693 }
694 }
696 /* load key bindings */
697 if( filename )
698 {
699 read_rc_file(filename, options);
700 g_free(filename);
701 filename = NULL;
702 }
704 return 0;
705 }