#include <ctype.h>
#include <string.h>

#include "config.h"
#include "db.h"
#include "externs.h"
#include "interface.h"
#include "attrib.h"
#include "oldattrib.h"
	
#ifdef MEM_CHECK
#include "mem_check.h"
#endif

#ifdef USE_NALLOC
#include "nalloc.h"
extern NALLOC *db_strings;
#endif

/* attribute list */
ATTR attr[] =
{
  { (char *) "AAHEAR",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ACLONE",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ACONNECT",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ADEATH",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ADESCRIBE",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ADISCONNECT",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ADROP",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "AEFAIL",          AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "AENTER",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "AFAILURE",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "AHEAR",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ALEAVE",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "AMHEAR",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "AMOVE",           AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "APAYMENT",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ASUCCESS",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "AUSE",            AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "AWAY",            AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "CHARGES",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "CLASS",           AF_NOPROG,                      NULL,   0 },
  { (char *) "COST",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "DEATH",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "DESCRIBE",	AF_NOPROG,			NULL,	0 },
  { (char *) "DOES",		AF_ODARK | AF_NOPROG,	       	NULL,	0 },
  { (char *) "DROP",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "EALIAS",          AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "EFAIL",           AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "ENTER",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "FAILURE",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "HAVEN",           AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "IDESCRIBE",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "IDLE",            AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "LALIAS",          AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "LAST",		AF_WIZARD | AF_LOCKED,		NULL,	0 },
  { (char *) "LASTSITE",        AF_LOCKED | AF_ODARK,           NULL,   0 },
  { (char *) "LEAVE",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "LISTEN",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "MOVE",            AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "ODEATH",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ODESCRIBE",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "ODROP",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "OEFAIL",          AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "OENTER",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "OFAILURE",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "OLEAVE",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "OMOVE",           AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "OPAYMENT",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "OSUCCESS",	AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "OUSE",            AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "OXENTER",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "OXLEAVE",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "PAYMENT",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "QUEUE",		AF_ODARK | AF_WIZARD,		NULL,	0 },
  { (char *) "RACE",            AF_NOPROG | AF_WIZARD,          NULL,   0 },
  { (char *) "RQUOTA",		AF_DARK | AF_WIZARD,		NULL,	0 },
  { (char *) "RUNOUT",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "SEX",		AF_NOPROG,			NULL,	0 },
  { (char *) "STARTUP",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "SUCCESS",		AF_ODARK | AF_NOPROG,		NULL,	0 },
  { (char *) "USE",             AF_ODARK | AF_NOPROG,           NULL,   0 },
  { (char *) "VA",		AF_ODARK,			NULL,	0 },
  { (char *) "VB",		AF_ODARK,			NULL,	0 },
  { (char *) "VC",		AF_ODARK,			NULL,	0 },
  { (char *) "VD",		AF_ODARK,			NULL,	0 },
  { (char *) "VE",		AF_ODARK,			NULL,	0 },
  { (char *) "VF",		AF_ODARK,			NULL,	0 },
  { (char *) "VG",		AF_ODARK,			NULL,	0 },
  { (char *) "VH",		AF_ODARK,			NULL,	0 },
  { (char *) "VI",		AF_ODARK,			NULL,	0 },
  { (char *) "VJ",		AF_ODARK,			NULL,	0 },
  { (char *) "VK",		AF_ODARK,			NULL,	0 },
  { (char *) "VL",		AF_ODARK,			NULL,	0 },
  { (char *) "VM",		AF_ODARK,			NULL,	0 },
  { (char *) "VN",		AF_ODARK,			NULL,	0 },
  { (char *) "VO",		AF_ODARK,			NULL,	0 },
  { (char *) "VP",		AF_ODARK,			NULL,	0 },
  { (char *) "VQ",		AF_ODARK,			NULL,	0 },
  { (char *) "VR",		AF_ODARK,			NULL,	0 },
  { (char *) "VS",		AF_ODARK,			NULL,	0 },
  { (char *) "VT",		AF_ODARK,			NULL,	0 },
  { (char *) "VU",		AF_ODARK,			NULL,	0 },
  { (char *) "VV",		AF_ODARK,			NULL,	0 },
  { (char *) "VW",		AF_ODARK,			NULL,	0 },
  { (char *) "VX",		AF_ODARK,			NULL,	0 },
  { (char *) "VY",		AF_ODARK,			NULL,	0 },
  { (char *) "VZ",		AF_ODARK,			NULL,	0 },
  { (char *) "WA",		AF_ODARK,			NULL,	0 },
  { (char *) "WB",		AF_ODARK,			NULL,	0 },
  { (char *) "WC",		AF_ODARK,			NULL,	0 },
  { (char *) "WD",		AF_ODARK,			NULL,	0 },
  { (char *) "WE",		AF_ODARK,			NULL,	0 },
  { (char *) "WF",		AF_ODARK,			NULL,	0 },
  { (char *) "WG",		AF_ODARK,			NULL,	0 },
  { (char *) "WH",		AF_ODARK,			NULL,	0 },
  { (char *) "WI",		AF_ODARK,			NULL,	0 },
  { (char *) "WJ",		AF_ODARK,			NULL,	0 },
  { (char *) "WK",		AF_ODARK,			NULL,	0 },
  { (char *) "WL",		AF_ODARK,			NULL,	0 },
  { (char *) "WM",		AF_ODARK,			NULL,	0 },
  { (char *) "WN",		AF_ODARK,			NULL,	0 },
  { (char *) "WO",		AF_ODARK,			NULL,	0 },
  { (char *) "WP",		AF_ODARK,			NULL,	0 },
  { (char *) "WQ",		AF_ODARK,			NULL,	0 },
  { (char *) "WR",		AF_ODARK,			NULL,	0 },
  { (char *) "WS",		AF_ODARK,			NULL,	0 },
  { (char *) "WT",		AF_ODARK,			NULL,	0 },
  { (char *) "WU",		AF_ODARK,			NULL,	0 },
  { (char *) "WV",		AF_ODARK,			NULL,	0 },
  { (char *) "WW",		AF_ODARK,			NULL,	0 },
  { (char *) "WX",		AF_ODARK,			NULL,	0 },
  { (char *) "WY",		AF_ODARK,			NULL,	0 },
  { (char *) "WZ",		AF_ODARK,			NULL,	0 },
  { (char *) "XA",		AF_ODARK,			NULL,	0 },
  { (char *) "XB",		AF_ODARK,			NULL,	0 },
  { (char *) "XC",		AF_ODARK,			NULL,	0 },
  { (char *) "XD",		AF_ODARK,			NULL,	0 },
  { (char *) "XE",		AF_ODARK,			NULL,	0 },
  { (char *) "XF",		AF_ODARK,			NULL,	0 },
  { (char *) "XG",		AF_ODARK,			NULL,	0 },
  { (char *) "XH",		AF_ODARK,			NULL,	0 },
  { (char *) "XI",		AF_ODARK,			NULL,	0 },
  { (char *) "XJ",		AF_ODARK,			NULL,	0 },
  { (char *) "XK",		AF_ODARK,			NULL,	0 },
  { (char *) "XL",		AF_ODARK,			NULL,	0 },
  { (char *) "XM",		AF_ODARK,			NULL,	0 },
  { (char *) "XN",		AF_ODARK,			NULL,	0 },
  { (char *) "XO",		AF_ODARK,			NULL,	0 },
  { (char *) "XP",		AF_ODARK,			NULL,	0 },
  { (char *) "XQ",		AF_ODARK,			NULL,	0 },
  { (char *) "XR",		AF_ODARK,			NULL,	0 },
  { (char *) "XS",		AF_ODARK,			NULL,	0 },
  { (char *) "XT",		AF_ODARK,			NULL,	0 },
  { (char *) "XU",		AF_ODARK,			NULL,	0 },
  { (char *) "XV",		AF_ODARK,			NULL,	0 },
  { (char *) "XW",		AF_ODARK,			NULL,	0 },
  { (char *) "XX",		AF_ODARK,			NULL,	0 },
  { (char *) "XY",		AF_ODARK,			NULL,	0 },
  { (char *) "XZ",		AF_ODARK,			NULL,	0 },
  { (char *) "XYXXY",		AF_DARK  | AF_NOPROG | AF_LOCKED | AF_WIZARD,
								NULL,	0 },
  { 	     NULL,		0,			NULL,	0 }
};

char *clean_atr_name(s)
    char *s;
{
    static char buf[BUFFER_LEN];
    char *q = buf;
    char *a;

    if(!*s || !s) {
      sprintf(buf, "NULL");
      return buf;
    }

    if(!string_compare("KILL", s)) {
      sprintf(buf, "DEATH");
      return buf;
    }

    if(!string_compare("KILL", s+1) &&
       (*s == 'o' || *s == 'O' || *s == 'a' || *s == 'A')) {
      sprintf(buf, "%c%s", *s, s+1);
      return buf;
    }

    for(a = s; *a; a++)
      if(isprint(*a) && !isspace(*a))
	*q++ = *a;
    *q = '\0';
    return buf;
}

ATTR *atr_str(s)
    char *s;
{
  ATTR *result;
  ATTR *a = attr;
  int done = 0;
  char *q;

#ifdef USE_NALLOC
  result = (ATTR *)na_alloc(db_strings, sizeof (ATTR));
#else
  result = (ATTR *)malloc(sizeof (ATTR));
#endif
  q = clean_atr_name(s);
#ifdef USE_NALLOC
  result->name = (char *) na_ualloc(db_strings, (strlen(q) + 1));
#else
  result->name = (char *) malloc(strlen(q)+1);
#endif
#ifdef MEM_CHECK
  add_check("attribute");
  add_check("attribute_name");
#endif
  strcpy(result->name, strupper(q));
  result->flags = AF_ODARK;

  while (a->name && !done) {
   if(!string_compare(result->name, a->name)) {
     result->flags = a->flags;
     done++;
   }
   a++;
  }
  return result;
}

struct boolatr *alloc_atr(name, s)
    char *name;
    char *s;
{
  struct boolatr *a;
  const char *p;

#ifdef USE_NALLOC
  a = (struct boolatr *)na_alloc(db_strings, sizeof(struct boolatr));
  a->name = (char *)na_ualloc(db_strings, strlen(name) + 1);
#else
  a = (struct boolatr *)malloc(sizeof (struct boolatr));
  a->name = (char *)malloc(strlen(name)+1);
#endif
  strcpy(a->name,name);
  p = compress(s);
#ifdef USE_NALLOC
  a->text = (char *)na_ualloc(db_strings, strlen(p) + 1);
#else
  a->text = (char *)malloc(strlen(p)+1);
#endif
  strcpy(a->text, p);
#ifdef MEM_CHECK
  add_check("bool_atr");
  add_check("bool_atr_name");
  add_check("bool_atr_val");
#endif
  return a;
}

void atr_clr(thing, atr)
    dbref thing;
    char *atr;
{
  ALIST *ptr = db[thing].list;

  while(ptr) {
    if(!string_compare(atr, AL_NAME(ptr))) {
      AL_DISPOSE(ptr);
      return;
    }
    ptr = AL_NEXT(ptr);
  }
}

ALIST *AL_MAKE(type, next, string, owner, flags)
    char *type;
    ALIST *next;
    char *string;
    dbref owner;
    dbref flags;
{
  ALIST *ptr;
  const char *p;

#ifdef USE_NALLOC
  ptr = (ALIST *)na_alloc(db_strings, sizeof(ALIST));
#else
  ptr = (ALIST *)malloc(sizeof(ALIST));
#endif
  AL_ATTR(ptr) = atr_str(type);
  AL_CREATOR(ptr) = owner;
  p = compress(string);
#ifdef USE_NALLOC
  AL_STR(ptr) = (char *)na_ualloc(db_strings, strlen(p) + 1);
#else
  AL_STR(ptr) = (char *)malloc(strlen(p) + 1);
#endif
#ifdef MEM_CHECK
  add_check("ALIST");
  add_check("attribute_value");
#endif
  strcpy(AL_STR(ptr), p);
  if(flags != NOTHING)
    AL_FLAGS(ptr) |= flags;
  AL_NEXT(ptr) = next;
  return ptr;
}

void atr_new_add(thing, atr, s, player, flags)
    dbref thing;
    char *atr;
    char *s;
    dbref player;
    dbref flags;
{
  s = (char *) compress(s);
  db[thing].list = AL_MAKE(atr, db[thing].list, s, player, flags);
}

int atr_add(thing, atr, s, player, flags)
    dbref thing;
    char *atr;
    char *s;
    dbref player;
    dbref flags;
{
  ALIST *ptr;
  dbref privs;

  if (thing == 0) {
	  privs = GOD;	/* should only be hit by first couple objects */
	  db[player].owner = GOD;
  }  
  else
	  privs = db[player].owner;

  if(!s) s = (char *)"";

  for (ptr = db[thing].list;
       (ptr && string_compare(atr, AL_NAME(ptr)));
       ptr = AL_NEXT(ptr));
  if(!*s) {
    if(ptr)
      if(!Wizard(privs) &&
         ((AL_FLAGS(ptr) & AF_WIZARD) ||
	  ((privs != db[AL_CREATOR(ptr)].owner) &&
           (AL_FLAGS(ptr) & AF_LOCKED)))) {
	return -1;
      } else {
        AL_DISPOSE(ptr);
        return 1;
      }
    else
      return 0;
  }
  s = (char *) compress(s);
  if(!ptr) {
    db[thing].list = AL_MAKE(atr, db[thing].list, s, privs, flags);
    return 1;
  } else {
    if(!Wizard(privs) &&
       ((AL_FLAGS(ptr) & AF_WIZARD) ||
        ((privs != db[AL_CREATOR(ptr)].owner) &&
         (AL_FLAGS(ptr) & AF_LOCKED)))) {
      return -1;
    } else {
      const char *p;
#ifdef USE_NALLOC
      na_unalloc(db_strings, (char *) AL_STR(ptr));
#else
      free((char *) AL_STR(ptr));
#endif
#ifdef MEM_CHECK
      del_check("attribute_value");
#endif
      p = compress(s);
#ifdef USE_NALLOC
      AL_STR(ptr) = (char *)na_ualloc(db_strings, strlen(p)+1);
#else
      AL_STR(ptr) = (char *)malloc(strlen(p)+1);
#endif
#ifdef MEM_CHECK
      add_check("attribute_value");
#endif
      strcpy(AL_STR(ptr), p);
      AL_CREATOR(ptr) = db[privs].owner;
      if(flags != NOTHING)
	AL_FLAGS(ptr) = flags;
      if(AL_BAD(ptr))
        AL_FLAGS(ptr) &= ~AF_NUKED;
      return 1;
    }
  }
  /*NOTREACHED*/
  return 0;
}

ATTR *atr_get(thing, atr)
    dbref thing;
    char *atr;
{
  ALIST *ptr;

  if(thing == NOTHING || !atr) return NULL;

  for(ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr)) {
    if(!AL_BAD(ptr) && !string_compare(AL_NAME(ptr), atr)) {
      return AL_ATTR(ptr);
    }
  }

  for(ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr)) {
    if(!AL_BAD(ptr) && string_prefix(AL_NAME(ptr), atr)) {
      return(AL_ATTR(ptr));
    }
  }
  return (ATTR *)NULL;
}

