/* script.c	-routines scripting.

    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 <time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <fnmatch.h>
#include <ctype.h>
#include "aux.h"
#include "sula.h"
/*#include "servers.h"*/
#include "script_gui.h"
#include "chanwin.h"
#include "global.h"
#include "script.h"
#include "parser.h"
/*#include "ignore.h"*/
#include "dcc.h"
#include "setting.h"

#include "cmd.h"

FD_scriptcontrol *script_form;
extern Winstruct *Win;
static int event_id=0; 
static int add_numeric(pNumHook *, int);
/*static int del_numeric(pNumHook *, NumHook *);*/
static void remove_script_numhooks(int , NumHook * );/* for all numerics*/
static void remove_hook(NumHook *h, int sp);/*for one numeric*/


typedef struct TMsgType{
	MsgNr nr;
	char *name;
}MsgType;
static MsgType msg_type[]={
/* keep names sorted*/
{ACTION, "ACTION" },
{CHATMSG,"CHATMSG"},
{CLIENTINFO,"CLIENTINFO"},
{CTCP_ALL,"CTCP_ALL"},
{CTCP_HELP,"CTCP_HELP"},
{CTCP_PING,"CTCP_PING"},
{CTCP_REPLY,"CTCP_REPLY"},
{CTCP_TIME,"CTCP_TIME"},
{CTCP_UNKNOWN,"CTCP_UNKNOWN"},
{DCC_CHAT_REQUEST,"DCC_CHAT_REQUEST"},
{DCC_GET_DONE,"DCC_GET_DONE"},
{DCC_SEND_DONE,"DCC_SEND_DONE"},
{DCC_SEND_REQUEST,"DCC_SEND_REQUEST"},
{ERROR,"ERROR"},
{CTCP_FINGER,"FINGER"},
{INVITE,"INVITE" },
{JOIN,"JOIN" },
{JUNK,"JUNK" },
{KICK,"KICK" },
{MODE,"MODE" },
{NICK,"NICK"},
{NOTE,"NOTE" },
{PART,"PART" },
{PING,"PING"},
{PONG,"PONG"},
{PRIVMSG,"PRIVMSG" },
{PRIVNOTICE,"PRIVNOTICE" },
{PUBMSG,"PUBMSG"},
{PUBNOTICE,"PUBNOTICE" },
{QUIT,"QUIT"},
{SED, "SED"},
{SILENCE, "SILENCE"},
{TOPIC,"TOPIC"},
{USERINFO,"USERINFO"},
{VERSION,"VERSION"},
{WALLOPS,"WALLOPS"} 
};

extern void event_setup(void){
MsgNr event[]={
ACTION, CHATMSG,CLIENTINFO,CTCP_ALL,CTCP_HELP,CTCP_PING,CTCP_REPLY,
CTCP_TIME, CTCP_UNKNOWN,DCC_CHAT_REQUEST,DCC_GET_DONE,DCC_SEND_DONE,
DCC_SEND_REQUEST,ERROR,CTCP_FINGER,INVITE ,JOIN ,JUNK ,KICK ,MODE ,
NICK, NOTE ,PART ,PING,PONG,PRIVMSG ,PRIVNOTICE ,PUBMSG,PUBNOTICE ,
QUIT, SED, SILENCE,TOPIC,USERINFO,VERSION,WALLOPS,INVALID},
*p=event;
	int i=(int)(*p);
	
	MAX_ON=sizeof(event)/sizeof(MsgNr)-1;/* -INVALID*/

	want=(Want *)malloc(sizeof(Want)*MAX_ON);
	if (!want)
		err_sys("%s %d error", __FILE__, __LINE__);
	while(*p!=INVALID && i<MAX_ON){
		want[i].type=*p;
		want[i].start=want[i].end=NULL;
		i=(int)(*++p);
	}
}

static void sp_cb(int fd, void *data)
{
	/* this has to be really big since we have to get all script output
	in one go to avoid losing win id
	*/
	
	char 	line[SCRIPT_RD_BUF+1];
	int n;
	static void parse_sp(int fd, char *line);
	
	if( (n=read(fd, line, SCRIPT_RD_BUF) )<0 ){
		 if(errno!=EAGAIN){
		 err_ret("read error from script %d", fd);
		 n=remove_sp(fd);
		 assert(!n);
		 }
	}else
	if(n==0){/* connection closed */
		n=remove_sp(fd);
		assert(n==0);
	}else{
	line[n]='\0';
	parse_sp(fd, line);
	}
}


extern  MsgNr get_type(const char *var){
	MsgType *p=msg_type;
	register int low, high, mid, cond;

	low=0; high=MAX_ON;
	while(low<=high){
		mid=(low+high)/2;
		if( (cond=strcasecmp(var, (p+mid)->name))<0 )
			high=mid-1;
		else if(cond>0)
			low=mid+1;
		else
			return (p+mid)->nr;
	}
	return INVALID;
}

void *lookup_type(const char *cmdname, int *is_numeric){
/* on return, flag==0 if type is  not numeric; */
 	static int num;
 	char *s;
 	*is_numeric=0;	/* is not numeric */
 	num=(int)strtol(cmdname, &s, 0);
	if(*s){
 		static MsgNr type=INVALID;
		type=get_type(cmdname);
		return ((void *)&type);
	}
	else{
		*is_numeric=1;
		if(num<1) return NULL;
		else return ((void *)&num);
	}
}

