343a8a4c547084c7cee4a057cfc6a1e1e4850092
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_LIST_WRAP "wrap-around"
57 #define CONF_FIND_WRAP "find-wrap"
58 #define CONF_AUDIBLE_BELL "audible-bell"
59 #define CONF_VISIBLE_BELL "visible-bell"
60 #define CONF_XTERM_TITLE "xterm-title"
62 /* Deprecated - configuration field names */
63 #define OLD_CONF_ENABLE_COLORS "enable_colors"
64 #define OLD_CONF_AUTO_CENTER "auto_center"
65 #define OLD_CONF_WIDE_CURSOR "wide_cursor"
66 #define CONF_COLOR_BACKGROUND "background_color"
67 #define CONF_COLOR_TITLE "title_color"
68 #define CONF_COLOR_LINE "line_color"
69 #define CONF_COLOR_LIST "list_color"
70 #define CONF_COLOR_PROGRESS "progress_color"
71 #define CONF_COLOR_STATUS "status_color"
72 #define CONF_COLOR_ALERT "alert_color"
75 typedef enum {
76 KEY_PARSER_UNKNOWN,
77 KEY_PARSER_CHAR,
78 KEY_PARSER_DEC,
79 KEY_PARSER_HEX,
80 KEY_PARSER_DONE
81 } key_parser_state_t;
83 static gboolean
84 str2bool(char *str)
85 {
86 if( !strcasecmp(str,"yes") || !strcasecmp(str,"true") ||
87 !strcasecmp(str,"on") || !strcasecmp(str,"1") )
88 return TRUE;
89 return FALSE;
90 }
92 static int
93 parse_key_value(char *str, size_t len, char **end)
94 {
95 int i, value;
96 key_parser_state_t state;
98 i=0;
99 value=0;
100 state=KEY_PARSER_UNKNOWN;
101 *end = str;
103 while( i<len && state!=KEY_PARSER_DONE )
104 {
105 int next = 0;
106 int c = str[i];
108 if( i+1<len )
109 next = str[i+1];
111 switch(state)
112 {
113 case KEY_PARSER_UNKNOWN:
114 if( c=='\'' )
115 state = KEY_PARSER_CHAR;
116 else if( c=='0' && next=='x' )
117 state = KEY_PARSER_HEX;
118 else if( isdigit(c) )
119 state = KEY_PARSER_DEC;
120 else {
121 fprintf(stderr,
122 _("Error: Unsupported key definition - %s\n"),
123 str);
124 return -1;
125 }
126 break;
127 case KEY_PARSER_CHAR:
128 if( next!='\'' )
129 {
130 fprintf(stderr,
131 _("Error: Unsupported key definition - %s\n"),
132 str);
133 return -1;
134 }
135 value = c;
136 *end = str+i+2;
137 state = KEY_PARSER_DONE;
138 break;
139 case KEY_PARSER_DEC:
140 value = (int) strtol(str+(i-1), end, 10);
141 state = KEY_PARSER_DONE;
142 break;
143 case KEY_PARSER_HEX:
144 if( !isdigit(next) )
145 {
146 fprintf(stderr,_("Error: Digit expexted after 0x - %s\n"), str);
147 return -1;
148 }
149 value = (int) strtol(str+(i+1), end, 16);
150 state = KEY_PARSER_DONE;
151 break;
152 case KEY_PARSER_DONE:
153 break;
154 }
155 i++;
156 }
158 if( *end> str+len )
159 *end = str+len;
161 return value;
162 }
164 static int
165 parse_key_definition(char *str)
166 {
167 char buf[MAX_LINE_LENGTH];
168 char *p, *end;
169 size_t len = strlen(str);
170 int i,j,key;
171 int keys[MAX_COMMAND_KEYS];
172 command_t cmd;
174 /* get the command name */
175 i=0;
176 j=0;
177 memset(buf, 0, MAX_LINE_LENGTH);
178 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
179 buf[j++] = str[i++];
180 if( (cmd=get_key_command_from_name(buf)) == CMD_NONE )
181 {
182 fprintf(stderr, _("Error: Unknown key command %s\n"), buf);
183 return -1;
184 }
186 /* skip whitespace */
187 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
188 i++;
190 /* get the value part */
191 memset(buf, 0, MAX_LINE_LENGTH);
192 strncpy(buf, str+i, len-i);
193 len = strlen(buf);
194 if( len==0 )
195 {
196 fprintf(stderr,_("Error: Incomplete key definition - %s\n"), str);
197 return -1;
198 }
200 /* parse key values */
201 i = 0;
202 key = 0;
203 len = strlen(buf);
204 p = buf;
205 end = buf+len;
206 memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
207 while( i<MAX_COMMAND_KEYS && p<end && (key=parse_key_value(p,len+1,&p))>=0 )
208 {
209 keys[i++] = key;
210 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
211 p++;
212 len = strlen(p);
213 }
214 if( key<0 )
215 {
216 fprintf(stderr,_("Error: Bad key definition - %s\n"), str);
217 return -1;
218 }
220 return assign_keys(cmd, keys);
221 }
223 static int
224 parse_color(char *str)
225 {
226 char *name = str;
227 char *value = NULL;
228 int len,i;
230 i=0;
231 len=strlen(str);
232 /* get the color name */
233 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
234 i++;
236 /* skip whitespace */
237 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
238 {
239 str[i]='\0';
240 i++;
241 }
243 if( i<len )
244 value = str+i;
246 return colors_assign(name, value);
247 }
250 static int
251 parse_color_definition(char *str)
252 {
253 char buf[MAX_LINE_LENGTH];
254 char *p, *end, *name;
255 size_t len = strlen(str);
256 int i,j,value;
257 short color, rgb[3];
259 /* get the command name */
260 i=0;
261 j=0;
262 memset(buf, 0, MAX_LINE_LENGTH);
263 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
264 buf[j++] = str[i++];
265 color=colors_str2color(buf);
266 if( color<0 )
267 {
268 fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
269 return -1;
270 }
271 name = g_strdup(buf);
273 /* skip whitespace */
274 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
275 i++;
277 /* get the value part */
278 memset(buf, 0, MAX_LINE_LENGTH);
279 strncpy(buf, str+i, len-i);
280 len = strlen(buf);
281 if( len==0 )
282 {
283 fprintf(stderr,_("Error: Incomplete color definition - %s\n"), str);
284 g_free(name);
285 return -1;
286 }
288 /* parse r,g.b values with the key definition parser */
289 i = 0;
290 value = 0;
291 len = strlen(buf);
292 p = buf;
293 end = buf+len;
294 memset(rgb, 0, sizeof(short)*3);
295 while( i<3 && p<end && (value=parse_key_value(p,len+1,&p))>=0 )
296 {
297 rgb[i++] = value;
298 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
299 p++;
300 len = strlen(p);
301 }
302 if( value<0 || i!=3)
303 {
304 fprintf(stderr,_("Error: Bad color definition - %s\n"), str);
305 g_free(name);
306 return -1;
307 }
308 value = colors_define(name, rgb[0], rgb[1], rgb[2]);
309 g_free(name);
310 return value;
311 }
313 static char *
314 get_format(char *str)
315 {
316 gsize len = strlen(str);
318 if( str && str[0]=='\"' && str[len-1] == '\"' )
319 {
320 str[len-1] = '\0';
321 str++;
322 }
323 return g_strdup(str);
324 }
327 static int
328 read_rc_file(char *filename, options_t *options)
329 {
330 int fd;
331 int quit = 0;
332 int free_filename = 0;
334 if( filename==NULL )
335 return -1;
337 D("Reading configuration file %s\n", filename);
338 if( (fd=open(filename,O_RDONLY)) <0 )
339 {
340 perror(filename);
341 if( free_filename )
342 g_free(filename);
343 return -1;
344 }
346 while( !quit )
347 {
348 int i,j;
349 int len;
350 int match_found;
351 char line[MAX_LINE_LENGTH];
352 char name[MAX_LINE_LENGTH];
353 char value[MAX_LINE_LENGTH];
355 line[0] = '\0';
356 value[0] = '\0';
358 i = 0;
359 /* read a line ending with '\n' */
360 do {
361 len = read( fd, &line[i], 1 );
362 if( len == 1 )
363 i++;
364 else
365 quit = 1;
366 } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
369 /* remove trailing whitespace */
370 line[i] = '\0';
371 i--;
372 while( i>=0 && IS_WHITESPACE(line[i]) )
373 {
374 line[i] = '\0';
375 i--;
376 }
377 len = i+1;
379 if( len>0 )
380 {
381 i = 0;
382 /* skip whitespace */
383 while( i<len && IS_WHITESPACE(line[i]) )
384 i++;
386 /* continue if this line is not a comment */
387 if( line[i] != COMMENT_TOKEN )
388 {
389 /* get the name part */
390 j=0;
391 while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
392 {
393 name[j++] = line[i++];
394 }
395 name[j] = '\0';
397 /* skip '=' and whitespace */
398 while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
399 i++;
401 /* get the value part */
402 j=0;
403 while( i<len )
404 {
405 value[j++] = line[i++];
406 }
407 value[j] = '\0';
409 match_found = 1;
411 /* key definition */
412 if( !strcasecmp(CONF_KEY_DEFINITION, name) )
413 {
414 parse_key_definition(value);
415 }
416 /* enable colors */
417 else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
418 {
419 options->enable_colors = str2bool(value);
420 }
421 /* auto center */
422 else if( !strcasecmp(CONF_AUTO_CENTER, name) )
423 {
424 options->auto_center = str2bool(value);
425 }
426 /* color assignment */
427 else if( !strcasecmp(CONF_COLOR, name) )
428 {
429 parse_color(value);
430 }
431 #ifdef ENABLE_OLD_SYNTAX
432 /* background color */
433 else if( !strcasecmp(CONF_COLOR_BACKGROUND, name) )
434 {
435 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
436 colors_assign("background", value);
437 }
438 /* color - top (title) window */
439 else if( !strcasecmp(CONF_COLOR_TITLE, name) )
440 {
441 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
442 colors_assign("title", value);
443 colors_assign("title2", value);
444 }
445 /* color - line (title) window */
446 else if( !strcasecmp(CONF_COLOR_LINE, name) )
447 {
448 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
449 colors_assign("line", value);
450 colors_assign("line2", value);
451 }
452 /* color - list window */
453 else if( !strcasecmp(CONF_COLOR_LIST, name) )
454 {
455 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
456 colors_assign("list", value);
457 }
458 /* color - progress bar */
459 else if( !strcasecmp(CONF_COLOR_PROGRESS, name) )
460 {
461 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
462 colors_assign("progressbar", value);
463 }
464 /* color - status window */
465 else if( !strcasecmp(CONF_COLOR_STATUS, name) )
466 {
467 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
468 colors_assign("status", value);
469 colors_assign("status2", value);
470 }
471 /* color - alerts */
472 else if( !strcasecmp(CONF_COLOR_ALERT, name) )
473 {
474 fprintf(stderr,"%s: %s deprecated!\n", filename,name);
475 colors_assign("alert", value);
476 }
477 /* enable colors */
478 else if( !strcasecmp(OLD_CONF_ENABLE_COLORS, name) )
479 {
480 fprintf(stderr,"%s: %s deprecated - use %s!\n", filename, name, CONF_ENABLE_COLORS);
481 options->enable_colors = str2bool(value);
482 }
483 /* auto center */
484 else if( !strcasecmp(OLD_CONF_AUTO_CENTER, name) )
485 {
486 fprintf(stderr,"%s: %s deprecated - use %s!\n", filename, name, CONF_AUTO_CENTER);
487 options->auto_center = str2bool(value);
488 }
489 /* wide cursor */
490 else if( !strcasecmp(OLD_CONF_WIDE_CURSOR, name) )
491 {
492 fprintf(stderr,"%s: %s deprecated - use %s!\n", filename, name, CONF_WIDE_CURSOR);
493 options->wide_cursor = str2bool(value);
494 }
495 #endif
496 /* wide cursor */
497 else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
498 {
499 options->wide_cursor = str2bool(value);
500 }
501 /* color definition */
502 else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
503 {
504 parse_color_definition(value);
505 }
506 /* list format string */
507 else if( !strcasecmp(CONF_LIST_FORMAT, name) )
508 {
509 g_free(options->list_format);
510 options->list_format = get_format(value);
511 }
512 /* status format string */
513 else if( !strcasecmp(CONF_STATUS_FORMAT, name) )
514 {
515 g_free(options->status_format);
516 options->status_format = get_format(value);
517 }
518 else if( !strcasecmp(CONF_LIST_WRAP, name) )
519 {
520 options->list_wrap = str2bool(value);
521 }
522 else if( !strcasecmp(CONF_FIND_WRAP, name) )
523 {
524 options->find_wrap = str2bool(value);
525 }
526 else if( !strcasecmp(CONF_AUDIBLE_BELL, name) )
527 {
528 options->audible_bell = str2bool(value);
529 }
530 else if( !strcasecmp(CONF_VISIBLE_BELL, name) )
531 {
532 options->visible_bell = str2bool(value);
533 }
534 else if( !strcasecmp(CONF_XTERM_TITLE, name) )
535 {
536 options->enable_xterm_title = str2bool(value);
537 }
538 else
539 {
540 match_found = 0;
541 }
544 if( !match_found )
545 fprintf(stderr,
546 _("Unknown configuration parameter: %s\n"),
547 name);
548 #ifdef DEBUG
549 printf( " %s = %s %s\n",
550 name,
551 value,
552 match_found ? "" : "- UNKNOWN SETTING!" );
553 #endif
555 }
556 }
557 }
559 D("--\n\n");
561 if( free_filename )
562 g_free(filename);
564 return 0;
565 }
567 int
568 check_user_conf_dir(void)
569 {
570 int retval;
571 char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
573 if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
574 {
575 g_free(dirname);
576 return 0;
577 }
578 retval = mkdir(dirname, 0755);
579 g_free(dirname);
580 return retval;
581 }
583 char *
584 get_user_key_binding_filename(void)
585 {
586 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
587 }
590 int
591 read_configuration(options_t *options)
592 {
593 char *filename = NULL;
595 /* check for command line configuration file */
596 if( options->config_file )
597 filename = g_strdup(options->config_file);
599 /* check for user configuration ~/.ncmpc/config */
600 if( filename == NULL )
601 {
602 filename = g_build_filename(g_get_home_dir(),
603 "." PACKAGE, "config", NULL);
604 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
605 {
606 g_free(filename);
607 filename = NULL;
608 }
609 }
611 /* check for global configuration SYSCONFDIR/ncmpc/config */
612 if( filename == NULL )
613 {
614 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
615 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
616 {
617 g_free(filename);
618 filename = NULL;
619 }
620 }
622 /* load configuration */
623 if( filename )
624 {
625 read_rc_file(filename, options);
626 g_free(filename);
627 filename = NULL;
628 }
630 /* check for command line key binding file */
631 if( options->key_file )
632 filename = g_strdup(options->key_file);
634 /* check for user key bindings ~/.ncmpc/keys */
635 if( filename == NULL )
636 {
637 filename = get_user_key_binding_filename();
638 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
639 {
640 g_free(filename);
641 filename = NULL;
642 }
643 }
645 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
646 if( filename == NULL )
647 {
648 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
649 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
650 {
651 g_free(filename);
652 filename = NULL;
653 }
654 }
656 /* load key bindings */
657 if( filename )
658 {
659 read_rc_file(filename, options);
660 g_free(filename);
661 filename = NULL;
662 }
664 return 0;
665 }