muddy/
muddy/CVS/
muddy/area/
muddy/area/CVS/
muddy/clans/CVS/
muddy/classes/CVS/
muddy/doc/
muddy/doc/CVS/
muddy/etc/CVS/
muddy/etc/i3/
muddy/etc/i3/CVS/
muddy/imc/CVS/
muddy/lang/CVS/
muddy/licenses/CVS/
muddy/msgdb/CVS/
muddy/new/CVS/
muddy/notes/
muddy/player/
muddy/races/CVS/
muddy/religions/CVS/
muddy/src/CVS/
muddy/src/comm/CVS/
muddy/src/db/CVS/
muddy/src/intermud/
muddy/src/intermud/CVS/
muddy/src/irc/CVS/
muddy/src/olc/CVS/
/* $Id: olc.c,v 1.666 2004/09/20 10:50:29 shrike Exp $ */

/************************************************************************************
 *    Copyright 2004 Astrum Metaphora consortium                                    *
 *                                                                                  *
 *    Licensed under the Apache License, Version 2.0 (the "License");               *
 *    you may not use this file except in compliance with the License.              *
 *    You may obtain a copy of the License at                                       *
 *                                                                                  *
 *    http://www.apache.org/licenses/LICENSE-2.0                                    *
 *                                                                                  *
 *    Unless required by applicable law or agreed to in writing, software           *
 *    distributed under the License is distributed on an "AS IS" BASIS,             *
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.      *
 *    See the License for the specific language governing permissions and           *
 *    limitations under the License.                                                *
 *                                                                                  *
 ************************************************************************************/

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "olc.h"
#include "db/lang.h"

/*
 * The version info.  Please use this info when reporting bugs.
 * It is displayed in the game by typing 'version' while editing.
 * Do not remove these from the code - by request of Jason Dinkel
 */
#define VERSION        "ILAB Online Creation [Beta 1.0, ROM 2.3 modified]\n" \
                "     Port a ROM 2.4 v1.7\n"
#define AUTHOR        "     By Jason(jdinkel@mines.colorado.edu)\n" \
                "     Modified for use with ROM 2.3\n"        \
                "     By Hans Birkeland (hansbi@ifi.uio.no)\n" \
                "     Modificado para uso en ROM 2.4b4a\n"        \
                "     Por Ivan Toledo (pvillanu@choapa.cic.userena.cl)\n" \
                "     Modified for use with Muddy\n" \
                "     Farmer Joe (fjoe@iclub.nsu.ru)\n"
#define DATE        "     (Apr. 7, 1995 - ROM mod, Apr 16, 1995)\n" \
                "     (Port a ROM 2.4 - Nov 2, 1996)\n" \
                "     Version actual : 1.71 - Mar 22, 1998\n"
#define CREDITS "     Original by Surreality(cxw197@psu.edu) and Locke(locke@lm.com)"

const char ED_AREA[]    = "area";
const char ED_ROOM[]    = "room";
const char ED_OBJ[]     = "object";
const char ED_MOB[]     = "mobile";
const char ED_MPCODE[]  = "mpcode";
const char ED_OPCODE[]  = "opcode";
const char ED_RPCODE[]  = "rpcode";
const char ED_HELP[]    = "help";
const char ED_CLAN[]    = "clan";
const char ED_MSG[]     = "msgdb";
const char ED_LANG[]    = "language";
const char ED_IMPL[]    = "implicit";
const char ED_EXPL[]    = "explicit";
const char ED_SOC[]     = "social";
const char ED_CLASS[]   = "class";
const char ED_RACE[]    = "race";
const char ED_RELIGION[]= "religion";
const char ED_HOMETOWN[]= "hometown";
const char ED_SKILL[]   = "skill";
const char ED_COMMAND[] = "command";
const char ED_ALIAS[]   = "alias";
const char ED_SKILL_ALIAS[] = "skillalias";
const char ED_PRICE[]   = "price";
const char ED_METEOR[]  = "meteor";
const char ED_SEC[]     = "sec";
// const char ED_ARTEFACT[]= "artefact"; // TODO: for artefact
const char ED_RIDDLE[]  = "riddle";
const char ED_IMMORTAL[]= "immortal";
const char ED_MATERIAL[]= "material";
const char ED_LIQUID[]  = "liquid";
const char ED_TATTOO[]  = "tattoo";
const char ED_PAINT[]   = "paint";
const char ED_RITUAL[]  = "ritual";

