tinymux/mux/modules/engine/command.cpp
Stephen Dennis bc4cec252e fix(nls): plural morphology baked into msgids, and a fragment that could not be translated (#1717)
Found while translating mail (#1419).  The tell was a line wrong in English
before translation is even involved:

    MAIL: 1 messages in folder 0 [unnamed] (1 unread, 0 cleared).

MN_() exists for this (#1622) and #1655 converted the @mail/stats family;
this set was missed.  A translator cannot work around it -- the msgid has no
msgid_plural, so there is no second form for a catalogue to supply, and es
must pick one wording for all counts while ru and pl need three and can
express none of them.  The fix is in the C++, not the .po.

## Converted, 11 sites

mail.cpp, mail_mod.cpp   mailbox is full; URGENT MAIL urgent messages
walkdb.cpp               universe contains; objects @chowned; objects marked
object.cpp               objects @chowned to you
wiz.cpp                  You toaded ... objects @chowned
command.cpp              at most N players logged in

The mail module duplicates are converted alongside the engine's so the two
implementations do not drift (#1614).

## One was a different bug than the issue described

walkdb.cpp built its message as M_("%d objects %smarked") with %s = "" or
"un".  That is fragment assembly, not a missing plural: a translator handed
that %s cannot use it, because the prefix is English morphology rather than
a word.  Split into two whole sentences, each pluralised -- the #1575 /
#1588 rule.  Now renders "1 object marked" / "8 objects unmarked" instead of
a %s a catalogue has no way to fill.

## Two deliberately NOT converted

    MAIL: %d messages in folder %d [%s] (%d unread, %d cleared).
    %d objects = %d rooms, %d exits, %d things, %d players. (%d garbage)

MN_(s, p, n) selects one form from one n.  Four independent counts are four
independent plural decisions, and in Russian each noun needs its own form.
These want rewording, which is a design call rather than a mechanical
conversion, so they are left and recorded rather than half-fixed.

## Catalogue fallout

.pot 18 -> 27 plural entries; all three catalogues msgmerge'd.

xx is `complete` policy, so its nine new entries had to be filled and
unfuzzied or the guard fails -- which it duly did, first try.  msgmerge had
copied the old singular-only text into BOTH forms, so msgstr[0] said
"messages"; each form is now prefixed from its own msgid.

es and ko received real translations for all nine.

    es.po  190 -> 198/976  (20.3%)
    ko.po  229 -> 236/976  (24.2%)

## Verified end to end, at n=1 and n>1

    en   1 object marked      8 objects marked
    es   1 objeto marcado     8 objetos marcados
    ko   사물 1개를 표시했습니다   사물 8개를 표시했습니다
    xx   [xx] 1 object marked [xx] 8 objects marked

Korean renders identically for both, which is correct: it declares
nplurals=1, so the catalogue's own rule is being honoured rather than
English's imposed on it.

The unconverted multi-count line still reads "1 messages", as documented.

make test: Smoke 1561 x3, tests/nls 7, ko 4, plural 43.  TESTEXIT=0.

Refs #1419, #1575, #1588, #1614, #1622, #1655, #1702.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 21:31:32 +00:00

5363 lines
188 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*! \file command.cpp
* \brief Command parser and support routines.
*
* The functions here crack command lists into commands, decode switches, and
* match commands to built-in and softcoded commands. This is one of the
* three parsers in the server. The other two parsers are for functions
* (see eval.cpp) and locks (boolexp.cpp).
*/
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
#include "ganl_stub.h"
#include "walk.h"
#include "mux_table.h"
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
// Switch tables for the various commands.
//
static NAMETAB attrib_sw[] =
{
{T("access"), 1, CA_GOD, ATTRIB_ACCESS},
{T("delete"), 1, CA_GOD, ATTRIB_DELETE},
{T("rename"), 1, CA_GOD, ATTRIB_RENAME},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB boot_sw[] =
{
{T("port"), 1, CA_WIZARD, BOOT_PORT|SW_MULTIPLE},
{T("quiet"), 1, CA_WIZARD, BOOT_QUIET|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB break_sw[] =
{
{T("inline"), 1, CA_WIZARD, BREAK_INLINE},
{T("queued"), 1, CA_WIZARD, BREAK_QUEUED},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB cboot_sw[] =
{
{T("quiet"), 1, CA_PUBLIC, CBOOT_QUIET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB comtitle_sw[] =
{
{T("gag"), 1, CA_PUBLIC, COMTITLE_GAG},
{T("off"), 2, CA_PUBLIC, COMTITLE_OFF},
{T("on"), 2, CA_PUBLIC, COMTITLE_ON},
{T("ungag"), 1, CA_PUBLIC, COMTITLE_UNGAG},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB cemit_sw[] =
{
{T("noheader"), 1, CA_PUBLIC, CEMIT_NOHEADER},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB chown_sw[] =
{
{T("nostrip"), 1, CA_PUBLIC, CHOWN_NOSTRIP},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB clone_sw[] =
{
{T("cost"), 1, CA_PUBLIC, CLONE_SET_COST},
{T("inherit"), 3, CA_PUBLIC, CLONE_INHERIT|SW_MULTIPLE},
{T("inventory"), 3, CA_PUBLIC, CLONE_INVENTORY},
{T("location"), 1, CA_PUBLIC, CLONE_LOCATION},
{T("nostrip"), 2, CA_WIZARD, CLONE_NOSTRIP|SW_MULTIPLE},
{T("parent"), 2, CA_PUBLIC, CLONE_FROM_PARENT|SW_MULTIPLE},
{T("preserve"), 2, CA_WIZARD, CLONE_PRESERVE|SW_MULTIPLE},
{ nullptr, 0, 0, 0}
};
static NAMETAB clist_sw[] =
{
{T("full"), 0, CA_PUBLIC, CLIST_FULL},
{T("headers"), 0, CA_PUBLIC, CLIST_HEADERS},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB cset_sw[] =
{
{T("anon"), 1, CA_PUBLIC, CSET_SPOOF},
{T("header"), 1, CA_PUBLIC, CSET_HEADER},
{T("list"), 2, CA_PUBLIC, CSET_LIST},
{T("log") , 3, CA_PUBLIC, CSET_LOG},
{T("loud"), 3, CA_PUBLIC, CSET_LOUD},
{T("mute"), 1, CA_PUBLIC, CSET_QUIET},
{T("nospoof"), 1, CA_PUBLIC, CSET_NOSPOOF},
{T("object"), 1, CA_PUBLIC, CSET_OBJECT},
{T("private"), 2, CA_PUBLIC, CSET_PRIVATE},
{T("public"), 2, CA_PUBLIC, CSET_PUBLIC},
{T("quiet"), 1, CA_PUBLIC, CSET_QUIET},
{T("spoof"), 1, CA_PUBLIC, CSET_SPOOF},
{T("timestamp_logs"), 1, CA_PUBLIC, CSET_LOG_TIME},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB dbck_sw[] =
{
{T("full"), 1, CA_WIZARD, DBCK_FULL},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB decomp_sw[] =
{
{T("dbref"), 1, CA_PUBLIC, DECOMP_DBREF},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB destroy_sw[] =
{
{T("instant"), 4, CA_PUBLIC, DEST_INSTANT|SW_MULTIPLE},
{T("override"), 8, CA_PUBLIC, DEST_OVERRIDE|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB dig_sw[] =
{
{T("teleport"), 1, CA_PUBLIC, DIG_TELEPORT},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB doing_sw[] =
{
{T("header"), 1, CA_PUBLIC, DOING_HEADER},
{T("message"), 1, CA_PUBLIC, DOING_MESSAGE},
{T("poll"), 1, CA_PUBLIC, DOING_POLL},
{T("quiet"), 1, CA_PUBLIC, DOING_QUIET|SW_MULTIPLE},
{T("unique"), 1, CA_PUBLIC, DOING_UNIQUE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB dolist_sw[] =
{
{T("break"), 1, CA_PUBLIC, DOLIST_BREAK|SW_MULTIPLE},
{T("delimit"), 1, CA_PUBLIC, DOLIST_DELIMIT},
{T("notify"), 1, CA_PUBLIC, DOLIST_NOTIFY|SW_MULTIPLE},
{T("now"), 1, CA_WIZARD, DOLIST_NOW|SW_MULTIPLE},
{T("space"), 1, CA_PUBLIC, DOLIST_SPACE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB query_sw[] =
{
{T("sql"), 1, CA_PUBLIC, QUERY_SQL},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB drop_sw[] =
{
{T("quiet"), 1, CA_PUBLIC, DROP_QUIET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB dump_sw[] =
{
{T("flatfile"), 1, CA_WIZARD, DUMP_FLATFILE|SW_MULTIPLE},
{T("structure"), 1, CA_WIZARD, DUMP_STRUCT|SW_MULTIPLE},
{T("text"), 1, CA_WIZARD, DUMP_TEXT|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB emit_sw[] =
{
{T("here"), 2, CA_PUBLIC, SAY_HERE|SW_MULTIPLE},
{T("html"), 2, CA_PUBLIC, SAY_HTML|SW_MULTIPLE},
{T("room"), 1, CA_PUBLIC, SAY_ROOM|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB enter_sw[] =
{
{T("quiet"), 1, CA_PUBLIC, MOVE_QUIET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB examine_sw[] =
{
{T("brief"), 1, CA_PUBLIC, EXAM_BRIEF},
{T("debug"), 1, CA_WIZARD, EXAM_DEBUG},
{T("full"), 1, CA_PUBLIC, EXAM_LONG},
{T("parent"), 1, CA_PUBLIC, EXAM_PARENT},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB femit_sw[] =
{
{T("here"), 1, CA_PUBLIC, PEMIT_HERE|SW_MULTIPLE},
{T("room"), 1, CA_PUBLIC, PEMIT_ROOM|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB fixdb_sw[] =
{
{T("contents"), 1, CA_GOD, FIXDB_CON},
{T("exits"), 1, CA_GOD, FIXDB_EXITS},
{T("location"), 1, CA_GOD, FIXDB_LOC},
{T("next"), 1, CA_GOD, FIXDB_NEXT},
{T("owner"), 1, CA_GOD, FIXDB_OWNER},
{T("pennies"), 1, CA_GOD, FIXDB_PENNIES},
{T("rename"), 1, CA_GOD, FIXDB_NAME},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB flag_sw[] =
{
{T("remove"), 1, CA_GOD, FLAG_REMOVE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB folder_sw[] =
{
{T("file"), 1, CA_PUBLIC, FOLDER_FILE},
{T("list"), 1, CA_PUBLIC, FOLDER_LIST},
{T("read"), 1, CA_PUBLIC, FOLDER_READ},
{T("set"), 1, CA_PUBLIC, FOLDER_SET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB force_sw[] =
{
{T("now"), 1, CA_WIZARD, FORCE_NOW},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB fpose_sw[] =
{
{T("default"), 1, CA_PUBLIC, 0},
{T("nospace"), 1, CA_PUBLIC, SAY_NOSPACE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB function_sw[] =
{
{T("delete"), 1, CA_WIZARD, FN_DELETE},
{T("list"), 1, CA_WIZARD, FN_LIST},
{T("preserve"), 3, CA_WIZARD, FN_PRES|SW_MULTIPLE},
{T("privileged"), 3, CA_WIZARD, FN_PRIV|SW_MULTIPLE},
{T("restrict"), 1, CA_WIZARD, FN_RESTRICT|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB get_sw[] =
{
{T("quiet"), 1, CA_PUBLIC, GET_QUIET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB give_sw[] =
{
{T("quiet"), 1, CA_WIZARD, GIVE_QUIET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB goto_sw[] =
{
{T("quiet"), 1, CA_PUBLIC, MOVE_QUIET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB halt_sw[] =
{
{T("all"), 1, CA_PUBLIC, HALT_ALL},
{T("pid"), 1, CA_PUBLIC, HALT_PID},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB hook_sw[] =
{
{T("after"), 3, CA_GOD, CEF_HOOK_AFTER},
{T("before"), 3, CA_GOD, CEF_HOOK_BEFORE},
{T("clear"), 3, CA_GOD, CEF_HOOK_CLEAR|SW_MULTIPLE},
{T("fail"), 1, CA_GOD, CEF_HOOK_AFAIL},
{T("ignore"), 3, CA_GOD, CEF_HOOK_IGNORE},
{T("igswitch"), 3, CA_GOD, CEF_HOOK_IGSWITCH},
{T("list"), 3, CA_GOD, CEF_HOOK_LIST},
{T("permit"), 3, CA_GOD, CEF_HOOK_PERMIT},
{T("args"), 3, CA_GOD, CEF_HOOK_ARGS},
{nullptr, 0, 0, 0}
};
static NAMETAB icmd_sw[] =
{
{T("check"), 2, CA_GOD, ICMD_CHECK},
{T("clear"), 2, CA_GOD, ICMD_CLEAR},
{T("croom"), 2, CA_GOD, ICMD_CROOM},
{T("disable"), 1, CA_GOD, ICMD_DISABLE},
{T("droom"), 2, CA_GOD, ICMD_DROOM},
{T("ignore"), 1, CA_GOD, ICMD_IGNORE},
{T("iroom"), 2, CA_GOD, ICMD_IROOM},
{T("lroom"), 2, CA_GOD, ICMD_LROOM},
{T("lallroom"), 2, CA_GOD, ICMD_LALLROOM},
{T("off"), 2, CA_GOD, ICMD_OFF},
{T("on"), 2, CA_GOD, ICMD_ON},
{nullptr, 0, 0, 0}
};
static NAMETAB leave_sw[] =
{
{T("quiet"), 1, CA_PUBLIC, MOVE_QUIET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB listmotd_sw[] =
{
{T("brief"), 1, CA_WIZARD, MOTD_BRIEF},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
NAMETAB lock_sw[] =
{
{T("defaultlock"), 1, CA_PUBLIC, A_LOCK},
{T("droplock"), 1, CA_PUBLIC, A_LDROP},
{T("enterlock"), 1, CA_PUBLIC, A_LENTER},
{T("getfromlock"), 1, CA_PUBLIC, A_LGET},
{T("givelock"), 1, CA_PUBLIC, A_LGIVE},
{T("leavelock"), 2, CA_PUBLIC, A_LLEAVE},
{T("linklock"), 2, CA_PUBLIC, A_LLINK},
{T("maillock"), 1, CA_PUBLIC, A_LMAIL},
{T("openlock"), 1, CA_PUBLIC, A_LOPEN},
{T("pagelock"), 3, CA_PUBLIC, A_LPAGE},
{T("parentlock"), 3, CA_PUBLIC, A_LPARENT},
{T("receivelock"), 1, CA_PUBLIC, A_LRECEIVE},
{T("speechlock"), 1, CA_PUBLIC, A_LSPEECH},
{T("teloutlock"), 2, CA_PUBLIC, A_LTELOUT},
{T("tportlock"), 2, CA_PUBLIC, A_LTPORT},
{T("uselock"), 1, CA_PUBLIC, A_LUSE},
{T("userlock"), 4, CA_PUBLIC, A_LUSER},
{T("visiblelock"), 1, CA_PUBLIC, A_LVISIBLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB look_sw[] =
{
{T("outside"), 1, CA_PUBLIC, LOOK_OUTSIDE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB mail_sw[] =
{
{T("abort"), 2, CA_PUBLIC, MAIL_ABORT},
{T("alias"), 4, CA_PUBLIC, MAIL_ALIAS},
{T("alist"), 4, CA_PUBLIC, MAIL_ALIST},
{T("bcc"), 1, CA_PUBLIC, MAIL_BCC},
{T("cc"), 2, CA_PUBLIC, MAIL_CC},
{T("clear"), 2, CA_PUBLIC, MAIL_CLEAR},
{T("debug"), 2, CA_PUBLIC, MAIL_DEBUG},
{T("dstats"), 2, CA_PUBLIC, MAIL_DSTATS},
{T("edit"), 1, CA_PUBLIC, MAIL_EDIT},
{T("file"), 2, CA_PUBLIC, MAIL_FILE},
{T("folder"), 3, CA_PUBLIC, MAIL_FOLDER},
{T("forward"), 3, CA_PUBLIC, MAIL_FORWARD},
{T("fstats"), 2, CA_PUBLIC, MAIL_FSTATS},
{T("fwd"), 2, CA_PUBLIC, MAIL_FORWARD},
{T("list"), 1, CA_PUBLIC, MAIL_LIST},
{T("next"), 3, CA_PUBLIC, MAIL_NEXT},
{T("nuke"), 1, CA_PUBLIC, MAIL_NUKE},
{T("proof"), 2, CA_PUBLIC, MAIL_PROOF},
{T("purge"), 2, CA_PUBLIC, MAIL_PURGE},
{T("quick"), 3, CA_PUBLIC, MAIL_QUICK},
{T("quote"), 3, CA_PUBLIC, MAIL_QUOTE|SW_MULTIPLE},
{T("read"), 3, CA_PUBLIC, MAIL_READ},
{T("reply"), 3, CA_PUBLIC, MAIL_REPLY},
{T("replyall"), 6, CA_PUBLIC, MAIL_REPLYALL},
{T("retract"), 3, CA_PUBLIC, MAIL_RETRACT},
{T("review"), 3, CA_PUBLIC, MAIL_REVIEW},
{T("safe"), 2, CA_PUBLIC, MAIL_SAFE},
{T("send"), 2, CA_PUBLIC, MAIL_SEND},
{T("stats"), 2, CA_PUBLIC, MAIL_STATS},
{T("tag"), 1, CA_PUBLIC, MAIL_TAG},
{T("unclear"), 3, CA_PUBLIC, MAIL_UNCLEAR},
{T("unsafe"), 3, CA_PUBLIC, MAIL_UNSAFE},
{T("untag"), 3, CA_PUBLIC, MAIL_UNTAG},
{T("urgent"), 2, CA_PUBLIC, MAIL_URGENT},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB malias_sw[] =
{
{T("add"), 1, CA_PUBLIC, MALIAS_ADD},
{T("chown"), 1, CA_PUBLIC, MALIAS_CHOWN},
{T("desc"), 1, CA_PUBLIC, MALIAS_DESC},
{T("delete"), 1, CA_PUBLIC, MALIAS_DELETE},
{T("list"), 1, CA_PUBLIC, MALIAS_LIST},
{T("remove"), 1, CA_PUBLIC, MALIAS_REMOVE},
{T("rename"), 1, CA_PUBLIC, MALIAS_RENAME},
{T("status"), 1, CA_PUBLIC, MALIAS_STATUS},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB mark_sw[] =
{
{T("clear"), 1, CA_PUBLIC, MARK_CLEAR},
{T("set"), 1, CA_PUBLIC, MARK_SET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB markall_sw[] =
{
{T("clear"), 1, CA_PUBLIC, MARK_CLEAR},
{T("set"), 1, CA_PUBLIC, MARK_SET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB motd_sw[] =
{
{T("brief"), 1, CA_WIZARD, MOTD_BRIEF|SW_MULTIPLE},
{T("connect"), 1, CA_WIZARD, MOTD_ALL},
{T("down"), 1, CA_WIZARD, MOTD_DOWN},
{T("full"), 1, CA_WIZARD, MOTD_FULL},
{T("list"), 1, CA_PUBLIC, MOTD_LIST},
{T("wizard"), 1, CA_WIZARD, MOTD_WIZ},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB notify_sw[] =
{
{T("all"), 1, CA_PUBLIC, NFY_NFYALL},
{T("first"), 1, CA_PUBLIC, NFY_NFY},
{T("quiet"), 1, CA_PUBLIC, NFY_QUIET|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB open_sw[] =
{
{T("inventory"), 1, CA_PUBLIC, OPEN_INVENTORY},
{T("location"), 1, CA_PUBLIC, OPEN_LOCATION},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB page_sw[] =
{
{T("blind"), 1, CA_PUBLIC, PAGE_BLIND|SW_MULTIPLE},
{T("noeval"), 1, CA_PUBLIC, SW_NOEVAL|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB addcommand_sw[] =
{
{T("noeval"), 1, CA_GOD, ADDCMD_NOEVAL|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB oemit_sw[] =
{
{T("list"), 1, CA_PUBLIC, PEMIT_LIST|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB pemit_sw[] =
{
{T("contents"), 1, CA_PUBLIC, PEMIT_CONTENTS|SW_MULTIPLE},
{T("html"), 1, CA_PUBLIC, PEMIT_HTML|SW_MULTIPLE},
{T("list"), 1, CA_PUBLIC, PEMIT_LIST|SW_MULTIPLE},
{T("noeval"), 1, CA_PUBLIC, SW_NOEVAL|SW_MULTIPLE},
{T("object"), 1, CA_PUBLIC, 0},
{T("silent"), 1, CA_PUBLIC, 0},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB pose_sw[] =
{
{T("default"), 1, CA_PUBLIC, 0},
{T("noeval"), 3, CA_PUBLIC, SW_NOEVAL|SW_MULTIPLE},
{T("nospace"), 3, CA_PUBLIC, SAY_NOSPACE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB ps_sw[] =
{
{T("all"), 1, CA_PUBLIC, PS_ALL|SW_MULTIPLE},
{T("brief"), 1, CA_PUBLIC, PS_BRIEF},
{T("long"), 1, CA_PUBLIC, PS_LONG},
{T("summary"), 1, CA_PUBLIC, PS_SUMM},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB quota_sw[] =
{
{T("all"), 1, CA_GOD, QUOTA_ALL|SW_MULTIPLE},
{T("fix"), 1, CA_WIZARD, QUOTA_FIX},
{T("remaining"), 1, CA_WIZARD, QUOTA_REM|SW_MULTIPLE},
{T("set"), 1, CA_WIZARD, QUOTA_SET},
{T("total"), 1, CA_WIZARD, QUOTA_TOT|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB protect_sw[] =
{
{T("add"), 2, CA_PUBLIC, PROTECT_ADD},
{T("alias"), 2, CA_PUBLIC, PROTECT_ALIAS},
{T("all"), 3, CA_WIZARD, PROTECT_ALL},
{T("del"), 1, CA_PUBLIC, PROTECT_DEL},
{T("list"), 1, CA_PUBLIC, PROTECT_LIST},
{T("unalias"), 1, CA_PUBLIC, PROTECT_UNALIAS},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB reference_sw[] =
{
{T("list"), 1, CA_PUBLIC, REFERENCE_LIST},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB say_sw[] =
{
{T("noeval"), 1, CA_PUBLIC, SAY_NOEVAL|SW_NOEVAL|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB set_sw[] =
{
{T("quiet"), 1, CA_PUBLIC, SET_QUIET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB stats_sw[] =
{
{T("all"), 1, CA_PUBLIC, STAT_ALL},
{T("me"), 1, CA_PUBLIC, STAT_ME},
{T("player"), 1, CA_PUBLIC, STAT_PLAYER},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB scan_sw[] =
{
{T("globals"), 1, CA_PUBLIC, SCAN_GLOBALS|SW_MULTIPLE},
{T("room"), 1, CA_PUBLIC, SCAN_ROOM|SW_MULTIPLE},
{T("self"), 1, CA_PUBLIC, SCAN_SELF|SW_MULTIPLE},
{T("zone"), 1, CA_PUBLIC, SCAN_ZONE|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB sweep_sw[] =
{
{T("commands"), 3, CA_PUBLIC, SWEEP_COMMANDS|SW_MULTIPLE},
{T("connected"), 3, CA_PUBLIC, SWEEP_CONNECT|SW_MULTIPLE},
{T("exits"), 1, CA_PUBLIC, SWEEP_EXITS|SW_MULTIPLE},
{T("here"), 1, CA_PUBLIC, SWEEP_HERE|SW_MULTIPLE},
{T("inventory"), 1, CA_PUBLIC, SWEEP_ME|SW_MULTIPLE},
{T("listeners"), 1, CA_PUBLIC, SWEEP_LISTEN|SW_MULTIPLE},
{T("players"), 1, CA_PUBLIC, SWEEP_PLAYER|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB switch_sw[] =
{
{T("all"), 1, CA_PUBLIC, SWITCH_ANY},
{T("default"), 1, CA_PUBLIC, SWITCH_DEFAULT},
{T("first"), 1, CA_PUBLIC, SWITCH_ONE},
{T("notify"), 1, CA_PUBLIC, SWITCH_NOTIFY|SW_MULTIPLE},
{T("now"), 1, CA_WIZARD, SWITCH_NOW|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB teleport_sw[] =
{
{T("list"), 1, CA_PUBLIC, TELEPORT_LIST|SW_MULTIPLE},
{T("loud"), 1, CA_PUBLIC, TELEPORT_DEFAULT},
{T("quiet"), 1, CA_PUBLIC, TELEPORT_QUIET},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB timecheck_sw[] =
{
{T("log"), 1, CA_WIZARD, TIMECHK_LOG | SW_MULTIPLE},
{T("reset"), 1, CA_WIZARD, TIMECHK_RESET | SW_MULTIPLE},
{T("screen"), 1, CA_WIZARD, TIMECHK_SCREEN | SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB toad_sw[] =
{
{T("no_chown"), 1, CA_WIZARD, TOAD_NO_CHOWN|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB walk_sw[] =
{
{T("stop"), 1, CA_PUBLIC, WALK_STOP},
{T("quiet"), 1, CA_PUBLIC, WALK_QUIET|SW_MULTIPLE},
{T("locked"), 1, CA_PUBLIC, WALK_LOCKED|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB include_sw[] =
{
{T("nobreak"), 1, CA_PUBLIC, INCLUDE_NOBREAK},
{T("localize"), 1, CA_PUBLIC, INCLUDE_LOCALIZE|SW_MULTIPLE},
{T("clearregs"), 1, CA_PUBLIC, INCLUDE_CLEARREGS|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB trig_sw[] =
{
{T("quiet"), 1, CA_PUBLIC, TRIG_QUIET},
{T("notify"), 1, CA_PUBLIC, TRIG_NOTIFY|SW_MULTIPLE},
{T("now"), 1, CA_WIZARD, TRIG_NOW|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB wait_sw[] =
{
{T("until"), 1, CA_PUBLIC, WAIT_UNTIL},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB verb_sw[] =
{
{T("no_name"), 3, CA_PUBLIC, VERB_NONAME},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB wall_sw[] =
{
{T("admin"), 1, CA_ADMIN, SHOUT_ADMIN|SW_MULTIPLE},
{T("emit"), 1, CA_ANNOUNCE, SHOUT_EMIT|SW_MULTIPLE},
{T("no_prefix"), 1, CA_ANNOUNCE, SHOUT_NOTAG|SW_MULTIPLE},
{T("pose"), 1, CA_ANNOUNCE, SHOUT_POSE|SW_MULTIPLE},
{T("wizard"), 1, CA_ANNOUNCE, SHOUT_WIZARD|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB warp_sw[] =
{
{T("check"), 1, CA_WIZARD, TWARP_CLEAN|SW_MULTIPLE},
{T("dump"), 1, CA_WIZARD, TWARP_DUMP|SW_MULTIPLE},
{T("events"), 1, CA_WIZARD, TWARP_EVENTS|SW_MULTIPLE},
{T("idle"), 1, CA_WIZARD, TWARP_IDLE|SW_MULTIPLE},
{T("queue"), 1, CA_WIZARD, TWARP_QUEUE|SW_MULTIPLE},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static NAMETAB lua_sw[] =
{
{T("inline"), 1, CA_WIZARD, LUA_INLINE},
{T("stats"), 1, CA_PUBLIC, LUA_STATS},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
// @lua handler — execute Lua scripts or show stats.
//
static void do_lua(dbref executor, dbref caller, dbref enactor, int eval,
int key, UTF8 *arg1, const UTF8 *cargs[], int ncargs)
{
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
if (nullptr == mudstate.pILuaControl)
{
notify(executor, M_("Lua module is not loaded."));
return;
}
if (LUA_STATS == key)
{
size_t nCalls, nErrors, nInsnHits, nMemHits, nBytes;
size_t nCacheHits, nCacheMisses, nCacheEntries;
mudstate.pILuaControl->GetStats(&nCalls, &nErrors,
&nInsnHits, &nMemHits, &nBytes,
&nCacheHits, &nCacheMisses, &nCacheEntries);
notify(executor, tprintf(M_("Lua stats: %zu calls, %zu errors, "
"%zu insn-limit hits, %zu mem-limit hits, %zu bytes used"),
nCalls, nErrors, nInsnHits, nMemHits, nBytes));
notify(executor, tprintf(M_("Cache: %zu entries, %zu hits, %zu misses"),
nCacheEntries, nCacheHits, nCacheMisses));
return;
}
if (LUA_INLINE == key)
{
if (!Wizard(executor))
{
notify(executor, M_("Permission denied."));
return;
}
UTF8 result[8000];
size_t nResult = 0;
mudstate.pILuaControl->Eval(executor, caller, enactor,
arg1, strlen(reinterpret_cast<const char *>(arg1)),
result, sizeof(result), &nResult);
notify(executor, result);
return;
}
// Default: @lua obj/attr — execute a Lua script on an object.
//
UTF8 *pSlash = reinterpret_cast<UTF8 *>(
const_cast<char *>(strchr(reinterpret_cast<const char *>(arg1), '/')));
if (nullptr == pSlash)
{
notify(executor, M_("Usage: @lua <object>/<attribute>"));
return;
}
*pSlash = '\0';
UTF8 *pAttrName = pSlash + 1;
dbref obj = lookup_player(executor, arg1, true);
if (NOTHING == obj)
{
init_match(executor, arg1, NOTYPE);
match_everything(MAT_EXIT_PARENTS);
obj = match_result();
}
if (!Good_obj(obj))
{
notify(executor, M_("No such object."));
return;
}
UTF8 result[8000];
size_t nResult = 0;
mudstate.pILuaControl->CallAttr(executor, caller, enactor,
obj, pAttrName, nullptr, 0,
result, sizeof(result), &nResult);
notify(executor, result);
}
/* ---------------------------------------------------------------------------
* Command table: Definitions for builtin commands, used to build the command
* hash table.
*
* Format: Name Switches Permissions Needed
* Key (if any) Calling Seq Handler
*/
static CMDENT_NO_ARG command_table_no_arg[] =
{
{T("@@"), nullptr, CA_PUBLIC, 0, CS_NO_ARGS, 0, do_comment},
{T("@dbck"), dbck_sw, CA_WIZARD, 0, CS_NO_ARGS, 0, do_dbck},
{T("@dbclean"), nullptr, CA_GOD, 0, CS_NO_ARGS, 0, do_dbclean},
{T("@dump"), dump_sw, CA_WIZARD, 0, CS_NO_ARGS, 0, do_dump},
{T("@logrotate"), nullptr, CA_WIZARD, 0, CS_NO_ARGS, 0, do_logrotate},
{T("@mark_all"), markall_sw, CA_WIZARD, MARK_SET, CS_NO_ARGS, 0, do_markall},
{T("@readcache"), nullptr, CA_WIZARD, 0, CS_NO_ARGS, 0, do_readcache},
{T("@restart"), nullptr, CA_NO_GUEST|CA_NO_SLAVE, 0, CS_NO_ARGS, 0, do_restart},
{T("@startslave"), nullptr, CA_WIZARD, 0, CS_NO_ARGS, 0, do_startslave},
{T("@timecheck"), timecheck_sw, CA_WIZARD, 0, CS_NO_ARGS, 0, do_timecheck},
{T("clearcom"), nullptr, CA_NO_SLAVE, 0, CS_NO_ARGS, 0, do_clearcom},
{T("help"), nullptr, CA_PUBLIC, CMD_HELP, CS_NO_ARGS, 0, logged_out0},
{T("info"), nullptr, CA_PUBLIC, CMD_INFO, CS_NO_ARGS, 0, logged_out0},
{T("inventory"), nullptr, CA_PUBLIC, 0, CS_NO_ARGS, 0, do_inventory},
{T("leave"), leave_sw, CA_LOCATION, 0, CS_NO_ARGS, 0, do_leave},
{T("logout"), nullptr, CA_PUBLIC, CMD_LOGOUT, CS_NO_ARGS, 0, logged_out0},
{T("quit"), nullptr, CA_PUBLIC, CMD_QUIT, CS_NO_ARGS, 0, logged_out0},
{T("report"), nullptr, CA_PUBLIC, 0, CS_NO_ARGS, 0, do_report},
{T("score"), nullptr, CA_PUBLIC, 0, CS_NO_ARGS, 0, do_score},
{T("version"), nullptr, CA_PUBLIC, 0, CS_NO_ARGS, 0, do_version},
{static_cast<UTF8*>(nullptr), nullptr, 0, 0, 0, 0, nullptr}
};
static CMDENT_ONE_ARG command_table_one_arg[] =
{
{T("@apply_marked"), nullptr, CA_WIZARD|CA_GBL_INTERP, 0, CS_ONE_ARG|CS_CMDARG|CS_NOINTERP|CS_STRIP_AROUND, 0, do_apply_marked},
{T("@boot"), boot_sw, CA_NO_GUEST|CA_NO_SLAVE, 0, CS_ONE_ARG|CS_INTERP, 0, do_boot},
{T("@ccreate"), nullptr, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_ONE_ARG, 0, do_createchannel},
{T("@cdestroy"), nullptr, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_ONE_ARG, 0, do_destroychannel},
{T("@clist"), clist_sw, CA_NO_SLAVE, 0, CS_ONE_ARG, 0, do_chanlist},
{T("@cut"), nullptr, CA_WIZARD|CA_LOCATION, 0, CS_ONE_ARG|CS_INTERP, 0, do_cut},
{T("@cwho"), nullptr, CA_NO_SLAVE, 0, CS_ONE_ARG, 0, do_channelwho},
{T("@crondel"), nullptr, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_ONE_ARG|CS_INTERP, 0, do_crondel},
{T("@crontab"), nullptr, CA_NO_SLAVE, 0, CS_ONE_ARG, 0, do_crontab},
{T("@destroy"), destroy_sw, CA_NO_SLAVE|CA_NO_GUEST|CA_GBL_BUILD, DEST_ONE, CS_ONE_ARG|CS_INTERP, 0, do_destroy},
{T("@disable"), nullptr, CA_WIZARD, GLOB_DISABLE, CS_ONE_ARG, 0, do_global},
{T("@doing"), doing_sw, CA_PUBLIC, 0, CS_ONE_ARG, 0, do_doing},
{T("@emit"), emit_sw, CA_LOCATION|CA_NO_GUEST|CA_NO_SLAVE, SAY_EMIT, CS_ONE_ARG|CS_INTERP, 0, do_say},
{T("@enable"), nullptr, CA_WIZARD, GLOB_ENABLE, CS_ONE_ARG, 0, do_global},
{T("@entrances"), nullptr, CA_NO_GUEST, 0, CS_ONE_ARG|CS_INTERP, 0, do_entrances},
{T("@eval"), nullptr, CA_NO_SLAVE, 0, CS_ONE_ARG|CS_INTERP, 0, do_eval},
{T("@find"), nullptr, CA_PUBLIC, 0, CS_ONE_ARG|CS_INTERP, 0, do_find},
{T("@halt"), halt_sw, CA_NO_SLAVE, 0, CS_ONE_ARG|CS_INTERP, 0, do_halt},
{T("@hook"), hook_sw, CA_GOD, 0, CS_ONE_ARG|CS_NOINTERP, 0, do_hook},
{T("@kick"), nullptr, CA_WIZARD, QUEUE_KICK, CS_ONE_ARG|CS_INTERP, 0, do_queue},
{T("@last"), nullptr, CA_NO_GUEST, 0, CS_ONE_ARG|CS_INTERP, 0, do_last},
{T("@list"), nullptr, CA_PUBLIC, 0, CS_ONE_ARG|CS_INTERP, 0, do_list},
{T("@lua"), lua_sw, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_ONE_ARG|CS_INTERP, 0, do_lua},
{T("@list_file"), nullptr, CA_WIZARD, 0, CS_ONE_ARG|CS_INTERP, 0, do_list_file},
{T("@listcommands"), nullptr, CA_GOD, 0, CS_ONE_ARG, 0, do_listcommands},
{T("@listmotd"), listmotd_sw,CA_PUBLIC, MOTD_LIST, CS_ONE_ARG, 0, do_motd},
{T("@mark"), mark_sw, CA_WIZARD, SRCH_MARK, CS_ONE_ARG|CS_NOINTERP, 0, do_search},
{T("@motd"), motd_sw, CA_WIZARD, 0, CS_ONE_ARG, 0, do_motd},
{T("@nemit"), emit_sw, CA_LOCATION|CA_NO_GUEST|CA_NO_SLAVE, SAY_EMIT, CS_ONE_ARG|CS_UNPARSE|CS_NOSQUISH, 0, do_say},
{T("@poor"), nullptr, CA_GOD, 0, CS_ONE_ARG|CS_INTERP, 0, do_poor},
{T("@ps"), ps_sw, CA_PUBLIC, 0, CS_ONE_ARG|CS_INTERP, 0, do_ps},
{T("@quitprogram"), nullptr, CA_PUBLIC, 0, CS_ONE_ARG|CS_INTERP, 0, do_quitprog},
{T("@scan"), scan_sw, CA_PUBLIC, 0, CS_ONE_ARG|CS_NOINTERP, 0, do_scan},
{T("@search"), nullptr, CA_PUBLIC, SRCH_SEARCH, CS_ONE_ARG|CS_NOINTERP, 0, do_search},
{T("@shutdown"), nullptr, CA_NO_GUEST|CA_NO_SLAVE, 0, CS_ONE_ARG, 0, do_shutdown},
{T("@stats"), stats_sw, CA_PUBLIC, 0, CS_ONE_ARG|CS_INTERP, 0, do_stats},
{T("@sweep"), sweep_sw, CA_PUBLIC, 0, CS_ONE_ARG, 0, do_sweep},
{T("@timewarp"), warp_sw, CA_WIZARD, 0, CS_ONE_ARG|CS_INTERP, 0, do_timewarp},
{T("@unlink"), nullptr, CA_NO_SLAVE|CA_GBL_BUILD, 0, CS_ONE_ARG|CS_INTERP, 0, do_unlink},
{T("@unlock"), lock_sw, CA_NO_SLAVE, 0, CS_ONE_ARG|CS_INTERP, 0, do_unlock},
{T("@wall"), wall_sw, CA_ANNOUNCE, SHOUT_DEFAULT, CS_ONE_ARG|CS_INTERP, 0, do_shout},
{T("@wipe"), nullptr, CA_NO_SLAVE|CA_NO_GUEST|CA_GBL_BUILD, 0, CS_ONE_ARG|CS_INTERP, 0, do_wipe},
{T("allcom"), nullptr, CA_NO_SLAVE, 0, CS_ONE_ARG, 0, do_allcom},
{T("comlist"), nullptr, CA_NO_SLAVE, 0, CS_ONE_ARG, 0, do_comlist},
{T("delcom"), nullptr, CA_NO_SLAVE, 0, CS_ONE_ARG, 0, do_delcom},
{T("doing"), nullptr, CA_PUBLIC, CMD_DOING, CS_ONE_ARG, 0, logged_out1},
{T("drop"), drop_sw, CA_NO_SLAVE|CA_CONTENTS|CA_LOCATION|CA_NO_GUEST, 0, CS_ONE_ARG|CS_INTERP, 0, do_drop},
{T("enter"), enter_sw, CA_LOCATION, 0, CS_ONE_ARG|CS_INTERP, 0, do_enter},
{T("examine"), examine_sw, CA_PUBLIC, 0, CS_ONE_ARG|CS_INTERP, 0, do_examine},
{T("get"), get_sw, CA_LOCATION|CA_NO_GUEST, 0, CS_ONE_ARG|CS_INTERP, 0, do_get},
{T("goto"), goto_sw, CA_LOCATION, 0, CS_ONE_ARG|CS_INTERP, 0, do_move},
{T("look"), look_sw, CA_LOCATION, LOOK_LOOK, CS_ONE_ARG|CS_INTERP, 0, do_look},
{T("outputprefix"), nullptr, CA_PUBLIC, CMD_PREFIX, CS_ONE_ARG, 0, logged_out1},
{T("outputsuffix"), nullptr, CA_PUBLIC, CMD_SUFFIX, CS_ONE_ARG, 0, logged_out1},
{T("pose"), pose_sw, CA_LOCATION|CA_NO_SLAVE, SAY_POSE, CS_ONE_ARG|CS_INTERP, 0, do_say},
{T("puebloclient"), nullptr, CA_PUBLIC, CMD_PUEBLOCLIENT, CS_ONE_ARG, 0, logged_out1},
{T("say"), say_sw, CA_LOCATION|CA_NO_SLAVE, SAY_SAY, CS_ONE_ARG|CS_INTERP, 0, do_say},
{T("session"), nullptr, CA_PUBLIC, CMD_SESSION, CS_ONE_ARG, 0, logged_out1},
{T("think"), nullptr, CA_NO_SLAVE, 0, CS_ONE_ARG, 0, do_think},
{T("train"), nullptr, CA_PUBLIC, 0, CS_ONE_ARG, 0, do_train},
{T("use"), nullptr, CA_NO_SLAVE|CA_GBL_INTERP, 0, CS_ONE_ARG|CS_INTERP, 0, do_use},
{T("who"), nullptr, CA_PUBLIC, CMD_WHO, CS_ONE_ARG, 0, logged_out1},
{T("\\"), nullptr, CA_NO_GUEST|CA_LOCATION|CF_DARK|CA_NO_SLAVE, SAY_PREFIX, CS_ONE_ARG|CS_INTERP|CS_LEADIN, 0, do_say},
{T(":"), nullptr, CA_LOCATION|CF_DARK|CA_NO_SLAVE, SAY_PREFIX, CS_ONE_ARG|CS_INTERP|CS_LEADIN, 0, do_say},
{T(";"), nullptr, CA_LOCATION|CF_DARK|CA_NO_SLAVE, SAY_PREFIX, CS_ONE_ARG|CS_INTERP|CS_LEADIN, 0, do_say},
{T("\""), nullptr, CA_LOCATION|CF_DARK|CA_NO_SLAVE, SAY_PREFIX, CS_ONE_ARG|CS_INTERP|CS_LEADIN, 0, do_say},
{T("-"), nullptr, CA_NO_GUEST|CA_NO_SLAVE|CF_DARK, 0, CS_ONE_ARG|CS_LEADIN, 0, do_postpend},
{T("~"), nullptr, CA_NO_GUEST|CA_NO_SLAVE|CF_DARK, 0, CS_ONE_ARG|CS_LEADIN, 0, do_prepend},
{T("#"), nullptr, CA_NO_SLAVE|CA_GBL_INTERP|CF_DARK, 0, CS_ONE_ARG|CS_INTERP|CS_CMDARG, 0, do_force_prefixed},
{static_cast<UTF8*>(nullptr), nullptr, 0, 0, 0, 0, nullptr}
};
static CMDENT_TWO_ARG command_table_two_arg[] =
{
{T("@addcommand"), addcommand_sw, CA_GOD, 0, CS_TWO_ARG, 0, do_addcommand},
{T("@admin"), nullptr, CA_WIZARD, 0, CS_TWO_ARG|CS_INTERP, 0, do_admin},
{T("@alias"), nullptr, CA_NO_GUEST|CA_NO_SLAVE, 0, CS_TWO_ARG, 0, do_alias},
{T("@assert"), break_sw, CA_PUBLIC, 0, CS_TWO_ARG|CS_CMDARG|CS_NOINTERP|CS_STRIP_AROUND, 0, do_assert},
{T("@attribute"), attrib_sw, CA_GOD, 0, CS_TWO_ARG|CS_INTERP, 0, do_attribute},
{T("@break"), break_sw, CA_PUBLIC, 0, CS_TWO_ARG|CS_CMDARG|CS_NOINTERP|CS_STRIP_AROUND, 0, do_break},
{T("@cboot"), cboot_sw, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG, 0, do_chboot},
{T("@ccharge"), nullptr, CA_NO_SLAVE|CA_NO_GUEST, EDIT_CHANNEL_CCHARGE, CS_TWO_ARG, 0, do_editchannel},
{T("@cchown"), nullptr, CA_NO_SLAVE|CA_NO_GUEST, EDIT_CHANNEL_CCHOWN, CS_TWO_ARG, 0, do_editchannel},
{T("@cemit"), cemit_sw, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG, 0, do_cemit},
{T("@chown"), chown_sw, CA_NO_SLAVE|CA_NO_GUEST|CA_GBL_BUILD, CHOWN_ONE, CS_TWO_ARG|CS_INTERP, 0, do_chown},
{T("@chownall"), chown_sw, CA_WIZARD|CA_GBL_BUILD, CHOWN_ALL, CS_TWO_ARG|CS_INTERP, 0, do_chownall},
{T("@chzone"), nullptr, CA_NO_SLAVE|CA_NO_GUEST|CA_GBL_BUILD, 0, CS_TWO_ARG|CS_INTERP, 0, do_chzone},
{T("@clone"), clone_sw, CA_NO_SLAVE|CA_GBL_BUILD|CA_CONTENTS|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_clone},
{T("@coflags"), nullptr, CA_NO_SLAVE, EDIT_CHANNEL_COFLAGS, CS_TWO_ARG, 0, do_editchannel},
{T("@cpflags"), nullptr, CA_NO_SLAVE, EDIT_CHANNEL_CPFLAGS, CS_TWO_ARG, 0, do_editchannel},
{T("@create"), nullptr, CA_NO_SLAVE|CA_GBL_BUILD|CA_CONTENTS|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_create},
{T("@cron"), nullptr, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_cron},
{T("@cset"), cset_sw, CA_NO_SLAVE, 0, CS_TWO_ARG|CS_INTERP, 0, do_chopen},
{T("@decompile"), decomp_sw, CA_PUBLIC, 0, CS_TWO_ARG|CS_INTERP, 0, do_decomp},
{T("@delcommand"), nullptr, CA_GOD, 0, CS_TWO_ARG, 0, do_delcommand},
{T("@dolist"), dolist_sw, CA_GBL_INTERP, 0, CS_TWO_ARG|CS_CMDARG|CS_NOINTERP|CS_STRIP_AROUND, 0, do_dolist},
{T("@drain"), nullptr, CA_GBL_INTERP|CA_NO_SLAVE|CA_NO_GUEST, NFY_DRAIN, CS_TWO_ARG, 0, do_notify},
{T("@email"), nullptr, CA_WIZARD, 0, CS_TWO_ARG, 0, do_plusemail},
{T("@femit"), femit_sw, CA_LOCATION|CA_NO_GUEST|CA_NO_SLAVE, PEMIT_FEMIT, CS_TWO_ARG|CS_INTERP, 0, do_pemit},
{T("@fixdb"), fixdb_sw, CA_GOD, 0, CS_TWO_ARG|CS_INTERP, 0, do_fixdb},
{T("@flag"), flag_sw, CA_GOD, 0, CS_TWO_ARG, 0, do_flag},
{T("@folder"), folder_sw, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_folder},
{T("@force"), force_sw, CA_NO_SLAVE|CA_GBL_INTERP|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP|CS_CMDARG, 0, do_force},
{T("@forwardlist"), nullptr, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG, 0, do_forwardlist},
{T("@fpose"), fpose_sw, CA_LOCATION|CA_NO_SLAVE, PEMIT_FPOSE, CS_TWO_ARG|CS_INTERP, 0, do_pemit},
{T("@fsay"), nullptr, CA_LOCATION|CA_NO_SLAVE, PEMIT_FSAY, CS_TWO_ARG|CS_INTERP, 0, do_pemit},
{T("@function"), function_sw,CA_GOD, 0, CS_TWO_ARG|CS_INTERP, 0, do_function},
{T("@link"), nullptr, CA_NO_SLAVE|CA_GBL_BUILD|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_link},
{T("@lock"), lock_sw, CA_NO_SLAVE, 0, CS_TWO_ARG|CS_INTERP, 0, do_lock},
{T("@log"), nullptr, CA_WIZARD, 0, CS_TWO_ARG, 0, do_log},
{T("@mail"), mail_sw, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_mail},
{T("@malias"), malias_sw, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_malias},
{T("@moniker"), nullptr, CA_NO_GUEST|CA_NO_SLAVE, 0, CS_TWO_ARG|CS_INTERP, 0, do_moniker},
{T("@name"), nullptr, CA_NO_SLAVE|CA_GBL_BUILD|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_name},
{T("@newpassword"), nullptr, CA_WIZARD, 0, CS_TWO_ARG, 0, do_newpassword},
{T("@notify"), notify_sw, CA_GBL_INTERP|CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG, 0, do_notify},
{T("@npemit"), pemit_sw, CA_NO_GUEST|CA_NO_SLAVE, PEMIT_PEMIT, CS_TWO_ARG|CS_UNPARSE|CS_NOSQUISH, 0, do_pemit},
{T("@oemit"), oemit_sw, CA_NO_GUEST|CA_NO_SLAVE, PEMIT_OEMIT, CS_TWO_ARG|CS_INTERP, 0, do_pemit},
{T("@parent"), nullptr, CA_NO_SLAVE|CA_GBL_BUILD|CA_NO_GUEST, 0, CS_TWO_ARG, 0, do_parent},
{T("@password"), nullptr, CA_NO_GUEST, 0, CS_TWO_ARG, 0, do_password},
{T("@pcreate"), nullptr, CA_WIZARD|CA_GBL_BUILD, PCRE_PLAYER, CS_TWO_ARG, 0, do_pcreate},
{T("@pemit"), pemit_sw, CA_NO_GUEST|CA_NO_SLAVE, PEMIT_PEMIT, CS_TWO_ARG|CS_INTERP, 0, do_pemit},
{T("@power"), nullptr, CA_PUBLIC, 0, CS_TWO_ARG, 0, do_power},
{T("@protect"), protect_sw, CA_NO_GUEST|CA_NO_SLAVE, PROTECT_ADD, CS_TWO_ARG|CS_INTERP, 0, do_protect},
{T("@program"), nullptr, CA_PUBLIC, 0, CS_TWO_ARG|CS_INTERP, 0, do_prog},
{T("@query"), query_sw, CA_WIZARD, 0, CS_TWO_ARG|CS_INTERP|CS_CMDARG, 0, do_query},
{T("@quota"), quota_sw, CA_PUBLIC, 0, CS_TWO_ARG|CS_INTERP, 0, do_quota},
{T("@reference"), reference_sw, CA_PUBLIC, 0, CS_TWO_ARG|CS_INTERP, 0, do_reference},
{T("@robot"), nullptr, CA_NO_SLAVE|CA_GBL_BUILD|CA_NO_GUEST|CA_PLAYER, PCRE_ROBOT, CS_TWO_ARG, 0, do_pcreate},
#ifdef REALITY_LVLS
{T("@rxlevel"), nullptr, CA_WIZARD, 0, CS_TWO_ARG|CS_INTERP, 0, do_rxlevel},
#endif
{T("@set"), set_sw, CA_NO_SLAVE|CA_GBL_BUILD|CA_NO_GUEST, 0, CS_TWO_ARG, 0, do_set},
{T("@teleport"), teleport_sw,CA_NO_GUEST, TELEPORT_DEFAULT, CS_TWO_ARG|CS_INTERP, 0, do_teleport},
#ifdef REALITY_LVLS
{T("@txlevel"), nullptr, CA_WIZARD, 0, CS_TWO_ARG|CS_INTERP, 0, do_txlevel},
#endif
{T("@toad"), toad_sw, CA_WIZARD, 0, CS_TWO_ARG|CS_INTERP, 0, do_toad},
{T("@walk"), walk_sw, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_walk},
{T("@patrol"), walk_sw, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_patrol},
{T("@wait"), wait_sw, CA_GBL_INTERP, 0, CS_TWO_ARG|CS_CMDARG|CS_NOINTERP|CS_STRIP_AROUND, 0, do_wait},
{T("addcom"), nullptr, CA_NO_SLAVE, 0, CS_TWO_ARG, 0, do_addcom},
{T("comtitle"), comtitle_sw,CA_NO_SLAVE, 0, CS_TWO_ARG, 0, do_comtitle},
{T("give"), give_sw, CA_LOCATION|CA_NO_GUEST, 0, CS_TWO_ARG|CS_INTERP, 0, do_give},
{T("kill"), nullptr, CA_NO_GUEST|CA_NO_SLAVE, KILL_KILL, CS_TWO_ARG|CS_INTERP, 0, do_kill},
{T("page"), page_sw, CA_NO_SLAVE, 0, CS_TWO_ARG|CS_INTERP, 0, do_page},
{T("slay"), nullptr, CA_WIZARD, KILL_SLAY, CS_TWO_ARG|CS_INTERP, 0, do_kill},
{T("whisper"), nullptr, CA_LOCATION|CA_NO_SLAVE, PEMIT_WHISPER, CS_TWO_ARG|CS_INTERP, 0, do_pemit},
{T("&"), nullptr, CA_NO_GUEST|CA_NO_SLAVE|CF_DARK, 0, CS_TWO_ARG|CS_LEADIN, 0, do_setvattr},
{static_cast<UTF8*>(nullptr), nullptr, 0, 0, 0, 0, nullptr}
};
static CMDENT_TWO_ARG_ARGV command_table_two_arg_argv[] =
{
{T("@cpattr"), nullptr, CA_NO_SLAVE|CA_NO_GUEST|CA_GBL_BUILD, 0, CS_TWO_ARG|CS_ARGV|CS_INTERP, 0, do_cpattr},
{T("@dig"), dig_sw, CA_NO_SLAVE|CA_NO_GUEST|CA_GBL_BUILD, 0, CS_TWO_ARG|CS_ARGV|CS_INTERP, 0, do_dig},
{T("@edit"), nullptr, CA_NO_SLAVE|CA_NO_GUEST, 0, CS_TWO_ARG|CS_ARGV|CS_STRIP_AROUND, 0, do_edit},
{T("@icmd"), icmd_sw, CA_GOD, 0, CS_TWO_ARG|CS_ARGV|CS_INTERP, 0, do_icmd},
{T("@if"), nullptr, CA_GBL_INTERP, 0, CS_TWO_ARG|CS_ARGV|CS_CMDARG|CS_NOINTERP|CS_STRIP_AROUND, 0, do_if},
{T("@include"), include_sw, CA_GBL_INTERP, 0, CS_TWO_ARG|CS_ARGV, 0, do_include},
{T("@mvattr"), nullptr, CA_NO_SLAVE|CA_NO_GUEST|CA_GBL_BUILD, 0, CS_TWO_ARG|CS_ARGV, 0, do_mvattr},
{T("@open"), open_sw, CA_NO_SLAVE|CA_GBL_BUILD|CA_NO_GUEST, 0, CS_TWO_ARG|CS_ARGV|CS_INTERP, 0, do_open},
{T("@switch"), switch_sw, CA_GBL_INTERP, 0, CS_TWO_ARG|CS_ARGV|CS_CMDARG|CS_NOINTERP|CS_STRIP_AROUND, 0, do_switch},
{T("@trigger"), trig_sw, CA_GBL_INTERP, 0, CS_TWO_ARG|CS_ARGV, 0, do_trigger},
{T("@verb"), verb_sw, CA_GBL_INTERP|CA_NO_SLAVE, 0, CS_TWO_ARG|CS_ARGV|CS_INTERP|CS_STRIP_AROUND, 0, do_verb},
{static_cast<UTF8*>(nullptr), nullptr, 0, 0, 0, 0, nullptr}
};
static CMDENT *goto_cmdp;
void commands_no_arg_add(CMDENT_NO_ARG cmdent[])
{
for (CMDENT_NO_ARG* cp0a = cmdent; cp0a->cmdname; cp0a++)
{
size_t nLen = strlen(reinterpret_cast<const char*>(cp0a->cmdname));
auto it = mudstate.command_htab.find(std::vector<UTF8>(cp0a->cmdname, cp0a->cmdname + nLen));
if (it == mudstate.command_htab.end())
{
mudstate.command_htab.emplace(std::vector<UTF8>(cp0a->cmdname, cp0a->cmdname + nLen), cp0a);
}
}
}
void commands_one_arg_add(CMDENT_ONE_ARG cmdent[])
{
CMDENT_ONE_ARG *cp1a;
for (cp1a = cmdent; cp1a->cmdname; cp1a++)
{
size_t nLen = strlen(reinterpret_cast<const char*>(cp1a->cmdname));
auto it = mudstate.command_htab.find(std::vector<UTF8>(cp1a->cmdname, cp1a->cmdname + nLen));
if (it == mudstate.command_htab.end())
{
mudstate.command_htab.emplace(std::vector<UTF8>(cp1a->cmdname, cp1a->cmdname + nLen), cp1a);
}
}
}
void commands_two_arg_add(CMDENT_TWO_ARG cmdent[])
{
CMDENT_TWO_ARG *cp2a;
for (cp2a = cmdent; cp2a->cmdname; cp2a++)
{
size_t nLen = strlen(reinterpret_cast<const char*>(cp2a->cmdname));
auto it = mudstate.command_htab.find(std::vector<UTF8>(cp2a->cmdname, cp2a->cmdname + nLen));
if (it == mudstate.command_htab.end())
{
mudstate.command_htab.emplace(std::vector<UTF8>(cp2a->cmdname, cp2a->cmdname + nLen), cp2a);
}
}
}
void commands_two_arg_argv_add(CMDENT_TWO_ARG_ARGV cmdent[])
{
CMDENT_TWO_ARG_ARGV *cp2aa;
for (cp2aa = cmdent; cp2aa->cmdname; cp2aa++)
{
size_t nLen = strlen(reinterpret_cast<const char*>(cp2aa->cmdname));
auto it = mudstate.command_htab.find(std::vector<UTF8>(cp2aa->cmdname, cp2aa->cmdname + nLen));
if (it == mudstate.command_htab.end())
{
mudstate.command_htab.emplace(std::vector<UTF8>(cp2aa->cmdname, cp2aa->cmdname + nLen), cp2aa);
}
}
}
void init_cmdtab(void)
{
// Load attribute-setting commands.
//
for (ATTR* ap = AttrTable; ap->name; ap++)
{
if (ap->flags & AF_NOCMD)
{
continue;
}
size_t nBuffer;
bool bValid;
const UTF8 *cbuff = MakeCanonicalAttributeCommand(ap->name, &nBuffer, &bValid);
if (!bValid)
{
continue;
}
CMDENT_TWO_ARG *cp2a = nullptr;
try
{
cp2a = new CMDENT_TWO_ARG;
}
catch (...)
{
}
if (nullptr != cp2a)
{
cp2a->cmdname = StringClone(cbuff);
cp2a->perms = CA_NO_GUEST | CA_NO_SLAVE;
cp2a->switches = nullptr;
if (ap->flags & (AF_WIZARD | AF_MDARK))
{
cp2a->perms |= CA_WIZARD;
}
cp2a->extra = ap->number;
cp2a->callseq = CS_TWO_ARG;
cp2a->flags = CEF_ALLOC;
cp2a->handler = do_setattr;
mudstate.command_htab.emplace(std::vector<UTF8>(cp2a->cmdname, cp2a->cmdname + nBuffer), cp2a);
}
else
{
STARTLOG(LOG_PROBLEMS, "CMD", "MEM");
log_printf(T("init_cmdtab: out of memory adding @set-attr command."));
ENDLOG;
}
}
// Load the builtin commands
//
commands_no_arg_add(command_table_no_arg);
commands_one_arg_add(command_table_one_arg);
commands_two_arg_add(command_table_two_arg);
commands_two_arg_argv_add(command_table_two_arg_argv);
cache_prefix_cmds();
{
const UTF8 *tmp = reinterpret_cast<const UTF8*>("goto");
auto it = mudstate.command_htab.find(std::vector<UTF8>(tmp, tmp + 4));
goto_cmdp = (it != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it->second) : nullptr;
}
}
static CMDENT *g_prefix_cmds[256];
void clear_prefix_cmds()
{
for (auto& g_prefix_cmd : g_prefix_cmds)
{
g_prefix_cmd = nullptr;
}
}
#ifdef SELFCHECK
void finish_cmdtab()
{
clear_prefix_cmds();
goto_cmdp = nullptr;
// First pass is to get rid of aliases.
//
StringPtrMap ht;
CMDENT *cmdp;
for (auto &[key, val] : mudstate.command_htab)
{
cmdp = static_cast<CMDENT*>(val);
if (0 == (cmdp->flags & CEF_VISITED))
{
cmdp->flags |= CEF_VISITED;
size_t nLen = strlen(reinterpret_cast<char*>(cmdp->cmdname));
ht.emplace(std::vector<UTF8>(cmdp->cmdname, cmdp->cmdname + nLen), cmdp);
}
}
mudstate.command_htab.clear();
// Second pass is to free unique CMDENTs and related things.
//
for (auto &[htkey, htval] : ht)
{
cmdp = static_cast<CMDENT*>(htval);
if (cmdp->callseq & CS_ADDED)
{
delete cmdp->addent;
cmdp->addent = nullptr;
}
if (cmdp->flags & CEF_ALLOC)
{
MEMFREE(cmdp->cmdname);
cmdp->cmdname = nullptr;
delete cmdp;
cmdp = nullptr;
}
}
}
#endif
/*! \brief Fills in the table of single-character prefix commands.
*
* Command entries for known prefix commands (<code>" : ; \ # & - ~</code>)
* are copied from the regular command table. Entries for all other starting
* characters are set to nullptr.
*
* \return None.
*/
void cache_prefix_cmds(void)
{
clear_prefix_cmds();
#define SET_PREFIX_CMD(s) \
do { \
const UTF8 *_tmp = reinterpret_cast<const UTF8*>(s); \
auto _it = mudstate.command_htab.find(std::vector<UTF8>(_tmp, _tmp + 1)); \
g_prefix_cmds[static_cast<unsigned char>((s)[0])] = \
(_it != mudstate.command_htab.end()) ? static_cast<CMDENT*>(_it->second) : nullptr; \
} while (0)
SET_PREFIX_CMD("\"");
SET_PREFIX_CMD(":");
SET_PREFIX_CMD(";");
SET_PREFIX_CMD("\\");
SET_PREFIX_CMD("#");
SET_PREFIX_CMD("&");
SET_PREFIX_CMD("-");
SET_PREFIX_CMD("~");
#undef SET_PREFIX_CMD
}
inline bool is_prefix_cmd(const UTF8 *pCommand, size_t *pnPrefix, CMDENT **ppcmd)
{
bool fReturn = false;
size_t nPrefix = 0;
CMDENT *pcmd = nullptr;
if (nullptr != pCommand)
{
pcmd = g_prefix_cmds[static_cast<unsigned char>(pCommand[0])];
if (nullptr != pcmd)
{
nPrefix = 1;
fReturn = true;
}
else if ( 0xE2 == pCommand[0]
&& 0x80 == pCommand[1]
&& 0x9C == pCommand[2])
{
// U+201C is a Unicode quote typically sent instead of ASCII double quote.
//
pcmd = g_prefix_cmds[static_cast<unsigned char>('"')];
if (nullptr != pcmd)
{
nPrefix = 3;
fReturn = true;
}
}
}
if (nullptr != pnPrefix)
{
*pnPrefix = nPrefix;
}
if (nullptr != ppcmd)
{
*ppcmd = pcmd;
}
return fReturn;
}
// ---------------------------------------------------------------------------
// check_access: Check if player has access to function.
//
bool check_access(dbref player, int mask)
{
if (mask & (CA_DISABLED|CA_STATIC))
{
return false;
}
if ( God(player)
|| mudstate.bReadingConfiguration)
{
return true;
}
if (mask & CA_MUSTBE_MASK)
{
// Since CA_GOD by itself is a frequent case, for the sake of
// performance, we test CA_GOD specifically. If CA_GOD were ever
// combined with anything, it would be passed through to the general
// case.
//
if ((mask & CA_MUSTBE_MASK) == CA_GOD)
{
return false;
}
// Since God(player) is always false here, CA_GOD is still handled by
// the following code even though it doesn't appear in any of the
// cases explicitly. CA_WIZARD by itself is also a common case, but
// since we have have a bit (mask & CA_MUSTBE_MASK), and since that
// bit is not a lone CA_GOD bit (handled above), and since CA_WIZARD
// it tested first below, it doesn't make sense to test CA_WIZARD
// as a special case.
//
if (!( ((mask & CA_WIZARD) && Wizard(player))
|| ((mask & CA_ADMIN) && WizRoy(player))
|| ((mask & CA_BUILDER) && Builder(player))
|| ((mask & CA_STAFF) && Staff(player))
|| ((mask & CA_HEAD) && Head(player))
|| ((mask & CA_ANNOUNCE) && Announce(player))
|| ((mask & CA_IMMORTAL) && Immortal(player))
|| ((mask & CA_UNINS) && Uninspected(player))
|| ((mask & CA_ROBOT) && Robot(player))))
{
return false;
}
}
// Check for forbidden flags.
//
if ( (mask & CA_CANTBE_MASK)
&& !Wizard(player))
{
if ( ((mask & CA_NO_HAVEN) && Player_haven(player))
|| ((mask & CA_NO_ROBOT) && Robot(player))
|| ((mask & CA_NO_SLAVE) && Slave(player))
|| ((mask & CA_NO_SUSPECT) && Suspect(player))
|| ((mask & CA_NO_GUEST) && Guest(player))
|| ((mask & CA_NO_UNINS) && Uninspected(player)))
{
return false;
}
}
return true;
}
/*****************************************************************************
* Process the various hook calls.
* Idea taken from TinyMUSH3, code from RhostMUSH, ported by Jake Nelson.
* Hooks processed: before, after, ignore, permit, fail
*****************************************************************************/
static UTF8 *hook_name(const UTF8 *pCommand, int key)
{
const char *keylet;
switch (key)
{
case CEF_HOOK_AFAIL:
keylet = "AF";
break;
case CEF_HOOK_AFTER:
keylet = "A";
break;
case CEF_HOOK_BEFORE:
keylet = "B";
break;
case CEF_HOOK_IGNORE:
keylet = "I";
break;
case CEF_HOOK_PERMIT:
keylet = "P";
break;
case CEF_HOOK_ARGS:
keylet = "R";
break;
default:
return nullptr;
}
const UTF8 *cmdName = pCommand;
if ( pCommand[0]
&& !pCommand[1])
{
switch (pCommand[0])
{
case '"' : cmdName = T("say"); break;
case ':' :
case ';' : cmdName = T("pose"); break;
case '\\': cmdName = T("@emit"); break;
case '#' : cmdName = T("@force"); break;
case '&' : cmdName = T("@set"); break;
case '-' : cmdName = T("@mail"); break;
case '~' : cmdName = T("@mail"); break;
}
}
return tprintf(T("%s_%s"), keylet, cmdName);
}
static bool process_hook(dbref executor, CMDENT *cmdp, int key, bool save_flg)
{
bool retval = true;
ATTR *hk_attr = atr_str(hook_name(cmdp->cmdname, key));
if (hk_attr)
{
dbref aowner;
int aflags;
LBuf atext = LBuf_Adopt(atr_get("process_hook.1060", mudconf.hook_obj,
hk_attr->number, &aowner, &aflags));
if (atext[0] && !(aflags & AF_NOPROG))
{
if ((aflags & AF_NOEVAL) || NoEval(mudconf.hook_obj))
{
// NOEVAL means "use this attribute's text literally", not
// "this attribute has no opinion". Returning true here
// discarded the content outright, so a NOEVAL permit or
// ignore hook allowed every command -- a permission control
// silently disabled, and indistinguishable from having no
// hook configured at all (#1280).
//
// Derive the boolean from the unevaluated text instead. That
// is what the rest of the NOEVAL work does: eval_boolexp,
// added in the same commit (4cb904fcc) and the only other
// boolean consumer in its list, compares the raw text rather
// than short-circuiting. A literal "0" or "#-1 ..." hook now
// blocks as written.
//
retval = xlate(atext);
}
else
{
reg_ref **preserve = nullptr;
if (save_flg)
{
preserve = PushRegisters(MAX_GLOBAL_REGS);
save_global_regs(preserve);
}
UTF8 *bufc;
LBuf buff = LBuf_Src("process_hook");
bufc = buff;
mux_exec(atext, LBUF_SIZE-1, buff, &bufc, mudconf.hook_obj, executor,
executor, AttrTrace(aflags, EV_FCHECK|EV_EVAL), nullptr, 0);
*bufc = '\0';
if (save_flg)
{
restore_global_regs(preserve);
PopRegisters(preserve, MAX_GLOBAL_REGS);
}
retval = xlate(buff);
}
}
}
return retval;
}
void process_hook_args(dbref executor, CMDENT *cmdp, UTF8* arg1, UTF8* arg2, UTF8* args[], int nargs, UTF8* sw)
{
ATTR *hk_attr = atr_str(hook_name(cmdp->cmdname, CEF_HOOK_ARGS));
if (hk_attr)
{
dbref aowner;
int aflags;
LBuf atext = LBuf_Adopt(atr_get("process_hook_args.1093", mudconf.hook_obj,
hk_attr->number, &aowner, &aflags));
if (atext[0] && !(aflags & AF_NOPROG))
{
reg_ref **preserve = nullptr;
preserve = PushRegisters(MAX_GLOBAL_REGS);
save_global_regs(preserve);
UTF8 *bufc = nullptr;
UTF8* inargs[5];
if (arg1)
{
inargs[0] = alloc_lbuf("process_hook_args.1103");
mux_strncpy(inargs[0], arg1, LBUF_SIZE-1);
}
else
{
inargs[0] = const_cast<UTF8 *>(T(""));
}
if (arg2)
{
inargs[1] = alloc_lbuf("process_hook_args.1110");
mux_strncpy(inargs[1], arg2, LBUF_SIZE-1);
}
else
{
inargs[1] = const_cast<UTF8 *>(T(""));
}
inargs[2] = const_cast<UTF8 *>(T(""));
inargs[4] = sw;
if (arg1)
{
bufc = arg1;
inargs[3] = const_cast<UTF8 *>(T("0"));
mux_exec(atext, LBUF_SIZE-1, arg1, &bufc, mudconf.hook_obj, executor,
executor, AttrTrace(aflags, EV_FCHECK|EV_EVAL), (const UTF8**)inargs, 5);
}
if (arg2)
{
bufc = arg2;
inargs[3] = const_cast<UTF8 *>(T("1"));
mux_exec(atext, LBUF_SIZE-1, arg2, &bufc, mudconf.hook_obj, executor,
executor, AttrTrace(aflags, EV_FCHECK|EV_EVAL), (const UTF8**)inargs, 5);
}
if (nargs)
{
inargs[2] = alloc_lbuf("process_hook_args.1135");
UTF8 qbuff[I64BUF_SIZE];
inargs[3] = qbuff;
for (int i = 0; i < nargs; i++)
{
mux_i64toa(i+2, qbuff);
bufc = args[i];
mux_strncpy(inargs[2], args[i], LBUF_SIZE-1);
mux_exec(atext, LBUF_SIZE-1, args[i], &bufc, mudconf.hook_obj, executor,
executor, AttrTrace(aflags, EV_FCHECK|EV_EVAL), (const UTF8**)inargs, 5);
}
free_lbuf(inargs[2]);
}
if (arg2) free_lbuf(inargs[1]);
if (arg1) free_lbuf(inargs[0]);
if (nullptr != bufc)
{
*bufc = '\0';
}
restore_global_regs(preserve);
PopRegisters(preserve, MAX_GLOBAL_REGS);
}
}
}
/* ---------------------------------------------------------------------------
* process_cmdent: Perform indicated command with passed args.
*/
static void process_cmdent(CMDENT *cmdp, UTF8 *switchp, dbref executor, dbref caller,
dbref enactor, int eval, bool interactive, UTF8 *arg, UTF8 *unp_command,
const UTF8 *cargs[], int ncargs)
{
// Perform object type checks.
//
if (Invalid_Objtype(executor))
{
notify(executor, M_("Command incompatible with executor type."));
return;
}
// Check if we have permission to execute the command.
//
if (!check_access(executor, cmdp->perms))
{
notify(executor, NOPERM_MESSAGE);
return;
}
// Check global flags
//
if ( !Builder(executor)
&& Protect(CA_GBL_BUILD)
&& !(mudconf.control_flags & CF_BUILD))
{
notify(executor, M_("Sorry, building is not allowed now."));
return;
}
if (Protect(CA_GBL_INTERP) && !(mudconf.control_flags & CF_INTERP))
{
notify(executor, M_("Sorry, queueing and triggering are not allowed now."));
return;
}
UTF8 *buf1, *buf2, tchar, *bp, *str, *buff, *j, *new0;
UTF8 *args[MAX_ARG];
UTF8* sw = switchp;
int nargs, i, interp, key, xkey, aflags;
dbref aowner;
ADDENT *add;
key = cmdp->extra & ~SW_MULTIPLE;
if (key & SW_GOT_UNIQUE)
{
i = 1;
key = key & ~SW_GOT_UNIQUE;
}
else
{
i = 0;
}
// Check command switches. Note that there may be more than one,
// and that we OR all of them together along with the extra value
// from the command table to produce the key value in the handler
// call.
//
if (switchp && cmdp->switches)
{
do
{
buf1 = reinterpret_cast<UTF8 *>(strchr(reinterpret_cast<char *>(switchp), '/'));
if (buf1)
{
*buf1++ = '\0';
}
if (!search_nametab(executor, cmdp->switches, switchp, &xkey))
{
if (xkey == -1)
{
notify(executor,
tprintf(M_("Unrecognized switch %s for command %s."),
switchp, cmdp->cmdname));
return;
}
else if (xkey == -2)
{
notify(executor, NOPERM_MESSAGE);
return;
}
}
else if (!(xkey & SW_MULTIPLE))
{
if (i == 1)
{
notify(executor, M_("Illegal combination of switches."));
return;
}
i = 1;
}
else
{
xkey &= ~SW_MULTIPLE;
}
key |= xkey;
switchp = buf1;
} while (buf1);
}
else if (switchp && !(cmdp->callseq & CS_ADDED))
{
notify(executor, tprintf(M_("Command %s does not take switches."),
cmdp->cmdname));
return;
}
bool bGoodHookObj = (Good_obj(mudconf.hook_obj) && !Going(mudconf.hook_obj));
// 'Before' hooks.
// @hook idea from TinyMUSH 3, code from RhostMUSH. Ported by Jake Nelson.
//
if ( (cmdp->flags & CEF_HOOK_BEFORE)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_BEFORE, false);
}
// We are allowed to run the command. Now, call the handler using
// the appropriate calling sequence and arguments.
//
//if ((cmdp->callseq & CS_INTERP) && (key & SW_NOEVAL))
if (key & SW_NOEVAL)
{
// The user specified /noeval on a @pemit or a @npemit,
// just do EV_STRIP_CURLY and remove the SW_NOEVAL from the
// 'key'.
//
interp = 0;
key &= ~SW_NOEVAL;
}
else if ( (cmdp->callseq & CS_INTERP)
|| !( interactive
|| (cmdp->callseq & CS_NOINTERP)))
{
// If the command is interpreted, or we're interactive (and
// the command isn't specified CS_NOINTERP), eval the args.
//
interp = EV_EVAL | EV_STRIP_CURLY;
}
else if (cmdp->callseq & CS_STRIP)
{
interp = EV_STRIP_CURLY;
}
else if (cmdp->callseq & CS_STRIP_AROUND)
{
interp = EV_STRIP_AROUND;
}
else
{
interp = 0;
}
UTF8 *aargs[NUM_ENV_VARS];
int nargs2;
switch (cmdp->callseq & CS_NARG_MASK)
{
case CS_NO_ARGS: // <cmd> (no args)
(*((reinterpret_cast<CMDENT_NO_ARG *>(cmdp))->handler))(executor, caller, enactor, eval, key);
break;
case CS_ONE_ARG: // <cmd> <arg>
// If an unparsed command, just give it to the handler
//
// Interpret if necessary, but not twice for CS_ADDED.
//
if ((interp & EV_EVAL) && !(cmdp->callseq & CS_ADDED))
{
buf1 = bp = alloc_lbuf("process_cmdent");
str = arg;
// Copy and skip first character for CS_LEADIN, to
// prevent the \ prefix command from escaping part of
// its argument.
//
if ( (cmdp->callseq & CS_LEADIN)
&& UTF8_SIZE1 == utf8_FirstByte[static_cast<unsigned char>(str[0])])
{
UTF8 ch = *str++;
*bp++ = ch;
if ( mudconf.space_compress
&& ( ';' == ch
|| ':' == ch)
&& str[0] == ' ')
{
// Skip following space to prevent the parser from space
// compressing it.
//
*bp++ = *str++;
}
}
mux_exec(str, LBUF_SIZE-1, buf1, &bp, executor, caller, enactor,
eval|interp|EV_FCHECK|EV_TOP, cargs, ncargs);
*bp = '\0';
}
else
{
buf1 = parse_to(&arg, '\0', interp | EV_TOP);
}
// Call the correct handler.
//
if (cmdp->callseq & CS_ADDED)
{
for (auto &addent : *cmdp->addent)
{
buff = atr_get("process_cmdent.1347", addent.thing, addent.atr, &aowner, &aflags);
// Attribute should contain at least two characters and first
// character is '$'.
//
if ( AMATCH_CMD != buff[0]
|| '\0' == buff[1])
{
free_lbuf(buff);
break;
}
// Skip the '$' character and the next to allow "$::cmd".
//
size_t iBuff;
for (iBuff = 2; '\0' != buff[iBuff]
&& ':' != buff[iBuff]; iBuff++)
{
; // Nothing.
}
if ('\0' == buff[iBuff])
{
free_lbuf(buff);
break;
}
buff[iBuff++] = '\0';
if (!(cmdp->callseq & CS_LEADIN))
{
for (j = unp_command; *j && (*j != ' '); j++)
{
; // Nothing.
}
}
else
{
for (j = unp_command; *j; j++)
{
; // Nothing.
}
}
new0 = alloc_lbuf("process_cmdent.soft");
bp = new0;
if (!*j)
{
// No args.
//
if (!(cmdp->callseq & CS_LEADIN))
{
safe_str(cmdp->cmdname, new0, &bp);
}
else
{
safe_str(unp_command, new0, &bp);
}
if (switchp)
{
safe_chr('/', new0, &bp);
safe_str(switchp, new0, &bp);
}
*bp = '\0';
}
else
{
j++;
safe_str(cmdp->cmdname, new0, &bp);
if (switchp)
{
safe_chr('/', new0, &bp);
safe_str(switchp, new0, &bp);
}
safe_chr(' ', new0, &bp);
safe_str(j, new0, &bp);
*bp = '\0';
}
if ( ( (aflags & AF_REGEXP)
&& regexp_match(buff + 1, new0,
((aflags & AF_CASE) ? PCRE2_CASELESS : 0), aargs,
NUM_ENV_VARS))
|| ( (aflags & AF_REGEXP) == 0
&& wild(buff + 1, new0, aargs, NUM_ENV_VARS)))
{
CLinearTimeAbsolute lta;
wait_que(addent.thing, caller, executor,
AttrTrace(aflags, 0), false, lta, NOTHING, 0,
buff + iBuff,
NUM_ENV_VARS, (const UTF8 **)aargs,
mudstate.global_regs);
for (i = 0; i < NUM_ENV_VARS; i++)
{
if (aargs[i])
{
free_lbuf(aargs[i]);
}
}
}
free_lbuf(new0);
free_lbuf(buff);
}
}
else
{
if ((cmdp->flags & CEF_HOOK_ARGS) && bGoodHookObj)
{
process_hook_args(executor, cmdp, buf1, nullptr, nullptr, 0, sw);
}
(*((reinterpret_cast<CMDENT_ONE_ARG *>(cmdp))->handler))(executor, caller,
enactor, eval, key, buf1, cargs, ncargs);
}
// Free the buffer if one was allocated.
//
if ((interp & EV_EVAL) && !(cmdp->callseq & CS_ADDED))
{
free_lbuf(buf1);
}
break;
case CS_TWO_ARG: // <cmd> <arg1> = <arg2>
// Interpret ARG1
//
buf2 = parse_to(&arg, '=', EV_STRIP_TS);
nargs2 = 0;
if (buf2)
{
if (arg)
{
nargs2 = 2;
}
else
{
nargs2 = 1;
}
}
// Handle when no '=' was specified.
//
if (!arg || (arg && !*arg))
{
arg = &tchar;
*arg = '\0';
}
buf1 = bp = alloc_lbuf("process_cmdent.2");
mux_exec(buf2, LBUF_SIZE-1, buf1, &bp, executor, caller, enactor,
eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL|EV_TOP, cargs, ncargs);
*bp = '\0';
if (cmdp->callseq & CS_ARGV)
{
// Arg2 is ARGV style. Go get the args.
//
parse_arglist(executor, caller, enactor, arg,
eval|interp|EV_STRIP_LS|EV_STRIP_TS, args, MAX_ARG, cargs,
ncargs, &nargs);
if ((cmdp->flags & CEF_HOOK_ARGS) && bGoodHookObj)
{
process_hook_args(executor, cmdp, buf1, nullptr, args, nargs, sw);
}
(*((reinterpret_cast<CMDENT_TWO_ARG_ARGV *>(cmdp))->handler))(executor, caller,
enactor, eval, key, buf1, args, nargs, cargs, ncargs);
// Free the argument buffers.
//
for (i = 0; i < nargs; i++)
{
free_lbuf(args[i]);
}
}
else
{
// Arg2 is normal style. Interpret if needed.
//
if (interp & EV_EVAL)
{
buf2 = bp = alloc_lbuf("process_cmdent.3");
mux_exec(arg, LBUF_SIZE-1, buf2, &bp, executor, caller, enactor,
eval|interp|EV_FCHECK|EV_TOP, cargs, ncargs);
*bp = '\0';
}
else if (cmdp->callseq & CS_UNPARSE)
{
buf2 = parse_to(&arg, '\0', eval|interp|EV_TOP|EV_NO_COMPRESS);
}
else
{
buf2 = parse_to(&arg, '\0', eval|interp|EV_STRIP_LS|EV_STRIP_TS|EV_TOP);
}
if ((cmdp->flags & CEF_HOOK_ARGS) && bGoodHookObj)
{
process_hook_args(executor, cmdp, nargs2>=1?buf1:nullptr, nargs2>=2?buf2:nullptr, nullptr, 0, sw);
}
(*((reinterpret_cast<CMDENT_TWO_ARG *>(cmdp))->handler))(executor, caller,
enactor, eval, key, nargs2, buf1, buf2, cargs, ncargs);
// Free the buffer, if needed.
//
if (interp & EV_EVAL)
{
free_lbuf(buf2);
}
}
// Free the buffer obtained by evaluating Arg1.
//
free_lbuf(buf1);
break;
}
// 'After' hooks.
// @hook idea from TinyMUSH 3, code from RhostMUSH. Ported by Jake Nelson.
//
if ( (cmdp->flags & CEF_HOOK_AFTER)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_AFTER, false);
}
}
static int cmdtest(dbref player, const UTF8 *cmd)
{
dbref aowner;
int aflags;
int rval = 0;
LBuf buff1 = LBuf_Adopt(atr_get("cmdtest.1573", player, A_CMDCHECK, &aowner, &aflags));
if (nullptr == buff1.get()) return 0;
const UTF8* pt1 = buff1;
while (pt1 && *pt1)
{
const auto pt2 = reinterpret_cast<const UTF8*>(strchr(reinterpret_cast<const char*>(pt1), ':'));
if (!pt2 || (pt2 == pt1))
break;
if (!strncmp(reinterpret_cast<const char*>(pt2)+1, reinterpret_cast<const char*>(cmd), strlen(reinterpret_cast<const char*>(cmd))))
{
if (*(pt2-1) == '1')
rval = 1;
else
rval = 2;
break;
}
pt1 = reinterpret_cast<const UTF8*>(strchr(reinterpret_cast<const char*>(pt2) + 1, ' '));
if (pt1 && *pt1)
{
while (mux_isspace(*pt1))
{
pt1++;
}
}
}
return rval;
}
static int zonecmdtest(dbref player, const UTF8 *cmd)
{
if (!Good_obj(player) || God(player))
{
return 0;
}
dbref loc = Location(player);
int i_ret = 0;
if (Good_obj(loc))
{
i_ret = cmdtest(loc, cmd);
if (i_ret == 0)
{
dbref zone = Zone(loc);
if ( Good_obj(zone)
&& ( isRoom(zone)
|| isThing(zone)))
{
i_ret = cmdtest(zone, cmd);
}
}
}
return i_ret;
}
// ---------------------------------------------------------------------------
// handle_gmcp: Process an inbound GMCP message.
//
// The payload is "package[.sub] [json]". We fire the A_GMCP attribute
// on the player with %0 = package, %1 = json data.
//
static void handle_gmcp(dbref player, const UTF8 *payload)
{
if (!Good_obj(player) || !isPlayer(player))
{
return;
}
// Split payload into package and JSON at the first space.
//
const UTF8 *pSpace = payload;
while (*pSpace && *pSpace != ' ')
{
pSpace++;
}
UTF8 pkg[SBUF_SIZE];
size_t nPkg = static_cast<size_t>(pSpace - payload);
if (nPkg >= SBUF_SIZE) nPkg = SBUF_SIZE - 1;
memcpy(pkg, payload, nPkg);
pkg[nPkg] = '\0';
const UTF8 *json = (*pSpace == ' ') ? pSpace + 1 : T("");
dbref aowner;
int aflags;
LBuf pGmcpAttr = LBuf_Adopt(atr_pget(player, A_GMCP, &aowner, &aflags));
if (pGmcpAttr[0] != '\0')
{
const UTF8 *gmcp_args[2];
gmcp_args[0] = pkg;
gmcp_args[1] = json;
LBuf lbuf = LBuf_Src("handle_gmcp");
UTF8 *lp = lbuf;
mux_exec(pGmcpAttr, strlen(reinterpret_cast<const char *>(pGmcpAttr.get())),
lbuf, &lp, player, player, player,
EV_FCHECK | EV_EVAL | EV_TOP,
gmcp_args, 2);
*lp = '\0';
}
}
// ---------------------------------------------------------------------------
// process_command: Execute a command.
//
UTF8 *process_command
(
dbref executor,
dbref caller,
dbref enactor,
int eval,
bool interactive,
UTF8 *arg_command,
const UTF8 *args[],
int nargs
)
{
thread_local UTF8 preserve_cmd[LBUF_SIZE];
UTF8 *pOriginalCommand = arg_command;
thread_local UTF8 SpaceCompressCommand[LBUF_SIZE];
thread_local UTF8 LowerCaseCommand[LBUF_SIZE];
// Robustify player.
//
const UTF8 *cmdsave = g_debug_cmd;
g_debug_cmd = T("< process_command >");
mudstate.nStackNest = 0;
mudstate.bStackLimitReached = false;
mux_assert(pOriginalCommand);
if (!Good_obj(executor))
{
STARTLOG(LOG_BUGS, T("CMD"), T("PLYR"));
log_printf(T("Bad player in process_command: %d"), executor);
ENDLOG;
g_debug_cmd = cmdsave;
return pOriginalCommand;
}
// Make sure player isn't going or halted.
//
if ( Going(executor)
|| ( Halted(executor)
&& !( isPlayer(executor)
&& interactive)))
{
notify(Owner(executor),
tprintf(T("Attempt to execute command by halted object #%d"), executor));
g_debug_cmd = cmdsave;
return pOriginalCommand;
}
// Intercept internal GMCP messages from the telnet layer.
// Format: "\x01GMCP package [json]"
//
if ( '\x01' == pOriginalCommand[0]
&& memcmp(pOriginalCommand + 1, "GMCP ", 5) == 0
&& interactive)
{
handle_gmcp(executor, pOriginalCommand + 6);
g_debug_cmd = cmdsave;
return pOriginalCommand;
}
if ( Suspect(executor)
&& (mudconf.log_options & LOG_SUSPECTCMDS))
{
STARTLOG(LOG_SUSPECTCMDS, T("CMD"), T("SUSP"));
log_name_and_loc(executor);
log_text(T(" entered: "));
log_text(pOriginalCommand);
ENDLOG;
}
else
{
STARTLOG(LOG_ALLCOMMANDS, T("CMD"), T("ALL"));
log_name_and_loc(executor);
log_text(T(" entered: "));
log_text(pOriginalCommand);
ENDLOG;
}
// Reset recursion limits.
//
// Note: include_nest_lev is intentionally not reset here — it tracks
// recursive process_command_list_inline depth across nested
// @include/@dolist/now invocations and is balanced by that path.
//
mudstate.func_nest_lev = 0;
mudstate.func_invk_ctr = 0;
mudstate.ntfy_nest_lev = 0;
mudstate.lock_nest_lev = 0;
if (Verbose(executor))
{
notify(Owner(executor), tprintf(T("%s] %s"), Name(executor), pOriginalCommand));
}
// Eat leading whitespace, and space-compress if configured.
//
while (mux_isspace(*pOriginalCommand))
{
pOriginalCommand++;
}
mux_strncpy(preserve_cmd, pOriginalCommand, sizeof(preserve_cmd)-1);
g_debug_cmd = pOriginalCommand;
mudstate.curr_cmd = preserve_cmd;
UTF8 *pCommand;
UTF8 *p, *q, *arg, *bp;
int aflags;
dbref exit, aowner;
if (mudconf.space_compress)
{
// Compress out the spaces and use that as the command
//
pCommand = SpaceCompressCommand;
p = pOriginalCommand;
q = SpaceCompressCommand;
while ( *p
&& q < SpaceCompressCommand + LBUF_SIZE)
{
while ( *p
&& !mux_isspace(*p)
&& q < SpaceCompressCommand + LBUF_SIZE)
{
*q++ = *p++;
}
while (mux_isspace(*p))
{
p++;
}
if ( *p
&& q < SpaceCompressCommand + LBUF_SIZE)
{
*q++ = ' ';
}
}
*q = '\0';
}
else
{
// Don't compress the spaces. Use the original command
// (without leading spaces) as the command to use.
//
pCommand = pOriginalCommand;
}
// Now comes the fun stuff. First check for single-letter leadins.
// We check these before checking HOME because they are among the
// most frequently executed commands, and they can never be the
// HOME command.
//
int cval = 0;
int hval = 0;
bool bGoodHookObj = (Good_obj(mudconf.hook_obj) && !Going(mudconf.hook_obj));
size_t nPrefix = 0;
CMDENT *cmdp = nullptr;
if (is_prefix_cmd(pCommand, &nPrefix, &cmdp))
{
// CmdCheck tests for @icmd. higcheck tests for i/p hooks.
// Both from RhostMUSH.
// cval/hval values: 0 normal, 1 disable, 2 ignore
//
UTF8 check2[UTF8_SIZE4+1];
memcpy(check2, pCommand, nPrefix);
check2[nPrefix] = '\0';
if (CmdCheck(executor))
{
cval = cmdtest(executor, check2);
}
else if (CmdCheck(Owner(executor)))
{
cval = cmdtest(Owner(executor), check2);
}
else
{
cval = 0;
}
if (cval == 0)
{
cval = zonecmdtest(executor, check2);
}
hval = 0;
if ( (cmdp->flags & (CEF_HOOK_IGNORE|CEF_HOOK_PERMIT))
&& bGoodHookObj)
{
if ( (cmdp->flags & CEF_HOOK_IGNORE)
&& !process_hook(executor, cmdp, CEF_HOOK_IGNORE, true))
{
hval = 2;
}
else if ( (cmdp->flags & CEF_HOOK_PERMIT)
&& !process_hook(executor, cmdp, CEF_HOOK_PERMIT, true))
{
hval = 1;
}
}
if ( cval != 2
&& hval != 2)
{
if ( cval == 1
|| hval == 1)
{
if ( (cmdp->flags & CEF_HOOK_AFAIL)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_AFAIL, false);
}
else
{
notify(executor, NOPERM_MESSAGE);
}
g_debug_cmd = cmdsave;
return preserve_cmd;
}
process_cmdent(cmdp, nullptr, executor, caller, enactor,
eval, interactive, pCommand, pCommand, args, nargs);
if (mudstate.bStackLimitReached)
{
STARTLOG(LOG_ALWAYS, T("CMD"), T("SPAM"));
log_name_and_loc(executor);
log_text(T(" entered: "));
log_text(pOriginalCommand);
ENDLOG;
}
mudstate.bStackLimitReached = false;
g_debug_cmd = cmdsave;
return preserve_cmd;
}
}
// Talk mode: if the player has TALKMODE set and this is interactive
// input, treat unprefix'd input as 'say'. A leading '.' escapes
// to normal command processing.
//
if ( interactive
&& isPlayer(executor)
&& TalkMode(executor))
{
if ('.' == pCommand[0])
{
// Strip the dot and fall through to normal command
// processing.
//
pCommand++;
// If the dot was the only character, ignore the empty
// command.
//
if ('\0' == *pCommand)
{
g_debug_cmd = cmdsave;
return preserve_cmd;
}
}
else
{
// Route to say.
//
do_say(executor, caller, enactor, eval, SAY_SAY, pCommand,
args, nargs);
g_debug_cmd = cmdsave;
return preserve_cmd;
}
}
if (!Slave(executor))
{
bool bHandled = false;
if (nullptr != mudstate.pIComsysControl)
{
mudstate.pIComsysControl->ProcessCommand(executor, pCommand,
&bHandled);
}
else
{
bHandled = !do_comsystem(executor, pCommand);
}
if (bHandled)
{
g_debug_cmd = cmdsave;
return preserve_cmd;
}
}
// Check for the HOME command.
//
if ( Has_location(executor)
&& string_compare(pCommand, T("home")) == 0)
{
// CmdCheck tests for @icmd. higcheck tests for i/p hooks.
// Both from RhostMUSH.
// cval/hval values: 0 normal, 1 disable, 2 ignore.
//
if (CmdCheck(executor))
{
cval = cmdtest(executor, T("home"));
}
else if (CmdCheck(Owner(executor)))
{
cval = cmdtest(Owner(executor), T("home"));
}
else
{
cval = 0;
}
if (cval == 0)
{
cval = zonecmdtest(executor, T("home"));
}
if (cval != 2)
{
if (!check_access(executor, mudconf.restrict_home))
{
notify(executor, NOPERM_MESSAGE);
g_debug_cmd = cmdsave;
return preserve_cmd;
}
if (cval == 1)
{
notify(executor, NOPERM_MESSAGE);
g_debug_cmd = cmdsave;
return preserve_cmd;
}
if ( ( Fixed(executor)
|| Fixed(Owner(executor)))
&& !WizRoy(executor))
{
notify(executor, mudconf.fixed_home_msg);
g_debug_cmd = cmdsave;
return preserve_cmd;
}
do_move(executor, caller, enactor, eval, 0, const_cast<UTF8 *>(T("home")), nullptr, 0);
g_debug_cmd = cmdsave;
return preserve_cmd;
}
}
// Only check for exits if we may use the goto command.
//
if (check_access(executor, goto_cmdp->perms))
{
// CmdCheck tests for @icmd. higcheck tests for i/p hooks.
// Both from RhostMUSH.
// cval/hval values: 0 normal, 1 disable, 2 ignore
// Master room exits are not affected.
//
if (CmdCheck(executor))
{
cval = cmdtest(executor, T("goto"));
}
else if (CmdCheck(Owner(executor)))
{
cval = cmdtest(Owner(executor), T("goto"));
}
else
{
cval = 0;
}
if (cval == 0)
{
cval = zonecmdtest(executor, T("goto"));
}
hval = 0;
if ( (goto_cmdp->flags & (CEF_HOOK_IGNORE|CEF_HOOK_PERMIT))
&& bGoodHookObj)
{
if ( (goto_cmdp->flags & CEF_HOOK_IGNORE)
&& !process_hook(executor, goto_cmdp, CEF_HOOK_IGNORE, true))
{
hval = 2;
}
else if ( (goto_cmdp->flags & CEF_HOOK_PERMIT)
&& !process_hook(executor, goto_cmdp, CEF_HOOK_PERMIT, true))
{
hval = 1;
}
}
if ( cval != 2
&& hval != 2)
{
// Check for an exit name.
//
init_match_check_keys(executor, pCommand, TYPE_EXIT);
match_exit_with_parents();
exit = last_match_result();
bool bMaster = (NOTHING == exit);
if (bMaster)
{
// Check for an exit in the master room.
//
init_match_check_keys(executor, pCommand, TYPE_EXIT);
match_master_exit();
exit = last_match_result();
}
if (exit != NOTHING)
{
if ( 1 == cval
|| 1 == hval)
{
if ( (goto_cmdp->flags & CEF_HOOK_AFAIL)
&& bGoodHookObj)
{
process_hook(executor, goto_cmdp, CEF_HOOK_AFAIL, false);
}
else
{
notify(executor, NOPERM_MESSAGE);
}
g_debug_cmd = cmdsave;
return preserve_cmd;
}
if ( (goto_cmdp->flags & CEF_HOOK_BEFORE)
&& bGoodHookObj)
{
process_hook(executor, goto_cmdp, CEF_HOOK_BEFORE, false);
}
if (!bMaster)
{
move_exit(executor, exit, false, T("You cant go that way."), 0);
}
else
{
move_exit(executor, exit, true, nullptr, 0);
}
if ( (goto_cmdp->flags & CEF_HOOK_AFTER)
&& bGoodHookObj)
{
process_hook(executor, goto_cmdp, CEF_HOOK_AFTER, false);
}
g_debug_cmd = cmdsave;
return preserve_cmd;
}
}
}
// Set up a lowercase command and an arg pointer for the hashed
// command check. Since some types of argument processing destroy
// the arguments, make a copy so that we keep the original command
// line intact. Store the edible copy in LowerCaseCommand after
// the lower-cased command.
//
// Make lowercase command (Unicode-aware).
//
size_t iPos = 0;
while ( pCommand[iPos]
&& !mux_isspace(pCommand[iPos]))
{
iPos++;
}
LBuf TempCmd = LBuf_Src("process_command");
size_t nCopy = iPos;
if (nCopy >= LBUF_SIZE)
{
nCopy = LBUF_SIZE - 1;
}
memcpy(TempCmd, pCommand, nCopy);
TempCmd[nCopy] = '\0';
size_t nLowerCaseCommand;
UTF8 *pLower = mux_strlwr(TempCmd, nLowerCaseCommand);
if (nLowerCaseCommand >= LBUF_SIZE)
{
nLowerCaseCommand = LBUF_SIZE - 1;
}
memcpy(LowerCaseCommand, pLower, nLowerCaseCommand);
LowerCaseCommand[nLowerCaseCommand] = '\0';
// Skip spaces before arg
//
while (mux_isspace(pCommand[iPos]))
{
iPos++;
}
// Remember where arg starts
//
arg = pCommand + iPos;
// Strip off any command switches and save them.
//
UTF8 *pSlash = reinterpret_cast<UTF8 *>(strchr(reinterpret_cast<char *>(LowerCaseCommand), '/'));
if (pSlash)
{
nLowerCaseCommand = pSlash - LowerCaseCommand;
*pSlash++ = '\0';
}
// Check for a builtin command (or an alias of a builtin command)
//
{
auto it_cmd = mudstate.command_htab.find(std::vector<UTF8>(LowerCaseCommand, LowerCaseCommand + nLowerCaseCommand));
cmdp = (it_cmd != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_cmd->second) : nullptr;
}
/* If command is checked to ignore NONMATCHING switches, fall through */
if (cmdp)
{
// CmdCheck tests for @icmd. higcheck tests for i/p hooks.
// Both from RhostMUSH.
// cval/hval values: 0 normal, 1 disable, 2 ignore
//
if (CmdCheck(executor))
{
cval = cmdtest(executor, cmdp->cmdname);
}
else if (CmdCheck(Owner(executor)))
{
cval = cmdtest(Owner(executor), cmdp->cmdname);
}
else
{
cval = 0;
}
if (cval == 0)
{
cval = zonecmdtest(executor, cmdp->cmdname);
}
hval = 0;
if ( (cmdp->flags & (CEF_HOOK_IGNORE|CEF_HOOK_PERMIT))
&& bGoodHookObj)
{
if ( (cmdp->flags & CEF_HOOK_IGNORE)
&& !process_hook(executor, cmdp, CEF_HOOK_IGNORE, true))
{
hval = 2;
}
else if ( (cmdp->flags & CEF_HOOK_PERMIT)
&& !process_hook(executor, cmdp, CEF_HOOK_PERMIT, true))
{
hval = 1;
}
}
// If the command contains a switch, but the command doesn't support
// any switches or the command contains one that isn't supported,
// CEF_HOOK_IGSWITCH will allow us to treat the entire command as if it
// weren't a built-in command.
//
int flagvalue;
if ( (cmdp->flags & CEF_HOOK_IGSWITCH)
&& pSlash)
{
if (cmdp->switches)
{
search_nametab(executor, cmdp->switches, pSlash, &flagvalue);
if (flagvalue & SW_MULTIPLE)
{
// All the switches given a command shouldn't exceed 200 chars together
UTF8 switch_buff[200];
UTF8 *switch_ptr;
mux_strncpy(switch_buff, pSlash, sizeof(switch_buff)-1);
string_token st(switch_buff, T("/"));
switch_ptr = st.parse();
while ( switch_ptr
&& *switch_ptr)
{
search_nametab(executor, cmdp->switches, switch_ptr, &flagvalue);
if (flagvalue == -1)
{
break;
}
switch_ptr = st.parse();
}
}
if (flagvalue == -1)
{
cval = 2;
}
}
else
{
// Switch exists but no switches allowed for command.
//
cval = 2;
}
}
if ( cval != 2
&& hval != 2)
{
if ( cval == 1
|| hval == 1)
{
if ( (cmdp->flags & CEF_HOOK_AFAIL)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_AFAIL, false);
}
else
{
notify(executor, NOPERM_MESSAGE);
}
g_debug_cmd = cmdsave;
return preserve_cmd;
}
if ( mudconf.space_compress
&& (cmdp->callseq & CS_NOSQUISH))
{
// We handle this specially -- there is no space compression
// involved, so we must go back to the original command.
// We skip over the command and a single space to position
// arg at the arguments.
//
arg = pCommand = pOriginalCommand;
while (*arg && !mux_isspace(*arg))
{
arg++;
}
if (*arg)
{
// We stopped on the space, advance to next.
//
arg++;
}
}
process_cmdent(cmdp, pSlash, executor, caller, enactor, eval,
interactive, arg, pCommand, args, nargs);
if (mudstate.bStackLimitReached)
{
STARTLOG(LOG_ALWAYS, T("CMD"), T("SPAM"));
log_name_and_loc(executor);
log_text(T(" entered: "));
log_text(pOriginalCommand);
ENDLOG;
}
mudstate.bStackLimitReached = false;
g_debug_cmd = cmdsave;
return preserve_cmd;
}
}
// Check for enter and leave aliases, user-defined commands on the
// player, other objects where the player is, on objects in the
// player's inventory, and on the room that holds the player. We
// evaluate the command line here to allow chains of $-commands
// to work.
//
bp = LowerCaseCommand;
mux_exec(pCommand, LBUF_SIZE-1, LowerCaseCommand, &bp, executor, caller, enactor,
eval|EV_EVAL|EV_FCHECK|EV_STRIP_CURLY|EV_TOP, args, nargs);
*bp = '\0';
bool succ = false;
// Idea for enter/leave aliases from R'nice@TinyTIM
//
if (Has_location(executor) && Good_obj(Location(executor)))
{
// Check for a leave alias.
//
p = atr_pget(Location(executor), A_LALIAS, &aowner, &aflags);
if (*p)
{
if (matches_exit_from_list(LowerCaseCommand, p))
{
free_lbuf(p);
// CmdCheck tests for @icmd. higcheck tests for i/p hooks.
// Both from RhostMUSH.
// cval/hval values: 0 normal, 1 disable, 2 ignore
//
if (CmdCheck(executor))
{
cval = cmdtest(executor, T("leave"));
}
else if (CmdCheck(Owner(executor)))
{
cval = cmdtest(Owner(executor), T("leave"));
}
else
{
cval = 0;
}
if (cval == 0)
{
cval = zonecmdtest(executor, T("leave"));
}
{
const UTF8 *tmp_leave = reinterpret_cast<const UTF8*>("leave");
auto it_leave = mudstate.command_htab.find(std::vector<UTF8>(tmp_leave, tmp_leave + 5));
cmdp = (it_leave != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_leave->second) : nullptr;
}
hval = 0;
if ( (cmdp->flags & (CEF_HOOK_IGNORE|CEF_HOOK_PERMIT))
&& bGoodHookObj)
{
if ( (cmdp->flags & CEF_HOOK_IGNORE)
&& !process_hook(executor, cmdp, CEF_HOOK_IGNORE, true))
{
hval = 2;
}
else if ( (cmdp->flags & CEF_HOOK_PERMIT)
&& !process_hook(executor, cmdp, CEF_HOOK_PERMIT, true))
{
hval = 1;
}
}
if ( cval != 2
&& hval != 2)
{
if ( cval == 1
|| hval == 1)
{
if ( (cmdp->flags & CEF_HOOK_AFAIL)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_AFAIL, false);
}
else
{
notify(executor, NOPERM_MESSAGE);
}
g_debug_cmd = cmdsave;
return preserve_cmd;
}
if ( (cmdp->flags & CEF_HOOK_BEFORE)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_BEFORE, false);
}
do_leave(executor, caller, executor, 0, 0);
if ( (cmdp->flags & CEF_HOOK_AFTER)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_AFTER, false);
}
g_debug_cmd = cmdsave;
return preserve_cmd;
}
}
}
free_lbuf(p);
DOLIST(exit, Contents(Location(executor)))
{
p = atr_pget(exit, A_EALIAS, &aowner, &aflags);
if (*p)
{
if (matches_exit_from_list(LowerCaseCommand, p))
{
free_lbuf(p);
// Check for enter aliases.
//
// CmdCheck tests for @icmd. higcheck tests for i/p hooks.
// Both from RhostMUSH.
// cval/hval values: 0 normal, 1 disable, 2 ignore
//
if (CmdCheck(executor))
{
cval = cmdtest(executor, T("enter"));
}
else if (CmdCheck(Owner(executor)))
{
cval = cmdtest(Owner(executor), T("enter"));
}
else
{
cval = 0;
}
if (cval == 0)
{
cval = zonecmdtest(executor, T("enter"));
}
{
const UTF8 *tmp_enter = reinterpret_cast<const UTF8*>("enter");
auto it_enter = mudstate.command_htab.find(std::vector<UTF8>(tmp_enter, tmp_enter + 5));
cmdp = (it_enter != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_enter->second) : nullptr;
}
hval = 0;
if ( (cmdp->flags & (CEF_HOOK_IGNORE|CEF_HOOK_PERMIT))
&& bGoodHookObj)
{
if ( (cmdp->flags & CEF_HOOK_IGNORE)
&& !process_hook(executor, cmdp, CEF_HOOK_IGNORE, true))
{
hval = 2;
}
else if ( (cmdp->flags & CEF_HOOK_PERMIT)
&& !process_hook(executor, cmdp, CEF_HOOK_PERMIT, true))
{
hval = 1;
}
}
if ( cval != 2
&& hval != 2)
{
if ( cval == 1
|| hval == 1)
{
if ( (cmdp->flags & CEF_HOOK_AFAIL)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_AFAIL, false);
}
else
{
notify(executor, NOPERM_MESSAGE);
}
g_debug_cmd = cmdsave;
return preserve_cmd;
}
if ( (cmdp->flags & CEF_HOOK_BEFORE)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_BEFORE, false);
}
do_enter_internal(executor, exit, false);
if ( (cmdp->flags & CEF_HOOK_AFTER)
&& bGoodHookObj)
{
process_hook(executor, cmdp, CEF_HOOK_AFTER, false);
}
g_debug_cmd = cmdsave;
return preserve_cmd;
}
else if (cval == 1)
{
notify_quiet(executor, NOPERM_MESSAGE);
g_debug_cmd = cmdsave;
return preserve_cmd;
}
}
}
free_lbuf(p);
}
}
// Check for $-command matches on me.
//
if (mudconf.match_mine && !No_Command(executor))
{
if ( ( !isPlayer(executor)
|| mudconf.match_mine_pl)
&& atr_match(executor, executor, AMATCH_CMD, LowerCaseCommand, preserve_cmd, true))
{
succ = true;
}
}
// Check for $-command matches on nearby things and on my room.
//
if (Has_location(executor))
{
succ |= list_check(Contents(Location(executor)), executor, AMATCH_CMD, LowerCaseCommand, preserve_cmd, true);
if (!No_Command(Location(executor)))
{
succ |= atr_match(Location(executor), executor, AMATCH_CMD, LowerCaseCommand, preserve_cmd, true);
}
}
// Check for $-command matches in my inventory.
//
if (Has_contents(executor))
{
succ |= list_check(Contents(executor), executor, AMATCH_CMD, LowerCaseCommand, preserve_cmd, true);
}
if ( !succ
&& mudconf.have_zones)
{
// now do check on zones.
//
dbref zone = Zone(executor);
dbref loc = Location(executor);
dbref zone_loc = NOTHING;
if ( Good_obj(loc)
&& Good_obj(zone_loc = Zone(loc)))
{
if (isRoom(zone_loc))
{
// zone of player's location is a parent room.
//
if (loc != zone)
{
// check parent room exits.
//
init_match_check_keys(executor, pCommand, TYPE_EXIT);
match_zone_exit();
exit = last_match_result();
if (exit != NOTHING)
{
move_exit(executor, exit, true, nullptr, 0);
g_debug_cmd = cmdsave;
return preserve_cmd;
}
succ |= list_check(Contents(zone_loc), executor,
AMATCH_CMD, LowerCaseCommand, preserve_cmd,
true);
// end of parent room checks.
//
}
}
else
{
// try matching commands on area zone object.
//
if (!No_Command(zone_loc))
{
succ |= atr_match(zone_loc, executor, AMATCH_CMD,
LowerCaseCommand, preserve_cmd, true);
}
}
}
// End of matching on zone of player's location.
//
// if nothing matched with parent room/zone object, try matching
// zone commands on the player's personal zone.
//
if ( !succ
&& Good_obj(zone)
&& !No_Command(zone)
&& zone_loc != zone)
{
succ |= atr_match(zone, executor, AMATCH_CMD, LowerCaseCommand, preserve_cmd, true);
}
}
// If we didn't find anything, try in the master room.
//
if (!succ)
{
if ( Good_obj(mudconf.master_room)
&& Has_contents(mudconf.master_room))
{
succ |= list_check(Contents(mudconf.master_room), executor,
AMATCH_CMD, LowerCaseCommand, preserve_cmd, false);
if (!No_Command(mudconf.master_room))
{
succ |= atr_match(mudconf.master_room, executor, AMATCH_CMD,
LowerCaseCommand, preserve_cmd, false);
}
}
}
// If we still didn't find anything, tell how to get help.
//
if (!succ)
{
if ( Good_obj(mudconf.global_error_obj)
&& !Going(mudconf.global_error_obj))
{
LBuf errtext = LBuf_Adopt(atr_get("process_command.2491", mudconf.global_error_obj, A_VA, &aowner, &aflags));
LBuf errbuff = LBuf_Src("process_command.error_msg");
UTF8 *errbufc = errbuff;
mux_exec(errtext, LBUF_SIZE-1, errbuff, &errbufc, mudconf.global_error_obj, caller, enactor,
AttrTrace(aflags, EV_EVAL|EV_FCHECK|EV_STRIP_CURLY|EV_TOP),
(const UTF8 **)&pCommand, 1);
*errbufc = '\0';
notify(executor, errbuff);
}
else
{
// We use LowerCaseCommand for another purpose.
//
notify(executor, M_("Huh? (Type “help” for help.)"));
STARTLOG(LOG_BADCOMMANDS, "CMD", "BAD");
log_name_and_loc(executor);
log_text(T(" entered: "));
log_text(pCommand);
ENDLOG;
}
}
g_debug_cmd = cmdsave;
return preserve_cmd;
}
// ---------------------------------------------------------------------------
// list_cmdtable: List internal commands.
//
static void list_cmdtable(dbref player)
{
LBuf buf = LBuf_Src("list_cmdtable");
UTF8 *bp = buf;
ITL itl;
ItemToList_Init(&itl, buf, &bp);
ItemToList_AddString(&itl, T("Commands:"));
for ( CMDENT_NO_ARG *cmdp0a = command_table_no_arg;
cmdp0a->cmdname;
cmdp0a++)
{
if ( check_access(player, cmdp0a->perms)
&& !(cmdp0a->perms & CF_DARK))
{
ItemToList_AddString(&itl, cmdp0a->cmdname);
}
}
for ( CMDENT_ONE_ARG *cmdp1a = command_table_one_arg;
cmdp1a->cmdname;
cmdp1a++)
{
if ( check_access(player, cmdp1a->perms)
&& !(cmdp1a->perms & CF_DARK))
{
ItemToList_AddString(&itl, cmdp1a->cmdname);
}
}
for ( CMDENT_TWO_ARG *cmdp2a = command_table_two_arg;
cmdp2a->cmdname;
cmdp2a++)
{
if ( check_access(player, cmdp2a->perms)
&& !(cmdp2a->perms & CF_DARK))
{
ItemToList_AddString(&itl, cmdp2a->cmdname);
}
}
for ( CMDENT_TWO_ARG_ARGV *cmdp2av = command_table_two_arg_argv;
cmdp2av->cmdname;
cmdp2av++)
{
if ( check_access(player, cmdp2av->perms)
&& !(cmdp2av->perms & CF_DARK))
{
ItemToList_AddString(&itl, cmdp2av->cmdname);
}
}
// Players get the list of logged-out cmds too
//
if (isPlayer(player))
{
for (NAMETAB *nt = logout_cmdtable; nt->name; nt++)
{
if ( God(player)
|| check_access(player, nt->perm))
{
ItemToList_AddString(&itl, nt->name);
}
}
}
ItemToList_Final(&itl);
notify(player, buf);
}
// ---------------------------------------------------------------------------
// list_attrtable: List available attributes.
//
static void list_attrtable(dbref player)
{
LBuf buf = LBuf_Src("list_attrtable");
UTF8 *bp = buf;
ITL itl;
ItemToList_Init(&itl, buf, &bp);
ItemToList_AddString(&itl, T("Attributes:"));
for (ATTR *ap = AttrTable; ap->name; ap++)
{
if (See_attr(player, player, ap))
{
ItemToList_AddString(&itl, ap->name);
}
}
ItemToList_Final(&itl);
*bp = '\0';
raw_notify(player, buf);
}
// ---------------------------------------------------------------------------
// list_cmdaccess: List access commands.
//
NAMETAB access_nametab[] =
{
{T("builder"), 6, CA_WIZARD, CA_BUILDER},
{T("dark"), 4, CA_GOD, CF_DARK},
{T("disabled"), 4, CA_GOD, CA_DISABLED},
{T("global_build"), 8, CA_PUBLIC, CA_GBL_BUILD},
{T("global_interp"), 8, CA_PUBLIC, CA_GBL_INTERP},
{T("god"), 2, CA_GOD, CA_GOD},
{T("head"), 2, CA_WIZARD, CA_HEAD},
{T("immortal"), 3, CA_WIZARD, CA_IMMORTAL},
{T("need_location"), 6, CA_PUBLIC, CA_LOCATION},
{T("need_contents"), 6, CA_PUBLIC, CA_CONTENTS},
{T("need_player"), 6, CA_PUBLIC, CA_PLAYER},
{T("no_haven"), 4, CA_PUBLIC, CA_NO_HAVEN},
{T("no_robot"), 4, CA_WIZARD, CA_NO_ROBOT},
{T("no_slave"), 5, CA_PUBLIC, CA_NO_SLAVE},
{T("no_suspect"), 5, CA_WIZARD, CA_NO_SUSPECT},
{T("no_guest"), 5, CA_WIZARD, CA_NO_GUEST},
{T("no_uninspected"), 5, CA_WIZARD, CA_NO_UNINS},
{T("robot"), 2, CA_WIZARD, CA_ROBOT},
{T("staff"), 4, CA_WIZARD, CA_STAFF},
{T("static"), 4, CA_GOD, CA_STATIC},
{T("uninspected"), 5, CA_WIZARD, CA_UNINS},
{T("wizard"), 3, CA_WIZARD, CA_WIZARD},
{nullptr, 0, 0, 0}
};
static void list_cmdaccess(dbref player)
{
for ( CMDENT_NO_ARG *cmdp0a = command_table_no_arg;
cmdp0a->cmdname;
cmdp0a++)
{
if ( check_access(player, cmdp0a->perms)
&& !(cmdp0a->perms & CF_DARK))
{
listset_nametab(player, access_nametab, cmdp0a->perms, cmdp0a->cmdname, true);
}
}
for ( CMDENT_ONE_ARG *cmdp1a = command_table_one_arg;
cmdp1a->cmdname;
cmdp1a++)
{
if ( check_access(player, cmdp1a->perms)
&& !(cmdp1a->perms & CF_DARK))
{
listset_nametab(player, access_nametab, cmdp1a->perms, cmdp1a->cmdname, true);
}
}
for ( CMDENT_TWO_ARG *cmdp2a = command_table_two_arg;
cmdp2a->cmdname;
cmdp2a++)
{
if ( check_access(player, cmdp2a->perms)
&& !(cmdp2a->perms & CF_DARK))
{
listset_nametab(player, access_nametab, cmdp2a->perms, cmdp2a->cmdname, true);
}
}
for ( CMDENT_TWO_ARG_ARGV *cmdp2av = command_table_two_arg_argv;
cmdp2av->cmdname;
cmdp2av++)
{
if ( check_access(player, cmdp2av->perms)
&& !(cmdp2av->perms & CF_DARK))
{
listset_nametab(player, access_nametab, cmdp2av->perms, cmdp2av->cmdname, true);
}
}
for (ATTR *ap = AttrTable; ap->name; ap++)
{
if (ap->flags & AF_NOCMD)
{
continue;
}
size_t nBuffer;
bool bValid;
UTF8 *buff2 = MakeCanonicalAttributeCommand(ap->name, &nBuffer, &bValid);
if (!bValid)
{
continue;
}
auto it_b2 = mudstate.command_htab.find(std::vector<UTF8>(buff2, buff2 + nBuffer));
CMDENT *cmdp = (it_b2 != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_b2->second) : nullptr;
if ( nullptr != cmdp
&& check_access(player, cmdp->perms)
&& !(cmdp->perms & CF_DARK))
{
listset_nametab(player, access_nametab, cmdp->perms, cmdp->cmdname, true);
}
}
}
// ---------------------------------------------------------------------------
// list_cmdswitches: List switches for commands.
//
static void list_cmdswitches(dbref player)
{
for ( CMDENT_NO_ARG *cmdp0a = command_table_no_arg;
cmdp0a->cmdname;
cmdp0a++)
{
if ( cmdp0a->switches
&& check_access(player, cmdp0a->perms)
&& !(cmdp0a->perms & CF_DARK))
{
display_nametab(player, cmdp0a->switches, cmdp0a->cmdname, false);
}
}
for ( CMDENT_ONE_ARG *cmdp1a = command_table_one_arg;
cmdp1a->cmdname;
cmdp1a++)
{
if ( cmdp1a->switches
&& check_access(player, cmdp1a->perms)
&& !(cmdp1a->perms & CF_DARK))
{
display_nametab(player, cmdp1a->switches, cmdp1a->cmdname, false);
}
}
for ( CMDENT_TWO_ARG *cmdp2a = command_table_two_arg;
cmdp2a->cmdname;
cmdp2a++)
{
if ( cmdp2a->switches
&& check_access(player, cmdp2a->perms)
&& !(cmdp2a->perms & CF_DARK))
{
display_nametab(player, cmdp2a->switches, cmdp2a->cmdname, false);
}
}
for ( CMDENT_TWO_ARG_ARGV *cmdp2av = command_table_two_arg_argv;
cmdp2av->cmdname;
cmdp2av++)
{
if ( cmdp2av->switches
&& check_access(player, cmdp2av->perms)
&& !(cmdp2av->perms & CF_DARK))
{
display_nametab(player, cmdp2av->switches, cmdp2av->cmdname, false);
}
}
}
// ---------------------------------------------------------------------------
// list_attraccess: List access to attributes.
//
NAMETAB attraccess_nametab[] =
{
{T("const"), 1, CA_PUBLIC, AF_CONST},
{T("dark"), 2, CA_WIZARD, AF_DARK},
{T("deleted"), 2, CA_WIZARD, AF_DELETED},
{T("god"), 1, CA_PUBLIC, AF_GOD},
{T("hidden"), 1, CA_WIZARD, AF_MDARK},
{T("ignore"), 2, CA_WIZARD, AF_NOCMD},
{T("internal"), 2, CA_WIZARD, AF_INTERNAL},
{T("is_lock"), 4, CA_PUBLIC, AF_IS_LOCK},
{T("locked"), 1, CA_PUBLIC, AF_LOCK},
{T("no_command"), 4, CA_PUBLIC, AF_NOPROG},
{T("no_inherit"), 4, CA_PUBLIC, AF_PRIVATE},
{T("private"), 1, CA_PUBLIC, AF_ODARK},
{T("regexp"), 1, CA_PUBLIC, AF_REGEXP},
{T("visual"), 1, CA_PUBLIC, AF_VISUAL},
{T("wizard"), 1, CA_PUBLIC, AF_WIZARD},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
NAMETAB indiv_attraccess_nametab[] =
{
{T("case"), 1, CA_PUBLIC, AF_CASE},
{T("hidden"), 1, CA_WIZARD, AF_MDARK},
{T("html"), 2, CA_PUBLIC, AF_HTML},
{T("no_command"), 4, CA_PUBLIC, AF_NOPROG},
{T("no_inherit"), 4, CA_PUBLIC, AF_PRIVATE},
{T("no_eval"), 4, CA_PUBLIC, AF_NOEVAL},
{T("no_name"), 4, CA_PUBLIC, AF_NONAME},
{T("no_parse"), 4, CA_PUBLIC, AF_NOPARSE},
{T("regexp"), 1, CA_PUBLIC, AF_REGEXP},
{T("trace"), 1, CA_PUBLIC, AF_TRACE},
{T("visual"), 1, CA_PUBLIC, AF_VISUAL},
{T("wizard"), 1, CA_WIZARD, AF_WIZARD},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
static void list_attraccess(dbref player)
{
for (ATTR *ap = AttrTable; ap->name; ap++)
{
if (bCanReadAttr(player, player, ap, false))
{
listset_nametab(player, attraccess_nametab, ap->flags, ap->name, true);
}
}
}
// ---------------------------------------------------------------------------
// cf_access: Change command or switch permissions.
//
CF_HAND(cf_access)
{
UNUSED_PARAMETER(vp);
CMDENT *cmdp;
UTF8 *ap;
bool set_switch;
for (ap = str; *ap && !mux_isspace(*ap) && (*ap != '/'); ap++) ;
if (*ap == '/')
{
set_switch = true;
*ap++ = '\0';
}
else
{
set_switch = false;
if (*ap)
{
*ap++ = '\0';
}
while (mux_isspace(*ap))
{
ap++;
}
}
{
size_t nStr = strlen(reinterpret_cast<char*>(str));
auto it_str = mudstate.command_htab.find(std::vector<UTF8>(str, str + nStr));
cmdp = (it_str != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_str->second) : nullptr;
}
if (cmdp != nullptr)
{
if (set_switch)
{
return cf_ntab_access(reinterpret_cast<int *>(cmdp->switches), ap, pExtra, nExtra,
player, cmd);
}
else
{
return cf_modify_bits(&(cmdp->perms), ap, pExtra, nExtra, player,
cmd);
}
}
else
{
if (!mux_stricmp(str, T("home")))
{
return cf_modify_bits(&(mudconf.restrict_home), ap, pExtra,
nExtra, player, cmd);
}
cf_log_notfound(player, cmd, T("Command"), str);
return -1;
}
}
// ---------------------------------------------------------------------------
// cf_acmd_access: Change command permissions for all attr-setting cmds.
//
CF_HAND(cf_acmd_access)
{
UNUSED_PARAMETER(vp);
ATTR *ap;
for (ap = AttrTable; ap->name; ap++)
{
size_t nBuffer;
bool bValid;
UTF8 *buff = MakeCanonicalAttributeCommand(ap->name, &nBuffer, &bValid);
if (!bValid)
{
continue;
}
auto it_bf = mudstate.command_htab.find(std::vector<UTF8>(buff, buff + nBuffer));
CMDENT *cmdp = (it_bf != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_bf->second) : nullptr;
if (cmdp != nullptr)
{
int save = cmdp->perms;
int failure = cf_modify_bits(&(cmdp->perms), str, pExtra, nExtra,
player, cmd);
if (failure != 0)
{
cmdp->perms = save;
return -1;
}
}
}
return 0;
}
// ---------------------------------------------------------------------------
// cf_attr_access: Change access on an attribute.
//
CF_HAND(cf_attr_access)
{
ATTR *ap;
UTF8 *sp;
ATTRPERM **ppv = (ATTRPERM **)vp;
for (sp = str; *sp && !mux_isspace(*sp); sp++)
{
; // Nothing
}
if (*sp)
{
*sp++ = '\0';
}
while (mux_isspace(*sp))
{
sp++;
}
ap = atr_str(reinterpret_cast<UTF8 *>(str));
if (ap)
{
// This is a straight-out built-in attribute, so we'll just modify directly.
//
return cf_modify_bits(&(ap->flags), sp, pExtra, nExtra, player, cmd);
}
else
{
// This is either a wildcard or a vattr, so should be added to the table.
//
ATTRPERM *perm = nullptr;
try
{
perm = new ATTRPERM;
}
catch (...)
{
; // Nothing.
}
if (nullptr == perm)
{
cf_log_syntax(player, cmd, T("Out of memory."));
return -1;
}
perm->wildcard = StringClone(str);
perm->flags = 0;
ATTRPERM *head = *ppv;
// Add our permission to the list.
//
if (mudstate.bReadingConfiguration)
{
if (nullptr == head)
{
*ppv = perm;
}
else
{
ATTRPERM *last;
for (last = head; last->next; last = last->next)
{
; // Nothing.
}
last->next = perm;
}
}
else
{
perm->next = head;
*ppv = perm;
}
// Call the standard permission parser.
//
return cf_modify_bits(&(perm->flags), sp, pExtra, nExtra, player, cmd);
}
}
// ---------------------------------------------------------------------------
// cf_cmd_alias: Add a command alias.
//
CF_HAND(cf_cmd_alias)
{
UNUSED_PARAMETER(pExtra);
UNUSED_PARAMETER(nExtra);
UTF8 *ap;
CMDENT *cmdp, *cmd2;
NAMETAB *nt;
string_token st(str, T(" \t=,"));
UTF8 *alias = st.parse();
UTF8 *orig = st.parse();
if (!orig)
{
// We only got one argument to @alias. Bad.
//
return -1;
}
for (ap = orig; *ap && (*ap != '/'); ap++) ;
if (*ap == '/')
{
// Switch form of command aliasing: create an alias for
// a command + a switch
//
*ap++ = '\0';
// Look up the command
//
{
StringPtrMap *htab = reinterpret_cast<StringPtrMap*>(vp);
size_t nOrig = strlen(reinterpret_cast<char*>(orig));
auto it_orig = htab->find(std::vector<UTF8>(orig, orig + nOrig));
cmdp = (it_orig != htab->end()) ? static_cast<CMDENT*>(it_orig->second) : nullptr;
}
if (cmdp == nullptr || cmdp->switches == nullptr)
{
cf_log_notfound(player, cmd, T("Command"), orig);
return -1;
}
// Look up the switch
//
nt = find_nametab_ent(player, reinterpret_cast<NAMETAB *>(cmdp->switches), ap);
if (!nt)
{
cf_log_notfound(player, cmd, T("Switch"), ap);
return -1;
}
{
StringPtrMap *htab_chk = reinterpret_cast<StringPtrMap*>(vp);
size_t nAlias = strlen(reinterpret_cast<char*>(alias));
auto it_alias = htab_chk->find(std::vector<UTF8>(alias, alias + nAlias));
if (it_alias == htab_chk->end())
{
// Create the new command table entry.
//
cmd2 = nullptr;
try
{
cmd2 = new CMDENT;
}
catch (...)
{
; // Nothing.
}
if (nullptr == cmd2)
{
STARTLOG(LOG_PROBLEMS, "CMD", "MEM");
log_printf(T("cf_alias: out of memory creating switch alias."));
ENDLOG;
return -1;
}
cmd2->cmdname = StringClone(alias);
cmd2->switches = cmdp->switches;
cmd2->perms = cmdp->perms | nt->perm;
cmd2->extra = (cmdp->extra | nt->flag) & ~SW_MULTIPLE;
if (!(nt->flag & SW_MULTIPLE))
{
cmd2->extra |= SW_GOT_UNIQUE;
}
cmd2->callseq = cmdp->callseq;
cmd2->handler = cmdp->handler;
cmd2->flags = CEF_ALLOC;
size_t nCmd2 = strlen(reinterpret_cast<char*>(cmd2->cmdname));
htab_chk->emplace(std::vector<UTF8>(cmd2->cmdname, cmd2->cmdname + nCmd2), cmd2);
}
}
}
else
{
// A normal (non-switch) alias
//
StringPtrMap *htab_ns = reinterpret_cast<StringPtrMap*>(vp);
size_t nOrig2 = strlen(reinterpret_cast<char*>(orig));
auto it_hp = htab_ns->find(std::vector<UTF8>(orig, orig + nOrig2));
void *hp = (it_hp != htab_ns->end()) ? it_hp->second : nullptr;
if (hp == nullptr)
{
cf_log_notfound(player, cmd, T("Entry"), orig);
return -1;
}
size_t nAlias2 = strlen(reinterpret_cast<char*>(alias));
htab_ns->emplace(std::vector<UTF8>(alias, alias + nAlias2), hp);
}
return 0;
}
// ---------------------------------------------------------------------------
// list_df_flags: List default flags at create time.
//
static void list_df_flags(dbref player)
{
FLAGSET fs;
fs = mudconf.player_flags;
fs.word[FLAG_WORD1] |= TYPE_PLAYER;
UTF8 *playerb = decode_flags(player, &fs);
fs = mudconf.room_flags;
fs.word[FLAG_WORD1] |= TYPE_ROOM;
UTF8 *roomb = decode_flags(player, &fs);
fs = mudconf.exit_flags;
fs.word[FLAG_WORD1] |= TYPE_EXIT;
UTF8 *exitb = decode_flags(player, &fs);
fs = mudconf.thing_flags;
fs.word[FLAG_WORD1] |= TYPE_THING;
UTF8 *thingb = decode_flags(player, &fs);
fs = mudconf.robot_flags;
fs.word[FLAG_WORD1] |= TYPE_PLAYER;
UTF8 *robotb = decode_flags(player, &fs);
LBuf buff = LBuf_Src("list_df_flags");
mux_sprintf(buff, LBUF_SIZE,
T("Default flags: Players...%s Rooms...%s Exits...%s Things...%s Robots...%s"),
playerb, roomb, exitb, thingb, robotb);
free_sbuf(playerb);
free_sbuf(roomb);
free_sbuf(exitb);
free_sbuf(thingb);
free_sbuf(robotb);
raw_notify(player, buff);
}
// ---------------------------------------------------------------------------
// list_costs: List the costs of things.
//
#define coin_name(s) (((s)==1) ? mudconf.one_coin : mudconf.many_coins)
static void list_costs(dbref player)
{
UTF8 *buff = alloc_mbuf("list_costs");
if (mudconf.quotas)
{
mux_sprintf(buff, MBUF_SIZE, T(" and %d quota"), mudconf.room_quota);
}
notify(player,
tprintf(M_("Digging a room costs %d %s%s."),
mudconf.digcost, coin_name(mudconf.digcost), buff));
if (mudconf.quotas)
{
mux_sprintf(buff, MBUF_SIZE, T(" and %d quota"), mudconf.exit_quota);
}
notify(player,
tprintf(M_("Opening a new exit costs %d %s%s."),
mudconf.opencost, coin_name(mudconf.opencost), buff));
notify(player,
tprintf(M_("Linking an exit, home, or dropto costs %d %s."),
mudconf.linkcost, coin_name(mudconf.linkcost)));
if (mudconf.quotas)
{
mux_sprintf(buff, MBUF_SIZE, T(" and %d quota"), mudconf.thing_quota);
}
if (mudconf.createmin == mudconf.createmax)
{
raw_notify(player,
tprintf(M_("Creating a new thing costs %d %s%s."),
mudconf.createmin,
coin_name(mudconf.createmin), buff));
}
else
{
raw_notify(player,
tprintf(M_("Creating a new thing costs between %d and %d %s%s."),
mudconf.createmin, mudconf.createmax,
mudconf.many_coins, buff));
}
if (mudconf.quotas)
{
mux_sprintf(buff, MBUF_SIZE, T(" and %d quota"), mudconf.player_quota);
}
notify(player,
tprintf(M_("Creating a robot costs %d %s%s."),
mudconf.robotcost, coin_name(mudconf.robotcost), buff));
if (mudconf.killmin == mudconf.killmax)
{
int chance = 100;
if (0 < mudconf.killguarantee)
{
chance = (mudconf.killmin * 100) / mudconf.killguarantee;
}
raw_notify(player, tprintf(M_("Killing costs %d %s, with a %d%% chance of success."),
mudconf.killmin, coin_name(mudconf.digcost), chance));
}
else
{
int cost_surething;
raw_notify(player, tprintf(M_("Killing costs between %d and %d %s."),
mudconf.killmin, mudconf.killmax, mudconf.many_coins));
if (0 < mudconf.killguarantee)
{
cost_surething = mudconf.killguarantee;
}
else
{
cost_surething = mudconf.killmin;
}
raw_notify(player, tprintf(M_("You must spend %d %s to guarantee success."),
cost_surething, coin_name(cost_surething)));
}
raw_notify(player,
tprintf(M_("Computationally expensive commands and functions (ie: @entrances, @find, @search, @stats (with an argument or switch), search(), and stats()) cost %d %s."),
mudconf.searchcost, coin_name(mudconf.searchcost)));
if (mudconf.machinecost > 0)
raw_notify(player,
tprintf(M_("Each command run from the queue costs 1/%d %s."),
mudconf.machinecost, mudconf.one_coin));
if (mudconf.waitcost > 0)
{
raw_notify(player,
tprintf(M_("A %d %s deposit is charged for putting a command on the queue."),
mudconf.waitcost, mudconf.one_coin));
raw_notify(player, M_("The deposit is refunded when the command is run or canceled."));
}
if (mudconf.sacfactor == 0)
{
mux_ltoa(mudconf.sacadjust, buff);
}
else if (mudconf.sacfactor == 1)
{
if (mudconf.sacadjust < 0)
mux_sprintf(buff, MBUF_SIZE, T("<create cost> - %d"), -mudconf.sacadjust);
else if (mudconf.sacadjust > 0)
mux_sprintf(buff, MBUF_SIZE, T("<create cost> + %d"), mudconf.sacadjust);
else
mux_sprintf(buff, MBUF_SIZE, T("<create cost>"));
}
else
{
if (mudconf.sacadjust < 0)
mux_sprintf(buff, MBUF_SIZE, T("(<create cost> / %d) - %d"), mudconf.sacfactor, -mudconf.sacadjust);
else if (mudconf.sacadjust > 0)
mux_sprintf(buff, MBUF_SIZE, T("(<create cost> / %d) + %d"), mudconf.sacfactor, mudconf.sacadjust);
else
mux_sprintf(buff, MBUF_SIZE, T("<create cost> / %d"), mudconf.sacfactor);
}
raw_notify(player, tprintf(M_("The value of an object is %s."), buff));
if (mudconf.clone_copy_cost)
raw_notify(player, M_("The default value of cloned objects is the value of the original object."));
else
raw_notify(player, tprintf(M_("The default value of cloned objects is %d %s."),
mudconf.createmin, coin_name(mudconf.createmin)));
free_mbuf(buff);
}
// ---------------------------------------------------------------------------
// list_options: List more game options from mudconf.
//
static const UTF8 *switchd[] =
{
T("/first"),
T("/all")
};
static const UTF8 *examd[] =
{
T("/brief"),
T("/full")
};
static const UTF8 *ed[] =
{
T("Disabled"),
T("Enabled")
};
static void list_options(dbref player)
{
UTF8 *buff;
CLinearTimeAbsolute ltaNow;
ltaNow.GetUTC();
if (mudconf.quotas)
raw_notify(player, M_("Building quotas are enforced."));
if (mudconf.name_spaces)
raw_notify(player, M_("Player names may contain spaces."));
else
raw_notify(player, M_("Player names may not contain spaces."));
if (!mudconf.robot_speak)
raw_notify(player, M_("Robots are not allowed to speak in public areas."));
if (mudconf.player_listen)
raw_notify(player, M_("The @Listen/@Ahear attribute set works on player objects."));
if (mudconf.ex_flags)
{
raw_notify(player, M_("The examine command lists the flag names for the objects flags."));
}
if (!mudconf.quiet_look)
raw_notify(player, M_("The look command shows visible attributes in addition to the description."));
if (mudconf.see_own_dark)
raw_notify(player, M_("The look command lists DARK objects owned by you."));
if (!mudconf.dark_sleepers)
raw_notify(player, M_("The look command shows disconnected players."));
if (mudconf.terse_look)
raw_notify(player, M_("The look command obeys the TERSE flag."));
if (mudconf.trace_topdown)
{
raw_notify(player, M_("Trace output is presented top-down (whole expression first, then sub-exprs)."));
raw_notify(player, tprintf(M_("Only %d lines of trace output are displayed."), mudconf.trace_limit));
}
else
{
raw_notify(player, M_("Trace output is presented bottom-up (subexpressions first)."));
}
if (!mudconf.quiet_whisper)
raw_notify(player, M_("The whisper command lets others in the room with you know you whispered."));
if (mudconf.pemit_players)
raw_notify(player, M_("The @pemit command may be used to emit to faraway players."));
if (!mudconf.terse_contents)
raw_notify(player, M_("The TERSE flag suppresses listing the contents of a location."));
if (!mudconf.terse_exits)
raw_notify(player, M_("The TERSE flag suppresses listing obvious exits in a location."));
if (!mudconf.terse_movemsg)
raw_notify(player, M_("The TERSE flag suppresses enter/leave/succ/drop messages generated by moving."));
if (mudconf.pub_flags)
{
raw_notify(player, M_("The flags() function will return the flags of any object."));
}
if (mudconf.read_rem_desc)
raw_notify(player, M_("The get() function will return the description of faraway objects,"));
if (mudconf.read_rem_name)
raw_notify(player, M_("The name() function will return the name of faraway objects."));
raw_notify(player, tprintf(M_("The default switch for the @switch command is %s."), switchd[mudconf.switch_df_all]));
raw_notify(player, tprintf(M_("The default switch for the examine command is %s."), examd[mudconf.exam_public]));
if (mudconf.sweep_dark)
raw_notify(player, M_("Players may @sweep dark locations."));
if (mudconf.fascist_tport)
raw_notify(player, M_("You may only @teleport out of locations that are JUMP_OK or that you control."));
raw_notify(player,
tprintf(M_("Players may have at most %d commands in the queue at one time."),
mudconf.queuemax));
if (mudconf.match_mine)
{
if (mudconf.match_mine_pl)
raw_notify(player, M_("All objects search themselves for $-commands."));
else
raw_notify(player, M_("Objects other than players search themselves for $-commands."));
}
if (!Wizard(player))
return;
buff = alloc_mbuf("list_options");
raw_notify(player,
tprintf(M_("%d commands are run from the queue when there is no net activity."),
mudconf.queue_chunk));
raw_notify(player,
tprintf(M_("%d commands are run from the queue when there is net activity."),
mudconf.active_q_chunk));
if (mudconf.idle_wiz_dark)
raw_notify(player, M_("Wizards idle for longer than the default timeout are automatically set DARK."));
if (mudconf.safe_unowned)
raw_notify(player, M_("Objects not owned by you are automatically considered SAFE."));
if (mudconf.paranoid_alloc)
raw_notify(player, M_("The buffer pools are checked for consistency on each allocate or free."));
if (mudconf.cache_names)
raw_notify(player, M_("A separate name cache is used."));
#if defined(HAVE_WORKING_FORK)
if (mudconf.fork_dump)
{
raw_notify(player, M_("Database dumps are performed by a fork()ed process."));
}
#endif // HAVE_WORKING_FORK
if (mudconf.max_players >= 0)
raw_notify(player,
tprintf(MN_("There may be at most %d player logged in at once.",
"There may be at most %d players logged in at once.",
mudconf.max_players),
mudconf.max_players));
if (mudconf.quotas)
mux_sprintf(buff, MBUF_SIZE, T(" and %d quota"), mudconf.start_quota);
else
*buff = '\0';
raw_notify(player,
tprintf(M_("New players are given %d %s to start with."),
mudconf.paystart, mudconf.many_coins));
raw_notify(player,
tprintf(M_("Players are given %d %s each day they connect."),
mudconf.paycheck, mudconf.many_coins));
raw_notify(player,
tprintf(M_("Earning money is difficult if you have more than %d %s."),
mudconf.paylimit, mudconf.many_coins));
if (mudconf.payfind > 0)
raw_notify(player,
tprintf(M_("Players have a 1 in %d chance of finding a %s each time they move."),
mudconf.payfind, mudconf.one_coin));
raw_notify(player,
tprintf(T("The head of the object freelist is #%d."),
mudstate.freelist));
mux_sprintf(buff, MBUF_SIZE, T("Intervals: Dump...%d Clean...%d Idlecheck...%d"),
mudconf.dump_interval, mudconf.check_interval,
mudconf.idle_interval);
raw_notify(player, buff);
CLinearTimeDelta ltdDump = mudstate.dump_counter - ltaNow;
CLinearTimeDelta ltdCheck = mudstate.check_counter - ltaNow;
CLinearTimeDelta ltdIdle = mudstate.idle_counter - ltaNow;
long lDump = ltdDump.ReturnSeconds();
long lCheck = ltdCheck.ReturnSeconds();
long lIdle = ltdIdle.ReturnSeconds();
mux_sprintf(buff, MBUF_SIZE, T("Timers: Dump...%ld Clean...%ld Idlecheck...%ld"),
lDump, lCheck, lIdle);
raw_notify(player, buff);
mux_sprintf(buff, MBUF_SIZE, T("Timeouts: Idle...%d Connect...%d Tries...%d"),
mudconf.idle_timeout, mudconf.conn_timeout,
mudconf.retry_limit);
raw_notify(player, buff);
mux_sprintf(buff, MBUF_SIZE, T("Scheduling: Timeslice...%s Max_Quota...%d Increment...%d"),
mudconf.timeslice.ReturnSecondsString(3), mudconf.cmd_quota_max,
mudconf.cmd_quota_incr);
raw_notify(player, buff);
mux_sprintf(buff, MBUF_SIZE, T("Spaces...%s"),
ed[mudconf.space_compress]);
raw_notify(player, buff);
mux_sprintf(buff, MBUF_SIZE, T("New characters: Room...#%d Home...#%d DefaultHome...#%d Quota...%d"),
mudconf.start_room, mudconf.start_home, mudconf.default_home,
mudconf.start_quota);
raw_notify(player, buff);
mux_sprintf(buff, MBUF_SIZE, T("Misc: GuestChar...#%d IdleQueueChunk...%d ActiveQueueChunk...%d Master_room...#%d"),
mudconf.guest_char, mudconf.queue_chunk,
mudconf.active_q_chunk, mudconf.master_room);
raw_notify(player, buff);
free_mbuf(buff);
}
// ---------------------------------------------------------------------------
// list_vattrs: List user-defined attributes
//
static void list_vattrs(dbref player, UTF8 *s_mask)
{
bool wild_mtch = s_mask
&& s_mask[0] != '\0';
LBuf buff = LBuf_Src("list_vattrs");
// If wild_match, then only list attributes that match wildcard(s)
//
UTF8 *p = tprintf(T("--- User-Defined Attributes %s---"),
wild_mtch ? "(wildmatched) " : "");
raw_notify(player, p);
ATTR *va;
int na;
int wna = 0;
for (va = vattr_first(), na = 0; va; va = vattr_next(va), na++)
{
if (!(va->flags & AF_DELETED))
{
// We need to be extremely careful that s_mask is !null and valid
//
if (wild_mtch)
{
mudstate.wild_invk_ctr = 0;
if (!quick_wild(s_mask, va->name))
{
continue;
}
wna++;
}
mux_sprintf(buff, LBUF_SIZE, T("%s(%d)"), va->name, va->number);
listset_nametab(player, attraccess_nametab, va->flags, buff, true);
}
}
if (wild_mtch)
{
p = tprintf(T("%d attributes matched, %d attributes total, next=%d"), wna,
na, mudstate.attr_next);
}
else
{
p = tprintf(T("%d attributes, next=%d"), na, mudstate.attr_next);
}
raw_notify(player, p);
}
// LeftJustifyString and RightJustifyNumber moved to stringutil.cpp (libmux.so).
// list_hashstats: List information from hash tables
//
// Hash table size list (#1667 Phase 4 C3). The old full-stat header
// (Size/Num/Del/Lookups/…) did not match the abbreviated rows (name +
// entry count only). Header and rows now share one schema.
//
static const size_t kHashNameCols = 13;
static const size_t kHashEntriesCols = 11;
static void list_hashstat_abbreviated(const dbref player, const UTF8* tab_name, const int entries)
{
UTF8 buff[MBUF_SIZE];
UTF8 num[16];
size_t pos = 0;
pos = mux_table_append_ljust(buff, sizeof(buff), pos, tab_name, kHashNameCols);
pos = mux_table_append_bytes(buff, sizeof(buff), pos, " ");
// RightJustifyNumber — mux_vsnprintf has no %* width (#1429).
//
size_t nw = RightJustifyNumber(num, kHashEntriesCols,
static_cast<int64_t>(entries), ' ');
num[nw] = '\0';
pos = mux_table_append_bytes(buff, sizeof(buff), pos,
reinterpret_cast<const char *>(num));
raw_notify(player, buff);
}
static void list_hashstats(const dbref player)
{
{
UTF8 header[MBUF_SIZE];
size_t pos = 0;
pos = mux_table_append_ljust(header, sizeof(header), pos,
M_("Name"), kHashNameCols);
pos = mux_table_append_bytes(header, sizeof(header), pos, " ");
pos = mux_table_append_ljust(header, sizeof(header), pos,
M_("Entries"), kHashEntriesCols);
raw_notify(player, header);
}
list_hashstat_abbreviated(player, T("Commands"), static_cast<int>(mudstate.command_htab.size()));
list_hashstat_abbreviated(player, T("Logout Cmds"), static_cast<int>(mudstate.logout_cmd_htab.size()));
list_hashstat_abbreviated(player, T("Functions"), static_cast<int>(mudstate.builtin_functions.size()));;
list_hashstat_abbreviated(player, T("Flags"), static_cast<int>(mudstate.flag_names_map.size()));;
list_hashstat_abbreviated(player, T("Powers"), static_cast<int>(mudstate.powers_htab.size()));
list_hashstat_abbreviated(player, T("Attr Names"), static_cast<int>(mudstate.builtin_attribute_names.size()));
list_hashstat_abbreviated(player, T("Vattr Names"), static_cast<int>(mudstate.vattr_name_map.size()));
list_hashstat_abbreviated(player, T("Player Names"), static_cast<int>(mudstate.player_htab.size()));
list_hashstat_abbreviated(player, T("Net Descr."), get_total_connections());
list_hashstat_abbreviated(player, T("Fwd. lists"), static_cast<int>(mudstate.forward_lists.size()));
list_hashstat_abbreviated(player, T("Excl. $-cmds"), static_cast<int>(mudstate.parent_htab.size()));
list_hashstat_abbreviated(player, T("Mail Messages"), static_cast<int>(mudstate.mail_htab.size()));
list_hashstat_abbreviated(player, T("Channel Names"), static_cast<int>(mudstate.channel_names.size()));;
list_hashstat_abbreviated(player, T("Attr. Cache"), static_cast<int>(mudstate.attribute_lru_cache_list.size()));
for (int i = 0; i < mudstate.nHelpDesc; i++)
{
list_hashstat_abbreviated(player, mudstate.aHelpDesc[i].pBaseFilename,
static_cast<int>(mudstate.aHelpDesc[i].ht->size()));
}
}
// ---------------------------------------------------------------------------
// list_process: List local resource usage stats of the mux process.
// Adapted from code by Claudius@PythonMUCK,
// posted to the net by Howard/Dark_Lord.
//
static void list_process(dbref player)
{
#ifdef HAVE_GETRUSAGE
struct rusage usage;
int ixrss, idrss, isrss, curr, last, dur;
getrusage(RUSAGE_SELF, &usage);
// Calculate memory use from the aggregate totals.
//
curr = mudstate.mstat_curr;
last = 1 - curr;
dur = mudstate.mstat_secs[curr] - mudstate.mstat_secs[last];
if (dur > 0)
{
ixrss = (mudstate.mstat_ixrss[curr] -
mudstate.mstat_ixrss[last]) / dur;
idrss = (mudstate.mstat_idrss[curr] -
mudstate.mstat_idrss[last]) / dur;
isrss = (mudstate.mstat_isrss[curr] -
mudstate.mstat_isrss[last]) / dur;
}
else
{
ixrss = 0;
idrss = 0;
isrss = 0;
}
#endif // HAVE_GETRUSAGE
#if defined(UNIX_NETWORKING)
#ifdef HAVE_GETDTABLESIZE
int maxfds = getdtablesize();
#else // HAVE_GETDTABLESIZE
int maxfds = sysconf(_SC_OPEN_MAX);
#endif // HAVE_GETDTABLESIZE
long psize = sysconf(_SC_PAGESIZE);
if (psize <= 0)
{
psize = 4096;
}
raw_notify(player, tprintf(T("Process ID: %10d %10ld bytes per page"), game_pid, psize));
#else // UNIX_NETWORKING
raw_notify(player, tprintf(T("Process ID: %10d"), game_pid));
#endif // UNIX_NETWORKING
#ifdef HAVE_GETRUSAGE
raw_notify(player, tprintf(T("Time used: %10d user %10d sys"),
usage.ru_utime.tv_sec, usage.ru_stime.tv_sec));
raw_notify(player, tprintf(T("Resident mem:%10d shared %10d private%10d stack"),
ixrss, idrss, isrss));
raw_notify(player,
tprintf(T("Integral mem:%10ld shared %10ld private%10ld stack"),
usage.ru_ixrss, usage.ru_idrss, usage.ru_isrss));
raw_notify(player,
tprintf(T("Max res mem: %10ld pages %10ld bytes"),
usage.ru_maxrss, (usage.ru_maxrss * static_cast<long>(psize))));
raw_notify(player,
tprintf(T("Page faults: %10ld hard %10ld soft %10ld swapouts"),
usage.ru_majflt, usage.ru_minflt, usage.ru_nswap));
raw_notify(player,
tprintf(T("Disk I/O: %10ld reads %10ld writes"),
usage.ru_inblock, usage.ru_oublock));
raw_notify(player,
tprintf(T("Network I/O: %10ld in %10ld out"),
usage.ru_msgrcv, usage.ru_msgsnd));
raw_notify(player,
tprintf(T("Context swi: %10ld vol %10ld forced %10ld sigs"),
usage.ru_nvcsw, usage.ru_nivcsw, usage.ru_nsignals));
raw_notify(player,
tprintf(T("Descs avail: %10d"), maxfds));
#endif // HAVE_GETRUSAGE
}
//----------------------------------------------------------------------------
// list_modules
//
//
static void list_modules(dbref executor)
{
raw_notify(executor, M_("Modules:"));
int i;
for (i = 0; ; i++)
{
MUX_MODULE_INFO ModuleInfo;
MUX_RESULT mr = mux_ModuleInfo(i, &ModuleInfo);
if ( MUX_FAILED(mr)
|| MUX_S_FALSE == mr)
{
break;
}
raw_notify(executor, tprintf(T("%s (%s)"), ModuleInfo.pName, ModuleInfo.bLoaded ? T("loaded") : T("unloaded")));
}
#ifdef STUB_SLAVE
{
mux_ISlaveControl *pISlaveControl = nullptr;
MUX_RESULT mr = mux_CreateInstance(CID_StubSlave, nullptr,
UseSlaveProcess, IID_ISlaveControl,
(void **)&pISlaveControl);
if (MUX_SUCCEEDED(mr) && nullptr != pISlaveControl)
{
for (i = 0; ; i++)
{
MUX_MODULE_INFO ModuleInfo;
mr = pISlaveControl->ModuleInfo(i, &ModuleInfo);
if ( MUX_FAILED(mr)
|| MUX_S_FALSE == mr)
{
break;
}
raw_notify(executor, tprintf(T("%s (%s) by stubslave"), ModuleInfo.pName, ModuleInfo.bLoaded ? T("loaded") : T("unloaded")));
}
pISlaveControl->Release();
}
else
{
// Say so, rather than printing nothing (#1535).
//
// Without this the whole slave section vanished on failure, and
// @list modules answered as if the slave held no modules -- while
// the slave process demonstrably had them mapped. The operator
// could not tell "nothing is loaded there" from "I could not ask",
// which on the release build (--enable-stubslave) is the only
// question this command exists to answer about the slave.
//
// It fails every time today: the interface is created a second
// time here, the slave's reply frame comes back empty, and
// mux_UnmarshalInterface reports MUX_E_CLASSNOTAVAILABLE (-3).
// Measured on aarch64 with a module confirmed loaded in the slave
// via /proc/<pid>/maps. That defect is not fixed here -- this
// only stops it being invisible.
//
raw_notify(executor, tprintf(
T("Could not query the stubslave module list (error %d)."),
static_cast<int>(mr)));
}
}
#endif
}
//----------------------------------------------------------------------------
// list_rlevels
//
#ifdef REALITY_LVLS
static void list_rlevels(dbref player)
{
// Reality levels (#1667 Phase 4 C9): labeled freeform fields with
// mux_table name width for the level name.
//
static const size_t kRlevelNameCols = 20;
int i;
raw_notify(player, M_("Reality levels:"));
for (i = 0; i < mudconf.no_levels; ++i)
{
UTF8 line[LBUF_SIZE];
UTF8 valbuf[32];
size_t pos = 0;
pos = mux_table_append_bytes(line, sizeof(line), pos, " ");
pos = mux_table_append_bytes(line, sizeof(line), pos,
reinterpret_cast<const char *>(M_("Level")));
pos = mux_table_append_bytes(line, sizeof(line), pos, ": ");
pos = mux_table_append_ljust(line, sizeof(line), pos,
mudconf.reality_level[i].name, kRlevelNameCols);
pos = mux_table_append_bytes(line, sizeof(line), pos, " ");
pos = mux_table_append_bytes(line, sizeof(line), pos,
reinterpret_cast<const char *>(M_("Value")));
pos = mux_table_append_bytes(line, sizeof(line), pos, ": ");
mux_sprintf(valbuf, sizeof(valbuf), T("0x%08x"),
mudconf.reality_level[i].value);
pos = mux_table_append_bytes(line, sizeof(line), pos,
reinterpret_cast<const char *>(valbuf));
pos = mux_table_append_bytes(line, sizeof(line), pos, " ");
pos = mux_table_append_bytes(line, sizeof(line), pos,
reinterpret_cast<const char *>(M_("Desc")));
pos = mux_table_append_bytes(line, sizeof(line), pos, ": ");
pos = mux_table_append_bytes(line, sizeof(line), pos,
reinterpret_cast<const char *>(mudconf.reality_level[i].attr));
raw_notify(player, line);
}
raw_notify(player, M_("--Completed."));
}
#endif // REALITY_LVLS
// ---------------------------------------------------------------------------
// do_list: List information stored in internal structures.
//
#define LIST_ATTRIBUTES 1
#define LIST_COMMANDS 2
#define LIST_COSTS 3
#define LIST_FLAGS 4
#define LIST_FUNCTIONS 5
#define LIST_GLOBALS 6
#define LIST_ALLOCATOR 7
#define LIST_LOGGING 8
#define LIST_DF_FLAGS 9
#define LIST_PERMS 10
#define LIST_ATTRPERMS 11
#define LIST_OPTIONS 12
#define LIST_HASHSTATS 13
#define LIST_BUFTRACE 14
#define LIST_CONF_PERMS 15
#define LIST_SITEINFO 16
#define LIST_POWERS 17
#define LIST_SWITCHES 18
#define LIST_VATTRS 19
#define LIST_DB_STATS 20
#define LIST_PROCESS 21
#define LIST_BADNAMES 22
#define LIST_GUESTS 24
#define LIST_MODULES 25
#define LIST_CACHE 26
#define LIST_LUA 27
#ifdef REALITY_LVLS
#define LIST_RLEVELS 28
#endif
NAMETAB list_names[] =
{
{T("allocations"), 2, CA_WIZARD, LIST_ALLOCATOR},
{T("attr_permissions"), 5, CA_WIZARD, LIST_ATTRPERMS},
{T("attributes"), 2, CA_PUBLIC, LIST_ATTRIBUTES},
{T("bad_names"), 2, CA_WIZARD, LIST_BADNAMES},
{T("buffers"), 2, CA_WIZARD, LIST_BUFTRACE},
{T("cache"), 2, CA_WIZARD, LIST_CACHE},
{T("commands"), 3, CA_PUBLIC, LIST_COMMANDS},
{T("config_permissions"), 3, CA_GOD, LIST_CONF_PERMS},
{T("costs"), 3, CA_PUBLIC, LIST_COSTS},
{T("db_stats"), 2, CA_WIZARD, LIST_DB_STATS},
{T("default_flags"), 1, CA_PUBLIC, LIST_DF_FLAGS},
{T("flags"), 2, CA_PUBLIC, LIST_FLAGS},
{T("functions"), 2, CA_PUBLIC, LIST_FUNCTIONS},
{T("globals"), 2, CA_WIZARD, LIST_GLOBALS},
{T("hashstats"), 1, CA_WIZARD, LIST_HASHSTATS},
{T("logging"), 1, CA_GOD, LIST_LOGGING},
{T("lua"), 2, CA_PUBLIC, LIST_LUA},
{T("modules"), 1, CA_WIZARD, LIST_MODULES},
{T("options"), 1, CA_PUBLIC, LIST_OPTIONS},
{T("permissions"), 2, CA_WIZARD, LIST_PERMS},
{T("powers"), 2, CA_WIZARD, LIST_POWERS},
{T("process"), 2, CA_WIZARD, LIST_PROCESS},
{T("site_information"), 2, CA_WIZARD, LIST_SITEINFO},
{T("switches"), 2, CA_PUBLIC, LIST_SWITCHES},
{T("user_attributes"), 1, CA_WIZARD, LIST_VATTRS},
{T("guests"), 2, CA_WIZARD, LIST_GUESTS},
#ifdef REALITY_LVLS
{T("rlevels"), 3, CA_PUBLIC, LIST_RLEVELS},
#endif
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};
void do_list(dbref executor, dbref caller, dbref enactor, int eval, const int key,
UTF8 *arg, const UTF8 *cargs[], const int ncargs)
{
UNUSED_PARAMETER(caller);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(key);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
string_token st(arg, T(" \t=,"));
UTF8* s_option = st.parse();
int flagvalue;
if (!search_nametab(executor, list_names, arg, &flagvalue))
{
if (flagvalue == -1)
{
display_nametab(executor, list_names, T("Unknown option. Use one of"), true);
}
else
{
notify(executor, NOPERM_MESSAGE);
}
return;
}
switch (flagvalue)
{
case LIST_ALLOCATOR:
list_bufstats(executor);
break;
case LIST_BUFTRACE:
list_buftrace(executor);
break;
case LIST_ATTRIBUTES:
list_attrtable(executor);
break;
case LIST_COMMANDS:
list_cmdtable(executor);
break;
case LIST_SWITCHES:
list_cmdswitches(executor);
break;
case LIST_COSTS:
list_costs(executor);
break;
case LIST_OPTIONS:
list_options(executor);
break;
case LIST_HASHSTATS:
list_hashstats(executor);
break;
case LIST_SITEINFO:
list_siteinfo(executor);
break;
case LIST_FLAGS:
display_flagtab(executor);
break;
case LIST_FUNCTIONS:
list_functable(executor);
break;
case LIST_GLOBALS:
interp_nametab(executor, enable_names, mudconf.control_flags,
T("Global parameters:"), T("enabled"), T("disabled"));
break;
case LIST_DF_FLAGS:
list_df_flags(executor);
break;
case LIST_PERMS:
list_cmdaccess(executor);
break;
case LIST_CONF_PERMS:
list_cf_access(executor);
break;
case LIST_POWERS:
display_powertab(executor);
break;
case LIST_ATTRPERMS:
list_attraccess(executor);
break;
case LIST_VATTRS:
s_option = st.parse();
list_vattrs(executor, s_option);
break;
case LIST_LOGGING:
interp_nametab(executor, logoptions_nametab, mudconf.log_options,
T("Events Logged:"), T("enabled"), T("disabled"));
interp_nametab(executor, logdata_nametab, mudconf.log_info,
T("Information Logged:"), T("yes"), T("no"));
break;
case LIST_DB_STATS:
list_cache_stats(executor);
break;
case LIST_PROCESS:
list_process(executor);
break;
case LIST_BADNAMES:
badname_list(executor, T("Disallowed names:"));
break;
case LIST_GUESTS:
Guest.ListAll(executor);
break;
case LIST_MODULES:
list_modules(executor);
break;
case LIST_CACHE:
list_cache_stats(executor);
break;
case LIST_LUA:
if (nullptr != mudstate.pILuaControl)
{
size_t nCalls, nErrors, nInsnHits, nMemHits, nBytes;
size_t nCacheHits, nCacheMisses, nCacheEntries;
mudstate.pILuaControl->GetStats(&nCalls, &nErrors,
&nInsnHits, &nMemHits, &nBytes,
&nCacheHits, &nCacheMisses, &nCacheEntries);
notify(executor, M_("Lua module loaded (Lua 5.4)."));
notify(executor, tprintf(T(" Lua JIT: %s"),
mudconf.lua_jit ? T("enabled") : T("disabled (default)")));
notify(executor, tprintf(T(" Calls: %zu"), nCalls));
notify(executor, tprintf(T(" Errors: %zu"), nErrors));
notify(executor, tprintf(T(" Insn limit hits: %zu"), nInsnHits));
notify(executor, tprintf(T(" Mem limit hits: %zu"), nMemHits));
notify(executor, tprintf(T(" Memory in use: %zu bytes"), nBytes));
notify(executor, tprintf(T(" Cache entries: %zu"), nCacheEntries));
notify(executor, tprintf(T(" Cache hits: %zu"), nCacheHits));
notify(executor, tprintf(T(" Cache misses: %zu"), nCacheMisses));
}
else
{
notify(executor, M_("Lua module is not loaded."));
}
break;
#ifdef REALITY_LVLS
case LIST_RLEVELS:
list_rlevels(executor);
break;
#endif
}
}
void do_assert(const dbref executor, const dbref caller, dbref enactor, const int eval, int key,
int nargs, UTF8 *arg1, UTF8 *command, const UTF8 *cargs[], const int ncargs)
{
UNUSED_PARAMETER(key);
UNUSED_PARAMETER(nargs);
if (!xlate(arg1))
{
break_called = true;
if ( nullptr != command
&& '\0' != command[0])
{
if (key & BREAK_INLINE)
{
process_command(executor, caller, enactor, eval, false, command, cargs, ncargs);
}
else
{
CLinearTimeAbsolute lta;
wait_que(executor, caller, enactor, eval, false, lta, NOTHING, 0,
command,
ncargs, cargs,
mudstate.global_regs);
}
}
}
}
void do_break(const dbref executor, const dbref caller, const dbref enactor, const int eval, int key,
const int nargs, UTF8 *arg1, UTF8 *command, const UTF8 *cargs[], const int ncargs)
{
UNUSED_PARAMETER(key);
UNUSED_PARAMETER(nargs);
if (xlate(arg1))
{
break_called = true;
if ( nullptr != command
&& '\0' != command[0])
{
if (key & BREAK_INLINE)
{
process_command(executor, caller, enactor, eval, false, command, cargs, ncargs);
}
else
{
CLinearTimeAbsolute lta;
wait_que(executor, caller, enactor, eval, false, lta, NOTHING, 0,
command,
ncargs, cargs,
mudstate.global_regs);
}
}
}
}
// do_icmd: Ignore or disable commands on a per-player or per-room basis.
// Used with express permission of RhostMUSH developers.
// Bludgeoned into MUX by Jake Nelson 7/2002.
//
void do_icmd(dbref player, dbref cause, dbref enactor, int eval, int key,
UTF8 *name, UTF8 *args[], int nargs, const UTF8 *cargs[], int ncargs)
{
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(cause);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
CMDENT *cmdp;
UTF8 *pt3, *atrpt;
int aflags;
dbref target = NOTHING, aowner;
bool set;
int loc_set = -1;
if ( key == ICMD_IROOM
|| key == ICMD_DROOM
|| key == ICMD_CROOM
|| key == ICMD_LROOM
|| key == ICMD_LALLROOM)
{
if (key != ICMD_LALLROOM)
{
target = match_thing_quiet(player, name);
}
if ( key != ICMD_LALLROOM
&& ( !Good_obj(target)
|| !( isRoom(target)
|| isThing(target))))
{
notify(player, M_("@icmd: Bad Location."));
return;
}
if (key == ICMD_CROOM)
{
atr_clr(target, A_CMDCHECK);
notify(player, M_("@icmd: Location - All cleared."));
notify(player, M_("@icmd: Done."));
return;
}
else if (key == ICMD_LROOM)
{
{
LBuf atrpt1 = LBuf_Adopt(atr_get("do_icmd.4060", target, A_CMDCHECK, &aowner, &aflags));
if (*atrpt1.get())
{
notify(player, M_("Location CmdCheck attribute is:"));
notify(player, atrpt1);
}
else
{
notify(player, M_("Location CmdCheck attribute is empty."));
}
}
notify(player, M_("@icmd: Done."));
return;
}
else if (key == ICMD_LALLROOM)
{
target = Location(player);
if ( !Good_obj(target)
|| Going(target)
|| isPlayer(target))
{
notify(player, M_("@icmd: Bad Location."));
return;
}
notify(player, M_("Scanning all locations and zones from your current location:"));
bool bFound = false;
{
LBuf atrpt2 = LBuf_Adopt(atr_get("do_icmd.4086", target, A_CMDCHECK, &aowner, &aflags));
if (*atrpt2.get())
{
notify(player, tprintf(T("%c --- At %s(#%d) :"),
(Zone(target) == target ? '*' : ' '), Name(target), target));
notify(player, atrpt2);
bFound = true;
}
}
if (Zone(target) != target)
{
dbref zone = Zone(target);
if ( Good_obj(zone)
&& ( isRoom(zone)
|| isThing(zone)))
{
LBuf atrpt3 = LBuf_Adopt(atr_get("do_icmd.4102", zone, A_CMDCHECK, &aowner, &aflags));
if (*atrpt3.get())
{
notify(player, tprintf(T("%c z-- At %s(#%d) :"),
'*', Name(zone), zone));
notify(player, atrpt3);
bFound = true;
}
}
}
if (!bFound)
{
notify(player, M_("@icmd: Location - No icmds found at current location."));
}
notify(player, M_("@icmd: Done."));
return;
}
else if (key == ICMD_IROOM)
{
loc_set = 1;
}
else if (key == ICMD_DROOM)
{
loc_set = 0;
}
}
if (loc_set == -1 )
{
target = lookup_player(player, name, false);
if (!Good_obj(target) || God(target))
{
notify(player, M_("@icmd: Bad player."));
return;
}
if ((key == ICMD_OFF) || (key == ICMD_CLEAR))
{
s_Flags(target, FLAG_WORD3, Flags3(target) & ~CMDCHECK);
if (key == ICMD_CLEAR)
{
atr_clr(target, A_CMDCHECK);
}
notify(player, M_("@icmd: All cleared."));
notify(player, M_("@icmd: Done."));
return;
}
else if (key == ICMD_ON)
{
s_Flags(target, FLAG_WORD3, Flags3(target) | CMDCHECK);
notify(player, M_("@icmd: Activated."));
notify(player, M_("@icmd: Done."));
return;
}
else if (key == ICMD_CHECK)
{
if (CmdCheck(target))
{
notify(player, M_("CmdCheck is active."));
}
else
{
notify(player, M_("CmdCheck is not active."));
}
{
LBuf atrpt4 = LBuf_Adopt(atr_get("do_icmd.4166", target, A_CMDCHECK, &aowner, &aflags));
if (*atrpt4.get())
{
notify(player, M_("CmdCheck attribute is:"));
notify(player, atrpt4);
}
else
{
notify(player, M_("CmdCheck attribute is empty."));
}
}
notify(player, M_("@icmd: Done."));
return;
}
}
else
{
key = loc_set;
}
auto message = T("");
LBuf buff1 = LBuf_Src("do_icmd");
for (int x = 0; x < nargs; x++)
{
// Lowercase the argument (Unicode-aware).
//
size_t nLower;
UTF8 *pLower = mux_strlwr(args[x], nLower);
if (nLower >= LBUF_SIZE)
{
nLower = LBUF_SIZE - 1;
}
memcpy(buff1, pLower, nLower);
buff1[nLower] = '\0';
UTF8* pt1;
UTF8* pt2;
if ( buff1[0] == '!'
&& buff1[1] != '\0')
{
pt1 = buff1 + 1;
set = false;
}
else
{
pt1 = buff1;
set = true;
}
if (*pt1)
{
bool bHome;
if (!string_compare(pt1, T("home")))
{
bHome = true;
cmdp = nullptr;
}
else
{
bHome = false;
{
size_t nPt1 = strlen(reinterpret_cast<char*>(pt1));
auto it_pt1 = mudstate.command_htab.find(std::vector<UTF8>(pt1, pt1 + nPt1));
cmdp = (it_pt1 != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_pt1->second) : nullptr;
}
}
if (cmdp || bHome)
{
bool bColon = false;
atrpt = atr_get("do_icmd.4223", target, A_CMDCHECK, &aowner, &aflags);
if (cmdp)
{
aflags = static_cast<int>(strlen(reinterpret_cast<char*>(cmdp->cmdname)));
bColon = ( aflags == 1
&& *(cmdp->cmdname) == ':');
}
else
{
aflags = 4;
}
UTF8* pt5 = atrpt;
while (pt1)
{
if (cmdp)
{
if (bColon)
{
pt1 = reinterpret_cast<UTF8*>(strstr(reinterpret_cast<char*>(pt5), "::"));
if (pt1)
{
pt1++;
}
}
else
{
pt1 = reinterpret_cast<UTF8*>(strstr(reinterpret_cast<char*>(pt5), reinterpret_cast<char*>(cmdp->cmdname)));
}
}
else
{
pt1 = reinterpret_cast<UTF8*>(strstr(reinterpret_cast<char*>(pt5), "home"));
}
if ( pt1
&& (pt1 > atrpt)
&& (*(pt1 - 1) == ':')
&& ( mux_isspace(*(pt1 + aflags))
|| !*(pt1 + aflags)))
{
break;
}
else if (pt1)
{
if (*pt1)
{
pt5 = pt1 + 1;
}
else
{
pt1 = nullptr;
break;
}
}
}
if (set)
{
if (!pt1)
{
if (cmdp)
{
pt3 = tprintf(T(" %d:%s"), key + 1, cmdp->cmdname);
}
else
{
pt3 = tprintf(T(" %d:home"), key + 1);
}
const size_t natrpt = strlen(reinterpret_cast<char*>(atrpt));
const size_t npt3 = strlen(reinterpret_cast<char*>(pt3));
if ((natrpt + npt3) < LBUF_SIZE - 1)
{
mux_strncpy(atrpt + natrpt, pt3, LBUF_SIZE-natrpt-1);
atr_add_raw(target, A_CMDCHECK, atrpt);
if ( loc_set == -1 )
{
s_Flags(target, FLAG_WORD3, Flags3(target) | CMDCHECK);
}
message = T("Set");
}
}
else
{
message = T("Command already present");
}
}
else
{
if (pt1)
{
pt2 = pt1 - 1;
while ((pt2 > atrpt) && !mux_isspace(*pt2))
{
pt2--;
}
size_t y = pt2 - atrpt + 1;
// Save the char that'll be nulled by mux_strncpy.
// (Even though it may be unneeded by this point.)
const UTF8 cSave = buff1[y];
mux_strncpy(buff1, atrpt, y);
buff1[y] = cSave;
if (y == 1)
{
*atrpt = '\0';
}
*(atrpt + y) = '\0';
pt2 = pt1 + aflags;
if (*pt2)
{
while (*pt2 && mux_isspace(*pt2))
{
pt2++;
}
if (*pt2)
{
const size_t natrpt = strlen(reinterpret_cast<char*>(atrpt));
mux_strncpy(atrpt + natrpt, pt2, LBUF_SIZE-natrpt-1);
}
}
if ((y > 1) && !*pt2)
{
pt2 = atrpt + y;
while ((pt2 > atrpt) && mux_isspace(*pt2))
{
pt2--;
}
*(pt2 + 1) = '\0';
}
if ((y == 1) && !*pt2)
{
atr_clr(target, A_CMDCHECK);
if (loc_set == -1)
{
s_Flags(target, FLAG_WORD3, Flags3(target) & ~CMDCHECK);
}
message = T("Cleared");
}
else
{
atr_add_raw(target, A_CMDCHECK, atrpt);
message = T("Cleared");
}
}
else
{
message = T("Command not present");
}
}
free_lbuf(atrpt);
}
else
{
message = T("Bad command");
}
notify(player, tprintf(T("@icmd:%s %s."), (loc_set == -1) ? T("") : T(" Location -"), message));
}
}
notify(player, M_("@icmd: Done."));
}
// do_train: show someone else in the same room what code you're entering and the result
// From RhostMUSH, changed to use notify_all_from_inside.
//
void do_train(const dbref executor, const dbref caller, const dbref enactor, const int eval, const int key, UTF8 *string, const UTF8 *cargs[], int ncargs)
{
UNUSED_PARAMETER(key);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
if (0 < mudstate.train_nest_lev)
{
notify(executor, M_("Train cannot be used to teach command, train."));
return;
}
mudstate.train_nest_lev++;
const dbref loc = Location(executor);
if (!Good_obj(loc))
{
notify(executor, M_("Bad location."));
mudstate.train_nest_lev--;
return;
}
if ( !string
|| !*string)
{
notify(executor, M_("Train requires an argument."));
mudstate.train_nest_lev--;
return;
}
notify_all_from_inside(loc, executor, tprintf(T("%s types -=> %s"),
Moniker(executor), string));
process_command(executor, caller, enactor, eval, true, string, nullptr, 0);
mudstate.train_nest_lev--;
}
void do_moniker(const dbref executor, const dbref caller, const dbref enactor, const int eval, const int key,
int nargs, UTF8 *name, UTF8 *instr, const UTF8 *cargs[], int ncargs)
{
UNUSED_PARAMETER(caller);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(key);
UNUSED_PARAMETER(nargs);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
const dbref thing = match_thing(executor, name);
if (!Good_obj(thing))
{
return;
}
if (!Controls(executor, thing))
{
notify(executor, NOPERM_MESSAGE);
return;
}
if ( instr == nullptr
|| instr[0] == '\0')
{
notify_quiet(executor, M_("Moniker cleared."));
s_Moniker(thing, nullptr);
}
else
{
s_Moniker(thing, instr);
if ( !Quiet(executor)
&& !Quiet(thing))
{
notify_quiet(executor, M_("Moniker set."));
}
}
set_modified(thing);
}
// do_hook: run softcode before or after running a hardcode command, or
// softcode access. Original idea from TinyMUSH 3, code from RhostMUSH.
// Used with express permission of RhostMUSH developers.
// Bludgeoned into MUX by Jake Nelson 7/2002.
//
static void show_hook(UTF8 *bf, UTF8 *bfptr, int key)
{
if (key & CEF_HOOK_BEFORE)
safe_str(T("before "), bf, &bfptr);
if (key & CEF_HOOK_AFTER)
safe_str(T("after "), bf, &bfptr);
if (key & CEF_HOOK_PERMIT)
safe_str(T("permit "), bf, &bfptr);
if (key & CEF_HOOK_IGNORE)
safe_str(T("ignore "), bf, &bfptr);
if (key & CEF_HOOK_IGSWITCH)
safe_str(T("igswitch "), bf, &bfptr);
if (key & CEF_HOOK_AFAIL)
safe_str(T("afail "), bf, &bfptr);
if (key & CEF_HOOK_ARGS)
safe_str(T("args "), bf, &bfptr);
*bfptr = '\0';
}
// @hook list row (#1667 Phase 4 C7): command 32 + " | " + mask freeform.
// Single-character aliases keep a 1-letter tag and a 30-col description.
//
static const size_t kHookCmdCols = 32;
static const size_t kHookTagCmdCols = 30;
static void hook_loop(const dbref executor, CMDENT *cmdp, UTF8 *s_ptr, UTF8 *s_ptrbuff)
{
show_hook(s_ptrbuff, s_ptr, HOOKMASK(cmdp->flags));
const UTF8 *pCmd = cmdp->cmdname;
UTF8 tag = '\0';
if ( pCmd[0] != '\0'
&& pCmd[1] == '\0')
{
switch (pCmd[0])
{
case '"':
tag = 'S';
pCmd = T("(\" hook on say)");
break;
case ':':
tag = 'P';
pCmd = T("(: hook on pose)");
break;
case ';':
tag = 'P';
pCmd = T("(; hook on pose)");
break;
case '\\':
tag = 'E';
pCmd = T("(\\\\ hook on @emit)");
break;
case '#':
tag = 'F';
pCmd = T("(# hook on @force)");
break;
case '&':
tag = 'V';
pCmd = T("(& hook on @set)");
break;
case '-':
tag = 'M';
pCmd = T("(- hook on @mail)");
break;
case '~':
tag = 'M';
pCmd = T("(~ hook on @mail)");
break;
}
}
UTF8 line[LBUF_SIZE];
size_t pos = 0;
if ('\0' != tag)
{
UTF8 tagbuf[4];
tagbuf[0] = tag;
tagbuf[1] = ' ';
tagbuf[2] = '\0';
pos = mux_table_append_bytes(line, sizeof(line), pos,
reinterpret_cast<const char *>(tagbuf));
pos = mux_table_append_ljust(line, sizeof(line), pos,
pCmd, kHookTagCmdCols);
}
else
{
pos = mux_table_append_ljust(line, sizeof(line), pos,
pCmd, kHookCmdCols);
}
pos = mux_table_append_bytes(line, sizeof(line), pos, " | ");
pos = mux_table_append_bytes(line, sizeof(line), pos,
reinterpret_cast<const char *>(s_ptrbuff));
notify(executor, line);
}
void do_hook(const dbref executor, const dbref caller, const dbref enactor, const int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
{
UNUSED_PARAMETER(caller);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
bool negate;
UTF8 *s_ptr, *s_ptrbuff, *p;
const UTF8 *q;
auto* cmdp = static_cast<CMDENT*>(nullptr);
if ( ( key
&& !(key & CEF_HOOK_LIST))
|| ( ( !key
|| (key & CEF_HOOK_LIST))
&& *name))
{
{
size_t nName = strlen(reinterpret_cast<char*>(name));
auto it_name = mudstate.command_htab.find(std::vector<UTF8>(name, name + nName));
cmdp = (it_name != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_name->second) : nullptr;
}
if (!cmdp)
{
notify(executor, M_("@hook: Non-existent command name given."));
return;
}
}
if ( (key & CEF_HOOK_CLEAR)
&& (key & CEF_HOOK_LIST))
{
notify(executor, M_("@hook: Incompatible switches."));
return;
}
if (key & CEF_HOOK_CLEAR)
{
negate = true;
key = ~CEF_HOOK_CLEAR & key;
key = ~SW_MULTIPLE & key;
}
else
{
negate = false;
}
if (key & (CEF_HOOK_BEFORE|CEF_HOOK_AFTER|CEF_HOOK_PERMIT|CEF_HOOK_IGNORE|CEF_HOOK_IGSWITCH|CEF_HOOK_AFAIL|CEF_HOOK_ARGS))
{
if (negate)
{
cmdp->flags = cmdp->flags & ~key;
}
else
{
cmdp->flags = cmdp->flags | key;
}
if (cmdp->flags)
{
LBuf hookbuf = LBuf_Src("@hook");
s_ptr = s_ptrbuff = hookbuf;
show_hook(s_ptrbuff, s_ptr, HOOKMASK(cmdp->flags));
notify(executor, tprintf(T("@hook: New mask for %s -> %s"), cmdp->cmdname, s_ptrbuff));
}
else
{
notify(executor, tprintf(T("@hook: New mask for %s is empty."), cmdp->cmdname));
}
}
if ( (key & CEF_HOOK_LIST)
|| !key)
{
if (cmdp)
{
if (cmdp->flags)
{
LBuf hookbuf = LBuf_Src("@hook");
s_ptr = s_ptrbuff = hookbuf;
show_hook(s_ptrbuff, s_ptr, HOOKMASK(cmdp->flags));
notify(executor, tprintf(T("@hook: Mask for hashed command %s -> %s"), cmdp->cmdname, s_ptrbuff));
}
else
{
notify(executor, tprintf(T("@hook: Mask for hashed command %s is empty."), cmdp->cmdname));
}
}
else
{
notify(executor, tprintf(T("%.32s-+-%s"),
"--------------------------------",
"--------------------------------------------"));
{
UTF8 header[LBUF_SIZE];
size_t pos = 0;
pos = mux_table_append_ljust(header, sizeof(header), pos,
M_("Built-in Command"), kHookCmdCols);
pos = mux_table_append_bytes(header, sizeof(header), pos, " | ");
pos = mux_table_append_bytes(header, sizeof(header), pos,
reinterpret_cast<const char *>(M_("Hook Mask Values")));
notify(executor, header);
}
notify(executor, tprintf(T("%.32s-+-%s"),
"--------------------------------",
"--------------------------------------------"));
bool found = false;
LBuf hookbuf3 = LBuf_Src("@hook");
s_ptr = s_ptrbuff = hookbuf3;
{
for (CMDENT_NO_ARG* cmdp2 = command_table_no_arg; cmdp2->cmdname; cmdp2++)
{
s_ptrbuff[0] = '\0';
s_ptr = s_ptrbuff;
if (0 != HOOKMASK(cmdp2->flags))
{
found = true;
hook_loop(executor, reinterpret_cast<CMDENT*>(cmdp2), s_ptr, s_ptrbuff);
}
}
}
{
for (CMDENT_ONE_ARG* cmdp2 = command_table_one_arg; cmdp2->cmdname; cmdp2++)
{
s_ptrbuff[0] = '\0';
s_ptr = s_ptrbuff;
if (0 != HOOKMASK(cmdp2->flags))
{
found = true;
hook_loop(executor, reinterpret_cast<CMDENT*>(cmdp2), s_ptr, s_ptrbuff);
}
}
}
{
for (CMDENT_TWO_ARG* cmdp2 = command_table_two_arg; cmdp2->cmdname; cmdp2++)
{
s_ptrbuff[0] = '\0';
s_ptr = s_ptrbuff;
if (0 != HOOKMASK(cmdp2->flags))
{
found = true;
hook_loop(executor, reinterpret_cast<CMDENT*>(cmdp2), s_ptr, s_ptrbuff);
}
}
}
{
for (CMDENT_TWO_ARG_ARGV* cmdp2 = command_table_two_arg_argv; cmdp2->cmdname; cmdp2++)
{
s_ptrbuff[0] = '\0';
s_ptr = s_ptrbuff;
if (0 != HOOKMASK(cmdp2->flags))
{
found = true;
hook_loop(executor, reinterpret_cast<CMDENT*>(cmdp2), s_ptr, s_ptrbuff);
}
}
}
if (!found)
{
notify(executor, tprintf(T("%26s -- No @hooks defined --"), " "));
}
found = false;
/* We need to search the attribute table as well */
notify(executor, tprintf(T("%.32s-+-%s"),
"--------------------------------",
"--------------------------------------------"));
{
UTF8 header[LBUF_SIZE];
size_t pos = 0;
pos = mux_table_append_ljust(header, sizeof(header), pos,
M_("Built-in Attribute"), kHookCmdCols);
pos = mux_table_append_bytes(header, sizeof(header), pos, " | ");
pos = mux_table_append_bytes(header, sizeof(header), pos,
reinterpret_cast<const char *>(M_("Hook Mask Values")));
notify(executor, header);
}
notify(executor, tprintf(T("%.32s-+-%s"),
"--------------------------------",
"--------------------------------------------"));
UTF8* cbuff = alloc_sbuf("cbuff_hook");
for (ATTR *ap = AttrTable; ap->name; ap++)
{
if (ap->flags & AF_NOCMD)
{
continue;
}
s_ptrbuff[0] = '\0';
s_ptr = s_ptrbuff;
p = cbuff;
safe_sb_chr('@', cbuff, &p);
// Lowercase attr name code-point-at-a-time (Unicode-aware).
//
for (q = ap->name; '\0' != *q; )
{
size_t n = utf8_FirstByte[static_cast<unsigned char>(*q)];
if (n >= UTF8_CONTINUE)
{
break;
}
bool bValid = true;
for (size_t j = 1; j < n; j++)
{
if ( '\0' == q[j]
|| UTF8_CONTINUE != utf8_FirstByte[static_cast<unsigned char>(q[j])])
{
bValid = false;
break;
}
}
if (!bValid)
{
break;
}
bool bXor;
const string_desc *qDesc = mux_tolower(q, bXor);
if (nullptr == qDesc)
{
for (size_t j = 0; j < n; j++)
{
safe_sb_chr(q[j], cbuff, &p);
}
}
else if (bXor)
{
if (qDesc->n_bytes != n)
{
for (size_t j = 0; j < n; j++)
{
safe_sb_chr(q[j], cbuff, &p);
}
}
else for (size_t j = 0; j < qDesc->n_bytes; j++)
{
safe_sb_chr(q[j] ^ qDesc->p[j], cbuff, &p);
}
}
else
{
for (size_t j = 0; j < qDesc->n_bytes; j++)
{
safe_sb_chr(qDesc->p[j], cbuff, &p);
}
}
q += n;
}
*p = '\0';
const size_t ncbuff = p - cbuff;
{
auto it_cb = mudstate.command_htab.find(std::vector<UTF8>(cbuff, cbuff + ncbuff));
cmdp = (it_cb != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_cb->second) : nullptr;
}
if ( cmdp
&& 0 != HOOKMASK(cmdp->flags))
{
found = true;
// Same C7 schema as the built-in command table.
//
hook_loop(executor, cmdp, s_ptr, s_ptrbuff);
}
}
free_sbuf(cbuff);
if (!found)
{
notify(executor, tprintf(T("%26s -- No @hooks defined --"), " "));
}
notify(executor, tprintf(T("%.32s-+-%s"),
"--------------------------------",
"--------------------------------------------"));
notify(executor, tprintf(T("The hook object is currently: #%d (%s)"),
mudconf.hook_obj,
( ( Good_obj(mudconf.hook_obj)
&& !Going(mudconf.hook_obj))
? "VALID" : "INVALID")));
}
}
}
NAMETAB allow_charset_nametab[] =
{
{T("ascii"), 5, 0, ALLOW_CHARSET_ASCII},
{T("hangul"), 6, 0, ALLOW_CHARSET_HANGUL},
{T("hiragana"), 8, 0, ALLOW_CHARSET_HIRAGANA},
{T("iso8859-1"), 9, 0, ALLOW_CHARSET_8859_1},
{T("iso8859-2"), 9, 0, ALLOW_CHARSET_8859_2},
{T("kanji"), 5, 0, ALLOW_CHARSET_KANJI},
{T("katakana"), 8, 0, ALLOW_CHARSET_KATAKANA},
{T("latin-1"), 7, 0, ALLOW_CHARSET_8859_1},
{T("latin-2"), 7, 0, ALLOW_CHARSET_8859_2},
{static_cast<UTF8*>(nullptr), 0, 0, 0}
};