void free_attrib(thisattr)
    ATTR *thisattr;
{
	if (thisattr) {
#ifdef USE_NALLOC
		if (thisattr->name)
			na_unalloc(db_strings, (char *) thisattr->name);
		if (thisattr->value)
			na_unalloc(db_strings, (char *) thisattr->value);
		na_unalloc(db_strings, (char *) thisattr);
#else
		if (thisattr->name)
			free((char *) thisattr->name);
		if (thisattr->value)
			free((char *) thisattr->value);
		free((char *) thisattr);
#endif
}

#ifdef MEM_CHECK
  del_check("attribute_name");
  del_check("attribute_value");
  del_check("attribute");
#endif
}

void atr_free(thing)
    dbref thing;
{
  ALIST *ptr, *next;

  for(ptr = db[thing].list; ptr; ptr = next) {
    next = AL_NEXT(ptr);
    free_attrib(AL_ATTR(ptr));
#ifdef USE_NALLOC
    na_unalloc(db_strings, (char *) ptr);
#else
    free((char *) ptr);
#endif
#ifdef MEM_CHECK
    del_check("ALIST");
#endif
  }
  db[thing].list = (ALIST *)NULL;
}

/* reconstruct an attribute list */
void atr_collect(thing)
    dbref thing;
{
  ALIST *ptr, *next;

  ptr = db[thing].list;
  db[thing].list = NULL;

  while(ptr) {
    if(!AL_BAD(ptr)) {
      db[thing].list = AL_MAKE(AL_NAME(ptr), db[thing].list,
			       AL_STR(ptr), AL_CREATOR(ptr), AL_FLAGS(ptr));
    }
    next = AL_NEXT(ptr);
    free_attrib(AL_ATTR(ptr));
#ifdef USE_NALLOC
    na_unalloc(db_strings, (char *)ptr);
#else
    free((char *) ptr);
#endif
#ifdef MEM_CHECK
    del_check("ALIST");
#endif
    ptr = next;
  }
}