olced_t olced_table[] = 
{
    { ED_AREA,     "AreaEd",      0,  olc_cmds_area         },
    { ED_ROOM,     "RoomEd",      0,  olc_cmds_room         },
    { ED_OBJ,      "ObjEd",       0,  olc_cmds_obj          },
    { ED_MOB,      "MobEd",       0,  olc_cmds_mob          },
    { ED_MPCODE,   "MPEd",        5,  olc_cmds_mpcode       },
    { ED_OPCODE,   "OPEd",        5,  olc_cmds_opcode       },
    { ED_RPCODE,   "RPEd",        5,  olc_cmds_rpcode       },
    { ED_HELP,     "HelpEd",      0,  olc_cmds_help         },
    { ED_MSG,      "MsgEd",       0,  olc_cmds_msg          },
    { ED_CLAN,     "ClanEd",      0,  olc_cmds_clan         },
    { ED_LANG,     "LangEd",      0,  olc_cmds_lang         },
    { ED_IMPL,     "ImplRuleEd",  0,  olc_cmds_impl         },
    { ED_EXPL,     "ExplRuleEd",  0,  olc_cmds_expl         },
    { ED_SOC,      "SocEd",       0,  olc_cmds_soc          }, 
    { ED_CLASS,    "ClassEd",     0,  olc_cmds_class        },
    { ED_RACE,     "RaceEd",      0,  olc_cmds_race         },
    { ED_RELIGION, "ReligionEd",  0,  olc_cmds_religion     },
    { ED_HOMETOWN, "HometownEd",  0,  olc_cmds_hometown     },
    { ED_SKILL,    "SkillEd",     0,  olc_cmds_skill        },
    { ED_COMMAND,  "CommandEd",   5,  olc_cmds_command      },
    { ED_ALIAS,    "AliasEd",     0,  olc_cmds_alias        },
    { ED_SKILL_ALIAS,"SkillAliasEd",  0,  olc_cmds_skill_alias  },
    { ED_PRICE,    "PriceEd",     0,  olc_cmds_price        },
    { ED_METEOR,   "MeteorEd",    0,  olc_cmds_meteor       },
    { ED_SEC,      "SecEd",       0,  olc_cmds_sec          },
    //{ ED_ARTEFACT, "ArtefactEd",  0,    olc_cmds_artefact},
    { ED_RIDDLE,   "RiddleEd",    0,  olc_cmds_riddle       },
    { ED_IMMORTAL, "ImmEd",       0,  olc_cmds_immortal     },
    { ED_MATERIAL, "MaterialEd",  0,  olc_cmds_material     },
    { ED_LIQUID,   "LiquidEd",    0,  olc_cmds_liquid       },
    { ED_TATTOO,   "TattooEd",    0,  olc_cmds_tattoo       },
    { ED_RITUAL,   "RitualEd",    0,  olc_cmds_ritual       },
    { ED_PAINT,    "PaintEd",     0,  olc_cmds_paint        },
    
    { NULL}
};

static olc_cmd_t *  olc_cmd_lookup  (olc_cmd_t *cmd_table, const char *name);

static void do_olc(CHAR_DATA *ch, const char *argument, int fun);

bool check_security_olc(CHAR_DATA *ch, olc_cmd_t *cmd, bool mess)
{
    if (ch == NULL
        || IS_NPC(ch)
        || cmd == NULL)
        return FALSE;

    if (ch->pcdata->security < cmd->sec)
    {
        if (mess)
            char_puts("Permission denied.\n", ch);
        return FALSE;
    }

    return TRUE;
}

