/* functions.c - MUSH function handlers */ #include "autoconf.h" #include "copyright.h" #ifndef lint static char RCSid[] = "$Id: functions.c,v 1.12 1995/03/29 02:55:13 ambar Exp $"; USE(RCSid); #endif #include <limits.h> #include <math.h> #include "externs.h" #include "flags.h" #include "attrs.h" #include "match.h" #include "command.h" #include "functions.h" #include "misc.h" #include "alloc.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; extern void FDECL(cf_log_notfound, (dbref player, char *cmd, const char *thingname, char *thing)); extern void FDECL(make_portlist, (dbref player, dbref target, char *buff)); /* This is the prototype for functions */ #define FUNCTION(x) \ static void x(buff, player, cause, fargs, nfargs, cargs, ncargs) \ char *buff; \ dbref player, cause; \ char *fargs[], *cargs[]; \ int nfargs, ncargs; /* This is for functions that take an optional delimiter character */ #define varargs_preamble(xname,xnargs) \ if (!fn_range_check(xname, nfargs, xnargs-1, xnargs, buff)) \ return; \ if (!delim_check(fargs, nfargs, xnargs, &sep, buff, 0, \ player, cause, cargs, ncargs)) \ return; #define evarargs_preamble(xname, xnargs) \ if (!fn_range_check(xname, nfargs, xnargs-1, xnargs, buff)) \ return; \ if (!delim_check(fargs, nfargs, xnargs, &sep, buff, 1, \ player, cause, cargs, ncargs)) \ return; #define mvarargs_preamble(xname,xminargs,xnargs) \ if (!fn_range_check(xname, nfargs, xminargs, xnargs, buff)) \ return; \ if (!delim_check(fargs, nfargs, xnargs, &sep, buff, 0, \ player, cause, cargs, ncargs)) \ return; /* Trim off leading and trailing spaces if the separator char is a space */ static 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 */ static 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. */ static 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); } static 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; } static void arr2list(arr, alen, list, sep) char *arr[], *list, sep; int alen; { char *p; int i; p = list; for (i = 0; i < alen; i++) { safe_str(arr[i], list, &p); safe_chr(sep, list, &p); } if (p != list) p--; *p = '\0'; } 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. * alternatively, if we're not using floating points, then we just * print the integer. */ #ifdef FLOATING_POINTS static void fval(buff, result) char *buff; double result; { char *p; sprintf(buff, "%.6f", result); /* get double val into buffer */ /* remove useless trailing 0's */ if ((p = (char *) rindex(buff, '0')) == NULL) return; else if (*(p + 1) == '\0') { while (*p == '0') *p-- = '\0'; } p = (char *) rindex(buff, '.'); /* take care of dangling '.' */ if (*(p + 1) == '\0') *p = '\0'; } #else #define fval(b,n) ltos(b, n) #endif /* FLOATING_POINTS */ /* --------------------------------------------------------------------------- * fn_range_check: Check # of args to a function with an optional argument * for validity. */ static int fn_range_check(fname, nfargs, minargs, maxargs, result) const char *fname; char *result; int nfargs, minargs, maxargs; { if ((nfargs >= minargs) && (nfargs <= maxargs)) return 1; if (maxargs == (minargs + 1)) sprintf(result, "#-1 FUNCTION (%s) EXPECTS %d OR %d ARGUMENTS", fname, minargs, maxargs); else sprintf(result, "#-1 FUNCTION (%s) EXPECTS BETWEEN %d AND %d ARGUMENTS", fname, minargs, maxargs); return 0; } /* --------------------------------------------------------------------------- * delim_check: obtain delimiter */ static int delim_check(fargs, nfargs, sep_arg, sep, buff, eval, player, cause, cargs, ncargs) char *fargs[], *cargs[], *sep, *buff; int nfargs, ncargs, sep_arg, eval; dbref player, cause; { char *tstr; int tlen; if (nfargs >= sep_arg) { tlen = strlen(fargs[sep_arg - 1]); if (tlen <= 1) eval = 0; if (eval) { tstr = exec(player, cause, EV_EVAL | EV_FCHECK, fargs[sep_arg - 1], cargs, ncargs); tlen = strlen(tstr); *sep = *tstr; free_lbuf(tstr); } if (tlen == 0) { *sep = ' '; } else if (tlen != 1) { strcpy(buff, "#-1 SEPARATOR MUST BE ONE CHARACTER"); 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 */ static 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) { strcpy(buff, "0"); return; } varargs_preamble("WORDS", 2); ltos(buff, 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); strcpy(buff, buff2); free_sbuf(buff2); } else strcpy(buff, "#-1"); return; } /* --------------------------------------------------------------------------- * fun_rand: Return a random number from 0 to arg1-1 */ FUNCTION(fun_rand) { int num; num = atoi(fargs[0]); if (num < 1) strcpy(buff, "0"); else sprintf(buff, "%ld", (random() % num)); } /* --------------------------------------------------------------------------- * fun_abs: Returns the absolute value of its argument. */ FUNCTION(fun_abs) { #ifdef FLOATING_POINTS double num; num = atof(fargs[0]); if (num == 0.0) { strcpy(buff, "0"); } else if (num < 0.0) { fval(buff, -num); } else { fval(buff, num); } #else ltos(buff, abs(atoi(fargs[0]))); #endif } /* --------------------------------------------------------------------------- * 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) strcpy(buff, "-1"); else if (num > 0) strcpy(buff, "1"); else strcpy(buff, "0"); } /* --------------------------------------------------------------------------- * fun_time: Returns nicely-formatted time. */ FUNCTION(fun_time) { char *temp; temp = (char *) ctime(&mudstate.now); temp[strlen(temp) - 1] = '\0'; strcpy(buff, temp); } /* --------------------------------------------------------------------------- * fun_time: Seconds since 0:00 1/1/70 */ FUNCTION(fun_secs) { ltos(buff, 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'; strcpy(buff, temp); } /* --------------------------------------------------------------------------- * 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++; \ } \ } static 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); 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); /* bad length */ return 0; } for (i = 0; (i < 12) && string_compare(monthtab[i], p); i++); if (i == 12) { free_sbuf(buf); /* not found */ 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; } } if (ttm->tm_year > 100) ttm->tm_year -= 1900; free_sbuf(buf); if (ttm->tm_year < 0) return 0; #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)) ltos(buff, timelocal(ttm)); else strcpy(buff, "-1"); } /* --------------------------------------------------------------------------- * 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'; strcpy(buff, temp); } /* --------------------------------------------------------------------------- * fun_get, fun_get_eval: Get attribute from object. */ static int check_read_perms(player, thing, attr, aowner, aflags, buff) dbref player, thing; ATTR *attr; int aowner, aflags; char *buff; { 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_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)) { strcpy(buff, (char *) "#-1 TOO FAR AWAY TO SEE"); return 0; } else { return 1; } } strcpy(buff, (char *) "#-1 PERMISSION DENIED"); return 0; } FUNCTION(fun_get) { dbref thing, aowner; int attrib, free_buffer, aflags; ATTR *attr; char *atr_gotten; struct boolexp *bool; if (!parse_attrib(player, fargs[0], &thing, &attrib)) { strcpy(buff, "#-1 NO MATCH"); return; } if (attrib == NOTHING) { *buff = '\0'; return; } free_buffer = 1; attr = atr_num(attrib); /* We need the attr's flags for this: */ if (!attr) { *buff = '\0'; return; } if (attr->flags & AF_IS_LOCK) { atr_gotten = atr_get(thing, attrib, &aowner, &aflags); 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); } /* Perform access checks. c_r_p fills buff with an error message * if needed. */ if (check_read_perms(player, thing, attr, aowner, aflags, buff)) strcpy(buff, atr_gotten); if (free_buffer) free_lbuf(atr_gotten); return; } FUNCTION(fun_get_eval) { dbref thing, aowner; int attrib, free_buffer, aflags, eval_it; ATTR *attr; char *atr_gotten; struct boolexp *bool; if (!parse_attrib(player, fargs[0], &thing, &attrib)) { strcpy(buff, "#-1 NO MATCH"); return; } if (attrib == NOTHING) { *buff = '\0'; return; } free_buffer = 1; eval_it = 1; attr = atr_num(attrib); /* We need the attr's flags for this: */ if (!attr) { *buff = '\0'; return; } if (attr->flags & AF_IS_LOCK) { atr_gotten = atr_get(thing, attrib, &aowner, &aflags); 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); } if (!check_read_perms(player, thing, attr, aowner, aflags, buff)) { if (free_buffer) free_lbuf(atr_gotten); return; } strcpy(buff, atr_gotten); if (free_buffer) free_lbuf(atr_gotten); if (eval_it) { atr_gotten = exec(thing, player, EV_FIGNORE | EV_EVAL, buff, (char **) NULL, 0); strcpy(buff, atr_gotten); free_lbuf(atr_gotten); } return; } /* --------------------------------------------------------------------------- * fun_u and fun_ulocal: Call a user-defined function. */ static void do_ufun(buff, player, cause, fargs, nfargs, cargs, ncargs, is_local) char *buff; dbref player, cause; char *fargs[], *cargs[]; int nfargs, ncargs, is_local; { dbref aowner, thing; int aflags, anum; ATTR *ap; char *atext, *result, *preserve[MAX_GLOBAL_REGS]; /* We need at least one argument */ if (nfargs < 1) { strcpy(buff, "#-1 TOO FEW ARGUMENTS"); 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) { *buff = '\0'; return; } /* Use it if we can access it, otherwise return an error. */ atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { *buff = '\0'; return; } if (!*atext) { free_lbuf(atext); *buff = '\0'; return; } if (!check_read_perms(player, thing, ap, aowner, aflags, buff)) { free_lbuf(atext); *buff = '\0'; return; } /* If we're evaluating locally, preserve the global registers. */ if (is_local) save_global_regs("fun_ulocal_save", preserve); /* Evaluate it using the rest of the passed function args */ result = exec(thing, cause, EV_FCHECK | EV_EVAL, atext, &(fargs[1]), nfargs - 1); free_lbuf(atext); strcpy(buff, result); free_lbuf(result); /* If we're evaluating locally, restore the preserved registers. */ if (is_local) restore_global_regs("fun_ulocal_restore", preserve); } FUNCTION(fun_u) { do_ufun(buff, player, cause, fargs, nfargs, cargs, ncargs, 0); } FUNCTION(fun_ulocal) { do_ufun(buff, player, cause, fargs, nfargs, cargs, ncargs, 1); } /* --------------------------------------------------------------------------- * fun_default, fun_edefault, and fun_udefault: * These check for the presence of an attribute. If it exists, then it * is gotten, via the equivalent of get(), get_eval(), or u(), respectively. * Otherwise, the default message is used. * In the case of udefault(), the remaining arguments to the function * are used as arguments to the u(). */ FUNCTION(fun_default) { dbref thing, aowner; int attrib, aflags; ATTR *attr; char *objname, *atr_gotten, *defcase; objname = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[0], cargs, ncargs); /* First we check to see that the attribute exists on the object. * If so, we grab it and use it. */ if (objname != NULL) { if (parse_attrib(player, objname, &thing, &attrib) && (attrib != NOTHING)) { attr = atr_num(attrib); if (attr && !(attr->flags & AF_IS_LOCK)) { atr_gotten = atr_pget(thing, attrib, &aowner, &aflags); if (*atr_gotten && check_read_perms(player, thing, attr, aowner, aflags, buff)) { strcpy(buff, atr_gotten); free_lbuf(atr_gotten); free_lbuf(objname); return; } free_lbuf(atr_gotten); } } free_lbuf(objname); } /* If we've hit this point, we've not gotten anything useful, so * we go and evaluate the default. */ defcase = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[1], cargs, ncargs); strcpy(buff, defcase); free_lbuf(defcase); } FUNCTION(fun_edefault) { dbref thing, aowner; int attrib, aflags; ATTR *attr; char *objname, *atr_gotten, *defcase; objname = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[0], cargs, ncargs); /* First we check to see that the attribute exists on the object. * If so, we grab it and use it. */ if (objname != NULL) { if (parse_attrib(player, objname, &thing, &attrib) && (attrib != NOTHING)) { attr = atr_num(attrib); if (attr && !(attr->flags & AF_IS_LOCK)) { atr_gotten = atr_pget(thing, attrib, &aowner, &aflags); if (*atr_gotten && check_read_perms(player, thing, attr, aowner, aflags, buff)) { strcpy(buff, atr_gotten); free_lbuf(atr_gotten); atr_gotten = exec(thing, player, EV_FIGNORE | EV_EVAL, buff, (char **) NULL, 0); strcpy(buff, atr_gotten); free_lbuf(atr_gotten); free_lbuf(objname); return; } free_lbuf(atr_gotten); } } free_lbuf(objname); } /* If we've hit this point, we've not gotten anything useful, so * we go and evaluate the default. */ defcase = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[1], cargs, ncargs); strcpy(buff, defcase); free_lbuf(defcase); } FUNCTION(fun_udefault) { dbref thing, aowner; int aflags, anum, i, j; ATTR *ap; char *objname, *atext, *result, *defcase, *xargs[NUM_ENV_VARS]; *buff = '\0'; if (nfargs < 2) /* must have at least two arguments */ return; objname = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[0], cargs, ncargs); /* First we check to see that the attribute exists on the object. * If so, we grab it and use it. */ if (objname != NULL) { if (parse_attrib(player, objname, &thing, &anum)) { if ((anum == NOTHING) || (!Good_obj(thing))) ap = NULL; else ap = atr_num(anum); } else { thing = player; ap = atr_str(objname); } if (ap) { atext = atr_pget(thing, ap->number, &aowner, &aflags); if (atext) { if (*atext && check_read_perms(player, thing, ap, aowner, aflags, buff)) { /* Now we have a problem -- we've got to go eval * all of those arguments to the function. */ for (i = 2, j = 0; j < NUM_ENV_VARS; i++, j++) { if ((i < NUM_ENV_VARS) && fargs[i]) xargs[j] = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, fargs[i], cargs, ncargs); else xargs[j] = NULL; } /* Now we should do the evaluation. */ result = exec(thing, cause, EV_FCHECK | EV_EVAL, atext, xargs, nfargs - 2); /* Then clean up after ourselves. */ for (j = 0; j < NUM_ENV_VARS; j++) if (xargs[j]) free_lbuf(xargs[j]); free_lbuf(atext); strcpy(buff, result); free_lbuf(result); free_lbuf(objname); return; } free_lbuf(atext); } } free_lbuf(objname); } /* If we've hit this point, we've not gotten anything useful, so * we go and evaluate the default. */ defcase = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[1], cargs, ncargs); strcpy(buff, defcase); free_lbuf(defcase); } /* --------------------------------------------------------------------------- * fun_hasattr: does object X have attribute Y. */ FUNCTION(fun_hasattr) { dbref thing, aowner; int aflags; ATTR *attr; char *tbuf; thing = match_thing(player, fargs[0]); if (thing == NOTHING) { strcpy(buff, "#-1 NO MATCH"); return; } else if (!Examinable(player, thing)) { strcpy(buff, "#-1 PERMISSION DENIED"); return; } attr = atr_str(fargs[1]); if (!attr) { strcpy(buff, "0"); return; } atr_pget_info(thing, attr->number, &aowner, &aflags); if (!See_attr(player, thing, attr, aowner, aflags)) strcpy(buff, "0"); else { tbuf = atr_pget(thing, attr->number, &aowner, &aflags); if (*tbuf) strcpy(buff, "1"); else strcpy(buff, "0"); free_lbuf(tbuf); } } /* --------------------------------------------------------------------------- * fun_objeval: Evaluate expression from perspective of another object. * All args to this function are passed _unevaluated_. */ FUNCTION(fun_objeval) { char *s, *name; dbref obj; /* Evaluate the first argument to get the object name. */ name = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[0], cargs, ncargs); if (name == NULL) { *buff = '\0'; return; } /* In order to evaluate from something else's viewpoint, you must * have the same owner as it, or be a wizard. Otherwise, we default * to evaluating from our own viewpoint. */ if (((obj = match_thing(player, name)) == NOTHING) || ((Owner(player) != Owner(obj)) && !Wizard(player))) obj = player; s = exec(obj, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[1], cargs, ncargs); if (s != NULL) { strcpy(buff, s); free_lbuf(s); } else { *buff = '\0'; } free_lbuf(name); } /* --------------------------------------------------------------------------- * 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))) { *buff = '#'; ltos(&buff[1], Parent(it)); } else { strcpy(buff, "#-1"); } return; } /* --------------------------------------------------------------------------- * fun_parse: Make list from evaluating arg3 with each member of arg2. * arg1 specifies a delimiter character to use in the parsing of arg2. * NOTE: This function expects that its arguments have not been evaluated. */ FUNCTION(fun_parse) { char *curr, *objstring, *buff2, *result, *bp, *cp, sep; int first; evarargs_preamble("PARSE", 3); cp = curr = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, fargs[0], cargs, ncargs); bp = buff; cp = trim_space_sep(cp, sep); if (!*cp) { free_lbuf(curr); *buff = '\0'; return; } first = 1; while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { objstring = split_token(&cp, sep); buff2 = replace_string(BOUND_VAR, objstring, fargs[1]); result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, buff2, cargs, ncargs); free_lbuf(buff2); if (!first) { safe_chr(' ', buff, &bp); } first = 0; safe_str(result, buff, &bp); free_lbuf(result); } free_lbuf(curr); *bp = '\0'; } /* --------------------------------------------------------------------------- * fun_mid: mid(foobar,2,3) returns oba */ FUNCTION(fun_mid) { int l, len; l = atoi(fargs[1]); len = atoi(fargs[2]); if ((l < 0) || (len < 0) || ((len + l) > LBUF_SIZE)) { strcpy(buff, "#-1 OUT OF RANGE"); return; } if (l < strlen(fargs[0])) strcpy(buff, fargs[0] + l); else *buff = 0; buff[len] = 0; } /* --------------------------------------------------------------------------- * 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) { *buff = '\0'; return; } varargs_preamble("FIRST", 2); s = trim_space_sep(fargs[0], sep); /* leading spaces ... */ first = split_token(&s, sep); if (!first) { *buff = '\0'; } else { strcpy(buff, first); } } /* --------------------------------------------------------------------------- * 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) { *buff = '\0'; return; } varargs_preamble("REST", 2); s = trim_space_sep(fargs[0], sep); /* leading spaces ... */ first = split_token(&s, sep); if (!s) { *buff = '\0'; } else { strcpy(buff, s); } } /* --------------------------------------------------------------------------- * fun_last: Returns last word in a string */ FUNCTION(fun_last) { char *s, *last, sep; int len, i; /* If we are passed an empty arglist return a null string */ if (nfargs == 0) { *buff = '\0'; return; } varargs_preamble("LAST", 2); s = trim_space_sep(fargs[0], sep); /* trim leading spaces */ /* If we're dealing with spaces, trim off the trailing stuff */ if (sep == ' ') { len = strlen(s); for (i = len - 1; s[i] == ' '; i--) ; if (i + 1 <= len) s[i + 1] = '\0'; } last = (char *) rindex(s, sep); if (last) strcpy(buff, ++last); else strcpy(buff, s); } /* --------------------------------------------------------------------------- * fun_left: Returns first n characters in a string */ FUNCTION(fun_left) { int len = atoi(fargs[1]); if (len < 1) { *buff = '\0'; } else { strcpy(buff, fargs[0]); if (len < LBUF_SIZE) buff[len] = '\0'; } } /* --------------------------------------------------------------------------- * fun_right: Returns last n characters in a string */ FUNCTION(fun_right) { int len = atoi(fargs[1]); if (len < 1) { *buff = '\0'; } else { len = strlen(fargs[0]) - len; if (len < 1) strcpy(buff, fargs[0]); else strcpy(buff, fargs[0] + len); } } /* --------------------------------------------------------------------------- * fun_v: Function form of %-substitution */ FUNCTION(fun_v) { dbref aowner; int aflags; char *sbuf, *sbufc, *tbuf; 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) { *buff = '\0'; return; } /* If we can access it, return it, otherwise return a * null string */ atr_pget_info(player, ap->number, &aowner, &aflags); if (See_attr(player, player, ap, aowner, aflags)) { tbuf = atr_pget(player, ap->number, &aowner, &aflags); strcpy(buff, tbuf); free_lbuf(tbuf); } else { *buff = '\0'; } 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); tbuf = exec(player, cause, EV_FIGNORE, sbuf, cargs, ncargs); strcpy(buff, tbuf); free_lbuf(tbuf); free_sbuf(sbuf); } /* --------------------------------------------------------------------------- * fun_s: Force substitution to occur. */ FUNCTION(fun_s) { char *tbuf; tbuf = exec(player, cause, EV_FIGNORE | EV_EVAL, fargs[0], cargs, ncargs); strcpy(buff, tbuf); free_lbuf(tbuf); } /* --------------------------------------------------------------------------- * 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))) { *buff = '#'; ltos(&buff[1], Contents(it)); return; } strcpy(buff, "#-1"); return; } /* --------------------------------------------------------------------------- * fun_exit: Returns first exit in exits list of room. */ FUNCTION(fun_exit) { dbref it, exam; it = match_thing(player, fargs[0]); if (Good_obj(it) && Has_exits(it) && Good_obj(Exits(it))) { exam = Examinable(player, it); if (exam || (where_is(player) == it) || (it == cause)) { *buff = '#'; ltos(&buff[1], next_exit(player, Exits(it), exam)); return; } } strcpy(buff, "#-1"); return; } /* --------------------------------------------------------------------------- * fun_next: return next thing in contents or exits chain */ FUNCTION(fun_next) { dbref it, loc, exam_here; it = match_thing(player, fargs[0]); if (Good_obj(it)) { loc = where_is(it); if (loc != NOTHING) { exam_here = Examinable(player, loc); if (exam_here || (loc == player) || (loc == where_is(player))) { *buff = '#'; ltos(&buff[1], next_exit(player, Next(it), exam_here)); return; } } } strcpy(buff, "#-1"); return; } /* --------------------------------------------------------------------------- * fun_lastcreate: Return the last object of type Y that X created. */ FUNCTION(fun_lastcreate) { int i, aowner, aflags, obj_list[4], obj_type; char *obj_str, *p; dbref obj = match_thing(player, fargs[0]); if (!controls(player, obj)) { /* Automatically checks for GoodObj */ strcpy(buff, "#-1"); return; } switch (*fargs[1]) { case 'R': case 'r': obj_type = 0; break; case 'E': case 'e': obj_type = 1;; break; case 'T': case 't': obj_type = 2; break; case 'P': case 'p': obj_type = 3; break; default: notify_quiet(player, "Invalid object type."); strcpy(buff, "#-1"); } if ((obj_str = atr_get(obj, A_NEWOBJS, &aowner, &aflags)) == NULL) { strcpy(buff, "#-1"); return; } if (!*obj_str) { free_lbuf(obj_str); strcpy(buff, "#-1"); return; } for (p = (char *) strtok(obj_str, " "), i = 0; p && (i < 4); p = (char *) strtok(NULL, " "), i++) { obj_list[i] = atoi(p); } free_lbuf(obj_str); *buff = '#'; ltos(&buff[1], obj_list[obj_type]); } /* --------------------------------------------------------------------------- * fun_findable: can X locate Y */ FUNCTION(fun_findable) { dbref obj = match_thing(player, fargs[0]); dbref victim = match_thing(player, fargs[1]); if (obj == NOTHING) strcpy(buff, "#-1 ARG1 NOT FOUND"); else if (victim == NOTHING) strcpy(buff, "#-1 ARG2 NOT FOUND"); else ltos(buff, locatable(obj, victim, obj)); } /* --------------------------------------------------------------------------- * fun_loc: Returns the location of something */ FUNCTION(fun_loc) { dbref it; it = match_thing(player, fargs[0]); if (locatable(player, it, cause)) { *buff = '#'; ltos(&buff[1], Location(it)); } else strcpy(buff, "#-1"); 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)) { *buff = '#'; ltos(&buff[1], where_is(it)); } else strcpy(buff, "#-1"); 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); } *buff = '#'; ltos(&buff[1], it); return; } strcpy(buff, "#-1"); } /* --------------------------------------------------------------------------- * 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)) { *buff = '#'; ltos(&buff[1], it); return; } } strcpy(buff, "#-1"); } else if (isRoom(it)) { *buff = '#'; ltos(&buff[1], it); } else { strcpy(buff, "#-1"); } 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); } *buff = '#'; ltos(&buff[1], it); } /* --------------------------------------------------------------------------- * fun_controls: Does x control y? */ FUNCTION(fun_controls) { dbref x, y; x = match_thing(player, fargs[0]); if (x == NOTHING) { strcpy(buff, "#-1 ARG1 NOT FOUND"); return; } y = match_thing(player, fargs[1]); if (y == NOTHING) { strcpy(buff, "#-1 ARG2 NOT FOUND"); return; } ltos(buff, Controls(x, y)); } /* --------------------------------------------------------------------------- * fun_visible: Can X examine Y. If X does not exist, 0 is returned. * If Y, the object, does not exist, 0 is returned. If * Y the object exists, but the optional attribute does * not, X's ability to return Y the object is returned. */ FUNCTION(fun_visible) { dbref it, thing, aowner; int aflags, atr; ATTR *ap; if ((it = match_thing(player, fargs[0])) == NOTHING) { strcpy(buff, "0"); return; } if (parse_attrib(player, fargs[1], &thing, &atr)) { if (atr == NOTHING) { ltos(buff, Examinable(it, thing)); return; } ap = atr_num(atr); atr_pget_info(thing, atr, &aowner, &aflags); ltos(buff, See_attr(it, thing, ap, aowner, aflags)); return; } thing = match_thing(player, fargs[1]); if (!Good_obj(thing)) { strcpy(buff, "0"); return; } ltos(buff, Examinable(it, thing)); } /* --------------------------------------------------------------------------- * 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) { buff[0] = '\0'; return; } if (!mudconf.read_rem_name) { if (!nearby_or_control(player, it) && (!isPlayer(it))) { strcpy(buff, "#-1 TOO FAR AWAY TO SEE"); return; } } strcpy(buff, Name(it)); } /* --------------------------------------------------------------------------- * fun_name: Return the name of an object */ FUNCTION(fun_name) { dbref it; char *s; it = match_thing(player, fargs[0]); if (it == NOTHING) { buff[0] = '\0'; return; } if (!mudconf.read_rem_name) { if (!nearby_or_control(player, it) && !isPlayer(it)) { strcpy(buff, "#-1 TOO FAR AWAY TO SEE"); return; } } strcpy(buff, Name(it)); if (isExit(it)) { for (s = buff; *s && (*s != ';'); s++); *s = '\0'; } } /* --------------------------------------------------------------------------- * fun_match, fun_matchall, fun_strmatch: Match arg2 against each word of * arg1 returning index of first match (or index of all matches), 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)) { ltos(buff, wcount); return; } wcount++; } while (s); strcpy(buff, "0"); } FUNCTION(fun_matchall) { int wcount; char *r, *s, *bp, sep, tbuf[8]; varargs_preamble("MATCHALL", 3); /* Check each word individually, returning the word number of all * that match. If none match, return an empty string. */ wcount = 1; bp = buff; s = trim_space_sep(fargs[0], sep); do { r = split_token(&s, sep); if (quick_wild(fargs[1], r)) { ltos(tbuf, wcount); if (bp != buff) { safe_chr(' ', buff, &bp); } safe_str(tbuf, buff, &bp); } wcount++; } while (s); if (bp != buff) *bp = '\0'; else *buff = '\0'; } FUNCTION(fun_strmatch) { /* Check if we match the whole string. If so, return 1 */ if (quick_wild(fargs[1], fargs[0])) strcpy(buff, "1"); else strcpy(buff, "0"); 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)) { *buff = '\0'; 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) { *buff = '\0'; 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); strcpy(buff, r); } int xlate(arg) char *arg; { /* Deals with either dbrefs or integers. This is not, mind you, * used by the boolean-determining functions such as or() and not(); * it's used by the search routines, though. */ int temp; char *temp2; if (arg[0] == '#') { arg++; if (is_integer(arg)) { temp = atoi(arg); if (temp == -1) temp = 0; return temp; } return 0; } temp2 = trim_space_sep(arg, ' '); if (!*temp2) return 0; if (is_integer(temp2)) return atoi(temp2); return 1; } /* --------------------------------------------------------------------------- * fun_elements: given a list of numbers, get corresponding elements from * the list. elements(ack bar eep foof yay,2 4) ==> bar foof * The function takes a separator, but the separator only applies to the * first list. */ FUNCTION(fun_elements) { int nwords, cur; char *ptrs[LBUF_SIZE / 2]; char *wordlist, *s, *r, *bp, sep; varargs_preamble("ELEMENTS", 3); /* Turn the first list into an array. */ wordlist = alloc_lbuf("fun_elements.wordlist"); strcpy(wordlist, fargs[0]); nwords = list2arr(ptrs, LBUF_SIZE / 2, wordlist, sep); bp = buff; s = trim_space_sep(fargs[1], ' '); /* Go through the second list, grabbing the numbers and finding the * corresponding elements. */ do { r = split_token(&s, ' '); cur = atoi(r) - 1; if ((cur >= 0) && (cur < nwords) && ptrs[cur]) { if (bp != buff) { safe_chr(sep, buff, &bp); } safe_str(ptrs[cur], buff, &bp); } } while (s); *bp = '\0'; free_lbuf(wordlist); } /* --------------------------------------------------------------------------- * fun_grab: a combination of extract() and match(), sortof. We grab the * single element that we match. * * grab(Test:1 Ack:2 Foof:3,*:2) => Ack:2 * grab(Test-1+Ack-2+Foof-3,*o*,+) => Ack:2 */ FUNCTION(fun_grab) { char *r, *s, sep; varargs_preamble("GRAB", 3); /* Walk the wordstring, until we find the word we want. */ s = trim_space_sep(fargs[0], sep); do { r = split_token(&s, sep); if (quick_wild(fargs[1], r)) { strcpy(buff, r); return; } } while (s); *buff = '\0'; } /* --------------------------------------------------------------------------- * 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, *bp; s = fargs[0]; c = *fargs[1]; start = atoi(fargs[2]); end = atoi(fargs[3]); *buff = '\0'; bp = buff; 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, not tabs or newlines, since people may * MUSHcode strings like "%r%tAmberyl %r%tMoonchilde %r%tEvinar" */ 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, &bp); return; } else { p++; } } } /* if we've gotten this far, we've run off the end of the string */ safe_str(s, buff, &bp); } FUNCTION(fun_cat) { int i; char *bp; bp = buff; safe_str(fargs[0], buff, &bp); for (i = 1; i < nfargs; i++) { safe_chr(' ', buff, &bp); safe_str(fargs[i], buff, &bp); } } FUNCTION(fun_version) { strcpy(buff, mudstate.version); } FUNCTION(fun_strlen) { ltos(buff, (int) strlen(fargs[0])); } FUNCTION(fun_num) { *buff = '#'; ltos(&buff[1], match_thing(player, fargs[0])); } FUNCTION(fun_gt) { ltos(buff, (aton(fargs[0]) > aton(fargs[1]))); } FUNCTION(fun_gte) { ltos(buff, (aton(fargs[0]) >= aton(fargs[1]))); } FUNCTION(fun_lt) { ltos(buff, (aton(fargs[0]) < aton(fargs[1]))); } FUNCTION(fun_lte) { ltos(buff, (aton(fargs[0]) <= aton(fargs[1]))); } FUNCTION(fun_eq) { ltos(buff, (aton(fargs[0]) == aton(fargs[1]))); } FUNCTION(fun_neq) { ltos(buff, (aton(fargs[0]) != aton(fargs[1]))); } FUNCTION(fun_and) { int i; if (nfargs < 2) { strcpy(buff, "#-1 TOO FEW ARGUMENTS"); } else { for (i = 0; (i < nfargs) && atoi(fargs[i]); i++) ; ltos(buff, i == nfargs); } return; } FUNCTION(fun_or) { int i; if (nfargs < 2) { strcpy(buff, "#-1 TOO FEW ARGUMENTS"); } else { for (i = 0; (i < nfargs) && !atoi(fargs[i]); i++) ; ltos(buff, i != nfargs); } return; } FUNCTION(fun_xor) { int i, val; if (nfargs < 2) { strcpy(buff, "#-1 TOO FEW ARGUMENTS"); } else { val = atoi(fargs[0]); for (i = 1; i < nfargs; i++) { if (val) { val = !atoi(fargs[i]); } else { val = atoi(fargs[i]); } } ltos(buff, val ? 1 : 0); } return; } FUNCTION(fun_not) { ltos(buff, !atoi(fargs[0])); } FUNCTION(fun_sqrt) { double val; val = atof(fargs[0]); if (val < 0) { strcpy(buff, "#-1 SQUARE ROOT OF NEGATIVE"); } else if (val == 0) { strcpy(buff, "0"); } else { fval(buff, sqrt(val)); } } FUNCTION(fun_add) { NVAL sum; int i; if (nfargs < 2) { strcpy(buff, "#-1 TOO FEW ARGUMENTS"); } else { sum = aton(fargs[0]); for (i = 1; i < nfargs; i++) { sum += aton(fargs[i]); } fval(buff, sum); } return; } FUNCTION(fun_sub) { fval(buff, aton(fargs[0]) - aton(fargs[1])); } FUNCTION(fun_mul) { NVAL prod; int i; if (nfargs < 2) { strcpy(buff, "#-1 TOO FEW ARGUMENTS"); } else { prod = aton(fargs[0]); for (i = 1; i < nfargs; i++) { prod *= aton(fargs[i]); } fval(buff, prod); } return; } FUNCTION(fun_floor) { sprintf(buff, "%.0f", floor(atof(fargs[0]))); } FUNCTION(fun_ceil) { sprintf(buff, "%.0f", ceil(atof(fargs[0]))); } FUNCTION(fun_round) { const char *fstr; 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; } sprintf(buff, fstr, atof(fargs[0])); /* Handle bogus result of "-0" from sprintf. Yay, cclib. */ if (!strcmp(buff, "-0")) { strcpy(buff, "0"); } } FUNCTION(fun_trunc) { int num; num = atoi(fargs[0]); ltos(buff, num); } FUNCTION(fun_div) { int bot; bot = atoi(fargs[1]); if (bot == 0) { strcpy(buff, "#-1 DIVIDE BY ZERO"); } else { ltos(buff, (atoi(fargs[0]) / bot)); } } FUNCTION(fun_fdiv) { double bot; bot = atof(fargs[1]); if (bot == 0) { strcpy(buff, "#-1 DIVIDE BY ZERO"); } else { fval(buff, (atof(fargs[0]) / bot)); } } FUNCTION(fun_mod) { int bot; bot = atoi(fargs[1]); if (bot == 0) bot = 1; ltos(buff, atoi(fargs[0]) % bot); } FUNCTION(fun_pi) { strcpy(buff, "3.141592654"); } FUNCTION(fun_e) { strcpy(buff, "2.718281828"); } FUNCTION(fun_sin) { fval(buff, sin(atof(fargs[0]))); } FUNCTION(fun_cos) { fval(buff, cos(atof(fargs[0]))); } FUNCTION(fun_tan) { fval(buff, tan(atof(fargs[0]))); } FUNCTION(fun_exp) { fval(buff, exp(atof(fargs[0]))); } FUNCTION(fun_power) { double val1, val2; val1 = atof(fargs[0]); val2 = atof(fargs[1]); if (val1 < 0) { strcpy(buff, "#-1 POWER OF NEGATIVE"); } else { fval(buff, pow(val1, val2)); } } FUNCTION(fun_ln) { double val; val = atof(fargs[0]); if (val > 0) fval(buff, log(val)); else strcpy(buff, "#-1 LN OF NEGATIVE OR ZERO"); } FUNCTION(fun_log) { double val; val = atof(fargs[0]); if (val > 0) { fval(buff, log10(val)); } else { strcpy(buff, "#-1 LOG OF NEGATIVE OR ZERO"); } } FUNCTION(fun_asin) { double val; val = atof(fargs[0]); if ((val < -1) || (val > 1)) { strcpy(buff, "#-1 ASIN ARGUMENT OUT OF RANGE"); } else { fval(buff, asin(val)); } } FUNCTION(fun_acos) { double val; val = atof(fargs[0]); if ((val < -1) || (val > 1)) { strcpy(buff, "#-1 ACOS ARGUMENT OUT OF RANGE"); } else { fval(buff, acos(val)); } } FUNCTION(fun_atan) { fval(buff, atan(atof(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); ltos(buff, 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); ltos(buff, d); } /* --------------------------------------------------------------------------- * fun_comp: string compare. */ FUNCTION(fun_comp) { int x; x = strcmp(fargs[0], fargs[1]); if (x > 0) strcpy(buff, "1"); else if (x < 0) strcpy(buff, "-1"); else strcpy(buff, "0"); } /* --------------------------------------------------------------------------- * 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 *bufp, *tbuf; int i, first, last; it = match_thing(player, fargs[0]); *buff = '\0'; bufp = buff; 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)) { tbuf = alloc_sbuf("fun_xcon"); /* 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 (*buff) { tbuf[0] = ' '; tbuf[1] = '#'; ltos(&tbuf[2], thing); } else { *tbuf = '#'; ltos(&tbuf[1], thing); } safe_str(tbuf, buff, &bufp); } free_sbuf(tbuf); *bufp = '\0'; } } else strcpy(buff, "#-1"); } /* --------------------------------------------------------------------------- * fun_lcon: Return a list of contents. */ FUNCTION(fun_lcon) { dbref thing, it; char *bufp, *tbuf; it = match_thing(player, fargs[0]); *buff = '\0'; bufp = buff; if ((it != NOTHING) && (Has_contents(it)) && (Examinable(player, it) || (Location(player) == it) || (it == cause))) { tbuf = alloc_sbuf("fun_lcon"); DOLIST(thing, Contents(it)) { if (*buff) { tbuf[0] = ' '; tbuf[1] = '#'; ltos(&tbuf[2], thing); } else { *tbuf = '#'; ltos(&tbuf[1], thing); } safe_str(tbuf, buff, &bufp); } free_sbuf(tbuf); *bufp = '\0'; } else strcpy(buff, "#-1"); } /* --------------------------------------------------------------------------- * fun_lexits: Return a list of exits. */ FUNCTION(fun_lexits) { dbref thing, it, parent; char *bufp, *tbuf; int exam, lev; *buff = '\0'; bufp = buff; it = match_thing(player, fargs[0]); if (!Good_obj(it) || !Has_exits(it)) { strcpy(buff, "#-1"); return; } exam = Examinable(player, it); if (!exam && (where_is(player) != it) && (it != cause)) { strcpy(buff, "#-1"); return; } tbuf = alloc_sbuf("fun_lexits"); /* Return info for all parent levels */ ITER_PARENTS(it, parent, lev) { /* Look for exits at each level */ if (!Has_exits(parent)) continue; for (thing = next_exit(player, Exits(parent), exam); thing != NOTHING; thing = next_exit(player, Next(thing), exam)) { if (*buff) { tbuf[0] = ' '; tbuf[1] = '#'; ltos(&tbuf[2], thing); } else { *tbuf = '#'; ltos(&tbuf[1], thing); } safe_str(tbuf, buff, &bufp); } } free_sbuf(tbuf); *bufp = '\0'; 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)) strcpy(buff, "#-1"); else if (Has_home(it)) { *buff = '#'; ltos(&buff[1], Home(it)); } else if (Has_dropto(it)) { *buff = '#'; ltos(&buff[1], Dropto(it)); } else if (isExit(it)) { *buff = '#'; ltos(&buff[1], where_is(it)); } else strcpy(buff, "#-1"); 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)) strcpy(buff, "#-1"); else ltos(buff, Pennies(it)); } /* --------------------------------------------------------------------------- * fun_pos: Find a word in a string */ FUNCTION(fun_pos) { int i = 1; char *s, *t, *u; s = fargs[1]; while (*s) { u = s; t = fargs[0]; while (*t && *t == *u) ++t, ++u; if (*t == '\0') { ltos(buff, i); return; } ++i, ++s; } strcpy(buff, "#-1"); 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, *bp; char c, tbuf[8]; int i; *buff = '\0'; if (!fargs[0] || !*fargs[0]) return; c = (char) *(fargs[1]); if (!c) c = ' '; for (i = 0, s = fargs[0], bp = buff; *s; i++, s++) { if (*s == c) { if (bp != buff) { safe_chr(' ', buff, &bp); } ltos(tbuf, i); safe_str(tbuf, buff, &bp); } } *bp = '\0'; } /* --------------------------------------------------------------------------- * 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, str, el, word, sep, flag) char *buff, *str, *word, sep; int el, flag; { int ct, overrun; char *sptr, *iptr, *eptr, *bp; 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))) { *buff = '\0'; return; } /* we can't fiddle with anything before the first position */ if (el < 1) { strcpy(buff, str); 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))) { strcpy(buff, str); return; } /* Split the 'target' word from the 'after' portion. */ if (eptr) iptr = split_token(&eptr, sep); else iptr = NULL; } bp = buff; switch (flag) { case IF_DELETE: /* deletion */ if (sptr) { safe_str(sptr, buff, &bp); if (eptr) { safe_chr(sep, buff, &bp); } } if (eptr) { safe_str(eptr, buff, &bp); } break; case IF_REPLACE: /* replacing */ if (sptr) { safe_str(sptr, buff, &bp); safe_chr(sep, buff, &bp); } safe_str(word, buff, &bp); if (eptr) { safe_chr(sep, buff, &bp); safe_str(eptr, buff, &bp); } break; case IF_INSERT: /* insertion */ if (sptr) { safe_str(sptr, buff, &bp); safe_chr(sep, buff, &bp); } safe_str(word, buff, &bp); if (iptr) { safe_chr(sep, buff, &bp); safe_str(iptr, buff, &bp); } if (eptr) { safe_chr(sep, buff, &bp); safe_str(eptr, buff, &bp); } break; } *bp = '\0'; } FUNCTION(fun_ldelete) { /* delete a word at position X of a list */ char sep; varargs_preamble("LDELETE", 3); do_itemfuns(buff, 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, 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, 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, *bp; char sep; int first, found; varargs_preamble("REMOVE", 3); if (index(fargs[1], sep)) { strcpy(buff, "#-1 CAN ONLY DELETE ONE ELEMENT"); 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. */ bp = buff; sp = s; found = 0; first = 1; while (s) { sp = split_token(&s, sep); if (found || strcmp(sp, word)) { if (!first) { safe_chr(sep, buff, &bp); } safe_str(sp, buff, &bp); first = 0; } else { found = 1; } } *bp = '\0'; } /* --------------------------------------------------------------------------- * 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)) { ltos(buff, wcount); return; } wcount++; } while (s); strcpy(buff, "0"); } /* --------------------------------------------------------------------------- * fun_secure, fun_escape: escape [, ], %, \, and the beginning of the string. */ FUNCTION(fun_secure) { char *s, *d; s = fargs[0]; d = buff; while (*s) { switch (*s) { case '%': case '$': case '\\': case '[': case ']': case '(': case ')': case '{': case '}': case ',': case ';': safe_chr(' ', buff, &d); break; default: safe_chr(*s, buff, &d); } s++; } *d = '\0'; } FUNCTION(fun_escape) { char *s, *d; s = fargs[0]; d = buff; while (*s) { switch (*s) { case '%': case '\\': case '[': case ']': case '{': case '}': case ';': safe_chr('\\', buff, &d); default: if (d == buff) { safe_chr('\\', buff, &d); } safe_chr(*s, buff, &d); } s++; } *d = '\0'; } /*--------------------------------------------------------------------------- * Pueblo HTML-related functions. */ #ifdef PUEBLO_SUPPORT FUNCTION(fun_html_escape) { *buff = '\0'; html_escape(fargs[0], buff, 0); } FUNCTION(fun_html_unescape) { const char *msg_orig; char *temp; int ret = 0; char **destp; temp = buff; destp = &temp; *buff = '\0'; for (msg_orig = fargs[0]; msg_orig && *msg_orig && !ret; msg_orig++) { switch (*msg_orig) { case '&': if (!strncmp(msg_orig, """, 6)) { ret = safe_chr_fn('\"', buff, destp); msg_orig += 5; } else if (!strncmp(msg_orig, "<", 4)) { ret = safe_chr_fn('<', buff, destp); msg_orig += 3; } else if (!strncmp(msg_orig, ">", 4)) { ret = safe_chr_fn('>', buff, destp); msg_orig += 3; } else if (!strncmp(msg_orig, "&", 5)) { ret = safe_chr_fn('&', buff, destp); msg_orig += 4; } break; default: ret = safe_chr_fn(*msg_orig, buff, destp); break; } } } FUNCTION(fun_url_escape) { /* These are the characters which are converted to %<hex> */ char *escaped_chars = "<>#%{}|\\^~[]';/?:@=&\"+"; const char *msg_orig; char *temp; int ret = 0; char **destp; char tbuf[10]; temp = buff; destp = &temp; *buff = '\0'; for (msg_orig = fargs[0]; msg_orig && *msg_orig && !ret; msg_orig++) { if (index(escaped_chars, *msg_orig)) { sprintf(tbuf, "%%%2x", *msg_orig); ret = safe_str(tbuf, buff, destp); } else if (*msg_orig == ' ') { ret = safe_chr_fn('+', buff, destp); } else{ ret = safe_chr_fn(*msg_orig, buff, destp); } } } FUNCTION(fun_url_unescape) { const char *msg_orig; char *temp; int ret = 0; char **destp; unsigned int tempchar; char tempstr[10]; temp = buff; destp = &temp; *buff = '\0'; for (msg_orig = fargs[0]; msg_orig && *msg_orig && !ret;) { switch (*msg_orig) { case '+': ret = safe_chr_fn(' ', buff, destp); msg_orig++; break; case '%': strncpy(tempstr, msg_orig+1, 2); tempstr[2] = '\0'; if (sscanf(tempstr, "%x", &tempchar) == 1) ret = safe_chr_fn(tempchar, buff, destp); if (*msg_orig) msg_orig++; /* Skip the '%' */ if (*msg_orig) /* Skip the 1st hex character. */ msg_orig++; if (*msg_orig) /* Skip the 2nd hex character. */ msg_orig++; break; default: ret = safe_chr_fn(*msg_orig, buff, destp); msg_orig++; break; } } return; } #endif /* PUEBLO_SUPPORT */ /*--------------------------------------------------------------------------- * 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); } ltos(buff, i); return; } strcpy(buff, "#-1"); return; } FUNCTION(fun_type) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it)) { strcpy(buff, "#-1 NOT FOUND"); return; } switch (Typeof(it)) { case TYPE_ROOM: strcpy(buff, "ROOM"); break; case TYPE_EXIT: strcpy(buff, "EXIT"); break; case TYPE_PLAYER: strcpy(buff, "PLAYER"); break; case TYPE_THING: strcpy(buff, "THING"); break; default: strcpy(buff, "#-1 ILLEGAL TYPE"); } 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("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) { strcpy(buff, "#-1 NOT FOUND"); } else { ap = atr_num(atr); atr_pget_info(it, atr, &aowner, &aflags); if (atr_has_flag(player, it, ap, aowner, aflags, fargs[1])) strcpy(buff, "1"); else strcpy(buff, "0"); } } else { it = match_thing(player, fargs[0]); if (!Good_obj(it)) { strcpy(buff, "#-1 NOT FOUND"); return; } if (mudconf.pub_flags || Examinable(player, it) || (it == cause)) { if (has_flag(player, it, fargs[1])) strcpy(buff, "1"); else strcpy(buff, "0"); } else { strcpy(buff, "#-1 PERMISSION DENIED"); } } } /*--------------------------------------------------------------------------- * fun_orflags, fun_andflags: check multiple flags at the same time. * Based off the PennMUSH 1.50 code (and the TinyMUX derivative of it). */ static int handle_flaglists(player, name, fstr, type) dbref player; char *name; char *fstr; int type; /* 0 for orflags, 1 for andflags */ { char *s, flagletter[2]; FLAGSET fset; FLAG p_type; int negate, temp, ret; dbref it = match_thing(player, name); if (it == NOTHING) return 0; ret = type; negate = temp = 0; for (s = fstr; *s; s++) { /* Check for a negation sign. If we find it, we note it and * increment the pointer to the next character. */ if (*s == '!') { negate = 1; s++; } else { negate = 0; } if (!*s) return 0; flagletter[0] = *s; flagletter[1] = '\0'; if (!convert_flags(player, flagletter, &fset, &p_type)) { /* Either we got a '!' that wasn't followed by a letter, or * we couldn't find that flag. For AND, since we've failed * a check, we can return false. Otherwise we just go on. */ if (type == 1) return 0; else continue; } else { /* Does the object have this flag? */ if ((Flags(it) & fset.word1) || (Flags2(it) & fset.word2) || (Typeof(it) == p_type)) temp = 1; else temp = 0; if ((type == 1) && ((negate && temp) || (!negate && !temp))) { /* Too bad there's no NXOR function... * At this point we've either got a flag and we don't want * it, or we don't have a flag and nwe want it. Since it's * AND, we return false. */ return 0; } else if ((type == 0) && ((!negate && temp) || (negate && !temp))) { /* We've found something we want, in an OR. Since we only * need one thing to be true in order for the entire * expression to be true, we can return true now. */ return 1; } /* Otherwise we don't need to do anything. */ } } return (ret); } FUNCTION(fun_orflags) { ltos(buff, handle_flaglists(player, fargs[0], fargs[1], 0)); } FUNCTION(fun_andflags) { ltos(buff, handle_flaglists(player, fargs[0], fargs[1], 1)); } /*--------------------------------------------------------------------------- */ FUNCTION(fun_delete) { char *s, *d; unsigned int i, start, nchars, len; s = fargs[0]; start = atoi(fargs[1]); nchars = atoi(fargs[2]); len = strlen(s); if ((start >= len) || (nchars <= 0)) { strcpy(buff, s); return; } d = buff; for (i = 0; i < start; i++) *d++ = (*s++); if ((i + nchars) < len) { s += nchars; while ((*d++ = *s++)); } else { *d = '\0'; } } FUNCTION(fun_lock) { dbref it, aowner; int aflags; char *tbuf; ATTR *attr; struct boolexp *bool; /* Parse the argument into obj + lock */ if (!get_obj_and_lock(player, fargs[0], &it, &attr, buff)) return; /* Get the attribute and decode it if we can read it */ tbuf = atr_get(it, attr->number, &aowner, &aflags); if (!Read_attr(player, it, attr, aowner, aflags)) { *buff = '\0'; free_lbuf(tbuf); } else { bool = parse_boolexp(player, tbuf, 1); free_lbuf(tbuf); tbuf = (char *) unparse_boolexp_function(player, bool); free_boolexp(bool); strcpy(buff, tbuf); } } FUNCTION(fun_elock) { dbref it, victim, aowner; int aflags; char *tbuf; ATTR *attr; struct boolexp *bool; /* Parse lock supplier into obj + lock */ if (!get_obj_and_lock(player, fargs[0], &it, &attr, buff)) return; /* Get the victim and ensure we can do it */ victim = match_thing(player, fargs[1]); if (!Good_obj(victim)) { strcpy(buff, "#-1 NOT FOUND"); } else if (!nearby_or_control(player, victim) && !nearby_or_control(player, it)) { strcpy(buff, "#-1 TOO FAR AWAY"); } else { tbuf = atr_get(it, attr->number, &aowner, &aflags); if ((attr->number == A_LOCK) || Read_attr(player, it, attr, aowner, aflags)) { bool = parse_boolexp(player, tbuf, 1); ltos(buff, eval_boolexp(victim, it, it, bool)); free_boolexp(bool); } else { strcpy(buff, "0"); } free_lbuf(tbuf); } } /* --------------------------------------------------------------------------- * fun_lwho: Return list of connected users. */ FUNCTION(fun_lwho) { make_ulist(player, buff); } /* --------------------------------------------------------------------------- * fun_ports: Returns a list of ports for a user. */ FUNCTION(fun_ports) { dbref target; if (!Wizard(player)) { *buff = '\0'; return; } target = lookup_player(player, fargs[0], 1); if (!Good_obj(target) || !Connected(target)) { *buff = '\0'; return; } make_portlist(player, target, buff); } /* --------------------------------------------------------------------------- * 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))) strcpy(buff, "0"); else if (nearby(obj1, obj2)) strcpy(buff, "1"); else strcpy(buff, "0"); } /* --------------------------------------------------------------------------- * fun_obj, fun_poss, and fun_subj: perform pronoun sub for object. */ static void process_sex(player, what, token, buff) dbref player; char *what, *buff; const char *token; { dbref it; char *tbuff; it = match_thing(player, what); if (!Good_obj(it) || (!isPlayer(it) && !nearby_or_control(player, it))) { strcpy(buff, "#-1 NO MATCH"); } else { tbuff = exec(it, it, 0, (char *) token, (char **) NULL, 0); strcpy(buff, tbuff); free_lbuf(tbuff); } } FUNCTION(fun_obj) { process_sex(player, fargs[0], "%o", buff); } FUNCTION(fun_poss) { process_sex(player, fargs[0], "%p", buff); } FUNCTION(fun_subj) { process_sex(player, fargs[0], "%s", buff); } FUNCTION(fun_aposs) { process_sex(player, fargs[0], "%a", buff); } /* --------------------------------------------------------------------------- * fun_mudname: Return the name of the mud. */ FUNCTION(fun_mudname) { char *bp; bp = buff; safe_str(mudconf.mud_name, buff, &bp); return; } /* --------------------------------------------------------------------------- * fun_lcstr, fun_ucstr, fun_capstr: Lowercase, uppercase, or capitalize str. */ FUNCTION(fun_lcstr) { char *ap, *bp; ap = fargs[0]; bp = buff; while (*ap) { *bp = ToLower(*ap); ap++; bp++; } *bp = '\0'; } FUNCTION(fun_ucstr) { char *ap, *bp; ap = fargs[0]; bp = buff; while (*ap) { *bp = ToUpper(*ap); ap++; bp++; } *bp = '\0'; } FUNCTION(fun_capstr) { strcpy(buff, fargs[0]); *buff = ToUpper(*buff); } /* --------------------------------------------------------------------------- * fun_lnum: Return a list of numbers. */ FUNCTION(fun_lnum) { char *bp, tbuf[10], sep; int bot, top, over, i; *buff = '\0'; if (nfargs == 0) { return; } mvarargs_preamble("LNUM", 1, 3); 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; } bp = buff; over = 0; if (top == bot) { ltos(buff, bot); return; } else if (top > bot) { for (i = bot; (i <= top) && !over; i++) { if (bp != buff) { safe_chr(sep, buff, &bp); } ltos(tbuf, i); over = safe_str(tbuf, buff, &bp); } } else { for (i = bot; (i >= top) && !over; i--) { if (bp != buff) { safe_chr(sep, buff, &bp); } ltos(tbuf, i); over = safe_str(tbuf, buff, &bp); } } *bp = '\0'; } /* --------------------------------------------------------------------------- * fun_lattr: Return list of attributes I can see on the object. */ FUNCTION(fun_lattr) { dbref thing; int ca; char *bp; ATTR *attr; bp = buff; /* 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. */ 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 (bp != buff) { safe_chr(' ', buff, &bp); } safe_str((char *) attr->name, buff, &bp); } } *bp = '\0'; } else { *buff = '\0'; } return; } /* --------------------------------------------------------------------------- * fun_objmem: return the following totals for an object or obj/attr: * memory consumed by attribute text * memory consumed by attribute overhead (i.e. names, etc.) * memory consumed by other stuff * total memory consumed * * Current implementation: only the memory consumed by attribute text * is returned. */ FUNCTION(fun_objmem) { dbref thing, aowner; int atr, aflags; char *abuf; ATTR *ap; int bytes_atext = 0; if (parse_attrib_wild(player, fargs[0], &thing, 0, 0, 1)) { for (atr = olist_first(); atr != NOTHING; atr = olist_next()) { ap = atr_num(atr); if (!ap) continue; abuf = atr_get(thing, atr, &aowner, &aflags); /* Player must be able to read attribute with 'examine' */ if (Examinable(player, thing) && Read_attr(player, thing, ap, aowner, aflags)) bytes_atext += strlen(abuf); free_lbuf(abuf); } } ltos(buff, bytes_atext); } /* --------------------------------------------------------------------------- * 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) { do_reverse(fargs[0], buff); } FUNCTION(fun_revwords) { char *temp, *bp, *tp, *t1, sep; int first; /* If we are passed an empty arglist return a null string */ if (nfargs == 0) { *buff = '\0'; return; } varargs_preamble("REVWORDS", 2); temp = alloc_lbuf("fun_revwords"); /* 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; bp = buff; first = 1; while (tp) { if (!first) { safe_chr(sep, buff, &bp); } t1 = split_token(&tp, sep); do_reverse(t1, bp); while (*bp) bp++; first = 0; } *bp = '\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) { *buff = '\0'; return; } if (!fn_range_check("AFTER", nfargs, 1, 2, buff)) 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 */ *buff = '\0'; return; } /* See if what follows is what we are looking for */ if (!strncmp(cp, mp, mlen)) { /* Yup, return what follows */ bp = cp + mlen; strcpy(buff, bp); return; } /* Continue search after found first character */ bp = cp + 1; } /* Ran off the end without finding it */ *buff = '\0'; return; } FUNCTION(fun_before) { char *bp, *cp, *mp, *ip; int mlen; if (nfargs == 0) { *buff = '\0'; return; } if (!fn_range_check("BEFORE", nfargs, 1, 2, buff)) 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 */ strcpy(buff, ip); return; } /* See if what follows is what we are looking for */ if (!strncmp(cp, mp, mlen)) { /* Yup, return what follows */ *cp = '\0'; strcpy(buff, ip); return; } /* Continue search after found first character */ bp = cp + 1; } /* Ran off the end without finding it */ strcpy(buff, ip); return; } /* --------------------------------------------------------------------------- * fun_max, fun_min: Return maximum (minimum) value. */ FUNCTION(fun_max) { int i; NVAL max, val; if (nfargs < 1) { strcpy(buff, "#-1 TOO FEW ARGUMENTS"); } else { max = aton(fargs[0]); for (i = 0; i < nfargs; i++) { val = aton(fargs[i]); if (max < val) max = val; } fval(buff, max); } return; } FUNCTION(fun_min) { int i; NVAL min, val; if (nfargs < 1) { strcpy(buff, "#-1 TOO FEW ARGUMENTS"); } else { min = aton(fargs[0]); for (i = 0; i < nfargs; i++) { val = aton(fargs[i]); if (min > val) min = val; } fval(buff, 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)) { strcpy(buff, "#-1 ERROR DURING SEARCH"); return; } /* Do the search and report the results */ search_perform(player, cause, &searchparm); bp = buff; nbuf = alloc_sbuf("fun_search"); for (thing = olist_first(); thing != NOTHING; thing = olist_next()) { if (bp == buff) { *nbuf = '#'; ltos(&nbuf[1], thing); } else { nbuf[0] = ' '; nbuf[1] = '#'; ltos(&nbuf[2], thing); } safe_str(nbuf, buff, &bp); } *bp = '\0'; free_sbuf(nbuf); olist_init(); } /* --------------------------------------------------------------------------- * 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) { strcpy(buff, "#-1 NOT FOUND"); return; } } if (!get_stats(player, who, &statinfo)) { strcpy(buff, "#-1 ERROR GETTING STATS"); return; } sprintf(buff, "%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, *bp; char c; /* do length checks first */ if (strlen(fargs[0]) != strlen(fargs[1])) { strcpy(buff, "#-1 STRING LENGTHS MUST BE EQUAL"); return; } if (strlen(fargs[2]) > 1) { strcpy(buff, "#-1 TOO MANY CHARACTERS"); 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], bp = buff; *str && *rep; str++, rep++, bp++) { if (*str == c) *bp = *rep; else *bp = *str; } *bp = '\0'; /* terminate */ /* There is no need to check for overflowing the buffer since * both strings are LBUF_SIZE or less and the new string cannot be * any longer. */ return; } /* --------------------------------------------------------------------------- * fun_splice: similar to MERGE(), replaces by word instead of by character. */ FUNCTION(fun_splice) { char *bp, *p1, *p2, *q1, *q2, sep; int words, i, first; varargs_preamble("SPLICE", 4); /* length checks */ if (countwords(fargs[2], sep) > 1) { strcpy(buff, "#-1 TOO MANY WORDS"); return; } words = countwords(fargs[0], sep); if (words != countwords(fargs[1], sep)) { strcpy(buff, "#-1 NUMBER OF WORDS MUST BE EQUAL"); return; } /* loop through the two lists */ bp = buff; p1 = fargs[0]; q1 = fargs[1]; first = 1; for (i = 0; i < words; i++) { p2 = split_token(&p1, sep); q2 = split_token(&q1, sep); if (!first) { safe_chr(sep, buff, &bp); } if (!strcmp(p2, fargs[2])) safe_str(q2, buff, &bp); /* replace */ else safe_str(p2, buff, &bp); /* copy */ first = 0; } *bp = '\0'; } /* --------------------------------------------------------------------------- * fun_munge: combines two lists in an arbitrary manner. */ FUNCTION(fun_munge) { dbref aowner, thing; int aflags, anum, nptrs1, nptrs2, nresults, i, j; ATTR *ap; char *list1, *list2, *rlist; char *ptrs1[LBUF_SIZE / 2], *ptrs2[LBUF_SIZE / 2], *results[LBUF_SIZE / 2]; char *atext, *bp, sep; if ((nfargs == 0) || !fargs[0] || !*fargs[0]) { *buff = '\0'; return; } varargs_preamble("MUNGE", 4); /* Find our object and attribute */ 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]); } if (!ap) { *buff = '\0'; return; } atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { *buff = '\0'; return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); *buff = '\0'; return; } /* Copy our lists and chop them up. */ list1 = alloc_lbuf("fun_munge.list1"); list2 = alloc_lbuf("fun_munge.list2"); strcpy(list1, fargs[1]); strcpy(list2, fargs[2]); nptrs1 = list2arr(ptrs1, LBUF_SIZE / 2, list1, sep); nptrs2 = list2arr(ptrs2, LBUF_SIZE / 2, list2, sep); if (nptrs1 != nptrs2) { strcpy(buff, "#-1 LISTS MUST BE OF EQUAL SIZE"); free_lbuf(atext); free_lbuf(list1); free_lbuf(list2); return; } /* Call the u-function with the first list as %0. */ rlist = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, atext, &fargs[1], 1); /* Now that we have our result, put it back into array form. Search * through list1 until we find the element position, then copy the * corresponding element from list2. */ nresults = list2arr(results, LBUF_SIZE / 2, rlist, sep); bp = buff; for (i = 0; i < nresults; i++) { for (j = 0; j < nptrs1; j++) { if (!strcmp(results[i], ptrs1[j])) { if (bp != buff) { safe_chr(sep, buff, &bp); } safe_str(ptrs2[j], buff, &bp); ptrs1[j][0] = '\0'; break; } } } *bp = '\0'; free_lbuf(atext); free_lbuf(list1); free_lbuf(list2); free_lbuf(rlist); } /* --------------------------------------------------------------------------- * fun_repeat: repeats a string */ FUNCTION(fun_repeat) { int times, i; char *bp; times = atoi(fargs[1]); if (times < 1) { *buff = '\0'; } else if (times == 1) { strcpy(buff, fargs[0]); } else if (strlen(fargs[0]) * times >= (LBUF_SIZE - 1)) { strcpy(buff, "#-1 STRING TOO LONG"); } else { bp = buff; for (i = 0; i < times; i++) safe_str(fargs[0], buff, &bp); *bp = '\0'; } } /* --------------------------------------------------------------------------- * fun_scramble: randomizes the letters in a string. */ FUNCTION(fun_scramble) { int n, i, j; char c; if (!fargs[0] || !*fargs[0]) { *buff = '\0'; return; } strcpy(buff, fargs[0]); n = strlen(buff); for (i = 0; i < n; i++) { j = (random() % (n - i)) + i; c = buff[i]; buff[i] = buff[j]; buff[j] = c; } } /* --------------------------------------------------------------------------- * fun_shuffle: randomize order of words in a list. */ static void swap(p, q) char **p; char **q; { /* swaps two points to strings */ char *temp; temp = *p; *p = *q; *q = temp; } FUNCTION(fun_shuffle) { char *words[LBUF_SIZE]; int n, i, j; char sep; if (!nfargs || !fargs[0] || !*fargs[0]) { *buff = '\0'; return; } varargs_preamble("SHUFFLE", 2); n = list2arr(words, LBUF_SIZE, fargs[0], sep); for (i = 0; i < n; i++) { j = (random() % (n - i)) + i; swap(&words[i], &words[j]); } arr2list(words, n, buff, sep); } /* --------------------------------------------------------------------------- * fun_iter: Make list from evaluating arg2 with each member of arg1. * NOTE: This function expects that its arguments have not been evaluated. */ FUNCTION(fun_iter) { char *curr, *objstring, *buff2, *result, *bp, *cp, sep; int first; evarargs_preamble("ITER", 3); cp = curr = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, fargs[0], cargs, ncargs); cp = trim_space_sep(cp, sep); if (!*cp) { free_lbuf(curr); *buff = '\0'; return; } bp = buff; first = 1; while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { objstring = split_token(&cp, sep); buff2 = replace_string(BOUND_VAR, objstring, fargs[1]); result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, buff2, cargs, ncargs); free_lbuf(buff2); if (!first) { safe_chr(' ', buff, &bp); } first = 0; safe_str(result, buff, &bp); free_lbuf(result); } free_lbuf(curr); *bp = '\0'; } /* --------------------------------------------------------------------------- * fun_fold: iteratively eval an attrib with a list of arguments * and an optional base case. With no base case, the first list element * is passed as %0 and the second is %1. The attrib is then evaluated * with these args, the result is then used as %0 and the next arg is * %1 and so it goes as there are elements left in the list. The * optinal base case gives the user a nice starting point. * * > &REP_NUM object=[%0][repeat(%1,%1)] * > say fold(OBJECT/REP_NUM,1 2 3 4 5,->) * You say "->122333444455555" * * NOTE: To use added list separator, you must use base case! */ FUNCTION(fun_fold) { dbref aowner, thing; int aflags, anum; ATTR *ap; char *atext, *result, *curr, *bp, *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) { *buff = '\0'; return; } /* Use it if we can access it, otherwise return an error. */ atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { *buff = '\0'; return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); *buff = '\0'; return; } /* Evaluate it using the rest of the passed function args */ cp = curr = fargs[1]; bp = buff; 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 = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, clist, 2); } else { clist[0] = split_token(&cp, sep); clist[1] = split_token(&cp, sep); result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, clist, 2); } 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 = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, clist, 2); strcpy(rstore, result); free_lbuf(result); } safe_str(rstore, buff, &bp); free_lbuf(rstore); free_lbuf(atext); free_lbuf(atextbuf); } /* --------------------------------------------------------------------------- * fun_filter: iteratively perform a function with a list of arguments * and return the arg, if the function evaluates to TRUE using the * arg. * * > &IS_ODD object=mod(%0,2) * > say filter(object/is_odd,1 2 3 4 5) * You say "1 3 5" * > say filter(object/is_odd,1-2-3-4-5,-) * You say "1-3-5" * * NOTE: If you specify a separator it is used to delimit returned list */ FUNCTION(fun_filter) { dbref aowner, thing; int aflags, anum, first; ATTR *ap; char *atext, *result, *curr, *objstring, *bp, *cp, *atextbuf, sep; varargs_preamble("FILTER", 3); /* Two possibilities for the first arg: <obj>/<attr> and <attr>. */ if (parse_attrib(player, fargs[0], &thing, &anum)) { if ((anum == NOTHING) || (!Good_obj(thing))) ap = NULL; else ap = atr_num(anum); } else { thing = player; ap = atr_str(fargs[0]); } /* Make sure we got a good attribute */ if (!ap) { *buff = '\0'; return; } /* Use it if we can access it, otherwise return an error. */ atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { *buff = '\0'; return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); *buff = '\0'; return; } /* Now iteratively eval the attrib with the argument list */ cp = curr = trim_space_sep(fargs[1], sep); bp = buff; atextbuf = alloc_lbuf("fun_filter"); first = 1; while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { objstring = split_token(&cp, sep); strcpy(atextbuf, atext); result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, &objstring, 1); if (!first && *result == '1') { safe_chr(sep, buff, &bp); } first = 0; if (*result == '1') safe_str(objstring, buff, &bp); free_lbuf(result); } free_lbuf(atext); free_lbuf(atextbuf); *bp = '\0'; } /* --------------------------------------------------------------------------- * fun_map: iteratively evaluate an attribute with a list of arguments. * * > &DIV_TWO object=fdiv(%0,2) * > say map(1 2 3 4 5,object/div_two) * You say "0.5 1 1.5 2 2.5" * > say map(object/div_two,1-2-3-4-5,-) * You say "0.5-1-1.5-2-2.5" * */ FUNCTION(fun_map) { dbref aowner, thing; int aflags, anum, first; ATTR *ap; char *atext, *result, *objstring, *bp, *cp, *atextbuf, sep; varargs_preamble("MAP", 3); *buff = '\0'; /* 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); if (!atext) { return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); return; } /* now process the list one element at a time */ cp = trim_space_sep(fargs[1], sep); bp = buff; atextbuf = alloc_lbuf("fun_map"); first = 1; while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { objstring = split_token(&cp, sep); strcpy(atextbuf, atext); result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, &objstring, 1); if (!first) { safe_chr(sep, buff, &bp); } else { first = 0; } safe_str(result, buff, &bp); free_lbuf(result); } free_lbuf(atext); free_lbuf(atextbuf); *bp = '\0'; } /* --------------------------------------------------------------------------- * fun_mix: Like map, but operates on two lists simultaneously, passing * the elements as %0 as %1. */ FUNCTION(fun_mix) { dbref aowner, thing; int aflags, anum, first; ATTR *ap; char *atext, *result, *os[2], *bp, *cp1, *cp2, *atextbuf, sep; varargs_preamble("MIX", 4); /* Get the attribute, check the permissions. */ 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]); } if (!ap) { *buff = '\0'; return; } atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { *buff = '\0'; return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); *buff = '\0'; return; } /* process the two lists, one element at a time. */ cp1 = trim_space_sep(fargs[1], sep); cp2 = trim_space_sep(fargs[2], sep); if (countwords(cp1, sep) != countwords(cp2, sep)) { free_lbuf(atext); strcpy(buff, "#-1 LISTS MUST BE OF EQUAL SIZE"); return; } atextbuf = alloc_lbuf("fun_mix"); bp = buff; first = 1; while (cp1 && cp2 && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { os[0] = split_token(&cp1, sep); os[1] = split_token(&cp2, sep); strcpy(atextbuf, atext); result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, &(os[0]), 2); if (!first) { safe_chr(sep, buff, &bp); } else { first = 0; } safe_str(result, buff, &bp); free_lbuf(result); } free_lbuf(atext); free_lbuf(atextbuf); *bp = '\0'; } /* --------------------------------------------------------------------------- * fun_foreach: like map(), but it operates on a string, rather than on a list, * calling a user-defined function for each character in the string. * No delimiter is inserted between the results. */ FUNCTION(fun_foreach) { dbref aowner, thing; int aflags, anum; ATTR *ap; char *atext, *atextbuf, *result, *bp, *cp, *cbuf; 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]); } if (!ap) { *buff = '\0'; return; } atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { *buff = '\0'; return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); *buff = '\0'; return; } atextbuf = alloc_lbuf("fun_foreach"); cbuf = alloc_lbuf("fun_foreach.cbuf"); cp = trim_space_sep(fargs[1], ' '); bp = buff; while (cp && *cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) { cbuf[0] = *cp++; cbuf[1] = '\0'; strcpy(atextbuf, atext); result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, &cbuf, 1); safe_str(result, buff, &bp); free_lbuf(result); } *bp = '\0'; free_lbuf(atextbuf); free_lbuf(atext); free_lbuf(cbuf); } /* --------------------------------------------------------------------------- * fun_edit: Edit text. */ FUNCTION(fun_edit) { char *tstr; edit_string(fargs[0], &tstr, fargs[1], fargs[2]); strcpy(buff, tstr); 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 (Wizard(player)) thing = match_thing(player, fargs[0]); else thing = match_controlled(player, fargs[0]); if (!Good_obj(thing)) { strcpy(buff, "#-1 PERMISSION DENIED"); 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); *buff = '#'; ltos(&buff[1], what); } /* --------------------------------------------------------------------------- * fun_switch: Return value based on pattern matching (ala @switch) * NOTE: This function expects that its arguments have not been evaluated. */ FUNCTION(fun_switch) { int i; char *mbuff, *tbuff; /* If we don't have at least 2 args, return nothing */ if (nfargs < 2) { *buff = '\0'; return; } /* Evaluate the target in fargs[0] */ mbuff = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, fargs[0], cargs, ncargs); /* Loop through the patterns looking for a match */ for (i = 1; (i < nfargs - 1) && fargs[i] && fargs[i + 1]; i += 2) { tbuff = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, fargs[i], cargs, ncargs); if (quick_wild(tbuff, mbuff)) { free_lbuf(tbuff); tbuff = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, fargs[i + 1], cargs, ncargs); strcpy(buff, tbuff); free_lbuf(mbuff); free_lbuf(tbuff); return; } free_lbuf(tbuff); } free_lbuf(mbuff); /* Nope, return the default if there is one */ if ((i < nfargs) && fargs[i]) { tbuff = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, fargs[i], cargs, ncargs); strcpy(buff, tbuff); free_lbuf(tbuff); } else { *buff = '\0'; } return; } /* --------------------------------------------------------------------------- * fun_space: Make spaces. */ FUNCTION(fun_space) { int num; char *cp; 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; } } else if (num >= LBUF_SIZE) { num = LBUF_SIZE - 1; } for (cp = buff; num > 0; num--) *cp++ = ' '; *cp = '\0'; return; } /* --------------------------------------------------------------------------- * fun_idle, fun_conn: return seconds idle or connected. */ FUNCTION(fun_idle) { dbref target; target = lookup_player(player, fargs[0], 1); if (Good_obj(target) && Dark(target) && !Wizard(player)) target = NOTHING; ltos(buff, fetch_idle(target)); } FUNCTION(fun_conn) { dbref target; target = lookup_player(player, fargs[0], 1); if (Good_obj(target) && Dark(target) && !Wizard(player)) target = NOTHING; ltos(buff, 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 = atof(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; char *ptrs[LBUF_SIZE / 2]; /* If we are passed an empty arglist return a null string */ if (nfargs == 0) { *buff = '\0'; return; } mvarargs_preamble("SORT", 1, 3); /* 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, sep); free_lbuf(list); } static char ucomp_buff[LBUF_SIZE]; static dbref ucomp_cause; static dbref ucomp_player; static int u_comp(s1, s2) const void *s1, *s2; { /* Note that this function is for use in conjunction with our own * sane_qsort routine, NOT with the standard library qsort! */ char *result, tbuf[LBUF_SIZE], *elems[2]; int n; /* Prevent hideously large recursive sorts. */ if ((mudstate.func_invk_ctr > mudconf.func_invk_lim) || (mudstate.func_nest_lev > mudconf.func_nest_lim)) return 0; elems[0] = (char *) s1; elems[1] = (char *) s2; strcpy(tbuf, ucomp_buff); result = exec(ucomp_player, ucomp_cause, EV_STRIP | EV_FCHECK | EV_EVAL, tbuf, &(elems[0]), 2); if (!result) n = 0; else { n = atoi(result); free_lbuf(result); } return n; } static void sane_qsort(array, left, right, compare) void *array[]; int left, right; int (*compare)(); { /* Andrew Molitor's qsort, which doesn't require transitivity between * comparisons (essential for preventing crashes due to boneheads * who write comparison functions where a > b doesn't mean b < a). */ int i, last; void *tmp; loop: if (left >= right) return; /* Pick something at random at swap it into the leftmost slot */ /* This is the pivot, we'll put it back in the right spot later */ i = random() % (1 + (right - left)); tmp = array[left + i]; array[left + i] = array[left]; array[left] = tmp; last = left; for(i = left+1; i <= right; i++) { /* Walk the array, looking for stuff that's less than our */ /* pivot. If it is, swap it with the next thing along */ if ((*compare)(array[i], array[left]) < 0) { last++; if (last == i) continue; tmp = array[last]; array[last] = array[i]; array[i] = tmp; } } /* Now we put the pivot back, it's now in the right spot, we never */ /* need to look at it again, trust me. */ tmp = array[last]; array[last] = array[left]; array[left] = tmp; /* At this point everything underneath the 'last' index is < the */ /* entry at 'last' and everything above it is not < it. */ if ((last - left) < (right - last)) { sane_qsort(array, left, last-1, compare); left = last+1; goto loop; } else { sane_qsort(array, last+1, right, compare); right = last - 1; goto loop; } } FUNCTION(fun_sortby) { char *atext, *list, *ptrs[LBUF_SIZE / 2], sep; int nptrs, aflags, anum; dbref thing, aowner; ATTR *ap; if ((nfargs == 0) || !fargs[0] || !*fargs[0]) { *buff = '\0'; return; } varargs_preamble("SORTBY", 3); 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]); } if (!ap) { *buff = '\0'; return; } atext = atr_pget(thing, ap->number, &aowner, &aflags); if (!atext) { *buff = '\0'; return; } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) { free_lbuf(atext); *buff = '\0'; return; } strcpy(ucomp_buff, atext); ucomp_player = thing; ucomp_cause = cause; list = alloc_lbuf("fun_sortby"); strcpy(list, fargs[1]); nptrs = list2arr(ptrs, LBUF_SIZE / 2, list, sep); if (nptrs > 1) /* pointless to sort less than 2 elements */ sane_qsort((void *) ptrs, 0, nptrs-1, u_comp); arr2list(ptrs, nptrs, buff, sep); free_lbuf(list); free_lbuf(atext); } /*--------------------------------------------------------------------------- * 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, oper, sep) char *fargs[], *buff, sep; int oper; { char *list1, *list2, *p, *oldp; char *ptrs1[LBUF_SIZE], *ptrs2[LBUF_SIZE]; int i1, i2, n1, n2, val, first; 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; first = 1; oldp = p = buff; *p = '\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, &p); break; } /* Process until one list is empty */ while ((i1 < n1) && (i2 < n2)) { /* Skip over duplicates */ if ((i1 > 0) || (i2 > 0)) { while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; while ((i2 < n2) && !strcmp(ptrs2[i2], oldp)) i2++; } /* Compare and copy */ if ((i1 < n1) && (i2 < n2)) { if (!first) { safe_chr(sep, buff, &p); } first = 0; oldp = p; if (strcmp(ptrs1[i1], ptrs2[i2]) < 0) { safe_str(ptrs1[i1], buff, &p); i1++; } else { safe_str(ptrs2[i2], buff, &p); i2++; } *p = '\0'; } } /* Copy rest of remaining list, stripping duplicates */ for (; i1 < n1; i1++) { if (strcmp(oldp, ptrs1[i1])) { if (!first) { safe_chr(sep, buff, &p); } first = 0; oldp = p; safe_str(ptrs1[i1], buff, &p); *p = '\0'; } } for (; i2 < n2; i2++) { if (strcmp(oldp, ptrs2[i2])) { if (!first) { safe_chr(sep, buff, &p); } first = 0; oldp = p; safe_str(ptrs2[i2], buff, &p); *p = '\0'; } } break; case SET_INTERSECT: /* Copy elements not in both lists */ while ((i1 < n1) && (i2 < n2)) { val = strcmp(ptrs1[i1], ptrs2[i2]); if (!val) { /* Got a match, copy it */ if (!first) { safe_chr(sep, buff, &p); } first = 0; oldp = p; safe_str(ptrs1[i1], buff, &p); i1++; i2++; while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; while ((i2 < n2) && !strcmp(ptrs2[i2], oldp)) i2++; } else if (val < 0) { i1++; } else { i2++; } } break; case SET_DIFF: /* Copy elements unique to list1 */ while ((i1 < n1) && (i2 < n2)) { val = strcmp(ptrs1[i1], ptrs2[i2]); if (!val) { /* Got a match, increment pointers */ oldp = ptrs1[i1]; while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; while ((i2 < n2) && !strcmp(ptrs2[i2], oldp)) i2++; } else if (val < 0) { /* Item in list1 not in list2, copy */ if (!first) { safe_chr(sep, buff, &p); } first = 0; safe_str(ptrs1[i1], buff, &p); oldp = ptrs1[i1]; i1++; while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; } else { /* Item in list2 but not in list1, discard */ oldp = ptrs2[i2]; i2++; while ((i2 < n2) && !strcmp(ptrs2[i2], oldp)) i2++; } } /* Copy remainder of list1 */ while (i1 < n1) { if (!first) { safe_chr(sep, buff, &p); } first = 0; safe_str(ptrs1[i1], buff, &p); oldp = ptrs1[i1]; i1++; while ((i1 < n1) && !strcmp(ptrs1[i1], oldp)) i1++; } } *p = '\0'; free_lbuf(list1); free_lbuf(list2); return; } FUNCTION(fun_setunion) { char sep; varargs_preamble("SETUNION", 3); handle_sets(fargs, buff, SET_UNION, sep); return; } FUNCTION(fun_setdiff) { char sep; varargs_preamble("SETDIFF", 3); handle_sets(fargs, buff, SET_DIFF, sep); return; } FUNCTION(fun_setinter) { char sep; varargs_preamble("SETINTER", 3); handle_sets(fargs, buff, SET_INTERSECT, sep); return; } /* --------------------------------------------------------------------------- * rjust, ljust, center: Justify or center text, specifying fill character */ FUNCTION(fun_ljust) { int spaces, i; char *bp, sep; varargs_preamble("LJUST", 3); spaces = atoi(fargs[1]) - strlen(fargs[0]); /* Sanitize number of spaces */ if (spaces <= 0) { /* no padding needed, just return string */ strcpy(buff, fargs[0]); return; } else if (spaces > LBUF_SIZE) { spaces = LBUF_SIZE; } bp = buff; safe_str(fargs[0], buff, &bp); for (i = 0; i < spaces; i++) { safe_chr(sep, buff, &bp); } } FUNCTION(fun_rjust) { int spaces, i; char *bp, sep; varargs_preamble("RJUST", 3); spaces = atoi(fargs[1]) - strlen(fargs[0]); /* Sanitize number of spaces */ if (spaces <= 0) { /* no padding needed, just return string */ strcpy(buff, fargs[0]); return; } else if (spaces > LBUF_SIZE) { spaces = LBUF_SIZE; } bp = buff; for (i = 0; i < spaces; i++) { safe_chr(sep, buff, &bp); } safe_str(fargs[0], buff, &bp); } FUNCTION(fun_center) { char *p, sep; int i, len, lead_chrs, trail_chrs, width; varargs_preamble("CENTER", 3); width = atoi(fargs[1]); len = strlen(fargs[0]); if (len >= width) { strcpy(buff, fargs[0]); return; } p = buff; lead_chrs = (width / 2) - (len / 2) + .5; for (i = 0; i < lead_chrs; i++) { safe_chr(sep, buff, &p); } safe_str(fargs[0], buff, &p); trail_chrs = width - lead_chrs - len; for (i = 0; i < trail_chrs; i++) { safe_chr(sep, buff, &p); } } /* --------------------------------------------------------------------------- * setq, r: set and read global registers. */ FUNCTION(fun_setq) { int regnum; regnum = atoi(fargs[0]); if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) { strcpy(buff, "#-1 INVALID GLOBAL REGISTER"); } else { if (!mudstate.global_regs[regnum]) mudstate.global_regs[regnum] = alloc_lbuf("fun_setq"); strcpy(mudstate.global_regs[regnum], fargs[1]); *buff = '\0'; } } FUNCTION(fun_r) { int regnum; regnum = atoi(fargs[0]); if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) { strcpy(buff, "#-1 INVALID GLOBAL REGISTER"); } else if (mudstate.global_regs[regnum]) { strcpy(buff, mudstate.global_regs[regnum]); } else { *buff = '\0'; } } /* --------------------------------------------------------------------------- * isword: is every character in the argument a letter? */ FUNCTION(fun_isword) { char *p; for (p = fargs[0]; *p; p++) { if (!isalpha(*p)) { strcpy(buff, "0"); return; } } strcpy(buff, "1"); } /* --------------------------------------------------------------------------- * isnum: is the argument a number? */ FUNCTION(fun_isnum) { strcpy(buff, (is_number(fargs[0]) ? "1" : "0")); } /* --------------------------------------------------------------------------- * isdbref: is the argument a valid dbref? */ FUNCTION(fun_isdbref) { char *p; dbref dbitem; p = fargs[0]; if (*p++ == NUMBER_TOKEN) { dbitem = parse_dbref(p); if (Good_obj(dbitem)) { strcpy(buff, "1"); return; } } strcpy(buff, "0"); } /* --------------------------------------------------------------------------- * trim: trim off unwanted white space. */ FUNCTION(fun_trim) { char *p, *lastchar, *q, sep; int trim; if (nfargs == 0) { *buff = '\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; } } strcpy(buff, q); } /* --------------------------------------------------------------------------- * fun_squish: Squash occurrences of a given character down to 1. * We do this both on leading and trailing chars, as well as * internal ones; if the player wants to trim off the leading * and trailing as well, they can always call trim(). */ FUNCTION(fun_squish) { char *bp, *tp, sep; if (nfargs == 0) { *buff = '\0'; return; } varargs_preamble("SQUISH", 2); bp = buff; tp = fargs[0]; while (*tp) { while (*tp && (*tp != sep)) /* copy non-seps */ *bp++ = *tp++; if (!*tp) { /* end of string */ *bp = '\0'; return; } /* We've hit a sep char. Copy it, then skip to the next non-sep. */ *bp++ = *tp++; while (*tp && (*tp == sep)) tp++; } *bp = '\0'; } /* --------------------------------------------------------------------------- * 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}, {"AND", fun_and, 0, FN_VARARGS, CA_PUBLIC}, {"ANDFLAGS", fun_andflags, 2, 0, CA_PUBLIC}, {"APOSS", fun_aposs, 1, 0, CA_PUBLIC}, {"ASIN", fun_asin, 1, 0, CA_PUBLIC}, {"ATAN", fun_atan, 1, 0, CA_PUBLIC}, {"BEFORE", fun_before, 0, FN_VARARGS, CA_PUBLIC}, {"CAPSTR", fun_capstr, -1, 0, CA_PUBLIC}, {"CAT", fun_cat, 0, FN_VARARGS, CA_PUBLIC}, {"CEIL", fun_ceil, 1, 0, CA_PUBLIC}, {"CENTER", fun_center, 0, FN_VARARGS, CA_PUBLIC}, {"COMP", fun_comp, 2, 0, CA_PUBLIC}, {"CON", fun_con, 1, 0, CA_PUBLIC}, {"CONN", fun_conn, 1, 0, CA_PUBLIC}, {"CONTROLS", fun_controls, 2, 0, CA_PUBLIC}, {"CONVSECS", fun_convsecs, 1, 0, CA_PUBLIC}, {"CONVTIME", fun_convtime, 1, 0, CA_PUBLIC}, {"COS", fun_cos, 1, 0, CA_PUBLIC}, {"DEFAULT", fun_default, 2, FN_NO_EVAL, CA_PUBLIC}, {"DELETE", fun_delete, 3, 0, CA_PUBLIC}, {"DIST2D", fun_dist2d, 4, 0, CA_PUBLIC}, {"DIST3D", fun_dist3d, 6, 0, CA_PUBLIC}, {"DIV", fun_div, 2, 0, CA_PUBLIC}, {"E", fun_e, 0, 0, CA_PUBLIC}, {"EDEFAULT", fun_edefault, 2, FN_NO_EVAL, CA_PUBLIC}, {"EDIT", fun_edit, 3, 0, CA_PUBLIC}, {"ELEMENTS", fun_elements, 0, FN_VARARGS, CA_PUBLIC}, {"ELOCK", fun_elock, 2, 0, CA_PUBLIC}, {"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}, {"FDIV", fun_fdiv, 2, 0, CA_PUBLIC}, {"FILTER", fun_filter, 0, FN_VARARGS, CA_PUBLIC}, {"FIRST", fun_first, 0, FN_VARARGS, CA_PUBLIC}, {"FLAGS", fun_flags, 1, 0, CA_PUBLIC}, {"FLOOR", fun_floor, 1, 0, CA_PUBLIC}, {"FOLD", fun_fold, 0, FN_VARARGS, CA_PUBLIC}, {"FOREACH", fun_foreach, 2, 0, CA_PUBLIC}, {"FINDABLE", fun_findable, 2, 0, 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}, {"GT", fun_gt, 2, 0, CA_PUBLIC}, {"GTE", fun_gte, 2, 0, CA_PUBLIC}, {"HASATTR", fun_hasattr, 2, 0, CA_PUBLIC}, {"HASFLAG", fun_hasflag, 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}, {"INDEX", fun_index, 4, 0, CA_PUBLIC}, {"INSERT", fun_insert, 0, FN_VARARGS, CA_PUBLIC}, {"ISDBREF", fun_isdbref, 1, 0, CA_PUBLIC}, {"ISNUM", fun_isnum, 1, 0, CA_PUBLIC}, {"ISWORD", fun_isword, 1, 0, CA_PUBLIC}, {"ITER", fun_iter, 0, FN_VARARGS | FN_NO_EVAL, CA_PUBLIC}, {"LAST", fun_last, 0, FN_VARARGS, CA_PUBLIC}, {"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}, {"LEXITS", fun_lexits, 1, 0, CA_PUBLIC}, {"LJUST", fun_ljust, 0, FN_VARARGS, CA_PUBLIC}, {"LN", fun_ln, 1, 0, CA_PUBLIC}, {"LNUM", fun_lnum, 0, FN_VARARGS, CA_PUBLIC}, {"LOC", fun_loc, 1, 0, CA_PUBLIC}, {"LOCATE", fun_locate, 3, 0, CA_PUBLIC}, {"LOCK", fun_lock, 1, 0, CA_PUBLIC}, {"LOG", fun_log, 1, 0, CA_PUBLIC}, {"LPOS", fun_lpos, 2, 0, CA_PUBLIC}, {"LT", fun_lt, 2, 0, CA_PUBLIC}, {"LTE", fun_lte, 2, 0, CA_PUBLIC}, {"LWHO", fun_lwho, 0, 0, CA_PUBLIC}, {"MAP", fun_map, 0, FN_VARARGS, CA_PUBLIC}, {"MATCH", fun_match, 0, FN_VARARGS, CA_PUBLIC}, {"MATCHALL", fun_matchall, 0, FN_VARARGS, CA_PUBLIC}, {"MAX", fun_max, 0, FN_VARARGS, CA_PUBLIC}, {"MEMBER", fun_member, 0, FN_VARARGS, CA_PUBLIC}, {"MERGE", fun_merge, 3, 0, CA_PUBLIC}, {"MID", fun_mid, 3, 0, CA_PUBLIC}, {"MIN", fun_min, 0, FN_VARARGS, CA_PUBLIC}, {"MIX", fun_mix, 0, FN_VARARGS, CA_PUBLIC}, {"MOD", fun_mod, 2, 0, CA_PUBLIC}, {"MONEY", fun_money, 1, 0, CA_PUBLIC}, {"MUDNAME", fun_mudname, 0, 0, CA_PUBLIC}, {"MUL", fun_mul, 0, FN_VARARGS, CA_PUBLIC}, {"MUNGE", fun_munge, 0, FN_VARARGS, CA_PUBLIC}, {"NAME", fun_name, 1, 0, CA_PUBLIC}, {"NEARBY", fun_nearby, 2, 0, CA_PUBLIC}, {"NEQ", fun_neq, 2, 0, CA_PUBLIC}, {"NEXT", fun_next, 1, 0, CA_PUBLIC}, {"NOT", fun_not, 1, 0, CA_PUBLIC}, {"NUM", fun_num, 1, 0, CA_PUBLIC}, {"OBJ", fun_obj, 1, 0, CA_PUBLIC}, {"OBJEVAL", fun_objeval, 2, FN_NO_EVAL, CA_PUBLIC}, {"OBJMEM", fun_objmem, 1, 0, CA_PUBLIC}, {"OR", fun_or, 0, FN_VARARGS, CA_PUBLIC}, {"ORFLAGS", fun_orflags, 2, 0, CA_PUBLIC}, {"OWNER", fun_owner, 1, 0, CA_PUBLIC}, {"PARENT", fun_parent, 1, 0, CA_PUBLIC}, {"PARSE", fun_parse, 0, FN_VARARGS | FN_NO_EVAL, CA_PUBLIC}, {"PI", fun_pi, 0, 0, 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}, {"R", fun_r, 1, 0, CA_PUBLIC}, {"RAND", fun_rand, 1, 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}, {"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}, {"SETDIFF", fun_setdiff, 0, FN_VARARGS, CA_PUBLIC}, {"SETINTER", fun_setinter, 0, FN_VARARGS, CA_PUBLIC}, {"SETQ", fun_setq, 2, 0, CA_PUBLIC}, {"SETUNION", fun_setunion, 0, FN_VARARGS, CA_PUBLIC}, {"SHUFFLE", fun_shuffle, 0, FN_VARARGS, CA_PUBLIC}, {"SIGN", fun_sign, 1, 0, CA_PUBLIC}, {"SIN", fun_sin, 1, 0, CA_PUBLIC}, {"SORT", fun_sort, 0, FN_VARARGS, CA_PUBLIC}, {"SORTBY", fun_sortby, 0, FN_VARARGS, CA_PUBLIC}, {"SPACE", fun_space, 1, 0, CA_PUBLIC}, {"SPLICE", fun_splice, 0, FN_VARARGS, CA_PUBLIC}, {"SQRT", fun_sqrt, 1, 0, CA_PUBLIC}, {"SQUISH", fun_squish, 0, FN_VARARGS, CA_PUBLIC}, {"STARTTIME", fun_starttime, 0, 0, CA_PUBLIC}, {"STATS", fun_stats, 1, 0, CA_PUBLIC}, {"STRLEN", fun_strlen, -1, 0, CA_PUBLIC}, {"STRMATCH", fun_strmatch, 2, 0, CA_PUBLIC}, {"SUB", fun_sub, 2, 0, CA_PUBLIC}, {"SUBJ", fun_subj, 1, 0, CA_PUBLIC}, {"SWITCH", fun_switch, 0, FN_VARARGS | FN_NO_EVAL, CA_PUBLIC}, {"TAN", fun_tan, 1, 0, CA_PUBLIC}, {"TIME", fun_time, 0, 0, CA_PUBLIC}, {"TRIM", fun_trim, 0, FN_VARARGS, CA_PUBLIC}, {"TRUNC", fun_trunc, 1, 0, CA_PUBLIC}, {"TYPE", fun_type, 1, 0, CA_PUBLIC}, {"U", fun_u, 0, FN_VARARGS, CA_PUBLIC}, {"UCSTR", fun_ucstr, -1, 0, CA_PUBLIC}, {"UDEFAULT", fun_udefault, 0, FN_VARARGS | FN_NO_EVAL, CA_PUBLIC}, {"ULOCAL", fun_ulocal, 0, FN_VARARGS, CA_PUBLIC}, #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}, {"VERSION", fun_version, 0, 0, CA_PUBLIC}, {"VISIBLE", fun_visible, 2, 0, CA_PUBLIC}, {"WHERE", fun_where, 1, 0, CA_PUBLIC}, {"WORDPOS", fun_wordpos, 0, FN_VARARGS, CA_PUBLIC}, {"WORDS", fun_words, 0, FN_VARARGS, CA_PUBLIC}, {"XCON", fun_xcon, 3, 0, CA_PUBLIC}, {"XOR", fun_xor, 0, FN_VARARGS, CA_PUBLIC}, {NULL, NULL, 0, 0, 0} }; void NDECL(init_functab) { FUN *fp; char *buff, *cp, *dp; buff = alloc_sbuf("init_functab"); hashinit(&mudstate.func_htab, 217); 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, 11); } 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); 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, "I don't see that here."); free_sbuf(np); return; } /* Make sure the attribute exists */ if (atr == NOTHING) { notify_quiet(player, "No such attribute."); free_sbuf(np); return; } /* Make sure attribute is readably by me */ ap = atr_num(atr); if (!ap) { notify_quiet(player, "No such attribute."); free_sbuf(np); return; } atr_get_info(obj, atr, &aowner, &aflags); if (!See_attr(player, obj, ap, aowner, aflags)) { notify_quiet(player, "Permission denied."); free_sbuf(np); return; } /* Privileged functions require you control the obj. */ if ((key & FN_PRIV) && !Controls(player, obj)) { notify_quiet(player, "Permission denied."); 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((char *) 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; }