mux2.4/game/data/
mux2.4/src/tools/
// comsys.cpp
//
// $Id: comsys.cpp,v 1.38 2005/10/16 03:01:21 sdennis Exp $
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"

#include <sys/types.h>

#include "ansi.h"
#include "attrs.h"
#include "comsys.h"
#include "functions.h"
#include "interface.h"
#include "powers.h"

int num_channels;
comsys_t *comsys_table[NUM_COMSYS];

#define DFLT_MAX_LOG        0
#define MIN_RECALL_REQUEST  1
#define DFLT_RECALL_REQUEST 10
#define MAX_RECALL_REQUEST  200

extern int iMod(int x, int y);

// Return value must be free_lbuf'ed.
//
char *RestrictTitleValue(char *pTitleRequest)
{
    // First, remove all '\r\n\t' from the string.
    //
    char *pNewTitle = RemoveSetOfCharacters(pTitleRequest, "\r\n\t");

    // Optimize/terminate any ANSI in the string.
    //
    char NewTitle_ANSI[MAX_TITLE_LEN+1];
    int nVisualWidth;
    int nLen = ANSI_TruncateToField(pNewTitle, sizeof(NewTitle_ANSI),
        NewTitle_ANSI, sizeof(NewTitle_ANSI), &nVisualWidth,
        ANSI_ENDGOAL_NORMAL);
    memcpy(pNewTitle, NewTitle_ANSI, nLen+1);
    return pNewTitle;
}

void do_setcomtitlestatus(dbref player, struct channel *ch, bool status)
{
    struct comuser *user = select_user(ch,player);
    if (ch && user)
    {
        user->ComTitleStatus = status;
    }
}

void do_setnewtitle(dbref player, struct channel *ch, char *pValidatedTitle)
{
    struct comuser *user = select_user(ch, player);

    if (ch && user)
    {
        if (user->title)
        {
            MEMFREE(user->title);
            user->title = NULL;
        }
        user->title = StringClone(pValidatedTitle);
    }
}

void load_comsys(char *filename)
{
    int i;
    char buffer[200];

    for (i = 0; i < NUM_COMSYS; i++)
    {
        comsys_table[i] = NULL;
    }

    FILE *fp = fopen(filename, "rb");
    if (!fp)
    {
        Log.tinyprintf("Error: Couldn't find %s." ENDLINE, filename);
    }
    else
    {
        DebugTotalFiles++;
        Log.tinyprintf("LOADING: %s" ENDLINE, filename);
        if (fscanf(fp, "*** Begin %s ***\n", buffer) == 1 && !strcmp(buffer, "CHANNELS"))
        {
            load_channels(fp);
        }
        else
        {
            Log.tinyprintf("Error: Couldn't find Begin CHANNELS in %s.", filename);
            return;
        }

        if (fscanf(fp, "*** Begin %s ***\n", buffer) == 1 && !strcmp(buffer, "COMSYS"))
        {
            load_comsystem(fp);
        }
        else
        {
            Log.tinyprintf("Error: Couldn't find Begin COMSYS in %s.", filename);
            return;
        }

        if (fclose(fp) == 0)
        {
            DebugTotalFiles--;
        }
        Log.tinyprintf("LOADING: %s (done)" ENDLINE, filename);
    }
}

void save_comsys(char *filename)
{
    char buffer[500];

    sprintf(buffer, "%s.#", filename);
    FILE *fp = fopen(buffer, "wb");
    if (!fp)
    {
        Log.tinyprintf("Unable to open %s for writing." ENDLINE, buffer);
        return;
    }
    DebugTotalFiles++;
    fprintf(fp, "*** Begin CHANNELS ***\n");
    save_channels(fp);

    fprintf(fp, "*** Begin COMSYS ***\n");
    save_comsystem(fp);

    if (fclose(fp) == 0)
    {
        DebugTotalFiles--;
    }
    ReplaceFile(buffer, filename);
}

// Aliases must be between 1 and 5 characters. No spaces. No ANSI.
//
char *MakeCanonicalComAlias
(
    const char *pAlias,
    int *nValidAlias,
    bool *bValidAlias
)
{
    static char Buffer[ALIAS_SIZE];
    *nValidAlias = 0;
    *bValidAlias = false;

    if (!pAlias)
    {
        return NULL;
    }
    const char *p = pAlias;
    char *q = Buffer;
    int n = 0;
    while (*p)
    {
        if (  !mux_isprint(*p)
           || *p == ' ')
        {
            return NULL;
        }
        if (  n <= MAX_ALIAS_LEN
           && q < Buffer + ALIAS_SIZE)
        {
            n++;
            *q++ = *p;
        }
        p++;
    }
    *q = '\0';
    if (  n < 1
       || MAX_ALIAS_LEN < n)
    {
        return NULL;
    }
    *nValidAlias = n;
    *bValidAlias = true;
    return Buffer;
}

bool ParseChannelLine(char *pBuffer, char *pAlias5, char **ppChannelName)
{
    // Fetch alias portion. We need to find the first space.
    //
    char *p = strchr(pBuffer, ' ');
    if (!p)
    {
        return false;
    }

    *p = '\0';
    bool bValidAlias;
    int  nValidAlias;
    char *pValidAlias = MakeCanonicalComAlias(pBuffer, &nValidAlias, &bValidAlias);
    if (!bValidAlias)
    {
        return false;
    }
    strcpy(pAlias5, pValidAlias);

    // Skip any leading space before the channel name.
    //
    p++;
    while (mux_isspace(*p))
    {
        p++;
    }

    if (*p == '\0')
    {
        return false;
    }

    // The rest of the line is the channel name.
    //
    *ppChannelName = StringClone(p);
    return true;
}

void load_channels(FILE *fp)
{
    int i, j;
    char buffer[LBUF_SIZE];
    comsys_t *c;

    int np = 0;
    int cc = fscanf(fp, "%d\n", &np);
    mux_assert(1 == cc);
    for (i = 0; i < np; i++)
    {
        c = create_new_comsys();
        c->who = 0;
        c->numchannels = 0;
        cc = fscanf(fp, "%d %d\n", &(c->who), &(c->numchannels));
        mux_assert(2 == cc);
        c->maxchannels = c->numchannels;
        if (c->maxchannels > 0)
        {
            c->alias = (char *)MEMALLOC(c->maxchannels * ALIAS_SIZE);
            ISOUTOFMEMORY(c->alias);
            c->channels = (char **)MEMALLOC(sizeof(char *) * c->maxchannels);
            ISOUTOFMEMORY(c->channels);

            for (j = 0; j < c->numchannels; j++)
            {
                int n = GetLineTrunc(buffer, sizeof(buffer), fp);
                if (buffer[n-1] == '\n')
                {
                    // Get rid of trailing '\n'.
                    //
                    n--;
                    buffer[n] = '\0';
                }
                if (!ParseChannelLine(buffer, c->alias + j * ALIAS_SIZE, c->channels+j))
                {
                    c->numchannels--;
                    j--;
                }
            }
            sort_com_aliases(c);
        }
        else
        {
            c->alias = NULL;
            c->channels = NULL;
        }
        if (Good_obj(c->who))
        {
            add_comsys(c);
        }
        else
        {
            Log.tinyprintf("Invalid dbref %d." ENDLINE, c->who);
        }
        purge_comsystem();
    }
}

void purge_comsystem(void)
{
#ifdef ABORT_PURGE_COMSYS
    return;
#endif // ABORT_PURGE_COMSYS

    comsys_t *c;
    comsys_t *d;
    int i;
    for (i = 0; i < NUM_COMSYS; i++)
    {
        c = comsys_table[i];
        while (c)
        {
            d = c;
            c = c->next;
            if (d->numchannels == 0)
            {
                del_comsys(d->who);
                continue;
            }
            if (isPlayer(d->who))
            {
                continue;
            }
            if (  God(Owner(d->who))
               && Going(d->who))
            {
                del_comsys(d->who);
                continue;
            }
        }
    }
}

void save_channels(FILE *fp)
{
    purge_comsystem();

    comsys_t *c;
    int i, j;
    int np = 0;
    for (i = 0; i < NUM_COMSYS; i++)
    {
        c = comsys_table[i];
        while (c)
        {
            np++;
            c = c->next;
        }
    }

    fprintf(fp, "%d\n", np);
    for (i = 0; i < NUM_COMSYS; i++)
    {
        c = comsys_table[i];
        while (c)
        {
            fprintf(fp, "%d %d\n", c->who, c->numchannels);
            for (j = 0; j < c->numchannels; j++)
            {
                fprintf(fp, "%s %s\n", c->alias + j * ALIAS_SIZE, c->channels[j]);
            }
            c = c->next;
        }
    }
}

comsys_t *create_new_comsys(void)
{
    comsys_t *c = (comsys_t *)MEMALLOC(sizeof(comsys_t));
    ISOUTOFMEMORY(c);

    c->who         = NOTHING;
    c->numchannels = 0;
    c->maxchannels = 0;
    c->alias       = NULL;
    c->channels    = NULL;
    c->next        = NULL;
    return c;
}

comsys_t *get_comsys(dbref which)
{
    if (which < 0)
    {
        return NULL;
    }

    comsys_t *c = comsys_table[which % NUM_COMSYS];

    while (c && (c->who != which))
        c = c->next;

    if (!c)
    {
        c = create_new_comsys();
        c->who = which;
        add_comsys(c);
    }
    return c;
}

void add_comsys(comsys_t *c)
{
    if (c->who < 0 || c->who >= mudstate.db_top)
    {
        Log.tinyprintf("add_comsys: dbref %d out of range [0, %d)" ENDLINE, c->who, mudstate.db_top);
        return;
    }

    c->next = comsys_table[c->who % NUM_COMSYS];
    comsys_table[c->who % NUM_COMSYS] = c;
}