/* Executed from comm.c.  Minimizes compiling when changes are made. */
bool run_olc_editor(DESCRIPTOR_DATA *d)
{
    char command[MAX_INPUT_LENGTH];
    olc_cmd_t *cmd;
    const char *argument;
    olced_t *olced = d->olced;

    if ((olced = d->olced) == NULL)
        return FALSE;

    argument = one_argument(d->incomm, command, sizeof(command));

    if (command[0] == '\0')
    {
        if (!check_security_olc(d->character, &(olced->cmd_table[FUN_SHOW]), TRUE))
            return FALSE;
        olced->cmd_table[FUN_SHOW].olc_fun(d->character, argument,
                                           olced->cmd_table+FUN_SHOW);
        return TRUE;
    }

    if (!str_cmp(command, "done"))
    {
        edit_done(d);
        return TRUE;
    }

    if ((cmd = olc_cmd_lookup(olced->cmd_table+FUN_FIRST, command)) == NULL
        ||  cmd->olc_fun == NULL)
        return FALSE;

    if (!check_security_olc(d->character, cmd, TRUE))
        return FALSE;

    if (cmd->olc_fun(d->character, argument, cmd)
        &&  olced->cmd_table[FUN_TOUCH].olc_fun)
        olced->cmd_table[FUN_TOUCH].olc_fun(d->character, str_empty,
                                            olced->cmd_table+FUN_TOUCH);

    return TRUE;
}

DO_FUN(do_create)
{
    do_olc(ch, argument, FUN_CREATE);
}

DO_FUN(do_edit)
{
    do_olc(ch, argument, FUN_EDIT);
}

DO_FUN(do_alist)
{
    do_olc(ch, argument, FUN_LIST);
}

DO_FUN(do_ashow)
{
    do_olc(ch, argument, FUN_SHOW);
}

/*
 * olced_busy -- returns TRUE if there is another character
 *                 is using the same OLC editor
 */
bool olced_busy(CHAR_DATA *ch, const char *id, void *edit, void *edit2)
{
    DESCRIPTOR_DATA *d;

    for (d = descriptor_list; d; d = d->next)
    {
        CHAR_DATA *vch = d->original ? d->original : d->character;

        if (vch != ch
            &&  d->olced
            &&  d->olced->id == id
            &&  (!edit || d->pEdit == edit)
            &&  (!edit2 || d->pEdit2 == edit2))
        {
            char_printf(ch, "%s: %s is locking this editor "
                        "right now.\n",
                        d->olced->name,
                        vch->name);
            return TRUE;
        }
    }

    return FALSE;
}

/*
 * Generic OLC editor functions.
 * All functions assume !IS_NPC(ch).
 */
OLC_FUN(olced_spell_out)
{
    char_puts("Spell it out.\n", ch);
    return FALSE;
}

OLC_FUN(olced_dummy)
{
    return FALSE;
}

bool olced_number(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, int *pInt)
{
    int val;
    char *endptr;
    char arg[MAX_STRING_LENGTH];
    VALIDATE_FUN *validator;

    one_argument(argument, arg, sizeof(arg));
    
    val = strtol(arg, &endptr, 0);
    
    if (*arg == '\0' || *endptr != '\0')
    {
        char_printf(ch, "Syntax: %s number\n", cmd->name);
        return FALSE;
    }

    if ((validator = cmd->arg1) && !validator(ch, &val))
        return FALSE;

    *pInt = val;
    
    char_puts("Ok.\n", ch);

    return TRUE;
}

bool olced_double(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, double *pDbl)
{
    double         val;
    char          *endptr;
    char           arg[MAX_STRING_LENGTH];
    VALIDATE_FUN  *validator;

    one_argument(argument, arg, sizeof(arg));
    
    val = strtod(arg, &endptr);
    
    if (*arg == '\0' || *endptr != '\0')
    {
        char_printf(ch, "Syntax: %s number\n", cmd->name);
        return FALSE;
    }

    if ((validator = cmd->arg1) && !validator(ch, &val))
        return FALSE;

    *pDbl = val;
    
    char_puts("Ok.\n", ch);
    
    return TRUE;
}

