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_LYRICS_TIMEOUT "lyrics-timeout"
71 #define CONF_SHOW_SPLASH "show-splash"
73 typedef enum {
74 KEY_PARSER_UNKNOWN,
75 KEY_PARSER_CHAR,
76 KEY_PARSER_DEC,
77 KEY_PARSER_HEX,
78 KEY_PARSER_DONE
79 } key_parser_state_t;
82 extern gint screen_get_id(char *name);
85 static gboolean
86 str2bool(char *str)
87 {
88 if( !strcasecmp(str,"yes") || !strcasecmp(str,"true") ||
89 !strcasecmp(str,"on") || !strcasecmp(str,"1") )
90 return TRUE;
91 return FALSE;
92 }
94 static int
95 parse_key_value(char *str, size_t len, char **end)
96 {
97 int i, value;
98 key_parser_state_t state;
100 i=0;
101 value=0;
102 state=KEY_PARSER_UNKNOWN;
103 *end = str;
105 while( i<len && state!=KEY_PARSER_DONE )
106 {
107 int next = 0;
108 int c = str[i];
110 if( i+1<len )
111 next = str[i+1];
113 switch(state)
114 {
115 case KEY_PARSER_UNKNOWN:
116 if( c=='\'' )
117 state = KEY_PARSER_CHAR;
118 else if( c=='0' && next=='x' )
119 state = KEY_PARSER_HEX;
120 else if( isdigit(c) )
121 state = KEY_PARSER_DEC;
122 else {
123 fprintf(stderr,
124 _("Error: Unsupported key definition - %s\n"),
125 str);
126 return -1;
127 }
128 break;
129 case KEY_PARSER_CHAR:
130 if( next!='\'' )
131 {
132 fprintf(stderr,
133 _("Error: Unsupported key definition - %s\n"),
134 str);
135 return -1;
136 }
137 value = c;
138 *end = str+i+2;
139 state = KEY_PARSER_DONE;
140 break;
141 case KEY_PARSER_DEC:
142 value = (int) strtol(str+(i-1), end, 10);
143 state = KEY_PARSER_DONE;
144 break;
145 case KEY_PARSER_HEX:
146 if( !isdigit(next) )
147 {
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);
172 int i,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 {
184 fprintf(stderr, _("Error: Unknown key command %s\n"), buf);
185 return -1;
186 }
188 /* skip whitespace */
189 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
190 i++;
192 /* get the value part */
193 memset(buf, 0, MAX_LINE_LENGTH);
194 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
195 len = strlen(buf);
196 if( len==0 )
197 {
198 fprintf(stderr,_("Error: Incomplete key definition - %s\n"), str);
199 return -1;
200 }
202 /* parse key values */
203 i = 0;
204 key = 0;
205 len = strlen(buf);
206 p = buf;
207 end = buf+len;
208 memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
209 while( i<MAX_COMMAND_KEYS && p<end && (key=parse_key_value(p,len+1,&p))>=0 )
210 {
211 keys[i++] = key;
212 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
213 p++;
214 len = strlen(p);
215 }
216 if( key<0 )
217 {
218 fprintf(stderr,_("Error: Bad key definition - %s\n"), str);
219 return -1;
220 }
222 return assign_keys(cmd, keys);
223 }
225 static char *
226 parse_timedisplay_type(char *str)
227 {
228 if((!strcmp(str,"elapsed")) || (!strcmp(str,"remaining"))){
229 return str;
230 } else {
231 fprintf(stderr,_("Error: Bad time display type - %s\n"), str);
232 return DEFAULT_TIMEDISPLAY_TYPE;
233 }
234 }
236 static int
237 parse_color(char *str)
238 {
239 char *name = str;
240 char *value = NULL;
241 int len,i;
243 i=0;
244 len=strlen(str);
245 /* get the color name */
246 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
247 i++;
249 /* skip whitespace */
250 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
251 {
252 str[i]='\0';
253 i++;
254 }
256 if( i<len )
257 value = str+i;
259 return colors_assign(name, value);
260 }
262 static int
263 parse_color_definition(char *str)
264 {
265 char buf[MAX_LINE_LENGTH];
266 char *p, *end, *name;
267 size_t len = strlen(str);
268 int i,j,value;
269 short color, rgb[3];
271 /* get the command name */
272 i=0;
273 j=0;
274 memset(buf, 0, MAX_LINE_LENGTH);
275 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
276 buf[j++] = str[i++];
277 color=colors_str2color(buf);
278 if( color<0 )
279 {
280 fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
281 return -1;
282 }
283 name = g_strdup(buf);
285 /* skip whitespace */
286 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
287 i++;
289 /* get the value part */
290 memset(buf, 0, MAX_LINE_LENGTH);
291 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
292 len = strlen(buf);
293 if( len==0 )
294 {
295 fprintf(stderr,_("Error: Incomplete color definition - %s\n"), str);
296 g_free(name);
297 return -1;
298 }
300 /* parse r,g.b values with the key definition parser */
301 i = 0;
302 value = 0;
303 len = strlen(buf);
304 p = buf;
305 end = buf+len;
306 memset(rgb, 0, sizeof(short)*3);
307 while( i<3 && p<end && (value=parse_key_value(p,len+1,&p))>=0 )
308 {
309 rgb[i++] = value;
310 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
311 p++;
312 len = strlen(p);
313 }
314 if( value<0 || i!=3)
315 {
316 fprintf(stderr,_("Error: Bad color definition - %s\n"), str);
317 g_free(name);
318 return -1;
319 }
320 value = colors_define(name, rgb[0], rgb[1], rgb[2]);
321 g_free(name);
322 return value;
323 }
325 static char *
326 get_format(char *str)
327 {
328 gsize len = strlen(str);
330 if( str && str[0]=='\"' && str[len-1] == '\"' )
331 {
332 str[len-1] = '\0';
333 str++;
334 }
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 {
349 tmp[i] = lowerstr(tmp[i]);
350 if( screen_get_id(tmp[i]) == -1 )
351 fprintf(stderr,
352 _("Error: Unsupported screen \"%s\"\n"),
353 tmp[i]);
354 else
355 {
356 screen = g_realloc(screen, (j+2)*sizeof(char *));
357 screen[j++] = g_strdup(tmp[i]);
358 screen[j] = NULL;
359 }
360 i++;
361 }
362 g_strfreev(tmp);
363 if( screen == NULL )
364 return g_strsplit_set(DEFAULT_SCREEN_LIST, " ", 0);
366 return screen;
367 }
369 static int
370 read_rc_file(char *filename, options_t *options)
371 {
372 int fd;
373 int quit = 0;
374 int free_filename = 0;
376 if( filename==NULL )
377 return -1;
379 D("Reading configuration file %s\n", filename);
380 if( (fd=open(filename,O_RDONLY)) <0 )
381 {
382 perror(filename);
383 if( free_filename )
384 g_free(filename);
385 return -1;
386 }
388 while( !quit )
389 {
390 int i,j;
391 int len;
392 int match_found;
393 char line[MAX_LINE_LENGTH];
394 char name[MAX_LINE_LENGTH];
395 char value[MAX_LINE_LENGTH];
397 line[0] = '\0';
398 value[0] = '\0';
400 i = 0;
401 /* read a line ending with '\n' */
402 do {
403 len = read( fd, &line[i], 1 );
404 if( len == 1 )
405 i++;
406 else
407 quit = 1;
408 } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
411 /* remove trailing whitespace */
412 line[i] = '\0';
413 i--;
414 while( i>=0 && IS_WHITESPACE(line[i]) )
415 {
416 line[i] = '\0';
417 i--;
418 }
419 len = i+1;
421 if( len>0 )
422 {
423 i = 0;
424 /* skip whitespace */
425 while( i<len && IS_WHITESPACE(line[i]) )
426 i++;
428 /* continue if this line is not a comment */
429 if( line[i] != COMMENT_TOKEN )
430 {
431 /* get the name part */
432 j=0;
433 while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
434 {
435 name[j++] = line[i++];
436 }
437 name[j] = '\0';
439 /* skip '=' and whitespace */
440 while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
441 i++;
443 /* get the value part */
444 j=0;
445 while( i<len )
446 {
447 value[j++] = line[i++];
448 }
449 value[j] = '\0';
451 match_found = 1;
453 /* key definition */
454 if( !strcasecmp(CONF_KEY_DEFINITION, name) )
455 {
456 parse_key_definition(value);
457 }
458 /* enable colors */
459 else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
460 {
461 options->enable_colors = str2bool(value);
462 }
463 /* auto center */
464 else if( !strcasecmp(CONF_AUTO_CENTER, name) )
465 {
466 options->auto_center = str2bool(value);
467 }
468 /* color assignment */
469 else if( !strcasecmp(CONF_COLOR, name) )
470 {
471 parse_color(value);
472 }
473 /* wide cursor */
474 else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
475 {
476 options->wide_cursor = str2bool(value);
477 }
478 /* timer display type */
479 else if( !strcasecmp(CONF_TIMEDISPLAY_TYPE, name) )
480 {
481 g_free(options->timedisplay_type);
482 options->timedisplay_type=g_strdup(parse_timedisplay_type(value));
483 D("deb");
484 D(options->timedisplay_type);
485 }
486 /* color definition */
487 else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
488 {
489 parse_color_definition(value);
490 }
491 /* list format string */
492 else if( !strcasecmp(CONF_LIST_FORMAT, name) )
493 {
494 g_free(options->list_format);
495 options->list_format = get_format(value);
496 }
497 /* status format string */
498 else if( !strcasecmp(CONF_STATUS_FORMAT, name) )
499 {
500 g_free(options->status_format);
501 options->status_format = get_format(value);
502 }
503 /* xterm title format string */
504 else if( !strcasecmp(CONF_XTERM_TITLE_FORMAT, name) )
505 {
506 g_free(options->xterm_title_format);
507 options->xterm_title_format = get_format(value);
508 }
509 else if( !strcasecmp(CONF_LIST_WRAP, name) )
510 {
511 options->list_wrap = str2bool(value);
512 }
513 else if( !strcasecmp(CONF_FIND_WRAP, name) )
514 {
515 options->find_wrap = str2bool(value);
516 }
517 else if( !strcasecmp(CONF_FIND_SHOW_LAST,name) )
518 {
519 options->find_show_last_pattern = str2bool(value);
520 }
521 else if( !strcasecmp(CONF_AUDIBLE_BELL, name) )
522 {
523 options->audible_bell = str2bool(value);
524 }
525 else if( !strcasecmp(CONF_VISIBLE_BELL, name) )
526 {
527 options->visible_bell = str2bool(value);
528 }
529 else if( !strcasecmp(CONF_XTERM_TITLE, name) )
530 {
531 options->enable_xterm_title = str2bool(value);
532 }
533 else if( !strcasecmp(CONF_ENABLE_MOUSE, name) )
534 {
535 options->enable_mouse = str2bool(value);
536 }
537 else if( !strcasecmp(CONF_CROSSFADE_TIME, name) )
538 {
539 options->crossfade_time = atoi(value);
540 }
541 else if( !strcasecmp(CONF_SEARCH_MODE, name) )
542 {
543 options->search_mode = atoi(value);
544 }
545 else if( !strcasecmp(CONF_HIDE_CURSOR, name) )
546 {
547 options->hide_cursor = atoi(value);
548 }
549 else if( !strcasecmp(CONF_SEEK_TIME, name) )
550 {
551 options->seek_time = atoi(value);
552 }
553 else if( !strcasecmp(CONF_SCREEN_LIST, name) )
554 {
555 g_strfreev(options->screen_list);
556 options->screen_list = check_screen_list(value);
557 }
558 else if( !strcasecmp(CONF_SHOW_SPLASH, name) )
559 {
560 options->show_splash = str2bool(value);
562 #ifdef DEBUG
563 D("screen-list:");
564 j=0;
565 while(options->screen_list[j])
566 D(" %s", options->screen_list[j++]);
567 D("\n");
568 #endif
569 }
570 else if( !strcasecmp(CONF_HOST, name))
571 {
572 options->host = get_format(value);
573 }
574 else if( !strcasecmp(CONF_PORT, name))
575 {
576 options->port = atoi(get_format(value));
577 }
578 else if( !strcasecmp(CONF_LYRICS_TIMEOUT, name))
579 {
580 options->lyrics_timeout = atoi(get_format(value));
581 }
582 else
583 {
584 match_found = 0;
585 }
587 if( !match_found )
588 fprintf(stderr,
589 _("Unknown configuration parameter: %s\n"),
590 name);
591 D("conf> %s = %s %s\n", name, value,
592 match_found ? "" : "- UNKNOWN SETTING!" );
593 }
594 }
595 }
597 D("--\n\n");
599 if( free_filename )
600 g_free(filename);
602 return 0;
603 }
605 int
606 check_user_conf_dir(void)
607 {
608 int retval;
609 char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
611 if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
612 {
613 g_free(dirname);
614 return 0;
615 }
616 retval = mkdir(dirname, 0755);
617 g_free(dirname);
618 return retval;
619 }
621 char *
622 get_user_key_binding_filename(void)
623 {
624 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
625 }
628 int
629 read_configuration(options_t *options)
630 {
631 char *filename = NULL;
633 /* check for command line configuration file */
634 if( options->config_file )
635 filename = g_strdup(options->config_file);
637 /* check for user configuration ~/.ncmpc/config */
638 if( filename == NULL )
639 {
640 filename = g_build_filename(g_get_home_dir(),
641 "." PACKAGE, "config", NULL);
642 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
643 {
644 g_free(filename);
645 filename = NULL;
646 }
647 }
649 /* check for global configuration SYSCONFDIR/ncmpc/config */
650 if( filename == NULL )
651 {
652 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
653 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
654 {
655 g_free(filename);
656 filename = NULL;
657 }
658 }
660 /* load configuration */
661 if( filename )
662 {
663 read_rc_file(filename, options);
664 g_free(filename);
665 filename = NULL;
666 }
668 /* check for command line key binding file */
669 if( options->key_file )
670 filename = g_strdup(options->key_file);
672 /* check for user key bindings ~/.ncmpc/keys */
673 if( filename == NULL )
674 {
675 filename = get_user_key_binding_filename();
676 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
677 {
678 g_free(filename);
679 filename = NULL;
680 }
681 }
683 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
684 if( filename == NULL )
685 {
686 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
687 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
688 {
689 g_free(filename);
690 filename = NULL;
691 }
692 }
694 /* load key bindings */
695 if( filename )
696 {
697 read_rc_file(filename, options);
698 g_free(filename);
699 filename = NULL;
700 }
702 return 0;
703 }