void del_comsys(dbref who)
{
    if (who < 0 || who >= mudstate.db_top)
    {
        Log.tinyprintf("del_comsys: dbref %d out of range [0, %d)" ENDLINE, who, mudstate.db_top);
        return;
    }

    comsys_t *c = comsys_table[who % NUM_COMSYS];

    if (c == NULL)
    {
        return;
    }

    if (c->who == who)
    {
        comsys_table[who % NUM_COMSYS] = c->next;
        destroy_comsys(c);
        return;
    }
    comsys_t *last = c;
    c = c->next;
    while (c)
    {
        if (c->who == who)
        {
            last->next = c->next;
            destroy_comsys(c);
            return;
        }
        last = c;
        c = c->next;
    }
}

void destroy_comsys(comsys_t *c)
{
    int i;

    if (c->alias)
    {
        MEMFREE(c->alias);
        c->alias = NULL;
    }
    for (i = 0; i < c->numchannels; i++)
    {
        MEMFREE(c->channels[i]);
        c->channels[i] = NULL;
    }
    if (c->channels)
    {
        MEMFREE(c->channels);
        c->channels = NULL;
    }
    MEMFREE(c);
    c = NULL;
}

void sort_com_aliases(comsys_t *c)
{
    int i;
    char buffer[10];
    char *s;
    bool cont = true;

    while (cont)
    {
        cont = false;
        for (i = 0; i < c->numchannels - 1; i++)
        {
            if (strcmp(c->alias + i * ALIAS_SIZE, c->alias + (i + 1) * ALIAS_SIZE) > 0)
            {
                strcpy(buffer, c->alias + i * ALIAS_SIZE);
                strcpy(c->alias + i * ALIAS_SIZE, c->alias + (i + 1) * ALIAS_SIZE);
                strcpy(c->alias + (i + 1) * ALIAS_SIZE, buffer);
                s = c->channels[i];
                c->channels[i] = c->channels[i + 1];
                c->channels[i + 1] = s;
                cont = true;
            }
        }
    }
}

char *get_channel_from_alias(dbref player, char *alias)
{
    int first, last, current, dir;

    comsys_t *c = get_comsys(player);

    current = first = 0;
    last = c->numchannels - 1;
    dir = 1;

    while (dir && (first <= last))
    {
        current = (first + last) / 2;
        dir = strcmp(alias, c->alias + ALIAS_SIZE * current);
        if (dir < 0)
            last = current - 1;
        else
            first = current + 1;
    }

    if (!dir)
    {
        return c->channels[current];
    }
    else
    {
        return "";
    }
}

void load_comsystem(FILE *fp)
{
    int i, j, dummy;
    int ver = 0;
    struct channel *ch;
    char temp[LBUF_SIZE];

    num_channels = 0;

    int nc = 0;
    fgets(temp, sizeof(temp), fp);
    if (!strncmp(temp, "+V", 2))
    {
        // +V2 has colored headers
        //
        ver = mux_atol(temp + 2);
        if (ver < 1 || 3 < ver)
        {
            return;
        }
        int cc;
        cc = fscanf(fp, "%d\n", &nc);
        mux_assert(1 == cc);
    }
    else
    {
        nc = mux_atol(temp);
    }

    num_channels = nc;

    for (i = 0; i < nc; i++)
    {
        ch = (struct channel *)MEMALLOC(sizeof(struct channel));
        ISOUTOFMEMORY(ch);

        int nChannel = GetLineTrunc(temp, sizeof(temp), fp);
        if (nChannel > MAX_CHANNEL_LEN)
        {
            nChannel = MAX_CHANNEL_LEN;
        }
        if (temp[nChannel-1] == '\n')
        {
            // Get rid of trailing '\n'.
            //
            nChannel--;
        }
        memcpy(ch->name, temp, nChannel);
        ch->name[nChannel] = '\0';

        if (ver >= 2)
        {
            int nHeader = GetLineTrunc(temp, sizeof(temp), fp);
            if (nHeader > MAX_HEADER_LEN)
            {
                nHeader = MAX_HEADER_LEN;
            }
            if (temp[nHeader-1] == '\n')
            {
                nHeader--;
            }
            memcpy(ch->header, temp, nHeader);
            ch->header[nHeader] = '\0';
        }

        ch->on_users = NULL;

        hashaddLEN(ch->name, nChannel, ch, &mudstate.channel_htab);

        ch->type         = 127;
        ch->temp1        = 0;
        ch->temp2        = 0;
        ch->charge       = 0;
        ch->charge_who   = NOTHING;
        ch->amount_col   = 0;
        ch->num_messages = 0;
        ch->chan_obj     = NOTHING;

        int cc;
        if (ver >= 1)
        {
            cc = fscanf(fp, "%d %d %d %d %d %d %d %d\n",
                &(ch->type), &(ch->temp1), &(ch->temp2),
                &(ch->charge), &(ch->charge_who),
                &(ch->amount_col), &(ch->num_messages), &(ch->chan_obj));
            mux_assert(8 == cc);
        }
        else
        {
            cc = fscanf(fp, "%d %d %d %d %d %d %d %d %d %d\n",
                &(ch->type), &(dummy), &(ch->temp1), &(ch->temp2),
                &(dummy), &(ch->charge), &(ch->charge_who),
                &(ch->amount_col), &(ch->num_messages), &(ch->chan_obj));
            mux_assert(10 == cc);
        }

        if (ver <= 1)
        {
            // Build colored header if not +V2 or later db.
            //
            if (ch->type & CHANNEL_PUBLIC)
            {
                sprintf(temp, "%s[%s%s%s%s%s]%s", ANSI_CYAN, ANSI_HILITE,
                    ANSI_BLUE, ch->name, ANSI_NORMAL, ANSI_CYAN, ANSI_NORMAL);
            }
            else
            {
                sprintf(temp, "%s[%s%s%s%s%s]%s", ANSI_MAGENTA, ANSI_HILITE,
                    ANSI_RED, ch->name, ANSI_NORMAL, ANSI_MAGENTA,
                    ANSI_NORMAL);
            }
            int vwVisual;
            ANSI_TruncateToField(temp, MAX_HEADER_LEN+1, ch->header,
                MAX_HEADER_LEN+1, &vwVisual, ANSI_ENDGOAL_NORMAL);
        }

        ch->num_users = 0;
        cc =fscanf(fp, "%d\n", &(ch->num_users));
        mux_assert(1 == cc);
        ch->max_users = ch->num_users;
        if (ch->num_users > 0)
        {
            ch->users = (struct comuser **)calloc(ch->max_users, sizeof(struct comuser *));
            ISOUTOFMEMORY(ch->users);

            int jAdded = 0;
            for (j = 0; j < ch->num_users; j++)
            {
                struct comuser t_user;
                memset(&t_user, 0, sizeof(t_user));

                t_user.who = NOTHING;
                t_user.bUserIsOn = false;
                t_user.ComTitleStatus = false;

                int iUserIsOn;
                if (ver == 3)
                {
                    int iComTitleStatus;
                    cc = fscanf(fp, "%d %d %d\n", &(t_user.who), &iUserIsOn,
                        &iComTitleStatus);
                    mux_assert(3 == cc);
                    t_user.bUserIsOn = (iUserIsOn ? true : false);
                    t_user.ComTitleStatus = (iComTitleStatus ? true : false);
                }
                else
                {
                    t_user.ComTitleStatus = true;
                    if (ver)
                    {
                        cc = fscanf(fp, "%d %d\n", &(t_user.who), &iUserIsOn);
                        mux_assert(2 == cc);
                        t_user.bUserIsOn = (iUserIsOn ? true : false);
                    }
                    else
                    {
                        cc = fscanf(fp, "%d %d %d", &(t_user.who), &(dummy), &(dummy));
                        mux_assert(3 == cc);
                        cc = fscanf(fp, "%d\n", &iUserIsOn);
                        mux_assert(1 == cc);
                        t_user.bUserIsOn = (iUserIsOn ? true : false);
                    }
                }

                // Read Comtitle.
                //
                int nTitle = GetLineTrunc(temp, sizeof(temp), fp);
                char *pTitle = temp;

                if (!Good_dbref(t_user.who))
                {
                    Log.tinyprintf("load_comsystem: dbref %d out of range [0, %d)." ENDLINE, t_user.who, mudstate.db_top);
                }
                else if (isGarbage(t_user.who))
                {
                    Log.tinyprintf("load_comsystem: dbref is GARBAGE." ENDLINE, t_user.who);
                }
                else
                {
                    // Validate comtitle
                    //
                    if (3 < nTitle && temp[0] == 't' && temp[1] == ':')
                    {
                        pTitle = temp+2;
                        nTitle -= 2;
                        if (pTitle[nTitle-1] == '\n')
                        {
                            // Get rid of trailing '\n'.
                            //
                            nTitle--;
                        }
                        if (nTitle <= 0 || MAX_TITLE_LEN < nTitle)
                        {
                            nTitle = 0;
                            pTitle = temp;
                        }
                    }
                    else
                    {
                        nTitle = 0;
                    }

                    struct comuser *user = (struct comuser *)MEMALLOC(sizeof(struct comuser));
                    ISOUTOFMEMORY(user);
                    memcpy(user, &t_user, sizeof(struct comuser));

                    user->title = StringCloneLen(pTitle, nTitle);
                    ch->users[jAdded++] = user;

                    if (  !(isPlayer(user->who))
                       && !(Going(user->who)
                       && (God(Owner(user->who)))))
                    {
                        do_joinchannel(user->who, ch);
                    }
                    user->on_next = ch->on_users;
                    ch->on_users = user;
                }
            }
            ch->num_users = jAdded;
            sort_users(ch);
        }
        else
        {
            ch->users = NULL;
        }
    }
}

void save_comsystem(FILE *fp)
{
    struct channel *ch;
    struct comuser *user;
    int j;

    fprintf(fp, "+V3\n");
    fprintf(fp, "%d\n", num_channels);
    for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         ch;
         ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        fprintf(fp, "%s\n", ch->name);
        fprintf(fp, "%s\n", ch->header);

        fprintf(fp, "%d %d %d %d %d %d %d %d\n", ch->type, ch->temp1,
            ch->temp2, ch->charge, ch->charge_who, ch->amount_col,
            ch->num_messages, ch->chan_obj);

        // Count the number of 'valid' users to dump.
        //
        int nUsers = 0;
        for (j = 0; j < ch->num_users; j++)
        {
            user = ch->users[j];
            if (user->who >= 0 && user->who < mudstate.db_top)
            {
                nUsers++;
            }
        }

        fprintf(fp, "%d\n", nUsers);
        for (j = 0; j < ch->num_users; j++)
        {
            user = ch->users[j];
            if (user->who >= 0 && user->who < mudstate.db_top)
            {
                user = ch->users[j];
                fprintf(fp, "%d %d %d\n", user->who, user->bUserIsOn, user->ComTitleStatus);
                if (user->title[0] != '\0')
                {
                    fprintf(fp, "t:%s\n", user->title);
                }
                else
                {
                    fprintf(fp, "t:\n");
                }
            }
        }
    }
}