void list_hooks(int do_list){
	static void list_numhooks(NumHook *h,FL_OBJECT *brow);
	int i;
	
	char *buf;
	FL_OBJECT *brow;
	
	if(!script_form) return;
	buf=malloc(sizeof(char)*513);
	if(buf==NULL){err_ret("Error"); return;}
	brow=do_list?((FD_event *)script_form->folder->u_vdata)->browser:
	((FD_script *)script_form->folder->u_ldata)->browser;
	fl_clear_browser(brow);
	fl_freeze_form(script_form->scriptcontrol);
	
	if(do_list){
	/*hooks*/
	   On *p;
	   sprintf(buf,"@N@m%-14.18s ID  %-10s Sock %s ","Command","SN","Pattern" );
	   fl_addto_browser(brow, buf);
	   for(i=0; i<MAX_ON;i++){
		p=want[i].start;
		while(p!=want[i].end){
			*buf=0;
			sprintf(buf,
			"%s%-19.17s %-5d %-12d %-6d %s", 
			(p->flag&HK_SUSPENDED || 
			  (p->type&CMD_FD &&
			   s_pipe[p->cmd.sp].status&(SCR_SUSPENDED|SCR_STOPPED)
			  )
			)? "@C4":"",
			msg_type[i].name,
			p->id,p->ser_number, 
			(p->type&CMD_FD)?p->cmd.sp:(-1),
			p->pattern);
			fl_addto_browser(brow, buf);
			p=p->next;
		}
	   }
	   free(buf);
	   list_numhooks(numTree, brow);
	}
	else{
	/* scripts*/
	   for(i=0; i<sp_count;i++){
		if(s_pipe[i].fd<0) continue;
		sprintf(buf, "%s%s %d", 
			s_pipe[i].status&SCR_SUSPENDED?"@C4":
			(s_pipe[i].status&SCR_STOPPED?
				"@i":""),
			s_pipe[i].sys_cmd, i);
		fl_addto_browser(brow, buf);
		}
	   free(buf);
	}
	fl_unfreeze_form(script_form->scriptcontrol);
} 
static void list_numhooks(NumHook *h, FL_OBJECT *brow){
	if(h){
		On *p;
		char *buf;
		list_numhooks(h->left, brow);
		 buf=malloc(sizeof(char)*513);
		if(!buf) return;
		p=h->start;
		while(p!=h->end){
			sprintf(buf,
			"%s%-19.3d %-5d %-12d %-6d %s", 
			(p->flag&HK_SUSPENDED || 
			  (p->type&CMD_FD &&
			   s_pipe[p->cmd.sp].status&(SCR_SUSPENDED|SCR_STOPPED)
			  )
			)? "@C4":"",
			h->numeric,
			p->id,p->ser_number, 
			(p->type&CMD_FD)?p->cmd.sp:(-1),
			p->pattern);
		   fl_addto_browser(brow, buf);
		   p=p->next;
		}
		free(buf);
		list_numhooks(h->right, brow);
	}
}
static void remove_script_numhooks(int sp, NumHook *h ){
/* remove script from all numeric lists
h=numTree
 */
	if(h){
		remove_script_numhooks(sp, h->left);
		remove_hook(h, sp);
		remove_script_numhooks(sp, h->right);
	}
}
static void remove_hook(NumHook *h, int sp){
/* remove script from  a numeric's list */
	On *p;
	if(!h) return;
	p=h->start;
	while(p!=h->end)
		if(p->cmd.sp==sp){
			On *q=p->next;
			free(p->pattern);
			if(q==h->end) h->end=p; else *p=*q;
			free(q);
			continue;
		}else
		p=p->next;
	
	#if 0
	#warning to fix bug and remove numeric (maybe it`s better like this?)
	//better
	if(h->end==h->start){
		if(gflags&VERBOSE_CLIENT)
		fprintf(stderr,"removing this numeric: %03d (no events)\n",h->numeric);
	 	del_numeric(&numTree, h);
	 }
	 #endif

}

typedef struct TCtrlType{
	int type;
	char *name;
} CtrlType;
#define sc_CONTINUE	(0)
#define sc_CMD		(1)
#define sc_CTCP		(2)
#define sc_EVENT	(3)
#define sc_REVENT	(4)
#define sc_SIGCONT	(5)
#define sc_SLEEPING	(6)

CtrlType ctrltype[]={
	{sc_CMD, "_CMD_"},
	{sc_CONTINUE,"_CONTINUE_"},
	{sc_CTCP, "_CTCP_"},
	{sc_EVENT,"_EVENT_"},
	{sc_REVENT,"_REVENT_"},
	{sc_SIGCONT, "_SIGCONT_"},
	{sc_SLEEPING, "_SLEEPING_"}
};
static int nctrltypes=sizeof(ctrltype)/sizeof(CtrlType);
static int lookup_ctrl(const char *name){
	CtrlType *p=ctrltype;
         register int low, high, mid, cond;
         low = 0; high = nctrltypes - 1;
         while (low <= high) {
         	mid = (low + high) / 2;
         	if ((cond = strcasecmp(name,(p + mid)->name)) < 0)
         	high = mid - 1;
         	else if (cond > 0) low = mid + 1;
         	else  return (p + mid)->type;
         }
         return -1;
}

#define RM_BY_ID	0
#define RM_BY_SNR	1
static void rm_event(int id, int fd, int flag);
static void add_type(int fd,const char *inp, int type);
static void parse_sp(int fd, char *inp){
	char *cmd;
	char   *buf = (char *) inp;
	int ctrl_cmd;
	

	if(!buf || !(*buf)) return;	/* dont remove this line!*/
	cmd=nextstr(buf,1);
	if(!cmd) return;
   if(*cmd=='_'){	/* quick check.. */
   	char msg[45], refresh_display=1;
	int i=strlen(cmd)-1;
	if(*(cmd+i)=='\n') *(cmd+i)='\0';
	if( (ctrl_cmd=lookup_ctrl(cmd))<0) goto weiter;
	msg[0]='\0';
   	switch(ctrl_cmd){
	case sc_CONTINUE:
	{
		for(i=0;i<sp_count;i++)
			if(s_pipe[i].fd==fd){
			 	if((s_pipe[i].status & SCR_SLEEPING)){
				s_pipe[i].status &= ~SCR_SLEEPING;
				sprintf(msg,"Process %d now ready", s_pipe[i].pid);
				}
				break;
			 }
		break;
	}
	case sc_SIGCONT:
	{
		for(i=0;i<sp_count;i++)
			if(s_pipe[i].fd==fd){
			 	if(s_pipe[i].status & SCR_STOPPED){
				s_pipe[i].status &= ~SCR_STOPPED;
				sprintf(msg,"Process %d continued",s_pipe[i].pid);
				}
				break;
			 }
		break;
	}
	case sc_SLEEPING:
	{
		for(i=0;i<sp_count;i++)
			if(s_pipe[i].fd==fd){
			 	if(!(s_pipe[i].status & SCR_SLEEPING)){
				s_pipe[i].status |= SCR_SLEEPING;
				sprintf(msg,"Process %d going to sleep",
				s_pipe[i].pid);
				}
			 	break;
			 }
		break;
	}
	case sc_REVENT:
	{
		if(sscanf(inp, "%*s %d", &i)>0)
			rm_event(i, fd, RM_BY_SNR);
		break;
	}
	case sc_EVENT:
	case sc_CTCP:
	case sc_CMD:
	  if(nextstr(buf,2)==NULL)
			fl_addto_browser(main_form->browser, inp);
	  else{
		char *ptr;
		if (!(ptr= strxstr(buf, "\n")))	/* strtok won't work */
			ptr = buf;
		while (ptr) {
			add_type(fd, ptr, ctrl_cmd);
			buf += strlen(ptr);
			if (*buf != '\n')
				break;
			else
				buf++;
			ptr = strxstr(buf, "\n");
			if (!ptr)
				ptr = buf;
		}
	    }
	    break;
	default:
		 sprintf(msg, "Error: unimplemented cmd %#X",ctrl_cmd);
		refresh_display=0;
		break;
       }/* swicth ctrl_cmd*/
       if(msg[0])
       	if(script_form){
		fit_object_label(script_form->status,msg);
		if(refresh_display)
		   {list_hooks(1); list_hooks(0);}
	}
	#if DEBUG
       	else fprintf(stderr,"%s\n",msg); 
	#endif
       return;
      weiter:
   }/* if(*cmd=='_')*/
   else{
		char *s=0;
		int w;
		rmstr(&buf, 1);
		w=strtol(cmd, &s, 0);
		if(*s || w<0 || !buf)
			fl_addto_browser/*_chars*/(main_form->browser, inp);
		else if(!(s=nextstr(buf,1)) || strcasecmp(s, "/reply"))
			send_blocktxt(w, buf, 0);
		else{
			int n=strlen(buf)-1;
			char *tmp = (char *) malloc(sizeof(char) * 
			(strlen(buf) + 8)+strlen(s));
			if (!tmp) {
				err_ret("%s %d ", __FILE__, __LINE__);
				return;
			}
			/* /reply xtr SFINGER @iLogin: tano*/
			if(*(buf+n)=='\n') *(buf+n)='\0';
			cmd=nextstr(buf,2);/* nick*/
			if(cmd){
				n=find_pos(buf,3);
				if(n<0) return; else buf=buf+n;
			} else 	return;
			if(!(*buf)) return;
			quote_str(&buf);
			sprintf(tmp,"%s %s %s",s, cmd, buf);
		 	parse_input(w, tmp, 0);
			free(tmp);
		}
		return;
	}
}


