csomh / source-git / rpm

Forked from source-git/rpm 4 years ago
Clone
2ff057
#include "system.h"
2ff057
2ff057
#include <stdlib.h>
2ff057
#include <stdio.h>
2ff057
#include <string.h>
2ff057
#include <stdarg.h>
2ff057
2ff057
#include <rpm/rpmstring.h>
2ff057
#include "rpmio/rpmhook.h"
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
#define RPMHOOK_TABLE_INITSIZE  256
2ff057
#define RPMHOOK_BUCKET_INITSIZE 5
2ff057
2ff057
typedef struct rpmhookItem_s {
2ff057
    rpmhookFunc func;
2ff057
    void *data;
2ff057
    struct rpmhookItem_s *next;
2ff057
} * rpmhookItem;
2ff057
2ff057
typedef struct rpmhookBucket_s {
2ff057
    unsigned long hash;
2ff057
    char *name;
2ff057
    rpmhookItem item;
2ff057
} * rpmhookBucket;
2ff057
2ff057
typedef struct rpmhookTable_s {
2ff057
    int size;
2ff057
    int used;
2ff057
    struct rpmhookBucket_s bucket[1];
2ff057
} * rpmhookTable;
2ff057
2ff057
2ff057
rpmhookArgs rpmhookArgsNew(int argc)
2ff057
{
2ff057
    rpmhookArgs args = (rpmhookArgs) xcalloc(1,
2ff057
			sizeof(*args) + sizeof(args->argv) * (argc-1));
2ff057
    args->argc = argc;
2ff057
    return args;
2ff057
}
2ff057
2ff057
rpmhookArgs rpmhookArgsFree(rpmhookArgs args)
2ff057
{
2ff057
    if (args != NULL)
2ff057
	free(args);
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
static rpmhookTable rpmhookTableNew(int size)
2ff057
{
2ff057
    rpmhookTable table = (rpmhookTable) xcalloc(1,
2ff057
		sizeof(*table) + sizeof(table->bucket) * (size-1));
2ff057
    table->size = size;
2ff057
    return table;
2ff057
}
2ff057
2ff057
#if 0
2ff057
static rpmhookTable rpmhookTableFree(rpmhookTable table)
2ff057
{
2ff057
    rpmhookItem item, nextItem;
2ff057
    int i;
2ff057
    for (i = 0; i != table->size; i++) {
2ff057
	if (table->bucket[i].name == NULL)
2ff057
	    continue;
2ff057
	free(table->bucket[i].name);
2ff057
	item = table->bucket[i].item;
2ff057
	while (item) {
2ff057
	    nextItem = item->next;
2ff057
	    free(item);
2ff057
	    item = nextItem;
2ff057
	}
2ff057
    }
2ff057
    free(table);
2ff057
    return NULL;
2ff057
}
2ff057
#endif
2ff057
2ff057
static void rpmhookTableRehash(rpmhookTable *table);
2ff057
2ff057
static int rpmhookTableFindBucket(rpmhookTable *table, const char *name)
2ff057
{
2ff057
    /* Hash based on http://www.isthe.com/chongo/tech/comp/fnv/ */
2ff057
    unsigned long perturb;
2ff057
    unsigned long hash = 0;
2ff057
    unsigned char *bp = (unsigned char *)name;
2ff057
    unsigned char *be = bp + strlen(name);
2ff057
    rpmhookBucket bucket;
2ff057
    int ret;
2ff057
2ff057
    if (((*table)->used/2)*3 > (*table)->size)
2ff057
	rpmhookTableRehash(table);
2ff057
    while (bp < be) {
2ff057
	hash ^= (unsigned long)*bp++;
2ff057
	hash *= (unsigned long)0x01000193;
2ff057
    }
2ff057
    perturb = hash;
2ff057
    ret = hash % (*table)->size;
2ff057
    bucket = &(*table)->bucket[ret];
2ff057
    while (bucket->name &&
2ff057
	    (bucket->hash != hash || !rstreq(bucket->name, name))) {
2ff057
	/* Collision resolution based on Python's perturb scheme. */
2ff057
	ret = ((ret << 2) + ret + perturb + 1) % (*table)->size;
2ff057
	perturb >>= 5;
2ff057
	bucket = &(*table)->bucket[ret];
2ff057
    }
2ff057
    if (!bucket->name)
2ff057
	bucket->hash = hash;
2ff057
    return ret;
2ff057
}
2ff057
2ff057
static void rpmhookTableRehash(rpmhookTable *table)
2ff057
{
2ff057
    rpmhookTable newtable = rpmhookTableNew((*table)->size*2);
2ff057
    int n, i = 0;
2ff057
2ff057
    for (; i != (*table)->size; i++) {
2ff057
	if ((*table)->bucket[i].name == NULL)
2ff057
	    continue;
2ff057
	n = rpmhookTableFindBucket(&newtable, (*table)->bucket[i].name);
2ff057
	newtable->bucket[n].name = (*table)->bucket[i].name;
2ff057
	newtable->bucket[n].item = (*table)->bucket[i].item;
2ff057
    }
2ff057
    newtable->used = (*table)->used;
2ff057
    free(*table);
2ff057
    *table = newtable;
2ff057
}
2ff057
2ff057
static void rpmhookTableAddItem(rpmhookTable *table, const char *name,
2ff057
				rpmhookFunc func, void *data)
2ff057
{
2ff057
    int n = rpmhookTableFindBucket(table, name);
2ff057
    rpmhookBucket bucket = &(*table)->bucket[n];
2ff057
    rpmhookItem *item = &bucket->item;
2ff057
    if (!bucket->name) {
2ff057
	bucket->name = xstrdup(name);
2ff057
	(*table)->used++;
2ff057
    }
2ff057
    while (*item) item = &(*item)->next;
2ff057
    *item = xcalloc(1, sizeof(**item));
2ff057
    (*item)->func = func;
2ff057
    (*item)->data = data;
2ff057
}
2ff057
2ff057
static void rpmhookTableDelItem(rpmhookTable *table, const char *name,
2ff057
		rpmhookFunc func, void *data,
2ff057
		int matchfunc, int matchdata)
2ff057
{
2ff057
    int n = rpmhookTableFindBucket(table, name);
2ff057
    rpmhookBucket bucket = &(*table)->bucket[n];
2ff057
    rpmhookItem item = bucket->item;
2ff057
    rpmhookItem lastItem = NULL;
2ff057
    rpmhookItem nextItem;
2ff057
    while (item) {
2ff057
	nextItem = item->next;
2ff057
	if ((!matchfunc || item->func == func) &&
2ff057
	    (!matchdata || item->data == data)) {
2ff057
	    free(item);
2ff057
	    if (lastItem)
2ff057
		lastItem->next = nextItem;
2ff057
	    else
2ff057
		bucket->item = nextItem;
2ff057
	} else {
2ff057
	    lastItem = item;
2ff057
	}
2ff057
	item = nextItem;
2ff057
    }
2ff057
    if (!bucket->item) {
2ff057
	free(bucket->name);
2ff057
	bucket->name = NULL;
2ff057
	(*table)->used--;
2ff057
    }
2ff057
}
2ff057
2ff057
static rpmhookArgs rpmhookArgsParse(const char *argt, va_list ap)
2ff057
{
2ff057
    rpmhookArgs args = rpmhookArgsNew(strlen(argt));
2ff057
    int i;
2ff057
2ff057
    args->argt = argt;
2ff057
    for (i = 0; i != args->argc; i++) {
2ff057
	switch (argt[i]) {
2ff057
	    case 's':
2ff057
		args->argv[i].s = va_arg(ap, char *);
2ff057
		break;
2ff057
	    case 'i':
2ff057
		args->argv[i].i = va_arg(ap, int);
2ff057
		break;
2ff057
	    case 'f':
2ff057
		args->argv[i].f = (float)va_arg(ap, double);
2ff057
		break;
2ff057
	    case 'p':
2ff057
		args->argv[i].p = va_arg(ap, void *);
2ff057
		break;
2ff057
	    default:
2ff057
		fprintf(stderr, "error: unsupported type '%c' as "
2ff057
				"a hook argument\n", argt[i]);
2ff057
		break;
2ff057
	}
2ff057
    }
2ff057
    return args;
2ff057
}
2ff057
2ff057
static void rpmhookTableCallArgs(rpmhookTable *table, const char *name,
2ff057
			rpmhookArgs args)
2ff057
{
2ff057
    int n = rpmhookTableFindBucket(table, name);
2ff057
    rpmhookItem item = (*table)->bucket[n].item;
2ff057
    rpmhookItem next;
2ff057
    while (item) {
2ff057
	next = item->next;
2ff057
	if (item->func(args, item->data) != 0)
2ff057
	    break;
2ff057
	item = next;
2ff057
    }
2ff057
}
2ff057
2ff057
static rpmhookTable globalTable = NULL;
2ff057
2ff057
void rpmhookRegister(const char *name, rpmhookFunc func, void *data)
2ff057
{
2ff057
    if (globalTable == NULL)
2ff057
	globalTable = rpmhookTableNew(RPMHOOK_TABLE_INITSIZE);
2ff057
    rpmhookTableAddItem(&globalTable, name, func, data);
2ff057
}
2ff057
2ff057
void rpmhookUnregister(const char *name, rpmhookFunc func, void *data)
2ff057
{
2ff057
    if (globalTable != NULL)
2ff057
	rpmhookTableDelItem(&globalTable, name, func, data, 1, 1);
2ff057
}
2ff057
2ff057
void rpmhookUnregisterAny(const char *name, rpmhookFunc func)
2ff057
{
2ff057
    if (globalTable != NULL)
2ff057
	rpmhookTableDelItem(&globalTable, name, func, NULL, 1, 0);
2ff057
}
2ff057
2ff057
void rpmhookUnregisterAll(const char *name)
2ff057
{
2ff057
    if (globalTable != NULL)
2ff057
	rpmhookTableDelItem(&globalTable, name, NULL, NULL, 0, 0);
2ff057
}
2ff057
2ff057
void rpmhookCall(const char *name, const char *argt, ...)
2ff057
{
2ff057
    if (globalTable != NULL) {
2ff057
	rpmhookArgs args;
2ff057
	va_list ap;
2ff057
	va_start(ap, argt);
2ff057
	args = rpmhookArgsParse(argt, ap);
2ff057
	rpmhookTableCallArgs(&globalTable, name, args);
2ff057
	(void) rpmhookArgsFree(args);
2ff057
	va_end(ap);
2ff057
    }
2ff057
}
2ff057
2ff057
void rpmhookCallArgs(const char *name, rpmhookArgs args)
2ff057
{
2ff057
    if (globalTable != NULL)
2ff057
	rpmhookTableCallArgs(&globalTable, name, args);
2ff057
}