tinymush-3.1p1/game/backups/
tinymush-3.1p1/game/bin/
tinymush-3.1p1/game/data/
tinymush-3.1p1/game/modules/
tinymush-3.1p1/game/modules/old/
tinymush-3.1p1/src/modules/comsys/
tinymush-3.1p1/src/modules/hello/
tinymush-3.1p1/src/modules/mail/
tinymush-3.1p1/src/tools/
/* set.c - commands which set parameters */
/* $Id: set.c,v 1.49 2003/02/24 18:05:23 rmg Exp $ */

#include "copyright.h"
#include "autoconf.h"
#include "config.h"

#include "alloc.h"	/* required by mudconf */
#include "flags.h"	/* required by mudconf */
#include "htab.h"	/* required by mudconf */
#include "mudconf.h"	/* required by code */

#include "db.h"		/* required by externs */
#include "externs.h"	/* required by code */

#include "match.h"	/* required by code */
#include "powers.h"	/* required by code */
#include "attrs.h"	/* required by code */
#include "ansi.h"	/* required by code */

extern NAMETAB indiv_attraccess_nametab[];

dbref match_controlled(player, name)
dbref player;
const char *name;
{
	dbref mat;

	init_match(player, name, NOTYPE);
	match_everything(MAT_EXIT_PARENTS);
	mat = noisy_match_result();
	if (Good_obj(mat) && !Controls(player, mat)) {
		notify_quiet(player, NOPERM_MESSAGE);
		return NOTHING;
	} else {
		return (mat);
	}
}

dbref match_controlled_quiet(player, name)
dbref player;
const char *name;
{
	dbref mat;

	init_match(player, name, NOTYPE);
	match_everything(MAT_EXIT_PARENTS);
	mat = match_result();
	if (Good_obj(mat) && !Controls(player, mat)) {
		return NOTHING;
	} else {
		return (mat);
	}
}

void do_chzone(player, cause, key, name, newobj)
dbref player, cause;
int key;
const char *name;
const char *newobj;
{
	dbref thing;
	dbref zone;

	if (!mudconf.have_zones) {
		notify(player, "Zones disabled.");
		return;
	}
	init_match(player, name, NOTYPE);
	match_everything(0);
	if ((thing = noisy_match_result()) == NOTHING)
		return;

	if (!newobj || !*newobj || !strcasecmp(newobj, "none"))
		zone = NOTHING;
	else {
		init_match(player, newobj, NOTYPE);
		match_everything(0);
		if ((zone = noisy_match_result()) == NOTHING)
			return;

		if ((Typeof(zone) != TYPE_THING) && (Typeof(zone) != TYPE_ROOM)) {
			notify(player, "Invalid zone object type.");
			return;
		}
	}

	if (!Wizard(player) && !(Controls(player, thing)) &&
	    !(check_zone_for_player(player, thing)) &&
	    !(db[player].owner == db[thing].owner)) {
		notify(player, "You don't have the power to shift reality.");
		return;
	}

	/* a player may change an object's zone to NOTHING or to an object he 
	 * owns 
	 */
	if ((zone != NOTHING) && !Wizard(player) &&
	    !(Controls(player, zone)) &&
	    !(db[player].owner == db[zone].owner)) {
		notify(player, "You cannot move that object to that zone.");
		return;
	}

	/* only rooms may be zoned to other rooms */

	if ((zone != NOTHING) &&
	    (Typeof(zone) == TYPE_ROOM) && Typeof(thing) != TYPE_ROOM) {
		notify(player, "Only rooms may have parent rooms.");
		return;
	}

	/* everything is okay, do the change */

	s_Zone(thing, zone);
	if (Typeof(thing) != TYPE_PLAYER) {
	    /* We do not strip flags and powers on players, due to the
	     * inconvenience involved in resetting them. Players will just
	     * have to be careful when @chzone'ing players with special
	     * privileges.
	     * For all other objects, we behave like @chown does.
	     */
	    if (key & CHZONE_NOSTRIP) {
		if (!God(player))
		    s_Flags(thing, Flags(thing) & ~WIZARD);
	    } else {
		s_Flags(thing, Flags(thing) & ~mudconf.stripped_flags.word1);
		s_Flags2(thing, Flags2(thing) & ~mudconf.stripped_flags.word2);
		s_Flags3(thing, Flags3(thing) & ~mudconf.stripped_flags.word3);
	    }
	    if (!(key & CHZONE_NOSTRIP) || !God(player)) {
		s_Powers(thing, 0);
		s_Powers2(thing, 0);
	    }
	}
	notify(player, "Zone changed.");
	s_Modified(thing);
}

void do_name(player, cause, key, name, newname)
dbref player, cause;
int key;
const char *name;
char *newname;
{
	dbref thing;
	char *buff;

	if ((thing = match_controlled(player, name)) == NOTHING)
		return;

	/*
	 * check for bad name 
	 */
	if ((*newname == '\0') || (strip_ansi_len(newname) == 0)) {
		notify_quiet(player, "Give it what new name?");
		return;
	}
	/*
	 * check for renaming a player 
	 */
	if (isPlayer(thing)) {

		buff = trim_spaces((char *)newname);
		if (!ok_player_name(buff) ||
		    !badname_check(buff)) {
			notify_quiet(player, "You can't use that name.");
			free_lbuf(buff);
			return;
		} else if (string_compare(buff, Name(thing)) &&
			   (lookup_player(NOTHING, buff, 0) != NOTHING)) {

			/*
			 * string_compare allows changing foo to Foo, etc. 
			 */

			notify_quiet(player, "That name is already in use.");
			free_lbuf(buff);
			return;
		}
		/*
		 * everything ok, notify 
		 */
		STARTLOG(LOG_SECURITY, "SEC", "CNAME")
			log_name(thing),
			log_printf(" renamed to %s", buff);
		ENDLOG
		if (Suspect(thing)) {
			raw_broadcast(WIZARD,
			   "[Suspect] %s renamed to %s", Name(thing), buff);
		}
		delete_player_name(thing, Name(thing));
		s_Name(thing, buff);
		add_player_name(thing, Name(thing));
		if (!Quiet(player) && !Quiet(thing))
			notify_quiet(player, "Name set.");
		free_lbuf(buff);
		s_Modified(thing);
		return;
	} else {
		if (!ok_name(newname)) {
			notify_quiet(player, "That is not a reasonable name.");
			return;
		}
		/*
		 * everything ok, change the name 
		 */
		s_Name(thing, newname);
		if (!Quiet(player) && !Quiet(thing))
			notify_quiet(player, "Name set.");
		s_Modified(thing);
	}
}