static void add_type(int fd,const char *inp, int ctrltype){
	/*int is_normal=1;*/
	char *str=(char *)inp;
	char *cmd, *cmdname;
	int i;
	int type;
	if(!str || !(*str)) return;
	cmd=nextstr(str,1);
	cmdname=nextstr(str,2);
	if(!cmdname) return;
	
	if( (type=lookup_ctrl(cmd))<0) return;
	if( type==sc_CMD || type==sc_CTCP){
		
		/*rmstr(&str,1);rmstr(&str,1);*/
		for(i=0; i<sp_count;i++)
		if(s_pipe[i].fd==fd){
			S_Pipe *p=&s_pipe[i];
			int flag;
			Cmd *cm;
			flag=type==sc_CMD?CMD_NORMAL:CMD_CTCP;
				
			if(!(cm=find_cmd(cmdname,flag|CMD_FD))){
				Cmd c;
				spCmd *s;
				
				s=malloc(sizeof(spCmd));
				if(!s){ err_ret("%s %d", __FILE__, __LINE__);
	    			    errno=0;
				    return;
				}
			       	s->name=strdup(cmdname);
				s->next=p->cmd;
				p->cmd=s;
				c.name=	p->cmd->name;
				c.type=CMD_FD|flag;
				c.help=(char *)malloc(sizeof(char)*strlen(str));
				if(!c.help ||
				sscanf(str, "%*s %*s %[^\n]\n", c.help)<1)
					c.help=NULL;
				c.cmd.sp=i;
				if(gflags&VERBOSE_CLIENT)
					fprintf(stderr,"+%s:%s %d\n", c.type&CMD_CTCP?"ctcp":"",
					s_pipe[c.cmd.sp].cmd->name,no_of_cmds);
				insert_cmd(&cmdTree, &c, flag);
					/*free(tmp); free(str);free(cmdname);--don't*/
			} /* if(!find..)*/
			else{
				fprintf(stderr,"[ignored] \"%s\" exists:"
				"%s:%s\n",cmdname,cm->name,cm->help?cm->help:"no help");
			}
			return;
		}/* if s_pipe..*/
	}/* is is_normal..*/
	else
   {
	cmdname=nextstr(str,3);
	if(!cmdname) return;
	if(type==sc_EVENT){
		
		for(i=0; i<sp_count;i++)
		if(s_pipe[i].fd==fd){
			On w;
			int num, flag;
			void *type;
			
			if(sscanf(str, "%*s %d", &w.ser_number)<1)
				return;
			w.pattern=(char *)malloc(sizeof(char)*strlen(str));
			if(!w.pattern) return;
			w.flag= HK_RESET;
			if(sscanf(str, "%*s %*d %*s %d", &flag)<1){
				if(sscanf(str, "%*s %*d %*s %[^\n]\n", w.pattern)<1)
				strcpy(w.pattern,"*");
			}else{
				if(flag) w.flag |= HK_EXCLUSIVE;
				if(sscanf(str, "%*s %*d %*s %*d %[^\n]\n", w.pattern)<1)
				strcpy(w.pattern,"*");
			}
			w.type=CMD_FD;
			w.cmd.sp=i;
			w.id=-1;
			type=lookup_type(cmdname, &num);
			if(!num){
			 	MsgNr t=*((MsgNr *)type);
			 	if(t!=INVALID){
					add_hook(t, &w);
					if(script_form) list_hooks(1);
				}
			 	else fprintf(stderr,
			 	"%s (%d) Unknown message type:%s\n",__FILE__,__LINE__, cmdname);
			}else{
				num=*((int *)type);
				if(num>0){
				add_numhook(num, &w);
				if(script_form) list_hooks(1);
				}
				#if DEBUG>1
				else
				fprintf(stderr,"%s (%d) Negative numeric (%d)? ignoring..\n",
					__FILE__,__LINE__,num);
				#endif
			}
			free(w.pattern);
			return;
		}/* if s_pipe..*/
	}
  }/* else*/
}

static jmp_buf	env;
static void rm_event(int id, int fd, int flag){
/* remove 1 event*/
	static void rm_event_numhooks(int snr, int sp, NumHook *h, int flag );
	int j, do_it=0;
	volatile int i;
	S_Pipe *p;
	
	assert(fd!=-1); /* error/bug*/
	for(i=0;i<sp_count;i++)
		if(s_pipe[i].fd==fd){
			do_it=1; 
			break;
		}
	if(!do_it) return;
	p=&s_pipe[i];
	for(j=0; j<MAX_ON;j++){
		On *q=want[j].start;
		while(q!=want[j].end)
			if( q->type&CMD_FD && q->cmd.sp==i && 
			(flag==RM_BY_ID?(q->id==id):(q->ser_number==id))
			){
				On *r=q->next;
				free(q->pattern);
				if(r==want[j].end) want[j].end=q; else *q=*r;
				free(r);
				return;
			}
			else q=q->next;
	}
	if(setjmp(env) != 0)/* in order to jump out of the numerics tree*/ 
		return; 
	rm_event_numhooks(id,i, numTree, flag);
}
static void rm_event_numhooks(int id, int sp, NumHook *h, int flag ){
/* remove numeric event */
	if(h){
		On *p;
		rm_event_numhooks(id,sp, h->left,flag);
		p=h->start;
		while(p!=h->end)
		if(p->cmd.sp==sp && (flag==RM_BY_ID?(p->id==id):(p->ser_number==id))
		){
			On *q=p->next;
			free(p->pattern);
			if(q==h->end) h->end=p; else *p=*q;
			free(q);
			longjmp(env, 1);
		}else
		p=p->next;
		rm_event_numhooks(id,sp, h->right, flag);
	}
}