void atr_cpy(dest, source)
    dbref dest, source;
{
  ALIST *ptr;
  ptr = db[source].list;

  db[dest].list = NULL;
  while(ptr) {
    if(!AL_BAD(ptr)) {
      db[dest].list = AL_MAKE(AL_NAME(ptr), db[dest].list,
			      AL_STR(ptr), AL_CREATOR(ptr), AL_FLAGS(ptr));
    }
    ptr = AL_NEXT(ptr);
  }
}

const char *convert_atr(oldatr)
    dbref oldatr;
{
  const static char result[MAX_COMMAND_LEN];
  int factor = 0;

  switch(oldatr) {
    case A_OSUCC:
      return "OSUCCESS";
    case A_OFAIL:
      return "OFAILURE";
    case A_FAIL:
      return "FAILURE";
    case A_SUCC:
      return "SUCCESS";
    case A_PASS:
      return "XYXXY";
    case A_DESC:
      return "DESCRIBE";
    case A_SEX:
      return "SEX";
    case A_ODROP:
      return "ODROP";
    case A_DROP:
      return "DROP";
    case A_OKILL:
      return "OKILL";
    case A_KILL:
      return "KILL";
    case A_ASUCC:
      return "ASUCCESS";
    case A_AFAIL:
      return "AFAILURE";
    case A_ADROP:
      return "ADROP";
    case A_AKILL:
      return "AKILL";
    case A_USE:
      return "DOES";
    case A_CHARGES:
      return "CHARGES";
    case A_RUNOUT:
      return "RUNOUT";
    case A_STARTUP:
      return "STARTUP";
    case A_ACLONE:
      return "ACLONE";
    case A_APAY:
      return "APAYMENT";
    case A_OPAY:
      return "OPAYMENT";
    case A_PAY:
      return "PAYMENT";
    case A_COST:
      return "COST";
    case A_RAND:
      return "RAND";
    case A_LISTEN:
      return "LISTEN";
    case A_AAHEAR:
      return "AAHEAR";
    case A_AMHEAR:
      return "AMHEAR";
    case A_AHEAR:
      return "AHEAR";
    case A_LAST:
      return "LAST";
    case A_QUEUE:
      return "QUEUE";
    case A_IDESC:
      return "IDESCRIBE";
    case A_ENTER:
      return "ENTER";
    case A_OXENTER:
      return "OXENTER";
    case A_AENTER:
      return "AENTER";
    case A_ADESC:
      return "ADESCRIBE";
    case A_ODESC:
      return "ODESCRIBE";
    case A_RQUOTA:
      return "RQUOTA";
    case A_ACONNECT:
      return "ACONNECT";
    case A_ADISCONNECT:
      return "ADISCONNECT";
    case A_LEAVE:
      return "LEAVE";
    case A_ALEAVE:
      return "ALEAVE";
    case A_OLEAVE:
      return "OLEAVE";
    case A_OENTER:
      return "OENTER";
    case A_OXLEAVE:
      return "OXLEAVE";
    default:
      if(oldatr >= 100 && oldatr < 126)
	factor = 0;
      else if(oldatr >= 126 && oldatr < 152)
	factor = 1;
      else if(oldatr >= 152 && oldatr < 178)
	factor = 2;
      else {
	fprintf(stderr,
		"ERROR: Invalid attribute number in convert_atr. aborting.\n");
	fflush(stderr);
	abort();
      }
      sprintf((char *)result, "%c%c",
	      'V'+factor, oldatr - (100 + (factor * 26)) + 'A');
      return result;
  }
  /*NOTREACHED*/
  return "";
}

