/* parser.c     -parse server output distributed as part of sula - an irc
 * client for X
 * 
 * (c) 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.
 * 
 */

#include <forms.h>
#include <errno.h>
#include <time.h>
#include <stdarg.h>

#include "aux.h"
#include "sula.h"
#include "chanwin.h"
#include "global.h"
#include "parser.h"
#include "mode.h"
#include "user.h"
#include "setting.h"
#include "ignore.h"
#include "cmd.h"
#include "script.h"

Winstruct *Win;			/* parser window */
int     X;			/* X=Win-winstruct ; window index*/
char   *Msg;

typedef struct TparsWinList {
	char   *channame;
	Winstruct *win;
	struct TparsWinList *next;
} parsWinList;
parsWinList *parsWin = NULL;

static void free_win_list(void)
{
	parsWinList *p;

	while (parsWin) {
		p = parsWin;
		parsWin = parsWin->next;
		free(p->channame);
		free(p);
	}
}

static int winlist_add(Winstruct * win, const char *channame)
{
	parsWinList *p = parsWin;

	parsWin = (parsWinList *) malloc(sizeof(parsWinList));
	if (!parsWin)
		return errno;
	parsWin->win = win;
	parsWin->channame = strdup(channame);
	parsWin->next = p;
	return 0;
}

static void build_winlist_byuser(int fd, const char *sender)
{
/* locate windows and channels this server with  channels for which user is
 * also a member chanToo: also add channel names to window list */
	int     i;

	free_win_list();
	for (i = 0; i < win_count; i++)
		if (winstruct[i].chanwin && winstruct[i].server &&
		    winstruct[i].server->fd == fd) {
			Channel *current = winstruct[i].chanStart->right;

			while (current != winstruct[i].chanStart) {
				if (get_user_byuser(current, sender))	/* found user */
					winlist_add(&winstruct[i], current->name);
				current = current->right;
			}
		}
}

extern Winstruct *get_win_bychanname(int fd, const char *channame)
{
/* locate window using channel name and server fd 
 * 
 * TODO: */
	int     i;

	if (fd < 0)
		return NULL;
	for (i = 0; i < win_count; i++)
		if (winstruct[i].chanwin && winstruct[i].server &&
		    winstruct[i].server->fd == fd) {
			Channel *current = winstruct[i].chanStart->left;

			while (current != winstruct[i].chanStart) {
				if (!strcasecmp(channame, current->name))
					return &winstruct[i];
				else
					current = current->left;
			}
		}
	return NULL;
}

extern Winstruct *getwinbycmd(Server * s, const char *cmd, const char *args,
			      int remove)
{
	CmdSent *p = cmdsentStart;

#if DEBUG>4
	fprintf(stderr, "%s(%d) getwinbycmd\n", __FILE__, __LINE__);
#endif
	while (p != cmdsentEnd)
		if (p->win->server && p->win->server == s &&
		    !strcasecmp(p->cmd, cmd) && (!args || !strcasecmp(p->arg, args)))
			break;
		else {
#if DEBUG>4
			fprintf(stderr, "(-%s-%s-)", cmd, args);
			fprintf(stderr, "at:%s:%s:", p->cmd, p->arg);

#endif
			p = p->next;
		}
#if DEBUG>4
	fprintf(stderr, "%s(%d) getwinbycmd ok\n", __FILE__, __LINE__);
#endif
	if (p == cmdsentEnd) {
		return NULL;
	}
	else {
		if (remove) {
			long    w = (long) p->win;
			CmdSent *q = p->next;

			free(p->cmd);
			free(p->arg);
			if (q == cmdsentEnd)
				cmdsentEnd = p;
			else {
				*p = *q;
				free(q);
			}
			return ((Winstruct *) w);	/*cs->win; */
		}
		else
			return p->win;

	}

}

extern User *get_user_byuser(const Channel * chan, const char *sender)
{
/* search channel for user named sender (nick) */

	if (sender && chan) {	/*dont use assert!!! */
		User   *p = chan->userStart;

		while (p != chan->userEnd)
			if (!strcasecmp(p->name, sender))
				return p;
			else
				p = p->next;
	}
	return NULL;
}


