musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/* 
 * MusicMUD - Colour and Character Set Module
 * Copyright (C) 1998-2003 Abigail Brady
 * 
 * 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; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 */
#include <string.h>
#include <ctype.h>
#include <stdio.h>

#include "msi.h"
#include "Socket.h"
#include "musicmud.h"
#include "privs.h"
#include "format.h"
#include "util.h"
#include "pflags.h"
#include "verbs.h"
#include "misc.h"

#define MODULE "colour"

#include "wcwidth.c"

//! the blank colours.
const colstate_t blank;


const char *namecol(int col)
{
  static const char *cols[] =
  {"black", "red", "green", "yellow",
   "blue", "mangenta", "cyan", "white"};
  
  return cols[col & 7];
}

static string desccol(int fgcol)
{
  if (fgcol == 0) return "#000000";
  if (fgcol == 1) return "#d00000";
  if (fgcol == 2) return "#00d000";
  if (fgcol == 3) return "#d0d000";
  if (fgcol == 4) return "#0000d0";
  if (fgcol == 5) return "#d000d0";
  if (fgcol == 6) return "#00d0d0";
  if (fgcol == 7) return "#d0d0d0";

  if (fgcol == 8)  return "#808080";
  if (fgcol == 9)  return "#ff0000";
  if (fgcol == 10) return "#00ff00";
  if (fgcol == 11) return "#ffff00";
  if (fgcol == 12) return "#0000ff";
  if (fgcol == 13) return "#ff00ff";
  if (fgcol == 14) return "#00ffff";
  if (fgcol == 15) return "#ffffff";

  if (fgcol >= 16 && fgcol <= 232) {
    const char *scale[] = {"00", "5f", "87", "af", "df", "ff"};
    fgcol -= 16;
    int red = (fgcol / 36) % 6;
    int grn = (fgcol / 6)  % 6;
    int blu = (fgcol)      % 6;
    return ssprintf("#%s%s%s", scale[red], scale[grn], scale[blu]);
  }

  return "????";
}

static string make_mxpdifference(colstate_t &current, const colstate_t &need)
{
  string s;

  colstate_t needed = need;

  if (current.bgcol == -1)   current.bgcol = 0;
  if (current.fgcol == -1)   current.fgcol = 7;
  if (current.bold)          current.fgcol |= 8;

  if (needed.bgcol == -1)    needed.bgcol = 0;
  if (needed.fgcol == -1)    needed.fgcol = 7;
  if (needed.bold)           needed.fgcol |= 8;

  if (current.sout && (!needed.sout || 
		       current.sout != needed.sout ||
		       current.fgcol != needed.fgcol ||
		       current.bgcol != needed.bgcol ||
		       current.undl != needed.undl)) {
    s += "</s>";
    current.sout = 0;
  }

  if (current.ital && (!needed.ital || current.fgcol != needed.fgcol ||
		       current.bgcol != needed.bgcol ||
		       current.undl != needed.undl)) {
    s += "</i>";
    current.ital = 0;
  }


  if (current.undl && (!needed.undl || current.fgcol != needed.fgcol 
		       || current.bgcol != needed.bgcol)) {
    s += "</u>";
    current.undl = 0;
  }

  if (current.fgcol != needed.fgcol || needed.bgcol != needed.bgcol) {
    if ((current.fgcol != 7 || current.bgcol != 0))
      s += ssprintf("</color>");

    if (needed.fgcol != 7 || needed.bgcol != 0)
      s += ssprintf("<color fore=%s back=%s>", 
		    desccol(needed.fgcol),
		    desccol(needed.bgcol));
 
    current.fgcol = needed.fgcol;
    current.bgcol = needed.bgcol;
  }


  if (needed.undl && !current.undl) {
    s += "<u>";
    current.undl = 1;
  }

  if (needed.ital && !current.ital) {
    s += "<i>";
    current.ital = 1;
  }

  if (needed.sout && !current.sout) {
    s += "<s>";
    current.sout = 1;
  }

  return s;
}


//! spit out the escape sequence to get from current to needed, and update current with needed
static string make_difference(colstate_t &current, const colstate_t &needed)
{
    string esc = "";
    if (current.bold && !needed.bold ||
	current.undl && !needed.undl ||
	current.ital && !needed.ital ||
	current.sout && !needed.sout ||
	(needed.fgcol == -1 && current.fgcol != -1) ||
	(needed.bgcol == -1 && current.bgcol != -1)) {
      esc += "0;";
      int olddec = current.dec;
      current = blank;
      current.dec = olddec;
    }
    if (current.bold != needed.bold) {
      esc += needed.bold?"1;":"22;";
    }
    if (current.undl != needed.undl) {
      esc += needed.undl?"4;":"24;";
    }
    if (current.ital != needed.ital) {
      esc += needed.ital?"3;":"23;";
    }
    if (current.sout != needed.sout) {
      esc += needed.sout?"9;":"29;";
    }
    if (current.fgcol != needed.fgcol) {
      char blah[20];
      if (needed.fgcol < 8)
	sprintf(blah, "%i;", 30+needed.fgcol);
      else
	sprintf(blah, "38;5;%i;", needed.fgcol);
      esc += blah;
    }
    if (current.bgcol != needed.bgcol) {
      char blah[20];
      if (needed.bgcol < 8)
	sprintf(blah, "%i;", 40+needed.bgcol);
      else
	sprintf(blah, "48;5;%i;", needed.bgcol);
      esc += blah;
    }
    esc = esc.substr(0, esc.length()-1);
    string buf = "";
    if (esc.length()) {
      buf = "\033[";
      buf += esc;
      buf += "m";
    }
    if (current.dec != needed.dec) {
      if (needed.dec) buf += "\033(0";
      else buf += "\033(B";
    }
    current = needed;
    return buf;
}

//! spit out the difference to get from current to needed, but only the DEC part of it
static string make_decdifference(colstate_t &current, const colstate_t &needed)
{
  string buf;
  if (current.dec != needed.dec) {
    if (needed.dec) buf += "\033(0";
    else buf += "\033(B";
  }
  current = needed;
  return buf;
}

#define HAS_256COLS 1
#define NO_ANSI     2
#define CRLF        4
#define DEC         16
#define JUSTLENGTH  64

//! charcode->colourindex map
static char *bcols = "lrgybmcw";

