// functions.cpp - MUX function handlers // // $Id: functions.cpp,v 1.28 2000/09/20 22:22:47 sdennis Exp $ // #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #include <limits.h> #include <math.h> #include <float.h> #include "mudconf.h" #include "db.h" #include "flags.h" #include "powers.h" #include "attrs.h" #include "match.h" #include "command.h" #include "functions.h" #include "misc.h" #include "alloc.h" #include "ansi.h" #include "comsys.h" UFUN *ufun_head; extern NAMETAB indiv_attraccess_nametab[]; extern void FDECL(cf_log_notfound, (dbref player, char *cmd, const char *thingname, char *thing)); /* * Function definitions from funceval.c */ #define XFUNCTION(x) extern void x(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs) XFUNCTION(fun_cwho); XFUNCTION(fun_beep); XFUNCTION(fun_ansi); XFUNCTION(fun_zone); #ifdef SIDE_EFFECT_FUNCTIONS XFUNCTION(fun_link); XFUNCTION(fun_tel); XFUNCTION(fun_pemit); XFUNCTION(fun_create); XFUNCTION(fun_set); #endif XFUNCTION(fun_last); XFUNCTION(fun_matchall); XFUNCTION(fun_ports); XFUNCTION(fun_mix); XFUNCTION(fun_foreach); XFUNCTION(fun_munge); XFUNCTION(fun_visible); XFUNCTION(fun_elements); XFUNCTION(fun_grab); XFUNCTION(fun_scramble); XFUNCTION(fun_shuffle); XFUNCTION(fun_sortby); XFUNCTION(fun_default); XFUNCTION(fun_edefault); XFUNCTION(fun_udefault); XFUNCTION(fun_findable); XFUNCTION(fun_isword); XFUNCTION(fun_hasattr); XFUNCTION(fun_hasattrp); XFUNCTION(fun_zwho); XFUNCTION(fun_inzone); XFUNCTION(fun_children); XFUNCTION(fun_encrypt); XFUNCTION(fun_decrypt); XFUNCTION(fun_objeval); XFUNCTION(fun_squish); XFUNCTION(fun_stripansi); XFUNCTION(fun_zfun); XFUNCTION(fun_columns); XFUNCTION(fun_playmem); XFUNCTION(fun_objmem); XFUNCTION(fun_orflags); XFUNCTION(fun_andflags); XFUNCTION(fun_strtrunc); XFUNCTION(fun_ifelse); XFUNCTION(fun_inc); XFUNCTION(fun_dec); XFUNCTION(fun_mail); XFUNCTION(fun_mailfrom); XFUNCTION(fun_die); XFUNCTION(fun_lit); XFUNCTION(fun_shl); XFUNCTION(fun_shr); XFUNCTION(fun_band); XFUNCTION(fun_bor); XFUNCTION(fun_bnand); XFUNCTION(fun_vadd); XFUNCTION(fun_vsub); XFUNCTION(fun_vmul); XFUNCTION(fun_vmag); XFUNCTION(fun_vunit); XFUNCTION(fun_vdim); XFUNCTION(fun_strcat); XFUNCTION(fun_grep); XFUNCTION(fun_grepi); XFUNCTION(fun_art); XFUNCTION(fun_alphamax); XFUNCTION(fun_alphamin); XFUNCTION(fun_valid); XFUNCTION(fun_hastype); XFUNCTION(fun_lparent); XFUNCTION(fun_empty); XFUNCTION(fun_push); XFUNCTION(fun_peek); XFUNCTION(fun_pop); XFUNCTION(fun_items); XFUNCTION(fun_lstack); XFUNCTION(fun_regmatch); XFUNCTION(fun_translate); XFUNCTION(fun_doing); // in netcommon.cpp XFUNCTION(fun_poll); // in netcommon.cpp XFUNCTION(fun_motd); // in netcommon.cpp XFUNCTION(fun_channels); // in comsys.cpp XFUNCTION(fun_comtitle); // in comsys.cpp XFUNCTION(fun_comalias); // in comsys.cpp XFUNCTION(fun_iadd); XFUNCTION(fun_isub); XFUNCTION(fun_imul); XFUNCTION(fun_digittime); XFUNCTION(fun_singletime); XFUNCTION(fun_exptime); XFUNCTION(fun_writetime); XFUNCTION(fun_cmds); XFUNCTION(fun_startsecs); XFUNCTION(fun_lflags); XFUNCTION(fun_lattrcmds); XFUNCTION(fun_lcmds); XFUNCTION(fun_conntotal); XFUNCTION(fun_connmax); XFUNCTION(fun_connlast); XFUNCTION(fun_connnum); XFUNCTION(fun_connleft); // Trim off leading and trailing spaces if the separator char is a // space -- known length version. // char *trim_space_sep_LEN(char *str, int nStr, char sep, int *nTrim) { if (sep != ' ') { *nTrim = nStr; return str; } // Advance over leading spaces. // char *pBegin = str; char *pEnd = str + nStr - 1; while (*pBegin == ' ') { pBegin++; } // Advance over trailing spaces. // for (; *pEnd == ' ' && pEnd > pBegin; pEnd--) { // Nothing } pEnd++; *pEnd = '\0'; *nTrim = pEnd - pBegin; return pBegin; } /* * Trim off leading and trailing spaces if the separator char is a space */ char *trim_space_sep(char *str, char sep) { char *p; if (sep != ' ') { return str; } while (*str == ' ') { str++; } for (p = str; *p; p++) { // Nothing } for (p--; *p == ' ' && p > str; p--) { // Nothing } p++; *p = '\0'; return str; } // next_token: Point at start of next token in string -- known length // version. // char *next_token_LEN(char *str, int *nStr, char sep) { char *pBegin = str; while (*pBegin && (*pBegin != sep)) { pBegin++; } if (!*pBegin) { *nStr = 0; return NULL; } pBegin++; if (sep == ' ') { while (*pBegin == ' ') { pBegin++; } } *nStr -= pBegin - str; return pBegin; } /* * next_token: Point at start of next token in string */ char *next_token(char *str, char sep) { while (*str && (*str != sep)) { str++; } if (!*str) { return NULL; } str++; if (sep == ' ') { while (*str == ' ') { str++; } } return str; } // split_token: Get next token from string as null-term string. String is // destructively modified -- known length version. // char *split_token_LEN(char **sp, int *nStr, char sep, int *nToken) { char *str, *save; save = str = *sp; if (!str) { *nStr = 0; *sp = NULL; *nToken = 0; return NULL; } // Advance over token // while (*str && (*str != sep)) { str++; } *nToken = str - save; if (*str) { *str++ = '\0'; if (sep == ' ') { while (*str == ' ') { str++; } } *nStr -= str - save; } else { *nStr = 0; str = NULL; } *sp = str; return save; } /* * split_token: Get next token from string as null-term string. String is * * destructively modified. */ char *split_token(char **sp, char sep) { char *str, *save; save = str = *sp; if (!str) { *sp = NULL; return NULL; } while (*str && (*str != sep)) { str++; } if (*str) { *str++ = '\0'; if (sep == ' ') { while (*str == ' ') { str++; } } } else { str = NULL; } *sp = str; return save; } /* * --------------------------------------------------------------------------- * * List management utilities. */ #define ALPHANUM_LIST 1 #define NUMERIC_LIST 2 #define DBREF_LIST 3 #define FLOAT_LIST 4 static int autodetect_list(char *ptrs[], int nitems) { int sort_type, i; char *p; sort_type = NUMERIC_LIST; for (i = 0; i < nitems; i++) { switch (sort_type) { case NUMERIC_LIST: if (!is_number(ptrs[i])) { // If non-numeric, switch to alphanum sort. Exception: if this // is the first element and it is a good dbref, switch to a // dbref sort. We're a little looser than the normal 'good // dbref' rules, any number following # the #-sign is // accepted. // if (i == 0) { p = ptrs[i]; if (*p++ != NUMBER_TOKEN) { return ALPHANUM_LIST; } else if (is_integer(p, 0)) { sort_type = DBREF_LIST; } else { return ALPHANUM_LIST; } } else { return ALPHANUM_LIST; } } else if (strchr(ptrs[i], '.')) { sort_type = FLOAT_LIST; } break; case FLOAT_LIST: if (!is_number(ptrs[i])) { sort_type = ALPHANUM_LIST; return ALPHANUM_LIST; } break; case DBREF_LIST: p = ptrs[i]; if (*p++ != NUMBER_TOKEN) { return ALPHANUM_LIST; } if (!is_integer(p, 0)) { return ALPHANUM_LIST; } break; default: return ALPHANUM_LIST; } } return sort_type; } static int get_list_type ( char *fargs[], int nfargs, int type_pos, char *ptrs[], int nitems ) { if (nfargs >= type_pos) { switch (Tiny_ToLower[(unsigned char)*fargs[type_pos - 1]]) { case 'd': return DBREF_LIST; case 'n': return NUMERIC_LIST; case 'f': return FLOAT_LIST; case '\0': return autodetect_list(ptrs, nitems); default: return ALPHANUM_LIST; } } return autodetect_list(ptrs, nitems); } int list2arr(char *arr[], int maxlen, char *list, char sep) { char *p; int i; list = trim_space_sep(list, sep); p = split_token(&list, sep); for (i = 0; p && i < maxlen; i++, p = split_token(&list, sep)) { arr[i] = p; } return i; } void arr2list(char *arr[], int alen, char *list, char **bufc, char sep) { int i; for (i = 0; i < alen-1; i++) { safe_str(arr[i], list, bufc); print_sep(sep, list, bufc); } if (alen) { safe_str(arr[i], list, bufc); } } static int dbnum(char *dbr) { if (dbr[0] != '#' || dbr[1] == '\0') { return 0; } else { return Tiny_atol(dbr + 1); } } /* * --------------------------------------------------------------------------- * * nearby_or_control: Check if player is near or controls thing */ int nearby_or_control(dbref player, dbref thing) { if (!Good_obj(player) || !Good_obj(thing)) return 0; if (Controls(player, thing)) return 1; if (!nearby(player, thing)) return 0; return 1; } static char *strip_useless_zeroes(char *pBegin, char *pEnd) { // Remove useless trailing 0's between pBegin and pEnd. // while (pBegin <= pEnd && pEnd[0] == '0') { pEnd--; } // Take care of dangling '.' // if (pBegin <= pEnd && pEnd[0] == '.') { pEnd--; } // Terminate string. // pEnd++; pEnd[0] = '\0'; return pEnd; } #ifdef HAVE_IEEE_FP_FORMAT char *TinyFPStrings[] = { "+Inf", "-Inf", "Ind", "NaN", "0", "0", "0", "0" }; #define TINY_FPGROUP_PASS 0x00 // Pass-through to printf #define TINY_FPGROUP_ZERO 0x10 // Force to be zero. #define TINY_FPGROUP_PINF 0x20 // "+Inf" #define TINY_FPGROUP_NINF 0x30 // "-Inf" #define TINY_FPGROUP_IND 0x40 // "Ind" #define TINY_FPGROUP_NAN 0x50 // "NaN" #define TINY_FPGROUP(x) ((x) & 0xF0) // Tiny_fpclass returns an integer that is one of the following: // #define TINY_FPCLASS_PINF (TINY_FPGROUP_PINF|0) // Positive infinity (+INF) #define TINY_FPCLASS_NINF (TINY_FPGROUP_NINF|1) // Negative infinity (-INF) #define TINY_FPCLASS_QNAN (TINY_FPGROUP_IND |2) // Quiet NAN (Indefinite) #define TINY_FPCLASS_SNAN (TINY_FPGROUP_NAN |3) // Signaling NAN #define TINY_FPCLASS_ND (TINY_FPGROUP_ZERO|4) // Negative denormalized #define TINY_FPCLASS_NZ (TINY_FPGROUP_ZERO|5) // Negative zero (-0) #define TINY_FPCLASS_PZ (TINY_FPGROUP_ZERO|6) // Positive zero (+0) #define TINY_FPCLASS_PD (TINY_FPGROUP_ZERO|7) // Positive denormalized #define TINY_FPCLASS_PN (TINY_FPGROUP_PASS|8) // Positive normalized non-zero #define TINY_FPCLASS_NN (TINY_FPGROUP_PASS|9) // Negative normalized non-zero #define TINY_FPCLASS(x) ((x) & 0x0F) #ifdef WIN32 #define IEEE_MASK_SIGN 0x8000000000000000ui64 #define IEEE_MASK_EXPONENT 0x7FF0000000000000i64 #define IEEE_MASK_MANTISSA 0x000FFFFFFFFFFFFFi64 #define IEEE_MASK_QNAN 0x0008000000000000i64 #else #define IEEE_MASK_SIGN 0x8000000000000000ull #define IEEE_MASK_EXPONENT 0x7FF0000000000000ull #define IEEE_MASK_MANTISSA 0x000FFFFFFFFFFFFFull #define IEEE_MASK_QNAN 0x0008000000000000ull #endif static int Tiny_fpclass(double result) { UINT64 i64; *((double *)&i64) = result; if ((i64 & IEEE_MASK_EXPONENT) == 0) { if (i64 & IEEE_MASK_MANTISSA) { if (i64 & IEEE_MASK_SIGN) return TINY_FPCLASS_ND; else return TINY_FPCLASS_PD; } else { if (i64 & IEEE_MASK_SIGN) return TINY_FPCLASS_NZ; else return TINY_FPCLASS_PZ; } } else if ((i64 & IEEE_MASK_EXPONENT) == IEEE_MASK_EXPONENT) { if (i64 & IEEE_MASK_MANTISSA) { if (i64 & IEEE_MASK_QNAN) return TINY_FPCLASS_QNAN; else return TINY_FPCLASS_SNAN; } else { if (i64 & IEEE_MASK_SIGN) return TINY_FPCLASS_NINF; else return TINY_FPCLASS_PINF; } } else { if (i64 & IEEE_MASK_SIGN) return TINY_FPCLASS_NN; else return TINY_FPCLASS_PN; } } #endif // HAVE_IEEE_FP_FORMAT /* * --------------------------------------------------------------------------- * * fval: copy the floating point value into a buffer and make it presentable */ static void fval(char *buff, char **bufc, double result) { char *pBegin = *bufc; // Get double val into buffer. // #ifdef HAVE_IEEE_FP_FORMAT int fpc = Tiny_fpclass(result); if (TINY_FPGROUP(fpc) == TINY_FPGROUP_PASS) { #endif // HAVE_IEEE_FP_FORMAT safe_tprintf_str(buff, bufc, "%.6f", result); char *pEnd = (*bufc) - 1; // Remove useless trailing 0's between pBegin and pEnd. // pEnd = strip_useless_zeroes(pBegin, pEnd); // Update bufc pointer. // *bufc = pEnd; #ifdef HAVE_IEEE_FP_FORMAT } else { safe_str(TinyFPStrings[TINY_FPCLASS(fpc)], buff, bufc); } #endif // HAVE_IEEE_FP_FORMAT } static void fval_buf(char *buff, double result) { char *pBegin = buff; // Get double val into buffer. // #ifdef HAVE_IEEE_FP_FORMAT int fpc = Tiny_fpclass(result); if (TINY_FPGROUP(fpc) == TINY_FPGROUP_PASS) { #endif // HAVE_IEEE_FP_FORMAT sprintf(buff, "%.6f", result); char *pEnd = buff + strlen(buff) - 1; // Remove useless trailing 0's between pBegin and pEnd. // strip_useless_zeroes(pBegin, pEnd); #ifdef HAVE_IEEE_FP_FORMAT } else { strcpy(buff, TinyFPStrings[TINY_FPCLASS(fpc)]); } #endif // HAVE_IEEE_FP_FORMAT } /* * --------------------------------------------------------------------------- * * fn_range_check: Check # of args to a function with an optional argument * * for validity. */ int fn_range_check(const char *fname, int nfargs, int minargs, int maxargs, char *result, char **bufc) { if ((nfargs >= minargs) && (nfargs <= maxargs)) return 1; if (maxargs == (minargs + 1)) safe_tprintf_str(result, bufc, "#-1 FUNCTION (%s) EXPECTS %d OR %d ARGUMENTS", fname, minargs, maxargs); else safe_tprintf_str(result, bufc, "#-1 FUNCTION (%s) EXPECTS BETWEEN %d AND %d ARGUMENTS", fname, minargs, maxargs); return 0; } /* * --------------------------------------------------------------------------- * * delim_check: obtain delimiter */ int delim_check ( char *fargs[], int nfargs, int sep_arg, char *sep, char *buff, char **bufc, int eval, dbref player, dbref cause, char *cargs[], int ncargs, int allow_special ) { int bSuccess = 1; if (nfargs >= sep_arg) { // First, we decide whether to evalute fargs[sep_arg-1] or not. // char *tstr = fargs[sep_arg-1]; int tlen = strlen(tstr); if (tlen <= 1) { eval = 0; } if (eval) { char *str = tstr; char *bp = tstr = alloc_lbuf("delim_check"); TinyExec(tstr, &bp, 0, player, cause, EV_EVAL | EV_FCHECK, &str, cargs, ncargs); *bp = '\0'; tlen = bp - tstr; } // Regardless of evaulation or no, tstr contains the we need to // look at, and tlen is the length of this string. // if (tlen == 1) { *sep = tstr[0]; } else if (tlen == 0) { *sep = ' '; } else { // We might have an issue. // if (tlen == 2 && allow_special) { if (memcmp(tstr, (char *)NULL_DELIM_VAR, 2) == 0) { *sep = '\0'; } else if (memcmp(tstr, (char *)"\r\n", 2) == 0) { *sep = '\r'; } else { bSuccess = 0; } } else { bSuccess = 0; } } // Clean up the evaluation buffer. // if (eval) { free_lbuf(tstr); } if (!bSuccess) { safe_str("#-1 SEPARATOR MUST BE ONE CHARACTER", buff, bufc); return 0; } } else { *sep = ' '; } return 1; } /* * --------------------------------------------------------------------------- * * fun_words: Returns number of words in a string. * * Added 1/28/91 Philip D. Wasson */ int countwords(char *str, char sep) { int n; str = trim_space_sep(str, sep); if (!*str) return 0; for (n = 0; str; str = next_token(str, sep), n++) ; return n; } FUNCTION(fun_words) { char sep; if (nfargs == 0) { safe_chr('0', buff, bufc); return; } varargs_preamble("WORDS", 2); safe_ltoa(countwords(fargs[0], sep), buff, bufc, LBUF_SIZE-1); } /* * fun_flags: Returns the flags on an object. * Because @switch is case-insensitive, not quite as useful as it could be. */ FUNCTION(fun_flags) { dbref it; char *buff2; it = match_thing(player, fargs[0]); if ((it != NOTHING) && (mudconf.pub_flags || Examinable(player, it) || (it == cause))) { buff2 = unparse_flags(player, it); safe_str(buff2, buff, bufc); free_sbuf(buff2); } else safe_str("#-1", buff, bufc); return; } /* * --------------------------------------------------------------------------- * * fun_rand: Return a random number from 0 to arg1-1 */ FUNCTION(fun_rand) { int num = Tiny_atol(fargs[0]); if (num < 1) { safe_chr('0', buff, bufc); } else { safe_ltoa(RandomLong(0,num-1), buff, bufc, LBUF_SIZE-1); } } /* * --------------------------------------------------------------------------- * * fun_abs: Returns the absolute value of its argument. */ FUNCTION(fun_abs) { double num; num = safe_atof(fargs[0]); if (num == 0.0) { safe_chr('0', buff, bufc); } else if (num < 0.0) { fval(buff, bufc, -num); } else { fval(buff, bufc, num); } } /* * --------------------------------------------------------------------------- * * fun_sign: Returns -1, 0, or 1 based on the the sign of its argument. */ FUNCTION(fun_sign) { double num; num = safe_atof(fargs[0]); if (num < 0) { safe_str("-1", buff, bufc); } else { safe_chr((num > 0) ? '1' : '0', buff, bufc); } } // --------------------------------------------------------------------------- // fun_time: // // With no arguments, it returns local time in the 'Ddd Mmm DD HH:MM:SS YYYY' // format. // // If an argument is provided, "utc" gives a UTC time string, and "local" // gives the local time string. // FUNCTION(fun_time) { CLinearTimeAbsolute ltaNow; if (!fn_range_check("TIME", nfargs, 0, 1, buff, bufc)) { return; } if (nfargs == 0 || _stricmp("utc", fargs[0]) != 0) { ltaNow.GetLocal(); } else { ltaNow.GetUTC(); } char *temp = ltaNow.ReturnDateString(); safe_str(temp, buff, bufc); } // --------------------------------------------------------------------------- // fun_secs. // // With no arguments, it returns seconds since Jan 01 00:00:00 1970 UTC not // counting leap seconds. // // If an argument is provided, "utc" gives UTC seconds, and "local" gives // an integer which corresponds to a local time string. It is not useful // as a count, but it can be given to convsecs(secs(),raw) to get the // corresponding time string. // FUNCTION(fun_secs) { CLinearTimeAbsolute ltaNow; if (!fn_range_check("SECS", nfargs, 0, 1, buff, bufc)) { return; } if (nfargs == 0 || _stricmp("local", fargs[0]) != 0) { ltaNow.GetUTC(); } else { ltaNow.GetLocal(); } safe_str(ltaNow.ReturnSecondsString(), buff, bufc); } // --------------------------------------------------------------------------- // fun_convsecs. // // With one arguments, it converts seconds from Jan 01 00:00:00 1970 UTC to a // local time string in the 'Ddd Mmm DD HH:MM:SS YYYY' format. // // If a second argument is given, it is the <zonename>: // // local - indicates that a conversion for timezone/DST of the server should // be applied (default if no second argument is given). // // utc - indicates that no timezone/DST conversions should be applied. // This is useful to give a unique one-to-one mapping between an // integer and it's corresponding text-string. // FUNCTION(fun_convsecs) { CLinearTimeAbsolute lta; if (!fn_range_check("CONVSECS", nfargs, 1, 2, buff, bufc)) { return; } lta.SetSecondsString(fargs[0]); if (nfargs == 1 || _stricmp("utc", fargs[1]) != 0) { lta.UTC2Local(); } char *temp = lta.ReturnDateString(); safe_str(temp, buff, bufc); } // --------------------------------------------------------------------------- // fun_convtime. // // With one argument, it converts a local time string in the format //'[Ddd] Mmm DD HH:MM:SS YYYY' to a count of seconds from Jan 01 00:00:00 1970 // UTC. // // If a second argument is given, it is the <zonename>: // // local - indicates that the given time string is for the local timezone // local DST adjustments (default if no second argument is given). // // utc - indicates that no timezone/DST conversions should be applied. // This is useful to give a unique one-to-one mapping between an // integer and it's corresponding text-string. // // This function returns -1 if there was a problem parsing the time string. // FUNCTION(fun_convtime) { CLinearTimeAbsolute lta; if (!fn_range_check("CONVTIME", nfargs, 1, 2, buff, bufc)) { return; } if (lta.SetString(fargs[0])) { if (nfargs == 1 || _stricmp("utc", fargs[1]) != 0) { lta.Local2UTC(); } safe_str(Tiny_i64toa_t(lta.ReturnSeconds()), buff, bufc); } else { safe_str("-1", buff, bufc); } } /* * --------------------------------------------------------------------------- * * fun_starttime: What time did this system last reboot? */ FUNCTION(fun_starttime) { char *temp = mudstate.start_time.ReturnDateString(); safe_str(temp, buff, bufc); } /* * --------------------------------------------------------------------------- * * fun_get, fun_get_eval: Get attribute from object. */ int check_read_perms(dbref player, dbref thing, ATTR *attr, int aowner, int aflags, char *buff, char **bufc) { int see_it; /* * If we have explicit read permission to the attr, return it */ if (See_attr_explicit(player, thing, attr, aowner, aflags)) return 1; /* * If we are nearby or have examine privs to the attr and it is * * * * * * visible to us, return it. */ see_it = See_attr(player, thing, attr, aowner, aflags); if ((Examinable(player, thing) || nearby(player, thing) || See_All(player)) && see_it) return 1; /* * For any object, we can read its visible attributes, EXCEPT * for * * * * * * descs, which are only visible if read_rem_desc is on. */ if (see_it) { if (!mudconf.read_rem_desc && (attr->number == A_DESC)) { safe_str("#-1 TOO FAR AWAY TO SEE", buff, bufc); return 0; } else { return 1; } } safe_str("#-1 PERMISSION DENIED", buff, bufc); return 0; } FUNCTION(fun_get) { dbref thing, aowner; int attrib, free_buffer, aflags; ATTR *attr; char *atr_gotten; struct boolexp *pBoolExp; if (!parse_attrib(player, fargs[0], &thing, &attrib)) { safe_str("#-1 NO MATCH", buff, bufc); return; } if (attrib == NOTHING) { return; } free_buffer = 1; // We need the attr's flags for this: // attr = atr_num(attrib); if (!attr) { return; } int nLen = 0; if (attr->flags & AF_IS_LOCK) { atr_gotten = atr_get_LEN(thing, attrib, &aowner, &aflags, &nLen); if (Read_attr(player, thing, attr, aowner, aflags)) { pBoolExp = parse_boolexp(player, atr_gotten, 1); free_lbuf(atr_gotten); atr_gotten = unparse_boolexp(player, pBoolExp); free_boolexp(pBoolExp); } else { free_lbuf(atr_gotten); atr_gotten = (char *)"#-1 PERMISSION DENIED"; } free_buffer = 0; } else { atr_gotten = atr_pget_LEN(thing, attrib, &aowner, &aflags, &nLen); } // Perform access checks. c_r_p fills buff with an error message // if needed. // if (check_read_perms(player, thing, attr, aowner, aflags, buff, bufc)) { if (free_buffer) { if (nLen) { safe_copy_buf(atr_gotten, nLen, buff, bufc, LBUF_SIZE-1); } } else { safe_str(atr_gotten, buff, bufc); } } if (free_buffer) { free_lbuf(atr_gotten); } } FUNCTION(fun_xget) { dbref thing, aowner; int attrib, aflags; ATTR *attr; char *atr_gotten; struct boolexp *pBoolExp; if (!*fargs[0] || !*fargs[1]) { return; } if (!parse_attrib(player, tprintf("%s/%s", fargs[0], fargs[1]), &thing, &attrib)) { safe_str("#-1 NO MATCH", buff, bufc); return; } if (attrib == NOTHING) { return; } // We need the attr's flags for this: attr = atr_num(attrib); if (!attr) { return; } int free_buffer = 1; int nLen = 0; if (attr->flags & AF_IS_LOCK) { atr_gotten = atr_get_LEN(thing, attrib, &aowner, &aflags, &nLen); if (Read_attr(player, thing, attr, aowner, aflags)) { pBoolExp = parse_boolexp(player, atr_gotten, 1); free_lbuf(atr_gotten); atr_gotten = unparse_boolexp(player, pBoolExp); free_boolexp(pBoolExp); } else { free_lbuf(atr_gotten); atr_gotten = (char *)"#-1 PERMISSION DENIED"; } free_buffer = 0; } else { atr_gotten = atr_pget_LEN(thing, attrib, &aowner, &aflags, &nLen); } // Perform access checks. c_r_p fills buff with an error message // if needed. // if (check_read_perms(player, thing, attr, aowner, aflags, buff, bufc)) { if (free_buffer) { safe_copy_buf(atr_gotten, nLen, buff, bufc, LBUF_SIZE-1); } else { safe_str(atr_gotten, buff, bufc); } } if (free_buffer) { free_lbuf(atr_gotten); } } FUNCTION(fun_get_eval) { dbref thing, aowner; int attrib, free_buffer, aflags, eval_it; ATTR *attr; char *atr_gotten, *str; struct boolexp *pBoolExp; if (!parse_attrib(player, fargs[0], &thing, &attrib)) { safe_str("#-1 NO MATCH", buff, bufc); return; } if (attrib == NOTHING) { return; } free_buffer = 1; eval_it = 1; attr = atr_num(attrib); /* * We need the attr's flags for this: */ if (!attr) { return; } if (attr->flags & AF_IS_LOCK) { atr_gotten = atr_get(thing, attrib, &aowner, &aflags); if (Read_attr(player, thing, attr, aowner, aflags)) { pBoolExp = parse_boolexp(player, atr_gotten, 1); free_lbuf(atr_gotten); atr_gotten = unparse_boolexp(player, pBoolExp); free_boolexp(pBoolExp); } else { free_lbuf(atr_gotten); atr_gotten = (char *)"#-1 PERMISSION DENIED"; } free_buffer = 0; eval_it = 0; } else { atr_gotten = atr_pget(thing, attrib, &aowner, &aflags); } if (!check_read_perms(player, thing, attr, aowner, aflags, buff, bufc)) { if (free_buffer) free_lbuf(atr_gotten); return; } if (eval_it) { str = atr_gotten; TinyExec(buff, bufc, 0, thing, player, EV_FIGNORE | EV_EVAL, &str, (char **)NULL, 0); } else { safe_str(atr_gotten, buff, bufc); } if (free_buffer) free_lbuf(atr_gotten); return; } FUNCTION(fun_subeval) { char *str; if (nfargs != 1) { safe_str("#-1 FUNCTION (EVALNOCOMP) EXPECTS 1 OR 2 ARGUMENTS", buff, bufc); return; } str = fargs[0]; TinyExec(buff, bufc, 0, player, cause, EV_EVAL|EV_NO_LOCATION|EV_NOFCHECK|EV_FIGNORE|EV_NO_COMPRESS, &str, (char **)NULL, 0); } FUNCTION(fun_eval) { dbref thing, aowner; int attrib, free_buffer, aflags, eval_it; ATTR *attr; char *atr_gotten, *str; struct boolexp *pBoolExp; if ((nfargs != 1) && (nfargs != 2)) { safe_str("#-1 FUNCTION (EVAL) EXPECTS 1 OR 2 ARGUMENTS", buff, bufc); return; } if (nfargs == 1) { str = fargs[0]; TinyExec(buff, bufc, 0, player, cause, EV_EVAL, &str, (char **)NULL, 0); return; } if (!*fargs[0] || !*fargs[1]) return; if (!parse_attrib(player, tprintf("%s/%s", fargs[0], fargs[1]), &thing, &attrib)) { safe_str("#-1 NO MATCH", buff, bufc); return; } if (attrib == NOTHING) { return; } free_buffer = 1; eval_it = 1; attr = atr_num(attrib); if (!attr) { return; } if (attr->flags & AF_IS_LOCK) { atr_gotten = atr_get(thing, attrib, &aowner, &aflags); if (Read_attr(player, thing, attr, aowner, aflags)) { pBoolExp = parse_boolexp(player, atr_gotten, 1); free_lbuf(atr_gotten); atr_gotten = unparse_boolexp(player, pBoolExp); free_boolexp(pBoolExp); } else { free_lbuf(atr_gotten); atr_gotten = (char *)"#-1 PERMISSION DENIED"; } free_buffer = 0; eval_it = 0; } else { atr_gotten = atr_pget(thing, attrib, &aowner, &aflags); } if (!check_read_perms(player, thing, attr, aowner, aflags, buff, bufc)) { if (free_buffer) free_lbuf(atr_gotten); return; } if (eval_it) { str = atr_gotten; TinyExec(buff, bufc, 0, thing, player, EV_FIGNORE | EV_EVAL, &str, (char **)NULL, 0); } else { safe_str(atr_gotten, buff, bufc); } if (free_buffer) free_lbuf(atr_gotten); return; } /* * --------------------------------------------------------------------------- * * fun_u and fun_ulocal: Call a user-defined function. */ static void do_ufun(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs, int is_local) { dbref aowner, thing; int aflags, anum; ATTR *ap; char *atext, *str; char *preserve[MAX_GLOBAL_REGS]; int preserve_len[MAX_GLOBAL_REGS]; // We need at least one argument. // if (nfargs < 1) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } // Two possibilities for the first arg: <obj>/<attr> and <attr>. // if (parse_attrib(player, fargs[0], &thing, &anum)) { if ((anum == NOTHING) || (!Good_obj(thing))) ap = NULL; else ap = atr_num(anum); } else { thing = player; ap = atr_str(fargs[0]); } /* * Make sure we got a good attribute */ if (!ap) { return; } /* * Use it if we can access it, otherwise return an error. */ atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { free_lbuf(atext); return; } if (!*atext) { free_lbuf(atext); return; } if (!check_read_perms(player, thing, ap, aowner, aflags, buff, bufc)) { free_lbuf(atext); return; } /* * If we're evaluating locally, preserve the global registers. */ if (is_local) { save_global_regs("fun_ulocal_save", preserve, preserve_len); } /* * Evaluate it using the rest of the passed function args */ str = atext; TinyExec(buff, bufc, 0, thing, cause, EV_FCHECK | EV_EVAL, &str, &(fargs[1]), nfargs - 1); free_lbuf(atext); /* * If we're evaluating locally, restore the preserved registers. */ if (is_local) { restore_global_regs("fun_ulocal_restore", preserve, preserve_len); } } FUNCTION(fun_u) { do_ufun(buff, bufc, player, cause, fargs, nfargs, cargs, ncargs, 0); } FUNCTION(fun_ulocal) { do_ufun(buff, bufc, player, cause, fargs, nfargs, cargs, ncargs, 1); } /* * --------------------------------------------------------------------------- * * fun_parent: Get parent of object. */ FUNCTION(fun_parent) { dbref it; it = match_thing(player, fargs[0]); if (Good_obj(it) && (Examinable(player, it) || (it == cause))) { safe_tprintf_str(buff, bufc, "#%d", Parent(it)); } else { safe_str("#-1", buff, bufc); } return; } /* * --------------------------------------------------------------------------- * * fun_parse: Make list from evaluating arg3 with each member of arg2. * * arg1 specifies a delimiter character to use in the parsing of arg2. * * NOTE: This function expects that its arguments have not been evaluated. */ FUNCTION(fun_parse) { char *curr, *objstring, *buff2, *buff3, *cp, sep; char *dp, *str; int first, number = 0; evarargs_preamble("PARSE", 3); cp = curr = dp = alloc_lbuf("fun_parse"); str = fargs[0]; TinyExec(curr, &dp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *dp = '\0'; cp = trim_space_sep(cp, sep); if (!*cp) { free_lbuf(curr); return; } first = 1; while (cp) { if (!first) safe_chr(' ', buff, bufc); first = 0; number++; objstring = split_token(&cp, sep); buff2 = replace_string(BOUND_VAR, objstring, fargs[1]); buff3 = replace_string(LISTPLACE_VAR, Tiny_ltoa_t(number), buff2); str = buff3; TinyExec(buff, bufc, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); free_lbuf(buff2); free_lbuf(buff3); } free_lbuf(curr); } /* * --------------------------------------------------------------------------- * * fun_mid: mid(foobar,2,3) returns oba */ FUNCTION(fun_mid) { // Initial checks for iPosition0 [0,LBUF_SIZE), nLength [0,LBUF_SIZE), // and iPosition1 [0,LBUF_SIZE). // int iPosition0 = Tiny_atol(fargs[1]); int nLength = Tiny_atol(fargs[2]); int iPosition1 = iPosition0 + nLength; if ( iPosition0 < 0 || iPosition0 > LBUF_SIZE-1 || nLength < 0 || nLength > LBUF_SIZE-1 || iPosition1 > LBUF_SIZE-1) { safe_str("#-1 OUT OF RANGE", buff, bufc); return; } // At this point, iPosition0, nLength, and iPosition1 are reasonable // numbers which may -still- not refer to valid data in the string. // struct ANSI_Context ac; ANSI_String_Init(&ac, fargs[0], 0); int nDone; ANSI_String_Skip(&ac, iPosition0, &nDone); if (nDone < iPosition0) { return; } int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; int nSize = ANSI_String_Copy(&ac, nBufferAvailable, *bufc, nLength, &nDone); *bufc += nSize; } /* * --------------------------------------------------------------------------- * * fun_first: Returns first word in a string */ FUNCTION(fun_first) { char *s, *first, sep; /* * If we are passed an empty arglist return a null string */ if (nfargs == 0) { return; } varargs_preamble("FIRST", 2); s = trim_space_sep(fargs[0], sep); /* * leading spaces ... */ first = split_token(&s, sep); if (first) { safe_str(first, buff, bufc); } } /* * --------------------------------------------------------------------------- * * fun_rest: Returns all but the first word in a string */ FUNCTION(fun_rest) { char *s, *first, sep; /* * If we are passed an empty arglist return a null string */ if (nfargs == 0) { return; } varargs_preamble("REST", 2); s = trim_space_sep(fargs[0], sep); /* * leading spaces ... */ first = split_token(&s, sep); if (s) { safe_str(s, buff, bufc); } } /* * --------------------------------------------------------------------------- * * fun_v: Function form of %-substitution */ FUNCTION(fun_v) { dbref aowner; int aflags; char *sbuf, *sbufc, *tbuf, *str; ATTR *ap; tbuf = fargs[0]; if (Tiny_IsAlpha[(unsigned char)tbuf[0]] && tbuf[1]) { // Fetch an attribute from me. First see if it exists, // returning a null string if it does not. // ap = atr_str(fargs[0]); if (!ap) { return; } // If we can access it, return it, otherwise return a null // string. // int nLen; tbuf = atr_pget_LEN(player, ap->number, &aowner, &aflags, &nLen); if (See_attr(player, player, ap, aowner, aflags)) { safe_copy_buf(tbuf, nLen, buff, bufc, LBUF_SIZE-1); } free_lbuf(tbuf); return; } // Not an attribute, process as %<arg> // sbuf = alloc_sbuf("fun_v"); sbufc = sbuf; safe_sb_chr('%', sbuf, &sbufc); safe_sb_str(fargs[0], sbuf, &sbufc); *sbufc = '\0'; str = sbuf; TinyExec(buff, bufc, 0, player, cause, EV_EVAL|EV_FIGNORE, &str, cargs, ncargs); free_sbuf(sbuf); } /* * --------------------------------------------------------------------------- * * fun_s: Force substitution to occur. */ FUNCTION(fun_s) { char *str; str = fargs[0]; TinyExec(buff, bufc, 0, player, cause, EV_FIGNORE | EV_EVAL, &str, cargs, ncargs); } /* * --------------------------------------------------------------------------- * * fun_con: Returns first item in contents list of object/room */ FUNCTION(fun_con) { dbref it; it = match_thing(player, fargs[0]); if ((it != NOTHING) && (Has_contents(it)) && (Examinable(player, it) || (where_is(player) == it) || (it == cause))) { safe_tprintf_str(buff, bufc, "#%d", Contents(it)); return; } safe_str("#-1", buff, bufc); return; } /* * --------------------------------------------------------------------------- * * fun_exit: Returns first exit in exits list of room. */ FUNCTION(fun_exit) { dbref it, exit; int key; it = match_thing(player, fargs[0]); if (Good_obj(it) && Has_exits(it) && Good_obj(Exits(it))) { key = 0; if (Examinable(player, it)) key |= VE_LOC_XAM; if (Dark(it)) key |= VE_LOC_DARK; DOLIST(exit, Exits(it)) { if (exit_visible(exit, player, key)) { safe_tprintf_str(buff, bufc, "#%d", exit); return; } } } safe_str("#-1", buff, bufc); return; } /* * --------------------------------------------------------------------------- * * fun_next: return next thing in contents or exits chain */ FUNCTION(fun_next) { dbref it, loc, exit, ex_here; int key; it = match_thing(player, fargs[0]); if (Good_obj(it) && Has_siblings(it)) { loc = where_is(it); ex_here = Good_obj(loc) ? Examinable(player, loc) : 0; if (ex_here || (loc == player) || (loc == where_is(player))) { if (!isExit(it)) { safe_tprintf_str(buff, bufc, "#%d", Next(it)); return; } else { key = 0; if (ex_here) key |= VE_LOC_XAM; if (Dark(loc)) key |= VE_LOC_DARK; DOLIST(exit, it) { if ((exit != it) && exit_visible(exit, player, key)) { safe_tprintf_str(buff, bufc, "#%d", exit); return; } } } } } safe_str("#-1", buff, bufc); return; } /* * --------------------------------------------------------------------------- * * fun_loc: Returns the location of something */ FUNCTION(fun_loc) { dbref it; it = match_thing(player, fargs[0]); if (locatable(player, it, cause)) safe_tprintf_str(buff, bufc, "#%d", Location(it)); else safe_str("#-1", buff, bufc); return; } /* * --------------------------------------------------------------------------- * * fun_where: Returns the "true" location of something */ FUNCTION(fun_where) { dbref it; it = match_thing(player, fargs[0]); if (locatable(player, it, cause)) safe_tprintf_str(buff, bufc, "#%d", where_is(it)); else safe_str("#-1", buff, bufc); return; } /* * --------------------------------------------------------------------------- * * fun_rloc: Returns the recursed location of something (specifying #levels) */ FUNCTION(fun_rloc) { int i, levels; dbref it; levels = Tiny_atol(fargs[1]); if (levels > mudconf.ntfy_nest_lim) levels = mudconf.ntfy_nest_lim; it = match_thing(player, fargs[0]); if (locatable(player, it, cause)) { for (i = 0; i < levels; i++) { if (!Good_obj(it) || !Has_location(it)) break; it = Location(it); } safe_tprintf_str(buff, bufc, "#%d", it); return; } safe_str("#-1", buff, bufc); } /* * --------------------------------------------------------------------------- * * fun_room: Find the room an object is ultimately in. */ FUNCTION(fun_room) { dbref it; int count; it = match_thing(player, fargs[0]); if (locatable(player, it, cause)) { for (count = mudconf.ntfy_nest_lim; count > 0; count--) { it = Location(it); if (!Good_obj(it)) break; if (isRoom(it)) { safe_tprintf_str(buff, bufc, "#%d", it); return; } } safe_str("#-1", buff, bufc); } else if (isRoom(it)) { safe_tprintf_str(buff, bufc, "#%d", it); } else { safe_str("#-1", buff, bufc); } return; } /* * --------------------------------------------------------------------------- * * fun_owner: Return the owner of an object. */ FUNCTION(fun_owner) { dbref it, aowner; int atr, aflags; if (parse_attrib(player, fargs[0], &it, &atr)) { if (atr == NOTHING) { it = NOTHING; } else { atr_pget_info(it, atr, &aowner, &aflags); it = aowner; } } else { it = match_thing(player, fargs[0]); if (it != NOTHING) it = Owner(it); } safe_tprintf_str(buff, bufc, "#%d", it); } /* * --------------------------------------------------------------------------- * * fun_controls: Does x control y? */ FUNCTION(fun_controls) { dbref x, y; x = match_thing(player, fargs[0]); if (x == NOTHING) { safe_tprintf_str(buff, bufc, "%s", "#-1 ARG1 NOT FOUND"); return; } y = match_thing(player, fargs[1]); if (y == NOTHING) { safe_tprintf_str(buff, bufc, "%s", "#-1 ARG2 NOT FOUND"); return; } safe_ltoa(Controls(x, y), buff, bufc, LBUF_SIZE-1); } /* * --------------------------------------------------------------------------- * * fun_fullname: Return the fullname of an object (good for exits) */ FUNCTION(fun_fullname) { dbref it; it = match_thing(player, fargs[0]); if (it == NOTHING) { return; } if (!mudconf.read_rem_name) { if (!nearby_or_control(player, it) && (!isPlayer(it))) { safe_str("#-1 TOO FAR AWAY TO SEE", buff, bufc); return; } } safe_str(Name(it), buff, bufc); } /* * --------------------------------------------------------------------------- * * fun_name: Return the name of an object */ FUNCTION(fun_name) { dbref it; char *s, *temp; it = match_thing(player, fargs[0]); if (it == NOTHING) { return; } if (!mudconf.read_rem_name) { if (!nearby_or_control(player, it) && !isPlayer(it) && !Long_Fingers(player)) { safe_str("#-1 TOO FAR AWAY TO SEE", buff, bufc); return; } } temp = *bufc; safe_str(Name(it), buff, bufc); if (isExit(it)) { for (s = temp; (s != *bufc) && (*s != ';'); s++) { // Do nothing // ; } if (*s == ';') *bufc = s; } } /* * --------------------------------------------------------------------------- * * fun_match, fun_strmatch: Match arg2 against each word of arg1 returning * * index of first match, or against the whole string. */ FUNCTION(fun_match) { int wcount; char *r, *s, sep; varargs_preamble("MATCH", 3); /* * Check each word individually, returning the word number of the * * * * * * * first one that matches. If none match, return 0. */ wcount = 1; s = trim_space_sep(fargs[0], sep); do { r = split_token(&s, sep); if (quick_wild(fargs[1], r)) { safe_ltoa(wcount, buff, bufc, LBUF_SIZE-1); return; } wcount++; } while (s); safe_chr('0', buff, bufc); } FUNCTION(fun_strmatch) { // Check if we match the whole string. If so, return 1. // int cc = quick_wild(fargs[1], fargs[0]); safe_chr(cc ? '1' : '0', buff, bufc); } /* * --------------------------------------------------------------------------- * * fun_extract: extract words from string: * * extract(foo bar baz,1,2) returns 'foo bar' * * extract(foo bar baz,2,1) returns 'bar' * * extract(foo bar baz,2,2) returns 'bar baz' * * * * Now takes optional separator extract(foo-bar-baz,1,2,-) returns 'foo-bar' */ FUNCTION(fun_extract) { int start, len; char *r, *s, *t, sep; varargs_preamble("EXTRACT", 4); s = fargs[0]; start = Tiny_atol(fargs[1]); len = Tiny_atol(fargs[2]); if ((start < 1) || (len < 1)) { return; } /* * Skip to the start of the string to save */ start--; s = trim_space_sep(s, sep); while (start && s) { s = next_token(s, sep); start--; } /* * If we ran of the end of the string, return nothing */ if (!s || !*s) { return; } /* * Count off the words in the string to save */ r = s; len--; while (len && s) { s = next_token(s, sep); len--; } /* * Chop off the rest of the string, if needed */ if (s && *s) t = split_token(&s, sep); safe_str(r, buff, bufc); } int xlate(char *arg) { int temp; char *temp2; if (arg[0] == '#') { arg++; if (is_integer(arg, 0)) { temp = Tiny_atol(arg); if (temp == -1) temp = 0; return temp; } return 0; } temp2 = trim_space_sep(arg, ' '); if (!*temp2) return 0; if (is_integer(temp2, 0)) return Tiny_atol(temp2); return 1; } /* * --------------------------------------------------------------------------- * * fun_index: like extract(), but it works with an arbitrary separator. * * index(a b | c d e | f gh | ij k, |, 2, 1) => c d e * * index(a b | c d e | f gh | ij k, |, 2, 2) => c d e | f g h */ FUNCTION(fun_index) { int start, end; char c, *s, *p; s = fargs[0]; c = *fargs[1]; start = Tiny_atol(fargs[2]); end = Tiny_atol(fargs[3]); if ((start < 1) || (end < 1) || (*s == '\0')) return; if (c == '\0') c = ' '; /* * move s to point to the start of the item we want */ start--; while (start && s && *s) { if ((s = (char *)strchr(s, c)) != NULL) s++; start--; } /* * skip over just spaces */ while (s && (*s == ' ')) s++; if (!s || !*s) return; /* * figure out where to end the string */ p = s; while (end && p && *p) { if ((p = (char *)strchr(p, c)) != NULL) { if (--end == 0) { do { p--; } while ((*p == ' ') && (p > s)); *(++p) = '\0'; safe_str(s, buff, bufc); return; } else { p++; } } } /* * if we've gotten this far, we've run off the end of the string */ safe_str(s, buff, bufc); } FUNCTION(fun_cat) { int i; safe_str(fargs[0], buff, bufc); for (i = 1; i < nfargs; i++) { safe_chr(' ', buff, bufc); safe_str(fargs[i], buff, bufc); } } FUNCTION(fun_version) { safe_str(mudstate.version, buff, bufc); } FUNCTION(fun_strlen) { safe_ltoa(strlen(strip_ansi(fargs[0])), buff, bufc, LBUF_SIZE-1); } FUNCTION(fun_num) { safe_tprintf_str(buff, bufc, "#%d", match_thing(player, fargs[0])); } FUNCTION(fun_pmatch) { if (*fargs[0] == '#') { safe_tprintf_str(buff, bufc, "#%d", match_thing(player, fargs[0])); return; } dbref thing = lookup_player(player, fargs[0], 1); if (thing != NOTHING) { safe_tprintf_str(buff, bufc, "#%d", thing); } else { safe_str("#-1 NO MATCH", buff, bufc); } } FUNCTION(fun_gt) { int ch = '0'; int nDigits0, nDigits1; if ( ( is_integer(fargs[0], &nDigits0) && nDigits0 <= 9 && is_integer(fargs[1], &nDigits1) && nDigits1 <= 9 && Tiny_atol(fargs[0]) > Tiny_atol(fargs[1])) || safe_atof(fargs[0]) > safe_atof(fargs[1])) { ch = '1'; } safe_chr(ch, buff, bufc); } FUNCTION(fun_gte) { int ch = '0'; int nDigits0, nDigits1; if ( ( is_integer(fargs[0], &nDigits0) && nDigits0 <= 9 && is_integer(fargs[1], &nDigits1) && nDigits1 <= 9 && Tiny_atol(fargs[0]) >= Tiny_atol(fargs[1])) || safe_atof(fargs[0]) >= safe_atof(fargs[1])) { ch = '1'; } safe_chr(ch, buff, bufc); } FUNCTION(fun_lt) { int ch = '0'; int nDigits0, nDigits1; if ( ( is_integer(fargs[0], &nDigits0) && nDigits0 <= 9 && is_integer(fargs[1], &nDigits1) && nDigits1 <= 9 && Tiny_atol(fargs[0]) < Tiny_atol(fargs[1])) || safe_atof(fargs[0]) < safe_atof(fargs[1])) { ch = '1'; } safe_chr(ch, buff, bufc); } FUNCTION(fun_lte) { int ch = '0'; int nDigits0, nDigits1; if ( ( is_integer(fargs[0], &nDigits0) && nDigits0 <= 9 && is_integer(fargs[1], &nDigits1) && nDigits1 <= 9 && Tiny_atol(fargs[0]) <= Tiny_atol(fargs[1])) || safe_atof(fargs[0]) <= safe_atof(fargs[1])) { ch = '1'; } safe_chr(ch, buff, bufc); } FUNCTION(fun_eq) { int ch = '0'; int nDigits0, nDigits1; if ( ( is_integer(fargs[0], &nDigits0) && nDigits0 <= 9 && is_integer(fargs[1], &nDigits1) && nDigits1 <= 9 && Tiny_atol(fargs[0]) == Tiny_atol(fargs[1])) || safe_atof(fargs[0]) == safe_atof(fargs[1])) { ch = '1'; } safe_chr(ch, buff, bufc); } FUNCTION(fun_neq) { int ch = '0'; int nDigits0, nDigits1; if ( ( is_integer(fargs[0], &nDigits0) && nDigits0 <= 9 && is_integer(fargs[1], &nDigits1) && nDigits1 <= 9 && Tiny_atol(fargs[0]) != Tiny_atol(fargs[1])) || safe_atof(fargs[0]) != safe_atof(fargs[1])) { ch = '1'; } safe_chr(ch, buff, bufc); } FUNCTION(fun_and) { if (nfargs <= 0) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } int val = TRUE; for (int i = 0; i < nfargs; i++) { val = val && Tiny_atol(fargs[i]); } safe_chr(val ? '1' : '0', buff, bufc); } FUNCTION(fun_or) { if (nfargs <= 0) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } int val = FALSE; for (int i = 0; i < nfargs; i++) { val = val || Tiny_atol(fargs[i]); } safe_chr(val ? '1' : '0', buff, bufc); } FUNCTION(fun_xor) { if (nfargs <= 0) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } int val = FALSE; for (int i = 0; i < nfargs; i++) { int tval = Tiny_atol(fargs[i]); val = (val && !tval) || (!val && tval); } safe_chr(val ? '1' : '0', buff, bufc); } FUNCTION(fun_not) { safe_ltoa(!xlate(fargs[0]), buff, bufc, LBUF_SIZE-1); } FUNCTION(fun_sqrt) { double val; val = safe_atof(fargs[0]); #ifndef HAVE_IEEE_FP_SNAN if (val < 0.0) { safe_str("Ind", buff, bufc); } else if (val == 0.0) { safe_chr('0', buff, bufc); } else { fval(buff, bufc, sqrt(val)); } #else fval(buff, bufc, sqrt(val)); #endif } FUNCTION(fun_add) { if (nfargs <= 0) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } double sum = 0.0; for (int i = 0; i < nfargs; i++) { sum += safe_atof(fargs[i]); } fval(buff, bufc, sum); } FUNCTION(fun_sub) { fval(buff, bufc, safe_atof(fargs[0]) - safe_atof(fargs[1])); } FUNCTION(fun_mul) { if (nfargs <= 0) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } double prod = 1.0; for (int i = 0; i < nfargs; i++) { prod *= safe_atof(fargs[i]); } fval(buff, bufc, prod); } FUNCTION(fun_floor) { double r = floor(safe_atof(fargs[0])); #ifdef HAVE_IEEE_FP_FORMAT int fpc = Tiny_fpclass(r); if (TINY_FPGROUP(fpc) == TINY_FPGROUP_PASS) { #endif // HAVE_IEEE_FP_FORMAT safe_tprintf_str(buff, bufc, "%.0f", r); #ifdef HAVE_IEEE_FP_FORMAT } else { safe_str(TinyFPStrings[TINY_FPCLASS(fpc)], buff, bufc); } #endif // HAVE_IEEE_FP_FORMAT } FUNCTION(fun_ceil) { double r = ceil(safe_atof(fargs[0])); #ifdef HAVE_IEEE_FP_FORMAT int fpc = Tiny_fpclass(r); if (TINY_FPGROUP(fpc) == TINY_FPGROUP_PASS) { #endif // HAVE_IEEE_FP_FORMAT safe_tprintf_str(buff, bufc, "%.0f", r); #ifdef HAVE_IEEE_FP_FORMAT } else { safe_str(TinyFPStrings[TINY_FPCLASS(fpc)], buff, bufc); } #endif // HAVE_IEEE_FP_FORMAT } FUNCTION(fun_round) { const char *fstr; char *oldp; oldp = *bufc; switch (Tiny_atol(fargs[1])) { case 1: fstr = "%.1f"; break; case 2: fstr = "%.2f"; break; case 3: fstr = "%.3f"; break; case 4: fstr = "%.4f"; break; case 5: fstr = "%.5f"; break; case 6: fstr = "%.6f"; break; default: fstr = "%.0f"; break; } double r = safe_atof(fargs[0]); #ifdef HAVE_IEEE_FP_FORMAT int fpc = Tiny_fpclass(r); if (TINY_FPGROUP(fpc) == TINY_FPGROUP_PASS) { #endif // HAVE_IEEE_FP_FORMAT safe_tprintf_str(buff, bufc, (char *)fstr, r); #ifdef HAVE_IEEE_FP_FORMAT } else { safe_str(TinyFPStrings[TINY_FPCLASS(fpc)], buff, bufc); } #endif // HAVE_IEEE_FP_FORMAT } FUNCTION(fun_trunc) { double rArg = safe_atof(fargs[0]); double rIntegerPart; double rFractionalPart; rFractionalPart = modf(rArg, &rIntegerPart); #ifdef HAVE_IEEE_FP_FORMAT int fpc = Tiny_fpclass(rIntegerPart); if (TINY_FPGROUP(fpc) == TINY_FPGROUP_PASS) { #endif // HAVE_IEEE_FP_FORMAT safe_tprintf_str(buff, bufc, "%.0f", rIntegerPart); #ifdef HAVE_IEEE_FP_FORMAT } else { safe_str(TinyFPStrings[TINY_FPCLASS(fpc)], buff, bufc); } #endif // HAVE_IEEE_FP_FORMAT } FUNCTION(fun_div) { INT64 bot, top; top = Tiny_atoi64(fargs[0]); bot = Tiny_atoi64(fargs[1]); if (bot == 0) { safe_str("#-1 DIVIDE BY ZERO", buff, bufc); } else { safe_str(Tiny_i64toa_t(top/bot), buff, bufc); } } FUNCTION(fun_fdiv) { double bot = safe_atof(fargs[1]); double top = safe_atof(fargs[0]); #ifndef HAVE_IEEE_FP_SNAN if (bot == 0.0) { if (top > 0.0) { safe_str("+Inf", buff, bufc); } else if (top < 0.0) { safe_str("-Inf", buff, bufc); } else { safe_str("Ind", buff, bufc); } } else { fval(buff, bufc, top/bot); } #else fval(buff, bufc, top/bot); #endif } FUNCTION(fun_mod) { INT64 bot, top; top = Tiny_atoi64(fargs[0]); bot = Tiny_atoi64(fargs[1]); if (bot == 0) { bot = 1; } safe_str(Tiny_i64toa_t(top%bot), buff, bufc); } FUNCTION(fun_pi) { safe_str("3.141592654", buff, bufc); } FUNCTION(fun_e) { safe_str("2.718281828", buff, bufc); } FUNCTION(fun_sin) { fval(buff, bufc, sin(safe_atof(fargs[0]))); } FUNCTION(fun_cos) { fval(buff, bufc, cos(safe_atof(fargs[0]))); } FUNCTION(fun_tan) { fval(buff, bufc, tan(safe_atof(fargs[0]))); } FUNCTION(fun_exp) { fval(buff, bufc, exp(safe_atof(fargs[0]))); } FUNCTION(fun_power) { double val1, val2; val1 = safe_atof(fargs[0]); val2 = safe_atof(fargs[1]); #ifndef HAVE_IEEE_FP_SNAN if (val1 < 0.0) { safe_str("Ind", buff, bufc); } else { fval(buff, bufc, pow(val1, val2)); } #else fval(buff, bufc, pow(val1, val2)); #endif } FUNCTION(fun_ln) { double val; val = safe_atof(fargs[0]); #ifndef HAVE_IEEE_FP_SNAN if (val < 0.0) { safe_str("Ind", buff, bufc); } else if (val == 0.0) { safe_str("-Inf", buff, bufc); } else { fval(buff, bufc, log(val)); } #else fval(buff, bufc, log(val)); #endif } FUNCTION(fun_log) { double val; val = safe_atof(fargs[0]); #ifndef HAVE_IEEE_FP_SNAN if (val < 0.0) { safe_str("Ind", buff, bufc); } else if (val == 0.0) { safe_str("-Inf", buff, bufc); } else { fval(buff, bufc, log10(val)); } #else fval(buff, bufc, log10(val)); #endif } FUNCTION(fun_asin) { double val; val = safe_atof(fargs[0]); #ifndef HAVE_IEEE_FP_SNAN if ((val < -1.0) || (val > 1.0)) { safe_str("Ind", buff, bufc); } else { fval(buff, bufc, asin(val)); } #else fval(buff, bufc, asin(val)); #endif } FUNCTION(fun_acos) { double val; val = safe_atof(fargs[0]); #ifndef HAVE_IEEE_FP_SNAN if ((val < -1.0) || (val > 1.0)) { safe_str("Ind", buff, bufc); } else { fval(buff, bufc, acos(val)); } #else fval(buff, bufc, acos(val)); #endif } FUNCTION(fun_atan) { fval(buff, bufc, atan(safe_atof(fargs[0]))); } FUNCTION(fun_dist2d) { int d; double r; d = Tiny_atol(fargs[0]) - Tiny_atol(fargs[2]); r = (double)(d * d); d = Tiny_atol(fargs[1]) - Tiny_atol(fargs[3]); r += (double)(d * d); d = (int)(sqrt(r) + 0.5); safe_ltoa(d, buff, bufc, LBUF_SIZE-1); } FUNCTION(fun_dist3d) { int d; double r; d = Tiny_atol(fargs[0]) - Tiny_atol(fargs[3]); r = (double)(d * d); d = Tiny_atol(fargs[1]) - Tiny_atol(fargs[4]); r += (double)(d * d); d = Tiny_atol(fargs[2]) - Tiny_atol(fargs[5]); r += (double)(d * d); d = (int)(sqrt(r) + 0.5); safe_ltoa(d, buff, bufc, LBUF_SIZE-1); } //------------------------------------------------------------------------ // Vector functions: VADD, VSUB, VMUL, VCROSS, VMAG, VUNIT, VDIM // Vectors are space-separated numbers. // #define MAXDIM 20 #define VADD_F 0 #define VSUB_F 1 #define VMUL_F 2 #define VDOT_F 3 #define VCROSS_F 4 static void handle_vectors ( char *vecarg1, char *vecarg2, char *buff, char **bufc, char sep, char osep, int flag ) { char *v1[(LBUF_SIZE+1)/2], *v2[(LBUF_SIZE+1)/2]; char vres[MAXDIM][LBUF_SIZE]; double scalar; int n, m, i; // Split the list up, or return if the list is empty. // if (!vecarg1 || !*vecarg1 || !vecarg2 || !*vecarg2) { return; } n = list2arr(v1, (LBUF_SIZE+1)/2, vecarg1, sep); m = list2arr(v2, (LBUF_SIZE+1)/2, vecarg2, sep); // It's okay to have vmul() be passed a scalar first or second arg, // but everything else has to be same-dimensional. // if ( (n != m) && ( (flag != VMUL_F) || ((n != 1) && (m != 1)) ) ) { safe_str("#-1 VECTORS MUST BE SAME DIMENSIONS", buff, bufc); return; } if (n > MAXDIM) { safe_str("#-1 TOO MANY DIMENSIONS ON VECTORS", buff, bufc); return; } switch (flag) { case VADD_F: for (i = 0; i < n; i++) { fval_buf(vres[i], safe_atof(v1[i]) + safe_atof(v2[i])); v1[i] = (char *) vres[i]; } arr2list(v1, n, buff, bufc, osep); break; case VSUB_F: for (i = 0; i < n; i++) { fval_buf(vres[i], safe_atof(v1[i]) - safe_atof(v2[i])); v1[i] = (char *) vres[i]; } arr2list(v1, n, buff, bufc, osep); break; case VMUL_F: // If n or m is 1, this is scalar multiplication. // otherwise, multiply elementwise. // if (n == 1) { scalar = safe_atof(v1[0]); for (i = 0; i < m; i++) { fval_buf(vres[i], safe_atof(v2[i]) * scalar); v1[i] = (char *) vres[i]; } n = m; } else if (m == 1) { scalar = safe_atof(v2[0]); for (i = 0; i < n; i++) { fval_buf(vres[i], safe_atof(v1[i]) * scalar); v1[i] = (char *) vres[i]; } } else { // Vector elementwise product. // // Note this is a departure from TinyMUX 1.6, but an // imitation of the PennMUSH and MUSH 3.0 behavior: // the documentation in Penn claims it's a dot product, // but the actual behavior isn't. We implement dot // product separately! // for (i = 0; i < n; i++) { fval_buf(vres[i], safe_atof(v1[i]) * safe_atof(v2[i])); v1[i] = (char *) vres[i]; } } arr2list(v1, n, buff, bufc, osep); break; case VDOT_F: scalar = 0.0; for (i = 0; i < n; i++) { scalar += safe_atof(v1[i]) * safe_atof(v2[i]); } fval(buff, bufc, scalar); break; case VCROSS_F: // cross product: (a,b,c) x (d,e,f) = (bf - ce, cd - af, ae - bd) // // Or in other words: // // | a b c | // det | d e f | = i(bf-ce) + j(cd-af) + k(ae-bd) // | i j k | // // where i, j, and k are unit vectors in the x, y, and z // cartisian coordinate space and are understood when expressed // in vector form. // if (n != 3) { safe_str("#-1 VECTORS MUST BE DIMENSION OF 3", buff, bufc); } else { double a[2][3]; for (i = 0; i < 3; i++) { a[0][i] = safe_atof(v1[i]); a[1][i] = safe_atof(v2[i]); v1[i] = (char *) vres[i]; } fval_buf(vres[0], (a[0][1] * a[1][2]) - (a[0][2] * a[1][1])); fval_buf(vres[1], (a[0][2] * a[1][0]) - (a[0][0] * a[1][2])); fval_buf(vres[2], (a[0][0] * a[1][1]) - (a[0][1] * a[1][0])); arr2list(v1, n, buff, bufc, osep); } break; default: // If we reached this, we're in trouble. // safe_str("#-1 UNIMPLEMENTED", buff, bufc); } } FUNCTION(fun_vadd) { char sep, osep; svarargs_preamble("VADD", 4); handle_vectors(fargs[0], fargs[1], buff, bufc, sep, osep, VADD_F); } FUNCTION(fun_vsub) { char sep, osep; svarargs_preamble("VSUB", 4); handle_vectors(fargs[0], fargs[1], buff, bufc, sep, osep, VSUB_F); } FUNCTION(fun_vmul) { char sep, osep; svarargs_preamble("VMUL", 4); handle_vectors(fargs[0], fargs[1], buff, bufc, sep, osep, VMUL_F); } FUNCTION(fun_vdot) { // dot product: (a,b,c) . (d,e,f) = ad + be + cf // char sep, osep; svarargs_preamble("VDOT", 4); handle_vectors(fargs[0], fargs[1], buff, bufc, sep, osep, VDOT_F); } FUNCTION(fun_vcross) { // cross product: (a,b,c) x (d,e,f) = (bf - ce, cd - af, ae - bd) // char sep, osep; svarargs_preamble("VCROSS", 4); handle_vectors(fargs[0], fargs[1], buff, bufc, sep, osep, VCROSS_F); } FUNCTION(fun_vmag) { char *v1[LBUF_SIZE]; int n, i; double tmp, res = 0.0; char sep; varargs_preamble("VMAG", 2); // Split the list up, or return if the list is empty. // if (!fargs[0] || !*fargs[0]) { return; } n = list2arr(v1, LBUF_SIZE, fargs[0], sep); if (n > MAXDIM) { safe_str("#-1 TOO MANY DIMENSIONS ON VECTORS", buff, bufc); return; } // Calculate the magnitude. // for (i = 0; i < n; i++) { tmp = safe_atof(v1[i]); res += tmp * tmp; } if (res > 0) { fval(buff, bufc, sqrt(res)); } else { safe_chr('0', buff, bufc); } } FUNCTION(fun_vunit) { char *v1[LBUF_SIZE]; char vres[MAXDIM][LBUF_SIZE]; int n, i; double tmp, res = 0.0; char sep; varargs_preamble("VUNIT", 2); // Split the list up, or return if the list is empty. // if (!fargs[0] || !*fargs[0]) { return; } n = list2arr(v1, LBUF_SIZE, fargs[0], sep); if (n > MAXDIM) { safe_str("#-1 TOO MANY DIMENSIONS ON VECTORS", buff, bufc); return; } // Calculate the magnitude. // for (i = 0; i < n; i++) { tmp = safe_atof(v1[i]); res += tmp * tmp; } if (res <= 0) { safe_str("#-1 CAN'T MAKE UNIT VECTOR FROM ZERO-LENGTH VECTOR", buff, bufc); return; } for (i = 0; i < n; i++) { fval_buf(vres[i], safe_atof(v1[i]) / sqrt(res)); v1[i] = (char *) vres[i]; } arr2list(v1, n, buff, bufc, sep); } FUNCTION(fun_vdim) { char sep; if (fargs == 0) { safe_chr('0', buff, bufc); } else { varargs_preamble("VDIM", 2); safe_ltoa(countwords(fargs[0],sep), buff, bufc, LBUF_SIZE-1); } } /* * --------------------------------------------------------------------------- * * fun_comp: string compare. */ FUNCTION(fun_comp) { int x; x = strcmp(fargs[0], fargs[1]); if (x < 0) { safe_str("-1", buff, bufc); } else { safe_chr((x == 0) ? '0' : '1', buff, bufc); } } #ifdef WOD_REALMS FUNCTION(fun_cansee) { if (nfargs < 2 || nfargs > 3) { safe_str("#-1", buff, bufc); return; } dbref looker, lookee; int mode; looker = match_thing(player, fargs[0]); if (looker != NOTHING) { lookee = match_thing(player, fargs[1]); if (lookee != NOTHING) { if (nfargs == 3) { mode = Tiny_atol(fargs[2]); switch (mode) { case ACTION_IS_STATIONARY: case ACTION_IS_MOVING: case ACTION_IS_TALKING: break; default: mode = ACTION_IS_STATIONARY; break; } } else { mode = ACTION_IS_STATIONARY; } // Do it. // int Realm_Do = DoThingToThingVisibility(looker, lookee, mode); int ch = '0'; if ((Realm_Do & REALM_DO_MASK) != REALM_DO_HIDDEN_FROM_YOU) { if (!Dark(lookee)) { ch = '1'; } } safe_chr(ch, buff, bufc); return; } } safe_str("#-1", buff, bufc); } #endif /* * --------------------------------------------------------------------------- * * fun_lcon: Return a list of contents. */ FUNCTION(fun_lcon) { dbref thing, it; char *tbuf; int first = 1; it = match_thing(player, fargs[0]); if ((it != NOTHING) && (Has_contents(it)) && (Examinable(player, it) || (Location(player) == it) || (it == cause))) { tbuf = alloc_sbuf("fun_lcon"); DOLIST(thing, Contents(it)) { #ifdef WOD_REALMS if (REALM_DO_HIDDEN_FROM_YOU != DoThingToThingVisibility(player, thing, ACTION_IS_STATIONARY)) { #endif if (!first) { tbuf[0] = ' '; tbuf[1] = '#'; Tiny_ltoa(thing, tbuf+2); } else { tbuf[0] = '#'; Tiny_ltoa(thing, tbuf+1); first = 0; } safe_str(tbuf, buff, bufc); #ifdef WOD_REALMS } #endif } free_sbuf(tbuf); } else { safe_str("#-1", buff, bufc); } } /* * --------------------------------------------------------------------------- * * fun_lexits: Return a list of exits. */ FUNCTION(fun_lexits) { dbref thing, it, parent; char *tbuf; int exam, lev, key; int first = 1; it = match_thing(player, fargs[0]); if (!Good_obj(it) || !Has_exits(it)) { safe_str("#-1", buff, bufc); return; } exam = Examinable(player, it); if (!exam && (where_is(player) != it) && (it != cause)) { safe_str("#-1", buff, bufc); return; } tbuf = alloc_sbuf("fun_lexits"); /* * Return info for all parent levels */ ITER_PARENTS(it, parent, lev) { /* * Look for exits at each level */ if (!Has_exits(parent)) continue; key = 0; if (Examinable(player, parent)) key |= VE_LOC_XAM; if (Dark(parent)) key |= VE_LOC_DARK; if (Dark(it)) key |= VE_BASE_DARK; DOLIST(thing, Exits(parent)) { if (exit_visible(thing, player, key)) { if (!first) sprintf(tbuf, " #%d", thing); else { sprintf(tbuf, "#%d", thing); first = 0; } safe_str(tbuf, buff, bufc); } } } free_sbuf(tbuf); return; } /* * -------------------------------------------------------------------------- * * fun_home: Return an object's home */ FUNCTION(fun_home) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it) || !Examinable(player, it)) safe_str("#-1", buff, bufc); else if (Has_home(it)) safe_tprintf_str(buff, bufc, "#%d", Home(it)); else if (Has_dropto(it)) safe_tprintf_str(buff, bufc, "#%d", Dropto(it)); else if (isExit(it)) safe_tprintf_str(buff, bufc, "#%d", where_is(it)); else safe_str("#-1", buff, bufc); return; } /* * --------------------------------------------------------------------------- * * fun_money: Return an object's value */ FUNCTION(fun_money) { dbref it; it = match_thing(player, fargs[0]); if ((it == NOTHING) || !Examinable(player, it)) safe_str("#-1", buff, bufc); else safe_ltoa(Pennies(it), buff, bufc, LBUF_SIZE-1); } /* * --------------------------------------------------------------------------- * * fun_pos: Find a word in a string */ FUNCTION(fun_pos) { int i = 1; char *s, *t, *u; i = 1; s = fargs[1]; while (*s) { u = s; t = fargs[0]; while (*t && *t == *u) ++t, ++u; if (*t == '\0') { safe_ltoa(i, buff, bufc, LBUF_SIZE-1); return; } ++i, ++s; } safe_str("#-1", buff, bufc); return; } /* --------------------------------------------------------------------------- * fun_lpos: Find all occurrences of a character in a string, and return * a space-separated list of the positions, starting at 0. i.e., * lpos(a-bc-def-g,-) ==> 1 4 8 */ FUNCTION(fun_lpos) { if (*fargs[0] == '\0') { return; } char c = *fargs[1]; if (!c) { c = ' '; } int i; char *bb_p = *bufc; char *s = strip_ansi(fargs[0]); for (i = 0; *s; i++, s++) { if (*s == c) { if (*bufc != bb_p) { safe_chr(' ', buff, bufc); } safe_ltoa(i, buff, bufc, LBUF_SIZE-1); } } } /* * --------------------------------------------------------------------------- * * ldelete: Remove a word from a string by place * * ldelete(<list>,<position>[,<separator>]) * * * * insert: insert a word into a string by place * * insert(<list>,<position>,<new item> [,<separator>]) * * * * replace: replace a word into a string by place * * replace(<list>,<position>,<new item>[,<separator>]) */ #define IF_DELETE 0 #define IF_REPLACE 1 #define IF_INSERT 2 static void do_itemfuns(char *buff, char **bufc, char *str, int el, char *word, char sep, int flag) { int ct, overrun; char *sptr, *iptr, *eptr; int slen = 0, ilen = 0, elen = 0; char nullb; // If passed a null string return an empty string, except that we // are allowed to append to a null string. // if ((!str || !*str) && ((flag != IF_INSERT) || (el != 1))) { return; } int nStr = strlen(str); // We can't fiddle with anything before the first position. // if (el < 1) { safe_copy_buf(str, nStr, buff, bufc, LBUF_SIZE-1); return; } // Split the list up into 'before', 'target', and 'after' chunks // pointed to by sptr, iptr, and eptr respectively. // nullb = '\0'; if (el == 1) { // No 'before' portion, just split off element 1 // sptr = NULL; slen = 0; if (!str || !*str) { eptr = NULL; iptr = NULL; } else { eptr = trim_space_sep_LEN(str, nStr, sep, &elen); iptr = split_token_LEN(&eptr, &elen, sep, &ilen); } } else { // Break off 'before' portion. // sptr = eptr = trim_space_sep_LEN(str, nStr, sep, &elen); overrun = 1; for ( ct = el; ct > 2 && eptr; eptr = next_token_LEN(eptr, &elen, sep), ct--) { // Nothing } if (eptr) { // Note: We are using (iptr,ilen) temporarily. It // doesn't represent the 'target' word, but the // the last token in the 'before' portion. // overrun = 0; iptr = split_token_LEN(&eptr, &elen, sep, &ilen); slen = (iptr - sptr) + ilen; } // If we didn't make it to the target element, just return // the string. Insert is allowed to continue if we are exactly // at the end of the string, but replace and delete are not. // if (!(eptr || ((flag == IF_INSERT) && !overrun))) { safe_copy_buf(str, nStr, buff, bufc, LBUF_SIZE-1); return; } // Split the 'target' word from the 'after' portion. // if (eptr) { iptr = split_token_LEN(&eptr, &elen, sep, &ilen); } else { iptr = NULL; ilen = 0; } } switch (flag) { case IF_DELETE: // deletion // if (sptr) { safe_copy_buf(sptr, slen, buff, bufc, LBUF_SIZE-1); if (eptr) { safe_chr(sep, buff, bufc); } } if (eptr) { safe_copy_buf(eptr, elen, buff, bufc, LBUF_SIZE-1); } break; case IF_REPLACE: // replacing. // if (sptr) { safe_copy_buf(sptr, slen, buff, bufc, LBUF_SIZE-1); safe_chr(sep, buff, bufc); } safe_str(word, buff, bufc); if (eptr) { safe_chr(sep, buff, bufc); safe_copy_buf(eptr, elen, buff, bufc, LBUF_SIZE-1); } break; case IF_INSERT: // Insertion. // if (sptr) { safe_copy_buf(sptr, slen, buff, bufc, LBUF_SIZE-1); safe_chr(sep, buff, bufc); } safe_str(word, buff, bufc); if (iptr) { safe_chr(sep, buff, bufc); safe_copy_buf(iptr, ilen, buff, bufc, LBUF_SIZE-1); } if (eptr) { safe_chr(sep, buff, bufc); safe_copy_buf(eptr, elen, buff, bufc, LBUF_SIZE-1); } break; } } FUNCTION(fun_ldelete) { /* * delete a word at position X of a list */ char sep; varargs_preamble("LDELETE", 3); do_itemfuns(buff, bufc, fargs[0], Tiny_atol(fargs[1]), NULL, sep, IF_DELETE); } FUNCTION(fun_replace) { /* * replace a word at position X of a list */ char sep; varargs_preamble("REPLACE", 4); do_itemfuns(buff, bufc, fargs[0], Tiny_atol(fargs[1]), fargs[2], sep, IF_REPLACE); } FUNCTION(fun_insert) { /* * insert a word at position X of a list */ char sep; varargs_preamble("INSERT", 4); do_itemfuns(buff, bufc, fargs[0], Tiny_atol(fargs[1]), fargs[2], sep, IF_INSERT); } /* * --------------------------------------------------------------------------- * * fun_remove: Remove a word from a string */ FUNCTION(fun_remove) { char *s, *sp, *word; char sep; int first, found; varargs_preamble("REMOVE", 3); if (strchr(fargs[1], sep)) { safe_str("#-1 CAN ONLY DELETE ONE ELEMENT", buff, bufc); return; } s = fargs[0]; word = fargs[1]; /* * Walk through the string copying words until (if ever) we get to * * * * * one that matches the target word. */ sp = s; found = 0; first = 1; while (s) { sp = split_token(&s, sep); if (found || strcmp(sp, word)) { if (!first) safe_chr(sep, buff, bufc); safe_str(sp, buff, bufc); first = 0; } else { found = 1; } } } /* * --------------------------------------------------------------------------- * * fun_member: Is a word in a string */ FUNCTION(fun_member) { int wcount; char *r, *s, sep; varargs_preamble("MEMBER", 3); wcount = 1; s = trim_space_sep(fargs[0], sep); do { r = split_token(&s, sep); if (!strcmp(fargs[1], r)) { safe_ltoa(wcount, buff, bufc, LBUF_SIZE-1); return; } wcount++; } while (s); safe_chr('0', buff, bufc); } // fun_secure: This function replaces any character in the set // '%$\[](){},;' with a space. It handles ANSI by not replacing // the '[' character within an ANSI sequence. // FUNCTION(fun_secure) { char *pString = fargs[0]; int nString = strlen(pString); while (nString) { int nTokenLength0; int nTokenLength1; int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1); if (iType == TOKEN_TEXT_ANSI) { // Process TEXT portion (pString, nTokenLength0). // nString -= nTokenLength0; while (nTokenLength0--) { if (Tiny_IsSecureCharacter[(unsigned char)*pString]) { safe_chr(' ', buff, bufc); } else { safe_chr(*pString, buff, bufc); } pString++; } nTokenLength0 = nTokenLength1; } if (nTokenLength0) { // Process ANSI portion (pString, nTokenLength0). // safe_copy_buf(pString, nTokenLength0, buff, bufc, LBUF_SIZE-1); pString += nTokenLength0; nString -= nTokenLength0; } } } // fun_escape: This function prepends a '\' to the beginning of a // string and before any character which occurs in the set "%\[]{};". // It handles ANSI by not treating the '[' character within an ANSI // sequence as a special character. // FUNCTION(fun_escape) { char *pString = fargs[0]; int nString = strlen(pString); while (nString) { int nTokenLength0; int nTokenLength1; int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1); if (iType == TOKEN_TEXT_ANSI) { // Process TEXT portion (pString, nTokenLength0). // nString -= nTokenLength0; while (nTokenLength0--) { if ( Tiny_IsEscapeCharacter[(unsigned char)*pString] || pString == fargs[0]) { safe_chr('\\', buff, bufc); } safe_chr(*pString, buff, bufc); pString++; } nTokenLength0 = nTokenLength1; } if (nTokenLength0) { // Process ANSI portion (pString, nTokenLength0). // safe_copy_buf(pString, nTokenLength0, buff, bufc, LBUF_SIZE-1); pString += nTokenLength0; nString -= nTokenLength0; } } } /* * Take a character position and return which word that char is in. * * wordpos(<string>, <charpos>) */ FUNCTION(fun_wordpos) { unsigned charpos; int i; char *cp, *tp, *xp, sep; varargs_preamble("WORDPOS", 3); charpos = Tiny_atol(fargs[1]); cp = fargs[0]; if ((charpos > 0) && (charpos <= strlen(cp))) { tp = &(cp[charpos - 1]); cp = trim_space_sep(cp, sep); xp = split_token(&cp, sep); for (i = 1; xp; i++) { if (tp < (xp + strlen(xp))) break; xp = split_token(&cp, sep); } safe_ltoa(i, buff, bufc, LBUF_SIZE-1); return; } safe_str("#-1", buff, bufc); return; } FUNCTION(fun_type) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_str("#-1 NOT FOUND", buff, bufc); return; } switch (Typeof(it)) { case TYPE_ROOM: safe_str("ROOM", buff, bufc); break; case TYPE_EXIT: safe_str("EXIT", buff, bufc); break; case TYPE_PLAYER: safe_str("PLAYER", buff, bufc); break; case TYPE_THING: safe_str("THING", buff, bufc); break; default: safe_str("#-1 ILLEGAL TYPE", buff, bufc); } return; } FUNCTION(fun_hasflag) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_str("#-1 NOT FOUND", buff, bufc); return; } if (mudconf.pub_flags || Examinable(player, it) || (it == cause)) { int cc = has_flag(player, it, fargs[1]); safe_chr(cc ? '1' : '0', buff, bufc); } else { safe_str("#-1 PERMISSION DENIED", buff, bufc); } } FUNCTION(fun_haspower) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_str("#-1 NOT FOUND", buff, bufc); return; } if (mudconf.pub_flags || Examinable(player, it) || (it == cause)) { int ch = '0'; if (has_power(player, it, fargs[1])) { ch = '1'; } safe_chr(ch, buff, bufc); } else { safe_str("#-1 PERMISSION DENIED", buff, bufc); } } FUNCTION(fun_delete) { char *s = fargs[0]; int iStart = Tiny_atol(fargs[1]); int nChars = Tiny_atol(fargs[2]); int nLen = strlen(s); int iEnd = iStart + nChars; // Are we deleting anything at all? // if (iEnd <= 0 || nLen <= iStart) { if (nLen) { safe_copy_buf(s, nLen, buff, bufc, LBUF_SIZE-1); } return; } if (iStart < 0) iStart = 0; if (nLen < iEnd ) iEnd = nLen; // ASSERT: Now [iStart,iEnd) exist somewhere within the the string // [s,nLen). // if (iStart) { safe_copy_buf(s, iStart, buff, bufc, LBUF_SIZE-1); } if (iEnd < nLen) { safe_copy_buf(s + iEnd, nLen - iEnd, buff, bufc, LBUF_SIZE-1); } } FUNCTION(fun_lock) { dbref it, aowner; int aflags; char *tbuf; ATTR *attr; struct boolexp *pBoolExp; /* * Parse the argument into obj + lock */ if (!get_obj_and_lock(player, fargs[0], &it, &attr, buff, bufc)) return; /* * Get the attribute and decode it if we can read it */ tbuf = atr_get(it, attr->number, &aowner, &aflags); if (Read_attr(player, it, attr, aowner, aflags)) { pBoolExp = parse_boolexp(player, tbuf, 1); free_lbuf(tbuf); tbuf = (char *)unparse_boolexp_function(player, pBoolExp); free_boolexp(pBoolExp); safe_str(tbuf, buff, bufc); } else free_lbuf(tbuf); } FUNCTION(fun_elock) { dbref it, victim, aowner; int aflags; char *tbuf; ATTR *attr; struct boolexp *pBoolExp; /* * Parse lock supplier into obj + lock */ if (!get_obj_and_lock(player, fargs[0], &it, &attr, buff, bufc)) return; /* * Get the victim and ensure we can do it */ victim = match_thing(player, fargs[1]); if (!Good_obj(victim)) { safe_str("#-1 NOT FOUND", buff, bufc); } else if (!nearby_or_control(player, victim) && !nearby_or_control(player, it)) { safe_str("#-1 TOO FAR AWAY", buff, bufc); } else { tbuf = atr_get(it, attr->number, &aowner, &aflags); if ((attr->number == A_LOCK) || Read_attr(player, it, attr, aowner, aflags)) { pBoolExp = parse_boolexp(player, tbuf, 1); safe_ltoa(eval_boolexp(victim, it, it, pBoolExp), buff, bufc, LBUF_SIZE-1); free_boolexp(pBoolExp); } else { safe_chr('0', buff, bufc); } free_lbuf(tbuf); } } /* * --------------------------------------------------------------------------- * * fun_lwho: Return list of connected users. */ FUNCTION(fun_lwho) { make_ulist(player, buff, bufc); } /* * --------------------------------------------------------------------------- * * fun_nearby: Return whether or not obj1 is near obj2. */ FUNCTION(fun_nearby) { dbref obj1, obj2; int ch = '0'; obj1 = match_thing(player, fargs[0]); obj2 = match_thing(player, fargs[1]); if ( ( nearby_or_control(player, obj1) || nearby_or_control(player, obj2)) && nearby(obj1, obj2)) { ch = '1'; } safe_chr(ch, buff, bufc); } /* * --------------------------------------------------------------------------- * * fun_obj, fun_poss, and fun_subj: perform pronoun sub for object. */ static void process_sex(dbref player, char *what, const char *token, char *buff, char **bufc) { dbref it; char *str; it = match_thing(player, what); if (!Good_obj(it) || (!isPlayer(it) && !nearby_or_control(player, it))) { safe_str("#-1 NO MATCH", buff, bufc); } else { str = (char *)token; TinyExec(buff, bufc, 0, it, it, EV_EVAL, &str, (char **)NULL, 0); } } FUNCTION(fun_obj) { process_sex(player, fargs[0], "%o", buff, bufc); } FUNCTION(fun_poss) { process_sex(player, fargs[0], "%p", buff, bufc); } FUNCTION(fun_subj) { process_sex(player, fargs[0], "%s", buff, bufc); } FUNCTION(fun_aposs) { process_sex(player, fargs[0], "%a", buff, bufc); } /* * --------------------------------------------------------------------------- * * fun_mudname: Return the name of the mud. */ FUNCTION(fun_mudname) { safe_str(mudconf.mud_name, buff, bufc); } void ANSI_TransformTextWithTable ( char *buff, char **bufc, char *pString, unsigned char xfrmTable[256]) { int nString = strlen(pString); char *pBuffer = *bufc; int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; while (nString) { int nTokenLength0; int nTokenLength1; int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1); if (iType == TOKEN_TEXT_ANSI) { // Determine how much to move. // int nMove = nTokenLength0; if (nMove > nBufferAvailable) { nMove = nBufferAvailable; } nBufferAvailable -= nMove; // Update pointers and counts. // char *p = pString; nString -= nTokenLength0; pString += nTokenLength0; // Transform and Move text. // while (nMove--) { *pBuffer++ = xfrmTable[(unsigned char)*p++]; } // Determine whether to move the ANSI part. // if (nTokenLength1) { if (nTokenLength1 <= nBufferAvailable) { memcpy(pBuffer, pString, nTokenLength1); pBuffer += nTokenLength1; nBufferAvailable -= nTokenLength1; } nString -= nTokenLength1; pString += nTokenLength1; } } else { // TOKEN_ANSI // // Determine whether to move the ANSI part. // if (nTokenLength0 <= nBufferAvailable) { memcpy(pBuffer, pString, nTokenLength0); pBuffer += nTokenLength0; nBufferAvailable -= nTokenLength0; } nString -= nTokenLength0; pString += nTokenLength0; } } *pBuffer = '\0'; *bufc = pBuffer; } /* * --------------------------------------------------------------------------- * * fun_lcstr, fun_ucstr, fun_capstr: Lowercase, uppercase, or capitalize str. */ FUNCTION(fun_lcstr) { ANSI_TransformTextWithTable(buff, bufc, fargs[0], Tiny_ToLower); } FUNCTION(fun_ucstr) { ANSI_TransformTextWithTable(buff, bufc, fargs[0], Tiny_ToUpper); } FUNCTION(fun_capstr) { char *pString = fargs[0]; char *pBuffer = *bufc; int nString = strlen(pString); nString = safe_copy_buf(pString, nString, buff, bufc, LBUF_SIZE-1); // Find the first text character in (nString, pBuffer). // while (nString) { int nTokenLength0; int nTokenLength1; int iType = ANSI_lex(nString, pBuffer, &nTokenLength0, &nTokenLength1); if (iType == TOKEN_TEXT_ANSI) { *pBuffer = Tiny_ToUpper[(unsigned char)*pBuffer]; return; } else { // iType == TOKEN_ANSI // pBuffer += nTokenLength0; nString -= nTokenLength0; } } } /* * --------------------------------------------------------------------------- * * fun_lnum: Return a list of numbers. */ FUNCTION(fun_lnum) { char sep; if ( nfargs == 0 || !fn_range_check("LNUM", nfargs, 1, 3, buff, bufc) || !delim_check(fargs, nfargs, 3, &sep, buff, bufc, 0, player, cause, cargs, ncargs, 1)) { return; } int bot = 0, top; if (nfargs == 1) { top = Tiny_atol(fargs[0]) - 1; if (top < 0) { return; } } else { bot = Tiny_atol(fargs[0]); top = Tiny_atol(fargs[1]); } int i; if (bot == top) { safe_ltoa(bot, buff, bufc, LBUF_SIZE-1); } else if (bot < top) { safe_ltoa(bot, buff, bufc, LBUF_SIZE-1); for (i = bot+1; i <= top; i++) { print_sep(sep, buff, bufc); char *p = *bufc; safe_ltoa(i, buff, bufc, LBUF_SIZE-1); if (p == *bufc) return; } } else if (top < bot) { safe_ltoa(bot, buff, bufc, LBUF_SIZE-1); for (i = bot-1; i >= top; i--) { print_sep(sep, buff, bufc); char *p = *bufc; safe_ltoa(i, buff, bufc, LBUF_SIZE-1); if (p == *bufc) return; } } } /* * --------------------------------------------------------------------------- * * fun_lattr: Return list of attributes I can see on the object. */ FUNCTION(fun_lattr) { dbref thing; int ca, first; ATTR *attr; // Check for wildcard matching. parse_attrib_wild checks for read // permission, so we don't have to. Have p_a_w assume the // slash-star if it is missing. // first = 1; olist_push(); if (parse_attrib_wild(player, fargs[0], &thing, 0, 0, 1)) { for (ca = olist_first(); ca != NOTHING; ca = olist_next()) { attr = atr_num(ca); if (attr) { if (!first) { safe_chr(' ', buff, bufc); } first = 0; safe_str((char *)attr->name, buff, bufc); } } } else { safe_str("#-1 NO MATCH", buff, bufc); } olist_pop(); } /* * --------------------------------------------------------------------------- * * fun_reverse, fun_revwords: Reverse things. */ static void Tiny_memrevcpy(char *dest, char *src, unsigned int n) { dest += n - 1; while (n--) { *dest-- = *src++; } } typedef void MEMXFORM(char *dest, char *src, unsigned int n); void ANSI_TransformTextReverseWithFunction ( char *buff, char **bufc, char *pString, MEMXFORM *pfMemXForm ) { // Bounds checking. // unsigned int nString = strlen(pString); char *pBuffer = *bufc; unsigned int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; if (nString > nBufferAvailable) { nString = nBufferAvailable; pString[nString] = '\0'; } // How it's done: We have a source string (pString, nString) and a // destination buffer (pBuffer, nString) of equal size. // // We recognize (ANSI,TEXT) phrases and place them at the end of // destination buffer working our way to the front. The ANSI part // is left inviolate, but the text part is reversed. // // In this way, (ANSI1,TEXT1)(ANSI2,TEXT2) is traslated into // (ANSI2,reverse(TEST2))(ANSI1,reverse(TEXT1)). // // TODO: Do not reverse the CRLF in the text part either. // int nANSI = 0; char *pANSI = pString; pBuffer += nString; *bufc = pBuffer; **bufc = '\0'; while (nString) { int nTokenLength0; int nTokenLength1; int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1); if (iType == TOKEN_TEXT_ANSI) { // (ANSI,TEXT) is given by (nANSI, nTokenLength0) // pBuffer -= nANSI + nTokenLength0; memcpy(pBuffer, pANSI, nANSI); pfMemXForm(pBuffer+nANSI, pString, nTokenLength0); // Adjust pointers and counts. // nString -= nTokenLength0; pString += nTokenLength0; pANSI = pString; nANSI = 0; nTokenLength0 = nTokenLength1; } // TOKEN_ANSI // nString -= nTokenLength0; pString += nTokenLength0; nANSI += nTokenLength0; } // Copy the last ANSI part (if present). It will be overridden by // ANSI further on, but we need to fill up the space. Code // elsewhere will compact it before it's sent to the client. // pBuffer -= nANSI; memcpy(pBuffer, pANSI, nANSI); } FUNCTION(fun_reverse) { ANSI_TransformTextReverseWithFunction(buff, bufc, fargs[0], Tiny_memrevcpy); } char ReverseWordsInText_Seperator; static void ReverseWordsInText(char *dest, char *src, unsigned int n) { dest += n; while (n) { char *pWord = strchr(src, ReverseWordsInText_Seperator); int nLen; if (pWord) { nLen = (pWord - src); } else { nLen = n; } dest -= nLen; memcpy(dest, src, nLen); src += nLen; n -= nLen; if (pWord) { dest--; *dest = ReverseWordsInText_Seperator; src++; n--; } } } FUNCTION(fun_revwords) { // If we are passed an empty arglist return a null string. // if (nfargs == 0) { return; } char sep; varargs_preamble("REVWORDS", 2); ReverseWordsInText_Seperator = sep; ANSI_TransformTextReverseWithFunction(buff, bufc, fargs[0], ReverseWordsInText); } /* * --------------------------------------------------------------------------- * * fun_after, fun_before: Return substring after or before a specified string. */ FUNCTION(fun_after) { char *cp, *mp; int mlen; if (nfargs == 0) { return; } if (!fn_range_check("AFTER", nfargs, 1, 2, buff, bufc)) { return; } // Sanity-check arg1 and arg2. // char *bp = fargs[0]; if (nfargs > 1) { mp = fargs[1]; mlen = strlen(mp); } else { mp = " "; mlen = 1; } if ((mlen == 1) && (*mp == ' ')) { bp = trim_space_sep(bp, ' '); } // Look for the target string. // while (*bp) { // Search for the first character in the target string. // cp = (char *)strchr(bp, *mp); if (cp == NULL) { // Not found, return empty string. // return; } // See if what follows is what we are looking for. // if (!strncmp(cp, mp, mlen)) { // Yup, return what follows. // bp = cp + mlen; safe_str(bp, buff, bufc); return; } // Continue search after found first character. // bp = cp + 1; } // Ran off the end without finding it. // return; } FUNCTION(fun_before) { char *cp, *mp, *ip; int mlen; if (nfargs == 0) { return; } if (!fn_range_check("BEFORE", nfargs, 1, 2, buff, bufc)) return; // Sanity-check arg1 and arg2. // char *bp = fargs[0]; if (nfargs > 1) { mp = fargs[1]; mlen = strlen(mp); } else { mp = " "; mlen = 1; } if ((mlen == 1) && (*mp == ' ')) { bp = trim_space_sep(bp, ' '); } ip = bp; // Look for the target string. // while (*bp) { // Search for the first character in the target string. // cp = (char *)strchr(bp, *mp); if (cp == NULL) { // Not found, return entire string. // safe_str(ip, buff, bufc); return; } // See if what follows is what we are looking for. // if (!strncmp(cp, mp, mlen)) { // Yup, return what follows. // *cp = '\0'; safe_str(ip, buff, bufc); return; } // Continue search after found first character. // bp = cp + 1; } // Ran off the end without finding it. // safe_str(ip, buff, bufc); return; } /* * --------------------------------------------------------------------------- * * fun_max, fun_min: Return maximum (minimum) value. */ FUNCTION(fun_max) { if (nfargs <= 0) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } double maximum = DBL_MIN; for (int i = 0; i < nfargs; i++) { double tval = safe_atof(fargs[i]); if (tval > maximum) { maximum = tval; } } fval(buff, bufc, maximum); } FUNCTION(fun_min) { if (nfargs <= 0) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } double minimum = DBL_MAX; for (int i = 0; i < nfargs; i++) { double tval = safe_atof(fargs[i]); if (tval < minimum) { minimum = tval; } } fval(buff, bufc, minimum); } /* * --------------------------------------------------------------------------- * * fun_search: Search the db for things, returning a list of what matches */ FUNCTION(fun_search) { dbref thing; char *bp, *nbuf; SEARCH searchparm; /* * Set up for the search. If any errors, abort. */ if (!search_setup(player, fargs[0], &searchparm)) { safe_str("#-1 ERROR DURING SEARCH", buff, bufc); return; } /* * Do the search and report the results */ olist_push(); search_perform(player, cause, &searchparm); bp = *bufc; nbuf = alloc_sbuf("fun_search"); for (thing = olist_first(); thing != NOTHING; thing = olist_next()) { if (bp == *bufc) { nbuf[0] = '#'; Tiny_ltoa(thing, nbuf+1); } else { nbuf[0] = ' '; nbuf[1] = '#'; Tiny_ltoa(thing, nbuf+2); } safe_str(nbuf, buff, bufc); } free_sbuf(nbuf); olist_pop(); } /* * --------------------------------------------------------------------------- * * fun_stats: Get database size statistics. */ FUNCTION(fun_stats) { dbref who; STATS statinfo; if ((!fargs[0]) || !*fargs[0] || !string_compare(fargs[0], "all")) { who = NOTHING; } else { who = lookup_player(player, fargs[0], 1); if (who == NOTHING) { safe_str("#-1 NOT FOUND", buff, bufc); return; } } if (!get_stats(player, who, &statinfo)) { safe_str("#-1 ERROR GETTING STATS", buff, bufc); return; } safe_tprintf_str(buff, bufc, "%d %d %d %d %d %d", statinfo.s_total, statinfo.s_rooms, statinfo.s_exits, statinfo.s_things, statinfo.s_players, statinfo.s_garbage); } /* * --------------------------------------------------------------------------- * * fun_merge: given two strings and a character, merge the two strings * * by replacing characters in string1 that are the same as the given * * character by the corresponding character in string2 (by position). * * The strings must be of the same length. */ FUNCTION(fun_merge) { char *str, *rep; char c; // Do length checks first. // if (strlen(fargs[0]) != strlen(fargs[1])) { safe_str("#-1 STRING LENGTHS MUST BE EQUAL", buff, bufc); return; } if (strlen(fargs[2]) > 1) { safe_str("#-1 TOO MANY CHARACTERS", buff, bufc); return; } // Find the character to look for. null character is considered a // space. // if (!*fargs[2]) c = ' '; else c = *fargs[2]; // Walk strings, copy from the appropriate string. // for (str = fargs[0], rep = fargs[1]; *str && *rep && ((*bufc - buff) < (LBUF_SIZE-1)); str++, rep++, (*bufc)++) { if (*str == c) **bufc = *rep; else **bufc = *str; } return; } /* * --------------------------------------------------------------------------- * * fun_splice: similar to MERGE(), eplaces by word instead of by character. */ FUNCTION(fun_splice) { char *p1, *p2, *q1, *q2, sep; int words, i, first; varargs_preamble("SPLICE", 4); /* * length checks */ if (countwords(fargs[2], sep) > 1) { safe_str("#-1 TOO MANY WORDS", buff, bufc); return; } words = countwords(fargs[0], sep); if (words != countwords(fargs[1], sep)) { safe_str("#-1 NUMBER OF WORDS MUST BE EQUAL", buff, bufc); return; } /* * loop through the two lists */ p1 = fargs[0]; q1 = fargs[1]; first = 1; for (i = 0; i < words; i++) { p2 = split_token(&p1, sep); q2 = split_token(&q1, sep); if (!first) safe_chr(sep, buff, bufc); if (!strcmp(p2, fargs[2])) safe_str(q2, buff, bufc); /* * replace */ else safe_str(p2, buff, bufc); /* * copy */ first = 0; } } /* * --------------------------------------------------------------------------- * * fun_repeat: repeats a string */ FUNCTION(fun_repeat) { int times; times = Tiny_atol(fargs[1]); if (times < 1 || *fargs[0] == '\0') { // Legal but no work to do. // return; } else if (times == 1) { // It turns into a string copy. // safe_str(fargs[0], buff, bufc); } else { int len = strlen(fargs[0]); if (len == 1) { // It turns into a memset. // int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; if (times > nBufferAvailable) { times = nBufferAvailable; } // Fill with repeat character. // memset(*bufc, *fargs[0], times); *bufc += times; } else { int nSize = len*times; if ( times > LBUF_SIZE - 1 || nSize > LBUF_SIZE - 1) { safe_str("#-1 STRING TOO LONG", buff, bufc); } else { int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; if (nSize > nBufferAvailable) { nSize = nBufferAvailable; } int nFullCopies = nSize / len; int nPartial = nSize - nFullCopies * len; while (nFullCopies--) { memcpy(*bufc, fargs[0], len); *bufc += len; } if (nPartial) { memcpy(*bufc, fargs[0], nPartial); *bufc += nPartial; } } } } } /* * --------------------------------------------------------------------------- * * fun_iter: Make list from evaluating arg2 with each member of arg1. * * NOTE: This function expects that its arguments have not been evaluated. */ FUNCTION(fun_iter) { char *curr, *objstring, *buff2, *buff3, *cp, *dp, sep, *str; int first, number = 0; evarargs_preamble("ITER", 3); dp = cp = curr = alloc_lbuf("fun_iter"); str = fargs[0]; TinyExec(curr, &dp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *dp = '\0'; cp = trim_space_sep(cp, sep); if (!*cp) { free_lbuf(curr); return; } first = 1; while (cp) { if (!first) safe_chr(' ', buff, bufc); first = 0; number++; objstring = split_token(&cp, sep); buff2 = replace_string(BOUND_VAR, objstring, fargs[1]); buff3 = replace_string(LISTPLACE_VAR, Tiny_ltoa_t(number), buff2); str = buff3; TinyExec(buff, bufc, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); free_lbuf(buff2); free_lbuf(buff3); } free_lbuf(curr); } FUNCTION(fun_list) { char *curr, *objstring, *buff2, *buff3, *result, *cp, *dp, *str, sep; int number = 0; evarargs_preamble("LIST", 3); cp = curr = dp = alloc_lbuf("fun_list"); str = fargs[0]; TinyExec(curr, &dp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); cp = trim_space_sep(cp, sep); if (!*cp) { free_lbuf(curr); return; } while (cp) { number++; objstring = split_token(&cp, sep); buff2 = replace_string(BOUND_VAR, objstring, fargs[1]); buff3 = replace_string(LISTPLACE_VAR, Tiny_ltoa_t(number), buff2); dp = result = alloc_lbuf("fun_list.2"); str = buff3; TinyExec(result, &dp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *dp = '\0'; free_lbuf(buff2); free_lbuf(buff3); notify(cause, result); free_lbuf(result); } free_lbuf(curr); } /* * --------------------------------------------------------------------------- * * fun_fold: iteratively eval an attrib with a list of arguments * * and an optional base case. With no base case, the first list element * * is passed as %0 and the second is %1. The attrib is then evaluated * * with these args, the result is then used as %0 and the next arg is * * %1 and so it goes as there are elements left in the list. The * * optinal base case gives the user a nice starting point. * * * * > &REP_NUM object=[%0][repeat(%1,%1)] * * > say fold(OBJECT/REP_NUM,1 2 3 4 5,->) * * You say "->122333444455555" * * * * NOTE: To use added list separator, you must use base case! */ FUNCTION(fun_fold) { dbref aowner, thing; int aflags, anum; ATTR *ap; char *atext, *result, *curr, *bp, *str, *cp, *atextbuf, *clist[2], *rstore, sep; /* * We need two to four arguements only */ mvarargs_preamble("FOLD", 2, 4); /* * Two possibilities for the first arg: <obj>/<attr> and <attr>. */ if (parse_attrib(player, fargs[0], &thing, &anum)) { if ((anum == NOTHING) || (!Good_obj(thing))) ap = NULL; else ap = atr_num(anum); } else { thing = player; ap = atr_str(fargs[0]); } /* * Make sure we got a good attribute */ if (!ap) { return; } /* * Use it if we can access it, otherwise return an error. */ atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); return; } /* * Evaluate it using the rest of the passed function args */ cp = curr = fargs[1]; atextbuf = alloc_lbuf("fun_fold"); StringCopy(atextbuf, atext); /* * may as well handle first case now */ if ((nfargs >= 3) && (fargs[2])) { clist[0] = fargs[2]; clist[1] = split_token(&cp, sep); result = bp = alloc_lbuf("fun_fold"); str = atextbuf; TinyExec(result, &bp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, clist, 2); *bp = '\0'; } else { clist[0] = split_token(&cp, sep); clist[1] = split_token(&cp, sep); result = bp = alloc_lbuf("fun_fold"); str = atextbuf; TinyExec(result, &bp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, clist, 2); *bp = '\0'; } rstore = result; result = NULL; while (cp) { clist[0] = rstore; clist[1] = split_token(&cp, sep); StringCopy(atextbuf, atext); result = bp = alloc_lbuf("fun_fold"); str = atextbuf; TinyExec(result, &bp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, clist, 2); *bp = '\0'; StringCopy(rstore, result); free_lbuf(result); } safe_str(rstore, buff, bufc); free_lbuf(rstore); free_lbuf(atext); free_lbuf(atextbuf); } /* * --------------------------------------------------------------------------- * * fun_filter: iteratively perform a function with a list of arguments * * and return the arg, if the function evaluates to TRUE using the * * arg. * * * * > &IS_ODD object=mod(%0,2) * * > say filter(object/is_odd,1 2 3 4 5) * * You say "1 3 5" * * > say filter(object/is_odd,1-2-3-4-5,-) * * You say "1-3-5" * * * * NOTE: If you specify a separator it is used to delimit returned list */ FUNCTION(fun_filter) { dbref aowner, thing; int aflags, anum, first; ATTR *ap; char *atext, *result, *curr, *objstring, *bp, *str, *cp, *atextbuf, sep; varargs_preamble("FILTER", 3); /* * Two possibilities for the first arg: <obj>/<attr> and <attr>. */ if (parse_attrib(player, fargs[0], &thing, &anum)) { if ((anum == NOTHING) || (!Good_obj(thing))) ap = NULL; else ap = atr_num(anum); } else { thing = player; ap = atr_str(fargs[0]); } /* * Make sure we got a good attribute */ if (!ap) { return; } /* * Use it if we can access it, otherwise return an error. */ atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); return; } /* * Now iteratively eval the attrib with the argument list */ cp = curr = trim_space_sep(fargs[1], sep); atextbuf = alloc_lbuf("fun_filter"); first = 1; while (cp) { objstring = split_token(&cp, sep); StringCopy(atextbuf, atext); result = bp = alloc_lbuf("fun_filter"); str = atextbuf; TinyExec(result, &bp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &objstring, 1); *bp = '\0'; if (!first && *result == '1') safe_chr(sep, buff, bufc); if (*result == '1') { safe_str(objstring, buff, bufc); first = 0; } free_lbuf(result); } free_lbuf(atext); free_lbuf(atextbuf); } /* * --------------------------------------------------------------------------- * * fun_map: iteratively evaluate an attribute with a list of arguments. * * * * > &DIV_TWO object=fdiv(%0,2) * * > say map(1 2 3 4 5,object/div_two) * * You say "0.5 1 1.5 2 2.5" * * > say map(object/div_two,1-2-3-4-5,-) * * You say "0.5-1-1.5-2-2.5" * * */ FUNCTION(fun_map) { dbref aowner, thing; int aflags, anum, first; ATTR *ap; char *atext, *objstring, *str, *cp, *atextbuf, sep; varargs_preamble("MAP", 3); /* * Two possibilities for the second arg: <obj>/<attr> and <attr>. */ if (parse_attrib(player, fargs[0], &thing, &anum)) { if ((anum == NOTHING) || (!Good_obj(thing))) ap = NULL; else ap = atr_num(anum); } else { thing = player; ap = atr_str(fargs[0]); } /* * Make sure we got a good attribute */ if (!ap) { return; } /* * Use it if we can access it, otherwise return an error. */ atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); return; } /* * now process the list one element at a time */ cp = trim_space_sep(fargs[1], sep); atextbuf = alloc_lbuf("fun_map"); first = 1; while (cp) { if (!first) safe_chr(sep, buff, bufc); first = 0; objstring = split_token(&cp, sep); StringCopy(atextbuf, atext); str = atextbuf; TinyExec(buff, bufc, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &objstring, 1); } free_lbuf(atext); free_lbuf(atextbuf); } /* * --------------------------------------------------------------------------- * * fun_edit: Edit text. */ FUNCTION(fun_edit) { char *tstr; edit_string((char *)strip_ansi(fargs[0]), &tstr, fargs[1], fargs[2]); safe_str(tstr, buff, bufc); free_lbuf(tstr); } /* * --------------------------------------------------------------------------- * * fun_locate: Search for things with the perspective of another obj. */ FUNCTION(fun_locate) { int pref_type, check_locks, verbose, multiple; dbref thing, what; char *cp; pref_type = NOTYPE; check_locks = verbose = multiple = 0; /* * Find the thing to do the looking, make sure we control it. */ if (See_All(player)) thing = match_thing(player, fargs[0]); else thing = match_controlled(player, fargs[0]); if (!Good_obj(thing)) { safe_str("#-1 PERMISSION DENIED", buff, bufc); return; } /* * Get pre- and post-conditions and modifiers */ for (cp = fargs[2]; *cp; cp++) { switch (*cp) { case 'E': pref_type = TYPE_EXIT; break; case 'L': check_locks = 1; break; case 'P': pref_type = TYPE_PLAYER; break; case 'R': pref_type = TYPE_ROOM; break; case 'T': pref_type = TYPE_THING; break; case 'V': verbose = 1; break; case 'X': multiple = 1; break; } } /* * Set up for the search */ if (check_locks) init_match_check_keys(thing, fargs[1], pref_type); else init_match(thing, fargs[1], pref_type); /* * Search for each requested thing */ for (cp = fargs[2]; *cp; cp++) { switch (*cp) { case 'a': match_absolute(); break; case 'c': match_carried_exit_with_parents(); break; case 'e': match_exit_with_parents(); break; case 'h': match_here(); break; case 'i': match_possession(); break; case 'm': match_me(); break; case 'n': match_neighbor(); break; case 'p': match_player(); break; case '*': match_everything(MAT_EXIT_PARENTS); break; } } /* * Get the result and return it to the caller */ if (multiple) what = last_match_result(); else what = match_result(); if (verbose) (void)match_status(player, what); safe_tprintf_str(buff, bufc, "#%d", what); } /* * --------------------------------------------------------------------------- * * fun_switch: Return value based on pattern matching (ala @switch) * * NOTE: This function expects that its arguments have not been evaluated. */ FUNCTION(fun_switch) { int i; char *mbuff, *tbuff, *bp, *str; // If we don't have at least 2 args, return nothing. // if (nfargs < 2) { return; } // Evaluate the target in fargs[0]. // mbuff = bp = alloc_lbuf("fun_switch"); str = fargs[0]; TinyExec(mbuff, &bp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; // Loop through the patterns looking for a match. // for (i = 1; (i < nfargs - 1) && fargs[i] && fargs[i + 1]; i += 2) { tbuff = bp = alloc_lbuf("fun_switch.2"); str = fargs[i]; TinyExec(tbuff, &bp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; if (quick_wild(tbuff, mbuff)) { free_lbuf(tbuff); str = fargs[i+1]; TinyExec(buff, bufc, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); free_lbuf(mbuff); return; } free_lbuf(tbuff); } free_lbuf(mbuff); // Nope, return the default if there is one. // if ((i < nfargs) && fargs[i]) { str = fargs[i]; TinyExec(buff, bufc, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } return; } FUNCTION(fun_case) { int i; char *mbuff, *bp, *str; // If we don't have at least 2 args, return nothing. // if (nfargs < 2) { return; } // Evaluate the target in fargs[0] // mbuff = bp = alloc_lbuf("fun_switch"); str = fargs[0]; TinyExec(mbuff, &bp, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; // Loop through the patterns looking for a match. // for (i = 1; (i < nfargs - 1) && fargs[i] && fargs[i + 1]; i += 2) { if (!string_compare(fargs[i], mbuff)) { str = fargs[i+1]; TinyExec(buff, bufc, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); free_lbuf(mbuff); return; } } free_lbuf(mbuff); // Nope, return the default if there is one. // if ((i < nfargs) && fargs[i]) { str = fargs[i]; TinyExec(buff, bufc, 0, player, cause, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } } /* * --------------------------------------------------------------------------- * * fun_space: Make spaces. */ FUNCTION(fun_space) { // Validate request. // int num; if (*fargs[0] == '\0') { num = 1; } else { num = Tiny_atol(fargs[0]); if (num == 0) { // If 'space(0)', 'space(00)', ..., then allow num == 0, // otherwise, we force to num to be 1. // if (!is_integer(fargs[0], 0)) { num = 1; } } else if (num < 0) { num = 0; } } // Check for buffer limits. // int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; if (num > nBufferAvailable) { num = nBufferAvailable; } // Fill with spaces. // memset(*bufc, ' ', num); *bufc += num; } /* * --------------------------------------------------------------------------- * * fun_idle, fun_conn: return seconds idle or connected. */ FUNCTION(fun_idle) { dbref target; target = lookup_player(player, fargs[0], 1); if (Good_obj(target) && Dark(target) && !Wizard(player)) target = NOTHING; safe_ltoa(fetch_idle(target), buff, bufc, LBUF_SIZE-1); } FUNCTION(fun_conn) { dbref target; target = lookup_player(player, fargs[0], 1); if (Good_obj(target) && Dark(target) && !Wizard(player)) target = NOTHING; safe_ltoa(fetch_connect(target), buff, bufc, LBUF_SIZE-1); } /* * --------------------------------------------------------------------------- * * fun_sort: Sort lists. */ typedef struct f_record f_rec; typedef struct i_record i_rec; struct f_record { double data; char *str; }; struct i_record { long data; char *str; }; static int DCL_CDECL a_comp(const void *s1, const void *s2) { return strcmp(*(char **)s1, *(char **)s2); } static int DCL_CDECL f_comp(const void *s1, const void *s2) { if (((f_rec *) s1)->data > ((f_rec *) s2)->data) return 1; if (((f_rec *) s1)->data < ((f_rec *) s2)->data) return -1; return 0; } static int DCL_CDECL i_comp(const void *s1, const void *s2) { if (((i_rec *) s1)->data > ((i_rec *) s2)->data) return 1; if (((i_rec *) s1)->data < ((i_rec *) s2)->data) return -1; return 0; } static void do_asort(char *s[], int n, int sort_type) { int i; f_rec *fp; i_rec *ip; switch (sort_type) { case ALPHANUM_LIST: qsort((void *)s, n, sizeof(char *), a_comp); break; case NUMERIC_LIST: ip = (i_rec *) MEMALLOC(n * sizeof(i_rec)); ISOUTOFMEMORY(ip); for (i = 0; i < n; i++) { ip[i].str = s[i]; ip[i].data = Tiny_atol(s[i]); } qsort((void *)ip, n, sizeof(i_rec), i_comp); for (i = 0; i < n; i++) { s[i] = ip[i].str; } MEMFREE(ip); break; case DBREF_LIST: ip = (i_rec *) MEMALLOC(n * sizeof(i_rec)); ISOUTOFMEMORY(ip); for (i = 0; i < n; i++) { ip[i].str = s[i]; ip[i].data = dbnum(s[i]); } qsort((void *)ip, n, sizeof(i_rec), i_comp); for (i = 0; i < n; i++) { s[i] = ip[i].str; } MEMFREE(ip); break; case FLOAT_LIST: fp = (f_rec *) MEMALLOC(n * sizeof(f_rec)); ISOUTOFMEMORY(fp); for (i = 0; i < n; i++) { fp[i].str = s[i]; fp[i].data = safe_atof(s[i]); } qsort((void *)fp, n, sizeof(f_rec), f_comp); for (i = 0; i < n; i++) { s[i] = fp[i].str; } MEMFREE(fp); break; } } FUNCTION(fun_sort) { int nitems, sort_type; char *list, sep; char *ptrs[LBUF_SIZE / 2]; /* * If we are passed an empty arglist return a null string */ if (nfargs == 0) { return; } mvarargs_preamble("SORT", 1, 3); /* * Convert the list to an array */ list = alloc_lbuf("fun_sort"); StringCopy(list, fargs[0]); nitems = list2arr(ptrs, LBUF_SIZE / 2, list, sep); sort_type = get_list_type(fargs, nfargs, 2, ptrs, nitems); do_asort(ptrs, nitems, sort_type); arr2list(ptrs, nitems, buff, bufc, sep); free_lbuf(list); } /* * --------------------------------------------------------------------------- * * fun_setunion, fun_setdiff, fun_setinter: Set management. */ #define SET_UNION 1 #define SET_INTERSECT 2 #define SET_DIFF 3 static void handle_sets(char *fargs[], char *buff, char **bufc, int oper, char sep) { char *list1, *list2, *oldp; char *ptrs1[LBUF_SIZE], *ptrs2[LBUF_SIZE]; int i1, i2, n1, n2, val, first; list1 = alloc_lbuf("fun_setunion.1"); StringCopy(list1, fargs[0]); n1 = list2arr(ptrs1, LBUF_SIZE, list1, sep); do_asort(ptrs1, n1, ALPHANUM_LIST); list2 = alloc_lbuf("fun_setunion.2"); StringCopy(list2, fargs[1]); n2 = list2arr(ptrs2, LBUF_SIZE, list2, sep); do_asort(ptrs2, n2, ALPHANUM_LIST); i1 = i2 = 0; first = 1; oldp = *bufc; **bufc = '\0'; switch (oper) { case SET_UNION: /* * Copy elements common to both lists */ /* * Handle case of two identical single-element lists */ if ((n1 == 1) && (n2 == 1) && (!strcmp(ptrs1[0], ptrs2[0]))) { safe_str(ptrs1[0], buff, bufc); break; } /* * Process until one list is empty */ while ((i1 < n1) && (i2 < n2)) { /* * Skip over duplicates */ if ((i1 > 0) || (i2 > 0)) { while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; while ((i2 < n2) && !strcmp(ptrs2[i2], oldp)) i2++; } /* * Compare and copy */ if ((i1 < n1) && (i2 < n2)) { if (!first) safe_chr(sep, buff, bufc); first = 0; oldp = *bufc; if (strcmp(ptrs1[i1], ptrs2[i2]) < 0) { safe_str(ptrs1[i1], buff, bufc); i1++; } else { safe_str(ptrs2[i2], buff, bufc); i2++; } **bufc = '\0'; } } /* * Copy rest of remaining list, stripping duplicates */ for (; i1 < n1; i1++) { if (strcmp(oldp, ptrs1[i1])) { if (!first) safe_chr(sep, buff, bufc); first = 0; oldp = *bufc; safe_str(ptrs1[i1], buff, bufc); **bufc = '\0'; } } for (; i2 < n2; i2++) { if (strcmp(oldp, ptrs2[i2])) { if (!first) safe_chr(sep, buff, bufc); first = 0; oldp = *bufc; safe_str(ptrs2[i2], buff, bufc); **bufc = '\0'; } } break; case SET_INTERSECT: /* * Copy elements not in both lists */ while ((i1 < n1) && (i2 < n2)) { val = strcmp(ptrs1[i1], ptrs2[i2]); if (!val) { /* * Got a match, copy it */ if (!first) safe_chr(sep, buff, bufc); first = 0; oldp = *bufc; safe_str(ptrs1[i1], buff, bufc); i1++; i2++; while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; while ((i2 < n2) && !strcmp(ptrs2[i2], oldp)) i2++; } else if (val < 0) { i1++; } else { i2++; } } break; case SET_DIFF: /* * Copy elements unique to list1 */ while ((i1 < n1) && (i2 < n2)) { val = strcmp(ptrs1[i1], ptrs2[i2]); if (!val) { /* * Got a match, increment pointers */ oldp = ptrs1[i1]; while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; while ((i2 < n2) && !strcmp(ptrs2[i2], oldp)) i2++; } else if (val < 0) { /* * Item in list1 not in list2, copy */ if (!first) safe_chr(sep, buff, bufc); first = 0; safe_str(ptrs1[i1], buff, bufc); oldp = ptrs1[i1]; i1++; while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; } else { /* * Item in list2 but not in list1, discard */ oldp = ptrs2[i2]; i2++; while ((i2 < n2) && !strcmp(ptrs2[i2], oldp)) i2++; } } /* * Copy remainder of list1 */ while (i1 < n1) { if (!first) safe_chr(sep, buff, bufc); first = 0; safe_str(ptrs1[i1], buff, bufc); oldp = ptrs1[i1]; i1++; while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; } } free_lbuf(list1); free_lbuf(list2); return; } FUNCTION(fun_setunion) { char sep; varargs_preamble("SETUNION", 3); handle_sets(fargs, buff, bufc, SET_UNION, sep); return; } FUNCTION(fun_setdiff) { char sep; varargs_preamble("SETDIFF", 3); handle_sets(fargs, buff, bufc, SET_DIFF, sep); return; } FUNCTION(fun_setinter) { char sep; varargs_preamble("SETINTER", 3); handle_sets(fargs, buff, bufc, SET_INTERSECT, sep); return; } /* * --------------------------------------------------------------------------- * * rjust, ljust, center: Justify or center text, specifying fill character */ FUNCTION(fun_ljust) { char sep; varargs_preamble("LJUST", 3); int nPad = Tiny_atol(fargs[1]) - strlen(strip_ansi(fargs[0])); safe_str(fargs[0], buff, bufc); if (nPad > 0) { int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; if (nPad > nBufferAvailable) { nPad = nBufferAvailable; } // Fill with padding character. // memset(*bufc, sep, nPad); *bufc += nPad; } } FUNCTION(fun_rjust) { char sep; varargs_preamble("RJUST", 3); int nPad = Tiny_atol(fargs[1]) - strlen(strip_ansi(fargs[0])); if (nPad > 0) { int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; if (nPad > nBufferAvailable) { nPad = nBufferAvailable; } // Fill with padding character. // memset(*bufc, sep, nPad); *bufc += nPad; } safe_str(fargs[0], buff, bufc); } FUNCTION(fun_center) { char sep; int i, len, lead_chrs, trail_chrs, width; varargs_preamble("CENTER", 3); width = Tiny_atol(fargs[1]); len = strlen((char *)strip_ansi(fargs[0])); if (width > LBUF_SIZE) { safe_str("#-1 OUT OF RANGE", buff, bufc); return; } if (len >= width) { safe_str(fargs[0], buff, bufc); return; } // The text is padded with the specified fill character before and // after. The length of the suffix padding is always equal to or one // greater than the length of the prefix padding. // lead_chrs = (width - len)/2; for (i = 0; i < lead_chrs; i++) { safe_chr(sep, buff, bufc); } safe_str(fargs[0], buff, bufc); trail_chrs = width - lead_chrs - len; for (i = 0; i < trail_chrs; i++) { safe_chr(sep, buff, bufc); } } /* * --------------------------------------------------------------------------- * * setq, setr, r: set and read global registers. */ FUNCTION(fun_setq) { int regnum; regnum = Tiny_atol(fargs[0]); if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) { safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc); } else { if (!mudstate.global_regs[regnum]) { mudstate.global_regs[regnum] = alloc_lbuf("fun_setq"); } int n = strlen(fargs[1]); memcpy(mudstate.global_regs[regnum], fargs[1], n+1); mudstate.glob_reg_len[regnum] = n; } } FUNCTION(fun_setr) { int regnum; regnum = Tiny_atol(fargs[0]); int n; if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) { safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc); return; } else { if (!mudstate.global_regs[regnum]) { mudstate.global_regs[regnum] = alloc_lbuf("fun_setq"); } n = strlen(fargs[1]); memcpy(mudstate.global_regs[regnum], fargs[1], n+1); mudstate.glob_reg_len[regnum] = n; } safe_copy_buf(fargs[1], n, buff, bufc, LBUF_SIZE-1); } FUNCTION(fun_r) { int regnum; regnum = Tiny_atol(fargs[0]); if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) { safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc); } else if (mudstate.global_regs[regnum]) { safe_copy_buf(mudstate.global_regs[regnum], mudstate.glob_reg_len[regnum], buff, bufc, LBUF_SIZE-1); } } /* * --------------------------------------------------------------------------- * * isnum: is the argument a number? */ FUNCTION(fun_isnum) { safe_str((is_number(fargs[0]) ? "1" : "0"), buff, bufc); } /* * --------------------------------------------------------------------------- * * isdbref: is the argument a valid dbref? */ FUNCTION(fun_isdbref) { char *p; dbref dbitem; int ch = '0'; p = fargs[0]; if (*p++ == NUMBER_TOKEN) { dbitem = parse_dbref(p); if (Good_obj(dbitem)) { ch = '1'; } } safe_chr(ch, buff, bufc); } /* * --------------------------------------------------------------------------- * * trim: trim off unwanted white space. */ FUNCTION(fun_trim) { char *p, *lastchar, *q, sep; int trim; if (nfargs == 0) { return; } mvarargs_preamble("TRIM", 1, 3); if (nfargs >= 2) { switch (Tiny_ToLower[(unsigned char)*fargs[1]]) { case 'l': trim = 1; break; case 'r': trim = 2; break; default: trim = 3; break; } } else { trim = 3; } if (trim == 2 || trim == 3) { p = lastchar = fargs[0]; while (*p != '\0') { if (*p != sep) lastchar = p; p++; } *(lastchar + 1) = '\0'; } q = fargs[0]; if (trim == 1 || trim == 3) { while (*q != '\0') { if (*q == sep) q++; else break; } } safe_str(q, buff, bufc); } /* --------------------------------------------------------------------------- * flist: List of existing functions in alphabetical order. */ FUN flist[] = { {"ABS", fun_abs, 1, 0, CA_PUBLIC}, {"ACOS", fun_acos, 1, 0, CA_PUBLIC}, {"ADD", fun_add, 0, FN_VARARGS, CA_PUBLIC}, {"AFTER", fun_after, 0, FN_VARARGS, CA_PUBLIC}, {"ALPHAMAX", fun_alphamax, 0, FN_VARARGS, CA_PUBLIC}, {"ALPHAMIN", fun_alphamin, 0, FN_VARARGS, CA_PUBLIC}, {"AND", fun_and, 0, FN_VARARGS, CA_PUBLIC}, {"ANDFLAGS", fun_andflags, 2, 0, CA_PUBLIC}, {"ANSI", fun_ansi, 2, 0, CA_PUBLIC}, {"APOSS", fun_aposs, 1, 0, CA_PUBLIC}, {"ART", fun_art, 1, 0, CA_PUBLIC}, {"ASIN", fun_asin, 1, 0, CA_PUBLIC}, {"ATAN", fun_atan, 1, 0, CA_PUBLIC}, {"BAND", fun_band, 2, 0, CA_PUBLIC}, {"BEEP", fun_beep, 0, 0, CA_WIZARD}, {"BEFORE", fun_before, 0, FN_VARARGS, CA_PUBLIC}, {"BNAND", fun_bnand, 2, 0, CA_PUBLIC}, {"BOR", fun_bor, 2, 0, CA_PUBLIC}, #ifdef WOD_REALMS {"CANSEE", fun_cansee, 0, FN_VARARGS, CA_PUBLIC}, #endif {"CAPSTR", fun_capstr, -1, 0, CA_PUBLIC}, {"CAT", fun_cat, 0, FN_VARARGS, CA_PUBLIC}, {"CEIL", fun_ceil, 1, 0, CA_PUBLIC}, {"CENTER", fun_center, 0, FN_VARARGS, CA_PUBLIC}, {"CHANNELS", fun_channels, 0, 0, CA_PUBLIC}, {"CHILDREN", fun_children, 1, 0, CA_PUBLIC}, {"COLUMNS", fun_columns, 0, FN_VARARGS, CA_PUBLIC}, {"COMALIAS", fun_comalias, 2, 0, CA_PUBLIC}, {"COMP", fun_comp, 2, 0, CA_PUBLIC}, {"COMTITLE", fun_comtitle, 2, 0, CA_PUBLIC}, {"CON", fun_con, 1, 0, CA_PUBLIC}, {"CONN", fun_conn, 1, 0, CA_PUBLIC}, {"CONTROLS", fun_controls, 2, 0, CA_PUBLIC}, {"CONVSECS", fun_convsecs, 0, FN_VARARGS, CA_PUBLIC}, {"CONVTIME", fun_convtime, 0, FN_VARARGS, CA_PUBLIC}, {"COS", fun_cos, 1, 0, CA_PUBLIC}, {"CREATE", fun_create, 0, FN_VARARGS, CA_PUBLIC}, {"CWHO", fun_cwho, 1, 0, CA_PUBLIC}, {"DEC", fun_dec, 1, 0, CA_PUBLIC}, {"DECRYPT", fun_decrypt, 2, 0, CA_PUBLIC}, {"DEFAULT", fun_default, 2, FN_NO_EVAL, CA_PUBLIC}, {"DELETE", fun_delete, 3, 0, CA_PUBLIC}, {"DIE", fun_die, 2, 0, CA_PUBLIC}, {"DIST2D", fun_dist2d, 4, 0, CA_PUBLIC}, {"DIST3D", fun_dist3d, 6, 0, CA_PUBLIC}, {"DIV", fun_div, 2, 0, CA_PUBLIC}, {"DOING", fun_doing, 1, 0, CA_PUBLIC}, {"E", fun_e, 0, 0, CA_PUBLIC}, {"EDEFAULT", fun_edefault, 2, FN_NO_EVAL, CA_PUBLIC}, {"EDIT", fun_edit, 3, 0, CA_PUBLIC}, {"ELEMENTS", fun_elements, 0, FN_VARARGS, CA_PUBLIC}, {"ELOCK", fun_elock, 2, 0, CA_PUBLIC}, {"EMPTY", fun_empty, 0, FN_VARARGS, CA_PUBLIC}, {"ENCRYPT", fun_encrypt, 2, 0, CA_PUBLIC}, {"EQ", fun_eq, 2, 0, CA_PUBLIC}, {"ESCAPE", fun_escape, -1, 0, CA_PUBLIC}, {"EXIT", fun_exit, 1, 0, CA_PUBLIC}, {"EXP", fun_exp, 1, 0, CA_PUBLIC}, {"EXTRACT", fun_extract, 0, FN_VARARGS, CA_PUBLIC}, {"EVAL", fun_eval, 0, FN_VARARGS, CA_PUBLIC}, {"SUBEVAL", fun_subeval, 1, 0, CA_PUBLIC}, {"FDIV", fun_fdiv, 2, 0, CA_PUBLIC}, {"FILTER", fun_filter, 0, FN_VARARGS, CA_PUBLIC}, {"FINDABLE", fun_findable, 2, 0, CA_PUBLIC}, {"FIRST", fun_first, 0, FN_VARARGS, CA_PUBLIC}, {"FLAGS", fun_flags, 1, 0, CA_PUBLIC}, {"FLOOR", fun_floor, 1, 0, CA_PUBLIC}, {"FOLD", fun_fold, 0, FN_VARARGS, CA_PUBLIC}, {"FOREACH", fun_foreach, 0, FN_VARARGS, CA_PUBLIC}, {"FULLNAME", fun_fullname, 1, 0, CA_PUBLIC}, {"GET", fun_get, 1, 0, CA_PUBLIC}, {"GET_EVAL", fun_get_eval, 1, 0, CA_PUBLIC}, {"GRAB", fun_grab, 0, FN_VARARGS, CA_PUBLIC}, {"GREP", fun_grep, 3, 0, CA_PUBLIC}, {"GREPI", fun_grepi, 3, 0, CA_PUBLIC}, {"GT", fun_gt, 2, 0, CA_PUBLIC}, {"GTE", fun_gte, 2, 0, CA_PUBLIC}, {"HASATTR", fun_hasattr, 2, 0, CA_PUBLIC}, {"HASATTRP", fun_hasattrp, 2, 0, CA_PUBLIC}, {"HASFLAG", fun_hasflag, 2, 0, CA_PUBLIC}, {"HASPOWER", fun_haspower, 2, 0, CA_PUBLIC}, {"HASTYPE", fun_hastype, 2, 0, CA_PUBLIC}, {"HOME", fun_home, 1, 0, CA_PUBLIC}, {"IADD", fun_iadd, 0, FN_VARARGS, CA_PUBLIC}, {"IDIV", fun_div, 2, 0, CA_PUBLIC}, {"IDLE", fun_idle, 1, 0, CA_PUBLIC}, {"IFELSE", fun_ifelse, 3, FN_NO_EVAL, CA_PUBLIC}, {"IMUL", fun_imul, 0, FN_VARARGS, CA_PUBLIC}, {"INC", fun_inc, 1, 0, CA_PUBLIC}, {"INDEX", fun_index, 4, 0, CA_PUBLIC}, {"INSERT", fun_insert, 0, FN_VARARGS, CA_PUBLIC}, {"INZONE", fun_inzone, 1, 0, CA_PUBLIC}, {"ISDBREF", fun_isdbref, 1, 0, CA_PUBLIC}, {"ISNUM", fun_isnum, 1, 0, CA_PUBLIC}, {"ISUB", fun_isub, 2, 0, CA_PUBLIC}, {"ISWORD", fun_isword, 1, 0, CA_PUBLIC}, {"ITER", fun_iter, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"LAST", fun_last, 0, FN_VARARGS, CA_PUBLIC}, {"LATTR", fun_lattr, 1, 0, CA_PUBLIC}, {"LCON", fun_lcon, 1, 0, CA_PUBLIC}, {"LCSTR", fun_lcstr, -1, 0, CA_PUBLIC}, {"LDELETE", fun_ldelete, 0, FN_VARARGS, CA_PUBLIC}, {"LEXITS", fun_lexits, 1, 0, CA_PUBLIC}, {"LPARENT", fun_lparent, 1, 0, CA_PUBLIC}, {"LIST", fun_list, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"LIT", fun_lit, 1, FN_NO_EVAL, CA_PUBLIC}, {"LJUST", fun_ljust, 0, FN_VARARGS, CA_PUBLIC}, {"LINK", fun_link, 2, 0, CA_PUBLIC}, {"LN", fun_ln, 1, 0, CA_PUBLIC}, {"LNUM", fun_lnum, 0, FN_VARARGS, CA_PUBLIC}, {"LOC", fun_loc, 1, 0, CA_PUBLIC}, {"LOCATE", fun_locate, 3, 0, CA_PUBLIC}, {"LOCK", fun_lock, 1, 0, CA_PUBLIC}, {"LOG", fun_log, 1, 0, CA_PUBLIC}, {"LPOS", fun_lpos, 2, 0, CA_PUBLIC}, {"LSTACK", fun_lstack, 0, FN_VARARGS, CA_PUBLIC}, {"LT", fun_lt, 2, 0, CA_PUBLIC}, {"LTE", fun_lte, 2, 0, CA_PUBLIC}, {"LWHO", fun_lwho, 0, 0, CA_PUBLIC}, {"MAIL", fun_mail, 0, FN_VARARGS, CA_PUBLIC}, {"MAILFROM", fun_mailfrom, 0, FN_VARARGS, CA_PUBLIC}, {"MAP", fun_map, 0, FN_VARARGS, CA_PUBLIC}, {"MATCH", fun_match, 0, FN_VARARGS, CA_PUBLIC}, {"MATCHALL", fun_matchall, 0, FN_VARARGS, CA_PUBLIC}, {"MAX", fun_max, 0, FN_VARARGS, CA_PUBLIC}, {"MEMBER", fun_member, 0, FN_VARARGS, CA_PUBLIC}, {"MERGE", fun_merge, 3, 0, CA_PUBLIC}, {"MID", fun_mid, 3, 0, CA_PUBLIC}, {"MIN", fun_min, 0, FN_VARARGS, CA_PUBLIC}, {"MIX", fun_mix, 0, FN_VARARGS, CA_PUBLIC}, {"MOD", fun_mod, 2, 0, CA_PUBLIC}, {"MONEY", fun_money, 1, 0, CA_PUBLIC}, {"MOTD", fun_motd, 0, 0, CA_PUBLIC}, {"MUDNAME", fun_mudname, 0, 0, CA_PUBLIC}, {"MUL", fun_mul, 0, FN_VARARGS, CA_PUBLIC}, {"MUNGE", fun_munge, 0, FN_VARARGS, CA_PUBLIC}, {"NAME", fun_name, 1, 0, CA_PUBLIC}, {"NEARBY", fun_nearby, 2, 0, CA_PUBLIC}, {"NEQ", fun_neq, 2, 0, CA_PUBLIC}, {"NEXT", fun_next, 1, 0, CA_PUBLIC}, {"NOT", fun_not, 1, 0, CA_PUBLIC}, {"NUM", fun_num, 1, 0, CA_PUBLIC}, {"ITEMS", fun_items, 0, FN_VARARGS, CA_PUBLIC}, {"OBJ", fun_obj, 1, 0, CA_PUBLIC}, {"OBJEVAL", fun_objeval, 2, FN_NO_EVAL, CA_PUBLIC}, {"OBJMEM", fun_objmem, 1, 0, CA_PUBLIC}, {"OR", fun_or, 0, FN_VARARGS, CA_PUBLIC}, {"ORFLAGS", fun_orflags, 2, 0, CA_PUBLIC}, {"OWNER", fun_owner, 1, 0, CA_PUBLIC}, {"PARENT", fun_parent, 1, 0, CA_PUBLIC}, {"PARSE", fun_parse, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"PEEK", fun_peek, 0, FN_VARARGS, CA_PUBLIC}, {"PEMIT", fun_pemit, 2, 0, CA_PUBLIC}, {"PI", fun_pi, 0, 0, CA_PUBLIC}, {"PLAYMEM", fun_playmem, 1, 0, CA_PUBLIC}, {"PMATCH", fun_pmatch, 1, 0, CA_PUBLIC}, {"POLL", fun_poll, 0, 0, CA_PUBLIC}, {"POP", fun_pop, 0, FN_VARARGS, CA_PUBLIC}, {"PORTS", fun_ports, 1, 0, CA_PUBLIC}, {"POS", fun_pos, 2, 0, CA_PUBLIC}, {"POSS", fun_poss, 1, 0, CA_PUBLIC}, {"POWER", fun_power, 2, 0, CA_PUBLIC}, {"PUSH", fun_push, 0, FN_VARARGS, CA_PUBLIC}, {"CASE", fun_case, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"R", fun_r, 1, 0, CA_PUBLIC}, {"RAND", fun_rand, 1, 0, CA_PUBLIC}, {"REGMATCH", fun_regmatch, 0, FN_VARARGS, CA_PUBLIC}, {"REMOVE", fun_remove, 0, FN_VARARGS, CA_PUBLIC}, {"REPEAT", fun_repeat, 2, 0, CA_PUBLIC}, {"REPLACE", fun_replace, 0, FN_VARARGS, CA_PUBLIC}, {"REST", fun_rest, 0, FN_VARARGS, CA_PUBLIC}, {"REVERSE", fun_reverse, -1, 0, CA_PUBLIC}, {"REVWORDS", fun_revwords, 0, FN_VARARGS, CA_PUBLIC}, {"RJUST", fun_rjust, 0, FN_VARARGS, CA_PUBLIC}, {"RLOC", fun_rloc, 2, 0, CA_PUBLIC}, {"ROOM", fun_room, 1, 0, CA_PUBLIC}, {"ROUND", fun_round, 2, 0, CA_PUBLIC}, {"S", fun_s, -1, 0, CA_PUBLIC}, {"SCRAMBLE", fun_scramble, 1, 0, CA_PUBLIC}, {"SEARCH", fun_search, -1, 0, CA_PUBLIC}, {"SECS", fun_secs, 0, FN_VARARGS, CA_PUBLIC}, {"SECURE", fun_secure, -1, 0, CA_PUBLIC}, {"SET", fun_set, 2, 0, CA_PUBLIC}, {"SETDIFF", fun_setdiff, 0, FN_VARARGS, CA_PUBLIC}, {"SETINTER", fun_setinter, 0, FN_VARARGS, CA_PUBLIC}, {"SETQ", fun_setq, 2, 0, CA_PUBLIC}, {"SETR", fun_setr, 2, 0, CA_PUBLIC}, {"SETUNION", fun_setunion, 0, FN_VARARGS, CA_PUBLIC}, {"SHL", fun_shl, 2, 0, CA_PUBLIC}, {"SHR", fun_shr, 2, 0, CA_PUBLIC}, {"SHUFFLE", fun_shuffle, 0, FN_VARARGS, CA_PUBLIC}, {"SIGN", fun_sign, 1, 0, CA_PUBLIC}, {"SIN", fun_sin, 1, 0, CA_PUBLIC}, {"SORT", fun_sort, 0, FN_VARARGS, CA_PUBLIC}, {"SORTBY", fun_sortby, 0, FN_VARARGS, CA_PUBLIC}, {"SPACE", fun_space, 1, 0, CA_PUBLIC}, {"SPLICE", fun_splice, 0, FN_VARARGS, CA_PUBLIC}, {"SQRT", fun_sqrt, 1, 0, CA_PUBLIC}, {"SQUISH", fun_squish, 1, 0, CA_PUBLIC}, {"STARTTIME",fun_starttime,0, 0, CA_PUBLIC}, {"STATS", fun_stats, 1, 0, CA_PUBLIC}, {"STRCAT", fun_strcat, 0, FN_VARARGS, CA_PUBLIC}, {"STRIPANSI",fun_stripansi,1, 0, CA_PUBLIC}, {"STRLEN", fun_strlen, -1, 0, CA_PUBLIC}, {"STRMATCH", fun_strmatch, 2, 0, CA_PUBLIC}, {"STRTRUNC", fun_strtrunc, 2, 0, CA_PUBLIC}, {"SUB", fun_sub, 2, 0, CA_PUBLIC}, {"SUBJ", fun_subj, 1, 0, CA_PUBLIC}, {"SWITCH", fun_switch, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"TAN", fun_tan, 1, 0, CA_PUBLIC}, {"TEL", fun_tel, 2, 0, CA_PUBLIC}, {"TIME", fun_time, 0, FN_VARARGS, CA_PUBLIC}, {"TRANSLATE",fun_translate,2, 0, CA_PUBLIC}, {"TRIM", fun_trim, 0, FN_VARARGS, CA_PUBLIC}, {"TRUNC", fun_trunc, 1, 0, CA_PUBLIC}, {"TYPE", fun_type, 1, 0, CA_PUBLIC}, {"U", fun_u, 0, FN_VARARGS, CA_PUBLIC}, {"UCSTR", fun_ucstr, -1, 0, CA_PUBLIC}, {"UDEFAULT", fun_udefault, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"ULOCAL", fun_ulocal, 0, FN_VARARGS, CA_PUBLIC}, {"V", fun_v, 1, 0, CA_PUBLIC}, {"VADD", fun_vadd, 0, FN_VARARGS, CA_PUBLIC}, {"VALID", fun_valid, 2, 0, CA_PUBLIC}, {"VCROSS", fun_vcross, 0, FN_VARARGS, CA_PUBLIC}, {"VDIM", fun_vdim, 0, FN_VARARGS, CA_PUBLIC}, {"VDOT", fun_vdot, 0, FN_VARARGS, CA_PUBLIC}, {"VERSION", fun_version, 0, 0, CA_PUBLIC}, {"VISIBLE", fun_visible, 2, 0, CA_PUBLIC}, {"VMAG", fun_vmag, 0, FN_VARARGS, CA_PUBLIC}, {"VMUL", fun_vmul, 0, FN_VARARGS, CA_PUBLIC}, {"VSUB", fun_vsub, 0, FN_VARARGS, CA_PUBLIC}, {"VUNIT", fun_vunit, 0, FN_VARARGS, CA_PUBLIC}, {"WHERE", fun_where, 1, 0, CA_PUBLIC}, {"WORDPOS", fun_wordpos, 0, FN_VARARGS, CA_PUBLIC}, {"WORDS", fun_words, 0, FN_VARARGS, CA_PUBLIC}, {"XGET", fun_xget, 2, 0, CA_PUBLIC}, {"XOR", fun_xor, 0, FN_VARARGS, CA_PUBLIC}, {"ZFUN", fun_zfun, 0, FN_VARARGS, CA_PUBLIC}, {"ZONE", fun_zone, 1, 0, CA_PUBLIC}, {"ZWHO", fun_zwho, 1, 0, CA_PUBLIC}, // Added by D.Piper (del@delphinian.com) 1997 and 2000-APR // {"DIGITTIME",fun_digittime,1, 0, CA_PUBLIC}, {"SINGLETIME", fun_singletime, 1, 0, CA_PUBLIC}, {"EXPTIME", fun_exptime, 1, 0, CA_PUBLIC}, {"WRITETIME",fun_writetime,1, 0, CA_PUBLIC}, {"CMDS", fun_cmds, 1, 0, CA_PUBLIC}, {"STARTSECS",fun_startsecs,0, 0, CA_PUBLIC}, {"LFLAGS", fun_lflags, 1, 0, CA_PUBLIC}, {"LATTRCMDS",fun_lattrcmds,1, 0, CA_PUBLIC}, {"LCMDS", fun_lcmds, 1, 0, CA_PUBLIC}, {"CONNTOTAL",fun_conntotal,1, 0, CA_PUBLIC}, {"CONNMAX", fun_connmax, 1, 0, CA_PUBLIC}, {"CONNLAST", fun_connlast, 1, 0, CA_PUBLIC}, {"CONNNUM", fun_connnum, 1, 0, CA_PUBLIC}, {"CONNLEFT", fun_connleft, 1, 0, CA_PUBLIC}, {NULL, NULL, 0, 0, 0} }; void NDECL(init_functab) { char *buff = alloc_sbuf("init_functab"); for (FUN *fp = flist; fp->name; fp++) { memcpy(buff, fp->name, SBUF_SIZE); buff[SBUF_SIZE-1] = '\0'; _strlwr(buff); hashaddLEN(buff, strlen(buff), (int *)fp, &mudstate.func_htab); } free_sbuf(buff); ufun_head = NULL; } void do_function(dbref player, dbref cause, int key, char *fname, char *target) { UFUN *ufp, *ufp2; ATTR *ap; char *np, *bp; int atr, aflags; dbref obj, aowner; if ((key & FN_LIST) || !fname || *fname == '\0') { notify(player, tprintf("%-28s %-8s %-30s Flgs", "Function Name", "DBref#", "Attribute")); notify(player, tprintf("%28s %8s %30s %4s", "----------------------------", "--------", "------------------------------", " -- ")); int count = 0; for (ufp2 = ufun_head; ufp2; ufp2 = ufp2->next) { ap = atr_num(ufp2->atr); notify(player, tprintf("%-28.28s #%-7d %-30.30s %c%c", ufp2->name, ufp2->obj, ap->name, ((ufp2->flags & FN_PRIV) ? 'W' : '-'), ((ufp2->flags & FN_PRES) ? 'p' : '-'))); count++; } notify(player, tprintf("%28s %8s %30s %4s", "----------------------------", "--------", "------------------------------", " -- ")); notify(player, tprintf("Total User-Defined Functions: %d", count)); return; } // Make a local uppercase copy of the function name. // bp = np = alloc_sbuf("add_user_func"); safe_sb_str(fname, np, &bp); *bp = '\0'; _strlwr(np); // Verify that the function doesn't exist in the builtin table. // if (hashfindLEN(np, strlen(np), &mudstate.func_htab) != NULL) { notify_quiet(player, "Function already defined in builtin function table."); free_sbuf(np); return; } // Make sure the target object exists. // if (!parse_attrib(player, target, &obj, &atr)) { notify_quiet(player, "I don't see that here."); free_sbuf(np); return; } // Make sure the attribute exists. // if (atr == NOTHING) { notify_quiet(player, "No such attribute."); free_sbuf(np); return; } // Make sure attribute is readably by me. // ap = atr_num(atr); if (!ap) { notify_quiet(player, "No such attribute."); free_sbuf(np); return; } atr_get_info(obj, atr, &aowner, &aflags); if (!See_attr(player, obj, ap, aowner, aflags)) { notify_quiet(player, NOPERM_MESSAGE); free_sbuf(np); return; } // Privileged functions require you control the obj. // if ((key & FN_PRIV) && !Controls(player, obj)) { notify_quiet(player, NOPERM_MESSAGE); free_sbuf(np); return; } // See if function already exists. If so, redefine it. // ufp = (UFUN *) hashfindLEN(np, strlen(np), &mudstate.ufunc_htab); if (!ufp) { ufp = (UFUN *) MEMALLOC(sizeof(UFUN)); ISOUTOFMEMORY(ufp); ufp->name = StringClone(np); _strupr(ufp->name); ufp->obj = obj; ufp->atr = atr; ufp->perms = CA_PUBLIC; ufp->next = NULL; if (!ufun_head) { ufun_head = ufp; } else { for (ufp2 = ufun_head; ufp2->next; ufp2 = ufp2->next) { // Nothing ; } ufp2->next = ufp; } hashaddLEN(np, strlen(np), (int *)ufp, &mudstate.ufunc_htab); } ufp->obj = obj; ufp->atr = atr; ufp->flags = key; free_sbuf(np); if (!Quiet(player)) { notify_quiet(player, tprintf("Function %s defined.", fname)); } } /* * --------------------------------------------------------------------------- * * list_functable: List available functions. */ void list_functable(dbref player) { FUN *fp; UFUN *ufp; char *buf, *bp, *cp; buf = alloc_lbuf("list_functable"); bp = buf; for (cp = (char *)"Functions:"; *cp; cp++) *bp++ = *cp; for (fp = flist; fp->name; fp++) { if (check_access(player, fp->perms)) { *bp++ = ' '; for (cp = (char *)(fp->name); *cp; cp++) *bp++ = *cp; } } for (ufp = ufun_head; ufp; ufp = ufp->next) { if (check_access(player, ufp->perms)) { *bp++ = ' '; for (cp = (char *)(ufp->name); *cp; cp++) *bp++ = *cp; } } *bp = '\0'; notify(player, buf); free_lbuf(buf); } /* * --------------------------------------------------------------------------- * * cf_func_access: set access on functions */ CF_HAND(cf_func_access) { FUN *fp; UFUN *ufp; char *ap; for (ap = str; *ap && !Tiny_IsSpace[(unsigned char)*ap]; ap++) ; if (*ap) *ap++ = '\0'; for (fp = flist; fp->name; fp++) { if (!string_compare(fp->name, str)) { return (cf_modify_bits(&fp->perms, ap, extra, player, cmd)); } } for (ufp = ufun_head; ufp; ufp = ufp->next) { if (!string_compare(ufp->name, str)) { return (cf_modify_bits(&ufp->perms, ap, extra, player, cmd)); } } cf_log_notfound(player, cmd, "Function", str); return -1; } // Some libraries go nuts...just because you force feed them lots of ASCII. // #define ATOF_LIMIT 100 double safe_atof(char *szString) { double ret; int n = strlen(szString); if (n > ATOF_LIMIT) { int ch = szString[ATOF_LIMIT-1]; szString[ATOF_LIMIT-1] = '\0'; ret = atof(szString); szString[ATOF_LIMIT-1] = ch; } else { ret = atof(szString); } return ret; } ///////////////////////////////////////////////////////////////// // Function : iadd(Arg[0], Arg[1],..,Arg[n]) // // Written by : Chris Rouse (Seraphim) 04/04/2000 ///////////////////////////////////////////////////////////////// FUNCTION(fun_iadd) { if (nfargs <= 0) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } INT64 sum = 0; for (int i = 0; i < nfargs; i++) { sum += Tiny_atoi64(fargs[i]); } safe_str(Tiny_i64toa_t(sum), buff, bufc); } ///////////////////////////////////////////////////////////////// // Function : isub(Arg[0], Arg[1]) // // Written by : Chris Rouse (Seraphim) 04/04/2000 ///////////////////////////////////////////////////////////////// FUNCTION(fun_isub) { INT64 diff = Tiny_atoi64(fargs[0]) - Tiny_atoi64(fargs[1]); safe_str(Tiny_i64toa_t(diff), buff, bufc); } ///////////////////////////////////////////////////////////////// // Function : imul(Arg[0], Arg[1], ... , Arg[n]) // // Written by : Chris Rouse (Seraphim) 04/04/2000 ///////////////////////////////////////////////////////////////// FUNCTION(fun_imul) { if (nfargs <= 0) { safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc); return; } INT64 prod = 1; for (int i = 0; i < nfargs; i++) { prod *= Tiny_atoi64(fargs[i]); } safe_str(Tiny_i64toa_t(prod), buff, bufc); } typedef struct { int iBase; char chLetter; int nName; char *pName; } RADIX_ENTRY; #define N_RADIX_ENTRIES 4 RADIX_ENTRY reTable[N_RADIX_ENTRIES] = { { 86400, 'd', 3, "day" }, { 3600, 'h', 4, "hour" }, { 60, 'm', 6, "minute" }, { 1, 's', 6, "second" } }; #define IDAYS 0 #define IHOURS 1 #define IMINUTES 2 #define ISECONDS 3 // This routine supports most of the time formats using the above // table. // void GeneralTimeConversion ( char *Buffer, long Seconds, int iStartBase, int iEndBase, BOOL bSingleTerm, BOOL bNames ) { char *p = Buffer; if (Seconds < 0) { Seconds = 0; } for (int i = iStartBase; i <= iEndBase; i++) { if (reTable[i].iBase <= Seconds || i == iEndBase) { int iValue; if (bSingleTerm) { // Round to the nearest. // iValue = (Seconds + (reTable[i].iBase >> 1))/reTable[i].iBase; } else { // Division and remainder. // iValue = Seconds/reTable[i].iBase; Seconds -= iValue * reTable[i].iBase; } if (iValue != 0 || i == iEndBase) { if (p != Buffer) { *p++ = ' '; } p += Tiny_ltoa(iValue, p); if (bNames) { // Use the names with the correct pluralization. // *p++ = ' '; memcpy(p, reTable[i].pName, reTable[i].nName); p += reTable[i].nName; if (iValue != 1) { // More than one or zero. // *p++ = 's'; } } else { *p++ = reTable[i].chLetter; } } if (bSingleTerm) { break; } } } *p++ = '\0'; } // This buffer is used by: // // time_format_1 (23 bytes) uses TimeBuffer64, // time_format_2 (17 bytes) uses TimeBuffer32, // expand_time (29 bytes) uses TimeBuffer32, // write_time (52 bytes) uses TimeBuffer64. // // time_format_1 and time_format_2 are called from within the same // printf, so they must use different buffers. // // We pick 32 and 64 as a round numbers. // static char TimeBuffer32[32]; static char TimeBuffer64[64]; // Show time in days, hours, and minutes // // 2^63/86400 is 1.07E14 which is at most 15 digits. // '(15)d (2):(2)\0' is at most 23 characters. // const char *time_format_1(int Seconds) { if (Seconds < 0) { Seconds = 0; } // We are showing the time in minutes, so round to the nearest // minute. // Seconds += 30; // Divide the time down into days, hours, and minutes. // int Days = Seconds / 86400; Seconds -= Days * 86400; int Hours = Seconds / 3600; Seconds -= Hours * 3600; int Minutes = Seconds / 60; if (Days > 0) { sprintf(TimeBuffer64, "%dd %02d:%02d", Days, Hours, Minutes); } else { sprintf(TimeBuffer64, "%02d:%02d", Hours, Minutes); } return TimeBuffer64; } // Show time in days, hours, minutes, or seconds. // const char *time_format_2(int Seconds) { // 2^63/86400 is 1.07E14 which is at most 15 digits. // '(15)d\0' is at most 17 characters. // GeneralTimeConversion(TimeBuffer32, Seconds, IDAYS, ISECONDS, TRUE, FALSE); return TimeBuffer32; } // Del's added functions for dooferMUX ! :) // D.Piper (del@delphinian.com) 1997 & 2000 // // expand_time - Written (short) time format. // const char *expand_time(int Seconds) { // 2^63/86400 is 1.07E14 which is at most 15 digits. // '(15)d (2)h (2)m (2)s\0' is at most 29 characters. // GeneralTimeConversion(TimeBuffer32, Seconds, IDAYS, ISECONDS, FALSE, FALSE); return TimeBuffer32; } // write_time - Written (long) time format. // const char *write_time(int Seconds) { // 2^63/86400 is 1.07E14 which is at most 15 digits. // '(15) days (2) hours (2) minutes (2) seconds\0' is at most // 52 characters. // GeneralTimeConversion(TimeBuffer64, Seconds, IDAYS, ISECONDS, FALSE, TRUE); return TimeBuffer64; } // digittime - Digital format time ([(days)d]HH:MM) from given // seconds. D.Piper - May 1997 & April 2000 // FUNCTION(fun_digittime) { int tt = Tiny_atol(fargs[0]); safe_str(time_format_1(tt), buff, bufc); } // singletime - Single element time from given seconds. // D.Piper - May 1997 & April 2000 // FUNCTION(fun_singletime) { int tt = Tiny_atol(fargs[0]); safe_str(time_format_2(tt), buff, bufc); } // exptime - Written (short) time from given seconds // D.Piper - May 1997 & April 2000 // FUNCTION(fun_exptime) { int tt = Tiny_atol(fargs[0]); safe_str(expand_time(tt), buff, bufc); } // writetime - Written (long) time from given seconds // D.Piper - May 1997 & April 2000 // FUNCTION(fun_writetime) { int tt = Tiny_atol(fargs[0]); safe_str(write_time(tt), buff, bufc); } // cmds - Return player command count (Wizard_Who OR Self ONLY) // D.Piper - May 1997 // FUNCTION(fun_cmds) { dbref target = lookup_player(player, fargs[0], 1); if (Good_obj(target) && Connected(target)) { if (!(Wizard_Who(player) || Controls(player, target))) { target = NOTHING; } safe_ltoa(fetch_cmds(target), buff, bufc, LBUF_SIZE-1); } else { safe_str("#-1 PLAYER NOT FOUND", buff, bufc); } } // startsecs - Time the MUX was started, in seconds // D.Piper - May 1997 & April 2000 // FUNCTION(fun_startsecs) { CLinearTimeAbsolute lta; lta = mudstate.start_time; lta.Local2UTC(); safe_str(lta.ReturnSecondsString(), buff, bufc); } // conntotal - Return player's total online time to the MUX // (including their current connection). D.Piper - May 1997 // FUNCTION(fun_conntotal) { dbref target = lookup_player(player, fargs[0], 1); if (Good_obj(target)) { long TotalTime = fetch_totaltime(target) + fetch_connect(target); safe_ltoa(TotalTime, buff, bufc, LBUF_SIZE-1); } else { safe_str("#-1 PLAYER NOT FOUND", buff, bufc); } } // connmax - Return player's longest session to the MUX // (including the current one). D.Piper - May 1997 // FUNCTION(fun_connmax) { dbref target = lookup_player(player, fargs[0], 1); if (Good_obj(target)) { long Longest = fetch_longestconnect(target); long Current = fetch_connect(target); if (Longest < Current) { Longest = Current; } safe_ltoa(Longest, buff, bufc, LBUF_SIZE-1); } else { safe_str("#-1 PLAYER NOT FOUND", buff, bufc); } } // connlast - Return player's last connection time to the MUX // D.Piper - May 1997 // FUNCTION(fun_connlast) { dbref target = lookup_player(player, fargs[0], 1); if (Good_obj(target)) { safe_ltoa(fetch_lastconnect(target), buff, bufc, LBUF_SIZE-1); } else { safe_str("#-1 PLAYER NOT FOUND", buff, bufc); } } // connnum - Return the total number of sessions this player has had // to the MUX (including the current one). D.Piper - May 1997 // FUNCTION(fun_connnum) { dbref target = lookup_player(player, fargs[0], 1); if (Good_obj(target)) { long NumConnections = fetch_numconnections(target) + 1; safe_ltoa(NumConnections, buff, bufc, LBUF_SIZE-1); } else { safe_str("#-1 PLAYER NOT FOUND", buff, bufc); } } // connleft - Return when a player last logged off the MUX as // UTC seconds. D.Piper - May 1997 // FUNCTION(fun_connleft) { dbref target = lookup_player(player, fargs[0], 1); if (Good_obj(target)) { CLinearTimeAbsolute cl = fetch_logouttime(target); safe_str(cl.ReturnSecondsString(), buff, bufc); } else { safe_str("#-1 PLAYER NOT FOUND", buff, bufc); } } // lattrcmds - Output a list of all attributes containing $ commands. // Altered from lattr(). D.Piper - May 1997 & April 2000 // FUNCTION(fun_lattrcmds) { dbref thing; int ca, first; ATTR *attr; // Check for wildcard matching. parse_attrib_wild checks for read // permission, so we don't have to. Have p_a_w assume the // slash-star if it is missing. // first = 1; olist_push(); if (parse_attrib_wild(player, fargs[0], &thing, 0, 0, 1)) { char *buf = alloc_lbuf("fun_lattrcmds"); for (ca = olist_first(); ca != NOTHING; ca = olist_next()) { attr = atr_num(ca); if (attr) { dbref aowner; int aflags; atr_get_str(buf, thing, attr->number, &aowner, &aflags); if (buf[0] == '$') { if (!first) { safe_chr(' ', buff, bufc); } first = 0; safe_str((char *)attr->name, buff, bufc); } } } free_lbuf(buf); } else { safe_str("#-1 NO MATCH", buff, bufc); } olist_pop(); } // lcmds - Output a list of all $ commands on an object. // Altered from MUX lattr(). D.Piper - May 1997 & April 2000 // FUNCTION(fun_lcmds) { dbref thing; int ca, first; ATTR *attr; // Check for wildcard matching. parse_attrib_wild checks for read // permission, so we don't have to. Have p_a_w assume the // slash-star if it is missing. // first = 1; olist_push(); if (parse_attrib_wild(player, fargs[0], &thing, 0, 0, 1)) { TINY_STRTOK_STATE tts; Tiny_StrTokControl(&tts, " *:"); char *buf = alloc_lbuf("fun_lattrcmds"); for (ca = olist_first(); ca != NOTHING; ca = olist_next()) { attr = atr_num(ca); if (attr) { dbref aowner; int aflags; atr_get_str(buf, thing, attr->number, &aowner, &aflags); if (buf[0] == '$') { if (!first) { safe_chr(' ', buff, bufc); } Tiny_StrTokString(&tts, buf+1); char *p = Tiny_StrTokParse(&tts); _strlwr(p); safe_str(p, buff, bufc); first = 0; } } } free_lbuf(buf); } else { safe_str("#-1 NO MATCH", buff, bufc); } olist_pop(); } extern FLAGENT gen_flags[]; // lflags - List flags as names - (modified from 'flag_description()' and // MUX flags(). D.Piper - May 1997 & May 2000 // FUNCTION(fun_lflags) { dbref target; FLAGENT *fp; FLAG fv; BOOL bFirst = TRUE; target = match_thing(player, fargs[0]); if ( (target != NOTHING) && (mudconf.pub_flags || Examinable(player, target) || (target == cause))) { for (fp = gen_flags; fp->flagname; fp++) { if (fp->flagflag & FLAG_WORD3) fv = Flags3(target); else if (fp->flagflag & FLAG_WORD2) fv = Flags2(target); else fv = Flags(target); if (fv & fp->flagvalue) { if ((fp->listperm & CA_WIZARD) && !Wizard(player)) continue; if ((fp->listperm & CA_GOD) && !God(player)) continue; if ( isPlayer(target) && (fp->flagvalue == CONNECTED) && (fp->flagflag & FLAG_WORD2) && ((Flags(target) & (WIZARD | DARK)) == (WIZARD | DARK)) && !Wizard(player)) { continue; } if (!bFirst) { safe_chr(' ', buff, bufc); } bFirst = FALSE; safe_str(fp->flagname, buff, bufc); } } } else { safe_str("#-1", buff, bufc); } }