/** * \file predicat.c * * \brief Predicates for testing various conditions in PennMUSH. * * */ #include "copyrite.h" #include "config.h" #include <stdio.h> #include <stdarg.h> #include <ctype.h> #include <string.h> #ifdef I_SYS_TYPES #include <sys/types.h> #endif #ifdef I_SYS_TIME #include <sys/time.h> #else #include <time.h> #endif #include <stdlib.h> #include "conf.h" #include "externs.h" #include "mushdb.h" #include "attrib.h" #include "lock.h" #include "flags.h" #include "match.h" #include "ansi.h" #include "parse.h" #include "dbdefs.h" #include "privtab.h" #include "mymalloc.h" #include "confmagic.h" int forbidden_name(const char *name); void do_switch(dbref player, char *expression, char **argv, dbref cause, int first, int notifyme, int regexp); void do_verb(dbref player, dbref cause, char *arg1, char **argv); static int grep_util_helper(dbref player, dbref thing, dbref parent, char const *pattern, ATTR *atr, void *args); static int grep_helper(dbref player, dbref thing, dbref parent, char const *pattern, ATTR *atr, void *args); void do_grep(dbref player, char *obj, char *lookfor, int flag, int insensitive); static int pay_quota(dbref, int); extern PRIV attr_privs[]; /** A generic function to generate a formatted string. The * return value is a statically allocated buffer. * * \param fmt format string. * \return formatted string. */ char *WIN32_CDECL tprintf(const char *fmt, ...) { #ifdef HAS_VSNPRINTF static char buff[BUFFER_LEN]; #else static char buff[BUFFER_LEN * 3]; /* safety margin */ #endif va_list args; va_start(args, fmt); #ifdef HAS_VSNPRINTF vsnprintf(buff, sizeof buff, fmt, args); #else vsprintf(buff, fmt, args); #endif buff[BUFFER_LEN - 1] = '\0'; va_end(args); return (buff); } /** lock evaluation -- determines if player passes lock on thing, for * the purposes of picking up an object or moving through an exit. * \param player to check against lock. * \param thing thing to check the basic lock on. * \retval 1 player passes lock. * \retval 0 player fails lock. */ int could_doit(dbref player, dbref thing) { if (!IsRoom(thing) && Location(thing) == NOTHING) return 0; return (eval_lock(player, thing, Basic_Lock)); } /** Execute an action on an object, and handle objects with limited charges. * \param player the enactor. * \param thing object being used. * \param awhat action attribute on object to be triggered. * \retval 0 no action taken. * \retval 1 action taken. */ int charge_action(dbref player, dbref thing, const char *awhat) { ATTR *b; char tbuf2[BUFFER_LEN]; int num; if (!awhat || !*awhat) return 0; /* check if object has # of charges */ b = atr_get_noparent(thing, "CHARGES"); if (!b) { /* no charges set, just execute the action */ return queue_attribute(thing, awhat, player); } else { strcpy(tbuf2, atr_value(b)); num = atoi(tbuf2); if (num) { /* charges left, decrement and execute */ int res; if ((res = queue_attribute(thing, awhat, player))) (void) atr_add(thing, "CHARGES", tprintf("%d", num - 1), Owner(b->creator), NOTHING); return res; } else { /* no charges left, try to execute runout */ return queue_attribute(thing, "RUNOUT", player); } } } /** A wrapper for real_did_it that clears the environment first. * \param player the enactor. * \param thing object being triggered. * \param what message attribute for enactor. * \param def default message to enactor. * \param owhat message attribute for others. * \param odef default message to others. * \param awhat action attribute to trigger. * \param loc location in which action is taking place. * \retval 0 no attributes were evaluated (only defaults used). * \retval 1 some attributes were evaluated. */ int did_it(dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc) { /* Bunch o' nulls */ static char *myenv[10] = { NULL }; return real_did_it(player, thing, what, def, owhat, odef, awhat, loc, myenv, NA_INTER_HEAR); } /** A wrapper for real_did_it that can set %0 and %1 to dbrefs. * \param player the enactor. * \param thing object being triggered. * \param what message attribute for enactor. * \param def default message to enactor. * \param owhat message attribute for others. * \param odef default message to others. * \param awhat action attribute to trigger. * \param loc location in which action is taking place. * \param env0 dbref to pass as %0, or NOTHING. * \param env1 dbref to pass as %1, or NOTHING. * \param flags interaction flags to pass to real_did_it. * \retval 0 no attributes were present, only defaults were used if given. * \retval 1 some attributes were evaluated and used. */ int did_it_with(dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc, dbref env0, dbref env1, int flags) { char *myenv[10] = { NULL }; char e0[SBUF_LEN], e1[SBUF_LEN], *ep; if (env0 != NOTHING) { ep = e0; safe_dbref(env0, e0, &ep); *ep = '\0'; myenv[0] = e0; } if (env1 != NOTHING) { ep = e1; safe_dbref(env1, e1, &ep); *ep = '\0'; myenv[1] = e1; } return real_did_it(player, thing, what, def, owhat, odef, awhat, loc, myenv, flags); } /** A wrapper for did_it that can pass interaction flags. * \param player the enactor. * \param thing object being triggered. * \param what message attribute for enactor. * \param def default message to enactor. * \param owhat message attribute for others. * \param odef default message to others. * \param awhat action attribute to trigger. * \param loc location in which action is taking place. * \param flags interaction flags to pass to real_did_it. * \retval 0 no attributes were present, only defaults were used if given. * \retval 1 some attributes were evaluated and used. */ int did_it_interact(dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc, int flags) { /* Bunch o' nulls */ static char *myenv[10] = { NULL }; return real_did_it(player, thing, what, def, owhat, odef, awhat, loc, myenv, flags); } /** Take an action on an object and trigger attributes. * \verbatim * executes the @attr, @oattr, @aattr for a command - gives a message * to the enactor and others in the room with the enactor, and executes * an action. We load wenv with the values in myenv. * \endverbatim * * \param player the enactor. * \param thing object being triggered. * \param what message attribute for enactor. * \param def default message to enactor. * \param owhat message attribute for others. * \param odef default message to others. * \param awhat action attribute to trigger. * \param loc location in which action is taking place. * \param myenv copy of the environment. * \param flags flags controlling type of interaction involved. * \retval 0 no attributes were present, only defaults were used if given. * \retval 1 some attributes were evaluated and used. */ int real_did_it(dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc, char *myenv[10], int flags) { ATTR *d; char buff[BUFFER_LEN], *bp, *sp, *asave; char const *ap; int j; char *preserves[10]; char *preserveq[NUMQ]; dbref preserve_orator = orator; int need_pres = 0; int attribs_used = 0; loc = (loc == NOTHING) ? Location(player) : loc; orator = player; /* only give messages if the location is good */ if (GoodObject(loc)) { /* message to player */ if (what && *what) { d = atr_get(thing, what); if (d) { attribs_used = 1; if (!need_pres) { need_pres = 1; save_global_regs("did_it_save", preserveq); save_global_env("did_it_save", preserves); } restore_global_env("did_it", myenv); asave = safe_atr_value(d); ap = asave; bp = buff; process_expression(buff, &bp, &ap, thing, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; notify_by(thing, player, buff); free((Malloc_t) asave); } else if (def && *def) notify_by(thing, player, def); } /* message to neighbors */ if (!DarkLegal(player)) { if (owhat && *owhat) { d = atr_get(thing, owhat); if (d) { attribs_used = 1; if (!need_pres) { need_pres = 1; save_global_regs("did_it_save", preserveq); save_global_env("did_it_save", preserves); } restore_global_env("did_it", myenv); asave = safe_atr_value(d); ap = asave; bp = buff; if (!((d)->flags & AF_NONAME)) { safe_str(Name(player), buff, &bp); if (!((d)->flags & AF_NOSPACE)) safe_chr(' ', buff, &bp); } sp = bp; process_expression(buff, &bp, &ap, thing, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; if (bp != sp) notify_except2(Contents(loc), player, thing, buff, flags); free((Malloc_t) asave); } else { if (odef && *odef) { notify_except2(Contents(loc), player, thing, tprintf("%s %s", Name(player), odef), flags); } } } } } if (need_pres) { restore_global_regs("did_it_save", preserveq); restore_global_env("did_it_save", preserves); } for (j = 0; j < 10; j++) global_eval_context.wnxt[j] = myenv[j]; for (j = 0; j < NUMQ; j++) global_eval_context.rnxt[j] = NULL; attribs_used = charge_action(player, thing, awhat) || attribs_used; orator = preserve_orator; return attribs_used; } /** Return the first object near another object that is visible to a player. * * BEWARE: * * first_visible() does not behave as intended. It _should_ return the first * object in `thing' that is !DARK. However, because of the controls() check * the function will return a DARK object if the player owns it. * * The behavior is left as is because so many functions in fundb.c rely on * the incorrect behavior to return expected values. The lv*() functions * also make rewriting this fairly pointless. * * \param player the looker. * \param thing an object in the location to be inspected. * \return dbref of first visible object or NOTHING. */ dbref first_visible(dbref player, dbref thing) { int lck = 0; int ldark; dbref loc; if (!GoodObject(thing) || IsRoom(thing)) return NOTHING; loc = IsExit(thing) ? Source(thing) : Location(thing); if (!GoodObject(loc)) return NOTHING; ldark = IsPlayer(loc) ? Opaque(loc) : Dark(loc); while (GoodObject(thing)) { if (can_interact(thing, player, INTERACT_SEE)) { if (DarkLegal(thing) || (ldark && !Light(thing))) { if (!lck) { if (See_All(player) || (loc == player) || controls(player, loc)) return thing; lck = 1; } if (controls(player, thing)) /* this is what causes DARK objects to show */ return thing; } else { return thing; } } thing = Next(thing); } return thing; } /** Can a player see something? * \param player the looker. * \param thing object to be seen. * \param can_see_loc 1 if player can see the location, 0 if location is dark. * \retval 1 player can see thing. * \retval 0 player can not see thing. */ int can_see(dbref player, dbref thing, int can_see_loc) { if (!can_interact(thing, player, INTERACT_SEE)) return 0; /* * 1) your own body isn't listed in a 'look' 2) exits aren't listed in a * 'look' 3) unconnected (sleeping) players aren't listed in a 'look' */ if (player == thing || IsExit(thing) || (IsPlayer(thing) && !Connected(thing))) return 0; /* if the room is lit, you can see any non-dark objects */ else if (can_see_loc) return (!DarkLegal(thing)); /* otherwise room is dark and you can only see lit things */ else return (Light(thing) && !DarkLegal(thing)); } /** Can a player control a thing? * The control rules are, in order: * Only God controls God. * Wizards control everything else. * Nothing else controls a wizard, and only royalty control royalty. * Mistrusted objects control only themselves. * Objects with the same owner control each other, unless the * target object is TRUST and the would-be controller isn't. * If ZMOs allow control, and you pass the ZMO, you control. * If the owner is a Zone Master, and you pass the ZM, you control. * If you pass the control lock, you control. * Otherwise, no dice. * \param who object attempting to control. * \param what object to be controlled. * \retval 1 who controls what. * \retval 0 who doesn't control what. */ int controls(dbref who, dbref what) { boolexp c; if (!GoodObject(what)) return 0; if (God(what) && !God(who)) return 0; if (Wizard(who)) return 1; if (Wizard(what) || (Hasprivs(what) && !Hasprivs(who))) return 0; if (Mistrust(who) && (who != what)) return 0; if (Owns(who, what) && (!Inheritable(what) || Inheritable(who))) return 1; if (Inheritable(what) || IsPlayer(what)) return 0; if (!ZONE_CONTROL_ZMP && (Zone(what) != NOTHING) && eval_lock(who, Zone(what), Zone_Lock)) return 1; if (ZMaster(Owner(what)) && !IsPlayer(what) && eval_lock(who, Owner(what), Zone_Lock)) return 1; c = getlock_noparent(what, Control_Lock); if (c != TRUE_BOOLEXP) { if (eval_boolexp(who, c, what)) return 1; } return 0; } /** Can someone pay for something (in cash and quota)? * Does who have enough pennies to pay for something, and if something * is being built, does who have enough quota? Wizards, roys * aren't subject to either. This function not only checks that they * can afford it, but actually charges them. * \param who player attempting to pay. * \param pennies cost in pennies. * \retval 1 who can pay. * \retval 0 who can't pay. */ int can_pay_fees(dbref who, int pennies) { /* check database size -- EVERYONE is subject to this! */ if (DBTOP_MAX && (db_top >= DBTOP_MAX + 1) && (first_free == NOTHING)) { notify(who, T("Sorry, there is no more room in the database.")); return 0; } /* Can they afford it? */ if (!NoPay(who) && (Pennies(Owner(who)) < pennies)) { notify_format(who, T("Sorry, you don't have enough %s."), MONIES); return 0; } /* check building quota */ if (!pay_quota(who, QUOTA_COST)) { notify(who, T("Sorry, your building quota has run out.")); return 0; } /* charge */ payfor(who, pennies); return 1; } /** Transfer pennies to an object's owner. * \param who recipient. * \param pennies amount of pennies to give. */ void giveto(dbref who, int pennies) { if (NoPay(who)) return; /* Giving to a NoPay object or owner */ who = Owner(who); if ((Pennies(who) + pennies) > Max_Pennies(who)) s_Pennies(who, Max_Pennies(who)); else s_Pennies(who, Pennies(who) + pennies); } /** Debit a player's pennies, if they can afford it. * \param who player to debit. * \param cost number of pennies to debit. * \retval 1 player successfully debited. * \retval 0 player can't afford the cost. */ int payfor(dbref who, int cost) { /* subtract cost from who's pennies */ int tmp; if (NoPay(who)) return 1; who = Owner(who); if ((tmp = Pennies(who)) >= cost) { s_Pennies(who, tmp - cost); return 1; } else return 0; } /** Retrieve the amount of quote remaining to a player. * Figure out a player's quota. Add the RQUOTA attribute if he doesn't * have one already. This function returns the REMAINING quota, not * the TOTAL limit. * \param who player to check. * \return player's remaining quota. */ int get_current_quota(dbref who) { ATTR *a; int i; int limit; int owned = 0; /* if he's got an RQUOTA attribute, his remaining quota is that */ a = atr_get_noparent(Owner(who), "RQUOTA"); if (a) return parse_integer(atr_value(a)); /* else, count up his objects. If he has less than the START_QUOTA, * then his remaining quota is that minus his number of current objects. * Otherwise, it's his current number of objects. Add the attribute * if he doesn't have it. */ for (i = 0; i < db_top; i++) if (Owner(i) == Owner(who)) owned++; owned--; /* don't count the player himself */ if (owned <= START_QUOTA) limit = START_QUOTA - owned; else limit = owned; (void) atr_add(Owner(who), "RQUOTA", tprintf("%d", limit), GOD, NOTHING); return limit; } /** Add or subtract from a player's quota. * \param who object whose owner has the quota changed. * \param payment amount to add to quota (may be negative). */ void change_quota(dbref who, int payment) { (void) atr_add(Owner(who), "RQUOTA", tprintf("%d", get_current_quota(who) + payment), GOD, NOTHING); } /** Debit a player's quota, if they can afford it. * \param who player whose quota is to be debitted. * \param cost amount of quota to be charged. * \retval 1 quota successfully debitted. * \retval 0 not enough quota to debit. */ static int pay_quota(dbref who, int cost) { int curr; /* figure out how much we have, and if it's big enough */ curr = get_current_quota(who); if (USE_QUOTA && !NoQuota(who) && (curr - cost < 0)) /* not enough */ return 0; change_quota(who, -cost); return 1; } /** Is a name in the forbidden names file? * \param name name to check. * \retval 1 name is forbidden. * \retval 0 name is not forbidden. */ int forbidden_name(const char *name) { char buf[BUFFER_LEN], *newlin, *ptr; FILE *fp; char *upname; upname = strupper(name); fp = fopen(NAMES_FILE, FOPEN_READ); if (!fp) return 0; while (fgets(buf, sizeof buf, fp)) { upcasestr(buf); /* step on the newline */ if ((newlin = strchr(buf, '\r'))) *newlin = '\0'; else if ((newlin = strchr(buf, '\n'))) *newlin = '\0'; ptr = buf; if (name && ptr && quick_wild(ptr, name)) { fclose(fp); return 1; } } fclose(fp); return 0; } /** Is a name valid for an object? * This involves several checks. * Names may not have leading or trailing spaces. * Names must be only printable characters. * Names may not exceed the length limit. * Names may not start with certain tokens, or be "me", "home", "here" * \param n name to check. * \retval 1 name is valid. * \retval 0 name is not valid. */ int ok_name(const char *n) { const unsigned char *p, *name = (const unsigned char *) n; if (!name || !*name) return 0; /* No leading spaces */ if (isspace((unsigned char) *name)) return 0; /* only printable characters */ for (p = name; p && *p; p++) { if (!isprint((unsigned char) *p)) return 0; if (ONLY_ASCII_NAMES && *p > 127) return 0; if (strchr("[]%\\=&|", *p)) return 0; } /* No trailing spaces */ p--; if (isspace((unsigned char) *p)) return 0; /* Not too long */ if (u_strlen(name) >= OBJECT_NAME_LIMIT) return 0; /* No magic cookies */ return (name && *name && *name != LOOKUP_TOKEN && *name != NUMBER_TOKEN && *name != NOT_TOKEN && strcasecmp((char *) name, "me") && strcasecmp((char *) name, "home") && strcasecmp((char *) name, "here")); } /** Is a name a valid player name? * Player names must be valid object names, but also not forbidden (unless * the player is a wizard). They are * subject to a different length limit, and subject to more stringent * restrictions on valid characters. Finally, it can't be the same as * an existing player name or alias. * \param name name to check. * \param player player for permission checks. * \retval 1 name is valid for players. * \retval 0 name is not valid for players. */ int ok_player_name(const char *name, dbref player) { const unsigned char *scan, *good; if (!ok_name(name) || (forbidden_name(name) && !(GoodObject(player) && Wizard(player))) || strlen(name) >= (size_t) PLAYER_NAME_LIMIT) return 0; good = (unsigned char *) (PLAYER_NAME_SPACES ? " `$_-.,'" : "`$_-.,'"); /* Make sure that the name contains legal characters only */ for (scan = (unsigned char *) name; scan && *scan; scan++) { if (isalnum((unsigned char) *scan)) continue; if (!strchr((char *) good, *scan)) return 0; } return (lookup_player(name) == NOTHING); } /** Is a password acceptable? * Acceptable passwords must be non-null and must contain only * printable characters and no whitespace. * \param password password to check. * \retval 1 password is acceptable. * \retval 0 password is not acceptable. */ int ok_password(const char *password) { const unsigned char *scan; if (*password == '\0') return 0; for (scan = (const unsigned char *) password; *scan; scan++) { if (!(isprint(*scan) && !isspace(*scan))) { return 0; } } return 1; } /** Is a name ok for a command or function? * It must begin with an uppercase alpha, and contain only * uppercase alpha, numbers, or underscore thereafter. * \param name name to check. * \retval 1 name is acceptable. * \retval 0 name is not acceptable. */ int ok_command_name(const char *name) { const unsigned char *p; if (!isupper((unsigned char) *name)) return 0; for (p = (unsigned char *) name; p && *p; p++) { if (!(isupper(*p) || isdigit(*p) || (*p == '_'))) return 0; } /* Not too long */ if (strlen(name) >= COMMAND_NAME_LIMIT) return 0; return 1; } /** Does params contain only acceptable HTML tag attributes? * Right now, this means: filter out SEND and XCH_CMD if * the player isn't a Wizard. Params may contain a space-separated * list of tag=value pairs. It's probably possible to fool this * checking. Needs more work, or removing HTML support. * \param player player using the attribute, or NOTHING for internal. * \param params the attributes to use. * \retval 1 params is acceptable. * \retval 0 params is not accpetable. */ int ok_tag_attribute(dbref player, const char *params) { const unsigned char *p, *q; if (!GoodObject(player) || Wizard(player)) return 1; p = (const unsigned char *) params; while (*p) { while (*p && isspace(*p)) p++; q = p; while (*q && *q != '=') q++; if (*q) { size_t n = q - p; /* Invalid params for non-wizards. Turn to a hashtable if we ever get more? */ if (strncasecmp(p, "SEND", n) == 0 || strncasecmp(p, "XCH_CMD", n) == 0) return 0; while (*q && isspace(*q)) q++; while (*q && !isspace(*q)) q++; p = q; } else return 0; /* Malformed param without an = */ } return 1; } /** The switch command. * \verbatim * For lack of better place the @switch code is here. * @switch expression=args * \endverbatim * \param player the enactor. * \param expression the expression to test against cases. * \param argv array of cases and actions. * \param cause the object that caused this code to run. * \param first if 1, run only first matching case; if 0, run all matching cases. * \param notifyme if 1, perform a notify after executing matched cases. * \param regexp if 1, do regular expression matching; if 0, wildcard globbing. */ void do_switch(dbref player, char *expression, char **argv, dbref cause, int first, int notifyme, int regexp) { int any = 0, a; char buff[BUFFER_LEN], *bp; char const *ap; char *tbuf1; if (!argv[1]) return; /* set up environment for any spawned commands */ for (a = 0; a < 10; a++) global_eval_context.wnxt[a] = global_eval_context.wenv[a]; for (a = 0; a < NUMQ; a++) global_eval_context.rnxt[a] = global_eval_context.renv[a]; /* now try a wild card match of buff with stuff in coms */ for (a = 1; !(first && any) && (a < (MAX_ARG - 1)) && argv[a] && argv[a + 1]; a += 2) { /* eval expression */ ap = argv[a]; bp = buff; process_expression(buff, &bp, &ap, player, cause, cause, PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; /* check for a match */ if (regexp ? quick_regexp_match(buff, expression, 0) : local_wild_match(buff, expression)) { any = 1; tbuf1 = replace_string("#$", expression, argv[a + 1]); parse_que(player, tbuf1, cause); mush_free(tbuf1, "replace_string.buff"); } } /* do default if nothing has been matched */ if ((a < MAX_ARG) && !any && argv[a]) { tbuf1 = replace_string("#$", expression, argv[a]); parse_que(player, tbuf1, cause); mush_free(tbuf1, "replace_string.buff"); } /* Pop on @notify me, if requested */ if (notifyme) parse_que(player, "@notify me", cause); } /** Parse possessive matches for the possessor. * This function parses strings of the form "Sam's bag" and attempts * to match "Sam". It returns NOTHING if * there's no possessive 's in the string. It destructively modifies * the string (terminating after the possessor name) and modifies the pointer * to the string to point at the name of the contained object. * \param player the enactor/looker. * \param str a pointer to a string to check for possessive matches. * \return matching dbref or NOTHING or AMBIGUOUS. */ dbref parse_match_possessor(dbref player, const char **str) { const char *box; /* name of container */ char *obj; /* name of object */ box = *str; /* check to see if we have an 's sequence */ if ((obj = strchr(box, '\'')) == NULL) return NOTHING; *obj++ = '\0'; /* terminate */ if ((*obj == '\0') || ((*obj != 's') && (*obj != 'S'))) return NOTHING; /* skip over the 's' and whitespace */ do { obj++; } while (isspace((unsigned char) *obj)); *str = obj; /* we already have a terminating null, so we're okay to just do matches */ return match_result(player, box, NOTYPE, MAT_NEIGHBOR | MAT_POSSESSION); } /** Autoreply messages for pages (HAVEN, IDLE, AWAY). * \param player the paging player. * \param target the paged player. * \param type type of message to return. * \param message name of attribute containing the message. * \param def default message to return. */ void page_return(dbref player, dbref target, const char *type, const char *message, const char *def) { ATTR *d; char buff[BUFFER_LEN], *bp, *asave; char const *ap; struct tm *ptr; if (message && *message) { d = atr_get(target, message); if (d) { asave = safe_atr_value(d); ap = asave; bp = buff; process_expression(buff, &bp, &ap, target, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; free((Malloc_t) asave); if (*buff) { ptr = (struct tm *) localtime(&mudtime); notify_format(player, T("%s message from %s: %s"), type, Name(target), buff); if (!Haven(target)) notify_format(target, T("[%d:%02d] %s message sent to %s."), ptr->tm_hour, ptr->tm_min, type, Name(player)); } } else if (def && *def) notify(player, def); } } /** Returns the apparent location of object. * This is the location for players and things, source for exits, and * NOTHING for rooms. * \param thing object to get location of. * \return apparent location of object (NOTHING for rooms). */ dbref where_is(dbref thing) { if (!GoodObject(thing)) return NOTHING; switch (Typeof(thing)) { case TYPE_ROOM: return NOTHING; case TYPE_EXIT: return Home(thing); default: return Location(thing); } } /** Are two objects near each other? * Returns 1 if obj1 is "nearby" object2. "Nearby" is a commutative * relation defined as: * obj1 is in the same room as obj2, obj1 is being carried by * obj2, or obj1 is carrying obj2. * Returns 0 if object isn't nearby or the input is invalid. * \param obj1 first object. * \param obj2 second object. * \retval 1 the objects are near each other. * \retval 0 the objects are not near each other. */ int nearby(dbref obj1, dbref obj2) { dbref loc1, loc2; if (!GoodObject(obj1) || !GoodObject(obj2)) return 0; if (IsRoom(obj1) && IsRoom(obj2)) return 0; loc1 = where_is(obj1); if (loc1 == obj2) return 1; loc2 = where_is(obj2); if ((loc2 == obj1) || (loc2 == loc1)) return 1; return 0; } /** User-defined verbs. * \verbatim * This implements the @verb command. * \endverbatim * \param player the enactor. * \param cause the object causing this command to run. * \param arg1 the object to read verb attributes from. * \param argv the array of remaining arguments to the verb command. */ void do_verb(dbref player, dbref cause, char *arg1, char **argv) { dbref victim; dbref actor; int i; char *wsave[10]; /* find the object that we want to read the attributes off * (the object that was the victim of the command) */ /* our victim object can be anything */ victim = match_result(player, arg1, NOTYPE, MAT_EVERYTHING); if (victim == NOTHING) { notify(player, T("What was the victim of the verb?")); return; } /* find the object that executes the action */ if (!argv || !argv[1] || !*argv[1]) { notify(player, T("What do you want to do with the verb?")); return; } actor = match_result(player, argv[1], NOTYPE, MAT_EVERYTHING); if (actor == NOTHING) { notify(player, T("What do you want to do the verb?")); return; } /* Control check is fascist. * First check: we don't want <actor> to do something involuntarily. * Both victim and actor have to be controlled by the thing which did * the @verb (for speed we do a WIZARD check first), or: cause controls * actor plus the second check is passed. * Second check: we need read access to the attributes. * Either the player controls victim or the player * must be priviledged, or the victim has to be VISUAL. */ if (!(Wizard(player) || (controls(player, victim) && controls(player, actor)) || ((controls(cause, actor) && Can_Examine(player, victim))))) { notify(player, T("Permission denied.")); return; } /* We're okay. Send out messages. */ for (i = 0; i < 10; i++) { wsave[i] = global_eval_context.wenv[i]; global_eval_context.wenv[i] = argv[i + 7]; } real_did_it(actor, victim, upcasestr(argv[2]), argv[3], upcasestr(argv[4]), argv[5], NULL, Location(actor), global_eval_context.wenv, NA_INTER_HEAR); for (i = 0; i < 10; i++) global_eval_context.wenv[i] = wsave[i]; /* Now we copy our args into the stack, and do the command. */ for (i = 0; i < 10; i++) global_eval_context.wnxt[i] = argv[i + 7]; charge_action(actor, victim, upcasestr(argv[6])); } /** Structure for passing arguments to grep_util_helper(). */ struct guh_args { char *buff; /**< Buffer for output */ char *bp; /**< Pointer to buff's current position */ char *lookfor; /**< String to grep for */ int len; /**< Length of lookfor */ int insensitive; /**< If 1, case-insensitive match; if 0, sensitive */ }; static int grep_util_helper(dbref player __attribute__ ((__unused__)), dbref thing __attribute__ ((__unused__)), dbref parent __attribute__ ((__unused__)), char const *pattern __attribute__ ((__unused__)), ATTR *atr, void *args) { struct guh_args *guh = args; int found; char *s; s = (char *) atr_value(atr); /* warning: static */ found = 0; while (*s && !found) { if ((!guh->insensitive && !strncmp(guh->lookfor, s, guh->len)) || (guh->insensitive && !strncasecmp(guh->lookfor, s, guh->len))) found = 1; else s++; } if (found) { if (guh->bp != guh->buff) safe_chr(' ', guh->buff, &guh->bp); safe_str(AL_NAME(atr), guh->buff, &guh->bp); } return found; } /** Utility function for grep funtions/commands. * This function returns a list of attributes on an object that * match a name pattern and contain another string. * \param player the enactor. * \param thing object to check attributes on. * \param pattern wildcard pattern for attributes to check. * \param lookfor string to find within each attribute. * \param len length of lookfor. * \param insensitive if 1, case-insensitive matching; if 0, case-sensitive. * \return string containing list of attribute names with matching data. */ char * grep_util(dbref player, dbref thing, char *pattern, char *lookfor, int len, int insensitive) { struct guh_args guh; guh.buff = (char *) mush_malloc(BUFFER_LEN + 1, "grep_util.buff"); guh.bp = guh.buff; guh.lookfor = lookfor; guh.len = len; guh.insensitive = insensitive; (void) atr_iter_get(player, thing, pattern, 0, grep_util_helper, &guh); *guh.bp = '\0'; return guh.buff; } /** Structure for grep_helper() arguments. */ struct gh_args { char *lookfor; /**< Pattern to look for. */ int len; /**< Length of lookfor. */ int insensitive; /**< if 1, case-insensitive matching; if 0, sensitive */ }; static int grep_helper(dbref player, dbref thing __attribute__ ((__unused__)), dbref parent __attribute__ ((__unused__)), char const *pattern __attribute__ ((__unused__)), ATTR *atr, void *args) { struct gh_args *gh = args; int found; char *s; char tbuf1[BUFFER_LEN]; char buf[BUFFER_LEN]; char *tbp; found = 0; tbp = tbuf1; s = (char *) atr_value(atr); /* warning: static */ while (s && *s) { if ((gh->insensitive && !strncasecmp(gh->lookfor, s, gh->len)) || (!gh->insensitive && !strncmp(gh->lookfor, s, gh->len))) { found = 1; strncpy(buf, s, gh->len); buf[gh->len] = '\0'; s += gh->len; safe_format(tbuf1, &tbp, "%s%s%s", ANSI_HILITE, buf, ANSI_NORMAL); } else { safe_chr(*s, tbuf1, &tbp); s++; } } *tbp = '\0'; /* if we got it, display it */ if (found) notify_format(player, "%s%s [#%d%s]%s %s", ANSI_HILITE, AL_NAME(atr), Owner(AL_CREATOR(atr)), privs_to_letters(attr_privs, AL_FLAGS(atr)), ANSI_NORMAL, tbuf1); return found; } /** The grep command * \verbatim * This implements @grep. * \endverbatim * \param player the enactor. * \param obj string containing obj/attr pattern to grep through. * \param lookfor unparsed string to search for. * \param flag if 0, return attribute names; if 1, return attrib text. * \param insensitive if 1, case-insensitive match; if 0, sensitive. */ void do_grep(dbref player, char *obj, char *lookfor, int flag, int insensitive) { struct gh_args gh; dbref thing; char *pattern; int len; char *tp; if ((len = strlen(lookfor)) < 1) { notify(player, T("What pattern do you want to grep for?")); return; } /* find the attribute pattern */ pattern = strchr(obj, '/'); if (!pattern) pattern = (char *) "*"; /* set it to global match */ else *pattern++ = '\0'; /* now we've got the object. match for it. */ if ((thing = noisy_match_result(player, obj, NOTYPE, MAT_EVERYTHING)) == NOTHING) return; if (!Can_Examine(player, thing)) { notify(player, T("Permission denied.")); return; } if (flag) { gh.lookfor = lookfor; gh.len = len; gh.insensitive = insensitive; if (!atr_iter_get(player, thing, pattern, 0, grep_helper, &gh)) notify(player, T("No matching attributes.")); } else { tp = grep_util(player, thing, pattern, lookfor, len, insensitive); notify_format(player, T("Matches of '%s' on %s(#%d): %s"), lookfor, Name(thing), thing, tp); mush_free((Malloc_t) tp, "grep_util.buff"); } }