//! lookup a 'classic' colour
static char
lookup_classic(char w)
{
  if (w=='S') return 'G';
  if (w=='o') return 'c';
  if (w=='p') return 'C';
  if (w=='P') return 'B';
  if (w=='t') return 'y';
  if (w=='s') return 'g';
  if (w=='k') return 'y';
  if (w=='i') return 'Y';
  if (w=='d') return 'W';
  if (w=='D') return 'C';

  return w;
}

//! lookup a 'paper' colour
static char
lookup_paper(char w)
{
  if (w=='o') return 'b';
  if (w=='p') return 'R';
  if (w=='d') return 'L';
  if (w=='D') return 'l';

  w = lookup_classic(w);

  if (w=='w') return 'l';
  if (w=='W') return 'L';

  if (w=='Y' || w=='y') return 'r';

  if (w=='l') return 'W';
  if (w=='L') return 'w';

  return w;
}

//! lookup a 'yellow' colour
static char
lookup_yellow(char w)
{
  if (w=='d') return 'Y';
  if (w=='D') return 'y';

  return lookup_classic(w);
}

//! lookup a 'red' colour
static char
lookup_red(char w)
{
  if (w=='o') return 'r';
  if (w=='P') return 'R';

  if (w=='p') return 'Y';

  return lookup_classic(w);
}

//! a colour-lookuper
typedef char(*lookup_t)(char);

//! lookup a colour in the given scheme
static char
lookup_col(char what, const char *scheme)
{
  if (!scheme)
    return what;
  lookup_t a = lookup_classic;
  if (!strcmp(scheme, "yellow")) a = lookup_yellow;
  if (!strcmp(scheme, "red")) a = lookup_red;
  if (!strcmp(scheme, "paper")) a = lookup_paper;
  return a(what);
}

//! try to set the foreground colour
static bool setfgcol(colstate_t &needed, char code, const Player *who, 
		     int flags, const char *scheme, int tripping=0) {
  if (who) {
    char name[256];
    sprintf(name, "col.%c", code);
    const char *of = who->get(name);
    if (of && strlen(of)==2 && of[0]=='^') {
      code = of[1];
    } else if (of && strlen(of)==5) {
      if (flags & HAS_256COLS) {
	const char *src = of+1;
	int rd = src[0]-'0';
	int g = src[1]-'0';       
	int b = src[2]-'0';       
	if (rd>5) rd = 5;
	if (g>5) g = 5;       
	if (b>5) b = 5; 
	needed.fgcol = 16 + (rd * 36 + g * 6 + b);
	needed.bold = isupper(*src);
	return 1;
      } else {
	code = of[4];
      }
    }
    code = lookup_col(code, scheme);
  } else 
    code = lookup_col(code, scheme);

  char * wh = strchr(bcols, tolower(code));
  if (wh) {
    if (!tripping) {
      needed.fgcol = wh - bcols;
      needed.bold = isupper(code);
    }
    return 1;
  }
  return 0;
}

/* This needs rewriting to be two different tables

   1 - mud code -> UCS lookup

   2 - UCS fallbacks for various values */

