iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / main / event.c
blob9b3bc5dca19e06e715a4e6fc42c841753eabdee3
1 /* Event handling functions */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include "elinks.h"
9 #include "main/event.h"
10 #include "util/error.h"
11 #include "util/hash.h"
12 #include "util/memory.h"
13 #include "util/snprintf.h"
14 #include "util/string.h"
17 /* First, we should set some terminology:
19 * o event -> Message being [triggerred] by some ELinks part and [catched]
20 * by various random other ones.
22 * o event hook -> Device being deployed by various ELinks parts,
23 * associated with certain [event It [catches] that event by having a
24 * handler executed when the [event] is triggerred.
26 * o event chain -> Line of [event hook]s associated with a given [event
27 * The [hook]s are ordered by [priority Each hook returns whenever should
28 * the chain continue or that no other events in the chain should be
29 * triggered (TODO).
32 struct event_handler {
33 /* The function to be called with the event data. */
34 event_hook_T callback;
36 /* The @priority of this handler. */
37 int priority;
39 /* Handler specific data. */
40 void *data;
43 struct event {
44 /* The event name has to be unique. */
45 unsigned char *name;
47 /* There are @count event @handlers all ordered by priority. */
48 struct event_handler *handlers;
49 unsigned int count;
51 /* The unique event id and position in events. */
52 int id;
55 static struct event *events = NULL;
56 static unsigned int eventssize = 0;
57 static struct hash *event_hash = NULL;
59 /* TODO: This should be tuned to the number of events. When we will have a lot
60 * of them, this should be some big enough number to reduce unnecessary
61 * slavery of CPU on the startup. Then after all main modules will be
62 * initialized and their events will be registered, we could call something
63 * like adjust_events_list() which will tune it to the exactly needed number.
64 * This should be also called after each new plugin loaded. */
65 #define EVENT_GRANULARITY 0x07
67 #define realloc_events(ptr, size) \
68 mem_align_alloc(ptr, (size), (size) + 1, EVENT_GRANULARITY)
70 static inline int
71 invalid_event_id(register int id)
73 return (id < 0 || id >= eventssize || id == EVENT_NONE);
76 int
77 register_event(unsigned char *name)
79 int id = get_event_id(name);
80 struct event *event;
81 int namelen;
83 if (id != EVENT_NONE) return id;
85 event = events;
86 if (!realloc_events(&events, eventssize)) return EVENT_NONE;
88 /* If @events got relocated update the hash. */
89 if (event != events) {
90 for (id = 0; id < eventssize; id++) {
91 struct hash_item *item;
92 int len = strlen(events[id].name);
94 item = get_hash_item(event_hash, events[id].name, len);
96 if (item) item->value = &events[id];
100 event = &events[eventssize];
102 namelen = strlen(name);
103 event->name = memacpy(name, namelen);
104 if (!event->name) return EVENT_NONE;
106 if (!add_hash_item(event_hash, event->name, namelen, event)) {
107 mem_free(event->name);
108 event->name = NULL;
109 return EVENT_NONE;
112 event->handlers = NULL;
113 event->count = 0;
114 event->id = eventssize++;
116 return event->id;
120 get_event_id(unsigned char *name)
122 struct hash_item *item;
123 int namelen;
125 assertm(name && name[0], "Empty or missing event name");
126 if_assert_failed return EVENT_NONE;
128 if (!event_hash) return EVENT_NONE;
130 namelen = strlen(name);
131 item = get_hash_item(event_hash, name, namelen);
132 if (item) {
133 struct event *event = item->value;
135 assertm(event != NULL, "Hash item with no value");
136 if_assert_failed return EVENT_NONE;
138 return event->id;
141 return EVENT_NONE;
144 unsigned char *
145 get_event_name(int id)
147 if (invalid_event_id(id)) return NULL;
149 return events[id].name;
152 static void
153 trigger_event_va(int id, va_list ap_init)
155 int i;
156 struct event_handler *ev_handler;
158 if (invalid_event_id(id)) return;
160 ev_handler = events[id].handlers;
161 for (i = 0; i < events[id].count; i++, ev_handler++) {
162 enum evhook_status ret;
163 va_list ap;
165 VA_COPY(ap, ap_init);
166 ret = ev_handler->callback(ap, ev_handler->data);
167 va_end(ap);
169 if (ret == EVENT_HOOK_STATUS_LAST) return;
173 void
174 trigger_event(int id, ...)
176 va_list ap;
178 va_start(ap, id);
179 trigger_event_va(id, ap);
180 va_end(ap);
183 void
184 trigger_event_name(unsigned char *name, ...)
186 va_list ap;
187 int id = get_event_id(name);
189 va_start(ap, name);
190 trigger_event_va(id, ap);
191 va_end(ap);
194 static inline void
195 move_event_handler(struct event *event, int to, int from)
197 int d = int_max(to, from);
199 memmove(&event->handlers[to], &event->handlers[from],
200 (event->count - d) * sizeof(*event->handlers));
204 register_event_hook(int id, event_hook_T callback, int priority, void *data)
206 struct event *event;
207 int i;
209 assert(callback);
210 if_assert_failed return EVENT_NONE;
212 if (invalid_event_id(id)) return EVENT_NONE;
214 event = &events[id];
216 for (i = 0; i < event->count; i++)
217 if (event->handlers[i].callback == callback) break;
219 if (i == event->count) {
220 struct event_handler *eh;
222 eh = mem_realloc(event->handlers,
223 (event->count + 1) * sizeof(*event->handlers));
225 if (!eh) return EVENT_NONE;
227 event->handlers = eh;
228 event->count++;
229 } else {
230 move_event_handler(event, i, i + 1);
233 for (i = 0; i < event->count - 1; i++)
234 if (event->handlers[i].priority < priority) break;
236 move_event_handler(event, i + 1, i);
238 event->handlers[i].callback = callback;
239 event->handlers[i].priority = priority;
240 event->handlers[i].data = data;
242 return id;
245 void
246 unregister_event_hook(int id, event_hook_T callback)
248 struct event *event;
250 assert(callback);
251 if_assert_failed return;
253 if (invalid_event_id(id)) return;
255 event = &events[id];
256 if (event->handlers) {
257 int i;
259 for (i = 0; i < event->count; i++) {
260 if (event->handlers[i].callback != callback)
261 continue;
263 move_event_handler(event, i, i + 1);
264 event->count--;
265 if (!event->count) {
266 mem_free(event->handlers);
267 event->handlers = NULL;
268 } else {
269 struct event_handler *eh;
271 eh = mem_realloc(event->handlers,
272 event->count * sizeof(*event->handlers));
273 if (eh) event->handlers = eh;
276 break;
281 void
282 register_event_hooks(struct event_hook_info *hooks)
284 int i;
286 for (i = 0; hooks[i].name; i++) {
287 int id = register_event(hooks[i].name);
289 if (id == EVENT_NONE) continue;
291 register_event_hook(id, hooks[i].callback, hooks[i].priority,
292 hooks[i].data);
296 void
297 unregister_event_hooks(struct event_hook_info *hooks)
299 int i;
301 for (i = 0; hooks[i].name; i++) {
302 int id = get_event_id(hooks[i].name);
304 if (id == EVENT_NONE) continue;
306 unregister_event_hook(id, hooks[i].callback);
310 void
311 init_event(void)
313 event_hash = init_hash8();
316 void
317 done_event(void)
319 int i;
321 for (i = 0; i < eventssize; i++) {
322 mem_free_if(events[i].handlers);
323 mem_free(events[i].name);
326 mem_free_set(&events, NULL);
328 if (event_hash) free_hash(&event_hash);
329 eventssize = 0;