void BuildChannelMessage
(
    bool bSpoof,
    const char *pHeader,
    struct comuser *user,
    char *pPose,
    char **messNormal,
    char **messNoComtitle
)
{
    // Allocate necessary buffers.
    //
    *messNormal = alloc_lbuf("BCM.messNormal");
    *messNoComtitle = NULL;
    if (!bSpoof)
    {
        *messNoComtitle = alloc_lbuf("BCM.messNoComtitle");
    }

    // Comtitle Check
    //
    bool hasComTitle = (user->title[0] != '\0');

    char *mnptr  = *messNormal;     // Message without comtitle removal
    char *mncptr = *messNoComtitle; // Message with comtitle removal

    safe_str(pHeader, *messNormal, &mnptr);
    safe_chr(' ', *messNormal, &mnptr);
    if (!bSpoof)
    {
        safe_str(pHeader, *messNoComtitle, &mncptr);
        safe_chr(' ', *messNoComtitle, &mncptr);
    }

    // Don't evaluate a title if there isn't one to parse or evaluation of
    // comtitles is disabled.
    // If they're set spoof, ComTitleStatus doesn't matter.
    if (hasComTitle && (user->ComTitleStatus || bSpoof))
    {
        if (mudconf.eval_comtitle)
        {
            // Evaluate the comtitle as code.
            //
            char TempToEval[LBUF_SIZE];
            strcpy(TempToEval, user->title);
            char *q = TempToEval;
            mux_exec(*messNormal, &mnptr, user->who, user->who, user->who,
                EV_FCHECK | EV_EVAL | EV_TOP, &q, (char **)NULL, 0);
        }
        else
        {
            safe_str(user->title, *messNormal, &mnptr);
        }
        if (!bSpoof)
        {
            safe_chr(' ', *messNormal, &mnptr);
            safe_str(Moniker(user->who), *messNormal, &mnptr);
            safe_str(Moniker(user->who), *messNoComtitle, &mncptr);
        }
    }
    else
    {
        safe_str(Moniker(user->who), *messNormal, &mnptr);
        if (!bSpoof)
        {
            safe_str(Moniker(user->who), *messNoComtitle, &mncptr);
        }
    }

    char *saystring = NULL;
    char *newPose = NULL;

    switch(pPose[0])
    {
    case ':':
        pPose++;
        newPose = modSpeech(user->who, pPose, true, "channel/pose");
        if (newPose)
        {
            pPose = newPose;
        }
        safe_chr(' ', *messNormal, &mnptr);
        safe_str(pPose, *messNormal, &mnptr);
        if (!bSpoof)
        {
            safe_chr(' ', *messNoComtitle, &mncptr);
            safe_str(pPose, *messNoComtitle, &mncptr);
        }
        break;

    case ';':
        pPose++;
        newPose = modSpeech(user->who, pPose, true, "channel/pose");
        if (newPose)
        {
            pPose = newPose;
        }
        safe_str(pPose, *messNormal, &mnptr);
        if (!bSpoof)
        {
            safe_str(pPose, *messNoComtitle, &mncptr);
        }
        break;

    default:
        newPose = modSpeech(user->who, pPose, true, "channel");
        if (newPose)
        {
            pPose = newPose;
        }
        saystring = modSpeech(user->who, pPose, false, "channel");
        if (saystring)
        {
            safe_chr(' ', *messNormal, &mnptr);
            safe_str(saystring, *messNormal, &mnptr);
            safe_str(" \"", *messNormal, &mnptr);
        }
        else
        {
            safe_str(" says, \"", *messNormal, &mnptr);
        }
        safe_str(pPose, *messNormal, &mnptr);
        safe_chr('"', *messNormal, &mnptr);
        if (!bSpoof)
        {
            if (saystring)
            {
                safe_chr(' ', *messNoComtitle, &mncptr);
                safe_str(saystring, *messNoComtitle, &mncptr);
                safe_str(" \"", *messNoComtitle, &mncptr);
            }
            else
            {
                safe_str(" says, \"", *messNoComtitle, &mncptr);
            }
            safe_str(pPose, *messNoComtitle, &mncptr);
            safe_chr('"', *messNoComtitle, &mncptr);
        }
        break;
    }
    *mnptr = '\0';
    if (!bSpoof)
    {
        *mncptr = '\0';
    }
    if (newPose)
    {
        free_lbuf(newPose);
    }
    if (saystring)
    {
        free_lbuf(saystring);
    }
}

void do_processcom(dbref player, char *arg1, char *arg2)
{
    if (!*arg2)
    {
        raw_notify(player, "No message.");
        return;
    }
    if (3500 < strlen(arg2))
    {
        arg2[3500] = '\0';
    }
    struct channel *ch = select_channel(arg1);
    if (!ch)
    {
        raw_notify(player, tprintf("Unknown channel %s.", arg1));
        return;
    }
    struct comuser *user = select_user(ch, player);
    if (!user)
    {
        raw_notify(player, "You are not listed as on that channel.  Delete this alias and readd.");
        return;
    }
    if (  Gagged(player)
       && !Wizard(player))
    {
        raw_notify(player, "GAGGED players may not speak on channels.");
        return;
    }
    if (!strcmp(arg2, "on"))
    {
        do_joinchannel(player, ch);
    }
    else if (!strcmp(arg2, "off"))
    {
        do_leavechannel(player, ch);
    }
    else if (!user->bUserIsOn)
    {
        raw_notify(player, tprintf("You must be on %s to do that.", arg1));
        return;
    }
    else if (!strcmp(arg2, "who"))
    {
        do_comwho(player, ch);
    }
    else if (  !strncmp(arg2, "last", 4)
            && (  arg2[4] == '\0'
               || (  arg2[4] == ' '
                  && is_integer(arg2 + 5, NULL))))
    {
        // Parse optional number after the 'last' command.
        //
        int nRecall = DFLT_RECALL_REQUEST;
        if (arg2[4] == ' ')
        {
            nRecall = mux_atol(arg2 + 5);
        }
        do_comlast(player, ch, nRecall);
    }
    else if (!test_transmit_access(player, ch))
    {
        raw_notify(player, "That channel type cannot be transmitted on.");
        return;
    }
    else
    {
        if (!payfor(player, Guest(player) ? 0 : ch->charge))
        {
            notify(player, tprintf("You don't have enough %s.", mudconf.many_coins));
            return;
        }
        else
        {
            ch->amount_col += ch->charge;
            giveto(ch->charge_who, ch->charge);
        }

        // BuildChannelMessage allocates messNormal and messNoComtitle,
        // SendChannelMessage frees them.
        //
        char *messNormal;
        char *messNoComtitle;
        BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0, ch->header, user,
            arg2, &messNormal, &messNoComtitle);
        SendChannelMessage(player, ch, messNormal, messNoComtitle);
    }
}

void SendChannelMessage
(
    dbref executor,
    struct channel *ch,
    char *msgNormal,
    char *msgNoComtitle
)
{
    bool bSpoof = ((ch->type & CHANNEL_SPOOF) != 0);
    ch->num_messages++;

    struct comuser *user;
    for (user = ch->on_users; user; user = user->on_next)
    {
        if (  user->bUserIsOn
           && test_receive_access(user->who, ch))
        {
            if (  user->ComTitleStatus
               || bSpoof
               || msgNoComtitle == NULL)
            {
                notify_with_cause_ooc(user->who, executor, msgNormal);
            }
            else
            {
                notify_with_cause_ooc(user->who, executor, msgNoComtitle);
            }
        }
    }

    dbref obj = ch->chan_obj;
    if (Good_obj(obj))
    {
        dbref aowner;
        int aflags;
        int logmax = DFLT_MAX_LOG;
        char *maxbuf;
        ATTR *pattr;
        if (  (pattr = atr_str("MAX_LOG"))
           && pattr->number)
        {
            maxbuf = atr_get(obj, pattr->number, &aowner, &aflags);
            logmax = mux_atol(maxbuf);
            free_lbuf(maxbuf);
        }
        if (logmax > 0)
        {
            if (logmax > MAX_RECALL_REQUEST)
            {
                logmax = MAX_RECALL_REQUEST;
                atr_add(ch->chan_obj, pattr->number, mux_ltoa_t(logmax), GOD,
                    AF_CONST|AF_NOPROG|AF_NOPARSE);
            }
            char *p = tprintf("HISTORY_%d", iMod(ch->num_messages, logmax));
            int atr = mkattr(GOD, p);
            if (0 < atr)
            {
                atr_add(ch->chan_obj, atr, msgNormal, GOD, AF_CONST|AF_NOPROG|AF_NOPARSE);
            }
        }
    }
    else if (ch->chan_obj != NOTHING)
    {
        ch->chan_obj = NOTHING;
    }

    // Since msgNormal and msgNoComTitle are no longer needed, free them here.
    //
    if (msgNormal)
    {
        free_lbuf(msgNormal);
    }
    if (  msgNoComtitle
       && msgNoComtitle != msgNormal)
    {
        free_lbuf(msgNoComtitle);
    }
}

