00001 #include "system.h"
00002
00003 #include <stdlib.h>
00004 #include <stdio.h>
00005 #include <string.h>
00006 #include <stdarg.h>
00007
00008 #include <rpmhook.h>
00009 #include "debug.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 for (; i != (*table)->size; i++) {
00125 if ((*table)->bucket[i].name == NULL)
00126 continue;
00127 n = rpmhookTableFindBucket(&newtable, (*table)->bucket[i].name);
00128 newtable->bucket[n].name = (*table)->bucket[i].name;
00129 newtable->bucket[n].item = (*table)->bucket[i].item;
00130 }
00131 newtable->used = (*table)->used;
00132
00133 free(*table);
00134
00135 *table = newtable;
00136 }
00137
00138 static void rpmhookTableAddItem(rpmhookTable *table, const char *name,
00139 rpmhookFunc func, void *data)
00140
00141 {
00142 int n = rpmhookTableFindBucket(table, name);
00143 rpmhookBucket bucket = &(*table)->bucket[n];
00144 rpmhookItem *item = &bucket->item;
00145 if (!bucket->name) {
00146 bucket->name = strdup(name);
00147 (*table)->used++;
00148 }
00149 while (*item) item = &(*item)->next;
00150 *item = xcalloc(1, sizeof(**item));
00151 (*item)->func = func;
00152
00153 (*item)->data = data;
00154
00155 }
00156
00157 static void rpmhookTableDelItem(rpmhookTable *table, const char *name,
00158 rpmhookFunc func, void *data,
00159 int matchfunc, int matchdata)
00160
00161 {
00162 int n = rpmhookTableFindBucket(table, name);
00163 rpmhookBucket bucket = &(*table)->bucket[n];
00164 rpmhookItem item = bucket->item;
00165 rpmhookItem lastItem = NULL;
00166 rpmhookItem nextItem;
00167 while (item) {
00168 nextItem = item->next;
00169 if ((!matchfunc || item->func == func) &&
00170 (!matchdata || item->data == data)) {
00171 free(item);
00172 if (lastItem)
00173 lastItem->next = nextItem;
00174 else
00175 bucket->item = nextItem;
00176 } else {
00177 lastItem = item;
00178 }
00179
00180 item = nextItem;
00181 }
00182 if (!bucket->item) {
00183 free(bucket->name);
00184 bucket->name = NULL;
00185 (*table)->used--;
00186 }
00187
00188 }
00189
00190 static rpmhookArgs rpmhookArgsParse(const char *argt, va_list ap)
00191
00192 {
00193 rpmhookArgs args = rpmhookArgsNew((int)strlen(argt));
00194 int i;
00195
00196
00197 args->argt = argt;
00198
00199 for (i = 0; i != args->argc; i++) {
00200 switch (argt[i]) {
00201 case 's':
00202 args->argv[i].s = va_arg(ap, char *);
00203 break;
00204 case 'i':
00205 args->argv[i].i = va_arg(ap, int);
00206 break;
00207 case 'f':
00208 args->argv[i].f = (float)va_arg(ap, double);
00209 break;
00210 case 'p':
00211 args->argv[i].p = va_arg(ap, void *);
00212 break;
00213 default:
00214
00215 fprintf(stderr, "error: unsupported type '%c' as "
00216 "a hook argument\n", argt[i]);
00217
00218 break;
00219 }
00220 }
00221 return args;
00222 }
00223
00224 static void rpmhookTableCallArgs(rpmhookTable *table, const char *name,
00225 rpmhookArgs args)
00226
00227 {
00228 int n = rpmhookTableFindBucket(table, name);
00229 rpmhookItem item = (*table)->bucket[n].item;
00230 while (item) {
00231 if (item->func(args, item->data) != 0)
00232 break;
00233 item = item->next;
00234 }
00235 }
00236
00237
00238 static rpmhookTable globalTable = NULL;
00239
00240 void rpmhookRegister(const char *name, rpmhookFunc func, void *data)
00241
00242
00243 {
00244 if (globalTable == NULL)
00245 globalTable = rpmhookTableNew(RPMHOOK_TABLE_INITSIZE);
00246 rpmhookTableAddItem(&globalTable, name, func, data);
00247 }
00248
00249 void rpmhookUnregister(const char *name, rpmhookFunc func, void *data)
00250 {
00251 if (globalTable != NULL)
00252 rpmhookTableDelItem(&globalTable, name, func, data, 1, 1);
00253 }
00254
00255 void rpmhookUnregisterAny(const char *name, rpmhookFunc func)
00256 {
00257 if (globalTable != NULL)
00258 rpmhookTableDelItem(&globalTable, name, func, NULL, 1, 0);
00259 }
00260
00261 void rpmhookUnregisterAll(const char *name)
00262 {
00263 if (globalTable != NULL)
00264 rpmhookTableDelItem(&globalTable, name, NULL, NULL, 0, 0);
00265 }
00266
00267 void rpmhookCall(const char *name, const char *argt, ...)
00268 {
00269 if (globalTable != NULL) {
00270 rpmhookArgs args;
00271 va_list ap;
00272 va_start(ap, argt);
00273 args = rpmhookArgsParse(argt, ap);
00274
00275 rpmhookTableCallArgs(&globalTable, name, args);
00276
00277 (void) rpmhookArgsFree(args);
00278 va_end(ap);
00279 }
00280 }
00281
00282 void rpmhookCallArgs(const char *name, rpmhookArgs args)
00283 {
00284
00285 if (globalTable != NULL)
00286 rpmhookTableCallArgs(&globalTable, name, args);
00287
00288 }