/*
   tclhash.c -- handles:
     bind and unbind
     checking and triggering the various bindings
     listing current bindings

   dprintf'ized, 4feb96
*/
/*
   This file is part of the eggdrop source code
   copyright (c) 1997 Robey Pointer
   and is distributed according to the GNU general public license.
   For full details, read the top of 'main.c' or the file called
   COPYING that was distributed with this code.
*/

#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "../lush.h"
#include "eggdrop.h"
#include "proto.h"
#include "cmdt.h"
#include "tclegg.h"

extern Tcl_Interp *interp;
extern int dcc_total;
extern struct dcc_t dcc[];
extern int require_x,require_p;
#ifdef NO_IRC
extern cmd_t C_dcc[];
#else
extern cmd_t C_msg[],C_dcc[],C_file[];
#endif
extern int cx_line; extern char cx_file[];

Tcl_HashTable H_msg, H_dcc, H_fil, H_pub, H_msgm, H_pubm, H_join, H_part,
   H_sign, H_kick, H_topc, H_mode, H_ctcp, H_ctcr, H_nick, H_raw, H_bot,
   H_chon, H_chof, H_sent, H_rcvd, H_chat, H_link, H_disc, H_splt, H_rejn,
   H_filt, H_flud, H_note, H_act, H_wall;
int hashtot=0;

int expmem_tclhash()
{
  return hashtot;
}

/* initialize hash tables */
void init_hash()
{
  Tcl_InitHashTable(&H_msg,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_dcc,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_fil,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_pub,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_msgm,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_pubm,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_join,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_part,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_sign,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_kick,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_topc,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_mode,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_ctcp,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_ctcr,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_nick,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_raw,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_bot,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_chon,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_chof,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_sent,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_rcvd,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_chat,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_link,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_disc,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_splt,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_rejn,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_filt,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_flud,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_note,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_act,TCL_STRING_KEYS);
  Tcl_InitHashTable(&H_wall,TCL_STRING_KEYS);
}

void *tclcmd_alloc(size)
int size;
{
  tcl_cmd_t *x=(tcl_cmd_t *)nmalloc(sizeof(tcl_cmd_t));
  hashtot+=sizeof(tcl_cmd_t);
  x->func_name=(char *)nmalloc(size);
  hashtot+=size;
  return (void *)x;
}

/* returns hashtable for that type */
/* also sets 'stk' if stackable, and sets 'name' the name, if non-NULL */
Tcl_HashTable *gethashtable(typ,stk,name)
int typ,*stk; char *name;
{
  char *nam=NULL; int st=0; Tcl_HashTable *ht=NULL;
  switch(typ) {
  case CMD_MSG: ht=&H_msg; nam="msg"; break;
  case CMD_DCC: ht=&H_dcc; nam="dcc"; break;
  case CMD_FIL: ht=&H_fil; nam="fil"; break;
  case CMD_PUB: ht=&H_pub; nam="pub"; break;
  case CMD_MSGM: ht=&H_msgm; nam="msgm"; st=1; break;
  case CMD_PUBM: ht=&H_pubm; nam="pubm"; st=1; break;
  case CMD_JOIN: ht=&H_join; nam="join"; st=1; break;
  case CMD_PART: ht=&H_part; nam="part"; st=1; break;
  case CMD_SIGN: ht=&H_sign; nam="sign"; st=1; break;
  case CMD_KICK: ht=&H_kick; nam="kick"; st=1; break;
  case CMD_TOPC: ht=&H_topc; nam="topc"; st=1; break;
  case CMD_MODE: ht=&H_mode; nam="mode"; st=1; break;
  case CMD_CTCP: ht=&H_ctcp; nam="ctcp"; break;
  case CMD_CTCR: ht=&H_ctcr; nam="ctcr"; break;
  case CMD_NICK: ht=&H_nick; nam="nick"; st=1; break;
  case CMD_RAW: ht=&H_raw; nam="raw"; st=1; break;
  case CMD_BOT: ht=&H_bot; nam="bot"; break;
  case CMD_CHON: ht=&H_chon; nam="chon"; st=1; break;
  case CMD_CHOF: ht=&H_chof; nam="chof"; st=1; break;
  case CMD_SENT: ht=&H_sent; nam="sent"; st=1; break;
  case CMD_RCVD: ht=&H_rcvd; nam="rcvd"; st=1; break;
  case CMD_CHAT: ht=&H_chat; nam="chat"; st=1; break;
  case CMD_LINK: ht=&H_link; nam="link"; st=1; break;
  case CMD_DISC: ht=&H_disc; nam="disc"; st=1; break;
  case CMD_SPLT: ht=&H_splt; nam="splt"; st=1; break;
  case CMD_REJN: ht=&H_rejn; nam="rejn"; st=1; break;
  case CMD_FILT: ht=&H_filt; nam="filt"; st=1; break;
  case CMD_FLUD: ht=&H_flud; nam="flud"; st=1; break;
  case CMD_NOTE: ht=&H_note; nam="note"; break;
  case CMD_ACT: ht=&H_act; nam="act"; st=1; break;
  case CMD_WALL: ht=&H_wall; nam="wall"; st=1; break;
  }
  if (name!=NULL) strcpy(name,nam);
  if (stk!=NULL) *stk=st;
  return ht;
}

