/* set.c */

#include "copyright.h"

/* commands which set parameters */
#ifdef WANT_ANSI
#ifdef __STDC__
#include <stddef.h>
#include <stdlib.h>
#endif /* __STDC__ */
#endif /* WANT_ANSI */
#include <stdio.h>
#include <ctype.h>

#include "mudconf.h"
#include "config.h"
#include "db.h"
#include "match.h"
#include "interface.h"
#include "externs.h"
#include "flags.h"

extern NAMETAB indiv_attraccess_nametab[];

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

	init_match(player, name, NOTYPE);
	match_everything();
	mat = noisy_match_result();
	if (mat != NOTHING && !Controls(player, mat)) {
		notify(player, "Permission denied.");
		return NOTHING;
	} else {
		return (mat);
	}
}

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

	init_match(player, name, NOTYPE);
	match_everything();
	mat = noisy_match_result();
	if (mat != NOTHING && !Affects(player, mat)) {
		notify(player, "Permission denied.");
		return NOTHING;
	} else {
		return (mat);
	}
}

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

	init_match(player, name, NOTYPE);
	match_everything();
	mat = noisy_match_result();
	if (mat != NOTHING && !Examinable(player, mat)) {
		notify(player, "Permission denied.");
		return NOTHING;
	} else {
		return (mat);
	}
}

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

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

	/* check for bad name */
	if (*newname == '\0') {
		notify(player, "Give it what new name?");
		return;
	}

	/* check for renaming a player */
	if (Typeof(thing) == TYPE_PLAYER) {

		buff = trim_spaces(newname);

		/* split off password */
		for (password = buff;
		     *password && !isspace(*password);
		     password++) ;

		/* eat whitespace */
		if (*password) {
			*password++ = '\0';	/* terminate name */
			while (*password && isspace(*password))
				password++;
		}

		/* check for null password */
		if (!*password) {
			notify(player,
				"You must specify a password to change a player name.");
			notify(player, "E.g.: name player = newname password");
			free_lbuf(buff);
			return;

		} else if (!check_pass(thing, password)) {
			notify(player, "Incorrect password.");
			free_lbuf(buff);
			return;
		} else if (!ok_player_name(buff) ||
			   !badname_check(buff)) {
			notify(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(player, "That name is already in use.");
			free_lbuf(buff);
			return;
		}

		/* everything ok, notify */
		STARTLOG(LOG_SECURITY,"SEC","CNAME")
			log_name(thing),
			log_text((char *)" renamed to ");
			log_text(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(player, "Name set.");
		free_lbuf(buff);
		return;
	} else {
		if (!ok_name(newname)) {
			notify(player, "That is not a reasonable name.");
			return;
		}

		/* everything ok, change the name */
		s_Name(thing, newname);
		if (!Quiet(player) && !Quiet(thing))
			notify(player, "Name set.");
	}
}

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

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

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

	/* check for renaming a player */

	ap = atr_num(A_ALIAS);
	if (Typeof(thing) == TYPE_PLAYER) {

		/* Fetch the old alias */

		oldalias = atr_pget(thing, A_ALIAS, &aowner, &aflags);
		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 only
			 * writable by GOD and we want to keep people from
			 * doing &ALIAS and bypassing the player name checks.
			 */

			notify(player, "Permission denied.");
		} else if (!*trimalias) {

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

			delete_player_name(thing, oldalias);
			if (!Quiet(player))
				notify(player, "Alias removed.");
		} else if (lookup_player(NOTHING, trimalias, 0) != NOTHING) {

			/* Make sure new alias isn't already in use */

			notify(player, "That name is already in use.");
		} else {

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

			delete_player_name(thing, oldalias);
			atr_add(thing, A_ALIAS, trimalias, Owner(player),
				aflags);
			if (add_player_name(thing, trimalias)) {
				if (!Quiet(player))
					notify(player, "Alias set.");
			} else {
				notify(player, "Error setting alias.");
			}
			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(player, "Permission denied.");
		} else {
			atr_add(thing, A_ALIAS, alias, Owner(player), aflags);
			if (!Quiet(player))
				notify(player, "Set.");
		}
	}
}

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

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

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

			ap = atr_num(atr);

			/* You may lock an attribute if:
			 * you could write the attribute if it were stored on
			 * yourself
			 *    --and--
			 * you own the attribute or are a wizard as long as
			 * you are not #1 and are trying to do something to #1.
			 */

			if (ap && (God(player) ||
			     (!God(thing) && Set_attr(player, player, ap, 0) &&
			      (Wizard(player) ||
			       (aowner == Owner(player)))))) {
				aflags |= AF_LOCK;
				atr_set_flags(thing, atr, aflags);
				if (!Quiet(player) && !Quiet(thing))
					notify(player, "Attribute locked.");
			} else {
				notify(player, "Permission denied.");
			}
			return;
		}
	}
    
	init_match(player, name, NOTYPE);
	match_everything();

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

	okey = parse_boolexp(player, keytext);
	if (okey == TRUE_BOOLEXP) {
		notify(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 (!Quiet(player) && !Quiet(thing))
			notify(player, "Locked.");
	}
	free_boolexp(okey);
}

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

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

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

			/* You may unlock an attribute if:
			 * you could write the attribute if it were stored on
			 * yourself
			 *   --and--
			 * you own the attribute or are a wizard as long as
			 * you are not #1 and are trying to do something to #1.
			 */
		
			if (ap && (God(player) ||
			     (!God(thing) && Set_attr(player, player, ap, 0) &&
			      (Wizard(player) ||
			       (aowner == Owner(player)))))) {
				aflags &= ~AF_LOCK;
				atr_set_flags(thing, atr, aflags);
				if (Owner(player != Owner(thing)))
				if (!Quiet(player) && !Quiet(thing))
					notify(player, "Attribute unlocked.");
			} else {
				notify(player, "Permission denied.");
			}
			return;
		}
	}
    
	if (!key)
		key = A_LOCK;
	if ((thing = match_controlled(player, name)) != NOTHING) {
		atr_clr(thing, key);
		if (!Quiet(player) && !Quiet(thing))
			notify(player, "Unlocked.");
	}
}

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

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

	init_match(player, name, TYPE_EXIT);
	match_exit();
	match_here();
	if (Wizard(player)) {
		match_absolute();
	}

	switch (exit = match_result()) {
	case NOTHING:
		notify(player, "Unlink what?");
		break;
	case AMBIGUOUS:
		notify(player, "I don't know which one you mean!");
		break;
	default:
		if (!controls(player, exit)) {
			notify(player, "Permission denied.");
		} else {
			switch (Typeof(exit)) {
			case TYPE_EXIT:
				s_Location(exit, NOTHING);
				if (!Quiet(player))
					notify(player, "Unlinked.");
				break;
			case TYPE_ROOM:
				s_Dropto(exit, NOTHING);
				if (!Quiet(player))
					notify(player, "Dropto removed.");
				break;
			default:
				notify(player, "You can't unlink that!");
				break;
			}
		}
	}
}

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

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

	if (parse_attrib(player, name, &thing, &atr)) {
		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(player,
					"Attribute not present on object.");
				return;
			}
			do_it = 0;
			if (owner == NOTHING) {
				notify(player, "I couldn't find that player.");
			} else if (God(thing) && !God(player)) {
				notify(player, "Permission denied.");
			} 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(player, "Permission denied.");
				} 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(player, "Permission denied.");
				} else {
					do_it = 1;
				}
			} else {
				notify(player, "Permission denied.");
			}

			if (!do_it) return;

			ap = atr_num(atr);
			if (!ap || !Set_attr(player, player, ap, aflags)) {
				notify(player, "Permission denied.");
				return;
			}

			atr_set_owner(thing, atr, owner);
			if (!Quiet(player))
				notify(player, "Attribute owner changed.");
			return;
		}
	}

	init_match(player, name, TYPE_THING);
	match_possession();
	match_here();
	match_exit();
	match_me();
	if(Wizard(player)) {
		match_player();
		match_absolute();
	}

	switch (thing = match_result()) {
	case NOTHING:
		notify(player, "You don't have that!");
		return;
	case AMBIGUOUS:
		notify(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(player, "I couldn't find that player.");
	} else if (Typeof(thing) == TYPE_PLAYER && !God(player)) {
		notify(player, "Players always own themselves.");
	} else if ((!controls(player, thing) && (!(Flags(thing) & CHOWN_OK) ||
		     ((Typeof(thing) == TYPE_THING) &&
		      (Location(thing) != player)))) ||
		   (!controls(player, owner))) {
		notify(player, "Permission denied.");
	} else if (canpayfees(player, owner, cost, quota)) {
		giveto(Owner(thing), cost);
		if (mudconf.quotas)
			add_quota(Owner(thing), quota);
		if(God(player)) {
			s_Owner(thing, owner);
		} else {
			s_Owner(thing, Owner(owner));
		}
		atr_chown(thing);
		s_Flags(thing, (Flags(thing) & ~(CHOWN_OK|INHERIT)) | HALT);
		halt_que(NOTHING, thing);
		if (!Quiet(player))
			notify(player, "Owner changed.");
	}
}

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

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

	attr = atr_num(attrnum);
	atr_pget_info(thing, attrnum, &aowner, &aflags);
	if (Set_attr(player, thing, attr, aflags)) {
		if ((attr->check != NULL) &&
		    (!(*attr->check)(0, player, thing, attrnum, attrtext)))
			return;
		could_hear = Hearer(thing);
		atr_add(thing, attrnum, attrtext, Owner(player), aflags);
		handle_ears(thing, could_hear, Hearer(thing));
		if (!Quiet(player) && !Quiet(thing))
			notify(player, "Set.");
	} else {
		notify(player, "Permission denied.");
	}
}

