/************
 *  cset.c  *
 ************
 *
 * My code for creating all the "Channel Sets" on a per channel
 * basis. We manage them here, and then just insert/remove/return them 
 * when a channel calls for them.
 * Code for creating and modifying "Window Sets" on a per window
 * basis. 
 *
 * Note: Notice the shameless use of typecasting to get these functions
 *	 to work as painlessly as possible.
 *
 * Written by Scott H Kilau
 * Modified by Colten D Edwards for /wset
 *
 * Copyright(c) 1997
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
 */

#if 0
[Sheik(~sheik@spartan.pei.edu)] notice in struct.h that the cset struct *does*
          have a ->next pointer.. thats for the future when we may want to
                    daisy chain cset's that don't have channels yet..  ie  a user can
                              /cset *warez*  and know that all warez channels would get defaulted
#endif                              

#include "irc.h"
#include "vars.h"    /* for do_boolean() and var_settings[] */
#include "window.h"  /* for get_current_channel_by_refnum() */
#include "screen.h"  /* for curr_scr_win */
#include "names.h"   /* for lookup_channel() */
#include "struct.h"
#include "ircaux.h"
#include "status.h"
#include "server.h"
#include "output.h"
#include "misc.h"
#include "cset.h"



/*
 * Array of structures for each of the cset vars... allows us to
 * get an offset into the CSetList struct to that var
 */
static CSetArray cset_array[] = {
{ "AINV",			INT_TYPE_VAR,	offsetof(CSetList, set_ainv) ,NULL },
{ "ANNOY_KICK",			BOOL_TYPE_VAR,	offsetof(CSetList, set_annoy_kick) ,NULL },
{ "AOP",			BOOL_TYPE_VAR,	offsetof(CSetList, set_aop) ,NULL },
{ "AUTO_REJOIN",		INT_TYPE_VAR,   offsetof(CSetList, set_auto_rejoin) ,NULL },
{ "BITCH",			BOOL_TYPE_VAR,	offsetof(CSetList, bitch_mode) ,NULL },
{ "COMPRESS_MODES",		BOOL_TYPE_VAR,	offsetof(CSetList, compress_modes) ,NULL },
{ "DEOPFLOOD",			BOOL_TYPE_VAR,	offsetof(CSetList, set_deopflood) ,NULL },
{ "DEOPFLOOD_TIME",		INT_TYPE_VAR,	offsetof(CSetList, set_deopflood_time) ,NULL },
{ "DEOP_ON_DEOPFLOOD",		INT_TYPE_VAR,	offsetof(CSetList, set_deop_on_deopflood) ,NULL },
{ "DEOP_ON_KICKFLOOD",		INT_TYPE_VAR,	offsetof(CSetList, set_deop_on_kickflood) ,NULL },
{ "HACKING",			INT_TYPE_VAR,	offsetof(CSetList, set_hacking) ,NULL },
{ "JOINFLOOD",			BOOL_TYPE_VAR,	offsetof(CSetList, set_joinflood) ,NULL },
{ "JOINFLOOD_TIME",		INT_TYPE_VAR,	offsetof(CSetList, set_joinflood_time) ,NULL },
{ "KICKFLOOD",			BOOL_TYPE_VAR,	offsetof(CSetList, set_kickflood) ,NULL },
{ "KICKFLOOD_TIME",		INT_TYPE_VAR,	offsetof(CSetList, set_kickflood_time) ,NULL },
{ "KICK_IF_BANNED",		BOOL_TYPE_VAR,  offsetof(CSetList, set_kick_if_banned) ,NULL },
{ "KICK_OPS",			BOOL_TYPE_VAR,	offsetof(CSetList, set_kick_ops), NULL },
{ "KICK_ON_DEOPFLOOD",		INT_TYPE_VAR,   offsetof(CSetList, set_kick_on_deopflood) ,NULL },
{ "KICK_ON_JOINFLOOD",		INT_TYPE_VAR,	offsetof(CSetList, set_kick_on_joinflood) ,NULL },
{ "KICK_ON_KICKFLOOD",		INT_TYPE_VAR,   offsetof(CSetList, set_kick_on_kickflood) ,NULL },
{ "KICK_ON_NICKFLOOD",		INT_TYPE_VAR,   offsetof(CSetList, set_kick_on_nickflood) ,NULL },
{ "KICK_ON_PUBFLOOD",		INT_TYPE_VAR,   offsetof(CSetList, set_kick_on_pubflood) ,NULL },
{ "LAMELIST",			BOOL_TYPE_VAR,  offsetof(CSetList, set_lamelist) ,NULL },
{ "NICKFLOOD",			BOOL_TYPE_VAR,	offsetof(CSetList, set_nickflood) ,NULL },
{ "NICKFLOOD_TIME",		INT_TYPE_VAR,	offsetof(CSetList, set_nickflood_time) ,NULL },
{ "PUBFLOOD",			BOOL_TYPE_VAR,	offsetof(CSetList, set_pubflood) ,NULL },
{ "PUBFLOOD_IGNORE_TIME",	INT_TYPE_VAR,	offsetof(CSetList, set_pubflood_ignore) ,NULL },
{ "PUBFLOOD_TIME",		INT_TYPE_VAR,	offsetof(CSetList, set_pubflood_time) ,NULL },
{ "SHITLIST",			BOOL_TYPE_VAR,  offsetof(CSetList, set_shitlist) ,NULL },
{ "USERLIST",			BOOL_TYPE_VAR,  offsetof(CSetList, set_userlist) ,NULL },
{ NULL,				0,		0, NULL }
};

