/* 
 * global.c     -global functions
 * distributed as part of _ s u l a _
 * 
 * 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 COPYING for further information.
 *
 *	The 3 err_XXX error routines  are  from the book
 *	_Advanced Programming in the Unix Environment_, 
 * 	by W.R.Stevens, Addison-Wesley. 
 * 
 */

#include <forms.h>
#include <errno.h>
#include <sys/time.h>
#include <stdarg.h>
/*#include <pwd.h>*/
#include <ctype.h>
#include <assert.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "config.h"
#include "nicks.h"
#include "aux.h"
#include "sula.h"
#include "servers.h"
#include "chanwin.h"
#include "global.h"
#include "lag.h"
#include "cmd.h"
#include "closed.xpm"
#include "connected.xpm"
#include "info.xpm"
FD_main_form   *main_form;
extern FD_serverlist *server_tool;
/* 
 * to move to the bottom:  */


extern void parse_server_output(Winstruct * win, char *msg);

int     atclose_dec_count(FL_FORM * form, void *count)
{
	(*((int *) count))--;
	return FL_OK;
}
int     atclose_reset(FL_FORM * form, void *count)
{
	*((int *) count) = 0;
	return FL_OK;
}

int     atclose_do_close(FL_FORM * form, void *fdui)
{

	return FL_OK;
}

int     dont_close_win(FL_FORM * form, void *fdui)
{

	return FL_IGNORE;
}

int     set_cursor(FL_OBJECT * ob, int event,
		   FL_Coord mx, FL_Coord my, int key, void *xev)
{
	switch (event) {
	    case FL_ENTER:
		    fl_set_cursor(FL_ObjWin(ob), ob->u_ldata);
		    return !FL_PREEMPT;
		    break;
	    case FL_LEAVE:
		    fl_reset_cursor(FL_ObjWin(ob));
		    return !FL_PREEMPT;
		    break;
	    default:
		    return !FL_PREEMPT;
	}
}

/*static Server *search_serverlistbyfd(int fd);*/
static int del_server(pServer * pp, Server * s, int flag);
static void print_server_tree(Server * p);


/*
	The err_XXX error routines below are  from the book
	_Advanced Programming in the Unix Environment_, 
 	by W.R.Stevens, Addison-Wesley.
*/

static void err_doit(int errnoflag, const char *fmt, va_list ap);
void    err_ret(const char *fmt,...)
{
	va_list ap;

	va_start(ap, fmt);
	err_doit(1, fmt, ap);
	va_end(ap);
	return;
}

void    err_sys(const char *fmt,...)
{
	va_list ap;

	va_start(ap, fmt);
	err_doit(1, fmt, ap);
	va_end(ap);
	exit(1);
}

void    err_msg(const char *fmt,...)
{
	va_list ap;

	va_start(ap, fmt);
	err_doit(0, fmt, ap);
	va_end(ap);
	return;
}
void    err_quit(const char *fmt,...)
{
	va_list ap;

	va_start(ap, fmt);
	err_doit(0, fmt, ap);
	va_end(ap);
	exit(1);
}

static void err_doit(int errnoflag, const char *fmt, va_list ap)
{
	int     errno_save;

	errno_save = errno;
	vfprintf(stderr, fmt, ap);
	if (errnoflag)
		fprintf(stderr, ": %s", strerror(errno_save));
	fprintf (stderr, "\n");
	errno=errno_save;
}
/* end end end of err_XXX() */
static void do_error(int flag, const char *fmt, va_list ap)
{
	char    buf[MAXLINE];
	int     errno_save;

	errno_save = errno;
	vsprintf(buf, fmt, ap);
	fl_addto_browser(main_form->browser, buf);
	*buf=0;
	if (flag){
		sprintf(buf, ": %s", strerror(errno_save));
		fl_addto_browser_chars(main_form->browser, buf);
	}
	errno=errno_save;
}

/* 
 * TODO: berr_ret(FL_OBJECT *browser, const char fmt,...); */

void    berr_ret(const char *fmt,...)
{
	va_list ap;

	va_start(ap, fmt);
	do_error(1, fmt, ap);
	va_end(ap);
	return;
}
void    berr_msg(const char *fmt,...)
{
	va_list ap;

	va_start(ap, fmt);
	do_error(0, fmt, ap);
	va_end(ap);
	return;
}

int     load_serverlist(const char *filename)
{
	Server  server;
	FILE   *fp;
	int     i;
	static int get_server(FILE *, Server *);

	if (!(fp = fopen(filename, "r")))
		return errno;
	while ((i = get_server(fp, &server)) != EOF)
		if (i == 0) {
			insert_server(&servTree, &server, BY_NAME);
		}
		#if DEBUG
		else if (i == 2)
			fprintf(stderr, "%s (%d) invalid server entry..ignoring",
			 __FILE__, __LINE__);
		#endif
	fclose(fp);
	return 0;
}

int     save_history(const char *f)
{
	History *current;
	FILE   *fp;

	if ((fp = fopen(f, "w")) == NULL)
		return errno;

	current = histStart->right;
	while (current != histStart) {
		fprintf(fp, "%s\n", current->line);
		current = current->right;
	}
	fclose(fp);
	return 0;
}
int     load_history(const char *f)
{
	FILE   *fp;
	char    s[512 + 1];

	if ((fp = fopen(f, "r")) == NULL)
		return errno;
	while (fgets(s, 512, fp)) {
		s[strlen(s) - 1] = '\0';
		if (s[0])
			AddToHistory(s);
	}
	fclose(fp);
	return 0;
}

int     save_servers(char *path)
{
	static void save_server_list(FILE * fp, Server * p);
	char    *buf;
	FILE   *fp;

	if (path)
		goto do_it;
	if (!(path = getenv("HOME")))
		path = ".";
	buf=(char *)malloc(sizeof(char)*(strlen(path)+strlen(P_HOME P_SERVERS)+8));
	sprintf(buf, "%s/%s/%s", path, P_HOME, P_SERVERS);
	path = strdup(buf);
	if (access(path, F_OK) > -1) {
		if (access(path, W_OK) < 0)
			{free(buf);return errno;}
		sprintf(buf, "%s.bak", path);
		rename(path, buf);
	}
	free(buf);
      do_it:
	if ((fp = fopen(path, "w")) == NULL)
		return errno;
	fprintf(fp, "%c" sula_NAME sula_VERSION "\n%cServer list %s\n\n", COMMENT, COMMENT, fl_now());
	save_server_list(fp, servTree);
	fflush(fp);
	fclose(fp);
	return 0;
}
static void save_server_list(FILE * fp, Server * p)
{
	char    buf[512];

	if (p) {
		save_server_list(fp, p->left);
		sprintf(buf, "%s:%u:%s:%s:%s:%s\n", p->servname, p->port, p->password ? p->password : "",
			p->nick, p->email, p->ircname);
		fputs(buf, fp);
		save_server_list(fp, p->right);
	}
}

extern void addto_cmd_sent(Winstruct * win, const char *cmd, const char *arg)
{
	CmdSent *p = cmdsentStart;

	assert (cmd && win);
	cmdsentStart = (CmdSent *) malloc(sizeof(CmdSent));
	if (cmdsentStart == NULL) {
		return;
	}
	cmdsentStart->cmd = strdup(cmd);
	cmdsentStart->arg = strdup2(arg);
	cmdsentStart->win = win;

	cmdsentStart->next = p;
	return;
}

/* 
 * Windows
 * ================================================================================ */

Winstruct *
        search_winlistbyfd(int fd, int *n)
{
/* 
 * look for the first window with this fd *n is total number of windows found 
 * for this server
 * 
 */
 	int i;
	Winstruct *win=NULL;

	*n = 0;
	for(i=0;i<win_count;i++)
		if (winstruct[i].chanwin && winstruct[i].server && winstruct[i].server->fd == fd) {
			(*n)++;
			if (!win) win=&winstruct[i];
		}
	return win;
}

Winstruct *
        search_winlist_byservname(char *name, int port, int *total)
{
/* 
 * return first window on this server 
 * 
 */
	Winstruct *p = NULL;
	int i;
	*total = 0;

	for(i=0;i<win_count;i++)
		if (winstruct[i].chanwin && winstruct[i].server && 
		!strcasecmp(name, winstruct[i].server->servname) &&
		    port == winstruct[i].server->port) {
			if (!p) p=&winstruct[i];
			(*total)++;
		}
	return p;
}