extern int remove_sp(int fd){
/* remove a script*/
	int i,j, do_it=0;
	S_Pipe *p;
	
	if(fd==-1) return 0; /* error/bug*/
	for(i=0;i<sp_count;i++)
		if(s_pipe[i].fd==fd){
			do_it=1; /* or indent for ever!*/
			break;
		}
	if(!do_it) return (-1);
	fl_remove_io_callback(fd, FL_READ, sp_cb);
	p=&s_pipe[i];
	close(p->fd);
	p->fd=-1;
	free(p->sys_cmd);
	while(p->cmd!=NULL){ /* remove cmds*/
		remove_cmd(p->cmd->name,CMD_FD);
		p->cmd=p->cmd->next;
	}
	/* remove message hooks*/
	for(j=0; j<MAX_ON;j++){
		On *q=want[j].start;
		while(q!=want[j].end){
			if( (q->type&CMD_FD) && q->cmd.sp==i){
				On *r=q->next;
				free(q->pattern);
				if(r==want[j].end) want[j].end=q; else *q=*r;
				free(r);
				continue;
			}
			else q=q->next;
		}
	}
	remove_script_numhooks(i, numTree);
	/*
	 hopefully SIGCHLD will prevent zombie
	*/
	if(script_form) {list_hooks(1); list_hooks(0);}
	return 0;
}


static void parse_args(char *buf, char **args){
	int count=0;
	while(*buf!='\0' && count<SCRIPT_ARGC){
		while( *buf==' ' || *buf=='\t' || *buf=='\n') *buf++='\0';
		*args++=buf; count++;
		while( *buf!='\0' && *buf!=' ' && *buf!='\t' && *buf!='\n') buf++;
	}
	*args='\0';
}


static int sp_allocate(void){
	int i;
	if(s_pipe==NULL)
		s_pipe=malloc(sizeof(S_Pipe)*SP_ALLOCATE);
	else
		s_pipe=realloc(s_pipe, (sp_count+SP_ALLOCATE)*sizeof(S_Pipe));
	if(s_pipe==NULL) return (-1);
	for (i=sp_count;i< sp_count+SP_ALLOCATE; i++)
		s_pipe[i].fd=-1;
	sp_count+=SP_ALLOCATE ;
	return (0);
}

void script_start(const char *prog){
	int	fd[2];
	pid_t	pid;
	int i;
	char *tmp, *errorstr=NULL;

	if(!prog || !(*prog)) return;
	tmp=strrchr(prog, '/');
	if(tmp){ 
		tmp++;
		if(!(*tmp)) return;
	}else tmp=(char *)prog;
	for(i=0;i<sp_count;i++)
		if(s_pipe[i].fd!=-1){
			char *pp=strrchr(s_pipe[i].sys_cmd, '/');
			if(!pp) pp=s_pipe[i].sys_cmd;
			else pp++;
			if(!strcmp(pp, tmp)){
				errorstr=malloc(sizeof(char)*(strlen(tmp)+120));
				sprintf(errorstr,
				"Program \"%s\" is already running (PID %d)"
				". Shut it down first.",tmp,s_pipe[i].pid);
				if(script_form)
				fit_object_label(script_form->status, errorstr);
				
				fl_ringbell(0);
				return;
			}
		}
	if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)<0){
		err_ret("socketpair error");
		return;
	}
	TELL_WAIT();
	if( (pid=fork())<0)
  	  {err_ret("@ifork error");return;}
	else if(pid>0){
		close(fd[1]);	/*stdin */
		if(!s_pipe && sp_allocate()) /* ran out of mem 
		kill child*/
			goto kill;
	retry:
		
		for(i=0;i<sp_count;i++){
			if(s_pipe[i].fd==-1){
				s_pipe[i].fd=fd[0];
				s_pipe[i].pid=pid;
				s_pipe[i].sys_cmd=strdup(prog);
				s_pipe[i].cmd=NULL; 
				s_pipe[i].status=SCR_RESET;
				if (fcntl(s_pipe[i].fd, F_SETFL, O_NONBLOCK) == -1)
					err_ret("fcntl error.Will block");
				fl_add_io_callback(s_pipe[i].fd, FL_READ, sp_cb, (void *)0);
				TELL_CHILD(pid);
				list_hooks(0);
				return;
			}
		}
		if(sp_allocate()){ /* out of men?*/
			err_ret("%s %d error",__FILE__,__LINE__);
			goto kill;
		}
		goto retry;
	kill:
		TELL_CHILD(pid);
		if(kill(pid, SIGKILL)<0) err_ret("@ikill error");
	   }
	   else{	/*child process*/
	   	/* DON'T USE berr_XXX in here!!! use err_XXX. stderr is 
		now 'connected' to the control window  */
		char dup_err=1;
		char *argv[SCRIPT_ARGC+1];
		#if 0
		sigset_t newset;
		sigemptyset(&newset);
		sigaddset(&newset, SIGCONT);
		if(sigprocmask(SIG_UNBLOCK, &newset, NULL)<0)
			err_....;
		#endif
	       	WAIT_PARENT();
		close(fd[0]);
		if(fd[1] != STDIN_FILENO)
		  if(dup2(fd[1], STDIN_FILENO) != STDIN_FILENO)
			 goto bye;
		if(fd[1] != STDOUT_FILENO)
		  if(dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)
			 goto bye;
		if(fd[1] != STDERR_FILENO)
		  if(dup2(fd[1], STDERR_FILENO) != STDERR_FILENO)
			 goto bye;
		parse_args((char *)prog, argv);
		if(execvp(*argv, argv) <0) {dup_err=0;goto bye;}
		
		exit(-1);
	bye:
		fprintf(stderr,"%s: %s",dup_err?"dup2": *argv,strerror(errno));
		exit(-1);
	     }
}

/*********************** h o o k s *******************************************/


