zim/area/
zim/bin/
zim/clans/plists/
zim/corefiles/
zim/doc/muddy/
zim/gods/
zim/log/
zim/player/
zim/skill_tree/
zim/tmp/
/*-
 * Copyright (c) 1998 fjoe <fjoe@iclub.nsu.ru>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: olc.c 936 2006-11-20 04:50:44Z zsuzsu $
 */

/***************************************************************************
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 *                                                                         *
 *  This code was freely distributed with the The Isles 1.1 source code,   *
 *  and has been used here for OLC - OLC would not be what it is without   *
 *  all the previous coders who released their source code.                *
 *                                                                         *
 ***************************************************************************/

#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 OLC_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 SoG\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_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_MATERIAL[]= "material";

olced_t olced_table[] = {
	{ 0, ED_AREA,	"AreaEd",	olc_cmds_area	},
	{ 0, ED_ROOM,	"RoomEd",	olc_cmds_room	},
	{ 0, ED_OBJ,	"ObjEd",	olc_cmds_obj	},
	{ 0, ED_MOB,	"MobEd",	olc_cmds_mob	},
	{ 5, ED_MPCODE,	"MPEd",		olc_cmds_mpcode	},
	{ 0, ED_HELP,	"HelpEd",	olc_cmds_help	},
	{ 0, ED_MSG,	"MsgEd",	olc_cmds_msg	},
	{ 0, ED_CLAN,	"ClanEd",	olc_cmds_clan	},
	{ 0, ED_LANG,	"LangEd",	olc_cmds_lang	},
	{ 0, ED_IMPL,	"ImplRuleEd",	olc_cmds_impl	},
	{ 0, ED_EXPL,	"ExplRuleEd",	olc_cmds_expl	},
	{ 0, ED_SOC,	"SocEd",	olc_cmds_soc	},
	/*{ ED_CLASS,	"ClassEd",	olc_cmds_class	},*/
	{ 0, ED_RACE,	"RaceEd",	olc_cmds_race	},
	{ 0, ED_MATERIAL,"MaterialEd",	olc_cmds_material	},
	{ 0, NULL }
};

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

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

/* 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') {
		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 = cmd_lookup(olced->cmd_table+FUN_FIRST, command)) == NULL
	||  cmd->olc_fun == NULL)
		return FALSE;

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

	return TRUE;
}

void do_create(CHAR_DATA *ch, const char *argument)
{
	do_olc(ch, argument, FUN_CREATE);
}

void do_edit(CHAR_DATA *ch, const char *argument)
{
	do_olc(ch, argument, FUN_EDIT);
}

void do_alist(CHAR_DATA *ch, const char *argument)
{
	do_olc(ch, argument, FUN_LIST);
}

void do_ashow(CHAR_DATA *ch, const char *argument)
{
	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_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(pStr, arg, ch, OLCED(ch)->name);
	}

	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 FALSE;
	}

	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 FALSE;
}

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(&ed->keyword, arg, ch, OLCED(ch)->name);
		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;
}

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;

	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, "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_material(CHAR_DATA *ch, const char *argument,
		olc_cmd_t *cmd, material_t **matp)
{
	int imat = 0;
	int i = 0;

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

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


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

	*matp = MATERIAL(imat);
	char_printf(ch, "Material set to '{c%s{x'.\n", (*matp)->name);
	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_flag32(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;
}

/*****************************************************************************
 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++) {
		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(OLC_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
		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 *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;
	}

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