rpm 5.3.7

rpmio/rpmhook.c

Go to the documentation of this file.
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 }