void    prepare_server_output(int fd, const char *line)
{

	static void parse_server_output(int);
	char   *tmp, *ptr, *pp;

	if (!(*line))
		return;
	Win = search_winlistbyfd(fd, &X);
	assert(Win);
	Win->server->read += strlen(line) + 1;
	tmp = strdup(line);
	if (!tmp) {
		err_ret("Error");
		return;
	}
	ptr = tmp;
	while ((pp = strnstr(ptr, "\n"))) {
		size_t  x = strlen(pp) - 1 /*, xstart=x */ ;

		Msg = pp;

		if (*(Msg + x - 1) == '\r') {
			ptr++;
			*(Msg + --x) = '\0';
		}
		/*if(xstart!=x) */ *(Msg + x) = '\0';
		if (*Msg == ':')
			Msg++;
		if (*Msg)
			parse_server_output(fd);
		ptr += x + 1;
		free(pp);
	}
	Msg = NULL;
	free(tmp);
}

typedef struct Tserver_cmd {
	char    junk;
	char   *name;
	void    (*func) (void);
} server_cmd;

static void p_msg(void);
static void p_notice(void);
static void p_wallops(void);
static void p_pong(void);
static void p_ping(void);
static void p_error(void);
static void p_junk(void);
static void p_silence(void);

/* needed if >1 window connected to server */
#define	NICK_	0
#define	CHAN_	1
#define	JOIN_	2
#define QUIT_	3
#define NONE_	4

static server_cmd serv_cmd[] =
{
	/* keeep sorted by name */
	{NONE_, "INVITE", p_invite},
	{JOIN_, "JOIN", p_join},
	{NONE_, "KICK", p_kick},
	{CHAN_, "MODE", p_mode},
	{NICK_, "NICK", p_nick},
	{NONE_, "NOTE", p_note},
	{CHAN_, "NOTICE", p_notice},
	{CHAN_, "PART", p_part},
	{NONE_, "PONG", p_pong},
	{CHAN_, "PRIVMSG", p_msg},
	{QUIT_, "QUIT", p_quit},
	{NONE_, "SILENCE", p_silence},
	{CHAN_, "TOPIC", p_topic},
	{NONE_, "WALLOPS", p_wallops}
};

