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