#include <ctype.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifdef DMALLOC
#include <dmalloc.h>
#endif

#include "tcltk.h"
#include "serverdcc.h"
#include "window.h"
#include "quirc.h"
#include "format.h"
#include "echo.h"
#include "mytcl.h"
#include "messages.h"


int TT_Proc___template__acceptlist(TT_PROC_ARGS) {
  //acceptlist add    send  NICK PSUEDOIP PORT FILE [SIZE]
  //acceptlist add    chat  NICK PSEUDOIP PORT
  //acceptlist accept IDTAG
  //acceptlist overwrite IDTAG
  //acceptlist rename IDTAG filename
  //acceptlist resume IDTAG (Not present)
  //acceptlist accept IDTAG
  //acceptlist reject IDTAG
  // 0          2      3     4       5     6    7     8
  tacceptitem acceptitem;
  tacceptitem *acceptitemp;
  struct in_addr in;
  twindow window;
  twindow *windowp;
  char num[21];
  tdcc *dccp;
  if(argc<4||argc>9) {
    Tcl_SetResult(interp,"acceptlist: Invalid number of arguments",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if(!(windowp=windows.find(window))) {
    Tcl_SetResult(interp,"acceptlist: Invalid index argument",TCL_VOLATILE);
    return TCL_ERROR;
  }
  if(!windowp->server) {
    Tcl_SetResult(interp,"acceptlist: Invalid server",TCL_VOLATILE);
    return TCL_ERROR;
  }
  if(!strcasecmp(argv[2],"add")) {
    if(!strcasecmp(argv[3],"chat")) {
      if(!strlen(argv[4])||!strlen(argv[5])||!strlen(argv[6])||!atoi(argv[5])||!atoi(argv[6])) {
	Tcl_SetResult(interp,"acceptlist: Invalid arguments given",TCL_VOLATILE);
	return TCL_ERROR;
      }
      acceptitem.setnick(argv[4]);
      sscanf(argv[5],"%u",&in.s_addr);
      in.s_addr=ntohl(in.s_addr);
      strcpy(acceptitem.ip,inet_ntoa(in));
      acceptitem.port=atoi(argv[6]);
      acceptitem.id=acceptnum++;
      acceptitem.type=DCCCHAT;
      windowp->server->acceptlist.insert_end(acceptitem);
      sprintf(num,"%d",acceptitem.id);
      windowp->server->script("event_dcc_accept","chat q q q q",num,argv[4],inet_ntoa(in),argv[6]);
      return TCL_OK;
    }
    if(!strcasecmp(argv[3],"send")) {
      if(!strlen(argv[4])||!strlen(argv[5])||!strlen(argv[6])||!atoi(argv[5])||!atoi(argv[6])||(argc!=8&&argc!=9)||!strlen(argv[7])) {
	Tcl_SetResult(interp,"acceptlist: Invalid arguments given",TCL_VOLATILE);
	return TCL_ERROR;
      }
      acceptitem.setnick(argv[4]);
      sscanf(argv[5],"%u",&in.s_addr);
      in.s_addr=ntohl(in.s_addr);
      strcpy(acceptitem.ip,inet_ntoa(in));
      acceptitem.port=atoi(argv[6]);
      acceptitem.id=acceptnum++;
      acceptitem.type=DCCFILEGET;
      strcpy(acceptitem.file,argv[7]);
      if(argc==9&&strlen(argv[8])&&atoi(argv[8])) {
	acceptitem.size=atoi(argv[8]);
      } else {
	acceptitem.size=0;
      }
      windowp->server->acceptlist.insert_end(acceptitem);
      sprintf(num,"%d",acceptitem.id);
      if(argc==8) {
	windowp->server->script("event_dcc_accept","s q q q q q","send",num,argv[4],inet_ntoa(in),argv[6],argv[7]);
      }
      if(argc==9&&(strcmp(argv[8],"0")&&!atoi(argv[8]))) {
	Tcl_SetResult(interp,"acceptlist: Invalid arguments given",TCL_VOLATILE);
	return TCL_ERROR;
      }
      if(argc==9) {
	windowp->server->script("event_dcc_accept","s q q q q q q","send",num,argv[4],inet_ntoa(in),argv[6],argv[7],argv[8]);
      }
      return TCL_OK;
    }
  }
  if(!strcasecmp(argv[2],"accept")) {
    acceptitem.id=atoi(argv[3]);
    if((acceptitemp=windowp->server->acceptlist.find(acceptitem))) {
      if(acceptitemp->type==DCCCHAT) {
	dccp=new tdcc(windowp->server,acceptitemp->getnick(),acceptitemp->ip,acceptitemp->port);
      } else if(acceptitemp->type==DCCFILEGET) {
	dccp=new tdcc(windowp->server,acceptitemp->getnick(),acceptitemp->ip,acceptitemp->port,acceptitemp->file,acceptitemp->size,0,acceptitemp->file);
      } else {
	Tcl_SetResult(interp,"acceptlist: Invalid accept type",TCL_STATIC);
	return TCL_ERROR;
      }
      if(!dccp) {
	Tcl_SetResult(interp,"acceptlist: Out of Memory",TCL_STATIC);
	return TCL_ERROR;
      }
      if(dccp->errortest) delete dccp;
      return TCL_OK;
    } else {
      Tcl_SetResult(interp,"acceptlist: Invalid accept index",TCL_VOLATILE);
      return TCL_ERROR;
    }
    windowp->server->acceptlist.deleteitem(acceptitem);
    return TCL_OK;
  }
  if(!strcasecmp(argv[2],"rename")) {
    if(argc!=5 || !strlen(argv[3]) || !strlen(argv[4])) {
      Tcl_SetResult(interp,"usage: acceptlist rename <idtag> <new_name> (Invalid data contained in, or number of parameters.",TCL_STATIC);
      return TCL_ERROR;
    }
    acceptitem.id=atoi(argv[3]);
    if((acceptitemp=windowp->server->acceptlist.find(acceptitem))) {
      if(acceptitemp->type!=DCCFILEGET) {
	Tcl_SetResult(interp,"usage: acceptlist overwrite <idtag> (Only DCC File Gets can use the overwrite option.)",TCL_STATIC);
	return TCL_ERROR;
      }
      dccp=new tdcc(windowp->server,acceptitemp->getnick(),acceptitemp->ip,acceptitemp->port,acceptitemp->file,acceptitemp->size,0,argv[4]);
      if(dccp->errortest) delete dccp;
      return TCL_OK;
    } else {
      Tcl_SetResult(interp,"acceptlist: Invalid accept index",TCL_STATIC);
      return TCL_ERROR;
    }
    windowp->server->acceptlist.deleteitem(acceptitem);
    return TCL_OK;
  }
  if(!strcasecmp(argv[2],"overwrite")) {
    acceptitem.id=atoi(argv[3]);
    if((acceptitemp=windowp->server->acceptlist.find(acceptitem))) {
      if(acceptitemp->type!=DCCFILEGET) {
	Tcl_SetResult(interp,"usage: acceptlist overwrite <idtag> (Only DCC File Gets can use the overwrite option.)",TCL_STATIC);
	return TCL_ERROR;
      }
      dccp=new tdcc(windowp->server,acceptitemp->getnick(),acceptitemp->ip,acceptitemp->port,acceptitemp->file,acceptitemp->size,DCC_OVERWRITE,acceptitemp->file);
      if(dccp->errortest) delete dccp;
      return TCL_OK;
    } else {
      Tcl_SetResult(interp,"acceptlist: Invalid accept index",TCL_STATIC);
      return TCL_ERROR;
    }
    windowp->server->acceptlist.deleteitem(acceptitem);
    return TCL_OK;
  }
  if(!strcasecmp(argv[2],"reject")) {
    acceptitem.id=atoi(argv[3]);
    windowp->server->acceptlist.deleteitem(acceptitem);
    return TCL_OK;
  }
  return TCL_OK;
  return TCL_ERROR;
}

int TT_Proc___template__channelmode(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  if(argc!=4) {
    Tcl_SetResult(interp,"Usage: channelmode <mode letter> <channel>",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if(!(windowp=windows.find(window))||!windowp->server) {
    Tcl_SetResult(interp,"channelmode: Invalid server\n",TCL_VOLATILE);
    return TCL_ERROR;
  }
  strcpy(channel.name,argv[3]);
  if((channelp=windowp->server->chanlist.find(channel))) {
    if(*argv[2]=='k') {
      Tcl_SetResult(interp,channelp->keyword,TCL_VOLATILE);
      return TCL_OK;
    } else if(*argv[2]=='l') {
      Tcl_SetResult(interp,strnum(channelp->limit),TCL_VOLATILE);
      return TCL_OK;
    } else {
      Tcl_SetResult(interp,strnum(channelp->getmode(*argv[2])),TCL_VOLATILE);
      return TCL_OK;
    }
  }
  Tcl_SetResult(interp,"channelmode: Channel not found",TCL_VOLATILE);
  return TCL_ERROR;
}

int TT_Proc___template__channels(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  string assemble;
  if(argc!=2) {
    Tcl_SetResult(interp,"Usage: channels",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      windowp->server->chanlist.init_trav();
      while((channelp=windowp->server->chanlist.trav())) {
	if(assemble.length()) assemble+=" ";
	assemble+=channelp->name;
      }
      Tcl_SetResult(interp,(char *)TT_StrF(TT_ARGS,"split %q \" \"",assemble.c_str()),TCL_VOLATILE);
    }
  }
  return TCL_OK;
}

int TT_Proc___template__connected(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  if(argc>=2) {
    Tcl_SetResult(interp,"0",TCL_VOLATILE);
    sprintf(window.pathname,".status%s",argv[1]);
    if((windowp=windows.find(window))) {
      if(windowp->server) {
	Tcl_SetResult(interp,strnum(windowp->server->connected),TCL_VOLATILE);
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__createchannel(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: createchannel <channel>",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  Tcl_SetResult(interp,"0",TCL_VOLATILE);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      windowp->server->createchannel(argv[2]);
    }
  }
  return TCL_OK;
}

int TT_Proc___template__createquery(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: createquery <nick>",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  Tcl_SetResult(interp,"0",TCL_VOLATILE);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      windowp->server->createquery(argv[2]);
    }
  }
  return TCL_OK;
}

int TT_Proc___template__dcc(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  tdcc *dccp;
  if(argc<4||argc>5) {
    Tcl_SetResult(interp,"Usage: dcc <type> <nick> [filename]",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if(!(windowp=windows.find(window))) {
    Tcl_SetResult(interp,"dcc: Invalid index argument",TCL_VOLATILE);
    return TCL_ERROR;
  }
  if(!windowp->server) {
    Tcl_SetResult(interp,"dcc: Invalid server",TCL_VOLATILE);
    return TCL_ERROR;
  }
  if(!strlen(argv[3])) {
    Tcl_SetResult(interp,"dcc: Empty nick not allowed",TCL_VOLATILE);
    return TCL_ERROR;
  }
  if(argc==5&&!strlen(argv[4])) {
    Tcl_SetResult(interp,"dcc: Empty filename not allowed",TCL_VOLATILE);
    return TCL_ERROR;
  }
  if(!strcasecmp(argv[2],"CHAT")&&argc==4) {
    dccp=new tdcc(windowp->server,argv[3]);
    if(dccp->errortest) delete dccp; else return TCL_OK;
  }
  if(!strcasecmp(argv[2],"SEND")&&argc==5) {
    dccp=new tdcc(windowp->server,argv[3],argv[4]);
    if(dccp->errortest) delete dccp; else return TCL_OK;
  }
  Tcl_SetResult(interp,"dcc: Error occured while attempting to create DCC",TCL_STATIC);
  return TCL_ERROR;
}

int TT_Proc___template__dccquote(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  twindow *dccwindowp;
  if(argc!=4) {
    Tcl_SetResult(interp,"usage: dccquote [=]<nick> <message>",TCL_STATIC);
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if(!(windowp=windows.find(window))||!windowp->server) {
    Tcl_SetResult(interp,"dccquote: The server for the given server number could not be found.",TCL_STATIC);
    return TCL_ERROR;
  }
  windows.init_trav();
  while((dccwindowp=windows.trav())) {
    if(dccwindowp->server==windowp->server&&dccwindowp->dcc) {
      if((!strcasecmp(dccwindowp->dcc->getnick(),argv[2]))||(!strcasecmp(dccwindowp->name,argv[2]))) {
	// DEBUG
	//TT_EvalF(TT_ARGS,".raw.text insert end \"DCC Command (tcl.cc:1078): %q\\n\"",argv[3]);
	dccwindowp->dcc->senddata(argv[3]);
	return TCL_OK;
      }
    }
  }
  Tcl_SetResult(interp,"usage: dccquote [=]<nick> <message> (Could not find the given nick)",TCL_STATIC);
  return TCL_ERROR;
}

int TT_Proc___template__fdisplay(TT_PROC_ARGS) {
  if(argc<3) {
    Tcl_SetResult(interp,"Usage: fdisplay <type> [arguments]",TCL_STATIC);
    return TCL_ERROR;
  }
  if(!fexists(argv[2])) {
    Tcl_SetResult(interp,"fdisplay: Invalid format type.",TCL_STATIC);
    return TCL_ERROR;
  }
  fdisplayv(argv[2],atoi(argv[1]),argc-3,&argv[3]);
  return TCL_OK;
}

int TT_Proc___template__fgetpathnames(TT_PROC_ARGS) {
  if(argc<3) {
    Tcl_SetResult(interp,"Usage: fgetpathnames <type> [arguments]",TCL_STATIC);
    return TCL_ERROR;
  }
  if(!fexists(argv[2])) {
    Tcl_SetResult(interp,"fgetpathnames: Invalid format type.",TCL_STATIC);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp,fgetpathnames(argv[2],atoi(argv[1]),argc-3,&argv[3]),TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc___template__fixnicklist(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  char temp[1000];
  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: fixnicklist <channel>",TCL_STATIC);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      strcpy(channel.name,argv[2]);
      if((channelp=windowp->server->chanlist.find(channel))) {
	channelp->clearnicklist();
	windowp->server->hide.n353=1;
	windowp->server->hide.n366=1;
	sprintf(temp,"NAMES %s",channelp->name);
	// DEBUG
	//TT_EvalF(TT_ARGS,".raw.text insert end \"Command (tcl.cc:1108): %q\\n\"",temp);
	windowp->server->senddata(temp);
      } else {
	Tcl_SetResult(interp,"Usage: fixnicklist <channel> (Invalid channel given)",TCL_STATIC);
	return TCL_ERROR;
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__fparse(TT_PROC_ARGS) {
  // ::template::fparse <server> <type> [arguments]
  // 0                  1        2      3+
  // argc 1             2        3      4
  char *result;
  if(argc<3) {
    Tcl_SetResult(interp,"Usage: fparse <type> [arguments]",TCL_STATIC);
    return TCL_ERROR;
  }
  if(!fexists(argv[2])) {
    Tcl_SetResult(interp,"fparse: Invalid format type.",TCL_STATIC);
    return TCL_ERROR;
  }
  if(!(result=fparsev(argv[2],atoi(argv[1]),argc-3,&argv[3]))) {
    Tcl_SetResult(interp,"fparse: Error parsing format (No argument present for format argument number?).",TCL_STATIC);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp,result,TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc___template__index(TT_PROC_ARGS) {
  if(argc!=2) {
    Tcl_SetResult(interp,"Usage: index",TCL_VOLATILE);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp,(char *)argv[1],TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc___template__ison(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  tnick *nickp;
  if(argc!=4) {
    Tcl_SetResult(interp,"Usage: ison <nick> <channel>",TCL_STATIC);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      strcpy(channel.name,argv[3]);
      Tcl_SetResult(interp,"0",TCL_STATIC);
      if((channelp=windowp->server->chanlist.find(channel))) {
	tnick nick(argv[2]);
	if((nickp=channelp->nicklist.find(nick))) {
	  Tcl_SetResult(interp,"1",TCL_STATIC);
	}
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__isop(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  tnick *nickp;
  if(argc!=4) {
    Tcl_SetResult(interp,"Usage: isop <nick> <channel>",TCL_STATIC);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      strcpy(channel.name,argv[3]); 
      Tcl_SetResult(interp,"0",TCL_STATIC);
      if((channelp=windowp->server->chanlist.find(channel))) {
	tnick nick(argv[2]);
	if((nickp=channelp->nicklist.find(nick))) {
	  if(nickp->oped) Tcl_SetResult(interp,"1",TCL_STATIC);
	}
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__isvoice(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  tnick *nickp;
  if(argc!=4) {
    Tcl_SetResult(interp,"Usage: isvoice <nick> <channel>",TCL_STATIC);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      strcpy(channel.name,argv[3]);
      Tcl_SetResult(interp,"0",TCL_STATIC);
      if((channelp=windowp->server->chanlist.find(channel))) {
	tnick nick(argv[2]);
	if((nickp=channelp->nicklist.find(nick))) {
	  if(nickp->voiced) Tcl_SetResult(interp,"1",TCL_STATIC);
	}
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__killdcc(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  tdccentry dccentry;
  tdccentry *dccentryp;
  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: killdcc <dcc_entry_index>",TCL_VOLATILE);
    return TCL_ERROR;
  }
  dccentry.index=atoi(argv[2]);
  if(dccentry.index!=-1) {
    sprintf(window.pathname,".status%s",argv[1]);
    if((windowp=windows.find(window))) {
      if(windowp->server) {
	if((dccentryp=windowp->server->dcclist.find(dccentry))) {
	  dccentryp->dcc->disconnect();
	}
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__modenicks(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  tnick *nickp;
  string assemble;
  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: modenicks #channel",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      strcpy(channel.name,argv[2]);
      if((channelp=windowp->server->chanlist.find(channel))) {
	channelp->nicklist.init_trav();
	while((nickp=channelp->nicklist.trav())) {
	  if(assemble.length()) assemble+=" ";
	  assemble+=nickp->getentry();
	}
	Tcl_SetResult(interp,(char *)TT_StrF(TT_ARGS,"split %q \" \"",assemble.c_str()),TCL_VOLATILE);
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__modes(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  char mode;
  char assemble[TEMPLEN]={0};
  char extra[TEMPLEN]={0};
  char tiny[2]={0};
  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: modes <channel>",TCL_STATIC);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      strcpy(channel.name,argv[2]);
      if((channelp=windowp->server->chanlist.find(channel))) {
	for(mode='a';mode<='z';mode++) {
	  if(channelp->getmode(mode)) {
	    if(!*assemble) strcpy(assemble,"+");
	    *tiny=mode;
	    strcat(assemble,tiny);
	    if(mode=='k') {
	      strcat(extra," ");
	      strcat(extra,channelp->keyword);
	    } else if (mode=='l') {
	      strcat(extra," ");
	      strcat(extra,strnum(channelp->limit));
	    }
	  }
	}
	for(mode='A';mode<='Z';mode++) {
	  if(channelp->getmode(mode)) {
	    if(!*assemble) strcpy(assemble,"+");
	    *tiny=mode;
	    strcat(assemble,tiny);
	  }
	}
	strcat(assemble,extra);
      }
    }
  }
  Tcl_SetResult(interp,assemble,TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc___template__mynick(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  if(argc>=2) {
    sprintf(window.pathname,".status%s",argv[1]);
    if((windowp=windows.find(window))) {
      if(windowp->server) {
	Tcl_SetResult(interp,(char*)windowp->server->getmynick(),TCL_VOLATILE);
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__nicks(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  tnick *nickp;
  string assemble;
  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: nicks #channel",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      strcpy(channel.name,argv[2]);
      if((channelp=windowp->server->chanlist.find(channel))) {
	channelp->nicklist.init_trav();
	while((nickp=channelp->nicklist.trav())) {
	  if(assemble.length()) assemble+=" ";
	  assemble+=nickp->getname();
	}
	Tcl_SetResult(interp,(char *)TT_StrF(TT_ARGS,"split %q \" \"",assemble.c_str()),TCL_VOLATILE);
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__onchannel(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: onchannel <channel>",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  Tcl_SetResult(interp,"0",TCL_STATIC);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      strcpy(channel.name,argv[2]);
      if((channelp=windowp->server->chanlist.find(channel))) {
	Tcl_SetResult(interp,strnum(channelp->ison),TCL_VOLATILE);
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__pathname(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  tquery query;
  tquery *queryp;
  twindow window;
  twindow *windowp;
  twindow *dccwindowp;
  Tcl_Obj *listptr;
  Tcl_Obj *objptr;
  if(argc<3||argc>4) {
    Tcl_SetResult(interp,"usage: pathname <type> [specifier]",TCL_STATIC);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      if(!strcasecmp(argv[2],"all")) {
	listptr=Tcl_NewListObj(0,0);
	windows.init_trav();
	while((dccwindowp=windows.trav())) {
	  if(dccwindowp->server==windowp->server) {
	    objptr=Tcl_NewStringObj(dccwindowp->pathname,strlen(dccwindowp->pathname));
	    if(Tcl_ListObjAppendElement(interp,listptr,objptr)==TCL_ERROR) return TCL_ERROR;
	  }
	}
	Tcl_SetObjResult(interp,listptr);
	return TCL_OK;
      }
      if(!strcasecmp(argv[2],"status")) {
	Tcl_SetResult(interp,windowp->server->pathname,TCL_VOLATILE);
	return TCL_OK;
      }
      if(!strcasecmp(argv[2],"files")) {
	windows.init_trav();
	while((dccwindowp=windows.trav())) {
	  if(dccwindowp->server==windowp->server&&!strncmp(".files",dccwindowp->pathname,6)) {
	    Tcl_SetResult(interp,dccwindowp->pathname,TCL_VOLATILE);
	    return TCL_OK;
	  }
	}
	return TCL_OK;
      }
      if(argc==4) {
        if(!strcasecmp(argv[2],"chat")) {
          windows.init_trav();
	  while((dccwindowp=windows.trav())) {
	    if(dccwindowp->server==windowp->server&&dccwindowp->dcc) {
	      if((!strcasecmp(dccwindowp->dcc->getnick(),argv[3]))||(!strcasecmp(dccwindowp->name,argv[3]))) {
		Tcl_SetResult(interp,dccwindowp->pathname,TCL_VOLATILE);
		return TCL_OK;
	      }
	    }
	  }
	}
	if(!strcasecmp(argv[2],"channel")) {
	  strcpy(channel.name,argv[3]);
	  if((channelp=windowp->server->chanlist.find(channel))) {
	    Tcl_SetResult(interp,channelp->pathname,TCL_VOLATILE);
	  }
	}
	if(!strcasecmp(argv[2],"query")) {
	  query.setname(argv[3]);
	  if((queryp=windowp->server->querylist.find(query))) {
	    Tcl_SetResult(interp,queryp->pathname,TCL_VOLATILE);
	  }
	}
	if(!strcasecmp(argv[2],"guess")) {
	  strcpy(channel.name,argv[3]);
	  if((channelp=windowp->server->chanlist.find(channel))) {
	    Tcl_SetResult(interp,channelp->pathname,TCL_VOLATILE);
	    return TCL_OK;
	  }
	  query.setname(argv[3]);
	  if((queryp=windowp->server->querylist.find(query))) {
	    Tcl_SetResult(interp,queryp->pathname,TCL_VOLATILE);
	    return TCL_OK;
	  }
	  windows.init_trav();
	  while((dccwindowp=windows.trav())) {
	    if(dccwindowp->server==windowp->server&&dccwindowp->dcc) {
	      if((!strcasecmp(dccwindowp->dcc->getnick(),argv[3]))||(!strcasecmp(dccwindowp->name,argv[3]))) {
		Tcl_SetResult(interp,dccwindowp->pathname,TCL_VOLATILE);
		return TCL_OK;
	      }
	    }
	  }
	  Tcl_SetResult(interp,windowp->server->pathname,TCL_VOLATILE);
	}
	return TCL_OK;
      }
      strcpy(channel.name,argv[2]);
      if((channelp=windowp->server->chanlist.find(channel))) {
	Tcl_SetResult(interp,channelp->pathname,TCL_VOLATILE);
	return TCL_OK;
      }
      query.setname(argv[2]);
      if((queryp=windowp->server->querylist.find(query))) {
	Tcl_SetResult(interp,queryp->pathname,TCL_VOLATILE);
	return TCL_OK;
      }
      windows.init_trav();
      while((dccwindowp=windows.trav())) {
	if(dccwindowp->server==windowp->server&&dccwindowp->dcc) {
	  if((!strcasecmp(dccwindowp->dcc->getnick(),argv[2]))||(!strcasecmp(dccwindowp->name,argv[2]))) {
	    Tcl_SetResult(interp,dccwindowp->pathname,TCL_VOLATILE);
	    return TCL_OK;
	  }
	}
      }
      Tcl_SetResult(interp,windowp->server->pathname,TCL_VOLATILE);
    }
  }
  return TCL_OK;
}

int TT_Proc___template__popup(TT_PROC_ARGS) {
  //popup add item <type> <submenu> <title> <command>
  //popup add submenu <type> <submenu> <title>
  //popup clear <type> <submenu>
  //  0     2    3      4         5       6      7
  twindow window;
  twindow *windowp;
  char menuname[TEMPLEN];
  char basemenuname[TEMPLEN];
  char *position;

  if(argc<5||argc==6||argc>8) {
    Tcl_SetResult(interp,"Incorrect number of arguments.",TCL_VOLATILE);
    return TCL_ERROR;
  }

  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      if(!strcasecmp(argv[2],"add")&&!strcasecmp(argv[3],"item")) {
	if(!strncmp(argv[4],"main",4)) {
	  return TCL_OK;
	} else {
	  sprintf(menuname,".menu%s%s%s",argv[4],argv[1],argv[5]);
	}
        TT_EvalF(TT_ARGS,"%s add command -label \"%q\" -command { namespace eval ::%d %q }",menuname,argv[6],windowp->server->index,argv[7]);
      }
      if(!strcasecmp(argv[2],"add")&&!strcasecmp(argv[3],"submenu")) {
	if(!strncmp(argv[4],"main",4)) {
	  return TCL_OK;
	} else {
	  sprintf(menuname,".menu%s%s%s",argv[4],argv[1],argv[5]);
	}
        position=strrchr(menuname,'.');
	*position=0;
	strcpy(basemenuname,menuname);
	*position='.';
        TT_EvalF(TT_ARGS,"%s add cascade -label \"%q\" -menu \"%q\"",basemenuname,argv[6],menuname);
        TT_EvalF(TT_ARGS,"menu %s",menuname);
      }
      if(!strcasecmp(argv[2],"clear")) {
	if(!strncmp(argv[3],"main",4)) {
	  return TCL_OK;
	} else {
	  sprintf(menuname,".menu%s%s%s",argv[3],argv[1],argv[4]);
	}
        TT_EvalF(TT_ARGS,"destroy %s",menuname);
        TT_EvalF(TT_ARGS,"menu %s",menuname);
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__queries(TT_PROC_ARGS) {
  tquery *queryp;
  twindow window;
  twindow *windowp;
  string assemble;
  if(argc!=2) {
    Tcl_SetResult(interp,"Usage: queries",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      windowp->server->querylist.init_trav();
      while((queryp=windowp->server->querylist.trav())) {
	if(assemble.length()) assemble+=" ";
	assemble+=queryp->getname();
      }
      Tcl_SetResult(interp,(char *)TT_StrF(TT_ARGS,"split %q \" \"",assemble.c_str()),TCL_VOLATILE);
    }
  }
  return TCL_OK;
}

int TT_Proc___template__quote(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  if(argc>=3) {
    sprintf(window.pathname,".status%s",argv[1]);
    if((windowp=windows.find(window))) {
      if(windowp->server) {
	// DEBUG
	//TT_EvalF(TT_ARGS,".raw.text insert end \"Command (tcl.cc:1512): %q\\n\"",argv[2]);
	windowp->server->senddata(argv[2]);
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__script(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  char temp[TEMPLEN];
  char assemble[TEMPLEN];
  char *pos;
  const char *afterslash;
  int n;
  if(argc>=3) {
    sprintf(window.pathname,".status%s",argv[1]);
    if((windowp=windows.find(window))) {
      if(windowp->server) {
	temp[0]=0;
	afterslash=argv[2];
	while((pos=strstr(afterslash,"/"))) afterslash=pos+1;;
	for(n=0;n<(signed)strlen(afterslash);n++) {
	  if(isalnum(afterslash[n])) {
	    temp[n]=afterslash[n];
	  } else {
	    temp[n]=0;
	    break;
	  }
	}
	if(strlen(temp)) {
	  TT_EvalF(TT_ARGS,"namespace eval ::%d::%s { namespace import -force ::%d::* }",windowp->server->index,temp,windowp->server->index);
	  TT_EvalF(TT_ARGS,"if { [lsearch -exact $::%d::scripts %s]!=-1 } { set ::%d::scripts [lreplace $::%d::scripts [lsearch -exact $::%d::scripts %s] [lsearch -exact $::%d::scripts %s]] }",windowp->server->index,temp,windowp->server->index,windowp->server->index,windowp->server->index,temp,windowp->server->index,temp);
	  TT_EvalF(TT_ARGS,"set ::%d::scripts [linsert $::%d::scripts 0 %s]",windowp->server->index,windowp->server->index,temp);
	  TT_EvalF(TT_ARGS,"namespace eval ::%d::%s { source \"$env(HOME)/.quirc/%q\" }",windowp->server->index,temp,argv[2]);
	  sprintf(assemble,"::%d::%s",windowp->server->index,temp);
	  fdisplay("SCRIPT_LOAD",windowp->server->index,2,argv[2],assemble);
	}
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__server(TT_PROC_ARGS) {
  char *check;
  char *colon;
  char *copy;
  twindow window;
  twindow *windowp;
  
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      if(argc==2) {
	windowp->server->server("",0,"");
      } else {
	if(!strlen(argv[2])) {
	  windowp->server->server("",0,"");
	} else {
	  switch(argc) {
	  case 3:
	    if(!(copy=strdup(argv[2]))) {
	      fprintf(stderr,M_OUT_OF_MEMORY);
	    }
	    if(!(colon=strstr(copy,":"))) {
	      // server
	      windowp->server->server(copy,0,"");
	    } else {
	      *colon=0;
	      colon++;
	      if(!(check=strstr(colon,":"))) {
		// server:port
		windowp->server->server(copy,atoi(colon),"");
	      } else {
		// server::password
		// server:port:password
		*check=0;
		windowp->server->server(copy,atoi(colon),check+1);
	      }
	    }
	    free(copy);
	    break;
	  case 4:
	    // server port
	    windowp->server->server(argv[2],atoi(argv[3]),"");
	    break;
	  case 5:
	    // server port password
	    windowp->server->server(argv[2],atoi(argv[3]),argv[4]);
	    break;
	  default:
	    Tcl_SetResult(interp,"usage: server [server[:[port][:password]]",TCL_STATIC);
	    return TCL_ERROR;
	  }
	}
      }
      return TCL_OK;
    }
  }
  Tcl_SetResult(interp,"usage: server [server[:[port][:password]] (Invalid server index specified.)",TCL_STATIC);
  return TCL_ERROR;
}

int TT_Proc___template__topic(TT_PROC_ARGS) {
  tchan channel;
  tchan *channelp;
  twindow window;
  twindow *windowp;
  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: topic <channel>",TCL_STATIC);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      strcpy(channel.name,argv[2]);
      if((channelp=windowp->server->chanlist.find(channel))) {
	if(channelp->topic) {
	  Tcl_SetResult(interp,channelp->topic,TCL_VOLATILE);
	}
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__unscript(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  char temp[TEMPLEN];
  char storage[TEMPLEN];
  char *pos;
  const char *afterslash;
  int n;
  if(argc>=3) {
    sprintf(window.pathname,".status%s",argv[1]);
    if((windowp=windows.find(window))) {
      if(windowp->server) {
	temp[0]=0;
	afterslash=argv[2];
	while((pos=strstr(afterslash,"/"))) afterslash=pos+1;;
	for(n=0;n<(signed)strlen(afterslash);n++) {
	  if(isalnum(afterslash[n])) {
	    temp[n]=afterslash[n];
	  } else {
	    temp[n]=0;
	    break;
	  }
	}
	if(strlen(temp)) {
	  sprintf(storage," \0030,4 SCRIPT \003 Running ::%d::%s::event_unload and destroying namespace.",windowp->server->index,temp);
	  echo(storage,windowp->server->pathname,1);
	  TT_EvalF(TT_ARGS,"if { [info commands ::%d::%s::event_unload]!=\"\" } { ::%d::%s::event_unload }",windowp->server->index,temp,windowp->server->index,temp);
	  TT_EvalF(TT_ARGS,"namespace delete ::%d::%s",windowp->server->index,temp);
	  TT_EvalF(TT_ARGS,"if { [lsearch -exact $::%d::scripts %s]!=\"\" } { set ::%d::scripts [lreplace $::%d::scripts [lsearch -exact $::%d::scripts %s] [lsearch -exact $::%d::scripts %s]] }",windowp->server->index,temp,windowp->server->index,windowp->server->index,windowp->server->index,temp,windowp->server->index,temp);
	}
      }
    }
  }
  return TCL_OK;
}

int TT_Proc___template__userhost(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  char *userhost;
  char *temp;
  int n;
  if(argc>=3) {
    sprintf(window.pathname,".status%s",argv[1]);
    if((windowp=windows.find(window))) {
      if(windowp->server) {
	temp = mystrdup(argv[2]);
	for(n=0;n<(signed)strlen(temp);n++) temp[n]=tolower(temp[n]);
	//Tcl_SetResult(interp,(char *)windowp->server->ial[argv[2]].c_str(),TCL_VOLATILE);
	if((userhost=hash_get(&windowp->server->ial,temp))) {
	  Tcl_SetResult(interp,userhost,TCL_VOLATILE);
	}
	free(temp);
      }
    }
  }
  return TCL_OK;
}

int tcl_list_append_string(Tcl_Obj *listptr, string &data) {
  Tcl_Obj *objptr;
  objptr=Tcl_NewStringObj((char *)data.data(),data.length());
  // Need to return error value here!!
  Tcl_ListObjAppendElement(TT_Interp,listptr,objptr);
  return TCL_OK;
}

int userlist_match(Tcl_Obj *obj, string &type, string &info) {
  char *nuh=strdup(Tcl_GetStringFromObj(obj,0));
  char *mask=strdup(info.c_str());
  unsigned n;
  if(!nuh || !mask) {
    fprintf(stderr,M_OUT_OF_MEMORY);
    exit(1);
  }
  for(n=0;n<strlen(nuh);n++) {
    nuh[n]=tolower(nuh[n]);
  }
  for(n=0;n<strlen(mask);n++) {
    mask[n]=tolower(mask[n]);
  }
  if(type=="simple") {
    return Tcl_StringMatch(nuh,mask);
  } else {
    return Tcl_RegExpMatch(TT_Interp,nuh,mask);
  }
}

int TT_Proc___template__userlist(TT_PROC_OBJS) {
  twindow window;
  twindow *windowp;
  tuserlist userlist;
  tuserlist *userlistp;
  tpair pair;
  tpair *pairp;
  int objcp;
  Tcl_Obj **objvp;
  int objcp2;
  Tcl_Obj **objvp2;
  char *str;
  char *data;
  int len;
  int morelen;
  int n;
  char *command;
  char *secondary;
  int invalidcommand=0;
  Tcl_Obj *listptr;
  Tcl_Obj *morelistptr;
  if(objc<3||objc>6) {
    Tcl_SetResult(interp,"usage: userlist <command> [<args> ...]",TCL_STATIC);
    return TCL_ERROR;
  }
  sprintf(window.pathname,".status%s",ARGV(1));
  if(!(windowp=windows.find(window))||!windowp->server) {
    Tcl_SetResult(interp,"First argument passed to ::template::userlist does not refer to a valid server window.",TCL_STATIC);
    return TCL_ERROR;
  }
  command=Tcl_GetStringFromObj(objv[2],0);
  if(!strcasecmp(command,"set")) {
    if(objc>3 && objc<7) {
      // userlist set <type/info> *
      if(Tcl_ListObjGetElements(interp,objv[3],&objcp,&objvp)==TCL_ERROR) return TCL_ERROR;
      if(objcp!=2) {
	Tcl_SetResult(interp,"usage: userlist set <type/info pair> (Type/info pair must contain two elements.)",TCL_STATIC);
	return TCL_ERROR;
      }
      str=Tcl_GetStringFromObj(objvp[0],&len);
      userlist.type.assign(str,len);
      if(strcmp(str,"simple")&&strcmp(str,"regexp")&&strcmp(str,"alias")) {
	Tcl_SetResult(interp,"usage: userlist set <type/info pair> (Type must be one of simple, regexp, or alias.)",TCL_STATIC);
	return TCL_ERROR;
      }
      str=Tcl_GetStringFromObj(objvp[1],&len);
      userlist.info.assign(str,len);
    }
    if(objc==4) {
      // userlist set <type info>
      if(!(userlistp=windowp->server->userlist.find(userlist))) {
	Tcl_SetResult(interp,"usage: userlist set <type/info pair> (Given type/info pair has no defined value.)",TCL_STATIC);
	return TCL_ERROR;
      }
      listptr=Tcl_NewListObj(0,0);
      userlistp->flags.init_trav();
      while((pairp=userlistp->flags.trav())) {
	if(pairp->value.length()) {
	  morelistptr=Tcl_NewListObj(0,0);
	  tcl_list_append_string(morelistptr,pairp->name);
	  tcl_list_append_string(morelistptr,pairp->value);
	  if(Tcl_ListObjAppendElement(interp,listptr,morelistptr)==TCL_ERROR) return TCL_ERROR;
	} else {
	  tcl_list_append_string(listptr,pairp->name);
	}
      }
      Tcl_SetObjResult(interp,listptr);
      return TCL_OK;
    } else if(objc==5 || objc==6) {
      // userlist set <type info> <list of flag/data> [<index>]
      userlist.index=0;
      if((userlistp=windowp->server->userlist.find(userlist))) {
	userlist.flags=userlistp->flags;
	userlist.index=userlistp->index;
	windowp->server->userlist.deleteitem(userlist);
      }
      if(objc==6) {
	// If there's a 6th argument, it's the index.  Use it to insert the
	// item into the userlist.
	str=Tcl_GetStringFromObj(objv[5],0);
	if(!strcmp(str,"end")) {
	  userlist.index=windowp->server->userlist.lastindex()+1;
	} else {
	  userlist.index=atoi(str);
	  if(userlist.index < 0) userlist.index = 0;
	  if(userlist.index > windowp->server->userlist.lastindex()+1) {
	    userlist.index = windowp->server->userlist.lastindex()+1;
	  }
	}
      }
      if(Tcl_ListObjGetElements(interp,objv[4],&objcp,&objvp)==TCL_ERROR) return TCL_ERROR;
      if(objcp==0) {
	userlist.flags.clear();
	windowp->server->userlist.insert_ascending(userlist);
	Tcl_SetResult(interp,strnum(userlist.index),TCL_STATIC);
	return TCL_OK;
      } else {
	for(n=0;n<objcp;n++) {
	  if(Tcl_ListObjGetElements(interp,objvp[n],&objcp2,&objvp2)==TCL_ERROR) {
	    str=Tcl_GetStringFromObj(objvp[n],&len);
	    data="";
	    morelen=0;
	  } else if (objcp2==1) {
	    str=Tcl_GetStringFromObj(objvp2[0],&len);
	    data="";
	    morelen=0;
	  } else if (objcp2==2) {
	    str=Tcl_GetStringFromObj(objvp2[0],&len);
	    data=Tcl_GetStringFromObj(objvp2[1],&morelen);
	  } else {
	    Tcl_SetResult(interp,"usage: userlist set <type/info pair> (flag/data set should be a list of 1 or 2, or a scalar.)",TCL_STATIC);
	    return TCL_ERROR;
	  }
	  if(n==0 && !(len && ( str[0]=='+' || str[0]=='-' ))) {
	    userlist.flags.clear();
	  }
	  if(len) {
	    pair.value.assign(data,morelen);
	    if(str[0]=='+') {
	      pair.name.assign(str+1,len-1);
	      if(!(pairp=userlist.flags.find(pair))) {
		userlist.flags.insert_end(pair);
	      } else {
		pairp->value.assign(data,morelen);
	      }
	    } else if (str[0]=='-') {
	      pair.name.assign(str+1,len-1);
	      userlist.flags.deleteitem(pair);
	    } else {
	      pair.name.assign(str,len);
	      if(!(pairp=userlist.flags.find(pair))) {
		userlist.flags.insert_end(pair);
	      } else {
		pairp->value.assign(data,morelen);
	      }
	    }
	  }
	}
	windowp->server->userlist.insert_ascending(userlist);
	Tcl_SetResult(interp,strnum(userlist.index),TCL_STATIC);
	return TCL_OK;
      }
    } else {
      invalidcommand=1;
    }
  } else if (!strcasecmp(command,"get")) {
    if(objc==3) {
      // userlist get
      windowp->server->userlist.init_trav();
      listptr=Tcl_NewListObj(0,0);
      while((userlistp=windowp->server->userlist.trav())) {
	morelistptr=Tcl_NewListObj(0,0);
	tcl_list_append_string(morelistptr,userlistp->type);
	tcl_list_append_string(morelistptr,userlistp->info);
	if(Tcl_ListObjAppendElement(interp,listptr,morelistptr)==TCL_ERROR) return TCL_ERROR;
      }
      Tcl_SetObjResult(interp,listptr);
      return TCL_OK;
    } else if(objc==4) {
      // userlist get <flag(s)>
      windowp->server->userlist.init_trav();
      listptr=Tcl_NewListObj(0,0);
      while((userlistp=windowp->server->userlist.trav())) {
	// Loop through the userlist flags, inner loop through the passed
	// flags.  If there is a match, add to the list.
	userlistp->flags.init_trav();
	if(Tcl_ListObjGetElements(interp,objv[3],&objcp2,&objvp2)==TCL_ERROR) return TCL_ERROR;
	while((pairp=userlistp->flags.trav())) {
	  for(n=0;n<objcp2;n++) {
	    if(pairp->name==Tcl_GetStringFromObj(objvp2[n],0)) {
	      morelistptr=Tcl_NewListObj(0,0);
	      tcl_list_append_string(morelistptr,userlistp->type);
	      tcl_list_append_string(morelistptr,userlistp->info);
	      if(Tcl_ListObjAppendElement(interp,listptr,morelistptr)==TCL_ERROR) return TCL_ERROR;
	      break;
	    }
	  }
	}
      }
      Tcl_SetObjResult(interp,listptr);
      return TCL_OK;
    } else {
      invalidcommand=1;
    }
  } else if (!strcasecmp(command,"unset")) {
    if(objc==4) {
      // userlist unset <type/info>
      if(Tcl_ListObjGetElements(interp,objv[3],&objcp,&objvp)==TCL_ERROR) return TCL_ERROR;
      if(objcp!=2) {
	Tcl_SetResult(interp,"usage: userlist set <type/info pair> (Type/info pair must contain two elements.)",TCL_STATIC);
	return TCL_ERROR;
      }
      str=Tcl_GetStringFromObj(objvp[0],&len);
      userlist.type.assign(str,len);
      str=Tcl_GetStringFromObj(objvp[1],&len);
      userlist.info.assign(str,len);
      windowp->server->userlist.deleteitem(userlist);
    } else {
      invalidcommand=1;
    }
  } else if (!strcasecmp(command,"move")) {
    if(objc==5) {
      // userlist move <old index> <new index>
      int oldindex;
      int newindex;
      str=Tcl_GetStringFromObj(objv[3],0);
      if(!strcmp(str,"end")) {
	oldindex=windowp->server->userlist.lastindex()+1;
      } else {
	oldindex=atoi(str);
      }
      str=Tcl_GetStringFromObj(objv[4],0);
      if(!strcmp(str,"end")) {
	newindex=windowp->server->userlist.lastindex()+1;
      } else {
	newindex=atoi(str);
      }
      windowp->server->userlist.init_trav();
      while((userlistp=windowp->server->userlist.trav())) {
	if(userlistp->index==oldindex) {
	  userlist.flags=userlistp->flags;
	  userlist.type=userlistp->type;
	  userlist.info=userlistp->info;
	  userlist.index=newindex;
	  windowp->server->userlist.deleteitem(userlist);
	  windowp->server->userlist.insert_ascending(userlist);
	  break;
	}
      }
    } else {
      invalidcommand=1;
    }
  } else if (!strcasecmp(command,"length")) {
    if(objc==3) {
      // userlist length
      Tcl_SetResult(interp,strnum(windowp->server->userlist.lastindex()+1),TCL_VOLATILE);
      return TCL_OK;
    } else {
      invalidcommand=1;
    }
  } else if (!strcasecmp(command,"index")) {
    if(objc==4) {
      // userlist index <type/info>
      if(Tcl_ListObjGetElements(interp,objv[3],&objcp,&objvp)==TCL_ERROR) return TCL_ERROR;
      if(objcp!=2) {
	Tcl_SetResult(interp,"usage: userlist set <type/info pair> (Type/info pair must contain two elements.)",TCL_STATIC);
	return TCL_ERROR;
      }
      str=Tcl_GetStringFromObj(objvp[0],&len);
      userlist.type.assign(str,len);
      str=Tcl_GetStringFromObj(objvp[1],&len);
      userlist.info.assign(str,len);
      Tcl_SetResult(interp,"-1",TCL_STATIC);
      if((userlistp=windowp->server->userlist.find(userlist))) {
	Tcl_SetResult(interp,strnum(userlistp->index),TCL_VOLATILE);
      }
    } else {
      invalidcommand=1;
    }
  } else if (!strcasecmp(command,"clear")) {
    // userlist clear
    if(objc==3) {
      windowp->server->userlist.clear();
    } else {
      invalidcommand=1;
    }
  } else if (!strcasecmp(command,"alias")) {
    // userlist alias *
    tuserlistalias userlistalias;
    tuserlistalias *userlistaliasp;
    tfullpair fullpair;
    tfullpair *fullpairp;
    if(objc>3) {
      // userlist alias <command> *
      secondary=Tcl_GetStringFromObj(objv[3],0);
      if(!strcasecmp(secondary,"set")) {
	if(objc>4 && objc<7) {
	  // userlist alias set <name> *
	  str=Tcl_GetStringFromObj(objv[4],&len);
	  userlistalias.name.assign(str,len);
	}
	if(objc==5) {
	  // userlist alias set <name>
	  if(!(userlistaliasp=windowp->server->aliaslist.find(userlistalias))) {
	    Tcl_SetResult(interp,"usage: userlist alias set <name> (Given alias has no defined value.)",TCL_STATIC);
	    return TCL_ERROR;
	  }
	  listptr=Tcl_NewListObj(0,0);
	  userlistaliasp->typeinfos.init_trav();
	  while((fullpairp=userlistaliasp->typeinfos.trav())) {
	    morelistptr=Tcl_NewListObj(0,0);
	    tcl_list_append_string(morelistptr,fullpairp->name);
	    tcl_list_append_string(morelistptr,fullpairp->value);
	    if(Tcl_ListObjAppendElement(interp,listptr,morelistptr)==TCL_ERROR) return TCL_ERROR;
	  }
	  Tcl_SetObjResult(interp,listptr);
	  return TCL_OK;
	} else if (objc==6) {
	  // userlist alias set <name> <type/info list>
	  if((userlistaliasp=windowp->server->aliaslist.find(userlistalias))) {
	    userlistalias.typeinfos=userlistaliasp->typeinfos;
	    windowp->server->aliaslist.deleteitem(userlistalias);
	  }
	  if(Tcl_ListObjGetElements(interp,objv[5],&objcp,&objvp)==TCL_ERROR) return TCL_ERROR;
	  if(objcp==0) {
	    userlistalias.typeinfos.clear();
	    windowp->server->aliaslist.insert_end(userlistalias);
	    return TCL_OK;
	  } else {
	    for(n=0;n<objcp;n++) {
	      if(Tcl_ListObjGetElements(interp,objvp[n],&objcp2,&objvp2)!=TCL_ERROR && objcp2==2 ) {
		str=Tcl_GetStringFromObj(objvp2[0],&len);
		data=Tcl_GetStringFromObj(objvp2[1],&morelen);
	      } else {
		Tcl_SetResult(interp,"usage: userlist alias set <name> <type/info list> (The type/info list must contain only type/info pairs.)",TCL_STATIC);
		return TCL_ERROR;
	      }
	      if(n==0 && !(len && ( str[0]=='+' || str[0]=='-' ))) {
		userlistalias.typeinfos.clear();
	      }
	      if(len) {
		fullpair.value.assign(data,morelen);
		if(str[0]=='+') {
		  fullpair.name.assign(str+1,len-1);
		  if(strcmp(str+1,"simple") && strcmp(str+1,"regexp")) {
		    Tcl_SetResult(interp,"usage: userlist alias set <name> <type/info list> (The type can be only simple or regexp.)",TCL_STATIC);
		    return TCL_ERROR;
		  }
		  if(!userlistalias.typeinfos.find(fullpair)) {
		    userlistalias.typeinfos.insert_end(fullpair);
		  }
		} else if (str[0]=='-') {
		  fullpair.name.assign(str+1,len-1);
		  if(strcmp(str+1,"simple") && strcmp(str+1,"regexp")) {
		    Tcl_SetResult(interp,"usage: userlist alias set <name> <type/info list> (The type can be only simple or regexp.)",TCL_STATIC);
		    return TCL_ERROR;
		  }
		  userlistalias.typeinfos.deleteitem(fullpair);
		} else {
		  fullpair.name.assign(str,len);
		  if(strcmp(str,"simple") && strcmp(str,"regexp")) {
		    Tcl_SetResult(interp,"usage: userlist alias set <name> <type/info list> (The type can be only simple or regexp.)",TCL_STATIC);
		    return TCL_ERROR;
		  }
		  if(!userlistalias.typeinfos.find(fullpair)) {
		    userlistalias.typeinfos.insert_end(fullpair);
		  }
		}
	      }
	    }
	    windowp->server->aliaslist.insert_ascending(userlistalias);
	    return TCL_OK;
	  }
	} else {
	  invalidcommand=1;
	}
      } else if(!strcasecmp(secondary,"unset")) {
	if(objc==5) {
	  // userlist alias unset <name>
	  str=Tcl_GetStringFromObj(objv[4],&len);
	  userlistalias.name.assign(str,len);
	  windowp->server->aliaslist.deleteitem(userlistalias);
	} else {
	  invalidcommand=1;
	}
      } else if(!strcasecmp(secondary,"list")) {
	if(objc==4) {
	  // userlist alias list
	  listptr=Tcl_NewListObj(0,0);
	  windowp->server->aliaslist.init_trav();
	  while((userlistaliasp=windowp->server->aliaslist.trav())) {
	    tcl_list_append_string(listptr, userlistaliasp->name);
	  }
	  Tcl_SetObjResult(interp,listptr);
	}
      } else if(!strcasecmp(secondary,"clear")) {
	if(objc==4) {
	  // userlist alias clear
	  windowp->server->aliaslist.clear();
	}
      } else if(!strcasecmp(secondary,"expand")) {
	if(objc==5) {
	  // userlist alias expand <type/info>
	  if(Tcl_ListObjGetElements(interp,objv[4],&objcp,&objvp)!=TCL_ERROR && objcp==2 ) {
	    str=Tcl_GetStringFromObj(objvp[0],&len);
	    data=Tcl_GetStringFromObj(objvp[1],&morelen);
	  } else {
	    Tcl_SetResult(interp,"usage: userlist alias expand <type/info> (type/info must be a tcl list of two elements.)",TCL_STATIC);
	    return TCL_ERROR;
	  }
	  listptr=Tcl_NewListObj(0,0);
	  if(!strcmp(str,"alias")) {
	    userlistalias.name.assign(data,morelen);
	    if(!(userlistaliasp=windowp->server->aliaslist.find(userlistalias))) {
	      Tcl_SetResult(interp,"usage: userlist alias expand <alias/name> (Given alias has no defined value.)",TCL_STATIC);
	      return TCL_ERROR;
	    }
	    userlistaliasp->typeinfos.init_trav();
	    while((fullpairp=userlistaliasp->typeinfos.trav())) {
	      morelistptr=Tcl_NewListObj(0,0);
	      tcl_list_append_string(morelistptr,fullpairp->name);
	      tcl_list_append_string(morelistptr,fullpairp->value);
	      if(Tcl_ListObjAppendElement(interp,listptr,morelistptr)==TCL_ERROR) return TCL_ERROR;
	    }
	  } else {
	    if(Tcl_ListObjAppendElement(interp, listptr, objv[4])==TCL_ERROR) return TCL_ERROR;
	  }
	  Tcl_SetObjResult(interp,listptr);
	} else {
	  invalidcommand=1;
	}
      } else {
	Tcl_SetResult(interp,"Unimplemented!",TCL_STATIC);
	return TCL_ERROR;
      }
    } else {
      invalidcommand=1;
    }
  } else if (!strcasecmp(command,"match")) {
    tuserlistalias userlistalias;
    tuserlistalias *userlistaliasp;
    tfullpair fullpair;
    tfullpair *fullpairp;
    int ret;
    if(objc==4) {
      // userlist match nick!user@host
      listptr=Tcl_NewListObj(0,0);
      windowp->server->userlist.init_trav();
      while((userlistp=windowp->server->userlist.trav())) {
	if(userlistp->type=="alias") {
	  userlistalias.name=userlistp->info;
	  if(!(userlistaliasp=windowp->server->aliaslist.find(userlistalias))) {
	    Tcl_SetResult(interp,"usage: userlist match <nick!user@host> (An alias present in the userlist is undefined.)",TCL_STATIC);
	    return TCL_ERROR;
	  }
	  userlistaliasp->typeinfos.init_trav();
	  while((fullpairp=userlistaliasp->typeinfos.trav())) {
	    ret=userlist_match(objv[3],fullpairp->name,fullpairp->value);
	    if(ret==-1) return TCL_ERROR;
	    if(ret) {
	      morelistptr=Tcl_NewListObj(0,0);
	      tcl_list_append_string(morelistptr,userlistp->type);
	      tcl_list_append_string(morelistptr,userlistp->info);
	      if(Tcl_ListObjAppendElement(interp,listptr,morelistptr)==TCL_ERROR) return TCL_ERROR;
	    }
	  }
	} else {
	  ret=userlist_match(objv[3],userlistp->type,userlistp->info);
	  if(ret==-1) return TCL_ERROR;
	  if(ret) {
	    morelistptr=Tcl_NewListObj(0,0);
	    tcl_list_append_string(morelistptr,userlistp->type);
	    tcl_list_append_string(morelistptr,userlistp->info);
	    if(Tcl_ListObjAppendElement(interp,listptr,morelistptr)==TCL_ERROR) return TCL_ERROR;
	  }
	}
      }
      Tcl_SetObjResult(interp,listptr);
      return TCL_OK;
    } else if (objc==5) {
      // userlist match nick!user@host flags
      Tcl_SetResult(interp,"Unimplemented!",TCL_STATIC);
      return TCL_ERROR;
    } else {
      invalidcommand=1;
    }
  } else {
    Tcl_SetResult(interp,"Unimplemented!",TCL_STATIC);
    return TCL_ERROR;
  }
  if(invalidcommand) {
    Tcl_SetResult(interp,"usage: userlist <command> [<args> ...] (Wrong number of arguments for given command.)",TCL_STATIC);
    return TCL_ERROR;
  }
  return TCL_OK;
}

/*
int TT_Proc___template__ialstats(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  char *hashstats;
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      hashstats=Tcl_HashStats(&windowp->server->ial);
    }
  }
  echo(hashstats,currentwindow,0);
  Tcl_Free(hashstats);
  return TCL_OK;
}

int TT_Proc___template__ialclear(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  sprintf(window.pathname,".status%s",argv[1]);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      hash_destroy(&windowp->server->ial);
      hash_create(&windowp->server->ial);
    }
  }
  return TCL_OK;
}
*/


syntax highlighted by Code2HTML, v. 0.9.1