bool olced_name(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, const char **pStr)
{
    VALIDATE_FUN *validator;
    bool changed;
    char arg[MAX_INPUT_LENGTH]; 

    argument = one_argument(argument, arg, sizeof(arg));
    if (arg[0] == '\0')
    {
        char_printf(ch, "Syntax: %s string\n", cmd->name);
        return FALSE;
    }

    if ((validator = cmd->arg1) && !validator(ch, argument))
        return FALSE;

    changed = FALSE;
    for (; arg[0]; argument = one_argument(argument, arg, sizeof(arg)))
    {
        if (!str_cmp(arg, "all"))
        {
            char_printf(ch, "%s: %s: Illegal name.\n",
                        OLCED(ch)->name, arg);
            continue;
        }
        changed = TRUE;
        name_toggle(ch, arg, OLCED(ch)->name, pStr);
    }

    return changed;
}

bool olced_str(CHAR_DATA *ch, const char *argument,  olc_cmd_t *cmd, const char **pStr)
{
    VALIDATE_FUN *validator;

    if (IS_NULLSTR(argument))
    {
        char_printf(ch, "Syntax: %s string\n", cmd->name);
        return FALSE;
    }

    if ((validator = cmd->arg1) && !validator(ch, argument))
        return FALSE;

    free_string(*pStr);
    *pStr = str_dup(argument);
    char_puts("Ok.\n", ch);
    return TRUE;
}

bool olced_str_text(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, const char **pStr)
{
    if (argument[0] =='\0')
    {
        string_append(ch, pStr);
        return TRUE;
    }

    char_printf(ch, "Syntax: %s\n", cmd->name);
    return FALSE;
}

bool olced_mlstr(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, mlstring **pmlstr)
{
    if (!mlstr_edit(pmlstr, argument))
    {
        char_printf(ch, "Syntax: %s lang string\n", cmd->name);
        return FALSE;
    }
    char_puts("Ok.\n", ch);
    return TRUE;
}

bool olced_mlstrnl(CHAR_DATA *ch, const char *argument,  olc_cmd_t *cmd, mlstring **pmlstr)
{
    if (!mlstr_editnl(pmlstr, argument))
    {
        char_printf(ch, "Syntax: %s lang string\n", cmd->name);
        return FALSE;
    }
    char_puts("Ok.\n", ch);
    return TRUE;
}

bool olced_mlstr_text(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, mlstring **pmlstr)
{
    if (!mlstr_append(ch, pmlstr, argument))
    {
        char_printf(ch, "Syntax: %s lang\n", cmd->name);
        return FALSE;
    }
    return TRUE;
}

static void cb_format(int lang, const char **p, void *arg)
{
    *p = format_string(*p);
}

