mux2.4/game/data/
mux2.4/src/tools/
// stringutil.cpp -- string utilities.
//
// $Id: stringutil.cpp,v 1.71 2005/08/05 17:11:07 sdennis Exp $
//
// MUX 2.4
// Copyright (C) 1998 through 2004 Solid Vertical Domains, Ltd. All
// rights not explicitly given are reserved.
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"

#include "ansi.h"
#include "pcre.h"

const bool mux_isprint[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 2
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 3
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 4
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 5
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 6
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,  // 7

    0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0,  // 8
    0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1,  // 9
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // A
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // B
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // C
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // D
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // E
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0   // F
};

const bool mux_isdigit[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  // 3
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 4
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 5
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 6
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
};

const bool mux_ishex[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  // 3
    0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 4
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 5
    0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 6
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
};

const bool mux_isalpha[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 3
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 4
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  // 5
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 6
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // B
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // C
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,  // D
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // E
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0   // F
};

const bool mux_isalnum[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  // 3
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 4
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  // 5
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 6
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // B
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // C
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,  // D
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // E
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0   // F
};

const bool mux_isupper[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 3
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 4
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  // 5
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 6
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // C
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,  // D
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
};

const bool mux_islower[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 3
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 4
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 5
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 6
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // B
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  // D
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // E
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0   // F
};

const bool mux_isspace[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 3
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 4
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 5
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 6
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
};

// The first character of an attribute name must be either alphabetic,
// '_', '#', '.', or '~'. It's handled by the following table.
//
// Characters thereafter may be letters, numbers, and characters from
// the set {'?!`/-_.@#$^&~=+<>()}. Lower-case letters are turned into
// uppercase before being used, but lower-case letters are valid input.
//
bool mux_AttrNameInitialSet[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 3
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 4
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  // 5
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 6
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,  // 7

    0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // B
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // C
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,  // D
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // E
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0   // F
};

bool mux_AttrNameSet[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1,  // 2
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1,  // 3
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 4
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,  // 5
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 6
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,  // 7

    0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // B
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // C
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,  // D
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // E
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0   // F
};

// Valid characters for an object name are all printable
// characters except those from the set {=&|}.
//
const bool mux_ObjectNameSet[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 2
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,  // 3
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 4
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 5
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 6
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,  // 7

    0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0,  // 8
    0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1,  // 9
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // A
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // B
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // C
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // D
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // E
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0   // F
};

// Valid characters for a player name are all alphanumeric plus
// {`$_-.,'} plus SPACE depending on configuration.
//
bool mux_PlayerNameSet[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,  // 2
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  // 3
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 4
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  // 5
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 6
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  // B
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // C
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,  // D
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // E
    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0   // F
};

// Characters which should be escaped for the secure()
// function: '%$\[](){},;'.
//
const bool mux_issecure[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,  // 3
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 4
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,  // 5
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 6
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,  // 7

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
};

// Characters which should be escaped for the escape()
// function: '%\[]{};,()^$'.
//
const bool mux_isescape[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,  // 3
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 4
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  // 5
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 6
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,  // 7

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
};

const bool ANSI_TokenTerminatorTable[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 3
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 4
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  // 5
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 6
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
};

const unsigned char mux_hex2dec[256] =
{
//  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
//
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 0
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 2
    0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  0,  0,  0,  0,  0,  // 3
    0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 4
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 5
    0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 6
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 7

    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 8
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // A
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // B
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // C
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // D
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // E
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0   // F
};

const unsigned char mux_toupper[256] =
{
//   0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F
//
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 1
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 2
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 3
    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, // 4
    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // 5
    0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, // 6
    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, // 7

    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, // 8
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x8A, 0x9B, 0x8C, 0x9D, 0x8E, 0x9F, // 9
    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, // A
    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, // B
    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, // C
    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, // D
    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, // E
    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xF7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xFF  // F
};

const unsigned char mux_tolower[256] =
{
//   0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F
//
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 1
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 2
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 3
    0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 4
    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // 5
    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 6
    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, // 7

    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x9A, 0x8B, 0x9C, 0x8D, 0x9E, 0x8F, // 8
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0xFF, // 9
    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, // A
    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, // B
    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, // C
    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xD7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, // D
    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, // E
    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF  // F
};

const unsigned char mux_StripAccents[256] =
{
//   0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F
//
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 1
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 2
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 3
    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, // 4
    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // 5
    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 6
    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, // 7

    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, // 8
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, // 9
    0xA0, 0x21, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0x22, 0xAC, 0xAD, 0xAE, 0xAF, // A
    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0x22, 0xBC, 0xBD, 0xBE, 0x3F, // B
    0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0xC6, 0x43, 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, // C
    0x44, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0xD7, 0x4F, 0x55, 0x55, 0x55, 0x55, 0x59, 0x50, 0x42, // D
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xE6, 0x63, 0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69, // E
    0x6F, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0xF7, 0x6F, 0x75, 0x75, 0x75, 0x75, 0x79, 0x70, 0x79, // F
};

// ANSI_lex - This function parses a string and returns two token types.
// The type identifies the token type of length nLengthToken0. nLengthToken1
// may also be present and is a token of the -other- type.
//
int ANSI_lex(int nString, const char *pString, int *nLengthToken0, int *nLengthToken1)
{
    *nLengthToken0 = 0;
    *nLengthToken1 = 0;

    const char *p = pString;

    for (;;)
    {
        // Look for an ESC_CHAR
        //
        p = strchr(p, ESC_CHAR);
        if (!p)
        {
            // This is the most common case by far.
            //
            *nLengthToken0 = nString;
            return TOKEN_TEXT_ANSI;
        }

        // We have an ESC_CHAR. Let's look at the next character.
        //
        if (p[1] != '[')
        {
            // Could be a '\0' or another non-'[' character.
            // Move the pointer to position ourselves over it.
            // And continue looking for an ESC_CHAR.
            //
            p = p + 1;
            continue;
        }

        // We found the beginning of an ANSI sequence.
        // Find the terminating character.
        //
        const char *q = p+2;
        while (ANSI_TokenTerminatorTable[(unsigned char)*q] == 0)
        {
            q++;
        }
        if (q[0] == '\0')
        {
            // There was no good terminator. Treat everything like text.
            // Also, we are at the end of the string, so just return.
            //
            *nLengthToken0 = q - pString;
            return TOKEN_TEXT_ANSI;
        }
        else
        {
            // We found an ANSI sequence.
            //
            if (p == pString)
            {
                // The ANSI sequence started it.
                //
                *nLengthToken0 = q - pString + 1;
                return TOKEN_ANSI;
            }
            else
            {
                // We have TEXT followed by an ANSI sequence.
                //
                *nLengthToken0 = p - pString;
                *nLengthToken1 = q - p + 1;
                return TOKEN_TEXT_ANSI;
            }
        }
    }
}

char *strip_ansi(const char *szString, size_t *pnString)
{
    static char Buffer[LBUF_SIZE];
    char *pBuffer = Buffer;

    const char *pString = szString;
    if (!pString)
    {
        if (pnString)
        {
            *pnString = 0;
        }
        *pBuffer = '\0';
        return Buffer;
    }
    size_t nString = strlen(szString);

    while (nString)
    {
        int nTokenLength0;
        int nTokenLength1;
        int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1);

        if (iType == TOKEN_TEXT_ANSI)
        {
            memcpy(pBuffer, pString, nTokenLength0);
            pBuffer += nTokenLength0;

            int nSkipLength = nTokenLength0 + nTokenLength1;
            nString -= nSkipLength;
            pString += nSkipLength;
        }
        else
        {
            // TOKEN_ANSI
            //
            nString -= nTokenLength0;
            pString += nTokenLength0;
        }
    }
    if (pnString)
    {
        *pnString = pBuffer - Buffer;
    }
    *pBuffer = '\0';
    return Buffer;
}

char *strip_accents(const char *szString, size_t *pnString)
{
    static char Buffer[LBUF_SIZE];
    char *pBuffer = Buffer;

    const char *pString = szString;
    if (pString)
    {
        while (*pString)
        {
            *pBuffer = mux_StripAccents(*pString);
            pBuffer++;
            pString++;
        }
    }
    if (pnString)
    {
        *pnString = pBuffer - Buffer;
    }
    *pBuffer = '\0';
    return Buffer;
}

#define ANSI_COLOR_INDEX_BLACK     0
#define ANSI_COLOR_INDEX_RED       1
#define ANSI_COLOR_INDEX_GREEN     2
#define ANSI_COLOR_INDEX_YELLOW    3
#define ANSI_COLOR_INDEX_BLUE      4
#define ANSI_COLOR_INDEX_MAGENTA   5
#define ANSI_COLOR_INDEX_CYAN      6
#define ANSI_COLOR_INDEX_WHITE     7
#define ANSI_COLOR_INDEX_DEFAULT   8

const ANSI_ColorState acsRestingStates[3] =
{
    {true,  false, false, false, false, ANSI_COLOR_INDEX_DEFAULT, ANSI_COLOR_INDEX_DEFAULT},
    {false, false, false, false, false, ANSI_COLOR_INDEX_WHITE,   ANSI_COLOR_INDEX_DEFAULT},
    {true,  false, false, false, false, ANSI_COLOR_INDEX_DEFAULT, ANSI_COLOR_INDEX_DEFAULT}
};