static void parse_server_output(int fd)
{

	server_cmd *p = serv_cmd;
	static int binary(server_cmd *, const char *var, int n);
	int     x;
	char    command[32];

	if (X == 1)
		goto start_parsing;
	else {
		/*char **argv;
		 * char *sender;
		 * char *tmp; */
		Winstruct *win = NULL;

		if (sscanf(Msg, "%*s %s", command) < 1)
			return;
		/*tmp=strdup(Msg);
		 * msg_args(tmp, &sender, &x, &argv);
		 * if(!sender) return; */
		if (atoi(command) > 0)
			goto start_parsing;
		else {
			char    what;

			x = binary(p, command, sizeof(serv_cmd) / sizeof(server_cmd));
			if (x < 0)
				goto start_parsing;
			switch ((what = serv_cmd[x].junk)) {
			    case CHAN_:
				    {
					    char   *channel = (char *) malloc(sizeof(char) * strlen(Msg));

					    if (sscanf(Msg, "%*s %*s %s", channel) < 1)
						    return;
					    if (*channel == '#' || *channel == '&')
						    win = get_win_bychanname(fd, channel);
					    if (win)
						    Win = win;
					    free(channel);
					    break;
				    }
			    case JOIN_:
				    {
					    char   *channel = (char *) malloc(sizeof(char) * strlen(Msg));
					    char   *nick = (char *) malloc(sizeof(char) * strlen(Msg));

					    if (sscanf(Msg, "%[^!]!%*s JOIN :%[^\r\n]\r\n", nick, channel) < 2) {
						    fprintf(stderr, "error %s\n", Msg);
						    return;
					    }
					    channel = unescape(channel);
					    if (!strcasecmp(nick, Win->server->nick))
						    win = getwinbycmd(Win->server, "JOIN", channel, 1);
					    else
						    win = get_win_bychanname(fd, channel);
					    if (win)
						    Win = win;
					    free(channel);
					    free(nick);
					    break;
				    }
			    case NICK_:
			    case QUIT_:
				    {

					    char   *nick = NULL, *new_nick = NULL;
					    char   *sender = (char *) malloc(sizeof(char) * strlen(Msg));
					    parsWinList *p;
					    char   *buf = NULL;
					    char   *tmp;

					    if (sscanf(Msg, "%s", sender) < 1)
						    return;
					    tmp = sender;
					    nick = (char *) malloc(sizeof(char) * strlen(sender));

					    if (sscanf(tmp, "%[^!]!%*s", nick) < 1)
						    return;
					    if (!strcasecmp(nick, Win->server->nick))
						    goto start_parsing;
					    if (what == NICK_) {
						    new_nick = (char *) malloc(sizeof(char) * strlen(Msg));

						    if (sscanf(Msg, "%*s %*s :%[^\r\n]\r\n)", new_nick) < 1) {
							    free(new_nick);
							    free(nick);
							    free(sender);
							    return;
						    }
						    buf = (char *) malloc(sizeof(char) * (strlen(nick) +
											  strlen  (new_nick) + 32));

						    sprintf(buf, "%s is now known as %s", nick, new_nick);
					    }
					    else {
						    char   *reason = (char *) malloc(sizeof(char) * (strlen(Msg)));

						    if (sscanf(Msg, "%*s %*s :%[^\r\n]\r\n", reason) < 1)
							    reason = strdup("?");
						    buf = (char *) malloc(sizeof(char) * (strlen(nick) +
						    strlen  (reason) + 32));

						    sprintf(buf, "%s has signed off (%s)", nick, reason ? reason : "");
						    free(reason);
					    }
					    build_winlist_byuser(fd, nick);
					    if (!parsWin)
						    return;
					    Win = parsWin->win;
					    p = parsWin->next;
					    while (p) {
						    if (what == QUIT_)
							    RemoveFromChanUsers(p->win, p->channame, nick);
						    else
							    RenameChanUser(p->win, p->channame, nick, new_nick);
						    fl_addto_browser(p->win->chanwin->brow2, buf);
						    p = p->next;
					    }
					    free(buf);
					    free(nick);
					    free(sender);
					    free(new_nick);
					    break;
				    }
			    default:
				    break;
			}	/*switch */
		}		/* else */
	}
      start_parsing:
	if (sscanf(Msg, "%*s %s", command) < 1)
		return;
	/*fprintf(stderr,"%s",Msg);*/
	if ((x = atoi(command)) > 0)
		p_numeric(x);
	else {
		x = binary(p, command, sizeof(serv_cmd) / sizeof(server_cmd));
		if (x > -1)
			(*serv_cmd[x].func) ();
		else if (!strncmp(Msg, "PING ", 5))
			p_ping();
		else if (!strncmp(Msg, "ERROR", 5))
			p_error();
		else if (!strncmp(Msg, "NOTICE", 6))
			p_note();
		else
			p_junk();
	}
       /*	fprintf(stderr,"-2");*/
}