bool olced_exd(CHAR_DATA *ch, const char* argument, olc_cmd_t *cmd, ED_DATA **ped)
{
    ED_DATA *ed;
    char command[MAX_INPUT_LENGTH];
    char keyword[MAX_INPUT_LENGTH];
    char arg[MAX_INPUT_LENGTH];

    argument = one_argument(argument, command, sizeof(command));
    argument = one_argument(argument, keyword, sizeof(keyword));
    argument = one_argument(argument, arg, sizeof(arg));

    if (command[0] == '\0' || keyword[0] == '\0')
    {
        do_help(ch, "'OLC EXD'");
        return FALSE;
    }

    if (!str_cmp(command, "add"))
    {
        ed      = ed_new();
        ed->keyword = str_dup(keyword);

        if (!mlstr_append(ch, &ed->description, arg))
        {
            ed_free(ed);
            do_help(ch, "'OLC EXD'");
            return FALSE;
        }

        ed->next    = *ped;
        *ped        = ed;
        char_puts("Extra description added.\n", ch);
        return TRUE;
    }

    if (!str_cmp(command, "name"))
    {
        ed = ed_lookup(keyword, *ped);
        if (ed == NULL)
        {
            char_printf(ch, "%s: Extra description keyword not found.\n", OLCED(ch)->name);
            return FALSE;
        }

        if (!str_cmp(arg, "none")
            ||  !str_cmp(arg, "all"))
        {
            char_printf(ch, "%s: %s: Illegal keyword.\n",
                        OLCED(ch)->name, arg);
            return FALSE;
        }
        name_toggle(ch, arg, OLCED(ch)->name, &ed->keyword);
        return TRUE;
    }

    if (!str_cmp(command, "edit"))
    {
        ed = ed_lookup(keyword, *ped);
        if (ed == NULL)
        {
            char_printf(ch, "%s: Extra description keyword not found.\n", OLCED(ch)->name);
            return FALSE;
        }

        if (!mlstr_append(ch, &ed->description, arg))
        {
            do_help(ch, "'OLC EXD'");
            return FALSE;
        }
        return TRUE;
    }

    if (!str_cmp(command, "delete"))
    {
        ED_DATA *prev = NULL;

        for (ed = *ped; ed; ed = ed->next)
        {
            if (is_name(keyword, ed->keyword))
                break;
            prev = ed;
        }

        if (ed == NULL)
        {
            char_printf(ch, "%s: Extra description keyword not found.\n", OLCED(ch)->name);
            return FALSE;
        }

        if (prev == NULL)
            *ped = ed->next;
        else
            prev->next = ed->next;

        ed->next = NULL;
        ed_free(ed);

        char_puts("Extra description deleted.\n", ch);
        return TRUE;
    }

    if (!str_cmp(command, "show"))
    {
        BUFFER *output;

        ed = ed_lookup(keyword, *ped);
        if (ed == NULL)
        {
            char_printf(ch, "%s: Extra description keyword not found.\n", OLCED(ch)->name);
            return FALSE;
        }

        output = buf_new(-1);
        buf_printf(output, "Keyword:     [%s]\n", ed->keyword);
        mlstr_dump(output, "Description: ", ed->description);
        page_to_char(buf_string(output), ch);
        buf_free(output);
        return FALSE;
    }

    if (!str_cmp(command, "format"))
    {
        ed = ed_lookup(keyword, *ped);
        if (ed == NULL)
        {
            char_printf(ch, "%s: Extra description keyword not found.\n", OLCED(ch)->name);
            return FALSE;
        }

        mlstr_for_each(&ed->description, NULL, cb_format);
        char_puts("Extra description formatted.\n", ch);
        return TRUE;
    }

    do_help(ch, "'OLC EXD'");
    return FALSE;
}

void show_namedp_table(CHAR_DATA *ch, const namedp_t *table)
{
    BUFFER *    output;
    int     col;

    output = buf_new(-1); 

    for (col = 0; (table+col)->name; col++)
    {
        if (col % 5 == 0)
            buf_add(output, "\n");
        buf_printf(output, "%-15.15s", (table+col)->name);
    }
    if (col % 5 != 0)
        buf_add(output, "\n");

    page_to_char(buf_string(output), ch);
    buf_free(output);

    return;

}

bool olced_named_pointer(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, void **pPointer)
{
    const namedp_t *table;

    if (!cmd->arg1)
    {
        char_printf(ch, "%s: %s: Table of values undefined (report it to implementors).\n", OLCED(ch)->name, cmd->name);
        return FALSE;
    }

    if (!str_cmp(argument, "?"))
    {
                table = (const namedp_t *) (cmd->arg1);
                show_namedp_table(ch, table);
        return FALSE;
    }

    if ((table = namedp_lookup(cmd->arg1, argument)) == NULL)
    {
        char_printf(ch, "Syntax: %s value\n"
                        "Type '%s ?' for a list of "
                        "acceptable values.\n",
                        cmd->name, cmd->name);
        return FALSE;
    }

        *pPointer = table->p;
     char_printf(ch, "%s: %s: '%s': Ok.\n",
                    OLCED(ch)->name, cmd->name, table->name);
        return TRUE;
}

