/** * \file set.c * * \brief PennMUSH commands that set parameters. * * */ #include "copyrite.h" #include "config.h" #include <stdio.h> #include <ctype.h> #include <string.h> #ifdef I_SYS_TIME #include <sys/time.h> #else #include <time.h> #endif #ifdef I_SYS_TYPES #include <sys/types.h> #endif #include <stdlib.h> #include "conf.h" #include "externs.h" #include "mushdb.h" #include "match.h" #include "attrib.h" #include "ansi.h" #include "command.h" #include "mymalloc.h" #include "flags.h" #include "dbdefs.h" #include "lock.h" #include "log.h" #include "game.h" #include "confmagic.h" static int chown_ok(dbref player, dbref thing, dbref newowner); void do_attrib_flags (dbref player, const char *obj, const char *atrname, const char *flag); static int af_helper(dbref player, dbref thing, dbref parent, char const *pattern, ATTR *atr, void *args); static int gedit_helper(dbref player, dbref thing, dbref parent, char const *pattern, ATTR *atr, void *args); static int wipe_helper(dbref player, dbref thing, dbref parent, char const *pattern, ATTR *atr, void *args); static void copy_attrib_flags(dbref player, dbref target, ATTR *atr, int flags); /** Rename something. * \verbatim * This implements @name. * \endverbatim * \param player the enactor. * \param name current name of object to rename. * \param newname new name for object. */ void do_name(dbref player, const char *name, char *newname) { dbref thing; ATTR *a = NULL; char alias[BUFFER_LEN]; char *password; char *myenv[10]; int i; newname = trim_space_sep(newname, ' '); if ((thing = match_controlled(player, name)) != NOTHING) { /* check for bad name */ if ((*newname == '\0') || strchr(newname, '[')) { notify(player, T("Give it what new name?")); return; } /* check for renaming a player */ if (IsPlayer(thing)) { if (PLAYER_NAME_SPACES) { if (*newname == '\"') { for (; *newname && ((*newname == '\"') || isspace((unsigned char) *newname)); newname++) ; password = newname; while (*password && (*password != '\"')) { while (*password && (*password != '\"')) password++; if (*password == '\"') { *password++ = '\0'; while (*password && isspace((unsigned char) *password)) password++; break; } } } else { password = newname; while (*password && !isspace((unsigned char) *password)) password++; if (*password) { *password++ = '\0'; while (*password && isspace((unsigned char) *password)) password++; } } } else { /* split off password */ for (password = newname + strlen(newname) - 1; *password && !isspace((unsigned char) *password); password--) ; for (; *password && isspace((unsigned char) *password); password--) ; /* eat whitespace */ if (*password) { *++password = '\0'; /* terminate name */ password++; while (*password && isspace((unsigned char) *password)) password++; } } a = atr_get_noparent(thing, "ALIAS"); if (a) strcpy(alias, atr_value(a)); if (strcasecmp(newname, Name(thing)) && !(a && !strcasecmp(newname, alias)) /* Swap alias, name */ &&!ok_player_name(newname, thing)) { /* strcasecmp allows changing foo to Foo, etc. */ notify(player, T("You can't give a player that name.")); return; } if (a && strcasecmp(alias, newname)) { a = NULL; } /* everything ok, notify */ do_log(LT_CONN, 0, 0, T("Name change by %s(#%d) to %s"), Name(thing), thing, newname); if (Suspect(thing)) flag_broadcast("WIZARD", 0, T("Broadcast: Suspect %s changed name to %s."), Name(thing), newname); /* everything ok, we can fall through to change the name */ } else { if (!ok_name(newname)) { notify(player, T("That is not a reasonable name.")); return; } } /* everything ok, change the name */ myenv[0] = (char *) mush_malloc(BUFFER_LEN, "string"); myenv[1] = (char *) mush_malloc(BUFFER_LEN, "string"); strncpy(myenv[0], Name(thing), BUFFER_LEN - 1); myenv[0][BUFFER_LEN - 1] = '\0'; strcpy(myenv[1], newname); for (i = 2; i < 10; i++) myenv[i] = NULL; if (IsPlayer(thing) && !a) delete_player(thing, NULL); else if (a) atr_add(thing, "ALIAS", Name(thing), player, 0); set_name(thing, newname); if (IsPlayer(thing) && !a) add_player(thing, NULL); if (!AreQuiet(player, thing)) notify(player, T("Name set.")); real_did_it(player, thing, NULL, NULL, "ONAME", NULL, "ANAME", NOTHING, myenv, NA_INTER_PRESENCE); mush_free(myenv[0], "string"); mush_free(myenv[1], "string"); } } /** Change an object's owner. * \verbatim * This implements @chown. * \endverbatim * \param player the enactor. * \param name name of object to change owner of. * \param newobj name of new owner for object. * \param preserve if 1, preserve privileges and don't halt the object. */ void do_chown(dbref player, const char *name, const char *newobj, int preserve) { dbref thing; dbref newowner = NOTHING; char *sp; long match_flags = MAT_POSSESSION | MAT_HERE | MAT_EXIT | MAT_ABSOLUTE; /* check for '@chown <object>/<atr>=<player>' */ sp = strchr(name, '/'); if (sp) { do_atrchown(player, name, newobj); return; } if (Wizard(player)) match_flags |= MAT_PLAYER; if ((thing = noisy_match_result(player, name, TYPE_THING, match_flags)) == NOTHING) return; if (!*newobj || !strcasecmp(newobj, "me")) { newowner = player; } else { if ((newowner = lookup_player(newobj)) == NOTHING) { notify(player, T("I couldn't find that player.")); return; } } if (IsPlayer(thing) && !God(player)) { notify(player, T("Players always own themselves.")); return; } /* Permissions checking */ if (!chown_ok(player, thing, newowner)) { notify(player, T("Permission denied.")); return; } if (IsThing(thing) && !Hasprivs(player) && !(GoodObject(Location(thing)) && (Location(thing) == player))) { notify(player, T("You must carry the object to @chown it.")); return; } if (preserve && !Wizard(player)) { notify(player, T("You cannot @CHOWN/PRESERVE. Use normal @CHOWN.")); return; } /* chowns to the zone master don't count towards fees */ if (!ZMaster(newowner)) { /* Debit the owner-to-be */ if (!can_pay_fees(newowner, Pennies(thing))) { /* not enough money or quota */ if (newowner != player) notify(player, T ("That player doesn't have enough money or quota to receive that object.")); return; } /* Credit the current owner */ giveto(Owner(thing), Pennies(thing)); change_quota(Owner(thing), QUOTA_COST); } chown_object(player, thing, newowner, preserve); notify(player, T("Owner changed.")); } static int chown_ok(dbref player, dbref thing, dbref newowner) { /* Cant' touch garbage */ if (IsGarbage(thing)) return 0; /* Wizards can do it all */ if (Wizard(player)) return 1; /* In order for non-wiz player to @chown thing to newowner, * player must control newowner or newowner must be a Zone Master * and player must pass its zone lock. * * In addition, one of the following must apply: * 1. player owns thing, or * 2. player controls Owner(thing), newowner is a zone master, * and Owner(thing) passes newowner's zone-lock, or * 3. thing is CHOWN_OK, and player holds thing if it's an object. * * The third condition is syntactic sugar to handle the situation * where Joe owns Box, an ordinary object, and Tool, an inherit object, * and ZMP, a Zone Master Player, is zone-locked to =tool. * In this case, if Joe doesn't pass ZMP's lock, we don't want * Joe to be able to @fo Tool=@chown Box=ZMP */ /* Does player control newowner, or is newowner a Zone Master and player * passes the lock? */ if (!(controls(player, newowner) || (ZMaster(newowner) && eval_lock(player, newowner, Zone_Lock)))) return 0; /* Target player is legitimate. Does player control the object? */ if (Owns(player, thing)) return 1; if (controls(player, Owner(thing)) && ZMaster(newowner) && eval_lock(Owner(thing), newowner, Zone_Lock)) return 1; if (ChownOk(thing) && (!IsThing(thing) || (Location(thing) == player))) return 1; return 0; } /** Actually change the ownership of something, and fix bits. * \param player the enactor. * \param thing object to change ownership of. * \param newowner new owner for thing. * \param preserve if 1, preserve privileges and don't halt. */ void chown_object(dbref player, dbref thing, dbref newowner, int preserve) { (void) undestroy(player, thing); if (God(player)) { Owner(thing) = newowner; } else { Owner(thing) = Owner(newowner); } /* Don't allow circular zones */ Zone(thing) = NOTHING; if (GoodObject(Zone(newowner))) { dbref tmp; int ok_to_zone = 1; int zone_depth = MAX_ZONES; for (tmp = Zone(Zone(newowner)); GoodObject(tmp); tmp = Zone(tmp)) { if (tmp == thing) { notify(player, T("Circular zone broken.")); ok_to_zone = 0; break; } if (tmp == Zone(tmp)) /* Ran into an object zoned to itself */ break; zone_depth--; if (!zone_depth) { ok_to_zone = 0; notify(player, T("Overly deep zone chain broken.")); break; } } if (ok_to_zone) Zone(thing) = Zone(newowner); } clear_flag_internal(thing, "CHOWN_OK"); if (!preserve || !Wizard(player)) { clear_flag_internal(thing, "WIZARD"); clear_flag_internal(thing, "ROYALTY"); clear_flag_internal(thing, "TRUST"); set_flag_internal(thing, "HALT"); destroy_flag_bitmask(Powers(thing)); Powers(thing) = new_flag_bitmask("POWER"); do_halt(thing, "", thing); } else { if ((newowner != player) && Wizard(thing) && !God(player)) { notify_format(player, T ("Warning: WIZ flag reset on #%d because @CHOWN/PRESERVE is to a third party."), thing); clear_flag_internal(thing, "WIZARD"); } if (!null_flagmask("POWER", Powers(thing)) || Wizard(thing) || Royalty(thing) || Inherit(thing)) notify_format(player, T ("Warning: @CHOWN/PRESERVE on a object (#%d) with WIZ, ROY, INHERIT, or @power privileges."), thing); } } /** Change an object's zone. * \verbatim * This implements @chzone. * \endverbatim * \param player the enactor. * \param name name of the object to change zone of. * \param newobj name of new ZMO. * \param noisy if 1, notify player about success and failure. * \retval 0 failed to change zone. * \retval 1 successfully changed zone. */ int do_chzone(dbref player, char const *name, char const *newobj, int noisy) { dbref thing; dbref zone; if ((thing = noisy_match_result(player, name, NOTYPE, MAT_NEARBY)) == NOTHING) return 0; if (!newobj || !*newobj || !strcasecmp(newobj, "none")) zone = NOTHING; else { if ((zone = noisy_match_result(player, newobj, NOTYPE, MAT_EVERYTHING)) == NOTHING) return 0; } if (Zone(thing) == zone) { if (noisy) notify(player, T("That object is already in that zone.")); return 0; } /* we do use ownership instead of control as a criterion because * we only want the owner to be able to rezone the object. Also, * this allows players to @chzone themselves to an object they own. */ if (!(God(player) || (!God(thing) && Wizard(player)) || Owns(player, thing))) { if (noisy) notify(player, T("You don't have the power to shift reality.")); return 0; } /* a player may change an object's zone to: * 1. NOTHING * 2. an object he owns * 3. an object with a chzone-lock that the player passes. * Note that an object with no chzone-lock isn't valid */ if (!(Wizard(player) || (zone == NOTHING) || Owns(player, zone) || ((getlock(zone, Chzone_Lock) != TRUE_BOOLEXP) && eval_lock(player, zone, Chzone_Lock)))) { if (noisy) notify(player, T("You cannot move that object to that zone.")); return 0; } /* Don't chzone object to itself for mortals! */ if ((zone == thing) && !Hasprivs(player)) { if (noisy) notify(player, T("You shouldn't zone objects to themselves!")); return 0; } /* Don't allow circular zones */ if (GoodObject(zone)) { dbref tmp; int zone_depth = MAX_ZONES; for (tmp = Zone(zone); GoodObject(tmp); tmp = Zone(tmp)) { if (tmp == thing) { notify(player, T("You can't make circular zones!")); return 0; } if (tmp == Zone(tmp)) /* Ran into an object zoned to itself */ break; zone_depth--; if (!zone_depth) { notify(player, T("Overly deep zone chain.")); return 0; } } } /* Don't allow chzone to objects without elocks! * If no lock is set, set a default lock (warn if zmo are used for control) * This checks for many trivial elocks (canuse/1, where &canuse=1) */ if (zone != NOTHING) check_zone_lock(player, zone, noisy); /* Warn Wiz/Royals when they zone their stuff */ if ((zone != NOTHING) && Hasprivs(Owner(thing))) { if (noisy) notify(player, T("Warning: @chzoning admin-owned object!")); } /* everything is okay, do the change */ Zone(thing) = zone; /* If we're not unzoning, and we're working with a non-player object, * we'll remove wizard, royalty, inherit, and powers, for security. */ if ((zone != NOTHING) && !IsPlayer(thing)) { /* if the object is a player, resetting these flags is rather * inconvenient -- although this may pose a bit of a security * risk. Be careful when @chzone'ing wizard or royal players. */ clear_flag_internal(thing, "WIZARD"); clear_flag_internal(thing, "ROYALTY"); clear_flag_internal(thing, "TRUST"); destroy_flag_bitmask(Powers(thing)); Powers(thing) = new_flag_bitmask("POWER"); } else { if (noisy && (zone != NOTHING)) { if (Hasprivs(thing)) notify(player, T("Warning: @chzoning a privileged player.")); if (Inherit(thing)) notify(player, T("Warning: @chzoning an TRUST player.")); } } if (noisy) notify(player, T("Zone changed.")); return 1; } /** Structure for af_helper() data. */ struct af_args { int f; /**< flag bits */ int clear; /**< True to remove the flag */ char *flag; /**< flag name */ }; static int af_helper(dbref player, dbref thing, dbref parent __attribute__ ((__unused__)), char const *pattern __attribute__ ((__unused__)), ATTR *atr, void *args) { struct af_args *af = args; /* We must be able to write to that attribute normally, * to prevent players from doing funky things to, say, LAST. * There is one special case - the resetting of the SAFE flag. */ if (!(Can_Write_Attr(player, thing, AL_ATTR(atr)) || (af->clear && (af->f & AF_SAFE) && Can_Write_Attr_Ignore_Safe(player, thing, AL_ATTR(atr))))) { notify_format(player, T("You cannot change that flag on %s/%s"), Name(thing), AL_NAME(atr)); return 0; } if (af->clear) { AL_FLAGS(atr) &= ~af->f; if (!AreQuiet(player, thing)) notify_format(player, T("%s/%s - %s reset."), Name(thing), AL_NAME(atr), af->flag); } else { AL_FLAGS(atr) |= af->f; if (!AreQuiet(player, thing)) notify_format(player, T("%s/%s - %s set."), Name(thing), AL_NAME(atr), af->flag); } return 1; } static void copy_attrib_flags(dbref player, dbref target, ATTR *atr, int flags) { if (!atr) return; if (!Can_Write_Attr(player, target, AL_ATTR(atr))) { notify_format(player, T("You cannot set attrib flags on %s/%s"), Name(target), AL_NAME(atr)); return; } AL_FLAGS(atr) = flags; } /** Set a flag on an attribute. * \param player the enactor. * \param obj the name of the object with the attribute. * \param atrname the name of the attribute. * \param flag the name of the flag to set or clear. */ void do_attrib_flags(dbref player, const char *obj, const char *atrname, const char *flag) { struct af_args af; dbref thing; const char *p; if ((thing = match_controlled(player, obj)) == NOTHING) return; if (!flag || !*flag) { notify(player, T("What flag do you want to set?")); return; } af.clear = 0; /* move past NOT token if there is one */ for (p = flag; *p && ((*p == NOT_TOKEN) || isspace((unsigned char) *p)); p++) if (*p == NOT_TOKEN) af.clear = !af.clear; if ((af.f = string_to_atrflag(player, p)) < 0) { notify(player, T("Unrecognized attribute flag.")); return; } af.flag = mush_strdup(atrflag_to_string(af.f), "af_flag list"); if (!atr_iter_get(player, thing, atrname, 0, af_helper, &af)) notify(player, T("No attribute found to change.")); mush_free((Malloc_t) af.flag, "af_flag list"); } /** Set a flag, attribute flag, or attribute. * \verbatim * This implements @set. * \endverbatim * \param player the enactor. * \param name the first (left) argument to the command. * \param flag the second (right) argument to the command. * \retval 1 successful set. * \retval 0 failure to set. */ int do_set(dbref player, const char *name, char *flag) { dbref thing; int her, listener, negate; char *p, *f; char flagbuff[BUFFER_LEN]; /* check for attribute flag set first */ if ((p = strchr(name, '/')) != NULL) { *p++ = '\0'; do_attrib_flags(player, name, p, flag); return 1; } /* find thing */ if ((thing = match_controlled(player, name)) == NOTHING) return 0; if (God(thing) && !God(player)) { notify(player, T("Only God can set himself!")); return 0; } /* check for attribute set first */ if ((p = strchr(flag, ':')) != NULL) { *p++ = '\0'; if (!command_check_byname(player, "ATTRIB_SET")) { notify(player, T("You may not set attributes.")); return 0; } return do_set_atr(thing, flag, p, player, 1); } /* we haven't set an attribute, so we must be setting flags */ strcpy(flagbuff, flag); p = trim_space_sep(flagbuff, ' '); if (*p == '\0') { notify(player, T("You must specify a flag to set.")); return 0; } do { her = Hearer(thing); /* Must be in loop, can change! */ listener = Listener(thing); /* Must be in loop, can change! */ f = split_token(&p, ' '); negate = 0; if (*f == NOT_TOKEN && *(f + 1)) { negate = 1; f++; } set_flag(player, thing, f, negate, her, listener); } while (p); return 1; } /** Copy or move an attribute. * \verbatim * This implements @cpattr and @mvattr. * the command is of the format: * @cpattr oldobj/oldattr = newobj1/newattr1, newobj2/newattr2, etc. * \endverbatim * \param player the enactor. * \param oldpair the obj/attribute pair to copy from. * \param newpair array of obj/attribute pairs to copy to. * \param move if 1, move rather than copy. * \param noflagcopy if 1, don't copy associated flags. */ void do_cpattr(dbref player, char *oldpair, char **newpair, int move, int noflagcopy) { dbref oldobj, newobj; char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN]; int i; char *p, *q; ATTR *a; char *text; int copies = 0; /* must copy from something */ if (!oldpair || !*oldpair) { notify(player, T("What do you want to copy from?")); return; } /* find the old object */ strcpy(tbuf1, oldpair); p = strchr(tbuf1, '/'); if (!p || !*p) { notify(player, T("What object do you want to copy the attribute from?")); return; } *p++ = '\0'; oldobj = noisy_match_result(player, tbuf1, NOTYPE, MAT_EVERYTHING); if (!GoodObject(oldobj)) return; strcpy(tbuf2, p); p = tbuf2; /* find the old attribute */ a = atr_get_noparent(oldobj, strupper(p)); if (!a) { notify(player, T("No such attribute to copy from.")); return; } /* check permissions to get it */ if (!Can_Read_Attr(player, oldobj, a)) { notify(player, T("Permission to read attribute denied.")); return; } /* we can read it. Copy the value. */ text = safe_atr_value(a); /* now we loop through our new object pairs and copy, calling @set. */ for (i = 1; i < MAX_ARG && (newpair[i] != NULL); i++) { if (!*newpair[i]) { notify(player, T("What do you want to copy to?")); } else { strcpy(tbuf1, newpair[i]); q = strchr(tbuf1, '/'); if (!q || !*q) { q = (char *) AL_NAME(a); } else { *q++ = '\0'; } newobj = noisy_match_result(player, tbuf1, NOTYPE, MAT_EVERYTHING); if (GoodObject(newobj) && ((newobj != oldobj) || strcasecmp(AL_NAME(a), q)) && do_set_atr(newobj, q, text, player, 1)) copies++; /* copy the attribute flags too */ if (!noflagcopy) copy_attrib_flags(player, newobj, atr_get_noparent(newobj, strupper(q)), a->flags); } } free((Malloc_t) text); /* safe_uncompress malloc()s memory */ if (copies) { notify_format(player, T("Attribute %s (%d copies)"), (move ? "moved" : "copied"), copies); if (move) do_set_atr(oldobj, AL_NAME(a), NULL, player, 1); } else { notify_format(player, T("Unable to %s attribute."), (move ? "move" : "copy")); } return; } /** Argument struct for gedit_helper */ struct gedit_args { enum edit_type target; /**< The type of edit */ char *from; /**< What is going to be replaced? */ char *to; /**< What it gets replaced with. */ }; static int gedit_helper(dbref player, dbref thing, dbref parent __attribute__ ((__unused__)), char const *pattern __attribute__ ((__unused__)), ATTR *a, void *args) { int ansi_long_flag = 0; const char *r; char *s, *val; char tbuf1[BUFFER_LEN], tbuf_ansi[BUFFER_LEN]; char *tbufp, *tbufap; size_t rlen, vlen; struct gedit_args *gargs; gargs = args; val = gargs->from; vlen = strlen(val); r = gargs->to ? gargs->to : ""; rlen = strlen(r); tbufp = tbuf1; tbufap = tbuf_ansi; if (!a) { /* Shouldn't ever happen, but better safe than sorry */ notify(player, T("No such attribute, try set instead.")); return 0; } if (!Can_Write_Attr(player, thing, a)) { notify(player, T("You need to control an attribute to edit it.")); return 0; } s = (char *) atr_value(a); /* warning: pointer to static buffer */ if (vlen == 1 && *val == '$') { /* append */ safe_str(s, tbuf1, &tbufp); safe_str(r, tbuf1, &tbufp); if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", s, ANSI_HILITE, r, ANSI_NORMAL)) ansi_long_flag = 1; } else if (vlen == 1 && *val == '^') { /* prepend */ safe_str(r, tbuf1, &tbufp); safe_str(s, tbuf1, &tbufp); if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", ANSI_HILITE, r, ANSI_NORMAL, s)) ansi_long_flag = 1; } else if (!*val) { /* insert replacement string between every character */ ansi_string *haystack; size_t last = 0; int too_long = 0; haystack = parse_ansi_string(s); /* Add one at the start */ if (!safe_strl(r, rlen, tbuf1, &tbufp)) { if (gargs->target != EDIT_FIRST) { for (last = 0; last < haystack->len; last++) { /* Add the next character */ if (safe_ansi_string(haystack, last, 1, tbuf1, &tbufp)) { too_long = 1; break; } if (!ansi_long_flag) { if (safe_ansi_string(haystack, last, 1, tbuf_ansi, &tbufap)) ansi_long_flag = 1; } /* Copy in r */ if (safe_strl(r, rlen, tbuf1, &tbufp)) { too_long = 1; break; } if (!ansi_long_flag) { if (safe_format(tbuf_ansi, &tbufap, "%s%s%s", ANSI_HILITE, r, ANSI_NORMAL)) ansi_long_flag = 1; } } } } free_ansi_string(haystack); } else { /* find and replace */ ansi_string *haystack; size_t last = 0; char *p; int too_long = 0; haystack = parse_ansi_string(s); while (last < haystack->len && (p = strstr(haystack->text + last, val)) != NULL) { if (safe_ansi_string(haystack, last, p - (haystack->text + last), tbuf1, &tbufp)) { too_long = 1; break; } if (!ansi_long_flag) { if (safe_ansi_string(haystack, last, p - (haystack->text + last), tbuf_ansi, &tbufap)) ansi_long_flag = 1; } /* Copy in r */ if (safe_strl(r, rlen, tbuf1, &tbufp)) { too_long = 1; break; } if (!ansi_long_flag) { if (safe_format(tbuf_ansi, &tbufap, "%s%s%s", ANSI_HILITE, r, ANSI_NORMAL)) ansi_long_flag = 1; } last = p - haystack->text + vlen; if (gargs->target == EDIT_FIRST) break; } if (last < haystack->len && !too_long) { safe_ansi_string(haystack, last, haystack->len, tbuf1, &tbufp); if (!ansi_long_flag) { if (safe_ansi_string(haystack, last, haystack->len, tbuf_ansi, &tbufap)) ansi_long_flag = 1; } } free_ansi_string(haystack); } *tbufp = '\0'; *tbufap = '\0'; if (do_set_atr(thing, AL_NAME(a), tbuf1, player, 0) && !AreQuiet(player, thing)) { if (!ansi_long_flag && ShowAnsi(player)) notify_format(player, "%s - Set: %s", AL_NAME(a), tbuf_ansi); else notify_format(player, "%s - Set: %s", AL_NAME(a), tbuf1); } return 1; } /** Edit an attribute. * \verbatim * This implements @edit obj/attribute = {search}, {replace} * \endverbatim * \param player the enactor. * \param it the object/attribute pair. * \param argv array containing the search and replace strings. */ void do_gedit(dbref player, char *it, char **argv, enum edit_type target) { dbref thing; char tbuf1[BUFFER_LEN]; char *q; struct gedit_args args; if (!(it && *it)) { notify(player, T("I need to know what you want to edit.")); return; } strcpy(tbuf1, it); q = strchr(tbuf1, '/'); if (!(q && *q)) { notify(player, T("I need to know what you want to edit.")); return; } *q++ = '\0'; thing = noisy_match_result(player, tbuf1, NOTYPE, MAT_EVERYTHING); if ((thing == NOTHING) || !controls(player, thing)) { notify(player, T("Permission denied.")); return; } if (!argv[1] || !*argv[1]) { notify(player, T("Nothing to do.")); return; } args.from = argv[1]; args.to = argv[2]; args.target = target; if (!atr_iter_get(player, thing, q, 0, gedit_helper, &args)) notify(player, T("No matching attributes.")); } /** Trigger an attribute. * \verbatim * This implements @trigger obj/attribute = list-of-arguments. * \endverbatim * \param player the enactor. * \param object the object/attribute pair. * \param argv array of arguments. */ void do_trigger(dbref player, char *object, char **argv) { dbref thing; int a; char *s; char tbuf1[BUFFER_LEN]; strcpy(tbuf1, object); for (s = tbuf1; *s && (*s != '/'); s++) ; if (!*s) { notify(player, T("I need to know what attribute to trigger.")); return; } *s++ = '\0'; thing = noisy_match_result(player, tbuf1, NOTYPE, MAT_EVERYTHING); if (thing == NOTHING) return; if (!controls(player, thing) && !(Owns(player, thing) && LinkOk(thing))) { notify(player, T("Permission denied.")); return; } if (God(thing) && !God(player)) { notify(player, T("You can't trigger God!")); return; } /* trigger modifies the stack */ for (a = 0; a < 10; a++) global_eval_context.wnxt[a] = argv[a + 1]; if (charge_action(player, thing, upcasestr(s))) { if (!AreQuiet(player, thing)) notify_format(player, "%s - Triggered.", Name(thing)); } else { notify(player, T("No such attribute.")); } } /** The use command. * It's here for lack of a better place. * \param player the enactor. * \param what name of the object to use. */ void do_use(dbref player, const char *what) { dbref thing; /* if we pass the use key, do it */ if ((thing = noisy_match_result(player, what, TYPE_THING, MAT_NEAR_THINGS)) != NOTHING) { if (!eval_lock(player, thing, Use_Lock)) { fail_lock(player, thing, Use_Lock, T("Permission denied."), NOTHING); return; } else did_it(player, thing, "USE", "Used.", "OUSE", NULL, "AUSE", NOTHING); } } /** Parent an object to another. * \verbatim * This implements @parent. * \endverbatim * \param player the enactor. * \param name the name of the child object. * \param parent_name the name of the new parent object. */ void do_parent(dbref player, char *name, char *parent_name) { dbref thing; dbref parent; dbref check; int i; if ((thing = noisy_match_result(player, name, NOTYPE, MAT_NEARBY)) == NOTHING) return; if (!parent_name || !*parent_name || !strcasecmp(parent_name, "none")) parent = NOTHING; else if ((parent = noisy_match_result(player, parent_name, NOTYPE, MAT_EVERYTHING)) == NOTHING) return; /* do control check */ if (!controls(player, thing) && !(Owns(player, thing) && LinkOk(thing))) { notify(player, T("Permission denied.")); return; } /* a player may change an object's parent to NOTHING or to an * object he owns, or one that is LINK_OK when the player passes * the parent lock * mod: also when the player controls the parent, it passes the parent lock * [removed owner and wizard check and added * control check (wich does those things * anyway, right?)] */ if ((parent != NOTHING) && !controls(player, parent) && !(LinkOk(parent) && eval_lock(player, parent, Parent_Lock))) { notify(player, T("Permission denied.")); return; } /* check to make sure no recursion can happen */ if (parent == thing) { notify(player, T("A thing cannot be its own ancestor!")); return; } if (parent != NOTHING) { for (i = 0, check = Parent(parent); (i < MAX_PARENTS) && (check != NOTHING); i++, check = Parent(check)) { if (check == thing) { notify(player, T("You are not allowed to be your own ancestor!")); return; } } if (i >= MAX_PARENTS) { notify(player, T("Too many ancestors.")); return; } } /* everything is okay, do the change */ Parent(thing) = parent; if (!AreQuiet(player, thing)) notify(player, T("Parent changed.")); } static int wipe_helper(dbref player, dbref thing, dbref parent __attribute__ ((__unused__)), char const *pattern, ATTR *atr, void *args __attribute__ ((__unused__))) { /* for added security, only God can modify wiz-only-modifiable * attributes using this command and wildcards. Wiping a specific * attr still works, though. */ if (wildcard(pattern) && AF_Wizard(atr) && !God(player)) return 0; return do_set_atr(thing, AL_NAME(atr), NULL, player, 0) == 1; } /** Clear an attribute. * \verbatim * This implements @wipe. * \endverbatim * \param player the enactor. * \param name the object/attribute-pattern to wipe. */ void do_wipe(dbref player, char *name) { dbref thing; char *pattern; if ((pattern = strchr(name, '/')) != NULL) *pattern++ = '\0'; if ((thing = noisy_match_result(player, name, NOTYPE, MAT_NEARBY)) == NOTHING) return; /* this is too destructive of a command to be used by someone who * doesn't own the object. Thus, the check is on Owns not controls. */ if (!Wizard(player) && !Owns(player, thing)) { notify(player, T("Permission denied.")); return; } /* protect SAFE objects unless doing a non-wildcard pattern */ if (Safe(thing) && !(pattern && *pattern && !wildcard(pattern))) { notify(player, T("That object is protected.")); return; } we_are_wiping = 1; if (!atr_iter_get(player, thing, pattern, 0, wipe_helper, NULL)) notify(player, T("No attributes wiped.")); else notify(player, T("Attributes wiped.")); we_are_wiping = 0; }