void add_hook(MsgNr i,On *what){
/* add message hook script*/
	On *p;

	if(want[i].start==want[i].end && want[i].start==NULL){
		want[i].start=want[i].end=malloc(sizeof(On));
		if(want[i].start==NULL){
			perror("error");
			return;
		}
		want[i].start->flag= HK_RESET;
	}
	p=want[i].start;
	while(p!=want[i].end){
		int cmp=strcasecmp(p->pattern, what->pattern);

		if
		(p->type==what->type
		&&
		 (
		    (what->type==CMD_IN && p->ser_number==what->ser_number
		        && (p->ser_number==0 || !cmp)
		    ) ||	   
		     
		    (!cmp && what->type==CMD_FD && s_pipe[p->cmd.sp].fd==
		         s_pipe[what->cmd.sp].fd && p->ser_number==what->ser_number
		    )
		   )
		)
		  {   /* replace already existing entry, dont want duplicate patterns*/
			#if DEBUG
			fprintf(stderr,"replace->s_nr:%d type:%d,pattern:%s\n",
			p->ser_number,	p->type,p->pattern);
			#endif
			if(cmp){
				free(p->pattern);
				p->pattern=strdup(what->pattern);
			}
			if(p->type==CMD_IN){
				free(p->cmd.cmd);
				p->cmd.cmd=strdup(what->cmd.cmd);
			}
			p->flag=what->flag;
			return;
		  }
		else
		p=p->next;
	}
	if(want[i].start==want[i].end
	|| (what->type==CMD_FD && want[i].start->type==CMD_FD &&
	   !(want[i].start->flag & HK_EXCLUSIVE))

  	|| (what->type==CMD_IN && want[i].start->type==CMD_IN &&
	want[i].start->ser_number==0) /* for this type,
		 handle serial nos first since check_msg() wont get to them
		 when we encounter  default action ( ser_no==0) */
	 ){ 
		/* place on the top of stack */

		p=want[i].start;
		want[i].start=malloc(sizeof(On));
		if(!want[i].start) err_sys("error");
		/* *want[i].start=*what; no; what->pattern is freed on return*/
		want[i].start->flag=what->flag;
		want[i].start->type=what->type;
		
		if(what->type==CMD_IN)
			want[i].start->cmd.cmd=strdup(what->cmd.cmd);
		else
			want[i].start->cmd.sp=what->cmd.sp;
		want[i].start->ser_number=what->ser_number;
		want[i].start->pattern=strdup(what->pattern);
		want[i].start->id=++event_id;
		want[i].start->next=p;
	}else{
		/* place as 2nd */
		p=malloc(sizeof(On));
		p->flag=what->flag;
		p->type=what->type;
		if(what->type==CMD_IN)
			p->cmd.cmd=strdup(what->cmd.cmd);
		else
			p->cmd.sp=what->cmd.sp;
		p->pattern=strdup(what->pattern);
		p->ser_number=what->ser_number;
		p->id=++event_id;
		p->next= want[i].start->next;
		want[i].start->next=p;
	}
}
		 

/* add num. hook */

void add_numhook(int numeric, On *what){
	NumHook *N;
	On *p;

	N=find_numhook(numeric);
	if(!N){
		add_numeric(&numTree, numeric);
		N=find_numhook(numeric);
		assert(N);
	}
	p=N->start;
	while(p!=N->end){
		int cmp=strcasecmp(p->pattern, what->pattern);
		if
		(p->type==what->type
		&&
		 (
		    (what->type==CMD_IN && p->ser_number==what->ser_number
		        && (p->ser_number==0 || !cmp)
		    ) ||	   
		     
		    (!cmp && what->type==CMD_FD && s_pipe[p->cmd.sp].fd==
		         s_pipe[what->cmd.sp].fd && p->ser_number==what->ser_number
		    )
		   )
		  )
		{
			/* pattern exists already; just change it*/
			if(cmp){
				free(p->pattern);
				p->pattern=strdup(what->pattern);
			}
			if(p->type==CMD_IN){
				free(p->cmd.cmd);
				p->cmd.cmd=strdup(what->cmd.cmd);
			}
			p->flag=what->flag;
			return;
		}
		else 	p=p->next;
	}
	/* not checkin for memory failure!!!!*/	
	if(N->start==N->end
	|| (what->type==CMD_IN && N->start->ser_number==0)
	|| (what->type==CMD_FD && !(N->start->flag&HK_EXCLUSIVE))
	){ 
		/* what goes to top */
		p=N->start;
		N->start=malloc(sizeof(On));
		N->start->flag=what->flag;
		N->start->type=what->type;
		if(what->type==CMD_IN)
		N->start->cmd.cmd=what->cmd.cmd?strdup(what->cmd.cmd):NULL;
		else N->start->cmd.sp=what->cmd.sp;
		N->start->pattern=strdup(what->pattern);
		N->start->ser_number=what->ser_number;
		N->start->id=++event_id;
		N->start->next=p;

	}else{/* w should be 2nd on the list*/
		p=malloc(sizeof(On));
		p->flag=what->flag;
		p->type=what->type;
		if(p->type==CMD_IN)
		p->cmd.cmd=what->cmd.cmd?strdup(what->cmd.cmd):NULL;
		else p->cmd.sp=what->cmd.sp;
		p->pattern=strdup(what->pattern);
		p->ser_number=what->ser_number;
		p->id=++event_id;
		p->next= N->start->next;
		N->start->next=p;
		
	}
return;
}

/*

FOR SERVER NUMERICS

*/
NumHook *find_numhook(int numeric)
{
	NumHook *p=numTree;

	while (p) {
		if(p->numeric==numeric) return p;
		else if(p->numeric>numeric) p=p->left;
		else p=p->right;
	}
	return NULL;
}

static void num_left_rotate(pNumHook *pp){
	pNumHook	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 num_right_rotate(pNumHook *pp){
	pNumHook 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;
}

static int add_numeric(pNumHook *pp, int numeric){

	int dH=0;
	pNumHook p=*pp;
	
	
	if(p==NULL){
		
		*pp=p=(pNumHook)malloc(sizeof(NumHook));
		if(!p){ err_sys("(%s %d) Error",__FILE__, __LINE__ ); }
		p->numeric=numeric;
		p->start=p->end=(On *)malloc(sizeof(On));
		if(!p->start){ err_sys("(%s %d) Error",__FILE__, __LINE__ ); }
		p->bf=0;
		p->left=p->right=NULL;
		dH=1;
	}else
{
	if(   numeric >p->numeric ){
	   if(add_numeric(&p->right, numeric)){
		p->bf++; 
		if(p->bf==1) dH=1; else
		if(p->bf==2){
			if(p->right->bf==-1) num_right_rotate(&p->right);
			num_left_rotate(pp);
		}
	   }
	}else
	if(  numeric<p->numeric ){
	   if(add_numeric(&p->left, numeric)){
	   	p->bf--;
		if(p->bf==-1) dH=1;else
		if(p->bf==-2){
			if(p->left->bf==1) num_left_rotate(&p->left);
			num_right_rotate(pp);
		}
	  }
	}
	else{/* numeric already in the list*/



	}

}
	return dH;
}
#if 0
static int del_numeric(pNumHook *pp, NumHook *c){
/*remove numeric if no more hooks exist for it
now unused
*/
	pNumHook p=*pp, *q;
	int dH=0;
	
	if(!p) return 0;
	if( 
	    c->numeric<p->numeric
	   ){
	   if(del_numeric(&p->left, c)){
	  	p->bf++;
		if(p->bf == 0) dH=1;else
		if(p->bf == 2){
			if(p->right->bf==-1) num_right_rotate(&p->right);
			num_left_rotate(pp);
			if(p->bf==0) dH=1;
		}
	   }
	}else
	if( 
		c->numeric>p->numeric
	   ){
	   if(del_numeric(&p->right, c)){
	   	p->bf--;
		if(p->bf == 0) dH=1;else
		if(p->bf == -2){
			if(p->left->bf==1) num_left_rotate(&p->left);
			num_right_rotate(pp);
			if(p->bf==0) dH=1;
		}
	   }
	}else{ /* found numeric */
		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->numeric=(*q)->numeric;
			p->start=(*q)->start;
			p->end=(*q)->end;
			(*q)->numeric= c->numeric;
			(*q)->start=c->start;
			(*q)->end=c->end;
			if(del_numeric(&p->left, c)){
				p->bf++;
				if(p->bf==0) dH=1;else
				if(p->bf==2){
					if(p->right->bf==-1) 
						num_right_rotate(&p->right);
					num_left_rotate(pp); dH=1;
				}
			}
		}
	}
	return dH;
}
#endif
static int close_script_form(FL_FORM * form, void *junk)
{
       /*	FD_event *e=(FD_event *)script_form->folder->u_vdata;
	FD_script *s=(FD_script *)script_form->folder->u_ldata;  */
	fl_hide_form(fl_get_active_folder(script_form->folder));
	fl_free_form(((FD_script *)script_form->folder->u_ldata)->script);
	fl_free_form(((FD_event *)script_form->folder->u_vdata)->event);
	fl_hide_form(script_form->scriptcontrol);
	fl_free_form(script_form->scriptcontrol);
	script_form=NULL;
	return FL_OK;
}

