/*
 * option.c: getting, setting options
 *
 * Copyright(c) 1997-2000 - All Rights Reserved
 *
 * See the COPYRIGHT file.
 */

#ifndef lint
static char rcsid[] = "@(#)$Id: option.c,v 1.36 2000/07/31 22:33:55 kalt Exp $";
#endif

#include "os.h"

#include "struct.h"
#include "window.h"
#include "format.h"
#include "option.h"
#include "utils.h"
#include "config.h"
#include "counter.h"

extern struct window_	*current, *active;
extern struct server_	*server;

static const struct custom_	defcust = 
{
  { 0 }, { 0 },
  default_format_str, NULL,
  {
    LOG_DEBUG|LOG_ISNIF,
    LOG_DEFAULT,
    LOG_DEFAULT,
    LOG_DEFAULT,
    LOG_HIGHLIGHT|LOG_KEYWORD,
    0, 0, 0,
    LOG_ALL|LOG_IGNORE|LOG_HIDE,
    LOG_INPUT|LOG_OSNIF, 0
  },
  {
    0,
    LOG_HIDE|LOG_IGNORE,
    LOG_IGNORE,
    0, 0, 0, 0, 0, 0, 0, 0
  },
  NULL, NULL, (char) 0
};
  
static struct custom_		topcust;
static struct custom_		protocust[P_MAX];

void
opt_init()
{
  bzero(&topcust, sizeof(topcust));
  bzero(&protocust, sizeof(protocust));
}

void
opt_cfg()
{
  struct server_ dummy, *save;

  cfg_read("top");

  save = server; server = &dummy;
  dummy.protocol = P_IRC; cfg_read("irc");
  dummy.protocol = P_E; cfg_read("EFnet");
  dummy.protocol = P_U; cfg_read("Undernet");
  dummy.protocol = P_D; cfg_read("DALnet");
  server = save;
}

void
opt_free(cust)
  struct custom_ *cust;
{
  struct keyword_ *ktmp = cust->keywords, *kdel;
  struct rewrite_ *rtmp = cust->rules, *rdel;
  int i;

  if (cust->format)
    {
      for (i = 0; i <= F_MAX; i++)
	  if (cust->format[i])
	      free(cust->format[i]);
      free(cust->format);
    }
  if (cust->numerics)
    {
      for (i = 0; i <= N_MAX; i++)
	  if (cust->numerics[i])
	      free(cust->numerics[i]);
      free(cust->numerics);
    }

  while (kdel = ktmp)
    {
      ktmp = ktmp->nextk;
      free(kdel->kword);
#if defined(HAVE_REGEXP)
      if (kdel->kflag & K_REGEXP)
	  regfree(&(kdel->rword));
#endif
      free(kdel);
    }

  while (rdel = rtmp)
    {
      rtmp = rtmp->nextr;
      free(rdel->match);
      free(rdel->new);
#if defined(HAVE_REGEXP)
      regfree(&(rdel->preg));
#endif
      free(rdel);
    }

  if (cust->log)
      fclose(cust->log);
}

/* sic_logfile: log the specific line to a file, unless filtered */
void
sic_logfile(line)
  struct log_ *line;
{
  struct custom_ *all_list[5] = { &topcust, NULL, NULL, NULL, NULL};
  int i;

  if (server)
    {
      if (server->protocol)
	  all_list[1] = &protocust[server->protocol-1];
      all_list[2] = &(server->custs);
    }
  all_list[3] = &(active->custw);
  if (line->dest && get_channel(line->dest))
    all_list[4] = &(get_channel(line->dest)->custc);    
  for(i = 4; i >= 0; i--)
    {
      if (all_list[i] == NULL)
	continue;
      if (all_list[i]->logfilter == 0)
	continue;
      if (all_list[i]->log &&
	  get_flag(line->flags, line->dest, all_list[i]->logfilter-1))
	  {
		fprintf(all_list[i]->log, "%s%s\n", line->prefix,
			line->formatted);
		fflush(all_list[i]->log);
	  }
    }
}

/*
 * GET - easy part
 */