//!
struct ucs_entry {
  char code;
  const char *unistr;
  const char *latin1;
  const char *ascii;
  char vt100;
  int uni;
} whats[] = {
  { '^', "^","^", "^",   0,    0      }, /* CIRCUMFLEX ACCENT */
  { '+',  0, "+", "+",   0x6e, 0x253c }, /* FORMS HORIZONTAL AND VERTICAL */
  { '|',  0, "|", "|",   0x78, 0x2502 }, /* FORMS VERTICAL */
  { '-',  0, "-", "-",   0x71, 0x2500 }, /* FORMS HORIZONTAL */
  { '/',  0, "|", "|",   0x6c, 0x250c }, /* FORMS DOWN AND RIGHT (256d) */
  { '\\', 0, "|", "|",   0x6b, 0x2510 }, /* FORMS DOWN AND LEFT 256e */
  { '[',  0, "|", "|",   0x6d, 0x2514 }, /* FORMS UP AND RIGHT 2570 */
  { ']',  0, "|", "|",   0x6a, 0x2518 }, /* FORMS UP AND LEFT 256f */
  { '>',  0, "|", "|",   0x74, 0x251c }, /* FORMS VERTICAL AND RIGHT */
  { '<',  0, "|", "|",   0x75, 0x2524 }, /* FORMS VERTICAL AND LEFT */
  { '!',  0, "-", "-",   0x77, 0x252c }, /* FORMS DOWN AND HORIZONTAL */
  { '~',  0, "-", "-",   0x76, 0x2534 }, /* FORMS UP AND HORIZONTAL */
  { 'O',  0, "M", "M",   0,    0x2642 }, /* MALE SIGN */
  { 'F',  0, "F", "F",   0,    0x2640 }, /* FEMALE SIGN */
  { '=',  0, "=", "=",   0,    0x2550 }, /* FORMS DOUBLE HORIZONTAL */

  {  0,   0, 0  , 0,     96 ,  0x25c6},  /* DIAMOND */
  {  0,   0, 0  , 0,     'a',  0x2592},  /* CHECKERBOARD */
  {  0,   0, 0  , "HT",     'b',  0x2409},  /* HT */
  {  0,   0, 0  , "FF",     'c',  0x240c},  /* FF */
  {  0,   0, 0  , "CR",     'd',  0x240d},  /* CR */
  {  0,   0, 0  , "LF",     'e',  0x240a},  /* LF */
  {  0,   0, 0  , "NL",     'h',  0x2424},  /* NL */
  {  0,   0, 0  , "VT",     'i',  0x240b},  /* VT */
  {  0,   0, 0  , "<=",     'y',  0x2264},  /* <= */
  {  0,   0, 0  , ">=",     'z',  0x2265},  /* >= */
//  {  0,   0, 0  , 0,     '{',  0x03c0},  /* PI */
  {  0,   0, 0  , "!=",     '|',  0x2260},  /* != */
  {  0,   0, 0  , "O",   0,   0x25cf},   /* FILLED CIRCLE */

  { 0, 0, 0, "S", 0, 0x2660}, /* SPADE */
  { 0, 0, 0, "H", 0, 0x2665}, /* HEART */
  { 0, 0, 0, "C", 0, 0x2663}, /* CLUB */
  { 0, 0, 0, "D", 0, 0x2666}, /* DIAMOND */

  { 0, 0, 0, "o/~", 0, 9834}, /* NOTE */

  { 0, 0, 0, "E", 0, 0x20ac},


  {0, 0, 0, " ", 0, 0x00a0},  
  {0, 0, 0, "!", 0, 0x00a1},  
  {0, 0, 0, "c", 0, 0x00a2},  
  {0, 0, 0, "L", '}', 0x00a3},  
  {0, 0, 0, "#", 0, 0x00a4},  
  {0, 0, 0, "Y", 0, 0x00a5},  
  {0, 0, 0, "|", 0, 0x00a6},  
  {0, 0, 0, "S", 0, 0x00a7},  
  {0, 0, 0, "\"", 0, 0x00a8},  
  {0, 0, 0, "(c)", 0, 0x00a9},  
  {0, 0, 0, "a", 0, 0x00aa},  
  {0, 0, 0, "<<", 0, 0x00ab},  
  {0, 0, 0, "!", 0, 0x00ac},  
  {0, 0, 0, "-", 0, 0x00ad},  
  {0, 0, 0, "(r)", 0, 0x00ae},  
  {0, 0, 0, "~", 0, 0x00af},  
  {0, 0, 0, "o", 'f', 0x00b0},  
  {0, 0, 0, "+-", 'g', 0x00b1},  
  {0, 0, 0, "", 0, 0x00b2},  
  {0, 0, 0, "~", 0, 0x00b3},  
  {0, 0, 0, "'", 0, 0x00b4},  
  {0, 0, 0, "u", 0, 0x00b5},  
  {0, 0, 0, "P", 0, 0x00b6},  
  {0, 0, 0, ".", 0, 0x00b7},  
  {0, 0, 0, ",", 0, 0x00b8},  
  {0, 0, 0, "-", 0, 0x00b9},  
  {0, 0, 0, "o", 0, 0x00ba},  
  {0, 0, 0, ">>", 0, 0x00bb},  
  {0, 0, 0, "1/4", 0, 0x00bc},  
  {0, 0, 0, "1/2", 0, 0x00bd},  
  {0, 0, 0, "3/4", 0, 0x00be},  
  {0, 0, 0, "?", 0, 0x00bf},  
  {0, 0, 0, "A", 0, 0x00c0},  
  {0, 0, 0, "A", 0, 0x00c1},  
  {0, 0, 0, "A", 0, 0x00c2},  
  {0, 0, 0, "A", 0, 0x00c3},  
  {0, 0, 0, "A", 0, 0x00c4},  
  {0, 0, 0, "A", 0, 0x00c5},  
  {0, 0, 0, "AE", 0, 0x00c6},  
  {0, 0, 0, "C", 0, 0x00c7},  
  {0, 0, 0, "E", 0, 0x00c8},  
  {0, 0, 0, "E", 0, 0x00c9},  
  {0, 0, 0, "E", 0, 0x00ca},  
  {0, 0, 0, "E", 0, 0x00cb},  
  {0, 0, 0, "I", 0, 0x00cc},  
  {0, 0, 0, "I", 0, 0x00cd},  
  {0, 0, 0, "I", 0, 0x00ce},  
  {0, 0, 0, "I", 0, 0x00cf},  
  {0, 0, 0, "Dh", 0, 0x00d0},  
  {0, 0, 0, "N", 0, 0x00d1},  
  {0, 0, 0, "O", 0, 0x00d2},  
  {0, 0, 0, "O", 0, 0x00d3},  
  {0, 0, 0, "O", 0, 0x00d4},  
  {0, 0, 0, "O", 0, 0x00d5},  
  {0, 0, 0, "O", 0, 0x00d6},  
  {0, 0, 0, "x", 0, 0x00d7},  
  {0, 0, 0, "O", 0, 0x00d8},  
  {0, 0, 0, "U", 0, 0x00d9},  
  {0, 0, 0, "U", 0, 0x00da},  
  {0, 0, 0, "U", 0, 0x00db},  
  {0, 0, 0, "U", 0, 0x00dc},  
  {0, 0, 0, "Y", 0, 0x00dd},  
  {0, 0, 0, "Th", 0, 0x00de},  
  {0, 0, 0, "ss", 0, 0x00df},  
  {0, 0, 0, "a", 0, 0x00e0},  
  {0, 0, 0, "a", 0, 0x00e1},  
  {0, 0, 0, "a", 0, 0x00e2},  
  {0, 0, 0, "a", 0, 0x00e3},  
  {0, 0, 0, "a", 0, 0x00e4},  
  {0, 0, 0, "a", 0, 0x00e5},  
  {0, 0, 0, "ae", 0, 0x00e6},  
  {0, 0, 0, "c", 0, 0x00e7},  
  {0, 0, 0, "e", 0, 0x00e8},  
  {0, 0, 0, "e", 0, 0x00e9},  
  {0, 0, 0, "e", 0, 0x00ea},  
  {0, 0, 0, "e", 0, 0x00eb},  
  {0, 0, 0, "i", 0, 0x00ec},  
  {0, 0, 0, "i", 0, 0x00ed},  
  {0, 0, 0, "i", 0, 0x00ee},  
  {0, 0, 0, "i", 0, 0x00ef},  
  {0, 0, 0, "dh", 0, 0x00f0},  
  {0, 0, 0, "n", 0, 0x00f1},  
  {0, 0, 0, "o", 0, 0x00f2},  
  {0, 0, 0, "o", 0, 0x00f3},  
  {0, 0, 0, "o", 0, 0x00f4},  
  {0, 0, 0, "o", 0, 0x00f5},  
  {0, 0, 0, "o", 0, 0x00f6},  
  {0, 0, 0, "/", 0, 0x00f7},  
  {0, 0, 0, "o", 0, 0x00f8},  
  {0, 0, 0, "u", 0, 0x00f9},  
  {0, 0, 0, "u", 0, 0x00fa},  
  {0, 0, 0, "u", 0, 0x00fb},  
  {0, 0, 0, "u", 0, 0x00fc},  
  {0, 0, 0, "y", 0, 0x00fd},  
  {0, 0, 0, "th", 0, 0x00fe},  
  {0, 0, 0, "y", 0, 0x00ff},  

#define CYR(a, b) { 0, 0, 0, a, 0, b }

  CYR("A",  0x104),
  CYR("a",  0x105),

  CYR("C",  0x106),
  CYR("c",  0x107),

  CYR("E",  0x118),
  CYR("e",  0x119),

  CYR("L",  0x141),
  CYR("l",  0x142),

  CYR("E",  0x403),
  CYR("I",  0x406),
  CYR("A",  0x410),
  CYR("B",  0x411),
  CYR("V",  0x412),
  CYR("G",  0x413),
  CYR("D",  0x414),
  CYR("E",  0x415),
  CYR("Zh", 0x416),
  CYR("Z",  0x417),
  CYR("I",  0x418),
  CYR("J",  0x419),
  CYR("K",  0x41a),
  CYR("L",  0x41b),
  CYR("M",  0x41c),
  CYR("N",  0x41d),
  CYR("O",  0x41e),
  CYR("P",  0x41f),
  CYR("R",  0x420),
  CYR("S",  0x421),
  CYR("T",  0x422),
  CYR("U",  0x423),
  CYR("F",  0x424),
  CYR("H",  0x425),
  CYR("C",  0x426),
  CYR("Ch", 0x427),
  CYR("Sh", 0x428),
  CYR("Shch", 0x429),
  CYR("''", 0x42a),
  CYR("Y", 0x42b),
  CYR("'", 0x42c),
  CYR("E", 0x42d),
  CYR("U", 0x42e),
  CYR("A", 0x42f),

  CYR("E",  0x453),
  CYR("I",  0x456),
  CYR("a",  0x430),
  CYR("b",  0x431),
  CYR("v",  0x432),
  CYR("g",  0x433),
  CYR("d",  0x434),
  CYR("e",  0x435),
  CYR("zh", 0x436),
  CYR("z",  0x437),
  CYR("i",  0x438),
  CYR("j",  0x439),
  CYR("k",  0x43a),
  CYR("l",  0x43b),
  CYR("m",  0x43c),
  CYR("n",  0x43d),
  CYR("o",  0x43e),
  CYR("p",  0x43f),
  CYR("r",  0x440),
  CYR("s",  0x441),
  CYR("t",  0x442),
  CYR("u",  0x443),
  CYR("f",  0x444),
  CYR("h",  0x445),
  CYR("c",  0x446),
  CYR("ch", 0x447),
  CYR("sh", 0x448),
  CYR("shch", 0x449),
  CYR("''", 0x44a),
  CYR("y", 0x44b),
  CYR("'", 0x44c),
  CYR("e", 0x44d),
  CYR("u", 0x44e),
  CYR("a", 0x44f),

  CYR("a", 0x3b1),
  CYR("b", 0x3b2),
  CYR("g", 0x3b3),
  CYR("d", 0x3b4),
  CYR("e", 0x3b5),
  CYR("z", 0x3b6),
  CYR("h", 0x3b7),
  CYR("th", 0x3b8),
  CYR("i", 0x3b9),
  CYR("k", 0x3ba),
  CYR("l", 0x3bb),
  CYR("m", 0x3bc),
  CYR("n", 0x3bd),
  CYR("x", 0x3be),
  CYR("o", 0x3bf),
  CYR("p", 0x3c0),
  CYR("r", 0x3c1),
  CYR("s", 0x3c2),
  CYR("s", 0x3c3),
  CYR("t", 0x3c4),
  CYR("u", 0x3c5),
  CYR("ph", 0x3c6),
  CYR("ch", 0x3c7),
  CYR("ps", 0x3c8),
  CYR("o", 0x3c9),

  CYR("A", 0x391),
  CYR("B", 0x392),
  CYR("G", 0x393),
  CYR("D", 0x394),
  CYR("E", 0x395),
  CYR("Z", 0x396),
  CYR("H", 0x397),
  CYR("Th", 0x398),
  CYR("I", 0x399),
  CYR("K", 0x39a),
  CYR("L", 0x39b),
  CYR("M", 0x39c),
  CYR("N", 0x39d),
  CYR("X", 0x39e),
  CYR("O", 0x39f),
  CYR("P", 0x3a0),
  CYR("R", 0x3a1),
  CYR("S", 0x3a2),
  CYR("S", 0x3a3),
  CYR("T", 0x3a4),
  CYR("U", 0x3a5),
  CYR("Ph", 0x3a6),
  CYR("Ch", 0x3a7),
  CYR("Ps", 0x3a8),
  CYR("O", 0x3a9),

  CYR( "f",  0x16a0), 
  CYR( "u",  0x16a2), 
  CYR( "th", 0x16a6), 
  CYR( "o",  0x16a9), 
  CYR( "r",  0x16b1), 
  CYR( "k",  0x16b3), 
  CYR( "g",  0x16b7), 
  CYR( "w",  0x16b9), 
  CYR( "h",  0x16bb), 
  CYR( "n",  0x16be), 
  CYR( "i",  0x16c1), 
  CYR( "g",  0x16c4), 
  CYR( "eo", 0x16c7), 
  CYR( "p",  0x16c8), 
  CYR( "eo", 0x16c9), 
  CYR( "s",  0x16cb), 
  CYR( "t",  0x16cf), 
  CYR( "b",  0x16d2), 
  CYR( "e",  0x16d6), 
  CYR( "m",  0x16d7), 
  CYR( "l",  0x16da), 
  CYR( "i",  0x16dd), 
  CYR( "e",  0x16df), 
  CYR( "d",  0x16de), 
  CYR( "a",  0x16aa), 
  CYR( "ae", 0x16ab), 
  CYR( "y",  0x16a3),
  CYR( "y",  0x16e6), 
  CYR( "ea", 0x16e0), 
  CYR( " ",  0x16eb),
  CYR( ".",  0x16ec),
  CYR( "c",  0x16e3),

CYR ( " ", 0x2800 ),
CYR ( "a", 0x2801 ), 
CYR ( "b", 0x2803 ), 
CYR ( "c", 0x2809 ), 
CYR ( "d", 0x2819 ),
CYR ( "e", 0x2811 ), 
CYR ( "f", 0x280b ), 
CYR ( "g", 0x281b ), 
CYR ( "h", 0x2813 ), 
CYR ( "i", 0x280a ), 
CYR ( "j", 0x281a ), 
CYR ( "k", 0x2805 ),
CYR ( "l", 0x2807), 
CYR ( "m", 0x280d ), 
CYR ( "n", 0x281d ), 
CYR ( "o", 0x2815 ), 
CYR ( "p", 0x280f ), 
CYR ( "q", 0x281f ), 
CYR ( "r", 0x2817 ), 
CYR ( "s", 0x280e ), 
CYR ( "t", 0x281e ), 
CYR ( "u", 0x2825 ), 
CYR ( "v", 0x2827 ), 
CYR ( "w", 0x283a ), 
CYR ( "x", 0x282d ), 
CYR ( "y", 0x283d ), 
CYR ( "z", 0x2835 ), 
CYR ( "ch", 0x2821 ), 
CYR ( "gh", 0x2823 ), 
CYR ( "sh", 0x2829 ), 
CYR ( "th", 0x2839 ), 
CYR ( "wh", 0x2831 ), 
CYR ( "ed", 0x282b ), 
CYR ( "er", 0x283b ), 
CYR ( "ou", 0x2833 ), 
CYR ( "ow", 0x282a ), 
CYR ( "en", 0x2828 ), 

  { '`',  0, "'", "'",   0,    0x2018 }, /* LEFT SINGLE QUATOATION MARK */
  { '\'', 0, "'", "'",   0,    0x2019 }, /* RIGHT SINGLE QUOTATION MARK */

  { '%',  0, "\"", "\"", 0,    0x201d }, /* RIGHT DOUBLE QUOTATION MARK */
  { '\"', 0, "\"", "\"", 0,    0x201c }, /* LEFT DOUBLE QUOTATION MARK */

  {0, 0, 0, "<", 0, 0x2190},
  {0, 0, 0, "^", 0, 0x2191},
  {0, 0, 0, ">", 0, 0x2192},
  {0, 0, 0, "v", 0, 0x2193},

  {0, 0, 0, " ", 0, 0x25ef},
};

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))

