rpm
5.2.1
|
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 /*@relnull@*/ 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 /*@only@*/ 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 /*@modifies *table @*/; 00083 00084 static int rpmhookTableFindBucket(rpmhookTable *table, const char *name) 00085 /*@modifies *table @*/ 00086 { 00087 /* Hash based on http://www.isthe.com/chongo/tech/comp/fnv/ */ 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 /* Collision resolution based on Python's perturb scheme. */ 00107 /*@-shiftimplementation@*/ 00108 ret = ((ret << 2) + ret + perturb + 1) % (*table)->size; 00109 /*@=shiftimplementation@*/ 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 /*@modifies *table @*/ 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 /*@-unqualifiedtrans@*/ 00133 free(*table); 00134 /*@=unqualifiedtrans@*/ 00135 *table = newtable; 00136 } 00137 00138 static void rpmhookTableAddItem(rpmhookTable *table, const char *name, 00139 rpmhookFunc func, void *data) 00140 /*@modifies *table @*/ 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 /*@-temptrans@*/ 00153 (*item)->data = data; 00154 /*@=temptrans@*/ 00155 } 00156 00157 static void rpmhookTableDelItem(rpmhookTable *table, const char *name, 00158 /*@null@*/ rpmhookFunc func, /*@null@*/ void *data, 00159 int matchfunc, int matchdata) 00160 /*@modifies *table @*/ 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 /*@-usereleased@*/ 00180 item = nextItem; 00181 } 00182 if (!bucket->item) { 00183 free(bucket->name); 00184 bucket->name = NULL; 00185 (*table)->used--; 00186 } 00187 /*@=usereleased@*/ 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 /*@-temptrans@*/ 00197 args->argt = argt; 00198 /*@=temptrans@*/ 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 /*@switchbreak@*/ break; 00204 case 'i': 00205 args->argv[i].i = va_arg(ap, int); 00206 /*@switchbreak@*/ break; 00207 case 'f': 00208 args->argv[i].f = (float)va_arg(ap, double); 00209 /*@switchbreak@*/ break; 00210 case 'p': 00211 args->argv[i].p = va_arg(ap, void *); 00212 /*@switchbreak@*/ break; 00213 default: 00214 /*@-modfilesys @*/ 00215 fprintf(stderr, "error: unsupported type '%c' as " 00216 "a hook argument\n", argt[i]); 00217 /*@=modfilesys @*/ 00218 /*@switchbreak@*/ break; 00219 } 00220 } 00221 return args; 00222 } 00223 00224 static void rpmhookTableCallArgs(rpmhookTable *table, const char *name, 00225 rpmhookArgs args) 00226 /*@modifies *table @*/ 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 /*@unchecked@*/ /*@only@*/ /*@null@*/ 00238 static rpmhookTable globalTable = NULL; 00239 00240 void rpmhookRegister(const char *name, rpmhookFunc func, void *data) 00241 /*@globals globalTable @*/ 00242 /*@modifies globalTable @*/ 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 /*@-noeffect@*/ 00275 rpmhookTableCallArgs(&globalTable, name, args); 00276 /*@=noeffect@*/ 00277 (void) rpmhookArgsFree(args); 00278 va_end(ap); 00279 } 00280 } 00281 00282 void rpmhookCallArgs(const char *name, rpmhookArgs args) 00283 { 00284 /*@-noeffect@*/ 00285 if (globalTable != NULL) 00286 rpmhookTableCallArgs(&globalTable, name, args); 00287 /*@=noeffect@*/ 00288 }