static CSetArray wset_array[] = {
{ "STATUS_AWAY",		STR_TYPE_VAR,	offsetof(WSet, status_away), build_status },
{ "STATUS_CDCCCOUNT",		STR_TYPE_VAR,	offsetof(WSet, status_cdcccount), build_status },
{ "STATUS_CHANNEL",		STR_TYPE_VAR,	offsetof(WSet, status_channel), build_status },
{ "STATUS_CHANOP",		STR_TYPE_VAR,	offsetof(WSet, status_chanop), build_status },
{ "STATUS_CLOCK",		STR_TYPE_VAR,	offsetof(WSet, status_clock), build_status },
{ "STATUS_CPU_SAVER",		STR_TYPE_VAR,	offsetof(WSet, status_cpu_saver), build_status },
{ "STATUS_DCCCOUNT",		STR_TYPE_VAR,	offsetof(WSet, status_dcccount), build_status },
{ "STATUS_FORMAT",              STR_TYPE_VAR,   offsetof(WSet, format_status), build_status },
{ "STATUS_FORMAT1",             STR_TYPE_VAR,   offsetof(WSet, format_status[1]), build_status },
{ "STATUS_FORMAT2",             STR_TYPE_VAR,   offsetof(WSet, format_status[2]), build_status },
{ "STATUS_FORMAT3",             STR_TYPE_VAR,   offsetof(WSet, format_status[3]), build_status },
{ "STATUS_HOLD",		STR_TYPE_VAR,	offsetof(WSet, status_hold), build_status },
{ "STATUS_HOLD_LINES",		STR_TYPE_VAR,	offsetof(WSet, status_hold_lines), build_status },
{ "STATUS_LAG",			STR_TYPE_VAR,	offsetof(WSet, status_lag), build_status },
{ "STATUS_MAIL",		STR_TYPE_VAR,	offsetof(WSet, status_mail), build_status },
{ "STATUS_MODE",		STR_TYPE_VAR,	offsetof(WSet, status_mode), build_status },
{ "STATUS_MSGCOUNT",		STR_TYPE_VAR,	offsetof(WSet, status_msgcount), build_status },
{ "STATUS_NOTIFY",		STR_TYPE_VAR,	offsetof(WSet, status_notify), build_status },
{ "STATUS_OPER_KILLS",		STR_TYPE_VAR,	offsetof(WSet, status_oper_kills), build_status },
{ "STATUS_QUERY",		STR_TYPE_VAR,	offsetof(WSet, status_query), build_status },
{ "STATUS_SERVER",		STR_TYPE_VAR,	offsetof(WSet, status_server), build_status },
{ "STATUS_TOPIC",		STR_TYPE_VAR,	offsetof(WSet, status_topic), build_status },
{ "STATUS_UMODE",		STR_TYPE_VAR,	offsetof(WSet, status_umode), build_status },
{ "STATUS_USER",		STR_TYPE_VAR,	offsetof(WSet, status_users), build_status },
{ "STATUS_USER1",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats1),  build_status },
{ "STATUS_USER10",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats10), build_status },
{ "STATUS_USER11",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats11), build_status },
{ "STATUS_USER12",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats12), build_status },
{ "STATUS_USER13",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats13), build_status },
{ "STATUS_USER14",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats14), build_status },
{ "STATUS_USER15",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats15), build_status },
{ "STATUS_USER16",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats16), build_status },
{ "STATUS_USER17",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats17), build_status },
{ "STATUS_USER18",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats18), build_status },
{ "STATUS_USER19",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats19), build_status },
{ "STATUS_USER2",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats2),  build_status },
{ "STATUS_USER3",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats3),  build_status },
{ "STATUS_USER4",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats4),  build_status },
{ "STATUS_USER5",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats5),  build_status },
{ "STATUS_USER6",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats6),  build_status },
{ "STATUS_USER7",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats7),  build_status },
{ "STATUS_USER8",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats8),  build_status },
{ "STATUS_USER9",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats9),  build_status },
{ "STATUS_USERS",		STR_TYPE_VAR,	offsetof(WSet, status_user_formats0),  build_status },
{ "STATUS_VOICE",		STR_TYPE_VAR,	offsetof(WSet, status_voice),  build_status },
{ NULL,				0,		0, NULL } 
};

