3af986b95fe89df1c99a72021c2b2b03b2352ddb
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"
74 typedef enum {
75 KEY_PARSER_UNKNOWN,
76 KEY_PARSER_CHAR,
77 KEY_PARSER_DEC,
78 KEY_PARSER_HEX,
79 KEY_PARSER_DONE
80 } key_parser_state_t;
83 extern gint screen_get_id(char *name);
86 static gboolean
87 str2bool(char *str)
88 {
89 if( !strcasecmp(str,"yes") || !strcasecmp(str,"true") ||
90 !strcasecmp(str,"on") || !strcasecmp(str,"1") )
91 return TRUE;
92 return FALSE;
93 }
95 static int
96 parse_key_value(char *str, size_t len, char **end)
97 {
98 int i, value;
99 key_parser_state_t state;
101 i=0;
102 value=0;
103 state=KEY_PARSER_UNKNOWN;
104 *end = str;
106 while( i<len && state!=KEY_PARSER_DONE )
107 {
108 int next = 0;
109 int c = str[i];
111 if( i+1<len )
112 next = str[i+1];
114 switch(state)
115 {
116 case KEY_PARSER_UNKNOWN:
117 if( c=='\'' )
118 state = KEY_PARSER_CHAR;
119 else if( c=='0' && next=='x' )
120 state = KEY_PARSER_HEX;
121 else if( isdigit(c) )
122 state = KEY_PARSER_DEC;
123 else {
124 fprintf(stderr,
125 _("Error: Unsupported key definition - %s\n"),
126 str);
127 return -1;
128 }
129 break;
130 case KEY_PARSER_CHAR:
131 if( next!='\'' )
132 {
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 {
149 fprintf(stderr,_("Error: Digit expected after 0x - %s\n"), str);
150 return -1;
151 }
152 value = (int) strtol(str+(i+1), end, 16);
153 state = KEY_PARSER_DONE;
154 break;
155 case KEY_PARSER_DONE:
156 break;
157 }
158 i++;
159 }
161 if( *end> str+len )
162 *end = str+len;
164 return value;
165 }
167 static int
168 parse_key_definition(char *str)
169 {
170 char buf[MAX_LINE_LENGTH];
171 char *p, *end;
172 size_t len = strlen(str);
173 int i,j,key;
174 int keys[MAX_COMMAND_KEYS];
175 command_t cmd;
177 /* get the command name */
178 i=0;
179 j=0;
180 memset(buf, 0, MAX_LINE_LENGTH);
181 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
182 buf[j++] = str[i++];
183 if( (cmd=get_key_command_from_name(buf)) == CMD_NONE )
184 {
185 fprintf(stderr, _("Error: Unknown key command %s\n"), buf);
186 return -1;
187 }
189 /* skip whitespace */
190 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
191 i++;
193 /* get the value part */
194 memset(buf, 0, MAX_LINE_LENGTH);
195 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
196 len = strlen(buf);
197 if( len==0 )
198 {
199 fprintf(stderr,_("Error: Incomplete key definition - %s\n"), str);
200 return -1;
201 }
203 /* parse key values */
204 i = 0;
205 key = 0;
206 len = strlen(buf);
207 p = buf;
208 end = buf+len;
209 memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
210 while( i<MAX_COMMAND_KEYS && p<end && (key=parse_key_value(p,len+1,&p))>=0 )
211 {
212 keys[i++] = key;
213 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
214 p++;
215 len = strlen(p);
216 }
217 if( key<0 )
218 {
219 fprintf(stderr,_("Error: Bad key definition - %s\n"), str);
220 return -1;
221 }
223 return assign_keys(cmd, keys);
224 }
226 static char *
227 parse_timedisplay_type(char *str)
228 {
229 if((!strcmp(str,"elapsed")) || (!strcmp(str,"remaining"))){
230 return str;
231 } else {
232 fprintf(stderr,_("Error: Bad time display type - %s\n"), str);
233 return DEFAULT_TIMEDISPLAY_TYPE;
234 }
235 }
237 static int
238 parse_color(char *str)
239 {
240 char *name = str;
241 char *value = NULL;
242 int len,i;
244 i=0;
245 len=strlen(str);
246 /* get the color name */
247 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
248 i++;
250 /* skip whitespace */
251 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
252 {
253 str[i]='\0';
254 i++;
255 }
257 if( i<len )
258 value = str+i;
260 return colors_assign(name, value);
261 }
263 static int
264 parse_color_definition(char *str)
265 {
266 char buf[MAX_LINE_LENGTH];
267 char *p, *end, *name;
268 size_t len = strlen(str);
269 int i,j,value;
270 short color, rgb[3];
272 /* get the command name */
273 i=0;
274 j=0;
275 memset(buf, 0, MAX_LINE_LENGTH);
276 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
277 buf[j++] = str[i++];
278 color=colors_str2color(buf);
279 if( color<0 )
280 {
281 fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
282 return -1;
283 }
284 name = g_strdup(buf);
286 /* skip whitespace */
287 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
288 i++;
290 /* get the value part */
291 memset(buf, 0, MAX_LINE_LENGTH);
292 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
293 len = strlen(buf);
294 if( len==0 )
295 {
296 fprintf(stderr,_("Error: Incomplete color definition - %s\n"), str);
297 g_free(name);
298 return -1;
299 }
301 /* parse r,g.b values with the key definition parser */
302 i = 0;
303 value = 0;
304 len = strlen(buf);
305 p = buf;
306 end = buf+len;
307 memset(rgb, 0, sizeof(short)*3);
308 while( i<3 && p<end && (value=parse_key_value(p,len+1,&p))>=0 )
309 {
310 rgb[i++] = value;
311 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
312 p++;
313 len = strlen(p);
314 }
315 if( value<0 || i!=3)
316 {
317 fprintf(stderr,_("Error: Bad color definition - %s\n"), str);
318 g_free(name);
319 return -1;
320 }
321 value = colors_define(name, rgb[0], rgb[1], rgb[2]);
322 g_free(name);
323 return value;
324 }
326 static char *
327 get_format(char *str)
328 {
329 gsize len = strlen(str);
331 if( str && str[0]=='\"' && str[len-1] == '\"' )
332 {
333 str[len-1] = '\0';
334 str++;
335 }
336 return g_strdup(str);
337 }
339 static char **
340 check_screen_list(char *value)
341 {
342 char **tmp = g_strsplit_set(value, " \t,", 100);
343 char **screen = NULL;
344 int i,j;
346 i=0;
347 j=0;
348 while( tmp && tmp[i] )
349 {
350 tmp[i] = lowerstr(tmp[i]);
351 if( screen_get_id(tmp[i]) == -1 )
352 fprintf(stderr,
353 _("Error: Unsupported screen \"%s\"\n"),
354 tmp[i]);
355 else
356 {
357 screen = g_realloc(screen, (j+2)*sizeof(char *));
358 screen[j++] = g_strdup(tmp[i]);
359 screen[j] = NULL;
360 }
361 i++;
362 }
363 g_strfreev(tmp);
364 if( screen == NULL )
365 return g_strsplit_set(DEFAULT_SCREEN_LIST, " ", 0);
367 return screen;
368 }
370 static int
371 read_rc_file(char *filename, options_t *options)
372 {
373 int fd;
374 int quit = 0;
375 int free_filename = 0;
377 if( filename==NULL )
378 return -1;
380 D("Reading configuration file %s\n", filename);
381 if( (fd=open(filename,O_RDONLY)) <0 )
382 {
383 perror(filename);
384 if( free_filename )
385 g_free(filename);
386 return -1;
387 }
389 while( !quit )
390 {
391 int i,j;
392 int len;
393 int match_found;
394 char line[MAX_LINE_LENGTH];
395 char name[MAX_LINE_LENGTH];
396 char value[MAX_LINE_LENGTH];
398 line[0] = '\0';
399 value[0] = '\0';
401 i = 0;
402 /* read a line ending with '\n' */
403 do {
404 len = read( fd, &line[i], 1 );
405 if( len == 1 )
406 i++;
407 else
408 quit = 1;
409 } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
412 /* remove trailing whitespace */
413 line[i] = '\0';
414 i--;
415 while( i>=0 && IS_WHITESPACE(line[i]) )
416 {
417 line[i] = '\0';
418 i--;
419 }
420 len = i+1;
422 if( len>0 )
423 {
424 i = 0;
425 /* skip whitespace */
426 while( i<len && IS_WHITESPACE(line[i]) )
427 i++;
429 /* continue if this line is not a comment */
430 if( line[i] != COMMENT_TOKEN )
431 {
432 /* get the name part */
433 j=0;
434 while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
435 {
436 name[j++] = line[i++];
437 }
438 name[j] = '\0';
440 /* skip '=' and whitespace */
441 while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
442 i++;
444 /* get the value part */
445 j=0;
446 while( i<len )
447 {
448 value[j++] = line[i++];
449 }
450 value[j] = '\0';
452 match_found = 1;
454 /* key definition */
455 if( !strcasecmp(CONF_KEY_DEFINITION, name) )
456 {
457 parse_key_definition(value);
458 }
459 /* enable colors */
460 else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
461 {
462 options->enable_colors = str2bool(value);
463 }
464 /* auto center */
465 else if( !strcasecmp(CONF_AUTO_CENTER, name) )
466 {
467 options->auto_center = str2bool(value);
468 }
469 /* color assignment */
470 else if( !strcasecmp(CONF_COLOR, name) )
471 {
472 parse_color(value);
473 }
474 /* wide cursor */
475 else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
476 {
477 options->wide_cursor = str2bool(value);
478 }
479 /* timer display type */
480 else if( !strcasecmp(CONF_TIMEDISPLAY_TYPE, name) )
481 {
482 g_free(options->timedisplay_type);
483 options->timedisplay_type=g_strdup(parse_timedisplay_type(value));
484 D("deb");
485 D(options->timedisplay_type);
486 }
487 /* color definition */
488 else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
489 {
490 parse_color_definition(value);
491 }
492 /* list format string */
493 else if( !strcasecmp(CONF_LIST_FORMAT, name) )
494 {
495 g_free(options->list_format);
496 options->list_format = get_format(value);
497 }
498 /* status format string */
499 else if( !strcasecmp(CONF_STATUS_FORMAT, name) )
500 {
501 g_free(options->status_format);
502 options->status_format = get_format(value);
503 }
504 /* xterm title format string */
505 else if( !strcasecmp(CONF_XTERM_TITLE_FORMAT, name) )
506 {
507 g_free(options->xterm_title_format);
508 options->xterm_title_format = get_format(value);
509 }
510 else if( !strcasecmp(CONF_LIST_WRAP, name) )
511 {
512 options->list_wrap = str2bool(value);
513 }
514 else if( !strcasecmp(CONF_FIND_WRAP, name) )
515 {
516 options->find_wrap = str2bool(value);
517 }
518 else if( !strcasecmp(CONF_FIND_SHOW_LAST,name) )
519 {
520 options->find_show_last_pattern = str2bool(value);
521 }
522 else if( !strcasecmp(CONF_AUDIBLE_BELL, name) )
523 {
524 options->audible_bell = str2bool(value);
525 }
526 else if( !strcasecmp(CONF_VISIBLE_BELL, name) )
527 {
528 options->visible_bell = str2bool(value);
529 }
530 else if( !strcasecmp(CONF_XTERM_TITLE, name) )
531 {
532 options->enable_xterm_title = str2bool(value);
533 }
534 else if( !strcasecmp(CONF_ENABLE_MOUSE, name) )
535 {
536 options->enable_mouse = str2bool(value);
537 }
538 else if( !strcasecmp(CONF_CROSSFADE_TIME, name) )
539 {
540 options->crossfade_time = atoi(value);
541 }
542 else if( !strcasecmp(CONF_SEARCH_MODE, name) )
543 {
544 options->search_mode = atoi(value);
545 }
546 else if( !strcasecmp(CONF_HIDE_CURSOR, name) )
547 {
548 options->hide_cursor = atoi(value);
549 }
550 else if( !strcasecmp(CONF_SEEK_TIME, name) )
551 {
552 options->seek_time = atoi(value);
553 }
554 else if( !strcasecmp(CONF_SCREEN_LIST, name) )
555 {
556 g_strfreev(options->screen_list);
557 options->screen_list = check_screen_list(value);
558 }
559 else if( !strcasecmp(CONF_SHOW_SPLASH, name) )
560 {
561 options->show_splash = str2bool(value);
563 #ifdef DEBUG
564 D("screen-list:");
565 j=0;
566 while(options->screen_list[j])
567 D(" %s", options->screen_list[j++]);
568 D("\n");
569 #endif
570 }
571 else if( !strcasecmp(CONF_HOST, name))
572 {
573 options->host = get_format(value);
574 }
575 else if( !strcasecmp(CONF_PORT, name))
576 {
577 options->port = atoi(get_format(value));
578 }
579 else if( !strcasecmp(CONF_PASSWORD, name))
580 {
581 options->password = get_format(value);
582 }
583 else if( !strcasecmp(CONF_LYRICS_TIMEOUT, name))
584 {
585 options->lyrics_timeout = atoi(get_format(value));
586 }
587 else
588 {
589 match_found = 0;
590 }
592 if( !match_found )
593 fprintf(stderr,
594 _("Unknown configuration parameter: %s\n"),
595 name);
596 D("conf> %s = %s %s\n", name, value,
597 match_found ? "" : "- UNKNOWN SETTING!" );
598 }
599 }
600 }
602 D("--\n\n");
604 if( free_filename )
605 g_free(filename);
607 return 0;
608 }
610 int
611 check_user_conf_dir(void)
612 {
613 int retval;
614 char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
616 if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
617 {
618 g_free(dirname);
619 return 0;
620 }
621 retval = mkdir(dirname, 0755);
622 g_free(dirname);
623 return retval;
624 }
626 char *
627 get_user_key_binding_filename(void)
628 {
629 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
630 }
633 int
634 read_configuration(options_t *options)
635 {
636 char *filename = NULL;
638 /* check for command line configuration file */
639 if( options->config_file )
640 filename = g_strdup(options->config_file);
642 /* check for user configuration ~/.ncmpc/config */
643 if( filename == NULL )
644 {
645 filename = g_build_filename(g_get_home_dir(),
646 "." PACKAGE, "config", NULL);
647 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
648 {
649 g_free(filename);
650 filename = NULL;
651 }
652 }
654 /* check for global configuration SYSCONFDIR/ncmpc/config */
655 if( filename == NULL )
656 {
657 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
658 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
659 {
660 g_free(filename);
661 filename = NULL;
662 }
663 }
665 /* load configuration */
666 if( filename )
667 {
668 read_rc_file(filename, options);
669 g_free(filename);
670 filename = NULL;
671 }
673 /* check for command line key binding file */
674 if( options->key_file )
675 filename = g_strdup(options->key_file);
677 /* check for user key bindings ~/.ncmpc/keys */
678 if( filename == NULL )
679 {
680 filename = get_user_key_binding_filename();
681 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
682 {
683 g_free(filename);
684 filename = NULL;
685 }
686 }
688 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
689 if( filename == NULL )
690 {
691 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
692 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
693 {
694 g_free(filename);
695 filename = NULL;
696 }
697 }
699 /* load key bindings */
700 if( filename )
701 {
702 read_rc_file(filename, options);
703 g_free(filename);
704 filename = NULL;
705 }
707 return 0;
708 }