//! comparator function
static int cmp(struct ucs_entry *a, struct ucs_entry *b) {
  if (a->uni > b->uni)
    return 1;
  if (a->uni == b->uni)
    return 0;
  return -1;
}

//! print out all the graphics characters known
static bool verb_graph(MudObject *who, int argc, const char **argv) {
  Divert d(who, "graph");

  qsort(&whats, ARRAY_SIZE(whats), sizeof(struct ucs_entry), (int(*)(const void *, const void *))cmp);
  int j = 0;
  for (size_t i=0;i<ARRAY_SIZE(whats);i++) {
    if (whats[i].uni && whats[i].code) {
      string s = "^";
      s += whats[i].code;
      who->printf("%04X %3s ^^%c       ", whats[i].uni, s.c_str(), whats[i].code);
      j++;
    } else if (whats[i].uni) {
      string s = ssprintf("^#%i;", whats[i].uni);
      who->printf("%04X %3s ^^#x%04x; ", whats[i].uni, s.c_str(), whats[i].uni);
      j++;
    } else
      continue;
    if ((j & 3)==0) {
      who->printf("\n");
    }
  }
  who->printf("\n");
  return true;
}

//! convert UCS to UTF-8
static string unitoutf8(int i)
{
  if (i < 0x80) {
    char b2[2] = {i, 0 };
    return b2;
  }
  if (i >= 0x80 && i <= 0x800) {
    char b2[3];
    b2[0] = 0xc0 | (i >> 6);
    b2[1] = 0x80 | (i & 0x3f);
    b2[2] = 0;
    return b2;
  }
  if (i < 0x10000) {
    char b2[4];
    b2[0] = 0xe0 | (i >> 12);
    b2[1] = 0x80 | ((i >> 6) & 0x3f);
    b2[2] = 0x80 | ((i >> 0) & 0x3f);
    b2[3] = 0;
    return b2;
  }
  if (i < 0x00200000) {
    char b2[5];
    b2[0] = 0xe0 | (i >> 18);
    b2[1] = 0x80 | ((i >> 12) & 0x3f);
    b2[2] = 0x80 | ((i >> 6) & 0x3f);
    b2[3] = 0x80 | ((i >> 0) & 0x3f);
    b2[4] = 0;
    return b2;
  }
  return "?";
}

