/* functions.c - MUSH function handlers */ /* $Id: functions.c,v 1.135.2.2 2000/05/25 12:51:31 rmg Exp $ */ #include "copyright.h" #include "autoconf.h" #include <limits.h> #include <math.h> #include "externs.h" #include "misc.h" #include "attrs.h" #include "match.h" #include "command.h" #include "functions.h" #include "ansi.h" #ifdef FLOATING_POINTS #ifndef linux /* linux defines atof as a macro */ double atof(); #endif /* ! linux */ #define aton atof typedef double NVAL; #else #define aton atoi typedef int NVAL; #endif /* FLOATING_POINTS */ UFUN *ufun_head; static char space_buffer[LBUF_SIZE]; extern NAMETAB indiv_attraccess_nametab[]; extern void FDECL(cf_log_notfound, (dbref player, char *cmd, const char *thingname, char *thing)); extern dbref FDECL(get_programmer, (dbref)); extern char * FDECL(get_doing, (dbref)); extern dbref FDECL(find_connected_ambiguous, (dbref, char *)); extern void FDECL(cf_display, (dbref, char *, char *, char **)); extern INLINE int FDECL(safe_chr_real_fn, (char, char *, char **, int)); /* Function definitions from funceval.c */ #define XFUNCTION(x) \ extern void FDECL(x, (char *, char **, dbref, dbref, \ char *[], int, char *[], int)) #ifdef USE_COMSYS XFUNCTION(fun_cwho); #endif XFUNCTION(fun_beep); XFUNCTION(fun_ansi); XFUNCTION(fun_zone); XFUNCTION(fun_link); XFUNCTION(fun_tel); XFUNCTION(fun_pemit); XFUNCTION(fun_remit); XFUNCTION(fun_force); XFUNCTION(fun_trigger); XFUNCTION(fun_wait); XFUNCTION(fun_create); XFUNCTION(fun_set); XFUNCTION(fun_wipe); XFUNCTION(fun_command); XFUNCTION(fun_last); XFUNCTION(fun_matchall); XFUNCTION(fun_ports); XFUNCTION(fun_mix); XFUNCTION(fun_step); 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_hasattr); XFUNCTION(fun_hasattrp); XFUNCTION(fun_zwho); XFUNCTION(fun_inzone); XFUNCTION(fun_children); XFUNCTION(fun_encrypt); XFUNCTION(fun_decrypt); XFUNCTION(fun_objeval); XFUNCTION(fun_localize); XFUNCTION(fun_null); 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_nonzero); XFUNCTION(fun_inc); XFUNCTION(fun_dec); #ifdef USE_MAIL XFUNCTION(fun_mail); XFUNCTION(fun_mailfrom); #endif XFUNCTION(fun_die); XFUNCTION(fun_lrand); XFUNCTION(fun_lit); XFUNCTION(fun_shl); XFUNCTION(fun_shr); XFUNCTION(fun_band); XFUNCTION(fun_bor); XFUNCTION(fun_bnand); 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_toss); XFUNCTION(fun_items); XFUNCTION(fun_lstack); XFUNCTION(fun_dup); XFUNCTION(fun_popn); XFUNCTION(fun_swap); XFUNCTION(fun_x); XFUNCTION(fun_setx); XFUNCTION(fun_xvars); XFUNCTION(fun_let); XFUNCTION(fun_lvars); XFUNCTION(fun_clearvars); XFUNCTION(fun_regparse); XFUNCTION(fun_regmatch); XFUNCTION(fun_translate); XFUNCTION(fun_lastcreate); XFUNCTION(fun_structure); XFUNCTION(fun_construct); XFUNCTION(fun_load); XFUNCTION(fun_unload); XFUNCTION(fun_destruct); XFUNCTION(fun_unstructure); XFUNCTION(fun_z); XFUNCTION(fun_modify); XFUNCTION(fun_lstructures); XFUNCTION(fun_linstances); XFUNCTION(fun_sql); #ifdef PUEBLO_SUPPORT XFUNCTION(fun_html_escape); XFUNCTION(fun_html_unescape); XFUNCTION(fun_url_escape); XFUNCTION(fun_url_unescape); #endif /* PUEBLO SUPPORT */ #ifdef TCL_INTERP_SUPPORT XFUNCTION(fun_tclclear); XFUNCTION(fun_tcleval); XFUNCTION(fun_tclmodule); XFUNCTION(fun_tclparams); XFUNCTION(fun_tclregs); #endif /* TCL_INTERP_SUPPORT */ /* This is the prototype for functions */ #define FUNCTION(x) \ static void x(buff, bufc, player, cause, fargs, nfargs, cargs, ncargs) \ char *buff, **bufc; \ dbref player, cause; \ char *fargs[], *cargs[]; \ int nfargs, ncargs; /* --------------------------------------------------------------------------- * Trim off leading and trailing spaces if the separator char is a space */ char *trim_space_sep(str, sep) char *str, sep; { char *p; if (sep != ' ') return str; while (*str && (*str == ' ')) str++; for (p = str; *p; p++) ; for (p--; *p == ' ' && p > str; p--) ; p++; *p = '\0'; return str; } /* next_token: Point at start of next token in string */ char *next_token(str, sep) char *str, sep; { while (*str && (*str != sep)) str++; if (!*str) return NULL; str++; if (sep == ' ') { while (*str == sep) str++; } return str; } /* split_token: Get next token from string as null-term string. String is * destructively modified. */ char *split_token(sp, sep) char **sp, 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 == sep) str++; } } else { str = NULL; } *sp = str; return save; } dbref match_thing(player, name) dbref player; char *name; { init_match(player, name, NOTYPE); match_everything(MAT_EXIT_PARENTS); return (noisy_match_result()); } /* --------------------------------------------------------------------------- * List management utilities. */ #define ALPHANUM_LIST 1 #define NUMERIC_LIST 2 #define DBREF_LIST 3 #define FLOAT_LIST 4 static int autodetect_list(ptrs, nitems) 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)) { sort_type = DBREF_LIST; } else { return ALPHANUM_LIST; } } else { return ALPHANUM_LIST; } } else if (index(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)) return ALPHANUM_LIST; break; default: return ALPHANUM_LIST; } } return sort_type; } static int get_list_type(fargs, nfargs, type_pos, ptrs, nitems) char *fargs[], *ptrs[]; int nfargs, nitems, type_pos; { if (nfargs >= type_pos) { switch (ToLower(*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(arr, maxlen, list, sep) char *arr[], *list, sep; int maxlen; { 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(arr, alen, list, bufc, sep) char *arr[], **bufc, *list, sep; int alen; { int i; if (alen) { safe_str(arr[0], list, bufc); } for (i = 1; i < alen; i++) { print_sep(sep, list, bufc); safe_str(arr[i], list, bufc); } } static int dbnum(dbr) char *dbr; { if ((*dbr != '#') || (strlen(dbr) < 2)) return 0; else return atoi(dbr + 1); } /* --------------------------------------------------------------------------- * nearby_or_control: Check if player is near or controls thing */ int nearby_or_control(player, thing) dbref player, thing; { if (!Good_obj(player) || !Good_obj(thing)) return 0; if (Controls(player, thing)) return 1; if (!nearby(player, thing)) return 0; return 1; } /* --------------------------------------------------------------------------- * fval: copy the floating point value into a buffer and make it presentable */ #ifdef FLOATING_POINTS #define FP_SIZE ((sizeof(double) + sizeof(unsigned int) - 1) / sizeof(unsigned int)) #define FP_EXP_WEIRD 0x1 #define FP_EXP_ZERO 0x2 typedef union { double d; unsigned int u[FP_SIZE]; } fp_union_uint; static unsigned int fp_check_weird(buff, bufc, result) char *buff, **bufc; double result; { static fp_union_uint fp_sign_mask, fp_exp_mask, fp_mant_mask, fp_val; static const double d_zero = 0.0; static int fp_initted = 0; unsigned int fp_sign, fp_exp, fp_mant; int i; if (!fp_initted) { bzero(fp_sign_mask.u, sizeof(fp_sign_mask)); bzero(fp_exp_mask.u, sizeof(fp_exp_mask)); bzero(fp_val.u, sizeof(fp_val)); fp_exp_mask.d = 1.0 / d_zero; fp_sign_mask.d = -1.0 / fp_exp_mask.d; for (i = 0; i < FP_SIZE; i++) { fp_mant_mask.u[i] = ~(fp_exp_mask.u[i] | fp_sign_mask.u[i]); } fp_initted = 1; } fp_val.d = result; fp_sign = fp_mant = 0; fp_exp = FP_EXP_ZERO | FP_EXP_WEIRD; for (i = 0; (i < FP_SIZE) && fp_exp; i++) { if (fp_exp_mask.u[i]) { unsigned int x = (fp_exp_mask.u[i] & fp_val.u[i]); if (x == fp_exp_mask.u[i]) { /* these bits are all set. can't be zero * exponent, but could still be max (weird) * exponent. */ fp_exp &= ~FP_EXP_ZERO; } else if (x == 0) { /* none of these bits are set. can't be max * exponent, but could still be zero exponent. */ fp_exp &= ~FP_EXP_WEIRD; } else { /* some bits were set but not others. can't * be either zero exponent or max exponent. */ fp_exp = 0; } } fp_sign |= (fp_sign_mask.u[i] & fp_val.u[i]); fp_mant |= (fp_mant_mask.u[i] & fp_val.u[i]); } if (fp_exp == FP_EXP_WEIRD) { if (fp_sign) { safe_chr('-', buff, bufc); } if (fp_mant) { safe_known_str("NaN", 3, buff, bufc); } else { safe_known_str("Inf", 3, buff, bufc); } } return fp_exp; } static void fval(buff, bufc, result) char *buff, **bufc; double result; { char *p, *buf1; switch (fp_check_weird(buff, bufc, result)) { case FP_EXP_WEIRD: return; case FP_EXP_ZERO: result = 0.0; /* FALLTHRU */ default: break; } buf1 = *bufc; safe_tprintf_str(buff, bufc, "%.6f", result); /* get double val * into buffer */ **bufc = '\0'; p = (char *)rindex(buf1, '0'); if (p == NULL) { /* remove useless trailing 0's */ return; } else if (*(p + 1) == '\0') { while (*p == '0') { *p-- = '\0'; } *bufc = p + 1; } p = (char *)rindex(buf1, '.'); /* take care of dangling '.' */ if ((p != NULL) && (*(p + 1) == '\0')) { *p = '\0'; *bufc = p; } /* Handle bogus result of "-0" from sprintf. Yay, cclib. */ if (!strcmp(buf1, "-0")) { *buf1 = '0'; *bufc = buf1 + 1; } } #else #define fval(b,p,n) safe_ltos(b,p,n) #endif /* --------------------------------------------------------------------------- * Random number generator. This uses Whip's implementation of an algorithm * in the _Communications of the ACM_, Volume 31, Number 10, from the article * "Random Number Generators: Good Ones are Hard to Find" (S.K. Park, * K.W. Miller). */ double makerandom() { /* An int must be at least 32 bits. Don't change these constants. */ const unsigned int a = 16807; const unsigned int m = 2147482647; const unsigned int q = 127773; /* m div a */ const unsigned int r = 2836; /* m mod a */ unsigned int lo, hi; int test; static unsigned int seed = 0; /* This isn't the best seed in the world, but it's portable. There's * nothing truly random that's portable to get to, unfortunately. We're * going to adjust with the PID because any normal user can get the time * the MUSH started (or close to it) trivially, which would make the whole * sequence predictable. Using PID isn't much better, but it's portable, * and means you at least have to have machine access to figure it out * (or be a wizard). */ if (!seed) seed = time(NULL) + (int) getpid(); hi = seed / q; lo = seed % q; test = (a * lo) - (r * hi); if (test > 0) { seed = test; } else { seed = test + m; } return ((double) seed / m); } /* --------------------------------------------------------------------------- * fn_range_check: Check # of args to a function with an optional argument * for validity. */ int fn_range_check(fname, nfargs, minargs, maxargs, result, bufc) const char *fname; char *result, **bufc; int nfargs, minargs, maxargs; { 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(fargs, nfargs, sep_arg, sep, buff, bufc, eval, player, cause, cargs, ncargs, allow_special) char *fargs[], *cargs[], *sep, *buff, **bufc; int nfargs, ncargs, sep_arg, eval, allow_special; dbref player, cause; { char *tstr, *bp, *str; int tlen; if (nfargs >= sep_arg) { tlen = strlen(fargs[sep_arg - 1]); if (tlen <= 1) eval = 0; if (eval) { tstr = bp = alloc_lbuf("delim_check"); str = fargs[sep_arg - 1]; exec(tstr, &bp, 0, player, cause, EV_EVAL | EV_FCHECK, &str, cargs, ncargs); *bp = '\0'; if (allow_special && !strcmp(tstr, (char *) NULL_DELIM_VAR)) { *sep = '\0'; tlen = 1; } else if (allow_special && !strcmp(tstr, (char *) "\r\n")) { *sep = '\r'; tlen = 1; } else { tlen = strlen(tstr); *sep = *tstr; } free_lbuf(tstr); } if (tlen == 0) { *sep = ' '; } else if (allow_special && !eval && (tlen == 2) && !strcmp(fargs[sep_arg - 1], (char *) NULL_DELIM_VAR)) { *sep = '\0'; } else if (allow_special && !eval && (tlen == 2) && !strcmp(fargs[sep_arg - 1], (char *) "\r\n")) { *sep = '\r'; } else if (tlen != 1) { safe_str("#-1 SEPARATOR MUST BE ONE CHARACTER", buff, bufc); return 0; } else if (!eval) { *sep = *fargs[sep_arg - 1]; } } else { *sep = ' '; } return 1; } /* --------------------------------------------------------------------------- * fun_words: Returns number of words in a string. * Added 1/28/91 Philip D. Wasson */ int countwords(str, sep) char *str, 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_ltos(buff, bufc, countwords(fargs[0], sep)); } /* ------------------------------------------------------------------------ * 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_nothing(buff, bufc); return; } /* --------------------------------------------------------------------------- * fun_rand: Return a random number from 0 to arg1-1 */ FUNCTION(fun_rand) { int num; num = atoi(fargs[0]); if (num < 1) { safe_chr('0', buff, bufc); } else safe_tprintf_str(buff, bufc, "%ld", (long) (makerandom() * num)); } /* --------------------------------------------------------------------------- * fun_abs: Returns the absolute value of its argument. */ FUNCTION(fun_abs) { NVAL num; num = aton(fargs[0]); if (num == 0) { safe_chr('0', buff, bufc); } else if (num < 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) { NVAL num; num = aton(fargs[0]); if (num < 0) safe_known_str("-1", 2, buff, bufc); else if (num > 0) { safe_chr('1', buff, bufc); } else { safe_chr('0', buff, bufc); } } /* --------------------------------------------------------------------------- * fun_time: Returns nicely-formatted time. */ FUNCTION(fun_time) { char *temp; temp = (char *)ctime(&mudstate.now); temp[strlen(temp) - 1] = '\0'; safe_str(temp, buff, bufc); } /* --------------------------------------------------------------------------- * fun_time: Seconds since 0:00 1/1/70 */ FUNCTION(fun_secs) { safe_ltos(buff, bufc, mudstate.now); } /* --------------------------------------------------------------------------- * fun_convsecs: converts seconds to time string, based off 0:00 1/1/70 */ FUNCTION(fun_convsecs) { char *temp; time_t tt; tt = atol(fargs[0]); temp = (char *)ctime(&tt); temp[strlen(temp) - 1] = '\0'; safe_str(temp, buff, bufc); } /* --------------------------------------------------------------------------- * fun_convtime: converts time string to seconds, based off 0:00 1/1/70 * additional auxiliary function and table used to parse time string, * since no ANSI standard function are available to do this. */ static const char *monthtab[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; static const char daystab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* converts time string to a struct tm. Returns 1 on success, 0 on fail. * Time string format is always 24 characters long, in format * Ddd Mmm DD HH:MM:SS YYYY */ #define get_substr(buf, p) { \ p = (char *)index(buf, ' '); \ if (p) { \ *p++ = '\0'; \ while (*p == ' ') p++; \ } \ } int do_convtime(str, ttm) char *str; struct tm *ttm; { char *buf, *p, *q; int i; if (!str || !ttm) return 0; while (*str == ' ') str++; buf = p = alloc_sbuf("do_convtime"); /* make a temp copy of arg */ safe_sb_str(str, buf, &p); *p = '\0'; get_substr(buf, p); /* day-of-week or month */ if (!p || strlen(buf) != 3) { free_sbuf(buf); return 0; } for (i = 0; (i < 12) && string_compare(monthtab[i], p); i++) ; if (i == 12) { get_substr(p, q); /* month */ if (!q || strlen(p) != 3) { free_sbuf(buf); return 0; } for (i = 0; (i < 12) && string_compare(monthtab[i], p); i++) ; if (i == 12) { free_sbuf(buf); return 0; } p = q; } ttm->tm_mon = i; get_substr(p, q); /* day of month */ if (!q || (ttm->tm_mday = atoi(p)) < 1 || ttm->tm_mday > daystab[i]) { free_sbuf(buf); return 0; } p = (char *)index(q, ':'); /* hours */ if (!p) { free_sbuf(buf); return 0; } *p++ = '\0'; if ((ttm->tm_hour = atoi(q)) > 23 || ttm->tm_hour < 0) { free_sbuf(buf); return 0; } if (ttm->tm_hour == 0) { while (isspace(*q)) q++; if (*q != '0') { free_sbuf(buf); return 0; } } q = (char *)index(p, ':'); /* minutes */ if (!q) { free_sbuf(buf); return 0; } *q++ = '\0'; if ((ttm->tm_min = atoi(p)) > 59 || ttm->tm_min < 0) { free_sbuf(buf); return 0; } if (ttm->tm_min == 0) { while (isspace(*p)) p++; if (*p != '0') { free_sbuf(buf); return 0; } } get_substr(q, p); /* seconds */ if (!p || (ttm->tm_sec = atoi(q)) > 59 || ttm->tm_sec < 0) { free_sbuf(buf); return 0; } if (ttm->tm_sec == 0) { while (isspace(*q)) q++; if (*q != '0') { free_sbuf(buf); return 0; } } get_substr(p, q); /* year */ if ((ttm->tm_year = atoi(p)) == 0) { while (isspace(*p)) p++; if (*p != '0') { free_sbuf(buf); return 0; } } free_sbuf(buf); if (ttm->tm_year > 100) ttm->tm_year -= 1900; if (ttm->tm_year < 0) { return 0; } /* We don't whether or not it's daylight savings time. */ ttm->tm_isdst = -1; #define LEAPYEAR_1900(yr) ((yr)%400==100||((yr)%100!=0&&(yr)%4==0)) return (ttm->tm_mday != 29 || i != 1 || LEAPYEAR_1900(ttm->tm_year)); #undef LEAPYEAR_1900 } FUNCTION(fun_convtime) { struct tm *ttm; ttm = localtime(&mudstate.now); if (do_convtime(fargs[0], ttm)) safe_ltos(buff, bufc, timelocal(ttm)); else safe_known_str("-1", 2, buff, bufc); } /* --------------------------------------------------------------------------- * fun_starttime: What time did this system last reboot? */ FUNCTION(fun_starttime) { char *temp; temp = (char *)ctime(&mudstate.start_time); temp[strlen(temp) - 1] = '\0'; safe_str(temp, buff, bufc); } /* --------------------------------------------------------------------------- * fun_restarts: How many times have we restarted? */ FUNCTION(fun_restarts) { safe_ltos(buff, bufc, mudstate.reboot_nums); } /* --------------------------------------------------------------------------- * fun_restarttime: When did we last restart? */ FUNCTION(fun_restarttime) { char *temp; temp = (char *)ctime(&mudstate.restart_time); temp[strlen(temp) - 1] = '\0'; safe_str(temp, buff, bufc); } /* --------------------------------------------------------------------------- * fun_get, fun_get_eval: Get attribute from object. */ int check_read_perms(player, thing, attr, aowner, aflags, buff, bufc) dbref player, thing; ATTR *attr; int aowner, aflags; char *buff, **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_noperm(buff, bufc); return 0; } FUNCTION(fun_get) { dbref thing, aowner; int attrib, free_buffer, aflags, alen; ATTR *attr; char *atr_gotten; struct boolexp *bool; if (!parse_attrib(player, fargs[0], &thing, &attrib)) { safe_nomatch(buff, bufc); return; } if (attrib == NOTHING) { return; } free_buffer = 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, &alen); if (Read_attr(player, thing, attr, aowner, aflags)) { bool = parse_boolexp(player, atr_gotten, 1); free_lbuf(atr_gotten); atr_gotten = unparse_boolexp(player, bool); free_boolexp(bool); } else { free_lbuf(atr_gotten); atr_gotten = (char *)"#-1 PERMISSION DENIED"; } free_buffer = 0; } else { atr_gotten = atr_pget(thing, attrib, &aowner, &aflags, &alen); } /* 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_known_str(atr_gotten, alen, buff, bufc); else safe_str(atr_gotten, buff, bufc); } if (free_buffer) free_lbuf(atr_gotten); return; } FUNCTION(fun_xget) { dbref thing, aowner; int attrib, free_buffer, aflags, alen; ATTR *attr; char *atr_gotten; struct boolexp *bool; if (!*fargs[0] || !*fargs[1]) return; if (!parse_attrib(player, tprintf("%s/%s", fargs[0], fargs[1]), &thing, &attrib)) { safe_nomatch(buff, bufc); return; } if (attrib == NOTHING) { return; } free_buffer = 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, &alen); if (Read_attr(player, thing, attr, aowner, aflags)) { bool = parse_boolexp(player, atr_gotten, 1); free_lbuf(atr_gotten); atr_gotten = unparse_boolexp(player, bool); free_boolexp(bool); } else { free_lbuf(atr_gotten); atr_gotten = (char *)"#-1 PERMISSION DENIED"; } free_buffer = 0; } else { atr_gotten = atr_pget(thing, attrib, &aowner, &aflags, &alen); } /* 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_known_str(atr_gotten, alen, buff, bufc); else safe_str(atr_gotten, buff, bufc); } if (free_buffer) free_lbuf(atr_gotten); return; } FUNCTION(fun_get_eval) { dbref thing, aowner; int attrib, free_buffer, aflags, alen, eval_it; ATTR *attr; char *atr_gotten, *str; struct boolexp *bool; if (!parse_attrib(player, fargs[0], &thing, &attrib)) { safe_nomatch(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, &alen); if (Read_attr(player, thing, attr, aowner, aflags)) { bool = parse_boolexp(player, atr_gotten, 1); free_lbuf(atr_gotten); atr_gotten = unparse_boolexp(player, bool); free_boolexp(bool); } 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, &alen); } 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; exec(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; str = fargs[0]; exec(buff, bufc, 0, player, cause, 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, alen, eval_it; ATTR *attr; char *atr_gotten, *str; struct boolexp *bool; if ((nfargs != 1) && (nfargs != 2)) { safe_str("#-1 FUNCTION (EVAL) EXPECTS 1 OR 2 ARGUMENTS", buff, bufc); return; } if (nfargs == 1) { str = fargs[0]; exec(buff, bufc, 0, player, cause, EV_EVAL|EV_FCHECK, &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_nomatch(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, &alen); if (Read_attr(player, thing, attr, aowner, aflags)) { bool = parse_boolexp(player, atr_gotten, 1); free_lbuf(atr_gotten); atr_gotten = unparse_boolexp(player, bool); free_boolexp(bool); } 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, &alen); } 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; exec(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(buff, bufc, player, cause, fargs, nfargs, cargs, ncargs, is_local) char *buff, **bufc; dbref player, cause; char *fargs[], *cargs[]; int nfargs, ncargs, is_local; { dbref aowner, thing; int aflags, alen, anum, preserve_len[MAX_GLOBAL_REGS]; ATTR *ap; char *atext, *preserve[MAX_GLOBAL_REGS], *str; /* We need at least one argument */ if (nfargs < 1) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, 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, &alen); 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; exec(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_dbref(buff, bufc, Parent(it)); } else { safe_nothing(buff, bufc); } return; } /* --------------------------------------------------------------------------- * fun_mid: mid(foobar,2,3) returns oba */ FUNCTION(fun_mid) { char *s, *savep; int count, start, nchars, len, have_normal; s = fargs[0]; start = atoi(fargs[1]); nchars = atoi(fargs[2]); len = strlen(strip_ansi(s)); if ((start < 0) || (nchars < 0) || (start > LBUF_SIZE - 1) || (nchars > LBUF_SIZE - 1)) { safe_str("#-1 OUT OF RANGE", buff, bufc); return; } if ((start >= len) || (nchars == 0)) return; if (start + nchars > len) nchars = len - start; have_normal = 1; for (count = 0; *s && (count < start + nchars); ) { if (*s == ESC_CHAR) { /* Start of an ANSI code. Skip to the end. */ savep = s; while (*s && (*s != ANSI_END)) { safe_chr(*s, buff, bufc); s++; } if (*s) { safe_chr(*s, buff, bufc); s++; } if (!strncmp(savep, ANSI_NORMAL, 4)) have_normal = 1; else have_normal = 0; } else { if (count >= start) safe_chr(*s, buff, bufc); s++; count++; } } if (!have_normal) safe_ansi_normal(buff, bufc); } /* --------------------------------------------------------------------------- * 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_left: Returns first n characters in a string */ FUNCTION(fun_left) { char *s, *savep; int nchars, len, count, have_normal; s = fargs[0]; nchars = atoi(fargs[1]); len = strlen(strip_ansi(s)); if ((len < 1) || (nchars < 1)) return; have_normal = 1; for (count = 0; *s && (count < nchars); ) { if (*s == ESC_CHAR) { /* Start of an ANSI code. Skip to the end. */ savep = s; while (*s && (*s != ANSI_END)) { safe_chr(*s, buff, bufc); s++; } if (*s) { safe_chr(*s, buff, bufc); s++; } if (!strncmp(savep, ANSI_NORMAL, 4)) have_normal = 1; else have_normal = 0; } else { safe_chr(*s, buff, bufc); s++; count++; } } if (!have_normal) safe_ansi_normal(buff, bufc); } /* --------------------------------------------------------------------------- * fun_right: Returns last n characters in a string */ FUNCTION(fun_right) { char *s, *savep; int nchars, start, len, count, have_normal; s = fargs[0]; nchars = atoi(fargs[1]); len = strlen(strip_ansi(s)); start = len - nchars; if ((len < 1) || (nchars < 1)) return; if (nchars > len) start = 0; else start = len - nchars; have_normal = 1; for (count = 0; *s && (count < start + nchars); ) { if (*s == ESC_CHAR) { /* Start of an ANSI code. Skip to the end. */ savep = s; while (*s && (*s != ANSI_END)) { safe_chr(*s, buff, bufc); s++; } if (*s) { safe_chr(*s, buff, bufc); s++; } if (!strncmp(savep, ANSI_NORMAL, 4)) have_normal = 1; else have_normal = 0; } else { if (count >= start) safe_chr(*s, buff, bufc); s++; count++; } } if (!have_normal) safe_ansi_normal(buff, bufc); } /* --------------------------------------------------------------------------- * fun_chomp: If the line ends with a newline ('\r\n'), chop it off. */ FUNCTION(fun_chomp) { int len = strlen(fargs[0]); if ((fargs[0][len - 2] == '\r') && (fargs[0][len - 1] == '\n')) { fargs[0][len - 2] = '\0'; } safe_str(fargs[0], buff, bufc); } /* --------------------------------------------------------------------------- * fun_v: Function form of %-substitution */ FUNCTION(fun_v) { dbref aowner; int aflags, alen; char *sbuf, *sbufc, *tbuf, *str; ATTR *ap; tbuf = fargs[0]; if (isalpha(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 */ tbuf = atr_pget(player, ap->number, &aowner, &aflags, &alen); if (See_attr(player, player, ap, aowner, aflags)) safe_known_str(tbuf, alen, buff, bufc); 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; exec(buff, bufc, 0, player, cause, EV_FIGNORE, &str, cargs, ncargs); free_sbuf(sbuf); } /* --------------------------------------------------------------------------- * fun_s: Force substitution to occur. */ FUNCTION(fun_s) { char *str; str = fargs[0]; exec(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_dbref(buff, bufc, Contents(it)); return; } safe_nothing(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_dbref(buff, bufc, exit); } } } safe_nothing(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_dbref(buff, bufc, 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_dbref(buff, bufc, exit); return; } } } } } safe_nothing(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_dbref(buff, bufc, Location(it)); } else { safe_nothing(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_dbref(buff, bufc, where_is(it)); } else { safe_nothing(buff, bufc); } return; } /* --------------------------------------------------------------------------- * fun_rloc: Returns the recursed location of something (specifying #levels) */ FUNCTION(fun_rloc) { int i, levels; dbref it; levels = atoi(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_dbref(buff, bufc, it); return; } safe_nothing(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_dbref(buff, bufc, it); return; } } safe_nothing(buff, bufc); } else if (isRoom(it)) { safe_dbref(buff, bufc, it); } else { safe_nothing(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_dbref(buff, bufc, 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_ltos(buff, bufc, Controls(x, y)); } /* --------------------------------------------------------------------------- * fun_sees: Can X see Y in the normal Contents list of the room. If X * or Y do not exist, 0 is returned. */ FUNCTION(fun_sees) { dbref it, thing; int can_see_loc; if ((it = match_thing(player, fargs[0])) == NOTHING) { safe_chr('0', buff, bufc); return; } thing = match_thing(player, fargs[1]); if (!Good_obj(thing)) { safe_chr('0', buff, bufc); return; } can_see_loc = (!Dark(Location(thing)) || (mudconf.see_own_dark && Examinable(player, Location(thing)))); safe_ltos(buff, bufc, can_see(it, thing, can_see_loc)); } /* --------------------------------------------------------------------------- * 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_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_name(it, buff, bufc); if (isExit(it)) { for (s = temp; (s != *bufc) && (*s != ';'); s++) ; 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_ltos(buff, bufc, wcount); return; } wcount++; } while (s); safe_chr('0', buff, bufc); } FUNCTION(fun_strmatch) { /* Check if we match the whole string. If so, return 1 */ if (quick_wild(fargs[1], fargs[0])) { safe_chr('1', buff, bufc); } else { safe_chr('0', buff, bufc); } return; } /* --------------------------------------------------------------------------- * fun_streq: compare two strings case-insensitively. */ FUNCTION(fun_streq) { if (!string_compare(fargs[0], fargs[1])) { safe_chr('1', buff, bufc); } else { safe_chr('0', buff, bufc); } return; } /* --------------------------------------------------------------------------- * 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 = atoi(fargs[1]); len = atoi(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(arg) char *arg; { char *temp2; if (arg[0] == '#') { arg++; if (is_integer(arg)) { if (mudconf.bools_oldstyle) { switch (atoi(arg)) { case -1: return 0; case 0: return 0; default: return 1; } } else { return (atoi(arg) >= 0); } } if (mudconf.bools_oldstyle) { return 0; } else { /* Case of '#-1 <string>' */ return !((arg[0] == '-') && (arg[1] == '1') && (arg[2] == ' ')); } } temp2 = trim_space_sep(arg, ' '); if (!*temp2) return 0; if (is_integer(temp2)) return atoi(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 = atoi(fargs[2]); end = atoi(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 *)index(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 *)index(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_ltos(buff, bufc, (int)strlen((char *)strip_ansi(fargs[0]))); } FUNCTION(fun_num) { safe_dbref(buff, bufc, match_thing(player, fargs[0])); } FUNCTION(fun_pmatch) { char *name, *temp, *tp; dbref thing; dbref *p_ptr; /* If we have a valid dbref, it's okay if it's a player. */ if ((*fargs[0] == NUMBER_TOKEN) && fargs[0][1]) { thing = parse_dbref(fargs[0] + 1); if (Good_obj(thing) && isPlayer(thing)) { safe_dbref(buff, bufc, thing); } else { safe_nothing(buff, bufc); } return; } /* If we have *name, just advance past the *; it doesn't matter */ name = fargs[0]; if (*fargs[0] == LOOKUP_TOKEN) { name++; } /* Look up the full name */ tp = temp = alloc_lbuf("fun_pmatch"); safe_str(name, temp, &tp); for (tp = temp; *tp; tp++) *tp = ToLower(*tp); p_ptr = (int *) hashfind(temp, &mudstate.player_htab); free_lbuf(temp); if (p_ptr) { /* We've got it. Check to make sure it's a good object. */ if (Good_obj(*p_ptr) && isPlayer(*p_ptr)) { safe_dbref(buff, bufc, (int) *p_ptr); } else { safe_nothing(buff, bufc); } return; } /* We haven't found anything. Now we try a partial match. */ thing = find_connected_ambiguous(player, name); if (thing == AMBIGUOUS) { safe_known_str("#-2", 3, buff, bufc); } else if (Good_obj(thing) && isPlayer(thing)) { safe_dbref(buff, bufc, thing); } else { safe_nothing(buff, bufc); } } FUNCTION(fun_pfind) { dbref thing; if (*fargs[0] == '#') { safe_dbref(buff, bufc, match_thing(player, fargs[0])); return; } if (!((thing = lookup_player(player, fargs[0], 1)) == NOTHING)) { safe_dbref(buff, bufc, thing); return; } else safe_nomatch(buff, bufc); } /*------------------------------------------------------------------------- * Comparison functions. */ FUNCTION(fun_gt) { safe_ltos(buff, bufc, (aton(fargs[0]) > aton(fargs[1]))); } FUNCTION(fun_gte) { safe_ltos(buff, bufc, (aton(fargs[0]) >= aton(fargs[1]))); } FUNCTION(fun_lt) { safe_ltos(buff, bufc, (aton(fargs[0]) < aton(fargs[1]))); } FUNCTION(fun_lte) { safe_ltos(buff, bufc, (aton(fargs[0]) <= aton(fargs[1]))); } FUNCTION(fun_eq) { safe_ltos(buff, bufc, (aton(fargs[0]) == aton(fargs[1]))); } FUNCTION(fun_neq) { safe_ltos(buff, bufc, (aton(fargs[0]) != aton(fargs[1]))); } FUNCTION(fun_and) { int i; if (nfargs < 2) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { for (i = 0; (i < nfargs) && atoi(fargs[i]); i++) ; safe_ltos(buff, bufc, i == nfargs); } } FUNCTION(fun_or) { int i; if (nfargs < 2) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { for (i = 0; (i < nfargs) && !atoi(fargs[i]); i++) ; safe_ltos(buff, bufc, i != nfargs); } } FUNCTION(fun_xor) { int i, val; if (nfargs < 2) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { val = atoi(fargs[0]); for (i = 1; i < nfargs; i++) { if (val) { val = !atoi(fargs[i]); } else { val = atoi(fargs[i]); } } safe_ltos(buff, bufc, val ? 1 : 0); } return; } FUNCTION(fun_not) { safe_ltos(buff, bufc, !atoi(fargs[0])); } /*------------------------------------------------------------------------- * List-based numeric functions. */ FUNCTION(fun_ladd) { NVAL sum; char *cp, *curr, sep; varargs_preamble("LADD", 2); sum = 0; cp = trim_space_sep(fargs[0], sep); while (cp) { curr = split_token(&cp, sep); sum += aton(curr); } fval(buff, bufc, sum); } FUNCTION(fun_lor) { int i; char *cp, *curr, sep; varargs_preamble("LOR", 2); i = 0; cp = trim_space_sep(fargs[0], sep); while (cp && !i) { curr = split_token(&cp, sep); i = atoi(curr); } safe_ltos(buff, bufc, (i != 0)); } FUNCTION(fun_land) { int i; char *cp, *curr, sep; varargs_preamble("LAND", 2); i = 1; cp = trim_space_sep(fargs[0], sep); while (cp && i) { curr = split_token(&cp, sep); i = atoi(curr); } safe_ltos(buff, bufc, (i != 0)); } FUNCTION(fun_lorbool) { int i; char *cp, *curr, sep; varargs_preamble("LORBOOL", 2); i = 0; cp = trim_space_sep(fargs[0], sep); while (cp && !i) { curr = split_token(&cp, sep); i = xlate(curr); } safe_ltos(buff, bufc, (i != 0)); } FUNCTION(fun_landbool) { int i; char *cp, *curr, sep; varargs_preamble("LANDBOOL", 2); i = 1; cp = trim_space_sep(fargs[0], sep); while (cp && i) { curr = split_token(&cp, sep); i = xlate(curr); } safe_ltos(buff, bufc, (i != 0)); } FUNCTION(fun_lmax) { NVAL max, val; char *cp, *curr, sep; varargs_preamble("LMAX", 2); cp = trim_space_sep(fargs[0], sep); if (cp) { curr = split_token(&cp, sep); max = aton(curr); while (cp) { curr = split_token(&cp, sep); val = aton(curr); if (max < val) max = val; } fval(buff, bufc, max); } } FUNCTION(fun_lmin) { NVAL min, val; char *cp, *curr, sep; varargs_preamble("LMIN", 2); cp = trim_space_sep(fargs[0], sep); if (cp) { curr = split_token(&cp, sep); min = aton(curr); while (cp) { curr = split_token(&cp, sep); val = aton(curr); if (min > val) min = val; } fval(buff, bufc, min); } } /*------------------------------------------------------------------------- * True boolean functions. */ FUNCTION(fun_andbool) { int i; if (nfargs < 2) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { for (i = 0; (i < nfargs) && xlate(fargs[i]); i++) ; safe_ltos(buff, bufc, i == nfargs); } return; } FUNCTION(fun_orbool) { int i; if (nfargs < 2) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { for (i = 0; (i < nfargs) && !xlate(fargs[i]); i++) ; safe_ltos(buff, bufc, i != nfargs); } return; } FUNCTION(fun_xorbool) { int i, val; if (nfargs < 2) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { val = xlate(fargs[0]); for (i = 1; i < nfargs; i++) { if (val) { val = !xlate(fargs[i]); } else { val = xlate(fargs[i]); } } safe_ltos(buff, bufc, val ? 1 : 0); } return; } FUNCTION(fun_notbool) { safe_ltos(buff, bufc, !xlate(fargs[0])); } FUNCTION(fun_t) { safe_ltos(buff, bufc, xlate(fargs[0]) ? 1 : 0); } FUNCTION(fun_sqrt) { NVAL val; val = aton(fargs[0]); if (val < 0) { safe_str("#-1 SQUARE ROOT OF NEGATIVE", buff, bufc); } else if (val == 0) { safe_chr('0', buff, bufc); } else { fval(buff, bufc, sqrt((double)val)); } } FUNCTION(fun_add) { NVAL sum; int i; if (nfargs < 2) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { sum = aton(fargs[0]); for (i = 1; i < nfargs; i++) { sum += aton(fargs[i]); } fval(buff, bufc, sum); } return; } FUNCTION(fun_sub) { fval(buff, bufc, aton(fargs[0]) - aton(fargs[1])); } FUNCTION(fun_mul) { NVAL prod; int i; if (nfargs < 2) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { prod = aton(fargs[0]); for (i = 1; i < nfargs; i++) { prod *= aton(fargs[i]); } fval(buff, bufc, prod); } return; } FUNCTION(fun_floor) { #ifdef FLOATING_POINTS char *oldp; NVAL x; oldp = *bufc; x = floor(aton(fargs[0])); switch (fp_check_weird(buff, bufc, x)) { case FP_EXP_WEIRD: return; case FP_EXP_ZERO: x = 0.0; /* FALLTHRU */ default: break; } safe_tprintf_str(buff, bufc, "%.0f", x); /* Handle bogus result of "-0" from sprintf. Yay, cclib. */ if (!strcmp(oldp, "-0")) { *oldp = '0'; *bufc = oldp + 1; } #else fval(buff, bufc, aton(fargs[0])); #endif } FUNCTION(fun_ceil) { #ifdef FLOATING_POINTS char *oldp; NVAL x; oldp = *bufc; x = ceil(aton(fargs[0])); switch (fp_check_weird(buff, bufc, x)) { case FP_EXP_WEIRD: return; case FP_EXP_ZERO: x = 0.0; /* FALLTHRU */ default: break; } safe_tprintf_str(buff, bufc, "%.0f", x); /* Handle bogus result of "-0" from sprintf. Yay, cclib. */ if (!strcmp(oldp, "-0")) { *oldp = '0'; *bufc = oldp + 1; } #else fval(buff, bufc, aton(fargs[0])); #endif } FUNCTION(fun_round) { #ifdef FLOATING_POINTS const char *fstr; char *oldp; NVAL x; oldp = *bufc; switch (atoi(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; } x = aton(fargs[0]); switch (fp_check_weird(buff, bufc, x)) { case FP_EXP_WEIRD: return; case FP_EXP_ZERO: x = 0.0; /* FALLTHRU */ default: break; } safe_tprintf_str(buff, bufc, (char *)fstr, x); /* Handle bogus result of "-0" from sprintf. Yay, cclib. */ if (!strcmp(oldp, "-0")) { *oldp = '0'; *bufc = oldp + 1; } #else fval(buff, bufc, aton(fargs[0])); #endif } FUNCTION(fun_trunc) { #ifdef FLOATING_POINTS NVAL x; x = aton(fargs[0]); x = (x >= 0) ? floor(x) : ceil(x); switch (fp_check_weird(buff, bufc, x)) { case FP_EXP_WEIRD: return; case FP_EXP_ZERO: x = 0.0; /* FALLTHRU */ default: break; } fval(buff, bufc, x); #else fval(buff, bufc, aton(fargs[0])); #endif } FUNCTION(fun_div) { int bot; bot = atoi(fargs[1]); if (bot == 0) { safe_str("#-1 DIVIDE BY ZERO", buff, bufc); } else { safe_ltos(buff, bufc, (atoi(fargs[0]) / bot)); } } FUNCTION(fun_fdiv) { NVAL bot; bot = aton(fargs[1]); if (bot == 0) { safe_str("#-1 DIVIDE BY ZERO", buff, bufc); } else { fval(buff, bufc, (aton(fargs[0]) / bot)); } } FUNCTION(fun_mod) { int bot; bot = atoi(fargs[1]); if (bot == 0) bot = 1; safe_ltos(buff, bufc, atoi(fargs[0]) % bot); } FUNCTION(fun_pi) { safe_known_str("3.141592654", 11, buff, bufc); } FUNCTION(fun_e) { safe_known_str("2.718281828", 11, buff, bufc); } FUNCTION(fun_sin) { fval(buff, bufc, sin(aton(fargs[0]))); } FUNCTION(fun_cos) { fval(buff, bufc, cos(aton(fargs[0]))); } FUNCTION(fun_tan) { fval(buff, bufc, tan(aton(fargs[0]))); } FUNCTION(fun_exp) { fval(buff, bufc, exp(aton(fargs[0]))); } FUNCTION(fun_power) { NVAL val1, val2; val1 = aton(fargs[0]); val2 = aton(fargs[1]); if (val1 < 0) { safe_str("#-1 POWER OF NEGATIVE", buff, bufc); } else { fval(buff, bufc, pow(val1, val2)); } } FUNCTION(fun_ln) { NVAL val; val = aton(fargs[0]); if (val > 0) fval(buff, bufc, log(val)); else safe_str("#-1 LN OF NEGATIVE OR ZERO", buff, bufc); } FUNCTION(fun_log) { NVAL val, base; if (!fn_range_check("LOG", nfargs, 1, 2, buff, bufc)) return; val = aton(fargs[0]); if (nfargs == 2) base = aton(fargs[1]); else base = 10; if ((val <= 0) || (base <= 0)) safe_str("#-1 LOG OF NEGATIVE OR ZERO", buff, bufc); else if (base == 1) safe_str("#-1 DIVISION BY ZERO", buff, bufc); else fval(buff, bufc, log(val) / log(base)); } FUNCTION(fun_asin) { NVAL val; val = aton(fargs[0]); if ((val < -1) || (val > 1)) { safe_str("#-1 ASIN ARGUMENT OUT OF RANGE", buff, bufc); } else { fval(buff, bufc, asin(val)); } } FUNCTION(fun_acos) { NVAL val; val = aton(fargs[0]); if ((val < -1) || (val > 1)) { safe_str("#-1 ACOS ARGUMENT OUT OF RANGE", buff, bufc); } else { fval(buff, bufc, acos(val)); } } FUNCTION(fun_atan) { fval(buff, bufc, atan(aton(fargs[0]))); } FUNCTION(fun_dist2d) { int d; double r; d = atoi(fargs[0]) - atoi(fargs[2]); r = (double)(d * d); d = atoi(fargs[1]) - atoi(fargs[3]); r += (double)(d * d); d = (int)(sqrt(r) + 0.5); safe_ltos(buff, bufc, d); } FUNCTION(fun_dist3d) { int d; double r; d = atoi(fargs[0]) - atoi(fargs[3]); r = (double)(d * d); d = atoi(fargs[1]) - atoi(fargs[4]); r += (double)(d * d); d = atoi(fargs[2]) - atoi(fargs[5]); r += (double)(d * d); d = (int)(sqrt(r) + 0.5); safe_ltos(buff, bufc, d); } /* ------------------------------------------------------------------------ * Vector functions: VADD, VSUB, VMUL, VCROSS, VMAG, VUNIT, VDIM * Vectors are space-separated numbers. */ #define VADD_F 0 #define VSUB_F 1 #define VMUL_F 2 #define VDOT_F 3 #define VCROSS_F 4 static void handle_vectors(vecarg1, vecarg2, buff, bufc, sep, osep, flag) char *vecarg1; char *vecarg2; char *buff; char **bufc; char sep; char osep; int flag; { char *v1[LBUF_SIZE], *v2[LBUF_SIZE]; NVAL 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, vecarg1, sep); m = list2arr(v2, LBUF_SIZE, 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; } switch (flag) { case VADD_F: fval(buff, bufc, aton(v1[0]) + aton(v2[0])); for (i = 1; i < n; i++) { print_sep(osep, buff, bufc); fval(buff, bufc, aton(v1[i]) + aton(v2[i])); } return; /* NOTREACHED */ case VSUB_F: fval(buff, bufc, aton(v1[0]) - aton(v2[0])); for (i = 1; i < n; i++) { print_sep(osep, buff, bufc); fval(buff, bufc, aton(v1[i]) - aton(v2[i])); } return; /* NOTREACHED */ case VMUL_F: /* if n or m is 1, this is scalar multiplication. * otherwise, multiply elementwise. */ if (n == 1) { scalar = aton(v1[0]); fval(buff, bufc, aton(v2[0]) * scalar); for (i = 1; i < m; i++) { print_sep(osep, buff, bufc); fval(buff, bufc, aton(v2[i]) * scalar); } } else if (m == 1) { scalar = aton(v2[0]); fval(buff, bufc, aton(v1[0]) * scalar); for (i = 1; i < n; i++) { print_sep(osep, buff, bufc); fval(buff, bufc, aton(v1[i]) * scalar); } } else { /* vector elementwise product. * * Note this is a departure from TinyMUX, but an imitation * of the PennMUSH behavior: the documentation in Penn * claims it's a dot product, but the actual behavior * isn't. We implement dot product separately! */ fval(buff, bufc, aton(v1[0]) * aton(v2[0])); for (i = 1; i < n; i++) { print_sep(osep, buff, bufc); fval(buff, bufc, aton(v1[i]) * aton(v2[i])); } } return; /* NOTREACHED */ case VDOT_F: scalar = 0; for (i = 0; i < n; i++) { scalar += aton(v1[i]) * aton(v2[i]); } fval(buff, bufc, scalar); return; /* NOTREACHED */ 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 * * no cross product implementation yet: it would be * (a,b,c) x (d,e,f) = (bf - ce, cd - af, ae - bd) */ char sep, osep; svarargs_preamble("VDOT", 4); handle_vectors(fargs[0], fargs[1], buff, bufc, sep, osep, VDOT_F); } FUNCTION(fun_vmag) { char *v1[LBUF_SIZE]; int n, i; NVAL tmp, res = 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); /* * calculate the magnitude */ for (i = 0; i < n; i++) { tmp = aton(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]; int n, i; NVAL tmp, res = 0; char sep, osep; svarargs_preamble("VUNIT", 3); /* * 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); /* * calculate the magnitude */ for (i = 0; i < n; i++) { tmp = aton(v1[i]); res += tmp * tmp; } if (res <= 0) { safe_str("#-1 CAN'T MAKE UNIT VECTOR FROM ZERO-LENGTH VECTOR", buff, bufc); return; } res = sqrt(res); fval(buff, bufc, aton(v1[0]) / res); for (i = 1; i < n; i++) { print_sep(osep, buff, bufc); fval(buff, bufc, aton(v1[i]) / res); } } FUNCTION(fun_vdim) { char sep; if (nfargs == 0) { safe_chr('0', buff, bufc); } else { varargs_preamble("VDIM", 2); safe_ltos(buff, bufc, countwords(fargs[0], sep)); } } /* --------------------------------------------------------------------------- * fun_comp: string compare. */ FUNCTION(fun_comp) { int x; x = strcmp(fargs[0], fargs[1]); if (x > 0) { safe_chr('1', buff, bufc); } else if (x < 0) { safe_str("-1", buff, bufc); } else { safe_chr('0', buff, bufc); } } /* --------------------------------------------------------------------------- * fun_ncomp: numerical compare. */ FUNCTION(fun_ncomp) { NVAL x, y; x = aton(fargs[0]); y = aton(fargs[1]); if (x == y) { safe_chr('0', buff, bufc); } else if (x < y) { safe_str("-1", buff, bufc); } else { safe_chr('1', buff, bufc); } } /* --------------------------------------------------------------------------- * fun_xcon: Return a partial list of contents of an object, starting from * a specified element in the list and copying a specified number * of elements. */ FUNCTION(fun_xcon) { dbref thing, it; char *bb_p; int i, first, last; it = match_thing(player, fargs[0]); bb_p = *bufc; if ((it != NOTHING) && (Has_contents(it)) && (Examinable(player, it) || (Location(player) == it) || (it == cause))) { first = atoi(fargs[1]); last = atoi(fargs[2]); if ((first > 0) && (last > 0)) { /* Move to the first object that we want */ for (thing = Contents(it), i = 1; (i < first) && (thing != NOTHING) && (Next(thing) != thing); thing = Next(thing), i++) ; /* Grab objects until we reach the last one we want */ for (i = 0; (i < last) && (thing != NOTHING) && (Next(thing) != thing); thing = Next(thing), i++) { if (*bufc != bb_p) safe_chr(' ', buff, bufc); safe_dbref(buff, bufc, thing); } } } else safe_nothing(buff, bufc); } /* --------------------------------------------------------------------------- * fun_lcon: Return a list of contents. */ FUNCTION(fun_lcon) { dbref thing, it; char *bb_p; it = match_thing(player, fargs[0]); bb_p = *bufc; if ((it != NOTHING) && (Has_contents(it)) && (Examinable(player, it) || (Location(player) == it) || (it == cause))) { DOLIST(thing, Contents(it)) { if (*bufc != bb_p) safe_chr(' ', buff, bufc); safe_dbref(buff, bufc, thing); } } else safe_nothing(buff, bufc); } /* --------------------------------------------------------------------------- * fun_lexits: Return a list of exits. */ FUNCTION(fun_lexits) { dbref thing, it, parent; char *bb_p; int exam, lev, key; it = match_thing(player, fargs[0]); if (!Good_obj(it) || !Has_exits(it)) { safe_nothing(buff, bufc); return; } exam = Examinable(player, it); if (!exam && (where_is(player) != it) && (it != cause)) { safe_nothing(buff, bufc); return; } /* Return info for all parent levels */ bb_p = *bufc; 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 (*bufc != bb_p) safe_chr(' ', buff, bufc); safe_dbref(buff, bufc, thing); } } } 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_nothing(buff, bufc); } else if (Has_home(it)) { safe_dbref(buff, bufc, Home(it)); } else if (Has_dropto(it)) { safe_dbref(buff, bufc, Dropto(it)); } else if (isExit(it)) { safe_dbref(buff, bufc, where_is(it)); } else { safe_nothing(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_nothing(buff, bufc); else safe_ltos(buff, bufc, Pennies(it)); } /* --------------------------------------------------------------------------- * fun_pos: Find a word in a string */ FUNCTION(fun_pos) { int i = 1; char *s, *t, *u; i = 1; s = strip_ansi(fargs[1]); while (*s) { u = s; t = fargs[0]; while (*t && *t == *u) ++t, ++u; if (*t == '\0') { safe_ltos(buff, bufc, i); return; } ++i, ++s; } safe_nothing(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) { char *s, *bb_p; char c, tbuf[8]; int i; if (!fargs[0] || !*fargs[0]) return; c = (char) *(fargs[1]); if (!c) c = ' '; bb_p = *bufc; for (i = 0, s = strip_ansi(fargs[0]); *s; i++, s++) { if (*s == c) { if (*bufc != bb_p) { safe_chr(' ', buff, bufc); } ltos(tbuf, i); safe_str(tbuf, buff, bufc); } } } /* --------------------------------------------------------------------------- * 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(buff, bufc, str, el, word, sep, flag) char *buff, **bufc, *str, *word, sep; int el, flag; { int ct, overrun; char *sptr, *iptr, *eptr; 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; } /* we can't fiddle with anything before the first position */ if (el < 1) { safe_str(str, buff, bufc); 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; if (!str || !*str) { eptr = NULL; iptr = NULL; } else { eptr = trim_space_sep(str, sep); iptr = split_token(&eptr, sep); } } else { /* Break off 'before' portion */ sptr = eptr = trim_space_sep(str, sep); overrun = 1; for (ct = el; ct > 2 && eptr; eptr = next_token(eptr, sep), ct--) ; if (eptr) { overrun = 0; iptr = split_token(&eptr, sep); } /* 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_str(str, buff, bufc); return; } /* Split the 'target' word from the 'after' portion. */ if (eptr) iptr = split_token(&eptr, sep); else iptr = NULL; } switch (flag) { case IF_DELETE: /* deletion */ if (sptr) { safe_str(sptr, buff, bufc); if (eptr) safe_chr(sep, buff, bufc); } if (eptr) { safe_str(eptr, buff, bufc); } break; case IF_REPLACE: /* replacing */ if (sptr) { safe_str(sptr, buff, bufc); safe_chr(sep, buff, bufc); } safe_str(word, buff, bufc); if (eptr) { safe_chr(sep, buff, bufc); safe_str(eptr, buff, bufc); } break; case IF_INSERT: /* insertion */ if (sptr) { safe_str(sptr, buff, bufc); safe_chr(sep, buff, bufc); } safe_str(word, buff, bufc); if (iptr) { safe_chr(sep, buff, bufc); safe_str(iptr, buff, bufc); } if (eptr) { safe_chr(sep, buff, bufc); safe_str(eptr, buff, bufc); } 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], atoi(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], atoi(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], atoi(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 (index(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_ltos(buff, bufc, wcount); return; } wcount++; } while (s); safe_chr('0', buff, bufc); } /* --------------------------------------------------------------------------- * fun_secure, fun_escape: escape [, ], %, \, and the beginning of the string. */ FUNCTION(fun_secure) { char *s; s = fargs[0]; while (*s) { switch (*s) { case '%': case '$': case '\\': case '[': case ']': case '(': case ')': case '{': case '}': case ',': case ';': safe_chr(' ', buff, bufc); break; default: safe_chr(*s, buff, bufc); } s++; } } FUNCTION(fun_escape) { char *s, *d; d = *bufc; s = fargs[0]; while (*s) { switch (*s) { case '%': case '\\': case '[': case ']': case '{': case '}': case ';': safe_chr('\\', buff, bufc); default: if (*bufc == d) safe_chr('\\', buff, bufc); safe_chr(*s, buff, bufc); } s++; } } /* Take a character position and return which word that char is in. * wordpos(<string>, <charpos>) */ FUNCTION(fun_wordpos) { int charpos, i; char *cp, *tp, *xp, sep; varargs_preamble("WORDPOS", 3); charpos = atoi(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_ltos(buff, bufc, i); return; } safe_nothing(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_known_str("ROOM", 4, buff, bufc); break; case TYPE_EXIT: safe_known_str("EXIT", 4, buff, bufc); break; case TYPE_PLAYER: safe_known_str("PLAYER", 6, buff, bufc); break; case TYPE_THING: safe_known_str("THING", 5, buff, bufc); break; default: safe_str("#-1 ILLEGAL TYPE", buff, bufc); } return; } /*--------------------------------------------------------------------------- * fun_hasflag: plus auxiliary function atr_has_flag. */ static int atr_has_flag(player, thing, attr, aowner, aflags, flagname) dbref player, thing; ATTR *attr; int aowner, aflags; char *flagname; { if (!See_attr(player, thing, attr, aowner, aflags)) return 0; else { if (string_prefix("dark", flagname)) return (aflags & AF_DARK); else if (string_prefix("wizard", flagname)) return (aflags & AF_WIZARD); else if (string_prefix("hidden", flagname)) return (aflags & AF_MDARK); else if (string_prefix("html", flagname)) return (aflags & AF_HTML); else if (string_prefix("locked", flagname)) return (aflags & AF_LOCK); else if (string_prefix("no_command", flagname)) return (aflags & AF_NOPROG); else if (string_prefix("no_parse", flagname)) return (aflags & AF_NOPARSE); else if (string_prefix("regexp", flagname)) return (aflags & AF_REGEXP); else if (string_prefix("god", flagname)) return (aflags & AF_GOD); else if (string_prefix("visual", flagname)) return (aflags & AF_VISUAL); else if (string_prefix("no_inherit", flagname)) return (aflags & AF_PRIVATE); else return 0; } } FUNCTION(fun_hasflag) { dbref it, aowner; int atr, aflags; ATTR *ap; if (parse_attrib(player, fargs[0], &it, &atr)) { if (atr == NOTHING) { safe_str("#-1 NOT FOUND", buff, bufc); } else { ap = atr_num(atr); atr_pget_info(it, atr, &aowner, &aflags); if (atr_has_flag(player, it, ap, aowner, aflags, fargs[1])) { safe_chr('1', buff, bufc); } else { safe_chr('0', buff, bufc); } } } else { 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)) { if (has_flag(player, it, fargs[1])) { safe_chr('1', buff, bufc); } else { safe_chr('0', buff, bufc); } } else { safe_noperm(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)) { if (has_power(player, it, fargs[1])) { safe_chr('1', buff, bufc); } else { safe_chr('0', buff, bufc); } } else { safe_noperm(buff, bufc); } } FUNCTION(fun_delete) { char *s, *savep; int count, start, nchars, len, have_normal; s = fargs[0]; start = atoi(fargs[1]); nchars = atoi(fargs[2]); len = strlen(strip_ansi(s)); if ((start >= len) || (nchars <= 0)) { safe_str(s, buff, bufc); return; } have_normal = 1; for (count = 0; *s && (count < len); ) { if (*s == ESC_CHAR) { /* Start of an ANSI code. Skip to the end. */ savep = s; while (*s && (*s != ANSI_END)) { safe_chr(*s, buff, bufc); s++; } if (*s) { safe_chr(*s, buff, bufc); s++; } if (!strncmp(savep, ANSI_NORMAL, 4)) have_normal = 1; else have_normal = 0; } else { if ((count >= start) && (count < start + nchars)) { s++; } else { safe_chr(*s, buff, bufc); s++; } count++; } } if (!have_normal) safe_ansi_normal(buff, bufc); } FUNCTION(fun_lock) { dbref it, aowner; int aflags, alen; char *tbuf; ATTR *attr; struct boolexp *bool; /* 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, &alen); if (Read_attr(player, it, attr, aowner, aflags)) { bool = parse_boolexp(player, tbuf, 1); free_lbuf(tbuf); tbuf = (char *)unparse_boolexp_function(player, bool); free_boolexp(bool); safe_str(tbuf, buff, bufc); } else free_lbuf(tbuf); } FUNCTION(fun_elock) { dbref it, victim, aowner; int aflags, alen; char *tbuf; ATTR *attr; struct boolexp *bool; /* 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, &alen); if ((aflags & AF_IS_LOCK) || Read_attr(player, it, attr, aowner, aflags)) { if (Pass_Locks(victim)) { safe_chr('1', buff, bufc); } else { bool = parse_boolexp(player, tbuf, 1); safe_ltos(buff, bufc, eval_boolexp(victim, it, it, bool)); free_boolexp(bool); } } 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_programmer: Returns the dbref or #1- of an object in a @program. */ FUNCTION(fun_programmer) { dbref target; target = lookup_player(player, fargs[0], 1); if (!Good_obj(target) || !Connected(target) || !Examinable(player, target)) { safe_nothing(buff, bufc); return; } safe_dbref(buff, bufc, get_programmer(target)); } /* --------------------------------------------------------------------------- * fun_doing: Returns a user's doing. */ FUNCTION(fun_doing) { dbref target; char *str; target = lookup_player(player, fargs[0], 1); if (!Good_obj(target) || !Connected(target)) return; if ((str = get_doing(target)) != NULL) safe_str(get_doing(target), buff, bufc); } /* --------------------------------------------------------------------------- * fun_nearby: Return whether or not obj1 is near obj2. */ FUNCTION(fun_nearby) { dbref obj1, obj2; obj1 = match_thing(player, fargs[0]); obj2 = match_thing(player, fargs[1]); if (!(nearby_or_control(player, obj1) || nearby_or_control(player, obj2))) { safe_chr('0', buff, bufc); } else if (nearby(obj1, obj2)) { safe_chr('1', buff, bufc); } else { safe_chr('0', buff, bufc); } } /* --------------------------------------------------------------------------- * fun_obj, fun_poss, and fun_subj: perform pronoun sub for object. */ static void process_sex(player, what, token, buff, bufc) dbref player; char *what, *buff, **bufc; const char *token; { dbref it; char *str; it = match_thing(player, what); if (!Good_obj(it) || (!isPlayer(it) && !nearby_or_control(player, it))) { safe_nomatch(buff, bufc); } else { str = (char *)token; exec(buff, bufc, 0, it, it, 0, &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); } /* --------------------------------------------------------------------------- * fun_lcstr, fun_ucstr, fun_capstr: Lowercase, uppercase, or capitalize str. */ FUNCTION(fun_lcstr) { char *ap; ap = fargs[0]; while (*ap && ((*bufc - buff) < LBUF_SIZE - 1)) { **bufc = ToLower(*ap); ap++; (*bufc)++; } } FUNCTION(fun_ucstr) { char *ap; ap = fargs[0]; while (*ap && ((*bufc - buff) < LBUF_SIZE - 1)) { **bufc = ToUpper(*ap); ap++; (*bufc)++; } } FUNCTION(fun_capstr) { char *s; s = *bufc; safe_str(fargs[0], buff, bufc); *s = ToUpper(*s); } /* --------------------------------------------------------------------------- * fun_lnum: Return a list of numbers. */ FUNCTION(fun_lnum) { char tbuf[12], sep; int bot, top, over, i; char *bb_p; if (nfargs == 0) { return; } /* lnum() is special, since its single delimiter is really an output * delimiter. */ if (!fn_range_check("LNUM", nfargs, 1, 3, buff, bufc)) return; if (!delim_check(fargs, nfargs, 3, &sep, buff, bufc, 0, player, cause, cargs, ncargs, 1)) return; if (nfargs >= 2) { bot = atoi(fargs[0]); top = atoi(fargs[1]); } else { bot = 0; top = atoi(fargs[0]); if (top-- < 1) /* still want to generate if arg is 1 */ return; } over = 0; bb_p = *bufc; if (top == bot) { safe_ltos(buff, bufc, bot); return; } else if (top > bot) { for (i = bot; (i <= top) && !over; i++) { if (*bufc != bb_p) { print_sep(sep, buff, bufc); } ltos(tbuf, i); over = safe_str(tbuf, buff, bufc); } } else { for (i = bot; (i >= top) && !over; i--) { if (*bufc != bb_p) { print_sep(sep, buff, bufc); } ltos(tbuf, i); over = safe_str(tbuf, buff, bufc); } } } /* --------------------------------------------------------------------------- * 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 { if (! mudconf.lattr_oldstyle) safe_nomatch(buff, bufc); } olist_pop(); return; } /* --------------------------------------------------------------------------- * do_reverse, fun_reverse, fun_revwords: Reverse things. */ static void do_reverse(from, to) char *from, *to; { char *tp; tp = to + strlen(from); *tp-- = '\0'; while (*from) { *tp-- = *from++; } } FUNCTION(fun_reverse) { /* Nasty bounds checking */ if (strlen(fargs[0]) >= LBUF_SIZE - (*bufc - buff) - 1) { *(fargs[0] + (LBUF_SIZE - (*bufc - buff) - 1)) = '\0'; } do_reverse(fargs[0], *bufc); *bufc += strlen(fargs[0]); } FUNCTION(fun_revwords) { char *temp, *tp, *t1, sep; int first; /* If we are passed an empty arglist return a null string */ if (nfargs == 0) { return; } varargs_preamble("REVWORDS", 2); temp = alloc_lbuf("fun_revwords"); /* Nasty bounds checking */ if (strlen(fargs[0]) >= LBUF_SIZE - (*bufc - buff) - 1) { *(fargs[0] + (LBUF_SIZE - (*bufc - buff) - 1)) = '\0'; } /* Reverse the whole string */ do_reverse(fargs[0], temp); /* Now individually reverse each word in the string. This will * undo the reversing of the words (so the words themselves are * forwards again. */ tp = temp; first = 1; while (tp) { if (!first) safe_chr(sep, buff, bufc); t1 = split_token(&tp, sep); do_reverse(t1, *bufc); *bufc += strlen(t1); first = 0; } free_lbuf(temp); } /* --------------------------------------------------------------------------- * fun_after, fun_before: Return substring after or before a specified string. */ FUNCTION(fun_after) { char *bp, *cp, *mp; int mlen; if (nfargs == 0) { return; } if (!fn_range_check("AFTER", nfargs, 1, 2, buff, bufc)) return; bp = fargs[0]; mp = fargs[1]; /* Sanity-check arg1 and arg2 */ if (bp == NULL) bp = ""; if (mp == NULL) mp = " "; if (!mp || !*mp) mp = (char *)" "; mlen = strlen(mp); 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 *)index(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 *bp, *cp, *mp, *ip; int mlen; if (nfargs == 0) { return; } if (!fn_range_check("BEFORE", nfargs, 1, 2, buff, bufc)) return; bp = fargs[0]; mp = fargs[1]; /* Sanity-check arg1 and arg2 */ if (bp == NULL) bp = ""; if (mp == NULL) mp = " "; if (!mp || !*mp) mp = (char *)" "; mlen = strlen(mp); 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 *)index(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) { int i; NVAL max, val; if (nfargs < 1) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { max = aton(fargs[0]); for (i = 0; i < nfargs; i++) { val = aton(fargs[i]); max = (max < val) ? val : max; } fval(buff, bufc, max); } return; } FUNCTION(fun_min) { int i; NVAL min, val; if (nfargs < 1) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); } else { min = aton(fargs[0]); for (i = 0; i < nfargs; i++) { val = aton(fargs[i]); min = (min > val) ? val : min; } fval(buff, bufc, min); } return; } /* --------------------------------------------------------------------------- * 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) sprintf(nbuf, "#%d", thing); else sprintf(nbuf, " #%d", thing); 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, *bb_p, sep, osep; int words, i; svarargs_preamble("SPLICE", 5); /* 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]; bb_p = *bufc; for (i = 0; i < words; i++) { p2 = split_token(&p1, sep); q2 = split_token(&q1, sep); if (*bufc != bb_p) { print_sep(osep, buff, bufc); } if (!strcmp(p2, fargs[2])) safe_str(q2, buff, bufc); /* replace */ else safe_str(p2, buff, bufc); /* copy */ } } /* --------------------------------------------------------------------------- * fun_repeat: repeats a string */ FUNCTION(fun_repeat) { int times, len, i; char *max; times = atoi(fargs[1]); if ((times < 1) || (fargs[0] == NULL) || (!*fargs[0])) { return; } else if (times == 1) { safe_str(fargs[0], buff, bufc); } else { /* We must check all of these things rather than just * multiplying length by times, because of the possibility * of an integer overflow. */ len = strlen(fargs[0]); if ((len > LBUF_SIZE - 1) || (times > LBUF_SIZE - 1) || (len * times > LBUF_SIZE - 1)) { safe_str("#-1 STRING TOO LONG", buff, bufc); } else { max = buff + LBUF_SIZE; for (i = 0; i < times && (*bufc < max); i++) safe_known_str(fargs[0], len, buff, bufc); } } } /* --------------------------------------------------------------------------- * fun_loop and fun_parse exist for reasons of backwards compatibility. * See notes on fun_iter for the explanation. */ static void perform_loop(buff, bufc, player, cause, list, exprstr, cargs, ncargs, sep, osep, flag) char *buff, **bufc; dbref player, cause; char *list, *exprstr; char *cargs[]; int ncargs; char sep, osep; int flag; /* 0 is parse(), 1 is loop() */ { char *curr, *objstring, *buff2, *buff3, *cp, *dp, *str, *result, *bb_p; char tbuf[8]; int number = 0; dp = cp = curr = alloc_lbuf("perform_loop.1"); str = list; exec(curr, &dp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *dp = '\0'; cp = trim_space_sep(cp, sep); if (!*cp) { free_lbuf(curr); return; } bb_p = *bufc; while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { if (!flag && (*bufc != bb_p)) { print_sep(osep, buff, bufc); } number++; objstring = split_token(&cp, sep); buff2 = replace_string(BOUND_VAR, objstring, exprstr); ltos(tbuf, number); buff3 = replace_string(LISTPLACE_VAR, tbuf, buff2); str = buff3; if (!flag) { exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } else { dp = result = alloc_lbuf("perform_loop.2"); exec(result, &dp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *dp = '\0'; notify(cause, result); free_lbuf(result); } free_lbuf(buff2); free_lbuf(buff3); } free_lbuf(curr); } FUNCTION(fun_parse) { char sep, osep; evarargs_preamble("PARSE", 2, 4); perform_loop(buff, bufc, player, cause, fargs[0], fargs[1], cargs, ncargs, sep, osep, 0); } FUNCTION(fun_loop) { char sep; varargs_preamble("LOOP", 3); perform_loop(buff, bufc, player, cause, fargs[0], fargs[1], cargs, ncargs, sep, ' ', 1); } /* --------------------------------------------------------------------------- * fun_iter() and fun_list() parse an expression, substitute elements of * a list, one at a time, using the '##' replacement token. Uses of these * functions can be nested. * In older versions of MUSH, these functions could not be nested. * parse() and loop() exist for reasons of backwards compatibility, * since the peculiarities of the way substitutions were done in the string * replacements make it necessary to provide some way of doing backwards * compatibility, in order to avoid breaking a lot of code that relies upon * particular patterns of necessary escaping. */ static void perform_iter(buff, bufc, player, cause, list, exprstr, cargs, ncargs, sep, osep, flag) char *buff, **bufc; dbref player, cause; char *list, *exprstr; char *cargs[]; int ncargs; char sep, osep; int flag; /* 0 is iter(), 1 is list() */ { char *list_str, *lp, *str, *input_p, *bb_p, *save_token, *work_buf; char *dp, *result; int save_num; /* The list argument is unevaluated. Go evaluate it. */ input_p = lp = list_str = alloc_lbuf("perform_iter.list"); str = list; exec(list_str, &lp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *lp = '\0'; input_p = trim_space_sep(input_p, sep); if (!*input_p) { free_lbuf(list_str); return; } mudstate.in_loop++; save_token = mudstate.loop_token; save_num = mudstate.loop_number; mudstate.loop_number = 0; bb_p = *bufc; while (input_p && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { if (!flag && (*bufc != bb_p)) { print_sep(osep, buff, bufc); } mudstate.loop_token = split_token(&input_p, sep); mudstate.loop_number++; work_buf = alloc_lbuf("perform_iter.eval"); strcpy(work_buf, exprstr); /* we might nibble this */ str = work_buf; if (!flag) { exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } else { dp = result = alloc_lbuf("perform_iter.out"); exec(result, &dp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *dp = '\0'; notify(cause, result); free_lbuf(result); } free_lbuf(work_buf); } free_lbuf(list_str); mudstate.loop_token = save_token; mudstate.loop_number = save_num; mudstate.in_loop--; } FUNCTION(fun_iter) { char sep, osep; evarargs_preamble("ITER", 2, 4); perform_iter(buff, bufc, player, cause, fargs[0], fargs[1], cargs, ncargs, sep, osep, 0); } FUNCTION(fun_list) { char sep; varargs_preamble("LIST", 3); perform_iter(buff, bufc, player, cause, fargs[0], fargs[1], cargs, ncargs, sep, ' ', 1); } /* --------------------------------------------------------------------------- * 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, alen, 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, &alen); 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"); strcpy(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; exec(result, &bp, 0, player, cause, EV_STRIP | 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; exec(result, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, clist, 2); *bp = '\0'; } rstore = result; result = NULL; while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { clist[0] = rstore; clist[1] = split_token(&cp, sep); strcpy(atextbuf, atext); result = bp = alloc_lbuf("fun_fold"); str = atextbuf; exec(result, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, clist, 2); *bp = '\0'; strcpy(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 */ static void handle_filter(player, cause, arg_func, arg_list, buff, bufc, sep, osep, flag) dbref player, cause; char *arg_func, *arg_list; char *buff; char **bufc; char sep, osep; int flag; /* 0 is filter(), 1 is filterbool() */ { dbref aowner, thing; int aflags, alen, anum; ATTR *ap; char *atext, *result, *curr, *objstring, *bp, *str, *cp, *atextbuf; char *bb_p; /* Two possibilities for the first arg: <obj>/<attr> and <attr>. */ if (parse_attrib(player, arg_func, &thing, &anum)) { if ((anum == NOTHING) || (!Good_obj(thing))) ap = NULL; else ap = atr_num(anum); } else { thing = player; ap = atr_str(arg_func); } /* 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, &alen); 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(arg_list, sep); atextbuf = alloc_lbuf("fun_filter"); bb_p = *bufc; while (cp) { objstring = split_token(&cp, sep); strcpy(atextbuf, atext); result = bp = alloc_lbuf("fun_filter"); str = atextbuf; exec(result, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, &objstring, 1); *bp = '\0'; if ((!flag && (*result == '1')) || (flag && xlate(result))) { if (*bufc != bb_p) { print_sep(osep, buff, bufc); } safe_str(objstring, buff, bufc); } free_lbuf(result); } free_lbuf(atext); free_lbuf(atextbuf); } FUNCTION(fun_filter) { char sep, osep; svarargs_preamble("FILTER", 4); handle_filter(player, cause, fargs[0], fargs[1], buff, bufc, sep, osep, 0); } FUNCTION(fun_filterbool) { char sep, osep; svarargs_preamble("FILTERBOOL", 4); handle_filter(player, cause, fargs[0], fargs[1], buff, bufc, sep, osep, 1); } /* --------------------------------------------------------------------------- * 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, alen, anum; ATTR *ap; char *atext, *objstring, *str, *cp, *atextbuf, *bb_p, sep, osep; svarargs_preamble("MAP", 4); /* If we don't have anything for a second arg, don't bother. */ if (!fargs[1] || !*fargs[1]) return; /* 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, &alen); 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"); bb_p = *bufc; while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { if (*bufc != bb_p) { print_sep(osep, buff, bufc); } objstring = split_token(&cp, sep); strcpy(atextbuf, atext); str = atextbuf; exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, &objstring, 1); } free_lbuf(atext); free_lbuf(atextbuf); } /* --------------------------------------------------------------------------- * fun_while: Evaluate a list until a termination condition is met: * while(EVAL_FN,CONDITION_FN,foo|flibble|baz|meep,1,|,-) * where EVAL_FN is "[strlen(%0)]" and CONDITION_FN is "[strmatch(%0,baz)]" * would result in '3-7-3' being returned. * The termination condition is an EXACT not wild match. */ FUNCTION(fun_while) { char sep, osep; dbref aowner1, thing1, aowner2, thing2; int aflags1, aflags2, anum1, anum2, alen1, alen2; int is_same; ATTR *ap; char *atext1, *atext2, *atextbuf, *condbuf; char *objstring, *cp, *str, *dp, *savep, *bb_p; svarargs_preamble("WHILE", 6); /* If our third arg is null (empty list), don't bother. */ if (!fargs[2] || !*fargs[2]) return; /* Our first and second args can be <obj>/<attr> or just <attr>. * Use them if we can access them, otherwise return an empty string. * * Note that for user-defined attributes, atr_str() returns a pointer * to a static, and that therefore we have to be careful about what * we're doing. */ if (parse_attrib(player, fargs[0], &thing1, &anum1)) { if ((anum1 == NOTHING) || !Good_obj(thing1)) ap = NULL; else ap = atr_num(anum1); } else { thing1 = player; ap = atr_str(fargs[0]); } if (!ap) return; atext1 = atr_pget(thing1, ap->number, &aowner1, &aflags1, &alen1); if (!*atext1 || !See_attr(player, thing1, ap, aowner1, aflags1)) { free_lbuf(atext1); return; } if (parse_attrib(player, fargs[1], &thing2, &anum2)) { if ((anum2 == NOTHING) || !Good_obj(thing2)) ap = NULL; else ap = atr_num(anum2); } else { thing2 = player; ap = atr_str(fargs[1]); } if (!ap) { free_lbuf(atext1); /* we allocated this, remember? */ return; } atext2 = atr_pget(thing2, ap->number, &aowner2, &aflags2, &alen2); if (!*atext2 || !See_attr(player, thing2, ap, aowner2, aflags2)) { free_lbuf(atext1); free_lbuf(atext2); return; } /* If our evaluation and condition are the same, we can save ourselves * some time later. */ if (!strcmp(atext1, atext2)) is_same = 1; else is_same = 0; /* Process the list one element at a time. */ cp = trim_space_sep(fargs[2], sep); atextbuf = alloc_lbuf("fun_while.eval"); if (!is_same) condbuf = alloc_lbuf("fun_while.cond"); bb_p = *bufc; while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { if (*bufc != bb_p) { print_sep(osep, buff, bufc); } objstring = split_token(&cp, sep); strcpy(atextbuf, atext1); str = atextbuf; savep = *bufc; exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, &objstring, 1); if (is_same) { if (!strcmp(savep, fargs[3])) break; } else { strcpy(condbuf, atext2); dp = str = savep = condbuf; exec(condbuf, &dp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, &objstring, 1); if (!strcmp(savep, fargs[3])) break; } } free_lbuf(atext1); free_lbuf(atext2); free_lbuf(atextbuf); if (!is_same) free_lbuf(condbuf); } /* --------------------------------------------------------------------------- * fun_edit: Edit text. */ FUNCTION(fun_edit) { char *tstr; edit_string(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_noperm(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_dbref(buff, bufc, what); } /* --------------------------------------------------------------------------- * fun_switch: Return value based on pattern matching (ala @switch/first) * fun_switchall: Similar, but ala @switch/all * fun_case: Like switch(), but a straight exact match instead of wildcard. * NOTE: These functions expect that their arguments have not been evaluated. */ FUNCTION(fun_switchall) { int i, got_one; char *mbuff, *tbuff, *bp, *str, *save_token; /* 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_switchall"); str = fargs[0]; exec(mbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; /* Loop through the patterns looking for a match */ mudstate.in_switch++; save_token = mudstate.switch_token; got_one = 0; for (i = 1; (i < nfargs - 1) && fargs[i] && fargs[i + 1]; i += 2) { tbuff = bp = alloc_lbuf("fun_switchall.2"); str = fargs[i]; exec(tbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; if (quick_wild(tbuff, mbuff)) { got_one = 1; free_lbuf(tbuff); mudstate.switch_token = mbuff; str = fargs[i+1]; exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } else { free_lbuf(tbuff); } } /* If we didn't match, return the default if there is one */ if (!got_one && (i < nfargs) && fargs[i]) { mudstate.switch_token = mbuff; str = fargs[i]; exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } free_lbuf(mbuff); mudstate.in_switch--; mudstate.switch_token = save_token; } FUNCTION(fun_switch) { int i; char *mbuff, *tbuff, *bp, *str, *save_token; /* 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]; exec(mbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; /* Loop through the patterns looking for a match */ mudstate.in_switch++; save_token = mudstate.switch_token; for (i = 1; (i < nfargs - 1) && fargs[i] && fargs[i + 1]; i += 2) { tbuff = bp = alloc_lbuf("fun_switch.2"); str = fargs[i]; exec(tbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; if (quick_wild(tbuff, mbuff)) { free_lbuf(tbuff); mudstate.switch_token = mbuff; str = fargs[i+1]; exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); free_lbuf(mbuff); mudstate.in_switch--; mudstate.switch_token = save_token; return; } free_lbuf(tbuff); } /* Nope, return the default if there is one */ if ((i < nfargs) && fargs[i]) { mudstate.switch_token = mbuff; str = fargs[i]; exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } free_lbuf(mbuff); mudstate.in_switch--; mudstate.switch_token = save_token; } 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_case"); str = fargs[0]; exec(mbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); *bp = '\0'; /* Loop through the patterns looking for a case-insensitive match */ for (i = 1; (i < nfargs - 1) && fargs[i] && fargs[i + 1]; i += 2) { if (!string_compare(fargs[i], mbuff)) { str = fargs[i + 1]; exec(buff, bufc, 0, player, cause, EV_STRIP | 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]; exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } return; } /* --------------------------------------------------------------------------- * fun_space: Make spaces. */ FUNCTION(fun_space) { int num, max; if (!fargs[0] || !(*fargs[0])) { num = 1; } else { num = atoi(fargs[0]); } if (num < 1) { /* If negative or zero spaces return a single space, * -except- allow 'space(0)' to return "" for calculated * padding */ if (!is_integer(fargs[0]) || (num != 0)) { num = 1; } } max = LBUF_SIZE - 1 - (*bufc - buff); num = (num > max) ? max : num; bcopy(space_buffer, *bufc, num); *bufc += num; **bufc = '\0'; } /* --------------------------------------------------------------------------- * 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) && Hidden(target) && !See_Hidden(player)) target = NOTHING; safe_ltos(buff, bufc, fetch_idle(target)); } FUNCTION(fun_conn) { dbref target; target = lookup_player(player, fargs[0], 1); if (Good_obj(target) && Hidden(target) && !See_Hidden(player)) target = NOTHING; safe_ltos(buff, bufc, fetch_connect(target)); } /* --------------------------------------------------------------------------- * 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 a_comp(s1, s2) const void *s1, *s2; { return strcmp(*(char **)s1, *(char **)s2); } static int f_comp(s1, s2) const void *s1, *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 i_comp(s1, s2) const void *s1, *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(s, n, sort_type) char *s[]; int n, 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 *) XMALLOC(n * sizeof(i_rec), "do_asort"); for (i = 0; i < n; i++) { ip[i].str = s[i]; ip[i].data = atoi(s[i]); } qsort((void *)ip, n, sizeof(i_rec), i_comp); for (i = 0; i < n; i++) { s[i] = ip[i].str; } free(ip); break; case DBREF_LIST: ip = (i_rec *) XMALLOC(n * sizeof(i_rec), "do_asort.2"); 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; } free(ip); break; case FLOAT_LIST: fp = (f_rec *) XMALLOC(n * sizeof(f_rec), "do_asort.3"); for (i = 0; i < n; i++) { fp[i].str = s[i]; fp[i].data = aton(s[i]); } qsort((void *)fp, n, sizeof(f_rec), f_comp); for (i = 0; i < n; i++) { s[i] = fp[i].str; } free(fp); break; } } FUNCTION(fun_sort) { int nitems, sort_type; char *list, sep, osep; char *ptrs[LBUF_SIZE / 2]; /* If we are passed an empty arglist return a null string */ if (nfargs == 0) { return; } if (!fn_range_check("SORT", nfargs, 1, 4, buff, bufc)) return; if (!delim_check(fargs, nfargs, 3, &sep, buff, bufc, 0, player, cause, cargs, ncargs, 0)) return; if (nfargs < 4) osep = sep; else if (!delim_check(fargs, nfargs, 4, &osep, buff, bufc, 0, player, cause, cargs, ncargs, 1)) return; /* Convert the list to an array */ list = alloc_lbuf("fun_sort"); strcpy(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, osep); 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(fargs, buff, bufc, oper, sep, osep) char *fargs[], *buff, **bufc, sep, osep; int oper; { char *list1, *list2, *oldp, *bb_p; char *ptrs1[LBUF_SIZE], *ptrs2[LBUF_SIZE]; int i1, i2, n1, n2, val; list1 = alloc_lbuf("fun_setunion.1"); strcpy(list1, fargs[0]); n1 = list2arr(ptrs1, LBUF_SIZE, list1, sep); do_asort(ptrs1, n1, ALPHANUM_LIST); list2 = alloc_lbuf("fun_setunion.2"); strcpy(list2, fargs[1]); n2 = list2arr(ptrs2, LBUF_SIZE, list2, sep); do_asort(ptrs2, n2, ALPHANUM_LIST); i1 = i2 = 0; bb_p = 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 (*bufc != bb_p) { print_sep(osep, buff, bufc); } 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 (*bufc != bb_p) { print_sep(osep, buff, bufc); } oldp = *bufc; safe_str(ptrs1[i1], buff, bufc); **bufc = '\0'; } } for (; i2 < n2; i2++) { if (strcmp(oldp, ptrs2[i2])) { if (*bufc != bb_p) { print_sep(osep, buff, bufc); } 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 (*bufc != bb_p) { print_sep(osep, buff, bufc); } 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 (*bufc != bb_p) { print_sep(osep, buff, bufc); } 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 (*bufc != bb_p) { print_sep(osep, buff, bufc); } 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, osep; svarargs_preamble("SETUNION", 4); handle_sets(fargs, buff, bufc, SET_UNION, sep, osep); return; } FUNCTION(fun_setdiff) { char sep, osep; svarargs_preamble("SETDIFF", 4); handle_sets(fargs, buff, bufc, SET_DIFF, sep, osep); return; } FUNCTION(fun_setinter) { char sep, osep; svarargs_preamble("SETINTER", 4); handle_sets(fargs, buff, bufc, SET_INTERSECT, sep, osep); return; } /* --------------------------------------------------------------------------- * rjust, ljust, center: Justify or center text, specifying fill character */ FUNCTION(fun_ljust) { int spaces, i, max; char *tp; char sep; varargs_preamble("LJUST", 3); spaces = atoi(fargs[1]) - strlen((char *)strip_ansi(fargs[0])); /* Sanitize number of spaces */ if (spaces <= 0) { /* no padding needed, just return string */ safe_str(fargs[0], buff, bufc); return; } safe_str(fargs[0], buff, bufc); tp = *bufc; max = LBUF_SIZE - 1 - (tp - buff); /* chars left in buffer */ spaces = (spaces > max) ? max : spaces; if (sep == ' ') { bcopy(space_buffer, tp, spaces); tp += spaces; } else { for (i = 0; i < spaces; i++) *tp++ = sep; } *tp = '\0'; *bufc = tp; } FUNCTION(fun_rjust) { int spaces, i, max; char *tp; char sep; varargs_preamble("RJUST", 3); spaces = atoi(fargs[1]) - strlen((char *)strip_ansi(fargs[0])); /* Sanitize number of spaces */ if (spaces <= 0) { /* no padding needed, just return string */ safe_str(fargs[0], buff, bufc); return; } tp = *bufc; max = LBUF_SIZE - 1 - (tp - buff); /* chars left in buffer */ spaces = (spaces > max) ? max : spaces; if (sep == ' ') { bcopy(space_buffer, tp, spaces); tp += spaces; } else { for (i = 0; i < spaces; i++) *tp++ = sep; } *bufc = tp; safe_str(fargs[0], buff, bufc); } FUNCTION(fun_center) { char sep; char *tp; int i, len, lead_chrs, trail_chrs, width, max; varargs_preamble("CENTER", 3); width = atoi(fargs[1]); len = strlen((char *)strip_ansi(fargs[0])); width = (width > LBUF_SIZE - 1) ? LBUF_SIZE - 1 : width; if (len >= width) { safe_str(fargs[0], buff, bufc); return; } lead_chrs = (width / 2) - (len / 2) + .5; tp = *bufc; max = LBUF_SIZE - 1 - (tp - buff); /* chars left in buffer */ lead_chrs = (lead_chrs > max) ? max : lead_chrs; if (sep == ' ') { bcopy(space_buffer, tp, lead_chrs); tp += lead_chrs; } else { for (i = 0; i < lead_chrs; i++) *tp++ = sep; } *bufc = tp; safe_str(fargs[0], buff, bufc); trail_chrs = width - lead_chrs - len; tp = *bufc; max = LBUF_SIZE - 1 - (tp - buff); trail_chrs = (trail_chrs > max) ? max : trail_chrs; if (sep == ' ') { bcopy(space_buffer, tp, trail_chrs); tp += trail_chrs; } else { for (i = 0; i < trail_chrs; i++) *tp++ = sep; } *tp = '\0'; *bufc = tp; } /* --------------------------------------------------------------------------- * setq, setr, r: set and read global registers. */ FUNCTION(fun_setq) { int regnum, len; regnum = atoi(fargs[0]); if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) { safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc); return; } if (!mudstate.global_regs[regnum]) mudstate.global_regs[regnum] = alloc_lbuf("fun_setq"); len = strlen(fargs[1]); bcopy(fargs[1], mudstate.global_regs[regnum], len + 1); mudstate.glob_reg_len[regnum] = len; } FUNCTION(fun_setr) { int regnum, len; regnum = atoi(fargs[0]); if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) { safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc); return; } if (!mudstate.global_regs[regnum]) mudstate.global_regs[regnum] = alloc_lbuf("fun_setr"); len = strlen(fargs[1]); bcopy(fargs[1], mudstate.global_regs[regnum], len + 1); mudstate.glob_reg_len[regnum] = len; safe_known_str(fargs[1], len, buff, bufc); } FUNCTION(fun_r) { int regnum; regnum = atoi(fargs[0]); if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) { safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc); } else if (mudstate.global_regs[regnum]) { safe_known_str(mudstate.global_regs[regnum], mudstate.glob_reg_len[regnum], buff, bufc); } } /* --------------------------------------------------------------------------- * isword: is every character in the argument a letter? */ FUNCTION(fun_isword) { char *p; for (p = fargs[0]; *p; p++) { if (!isalpha(*p)) { safe_chr('0', buff, bufc); return; } } safe_chr('1', buff, bufc); } /* --------------------------------------------------------------------------- * isnum: is the argument a number? */ FUNCTION(fun_isnum) { safe_chr((is_number(fargs[0]) ? '1' : '0'), buff, bufc); } /* --------------------------------------------------------------------------- * isdbref: is the argument a valid dbref? */ FUNCTION(fun_isdbref) { char *p; dbref dbitem; p = fargs[0]; if (*p++ == NUMBER_TOKEN) { if (*p) { /* just the string '#' won't do! */ dbitem = parse_dbref(p); if (Good_obj(dbitem)) { safe_chr('1', buff, bufc); return; } } } safe_chr('0', 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 (ToLower(*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); } /* --------------------------------------------------------------------------- * config: Display a MUSH config parameter. */ FUNCTION(fun_config) { cf_display(player, fargs[0], buff, bufc); } /* *INDENT-OFF* */ /* --------------------------------------------------------------------------- * 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}, {"ANDBOOL", fun_andbool, 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}, {"CAPSTR", fun_capstr, -1, 0, CA_PUBLIC}, {"CASE", fun_case, 0, FN_VARARGS|FN_NO_EVAL, 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}, {"CHILDREN", fun_children, 1, 0, CA_PUBLIC}, {"CHOMP", fun_chomp, 1, 0, CA_PUBLIC}, {"CLEARVARS", fun_clearvars, 0, 0, CA_PUBLIC}, {"COLUMNS", fun_columns, 0, FN_VARARGS, CA_PUBLIC}, {"COMMAND", fun_command, 0, FN_VARARGS, CA_PUBLIC}, {"COMP", fun_comp, 2, 0, CA_PUBLIC}, {"CON", fun_con, 1, 0, CA_PUBLIC}, {"CONFIG", fun_config, 1, 0, CA_PUBLIC}, {"CONN", fun_conn, 1, 0, CA_PUBLIC}, {"CONSTRUCT", fun_construct, 0, FN_VARARGS, CA_PUBLIC}, {"CONTROLS", fun_controls, 2, 0, CA_PUBLIC}, {"CONVSECS", fun_convsecs, 1, 0, CA_PUBLIC}, {"CONVTIME", fun_convtime, 1, 0, CA_PUBLIC}, {"COS", fun_cos, 1, 0, CA_PUBLIC}, {"CREATE", fun_create, 0, FN_VARARGS, CA_PUBLIC}, #ifdef USE_COMSYS {"CWHO", fun_cwho, 1, 0, CA_PUBLIC}, #endif {"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}, {"DESTRUCT", fun_destruct, 1, 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}, {"DUP", fun_dup, 0, FN_VARARGS, 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}, {"FILTERBOOL", fun_filterbool, 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}, {"FORCE", fun_force, 2, 0, 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}, #ifdef PUEBLO_SUPPORT {"HTML_ESCAPE", fun_html_escape,-1, 0, CA_PUBLIC}, {"HTML_UNESCAPE",fun_html_unescape,-1,0, CA_PUBLIC}, #endif /* PUEBLO_SUPPORT */ {"IDLE", fun_idle, 1, 0, CA_PUBLIC}, {"IFELSE", fun_ifelse, 3, FN_NO_EVAL, 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}, {"ISWORD", fun_isword, 1, 0, CA_PUBLIC}, {"ITEMS", fun_items, 0, FN_VARARGS, CA_PUBLIC}, {"ITER", fun_iter, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"LADD", fun_ladd, 0, FN_VARARGS, CA_PUBLIC}, {"LAND", fun_land, 0, FN_VARARGS, CA_PUBLIC}, {"LANDBOOL", fun_landbool, 0, FN_VARARGS, CA_PUBLIC}, {"LAST", fun_last, 0, FN_VARARGS, CA_PUBLIC}, {"LASTCREATE", fun_lastcreate, 2, 0, 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}, {"LEFT", fun_left, 2, 0, CA_PUBLIC}, {"LET", fun_let, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"LEXITS", fun_lexits, 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}, {"LINSTANCES", fun_linstances, 0, 0, CA_PUBLIC}, {"LMAX", fun_lmax, 0, FN_VARARGS, CA_PUBLIC}, {"LMIN", fun_lmin, 0, FN_VARARGS, CA_PUBLIC}, {"LN", fun_ln, 1, 0, CA_PUBLIC}, {"LNUM", fun_lnum, 0, FN_VARARGS, CA_PUBLIC}, {"LOAD", fun_load, 0, FN_VARARGS, CA_PUBLIC}, {"LOC", fun_loc, 1, 0, CA_PUBLIC}, {"LOCATE", fun_locate, 3, 0, CA_PUBLIC}, {"LOCALIZE", fun_localize, 1, FN_NO_EVAL, CA_PUBLIC}, {"LOCK", fun_lock, 1, 0, CA_PUBLIC}, {"LOG", fun_log, 0, FN_VARARGS, CA_PUBLIC}, {"LPARENT", fun_lparent, 1, 0, CA_PUBLIC}, {"LOOP", fun_loop, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"LOR", fun_lor, 0, FN_VARARGS, CA_PUBLIC}, {"LORBOOL", fun_lorbool, 0, FN_VARARGS, CA_PUBLIC}, {"LPOS", fun_lpos, 2, 0, CA_PUBLIC}, {"LRAND", fun_lrand, 0, FN_VARARGS, CA_PUBLIC}, {"LSTACK", fun_lstack, 0, FN_VARARGS, CA_PUBLIC}, {"LSTRUCTURES", fun_lstructures, 0, 0, CA_PUBLIC}, {"LT", fun_lt, 2, 0, CA_PUBLIC}, {"LTE", fun_lte, 2, 0, CA_PUBLIC}, {"LVARS", fun_lvars, 0, 0, CA_PUBLIC}, {"LWHO", fun_lwho, 0, 0, CA_PUBLIC}, #ifdef USE_MAIL {"MAIL", fun_mail, 0, FN_VARARGS, CA_PUBLIC}, {"MAILFROM", fun_mailfrom, 0, FN_VARARGS, CA_PUBLIC}, #endif {"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}, {"MODIFY", fun_modify, 3, 0, CA_PUBLIC}, {"MONEY", fun_money, 1, 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}, {"NCOMP", fun_ncomp, 2, 0, CA_PUBLIC}, {"NEARBY", fun_nearby, 2, 0, CA_PUBLIC}, {"NEQ", fun_neq, 2, 0, CA_PUBLIC}, {"NEXT", fun_next, 1, 0, CA_PUBLIC}, {"NONZERO", fun_nonzero, 3, FN_NO_EVAL, CA_PUBLIC}, {"NOT", fun_not, 1, 0, CA_PUBLIC}, {"NOTBOOL", fun_notbool, 1, 0, CA_PUBLIC}, {"NULL", fun_null, 1, 0, CA_PUBLIC}, {"NUM", fun_num, 1, 0, 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}, {"ORBOOL", fun_orbool, 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}, {"PFIND", fun_pfind, 1, 0, CA_PUBLIC}, {"PI", fun_pi, 0, 0, CA_PUBLIC}, {"PLAYMEM", fun_playmem, 1, 0, CA_PUBLIC}, {"PMATCH", fun_pmatch, 1, 0, CA_PUBLIC}, {"POP", fun_pop, 0, FN_VARARGS, CA_PUBLIC}, {"POPN", fun_popn, 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}, {"PROGRAMMER", fun_programmer, 1, 0, CA_PUBLIC}, {"PUSH", fun_push, 0, FN_VARARGS, CA_PUBLIC}, {"R", fun_r, 1, 0, CA_PUBLIC}, {"RAND", fun_rand, 1, 0, CA_PUBLIC}, {"REGMATCH", fun_regmatch, 0, FN_VARARGS, CA_PUBLIC}, {"REGPARSE", fun_regparse, 3, 0, CA_PUBLIC}, {"REMIT", fun_remit, 2, 0, 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}, {"RESTARTS", fun_restarts, 0, 0, CA_PUBLIC}, {"RESTARTTIME", fun_restarttime, 0, 0, CA_PUBLIC}, {"REVERSE", fun_reverse, -1, 0, CA_PUBLIC}, {"REVWORDS", fun_revwords, 0, FN_VARARGS, CA_PUBLIC}, {"RIGHT", fun_right, 2, 0, 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, 0, CA_PUBLIC}, {"SECURE", fun_secure, -1, 0, CA_PUBLIC}, {"SEES", fun_sees, 2, 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}, {"SETX", fun_setx, 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}, {"SQL", fun_sql, 0, FN_VARARGS, CA_SQL_OK}, {"SQRT", fun_sqrt, 1, 0, CA_PUBLIC}, {"SQUISH", fun_squish, 0, FN_VARARGS, CA_PUBLIC}, {"STARTTIME", fun_starttime, 0, 0, CA_PUBLIC}, {"STATS", fun_stats, 1, 0, CA_PUBLIC}, {"STEP", fun_step, 0, FN_VARARGS, CA_PUBLIC}, {"STRCAT", fun_strcat, 0, FN_VARARGS, CA_PUBLIC}, {"STREQ", fun_streq, 2, 0, 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}, {"STRUCTURE", fun_structure, 0, FN_VARARGS, CA_PUBLIC}, {"SUB", fun_sub, 2, 0, CA_PUBLIC}, {"SUBJ", fun_subj, 1, 0, CA_PUBLIC}, {"SWAP", fun_swap, 0, FN_VARARGS, CA_PUBLIC}, {"SWITCH", fun_switch, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"SWITCHALL", fun_switchall, 0, FN_VARARGS|FN_NO_EVAL, CA_PUBLIC}, {"T", fun_t, 1, 0, CA_PUBLIC}, {"TAN", fun_tan, 1, 0, CA_PUBLIC}, #ifdef TCL_INTERP_SUPPORT {"TCLCLEAR", fun_tclclear, 0, 0, CA_WIZARD}, {"TCLEVAL", fun_tcleval, 1, 0, CA_WIZARD}, {"TCLMODULE", fun_tclmodule, 1, 0, CA_WIZARD}, {"TCLPARAMS", fun_tclparams, 0, FN_VARARGS, CA_WIZARD}, {"TCLREGS", fun_tclregs, 0, 0, CA_WIZARD}, #endif /* TCL_INTERP_SUPPORT */ {"TEL", fun_tel, 2, 0, CA_PUBLIC}, {"TIME", fun_time, 0, 0, CA_PUBLIC}, {"TOSS", fun_toss, 0, FN_VARARGS, CA_PUBLIC}, {"TRANSLATE", fun_translate, 2, 0, CA_PUBLIC}, {"TRIGGER", fun_trigger, 0, FN_VARARGS, 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}, {"UNLOAD", fun_unload, 0, FN_VARARGS, CA_PUBLIC}, {"UNSTRUCTURE", fun_unstructure,1, 0, CA_PUBLIC}, #ifdef PUEBLO_SUPPORT {"URL_ESCAPE", fun_url_escape, -1, 0, CA_PUBLIC}, {"URL_UNESCAPE",fun_url_unescape,-1,0, CA_PUBLIC}, #endif /* PUEBLO_SUPPORT */ {"V", fun_v, 1, 0, CA_PUBLIC}, {"VADD", fun_vadd, 0, FN_VARARGS, CA_PUBLIC}, {"VALID", fun_valid, 2, 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}, {"WAIT", fun_wait, 2, 0, CA_PUBLIC}, {"WHERE", fun_where, 1, 0, CA_PUBLIC}, {"WHILE", fun_while, 0, FN_VARARGS, CA_PUBLIC}, {"WIPE", fun_wipe, 1, 0, CA_PUBLIC}, {"WORDPOS", fun_wordpos, 0, FN_VARARGS, CA_PUBLIC}, {"WORDS", fun_words, 0, FN_VARARGS, CA_PUBLIC}, {"X", fun_x, 1, 0, CA_PUBLIC}, {"XCON", fun_xcon, 3, 0, CA_PUBLIC}, {"XGET", fun_xget, 2, 0, CA_PUBLIC}, {"XOR", fun_xor, 0, FN_VARARGS, CA_PUBLIC}, {"XORBOOL", fun_xorbool, 0, FN_VARARGS, CA_PUBLIC}, {"XVARS", fun_xvars, 0, FN_VARARGS, CA_PUBLIC}, {"Z", fun_z, 2, 0, CA_PUBLIC}, {"ZFUN", fun_zfun, 0, FN_VARARGS, CA_PUBLIC}, {"ZONE", fun_zone, 1, 0, CA_PUBLIC}, {"ZWHO", fun_zwho, 1, 0, CA_PUBLIC}, {NULL, NULL, 0, 0, 0}}; /* *INDENT-ON* */ void NDECL(init_functab) { FUN *fp; char *buff, *cp, *dp; int i; buff = alloc_sbuf("init_functab"); hashinit(&mudstate.func_htab, 250 * HASH_FACTOR); for (fp = flist; fp->name; fp++) { cp = (char *)fp->name; dp = buff; while (*cp) { *dp = ToLower(*cp); cp++; dp++; } *dp = '\0'; hashadd(buff, (int *)fp, &mudstate.func_htab); } free_sbuf(buff); ufun_head = NULL; hashinit(&mudstate.ufunc_htab, 15 * HASH_FACTOR); /* Initialize the space table, which is used for fast copies on * functions like ljust(). */ for (i = 0; i < LBUF_SIZE; i++) space_buffer[i] = ' '; space_buffer[LBUF_SIZE - 1] = '\0'; } void do_function(player, cause, key, fname, target) dbref player, cause; int key; char *fname, *target; { UFUN *ufp, *ufp2; ATTR *ap; char *np, *bp; int atr, aflags; dbref obj, aowner; /* Make a local uppercase copy of the function name */ bp = np = alloc_sbuf("add_user_func"); safe_sb_str(fname, np, &bp); *bp = '\0'; for (bp = np; *bp; bp++) *bp = ToLower(*bp); /* Verify that the function doesn't exist in the builtin table */ if (hashfind(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, NOMATCH_MESSAGE); 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 *) hashfind(np, &mudstate.ufunc_htab); if (!ufp) { ufp = (UFUN *) XMALLOC(sizeof(UFUN), "do_function"); ufp->name = strsave(np); for (bp = (char *)ufp->name; *bp; bp++) *bp = ToUpper(*bp); 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) ; ufp2->next = ufp; } if (hashadd(np, (int *) ufp, &mudstate.ufunc_htab)) { notify_quiet(player, tprintf("Function %s not defined.", fname)); XFREE(ufp->name, "do_function"); XFREE(ufp, "do_function.2"); free_sbuf(np); return; } } 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(player) 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 && !isspace(*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; }