nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
//*****************************************************************************
//
// utils.c
//
// This file contains all sorts of utility functions used
// all sorts of places in the code.
//
//*****************************************************************************
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <dirent.h> 
#include <time.h>

// include main header file
#include "mud.h"
#include "prototype.h"
#include "character.h"
#include "object.h"
#include "world.h"
#include "zone.h"
#include "room.h"
#include "room_reset.h"
#include "exit.h"
#include "socket.h"
#include "utils.h"
#include "save.h"
#include "handler.h"
#include "inform.h"
#include "event.h"
#include "action.h"
#include "hooks.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "items/items.h"



//*****************************************************************************
// implementation of utils.h
//*****************************************************************************
void  extract_obj_final(OBJ_DATA *obj) {
  obj_from_game(obj);
  deleteObj(obj);
}

void extract_obj(OBJ_DATA *obj) {
  // make sure we're not attached to anything
  CHAR_DATA *sitter = NULL;
  while( (sitter = (CHAR_DATA *)listGet(objGetUsers(obj), 0)) != NULL)
    char_from_furniture(sitter);

  OBJ_DATA *content = NULL;
  while( (content = (OBJ_DATA *)listGet(objGetContents(obj), 0)) != NULL)
    extract_obj(content);

  if(objGetRoom(obj))
    obj_from_room(obj);
  if(objGetWearer(obj))
    try_unequip(objGetWearer(obj), obj);
  if(objGetCarrier(obj))
    obj_from_char(obj);
  if(objGetContainer(obj))
    obj_from_obj(obj);

  if(!listIn(objs_to_delete, obj))
    listPut(objs_to_delete, obj);
}

void extract_mobile_final(CHAR_DATA *ch) {
  char_from_game(ch);
  if(charIsNPC(ch) || !player_exists(charGetName(ch)))
    deleteChar(ch);
  else
    unreference_player(ch);
}

void extract_mobile(CHAR_DATA *ch) {
  // unequip everything the character is wearing
  // and send it to inventory
  unequip_all(ch);
  // extract everything in the character's inventory
  LIST_ITERATOR *obj_i = newListIterator(charGetInventory(ch));
  OBJ_DATA        *obj = NULL;
  ITERATE_LIST(obj, obj_i) {
    extract_obj(obj);
  } deleteListIterator(obj_i);

  // make sure we're not attached to anything
  if(charGetFurniture(ch))
    char_from_furniture(ch);
  if(charGetRoom(ch))
    char_from_room(ch);

  // make sure we close down the socket
  if(charGetSocket(ch) != NULL) {
    close_socket(charGetSocket(ch), FALSE);
    charSetSocket(ch, NULL);
  }

  if(!listIn(mobs_to_delete, ch))
    listPut(mobs_to_delete, ch);
}

void extract_room_final(ROOM_DATA *room) {
  room_from_game(room);
  deleteRoom(room);
}

void extract_room(ROOM_DATA *room) {
  roomSetExtracted(room);

  // extract all of the objects characters in us
  CHAR_DATA *ch = NULL;
  while( (ch = listGet(roomGetCharacters(room), 0)) != NULL)
    extract_mobile(ch);

  // and the objects
  OBJ_DATA *obj = NULL;
  while( (obj = listGet(roomGetContents(room), 0)) != NULL)
    extract_obj(obj);

  // remove us from the world we're in ... the if should always be true if
  // we are in fact part of the world.
  if(worldRoomLoaded(gameworld, roomGetClass(room)) &&
     worldGetRoom(gameworld, roomGetClass(room)) == room)
    worldRemoveRoom(gameworld, roomGetClass(room));

  // If anyone's last room was us, make sure we set the last room to NULL
  LIST_ITERATOR *ch_i = newListIterator(mobile_list);
  ITERATE_LIST(ch, ch_i) {
    if(charGetLastRoom(ch) == room)
      charSetLastRoom(ch, NULL);
  } deleteListIterator(ch_i);

  if(!listIn(rooms_to_delete, room))
    listPut(rooms_to_delete, room);
}

void communicate(CHAR_DATA *dMob, char *txt, int range)
{
  switch(range) {
  default:
    bug("Communicate: Bad Range %d.", range);
    return;

  case COMM_LOG:
    send_to_groups("admin, scripter", "[LOG: %s]\r\n", txt);
    break;
  }
}


/*
 * load the world and its inhabitants, as well as other misc game data
 */
void load_muddata() {  
  if(gameworld == NULL) {
    log_string("ERROR: Could not boot game world.");
    abort();
  }

  worldAddType(gameworld, "mproto", protoRead,  protoStore,  deleteProto, 
	       protoSetKey);
  worldAddType(gameworld, "oproto", protoRead,  protoStore,  deleteProto,
	       protoSetKey);
  worldAddType(gameworld, "rproto", protoRead,  protoStore,  deleteProto,
	       protoSetKey);
  worldAddType(gameworld, "reset",  resetListRead, resetListStore, 
	       deleteResetList, resetListSetKey);

  worldSetPath(gameworld, WORLD_PATH);
  worldInit(gameworld);

  greeting = read_file("../lib/txt/greeting");
  motd     = read_file("../lib/txt/motd");
}


char *get_time()
{
  static char buf[16];
  char *strtime;
  int i;

  strtime = ctime(&current_time);
  for (i = 0; i < 15; i++)   
    buf[i] = strtime[i + 4];
  buf[15] = '\0';

  return buf;
}

// try entering into the game
// disconnect the socket if we fail.
bool try_enter_game(CHAR_DATA *ch) {
  // find the loadroom
  ROOM_DATA *loadroom = worldGetRoom(gameworld, charGetLoadroom(ch));

  // if the loadroom doesn't exist, 
  // try loading the character to the start room
  if(loadroom == NULL) {
    send_to_char(ch, 
		 "Your loadroom seems to be missing. Attempting to "
		 "load you to the start room.\r\n");
    log_string("ERROR: %s had invalid loadroom, %s", 
	       charGetName(ch), charGetLoadroom(ch));
    loadroom = worldGetRoom(gameworld, START_ROOM);
  }
  
  // we have a loadroom... all is good
  if(loadroom != NULL) {
    // add him to the game
    char_to_game(ch);
    char_to_room(ch, loadroom);
    return TRUE;
  }

  // oops! No rooms seem to exist
  else {
    send_to_char(ch,
		 "The gameworld was not properly loaded. The MUD may "
		 "be going through maintenance. Try again later.\r\n");
    log_string("ERROR: When loading %s, start room did not exist!",
	       charGetName(ch));

    return FALSE;
  }
}

CHAR_DATA *check_reconnect(const char *player) {
  CHAR_DATA *dMob;
  LIST_ITERATOR *mob_i = newListIterator(mobile_list);

  ITERATE_LIST(dMob, mob_i) {
    if (!charIsNPC(dMob) && charIsName(dMob, player)) {
      if (charGetSocket(dMob)) {
        close_socket(charGetSocket(dMob), TRUE);
	charSetSocket(dMob, NULL);
      }
      break;
    }
  } deleteListIterator(mob_i);
  return dMob;
}

