]> mj.ucw.cz Git - libucw.git/blob - lib/conf.c
When processing the `-S' option, work on a copy, so that the command
[libucw.git] / lib / conf.c
1 /*
2  *      Sherlock Library -- Reading of configuration files
3  *
4  *      (c) 2001 Robert Spalek <robert@ucw.cz>
5  *      (c) 2003 Martin Mares <mj@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "lib/lib.h"
12 #include "lib/chartype.h"
13 #include "lib/fastbuf.h"
14 #include "lib/pools.h"
15
16 #include "lib/conf.h"
17
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <errno.h>
24
25 #define BUFFER          1024
26 #define MAX_LEVEL       8
27
28 static struct cfitem *cfsection;
29 struct mempool *cfpool;
30
31 byte *cfdeffile = DEFAULT_CONFIG;
32
33 static void CONSTRUCTOR
34 conf_init(void)
35 {
36         cfpool = mp_new(4096);
37 }
38
39 void *
40 cfg_malloc(uns size)
41 {
42         return mp_alloc(cfpool, size);
43 }
44
45 byte *
46 cfg_strdup(byte *s)
47 {
48         return mp_strdup(cfpool, s);
49 }
50
51 void cf_register(struct cfitem *items)
52 {
53         if(items[0].type!=CT_SECTION && items[0].type!=CT_INCOMPLETE_SECTION)
54                 die("cf_register: Invalid section type");
55         items[0].var=cfsection;
56         cfsection=items;
57 }
58
59 int cf_item_count(void)
60 {
61         struct cfitem *sect, *item;
62         int count = 0;
63         for (sect = cfsection; sect; sect = sect->var)
64                 for (item = sect+1; item->type; item++)
65                         count++;
66         return count;
67 }
68
69 struct cfitem *cf_get_item(byte *sect, byte *name)
70 {
71         struct cfitem *item, *section;
72
73         item=cfsection;
74         while(item && strcasecmp(item->name,sect))
75                 item=item->var;
76         if(!item)       /* unknown section */
77                 return NULL;
78         section = item;
79
80         for(item++; item->type && strcasecmp(item->name,name); item++);
81         if (!item->type && section->type == CT_INCOMPLETE_SECTION)
82                 return NULL;
83
84         return item;    /* item->type == 0 if not found */
85 }
86
87 struct unit {
88         uns name;                       /* One-letter name of the unit */
89         uns num, den;                   /* Fraction */
90 };
91
92 static const struct unit units[] = {
93         { 'd', 86400, 1 },
94         { 'h', 3600, 1 },
95         { 'k', 1000, 1 },
96         { 'm', 1000000, 1 },
97         { 'g', 1000000000, 1 },
98         { 'K', 1024, 1 },
99         { 'M', 1048576, 1 },
100         { 'G', 1073741824, 1 },
101         { '%', 1, 100 },
102         { 0, 0, 0 }
103 };
104
105 static const struct unit *cf_lookup_unit(byte *value, byte *end, char **msg)
106 {
107         if (end && *end) {
108                 if (end == value || end[1] || *end >= '0' && *end <= '9')
109                         *msg = "Invalid number";
110                 else {
111                         for (const struct unit *u=units; u->name; u++)
112                                 if (u->name == *end)
113                                         return u;
114                         *msg = "Invalid unit";
115                 }
116         }
117         return NULL;
118 }
119
120 static char cf_rngerr[] = "Number out of range";
121
122 byte *cf_parse_int(byte *value, uns *varp)
123 {
124         char *msg = NULL;
125         const struct unit *u;
126
127         if (!*value)
128                 msg = "Missing number";
129         else {
130                 errno = 0;
131                 char *end;
132                 uns x = strtoul(value, &end, 0);
133                 if (errno == ERANGE)
134                         msg = cf_rngerr;
135                 else if (u = cf_lookup_unit(value, end, &msg)) {
136                         u64 y = (u64)x * u->num;
137                         if (y % u->den)
138                                 msg = "Number is not an integer";
139                         else {
140                                 y /= u->den;
141                                 if (y > 0xffffffff)
142                                         msg = cf_rngerr;
143                                 *varp = y;
144                         }
145                 } else
146                         *varp = x;
147         }
148         return msg;
149 }
150
151 byte *cf_parse_u64(byte *value, u64 *varp)
152 {
153         char *msg = NULL;
154         const struct unit *u;
155
156         if (!*value)
157                 msg = "Missing number";
158         else {
159                 errno = 0;
160                 char *end;
161                 u64 x = strtoull(value, &end, 0);
162                 if (errno == ERANGE)
163                         msg = cf_rngerr;
164                 else if (u = cf_lookup_unit(value, end, &msg)) {
165                         if (x > ~(u64)0 / u->num)
166                                 msg = "Number out of range";
167                         else {
168                                 x *= u->num;
169                                 if (x % u->den)
170                                         msg = "Number is not an integer";
171                                 else
172                                         *varp = x / u->den;
173                         }
174                 } else
175                         *varp = x;
176         }
177         return msg;
178 }
179
180 byte *cf_parse_double(byte *value, double *varp)
181 {
182         char *msg = NULL;
183         const struct unit *u;
184
185         if (!*value)
186                 msg = "Missing number";
187         else {
188                 errno = 0;
189                 char *end;
190                 double x = strtoul(value, &end, 0);
191                 if (errno == ERANGE)
192                         msg = cf_rngerr;
193                 else if (u = cf_lookup_unit(value, end, &msg))
194                         *varp = x * u->num / u->den;
195                 else
196                         *varp = x;
197         }
198         return msg;
199 }
200
201 byte *cf_set_item(byte *sect, byte *name, byte *value)
202 {
203         struct cfitem *item;
204         byte *msg=NULL;
205
206         if (!*sect)
207                 return "Empty section name";
208         item=cf_get_item(sect,name);
209         if(!item)       /* ignore unknown section */
210                 return NULL;
211
212         switch(item->type){
213                 case CT_INT:
214                         msg = cf_parse_int(value, (uns *) item->var);
215                         break;
216                 case CT_STRING:
217                         *((byte **) item->var) = cfg_strdup(value);
218                         break;
219                 case CT_FUNCTION:
220                         msg = ((ci_func) item->var)(item, cfg_strdup(value));
221                         break;
222                 case CT_DOUBLE:
223                         msg = cf_parse_double(value, (double *) item->var);
224                         break;
225                 case CT_U64:
226                         msg = cf_parse_u64(value, (u64 *) item->var);
227                         break;
228                 default:
229                         msg = "Unknown keyword";
230         }
231
232         return msg;
233 }
234
235 static int cf_subread(byte *filename,int level)
236 {
237         int fd;
238         struct fastbuf *b;
239         byte def_section[BUFFER];
240         int line;
241         byte *msg=NULL;
242
243         if(level>=MAX_LEVEL){
244                 log(L_ERROR,"Too many (%d) nested files when reading %s",level,filename);
245                 return 0;
246         }
247                 
248         fd=open(filename,O_RDONLY, 0666);
249         if(fd<0){
250                 log(L_ERROR,"Cannot open configuration file %s: %m",filename);
251                 return 0;
252         }
253         b=bfdopen(fd,4096);
254
255         def_section[0]=0;
256         line=0;
257         while(1){
258                 byte buf[BUFFER];
259                 byte *c;
260
261                 if(!bgets(b,buf,BUFFER))
262                         break;
263                 line++;
264
265                 c=buf+strlen(buf);
266                 while(c>buf && Cspace(c[-1]))
267                         *--c=0;
268                 c=buf;
269                 while(*c && Cspace(*c))
270                         c++;
271                 if(!*c || *c=='#')
272                         continue;
273
274                 if(*c=='['){
275                         strcpy(def_section,c+1);
276                         c=strchr(def_section,']');
277                         if(c){
278                                 *c=0;
279                                 if(c[1]){
280                                         msg="Garbage after ]";
281                                         break;
282                                 }
283                         }else{
284                                 msg="Missing ]";
285                                 break;
286                         }
287
288                 }else{
289                         byte *sect,*name,*value;
290
291                         name=c;
292                         while(*c && !Cspace(*c))
293                                 c++;
294                         while(*c && Cspace(*c))
295                                 *c++=0;
296                         value=c;
297
298                         if(!strcasecmp(name,"include")){
299                                 if(!cf_subread(value,level+1)){
300                                         msg="Included from here";
301                                         break;
302                                 }
303                         }else{
304                                 c=strchr(name,'.');
305                                 if(!c)
306                                         sect=def_section;
307                                 else{
308                                         sect=name;
309                                         *c++=0;
310                                         name=c;
311                                 }
312
313                                 msg=cf_set_item(sect,name,value);
314                         }
315                         if(msg)
316                                 break;
317                 }
318
319         }       /* for every line */
320
321         if(msg)
322                 log(L_ERROR,"%s, line %d: %s",filename,line,msg);
323         bclose(b);
324         return !msg;
325 }
326
327 void cf_read(byte *filename)
328 {
329         if(!cf_subread(filename,0))
330                 die("Reading config file %s failed",filename);
331         cfdeffile = NULL;
332 }
333
334 static void cf_opt_S(void)
335 {
336         byte *sect,*name,*value;
337         byte *c;
338         byte *msg=NULL;
339         byte arg[strlen(optarg)+1];
340
341         strcpy(arg, optarg);
342         name = arg;
343         c=strchr(name,'=');
344         if(!c){
345                 msg="Missing argument";
346                 sect=value="";
347         }else{
348                 *c++=0;
349                 value=c;
350
351                 c=strchr(name,'.');
352                 if(!c)
353                         sect="";
354                 else{
355                         sect=name;
356                         *c++=0;
357                         name=c;
358                 }
359
360                 if (cfdeffile)
361                         cf_read(cfdeffile);
362                 msg=cf_set_item(sect,name,value);
363         }
364         if(msg)
365                 die("Invalid command line argument -S%s.%s=%s: %s",sect,name,value,msg);
366
367 }
368
369 int cf_getopt(int argc,char * const argv[],
370                 const char *shortopts,const struct option *longopts,
371                 int *longindex)
372 {
373         int res;
374         static int other_options;
375
376         do{
377                 res=getopt_long(argc,argv,shortopts,longopts,longindex);
378                 if(res == 'S' || res == 'C') {
379                         if(other_options)
380                                 die("The -S and -C options must precede all other arguments");
381                 }
382                 if(res=='S'){
383                         cf_opt_S();
384                 }else if(res=='C'){
385                         cf_read(optarg);
386                 }else{
387                         /* unhandled option or end of options */
388                         if(cfdeffile)
389                                 cf_read(cfdeffile);
390                         other_options++;
391                         return res;
392                 }
393         }while(1);
394 }