extern void update_topic(Winstruct * win, const char *channel, const char *topic)
{
	char   *buf;

	assert(topic && channel && win);
	if (strcasecmp(channel, win->chanCur->name))
		{return;}
	buf = (char *) malloc(sizeof(char) *(strlen(channel)+strlen(topic)+8));
	sprintf(buf, "%s: %s", channel, topic);
	fit_object_label(win->chanwin->WinStatusLine, buf);
	free(buf);
}

extern void fit_object_label(FL_OBJECT * ob, const char *label)
{
	int     ll;
	size_t  l;
	char   *buf;

	buf = strdup2(label);
	if (!buf)
		buf = "";
	l = strlen(buf);
	ll = ob->w - 2;
	while (buf && fl_get_string_width(ob->lstyle, ob->lsize, buf, l) > ll)
		*(buf + --l) = '\0';
	fl_set_object_label(ob, buf);

}

void    UpdateStatus(Winstruct * win)
{
	char   *buf;
	int     isserverop;

		if (win->server) {
		int     ischanop = 0, cantalk = 0;
		char   *chanmode = NULL, *mymode = NULL;
		char   *query = NULL;
		char   *t = NULL, *s = NULL;
		char    has_channels = 0;
		char	*dcc_chat = NULL;
		
		buf = (char *) malloc(sizeof(char) * 1024);
		if (!buf) {
			perror("Error");
			return;
		}
		if (MaxNickLen < 1)
			MaxNickLen = 1;
		if (MaxChanLen < 1)
			MaxChanLen = 1;
		isserverop = (win->server && win->server->mode && strchr(win->server->mode, 'o')) ? 1 : 0;
		if (win->server->mode) {
			mymode = (char *) malloc(sizeof(char) * (strlen(win->server->mode) + 12));

			if (strdchr(win->server->mode, 'o'))
				sprintf(mymode, "  (+%s) ", strdchr(win->server->mode, 'o'));
			else
				strcpy(mymode, "");
		}
		else
			mymode = strdup("");
		if (win->chanCur == win->chanStart || win->chanStart->left == win->chanStart) {
			/*  not on a channel (empty channel list) */
			sprintf(buf, "%s%s%s",
				strcp(win->server->nick, MaxNickLen), (isserverop) ? "*" : "",
				mymode);
		}
		else {
			has_channels = 1;
			ischanop = (win->chanCur->modes && strchr(win->chanCur->modes, 'o'));
			cantalk = (win->chanCur->modes && strchr(win->chanCur->modes, 'v'));
			t = (char *) malloc(sizeof(char) * 40);

			if (win->chanCur->limit)
				sprintf(t, " <limit: %d>", win->chanCur->limit);
			else
				strcpy(t, " ");
			if (win->chanCur->key) {
				s = (char *) malloc(sizeof(char) * (strlen(win->chanCur->key) + 10));

				sprintf(s, " <key: %s>", win->chanCur->key);
			}
			if (win->chanCur->modes) {
				char   *tmp = strdup(win->chanCur->modes);
				chanmode = (char *) malloc(sizeof(char) * (
				strlen(win->chanCur->modes) + 10));

				if (ischanop)
					tmp = strdchr(tmp, 'o');
				if (cantalk)
					tmp = strdchr(tmp, 'v');
				if (*tmp)
					sprintf(chanmode, " (+%s) ", tmp);
				else
					strcpy(chanmode, "");
				free(tmp);
			}
			if (win->chanCur->topic)
				update_topic(win, win->chanCur->name, win->chanCur->topic);
			else
				update_topic(win, win->chanCur->name, 
				"No topic is set (" sula_NAME sula_VERSION ")");
		}
		if (fl_get_button(win->chanwin->r_query) && win->query_nick) {
			query = (char *) malloc(sizeof(char) * (strlen(win->query_nick) + 15));

			sprintf(query, " [Query: %s]", win->query_nick);
		}
		else if (	/*
				 * fl_get_button(win->chanwin->r_dcc_chat) && 
				 */
				win->dccCur != win->dccStart &&
				win->dccStart->left != win->dccStart) {
			dcc_chat = (char *) malloc(sizeof(char) * (strlen(win->dccCur->nick) + 15));

			sprintf(dcc_chat, " [DCC chat: %s]", win->dccCur->nick);
		}
		if (has_channels)
			sprintf(buf, "%s%s%s%s%s%s%s on %s%s%s%s",
				strcp(win->server->nick, MaxNickLen),
				(isserverop) ? "*" : "",
				(ischanop) ? "@" : "",
				(cantalk && !ischanop) ? "+" : "",
			mymode, query ? query : "", dcc_chat ? dcc_chat : "",
				strcp(win->chanCur->name, MaxChanLen),
				chanmode ? chanmode : "",
				t ? t : "", s ? s : ""
			   );
		else
			sprintf(buf, "%s%s%s%s%s%s",
				(!query && !dcc_chat) ? "[" sula_NAME sula_VERSION "]  " : "",
				strcp(win->server->nick, MaxNickLen),
				(isserverop) ? "*" : "",
			mymode, query ? query : "", dcc_chat ? dcc_chat : ""

			   );
		free(t);free(mymode); free(dcc_chat); free(query); 
		free(s);free(chanmode);

	}	/*  if( win->server) */
	else if (fl_get_button(win->chanwin->r_dcc_chat) && win->dccCur != win->dccStart &&
		 win->dccStart->left != win->dccStart) {
		buf = (char *) malloc(sizeof(char) * (32+strlen(win->dccCur->nick)));
		sprintf(buf, " [DCC chat with %s]", win->dccCur->nick);

	}
	else
		buf=strdup( "Infoterial: " sula_INFOSITE);
	fit_object_label(win->chanwin->status, buf);
	free(buf);
}

void    UpdateWindows(int fd, char connected)
{
/* 
 * update windows status displays upon establishing or closing down a
 * connection */
	WinList *p;
	char    *buf=NULL;

#include "here.xpm"
#include "away.xpm"

	if (fd < 0)
		return;
	build_winlistbyfd(fd);
	p = servWin;
	while (p) {
		if (connected) {
			buf = (char *) malloc(sizeof(char) * (64+strlen(
			p->win->server->servname)));
			sprintf(buf, "You are now on IRC (via %s, port %d).",
			    p->win->server->servname, p->win->server->port);
			fl_set_object_label(p->win->chanwin->WinStatusLine, buf);
			free(buf);
			UpdateStatus(p->win);
			fl_set_pixmapbutton_data(p->win->chanwin->info, info_xpm);
			fl_show_object(p->win->chanwin->info);
			fl_free_pixmapbutton_pixmap(p->win->chanwin->close_con);
			fl_set_pixmapbutton_data(p->win->chanwin->close_con, connected_xpm);
			fl_set_pixmapbutton_data(p->win->chanwin->away,
				 p->win->server->flag & SET_AWAY ? away_xpm : here_xpm);
			fl_show_object(p->win->chanwin->away);

		}
		else {
			CmdSent *cc = cmdsentStart;
			buf = (char *) malloc(sizeof(char) * (64+strlen(
			p->win->server->servname)));
			sprintf(buf, "Connection to server %s (port %d) closed",
			    p->win->server->servname, p->win->server->port);

			/*fl_addto_browser(p->win->chanwin->brow2, buf);*/
			fl_set_object_label(p->win->chanwin->WinStatusLine, buf);
			free(buf);
			fl_hide_object(p->win->chanwin->info);
			fl_free_pixmapbutton_pixmap(p->win->chanwin->info);

			fl_hide_object(p->win->chanwin->away);
			fl_free_pixmapbutton_pixmap(p->win->chanwin->away);

			fl_free_pixmapbutton_pixmap(p->win->chanwin->close_con);
			fl_set_pixmapbutton_data(p->win->chanwin->close_con, closed_xpm);
			#if 1
			if(p->win->server->connecting>0){
				fl_activate_object(p->win->chanwin->close_con);
				fl_set_object_color(p->win->chanwin->close_con, 
				FL_COL1, FL_MCOL);
			}
			#endif
			if (p->win->chanwin->chanusers->u_vdata) {
				FD_chanusers *f=(FD_chanusers *) p->win->chanwin->chanusers->u_vdata;
				fl_clear_choice(f->channel);
				fl_clear_browser(f->browser);
			}
			while (cc != cmdsentEnd)
				if (cc->win == p->win) {
					CmdSent *q = cc->next;

					free(cc->cmd);
					if (cc->arg)
						free(cc->arg);
					if (q == cmdsentEnd)
						cmdsentEnd = cc;
					else
						*cc = *q;
					free(q);
				}
				else
					cc = cc->next;
		}
		p = p->next;
	}
}

/* 
 * BUILD WINDOW LIST     */