void do_joinchannel(dbref player, struct channel *ch)
{
    struct comuser **cu;
    int i;

    struct comuser *user = select_user(ch, player);

    if (!user)
    {
        ch->num_users++;
        if (ch->num_users >= ch->max_users)
        {
            ch->max_users += 10;
            cu = (struct comuser **)MEMALLOC(sizeof(struct comuser *) * ch->max_users);
            ISOUTOFMEMORY(cu);

            for (i = 0; i < (ch->num_users - 1); i++)
            {
                cu[i] = ch->users[i];
            }
            MEMFREE(ch->users);
            ch->users = cu;
        }
        user = (struct comuser *)MEMALLOC(sizeof(struct comuser));
        ISOUTOFMEMORY(user);

        for (i = ch->num_users - 1; i > 0 && ch->users[i - 1]->who > player; i--)
        {
            ch->users[i] = ch->users[i - 1];
        }
        ch->users[i] = user;

        user->who            = player;
        user->bUserIsOn      = true;
        user->ComTitleStatus = true;
        user->title          = StringClone("");

        // if (Connected(player))&&(isPlayer(player))
        //
        if (UNDEAD(player))
        {
            user->on_next = ch->on_users;
            ch->on_users  = user;
        }
    }
    else if (!user->bUserIsOn)
    {
        user->bUserIsOn = true;
    }
    else
    {
        raw_notify(player, tprintf("You are already on channel %s.", ch->name));
        return;
    }

    if (!Hidden(player))
    {
        char *messNormal, *messNoComtitle;
        BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0, ch->header, user,
            ":has joined this channel.", &messNormal, &messNoComtitle);
        SendChannelMessage(player, ch, messNormal, messNoComtitle);
    }
}

void do_leavechannel(dbref player, struct channel *ch)
{
    struct comuser *user = select_user(ch, player);
    raw_notify(player, tprintf("You have left channel %s.", ch->name));
    if (  user->bUserIsOn
       && !Hidden(player))
    {
        char *messNormal, *messNoComtitle;
        BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0, ch->header, user,
            ":has left this channel.", &messNormal, &messNoComtitle);
        SendChannelMessage(player, ch, messNormal, messNoComtitle);
    }
    user->bUserIsOn = false;
}

void do_comwho_line
(
    dbref player,
    struct channel *ch,
    struct comuser *user
)
{
    char *msg;
    char *buff = NULL;

    if (user->title[0] != '\0')
    {
        // There is a comtitle
        //
        if (Staff(player))
        {
            buff = unparse_object(player, user->who, false);
            if (ch->type & CHANNEL_SPOOF)
            {
                msg = tprintf("%s as %s", buff, user->title);
            }
            else
            {
                msg = tprintf("%s as %s %s", buff, user->title, buff);
            }
        }
        else
        {
            if (ch->type & CHANNEL_SPOOF)
            {
                msg = user->title;
            }
            else
            {
                buff = unparse_object(player, user->who, false);
                msg = tprintf("%s %s", user->title, buff);
            }
        }
    }
    else
    {
        buff = unparse_object(player, user->who, false);
        msg = buff;
    }

    raw_notify(player, msg);
    if (buff)
    {
        free_lbuf(buff);
    }
}

void do_comwho(dbref player, struct channel *ch)
{
    struct comuser *user;

    raw_notify(player, "-- Players --");
    for (user = ch->on_users; user; user = user->on_next)
    {
        if (isPlayer(user->who))
        {
            if (  Connected(user->who)
               && (  !Hidden(user->who)
                  || Wizard_Who(player)
                  || See_Hidden(player)))
            {
                if (user->bUserIsOn)
                {
                    do_comwho_line(player, ch, user);
                }
            }
            else if (!Hidden(user->who))
            {
                do_comdisconnectchannel(user->who, ch->name);
            }
        }
    }
    raw_notify(player, "-- Objects --");
    for (user = ch->on_users; user; user = user->on_next)
    {
        if (!isPlayer(user->who))
        {
            if (  Going(user->who)
               && God(Owner(user->who)))
            {
                do_comdisconnectchannel(user->who, ch->name);
            }
            else if (user->bUserIsOn)
            {
                do_comwho_line(player, ch, user);
            }
        }
    }
    raw_notify(player, tprintf("-- %s --", ch->name));
}

void do_comlast(dbref player, struct channel *ch, int arg)
{
    if (!Good_obj(ch->chan_obj))
    {
        raw_notify(player, "Channel does not have an object.");
        return;
    }
    dbref aowner;
    int aflags;
    dbref obj = ch->chan_obj;
    int logmax = MAX_RECALL_REQUEST;
    ATTR *pattr;
    if (  (pattr = atr_str("MAX_LOG"))
       && (atr_get_info(obj, pattr->number, &aowner, &aflags)))
    {
        char *maxbuf = atr_get(obj, pattr->number, &aowner, &aflags);
        logmax = mux_atol(maxbuf);
        free_lbuf(maxbuf);
    }
    if (logmax < 1)
    {
        raw_notify(player, "Channel does not log.");
        return;
    }
    if (arg < MIN_RECALL_REQUEST)
    {
        arg = MIN_RECALL_REQUEST;
    }
    if (arg > logmax)
    {
        arg = logmax;
    }

    char *message;
    int histnum = ch->num_messages - arg;

    raw_notify(player, "-- Begin Comsys Recall --");
    for (int count = 0; count < arg; count++)
    {
        histnum++;
        pattr = atr_str(tprintf("HISTORY_%d", iMod(histnum, logmax)));
        if (pattr)
        {
            message = atr_get(obj, pattr->number, &aowner, &aflags);
            raw_notify(player, message);
            free_lbuf(message);
        }
    }
    raw_notify(player, "-- End Comsys Recall --");
}

bool do_chanlog(dbref player, char *channel, char *arg)
{
    int value;
    if (  !*arg
       || !is_integer(arg, NULL)
       || (value = mux_atol(arg)) > MAX_RECALL_REQUEST)
    {
        return false;
    }
    if (value < 0)
    {
        value = 0;
    }
    struct channel *ch = select_channel(channel);
    if (!Good_obj(ch->chan_obj))
    {
        // No channel object has been set.
        //
        return false;
    }
    int atr = mkattr(GOD, "MAX_LOG");
    if (atr <= 0)
    {
        return false;
    }
    dbref aowner;
    int aflags;
    char *oldvalue = atr_get(ch->chan_obj, atr, &aowner, &aflags);
    if (oldvalue)
    {
        int oldnum = mux_atol(oldvalue);
        if (oldnum > value)
        {
            ATTR *hist;
            for (int count = 0; count <= oldnum; count++)
            {
                hist = atr_str(tprintf("HISTORY_%d", count));
                if (hist)
                {
                    atr_clr(ch->chan_obj, hist->number);
                }
            }
        }
        free_lbuf(oldvalue);
    }
    atr_add(ch->chan_obj, atr, mux_ltoa_t(value), GOD,
        AF_CONST|AF_NOPROG|AF_NOPARSE);
    return true;
}

struct channel *select_channel(char *channel)
{
    struct channel *cp = (struct channel *)hashfindLEN(channel,
        strlen(channel), &mudstate.channel_htab);
    return cp;
}

struct comuser *select_user(struct channel *ch, dbref player)
{
    if (!ch)
    {
        return NULL;
    }

    int first = 0;
    int last = ch->num_users - 1;
    int dir = 1;
    int current = 0;

    while (dir && (first <= last))
    {
        current = (first + last) / 2;
        if (ch->users[current] == NULL)
        {
            last--;
            continue;
        }
        if (ch->users[current]->who == player)
        {
            dir = 0;
        }
        else if (ch->users[current]->who < player)
        {
            dir = 1;
            first = current + 1;
        }
        else
        {
            dir = -1;
            last = current - 1;
        }
    }

    if (!dir)
    {
        return ch->users[current];
    }
    else
    {
        return NULL;
    }
}

#define MAX_ALIASES_PER_PLAYER 50

void do_addcom
(
    dbref executor,
    dbref caller,
    dbref enactor,
    int   key,
    int   nargs,
    char *arg1,
    char *arg2
)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    bool bValidAlias;
    int  nValidAlias;
    char *pValidAlias = MakeCanonicalComAlias(arg1, &nValidAlias, &bValidAlias);
    if (!bValidAlias)
    {
        raw_notify(executor, "You need to specify a valid alias.");
        return;
    }
    char *s = arg2;
    if (!*s)
    {
        raw_notify(executor, "You need to specify a channel.");
        return;
    }
    char channel[MAX_CHANNEL_LEN+1];
    char *t = channel;
    while (*s && ((t - channel) < MAX_CHANNEL_LEN))
    {
        if (*s != ' ')
            *t++ = *s++;
        else
            s++;
    }
    *t = '\0';

    int i, j, where;
    char *na;
    char **nc;
    struct channel *ch = select_channel(channel);
    char Buffer[MAX_CHANNEL_LEN+1];
    if (!ch)
    {
        int nVisualWidth;
        ANSI_TruncateToField(channel, sizeof(Buffer), Buffer, sizeof(Buffer), &nVisualWidth, ANSI_ENDGOAL_NORMAL);
        raw_notify(executor, tprintf("Channel %s does not exist yet.", Buffer));
        return;
    }
    if (!test_join_access(executor, ch))
    {
        raw_notify(executor, "Sorry, this channel type does not allow you to join.");
        return;
    }
    comsys_t *c = get_comsys(executor);
    if (c->numchannels >= MAX_ALIASES_PER_PLAYER)
    {
        raw_notify(executor, tprintf("Sorry, but you have reached the maximum number of aliases allowed."));
        return;
    }
    for (j = 0; j < c->numchannels && (strcmp(pValidAlias, c->alias + j * ALIAS_SIZE) > 0); j++)
    {
        ; // Nothing.
    }
    if (j < c->numchannels && !strcmp(pValidAlias, c->alias + j * ALIAS_SIZE))
    {
        char *p = tprintf("That alias is already in use for channel %s.", c->channels[j]);
        raw_notify(executor, p);
        return;
    }
    if (c->numchannels >= c->maxchannels)
    {
        c->maxchannels += 10;

        na = (char *)MEMALLOC(ALIAS_SIZE * c->maxchannels);
        ISOUTOFMEMORY(na);
        nc = (char **)MEMALLOC(sizeof(char *) * c->maxchannels);
        ISOUTOFMEMORY(nc);

        for (i = 0; i < c->numchannels; i++)
        {
            strcpy(na + i * ALIAS_SIZE, c->alias + i * ALIAS_SIZE);
            nc[i] = c->channels[i];
        }
        if (c->alias)
        {
            MEMFREE(c->alias);
            c->alias = NULL;
        }
        if (c->channels)
        {
            MEMFREE(c->channels);
            c->channels = NULL;
        }
        c->alias = na;
        c->channels = nc;
    }
    where = c->numchannels++;
    for (i = where; i > j; i--)
    {
        strcpy(c->alias + i * ALIAS_SIZE, c->alias + (i - 1) * ALIAS_SIZE);
        c->channels[i] = c->channels[i - 1];
    }

    where = j;
    memcpy(c->alias + where * ALIAS_SIZE, pValidAlias, nValidAlias);
    *(c->alias + where * ALIAS_SIZE + nValidAlias) = '\0';
    c->channels[where] = StringClone(channel);

    if (!select_user(ch, executor))
    {
        do_joinchannel(executor, ch);
    }

    raw_notify(executor, tprintf("Channel %s added with alias %s.", channel, pValidAlias));
}