//! obtain the best version of /ch/ we can given the given settings
static string downgrade(int ch, int flags, charset cset, int *dec=0)
{
  if (ch >= 0x20 && ch <= 0x7e)
    {
      /* characters in US-ASCII range converted directly */
      string s;
      s = ch;
      return s;
    }

  if (cset == CHAR_LATIN1) {
    /* Latin1 ranged passed directly through if in latin1 */
    if (ch >= 0x80 && ch <= 0xff) {
      string s;
      s += ch;
      return s;
    }
  }

  if (cset == CHAR_ASCII) {
    /* first lets try converting through dec */
    if (flags & DEC && dec) {
      for (size_t i=0;i<ARRAY_SIZE(whats);i++) {
	if (whats[i].uni == ch && whats[i].vt100) {
	  *dec = 1;
	  string s;
	  s = whats[i].vt100;
	  return s;
	}
      }
    }
  }

  if (cset == CHAR_UNICODE) {
    /* If in JustLength, keep strlen()==wcswidth()) */
    if (flags & JUSTLENGTH) {
      switch (mk_wcwidth(ch)) {
      case 0: return "";
      case 1: return "?";
      case 2: return "??";
      default: return "?";
      }
    }
    /* otherwise return it in UTF-8 */
    return unitoutf8(ch);
  }

  if (has_table(cset)) {
    /* If we have a table, make use of the mapping */
    string b;
    b += uni2cp(ch, cset);
    return b;
  }

  /* we may be able to find this character in the dec table */
  if (flags & DEC && dec) {
    for (size_t i=0;i<ARRAY_SIZE(whats);i++) {
      if (whats[i].uni == ch && whats[i].vt100) {
	*dec = 1;
	string s;
	s = whats[i].vt100;
	return s;
      }
    }
  }

  for (size_t i=0;i<ARRAY_SIZE(whats);i++) {
    if (whats[i].uni == ch) {
      if (whats[i].latin1 && has_table(cset)) {
	string b;
	b += uni2cp(ch, cset);
	return b;
      }
      if (whats[i].ascii) {
	string s;
	s = whats[i].ascii;
	return s;
      }
    }
  }

  /* Give up */
  switch (mk_wcwidth(ch)) {
  case 0: return "";
  case 1: return "?";
  case 2: return "??";
  default: return "?";
  }
}