ATTR *atr_match(string)
  char *string;
{
  ATTR *a = attr;

  while (a->name) {
   if(!string_compare(string, a->name)) {
     return a;
   }
   a++;
  }
  return (ATTR *)NULL;
}

int atr_comm_match(thing, player, type, end, str)
    dbref thing, player;
    char type, end;
    char *str;
{
  ALIST *ptr;
  int match = 0;
  char tbuf1[BUFFER_LEN];
  char *s;

  if(thing < 0 || thing >= db_top)
    return 0;

  for(ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr)) {
    if(!AL_BAD(ptr) && (*AL_STR(ptr) == type) && !(AL_FLAGS(ptr) & AF_NOPROG)){
      strcpy(tbuf1, uncompress(AL_STR(ptr)));
      for(s = tbuf1 + 1; *s && (*s != end); s++);
      if(!*s)
        continue;
      *s++ = '\0';
      if(wild_match(tbuf1 + 1, str)) {
	match = 1;
	if ((type == '$') &&
	    (!eval_boolexp(player, db[thing].usekey, thing, 0, USELOCK)))
	  notify(player, "Permission denied.");
	else
	  parse_que(thing, s, player);
      }
    }
  }
  return match;
}

void do_atrlock(player, arg1, arg2)
    dbref player;
    const char *arg1, *arg2;
{
  dbref thing;
  char *p;
  ALIST *ptr;
  int status;

  if(!arg2 || !*arg2)
    status = 0;
  else {
    if(!string_compare(arg2, "on")) {
      status = 1;
    } else if(!string_compare(arg2, "off")) {
      status = 2;
    } else
     status = 0;
  }

  if(!arg1 || !*arg1) {
    notify(player, "You need to give an object/attribute pair.");
    return;
  }

  if(!(p = index(arg1, '/')) || !(*(p+1))) {
    notify(player, "You need to give an object/attribute pair.");
    return;
  }
  *p++ = '\0';

  init_match(player, arg1, NOTYPE);
  match_everything();
  if((thing = noisy_match_result()) == NOTHING)
    return;

  for(ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr))
    if(!AL_BAD(ptr) && !string_compare(AL_NAME(ptr), p))
      break;

  if(!ptr)
    for(ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr))
      if(!AL_BAD(ptr) && string_prefix(AL_NAME(ptr), p))
	break;

  if(ptr) {
    if(!status) {
      notify(player, tprintf("That attribute is %slocked.",
			     (AL_FLAGS(ptr) & AF_LOCKED) ? "" : "un"));
      return;
    } else if(!Wizard(player) &&
	      (db[AL_CREATOR(ptr)].owner != db[player].owner)) {
      notify(player, "You need to own the attribute to change its lock.");
      return;
    } else {
      if(status == 1) {
        AL_FLAGS(ptr) |= AF_LOCKED;
        notify(player, "Attribute locked.");
	return;
      } else if(status == 2) {
	AL_FLAGS(ptr) &= ~AF_LOCKED;
	notify(player, "Attribute unlocked.");
	return;
      } else {
	notify(player, "Invalid status on atrlock.. Notify god.");
	return;
      }
    }
  } else
    notify(player, "No such attribute.");
  return;
}