/*
 * returns the requested int from the cset struct
 * Will work fine with either BOOL or INT type of csets.
 */
int get_cset_int_var(CSetList *tmp, int var)
{
	char *ptr = (char *) tmp + cset_array[var].offset;
	return (int) *ptr;
}

/*
 * returns the requested int ADDRESS from the cset struct
 * Will work fine with either BOOL or INT type of csets.
 */
static int * get_cset_int_var_address(CSetList *tmp, int var)
{
	char *ptr = (char *) tmp + cset_array[var].offset;
	return (int *) ptr;
}



/*
 * sets the requested int from the cset struct
 * Will work fine with either BOOL or INT type of csets.
 */
void set_cset_int_var(CSetList *tmp, int var, int value)
{
	char *ptr = (char *) tmp + cset_array[var].offset;
	(int) *ptr = value;
}

/*
 * Next few functions ripped from vars.c, as we are really doing the same
 * thing as those functions did, with a couple changes... The nice
 * thing about this is that we will get the same "feel" now with
 * SET's and CSET's
 */
static int find_cset_variable(CSetArray *array, char *org_name, int *cnt)
{
	CSetArray *v, *first;
	int     len, var_index;
	char    *name = (char *) 0;

	malloc_strcpy(&name, org_name);
	upper(name);
	len = strlen(name);
	var_index = 0;
	for (first = array; first->name; first++, var_index++) 
	{
		if (strncmp(name, first->name, len) == 0) 
		{
			*cnt = 1;
			break;
		}
	}
	if (first->name) 
	{
		if (strlen(first->name) != len) 
		{
			v = first;
			for (v++; v->name; v++, (*cnt)++) 
			{
				if (strncmp(name, v->name, len) != 0)
					break;
			}
		}
		new_free(&name);
		return (var_index);
	}
	else 
	{
		*cnt = 0;
		new_free(&name);
		return (-1);
	}
}