void do_script(void){

	if(script_form) {
		fl_raise_form(script_form->scriptcontrol);
		return;
	}else{
	FD_event *eventform = create_form_event();
	FD_script *scriptform = create_form_script();

	script_form= create_form_scriptcontrol();
	fl_set_object_bw(script_form->folder, -2);
	fl_set_browser_fontsize(eventform->browser, FL_NORMAL_SIZE);
	fl_set_browser_fontstyle(eventform->browser, FL_FIXED_STYLE);
	fl_set_browser_fontsize(scriptform->browser, FL_NORMAL_SIZE);
	fl_set_browser_fontstyle(scriptform->browser, FL_FIXED_STYLE);

	fl_set_form_atclose(script_form->scriptcontrol, close_script_form,
	 (void *) &script_form);
	fl_set_form_minsize(script_form->scriptcontrol,  560, 380);
	fl_set_form_maxsize(script_form->scriptcontrol,560,380);
	fl_addto_tabfolder(script_form->folder, "Scripts", scriptform->script);
	fl_addto_tabfolder(script_form->folder, "Events", eventform->event);
	/* hang "Event" form on to folder->u_vdata and "Scripts" form on to form->u_ldata
		since none of both forms is global
	*/
	fl_deactivate_object(eventform->info);
	fl_set_object_color(eventform->info, FL_INACTIVE_COL, FL_INACTIVE_COL);
	script_form->folder->u_vdata=(void *)eventform;
	script_form->folder->u_ldata=(long)scriptform;

	list_hooks(1);
	list_hooks(0);
	fl_show_form(script_form->scriptcontrol, FL_PLACE_CENTER|FL_FREE_SIZE,
	 FL_FULLBORDER,
	 sula_NAME sula_VERSION ": Event control (test)");

	fl_do_forms();
	}
}

typedef struct TEdit_arg{
	char is_numeric;
	On *on;
	union{
	MsgNr nr;
	int numeric;
	}cmd;
}Edit_arg;
static void edit_hook(FL_OBJECT * ob, void *data){
	static int busy=0;
	int i;
	FD_hook_edit *form;
	if(busy) return;
	busy=1;
	fl_set_cursor(script_form->scriptcontrol->window, XC_watch);
	fl_deactivate_form(script_form->scriptcontrol);
	form=create_form_hook_edit();
	form->close->u_vdata=(void *)&busy;
	form->pattern->u_ldata = form->action->u_ldata = form->s_nr->u_ldata =
	form->numeric->u_ldata = (long) XC_xterm;
	fl_set_object_prehandler(form->pattern, set_cursor);
	fl_set_object_prehandler(form->action, set_cursor);
	fl_set_object_prehandler(form->numeric, set_cursor);
	fl_set_object_prehandler(form->s_nr, set_cursor);
	fl_set_choice_fontstyle(form->type, FL_TIMES_STYLE);
	
	for(i=0;i<MAX_ON;i++)
		fl_addto_choice(form->type, msg_type[i].name);
	if(data){/* edit */
		Edit_arg *arg=(Edit_arg *)data;
		char nr[12];
		sprintf(nr,"%d",arg->on->ser_number);
		fl_set_input(form->s_nr, nr);
		fl_set_input(form->pattern, arg->on->pattern);
		
		if(arg->is_numeric) {
			sprintf(nr,"%d",arg->cmd.numeric);
			fl_set_input(form->numeric, nr);
			fl_addto_choice(form->type,"(ignore)");
			fl_set_choice_text(form->type,"(ignore)");
		}else fl_set_choice_text(form->type,msg_type[arg->cmd.nr].name);	
		form->ok->u_vdata=(void *)arg->on;
		if(arg->on->type==CMD_IN){
			fl_set_input(form->action,arg->on->cmd.cmd);
		}
		/* event defined by a script*/
		else{
			fl_set_object_label(form->status,
			s_pipe[arg->on->cmd.sp].sys_cmd);
		}
	}else{
	  fl_set_input(form->s_nr,"0");
	  fl_set_input(form->pattern,"*");
	  form->ok->u_vdata=NULL;
	}
	fl_set_form_atclose(form->hook_edit,dont_close_win,0);
	fl_set_form_minsize(form->hook_edit,355, 225);
	fl_show_form(form->hook_edit,FL_PLACE_CENTER|FL_FREE_SIZE,
	 FL_TRANSIENT, data?
	 sula_NAME sula_VERSION ": Edit event":
	 sula_NAME sula_VERSION ": Add event");
	while(fl_do_forms()!=form->close);
}