static void free_win_list(void){
/* pop all off*/	
	WinList *p;
	while(servWin){
		p=servWin;
		servWin=servWin->next;
		free(p);
	}
}
int     win_add(Winstruct * win)
{
/* 
 * push a newwin  */
	WinList *p = servWin;

	servWin = (WinList *) malloc(sizeof(WinList));
	if (!servWin)
		return errno;
	servWin->win = win;
	servWin->next=p;
	return 0;
}

void   *
        build_winlistbyfd(int fd)
{
	int i;
	free_win_list();
	for(i=0;i<win_count;i++)
		if (winstruct[i].chanwin && winstruct[i].server && 
		    fd == winstruct[i].server->fd)
			if(win_add(&winstruct[i])) return NULL;
	return (void *) servWin;
}

void   *
        build_winlistbyservname(char *name, int port)
{
/* 
 * similar to build_winlistbyfd  */

	int i;
	free_win_list();
	for(i=0;i<win_count;i++)
	if (winstruct[i].chanwin && winstruct[i].server && 
	!strcasecmp(name, winstruct[i].server->servname) &&
	    port == winstruct[i].server->port) 
		if(win_add(&winstruct[i])) return (void *)0;
	return (void *) servWin;
}

/* 
 * ------------------SERVERS    
 * ============================================================================ */

int     remove_server_from_tree(char *servname, int port)
{
	Server *server;
	int     found;

	server = search_server_tree_byname(servname, port);
	if (!server)
		return 2;
	else {
		Winstruct *win;

		win = search_winlist_byservname(server->servname, server->port, &found);
		if (win)
			return 0;
		close_server_connection(&server->fd);
		del_server(&servTree, server, BY_NAME);
	}
	return 1;
}