//! the next colour to use for tripping
static int next_tripcol(int a)
{
  if (a==1) return 3; // red -> yellow
  if (a==3) return 2; // yellow -> green
  if (a==2) return 6; // green -> cyan
  if (a==6) return 4; // cyan -> blue
  if (a==4) return 5; // blue -> magenta
  if (a==5) return 1; // magenta -> red

  return 1;
}

//! the next colour to use of tripping in 256-colour mode
static int next_tripcol256(int a)
{
  static unsigned char tcols[] = {
    226, 220, 214, 208, 202, 196, 197, 198, 
    199, 200, 201, 165, 129,  93,  57,  21,
    27,   33,  39,  45,  51,  50,  49,  48,
    47,   46,  82, 118, 154, 190, 226,   0
  };
  
  if (a==0) 
    return tcols[0];

  int i = 0;
  while (tcols[i]) {
    if (tcols[i]==a)
      return tcols[i+1];
    i++;
  }
  
  return tcols[0];
}

//! the colour parser!
static string colour_parse(const char *src, colstate_t &current, 
			  colstate_t &tempblank, int flags, charset cset,
			  const Player *who=NULL, const char *scheme=NULL)
{
  colstate_t needed = current;
  string buf;
  int tripmode = 1;

  int tripping = who?who->get_int("$trip")>1:0;

  int in_tag = 0;
  string fn = "";
  
  int cchanged = 0;
    
  while (*src) {

    string actual;
    
    needed.dec = 0;

    if (src[0]=='^') {
      int lock = 0;
      src++;
      if (!src[0]) {
	continue;
      }
      if (*src=='^') {
	goto end;
      }
      if (*src=='n') {
	if (!tripping)
	  needed = tempblank;
	src++;
	continue;
      }
      if (*src=='}') {
	if (!tripping)
	  needed = tempblank = blank;
	src++;
	continue;
      }
      if (!*src) {
	continue;
      }
      if (*src=='{') {
	lock = 1;
	src++;
      }
      if (!setfgcol(needed, *src, who, flags, scheme, tripping)) {
	if (isdigit(src[0]) && isdigit(src[1]) && isdigit(src[2]))
	  if (flags & HAS_256COLS) {
	    int rd = src[0]-'0';       
	    int g = src[1]-'0';       
	    int b = src[2]-'0';       
	    if (rd>5) rd = 5;
	    if (g>5) g = 5;       
	    if (b>5) b = 5; 
	    src += 3;
	    if (!tripping) {
	      needed.fgcol = 16 + (rd * 36 + g * 6 + b);
	      needed.bold = isupper(*src);
	    }
	    cchanged = 1;
	  } else {
	    src += 3;
	    char *wh = strchr(bcols, tolower(*src));
	    if (!tripping) {
	      needed.fgcol = wh - bcols;
	      needed.bold = isupper(*src);
	    }
	    cchanged = 1;
	  }
      } else {
	cchanged = 1;
      }

      if (lock)
	tempblank = needed;
      
      if (*src=='#') {
	const char *anchor = src+1;
	if (!*anchor) {
	  continue;
	}
	src++;
	while ((*src=='x' || isxdigit(*src)))
	  src++;

	if (!*src) {
	  continue;
	}
	
	if (*src==';') {
	  src++;
	  
	  int i = atoi(anchor);
	  if (*anchor=='x') {
	    i = strtol(anchor+1, 0, 16);
	  }
	    
	  if (i >= 0x20 && i <= 0x7f) {
	    char tmp[2];
	    sprintf(tmp, "%c", i);
	    actual = tmp;
	  } else {
	    actual = downgrade(i, flags, cset, &needed.dec);
	  }
	  src--;
	  goto end;
	}
      }
	
      if (!*src)
	continue;

      for (size_t i=0;i<ARRAY_SIZE(whats);i++) {
	if (whats[i].code == *src) {
	  if (cset == CHAR_UNICODE) {
	    actual = downgrade(whats[i].uni, flags, cset);
	    /* first use the actual unicode value (in UTF-8) */
	  }
	  else if (flags & DEC && whats[i].vt100) {
	  /* if we can do dec, and it is a dec character, then use that */
	    needed.dec = 1;
	    actual = whats[i].vt100;
	  } else if (cset == CHAR_ASCII)
	    /* if our charset is ASCII, just use the ascii value */
	    actual = whats[i].ascii;
	  else if (has_table(cset)) {
	    /* if we have a table, try converting to unicode value to it */
	    actual = downgrade(whats[i].uni, flags, cset);
	    if (actual == "?" || actual == "??")
	      /* if that didn't work, try converting the latin1 value to it */
	      actual = downgrade((unsigned char)(*whats[i].latin1), flags, cset);
	  } else
	    /* just use the latin1 value */
	    actual = downgrade((unsigned char)(*whats[i].latin1), flags, cset);
	  src--;
	  break;
	}
      }
      
      src++;
      if (!actual.length())
	continue;
      else
	goto end;
    }

    if (src[0]=='&') {
      src++;
      if (!src[0]) {
	continue;
      }
      if (src[0]=='/') {
	needed.ital = !needed.ital;
	src++;
	continue;
      }
      if (src[0]=='_') {
	needed.undl = !needed.undl;
	src++;
	continue;
      }
      if (src[0]=='|') {
	needed.sout = !needed.sout;
	src++;
	continue;
      }
      if (src[0]=='-') {
	src++;
	if (isdigit(src[0]) && isdigit(src[1]) && isdigit(src[2]) && src[3]) {
	  int rd = src[0]-'0' <? 5;
	  int gr = src[1]-'0' <? 5;
	  int bl = src[2]-'0' <? 5;

	  if (!tripping) {
	    if (flags & HAS_256COLS)
	      needed.bgcol = 16 + (rd * 36 + gr * 6 + bl);
	    else
	      if (char *wh = strchr(bcols, tolower(src[3])))
		needed.bgcol = wh - bcols;
	  }

	  src += 4;
	  continue;
	} else {
	  char *wh = strchr(bcols, tolower(*src));
	  if (wh && !tripping) {
	    needed.bgcol = wh - bcols;
	  }
	  src++;
	  continue;
	}
      }
      if (src[0]=='=') {
	src++;
	if (!src[0]) {
	  continue;
	}
	char *wh = strchr(bcols, tolower(*src));
	if (wh && !tripping) {
	  needed.fgcol = wh - bcols;
	  needed.bold = tolower(*src);
	}
	src++;
	if (!src[0]) {
	  continue;
	}
	wh = strchr(bcols, tolower(*src));
	if (wh && !tripping) {
	  needed.bgcol = wh - bcols;
	}
	src++;
	if (!src[0]) {
	  continue;
	}
	continue;
      }
      if (src[0] == '&')
	goto end;
      src--;
    }

    if (*src=='\n' || *src=='\v') {
      if (*src=='\n')
	if (!tripping)
	  needed = blank;

      if (flags & CRLF)
	actual = "\r\n";
    }

    if (*src & 0x80) {
      actual = downgrade((unsigned char)*src, flags, cset);
    }

    if (*src == '\033') {
      tripmode = 0;
    }

  end:
    if (!in_tag && tripmode && tripping && (cchanged || random_number(2)==0)) {
      if (flags & HAS_256COLS) {
	needed.fgcol = next_tripcol256(needed.fgcol);
      } else {
	needed.fgcol = next_tripcol(needed.fgcol);
	if (needed.fgcol>=8) 
	  needed.fgcol = 1;
      }
      needed.bold = 1;
      cchanged = 0;
    }

    if (*src == '\2') {
      tripmode = 1;
      src++;
      continue;
    }

    string x;
    
    if (actual.length())
      x = actual;
    else
      x = *src;

    int antidec = 0;
    const char *s = x.c_str();
    while (*s) {
      if (*s != '\r' || *s != '\n') {
	// It's ok to send \r and \n in DEC mode
	antidec = 1;
	break;
      }
      s++;
    }

    if (!antidec)
      needed.dec = current.dec;

    if (!in_tag) {
      if (!(flags & NO_ANSI)) {
	if (who && who->p && who->p->mxp)
	  if (x[0]!=3)
	    buf += make_mxpdifference(current, needed);
	  else
	    // forcibly reset MXP codes before a <>
	    buf += make_mxpdifference(current, blank);
	else
	  buf += make_difference(current, needed);
      }
      else if (!(flags & JUSTLENGTH))
	buf += make_decdifference(current, needed);
    }

    if (who && who->p && who->p->mxp) {
      const char *xs = x.c_str();
      while (*xs) {
	char c = *xs;
	xs++;

	switch (c) {
	case 3: buf += "<"; in_tag++; break;
	case 4: buf += ">"; in_tag--; break;
	case '\n': buf += who->p->secure_locked?"\n":"\n\033[1z"; break;
	default:
	  if (in_tag < 1)
	    switch (c) {
	    case '&': buf += "&amp;"; break;
	    case '<': buf += "&lt;"; break;
	    case '>': buf += "&gt;"; break;
	   default: buf += c;
	    }
	  else
	    buf += c;
	}
      }
    } else {
      const char *xs = x.c_str();
      while (*xs) {
	if (*xs=='\3')
	  in_tag++;
	if (!in_tag)
	  buf += *xs;
	if (*xs=='\4')
	  in_tag--;
	xs++;
      }
    }

    src++;
  }
  if (!(flags & NO_ANSI))
    buf += make_difference(current, needed);
  return buf;
}

