/*
 * IMC2 - an inter-mud communications protocol
 *
 * imc-mercbase.c: integrated interface code for Merc-derived codebases.
 *                 Now also includes patches for Circle.
 *
 * Copyright (C) 1999 Peter Keeler <Scion@usa.net> and Gregor Moody <moodyg@erols.com>
 * Copyright (C) 1996,1997 Oliver Jowett <oliver@jowett.manawatu.planet.co.nz>
 *
 * 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.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file COPYING); if not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* Modifications by:
 *  Erwin S. Andreasen <erwin@pip.dknet.dk>    (Envy changes)
 *  Erin Moeller <altrag@game.org>             (Merc, SMAUG changes)
 *  Stephen Zepp <zenithar@fullnet.net>        (Ack! changes)
 *  Trevor Man <tman@iname.com>                (Circle changes)
 * 
 * integration by Oliver Jowett
 */

#if !defined(CIRCLE)
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#endif

#include "imc.h"

/* ick! */
#define IN_IMC
#include "imc-mercdefs.h"
#include "imc-mercbase.h"

extern int imc_log_on;

/* memory allocation hooks */
#ifdef LOCAL_LOG_ROUTINE
  void imc_local_log(const char * string);
#endif
void ev_iced_refresh(void *dummy) /*dummy catch cause muds has no iced.c-shogar */
{
}

void *imc_malloc(int size)
{
#ifdef LOCALMEM
#if defined(SMAUG) || defined(CIRCLE)
  /* Have to use char because CREATE uses a sizeof, which doesnt like void */
  char *mem;

  CREATE(mem, char, size);
  return (void *)mem;
#elif defined(ACK)
  return getmem(size);
#else
  return alloc_mem(size);
#endif
#else
	return malloc(size);
#endif
}

void IMC_free(void *block, int size)
{
  if(!block)
	return;
#ifdef LOCALMEM
#ifdef SMAUG
  DISPOSE(block);
#elif defined(ACK)
  dispose(block, size);
#elif defined(CIRCLE)
  free(block);
#else
  free_mem(block, size);
#endif
#else
  free(block);
#endif
}

char *imc_strdup(const char *src)
{
#ifdef LOCALMEM
  return str_dup(src);
#else
  return strdup(src);
#endif
}

void IMC_strfree(char *str)
{
  if(!str)
	return;
#ifdef LOCALMEM
#ifdef SMAUG
  DISPOSE(str);
#elif defined(CIRCLE)
  free(str);
#else
  free_string(str);
#endif
#else
  free(str);
#endif
}

/* config hooks */

int imc_readconfighook(const char *word, const char *value)
{
  return 0;
}

void imc_saveconfighook(FILE *fp)
{
}

/* call markfn for every chunk of memory allocated by IMC */
void imc_markmemory(void (*markfn)(void *))
{
  imc_reminfo *r;
  imc_mail *mailp;
  imc_qnode *queuep;
  imc_mailid *idp;
  imc_info *infop;
  imc_connect *cp;
  imc_event *ep;

  for (infop=imc_info_list; infop; infop=infop->next)
    markfn(infop);

  for (cp=imc_connect_list; cp; cp=cp->next)
  {
    markfn(cp->inbuf);
    markfn(cp->outbuf);
    markfn(cp);
  }

  for (r=imc_reminfo_list; r; r=r->next)
    markfn(r);

  for (mailp=imc_ml_head; mailp; mailp=mailp->next)
    markfn(mailp);

  for (queuep=imc_mq_head; queuep; queuep=queuep->next)
    markfn(queuep);

  for (idp=imc_idlist; idp; idp=idp->next)
    markfn(idp);

  for (ep=imc_event_list; ep; ep=ep->next)
    markfn(ep);

  for (ep=imc_event_free; ep; ep=ep->next)
    markfn(ep);
}

/* call markfn for every string allocated by IMC */
void imc_markstrings(void (*markfn)(char *))
{
  int i;
  imc_reminfo *r;
  imc_mail *mailp;
  imc_qnode *queuep;
  imc_mailid *idp;
  imc_info *infop;
  imc_ignore_data *ign;

  markfn(imc_name);
  markfn(imc_prefix);

  for (infop=imc_info_list; infop; infop=infop->next)
  {
    markfn(infop->name);
    markfn(infop->host);
    markfn(infop->serverpw);
    markfn(infop->clientpw);
  }

  for (r=imc_reminfo_list; r; r=r->next)
  {
    markfn(r->name);
    markfn(r->version);
    markfn(r->route);
  }

  for (ign=imc_ignore_list; ign; ign=ign->next)
    markfn(ign->name);

  for (i=0; i<IMC_MEMORY; i++)
    if (imc_memory[i].from)
      markfn(imc_memory[i].from);

  for (mailp=imc_ml_head; mailp; mailp=mailp->next)
  {
    markfn(mailp->from);
    markfn(mailp->to);
    markfn(mailp->date);
    markfn(mailp->text);
    markfn(mailp->subject);
    markfn(mailp->id);
  }
  
  for (queuep=imc_mq_head; queuep; queuep=queuep->next)
    markfn(queuep->tomud);
  
  for (idp=imc_idlist; idp; idp=idp->next)
    markfn(idp->id);
}


/* Color configuration (more tables to configure.. sowwy)
 *
 * The various #defines here are used within the IMC code to format 
 * things like rtells themselves (not the text of the message, but the
 * "a@b rtells you 'blah'" structure)
 *
 * Each C_? #define maps an IMC color code (the ?) to one of your color codes.
 *
 * The various FORMATs define colors for rbeep, etc
 *
 * this may seem ugly, but it makes things expandable - and it will probably
 * go away in the near future anyway
 *
 */

#ifdef ACK
#define RTELL_FORMAT_1 COL(W) "You rtell " COL(Y) "%s " COL(W) "'" COL(w) "%s" COL(W) "'\n\r" COL(d)
#define RTELL_FORMAT_2 COL(Y) "%s " COL(W) "rtells you '" COL(w) "%s" COL(W) "'\n\r" COL(d)
#define RBEEP_FORMAT_1 COL(R) "You rbeep " COL(Y) "%s" COL(R) ".\n\r" COL(d)
#define RBEEP_FORMAT_2 COL(Y) "\a%s " COL(R) "rbeeps you.\n\r" COL(d)
#else /* custom formats */
#define RTELL_FORMAT_1 "You rtell %s '%s" COL(d) "'.\n\r"
#define RTELL_FORMAT_2 "%s rtells you '%s" COL(d) "'.\n\r"
#define RBEEP_FORMAT_1 "You rbeep %s.\n\r"
#define RBEEP_FORMAT_2 "\a%s rbeeps you.\n\r"
#endif

#ifdef ACK
/* Ack! colors */
#define COL(x) C_##x
#define C_b "@@B"
#define C_g "@@G"
#define C_r "@@R"
#define C_y "@@b"
#define C_m "@@m"
#define C_c "@@c"
#define C_w "@@g"
#define C_D "@@d"
#define C_B "@@l"
#define C_G "@@r"
#define C_R "@@e"
#define C_Y "@@y"
#define C_M "@@p"
#define C_C "@@a"
#define C_W "@@W"
#define C_x "@@N"
#define C_d "@@N"

#elif defined(SMAUG)
/* SMAUG colors */
#define COL(x) C_##x
#define C_b "&b"
#define C_g "&g"
#define C_r "&r"
#define C_y "&O"
#define C_m "&p"
#define C_c "&c"
#define C_w "&w"
#define C_D "&z"
#define C_B "&B"
#define C_G "&G"
#define C_R "&R"
#define C_Y "&Y"
#define C_M "&P"
#define C_C "&C"
#define C_W "&W"
#define C_x "&w"
#define C_d "&w"

#elif defined(CIRCLE_COLOR)
/* Circle colors - Easy Colour 2.2 */
#define COL(x) C_##x
#define C_b "&b"
#define C_g "&g"
#define C_r "&r"
#define C_y "&y"
#define C_m "&m"
#define C_c "&c"
#define C_w "&w"
#define C_D "&K"
#define C_B "&B"
#define C_G "&G"
#define C_R "&R"
#define C_Y "&Y"
#define C_M "&M"
#define C_C "&C"
#define C_W "&W"
#define C_x "&n"
#define C_d "&n"

#elif defined(IMC_COLOR)
/* custom color configuration */
#define COL(x) C_##x
#define C_b "{b"
#define C_g "{g"
#define C_r "{r"
#define C_y "{y"
#define C_m "{m"
#define C_c "{c"
#define C_w "{w"
#define C_D "{D"
#define C_B "{B"
#define C_G "{G"
#define C_R "{R"
#define C_Y "{Y"
#define C_M "{M"
#define C_C "{C"
#define C_W "{W"
#define C_x "{x"
#define C_d "{d"

#else
/* no color */
#define COL(x) ""

#endif

/*  maps IMC standard -> mud local color codes
 *  let's be unique, noone uses ~ :>
 */

/* Most of this table will be generated from the #defines above;
 * you will just need to add the exceptions.
 */