static void set_cset_var_value(CSetList *tmp, int var_index, char *value)
{
	char    *rest;
	CSetArray *var;

	var = &(cset_array[var_index]);

	switch (var->type) 
	{
	case BOOL_TYPE_VAR:
        	if (value && *value && (value = next_arg(value, &rest))) {
			if (do_boolean(value, get_cset_int_var_address(tmp, var_index))) 
			{
				say("Value must be either ON, OFF, or TOGGLE");
				break;
			}
			put_it("%s", convert_output_format(fget_string_var(FORMAT_CSET_FSET), "%s %s %s", var->name, tmp->channel, get_cset_int_var(tmp, var_index)?var_settings[ON] : var_settings[OFF]));
#if 0
			say("Value of %s for channel %s set to %s",
				var->name, tmp->channel, 
				(get_cset_int_var(tmp, var_index)) ? 
				var_settings[ON] : var_settings[OFF]);
#endif
		}
		else 
			put_it("%s", convert_output_format(fget_string_var(FORMAT_CSET_FSET), "%s %s %s", var->name, tmp->channel, get_cset_int_var(tmp, var_index)?var_settings[ON] : var_settings[OFF]));
#if 0
			say("Current value of %s for channel %s is %s",
				var->name, tmp->channel,
				(get_cset_int_var(tmp, var_index)) ?
				var_settings[ON] : var_settings[OFF]);
#endif
		break;

	case INT_TYPE_VAR:
		if (value && *value && (value = next_arg(value, &rest))) 
		{
			int     val;

			if (!is_number(value)) 
			{
				say("Value of %s must be numeric!", var->name);
				break;
			}
			if ((val = atoi(value)) < 0) 
			{
				say("Value of %s must be greater than 0", var->name);
				break;
			}
			set_cset_int_var(tmp, var_index, val);
			put_it("%s", convert_output_format(fget_string_var(FORMAT_CSET_FSET), "%s %s %d", var->name, tmp->channel, get_cset_int_var(tmp, var_index)));
		}
		else 
			put_it("%s", convert_output_format(fget_string_var(FORMAT_CSET_FSET), "%s %s %d", var->name, tmp->channel, get_cset_int_var(tmp, var_index)));
		break;
	}
}

static inline void cset_variable_case1(char *channel, int var_index, char *args)
{
	ChannelList *chan = server_list[curr_scr_win->server].chan_list;
	int tmp = 0;
	/* 
	 * implement a queue for channels that don't exist... later...
	 * go home if user doesn't have any channels.
	 */
	if (!chan) {
		say("CSET_VARIABLE: You have no channels to cset on!");
		return;
	}

	for (; chan; chan = chan->next) {
		tmp = var_index;
		if (match(channel, chan->channel))
			set_cset_var_value(chan->csets, tmp, args);
	}
}

static inline void cset_variable_casedef(char *channel, int cnt, int var_index, char *args)
{
	ChannelList *chan = server_list[curr_scr_win->server].chan_list;
	int tmp, tmp2;
	
	if (!chan) {
		say("CSET_VARIABLE: You have no channels to cset on!");
		return;
	}

	for (; chan; chan = chan->next) {
		tmp = var_index;
		tmp2 = cnt;
		if (match(channel, chan->channel)) {
			for (tmp2 += tmp; tmp < tmp2; tmp++)
				set_cset_var_value(chan->csets, tmp, empty_string);
		}
	}
}

static inline void cset_variable_noargs(char *channel)
{
	int var_index = 0;
	ChannelList *chan = server_list[curr_scr_win->server].chan_list;

	if (!chan) {
		say("CSET_VARIABLE: You have no channels to cset on!");
		return;
	}

	for (; chan; chan = chan->next) {
		if (match(channel, chan->channel)) {
			for (var_index = 0; var_index < NUMBER_OF_CSETS; var_index++)
				set_cset_var_value(chan->csets, var_index, empty_string);
		}
	}
}

BUILT_IN_COMMAND(cset_variable)
{
	char    *var, *channel = NULL;
	int     no_args = 1, cnt, var_index;

	if (from_server == -1 || curr_scr_win->server == -1)
		return;
	if (args && *args && (is_channel(args) || *args == '*'))
		channel = next_arg(args, &args);
	else
		channel = get_current_channel_by_refnum(0);
	if (!channel)
		return;
	if ((var = next_arg(args, &args)) != NULL) 
	{
		if (*var == '-') 
		{
			var++;
			args = (char *) 0;
		}
		var_index = find_cset_variable(cset_array, var, &cnt);
		switch (cnt) 
		{
			case 0:
				say("No such variable \"%s\"", var);
				return;
			case 1:
				cset_variable_case1(channel, var_index, args);
				return;
			default:
				say("%s is ambiguous", var);
				cset_variable_casedef(channel, cnt, var_index, args);
				return;
		}
	}
	if (no_args)
		cset_variable_noargs(channel);
}