bool olced_flag64(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, flag64_t *pflag)
{
    const flag_t *flag64_table;
    const flag_t *f;
    flag64_t ttype;
    const char *tname;

    if (!cmd->arg1)
    {
        char_printf(ch, "%s: %s: Table of values undefined (report it to implementors).\n", OLCED(ch)->name, cmd->name);
        return FALSE;
    }

    if (!str_cmp(argument, "?"))
    {
        show_flags(ch, cmd->arg1);
        return FALSE;
    }

    flag64_table = cmd->arg1;
    tname = flag64_table->name;
    ttype = flag64_table->bit;
    flag64_table++;

    switch (ttype)
    {
    case TABLE_BITVAL: {
            flag64_t marked = 0;

            /*
             * Accept multiple flags.
             */
            for (;;)
            {
                char word[MAX_INPUT_LENGTH];

                argument = one_argument(argument, word, sizeof(word));

                if (word[0] == '\0')
                    break;

                if ((f = flag_lookup(cmd->arg1, word)) == NULL)
                {
                    char_printf(ch, "Syntax: %s flag...\n"
                                "Type '%s ?' for a list of "
                                "acceptable flags.\n",
                                cmd->name, cmd->name);
                    return FALSE;
                }
                if (!f->settable)
                {
                    char_printf(ch, "%s: %s: '%s': flag is not settable.\n",
                                OLCED(ch)->name, cmd->name, f->name);
                    continue;
                }
                SET_BIT(marked, f->bit);
            }
            if (marked)
            {
                TOGGLE_BIT(*pflag, marked);
                char_printf(ch, "%s: %s: '%s': flag(s) toggled.\n",
                            OLCED(ch)->name, cmd->name,
                            flag_string(cmd->arg1, marked));
                return TRUE;
            }
            return FALSE;

            /* NOT REACHED */
        }

    case TABLE_INTVAL: 
        if ((f = flag_lookup(cmd->arg1, argument)) == NULL)
        {
            char_printf(ch, "Syntax: %s value\n"
                        "Type '%s ?' for a list of "
                        "acceptable values.\n",
                        cmd->name, cmd->name);
            return FALSE;
        }
        if (!f->settable)
        {
            char_printf(ch, "%s: %s: '%s': value is not settable.\n",
                        OLCED(ch)->name, cmd->name, f->name);
            return FALSE;
        }
        *pflag = f->bit;
        char_printf(ch, "%s: %s: '%s': Ok.\n",
                    OLCED(ch)->name, cmd->name, f->name);
        return TRUE;
        /* NOT REACHED */

    default:
        char_printf(ch, "%s: %s: %s: table type %d unknown (report it to implementors).\n", OLCED(ch)->name, cmd->name, tname, ttype);
        return FALSE;
        /* NOT REACHED */
    }
}

bool olced_flag32(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, flag32_t *psflag)
{
    flag64_t flag = (flag64_t) (*psflag);
    bool retval = olced_flag64(ch, argument, cmd, &flag);
    if (retval)
        *psflag = (flag32_t) flag;
    return retval;
}

bool olced_dice(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, int *dice)
{
    int num, type, bonus;
    char* p;

    if (argument[0] == '\0')
        goto bail_out;

    num = strtol(argument, &p, 0);
    if (num < 1 || *p != 'd')
        goto bail_out;

    type = strtol(p+1, &p, 0);
    if (type < 1 || *p != '+')
        goto bail_out;

    bonus = strtol(p+1, &p, 0);
    if (bonus < 0 || *p != '\0')
        goto bail_out;

    dice[DICE_NUMBER] = num;
    dice[DICE_TYPE]   = type;
    dice[DICE_BONUS]  = bonus;

    char_printf(ch, "%s set to %dd%d+%d.\n", cmd->name, num, type, bonus);
    return TRUE;

    bail_out:
    char_printf(ch, "Syntax: %s <number>d<type>+<bonus>\n", cmd->name);
    return FALSE;
}