struct {
  char *imc;        /* IMC code to convert */
  char *mud;        /* Equivalent mud code */
}
trans_table[]=
{
  /* common color definitions, derived from the #defines above */

  { "~!", COL(x) },  /* reset */
  { "~d", COL(d) },  /* default */

  { "~b", COL(b) },  /* dark blue */
  { "~g", COL(g) },  /* dark green */
  { "~r", COL(r) },  /* dark red */
  { "~y", COL(y) },  /* dark yellow / brown */
  { "~m", COL(m) },  /* dark magenta / purple */
  { "~c", COL(c) },  /* cyan */
  { "~w", COL(w) },  /* white */

  { "~D", COL(D) },  /* dark grey / "z" ? */
  { "~B", COL(B) },  /* light blue */
  { "~G", COL(G) },  /* light green */
  { "~R", COL(R) },  /* light red */
  { "~Y", COL(Y) },  /* light yellow */
  { "~M", COL(M) },  /* light magenta / purple */
  { "~C", COL(C) },  /* light cyan */
  { "~W", COL(W) },  /* bright white */

#ifdef SMAUG
  /* built-in SMAUG color */

  { "&",  "&&" },
  { "^",  "^^" },

  { "~~", "&-" },  /* note ordering here.. */
  { "~~", "~"  },

  /* background colors - no support for these in IMC */

  { "",   "^b" },
  { "",   "^x" },
  { "",   "^g" },
  { "",   "^r" },
  { "",   "^O" },
  { "",   "^p" },
  { "",   "^c" },
  { "",   "^w" },
  { "",   "^z" },
  { "",   "^B" },
  { "",   "^G" },
  { "",   "^R" },
  { "",   "^Y" },
  { "",   "^P" },
  { "",   "^C" },
  { "",   "^W" },

#elif defined(ACK)
  /* built-in Ack! color */

  { "~~", "~"   },  /* escape raw tildes */

  { "@@", "@ @" },  /* escape our color character */
  
  { "~!", "@@x" },  /* catch commonly used (but incorrect) codes */
  { "~!", "@@f" },  /*  ditto  */
  { "~!", "@@!" },  /*  ditto  */
  { "~!", "@@." },  /*  ditto  */
  { "~!", "@@n" },  /*  ditto  */
  { "~!", "@@i" },  /*  ditto  */
  { "~D", "@@k" },  /* black -> grey */

#elif defined(IMC_COLOR)
  /* generic color; change as needed */

  { "~~", "~"  },  /* escape raw tildes */
  { "{",  "{{" },  /* escape our color character */

#else
  /* no color support */

  { "~~", "~"  },  /* escape raw tildes */

#endif
};

#define numtrans (sizeof(trans_table)/sizeof(trans_table[0]))

/* convert from imc color -> mud color */

const char *color_itom(const char *s)
{
  char *buf=imc_getsbuf(IMC_DATA_LENGTH);
  const char *current;
  char *out;
  int i, l;
  int count;

  for (current=s, out=buf, count=0; *current && count<IMC_DATA_LENGTH; )
  {
    if (*current=='~' || *current==IMC_COLORCHAR || *current==IMC_COLORCHAR2)
    {
      for (i=0; i<numtrans; i++)
      {
	l=strlen(trans_table[i].imc);
	if (l && !strncmp(current, trans_table[i].imc, l))
	  break;
      }
      
      if (i!=numtrans)       /* match */
  
	  {
	int len;

	len=strlen(trans_table[i].mud);
	count+=len;
	if (count >= IMC_DATA_LENGTH)
	  break;
	memcpy(out, trans_table[i].mud, len);
	out+=len;
	current+=l;
	continue;
      }
    }

    *out++=*current++;
    count++;
  }
  
  *out=0;
  imc_shrinksbuf(buf);
  return buf;
}

/* convert from mud color -> imc color */

const char *color_mtoi(const char *s)
{
  char *buf=imc_getsbuf(IMC_DATA_LENGTH);
  const char *current;
  char *out;
  int i, l;
  int count;

  for (current=s, out=buf, count=0; *current && count<IMC_DATA_LENGTH; )
  {
    if (*current=='~' || *current==IMC_COLORCHAR || *current==IMC_COLORCHAR2)
    {
      for (i=0; i<numtrans; i++)
      {
	l=strlen(trans_table[i].mud);
	if (l && !strncmp(current, trans_table[i].mud, l))
	  break;
      }
      
      if (i!=numtrans)      /* match */
      {
	int len;

	len=strlen(trans_table[i].imc);
	count+=len;
	if (count>=IMC_DATA_LENGTH)
	  break;
	memcpy(out, trans_table[i].imc, len);
	out+=len;
	current+=l;
	continue;
      }
    }

    *out++=*current++;
    count++;
  }
  
  *out=0;
  imc_shrinksbuf(buf);
  return buf;
}

/* #ifdefs here are ugly, but will eventually disappear */

static struct {
  int number;
  const char *name;
  const char *chatstr;
  const char *emotestr;
  long flag;
  int minlevel;
  char *to;
} imc_channels[]=
{
#ifdef ACK
  /* Ack!-specific formatting - ick */
  
  {
    2,
    "RInfo",
    "@@W[@@aRINFO@@W] @@a%s@@W:'@@g%s@@W'@@N\n\r",
    "@@W[@@aRINFO@@W] @@a%s@@W %s@@N\n\r",
    IMC_RINFO,
    L_SUP,
    "*",
  },

  /* ICE status channel */
  {
    15,
    "ICE",
    "@@W[@@aICE@@W] @@a%s@@W %s@@N\n\r",
    "@@W[@@aICE@@W] @@a%s@@W %s@@N\n\r",
    IMC_ICE,
    L_SUP,
    "*"
  },

#else /* non-Ack! */

  {
    2,
    "RInfo",
    /* why didn't I think of this? :) */
    "[Rinfo] %s announces '%s" COL(d) "'\n\r",
    "[RInfo] %s %s\n\r" COL(d),
    IMC_RINFO,
    LEVEL_IMMORTAL,
    "*",
  },

  {
    15,
    "ICE",
    "[ICE] %s %s" COL(d) "\n\r",
    "[ICE] %s %s" COL(d) "\n\r",
    IMC_ICE,
    LEVEL_IMMORTAL,
    "*"
  }
#endif /* #ifdef ACK */

  /*
   *  An example of how to set up a channel that is only distributed between
   *  the three muds 'mud1', 'mud2', and 'mud3'.

  {
    76,                            -- channel number to use, change this!
    "LocalChannel",                -- the channel name
    "%s localchannels '%s'\n\r",   -- format for a normal message
    "[LocalChannel] %s %s\n\r",    -- format for an emote
    IMC_LOCALCHANNEL,              -- flag in imc_deaf, etc
    LEVEL_IMMORTAL,                -- min. level for channel
    "mud1 mud2 mud3"               -- muds to distribute to (include your own)
  }

   * (see the interack channel in #ifdef ACK, above, for an example)
   *
   */
};

#define numchannels (sizeof(imc_channels)/sizeof(imc_channels[0]))

static int getlevel(int l)
{
  if (l<LEVEL_HERO)
    return 1;
  else
    return -1;
}

#ifdef CIRCLE
void bug(char *format, int tmp)
{
  sprintf(buf, format, tmp);
  log(buf);
  
  return;
}
#endif

#if defined(CIRCLE) && defined(NEED_STR_PREFIX)
bool str_prefix(const char *astr, const char *bstr)
{
  if (!astr) {
    log("Strn_cmp: null astr.");
    return TRUE;
  }
  if (!bstr) {
    log("Strn_cmp: null astr.");
    return TRUE;
  }
  for(; *astr; astr++, bstr++) {
    if(LOWER(*astr) != LOWER(*bstr)) return TRUE;
  }
  return FALSE;
}
#endif

const imc_char_data *imc_getdata(CHAR_DATA *ch)
{
  static imc_char_data d;

  if (!ch) /* fake system character */
  {
    d.wizi=0;
    d.level=-1;
    d.invis=0;
    strcpy(d.name, "*");
    return &d;
  }

  if (IS_RINVIS(ch))
    d.invis=1;
  else
    d.invis=0;

#ifdef ROM
  d.wizi=(ch->invis_level || ch->incog_level ||
	  IS_AFFECTED(ch, AFF_HIDE) || IS_AFFECTED(ch, AFF_SNEAK) ||
#ifdef ROT
          IS_SHIELDED(ch, SHD_INVISIBLE)) ? -1 : 0;
#else
	  IS_AFFECTED(ch, AFF_INVISIBLE)) ? -1 : 0;
#endif

#elif defined(SMAUG14)
  d.wizi=(xIS_SET(ch->act, PLR_WIZINVIS) ||
	  IS_AFFECTED(ch, AFF_HIDE) || IS_AFFECTED(ch, AFF_SNEAK) ||
	  IS_AFFECTED(ch, AFF_INVISIBLE)) ? -1 : 0;
#elif defined(ACK)
  d.wizi=(IS_SET(ch->act, PLR_WIZINVIS) ||
	  IS_AFFECTED(ch, AFF_HIDE) || IS_AFFECTED(ch, AFF_SNEAK) ||
	  IS_AFFECTED(ch, AFF_INVISIBLE)) ? -1 : 0;
#elif defined(CIRCLE)
  d.wizi=GET_INVIS_LEV(ch);
#else
  d.wizi=(IS_SET(ch->act, PLR_WIZINVIS) ||
	  IS_AFFECTED(ch, AFF_HIDE) || IS_AFFECTED(ch, AFF_SNEAK) ||
	  IS_AFFECTED(ch, AFF_INVISIBLE)) ? -1 : 0;