/* get_format: returns the format string for a particular channel/window */
char *
get_format(type, channel)
unsigned int type;
char *channel;
{
  struct channel_ *ctmp;

  assert (type >= 0 && type <= F_MAX);

  if (ctmp = get_channel(channel))
      if (ctmp->custc.format && ctmp->custc.format[type])
	  return ctmp->custc.format[type];
  if (active->custw.format && active->custw.format[type])
      return active->custw.format[type];
  if (server)
    {
      if (server->custs.format && server->custs.format[type])
	  return server->custs.format[type];
      if (server->protocol && protocust[server->protocol-1].format
	  && protocust[server->protocol-1].format[type])
	  return server->custs.format[type];
    }
  if (topcust.format && topcust.format[type])
      return topcust.format[type];
  return defcust.format[type];
}

/* get_nformat: returns the format string for a particular channel/window */
char *
get_nformat(fnum, channel)
unsigned int fnum;
char *channel;
{
  struct channel_ *ctmp;

  assert (fnum >= 0 && fnum <= N_MAX);

  if (ctmp = get_channel(channel))
      if (ctmp->custc.numerics && ctmp->custc.numerics[fnum])
	  return ctmp->custc.numerics[fnum];
  if (active->custw.numerics && active->custw.numerics[fnum])
      return active->custw.numerics[fnum];
  if (server)
    {
      if (server->custs.numerics && server->custs.numerics[fnum])
	  return server->custs.numerics[fnum];
      if (server->protocol && protocust[server->protocol-1].numerics
          && protocust[server->protocol-1].numerics[fnum])
	  return server->custs.numerics[fnum];
    }
  if (topcust.numerics && topcust.numerics[fnum])
      return topcust.numerics[fnum];
  /* if (defcust.numerics[fnum])
      return defcust.numerics[fnum]; */
  return get_format(F_NUM, channel);
}

/* get_option: get the option setting for a particular channel/window */
int
get_option(opt, channel)
unsigned int opt;
char *channel;
{
  struct channel_ *ctmp;

  if (ctmp = get_channel(channel))
    {
      if (option(ctmp->custc.zopt_on, opt))
	  return 1;
      if (option(ctmp->custc.zopt_off, opt))
	  return 0;
    }
  if (option(active->custw.zopt_on, opt))
      return 1;
  if (option(active->custw.zopt_off, opt))
      return 0;
  if (server)
    {
      if (option(server->custs.zopt_on, opt))
	  return 1;
      if (option(server->custs.zopt_off, opt))
	  return 0;
      if (server->protocol)
	{
	  if (option(protocust[server->protocol-1].zopt_on, opt))
	      return 1;
	  if (option(protocust[server->protocol-1].zopt_off, opt))
	      return 0;
	}
    }	
  if (option(topcust.zopt_on, opt))
      return 1;
  if (option(topcust.zopt_off, opt))
      return 0;
  if (option(defcust.zopt_on, opt))
      return 1;
  if (option(defcust.zopt_off, opt)) /* useless */
      return 0;
  return 0;
}

/* get_flag: get the flag setting for a particular channel/window */
int
get_flag(mask, channel, nb)
unsigned int mask, nb;
char *channel;
{
  struct channel_ *ctmp;
  int rc = -1;

  assert (nb >= 0 && nb < 11);

  if (ctmp = get_channel(channel))
    {
      if (ctmp->custc.mask_off[nb] & mask)
	  rc = 0;
      else if (ctmp->custc.mask_on[nb] & mask)
	  rc = 1;
      else
	  rc = -1;
    }
  if (rc == -1)
      if (active->custw.mask_off[nb] & mask)
	  rc = 0;
      else if (active->custw.mask_on[nb] & mask)
	  rc = 1;
      else if (server && server->custs.mask_off[nb] & mask)
	  rc = 0;
      else if (server && server->custs.mask_on[nb] & mask)
	  rc = 1;
      else if (server && server->protocol &&
	       protocust[server->protocol-1].mask_off[nb] & mask)
	  rc = 0;
      else if (server && server->protocol &&
	       protocust[server->protocol-1].mask_on[nb] & mask)
	  rc = 1;
      else if (topcust.mask_off[nb] & mask)
	  rc = 0;
      else if (topcust.mask_on[nb] & mask)
	  rc = 1;
      else if (defcust.mask_off[nb] & mask)
	  rc = 0;
      else if (defcust.mask_on[nb] & mask)
	  rc = 1;
      else
	  rc = 0;

  if (mask & LOG_DEL)
      if (rc != get_flag(mask ^ LOG_DEL, channel, nb))
	  /* LOG_DEL made a difference, so return rc (overrides Z_UNDEL) */
	  return rc;
      else if (rc == 1)
	  return get_option(Z_UNDEL, channel);

  return rc;
}