void do_mass_transfer(ROOM_DATA *from, ROOM_DATA *to, bool chars, bool mobs,
		      bool objs) {
  if(chars || mobs) {
    LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(from));
    CHAR_DATA       *ch = NULL;
    ITERATE_LIST(ch, ch_i) {
      if(mobs || (chars && !charIsNPC(ch))) {
	char_from_room(ch);
	char_to_room(ch, to);
      }
    } deleteListIterator(ch_i);
  }

  if(objs) {
    LIST_ITERATOR *obj_i = newListIterator(roomGetContents(from));
    OBJ_DATA        *obj = NULL;
    ITERATE_LIST(obj, obj_i) {
      obj_from_room(obj);
      obj_to_room(obj, to);
    } deleteListIterator(obj_i);
  }
}

const char *exitGetToFull(EXIT_DATA *exit) {
  if(exitGetRoom(exit) == NULL)
    return exitGetTo(exit);
  else
    return get_fullkey_relative(exitGetTo(exit), get_key_locale(roomGetClass(exitGetRoom(exit))));
}

ROOM_DATA *objGetRootRoom(OBJ_DATA *obj) {
  if(objGetCarrier(obj))
    return charGetRoom(objGetCarrier(obj));
  else if(objGetWearer(obj))
    return charGetRoom(objGetWearer(obj));
  else if(objGetContainer(obj))
    return objGetRootRoom(objGetContainer(obj));
  return objGetRoom(obj);
}


int can_see_hidden(CHAR_DATA *ch) {
  return 0;
}

int can_see_invis(CHAR_DATA *ch) {
  return 0;
}

LIST *char_see_checks = NULL;
LIST *obj_see_checks  = NULL;
LIST *exit_see_checks = NULL;

void register_char_see( bool (* check)(CHAR_DATA *ch, CHAR_DATA *target)) {
  if(char_see_checks == NULL)
    char_see_checks = newList();
  listPut(char_see_checks, check);
}

void register_obj_see( bool (* check)(CHAR_DATA *ch, OBJ_DATA *target)) {
  if(obj_see_checks == NULL)
    obj_see_checks = newList();
  listPut(obj_see_checks, check);
}

void register_exit_see(bool (* check)(CHAR_DATA *ch, EXIT_DATA *target)) {
  if(exit_see_checks == NULL)
    exit_see_checks = newList();
  listPut(exit_see_checks, check);
}

bool  can_see_char(CHAR_DATA *ch, CHAR_DATA *target) {
  if(ch == target)
    return TRUE;
  if(poscmp(charGetPos(ch), POS_SLEEPING) <= 0)
    return FALSE;

  bool ret = TRUE;
  if(char_see_checks != NULL) {
    LIST_ITERATOR *chk_i = newListIterator(char_see_checks);
    bool (*chk)(CHAR_DATA *, CHAR_DATA *) = NULL;
    ITERATE_LIST(chk, chk_i) {
      ret = chk(ch, target);
      if(ret == FALSE)
	break;
    } deleteListIterator(chk_i);
  }
  return ret;
}

bool can_see_obj(CHAR_DATA *ch, OBJ_DATA  *target) {
  if(poscmp(charGetPos(ch), POS_SLEEPING) <= 0)
    return FALSE;

  bool ret = TRUE;
  if(obj_see_checks != NULL) {
    LIST_ITERATOR *chk_i = newListIterator(obj_see_checks);
    bool (*chk)(CHAR_DATA *, OBJ_DATA *) = NULL;
    ITERATE_LIST(chk, chk_i) {
      ret = chk(ch, target);
      if(ret == FALSE)
	break;
    } deleteListIterator(chk_i);
  }
  return ret;
}

bool can_see_exit(CHAR_DATA *ch, EXIT_DATA *target) {
  if(poscmp(charGetPos(ch), POS_SLEEPING) <= 0)
    return FALSE;

  bool ret = TRUE;
  if(exit_see_checks != NULL) {
    LIST_ITERATOR *chk_i = newListIterator(exit_see_checks);
    bool (*chk)(CHAR_DATA *, EXIT_DATA *) = NULL;
    ITERATE_LIST(chk, chk_i) {
      ret = chk(ch, target);
      if(ret == FALSE)
	break;
    } deleteListIterator(chk_i);
  }
  return ret;
}

const char *see_exit_as(CHAR_DATA *ch, EXIT_DATA *target) {
  if(!can_see_exit(ch, target))
    return SOMEWHERE;
  else {
    BUFFER     *buf = newBuffer(1);
    const char *dir = NULL;
    ROOM_DATA *dest = NULL;

    if(exitGetRoom(target))
      dir = roomGetExitDir(exitGetRoom(target), target);

    // build up what the exit looks like
    if(exitIsClosed(target)) {
      if(*exitGetName(target))
	bprintf(buf, exitGetName(target));
      else
	bprintf(buf, "closed");
    }
    else if( (dest = worldGetRoom(gameworld, exitGetToFull(target))) != NULL)
      bprintf(buf, "%s", roomGetName(dest));
    else
      bprintf(buf, "%s", SOMEWHERE);

    listPush(bufs_to_delete, buf);
    return bufferString(buf);
  }
}

const char *see_char_as(CHAR_DATA *ch, CHAR_DATA *target) {
  if(!can_see_char(ch, target))
    return SOMEONE;
  // put the interaction commands around our name
  else
    return charGetName(target);
}

//
// finds the UID of the room the object is in
int find_obj_room(OBJ_DATA *obj) {
  if(objGetRoom(obj))
    return roomGetUID(objGetRoom(obj));
  else if(objGetCarrier(obj))
    return roomGetUID(charGetRoom(objGetCarrier(obj)));
  else if(objGetWearer(obj))
    return roomGetUID(charGetRoom(objGetWearer(obj)));
  else if(objGetContainer(obj))
    return find_obj_room(objGetContainer(obj));
  return NOWHERE;
}

const char *see_obj_as(CHAR_DATA *ch, OBJ_DATA  *target) {
  if(!can_see_obj(ch, target))
    return SOMETHING;
  else 
    return objGetName(target);
}

//
// returns a name as a UID, or -1 if it does not translate
int name_as_uid(const char *name) {
  if(name == NULL)
    return NOBODY;
  int i = 0, uid = NOBODY;
  for(; name[i] != '\0'; i++)
    if(!isdigit(name[i]))
      return NOBODY;
  sscanf(name, "%d", &uid);
  return uid;
}

int count_objs(CHAR_DATA *looker, LIST *list, const char *name, 
	       const char *prototype, bool must_see) {
  LIST_ITERATOR *obj_i = newListIterator(list);
  OBJ_DATA *obj;
  int count = 0;
  int   uid = name_as_uid(name);

  ITERATE_LIST(obj, obj_i) {
    if(must_see && !can_see_obj(looker, obj))
      continue;
    // if we have a name, search by it
    if(objGetUID(obj) == uid || (name && *name && objIsName(obj, name)))
      count++;
    // otherwise search by prototype
    else if(prototype && *prototype && objIsInstance(obj, prototype))
      count++;
  }
  deleteListIterator(obj_i);
  return count;
}

int count_chars(CHAR_DATA *looker, LIST *list, const char *name,
		const char *prototype, bool must_see) {
  LIST_ITERATOR *char_i = newListIterator(list);
  CHAR_DATA *ch;
  int count = 0;
  int   uid = name_as_uid(name);

  ITERATE_LIST(ch, char_i) {
    if(must_see && !can_see_char(looker, ch))
      continue;
    // if we have a name, search by it
    if((name && *name && charIsName(ch, name)) || charGetUID(ch) == uid)
      count++;
    // otherwise, search by prototype
    else if(prototype && *prototype && charIsInstance(ch, prototype))
      count++;
  } deleteListIterator(char_i);

  return count;
}

