00001
00002 #include "system.h"
00003
00004 #include <stdlib.h>
00005 #include <stdio.h>
00006 #include <string.h>
00007 #include <stdarg.h>
00008
00009 #include <rpmhook.h>
00010
00011 #define RPMHOOK_TABLE_INITSIZE 256
00012 #define RPMHOOK_BUCKET_INITSIZE 5
00013
00014 typedef struct rpmhookItem_s {
00015 rpmhookFunc func;
00016 void *data;
00017 struct rpmhookItem_s *next;
00018 } * rpmhookItem;
00019
00020 typedef struct rpmhookBucket_s {
00021 unsigned long hash;
00022
00023 char *name;
00024 rpmhookItem item;
00025 } * rpmhookBucket;
00026
00027 typedef struct rpmhookTable_s {
00028 int size;
00029 int used;
00030 struct rpmhookBucket_s bucket[1];
00031 } * rpmhookTable;
00032
00033
00034 rpmhookArgs rpmhookArgsNew(int argc)
00035 {
00036 rpmhookArgs args = (rpmhookArgs) xcalloc(1,
00037 sizeof(*args) + sizeof(args->argv) * (argc-1));
00038 args->argc = argc;
00039 return args;
00040 }
00041
00042 rpmhookArgs rpmhookArgsFree(rpmhookArgs args)
00043 {
00044 if (args != NULL)
00045 free(args);
00046 return NULL;
00047 }
00048
00049
00050 static rpmhookTable rpmhookTableNew(int size)
00051
00052 {
00053 rpmhookTable table = (rpmhookTable) xcalloc(1,
00054 sizeof(*table) + sizeof(table->bucket) * (size-1));
00055 table->size = size;
00056 return table;
00057 }
00058
00059 #if 0
00060 static rpmhookTable rpmhookTableFree(rpmhookTable table)
00061
00062 {
00063 rpmhookItem item, nextItem;
00064 int i;
00065 for (i = 0; i != table->size; i++) {
00066 if (table->bucket[i].name == NULL)
00067 continue;
00068 free(table->bucket[i].name);
00069 item = table->bucket[i].item;
00070 while (item) {
00071 nextItem = item->next;
00072 free(item);
00073 item = nextItem;
00074 }
00075 }
00076 free(table);
00077 return NULL;
00078 }
00079 #endif
00080
00081 static void rpmhookTableRehash(rpmhookTable *table)
00082 ;
00083
00084 static int rpmhookTableFindBucket(rpmhookTable *table, const char *name)
00085
00086 {
00087
00088 unsigned long perturb;
00089 unsigned long hash = 0;
00090 unsigned char *bp = (unsigned char *)name;
00091 unsigned char *be = bp + strlen(name);
00092 rpmhookBucket bucket;
00093 int ret;
00094
00095 if (((*table)->used/2)*3 > (*table)->size)
00096 rpmhookTableRehash(table);
00097 while (bp < be) {
00098 hash ^= (unsigned long)*bp++;
00099 hash *= (unsigned long)0x01000193;
00100 }
00101 perturb = hash;
00102 ret = hash % (*table)->size;
00103 bucket = &(*table)->bucket[ret];
00104 while (bucket->name &&
00105 (bucket->hash != hash || strcmp(bucket->name, name) != 0)) {
00106
00107
00108 ret = ((ret << 2) + ret + perturb + 1) % (*table)->size;
00109
00110 perturb >>= 5;
00111 bucket = &(*table)->bucket[ret];
00112 }
00113 if (!bucket->name)
00114 bucket->hash = hash;
00115 return ret;
00116 }
00117
00118 static void rpmhookTableRehash(rpmhookTable *table)
00119
00120 {
00121 rpmhookTable newtable = rpmhookTableNew((*table)->size*2);
00122 int n, i = 0;
00123
00124
00125 for (; i != (*table)->size; i++) {
00126 if ((*table)->bucket[i].name == NULL)
00127 continue;
00128 n = rpmhookTableFindBucket(&newtable, (*table)->bucket[i].name);
00129 newtable->bucket[n].name = (*table)->bucket[i].name;
00130 newtable->bucket[n].item = (*table)->bucket[i].item;
00131 }
00132
00133 newtable->used = (*table)->used;
00134
00135 free(*table);
00136
00137 *table = newtable;
00138 }
00139
00140 static void rpmhookTableAddItem(rpmhookTable *table, const char *name,
00141 rpmhookFunc func, void *data)
00142
00143 {
00144 int n = rpmhookTableFindBucket(table, name);
00145 rpmhookBucket bucket = &(*table)->bucket[n];
00146 rpmhookItem *item = &bucket->item;
00147 if (!bucket->name) {
00148 bucket->name = strdup(name);
00149 (*table)->used++;
00150 }
00151 while (*item) item = &(*item)->next;
00152 *item = xcalloc(1, sizeof(**item));
00153 (*item)->func = func;
00154
00155 (*item)->data = data;
00156
00157 }
00158
00159 static void rpmhookTableDelItem(rpmhookTable *table, const char *name,
00160 rpmhookFunc func, void *data,
00161 int matchfunc, int matchdata)
00162
00163 {
00164 int n = rpmhookTableFindBucket(table, name);
00165 rpmhookBucket bucket = &(*table)->bucket[n];
00166 rpmhookItem item = bucket->item;
00167 rpmhookItem lastItem = NULL;
00168 rpmhookItem nextItem;
00169 while (item) {
00170 nextItem = item->next;
00171
00172 if ((!matchfunc || item->func == func) &&
00173 (!matchdata || item->data == data)) {
00174 free(item);
00175 if (lastItem)
00176 lastItem->next = nextItem;
00177 else
00178 bucket->item = nextItem;
00179 } else {
00180 lastItem = item;
00181 }
00182
00183
00184 item = nextItem;
00185 }
00186 if (!bucket->item) {
00187 free(bucket->name);
00188 bucket->name = NULL;
00189 (*table)->used--;
00190 }
00191
00192 }
00193
00194 static rpmhookArgs rpmhookArgsParse(const char *argt, va_list ap)
00195
00196 {
00197 rpmhookArgs args = rpmhookArgsNew(strlen(argt));
00198 int i;
00199
00200
00201 args->argt = argt;
00202
00203 for (i = 0; i != args->argc; i++) {
00204 switch (argt[i]) {
00205 case 's':
00206 args->argv[i].s = va_arg(ap, char *);
00207 break;
00208 case 'i':
00209 args->argv[i].i = va_arg(ap, int);
00210 break;
00211 case 'f':
00212 args->argv[i].f = (float)va_arg(ap, double);
00213 break;
00214 case 'p':
00215 args->argv[i].p = va_arg(ap, void *);
00216 break;
00217 default:
00218
00219 fprintf(stderr, "error: unsupported type '%c' as "
00220 "a hook argument\n", argt[i]);
00221
00222 break;
00223 }
00224 }
00225 return args;
00226 }
00227
00228 static void rpmhookTableCallArgs(rpmhookTable *table, const char *name,
00229 rpmhookArgs args)
00230
00231 {
00232 int n = rpmhookTableFindBucket(table, name);
00233 rpmhookItem item = (*table)->bucket[n].item;
00234 while (item) {
00235 if (item->func(args, item->data) != 0)
00236 break;
00237 item = item->next;
00238 }
00239 }
00240
00241
00242 static rpmhookTable globalTable = NULL;
00243
00244 void rpmhookRegister(const char *name, rpmhookFunc func, void *data)
00245
00246
00247 {
00248 if (globalTable == NULL)
00249 globalTable = rpmhookTableNew(RPMHOOK_TABLE_INITSIZE);
00250 rpmhookTableAddItem(&globalTable, name, func, data);
00251 }
00252
00253 void rpmhookUnregister(const char *name, rpmhookFunc func, void *data)
00254 {
00255 if (globalTable != NULL)
00256 rpmhookTableDelItem(&globalTable, name, func, data, 1, 1);
00257 }
00258
00259 void rpmhookUnregisterAny(const char *name, rpmhookFunc func)
00260 {
00261 if (globalTable != NULL)
00262 rpmhookTableDelItem(&globalTable, name, func, NULL, 1, 0);
00263 }
00264
00265 void rpmhookUnregisterAll(const char *name)
00266 {
00267 if (globalTable != NULL)
00268 rpmhookTableDelItem(&globalTable, name, NULL, NULL, 0, 0);
00269 }
00270
00271 void rpmhookCall(const char *name, const char *argt, ...)
00272 {
00273 if (globalTable != NULL) {
00274 rpmhookArgs args;
00275 va_list ap;
00276 va_start(ap, argt);
00277 args = rpmhookArgsParse(argt, ap);
00278
00279 rpmhookTableCallArgs(&globalTable, name, args);
00280
00281 (void) rpmhookArgsFree(args);
00282 va_end(ap);
00283 }
00284 }
00285
00286 void rpmhookCallArgs(const char *name, rpmhookArgs args)
00287 {
00288
00289 if (globalTable != NULL)
00290 rpmhookTableCallArgs(&globalTable, name, args);
00291
00292 }
00293