CSetList *create_csets_for_channel(char *channel)
{
	CSetList *tmp; 

	tmp = (CSetList *) new_malloc(sizeof(CSetList));
	/* use default settings. */
	tmp->set_aop = get_int_var(AOP_VAR);
	tmp->set_annoy_kick = get_int_var(ANNOY_KICK_VAR);
	tmp->set_ainv = get_int_var(AINV_VAR);
	tmp->set_auto_rejoin = get_int_var(AUTO_REJOIN_VAR);
	tmp->compress_modes = get_int_var(COMPRESS_MODES_VAR);
	tmp->bitch_mode = get_int_var(BITCH_VAR);

	tmp->set_joinflood = get_int_var(JOINFLOOD_VAR);
	tmp->set_joinflood_time = get_int_var(JOINFLOOD_TIME_VAR);
	tmp->set_deop_on_deopflood = get_int_var(DEOP_ON_DEOPFLOOD_VAR);
	tmp->set_deop_on_kickflood = get_int_var(DEOP_ON_KICKFLOOD_VAR);
	tmp->set_deopflood = get_int_var(DEOPFLOOD_VAR);
	tmp->set_deopflood_time = get_int_var(DEOPFLOOD_TIME_VAR);

	tmp->set_hacking = get_int_var(HACKING_VAR);

	tmp->set_kick_on_deopflood = get_int_var(KICK_ON_DEOPFLOOD_VAR);
	tmp->set_kick_on_joinflood = get_int_var(KICK_ON_JOINFLOOD_VAR);
	tmp->set_kick_on_kickflood = get_int_var(KICK_ON_KICKFLOOD_VAR);
	tmp->set_kick_on_nickflood = get_int_var(KICK_ON_NICKFLOOD_VAR);
	tmp->set_kick_on_pubflood = get_int_var(KICK_ON_PUBFLOOD_VAR);

	tmp->set_kickflood = get_int_var(KICKFLOOD_VAR);
	tmp->set_kickflood_time = get_int_var(KICKFLOOD_TIME_VAR);
	tmp->set_kick_ops = get_int_var(KICK_OPS_VAR);
	tmp->set_nickflood = get_int_var(NICKFLOOD_VAR);
	tmp->set_nickflood_time = get_int_var(NICKFLOOD_TIME_VAR);

	tmp->set_pubflood = get_int_var(PUBFLOOD_VAR);
	tmp->set_pubflood_time = get_int_var(PUBFLOOD_TIME_VAR);
	tmp->set_pubflood_ignore = 60;
	tmp->set_userlist = get_int_var(USERLIST_VAR);
	tmp->set_shitlist = get_int_var(SHITLIST_VAR);
	tmp->set_lamelist = get_int_var(LAMELIST_VAR);
	tmp->set_kick_if_banned = get_int_var(KICK_IF_BANNED_VAR);
	malloc_strcpy(&(tmp->channel), channel);

	return tmp;
}

void remove_csets_for_channel(CSetList *tmp)
{
	new_free(&(tmp->channel));
	new_free(&tmp);
}



/*
 * returns the requested int from the cset struct
 * Will work fine with either BOOL or INT type of csets.
 */
char *get_wset_string_var(WSet *tmp, int var)
{
/*	char *ptr = *(((char **)tmp + (wset_array[var].offset/sizeof(char *))));*/
	char **ptr = (char **)((unsigned long)tmp + wset_array[var].offset);
	return *ptr;
}

/*
 * returns the requested int ADDRESS from the wset struct
 * Will work fine with STR_TYPE_VAR type of csets.
 */
static char ** get_wset_str_var_address(WSet *tmp, int var)
{
/*	char **ptr = (((char **)tmp + (wset_array[var].offset/sizeof(char *))));*/
	char **ptr = (char **)((unsigned long)tmp + wset_array[var].offset);
	return ptr;
}

/*
 * sets the requested int from the wset struct
 */

void set_wset_string_var(WSet *tmp, int var, char *value)
{
/*	char **ptr = ((char **)tmp + wset_array[var].offset/sizeof(char *));*/
	char **ptr = (char **)((unsigned long)tmp + wset_array[var].offset);
	if (value && *value)
		malloc_strcpy(ptr, value);
	else
		new_free(ptr);
}





static void set_wset_var_value(Window *win, int var_index, char *value)
{
	CSetArray *var;

	var = &(wset_array[var_index]);

	switch (var->type) 
	{
	case STR_TYPE_VAR:
		{
			char **val = NULL;
			if ((val = get_wset_str_var_address(win->wset, var_index))) 
			{
				if (value)
				{
					if (*value)
						malloc_strcpy(val, value);
					else
					{
						put_it("%s", convert_output_format(fget_string_var(FORMAT_SET_FSET), "%s %s", var->name, *val?*val:empty_string));
						return;
					}
				} else
					new_free(val);
				if (var->func)
					(var->func) (curr_scr_win, *val, 0);
				say("Value of %s set to %s", var->name, *val ?
					*val : "<EMPTY>");
			}
		}
		break;
	default:
		say("WSET_type not supported");
	}
}