#endif

  d.level=getlevel(get_trust(ch));
  strcpy(d.name, GET_NAME(ch));

  return &d;
}

int imc_visible(const imc_char_data *viewer, const imc_char_data *viewed)
{
  /* this is now a lot simpler */

  return
    !viewed->invis &&
    (viewer->level < 0 ||
     viewed->wizi >= 0);
}

static const char *getname(CHAR_DATA *ch, const imc_char_data *vict)
{
  const char *mud, *name;
  const imc_char_data *chdata;

  mud=imc_mudof(vict->name);
  name=imc_nameof(vict->name);
  chdata=imc_getdata(ch);

#ifdef CIRCLE
  if (!str_cmp((char *)mud, imc_name))
#else
  if (!str_cmp(mud, imc_name))
#endif
#ifdef ACK
    return imc_visible(chdata,vict) ? imc_nameof(name)
      : (chdata->wizi ? "A Mystical Being" : "someone");
#else
    return imc_visible(imc_getdata(ch),vict) ? imc_nameof(name) : "someone";
#endif
  else
  {
    if (imc_visible(imc_getdata(ch),vict))
      return vict->name;
    else
    {
      char *buf=imc_getsbuf(IMC_NAME_LENGTH);
#ifdef ACK
      sprintf(buf, "%s@%s",
	      (imc_getdata(ch))->wizi ? "A Mystical Being" : "someone", mud);
#else
      sprintf(buf, "someone@%s", mud);
#endif
      imc_shrinksbuf(buf);
      return buf;
    }
  }
}

static void send_rchannel(CHAR_DATA *ch, char *argument, int number)
{
  char arg[MAX_STRING_LENGTH];
  char *arg2;
  int chan;

  CHECKIMC(ch);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  for (chan=0; chan<numchannels; chan++)
    if (imc_channels[chan].number==number)
      break;

  if (chan==numchannels)
  {
    imc_to_char("That channel doesn't seem to exist!\n\r", ch);
    bug("IMC channel %d doesn't exist?!", number);
    return; /* oops */
  }

  if (!CAN(ch, imc_channels[chan].flag, imc_channels[chan].minlevel))
  {
    sprintf(arg, "You are not authorised to use %s.\n\r",
	    imc_channels[chan].name);
    imc_to_char(arg, ch);
    return;
  }

  if (!argument[0])
  {
    if (IS_SET(IMC_DEAF_FLAGS(ch), imc_channels[chan].flag))
    {
      sprintf(arg, "%s channel is now ON.\n\r", imc_channels[chan].name);
      imc_to_char(arg, ch);
      REMOVE_BIT(IMC_DEAF_FLAGS(ch), imc_channels[chan].flag);
      return;
    }
    
    sprintf(arg, "%s channel is now OFF.\n\r", imc_channels[chan].name);
    imc_to_char(arg, ch);

    SET_BIT(IMC_DEAF_FLAGS(ch), imc_channels[chan].flag);
    return;
  }

  if (IS_NOCHAN(ch))
  {
    imc_to_char("The gods have revoked your channel priviliges.\n\r", ch);
    return;
  }

  if (IS_QUIET(ch))
  {
    imc_to_char("You must turn off quiet mode first.\n\r", ch);
    return;
  }

  if (IS_SILENT(ch))
  {
    imc_to_char ("You can't seem to break the silence.\n\r",ch);
    return;
  }

  if (IS_RINVIS(ch))
  {
    imc_to_char ("You cannot use IMC channels while invisible to IMC.\n\r", ch);
    return;
  }
  
  REMOVE_BIT(IMC_DEAF_FLAGS(ch), imc_channels[chan].flag);

  /* NargoRoth's bugfix here */
  arg2=one_argument(argument, arg);

#ifdef CIRCLE
  skip_spaces(&arg2);
#endif

  if (!str_cmp(arg, ",") || !str_cmp(arg, "emote"))
    imc_send_emote(imc_getdata(ch), number, color_mtoi(arg2), imc_channels[chan].to);
  else
    imc_send_chat(imc_getdata(ch), number, color_mtoi(argument),
                  imc_channels[chan].to);
}

DEFINE_DO_FUN(do_rchat)
{
  send_rchannel(ch, argument, 0);
}

DEFINE_DO_FUN(do_rimm)
{
  send_rchannel(ch, argument, 1);
}

DEFINE_DO_FUN(do_rinfo)
{
  send_rchannel(ch, argument, 2);
}

DEFINE_DO_FUN(do_rcode)
{
  send_rchannel(ch, argument, 3);
}

#ifdef ACK
DEFINE_DO_FUN(do_interack)
{
  send_rchannel(ch, argument, 243);
}
#endif

DEFINE_DO_FUN(do_rtell)
{
  char buf[MAX_STRING_LENGTH], buf1[MAX_STRING_LENGTH];

  CHECKIMC(ch);

  argument=one_argument(argument, buf);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!buf[0] || !strchr(buf, '@') || !argument[0])
  {
    imc_to_char("rtell who@where what?\n\r", ch);
    return;
  }

  if (!CAN(ch, IMC_RTELL, IMC_LEVEL_RTELL))
  {
    imc_to_char("You are not authorised to use rtell.\n\r", ch);
    return;
  }

  if (IS_NOTELL(ch))
  {
    imc_to_char("You cannot tell!\n\r", ch);
    return;
  }

  if (IS_RINVIS(ch))
  {
    imc_to_char ("You cannot rtell while invisible to IMC.\n\r", ch);
    return;
  }

  if (IS_SET(IMC_DEAF_FLAGS(ch), IMC_RTELL))
  {
    imc_to_char("Enable incoming rtells first ('rchannel +rtell').\n\r", ch);
    return;
  }

#ifdef ACK
  if (IS_SET(ch->pcdata->pflags, PFLAG_AFK))
  {
    imc_to_char("Turn off AFK mode first.\n\r", ch);
    return;
  }
#endif

#ifdef ROM
  if (IS_SET(ch->comm, COMM_AFK))
  {
    imc_to_char("Turn off AFK mode first.\n\r", ch);
    return;
  }

  if (IS_SET(ch->comm, COMM_DEAF))
  {
    imc_to_char("You must turn off deaf mode first.\n\r", ch);
    return;
  }
#endif

  if (IS_QUIET(ch))
  {
    imc_to_char("You must turn off quiet mode first.\n\r", ch);
    return;
  }
  
  CHECKMUDOF(ch, buf);
  imc_send_tell(imc_getdata(ch), buf, color_mtoi(argument), 0);
  
  sprintf(buf1,
          RTELL_FORMAT_1,
	  buf, argument);
  imc_to_char(buf1, ch);
}

DEFINE_DO_FUN(do_rreply)
{
  char buf1[MAX_STRING_LENGTH];

  CHECKIMC(ch);

  if (!IMC_RREPLY(ch))
  {
    imc_to_char("rreply to who?\n\r", ch);
    return;
  }

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!argument[0])
  {
    imc_to_char("rreply what?\n\r", ch);
    return;
  }

  /* just check for deny, since you can rtell someone < IMC_LEVEL_RTELL */
  if (IS_SET(IMC_DENY_FLAGS(ch), IMC_RTELL))
  {
    imc_to_char("You are not authorised to use rreply.\n\r", ch);
    return;
  }

  if (IS_NOTELL(ch))
  {
    imc_to_char("You cannot tell!\n\r", ch);
    return;
  }

  if (IS_RINVIS(ch))
  {
    imc_to_char ("You cannot rreply while invisible to IMC.\n\r", ch);
    return;
  }

  if (IS_SET(IMC_DEAF_FLAGS(ch), IMC_RTELL))
  {
    imc_to_char("Enable incoming rtells first ('rchannel +rtell').\n\r", ch);
    return;
  }

#ifdef ACK
  if (IS_SET(ch->pcdata->pflags, PFLAG_AFK))
  {
    imc_to_char("Turn off AFK mode first.\n\r", ch);
    return;
  }
#endif

#ifdef ROM
  if (IS_SET(ch->comm, COMM_AFK))
  {
    imc_to_char("Turn off AFK mode first.\n\r", ch);
    return;
  }

  if (IS_SET(ch->comm, COMM_DEAF))
  {
    imc_to_char("You must turn off deaf mode first.\n\r",ch);
    return;
  }
#endif

  if (IS_QUIET(ch))
  {
    imc_to_char("You must turn off quiet mode first.\n\r", ch);
    return;
  }
  
  CHECKMUDOF(ch, IMC_RREPLY(ch));
  imc_send_tell(imc_getdata(ch), IMC_RREPLY(ch), color_mtoi(argument), 1);

  sprintf(buf1,
          RTELL_FORMAT_1,
	  IMC_RREPLY_NAME(ch), argument);
  imc_to_char(buf1, ch);
} 

DEFINE_DO_FUN(do_rwho)
{
  char arg[MAX_STRING_LENGTH];

  CHECKIMC(ch);

  argument=one_argument(argument, arg);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!arg[0])
  {
    imc_to_char("rwho where?\n\r", ch);
    return;
  }
  
  CHECKMUD(ch, arg);
  imc_send_who(imc_getdata(ch), arg, argument[0] ? argument : "who");
}

DEFINE_DO_FUN(do_rwhois)
{
  char arg[MAX_STRING_LENGTH];

  CHECKIMC(ch);

  argument=one_argument(argument, arg);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!arg[0])
  {
    imc_to_char("rwho whom?\n\r", ch);
    return;
  }

  imc_send_whois(imc_getdata(ch), arg);
}