//
// find the numth occurance of the character with name, "name"
// if name is not supplied, search by prototype. If name is supplied as m.XXX,
// then search by the mob's UID
//
CHAR_DATA *find_char(CHAR_DATA *looker, LIST *list, int num, const char *name,
		     const char *prototype, bool must_see) {
  if(num <= 0)
    return NULL;
  CHAR_DATA *ch = NULL;

  // is everything in the description a number? Search by UID
  int uid = name_as_uid(name);
  if(uid != NOBODY) {
    ch = propertyTableGet(mob_table, uid);
    if(listIn(list, ch) && can_see_char(looker, ch))
      return ch;
    return NULL;
  }

  LIST_ITERATOR *char_i = newListIterator(list);
  ITERATE_LIST(ch, char_i) {
    if(must_see && !can_see_char(looker, ch))
      continue;
    // if we have a name, search by name
    if(name && *name && charIsName(ch, name))
      num--;
    // otherwise search by prototype
    else if(prototype && *prototype && charIsInstance(ch, prototype))
      num--;
    if(num == 0)
      break;
  } deleteListIterator(char_i);
  return ch;
}


//
// find the numth occurance of the object with name, "name"
// if name is not supplied, search by prototype
//
OBJ_DATA *find_obj(CHAR_DATA *looker, LIST *list, int num, 
		   const char *name, const char *prototype, bool must_see) {
  if(num == 0)
    return NULL;
  OBJ_DATA *obj = NULL;

  // is everything in the description a number? Search by UID
  int uid = name_as_uid(name);
  if(uid != NOTHING) {
    obj = propertyTableGet(obj_table, uid);
    if(listIn(list, obj) && can_see_obj(looker, obj))
      return obj;
    return NULL;
  }

  LIST_ITERATOR *obj_i = newListIterator(list);
  ITERATE_LIST(obj, obj_i) {
    if(must_see && !can_see_obj(looker, obj))
      continue;
    // if a name is supplied, search by name
    if(name && *name && objIsName(obj, name))
      num--;
    // otherwise, search by prototype
    else if(prototype && *prototype && objIsInstance(obj, prototype))
      num--;
    if(num == 0)
      break;
  } deleteListIterator(obj_i);
  return obj;
}


//
// Find all characters in the list with the given name. Or all characters
// if name is NULL
//
LIST *find_all_chars(CHAR_DATA *looker, LIST *list, const char *name,
		     const char *prototype, bool must_see) {
  LIST_ITERATOR *char_i = newListIterator(list);
  LIST *char_list = newList();
  int         uid = name_as_uid(name);
  CHAR_DATA *ch;

  ITERATE_LIST(ch, char_i) {
    if(must_see && !can_see_char(looker, ch))
      continue;
    if((name && (!*name || charIsName(ch, name))) || charGetUID(ch) == uid)
      listPut(char_list, ch);
    else if(prototype && *prototype && charIsInstance(ch, prototype))
      listPut(char_list, ch);
  } deleteListIterator(char_i);
  return char_list;
}


//
// Returns a llist of all the chars found with a specific name in the list.
// the list must be deleted after use.
//
LIST *find_all_objs(CHAR_DATA *looker, LIST *list, const char *name, 
		    const char *prototype, bool must_see) {

  LIST_ITERATOR *obj_i = newListIterator(list);
  LIST       *obj_list = newList();
  int              uid = name_as_uid(name);
  OBJ_DATA *obj;

  ITERATE_LIST(obj, obj_i) {
    if(must_see && !can_see_obj(looker, obj))
      continue;
    if((name && (!*name || objIsName(obj, name))) || objGetUID(obj) == uid)
      listPut(obj_list, obj);
    else if(prototype && *prototype && objIsInstance(obj, prototype))
      listPut(obj_list, obj);
  } deleteListIterator(obj_i);
  return obj_list;
}

//
// separates a joint item-count (e.g. 2.sword, all.woman) into its
// count and its word
//
void get_count(const char *buf, char *target, int *count) {
  // hmmm... we can probably condense these two checks into one, non?
  if(!strncasecmp(buf, "all.", 4)) {
    //sscanf(buf, "all.%s", target);
    strcpyto(target, buf+4, '\0');
    *count = COUNT_ALL;
  }
  else if(!strcasecmp(buf, "all")) {
    *target = '\0';
    *count = COUNT_ALL;
  }
  else if(!strstr(buf, ".")) {
    strcpyto(target, buf, '\0');
    //sscanf(buf, "%s", target);
    *count = 1;
  }
  else {
    //*count = 1;
    //strcpyto(target, buf, '\0');
    sscanf(buf, "%d", count);
    strcpyto(target, buf+next_letter_in(buf, '.')+1, '\0');
  }
}


//
// concats an item count and a target into a single term
//
void print_count(char *buf, const char *target, int count) {
  if(count > 1)               
    sprintf(buf, "%d.%s", count, target);
  else if(count == COUNT_ALL && *target)
    sprintf(buf, "all.%s", target);
  else if(count == COUNT_ALL)
    sprintf(buf, "all");
  else          
    sprintf(buf, "%s", target);
}


//
// Calculates how many letters until we hit the next one
// of a specified type
//
int next_letter_in(const char *string, char marker) {
  int i = 0;
  for(i = 0; string[i] != '\0'; i++)
    if(string[i] == marker)
      return i;
  return -1; // none found
}


//
// Calculates how many characters until we hit the next whitespace. Newlines,
// tabs, and spaces are treated as whitespace.
int next_space_in(const char *string) {
  int i = 0;
  for(i = 0; string[i] != '\0'; i++)
    if(isspace(string[i]))
      return i;
  return -1; // none found
}

//
// If we're at the beginning of a new paragraph, return where the new paragraph
// starts. Otherwise, return our current position (index)
int is_paragraph_marker(const char *string, int index) {
  int nl_count = 0;
  int i = 0;
  for(i = index; isspace(string[i]); i++) {
    if(string[i] == '\n')
      nl_count++;
  }
  if(nl_count > 1)
    return i;
  else
    return index;
}