static int binary(server_cmd * p, const char *var, int n)
{
	register int low, high, mid, cond;

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

static void p_msg(void)
{
	p_privmsg(NOTICE + 1);
}
static void p_notice(void)
{
	p_privmsg(NOTICE);
}

/****************************************************************/
/* */
/* w a l l o p s                                        */
/* */
/****************************************************************/

/* what's that????????? */
static void p_wallops(void)
{
	if (check_msg(WALLOPS, Msg))
		return;
	berr_msg("[%s] %s", Win->server->servname, Msg);

}

/****************************************************************/
/* */
/* P I N G                                              */
/* */
/****************************************************************/

static void p_ping(void)
{

	if (check_msg(PING, Msg))	/* warning: not advisable */
		return;
	else {
		char   *sender;
		sender = malloc(sizeof(char) * strlen(Msg));

		if (sscanf(Msg, "%*s %[^\n]\n", sender) > 0) {
			char   *buf = malloc(sizeof(char) * (strlen(sender) + 8));

			sprintf(buf, "PONG %s\n", *sender == ':' ? sender + 1 : sender);
			/* send reply directly to server! -fotang */
			writen(Win->server->fd, buf, strlen(buf));
			/*sendto_server(Win, buf); */
			free(buf);
		}
		free(sender);
	}
}

/****************************************************************/
/* */
/* P O N G                                              */
/* */
/****************************************************************/

static void p_pong(void)
{

	if (check_msg(PONG, Msg))
		return;
	else if (gflags & SHOW_RAW_MSG)
		fl_addto_browser(Win->chanwin->brow2, Msg);
	else {
		char   *buf;
		buf = (char *) malloc(sizeof(char) * (strlen(Msg) + 32));

		sprintf(buf, "%s: received PONG from %s", nextstr(Msg, 1),
			nextstr(Msg, 3));
		if (X == 1)
			fl_addto_browser(Win->chanwin->brow2, buf);
		else
			berr_msg(buf);
		free(buf);
	}
}

/****************************************************************/
/* */
/* S I L E N C E                                        */
/* */
/****************************************************************/

static void p_silence(void)
{
	if (check_msg(SILENCE, Msg))
		return;
	else if (gflags & SHOW_RAW_MSG) {
		if (X == 1)
			fl_addto_browser(Win->chanwin->brow2, Msg);
		else
			berr_msg(Msg);
	}
	else {
		char   *buf;
		char   *silenced = malloc(sizeof(char) * strlen(Msg));

		if (sscanf(Msg, "%*s %*s %s", silenced) < 1)
			return;
		buf = (char *) malloc(sizeof(char) * (strlen(Msg) + 24));

		sprintf(buf, "@i%s silencing %s",
		      *silenced == '+' ? "Now" : "No longer", silenced + 1);
		if (X == 1)
			fl_addto_browser(Win->chanwin->brow2, buf);
		else
			berr_msg("%s: %s", Win->server->servname, buf + 2);
		free(buf);
		free(silenced);
	}
}

/****************************************************************/
/* */
/* E R R O R                                    */
/* */
/****************************************************************/

static void p_error(void)
{

	if (check_msg(ERROR, Msg))
		return;
	else if (gflags & SHOW_RAW_MSG)
		fl_addto_browser(Win->chanwin->brow2, Msg);
	else {
		char   *buf;
		buf = (char *) malloc(sizeof(char) * (strlen(Msg)));

		if ((sscanf(Msg, "ERROR :%[^\n]\n", buf) < 1))
			return;
		if (X == 1)
			fl_addto_browser(Win->chanwin->brow2, buf);
		else
			berr_msg("%s says: %s", Win->server->servname, buf);
		free(buf);
	}
}
/****************************************************************/
/* */
/* U N K N O W N = J U N K                              */
/* */
/****************************************************************/
static void p_junk(void)
{
	if (check_msg(JUNK, Msg))
		return;
	else if (gflags & SHOW_RAW_MSG)
		fl_addto_browser(Win->chanwin->brow2, Msg);
	else {
		if (X == 1) {
			char   *buf;
			buf = (char *) malloc(sizeof(char) * (strlen(Msg) + strlen(
					      Win->server->servname) + 48));

			sprintf(buf, "(Unknown stuff from %s) %s",
				Win->server->servname, Msg);
			fl_addto_browser(Win->chanwin->brow2, buf);
			free(buf);
		}
		else
			berr_msg("(Unknown stuff from %s) %s", Win->server->servname, Msg);
	}
}
/*-----------------------------------------------------------------------*/

extern void load_userlist(Winstruct * win, const char *channame, int do_cur_chan)
{
	/* do_cur_chan: continue only if channame is same as choice text 
	 * channame can be NULL
	 */

	if (!win || !win->chanwin->chanusers->u_vdata)
		return;
	if (win->chanStart != win->chanCur) {
		User   *u;
		char    buf[256];
		Channel *chan;
		FD_chanusers *f = (FD_chanusers *) win->chanwin->chanusers->u_vdata;
		char   *channel = (char *) fl_get_choice_text(f->channel);

		chan = channame ? get_chan_byname(win, channame) : win->chanCur;
		if (!chan || (do_cur_chan && (!channel || strcasecmp(channel, channame))))
			return;
		u = chan->userStart;
		fl_freeze_form(f->chanusers);
		fl_clear_browser(f->browser);
		while (u != chan->userEnd) {
			sprintf(buf, "@f%-*.*s %-4s %s (%s)", MaxNickLen + 2, MaxNickLen,
				u->name, u->mode, u->address,
				u->ircname ? u->ircname : "(try update?)");
			fl_addto_browser(f->browser, buf);
			u = u->next;
		}
		fl_unfreeze_form(f->chanusers);
		fl_set_choice_text(f->channel, chan->name);
	}
	else
		fl_clear_browser(((FD_chanusers *) win->chanwin->chanusers->u_vdata)->browser);

}

extern int new_addto_chanusers(Winstruct * win, const char *channame, User * u)
{
	Channel *chan;
	User   *q;

	assert(u && channame && win);
	chan = get_chan_byname(win, channame);
	if (chan == NULL) {
		return -1;
	}

	q = chan->userStart;
	while (q != chan->userEnd)
		if (!strcasecmp(q->name, u->name))
			break;
		else
			q = q->next;
	if (q != chan->userEnd) {	/*found; update just ircname */
		q->ircname = strdup2(u->ircname);
		q->mode = strdup2(u->mode);
		return 0;
	}

	q = chan->userStart;
	chan->userStart = (User *) malloc(sizeof(User));
	if (!chan->userStart) {
		err_sys("%s %d", __FILE__, __LINE__);
	}
	chan->userStart->name = strdup(u->name);
	chan->userStart->address = strdup(u->address);
	chan->userStart->ircname = strdup2(u->ircname);
	chan->userStart->mode = strdup2(u->mode);
	chan->userStart->next = q;
	return 0;

}

static Sender *in_list(const char *sender)
{
	Sender *p = senderStart->left;

	while (p != senderStart)
		if (!strcasecmp(p->sender, sender))
			return p;
		else
			p = p->left;
	return NULL;
}
extern void addto_sender(const char *sender, u_int what)
{
	/* flag=SS_XXX, should be <16 (16bits). to increaes later */
	Sender *p;

	assert(what < 16);
	what %= 16;
	p = in_list(sender);
	if (!p) {
		char   *ptr;

		p = (Sender *) malloc(sizeof(Sender));
		if (p == NULL)
			return;
		if (!(p->sender = strdup(sender)))
			return;
		ptr = (char *) sender;
		while (*ptr && *ptr++ != '!');	/* compute nick length */
		p->what = (1 << what) | (*sender != '!' && *ptr ? ((ptr - sender) << 16) : 0);
		p->left = senderCur;
		p->right = senderCur->right;
		senderCur->right->left = p;
		senderCur->right = p;
	}
	else
		p->what |= (1 << what);
	senderCur = p;
}

extern Channel *get_chan_byname(const Winstruct * win, const char *name)
{
	Channel *current;

	/*assert(name && win); */
	current = win->chanStart->right;
	while (current != win->chanStart) {
		if (!strcasecmp(current->name, name)) {
			return current;
		}
		else {
			current = current->right;
		}
	}
	return NULL;

}
extern User *search_userlist(const Channel * chan, const char *nick, int *found)
{
/* search a channel for a a nick
 * 
 * function still being used?? what#s this for? */
	User   *p;

	p = chan->userStart;
	while (p != chan->userEnd)
		if (!strcasecmp(p->name, nick)) {
			*found = 1;
			return p;
		}
		else
			p = p->next;
	*found = 0;
	return p;
}

extern char *format_msg(char *fmt,...)
{
#warning (fotang) buf--- realloc(2) instead?
	char   buf[MAXLINE + 64];		/* buffer for formatted string,temporal solution */
	char   *p, *ptr;
	char    stop = 0;	/* stop!=0: no more program supplied variables in the string */
	char   *server = 0;	/*server or nick@domain */
	char   *nick = NULL, *login = NULL, *host = NULL, *dest = NULL;		/* channel or person */
	char   *msg = NULL;
	char    f = 0, n = 0, l = 0, h = 0, c = 0, mmsg = 0;	/*mm:message */
	char    hr = 0, day = 0, min = 0, secs = 0, month = 0;
	char    year = 0;
	unsigned read = 0, totalread = 0;	/*z, Z */
	char    z = 0, Z = 0;
	time_t  t_t = time(NULL);
	struct tm *tt = localtime(&t_t);
	va_list arg_ptr;

	if (!fmt)
		return NULL;
	p = (char *) malloc(sizeof(char) * (strlen(fmt_string) + strlen(fmt) + 1));

	if (!p) {
		err_ret("%s (%d)", __FILE__, __LINE__);
		return NULL;
	}
	sprintf(p, "%s%s", fmt_string, fmt);
	fmt = strdup(p);
	free(p);
	/*buf = (char *) malloc(sizeof(char) * (MAXLINE + 64));*/

	p = buf;
	va_start(arg_ptr, fmt);
	while (*fmt) {
		while ((p - buf) < MAXLINE && *fmt && !(*fmt == '$' && !stop) && *fmt != '%')
			*p++ = *fmt++;
		if ((p - buf) == MAXLINE)
			break;
		if (*fmt == '$') {
			if (!stop)
				stop = 1;
			fmt++;
			continue;
		}
		if (*fmt == '%')
			switch (*++fmt) {
			    case 'S':{
					    static char u[3];

					    if (secs == 0) {
						    sprintf(u, "%02d", tt->tm_sec);
						    secs = 1;
					    }
					    ptr = u;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    fmt++;
				    break;
			    case 'M':{
					    static char u[3];

					    if (min == 0) {
						    sprintf(u, "%02d", tt->tm_min);
						    min = 1;
					    }
					    ptr = u;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    fmt++;
				    break;
			    case 'H':{
					    static char u[3];

					    if (hr == 0) {
						    sprintf(u, "%02d", tt->tm_hour);
						    hr = 1;
					    }
					    ptr = u;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    fmt++;
				    break;
			    case '/':
				    *p++ = '\n';
				    fmt++;
				    break;
			    case 't':
				    *p++ = '\t';
				    fmt++;
				    break;
			    case 'd':{
					    static char u[3];

					    if (day == 0) {
						    sprintf(u, "%02d", tt->tm_mday);
						    day = 1;
					    }
					    ptr = u;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    fmt++;
				    break;
			    case 'm':{
					    static char u[3];

					    if (month == 0) {
						    sprintf(u, "%2d", tt->tm_mon);
						    month = 1;
					    }
					    ptr = u;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    fmt++;
				    break;
			    case 'Y':{
					    static char u[5];

					    if (year == 0) {
						    sprintf(u, "%4d", 1900 + tt->tm_year);
						    year = 1;
					    }
					    ptr = u;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    fmt++;
				    break;
			    case 'D':{	/*Sun Sep 21 14:08:25 1997 */
					    ptr = (char *) fl_now();	/* or ctime(&t-t) !!note: \n */
					    while (*ptr)
						    *p++ = *ptr++;
					    fmt++;
					    break;
				    }

			    case 'z':
				    if (z) {
					    char    u[10];

					    ptr = u;
					    sprintf(u, "%d", read);
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    else {
					    read = va_arg(arg_ptr, int);

					    z = 1;
				    }
				    fmt++;
				    break;

			    case 'Z':
				    if (Z) {
					    char    u[10];

					    ptr = u;
					    sprintf(u, "%d", (int) totalread);
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    else {
					    totalread = va_arg(arg_ptr, int);

					    Z = 1;
				    }
				    fmt++;
				    break;

			    case 'f':	/* sender of msg:from */
				    if (f) {	/* already assigned %s */
					    ptr = server;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    else if (!stop) {
					    server = va_arg(arg_ptr, char *);

					    f = 1;
				    }
				    fmt++;
				    break;
			    case 'c':
				    if (c) {
					    ptr = dest;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    else if (!stop) {
					    dest = va_arg(arg_ptr, char *);

					    c = 1;
				    }
				    fmt++;
				    break;

			    case 'n':
				    if (n) {
					    ptr = nick;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    else if (!stop) {
					    nick = va_arg(arg_ptr, char *);

					    n = 1;
				    }
				    fmt++;
				    break;
			    case 'l':
				    if (l) {
					    ptr = login;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    else if (!stop) {
					    login = va_arg(arg_ptr, char *);

					    l = 1;
				    }
				    fmt++;
				    break;
			    case 'h':
				    if (h) {
					    ptr = host;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    else if (!stop) {
					    host = va_arg(arg_ptr, char *);

					    h = 1;
				    }
				    fmt++;
				    break;

			    case 'p':
				    if (mmsg) {
					    ptr = msg;
					    while (*ptr)
						    *p++ = *ptr++;
				    }
				    else if (!stop) {
					    msg = va_arg(arg_ptr, char *);

					    mmsg = 1;
				    }
				    fmt++;
				    break;

			    case '%':
				    *p++ = *fmt++;
				    break;	/* %% */
			    default:{
					    fmt++;	/* skip over unknown format char */
				    }
				    break;
			}	/* switch */
	}			/*while */
	va_end(arg_ptr);
	*p = '\0';
	return (buf[0] ? strdup(buf) : NULL);
}