void do_set(dbref player, dbref cause, int key, char *name, char *flag)
{
dbref	thing, aowner;
char	*p, *buff;
int	canhear, atr, aflags, 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)) {
		if (atr != NOTHING) {

			/* You must specify a flag name */

			if (!flag || !*flag) {
				notify(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 == -1) {
				notify(player, "You can't set that!");
				return;
			}

			/* Make sure the object has the attribute present */

			if (!atr_get_info(thing, atr, &aowner, &aflags)) {
				notify(player,
					"Attribute not present on object.");
				return;
			}

			/* Make sure we can write to the attribute */

			attr = atr_num(atr);
			if (!Set_attr(player, thing, attr, aflags)) {
				notify(player, "Permission denied.");
				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 (!Quiet(player) && !Quiet(thing)) {
				if (clear)
					notify(player, "Cleared.");
				else
					notify(player, "Set.");
			}
			return;
		}
	}

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

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

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

		buff=alloc_lbuf("do_set");

		/* check for _ */
		if (*p == '_') {
			strcpy(buff, p + 1);
			if (!parse_attrib(player, p + 1, &thing, &atr) ||
			    (atr == NOTHING)) {
				notify(player, "No match.");
				free_lbuf(buff);
				return;
			}

			attr2 = atr_num(atr);
			p = buff;
		        atr_pget_str(buff, thing, atr, &aowner, &aflags);

			if (!See_attr(player, thing, attr2, aowner)) {
				notify(player, "Permission denied.");
				free_lbuf(buff);
				return;
			}
		}

		/* Go set it */

		set_attr_internal (player, thing, attr->number, p);
		free_lbuf(buff);
		return;
	}

	/* Set or clear a flag */  

	flag_set(thing, player, flag);
}

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

	init_match(player, name, NOTYPE);
	match_everything();
	thing = noisy_match_result();
	if (thing == NOTHING)
		return;
	set_attr_internal(player, thing, attrnum, attrtext);
}

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

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

	if (nargs < 2) {
		notify(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 attribute.  If the name check fails, try by number */

	in_attr = atr_str(args[0]);
	if (in_attr == NULL)
		in_attr = atr_num(atoi(args[0]));
	if (in_attr == NULL) {
		notify(player, tprintf("%s: No such attribute.", args[0]));
		return;
	}
	astr = atr_get(thing, in_attr->number, &aowner, &aflags);
	if (!See_attr(player, thing, in_attr, aowner)) {
		notify(player, tprintf("%s: Permission denied.", args[0]));
		free_lbuf(astr);
		return;
	}

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

	no_delete = 0;
	for (i=1; i<nargs; i++) {
		out_attr = atr_str(args[i]);
		isnum = 0;
		anum = 0;
		if (out_attr == NULL) {
			isnum = is_number(args[i]);
			if (isnum) {
				anum = atoi(args[i]);
			} else {
				anum = mkattr(args[i]);
			}
			if (anum > 0)
				out_attr = atr_num(anum);
		}
		if (out_attr == NULL) {
			if (!God(player) || !isnum) {
				notify(player,
					tprintf("%s is not a good name for an attribute.",
						args[i]));
			} else {
				notify(player,
					tprintf("Warning: Attribute # %d is not defined.",
						anum));
				atr_add(thing, anum, astr, Owner(player),
					aflags);
			}
			continue;
		}
		if (out_attr->number == in_attr->number) {
			no_delete = 1;
		} else {
			atr_get_info(thing, out_attr->number, &axowner,
				&axflags);
			if (!Set_attr(player, thing, out_attr, axflags)) {
				notify(player,
					tprintf("%s: Permission denied.",
						args[i]));
			} else {
				atr_add(thing, out_attr->number, astr,
					Owner(player), aflags);
				if (!Quiet(player))
					notify(player,
						tprintf("Set: %s",
							out_attr->name));
			}
		}
	}

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

	if (!no_delete) {
		if (Set_attr(player, thing, in_attr, aflags)) {
			atr_clr(thing, in_attr->number);
		} else {
			notify(player,
				"Could not remove old attribute.  Permission denied.");
		}
	}
	free_lbuf(astr);
}

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

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

	if (!str)
		return 0;

	buff=alloc_lbuf("parse_attrib");
	strcpy(buff, str);

	/* get name up to / */
	for (str=buff; *str && (*str!='/'); str++) ;
	if (!*str) {
		free_lbuf(buff);
		return 0;
	}
	*str++ = 0;

	init_match(player, buff, NOTYPE);
	match_everything();
	*thing = match_result();
	if ((*thing == NOTHING) || (*thing == AMBIGUOUS)) {
		free_lbuf(buff);
		return 0;
	}

	/* rest is attrib name.  Note: return true with atr=NOTHING if
	 * the attribute doesn't exist or is unreadable */

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

int parse_attrib_wild(dbref player, char *str, dbref *thing, int get_locks)
{
ATTR	*attr;
char	*buff, *as;
dbref	aowner;
int	aflags, ca, ok;

	olist_init();
	if (!str)
		return 0;

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

	/* get name up to / */
	for (str=buff; *str && (*str!='/'); str++) ;
	if (!*str) {
		free_lbuf(buff);
		return 0;
	}
	*str++ = '\0';

	init_match(player, buff, NOTYPE);
	match_everything();
	*thing = match_result();
	if ((*thing == NOTHING) || (*thing == AMBIGUOUS)) {
		free_lbuf(buff);
		return 0;
	}

	/* Process the object's attribute list, storing matches in the olist
	 * area.
	 */

	for (ca=atr_head(*thing, &as); ca; ca=atr_next(&as)) {
		attr = atr_num(ca);
		if (attr) {
			atr_get_info(*thing, ca, &aowner, &aflags);
			if (get_locks)
				ok = Read_attr(player, *thing, attr, aowner);
			else
				ok = See_attr(player, *thing, attr, aowner);
			if (ok && quick_wild(str, (char *)attr->name)) {
					olist_add(ca);
			}
		}
	}
	free_lbuf(buff);
	return 1;
}

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

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

	/* Do the substitution.  Idea for prefix/suffix from R'nice@TinyTIM */

	if (!strcmp(from, "^")) {
		/* Prepend 'to' to string */

		*dst = alloc_lbuf("edit_string.^");
		cp = *dst;
		safe_str(to, *dst, &cp);
		safe_str(src, *dst, &cp);
		*cp = '\0';
	} else if (!strcmp(from, "$")) {
		/* Append 'to' to string */

		*dst = alloc_lbuf("edit_string.$");
		cp = *dst;
		safe_str(src, *dst, &cp);
		safe_str(to, *dst, &cp);
		*cp = '\0';
	} else {
		/* replace all occurances of from with to */

		*dst = replace_string(from, to, src);
	}
}

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

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

	if ((nargs < 1) || !*args[0]) {
		notify(player, "Nothing to do.");
		return;
	}

	from = args[0];
	to = (nargs >= 2) ? args[1] : (char *)"";

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

	if (!it || !*it || !parse_attrib_wild(player, it, &thing, 0)) {
		notify(player, "No match.");
		return;
	}

	/* Iterate through matching attributes, performing edit */

	got_one = 0;
	atext = alloc_lbuf("do_edit.atext");

	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);
			if (Set_attr(player, thing, ap, aflags)) {

				/* Do the edit and save the result */

				got_one = 1;
				edit_string(atext, &result, 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(player,
							tprintf("Set - %s: %s",
								ap->name,
								result));
				}
				free_lbuf(result);
			} else {

				/* No rights to change the attr */

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

		}
	}

	/* Clean up */

	free_lbuf(atext);
	olist_init();

	if (!got_one) {
		notify(player, "No matching attributes.");
	}
}