int get_bind_type(name)
char *name;
{
  int tp=(-1);
  if (strcasecmp(name,"dcc")==0) tp=CMD_DCC;
  if (strcasecmp(name,"msg")==0) tp=CMD_MSG;
  if (strcasecmp(name,"fil")==0) tp=CMD_FIL;
  if (strcasecmp(name,"pub")==0) tp=CMD_PUB;
  if (strcasecmp(name,"msgm")==0) tp=CMD_MSGM;
  if (strcasecmp(name,"pubm")==0) tp=CMD_PUBM;
  if (strcasecmp(name,"join")==0) tp=CMD_JOIN;
  if (strcasecmp(name,"part")==0) tp=CMD_PART;
  if (strcasecmp(name,"sign")==0) tp=CMD_SIGN;
  if (strcasecmp(name,"kick")==0) tp=CMD_KICK;
  if (strcasecmp(name,"topc")==0) tp=CMD_TOPC;
  if (strcasecmp(name,"mode")==0) tp=CMD_MODE;
  if (strcasecmp(name,"ctcp")==0) tp=CMD_CTCP;
  if (strcasecmp(name,"ctcr")==0) tp=CMD_CTCR;
  if (strcasecmp(name,"nick")==0) tp=CMD_NICK;
  if (strcasecmp(name,"bot")==0) tp=CMD_BOT;
  if (strcasecmp(name,"chon")==0) tp=CMD_CHON;
  if (strcasecmp(name,"chof")==0) tp=CMD_CHOF;
  if (strcasecmp(name,"sent")==0) tp=CMD_SENT;
  if (strcasecmp(name,"rcvd")==0) tp=CMD_RCVD;
  if (strcasecmp(name,"chat")==0) tp=CMD_CHAT;
  if (strcasecmp(name,"link")==0) tp=CMD_LINK;
  if (strcasecmp(name,"disc")==0) tp=CMD_DISC;
  if (strcasecmp(name,"rejn")==0) tp=CMD_REJN;
  if (strcasecmp(name,"splt")==0) tp=CMD_SPLT;
  if (strcasecmp(name,"filt")==0) tp=CMD_FILT;
  if (strcasecmp(name,"flud")==0) tp=CMD_FLUD;
  if (strcasecmp(name,"note")==0) tp=CMD_NOTE;
  if (strcasecmp(name,"act")==0) tp=CMD_ACT;
  if (strcasecmp(name,"wall")==0) tp=CMD_WALL;
#ifdef USE_CONSOLE_R
  if (strcasecmp(name,"raw")==0) tp=CMD_RAW;
#endif
  return tp;
}