//
// hashing array 1 for pearson hashing
int pearson_table1[] = { 66, 93, 11, 153, 155, 113, 214, 132, 91, 193, 240, 82, 175, 145, 84, 34, 76, 217, 250, 230, 139, 172, 65, 254, 196, 56, 165, 116, 48, 219, 199, 142, 35, 27, 210, 149, 45, 127, 41, 150, 85, 87, 253, 100, 234, 216, 192, 226, 154, 106, 78, 146, 131, 38, 120, 151, 177, 29, 50, 231, 68, 168, 227, 161, 126, 141, 36, 191, 110, 81, 197, 190, 9, 236, 140, 0, 20, 162, 23, 189, 42, 130, 117, 86, 243, 123, 237, 249, 64, 135, 61, 167, 39, 57, 96, 148, 118, 13, 235, 188, 19, 71, 49, 115, 21, 182, 15, 200, 179, 251, 75, 77, 204, 32, 180, 16, 218, 22, 171, 88, 30, 248, 47, 238, 105, 94, 92, 67, 28, 69, 33, 215, 1, 7, 241, 109, 209, 98, 12, 208, 156, 52, 195, 89, 185, 55, 170, 104, 17, 173, 122, 138, 4, 202, 136, 247, 169, 222, 163, 211, 144, 252, 2, 186, 201, 40, 207, 107, 18, 24, 46, 129, 44, 14, 174, 26, 124, 194, 37, 223, 102, 183, 99, 114, 70, 158, 53, 111, 147, 119, 73, 152, 79, 203, 157, 221, 10, 97, 133, 62, 229, 178, 205, 184, 164, 176, 198, 80, 58, 245, 31, 59, 128, 101, 60, 181, 246, 232, 63, 143, 121, 213, 187, 206, 43, 134, 6, 225, 228, 72, 54, 233, 224, 5, 8, 239, 112, 244, 255, 137, 3, 74, 108, 159, 83, 125, 103, 51, 220, 25, 166, 90, 160, 212, 95, 242 };

int pearson_table2[] = { 202, 142, 134, 22, 233, 237, 13, 31, 41, 97, 141, 148, 74, 165, 7, 162, 53, 117, 210, 226, 174, 88, 5, 163, 17, 49, 170, 99, 93, 39, 69, 108, 207, 244, 254, 101, 159, 30, 188, 67, 235, 150, 24, 136, 208, 221, 234, 43, 96, 12, 78, 10, 25, 81, 239, 120, 37, 21, 42, 183, 121, 213, 14, 161, 137, 23, 9, 255, 245, 209, 222, 236, 119, 199, 216, 71, 115, 110, 63, 107, 173, 70, 20, 89, 91, 102, 227, 1, 177, 113, 104, 111, 253, 181, 95, 243, 72, 6, 124, 131, 190, 86, 164, 85, 251, 3, 50, 154, 217, 155, 8, 105, 82, 28, 75, 147, 0, 80, 252, 140, 29, 2, 242, 160, 57, 34, 247, 167, 47, 126, 144, 168, 18, 231, 44, 58, 206, 77, 125, 171, 36, 76, 98, 26, 241, 180, 61, 151, 194, 15, 45, 84, 212, 32, 196, 92, 192, 139, 112, 229, 100, 109, 189, 248, 156, 94, 153, 145, 127, 146, 175, 138, 215, 195, 198, 128, 182, 218, 19, 250, 132, 40, 214, 103, 83, 73, 106, 133, 135, 186, 123, 219, 158, 38, 152, 232, 116, 64, 172, 52, 200, 204, 240, 224, 203, 249, 59, 157, 114, 191, 230, 48, 166, 122, 65, 228, 184, 4, 205, 220, 225, 62, 169, 79, 27, 197, 11, 87, 193, 179, 223, 56, 68, 178, 187, 149, 143, 16, 55, 35, 201, 118, 185, 51, 66, 33, 54, 176, 60, 46, 211, 246, 238, 130, 90, 129 };

int pearson_table3[] = { 44, 181, 139, 127, 174, 243, 236, 14, 5, 200, 235, 180, 195, 185, 193, 116, 161, 110, 72, 121, 9, 3, 104, 224, 136, 182, 15, 94, 222, 84, 186, 20, 239, 147, 21, 34, 183, 93, 61, 164, 189, 89, 17, 226, 56, 205, 176, 51, 128, 201, 73, 75, 190, 163, 96, 178, 102, 33, 150, 130, 98, 76, 120, 240, 79, 158, 24, 196, 78, 4, 202, 168, 48, 255, 227, 97, 138, 101, 207, 170, 58, 27, 45, 225, 179, 215, 251, 123, 209, 38, 63, 36, 6, 175, 62, 208, 86, 65, 107, 188, 249, 146, 198, 194, 254, 230, 253, 103, 46, 133, 192, 26, 108, 219, 191, 77, 137, 81, 145, 66, 91, 106, 7, 10, 29, 95, 165, 88, 119, 151, 40, 22, 1, 122, 114, 134, 37, 214, 217, 68, 0, 83, 199, 70, 80, 156, 140, 144, 90, 118, 42, 173, 111, 157, 204, 171, 212, 241, 53, 11, 148, 135, 206, 172, 23, 109, 13, 220, 245, 197, 177, 47, 59, 210, 49, 74, 54, 25, 234, 115, 43, 169, 218, 50, 87, 31, 143, 248, 69, 92, 160, 141, 41, 228, 184, 233, 117, 2, 231, 252, 28, 216, 125, 153, 18, 100, 166, 8, 52, 124, 250, 132, 67, 244, 12, 246, 32, 64, 213, 242, 247, 223, 82, 131, 112, 221, 113, 155, 71, 211, 60, 129, 149, 237, 30, 85, 55, 152, 229, 35, 232, 187, 19, 105, 203, 39, 159, 238, 167, 162, 57, 126, 99, 154, 142, 16 };

int pearson_table4[] = { 231, 223, 174, 160, 113, 173, 104, 49, 122, 229, 13, 8, 232, 81, 38, 90, 64, 131, 123, 176, 33, 84, 166, 241, 16, 190, 159, 144, 189, 193, 66, 137, 163, 162, 111, 26, 15, 9, 1, 119, 255, 136, 167, 178, 94, 130, 114, 32, 110, 99, 78, 153, 239, 179, 243, 249, 133, 76, 86, 242, 165, 169, 19, 155, 152, 67, 215, 220, 36, 24, 213, 105, 117, 168, 128, 204, 248, 238, 177, 161, 222, 205, 108, 186, 116, 51, 230, 68, 181, 58, 39, 47, 71, 211, 209, 101, 251, 164, 60, 10, 12, 83, 180, 50, 172, 55, 158, 20, 70, 107, 147, 170, 91, 118, 40, 140, 3, 252, 184, 34, 146, 127, 28, 254, 150, 57, 228, 126, 192, 14, 253, 45, 227, 225, 30, 4, 17, 44, 95, 201, 18, 149, 124, 65, 129, 154, 195, 102, 212, 219, 6, 75, 132, 244, 52, 187, 240, 198, 85, 217, 185, 224, 208, 21, 250, 87, 216, 183, 245, 203, 79, 61, 100, 200, 246, 80, 138, 109, 120, 92, 247, 143, 202, 72, 134, 156, 207, 233, 218, 37, 23, 125, 82, 234, 56, 151, 106, 112, 197, 41, 88, 96, 171, 31, 175, 93, 182, 188, 135, 48, 148, 5, 226, 103, 29, 97, 139, 115, 0, 77, 89, 74, 35, 42, 157, 59, 69, 2, 62, 236, 142, 196, 145, 7, 54, 237, 206, 194, 73, 121, 98, 199, 27, 235, 11, 221, 46, 141, 43, 63, 25, 22, 210, 53, 191, 214 };

unsigned long pearson_hash8(const char *string, int *table) {
  unsigned long h = 0;
  for(; *string; string++)
    h = table[h ^ tolower(*string)];
  return h;
}

unsigned long pearson_hash8_1(const char *string) {
  return pearson_hash8(string, pearson_table1);
}

unsigned long pearson_hash8_2(const char *string) {
  return pearson_hash8(string, pearson_table2);
}

unsigned long pearson_hash8_3(const char *string) {
  return pearson_hash8(string, pearson_table3);
}

