1 /*
2 * (c) 2004 by Kalle Wallin (kaw@linux.se)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 */
19 #include <ctype.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
29 #include <glib.h>
30 #include <ncurses.h>
31 #include "config.h"
32 #include "ncmpc.h"
33 #include "options.h"
34 #include "support.h"
35 #include "command.h"
36 #include "colors.h"
37 #include "conf.h"
39 #define ENABLE_OLD_COLOR_SYNTAX
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_KEY_DEFINITION "key"
49 #define CONF_COLOR "color"
50 #define CONF_COLOR_DEFINITION "colordef"
52 /* Deprecated - configuration field names */
53 #define CONF_COLOR_BACKGROUND "background_color"
54 #define CONF_COLOR_TITLE "title_color"
55 #define CONF_COLOR_LINE "line_color"
56 #define CONF_COLOR_LIST "list_color"
57 #define CONF_COLOR_PROGRESS "progress_color"
58 #define CONF_COLOR_STATUS "status_color"
59 #define CONF_COLOR_ALERT "alert_color"
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 int
71 str2bool(char *str)
72 {
73 if( !strcasecmp(str,"no") || !strcasecmp(str,"false") ||
74 !strcasecmp(str,"off") || !strcasecmp(str,"0") )
75 return 0;
76 return 1;
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 strncpy(buf, str+i, len-i);
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 strncpy(buf, str+i, len-i);
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 }
301 static int
302 read_rc_file(char *filename, options_t *options)
303 {
304 int fd;
305 int quit = 0;
306 int free_filename = 0;
308 if( filename==NULL )
309 return -1;
311 D(printf("Reading configuration file %s\n", filename));
312 if( (fd=open(filename,O_RDONLY)) <0 )
313 {
314 perror(filename);
315 if( free_filename )
316 g_free(filename);
317 return -1;
318 }
320 while( !quit )
321 {
322 int i,j;
323 int len;
324 int match_found;
325 char line[MAX_LINE_LENGTH];
326 char name[MAX_LINE_LENGTH];
327 char value[MAX_LINE_LENGTH];
329 line[0] = '\0';
330 value[0] = '\0';
332 i = 0;
333 /* read a line ending with '\n' */
334 do {
335 len = read( fd, &line[i], 1 );
336 if( len == 1 )
337 i++;
338 else
339 quit = 1;
340 } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
343 /* remove trailing whitespace */
344 line[i] = '\0';
345 i--;
346 while( i>=0 && IS_WHITESPACE(line[i]) )
347 {
348 line[i] = '\0';
349 i--;
350 }
351 len = i+1;
353 if( len>0 )
354 {
355 i = 0;
356 /* skip whitespace */
357 while( i<len && IS_WHITESPACE(line[i]) )
358 i++;
360 /* continue if this line is not a comment */
361 if( line[i] != COMMENT_TOKEN )
362 {
363 /* get the name part */
364 j=0;
365 while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
366 {
367 name[j++] = line[i++];
368 }
369 name[j] = '\0';
371 /* skip '=' and whitespace */
372 while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
373 i++;
375 /* get the value part */
376 j=0;
377 while( i<len )
378 {
379 value[j++] = line[i++];
380 }
381 value[j] = '\0';
383 match_found = 1;
385 /* key definition */
386 if( !strcasecmp(CONF_KEY_DEFINITION, name) )
387 {
388 parse_key_definition(value);
389 }
390 /* enable colors */
391 else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
392 {
393 options->enable_colors = str2bool(value);
394 }
395 /* auto center */
396 else if( !strcasecmp(CONF_AUTO_CENTER, name) )
397 {
398 options->auto_center = str2bool(value);
399 }
400 /* color assignment */
401 else if( !strcasecmp(CONF_COLOR, name) )
402 {
403 parse_color(value);
404 }
405 #ifdef ENABLE_OLD_COLOR_SYNTAX
406 /* background color */
407 else if( !strcasecmp(CONF_COLOR_BACKGROUND, name) )
408 {
409 fprintf(stderr,"%s: %s - deprecated!\n", filename,name);
410 colors_assign("background", value);
411 }
412 /* color - top (title) window */
413 else if( !strcasecmp(CONF_COLOR_TITLE, name) )
414 {
415 fprintf(stderr,"%s: %s - deprecated!\n", filename,name);
416 colors_assign("title", value);
417 colors_assign("title2", value);
418 }
419 /* color - line (title) window */
420 else if( !strcasecmp(CONF_COLOR_LINE, name) )
421 {
422 fprintf(stderr,"%s: %s - deprecated!\n", filename,name);
423 colors_assign("line", value);
424 colors_assign("line2", value);
425 }
426 /* color - list window */
427 else if( !strcasecmp(CONF_COLOR_LIST, name) )
428 {
429 fprintf(stderr,"%s: %s - deprecated!\n", filename,name);
430 colors_assign("list", value);
431 }
432 /* color - progress bar */
433 else if( !strcasecmp(CONF_COLOR_PROGRESS, name) )
434 {
435 fprintf(stderr,"%s: %s - deprecated!\n", filename,name);
436 colors_assign("progressbar", value);
437 }
438 /* color - status window */
439 else if( !strcasecmp(CONF_COLOR_STATUS, name) )
440 {
441 fprintf(stderr,"%s: %s - deprecated!\n", filename,name);
442 colors_assign("status", value);
443 colors_assign("status2", value);
444 }
445 /* color - alerts */
446 else if( !strcasecmp(CONF_COLOR_ALERT, name) )
447 {
448 fprintf(stderr,"%s: %s - deprecated!\n", filename,name);
449 colors_assign("alert", value);
450 }
451 #endif
452 /* wide cursor */
453 else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
454 {
455 options->wide_cursor = str2bool(value);
456 }
457 /* color definition */
458 else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
459 {
460 parse_color_definition(value);
461 }
462 else
463 {
464 match_found = 0;
465 }
468 if( !match_found )
469 fprintf(stderr,
470 _("Unknown configuration parameter: %s\n"),
471 name);
472 #ifdef DEBUG
473 printf( " %s = %s %s\n",
474 name,
475 value,
476 match_found ? "" : "- UNKNOWN SETTING!" );
477 #endif
479 }
480 }
481 }
483 D(printf( "--\n\n" ));
485 if( free_filename )
486 g_free(filename);
488 return 0;
489 }
491 int
492 check_user_conf_dir(void)
493 {
494 int retval;
495 char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
497 if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
498 {
499 g_free(dirname);
500 return 0;
501 }
502 retval = mkdir(dirname, 0755);
503 g_free(dirname);
504 return retval;
505 }
507 char *
508 get_user_key_binding_filename(void)
509 {
510 return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
511 }
514 int
515 read_configuration(options_t *options)
516 {
517 char *filename = NULL;
519 /* check for command line configuration file */
520 if( options->config_file )
521 filename = g_strdup(options->config_file);
523 /* check for user configuration ~/.ncmpc/config */
524 if( filename == NULL )
525 {
526 filename = g_build_filename(g_get_home_dir(),
527 "." PACKAGE, "config", NULL);
528 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
529 {
530 g_free(filename);
531 filename = NULL;
532 }
533 }
535 /* check for global configuration SYSCONFDIR/ncmpc/config */
536 if( filename == NULL )
537 {
538 filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
539 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
540 {
541 g_free(filename);
542 filename = NULL;
543 }
544 }
546 /* load configuration */
547 if( filename )
548 {
549 read_rc_file(filename, options);
550 g_free(filename);
551 filename = NULL;
552 }
554 /* check for command line key binding file */
555 if( options->key_file )
556 filename = g_strdup(options->key_file);
558 /* check for user key bindings ~/.ncmpc/keys */
559 if( filename == NULL )
560 {
561 filename = get_user_key_binding_filename();
562 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
563 {
564 g_free(filename);
565 filename = NULL;
566 }
567 }
569 /* check for global key bindings SYSCONFDIR/ncmpc/keys */
570 if( filename == NULL )
571 {
572 filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
573 if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
574 {
575 g_free(filename);
576 filename = NULL;
577 }
578 }
580 /* load key bindings */
581 if( filename )
582 {
583 read_rc_file(filename, options);
584 g_free(filename);
585 filename = NULL;
586 }
588 return 0;
589 }