void do_atrchown(player, arg1, arg2)
    dbref player;
    const char *arg1, *arg2;
{
  dbref thing, new_owner;
  char *p;
  ALIST *ptr;

  if(!arg1 || !*arg1) {
    notify(player, "You need to give an object/attribute pair.");
    return; 
  }

  if(!(p = index(arg1, '/')) || !(*(p+1))) {
    notify(player, "You need to give an object/attribute pair.");
    return;
  }
  *p++ = '\0';

  init_match(player, arg1, NOTYPE);
  match_everything();
  if((thing = noisy_match_result()) == NOTHING)
    return;

  if((!arg2 && !*arg2) || !string_compare(arg2, "me"))
    new_owner = player;
  else
    new_owner = lookup_player(arg2);

  if(new_owner == NOTHING) {
    notify(player, "I can't find that player");
    return;
  }

  for(ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr))
    if(!AL_BAD(ptr) && !string_compare(AL_NAME(ptr), p))
      break;

  if(!ptr)
    for(ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr))
      if(!AL_BAD(ptr) && string_prefix(AL_NAME(ptr), p))
	break;

  if(ptr) {
    if((controls(player, thing) && !(AL_FLAGS(ptr) & AF_LOCKED)) ||
       (db[player].owner == db[AL_CREATOR(ptr)].owner)) {
      if(new_owner != db[thing].owner && !Wizard(player)) {
	notify(player,
         "You can only chown an attribute to the current owner of the object.");
	return;
      }
      AL_CREATOR(ptr) = db[new_owner].owner;
      notify(player, "Attribute owner changed.");
      return;
    } else {
      notify(player, "You don't have the permission to chown that.");
      return;
    }
  } else
    notify(player, "No such attribute.");
}

ATTR *atr_complete_match(player, atr, privs)
    dbref player;
    char *atr;
    dbref privs;
{
  ATTR *a;
  char *s = atr;
  dbref thing;

  if(*s == '_') s++;

  if((a = atr_get(player, s)) != NULL) {
    if(!controls(privs, player) &&
       db[privs].owner != db[a->creator].owner &&
       (a->flags & AF_ODARK)) {
      DOLIST(thing, db[player].contents) {
	if((a = atr_get(thing, s)) != NULL) {
	  if(!controls(privs, player) &&
             db[privs].owner != db[a->creator].owner &&
             (a->flags & AF_ODARK)) {
	    return NULL;
	  } else {
	    return a;
	  }
	}
      }
    } else {
      return a;
    }
  }
  return NULL;
}