int
get_keyword(channel, where, attr, flags)
  char *channel, **where, *attr;
  unsigned int *flags;
{
  struct keyword_ *ktmp = NULL;
  char *cp, *best = NULL, bestv = 0;
  int i, len = 0;
#if defined(HAVE_REGEXP)
  regmatch_t pmatch[20];
#endif

  assert(channel);
  *attr = 0;
  for (i = 0 ; i < 5 ; i++)
    {
      switch (i)
	{
      case 0:
	  ktmp = get_channel(channel)->custc.keywords;
	  break;
      case 1:
	  ktmp = active->custw.keywords;
	  break;
      case 2:
	  if (server)
	      ktmp = server->custs.keywords;
	  break;
      case 3:
	  if (server && server->protocol)
	      ktmp = protocust[server->protocol-1].keywords;
	  break;
      case 4:
	  ktmp = topcust.keywords;
	  break;
      default:
	  abort();
	}
      while (ktmp)
	{
	  if ((((ktmp->kflag & K_REGEXP) == 0)
	       && (cp = strcasestr(*where, ktmp->kword)))
#if defined(HAVE_REGEXP)
	      || (ktmp->kflag & K_REGEXP && 
		  regexec(&(ktmp->rword), *where, 20, pmatch, 0) == 0)
#endif
	      )
	    {
	      *flags |= LOG_KEYWORD;
	      if (ktmp->kflag & K_BEEP)
		  *attr |= K_BEEP;
	      if (ktmp->kflag & K_SPACE)
		  *attr |= K_SPACE;
	      if (ktmp->kflag & K_NOTIFY)
		  set_option(active->wopt, W_KEYWORD);
	      if (ktmp->kflag & K_HIDE)
		  *flags |= LOG_HIDE;
	      if (ktmp->kflag & K_IGNORE)
		  *flags |= LOG_IGNORE;
#if defined(HAVE_REGEXP)
	      if (ktmp->kflag & K_REGEXP)
		  cp = *where + pmatch[0].rm_so;
#endif
	      if (best == NULL || cp < best
		  && (ktmp->kflag & (K_BOLD|K_UNDERLINE|K_STANDOUT|K_CHIDE)))
		{
		  best = cp;
		  bestv = ktmp->kflag & (K_BOLD|K_UNDERLINE|K_STANDOUT|K_CHIDE);
#if defined(HAVE_REGEXP)
		  if (ktmp->kflag & K_REGEXP)
		      len = pmatch[0].rm_eo - pmatch[0].rm_so;
		  else
#endif
		      len = strlen(ktmp->kword);
		}
	    }
	  ktmp = ktmp->nextk;	      
	}
    }
  if (best)
      {
	*where = best;
	*attr |= bestv;
      }
  else
      *where = NULL;
  return len;
}

void
get_ignore(from, channel, flags)
  char *from, *channel;
  unsigned int *flags;
{
  struct keyword_ *ktmp = NULL;
  int i;

  for (i = (channel) ? 0 : 1 ; i < 5 ; i++)
    {
      switch (i)
	{
      case 0:
	  ktmp = get_channel(channel)->custc.keywords;
	  break;
      case 1:
	  ktmp = active->custw.keywords;
	  break;
      case 2:
	  if (server)
	      ktmp = server->custs.keywords;
	  break;
      case 3:
          if (server && server->protocol)
              ktmp = protocust[server->protocol-1].keywords;
          break;
      case 4:
	  ktmp = topcust.keywords;
	  break;
      default:
	  abort();
	}
      while (ktmp)
	{
	  if (ktmp->kflag & (K_OIGNORE|K_OHIGHLIGHT))
	      if (rmatch(ktmp->kword, from))
		{
		  if (ktmp->kflag & K_OIGNORE)
		      *flags |= LOG_IGNORE;
		  if (ktmp->kflag & K_OHIGHLIGHT)
		      *flags |= LOG_HIGHLIGHT;
		}
	  ktmp = ktmp->nextk;	      
	}
    }
}