void hook_edit_cb(FL_OBJECT * ob, long data){
	FD_hook_edit *form=(FD_hook_edit *)ob->form->fdui;

	switch(data){
	case 0: /* okay*/
	{
		On w;
		char *buf;
		if(ob->u_vdata){
			w=*((On *)ob->u_vdata); /*edit*/
			/* ((On *)ob->u_vdata)->flag |= HK_SUSPENDED; */
		}else{
			w.flag= HK_RESET;
			w.type=CMD_IN;
			/*w.id=-1;*/
		}
		buf=(char *)fl_get_input(form->pattern);
		if(*buf) w.pattern=buf;
		else w.pattern="*";
		if(w.type==CMD_FD)
			w.cmd.sp=((On *)ob->u_vdata)->cmd.sp;
		if(w.type==CMD_IN){
			w.cmd.cmd=(char *)fl_get_input(form->action);
			if(!(*w.cmd.cmd)){
				w.cmd.cmd=":";
				fl_set_input(form->action, ":");
			}
		}
		buf=(char *)fl_get_input(form->s_nr);
		if(*buf) w.ser_number=atoi(buf);
		else w.ser_number=0;
		buf=(char *)fl_get_input(form->numeric);
		if(*buf) {
			int num=atoi(buf);
			if(num>0){
				add_numhook(num, &w);
				list_hooks(1);
			}else
			fl_set_object_label(form->status, "invalid numeric");
		}
		else{
			/*buf=fl_get_choice_text(form->type);*/
		   MsgNr type;
		          buf=(char *)fl_get_choice_text(form->type);
		   if(!(*buf)) break;
		   type=get_type(buf);
		   if(type!=INVALID){
		      add_hook(type, &w);
		      list_hooks(1);
		   }
		   else 
		   fl_set_object_label(form->status,"need message type or numeric");
		}
		break;
	}
	case 1: /* close*/
	   {
		*((int *)ob->u_vdata)=0;
		fl_hide_form(form->hook_edit);
		fl_free_form(form->hook_edit);
		fl_activate_form(script_form->scriptcontrol);
		fl_reset_cursor(script_form->scriptcontrol->window);
		break;
	  }
	case 2:/*message type*/
	{
		fl_set_input(form->numeric,"");
		break;
	}
	}/*switch*/
}


void script_cb(FL_OBJECT * ob, long data){
    switch(data){
	/*todo: data<20 -->script */
	case 10:	/* run*/
	{
		char *fname;
		fl_set_cursor(script_form->scriptcontrol->window, XC_watch);
		fl_deactivate_form(script_form->scriptcontrol);
		fname = getstr2("Enter shell command: ",
			    sula_NAME sula_VERSION ": Run a program", NULL, 1);
		if (fname)
			script_start(fname);
		   fl_activate_form(script_form->scriptcontrol);
		   fl_reset_cursor(script_form->scriptcontrol->window);
		    return;
	   }
	case 11: /* script suspend (not sigstop)*/
	case 17: /*script stop, sigstop */
	case 18: /* continue/resume*/
	case 12: /* script kill->sigkill*/
	{
	  FD_script *form=(FD_script *)script_form->folder->u_ldata;
	  int browser_line=fl_get_browser(form->browser);
	  if(browser_line<1)
	     		fit_object_label(script_form->status, "make a selection first");
 	  else{
		
		char *buf, refresh_display=0;
		int sock;
	    	buf=(char *)fl_get_browser_line(form->browser, browser_line);
		if(!buf || !(*buf) || sscanf(buf,"%*s %d", &sock)<1)
			 return;
		if(sock<0) return; /* internal hook */
		assert(sock<sp_count && -1<s_pipe[sock].fd);
		#if DEBUG>2
		fprintf(stderr,"sock:%d ",sock);
		fprintf(stderr,"cmd:%s, pid=%d, fd=%d\n",
			s_pipe[sock].sys_cmd,s_pipe[sock].pid,s_pipe[sock].fd);
		#endif
		buf=malloc(sizeof(char)*120);
		buf[0]=0;
		switch(data){
			case 11:
				if(!(s_pipe[sock].status&SCR_SUSPENDED)){
				s_pipe[sock].status |=SCR_SUSPENDED;
				sprintf(buf,"Process %d suspended. New status %#X",
				   	s_pipe[sock].pid,s_pipe[sock].status);
				refresh_display=1;
				}
				break;
			case 17:
			       if(!(s_pipe[sock].status&SCR_STOPPED)){
			           if(kill(s_pipe[sock].pid,SIGSTOP)<0)
				     strncpy(buf,strerror(errno),58);
				     #if 0
				   else{
				   	sprintf(buf,"Sent stop signal to %d",
				   	s_pipe[sock].pid);
				   	/* let signal handler update*/
				   }
				     #endif
				}
				break;
			case 12:
				if(kill(s_pipe[sock].pid,SIGKILL)<0){
				   strncpy(buf,strerror(errno),58);
				}
				#if 0
				else{
				 sprintf(buf,"Sent SIGKILL to %d",s_pipe[sock].pid);
				 refresh_display=0;/* let SIGCHLD cause update*/
				 }
				 #endif
				break;
			case 18:
				if((s_pipe[sock].status&SCR_STOPPED)){
			           if(kill(s_pipe[sock].pid,SIGCONT)<0)
				     strncpy(buf,strerror(errno),58);
				   #if 0
				   else{
				   	sprintf(buf,"Sent signal to %d",
				   	s_pipe[sock].pid);
				   	/* let signal handler update*/
				   }
				   #endif
				}
				if((s_pipe[sock].status&SCR_SUSPENDED)){
				s_pipe[sock].status &= (~SCR_SUSPENDED);
				sprintf(buf,"Process %d resumed?. New status %#X",
				   	s_pipe[sock].pid,s_pipe[sock].status);
				refresh_display=1;
				}
				break;
		}/*switch data */
		if(*buf) fit_object_label(script_form->status,buf);
		if(refresh_display) {list_hooks(1);list_hooks(0);}
		free(buf);
		
	      }/*else*/
	  break;
	}
	/*data >20 -->event form*/
	case 23: /*info*/
	case 24:/*edit*/
	case 28:/* remove */
	case 27:/* pause */
	case 26:/*continue*/
	{
		FD_event *form=(FD_event *)script_form->folder->u_vdata;
		int n=fl_get_browser(form->browser);
	  	if(n<1)
	     		fit_object_label(script_form->status, "select first");
 	  	else{
			char *buf=(char *)fl_get_browser_line(form->browser, n);
			char *tmp;
			char *ptr;
			void *type;
			int id;
			On *p=NULL, **pp;
			Edit_arg args;
			
			if(!buf || sscanf(buf,"%*s %d", &id)!=1){ return;}
			tmp=nextstr(buf,1);
			/* kludge --to remove xforms format characters*/
			ptr=strrchr(tmp,'@');
			if(ptr /*&& ptr+2*/) 
				strcpy(tmp,*(ptr+1)=='C'?(ptr+3):(ptr+2));
			type=lookup_type(tmp, &n);
			if(!n){
			 	MsgNr t=*((MsgNr *)type);
				args.is_numeric=0;
				args.cmd.nr=t;
			 	if(t!=INVALID){
					p=want[t].start;
					while(p!=want[t].end)
						if(p->id==id) break;
						else p=p->next;
					if(p==want[t].end) p=NULL;
					pp=&want[t].end;
				}
			}else
			{
				NumHook *N=find_numhook(*((int *)type));
				assert(N);
				args.is_numeric=1;
				args.cmd.numeric=*((int *)type);
				p=N->start;
				while(p!=N->end)
					if(p->id==id) break;
					else p=p->next;
				if(p==N->end) p=NULL;
				pp=&N->end;
			}
			if(!p) break;  /* id not found */
			switch(data){
			case 23:/*info*/ break;
			case 24:
				args.on=p;
				edit_hook(ob,(void *)&args);
				break;
			case 27:
				if(p->flag & HK_SUSPENDED) break;
				p->flag |= HK_SUSPENDED;
				list_hooks(1);
				break;
			case 26:
				if(!(p->flag & HK_SUSPENDED))
				 break;
				p->flag &= ~HK_SUSPENDED; 
				list_hooks(1);
				break;
			case 28:/*remove*/
			{
				On *q=p->next;
				free(p->pattern);
				if(q== *pp) *pp=p;
				else *p=*q;
				free(q);
				list_hooks(1);
				break;
			}
			}/* switch*/

		}
	}
		break;
	case 25:{ /*new*/
		
		edit_hook(ob,NULL);
		/*fl_ringbell(0);
		fl_set_object_label(script_form->status, "not yet implemented");
		*/
		}
		break;
	case 6: /*done */
	{
		
		close_script_form(NULL,NULL);
		#if 0
		FD_event *e=(FD_event *)script_form->folder->u_vdata;
		FD_script *s=(FD_script *)script_form->folder->u_ldata;
		
		fl_hide_form(s->script);/*script form*/
		fl_free_form(s->script);/* script form*/
		fl_hide_form(e->event);/* event form*/
		fl_free_form(e->event);/* event form*/
		fl_hide_form(script_form->scriptcontrol);
		/**((FD_scriptcontrol **) ob->u_vdata) = NULL;*/
		fl_free_form(script_form->scriptcontrol);
		script_form=NULL;
		#endif
		   break;
	}
	case 8:/* update*/
		{list_hooks(1);list_hooks(0);}
		break;
	default:   break;
    }
}