/*
 * ---------------------------------------------------------------------------
 * * do_alias: Make an alias for a player or object.
 */

static void set_player_aliases(player, target, oldalias, list, aflags)
dbref player, target;
char *oldalias, *list;
int aflags;
{
    int i, j, n_aliases, retcode;
    char *p, *tokp;
    char alias_buf[LBUF_SIZE], tmp_buf[LBUF_SIZE], *alias_ptrs[LBUF_SIZE / 2];


    /* Clear out the original alias, so we can rewrite a new alias
     * that uses the same names, if necessary.
     */

    Clear_Player_Aliases(target, oldalias);

    /* Don't nibble the original buffer. Copy it all into an array, because
     * we have to eat leading and trailing spaces.
     */

    retcode = 1;
    strcpy(tmp_buf, list);
    for (n_aliases = 0, p = strtok_r(tmp_buf, ";", &tokp); p;
	 n_aliases++, p = strtok_r(NULL, ";", &tokp)) {
	alias_ptrs[n_aliases] = trim_spaces(p);
    }

    /* Enforce a maximum number of aliases. */

    if (n_aliases > mudconf.max_player_aliases) {
	notify_quiet(player,
		     tprintf("You cannot have more than %d aliases.",
			     mudconf.max_player_aliases));
	retcode = 0;
    }

    /* Enforce player name regulations. */
	
    for (i = 0; retcode && (i < n_aliases); i++) {
	if (lookup_player(NOTHING, alias_ptrs[i], 0) != NOTHING) {
	    notify_quiet(player,
			 tprintf("The name '%s' is already in use.",
				 alias_ptrs[i]));
	    retcode = 0;
	} else if (!(badname_check(alias_ptrs[i]) &&
		     ok_player_name(alias_ptrs[i]))) {
	    notify_quiet(player,
			 tprintf("You cannot use '%s' as an alias.",
				 alias_ptrs[i]));
	    retcode = 0;
	} else {

	    /* Make sure this alias doesn't duplicate another in the list. */

	    for (j = i + 1; retcode && (j < n_aliases); j++) {
		if (!strcasecmp(alias_ptrs[i], alias_ptrs[j])) {
		    notify_quiet(player, 
		      tprintf("You have duplicated '%s' in your alias list.",
			      alias_ptrs[i]));
		    retcode = 0;
		}
	    }
	}
    }

    /* Construct a new alias list, with spaces removed. */

    for (i = 0, p = alias_buf; retcode && (i < n_aliases); i++) {
	if (add_player_name(target, alias_ptrs[i])) {
	    if (p != alias_buf) {
		safe_chr(';', alias_buf, &p);
	    }
	    safe_str(alias_ptrs[i], alias_buf, &p);
	} else {
	    
	    retcode = 0;
	    notify_quiet(player,
	        tprintf("The alias '%s' is already in use or is illegal.",
			alias_ptrs[i]));

	    /* Ugh. Now we have to delete aliases we added up 'til now. */

	    for (j = 0; j < i; j++)
		delete_player_name(target, alias_ptrs[j]);
	}
    }

    /* Free memory allocated by trim_spaces(). */

    for (i = 0; i < n_aliases; i++) {
	free_lbuf(alias_ptrs[i]);
    }

    /* Twiddle the alias attribute on the object. Note that we have to
     * do this regardless of the outcome, since we wiped out the original
     * aliases from the player name table earlier.
     */

    if (retcode) {
	atr_add(target, A_ALIAS, alias_buf, Owner(player), aflags);
	if (!Quiet(player)) {
	    notify_quiet(player, "Alias set.");
	}
    } else {
	atr_clr(target, A_ALIAS);
	notify_quiet(player, "Alias cleared due to error.");
    }
}

void do_alias(player, cause, key, name, alias)
dbref player, cause;
int key;
char *name, *alias;
{
	dbref thing, aowner;
	int aflags, alen;
	ATTR *ap;
	char *oldalias, *trimalias;

	if ((thing = match_controlled(player, name)) == NOTHING)
		return;

	/*
	 * check for renaming a player 
	 */

	ap = atr_num(A_ALIAS);
	if (isPlayer(thing)) {

		/*
		 * Fetch the old alias 
		 */

		oldalias = atr_get(thing, A_ALIAS, &aowner, &aflags, &alen);
		trimalias = trim_spaces(alias);

		if (!Controls(player, thing)) {

			/*
			 * Make sure we have rights to do it.  We can't do
			 * the normal Set_attr check because ALIAS is
			 * set CONSTANT and we want to keep people from
			 * doing &ALIAS and bypassing the player name checks.
			 */

			notify_quiet(player, NOPERM_MESSAGE);
		} else if (!*trimalias) {

			/*
			 * New alias is null, just clear it 
			 */

			Clear_Player_Aliases(thing, oldalias);
			atr_clr(thing, A_ALIAS);
			if (!Quiet(player))
				notify_quiet(player, "Alias removed.");
		} else {

			/*
			 * Remove the old name and add the new name 
			 */

			set_player_aliases(player, thing, oldalias,
					   trimalias, aflags);
		}
		free_lbuf(trimalias);
		free_lbuf(oldalias);
	} else {
		atr_pget_info(thing, A_ALIAS, &aowner, &aflags);

		/*
		 * Make sure we have rights to do it 
		 */

		if (!Set_attr(player, thing, ap, aflags)) {
			notify_quiet(player, NOPERM_MESSAGE);
		} else {
			atr_add(thing, A_ALIAS, alias, Owner(player), aflags);
			if (!Quiet(player))
				notify_quiet(player, "Set.");
		}
	}
}

