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