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