/*
 * ---------------------------------------------------------------------------
 * * do_lock: Set a lock on an object or attribute.
 */

void do_lock(player, cause, key, name, keytext)
dbref player, cause;
int key;
char *name, *keytext;
{
	dbref thing, aowner;
	int atr, aflags;
	ATTR *ap;
	struct boolexp *okey;

	if (parse_attrib(player, name, &thing, &atr, 0)) {
		if (atr != NOTHING) {
			if (!atr_get_info(thing, atr, &aowner, &aflags)) {
				notify_quiet(player,
					"Attribute not present on object.");
				return;
			}
			ap = atr_num(atr);

			if (ap && Lock_attr(player, thing, ap, aowner)) {
				aflags |= AF_LOCK;
				atr_set_flags(thing, atr, aflags);
				if (!Quiet(player) && !Quiet(thing))
					notify_quiet(player,
						     "Attribute locked.");
			} else {
				notify_quiet(player, NOPERM_MESSAGE);
			}
			return;
		}
	}
	init_match(player, name, NOTYPE);
	match_everything(MAT_EXIT_PARENTS);
	thing = match_result();

	switch (thing) {
	case NOTHING:
		notify_quiet(player,
			     "I don't see what you want to lock!");
		return;
	case AMBIGUOUS:
		notify_quiet(player,
			     "I don't know which one you want to lock!");
		return;
	default:
		if (!controls(player, thing)) {
			notify_quiet(player, "You can't lock that!");
			return;
		}
	}

	okey = parse_boolexp(player, keytext, 0);
	if (okey == TRUE_BOOLEXP) {
		notify_quiet(player, "I don't understand that key.");
	} else {

		/*
		 * everything ok, do it 
		 */

		if (!key)
			key = A_LOCK;
		atr_add_raw(thing, key, unparse_boolexp_quiet(player, okey));
		if (key == A_LDARK)
			s_Has_Darklock(thing);
		if (!Quiet(player) && !Quiet(thing))
			notify_quiet(player, "Locked.");
	}
	free_boolexp(okey);
}

/*
 * ---------------------------------------------------------------------------
 * * Remove a lock from an object of attribute.
 */

void do_unlock(player, cause, key, name)
dbref player, cause;
int key;
char *name;
{
	dbref thing, aowner;
	int atr, aflags;
	ATTR *ap;

	if (parse_attrib(player, name, &thing, &atr, 0)) {
		if (atr != NOTHING) {
			if (!atr_get_info(thing, atr, &aowner, &aflags)) {
				notify_quiet(player,
					"Attribute not present on object.");
				return;
			}
			ap = atr_num(atr);

			if (ap && Lock_attr(player, thing, ap, aowner)) {
				aflags &= ~AF_LOCK;
				atr_set_flags(thing, atr, aflags);
				if (Owner(player != Owner(thing)))
					if (!Quiet(player) && !Quiet(thing))
						notify_quiet(player,
						     "Attribute unlocked.");
			} else {
				notify_quiet(player, NOPERM_MESSAGE);
			}
			return;
		}
	}
	if (!key)
		key = A_LOCK;
	if ((thing = match_controlled(player, name)) != NOTHING) {
		atr_clr(thing, key);
		if (key == A_LDARK)
			c_Has_Darklock(thing);
		if (!Quiet(player) && !Quiet(thing))
			notify_quiet(player, "Unlocked.");
	}
}

/*
 * ---------------------------------------------------------------------------
 * * do_unlink: Unlink an exit from its destination or remove a dropto.
 */

void do_unlink(player, cause, key, name)
dbref player, cause;
int key;
char *name;
{
	dbref exit;

	init_match(player, name, TYPE_EXIT);
	match_everything(0);
	exit = match_result();

	switch (exit) {
	case NOTHING:
		notify_quiet(player, "Unlink what?");
		break;
	case AMBIGUOUS:
		notify_quiet(player, AMBIGUOUS_MESSAGE);
		break;
	default:
		if (!controls(player, exit)) {
			notify_quiet(player, NOPERM_MESSAGE);
		} else {
			switch (Typeof(exit)) {
			case TYPE_EXIT:
				s_Location(exit, NOTHING);
				if (!Quiet(player))
					notify_quiet(player, "Unlinked.");
				break;
			case TYPE_ROOM:
				s_Dropto(exit, NOTHING);
				if (!Quiet(player))
					notify_quiet(player,
						     "Dropto removed.");
				break;
			default:
				notify_quiet(player, "You can't unlink that!");
				break;
			}
		}
	}
}

/* ---------------------------------------------------------------------------
 * do_chown: Change ownership of an object or attribute.
 */