unsigned long pearson_hash8_4(const char *string) {
  return pearson_hash8(string, pearson_table4);
}

// concatinate two strings of 8 bits
unsigned long pearson_hash16_1(const char *string) {
  return pearson_hash8_1(string)  | (pearson_hash8_2(string) << 8);
}

// again, different hashing functions
unsigned long pearson_hash16_2(const char *string) {
  return pearson_hash8_3(string)  | (pearson_hash8_4(string) << 8);
}

// concatinate four strings of 8 bits
unsigned long pearson_hash32(const char *string) {
  return ((pearson_hash8_1(string))       | (pearson_hash8_2(string) << 8) |
	  (pearson_hash8_3(string) << 16) | (pearson_hash8_4(string) << 24));
}

//
// just a generic function for hashing a string. This could be 
// sped up tremendously if it's performance becoming a problem.
unsigned long string_hash(const char *key) {
  return pearson_hash32(key);
}

bool endswith(const char *string, const char *end) {
  int slen = strlen(string);
  int elen = strlen(end);
  return (slen >= elen && !strcasecmp(string + slen - elen, end));
}

bool startswith(const char *string, const char *start) {
  return !strncasecmp(string, start, strlen(start));
}

const char *strcpyto(char *to, const char *from, char end) {
  // copy everything up to end, and then delimit our destination buffer
  for(; *from != '\0' && *from != end; to++, from++)
    *to = *from;
  *to = '\0';

  // skip our end character and return whatever's left
  if(*from != '\0')
    from++;
  return from;
}

//
// counts how many times ch occurs in string
//
int count_letters(const char *string, const char ch, const int strlen) {

  int i, n;
  for(i = n = 0; i < strlen; i++)
    if(string[i] == ch)
      n++;

  return n;
}

//
// counts how many times word occurs in string. Assumes strlen(word) >= 1
//
int count_occurences(const char *string, const char *word) {
  int count = 0, i = 0, word_len = strlen(word);
  for(; string[i] != '\0'; i++) {
    if(!strncmp(string+i, word, word_len)) {
      count++;
      i += word_len;
    }
  }
  return count;
}

//
// return a pointer to the start of the line num (lines are ended with \n's).
// return NULL if the line does not exist
//
char *line_start(char *string, int line) {
  // skip forward to the appropriate line
  int i, count = 1;

  // are we looking for the start?
  if(line == 1) return string;

  for(i = 0; string[i] != '\0'; i++) {
    if(string[i] == '\n')
      count++;
    if(count == line)
      return string+i+1;
  }

  return NULL;
}


//
// trim trailing and leading whitespace
//
void trim(char *string) {
  int len = strlen(string);
  int max = len-1;
  int min = 0;
  int i;

  // kill all whitespace to the right
  for(max = len - 1; max >= 0; max--) {
    if(isspace(string[max]))
      string[max] = '\0';
    else
      break;
  }

  // find our first non-whitespace
  while(isspace(string[min]))
    min++;

  // shift everything to the left
  for(i = 0; i <= max-min; i++)
    string[i] = string[i+min];
  string[i] = '\0';
}


//
// Returns true if "word" is found in the comma-separated list of
// keywords
//
bool is_keyword(const char *keywords, const char *word, bool abbrev_ok) {
  int word_len = strlen(word);
  if(word_len < 1)
    return FALSE;

  // while we haven't reached the end of the string
  while(*keywords != '\0') {
    // skip all spaces and commas
    while(isspace(*keywords) || *keywords == ',')
      keywords = keywords+1;
    // figure out the length of the current keyword
    int keyword_len = next_letter_in(keywords, ',');
    if(keyword_len == -1)
      keyword_len = strlen(keywords);

    // see if we compare to the current keyword
    if(!abbrev_ok && !strncasecmp(keywords, word, keyword_len) &&
       keyword_len == word_len)
      return TRUE;
    if(abbrev_ok && !strncasecmp(keywords, word, word_len))
      return TRUE;
    // we didn't. skip this current keyword, and find
    // the start of the next keyword or the end of the string
    else while(!(*keywords == '\0' || *keywords == ','))
      keywords = keywords+1;
  }
  return FALSE;
}


//
// Return 0 if the head of the string does not match any of our
// keywords. Returns the length of the keyword if it does match
int find_keyword(const char *keywords, const char *string) {
  LIST           *words = parse_keywords(keywords);
  LIST_ITERATOR *word_i = newListIterator(words);
  char            *word = NULL;
  int               len = 0;

  // try to find the longest keyword
  ITERATE_LIST(word, word_i) {
    int word_len = strlen(word);
    if(!strncasecmp(word, string, word_len) && word_len > len)
      len = word_len;
  } deleteListIterator(word_i);
  deleteListWith(words, free);

  return len;
}

//
// returns whether or not a keyword exists twice in the list
bool dup_keywords_exist(const char *keywords) {
  LIST           *keys = parse_keywords(keywords);
  char            *key = NULL;
  bool       dup_found = FALSE;

  while( !dup_found && (key = listPop(keys)) != NULL) {
    if(listGetWith(keys, key, strcasecmp) != NULL)
      dup_found = TRUE;
    free(key);
  } deleteListWith(keys, free);

  return dup_found;
}

//
// Return a list of all the strings in this list. String are separated by the
// delimeter. The list and contents must be deleted after use.
LIST *parse_strings(const char *string, char delimeter) {
  // make our list that we will be returning
  LIST *list = newList();

  // first, we check if the string have any non-spaces
  int i;
  bool nonspace_found = FALSE;
  for(i = 0; string[i] != '\0'; i++) {
    if(!isspace(string[i])) {
      nonspace_found = TRUE;
      break;
    }
  }

  // we didn't find any non-spaces. Return NULL
  if(!nonspace_found)
    return list;

  // find all of our keywords
  while(*string != '\0') {
    i = 0;
    // find the endpoint
    while(string[i] != delimeter && string[i] != '\0')
      i++;

    char buf[i+1];
    strncpy(buf, string, i); buf[i] = '\0';
    trim(buf); // skip all whitespaces

    // make sure something still exists. If it does, queue it
    if(*buf != '\0')
      listQueue(list, strdup(buf));

    // skip everything we just copied, plus our delimeter
    string = &string[i+(string[i] != '\0' ? 1 : 0)];
  }

  // return whatever we found
  return list;
}


//
// return a list of all the unique keywords found in the keywords string
// keywords can be more than one word long (e.g. blue flame) and each
// keyword must be separated by a comma
LIST *parse_keywords(const char *keywords) {
  return parse_strings(keywords, ',');
}


//
// If the keyword does not already exist, add it to the keyword list
// keywords may be freed and re-built in the process, to make room
void add_keyword(char **keywords_ptr, const char *word) {
  // if it's already a keyword, do nothing
  if(!is_keyword(*keywords_ptr, word, FALSE)) {
    BUFFER *buf = newBuffer(MAX_BUFFER);
    // make our new keyword list
    bprintf(buf, "%s%s%s", *keywords_ptr, (**keywords_ptr ? ", " : ""), word);
    // free the old string, copy the new one
    free(*keywords_ptr);
    *keywords_ptr = strdup(bufferString(buf));
    // clean up our garbage
    deleteBuffer(buf);
  }
}