/* remove command */
int cmd_unbind(typ,flags,cmd,proc)
int typ,flags; char *cmd,*proc;
{
  tcl_cmd_t *tt,*last; Tcl_HashEntry *he; Tcl_HashTable *ht;
  context;
  ht=gethashtable(typ,NULL,NULL);
  he=Tcl_FindHashEntry(ht,cmd);
  if (he==NULL) return 0;   /* no such binding */
  tt=(tcl_cmd_t *)Tcl_GetHashValue(he); last=NULL;
  while (tt!=NULL) {
    /* if procs are same, erase regardless of flags */
    if (strcasecmp(tt->func_name,proc)==0) {
      /* erase it */
      if (last!=NULL) last->next=tt->next;
      else {
	if (tt->next==NULL) Tcl_DeleteHashEntry(he);
	else Tcl_SetHashValue(he,tt->next);
      }
      hashtot-=(strlen(tt->func_name)+1); nfree(tt->func_name);
      nfree(tt); hashtot-=sizeof(tcl_cmd_t);
      return 1;
    }
    last=tt; tt=tt->next;
  }
  return 0;   /* no match */
}

/* add command (remove old one if necessary) */
int cmd_bind(typ,flags,cmd,proc)
int typ,flags; char *cmd,*proc;
{
  tcl_cmd_t *tt; int new; Tcl_HashEntry *he; Tcl_HashTable *ht; int stk;
  if (proc[0]=='#') {
    putlog(LOG_MISC,"*","Note: binding to '#' is obsolete.");
    return 0;
  }
  cmd_unbind(typ,flags,cmd,proc);    /* make sure we don't dup */
  tt=(tcl_cmd_t *)nmalloc(sizeof(tcl_cmd_t)); hashtot+=sizeof(tcl_cmd_t);
  tt->flags_needed=flags; tt->next=NULL;
  tt->func_name=(char *)nmalloc(strlen(proc)+1); hashtot+=strlen(proc)+1;
  strcpy(tt->func_name,proc);
  ht=gethashtable(typ,&stk,NULL);
  he=Tcl_CreateHashEntry(ht,cmd,&new);
  if (!new) {
    tt->next=(tcl_cmd_t *)Tcl_GetHashValue(he);
    if (!stk) {
      /* remove old one -- these are not stackable */
      hashtot-=(strlen(tt->next->func_name)+1); hashtot-=sizeof(tcl_cmd_t);
      nfree(tt->next->func_name); nfree(tt->next); tt->next=NULL;
    }
  }
  Tcl_SetHashValue(he,tt);
  return 1;
}

/* match types for check_tcl_bind */
#define MATCH_PARTIAL       0
#define MATCH_EXACT         1
#define MATCH_MASK          2
/* bitwise 'or' these: */
#define BIND_USE_ATTR       4
#define BIND_STACKABLE      8
#define BIND_HAS_BUILTINS   16
#define BIND_WANTRET        32

/* return values */
#define BIND_NOMATCH    0
#define BIND_AMBIGUOUS  1
#define BIND_MATCHED    2    /* but the proc couldn't be found */
#define BIND_EXECUTED   3
#define BIND_EXEC_LOG   4    /* proc returned 1 -> wants to be logged */
/* 10+  -->  matches builtin # (retval-10)  */

/* trigger (execute) a proc */
int trigger_bind(proc,param)
char *proc,*param;
{
#ifdef EBUG_TCL
  FILE *f=fopen("DEBUG.TCL","a");
  if (f!=NULL) fprintf(f,"eval: %s%s\n",proc,param);
#endif
  set_tcl_vars();
  if (Tcl_VarEval(interp,proc,param,NULL)==TCL_ERROR) {
#ifdef EBUG_TCL
    if (f!=NULL) { fprintf(f,"done eval. error.\n"); fclose(f); }
#endif
    putlog(LOG_MISC,"*","Tcl error [%s]: %s",proc,interp->result);
    return BIND_EXECUTED;
  }
  else {
#ifdef EBUG_TCL
    if (f!=NULL) { fprintf(f,"done eval. ok.\n"); fclose(f); }
#endif
    get_tcl_vars();
    return (atoi(interp->result)>0)?BIND_EXEC_LOG:BIND_EXECUTED;
  }
}

