Code

the updated sources
[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_FIND_SHOW_LAST          "find-show-last"
58 #define CONF_AUDIBLE_BELL            "audible-bell"
59 #define CONF_VISIBLE_BELL            "visible-bell"
60 #define CONF_XTERM_TITLE             "set-xterm-title"
61 #define CONF_ENABLE_MOUSE            "enable-mouse"
62 #define CONF_CROSSFADE_TIME          "crossfade-time"
63 #define CONF_SEARCH_MODE             "search-mode"
64 #define CONF_HIDE_CURSOR             "hide-cursor"
65 #define CONF_SEEK_TIME               "seek-time"
66 #define CONF_SCREEN_LIST             "screen-list"
67 #define CONF_TIMEDISPLAY_TYPE        "timedisplay-type"
68 #define CONF_HOST                    "host"
69 #define CONF_PORT                    "port"
70 #define CONF_LYRICS_TIMEOUT          "lyrics-timeout"
72 typedef enum {
73   KEY_PARSER_UNKNOWN,
74   KEY_PARSER_CHAR,
75   KEY_PARSER_DEC,
76   KEY_PARSER_HEX,
77   KEY_PARSER_DONE
78 } key_parser_state_t;
81 extern gint screen_get_id(char *name);
84 static gboolean
85 str2bool(char *str)
86 {
87   if( !strcasecmp(str,"yes")  || !strcasecmp(str,"true") || 
88       !strcasecmp(str,"on")   || !strcasecmp(str,"1") )
89     return TRUE;
90   return FALSE;
91 }
93 static int
94 parse_key_value(char *str, size_t len, char **end)
95 {
96   int i, value;
97   key_parser_state_t state;
99   i=0;
100   value=0;
101   state=KEY_PARSER_UNKNOWN;
102   *end = str;
104   while( i<len && state!=KEY_PARSER_DONE )
105     {
106       int next = 0;
107       int c = str[i];
109       if( i+1<len )
110         next = str[i+1];
112       switch(state)
113         {
114         case KEY_PARSER_UNKNOWN:
115           if( c=='\'' )
116             state = KEY_PARSER_CHAR;
117           else if( c=='0' && next=='x' )
118             state = KEY_PARSER_HEX;
119           else if( isdigit(c) )
120             state = KEY_PARSER_DEC;
121           else {
122             fprintf(stderr,
123                     _("Error: Unsupported key definition - %s\n"), 
124                     str);
125             return -1;
126           }
127           break;
128         case KEY_PARSER_CHAR:
129           if( next!='\'' )
130             {
131               fprintf(stderr,
132                       _("Error: Unsupported key definition - %s\n"), 
133                       str);
134               return -1;
135             }
136           value = c;
137           *end = str+i+2;
138           state = KEY_PARSER_DONE;
139           break;
140         case KEY_PARSER_DEC:
141           value = (int) strtol(str+(i-1), end, 10);
142           state = KEY_PARSER_DONE;
143           break;
144         case KEY_PARSER_HEX:
145           if( !isdigit(next) )
146             {
147               fprintf(stderr,_("Error: Digit expexted after 0x - %s\n"), str);
148               return -1;
149             }
150           value = (int) strtol(str+(i+1), end, 16);
151           state = KEY_PARSER_DONE;
152           break;
153         case KEY_PARSER_DONE:
154           break;
155         }
156       i++;
157     }
159   if( *end> str+len )
160     *end = str+len;
162   return value;
165 static int
166 parse_key_definition(char *str)
168   char buf[MAX_LINE_LENGTH];
169   char *p, *end;
170   size_t len = strlen(str);
171   int i,j,key;
172   int keys[MAX_COMMAND_KEYS];
173   command_t cmd;
175   /* get the command name */
176   i=0;
177   j=0;
178   memset(buf, 0, MAX_LINE_LENGTH);
179   while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
180     buf[j++] = str[i++];
181   if( (cmd=get_key_command_from_name(buf)) == CMD_NONE )
182     {
183       fprintf(stderr, _("Error: Unknown key command %s\n"), buf);
184       return -1;
185     }
187   /* skip whitespace */
188   while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
189     i++;
191   /* get the value part */
192   memset(buf, 0, MAX_LINE_LENGTH);
193   g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
194   len = strlen(buf);
195   if( len==0 )
196     {
197       fprintf(stderr,_("Error: Incomplete key definition - %s\n"), str);
198       return -1;
199     }
201   /* parse key values */
202   i = 0;
203   key = 0;
204   len = strlen(buf);
205   p = buf;
206   end = buf+len;
207   memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
208   while( i<MAX_COMMAND_KEYS && p<end && (key=parse_key_value(p,len+1,&p))>=0 )
209     {
210       keys[i++] = key;
211       while( p<end && (*p==',' || *p==' ' || *p=='\t') )
212         p++;
213       len = strlen(p);
214     } 
215   if( key<0 )
216     {
217       fprintf(stderr,_("Error: Bad key definition - %s\n"), str);
218       return -1;
219     }
221   return assign_keys(cmd, keys);
224 static char *
225 parse_timedisplay_type(char *str)
227  if((!strcmp(str,"elapsed")) || (!strcmp(str,"remaining"))){
228    return str;
229  } else {
230    fprintf(stderr,_("Error: Bad time display type - %s\n"), str);
231    return DEFAULT_TIMEDISPLAY_TYPE;
232  }
235 static int
236 parse_color(char *str)
238   char *name = str;
239   char *value = NULL;
240   int len,i;
242   i=0;
243   len=strlen(str);
244   /* get the color name */
245   while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
246     i++;
247   
248   /* skip whitespace */
249   while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
250     {
251       str[i]='\0';
252       i++;
253     } 
254   
255   if( i<len )
256     value = str+i;
258   return colors_assign(name, value);
261 static int
262 parse_color_definition(char *str)
264   char buf[MAX_LINE_LENGTH];
265   char *p, *end, *name;
266   size_t len = strlen(str);
267   int i,j,value;
268   short color, rgb[3];
270   /* get the command name */
271   i=0;
272   j=0;
273   memset(buf, 0, MAX_LINE_LENGTH);
274   while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
275     buf[j++] = str[i++];
276   color=colors_str2color(buf);
277   if( color<0 )
278     {
279       fprintf(stderr,_("Error: Bad color %s [%d]\n"), buf, color);
280       return -1;
281     }
282   name = g_strdup(buf);
284   /* skip whitespace */
285   while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
286     i++;
288   /* get the value part */
289   memset(buf, 0, MAX_LINE_LENGTH);
290   g_strlcpy(buf, str+i, MAX_LINE_LENGTH);
291   len = strlen(buf);
292   if( len==0 )
293     {
294       fprintf(stderr,_("Error: Incomplete color definition - %s\n"), str);
295       g_free(name);
296       return -1;
297     }
299   /* parse r,g.b values with the key definition parser */
300   i = 0;
301   value = 0;
302   len = strlen(buf);
303   p = buf;
304   end = buf+len;
305   memset(rgb, 0, sizeof(short)*3);
306   while( i<3 && p<end && (value=parse_key_value(p,len+1,&p))>=0 )
307     {
308       rgb[i++] = value;
309       while( p<end && (*p==',' || *p==' ' || *p=='\t') )
310         p++;
311       len = strlen(p);
312     } 
313   if( value<0 || i!=3)
314     {
315       fprintf(stderr,_("Error: Bad color definition - %s\n"), str);
316       g_free(name);
317       return -1;
318     }
319   value = colors_define(name, rgb[0], rgb[1], rgb[2]);
320   g_free(name);
321   return value;
324 static char *
325 get_format(char *str)
327   gsize len = strlen(str);
329   if( str && str[0]=='\"' && str[len-1] == '\"' )
330     {
331       str[len-1] = '\0';
332       str++;
333     }
334   return g_strdup(str);
337 static char **
338 check_screen_list(char *value)
340   char **tmp = g_strsplit_set(value, " \t,", 100);
341   char **screen = NULL;
342   int i,j;
344   i=0;
345   j=0;
346   while( tmp && tmp[i] )
347     {
348       tmp[i] = lowerstr(tmp[i]);
349       if( screen_get_id(tmp[i]) == -1 )
350         fprintf(stderr,
351                 _("Error: Unsupported screen \"%s\"\n"), 
352                 tmp[i]);
353       else
354         {
355           screen = g_realloc(screen, (j+2)*sizeof(char *));
356           screen[j++] = g_strdup(tmp[i]);
357           screen[j] = NULL;
358         }
359       i++;
360     }
361   g_strfreev(tmp);
362   if( screen == NULL )
363     return g_strsplit_set(DEFAULT_SCREEN_LIST, " ", 0);
365   return screen;
368 static int
369 read_rc_file(char *filename, options_t *options)
371   int fd;
372   int quit  = 0;
373   int free_filename = 0;
375   if( filename==NULL )
376     return -1;
378   D("Reading configuration file %s\n", filename);
379   if( (fd=open(filename,O_RDONLY)) <0 )
380     {
381       perror(filename);
382       if( free_filename )
383         g_free(filename);
384       return -1;
385     }
387   while( !quit )
388     {
389       int i,j;
390       int len;
391       int match_found;
392       char line[MAX_LINE_LENGTH];
393       char name[MAX_LINE_LENGTH];
394       char value[MAX_LINE_LENGTH];
396       line[0]  = '\0';
397       value[0] = '\0';
399       i = 0;
400       /* read a line ending with '\n' */
401       do {
402         len = read( fd, &line[i], 1 );
403         if( len == 1 )
404           i++;
405         else
406           quit = 1;
407       } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
408       
409      
410       /* remove trailing whitespace */
411       line[i] = '\0';
412       i--;
413       while( i>=0 && IS_WHITESPACE(line[i]) )
414         {
415           line[i] = '\0';
416           i--;
417         }     
418       len = i+1;
420       if( len>0 )
421         {
422           i = 0;
423           /* skip whitespace */
424           while( i<len && IS_WHITESPACE(line[i]) )
425             i++;
426           
427           /* continue if this line is not a comment */
428           if( line[i] != COMMENT_TOKEN )
429             {
430               /* get the name part */
431               j=0;
432               while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
433                 {
434                   name[j++] = line[i++];
435                 }
436               name[j] = '\0';
437               
438               /* skip '=' and whitespace */
439               while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
440                 i++;
441               
442               /* get the value part */
443               j=0;
444               while( i<len )
445                 {
446                   value[j++] = line[i++];
447                 }
448               value[j] = '\0';
449               
450               match_found = 1;
452               /* key definition */
453               if( !strcasecmp(CONF_KEY_DEFINITION, name) )
454                 {
455                   parse_key_definition(value);
456                 }
457               /* enable colors */
458               else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
459                 {
460                   options->enable_colors = str2bool(value);
461                 }
462               /* auto center */
463               else if( !strcasecmp(CONF_AUTO_CENTER, name) )
464                 {
465                   options->auto_center = str2bool(value);
466                 }
467               /* color assignment */
468               else if( !strcasecmp(CONF_COLOR, name) )
469                 {
470                   parse_color(value);
471                 }
472               /* wide cursor */
473               else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
474                 {
475                   options->wide_cursor = str2bool(value);
476                 }
477               /* timer display type */
478               else if( !strcasecmp(CONF_TIMEDISPLAY_TYPE, name) )
479                 {
480                     g_free(options->timedisplay_type);
481                     options->timedisplay_type=g_strdup(parse_timedisplay_type(value));
482                     D("deb");
483                     D(options->timedisplay_type);
484                 }
485               /* color definition */
486               else if( !strcasecmp(CONF_COLOR_DEFINITION, name) )
487                 {
488                   parse_color_definition(value);
489                 }
490               /* list format string */
491               else if( !strcasecmp(CONF_LIST_FORMAT, name) )
492                 {
493                   g_free(options->list_format);
494                   options->list_format = get_format(value);
495                 }
496               /* status format string */
497               else if( !strcasecmp(CONF_STATUS_FORMAT, name) )
498                 {
499                   g_free(options->status_format);
500                   options->status_format = get_format(value);
501                 }
502               /* xterm title format string */
503               else if( !strcasecmp(CONF_XTERM_TITLE_FORMAT, name) )
504                 {
505                   g_free(options->xterm_title_format);
506                   options->xterm_title_format = get_format(value);
507                 }
508               else if( !strcasecmp(CONF_LIST_WRAP, name) )
509                 {
510                   options->list_wrap = str2bool(value);
511                 }
512               else if( !strcasecmp(CONF_FIND_WRAP, name) )
513                 {
514                   options->find_wrap = str2bool(value);
515                 }
516               else if( !strcasecmp(CONF_FIND_SHOW_LAST,name) )
517                 {
518                   options->find_show_last_pattern = str2bool(value);
519                 }
520               else if( !strcasecmp(CONF_AUDIBLE_BELL, name) )
521                 {
522                   options->audible_bell = str2bool(value);
523                 }
524               else if( !strcasecmp(CONF_VISIBLE_BELL, name) )
525                 {
526                   options->visible_bell = str2bool(value);
527                 }
528               else if( !strcasecmp(CONF_XTERM_TITLE, name) )
529                 {
530                   options->enable_xterm_title = str2bool(value);
531                 }
532               else if( !strcasecmp(CONF_ENABLE_MOUSE, name) )
533                 {
534                   options->enable_mouse = str2bool(value);
535                 }
536               else if( !strcasecmp(CONF_CROSSFADE_TIME, name) )
537                 {
538                   options->crossfade_time = atoi(value);
539                 }
540               else if( !strcasecmp(CONF_SEARCH_MODE, name) )
541                 {
542                   options->search_mode = atoi(value);
543                 }
544               else if( !strcasecmp(CONF_HIDE_CURSOR, name) )
545                 {
546                   options->hide_cursor = atoi(value);
547                 }
548               else if( !strcasecmp(CONF_SEEK_TIME, name) )
549                 {
550                   options->seek_time = atoi(value);
551                 }
552               else if( !strcasecmp(CONF_SCREEN_LIST, name) )
553                 {
554                   g_strfreev(options->screen_list);
555                   options->screen_list = check_screen_list(value);
556                   
557 #ifdef DEBUG
558                   D("screen-list:"); 
559                   j=0;
560                   while(options->screen_list[j])
561                     D(" %s", options->screen_list[j++]);
562                   D("\n"); 
563 #endif
564                 }
565             else if( !strcasecmp(CONF_HOST, name))
566             {
567             options->host = get_format(value);
568             }
569             else if( !strcasecmp(CONF_PORT, name))
570             {
571             options->port = atoi(get_format(value));
572             }
573                 else if( !strcasecmp(CONF_LYRICS_TIMEOUT, name))
574             {
575             options->lyrics_timeout = atoi(get_format(value));
576             }           
577               else
578                 {
579                   match_found = 0;
580                 }
582               if( !match_found )
583                 fprintf(stderr, 
584                         _("Unknown configuration parameter: %s\n"), 
585                         name);
586               D("conf>  %s = %s %s\n", name, value,
587                 match_found ? "" : "- UNKNOWN SETTING!" );
588             }
589         }         
590     }
592   D("--\n\n");
594   if( free_filename )
595     g_free(filename);
596  
597   return 0;
600 int
601 check_user_conf_dir(void)
603   int retval;
604   char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
605   
606   if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
607     {
608       g_free(dirname);
609       return 0;
610     }
611   retval = mkdir(dirname, 0755);
612   g_free(dirname);
613   return retval;
616 char *
617 get_user_key_binding_filename(void)
619   return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
623 int
624 read_configuration(options_t *options)
626   char *filename = NULL;
628   /* check for command line configuration file */
629   if( options->config_file )
630     filename = g_strdup(options->config_file);
632   /* check for user configuration ~/.ncmpc/config */
633   if( filename == NULL )
634     {
635       filename = g_build_filename(g_get_home_dir(), 
636                                   "." PACKAGE, "config", NULL);
637       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
638         {
639           g_free(filename);
640           filename = NULL;
641         }
642     }
644   /* check for  global configuration SYSCONFDIR/ncmpc/config */
645   if( filename == NULL )
646     {
647       filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
648       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
649         {
650           g_free(filename);
651           filename = NULL;
652         }
653     }
655   /* load configuration */
656   if( filename )
657     {
658       read_rc_file(filename, options);
659       g_free(filename);
660       filename = NULL;
661     }
663   /* check for command line key binding file */
664   if( options->key_file )
665     filename = g_strdup(options->key_file);
667   /* check for  user key bindings ~/.ncmpc/keys */
668   if( filename == NULL )
669     {
670       filename = get_user_key_binding_filename();
671       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
672         {
673           g_free(filename);
674           filename = NULL;
675         }
676     }
678   /* check for  global key bindings SYSCONFDIR/ncmpc/keys */
679   if( filename == NULL )
680     {
681       filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
682       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
683         {
684           g_free(filename);
685           filename = NULL;
686         }
687     }
689   /* load key bindings */
690   if( filename )
691     {
692       read_rc_file(filename, options);
693       g_free(filename);
694       filename = NULL;
695     }
697   return 0;