/* get_rewrite: rewrite the output, if applicable. Also deal with counters */
int
get_rewrite(fmt, string, attributes, flags, channel)
  int fmt;
  char *string, *channel;
  u_char *attributes;
  unsigned int *flags;
{
#if defined(HAVE_REGEXP)
  struct rewrite_ *rtmp = NULL;
  int i, rc = 0;
  regmatch_t pmatch[20];

  for (i = (channel) ? 0 : 1 ; i < 5 ; i++)
    {
      switch (i)
	{
      case 0:
	  rtmp = get_channel(channel)->custc.rules;
	  break;
      case 1:
	  rtmp = active->custw.rules;
	  break;
      case 2:
	  if (server)
	      rtmp = server->custs.rules;
	  break;
      case 3:
          if (server && server->protocol)
              rtmp = protocust[server->protocol-1].rules;
          break;
      case 4:
	  rtmp = topcust.rules;
	  break;
      default:
	  abort();
	}
      while (rtmp)
	{
	  if (rtmp->fmt == fmt
	      && regexec(&(rtmp->preg), string, 20, pmatch, 0) == 0)
	    {
	      if (*rtmp->new)
		{
		  char ostr[1024], oattr[1024],
		       *nfmt = rtmp->new, *nstr = string;
		  u_char *nattr =attributes;
		  
		  strcpy(ostr, string);
		  bcopy(attributes, oattr, strlen(string)+1);

		  if (*nfmt == ':')
		    {
		      nfmt += 1;
		      *flags |= LOG_HIDE;
		    }
		  while (*nfmt)
		    {
		      if (*nfmt != '$')
			{
			  *nstr++ = *nfmt++;
			  *nattr++ = '\0';
			}
		      else
			{
			  int n = -1, j;
			  
			  if (*++nfmt == '{')
			    {
			      n = atoi(++nfmt);
			      nfmt = index(nfmt, '}') + 1;
			    }
			  else
			    {
			      n = atoi(nfmt);
			      while (isdigit((int) *nfmt))
				  nfmt++;
			    }
			  if (n < 20 && (j = pmatch[n].rm_so) != -1)
			    {
			      while (j < pmatch[n].rm_eo && ostr[j])
				{
				  *nstr++ = ostr[j];
				  *nattr++ = oattr[j++];
				}
			    }
			}
		    }
		  *nstr = '\0';
		  *nattr = '\0';
		}
	      else
		  rc += counter_add((void *)rtmp, string, attributes, pmatch,
				    flags, channel);
	      if (rtmp->type == 0)
		  return rc;
	    }
		  
	  rtmp = rtmp->nextr;
	}
    }
  return rc;
#endif
}

/*
 * SET
 */

/* real_set:
 *
 */
