[4.0] Use strip (instead of eu-strip) to support --strip-debug of *.so at build time
[platform/upstream/rpm.git] / rpmio / rpmhook.c
1 #include "system.h"
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdarg.h>
7
8 #include <rpm/rpmstring.h>
9 #include "rpmio/rpmhook.h"
10
11 #include "debug.h"
12
13 #define RPMHOOK_TABLE_INITSIZE  256
14 #define RPMHOOK_BUCKET_INITSIZE 5
15
16 typedef struct rpmhookItem_s {
17     rpmhookFunc func;
18     void *data;
19     struct rpmhookItem_s *next;
20 } * rpmhookItem;
21
22 typedef struct rpmhookBucket_s {
23     unsigned long hash;
24     char *name;
25     rpmhookItem item;
26 } * rpmhookBucket;
27
28 typedef struct rpmhookTable_s {
29     int size;
30     int used;
31     struct rpmhookBucket_s bucket[1];
32 } * rpmhookTable;
33
34
35 rpmhookArgs rpmhookArgsNew(int argc)
36 {
37     rpmhookArgs args = (rpmhookArgs) xcalloc(1,
38                         sizeof(*args) + sizeof(args->argv) * (argc-1));
39     args->argc = argc;
40     return args;
41 }
42
43 rpmhookArgs rpmhookArgsFree(rpmhookArgs args)
44 {
45     if (args != NULL)
46         free(args);
47     return NULL;
48 }
49
50 static rpmhookTable rpmhookTableNew(int size)
51 {
52     rpmhookTable table = (rpmhookTable) xcalloc(1,
53                 sizeof(*table) + sizeof(table->bucket) * (size-1));
54     table->size = size;
55     return table;
56 }
57
58 #if 0
59 static rpmhookTable rpmhookTableFree(rpmhookTable table)
60 {
61     rpmhookItem item, nextItem;
62     int i;
63     for (i = 0; i != table->size; i++) {
64         if (table->bucket[i].name == NULL)
65             continue;
66         free(table->bucket[i].name);
67         item = table->bucket[i].item;
68         while (item) {
69             nextItem = item->next;
70             free(item);
71             item = nextItem;
72         }
73     }
74     free(table);
75     return NULL;
76 }
77 #endif
78
79 static void rpmhookTableRehash(rpmhookTable *table);
80
81 static int rpmhookTableFindBucket(rpmhookTable *table, const char *name)
82 {
83     /* Hash based on http://www.isthe.com/chongo/tech/comp/fnv/ */
84     unsigned long perturb;
85     unsigned long hash = 0;
86     unsigned char *bp = (unsigned char *)name;
87     unsigned char *be = bp + strlen(name);
88     rpmhookBucket bucket;
89     int ret;
90
91     if (((*table)->used/2)*3 > (*table)->size)
92         rpmhookTableRehash(table);
93     while (bp < be) {
94         hash ^= (unsigned long)*bp++;
95         hash *= (unsigned long)0x01000193;
96     }
97     perturb = hash;
98     ret = hash % (*table)->size;
99     bucket = &(*table)->bucket[ret];
100     while (bucket->name &&
101             (bucket->hash != hash || !rstreq(bucket->name, name))) {
102         /* Collision resolution based on Python's perturb scheme. */
103         ret = ((ret << 2) + ret + perturb + 1) % (*table)->size;
104         perturb >>= 5;
105         bucket = &(*table)->bucket[ret];
106     }
107     if (!bucket->name)
108         bucket->hash = hash;
109     return ret;
110 }
111
112 static void rpmhookTableRehash(rpmhookTable *table)
113 {
114     rpmhookTable newtable = rpmhookTableNew((*table)->size*2);
115     int n, i = 0;
116
117     for (; i != (*table)->size; i++) {
118         if ((*table)->bucket[i].name == NULL)
119             continue;
120         n = rpmhookTableFindBucket(&newtable, (*table)->bucket[i].name);
121         newtable->bucket[n].name = (*table)->bucket[i].name;
122         newtable->bucket[n].item = (*table)->bucket[i].item;
123     }
124     newtable->used = (*table)->used;
125     free(*table);
126     *table = newtable;
127 }
128
129 static void rpmhookTableAddItem(rpmhookTable *table, const char *name,
130                                 rpmhookFunc func, void *data)
131 {
132     int n = rpmhookTableFindBucket(table, name);
133     rpmhookBucket bucket = &(*table)->bucket[n];
134     rpmhookItem *item = &bucket->item;
135     if (!bucket->name) {
136         bucket->name = xstrdup(name);
137         (*table)->used++;
138     }
139     while (*item) item = &(*item)->next;
140     *item = xcalloc(1, sizeof(**item));
141     (*item)->func = func;
142     (*item)->data = data;
143 }
144
145 static void rpmhookTableDelItem(rpmhookTable *table, const char *name,
146                 rpmhookFunc func, void *data,
147                 int matchfunc, int matchdata)
148 {
149     int n = rpmhookTableFindBucket(table, name);
150     rpmhookBucket bucket = &(*table)->bucket[n];
151     rpmhookItem item = bucket->item;
152     rpmhookItem lastItem = NULL;
153     rpmhookItem nextItem;
154     while (item) {
155         nextItem = item->next;
156         if ((!matchfunc || item->func == func) &&
157             (!matchdata || item->data == data)) {
158             free(item);
159             if (lastItem)
160                 lastItem->next = nextItem;
161             else
162                 bucket->item = nextItem;
163         } else {
164             lastItem = item;
165         }
166         item = nextItem;
167     }
168     if (!bucket->item) {
169         free(bucket->name);
170         bucket->name = NULL;
171         (*table)->used--;
172     }
173 }
174
175 static rpmhookArgs rpmhookArgsParse(const char *argt, va_list ap)
176 {
177     rpmhookArgs args = rpmhookArgsNew(strlen(argt));
178     int i;
179
180     args->argt = argt;
181     for (i = 0; i != args->argc; i++) {
182         switch (argt[i]) {
183             case 's':
184                 args->argv[i].s = va_arg(ap, char *);
185                 break;
186             case 'i':
187                 args->argv[i].i = va_arg(ap, int);
188                 break;
189             case 'f':
190                 args->argv[i].f = (float)va_arg(ap, double);
191                 break;
192             case 'p':
193                 args->argv[i].p = va_arg(ap, void *);
194                 break;
195             default:
196                 fprintf(stderr, "error: unsupported type '%c' as "
197                                 "a hook argument\n", argt[i]);
198                 break;
199         }
200     }
201     return args;
202 }
203
204 static void rpmhookTableCallArgs(rpmhookTable *table, const char *name,
205                         rpmhookArgs args)
206 {
207     int n = rpmhookTableFindBucket(table, name);
208     rpmhookItem item = (*table)->bucket[n].item;
209     rpmhookItem next;
210     while (item) {
211         next = item->next;
212         if (item->func(args, item->data) != 0)
213             break;
214         item = next;
215     }
216 }
217
218 static rpmhookTable globalTable = NULL;
219
220 void rpmhookRegister(const char *name, rpmhookFunc func, void *data)
221 {
222     if (globalTable == NULL)
223         globalTable = rpmhookTableNew(RPMHOOK_TABLE_INITSIZE);
224     rpmhookTableAddItem(&globalTable, name, func, data);
225 }
226
227 void rpmhookUnregister(const char *name, rpmhookFunc func, void *data)
228 {
229     if (globalTable != NULL)
230         rpmhookTableDelItem(&globalTable, name, func, data, 1, 1);
231 }
232
233 void rpmhookUnregisterAny(const char *name, rpmhookFunc func)
234 {
235     if (globalTable != NULL)
236         rpmhookTableDelItem(&globalTable, name, func, NULL, 1, 0);
237 }
238
239 void rpmhookUnregisterAll(const char *name)
240 {
241     if (globalTable != NULL)
242         rpmhookTableDelItem(&globalTable, name, NULL, NULL, 0, 0);
243 }
244
245 void rpmhookCall(const char *name, const char *argt, ...)
246 {
247     if (globalTable != NULL) {
248         rpmhookArgs args;
249         va_list ap;
250         va_start(ap, argt);
251         args = rpmhookArgsParse(argt, ap);
252         rpmhookTableCallArgs(&globalTable, name, args);
253         (void) rpmhookArgsFree(args);
254         va_end(ap);
255     }
256 }
257
258 void rpmhookCallArgs(const char *name, rpmhookArgs args)
259 {
260     if (globalTable != NULL)
261         rpmhookTableCallArgs(&globalTable, name, args);
262 }