void ANSI_Parse_m(ANSI_ColorState *pacsCurrent, int nANSI, const char *pANSI,
                  bool *pbSawNormal)
{
    // If the last character isn't an 'm', then it's an ANSI sequence we
    // don't support, yet. TODO: There should be a ANSI_Parse() function
    // that calls into this one -only- if there's an 'm', but since 'm'
    // is the only command this game understands at the moment, it's easier
    // to put the test here.
    //
    if (pANSI[nANSI-1] != 'm')
    {
        return;
    }

    // Process entire string and update the current color state structure.
    //
    while (nANSI)
    {
        // Process the next attribute phrase (terminated by ';' or 'm'
        // typically).
        //
        const char *p = pANSI;
        while (mux_isdigit(*p))
        {
            p++;
        }
        size_t nLen = p - pANSI + 1;
        if (p[0] == 'm' || p[0] == ';')
        {
            // We have an attribute.
            //
            if (nLen == 2)
            {
                int iCode = pANSI[0] - '0';
                switch (iCode)
                {
                case 0:
                    // Normal.
                    //
                    *pacsCurrent = acsRestingStates[ANSI_ENDGOAL_NORMAL];
                    *pbSawNormal = true;
                    break;

                case 1:
                    // High Intensity.
                    //
                    pacsCurrent->bHighlite = true;
                    pacsCurrent->bNormal = false;
                    break;

                case 2:
                    // Low Intensity.
                    //
                    pacsCurrent->bHighlite = false;
                    pacsCurrent->bNormal = false;
                    break;

                case 4:
                    // Underline.
                    //
                    pacsCurrent->bUnder = true;
                    pacsCurrent->bNormal = false;
                    break;

                case 5:
                    // Blinking.
                    //
                    pacsCurrent->bBlink = true;
                    pacsCurrent->bNormal = false;
                    break;

                case 7:
                    // Reverse Video
                    //
                    pacsCurrent->bInverse = true;
                    pacsCurrent->bNormal = false;
                    break;
                }
            }
            else if (nLen == 3)
            {
                int iCode0 = pANSI[0] - '0';
                int iCode1 = pANSI[1] - '0';
                if (iCode0 == 3)
                {
                    // Foreground Color
                    //
                    if (iCode1 <= 7)
                    {
                        pacsCurrent->iForeground = iCode1;
                        pacsCurrent->bNormal = false;
                    }
                }
                else if (iCode0 == 4)
                {
                    // Background Color
                    //
                    if (iCode1 <= 7)
                    {
                        pacsCurrent->iBackground = iCode1;
                        pacsCurrent->bNormal = false;
                    }
                }
            }
        }
        pANSI += nLen;
        nANSI -= nLen;
    }
}

// The following is really 30 (E[0mE[1mE[4mE[5mE[7mE[33mE[43m) but we are
// being conservative.
//
#define ANSI_MAXIMUM_BINARY_TRANSITION_LENGTH 60

// Generate the minimal ANSI sequence that will transition from one color state
// to another.
//
char *ANSI_TransitionColorBinary
(
    ANSI_ColorState *acsCurrent,
    const ANSI_ColorState *pcsNext,
    int *nTransition,
    int  iEndGoal
)
{
    static char Buffer[ANSI_MAXIMUM_BINARY_TRANSITION_LENGTH+1];

    if (memcmp(acsCurrent, pcsNext, sizeof(ANSI_ColorState)) == 0)
    {
        *nTransition = 0;
        Buffer[0] = '\0';
        return Buffer;
    }
    ANSI_ColorState tmp = *acsCurrent;
    char *p = Buffer;

    if (pcsNext->bNormal)
    {
        // With NOBLEED, we can't stay in the normal mode. We must eventually
        // be on a white foreground.
        //
        pcsNext = &acsRestingStates[iEndGoal];
    }

    // Do we need to go through the normal state?
    //
    if (  tmp.bHighlite && !pcsNext->bHighlite
       || tmp.bUnder    && !pcsNext->bUnder
       || tmp.bBlink    && !pcsNext->bBlink
       || tmp.bInverse  && !pcsNext->bInverse
       || (  tmp.iBackground != ANSI_COLOR_INDEX_DEFAULT
          && pcsNext->iBackground == ANSI_COLOR_INDEX_DEFAULT)
       || (  tmp.iForeground != ANSI_COLOR_INDEX_DEFAULT
          && pcsNext->iForeground == ANSI_COLOR_INDEX_DEFAULT))
    {
        memcpy(p, ANSI_NORMAL, sizeof(ANSI_NORMAL)-1);
        p += sizeof(ANSI_NORMAL)-1;
        tmp = acsRestingStates[ANSI_ENDGOAL_NORMAL];
    }
    if (tmp.bHighlite != pcsNext->bHighlite)
    {
        memcpy(p, ANSI_HILITE, sizeof(ANSI_HILITE)-1);
        p += sizeof(ANSI_HILITE)-1;
    }
    if (tmp.bUnder != pcsNext->bUnder)
    {
        memcpy(p, ANSI_UNDER, sizeof(ANSI_UNDER)-1);
        p += sizeof(ANSI_UNDER)-1;
    }
    if (tmp.bBlink != pcsNext->bBlink)
    {
        memcpy(p, ANSI_BLINK, sizeof(ANSI_BLINK)-1);
        p += sizeof(ANSI_BLINK)-1;
    }
    if (tmp.bInverse != pcsNext->bInverse)
    {
        memcpy(p, ANSI_INVERSE, sizeof(ANSI_INVERSE)-1);
        p += sizeof(ANSI_INVERSE)-1;
    }
    if (tmp.iForeground != pcsNext->iForeground)
    {
        memcpy(p, ANSI_FOREGROUND, sizeof(ANSI_FOREGROUND)-1);
        p += sizeof(ANSI_FOREGROUND)-1;
        *p++ = pcsNext->iForeground + '0';
        *p++ = ANSI_ATTR_CMD;
    }
    if (tmp.iBackground != pcsNext->iBackground)
    {
        memcpy(p, ANSI_BACKGROUND, sizeof(ANSI_BACKGROUND)-1);
        p += sizeof(ANSI_BACKGROUND)-1;
        *p++ = pcsNext->iBackground + '0';
        *p++ = ANSI_ATTR_CMD;
    }
    *p = '\0';
    *nTransition = p - Buffer;
    return Buffer;
}

// The following is really 21 (%xn%xh%xu%xi%xf%xR%xr) but we are being conservative
//
#define ANSI_MAXIMUM_ESCAPE_TRANSITION_LENGTH 42

// Generate the minimal MU ANSI %-sequence that will transition from one color state
// to another.
//
char *ANSI_TransitionColorEscape
(
    ANSI_ColorState *acsCurrent,
    ANSI_ColorState *acsNext,
    int *nTransition
)
{
    static char Buffer[ANSI_MAXIMUM_ESCAPE_TRANSITION_LENGTH+1];
    static const char cForegroundColors[9] = "xrgybmcw";
    static const char cBackgroundColors[9] = "XRGYBMCW";

    if (memcmp(acsCurrent, acsNext, sizeof(ANSI_ColorState)) == 0)
    {
        *nTransition = 0;
        Buffer[0] = '\0';
        return Buffer;
    }
    ANSI_ColorState tmp = *acsCurrent;
    int  i = 0;

    // Do we need to go through the normal state?
    //
    if (  tmp.bBlink    && !acsNext->bBlink
       || tmp.bHighlite && !acsNext->bHighlite
       || tmp.bInverse  && !acsNext->bInverse
       || (  tmp.iBackground != ANSI_COLOR_INDEX_DEFAULT
          && acsNext->iBackground == ANSI_COLOR_INDEX_DEFAULT)
       || (  tmp.iForeground != ANSI_COLOR_INDEX_DEFAULT
          && acsNext->iForeground == ANSI_COLOR_INDEX_DEFAULT))
    {
        Buffer[i  ] = '%';
        Buffer[i+1] = 'x';
        Buffer[i+2] = 'n';
        i = i + 3;
        tmp = acsRestingStates[ANSI_ENDGOAL_NORMAL];
    }
    if (tmp.bHighlite != acsNext->bHighlite)
    {
        Buffer[i  ] = '%';
        Buffer[i+1] = 'x';
        Buffer[i+2] = 'h';
        i = i + 3;
    }
    if (tmp.bUnder != acsNext->bUnder)
    {
        Buffer[i  ] = '%';
        Buffer[i+1] = 'x';
        Buffer[i+2] = 'u';
        i = i + 3;
    }
    if (tmp.bBlink != acsNext->bBlink)
    {
        Buffer[i  ] = '%';
        Buffer[i+1] = 'x';
        Buffer[i+2] = 'f';
        i = i + 3;
    }
    if (tmp.bInverse != acsNext->bInverse)
    {
        Buffer[i  ] = '%';
        Buffer[i+1] = 'x';
        Buffer[i+2] = 'i';
        i = i + 3;
    }
    if (tmp.iForeground != acsNext->iForeground)
    {
        Buffer[i  ] = '%';
        Buffer[i+1] = 'x';
        Buffer[i+2] = cForegroundColors[acsNext->iForeground];
        i = i + 3;
    }
    if (tmp.iBackground != acsNext->iBackground)
    {
        Buffer[i  ] = '%';
        Buffer[i+1] = 'x';
        Buffer[i+2] = cBackgroundColors[acsNext->iBackground];
        i = i + 3;
    }
    Buffer[i] = '\0';
    *nTransition = i;
    return Buffer;
}

void ANSI_String_In_Init
(
    struct ANSI_In_Context *pacIn,
    const char *szString,
    int        iEndGoal
)
{
    pacIn->m_acs = acsRestingStates[iEndGoal];
    pacIn->m_p   = szString;
    pacIn->m_n   = strlen(szString);
    pacIn->m_bSawNormal = false;
}

void ANSI_String_Out_Init
(
    struct ANSI_Out_Context *pacOut,
    char *pField,
    int   nField,
    int   vwMax,
    int   iEndGoal
)
{
    pacOut->m_acs      = acsRestingStates[ANSI_ENDGOAL_NORMAL];
    pacOut->m_bDone    = false;
    pacOut->m_iEndGoal = iEndGoal;
    pacOut->m_n        = 0;
    pacOut->m_nMax     = nField;
    pacOut->m_p        = pField;
    pacOut->m_vw       = 0;
    pacOut->m_vwMax    = vwMax;
}

