Code

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