/*@-bounds@*/ #include "system.h" #include #include #include #include #include #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; /*@relnull@*/ 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; } /*@only@*/ 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) /*@modifies *table @*/; static int rpmhookTableFindBucket(rpmhookTable *table, const char *name) /*@modifies *table @*/ { /* 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. */ /*@-shiftimplementation@*/ ret = ((ret << 2) + ret + perturb + 1) % (*table)->size; /*@=shiftimplementation@*/ perturb >>= 5; bucket = &(*table)->bucket[ret]; } if (!bucket->name) bucket->hash = hash; return ret; } static void rpmhookTableRehash(rpmhookTable *table) /*@modifies *table @*/ { rpmhookTable newtable = rpmhookTableNew((*table)->size*2); int n, i = 0; /*@-branchstate@*/ 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; } /*@=branchstate@*/ newtable->used = (*table)->used; /*@-unqualifiedtrans@*/ free(*table); /*@=unqualifiedtrans@*/ *table = newtable; } static void rpmhookTableAddItem(rpmhookTable *table, const char *name, rpmhookFunc func, void *data) /*@modifies *table @*/ { int n = rpmhookTableFindBucket(table, name); rpmhookBucket bucket = &(*table)->bucket[n]; rpmhookItem *item = &bucket->item; if (!bucket->name) { bucket->name = strdup(name); (*table)->used++; } while (*item) item = &(*item)->next; *item = xcalloc(1, sizeof(**item)); (*item)->func = func; /*@-temptrans@*/ (*item)->data = data; /*@=temptrans@*/ } static void rpmhookTableDelItem(rpmhookTable *table, const char *name, /*@null@*/ rpmhookFunc func, /*@null@*/ void *data, int matchfunc, int matchdata) /*@modifies *table @*/ { int n = rpmhookTableFindBucket(table, name); rpmhookBucket bucket = &(*table)->bucket[n]; rpmhookItem item = bucket->item; rpmhookItem lastItem = NULL; rpmhookItem nextItem; while (item) { nextItem = item->next; /*@-branchstate@*/ if ((!matchfunc || item->func == func) && (!matchdata || item->data == data)) { free(item); if (lastItem) lastItem->next = nextItem; else bucket->item = nextItem; } else { lastItem = item; } /*@=branchstate@*/ /*@-usereleased@*/ item = nextItem; } if (!bucket->item) { free(bucket->name); bucket->name = NULL; (*table)->used--; } /*@=usereleased@*/ } static rpmhookArgs rpmhookArgsParse(const char *argt, va_list ap) /*@*/ { rpmhookArgs args = rpmhookArgsNew(strlen(argt)); int i; /*@-temptrans@*/ args->argt = argt; /*@=temptrans@*/ for (i = 0; i != args->argc; i++) { switch (argt[i]) { case 's': args->argv[i].s = va_arg(ap, char *); /*@switchbreak@*/ break; case 'i': args->argv[i].i = va_arg(ap, int); /*@switchbreak@*/ break; case 'f': args->argv[i].f = (float)va_arg(ap, double); /*@switchbreak@*/ break; case 'p': args->argv[i].p = va_arg(ap, void *); /*@switchbreak@*/ break; default: /*@-modfilesys @*/ fprintf(stderr, "error: unsupported type '%c' as " "a hook argument\n", argt[i]); /*@=modfilesys @*/ /*@switchbreak@*/ break; } } return args; } static void rpmhookTableCallArgs(rpmhookTable *table, const char *name, rpmhookArgs args) /*@modifies *table @*/ { int n = rpmhookTableFindBucket(table, name); rpmhookItem item = (*table)->bucket[n].item; while (item) { if (item->func(args, item->data) != 0) break; item = item->next; } } /*@unchecked@*/ /*@only@*/ /*@null@*/ static rpmhookTable globalTable = NULL; void rpmhookRegister(const char *name, rpmhookFunc func, void *data) /*@globals globalTable @*/ /*@modifies globalTable @*/ { 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); /*@-noeffect@*/ rpmhookTableCallArgs(&globalTable, name, args); /*@=noeffect@*/ (void) rpmhookArgsFree(args); va_end(ap); } } void rpmhookCallArgs(const char *name, rpmhookArgs args) { /*@-noeffect@*/ if (globalTable != NULL) rpmhookTableCallArgs(&globalTable, name, args); /*@=noeffect@*/ } /*@=bounds@*/