void do_chown(player, cause, key, name, newown)
dbref player, cause;
int key;
char *name, *newown;
{
	dbref thing, owner, aowner;
	int atr, aflags, do_it, cost, quota;
	ATTR *ap;

	if (parse_attrib(player, name, &thing, &atr, 1)) {
		if (atr != NOTHING) {
			if (!*newown) {
				owner = Owner(thing);
			} else if (!(string_compare(newown, "me"))) {
				owner = Owner(player);
			} else {
				owner = lookup_player(player, newown, 1);
			}

			/*
			 * You may chown an attr to yourself if you
			 * own the object and the attr is not locked.
			 * You may chown an attr to the owner of the
			 * object if you own the attribute. To do
			 * anything else you must be a wizard. Only
			 * #1 can chown attributes on #1.
			 */

			if (!atr_get_info(thing, atr, &aowner, &aflags)) {
				notify_quiet(player,
					"Attribute not present on object.");
				return;
			}
			do_it = 0;
			if (owner == NOTHING) {
				notify_quiet(player,
					     "I couldn't find that player.");
			} else if (God(thing) && !God(player)) {
				notify_quiet(player, NOPERM_MESSAGE);
			} else if (Wizard(player)) {
				do_it = 1;
			} else if (owner == Owner(player)) {

				/*
				 * chown to me: only if I own the obj
				 * and !locked 
				 */

				if (!Controls(player, thing) ||
				    (aflags & AF_LOCK)) {
					notify_quiet(player,
						     NOPERM_MESSAGE);
				} else {
					do_it = 1;
				}
			} else if (owner == Owner(thing)) {

				/*
				 * chown to obj owner: only if I own attr
				 * and !locked 
				 */

				if ((Owner(player) != aowner) ||
				    (aflags & AF_LOCK)) {
					notify_quiet(player,
						     NOPERM_MESSAGE);
				} else {
					do_it = 1;
				}
			} else {
				notify_quiet(player, NOPERM_MESSAGE);
			}

			if (!do_it)
				return;

			ap = atr_num(atr);
			if (!ap || !Set_attr(player, player, ap, aflags)) {
				notify_quiet(player, NOPERM_MESSAGE);
				return;
			}
			atr_set_owner(thing, atr, owner);
			if (!Quiet(player))
				notify_quiet(player,
					     "Attribute owner changed.");
			s_Modified(thing);
			return;
		}
	}
	init_match(player, name, TYPE_THING);
	match_possession();
	match_here();
	match_exit();
	match_me();
	if (Chown_Any(player)) {
		match_player();
		match_absolute();
	}
	switch (thing = match_result()) {
	case NOTHING:
		notify_quiet(player, "You don't have that!");
		return;
	case AMBIGUOUS:
		notify_quiet(player, "I don't know which you mean!");
		return;
	}

	if (!*newown || !(string_compare(newown, "me"))) {
		owner = Owner(player);
	} else {
		owner = lookup_player(player, newown, 1);
	}

	cost = 1;
	quota = 1;
	switch (Typeof(thing)) {
	case TYPE_ROOM:
		cost = mudconf.digcost;
		quota = mudconf.room_quota;
		break;
	case TYPE_THING:
		cost = OBJECT_DEPOSIT(Pennies(thing));
		quota = mudconf.thing_quota;
		break;
	case TYPE_EXIT:
		cost = mudconf.opencost;
		quota = mudconf.exit_quota;
		break;
	case TYPE_PLAYER:
		cost = mudconf.robotcost;
		quota = mudconf.player_quota;
	}

	if (owner == NOTHING) {
		notify_quiet(player, "I couldn't find that player.");
	} else if (isPlayer(thing) && !God(player)) {
		notify_quiet(player, "Players always own themselves.");
	} else if (((!controls(player, thing) && !Chown_Any(player) &&
		     !(Chown_ok(thing) &&
		       could_doit(player, thing, A_LCHOWN))) ||
		    (isThing(thing) && (Location(thing) != player) &&
		     !Chown_Any(player))) ||
		   (!controls(player, owner) && !Chown_Any(player)) ||
		   God(thing)) {
		notify_quiet(player, NOPERM_MESSAGE);
	} else if (canpayfees(player, owner, cost, quota, Typeof(thing))) {
	    payfees(owner, cost, quota, Typeof(thing));
	    payfees(Owner(thing), -cost, -quota, Typeof(thing));
		
	    if (God(player)) {
		s_Owner(thing, owner);
	    } else {
		s_Owner(thing, Owner(owner));
	    }
	    atr_chown(thing);

	    /* If we're not stripping flags, and we're God, don't strip the
	     * WIZARD flag. Otherwise, do that, at least.
	     */
	    if (key & CHOWN_NOSTRIP) {
		if (God(player)) {
		    s_Flags(thing, (Flags(thing) & ~CHOWN_OK) | HALT);
		} else {
		    s_Flags(thing,
			    (Flags(thing) & ~(CHOWN_OK | WIZARD)) | HALT);
		}
	    } else {
		s_Flags(thing,
			(Flags(thing) &
			 ~(CHOWN_OK | mudconf.stripped_flags.word1)) | HALT);
		s_Flags2(thing,
			 (Flags2(thing) & ~(mudconf.stripped_flags.word2)));
		s_Flags3(thing,
			 (Flags3(thing) & ~(mudconf.stripped_flags.word3)));
	    }

	    /* Powers are only preserved by God with nostrip */

	    if (!(key & CHOWN_NOSTRIP) || !God(player)) { 
		s_Powers(thing, 0);
		s_Powers2(thing, 0);
	    }

	    halt_que(NOTHING, thing);
	    if (!Quiet(player))
		notify_quiet(player, "Owner changed.");
	    s_Modified(thing);
	}
}

/*
 * ---------------------------------------------------------------------------
 * * do_set: Set flags or attributes on objects, or flags on attributes.
 */

