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 #include <ctype.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
31 #include <glib.h>
32 #include <ncurses.h>
33 #include "config.h"
34 #include "ncmpc.h"
35 #include "options.h"
36 #include "support.h"
37 #include "command.h"
38 #include "colors.h"
39 #include "conf.h"
41 #define MAX_LINE_LENGTH 1024
42 #define COMMENT_TOKEN '#'
44 /* configuration field names */
45 #define CONF_ENABLE_COLORS "enable-colors"
46 #define CONF_AUTO_CENTER "auto-center"
47 #define CONF_WIDE_CURSOR "wide-cursor"
48 #define CONF_ENABLE_BELL "enable-bell"
49 #define CONF_KEY_DEFINITION "key"
50 #define CONF_COLOR "color"
51 #define CONF_COLOR_DEFINITION "colordef"
52 #define CONF_LIST_FORMAT "list-format"
53 #define CONF_STATUS_FORMAT "status-format"
54 #define CONF_XTERM_TITLE_FORMAT "xterm-title-format"
55 #define CONF_LIST_WRAP "wrap-around"
56 #define CONF_FIND_WRAP "find-wrap"
57 #define CONF_FIND_SHOW_LAST "find-show-last"
58 #define CONF_AUDIBLE_BELL "audible-bell"
59 #define CONF_VISIBLE_BELL "visible-bell"
60 #define CONF_XTERM_TITLE "set-xterm-title"
61 #define CONF_ENABLE_MOUSE "enable-mouse"
62 #define CONF_CROSSFADE_TIME "crossfade-time"
63 #define CONF_SEARCH_MODE "search-mode"
64 #define CONF_HIDE_CURSOR "hide-cursor"
65 #define CONF_SEEK_TIME "seek-time"
66 #define CONF_SCREEN_LIST "screen-list"
67 #define CONF_TIMEDISPLAY_TYPE "timedisplay-type"
68 #define CONF_HOST "host"
69 #define CONF_PORT "port"
70 #define CONF_PASSWORD "password"
71 #define CONF_LYRICS_TIMEOUT "lyrics-timeout"
72 #define CONF_SHOW_SPLASH "show-splash"
73 #define CONF_SCROLL "scroll"
74 #define CONF_SCROLL_SEP "scroll-sep"
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 int i, value;
101 key_parser_state_t state;
103 i=0;
104 value=0;
105 state=KEY_PARSER_UNKNOWN;
106 *end = str;
108 while( i<len && state!=KEY_PARSER_DONE )
109 {
110 int next = 0;
111 int c = str[i];
113 if( i+1<len )
114 next = str[i+1];
116 switch(state)
117 {
118 case KEY_PARSER_UNKNOWN:
119 if( c=='\'' )
120 state = KEY_PARSER_CHAR;
121 else if( c=='0' && next=='x' )
122 state = KEY_PARSER_HEX;
123 else if( isdigit(c) )
124 state = KEY_PARSER_DEC;
125 else {
126 fprintf(stderr,
127 _("Error: Unsupported key definition - %s\n"),
128 str);
129 return -1;
130 }
131 break;
132 case KEY_PARSER_CHAR:
133 if( next!='\'' )
134 {
135 fprintf(stderr,
136 _("Error: Unsupported key definition - %s\n"),
137 str);
138 return -1;
139 }
140 value = c;
141 *end = str+i+2;
142 state = KEY_PARSER_DONE;
143 break;
144 case KEY_PARSER_DEC:
145 value = (int) strtol(str+(i-1), end, 10);
146 state = KEY_PARSER_DONE;
147 break;
148 case KEY_PARSER_HEX:
149 if( !isdigit(next) )
150 {
151 fprintf(stderr,_("Error: Digit expected after 0x - %s\n"), str);
152 return -1;
153 }
154 value = (int) strtol(str+(i+1), end, 16);
155 state = KEY_PARSER_DONE;
156 break;
157 case KEY_PARSER_DONE:
158 break;
159 }
160 i++;
161 }
163 if( *end> str+len )
164 *end = str+len;
166 return value;
167 }
169 static int
170 parse_key_definition(char *str)
171 {
172 char buf[MAX_LINE_LENGTH];
173 char *p, *end;
174 size_t len = strlen(str);
175 int i,j,key;
176 int keys[MAX_COMMAND_KEYS];
177 command_t cmd;
179 /* get the command name */
180 i=0;
181 j=0;
182 memset(buf, 0, MAX_LINE_LENGTH);
183 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
184 buf[j++] = str[i++];
185 if( (cmd=get_key_command_from_name(buf)) == CMD_NONE )
186 {
187 fprintf(stderr, _("Error: Unknown key command %s\n"), buf);
188 return -1;
189 }
191 /* skip whitespace */
192 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
193 i++;
195 /* get the value part */
196 memset(buf, 0, MAX_LINE_LENGTH);
197 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
198 len = strlen(buf);
199 if( len==0 )
200 {
201 fprintf(stderr,_("Error: Incomplete key definition - %s\n"), str);
202 return -1;
203 }
205 /* parse key values */
206 i = 0;
207 key = 0;
208 len = strlen(buf);
209 p = buf;
210 end = buf+len;
211 memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
212 while( i<MAX_COMMAND_KEYS && p<end && (key=parse_key_value(p,len+1,&p))>=0 )
213 {
214 keys[i++] = key;
215 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
216 p++;
217 len = strlen(p);
218 }
219 if( key<0 )
220 {
221 fprintf(stderr,_("Error: Bad key definition - %s\n"), str);
222 return -1;
223 }
225 return assign_keys(cmd, keys);
226 }
228 static char *
229 parse_timedisplay_type(char *str)
230 {
231 if((!strcmp(str,"elapsed")) || (!strcmp(str,"remaining"))){
232 return str;
233 } else {
234 fprintf(stderr,_("Error: Bad time display type - %s\n"), str);
235 return DEFAULT_TIMEDISPLAY_TYPE;
236 }
237 }
239 static int
240 parse_color(char *str)
241 {
242 char *name = str;
243 char *value = NULL;
244 int len,i;
246 i=0;
247 len=strlen(str);
248 /* get the color name */
249 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
250 i++;
252 /* skip whitespace */
253 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
254 {
255 str[i]='\0';
256 i++;
257 }
259 if( i<len )
260 value = str+i;
262 return colors_assign(name, value);
263 }
265 static int
266 parse_color_definition(char *str)
267 {
268 char buf[MAX_LINE_LENGTH];
269 char *p, *end, *name;
270 size_t len = strlen(str);
271 int i,j,value;
272 short color, rgb[3];
274 /* get the command name */
275 i=0;
276 j=0;
277 memset(buf, 0, MAX_LINE_LENGTH);
278 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
279 buf[j++] = str[i++];
280 color=colors_str2color(buf);
281 if( color<0 )
282 {
283 fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
284 return -1;
285 }
286 name = g_strdup(buf);
288 /* skip whitespace */
289 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
290 i++;
292 /* get the value part */
293 memset(buf, 0, MAX_LINE_LENGTH);
294 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
295 len = strlen(buf);
296 if( len==0 )
297 {
298 fprintf(stderr,_("Error: Incomplete color definition - %s\n"), str);
299 g_free(name);
300 return -1;
301 }
303 /* parse r,g.b values with the key definition parser */
304 i = 0;
305 value = 0;
306 len = strlen(buf);
307 p = buf;
308 end = buf+len;
309 memset(rgb, 0, sizeof(short)*3);
310 while( i<3 && p<end && (value=parse_key_value(p,len+1,&p))>=0 )
311 {
312 rgb[i++] = value;
313 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
314 p++;
315 len = strlen(p);
316 }
317 if( value<0 || i!=3)
318 {
319 fprintf(stderr,_("Error: Bad color definition - %s\n"), str);
320 g_free(name);
321 return -1;
322 }
323 value = colors_define(name, rgb[0], rgb[1], rgb[2]);
324 g_free(name);
325 return value;
326 }
328 static char *
329 get_format(char *str)
330 {
331 gsize len = strlen(str);
333 if( str && str[0]=='\"' && str[len-1] == '\"' )
334 {
335 str[len-1] = '\0';
336 str++;
337 }
338 return g_strdup(str);
339 }
341 static char **
342 check_screen_list(char *value)
343 {
344 char **tmp = g_strsplit_set(value, " \t,", 100);
345 char **screen = NULL;
346 int i,j;
348 i=0;
349 j=0;
350 while( tmp && tmp[i] )
351 {
352 tmp[i] = lowerstr(tmp[i]);
353 if( screen_get_id(tmp[i]) == -1 )
354 fprintf(stderr,
355 _("Error: Unsupported screen \"%s\"\n"),
356 tmp[i]);
357 else
358 {
359 screen = g_realloc(screen, (j+2)*sizeof(char *));
360 screen[j++] = g_strdup(tmp[i]);
361 screen[j] = NULL;
362 }
363 i++;
364 }
365 g_strfreev(tmp);
366 if( screen == NULL )
367 return g_strsplit_set(DEFAULT_SCREEN_LIST, " ", 0);
369 return screen;
370 }
372 static int
373 read_rc_file(char *filename, options_t *options)
374 {
375 int fd;
376 int quit = 0;
377 int free_filename = 0;
379 if( filename==NULL )
380 return -1;
382 D("Reading configuration file %s\n", filename);
383 if( (fd=open(filename,O_RDONLY)) <0 )
384 {
385 perror(filename);
386 if( free_filename )
387 g_free(filename);
388 return -1;
389 }
391 while( !quit )
392 {
393 int i,j;
394 int len;
395 int match_found;
396 char line[MAX_LINE_LENGTH];
397 char name[MAX_LINE_LENGTH];
398 char value[MAX_LINE_LENGTH];
400 line[0] = '\0';
401 value[0] = '\0';
403 i = 0;
404 /* read a line ending with '\n' */
405 do {
406 len = read( fd, &line[i], 1 );
407 if( len == 1 )
408 i++;
409 else
410 quit = 1;
411 } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
414 /* remove trailing whitespace */
415 line[i] = '\0';
416 i--;
417 while( i>=0 && IS_WHITESPACE(line[i]) )
418 {
419 line[i] = '\0';
420 i--;
421 }
422 len = i+1;
424 if( len>0 )
425 {
426 i = 0;
427 /* skip whitespace */
428 while( i<len && IS_WHITESPACE(line[i]) )
429 i++;
431 /* continue if this line is not a comment */
432 if( line[i] != COMMENT_TOKEN )
433 {
434 /* get the name part */
435 j=0;
436 while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
437 {
438 name[j++] = line[i++];
439 }
440 name[j] = '\0';
442 /* skip '=' and whitespace */
443 while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
444 i++;
446 /* get the value part */
447 j=0;
448 while( i<len )
449 {
450 value[j++] = line[i++];
451 }
452 value[j] = '\0';
454 match_found = 1;
456 /* key definition */
457 if( !strcasecmp(CONF_KEY_DEFINITION, name) )
458 {
459 parse_key_definition(value);
460 }
461 /* enable colors */
462 else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
463 {
464 options->enable_colors = str2bool(value);
465 }
466 /* auto center */
467 else if( !strcasecmp(CONF_AUTO_CENTER, name) )
468 {
469 options->auto_center = str2bool(value);
470 }
471 /* color assignment */
472 else if( !strcasecmp(CONF_COLOR, name) )
473 {
474 parse_color(value);
475 }
476 /* wide cursor */
477 else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
478 {
479 options->wide_cursor = str2bool(value);
480 }
481 /* timer display type */
482 else if( !strcasecmp(CONF_TIMEDISPLAY_TYPE, name) )
483 {
484 g_free(options->timedisplay_type);
485 options->timedisplay_type=g_strdup(parse_timedisplay_type(value));
486 D("deb");
487 D(options->timedisplay_type);
488 }
489 /* color definition */
490 else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
491 {
492 parse_color_definition(value);
493 }
494 /* list format string */
495 else if( !strcasecmp(CONF_LIST_FORMAT, name) )
496 {
497 g_free(options->list_format);
498 options->list_format = get_format(value);
499 }
500 /* status format string */
501 else if( !strcasecmp(CONF_STATUS_FORMAT, name) )
502 {
503 g_free(options->status_format);
504 options->status_format = get_format(value);
505 }
506 /* xterm title format string */
507 else if( !strcasecmp(CONF_XTERM_TITLE_FORMAT, name) )
508 {
509 g_free(options->xterm_title_format);
510 options->xterm_title_format = get_format(value);
511 }
512 else if( !strcasecmp(CONF_LIST_WRAP, name) )
513 {
514 options->list_wrap = str2bool(value);
515 }
516 else if( !strcasecmp(CONF_FIND_WRAP, name) )
517 {
518 options->find_wrap = str2bool(value);
519 }
520 else if( !strcasecmp(CONF_FIND_SHOW_LAST,name) )
521 {
522 options->find_show_last_pattern = str2bool(value);
523 }
524 else if( !strcasecmp(CONF_AUDIBLE_BELL, name) )
525 {
526 options->audible_bell = str2bool(value);
527 }
528 else if( !strcasecmp(CONF_VISIBLE_BELL, name) )
529 {
530 options->visible_bell = str2bool(value);
531 }
532 else if( !strcasecmp(CONF_XTERM_TITLE, name) )
533 {
534 options->enable_xterm_title = str2bool(value);
535 }
536 else if( !strcasecmp(CONF_ENABLE_MOUSE, name) )
537 {
538 options->enable_mouse = str2bool(value);
539 }
540 else if( !strcasecmp(CONF_CROSSFADE_TIME, name) )
541 {
542 options->crossfade_time = atoi(value);
543 }
544 else if( !strcasecmp(CONF_SEARCH_MODE, name) )
545 {
546 options->search_mode = atoi(value);
547 }
548 else if( !strcasecmp(CONF_HIDE_CURSOR, name) )
549 {
550 options->hide_cursor = atoi(value);
551 }
552 else if( !strcasecmp(CONF_SEEK_TIME, name) )
553 {
554 options->seek_time = atoi(value);
555 }
556 else if( !strcasecmp(CONF_SCREEN_LIST, name) )
557 {
558 g_strfreev(options->screen_list);
559 options->screen_list = check_screen_list(value);
560 }
561 else if( !strcasecmp(CONF_SHOW_SPLASH, name) )
562 {
563 /* the splash screen was removed */
564 }
565 else if( !strcasecmp(CONF_HOST, name))
566 {
567 options->host = get_format(value);
568 }
569 else if( !strcasecmp(CONF_PORT, name))
570 {
571 options->port = atoi(get_format(value));
572 }
573 else if( !strcasecmp(CONF_PASSWORD, name))
574 {
575 options->password = get_format(value);
576 }
577 else if( !strcasecmp(CONF_LYRICS_TIMEOUT, name))
578 {
579 options->lyrics_timeout = atoi(get_format(value));
580 }
581 else if( !strcasecmp(CONF_SCROLL, name))
582 {
583 options->scroll = str2bool(value);
584 }
585 else if( !strcasecmp(CONF_SCROLL_SEP, name))
586 {
587 g_free(options->scroll_sep);
588 options->scroll_sep = get_format(value);
589 }
590 else
591 {
592 match_found = 0;
593 }
595 if( !match_found )
596 fprintf(stderr,
597 _("Unknown configuration parameter: %s\n"),
598 name);
599 D("conf> %s = %s %s\n", name, value,
600 match_found ? "" : "- UNKNOWN SETTING!" );
601 }
602 }
603 }
605 D("--\n\n");
607 if( free_filename )
608 g_free(filename);
610 return 0;
611 }
613 int
614 check_user_conf_dir(void)
615 {
616 int retval;
617 char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
619 if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
620 {
621 g_free(dirname);
622 return 0;
623 }
624 retval = mkdir(dirname, 0755);
625 g_free(dirname);
626 return retval;
627 }
629 char *
630 get_user_key_binding_filename(void)
631 {
632 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
633 }
636 int
637 read_configuration(options_t *options)
638 {
639 char *filename = NULL;
641 /* check for command line configuration file */
642 if( options->config_file )
643 filename = g_strdup(options->config_file);
645 /* check for user configuration ~/.ncmpc/config */
646 if( filename == NULL )
647 {
648 filename = g_build_filename(g_get_home_dir(),
649 "." PACKAGE, "config", NULL);
650 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
651 {
652 g_free(filename);
653 filename = NULL;
654 }
655 }
657 /* check for global configuration SYSCONFDIR/ncmpc/config */
658 if( filename == NULL )
659 {
660 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
661 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
662 {
663 g_free(filename);
664 filename = NULL;
665 }
666 }
668 /* load configuration */
669 if( filename )
670 {
671 read_rc_file(filename, options);
672 g_free(filename);
673 filename = NULL;
674 }
676 /* check for command line key binding file */
677 if( options->key_file )
678 filename = g_strdup(options->key_file);
680 /* check for user key bindings ~/.ncmpc/keys */
681 if( filename == NULL )
682 {
683 filename = get_user_key_binding_filename();
684 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
685 {
686 g_free(filename);
687 filename = NULL;
688 }
689 }
691 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
692 if( filename == NULL )
693 {
694 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
695 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
696 {
697 g_free(filename);
698 filename = NULL;
699 }
700 }
702 /* load key bindings */
703 if( filename )
704 {
705 read_rc_file(filename, options);
706 g_free(filename);
707 filename = NULL;
708 }
710 return 0;
711 }