#include <ctype.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/socket.h>

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

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


time_t lastcommand;


int TT_Proc_activebutton(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  strcpy(window.pathname,argv[1]);
  windowp=windows.find(window);
  if(windowp) TT_EvalF(TT_ARGS,
		       "QListBox::colorize .windowlist %d active",
		       windowp->index);
  return TCL_OK;
}

int TT_Proc_bgerror(TT_PROC_ARGS) {
  static int inbgerror;
  char *result;
  int wasinbgerror=inbgerror;
  int baderror=0;

  if(!inbgerror && TT_Int(TT_ARGS,"set ::internal::done_main_window")) {
    inbgerror=1;
    if(!(result=fparse("ERROR_INFO",-1,1,TT_Str(TT_ARGS,"set errorInfo")))) {
      baderror=1;
    }
    if(!baderror) {
      echo(result,".main",1);
      if(!(result=fparse("ERROR_CODE",-1,1,TT_Str(TT_ARGS,"set errorCode")))) {
	baderror=1;
      }
    }
    if(!baderror) {
      echo(result,".main",1);
      if(!(result=fparse("ERROR_ADDITIONAL",-1,1,argv[1]))) {
	baderror=1;
      }
    }
    if(!baderror) {
      echo(result,".main",1);
    }
    inbgerror=0;
  }
  if(wasinbgerror) {
    fprintf(stderr,"********************************************************************************\n");
    fprintf(stderr,M_ERROR_INFINITE_LOOP);
    fprintf(stderr,"********************************************************************************\n");
  }
  if(baderror) {
    fprintf(stderr,M_ERROR_NO_FORMAT);
  }
  if(baderror || wasinbgerror || !TT_Int(TT_ARGS,"set ::internal::done_main_window")) {
    fprintf(stderr,"%s:\n%s\n",M_ERROR_INFO,TT_Str(TT_ARGS,"set errorInfo"));
    fprintf(stderr,"%s: %s\n",M_ERROR_CODE,TT_Str(TT_ARGS,"set errorCode"));
    if(argc==2) {
      fprintf(stderr,"%s: %s\n",M_ERROR_ADDITIONAL,argv[1]);
    }
    exit(1);
  }
  return TCL_OK;
}

int TT_Proc_callproc(TT_PROC_ARGS) {
  // argv[1] is the proc name
  // argv[2] is the list of arguments to the proc
  // argv[3] is the server index
  if(argc!=4) {
    Tcl_SetResult(interp,"Usage: callproc <procname> <argument_list> <server_index>",TCL_VOLATILE);
    return TCL_ERROR;
  }
  TT_EvalF(TT_ARGS,"%s %s %s",argv[1],argv[3],argv[2]);
  return TCL_OK;
}

int TT_Proc_channel(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  strcpy(window.pathname,currentwindow);
  if((windowp=windows.find(window))) {
    if(!strncmp(windowp->pathname,".channel",8)) {
      Tcl_SetResult(interp,windowp->name,TCL_VOLATILE);
    }
  }
  return TCL_OK;
}

int TT_Proc_configtags(TT_PROC_ARGS) {
  configuretags(argv[1],argv[2],argv[3]);
  return TCL_OK;
}

int TT_Proc_connect(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  if(argc>2) {
    Tcl_SetResult(interp,"Usage: connect [<serverindex>]",TCL_VOLATILE);
    return TCL_ERROR;
  }
  if(argc==1) {
    strcpy(window.pathname,currentwindow);
  } else {
    sprintf(window.pathname,".status%s",argv[1]);
  }
  if((windowp=windows.find(window))&&windowp->server) {
    windowp->server->do_connect();
  }
  return TCL_OK;
}
  
int TT_Proc_closewindow(TT_PROC_ARGS) {
  tchan chan;
  tchan *chanp;
  tquery query;
  tserver *serverp;
  char temp[TEMPLEN];
  twindow window;
  twindow *windowp;
  int newindex;
  
  if(argc!=2) {
    Tcl_SetResult(interp,"Usage: closewindow <pathname>",TCL_STATIC);
    return TCL_ERROR;
  }
  if(strcmp(argv[1],".main")) {
    strcpy(window.pathname,argv[1]);
    if((windowp=windows.find(window))) {
      newindex=windowp->index-1;
      if(!strncmp(argv[1],".query",6)) {
	TT_EvalF(TT_ARGS,"destroy %s",argv[1]);
	strcpy(query.pathname,argv[1]);
	windowp->server->querylist.deleteitem(query);
	TT_EvalF(TT_ARGS,"QListBox::delete .windowlist %d",windowp->index);
	windows.deleteitem(window);
	TT_EvalF(TT_ARGS,"totop %d",newindex);
      }
      if(!strncmp(argv[1],".chat",5)) {
	windowp->dcc->disconnect();
	TT_EvalF(TT_ARGS,"destroy %s",argv[1]);
	TT_EvalF(TT_ARGS,"QListBox::delete .windowlist %d",windowp->index);
	delete windowp->dcc;
	windows.deleteitem(window);
	TT_EvalF(TT_ARGS,"totop %d",newindex);
      }
      if(!strncmp(argv[1],".files",6)) {
	if(windowp->server->dcclist.lastindex()==-1) {
	  //windowp->dcc->disconnect();
	  TT_EvalF(TT_ARGS,"destroy %s",argv[1]);
	  TT_EvalF(TT_ARGS,"QListBox::delete .windowlist %d",windowp->index);
	  //delete windowp->dcc;
	  windows.deleteitem(window);
	  TT_EvalF(TT_ARGS,"totop %d",newindex);
	}
      }
      if(!strncmp(argv[1],".channel",8)) {
	TT_EvalF(TT_ARGS,"destroy %s",argv[1]);
	strcpy(chan.pathname,argv[1]);
	if((chanp=windowp->server->chanlist.find(chan))) {           
	  if(chanp->ison) {
	    sprintf(temp,"PART %s",chanp->name);
	    // DEBUG
	    //TT_EvalF(TT_ARGS,".raw.text insert end \"Command (tcl.cc:173): %q\\n\"",temp);
	    windowp->server->senddata(temp);
	  }
	  windowp->server->chanlist.deleteitem(chan);
	}
	TT_EvalF(TT_ARGS,"QListBox::delete .windowlist %d",windowp->index);
	windows.deleteitem(window);
	TT_EvalF(TT_ARGS,"totop %d",newindex);
      }
      if(!strncmp(argv[1],".status",7)) {
	if(windowp->server->dcclist.lastindex()==-1) {
	  serverp=windowp->server;
	  if(serverp->connected) serverp->disconnect(1);
	  windows.init_trav();
	  while((windowp=windows.trav())) {
	    if(serverp==windowp->server) {
	      if(!strncmp(windowp->pathname,".chat",5)) {
		windowp->dcc->disconnect();
	      }
	      strcpy(window.pathname,windowp->pathname);
	      TT_EvalF(TT_ARGS,"destroy %s",windowp->pathname);
	      if((windowp=windows.find(window))) TT_EvalF(TT_ARGS,"QListBox::delete .windowlist %d",windowp->index);
	      windows.deleteitem(window);
	    }
	  }
	  delete serverp;
	  TT_EvalF(TT_ARGS,"totop %d",newindex);
	}
      }
    }
  } else {
    TT_EvalF(TT_ARGS,"\
      %s.text configure -state normal\n\
      %s.text delete 0.0 end\n\
      for { set n 0 } { $n < $::dynamic::blank_lines_before_text } { incr n } {\n\
	%s.text insert end \\n\n\
      }\n\
      %s.text configure -state disabled\n\
      %s.text yview moveto 1\
    ",argv[1],argv[1],argv[1],argv[1],argv[1]);
  }
  return TCL_OK;
}