void set_attr_internal(player, thing, attrnum, attrtext, key, buff, bufc)
dbref player, thing;
int attrnum, key;
char *attrtext, *buff, **bufc;
{
	dbref aowner;
	int aflags, could_hear;
	ATTR *attr;

	attr = atr_num(attrnum);
	atr_pget_info(thing, attrnum, &aowner, &aflags);
	if (attr && Set_attr(player, thing, attr, aflags)) {
		if ((attr->check != NULL) &&
		    (!(*attr->check) (0, player, thing, attrnum, attrtext))) {
		    if (buff) {
			safe_noperm(buff, bufc);
		    }
		    return;
		}
		could_hear = Hearer(thing);
		atr_add(thing, attrnum, attrtext, Owner(player),
			aflags & ~AF_STRUCTURE);
		handle_ears(thing, could_hear, Hearer(thing));
		if (!(key & SET_QUIET) && !Quiet(player) && !Quiet(thing))
			notify_quiet(player, "Set.");
	} else {
	    if (buff) {
		safe_noperm(buff, bufc);
	    } else {
		notify_quiet(player, NOPERM_MESSAGE);
	    }
	}
}

void do_set(player, cause, key, name, flag)
dbref player, cause;
int key;
char *name, *flag;
{
	dbref thing, thing2, aowner;
	char *p, *buff;
	int atr, atr2, aflags, alen, clear, flagvalue, could_hear;
	ATTR *attr, *attr2;

	/*
	 * See if we have the <obj>/<attr> form, which is how you set * * *
	 * attribute * flags. 
	 */

	if (parse_attrib(player, name, &thing, &atr, 1)) {
		if (atr != NOTHING) {

			/*
			 * You must specify a flag name 
			 */

			if (!flag || !*flag) {
				notify_quiet(player,
				      "I don't know what you want to set!");
				return;
			}
			/*
			 * Check for clearing 
			 */

			clear = 0;
			if (*flag == NOT_TOKEN) {
				flag++;
				clear = 1;
			}
			/*
			 * Make sure player specified a valid attribute flag 
			 */

			flagvalue = search_nametab(player,
					    indiv_attraccess_nametab, flag);
			if (flagvalue < 0) {
				notify_quiet(player, "You can't set that!");
				return;
			}
			/*
			 * Make sure the object has the attribute present 
			 */

			if (!atr_get_info(thing, atr, &aowner, &aflags)) {
				notify_quiet(player,
					"Attribute not present on object.");
				return;
			}
			/*
			 * Make sure we can write to the attribute 
			 */

			attr = atr_num(atr);
			if (!attr || !Set_attr(player, thing, attr, aflags)) {
				notify_quiet(player, NOPERM_MESSAGE);
				return;
			}
			/*
			 * Go do it 
			 */

			if (clear)
				aflags &= ~flagvalue;
			else
				aflags |= flagvalue;
			could_hear = Hearer(thing);
			atr_set_flags(thing, atr, aflags);

			/*
			 * Tell the player about it. 
			 */

			handle_ears(thing, could_hear, Hearer(thing));
			if (!(key & SET_QUIET) &&
			    !Quiet(player) && !Quiet(thing)) {
				if (clear)
					notify_quiet(player, "Cleared.");
				else
					notify_quiet(player, "Set.");
			}
			return;
		}
	}
	/*
	 * find thing 
	 */

	if ((thing = match_controlled(player, name)) == NOTHING)
		return;

	/*
	 * check for attribute set first 
	 */
	for (p = flag; *p && (*p != ':'); p++) ;

	if (*p) {
		*p++ = 0;
		atr = mkattr(flag);
		if (atr <= 0) {
			notify_quiet(player, "Couldn't create attribute.");
			return;
		}
		attr = atr_num(atr);
		if (!attr) {
			notify_quiet(player, NOPERM_MESSAGE);
			return;
		}
		atr_get_info(thing, atr, &aowner, &aflags);
		if (!Set_attr(player, thing, attr, aflags)) {
			notify_quiet(player, NOPERM_MESSAGE);
			return;
		}
		buff = alloc_lbuf("do_set");

		/*
		 * check for _ 
		 */
		if (*p == '_') {
			StringCopy(buff, p + 1);
			if (!parse_attrib(player, p + 1, &thing2, &atr2, 0) ||
			    (atr2 == NOTHING)) {
				notify_quiet(player, "No match.");
				free_lbuf(buff);
				return;
			}
			attr2 = atr_num(atr2);
			p = buff;
			atr_pget_str(buff, thing2, atr2, &aowner, &aflags, &alen);

			if (!attr2 ||
			 !See_attr(player, thing2, attr2, aowner, aflags)) {
				notify_quiet(player, NOPERM_MESSAGE);
				free_lbuf(buff);
				return;
			}
		}
		/*
		 * Go set it 
		 */

		set_attr_internal(player, thing, atr, p, key, NULL, NULL);
		free_lbuf(buff);
		return;
	}
	/*
	 * Set or clear a flag 
	 */

	flag_set(thing, player, flag, key);
}

void do_power(player, cause, key, name, flag)
dbref player, cause;
int key;
char *name, *flag;
{
	dbref thing;

	if (!flag || !*flag) {
		notify_quiet(player,
			     "I don't know what you want to set!");
		return;
	}
	/*
	 * find thing 
	 */

	if ((thing = match_controlled(player, name)) == NOTHING)
		return;

	power_set(thing, player, flag, key);
}

void do_setattr(player, cause, attrnum, name, attrtext)
dbref player, cause;
int attrnum;
char *name, *attrtext;
{
	dbref thing;

	init_match(player, name, NOTYPE);
	match_everything(MAT_EXIT_PARENTS);
	thing = noisy_match_result();

	if (thing == NOTHING)
		return;
	set_attr_internal(player, thing, attrnum, attrtext, 0, NULL, NULL);
}