static void
real_set(action, cust, fnum, val_str, val_ui)
  int		fnum;
  unsigned int	action, *val_ui;
  char		**val_str;
  struct custom_	*cust;
{
  if (fnum >= 0)
    {
      char ***formats = &(cust->format);
      int  sz = F_MAX;

      if (fnum > F_MAX)
	{
	  fnum -= F_MAX+1;
	  sz = N_MAX;
	  formats= &(cust->numerics);
	}
      switch (action & O_ACTIONMASK)
	{
	case O_CLEAR:
	  if (*formats)
	    {
	      if ((*formats)[fnum])
		  free((*formats)[fnum]);
	      (*formats)[fnum] = NULL;
	    }
	  break;
	case O_SET:
	  if ((*formats) == NULL)
	    {
	      *formats = (char **) malloc(sizeof(char *) * (sz+1));
	      bzero(*formats, sizeof(char *) * (sz+1));
	    }
	  if ((*formats)[fnum])
	    free((*formats)[fnum]);
	  (*formats)[fnum] = strdup(*val_str);
	  if (**val_str == ':')
	      *((*formats)[fnum]) = '\0';
	  break;
	case O_GET:
	  if (*formats)
	      *val_str = (*formats)[fnum];
	  else
	      *val_str = NULL;
	  break;
	}
    }
  else if (action & O_KEYWORD)
    {
      struct keyword_ *k1, *k2;

      switch (action & O_ACTIONMASK)
	{
	case O_CLEAR:
	  k2 = cust->keywords;
	  while (k1 = k2)
	    {
	      k2 = k2->nextk;
#if defined(HAVE_REGEXP)
	      if (k1->kflag & K_REGEXP)
		  regfree(&(k1->rword));
#endif
	      free(k1);
	    }
	  cust->keywords = NULL;
	  break;
	case O_SET:
	  k2 = cust->keywords;
	  while (k2 && strcasecmp(k2->kword, *val_str))
	    k2 = k2->nextk;
	  if (k2)
	      k2->kflag = *val_ui | (k2->kflag & K_REGEXP);
	  else
	    {
	      k1 = (struct keyword_ *) malloc(sizeof(struct keyword_));

#if defined(HAVE_REGEXP)
	      if (*val_ui & K_REGEXP)
		{
		  static char rx_errbuf[1024];
		  int rx_err;

		  if (rx_err = regcomp(&(k1->rword), *val_str,
				       REG_EXTENDED|REG_ICASE))
		    {
		      if (regerror(rx_err, &(k1->rword), rx_errbuf, 1024)>1024)
			  *val_str="Error compiling regexp (rx_errbuf too small!)";
		      else
			  *val_str = rx_errbuf;
		      free(k1);
		      return;
		    }
		}
#endif

	      k1->nextk = cust->keywords;
	      cust->keywords = k1;
	      k1->kword = strdup(*val_str);
	      k1->kflag = *val_ui;
	    }
	  *val_str = NULL;
	  break;
	case O_GET:
	  k2 = cust->keywords;
	  if (*val_str)
	      while (k2 && strcasecmp(k2->kword, *val_str))
		  k2 = k2->nextk;
	  else
	      while (k2 && ++fnum)
		  k2 = k2->nextk;
	  if (k2)
	    {
	      *val_str = k2->kword;
	      *val_ui = (unsigned int) k2->kflag;
	    }
	  else
	    {
	      *val_str = NULL;
	      *val_ui = 0;
	    }
	  break;
	}
    }
  else if (action & O_LOGFILE)
    {
      switch (action & O_ACTIONMASK)
	{
      case O_CLEAR:
	  *val_str = NULL;
	  if (cust->log)
	    {
	      fprintf(cust->log, "--- End of log\n");
	      fclose(cust->log);
	      cust->log = NULL;
	    }
	  break;
      case O_GET:
	  *val_ui = cust->logfilter-1;
	  *val_str = (cust->log) ? "Active" : "Disabled";
	  break;
      case O_SET:
	  cust->logfilter = *val_ui+1;
	  if (**val_str == '\0')
	    {
	      if (cust->log)
		*val_str = NULL;
	      else
		*val_str = "No active log (filter set)";
	      return;
	    }
	  if (cust->log && **val_str)
	    {
	      fprintf(cust->log, "--- Log restarted: %s\n", *val_str);
	      fclose(cust->log);
	      cust->log = NULL;
	    }
	  if (**val_str)
	    {
	      char fname[255], *env;
	     
	      strcpy(fname, cfgdir);
	      if (env = getenv("SICLOG"))
		if (*env == '/')
		  strcpy(fname, getenv("SICLOG"));
		else
		  strcat(fname, env);
	      else
		strcat(fname, "log");
	      strcat(fname, "/");
	      strcat(fname, *val_str);
	      cust->log = fopen(fname, "a");
	    }
	  if (cust->log == NULL)
	      *val_str = "Unable to open logfile";
	  else
	    {
	      *val_str = NULL;
	      fprintf(cust->log, "--- Log started\n");
	    }
	  break;
      default:
	  abort(); /* never */
	}
    }
  else if (action & O_SWITCH)
    {
      switch (action & O_ACTIONMASK)
	{
      case O_GET:
	  if (option(cust->zopt_on, atoi(*val_str)))
	      *val_str = "on";
	  else if (option(cust->zopt_off, atoi(*val_str)))
	      *val_str = "off";
	  else
	      *val_str = "unset";
	  break;
      case O_SET:
	  if (*val_ui == 0)
	    {
	      unset_option(cust->zopt_on, atoi(*val_str));
	      set_option(cust->zopt_off, atoi(*val_str));
	    }
	  else if (*val_ui == 1)
	    {
	      set_option(cust->zopt_on, atoi(*val_str));
	      unset_option(cust->zopt_off, atoi(*val_str));
	    }
	  else
	      abort();
	  break;
      case O_CLEAR:
	  unset_option(cust->zopt_on, atoi(*val_str));
	  unset_option(cust->zopt_off, atoi(*val_str));
	  break;
      default:
	  abort(); /* never */
	}
    }
  else if (action & O_REWRITE)
    {
#if !defined(HAVE_REGEXP)
      abort();
#else
      struct rewrite_ *r1, *r2;
      char *delim;

      switch (action & O_ACTIONMASK)
	{
	case O_CLEAR:
	  r2 = cust->rules;
	  while (r1 = r2)
	    {
	      r2 = r2->nextr;
	      free(r1);
	    }
	  cust->rules = NULL;
	  break;
	case O_SET:
	  if (!(delim = strstr(*val_str, ">|>"))
	      && !(delim = strstr(*val_str, ">+>")))
	    {
	      *val_str = "Invalid rule syntax.";
	      return;
	    }
	  *delim = '\0';
	  delim += 3;

	  r2 = cust->rules;
	  while (r2 && (r2->fmt != *val_ui || strcasecmp(r2->match, *val_str)))
	    r2 = r2->nextr;
	  if (r1 = r2)
	      free(r1->new);
	  else
	    {
	      static char rx_errbuf[1024];
	      int rx_err;

	      r2 = cust->rules;
	      while (r2 && r2->nextr)
		  r2 = r2->nextr;
	      r1 = (struct rewrite_ *) malloc(sizeof(struct rewrite_));

	      if (rx_err =regcomp(&(r1->preg),*val_str,REG_EXTENDED|REG_ICASE))
		{
		  if (regerror(rx_err, &(r1->preg), rx_errbuf, 1024) > 1024)
		      *val_str="Error compiling regexp (rx_errbuf too small!)";
		  else
		      *val_str = rx_errbuf;
		  free(r1);
		  return;
		}

	      if (cust->rules)
		  r2->nextr = r1;
	      else
		  cust->rules = r1;
	      r1->nextr = NULL;

	      r1->fmt = *val_ui;
	      r1->match = strdup(*val_str);
	    }
	  r1->type = (*(delim-2) == '+') ? 1 : 0;
	  r1->new = strdup(delim);
	  *val_str = NULL;
	  break;
	case O_GET:
	  r2 = cust->rules;
	  if (*val_str && *val_ui <= F_MAX)
	      while (r2 && (r2->fmt != *val_ui ||
			    (*val_str && strcasecmp(r2->match, *val_str))))
		  r2 = r2->nextr;
	  else if (*val_ui <= F_MAX)
	      while (r2 && (r2->fmt != *val_ui || fnum < 0))
		{
		  if (r2->fmt == *val_ui)
		      if (!++fnum)
			  break;
		  r2 = r2->nextr;
		}
	  else while (r2 && ++fnum)
	      r2 = r2->nextr;
	  if (r2)
	    {
	      static char result[512];

	      sprintf(result, "%s>%c>%s", r2->match, (r2->type) ? '+' : '|',
		      r2->new);
	      *val_str = result;
	      *val_ui = (unsigned int) r2->fmt;
	    }
	  else
	    {
	      *val_str = NULL;
	      *val_ui = 0;
	    }
	  break;
	}
#endif
    }
  else
    {
      unsigned int *val_i;

      switch (action & O_TYPEMASK)
	{
	case O_MASK|O_ON:
	  val_i = &(cust->mask_on[atoi(*val_str)]);
	  break;
	case O_MASK|O_OFF:
	  val_i = &(cust->mask_off[atoi(*val_str)]);
	  break;
	case O_ON:
	  abort(); /* not implemented */
	  break;
	case O_OFF:
	  abort(); /* not implemented */
	  break;
	default:
	  abort();
	}
      switch (action & O_ACTIONMASK)
	{
      case O_CLEAR:
	  *val_i = 0;
	  break;
      case O_GET:
	  *val_ui = *val_i;
	  break;
      case O_SET:
	  *val_i = *val_ui;
	  break;
      default:
	  abort(); /* never */
	}
    }
}

/* customize: get or set an option value
 * recursion isn't implemented
 * the caller is responsible for making sure that O_DEFAULT is only used
 * with O_GET
 */
void
customize(action, channel, fnum, val_str, val_ui)
  int		fnum;
  unsigned int	action, *val_ui;
  char		**val_str, *channel;
{
  struct channel_ *ctmp;

  if (channel)
      {
	ctmp = get_channel(channel);
	real_set(action, &(ctmp->custc), fnum, val_str, val_ui);
      }
  else switch (action & O_TARGETMASK)
    {
  case O_WINDOW:
      real_set(action, &(current->custw), fnum, val_str, val_ui);
      break;
  case O_SERVER:
      if (server)
	  real_set(action, &(server->custs), fnum, val_str, val_ui);
      break;
  case O_PROTO:
      if (server && server->protocol)
	  real_set(action, &(protocust[server->protocol - 1]),
		   fnum, val_str, val_ui);
      break;
  case O_TOPLVL:
      real_set(action, &(topcust), fnum, val_str, val_ui);
      break;
  case O_DEFAULT:
      real_set(action, &(defcust), fnum, val_str, val_ui);
      break;
  default:
      abort();
    }
}
