/* cmd.c	-stuff dealing with client commands.
		more in cmd_defs.c

    Author: Tano Fotang, 1997

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License , or
    (at your option) any later version.

    see the file sula/COPYING for details.

*/




#include <forms.h>
#include "config.h"
#include "nicks.h"
#include "aux.h"
#include "sula.h"
#include "chanwin.h"
#include "global.h"
#include "script_gui.h"
#include "ignore.h"
#include "setting.h"
#include "dcc.h"
#include "cmd.h"
#include "cmd_def.h"
#include "help.h"
#include "parser.h"
#include "script.h"
extern FD_scriptcontrol *script_form;
static int argc;
static CmdStruct *cs;	/* routines calling *process-cmd() must declare this again!*/
static int want_help(const char *cmdstr, const char *opt, Winstruct *win){
/* cmd -help */
	if(opt && !strncasecmp(opt,"-help",2)){
		show_std_help(uppercase(nextstr(cmdstr,1)),
		NULL, win->chanwin?win->chanwin:NULL);
		return 1;
	}
	return 0;
}


static void history_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	history(argc, argv, cs->win);
	return;
}
static void ignore_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	ignore(argc, argv,cs->win);
	return; 

}

static void join_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	join(argc, argv, cs->win);
}
static void invite_cmd(void *data){
	char *argv[MAXARGC];
	
	CmdStruct *cs=(CmdStruct *)data;

	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv,MAXARGC);
	invite(argc, argv, cs->win);
}
static void part_cmd(void *data){
	
	char *argv[5];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv,5);
	part(argc, argv, cs->win);
}

static void topic_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	topic(argc, argv, cs->win);
}
static void quit_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	
	quit(argc, argv, cs->win);
}
static void window_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	window(argc, argv, cs->win);
}

static void empty_cmd(void *data){
char *cmd=NULL;
cs=(CmdStruct *)data;
if(cs->win && (cmd=nextstr(cs->args,2)))
      (void)want_help(cs->args,cmd , cs->win);
}

static void clear_in_hook(On **p,On **end, int ser_nr,char * pattern){
/* remove one on-hook entry from a hook list
top of list: *p
bottom of list: *end
*/
	if( (*p)->type&CMD_IN && ((*p)->ser_number==ser_nr || ser_nr<0) && 
	      (!pattern || !strcasecmp((*p)->pattern, pattern))){
		On *q=(*p)->next;
		free((*p)->pattern);
		free((*p)->cmd.cmd);
		if(q==*end) 
			*end=*p;
		else **p=*q;
	       	free(q);
	}else *p=(*p)->next;
}

static void remove_hook_bytype(MsgNr j, int ser_nr,char * pattern){
/* remove on-hooks for a non-numeric msg type if event matches <pattern*/
	On *q=want[j].start;
	while(q!=want[j].end)
		clear_in_hook(&q,&(want[j].end), ser_nr, pattern);
}

static void remove_numhook_bynum(int num, int ser_nr,char * pattern,NumHook *h){
/* remove on-hooks for a numeric if event matches <pattern>*/
	if(h){
		On *p;
		remove_numhook_bynum(num,ser_nr,pattern,h->left);
		p=h->start;
		while(p!=h->end)
			clear_in_hook(&p,&(h->end), ser_nr, pattern);
		remove_numhook_bynum(num,ser_nr,pattern,h->right);
	}
}
static int remove_hook(MsgNr type,char *str,int num ){
/*  
remove one or many events for a message type or for a numeric(if
numeric!=0)

#privmsg 300 "foo bar base.." 
quotes not required if either a ser_no is given, or '#' is not used.
1)
-del #type [ser_no] [pattern]
	ser_no not given => remove all serial nos (ser_no=-1)
2)
-del type [pattern]
	=> ser_no=0
pattern not given => all patterns

*/
	char *pattern=NULL;
	int ser_nr=-1;
	char *s;
	
	if(!str || !(*str)) return -1;
	pattern=malloc(sizeof(char)*strlen(str));
	if(!pattern) return errno;
	*pattern='\0';
	if(*str=='#') {
		if(*(str+1) =='\0') return -2;
		if(sscanf(str, "%*s %d", &ser_nr)<1 && nextstr(str,2))
		{
		 fprintf(stderr,"error:missing ser_no for \"#\"\n");
			return -2;
		  }
		 sscanf(str, "%*s %*s %[^\n]\n", pattern);

	}else{
		ser_nr=0;
		sscanf(str, "%*s %[^\n]\n", pattern);
	}
	if(*pattern && *pattern=='"'){
		size_t ll=strlen(pattern)-1;
		if(ll>0 && *(pattern+ll)=='"'){
		  *(pattern+ll)='\0';
		  s=pattern+1;
		  }
		else{fprintf(stderr,"unterminated \"\"\"\n"); return -3;}
	  }else s=*pattern?pattern:NULL;
	if(num)
		remove_numhook_bynum(num, ser_nr,s, numTree);
	 else remove_hook_bytype(type, ser_nr,s);
	 if(script_form)
		list_hooks(1);
	free(pattern);
	return 0;
}


static void on_cmd(void *data){
/*
Window not needed.
we dont want to see who created channel:
on 329 * :
and who set topic:
on 333 * :
on #pubmsg 100 "* * * *silly*" /notice $3 No more silliness, please!
on #topic 100 "* * #4 *" /action $3 hates topic changes on channel #4.
on CTCP_REPLY "* * * *ERRMSG *SED*" :
on finger * /reply $3 FINGER Keep your damn fingers to yourself!
*/
	On w;
	char *cmd;
	char *str;
	int num=-1, pos=3, n;
	char *tt, *tmp, *args=NULL;
	
	cs=(CmdStruct *)data;
	str=cs->args;
	w.ser_number=0;
	cmd=nextstr(cs->args,2);
	if(want_help(cs->args,cmd, cs->win)) return;
	
	if(!cmd){
		fprintf(stderr, "%s what?\n", cs->args);
		return;
	}
	if(!strncasecmp(cmd,"-del",4)){
		int had_error=1;
		cmd=nextstr(cs->args,3);
		if(*cmd=='#') cmd++;
		if((*cmd=='\0')) goto out;
		num=strtol(cmd, &tt,0);
		if( *tt ){
		       MsgNr type=get_type(cmd);
		       if(type!=INVALID)
			had_error=remove_hook(type,cs->args+find_pos(cs->args,3),0);
			else 
			fprintf(stderr,"Unknown message type:%s\n", cmd);
		}
		else
		if(!(*tt) && num>0)
	 		had_error=remove_hook(0,cs->args+find_pos(cs->args,3),num);
		else
			fprintf(stderr,	"Invalid numeric:%s\n", cmd);
		if(!had_error && script_form)
			list_hooks(1);
		return;
	}
	if(*cmd=='#') {
		pos=4;
		n=sscanf(str, "%*s %*s %d", &w.ser_number);
		if(n<1 || (*++cmd=='\0')) goto out;
	};
	w.type=CMD_IN;
	w.flag=HK_RESET;
	w.cmd.cmd=NULL;
	w.pattern=NULL;
	w.id=-1;
	tt=nextstr(str, pos);
	if(!tt) goto out;
	tmp=malloc(sizeof(char)*strlen(cs->args));
	args=malloc(sizeof(char)*strlen(cs->args));
	if(*tt=='"'){
		if(pos==4)
		   n=sscanf(str, "%*s %*s %*s \"%[^\"]\" %[^\n]\n",tmp, args);
	       	else
		   n=sscanf(str, "%*s %*s \"%[^\"]\" %[^\n]\n",tmp, args);
	}
	else{
		
		if(pos==4) n=sscanf(str, "%*s %*s %*s %s %[^\n]\n", tmp,args);
		else n=sscanf(str, "%*s %*s %s %[^\n]\n", tmp, args);
		
	}
	if(n<2){free(tmp);free(args); goto out;}
	w.pattern=strdup(tmp);
	w.cmd.cmd=strdup(args);
	num=strtol(cmd, &tt,0);
	if(*tt){
	 	MsgNr type=get_type(cmd);
	 	if(type!=INVALID){
			 add_hook(type, &w);
			 if(script_form) list_hooks(1);
		}
	 	else fprintf(stderr,
		 	"Unknown message type:%s\n", cmd);
	}else if( num>0){
		add_numhook(num, &w);
		if(script_form) list_hooks(1);
	}
	else
		fprintf(stderr,"invalid numeric (%d)?\n",num);
	free(w.pattern); free(w.cmd.cmd);
	free(args);
	free(tmp);
	return;
      out:
	fputs("error. unterminated \" or missing pattern or numeric",stderr);

}	

static void bell_cmd(void *data){
	CmdStruct *cs=(CmdStruct *)data;
	char *arg=nextstr(cs->args,2);
	if(want_help(cs->args,arg, cs->win)) return;
	fl_ringbell( (arg?atoi(arg):0) % 100);
}

