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