/** * \file function.c * * \brief The function parser. * * */ #include "copyrite.h" #include "config.h" #include <limits.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include "conf.h" #include "externs.h" #include "attrib.h" #include "dbdefs.h" #include "mushdb.h" #include "function.h" #include "match.h" #include "htab.h" #include "parse.h" #include "lock.h" #include "flags.h" #include "game.h" #include "mymalloc.h" #include "funs.h" #include "confmagic.h" static void func_hash_insert(const char *name, FUN *func); extern void local_functions(void); static int apply_restrictions(unsigned int result, const char *restriction); USERFN_ENTRY *userfn_tab; /**< Table of user-defined functions */ HASHTAB htab_function; /**< Function hash table */ HASHTAB htab_user_function; /**< User-defined function hash table */ /* -------------------------------------------------------------------------* * Utilities. */ /** Save a copy of the q-registers. * \param funcname name of function calling (for memory leak testing) * \param preserve pointer to array to store the q-registers in. */ void save_global_regs(const char *funcname, char *preserve[]) { int i; for (i = 0; i < NUMQ; i++) { if (!global_eval_context.renv[i][0]) preserve[i] = NULL; else { preserve[i] = (char *) mush_malloc(BUFFER_LEN, funcname); strcpy(preserve[i], global_eval_context.renv[i]); } } } /** Restore the q-registers, freeing the storage array. * \param funcname name of function calling (for memory leak testing) * \param preserve pointer to array to restore the q-registers from. */ void restore_global_regs(const char *funcname, char *preserve[]) { int i; for (i = 0; i < NUMQ; i++) { if (preserve[i]) { strcpy(global_eval_context.renv[i], preserve[i]); mush_free(preserve[i], funcname); preserve[i] = NULL; } else { global_eval_context.renv[i][0] = '\0'; } } } /** Free the storage array for the q-registers, without restoring * \param funcname name of function calling (for memory leak testing) * \param preserve pointer to array to free q-registers from. */ void free_global_regs(const char *funcname, char *preserve[]) { int i; for (i = 0; i < NUMQ; i++) { if (preserve[i]) mush_free(preserve[i], funcname); } } /** Initilalize an array for the q-registers, setting all NULL. * \param preserve pointer to array to free q-registers from. */ void init_global_regs(char *preserve[]) { int i; for (i = 0; i < NUMQ; i++) { preserve[i] = NULL; } } /** Restore the q-registers, without freeing the storage array. * \param preserve pointer to array to restore the q-registers from. */ void load_global_regs(char *preserve[]) { int i; for (i = 0; i < NUMQ; i++) { if (preserve[i]) { strcpy(global_eval_context.renv[i], preserve[i]); } else { global_eval_context.renv[i][0] = '\0'; } } } /** Save a copy of the environment (%0-%9) * \param funcname name of function calling (for memory leak testing) * \param preserve pointer to array to store %0-%9 in. */ void save_global_env(const char *funcname __attribute__ ((__unused__)), char *preserve[]) { int i; for (i = 0; i < 10; i++) preserve[i] = global_eval_context.wenv[i]; } /** Restore the environment (%0-%9) * \param funcname name of function calling (for memory leak testing) * \param preserve pointer to array to restore %0-%9 from. */ void restore_global_env(const char *funcname __attribute__ ((__unused__)), char *preserve[]) { int i; for (i = 0; i < 10; i++) global_eval_context.wenv[i] = preserve[i]; } /** Check for a delimiter in an argument of a function call. * This function checks a given argument of a function call and sees * if it could be used as a delimiter. A delimiter must be a single * character. If the argument isn't present or is null, we return * the default delimiter, a space. * \param buff unused. * \param bp unused. * \param nfargs number of arguments to the function. * \param fargs array of function arguments. * \param sep_arg index of the argument to check for a delimiter. * \param sep pointer to separator character, used to return separator. * \retval 0 illegal separator argument. * \retval 1 successfully returned a separator (maybe the default one). */ int delim_check(char *buff, char **bp, int nfargs, char *fargs[], int sep_arg, char *sep) { /* Find a delimiter. */ if (nfargs >= sep_arg) { if (!*fargs[sep_arg - 1]) *sep = ' '; else if (strlen(fargs[sep_arg - 1]) != 1) { safe_str(T("#-1 SEPARATOR MUST BE ONE CHARACTER"), buff, bp); return 0; } else *sep = *fargs[sep_arg - 1]; } else *sep = ' '; return 1; } /* -------------------------------------------------------------------------- * The actual function handlers */ /** An entry in the function table. * This structure represents a function's entry in the function table. */ typedef struct fun_tab { const char *name; /**< Name of the function, uppercase. */ function_func fun; /**< Pointer to code to call for this function. */ int minargs; /**< Minimum args required. */ int maxargs; /**< Maximum args, or INT_MAX. If <0, last arg may have commas */ int flags; /**< Flags to control how the function is parsed. */ } FUNTAB; /** The function table. Functions can also be added at runtime with * add_function(). */ FUNTAB flist[] = { {"@@", fun_atat, 1, -1, FN_NOPARSE}, {"ABS", fun_abs, 1, 1, FN_REG}, {"ACCENT", fun_accent, 2, 2, FN_REG}, {"ACCNAME", fun_accname, 1, 1, FN_REG}, {"ADD", fun_add, 2, INT_MAX, FN_REG}, {"AFTER", fun_after, 2, 2, FN_REG}, {"ALIGN", fun_align, 2, INT_MAX, FN_REG}, {"ALLOF", fun_allof, 2, INT_MAX, FN_NOPARSE}, {"ALPHAMAX", fun_alphamax, 1, INT_MAX, FN_REG}, {"ALPHAMIN", fun_alphamin, 1, INT_MAX, FN_REG}, {"AND", fun_and, 2, INT_MAX, FN_REG}, {"ANDFLAGS", fun_andflags, 2, 2, FN_REG}, {"ANDLFLAGS", fun_andlflags, 2, 2, FN_REG}, {"ANDLPOWERS", fun_andlflags, 2, 2, FN_REG}, {"ANDPOWERS", fun_andflags, 2, 2, FN_REG}, {"ANSI", fun_ansi, 2, -2, FN_NOPARSE}, {"APOSS", fun_aposs, 1, 1, FN_REG}, {"ART", fun_art, 1, 1, FN_REG}, {"ATRLOCK", fun_atrlock, 1, 2, FN_REG}, {"BAND", fun_band, 1, INT_MAX, FN_REG}, {"BASECONV", fun_baseconv, 3, 3, FN_REG}, {"BEEP", fun_beep, 0, 1, FN_REG}, {"BEFORE", fun_before, 2, 2, FN_REG}, {"BNAND", fun_bnand, 2, 2, FN_REG}, {"BNOT", fun_bnot, 1, 1, FN_REG}, {"BOR", fun_bor, 1, INT_MAX, FN_REG}, {"BOUND", fun_bound, 2, 3, FN_REG}, {"BRACKETS", fun_brackets, 1, 1, FN_REG}, {"BXOR", fun_bxor, 1, INT_MAX, FN_REG}, {"CAND", fun_cand, 2, INT_MAX, FN_NOPARSE}, {"CAPSTR", fun_capstr, 1, -1, FN_REG}, {"CASE", fun_switch, 3, INT_MAX, FN_NOPARSE}, {"CASEALL", fun_switch, 3, INT_MAX, FN_NOPARSE}, {"CAT", fun_cat, 1, INT_MAX, FN_REG}, {"CEMIT", fun_cemit, 2, 3, FN_REG}, {"CFLAGS", fun_cflags, 1, 2, FN_REG}, {"CHANNELS", fun_channels, 0, 2, FN_REG}, {"CLOCK", fun_clock, 1, 2, FN_REG}, {"COWNER", fun_cowner, 1, 1, FN_REG}, {"CTITLE", fun_ctitle, 2, 2, FN_REG}, {"CWHO", fun_cwho, 1, 1, FN_REG}, {"CENTER", fun_center, 2, 3, FN_REG}, {"CHILDREN", fun_lsearch, 1, 1, FN_REG}, {"CHR", fun_chr, 1, 1, FN_REG}, {"CHECKPASS", fun_checkpass, 2, 2, FN_REG | FN_WIZARD}, {"CLONE", fun_clone, 1, 1, FN_REG}, {"CMDS", fun_cmds, 1, 1, FN_REG}, {"COMP", fun_comp, 2, 3, FN_REG}, {"CON", fun_con, 1, 1, FN_REG}, {"CONFIG", fun_config, 1, 1, FN_REG}, {"CONN", fun_conn, 1, 1, FN_REG}, {"CONTROLS", fun_controls, 2, 2, FN_REG}, {"CONVSECS", fun_convsecs, 1, 2, FN_REG}, {"CONVUTCSECS", fun_convsecs, 1, 1, FN_REG}, {"CONVTIME", fun_convtime, 1, 1, FN_REG}, {"COR", fun_cor, 2, INT_MAX, FN_NOPARSE}, {"CREATE", fun_create, 1, 2, FN_REG}, {"CTIME", fun_ctime, 1, 1, FN_REG}, {"DEC", fun_dec, 1, 1, FN_REG}, {"DECRYPT", fun_decrypt, 2, 2, FN_REG}, {"DEFAULT", fun_default, 2, 2, FN_NOPARSE}, {"DELETE", fun_delete, 3, 3, FN_REG}, {"DIE", fun_die, 2, 3, FN_REG}, {"DIG", fun_dig, 1, 3, FN_REG}, {"DIGEST", fun_digest, 2, -2, FN_REG}, {"DIST2D", fun_dist2d, 4, 4, FN_REG}, {"DIST3D", fun_dist3d, 6, 6, FN_REG}, {"DIV", fun_div, 2, 2, FN_REG}, {"DOING", fun_doing, 1, 1, FN_REG}, {"EDEFAULT", fun_edefault, 2, 2, FN_NOPARSE}, {"EDIT", fun_edit, 3, INT_MAX, FN_REG}, {"ELEMENT", fun_element, 3, 3, FN_REG}, {"ELEMENTS", fun_elements, 2, 4, FN_REG}, {"ELIST", fun_itemize, 1, 5, FN_REG}, {"ELOCK", fun_elock, 2, 2, FN_REG}, {"EMIT", fun_emit, 1, -1, FN_REG}, {"ENCRYPT", fun_encrypt, 2, 2, FN_REG}, {"ENTRANCES", fun_entrances, 0, 4, FN_REG}, {"ETIMEFMT", fun_etimefmt, 2, 2, FN_REG}, {"EQ", fun_eq, 2, 2, FN_REG}, {"EVAL", fun_eval, 2, 2, FN_REG}, {"ESCAPE", fun_escape, 1, -1, FN_REG}, {"EXIT", fun_exit, 1, 1, FN_REG}, {"EXTRACT", fun_extract, 3, 4, FN_REG}, {"FILTER", fun_filter, 2, 4, FN_REG}, {"FILTERBOOL", fun_filter, 2, 4, FN_REG}, {"FINDABLE", fun_findable, 2, 2, FN_REG}, {"FIRST", fun_first, 1, 2, FN_REG}, {"FIRSTOF", fun_firstof, 0, INT_MAX, FN_NOPARSE}, {"FLAGS", fun_flags, 0, 1, FN_REG}, {"FLIP", fun_flip, 1, 1, FN_REG}, {"FLOORDIV", fun_floordiv, 2, 2, FN_REG}, {"FOLD", fun_fold, 2, 4, FN_REG}, {"FOLDERSTATS", fun_folderstats, 0, 2, FN_REG}, {"FOLLOWERS", fun_followers, 1, 1, FN_REG}, {"FOLLOWING", fun_following, 1, 1, FN_REG}, {"FOREACH", fun_foreach, 2, 4, FN_REG}, {"FRACTION", fun_fraction, 1, 1, FN_REG}, {"FUNCTIONS", fun_functions, 0, 0, FN_REG}, {"FULLNAME", fun_fullname, 1, 1, FN_REG}, {"GET", fun_get, 1, 1, FN_REG}, {"GET_EVAL", fun_get_eval, 1, 1, FN_REG}, {"GRAB", fun_grab, 2, 3, FN_REG}, {"GRABALL", fun_graball, 2, 4, FN_REG}, {"GREP", fun_grep, 3, 3, FN_REG}, {"GREPI", fun_grep, 3, 3, FN_REG}, {"GT", fun_gt, 2, 2, FN_REG}, {"GTE", fun_gte, 2, 2, FN_REG}, {"HASATTR", fun_hasattr, 2, 2, FN_REG}, {"HASATTRP", fun_hasattr, 2, 2, FN_REG}, {"HASATTRPVAL", fun_hasattr, 2, 2, FN_REG}, {"HASATTRVAL", fun_hasattr, 2, 2, FN_REG}, {"HASFLAG", fun_hasflag, 2, 2, FN_REG}, {"HASPOWER", fun_haspower, 2, 2, FN_REG}, {"HASTYPE", fun_hastype, 2, 2, FN_REG}, {"HEIGHT", fun_height, 1, 1, FN_REG}, {"HIDDEN", fun_hidden, 1, 1, FN_REG}, {"HOME", fun_home, 1, 1, FN_REG}, {"HOST", fun_hostname, 1, 1, FN_REG}, {"HOSTNAME", fun_hostname, 1, 1, FN_REG}, {"IDLE", fun_idlesecs, 1, 1, FN_REG}, {"IDLESECS", fun_idlesecs, 1, 1, FN_REG}, {"IF", fun_if, 2, 3, FN_NOPARSE}, {"IFELSE", fun_if, 3, 3, FN_NOPARSE}, {"ILEV", fun_ilev, 0, 0, FN_REG}, {"INAME", fun_iname, 1, 1, FN_REG}, {"INC", fun_inc, 1, 1, FN_REG}, {"INDEX", fun_index, 4, 4, FN_REG}, {"INSERT", fun_insert, 3, 4, FN_REG}, {"INUM", fun_inum, 1, 1, FN_REG}, {"IPADDR", fun_ipaddr, 1, 1, FN_REG}, {"ISDAYLIGHT", fun_isdaylight, 0, 0, FN_REG}, {"ISDBREF", fun_isdbref, 1, 1, FN_REG}, {"ISINT", fun_isint, 1, 1, FN_REG}, {"ISNUM", fun_isnum, 1, 1, FN_REG}, {"ISWORD", fun_isword, 1, 1, FN_REG}, {"ITER", fun_iter, 2, 4, FN_NOPARSE}, {"ITEMS", fun_items, 2, 2, FN_REG}, {"ITEMIZE", fun_itemize, 1, 4, FN_REG}, {"ITEXT", fun_itext, 1, 1, FN_REG}, {"LAST", fun_last, 1, 2, FN_REG}, {"LATTR", fun_lattr, 1, 1, FN_REG}, {"LATTRP", fun_lattr, 1, 1, FN_REG}, {"LCON", fun_dbwalker, 1, 1, FN_REG}, {"LCSTR", fun_lcstr, 1, -1, FN_REG}, {"LDELETE", fun_ldelete, 2, 3, FN_REG}, {"LEFT", fun_left, 2, 2, FN_REG}, {"LEMIT", fun_lemit, 1, -1, FN_REG}, {"LEXITS", fun_dbwalker, 1, 1, FN_REG}, {"LFLAGS", fun_lflags, 0, 1, FN_REG}, {"LINK", fun_link, 2, 2, FN_REG}, {"LIST", fun_list, 1, 1, FN_REG}, {"LIT", fun_lit, 1, -1, FN_LITERAL}, {"LJUST", fun_ljust, 2, 3, FN_REG}, {"LLOCKFLAGS", fun_lockflags, 0, 1, FN_REG}, {"LLOCKS", fun_locks, 1, 1, FN_REG}, {"LMATH", fun_lmath, 2, 3, FN_REG}, {"LNUM", fun_lnum, 1, 3, FN_REG}, {"LOC", fun_loc, 1, 1, FN_REG}, {"LOCALIZE", fun_localize, 1, 1, FN_NOPARSE}, {"LOCATE", fun_locate, 3, 3, FN_REG}, {"LOCK", fun_lock, 1, 2, FN_REG}, {"LOCKFLAGS", fun_lockflags, 0, 1, FN_REG}, {"LOCKS", fun_locks, 1, 1, FN_REG}, {"LPARENT", fun_lparent, 1, 1, FN_REG}, {"LPLAYERS", fun_dbwalker, 1, 1, FN_REG}, {"LPORTS", fun_lports, 0, 0, FN_REG}, {"LPOS", fun_lpos, 2, 2, FN_REG}, {"LSEARCH", fun_lsearch, 1, 5, FN_REG}, {"LSEARCHR", fun_lsearch, 1, 5, FN_REG}, {"LSET", fun_lset, 2, 2, FN_REG}, {"LSTATS", fun_lstats, 0, 1, FN_REG}, {"LT", fun_lt, 2, 2, FN_REG}, {"LTE", fun_lte, 2, 2, FN_REG}, {"LTHINGS", fun_dbwalker, 1, 1, FN_REG}, {"LVCON", fun_dbwalker, 1, 1, FN_REG}, {"LVEXITS", fun_dbwalker, 1, 1, FN_REG}, {"LVPLAYERS", fun_dbwalker, 1, 1, FN_REG}, {"LVTHINGS", fun_dbwalker, 1, 1, FN_REG}, {"LWHO", fun_lwho, 0, 1, FN_REG}, {"MAIL", fun_mail, 0, 2, FN_REG}, {"MAILFROM", fun_mailfrom, 1, 2, FN_REG}, {"MAILSEND", fun_mailsend, 2, 2, FN_REG}, {"MAILSTATS", fun_mailstats, 1, 1, FN_REG}, {"MAILDSTATS", fun_mailstats, 1, 1, FN_REG}, {"MAILFSTATS", fun_mailstats, 1, 1, FN_REG}, {"MAILSTATUS", fun_mailstatus, 1, 2, FN_REG}, {"MAILSUBJECT", fun_mailsubject, 1, 2, FN_REG}, {"MAILTIME", fun_mailtime, 1, 2, FN_REG}, {"MALIAS", fun_malias, 0, 2, FN_REG}, {"MAP", fun_map, 2, 4, FN_REG}, {"MATCH", fun_match, 2, 3, FN_REG}, {"MATCHALL", fun_matchall, 2, 4, FN_REG}, {"MAX", fun_max, 1, INT_MAX, FN_REG}, {"MEAN", fun_mean, 1, INT_MAX, FN_REG}, {"MEDIAN", fun_median, 1, INT_MAX, FN_REG}, {"MEMBER", fun_member, 2, 3, FN_REG}, {"MERGE", fun_merge, 3, 3, FN_REG}, {"MID", fun_mid, 3, 3, FN_REG}, {"MIN", fun_min, 1, INT_MAX, FN_REG}, {"MIX", fun_mix, 3, 12, FN_REG}, {"MODULO", fun_modulo, 2, 2, FN_REG}, {"MONEY", fun_money, 1, 1, FN_REG}, {"MTIME", fun_mtime, 1, 1, FN_REG}, {"MUDNAME", fun_mudname, 0, 0, FN_REG}, {"MUL", fun_mul, 2, INT_MAX, FN_REG}, {"MUNGE", fun_munge, 3, 5, FN_REG}, {"MWHO", fun_lwho, 0, 0, FN_REG}, {"NAME", fun_name, 0, 2, FN_REG}, {"NAND", fun_nand, 1, INT_MAX, FN_REG}, {"NATTR", fun_nattr, 1, 1, FN_REG}, {"NATTRP", fun_nattr, 1, 1, FN_REG}, {"NCON", fun_dbwalker, 1, 1, FN_REG}, {"NEXITS", fun_dbwalker, 1, 1, FN_REG}, {"NPLAYERS", fun_dbwalker, 1, 1, FN_REG}, {"NEARBY", fun_nearby, 2, 2, FN_REG}, {"NEQ", fun_neq, 2, 2, FN_REG}, {"NEXT", fun_next, 1, 1, FN_REG}, {"NMWHO", fun_nwho, 0, 0, FN_REG}, {"NOR", fun_nor, 1, INT_MAX, FN_REG}, {"NOT", fun_not, 1, 1, FN_REG}, {"NSCEMIT", fun_cemit, 2, 3, FN_REG}, {"NSEMIT", fun_emit, 1, -1, FN_REG}, {"NSLEMIT", fun_lemit, 1, -1, FN_REG}, {"NSOEMIT", fun_oemit, 2, -2, FN_REG}, {"NSPEMIT", fun_pemit, 2, -2, FN_REG}, {"NSREMIT", fun_remit, 2, -2, FN_REG}, {"NSZEMIT", fun_zemit, 2, -2, FN_REG}, {"NTHINGS", fun_dbwalker, 1, 1, FN_REG}, {"NUM", fun_num, 1, 1, FN_REG}, {"NULL", fun_null, 1, INT_MAX, FN_REG}, {"NVCON", fun_dbwalker, 1, 1, FN_REG}, {"NVEXITS", fun_dbwalker, 1, 1, FN_REG}, {"NVPLAYERS", fun_dbwalker, 1, 1, FN_REG}, {"NVTHINGS", fun_dbwalker, 1, 1, FN_REG}, {"NWHO", fun_nwho, 0, 0, FN_REG}, {"OBJ", fun_obj, 1, 1, FN_REG}, {"OBJEVAL", fun_objeval, 2, -2, FN_NOPARSE}, {"OBJID", fun_objid, 1, 1, FN_REG}, {"OBJMEM", fun_objmem, 1, 1, FN_REG}, {"OEMIT", fun_oemit, 2, -2, FN_REG}, {"OPEN", fun_open, 2, 2, FN_REG}, {"OR", fun_or, 2, INT_MAX, FN_REG}, {"ORD", fun_ord, 1, 1, FN_REG}, {"ORFLAGS", fun_orflags, 2, 2, FN_REG}, {"ORLFLAGS", fun_orlflags, 2, 2, FN_REG}, {"ORLPOWERS", fun_orlflags, 2, 2, FN_REG}, {"ORPOWERS", fun_orflags, 2, 2, FN_REG}, {"OWNER", fun_owner, 1, 1, FN_REG}, {"PARENT", fun_parent, 1, 2, FN_REG}, {"PCREATE", fun_pcreate, 1, 2, FN_REG}, {"PEMIT", fun_pemit, 2, -2, FN_REG}, {"PLAYERMEM", fun_playermem, 1, 1, FN_REG}, {"PMATCH", fun_pmatch, 1, 1, FN_REG}, {"POLL", fun_poll, 0, 0, FN_REG}, {"PORTS", fun_ports, 1, 1, FN_REG}, {"POS", fun_pos, 2, 2, FN_REG}, {"POSS", fun_poss, 1, 1, FN_REG}, {"POWERS", fun_powers, 1, 2, FN_REG}, {"PUEBLO", fun_pueblo, 1, 1, FN_REG}, {"QUOTA", fun_quota, 1, 1, FN_REG}, {"R", fun_r, 1, 1, FN_REG}, {"RAND", fun_rand, 1, 2, FN_REG}, {"RANDWORD", fun_randword, 1, 2, FN_REG}, {"RECV", fun_recv, 1, 1, FN_REG}, {"REGEDIT", fun_regreplace, 3, INT_MAX, FN_NOPARSE}, {"REGEDITALL", fun_regreplace, 3, INT_MAX, FN_NOPARSE}, {"REGEDITALLI", fun_regreplace, 3, INT_MAX, FN_NOPARSE}, {"REGEDITI", fun_regreplace, 3, INT_MAX, FN_NOPARSE}, {"REGMATCH", fun_regmatch, 2, 3, FN_REG}, {"REGMATCHI", fun_regmatch, 2, 3, FN_REG}, {"REGRAB", fun_regrab, 2, 4, FN_REG}, {"REGRABALL", fun_regrab, 2, 4, FN_REG}, {"REGRABALLI", fun_regrab, 2, 3, FN_REG}, {"REGRABI", fun_regrab, 2, 3, FN_REG}, {"REGREP", fun_regrep, 3, 3, FN_REG}, {"REGREPI", fun_regrep, 3, 3, FN_REG}, {"RESWITCH", fun_reswitch, 3, INT_MAX, FN_NOPARSE}, {"RESWITCHALL", fun_reswitch, 3, INT_MAX, FN_NOPARSE}, {"RESWITCHALLI", fun_reswitch, 3, INT_MAX, FN_NOPARSE}, {"RESWITCHI", fun_reswitch, 3, INT_MAX, FN_NOPARSE}, {"REMAINDER", fun_remainder, 2, 2, FN_REG}, {"REMIT", fun_remit, 2, -2, FN_REG}, {"REMOVE", fun_remove, 2, 3, FN_REG}, {"REPEAT", fun_repeat, 2, 2, FN_REG}, {"REPLACE", fun_replace, 3, 4, FN_REG}, {"REST", fun_rest, 1, 2, FN_REG}, {"RESTARTS", fun_restarts, 0, 0, FN_REG}, {"RESTARTTIME", fun_restarttime, 0, 0, FN_REG}, {"REVERSE", fun_flip, 1, 1, FN_REG}, {"REVWORDS", fun_revwords, 1, 3, FN_REG}, {"RIGHT", fun_right, 2, 2, FN_REG}, {"RJUST", fun_rjust, 2, 3, FN_REG}, {"RLOC", fun_rloc, 2, 2, FN_REG}, {"RNUM", fun_rnum, 2, 2, FN_REG}, {"ROOM", fun_room, 1, 1, FN_REG}, {"ROOT", fun_root, 2, 2, FN_REG}, {"S", fun_s, 1, -1, FN_REG}, {"SCAN", fun_scan, 1, -2, FN_REG}, {"SCRAMBLE", fun_scramble, 1, -1, FN_REG}, {"SECS", fun_secs, 0, 0, FN_REG}, {"SECURE", fun_secure, 1, -1, FN_REG}, {"SENT", fun_sent, 1, 1, FN_REG}, {"SET", fun_set, 2, 2, FN_REG}, {"SETQ", fun_setq, 2, 2, FN_REG}, {"SETR", fun_setq, 2, 2, FN_REG}, {"SETDIFF", fun_setdiff, 2, 5, FN_REG}, {"SETINTER", fun_setinter, 2, 5, FN_REG}, {"SETUNION", fun_setunion, 2, 5, FN_REG}, {"SHA0", fun_sha0, 1, 1, FN_REG}, {"SHL", fun_shl, 2, 2, FN_REG}, {"SHR", fun_shr, 2, 2, FN_REG}, {"SHUFFLE", fun_shuffle, 1, 3, FN_REG}, {"SIGN", fun_sign, 1, 1, FN_REG}, {"SORT", fun_sort, 1, 4, FN_REG}, {"SORTBY", fun_sortby, 2, 4, FN_REG}, {"SOUNDEX", fun_soundex, 1, 1, FN_REG}, {"SOUNDSLIKE", fun_soundlike, 2, 2, FN_REG}, {"SPACE", fun_space, 1, 1, FN_REG}, {"SPELLNUM", fun_spellnum, 1, 1, FN_REG}, {"SPLICE", fun_splice, 3, 4, FN_REG}, {"SQL", fun_sql, 1, 3, FN_REG}, {"SQLESCAPE", fun_sql_escape, 1, 1, FN_REG}, {"SQUISH", fun_squish, 1, 2, FN_REG}, {"SSL", fun_ssl, 1, 1, FN_REG}, {"STARTTIME", fun_starttime, 0, 0, FN_REG}, {"STEP", fun_step, 3, 5, FN_REG}, {"STRCAT", fun_strcat, 1, INT_MAX, FN_REG}, {"STRINSERT", fun_strinsert, 3, -3, FN_REG}, {"STRIPACCENTS", fun_stripaccents, 1, 1, FN_REG}, {"STRIPANSI", fun_stripansi, 1, -1, FN_REG}, {"STRLEN", fun_strlen, 1, -1, FN_REG}, {"STRMATCH", fun_strmatch, 2, 2, FN_REG}, {"STRREPLACE", fun_strreplace, 4, 4, FN_REG}, {"SUB", fun_sub, 2, 2, FN_REG}, {"SUBJ", fun_subj, 1, 1, FN_REG}, {"SWITCH", fun_switch, 3, INT_MAX, FN_NOPARSE}, {"SWITCHALL", fun_switch, 3, INT_MAX, FN_NOPARSE}, {"T", fun_t, 1, 1, FN_REG}, {"TABLE", fun_table, 1, 5, FN_REG}, {"TEL", fun_tel, 2, 4, FN_REG}, {"TERMINFO", fun_terminfo, 1, 1, FN_REG}, {"TEXTFILE", fun_textfile, 2, 2, FN_REG}, {"TIME", fun_time, 0, 1, FN_REG}, {"TIMEFMT", fun_timefmt, 1, 2, FN_REG}, {"TIMESTRING", fun_timestring, 1, 2, FN_REG}, {"TR", fun_tr, 3, 3, FN_REG}, {"TRIM", fun_trim, 1, 3, FN_REG}, {"TRIMPENN", fun_trim, 1, 3, FN_REG}, {"TRIMTINY", fun_trim, 1, 3, FN_REG}, {"TRUNC", fun_trunc, 1, 1, FN_REG}, {"TYPE", fun_type, 1, 1, FN_REG}, {"UCSTR", fun_ucstr, 1, -1, FN_REG}, {"UDEFAULT", fun_uldefault, 2, 12, FN_NOPARSE}, {"UFUN", fun_ufun, 1, 11, FN_REG}, {"ULDEFAULT", fun_uldefault, 1, 12, FN_NOPARSE}, {"ULOCAL", fun_ulocal, 1, 11, FN_REG}, {"UTCTIME", fun_time, 0, 0, FN_REG}, {"U", fun_ufun, 1, 11, FN_REG}, {"V", fun_v, 1, 1, FN_REG}, {"VALID", fun_valid, 2, 2, FN_REG}, {"VERSION", fun_version, 0, 0, FN_REG}, {"VISIBLE", fun_visible, 2, 2, FN_REG}, {"WHERE", fun_where, 1, 1, FN_REG}, {"WIDTH", fun_width, 1, 1, FN_REG}, {"WIPE", fun_wipe, 1, 1, FN_REG}, {"WORDPOS", fun_wordpos, 2, 3, FN_REG}, {"WORDS", fun_words, 1, 2, FN_REG}, {"WRAP", fun_wrap, 2, 4, FN_REG}, {"XATTR", fun_lattr, 3, 3, FN_REG}, {"XATTRP", fun_lattr, 3, 3, FN_REG}, {"XCON", fun_dbwalker, 3, 3, FN_REG}, {"XEXITS", fun_dbwalker, 3, 3, FN_REG}, {"XMWHO", fun_xwho, 2, 2, FN_REG}, {"XPLAYERS", fun_dbwalker, 3, 3, FN_REG}, {"XGET", fun_xget, 2, 2, FN_REG}, {"XOR", fun_xor, 2, INT_MAX, FN_REG}, {"XTHINGS", fun_dbwalker, 3, 3, FN_REG}, {"XVCON", fun_dbwalker, 3, 3, FN_REG}, {"XVEXITS", fun_dbwalker, 3, 3, FN_REG}, {"XVPLAYERS", fun_dbwalker, 3, 3, FN_REG}, {"XVTHINGS", fun_dbwalker, 3, 3, FN_REG}, {"XWHO", fun_xwho, 2, 2, FN_REG}, {"ZEMIT", fun_zemit, 2, -2, FN_REG}, {"ZFUN", fun_zfun, 1, 11, FN_REG}, {"ZONE", fun_zone, 1, 2, FN_REG}, {"ZMWHO", fun_zwho, 1, 1, FN_REG}, {"ZWHO", fun_zwho, 1, 2, FN_REG}, {"VADD", fun_vadd, 2, 3, FN_REG}, {"VCROSS", fun_vcross, 2, 3, FN_REG}, {"VSUB", fun_vsub, 2, 3, FN_REG}, {"VMAX", fun_vmax, 2, 3, FN_REG}, {"VMIN", fun_vmin, 2, 3, FN_REG}, {"VMUL", fun_vmul, 2, 3, FN_REG}, {"VDOT", fun_vdot, 2, 3, FN_REG}, {"VMAG", fun_vmag, 1, 2, FN_REG}, {"VDIM", fun_words, 1, 2, FN_REG}, {"VUNIT", fun_vunit, 1, 2, FN_REG}, {"ACOS", fun_acos, 1, 2, FN_REG}, {"ASIN", fun_asin, 1, 2, FN_REG}, {"ATAN", fun_atan, 1, 2, FN_REG}, {"ATAN2", fun_atan2, 2, 3, FN_REG}, {"CEIL", fun_ceil, 1, 1, FN_REG}, {"COS", fun_cos, 1, 2, FN_REG}, {"CTU", fun_ctu, 3, 3, FN_REG}, {"E", fun_e, 0, 0, FN_REG}, {"EXP", fun_exp, 1, 1, FN_REG}, {"FDIV", fun_fdiv, 2, 2, FN_REG}, {"FMOD", fun_fmod, 2, 2, FN_REG}, {"FLOOR", fun_floor, 1, 1, FN_REG}, {"LOG", fun_log, 1, 2, FN_REG}, {"LN", fun_ln, 1, 1, FN_REG}, {"PI", fun_pi, 0, 0, FN_REG}, {"POWER", fun_power, 2, 2, FN_REG}, {"ROUND", fun_round, 2, 2, FN_REG}, {"SIN", fun_sin, 1, 2, FN_REG}, {"SQRT", fun_sqrt, 1, 1, FN_REG}, {"STDDEV", fun_stddev, 1, INT_MAX, FN_REG}, {"TAN", fun_tan, 1, 2, FN_REG}, {"HTML", fun_html, 1, 1, FN_REG}, {"TAG", fun_tag, 1, INT_MAX, FN_REG}, {"ENDTAG", fun_endtag, 1, 1, FN_REG}, {"TAGWRAP", fun_tagwrap, 2, 3, FN_REG}, {NULL, NULL, 0, 0, 0} }; /** List all functions. * \verbatim * This is the mail interface to @list functions. * \endverbatim * \param player the enactor. * \param lc if 1, return functions in lowercase. */ void do_list_functions(dbref player, int lc) { /* lists all built-in functions. */ char *b = list_functions(); notify_format(player, "Functions: %s", lc ? strlower(b) : b); } /** Return a list of function names. * This function returns the list of function names as a string. * \return list of function names as a static string. */ char * list_functions(void) { FUN *fp; const char *ptrs[BUFFER_LEN / 2]; static char buff[BUFFER_LEN]; char *bp; int nptrs = 0, i; for (fp = (FUN *) hash_firstentry(&htab_function); fp; fp = (FUN *) hash_nextentry(&htab_function)) { if (fp->flags & FN_OVERRIDE) continue; ptrs[nptrs++] = fp->name; } fp = (FUN *) hash_firstentry(&htab_user_function); while (fp) { ptrs[nptrs++] = fp->name; fp = (FUN *) hash_nextentry(&htab_user_function); } /* do_gensort needs a dbref now, but only for sort types that aren't * used here anyway */ do_gensort((dbref) 0, (char **) ptrs, nptrs, ALPHANUM_LIST); bp = buff; safe_str(ptrs[0], buff, &bp); for (i = 1; i < nptrs; i++) { safe_chr(' ', buff, &bp); safe_str(ptrs[i], buff, &bp); } *bp = '\0'; return buff; } /*--------------------------------------------------------------------------- * Hashed function table stuff */ /** Look up a function by name. * \param name name of function to look up. * \return pointer to function data, or NULL. */ FUN * func_hash_lookup(const char *name) { FUN *f; f = (FUN *) hashfind(strupper(name), &htab_function); if (!f || f->flags & FN_OVERRIDE) f = (FUN *) hashfind(strupper(name), &htab_user_function); return f; } static void func_hash_insert(const char *name, FUN *func) { hashadd(name, (void *) func, &htab_function); } /** Initialize the function hash table. */ void init_func_hashtab(void) { FUNTAB *ftp; hashinit(&htab_function, 512, sizeof(FUN)); hashinit(&htab_user_function, 32, sizeof(FUN)); for (ftp = flist; ftp->name; ftp++) { function_add(ftp->name, ftp->fun, ftp->minargs, ftp->maxargs, ftp->flags); } local_functions(); } /** Function initization to perform after reading the config file. * This function performs post-config initialization. Specifically, * we need the max_globals value from the config file before we * can allocate the global user function table here. */ void function_init_postconfig(void) { userfn_tab = (USERFN_ENTRY *) mush_malloc(MAX_GLOBAL_FNS * sizeof(USERFN_ENTRY), "userfn_tab"); } /** Check permissions to run a function. * \param player the executor. * \param fp pointer to function data. * \retval 1 executor may use the function. * \retval 0 permission denied. */ int check_func(dbref player, FUN *fp) { if (!fp) return 0; if ((fp->flags & (~FN_ARG_MASK)) == 0) return 1; if (fp->flags & FN_DISABLED) return 0; if ((fp->flags & FN_GOD) && !God(player)) return 0; if ((fp->flags & FN_WIZARD) && !Wizard(player)) return 0; if ((fp->flags & FN_ADMIN) && !Hasprivs(player)) return 0; if ((fp->flags & FN_NOGAGGED) && Gagged(player)) return 0; if ((fp->flags & FN_NOFIXED) && Fixed(player)) return 0; if ((fp->flags & FN_NOGUEST) && Guest(player)) return 0; return 1; } /** Add an alias to a function. * This function adds an alias to a function in the hash table. * \param function name of function to alias. * \param alias name of the alias to add. * \retval 0 failure (alias exists, or function doesn't, or is a user fun). * \retval 1 success. */ int alias_function(const char *function, const char *alias) { FUN *fp; /* Make sure the alias doesn't exist already */ if (func_hash_lookup(alias)) return 0; /* Look up the original */ fp = func_hash_lookup(function); if (!fp) return 0; /* We can't alias @functions. Just use another @function for these */ if (!(fp->flags & FN_BUILTIN)) return 0; function_add(strdup(strupper(alias)), fp->where.fun, fp->minargs, fp->maxargs, fp->flags); return 1; } /** Add a function. * \param name name of the function to add. * \param fun pointer to compiled function code. * \param minargs minimum arguments to function. * \param maxargs maximum arguments to function. * \param ftype function evaluation flags. */ void function_add(const char *name, function_func fun, int minargs, int maxargs, int ftype) { FUN *fp; fp = (FUN *) mush_malloc(sizeof(FUN), "function"); memset(fp, 0, sizeof(FUN)); fp->name = name; fp->where.fun = fun; fp->minargs = minargs; fp->maxargs = maxargs; fp->flags = FN_BUILTIN | ftype; func_hash_insert(name, fp); } /*------------------------------------------------------------------------- * Function handlers and the other good stuff. Almost all this code is * a modification of TinyMUSH 2.0 code. */ /** Strip a level of braces. * this is a hack which just strips a level of braces. It malloc()s memory * which must be free()d later. * \param str string to strip braces from. * \return newly allocated string with the first level of braces stripped. */ char * strip_braces(const char *str) { char *buff; char *bufc; buff = (char *) mush_malloc(BUFFER_LEN, "strip_braces.buff"); bufc = buff; while (isspace((unsigned char) *str)) /* eat spaces at the beginning */ str++; switch (*str) { case '{': str++; process_expression(buff, &bufc, &str, 0, 0, 0, PE_NOTHING, PT_BRACE, NULL); *bufc = '\0'; return buff; break; /* NOT REACHED */ default: strcpy(buff, str); return buff; } } /*------------------------------------------------------------------------ * User-defined global function handlers */ static Size_t userfn_count = 0; static int apply_restrictions(unsigned int result, const char *restriction) { int flag, clear = 0; char *tp; while (restriction && *restriction) { if ((tp = strchr(restriction, ' '))) *tp++ = '\0'; if (*restriction == '!') { restriction++; clear = 1; } flag = 0; if (!strcasecmp(restriction, "nobody")) { flag = FN_DISABLED; } else if (string_prefix(restriction, "nogag")) { flag = FN_NOGAGGED; } else if (string_prefix(restriction, "nofix")) { flag = FN_NOFIXED; } else if (!strcasecmp(restriction, "noguest")) { flag = FN_NOGUEST; } else if (!strcasecmp(restriction, "admin")) { flag = FN_ADMIN; } else if (!strcasecmp(restriction, "wizard")) { flag = FN_WIZARD; } else if (!strcasecmp(restriction, "god")) { flag = FN_GOD; } else if (!strcasecmp(restriction, "nosidefx")) { flag = FN_NOSIDEFX; } else if (!strcasecmp(restriction, "logargs")) { flag = FN_LOGARGS; } else if (!strcasecmp(restriction, "logname")) { flag = FN_LOGNAME; } else if (!strcasecmp(restriction, "noparse")) { flag = FN_NOPARSE; } if (clear) result &= ~flag; else result |= flag; restriction = tp; } return result; } /** Given a function name and a restriction, apply the restriction to the * function in addition to whatever its usual restrictions are. * This is used by the configuration file startup in conf.c * \verbatim * Valid restrictions are: * nobody disable the command * nogagged can't be used by gagged players * nofixed can't be used by fixed players * noguest can't be used by guests * admin can only be used by royalty or wizards * wizard can only be used by wizards * god can only be used by god * noplayer can't be used by players, just objects/rooms/exits * nosidefx can't be used to do side-effect thingies * \endverbatim * \param name name of function to restrict. * \param restriction name of restriction to apply to function. * \retval 1 success. * \retval 0 failure (invalid function or restriction name). */ int restrict_function(const char *name, const char *restriction) { FUN *fp; if (!name || !*name) return 0; fp = func_hash_lookup(name); if (!fp) return 0; fp->flags = apply_restrictions(fp->flags, restriction); return 1; } /** Softcode interface to restrict a function. * \verbatim * This is the implementation of @function/restrict. * \endverbatim * \param player the enactor. * \param name name of function to restrict. * \param restriction name of restriction to add. */ void do_function_restrict(dbref player, const char *name, const char *restriction) { if (!Wizard(player)) { notify(player, T("Permission denied.")); return; } if (!name) { notify(player, T("Restrict what function?")); return; } if (!restriction) { notify(player, T("Do what with the function?")); return; } if (restrict_function(name, restriction)) notify(player, T("Restrictions modified.")); else notify(player, T("Restrict attempt failed.")); } /** Add a user-defined function. * \verbatim * This is the implementation of the @function command. If no arguments * are given, it lists all @functions defined. Otherwise, this adds * an @function. * \endverbatim * \param player the enactor. * \param name name of function to add. * \param argv array of arguments. */ void do_function(dbref player, char *name, char *argv[]) { char tbuf1[BUFFER_LEN]; char *bp = tbuf1; dbref thing; FUN *fp; /* if no arguments, just give the list of user functions, by walking * the function hash table, and looking up all functions marked * as user-defined. */ if (!name || !*name) { if (userfn_count == 0) { notify(player, T("No global user-defined functions exist.")); return; } if (Global_Funcs(player)) { /* if the player is privileged, display user-def'ed functions * with corresponding dbref number of thing and attribute name. */ notify(player, T("Function Name Dbref # Attrib")); for (fp = (FUN *) hash_firstentry(&htab_user_function); fp; fp = (FUN *) hash_nextentry(&htab_user_function)) { notify_format(player, "%-32s %6d %s", fp->name, userfn_tab[fp->where.offset].thing, userfn_tab[fp->where.offset].name); } } else { /* just print out the list of available functions */ safe_str(T("User functions:"), tbuf1, &bp); for (fp = (FUN *) hash_firstentry(&htab_user_function); fp; fp = (FUN *) hash_nextentry(&htab_user_function)) { safe_chr(' ', tbuf1, &bp); safe_str(fp->name, tbuf1, &bp); } *bp = '\0'; notify(player, tbuf1); } return; } /* otherwise, we are adding a user function. * Only those with the Global_Funcs power may add stuff. * If you add a function that is already a user-defined function, * the old function gets over-written. */ if (!Global_Funcs(player)) { notify(player, T("Permission denied.")); return; } if (!argv[1] || !*argv[1] || !argv[2] || !*argv[2]) { notify(player, T("You must specify an object and an attribute.")); return; } /* make sure the function name length is okay */ if (strlen(name) >= SBUF_LEN) { notify(player, T("Function name too long.")); return; } /* find the object. For some measure of security, the player must * be able to examine it. */ if ((thing = noisy_match_result(player, argv[1], NOTYPE, MAT_EVERYTHING)) == NOTHING) return; if (!Can_Examine(player, thing)) { notify(player, T("No permission to examine object.")); return; } /* we don't need to check if the attribute exists. If it doesn't, * it's not our problem - it's the user's responsibility to make * sure that the attribute exists (if it doesn't, invoking the * function will return a #-1 NO SUCH ATTRIBUTE error). * We do, however, need to make sure that the user isn't trying * to replace a built-in function. */ fp = func_hash_lookup(upcasestr(name)); if (!fp) { if (userfn_count >= (Size_t) MAX_GLOBAL_FNS) { notify(player, T("Function table full.")); return; } if (argv[6] && *argv[6]) { notify(player, T("Expected between 1 and 5 arguments.")); return; } /* a completely new entry. First, insert it into general hash table */ fp = (FUN *) mush_malloc(sizeof(FUN), "func_hash.FUN"); fp->name = mush_strdup(name, "func_hash.name"); fp->where.offset = userfn_count; if (argv[3] && *argv[3]) { fp->minargs = parse_integer(argv[3]); if (fp->minargs < 0) fp->minargs = 0; else if (fp->minargs > 10) fp->minargs = 10; } else fp->minargs = 0; if (argv[4] && *argv[4]) { fp->maxargs = parse_integer(argv[4]); if (fp->maxargs < -10) fp->maxargs = -10; else if (fp->maxargs > 10) fp->maxargs = 10; } else fp->maxargs = 10; if (argv[5] && *argv[5]) fp->flags = apply_restrictions(0, argv[5]); else fp->flags = 0; hashadd(name, fp, &htab_user_function); /* now add it to the user function table */ userfn_tab[userfn_count].thing = thing; userfn_tab[userfn_count].name = mush_strdup(upcasestr(argv[2]), "userfn_tab.name"); userfn_tab[userfn_count].fn = mush_strdup(name, "usrfn_tab.fn"); userfn_count++; notify(player, T("Function added.")); return; } else { /* we are modifying an old entry */ if ((fp->flags & FN_BUILTIN) && !(fp->flags & FN_OVERRIDE)) { notify(player, T("You cannot change that built-in function.")); return; } if (fp->flags & FN_BUILTIN) { /* Overriding a built in function */ if (userfn_count >= (Size_t) MAX_GLOBAL_FNS) { notify(player, T("Function table full.")); return; } fp = (FUN *) mush_malloc(sizeof(FUN), "func_hash.FUN"); fp->name = mush_strdup(name, "func_hash.name"); fp->where.offset = userfn_count; fp->flags = 0; userfn_count++; hashadd(name, fp, &htab_user_function); } userfn_tab[fp->where.offset].thing = thing; if (userfn_tab[fp->where.offset].name) mush_free((Malloc_t) userfn_tab[fp->where.offset].name, "userfn_tab.name"); userfn_tab[fp->where.offset].name = mush_strdup(upcasestr(argv[2]), "userfn_tab.name"); if (argv[3] && *argv[3]) { fp->minargs = parse_integer(argv[3]); if (fp->minargs < 0) fp->minargs = 0; else if (fp->minargs > 10) fp->minargs = 10; } else fp->minargs = 0; if (argv[4] && *argv[4]) { fp->maxargs = parse_integer(argv[4]); if (fp->maxargs < -10) fp->maxargs = -10; else if (fp->maxargs > 10) fp->maxargs = 10; } else fp->maxargs = 10; notify(player, T("Function updated.")); } } /** Restore an overridden built-in function. * \verbatim * If a built-in function is deleted with @function/delete, it can be * restored with @function/restore. This implements @function/restore. * If a user-defined function has been added, it will be removed by * this function. * \endverbatim * \param player the enactor. * \param name name of function to restore. */ void do_function_restore(dbref player, const char *name) { FUN *fp; Size_t table_index, i; if (!Wizard(player)) { notify(player, T("Permission denied.")); return; } if (!name || !*name) { notify(player, T("Restore what?")); return; } fp = (FUN *) hashfind(strupper(name), &htab_function); if (!fp) { notify(player, T("That's not a builtin function.")); return; } if (!(fp->flags & FN_OVERRIDE)) { notify(player, T("That function isn't deleted!")); return; } fp->flags &= ~FN_OVERRIDE; notify(player, T("Restored.")); /* Delete any @function with the same name */ fp = (FUN *) hashfind(strupper(name), &htab_user_function); if (!fp) return; /* Remove it from the hash table */ hashdelete(fp->name, &htab_user_function); /* Free its memory */ table_index = fp->where.offset; mush_free((void *) fp->name, "func_hash.name"); mush_free(fp, "func_hash.FUN"); /* Fix up the user function table. Expensive, but how often will * we need to delete an @function anyway? */ mush_free((Malloc_t) userfn_tab[table_index].name, "userfn_tab.name"); mush_free((Malloc_t) userfn_tab[table_index].fn, "userfn_tab.fn"); userfn_count--; for (i = table_index; i < userfn_count; i++) { fp = (FUN *) hashfind(userfn_tab[i + 1].fn, &htab_user_function); fp->where.offset = i; userfn_tab[i].thing = userfn_tab[i + 1].thing; userfn_tab[i].name = mush_strdup(userfn_tab[i + 1].name, "userfn_tab.name"); mush_free((Malloc_t) userfn_tab[i + 1].name, "userfn_tab.name"); userfn_tab[i].fn = mush_strdup(userfn_tab[i + 1].fn, "userfn_tab.fn"); mush_free((Malloc_t) userfn_tab[i + 1].fn, "userfn_tab.fn"); } } /** Delete a function. * \verbatim * This code implements @function/delete, which deletes a function - * either a built-in or a user-defined one. * \endverbatim * \param player the enactor. * \param name name of the function to delete. */ void do_function_delete(dbref player, char *name) { /* Deletes a user-defined function. * For security, you must control the object the function uses * to delete the function. */ Size_t table_index, i; FUN *fp; if (!Global_Funcs(player)) { notify(player, T("Permission denied.")); return; } fp = func_hash_lookup(name); if (!fp) { notify(player, T("No such function.")); return; } if (fp->flags & FN_BUILTIN) { if (!Wizard(player)) { notify(player, T("You can't delete that @function.")); return; } fp->flags |= FN_OVERRIDE; notify(player, T("Function deleted.")); return; } table_index = fp->where.offset; if (!controls(player, userfn_tab[table_index].thing)) { notify(player, T("You can't delete that @function.")); return; } /* Remove it from the hash table */ hashdelete(fp->name, &htab_user_function); /* Free its memory */ mush_free((void *) fp->name, "func_hash.name"); mush_free(fp, "func_hash.FUN"); /* Fix up the user function table. Expensive, but how often will * we need to delete an @function anyway? */ mush_free((Malloc_t) userfn_tab[table_index].name, "userfn_tab.name"); mush_free((Malloc_t) userfn_tab[table_index].fn, "userfn_tab.fn"); userfn_count--; for (i = table_index; i < userfn_count; i++) { fp = (FUN *) hashfind(userfn_tab[i + 1].fn, &htab_user_function); fp->where.offset = i; userfn_tab[i].thing = userfn_tab[i + 1].thing; userfn_tab[i].name = mush_strdup(userfn_tab[i + 1].name, "userfn_tab.name"); mush_free((Malloc_t) userfn_tab[i + 1].name, "userfn_tab.name"); userfn_tab[i].fn = mush_strdup(userfn_tab[i + 1].fn, "userfn_tab.fn"); mush_free((Malloc_t) userfn_tab[i + 1].fn, "userfn_tab.fn"); } notify(player, T("Function deleted.")); } /** Enable or disable a function. * \verbatim * This implements @function/disable and @function/enable. * \endverbatim * \param player the enactor. * \param name name of the function to enable or disable. * \param toggle if 1, enable; if 0, disable. */ void do_function_toggle(dbref player, char *name, int toggle) { FUN *fp; if (!Wizard(player)) { notify(player, T("Permission denied.")); return; } fp = func_hash_lookup(name); if (!fp) { notify(player, T("No such function.")); return; } if (toggle) { fp->flags &= ~FN_DISABLED; notify(player, T("Enabled.")); } else { fp->flags |= FN_DISABLED; notify(player, T("Disabled.")); } } /** Get information about a function. * \verbatim * This implements the @function <function> command, which reports function * details to the enactor. * \endverbatim * \param player the enactor. * \param name name of the function. */ void do_function_report(dbref player, char *name) { FUN *fp; char tbuf[BUFFER_LEN]; char *tp; const char *state, *state2; int first = 1; int maxargs; fp = func_hash_lookup(name); if (!fp) { notify(player, T("No such function.")); return; } if (fp->flags & FN_BUILTIN) state2 = ""; else state2 = " @function"; if (fp->flags & FN_DISABLED) state = "Disabled"; else state = "Enabled"; notify_format(player, T("Name : %s() (%s%s)"), fp->name, state, state2); tp = tbuf; tbuf[0] = '\0'; if (fp->flags & FN_NOPARSE) { safe_str("Noparse", tbuf, &tp); if (first) first = 0; } if (fp->flags & FN_LITERAL) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("Literal", tbuf, &tp); first = 0; } if (fp->flags & FN_NOSIDEFX) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("Nosidefx", tbuf, &tp); first = 0; } if (fp->flags & FN_LOGARGS) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("LogArgs", tbuf, &tp); first = 0; } else if (fp->flags & FN_LOGNAME) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("LogName", tbuf, &tp); first = 0; } if (fp->flags & FN_NOGAGGED) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("Nogagged", tbuf, &tp); first = 0; } if (fp->flags & FN_NOGUEST) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("Noguest", tbuf, &tp); first = 0; } if (fp->flags & FN_NOFIXED) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("Nofixed", tbuf, &tp); first = 0; } if (fp->flags & FN_WIZARD) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("Wizard", tbuf, &tp); first = 0; } if (fp->flags & FN_ADMIN) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("Admin", tbuf, &tp); first = 0; } if (fp->flags & FN_GOD) { if (first == 0) safe_strl(", ", 2, tbuf, &tp); safe_str("God", tbuf, &tp); first = 0; } *tp = '\0'; notify_format(player, T("Flags : %s"), tbuf); if (!(fp->flags & FN_BUILTIN) && Global_Funcs(player)) { notify_format(player, T("Location : #%d/%s"), userfn_tab[fp->where.offset].thing, userfn_tab[fp->where.offset].name); } maxargs = abs(fp->maxargs); tp = tbuf; if (fp->maxargs < 0) { safe_str(T("(Commas okay in last argument)"), tbuf, &tp); *tp = '\0'; } else tbuf[0] = '\0'; if (fp->minargs == maxargs) notify_format(player, T("Arguments : %d %s"), fp->minargs, tbuf); else if (fp->maxargs == INT_MAX) notify_format(player, T("Arguments : At least %d %s"), fp->minargs, tbuf); else notify_format(player, T("Arguments : %d to %d %s"), fp->minargs, maxargs, tbuf); }