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