int TT_Proc_currentindex(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  strcpy(window.pathname,currentwindow);
  Tcl_SetResult(interp,"-1",TCL_VOLATILE);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      Tcl_SetResult(interp,strnum(windowp->server->index),TCL_VOLATILE);
    }
  }
  return TCL_OK;
}

int TT_Proc_currentwindow(TT_PROC_ARGS) {
  Tcl_SetResult(interp,currentwindow,TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc_disconnect(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  if(argc>2) {
    Tcl_SetResult(interp,"Usage: disconnect [<serverindex>]",TCL_VOLATILE);
    return TCL_ERROR;
  }
  if(argc==1) {
    strcpy(window.pathname,currentwindow);
  } else {
    sprintf(window.pathname,".status%s",argv[1]);
  }
  if((windowp=windows.find(window))&&windowp->server) {
    windowp->server->disconnect(1);
  } else {
    Tcl_SetResult(interp,"Usage: disconnect [<serverindex>]",TCL_VOLATILE);
    return TCL_ERROR;
  }
  return TCL_OK;
}
  
int TT_Proc_echo(TT_PROC_OBJS) {
  int listc;
  char **listv;
  int n,i;
  if(objc>4||objc<2) {
    Tcl_SetResult(interp,"Usage: echo <text> [<pathname>] [<activate_windowlist>]",TCL_STATIC);
    return TCL_ERROR;
  }
  if((objc==4)&&((!strcmp(ARGV(2),"0"))||(!strcmp(ARGV(2),"1")))) {
    // echo pathname activate text
    echo_n(objv[3],ARGV(1),atoi(ARGV(2)));
  } else {
    if(objc==2) {
      // echo text
      if(strlen(currentwindow)) echo_n(objv[1],currentwindow,0);
      return TCL_OK;
    }
    if(objc==3) n=1; else n=atoi(ARGV(3));
    // echo text pathname [activate]
    if(Tcl_SplitList(interp,ARGV(2),&listc,(const char ***)&listv)==TCL_ERROR)
      return TCL_ERROR;
    for(i=0;i<listc;i++) {
      // The old server specific echo used to check if windows existed
      echo_n(objv[1],listv[i],n);
    }
    Tcl_Free((char *)listv);
  }
  return TCL_OK;
}

int TT_Proc_echotags(TT_PROC_OBJS) {
  int listc;
  char **listv;
  int n,i;
  char *tempstring;
  if(objc>4||objc<2) {
    Tcl_SetResult(interp,"Usage: echotags <text> [<pathname>] [<activate_windowlist>]",TCL_STATIC);
    return TCL_ERROR;
  }
  if(objc==2) {
    // echotags text
    if(strlen(currentwindow)) echotags(objv[1],currentwindow,0);
    return TCL_OK;
  }
  if(objc==3) n=1; else n=atoi(ARGV(3));

  // echotags text pathname [activate]
  tempstring = mystrdup(ARGV(2));
  if(Tcl_SplitList(interp,
		   tempstring,
		   &listc,
		   (const char ***)&listv)==TCL_ERROR) {
    free(tempstring);
    return TCL_ERROR;
  }
  free(tempstring);
  for(i=0;i<listc;i++) {
    echotags(objv[1],listv[i],n);
  }
  Tcl_Free((char *)listv);
  return TCL_OK;
}

int TT_Proc_escape(TT_PROC_OBJS) {
  int length;
  Tcl_Obj *objptr;
  char *dst;
  char *currentpiece;
  int flags;
  if(objc!=2) {
    Tcl_SetResult(interp,"usage: escape <data>",TCL_STATIC);
    return TCL_ERROR;
  }
  currentpiece=Tcl_GetStringFromObj(objv[1],&length);
  dst=(char *)malloc(Tcl_ScanCountedElementFixed(currentpiece,length,&flags));
  flags|=TCL_DONT_USE_BRACES;
  length=Tcl_ConvertCountedElement(currentpiece, length, dst, flags);
  objptr=Tcl_NewStringObj(dst,length);
  free(dst);
  Tcl_SetObjResult(interp,objptr);
  return TCL_OK;
}

int TT_Proc_exit(TT_PROC_ARGS) {
  //foreach server [servers] {
  //::template::quote $server \"QUIT :$::dynamic::default_quit\"
  //}
  TT_Eval(TT_ARGS,"\
    shutdown\n\
    die\n\
  ");
  return TCL_OK;
}

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

int TT_Proc_fgetformat(TT_PROC_ARGS) {
  if(argc<2) {
    Tcl_SetResult(interp,"Usage: fgetformat <type>",TCL_STATIC);
    return TCL_ERROR;
  }
  if(!fexists(argv[1])) {
    Tcl_SetResult(interp,"fdisplay: Invalid format type.",TCL_STATIC);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp,fgetformat(argv[1]),TCL_VOLATILE);
  return TCL_OK;
}

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

int TT_Proc_flocation(TT_PROC_ARGS) {
  char assemble[TEMPLEN];
  if(argc!=2 && argc!=3) {
    Tcl_SetResult(interp,"Usage: flocation <type> [argument]",TCL_STATIC);
    return TCL_ERROR;
  }
  if(!strcasecmp(argv[1],"main")) {
    Tcl_SetResult(interp,".main",TCL_STATIC);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"current")) {
    Tcl_SetResult(interp,"[currentwindow]",TCL_STATIC);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"status")) {
    Tcl_SetResult(interp,"[pathname status]",TCL_STATIC);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"currentorstatus")) {
    Tcl_SetResult(interp,"[if { [currentindex]==[index] } { set ::internal::junk [currentwindow] } else { set ::internal::junk [pathname status] }]",TCL_STATIC);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"serverall")) {
    Tcl_SetResult(interp,"[pathname all]",TCL_STATIC);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"files")) {
    Tcl_SetResult(interp,"[pathname files]",TCL_STATIC);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"all")) {
    Tcl_SetResult(interp,"[windows]",TCL_STATIC);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"none")) {
    Tcl_SetResult(interp,"",TCL_STATIC);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"onchannel") && argc==3) {
    sprintf(assemble,"onchannel %s",argv[2]);
    Tcl_SetResult(interp,assemble,TCL_VOLATILE);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"channelorstatus") && argc==3) {
    sprintf(assemble,"channelorstatus %s",argv[2]);
    Tcl_SetResult(interp,assemble,TCL_VOLATILE);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"pathname") && argc==3) {
    sprintf(assemble,"pathname %s",argv[2]);
    Tcl_SetResult(interp,assemble,TCL_VOLATILE);
    return TCL_OK;
  }
  if(!strcasecmp(argv[1],"chat") && argc==3) {
    sprintf(assemble,"chat %s",argv[2]);
    Tcl_SetResult(interp,assemble,TCL_VOLATILE);
    return TCL_OK;
  }
  Tcl_SetResult(interp,"flocation: Invalid type or too few arguments.",TCL_STATIC);
  return TCL_ERROR;
}