void ANSI_String_Skip
(
    struct ANSI_In_Context *pacIn,
    int   maxVisualWidth,
    int  *pnVisualWidth)
{
    *pnVisualWidth = 0;
    while (pacIn->m_n)
    {
        int nTokenLength0;
        int nTokenLength1;
        int iType = ANSI_lex(pacIn->m_n, pacIn->m_p, &nTokenLength0, &nTokenLength1);

        if (iType == TOKEN_TEXT_ANSI)
        {
            // Process TEXT
            //
            int nTextToSkip = maxVisualWidth - *pnVisualWidth;
            if (nTokenLength0 > nTextToSkip)
            {
                // We have reached the limits of the field
                //
                *pnVisualWidth += nTextToSkip;
                pacIn->m_p     += nTextToSkip;
                pacIn->m_n     -= nTextToSkip;
                return;
            }

            pacIn->m_p     += nTokenLength0;
            pacIn->m_n     -= nTokenLength0;
            *pnVisualWidth += nTokenLength0;

            if (nTokenLength1)
            {
                // Process ANSI
                //
                ANSI_Parse_m(&(pacIn->m_acs), nTokenLength1, pacIn->m_p, &(pacIn->m_bSawNormal));
                pacIn->m_p     += nTokenLength1;
                pacIn->m_n     -= nTokenLength1;
            }
        }
        else
        {
            // Process ANSI
            //
            ANSI_Parse_m(&(pacIn->m_acs), nTokenLength0, pacIn->m_p, &(pacIn->m_bSawNormal));
            pacIn->m_n     -= nTokenLength0;
            pacIn->m_p     += nTokenLength0;
        }
    }
}

// TODO: Rework comment block.
//
// ANSI_String_Copy -- Copy characters into a buffer starting at
// pField0 with maximum size of nField. Truncate the string if it would
// overflow the buffer -or- if it would have a visual with of greater
// than maxVisualWidth. Returns the number of ANSI-encoded characters
// copied to. Also, the visual width produced by this is returned in
// *pnVisualWidth.
//
// There are three ANSI color states that we deal with in this routine:
//
// 1. acsPrevious is the color state at the current end of the field.
//    It has already been encoded into the field.
//
// 2. acsCurrent is the color state that the current TEXT will be shown
//    with. It hasn't been encoded into the field, yet, and if we don't
//    have enough room for at least one character of TEXT, then it may
//    never be encoded into the field.
//
// 3. acsFinal is the required color state at the end. This is usually
//    the normal state or in the case of NOBLEED, it's a specific (and
//    somewhate arbitrary) foreground/background combination.
//
void ANSI_String_Copy
(
    struct ANSI_Out_Context *pacOut,
    struct ANSI_In_Context  *pacIn,
    int maxVisualWidth0
)
{
    // Check whether we have previous struck the session limits (given
    // by ANSI_String_Out_Init() for field size or visual width.
    //
    if (pacOut->m_bDone)
    {
        return;
    }

    // What is the working limit for visual width.
    //
    int vw = 0;
    int vwMax = pacOut->m_vwMax;
    if (maxVisualWidth0 < vwMax)
    {
        vwMax = maxVisualWidth0;
    }

    // What is the working limit for field size.
    //
    int nMax = pacOut->m_nMax;

    char *pField = pacOut->m_p;
    while (pacIn->m_n)
    {
        int nTokenLength0;
        int nTokenLength1;
        int iType = ANSI_lex(pacIn->m_n, pacIn->m_p, &nTokenLength0,
            &nTokenLength1);

        if (iType == TOKEN_TEXT_ANSI)
        {
            // We have a TEXT+[ANSI] phrase. The text length is given
            // by nTokenLength0, and the ANSI characters that follow
            // (if present) are of length nTokenLength1.
            //
            // Process TEXT part first.
            //
            // TODO: If there is a maximum size for the transitions,
            // and we have gobs of space, don't bother calculating
            // sizes so carefully. It might be faster

            // nFieldEffective is used to allocate and plan space for
            // the rest of the physical field (given by the current
            // nField length).
            //
            int nFieldEffective = nMax - 1; // Leave room for '\0'.

            int nTransitionFinal = 0;
            if (pacOut->m_iEndGoal <= ANSI_ENDGOAL_NOBLEED)
            {
                // If we lay down -any- of the TEXT part, we need to make
                // sure we always leave enough room to get back to the
                // required final ANSI color state.
                //
                if (memcmp( &(pacIn->m_acs),
                            &acsRestingStates[pacOut->m_iEndGoal],
                            sizeof(ANSI_ColorState)) != 0)
                {
                    // The color state of the TEXT isn't the final state,
                    // so how much room will the transition back to the
                    // final state take?
                    //
                    ANSI_TransitionColorBinary( &(pacIn->m_acs),
                                                &acsRestingStates[pacOut->m_iEndGoal],
                                                &nTransitionFinal,
                                                pacOut->m_iEndGoal);

                    nFieldEffective -= nTransitionFinal;
                }
            }

            // If we lay down -any- of the TEXT part, it needs to be
            // the right color.
            //
            int nTransition = 0;
            char *pTransition =
                ANSI_TransitionColorBinary( &(pacOut->m_acs),
                                            &(pacIn->m_acs),
                                            &nTransition,
                                            pacOut->m_iEndGoal);
            nFieldEffective -= nTransition;

            // If we find that there is no room for any of the TEXT,
            // then we're done.
            //
            // TODO: The visual width test can be done further up to save time.
            //
            if (  nFieldEffective <= nTokenLength0
               || vw + nTokenLength0 > vwMax)
            {
                // We have reached the limits of the field.
                //
                if (nFieldEffective > 0)
                {
                    // There was enough physical room in the field, but
                    // we would have exceeded the maximum visual width
                    // if we used all the text.
                    //
                    if (nTransition)
                    {
                        // Encode the TEXT color.
                        //
                        memcpy(pField, pTransition, nTransition);
                        pField += nTransition;
                    }

                    // Place just enough of the TEXT in the field.
                    //
                    int nTextToAdd = vwMax - vw;
                    if (nTextToAdd < nFieldEffective)
                    {
                        nFieldEffective = nTextToAdd;
                    }
                    memcpy(pField, pacIn->m_p, nFieldEffective);
                    pField += nFieldEffective;
                    pacIn->m_p += nFieldEffective;
                    pacIn->m_n -= nFieldEffective;
                    vw += nFieldEffective;
                    pacOut->m_acs = pacIn->m_acs;

                    // Was this visual width limit related to the session or
                    // the call?
                    //
                    if (vwMax != maxVisualWidth0)
                    {
                        pacOut->m_bDone = true;
                    }
                }
                else
                {
                    // Was size limit related to the session or the call?
                    //
                    pacOut->m_bDone = true;
                }
                pacOut->m_n += pField - pacOut->m_p;
                pacOut->m_nMax -= pField - pacOut->m_p;
                pacOut->m_p  = pField;
                pacOut->m_vw += vw;
                return;
            }

            if (nTransition)
            {
                memcpy(pField, pTransition, nTransition);
                pField += nTransition;
                nMax   -= nTransition;
            }
            memcpy(pField, pacIn->m_p, nTokenLength0);
            pField  += nTokenLength0;
            nMax    -= nTokenLength0;
            pacIn->m_p += nTokenLength0;
            pacIn->m_n -= nTokenLength0;
            vw += nTokenLength0;
            pacOut->m_acs = pacIn->m_acs;

            if (nTokenLength1)
            {
                // Process ANSI
                //
                ANSI_Parse_m(&(pacIn->m_acs), nTokenLength1, pacIn->m_p, &(pacIn->m_bSawNormal));
                pacIn->m_p += nTokenLength1;
                pacIn->m_n -= nTokenLength1;
            }
        }
        else
        {
            // Process ANSI
            //
            ANSI_Parse_m(&(pacIn->m_acs), nTokenLength0, pacIn->m_p, &(pacIn->m_bSawNormal));
            pacIn->m_n -= nTokenLength0;
            pacIn->m_p += nTokenLength0;
        }
    }
    pacOut->m_n += pField - pacOut->m_p;
    pacOut->m_nMax -= pField - pacOut->m_p;
    pacOut->m_p  = pField;
    pacOut->m_vw += vw;
}

int ANSI_String_Finalize
(
    struct ANSI_Out_Context *pacOut,
    int *pnVisualWidth
)
{
    char *pField = pacOut->m_p;
    if (pacOut->m_iEndGoal <= ANSI_ENDGOAL_NOBLEED)
    {
        int nTransition = 0;
        char *pTransition =
            ANSI_TransitionColorBinary( &(pacOut->m_acs),
                                        &acsRestingStates[pacOut->m_iEndGoal],
                                        &nTransition, pacOut->m_iEndGoal);
        if (nTransition)
        {
            memcpy(pField, pTransition, nTransition);
            pField += nTransition;
        }
    }
    *pField = '\0';
    pacOut->m_n += pField - pacOut->m_p;
    pacOut->m_p  = pField;
    *pnVisualWidth = pacOut->m_vw;
    return pacOut->m_n;
}

// Take an ANSI string and fit as much of the information as possible
// into a field of size nField. Truncate text. Also make sure that no color
// leaks out of the field.
//
int ANSI_TruncateToField
(
    const char *szString,
    int nField,
    char *pField0,
    int maxVisualWidth,
    int *pnVisualWidth,
    int  iEndGoal
)
{
    if (!szString)
    {
        pField0[0] = '\0';
        return 0;
    }
    struct ANSI_In_Context aic;
    struct ANSI_Out_Context aoc;
    ANSI_String_In_Init(&aic, szString, iEndGoal);
    ANSI_String_Out_Init(&aoc, pField0, nField, maxVisualWidth, iEndGoal);
    ANSI_String_Copy(&aoc, &aic, maxVisualWidth);
    return ANSI_String_Finalize(&aoc, pnVisualWidth);
}