//
// go through the keywords and if the word is found, remove it
void remove_keyword(char *keywords, const char *word) {
  LIST *words = parse_keywords(keywords);
  char  *copy = NULL;
  int   count = 0;

  // remove all copies of it
  while( (copy = listRemoveWith(words, word, strcasecmp)) != NULL) {
    free(copy);
    count++;
  }

  // did we find a copy of the bad keyword?
  if(count > 0) {
    LIST_ITERATOR *word_i = newListIterator(words);
    int             key_i = 0;
    count = 0;

    // clear the current keywords... we'll rebuild them
    *keywords = '\0';

    ITERATE_LIST(copy, word_i) {
      count++;
      key_i += sprintf(keywords+key_i, "%s", copy);
      if(count < listSize(words) - 1)
	key_i += sprintf(keywords+key_i, ", ");
    } deleteListIterator(word_i);
  }

  // clean up our garbage
  deleteListWith(words, free);
}


//
// print the string on the buffer, centered for a line of length linelen.
// if border is TRUE, put a border to either side of the string.
//
void center_string(char *buf, const char *string, int linelen, int buflen, 
		   bool border) {
  char fmt[32];
  int str_len = strlen(string);
  int spaces  = (linelen - str_len)/2;

  if(border) {
    int i, buf_i = 0;
    sprintf(fmt, "{n%%-%ds[{c", spaces-2);
    buf_i  = snprintf(buf, buflen, fmt, " ");
    // replace all of the spaces with -
    for(i = 0; buf[i] != '\0'; i++) if(buf[i] == ' ') buf[i] = '-';

    buf_i += snprintf(buf+buf_i, buflen-buf_i, " %s ", string);
    sprintf(fmt, "{n]%%-%ds\r\n", 
	    spaces-2 + (((linelen-str_len) % 2) == 1 ? 1 : 0));

    i = buf_i;
    buf_i += snprintf(buf+buf_i, buflen-buf_i, fmt, " ");
    // replace all of the spaces with -
    for(; buf[i] != '\0'; i++) if(buf[i] == ' ') buf[i] = '-';
  }
  else {
    int buf_i = 0;
    sprintf(fmt, "%%-%ds", spaces);
    buf_i  = snprintf(buf, buflen, fmt, " ");
    buf_i += snprintf(buf+buf_i, buflen-buf_i, "%s\r\n", string);
  }
}


//
// fill up the buffer with characters until
// a newline is reached, or we hit our critical
// length. Return how many characters were read
//
int fgetline(FILE *file, char *p, int maxlen)
{
  int count = 0;

  while(!feof(file) && count < maxlen - 1) {

    p[count] = fgetc(file);
    if(p[count] == '\n')
      break;

    count++;
  }

  p[count] = '\0';
  return count;
}

int rand_number(int min, int max) {
  if(min > max) {
    log_string("ERROR: rand_number passed a min (%d) higher than its max (%d).",
	       min, max);
    return 0;
  }

  return min + rand() % (max-min + 1);
}

double rand_percent(void) {
  double rnd = rand_number(0, RAND_MAX);
  return rnd / (double)RAND_MAX;
}

double rand_gaussian(void) {
  return sqrt(-2.0 * log(rand_percent())) * cos(2.0 * PI * rand_percent());
}

double sigmoid(double val) {
  return 1.0 / (1.0 + pow(MATH_E, -val));
}


//
// returns "st", "nd", "rd", or "th", based on the number passed in
//
const char *numth(int num) {
  if(num != 11 && num % 10 == 1)
    return "st";
  else if(num != 12 && num % 10 == 2)
    return "nd";
  else if(num != 13 && num % 10 == 3)
    return "rd";
  else
    return "th";
}


//
// Strips all occurances of a word from the string
//
// used to just replace occurances of 'word' with nothing,
// but this doesn't work so well since replace_string will
// replace it, even if the 'word' is embedded inside of another
// word. This is undesirable.
//
//  replace_string(&string, word, "", TRUE);
//
void strip_word(char *string, const char *word) {
  int word_len = strlen(word);
  int i = 0;

  for(i = 0; string[i] != '\0'; i++) {

    // skip all spaces and newline stuff
    while(isspace(string[i]) || string[i] == '\n' || string[i] == '\r')
      i++;

    // we've found a match
    if(!strncmp(string+i, word, word_len) &&
       (string[i+word_len] == '\0' || isspace(string[i+word_len]) ||
	string[i+word_len] == '\n' || string[i+word_len] == '\r')) {

      // count all of the spaces following us, so we can remove them too
      int space_count = 0;
      while(isspace(string[i+word_len+space_count]))
	space_count++;

      // shuffle everything down
      int j = i-1;
      do {
	j++;
	string[j] = string[j+word_len+space_count];
      } while(string[j+word_len+space_count] != '\0');
    }
  }
}


char *tag_keywords(const char *string, const char *keywords, 
		   const char *start_tag, const char *end_tag) {
  char buf[strlen(string) * 2];
  int i = 0, j = 0;
  int start_len = strlen(start_tag);
  int end_len   = strlen(end_tag);

  while(*string) {
    int keyword_len;
    // check for a match
    if( (keyword_len = find_keyword(keywords, string)) != 0) {
      for(j = 0; j < start_len; j++)
	buf[i++] = start_tag[j];
      while(keyword_len > 0) {
	buf[i++] = *string;
	string++;
	keyword_len--;
      }
      for(j = 0; j < end_len; j++)
	buf[i++] = end_tag[j];
    }

    // skip ahead one word
    else {
      while(!isspace(*string) && *string != '\0') {
	buf[i++] = *string;
	string++;
      }
      // put in our space
      if(*string != '\0') {
	buf[i++] = *string;
	string++;
      }
    }
  }
  buf[i++] = '\0';//dialogue++;

  return strdup(buf);
}


void buf_tag_keywords(BUFFER *buf, const char *keywords, const char *start_tag,
		      const char *end_tag) {
  char *new_str = tag_keywords(bufferString(buf), keywords, start_tag, end_tag);
  bufferClear(buf);
  bufferCat(buf, new_str);
  free(new_str);
}

bitvector_t parse_bits(const char *string) {
  bitvector_t bits = 0;

  while(*string) {
    SET_BIT(bits, (1 << (*string-'a')));
    string = string+1;
  }

  return bits;
}

const char *write_bits(bitvector_t bits) {
  static char buf[SMALL_BUFFER]; // really, we only need about 64...
  int buf_i = 0, i = 0;

  for(i = 0; i < BITS_PER_BITVECTOR; i++)
    if(IS_SET(bits, (1 << i)))
      buf[buf_i++] = 'a'+i;

  buf[buf_i] = '\0';
  return buf;
}


void print_bits(bitvector_t bits, const char **names, char *buf) {
  int i, count = 0;
  for(i = 0; i < BITS_PER_BITVECTOR; i++) {
    if(IS_SET(bits, (1 << i))) {
      count++;
      sprintf(buf, "%s%s%s",
	      (count == 1 ? "" : buf ),
	      (count == 1 ? "" : ", "),
	      names[i]);
    }
  }

  if(count == 0)
    sprintf(buf, "NOBITS");
}