void do_trigger(dbref player, dbref cause, int key, char *object,
	char *argv[], int nargs)
{
  dbref thing;
  int attrib;
  if (!parse_attrib(player, object, &thing, &attrib) || (attrib == NOTHING)) {
    notify(player, "No match.");
    return;
  }
  if (!controls(player, thing)) {
    notify(player, "Permission denied.");
    return;
  }
  did_it(player, thing, 0, NULL, 0, NULL, attrib, argv, nargs);
  if (!Quiet(player)) notify(player, "Triggered.");
  /* XXX be more descriptive as to what was triggered? */
}

void do_use (dbref player, dbref cause, int key, char *object)
{
char	*df_use, *df_ouse, *temp;
dbref	thing, aowner;
int	aflags, 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, (char **)NULL, 0);
		return;
	}

        temp = alloc_lbuf("do_use");
	doit = 0;
	if (*atr_pget_str(temp, thing, A_USE, &aowner, &aflags))
		doit = 1;
	else if (*atr_pget_str(temp, thing, A_OUSE, &aowner, &aflags))
		doit = 1;
	else if (*atr_pget_str(temp, thing, A_AUSE, &aowner, &aflags))
		doit = 1;
	free_lbuf(temp);

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

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

void do_setvattr(dbref player, dbref cause, int key, char *arg1,
		  char *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(player, "That's not a good name for an attribute.");
		return;
	}
	do_setattr(player, cause, anum, s, arg2);
}