char *ANSI_TruncateAndPad_sbuf(const char *pString, int nMaxVisualWidth, char fill)
{
    char *pStringModified = alloc_sbuf("ANSI_TruncateAndPad_sbuf");
    int nAvailable = SBUF_SIZE - nMaxVisualWidth;
    int nVisualWidth;
    int nLen = ANSI_TruncateToField(pString, nAvailable,
        pStringModified, nMaxVisualWidth, &nVisualWidth, ANSI_ENDGOAL_NORMAL);
    for (int i = nMaxVisualWidth - nVisualWidth; i > 0; i--)
    {
        pStringModified[nLen] = fill;
        nLen++;
    }
    pStringModified[nLen] = '\0';
    return pStringModified;
}

char *normal_to_white(const char *szString)
{
    static char Buffer[LBUF_SIZE];
    int nVisualWidth;
    ANSI_TruncateToField( szString,
                          sizeof(Buffer),
                          Buffer,
                          sizeof(Buffer),
                          &nVisualWidth,
                          ANSI_ENDGOAL_NOBLEED
                        );
    return Buffer;
}

typedef struct
{
    int len;
    char *p;
} LITERAL_STRING_STRUCT;

LITERAL_STRING_STRUCT MU_Substitutes[] =
{
    { 1, " "  },  // 0
    { 1, " "  },  // 1
    { 2, "%t" },  // 2
    { 2, "%r" },  // 3
    { 0, NULL },  // 4
    { 2, "%b" },  // 5
    { 2, "%%" },  // 6
    { 2, "%(" },  // 7
    { 2, "%)" },  // 8
    { 2, "%[" },  // 9
    { 2, "%]" },  // 10
    { 2, "%{" },  // 11
    { 2, "%}" },  // 12
    { 2, "\\\\" } // 13
};

const unsigned char MU_EscapeConvert[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 4, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    1, 0, 0, 0, 0, 6, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 3
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 4
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,13,10, 0, 0,  // 5
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 6
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,11, 0,12, 0, 0,  // 7

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
};

const unsigned char MU_EscapeNoConvert[256] =
{
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
//
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 0,  // 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 3
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 4
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 5
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 6
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 7

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
};

// Convert raw character sequences into MUX substitutions (type = 1)
// or strips them (type = 0).
//
char *translate_string(const char *szString, bool bConvert)
{
    static char szTranslatedString[LBUF_SIZE];
    char *pTranslatedString = szTranslatedString;

    const char *pString = szString;
    if (!szString)
    {
        *pTranslatedString = '\0';
        return szTranslatedString;
    }
    size_t nString = strlen(szString);

    ANSI_ColorState acsCurrent;
    ANSI_ColorState acsPrevious;
    acsCurrent = acsRestingStates[ANSI_ENDGOAL_NOBLEED];
    acsPrevious = acsCurrent;
    bool bSawNormal = false;
    const unsigned char *MU_EscapeChar = (bConvert)? MU_EscapeConvert : MU_EscapeNoConvert;
    while (nString)
    {
        int nTokenLength0;
        int nTokenLength1;
        int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1);

        if (iType == TOKEN_TEXT_ANSI)
        {
            // Process TEXT
            //
            int nTransition = 0;
            if (bConvert)
            {
                char *pTransition = ANSI_TransitionColorEscape(&acsPrevious, &acsCurrent, &nTransition);
                safe_str(pTransition, szTranslatedString, &pTranslatedString);
            }
            nString -= nTokenLength0;

            while (nTokenLength0--)
            {
                unsigned char ch = *pString++;
                unsigned char code = MU_EscapeChar[ch];
                if (code)
                {
                    // The following can look one ahead off the end of the
                    // current token (and even at the '\0' at the end of the
                    // string, but this is acceptable. An extra look will
                    // always see either ESC from the next ANSI sequence,
                    // or the '\0' on the end of the string. No harm done.
                    //
                    if (ch == ' ' && pString[0] == ' ')
                    {
                        code = 5;
                    }
                    safe_copy_buf(MU_Substitutes[code].p,
                        MU_Substitutes[code].len, szTranslatedString,
                        &pTranslatedString);
                }
                else
                {
                    safe_chr(ch, szTranslatedString, &pTranslatedString);
                }
            }
            acsPrevious = acsCurrent;

            if (nTokenLength1)
            {
                // Process ANSI
                //
                ANSI_Parse_m(&acsCurrent, nTokenLength1, pString, &bSawNormal);
                pString += nTokenLength1;
                nString -= nTokenLength1;
            }
        }
        else
        {
            // Process ANSI
            //
            ANSI_Parse_m(&acsCurrent, nTokenLength0, pString, &bSawNormal);
            nString -= nTokenLength0;
            pString += nTokenLength0;
        }
    }
    *pTranslatedString = '\0';
    return szTranslatedString;
}

/* ---------------------------------------------------------------------------
 * munge_space: Compress multiple spaces to one space, also remove leading and
 * trailing spaces.
 */
char *munge_space(const char *string)
{
    char *buffer = alloc_lbuf("munge_space");
    const char *p = string;
    char *q = buffer;

    if (p)
    {
        // Remove initial spaces.
        //
        while (mux_isspace(*p))
            p++;

        while (*p)
        {
            while (*p && !mux_isspace(*p))
                *q++ = *p++;

            while (mux_isspace(*p))
            {
                p++;
            }

            if (*p)
                *q++ = ' ';
        }
    }

    // Remove terminal spaces and terminate string.
    //
    *q = '\0';
    return buffer;
}

/* ---------------------------------------------------------------------------
 * trim_spaces: Remove leading and trailing spaces.
 */
char *trim_spaces(char *string)
{
    char *buffer = alloc_lbuf("trim_spaces");
    char *p = string;
    char *q = buffer;

    if (p)
    {
        // Remove initial spaces.
        //
        while (mux_isspace(*p))
        {
            p++;
        }

        while (*p)
        {
            // Copy non-space characters.
            //
            while (*p && !mux_isspace(*p))
            {
                *q++ = *p++;
            }

            // Compress spaces.
            //
            while (mux_isspace(*p))
            {
                p++;
            }

            // Leave one space.
            //
            if (*p)
            {
                *q++ = ' ';
            }
        }
    }

    // Terminate string.
    //
    *q = '\0';
    return buffer;
}

/*
 * ---------------------------------------------------------------------------
 * * grabto: Return portion of a string up to the indicated character.  Also
 * * returns a modified pointer to the string ready for another call.
 */

char *grabto(char **str, char targ)
{
    char *savec, *cp;

    if (!str || !*str || !**str)
        return NULL;

    savec = cp = *str;
    while (*cp && *cp != targ)
        cp++;
    if (*cp)
        *cp++ = '\0';
    *str = cp;
    return savec;
}

int string_compare(const char *s1, const char *s2)
{
    if (  mudstate.bStandAlone
       || mudconf.space_compress)
    {
        while (mux_isspace(*s1))
        {
            s1++;
        }
        while (mux_isspace(*s2))
        {
            s2++;
        }

        while (  *s1 && *s2
              && (  (mux_tolower(*s1) == mux_tolower(*s2))
                 || (mux_isspace(*s1) && mux_isspace(*s2))))
        {
            if (mux_isspace(*s1) && mux_isspace(*s2))
            {
                // skip all other spaces.
                //
                do
                {
                    s1++;
                } while (mux_isspace(*s1));

                do
                {
                    s2++;
                } while (mux_isspace(*s2));
            }
            else
            {
                s1++;
                s2++;
            }
        }
        if (  *s1
           && *s2)
        {
            return 1;
        }

        if (mux_isspace(*s1))
        {
            while (mux_isspace(*s1))
            {
                s1++;
            }
            return *s1;
        }
        if (mux_isspace(*s2))
        {
            while (mux_isspace(*s2))
            {
                s2++;
            }
            return *s2;
        }
        if (  *s1
           || *s2)
        {
            return 1;
        }
        return 0;
    }
    else
    {
        return mux_stricmp(s1, s2);
    }
}

int string_prefix(const char *string, const char *prefix)
{
    int count = 0;

    while (*string && *prefix
          && (mux_tolower(*string) == mux_tolower(*prefix)))
    {
        string++, prefix++, count++;
    }
    if (*prefix == '\0')
    {
        // Matched all of prefix.
        //
        return count;
    }
    else
    {
        return 0;
    }
}

/*
 * accepts only nonempty matches starting at the beginning of a word
 */

const char *string_match(const char *src, const char *sub)
{
    if ((*sub != '\0') && (src))
    {
        while (*src)
        {
            if (string_prefix(src, sub))
            {
                return src;
            }

            // else scan to beginning of next word
            //
            while (mux_isalnum(*src))
            {
                src++;
            }
            while (*src && !mux_isalnum(*src))
            {
                src++;
            }
        }
    }
    return 0;
}

/*
 * ---------------------------------------------------------------------------
 * * replace_string: Returns an lbuf containing string STRING with all occurances
 * * of OLD replaced by NEW. OLD and NEW may be different lengths.
 * * (mitch 1 feb 91)
 */

char *replace_string(const char *old, const char *new0, const char *s)
{
    if (!s)
    {
        return NULL;
    }
    size_t olen = strlen(old);
    char *result = alloc_lbuf("replace_string");
    char *r = result;
    while (*s)
    {
        // Find next occurrence of the first character of OLD string.
        //
        const char *p;
        if (  olen
           && (p = strchr(s, old[0])))
        {
            // Copy up to the next occurrence of the first char of OLD.
            //
            size_t n = p - s;
            if (n)
            {
                safe_copy_buf(s, n, result, &r);
                s += n;
            }

            // If we are really at an complete OLD, append NEW to the result
            // and bump the input string past the occurrence of OLD.
            // Otherwise, copy the character and try matching again.
            //
            if (!strncmp(old, s, olen))
            {
                safe_str(new0, result, &r);
                s += olen;
            }
            else
            {
                safe_chr(*s, result, &r);
                s++;
            }
        }
        else
        {
            // Finish copying source string. No matches. No further
            // work to perform.
            //
            safe_str(s, result, &r);
            break;
        }
    }
    *r = '\0';
    return result;
}

