/* This file is part of Eclat.
Copyright (C) 2012-2018 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_foreach(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_foreach(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;
}