Code

New configuraton option: screen-list
[ncmpc.git] / src / conf.c
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"
64 #define CONF_SEEK_TIME               "seek-time"
65 #define CONF_SCREEN_LIST             "screen-list"
67 typedef enum {
68   KEY_PARSER_UNKNOWN,
69   KEY_PARSER_CHAR,
70   KEY_PARSER_DEC,
71   KEY_PARSER_HEX,
72   KEY_PARSER_DONE
73 } key_parser_state_t;
76 extern gint screen_get_id(char *name);
79 static gboolean
80 str2bool(char *str)
81 {
82   if( !strcasecmp(str,"yes")  || !strcasecmp(str,"true") || 
83       !strcasecmp(str,"on")   || !strcasecmp(str,"1") )
84     return TRUE;
85   return FALSE;
86 }
88 static int
89 parse_key_value(char *str, size_t len, char **end)
90 {
91   int i, value;
92   key_parser_state_t state;
94   i=0;
95   value=0;
96   state=KEY_PARSER_UNKNOWN;
97   *end = str;
99   while( i<len && state!=KEY_PARSER_DONE )
100     {
101       int next = 0;
102       int c = str[i];
104       if( i+1<len )
105         next = str[i+1];
107       switch(state)
108         {
109         case KEY_PARSER_UNKNOWN:
110           if( c=='\'' )
111             state = KEY_PARSER_CHAR;
112           else if( c=='0' && next=='x' )
113             state = KEY_PARSER_HEX;
114           else if( isdigit(c) )
115             state = KEY_PARSER_DEC;
116           else {
117             fprintf(stderr,
118                     _("Error: Unsupported key definition - %s\n"), 
119                     str);
120             return -1;
121           }
122           break;
123         case KEY_PARSER_CHAR:
124           if( next!='\'' )
125             {
126               fprintf(stderr,
127                       _("Error: Unsupported key definition - %s\n"), 
128                       str);
129               return -1;
130             }
131           value = c;
132           *end = str+i+2;
133           state = KEY_PARSER_DONE;
134           break;
135         case KEY_PARSER_DEC:
136           value = (int) strtol(str+(i-1), end, 10);
137           state = KEY_PARSER_DONE;
138           break;
139         case KEY_PARSER_HEX:
140           if( !isdigit(next) )
141             {
142               fprintf(stderr,_("Error: Digit expexted after 0x - %s\n"), str);
143               return -1;
144             }
145           value = (int) strtol(str+(i+1), end, 16);
146           state = KEY_PARSER_DONE;
147           break;
148         case KEY_PARSER_DONE:
149           break;
150         }
151       i++;
152     }
154   if( *end> str+len )
155     *end = str+len;
157   return value;
160 static int
161 parse_key_definition(char *str)
163   char buf[MAX_LINE_LENGTH];
164   char *p, *end;
165   size_t len = strlen(str);
166   int i,j,key;
167   int keys[MAX_COMMAND_KEYS];
168   command_t cmd;
170   /* get the command name */
171   i=0;
172   j=0;
173   memset(buf, 0, MAX_LINE_LENGTH);
174   while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
175     buf[j++] = str[i++];
176   if( (cmd=get_key_command_from_name(buf)) == CMD_NONE )
177     {
178       fprintf(stderr, _("Error: Unknown key command %s\n"), buf);
179       return -1;
180     }
182   /* skip whitespace */
183   while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
184     i++;
186   /* get the value part */
187   memset(buf, 0, MAX_LINE_LENGTH);
188   g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
189   len = strlen(buf);
190   if( len==0 )
191     {
192       fprintf(stderr,_("Error: Incomplete key definition - %s\n"), str);
193       return -1;
194     }
196   /* parse key values */
197   i = 0;
198   key = 0;
199   len = strlen(buf);
200   p = buf;
201   end = buf+len;
202   memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
203   while( i<MAX_COMMAND_KEYS && p<end && (key=parse_key_value(p,len+1,&p))>=0 )
204     {
205       keys[i++] = key;
206       while( p<end && (*p==',' || *p==' ' || *p=='\t') )
207         p++;
208       len = strlen(p);
209     } 
210   if( key<0 )
211     {
212       fprintf(stderr,_("Error: Bad key definition - %s\n"), str);
213       return -1;
214     }
216   return assign_keys(cmd, keys);
219 static int
220 parse_color(char *str)
222   char *name = str;
223   char *value = NULL;
224   int len,i;
226   i=0;
227   len=strlen(str);
228   /* get the color name */
229   while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
230     i++;
231   
232   /* skip whitespace */
233   while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
234     {
235       str[i]='\0';
236       i++;
237     } 
238   
239   if( i<len )
240     value = str+i;
242   return colors_assign(name, value);
246 static int
247 parse_color_definition(char *str)
249   char buf[MAX_LINE_LENGTH];
250   char *p, *end, *name;
251   size_t len = strlen(str);
252   int i,j,value;
253   short color, rgb[3];
255   /* get the command name */
256   i=0;
257   j=0;
258   memset(buf, 0, MAX_LINE_LENGTH);
259   while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
260     buf[j++] = str[i++];
261   color=colors_str2color(buf);
262   if( color<0 )
263     {
264       fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
265       return -1;
266     }
267   name = g_strdup(buf);
269   /* skip whitespace */
270   while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
271     i++;
273   /* get the value part */
274   memset(buf, 0, MAX_LINE_LENGTH);
275   g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
276   len = strlen(buf);
277   if( len==0 )
278     {
279       fprintf(stderr,_("Error: Incomplete color definition - %s\n"), str);
280       g_free(name);
281       return -1;
282     }
284   /* parse r,g.b values with the key definition parser */
285   i = 0;
286   value = 0;
287   len = strlen(buf);
288   p = buf;
289   end = buf+len;
290   memset(rgb, 0, sizeof(short)*3);
291   while( i<3 && p<end && (value=parse_key_value(p,len+1,&p))>=0 )
292     {
293       rgb[i++] = value;
294       while( p<end && (*p==',' || *p==' ' || *p=='\t') )
295         p++;
296       len = strlen(p);
297     } 
298   if( value<0 || i!=3)
299     {
300       fprintf(stderr,_("Error: Bad color definition - %s\n"), str);
301       g_free(name);
302       return -1;
303     }
304   value = colors_define(name, rgb[0], rgb[1], rgb[2]);
305   g_free(name);
306   return value;
309 static char *
310 get_format(char *str)
312   gsize len = strlen(str);
314   if( str && str[0]=='\"' && str[len-1] == '\"' )
315     {
316       str[len-1] = '\0';
317       str++;
318     }
319   return g_strdup(str);
322 static char **
323 check_screen_list(char *value)
325   char **tmp = g_strsplit_set(value, " \t,", 100);
326   char **screen = NULL;
327   int i,j;
329   i=0;
330   j=0;
331   while( tmp && tmp[i] )
332     {
333       tmp[i] = lowerstr(tmp[i]);
334       if( screen_get_id(tmp[i]) == -1 )
335         fprintf(stderr,
336                 _("Error: Unsupported screen \"%s\"\n"), 
337                 tmp[i]);
338       else
339         {
340           screen = g_realloc(screen, (j+2)*sizeof(char *));
341           screen[j++] = g_strdup(tmp[i]);
342           screen[j] = NULL;
343         }
344       i++;
345     }
346   g_strfreev(tmp);
347   if( screen == NULL )
348     return g_strsplit_set(DEFAULT_SCREEN_LIST, " ", 0);
350   return screen;
353 static int
354 read_rc_file(char *filename, options_t *options)
356   int fd;
357   int quit  = 0;
358   int free_filename = 0;
360   if( filename==NULL )
361     return -1;
363   D("Reading configuration file %s\n", filename);
364   if( (fd=open(filename,O_RDONLY)) <0 )
365     {
366       perror(filename);
367       if( free_filename )
368         g_free(filename);
369       return -1;
370     }
372   while( !quit )
373     {
374       int i,j;
375       int len;
376       int match_found;
377       char line[MAX_LINE_LENGTH];
378       char name[MAX_LINE_LENGTH];
379       char value[MAX_LINE_LENGTH];
381       line[0]  = '\0';
382       value[0] = '\0';
384       i = 0;
385       /* read a line ending with '\n' */
386       do {
387         len = read( fd, &line[i], 1 );
388         if( len == 1 )
389           i++;
390         else
391           quit = 1;
392       } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
393       
394      
395       /* remove trailing whitespace */
396       line[i] = '\0';
397       i--;
398       while( i>=0 && IS_WHITESPACE(line[i]) )
399         {
400           line[i] = '\0';
401           i--;
402         }     
403       len = i+1;
405       if( len>0 )
406         {
407           i = 0;
408           /* skip whitespace */
409           while( i<len && IS_WHITESPACE(line[i]) )
410             i++;
411           
412           /* continue if this line is not a comment */
413           if( line[i] != COMMENT_TOKEN )
414             {
415               /* get the name part */
416               j=0;
417               while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
418                 {
419                   name[j++] = line[i++];
420                 }
421               name[j] = '\0';
422               
423               /* skip '=' and whitespace */
424               while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
425                 i++;
426               
427               /* get the value part */
428               j=0;
429               while( i<len )
430                 {
431                   value[j++] = line[i++];
432                 }
433               value[j] = '\0';
434               
435               match_found = 1;
437               /* key definition */
438               if( !strcasecmp(CONF_KEY_DEFINITION, name) )
439                 {
440                   parse_key_definition(value);
441                 }
442               /* enable colors */
443               else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
444                 {
445                   options->enable_colors = str2bool(value);
446                 }
447               /* auto center */
448               else if( !strcasecmp(CONF_AUTO_CENTER, name) )
449                 {
450                   options->auto_center = str2bool(value);
451                 }
452               /* color assignment */
453               else if( !strcasecmp(CONF_COLOR, name) )
454                 {
455                   parse_color(value);
456                 }
457               /* wide cursor */
458               else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
459                 {
460                   options->wide_cursor = str2bool(value);
461                 }
462               /* color definition */
463               else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
464                 {
465                   parse_color_definition(value);
466                 }
467               /* list format string */
468               else if( !strcasecmp(CONF_LIST_FORMAT, name) )
469                 {
470                   g_free(options->list_format);
471                   options->list_format = get_format(value);
472                 }
473               /* status format string */
474               else if( !strcasecmp(CONF_STATUS_FORMAT, name) )
475                 {
476                   g_free(options->status_format);
477                   options->status_format = get_format(value);
478                 }
479               /* xterm title format string */
480               else if( !strcasecmp(CONF_XTERM_TITLE_FORMAT, name) )
481                 {
482                   g_free(options->xterm_title_format);
483                   options->xterm_title_format = get_format(value);
484                 }
485               else if( !strcasecmp(CONF_LIST_WRAP, name) )
486                 {
487                   options->list_wrap = str2bool(value);
488                 }
489               else if( !strcasecmp(CONF_FIND_WRAP, name) )
490                 {
491                   options->find_wrap = str2bool(value);
492                 }
493               else if( !strcasecmp(CONF_AUDIBLE_BELL, name) )
494                 {
495                   options->audible_bell = str2bool(value);
496                 }
497               else if( !strcasecmp(CONF_VISIBLE_BELL, name) )
498                 {
499                   options->visible_bell = str2bool(value);
500                 }
501               else if( !strcasecmp(CONF_XTERM_TITLE, name) )
502                 {
503                   options->enable_xterm_title = str2bool(value);
504                 }
505               else if( !strcasecmp(CONF_ENABLE_MOUSE, name) )
506                 {
507                   options->enable_mouse = str2bool(value);
508                 }
509               else if( !strcasecmp(CONF_CROSSFADE_TIME, name) )
510                 {
511                   options->crossfade_time = atoi(value);
512                 }
513               else if( !strcasecmp(CONF_SEARCH_MODE, name) )
514                 {
515                   options->search_mode = atoi(value);
516                 }
517               else if( !strcasecmp(CONF_HIDE_CURSOR, name) )
518                 {
519                   options->hide_cursor = atoi(value);
520                 }
521               else if( !strcasecmp(CONF_SEEK_TIME, name) )
522                 {
523                   options->seek_time = atoi(value);
524                 }
525               else if( !strcasecmp(CONF_SCREEN_LIST, name) )
526                 {
527                   g_strfreev(options->screen_list);
528                   options->screen_list = check_screen_list(value);
529                   
530 #ifdef DEBUG
531                   D("screen-list:"); 
532                   j=0;
533                   while(options->screen_list[j])
534                     D(" %s", options->screen_list[j++]);
535                   D("\n"); 
536 #endif
537                 }
538               else
539                 {
540                   match_found = 0;
541                 }
543               if( !match_found )
544                 fprintf(stderr, 
545                         _("Unknown configuration parameter: %s\n"), 
546                         name);
547               D("conf>  %s = %s %s\n", name, value,
548                 match_found ? "" : "- UNKNOWN SETTING!" );
549             }
550         }         
551     }
553   D("--\n\n");
555   if( free_filename )
556     g_free(filename);
557  
558   return 0;
561 int
562 check_user_conf_dir(void)
564   int retval;
565   char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
566   
567   if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
568     {
569       g_free(dirname);
570       return 0;
571     }
572   retval = mkdir(dirname, 0755);
573   g_free(dirname);
574   return retval;
577 char *
578 get_user_key_binding_filename(void)
580   return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
584 int
585 read_configuration(options_t *options)
587   char *filename = NULL;
589   /* check for command line configuration file */
590   if( options->config_file )
591     filename = g_strdup(options->config_file);
593   /* check for user configuration ~/.ncmpc/config */
594   if( filename == NULL )
595     {
596       filename = g_build_filename(g_get_home_dir(), 
597                                   "." PACKAGE, "config", NULL);
598       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
599         {
600           g_free(filename);
601           filename = NULL;
602         }
603     }
605   /* check for  global configuration SYSCONFDIR/ncmpc/config */
606   if( filename == NULL )
607     {
608       filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
609       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
610         {
611           g_free(filename);
612           filename = NULL;
613         }
614     }
616   /* load configuration */
617   if( filename )
618     {
619       read_rc_file(filename, options);
620       g_free(filename);
621       filename = NULL;
622     }
624   /* check for command line key binding file */
625   if( options->key_file )
626     filename = g_strdup(options->key_file);
628   /* check for  user key bindings ~/.ncmpc/keys */
629   if( filename == NULL )
630     {
631       filename = get_user_key_binding_filename();
632       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
633         {
634           g_free(filename);
635           filename = NULL;
636         }
637     }
639   /* check for  global key bindings SYSCONFDIR/ncmpc/keys */
640   if( filename == NULL )
641     {
642       filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
643       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
644         {
645           g_free(filename);
646           filename = NULL;
647         }
648     }
650   /* load key bindings */
651   if( filename )
652     {
653       read_rc_file(filename, options);
654       g_free(filename);
655       filename = NULL;
656     }
658   return 0;