static inline void wset_variable_case1(Window *win, char *name, int var_index, char *args)
{
Window *tmp = screen_list->window_list;
int count = 0;
int i, j;
	if (!name)
	{
		set_wset_var_value(win, var_index, args);
		return;
	}
	for (j = 0; j < 2; j++, tmp = invisible_list)
	{
		for (; tmp; tmp = tmp->next)
		{
			i = var_index;
			if (*name == '*')
			{
				set_wset_var_value(tmp, i, args);
				count++;
			}
			else if (tmp->name && match(name, tmp->name))
			{
				set_wset_var_value(tmp, i, args);
				count++;
			}
		}
	}
	if (!count)
		say("No such window name [%s]", name);
}

static inline void wset_variable_casedef(Window *win, char *name, int cnt, int var_index, char *args)
{
Window *tmp = screen_list->window_list;
int count = 0;
int c, i, j;
	if (!name)
	{
		set_wset_var_value(win, var_index, args);
		return;
	}
	for (j = 0; j < 2; j++, tmp = invisible_list)
	{
		for (; tmp; tmp = tmp->next) 
		{
			c = cnt;
			i = var_index;
			if (*name == '*')
			{
				for (c += i; i < c; i++)
					set_wset_var_value(tmp, i, empty_string);
				count++;
			}
			else if (tmp->name && match(name, tmp->name)) 
			{
				for (c += i; i < c; i++)
					set_wset_var_value(tmp, i, empty_string);
				count++;
			}
		}
	}
	if (!count)
		say("No such window name [%s]", name);
}

static inline void wset_variable_noargs(Window *win, char *name)
{
Window *tmp = screen_list->window_list;
int var_index = 0;
int j;
	if (!name)
	{
		for (var_index = 0; var_index < NUMBER_OF_WSETS; var_index++)
			set_wset_var_value(win, var_index, empty_string);
	} 
	else
	{
		int count = 0;
		for (j = 0; j < 2; j++, tmp = invisible_list)
		{
			for (; tmp; tmp = tmp->next)
			{
				if (*name == '*')
				{
					for (var_index = 0; var_index < NUMBER_OF_WSETS; var_index++)
						set_wset_var_value(tmp, var_index, empty_string);
					count++;
				}
				else if (tmp->name && match(name, tmp->name))
				{
					for (var_index = 0; var_index < NUMBER_OF_WSETS; var_index++)
						set_wset_var_value(tmp, var_index, empty_string);
					count++;
				}
			}
		}
		if (!count)
			say("No such window name [%s]", name);
	}
}


BUILT_IN_COMMAND(wset_variable)
{
	char    *var;
	char	*possible;
	char 	*name = NULL;
	int     no_args = 1, cnt, var_index;
	Window *win = get_window_by_refnum(0);

	if (args && *args == '*')
		name = next_arg(args, &args);
	else if (args && *args)
	{
		possible = alloca(strlen(args)+1);
		strcpy(possible, args);
		name = next_arg(possible, &possible);
		if (!(win = get_window_by_name(name)))
		{
			win = get_window_by_refnum(0);
			name = NULL;
		}
		else
			var = next_arg(args, &args);
	}
	else 
		win = get_window_by_refnum(0);

	
	if ((var = next_arg(args, &args)) != NULL) 
	{
		if (*var == '-') 
		{
			var++;
			args = NULL;
		}
		var_index = find_cset_variable(wset_array, var, &cnt);
		switch (cnt) 
		{
			case 0:
				say("No such variable \"%s\"", var);
				return;
			case 1:
				wset_variable_case1(win, name, var_index, args);
				return;
			default:
				say("%s is ambiguous", var);
				wset_variable_casedef(win, name, cnt, var_index, args);
				return;
		}
	}
	if (no_args) 
		wset_variable_noargs(win, name);
}


