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