void do_internal_hook(On *h , const char *msg, int w){
#define BUFF	1023
	char *cmd;
	char *str;
	char buf[BUFF+1];
	char *ptr=buf, *pp;
	char *buffer;
	
	assert(h->type==CMD_IN);
	if(!strcmp(h->cmd.cmd,":")) return;
	str=strdup(h->cmd.cmd);
	cmd=nextstr(h->cmd.cmd,1);
	pp=str+find_pos(str,2);
	while(*pp && (*ptr=*pp++) && (ptr-buf)<BUFF){
		if(*ptr=='\\' && *pp=='$'){ *ptr='$'; pp++;} 
		else if(*ptr=='$' && isdigit(*pp)){
			u_short pos;
			char *p;
			if(sscanf(pp,"%hu", &pos)<0) continue;
			else{
				char t[10];
				
				sprintf(t, "%hu", pos);
				pp+=strlen(t);
				if(*pp=='-' || pos==0){
					int n;
					if(*pp=='-') pp++;
					
					 n=pos?find_pos((char *)msg, pos):0;
					 if(n<0) continue;
					
					p=(char *)msg+n;
					while(*p) *ptr++=*p++;
				}else
				{
					p=nextstr(msg, pos); /* $1=>1st word */
					if(p)
				  		while(*p) *ptr++=*p++;
				}
				continue;
			}
		}
		ptr++;
	}
	*ptr='\0';
	free(str);
	buffer=malloc(sizeof(char)*(strlen(buf)+3+strlen(cmd)));
	if(!buffer) {perror("error"); return;}
	sprintf(buffer,"%s %s", *cmd==cmdchar?cmd+1:cmd, buf);
	#if DEBUG>4
	fprintf(stderr,"%s\n", buffer);
	#endif
	new_process_cmd(buffer, w);
	free(buffer);
}

extern int check_msg(MsgNr type, const char *mSg){
	int j, i/*=find_type(cmd)*/, w=-1;
	On *p;
	char   *buf=NULL, *m=NULL, stop=0, do_external=1;

	i=(int)type;
	if(want[i].start==want[i].end) return 0;

	buf = (char *) malloc(sizeof(char) * (strlen(mSg) + 32
	+strlen(Win->server->servname)));
	if(!buf) return 0;
	for(j=0;j<win_count;j++)
		if(&winstruct[j]==Win){
			w=j;
			break;
		}
		/* <win> <id> <server,port> <msg> */
	p=want[i].start;
	m=gflags&MATCH_CASELESS?lowercase(mSg):strdup(mSg);
	while(p!=want[i].end){
		if(!fnmatch(
		gflags&MATCH_CASELESS?lowercase(p->pattern):p->pattern,m,
		 FNM_PATHNAME|FNM_PERIOD) && !(p->flag&HK_SUSPENDED)){
			if(p->type==CMD_FD){
			  if(do_external){
				sprintf(buf,"%d %d %s,%d %s\n", w,p->ser_number, 
				Win->server->servname,Win->server->port,mSg);
				if(!write_to_sp(p->cmd.sp, buf) && 
				(p->flag&HK_EXCLUSIVE))
				do_external=0;
			  }
			}else
			if(p->type==CMD_IN /*&& !stop <-- wrong */){
			/* stop after first match if ser_number==0*/
				do_internal_hook(p, mSg,w);
				if(p->ser_number==0) stop=1;
			}

		}
		p=p->next;
	}
	free(buf);free(m);
	return (stop || !do_external);
}

extern int check_numeric(int numeric, const char *msg){						
	NumHook *N;
	On *p; 
	int j, w=-1;
	char *buf, *m, stop=0, do_external=1;
	N=find_numhook(numeric);
	if(!N)
		return 0;
	if(N->start==N->end) return 0;
	buf = (char *) malloc(sizeof(char) * (strlen(msg) + 24)+
	strlen(Win->server->servname));
	if(!buf) return 0;
	for(j=0;j<win_count;j++)
		if(&winstruct[j]==Win){
			w=j;
			break;
		}
		/* <win> <id> <server,port> <msg> */
	p=N->start;
	m=gflags&MATCH_CASELESS?lowercase(msg):strdup(msg);
	while(p!=N->end){
		if(!fnmatch(lowercase(p->pattern),m, FNM_PATHNAME|FNM_PERIOD)
		&& !(p->flag&HK_SUSPENDED)){
			if(p->type==CMD_FD){
			  if(do_external){
				sprintf(buf,"%d %d %s,%d %s\n", w,p->ser_number, 
				Win->server->servname,Win->server->port,msg);
				if(!write_to_sp(p->cmd.sp, buf) && 
				(p->flag&HK_EXCLUSIVE))
				do_external=0;
			  }
			}else
			if(p->type==CMD_IN){
				do_internal_hook(p, msg,w);
				if(p->ser_number==0) stop=1;
			}

		}
		p=p->next;
	}

	free(buf); free(m);
	return (stop || !do_external);

}