//! wrapper round the parser
static string msi_colour_parse(colstate_t *colstate, 
		       const char *what, colourinfo_t cu) {
  colstate_t defcolstate = blank;
  colstate_t tblank = blank;

  int flags = CRLF;

  if (!cu.colour) {
    flags |= NO_ANSI;
  }

  if (cu.dec==2) /* lines on */
    flags |= DEC;

  if (cu.dec==1) /* lines auto */ {
    if (streq(cu.term, "xterm") ||
	streq(cu.term, "screen") ||
	streq(cu.term, "vt100") ||
	streq(cu.term, "vt102") ||
	streq(cu.term, "vt320") ||
	streq(cu.term, "cryotel") || 
	streq(cu.term, "dtterm") ||
	streq(cu.term, "ucryotel"))
    
    flags |= DEC;
  }

  if (cu.xtermcols==256)
    flags |= HAS_256COLS;

  if (cu.who && cu.who->p && cu.who->p->mxp)
    flags |= HAS_256COLS;

  if (cu.cset==CHAR_FAKEUNI) {
    cu.cset = CHAR_UNICODE;
    flags |= JUSTLENGTH;
  }

  string res = "";
  res += colour_parse(what, colstate?*colstate:defcolstate, tblank, flags, 
		     cu.cset, cu.who, cu.scheme);
  if (!colstate) {
    res += colour_parse("^n", defcolstate, tblank, flags, cu.cset, cu.who, 
		       cu.scheme);
    /* is this ever called with p==0 and cu.colour == 1? */
  }
  return res;
}

//! wrapper round the parser to get the lenght
static size_t get_length(const char *a, int lenof, const colourinfo_t &ci) {
  colstate_t colstate = blank;
  colstate_t tblank = blank;
  string res;
  int flags = 0;

  if (ci.dec==2) /* lines on */
    flags |= DEC;

  if (ci.dec==1) /* lines auto */ {
    if (streq(ci.term, "xterm") ||
	streq(ci.term, "screen") ||
	streq(ci.term, "vt100") ||
	streq(ci.term, "vt102") ||
	streq(ci.term, "vt320") ||
	streq(ci.term, "cryotel") || 
	streq(ci.term, "dtterm") ||
	streq(ci.term, "ucryotel"))
    
    flags |= DEC;
  }

  if (lenof != -1) {
    string tmp = string(a, lenof);
    res = colour_parse(tmp.c_str(), 
		       colstate, tblank, flags|NO_ANSI|JUSTLENGTH, 
		      ci.cset);
  } else {
    res = colour_parse(a, colstate, tblank, flags|NO_ANSI|JUSTLENGTH, 
		      ci.cset);
  }
  return res.length();
}