char *print_list(LIST *list, void *descriptor, void *multi_descriptor) {
  const char             *(* desc_func)(void *) = descriptor;
  const char            *(* multi_desc)(void *) = multi_descriptor;

  if(listSize(list) == 0)
    return strdup("");
  
  char buf[MAX_BUFFER] = "";
  char one_thing[SMALL_BUFFER];
  int i;
  int tot_things = 0;
  int size = listSize(list);
  int counts[size];
  void *things[size];
  void *thing;
  LIST_ITERATOR *thing_i = newListIterator(list);

  for(i = 0; i < size; i++) {
    counts[i] = 0;
    things[i] = NULL;
  }

  // first, collect groups of all the things in the list
  ITERATE_LIST(thing, thing_i) {
    // see if we have one with the current name already
    for(i = 0; i < size; i++) {
      // it's a thing with a new name
      if(things[i] == NULL) {
	things[i] = thing;
	counts[i]++;
	tot_things++;
	break;
      }
      // it has the same name as something else
      else if(!strcmp(desc_func(thing), desc_func(things[i]))) {
	counts[i]++;
	break;
      }
    }
  } deleteListIterator(thing_i);


  // now, print everything to the buffer
  for(i = 0; i < tot_things; i++) {
    if(counts[i] == 1)
      sprintf(one_thing, "%s", desc_func(things[i]));
    else if(multi_desc == NULL || !*multi_desc(things[i]))
      sprintf(one_thing, "(%d) %s", counts[i], desc_func(things[i]));
    else
      sprintf(one_thing, multi_desc(things[i]), counts[i]);

    // we're at the last one... we have to print an and
    if(i == tot_things - 1)
      sprintf(buf, "%s%s%s", buf,
	      (i < 1 ? "" : (i == 1 ? " and " : ", and ")),
	      one_thing);
    // regular comma-separated dealy
    else
      sprintf(buf, "%s%s%s", 
	      (i == 0 ? "" : buf), (i == 0 ? "" : ", "), one_thing);
  }

  return strdup(buf);
}


void show_list(CHAR_DATA *ch, LIST *list, void *descriptor, 
	       void *multi_descriptor) {
  const char             *(* desc_func)(void *) = descriptor;
  const char            *(* multi_desc)(void *) = multi_descriptor;

  int i;
  int size = listSize(list);
  int counts[size];
  void *things[size];
  void *thing;
  LIST_ITERATOR *thing_i = newListIterator(list);

  for(i = 0; i < size; i++) {
    counts[i] = 0;
    things[i] = NULL;
  }

  // find a list of all things with unique names
  ITERATE_LIST(thing, thing_i) {
    // see if we have one with the current name already
    for(i = 0; i < size; i++) {
      // it's a thing with a new name
      if(things[i] == NULL) {
	things[i] = thing;
	counts[i]++;
	break;
      }
      // it has the same name as something else
      else if(!strcmp(desc_func(thing), desc_func(things[i]))) {
	counts[i]++;
	break;
      }
    }
  } deleteListIterator(thing_i);


  // print out all of the things
  for(i = 0; i < size; i++) {
    // we've run into the end of the list
    if(things[i] == NULL)
      break;
    else {
      if(counts[i] == 1)
	send_to_char(ch, "{n%s\r\n", desc_func(things[i]));
      else if(multi_desc == NULL || !*multi_desc(things[i]))
	send_to_char(ch, "{n(%d) %s\r\n", counts[i], desc_func(things[i]));
      else {
	char fmt[SMALL_BUFFER];
	sprintf(fmt, "{n%s\r\n", multi_desc(things[i]));
	send_to_char(ch, fmt, counts[i]);
      }
    }
  }
}


LIST *get_unused_items(CHAR_DATA *ch, LIST *list, bool invis_ok) {
  OBJ_DATA *obj;
  LIST *newlist = newList();
  LIST_ITERATOR *obj_i = newListIterator(list);

  ITERATE_LIST(obj, obj_i) {
    if(listSize(objGetUsers(obj)) != 0)
      continue;
    if(!(invis_ok || can_see_obj(ch, obj)))
      continue;

    listPut(newlist, obj);
  } deleteListIterator(obj_i);
  return newlist;
}

LIST *get_used_items(CHAR_DATA *ch, LIST *list, bool invis_ok) {
  OBJ_DATA *obj;
  LIST *newlist = newList();
  LIST_ITERATOR *obj_i = newListIterator(list);

  ITERATE_LIST(obj, obj_i) {
    if(listSize(objGetUsers(obj)) < 1)
      continue;
    if(!(invis_ok || can_see_obj(ch, obj)))
      continue;
    listPut(newlist, obj);
  } deleteListIterator(obj_i);
  return newlist;
}

bool has_obj(CHAR_DATA *ch, const char *prototype) {
  LIST *vis_inv = find_all_objs(ch, charGetInventory(ch), NULL, prototype,TRUE);
  int   ret_val = FALSE;
  if(listSize(vis_inv) > 0)
    ret_val = TRUE;
  deleteList(vis_inv);
  return ret_val;
}

void *identity_func(void *data) {
  return data;
}

LIST *reverse_list(LIST *list) {
  LIST         *newlist = newList();
  LIST_ITERATOR *list_i = newListIterator(list);
  void            *elem = NULL;
  ITERATE_LIST(elem, list_i) {
    listPut(newlist, elem);
  } deleteListIterator(list_i);
  return newlist;
}

bool parse_worldkey(const char *key, char *name, char *locale) {
  *name = *locale = '\0';
  int pos = next_letter_in(key, '@');
  if(pos == -1)
    return FALSE;
  else {
    strncpy(name, key, pos);
    *(name+pos) = '\0';
    strcpy(locale, key+pos+1);
    if(*name == '\0' || *locale == '\0')
      return FALSE;
    return TRUE;
  }
}

bool parse_worldkey_relative(CHAR_DATA *ch, const char *key, char *name, 
			     char *locale) {
  int pos = next_letter_in(key, '@');
  if(pos != -1)
    return parse_worldkey(key, name, locale);
  else {
    strcpy(name, key);
    strcpy(locale, get_key_locale(roomGetClass(charGetRoom(ch))));
    return TRUE;
  }
}

const char *get_key_locale(const char *key) {
  int pos = next_letter_in(key, '@');
  if(pos == -1)
    return "";
  else
    return key+pos+1;
}

const char *get_key_name(const char *key) {
  int pos = next_letter_in(key, '@');
  if(pos == -1)
    return key;
  else {
    static char name[SMALL_BUFFER];
    strcpy(name, key);
    name[pos] = '\0';
    return name;
  }
}

const char *get_fullkey(const char *name, const char *locale) {
  static char full_key[SMALL_BUFFER];
  sprintf(full_key, "%s@%s", name, locale);
  return full_key;
}

const char *get_fullkey_relative(const char *key, const char *locale) {
  int pos = next_letter_in(key, '@');
  if(pos > 0)
    return key;
  else {
    static char full_key[SMALL_BUFFER];
    sprintf(full_key, "%s@%s", key, locale);
    return full_key;
  }
}

const char *get_shortkey(const char *key, const char *to) {
  // are we missing a locale?
  int pos = next_letter_in(key, '@');
  if(pos < 0)
    return key;

  // if 'to' doesn't exist, make sure we return the full thing back.
  // Only relevant for attaching triggers to characters instead of mobiles
  if(!*to)
    return key;

  // is 'to' missing a locale?
  pos = next_letter_in(to, '@');
  if(pos < 0)
    return get_key_name(key);

  // two keys, both with a name and a locale. See if our locales match up
  static char name[SMALL_BUFFER];
  static char locale[SMALL_BUFFER];
  parse_worldkey(key, name, locale);
  if(!strcmp(locale, get_key_locale(to)))
    return name;

  // different locales, return the full key
  return key;
}

