Code

0c0448ed4e9f9200774796ae24332607b4dd371c
[ncmpc.git] / conf.c
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>
32 #include "config.h"
33 #include "options.h"
34 #include "support.h"
35 #include "command.h"
36 #include "conf.h"
38 #ifdef DEBUG
39 #define D(x) x
40 #else
41 #define D(x)
42 #endif
44 #define RCFILE "." PACKAGE "rc"
46 #define MAX_LINE_LENGTH 1024
47 #define COMMENT_TOKEN   '#'
49 /* configuration field names */
50 #define CONF_ENABLE_COLORS           "enable_colors"
51 #define CONF_AUTO_CENTER             "auto_center"
53 /* configuration field names - colors */
54 #define CONF_COLOR_BACKGROUND        "background_color"
55 #define CONF_COLOR_TITLE             "title_color"
56 #define CONF_COLOR_LINE              "line_color"
57 #define CONF_COLOR_LIST              "list_color"
58 #define CONF_COLOR_PROGRESS          "progress_color"
59 #define CONF_COLOR_STATUS            "status_color"
60 #define CONF_COLOR_ALERT             "alert_color"
61 #define CONF_WIDE_CURSOR             "wide_cursor"
62 #define CONF_KEY_DEFINITION          "key"
64 typedef enum {
65   KEY_PARSER_UNKNOWN,
66   KEY_PARSER_CHAR,
67   KEY_PARSER_DEC,
68   KEY_PARSER_HEX,
69   KEY_PARSER_DONE
70 } key_parser_state_t;
72 static int
73 str2bool(char *str)
74 {
75   if( !strcasecmp(str,"no")  || !strcasecmp(str,"false") || 
76       !strcasecmp(str,"off") || !strcasecmp(str,"0") )
77     return 0;
78   return 1;
79 }
81 static int
82 str2color(char *str)
83 {
84   if( !strcasecmp(str,"black") )
85     return COLOR_BLACK;
86   else if( !strcasecmp(str,"red") )
87     return COLOR_RED;
88   else if( !strcasecmp(str,"green") )
89     return COLOR_GREEN;
90   else if( !strcasecmp(str,"yellow") )
91     return COLOR_YELLOW;
92   else if( !strcasecmp(str,"blue") )
93     return COLOR_BLUE;
94   else if( !strcasecmp(str,"magenta") )
95     return COLOR_MAGENTA;
96   else if( !strcasecmp(str,"cyan") )
97     return COLOR_CYAN;
98   else if( !strcasecmp(str,"white") )
99     return COLOR_WHITE;
100   fprintf(stderr,"Warning: unknown color %s\n", str);
101   return -1;
104 static int
105 parse_key_value(char *str, size_t len, char **end)
107   int i, value;
108   key_parser_state_t state;
110   i=0;
111   value=0;
112   state=KEY_PARSER_UNKNOWN;
113   *end = str;
115   while( i<len && state!=KEY_PARSER_DONE )
116     {
117       int next = 0;
118       int c = str[i];
120       if( i+1<len )
121         next = str[i+1];
123       switch(state)
124         {
125         case KEY_PARSER_UNKNOWN:
126           if( c=='\'' )
127             state = KEY_PARSER_CHAR;
128           else if( c=='0' && next=='x' )
129             state = KEY_PARSER_HEX;
130           else if( isdigit(c) )
131             state = KEY_PARSER_DEC;
132           else {
133             fprintf(stderr, "Error: Unsupported key definition - %s\n", str);
134             return -1;
135           }
136           break;
137         case KEY_PARSER_CHAR:
138           if( next!='\'' )
139             {
140               fprintf(stderr, "Error: Unsupported key definition - %s\n", str);
141               return -1;
142             }
143           value = c;
144           *end = str+i+2;
145           state = KEY_PARSER_DONE;
146           break;
147         case KEY_PARSER_DEC:
148           value = (int) strtol(str+(i-1), end, 10);
149           state = KEY_PARSER_DONE;
150           break;
151         case KEY_PARSER_HEX:
152           if( !isdigit(next) )
153             {
154               fprintf(stderr, "Error: Digit expexted after 0x - %s\n", str);
155               return -1;
156             }
157           value = (int) strtol(str+(i+1), end, 16);
158           state = KEY_PARSER_DONE;
159           break;
160         case KEY_PARSER_DONE:
161           break;
162         }
163       i++;
164     }
166   if( *end> str+len )
167     *end = str+len;
169   return value;
172 static int
173 parse_key_definition(char *str)
175   char buf[MAX_LINE_LENGTH];
176   char *p, *end;
177   size_t len = strlen(str);
178   int i,j,key;
179   int keys[MAX_COMMAND_KEYS];
180   command_t cmd;
182   /* get the command name */
183   i=0;
184   j=0;
185   memset(buf, 0, MAX_LINE_LENGTH);
186   while( i<len && str[i]!='=' && !IS_WHITESPACE(str[i]) )
187     buf[j++] = str[i++];
188   if( (cmd=get_key_command_from_name(buf)) == CMD_NONE )
189     {
190       fprintf(stderr, "Error: Unknown key command %s\n", buf);
191       return -1;
192     }
194   /* skip whitespace */
195   while( i<len && (str[i]=='=' || IS_WHITESPACE(str[i])) )
196     i++;
198   /* get the value part */
199   memset(buf, 0, MAX_LINE_LENGTH);
200   strncpy(buf, str+i, len-i);
201   len = strlen(buf);
202   if( len==0 )
203     {
204       fprintf(stderr,"Error: Incomplete key definition - %s\n", str);
205       return -1;
206     }
208   /* parse key values */
209   i = 0;
210   key = 0;
211   len = strlen(buf);
212   p = buf;
213   end = buf+len;
214   memset(keys, 0, sizeof(int)*MAX_COMMAND_KEYS);
215   while( i<MAX_COMMAND_KEYS && p<end && (key=parse_key_value(p,len+1,&p))>=0 )
216     {
217       keys[i++] = key;
218       while( p<end && (*p==',' || *p==' ' || *p=='\t') )
219         p++;
220       len = strlen(p);
221     } 
222   if( key<0 )
223     {
224       fprintf(stderr,"Error: Bad key definition - %s\n", str);
225       return -1;
226     }
228   return assign_keys(cmd, keys);
231 static int
232 read_rc_file(char *filename, options_t *options)
234   int fd;
235   int quit  = 0;
236   int color = -1;
237   int free_filename = 0;
239   if( filename==NULL )
240     return -1;
242   D(printf("Reading configuration file %s\n", filename));
243   if( (fd=open(filename,O_RDONLY)) <0 )
244     {
245       D(perror(filename));
246       if( free_filename )
247         g_free(filename);
248       return -1;
249     }
251   while( !quit )
252     {
253       int i,j;
254       int len;
255       int match_found;
256       char line[MAX_LINE_LENGTH];
257       char name[MAX_LINE_LENGTH];
258       char value[MAX_LINE_LENGTH];
260       line[0]  = '\0';
261       value[0] = '\0';
263       i = 0;
264       /* read a line ending with '\n' */
265       do {
266         len = read( fd, &line[i], 1 );
267         if( len == 1 )
268           i++;
269         else
270           quit = 1;
271       } while( !quit && i<MAX_LINE_LENGTH && line[i-1]!='\n' );
272       
273      
274       /* remove trailing whitespace */
275       line[i] = '\0';
276       i--;
277       while( i>=0 && IS_WHITESPACE(line[i]) )
278         {
279           line[i] = '\0';
280           i--;
281         }     
282       len = i+1;
284       if( len>0 )
285         {
286           i = 0;
287           /* skip whitespace */
288           while( i<len && IS_WHITESPACE(line[i]) )
289             i++;
290           
291           /* continue if this line is not a comment */
292           if( line[i] != COMMENT_TOKEN )
293             {
294               /* get the name part */
295               j=0;
296               while( i<len && line[i]!='=' && !IS_WHITESPACE(line[i]) )
297                 {
298                   name[j++] = line[i++];
299                 }
300               name[j] = '\0';
301               
302               /* skip '=' and whitespace */
303               while( i<len && (line[i]=='=' || IS_WHITESPACE(line[i])) )
304                 i++;
305               
306               /* get the value part */
307               j=0;
308               while( i<len )
309                 {
310                   value[j++] = line[i++];
311                 }
312               value[j] = '\0';
313               
314               match_found = 1;
315               
316               if( !strcasecmp(CONF_KEY_DEFINITION, name) )
317                 {
318                   parse_key_definition(value);
319                 }
320               /* enable colors */
321               else if( !strcasecmp(CONF_ENABLE_COLORS, name) )
322                 {
323                   options->enable_colors = str2bool(value);
324                 }
325               /* auto center */
326               else if( !strcasecmp(CONF_AUTO_CENTER, name) )
327                 {
328                   options->auto_center = str2bool(value);
329                 }
330               /* background color */
331               else if( !strcasecmp(CONF_COLOR_BACKGROUND, name) )
332                 {
333                   if( (color=str2color(value)) >= 0 )
334                     options->bg_color = color;
335                 }
336               /* color - top (title) window */
337               else if( !strcasecmp(CONF_COLOR_TITLE, name) )
338                 {
339                   if( (color=str2color(value)) >= 0 )
340                     options->title_color = color;
341                 }
342               /* color - line (title) window */
343               else if( !strcasecmp(CONF_COLOR_LINE, name) )
344                 {
345                   if( (color=str2color(value)) >= 0 )
346                     options->line_color = color;
347                 }
348               /* color - list window */
349               else if( !strcasecmp(CONF_COLOR_LIST, name) )
350                 {
351                   if( (color=str2color(value)) >= 0 )
352                     options->list_color = color;
353                 }
354               /* color - progress bar */
355               else if( !strcasecmp(CONF_COLOR_PROGRESS, name) )
356                 {
357                   if( (color=str2color(value)) >= 0 )
358                     options->progress_color = color;
359                 }
360               /* color - status window */
361               else if( !strcasecmp(CONF_COLOR_STATUS, name) )
362                 {
363                   if( (color=str2color(value)) >= 0 )
364                     options->status_color = color;
365                 }
366               /* color - alerts */
367               else if( !strcasecmp(CONF_COLOR_ALERT, name) )
368                 {
369                   if( (color=str2color(value)) >= 0 )
370                     options->alert_color = color;
371                 }
372               else if( !strcasecmp(CONF_WIDE_CURSOR, name) )
373                 {
374                   options->wide_cursor = str2bool(value);
375                 }
376               else
377                 {
378                   match_found = 0;
379                 }
380               
382               if( !match_found )
383                 fprintf(stderr, 
384                         "Unknown configuration parameter: %s\n", 
385                         name);
386 #ifdef DEBUG
387               printf( "  %s = %s %s\n", 
388                       name, 
389                       value,
390                       match_found ? "" : "- UNKNOWN SETTING!" );
391 #endif
393             }
394         }         
395     }
397   D(printf( "--\n\n" ));
399   if( free_filename )
400     g_free(filename);
401  
402   return 0;
405 int
406 check_user_conf_dir(void)
408   int retval;
409   char *dirname = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
410   
411   if( g_file_test(dirname, G_FILE_TEST_IS_DIR) )
412     {
413       g_free(dirname);
414       return 0;
415     }
416   retval = mkdir(dirname, 0755);
417   g_free(dirname);
418   return retval;
421 char *
422 get_user_key_binding_filename(void)
424   return g_build_filename(g_get_home_dir(), "." PACKAGE, "keys", NULL);
428 int
429 read_configuration(options_t *options)
431   char *filename = NULL;
433   /* check for user configuration ~/.ncmpc/config */
434   filename = g_build_filename(g_get_home_dir(), "." PACKAGE, "config", NULL);
435   if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
436     {
437       g_free(filename);
438       filename = NULL;
439     }
441   /* check for  global configuration SYSCONFDIR/ncmpc/config */
442   if( filename == NULL )
443     {
444       filename = g_build_filename(SYSCONFDIR, PACKAGE, "config", NULL);
445       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
446         {
447           g_free(filename);
448           filename = NULL;
449         }
450     }
452   /* load configuration */
453   if( filename )
454     {
455       read_rc_file(filename, options);
456       g_free(filename);
457       filename = NULL;
458     }
460   /* check for  user key bindings ~/.ncmpc/keys */
461   filename = get_user_key_binding_filename();
462   if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
463     {
464       g_free(filename);
465       filename = NULL;
466     }
468   /* check for  global key bindings SYSCONFDIR/ncmpc/keys */
469   if( filename == NULL )
470     {
471       filename = g_build_filename(SYSCONFDIR, PACKAGE, "keys", NULL);
472       if( !g_file_test(filename, G_FILE_TEST_IS_REGULAR) )
473         {
474           g_free(filename);
475           filename = NULL;
476         }
477     }
479   /* load key bindings */
480   if( filename )
481     {
482       read_rc_file(filename, options);
483       g_free(filename);
484       filename = NULL;
485     }
487   return 0;