void do_cpattr(player, cause, key, oldpair, newpair, nargs)
    dbref player, cause;
    int key, nargs;
    char *oldpair;
    char *newpair[];
{
    int i, ca, got = 0;
    dbref oldthing;
    char **newthings, **newattrs, *tp;
    ATTR *oldattr;

    if (!*oldpair || !**newpair || !oldpair || !*newpair)
	return;
    if (nargs < 1)
	return;

    /* newpair gets whacked to bits by parse_to(). Do it just once. */

    newthings = (char **) XCALLOC(nargs, sizeof(char *), "do_cpattr.dbrefs");
    newattrs = (char **) XCALLOC(nargs, sizeof(char *), "do_cpattr.attrs");
    for (i = 0; i < nargs; i++) {
	tp = newpair[i];
	newthings[i] = parse_to(&tp, '/', 1);
	newattrs[i] = tp;
    }

    olist_push();
    if (parse_attrib_wild(player,
			  ((strchr(oldpair, '/') == NULL) ?
			   tprintf("me/%s", oldpair) : oldpair),
			  &oldthing, 0, 0, 1, 0)) {
	for (ca = olist_first(); ca != NOTHING; ca = olist_next()) {
	    oldattr = atr_num(ca);
	    if (oldattr) {
		got = 1;
		for (i = 0; i < nargs; i++) {
		    do_set(player, cause, 0, newthings[i],
			   tprintf("%s:_#%d/%s",
				   (newattrs[i] ? newattrs[i] : oldattr->name),
				   oldthing, oldattr->name));
		}
	    }
	}
    }
    if (!got) {
	notify_quiet(player, "No matching attributes found.");
    }
    olist_pop();
    XFREE(newthings, "do_cpattr.dbrefs");
    XFREE(newattrs, "do_cpattr.attrs");
}


void do_mvattr(player, cause, key, what, args, nargs)
dbref player, cause;
int key, nargs;
char *what, *args[];
{
	dbref thing, aowner, axowner;
	ATTR *in_attr, *out_attr;
	int i, anum, in_anum, aflags, alen, axflags, no_delete, num_copied;
	char *astr;

	/*
	 * Make sure we have something to do. 
	 */

	if (nargs < 2) {
		notify_quiet(player, "Nothing to do.");
		return;
	}
	/*
	 * Find and make sure we control the target object. 
	 */

	thing = match_controlled(player, what);
	if (thing == NOTHING)
		return;

	/*
	 * Look up the source attribute.  If it either doesn't exist or isn't
	 * * * * * readable, use an empty string. 
	 */

	in_anum = -1;
	astr = alloc_lbuf("do_mvattr");
	in_attr = atr_str(args[0]);
	if (in_attr == NULL) {
		*astr = '\0';
	} else {
		atr_get_str(astr, thing, in_attr->number, &aowner, &aflags, &alen);
		if (!See_attr(player, thing, in_attr, aowner, aflags)) {
			*astr = '\0';
		} else {
			in_anum = in_attr->number;
		}
	}

	/*
	 * Copy the attribute to each target in turn. 
	 */

	no_delete = 0;
	num_copied = 0;
	for (i = 1; i < nargs; i++) {
		anum = mkattr(args[i]);
		if (anum <= 0) {
			notify_quiet(player,
				     tprintf("%s: That's not a good name for an attribute.",
					     args[i]));
			continue;
		}
		out_attr = atr_num(anum);
		if (!out_attr) {
			notify_quiet(player,
				tprintf("%s: Permission denied.", args[i]));
		} else if (out_attr->number == in_anum) {
			no_delete = 1;
		} else {
			atr_get_info(thing, out_attr->number, &axowner,
				     &axflags);
			if (!Set_attr(player, thing, out_attr, axflags)) {
				notify_quiet(player,
					   tprintf("%s: Permission denied.",
						   args[i]));
			} else {
				atr_add(thing, out_attr->number, astr,
					Owner(player), aflags);
				num_copied++;
				if (!Quiet(player))
					notify_quiet(player,
						     tprintf("%s: Set.",
							   out_attr->name));
			}
		}
	}

	/*
	 * Remove the source attribute if we can. 
	 */

	if (num_copied < 1) {
	    if (in_attr) {
		notify_quiet(player,
			     tprintf("%s: Not copied anywhere. Not cleared.",
				     in_attr->name));
	    } else {
		notify_quiet(player, "Not copied anywhere. Non-existent attribute.");
	    }
	} else if ((in_anum > 0) && !no_delete) {
		in_attr = atr_num(in_anum);
		if (in_attr && Set_attr(player, thing, in_attr, aflags)) {
			atr_clr(thing, in_attr->number);
			if (!Quiet(player))
				notify_quiet(player,
					     tprintf("%s: Cleared.",
						     in_attr->name));
		} else {
		    if (in_attr) {
			notify_quiet(player,
				     tprintf("%s: Could not remove old attribute.  Permission denied.",
					     in_attr->name));
		    } else {
			notify_quiet(player, "Could not remove old attribute. Non-existent attribute.");
		    }
		}
	}
	free_lbuf(astr);
}

/*
 * ---------------------------------------------------------------------------
 * * parse_attrib, parse_attrib_wild: parse <obj>/<attr> tokens.
 */

int parse_attrib(player, str, thing, atr, ok_structs)
dbref player, *thing;
int *atr;
char *str;
int ok_structs;
{
	ATTR *attr;
	char *buff;
	dbref aowner;
	int aflags;

	if (!str)
		return 0;

	/*
	 * Break apart string into obj and attr.  Return on failure 
	 */

	buff = alloc_lbuf("parse_attrib");
	StringCopy(buff, str);
	if (!parse_thing_slash(player, buff, &str, thing)) {
		free_lbuf(buff);
		return 0;
	}
	/*
	 * Get the named attribute from the object if we can 
	 */

	attr = atr_str(str);
	free_lbuf(buff);
	if (!attr) {
		*atr = NOTHING;
		return NOTHING;
	} else {
		atr_pget_info(*thing, attr->number, &aowner, &aflags);
		if (!See_attr_all(player, *thing, attr, aowner, aflags,
				  ok_structs)) {
			*atr = NOTHING;
		} else {
			*atr = attr->number;
		}
	}
	return 1;
}