static void exit_cmd(void *data){
	CmdStruct *cs=(CmdStruct *)data;
	char *arg;

	if(!cs || !cs->win) return;
	arg=nextstr(cs->args,2);
	if(want_help(cs->args,arg, cs->win)) return;
	if(!arg) fl_trigger_object(main_form->quit);
	else
	if ( !strncasecmp( arg,"-f",2)) {
		fl_finish();
		exit(0);
	}
	return;
}
static void lag_cmd(void *data){
	CmdStruct *cs=(CmdStruct *)data;

	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	fl_trigger_object(cs->win->chanwin->lag);
	return;
}


static void set_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);

	set(argc, argv, cs->win);
	return;
}

static void whatis_cmd(void *data){
	
	
	char *argv[10];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, 10);
	whatis(argc, argv, cs->win);
	return;
}

static void encrypt_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	
	do_encrypt(argc, argv, cs->win);
	return;
}

static void apropos_cmd(void *data){
	/*static void apropos(int argc, char **argv, Winstruct *win);*/
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	apropos(argc, argv, cs->win);
	return;
}

static void help_cmd(void *data){
	
	char *argv[4];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, 4);
	
	help(argc, argv, cs->win);
	return;
}


int show_std_help(const char *s1, const char *s2, FD_chanwin *chanwin){
/* leave here so as not to have to include help.h evrywhere */
	char *hfname;
	char *hdir=sula_lib;
	size_t ll;
	if(!s1) return -1;
	ll=strlen(hdir);
	#if 0
	if(*(hdir+ll-1)=='/')
		*(hdir+ --ll)='\0';
	#endif
	if(ll==0) ll=1;
	if(!s2){
		hfname=(char *)malloc(sizeof(char)*(ll+	strlen(s1)+ 16));
		if(!hfname) goto failed;
		sprintf(hfname,"%s/help/%s.hlp", *hdir?hdir:".",s1);
	}
	else {
		hfname=(char *)malloc(sizeof(char)*(ll+ strlen(s1)+ strlen(s2)+16));
		if(!hfname) goto failed;
		sprintf(hfname,"%s/help/%s/%s.hlp", *hdir?hdir:".", s1, s2);
	}
	if( access( hfname, R_OK)<0) goto failed;
	else{
		
		#define BUFF 1024
		FILE *fp=fopen(hfname,"r");
		char buf[BUFF+2];
		if(fp==NULL) goto failed;
		if(chanwin) fl_freeze_form(chanwin->chanwin);
		while(fgets(buf, BUFF, fp)){
			buf[strlen(buf)-1]=' ';	/* kludge:include empty lines */
			if(chanwin) fl_addto_browser(chanwin->browser, buf);
			else fputs(buf,stderr);
		}
		if(chanwin){ fl_addto_browser(chanwin->browser, "\n");
			fl_unfreeze_form(chanwin->chanwin);
		}
		fclose(fp);
		/*fl_load_browser(chanwin->browser,hfname); this clears browser!*/
		
		
	}
	return 0;
	failed:
		{
		
		char *buf=0;
		char *errstr=strerror(errno);
		int err=errno;
		if (gflags & BEEP_ERR) fl_ringbell(0);
		buf=(char *)malloc(sizeof(char)*(strlen(s1)+(s2?strlen(s2):0)+
		strlen(errstr)+ strlen(PROMPT)+16));
	 	sprintf(buf,PROMPT"help for %s %s: %s", s1, s2?s2:"",errstr);
	 	fl_addto_browser(chanwin->brow2, buf);
		free(buf);
		return err;
		}

}

static void show_cmds(Cmd *p, FL_OBJECT *brow){
	
	if(p){
		char *buf;
		show_cmds(p->left, brow);
		if(p->help) {
			buf=(char *)malloc(sizeof(char)*(strlen(p->name)+
			strlen(p->help) +50));
			sprintf(buf, "@b%s%s%s\n@f\t %s",  p->type&CMD_IN?"":"*", 
		 	p->name, p->type&CMD_CTCP?" (CTCP):":":", p->help
		   );
			fl_addto_browser(brow, buf);
		}
		show_cmds(p->right, brow);
	}
}


extern int save_bookmarks(const char *path, Bookmark *tree){
/* called by quit_cb() in glirc_cb.c */
	static void save_bm(FILE *fp, Bookmark *p);
	FILE *fp;
	/*time_t t=time(NULL);*/

	if( (fp=fopen(path, "w"))==NULL) return errno;
	fprintf(fp,sula_NAME sula_VERSION"\nBookmarks created %s\n\n",  fl_now());
	save_bm(fp, tree);
	fflush(fp);
	fclose(fp);
	return 0;
}
static void save_bm(FILE *fp, Bookmark *p){
	char buf[256];

	if(p){
		save_bm(fp, p->left);
		sprintf(buf,"%s:%s:%s:%u\n", p->channel,
			 p->title?p->title:"", 
			 p->server?p->server:"", 
			 p->port
			 );
		fputs(buf, fp);
	     	save_bm(fp, p->right);
	}
}

#if DEBUG>1
void    print_bookmark_tree(Bookmark * p)
{

	if (p) {
		print_bookmark_tree(p->right);
		fprintf(stderr,"%d. %s %d %s %s\n",
			 p->id, p->server?p->server:"",
			p->port, p->channel, p->title?p->title:"");
		
		print_bookmark_tree(p->left);
	}
}
#endif

int load_bookmarks(const char *filename, Bookmark **tree ){
	Bookmark *bm;
	FILE *fp;
	char *buf;
	int total=0;
	char *channel, *title, *server, *port;
	#define BUF	512
	if ( !(fp = fopen (filename, "r")) ){
      		return errno;
	} 
       	buf=(char *)malloc(sizeof(char)*(BUF+1));
	if(!buf) return errno;
	while( fgets(buf, BUF, fp)!=NULL){
			char *l, *ptr;
			size_t ll;

			buf[strlen(buf)-1]='\0';
			l=clean(buf);
			if(!l || (*l!='#' && *l!='&')) continue;
			bm=(Bookmark *)malloc(sizeof(Bookmark));
			if(!bm) {err_ret("%s %d", __FILE__, __LINE__); return errno;} 
			ll=strlen(buf);
			channel= (char *)malloc(sizeof(char)*ll);
			title= (char *)malloc(sizeof(char)*ll);
			server=(char *)malloc(sizeof(char)*ll);
			port=(char *)malloc(sizeof(char)*ll);
			if(!channel || !server || ! title || !port) return errno;
			ptr=channel;
			while(*buf && *buf!=':') *ptr++=*buf++;
			if(*buf==':'){ *ptr='\0'; buf++;}
			if(!(*channel)) return -2;
			ptr=title;
			while(*buf && *buf!=':') *ptr++=*buf++;
			if(*buf==':'){ *ptr='\0'; buf++;}
			if(!(*title)){ free(title); title=NULL;}
			ptr=server;
			while(*buf && *buf!=':') *ptr++=*buf++;
			if(*buf==':'){ *ptr='\0'; buf++;}
			if(!(*server)){ free(server);server=NULL;}
			ptr=port;
			while((*ptr++=*buf++));
			if(*port){
				int i=atoi(port);
				if(i<0) *port='\0';
				ll=strlen(port);
				while ((i=atoi(port))>USHRT_MAX) *(port+ --ll)='\0';
				if( (bm->port=i)==0 ) bm->port= DEFAULT_PORT;
			}
			 else bm->port= DEFAULT_PORT;
			
			bm->channel=channel;
			bm->title=title;
			bm->server=server;
			bm->id=++bm_id;
			insert_bookmark(tree, bm, &total);
			free(channel);free(title);free(server);free(port);

		}
	fclose(fp);
	#if DEBUG >1
	fprintf(stderr,"added %d bookmarks from %s\n", total,filename);
	print_bookmark_tree(bmTree);
	#endif
	return 0;
}


static void shell_cmd(void *data){
/*	/!ls -l or /! ls -l  */
	cs=(CmdStruct *)data;
	assert(cs && cs->win);
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) 
		return;
	else rmstr(&cs->args,1);
	if(cs->args && *(cs->args)){
		/* fl_show_command_log(FL_FULLBORDER);*/
		 fl_exe_command(cs->args, 0);
	}
	return;
}

static void warranty_cmd(void *data){
/* bugs && warranty */
	char *buf;
	
	cs=(CmdStruct *)data;
	assert(cs && cs->win);
	buf=nextstr(cs->args,2);
	if(want_help(cs->args,buf, cs->win)) return;
	else
	if( access( warranty_file, R_OK)==0)
		fl_load_browser(cs->win->chanwin->browser,warranty_file);
	else {
		buf=(char *)malloc(sizeof(char)*(strlen(PROMPT)+
		strlen(warranty_file)+128));
		sprintf(buf,PROMPT"Cant access %s: %s", warranty_file, strerror(errno));
		fl_addto_browser(cs->win->chanwin->brow2, buf);
		free(buf);
	}
	return;
}


