00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifndef lint
00021 static const char rcsid[] = "$Id: binds.c,v 1.30 2007-01-13 12:23:39 sven Exp $";
00022 #endif
00023
00024 #include <eggdrop/eggdrop.h>
00025
00026
00027 static bind_table_t *bind_table_list_head = NULL;
00028
00029
00030 static int bind_vcheck_hits(bind_table_t *table, flags_t *user_flags, const char *match, int *hits, va_list args);
00031
00032
00033 static void bind_table_really_del(bind_table_t *table);
00034 static void bind_entry_really_del(bind_table_t *table, bind_entry_t *entry);
00035
00036 bind_table_t *bind_table_list(void)
00037 {
00038 return bind_table_list_head;
00039 }
00040
00041 static int internal_bind_cleanup()
00042 {
00043 bind_table_t *table, *next_table;
00044 bind_entry_t *entry, *next_entry;
00045
00046 for (table = bind_table_list_head; table; table = next_table) {
00047 next_table = table->next;
00048 if (table->flags & BIND_DELETED) {
00049 bind_table_really_del(table);
00050 continue;
00051 }
00052 for (entry = table->entries; entry; entry = next_entry) {
00053 next_entry = entry->next;
00054 if (entry->flags & BIND_DELETED) bind_entry_really_del(table, entry);
00055 }
00056 }
00057 return(0);
00058 }
00059
00060 static void schedule_bind_cleanup()
00061 {
00062 garbage_add(internal_bind_cleanup, NULL, GARBAGE_ONCE);
00063 }
00064
00065 void kill_binds(void)
00066 {
00067 while (bind_table_list_head) bind_table_del(bind_table_list_head);
00068 }
00069
00070 int kill_binds_by_owner(egg_module_t *module, void *script)
00071 {
00072 int deleted = 0;
00073 bind_entry_t *entry;
00074 bind_table_t *table;
00075
00076 for (table = bind_table_list_head; table; table = table->next) {
00077 if (table->flags & BIND_DELETED) continue;
00078 for (entry = table->entries; entry; entry = entry->next) {
00079 if (entry->flags & BIND_DELETED) continue;
00080 if (entry->owner && entry->owner->module == module && (!script || entry->owner->client_data == script)) {
00081 deleted++;
00082 entry->flags |= BIND_DELETED;
00083 }
00084 }
00085 }
00086 internal_bind_cleanup();
00087 return deleted;
00088 }
00089
00090 bind_table_t *bind_table_add(const char *name, int nargs, const char *syntax, int match_type, int flags)
00091 {
00092 bind_table_t *table;
00093
00094 for (table = bind_table_list_head; table; table = table->next) {
00095 if (!strcmp(table->name, name)) break;
00096 }
00097
00098
00099 if (!table) {
00100 table = calloc(1, sizeof(*table));
00101 table->name = strdup(name);
00102 table->next = bind_table_list_head;
00103 bind_table_list_head = table;
00104 }
00105 else if (!(table->flags & BIND_FAKE)) return(table);
00106
00107 table->nargs = nargs;
00108 if (syntax) table->syntax = strdup(syntax);
00109 table->match_type = match_type;
00110 table->flags = flags;
00111 return(table);
00112 }
00113
00114 void bind_table_del(bind_table_t *table)
00115 {
00116 bind_table_t *cur;
00117
00118 for (cur = bind_table_list_head; cur; cur = cur->next) {
00119 if (!strcmp(table->name, cur->name)) break;
00120 }
00121
00122 egg_assert(cur != NULL);
00123
00124
00125 table->flags |= BIND_DELETED;
00126 schedule_bind_cleanup();
00127 }
00128
00129 static void bind_table_really_del(bind_table_t *table)
00130 {
00131 bind_table_t *cur, *prev;
00132 bind_entry_t *entry, *next;
00133
00134 for (prev = NULL, cur = bind_table_list_head; cur; prev = cur, cur = cur->next) {
00135 if (cur == table)
00136 break;
00137 }
00138
00139 if (cur == NULL) {
00140 putlog(LOG_DEBUG, "*", "bind table '%s' is marked for destroying but isn't found.", table->name);
00141 return;
00142 }
00143
00144
00145 if (prev == NULL) bind_table_list_head = table->next;
00146 else prev->next = table->next;
00147
00148 for (entry = table->entries; entry; entry = next) {
00149 next = entry->next;
00150 if (entry->owner && entry->owner->on_delete) entry->owner->on_delete(entry->owner, entry->client_data);
00151 if (entry->function_name) free(entry->function_name);
00152 if (entry->mask) free(entry->mask);
00153 free(entry);
00154 }
00155 if (table->name) free(table->name);
00156 if (table->syntax) free(table->syntax);
00157 free(table);
00158 }
00159
00160 bind_table_t *bind_table_lookup(const char *name)
00161 {
00162 bind_table_t *table;
00163
00164 for (table = bind_table_list_head; table; table = table->next) {
00165 if (!(table->flags & BIND_DELETED) && !strcmp(table->name, name)) break;
00166 }
00167 return(table);
00168 }
00169
00170 bind_table_t *bind_table_lookup_or_fake(const char *name)
00171 {
00172 bind_table_t *table;
00173
00174 table = bind_table_lookup(name);
00175 if (!table) table = bind_table_add(name, 0, NULL, 0, BIND_FAKE);
00176 return(table);
00177 }
00178
00179
00180 bind_entry_t *bind_entry_lookup(bind_table_t *table, const char *mask, const char *function_name, Function callback)
00181 {
00182 bind_entry_t *entry = NULL;
00183 int hit, searchall = 0;
00184
00185 if (!table) {
00186 searchall = 1;
00187 table = bind_table_list_head;
00188 }
00189
00190 for (; table; table = table->next) {
00191 for (entry = table->entries; entry; entry = entry->next) {
00192 if (entry->flags & BIND_DELETED) continue;
00193 else {
00194 hit = 0;
00195 if (!entry->mask || !strcmp(entry->mask, mask)) hit++;
00196 if (!entry->function_name || !strcmp(entry->function_name, function_name)) hit++;
00197 if (!callback || entry->callback == callback) hit++;
00198 if (hit == 3) break;
00199 }
00200 }
00201 if (entry || !searchall) break;
00202 }
00203 return(entry);
00204 }
00205
00206 int bind_entry_del(bind_table_t *table, const char *mask, const char *function_name, Function callback)
00207 {
00208 bind_entry_t *entry;
00209
00210 entry = bind_entry_lookup(table, mask, function_name, callback);
00211
00212
00213
00214 if (entry == NULL) {
00215 if (table) {
00216 putlog(LOG_DEBUG, "*", "A bind entry '%s/%s' is marked for destroying but isn't found in table '%s'.",
00217 mask, function_name, table->name);
00218 putlog(LOG_DEBUG, "*", "Current entries are:");
00219 for (entry = table->entries; entry; entry = entry->next) {
00220 putlog(LOG_DEBUG, "*", " %s/%s", entry->mask, entry->function_name);
00221 }
00222 } else {
00223 putlog(LOG_DEBUG, "*", "A bind entry '%s/%s' is marked for destroying but isn't found in any table.",
00224 mask, function_name);
00225 }
00226 return -1;
00227 }
00228
00229
00230
00231 entry->flags |= BIND_DELETED;
00232 schedule_bind_cleanup();
00233 return(0);
00234 }
00235
00236 static void bind_entry_really_del(bind_table_t *table, bind_entry_t *entry)
00237 {
00238 if (entry->next) entry->next->prev = entry->prev;
00239 if (entry->prev) entry->prev->next = entry->next;
00240 else table->entries = entry->next;
00241
00242 if (entry->owner && entry->owner->on_delete) entry->owner->on_delete(entry->owner, entry->client_data);
00243 if (entry->function_name) free(entry->function_name);
00244 if (entry->mask) free(entry->mask);
00245 free(entry);
00246 }
00247
00248
00249 int bind_entry_modify(bind_table_t *table, const char *mask, const char *function_name, const char *newflags, const char *newmask)
00250 {
00251 bind_entry_t *entry;
00252
00253 entry = bind_entry_lookup(table, mask, function_name, NULL);
00254 if (!entry) return(-1);
00255
00256
00257 if (newflags) flag_from_str(&entry->user_flags, newflags);
00258 if (newmask) str_redup(&entry->mask, newmask);
00259
00260 return(0);
00261 }
00262
00263
00264 int bind_entry_overwrite(bind_table_t *table, const char *mask, const char *function_name, Function callback, void *client_data, event_owner_t *owner)
00265 {
00266 bind_entry_t *entry;
00267
00268 entry = bind_entry_lookup(table, mask, function_name, NULL);
00269 if (!entry) return(-1);
00270
00271 if ((entry->client_data != client_data || entry->owner != owner) && entry->owner && entry->owner->on_delete) entry->owner->on_delete(entry->owner, entry->client_data);
00272 callback = callback;
00273 entry->client_data = client_data;
00274 entry->owner = owner;
00275 return(0);
00276 }
00277
00278 int bind_entry_add(bind_table_t *table, const char *flags, const char *mask, const char *function_name, int bind_flags, Function callback, void *client_data, event_owner_t *owner)
00279 {
00280 bind_entry_t *entry, *old_entry;
00281
00282 old_entry = bind_entry_lookup(table, mask, function_name, NULL);
00283
00284 if (old_entry) {
00285 if (table->flags & BIND_STACKABLE) {
00286 entry = calloc(1, sizeof(*entry));
00287 entry->prev = old_entry;
00288 entry->next = old_entry->next;
00289 old_entry->next = entry;
00290 if (entry->next) entry->next->prev = entry;
00291 }
00292 else {
00293 entry = old_entry;
00294 if (entry->owner && entry->owner->on_delete) entry->owner->on_delete(entry->owner, entry->client_data);
00295 if (entry->function_name) free(entry->function_name);
00296 if (entry->mask) free(entry->mask);
00297 }
00298 }
00299 else {
00300 for (old_entry = table->entries; old_entry && old_entry->next; old_entry = old_entry->next) {
00301 ;
00302 }
00303 entry = calloc(1, sizeof(*entry));
00304 if (old_entry) old_entry->next = entry;
00305 else table->entries = entry;
00306 entry->prev = old_entry;
00307 }
00308
00309 if (flags) flag_from_str(&entry->user_flags, flags);
00310 if (mask) entry->mask = strdup(mask);
00311 if (function_name) entry->function_name = strdup(function_name);
00312 entry->callback = callback;
00313 entry->client_data = client_data;
00314 entry->flags = bind_flags;
00315 entry->owner = owner;
00316
00317 return(0);
00318 }
00319
00320
00321 static int bind_entry_exec(bind_table_t *table, bind_entry_t *entry, void **al)
00322 {
00323 bind_entry_t *prev;
00324 int retval;
00325
00326
00327 entry->nhits++;
00328
00329
00330 if (entry->flags & BIND_WANTS_CD) {
00331 *al = entry->client_data;
00332 }
00333 else al++;
00334
00335 retval = entry->callback(al[0], al[1], al[2], al[3], al[4], al[5], al[6], al[7], al[8], al[9]);
00336
00337 if (table->match_type & (MATCH_MASK | MATCH_PARTIAL | MATCH_NONE)) return(retval);
00338
00339
00340 for (prev = entry->prev; prev; prev = prev->prev) {
00341 if (!(prev->flags & BIND_DELETED) && (prev->nhits >= entry->nhits)) break;
00342 }
00343
00344
00345 if (entry->prev != prev) {
00346
00347 if (entry->prev) entry->prev->next = entry->next;
00348 else table->entries = entry->next;
00349 if (entry->next) entry->next->prev = entry->prev;
00350
00351
00352 if (prev) {
00353 entry->next = prev->next;
00354 if (prev->next) prev->next->prev = entry;
00355 prev->next = entry;
00356 }
00357 else {
00358 entry->next = table->entries;
00359 table->entries = entry;
00360 }
00361 entry->prev = prev;
00362 if (entry->next) entry->next->prev = entry;
00363 }
00364 return(retval);
00365 }
00366
00367 int bind_check(bind_table_t *table, flags_t *user_flags, const char *match, ...)
00368 {
00369 va_list args;
00370 int ret;
00371
00372 va_start(args, match);
00373 ret = bind_vcheck_hits(table, user_flags, match, NULL, args);
00374 va_end(args);
00375
00376 return ret;
00377 }
00378
00379 int bind_check_hits(bind_table_t *table, flags_t *user_flags, const char *match, int *hits,...)
00380 {
00381 va_list args;
00382 int ret;
00383
00384 va_start(args, hits);
00385 ret = bind_vcheck_hits(table, user_flags, match, hits, args);
00386 va_end(args);
00387
00388 return ret;
00389 }
00390
00391 static int bind_vcheck_hits(bind_table_t *table, flags_t *user_flags, const char *match, int *hits, va_list ap)
00392 {
00393 void *args[11];
00394 bind_entry_t *entry, *next, *winner = NULL;
00395 int i, cmp, retval;
00396 int tie = 0, matchlen = 0;
00397
00398 for (i = 1; i <= table->nargs; i++) {
00399 args[i] = va_arg(ap, void *);
00400 }
00401
00402 if (hits) (*hits) = 0;
00403
00404
00405 retval = 0;
00406
00407
00408 if (table->match_type & MATCH_PARTIAL) matchlen = strlen(match);
00409
00410 for (entry = table->entries; entry; entry = next) {
00411 next = entry->next;
00412 if (entry->flags & BIND_DELETED) continue;
00413
00414
00415 if (table->match_type & MATCH_FLAGS) {
00416 if (!(entry->user_flags.builtin | entry->user_flags.udef)) cmp = 1;
00417 else if (!user_flags) cmp = 0;
00418 else if (entry->flags & MATCH_FLAGS_AND) cmp = flag_match_subset(&entry->user_flags, user_flags);
00419 else cmp = flag_match_partial(user_flags, &entry->user_flags);
00420 if (!cmp) continue;
00421 }
00422
00423 if (table->match_type & MATCH_NONE || !entry->mask) cmp = 0;
00424 else if (table->match_type & MATCH_MASK) {
00425 cmp = !wild_match_per(entry->mask, match);
00426 }
00427 else if (table->match_type & MATCH_PARTIAL) {
00428 cmp = 1;
00429 if (!strncasecmp(match, entry->mask, matchlen)) {
00430 winner = entry;
00431
00432 if (!entry->mask[matchlen]) {
00433 tie = 1;
00434 break;
00435 }
00436 else tie++;
00437 }
00438 }
00439 else {
00440 if (table->match_type & MATCH_CASE) cmp = strcmp(entry->mask, match);
00441 else cmp = strcasecmp(entry->mask, match);
00442 }
00443 if (cmp) continue;
00444
00445 if (hits) (*hits)++;
00446
00447 retval = bind_entry_exec(table, entry, args);
00448 if ((table->flags & BIND_BREAKABLE) && (retval & BIND_RET_BREAK)) break;
00449 }
00450
00451 if (winner && tie == 1) {
00452 if (hits) (*hits)++;
00453 retval = bind_entry_exec(table, winner, args);
00454 }
00455
00456 return(retval);
00457 }
00458
00459 void bind_add_list(const char *table_name, bind_list_t *cmds)
00460 {
00461 char name[50];
00462 bind_table_t *table;
00463
00464 table = bind_table_lookup_or_fake(table_name);
00465
00466 for (; cmds->callback; cmds++) {
00467 snprintf(name, 50, "*%s:%s", table->name, cmds->mask ? cmds->mask : "");
00468 name[49] = 0;
00469 bind_entry_add(table, cmds->user_flags, cmds->mask, name, 0, cmds->callback, NULL, NULL);
00470 }
00471 }
00472
00473 void bind_add_simple(const char *table_name, const char *flags, const char *mask, Function callback)
00474 {
00475 char name[50];
00476 bind_table_t *table;
00477
00478 table = bind_table_lookup_or_fake(table_name);
00479
00480 snprintf(name, sizeof(name), "*%s:%s", table->name, mask ? mask : "");
00481 name[sizeof(name)-1] = 0;
00482
00483 bind_entry_add(table, flags, mask, name, 0, callback, NULL, NULL);
00484 }
00485
00486 void bind_rem_list(const char *table_name, bind_list_t *cmds)
00487 {
00488 char name[50];
00489 bind_table_t *table;
00490
00491 table = bind_table_lookup(table_name);
00492 if (!table) return;
00493
00494 for (; cmds->callback; cmds++) {
00495 snprintf(name, sizeof(name), "*%s:%s", table->name, cmds->mask ? cmds->mask : "");
00496 name[sizeof(name)-1] = 0;
00497 bind_entry_del(table, cmds->mask, name, cmds->callback);
00498 }
00499 }
00500
00501 void bind_rem_simple(const char *table_name, const char *flags, const char *mask, Function callback)
00502 {
00503 char name[50];
00504 bind_table_t *table;
00505
00506 table = bind_table_lookup(table_name);
00507 if (!table) return;
00508
00509 snprintf(name, sizeof(name), "*%s:%s", table->name, mask ? mask : "");
00510 name[sizeof(name)-1] = 0;
00511 bind_entry_del(table, mask, name, callback);
00512 }