marrakesh/mrklib_repodb.c

337 lines
7.9 KiB
C

#include "mrklib_priv.h"
typedef struct
{
const char *arch;
Eina_Hash *name_hash;
Eina_Hash *category_hash;
Eina_Hash *tag_hash;
Eina_Hash *key_hash;
} Db;
struct _Mrk_Repodb
{
const char *repodir;
Eina_List *dbs;
Ecore_Exe *exe;
Eina_Bool update;
Eina_List *handlers;
struct {
void (*update) (void *data, Mrk_Repodb *repodb);
} func;
struct {
void *update;
} data;
};
static void
db_load_hash(Eina_Hash *hash, FILE *f)
{
char line[16384], *s;
int len;
Eina_List *list = NULL;
const char *key = NULL;
for (;;)
{
s = fgets(line, sizeof(line), f);
if (!s) break;;
len = strlen(s);
if (len < 1) continue;
s[len - 1] = 0;
if ((s[0] == '\t') && (key))
{
list = eina_list_append(list, eina_stringshare_add(s + 1));
}
else
{
if (key) eina_hash_set(hash, key, list);
list = NULL;
if (key) eina_stringshare_del(key);
key = eina_stringshare_add(s);
}
}
if (key) eina_hash_set(hash, key, list);
list = NULL;
if (key) eina_stringshare_del(key);
key = NULL;
}
static Eina_Bool
db_clear_hash_cb(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *data, void *fdata EINA_UNUSED)
{
const char *s;
EINA_LIST_FREE(data, s)
{
eina_stringshare_del(s);
}
return EINA_TRUE;
}
static void
db_clear_hash(Eina_Hash *hash)
{
if (!hash) return;
eina_hash_foreach(hash, db_clear_hash_cb, NULL);
eina_hash_free(hash);
}
static void
db_clear(Mrk_Repodb *rdb)
{
Db *db;
EINA_LIST_FREE(rdb->dbs, db)
{
db_clear_hash(db->name_hash);
db_clear_hash(db->category_hash);
db_clear_hash(db->tag_hash);
db_clear_hash(db->key_hash);
free(db);
}
}
static void
db_load(Mrk_Repodb *rdb)
{
Eina_List *files;
char *s;
char tmp[PATH_MAX];
Db *db = NULL;
FILE *f;
files = ecore_file_ls(rdb->repodir);
EINA_LIST_FREE(files, s)
{
if ((s[0] == '.') || (!_mrk_util_arch_ok(s)))
{
free(s);
continue;
}
snprintf(tmp, sizeof(tmp), "%s/%s/.mrkdb-name.txt", rdb->repodir, s);
f = fopen(tmp, "rb");
if (f)
{
db = calloc(1, sizeof(Db));
if (db)
{
db->arch = eina_stringshare_add(s);
if (db->arch)
{
db->name_hash = eina_hash_string_superfast_new(NULL);
db->category_hash = eina_hash_string_superfast_new(NULL);
db->tag_hash = eina_hash_string_superfast_new(NULL);
db->key_hash = eina_hash_string_superfast_new(NULL);
rdb->dbs = eina_list_append(rdb->dbs, db);
db_load_hash(db->name_hash, f);
}
else
{
free(db);
db = NULL;
}
}
fclose(f);
}
if (db)
{
snprintf(tmp, sizeof(tmp), "%s/%s/.mrkdb-tag.txt", rdb->repodir, s);
f = fopen(tmp, "rb");
if (f)
{
db_load_hash(db->tag_hash, f);
fclose(f);
}
snprintf(tmp, sizeof(tmp), "%s/%s/.mrkdb-category.txt", rdb->repodir, s);
f = fopen(tmp, "rb");
if (f)
{
db_load_hash(db->category_hash, f);
fclose(f);
}
snprintf(tmp, sizeof(tmp), "%s/%s/.mrkdb-key.txt", rdb->repodir, s);
f = fopen(tmp, "rb");
if (f)
{
db_load_hash(db->key_hash, f);
fclose(f);
}
}
db = NULL;
}
}
static Db *
db_arch_find(Mrk_Repodb *rdb, const char *arch)
{
Eina_List *l;
Db *db;
if (!arch) return NULL;
EINA_LIST_FOREACH(rdb->dbs, l, db)
{
if (!strcmp(db->arch, arch))
{
if (rdb->dbs != l) rdb->dbs = eina_list_promote_list(rdb->dbs, l);
return db;
}
}
return NULL;
}
static Eina_List *
search_intersect(Eina_List *list_in, Eina_List *list2)
{
Eina_List *list = NULL, *ll;
const char *s, *ss;
int ok;
EINA_LIST_FREE(list_in, s)
{
ok = 0;
EINA_LIST_FOREACH(list2, ll, ss)
{
if (ss == s)
{
ok = 1;
break;
}
}
if (ok) list = eina_list_append(list, s);
else eina_stringshare_del(s);
}
return list;
}
static Eina_List *
db_search(Eina_Hash *hash, const char *search)
{
Eina_List *list = NULL, *tl, *l;
const char *s;
char *ts = strdup(search);
char *p, *end = ts;
if (!ts) return NULL;
for (p = ts; *p; p++)
{
if (isspace(*p)) *p = 0;
end = p;
}
for (p = ts; p < end; p++)
{
if (!*p) continue;
tl = eina_hash_find(hash, p);
if (p == ts)
{
EINA_LIST_FOREACH(tl, l, s)
list = eina_list_append(list, eina_stringshare_add(s));
}
else
{
list = search_intersect(list, tl);
}
p += strlen(p);
}
free(ts);
return list;
}
static Eina_Bool
_mrk_cb_exe_del(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Exe_Event_Del *e = event;
Mrk_Repodb *rdb = data;
const char *tag;
if (!e->exe) return EINA_TRUE;
tag = ecore_exe_tag_get(e->exe);
if (!tag) return EINA_TRUE;
if (!strcmp(tag, "-=# mrk serve - db exe #=-"))
{
if (ecore_exe_data_get(e->exe) != rdb) return EINA_TRUE;
if (e->exe != rdb->exe) return EINA_TRUE;
rdb->exe = NULL;
db_clear(rdb);
db_load(rdb);
if (rdb->update)
{
rdb->update = EINA_FALSE;
mrk_repodb_update(rdb);
}
if (rdb->func.update) rdb->func.update(rdb->data.update, rdb);
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI Mrk_Repodb *
mrk_repodb_load(const char *repodir)
{
Mrk_Repodb *rdb;
rdb = calloc(1, sizeof(Mrk_Repodb));
if (!rdb) return NULL;
rdb->repodir = eina_stringshare_add(repodir);
rdb->handlers = eina_list_append(rdb->handlers,
ecore_event_handler_add
(ECORE_EXE_EVENT_DEL,
_mrk_cb_exe_del, rdb));
db_load(rdb);
return rdb;
}
EAPI void
mrk_repodb_free(Mrk_Repodb *rdb)
{
Ecore_Event_Handler *hnd;
EINA_LIST_FREE(rdb->handlers, hnd) ecore_event_handler_del(hnd);
db_clear(rdb);
if (rdb->exe) ecore_exe_free(rdb->exe);
free(rdb);
}
EAPI void
mrk_repodb_callback_update_set(Mrk_Repodb *rdb,
void (*update) (void *data, Mrk_Repodb *rdb),
void *update_data)
{
rdb->func.update = update;
rdb->data.update = update_data;
}
EAPI void
mrk_repodb_update(Mrk_Repodb *rdb)
{
char tmp[PATH_MAX];
if (rdb->exe)
{
rdb->update = EINA_TRUE;
return;
}
snprintf(tmp, sizeof(tmp), "mrk index %s/*-*", rdb->repodir);
rdb->exe = ecore_exe_pipe_run(tmp, 0, rdb);
if (rdb->exe) ecore_exe_tag_set(rdb->exe, "-=# mrk serve - db exe #=-");
}
EAPI const Eina_List *
mrk_repodb_category_list(Mrk_Repodb *rdb, const char *arch, const char *category)
{
Db *db = db_arch_find(rdb, arch);
if (!db) return NULL;
if (!category) category = "@"; // @ is the "everything" category and list
return eina_hash_find(db->category_hash, category);
}
EAPI Eina_List *
mrk_repodb_search(Mrk_Repodb *rdb, const char *arch, const char *search)
{
Db *db = db_arch_find(rdb, arch);
if (!db) return NULL;
return db_search(db->key_hash, search);
}