DEFINE_DO_FUN(do_rfinger)
{
  char arg[MAX_STRING_LENGTH];
  char name[MAX_STRING_LENGTH];

  CHECKIMC(ch);

  argument=one_argument(argument, arg);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!arg[0] || !strchr(arg, '@'))
  {
    imc_to_char("rfinger who@where?\n\r", ch);
    return;
  }

  CHECKMUD(ch, imc_mudof(arg));
  sprintf(name, "finger %s", imc_nameof(arg));
  imc_send_who(imc_getdata(ch), imc_mudof(arg), name);
}

DEFINE_DO_FUN(do_rquery)
{
  char arg[MAX_STRING_LENGTH];

  CHECKIMC(ch);

  argument=one_argument(argument, arg);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!arg[0])
  {
    imc_to_char("rquery where?\n\r", ch);
    return;
  }

  CHECKMUD(ch, arg);
  imc_send_who(imc_getdata(ch), arg, argument[0] ? argument : "help");
}

DEFINE_DO_FUN(do_rbeep)
{
  char buf[MAX_STRING_LENGTH];

  CHECKIMC(ch);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!argument[0] || !strchr(argument, '@'))
  {
    imc_to_char("rbeep who@where?\n\r", ch);
    return;
  }

  if (!CAN(ch, IMC_RBEEP, IMC_LEVEL_RBEEP))
  {
    imc_to_char("You are not authorised to rbeep.\n\r", ch);
    return;
  }

  if (IS_NOTELL(ch))
  {
    imc_to_char("You cannot beep!\n\r", ch);
    return;
  }

  if (IS_RINVIS(ch))
  {
    imc_to_char ("You cannot rbeep while invisible to IMC.\n\r", ch);
    return;
  }

  if (IS_QUIET(ch))
  {
    imc_to_char("You must turn off quiet mode first.\n\r", ch);
    return;
  }
  
#ifdef ROM
  if (IS_SET(ch->comm, COMM_DEAF))
  {
    imc_to_char("You must turn off deaf mode first.\n\r", ch);
    return;
  }
#endif

  CHECKMUDOF(ch, argument);
  imc_send_beep(imc_getdata(ch), argument);
  sprintf(buf, RBEEP_FORMAT_1, argument);
  imc_to_char(buf, ch);
   
#ifdef CIRCLE
  WAIT_STATE(ch, PASSES_PER_SEC * 5);
#else
  WAIT_STATE(ch, PULSE_PER_SECOND * 5 );
#endif
}

DEFINE_DO_FUN(do_imclist)
{

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

#ifdef SMAUG
  set_pager_color(AT_PLAIN,ch);
#endif
  if (!argument[0])
    imc_to_pager(imc_list(3), ch);
  else if (!str_prefix(argument, "all"))
    imc_to_pager(imc_list(6), ch);
  else if (!str_prefix(argument, "routes"))
  {
	char buf[IMC_DATA_LENGTH];
	imc_reminfo *i;

	*buf=0;
  	for (i=imc_reminfo_list; i; i=i->next)
        {
                if(i->hide)
			strcat(buf,"[H]");
		sprintf(buf + strlen(buf),"%s:",i->name);
            	if(i->path)
			sprintf(buf + strlen(buf),"%s\n\r",i->path);
		else
			sprintf(buf + strlen(buf),"\n\r");
	}
	imc_to_pager(buf,ch);
  }
		
  else if (!str_prefix(argument, "direct"))
  {
    if (get_trust(ch)>=MAX_LEVEL-1)
      imc_to_pager(imc_list(2), ch);
    else if (IS_IMMORTAL(ch))
      imc_to_pager(imc_list(1), ch);
    else
      imc_to_pager(imc_list(0), ch);
  }
  else if (!str_prefix(argument, "config"))
  {
    if (IS_IMMORTAL(ch))
      imc_to_pager(imc_list(5), ch);
    else
      imc_to_pager(imc_list(4), ch);
  }
  else
    imc_to_pager("Unknown option.", ch);

  imc_to_pager("\n\r", ch);
}

DEFINE_DO_FUN(do_rsockets)
{
  CHECKIMC(ch);

  imc_to_pager(imc_sockets(), ch);
}

DEFINE_DO_FUN(do_imcpoll) {
	imc_add_event(5, ev_imc_optimize, NULL, 1);
	imc_to_char("Polling for hubs in 5 seconds.\n\r", ch);
}

DEFINE_DO_FUN(do_imc)
{
  int r;

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  r=imc_command(argument);

  if (r>0)
  {
    imc_to_char("Ok.\n\r", ch);
    return;
  }
  else if (r==0)
  {
    imc_to_char("Syntax:  imc add <mudname>\n\r"
		 "         imc delete <mudname>\n\r"
		 "         imc set <mudname> all <host> <port> <clientpw> <serverpw>\n\r"
		 "                 <rcvstamp> <noforward> <flags>\n\r"
		 "         imc set <mudname> host|port|clientpw|serverpw|rcvstamp|\n\r"
		 "                           noforward|flags <value>\n\r"
		 "         imc rename <oldmudname> <newmudname>\n\r"
		 "         imc localname <mudname>\n\r"
		 "         imc localport <portnum>\n\r"
		 "         imc log (toggles all log messages)\n\r"
		 "         imc hubswitch (toggles hubswitching - for firewalls)\n\r"
		 "         imc info name <LocalName>\n\r"
                 "         imc info host <InfoHost> (server ip addy or dns name)\n\r"
		 "         imc info imail <admins imc mail address>\n\r"
		 "         imc info email <admins internet mail address>\n\r"
                 "         imc info www <URL for webpage>\n\r"
                 "         imc info details <text>\n\r"
		 "         imc reload\n\r",
		 ch);
    return;
  }

#ifdef CIRCLE
  imc_to_char((char *)imc_error(), ch);
#else
  imc_to_char(imc_error(), ch);
#endif
  imc_to_char("\n\r", ch);
}

DEFINE_DO_FUN(do_rignore)
{
#ifdef CIRCLE
  skip_spaces(&argument);
#endif

#ifdef CIRCLE
  imc_to_char((char *)imc_ignore(argument), ch);
#else
  imc_to_char(imc_ignore(argument), ch);
#endif
  imc_to_char("\n\r", ch);
}

DEFINE_DO_FUN(do_rconnect)
{
  CHECKIMC(ch);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!argument[0])
  {
    imc_to_char("rconnect to where?\n\r", ch);
    return;
  }

  if (imc_connect_to(argument))
  {
    imc_to_char("Ok.\n\r", ch);
    return;
  }

#ifdef CIRCLE
  imc_to_char((char *)imc_error(), ch);
#else
  imc_to_char(imc_error(), ch);
#endif
  imc_to_char("\n\r", ch);
}

DEFINE_DO_FUN(do_rdisconnect)
{
  CHECKIMC(ch);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!argument[0])
  {
    imc_to_char("rdisconnect where?\n\r", ch);
    return;
  }

  if (imc_disconnect(argument))
  {
    imc_to_char("Ok.\n\r", ch);
    return;
  }

#ifdef CIRCLE
  imc_to_char((char *)imc_error(), ch);
#else
  imc_to_char(imc_error(), ch);
#endif
  imc_to_char("\n\r", ch);
}

DEFINE_DO_FUN(do_mailqueue)
{
  CHECKIMC(ch);

  imc_to_pager(imc_mail_showqueue(), ch);
}

DEFINE_DO_FUN(do_istats)
{
  CHECKIMC(ch);

#ifdef CIRCLE
  imc_to_char((char *)imc_getstats(), ch);
#else
  imc_to_char(imc_getstats(), ch);
#endif
}