void do_delcom(dbref executor, dbref caller, dbref enactor, int key, char *arg1)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    if (!arg1)
    {
        raw_notify(executor, "Need an alias to delete.");
        return;
    }
    comsys_t *c = get_comsys(executor);
    int i;

    for (i = 0; i < c->numchannels; i++)
    {
        if (!strcmp(arg1, c->alias + i * ALIAS_SIZE))
        {
            int itmp, found=0;
            for (itmp = 0;itmp < c->numchannels; itmp++)
            {
                if (!strcmp(c->channels[itmp],c->channels[i]))
                {
                    found++;
                }
            }

            // If we found no other channels, delete it
            //
            if (found <= 1)
            {
                do_delcomchannel(executor, c->channels[i], false);
                raw_notify(executor, tprintf("Channel %s deleted.", c->channels[i]));
                MEMFREE(c->channels[i]);
            }
            else
            {
                raw_notify(executor, tprintf("Alias for channel %s deleted.",
                                           c->channels[i]));
            }

            c->channels[i]=NULL;
            c->numchannels--;

            for (; i < c->numchannels; i++)
            {
                strcpy(c->alias + i * ALIAS_SIZE, c->alias + (i + 1) * ALIAS_SIZE);
                c->channels[i] = c->channels[i + 1];
            }
            return;
        }
    }
    raw_notify(executor, "Unable to find that alias.");
}

void do_delcomchannel(dbref player, char *channel, bool bQuiet)
{
    struct comuser *user;

    struct channel *ch = select_channel(channel);
    if (!ch)
    {
        raw_notify(player, tprintf("Unknown channel %s.", channel));
    }
    else
    {
        int i;
        int j = 0;
        for (i = 0; i < ch->num_users && !j; i++)
        {
            user = ch->users[i];
            if (user->who == player)
            {
                do_comdisconnectchannel(player, channel);
                if (!bQuiet)
                {
                    if (  user->bUserIsOn
                       && !Hidden(player))
                    {
                        char *messNormal, *messNoComtitle;
                        BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0,
                                            ch->header, user, ":has left this channel.",
                                            &messNormal, &messNoComtitle);
                        SendChannelMessage(player, ch, messNormal, messNoComtitle);
                    }
                    raw_notify(player, tprintf("You have left channel %s.", channel));
                }

                if (user->title)
                {
                    MEMFREE(user->title);
                    user->title = NULL;
                }
                MEMFREE(user);
                user = NULL;
                j = 1;
            }
        }

        if (j)
        {
            ch->num_users--;
            for (i--; i < ch->num_users; i++)
            {
                ch->users[i] = ch->users[i + 1];
            }
        }
    }
}

void do_createchannel(dbref executor, dbref caller, dbref enactor, int key, char *channel)
{
    if (select_channel(channel))
    {
        raw_notify(executor, tprintf("Channel %s already exists.", channel));
        return;
    }
    if (!*channel)
    {
        raw_notify(executor, "You must specify a channel to create.");
        return;
    }
    if (!Comm_All(executor))
    {
        raw_notify(executor, NOPERM_MESSAGE);
        return;
    }
    struct channel *newchannel = (struct channel *)MEMALLOC(sizeof(struct channel));
    ISOUTOFMEMORY(newchannel);

    int   vwChannel;
    size_t nNameNoANSI;
    char *pNameNoANSI;
    char Buffer[MAX_HEADER_LEN];
    int nChannel = ANSI_TruncateToField(channel, sizeof(Buffer),
        Buffer, sizeof(Buffer), &vwChannel, ANSI_ENDGOAL_NORMAL);
    if (nChannel == vwChannel)
    {
        // The channel name does not contain ANSI, so first, we add some to
        // get the header.
        //
        const int nMax = MAX_HEADER_LEN - (sizeof(ANSI_HILITE)-1)
                       - (sizeof(ANSI_NORMAL)-1) - 2;
        if (nChannel > nMax)
        {
            nChannel = nMax;
        }
        Buffer[nChannel] = '\0';
        sprintf(newchannel->header, "%s[%s]%s", ANSI_HILITE, Buffer,
            ANSI_NORMAL);

        // Then, we use the non-ANSI part for the name.
        //
        nNameNoANSI = nChannel;
        pNameNoANSI = Buffer;
    }
    else
    {
        // The given channel name does contain ANSI.
        //
        memcpy(newchannel->header, Buffer, nChannel+1);
        pNameNoANSI = strip_ansi(Buffer, &nNameNoANSI);
    }
    if (nNameNoANSI > MAX_CHANNEL_LEN)
    {
        nNameNoANSI = MAX_CHANNEL_LEN;
    }
    memcpy(newchannel->name, pNameNoANSI, nNameNoANSI);
    newchannel->name[nNameNoANSI] = '\0';

    newchannel->type = 127;
    newchannel->temp1 = 0;
    newchannel->temp2 = 0;
    newchannel->charge = 0;
    newchannel->charge_who = executor;
    newchannel->amount_col = 0;
    newchannel->num_users = 0;
    newchannel->max_users = 0;
    newchannel->users = NULL;
    newchannel->on_users = NULL;
    newchannel->chan_obj = NOTHING;
    newchannel->num_messages = 0;

    num_channels++;

    hashaddLEN(newchannel->name, strlen(newchannel->name), newchannel, &mudstate.channel_htab);

    // Report the channel creation using non-ANSI name.
    //
    raw_notify(executor, tprintf("Channel %s created.", newchannel->name));
}

void do_destroychannel(dbref executor, dbref caller, dbref enactor, int key, char *channel)
{
    struct channel *ch;
    int j;

    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    ch = (struct channel *)hashfindLEN(channel, strlen(channel), &mudstate.channel_htab);

    if (!ch)
    {
        raw_notify(executor, tprintf("Could not find channel %s.", channel));
        return;
    }
    else if (  !Comm_All(executor)
            && !Controls(executor, ch->charge_who))
    {
        raw_notify(executor, NOPERM_MESSAGE);
        return;
    }
    num_channels--;
    hashdeleteLEN(channel, strlen(channel), &mudstate.channel_htab);

    for (j = 0; j < ch->num_users; j++)
    {
        MEMFREE(ch->users[j]);
        ch->users[j] = NULL;
    }
    MEMFREE(ch->users);
    ch->users = NULL;
    MEMFREE(ch);
    ch = NULL;
    raw_notify(executor, tprintf("Channel %s destroyed.", channel));
}

#if 0
void do_cleanupchannels(void)
{
    struct channel *ch;
    for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         ch; ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        struct comuser *user, *prevuser = NULL;
        for (user = ch->on_users; user; )
        {
            if (isPlayer(user->who))
            {
                if (!test_join_access(user->who, ch))
                //if (!Connected(user->who))
                {
                    // Go looking for user in the array.
                    //
                    bool bFound = false;
                    int iPos;
                    for (iPos = 0; iPos < ch->num_users && !bFound; iPos++)
                    {
                        if (ch->users[iPos] == user)
                        {
                            bFound = true;
                        }
                    }

                    if (bFound)
                    {
                        // Remove user from the array.
                        //
                        ch->num_users--;
                        for (iPos--; iPos < ch->num_users; iPos++)
                        {
                            ch->users[iPos] = ch->users[iPos+1];
                        }

                        // Save user pointer for later reporting and freeing.
                        //
                        struct comuser *cuVictim = user;

                        // Unlink user from the list, and decide who to look at next.
                        //
                        if (prevuser)
                        {
                            prevuser->on_next = user->on_next;
                        }
                        else
                        {
                            ch->on_users = user->on_next;
                        }
                        user = user->on_next;

                        // Reporting
                        //
                        if (!Hidden(cuVictim->who))
                        {
                            char *mess = StartBuildChannelMessage(cuVictim->who,
                                (ch->type & CHANNEL_SPOOF) != 0, ch->header, cuVictim->title,
                                Moniker(cuVictim->who), ":is booted off the channel by the system.");
                            do_comsend(ch, mess);
                            EndBuildChannelMessage(mess);
                        }
                        raw_notify(cuVictim->who, tprintf("The system has booted you off channel %s.", ch->name));

                        // Freeing
                        //
                        if (cuVictim->title)
                        {
                            MEMFREE(cuVictim->title);
                            cuVictim->title = NULL;
                        }
                        MEMFREE(cuVictim);
                        cuVictim = NULL;

                        continue;
                    }
                }
            }

            prevuser = user;
            user = user->on_next;
        }
    }
}
#endif

