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