rpm/rpmio/rpmhook.c

262 lines
6.0 KiB
C

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