WSet *create_wsets_for_window(Window *win)
{
	WSet *tmp; 

	tmp = (WSet *) new_malloc(sizeof(WSet));

	malloc_strcpy(&tmp->status_channel, get_string_var(STATUS_CHANNEL_VAR));
	malloc_strcpy(&tmp->status_chanop, get_string_var(STATUS_CHANOP_VAR));
	malloc_strcpy(&tmp->status_clock, get_string_var(STATUS_CLOCK_VAR));
	malloc_strcpy(&tmp->status_hold_lines, get_string_var(STATUS_HOLD_LINES_VAR));
	malloc_strcpy(&tmp->status_hold, get_string_var(STATUS_HOLD_VAR));
	malloc_strcpy(&tmp->status_voice, get_string_var(STATUS_VOICE_VAR));
	malloc_strcpy(&tmp->status_dcccount, get_string_var(STATUS_DCCCOUNT_VAR));
	malloc_strcpy(&tmp->status_cdcccount, get_string_var(STATUS_CDCCCOUNT_VAR));
	malloc_strcpy(&tmp->status_cpu_saver, get_string_var(STATUS_CPU_SAVER_VAR));
	malloc_strcpy(&tmp->status_lag, get_string_var(STATUS_LAG_VAR));
	malloc_strcpy(&tmp->status_away, get_string_var(STATUS_AWAY_VAR));
	malloc_strcpy(&tmp->status_mail, get_string_var(STATUS_MAIL_VAR));
	malloc_strcpy(&tmp->status_mode, get_string_var(STATUS_MODE_VAR));
	malloc_strcpy(&tmp->status_notify, get_string_var(STATUS_NOTIFY_VAR));
	malloc_strcpy(&tmp->status_oper_kills, get_string_var(STATUS_OPER_KILLS_VAR));
	malloc_strcpy(&tmp->status_query, get_string_var(STATUS_QUERY_VAR));
	malloc_strcpy(&tmp->status_server, get_string_var(STATUS_SERVER_VAR));
	malloc_strcpy(&tmp->status_topic, get_string_var(STATUS_TOPIC_VAR));
	malloc_strcpy(&tmp->status_umode, get_string_var(STATUS_UMODE_VAR));
	malloc_strcpy(&tmp->status_users, get_string_var(STATUS_USER_VAR));
	malloc_strcpy(&tmp->status_msgcount, get_string_var(STATUS_MSGCOUNT_VAR));
	
	malloc_strcpy(&tmp->format_status[0], get_string_var(STATUS_FORMAT_VAR));
	malloc_strcpy(&tmp->format_status[1], get_string_var(STATUS_FORMAT1_VAR));
	malloc_strcpy(&tmp->format_status[2], get_string_var(STATUS_FORMAT2_VAR));
	malloc_strcpy(&tmp->format_status[3], get_string_var(STATUS_FORMAT3_VAR));

	malloc_strcpy(&tmp->status_user_formats1, get_string_var(STATUS_USER1_VAR));
	malloc_strcpy(&tmp->status_user_formats10, get_string_var(STATUS_USER10_VAR));
	malloc_strcpy(&tmp->status_user_formats11, get_string_var(STATUS_USER11_VAR));
	malloc_strcpy(&tmp->status_user_formats12, get_string_var(STATUS_USER12_VAR));
	malloc_strcpy(&tmp->status_user_formats13, get_string_var(STATUS_USER13_VAR));
	malloc_strcpy(&tmp->status_user_formats14, get_string_var(STATUS_USER14_VAR));
	malloc_strcpy(&tmp->status_user_formats15, get_string_var(STATUS_USER15_VAR));
	malloc_strcpy(&tmp->status_user_formats16, get_string_var(STATUS_USER16_VAR));
	malloc_strcpy(&tmp->status_user_formats17, get_string_var(STATUS_USER17_VAR));
	malloc_strcpy(&tmp->status_user_formats18, get_string_var(STATUS_USER18_VAR));
	malloc_strcpy(&tmp->status_user_formats19, get_string_var(STATUS_USER19_VAR));
	malloc_strcpy(&tmp->status_user_formats2, get_string_var(STATUS_USER2_VAR));
	malloc_strcpy(&tmp->status_user_formats3, get_string_var(STATUS_USER3_VAR));
	malloc_strcpy(&tmp->status_user_formats4, get_string_var(STATUS_USER4_VAR));
	malloc_strcpy(&tmp->status_user_formats5, get_string_var(STATUS_USER5_VAR));
	malloc_strcpy(&tmp->status_user_formats6, get_string_var(STATUS_USER6_VAR));
	malloc_strcpy(&tmp->status_user_formats7, get_string_var(STATUS_USER7_VAR));
	malloc_strcpy(&tmp->status_user_formats8, get_string_var(STATUS_USER8_VAR));
	malloc_strcpy(&tmp->status_user_formats9, get_string_var(STATUS_USER9_VAR));
	malloc_strcpy(&tmp->status_user_formats0, get_string_var(STATUS_USERS_VAR));
	win->wset = tmp;	
	return tmp;
}