int TT_Proc_fparse(TT_PROC_ARGS) {
  // fparse <type> [arguments]
  // 0      1      2+
  // argc 1 2      3
  char *result;
  if(argc<2) {
    Tcl_SetResult(interp,"Usage: fparse <type> [arguments]",TCL_STATIC);
    return TCL_ERROR;
  }
  if(!fexists(argv[1])) {
    Tcl_SetResult(interp,"fparse: Invalid format type.",TCL_STATIC);
    return TCL_ERROR;
  }
  if(!(result=fparsev(argv[1],-1,argc-2,&argv[2]))) {
    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_fset(TT_PROC_ARGS) {
  if(argc<3 || argc>5) {
    Tcl_SetResult(interp,"Usage: fset <type> <format> [location [activate]]",TCL_STATIC);
    return TCL_ERROR;
  }
  hash_set(&formats,argv[1],argv[2]);
  if(argc<5) hash_set(&activate,argv[1],"1"); else hash_set(&activate,argv[1],argv[4]);
  if(argc>3) {
    hash_set(&locations,argv[1],argv[3]);
  } else {
    hash_set(&locations,argv[1],".main");
  }
  return TCL_OK;
}

int TT_Proc_gettags(TT_PROC_OBJS) {
  Tcl_Obj *result;
  char *pathname;
  if(objc>3||objc<2) {
    Tcl_SetResult(interp,"Usage: gettags <text> [<pathname>]",TCL_STATIC);
    return TCL_ERROR;
  }
  if(objc==2) {
    // gettags text
    pathname=currentwindow;
  } else {
    // gettags text pathname
    pathname=ARGV(2);
  }
  if(!strlen(pathname)) {
    Tcl_SetResult(interp,"Usage: gettags <text> [<pathname>]  (Pathname cannot be an empty string.)",TCL_STATIC);
    return TCL_ERROR;
  }
  result=gettags(objv[1],pathname);
  Tcl_SetObjResult(interp,result);
  Tcl_DecrRefCount(result);
  return TCL_OK;
}

int TT_Proc_hist(TT_PROC_ARGS) {
  tcommand *commandp;
  char assemble[TEMPLEN];
  history.init_trav();
  while((commandp=history.trav())) {
    sprintf(assemble," \0030,14 HISTORY \003 %d:  %s",commandp->index,commandp->line);
    echo(assemble,currentwindow,1);
  }
  return TCL_OK;
}

int TT_Proc_histdown(TT_PROC_ARGS) {
  tcommand command;
  tcommand *commandp;
  if(historyposition!=-1) {
    if(historyposition==0) {
      TT_EvalF(TT_ARGS,"%s.entry delete 0 end",currentwindow);
      TT_EvalF(TT_ARGS,"%s.entry insert 0 %q",currentwindow,historycommand);
      if(historycommand) free(historycommand);
      historycommand=0;
      historyposition--;
    } else {
      historyposition--;
      TT_EvalF(TT_ARGS,"%s.entry delete 0 end",currentwindow);
      command.index=historyposition;
      commandp=history.find(command);
      TT_EvalF(TT_ARGS,"%s.entry insert 0 %q",currentwindow,commandp->line);
    }
  }
  return TCL_OK;
}

int TT_Proc_histup(TT_PROC_ARGS) {
  tcommand command;
  tcommand *commandp;
  if((historyposition!=99)&&(historyposition<history.lastindex())) {
    if(historyposition==-1) {
      historycommand=strdup(TT_StrF(TT_ARGS,"%s.entry get",currentwindow));
    }
    historyposition++;
    TT_EvalF(TT_ARGS,"%s.entry delete 0 end",currentwindow);
    command.index=historyposition;
    commandp=history.find(command);
    TT_EvalF(TT_ARGS,"%s.entry insert 0 %q",currentwindow,commandp->line);
  }
  return TCL_OK;
}

int TT_Proc_idle(TT_PROC_ARGS) {
  if(!lastcommand) {
    Tcl_SetResult(interp,"0",TCL_STATIC);
  } else {
    Tcl_SetResult(interp,strnum(time(0)-lastcommand),TCL_VOLATILE);
  }
  return TCL_OK;
}

int TT_Proc_index(TT_PROC_ARGS) {
  if(argc!=1) {
    Tcl_SetResult(interp,"Usage: index",TCL_VOLATILE);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp,"-1",TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc_ischannel(TT_PROC_ARGS) {
  if(argc>=2) {
    if(ischannel(argv[1]))
      Tcl_SetResult(interp,"1",TCL_VOLATILE);
    else
      Tcl_SetResult(interp,"0",TCL_VOLATILE);
  }
  return TCL_OK;
}

int TT_Proc_memory(TT_PROC_ARGS) {
  twindow *windowp;
  int count=0;
  windows.init_trav();
  while((windowp=windows.trav())) {
    count+=TT_IntF(TT_ARGS,"\
      set all [%s.text dump -text 0.0 end]\n\
      set ::internal::count 0\n\
      for { set n 0 } { $n < [llength $all] } { incr n } {\n\
        if { [lindex $all $n]==\"text\" } {\n\
          incr ::internal::count [string length [lindex $all [expr $n+1]]]\n\
        }\n\
      }\n\
      echo \"Characters in %s: $::internal::count\"\n\
      set ::internal::count\n\
    ",windowp->pathname,windowp->pathname);
  }    
  count+=TT_Int(TT_ARGS,"\
    set all [.raw.text dump -text 0.0 end]\n\
    set ::internal::count 0\n\
    for { set n 0 } { $n < [llength $all] } { incr n } {\n\
      if { [lindex $all $n]==\"text\" } {\n\
        incr ::internal::count [string length [lindex $all [expr $n+1]]]\n\
      }\n\
    }\n\
    echo \"Characters in .raw: $::internal::count\"\n\
    set ::internal::count\n\
  ");
  TT_EvalF(TT_ARGS,"echo \"Total Characters: %d (*3=%d)\"",count,count*3);
  TT_Eval(TT_ARGS,"echo \"Memory usage: [lindex [exec cat /proc/[pid]/stat] 22]\"");
  TT_EvalF(TT_ARGS,"echo \"Memory Usage minus Total Predicted: [expr [lindex [exec cat /proc/[pid]/stat] 22]-%d]\"",count*3);
  /*
  struct rusage usage;
  if(getrusage(RUSAGE_SELF,&usage)==-1) {
    perror("getrusage()");
    exit(1);
  }
  printf("user time used: %ld and %ld\n",usage.ru_utime.tv_sec,usage.ru_utime.tv_usec);
  printf("system time used: %ld and %ld\n",usage.ru_stime.tv_sec,usage.ru_stime.tv_usec);
  printf("maximum resident set size: %ld\n",usage.ru_maxrss);
  printf("integral shared memory size: %ld\n",usage.ru_ixrss);
  printf("integral unshared data size: %ld\n",usage.ru_idrss);
  printf("integral unshared stack size: %ld\n",usage.ru_isrss);
  printf("page reclaims: %ld\n",usage.ru_minflt);
  printf("page faults: %ld\n",usage.ru_majflt);
  printf("swaps: %ld\n",usage.ru_nswap);
  printf("block input operations: %ld\n",usage.ru_inblock);
  printf("block output operations: %ld\n",usage.ru_oublock);
  printf("messages sent: %ld\n",usage.ru_msgsnd);
  printf("messages received: %ld\n",usage.ru_msgrcv);
  printf("signals received: %ld\n",usage.ru_nsignals);
  printf("voluntary context switches: %ld\n",usage.ru_nvcsw);
  printf("involuntary context switches: %ld\n",usage.ru_nivcsw);
  */
  //struct timeval ru_utime; /* user time used */
  //struct timeval ru_stime; /* system time used */
  //long ru_maxrss;          /* maximum resident set size */
  //long ru_ixrss;      /* integral shared memory size */
  //long ru_idrss;      /* integral unshared data size */
  //long ru_isrss;      /* integral unshared stack size */
  //long ru_minflt;          /* page reclaims */
  //long ru_majflt;          /* page faults */
  //long ru_nswap;      /* swaps */
  //long ru_inblock;         /* block input operations */
  //long ru_oublock;         /* block output operations */
  //long ru_msgsnd;          /* messages sent */
  //long ru_msgrcv;          /* messages received */
  //long ru_nsignals;        /* signals received */
  //long ru_nvcsw;      /* voluntary context switches */
  //long ru_nivcsw;          /* involuntary context switches */
  return TCL_OK;
}

int TT_Proc_microtime(TT_PROC_ARGS) {
  struct timeval tv;
  struct timezone tz;
  char temptime[100];
  tz.tz_minuteswest=0;
  tz.tz_dsttime=0;
  if(gettimeofday(&tv,&tz)) {
    Tcl_SetResult(interp,"Error occured during call to gettimeofday()",TCL_VOLATILE);
    return TCL_ERROR;
  }
  sprintf(temptime,"%.6f",tv.tv_sec+tv.tv_usec/1000000.0);
  Tcl_SetResult(interp,temptime,TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc_newserver(TT_PROC_ARGS) {
  tserver *serverp;
  char *check;
  char *colon;
  char *copy;
  if(argc==1) {
    serverp=new tserver("",0,"");
  } else {
    if(!strlen(argv[1])) {
      serverp=new tserver("",0,"");
    } else {
      switch(argc) {
      case 2:
	if(!(copy=strdup(argv[1]))) {
	  fprintf(stderr,M_OUT_OF_MEMORY);
	}
	if(!(colon=strstr(copy,":"))) {
	  // server
	  serverp=new tserver(copy,0,"");
	} else {
	  *colon=0;
	  colon++;
	  if(!(check=strstr(colon,":"))) {
	    // server:port
	    serverp=new tserver(copy,atoi(colon),"");
	  } else {
	    // server::password
	    // server:port:password
	    *check=0;
	    serverp=new tserver(copy,atoi(colon),check+1);
	  }
	}
	free(copy);
	break;
      case 3:
	// server port
	serverp=new tserver(argv[1],atoi(argv[2]),"");
	break;
      case 4:
	// server port password
	serverp=new tserver(argv[1],atoi(argv[2]),argv[3]);
	break;
      default:
	Tcl_SetResult(interp,"usage: newserver [<server>[:[<port>][:<password>]]]",TCL_STATIC);
	return TCL_ERROR;
      }
    }
  }
  Tcl_SetResult(interp,strnum(serverp->index),TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc_nextwindow(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  
  strcpy(window.pathname,currentwindow);
  if((windowp=windows.find(window))) {
    if(windowp->index<windows.lastindex()) {
      TT_EvalF(TT_ARGS,"totop %d",windowp->index+1);
    } else {
      TT_Eval(TT_ARGS,"totop 0");
    }
  }
  return TCL_OK;
}

int TT_Proc_parse(TT_PROC_ARGS) {
  if(argc<3) {
    Tcl_SetResult(interp,"usage: parse <command> <argument> [<argument> ...]",TCL_STATIC);
    return TCL_ERROR;
  }
  if(!strcasecmp(argv[1],"format")) {
    //parse format TYPE "format string"
    if(argc!=4) {
      Tcl_SetResult(interp,"usage: parse format <type> <format_string>",TCL_STATIC);
      return TCL_ERROR;
    }
  } else if(!strcasecmp(argv[1],"args")) {
    //parse args TYPE "arg types"
    if(argc!=4) {
      Tcl_SetResult(interp,"usage: parse args <type> <input_arguments>",TCL_STATIC);
      return TCL_ERROR;
    }
  } else if(!strcasecmp(argv[1],"location")) {
    //parse location TYPE "location string"
    //[parse location TYPE]
    if(argc>4) {
      Tcl_SetResult(interp,"usage: parse location <type> [<location_string>]",TCL_STATIC);
      return TCL_ERROR;
    }
  } else {
    //[parse TYPE args ...]
  }
  return TCL_OK;  
}

char *mystrtok(char *s, char *delim) {
  static char *nextposition;
  char *position;
  char *oldposition;
  // DEBUG
  //TT_EvalF(TT_ARGS,".raw.text insert end \"DEBUG - s is *%d*\\n\"",(int)s);
  if(s) nextposition=s;
  // DEBUG
  //TT_EvalF(TT_ARGS,".raw.text insert end \"DEBUG - nextposition  is *%d*\\n\"",(int)nextposition);
  if(!nextposition) return NULL;
  // DEBUG
  //TT_EvalF(TT_ARGS,".raw.text insert end \"DEBUG - nextposition is non-null and is *%q*\\n\"",nextposition);
  if((position=strpbrk(nextposition,delim))) { 
    // DEBUG
    //TT_EvalF(TT_ARGS,".raw.text insert end \"DEBUG - Found a *%q*\\n\"",delim);
    *position=0;
    position++;
  }
  oldposition=nextposition;
  // DEBUG
  //TT_EvalF(TT_ARGS,".raw.text insert end \"DEBUG - Returning *%q*\\n\"",oldposition);
  nextposition=position;
  // DEBUG
  //TT_EvalF(TT_ARGS,".raw.text insert end \"DEBUG - To be checked next time is *%q*\\n\"",nextposition);
  return oldposition;
}

int TT_Proc_parseentry(TT_PROC_ARGS) {
  // Split up long lines!!!!
  // argv[1] is the origin window
  tchan chan;
  tchan *chanp;
  tquery query;
  tquery *queryp;
  char temp[TEMPLEN];
  char *theentry;
  char *returned;
  char *position;
  twindow window;
  twindow *windowp;
  tcommand command;

  if(argc!=2) {
    Tcl_SetResult(interp,"Usage: parseentry <pathname>",TCL_STATIC);
    return TCL_ERROR;
  }
  strcpy(window.pathname,argv[1]);
  if((windowp=windows.find(window))) {
    theentry=strdup(TT_StrF(TT_ARGS,"%s.entry get",argv[1]));
    if(!theentry) {
      fprintf(stderr,M_OUT_OF_MEMORY);
      exit(1);
    }
    TT_EvalF(TT_ARGS,"%s.entry delete 0 end",argv[1]);
    if(strlen(TT_StrF(TT_ARGS,"info command ::event_enter"))) {
      returned=strdup(TT_StrF(TT_ARGS,"::event_enter %q %s",theentry,argv[1]));
      free(theentry);
      if(!returned) {
	fprintf(stderr,M_OUT_OF_MEMORY);
	exit(1);
      }
      theentry=returned;
    }
    if(strlen(theentry)) {
      if(historycommand) free(historycommand);
      historycommand=0;
      historyposition=-1;
      command.line=strdup(theentry);
      history.insert_start(command);
      if(history.lastindex()==COMMANDHISTORYLEN) {
        command.index=history.lastindex();
	history.deleteitem(command);
      }
      position=mystrtok(theentry,"\n");
      while(position) {
	if(!strlen(position)) {
	  position=mystrtok(0,"\n");
	  continue;
	}
	if(position[0]=='/') {
	  do_command(argv[1],position);
	} else {
	  if(!strncmp(argv[1],".channel",8)) {
	    strcpy(chan.pathname,argv[1]);
	    if((chanp=windowp->server->chanlist.find(chan))) {
	      sprintf(temp,"PRIVMSG %s :%s",chanp->name,position);
	      windowp->server->senddata(temp);
	    }
	  }
	  if(!strncmp(argv[1],".query",6)) {
	    strcpy(query.pathname,argv[1]);
	    if((queryp=windowp->server->querylist.find(query))) {
	      sprintf(temp,"PRIVMSG %s :%s",queryp->getname(),position);
	      windowp->server->senddata(temp);
	    }
	  }
	  if(!strncmp(argv[1],".chat",5)) {
	    twindow window2;
	    twindow *windowp2;
	    strcpy(window2.pathname,argv[1]);
	    if((windowp2=windows.find(window2))) {
	      if(windowp2->dcc) {
		windowp2->dcc->senddata(position);
	      }
	    }
	  }
	  if(strncmp(argv[1],".status",7)&&strcmp(argv[1],".main")) {
	    fdisplay("OUTGOING_TEXT",windowp->server->index,2,argv[1],position);
	  }
	}
	position=mystrtok(0,"\n");
      }
    }
    free(theentry);
  }
  lastcommand=time(0);
  return TCL_OK;  
}

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

  if(argc<4||argc==5||argc>7) {
    Tcl_SetResult(interp,"Usage: popup <command> [2 to 5 arguments] (Global)",TCL_VOLATILE);
    return TCL_ERROR;
  }

  if(!strcasecmp(argv[1],"add")&&!strcasecmp(argv[2],"item")) {
    if(!strncmp(argv[3],"main",4)) {
      sprintf(menuname,".menu%s%s",argv[3],argv[4]);
    } else {
      return TCL_OK;
    }
    TT_EvalF(TT_ARGS,"%s add command -label \"%q\" -command %q",menuname,argv[5],argv[6]);
  }

  if(!strcasecmp(argv[1],"add")&&!strcasecmp(argv[2],"submenu")) {
    if(!strncmp(argv[3],"main",4)) {
      sprintf(menuname,".menu%s%s",argv[3],argv[4]);
    } else {
      return TCL_OK;
    }
    position=strrchr(menuname,'.');
    *position=0;
    strcpy(basemenuname,menuname);
    *position='.';
    TT_EvalF(TT_ARGS,"%s add cascade -label \"%q\" -menu \"%q\"",basemenuname,argv[5],menuname);
    TT_EvalF(TT_ARGS,"menu %s",menuname);
  }

  if(!strcasecmp(argv[1],"clear")) {
    if(!strncmp(argv[2],"main",4)) {
      sprintf(menuname,".menu%s%s",argv[2],argv[3]);
    } else {
      return TCL_OK;
    }
    TT_EvalF(TT_ARGS,"destroy %s",menuname);
    TT_EvalF(TT_ARGS,"menu %s",menuname);
  }

  return TCL_OK;
}

int TT_Proc_previouswindow(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  
  strcpy(window.pathname,currentwindow);
  if((windowp=windows.find(window))) {
    if(windowp->index>0) { 
      TT_EvalF(TT_ARGS,"totop %d",windowp->index-1);
    } else {
      TT_EvalF(TT_ARGS,"totop %d",windows.lastindex());
    }
  }
  return TCL_OK;
}

int TT_Proc_renamewindow(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  int oldindex;
  int newindex;
  int reallynewindex;

  if(argc!=3) {
    Tcl_SetResult(interp,"Usage: renamewindow <pathname> <new name>",TCL_STATIC);
    return TCL_ERROR;
  }
  strcpy(window.pathname,currentwindow);
  if(!(windowp=windows.find(window))) {
    fprintf(stderr,"Evil Error: The curent window isn't in the window list.  This should never happen.\n");
    exit(1);
  }
  oldindex=windowp->index;
  strcpy(window.pathname,argv[1]);
  if(!(windowp=windows.find(window))) {
    Tcl_SetResult(interp,"Usage: renamewindow <pathname> <new name> (Invalid pathname specified.)",TCL_STATIC);
    return TCL_ERROR;
  }
  newindex=windowp->index;

  strcpy(window.pathname,windowp->pathname);
  window.title=windowp->title;
  window.server=windowp->server;
  window.dcc=windowp->dcc;

  windows.deleteitem(window);

  strcpy(window.name,argv[2]);

  reallynewindex=windows.insert_ascending(window);

  TT_EvalF(TT_ARGS,"QListBox::delete .windowlist %d",newindex);

  if(strncmp(".status",argv[1],7)&&strcmp(".main",argv[1])) {
    TT_EvalF(TT_ARGS,"QListBox::insert .windowlist %d \"${::dynamic::theme_windowlist_indent}%q\"",reallynewindex,argv[2]);
  } else {
    TT_EvalF(TT_ARGS,"QListBox::insert .windowlist %d \"%q\"",reallynewindex,argv[2]);
  }

  if(oldindex==newindex) {
    TT_EvalF(TT_ARGS,"QListBox::select .windowlist %d",reallynewindex);
  } else {
    // Select old selected window again.
    strcpy(window.pathname,currentwindow);
    if(!(windowp=windows.find(window))) {
      fprintf(stderr,"Evil Error: The curent window isn't in the window list.  This should never happen.\n");
      exit(1);
    }
    TT_EvalF(TT_ARGS,"QListBox::select .windowlist %d",windowp->index);
  }
  Tcl_SetResult(interp,strnum(reallynewindex),TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc_say(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  tchan chan;
  tchan *chanp;
  tquery query;
  tquery *queryp;
  char argument[TEMPLEN];
  char temp[TEMPLEN];
  int ok=0;
  char *position;
  if(argc>3||argc<2) {
    Tcl_SetResult(interp,"Usage: say <message> [<target pathname>]",TCL_VOLATILE);
    return TCL_ERROR;
  }
  if(argc==3) {
    strcpy(window.pathname,argv[2]);
  } else {
    strcpy(window.pathname,currentwindow);
  }
  if(!(windowp=windows.find(window))) {
    Tcl_SetResult(interp,"say: Invalid target pathname.",TCL_VOLATILE);
    return TCL_ERROR;
  }
  strcpy(argument,argv[1]);
  position=strtok(argument,"\n");
  if(!strncmp(window.pathname,".channel",8)) {
    strcpy(chan.pathname,window.pathname);
    chanp=windowp->server->chanlist.find(chan);
    while(position) {
      sprintf(temp,"PRIVMSG %s :%s",chanp->name,position);
      windowp->server->senddata(temp);
      fdisplay("OUTGOING_TEXT",windowp->server->index,2,window.pathname,position);
      position=strtok(NULL,"\n");
    }
    ok=1;
  }
  if(!strncmp(window.pathname,".query",6)) {
    strcpy(query.pathname,window.pathname);
    queryp=windowp->server->querylist.find(query);
    while(position) {
      sprintf(temp,"PRIVMSG %s :%s",queryp->getname(),position);
      windowp->server->senddata(temp);
      fdisplay("OUTGOING_TEXT",windowp->server->index,2,window.pathname,position);
      position=strtok(NULL,"\n");
    }
    ok=1;
  }
  if(!strncmp(window.pathname,".chat",5)) {
    while(position) {
      windowp->dcc->senddata(position);
      fdisplay("OUTGOING_TEXT",windowp->server->index,2,window.pathname,position);
      position=strtok(NULL,"\n");
    }
    ok=1;
  }
  if(ok) return TCL_OK; else {
    Tcl_SetResult(interp,"say: Improper say usage.  Please use only for query, channel, or chat windows.",TCL_VOLATILE);
    return TCL_ERROR;
  }
}

int TT_Proc_selectednicks(TT_PROC_ARGS) {
  TT_Eval(TT_ARGS,"\
    set tempvariable \"\"\n\
    if { [string match \".channel*\" [currentwindow]] } {\n\
      foreach index [[currentwindow].nicks curselection] {\n\
        lappend tempvariable [string trim [join [[currentwindow].nicks get $index $index]] \"+@\"]\n\
      }\n\
    }\n\
  ");
  Tcl_SetResult(interp,
		(char *)TT_Str(TT_ARGS,"set tempvariable"),
		TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc_selectedmodenicks(TT_PROC_ARGS) {
  TT_Eval(TT_ARGS,"\
    set tempvariable \"\"\n\
    if { [string match \".channel*\" [currentwindow]] } {\n\
      foreach index [[currentwindow].nicks curselection] {\n\
        lappend tempvariable [join [[currentwindow].nicks get $index $index]]\b\
      }\n\
    }\n\
  ");
  Tcl_SetResult(interp,
		(char *)TT_Str(TT_ARGS,"set tempvariable"),
		TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc_serverindex(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  if(argc!=2) {
    Tcl_SetResult(interp,"Usage: serverindex <pathname>",TCL_STATIC);
    return TCL_ERROR;
  }
  strcpy(window.pathname,argv[1]);
  Tcl_SetResult(interp,"-1",TCL_VOLATILE);
  if((windowp=windows.find(window))) {
    if(windowp->server) {
      Tcl_SetResult(interp,strnum(windowp->server->index),TCL_VOLATILE);
    }
  }
  return TCL_OK;
}

int TT_Proc_servers(TT_PROC_ARGS) {
  twindow *windowp;
  string servers;
  windows.init_trav();
  while((windowp=windows.trav())) {
    if(!strncmp(windowp->pathname,".status",7)) {
      if(servers.length()) servers+=" ";
      servers+=strnum(windowp->server->index);
    }
  }
  Tcl_SetResult(interp,(char *)servers.c_str(),TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc_shutdown(TT_PROC_ARGS) {
  twindow *windowp;
  char temp[1000];

  if(fparse_result) free(fparse_result);

  // Clean up format/location hashes
  hash_destroy(&formats);
  hash_destroy(&activate);
  hash_destroy(&locations);

  TT_Eval(TT_ARGS,"set ::dynamic::do_rawview 0");
  windows.init_trav();
  while((windowp=windows.trav())) {
    if(!strncmp(windowp->pathname,".status",7)) {
      if(windowp->server) {
	if(windowp->server->connected) {
	  //printf("We are here closing sockets.\n");
	  /*
	  struct linger lin;
	  socklen_t linlen=sizeof(lin);
	  getsockopt(windowp->server->sockfd,SOL_SOCKET,SO_LINGER,&lin,&linlen);
	  printf("%d and %d\n",lin.l_onoff,lin.l_linger);
	  */
	  TT_EvalF(TT_ARGS,"set ::%d::intentional_disconnect 1",windowp->server->index);
	  sprintf(temp,"QUIT :%s",TT_Str(TT_ARGS,"set ::dynamic::default_quit"));
	  windowp->server->senddata(temp);
	  /*
	  printf("Pausing for 5 seconds.\n");
	  sleep(10);
	  printf("Done!\n");
	  printf("Shutdown returned: %d\n",shutdown(windowp->server->sockfd,2));
	  printf("Close returned %d\n",close(windowp->server->sockfd));
	  */
	  shutdown(windowp->server->sockfd,1);
	  close(windowp->server->sockfd);
	}
	if(windowp->server->connecting) close(windowp->server->sockfd);
	delete windowp->server;
      }
    }
  }
  return TCL_OK;
}

int TT_Proc_title(TT_PROC_OBJS) {
  int len;
  char *arg;
  twindow window;
  twindow *windowp;
  Tcl_Obj *objp;

  if(objc>3||objc<2) {
    Tcl_SetResult(interp,"Usage: title <pathname> [<text>]",TCL_VOLATILE);
    return TCL_ERROR;
  }
  strcpy(window.pathname,ARGV(1));
  if((windowp=windows.find(window))) {
    if(objc==2) {
      objp=Tcl_NewStringObj((char *)windowp->title.data(),windowp->title.size());
      Tcl_SetObjResult(interp,objp);
    } else {
      arg=Tcl_GetStringFromObj(objv[2],&len);
      windowp->title.assign(arg,len);
      if(!strcmp(windowp->pathname,currentwindow)) {
	TT_EvalF(TT_ARGS,"wm title . %q",arg);
      }
    }
  } else {
    Tcl_SetResult(interp,"Usage: title <pathname> [<text>] (Invalid pathname specified.)",TCL_STATIC);
    return TCL_ERROR;
  }
  return TCL_OK;
}

int TT_Proc_totop(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  twindow *windowp2;
  string userhostresults;
  int n;
  int i,i2;
  n=atoi(argv[1]);
  window.index=n;
  windowp=windows.find(window);
  if(windowp) {
    i=-1;
    if(windowp->server) {
      i=windowp->server->index;
    }
    window.index=-1;
    strcpy(window.pathname,currentwindow);
    if((windowp2=windows.find(window))) {
      i2=-1;
      if(windowp2->server) {
	i2=windowp2->server->index;
      }
      TT_EvalF(TT_ARGS," if { [info commands ::event_windowchanged]!=\"\" } { event_windowchanged %q %q %d %d %q %q %d %d }",windowp2->pathname,windowp2->name,i2,windowp2->index,windowp->pathname,windowp->name,i,windowp->index);
    }
    TT_EvalF(TT_ARGS,"wm title . %q",windowp->title.c_str());
  }
  if(windowp) {
    TT_EvalF(TT_ARGS,"raise %s",windowp->pathname);
    TT_EvalF(TT_ARGS,"QListBox::colorize .windowlist %d normal", n);
    TT_EvalF(TT_ARGS,"QListBox::select .windowlist %d",n);
    TT_EvalF(TT_ARGS,"%s.text see end",windowp->pathname);
    TT_EvalF(TT_ARGS,"focus %s.entry",windowp->pathname);
    strcpy(currentwindow,windowp->pathname);
    if(windowp->server) {
      TT_EvalF(TT_ARGS,"::template::condis %d",windowp->server->index);
    } else {
      TT_Eval(TT_ARGS,"::template::condis -1");
    }
    TT_Eval(TT_ARGS,"update idletasks");
    return TCL_OK;
  } else {
    return TCL_ERROR;
  }
}

int TT_Proc_version(TT_PROC_ARGS) {
  Tcl_SetResult(interp,VERSION,TCL_VOLATILE);
  return TCL_OK;
}

int TT_Proc_windowindex(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  if(argc>2) {
    Tcl_SetResult(interp,"Usage: windowindex [<pathname>]",TCL_STATIC);
    return TCL_ERROR;
  }
  if(argc==1) {
    strcpy(window.pathname,currentwindow);
  } else {
    strcpy(window.pathname,argv[1]);
  }
  Tcl_SetResult(interp,"-1",TCL_STATIC);
  if((windowp=windows.find(window))) {
    Tcl_SetResult(interp,strnum(windowp->index),TCL_VOLATILE);
  } else {
    Tcl_SetResult(interp,"Usage: windowindex [<pathname>] (Invalid pathname given.",TCL_STATIC);
    return TCL_ERROR;
  }
  return TCL_OK;
}

int TT_Proc_windowname(TT_PROC_ARGS) {
  twindow window;
  twindow *windowp;
  
  if(argc>1) {
    strcpy(window.pathname,argv[1]);
  } else {
    strcpy(window.pathname,currentwindow);
  }
  if((windowp=windows.find(window))) {
    Tcl_SetResult(interp,windowp->name,TCL_VOLATILE);
  }
  return TCL_OK;
}

int TT_Proc_windows(TT_PROC_ARGS) {
  Tcl_Obj *listptr;
  Tcl_Obj *objptr;
  twindow *windowp;
  if(argc!=1) {
    Tcl_SetResult(interp,"Usage: windows",TCL_STATIC);
    return TCL_ERROR;
  }
  listptr=Tcl_NewListObj(0,0);
  windows.init_trav();
  while((windowp=windows.trav())) {
    objptr=Tcl_NewStringObj(windowp->pathname,strlen(windowp->pathname));
    if(Tcl_ListObjAppendElement(interp,listptr,objptr)==TCL_ERROR) return TCL_ERROR;
  }
  Tcl_SetObjResult(interp,listptr);
  return TCL_OK;
}

int TT_Proc_windowtype(TT_PROC_ARGS) {
  int x=1;
  char temppath[20];
  if(argc!=2) {
    Tcl_SetResult(interp,"Usage: windowtype <pathname>",TCL_STATIC);
    return TCL_ERROR;
  }
  strcpy(temppath,argv[1]);
  while(isalpha(temppath[x])) x++;
  temppath[x]=0;
  Tcl_SetResult(interp,temppath+1,TCL_VOLATILE);
  return TCL_OK;
}


syntax highlighted by Code2HTML, v. 0.9.1