/* check a tcl binding and execute the procs necessary */
int check_tcl_bind(hash,match,atr,param,match_type,builtin)
Tcl_HashTable *hash; char *match,*param; int atr,match_type; cmd_t *builtin;
{
  Tcl_HashSearch srch; Tcl_HashEntry *he; int cnt=0; char *proc=NULL;
  tcl_cmd_t *tt; int f=0,atrok,x;
  for (he=Tcl_FirstHashEntry(hash,&srch); (he!=NULL) && (!f);
       he=Tcl_NextHashEntry(&srch)) {
    int ok=0;
    switch (match_type&0x03) {
    case MATCH_PARTIAL:
      ok=(strncasecmp(match,Tcl_GetHashKey(hash,he),strlen(match))==0); break;
    case MATCH_EXACT:
      ok=(strcasecmp(match,Tcl_GetHashKey(hash,he))==0); break;
    case MATCH_MASK:
      ok=wild_match_per(Tcl_GetHashKey(hash,he),match); break;
    }
    if (ok) {
      tt=(tcl_cmd_t *)Tcl_GetHashValue(he);
      switch (match_type&0x03) {
      case MATCH_MASK:
	/* could be multiple triggers */
	while (tt!=NULL) {
	  if (match_type & BIND_HAS_BUILTINS)   
	    atrok=flags_ok(tt->flags_needed,atr);
	  else atrok=flags_eq(tt->flags_needed,atr);
	  if ((!(match_type&BIND_USE_ATTR)) || atrok) {
	    cnt++; x=trigger_bind(tt->func_name,param);
	    if ((match_type&BIND_WANTRET) && (x==BIND_EXEC_LOG)) return x;
	  }
	  tt=tt->next;
	}
	break;
      default:
	if (match_type & BIND_HAS_BUILTINS)   
	  atrok=flags_ok(tt->flags_needed,atr);
	else atrok=flags_eq(tt->flags_needed,atr);
	if ((!(match_type&BIND_USE_ATTR)) || atrok) {
	  cnt++; proc=tt->func_name;
	  if (strcasecmp(match,Tcl_GetHashKey(hash,he))==0) {
	    cnt=1; f=1;  /* perfect match */
	  }
	}
	break;
      }
    }
  }
  if (cnt==0) return BIND_NOMATCH;
  if ((match_type&0x03)==MATCH_MASK) return BIND_EXECUTED;
  if (cnt>1) return BIND_AMBIGUOUS;
  if ((match_type&BIND_HAS_BUILTINS) && (proc[0]=='*')) {
    /* calling builtin function */
    int top=0,bot=0,try=0,xx; char pr[81];
    strcpy(pr,&proc[1]); f=0;
    while (builtin[bot].access!=(-1)) bot++;  /* find bottom */
    /* binary search: */
    while (!f) {
      try=(top+bot)/2; xx=strcasecmp(builtin[try].name,pr);
      if (xx==0) f=1;
      if (xx<0) top=try+1;
      if (xx>0) bot=try;
      if (top==bot) {
	putlog(LOG_MISC,"*","Tcl error [%s]: no such builtin function",proc);
	return BIND_MATCHED;
      }
    }
    return try+10;
  }
  return trigger_bind(proc,param);
}