/* list all channels on this window */
static void lch_cmd(void *data){
	
	Channel *current;
	int i=1;

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	else{
	current=cs->win->chanStart->left;
	while(current!=cs->win->chanStart){
		char buf[128];
		sprintf(buf,"%s\t%d. %s", current==cs->win->chanCur?"*":"",
		i++, current->name);
		fl_addto_browser(cs->win->chanwin->brow2, buf);
		current=current->left;
	}
	}
	return;
}

static void lg_cmd(void *data){
/* list allchannels*/
	gChannel *p=chanStart;
	char buf[MAXLINE];
	unsigned char i=1;

	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	while(p!=chanEnd){
		sprintf(buf, "\t%d. %-*s on %s (%d)",i++,MaxChanLen, p->name, p->server, p->port);
		fl_addto_browser(cs->win->chanwin->brow2, buf);
		p=p->next;
	}
	return;
}
static void lu_cmd(void *data){
/* list users in curren channel */
	
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	else
	if( cs->win->chanCur!=cs->win->chanStart) {
		char buf[256];
		Channel *p=cs->win->chanCur;
		User	*u;
		sprintf(buf, "@b@fInternal user list for %s",p->name);
		u=p->userStart;
		while(u!=p->userEnd){
			sprintf(buf, "@f%-*.*s %-4s %s (%s)", MaxNickLen+2,MaxNickLen,
			 u->name,u->mode,u->address,
			 u->ircname?u->ircname:"(null): update?");
			fl_addto_browser(cs->win->chanwin->browser, buf);
			u=u->next;
		}
		return;
	}
       return;
}