DEFINE_DO_FUN(do_rchannels)
{
  char buf[MAX_STRING_LENGTH];
  char arg[MAX_STRING_LENGTH];
  bool toggle;
  int i;
  const char *name;
  long flag;
  
  if (IS_NPC(ch))
  {
    imc_to_char("NPCs cannot use IMC.\n\r", ch);
    return;
  }

  one_argument(argument, arg);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif
  
  if (!arg[0])
  {
    /* list channel status */

    bool found=FALSE;
    
    strcpy(buf,
	   "channel      status\n\r"
	   "-------------------\n\r");

    for (i=0; i<numchannels; i++)
    {
      if (IS_SET(IMC_DENY_FLAGS(ch), imc_channels[i].flag))
      {
	sprintf(buf+strlen(buf),
		"%-10s : you are denied from this channel.\n\r",
		imc_channels[i].name);
	found=TRUE;
      }
      else if (CAN(ch, imc_channels[i].flag, imc_channels[i].minlevel))
      {
	sprintf(buf+strlen(buf),
		"%-10s : %s\n\r",
		imc_channels[i].name,
		(IS_SET(IMC_DEAF_FLAGS(ch), imc_channels[i].flag) ?
		 "OFF" : "ON"));
	found=TRUE;
      }
    }

    if (IS_SET(IMC_DENY_FLAGS(ch), IMC_RTELL))
    {
      strcat(buf, "RTells     : you cannot rtell.\n\r");
      found=TRUE;
    }
    else if (CAN(ch, IMC_RTELL, IMC_LEVEL_RTELL))
    {
      sprintf(buf+strlen(buf), "%-10s : %s\n\r", "RTell",
	      IS_SET(IMC_DEAF_FLAGS(ch), IMC_RTELL) ? "OFF" : "ON");
      found=TRUE;
    }

    if (IS_SET(IMC_DENY_FLAGS(ch), IMC_RBEEP))
    {
      strcat(buf, "RBeeps     : you cannot rbeep.\n\r");
      found=TRUE;
    }
    else if (CAN(ch, IMC_RBEEP, IMC_LEVEL_RBEEP))
    {
      sprintf(buf+strlen(buf), "%-10s : %s\n\r", "RBeep",
	      IS_SET(IMC_DEAF_FLAGS(ch), IMC_RBEEP) ? "OFF" : "ON");
      found=TRUE;
    }

    if (IS_SET(IMC_DEAF_FLAGS(ch), IMC_RINVIS))
      sprintf(buf+strlen(buf), "You are not visible to others over IMC.\n\r");

    if (!found)
      imc_to_char("You have access to no IMC channels.\n\r", ch);
    else
      imc_to_char(buf, ch);
      
    return;
  }

  /* turn some things on or off */

  while((argument=one_argument(argument, arg)), arg[0])
  {
    if (arg[0]=='-')
      toggle=FALSE;
    else if (arg[0]=='+')
      toggle=TRUE;
    else
    {
      imc_to_char("Syntax: rchannels          displays current IMC channels\n\r"
		   "        rchannels +chan    turn on a channel\n\r"
		   "        rchannels -chan    turn off a channel\n\r"
                   "chan may be an IMC channel name, 'rbeep', 'rtell', 'rinvis', or 'all'.\n\r"
		   "Multiple settings may be given in one command.\n\r", ch);
      return;
    }

    if (!str_cmp(arg+1, "all"))
    {
      if (toggle)
      {
	imc_to_char("ALL available IMC channels are now on.\n\r", ch);
	IMC_DEAF_FLAGS(ch)=0;
      }
      else
      {
	imc_to_char("ALL available IMC channels are now off.\n\r", ch);
	IMC_DEAF_FLAGS(ch)=-1L;
      }

      continue;
    }
    else if (!str_cmp(arg+1, "rtell") && CAN(ch, IMC_RTELL, IMC_LEVEL_RTELL))
    {
      name="RTell";
      flag=IMC_RTELL;
    }
    else if (!str_cmp(arg+1, "rbeep") && CAN(ch, IMC_RBEEP, IMC_LEVEL_RBEEP))
    {
      name="RBeep";
      flag=IMC_RBEEP;
    }
    else if (!str_cmp(arg+1, "rinvis"))
    {
      name="RInvis";
      flag=IMC_RINVIS;
      toggle=!toggle;
    }
    else
    {
      for (i=0; i<numchannels; i++)
      {
	if (CAN(ch, imc_channels[i].flag, imc_channels[i].minlevel) &&
#ifdef CIRCLE
	    !str_cmp((char *)imc_channels[i].name, arg+1))
#else
	    !str_cmp(imc_channels[i].name, arg+1))
#endif
	  break;
      }

      if (i==numchannels)
      {
	sprintf(buf, "You don't have access to an IMC channel called \"%s\".\n\r", arg);
	imc_to_char(buf, ch);
        continue;
      }

      name=imc_channels[i].name;
      flag=imc_channels[i].flag;
    }
  
    if (toggle && !IS_SET(IMC_DEAF_FLAGS(ch), flag))
    {
      if (flag==IMC_RINVIS)
	strcpy(buf, "You are already visible on IMC.\n\r");
      else
	sprintf(buf, "%s is already on.\n\r", name);
      imc_to_char(buf, ch);
      continue;
    }

    if (!toggle && IS_SET(IMC_DEAF_FLAGS(ch), flag))
    {
      if (flag==IMC_RINVIS)
	strcpy(buf, "You are already invisible on IMC.\n\r");
      else
	sprintf(buf, "%s is already off.\n\r", name);
      imc_to_char(buf, ch);
      continue;
    }

    if (toggle)
    {
      if (flag==IMC_RINVIS)
	strcpy(buf, "You are no longer invisible to IMC.\n\r");
      else
	sprintf(buf, "%s is now ON.\n\r", name);
      REMOVE_BIT(IMC_DEAF_FLAGS(ch), flag);
    }
    else
    {
      if (flag==IMC_RINVIS)
        strcpy(buf, "You are now invisible to rwho, rtell and rbeep on IMC.\n\r");
      else
	sprintf(buf, "%s is now OFF.\n\r", name);
      SET_BIT(IMC_DEAF_FLAGS(ch), flag);
    }

    imc_to_char(buf, ch);
  }
}

DEFINE_DO_FUN(do_rchanset)
{
  char arg[MAX_STRING_LENGTH];
  CHAR_DATA *victim;
  int flag, i;
  const char *name;
  int fn;
  bool found=FALSE;
  char buf[MAX_STRING_LENGTH];

  argument=one_argument(argument, arg);

#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  if (!arg[0])
  {
    imc_to_char("Syntax: rchanset <char>            - check flag settings\n\r"
		 "        rchanset <char> +<channel> - set allow flag\n\r"
		 "        rchanset <char> -<channel> - set deny flag\n\r"
		 "        rchanset <char> =<channel> - reset allow/deny\n\r",
		 ch);
    return;
  }

  if (!(victim=get_char_world(ch, arg)))
  {
    imc_to_char("They're not here.\n\r", ch);
    return;
  }

  if (IS_NPC(victim))
  {
    imc_to_char("Can't set rchannel status on NPCs.\n\r", ch);
    return;
  }

  if (get_trust(victim) >= get_trust(ch))
  {
    imc_to_char("You failed.\n\r", ch);
    return;
  }

  if (!argument[0])
  {
    sprintf(buf,
	    "%s's IMC channel flags:\n\r"
	    "------------------------------\n\r",
	    GET_NAME(victim));

    for (i=0; i<numchannels; i++)
    {
      if (IS_SET(IMC_ALLOW_FLAGS(victim), imc_channels[i].flag))
      {
	sprintf(buf+strlen(buf), "%-10s : allow flag set.\n\r",
		imc_channels[i].name);
	found=TRUE;
      }

      if (IS_SET(IMC_DENY_FLAGS(victim), imc_channels[i].flag))
      {
	sprintf(buf+strlen(buf), "%-10s : deny flag set.\n\r",
		imc_channels[i].name);
	found=TRUE;
      }
    }

    if (IS_SET(IMC_ALLOW_FLAGS(victim), IMC_RTELL))
    {
      sprintf(buf+strlen(buf), "%-10s : allow flag set.\n\r",
	      "RTell");
      found=TRUE;
    }

    if (IS_SET(IMC_DENY_FLAGS(victim), IMC_RTELL))
    {
      sprintf(buf+strlen(buf), "%-10s : deny flag set.\n\r",
	      "RTell");
      found=TRUE;
    }

    if (IS_SET(IMC_ALLOW_FLAGS(victim), IMC_RBEEP))
    {
      sprintf(buf+strlen(buf), "%-10s : allow flag set.\n\r",
	      "RBeep");
      found=TRUE;
    }

    if (IS_SET(IMC_DENY_FLAGS(victim), IMC_RBEEP))
    {
      sprintf(buf+strlen(buf), "%-10s : deny flag set.\n\r",
	      "RBeep");
      found=TRUE;
    }

    if (!found)
      sprintf(buf, "%s has no IMC flags set.\n\r", GET_NAME(victim));

    imc_to_char(buf, ch);
    return;
  }
  
  if (argument[0]=='-')
    fn=0;
  else if (argument[0]=='+')
    fn=1;
  else if (argument[0]=='=')
    fn=2;
  else
  {
    imc_to_char("Channel name must be preceeded by +, -, or =.\n\r", ch);
    return;
  }

  argument++;

  if (!str_cmp(argument, "rtell"))
  {
    flag=IMC_RTELL;
    name="RTell";
  }
  else if (!str_cmp(argument, "rbeep"))
  {
    flag=IMC_RBEEP;
    name="RBeep";
  }
  else
  {
    for (i=0; i<numchannels; i++)
#ifdef CIRCLE
      if (!str_cmp((char *)imc_channels[i].name, argument))
#else
      if (!str_cmp(imc_channels[i].name, argument))
#endif
	break;

    if (i==numchannels)
    {
      imc_to_char("No such channel.\n\r", ch);
      return;
    }

    flag=imc_channels[i].flag;
    name=imc_channels[i].name;
  }

  switch(fn)
  {
  case 0: /* set deny flag */
    if (IS_SET(IMC_DENY_FLAGS(victim), flag))
    {
      imc_to_char("Deny flag already set.\n\r", ch);
      return;
    }

    SET_BIT(IMC_DENY_FLAGS(victim), flag);
    REMOVE_BIT(IMC_ALLOW_FLAGS(victim), flag);
    sprintf(buf, "The gods have revoked your %s priviliges.\n\r", name);
    imc_to_char(buf, victim);
    imc_to_char("Ok.\n\r", ch);
#ifdef ROM
    sprintf(buf, "$N revokes %s's access to %s.", GET_NAME(victim), name);
    wiznet(buf, ch, NULL, WIZ_PENALTIES, WIZ_SECURE, 0);
#endif
    return;

  case 1: /* set allow flag */
    if (IS_SET(IMC_ALLOW_FLAGS(victim), flag))
    {
      imc_to_char("Allow flag already set.\n\r", ch);
      return;
    }

    SET_BIT(IMC_ALLOW_FLAGS(victim), flag);
    REMOVE_BIT(IMC_DENY_FLAGS(victim), flag);
    sprintf(buf, "The gods have allowed you access to %s.\n\r", name);
    imc_to_char(buf, victim);
    imc_to_char("Ok.\n\r", ch);
#ifdef ROM
    sprintf(buf, "$N allows %s access to %s.", GET_NAME(victim), name);
    wiznet(buf, ch, NULL, WIZ_PENALTIES, WIZ_SECURE, 0);
#endif
    return;

  case 2: /* clears flags */
    if (IS_SET(IMC_ALLOW_FLAGS(victim), flag))
    {
      REMOVE_BIT(IMC_ALLOW_FLAGS(victim), flag);
      sprintf(buf, "The gods have removed your special access to %s.\n\r",
	      name);
      imc_to_char(buf, victim);
#ifdef ROM
      sprintf(buf, "$N removes %s's access to %s.", GET_NAME(victim), name);
      wiznet(buf, ch, NULL, WIZ_PENALTIES, WIZ_SECURE, 0);
#endif
      found=TRUE;
    }

    if (IS_SET(IMC_DENY_FLAGS(victim), flag))
    {
      REMOVE_BIT(IMC_DENY_FLAGS(victim), flag);
      sprintf(buf, "The gods have restored your %s priviliges.\n\r", name);
      imc_to_char(buf, victim);
#ifdef ROM
      sprintf(buf, "$N restores %s to %s.", name, GET_NAME(victim));
      wiznet(buf, ch, NULL, WIZ_PENALTIES, WIZ_SECURE, 0);
#endif
      found=TRUE;
    }
    
    if (!found)
      imc_to_char("They have no flags to clear.\n\r", ch);
    else
      imc_to_char("Ok.\n\r", ch);
    return;
  }
}