// ---------------------------------------------------------------------------
// replace_tokens: Performs ## and #@ substitution.
//
char *replace_tokens
(
    const char *s,
    const char *pBound,
    const char *pListPlace,
    const char *pSwitch
)
{
    if (!s)
    {
        return NULL;
    }
    char *result = alloc_lbuf("replace_tokens");
    char *r = result;

    while (*s)
    {
        // Find next '#'.
        //
        const char *p = strchr(s, '#');
        if (p)
        {
            // Copy up to the next occurrence of the first character.
            //
            size_t n = p - s;
            if (n)
            {
                safe_copy_buf(s, n, result, &r);
                s += n;
            }

            if (  s[1] == '#'
               && pBound)
            {
                // BOUND_VAR
                //
                safe_str(pBound, result, &r);
                s += 2;
            }
            else if (  s[1] == '@'
                    && pListPlace)
            {
                // LISTPLACE_VAR
                //
                safe_str(pListPlace, result, &r);
                s += 2;
            }
            else if (  s[1] == '$'
                    && pSwitch)
            {
                // SWITCH_VAR
                //
                safe_str(pSwitch, result, &r);
                s += 2;
            }
            else
            {
                safe_chr(*s, result, &r);
                s++;
            }
        }
        else
        {
            // Finish copying source string. No matches. No further
            // work to perform.
            //
            safe_str(s, result, &r);
            break;
        }
    }
    *r = '\0';
    return result;
}

#if 0
// Returns the number of identical characters in the two strings.
//
int prefix_match(const char *s1, const char *s2)
{
    int count = 0;

    while (*s1 && *s2
          && (mux_tolower(*s1) == mux_tolower(*s2)))
    {
        s1++, s2++, count++;
    }

    // If the whole string matched, count the null.  (Yes really.)
    //
    if (!*s1 && !*s2)
    {
        count++;
    }
    return count;
}
#endif // 0

bool minmatch(char *str, char *target, int min)
{
    while (*str && *target
          && (mux_tolower(*str) == mux_tolower(*target)))
    {
        str++;
        target++;
        min--;
    }
    if (*str)
    {
        return false;
    }
    if (!*target)
    {
        return true;
    }
    return (min <= 0);
}

// --------------------------------------------------------------------------
// StringCloneLen: allocate memory and copy string
//
char *StringCloneLen(const char *str, size_t nStr)
{
    char *buff = (char *)MEMALLOC(nStr+1);
    ISOUTOFMEMORY(buff);
    memcpy(buff, str, nStr);
    buff[nStr] = '\0';
    return buff;
}

// --------------------------------------------------------------------------
// StringClone: allocate memory and copy string
//
char *StringClone(const char *str)
{
    return StringCloneLen(str, strlen(str));
}

#if 0
// --------------------------------------------------------------------------
// BufferCloneLen: allocate memory and copy buffer
//
char *BufferCloneLen(const char *pBuffer, unsigned int nBuffer)
{
    char *buff = (char *)MEMALLOC(nBuffer);
    ISOUTOFMEMORY(buff);
    memcpy(buff, pBuffer, nBuffer);
    return buff;
}
#endif // 0

/* ---------------------------------------------------------------------------
 * safe_copy_str, safe_copy_chr - Copy buffers, watching for overflows.
 */

void safe_copy_str(const char *src, char *buff, char **bufp, int nSizeOfBuffer)
{
    if (src == NULL) return;

    char *tp = *bufp;
    char *maxtp = buff + nSizeOfBuffer;
    while (tp < maxtp && *src)
    {
        *tp++ = *src++;
    }
    *bufp = tp;
}

void safe_copy_str_lbuf(const char *src, char *buff, char **bufp)
{
    if (src == NULL)
    {
        return;
    }

    char *tp = *bufp;
    char *maxtp = buff + LBUF_SIZE - 1;
    while (tp < maxtp && *src)
    {
        *tp++ = *src++;
    }
    *bufp = tp;
}

size_t safe_copy_buf(const char *src, size_t nLen, char *buff, char **bufc)
{
    size_t left = LBUF_SIZE - (*bufc - buff) - 1;
    if (left < nLen)
    {
        nLen = left;
    }
    memcpy(*bufc, src, nLen);
    *bufc += nLen;
    return nLen;
}

size_t safe_fill(char *buff, char **bufc, char chFill, size_t nSpaces)
{
    // Check for buffer limits.
    //
    size_t nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
    if (nSpaces > nBufferAvailable)
    {
        nSpaces = nBufferAvailable;
    }

    // Fill with spaces.
    //
    memset(*bufc, chFill, nSpaces);
    *bufc += nSpaces;
    return nSpaces;
}

bool matches_exit_from_list(char *str, const char *pattern)
{
    char *s;

    while (*pattern)
    {
        for (s = str;   // check out this one
             ( *s
             && (mux_tolower(*s) == mux_tolower(*pattern))
             && *pattern
             && (*pattern != EXIT_DELIMITER));
             s++, pattern++) ;

        // Did we match it all?
        //
        if (*s == '\0')
        {
            // Make sure nothing afterwards
            //
            while (mux_isspace(*pattern))
            {
                pattern++;
            }

            // Did we get it?
            //
            if (  !*pattern
               || (*pattern == EXIT_DELIMITER))
            {
                return true;
            }
        }
        // We didn't get it, find next string to test
        //
        while (  *pattern
              && *pattern++ != EXIT_DELIMITER)
        {
            ; // Nothing.
        }
        while (mux_isspace(*pattern))
        {
            pattern++;
        }
    }
    return false;
}

const char Digits100[201] =
"001020304050607080900111213141516171819102122232425262728292\
031323334353637383930414243444546474849405152535455565758595\
061626364656667686960717273747576777879708182838485868788898\
09192939495969798999";

size_t mux_ltoa(long val, char *buf)
{
    char *p = buf;

    if (val < 0)
    {
        *p++ = '-';
        val = -val;
    }
    unsigned long uval = (unsigned long)val;

    char *q = p;

    const char *z;
    while (uval > 99)
    {
        z = Digits100 + ((uval % 100) << 1);
        uval /= 100;
        *p++ = *z;
        *p++ = *(z+1);
    }
    z = Digits100 + (uval << 1);
    *p++ = *z;
    if (uval > 9)
    {
        *p++ = *(z+1);
    }

    size_t nLength = p - buf;
    *p-- = '\0';

    // The digits are in reverse order with a possible leading '-'
    // if the value was negative. q points to the first digit,
    // and p points to the last digit.
    //
    while (q < p)
    {
        // Swap characters are *p and *q
        //
        char temp = *p;
        *p = *q;
        *q = temp;

        // Move p and first digit towards the middle.
        //
        --p;
        ++q;

        // Stop when we reach or pass the middle.
        //
    }
    return nLength;
}

char *mux_ltoa_t(long val)
{
    static char buff[12];
    mux_ltoa(val, buff);
    return buff;
}

void safe_ltoa(long val, char *buff, char **bufc)
{
    static char temp[12];
    size_t n = mux_ltoa(val, temp);
    safe_copy_buf(temp, n, buff, bufc);
}

size_t mux_i64toa(INT64 val, char *buf)
{
    char *p = buf;

    if (val < 0)
    {
        *p++ = '-';
        val = -val;
    }
    UINT64 uval = (UINT64)val;

    char *q = p;

    const char *z;
    while (uval > 99)
    {
        z = Digits100 + ((uval % 100) << 1);
        uval /= 100;
        *p++ = *z;
        *p++ = *(z+1);
    }
    z = Digits100 + (uval << 1);
    *p++ = *z;
    if (uval > 9)
    {
        *p++ = *(z+1);
    }

    size_t nLength = p - buf;
    *p-- = '\0';

    // The digits are in reverse order with a possible leading '-'
    // if the value was negative. q points to the first digit,
    // and p points to the last digit.
    //
    while (q < p)
    {
        // Swap characters are *p and *q
        //
        char temp = *p;
        *p = *q;
        *q = temp;

        // Move p and first digit towards the middle.
        //
        --p;
        ++q;

        // Stop when we reach or pass the middle.
        //
    }
    return nLength;
}

#if 0
char *mux_i64toa_t(INT64 val)
{
    static char buff[22];
    mux_i64toa(val, buff);
    return buff;
}
#endif

void safe_i64toa(INT64 val, char *buff, char **bufc)
{
    static char temp[22];
    size_t n = mux_i64toa(val, temp);
    safe_copy_buf(temp, n, buff, bufc);
}

