]> mj.ucw.cz Git - libucw.git/blobdiff - lib/conf.c
Added bit_array_assign(), replaced BIT_ARRAY_ALLOC by functions.
[libucw.git] / lib / conf.c
index b899b55e98bc19b1c75271b4ccba2e5fe0e83887..7f593d8e419d68f6c46288f439a083db7f73d216 100644 (file)
@@ -1,28 +1,38 @@
 /*
- *     Sherlock Library -- Reading configuration files
+ *     UCW Library -- Reading of configuration files
  *
  *     (c) 2001 Robert Spalek <robert@ucw.cz>
+ *     (c) 2003--2005 Martin Mares <mj@ucw.cz>
+ *
+ *     This software may be freely distributed and used according to the terms
+ *     of the GNU Lesser General Public License.
  */
 
 #include "lib/lib.h"
+#include "lib/chartype.h"
+#include "lib/fastbuf.h"
+#include "lib/mempool.h"
+
+#include "lib/conf.h"
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <fcntl.h>
 #include <getopt.h>
-
-#include "lib/chartype.h"
-#include "lib/fastbuf.h"
-#include "lib/pools.h"
-
-#include "lib/conf.h"
+#include <errno.h>
 
 #define        BUFFER          1024
 #define        MAX_LEVEL       8
 
 static struct cfitem *cfsection;
-static struct mempool *cfpool;
+struct mempool *cfpool;
+
+#ifndef DEFAULT_CONFIG
+#define DEFAULT_CONFIG NULL
+#endif
+
+byte *cfdeffile = DEFAULT_CONFIG;
 
 static void CONSTRUCTOR
 conf_init(void)
@@ -36,69 +46,207 @@ cfg_malloc(uns size)
        return mp_alloc(cfpool, size);
 }
 
+void *
+cfg_malloc_zero(uns size)
+{
+       return mp_alloc_zero(cfpool, size);
+}
+
 byte *
-cfg_stralloc(byte *s)
+cfg_strdup(byte *s)
 {
-  uns l = strlen(s);
-  byte *k = cfg_malloc(l + 1);
-  strcpy(k, s);
-  return k;
+       return mp_strdup(cfpool, s);
+}
+
+byte *
+cfg_printf(char *fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+       byte *res = mp_vprintf(cfpool, fmt, args);
+       va_end(args);
+       return res;
 }
 
 void cf_register(struct cfitem *items)
 {
-       if(items[0].type!=CT_SECTION)
-               die("Invalid configuration section, first item must be of type CT_SECTION");
+       if(items[0].type!=CT_SECTION && items[0].type!=CT_INCOMPLETE_SECTION)
+               die("cf_register: Invalid section type");
        items[0].var=cfsection;
        cfsection=items;
 }
 
+int cf_item_count(void)
+{
+       struct cfitem *sect, *item;
+       int count = 0;
+       for (sect = cfsection; sect; sect = sect->var)
+               for (item = sect+1; item->type; item++)
+                       count++;
+       return count;
+}
+
 struct cfitem *cf_get_item(byte *sect, byte *name)
 {
-       struct cfitem *item;
+       struct cfitem *item, *section;
 
        item=cfsection;
        while(item && strcasecmp(item->name,sect))
                item=item->var;
        if(!item)       /* unknown section */
                return NULL;
+       section = item;
 
        for(item++; item->type && strcasecmp(item->name,name); item++);
+       if (!item->type && section->type == CT_INCOMPLETE_SECTION)
+               return NULL;
 
        return item;    /* item->type == 0 if not found */
 }
 