/* renamed from do_rchannel */
static void do_imcchannel(const imc_char_data *from, int number,
			  const char *argument, int emote)
{
  DESCRIPTOR_DATA *d;
  CHAR_DATA *victim;
  const char *str;
  const char *arg;
  char buf[IMC_DATA_LENGTH];
  int chan;

  for (chan=0; chan<numchannels; chan++)
    if (imc_channels[chan].number==number)
      break;

  if (chan==numchannels)
    return;

  str=emote ? imc_channels[chan].emotestr : imc_channels[chan].chatstr;
  arg=color_itom(argument);

  for (d=descriptor_list; d; d=d->next)
  {
    if (d->connected==CON_PLAYING &&
	(victim=d->original ? d->original : d->character)!=NULL &&
	!IS_QUIET(victim) &&
	!IS_SILENT(victim) &&
	!IS_SET(IMC_DEAF_FLAGS(victim), imc_channels[chan].flag) &&
	CAN(victim, imc_channels[chan].flag, imc_channels[chan].minlevel))
    {
#if NO_VSNPRINTF
      sprintf(buf, str, getname(victim, from), arg);
#else
      snprintf(buf, IMC_DATA_LENGTH, str, getname(victim, from), arg);
#endif
      imc_to_char(buf, victim);
    }
  }
}

/*  Traceroute and ping.
 *
 *  Be lazy - only remember the last traceroute
 */

static char lastping[IMC_MNAME_LENGTH];
static char pinger[100];

DEFINE_DO_FUN(do_rping)
{
  char arg[MAX_STRING_LENGTH];
  struct timeval tv;
  
  CHECKIMC(ch);
  
#ifdef CIRCLE
  skip_spaces(&argument);
#endif

  one_argument(argument, arg);
  
  if (!arg[0])
  {
    imc_to_char("Ping which mud?\n\r", ch);
    return;
  }

  CHECKMUD(ch, arg);

  gettimeofday(&tv, NULL);
  strcpy(lastping, arg);
#ifdef CIRCLE
  strcpy(pinger, ch->player.name);
#else
  strcpy(pinger, ch->name );
#endif
  imc_send_ping(arg, tv.tv_sec, tv.tv_usec);
}

void imc_traceroute(int ping, const char *pathto, const char *pathfrom)
{
  if (!str_cmp(imc_firstinpath(pathfrom), lastping))
  {
    DESCRIPTOR_DATA *d;
    CHAR_DATA *ch=NULL;
    char buf[MAX_STRING_LENGTH];

    for (d=descriptor_list; d; d=d->next)
    {
      if (d->connected == CON_PLAYING &&
	  (ch =d->original ? d->original : d->character)!=NULL &&
#ifdef CIRCLE
          !str_cmp(ch->player.name, pinger))
#else
	  !str_cmp(ch->name, pinger))
#endif
	break;
    }
    
    if (!ch)
      return;

    sprintf(buf,
	    "%s: %dms round-trip-time.\n\r"
	    "Return path: %s\n\r"
	    "Send path:   %s\n\r",
	    imc_firstinpath(pathfrom),
	    ping,
	    pathfrom,
	    pathto ? pathto : "unknown");

    imc_to_char(buf, ch);
  }
}

void imc_recv_chat(const imc_char_data *from, int channel,
		   const char *argument)
{
  do_imcchannel(from, channel, argument, 0);
}

void imc_recv_emote(const imc_char_data *from, int channel,
		    const char *argument)
{
  do_imcchannel(from, channel, argument, 1);
}

/* new imc_whoreply_* support, see imc-interp.c */

/* expanded for minimal mud-specific code. I really don't want to replicate
 * stock in-game who displays here, since it's one of the most commonly
 * changed pieces of code. shrug.
 */
#ifndef LOCAL_RWHO_REPLY_ROUTINE
void process_rwho(const imc_char_data *from, const char *argument)
{
  CHAR_DATA *victim;
  int count=0;
  char output[MAX_STRING_LENGTH];
  DESCRIPTOR_DATA *d;

  imc_whoreply_start(from->name);
  
#ifdef ACK
  imc_whoreply_add(color_mtoi("@@RWho's Playing " mudnamecolor ":\n\r"));
#endif

  for (d = descriptor_list; d; d = d->next)
  {
    victim = ( d->original ) ? d->original : d->character;

    if (d->connected != CON_PLAYING || !imc_visible(from, imc_getdata(victim)) ||
        IS_NPC(victim))
      continue;

    count++;
    
#ifdef ACK
    sprintf(output,
	    "@@W[@@p%s@@W] @@G%s@@N%s@@N\n\r",
	    ((IS_IMMORTAL(victim) ||
	      (victim->adept_level>0)) ? victim->pcdata->who_name
	     : is_remort(victim) ? "    @@mREMORT@@N    "
	     : "    @@cMORTAL@@N    "),
	    GET_NAME(victim), GET_TITLE(victim));
#elif defined(CIRCLE)
    sprintf(output, "%s %s\r\n",
            GET_NAME(victim),
            GET_TITLE(victim));
#else
    sprintf(output, "%s%s\n\r",
	    GET_NAME(victim),
	    GET_TITLE(victim));
#endif

    imc_whoreply_add(color_mtoi(output));
  }

  sprintf(output, "\n\rRWHO for %s [%d players found]\n\r", imc_name, count);
  imc_whoreply_add(output);
  imc_whoreply_end();
}
#endif
/* edit this if you want to support rfinger */
#ifndef LOCAL_RFINGER_ROUTINE
void process_rfinger(const imc_char_data *from, const char *argument)
{
  imc_send_whoreply(from->name,
		    "Sorry, no information is available of that type.\n\r", -1);
}
#endif

void imc_recv_who(const imc_char_data *from, const char *type)
{
  char arg[MAX_STRING_LENGTH];
  char output[MAX_STRING_LENGTH];

  type=imc_getarg(type, arg, MAX_STRING_LENGTH);

  if (!str_cmp(arg, "who"))
  {
    process_rwho(from, type);
    return;
  }
  else if (!str_cmp(arg, "finger"))
  {
    process_rfinger(from, type);
    return;
  }
  else if (!str_cmp(arg, "info"))
    sprintf(output, "Site Information --\r\nSite Name: %s\r\nSite Host: %s\r\nAdmin Email: %s\r\nAdmin MUDMail: %s\r\nWeb Site: %s\r\nIMC Information --\r\nIMC Version: %s\r\nIMC Details: %s\r\nIMC Flags: %s\r\nMUD Information:\r\n%s\r\n",
	imc_siteinfo.name,
    imc_siteinfo.host,
    imc_siteinfo.email,
    imc_siteinfo.imail,
    imc_siteinfo.www,
    IMC_VERSIONID,
    imc_siteinfo.details,
    imc_siteinfo.flags,
	IMC_MUD_INFO);
  else if (!str_cmp(arg, "list"))
    strcpy(output, imc_list(3));
  else if (!str_cmp(arg, "config"))
    strcpy(output, imc_list(4));
  else if (!str_cmp(arg, "direct"))
    strcpy(output, imc_list(0));
  else if (!str_cmp(arg, "istats"))
    imc_send_whoreply(from->name, imc_getstats(), -1);
  else if (!str_cmp(arg, "options") ||
	   !str_cmp(arg, "services") ||
	   !str_cmp(arg, "help"))
    strcpy(output,
	   "Available rquery types:\n\r"
	   "help       - this list\n\r"
	   "who        - who listing\n\r"
	   "info       - mud information\n\r"
	   "list       - active IMC connections\n\r"
	   "direct     - direct IMC connections\n\r"
	   "config     - local IMC configuration\n\r"
	   "istats     - network traffic statistics\n\r"
	   /* uncomment if you support rfinger */
	   /* "finger xxx - finger player xxx\n\r" */
	   );
  else
    strcpy(output, "Sorry, no information is available of that type.\n\r");

  imc_send_whoreply(from->name, color_mtoi(output), -1);
}