//! custom colour
struct custom_colour {
  const char *n;
  const char *ui;
  const char *def;
  PFlag priv;
  int minlev;
} cols[] = {
  { "L", "darkgray",}, 
  { NULL, NULL },
  { "o", "object" },
  { "p", "player" },
  { "P", "mobile" },
  { "d", "direction" },
  { "D", "destination" },
  { NULL, NULL },
  { "s", "say" },
  { "t", "tell" },
  { "k", "whisper" },
  { NULL, NULL },
  { "i", "shout" },
  { "S", "chat", 0, PFL_NONE, 4} ,  
  { "col.com", "hero", "^B", PFL_NONE, LEV_COMMANDER },
  { "col.cap", "esteemed", "^m", PFL_NONE, LEV_CAPTAIN },
  { "col.adm", "revered", "^Y", PFL_NONE, LEV_ADMIRAL },
  { NULL, NULL },
  { "col.achat", "achat", "^c", PFL_ABERCHAT },
  { "col.acode", "acode", "^y", PFL_CODER },
  { "col.agod", "agod", "^R",   PFL_GOD },
};

//! show the custom colours of you or another
static bool verb_showcol(MudObject *who, int argc, const char **argv)
{
  MudObject *qui = who;
  if (argc>1  && who->get_priv(PFL_SEEINFO)) {
    qui = get_player(who, argv[1]);
    if (!qui) {
      who->printf("View colour scheme of who?\n");
      return true;
    }
  }

  who->printf("%s\n\n", title_for(qui==who?"Showcol":ssprintf("Showcol for %M", qui).c_str(), who).c_str());

  const char *def = qui->get("colours");
  if (!def)
    def = "classic";

  who->printf("  Colours based on the '%#s' scheme.\n\n", def);

  for (size_t i=0;i<ARRAY_SIZE(cols);i++) {
    const char *n = cols[i].n;

    if (!n) {
      who->printf("\n");
      continue;
    }

    if (privs_of(qui) < cols[i].minlev)
      continue;

    if (cols[i].priv != PFL_NONE && !qui->get_priv(cols[i].priv))
      continue;

    const char *s = qui->get(ssprintf("col.%s", n).c_str());
    if (!s) {
      s = qui->get(n);
    }

    if (strlen(n)>1) {
      if (s)
	who->printf("   %s%15s^n  ^W^%s^n", s, cols[i].ui, s);
      else
	who->printf("   %s%15s^n  ^%s", cols[i].def, cols[i].ui, cols[i].def);
    } else {
	lookup_t a = lookup_classic;
	if (!strcmp(def, "yellow")) a = lookup_yellow;
	if (!strcmp(def, "red")) a = lookup_red;
	if (!strcmp(def, "paper")) a = lookup_paper;
	char l = a(*n);
	
	if (s) {
	  who->printf("   %s%15s^n  ^W^%s^n", s, cols[i].ui, s?s:"");
	} else {
	  who->printf("   ^%c%15s^n  ^^%c", l, cols[i].ui, l);
      }
    }
    who->printf("  %s\n", cols[i].ui);
  }
  who->printf("\n");
  who->printf("%s\n", footer_for(who).c_str());
  return true;
}

//! set a custom colour
static bool verb_setcol(MudObject *who, int argc, const char **argv)
{
  if (argc < 2)
    return 1;
  for (size_t i=0;i<ARRAY_SIZE(cols);i++) {
    const char *n = cols[i].ui;
    char prop[100];

    if (!cols[i].n)
      continue;

    if (strlen(cols[i].n)==1) 
      sprintf(prop, "col.%c", *cols[i].n);
    else
      sprintf(prop, "%s", cols[i].n);

    if (n && streq(n, argv[1])) {
      if (argc < 3) {
	who->printf("Removing %s.\n", prop);
	who->unset(prop);
      } else {
	who->printf("Setting colour for %s to %s.\n", argv[1], argv[2]+1);
	who->set(prop, argv[2]);
      }
      return true;
    }
  }
  who->printf("'%s' isn't a customisable colour.\n", argv[1]);
  return true;
}

//! monochrome
static bool verb_mono(MudObject *p, int, const char **) {
  if (!p->get_flag(FL_COLOUR)) {
    p->printf("Colour already off.\n");
    return true;
  }
  p->printf("\033[0m\2Colour turned off.\n");
  p->set_bflag(FL_COLOUR, 0);
  return true;
}

//! set a colour scheme
static bool verb_colour(MudObject *p, int argc, const char **argv) {

  if (argc>1) {
    if (streq(argv[1], "mono") || streq(argv[1], "off")) {
      return verb_mono(p, 0, 0);
    }
    if (streq(argv[1], "paper")) {
      p->set_bflag(FL_COLOUR, 1);
      p->set("colours", "paper");
      p->printf("Colour scheme ^WPaper^n selected.\n");
      return true;
    }
    if (streq(argv[1], "yellow")) {
      p->set_bflag(FL_COLOUR, 1);
      p->set("colours", "yellow");
      p->printf("Colour scheme ^YYellow^n selected.\n");
      return true;
    }
    if (streq(argv[1], "red")) {
      p->set_bflag(FL_COLOUR, 1);
      p->set("colours", "red");
      p->printf("Colour scheme ^RRed^n selected.\n");
      return true;
    }
    if (streq(argv[1], "classic")) {
      p->set_bflag(FL_COLOUR, 1);
      p->set("colours", "classic");
      p->printf("Colour scheme ^rclassic^n selected.\n");
      return true;
    }
    p->printf("Known colour schemes : classic mono yellow paper.\n");
    return true;
  }

  if (p->get_flag(FL_COLOUR)) {
    p->set_bflag(FL_COLOUR, 1);
    p->printf("Known colour schemes : classic mono yellow paper.\n");
    return true;
  }
  else {
    p->set_bflag(FL_COLOUR, 1);
    p->printf("Welcome to the wonderful world of ^RC^YO^GL^CO^BU^MR^R!^n\n");
  }
  return true;
}

#define CLEANUP \
	msi_colourfilter = 0;\
	msi_stringlength = 0;

#include "verbmodule.h"

void startup() {
	msi_colourfilter = msi_colour_parse;
	msi_stringlength = get_length;

AUTO_VERB(showcol, 5, 0, PFL_NONE);
AUTO_VERB(setcol, 5, 0, PFL_NONE);
AUTO_VERB(graph, 5, 0, PFL_NONE);
AUTO_VERB(colour, 5, 0, PFL_NONE);
ADD_ALIAS(color, 4, colour);
AUTO_VERB(mono, 4, 0, PFL_NONE);

}