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_AUDIBLE_BELL "audible-bell"
58 #define CONF_VISIBLE_BELL "visible-bell"
59 #define CONF_XTERM_TITLE "set-xterm-title"
60 #define CONF_ENABLE_MOUSE "enable-mouse"
61 #define CONF_CROSSFADE_TIME "crossfade-time"
62 #define CONF_SEARCH_MODE "search-mode"
63 #define CONF_HIDE_CURSOR "hide-cursor"
64 #define CONF_SEEK_TIME "seek-time"
66 typedef enum {
67 KEY_PARSER_UNKNOWN,
68 KEY_PARSER_CHAR,
69 KEY_PARSER_DEC,
70 KEY_PARSER_HEX,
71 KEY_PARSER_DONE
72 } key_parser_state_t;
74 static gboolean
75 str2bool(char *str)
76 {
77 if( !strcasecmp(str,"yes") || !strcasecmp(str,"true") ||
78 !strcasecmp(str,"on") || !strcasecmp(str,"1") )
79 return TRUE;
80 return FALSE;
81 }
83 static int
84 parse_key_value(char *str, size_t len, char **end)
85 {
86 int i, value;
87 key_parser_state_t state;
89 i=0;
90 value=0;
91 state=KEY_PARSER_UNKNOWN;
92 *end = str;
94 while( i<len && state!=KEY_PARSER_DONE )
95 {
96 int next = 0;
97 int c = str[i];
99 if( i+1<len )
100 next = str[i+1];
102 switch(state)
103 {
104 case KEY_PARSER_UNKNOWN:
105 if( c=='\'' )
106 state = KEY_PARSER_CHAR;
107 else if( c=='0' && next=='x' )
108 state = KEY_PARSER_HEX;
109 else if( isdigit(c) )
110 state = KEY_PARSER_DEC;
111 else {
112 fprintf(stderr,
113 _("Error: Unsupported key definition - %s\n"),
114 str);
115 return -1;
116 }
117 break;
118 case KEY_PARSER_CHAR:
119 if( next!='\'' )
120 {
121 fprintf(stderr,
122 _("Error: Unsupported key definition - %s\n"),
123 str);
124 return -1;
125 }
126 value = c;
127 *end = str+i+2;
128 state = KEY_PARSER_DONE;
129 break;
130 case KEY_PARSER_DEC:
131 value = (int) strtol(str+(i-1), end, 10);
132 state = KEY_PARSER_DONE;
133 break;
134 case KEY_PARSER_HEX:
135 if( !isdigit(next) )
136 {
137 fprintf(stderr,_("Error: Digit expexted after 0x - %s\n"), str);
138 return -1;
139 }
140 value = (int) strtol(str+(i+1), end, 16);
141 state = KEY_PARSER_DONE;
142 break;
143 case KEY_PARSER_DONE:
144 break;
145 }
146 i++;
147 }
149 if( *end> str+len )
150 *end = str+len;
152 return value;
153 }
155 static int
156 parse_key_definition(char *str)
157 {
158 char buf[MAX_LINE_LENGTH];
159 char *p, *end;
160 size_t len = strlen(str);
161 int i,j,key;
162 int keys[MAX_COMMAND_KEYS];
163 command_t cmd;
165 /* get the command name */
166 i=0;
167 j=0;
168 memset(buf, 0, MAX_LINE_LENGTH);
169 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
170 buf[j++] = str[i++];
171 if( (cmd=get_key_command_from_name(buf)) == CMD_NONE )
172 {
173 fprintf(stderr, _("Error: Unknown key command %s\n"), buf);
174 return -1;
175 }
177 /* skip whitespace */
178 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
179 i++;
181 /* get the value part */
182 memset(buf, 0, MAX_LINE_LENGTH);
183 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
184 len = strlen(buf);
185 if( len==0 )
186 {
187 fprintf(stderr,_("Error: Incomplete key definition - %s\n"), str);
188 return -1;
189 }
191 /* parse key values */
192 i = 0;
193 key = 0;
194 len = strlen(buf);
195 p = buf;
196 end = buf+len;
197 memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
198 while( i<MAX_COMMAND_KEYS && p<end && (key=parse_key_value(p,len+1,&p))>=0 )
199 {
200 keys[i++] = key;
201 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
202 p++;
203 len = strlen(p);
204 }
205 if( key<0 )
206 {
207 fprintf(stderr,_("Error: Bad key definition - %s\n"), str);
208 return -1;
209 }
211 return assign_keys(cmd, keys);
212 }
214 static int
215 parse_color(char *str)
216 {
217 char *name = str;
218 char *value = NULL;
219 int len,i;
221 i=0;
222 len=strlen(str);
223 /* get the color name */
224 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
225 i++;
227 /* skip whitespace */
228 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
229 {
230 str[i]='\0';
231 i++;
232 }
234 if( i<len )
235 value = str+i;
237 return colors_assign(name, value);
238 }
241 static int
242 parse_color_definition(char *str)
243 {
244 char buf[MAX_LINE_LENGTH];
245 char *p, *end, *name;
246 size_t len = strlen(str);
247 int i,j,value;
248 short color, rgb[3];
250 /* get the command name */
251 i=0;
252 j=0;
253 memset(buf, 0, MAX_LINE_LENGTH);
254 while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
255 buf[j++] = str[i++];
256 color=colors_str2color(buf);
257 if( color<0 )
258 {
259 fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
260 return -1;
261 }
262 name = g_strdup(buf);
264 /* skip whitespace */
265 while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
266 i++;
268 /* get the value part */
269 memset(buf, 0, MAX_LINE_LENGTH);
270 g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
271 len = strlen(buf);
272 if( len==0 )
273 {
274 fprintf(stderr,_("Error: Incomplete color definition - %s\n"), str);
275 g_free(name);
276 return -1;
277 }
279 /* parse r,g.b values with the key definition parser */
280 i = 0;
281 value = 0;
282 len = strlen(buf);
283 p = buf;
284 end = buf+len;
285 memset(rgb, 0, sizeof(short)*3);
286 while( i<3 && p<end && (value=parse_key_value(p,len+1,&p))>=0 )
287 {
288 rgb[i++] = value;
289 while( p<end && (*p==',' || *p==' ' || *p=='\t') )
290 p++;
291 len = strlen(p);
292 }
293 if( value<0 || i!=3)
294 {
295 fprintf(stderr,_("Error: Bad color definition - %s\n"), str);
296 g_free(name);
297 return -1;
298 }
299 value = colors_define(name, rgb[0], rgb[1], rgb[2]);
300 g_free(name);
301 return value;
302 }
304 static char *
305 get_format(char *str)
306 {
307 gsize len = strlen(str);
309 if( str && str[0]=='\"' && str[len-1] == '\"' )
310 {
311 str[len-1] = '\0';
312 str++;
313 }
314 return g_strdup(str);
315 }
318 static int
319 read_rc_file(char *filename, options_t *options)
320 {
321 int fd;
322 int quit = 0;
323 int free_filename = 0;
325 if( filename==NULL )
326 return -1;
328 D("Reading configuration file %s\n", filename);
329 if( (fd=open(filename,O_RDONLY)) <0 )
330 {
331 perror(filename);
332 if( free_filename )
333 g_free(filename);
334 return -1;
335 }
337 while( !quit )
338 {
339 int i,j;
340 int len;
341 int match_found;
342 char line[MAX_LINE_LENGTH];
343 char name[MAX_LINE_LENGTH];
344 char value[MAX_LINE_LENGTH];
346 line[0] = '\0';
347 value[0] = '\0';
349 i = 0;
350 /* read a line ending with '\n' */
351 do {
352 len = read( fd, &line[i], 1 );
353 if( len == 1 )
354 i++;
355 else
356 quit = 1;
357 } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
360 /* remove trailing whitespace */
361 line[i] = '\0';
362 i--;
363 while( i>=0 && IS_WHITESPACE(line[i]) )
364 {
365 line[i] = '\0';
366 i--;
367 }
368 len = i+1;
370 if( len>0 )
371 {
372 i = 0;
373 /* skip whitespace */
374 while( i<len && IS_WHITESPACE(line[i]) )
375 i++;
377 /* continue if this line is not a comment */
378 if( line[i] != COMMENT_TOKEN )
379 {
380 /* get the name part */
381 j=0;
382 while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
383 {
384 name[j++] = line[i++];
385 }
386 name[j] = '\0';
388 /* skip '=' and whitespace */
389 while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
390 i++;
392 /* get the value part */
393 j=0;
394 while( i<len )
395 {
396 value[j++] = line[i++];
397 }
398 value[j] = '\0';
400 match_found = 1;
402 /* key definition */
403 if( !strcasecmp(CONF_KEY_DEFINITION, name) )
404 {
405 parse_key_definition(value);
406 }
407 /* enable colors */
408 else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
409 {
410 options->enable_colors = str2bool(value);
411 }
412 /* auto center */
413 else if( !strcasecmp(CONF_AUTO_CENTER, name) )
414 {
415 options->auto_center = str2bool(value);
416 }
417 /* color assignment */
418 else if( !strcasecmp(CONF_COLOR, name) )
419 {
420 parse_color(value);
421 }
422 /* wide cursor */
423 else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
424 {
425 options->wide_cursor = str2bool(value);
426 }
427 /* color definition */
428 else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
429 {
430 parse_color_definition(value);
431 }
432 /* list format string */
433 else if( !strcasecmp(CONF_LIST_FORMAT, name) )
434 {
435 g_free(options->list_format);
436 options->list_format = get_format(value);
437 }
438 /* status format string */
439 else if( !strcasecmp(CONF_STATUS_FORMAT, name) )
440 {
441 g_free(options->status_format);
442 options->status_format = get_format(value);
443 }
444 /* xterm title format string */
445 else if( !strcasecmp(CONF_XTERM_TITLE_FORMAT, name) )
446 {
447 g_free(options->xterm_title_format);
448 options->xterm_title_format = get_format(value);
449 }
450 else if( !strcasecmp(CONF_LIST_WRAP, name) )
451 {
452 options->list_wrap = str2bool(value);
453 }
454 else if( !strcasecmp(CONF_FIND_WRAP, name) )
455 {
456 options->find_wrap = str2bool(value);
457 }
458 else if( !strcasecmp(CONF_AUDIBLE_BELL, name) )
459 {
460 options->audible_bell = str2bool(value);
461 }
462 else if( !strcasecmp(CONF_VISIBLE_BELL, name) )
463 {
464 options->visible_bell = str2bool(value);
465 }
466 else if( !strcasecmp(CONF_XTERM_TITLE, name) )
467 {
468 options->enable_xterm_title = str2bool(value);
469 }
470 else if( !strcasecmp(CONF_ENABLE_MOUSE, name) )
471 {
472 options->enable_mouse = str2bool(value);
473 }
474 else if( !strcasecmp(CONF_CROSSFADE_TIME, name) )
475 {
476 options->crossfade_time = atoi(value);
477 }
478 else if( !strcasecmp(CONF_SEARCH_MODE, name) )
479 {
480 options->search_mode = atoi(value);
481 }
482 else if( !strcasecmp(CONF_HIDE_CURSOR, name) )
483 {
484 options->hide_cursor = atoi(value);
485 }
486 else if( !strcasecmp(CONF_SEEK_TIME, name) )
487 {
488 options->seek_time = atoi(value);
489 }
490 else
491 {
492 match_found = 0;
493 }
496 if( !match_found )
497 fprintf(stderr,
498 _("Unknown configuration parameter: %s\n"),
499 name);
500 D("conf> %s = %s %s\n", name, value,
501 match_found ? "" : "- UNKNOWN SETTING!" );
502 }
503 }
504 }
506 D("--\n\n");
508 if( free_filename )
509 g_free(filename);
511 return 0;
512 }
514 int
515 check_user_conf_dir(void)
516 {
517 int retval;
518 char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
520 if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
521 {
522 g_free(dirname);
523 return 0;
524 }
525 retval = mkdir(dirname, 0755);
526 g_free(dirname);
527 return retval;
528 }
530 char *
531 get_user_key_binding_filename(void)
532 {
533 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
534 }
537 int
538 read_configuration(options_t *options)
539 {
540 char *filename = NULL;
542 /* check for command line configuration file */
543 if( options->config_file )
544 filename = g_strdup(options->config_file);
546 /* check for user configuration ~/.ncmpc/config */
547 if( filename == NULL )
548 {
549 filename = g_build_filename(g_get_home_dir(),
550 "." PACKAGE, "config", NULL);
551 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
552 {
553 g_free(filename);
554 filename = NULL;
555 }
556 }
558 /* check for global configuration SYSCONFDIR/ncmpc/config */
559 if( filename == NULL )
560 {
561 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
562 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
563 {
564 g_free(filename);
565 filename = NULL;
566 }
567 }
569 /* load configuration */
570 if( filename )
571 {
572 read_rc_file(filename, options);
573 g_free(filename);
574 filename = NULL;
575 }
577 /* check for command line key binding file */
578 if( options->key_file )
579 filename = g_strdup(options->key_file);
581 /* check for user key bindings ~/.ncmpc/keys */
582 if( filename == NULL )
583 {
584 filename = get_user_key_binding_filename();
585 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
586 {
587 g_free(filename);
588 filename = NULL;
589 }
590 }
592 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
593 if( filename == NULL )
594 {
595 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
596 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
597 {
598 g_free(filename);
599 filename = NULL;
600 }
601 }
603 /* load key bindings */
604 if( filename )
605 {
606 read_rc_file(filename, options);
607 g_free(filename);
608 filename = NULL;
609 }
611 return 0;
612 }