// funceval.cpp -- MUX function handlers. // // $Id: funceval.cpp,v 1.94 2005/10/16 20:48:14 sdennis Exp $ // #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #include <limits.h> #include <math.h> #include "ansi.h" #include "attrs.h" #include "command.h" #include "comsys.h" #include "functions.h" #include "misc.h" #include "pcre.h" #ifdef REALITY_LVLS #include "levels.h" #endif /* REALITY_LVLS */ /* Note: Many functions in this file have been taken, whole or in part, from * PennMUSH 1.50, and TinyMUSH 2.2, for softcode compatibility. The * maintainers of MUX would like to thank those responsible for PennMUSH 1.50 * and TinyMUSH 2.2, and hope we have adequately noted in the source where * credit is due. */ extern NAMETAB indiv_attraccess_nametab[]; extern int countwords(char *str, SEP *psep); extern void arr2list(char *arr[], int alen, char *list, char **bufc, SEP *psep); extern void make_portlist(dbref, dbref, char *, char **); bool parse_and_get_attrib(dbref executor, char *fargs[], char **atext, dbref *thing, char *buff, char **bufc) { ATTR *ap; // Two possibilities for the first arg: <obj>/<attr> and <attr>. // if (!parse_attrib(executor, fargs[0], thing, &ap)) { *thing = executor; ap = atr_str(fargs[0]); } // Make sure we got a good attribute. // if (!ap) { return false; } // Use it if we can access it, otherwise return an error. // if (!See_attr(executor, *thing, ap)) { safe_noperm(buff, bufc); return false; } dbref aowner; int aflags; *atext = atr_pget(*thing, ap->number, &aowner, &aflags); if (!*atext) { return false; } else if (!**atext) { free_lbuf(*atext); return false; } return true; } #define CWHO_ON 0 #define CWHO_OFF 1 #define CWHO_ALL 2 FUNCTION(fun_cwho) { struct channel *ch = select_channel(fargs[0]); if (!ch) { safe_str("#-1 CHANNEL NOT FOUND", buff, bufc); return; } if ( !mudconf.have_comsys || ( !Comm_All(executor) && executor != ch->charge_who)) { safe_noperm(buff, bufc); return; } int match_type = CWHO_ON; if (nfargs == 2) { if (mux_stricmp(fargs[1], "all") == 0) { match_type = CWHO_ALL; } else if (mux_stricmp(fargs[1], "off") == 0) { match_type = CWHO_OFF; } else if (mux_stricmp(fargs[1], "on") == 0) { match_type = CWHO_ON; } } ITL pContext; struct comuser *user; ItemToList_Init(&pContext, buff, bufc, '#'); for (user = ch->on_users; user; user = user->on_next) { if ( ( match_type == CWHO_ALL || ( (Connected(user->who) || isThing(user->who)) && ( (match_type == CWHO_ON && user->bUserIsOn) || (match_type == CWHO_OFF && !(user->bUserIsOn))))) && !ItemToList_AddInteger(&pContext, user->who)) { break; } } ItemToList_Final(&pContext); } FUNCTION(fun_beep) { safe_chr(BEEP_CHAR, buff, bufc); } #define ANSI_F 0x00000001 #define ANSI_H 0x00000002 #define ANSI_U 0x00000004 #define ANSI_I 0x00000008 #define ANSI_FC 0x00000010 #define ANSI_BC 0x00000020 static const unsigned char ansi_have_table[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10-0x1F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x30-0x3F 0, 0, ANSI_BC, ANSI_BC, // 0x40-0x43 0, 0, 0, ANSI_BC, // 0x44-0x47 0, 0, 0, 0, // 0x48-0x4B 0, ANSI_BC, 0, 0, // 0x4B-0x4F 0, 0, ANSI_BC, 0, // 0x50-0x53 0, 0, 0, ANSI_BC, // 0x54-0x57 ANSI_BC, ANSI_BC, 0, 0, // 0x58-0x5B 0, 0, 0, 0, // 0x5B-0x5F 0, 0, ANSI_FC, ANSI_FC, // 0x60-0x63 0, 0, ANSI_F, ANSI_FC, // 0x64-0x67 ANSI_H, ANSI_I, 0, 0, // 0x68-0x6B 0, ANSI_FC, 0, 0, // 0x6C-0x6F 0, 0, ANSI_FC, 0, // 0x70-0x73 0, ANSI_U, 0, ANSI_FC, // 0x74-0x77 ANSI_FC, ANSI_FC, 0, 0, // 0x78-0x7B 0, 0, 0, 0, // 0x7B-0x7F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0-0xAF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0-0xBF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0-0xCF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0-0xDF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0-0xEF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0-0xFF }; void SimplifyColorLetters(char *pOut, char *pIn) { if ( pIn[0] == 'n' && pIn[1] == '\0') { pOut[0] = 'n'; pOut[1] = '\0'; return; } char *p; int have = 0; size_t nIn = strlen(pIn); for (p = pIn + nIn - 1; p >= pIn && *p != 'n'; p--) { int mask = ansi_have_table[(unsigned char)*p]; if ( mask && (have & mask) == 0) { *pOut++ = *p; have |= mask; } } *pOut = '\0'; } // This function was originally taken from PennMUSH 1.50 // FUNCTION(fun_ansi) { int iArg0; for (iArg0 = 0; iArg0 + 1 < nfargs; iArg0 += 2) { char pOut[8]; SimplifyColorLetters(pOut, fargs[iArg0]); char tmp[LBUF_SIZE]; char *bp = tmp; char *s = pOut; while (*s) { extern const char *ColorTable[256]; const char *pColor = ColorTable[(unsigned char)*s]; if (pColor) { safe_str(pColor, tmp, &bp); } s++; } safe_str(fargs[iArg0+1], tmp, &bp); *bp = '\0'; int nVisualWidth; size_t nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; size_t nLen = ANSI_TruncateToField(tmp, nBufferAvailable, *bufc, LBUF_SIZE, &nVisualWidth, ANSI_ENDGOAL_NORMAL); *bufc += nLen; } } FUNCTION(fun_zone) { if (!mudconf.have_zones) { safe_str("#-1 ZONES DISABLED", buff, bufc); return; } dbref it = match_thing_quiet(executor, fargs[0]); if (!Good_obj(it)) { safe_match_result(it, buff, bufc); } else if (Examinable(executor, it)) { safe_tprintf_str(buff, bufc, "#%d", Zone(it)); } else { safe_nothing(buff, bufc); } } #ifdef SIDE_EFFECT_FUNCTIONS static bool check_command(dbref player, char *name, char *buff, char **bufc) { CMDENT *cmdp = (CMDENT *)hashfindLEN(name, strlen(name), &mudstate.command_htab); if (cmdp) { // Perform checks similiar to (but not exactly like) the // ones in process_cmdent(): object type checks, permission // checks, ands global flags. // if ( Invalid_Objtype(player) || !check_access(player, cmdp->perms) || ( !Builder(player) && Protect(CA_GBL_BUILD) && !(mudconf.control_flags & CF_BUILD))) { safe_noperm(buff, bufc); return true; } } return false; } FUNCTION(fun_link) { if (check_command(executor, "@link", buff, bufc)) { return; } do_link(executor, caller, enactor, 0, 2, fargs[0], fargs[1]); } FUNCTION(fun_tel) { if (check_command(executor, "@teleport", buff, bufc)) { return; } do_teleport(executor, caller, enactor, 0, 2, fargs[0], fargs[1]); } FUNCTION(fun_pemit) { if (check_command(executor, "@pemit", buff, bufc)) { return; } do_pemit_list(executor, PEMIT_PEMIT, false, 0, fargs[0], 0, fargs[1]); } FUNCTION(fun_oemit) { if (check_command(executor, "@oemit", buff, bufc)) { return; } do_pemit_single(executor, PEMIT_OEMIT, false, 0, fargs[0], 0, fargs[1]); } FUNCTION(fun_emit) { if (check_command(executor, "@emit", buff, bufc)) { return; } do_say(executor, caller, enactor, SAY_EMIT, fargs[0]); } FUNCTION(fun_remit) { if (check_command(executor, "@pemit", buff, bufc)) { return; } do_pemit_single(executor, PEMIT_PEMIT, true, 0, fargs[0], 0, fargs[1]); } FUNCTION(fun_cemit) { if (check_command(executor, "@cemit", buff, bufc)) { return; } do_cemit(executor, caller, enactor, 0, nfargs, fargs[0], fargs[1]); } // ------------------------------------------------------------------------ // fun_create: Creates a room, thing or exit. // FUNCTION(fun_create) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT)) { return; } char *name = fargs[0]; if (!name || !*name) { safe_str("#-1 ILLEGAL NAME", buff, bufc); return; } if (nfargs >= 3 && *fargs[2]) { sep.str[0] = *fargs[2]; } else { sep.str[0] = 't'; } dbref thing; int cost; switch (sep.str[0]) { case 'r': if (check_command(executor, "@dig", buff, bufc)) { return; } thing = create_obj(executor, TYPE_ROOM, name, 0); if (thing != NOTHING) { local_data_create(thing); } break; case 'e': if (check_command(executor, "@open", buff, bufc)) { return; } thing = create_obj(executor, TYPE_EXIT, name, 0); if (thing != NOTHING) { s_Exits(thing, executor); s_Next(thing, Exits(executor)); s_Exits(executor, thing); local_data_create(thing); } break; default: if (check_command(executor, "@create", buff, bufc)) { return; } if (*fargs[1]) { cost = mux_atol(fargs[1]); if ( cost < mudconf.createmin || mudconf.createmax < cost) { safe_range(buff, bufc); return; } } else { cost = mudconf.createmin; } thing = create_obj(executor, TYPE_THING, name, cost); if (thing != NOTHING) { move_via_generic(thing, executor, NOTHING, 0); s_Home(thing, new_home(executor)); local_data_create(thing); } break; } safe_tprintf_str(buff, bufc, "#%d", thing); } FUNCTION(fun_textfile) { mux_strlwr(fargs[0]); CMDENT_ONE_ARG *cmdp = (CMDENT_ONE_ARG *)hashfindLEN(fargs[0], strlen(fargs[0]), &mudstate.command_htab); if ( !cmdp || cmdp->handler != do_help) { safe_str("#-1 NOT FOUND", buff, bufc); return; } if (check_command(executor, fargs[0], buff, bufc)) { return; } help_helper(executor, cmdp->extra, fargs[1], buff, bufc); } /* --------------------------------------------------------------------------- * fun_set: sets an attribute on an object */ static void set_attr_internal(dbref player, dbref thing, int attrnum, char *attrtext, int key, char *buff, char **bufc) { if (!Good_obj(thing)) { safe_noperm(buff, bufc); notify_quiet(player, "You shouldn't be rummaging through the garbage."); return; } dbref aowner; int aflags; ATTR *pattr = atr_num(attrnum); atr_pget_info(thing, attrnum, &aowner, &aflags); if ( pattr && bCanSetAttr(player, thing, pattr)) { bool could_hear = Hearer(thing); atr_add(thing, attrnum, attrtext, Owner(player), aflags); handle_ears(thing, could_hear, Hearer(thing)); if ( !(key & SET_QUIET) && !Quiet(player) && !Quiet(thing)) { notify_quiet(player, "Set."); } } else { safe_noperm(buff, bufc); } } FUNCTION(fun_set) { if (check_command(executor, "@set", buff, bufc)) { return; } dbref thing, aowner; int aflags; ATTR *pattr; // See if we have the <obj>/<attr> form, which is how you set // attribute flags. // if (parse_attrib(executor, fargs[0], &thing, &pattr)) { if ( pattr && See_attr(executor, thing, pattr)) { char *flagname = fargs[1]; // You must specify a flag name. // if (flagname[0] == '\0') { safe_str("#-1 UNSPECIFIED PARAMETER", buff, bufc); return; } // Check for clearing. // bool clear = false; if (flagname[0] == NOT_TOKEN) { flagname++; clear = true; } // Make sure player specified a valid attribute flag. // int flagvalue; if (!search_nametab(executor, indiv_attraccess_nametab, flagname, &flagvalue)) { safe_str("#-1 CANNOT SET", buff, bufc); return; } // Make sure the object has the attribute present. // if (!atr_get_info(thing, pattr->number, &aowner, &aflags)) { safe_str("#-1 ATTRIBUTE NOT PRESENT ON OBJECT", buff, bufc); return; } // Make sure we can write to the attribute. // if (!bCanSetAttr(executor, thing, pattr)) { safe_noperm(buff, bufc); return; } // Go do it. // if (clear) { aflags &= ~flagvalue; } else { aflags |= flagvalue; } atr_set_flags(thing, pattr->number, aflags); return; } } // Find thing. // thing = match_controlled_quiet(executor, fargs[0]); if (!Good_obj(thing)) { safe_nothing(buff, bufc); return; } // Check for attr set first. // char *p; for (p = fargs[1]; *p && *p != ':'; p++) { ; // Nothing } if (*p) { *p++ = 0; int atr = mkattr(executor, fargs[1]); if (atr <= 0) { safe_str("#-1 UNABLE TO CREATE ATTRIBUTE", buff, bufc); return; } pattr = atr_num(atr); if (!pattr) { safe_noperm(buff, bufc); return; } if (!bCanSetAttr(executor, thing, pattr)) { safe_noperm(buff, bufc); return; } char *buff2 = alloc_lbuf("fun_set"); // Check for _ // if (*p == '_') { ATTR *pattr2; dbref thing2; strcpy(buff2, p + 1); if (!( parse_attrib(executor, p + 1, &thing2, &pattr2) && pattr2)) { free_lbuf(buff2); safe_nomatch(buff, bufc); return; } p = buff2; atr_pget_str(buff2, thing2, pattr2->number, &aowner, &aflags); if (!See_attr(executor, thing2, pattr2)) { free_lbuf(buff2); safe_noperm(buff, bufc); return; } } // Go set it. // set_attr_internal(executor, thing, atr, p, 0, buff, bufc); free_lbuf(buff2); return; } // Set/clear a flag. // flag_set(thing, executor, fargs[1], 0); } #endif // Generate a substitution array. // static unsigned int GenCode(char *pCode, const char *pCodeASCII) { // Strip out the ANSI. // size_t nIn; char *pIn = strip_ansi(pCodeASCII, &nIn); // Process the printable characters. // char *pOut = pCode; while (*pIn) { unsigned char ch = *pIn; if ( ' ' <= ch && ch <= '~') { *pOut++ = ch - ' '; } pIn++; } *pOut = '\0'; return pOut - pCode; } static char *crypt_code(char *code, char *text, bool type) { if ( !text || text[0] == '\0') { return ""; } if ( !code || code[0] == '\0') { return text; } char codebuff[LBUF_SIZE]; unsigned int nCode = GenCode(codebuff, code); if (nCode == 0) { return text; } static char textbuff[LBUF_SIZE]; char *p = strip_ansi(text); char *q = codebuff; unsigned int nq = nCode; char *r = textbuff; int iMod = '~' - ' ' + 1; // Encryption loop: // while (*p) { unsigned char ch = *p; if ( ' ' <= ch && ch <= '~') { int iCode = ch - ' '; if (type) { iCode += *q; if (iMod <= iCode) { iCode -= iMod; } } else { iCode -= *q; if (iCode < 0) { iCode += iMod; } } *r++ = iCode + ' '; q++; nq--; if (0 == nq) { q = codebuff; nq = nCode; } } p++; } *r = '\0'; return textbuff; } // Code for encrypt() and decrypt() was taken from the DarkZone // server. // FUNCTION(fun_encrypt) { safe_str(crypt_code(fargs[1], fargs[0], true), buff, bufc); } FUNCTION(fun_decrypt) { safe_str(crypt_code(fargs[1], fargs[0], false), buff, bufc); } // Borrowed from DarkZone // void scan_zone ( dbref executor, char *szZone, int ObjectType, char *buff, char **bufc ) { if (!mudconf.have_zones) { safe_str("#-1 ZONES DISABLED", buff, bufc); return; } dbref it = match_thing_quiet(executor, szZone); if (!Good_obj(it)) { safe_match_result(it, buff, bufc); return; } else if (!( WizRoy(executor) || Controls(executor, it))) { safe_noperm(buff, bufc); return; } dbref i; ITL pContext; ItemToList_Init(&pContext, buff, bufc, '#'); DO_WHOLE_DB(i) { if ( Typeof(i) == ObjectType && Zone(i) == it && !ItemToList_AddInteger(&pContext, i)) { break; } } ItemToList_Final(&pContext); } FUNCTION(fun_zwho) { scan_zone(executor, fargs[0], TYPE_PLAYER, buff, bufc); } FUNCTION(fun_inzone) { scan_zone(executor, fargs[0], TYPE_ROOM, buff, bufc); } // Borrowed from DarkZone // FUNCTION(fun_children) { dbref it = match_thing_quiet(executor, fargs[0]); if (!Good_obj(it)) { safe_match_result(it, buff, bufc); return; } else if (!( WizRoy(executor) || Controls(executor, it))) { safe_noperm(buff, bufc); return; } dbref i; ITL pContext; ItemToList_Init(&pContext, buff, bufc, '#'); DO_WHOLE_DB(i) { if ( Parent(i) == it && !ItemToList_AddInteger(&pContext, i)) { break; } } ItemToList_Final(&pContext); } FUNCTION(fun_objeval) { if (!*fargs[0]) { return; } char *name = alloc_lbuf("fun_objeval"); char *bp = name; char *str = fargs[0]; mux_exec(name, &bp, executor, caller, enactor, EV_FCHECK | EV_STRIP_CURLY | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; dbref obj = match_thing_quiet(executor, name); free_lbuf(name); if (!Good_obj(obj)) { safe_match_result(obj, buff, bufc); return; } if (!Controls(executor, obj)) { // The right circumstances were not met, so we are evaluating // as the executor who gave the command instead of the // requested object. // obj = executor; } mudstate.nObjEvalNest++; str = fargs[1]; mux_exec(buff, bufc, obj, executor, enactor, EV_FCHECK | EV_STRIP_CURLY | EV_EVAL, &str, cargs, ncargs); mudstate.nObjEvalNest--; } FUNCTION(fun_localize) { char **preserve = NULL; int *preserve_len = NULL; preserve = PushPointers(MAX_GLOBAL_REGS); preserve_len = PushIntegers(MAX_GLOBAL_REGS); save_global_regs("fun_localize", preserve, preserve_len); char *str = fargs[0]; mux_exec(buff, bufc, executor, caller, enactor, EV_FCHECK | EV_STRIP_CURLY | EV_EVAL, &str, cargs, ncargs); restore_global_regs("fun_localize", preserve, preserve_len); PopIntegers(preserve_len, MAX_GLOBAL_REGS); PopPointers(preserve, MAX_GLOBAL_REGS); } FUNCTION(fun_null) { return; } FUNCTION(fun_squish) { if (nfargs == 0) { return; } SEP sep; if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT)) { return; } char *p; char *q = fargs[0]; while ((p = strchr(q, sep.str[0])) != NULL) { p = p + 1; size_t nLen = p - q; safe_copy_buf(q, nLen, buff, bufc); q = p; while (*q == sep.str[0]) { q++; } } safe_str(q, buff, bufc); } FUNCTION(fun_stripansi) { safe_str(strip_ansi(fargs[0]), buff, bufc); } // Borrowed from PennMUSH 1.50 // FUNCTION(fun_zfun) { if (!mudconf.have_zones) { safe_str("#-1 ZONES DISABLED", buff, bufc); return; } dbref zone = Zone(executor); if (!Good_obj(zone)) { safe_str("#-1 INVALID ZONE", buff, bufc); return; } // Find the user function attribute. // int attrib = get_atr(fargs[0]); if (!attrib) { safe_str("#-1 NO SUCH USER FUNCTION", buff, bufc); return; } dbref aowner; int aflags; ATTR *pattr = atr_num(attrib); char *tbuf1 = atr_pget(zone, attrib, &aowner, &aflags); if ( !pattr || !See_attr(executor, zone, pattr)) { safe_noperm(buff, bufc); free_lbuf(tbuf1); return; } char *str = tbuf1; mux_exec(buff, bufc, zone, executor, enactor, EV_EVAL | EV_STRIP_CURLY | EV_FCHECK, &str, &(fargs[1]), nfargs - 1); free_lbuf(tbuf1); } FUNCTION(fun_columns) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_EVAL|DELIM_STRING)) { return; } int nWidth = mux_atol(fargs[1]); int nIndent = 0; if (nfargs == 4) { nIndent = mux_atol(fargs[3]); if (nIndent < 0 || 77 < nIndent) { nIndent = 1; } } int nRight = nIndent + nWidth; if ( nWidth < 1 || 78 < nWidth || nRight < 1 || 78 < nRight) { safe_range(buff, bufc); return; } char *curr = alloc_lbuf("fun_columns"); char *bp = curr; char *str = fargs[0]; mux_exec(curr, &bp, executor, caller, enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; int ncp; char *cp = trim_space_sep_LEN(curr, bp-curr, &sep, &ncp); if (!*cp) { free_lbuf(curr); return; } int nColumns = (78-nIndent)/nWidth; int iColumn = 0; int nBufferAvailable = LBUF_SIZE - (*bufc-buff) - 1; bool bNeedCRLF = false; while ( cp && 0 < nBufferAvailable) { if (iColumn == 0) { nBufferAvailable -= safe_fill(buff, bufc, ' ', nIndent); } char *objstring = split_token(&cp, &sep); int nVisualWidth; int nLen = ANSI_TruncateToField(objstring, nBufferAvailable, *bufc, nWidth, &nVisualWidth, ANSI_ENDGOAL_NORMAL); *bufc += nLen; nBufferAvailable -= nLen; if (nColumns-1 <= iColumn) { iColumn = 0; nBufferAvailable -= safe_copy_buf("\r\n", 2, buff, bufc); bNeedCRLF = false; } else { iColumn++; nBufferAvailable -= safe_fill(buff, bufc, ' ', nWidth - nVisualWidth); bNeedCRLF = true; } } if (bNeedCRLF) { safe_copy_buf("\r\n", 2, buff, bufc); } free_lbuf(curr); } // table(<list>,<field width>,<line length>,<delimiter>,<output separator>, <padding>) // // Ported from PennMUSH 1.7.3 by Morgan. // // TODO: Support ANSI in output separator and padding. // FUNCTION(fun_table) { // Check argument numbers, assign values and defaults if necessary. // char *pPaddingStart = NULL; char *pPaddingEnd = NULL; if (nfargs == 6 && *fargs[5]) { pPaddingStart = strip_ansi(fargs[5]); pPaddingEnd = strchr(pPaddingStart, '\0'); } // Get single-character separator. // char cSeparator = ' '; if (nfargs >= 5 && fargs[4][0] != '\0') { if (fargs[4][1] == '\0') { cSeparator = *fargs[4]; } else { safe_str("#-1 SEPARATOR MUST BE ONE CHARACTER", buff, bufc); return; } } // Get single-character delimiter. // char cDelimiter = ' '; if (nfargs >= 4 && fargs[3][0] != '\0') { if (fargs[3][1] == '\0') { cDelimiter = *fargs[3]; } else { safe_str("#-1 DELIMITER MUST BE ONE CHARACTER", buff, bufc); return; } } // Get line length. // int nLineLength = 78; if (nfargs >= 3) { nLineLength = mux_atol(fargs[2]); } // Get field width. // int nFieldWidth = 10; if (nfargs >= 2) { nFieldWidth = mux_atol(fargs[1]); } else { nFieldWidth = 10; } // Validate nFieldWidth and nLineLength. // if ( nLineLength < 1 || LBUF_SIZE <= nLineLength || nFieldWidth < 1 || nLineLength < nFieldWidth) { safe_range(buff, bufc); return; } int nNumCols = nLineLength / nFieldWidth; SEP sep; sep.n = 1; sep.str[0] = cDelimiter; char *pNext = trim_space_sep(fargs[0], &sep); if (!*pNext) { return; } char *pCurrent = split_token(&pNext, &sep); if (!pCurrent) { return; } int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; int nCurrentCol = nNumCols - 1; for (;;) { int nVisibleLength, nPaddingLength; int nStringLength = ANSI_TruncateToField( pCurrent, nBufferAvailable, *bufc, nFieldWidth, &nVisibleLength, ANSI_ENDGOAL_NORMAL); *bufc += nStringLength; nBufferAvailable -= nStringLength; nPaddingLength = nFieldWidth - nVisibleLength; if (nPaddingLength > nBufferAvailable) { nPaddingLength = nBufferAvailable; } if (nPaddingLength) { nBufferAvailable -= nPaddingLength; if (pPaddingStart) { for ( char *pPaddingCurrent = pPaddingStart; nPaddingLength > 0; nPaddingLength--) { **bufc = *pPaddingCurrent; (*bufc)++; pPaddingCurrent++; if (pPaddingCurrent == pPaddingEnd) { pPaddingCurrent = pPaddingStart; } } } else { memset(*bufc, ' ', nPaddingLength); *bufc += nPaddingLength; } } pCurrent = split_token(&pNext, &sep); if (!pCurrent) { break; } if (!nCurrentCol) { nCurrentCol = nNumCols - 1; if (nBufferAvailable >= 2) { char *p = *bufc; p[0] = '\r'; p[1] = '\n'; nBufferAvailable -= 2; *bufc += 2; } else { // nBufferAvailable has less than 2 characters left, if there's // no room left just break out. // if (!nBufferAvailable) { break; } } } else { nCurrentCol--; if (!nBufferAvailable) { break; } **bufc = cSeparator; (*bufc)++; nBufferAvailable--; } } } // Code for objmem and playmem borrowed from PennMUSH 1.50 // static int mem_usage(dbref thing) { int ca; char *as; int k = sizeof(struct object) + strlen(Name(thing)) + 1; for (ca = atr_head(thing, &as); ca; ca = atr_next(&as)) { size_t nLen; const char *str = atr_get_raw_LEN(thing, ca, &nLen); k += nLen+1; ATTR *pattr = atr_num(ca); if (pattr) { str = pattr->name; if ( str && *str) { k += strlen(str)+1; } } } return k; } FUNCTION(fun_objmem) { dbref thing = match_thing_quiet(executor, fargs[0]); if (!Good_obj(thing)) { safe_match_result(thing, buff, bufc); } else if (Examinable(executor, thing)) { safe_ltoa(mem_usage(thing), buff, bufc); } else { safe_noperm(buff, bufc); } } FUNCTION(fun_playmem) { dbref thing; if (nfargs == 1) { thing = match_thing_quiet(executor, fargs[0]); if (!Good_obj(thing)) { safe_match_result(thing, buff, bufc); return; } else if (!Examinable(executor, thing)) { safe_noperm(buff, bufc); return; } } else { thing = executor; } int tot = 0; dbref j; DO_WHOLE_DB(j) { if (Owner(j) == thing) { tot += mem_usage(j); } } safe_ltoa(tot, buff, bufc); } // Code for andflags() and orflags() borrowed from PennMUSH 1.50 // false for orflags, true for andflags // static bool handle_flaglists(dbref player, char *name, char *fstr, bool type) { dbref it = match_thing_quiet(player, name); if (!Good_obj(it)) { return false; } char *s; char flagletter[2]; FLAGSET fset; FLAG p_type; bool negate = false; bool temp = false; bool ret = type; for (s = fstr; *s; s++) { // Check for a negation sign. If we find it, we note it and // increment the pointer to the next character. // if (*s == '!') { negate = true; s++; } else { negate = false; } if (!*s) { return false; } flagletter[0] = *s; flagletter[1] = '\0'; if (!convert_flags(player, flagletter, &fset, &p_type)) { // Either we got a '!' that wasn't followed by a letter, or we // couldn't find that flag. For AND, since we've failed a check, // we can return false. Otherwise we just go on. // if (type) { return false; } else { continue; } } else { // Does the object have this flag? // if ( (Flags(it) & fset.word[FLAG_WORD1]) || (Flags2(it) & fset.word[FLAG_WORD2]) || (Flags3(it) & fset.word[FLAG_WORD3]) || Typeof(it) == p_type) { if ( isPlayer(it) && fset.word[FLAG_WORD2] == CONNECTED && Hidden(it) && !See_Hidden(player)) { temp = false; } else { temp = true; } } else { temp = false; } if ( type && ( (negate && temp) || (!negate && !temp))) { // Too bad there's no NXOR function. At this point we've // either got a flag and we don't want it, or we don't have a // flag and we want it. Since it's AND, we return false. // return false; } else if ( !type && ( (!negate && temp) || (negate && !temp))) { // We've found something we want, in an OR. We OR a true with // the current value. // ret |= true; } // Otherwise, we don't need to do anything. // } } return ret; } FUNCTION(fun_orflags) { safe_bool(handle_flaglists(executor, fargs[0], fargs[1], false), buff, bufc); } FUNCTION(fun_andflags) { safe_bool(handle_flaglists(executor, fargs[0], fargs[1], true), buff, bufc); } FUNCTION(fun_strtrunc) { int maxVisualWidth = mux_atol(fargs[1]); if (maxVisualWidth < 0) { safe_range(buff, bufc); return; } if (maxVisualWidth == 0) { return; } int nVisualWidth; char buf[LBUF_SIZE+1]; ANSI_TruncateToField(fargs[0], LBUF_SIZE, buf, maxVisualWidth, &nVisualWidth, ANSI_ENDGOAL_NORMAL); safe_str(buf, buff, bufc); } FUNCTION(fun_ifelse) { // This function assumes that its arguments have not been evaluated. // char *lbuff = alloc_lbuf("fun_ifelse"); char *bp = lbuff; char *str = fargs[0]; mux_exec(lbuff, &bp, executor, caller, enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; if (!xlate(lbuff)) { if (nfargs == 3) { str = fargs[2]; mux_exec(buff, bufc, executor, caller, enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } } else { str = fargs[1]; mux_exec(buff, bufc, executor, caller, enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } free_lbuf(lbuff); } // Mail functions borrowed from DarkZone. // // This function can take one of three formats: // // 1. mail(num) --> returns message <num> for privs. // 2. mail(executor) --> returns number of messages for <executor>. // 3. mail(executor, num) --> returns message <num> for <executor>. // 4. mail() --> returns number of messages for executor. // FUNCTION(fun_mail) { if (!mudconf.have_mailer) { safe_str("#-1 MAILER DISABLED.", buff, bufc); return; } dbref playerask; int num, rc, uc, cc; // Make sure we have the right number of arguments. // if (nfargs == 0 || !fargs[0] || !fargs[0][0]) { count_mail(executor, 0, &rc, &uc, &cc); safe_ltoa(rc + uc, buff, bufc); return; } else if (nfargs == 1) { if (!is_integer(fargs[0], NULL)) { // Handle the case of wanting to count the number of // messages. // playerask = lookup_player(executor, fargs[0], true); if (playerask == NOTHING) { playerask = match_thing_quiet(executor, fargs[0]); if (!isPlayer(playerask)) { safe_str("#-1 NO SUCH PLAYER", buff, bufc); return; } } if (playerask == executor || Wizard(executor)) { count_mail(playerask, 0, &rc, &uc, &cc); safe_tprintf_str(buff, bufc, "%d %d %d", rc, uc, cc); } else { safe_noperm(buff, bufc); } return; } else { playerask = executor; num = mux_atol(fargs[0]); } } else // if (nfargs == 2) { playerask = lookup_player(executor, fargs[0], true); if (playerask == NOTHING) { safe_str("#-1 NO SUCH PLAYER", buff, bufc); return; } else if ( (playerask == executor && !mudstate.nObjEvalNest) || God(executor)) { num = mux_atol(fargs[1]); } else { safe_noperm(buff, bufc); return; } } if ( num < 1 || !isPlayer(playerask)) { safe_str("#-1 NO SUCH MESSAGE", buff, bufc); return; } const char *p = mail_fetch_message(playerask, num); if (p) { safe_str(p, buff, bufc); return; } // Ran off the end of the list without finding anything. // safe_str("#-1 NO SUCH MESSAGE", buff, bufc); } // This function can take these formats: // // 1) mailfrom(<num>) // 2) mailfrom(<executor>,<num>) // // It returns the dbref of the executor the mail is from. // FUNCTION(fun_mailfrom) { if (!mudconf.have_mailer) { safe_str("#-1 MAILER DISABLED.", buff, bufc); return; } // Make sure we have the right number of arguments. // int num; dbref playerask; if (nfargs == 1) { playerask = executor; num = mux_atol(fargs[0]); } else // if (nfargs == 2) { playerask = lookup_player(executor, fargs[0], true); if (playerask == NOTHING) { safe_str("#-1 NO SUCH PLAYER", buff, bufc); return; } if ( playerask == executor || Wizard(executor)) { num = mux_atol(fargs[1]); } else { safe_noperm(buff, bufc); return; } } if ( num < 1 || !isPlayer(playerask)) { safe_str("#-1 NO SUCH MESSAGE", buff, bufc); return; } int from = mail_fetch_from(playerask, num); if (NOTHING != from) { safe_tprintf_str(buff, bufc, "#%d", from); return; } // Ran off the end of the list without finding anything. // safe_str("#-1 NO SUCH MESSAGE", buff, bufc); } // --------------------------------------------------------------------------- // fun_hasattr: does object X have attribute Y. // Hasattr (and hasattrp, which is derived from hasattr) borrowed from // TinyMUSH 2.2. void hasattr_handler(char *buff, char **bufc, dbref executor, char *fargs[], bool bCheckParent) { dbref thing = match_thing_quiet(executor, fargs[0]); if (!Good_obj(thing)) { safe_match_result(thing, buff, bufc); return; } ATTR *pattr = atr_str(fargs[1]); bool result = false; if (pattr) { if (!bCanReadAttr(executor, thing, pattr, bCheckParent)) { safe_noperm(buff, bufc); return; } else { if (bCheckParent) { dbref aowner; int aflags; char *tbuf = atr_pget(thing, pattr->number, &aowner, &aflags); result = (tbuf[0] != '\0'); free_lbuf(tbuf); } else { const char *tbuf = atr_get_raw(thing, pattr->number); result = (tbuf != NULL); } } } safe_bool(result, buff, bufc); } FUNCTION(fun_hasattr) { hasattr_handler(buff, bufc, executor, fargs, false); } FUNCTION(fun_hasattrp) { hasattr_handler(buff, bufc, executor, fargs, true); } /* --------------------------------------------------------------------------- * fun_default, fun_edefault, and fun_udefault: * These check for the presence of an attribute. If it exists, then it * is gotten, via the equivalent of get(), get_eval(), or u(), respectively. * Otherwise, the default message is used. * In the case of udefault(), the remaining arguments to the function * are used as arguments to the u(). */ // default(), edefault(), and udefault() borrowed from TinyMUSH 2.2 // #define DEFAULT_DEFAULT 1 #define DEFAULT_EDEFAULT 2 #define DEFAULT_UDEFAULT 4 void default_handler(char *buff, char **bufc, dbref executor, dbref caller, dbref enactor, char *fargs[], int nfargs, char *cargs[], int ncargs, int key) { // Evaluating the first argument. // char *objattr = alloc_lbuf("default_handler"); char *bp = objattr; char *str = fargs[0]; mux_exec(objattr, &bp, executor, caller, enactor, EV_EVAL | EV_STRIP_CURLY | EV_FCHECK, &str, cargs, ncargs); *bp = '\0'; // Parse the first argument as either <dbref>/<attrname> or <attrname>. // dbref thing; ATTR *pattr; if (!parse_attrib(executor, objattr, &thing, &pattr)) { thing = executor; pattr = atr_str(objattr); } free_lbuf(objattr); if ( pattr && See_attr(executor, thing, pattr)) { dbref aowner; int aflags; char *atr_gotten = atr_pget(thing, pattr->number, &aowner, &aflags); if (atr_gotten[0] != '\0') { switch (key) { case DEFAULT_DEFAULT: safe_str(atr_gotten, buff, bufc); break; case DEFAULT_EDEFAULT: str = atr_gotten; mux_exec(buff, bufc, thing, executor, executor, EV_FIGNORE | EV_EVAL, &str, (char **)NULL, 0); break; case DEFAULT_UDEFAULT: { char *xargs[NUM_ENV_VARS]; int nxargs = nfargs-2; int i; for (i = 0; i < nxargs; i++) { xargs[i] = alloc_lbuf("fun_udefault_args"); char *bp2 = xargs[i]; str = fargs[i+2]; mux_exec(xargs[i], &bp2, thing, caller, enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } str = atr_gotten; mux_exec(buff, bufc, thing, caller, enactor, EV_FCHECK | EV_EVAL, &str, xargs, nxargs); for (i = 0; i < nxargs; i++) { free_lbuf(xargs[i]); } } break; } free_lbuf(atr_gotten); return; } free_lbuf(atr_gotten); } // If we've hit this point, we've not gotten anything useful, so // we go and evaluate the default. // str = fargs[1]; mux_exec(buff, bufc, executor, caller, enactor, EV_EVAL | EV_STRIP_CURLY | EV_FCHECK, &str, cargs, ncargs); } FUNCTION(fun_default) { default_handler(buff, bufc, executor, caller, enactor, fargs, nfargs, cargs, ncargs, DEFAULT_DEFAULT); } FUNCTION(fun_edefault) { default_handler(buff, bufc, executor, caller, enactor, fargs, nfargs, cargs, ncargs, DEFAULT_EDEFAULT); } FUNCTION(fun_udefault) { default_handler(buff, bufc, executor, caller, enactor, fargs, nfargs, cargs, ncargs, DEFAULT_UDEFAULT); } /* --------------------------------------------------------------------------- * fun_findable: can X locate Y * Borrowed from PennMUSH 1.50 */ FUNCTION(fun_findable) { dbref obj = match_thing_quiet(executor, fargs[0]); if (!Good_obj(obj)) { safe_match_result(obj, buff, bufc); safe_str(" (ARG1)", buff, bufc); return; } dbref victim = match_thing_quiet(executor, fargs[1]); if (!Good_obj(victim)) { safe_match_result(victim, buff, bufc); safe_str(" (ARG2)", buff, bufc); return; } #ifndef WOD_REALMS #ifndef REALITY_LVLS safe_bool(locatable(obj, victim, obj), buff, bufc); #else if (IsReal(obj, victim)) { safe_bool(locatable(obj, victim, obj), buff, bufc); } else safe_chr('0', buff, bufc); #endif #else #ifndef REALITY_LVLS if (REALM_DO_HIDDEN_FROM_YOU != DoThingToThingVisibility(obj, victim, ACTION_IS_STATIONARY)) { safe_bool(locatable(obj, victim, obj), buff, bufc); } else { safe_chr('0', buff, bufc); } #else if (REALM_DO_HIDDEN_FROM_YOU != DoThingToThingVisibility(obj, victim, ACTION_IS_STATIONARY)) { safe_bool(locatable(obj, victim, obj), buff, bufc); } else if (IsReal(obj, victim)) { safe_bool(locatable(obj, victim, obj), buff, bufc); } else safe_chr('0', buff, bufc); #endif #endif } /* --------------------------------------------------------------------------- * isword: is every character in the argument a letter? * Borrowed from PennMUSH 1.50 */ FUNCTION(fun_isword) { char *p; bool result = true; for (p = fargs[0]; *p; p++) { if (!mux_isalpha(*p)) { result = false; break; } } safe_bool(result, buff, bufc); } /* --------------------------------------------------------------------------- * fun_visible. Borrowed from PennMUSH 1.50 */ FUNCTION(fun_visible) { dbref it = match_thing_quiet(executor, fargs[0]); if (!Good_obj(it)) { safe_match_result(it, buff, bufc); safe_str(" (ARG1)", buff, bufc); return; } else if (!Controls(executor, it)) { safe_noperm(buff, bufc); return; } bool result = false; dbref thing; ATTR *pattr; if (!parse_attrib(executor, fargs[1], &thing, &pattr)) { thing = match_thing_quiet(executor, fargs[1]); if (!Good_obj(thing)) { safe_match_result(thing, buff, bufc); safe_str(" (ARG2)", buff, bufc); return; } } if (Good_obj(thing)) { if (pattr) { result = (See_attr(it, thing, pattr)); } else { result = (Examinable(it, thing)); } } safe_bool(result, buff, bufc); } /* --------------------------------------------------------------------------- * fun_elements: given a list of numbers, get corresponding elements from * the list. elements(ack bar eep foof yay,2 4) ==> bar foof * The function takes a separator, but the separator only applies to the * first list. * Borrowed from PennMUSH 1.50 */ FUNCTION(fun_elements) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING)) { return; } SEP osep = sep; if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT)) { return; } int nwords, cur; char *ptrs[LBUF_SIZE / 2]; char *wordlist, *s, *r; bool bFirst = true; // Turn the first list into an array. // wordlist = alloc_lbuf("fun_elements.wordlist"); strcpy(wordlist, fargs[0]); nwords = list2arr(ptrs, LBUF_SIZE / 2, wordlist, &sep); s = trim_space_sep(fargs[1], &sepSpace); // Go through the second list, grabbing the numbers and finding the // corresponding elements. // do { r = split_token(&s, &sepSpace); cur = mux_atol(r) - 1; if ( cur >= 0 && cur < nwords && ptrs[cur]) { if (!bFirst) { print_sep(&osep, buff, bufc); } else { bFirst = false; } safe_str(ptrs[cur], buff, bufc); } } while (s); free_lbuf(wordlist); } /* --------------------------------------------------------------------------- * fun_grab: a combination of extract() and match(), sortof. We grab the * single element that we match. * * grab(Test:1 Ack:2 Foof:3,*:2) => Ack:2 * grab(Test-1+Ack-2+Foof-3,*o*,+) => Foof-3 * Borrowed from PennMUSH 1.50 */ FUNCTION(fun_grab) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING)) { return; } // Walk the wordstring, until we find the word we want. // char *s = trim_space_sep(fargs[0], &sep); do { char *r = split_token(&s, &sep); mudstate.wild_invk_ctr = 0; if (quick_wild(fargs[1], r)) { safe_str(r, buff, bufc); return; } } while (s); } FUNCTION(fun_graball) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING)) { return; } SEP osep = sep; if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT)) { return; } bool bFirst = true; char *s = trim_space_sep(fargs[0], &sep); do { char *r = split_token(&s, &sep); mudstate.wild_invk_ctr = 0; if (quick_wild(fargs[1], r)) { if (!bFirst) { print_sep(&osep, buff, bufc); } else { bFirst = false; } safe_str(r, buff, bufc); } } while (s); } /* --------------------------------------------------------------------------- * fun_scramble: randomizes the letters in a string. * Borrowed from PennMUSH 1.50 */ FUNCTION(fun_scramble) { size_t n; char *old = strip_ansi(fargs[0], &n); if (2 <= n) { unsigned int i; for (i = 0; i < n-1; i++) { int j = RandomINT32(i, n-1); char c = old[i]; old[i] = old[j]; old[j] = c; } } safe_str(old, buff, bufc); } /* --------------------------------------------------------------------------- * fun_shuffle: randomize order of words in a list. * Borrowed from PennMUSH 1.50 */ FUNCTION(fun_shuffle) { SEP sep; if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING)) { return; } SEP osep = sep; if (!OPTIONAL_DELIM(3, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT)) { return; } char *words[LBUF_SIZE]; int n, i, j; n = list2arr(words, LBUF_SIZE, fargs[0], &sep); for (i = 0; i < n-1; i++) { j = RandomINT32(i, n-1); // Swap words[i] with words[j] // char *temp = words[i]; words[i] = words[j]; words[j] = temp; } arr2list(words, n, buff, bufc, &osep); } // pickrand -- choose a random item from a list. // FUNCTION(fun_pickrand) { if ( nfargs == 0 || fargs[0][0] == '\0') { return; } SEP sep; if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING)) { return; } char *s = trim_space_sep(fargs[0], &sep); char *t = s; if (s[0] == '\0') { return; } INT32 n; for (n = 0; t; t = next_token(t, &sep), n++) { ; // Nothing } if (n >= 1) { INT32 w = RandomINT32(0, n-1); for (n = 0; n < w; n++) { s = next_token(s, &sep); } t = split_token(&s, &sep); safe_str(t, buff, bufc); } } // sortby() code borrowed from TinyMUSH 2.2 // static char ucomp_buff[LBUF_SIZE]; static dbref ucomp_executor; static dbref ucomp_caller; static dbref ucomp_enactor; static int u_comp(const void *s1, const void *s2) { // Note that this function is for use in conjunction with our own // sane_qsort routine, NOT with the standard library qsort! // char *result, *tbuf, *elems[2], *bp, *str; int n; if ( mudstate.func_invk_ctr > mudconf.func_invk_lim || mudstate.func_nest_lev > mudconf.func_nest_lim || MuxAlarm.bAlarmed) { return 0; } tbuf = alloc_lbuf("u_comp"); elems[0] = (char *)s1; elems[1] = (char *)s2; strcpy(tbuf, ucomp_buff); result = bp = alloc_lbuf("u_comp"); str = tbuf; mux_exec(result, &bp, ucomp_executor, ucomp_caller, ucomp_enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &(elems[0]), 2); *bp = '\0'; if (!result) { n = 0; } else { n = mux_atol(result); free_lbuf(result); } free_lbuf(tbuf); return n; } typedef int PV(const void *, const void *); static void sane_qsort(void *array[], int left, int right, PV compare) { // Andrew Molitor's qsort, which doesn't require transitivity between // comparisons (essential for preventing crashes due to boneheads // who write comparison functions where a > b doesn't mean b < a). // int i, last; void *tmp; loop: if (left >= right) { return; } // Pick something at random at swap it into the leftmost slot // This is the pivot, we'll put it back in the right spot later. // i = RandomINT32(0, right - left); tmp = array[left + i]; array[left + i] = array[left]; array[left] = tmp; last = left; for (i = left + 1; i <= right; i++) { // Walk the array, looking for stuff that's less than our // pivot. If it is, swap it with the next thing along // if ((*compare) (array[i], array[left]) < 0) { last++; if (last == i) { continue; } tmp = array[last]; array[last] = array[i]; array[i] = tmp; } } // Now we put the pivot back, it's now in the right spot, we never // need to look at it again, trust me. // tmp = array[last]; array[last] = array[left]; array[left] = tmp; // At this point everything underneath the 'last' index is < the // entry at 'last' and everything above it is not < it. // if ((last - left) < (right - last)) { sane_qsort(array, left, last - 1, compare); left = last + 1; goto loop; } else { sane_qsort(array, last + 1, right, compare); right = last - 1; goto loop; } } FUNCTION(fun_sortby) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING)) { return; } SEP osep = sep; if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT)) { return; } char *atext; dbref thing; if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc)) { return; } strcpy(ucomp_buff, atext); ucomp_executor = thing; ucomp_caller = executor; ucomp_enactor = enactor; char *list = alloc_lbuf("fun_sortby"); strcpy(list, fargs[1]); char *ptrs[LBUF_SIZE / 2]; int nptrs = list2arr(ptrs, LBUF_SIZE / 2, list, &sep); if (nptrs > 1) { sane_qsort((void **)ptrs, 0, nptrs - 1, u_comp); } arr2list(ptrs, nptrs, buff, bufc, &osep); free_lbuf(list); free_lbuf(atext); } // fun_last: Returns last word in a string. Borrowed from TinyMUSH 2.2. // FUNCTION(fun_last) { // If we are passed an empty arglist return a null string. // if (nfargs == 0) { return; } SEP sep; if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING)) { return; } char *str; char *lstr = trim_space_sep(fargs[0], &sep); while (NULL != (str = next_token(lstr, &sep))) { lstr = str; } safe_str(lstr, buff, bufc); } // Borrowed from TinyMUSH 2.2 // FUNCTION(fun_matchall) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING)) { return; } int wcount; char *r, *s, *old, tbuf[8]; old = *bufc; // Check each word individually, returning the word number of all that // match. If none match, return 0. // wcount = 1; s = trim_space_sep(fargs[0], &sep); do { r = split_token(&s, &sep); mudstate.wild_invk_ctr = 0; if (quick_wild(fargs[1], r)) { mux_ltoa(wcount, tbuf); if (old != *bufc) { safe_chr(' ', buff, bufc); } safe_str(tbuf, buff, bufc); } wcount++; } while (s); if (*bufc == old) { safe_chr('0', buff, bufc); } } // --------------------------------------------------------------------------- // fun_ports: Returns a list of ports for a user. // Borrowed from TinyMUSH 2.2 // FUNCTION(fun_ports) { dbref target = lookup_player(executor, fargs[0], true); if (Good_obj(target)) { if (target == executor || Wizard(executor)) { if (Connected(target)) { make_portlist(executor, target, buff, bufc); } } else { safe_noperm(buff, bufc); } } else { safe_nomatch(buff, bufc); } } /* --------------------------------------------------------------------------- * fun_mix: Like map, but operates on up to ten lists simultaneously, passing * the elements as %0 - %10. * Borrowed from PennMUSH 1.50, upgraded by RhostMUSH. */ FUNCTION(fun_mix) { // Check to see if we have an appropriate number of arguments. // If there are more than three arguments, the last argument is // ALWAYS assumed to be a delimiter. // SEP sep; int lastn; if (nfargs < 4) { sep.n = 1; sep.str[0] = ' '; sep.str[1] = '\0'; lastn = nfargs - 1; } else if (!OPTIONAL_DELIM(nfargs, sep, DELIM_DFLT|DELIM_STRING)) { return; } else { lastn = nfargs - 2; } // Get the attribute, check the permissions. // dbref thing; char *atext; if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc)) { return; } char *cp[10]; int i; for (i = 0; i < lastn; i++) { cp[i] = NULL; } // Process the lists, one element at a time. // for (i = 0; i < lastn; i++) { cp[i] = trim_space_sep(fargs[i+1], &sep); } int twords; int nwords = countwords(cp[0], &sep); for (i = 1; i < lastn; i++) { twords = countwords(cp[i], &sep); if (twords > nwords) { nwords = twords; } } char *atextbuf = alloc_lbuf("fun_mix"); char *str, *os[10]; bool bFirst = true; for (int wc = 0; wc < nwords && !MuxAlarm.bAlarmed; wc++) { if (!bFirst) { print_sep(&sep, buff, bufc); } else { bFirst = false; } for (i = 0; i < lastn; i++) { os[i] = split_token(&cp[i], &sep); } strcpy(atextbuf, atext); str = atextbuf; mux_exec(buff, bufc, thing, executor, enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &(os[0]), lastn); } free_lbuf(atext); free_lbuf(atextbuf); } /* --------------------------------------------------------------------------- * fun_foreach: like map(), but it operates on a string, rather than on a list, * calling a user-defined function for each character in the string. * No delimiter is inserted between the results. * Borrowed from TinyMUSH 2.2 */ FUNCTION(fun_foreach) { if ( nfargs != 2 && nfargs != 4) { safe_str("#-1 FUNCTION (FOREACH) EXPECTS 2 OR 4 ARGUMENTS", buff, bufc); return; } char *atext; dbref thing; if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc)) { return; } char *str; char cbuf[2], prev = '\0'; char *atextbuf = alloc_lbuf("fun_foreach"); SEP sep; sep.n = 1; sep.str[0] = ' '; sep.str[1] = '\0'; char *cp = trim_space_sep(fargs[1], &sep); char *bp = cbuf; cbuf[1] = '\0'; if (nfargs == 4) { bool flag = false; while ( cp && *cp && mudstate.func_invk_ctr < mudconf.func_invk_lim && !MuxAlarm.bAlarmed) { cbuf[0] = *cp++; if (flag) { if ( cbuf[0] == *fargs[3] && prev != '\\' && prev != '%') { flag = false; continue; } } else { if ( cbuf[0] == *fargs[2] && prev != '\\' && prev != '%') { flag = true; continue; } else { safe_chr(cbuf[0], buff, bufc); continue; } } strcpy(atextbuf, atext); str = atextbuf; mux_exec(buff, bufc, thing, executor, enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &bp, 1); prev = cbuf[0]; } } else { while ( cp && *cp && mudstate.func_invk_ctr < mudconf.func_invk_lim && !MuxAlarm.bAlarmed) { cbuf[0] = *cp++; strcpy(atextbuf, atext); str = atextbuf; mux_exec(buff, bufc, thing, executor, enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &bp, 1); } } free_lbuf(atextbuf); free_lbuf(atext); } /* --------------------------------------------------------------------------- * fun_munge: combines two lists in an arbitrary manner. * Borrowed from TinyMUSH 2.2 */ FUNCTION(fun_munge) { SEP sep; if (!OPTIONAL_DELIM(4, sep, DELIM_DFLT|DELIM_STRING)) { return; } // Find our object and attribute. // char *atext; dbref thing; if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc)) { return; } int nptrs1, nptrs2, nresults, i, j; char *list1, *list2, *rlist, *bp, *str, *oldp; char *ptrs1[LBUF_SIZE / 2], *ptrs2[LBUF_SIZE / 2], *results[LBUF_SIZE / 2]; char *uargs[2]; oldp = *bufc; // Copy our lists and chop them up. // list1 = alloc_lbuf("fun_munge.list1"); list2 = alloc_lbuf("fun_munge.list2"); strcpy(list1, fargs[1]); strcpy(list2, fargs[2]); nptrs1 = list2arr(ptrs1, LBUF_SIZE / 2, list1, &sep); nptrs2 = list2arr(ptrs2, LBUF_SIZE / 2, list2, &sep); if (nptrs1 != nptrs2) { safe_str("#-1 LISTS MUST BE OF EQUAL SIZE", buff, bufc); free_lbuf(atext); free_lbuf(list1); free_lbuf(list2); return; } // Call the u-function with the first list as %0. // bp = rlist = alloc_lbuf("fun_munge"); str = atext; uargs[0] = fargs[1]; uargs[1] = sep.str; mux_exec(rlist, &bp, executor, caller, enactor, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, uargs, 2); *bp = '\0'; // Now that we have our result, put it back into array form. // Search through list1 until we find the element position, then // copy the corresponding element from list2. // nresults = list2arr(results, LBUF_SIZE / 2, rlist, &sep); bool bFirst = true; for (i = 0; i < nresults; i++) { for (j = 0; j < nptrs1; j++) { if (!strcmp(results[i], ptrs1[j])) { if (!bFirst) { print_sep(&sep, buff, bufc); } else { bFirst = false; } safe_str(ptrs2[j], buff, bufc); ptrs1[j][0] = '\0'; break; } } } free_lbuf(atext); free_lbuf(list1); free_lbuf(list2); free_lbuf(rlist); } FUNCTION(fun_die) { int n = mux_atol(fargs[0]); int die = mux_atol(fargs[1]); if ( n == 0 || die <= 0) { safe_chr('0', buff, bufc); return; } if ( n < 1 || LBUF_SIZE <= n) { safe_range(buff, bufc); return; } if ( 3 <= nfargs && isTRUE(mux_atol(fargs[2]))) { ITL pContext; ItemToList_Init(&pContext, buff, bufc); for (int count = 0; count < n; count++) { if (!ItemToList_AddInteger(&pContext, RandomINT32(1, die))) { break; } } ItemToList_Final(&pContext); return; } int total = 0; for (int count = 0; count < n; count++) { total += RandomINT32(1, die); } safe_ltoa(total, buff, bufc); } FUNCTION(fun_lrand) { SEP sep; if (!OPTIONAL_DELIM(4, sep, DELIM_NULL|DELIM_CRLF|DELIM_STRING)) { return; } int n_times = mux_atol(fargs[2]); if (n_times < 1) { return; } if (n_times > LBUF_SIZE) { n_times = LBUF_SIZE; } INT32 iLower = mux_atol(fargs[0]); INT32 iUpper = mux_atol(fargs[1]); if (iLower <= iUpper) { for (int i = 0; i < n_times-1; i++) { INT32 val = RandomINT32(iLower, iUpper); safe_ltoa(val, buff, bufc); print_sep(&sep, buff, bufc); } INT32 val = RandomINT32(iLower, iUpper); safe_ltoa(val, buff, bufc); } } // Borrowed from PennMUSH 1.50 // FUNCTION(fun_lit) { // Just returns the argument, literally. // safe_str(fargs[0], buff, bufc); } FUNCTION(fun_dumping) { #ifdef WIN32 safe_chr('0', buff, bufc); #else // WIN32 safe_bool(mudstate.dumping, buff, bufc); #endif // WIN32 } // The following table contains 64 symbols, so this supports -a- // radix-64 encoding. It is not however 'unix-to-unix' encoding. // All of the following characters are valid for an attribute // name, but not for the first character of an attribute name. // static char aRadixTable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$"; FUNCTION(fun_unpack) { // Validate radix if present. // INT64 iRadix = 64; if (nfargs == 2) { if ( !is_integer(fargs[1], NULL) || (iRadix = mux_atoi64(fargs[1])) < 2 || 64 < iRadix) { safe_str("#-1 RADIX MUST BE A NUMBER BETWEEN 2 and 64", buff, bufc); return; } } // Build Table of valid characters. // char MatchTable[256]; memset(MatchTable, 0, sizeof(MatchTable)); for (int i = 0; i < iRadix; i++) { MatchTable[aRadixTable[i]] = i+1; } // Validate that first argument contains only characters from the // subset of permitted characters. // char *pString = fargs[0]; INT64 sum; int c; int LeadingCharacter; // Leading whitespace // while (mux_isspace(*pString)) { pString++; } // Possible sign // LeadingCharacter = c = *pString++; if (c == '-' || c == '+') { c = *pString++; } sum = 0; // Convert symbols // int iValue; while ((iValue = MatchTable[(unsigned int)c])) { sum = iRadix * sum + iValue - 1; c = *pString++; } // Interpret sign // if (LeadingCharacter == '-') { sum = -sum; } safe_i64toa(sum, buff, bufc); } size_t mux_Pack(INT64 val, int iRadix, char *buf) { char *p = buf; // Handle sign. // if (val < 0) { *p++ = '-'; val = -val; } char *q = p; while (val > iRadix-1) { INT64 iDiv = val / iRadix; INT64 iTerm = val - iDiv * iRadix; val = iDiv; *p++ = aRadixTable[iTerm]; } *p++ = aRadixTable[val]; int nLength = p - buf; *p-- = '\0'; // The digits are in reverse order with a possible leading '-' // if the value was negative. q points to the first digit, // and p points to the last digit. // while (q < p) { // Swap characters are *p and *q // char temp = *p; *p = *q; *q = temp; // Move p and first digit towards the middle. // --p; ++q; // Stop when we reach or pass the middle. // } return nLength; } FUNCTION(fun_pack) { // Validate the arguments are numeric. // if ( !is_integer(fargs[0], NULL) || (nfargs == 2 && !is_integer(fargs[1], NULL))) { safe_str("#-1 ARGUMENTS MUST BE NUMBERS", buff, bufc); return; } INT64 val = mux_atoi64(fargs[0]); // Validate the radix is between 2 and 64. // int iRadix = 64; if (nfargs == 2) { iRadix = mux_atol(fargs[1]); if (iRadix < 2 || 64 < iRadix) { safe_str("#-1 RADIX MUST BE A NUMBER BETWEEN 2 and 64", buff, bufc); return; } } char TempBuffer[76]; // 1 '-', 63 binary digits, 1 '\0', 11 for safety. int nLength = mux_Pack(val, iRadix, TempBuffer); safe_copy_buf(TempBuffer, nLength, buff, bufc); } FUNCTION(fun_strcat) { int i; for (i = 0; i < nfargs; i++) { safe_str(fargs[i], buff, bufc); } } // grep() and grepi() code borrowed from PennMUSH 1.50 // char *grep_util(dbref player, dbref thing, char *pattern, char *lookfor, int len, bool insensitive) { // Returns a list of attributes which match <pattern> on <thing> // whose contents have <lookfor>. // dbref aowner; char *tbuf1, *buf; char *bp, *bufc; int ca, aflags; tbuf1 = alloc_lbuf("grep_util"); bufc = buf = alloc_lbuf("grep_util.parse_attrib"); bp = tbuf1; safe_tprintf_str(buf, &bufc, "#%d/%s", thing, pattern); olist_push(); if (parse_attrib_wild(player, buf, &thing, false, false, true)) { BMH_State bmhs; if (insensitive) { BMH_PrepareI(&bmhs, len, lookfor); } else { BMH_Prepare(&bmhs, len, lookfor); } for (ca = olist_first(); ca != NOTHING && !MuxAlarm.bAlarmed; ca = olist_next()) { size_t nText; char *attrib = atr_get_LEN(thing, ca, &aowner, &aflags, &nText); int i; if (insensitive) { i = BMH_ExecuteI(&bmhs, len, lookfor, nText, attrib); } else { i = BMH_Execute(&bmhs, len, lookfor, nText, attrib); } if (i >= 0) { if (bp != tbuf1) { safe_chr(' ', tbuf1, &bp); } ATTR *ap = atr_num(ca); const char *pName = "(WARNING: Bad Attribute Number)"; if (ap) { pName = ap->name; } safe_str(pName, tbuf1, &bp); } free_lbuf(attrib); } } free_lbuf(buf); *bp = '\0'; olist_pop(); return tbuf1; } void grep_handler(char *buff, char **bufc, dbref executor, char *fargs[], bool bCaseInsens) { dbref it = match_thing_quiet(executor, fargs[0]); if (!Good_obj(it)) { safe_match_result(it, buff, bufc); return; } if (!Examinable(executor, it)) { safe_noperm(buff, bufc); return; } // Make sure there's an attribute and a pattern // if (!fargs[1] || !*fargs[1]) { safe_str("#-1 NO SUCH ATTRIBUTE", buff, bufc); return; } if (!fargs[2] || !*fargs[2]) { safe_str("#-1 INVALID GREP PATTERN", buff, bufc); return; } char *tp = grep_util(executor, it, fargs[1], fargs[2], strlen(fargs[2]), bCaseInsens); safe_str(tp, buff, bufc); free_lbuf(tp); } FUNCTION(fun_grep) { grep_handler(buff, bufc, executor, fargs, false); } FUNCTION(fun_grepi) { grep_handler(buff, bufc, executor, fargs, true); } // Borrowed from PennMUSH 1.50 // FUNCTION(fun_alphamax) { char *amax = fargs[0]; for (int i = 1; i < nfargs; i++) { if (fargs[i] && strcmp(amax, fargs[i]) < 0) { amax = fargs[i]; } } safe_str(amax, buff, bufc); } // Borrowed from PennMUSH 1.50 // FUNCTION(fun_alphamin) { char *amin = fargs[0]; for (int i = 1; i < nfargs; i++) { if (fargs[i] && strcmp(amin, fargs[i]) > 0) { amin = fargs[i]; } } safe_str(amin, buff, bufc); } // Borrowed from PennMUSH 1.50 // FUNCTION(fun_valid) { // Checks to see if a given <something> is valid as a parameter of // a given type (such as an object name) // int nValidName; bool bValid; if (!*fargs[0] || !*fargs[1]) { bValid = false; } else if (!mux_stricmp(fargs[0], "name")) { MakeCanonicalObjectName(fargs[1], &nValidName, &bValid); } else if (!mux_stricmp(fargs[0], "attrname")) { MakeCanonicalAttributeName(fargs[1], &nValidName, &bValid); } else if (!mux_stricmp(fargs[0], "playername")) { bValid = ValidatePlayerName(fargs[1]); } else { safe_nothing(buff, bufc); return; } safe_bool(bValid, buff, bufc); } // Borrowed from PennMUSH 1.50 // FUNCTION(fun_hastype) { dbref it = match_thing_quiet(executor, fargs[0]); if (!Good_obj(it)) { safe_match_result(it, buff, bufc); return; } bool bResult = false; switch (mux_tolower(fargs[1][0])) { case 'r': bResult = isRoom(it); break; case 'e': bResult = isExit(it); break; case 'p': bResult = isPlayer(it); break; case 't': bResult = isThing(it); break; default: safe_str("#-1 NO SUCH TYPE", buff, bufc); break; } safe_bool(bResult, buff, bufc); } // Borrowed from PennMUSH 1.50 // FUNCTION(fun_lparent) { dbref it = match_thing_quiet(executor, fargs[0]); if (!Good_obj(it)) { safe_match_result(it, buff, bufc); return; } else if (!Examinable(executor, it)) { safe_noperm(buff, bufc); return; } ITL pContext; ItemToList_Init(&pContext, buff, bufc, '#'); if (!ItemToList_AddInteger(&pContext, it)) { ItemToList_Final(&pContext); return; } dbref par = Parent(it); int iNestLevel = 1; while ( Good_obj(par) && Examinable(executor, it) && iNestLevel < mudconf.parent_nest_lim) { if (!ItemToList_AddInteger(&pContext, par)) { break; } it = par; par = Parent(par); iNestLevel++; } ItemToList_Final(&pContext); } // stacksize - returns how many items are stuffed onto an object stack // int stacksize(dbref doer) { int i; STACK *sp; for (i = 0, sp = Stack(doer); sp != NULL; sp = sp->next, i++) { // Nothing ; } return i; } FUNCTION(fun_lstack) { STACK *sp; dbref doer; if (nfargs == 0 || !*fargs[0]) { doer = executor; } else { doer = match_thing_quiet(executor, fargs[0]); if (!Good_obj(doer)) { safe_match_result(doer, buff, bufc); return; } } if (!Controls(executor, doer)) { safe_noperm(buff, bufc); return; } for (sp = Stack(doer); sp != NULL; sp = sp->next) { safe_str(sp->data, buff, bufc); if (sp->next != NULL) { safe_chr(' ', buff, bufc); } } } // stack_clr - clear the stack. // void stack_clr(dbref obj) { // Clear the stack. // STACK *sp, *next; for (sp = Stack(obj); sp != NULL; sp = next) { next = sp->next; free_lbuf(sp->data); MEMFREE(sp); sp = NULL; } s_Stack(obj, NULL); } FUNCTION(fun_empty) { dbref doer; if (nfargs == 0 || !*fargs[0]) { doer = executor; } else { doer = match_thing_quiet(executor, fargs[0]); if (!Good_obj(doer)) { safe_match_result(doer, buff, bufc); return; } } if (!Controls(executor, doer)) { safe_noperm(buff, bufc); return; } stack_clr(doer); } FUNCTION(fun_items) { dbref doer; if (nfargs == 0 || !*fargs[0]) { doer = executor; } else { doer = match_thing_quiet(executor, fargs[0]); if (!Good_obj(doer)) { safe_match_result(doer, buff, bufc); return; } } if (!Controls(executor, doer)) { safe_noperm(buff, bufc); return; } safe_ltoa(stacksize(doer), buff, bufc); } FUNCTION(fun_peek) { STACK *sp; dbref doer; int count, pos; if (nfargs <= 0 || !*fargs[0]) { doer = executor; } else { doer = match_thing_quiet(executor, fargs[0]); if (!Good_obj(doer)) { safe_match_result(doer, buff, bufc); return; } } if (!Controls(executor, doer)) { safe_noperm(buff, bufc); return; } if (nfargs <= 1 || !*fargs[1]) { pos = 0; } else { pos = mux_atol(fargs[1]); } if (stacksize(doer) == 0) { return; } if (pos > (stacksize(doer) - 1)) { safe_str("#-1 POSITION TOO LARGE", buff, bufc); return; } count = 0; sp = Stack(doer); while (count != pos) { if (sp == NULL) { return; } count++; sp = sp->next; } safe_str(sp->data, buff, bufc); } FUNCTION(fun_pop) { dbref doer; if (nfargs <= 0 || !*fargs[0]) { doer = executor; } else { doer = match_thing_quiet(executor, fargs[0]); if (!Good_obj(doer)) { safe_match_result(doer, buff, bufc); return; } } if (!Controls(executor, doer)) { safe_noperm(buff, bufc); return; } int pos; if (nfargs <= 1 || !*fargs[1]) { pos = 0; } else { pos = mux_atol(fargs[1]); } if (stacksize(doer) == 0) { return; } if (pos > (stacksize(doer) - 1)) { safe_str("#-1 POSITION TOO LARGE", buff, bufc); return; } STACK *sp = Stack(doer); STACK *prev = NULL; int count = 0; while (count != pos) { if (sp == NULL) { return; } prev = sp; sp = sp->next; count++; } safe_str(sp->data, buff, bufc); if (count == 0) { s_Stack(doer, sp->next); free_lbuf(sp->data); MEMFREE(sp); sp = NULL; } else { prev->next = sp->next; free_lbuf(sp->data); MEMFREE(sp); sp = NULL; } } FUNCTION(fun_push) { dbref doer; char *data; if (nfargs <= 1 || !*fargs[1]) { doer = executor; data = fargs[0]; } else { doer = match_thing_quiet(executor, fargs[0]); if (!Good_obj(doer)) { safe_match_result(doer, buff, bufc); return; } data = fargs[1]; } if (!Controls(executor, doer)) { safe_noperm(buff, bufc); return; } if (stacksize(doer) >= mudconf.stack_limit) { safe_str("#-1 STACK SIZE EXCEEDED", buff, bufc); return; } STACK *sp = (STACK *)MEMALLOC(sizeof(STACK)); ISOUTOFMEMORY(sp); sp->next = Stack(doer); sp->data = alloc_lbuf("push"); strcpy(sp->data, data); s_Stack(doer, sp); } /* --------------------------------------------------------------------------- * fun_regmatch: Return 0 or 1 depending on whether or not a regular * expression matches a string. If a third argument is specified, dump * the results of a regexp pattern match into a set of arbitrary r()-registers. * * regmatch(string, pattern, list of registers) * If the number of matches exceeds the registers, those bits are tossed * out. * If -1 is specified as a register number, the matching bit is tossed. * Therefore, if the list is "-1 0 3 5", the regexp $0 is tossed, and * the regexp $1, $2, and $3 become r(0), r(3), and r(5), respectively. */ void real_regmatch(const char *search, const char *pattern, char *registers, int nfargs, char *buff, char **bufc, bool cis) { if (MuxAlarm.bAlarmed) { return; } const char *errptr; int erroffset; // To capture N substrings, you need space for 3(N+1) offsets in the // offset vector. We'll allow 2N-1 substrings and possibly ignore some. // const int ovecsize = 6 * MAX_GLOBAL_REGS; int ovec[ovecsize]; pcre *re = pcre_compile(pattern, cis ? PCRE_CASELESS : 0, &errptr, &erroffset, NULL); if (!re) { // Matching error. // safe_str("#-1 REGEXP ERROR ", buff, bufc); safe_str(errptr, buff, bufc); return; } int matches = pcre_exec(re, NULL, search, strlen(search), 0, 0, ovec, ovecsize); if (matches == 0) { // There were too many substring matches. See docs for // pcre_copy_substring(). // matches = ovecsize / 3; } safe_bool(matches > 0, buff, bufc); if (matches < 0) { matches = 0; } // If we don't have a third argument, we're done. // if (nfargs != 3) { MEMFREE(re); return; } // We need to parse the list of registers. If a register is // mentioned in the list, then either fill the register with the // subexpression, or if there wasn't a match, clear it. // const int NSUBEXP = 2 * MAX_GLOBAL_REGS; char *qregs[NSUBEXP]; SEP sep; sep.n = 1; memcpy(sep.str, " ", 2); int nqregs = list2arr(qregs, NSUBEXP, registers, &sep); int i; for (i = 0; i < nqregs; i++) { int curq; if ( qregs[i] && *qregs[i] && (curq = mux_RegisterSet[(unsigned char)qregs[i][0]]) != -1 && qregs[i][1] == '\0' && curq < MAX_GLOBAL_REGS) { if (!mudstate.global_regs[curq]) { mudstate.global_regs[curq] = alloc_lbuf("fun_regmatch"); } int len; len = pcre_copy_substring(search, ovec, matches, i, mudstate.global_regs[curq], LBUF_SIZE); mudstate.glob_reg_len[curq] = (len > 0 ? len : 0); } } MEMFREE(re); } FUNCTION(fun_regmatch) { real_regmatch(fargs[0], fargs[1], fargs[2], nfargs, buff, bufc, false); } FUNCTION(fun_regmatchi) { real_regmatch(fargs[0], fargs[1], fargs[2], nfargs, buff, bufc, true); } /* --------------------------------------------------------------------------- * regrab(), regraball(). Like grab() and graball(), using a regular expression * instead of a wildcard pattern. The versions ending in i are case-insensitive. */ void real_regrab(char *search, const char *pattern, SEP *psep, char *buff, char **bufc, bool cis, bool all) { if (MuxAlarm.bAlarmed) { return; } pcre *re; pcre_extra *study = NULL; const char *errptr; int erroffset; // To capture N substrings, you need space for 3(N+1) offsets in the // offset vector. We'll allow 2N-1 substrings and possibly ignore some. // const int ovecsize = 6 * MAX_GLOBAL_REGS; int ovec[ovecsize]; re = pcre_compile(pattern, cis ? PCRE_CASELESS : 0, &errptr, &erroffset, NULL); if (!re) { // Matching error. // safe_str("#-1 REGEXP ERROR ", buff, bufc); safe_str(errptr, buff, bufc); return; } if (all) { study = pcre_study(re, 0, &errptr); } bool first = true; char *s = trim_space_sep(search, psep); do { char *r = split_token(&s, psep); if ( !MuxAlarm.bAlarmed && pcre_exec(re, study, r, strlen(r), 0, 0, ovec, ovecsize) >= 0) { if (first) { first = false; } else { print_sep(psep, buff, bufc); } safe_str(r, buff, bufc); if (!all) { break; } } } while (s); MEMFREE(re); if (study) { MEMFREE(study); } } FUNCTION(fun_regrab) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING)) { return; } real_regrab(fargs[0], fargs[1], &sep, buff, bufc, false, false); } FUNCTION(fun_regrabi) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING)) { return; } real_regrab(fargs[0], fargs[1], &sep, buff, bufc, true, false); } FUNCTION(fun_regraball) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING)) { return; } real_regrab(fargs[0], fargs[1], &sep, buff, bufc, false, true); } FUNCTION(fun_regraballi) { SEP sep; if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING)) { return; } real_regrab(fargs[0], fargs[1], &sep, buff, bufc, true, true); } /* --------------------------------------------------------------------------- * fun_translate: Takes a string and a second argument. If the second argument * is 0 or s, control characters are converted to spaces. If it's 1 or p, * they're converted to percent substitutions. */ FUNCTION(fun_translate) { int ch = fargs[1][0]; bool type = (ch == 'p' || ch == '1'); safe_str(translate_string(fargs[0], type), buff, bufc); } // Construct a CBitField to hold (nMaximum_arg+1) bits numbered 0 through // nMaximum_arg. // CBitField::CBitField(unsigned int nMaximum_arg) { nMaximum = 0; nInts = 0; pInts = NULL; pMasks = NULL; nBitsPer = sizeof(UINT32)*8; // Calculate Shift // nShift = 0; unsigned int i = 1; while (i < nBitsPer) { nShift++; i <<= 1; } // Calculate Mask // nMask = nBitsPer - 1; // Allocate array of UINT32s. // Resize(nMaximum_arg); } #define MINIMUM_RESIZE (4096*sizeof(UINT32)) void CBitField::Resize(unsigned int nMaximum_arg) { if ( 0 < nMaximum_arg && nMaximum < nMaximum_arg) { unsigned int nNewMaximum = nMaximum_arg; // This provides some assurances that we are not resizing too often. // if ( pMasks && nNewMaximum < nMaximum + MINIMUM_RESIZE) { nNewMaximum = nMaximum + MINIMUM_RESIZE; } size_t nNewInts = (nNewMaximum+nBitsPer) >> nShift; UINT32 *pNewMasks = (UINT32 *)MEMALLOC((nNewInts+nBitsPer) * sizeof(UINT32)); ISOUTOFMEMORY(pNewMasks); UINT32 *pNewInts = pNewMasks + nBitsPer; // Is this the first sizing or a re-sizing? // if (pMasks) { // Copy existing masks and bits to the new location, and // clear the new bits. // memcpy(pNewMasks, pMasks, (nInts+nBitsPer)*sizeof(UINT32)); memset(pNewInts + nInts, 0, (nNewInts - nInts)*sizeof(UINT32)); // Free the previous allocation. // MEMFREE(pMasks); // A reallocation. // nMaximum = nNewMaximum; nInts = nNewInts; pMasks = pNewMasks; pInts = pNewInts; } else { // First allocation. // nMaximum = nNewMaximum; nInts = nNewInts; pMasks = pNewMasks; pInts = pNewInts; // Initialize masks by calculating all possible single bits. // for (int i = 0; i < nBitsPer; i++) { pMasks[i] = ((UINT32)1) << i; } // Initialize bits by clearing them all. // ClearAll(); } } } CBitField::~CBitField(void) { pInts = NULL; if (pMasks) { MEMFREE(pMasks); pMasks = NULL; } } void CBitField::ClearAll(void) { memset(pInts, 0, nInts*sizeof(UINT32)); } void CBitField::Set(unsigned int i) { if (i <= nMaximum) { pInts[i>>nShift] |= pMasks[i&nMask]; } } void CBitField::Clear(unsigned int i) { if (i <= nMaximum) { pInts[i>>nShift] &= ~pMasks[i&nMask]; } } bool CBitField::IsSet(unsigned int i) { if (i <= nMaximum) { if (pInts[i>>nShift] & pMasks[i&nMask]) { return true; } } return false; } // ------------------------------------------------------------------------- // fun_lrooms: Takes a dbref (room), an int (N), and an optional bool (B). // // MUX Syntax: lrooms(<room> [,<N>[,<B>]]) // // Returns a list of rooms <N>-levels deep from <room>. If <B> == 1, it will // return all room dbrefs between 0 and <N> levels, while <B> == 0 will // return only the room dbrefs on the Nth level. The default is to show all // rooms dbrefs between 0 and <N> levels. // // Written by Marlek. Idea from RhostMUSH. // static void room_list ( dbref player, dbref enactor, dbref room, int level, int maxlevels, bool showall ) { // Make sure the player can really see this room from their location. // if ( ( level == maxlevels || showall) && ( Examinable(player, room) || Location(player) == room || room == enactor)) { mudstate.bfReport.Set(room); } // If the Nth level has been reach, stop this branch in the recursion // if (level >= maxlevels) { return; } // Return info for all parent levels. // int lev; dbref parent; ITER_PARENTS(room, parent, lev) { // Look for exits at each level. // if (!Has_exits(parent)) { continue; } int key = 0; if (Examinable(player, parent)) { key |= VE_LOC_XAM; } if (Dark(parent)) { key |= VE_LOC_DARK; } if (Dark(room)) { key |= VE_BASE_DARK; } dbref thing; DOLIST(thing, Exits(parent)) { dbref loc = Location(thing); if ( exit_visible(thing, player, key) && !mudstate.bfTraverse.IsSet(loc)) { mudstate.bfTraverse.Set(loc); room_list(player, enactor, loc, (level + 1), maxlevels, showall); } } } } FUNCTION(fun_lrooms) { dbref room = match_thing_quiet(executor, fargs[0]); if (!Good_obj(room)) { safe_match_result(room, buff, bufc); return; } else if (!isRoom(room)) { safe_str("#-1 FIRST ARGUMENT MUST BE A ROOM", buff, bufc); return; } int N = 1; if (nfargs >= 2) { N = mux_atol(fargs[1]); if (N < 0) { safe_str("#-1 SECOND ARGUMENT MUST BE A POSITIVE NUMBER", buff, bufc); return; } else if (N > 50) { // Maybe this can be turned into a config parameter to prevent // misuse by putting in really large values. // safe_str("#-1 SECOND ARGUMENT IS TOO LARGE", buff, bufc); return; } } bool B = true; if (nfargs == 3) { B = xlate(fargs[2]); } mudstate.bfReport.Resize(mudstate.db_top-1); mudstate.bfTraverse.Resize(mudstate.db_top-1); mudstate.bfReport.ClearAll(); mudstate.bfTraverse.ClearAll(); mudstate.bfTraverse.Set(room); room_list(executor, enactor, room, 0, N, B); mudstate.bfReport.Clear(room); ITL pContext; ItemToList_Init(&pContext, buff, bufc, '#'); dbref i; DO_WHOLE_DB(i) { if ( mudstate.bfReport.IsSet(i) && !ItemToList_AddInteger(&pContext, i)) { break; } } ItemToList_Final(&pContext); }