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 ENABLE_OLD_SYNTAX
43 #define MAX_LINE_LENGTH 1024
44 #define COMMENT_TOKEN '#'
46 /* configuration field names */
47 #define CONF_ENABLE_COLORS "enable-colors"
48 #define CONF_AUTO_CENTER "auto-center"
49 #define CONF_WIDE_CURSOR "wide-cursor"
50 #define CONF_ENABLE_BELL "enable-bell"
51 #define CONF_KEY_DEFINITION "key"
52 #define CONF_COLOR "color"
53 #define CONF_COLOR_DEFINITION "colordef"
54 #define CONF_LIST_FORMAT "list-format"
55 #define CONF_STATUS_FORMAT "status-format"
56 #define CONF_XTERM_TITLE_FORMAT "xterm-title-format"
57 #define CONF_LIST_WRAP "wrap-around"
58 #define CONF_FIND_WRAP "find-wrap"
59 #define CONF_AUDIBLE_BELL "audible-bell"
60 #define CONF_VISIBLE_BELL "visible-bell"
61 #define CONF_XTERM_TITLE "set-xterm-title"
64 /* Deprecated - configuration field names */
65 #define OLD_CONF_ENABLE_COLORS "enable_colors"
66 #define OLD_CONF_AUTO_CENTER "auto_center"
67 #define OLD_CONF_WIDE_CURSOR "wide_cursor"
68 #define CONF_COLOR_BACKGROUND "background_color"
69 #define CONF_COLOR_TITLE "title_color"
70 #define CONF_COLOR_LINE "line_color"
71 #define CONF_COLOR_LIST "list_color"
72 #define CONF_COLOR_PROGRESS "progress_color"
73 #define CONF_COLOR_STATUS "status_color"
74 #define CONF_COLOR_ALERT "alert_color"
77 typedef enum {
78 KEY_PARSER_UNKNOWN,
79 KEY_PARSER_CHAR,
80 KEY_PARSER_DEC,
81 KEY_PARSER_HEX,
82 KEY_PARSER_DONE
83 } key_parser_state_t;
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 expexted 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 strncpy(buf, str+i, len-i);
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 int
226 parse_color(char *str)
227 {
228 char *name = str;
229 char *value = NULL;
230 int len,i;
232 i=0;
233 len=strlen(str);
234 /* get the color name */
235 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
236 i++;
238 /* skip whitespace */
239 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
240 {
241 str[i]='\0';
242 i++;
243 }
245 if( i<len )
246 value = str+i;
248 return colors_assign(name, value);
249 }
252 static int
253 parse_color_definition(char *str)
254 {
255 char buf[MAX_LINE_LENGTH];
256 char *p, *end, *name;
257 size_t len = strlen(str);
258 int i,j,value;
259 short color, rgb[3];
261 /* get the command name */
262 i=0;
263 j=0;
264 memset(buf, 0, MAX_LINE_LENGTH);
265 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
266 buf[j++] = str[i++];
267 color=colors_str2color(buf);
268 if( color<0 )
269 {
270 fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
271 return -1;
272 }
273 name = g_strdup(buf);
275 /* skip whitespace */
276 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
277 i++;
279 /* get the value part */
280 memset(buf, 0, MAX_LINE_LENGTH);
281 strncpy(buf, str+i, len-i);
282 len = strlen(buf);
283 if( len==0 )
284 {
285 fprintf(stderr,_("Error: Incomplete color definition - %s\n"), str);
286 g_free(name);
287 return -1;
288 }
290 /* parse r,g.b values with the key definition parser */
291 i = 0;
292 value = 0;
293 len = strlen(buf);
294 p = buf;
295 end = buf+len;
296 memset(rgb, 0, sizeof(short)*3);
297 while( i<3 && p<end && (value=parse_key_value(p,len+1,&p))>=0 )
298 {
299 rgb[i++] = value;
300 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
301 p++;
302 len = strlen(p);
303 }
304 if( value<0 || i!=3)
305 {
306 fprintf(stderr,_("Error: Bad color definition - %s\n"), str);
307 g_free(name);
308 return -1;
309 }
310 value = colors_define(name, rgb[0], rgb[1], rgb[2]);
311 g_free(name);
312 return value;
313 }
315 static char *
316 get_format(char *str)
317 {
318 gsize len = strlen(str);
320 if( str && str[0]=='\"' && str[len-1] == '\"' )
321 {
322 str[len-1] = '\0';
323 str++;
324 }
325 return g_strdup(str);
326 }
329 static int
330 read_rc_file(char *filename, options_t *options)
331 {
332 int fd;
333 int quit = 0;
334 int free_filename = 0;
336 if( filename==NULL )
337 return -1;
339 D("Reading configuration file %s\n", filename);
340 if( (fd=open(filename,O_RDONLY)) <0 )
341 {
342 perror(filename);
343 if( free_filename )
344 g_free(filename);
345 return -1;
346 }
348 while( !quit )
349 {
350 int i,j;
351 int len;
352 int match_found;
353 char line[MAX_LINE_LENGTH];
354 char name[MAX_LINE_LENGTH];
355 char value[MAX_LINE_LENGTH];
357 line[0] = '\0';
358 value[0] = '\0';
360 i = 0;
361 /* read a line ending with '\n' */
362 do {
363 len = read( fd, &line[i], 1 );
364 if( len == 1 )
365 i++;
366 else
367 quit = 1;
368 } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
371 /* remove trailing whitespace */
372 line[i] = '\0';
373 i--;
374 while( i>=0 && IS_WHITESPACE(line[i]) )
375 {
376 line[i] = '\0';
377 i--;
378 }
379 len = i+1;
381 if( len>0 )
382 {
383 i = 0;
384 /* skip whitespace */
385 while( i<len && IS_WHITESPACE(line[i]) )
386 i++;
388 /* continue if this line is not a comment */
389 if( line[i] != COMMENT_TOKEN )
390 {
391 /* get the name part */
392 j=0;
393 while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
394 {
395 name[j++] = line[i++];
396 }
397 name[j] = '\0';
399 /* skip '=' and whitespace */
400 while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
401 i++;
403 /* get the value part */
404 j=0;
405 while( i<len )
406 {
407 value[j++] = line[i++];
408 }
409 value[j] = '\0';
411 match_found = 1;
413 /* key definition */
414 if( !strcasecmp(CONF_KEY_DEFINITION, name) )
415 {
416 parse_key_definition(value);
417 }
418 /* enable colors */
419 else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
420 {
421 options->enable_colors = str2bool(value);
422 }
423 /* auto center */
424 else if( !strcasecmp(CONF_AUTO_CENTER, name) )
425 {
426 options->auto_center = str2bool(value);
427 }
428 /* color assignment */
429 else if( !strcasecmp(CONF_COLOR, name) )
430 {
431 parse_color(value);
432 }
433 #ifdef ENABLE_OLD_SYNTAX
434 /* background color */
435 else if( !strcasecmp(CONF_COLOR_BACKGROUND, name) )
436 {
437 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
438 colors_assign("background", value);
439 }
440 /* color - top (title) window */
441 else if( !strcasecmp(CONF_COLOR_TITLE, name) )
442 {
443 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
444 colors_assign("title", value);
445 colors_assign("title2", value);
446 }
447 /* color - line (title) window */
448 else if( !strcasecmp(CONF_COLOR_LINE, name) )
449 {
450 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
451 colors_assign("line", value);
452 colors_assign("line2", value);
453 }
454 /* color - list window */
455 else if( !strcasecmp(CONF_COLOR_LIST, name) )
456 {
457 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
458 colors_assign("list", value);
459 }
460 /* color - progress bar */
461 else if( !strcasecmp(CONF_COLOR_PROGRESS, name) )
462 {
463 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
464 colors_assign("progressbar", value);
465 }
466 /* color - status window */
467 else if( !strcasecmp(CONF_COLOR_STATUS, name) )
468 {
469 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
470 colors_assign("status", value);
471 colors_assign("status2", value);
472 }
473 /* color - alerts */
474 else if( !strcasecmp(CONF_COLOR_ALERT, name) )
475 {
476 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
477 colors_assign("alert", value);
478 }
479 /* enable colors */
480 else if( !strcasecmp(OLD_CONF_ENABLE_COLORS, name) )
481 {
482 fprintf(stderr,"%s: %s deprecated - use %s!\n", filename, name, CONF_ENABLE_COLORS);
483 options->enable_colors = str2bool(value);
484 }
485 /* auto center */
486 else if( !strcasecmp(OLD_CONF_AUTO_CENTER, name) )
487 {
488 fprintf(stderr,"%s: %s deprecated - use %s!\n", filename, name, CONF_AUTO_CENTER);
489 options->auto_center = str2bool(value);
490 }
491 /* wide cursor */
492 else if( !strcasecmp(OLD_CONF_WIDE_CURSOR, name) )
493 {
494 fprintf(stderr,"%s: %s deprecated - use %s!\n", filename, name, CONF_WIDE_CURSOR);
495 options->wide_cursor = str2bool(value);
496 }
497 #endif
498 /* wide cursor */
499 else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
500 {
501 options->wide_cursor = str2bool(value);
502 }
503 /* color definition */
504 else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
505 {
506 parse_color_definition(value);
507 }
508 /* list format string */
509 else if( !strcasecmp(CONF_LIST_FORMAT, name) )
510 {
511 g_free(options->list_format);
512 options->list_format = get_format(value);
513 }
514 /* status format string */
515 else if( !strcasecmp(CONF_STATUS_FORMAT, name) )
516 {
517 g_free(options->status_format);
518 options->status_format = get_format(value);
519 }
520 /* xterm title format string */
521 else if( !strcasecmp(CONF_XTERM_TITLE_FORMAT, name) )
522 {
523 g_free(options->xterm_title_format);
524 options->xterm_title_format = get_format(value);
525 }
526 else if( !strcasecmp(CONF_LIST_WRAP, name) )
527 {
528 options->list_wrap = str2bool(value);
529 }
530 else if( !strcasecmp(CONF_FIND_WRAP, name) )
531 {
532 options->find_wrap = str2bool(value);
533 }
534 else if( !strcasecmp(CONF_AUDIBLE_BELL, name) )
535 {
536 options->audible_bell = str2bool(value);
537 }
538 else if( !strcasecmp(CONF_VISIBLE_BELL, name) )
539 {
540 options->visible_bell = str2bool(value);
541 }
542 else if( !strcasecmp(CONF_XTERM_TITLE, name) )
543 {
544 options->enable_xterm_title = str2bool(value);
545 }
546 else
547 {
548 match_found = 0;
549 }
552 if( !match_found )
553 fprintf(stderr,
554 _("Unknown configuration parameter: %s\n"),
555 name);
556 #ifdef DEBUG
557 printf( " %s = %s %s\n",
558 name,
559 value,
560 match_found ? "" : "- UNKNOWN SETTING!" );
561 #endif
563 }
564 }
565 }
567 D("--\n\n");
569 if( free_filename )
570 g_free(filename);
572 return 0;
573 }
575 int
576 check_user_conf_dir(void)
577 {
578 int retval;
579 char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
581 if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
582 {
583 g_free(dirname);
584 return 0;
585 }
586 retval = mkdir(dirname, 0755);
587 g_free(dirname);
588 return retval;
589 }
591 char *
592 get_user_key_binding_filename(void)
593 {
594 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
595 }
598 int
599 read_configuration(options_t *options)
600 {
601 char *filename = NULL;
603 /* check for command line configuration file */
604 if( options->config_file )
605 filename = g_strdup(options->config_file);
607 /* check for user configuration ~/.ncmpc/config */
608 if( filename == NULL )
609 {
610 filename = g_build_filename(g_get_home_dir(),
611 "." PACKAGE, "config", NULL);
612 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
613 {
614 g_free(filename);
615 filename = NULL;
616 }
617 }
619 /* check for global configuration SYSCONFDIR/ncmpc/config */
620 if( filename == NULL )
621 {
622 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
623 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
624 {
625 g_free(filename);
626 filename = NULL;
627 }
628 }
630 /* load configuration */
631 if( filename )
632 {
633 read_rc_file(filename, options);
634 g_free(filename);
635 filename = NULL;
636 }
638 /* check for command line key binding file */
639 if( options->key_file )
640 filename = g_strdup(options->key_file);
642 /* check for user key bindings ~/.ncmpc/keys */
643 if( filename == NULL )
644 {
645 filename = get_user_key_binding_filename();
646 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
647 {
648 g_free(filename);
649 filename = NULL;
650 }
651 }
653 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
654 if( filename == NULL )
655 {
656 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
657 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
658 {
659 g_free(filename);
660 filename = NULL;
661 }
662 }
664 /* load key bindings */
665 if( filename )
666 {
667 read_rc_file(filename, options);
668 g_free(filename);
669 filename = NULL;
670 }
672 return 0;
673 }