FW: [REQ] generic config struct for parameter value lists
"fred" <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Message-ID | <000501c76a76$d4896dd0$35e0110a@FRED42> |
-----Original Message----- From: fred [mailto:[email protected]] Sent: 20 March 2007 09:23 To: 'Stipe Tolj' Subject: RE: [REQ] generic config struct for parameter value lists I needed such a feature, here is an example: Here the LHS is a list of reg expressions, match and the substitution expression mt-dest-addr-regex-subst = "^1([1-9][0-9]{9})$" "$1" = "^0([2-9][0-9]{9})$" "$1" mo-source-addr-regex-subst = "(^[2-9][0-9]{9})$" "1$1" = "^0([2-9][0-9]{9})$" "1$1" All the stuff is in cfg.c, so I'll simply attach my version.... -----Original Message----- From: Stipe Tolj [mailto:[email protected]] Sent: 20 March 2007 03:27 To: Kannel Development list Subject: [REQ] generic config struct for parameter value lists Hi list, I have some good input on extending Kannel's config handling abilities: Currently we have several needs for a config value list, some examples: group = smsc ... accepted-smsc-id = "A;B;C" group = sms-service ... aliases = "key2;key3;key4" group = smsbox-route ... shortcode = "1111;2222;3333" so, all of these are either of type word-list or id-list, semantically it's a semicolon seperated list. For some parts we have defined foobar-regex config directives to handle PCRE/regex for a more simple definition. The idea now is to create a more generic construct for this, which handles 3 (maybe more?) different ways for any "list-type": foobar = "A;B;C" foobar-url = "http://somewhere/foobar-list" foobar-regex = "<the regex expression>" so users can *CHOOSE* which way they prefer. And we handle the processing of the list "transparently" to the logic that requires the list from the config sections. I like this concept of abstraction here. Since we would encapsulate the plain, -url and -regex handling to the scope of gwlib/cfg.{ch] and the other calling modules simply pull for the list. Thoughts? ------------------------------------------------------------------- Kölner Landstrasse 419 40589 Düsseldorf, NRW, Germany tolj.org system architecture Kannel Software Foundation (KSF) http://www.tolj.org/ http://www.kannel.org/ mailto:st_{at}_tolj.org mailto:stolj_{at}_kannel.org -------------------------------------------------------------------
cfg.c
(text/plain, 23.1 KB)
/* ==================================================================== * The Kannel Software License, Version 1.0 * * Copyright (c) 2001-2005 Kannel Group * Copyright (c) 1998-2001 WapIT Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Kannel Group (http://www.kannel.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Kannel" and "Kannel Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact [email protected]. * * 5. Products derived from this software may not be called "Kannel", * nor may "Kannel" appear in their name, without prior written * permission of the Kannel Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Kannel Group. For more information on * the Kannel Group, please see <http://www.kannel.org/>. * * Portions of this software are based upon software originally written at * WapIT Ltd., Helsinki, Finland for the Kannel project. */ /* * cfg.c - configuration file handling * * Lars Wirzenius */ #include "gwlib/gwlib.h" /* for include dir */ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <dirent.h> struct CfgGroup { Octstr *name; Dict *vars; Octstr *configfile; long line; }; static CfgGroup *create_group(void) { CfgGroup *grp; grp = gw_malloc(sizeof(*grp)); grp->name = NULL; grp->vars = dict_create(64, octstr_destroy_item); grp->configfile = NULL; grp->line = 0; return grp; } static void destroy_group(void *arg) { CfgGroup *grp; List *names; Octstr *name=0; if (arg != NULL) { grp = arg; names = dict_keys(grp->vars); while ((name = gwlist_extract_first(names)) != NULL) { if(cfg_type_(name)==TYP_OCTSTRARY) { cfg_destroy_list(grp,name); } octstr_destroy(name); } octstr_destroy(grp->name); octstr_destroy(grp->configfile); dict_destroy(grp->vars); gw_free(grp); } } struct CfgLoc { Octstr *filename; long line_no; Octstr *line; }; static CfgLoc *cfgloc_create(Octstr *filename) { CfgLoc *cfgloc; cfgloc = gw_malloc(sizeof(*cfgloc)); cfgloc->filename = octstr_duplicate(filename); cfgloc->line_no = 0; cfgloc->line = NULL; return cfgloc; } static void cfgloc_destroy(CfgLoc *cfgloc) { if (cfgloc != NULL) { octstr_destroy(cfgloc->filename); octstr_destroy(cfgloc->line); gw_free(cfgloc); } } static void destroy_group_list(void *arg) { gwlist_destroy(arg, destroy_group); } static void set_group_name(CfgGroup *grp, Octstr *name) { octstr_destroy(grp->name); grp->name = octstr_duplicate(name); } struct Cfg { Octstr *filename; Dict *single_groups; Dict *multi_groups; }; static int is_allowed_in_group(Octstr *group, Octstr *variable) { Octstr *groupstr; groupstr = octstr_imm("group"); #define OCTSTR(name) \ if (octstr_compare(octstr_imm(#name), variable) == 0) \ return 1; #define OCTSTRARY(name) \ if (octstr_compare(octstr_imm(#name), variable) == 0) \ return 1; #define SINGLE_GROUP(name, fields) \ if (octstr_compare(octstr_imm(#name), group) == 0) { \ if (octstr_compare(groupstr, variable) == 0) \ return 1; \ fields \ return 0; \ } #define MULTI_GROUP(name, fields) \ if (octstr_compare(octstr_imm(#name), group) == 0) { \ if (octstr_compare(groupstr, variable) == 0) \ return 1; \ fields \ return 0; \ } #include "cfg.def" return 0; } static int is_single_group(Octstr *query) { #define OCTSTR(name) #define OCTSTRARY(name) #define SINGLE_GROUP(name, fields) \ if (octstr_compare(octstr_imm(#name), query) == 0) \ return 1; #define MULTI_GROUP(name, fields) \ if (octstr_compare(octstr_imm(#name), query) == 0) \ return 0; #include "cfg.def" return 0; } static int add_group(Cfg *cfg, CfgGroup *grp) { Octstr *groupname; Octstr *name; List *names; List *list; groupname = cfg_get(grp, octstr_imm("group")); if (groupname == NULL) { error(0, "Group does not contain variable 'group'."); return -1; } set_group_name(grp, groupname); names = dict_keys(grp->vars); while ((name = gwlist_extract_first(names)) != NULL) { if (!is_allowed_in_group(groupname, name)) { error(0, "Group '%s' may not contain field '%s'.", octstr_get_cstr(groupname), octstr_get_cstr(name)); octstr_destroy(name); octstr_destroy(groupname); gwlist_destroy(names, octstr_destroy_item); return -1; } octstr_destroy(name); } gwlist_destroy(names, NULL); if (is_single_group(groupname)) dict_put(cfg->single_groups, groupname, grp); else { list = dict_get(cfg->multi_groups, groupname); if (list == NULL) { list = gwlist_create(); dict_put(cfg->multi_groups, groupname, list); } gwlist_append(list, grp); } octstr_destroy(groupname); return 0; } Cfg *cfg_create(Octstr *filename) { Cfg *cfg; cfg = gw_malloc(sizeof(*cfg)); cfg->filename = octstr_duplicate(filename); cfg->single_groups = dict_create(64, destroy_group); cfg->multi_groups = dict_create(64, destroy_group_list); return cfg; } void cfg_destroy(Cfg *cfg) { if (cfg != NULL) { octstr_destroy(cfg->filename); dict_destroy(cfg->single_groups); dict_destroy(cfg->multi_groups); gw_free(cfg); } } static void parse_value(Octstr *value) { Octstr *temp; long len; int c; octstr_strip_blanks(value); len = octstr_len(value); if (octstr_get_char(value, 0) != '"' || octstr_get_char(value, len - 1) != '"') return; octstr_delete(value, len - 1, 1); octstr_delete(value, 0, 1); temp = octstr_duplicate(value); octstr_truncate(value, 0); while (octstr_len(temp) > 0) { c = octstr_get_char(temp, 0); octstr_delete(temp, 0, 1); if (c != '\\' || octstr_len(temp) == 0) octstr_append_char(value, c); else { c = octstr_get_char(temp, 0); octstr_delete(temp, 0, 1); switch (c) { case '\\': case '"': octstr_append_char(value, c); break; default: octstr_append_char(value, '\\'); octstr_append_char(value, c); break; } } } octstr_destroy(temp); } static List *expand_file(Octstr *file, int forward) { Octstr *os; Octstr *line; List *lines; List *expand; long lineno; CfgLoc *loc; os = octstr_read_file(octstr_get_cstr(file)); if (os == NULL) return NULL; lines = octstr_split(os, octstr_imm("\n")); lineno = 0; expand = gwlist_create(); while ((line = gwlist_extract_first(lines)) != NULL) { ++lineno; loc = cfgloc_create(file); loc->line_no = lineno; loc->line = line; if (forward) gwlist_append(expand, loc); else gwlist_insert(expand, 0, loc); } /* * add newline at each end of included files to avoid * concatenating different groups by mistake */ if (lineno > 0) { loc = cfgloc_create(file); loc->line_no = lineno; loc->line = octstr_create("\n"); if (forward) gwlist_append(expand, loc); else gwlist_insert(expand, 0, loc); } gwlist_destroy(lines, octstr_destroy_item); octstr_destroy(os); return expand; } int cfg_read(Cfg *cfg) { CfgLoc *loc; CfgLoc *loc_inc; List *lines; List *expand; List *stack; Octstr *name; Octstr *value; Octstr *filename; CfgGroup *grp; List *valuelist=0; long equals; long lineno; long error_lineno; loc = loc_inc = NULL; /* * expand initial main config file and add it to the recursion * stack to protect against cycling */ if ((lines = expand_file(cfg->filename, 1)) == NULL) { panic(0, "Failed to load main configuration file `%s'. Aborting!", octstr_get_cstr(cfg->filename)); } stack = gwlist_create(); gwlist_insert(stack, 0, octstr_duplicate(cfg->filename)); grp = NULL; lineno = 0; error_lineno = 0; while (error_lineno == 0 && (loc = gwlist_extract_first(lines)) != NULL) { octstr_strip_blanks(loc->line); if (octstr_len(loc->line) == 0) { if (grp != NULL && add_group(cfg, grp) == -1) { error_lineno = loc->line_no; destroy_group(grp); } grp = NULL; } else if (octstr_get_char(loc->line, 0) != '#') { equals = octstr_search_char(loc->line, '=', 0); if (equals == -1) { error(0, "An equals sign ('=') is missing on line %ld of file %s.", loc->line_no, octstr_get_cstr(loc->filename)); error_lineno = loc->line_no; } else /* * check for special config directives, like include or conditional * directives here */ if (octstr_search(loc->line, octstr_imm("include"), 0) != -1) { filename = octstr_copy(loc->line, equals + 1, octstr_len(loc->line)); parse_value(filename); /* check if we are cycling */ if (gwlist_search(stack, filename, octstr_item_match) != NULL) { panic(0, "Recursive include for config file `%s' detected " "(on line %ld of file %s).", octstr_get_cstr(filename), loc->line_no, octstr_get_cstr(loc->filename)); } else { List *files = gwlist_create(); Octstr *file; struct stat filestat; /* check if included file is a directory */ lstat(octstr_get_cstr(filename), &filestat); /* * is a directory, create a list with files of * this directory and load all as part of the * whole configuration. */ if (S_ISDIR(filestat.st_mode)) { DIR *dh; struct dirent *diritem; debug("gwlib.cfg", 0, "Loading include dir `%s' " "(on line %ld of file %s).", octstr_get_cstr(filename), loc->line_no, octstr_get_cstr(loc->filename)); dh = opendir(octstr_get_cstr(filename)); while ((diritem = readdir(dh))) { Octstr *fileitem; fileitem = octstr_duplicate(filename); octstr_append(fileitem, octstr_create("/")); octstr_append(fileitem, octstr_create(diritem->d_name)); lstat(octstr_get_cstr(fileitem), &filestat); if (!S_ISDIR(filestat.st_mode)) { gwlist_insert(files, 0, fileitem); } } closedir(dh); } /* is a file, create a list with it */ else { gwlist_insert(files, 0, octstr_duplicate(filename)); } /* include files */ while ((file = gwlist_extract_first(files)) != NULL) { gwlist_insert(stack, 0, octstr_duplicate(file)); debug("gwlib.cfg", 0, "Loading include file `%s' (on line %ld of file %s).", octstr_get_cstr(file), loc->line_no, octstr_get_cstr(loc->filename)); /* * expand the given include file and add it to the current * processed main while loop */ if ((expand = expand_file(file, 0)) != NULL) { while ((loc_inc = gwlist_extract_first(expand)) != NULL) gwlist_insert(lines, 0, loc_inc); } else { panic(0, "Failed to load whole configuration. Aborting!"); } gwlist_destroy(expand, NULL); cfgloc_destroy(loc_inc); octstr_destroy(file); } gwlist_destroy(files, octstr_destroy_item); } octstr_destroy(filename); } /* * this is a "normal" line, so process it accodingly */ else { int cfg_type; if (grp == NULL) grp = create_group(); if (grp->configfile != NULL) { octstr_destroy(grp->configfile); grp->configfile=NULL; } grp->configfile = octstr_duplicate(cfg->filename); name = octstr_copy(loc->line, 0, equals); octstr_strip_blanks(name); value = octstr_copy(loc->line, equals + 1, octstr_len(loc->line)); cfg_type = cfg_type_(name); if(octstr_len(name) > 0 ) // a line starting with a name stops/starts an array { if(cfg_type == TYP_OCTSTRARY ) // consecutive lines starting with '=' are part of this list { valuelist = gwlist_create(); cfg_set_list(grp, name, valuelist); } else if(valuelist) { valuelist=0; } } if(valuelist) { gwlist_insert(valuelist, 0, octstr_duplicate(value)); } else { if(octstr_len(name) ==0) { error(0, "line is not an array setting: line %ld file %s.", loc->line_no, octstr_get_cstr(loc->filename)); error_lineno = loc->line_no; break; } parse_value(value); cfg_set(grp, name, value); } octstr_destroy(name); octstr_destroy(value); } } cfgloc_destroy(loc); } if (grp != NULL && add_group(cfg, grp) == -1) { error_lineno = 1; destroy_group(grp); } gwlist_destroy(lines, NULL); gwlist_destroy(stack, octstr_destroy_item); if (error_lineno != 0) { error(0, "Error found on line %ld of file `%s'.", error_lineno, octstr_get_cstr(cfg->filename)); return -1; } return 0; } CfgGroup *cfg_get_single_group(Cfg *cfg, Octstr *name) { return dict_get(cfg->single_groups, name); } List *cfg_get_multi_group(Cfg *cfg, Octstr *name) { List *list, *copy; long i; list = dict_get(cfg->multi_groups, name); if (list == NULL) return NULL; copy = gwlist_create(); for (i = 0; i < gwlist_len(list); ++i) gwlist_append(copy, gwlist_get(list, i)); return copy; } Octstr *cfg_get_group_name(CfgGroup *grp) { return octstr_duplicate(grp->name); } Octstr *cfg_get_configfile(CfgGroup *grp) { return octstr_duplicate(grp->configfile); } Octstr *cfg_get_real(CfgGroup *grp, Octstr *varname, const char *file, long line, const char *func) { Octstr *os; if(grp == NULL) panic(0, "Trying to fetch variable `%s' in non-existing group", octstr_get_cstr(varname)); if (grp->name != NULL && !is_allowed_in_group(grp->name, varname)) panic(0, "Trying to fetch variable `%s' in group `%s', not allowed.", octstr_get_cstr(varname), octstr_get_cstr(grp->name)); os = dict_get(grp->vars, varname); if (os == NULL) return NULL; return gw_claim_area_for(octstr_duplicate(os), file, line, func); } int cfg_get_integer(long *n, CfgGroup *grp, Octstr *varname) { Octstr *os; int ret; os = cfg_get(grp, varname); if (os == NULL) return -1; if (octstr_parse_long(n, os, 0, 0) == -1) ret = -1; else ret = 0; octstr_destroy(os); return ret; } int cfg_get_longlong(longlong_t *n, CfgGroup *grp, Octstr *varname) { Octstr *os; int ret=0; os = cfg_get(grp, varname); if (os == NULL) return -1; if (octstr_parse_longlongnumber(n, os, 0) == -1) if(errno == EINVAL || errno == ERANGE) ret = -1; octstr_destroy(os); return ret; } int cfg_get_bool(int *n, CfgGroup *grp, Octstr *varname) { Octstr *os; os = cfg_get(grp, varname); if (os == NULL) { *n = 0; return -1; } if (octstr_case_compare(os, octstr_imm("true")) == 0 || octstr_case_compare(os, octstr_imm("yes")) == 0 || octstr_case_compare(os, octstr_imm("on")) == 0 || octstr_case_compare(os, octstr_imm("1")) == 0) { *n = 1; } else if (octstr_case_compare(os, octstr_imm("false")) == 0 || octstr_case_compare(os, octstr_imm("no")) == 0 || octstr_case_compare(os, octstr_imm("off")) == 0 || octstr_case_compare(os, octstr_imm("0")) == 0) { *n = 0; } else { *n = 1; warning(0, "bool variable set to strange value, assuming 'true'"); } octstr_destroy(os); return 0; } List *cfg_get_list(CfgGroup *grp, Octstr *varname) { List *list; if(grp == NULL) panic(0, "Trying to fetch variable `%s' in non-existing group", octstr_get_cstr(varname)); if (grp->name != NULL && !is_allowed_in_group(grp->name, varname)) panic(0, "Trying to fetch variable `%s' in group `%s', not allowed.", octstr_get_cstr(varname), octstr_get_cstr(grp->name)); list = dict_get(grp->vars, varname); if (list == NULL) return NULL; return list; } /** * read config lines like * source-addr-regex-subst = "(^[2-9][0-9]{9})" "1$1" * = "(^0[2-9][0-9]{9})" "1$1" * * */ List *cfg_get_lineslist(CfgGroup *grp, Octstr *varname) { Octstr *os; List *list; os = cfg_get(grp, varname); if (os == NULL) return NULL; list = octstr_split_words(os); octstr_destroy(os); return list; } void cfg_set(CfgGroup *grp, Octstr *varname, Octstr *value) { dict_put(grp->vars, varname, octstr_duplicate(value)); } void cfg_set_list(CfgGroup *grp, Octstr *varname, List *value) { List *list; list = dict_get(grp->vars, varname); if(list) { gwlist_destroy(list,octstr_destroy_item); dict_remove(grp->vars, varname); } dict_put(grp->vars, varname, value); } void cfg_destroy_list(CfgGroup *grp, Octstr *name) { List *valuelist=0; valuelist = cfg_get_list(grp, name); gwlist_destroy(valuelist,octstr_destroy_item); dict_remove(grp->vars, name); } void grp_dump(CfgGroup *grp) { List *names; Octstr *name; Octstr *value; List *valuelist=0; long listlen; long i; if (grp->name == NULL) debug("gwlib.cfg", 0, " dumping group (name not set):"); else debug("gwlib.cfg", 0, " dumping group (%s):", octstr_get_cstr(grp->name)); names = dict_keys(grp->vars); while ((name = gwlist_extract_first(names)) != NULL) { if(cfg_type_(name)==TYP_OCTSTRARY) { valuelist = cfg_get_list(grp, name); listlen = gwlist_len(valuelist); for(i=0;i<listlen;i++) { value = gwlist_get(valuelist,i); debug("gwlib.cfg", 0, " <%s> = <%s>", octstr_get_cstr(name), octstr_get_cstr(value)); } octstr_destroy(name); } else { value = cfg_get(grp, name); debug("gwlib.cfg", 0, " <%s> = <%s>", octstr_get_cstr(name), octstr_get_cstr(value)); octstr_destroy(value); octstr_destroy(name); } } gwlist_destroy(names, NULL); } void cfg_dump(Cfg *cfg) { CfgGroup *grp; List *list; List *names; Octstr *name; debug("gwlib.cfg", 0, "Dumping Cfg %p", (void *) cfg); debug("gwlib.cfg", 0, " filename = <%s>", octstr_get_cstr(cfg->filename)); names = dict_keys(cfg->single_groups); while ((name = gwlist_extract_first(names)) != NULL) { grp = cfg_get_single_group(cfg, name); if (grp != NULL) grp_dump(grp); octstr_destroy(name); } gwlist_destroy(names, NULL); names = dict_keys(cfg->multi_groups); while ((name = gwlist_extract_first(names)) != NULL) { list = cfg_get_multi_group(cfg, name); while ((grp = gwlist_extract_first(list)) != NULL) grp_dump(grp); gwlist_destroy(list, NULL); octstr_destroy(name); } gwlist_destroy(names, NULL); debug("gwlib.cfg", 0, "Dump ends."); } void cfg_dump_all(void) { #define OCTSTR(name) \ printf("%s = <please consult user doc>\n", #name); #define OCTSTRARY(name) \ printf("OCTSTRARY %s <please consult user doc>\n", #name); #define SINGLE_GROUP(name, fields) \ printf("#\n# Single Group\n#\n"); \ printf("group = %s\n", #name); \ fields; \ printf("\n\n"); #define MULTI_GROUP(name, fields) \ printf("#\n# Multi Group\n#\n"); \ printf("group = %s\n", #name); \ fields; \ printf("\n\n"); #include "cfg.def" } int cfg_type_(Octstr *configname) { #define OCTSTR(name) \ if (octstr_compare(octstr_imm(#name), configname) == 0) \ return TYP_OCTSTR; #define OCTSTRARY(name) \ if (octstr_compare(octstr_imm(#name), configname) == 0) \ return TYP_OCTSTRARY; #define SINGLE_GROUP(name, fields) fields #define MULTI_GROUP(name, fields) fields #include "cfg.def" return TYP_NONE; }