void imc_recv_whoreply(const char *to, const char *text, int sequence)
{
  DESCRIPTOR_DATA *d;
  CHAR_DATA *victim;

  for (d=descriptor_list; d; d=d->next)
  {
    if (d->connected==CON_PLAYING &&
	(victim=d->original ? d->original : d->character)!=NULL &&
        is_name((char *)to, GET_NAME(victim)))
    {
      imc_to_pager(color_itom(text), victim);
      return;
    }
  }
}

void imc_recv_whois(const imc_char_data *from, const char *to)
{
  DESCRIPTOR_DATA *d;
  CHAR_DATA *victim;
  char buf[MAX_STRING_LENGTH];

  for (d=descriptor_list; d; d = d->next)
  {
    if (d->connected==CON_PLAYING &&
	(victim = d->original ? d->original : d->character)!=NULL &&
	!IS_NPC(victim) &&
	imc_visible(from, imc_getdata(victim)) &&
#ifdef CIRCLE
	(!str_cmp((char *)to, GET_NAME(victim)) ||
#else
	(!str_cmp(to, GET_NAME(victim)) ||
#endif
	 (strlen(to)>3 && !str_prefix(to, GET_NAME(victim)))))
    {
      sprintf(buf, "rwhois %s : %s@%s is online.\n\r", to,
	      GET_NAME(victim), imc_name);
      imc_send_whoisreply(from->name, buf);
    }
  }
}

void imc_recv_whoisreply(const char *to, const char *text)
{
  DESCRIPTOR_DATA *d;
  CHAR_DATA *victim;

  for (d=descriptor_list; d; d=d->next)
  {
    if (d->connected==CON_PLAYING &&
	(victim=d->original ? d->original : d->character)!=NULL &&
        !str_cmp((char *)to, GET_NAME(victim)))
    {
#ifdef CIRCLE
      imc_to_char((char *)color_itom(text), victim);
#else
      imc_to_char(color_itom(text), victim);
#endif
      return;
    }
  }
}

void imc_recv_tell(const imc_char_data *from, const char *to,
		   const char *argument, int isreply)
{
  DESCRIPTOR_DATA *d;
  CHAR_DATA *victim, *vch;
  char buf[IMC_DATA_LENGTH];

  if (!strcmp(to, "*")) /* ignore messages to system */
    return;

  victim=NULL;
  for (d=descriptor_list; d; d=d->next)
  {
    if (d->connected==CON_PLAYING &&
	(vch=d->original ? d->original : d->character)!=NULL &&
	!IS_NPC(vch) &&  /* sanity check */
        !IS_RINVIS(vch) &&
        (isreply || imc_visible(from, imc_getdata(vch))))
    {
      if (!str_cmp((char *)to, GET_NAME(vch)))
      {
	victim=vch;
	break;
      }
      if (is_name((char *)to, GET_NAME(vch)))
	victim=vch;
    }
  }

  if (victim)
  {
    if (IS_SET(IMC_DEAF_FLAGS(victim), IMC_RTELL) ||
	IS_QUIET(victim) ||
#ifdef ROM    
	IS_SET(victim->comm, COMM_DEAF) ||
#endif
#ifdef ACK
	IS_SET(victim->pcdata->pflags, PFLAG_AFK) ||
#endif
	IS_SILENT(victim) ||
	IS_SET(IMC_DENY_FLAGS(victim), IMC_RTELL))
    {
#ifdef CIRCLE
      if (str_cmp((char *)imc_nameof(from->name), "*"))
#else
      if (str_cmp(imc_nameof(from->name), "*"))
#endif
      {
	sprintf(buf, "%s is not receiving tells.", to);
	imc_send_tell(NULL, from->name, buf, 1);
      }
      return;
    }
    
#ifdef ROM
    if (IS_SET(victim->comm, COMM_AFK) &&
	str_cmp(imc_nameof(from->name), "*"))
    {
      sprintf(buf, "%s is AFK, but your tell will go through when they "
	      "return.", to);
      imc_send_tell(NULL, from->name, buf, 1);
    }
    else if (!victim->desc &&
	     str_cmp(imc_nameof(from->name), "*"))
    {
      sprintf(buf, "%s is switched, but your tell will go through when "
	      "they return.", to);
      imc_send_tell(NULL, from->name, buf, 1);
    }
#endif

#ifdef CIRCLE
    if (str_cmp((char *)imc_nameof(from->name), "*"))     /* not a system message */
#else
    if (str_cmp(imc_nameof(from->name), "*"))     /* not a system message */
#endif
    {
      if (IMC_RREPLY(victim))
	imc_strfree(IMC_RREPLY(victim));
      if (IMC_RREPLY_NAME(victim))
	imc_strfree(IMC_RREPLY_NAME(victim));
      
      IMC_RREPLY(victim)=imc_strdup(from->name);
      IMC_RREPLY_NAME(victim)=imc_strdup(getname(victim, from));
    }	
    
#if NO_VSNPRINTF
    sprintf(buf,
            RTELL_FORMAT_2,
	    getname(victim, from),
	    color_itom(argument));
#else
    snprintf(buf, IMC_DATA_LENGTH,
	     RTELL_FORMAT_2,
	     getname(victim, from), color_itom(argument));
#endif

#ifdef ROM
    if (IS_SET(victim->comm, COMM_AFK) || !victim->desc)
      add_buf(victim->pcdata->buffer, buf);
    else
      imc_to_pager(buf, victim);
#else
    imc_to_pager(buf, victim);
#endif
  }
  else
  {
    sprintf(buf, "%s is not here.", to);
    imc_send_tell(NULL, from->name, buf, 1);
  }
}

void imc_recv_beep(const imc_char_data *from, const char *to)
{
  DESCRIPTOR_DATA *d;
  CHAR_DATA *victim, *vch;
  char buf[IMC_DATA_LENGTH];

  if (!strcmp(to, "*")) /* ignore messages to system */
    return;

  victim=NULL;
  for (d=descriptor_list; d; d=d->next)
  {
    if (d->connected==CON_PLAYING &&
	(vch=d->original ? d->original : d->character)!=NULL &&
	imc_visible(from, imc_getdata(vch)))
    {
      if (!str_cmp((char *)to, GET_NAME(vch)))
      {
	victim=vch;
	break;
      }
      if (is_name((char *)to, GET_NAME(vch)))
	victim=vch;
    }
  }

  if (victim)
  {
    if (IS_SET(IMC_DEAF_FLAGS(victim), IMC_RBEEP) ||
	IS_SILENT(victim) ||
	!CAN(victim, IMC_RTELL, IMC_LEVEL_RTELL))
    {
      sprintf(buf, "%s is not receiving beeps.", to);
      imc_send_tell(NULL, from->name, buf, 1);
      return;
    }
    
    /* always display the true name here */
    sprintf(buf, RBEEP_FORMAT_2, from->name);
    imc_to_char(buf, victim);
  }
  else
  {
    sprintf(buf, "%s is not here", to);
    imc_send_tell(NULL, from->name, buf, 1);
  }
}



/**  IMC MAIL  **/

/* shell for sending IMC mail */

void imc_post_mail(CHAR_DATA *from,
		   const char *sender,
		   const char *to_list,
		   const char *subject,
		   const char *date,
		   const char *text)
{
  char arg[MAX_STRING_LENGTH];
  const char *temp;

  CHECKIMC(from);

  if (!strchr(to_list, '@'))
    return;

  for (temp=one_argument((char *)to_list, arg); arg[0]; /* blech */
       temp=one_argument((char *)temp, arg))
    if (strchr(arg, '@'))
      CHECKMUD(from, imc_mudof(arg));

  imc_send_mail(sender, to_list, subject, date, color_mtoi(text));
}

/* Since there are half-a-dozen different ways that people do this, I'm
 * sticking to the stock code, plus Erwin's board system.
 */

#ifdef SMAUG

/*
 * Find the IMC mail board.  It SHOULD exist.. :)
 */
BOARD_DATA *imc_find_board(void)
{
  BOARD_DATA *board;
  
  for (board = first_board; board; board = board->next)
    if (board->board_obj == sysdata.imc_mail_vnum)
      return board;
  return NULL;
}

char *imc_mail_arrived (const char *from, const char *to, const char *date,
                        const char *subject, const char *text)
{
  void write_board(BOARD_DATA *board);
  BOARD_DATA *board;
  NOTE_DATA *pnote;
  char buf[MAX_STRING_LENGTH];
  
  /* don't let people write to 'all' over IMC */
  /* yeesh, why doesn't anyone maintain 'const'-ness any more? */
  
  if (is_name("all", (char *)to))
    return "Notes to 'all' are not accepted.";
  
  if ( !(board = imc_find_board()) )
    return "Could not find the IMC board.";
  
  if ( board->num_posts >= board->max_posts )
    return "There is no room on the IMC board to post your note.";
  
  CREATE( pnote, NOTE_DATA, 1 );
  
  /* Smash all the tildes, just in case
   * This is a complete mess, but smaug's string hasher would do bad things
   * if we smash_tilde'd a STRALLOC'd string..
   */

#define SET_STR(new, old) \
  do { \
    strcpy(buf, old); \
    smash_tilde(buf); \
    pnote->new = STRALLOC(buf); \
  } while(0)

  SET_STR(sender, from);
  SET_STR(to_list, to);
  SET_STR(date, date);
  SET_STR(subject, color_itom(subject));
  SET_STR(text, text);
#undef SET_STR

  pnote->voting = 0;
  pnote->yesvotes = str_dup("");
  pnote->novotes = str_dup("");
  pnote->abstentions = str_dup("");

  LINK(pnote, board->first_note, board->last_note, next, prev);
  board->num_posts++;
  write_board( board );
  
  return NULL;                          /* Errors? What errors? <G> */
}

#endif /* SMAUG */

#if defined(ACK)
/* Added support for ackmud boards.
 * You must configure IMC_BOARD_PUBLIC and IMC_BOARD_IMMORTAL
 * in imc-mercbase.h to suit your mud!
 */
#ifndef IMC_BOARD_PUBLIC
#error You must #define a value for IMC_BOARD_VNUM in imc-mercbase.h!
#endif
#ifndef IMC_BOARD_IMMORTAL
#error You must #define a value for IMC_BOARD_VNUM in imc-mercbase.h!
#endif

char *imc_mail_arrived(const char *from, const char *to, const char *date,
		       const char *subject, const char *text)
{
  BOARD_DATA *board;
  MESSAGE_DATA *msg;
  char buf[MAX_STRING_LENGTH];
  const char *temp=to;
  int vnum;
  int toimm=0;
  char toname[MAX_STRING_LENGTH];

  toname[0]=0;
  while ((temp=imc_getarg(temp, buf, MAX_STRING_LENGTH)), buf[0])
  {
    if (!strchr(buf, '@'))
    {
      if (toname[0])
	return "Sorry, multiple to: names over IMC are not supported here.";
      strcpy(toname, buf);
    }
  }

  if (str_cmp("all", toname))
    return "Notes to 'all' are not accepted.";
  
  if (str_cmp("immortal", toname) || str_cmp("imm", toname) ||
      str_cmp("imms", toname) || str_cmp("immortals", toname))
  {
    vnum=IMC_BOARD_IMMORTAL;
    toimm=1;
  }
  else
    vnum=IMC_BOARD_PUBLIC;

  /* First find the board, and if not there, create one. */
  for (board=first_board; board != NULL; board=board->next)
  {
    if (board->vnum==vnum)
      break;
  }

  if (board==NULL)
  {
    /* uh oh.. */
    OBJ_INDEX_DATA *pObj;
    int i;

    for (i=0; i<MAX_KEY_HASH; i++)
      for (pObj=obj_index_hash[i]; pObj; pObj=pObj->next)
	if (pObj->item_type==ITEM_BOARD && pObj->value[3]==vnum)
          break;

    if (!pObj)
      return "Couldn't find a board to post the note on.";
    
    board=load_board(pObj);
  }

  GET_FREE(msg, message_free);
  msg->datetime=time(NULL);

  /* erk.. this is messy.
   * assume that notes to 'immortal' are public
   * assume that anything else is private
   */

  if (toimm)
    sprintf(buf, "%s @@a%s@@N\n\r", subject, date);
  else
    sprintf(buf, "to: %s @@a%s@@N\n\r", toname, date);
  msg->title=str_dup(buf);
  msg->author=str_dup(from);
  msg->board=board;
  msg->message=str_dup(color_itom(text));
  LINK(msg, board->first_message, board->last_message, next, prev);

  return NULL;
}
#endif /* ACK */

/* #define ERWIN   -- to use the code for Erwin's boards */

#if defined(MERC) || defined(ROM) || defined(ENVY)
#if !defined(ERWIN)

/* post a note; currently all rnotes go to the default note spool */
char *imc_mail_arrived(const char *from, const char *to, const char *date,
		       const char *subject, const char *text)
{
  NOTE_DATA *pnote;
#if defined(MERC) || defined(ENVY)
  NOTE_DATA *prev_note;
  FILE *fp;
#endif

  /* don't let people write to 'all' over IMC */
  /* yeesh, why doesn't anyone maintain 'const'-ness any more? */

  if (is_name("all", (char *)to))
    return "Notes to 'all' are not accepted.";

#ifdef ROM
  pnote=new_note();
  pnote->type=NOTE_NOTE;
#else
  pnote=alloc_perm(sizeof(*pnote));
#endif

  pnote->sender=str_dup(from);
  pnote->to_list=str_dup(to);
  pnote->date=str_dup(date);
  pnote->subject=str_dup(subject);
  pnote->text=str_dup(color_itom(text));
  pnote->date_stamp=current_time;
  pnote->next=NULL;

  /* a bit of paranoia re. tildes here.. */
  smash_tilde(pnote->sender);
  smash_tilde(pnote->to_list);
  smash_tilde(pnote->date);
  smash_tilde(pnote->subject);
  smash_tilde(pnote->text);

#ifdef ROM
  append_note(pnote);
#else
  if (!note_list)
  {
    note_list = pnote;
  }
  else
  {
    for (prev_note = note_list; prev_note->next; prev_note = prev_note->next)
      ;
    prev_note->next = pnote;
  }
  
  fclose (fpReserve);
  if (!(fp = fopen (NOTE_FILE, "a")))
  {
    perror (NOTE_FILE);
    fpReserve = fopen (NULL_FILE, "r");
    return "Couldn't open the note file";
  }
  else
  {
    fprintf (fp, "Sender  %s~\n", pnote->sender);
    fprintf (fp, "Date    %s~\n", pnote->date);
    fprintf (fp, "Stamp   %ld\n", (unsigned long) pnote->date_stamp);
    fprintf (fp, "To      %s~\n", pnote->to_list);
    fprintf (fp, "Subject %s~\n", pnote->subject);
    fprintf (fp, "Text\n%s~\n\n", pnote->text);
    fclose (fp);
  }
  fpReserve = fopen (NULL_FILE, "r");
#endif

  return NULL;                            /* Errors? What errors? <G> */
}

#else

/*
 * If you are using my code (ftp://pip.dknet.dk/pub/pip1773/board-2.tgz)
 * there is another imc_mail_arrived() below - Erwin
 *
 * Note that this assumes that your make_note() does a smash_tilde(), the
 * latest version of the board snippet does, but your may not. Oh well.
 */

char *imc_mail_arrived (const char *from, const char *to, const char *date,
                        const char *subject, const char *text)
{
  /* don't let people write to 'all' over IMC */
  /* yeesh, why doesn't anyone maintain 'const'-ness any more? */

  if (is_name("all", (char *)to))
    return "Notes to 'all' are not accepted.";
  
  make_note ("Personal", from, to, subject, 14, color_itom(text));
  return NULL;
}

#endif /* Erwin's board code */
#endif /* merc/rom/envy */

#ifdef CIRCLE
char *imc_mail_arrived(const char *from, const char *to, const char *date,
                       const char *subject, const char *text)
{
  char buf[MAX_STRING_LENGTH];

  /* don't let people write to 'all' over IMC */
  /* yeesh, why doesn't anyone maintain 'const'-ness any more? */

  if (isname("all", (char *) to))
    return "Notes to 'all' are not accepted.";

  sprintf(buf,
          "-= IMC Header =-\r\n"
          "   From: %s\r\n"
          "     To: %s\r\n"
          "   Date: %s\r\n"
          "Subject: %s\r\n"
          "\r\n%s",
          from,
          to,
          date,
          subject,
          text);

  store_mail(get_id_by_name((char *)to), 0, buf);

  return NULL;                  /* Errors? What errors? <G> */
}
#endif

void imc_log(const char *string)
{
#ifdef LOCAL_LOG_ROUTINE
  imc_local_log(string);
#else
  char buf[MAX_STRING_LENGTH];
 
  sprintf(buf, "imc: %s", string);
  if(imc_log_on) /* configurable log messages */
  {
	  log_string(buf);
#ifdef ACK
  	monitor_chan(buf, MONITOR_CONNECT);
#endif

#ifdef CIRCLE
  	mudlog(buf, BRF, LVL_IMMORT, FALSE); /* FALSE b/c it's already written to file
										this fix to make imc syslogs show up inside
										CircleMUDs. -- Scion */
#endif

#ifdef ROM
  /*  ick, wiznet is a bit broken wrt. passing non-objects to act..
   *  it should really take a void * as its 3rd argument
   */
        fprintf(stderr,"%s\n",buf);
  	wiznet("imc: $t", NULL, (OBJ_DATA *)string, WIZ_IMC, 0, 0);
#endif
  }
#endif
}

void imc_debug(const imc_connect *c, int out, const char *string)
{
  char buf[MAX_STRING_LENGTH];
  char *dir;

  dir=out ? "<" : ">";

  /* chop to 130 chars (should fit on 2 lines) */
  sprintf(buf, "%s %s %-.130s", imc_getconnectname(c), dir, string);
#ifdef ROM
  wiznet("$t", NULL, (OBJ_DATA *)buf, WIZ_IMCDEBUG, 0, 0);
#endif
#ifdef CIRCLE
  mudlog(buf, CMP, LVL_IMPL, FALSE); /* Scion */
#endif

}