static void find_wild_attrs(player, thing, str, check_exclude, hash_insert,
			    get_locks, ok_structs)
dbref player, thing;
char *str;
int check_exclude, hash_insert, get_locks, ok_structs;
{
	ATTR *attr;
	char *as;
	dbref aowner;
	int ca, ok, aflags;

	/*
	 * Walk the attribute list of the object 
	 */

	atr_push();
	for (ca = atr_head(thing, &as); ca; ca = atr_next(&as)) {
		attr = atr_num(ca);

		/*
		 * Discard bad attributes and ones we've seen before. 
		 */

		if (!attr)
			continue;

		if (check_exclude &&
		    ((attr->flags & AF_PRIVATE) ||
		     nhashfind(ca, &mudstate.parent_htab)))
			continue;

		/*
		 * If we aren't the top level remember this attr so we * * *
		 * exclude * it in any parents. 
		 */

		atr_get_info(thing, ca, &aowner, &aflags);
		if (check_exclude && (aflags & AF_PRIVATE))
			continue;

		if (get_locks)
			ok = Read_attr_all(player, thing, attr, aowner,
					   aflags, ok_structs);
		else
			ok = See_attr_all(player, thing, attr, aowner,
					  aflags, ok_structs);

		/*
		 * Enforce locality restriction on descriptions 
		 */

		if (ok && (attr->number == A_DESC) && !mudconf.read_rem_desc &&
		    !Examinable(player, thing) && !nearby(player, thing))
			ok = 0;

		if (ok && quick_wild(str, (char *)attr->name)) {
			olist_add(ca);
			if (hash_insert) {
				nhashadd(ca, (int *)attr,
					 &mudstate.parent_htab);
			}
		}
	}
	atr_pop();
}

int parse_attrib_wild(player, str, thing, check_parents, get_locks, df_star,
		      ok_structs)
dbref player, *thing;
char *str;
int check_parents, get_locks, df_star, ok_structs;
{
	char *buff;
	dbref parent;
	int check_exclude, hash_insert, lev;

	if (!str)
		return 0;

	buff = alloc_lbuf("parse_attrib_wild");
	StringCopy(buff, str);

	/*
	 * Separate name and attr portions at the first / 
	 */

	if (!parse_thing_slash(player, buff, &str, thing)) {

		/*
		 * Not in obj/attr format, return if not defaulting to * 
		 */

		if (!df_star) {
			free_lbuf(buff);
			return 0;
		}
		/*
		 * Look for the object, return failure if not found 
		 */

		init_match(player, buff, NOTYPE);
		match_everything(MAT_EXIT_PARENTS);
		*thing = match_result();

		if (!Good_obj(*thing)) {
			free_lbuf(buff);
			return 0;
		}
		str = (char *)"*";
	}
	/*
	 * Check the object (and optionally all parents) for attributes 
	 */

	if (check_parents) {
		check_exclude = 0;
		hash_insert = check_parents;
		nhashflush(&mudstate.parent_htab, 0);
		ITER_PARENTS(*thing, parent, lev) {
			if (!Good_obj(Parent(parent)))
				hash_insert = 0;
			find_wild_attrs(player, parent, str, check_exclude,
					hash_insert, get_locks, ok_structs);
			check_exclude = 1;
		}
	} else {
		find_wild_attrs(player, *thing, str, 0, 0, get_locks,
				ok_structs);
	}
	free_lbuf(buff);
	return 1;
}


/*
 * ---------------------------------------------------------------------------
 * edit_string_ansi, do_edit: Modify attributes.
 */

void edit_string_ansi(src, dst, returnstr, from, to)
char *src, **dst, **returnstr, *from, *to;
{
    edit_string(src, dst, from, to);

    if (mudconf.ansi_colors) {
	edit_string(src, returnstr, from,
		    tprintf("%s%s%s%s", ANSI_HILITE, to, ANSI_NORMAL,
			    ANSI_NORMAL));
    } else {
	*returnstr = alloc_lbuf("edit_string_ansi");
	StringCopy(*returnstr, *dst);
    }
}

void do_edit(player, cause, key, it, args, nargs)
dbref player, cause;
int key, nargs;
char *it, *args[];
{
	dbref thing, aowner;
	int attr, got_one, aflags, alen, doit;
	int could_hear;
	char *from, *to, *result, *returnstr, *atext;
	ATTR *ap;

	/*
	 * Make sure we have something to do. 
	 */

	if ((nargs < 1) || !*args[0]) {
		notify_quiet(player, "Nothing to do.");
		return;
	}
	from = args[0];
	to = (nargs >= 2) ? args[1] : (char *)"";

	/*
	 * Look for the object and get the attribute (possibly wildcarded) 
	 */

	olist_push();
	if (!it || !*it ||
	    !parse_attrib_wild(player, it, &thing, 0, 0, 0, 0)) {
		notify_quiet(player, "No match.");
		olist_pop();
		return;
	}
	/*
	 * Iterate through matching attributes, performing edit 
	 */

	got_one = 0;
	atext = alloc_lbuf("do_edit.atext");
	could_hear = Hearer(thing);

	for (attr = olist_first(); attr != NOTHING; attr = olist_next()) {
		ap = atr_num(attr);
		if (ap) {

			/*
			 * Get the attr and make sure we can modify it. 
			 */

			atr_get_str(atext, thing, ap->number,
				    &aowner, &aflags, &alen);
			if (Set_attr(player, thing, ap, aflags)) {

				/*
				 * Do the edit and save the result 
				 */

				got_one = 1;
				edit_string_ansi(atext, &result, &returnstr, from, to);
				if (ap->check != NULL) {
					doit = (*ap->check) (0, player, thing,
							ap->number, result);
				} else {
					doit = 1;
				}
				if (doit) {
					atr_add(thing, ap->number, result,
						Owner(player), aflags);
					if (!Quiet(player))
						notify_quiet(player,
						     tprintf("Set - %s: %s",
							     ap->name,
							     returnstr));
				}
				free_lbuf(result);
				free_lbuf(returnstr);
			} else {

				/*
				 * No rights to change the attr 
				 */

				notify_quiet(player,
					   tprintf("%s: Permission denied.",
						   ap->name));
			}

		}
	}

	/*
	 * Clean up 
	 */

	free_lbuf(atext);
	olist_pop();

	if (!got_one) {
		notify_quiet(player, "No matching attributes.");
	} else {
	    	handle_ears(thing, could_hear, Hearer(thing));
	}
}

