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