const char TableATOI[16][10] =
{
    {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9},
    { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
    { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29},
    { 30, 31, 32, 33, 34, 35, 36, 37, 38, 39},
    { 40, 41, 42, 43, 44, 45, 46, 47, 48, 49},
    { 50, 51, 52, 53, 54, 55, 56, 57, 58, 59},
    { 60, 61, 62, 63, 64, 65, 66, 67, 68, 69},
    { 70, 71, 72, 73, 74, 75, 76, 77, 78, 79},
    { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89},
    { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99}
};

long mux_atol(const char *pString)
{
    long sum = 0;
    int LeadingCharacter = 0;

    // Convert ASCII digits
    //
    unsigned int c1;
    unsigned int c0 = pString[0];
    if (!mux_isdigit(c0))
    {
        while (mux_isspace(pString[0]))
        {
            pString++;
        }
        LeadingCharacter = pString[0];
        if (  LeadingCharacter == '-'
           || LeadingCharacter == '+')
        {
            pString++;
        }
        c0 = pString[0];
        if (!mux_isdigit(c0))
        {
            return 0;
        }
    }

    do
    {
        c1 = pString[1];
        if (mux_isdigit(c1))
        {
            sum = 100 * sum + TableATOI[c0-'0'][c1-'0'];
            pString += 2;
        }
        else
        {
            sum = 10 * sum + (c0-'0');
            break;
        }
    } while (mux_isdigit(c0 = pString[0]));

    // Interpret sign
    //
    if (LeadingCharacter == '-')
    {
        sum = -sum;
    }
    return sum;
}

INT64 mux_atoi64(const char *pString)
{
    INT64 sum = 0;
    int LeadingCharacter = 0;

    // Convert ASCII digits
    //
    unsigned int c1;
    unsigned int c0 = pString[0];
    if (!mux_isdigit(c0))
    {
        while (mux_isspace(pString[0]))
        {
            pString++;
        }
        LeadingCharacter = pString[0];
        if (  LeadingCharacter == '-'
           || LeadingCharacter == '+')
        {
            pString++;
        }
        c0 = pString[0];
        if (!mux_isdigit(c0))
        {
            return 0;
        }
    }

    do
    {
        c1 = pString[1];
        if (mux_isdigit(c1))
        {
            sum = 100 * sum + TableATOI[c0-'0'][c1-'0'];
            pString += 2;
        }
        else
        {
            sum = 10 * sum + (c0-'0');
            break;
        }
    } while (mux_isdigit(c0 = pString[0]));

    // Interpret sign
    //
    if (LeadingCharacter == '-')
    {
        sum = -sum;
    }
    return sum;
}

// Floating-point strings match one of the following patterns:
//
// [+-]?[0-9]?(.[0-9]+)([eE][+-]?[0-9]{1,3})?
// [+-]?[0-9]+(.[0-9]?)([eE][+-]?[0-9]{1,3})?
// +Inf
// -Inf
// Ind
// NaN
//
bool ParseFloat(PARSE_FLOAT_RESULT *pfr, const char *str, bool bStrict)
{
    memset(pfr, 0, sizeof(PARSE_FLOAT_RESULT));

    // Parse Input
    //
    unsigned char ch;
    pfr->pMeat = str;
    if (  !mux_isdigit(*str)
       && *str != '.')
    {
        while (mux_isspace(*str))
        {
            str++;
        }

        pfr->pMeat = str;
        if (*str == '-')
        {
            pfr->iLeadingSign = '-';
            str++;
        }
        else if (*str == '+')
        {
            pfr->iLeadingSign = '+';
            str++;
        }

        if (  !mux_isdigit(*str)
           && *str != '.')
        {
            // Look for three magic strings.
            //
            ch = mux_toupper(str[0]);
            if (ch == 'I')
            {
                // Could be 'Inf' or 'Ind'
                //
                ch = mux_toupper(str[1]);
                if (ch == 'N')
                {
                    ch = mux_toupper(str[2]);
                    if (ch == 'F')
                    {
                        // Inf
                        //
                        if (pfr->iLeadingSign == '-')
                        {
                            pfr->iString = IEEE_MAKE_NINF;
                        }
                        else
                        {
                            pfr->iString = IEEE_MAKE_PINF;
                        }
                        str += 3;
                        goto LastSpaces;
                    }
                    else if (ch == 'D')
                    {
                        // Ind
                        //
                        pfr->iString = IEEE_MAKE_IND;
                        str += 3;
                        goto LastSpaces;
                    }
                }
            }
            else if (ch == 'N')
            {
                // Could be 'Nan'
                //
                ch = mux_toupper(str[1]);
                if (ch == 'A')
                {
                    ch = mux_toupper(str[2]);
                    if (ch == 'N')
                    {
                        // Nan
                        //
                        pfr->iString = IEEE_MAKE_NAN;
                        str += 3;
                        goto LastSpaces;
                    }
                }
            }
            return false;
        }
    }

    // At this point, we have processed the leading sign, handled all
    // the magic strings, skipped the leading spaces, and best of all
    // we either have a digit or a decimal point.
    //
    pfr->pDigitsA = str;
    while (mux_isdigit(*str))
    {
        pfr->nDigitsA++;
        str++;
    }

    if (*str == '.')
    {
        str++;
    }

    pfr->pDigitsB = str;
    while (mux_isdigit(*str))
    {
        pfr->nDigitsB++;
        str++;
    }

    if (  pfr->nDigitsA == 0
       && pfr->nDigitsB == 0)
    {
        return false;
    }

    ch = mux_toupper(*str);
    if (ch == 'E')
    {
        // There is an exponent portion.
        //
        str++;
        if (*str == '-')
        {
            pfr->iExponentSign = '-';
            str++;
        }
        else if (*str == '+')
        {
            pfr->iExponentSign = '+';
            str++;
        }
        pfr->pDigitsC = str;
        while (mux_isdigit(*str))
        {
            pfr->nDigitsC++;
            str++;
        }

        if (  pfr->nDigitsC < 1
           || 4 < pfr->nDigitsC)
        {
            return false;
        }
    }

LastSpaces:

    pfr->nMeat = str - pfr->pMeat;

    // Trailing spaces.
    //
    while (mux_isspace(*str))
    {
        str++;
    }

    if (bStrict)
    {
        return (!*str);
    }
    else
    {
        return true;
    }
}

#define ATOF_LIMIT 100
static const double powerstab[10] =
{
            1.0,
           10.0,
          100.0,
         1000.0,
        10000.0,
       100000.0,
      1000000.0,
     10000000.0,
    100000000.0,
   1000000000.0
};

double mux_atof(char *szString, bool bStrict)
{
    PARSE_FLOAT_RESULT pfr;
    if (!ParseFloat(&pfr, szString, bStrict))
    {
        return 0.0;
    }

    if (pfr.iString)
    {
        // Return the double value which corresponds to the
        // string when HAVE_IEEE_FORMAT.
        //
#ifdef HAVE_IEEE_FP_FORMAT
        return MakeSpecialFloat(pfr.iString);
#else // HAVE_IEEE_FP_FORMAT
        return 0.0;
#endif // HAVE_IEEE_FP_FORMAT
    }

    // See if we can shortcut the decoding process.
    //
    double ret;
    if (  pfr.nDigitsA <= 9
       && pfr.nDigitsC == 0)
    {
        if (pfr.nDigitsB <= 9)
        {
            if (pfr.nDigitsB == 0)
            {
                // This 'floating-point' number is just an integer.
                //
                ret = (double)mux_atol(pfr.pDigitsA);
            }
            else
            {
                // This 'floating-point' number is fixed-point.
                //
                double rA = (double)mux_atol(pfr.pDigitsA);
                double rB = (double)mux_atol(pfr.pDigitsB);
                double rScale = powerstab[pfr.nDigitsB];
                ret = rA + rB/rScale;

                // As it is, ret is within a single bit of what a
                // a call to atof would return. However, we can
                // achieve that last lowest bit of precision by
                // computing a residual.
                //
                double residual = (ret - rA)*rScale;
                ret += (rB - residual)/rScale;
            }
            if (pfr.iLeadingSign == '-')
            {
                ret = -ret;
            }
            return ret;
        }
    }

    const char *p = pfr.pMeat;
    size_t n = pfr.nMeat;

    // We need to protect certain libraries from going nuts from being
    // force fed lots of ASCII.
    //
    char *pTmp = NULL;
    if (n > ATOF_LIMIT)
    {
        pTmp = alloc_lbuf("mux_atof");
        memcpy(pTmp, p, ATOF_LIMIT);
        pTmp[ATOF_LIMIT] = '\0';
        p = pTmp;
    }

    ret = mux_strtod(p, NULL);

    if (pTmp)
    {
        free_lbuf(pTmp);
    }

    return ret;
}

extern char *mux_dtoa(double d, int mode, int nRequest, int *iDecimalPoint,
                       int *sign, char **rve);

char *mux_ftoa(double r, bool bRounded, int frac)
{
    static char buffer[100];
    char *q = buffer;
    char *rve = NULL;
    int iDecimalPoint = 0;
    int bNegative = 0;
    int mode = 0;
    int nRequest = 50;

    if (bRounded)
    {
        mode = 3;
        nRequest = frac;
        if (50 < nRequest)
        {
            nRequest = 50;
        }
        else if (nRequest < -20)
        {
            nRequest = -20;
        }
    }
    char *p = mux_dtoa(r, mode, nRequest, &iDecimalPoint, &bNegative, &rve);
    int nSize = rve - p;
    if (nSize > 50)
    {
        nSize = 50;
    }
    if (bNegative)
    {
        *q++ = '-';
    }
    if (iDecimalPoint == 9999)
    {
        // Inf or NaN
        //
        memcpy(q, p, nSize);
        q += nSize;
    }
    else if (nSize <= 0)
    {
        // Zero
        //
        *q++ = '0';
        if (  bRounded
           && 0 < nRequest)
        {
            *q++ = '.';
            memset(q, '0', nRequest);
            q += nRequest;
        }
    }
    else if (  iDecimalPoint <= -6
            || 18 <= iDecimalPoint)
    {
        *q++ = *p++;
        if (1 < nSize)
        {
            *q++ = '.';
            memcpy(q, p, nSize-1);
            q += nSize-1;
        }
        *q++ = 'E';
        q += mux_ltoa(iDecimalPoint-1, q);
    }
    else if (iDecimalPoint <= 0)
    {
        // iDecimalPoint = -5 to 0
        //
        *q++ = '0';
        *q++ = '.';
        memset(q, '0', -iDecimalPoint);
        q += -iDecimalPoint;
        memcpy(q, p, nSize);
        q += nSize;
        if (bRounded)
        {
            int nPad = nRequest - (nSize - iDecimalPoint);
            if (0 < nPad)
            {
                memset(q, '0', nPad);
                q += nPad;
            }
        }
    }
    else
    {
        // iDecimalPoint = 1 to 17
        //
        if (nSize <= iDecimalPoint)
        {
            memcpy(q, p, nSize);
            q += nSize;
            memset(q, '0', iDecimalPoint - nSize);
            q += iDecimalPoint - nSize;
            if (  bRounded
               && 0 < nRequest)
            {
                *q++ = '.';
                memset(q, '0', nRequest);
                q += nRequest;
            }
        }
        else
        {
            memcpy(q, p, iDecimalPoint);
            q += iDecimalPoint;
            p += iDecimalPoint;
            *q++ = '.';
            memcpy(q, p, nSize - iDecimalPoint);
            q += nSize - iDecimalPoint;
            if (bRounded)
            {
                int nPad = nRequest - (nSize - iDecimalPoint);
                if (0 < nPad)
                {
                    memset(q, '0', nPad);
                    q += nPad;
                }
            }
        }
    }
    *q = '\0';
    return buffer;
}

bool is_integer(char *str, int *pDigits)
{
    int nDigits = 0;
    if (pDigits)
    {
        *pDigits = 0;
    }

    // Leading spaces.
    //
    while (mux_isspace(*str))
    {
        str++;
    }

    // Leading minus or plus
    //
    if (*str == '-' || *str == '+')
    {
        str++;

        // Just a sign by itself isn't an integer.
        //
        if (!*str)
        {
            return false;
        }
    }

    // Need at least 1 integer
    //
    if (!mux_isdigit(*str))
    {
        return false;
    }

    // The number (int)
    //
    do
    {
        str++;
        nDigits++;
    } while (mux_isdigit(*str));

    if (pDigits)
    {
        *pDigits = nDigits;
    }

    // Trailing Spaces.
    //
    while (mux_isspace(*str))
    {
        str++;
    }

    return (!*str);
}

bool is_rational(char *str)
{
    // Leading spaces.
    //
    while (mux_isspace(*str))
    {
        str++;
    }

    // Leading minus or plus sign.
    //
    if (*str == '-' || *str == '+')
    {
        str++;

        // But not if just a sign.
        //
        if (!*str)
        {
            return false;
        }
    }

    // Need at least one digit.
    //
    bool got_one = false;
    if (mux_isdigit(*str))
    {
        got_one = true;
    }

    // The number (int)
    //
    while (mux_isdigit(*str))
    {
        str++;
    }

    // Decimal point.
    //
    if (*str == '.')
    {
        str++;
    }

    // Need at least one digit
    //
    if (mux_isdigit(*str))
    {
        got_one = true;
    }

    if (!got_one)
    {
        return false;
    }

    // The number (fract)
    //
    while (mux_isdigit(*str))
    {
        str++;
    }

    // Trailing spaces.
    //
    while (mux_isspace(*str))
    {
        str++;
    }

    // There must be nothing else after the trailing spaces.
    //
    return (!*str);
}

bool is_real(char *str)
{
    PARSE_FLOAT_RESULT pfr;
    return ParseFloat(&pfr, str);
}

// mux_strtok_src, mux_strtok_ctl, mux_strtok_parse.
//
// These three functions work together to replace the functionality of the
// strtok() C runtime library function. Call mux_strtok_src() first with
// the string to parse, then mux_strtok_ctl() with the control
// characters, and finally mux_strtok_parse() to parse out the tokens.
//
// You may call mux_strtok_ctl() to change the set of control characters
// between mux_strtok_parse() calls, however keep in mind that the parsing
// may not occur how you intend it to as mux_strtok_parse() does not
// consume -all- of the controlling delimiters that separate two tokens.
// It consumes only the first one.
//
void mux_strtok_src(MUX_STRTOK_STATE *tts, char *arg_pString)
{
    if (!tts || !arg_pString) return;

    // Remember the string to parse.
    //
    tts->pString = arg_pString;
}

void mux_strtok_ctl(MUX_STRTOK_STATE *tts, char *pControl)
{
    if (!tts || !pControl) return;

    // No character is a control character.
    //
    memset(tts->aControl, 0, sizeof(tts->aControl));

    // The NULL character is always a control character.
    //
    tts->aControl[0] = 1;

    // Record the user-specified control characters.
    //
    while (*pControl)
    {
        tts->aControl[(unsigned char)*pControl] = 1;
        pControl++;
    }
}

char *mux_strtok_parseLEN(MUX_STRTOK_STATE *tts, int *pnLen)
{
    *pnLen = 0;
    if (!tts)
    {
        return NULL;
    }
    char *p = tts->pString;
    if (!p)
    {
        return NULL;
    }

    // Skip over leading control characters except for the NUL character.
    //
    while (tts->aControl[(unsigned char)*p] && *p)
    {
        p++;
    }

    char *pReturn = p;

    // Skip over non-control characters.
    //
    while (tts->aControl[(unsigned char)*p] == 0)
    {
        p++;
    }

    // What is the length of this token?
    //
    *pnLen = p - pReturn;

    // Terminate the token with a NUL.
    //
    if (p[0])
    {
        // We found a non-NUL delimiter, so the next call will begin parsing
        // on the character after this one.
        //
        tts->pString = p+1;
    }
    else
    {
        // We hit the end of the string, so the end of the string is where
        // the next call will begin.
        //
        tts->pString = p;
    }

    // Did we find a token?
    //
    if (*pnLen > 0)
    {
        return pReturn;
    }
    else
    {
        return NULL;
    }
}

char *mux_strtok_parse(MUX_STRTOK_STATE *tts)
{
    int nLen;
    char *p = mux_strtok_parseLEN(tts, &nLen);
    if (p)
    {
        p[nLen] = '\0';
    }
    return p;
}

// This function will filter out any characters in the the set from
// the string.
//
char *RemoveSetOfCharacters(char *pString, char *pSetToRemove)
{
    static char Buffer[LBUF_SIZE];
    char *pBuffer = Buffer;

    int nLen;
    int nLeft = sizeof(Buffer) - 1;
    char *p;
    MUX_STRTOK_STATE tts;
    mux_strtok_src(&tts, pString);
    mux_strtok_ctl(&tts, pSetToRemove);
    for ( p = mux_strtok_parseLEN(&tts, &nLen);
          p && nLeft;
          p = mux_strtok_parseLEN(&tts, &nLen))
    {
        if (nLeft < nLen)
        {
            nLen = nLeft;
        }
        memcpy(pBuffer, p, nLen);
        pBuffer += nLen;
        nLeft -= nLen;
    }
    *pBuffer = '\0';
    return Buffer;
}

void ItemToList_Init(ITL *p, char *arg_buff, char **arg_bufc,
    char arg_chPrefix, char arg_chSep)
{
    p->bFirst = true;
    p->chPrefix = arg_chPrefix;
    p->chSep = arg_chSep;
    p->buff = arg_buff;
    p->bufc = arg_bufc;
    p->nBufferAvailable = LBUF_SIZE - (*arg_bufc - arg_buff) - 1;
}

bool ItemToList_AddInteger(ITL *pContext, int i)
{
    char smbuf[SBUF_SIZE];
    char *p = smbuf;
    if (  !pContext->bFirst
       && pContext->chSep)
    {
        *p++ = pContext->chSep;
    }
    if (pContext->chPrefix)
    {
        *p++ = pContext->chPrefix;
    }
    p += mux_ltoa(i, p);
    size_t nLen = p - smbuf;
    if (nLen > pContext->nBufferAvailable)
    {
        // Out of room.
        //
        return false;
    }
    if (pContext->bFirst)
    {
        pContext->bFirst = false;
    }
    memcpy(*(pContext->bufc), smbuf, nLen);
    *(pContext->bufc) += nLen;
    pContext->nBufferAvailable -= nLen;
    return true;
}

bool ItemToList_AddStringLEN(ITL *pContext, size_t nStr, char *pStr)
{
    size_t nLen = nStr;
    if (  !pContext->bFirst
       && pContext->chSep)
    {
        nLen++;
    }
    if (pContext->chPrefix)
    {
        nLen++;
    }
    if (nLen > pContext->nBufferAvailable)
    {
        // Out of room.
        //
        return false;
    }
    char *p = *(pContext->bufc);
    if (pContext->bFirst)
    {
        pContext->bFirst = false;
    }
    else if (pContext->chSep)
    {
        *p++ = pContext->chSep;
    }
    if (pContext->chPrefix)
    {
        *p++ = pContext->chPrefix;
    }
    memcpy(p, pStr, nStr);
    *(pContext->bufc) += nLen;
    pContext->nBufferAvailable -= nLen;
    return true;
}

bool ItemToList_AddString(ITL *pContext, char *pStr)
{
    size_t nStr = strlen(pStr);
    return ItemToList_AddStringLEN(pContext, nStr, pStr);
}

void ItemToList_Final(ITL *pContext)
{
    **(pContext->bufc) = '\0';
}

// mux_stricmp - Compare two strings ignoring case.
//
int mux_stricmp(const char *a, const char *b)
{
    while (  *a
          && *b
          && mux_tolower(*a) == mux_tolower(*b))
    {
        a++;
        b++;
    }
    int c1 = mux_tolower(*a);
    int c2 = mux_tolower(*b);
    if (c1 < c2)
    {
        return -1;
    }
    else if (c1 > c2)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

// mux_memicmp - Compare two buffers ignoring case.
//
int mux_memicmp(const void *p1_arg, const void *p2_arg, size_t n)
{
    unsigned char *p1 = (unsigned char *)p1_arg;
    unsigned char *p2 = (unsigned char *)p2_arg;
    while (  n
          && mux_tolower(*p1) == mux_tolower(*p2))
    {
        n--;
        p1++;
        p2++;
    }
    if (n)
    {
        int c1 = mux_tolower(*p1);
        int c2 = mux_tolower(*p2);
        if (c1 < c2)
        {
            return -1;
        }
        else if (c1 > c2)
        {
            return 1;
        }
    }
    return 0;
}

// mux_strlwr - Convert string to all lower case.
//
void mux_strlwr(char *a)
{
    while (*a)
    {
        *a = mux_tolower(*a);
        a++;
    }
}

// mux_strupr - Convert string to all upper case.
//
void mux_strupr(char *a)
{
    while (*a)
    {
        *a = mux_toupper(*a);
        a++;
    }
}

#ifdef WIN32
#define VSNPRINTF _vsnprintf
#else // WIN32
#ifdef NEED_VSPRINTF_DCL
extern char *vsprintf(char *, char *, va_list);
#endif // NEED_VSPRINTF_DCL
#define VSNPRINTF vsnprintf
#endif // WIN32

// mux_vsnprintf - Is an sprintf-like function that will not overflow
// a buffer of specific size. The size is give by count, and count
// should be chosen to include the '\0' termination.
//
// Returns: A number from 0 to count-1 that is the string length of
// the returned (possibly truncated) buffer.
//
int DCL_CDECL mux_vsnprintf(char *buff, int count, const char *fmt, va_list va)
{
    // From the manuals:
    //
    // vsnprintf returns the number of characters written, not
    // including the terminating '\0' character.
    //
    // It returns a -1 if an output error occurs.
    //
    // It can return a number larger than the size of the buffer
    // on some systems to indicate how much space it -would- have taken
    // if not limited by the request.
    //
    // On Win32, it can fill the buffer completely without a
    // null-termination and return -1.


    // To favor the Unix case, if there is an output error, but
    // vsnprint doesn't touch the buffer, we avoid undefined trash by
    // null-terminating the buffer to zero-length before the call.
    // Not sure that this happens, but it's a cheap precaution.
    //
    buff[0] = '\0';

    // If Unix version does start touching the buffer, null-terminates,
    // and returns -1, we are still safe. However, if Unix version
    // touches the buffer writes garbage, and then returns -1, we may
    // pass garbage, but this possibility seems very unlikely.
    //
    int len = VSNPRINTF(buff, count, fmt, va);
    if (len < 0 || len > count-1)
    {
        if (buff[0] == '\0')
        {
            // vsnprintf did not touch the buffer.
            //
            len = 0;
        }
        else
        {
            len = count-1;
        }
    }
    buff[len] = '\0';
    return len;
}

// This function acts like fgets except that any data on the end of the
// line past the buffer size is truncated instead of being returned on
// the next call.
//
int GetLineTrunc(char *Buffer, size_t nBuffer, FILE *fp)
{
    size_t lenBuffer = 0;
    if (fgets(Buffer, nBuffer, fp))
    {
        lenBuffer = strlen(Buffer);
    }
    if (lenBuffer <= 0)
    {
        memcpy(Buffer, "\n", 2);
        return 1;
    }
    if (Buffer[lenBuffer-1] != '\n')
    {
        // The line was too long for the buffer. Continue reading until the
        // end of the line.
        //
        char TruncBuffer[SBUF_SIZE];
        size_t lenTruncBuffer;
        do
        {
            if (!fgets(TruncBuffer, sizeof(TruncBuffer), fp))
            {
                break;
            }
            lenTruncBuffer = strlen(TruncBuffer);
        }
        while (TruncBuffer[lenTruncBuffer-1] != '\n');
    }
    return lenBuffer;
}

// Method: Boyer-Moore-Horspool
//
// This method is a simplification of the Boyer-Moore String Searching
// Algorithm, but a useful one. It does not require as much temporary
// storage, and the setup costs are not as high as the full Boyer-Moore.
//
// If we were searching megabytes of data instead of 8KB at most, then
// the full Boyer-Moore would make more sense.
//
#define BMH_LARGE 32767
void BMH_Prepare(BMH_State *bmhs, int nPat, const char *pPat)
{
    if (nPat <= 0)
    {
        return;
    }
    int k;
    for (k = 0; k < 256; k++)
    {
        bmhs->m_d[k] = nPat;
    }

    char chLastPat = pPat[nPat-1];
    bmhs->m_skip2 = nPat;
    for (k = 0; k < nPat - 1; k++)
    {
        bmhs->m_d[(unsigned char)pPat[k]] = nPat - k - 1;
        if (pPat[k] == chLastPat)
        {
            bmhs->m_skip2 = nPat - k - 1;
        }
    }
    bmhs->m_d[(unsigned char)chLastPat] = BMH_LARGE;
}

int BMH_Execute(BMH_State *bmhs, int nPat, const char *pPat, int nSrc, const char *pSrc)
{
    if (nPat <= 0)
    {
        return -1;
    }
    for (int i = nPat-1; i < nSrc; i += bmhs->m_skip2)
    {
        while ((i += bmhs->m_d[(unsigned char)(pSrc[i])]) < nSrc)
        {
            ; // Nothing.
        }
        if (i < BMH_LARGE)
        {
            break;
        }
        i -= BMH_LARGE;
        int j = nPat - 1;
        const char *s = pSrc + (i - j);
        while (--j >= 0 && s[j] == pPat[j])
        {
            ; // Nothing.
        }
        if (j < 0)
        {
            return s-pSrc;
        }
    }
    return -1;
}

int BMH_StringSearch(int nPat, const char *pPat, int nSrc, const char *pSrc)
{
    BMH_State bmhs;
    BMH_Prepare(&bmhs, nPat, pPat);
    return BMH_Execute(&bmhs, nPat, pPat, nSrc, pSrc);
}

void BMH_PrepareI(BMH_State *bmhs, int nPat, const char *pPat)
{
    if (nPat <= 0)
    {
        return;
    }
    int k;
    for (k = 0; k < 256; k++)
    {
        bmhs->m_d[k] = nPat;
    }

    char chLastPat = pPat[nPat-1];
    bmhs->m_skip2 = nPat;
    for (k = 0; k < nPat - 1; k++)
    {
        bmhs->m_d[mux_toupper(pPat[k])] = nPat - k - 1;
        bmhs->m_d[mux_tolower(pPat[k])] = nPat - k - 1;
        if (pPat[k] == chLastPat)
        {
            bmhs->m_skip2 = nPat - k - 1;
        }
    }
    bmhs->m_d[mux_toupper(chLastPat)] = BMH_LARGE;
    bmhs->m_d[mux_tolower(chLastPat)] = BMH_LARGE;
}

int BMH_ExecuteI(BMH_State *bmhs, int nPat, const char *pPat, int nSrc, const char *pSrc)
{
    if (nPat <= 0)
    {
        return -1;
    }
    for (int i = nPat-1; i < nSrc; i += bmhs->m_skip2)
    {
        while ((i += bmhs->m_d[(unsigned char)(pSrc[i])]) < nSrc)
        {
            ; // Nothing.
        }
        if (i < BMH_LARGE)
        {
            break;
        }
        i -= BMH_LARGE;
        int j = nPat - 1;
        const char *s = pSrc + (i - j);
        while (  --j >= 0
              && mux_toupper(s[j]) == mux_toupper(pPat[j]))
        {
            ; // Nothing.
        }
        if (j < 0)
        {
            return s-pSrc;
        }
    }
    return -1;
}

int BMH_StringSearchI(int nPat, const char *pPat, int nSrc, const char *pSrc)
{
    BMH_State bmhs;
    BMH_PrepareI(&bmhs, nPat, pPat);
    return BMH_ExecuteI(&bmhs, nPat, pPat, nSrc, pSrc);
}

// ---------------------------------------------------------------------------
// cf_art_except:
//
// Add an article rule to the ruleset.
//

extern void DCL_CDECL cf_log_syntax(dbref player, char *cmd, const char *fmt, ...);

CF_HAND(cf_art_rule)
{
    char* pCurrent = str;

    while (mux_isspace(*pCurrent))
    {
        pCurrent++;
    }
    char* pArticle = pCurrent;
    while (  !mux_isspace(*pCurrent)
          && *pCurrent != '\0')
    {
        pCurrent++;
    }
    if (*pCurrent == '\0')
    {
        cf_log_syntax(player, cmd, "No article or regexp specified.");
        return -1;
    }

    bool bUseAn = false;
    bool bOkay = false;

    if (pCurrent - pArticle <= 2)
    {
        if (mux_tolower(pArticle[0]) == 'a')
        {
            if (mux_tolower(pArticle[1]) == 'n')
            {
                bUseAn = true;
                bOkay = true;
            }

            if (mux_isspace(pArticle[1]))
            {
                bOkay = true;
            }
        }
    }

    if (!bOkay)
    {
        *pCurrent = '\0';
        cf_log_syntax(player, cmd, "Invalid article '%s'.", pArticle);
        return -1;
    }

    while (mux_isspace(*pCurrent))
    {
        pCurrent++;
    }

    if (*pCurrent == '\0')
    {
        cf_log_syntax(player, cmd, "No regexp specified.");
        return -1;
    }

    const char *errptr;
    int erroffset;
    pcre* reNewRegexp = pcre_compile(pCurrent, 0, &errptr, &erroffset, NULL);
    if (!reNewRegexp)
    {
        cf_log_syntax(player, cmd, "Error processing regexp '%s':.",
              pCurrent, errptr);
        return -1;
    }

    pcre_extra *study = pcre_study(reNewRegexp, 0, &errptr);

    // Push new rule at head of list.
    ArtRuleset** arRules = (ArtRuleset **) vp;
    ArtRuleset* arNewRule = (ArtRuleset *) MEMALLOC(sizeof(ArtRuleset));

    arNewRule->m_pNextRule = *arRules;
    arNewRule->m_bUseAn = bUseAn;
    arNewRule->m_pRegexp = reNewRegexp;
    arNewRule->m_pRegexpStudy = study;

    *arRules = arNewRule;
    return 0;
}