+struct unit {
+       uns name;                       /* One-letter name of the unit */
+       uns num, den;                   /* Fraction */
+};
+
+static const struct unit units[] = {
+       { 'd', 86400, 1 },
+       { 'h', 3600, 1 },
+       { 'k', 1000, 1 },
+       { 'm', 1000000, 1 },
+       { 'g', 1000000000, 1 },
+       { 'K', 1024, 1 },
+       { 'M', 1048576, 1 },
+       { 'G', 1073741824, 1 },
+       { '%', 1, 100 },
+       { 0, 0, 0 }
+};
+
+static const struct unit *cf_lookup_unit(byte *value, byte *end, char **msg)
+{
+       if (end && *end) {
+               if (end == value || end[1] || *end >= '0' && *end <= '9')
+                       *msg = "Invalid number";
+               else {
+                       for (const struct unit *u=units; u->name; u++)
+                               if (u->name == *end)
+                                       return u;
+                       *msg = "Invalid unit";
+               }
+       }
+       return NULL;
+}
+
+static char cf_rngerr[] = "Number out of range";
+
+byte *cf_parse_int(byte *value, uns *varp)
+{
+       char *msg = NULL;
+       const struct unit *u;
+
+       if (!*value)
+               msg = "Missing number";
+       else {
+               errno = 0;
+               char *end;
+               uns x = strtoul(value, &end, 0);
+               if (errno == ERANGE)
+                       msg = cf_rngerr;
+               else if (u = cf_lookup_unit(value, end, &msg)) {
+                       u64 y = (u64)x * u->num;
+                       if (y % u->den)
+                               msg = "Number is not an integer";
+                       else {
+                               y /= u->den;
+                               if (y > 0xffffffff)
+                                       msg = cf_rngerr;
+                               *varp = y;
+                       }
+               } else
+                       *varp = x;
+       }
+       return msg;
+}
+
+byte *cf_parse_u64(byte *value, u64 *varp)
+{
+       char *msg = NULL;
+       const struct unit *u;
+
+       if (!*value)
+               msg = "Missing number";
+       else {
+               errno = 0;
+               char *end;
+               u64 x = strtoull(value, &end, 0);
+               if (errno == ERANGE)
+                       msg = cf_rngerr;
+               else if (u = cf_lookup_unit(value, end, &msg)) {
+                       if (x > ~(u64)0 / u->num)
+                               msg = "Number out of range";
+                       else {
+                               x *= u->num;
+                               if (x % u->den)
+                                       msg = "Number is not an integer";
+                               else
+                                       *varp = x / u->den;
+                       }
+               } else
+                       *varp = x;
+       }
+       return msg;
+}
+
+byte *cf_parse_double(byte *value, double *varp)
+{
+       char *msg = NULL;
+       const struct unit *u;
+
+       if (!*value)
+               msg = "Missing number";
+       else {
+               errno = 0;
+               char *end;
+               double x = strtoul(value, &end, 0);
+               if (errno == ERANGE)
+                       msg = cf_rngerr;
+               else if (u = cf_lookup_unit(value, end, &msg))
+                       *varp = x * u->num / u->den;
+               else
+                       *varp = x;
+       }
+       return msg;
+}
+
 byte *cf_set_item(byte *sect, byte *name, byte *value)
 {
        struct cfitem *item;
        byte *msg=NULL;
 
+       if (!*sect)
+               return "Empty section name";
        item=cf_get_item(sect,name);
        if(!item)       /* ignore unknown section */
                return NULL;
 
        switch(item->type){
                case CT_INT:
-                       {
-                               char *end;
-                               if(!*value)
-                                       msg="Missing number";
-                               else{
-                                       *((uns *) item->var) = strtoul(value, &end, 0);
-                                       if (end && *end)
-                                               msg = "Invalid number";
-                               }
-                               break;
-                       }
+                       msg = cf_parse_int(value, (uns *) item->var);
+                       break;
                case CT_STRING:
-                       *((byte **) item->var) = cfg_stralloc(value);
+                       *((byte **) item->var) = cfg_strdup(value);
                        break;
                case CT_FUNCTION:
-                       msg = ((ci_func) item->var)(item, cfg_stralloc(value));
+                       msg = ((ci_func) item->var)(item, cfg_strdup(value));
+                       break;
+               case CT_DOUBLE:
+                       msg = cf_parse_double(value, (double *) item->var);
+                       break;
+               case CT_U64:
+                       msg = cf_parse_u64(value, (u64 *) item->var);
                        break;
                default:
                        msg = "Unknown keyword";
-                       break;
        }
 
        return msg;
@@ -200,6 +348,42 @@ void cf_read(byte *filename)
 {
        if(!cf_subread(filename,0))
                die("Reading config file %s failed",filename);
+       cfdeffile = NULL;
+}
+
+static void cf_opt_S(void)
+{
+       byte *sect,*name,*value;
+       byte *c;
+       byte *msg=NULL;
+       byte arg[strlen(optarg)+1];
+
+       strcpy(arg, optarg);
+       name = arg;
+       c=strchr(name,'=');
+       if(!c){
+               msg="Missing argument";
+               sect=value="";
+       }else{
+               *c++=0;
+               value=c;
+
+               c=strchr(name,'.');
+               if(!c)
+                       sect="";
+               else{
+                       sect=name;
+                       *c++=0;
+                       name=c;
+               }
+
+               if (cfdeffile)
+                       cf_read(cfdeffile);
+               msg=cf_set_item(sect,name,value);
+       }
+       if(msg)
+               die("Invalid command line argument -S%s.%s=%s: %s",sect,name,value,msg);
+
 }
 
 int cf_getopt(int argc,char * const argv[],
@@ -207,42 +391,24 @@ int cf_getopt(int argc,char * const argv[],
                int *longindex)
 {
        int res;
+       static int other_options;
 
        do{
                res=getopt_long(argc,argv,shortopts,longopts,longindex);
+               if(res == 'S' || res == 'C') {
+                       if(other_options)
+                               die("The -S and -C options must precede all other arguments");
+               }
                if(res=='S'){
-                       byte *sect,*name,*value;
-                       byte *c;
-                       byte *msg=NULL;
-
-                       name=optarg;
-                       c=strchr(name,'=');
-                       if(!c){
-                               msg="Missing argument";
-                               sect=value="";
-                       }else{
-                               *c++=0;
-                               value=c;
-
-                               c=strchr(name,'.');
-                               if(!c)
-                                       sect="";
-                               else{
-                                       sect=name;
-                                       *c++=0;
-                                       name=c;
-                               }
-
-                               msg=cf_set_item(sect,name,value);
-                       }
-                       if(msg)
-                               die("Invalid command line argument %s.%s=%s: %s",sect,name,value,msg);
-
+                       cf_opt_S();
                }else if(res=='C'){
                        cf_read(optarg);
-               }else{  /* unhandled option */
+               }else{
+                       /* unhandled option or end of options */
+                       if(cfdeffile)
+                               cf_read(cfdeffile);
+                       other_options++;
                        return res;
                }
        }while(1);
 }
-