bool key_malformed(const char *key) {
  int at_count = 0;
  for(; *key; *key++) {
    if(*key == '@') {
      if(at_count > 0)
	return TRUE;
      at_count++;
    }
    else if(!(*key == '_' || isalpha(*key) || isdigit(*key)))
      return TRUE;
  }
  return FALSE;
}

bool locale_malformed(const char *key) {
  for(; *key; *key++) {
    if(!(isalpha(*key) || isdigit(*key) || *key == '_'))
      return TRUE;
  }
  return FALSE;
}

bool cmd_matches(const char *pattern, const char *cmd) {
  int len = next_letter_in(pattern, '*');
  // we have to match exactly
  if(len == -1)
    return !strcasecmp(pattern, cmd);
  else if(len == 0)
    return TRUE;
  else
    return !strncasecmp(pattern, cmd, len);
}

bool charHasMoreUserGroups(CHAR_DATA *ch1, CHAR_DATA *ch2) {
  return (bitIsAllSet(charGetUserGroups(ch1),
		      bitvectorGetBits(charGetUserGroups(ch2))) &&
	  !bitIsAllSet(charGetUserGroups(ch2),
		       bitvectorGetBits(charGetUserGroups(ch1))));
}

bool canEditZone(ZONE_DATA *zone, CHAR_DATA *ch) {
  return (!charIsNPC(ch) && 
	  (is_keyword(zoneGetEditors(zone), charGetName(ch), FALSE) ||
	   bitIsOneSet(charGetUserGroups(ch), "admin")));
}

bool file_exists(const char *fname) {
  FILE *fl = fopen(fname, "r");
  if(fl == NULL) return FALSE;
  fclose(fl);
  return TRUE;
}

bool dir_exists(const char *dname) {
  DIR *dir = opendir(dname);
  if(dir == NULL) return FALSE;
  closedir(dir);
  return TRUE;
}

void show_prompt(SOCKET_DATA *socket) {
  if(socketGetChar(socket))
    text_to_buffer(socket, custom_prompt(socketGetChar(socket)));
}

const char *custom_prompt(CHAR_DATA *ch) {
  static char prompt[SMALL_BUFFER];
  *prompt = '\0';
  strcat(prompt, "\r\n{nprompt> ");    
  return prompt;
}

//
// Delete an object, room, mobile, etc... from the game. First remove it from
// the gameworld database, and then delete it and its contents. The onus is on
// the builder to make sure deleting a prototype won't screw anything up (e.g.
// people standing about in a room when it's deleted).
bool do_delete(CHAR_DATA *ch, const char *type, void *deleter, const char *arg){
  void  (* delete_func)(void *data) = deleter;
  void      *data = NULL;
  const char *key = get_fullkey_relative(arg, 
		        get_key_locale(roomGetClass(charGetRoom(ch))));
  ZONE_DATA *zone = worldGetZone(gameworld, get_key_locale(key));
  
  if(!arg || !*arg)
    send_to_char(ch, "Which %s did you want to delete?\r\n", type);
  else if(key_malformed(arg))
    send_to_char(ch, "The %s key you entered was malformed.\r\n", type);
  else if(zone == NULL)
    send_to_char(ch, "No such zone exists.\r\n");
  else if(!canEditZone(zone, ch))
    send_to_char(ch, "You are not authorized to edit that zone.\r\n");
  else if((data = worldRemoveType(gameworld, type, key)) == NULL)
    send_to_char(ch, "The %s you tried to delete does not exist.\r\n", type);
  else {
    send_to_char(ch, "You remove the %s %s from the world database.\r\n", 
		 key, type);
    delete_func(data);
    return TRUE;
  }
  return FALSE;
}

//
// generic xxxlist for builders.
void do_list(CHAR_DATA *ch, const char *locale, const char *type, 
	     const char *header, void *informer) {
  if(locale_malformed(locale)) {
    send_to_char(ch, "You supplied a malformed zone name.\r\n");
    return;
  }

  ZONE_DATA *zone = worldGetZone(gameworld, locale);
  if(zone == NULL)
    send_to_char(ch, "No such zone exists.\r\n");
  else {
    const char *(* info_func)(void *) = informer;
    LIST *name_list = zoneGetTypeKeys(zone, type);
    listSortWith(name_list, strcasecmp);
    LIST_ITERATOR *name_i = newListIterator(name_list);
    char            *name = NULL;
    BUFFER           *buf = newBuffer(1);
    void            *data = NULL;
    bprintf(buf, " {wKey %74s \r\n"
"{b--------------------------------------------------------------------------------{n\r\n", header);
    ITERATE_LIST(name, name_i) {
      data = worldGetType(gameworld, type, get_fullkey(name, locale));
      if(data != NULL)
	bprintf(buf, " {c%-20s %57s{n\r\n", name, 
		(info_func ? info_func(zoneGetType(zone, type, name)) : ""));
    } deleteListIterator(name_i);
    deleteListWith(name_list, free);
    page_string(charGetSocket(ch), bufferString(buf));
    deleteBuffer(buf);
  }
}

//
// moves something of the given type with one name to the other name
bool do_rename(CHAR_DATA *ch,const char *type,const char *from,const char *to) {
  // make sure we can edit all of the zones involved
  const char  *locale = get_key_locale(roomGetClass(charGetRoom(ch)));
  void           *tgt = NULL;
  ZONE_DATA     *zone = NULL;

  // make sure everything is formed properly
  if(key_malformed(from) || key_malformed(to))
    send_to_char(ch, "You entered a malformed %s key.\r\n", type);
  // make sure "to" does not already exist, and that we have zone editing privs
  else if((zone = worldGetZone(gameworld, 
	     get_key_locale(get_fullkey_relative(to, locale)))) == NULL)
    send_to_char(ch, "Destination zone does not exist!\r\n");
  else if(!canEditZone(zone, ch))
    send_to_char(ch, "You cannot edit the destination zone.\r\n");
  else if(worldGetType(gameworld, type, get_fullkey_relative(to, locale)))
    send_to_char(ch, "%s already exists!\r\n", to);
  else {
    // make sure "from" exists, and that we have zone editing privs
    if((zone = worldGetZone(gameworld, 
	       get_key_locale(get_fullkey_relative(from, locale)))) == NULL)
      send_to_char(ch, "The originating zone does not exist!\r\n");
    else if(!canEditZone(zone, ch))
      send_to_char(ch, "You do not have editing priviledges for %s!\r\n", from);
    else if( (tgt = worldRemoveType(gameworld, type, 
                    get_fullkey_relative(from, locale))) == NULL)
      send_to_char(ch, "%s %s does not exist.\r\n", from, type);
    else {
      send_to_char(ch, "%s %s renamed to %s.\r\n", from, type, to);
      worldPutType(gameworld, type, get_fullkey_relative(to, locale), tgt);
      worldSaveType(gameworld, type, get_fullkey_relative(to, locale));
      return TRUE;
    }
  }

  // failed!
  return FALSE;
}