static void fwdcr_cmd(void *data){
/* forward a chat req received*/
	ChatReqRecvd *q=chatreqStart;
	int id;
	char *nick;

	cs=(CmdStruct *)data;
	if(!cs || !cs->args || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	rmstr(&cs->args,1);
	if(!cs->args) return;
	nick=(char*)malloc(sizeof(char)*strlen(cs->args));
	if(sscanf(cs->args,"%u %s", &id, nick)<2) return;
	while(q!=chatreqEnd)
		if(q->id==id){
			ChatReqRecvd *p=q->next;
			char buf[512];
			sprintf(buf, "PRIVMSG %s :%cDCC CHAT chat %lu %u%c\r\n",
				nick, 0x01,q->inetaddr, q->port,0x01);
			sendto_server(cs->win, buf);
			if(gflags & RM_ON_FWD){
				free(q->nick); free(q->email);
				if(p==chatreqEnd) chatreqEnd=q;else *q=*p; free(p);
			}
			break;
		}
		else q=q->next;
	free(nick);
	return;
}
static void fwdfr_cmd(void *data){
/*forward a file request received*/
	DCCFileR *q=filegetStart;
	int id;
	char *nick, *newname;

	cs=(CmdStruct *)data;
	if(!cs || !cs->args || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	rmstr(&cs->args,1);
	if(!cs->args) return;
	nick=(char*)malloc(sizeof(char)*strlen(cs->args));
	newname=(char*)malloc(sizeof(char)*strlen(cs->args));
	if(sscanf(cs->args,"%u %s", &id, nick)<2) return;
	if(sscanf(cs->args,"%*d %*s %s", newname)<1) newname=NULL;
	while(q!=filegetEnd)
		if(q->id==id){
			DCCFileR *p=q->next;
			char buf[512];
			sprintf(buf, "PRIVMSG %s :%cDCC SEND %s %lu %d %lu%c\r\n",
			nick, 0x01,newname?newname:q->filename, q->inetaddr, q->port, q->size, 0x01); 
			sendto_server(cs->win, buf);
			if(gflags & RM_ON_FWD){
					free(q->nick); free(q->email);free(q->filename);
					if(p==filegetEnd) filegetEnd=q;else *q=*p; free(p);
			}
			return;
		}
		else q=q->next;
	fl_addto_browser(cs->win->chanwin->brow2,PROMPT"No file offer with that id");
	return;
}

static void lac_cmd(void *data){
/* chats in progress */
	DCCChatWin *p=dccchatwinStart;
	char buf[MAXLINE];

	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	while(p!=dccchatwinEnd){
		sprintf(buf,"@f%3d. %-15s %s(%d) %d", p->id, p->nick, p->email?p->email:"",
		*p->fd, p->read);
		fl_addto_browser(cs->win->chanwin->brow2, buf); 		
		p=p->next;
	}
	return;
}
static void lcrr_cmd(void *data){
/* chat reqs received */
	ChatReqRecvd *p=chatreqStart;
	char buf[MAXLINE];
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	while(p!=chatreqEnd){
		sprintf(buf,"@f%3d. %-15s (%s)", p->id, p->nick, p->email);
		fl_addto_browser(cs->win->chanwin->brow2, buf); 
		p=p->next;
	}
	return;
}
static void lcrs_cmd(void *data){
/* chat reqs sent */
	ChatReqSent *p=chatsentStart;
	char buf[MAXLINE];
	 cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	while(p!=chatsentEnd){
		sprintf(buf,"@f%3d. %-15s (%d) %s", p->id, p->nick, 
		p->fd,ctime(&p->starttime));
		fl_addto_browser(cs->win->chanwin->brow2, buf); 
		p=p->next;
	}
	return;
}

static void lfrr_cmd(void *data){
/* waiting file offers received */
	DCCFileR *p=filegetStart;
	char buf[MAXLINE];
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	while(p!=filegetEnd){
		sprintf(buf,"@f%3d. %-15.13s %s (%s) %lu %d", p->id, p->nick,p->filename, 
			p->email, p->inetaddr, p->port);
		fl_addto_browser(cs->win->chanwin->brow2, buf); 
		p=p->next;
	}
	return;
}


static void laft_cmd(void *data){
/* active file transfers */

	DCCFile *p=DCCfileStart;
	char buf[MAXLINE];
	 cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	while(p!=DCCfileEnd){
		struct timeval	t;
		gettimeofday(&t,NULL);
		sprintf(buf,"@f%3d. %-15.13s %s %lu %lu %ld", 
		p->id, p->nick,p->filename,p->size,
		 p->read, (long) (time(NULL)- p->starttime.tv_sec));
		fl_addto_browser(cs->win->chanwin->brow2, buf); 
		p=p->next;
	}
	return;

}

static void lfrs_cmd(void *data){
/* waiting file offers sent */
	DCCFileS *p=filesendStart;
	char buf[MAXLINE];
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	while(p!=filesendEnd){
		sprintf(buf,"@f%3d. %-*s %-30.28s(%d) %-20.19s", p->id,MaxNickLen, p->nick,p->filename,p->fd, ctime(&p->starttime));
		fl_addto_browser(cs->win->chanwin->brow2, buf); 
		p=p->next;
	}
	return;
}
static void raft_cmd(void *data){
/* stop a file transfer*/
	int id;
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	if(sscanf(cs->args, "%*s %u", &id)<1) return;
	close_getfile(id, cs->win);
	return;
}
static void rfrr_cmd(void *data){
/* remove a file request received*/
	DCCFileR *q=filegetStart;
	int id;
	cs=(CmdStruct *)data;
	if(!cs ||  !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	if(sscanf(cs->args, "%*s %u", &id)<1) return;
	while(q!=filegetEnd)
		if(q->id==id){
			DCCFileR *p=q->next;
			free(q->nick); free(q->email);free(q->filename);
			if(p==filegetEnd) filegetEnd=q;else *q=*p; free(p);
			return;
		}
		else q=q->next;
	fl_addto_browser(cs->win->chanwin->brow2,PROMPT"No file offer with that id");
	return;
}

static void rfrs_cmd(void *data){
/* remove file request sent*/
	int id;
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;
	if(sscanf(cs->args, "%*s %u", &id)<1) return;
	remove_file_offer(id);
	return;
}

static void rcrs_cmd(void *data){
/* remove a chat req u sent*/
	int id;
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	if(want_help(cs->args,nextstr(cs->args,2), cs->win)) return;

	if(sscanf(cs->args, "%*s %u", &id)<1) return;
	remove_sentchatreq(id);
	return;
}

static void rcrr_cmd(void *data){
/* remove a chat req received*/
	ChatReqRecvd *q=chatreqStart;
	int id;
	char *arg;
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	arg=nextstr(cs->args,2);
	if(want_help(cs->args,arg, cs->win)) return;
	if(!arg) return;
	id=atoi(arg);
	if(id==0) return;
	while(q!=chatreqEnd)
		if(q->id==id){
			ChatReqRecvd *p=q->next;
			free(q->nick); free(q->email);
			if(p==chatreqEnd) chatreqEnd=q;else *q=*p; free(p);
			return;
		}
		else q=q->next;
	fl_addto_browser(cs->win->chanwin->brow2,PROMPT"No dcc chat with that id");
	return;
}

static void query_cmd(void *data){
	char *nick;
	char buf[128];

	 cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	nick=nextstr(cs->args,2);
	if(want_help(cs->args,nick, cs->win)) return;
	if(!cs->win->server){
	     fl_addto_browser(cs->win->chanwin->brow2,"Not connected toa server");
	 return;
	 }
	if(!nick){
		if(cs->win->query_nick){
			sprintf(buf, PROMPT"Ending conversation with %s",
			 cs->win->query_nick);
			free(cs->win->query_nick);
			cs->win->query_nick=NULL;
			if(fl_get_button(cs->win->chanwin->r_query))
				fl_set_button(cs->win->chanwin->r_quote,1);
			UpdateStatus(cs->win);
			fl_addto_browser(cs->win->chanwin->brow2,buf);
		}else
		fl_addto_browser(cs->win->chanwin->brow2,
		PROMPT"You aren't querying anyone!");
		return;
	}
	
	cs->win->query_nick=strdup(nick);
	fl_set_button(cs->win->chanwin->r_query,1);
	UpdateStatus(cs->win);
	sprintf(buf, PROMPT"Starting conversation with %s", nick);
	fl_addto_browser(cs->win->chanwin->brow2,buf);
	return;

}


static void msg_cmd(void *data){
/*
	/msg <nick or channnel or *> <msg>
	* = current channel
*/
	char *dest, type[10];
	char *msg, *msg0, *buf;
	char *fmt=NULL;
	cs=(CmdStruct *)data;
	
	if(!cs || !cs->win) return;
	
	dest=nextstr(cs->args,2);
	if(want_help(cs->args,dest, cs->win)) return;
	if(!cs->win->server){
		fl_addto_browser(cs->win->chanwin->brow2,
		"This window is not connected to a an irc server");
		fl_ringbell(0);
		return;
	}
	if(!dest){
		msg0=nextstr(cs->args,1);
		buf=(char *)malloc(sizeof(char)*(strlen(msg0)+16));
		sprintf(buf,"%s: to whom?",msg0 );
		fl_addto_browser(cs->win->chanwin->brow2, buf);
		free(buf);
		return;
	}
	if((!strcmp(dest,"*")) )
	if(cs->win->chanCur==cs->win->chanStart) return;
	else dest=cs->win->chanCur->name;
	buf=(char *)malloc(sizeof(char)*(strlen(cs->args)+50));
	msg=(char *)malloc(sizeof(char)*strlen(cs->args));
	if(!buf | !msg ){ err_ret("%s (%d)", __FILE__, __LINE__); return;}

	if(sscanf(cs->args, "%s %*s %[^\n]\n", type, msg) <2) return;
	msg0=strdup(msg);
	if((gflags & ENCRYPT_MSG) && (fmt=have_key(dest, ENCRYPT))){
		int ll=strlen(msg);
		encrypt_str(msg, ll, fmt);
		msg=ctcp_quote_it(msg, ll);
		free(buf);
		buf=(char *)malloc(sizeof(char)*(strlen(dest)+strlen(msg)+50));
		sprintf(buf,"PRIVMSG %s :%cSED %s%c\n", dest,0x01,msg,0x01);
		fmt=fmt_sed_your;
		/*fmt=strcasecmp(type, "msg")?fmt_you_priv_notice:fmt_you_priv_msg;*/
	}else
	if( !strcasecmp(type, "msg")){
		sprintf(buf, "PRIVMSG %s :%s\r\n", dest, msg);
		if(*dest=='#' || *dest=='&') fmt= fmt_you_pub_msg;
		else fmt=fmt_you_priv_msg;
	}
	else{
		sprintf(buf, "NOTICE %s :%s\r\n", dest, msg);
		if(*dest=='#' || *dest=='&') fmt= fmt_you_pub_notice;
		else fmt=fmt_you_priv_notice;
	}
	#if 1
	if(*dest!='#' && *dest!='&'){
	   if( !strcasecmp(type, "msg")){
		if(sflags & S_MSG) addto_sender(dest, SS_MSG);
	   }else if (sflags & S_NOTICE) addto_sender(dest, SS_NOTICE);
	}
	#endif

	sendto_server(cs->win, buf);
	fmt_string="%n%c%l%h%p%z$";
	free(buf);
	buf=format_msg(fmt, cs->win->server->nick,dest,Name,host,msg0,strlen(msg0));
	if(buf){
		fl_addto_browser(cs->win->chanwin->browser, buf);
		free(buf);
	}
       	free(msg0);
	return;
}

static void nick_cmd(void *data){
	cs=(CmdStruct *)data;
	if(cs && cs->win){
		char *argv[5];
		
		new_str2args(cs->args, &argc,argv,5);
		nick(argc, argv, cs->win);
	}
}

#if 0
//for later use..
static void list_chanwin(FL_OBJECT *ob){

	int i;
	char buf[MAXLINE];

	for(i=0;i<win_count;i++){
		if (winstruct[i].chanwin)
		if(winstruct[i].server)
			sprintf(buf,"%s\t%d. %-30s (%d) [%d] %p", winstruct[i].server->fd>-1?"*":"",
			i,winstruct[i].server->servname,
			  winstruct[i].server->port,winstruct[i].server->fd, &winstruct[i]);
		else
	      sprintf(buf,"\t%d. %-25s",
			i++,winstruct[i].dccCur==winstruct[i].dccStart?"Not connected":"DCC Chat");
		fl_addto_browser(ob, buf);
	}
}
#endif

static void echo_cmd(void *data){
/* echo [[-1|-2] txt] */
	char *arg;
	int mainbrow;	/* echo to browser1*/

	cs=(CmdStruct *)data;
	if(!cs || !cs->win ) return;
	arg=nextstr(cs->args,2);
	if(want_help(cs->args,arg, cs->win)) return;
	if(!arg) return;
	rmstr(&cs->args,1);
	if( !(mainbrow=strcmp( arg,"-2")) || !strcmp( arg,"-1"))
		 rmstr(&cs->args,1);
	if(*cs->args) fl_addto_browser(mainbrow?
	cs->win->chanwin->browser:cs->win->chanwin->brow2, cs->args);
	return;
}

static void quote_cmd(void *data){
 
	CmdStruct *cs=(CmdStruct *)data;
	char *arg;

	if(!cs || !cs->win) return;
	arg=nextstr(cs->args,2);
	if(!arg) return;
	if(want_help(cs->args,arg, cs->win)) return;
	else
   {
	if(!cs->win->server) return;
	if(!strcasecmp(nextstr(cs->args,2),"join")){
		fl_addto_browser(cs->win->chanwin->brow2,
		PROMPT"sorry, currently cant quote join!");
		return;
	}else{
		arg=(char *)malloc(sizeof(char)*(strlen(cs->args)+2));
		if(!arg){ err_ret("%s %d", __FILE__,__LINE__); return;}
		rmstr(&cs->args,1);
		sprintf(arg,"%s\n",cs->args);
		sendto_server(cs->win, arg);
		free(arg);
		return;
	}
    }
}


static void server_cmd(void *data){
 
	CmdStruct *cs=(CmdStruct *)data;
	
	if(!cs || !cs->win) return;
	else{
		char *argv[12];
		
		new_str2args(cs->args, &argc,argv,12);
		server(argc, argv, cs->win);
	}
	return;
}
static void who_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	who(argc, argv, cs->win);
	return;
}

#if 0
static void whois_cmd(void *data){
	char **argv;
	int argc;
	CmdStruct *cs=(CmdStruct *)data;


	if(!cs || !cs->win) return;
	str2args(cs->args, &argc,&argv);

       	whois(argc, argv,cs->win);
	return; 

}
#endif
static void op_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	
	op(argc, argv, cs->win);
	return;
}
static void kick_cmd(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	kick(argc, argv, cs->win);
	return;
}

static void mode_cmd(void *data){
	char *buf;
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	buf=nextstr(cs->args,2);

	if(!buf) {
		if(!cs->win->server) return;
		buf=(char *)malloc(sizeof(char)*(strlen(cs->win->server->nick)+8));
		sprintf(buf,"MODE %s\n", cs->win->server->nick);
		sendto_server(cs->win, buf);
		fputs(buf, stderr);
		free(buf);
		return;
	}else
	if(want_help(cs->args,buf, cs->win)) return;
	else{
		char *mode=(char *)malloc(sizeof(char)*strlen(cs->args));
		char *nick=nextstr(cs->args,2);
		buf=(char *)malloc(sizeof(char)*(strlen(cs->args)+256));
		if(!buf || !mode){ err_ret("%s %d", __FILE__,__LINE__); return;}
		if(sscanf(cs->args, "%*s %*s %[^\n]\n", mode)<1){free(mode); mode=NULL;}
		sprintf(buf,"MODE %s %s\n",strcmp(nick,"*")?nick:
	#if 1
	/* not working */ 
	cs->win->chanCur!=cs->win->chanStart?cs->win->chanCur->name:
	#endif
			cs->win->server->nick, mode?mode:"");
	
		sendto_server(cs->win, buf);
		if(!mode && (*nick=='#' || *nick=='&'))
			addto_cmd_sent(cs->win, "MODE",nick);
		free(buf); if(mode) free(mode);
	}
	return;
}
static void away_cmd(void *data){
	char *msg=NULL;
	char mark_away=1;
	 CmdStruct *inp=(CmdStruct *)data;
	if(!inp || !inp->win) return;
	msg=nextstr(inp->args,2);
	if(want_help(inp->args,msg, inp->win)) return;
	if(!msg){
		if(!inp->win->server) return;
		if(!(inp->win->server->flag&SET_AWAY)){
		 	msg=(char *)malloc(sizeof(char)*(strlen(away_text)+16));
			if(!msg) {err_ret("%s (%d)", __FILE__, __LINE__); return;}
			sprintf(msg,"AWAY :%s\n",away_text);
			mark_away=1;
		}else{
			msg=strdup("AWAY\n");
			mark_away=0;
		}
		goto doit;
	}
	if(!inp->win->server) return;
	msg=(char *)malloc(sizeof(char)*(strlen(inp->args)+10));
	if(!msg) {err_ret("%s (%d)", __FILE__, __LINE__); return;}
	/*rmstr(&inp->args,1); puts("2");*/
	sprintf(msg,"AWAY :%s\n", inp->args+find_pos(inp->args,2));
doit:
	if(!sendto_server(inp->win, msg)){
		#if 0
		/* instead wait for numeric replies */
		gChannel *p=chanStart;
		char buf[512];
		while(p!=chanEnd){
			if(!strcasecmp(p->server, inp->win->server->servname) && 
			 inp->win->server->port==p->port){
				sprintf(buf,"action %s is %s. %s",  p->name,
				mark_away? "away":"back", mark_away?inp->args:away2_text);
				process_cmd(buf, inp->win);
			}
			p=p->next;
		}
		#endif
	}
	free(msg);
	return;
}
		
static void me_cmd(void *data){
	char *buf, *dest;
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	buf=nextstr(cs->args,2);
	if(!buf) {
	fl_addto_browser(cs->win->chanwin->brow2, PROMPT"You what?"); 
	return;
	}
	if(want_help(cs->args,buf, cs->win)) {free(buf);return;}
	if(!cs->win->server) return;
	if(fl_get_button(cs->win->chanwin->r_query) && cs->win->query_nick)
		dest=cs->win->query_nick;
	else if(cs->win->chanCur!=cs->win->chanStart)
		dest=cs->win->chanCur->name;
	else  return;/* no channels*/

	buf=(char *)malloc(sizeof(char)*(strlen(cs->args)+strlen(dest)+50));
	if(!buf) {err_ret("%s (%d)", __FILE__, __LINE__); return;}
	rmstr(&cs->args,1);

	sprintf(buf,"PRIVMSG %s :%cACTION %s%c\r\n", dest,0x01,cs->args,0x01);
	if(sendto_server(cs->win, buf)==0){
		fmt_string="%n%c%l%h%p%z$";
		free(buf);
		buf=format_msg(fmt_you_pub_action, cs->win->server->nick,
			cs->win->chanCur->name,Name,host,cs->args, strlen(cs->args));
	  	if(buf) fl_addto_browser(cs->win->chanwin->browser,buf);
	}
	free(buf);
	return;
}
static void action_cmd(void *data){
/*
	/action <recipient> <msg>
	recipient = * means current channel

*/
	char *buf, *msg;
	char *nick;
	CmdStruct *cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	msg=nextstr(cs->args,2);
	if(!msg){
		fl_addto_browser(cs->win->chanwin->brow2,PROMPT"action what/who??");
		return;
	}
	if(want_help(cs->args,msg, cs->win)) {free(msg);return;}
	if(!cs->win->server) return;
	msg=(char *)malloc(sizeof(char)*strlen(cs->args));
	nick=(char *)malloc(sizeof(char)*strlen(cs->args));
	if(!nick || !msg) {err_ret("%s (%d)", __FILE__, __LINE__); return;}
	if(sscanf(cs->args, "%*s %s %[^\n]\n", nick,msg) < 2){
		fl_ringbell(0);
		fl_addto_browser(cs->win->chanwin->brow2,PROMPT"No message?");
		return;
	}
	buf=(char *)malloc(sizeof(char)*(strlen(cs->args)+50));
	if(!buf) {err_ret("%s (%d)", __FILE__, __LINE__); return;}
	if(!strcmp(nick,"*")) if(cs->win->chanCur==cs->win->chanStart) return;
	else nick=cs->win->chanCur->name;
	if(*nick!='#' && *nick!='&' && sflags & S_ACTION)
		addto_sender(nick, SS_ACTION); 
	sprintf(buf,"PRIVMSG %s :%cACTION %s%c\r\n", nick, 0x01,msg,0x01);
	if(sendto_server(cs->win,buf)==0){
		fmt_string="%n%c%l%h%p%z$";
		free(buf);
		buf=format_msg(fmt_you_priv_action, cs->win->server->nick,nick,Name,host,msg,strlen(msg));
		if(buf) fl_addto_browser(cs->win->chanwin->browser,buf);
	}
	free(buf);
	return;
}

static void chat_accept_cmd(void *data){
/* accept a chat request*/
	
	
	char *argv[5];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, 5);
	
	achat(argc, argv, cs->win);
	return;
}

