tclhash.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /*
  2. * tclhash.c -- handles:
  3. * bind and unbind
  4. * checking and triggering the various in-bot bindings
  5. * listing current bindings
  6. * adding/removing new binding tables
  7. * (non-Tcl) procedure lookups for msg/dcc/file commands
  8. * (Tcl) binding internal procedures to msg/dcc/file commands
  9. *
  10. */
  11. #include "eggmain.h"
  12. #include "tclhash.h"
  13. #include "debug.h"
  14. #include "chan.h"
  15. #include "users.h"
  16. #include "match.h"
  17. #include "egg_timer.h"
  18. #include <stdarg.h>
  19. extern struct dcc_t *dcc;
  20. extern int dcc_total;
  21. extern mycmds cmdlist[];
  22. extern int cmdi;
  23. /* The head of the bind table linked list. */
  24. static bind_table_t *bind_table_list_head = NULL;
  25. /* Garbage collection stuff. */
  26. static int check_bind_executing = 0;
  27. static int already_scheduled = 0;
  28. static void bind_table_really_del(bind_table_t *table);
  29. static void bind_entry_really_del(bind_table_t *table, bind_entry_t *entry);
  30. void binds_init(void)
  31. {
  32. bind_table_list_head = NULL;
  33. }
  34. static int internal_bind_cleanup()
  35. {
  36. bind_table_t *table, *next_table;
  37. bind_entry_t *entry, *next_entry;
  38. for (table = bind_table_list_head; table; table = next_table) {
  39. next_table = table->next;
  40. if (table->flags & BIND_DELETED) {
  41. bind_table_really_del(table);
  42. continue;
  43. }
  44. for (entry = table->entries; entry; entry = next_entry) {
  45. next_entry = entry->next;
  46. if (entry->flags & BIND_DELETED) bind_entry_really_del(table, entry);
  47. }
  48. }
  49. already_scheduled = 0;
  50. return(0);
  51. }
  52. static void schedule_bind_cleanup()
  53. {
  54. egg_timeval_t when;
  55. if (already_scheduled) return;
  56. already_scheduled = 1;
  57. when.sec = 0;
  58. when.usec = 0;
  59. timer_create(&when, internal_bind_cleanup);
  60. }
  61. void kill_binds(void)
  62. {
  63. while (bind_table_list_head) bind_table_del(bind_table_list_head);
  64. }
  65. bind_table_t *bind_table_add(const char *name, int nargs, const char *syntax, int match_type, int flags)
  66. {
  67. bind_table_t *table;
  68. for (table = bind_table_list_head; table; table = table->next) {
  69. if (!strcmp(table->name, name)) break;
  70. }
  71. /* If it doesn't exist, create it. */
  72. if (!table) {
  73. table = (bind_table_t *)calloc(1, sizeof(*table));
  74. table->name = strdup(name);
  75. table->next = bind_table_list_head;
  76. bind_table_list_head = table;
  77. }
  78. else if (!(table->flags & BIND_FAKE)) return(table);
  79. table->nargs = nargs;
  80. if (syntax) table->syntax = strdup(syntax);
  81. table->match_type = match_type;
  82. table->flags = flags;
  83. return(table);
  84. }
  85. void bind_table_del(bind_table_t *table)
  86. {
  87. bind_table_t *cur, *prev;
  88. for (prev = NULL, cur = bind_table_list_head; cur; prev = cur, cur = cur->next) {
  89. if (!strcmp(table->name, cur->name)) break;
  90. }
  91. /* If it's found, remove it from the list. */
  92. if (cur) {
  93. if (prev) prev->next = cur->next;
  94. else bind_table_list_head = cur->next;
  95. }
  96. /* Now delete it. */
  97. if (check_bind_executing) {
  98. table->flags |= BIND_DELETED;
  99. schedule_bind_cleanup();
  100. }
  101. else {
  102. bind_table_really_del(table);
  103. }
  104. }
  105. static void bind_table_really_del(bind_table_t *table)
  106. {
  107. bind_entry_t *entry, *next;
  108. free(table->name);
  109. for (entry = table->entries; entry; entry = next) {
  110. next = entry->next;
  111. free(entry->function_name);
  112. free(entry->mask);
  113. free(entry);
  114. }
  115. free(table);
  116. }
  117. bind_table_t *bind_table_lookup(const char *name)
  118. {
  119. bind_table_t *table;
  120. for (table = bind_table_list_head; table; table = table->next) {
  121. if (!(table->flags & BIND_DELETED) && !strcmp(table->name, name)) break;
  122. }
  123. return(table);
  124. }
  125. bind_table_t *bind_table_lookup_or_fake(const char *name)
  126. {
  127. bind_table_t *table;
  128. table = bind_table_lookup(name);
  129. if (!table) table = bind_table_add(name, 0, NULL, 0, BIND_FAKE);
  130. return(table);
  131. }
  132. /* Look up a bind entry based on either function name or id. */
  133. bind_entry_t *bind_entry_lookup(bind_table_t *table, int id, const char *mask, const char *function_name)
  134. {
  135. bind_entry_t *entry;
  136. for (entry = table->entries; entry; entry = entry->next) {
  137. if (entry->flags & BIND_DELETED) continue;
  138. if (entry->id == id || (!strcmp(entry->mask, mask) && !strcmp(entry->function_name, function_name))) break;
  139. }
  140. return(entry);
  141. }
  142. int bind_entry_del(bind_table_t *table, int id, const char *mask, const char *function_name, void *cdata)
  143. {
  144. bind_entry_t *entry;
  145. entry = bind_entry_lookup(table, id, mask, function_name);
  146. if (!entry) return(-1);
  147. /* Delete it. */
  148. if (check_bind_executing) {
  149. entry->flags |= BIND_DELETED;
  150. schedule_bind_cleanup();
  151. }
  152. else bind_entry_really_del(table, entry);
  153. return(0);
  154. }
  155. static void bind_entry_really_del(bind_table_t *table, bind_entry_t *entry)
  156. {
  157. if (entry->next) entry->next->prev = entry->prev;
  158. if (entry->prev) entry->prev->next = entry->next;
  159. else table->entries = entry->next;
  160. free(entry->function_name);
  161. free(entry->mask);
  162. memset(entry, 0, sizeof(*entry));
  163. free(entry);
  164. }
  165. /* Modify a bind entry's flags and mask. */
  166. int bind_entry_modify(bind_table_t *table, int id, const char *mask, const char *function_name, const char *newflags, const char *newmask)
  167. {
  168. bind_entry_t *entry;
  169. entry = bind_entry_lookup(table, id, mask, function_name);
  170. if (!entry) return(-1);
  171. /* Modify it. */
  172. free(entry->mask);
  173. entry->mask = strdup(newmask);
  174. entry->user_flags.match = FR_GLOBAL | FR_CHAN;
  175. break_down_flags(newflags, &(entry->user_flags), NULL);
  176. return(0);
  177. }
  178. 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)
  179. {
  180. bind_entry_t *entry, *old_entry;
  181. old_entry = bind_entry_lookup(table, -1, mask, function_name);
  182. if (old_entry) {
  183. if (table->flags & BIND_STACKABLE) {
  184. entry = (bind_entry_t *)calloc(1, sizeof(*entry));
  185. entry->prev = old_entry;
  186. entry->next = old_entry->next;
  187. old_entry->next = entry;
  188. if (entry->next) entry->next->prev = entry;
  189. }
  190. else {
  191. entry = old_entry;
  192. free(entry->function_name);
  193. free(entry->mask);
  194. }
  195. }
  196. else {
  197. for (old_entry = table->entries; old_entry && old_entry->next; old_entry = old_entry->next) {
  198. ; /* empty loop */
  199. }
  200. entry = (bind_entry_t *)calloc(1, sizeof(*entry));
  201. if (old_entry) old_entry->next = entry;
  202. else table->entries = entry;
  203. entry->prev = old_entry;
  204. }
  205. entry->mask = strdup(mask);
  206. entry->function_name = strdup(function_name);
  207. entry->callback = callback;
  208. entry->client_data = client_data;
  209. entry->flags = bind_flags;
  210. entry->user_flags.match = FR_GLOBAL | FR_CHAN;
  211. break_down_flags(flags, &(entry->user_flags), NULL);
  212. return(0);
  213. }
  214. /* Execute a bind entry with the given argument list. */
  215. static int bind_entry_exec(bind_table_t *table, bind_entry_t *entry, void **al)
  216. {
  217. bind_entry_t *prev;
  218. /* Give this entry a hit. */
  219. entry->nhits++;
  220. /* Search for the last entry that isn't deleted. */
  221. for (prev = entry->prev; prev; prev = prev->prev) {
  222. if (!(prev->flags & BIND_DELETED) && (prev->nhits >= entry->nhits)) break;
  223. }
  224. /* See if this entry is more popular than the preceding one. */
  225. if (entry->prev != prev) {
  226. /* Remove entry. */
  227. if (entry->prev) entry->prev->next = entry->next;
  228. else table->entries = entry->next;
  229. if (entry->next) entry->next->prev = entry->prev;
  230. /* Re-add in correct position. */
  231. if (prev) {
  232. entry->next = prev->next;
  233. if (prev->next) prev->next->prev = entry;
  234. prev->next = entry;
  235. }
  236. else {
  237. entry->next = table->entries;
  238. table->entries = entry;
  239. }
  240. entry->prev = prev;
  241. if (entry->next) entry->next->prev = entry;
  242. }
  243. /* Does the callback want client data? */
  244. if (entry->flags & BIND_WANTS_CD) {
  245. *al = entry->client_data;
  246. }
  247. else al++;
  248. return entry->callback(al[0], al[1], al[2], al[3], al[4], al[5], al[6], al[7], al[8], al[9]);
  249. }
  250. int check_bind(bind_table_t *table, const char *match, struct flag_record *flags, ...)
  251. {
  252. void *args[11];
  253. bind_entry_t *entry, *next;
  254. int i, cmp, retval;
  255. va_list ap;
  256. Assert(table);
  257. check_bind_executing++;
  258. va_start(ap, flags);
  259. for (i = 1; i <= table->nargs; i++) {
  260. args[i] = va_arg(ap, void *);
  261. }
  262. va_end(ap);
  263. /* Default return value is 0 */
  264. retval = 0;
  265. /* If it's a partial bind, we have to find the closest match. */
  266. if (table->match_type & MATCH_PARTIAL) {
  267. int matchlen, masklen, tie;
  268. bind_entry_t *winner;
  269. matchlen = strlen(match);
  270. tie = 0;
  271. winner = NULL;
  272. for (entry = table->entries; entry; entry = entry->next) {
  273. if (entry->flags & BIND_DELETED) continue;
  274. if (table->flags & BIND_USE_ATTR) {
  275. if (table->flags & BIND_STRICT_ATTR) cmp = flagrec_eq(&entry->user_flags, flags);
  276. else cmp = flagrec_ok(&entry->user_flags, flags);
  277. if (!cmp) continue;
  278. }
  279. masklen = strlen(entry->mask);
  280. if (!strncasecmp(match, entry->mask, masklen < matchlen ? masklen : matchlen)) {
  281. winner = entry;
  282. if (masklen == matchlen) break;
  283. else if (tie) return(-1);
  284. else tie = 1;
  285. }
  286. }
  287. if (winner) retval = bind_entry_exec(table, winner, args);
  288. else retval = -1;
  289. check_bind_executing--;
  290. return(retval);
  291. }
  292. for (entry = table->entries; entry; entry = next) {
  293. next = entry->next;
  294. if (entry->flags & BIND_DELETED) continue;
  295. if (table->match_type & MATCH_MASK) {
  296. cmp = !wild_match_per((unsigned char *)entry->mask, (unsigned char *)match);
  297. }
  298. else {
  299. if (table->match_type & MATCH_CASE) cmp = strcmp(entry->mask, match);
  300. else cmp = strcasecmp(entry->mask, match);
  301. }
  302. if (cmp) continue; /* Doesn't match. */
  303. /* Check flags. */
  304. if (table->flags & BIND_USE_ATTR) {
  305. if (table->flags & BIND_STRICT_ATTR) cmp = flagrec_eq(&entry->user_flags, flags);
  306. else cmp = flagrec_ok(&entry->user_flags, flags);
  307. if (!cmp) continue;
  308. }
  309. retval = bind_entry_exec(table, entry, args);
  310. if ((table->flags & BIND_BREAKABLE) && (retval & BIND_RET_BREAK)) {
  311. check_bind_executing--;
  312. return(retval);
  313. }
  314. }
  315. check_bind_executing--;
  316. return(retval);
  317. }
  318. void add_builtins(const char *table_name, cmd_t *cmds)
  319. {
  320. char name[50];
  321. bind_table_t *table;
  322. table = bind_table_lookup_or_fake(table_name);
  323. for (; cmds->name; cmds++) {
  324. /* add BT_dcc cmds to cmdlist[] :: add to the help system.. */
  325. if (!strcmp(table->name, "dcc") && !(cmds->nohelp)) {
  326. cmdlist[cmdi].name = cmds->name;
  327. cmdlist[cmdi].flags.match = FR_GLOBAL | FR_CHAN;
  328. break_down_flags(cmds->flags, &(cmdlist[cmdi].flags), NULL);
  329. cmdi++;
  330. }
  331. egg_snprintf(name, 50, "*%s:%s", table->name, cmds->funcname ? cmds->funcname : cmds->name);
  332. bind_entry_add(table, cmds->flags, cmds->name, name, 0, cmds->func, NULL);
  333. }
  334. }
  335. void rem_builtins(const char *table_name, cmd_t *cmds)
  336. {
  337. char name[50];
  338. bind_table_t *table;
  339. table = bind_table_lookup(table_name);
  340. if (!table) return;
  341. for (; cmds->name; cmds++) {
  342. sprintf(name, "*%s:%s", table->name, cmds->funcname ? cmds->funcname : cmds->name);
  343. bind_entry_del(table, -1, cmds->name, name, NULL);
  344. }
  345. }