diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-06-21 10:43:50 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-06-21 10:43:50 +0300 |
commit | 7d63e9e21b74f3b0ca3a87714e19ef1bbfeb7fbf (patch) | |
tree | 15b0b4a18fb91cc4b5146b9fe96070886facdcfb | |
parent | b21233c538ebb235a780e068641263801730339d (diff) | |
download | smap-7d63e9e21b74f3b0ca3a87714e19ef1bbfeb7fbf.tar.gz smap-7d63e9e21b74f3b0ca3a87714e19ef1bbfeb7fbf.tar.bz2 |
Implement argument transformations.
Argument transformations allow to change map and/or
key in the input query before actually processing it.
They are implemented at the module level, via additional
method in smap_module structure.
* include/smap/module.h (SMAP_MODULE_VERSION): Define to 2.
(SMAP_CAPA_DEFAULT): Change constant.
(SMAP_CAPA_XFORM): New define.
(smap_module)<smap_xform>: New member.
* src/module.c (_load_module): Fix consistency checking.
* src/query.c: New condition "key [op] value".
New target "transform dbname".
(query_cond_type): New constant query_cond_key.
(map_comparison): Rename to comparison. All uses changed.
(map_cond): Rename to comp_cond. All uses changed.
(dispatch_rule)<xform>: New member.
(query_tok)<T_TRANSFORM, T_KEY>: New tokens.
(query_kwtab): New keywords: key, transform.
(parse_dispatch_map): Rename to parse_dispatch_comp; change
signature. All uses changed.
(parse_subcond): Handle T_KEY token.
(parse_complex_dispatch): Handle T_KEY and T_TRANSFORM.
(rule_fixup): New function.
(link_dispatch_rules): Use rule_fixup.
(find_dispatch_rule): Second argument defines where to
start searching.
(dispatch_query): Handle transformations. Add checking of return
values.
* modules/guile/guile.c (guile_proc_ind)<xform_proc>: New index.
(guile_proc_name): New proc "xform".
(guile_init_db): Bail out if neither query nor xform is defined.
(guile_xform): New function.
(module): Add guile_xform.
* modules/guile/getpw.scm (smap-xform): Implement an example
transformation.
* modules/echo/echo.c (module): Update.
* modules/mailutils/mailutils.c: Likewise.
-rw-r--r-- | include/smap/module.h | 15 | ||||
-rw-r--r-- | modules/echo/echo.c | 5 | ||||
-rw-r--r-- | modules/guile/getpw.scm | 7 | ||||
-rw-r--r-- | modules/guile/guile.c | 55 | ||||
-rw-r--r-- | modules/mailutils/mailutils.c | 5 | ||||
-rw-r--r-- | src/module.c | 24 | ||||
-rw-r--r-- | src/query.c | 232 |
7 files changed, 251 insertions, 92 deletions
diff --git a/include/smap/module.h b/include/smap/module.h index 120bf6e..27befad 100644 --- a/include/smap/module.h +++ b/include/smap/module.h @@ -20,9 +20,11 @@ #define __smap_s_cat3__(a,b,c) a ## b ## c #define SMAP_EXPORT(module,name) __smap_s_cat3__(module,_LTX_,name) -#define SMAP_MODULE_VERSION 1 +#define SMAP_MODULE_VERSION 2 #define SMAP_CAPA_NONE 0 -#define SMAP_CAPA_DEFAULT SMAP_CAPA_NONE +#define SMAP_CAPA_QUERY 0x0001 +#define SMAP_CAPA_XFORM 0x0002 +#define SMAP_CAPA_DEFAULT SMAP_CAPA_QUERY typedef struct smap_database *smap_database_t; @@ -37,9 +39,10 @@ struct smap_conninfo { struct smap_module { unsigned smap_version; - unsigned smap_capabilities; /* Unused so far */ + unsigned smap_capabilities; int (*smap_init)(int argc, char **argv); - smap_database_t (*smap_init_db)(const char *dbid, int argc, char **argv); + smap_database_t (*smap_init_db)(const char *dbid, + int argc, char **argv); int (*smap_free_db)(smap_database_t dbp); int (*smap_open) (smap_database_t hp); int (*smap_close) (smap_database_t hp); @@ -47,6 +50,10 @@ struct smap_module { smap_stream_t ostr, const char *map, const char *key, struct smap_conninfo const *conninfo); + int (*smap_xform)(smap_database_t dbp, + const char *map, const char *key, + struct smap_conninfo const *conninfo, + char **newmap, char **newkey); }; #endif diff --git a/modules/echo/echo.c b/modules/echo/echo.c index ad1d770..57dbbce 100644 --- a/modules/echo/echo.c +++ b/modules/echo/echo.c @@ -69,11 +69,12 @@ echo_query(smap_database_t dbp, struct smap_module SMAP_EXPORT(echo, module) = { SMAP_MODULE_VERSION, - SMAP_CAPA_NONE, + SMAP_CAPA_DEFAULT, NULL, /* smap_init */ echo_init_db, echo_free_db, NULL, /* smap_open */ NULL, /* smap_close */ - echo_query + echo_query, + NULL /* smap_xform */ }; diff --git a/modules/guile/getpw.scm b/modules/guile/getpw.scm index 2fd4a44..88bd724 100644 --- a/modules/guile/getpw.scm +++ b/modules/guile/getpw.scm @@ -107,12 +107,19 @@ "NOTFOUND"))) (newline))) +(define-public (smap-xform handle map arg . rest) + (let ((arg-parts (string-split arg #\@))) + (if (null? (cdr arg-parts)) + #f + (cons #f (car arg-parts))))) + ;;; Module initialization function returns an associative list ;;; of methods implemented by the module. Each method is represented ;;; by a cons. (define (init dbname) (list (cons "query" smap-query) + (cons "xform" smap-xform) (cons "open" smap-open) (cons "close" smap-close))) diff --git a/modules/guile/guile.c b/modules/guile/guile.c index 4853ee5..510cd7d 100644 --- a/modules/guile/guile.c +++ b/modules/guile/guile.c @@ -328,16 +328,18 @@ enum guile_proc_ind { done_proc, /* FIXME: this one too */ open_proc, close_proc, - query_proc + query_proc, + xform_proc }; -#define MAX_PROC (query_proc+1) +#define MAX_PROC (xform_proc+1) static char *guile_proc_name[] = { "init", "done", "open", "close", - "query" + "query", + "xform" }; typedef SCM guile_vtab[MAX_PROC]; @@ -528,9 +530,10 @@ guile_init_db(const char *dbname, int argc, char **argv) return NULL; } - if (!db->vtab[query_proc]) { - smap_error("guile: %s: %s: void virtual function", - argv[0], guile_proc_name[query_proc]); + if (!db->vtab[query_proc] && !db->vtab[xform_proc]) { + smap_error("guile: %s: neither %s nor %s are defined", + dbname, guile_proc_name[query_proc], + guile_proc_name[xform_proc]); free(db); return NULL; } @@ -615,13 +618,49 @@ guile_query(smap_database_t dbp, return res == SCM_BOOL_F; } +int +guile_xform(smap_database_t dbp, + const char *map, const char *key, + struct smap_conninfo const *conninfo, + char **newmap, char **newkey) +{ + struct _guile_database *db = (struct _guile_database *)dbp; + SCM res, arg; + + if (!db->vtab[xform_proc]) + return 1; + + arg = scm_append(scm_list_2(scm_list_3(db->handle, + scm_from_locale_string(map), + scm_from_locale_string(key)), + scm_from_smap_conninfo(conninfo))); + if (guile_call_proc(&res, db->vtab[xform_proc], arg)) + return 1; + if (!scm_is_pair(res)) + return 1; + + arg = scm_car(res); + if (!scm_is_string(arg)) + *newmap = NULL; + else + *newmap = scm_to_locale_string(arg); + arg = scm_cdr(res); + if (!scm_is_string(arg)) + *newkey = NULL; + else + *newkey = scm_to_locale_string(arg); + + return 0; +} + struct smap_module SMAP_EXPORT(guile, module) = { SMAP_MODULE_VERSION, - SMAP_CAPA_NONE, + SMAP_CAPA_QUERY|SMAP_CAPA_XFORM, guile_init, guile_init_db, guile_free_db, guile_open, guile_close, - guile_query + guile_query, + guile_xform, }; diff --git a/modules/mailutils/mailutils.c b/modules/mailutils/mailutils.c index 1423227..4566b2b 100644 --- a/modules/mailutils/mailutils.c +++ b/modules/mailutils/mailutils.c @@ -576,11 +576,12 @@ mod_mailutils_open(smap_database_t dbp) struct smap_module SMAP_EXPORT(mailutils, module) = { SMAP_MODULE_VERSION, - SMAP_CAPA_NONE, + SMAP_CAPA_DEFAULT, mod_mailutils_init, mod_mailutils_init_db, mod_mailutils_free_db, mod_mailutils_open, /* smap_open */ NULL, /* smap_close */ - mod_mailutils_query + mod_mailutils_query, + NULL, /* smap_xform */ }; diff --git a/src/module.c b/src/module.c index 6160893..fa79425 100644 --- a/src/module.c +++ b/src/module.c @@ -111,13 +111,15 @@ module_declare(const char *file, unsigned line, } #define MODULE_ASSERT(cond) \ - if (!(cond)) { \ - lt_dlclose(handle); \ - smap_error("%s: faulty module: (%s) failed", \ - inst->id, \ - #cond); \ - return 1; \ - } + do { \ + if (!(cond)) { \ + lt_dlclose(handle); \ + smap_error("%s: faulty module: (%s) failed", \ + inst->id, \ + #cond); \ + return 1; \ + } \ + } while (0) static int _load_module(struct smap_module_instance *inst) @@ -149,7 +151,13 @@ _load_module(struct smap_module_instance *inst) MODULE_ASSERT(pmod->smap_version <= SMAP_MODULE_VERSION); MODULE_ASSERT(pmod->smap_init_db); MODULE_ASSERT(pmod->smap_free_db); - MODULE_ASSERT(pmod->smap_query); + if (pmod->smap_version == 1) + MODULE_ASSERT(pmod->smap_query); + else if (pmod->smap_capabilities & SMAP_CAPA_QUERY) + MODULE_ASSERT(pmod->smap_query); + else if (pmod->smap_capabilities & SMAP_CAPA_XFORM) + MODULE_ASSERT(pmod->smap_xform); + if (pmod->smap_init && pmod->smap_init(inst->argc, inst->argv)) { lt_dlclose(handle); smap_error("%s: initialization failed", inst->argv[0]); diff --git a/src/query.c b/src/query.c index 15dc467..a2ee1f5 100644 --- a/src/query.c +++ b/src/query.c @@ -28,17 +28,18 @@ enum query_cond_type { query_cond_not, query_cond_source, query_cond_server, - query_cond_map + query_cond_map, + query_cond_key }; -enum map_comparison { - map_eq, - map_like, - map_re +enum comparison { + comp_eq, + comp_like, + comp_re }; -struct map_cond { - enum map_comparison op; +struct comp_cond { + enum comparison op; char *str; regex_t re; }; @@ -50,7 +51,8 @@ struct query_cond { struct query_cond *subcond; /* query_cond_not */ struct smap_sockaddr *addr; /* query_cond_source */ char *id; /* query_cond_server */ - struct map_cond mapcond; /* query_cond_map */ + struct comp_cond comp; /* query_cond_map/ + query_cond_key */ } v; }; @@ -61,6 +63,7 @@ struct dispatch_rule { struct query_cond *cond; char *dbname; struct smap_database_instance *dbi; + int xform; }; @@ -128,7 +131,9 @@ enum query_tok { T_EQ, T_REGEXP, T_DB, - T_NOT + T_NOT, + T_TRANSFORM, + T_KEY }; static struct smap_kwtab query_kwtab[] = { @@ -142,6 +147,8 @@ static struct smap_kwtab query_kwtab[] = { { "is", T_EQ }, { "database", T_DB }, { "not", T_NOT }, + { "transform", T_TRANSFORM }, + { "key", T_KEY }, { NULL } }; @@ -173,7 +180,8 @@ parse_dispatch_server(struct query_cond **pcond) } static int parse_dispatch_not(struct query_cond **pcond); -static int parse_dispatch_map(struct query_cond **pcond); +static int parse_dispatch_comp(struct query_cond **pcond, + enum query_cond_type t); static int parse_dispatch_from(struct query_cond **pcond); static int @@ -201,7 +209,10 @@ parse_subcond(struct query_cond **pcond) return parse_dispatch_server(pcond); case T_MAP: - return parse_dispatch_map(pcond); + return parse_dispatch_comp(pcond, query_cond_map); + + case T_KEY: + return parse_dispatch_comp(pcond, query_cond_key); } smap_error("%s:%lu: unexpected keyword: %s", cfg_file_name, cfg_line, s); @@ -223,7 +234,7 @@ parse_dispatch_not(struct query_cond **pcond) } static int -parse_regexp(const char *s, struct map_cond *mapcond) +parse_regexp(const char *s, struct comp_cond *comp) { int rc; char *buf; @@ -270,24 +281,24 @@ parse_regexp(const char *s, struct map_cond *mapcond) buf = emalloc(len + 1); memcpy(buf, s + 1, len); buf[len] = 0; - rc = regcomp(&mapcond->re, buf, flags); + rc = regcomp(&comp->re, buf, flags); free(buf); if (rc) { char errbuf[512]; - regerror(rc, &mapcond->re, errbuf, sizeof(errbuf)); + regerror(rc, &comp->re, errbuf, sizeof(errbuf)); smap_error("%s:%lu: regexp error: %s", cfg_file_name, cfg_line, errbuf); return 1; } - mapcond->op = map_re; + comp->op = comp_re; return 0; } static int -parse_dispatch_map(struct query_cond **pcond) +parse_dispatch_comp(struct query_cond **pcond, enum query_cond_type t) { struct query_cond *cond; - struct map_cond mapcond; + struct comp_cond comp; char *s = nextarg(); int tok; @@ -300,32 +311,32 @@ parse_dispatch_map(struct query_cond **pcond) s = nextarg(); if (!s) return 1; - mapcond.op = map_like; + comp.op = comp_like; break; case T_EQ: s = nextarg(); if (!s) return 1; - mapcond.op = map_eq; + comp.op = comp_eq; break; case T_REGEXP: s = nextarg(); if (!s) return 1; - if (parse_regexp(s, &mapcond)) + if (parse_regexp(s, &comp)) return 1; break; default: - mapcond.op = map_eq; + comp.op = comp_eq; } } else - mapcond.op = map_eq; - mapcond.str = estrdup(s); - cond = query_cond_new(query_cond_map); - cond->v.mapcond = mapcond; + comp.op = comp_eq; + comp.str = estrdup(s); + cond = query_cond_new(t); + cond->v.comp = comp; *pcond = cond; return 0; } @@ -423,7 +434,8 @@ parse_complex_dispatch() struct query_cond *head = NULL, *tail = NULL; char *dbname = NULL; struct dispatch_rule *rp; - + int xform; + while (*input && rc == 0) { char *s = *input++; int tok; @@ -450,10 +462,15 @@ parse_complex_dispatch() break; case T_MAP: - rc = parse_dispatch_map(&cond); + rc = parse_dispatch_comp(&cond, query_cond_map); + break; + + case T_KEY: + rc = parse_dispatch_comp(&cond, query_cond_key); break; case T_DB: + case T_TRANSFORM: s = nextarg(); if (!s) rc = 1; @@ -463,6 +480,7 @@ parse_complex_dispatch() rc = 1; } else dbname = estrdup(s); + xform = tok == T_TRANSFORM; } if (rc == 1) break; @@ -486,6 +504,7 @@ parse_complex_dispatch() rp->line = cfg_line; rp->cond = head; rp->dbname = dbname; + rp->xform = xform; dispatch_attach(rp); return 0; } @@ -532,6 +551,49 @@ parse_dispatch(char **wordv) return parse_complex_dispatch(); } +int +rule_fixup(struct dispatch_rule *p) +{ + struct smap_database_instance *dbi; + struct smap_module *mod; + + dbi = database_locate(p->dbname); + if (!dbi) { + smap_error("%s:%u: no such database: %s", + p->file, p->line, p->dbname); + return 1; + } + + mod = dbi->inst->module; + if (mod->smap_version == 1) { + if (p->xform) { + smap_error("%s:%u: database %s does not " + "handle transformations", + p->file, p->line, p->dbname); + return 1; + } + } else if (mod->smap_version > 1) { + if (p->xform) { + if (!(mod->smap_capabilities & SMAP_CAPA_XFORM)) { + smap_error("%s:%u: database %s does not " + "handle transformations", + p->file, p->line, p->dbname); + return 1; + } + } else { + if (!(mod->smap_capabilities & SMAP_CAPA_QUERY)) { + smap_error("%s:%u: database %s does not " + "handle queries: %x", + p->file, p->line, p->dbname, + mod->smap_capabilities); + return 1; + } + } + } + p->dbi = dbi; + return 0; +} + void link_dispatch_rules() { @@ -539,18 +601,12 @@ link_dispatch_rules() for (p = dispatch_head; p; ) { struct dispatch_rule *next = p->next; - struct smap_database_instance *dbi; - - dbi = database_locate(p->dbname); - if (!dbi) { - smap_error("%s:%u: no such database: %s", - p->file, p->line, p->dbname); + if (rule_fixup(p)) { debug(DBG_MODULE, 1, ("removing query %s:%u", p->file, p->line)); dispatch_detach(p); /* FIXME: free memory */ } - p->dbi = dbi; p = next; } } @@ -563,16 +619,16 @@ struct query_pack { }; static int -match_map(struct map_cond *cond, const char *map) +compare(struct comp_cond *cond, const char *map) { switch (cond->op) { - case map_eq: + case comp_eq: return strcmp(cond->str, map) == 0; - case map_like: + case comp_like: return fnmatch(cond->str, map, 0) == 0; - case map_re: + case comp_re: return regexec(&cond->re, map, 0, NULL, 0) == 0; } return 0; @@ -594,7 +650,10 @@ match_cond(struct query_cond *cond, struct query_pack *qp) return strcmp(cond->v.id, qp->server_id) == 0; case query_cond_map: - return match_map(&cond->v.mapcond, qp->map); + return compare(&cond->v.comp, qp->map); + + case query_cond_key: + return compare(&cond->v.comp, qp->key); } return 0; } @@ -609,11 +668,13 @@ match_cond_list(struct query_cond *cond, struct query_pack *qp) } static struct dispatch_rule * -find_dispatch_rule(struct query_pack *qp) +find_dispatch_rule(struct query_pack *qp, struct dispatch_rule *start) { struct dispatch_rule *p; - for (p = dispatch_head; p; p = p->next) { + if (!start) + start = dispatch_head; + for (p = start; p; p = p->next) { debug(DBG_QUERY, 2, ("trying %s:%u", p->file, p->line)); if (match_cond_list(p->cond, qp)) break; @@ -628,38 +689,73 @@ dispatch_query(const char *id, struct smap_conninfo const *conninfo, struct smap_database_instance *dbi; struct smap_module *mod; struct query_pack query; - struct dispatch_rule *qr; - - debug(DBG_QUERY, 1, ("routing query %s %s", map, key)); + struct dispatch_rule *next = NULL; + char *alloc_map = NULL, *alloc_key = NULL; + + debug(DBG_QUERY, 1, ("dispatching query %s %s", map, key)); query.server_id = id; query.conninfo = conninfo; query.map = map; query.key = key; - qr = find_dispatch_rule(&query); - if (qr) - debug(DBG_QUERY, 1, ("route at %s:%u, database %s", - qr->file, qr->line, qr->dbname)); - else { - smap_error("no match for %s %s", map, key); - smap_stream_printf(ostr, "NOTFOUND\n"); - return; - } - dbi = qr->dbi; - mod = dbi->inst->module; - if (!dbi->opened) { - int rc = 0; - - debug(DBG_DATABASE, 2, ("opening database %s", dbi->id)); - if (mod->smap_open) - rc = mod->smap_open(dbi->dbh); - if (rc) { - smap_error("cannot open database %s", dbi->id); - /* FIXME: mark database unusable */ - smap_stream_printf(ostr, "NOTFOUND\n"); + do { + struct dispatch_rule *qr; + + qr = find_dispatch_rule(&query, next); + if (qr) + debug(DBG_QUERY, 1, ("rule at %s:%u, database %s", + qr->file, qr->line, qr->dbname)); + else + break; + + dbi = qr->dbi; + mod = dbi->inst->module; + if (!dbi->opened) { + int rc = 0; + + debug(DBG_DATABASE, 2, + ("opening database %s", dbi->id)); + if (mod->smap_open) + rc = mod->smap_open(dbi->dbh); + if (rc) { + smap_error("cannot open database %s", dbi->id); + /* FIXME: mark database unusable */ + smap_stream_printf(ostr, "NOTFOUND\n"); + return; + } + dbi->opened = 1; + } + + if (qr->xform) { + char *nmap = NULL; + char *nkey = NULL; + if (mod->smap_xform(dbi->dbh, map, key, conninfo, + &nmap, &nkey) == 0) { + if (nmap) { + if (alloc_map) + free(alloc_map); + alloc_map = nmap; + query.map = nmap; + } + if (nkey) { + if (alloc_key) + free(alloc_key); + alloc_key = nkey; + query.key = nkey; + } + debug(DBG_QUERY, 1, + ("rule at %s:%u, transformed query: %s %s", + qr->file, qr->line, query.map, query.key)); + } + next = qr->next; + } else { + if (mod->smap_query(dbi->dbh, ostr, + query.map, query.key, + conninfo)) + break; return; } - dbi->opened = 1; - } - mod->smap_query(dbi->dbh, ostr, map, key, conninfo); + } while (next); + smap_error("no database matches %s %s", map, key); + smap_stream_printf(ostr, "NOTFOUND\n"); } |