tclhash.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  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 "main.h"
  12. #include "chan.h"
  13. #include "users.h"
  14. #include "match.c"
  15. extern Tcl_Interp *interp;
  16. extern struct dcc_t *dcc;
  17. extern struct userrec *userlist;
  18. extern int dcc_total;
  19. extern time_t now;
  20. extern mycmds cmds[];
  21. extern int cmdi;
  22. extern char dcc_prefix[];
  23. p_tcl_bind_list bind_table_list;
  24. p_tcl_bind_list H_chat, H_act, H_bcst, H_chon, H_chof,
  25. H_load, H_unld, H_link, H_disc, H_dcc, H_chjn, H_chpt,
  26. H_bot, H_time, H_nkch, H_away, H_note, H_filt, H_event;
  27. static int builtin_2char();
  28. static int builtin_3char();
  29. static int builtin_5int();
  30. static int builtin_char();
  31. static int builtin_chpt();
  32. static int builtin_chjn();
  33. static int builtin_idxchar();
  34. static int builtin_charidx();
  35. static int builtin_chat();
  36. static int builtin_dcc();
  37. /* Allocate and initialise a chunk of memory.
  38. */
  39. static inline void *n_malloc_null(int size, const char *file, int line)
  40. {
  41. #ifdef DEBUG_MEM
  42. # define nmalloc_null(size) n_malloc_null(size, __FILE__, __LINE__)
  43. void *ptr = n_malloc(size, file, line);
  44. #else
  45. # define nmalloc_null(size) n_malloc_null(size, NULL, 0)
  46. void *ptr = nmalloc(size);
  47. #endif
  48. egg_memset(ptr, 0, size);
  49. return ptr;
  50. }
  51. /* Delete trigger/command.
  52. */
  53. static inline void tcl_cmd_delete(tcl_cmd_t *tc)
  54. {
  55. nfree(tc->func_name);
  56. nfree(tc);
  57. }
  58. /* Delete bind and its elements.
  59. */
  60. static inline void tcl_bind_mask_delete(tcl_bind_mask_t *tm)
  61. {
  62. tcl_cmd_t *tc, *tc_next;
  63. for (tc = tm->first; tc; tc = tc_next) {
  64. tc_next = tc->next;
  65. tcl_cmd_delete(tc);
  66. }
  67. nfree(tm->mask);
  68. nfree(tm);
  69. }
  70. /* Delete bind list and its elements.
  71. */
  72. static inline void tcl_bind_list_delete(tcl_bind_list_t *tl)
  73. {
  74. tcl_bind_mask_t *tm, *tm_next;
  75. for (tm = tl->first; tm; tm = tm_next) {
  76. tm_next = tm->next;
  77. tcl_bind_mask_delete(tm);
  78. }
  79. nfree(tl);
  80. }
  81. inline void garbage_collect_tclhash(void)
  82. {
  83. tcl_bind_list_t *tl, *tl_next, *tl_prev;
  84. tcl_bind_mask_t *tm, *tm_next, *tm_prev;
  85. tcl_cmd_t *tc, *tc_next, *tc_prev;
  86. for (tl = bind_table_list, tl_prev = NULL; tl; tl = tl_next) {
  87. tl_next = tl->next;
  88. if (tl->flags & HT_DELETED) {
  89. if (tl_prev)
  90. tl_prev->next = tl->next;
  91. else
  92. bind_table_list = tl->next;
  93. tcl_bind_list_delete(tl);
  94. } else {
  95. for (tm = tl->first, tm_prev = NULL; tm; tm = tm_next) {
  96. tm_next = tm->next;
  97. if (!(tm->flags & TBM_DELETED)) {
  98. for (tc = tm->first, tc_prev = NULL; tc; tc = tc_next) {
  99. tc_next = tc->next;
  100. if (tc->attributes & TC_DELETED) {
  101. if (tc_prev)
  102. tc_prev->next = tc->next;
  103. else
  104. tm->first = tc->next;
  105. tcl_cmd_delete(tc);
  106. } else
  107. tc_prev = tc;
  108. }
  109. }
  110. /* Delete the bind when it's marked as deleted or
  111. when it's empty. */
  112. if ((tm->flags & TBM_DELETED) || tm->first == NULL) {
  113. if (tm_prev)
  114. tm_prev->next = tm->next;
  115. else
  116. tl->first = tm_next;
  117. tcl_bind_mask_delete(tm);
  118. } else
  119. tm_prev = tm;
  120. }
  121. tl_prev = tl;
  122. }
  123. }
  124. }
  125. static inline int tcl_cmd_expmem(tcl_cmd_t *tc)
  126. {
  127. int tot;
  128. tot = sizeof(*tc);
  129. if (tc->func_name)
  130. tot += strlen(tc->func_name) + 1;
  131. return tot;
  132. }
  133. static inline int tcl_bind_mask_expmem(tcl_bind_mask_t *tm)
  134. {
  135. int tot = 0;
  136. tcl_cmd_t *tc;
  137. for (tc = tm->first; tc; tc = tc->next)
  138. tot += tcl_cmd_expmem(tc);
  139. if (tm->mask)
  140. tot += strlen(tm->mask) + 1;
  141. tot += sizeof(*tm);
  142. return tot;
  143. }
  144. static inline int tcl_bind_list_expmem(tcl_bind_list_t *tl)
  145. {
  146. int tot = 0;
  147. tcl_bind_mask_t *tm;
  148. for (tm = tl->first; tm; tm = tm->next)
  149. tot += tcl_bind_mask_expmem(tm);
  150. tot += sizeof(*tl);
  151. return tot;
  152. }
  153. int expmem_tclhash(void)
  154. {
  155. int tot = 0;
  156. tcl_bind_list_t *tl;
  157. for (tl = bind_table_list; tl; tl = tl->next)
  158. tot += tcl_bind_list_expmem(tl);
  159. return tot;
  160. }
  161. extern dcc_cmd_t C_dcc[];
  162. static int tcl_bind();
  163. static cd_tcl_cmd cd_cmd_table[] = {
  164. {"bind", tcl_bind, (void *)0},
  165. {"unbind", tcl_bind, (void *)1},
  166. {0}
  167. };
  168. void init_bind(void)
  169. {
  170. bind_table_list = NULL;
  171. Context;
  172. add_cd_tcl_cmds(cd_cmd_table);
  173. H_unld = add_bind_table("unld", HT_STACKABLE, builtin_char);
  174. H_time = add_bind_table("time", HT_STACKABLE, builtin_5int);
  175. H_note = add_bind_table("note", 0, builtin_3char);
  176. H_nkch = add_bind_table("nkch", HT_STACKABLE, builtin_2char);
  177. H_load = add_bind_table("load", HT_STACKABLE, builtin_char);
  178. H_link = add_bind_table("link", HT_STACKABLE, builtin_2char);
  179. H_filt = add_bind_table("filt", HT_STACKABLE, builtin_idxchar);
  180. H_disc = add_bind_table("disc", HT_STACKABLE, builtin_char);
  181. H_dcc = add_bind_table("dcc", 0, builtin_dcc);
  182. H_chpt = add_bind_table("chpt", HT_STACKABLE, builtin_chpt);
  183. H_chon = add_bind_table("chon", HT_STACKABLE, builtin_charidx);
  184. H_chof = add_bind_table("chof", HT_STACKABLE, builtin_charidx);
  185. H_chjn = add_bind_table("chjn", HT_STACKABLE, builtin_chjn);
  186. H_chat = add_bind_table("chat", HT_STACKABLE, builtin_chat);
  187. H_bot = add_bind_table("bot", 0, builtin_3char);
  188. H_bcst = add_bind_table("bcst", HT_STACKABLE, builtin_chat);
  189. H_away = add_bind_table("away", HT_STACKABLE, builtin_chat);
  190. H_act = add_bind_table("act", HT_STACKABLE, builtin_chat);
  191. H_event = add_bind_table("evnt", HT_STACKABLE, builtin_char);
  192. add_builtins_dcc(H_dcc, C_dcc);
  193. Context;
  194. }
  195. void kill_bind(void)
  196. {
  197. tcl_bind_list_t *tl, *tl_next;
  198. rem_builtins_dcc(H_dcc, C_dcc);
  199. for (tl = bind_table_list; tl; tl = tl_next) {
  200. tl_next = tl->next;
  201. if (!(tl->flags |= HT_DELETED))
  202. putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name);
  203. tcl_bind_list_delete(tl);
  204. }
  205. bind_table_list = NULL;
  206. }
  207. tcl_bind_list_t *add_bind_table(const char *nme, int flg, Function func)
  208. {
  209. tcl_bind_list_t *tl, *tl_prev;
  210. int v;
  211. /* Do not allow coders to use bind table names longer than
  212. 4 characters. */
  213. Assert(strlen(nme) <= 4);
  214. for (tl = bind_table_list, tl_prev = NULL; tl; tl_prev = tl, tl = tl->next) {
  215. if (tl->flags & HT_DELETED)
  216. continue;
  217. v = egg_strcasecmp(tl->name, nme);
  218. if (!v)
  219. return tl; /* Duplicate, just return old value. */
  220. if (v > 0)
  221. break; /* New. Insert at start of list. */
  222. }
  223. tl = nmalloc_null(sizeof(tcl_bind_list_t));
  224. strcpy(tl->name, nme);
  225. tl->flags = flg;
  226. tl->func = func;
  227. if (tl_prev) {
  228. tl->next = tl_prev->next;
  229. tl_prev->next = tl;
  230. } else {
  231. tl->next = bind_table_list;
  232. bind_table_list = tl;
  233. }
  234. putlog(LOG_DEBUG, "*", "Allocated bind table %s (flags %d)", nme, flg);
  235. return tl;
  236. }
  237. void del_bind_table(tcl_bind_list_t *tl_which)
  238. {
  239. tcl_bind_list_t *tl;
  240. for (tl = bind_table_list; tl; tl = tl->next) {
  241. if (tl->flags & HT_DELETED)
  242. continue;
  243. if (tl == tl_which) {
  244. tl->flags |= HT_DELETED;
  245. putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name);
  246. return;
  247. }
  248. }
  249. putlog(LOG_DEBUG, "*", "??? Tried to delete not listed bind table ???");
  250. }
  251. tcl_bind_list_t *find_bind_table(const char *nme)
  252. {
  253. tcl_bind_list_t *tl;
  254. int v;
  255. for (tl = bind_table_list; tl; tl = tl->next) {
  256. if (tl->flags & HT_DELETED)
  257. continue;
  258. v = egg_strcasecmp(tl->name, nme);
  259. if (!v)
  260. return tl;
  261. if (v > 0)
  262. return NULL;
  263. }
  264. return NULL;
  265. }
  266. static void dump_bind_tables(Tcl_Interp *irp)
  267. {
  268. tcl_bind_list_t *tl;
  269. u_8bit_t i;
  270. for (tl = bind_table_list, i = 0; tl; tl = tl->next) {
  271. if (tl->flags & HT_DELETED)
  272. continue;
  273. if (i)
  274. Tcl_AppendResult(irp, ", ", NULL);
  275. else
  276. i = 1;
  277. Tcl_AppendResult(irp, tl->name, NULL);
  278. }
  279. }
  280. static int unbind_bind_entry(tcl_bind_list_t *tl, const char *flags,
  281. const char *cmd, const char *proc)
  282. {
  283. tcl_bind_mask_t *tm;
  284. /* Search for matching bind in bind list. */
  285. for (tm = tl->first; tm; tm = tm->next) {
  286. if (tm->flags & TBM_DELETED)
  287. continue;
  288. if (!strcmp(cmd, tm->mask))
  289. break; /* Found it! fall out! */
  290. }
  291. if (tm) {
  292. tcl_cmd_t *tc;
  293. /* Search for matching proc in bind. */
  294. for (tc = tm->first; tc; tc = tc->next) {
  295. if (tc->attributes & TC_DELETED)
  296. continue;
  297. if (!egg_strcasecmp(tc->func_name, proc)) {
  298. /* Erase proc regardless of flags. */
  299. tc->attributes |= TC_DELETED;
  300. return 1; /* Match. */
  301. }
  302. }
  303. }
  304. return 0; /* No match. */
  305. }
  306. /* Add command (remove old one if necessary)
  307. */
  308. static int bind_bind_entry(tcl_bind_list_t *tl, const char *flags,
  309. const char *cmd, const char *proc)
  310. {
  311. tcl_cmd_t *tc;
  312. tcl_bind_mask_t *tm, *tm_last;
  313. Context;
  314. /* Search for matching bind in bind list. */
  315. for (tm = tl->first, tm_last = NULL; tm; tm_last = tm, tm = tm->next) {
  316. if (tm->flags & TBM_DELETED)
  317. continue;
  318. if (!strcmp(cmd, tm->mask))
  319. break; /* Found it! fall out! */
  320. }
  321. /* Create bind if it doesn't exist yet. */
  322. if (!tm) {
  323. tm = nmalloc_null(sizeof(tcl_bind_mask_t));
  324. tm->mask = nmalloc(strlen(cmd) + 1);
  325. strcpy(tm->mask, cmd);
  326. /* Link into linked list of binds. */
  327. tm->next = tl->first;
  328. tl->first = tm;
  329. }
  330. /* Proc already defined? If so, replace. */
  331. for (tc = tm->first; tc; tc = tc->next) {
  332. if (tc->attributes & TC_DELETED)
  333. continue;
  334. if (!egg_strcasecmp(tc->func_name, proc)) {
  335. tc->flags.match = FR_GLOBAL | FR_CHAN;
  336. break_down_flags(flags, &(tc->flags), NULL);
  337. return 1;
  338. }
  339. }
  340. /* If this bind list is not stackable, remove the
  341. old entry from this bind. */
  342. if (!(tl->flags & HT_STACKABLE)) {
  343. for (tc = tm->first; tc; tc = tc->next) {
  344. if (tc->attributes & TC_DELETED)
  345. continue;
  346. /* NOTE: We assume there's only one not-yet-deleted
  347. entry. */
  348. tc->attributes |= TC_DELETED;
  349. break;
  350. }
  351. }
  352. tc = nmalloc_null(sizeof(tcl_cmd_t));
  353. tc->flags.match = FR_GLOBAL | FR_CHAN;
  354. break_down_flags(flags, &(tc->flags), NULL);
  355. tc->func_name = nmalloc(strlen(proc) + 1);
  356. strcpy(tc->func_name, proc);
  357. /* Link into linked list of the bind's command list. */
  358. tc->next = tm->first;
  359. tm->first = tc;
  360. return 1;
  361. }
  362. static int tcl_getbinds(tcl_bind_list_t *tl_kind, const char *name)
  363. {
  364. tcl_bind_mask_t *tm;
  365. for (tm = tl_kind->first; tm; tm = tm->next) {
  366. if (tm->flags & TBM_DELETED)
  367. continue;
  368. if (!egg_strcasecmp(tm->mask, name)) {
  369. tcl_cmd_t *tc;
  370. for (tc = tm->first; tc; tc = tc->next) {
  371. if (tc->attributes & TC_DELETED)
  372. continue;
  373. Tcl_AppendElement(interp, tc->func_name);
  374. }
  375. break;
  376. }
  377. }
  378. return TCL_OK;
  379. }
  380. static int tcl_bind STDVAR
  381. {
  382. tcl_bind_list_t *tl;
  383. /* Note: `cd' defines what tcl_bind is supposed do: 0 stands for
  384. bind and 1 stands for unbind. */
  385. if ((long int) cd == 1)
  386. BADARGS(5, 5, " type flags cmd/mask procname");
  387. else
  388. BADARGS(4, 5, " type flags cmd/mask ?procname?");
  389. tl = find_bind_table(argv[1]);
  390. if (!tl) {
  391. Tcl_AppendResult(irp, "bad type, should be one of: ", NULL);
  392. dump_bind_tables(irp);
  393. return TCL_ERROR;
  394. }
  395. if ((long int) cd == 1) {
  396. if (!unbind_bind_entry(tl, argv[2], argv[3], argv[4])) {
  397. /* Don't error if trying to re-unbind a builtin */
  398. if (argv[4][0] != '*' || argv[4][4] != ':' ||
  399. strcmp(argv[3], &argv[4][5]) || strncmp(argv[1], &argv[4][1], 3)) {
  400. Tcl_AppendResult(irp, "no such binding", NULL);
  401. return TCL_ERROR;
  402. }
  403. }
  404. } else {
  405. if (argc == 4)
  406. return tcl_getbinds(tl, argv[3]);
  407. bind_bind_entry(tl, argv[2], argv[3], argv[4]);
  408. }
  409. Tcl_AppendResult(irp, argv[3], NULL);
  410. return TCL_OK;
  411. }
  412. int check_validity(char *nme, Function func)
  413. {
  414. char *p;
  415. tcl_bind_list_t *tl;
  416. if (*nme != '*')
  417. return 0;
  418. p = strchr(nme + 1, ':');
  419. if (p == NULL)
  420. return 0;
  421. *p = 0;
  422. tl = find_bind_table(nme + 1);
  423. *p = ':';
  424. if (!tl)
  425. return 0;
  426. if (tl->func != func)
  427. return 0;
  428. return 1;
  429. }
  430. static int builtin_3char STDVAR
  431. {
  432. Function F = (Function) cd;
  433. BADARGS(4, 4, " from to args");
  434. CHECKVALIDITY(builtin_3char);
  435. F(argv[1], argv[2], argv[3]);
  436. return TCL_OK;
  437. }
  438. static int builtin_2char STDVAR
  439. {
  440. Function F = (Function) cd;
  441. BADARGS(3, 3, " nick msg");
  442. CHECKVALIDITY(builtin_2char);
  443. F(argv[1], argv[2]);
  444. return TCL_OK;
  445. }
  446. static int builtin_5int STDVAR
  447. {
  448. Function F = (Function) cd;
  449. BADARGS(6, 6, " min hrs dom mon year");
  450. CHECKVALIDITY(builtin_5int);
  451. F(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]));
  452. return TCL_OK;
  453. }
  454. static int builtin_char STDVAR
  455. {
  456. Function F = (Function) cd;
  457. BADARGS(2, 2, " handle");
  458. CHECKVALIDITY(builtin_char);
  459. F(argv[1]);
  460. return TCL_OK;
  461. }
  462. static int builtin_chpt STDVAR
  463. {
  464. Function F = (Function) cd;
  465. BADARGS(3, 3, " bot nick sock");
  466. CHECKVALIDITY(builtin_chpt);
  467. F(argv[1], argv[2], atoi(argv[3]));
  468. return TCL_OK;
  469. }
  470. static int builtin_chjn STDVAR
  471. {
  472. Function F = (Function) cd;
  473. BADARGS(6, 6, " bot nick chan# flag&sock host");
  474. CHECKVALIDITY(builtin_chjn);
  475. F(argv[1], argv[2], atoi(argv[3]), argv[4][0],
  476. argv[4][0] ? atoi(argv[4] + 1) : 0, argv[5]);
  477. return TCL_OK;
  478. }
  479. static int builtin_idxchar STDVAR
  480. {
  481. Function F = (Function) cd;
  482. int idx;
  483. char *r;
  484. BADARGS(3, 3, " idx args");
  485. CHECKVALIDITY(builtin_idxchar);
  486. if (atoi(argv[1]) < 0) { /* this is a remote-simul idx */
  487. idx = atoi(argv[1]) * -1;
  488. } else {
  489. idx = findidx(atoi(argv[1]));
  490. }
  491. if (idx < 0) {
  492. Tcl_AppendResult(irp, "invalid idx", NULL);
  493. return TCL_ERROR;
  494. }
  495. r = (((char *(*)()) F) (idx, argv[2]));
  496. Tcl_ResetResult(irp);
  497. Tcl_AppendResult(irp, r, NULL);
  498. return TCL_OK;
  499. }
  500. int findanyidx(register int z)
  501. {
  502. register int j;
  503. for (j = 0; j < dcc_total; j++)
  504. if (dcc[j].sock == z)
  505. return j;
  506. return -1;
  507. }
  508. static int builtin_charidx STDVAR
  509. {
  510. Function F = (Function) cd;
  511. int idx;
  512. BADARGS(3, 3, " handle idx");
  513. CHECKVALIDITY(builtin_charidx);
  514. idx = findanyidx(atoi(argv[2]));
  515. if (idx < 0) {
  516. Tcl_AppendResult(irp, "invalid idx", NULL);
  517. return TCL_ERROR;
  518. }
  519. Tcl_AppendResult(irp, int_to_base10(F(argv[1], idx)), NULL);
  520. return TCL_OK;
  521. }
  522. static int builtin_chat STDVAR
  523. {
  524. Function F = (Function) cd;
  525. int ch;
  526. BADARGS(4, 4, " handle idx text");
  527. CHECKVALIDITY(builtin_chat);
  528. ch = atoi(argv[2]);
  529. F(argv[1], ch, argv[3]);
  530. return TCL_OK;
  531. }
  532. static int builtin_dcc STDVAR
  533. {
  534. int idx;
  535. Function F = (Function) cd;
  536. BADARGS(4, 4, " hand idx param");
  537. if (atoi(argv[2]) < 0) { /* this is a remote-simul idx */
  538. idx = atoi(argv[2]) * -1;
  539. } else {
  540. idx = findidx(atoi(argv[2]));
  541. }
  542. if (idx < 0) {
  543. Tcl_AppendResult(irp, "invalid idx", NULL);
  544. return TCL_ERROR;
  545. }
  546. if (F == NULL) {
  547. Tcl_AppendResult(irp, "break", NULL);
  548. return TCL_OK;
  549. }
  550. /* Check if it's a password change, if so, don't show the password. We
  551. * don't need pretty formats here, as it's only for debugging purposes.
  552. */
  553. debug4(STR("tcl: builtin dcc call: %s %s %s %s"), argv[0], argv[1], argv[2],
  554. (!strcmp(argv[0] + 5, "newpass") ||
  555. !strcmp(argv[0] + 5, "chpass")) ? "[something]" : argv[3]);
  556. (F) (dcc[idx].user, idx, argv[3]);
  557. Tcl_ResetResult(irp);
  558. Tcl_AppendResult(irp, "0", NULL);
  559. return TCL_OK;
  560. }
  561. /* trigger (execute) a proc */
  562. static int trigger_bind(const char *proc, const char *param)
  563. {
  564. int x;
  565. /* We now try to debug the Tcl_VarEval() call below by remembering both
  566. * the called proc name and it's parameters. This should render us a bit
  567. * less helpless when we see context dumps.
  568. */
  569. {
  570. const char *msg = "Tcl proc: %s, param: %s";
  571. char *buf;
  572. Context;
  573. buf = nmalloc(strlen(msg) + (proc ? strlen(proc) : 6)
  574. + (param ? strlen(param) : 6) + 1);
  575. sprintf(buf, msg, proc ? proc : "<null>", param ? param : "<null>");
  576. ContextNote(buf);
  577. nfree(buf);
  578. }
  579. x = Tcl_VarEval(interp, proc, param, NULL);
  580. Context;
  581. if (x == TCL_ERROR) {
  582. if (strlen(interp->result) > 400)
  583. interp->result[400] = 0;
  584. putlog(LOG_MISC, "*", "Tcl error [%s]: %s", proc, interp->result);
  585. return BIND_EXECUTED;
  586. } else {
  587. if (!strcmp(interp->result, "break"))
  588. return BIND_EXEC_BRK;
  589. return (atoi(interp->result) > 0) ? BIND_EXEC_LOG : BIND_EXECUTED;
  590. }
  591. }
  592. int check_tcl_bind(tcl_bind_list_t *tl, const char *match,
  593. struct flag_record *atr, const char *param, int match_type)
  594. {
  595. tcl_bind_mask_t *tm, *tm_last = NULL, *tm_p = NULL;
  596. int cnt = 0;
  597. char *proc = NULL, *fullmatch = NULL;
  598. tcl_cmd_t *tc, *htc = NULL;
  599. int finish = 0, atrok, x, ok;
  600. for (tm = tl->first; tm && !finish; tm_last = tm, tm = tm->next) {
  601. if (tm->flags & TBM_DELETED)
  602. continue;
  603. /* Find out whether this bind matches the mask or provides
  604. the the requested atcributes, depending on the specified
  605. requirements. */
  606. switch (match_type & 0x03) {
  607. case MATCH_PARTIAL:
  608. ok = !egg_strncasecmp(match, tm->mask, strlen(match));
  609. break;
  610. case MATCH_EXACT:
  611. ok = !egg_strcasecmp(match, tm->mask);
  612. break;
  613. case MATCH_CASE:
  614. ok = !strcmp(match, tm->mask);
  615. break;
  616. case MATCH_MASK:
  617. ok = wild_match_per((unsigned char *) tm->mask, (unsigned char *) match);
  618. break;
  619. default:
  620. ok = 0;
  621. }
  622. if (!ok)
  623. continue; /* This bind does not match. */
  624. if (match_type & BIND_STACKABLE) {
  625. /* Could be multiple commands/triggers. */
  626. for (tc = tm->first; tc; tc = tc->next) {
  627. if (match_type & BIND_USE_ATTR) {
  628. /* Check whether the provided flags suffice for
  629. this command/trigger. */
  630. if (match_type & BIND_HAS_BUILTINS)
  631. atrok = flagrec_ok(&tc->flags, atr);
  632. else
  633. atrok = flagrec_eq(&tc->flags, atr);
  634. } else
  635. atrok = 1;
  636. if (atrok) {
  637. cnt++;
  638. tc->hits++;
  639. tm_p = tm_last;
  640. Tcl_SetVar(interp, "lastbind", (char *) tm->mask, TCL_GLOBAL_ONLY);
  641. x = trigger_bind(tc->func_name, param);
  642. Context;
  643. if (match_type & BIND_ALTER_ARGS) {
  644. if (interp->result == NULL || !interp->result[0])
  645. return x;
  646. } else if ((match_type & BIND_WANTRET) && x == BIND_EXEC_LOG)
  647. return x;
  648. }
  649. }
  650. /* Apart from MATCH_MASK, currently no match type allows us to match
  651. against more than one bind. So if this isn't MATCH_MASK then exit
  652. the loop now. */
  653. /* This will suffice until we have stackable partials. */
  654. if ((match_type & 3) != MATCH_MASK)
  655. finish = 1;
  656. } else {
  657. /* Search for valid entry. */
  658. for (tc = tm->first; tc; tc = tc->next)
  659. if (!(tc->attributes & TC_DELETED))
  660. break;
  661. if (tc) {
  662. /* Check whether the provided flags suffice for this
  663. command/trigger. */
  664. if (match_type & BIND_USE_ATTR) {
  665. if (match_type & BIND_HAS_BUILTINS)
  666. atrok = flagrec_ok(&tc->flags, atr);
  667. else
  668. atrok = flagrec_eq(&tc->flags, atr);
  669. } else
  670. atrok = 1;
  671. if (atrok) {
  672. cnt++;
  673. /* Remember information about this bind and its only
  674. command/trigger. */
  675. proc = tc->func_name;
  676. fullmatch = tm->mask;
  677. htc = tc;
  678. tm_p = tm_last;
  679. /* Either this is a non-partial match, which means we
  680. only want to execute _one_ bind ... */
  681. if ((match_type & 3) != MATCH_PARTIAL ||
  682. /* ... or this is happens to be an exact match. */
  683. !egg_strcasecmp(match, tm->mask))
  684. cnt = finish = 1;
  685. }
  686. }
  687. }
  688. }
  689. if (!cnt)
  690. return BIND_NOMATCH;
  691. if ((match_type & 0x03) == MATCH_MASK ||
  692. (match_type & 0x03) == MATCH_CASE)
  693. return BIND_EXECUTED;
  694. /* Now that we have found at least one bind, we can update the
  695. preferred entries information. */
  696. if (htc)
  697. htc->hits++;
  698. if (tm_p) {
  699. /* Move mask to front of bind's mask list. */
  700. tm = tm_p->next;
  701. tm_p->next = tm->next; /* Unlink mask from list. */
  702. tm->next = tl->first; /* Readd mask to front of list. */
  703. tl->first = tm;
  704. }
  705. if (cnt > 1)
  706. return BIND_AMBIGUOUS;
  707. Tcl_SetVar(interp, "lastbind", (char *) fullmatch, TCL_GLOBAL_ONLY);
  708. Context;
  709. return trigger_bind(proc, param);
  710. }
  711. /* Check for tcl-bound dcc command, return 1 if found
  712. * dcc: proc-name <handle> <sock> <args...>
  713. */
  714. int check_tcl_dcc(char *cmd, int idx, char *args)
  715. {
  716. struct tcl_bind_mask_b *hm;
  717. struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
  718. int x;
  719. #ifdef S_DCCPASS
  720. int found = 0;
  721. #endif
  722. char s[11];
  723. get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
  724. if (dcc[idx].simul) {
  725. egg_snprintf(s, sizeof s, "-%d", idx);
  726. } else {
  727. egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock);
  728. }
  729. #ifdef S_DCCPASS
  730. for (hm = H_dcc->first; hm; hm = hm->next) {
  731. if (!egg_strcasecmp(cmd, hm->mask)) {
  732. found = 1;
  733. break;
  734. }
  735. }
  736. if (found) {
  737. if (has_cmd_pass(cmd)) {
  738. char *p,
  739. work[1024],
  740. pass[128];
  741. p = strchr(args, ' ');
  742. if (p)
  743. *p = 0;
  744. strncpy0(pass, args, sizeof(pass));
  745. if (check_cmd_pass(cmd, pass)) {
  746. if (p)
  747. *p = ' ';
  748. strncpy0(work, args, sizeof(work));
  749. p = work;
  750. newsplit(&p);
  751. strcpy(args, p);
  752. } else {
  753. dprintf(idx, "Invalid command password. Use %scommand password arguments\n", dcc_prefix);
  754. putlog(LOG_MISC, "*", "%s attempted %s%s with missing or incorrect command password", dcc[idx].nick, dcc_prefix, cmd);
  755. return 0;
  756. }
  757. }
  758. }
  759. #endif
  760. Tcl_SetVar(interp, "_dcc1", (char *) dcc[idx].nick, 0);
  761. Tcl_SetVar(interp, "_dcc2", (char *) s, 0);
  762. Tcl_SetVar(interp, "_dcc3", (char *) args, 0);
  763. x = check_tcl_bind(H_dcc, cmd, &fr, " $_dcc1 $_dcc2 $_dcc3",
  764. MATCH_PARTIAL | BIND_USE_ATTR | BIND_HAS_BUILTINS);
  765. if (x == BIND_AMBIGUOUS) {
  766. dprintf(idx, MISC_AMBIGUOUS);
  767. return 0;
  768. }
  769. if (x == BIND_NOMATCH) {
  770. dprintf(idx, "What? You need '%shelp'\n", dcc_prefix);
  771. return 0;
  772. }
  773. if (x == BIND_EXEC_BRK)
  774. return 1; /* quit */
  775. if (x == BIND_EXEC_LOG)
  776. putlog(LOG_CMDS, "*", "#%s# %s %s", dcc[idx].nick, cmd, args);
  777. return 0;
  778. }
  779. void check_tcl_bot(const char *nick, const char *code, const char *param)
  780. {
  781. Tcl_SetVar(interp, "_bot1", (char *) nick, 0);
  782. Tcl_SetVar(interp, "_bot2", (char *) code, 0);
  783. Tcl_SetVar(interp, "_bot3", (char *) param, 0);
  784. check_tcl_bind(H_bot, code, 0, " $_bot1 $_bot2 $_bot3", MATCH_EXACT);
  785. }
  786. void check_tcl_chonof(char *hand, int sock, tcl_bind_list_t *tl)
  787. {
  788. struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
  789. char s[11];
  790. struct userrec *u;
  791. u = get_user_by_handle(userlist, hand);
  792. touch_laston(u, "partyline", now);
  793. get_user_flagrec(u, &fr, NULL);
  794. Tcl_SetVar(interp, "_chonof1", (char *) hand, 0);
  795. egg_snprintf(s, sizeof s, "%d", sock);
  796. Tcl_SetVar(interp, "_chonof2", (char *) s, 0);
  797. check_tcl_bind(tl, hand, &fr, " $_chonof1 $_chonof2", MATCH_MASK |
  798. BIND_USE_ATTR | BIND_STACKABLE | BIND_WANTRET);
  799. }
  800. void check_tcl_chatactbcst(const char *from, int chan, const char *text,
  801. tcl_bind_list_t *tl)
  802. {
  803. char s[11];
  804. egg_snprintf(s, sizeof s, "%d", chan);
  805. Tcl_SetVar(interp, "_cab1", (char *) from, 0);
  806. Tcl_SetVar(interp, "_cab2", (char *) s, 0);
  807. Tcl_SetVar(interp, "_cab3", (char *) text, 0);
  808. check_tcl_bind(tl, text, 0, " $_cab1 $_cab2 $_cab3",
  809. MATCH_MASK | BIND_STACKABLE);
  810. }
  811. void check_tcl_nkch(const char *ohand, const char *nhand)
  812. {
  813. Tcl_SetVar(interp, "_nkch1", (char *) ohand, 0);
  814. Tcl_SetVar(interp, "_nkch2", (char *) nhand, 0);
  815. check_tcl_bind(H_nkch, ohand, 0, " $_nkch1 $_nkch2",
  816. MATCH_MASK | BIND_STACKABLE);
  817. }
  818. void check_tcl_link(const char *bot, const char *via)
  819. {
  820. Tcl_SetVar(interp, "_link1", (char *) bot, 0);
  821. Tcl_SetVar(interp, "_link2", (char *) via, 0);
  822. check_tcl_bind(H_link, bot, 0, " $_link1 $_link2",
  823. MATCH_MASK | BIND_STACKABLE);
  824. }
  825. void check_tcl_disc(const char *bot)
  826. {
  827. Tcl_SetVar(interp, "_disc1", (char *) bot, 0);
  828. check_tcl_bind(H_disc, bot, 0, " $_disc1", MATCH_MASK | BIND_STACKABLE);
  829. }
  830. void check_tcl_loadunld(const char *mod, tcl_bind_list_t *tl)
  831. {
  832. Tcl_SetVar(interp, "_lu1", (char *) mod, 0);
  833. check_tcl_bind(tl, mod, 0, " $_lu1", MATCH_MASK | BIND_STACKABLE);
  834. }
  835. const char *check_tcl_filt(int idx, const char *text)
  836. {
  837. char s[11];
  838. int x;
  839. struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
  840. egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock);
  841. get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
  842. Tcl_SetVar(interp, "_filt1", (char *) s, 0);
  843. Tcl_SetVar(interp, "_filt2", (char *) text, 0);
  844. x = check_tcl_bind(H_filt, text, &fr, " $_filt1 $_filt2",
  845. MATCH_MASK | BIND_USE_ATTR | BIND_STACKABLE |
  846. BIND_WANTRET | BIND_ALTER_ARGS);
  847. if (x == BIND_EXECUTED || x == BIND_EXEC_LOG) {
  848. if (interp->result == NULL || !interp->result[0])
  849. return "";
  850. else
  851. return interp->result;
  852. } else
  853. return text;
  854. }
  855. int check_tcl_note(const char *from, const char *to, const char *text)
  856. {
  857. int x;
  858. Tcl_SetVar(interp, "_note1", (char *) from, 0);
  859. Tcl_SetVar(interp, "_note2", (char *) to, 0);
  860. Tcl_SetVar(interp, "_note3", (char *) text, 0);
  861. x = check_tcl_bind(H_note, to, 0, " $_note1 $_note2 $_note3", MATCH_EXACT);
  862. return (x == BIND_MATCHED || x == BIND_EXECUTED || x == BIND_EXEC_LOG);
  863. }
  864. void check_tcl_listen(const char *cmd, int idx)
  865. {
  866. char s[11];
  867. int x;
  868. egg_snprintf(s, sizeof s, "%d", idx);
  869. Tcl_SetVar(interp, "_n", (char *) s, 0);
  870. x = Tcl_VarEval(interp, cmd, " $_n", NULL);
  871. if (x == TCL_ERROR)
  872. putlog(LOG_MISC, "*", "error on listen: %s", interp->result);
  873. }
  874. void check_tcl_chjn(const char *bot, const char *nick, int chan,
  875. const char type, int sock, const char *host)
  876. {
  877. struct flag_record fr = {FR_GLOBAL, 0, 0, 0, 0, 0};
  878. char s[11], t[2], u[11];
  879. t[0] = type;
  880. t[1] = 0;
  881. switch (type) {
  882. case '^':
  883. fr.global = USER_ADMIN;
  884. break;
  885. case '*':
  886. fr.global = USER_OWNER;
  887. break;
  888. case '+':
  889. fr.global = USER_MASTER;
  890. break;
  891. case '@':
  892. fr.global = USER_OP;
  893. break;
  894. }
  895. egg_snprintf(s, sizeof s, "%d", chan);
  896. egg_snprintf(u, sizeof u, "%d", sock);
  897. Tcl_SetVar(interp, "_chjn1", (char *) bot, 0);
  898. Tcl_SetVar(interp, "_chjn2", (char *) nick, 0);
  899. Tcl_SetVar(interp, "_chjn3", (char *) s, 0);
  900. Tcl_SetVar(interp, "_chjn4", (char *) t, 0);
  901. Tcl_SetVar(interp, "_chjn5", (char *) u, 0);
  902. Tcl_SetVar(interp, "_chjn6", (char *) host, 0);
  903. check_tcl_bind(H_chjn, s, &fr,
  904. " $_chjn1 $_chjn2 $_chjn3 $_chjn4 $_chjn5 $_chjn6",
  905. MATCH_MASK | BIND_STACKABLE);
  906. }
  907. void check_tcl_chpt(const char *bot, const char *hand, int sock, int chan)
  908. {
  909. char u[11], v[11];
  910. egg_snprintf(u, sizeof u, "%d", sock);
  911. egg_snprintf(v, sizeof v, "%d", chan);
  912. Tcl_SetVar(interp, "_chpt1", (char *) bot, 0);
  913. Tcl_SetVar(interp, "_chpt2", (char *) hand, 0);
  914. Tcl_SetVar(interp, "_chpt3", (char *) u, 0);
  915. Tcl_SetVar(interp, "_chpt4", (char *) v, 0);
  916. check_tcl_bind(H_chpt, v, 0, " $_chpt1 $_chpt2 $_chpt3 $_chpt4",
  917. MATCH_MASK | BIND_STACKABLE);
  918. }
  919. void check_tcl_away(const char *bot, int idx, const char *msg)
  920. {
  921. char u[11];
  922. egg_snprintf(u, sizeof u, "%d", idx);
  923. Tcl_SetVar(interp, "_away1", (char *) bot, 0);
  924. Tcl_SetVar(interp, "_away2", (char *) u, 0);
  925. Tcl_SetVar(interp, "_away3", msg ? (char *) msg : "", 0);
  926. check_tcl_bind(H_away, bot, 0, " $_away1 $_away2 $_away3",
  927. MATCH_MASK | BIND_STACKABLE);
  928. }
  929. void check_tcl_time(struct tm *tm)
  930. {
  931. char y[18];
  932. egg_snprintf(y, sizeof y, "%02d", tm->tm_min);
  933. Tcl_SetVar(interp, "_time1", (char *) y, 0);
  934. egg_snprintf(y, sizeof y, "%02d", tm->tm_hour);
  935. Tcl_SetVar(interp, "_time2", (char *) y, 0);
  936. egg_snprintf(y, sizeof y, "%02d", tm->tm_mday);
  937. Tcl_SetVar(interp, "_time3", (char *) y, 0);
  938. egg_snprintf(y, sizeof y, "%02d", tm->tm_mon);
  939. Tcl_SetVar(interp, "_time4", (char *) y, 0);
  940. egg_snprintf(y, sizeof y, "%04d", tm->tm_year + 1900);
  941. Tcl_SetVar(interp, "_time5", (char *) y, 0);
  942. egg_snprintf(y, sizeof y, "%02d %02d %02d %02d %04d", tm->tm_min, tm->tm_hour,
  943. tm->tm_mday, tm->tm_mon, tm->tm_year + 1900);
  944. check_tcl_bind(H_time, y, 0,
  945. " $_time1 $_time2 $_time3 $_time4 $_time5",
  946. MATCH_MASK | BIND_STACKABLE);
  947. }
  948. void check_tcl_event(const char *event)
  949. {
  950. Tcl_SetVar(interp, "_event1", (char *) event, 0);
  951. check_tcl_bind(H_event, event, 0, " $_event1", MATCH_EXACT | BIND_STACKABLE);
  952. }
  953. void tell_binds(int idx, char *par)
  954. {
  955. tcl_bind_list_t *tl, *tl_kind;
  956. tcl_bind_mask_t *tm;
  957. int fnd = 0, showall = 0, patmatc = 0;
  958. tcl_cmd_t *tc;
  959. char *name, *proc, *s, flg[100];
  960. if (par[0])
  961. name = newsplit(&par);
  962. else
  963. name = NULL;
  964. if (par[0])
  965. s = newsplit(&par);
  966. else
  967. s = NULL;
  968. if (name)
  969. tl_kind = find_bind_table(name);
  970. else
  971. tl_kind = NULL;
  972. if ((name && name[0] && !egg_strcasecmp(name, "all")) || (s && s[0] && !egg_strcasecmp(s, "all")))
  973. showall = 1;
  974. if (tl_kind == NULL && name && name[0] && egg_strcasecmp(name, "all"))
  975. patmatc = 1;
  976. dprintf(idx, MISC_CMDBINDS);
  977. dprintf(idx, " TYPE FLGS COMMAND HITS BINDING (TCL)\n");
  978. for (tl = tl_kind ? tl_kind : bind_table_list; tl;
  979. tl = tl_kind ? 0 : tl->next) {
  980. if (tl->flags & HT_DELETED)
  981. continue;
  982. for (tm = tl->first; tm; tm = tm->next) {
  983. if (tm->flags & TBM_DELETED)
  984. continue;
  985. for (tc = tm->first; tc; tc = tc->next) {
  986. if (tc->attributes & TC_DELETED)
  987. continue;
  988. proc = tc->func_name;
  989. build_flags(flg, &(tc->flags), NULL);
  990. if (showall || proc[0] != '*') {
  991. int ok = 0;
  992. if (patmatc == 1) {
  993. if (wild_match_per(name, tl->name) ||
  994. wild_match_per(name, tm->mask) ||
  995. wild_match_per(name, tc->func_name))
  996. ok = 1;
  997. } else
  998. ok = 1;
  999. if (ok) {
  1000. dprintf(idx, " %-4s %-8s %-20s %4d %s\n", tl->name, flg, tm->mask,
  1001. tc->hits, tc->func_name);
  1002. fnd = 1;
  1003. }
  1004. }
  1005. }
  1006. }
  1007. }
  1008. if (!fnd) {
  1009. if (patmatc)
  1010. dprintf(idx, "No command bindings found that match %s\n", name);
  1011. else if (tl_kind)
  1012. dprintf(idx, "No command bindings for type: %s.\n", name);
  1013. else
  1014. dprintf(idx, "No command bindings exist.\n");
  1015. }
  1016. }
  1017. /* Bring the default msg/dcc/fil commands into the Tcl interpreter */
  1018. void add_builtins(tcl_bind_list_t *tl, cmd_t *cc)
  1019. {
  1020. int k, i;
  1021. char p[1024], *l;
  1022. cd_tcl_cmd table[2];
  1023. table[0].name = p;
  1024. table[0].callback = tl->func;
  1025. table[1].name = NULL;
  1026. for (i = 0; cc[i].name; i++) {
  1027. egg_snprintf(p, sizeof p, "*%s:%s", tl->name,
  1028. cc[i].funcname ? cc[i].funcname : cc[i].name);
  1029. l = (char *) nmalloc(Tcl_ScanElement(p, &k));
  1030. Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES);
  1031. table[0].cdata = (void *)cc[i].func;
  1032. add_cd_tcl_cmds(table);
  1033. bind_bind_entry(tl, cc[i].flags, cc[i].name, l);
  1034. nfree(l);
  1035. }
  1036. }
  1037. void add_builtins_dcc(tcl_bind_list_t *tl, dcc_cmd_t * cc)
  1038. {
  1039. int k, i;
  1040. char p[1024], *l;
  1041. cd_tcl_cmd table[2];
  1042. table[0].name = p;
  1043. table[0].callback = tl->func;
  1044. table[1].name = NULL;
  1045. for (i = 0; cc[i].name; i++) {
  1046. //lets add to the help system..
  1047. cmds[cmdi].name = cc[i].name;
  1048. cmds[cmdi].usage = cc[i].usage;
  1049. cmds[cmdi].desc = cc[i].desc;
  1050. cmds[cmdi].flags.match = FR_GLOBAL | FR_CHAN;
  1051. break_down_flags(cc[i].flags, &(cmds[cmdi].flags), NULL);
  1052. cmdi++;
  1053. egg_snprintf(p, sizeof p, "*%s:%s", tl->name,
  1054. cc[i].funcname ? cc[i].funcname : cc[i].name);
  1055. l = (char *) nmalloc(Tcl_ScanElement(p, &k));
  1056. Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES);
  1057. table[0].cdata = (void *)cc[i].func;
  1058. add_cd_tcl_cmds(table);
  1059. bind_bind_entry(tl, cc[i].flags, cc[i].name, l);
  1060. nfree(l);
  1061. }
  1062. /*
  1063. int i;
  1064. for (i = 0; cc[i].name; i++) {
  1065. bind_bind_entry(table, cc[i].flags, cc[i].name, "");
  1066. }
  1067. */
  1068. }
  1069. void rem_builtins_dcc(tcl_bind_list_t *table, dcc_cmd_t *cc)
  1070. {
  1071. int i;
  1072. for (i = 0; cc[i].name; i++) {
  1073. unbind_bind_entry(table, cc[i].flags, cc[i].name, "");
  1074. }
  1075. }
  1076. /* Remove the default msg/dcc/fil commands from the Tcl interpreter */
  1077. void rem_builtins(tcl_bind_list_t *table, cmd_t *cc)
  1078. {
  1079. int k, i;
  1080. char p[1024], *l;
  1081. for (i = 0; cc[i].name; i++) {
  1082. egg_snprintf(p, sizeof p, "*%s:%s", table->name,
  1083. cc[i].funcname ? cc[i].funcname : cc[i].name);
  1084. l = (char *) nmalloc(Tcl_ScanElement(p, &k));
  1085. Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES);
  1086. Tcl_DeleteCommand(interp, p);
  1087. unbind_bind_entry(table, cc[i].flags, cc[i].name, l);
  1088. nfree(l);
  1089. }
  1090. }