bool olced_clan(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, int *vnum)
{
    int cln;
    int i;

    if (IS_NULLSTR(argument))
    {
        char_printf(ch, "Syntax: %s clan\n"
                    "Use 'clan ?' for list of valid clans.\n"
                    "Use 'clan none' to reset clan.\n",
                    cmd->name);
        return FALSE;
    }

    if (!str_cmp(argument, "?"))
    {
        for (i = 0; i < clans.nused; i++)
            char_printf(ch, "[%d] %s\n", i, CLAN(i)->name);
        return FALSE;
    }

    if (!str_cmp(argument, "none"))
    {
        *vnum = 0;
        return TRUE;
    }

    if ((cln = cln_lookup(argument)) < 0)
    {
        char_printf(ch, "'%s': unknown clan.\n", argument);
        return FALSE;
    }

    *vnum = cln;
    return TRUE;
}

bool olced_rulecl(CHAR_DATA *ch, const char *argument,  olc_cmd_t *cmd, lang_t *l)
{
    char arg[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int rulecl;

    argument = one_argument(argument, arg, sizeof(arg));
    argument = one_argument(argument, arg2, sizeof(arg2));

    if (argument[0] == '\0')
    {
        do_help(ch, "'OLC RULECLASS'");
        return FALSE;
    }

    if ((rulecl = flag_value(rulecl_names, arg)) < 0)
    {
        char_printf(ch, "%s: %s: unknown rule class\n",
                    OLCED(ch)->name, arg);
        return FALSE;
    }

    if (!str_prefix(arg2, "implicit"))
    {
        cmd->arg1 = validate_filename;
        return olced_str(ch, argument, cmd, &l->rules[rulecl].file_impl);
    }

    if (!str_prefix(arg2, "explicit"))
    {
        cmd->arg1 = validate_filename;
        return olced_str(ch, argument, cmd, &l->rules[rulecl].file_expl);
    }

    if (!str_prefix(arg2, "flags"))
    {
        cmd->arg1 = rulecl_flags;
        return olced_flag64(ch, argument, cmd, &l->rules[rulecl].flags);
    }

    do_help(ch, "'OLC RULECLASS'");
    return FALSE;
}

bool olced_vform_add(CHAR_DATA *ch, const char *argument,  olc_cmd_t *cmd, rule_t *r)
{
    char arg[MAX_STRING_LENGTH];

    argument = one_argument(argument, arg, sizeof(arg));
    if (argument[0] == '\0' || !is_number(arg))
    {
        do_help(ch, "'OLC VFORM'");
        return FALSE;
    }

    vform_add(r->f, atoi(arg), argument);
    char_puts("Form added.\n", ch);
    return TRUE;
}

bool olced_vform_del(CHAR_DATA *ch, const char *argument, olc_cmd_t *cmd, rule_t *r)
{
    char arg[MAX_STRING_LENGTH];

    argument = one_argument(argument, arg, sizeof(arg));
    if (!is_number(arg))
    {
        do_help(ch, "'OLC FORM'");
        return FALSE;
    }

    vform_del(r->f, atoi(arg));
    char_puts("Form deleted.\n", ch);
    return TRUE;
}

VALIDATE_FUN(validate_filename)
{
    if (strpbrk(arg, "/"))
    {
        char_printf(ch, "%s: Invalid characters in file name.\n",
                    OLCED(ch)->name);
        return FALSE;
    }
    return TRUE;
}

VALIDATE_FUN(validate_room_vnum)
{
    int vnum = *(int*) arg;

    if (vnum && get_room_index(vnum) == NULL)
    {
        char_printf(ch, "OLC: %d: no such room.\n", vnum);
        return FALSE;
    }

    return TRUE;
}

VALIDATE_FUN(validate_mob_vnum)
{
    int vnum = *(int*) arg;

    if (vnum && get_mob_index(vnum) == NULL)
    {
        char_printf(ch, "OLC: %d: no such mob.\n", vnum);
        return FALSE;
    }

    return TRUE;
}

VALIDATE_FUN(validate_obj_vnum)
{
    int vnum = *(int*) arg;

    if (vnum && get_obj_index(vnum) == NULL)
    {
        char_printf(ch, "OLC: %d: no such obj.\n", vnum);
        return FALSE;
    }

    return TRUE;
}


/*****************************************************************************
 Name:                show_commands
 Purpose:        Display all olc commands.
 Called by:        olc interpreters.
 ****************************************************************************/
OLC_FUN(show_commands)
{
    BUFFER *    output;
    int     col;

    output = buf_new(-1); 

    col = 0;
    for (cmd = OLCED(ch)->cmd_table+FUN_FIRST; cmd->name; cmd++)
    {
        if (cmd->sec > ch->pcdata->security)
            continue;
        buf_printf(output, "%-15.15s", cmd->name);
        if (++col % 5 == 0)
            buf_add(output, "\n");
    }
    if (col % 5 != 0)
        buf_add(output, "\n");

    page_to_char(buf_string(output), ch);
    buf_free(output);

    return FALSE;
}

OLC_FUN(show_version)
{
    char_puts(VERSION   "\n"
              AUTHOR    "\n"
              DATE      "\n"
              CREDITS   "\n", ch);

    return FALSE;
}    

AREA_DATA *get_edited_area(CHAR_DATA *ch)
{
    int vnum;
    olced_t *olced = OLCED(ch);
    void *p = ch->desc->pEdit;

    if (!olced)
        return NULL;

    if (olced->id == ED_AREA)
        return p;

    if (olced->id == ED_HELP)
        return((HELP_DATA*) p)->area;

    if (olced->id == ED_ROOM)
        return ch->in_room->area;

    if (olced->id == ED_OBJ)
        vnum = ((OBJ_INDEX_DATA*) p)->vnum;
    else if (olced->id == ED_MOB)
        vnum = ((MOB_INDEX_DATA*) p)->vnum;
    else if (olced->id == ED_MPCODE)
        vnum = ((MPCODE*) p)->vnum;
    else if (olced->id == ED_OPCODE)
        vnum = ((MPCODE*) p)->vnum;
/*        else if (olced->id == ED_RPCODE)
        vnum = ((MPCODE*) p)->vnum; */
    else
        return NULL;

    return area_vnum_lookup(vnum);
}

bool touch_area(AREA_DATA *pArea)
{
    if (pArea)
        SET_BIT(pArea->flags, AREA_CHANGED);
    return FALSE;
}

bool touch_vnum(int vnum)
{
    return touch_area(area_vnum_lookup(vnum));
}

void edit_done(DESCRIPTOR_DATA *d)
{
    d->pEdit = NULL;
    d->olced = NULL;
}

/* Local functions */

/* lookup OLC editor by id */
olced_t *olced_lookup(const char * id)
{
    olced_t *olced;

    if (IS_NULLSTR(id))
        return NULL;

    for (olced = olced_table; olced->id; olced++)
        if (!str_prefix(id, olced->id))
            return olced;
    return NULL;
}

/* lookup cmd function by name */
static olc_cmd_t *olc_cmd_lookup(olc_cmd_t *cmd_table, const char *name)
{
    for (; cmd_table->name; cmd_table++)
        if (!str_prefix(name, cmd_table->name))
            return cmd_table;
    return NULL;
}

char* help_topics[FUN_MAX] =
{
    "'OLC CREATE'",
    "'OLC EDIT'",
    str_empty,
    "'OLC ASHOW'",
    "'OLC ALIST'"
};

static void do_olc(CHAR_DATA *ch, const char *argument, int fun)
{
    char command[MAX_INPUT_LENGTH];
    olced_t *olced;

    if (IS_NPC(ch))
        return;

    argument = one_argument(argument, command, sizeof(command));
    if ((olced = olced_lookup(command)) == NULL)
    {
        do_help(ch, help_topics[fun]);
        return;
    }

    if (ch->pcdata->security < olced->sec)
    {
        char_puts("Permission denied.\n", ch);
        return;
    }

    olced->cmd_table[fun].olc_fun(ch, argument, olced->cmd_table+fun);
}


bool validate_item_vnum (const char *argument)
{
    int              vnum;
    char             arg[MAX_STRING_LENGTH];
    OBJ_INDEX_DATA * obj;
    
    one_argument(argument, arg, sizeof(arg));
    
    vnum = atoi(arg);
    obj = get_obj_index(vnum);

    if ( obj == NULL)
    {
        return FALSE;
    }

    return TRUE;
}