void do_listchannels(dbref player)
{
    struct channel *ch;
    char temp[LBUF_SIZE];

    bool perm = Comm_All(player);
    if (!perm)
    {
        raw_notify(player, "Warning: Only public channels and your channels will be shown.");
    }
    raw_notify(player, "*** Channel      --Flags--  Obj   Own   Charge  Balance  Users   Messages");

    for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         ch; ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        if (  perm
           || (ch->type & CHANNEL_PUBLIC)
           || Controls(player, ch->charge_who))
        {
            sprintf(temp, "%c%c%c %-13.13s %c%c%c/%c%c%c %5d %5d %8d %8d %6d %10d",
                (ch->type & CHANNEL_PUBLIC) ? 'P' : '-',
                (ch->type & CHANNEL_LOUD) ? 'L' : '-',
                (ch->type & CHANNEL_SPOOF) ? 'S' : '-',
                ch->name,
                (ch->type & CHANNEL_PLAYER_JOIN) ? 'J' : '-',
                (ch->type & CHANNEL_PLAYER_TRANSMIT) ? 'X' : '-',
                (ch->type & CHANNEL_PLAYER_RECEIVE) ? 'R' : '-',
                (ch->type & CHANNEL_OBJECT_JOIN) ? 'j' : '-',
                (ch->type & CHANNEL_OBJECT_TRANSMIT) ? 'x' : '-',
                (ch->type & CHANNEL_OBJECT_RECEIVE) ? 'r' : '-',
                (ch->chan_obj != NOTHING) ? ch->chan_obj : -1,
                ch->charge_who, ch->charge, ch->amount_col, ch->num_users, ch->num_messages);
            raw_notify(player, temp);
        }
    }
    raw_notify(player, "-- End of list of Channels --");
}

void do_comtitle
(
    dbref executor,
    dbref caller,
    dbref enactor,
    int   key,
    int   nargs,
    char *arg1,
    char *arg2
)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    if (!*arg1)
    {
        raw_notify(executor, "Need an alias to do comtitle.");
        return;
    }

    char channel[MAX_CHANNEL_LEN+1];
    strcpy(channel, get_channel_from_alias(executor, arg1));

    if (channel[0] == '\0')
    {
        raw_notify(executor, "Unknown alias.");
        return;
    }
    struct channel *ch = select_channel(channel);
    if (ch)
    {
        if (select_user(ch, executor))
        {
            if (key == COMTITLE_OFF)
            {
                if ((ch->type & CHANNEL_SPOOF) == 0)
                {
                    raw_notify(executor, tprintf("Comtitles are now off for channel %s", channel));
                    do_setcomtitlestatus(executor, ch, false);
                }
                else
                {
                    raw_notify(executor, "You can not turn off comtitles on that channel.");
                }
            }
            else if (key == COMTITLE_ON)
            {
                raw_notify(executor, tprintf("Comtitles are now on for channel %s", channel));
                do_setcomtitlestatus(executor, ch, true);
            }
            else
            {
                char *pValidatedTitleValue = RestrictTitleValue(arg2);
                do_setnewtitle(executor, ch, pValidatedTitleValue);
                raw_notify(executor, tprintf("Title set to '%s' on channel %s.",
                    pValidatedTitleValue, channel));
            }
        }
    }
    else
    {
        raw_notify(executor, "Illegal comsys alias, please delete.");
    }
}

void do_comlist
(
    dbref executor,
    dbref caller,
    dbref enactor,
    int key,
    char* pattern
)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }

    bool bWild;
    if (  NULL != pattern
       && '\0' != *pattern)
    {
        bWild = true;
    }
    else
    {
        bWild = false;
    }

    raw_notify(executor, "Alias     Channel            Status   Title");

    comsys_t *c = get_comsys(executor);
    int i;
    for (i = 0; i < c->numchannels; i++)
    {
        struct comuser *user = select_user(select_channel(c->channels[i]), executor);
        if (user)
        {
            if (  !bWild
               || quick_wild(pattern,c->channels[i]))
            {
                char *p =
                    tprintf("%-9.9s %-18.18s %s %s %s",
                        c->alias + i * ALIAS_SIZE,
                        c->channels[i],
                        (user->bUserIsOn ? "on " : "off"),
                        (user->ComTitleStatus ? "con " : "coff"),
                        user->title);
                raw_notify(executor, p);
            }
        }
        else
        {
            raw_notify(executor, tprintf("Bad Comsys Alias: %s for Channel: %s", c->alias + i * ALIAS_SIZE, c->channels[i]));
        }
    }
    raw_notify(executor, "-- End of comlist --");
}

void do_channelnuke(dbref player)
{
    struct channel *ch;
    int j;

    for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         ch; ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        if (player == ch->charge_who)
        {
            num_channels--;
            hashdeleteLEN(ch->name, strlen(ch->name), &mudstate.channel_htab);

            for (j = 0; j < ch->num_users; j++)
            {
                MEMFREE(ch->users[j]);
                ch->users[j] = NULL;
            }
            MEMFREE(ch->users);
            ch->users = NULL;
            MEMFREE(ch);
            ch = NULL;
        }
    }
}

void do_clearcom(dbref executor, dbref caller, dbref enactor, int unused2)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    comsys_t *c = get_comsys(executor);

    int i;
    for (i = (c->numchannels) - 1; i > -1; --i)
    {
        do_delcom(executor, caller, enactor, 0, c->alias + i * ALIAS_SIZE);
    }
}

void do_allcom(dbref executor, dbref caller, dbref enactor, int key, char *arg1)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    if (  strcmp(arg1, "who") != 0
       && strcmp(arg1, "on")  != 0
       && strcmp(arg1, "off") != 0)
    {
        raw_notify(executor, "Only options available are: on, off and who.");
        return;
    }

    comsys_t *c = get_comsys(executor);
    int i;
    for (i = 0; i < c->numchannels; i++)
    {
        do_processcom(executor, c->channels[i], arg1);
        if (strcmp(arg1, "who") == 0)
        {
            raw_notify(executor, "");
        }
    }
}

void sort_users(struct channel *ch)
{
    int i;
    bool done = false;
    struct comuser *user;
    int nu = ch->num_users;

    while (!done)
    {
        done = true;
        for (i = 0; i < (nu - 1); i++)
        {
            if (ch->users[i]->who > ch->users[i + 1]->who)
            {
                user = ch->users[i];
                ch->users[i] = ch->users[i + 1];
                ch->users[i + 1] = user;
                done = false;
            }
        }
    }
}

void do_channelwho(dbref executor, dbref caller, dbref enactor, int key, char *arg1)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }

    char channel[MAX_CHANNEL_LEN+1];
    char *s = arg1;
    char *t = channel;
    while (*s && *s != '/' && ((t - channel) < MAX_CHANNEL_LEN))
    {
        *t++ = *s++;
    }
    *t = 0;

    bool flag = false;
    if (*s && *(s + 1))
    {
        flag = (*(s + 1) == 'a');
    }

    struct channel *ch = select_channel(channel);
    if (!ch)
    {
        raw_notify(executor, tprintf("Unknown channel %s.", channel));
        return;
    }
    if ( !(  Comm_All(executor)
          || Controls(executor,ch->charge_who)))
    {
        raw_notify(executor, NOPERM_MESSAGE);
        return;
    }
    raw_notify(executor, tprintf("-- %s --", ch->name));
    raw_notify(executor, tprintf("%-29.29s %-6.6s %-6.6s", "Name", "Status", "Player"));
    struct comuser *user;
    char *buff;
    char temp[LBUF_SIZE];
    int i;
    for (i = 0; i < ch->num_users; i++)
    {
        user = ch->users[i];
        if (  (  flag
              || UNDEAD(user->who))
           && (  !Hidden(user->who)
              || Wizard_Who(executor)
              || See_Hidden(executor)))
        {
            buff = unparse_object(executor, user->who, false);
            sprintf(temp, "%-29.29s %-6.6s %-6.6s", strip_ansi(buff),
                user->bUserIsOn ? "on " : "off",
                isPlayer(user->who) ? "yes" : "no ");
            raw_notify(executor, temp);
            free_lbuf(buff);
        }
    }
    raw_notify(executor, tprintf("-- %s --", ch->name));
}

void do_comdisconnectraw_notify(dbref player, char *chan)
{
    struct channel *ch = select_channel(chan);
    if (!ch) return;

    struct comuser *cu = select_user(ch, player);
    if (!cu) return;

    if (  (ch->type & CHANNEL_LOUD)
       && cu->bUserIsOn
       && !Hidden(player))
    {
        char *messNormal, *messNoComtitle;
        BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0, ch->header, cu,
            ":has disconnected.", &messNormal, &messNoComtitle);
        SendChannelMessage(player, ch, messNormal, messNoComtitle);
    }
}

void do_comconnectraw_notify(dbref player, char *chan)
{
    struct channel *ch = select_channel(chan);
    if (!ch) return;
    struct comuser *cu = select_user(ch, player);
    if (!cu) return;

    if (  (ch->type & CHANNEL_LOUD)
       && cu->bUserIsOn
       && !Hidden(player))
    {
        char *messNormal, *messNoComtitle;
        BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0, ch->header, cu,
            ":has connected.", &messNormal, &messNoComtitle);
        SendChannelMessage(player, ch, messNormal, messNoComtitle);
    }
}

void do_comconnectchannel(dbref player, char *channel, char *alias, int i)
{
    struct comuser *user;

    struct channel *ch = select_channel(channel);
    if (ch)
    {
        for (user = ch->on_users;
        user && user->who != player;
        user = user->on_next) ;

        if (!user)
        {
            user = select_user(ch, player);
            if (user)
            {
                user->on_next = ch->on_users;
                ch->on_users = user;
            }
            else
            {
                raw_notify(player, tprintf("Bad Comsys Alias: %s for Channel: %s", alias + i * ALIAS_SIZE, channel));
            }
        }
    }
    else
    {
        raw_notify(player, tprintf("Bad Comsys Alias: %s for Channel: %s", alias + i * ALIAS_SIZE, channel));
    }
}

void do_comdisconnect(dbref player)
{
    comsys_t *c = get_comsys(player);
    int i;

    for (i = 0; i < c->numchannels; i++)
    {
        char *CurrentChannel = c->channels[i];
        bool bFound = false;
        int j;

        for (j = 0; j < i; j++)
        {
            if (strcmp(c->channels[j], CurrentChannel) == 0)
            {
                bFound = true;
                break;
            }
        }

        if (!bFound)
        {
            do_comdisconnectchannel(player, CurrentChannel);
            do_comdisconnectraw_notify(player, CurrentChannel);
        }
    }
}