static void chat_cmd(void *data){
/* send a chat req*/
	char *nick;
	int flag;
	char buf[128];
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;
	nick=nextstr(cs->args,2);
	
	if(!nick){
		fl_addto_browser(cs->win->chanwin->brow2,PROMPT"chat with ... ?");
		return;
	}
	if(want_help(cs->args,nick, cs->win)) {return;}
	if(!cs->win->server){
		fl_ringbell(0);
		fl_addto_browser(cs->win->chanwin->brow2, 
		PROMPT"Cannot send request. You are not connected to a server");
		return;
	}
	nick=(char *)malloc(sizeof(char)*strlen(cs->args));
	if(sscanf(cs->args, "%*s %s", nick) <1 ){
	      fl_addto_browser(cs->win->chanwin->brow2, PROMPT"nickname required for DCC chat");
	      {free(nick);return;}
	}
	if(sscanf(cs->args,"%*s %*s %d", &flag)<1) flag=0;
	if(dcc_sendchatreq(cs->win, flag?NULL: cs->win, nick)){
		sprintf(buf, PROMPT"DCC chat connection to %s failed",nick);
		fl_addto_browser(cs->win->chanwin->brow2, buf);
	}else{
		sprintf(buf, PROMPT"DCC chat request sent to %s",nick);
		fl_addto_browser(cs->win->chanwin->brow2, buf);
	}
	free(nick);return;
}
static void chat_close_cmd(void *data){
/* close a chat session */
	char *reason;
	DCCChatWin *q=dccchatwinStart;
	int id;
	cs=(CmdStruct *)data;
	if(!cs || !cs->win) return;

	reason=nextstr(cs->args,2);
	if(want_help(cs->args,reason, cs->win)) {return;}
	if(!reason){
		fl_addto_browser(cs->win->chanwin->brow2,PROMPT"close which DCC chat?");
		return;
	}
	id=atoi(reason);
	if(id)
		while(q!=dccchatwinEnd) if(q->id==id) break; else q=q->next;
	else{
		size_t ll=strlen(reason); 
		while(q!=dccchatwinEnd) 
			if(!strncasecmp(reason, q->nick, ll)) { id=q->id; break;}
			else q=q->next;
	}
	if(q==dccchatwinEnd){
		fl_addto_browser(cs->win->chanwin->brow2,PROMPT"No chat with that id/person");
		return;
	}
	reason=(char *)malloc(sizeof(char)*strlen(cs->args));
	/*if(sscanf(cs->args, "%*s %d", &id)<1) return;*/
	if(sscanf(cs->args, "%*s %*s %[^\n]\n",  reason)<1) 
		reason=NULL;
	if(!remove_dccchat(id, reason))
		fl_addto_browser(cs->win->chanwin->brow2,PROMPT"No dcc chat with that id to close");
	else{
	       UpdateStatus(cs->win);
	       fl_addto_browser(cs->win->chanwin->brow2,PROMPT"DCC chat closed.");
	}
	free(reason);return;
}


static void get_cmd(void *data){
/* receive a file*/
	
	char *argv[5];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, 5);
	
	get(argc, argv, cs->win);

	return;
}
static void send_cmd(void *data){
/* offer file to someone*/
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	
	offer(argc, argv, cs->win);
	return;
}

static void quoted_cmds(void *data){
	
	char *argv[MAXARGC];
	

       	cs=(CmdStruct *)data ;
	if(!cs || !cs->win) return;
	new_str2args(cs->args, &argc,argv, MAXARGC);
	
	quoted_cmd(argc, argv, cs->win);
	return;
}



/****************************************************************/
/*	c t c p							*/
/****************************************************************/

static void ping_cmd(void *data){
/* /ping nikc1 nick2,nick3 ... */
	char *buf;
	
	cs=(CmdStruct *)data;
	
	if(!cs || !cs->win) return;

	if(want_help(cs->args,nextstr(cs->args,2), cs->win))
	 {return;}
	if(!cs->win->server) return;

	buf=(char *)malloc(sizeof(char)*strlen(cs->args));
	if (sscanf(cs->args,"%*s %[^\n]\n", buf)<1) return;
	buf=strtok(buf,", \t");
	while(buf){
		char s[128];
		struct timeval t;

		if(gettimeofday(&t, NULL)!=EINVAL)
		sprintf(s,"PRIVMSG %s :%cPING %d %d%c\n",
		 buf,0x01,t.tv_sec, t.tv_usec,0x01);
		 else sprintf(s,"PRIVMSG %s :%cPING %ld%c\n",
		 buf,0x01,time(NULL),0x01);
		sendto_server(cs->win,s);
	  	buf=strtok(NULL,", \t");
	}
	 return; 
}