/* check for tcl-bound msg command, return 1 if found */
/* msg: proc-name <nick> <user@host> <handle> <args...> */
int check_tcl_msg(cmd,nick,uhost,hand,args)
char *cmd,*nick,*uhost,*hand,*args;
{
#ifndef NO_IRC
  int x,atr;
  context;
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",args,TCL_GLOBAL_ONLY);
  x=check_tcl_bind(&H_msg,cmd,atr," $_n $_uh $_h $_a",
		   MATCH_PARTIAL|BIND_HAS_BUILTINS|BIND_USE_ATTR,C_msg);
  if (x==BIND_EXEC_LOG)
    putlog(LOG_CMDS,"*","(%s!%s) !%s! %s %s",nick,uhost,hand,cmd,args);
  if (x>=10) return (C_msg[x-10].func)(hand,nick,uhost,args);
  return ((x==BIND_MATCHED)||(x==BIND_EXECUTED)||(x==BIND_EXEC_LOG));
#else
  return 0;
#endif
}

/* check for tcl-bound dcc command, return 1 if found */
/* dcc: proc-name <handle> <sock> <args...> */
int check_tcl_dcc(cmd,idx,args)
char *cmd,*args; int idx;
{
  int x,atr; char s[5];
  atr=get_attr_handle(dcc[idx].nick);
  sprintf(s,"%d",dcc[idx].sock);
  Tcl_SetVar(interp,"_n",dcc[idx].nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_i",s,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",args,TCL_GLOBAL_ONLY);
  x=check_tcl_bind(&H_dcc,cmd,atr," $_n $_i $_a",
		   MATCH_PARTIAL|BIND_USE_ATTR|BIND_HAS_BUILTINS,C_dcc);
  if (x==BIND_AMBIGUOUS) {
    dprintf(idx,"Ambigious command.\n");
    return 0;
  }
  if (x==BIND_NOMATCH) {
    dprintf(idx,"What?  Try '.help'\n");
    return 0;
  }
  if (x>=10) {
    if (C_dcc[x-10].func==CMD_LEAVE) return 1;
    (C_dcc[x-10].func)(idx,args);
    return 0;
  }
  if (x==BIND_EXEC_LOG)
    putlog(LOG_CMDS,"*","#%s# %s %s",dcc[idx].nick,cmd,args);
  return 0;
}

/* check for tcl-bound file command, return 1 if found */
/* fil: proc-name <handle> <dcc-handle> <args...> */
int check_tcl_fil(cmd,idx,args)
char *cmd,*args; int idx;
{
#ifndef NO_IRC
  int atr,x; char s[5];
  context;
  atr=get_attr_handle(dcc[idx].nick);
  sprintf(s,"%d",dcc[idx].sock);
  Tcl_SetVar(interp,"_n",dcc[idx].nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_i",s,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",args,TCL_GLOBAL_ONLY);
  x=check_tcl_bind(&H_fil,cmd,atr," $_n $_i $_a",
		   MATCH_PARTIAL|BIND_USE_ATTR|BIND_HAS_BUILTINS,C_file);
  if (x==BIND_AMBIGUOUS) {
    dprintf(idx,"Ambigious command.\n");
    return 0;
  }
  if (x==BIND_NOMATCH) {
    dprintf(idx,"What?  Try 'help'\n");
    return 0;
  }
  if (x>=10) {
    if (C_file[x-10].func == CMD_LEAVE) return 1;
    (C_file[x-10].func)(idx,args);
    return 0;
  }
  if (x==BIND_EXEC_LOG)
    putlog(LOG_FILES,"*","#%s# files: %s %s",dcc[idx].nick,cmd,args);
#endif
  return 0;
}

int check_tcl_pub(nick,from,chname,msg)
char *nick,*from,*chname,*msg;
{
  int x,atr; char args[512],cmd[512],host[161],handle[21];
  context;
  strcpy(args,msg); nsplit(cmd,args); sprintf(host,"%s!%s",nick,from);
  get_handle_by_host(handle,host);
  atr=get_attr_handle(handle);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",from,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",handle,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",args,TCL_GLOBAL_ONLY);
  x=check_tcl_bind(&H_pub,cmd,atr," $_n $_uh $_h $_a $_aa",
		   MATCH_EXACT|BIND_USE_ATTR,NULL);
  if (x==BIND_NOMATCH) return 0;
  if (x==BIND_EXEC_LOG)
    putlog(LOG_CMDS,chname,"<<%s>> !%s! %s %s",nick,handle,cmd,args);
  return 1;
}