void do_comconnect(dbref player)
{
    comsys_t *c = get_comsys(player);
    int i;

    for (i = 0; i < c->numchannels; i++)
    {
        char *CurrentChannel = c->channels[i];
        bool bFound = false;
        int j;

        for (j = 0; j < i; j++)
        {
            if (strcmp(c->channels[j], CurrentChannel) == 0)
            {
                bFound = true;
                break;
            }
        }

        if (!bFound)
        {
            do_comconnectchannel(player, CurrentChannel, c->alias, i);
            do_comconnectraw_notify(player, CurrentChannel);
        }
    }
}


void do_comdisconnectchannel(dbref player, char *channel)
{
    struct channel *ch = select_channel(channel);
    if (!ch)
    {
        return;
    }

    struct comuser *prevuser = NULL;
    struct comuser *user;
    for (user = ch->on_users; user;)
    {
        if (user->who == player)
        {
            if (prevuser)
            {
                prevuser->on_next = user->on_next;
            }
            else
            {
                ch->on_users = user->on_next;
            }
            return;
        }
        else
        {
            prevuser = user;
            user = user->on_next;
        }
    }
}

void do_editchannel
(
    dbref executor,
    dbref caller,
    dbref enactor,
    int   flag,
    int   nargs,
    char *arg1,
    char *arg2
)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    struct channel *ch = select_channel(arg1);
    if (!ch)
    {
        raw_notify(executor, tprintf("Unknown channel %s.", arg1));
        return;
    }
    if ( !(  Comm_All(executor)
          || Controls(executor, ch->charge_who)))
    {
        raw_notify(executor, NOPERM_MESSAGE);
        return;
    }

    bool add_remove = true;
    char *s = arg2;
    if (*s == '!')
    {
        add_remove = false;
        s++;
    }
    switch (flag)
    {
    case 0:
        {
            dbref who = lookup_player(executor, arg2, true);
            if (NOTHING == who)
            {
                raw_notify(executor, "Invalid player.");
            }
            else
            {
                ch->charge_who = who;
                raw_notify(executor, "Set.");
            }
        }
        break;

    case 1:
        ch->charge = mux_atol(arg2);
        raw_notify(executor, "Set.");
        break;

    case 3:
        {
            int access = 0;
            if (strcmp(s, "join") == 0)
            {
                access = CHANNEL_PLAYER_JOIN;
            }
            else if (strcmp(s, "receive") == 0)
            {
                access = CHANNEL_PLAYER_RECEIVE;
            }
            else if (strcmp(s, "transmit") == 0)
            {
                access = CHANNEL_PLAYER_TRANSMIT;
            }
            else
            {
                raw_notify(executor, "@cpflags: Unknown Flag.");
            }

            if (access)
            {
                if (add_remove)
                {
                    ch->type |= access;
                    raw_notify(executor, "@cpflags: Set.");
                }
                else
                {
                    ch->type &= ~access;
                    raw_notify(executor, "@cpflags: Cleared.");
                }
            }
        }
        break;

    case 4:
        {
            int access = 0;
            if (strcmp(s, "join") == 0)
            {
                access = CHANNEL_OBJECT_JOIN;
            }
            else if (strcmp(s, "receive") == 0)
            {
                access = CHANNEL_OBJECT_RECEIVE;
            }
            else if (strcmp(s, "transmit") == 0)
            {
                access = CHANNEL_OBJECT_TRANSMIT;
            }
            else
            {
                raw_notify(executor, "@coflags: Unknown Flag.");
            }

            if (access)
            {
                if (add_remove)
                {
                    ch->type |= access;
                    raw_notify(executor, "@coflags: Set.");
                }
                else
                {
                    ch->type &= ~access;
                    raw_notify(executor, "@coflags: Cleared.");
                }
            }
        }
        break;
    }
}

bool test_join_access(dbref player, struct channel *chan)
{
    if (Comm_All(player))
    {
        return true;
    }

    int access;
    if (isPlayer(player))
    {
        access = CHANNEL_PLAYER_JOIN;
    }
    else
    {
        access = CHANNEL_OBJECT_JOIN;
    }
    return (  (chan->type & access) != 0
           || could_doit(player, chan->chan_obj, A_LOCK));
}

bool test_transmit_access(dbref player, struct channel *chan)
{
    if (Comm_All(player))
    {
        return true;
    }

    int access;
    if (isPlayer(player))
    {
        access = CHANNEL_PLAYER_TRANSMIT;
    }
    else
    {
        access = CHANNEL_OBJECT_TRANSMIT;
    }
    return (  (chan->type & access) != 0
           || could_doit(player, chan->chan_obj, A_LUSE));

}

bool test_receive_access(dbref player, struct channel *chan)
{
    if (Comm_All(player))
    {
        return true;
    }

    int access;
    if (isPlayer(player))
    {
        access = CHANNEL_PLAYER_RECEIVE;
    }
    else
    {
        access = CHANNEL_OBJECT_RECEIVE;
    }
    return (  (chan->type & access) != 0
           || could_doit(player, chan->chan_obj, A_LENTER));

}

// true means continue, false means stop
//
bool do_comsystem(dbref who, char *cmd)
{
    char *t;
    char *alias = alloc_lbuf("do_comsystem");
    char *s = alias;
    for (t = cmd; *t && *t != ' ' && s < alias + LBUF_SIZE; *s++ = *t++)
    {
        ; // Nothing.
    }

    *s = '\0';

    if (*t)
    {
        t++;
    }

    char *ch = get_channel_from_alias(who, alias);
    if (  ch[0] != '\0'
       && t[0] != '\0')
    {
        do_processcom(who, ch, t);
        free_lbuf(alias);
        return false;
    }
    else
    {
        free_lbuf(alias);
    }
    return true;
}

void do_cemit
(
    dbref executor,
    dbref caller,
    dbref enactor,
    int   key,
    int   nargs,
    char *chan,
    char *text
)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    struct channel *ch = select_channel(chan);
    if (!ch)
    {
        raw_notify(executor, tprintf("Channel %s does not exist.", chan));
        return;
    }
    if (  !Controls(executor, ch->charge_who)
       && !Comm_All(executor))
    {
        raw_notify(executor, NOPERM_MESSAGE);
        return;
    }
    char *text2 = alloc_lbuf("do_cemit");
    if (key == CEMIT_NOHEADER)
    {
        strcpy(text2, text);
    }
    else
    {
        strcpy(text2, tprintf("%s %s", ch->header, text));
    }
    SendChannelMessage(executor, ch, text2, text2);
}

void do_chopen
(
    dbref executor,
    dbref caller,
    dbref enactor,
    int   key,
    int   nargs,
    char *chan,
    char *value
)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    if (key == CSET_LIST)
    {
        do_chanlist(executor, caller, enactor, 1, NULL);
        return;
    }

    char *msg = NULL;
    struct channel *ch = select_channel(chan);
    if (!ch)
    {
        msg = tprintf("@cset: Channel %s does not exist.", chan);
        raw_notify(executor, msg);
        return;
    }
    if (  !Controls(executor, ch->charge_who)
       && !Comm_All(executor))
    {
        raw_notify(executor, NOPERM_MESSAGE);
        return;
    }
    char *buff;
    dbref thing;

    switch (key)
    {
    case CSET_PUBLIC:
        ch->type |= CHANNEL_PUBLIC;
        msg = tprintf("@cset: Channel %s placed on the public listings.", chan);
        break;

    case CSET_PRIVATE:
        ch->type &= ~CHANNEL_PUBLIC;
        msg = tprintf("@cset: Channel %s taken off the public listings." ,chan);
        break;

    case CSET_LOUD:
        ch->type |= CHANNEL_LOUD;
        msg = tprintf("@cset: Channel %s now sends connect/disconnect msgs.", chan);
        break;

    case CSET_QUIET:
        ch->type &= ~CHANNEL_LOUD;
        msg = tprintf("@cset: Channel %s connect/disconnect msgs muted.", chan);
        break;

    case CSET_SPOOF:
        ch->type |= CHANNEL_SPOOF;
        msg = tprintf("@cset: Channel %s set spoofable.", chan);
        break;

    case CSET_NOSPOOF:
        ch->type &= ~CHANNEL_SPOOF;
        msg = tprintf("@cset: Channel %s set unspoofable.", chan);
        break;

    case CSET_OBJECT:
        init_match(executor, value, NOTYPE);
        match_everything(0);
        thing = match_result();

        if (thing == NOTHING)
        {
            ch->chan_obj = thing;
            msg = tprintf("Channel %s is now disassociated from any channel object.", ch->name);
        }
        else if (Good_obj(thing))
        {
            ch->chan_obj = thing;
            buff = unparse_object(executor, thing, false);
            msg = tprintf("Channel %s is now using %s as channel object.", ch->name, buff);
            free_lbuf(buff);
        }
        else
        {
            msg = tprintf("%d is not a valid channel object.", thing);
        }
        break;

    case CSET_HEADER:
        do_cheader(executor, chan, value);
        msg = "Set.";
        break;

    case CSET_LOG:
        if (do_chanlog(executor, chan, value))
        {
            msg = tprintf("@cset: Channel %s maximum history set.", chan);
        }
        else
        {
            msg = tprintf("@cset: Maximum history must be a number less than or equal to %d.", MAX_RECALL_REQUEST);
        }
        break;
    }
    raw_notify(executor, msg);
}