void do_wipe(player, cause, key, it)
dbref player, cause;
int key;
char *it;
{
	dbref thing, aowner;
	int attr, got_one, aflags, alen;
	int could_hear;
	ATTR *ap;
	char *atext;

	olist_push();
	if (!it || !*it ||
	    !parse_attrib_wild(player, it, &thing, 0, 0, 1, 1)) {
		notify_quiet(player, "No match.");
		olist_pop();
		return;
	}
	/*
	 * Iterate through matching attributes, zapping the writable ones 
	 */

	got_one = 0;
	atext = alloc_lbuf("do_wipe.atext");
	could_hear = Hearer(thing);

	for (attr = olist_first(); attr != NOTHING; attr = olist_next()) {
		ap = atr_num(attr);
		if (ap) {

			/*
			 * Get the attr and make sure we can modify it. 
			 */

			atr_get_str(atext, thing, ap->number,
				    &aowner, &aflags, &alen);
			if (Set_attr(player, thing, ap, aflags)) {
				atr_clr(thing, ap->number);
				got_one = 1;
			}
		}
	}
	/*
	 * Clean up 
	 */

	free_lbuf(atext);
	olist_pop();

	if (!got_one) {
		notify_quiet(player, "No matching attributes.");
	} else {
		handle_ears(thing, could_hear, Hearer(thing));
		if (!Quiet(player))
			notify_quiet(player, "Wiped.");
	}
}

void do_trigger(player, cause, key, object, argv, nargs)
dbref player, cause;
int key, nargs;
char *object, *argv[];
{
	dbref thing;
	int attrib;

	if (!((parse_attrib(player, object, &thing, &attrib, 0)
	       && (attrib != NOTHING)) ||
	      (parse_attrib(player, tprintf("me/%s", object),
			    &thing, &attrib, 0)
	       && (attrib != NOTHING)))) {
		notify_quiet(player, "No match.");
		return;
	}
	if (!controls(player, thing)) {
		notify_quiet(player, NOPERM_MESSAGE);
		return;
	}
	did_it(player, thing, A_NULL, NULL, A_NULL, NULL,
	       attrib, key & TRIG_NOW, argv, nargs, 0);

	/*
	 * XXX be more descriptive as to what was triggered? 
	 */
	if (!(key & TRIG_QUIET) && !Quiet(player))
		notify_quiet(player, "Triggered.");

}

void do_use(player, cause, key, object)
dbref player, cause;
int key;
char *object;
{
	char *df_use, *df_ouse, *temp, *bp;
	dbref thing, aowner;
	int aflags, alen, doit;

	init_match(player, object, NOTYPE);
	match_neighbor();
	match_possession();
	if (Wizard(player)) {
		match_absolute();
		match_player();
	}
	match_me();
	match_here();
	thing = noisy_match_result();
	if (thing == NOTHING)
		return;

	/*
	 * Make sure player can use it 
	 */

	if (!could_doit(player, thing, A_LUSE)) {
		did_it(player, thing, A_UFAIL,
		       "You can't figure out how to use that.",
		       A_OUFAIL, NULL, A_AUFAIL, 0, (char **)NULL, 0,
		       MSG_PRESENCE);
		return;
	}
	temp = alloc_lbuf("do_use");
	doit = 0;
	if (*atr_pget_str(temp, thing, A_USE, &aowner, &aflags, &alen))
		doit = 1;
	else if (*atr_pget_str(temp, thing, A_OUSE, &aowner, &aflags, &alen))
		doit = 1;
	else if (*atr_pget_str(temp, thing, A_AUSE, &aowner, &aflags, &alen))
		doit = 1;
	free_lbuf(temp);

	if (doit) {
		df_use = alloc_lbuf("do_use.use");
		df_ouse = alloc_lbuf("do_use.ouse");
		bp = df_use;
		safe_tprintf_str(df_use, &bp, "You use %s", Name(thing));
		bp = df_ouse;
		safe_tprintf_str(df_ouse, &bp, "uses %s", Name(thing));
		did_it(player, thing, A_USE, df_use, A_OUSE, df_ouse, A_AUSE,
		       1, (char **)NULL, 0, MSG_PRESENCE);
		free_lbuf(df_use);
		free_lbuf(df_ouse);
	} else {
		notify_quiet(player, "You can't figure out how to use that.");
	}
}

/*
 * ---------------------------------------------------------------------------
 * * do_setvattr: Set a user-named (or possibly a predefined) attribute.
 */

void do_setvattr(player, cause, key, arg1, arg2)
dbref player, cause;
int key;
char *arg1, *arg2;
{
	char *s;
	int anum;

	arg1++;			/*
				 * skip the '&' 
				 */
	for (s = arg1; *s && !isspace(*s); s++) ;	/*
							 * take to the space 
							 */
	if (*s)
		*s++ = '\0';	/*
				 * split it 
				 */

	anum = mkattr(arg1);	/*
				 * Get or make attribute 
				 */
	if (anum <= 0) {
		notify_quiet(player,
			     "That's not a good name for an attribute.");
		return;
	}
	do_setattr(player, cause, anum, s, arg2);
}