void check_tcl_pubm(nick,from,chname,msg)
char *nick,*from,*chname,*msg;
{
  char args[512],host[161],handle[21]; int atr;
  context;
  strcpy(args,chname); strcat(args," "); strcat(args,msg);
  sprintf(host,"%s!%s",nick,from);
  get_handle_by_host(handle,host); atr=get_attr_handle(handle);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",from,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",handle,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",msg,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_pubm,args,atr," $_n $_uh $_h $_a $_aa",
		   MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_msgm(cmd,nick,uhost,hand,arg)
char *cmd,*nick,*uhost,*hand,*arg;
{
  int atr; char args[512];
  context;
  if (arg[0]) sprintf(args,"%s %s",cmd,arg);
  else strcpy(args,cmd);
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",args,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_msgm,args,atr," $_n $_uh $_h $_a",
		   MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_join(nick,uhost,hand,chname)
char *nick,*uhost,*hand,*chname;
{
  int atr; char args[512];
  context;
  sprintf(args,"%s %s!%s",chname,nick,uhost);
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_join,args,atr," $_n $_uh $_h $_a",
		   MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_part(nick,uhost,hand,chname)
char *nick,*uhost,*hand,*chname;
{
  int atr,x; char args[512];
  context;
  sprintf(args,"%s %s!%s",chname,nick,uhost);
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  x=check_tcl_bind(&H_part,args,atr," $_n $_uh $_h $_a",
		   MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_sign(nick,uhost,hand,chname,reason)
char *nick,*uhost,*hand,*chname,*reason;
{
  int atr; char args[512];
  context;
  sprintf(args,"%s %s!%s",chname,nick,uhost);
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",reason,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_sign,args,atr," $_n $_uh $_h $_a $_aa",
		 MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
  context;
}

void check_tcl_topc(nick,uhost,hand,chname,topic)
char *nick,*uhost,*hand,*chname,*topic;
{
  int atr; char args[512];
  context;
  sprintf(args,"%s %s",chname,topic);
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",topic,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_topc,args,atr," $_n $_uh $_h $_a $_aa",
		 MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_nick(nick,uhost,hand,chname,newnick)
char *nick,*uhost,*hand,*chname,*newnick;
{
  int atr=get_attr_handle(hand); char args[512];
  context;
  sprintf(args,"%s %s",chname,newnick);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",newnick,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_nick,args,atr," $_n $_uh $_h $_a $_aa",
		 MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_kick(nick,uhost,hand,chname,dest,reason)
char *nick,*uhost,*hand,*chname,*dest,*reason;
{
  char args[512];
  context;
  sprintf(args,"%s %s",chname,dest);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",dest,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aaa",reason,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_kick,args,0," $_n $_uh $_h $_a $_aa $_aaa",
		 MATCH_MASK|BIND_STACKABLE,NULL);
}

#ifdef USE_CONSOLE_R
void check_tcl_raw(raw)
char *raw;
{
  context;
  Tcl_SetVar(interp,"_a",raw,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_raw,raw,0," $_a",MATCH_MASK|BIND_STACKABLE,NULL);
}
#endif

void check_tcl_tand(nick,code,param)
char *nick,*code,*param;
{
  context;
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",code,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",param,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_bot,code,0," $_n $_h $_a",MATCH_EXACT,NULL);
}

void check_tcl_mode(nick,uhost,hand,chname,mode)
char *nick,*uhost,*hand,*chname,*mode;
{
  char args[512];
  context;
  sprintf(args,"%s %s",chname,mode);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",mode,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_mode,args,0," $_n $_uh $_h $_a $_aa",
		 MATCH_MASK|BIND_STACKABLE,NULL);
}

int check_tcl_ctcp(nick,uhost,hand,dest,keyword,args)
char *nick,*uhost,*hand,*dest,*keyword,*args;
{
  int atr,x;
  context;
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",dest,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",keyword,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aaa",args,TCL_GLOBAL_ONLY);
  x=check_tcl_bind(&H_ctcp,keyword,atr," $_n $_uh $_h $_a $_aa $_aaa",
		   MATCH_MASK|BIND_USE_ATTR|BIND_WANTRET,NULL);
  return (x==BIND_EXEC_LOG);
/*  return ((x==BIND_MATCHED)||(x==BIND_EXECUTED)||(x==BIND_EXEC_LOG)); */
}

int check_tcl_ctcr(nick,uhost,hand,dest,keyword,args)
char *nick,*uhost,*hand,*dest,*keyword,*args;
{
  int atr;
  context;
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",dest,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",keyword,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aaa",args,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_ctcr,keyword,atr," $_n $_uh $_h $_a $_aa $_aaa",
		 MATCH_MASK|BIND_USE_ATTR,NULL);
  return 1;
}

void check_tcl_chon(hand,idx)
char *hand; int idx;
{
  int atr; char s[20];
  context;
  sprintf(s,"%d",idx);
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",s,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_chon,hand,atr," $_n $_a",
		 MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_chof(hand,idx)
char *hand; int idx;
{
  int atr; char s[20];
  context;
  sprintf(s,"%d",idx);
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",s,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_chof,hand,atr," $_n $_a",
		 MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_sent(hand,nick,path)
char *hand,*nick,*path;
{
  int atr;
  context;
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",path,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_sent,hand,atr," $_n $_a $_aa",
		 MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_rcvd(hand,nick,path)
char *hand,*nick,*path;
{
  int atr;
  context;
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",path,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_rcvd,hand,atr," $_n $_a $_aa",
		 MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_chat(from,chan,text)
char *from,*text; int chan;
{
  char s[10];
  context;
  sprintf(s,"%d",chan);
  Tcl_SetVar(interp,"_n",from,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",s,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",text,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_chat,text,0," $_n $_a $_aa",MATCH_MASK|BIND_STACKABLE,
		 NULL);
}

void check_tcl_link(bot,via)
char *bot,*via;
{
  context;
  Tcl_SetVar(interp,"_n",bot,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",via,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_link,bot,0," $_n $_a",MATCH_MASK|BIND_STACKABLE,NULL);
}

void check_tcl_disc(bot)
char *bot;
{
  context;
  Tcl_SetVar(interp,"_n",bot,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_disc,bot,0," $_n",MATCH_MASK|BIND_STACKABLE,NULL);
}

void check_tcl_splt(nick,uhost,hand,chname)
char *nick,*uhost,*hand,*chname;
{
  int atr; char args[512];
  context;
  sprintf(args,"%s %s!%s",chname,nick,uhost);
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_splt,args,atr," $_n $_uh $_h $_a",
		   MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

void check_tcl_rejn(nick,uhost,hand,chname)
char *nick,*uhost,*hand,*chname;
{
  int atr; char args[512];
  context;
  sprintf(args,"%s %s!%s",chname,nick,uhost);
  atr=get_attr_handle(hand);
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",chname,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_rejn,args,atr," $_n $_uh $_h $_a",
		   MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE,NULL);
}

int check_tcl_filt(idx,text)
int idx; char *text;
{
  char s[10]; int x,atr;
  atr=get_attr_handle(dcc[idx].nick); sprintf(s,"%d",dcc[idx].sock);
  Tcl_SetVar(interp,"_n",s,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",text,TCL_GLOBAL_ONLY);
  x=check_tcl_bind(&H_filt,text,atr," $_n $_a",
		 MATCH_MASK|BIND_USE_ATTR|BIND_STACKABLE|BIND_WANTRET,NULL);
  return (x==BIND_EXEC_LOG);
}

int check_tcl_flud(nick,uhost,hand,ftype,chname)
char *nick,*uhost,*hand,*ftype,*chname;
{
  int x;
  context;
  Tcl_SetVar(interp,"_n",nick,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_uh",uhost,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",hand,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",ftype,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",chname,TCL_GLOBAL_ONLY);
  x=check_tcl_bind(&H_flud,ftype,0," $_n $_uh $_h $_a $_aa",
		   MATCH_MASK|BIND_STACKABLE|BIND_WANTRET,NULL);
  return (x==BIND_EXEC_LOG);
}

int check_tcl_note(from,to,text)
char *from,*to,*text;
{
  int x;
  context;
  Tcl_SetVar(interp,"_n",from,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_h",to,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",text,TCL_GLOBAL_ONLY);
  x=check_tcl_bind(&H_note,to,0," $_n $_h $_a",MATCH_EXACT,NULL);
  return ((x==BIND_MATCHED)||(x==BIND_EXECUTED)||(x==BIND_EXEC_LOG));
}

void check_tcl_act(from,chan,text)
char *from,*text; int chan;
{
  char s[10];
  context;
  sprintf(s,"%d",chan);
  Tcl_SetVar(interp,"_n",from,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",s,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_aa",text,TCL_GLOBAL_ONLY);
  check_tcl_bind(&H_act,text,0," $_n $_a $_aa",MATCH_MASK|BIND_STACKABLE,
		 NULL);
}

int check_tcl_wall(from,msg)
char *from,*msg;
{
  int x;
  context;
  Tcl_SetVar(interp,"_n",from,0);
  Tcl_SetVar(interp,"_a",msg,0);
  x=check_tcl_bind(&H_wall,msg,0," $_n $_a",MATCH_MASK|BIND_STACKABLE);
  if (x==BIND_EXEC_LOG) {
    putlog(LOG_WALL,"*","!%s! %s",from,msg);
    return 1;
  } else return 0;
}

void tell_binds(idx,name)
int idx; char *name;
{
  Tcl_HashEntry *he; Tcl_HashSearch srch; Tcl_HashTable *ht; int i,fnd=0;
  tcl_cmd_t *tt; char typ[5],*s,*proc,flg[20]; int kind;
  context;
  kind=get_bind_type(name);
  for (i=0; i<BINDS; i++) if ((kind==(-1)) || (kind==i)) {
    ht=gethashtable(i,NULL,typ);
    for (he=Tcl_FirstHashEntry(ht,&srch); (he!=NULL);
	 he=Tcl_NextHashEntry(&srch)) {
      if (!fnd) {
	dprintf(idx,"Command bindings:\n"); fnd=1;
	dprintf(idx,"  TYPE FLGS COMMAND              BINDING (TCL)\n");
      }
      tt=(tcl_cmd_t *)Tcl_GetHashValue(he);
      s=Tcl_GetHashKey(ht,he);
      while (tt!=NULL) {
	proc=tt->func_name; flags2str(tt->flags_needed,flg);
	if ((proc[0]!='*') || (strcmp(s,proc+1)!=0))
	  dprintf(idx,"  %-4s %-4s %-20s %s\n",typ,flg,s,tt->func_name);
	tt=tt->next;
      }
    }
  }
  if (!fnd) {
    if (kind==(-1)) dprintf(idx,"No command bindings.\n");
    else dprintf(idx,"No bindings for %s.\n",name);
  }
}

int call_tcl_func(name,idx,args)
char *name,*args; int idx;
{
  char s[11];
  set_tcl_vars(); sprintf(s,"%d",idx);
  Tcl_SetVar(interp,"_n",s,TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp,"_a",args,TCL_GLOBAL_ONLY);
  if (Tcl_VarEval(interp,name," $_n $_a",NULL)==TCL_ERROR) {
    putlog(LOG_MISC,"*","Tcl error [%s]: %s",name,interp->result);
    return -1;
  }
  get_tcl_vars();
  return (atoi(interp->result));
}