void do_chboot
(
    dbref executor,
    dbref caller,
    dbref enactor,
    int   key,
    int   nargs,
    char *channel,
    char *victim
)
{
    // I sure hope it's not going to be that long.
    //
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    struct channel *ch = select_channel(channel);
    if (!ch)
    {
        raw_notify(executor, "@cboot: Unknown channel.");
        return;
    }
    struct comuser *user = select_user(ch, executor);
    if (!user)
    {
        raw_notify(executor, "@cboot: You are not on that channel.");
        return;
    }
    if (  !Controls(executor, ch->charge_who)
       && !Comm_All(executor))
    {
        raw_notify(executor, "@cboot: You can't do that!");
        return;
    }
    dbref thing = match_thing(executor, victim);

    if (!Good_obj(thing))
    {
        return;
    }
    struct comuser *vu = select_user(ch, thing);
    if (!vu)
    {
        raw_notify(executor, tprintf("@cboot: %s is not on the channel.",
            Moniker(thing)));
        return;
    }

    raw_notify(executor, tprintf("You boot %s off channel %s.",
                                 Moniker(thing), ch->name));
    raw_notify(thing, tprintf("%s boots you off channel %s.",
                              Moniker(thing), ch->name));

    if (!(key & CBOOT_QUIET))
    {
        char *mess1, *mess1nct;
        char *mess2, *mess2nct;
        BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0, ch->header, user,
                            ":boots", &mess1, &mess1nct);
        BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0, 0, vu,
                            ":off the channel.", &mess2, &mess2nct);
        char *messNormal = alloc_lbuf("do_chboot.messnormal");
        char *messNoComtitle = alloc_lbuf("do_chboot.messnocomtitle");
        char *mnp = messNormal;
        char *mnctp = messNoComtitle;
        if (mess1)
        {
            safe_str(mess1, messNormal, &mnp);
            free_lbuf(mess1);
        }
        if (mess2)
        {
            safe_str(mess2, messNormal, &mnp);
            free_lbuf(mess2);
        }
        *mnp = '\0';
        if (mess1nct)
        {
            safe_str(mess1nct, messNoComtitle, &mnctp);
            free_lbuf(mess1nct);
        }
        if (mess2nct)
        {
            safe_str(mess2nct, messNoComtitle, &mnctp);
            free_lbuf(mess2nct);
        }
        *mnctp = '\0';
        SendChannelMessage(executor, ch, messNormal, messNoComtitle);
        do_delcomchannel(thing, channel, false);
    }
    else
    {
        do_delcomchannel(thing, channel, true);
    }
}

void do_cheader(dbref player, char *channel, char *header)
{
    struct channel *ch = select_channel(channel);
    if (!ch)
    {
        raw_notify(player, "That channel does not exist.");
        return;
    }
    if (  !Controls(player, ch->charge_who)
       && !Comm_All(player))
    {
        raw_notify(player, NOPERM_MESSAGE);
        return;
    }
    char *p = RemoveSetOfCharacters(header, "\r\n\t");

    // Optimize/terminate any ANSI in the string.
    //
    char NewHeader_ANSI[MAX_HEADER_LEN+1];
    int nVisualWidth;
    int nLen = ANSI_TruncateToField(p, sizeof(NewHeader_ANSI),
        NewHeader_ANSI, sizeof(NewHeader_ANSI), &nVisualWidth,
        ANSI_ENDGOAL_NORMAL);
    memcpy(ch->header, NewHeader_ANSI, nLen+1);
}

struct chanlist_node
{
    char *           name;
    struct channel * ptr;
};

int DCL_CDECL chanlist_comp(const void* a, const void* b)
{
    chanlist_node* ca = (chanlist_node*)a;
    chanlist_node* cb = (chanlist_node*)b;
    return mux_stricmp(ca->name, cb->name);
}

void do_chanlist
(
    dbref executor,
    dbref caller,
    dbref enactor,
    int   key,
    char *pattern
)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(executor, "Comsys disabled.");
        return;
    }
    if (key & CLIST_FULL)
    {
        do_listchannels(executor);
        return;
    }

    dbref owner;
    struct channel *ch;
    int flags = 0;
    char *atrstr;
    char *temp = alloc_mbuf("do_chanlist_temp");
    char *buf = alloc_mbuf("do_chanlist_buf");

    if (key & CLIST_HEADERS)
    {
        raw_notify(executor, "*** Channel       Owner           Header");
    }
    else
    {
        raw_notify(executor, "*** Channel       Owner           Description");
    }

    bool bWild;
    if (  NULL != pattern
       && '\0' != *pattern)
    {
        bWild = true;
    }
    else
    {
        bWild = false;
    }

#define MAX_SUPPORTED_NUM_ENTRIES 10000

    INT64 entries = mudstate.channel_htab.GetEntryCount();
    if (MAX_SUPPORTED_NUM_ENTRIES < entries)
    {
        // Nobody should have so many channels.
        //
        entries = MAX_SUPPORTED_NUM_ENTRIES;
    }

    struct chanlist_node* charray = (chanlist_node*)MEMALLOC(sizeof(chanlist_node)*entries);
    ISOUTOFMEMORY(charray);

    // Arrayify all the channels
    //
    int   actualEntries;
    for (  actualEntries = 0, ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
           ch
        && actualEntries < entries;
           ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
            if (  !bWild
               || quick_wild(pattern, ch->name))
            {
                charray[actualEntries].name = ch->name;
                charray[actualEntries].ptr = ch;
                actualEntries++;
            }
    }

    qsort(charray, actualEntries, sizeof(struct chanlist_node), chanlist_comp);

    for (int i = 0; i < actualEntries; i++)
    {
        ch = charray[i].ptr;
        if (  Comm_All(executor)
           || (ch->type & CHANNEL_PUBLIC)
           || Controls(executor, ch->charge_who))
        {
            char *pBuffer;
            if (key & CLIST_HEADERS)
            {
                pBuffer = ch->header;
            }
            else
            {
                atrstr = atr_pget(ch->chan_obj, A_DESC, &owner, &flags);
                if (  NOTHING == ch->chan_obj
                   || !*atrstr)
                {
                    strcpy(buf, "No description.");
                }
                else
                {
                    sprintf(buf, "%-54.54s", atrstr);
                }
                free_lbuf(atrstr);

                pBuffer = buf;
            }

            char *ownername_ansi = ANSI_TruncateAndPad_sbuf(Moniker(ch->charge_who), 15);
            sprintf(temp, "%c%c%c %-13.13s %s %-45.45s",
                (ch->type & (CHANNEL_PUBLIC)) ? 'P' : '-',
                (ch->type & (CHANNEL_LOUD)) ? 'L' : '-',
                (ch->type & (CHANNEL_SPOOF)) ? 'S' : '-',
                ch->name, ownername_ansi, pBuffer);
            free_sbuf(ownername_ansi);

            raw_notify(executor, temp);
        }
    }
    MEMFREE(charray);
    free_mbuf(temp);
    free_mbuf(buf);
    raw_notify(executor, "-- End of list of Channels --");
}

// Returns a player's comtitle for a named channel.
//
FUNCTION(fun_comtitle)
{
    if (!mudconf.have_comsys)
    {
        safe_str("#-1 COMSYS DISABLED", buff, bufc);
        return;
    }

    dbref victim = lookup_player(executor, fargs[0], true);
    if (!Good_obj(victim))
    {
        init_match(executor, fargs[0], TYPE_THING);
        match_everything(0);
        victim = match_result();
        if (!Good_obj(victim))
        {
            safe_str("#-1 OBJECT DOES NOT EXIST", buff, bufc);
            return;
        }
    }

    struct channel *chn = select_channel(fargs[1]);
    if (!chn)
    {
        safe_str("#-1 CHANNEL DOES NOT EXIST", buff, bufc);
        return;
    }

    comsys_t *c = get_comsys(executor);
    struct comuser *user;

    int i;
    bool onchannel = false;
    if (Wizard(executor))
    {
        onchannel = true;
    }
    else
    {
        for (i = 0; i < c->numchannels; i++)
        {
            user = select_user(chn, executor);
            if (user)
            {
                onchannel = true;
                break;
            }
        }
    }

    if (!onchannel)
    {
        safe_noperm(buff, bufc);
        return;
    }

    for (i = 0; i < c->numchannels; i++)
    {
        user = select_user(chn, victim);
        if (user)
        {
          // Do we want this function to evaluate the comtitle or not?
#if 0
          char *nComTitle = GetComtitle(user);
          safe_str(nComTitle, buff, bufc);
          FreeComtitle(nComTitle);
          return;
#else
          safe_str(user->title, buff, bufc);
          return;
#endif
        }
    }
    safe_str("#-1 OBJECT NOT ON THAT CHANNEL", buff, bufc);
}

// Returns a player's comsys alias for a named channel.
//
FUNCTION(fun_comalias)
{
    if (!mudconf.have_comsys)
    {
        safe_str("#-1 COMSYS DISABLED", buff, bufc);
        return;
    }

    dbref victim = lookup_player(executor, fargs[0], true);
    if (!Good_obj(victim))
    {
        init_match(executor, fargs[0], TYPE_THING);
        match_everything(0);
        victim = match_result();
        if (!Good_obj(victim))
        {
            safe_str("#-1 OBJECT DOES NOT EXIST", buff, bufc);
            return;
        }
    }

    struct channel *chn = select_channel(fargs[1]);
    if (!chn)
    {
        safe_str("#-1 CHANNEL DOES NOT EXIST", buff, bufc);
        return;
    }

    // Wizards can get the comalias for anyone. Players and objects can check
    // for themselves. Objects that Inherit can check for their owners.
    //
    if (  !Wizard(executor)
       && executor != victim
       && (  Owner(executor) != victim
          || !Inherits(executor)))
    {
        safe_noperm(buff, bufc);
        return;
    }

    comsys_t *cc = get_comsys(victim);
    for (int i = 0; i < cc->numchannels; i++)
    {
        if (!strcmp(fargs[1], cc->channels[i]))
        {
            safe_str(cc->alias + i * ALIAS_SIZE, buff, bufc);
            return;
        }
    }
    safe_str("#-1 OBJECT NOT ON THAT CHANNEL", buff, bufc);
}

// Returns a list of channels.
//
FUNCTION(fun_channels)
{
    if (!mudconf.have_comsys)
    {
        safe_str("#-1 COMSYS DISABLED", buff, bufc);
        return;
    }

    dbref who = NOTHING;
    if (nfargs >= 1)
    {
        who = lookup_player(executor, fargs[0], true);
        if (  who == NOTHING
           && mux_stricmp(fargs[0], "all") != 0)
        {
            safe_str("#-1 PLAYER NOT FOUND", buff, bufc);
            return;
        }
    }

    ITL itl;
    ItemToList_Init(&itl, buff, bufc);
    struct channel *chn;
    for (chn = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         chn;
         chn = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        if (  (  Comm_All(executor)
              || (chn->type & CHANNEL_PUBLIC)
              || Controls(executor, chn->charge_who))
           && (  who == NOTHING
              || Controls(who, chn->charge_who))
           && !ItemToList_AddString(&itl, chn->name))
        {
            break;
        }
    }
}