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