static void left_rotate(pServer * pp)
{
	pServer 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(pServer * pp)
{
	pServer 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;
}

int     insert_server(pServer * pp, Server * s, int flag)
{
	int     dH = 0;
	pServer p = *pp;
	int     tt=0;

	assert (s && s->servname && (flag == BY_FD || flag == BY_NAME));
	if ( (flag == BY_FD && s->fd < 0))
		err_quit("%s (%d) Bug", __FILE__, __LINE__);
	if (p == NULL) {

		*pp = p = (pServer) malloc(sizeof(Server));
		if (!p)
			err_sys("%s %d", __FILE__, __LINE__);
		if (flag == BY_FD) {
			*p = *s;
			p->port = s->port;
			p->fd = s->fd;
			p->servname = strdup(s->servname);
			p->left = p->right = NULL;
			p->bf = 0;
			return 1;
		}
		p->servname = strdup(s->servname);
		p->nick = s->nick ? strdup(s->nick) : "sula";	/* * must be a bug somewhere								 */
		p->iNick=s->iNick;
		if(p->iNick<0){
			Nickcount i=get_list_bylabel("default");
			if(i<0){
				i=new_nicklist("default");
				if(i<0) err_sys("error");
			}
			p->iNick= i;
			aNickList[p->iNick].current=
				add_nick_to_list(p->iNick, p->nick);
			if(!aNickList[p->iNick].current) err_sys("error");
		}

		p->email = s->email ? strdup(s->email) : "sula@unknown.org";
		p->ircname = s->ircname ? strdup(s->ircname) : sula_NAME sula_VERSION " Tester";
		p->mode = NULL;
		p->lag=NULL;
		p->flag = SERVER_FLAG_RESET;
		p->connecting=0;
		p->t.tv_sec=p->t.tv_usec=0;
		p->port = s->port;
		p->state = s->state;
		p->password = strdup2(s->password);
		p->fd = s->fd;
		p->lastInvite = NULL;
		p->read = 0;
		p->bf = 0;

		p->left = p->right = NULL;
		dH = 1;
	}
	else if (
			(flag == BY_NAME &&
			 ((tt = strcasecmp(s->servname, p->servname)) > 0 ||
			  (tt == 0 && s->port > p->port))
			) ||
			(flag == BY_FD && s->fd > p->fd)
	   ) {
		if (insert_server(&p->right, s, 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 (
			(flag == BY_NAME && (tt < 0 || (tt == 0 && s->port < p->port))) ||
			(flag == BY_FD && s->fd < p->fd)
	   ) {
		if (insert_server(&p->left, s, 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 if (flag == BY_FD && s->fd == p->fd)
				/*two servers have same fd */
		err_quit("%s %d Bug.", __FILE__, __LINE__);
	return dH;
}

static int del_server(pServer * pp, Server * s, int flag)
{
	pServer p = *pp, *q;
	int     dH = 0;
	int     tt=0;

	if (!p)
		return 0;
	if (
		   (flag == BY_FD && s->fd < p->fd) ||
		   (flag == BY_NAME && (
			  (tt = strcasecmp(s->servname, p->servname)) < 0 ||
					       (tt == 0 && s->port < p->port)
		    )
		   )
	   ) {
		if (del_server(&p->left, s, flag)) {
			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 ((flag == BY_FD && s->fd > p->fd) ||
		 (flag == BY_NAME && (tt > 0 || (tt == 0 && s->port > p->port)))
	   ) {
		if (del_server(&p->right, s, flag)) {
			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 {			/* * found  */
		if (p->right == NULL) {
			*pp = p->left;
			if (flag == BY_FD) {
				free(p);
			}
			else {	/* * do_free(p); */
#ifdef this_causes_a_seg_fault
				free(p->mode);
				free(p->servname);
				etc
#endif
				free(p);
			}
			return 1;
		}
		else if (p->left == NULL) {
			*pp = p->right;
			if (flag == BY_FD) {
				free(p);
			}
			else {	/* 	 * do_free(p); */
#ifdef this_causes_a_seg_fault
				free(p->servname);
				free(p->nick);
				etc
#endif
				free(p);
			}
			return 1;
		}
		else {
			q = &p->left;
			while ((*q)->right != NULL)
				q = &(*q)->right;
			if (flag == BY_FD) {
				p->fd = (*q)->fd;
				p->servname = (*q)->servname;
				p->port = (*q)->port;

			}
			else {
				p->port = (*q)->port;
				p->servname = strdup2((*q)->servname);
				p->fd = (*q)->fd;
				p->nick = strdup2((*q)->nick);
				p->ircname = strdup2((*q)->ircname);
				p->mode = strdup2((*q)->mode);
				p->flag = (*q)->flag;
				p->connecting=(*q)->connecting;
				p->t.tv_sec=(*q)->t.tv_sec;p->t.tv_usec=(*q)->t.tv_usec;
				p->lag=(Lag *)malloc(sizeof(Lag));
				p->lag=((*q)->lag);
				p->password = strdup2((*q)->password);
				p->email = strdup2((*q)->email);
				p->state = (*q)->state;
				p->read = (*q)->read;
				p->lastInvite = strdup2((*q)->lastInvite);
				p->iNick=(*q)->iNick;
			}
			if (flag == BY_FD) {
				(*q)->fd = s->fd;
				(*q)->servname = s->servname;
				(*q)->port = s->port;
			}
			else {

				(*q)->fd = s->fd;
				(*q)->servname = (s->servname);
				(*q)->nick = strdup2(s->nick);
				(*q)->ircname = strdup2(s->ircname);
				(*q)->email = strdup2(s->email);
				(*q)->flag = s->flag;
				(*q)->connecting=s->connecting;
				(*q)->t.tv_sec=s->t.tv_sec;
				(*q)->t.tv_usec=s->t.tv_usec;
				(*q)->lag=(Lag *)malloc(sizeof(Lag ));
				(*q)->lag=s->lag;
				(*q)->mode = strdup2(s->mode);
				(*q)->port = s->port;
				(*q)->password = strdup2(s->password);
				(*q)->state = s->state;
				(*q)->read = s->read;
				(*q)->lastInvite = strdup2(s->lastInvite);
				(*q)->iNick=s->iNick;
			}
			if (del_server(&p->left, s, flag)) {
				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;
}

static void print_tree(Server * p, int pos)
{
	if (p) {
		print_tree(p->right, pos + 8);
		printf("%*.8s(%d) %d\n", pos, p->servname, p->port, p->bf);
		print_tree(p->left, pos + 8);
	}
}

static void print_fdtree(Server * p, int pos)
{
	if (p) {
		print_fdtree(p->right, pos + 8);
		printf("%*d %6s %d \n", pos, p->fd, p->servname, p->bf);
		print_fdtree(p->left, pos + 8);
	}
}

void    list_servers(void)
{
	print_server_tree(servTree);
	print_tree(servTree, 8);
	print_fdtree(servFdTree, 3);
}

void    print_server_tree(Server * p)
{
	int     i = 1;

	if (p) {
		print_server_tree(p->left);
		err_msg("%s\t%d. %s %d %s %s (%s)",
			p->fd > 0 ? "*" : "", i++, p->servname,
			p->port, p->nick, p->email, p->ircname);
		print_server_tree(p->right);
	}
}
static Server *
        search_server_tree_byfd(int fd)
{
	Server *p = servFdTree;

	while (p) {
		if (fd == p->fd)
			return p;
		if (fd < p->fd)
			p = p->left;
		else
			p = p->right;
	}
	return NULL;
}
Server *
        search_server_tree_byname(const char *name, int port)
{
	Server *p = servTree;
	int     c;

	while (p) {
		c = strcasecmp(name, p->servname);
		if (c == 0) {
			if (p->port == port)
				return p;
			else if (port > p->port)
				p = p->right;
			else
				p = p->left;
		}
		else if (c < 0)
			p = p->left;
		else
			p = p->right;
	}
	return NULL;
}
static void RemoveChannels(Server *);
static void RemoveCommands(Server *);
static void close_server(Server *server){
	del_server(&servFdTree, server, BY_FD);
 	UpdateWindows(server->fd, 0);
 	ClearLag(server);
 	server->fd = -1;
	if(server->connecting>0) server->connecting=0;/* dont reset if <0 : ext proc.*/
 	RemoveCommands(server);
 	RemoveChannels(server);
	if(server_tool) fl_trigger_object(server_tool->update);
}

static void ircserver_cb(int fd, void *data)
{
#define SERV_RD_MAX	2048
	char    line[SERV_RD_MAX + 1];
	int     n;

#ifndef SA_RESTART
/* interrupted system calls will not be repeated if signal.h doesnt define this */
retry:
#endif
	if ((n = read(fd, line, SERV_RD_MAX)) < 0) {
		if(errno!=EAGAIN)
		#ifndef SA_RESTART
	       	if(errno==EINTR) goto retry;
		else
		#endif
		{
		int     x;
		Winstruct *win;

		fl_remove_io_callback(fd, FL_READ, ircserver_cb);
		close(fd);
		win = search_winlistbyfd(fd, &x);
		assert(win != NULL);
			/*err_quit("Bug: Channel window list is screwed up");*/
		berr_msg(PROMPT "Read error from port %d of server %s.Closing..",
			 win->server->servname, win->server->port);
		close_server(win->server);
		}
		return;
	}
	if (n == 0) {
		int     x;
		Winstruct *win;
		fl_remove_io_callback(fd, FL_READ, ircserver_cb);
		close(fd);
		win = search_winlistbyfd(fd, &x);
		assert(win);
			/*err_quit("Bug: Channel window list is screwed up");*/
		berr_msg(PROMPT "Connection closed from port %d of server %s",
			 win->server->port, win->server->servname);
		close_server(win->server);
		return;
	}
	line[n] = '\0';
	prepare_server_output(fd, line);

}
/********************************************************/
/*							*/
/*	C o n n e c t   t o   s e r v e r		*/
/*							*/
/********************************************************/

extern void connecting(char *server, u_short port, char done){
/* to call before and after successfull connection to server */
	WinList *p;
	build_winlistbyservname(server,port);
	p = servWin;
	while(p){
		if(!done){
			fl_deactivate_object(p->win->chanwin->close_con);
			fl_set_object_color(p->win->chanwin->close_con, 
				FL_INACTIVE_COL, FL_MCOL);
		}else{
			fl_activate_object(p->win->chanwin->close_con);
			fl_set_object_color(p->win->chanwin->close_con, 
				FL_COL1, FL_MCOL);
		}
		p=p->next;
	}
	if(done && server_tool)
		fl_trigger_object(server_tool->update);
	
}

typedef struct THp_struct{
	char *name;
	u_short port;
	int win;
}Hp_struct;
extern int RemoveFromWinChanList(Winstruct *, const char *);
static void gethp_cb(int fd, void *data)
{
	#define MAXRD	255
	char 	line[MAXRD+1], *buf=0;
	int n;
	Hp_struct *hp=data;
	Winstruct *win=(hp->win>-1 && winstruct[hp->win].chanwin)?
		&winstruct[hp->win]:NULL;
	long old=0;
	int no_of_wins=0, old_fd=-1;
	 Server *s;
	
	n=read(fd, line,MAXRD);
	fl_remove_io_callback(fd,FL_READ, gethp_cb);
	close(fd);
	if(server_tool) fl_trigger_object(server_tool->update);
	if(win){
		if(win->chanwin->close_con->u_vdata)
			win->chanwin->close_con->u_vdata=NULL;
		else{
		/* another serevr is has connected using our win*/
			#if DEBUG
			fprintf(stderr,"too late (%p)..?\n",win);
			#endif
			return;
		}
		old=(long)win->server;
		if(win->server){
		   search_winlistbyfd(win->server->fd, &no_of_wins);
		   old_fd=win->server->fd;
		}
	}
	s=search_server_tree_byname(hp->name, hp->port);
	if(s==NULL){ /* server's beeen removed in the meantime!!!!*/
		fl_ringbell(25);
		#if DEBUG
		fprintf(stderr,"server %s (%hu) is gone..?\n",hp->name, hp->port);
		#endif
		return;
	}
	free(hp->name);
	assert(s->connecting==-1);
	s->connecting=0;
	buf=malloc(sizeof(char)*(strlen(s->servname)+255));
	if(n<1){
		sprintf(buf,"Process couldnt get hostname for %s.%s",
		s->servname,
		sflags&FORCE_CONNECTION? " Connecting but may block":"Stop." );
		if(!(sflags&FORCE_CONNECTION))
		 goto out;
		else line[0]='\0';
	}else{
	      line[n]='\0';
	      #if DEBUG
	      sprintf(buf,"Got hostname for %s:%s."
	       " Trying to connect..",      s->servname,line);
	      #endif
	}
	if(win){
		fit_object_label(win->chanwin->WinStatusLine,buf);
		XFlush(fl_display);
	}
	if(server_tool) fit_object_label(server_tool->status,buf);
	#if DEBUG
	fprintf(stderr,"\E[32m%s\E[m\n",buf);
	#endif
	free(buf);
	if(inet_addr(line)==INADDR_NONE){ /* external prog buggy or what?*/
		fprintf(stderr,"\E[1mBad IP address:%s.\E[m Ignored."
		"Check external program %s.\n", line, gethostbynameprogram);
		line[0]=0;
	}
	if(connect_to_server(s,win, line[0]?line:NULL,1))
	{
		buf=malloc(sizeof(char)*(strlen(s->servname)+80));
		sprintf(buf,"Connection to port %d of %s failed", 
		s->port, s->servname);
		if(win) win->server=(Server *)old;
	out:
		if(win) fit_object_label(win->chanwin->WinStatusLine,buf);
		#if DEBUG
		fprintf(stderr,"\E[31m%s\E[m\n",buf);
		#endif
		free(buf);
		return;
	} else if(win && old){
		Channel *current;

		if(no_of_wins>1){ 
			if(old_fd > -1){
				current=win->chanStart->right;
				while(current!=win->chanStart){
					sprintf(line,"NOTICE %s :(%s changing server for %p)\n",
					current->name,sula_NAME sula_VERSION,win);
					n=strlen(line);
					if(writen(old_fd,line,n)!=n) goto ende;
					sprintf(line,"PART %s\r\n",current->name);
					n=strlen(line);
					if(writen(old_fd,line,n)!=n) goto ende;
					current=current->right;
					continue;
					ende:
					close_server_connection(&old_fd);
					break;
				}
			}
		}
		if((Server *)old != win->server && no_of_wins<2 ){
			new_close_connection( &((Server *)old)->fd,
			 sula_NAME sula_VERSION ": Changing servers", 0);
			((Server *)old)->state=-1;
		}
			/*rejoin channels*/
		current=win->chanStart->right;
		while(current!=win->chanStart){
			buf=malloc(sizeof(char)*(strlen(current->name)+20));
			sprintf(buf,"JOIN %s", current->name);
			add_cmdoncon(CLI_CMD,buf, s->servname, s->port);
			free(buf);
			current=current->right;
		}
		current=win->chanStart->right;
		/* close all channels*/
		while(current!=win->chanStart){
			RemoveFromWinChanList(win, current->name);
			current=current->right;
		}
	}/* else if (win && old) */

	
}
static int gethostname_for_connection(Server * s, Winstruct * win){
int	fd[2], pid;
 char *prog=strrchr(gethostbynameprogram,'/');
 if(!prog || !(*++prog)) prog= gethostbynameprogram;/* not checkin
 	whether only a directory name's been given.. uh trouble*/	
       if(win)	fit_object_label(win->chanwin->WinStatusLine,
	"Calling external process for hostname...");
	#if DEBUG
	fprintf(stderr,"\E[1mStarting process for remote hostname %s\E[m\n",
	s->servname);
	#endif
	if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)<0){
		err_ret("socketpair error");
		return -1;
	}
	TELL_WAIT(); /* to synchronize child/parent */
	if( (pid=fork())<0)
  	  {err_ret("fork error");return -1;}
	else if(pid>0){
		Hp_struct *hp=malloc(sizeof(Hp_struct));
		#if DEBUG
		fprintf(stderr,"(%s) PID %d. \E[7mTimeout set to %d secs\E[m\n",
		prog,pid,connect_timeout);
		#endif
		hp->name=strdup(s->servname);
		hp->port=s->port;
		hp->win=-1;
		if(win){
			int i;
			for(i=0;i<win_count;i++)
			  if(&winstruct[i]==win){
				hp->win=i;
				win->chanwin->close_con->u_vdata=(void *)hp;/*
					anything; flag to know we are connecting*/
				break;
			  }
		}
		close(fd[1]);	/*stdin */
		fl_add_io_callback(fd[0], FL_READ, gethp_cb, (void *)hp);
		TELL_CHILD(pid);
		/* sit back and wait */
		return 0;
	}
	else{
	       	char  buf2[12];
		WAIT_PARENT();/* wait for parent to set up callback*/
		sprintf(buf2,"%u", connect_timeout);
		close(fd[0]);
		if(fd[1] != STDOUT_FILENO)
		  if(dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)
		  	exit(-1);
		if(execlp(gethostbynameprogram,prog,"-s",s->servname,"-t",
		 buf2,NULL) <0)
			{err_ret("%s",gethostbynameprogram); exit(errno);}
	     }
	     return 0;
}

static void rm_cmdoncon(Server *server){
/* remove all commands to be done upon connecting to this server */
	CmdOnCon *p= cmdonconStart;
	while (p != cmdonconEnd)
		if ((!p->server && (p->port == server->port || p->port < 1)) ||
		    (!strcasecmp(p->server, server->servname) &&
		     (p->port == server->port || p->port < 1))
		   ) {
			CmdOnCon *q = p->next;
			free(p->cmd);
			free(p->server);
			if (q == cmdonconEnd)
				cmdonconEnd = p;
			else
				*p = *q;
			free(q);
		}
		else
		p = p->next;
}

extern int connect_to_server(Server * s, Winstruct * win,
 const char *ip_addr, int have_tried)
{
	/*
	 * have to sort all these out someday!
	 * -1: already conected
	 * 0:  either success (2nd time we get in here), or calling ext. process
	 * errno: some error..
	 * 
	 * create new win if win==NULL
	 */
	char    line[512];
	Server *server=NULL;
	int     fd;
	int     found = 0, new_win;
	int     n, err_no;
	struct linger l;

	assert(s && s->servname); /* uh? cant fail..?*/
	if(ip_addr || have_tried)
		goto do_it; /* either we've been here before or we 
			already have an IP address*/
	server = search_server_tree_byname(s->servname, s->port);
	if (server) {
		if(server->connecting){
			fl_ringbell(10);
			fprintf(stderr,"already trying to resolve server name:"
			"%s (%d). Wait.\n",server->servname, server->port);
			return -2;
		}else
		{
			found = 1;
			if (server->fd > -1){
				fprintf(stderr,"already connected\n");
				return -1;
			}
		}
	}
	else {
		insert_server(&servTree, s, BY_NAME);
		server = search_server_tree_byname(s->servname, s->port);
		assert (server); /* if recheck fails then code is buggy  and
				this program is crap*/
	}
	if(inet_addr(s->servname)!=INADDR_NONE)
		goto do_it;
	if(sflags & EXTERNALGETHOST){
	   server->connecting=-1;
	   if(gethostname_for_connection(server, win)==0){
		if(server_tool) fl_trigger_object(server_tool->update);
		return 0; /* return value now meaningless to caller.
			sit back and wait.
			 we shall be back l8r when with the IP numbers*/
	   }
	   else{
	    	server->connecting=0;
	    fprintf(stderr,"Coulnt start external process. Connecting to %s (%d)"
		" but may block\n", server->servname,server->port);
		}
	}
do_it:
	if(have_tried){
		found=1; /* was inserted first time we were here */
		/* just check again, just to be certain. dont we have all the
		time in the world..? */
		server = search_server_tree_byname(s->servname, s->port);
		assert(server); /* something is wrong if server not found */
	}
	#if DEBUG
	fprintf(stderr,"\E[1mConnecting to %s.\E[m Bonne chance.\n",
		ip_addr?ip_addr:server->servname);
	#endif
	if ((fd = tcp_open(ip_addr?(char *)ip_addr:server->servname,
		 0, NULL, server->port, connect_timeout)) < 0){ 
		berr_msg("@i"PROMPT "%s (%d) Connection failed. Diagnostics:", 
		server->servname, server->port);
		berr_msg(tcp_err_msg);
		#if DEBUG
		fprintf(stderr,"%s\n",tcp_err_msg);
		#endif
		if(server_tool) fit_object_label(server_tool->status,
			"Connection failed");
		rm_cmdoncon(server);
		return -1;
	}
	server->fd = fd;
	insert_server(&servFdTree, server, BY_FD);
	assert (search_server_tree_byfd(server->fd));
		/*err_quit("%s %d BUg:fd failure", __FILE__, __LINE__);*/
#if DEBUG
	fprintf(stderr,"Connected to port %d of %s (fd=%d)\n", server->port,
		server->servname, server->fd);
#endif
	if (fcntl(server->fd, F_SETFL, O_NONBLOCK) == -1){
		err_no=errno;
		err_ret("(Will block.. ciao.) fcntl error");
		close(server->fd);
		rm_cmdoncon(server);
		return err_no;
	}
	l.l_onoff = 0;
	l.l_linger = 0;
	if (setsockopt(server->fd, SOL_SOCKET, SO_LINGER, (void *) &l, sizeof(l)) < 0) 
		err_ret("SO_LINGER- setsockopt error ignored");
	new_win = win ? 0 : 1;
	if (!found || server->state < 0) {
		if (win == NULL) {
			win = new_chan_win();
			set_object_attribs(win);
			win->server = server;
			
		}
		else 
			win->server = server;
		win->server->read = 0;
	}
	else{
		/* check whether server is actually in the server list*/
		int     x;
		win = search_winlist_byservname(server->servname, server->port, &x);
		assert (win && x );	
	}
	if (win->server->state < 0) {	
		char   *label = (char *) malloc(sizeof(char) *
		 (strlen(win->server->servname) + strlen(sula_NAME sula_VERSION) 
		 + 24));
		sprintf(label, "[%d] "sula_NAME sula_VERSION ": %s %d", 
		(win-winstruct)+1, win->server->servname, win->server->port);
		if (new_win){
			fl_show_form(win->chanwin->chanwin, FL_PLACE_FREE, 
			FL_FULLBORDER, label);
		}
		else
			fl_set_form_title(win->chanwin->chanwin, label);
		free(label);
		win->server->state = 1;
	}
	fl_add_io_callback(win->server->fd, FL_READ, ircserver_cb, (void *) 0);
	
	sprintf(line, "NICK %s\n", win->server->nick);
	n = strlen(line);
	if (writen(win->server->fd, line, n) != n)
		goto do_error;
	if (win->server->password) {
		sprintf(line, "PASS %s\n", win->server->password);
		n = strlen(line);
		if (writen(win->server->fd, line, n) != n)
			goto do_error;
	}
	if (server->email) {
		char *name=strxstr(win->server->email, "@");
		char *hhost=strsstr(win->server->email, "@");
		if(!name) name=Name;
		if(!hhost) hhost=host;
			sprintf(line, "USER %s %s %s :%s\n", name, hhost,
			win->server->servname, win->server->ircname ?
			 server->ircname : sula_NAME sula_VERSION);
	}
	else
		sprintf(line, "USER %s %s %s :%s\n", Name, host, server->servname,
		server->ircname ? server->ircname : sula_NAME sula_VERSION);
	n = strlen(line);
	if ((n = writen(win->server->fd, line, n)) != n) 
		goto do_error;
	fit_object_label(win->chanwin->WinStatusLine,
	 "Server contacted. Wait for confirmation..");
	if(server_tool) fl_trigger_object(server_tool->update);
	connecting(win->server->servname, win->server->port, 0);
	win->server->connecting=1;
	/*if(!(win->server->flag & GOT_MOTD))
		win->server->flag |= GOT_MOTD;*//* confusing */
	gettimeofday(&win->server->t,NULL);
	#ifndef NOLAG
	send_lag_junk(win->server,1,0);
	#endif
	if (new_win ){ 
		fl_trigger_object(win->chanwin->update);
		fl_do_forms();
	}
	return 0;


 do_error:
 	{
 	err_no=errno;
	perror("write error");
	close_server_connection(&win->server->fd);
	return (errno=err_no);
 	}


}
static void RemoveChannels(Server * server);
void    new_close_connection(int *fd, char *reason, int update_win)
{
	/*
	 * undate_win: update corresponding channelr windows after closing connection
	 */
	Server *s, *server;

	if (*fd < 1)
		return;
	fl_remove_io_callback(*fd, FL_READ, ircserver_cb);
	s = search_server_tree_byfd(*fd);
	assert (s && s->fd == *fd);
	server = search_server_tree_byname(s->servname, s->port);
	assert (!strcasecmp(server->servname, s->servname) && server->port==s->port);
	if (1) {
		char   *buf = (char *) malloc(sizeof(char) * (
		(reason ? strlen(reason):strlen(quit_text)) +10));
		if(buf){
			sprintf(buf, "QUIT :%s\n", reason ? reason : quit_text);
			writen(server->fd, buf, strlen(buf));
			free(buf);
		}
	}
	close(server->fd);
	del_server(&servFdTree, server, BY_FD);
	if (update_win)
		UpdateWindows(*fd, 0);
	ClearLag(server);
	*fd = server->fd = -1;
	if(server->connecting>0) server->connecting=0;
	if(server_tool) fl_trigger_object(server_tool->update);
	RemoveCommands(server);
	RemoveChannels(server);
	return;
}
void    close_server_connection(int *fd)
{
	static void RemoveSender(Server *);
	Server *server, *s2;

	if (*fd < 0)
		return;
	fl_remove_io_callback(*fd, FL_READ, ircserver_cb);
	/*
	 * do a reverse server  check..make sure we actually have a server connected 
	 */
	s2 = search_server_tree_byfd(*fd);
	assert (s2 && s2->fd == *fd);
	server = search_server_tree_byname(s2->servname, s2->port);
	assert(!strcasecmp(server->servname, s2->servname)&& server->port== s2->port);
	if(gflags&VERBOSE_CLIENT)
	berr_msg(PROMPT "Sending %s (%d) the QUIT cmd..", server->servname, server->port);
	if (1) {		/* for the sake of politeness... */
		char   *buf = (char *) malloc(sizeof(char) * (strlen(quit_text) + 10));
		if (buf) {
			sprintf(buf, "QUIT :%s\n", quit_text);
			writen(server->fd, buf, strlen(buf));
			free(buf);
		}
	}
	close(server->fd);
	UpdateWindows(*fd, 0);
	ClearLag(server);
	del_server(&servFdTree, server, BY_FD);
	*fd = server->fd = -1;
	if(server->connecting>0) server->connecting=0;
	if(server_tool) fl_trigger_object(server_tool->update);
	RemoveCommands(server);
	RemoveChannels(server);
}
static gChannel *
        get_nextgchannel(const Server * s)
{
/* 
 * search global channel list for  servername */
	gChannel *p = chanStart;

	if (!s)
		return NULL;
	while (p != chanEnd)
		if (
			   !strcasecmp(p->server, s->servname) &&
			   s->port == p->port
		   )
			return p;
		else
			p = p->next;
	return NULL;
}

static void RemoveChannels(Server * s)
{
	gChannel *p;
	short   i = 0;

	assert(s);
	while ((p = get_nextgchannel(s))) {
		gChannel *q;

		free(p->name);
		free(p->server);
		q = p->next;
		if (q == chanEnd)
			chanEnd = p;
		else
			*p = *q;
		free(q);
		i++;
	}

}
static void RemoveCommands(Server * s){
/* remove all commands sent to this server */
	CmdSent *p=cmdsentStart, *q;

	while(p!=cmdsentEnd){
		fprintf(stderr,"%p %p\n",p->win->server, s);
		if(p->win->server==s){
			fprintf(stderr, "removing %s %s\n",p->cmd,p->arg);
			q=p->next;
			free(p->cmd); free(p->arg);
			if(q==cmdsentEnd) cmdsentEnd=p; else *p=*q; free(q);
		}
		p=p->next;
	}
}

int     sendto_server(Winstruct * win, const char *buf)
{
	int     n;

	assert(win && buf);
	if (!win->server || win->server->fd < 0) {
		fl_set_object_label(win->chanwin->WinStatusLine,
				    "You are not connected to a server");
		fl_ringbell(0);
		return -1;
	}
	if(win->server->connecting>0){
		add_cmdoncon(SERV_CMD,(char *)buf,
		win->server->servname, win->server->port);
		return 0;
	}
	n = strlen(buf);
	if (writen(win->server->fd, buf, n) != n) {
		if(errno!=EAGAIN){
		fl_ringbell(0);
		berr_ret("Write error to port %d of %s", 
		win->server->port, win->server->servname);
		close_server_connection(&win->server->fd);
		}
	}
	return 0;
}


void    close_server_connectionbyname(char *servname, int port)
{
	Server *server;

	server = search_server_tree_byname(servname, port);

	if (!server)
		err_msg("%s: No connection to port %d found", servname, port);
	else if (server->fd < 0)
		err_msg("You are not connected to port %d of %s", 
		server->port, server->servname, "");
	else {
	WinList *p;
	build_winlistbyfd(server->fd);
	p=servWin;
	while (p) {
		ClearWinChanList(p->win);
		UpdateStatus(p->win);
		p=p->next;
	}
	new_close_connection(&server->fd, NULL, 1);
	}
}

static char *
        get_serverline(FILE * fp)
{
/* 
 * read one line from serverlist  */
	char    p[513];
	char   *buf;

	while (fgets(p, 512, fp)) {
		p[strlen(p) - 1] = '\0';
		if (!(buf = clean(p)) || *buf == COMMENT || *buf == SERV_DELIM_CHR)
			continue;
		else
			return buf;
	}
	return NULL;
}

static int get_server(FILE * fp, Server * server)
{
/* 
 * get line of form  server:port:passwd:nick:email:ircname return <0: EOF
 * return >1:item format error.. try next item.
 * 
 */
	char   *buf;
	char   *tmp;/*password, *nick, *ircname, *email;*/
	size_t  ll;
	int     i;
	char   *ptr;

	if ((buf = get_serverline(fp)) == NULL)
		return EOF;
	ll = strlen(buf);
	
	tmp = (char *) malloc(sizeof(char) * ll);
	if (!tmp) {
		err_ret("%s (%d)", __FILE__, __LINE__);
		return errno;
	}
	ptr = tmp;
	while (*buf && *buf != ':')
		*ptr++ = *buf++;
	if (*buf == ':') {
		*ptr = '\0';
		buf++;
	}
	if (!(*tmp))
		return -2;
	else server->servname = strdup(tmp);
	*tmp=0;
	ptr = tmp;
	while (*buf && *buf != ':')
		*ptr++ = *buf++;
	if (*buf == ':') {
		*ptr = '\0';
		buf++;
	}
	i = atoi(tmp);
	if (i < 0)
		*tmp = '\0';
	ll = strlen(tmp);
	while ((i = atoi(tmp)) > USHRT_MAX)
		*(tmp + --ll) = '\0';
	if ((server->port = i) == 0)
		server->port = DEFAULT_PORT;
	*tmp=0;
	ptr = tmp;
	while (*buf && *buf != ':')
		*ptr++ = *buf++;
	if (*buf == ':') {
		*ptr = '\0';
		buf++;
	}
	if (!(*tmp)) {
		server->password = NULL;
	}else
		server->password = strdup(tmp);
	*tmp=0;
	ptr = tmp;
	while (*buf && *buf != ':')
		*ptr++ = *buf++;
	if (*buf == ':') {
		*ptr = '\0';
		buf++;
	}
	if (!(*tmp))
		server->nick = strdup(Name);
	else server->nick=strdup(tmp);
	*tmp=0;
	ptr = tmp;
	while (*buf && *buf != ':')
		*ptr++ = *buf++;
	if (*buf == ':') {
		*ptr = '\0';
		buf++;
	}
	if (!(*tmp)) {
		server->email = (char *) malloc(sizeof(char) * (strlen(Name) + strlen(host) + 3));
		sprintf(server->email, "%s@%s", Name, host);
	}else
	server->email=strdup(tmp);
	*tmp=0;
	ptr = tmp;
	while ((*ptr++ = *buf++));
	if (!(*tmp)) {
		server->ircname = (char *) malloc(sizeof(char) * (strlen(sula_NAME sula_VERSION) + 8));
		strcpy(server->ircname, sula_NAME sula_VERSION " user");
	}else
	server->ircname=strdup(tmp);
	free(tmp);
	server->mode = NULL;
	server->flag = SERVER_FLAG_RESET;
	server->state = -1;
	server->fd = -1;
	server->lastInvite = NULL;
	server->left = server->right = NULL;
	server->iNick=get_list_bylabel("default");
	if(server->iNick<0){
		server->iNick=new_nicklist("default");
		if(server->iNick<0) err_sys("error");
	}
	aNickList[server->iNick].current=
		add_nick_to_list(server->iNick,server->nick);
	return 0;
}

extern Winstruct *new_win(void)
{
	Winstruct *win= new_chan_win();
	if (win)
		set_object_attribs(win);
	return win;
}
static int win_alloc(){
	int i;
	if(winstruct==NULL)
		winstruct=malloc(sizeof(Winstruct)*WIN_ALLOC);
	else
		winstruct=realloc(winstruct, (win_count+WIN_ALLOC)*sizeof(Winstruct));
	if(winstruct==NULL) return (-1);
	for (i=win_count;i< win_count+WIN_ALLOC; i++){
		winstruct[i].chanwin=NULL;
		winstruct[i].server=NULL;	/* new_process_cmd needs this*/
	}


	win_count+=WIN_ALLOC ;
	return (0);
}
static int win_raw_cb(FL_FORM *form, void *xevent){
	
	switch(((XEvent *)xevent)->type){
		case KeyPress:
		  switch(XLookupKeysym((XKeyEvent *)xevent,0)){
		  case XK_Tab:
			{
		/* use TAB key to insert nicks into input field */
			fl_trigger_object(((FD_chanwin *)form->fdui)->tab);
			return FL_PREEMPT;
			}
		  case XK_Escape:
		  /* use ESC key to show/hide control window! */
		    {
		  	static FL_Coord x=0, y=0;
			if(gflags&CTRL_WIN_SHOWN){
				x=main_form->main_form->x;
				y=main_form->main_form->y;
				fl_hide_form(main_form->main_form );
				gflags &= ~CTRL_WIN_SHOWN;
			}else
			{
			 	fl_set_form_position(main_form->main_form,x,y);
				fl_show_form(main_form->main_form,
				FL_PLACE_POSITION, FL_FULLBORDER, 
				sula_NAME sula_VERSION ": Control");

				gflags |= CTRL_WIN_SHOWN;
			}
			return FL_PREEMPT;
		 }
		  }/*switch*/
		default: break;
	}
	return !FL_PREEMPT;
}


extern Winstruct *new_chan_win(void){
	int i;
	if(winstruct==NULL && win_alloc()) 
		return (NULL);
retry:
	for(i=0;i< win_count; i++)
		if(winstruct[i].chanwin==NULL){
		Winstruct *win;
	Go     *p;
	win=& winstruct[i];
	win->chanwin= create_form_chanwin();
	fl_set_form_atclose(win->chanwin->chanwin, dont_close_win, 0);
	win->chanwin->bar->u_ldata = (long)i;		/* this window; used in posthandler etc*/
	/*win->chanwin->clock->u_ldata = (long) 0;*/	/* no go pup */
	win->chanwin->status->u_ldata = (long) 0;	/* no of entries in Go_menu */
	win->chanwin->chanusers->u_vdata = NULL;	/* chanusers not open*/
	win->chanwin->find->u_vdata = NULL;		/* find not open*/
	win->chanwin->minput->u_vdata = NULL;

	/*fl_set_object_prehandler(win->chanwin->sinput, set_cursor);*/
	win->chanwin->sinput->u_ldata = (long) XC_xterm;
	fl_set_object_prehandler(win->chanwin->sinput, set_cursor);

	win->chanwin->browser->u_ldata = (long) XC_right_ptr;
	fl_set_object_prehandler(win->chanwin->browser, set_cursor);
	
	win->server = NULL;
	win->chanwin->cmd_fwd->u_ldata = (long) histCur;
	win->chanwin->tab->u_ldata=(long) senderCur;/* TAB key */
	win->chanStart = (Channel *) malloc(sizeof(Channel));
	if (win->chanStart == NULL)
		goto errur;
	win->chanStart->left = win->chanStart->right = win->chanCur = win->chanStart;
	win->dccStart = (DCCChat *) malloc(sizeof(DCCChat));
	if (win->dccStart == NULL)
		goto errur;
	win->dccStart->left = win->dccStart->right = win->dccCur = win->dccStart;
	win->lastJoin = (NoChannel *) malloc(sizeof(NoChannel));
	win->lastPart = (NoChannel *) malloc(sizeof(NoChannel));
	if (!win->lastJoin || !win->lastPart)
		goto errur;
	win->lastJoin->name = NULL;
	win->lastJoin->key = NULL;
	win->lastPart->name = NULL;
	win->lastPart->key = NULL;
	win->goStart = win->goEnd = (Go *) malloc(sizeof(Go));
	if (win->goStart == NULL)
		goto errur;
	p = win->goStart;
	win->goStart = (Go *) malloc(sizeof(Go));
	if (win->goStart == NULL)
		goto errur;
	win->goStart->name = strdup("#sula");	/* okay.otherwise free() gets stuck */
	win->goStart->key = NULL;
	win->goStart->next = p;
	win->query_nick = NULL;
	win->chanwin->go_menu->u_ldata=-1;
	win->chanwin->file_menu->u_ldata=-1;
	win->chanwin->go_menu->u_ldata=-1;
	win->chanwin->bookmark_menu->u_ldata=-1;
	win->chanwin->edit_menu->u_ldata=-1;
	win->chanwin->window_menu->u_ldata=-1;
	win->chanwin->help_menu->u_ldata=-1;
	menu_setup(win);
	go_menu_setup(win);

	fl_register_raw_callback(
		win->chanwin->chanwin,KeyPressMask, win_raw_cb);
	return (&winstruct[i]);
errur:
	perror("Error");
	winstruct[i].chanwin=NULL;
	return (NULL);
	}
      if(win_alloc()) return (NULL);
	goto retry;

}


static int win_ph(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);

extern void set_object_attribs(Winstruct * win)
{

#include "down_arrow.xbm"
#include "up_arrow.xbm"

#include "join.xpm"
#include "part.xpm"
#include "dcc.xpm"
#include "chanusers.xpm"
#include "find.xpm"
#include "minput.xpm"
#include "copy.xpm"
#include "cut.xpm"
#include "paste.xpm"
#include "delete.xpm"
#ifdef 0
Pixmap P, mask;
unsigned width,height;
#include  "win.xpm"

	 P=fl_create_from_pixmapdata(fl_default_window(),
	  win_xpm, &width,&height, &mask,0,0,0);
	fl_set_form_icon(win->chanwin->chanwin, P, mask);
#endif
	fl_set_object_bw(win->chanwin->browser, -2);
	fl_set_object_bw(win->chanwin->bar, -2);
	fl_set_object_bw(win->chanwin->msg_radio_grp, 2);

	fl_set_browser_fontsize(win->chanwin->browser, browser_fontsize);
	fl_set_browser_fontstyle(win->chanwin->browser, browser_fontsyle);
	fl_set_browser_fontsize(win->chanwin->brow2, browser2_fontsize);
	fl_set_browser_fontstyle(win->chanwin->brow2, browser2_fontsyle);
	fl_set_object_color(win->chanwin->browser,browser_colour, 
	browser_selected_colour);
	fl_set_object_color(win->chanwin->brow2,browser2_colour, 
	browser2_selected_colour);
	fl_set_object_lcolor(win->chanwin->browser,browser_textcolour);
	fl_set_object_lcolor(win->chanwin->brow2,browser2_textcolour);
	fl_set_object_lstyle(win->chanwin->sinput, input_fontstyle);
	fl_set_input_color(win->chanwin->sinput, input_colour,FL_BLUE);
	fl_set_choice_fontstyle(win->chanwin->target, target_fontsyle);
	fl_set_choice_fontsize(win->chanwin->target, target_fontsize);
	fl_set_object_color(win->chanwin->target, target_colour,target_colour2);

	fl_set_input_return(win->chanwin->sinput, FL_RETURN_END);
	fl_set_choice_align(win->chanwin->target, FL_ALIGN_LEFT);

	fl_set_button(win->chanwin->r_echo, 1);
	fl_set_button(win->chanwin->type, 0);

	fl_set_button(win->chanwin->history, gflags & HISTORY);
	fl_hide_object(win->chanwin->info);
	fl_hide_object(win->chanwin->away);

	fl_set_pixmapbutton_data(win->chanwin->close_con, closed_xpm);
	fl_set_pixmapbutton_data(win->chanwin->join, join_xpm);
	fl_set_pixmapbutton_data(win->chanwin->part, part_xpm);
	fl_set_pixmapbutton_data(win->chanwin->dcc, dcc_xpm);
	fl_set_pixmapbutton_data(win->chanwin->chanusers, chanusers_xpm);
	fl_set_pixmapbutton_data(win->chanwin->minput, minput_xpm);
	fl_set_pixmapbutton_data(win->chanwin->find, find_xpm);
	fl_set_pixmapbutton_data(win->chanwin->copy, copy_xpm);
	fl_set_pixmapbutton_data(win->chanwin->cut, cut_xpm);
	fl_set_pixmapbutton_data(win->chanwin->paste, paste_xpm);
	fl_set_pixmapbutton_data(win->chanwin->remove, delete_xpm);
	fl_set_bitmapbutton_data(win->chanwin->chan_right, up_arrow_width,
				 up_arrow_height, up_arrow_bits);
	fl_set_bitmapbutton_data(win->chanwin->chan_left, dwn_arrow_width,
				 dwn_arrow_height, dwn_arrow_bits);

	
	
	fl_addto_choice(win->chanwin->operation,
	  "msg|notice|action|whois|chat|clientinfo|finger|ping|time|version"
	      "|userinfo|chelp|kick *|op|deop|invite|userhost|ison|whowas|query");
	fl_addto_choice(win->chanwin->action, "notice|action|msg");
	fl_set_form_minsize(win->chanwin->chanwin, 514, 632);
	fl_set_form_maxsize(win->chanwin->chanwin, 632, 632);
	/*fl_set_object_posthandler(win->chanwin->sinput, win_ph);*/
	fl_set_object_posthandler(win->chanwin->join, win_ph);
	fl_set_object_posthandler(win->chanwin->part, win_ph);
	fl_set_object_posthandler(win->chanwin->dcc, win_ph);
	fl_set_object_posthandler(win->chanwin->chanusers, win_ph);
	fl_set_object_posthandler(win->chanwin->minput, win_ph);
	fl_set_object_posthandler(win->chanwin->paste, win_ph);
	fl_set_object_posthandler(win->chanwin->copy, win_ph);
	fl_set_object_posthandler(win->chanwin->cut, win_ph);
	fl_set_object_posthandler(win->chanwin->remove, win_ph);
	fl_set_object_posthandler(win->chanwin->lag, win_ph);
	fl_set_object_posthandler(win->chanwin->away, win_ph);
	fl_set_object_posthandler(win->chanwin->find, win_ph);
	fl_set_object_posthandler(win->chanwin->info, win_ph);
	fl_set_object_posthandler(win->chanwin->close_con, win_ph);
	fl_set_object_posthandler(win->chanwin->chan_left, win_ph);
	fl_set_object_posthandler(win->chanwin->chan_right, win_ph);
	fl_set_object_posthandler(win->chanwin->topic, win_ph);
	fl_set_object_posthandler(win->chanwin->r_quote, win_ph);
	fl_set_object_posthandler(win->chanwin->r_query, win_ph);
	fl_set_object_posthandler(win->chanwin->r_channel, win_ph);
	fl_set_object_posthandler(win->chanwin->r_dcc_chat, win_ph);
	fl_set_object_posthandler(win->chanwin->r_echo, win_ph);
	fl_set_object_posthandler(win->chanwin->type, win_ph);
	fl_set_object_posthandler(win->chanwin->action, win_ph);
	fl_set_object_posthandler(win->chanwin->go, win_ph);
	fl_set_object_posthandler(win->chanwin->nick, win_ph);
	fl_set_object_posthandler(win->chanwin->history, win_ph);
	fl_set_object_posthandler(win->chanwin->do_it, win_ph);
	fl_set_object_posthandler(win->chanwin->operation, win_ph);
	fl_set_object_posthandler(win->chanwin->update, win_ph);

	fl_trigger_object(win->chanwin->update);
}

static Ph ph;
void do_tips(int id, void *data)
{
        Ph     *p = data;

        fl_show_oneliner(p->s, p->ob->form->x + p->ob->x,
                         p->ob->form->y + p->ob->y + p->ob->h + 2);
        timeoutID = fl_add_timeout((double) tips_tmout, do_tips, (void *) p);
}

static int win_ph(FL_OBJECT * ob, int event, FL_Coord mx, FL_Coord my, int key, void *xev)
{

	if (!(gflags & SHOW_TIPS))
		return 0;
	else{
	FD_chanwin *chanwin=(FD_chanwin *)ob->form->fdui;
	ph.ob = ob;
	ph.s = NULL;
	if (ob == chanwin->join)
		ph.s = "join new|last|invite";
	else if (ob == chanwin->part)
		ph.s = "part current|all|last";
	else if (ob == chanwin->dcc)
		ph.s = "chat accept|in new win|close";
	else if (ob == chanwin->chanusers)
		ph.s = "channel members|list|update";
	else if (ob == chanwin->away){
		Winstruct *win;
		win = &winstruct[chanwin->bar->u_ldata];
		ph.s = (win->server && win->server->flag & SET_AWAY) ? 
		"mark present" : "mark away";
	}
	else if (ob == chanwin->minput)
		ph.s = "compose multiline message";
	else if (ob == chanwin->copy)
		ph.s = "copy from 1. | 2. browser";
	else if (ob == chanwin->cut)
		ph.s = "cut from 1. | 2. browser";
	else if (ob == chanwin->remove)
		ph.s = "delete from 1. | 2. browser";
	else if (ob == chanwin->paste)
		ph.s = "paste into input|browsers";

	else if (ob == chanwin->find)
		ph.s = "find line with text";
	else if (ob == chanwin->info)
		ph.s = "not in use..";
	else if (ob == chanwin->close_con) {
		int i=chanwin->bar->u_ldata;
		Winstruct *win;
		if(i>win_count) err_quit("err");
		win = &winstruct[i];
		if (win->server && win->server->fd > -1)
			ph.s = "disconnect from server";
		else
			ph.s = "reconnect to server";
	}
	else if (ob == chanwin->lag)
		ph.s= "force lag update";
	else if (ob == chanwin->chan_left)
		ph.s = "<< channel | chat";
	else if (ob == chanwin->chan_right)
		ph.s = ">> channel| chat";
	else if (ob == chanwin->topic)
		ph.s = "change|clear|query topic";
	else if (ob == chanwin->r_quote)
		ph.s = "quote input to server";
	else if (ob == chanwin->r_query)
		ph.s = "feed input to query";
	else if (ob == chanwin->r_channel)
		ph.s = "feed input to channel";
	else if (ob == chanwin->r_dcc_chat)
		ph.s = "feed input to DCC chat";
	else if (ob == chanwin->r_echo)
		ph.s = "echo input";
	else if (ob == chanwin->type)
		ph.s = fl_get_button(ob) ? "deactivate deflt msg type":"use default msg type" ;
	else if (ob == chanwin->action)
		ph.s = "default message type";
	else if (ob == chanwin->history)
		ph.s = fl_get_button(ob) ? "stop operation logging" : 
		"resume operation log";
	else if (ob == chanwin->go)
		ph.s = "execute op using this msg";
	else if (ob == chanwin->nick)
		ph.s = "set nick|mode|channel mode";
	else if (ob == chanwin->do_it)
		ph.s = "<- execute op";
	else if (ob == chanwin->operation)
		ph.s = "operation";
	else if (ob == chanwin->update)
		ph.s = "update targets ->";
	else
	return 0;
	if (event == FL_ENTER)
                	timeoutID = fl_add_timeout((double) tips_tmout,
                     	do_tips, (void *) &ph);
        else if (event == FL_LEAVE || event == FL_PUSH) {
                	fl_hide_oneliner();
                	if (timeoutID) {
                         	fl_remove_timeout(timeoutID);
                         	timeoutID = 0;
                 	}
         }
        }
	return 0;
}


