/* This file is part of Eclat. Copyright (C) 2012-2014 Sergey Poznyakoff. Eclat is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. Eclat is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Eclat. If not, see . */ #include "libeclat.h" #include "wordsplit.h" #include #include #include static int map_dbg = -1; static struct grecs_symtab *openmap_symtab, *mapdrv_symtab; void eclat_map_init() { map_dbg = debug_register("map"); } int eclat_get_string_node(struct grecs_node *node, const char *name, int optional, struct grecs_node **pret) { struct grecs_node *p = grecs_find_node(node->down, name); if (!p) { if (optional) return eclat_map_not_found; grecs_error(&node->locus, 0, "no \"%s\" statement found", name); return eclat_map_failure; } if (p->type != grecs_node_stmt) { grecs_error(&p->locus, 0, "must be simple statement"); return eclat_map_failure; } if (p->v.value->type != GRECS_TYPE_STRING) { grecs_error(&p->locus, 0, "must be scalar"); return eclat_map_failure; } *pret = p; return eclat_map_ok; } static int drv_copy(void *a, void *b) { struct eclat_map_drv *drva = a; struct eclat_map_drv *drvb = b; *drva = *drvb; drva->name = strdup(drvb->name); return drva->name == NULL; } int eclat_map_drv_register(struct eclat_map_drv *drv) { int install; if (!mapdrv_symtab) { mapdrv_symtab = grecs_symtab_create(sizeof(*drv), NULL, NULL, drv_copy, NULL, NULL); if (!mapdrv_symtab) grecs_alloc_die(); } install = 1; if (!grecs_symtab_lookup_or_install(mapdrv_symtab, drv, &install)) die(EX_SOFTWARE, "cannot install map"); return install ? eclat_map_ok : eclat_map_failure; } struct eclat_map * eclat_map_lookup(const char *name) { struct eclat_map key; if (!openmap_symtab) return NULL; key.name = (char*) name; return grecs_symtab_lookup_or_install(openmap_symtab, &key, NULL); } static void map_free(void *p) { struct eclat_map *map = p; if (map->flags & ECLAT_MAP_OPEN) eclat_map_close(map); if (map->data && map->drv->map_free) map->drv->map_free(map_dbg, map->data); free(map->name); free(map->keytrans); free(map); } int eclat_map_config(struct grecs_node *node, struct eclat_map **return_map) { char *mapname; struct grecs_node *p; struct eclat_map key, *map; struct eclat_map_drv dkey, *drv; int install, rc; if (node->v.value->type != GRECS_TYPE_STRING) { grecs_error(&p->locus, 0, "value must be scalar"); return eclat_map_failure; } mapname = node->v.value->v.string; if (debug_level(map_dbg) > 1) diag(&node->locus, "debug", "configuring map \"%s\"", mapname); map = eclat_map_lookup(mapname); if (map) { diag(&node->locus, "warning", "map \"%s\" already declared", mapname); diag(&map->locus, "warning", "this is the location of the previous declaration"); *return_map = map; return eclat_map_ok; } if (eclat_get_string_node(node, "type", 0, &p)) return eclat_map_failure; dkey.name = p->v.value->v.string; if (!mapdrv_symtab) { grecs_error(&p->locus, 0, "no drivers compiled"); return eclat_map_failure; } drv = grecs_symtab_lookup_or_install(mapdrv_symtab, &dkey, NULL); if (!drv) { grecs_error(&p->locus, 0, "no driver for this type"); return eclat_map_failure; } if (!openmap_symtab) { openmap_symtab = grecs_symtab_create(sizeof(key), NULL, NULL, NULL, NULL, map_free); if (!openmap_symtab) grecs_alloc_die(); } install = 1; key.name = (char*) mapname; map = grecs_symtab_lookup_or_install(openmap_symtab, &key, &install); if (!map) die(EX_SOFTWARE, "cannot install map"); map->drv = drv; rc = drv->map_config(map_dbg, node, &map->data); if (rc != eclat_map_ok) { grecs_error(&node->locus, 0, "map configration failed"); eclat_map_free(map); return eclat_map_failure; } if (eclat_get_string_node(node, "key", 1, &p) == 0) map->keytrans = grecs_strdup(p->v.value->v.string); /* FIXME: See eclat.c:708 */ map->locus = node->locus; debug(map_dbg, 1, ("map \"%s\" configured", mapname)); *return_map = map; return eclat_map_ok; } void eclat_map_free(struct eclat_map *map) { grecs_symtab_remove(openmap_symtab, map); } int eclat_map_open(struct eclat_map *map) { debug(map_dbg, 1, ("opening map \"%s\"", map->name)); if (map->flags & ECLAT_MAP_OPEN) { debug(map_dbg, 1, ("map \"%s\" already open", map->name)); return eclat_map_ok; } if (map->drv->map_open(map_dbg, map->data)) { return eclat_map_failure; } debug(map_dbg, 1, ("map \"%s\" opened successfully", map->name)); map->flags |= ECLAT_MAP_OPEN; return eclat_map_ok; } int eclat_map_close(struct eclat_map *map) { int rc = eclat_map_ok; debug(map_dbg, 1, ("closing map \"%s\"", map->name)); if (!(map->flags & ECLAT_MAP_OPEN)) { debug(map_dbg, 1, ("map \"%s\" not open", map->name)); return eclat_map_ok; } if (map->drv->map_close) rc = map->drv->map_close(map_dbg, map->data); if (rc == eclat_map_ok) { debug(map_dbg, 1, ("map \"%s\" closed successfully", map->name)); map->flags &= ~ECLAT_MAP_OPEN; } else err("failed to close map %s", map->name); return rc; } int eclat_map_get(struct eclat_map *map, int dir, const char *key, char **value) { int rc; char *p = NULL; debug(map_dbg, 1, ("looking up \"%s\" in map \"%s\", dir=%d", key, map->name, dir)); if (!(map->flags & ECLAT_MAP_OPEN)) { debug(map_dbg, 1, ("map \"%s\" not open", map->name)); return eclat_map_failure; } if (map->keytrans) { const char *kwe[7]; kwe[0] = "key"; kwe[1] = key; kwe[2] = "map"; kwe[3] = map->name; kwe[4] = "dir"; kwe[5] = dir ? "1" : "0"; kwe[6] = NULL; p = eclat_expand_kw(map->keytrans, kwe); debug(map_dbg, 1, ("transformed key \"%s\" => \"%s\"", key, p)); key = p; } rc = map->drv->map_get(map_dbg, dir, map->data, key, value); free(p); debug(map_dbg, 1, ("result: \"%s\"", eclat_map_strerror(rc))); if (rc == eclat_map_ok) debug(map_dbg, 2, ("found value: \"%s\"", *value)); return rc; } const char * eclat_map_strerror(int rc) { switch (rc) { case eclat_map_ok: return "success"; case eclat_map_failure: return "failure"; case eclat_map_not_found: return "not found"; case eclat_map_bad_dir: return "wrond lookup direction"; } return "unknown error"; } struct foreach_closure { int (*fun)(struct eclat_map *, void *); void *data; }; static int map_foreach(void *item, void *data) { struct eclat_map *map = item; struct foreach_closure *cp = data; return cp->fun(map, cp->data); } void eclat_map_foreach(int (*fun)(struct eclat_map *, void *), void *data) { struct foreach_closure cl; if (!openmap_symtab) return; cl.fun = fun; cl.data = data; grecs_symtab_enumerate(openmap_symtab, map_foreach, &cl); } void eclat_map_free_all() { if (openmap_symtab) { grecs_symtab_free(openmap_symtab); openmap_symtab = NULL; } } static int drv_help(void *data, void *unused) { struct eclat_map_drv *drv = data; if (drv->map_confhelp) drv->map_confhelp(); return 0; } void eclat_map_confhelp() { grecs_symtab_enumerate(mapdrv_symtab, drv_help, NULL); } int eclat_map_name_split(const char *mapname, char **name, char **endp) { size_t len = strcspn(mapname, ":"); int dir; if (endp) *endp = (char*) mapname + len; if (!mapname[len]) { *name = grecs_strdup(mapname); dir = MAP_DIR; } else { const char *q = mapname + len + 1; if (strcmp(q, "0") == 0 || strcmp(q, "dir") == 0) dir = MAP_DIR; else if (strcmp(q, "1") == 0 || strcmp(q, "rev") == 0) dir = MAP_REV; else return -1; *name = grecs_malloc(len + 1); memcpy(*name, mapname, len); (*name)[len] = 0; } return dir; }