void remove_wsets_for_window(Window *tmp)
{
	new_free(&tmp->wset->status_channel);
	new_free(&tmp->wset->status_clock); 
	new_free(&tmp->wset->status_hold_lines);
	new_free(&tmp->wset->status_hold);
	new_free(&tmp->wset->status_voice);
	new_free(&tmp->wset->status_dcccount);
	new_free(&tmp->wset->status_cdcccount);
	new_free(&tmp->wset->status_lag); 
	new_free(&tmp->wset->status_away); 

	new_free(&tmp->wset->status_mail);
	new_free(&tmp->wset->status_msgcount);
	new_free(&tmp->wset->status_cpu_saver);
	new_free(&tmp->wset->status_chanop);
	new_free(&tmp->wset->status_mode);
	
	new_free(&tmp->wset->status_notify);
	new_free(&tmp->wset->status_oper_kills);
	new_free(&tmp->wset->status_query);
	new_free(&tmp->wset->status_server);
	new_free(&tmp->wset->status_topic);
	new_free(&tmp->wset->status_umode);
	new_free(&tmp->wset->status_users);

	new_free(&tmp->wset->status_user_formats1);
	new_free(&tmp->wset->status_user_formats10);
	new_free(&tmp->wset->status_user_formats11);
	new_free(&tmp->wset->status_user_formats12);
	new_free(&tmp->wset->status_user_formats13);
	new_free(&tmp->wset->status_user_formats14);
	new_free(&tmp->wset->status_user_formats15);
	new_free(&tmp->wset->status_user_formats16);
	new_free(&tmp->wset->status_user_formats17);
	new_free(&tmp->wset->status_user_formats18);
	new_free(&tmp->wset->status_user_formats19);
	new_free(&tmp->wset->status_user_formats2);
	new_free(&tmp->wset->status_user_formats3);
	new_free(&tmp->wset->status_user_formats4);
	new_free(&tmp->wset->status_user_formats5);
	new_free(&tmp->wset->status_user_formats6);
	new_free(&tmp->wset->status_user_formats7);
	new_free(&tmp->wset->status_user_formats8);
	new_free(&tmp->wset->status_user_formats9);
	new_free(&tmp->wset->status_user_formats0);

	new_free(&tmp->wset->format_status[0]);
	new_free(&tmp->wset->format_status[1]);
	new_free(&tmp->wset->format_status[2]);
	new_free(&tmp->wset->format_status[3]); 
	new_free(&tmp->wset->status_format[0]);
	new_free(&tmp->wset->status_format[1]);
	new_free(&tmp->wset->status_format[2]);
	new_free(&tmp->wset->status_format[3]); 
	new_free(&tmp->wset->status_line[0]);
	new_free(&tmp->wset->status_line[1]);
	new_free(&tmp->wset->status_line[2]);


	new_free(&tmp->wset->mode_format);
	new_free(&tmp->wset->umode_format);
	new_free(&tmp->wset->topic_format);
	new_free(&tmp->wset->query_format);
	new_free(&tmp->wset->clock_format);
	new_free(&tmp->wset->hold_lines_format);
	new_free(&tmp->wset->channel_format);
	new_free(&tmp->wset->mail_format);
	new_free(&tmp->wset->server_format);
	new_free(&tmp->wset->notify_format);
	new_free(&tmp->wset->status_oper_kills_format);
	new_free(&tmp->wset->status_lag_format);
	new_free(&tmp->wset->cpu_saver_format);
	new_free(&tmp->wset->msgcount_format);
	new_free(&tmp->wset->dcccount_format);
	new_free(&tmp->wset->cdcccount_format);


	new_free(&tmp->wset);
}