static void std_ctcp(void *data){
/* e.g.:	/finger nick1 nick2 nick3 ... */
	const char 	*std_ctcp_cmd[]={
		"CHELP",
		"CLIENTINFO",
		"FINGER",
		"HELP",
		"TIME",
		"USERINFO",
		"VERSION",
		
	};
	static int binary(const char **p, const char *var, int n);
	int i;
	char *cmd, *nick;
	const char **p=std_ctcp_cmd;

	cs=(CmdStruct *)data;
	if(!cs || !cs->win ) return;
	nick=nextstr(cs->args,2);
	if(!nick) {
		fl_ringbell(0);
		fl_addto_browser(cs->win->chanwin->brow2,
		PROMPT"Need nicknames for command");
		return;
	}
	if(want_help(cs->args,nick, cs->win)) {return;}
	cmd=nextstr(cs->args,1);
	i=binary(p, cmd, sizeof(std_ctcp_cmd)/sizeof(char *) );
	if(i<0){
		char *buf=(char *)malloc(sizeof(char)*(strlen(cmd)+32));
		sprintf(buf, PROMPT"%s: Unknown CTCP cmd",cmd);
		fl_addto_browser(cs->win->chanwin->brow2,buf);
		return;
	}else{
		int error=0;
		if(!strcasecmp("chelp", std_ctcp_cmd[i])) cmd="HELP";
		else cmd=(char *)std_ctcp_cmd[i];
		nick=strtok(cs->args,", \t");
		while( (nick=strtok(NULL,", \t")) && !error){
			char s[128];
			sprintf(s,"PRIVMSG %s :%c%s%c\n", nick,0x01,cmd,0x01);
			error=sendto_server(cs->win,s);
		} 
	}
	 return;
}
static int binary(const char **p, const char *var, int n){
	register int low, high, mid, cond;
	low=0; high=n-1;
	while(low<=high){
		mid=(low+high)/2;
		if( (cond=strcasecmp(var, *(p+mid)) )<0 )
			high=mid-1;
		else if(cond>0)
			low=mid+1;
		else
			return mid;
	}
	return -1;
}


static void other_ctcp(void *data){
/*
	/ctcp nick args
	default args="VERSION"
	/reply nick usersinfo bang bang!
*/
	char *buf=NULL, *nick=NULL;
	char *bigbuf=NULL;
	
	cs=(CmdStruct *)data;

	if(!cs->win) return;
	nick=nextstr(cs->args,2);
	if(!nick) {
		fl_addto_browser(cs->win->chanwin->brow2,
		PROMPT"CTCP to whom?");
		return;
	}
	if(want_help(cs->args,nick, cs->win)) {return;}
	if(!cs->win->server){
		fl_ringbell(0);
		fl_addto_browser(cs->win->chanwin->brow2,
		PROMPT"No server");
	 	return;
	}
	buf=(char *)malloc(sizeof(char)*(strlen(cs->args)));
	if (sscanf(cs->args,"%*s %*s %[^\n]\n",buf)<1)
		{free(buf);buf=strdup("VERSION");}
	bigbuf=(char *)malloc(sizeof(char)*(strlen(cs->args)+64));
	if(!strncasecmp(cs->args,"reply ",6)){
		#define MAX_SERV_SEND	400/*485*/
			sprintf(bigbuf,"NOTICE %s :%c%s%c\n", nick,0x01,buf,0x01);
			if(strlen(bigbuf)>MAX_SERV_SEND){
			 *(bigbuf+MAX_SERV_SEND-2)=0x01;
			 *(bigbuf+MAX_SERV_SEND-1)='\n';
			*(bigbuf+MAX_SERV_SEND)='\0';
			 }
			 sendto_server(cs->win, bigbuf);
	}
	else 
	       if(!strcmp(nick,"*"))
		  if(cs->win->chanCur!=cs->win->chanStart){
		  	User *p=cs->win->chanCur->userStart;
			while(p!=cs->win->chanCur->userEnd){
				sprintf(bigbuf,"PRIVMSG %s :%c%s%c\n",
				 p->name,0x01,buf,0x01);
				 sendto_server(cs->win, bigbuf);
				 p=p->next;
			}
		   }else fl_addto_browser(cs->win->chanwin->brow2,
			PROMPT"*: No channels");
		else {
			sprintf(bigbuf,"PRIVMSG %s :%c%s%c\n", nick,0x01,buf,0x01);
			sendto_server(cs->win, bigbuf);
		}
	free(buf); free(bigbuf);
	 return;
}


extern void add_cmdoncon(int type, char *cmd, char *server, u_short port){
/* command to execute on connection to server*/
	CmdOnCon *p;

	if(!cmd) return;
	p=cmdonconStart;
	cmdonconStart=(CmdOnCon *)malloc(sizeof(CmdOnCon));
	if(!cmdonconStart){err_ret("%s (%d)", __FILE__, __LINE__); return;}
	cmdonconStart->type=type;
	cmdonconStart->cmd=strdup(cmd);
	cmdonconStart->server=strdup2(server);	/* NULL-->any server */
	cmdonconStart->port=port;		/* <1-->any port */
	cmdonconStart->next=p;
	return;
}
static void lnicks_cmd(void *data){
	 int n=0;
	 while(n<iNickListCount){
	 	if(aNickList[n].label){
			 Nicks *p=aNickList[n].start->left;
			 
			 fprintf(stdout,"\E[4m%s\E[m:\n",aNickList[n].label);
			 while(p!=aNickList[n].start){
			 	fprintf(stdout,"\t%s%s\n",
				p==aNickList[n].current?"*":"",
				p->nick	);
				p=p->left;
			}
	 	}
	 	n++;
	 }
}


/*	s t o p	       s t o p		s t o p		s t o p			*/

/************************************************/
/*	cmd handling routines			*/
/************************************************/

void print_cmd_tree(Cmd *p , int pos){
	if(p){
		print_cmd_tree(p->right, pos+8);
		printf("%*.10s(%s) %d\n",pos,
		  p->name, p->type&CMD_IN?"IN":"EX", p->bf);
		print_cmd_tree(p->left, pos+8);
	}
}
void list_cmds(Cmd *p){
	if(p){
		list_cmds(p->left);
		printf("%s%s:%s %s\n",
		   p->type&CMD_IN?"":"*", p->name, 
		   p->help?"\n\t":"", p->help?p->help:""
		   );
		list_cmds(p->right);
	}
}

extern int load_bi_cmds(void){
/*load built-in cmds*/
typedef struct TBiCmd{
	char *name;
	char *help;
	void (*func)(void *);
} BiCmd;
BiCmd	built_in_cmds[]={
	{"whatis",	"display brief description of a command",& whatis_cmd},
	{"apropos",	"search whatis list for strings", &apropos_cmd},
	{"ignore",	"specify what to ignore from names users./ig -help",&ignore_cmd},
	{"set",		"change client setting",& set_cmd},
	{"history",	"Manipulate the command history list",&history_cmd},

	
	{"lch",		"list channels on current window", &lch_cmd},
	{"lg",		"list all your irc channels", &lg_cmd},
	{"lu",		"list members of current channel", &lu_cmd},
	{"fwdcr",	"forward chat request", &fwdcr_cmd},
	{"fwdfr",	"forward file request", &fwdfr_cmd},
	{"lac",		"list active chats", &lac_cmd},
	{"lcrr",      	"list chat reqs recvd", &lcrr_cmd},
	{"lcrs",      	"list chat reqs sent", &lcrs_cmd},
	{"lfrs",      	"list file reqs sent", &lfrs_cmd},
	{"lfrr",      	"list file reqs recvd", &lfrr_cmd},
	{"laft",      	"list active file transfers", &laft_cmd},
	{"fclose",	"stop an active file transfer", &raft_cmd},
	{"rfrs",      	"remove file request sent", &rfrs_cmd},
	{"rfrr",      	"rm file request received", &rfrr_cmd},
	{"rcrs",      	"remove chat request sent", &rcrs_cmd},
	{"rcrr",      	"rm chat request received", &rcrr_cmd},
	{"server",	"manipulate server list or change server", &server_cmd},
	{"send",      	"offer files to somebody",&send_cmd}, 
	{"cclose",	"close a DCC chat connection", &chat_close_cmd},
	{"get",		"accept a file", &get_cmd},
       	{"chat",      	"create a DCC chat connection",&chat_cmd},
	{"achat",	"accept a DCC chat request",&chat_accept_cmd},
	{"ping",       	"send the std un*x time to multiple recipients",&ping_cmd},
	{"query",	"start a private conversation", &query_cmd},
	{"msg",		"send a message to a channel or to somebody", &msg_cmd},
	{"notice",	"send a notice to a channel or to person", &msg_cmd},
	{"nick"	,	"change your nickname", &nick_cmd},
	{"help"	,	"show help on a command", &help_cmd},
	{"me",		"send a CTCP ACTION to a channel", &me_cmd},
	{"action",	"send a CTCP ACTION to a person or channel", &action_cmd},
	{"ctcp",       	"send a CTCP command",&other_ctcp},
	{"reply",      	"manually reply to a CTCP command",&other_ctcp}, 
	{"encrypt",	"handle encrypted messages", encrypt_cmd},
	{"clientinfo",	"send a CTCP CLIENTINFO to multiple recipients",&std_ctcp},
	{"finger",	"send a CTCP FINGER to multiple recipients",&std_ctcp},
	{"chelp",	"send a CTCP HELP to multiple recipients",&std_ctcp},
	{"time",	"send a CTCP TIME to multiple recipients",&std_ctcp}, 
	{"userinfo",	"send a CTCP USERINFO to multiple recipients",&std_ctcp}, 
	{"version",	"send a CTCP VERSION to multiple recipients",&std_ctcp}, 
	
	{"quote",	"quote a command to server", &quote_cmd},
	{"who",		"list users on irc or upate internal channel user list", &who_cmd},
	{"whois",	"request info about persons using named nicknames", &quoted_cmds},
	
	{"on",		"preliminary hook structure..testing",&on_cmd},
	{":",		"empty command. Has no effect.",&empty_cmd},
	{"window",	"operations on channel window objects",&window_cmd},
	{"bell",	"ring the keyboard bell", &bell_cmd},
	
	{"whowas",	"request info about persons who used the named nicknames",&quoted_cmds},
	{"userhost",	"ask for information about named persons",&quoted_cmds},
	{"ison",	"ask whether named persons are currently on irc",&quoted_cmds},
	{"away",	"(un)mark yourself as (not)being away", &away_cmd}, 
	{"mode",	"change user or channel mode", &mode_cmd},
	{"op",		"make people operators on current channel; channel MODE",&op_cmd},
	{"kick",	"eject somebody from a channel", &kick_cmd},
	{"invite",	"invite someone to a channel", &invite_cmd},
	{"deop",	"unmake people operators on current channel; channel MODE ",&op_cmd},
	{"topic",	"change channel topic",topic_cmd},
	{"leave",	"leave a channel (equivalent to part)",part_cmd},
	{"part",	"leave a channel",part_cmd},
	{"join",	"join channels", &join_cmd},
	{"!",		"run a shell command",&shell_cmd},
	{"lag",		"force lag update", &lag_cmd},
	{"echo",	"echo a text", &echo_cmd},
	{"bugs",	"list known bugs", &warranty_cmd},
	{"warranty",	"bugs and warranty info", &warranty_cmd},
	{"quit",	"quit IRC session on a server", &quit_cmd},
	{"exit",	"quit irc and end application", &exit_cmd},

	{"ln",	"-debug- list nickname groups to stdout", &lnicks_cmd},

	};	

	Cmd cmd;
	int nvars=sizeof(built_in_cmds)/sizeof(BiCmd);
       int i;

       for(i=0;i<nvars;i++){
	       	cmd.name=built_in_cmds[i].name;
		cmd.help=built_in_cmds[i].help;
		cmd.type=CMD_IN|CMD_NORMAL;
		cmd.cmd.in=built_in_cmds[i].func;
		insert_cmd(&cmdTree, &cmd, CMD_NORMAL);
	}
	return 0;

}
extern int process_cmd(const char *str, Winstruct *win){
/* win can be NULL */
	Cmd *cmd;
	
	cmd=find_cmd(nextstr(str,1),CMD_NORMAL);
	if(!cmd) return -1;
	if(cmd->type & CMD_IN){
		CmdStruct cs;
		char *buf=(char *)malloc(sizeof(char)*(strlen(str)));
		if(!buf) {err_ret("%s (%d)", __FILE__, __LINE__); return errno;}
		if(sscanf(str,"%*s %[^\n]", buf)<1)
			cs.args=strdup(cmd->name);
		else{
			cs.args=(char *)malloc(sizeof(char)*(strlen(str)+strlen(cmd->name)));
			if(!cs.args) {err_ret("%s (%d)", __FILE__, __LINE__); return errno;}
			sprintf(cs.args, "%s %s", cmd->name, buf);
		}
		cs.win=win;
		free(buf);
       		(cmd->cmd.in)( (void*)&cs);
		free(cs.args);
	}
	 else	if(cmd->type & CMD_FD){
			size_t ll=strlen(str);
			if(writen( s_pipe[cmd->cmd.sp].fd, str,ll)!=ll){
				if(errno==EINTR || errno==EAGAIN) errno=0;
				else{
				 berr_ret("%s (%d) write error to %d",
				  __FILE__, __LINE__,s_pipe[cmd->cmd.sp].fd);
				 remove_sp(s_pipe[cmd->cmd.sp].fd);
				 }
			}
		} 
	/*else if(cmd->type & CMD_SYS)
		do_exec(cmd->cmd.sys_cmd, str);*/
	else {
		#if DEBUG
		fprintf(stderr,"bug? unknown cmd type %#x", cmd->type);
		#endif
		return (-1);
		}
	return 0;
}
extern int write_to_sp(int sp, const char *buf){
	size_t ll;
	if(s_pipe[sp].fd<0) return (-1);
	if(s_pipe[sp].status & (SCR_STOPPED | SCR_SLEEPING | SCR_SUSPENDED)) return (-2);
	ll=strlen(buf);
	if(writen( s_pipe[sp].fd, buf,ll)!=ll)
		if(errno==EINTR || errno==EAGAIN) return errno;
		else{
			int err=errno;
			berr_ret("%s (%d) write error to %d:%s(pid=%d)", 
			__FILE__, __LINE__,
			s_pipe[sp].fd,
			s_pipe[sp].sys_cmd, s_pipe[sp].pid);
			remove_sp(s_pipe[sp].fd);
			return err;
		}
	return 0;
}
extern int new_process_cmd(const char *str, int i){
/* win can be NULL */
	Cmd *cmd;
	
	cmd=find_cmd(nextstr(str,1),CMD_NORMAL);
	if(!cmd) return (-1);
	if(cmd->type & CMD_IN){
		CmdStruct cs;
		char *buf=(char *)malloc(sizeof(char)*(strlen(str)));
		if(!buf) {err_ret("%s (%d)", __FILE__, __LINE__); return errno;}
		buf=strdup(str);
		rmstr(&buf,1);
		if(buf==NULL)
			cs.args=strdup(cmd->name);
		else{
			cs.args=(char *)malloc(sizeof(char)*(strlen(str)+strlen(cmd->name)));
			if(!cs.args) {err_ret("%s (%d)", __FILE__, __LINE__); return errno;}
			sprintf(cs.args, "%s %s", cmd->name, buf);
		}
		if(i<0 || i>win_count-1 || winstruct[i].chanwin==NULL)
			cs.win=NULL;
		else cs.win=&winstruct[i];
		if(buf) free(buf);
       		(cmd->cmd.in)( (void*)&cs);
		free(cs.args);
	}
	 else	if(cmd->type & CMD_FD){
			char *tmp;
			int port=0;
			char   *buf;

			assert(cmd->cmd.sp<sp_count && s_pipe[cmd->cmd.sp].fd>-1);
			if(i>-1 && i<win_count)
				if(winstruct[i].server && winstruct[i].server->fd>0) 
					port=1;
			buf = (char *) malloc(sizeof(char) * (strlen(str) + 24 +
			(port?	strlen(winstruct[i].server->servname):0)
			));
			if(!buf) return 0;
			tmp=strdup(str);
			rmstr(&tmp,1);
			/* <id> <cmd> <server,port> <rest> */
			sprintf(buf,"%d %s %s,%d %s\n", i, cmd->name, port?
			winstruct[i].server->servname:"0", 
			port?winstruct[i].server->port:0,
			tmp);
			free(tmp);
			write_to_sp(cmd->cmd.sp, buf);
			return 0;
		} 
	/*else if(cmd->type & CMD_SYS)
		do_exec(cmd->cmd.sys_cmd, str);*/
	else {
		#if DEBUG
		fprintf(stderr,"bug? unknown cmd type %#x", cmd->type);
		#endif
		return (-1);
		}
	return 0;
}

extern Cmd *find_cmd(char *name, int type){
	/*
	type is an OR'ing of any number of types 
	*/
	Cmd *p=cmdTree;
	int c;
	size_t ll;
	if(!name) return NULL;
	ll=strlen(name);
	while(p){
		c= strncasecmp(name, p->name,ll);
		if(c==0){
			if(p->type & type) return p;
			else if(type > p->type) p=p->right;
			else p=p->left;
		}else 
		if(c<0) p=p->left;
		else p=p->right;
	}
	return NULL;
} 


extern int remove_cmd(char *name,int type){
	static int del_cmd(pCmd *pp, Cmd *c);
	Cmd *c;
	c=find_cmd(name, type);
	if(!c) return 2;
	else{
		del_cmd(&cmdTree, c);
	}
	return 1;
}



static void left_rotate(pCmd	*pp){
	pCmd	p=*pp, r;
	*pp= r = p->right;
	p->right = r->left;
	r->left=p;
	p->bf--;
	if(r->bf>0) p->bf -= r->bf;
	r->bf--;
	if(p->bf<0) r->bf += p->bf;
}

static void right_rotate(pCmd *pp){
	pCmd	p=*pp, l;
	*pp= l = p->left;
	p->left = l->right;
	l->right=p;
	p->bf++;
	if(l->bf<0) p->bf -= l->bf;
	l->bf++;
	if(p->bf>0) l->bf += p->bf;
}

extern int insert_cmd(pCmd *pp, Cmd *c, int flag){
	/*
	no duplicate command names allowed for the same cmd flag.
	(CMD_NORMAL or CMD_CTCP).
	cmd type is: flag | (either CMD_FD, CMD_FD or CMD_SYS). was set by caller
	*/

	int dH=0;
	pCmd p=*pp;
	int tt;
	
	assert(c && c->name);
	if(p==NULL){
		
		*pp=p=(pCmd)malloc(sizeof(Cmd));
		if(!p) err_sys("%s %d", __FILE__, __LINE__);
		p->name=strdup(c->name);
		p->help=strdup2(c->help);
		p->type=c->type;
		p->cmd=c->cmd;
		p->bf=0;
		p->left=p->right=NULL;
		dH=1;
		no_of_cmds++;
	}else
	if( 
	    ((tt=strcasecmp( c->name, p->name)) >0 || 
	    	(tt==0 && ((p->type&flag) > flag)))
	  ){
	   if(insert_cmd(&p->right, c, flag)){
		p->bf++; 	
		if(p->bf==1) dH=1; else
		if(p->bf==2){
			if(p->right->bf==-1) right_rotate(&p->right);
			left_rotate(pp);
		}
	   }
	}else
	if( 
	   tt<0 || (tt==0 && ((p->type&flag) <  flag))
	  ){
	   if(insert_cmd(&p->left, c, flag)){
	   	p->bf--;
		if(p->bf==-1) dH=1;else
		if(p->bf==-2){
			if(p->left->bf==1) left_rotate(&p->left);
			right_rotate(pp);
		}
	  }
	}
	else{
	/* this isnt very correct. program wont run correctly after this.
	avoid duplicates by cheching before calling this routine 
	*/
	fprintf(stderr,"%s(%d):duplicate %s. flags:%#x, %#x\n",
		__FILE__,__LINE__, c->name, p->type,flag);	/* duplicate cmd */
		return 0;
	}
	return dH;
}

static int del_cmd(pCmd *pp, Cmd *c){
	pCmd p=*pp, *q;
	int dH=0;
	int tt;
	
	if(!p) return 0;
	if( 
	    		(tt=strcasecmp(c->name, p->name))<0 || 
			(tt==0 && c->type < p->type)
	    
	   ){
	   if(del_cmd(&p->left, c)){
	  	p->bf++;
		if(p->bf == 0) dH=1;else
		if(p->bf == 2){
			if(p->right->bf==-1) right_rotate(&p->right);
			left_rotate(pp);
			if(p->bf==0) dH=1;
		}
	   }
	}else
	if( 
	 	(tt>0 || (tt==0 && c->type > p->type))
		
	   ){
	   if(del_cmd(&p->right, c)){
	   	p->bf--;
		if(p->bf == 0) dH=1;else
		if(p->bf == -2){
			if(p->left->bf==1) left_rotate(&p->left);
			right_rotate(pp);
			if(p->bf==0) dH=1;
		}
	   }
	}else{ /* got it! */
		#define TT
		no_of_cmds--;
		if(p->right ==NULL){
			*pp=p->left;
			free(p);

			 return 1;
		}else
		if(p->left ==NULL){
			*pp=p->right;
			free(p);
			return 1;
		}else{
			q= &p->left;
			while( (*q)->right!=NULL ) q= &(*q)->right;
			p->name=strdup((*q)->name);
	       		p->help=strdup((*q)->help);
			p->type=(*q)->type;
			p->cmd=(*q)->cmd;
			no_of_cmds++;
			
			(*q)->name=strdup(c->name);
			(*q)->help=strdup2(c->help);
			(*q)->type=c->type;
			(*q)->cmd=c->cmd;
			if(del_cmd(&p->left, c)){
				p->bf++;
				if(p->bf==0) dH=1;else
				if(p->bf==2){
					if(p->right->bf==-1) right_rotate(&p->right);
					left_rotate(pp); dH=1;
				}
			}
		}
	
	}
	return dH;
}


/*
	bookmark list
*/

static void bm_left_rotate(pBookmark *pp){
	pBookmark	p=*pp, r;
	*pp= r = p->right;
	p->right = r->left;
	r->left=p;
	p->bf--;
	if(r->bf>0) p->bf -= r->bf;
	r->bf--;
	if(p->bf<0) r->bf += p->bf;
}

static void bm_right_rotate(pBookmark *pp){
	pBookmark p=*pp, l;
	*pp= l = p->left;
	p->left = l->right;
	l->right=p;
	p->bf++;
	if(l->bf<0) p->bf -= l->bf;
	l->bf++;
	if(p->bf>0) l->bf += p->bf;
}

extern int insert_bookmark(pBookmark *pp, Bookmark *c, int *id){
/* #if OLD__ then dont allow duplicate bookmarks */

	int dH=0;
	pBookmark p=*pp;
	
	
	assert(c && c->channel);
	if(p==NULL){
		
		*pp=p=(pBookmark)malloc(sizeof(Bookmark));
		if(!p){ err_sys("%s %d",__FILE__, __LINE__ ); }
		p->channel=strdup(c->channel);
		p->title=strdup2(c->title);
		p->server=strdup2(c->server);
		p->port=c->port;
		p->id=c->id;
		p->bf=0;
		p->left=p->right=NULL;
		*id=p->id;
		dH=1;
	}else
{
	/* we shall allow  duplicate bookmarks */
	if( 
	    c->id >p->id
	  ){
	   if(insert_bookmark(&p->right, c, id)){
		p->bf++; 
		if(p->bf==1) dH=1; else
		if(p->bf==2){
			if(p->right->bf==-1) bm_right_rotate(&p->right);
			bm_left_rotate(pp);
		}
	   }
	}else
	if( 
	   c->id<p->id
	  ){
	   if(insert_bookmark(&p->left, c, id)){
	   	p->bf--;
		if(p->bf==-1) dH=1;else
		if(p->bf==-2){
			if(p->left->bf==1) bm_left_rotate(&p->left);
			bm_right_rotate(pp);
		}
	  }
	}
	#if DEBUG
	else{
	/* Bug */
	fprintf(stderr,"%s(%d):duplicate bm id %d\n",
		__FILE__,__LINE__, c->id);
	}	
	#endif
}
	return dH;
}

extern int del_bookmark(pBookmark *pp, Bookmark *c){
	pBookmark p=*pp, *q;
	int dH=0;
	

	
	if(!p) return 0;
	if( 
	    c->id<p->id
	   ){
	   if(del_bookmark(&p->left, c)){
	  	p->bf++;
		if(p->bf == 0) dH=1;else
		if(p->bf == 2){
			if(p->right->bf==-1) bm_right_rotate(&p->right);
			bm_left_rotate(pp);
			if(p->bf==0) dH=1;
		}
	   }
	}else
	if( 
		c->id>p->id
	   ){
	   if(del_bookmark(&p->right, c)){
	   	p->bf--;
		if(p->bf == 0) dH=1;else
		if(p->bf == -2){
			if(p->left->bf==1) bm_left_rotate(&p->left);
			bm_right_rotate(pp);
			if(p->bf==0) dH=1;
		}
	   }
	}else{ /* found bookmark */
		if(p->right ==NULL){
			*pp=p->left;
			free(p);
			 return 1;
		}else
		if(p->left ==NULL){
			*pp=p->right;
			free(p);
			return 1;
		}else{
			q= &p->left;
			while( (*q)->right!=NULL ) q= &(*q)->right;
			/**p=*(*q);*/
			p->channel= strdup((*q)->channel);
			p->title=strdup2((*q)->title);
			p->server=strdup2((*q)->server);
			p->port=(*q)->port;
			p->id=(*q)->id;
		}

			/**(*q)=*c;*/
			(*q)->channel= strdup(c->channel);
			(*q)->title=strdup2(c->title);
			(*q)->server=strdup2(c->server);
			(*q)->port=c->port;
			(*q)->id=c->id;
			if(del_bookmark(&p->left, c)){
				p->bf++;
				if(p->bf==0) dH=1;else
				if(p->bf==2){
					if(p->right->bf==-1) bm_right_rotate(&p->right);
					bm_left_rotate(pp); dH=1;
				}
			}
	}
	return dH;
}

/*

numeric hooks

*/


