LOP/
LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/classes/
LOP/color/
LOP/councils/
LOP/deity/
LOP/races/
LOP/src/specials/
/*****************************************************************************
 * DikuMUD (C) 1990, 1991 by:                                                *
 *   Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen,   *
 *   and Katja Nyboe.                                                        *
 *---------------------------------------------------------------------------*
 * MERC 2.1 (C) 1992, 1993 by:                                               *
 *   Michael Chastain, Michael Quan, and Mitchell Tse.                       *
 *---------------------------------------------------------------------------*
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider.                    *
 *   Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, *
 *         gorog, Grishnakh, Nivek, Tricops, and Fireblade.                  *
 *---------------------------------------------------------------------------*
 * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community.              *
 *                    Their contributions are greatly appreciated.           *
 *---------------------------------------------------------------------------*
 * LoP (C) 2006, 2007, 2008 by: the LoP team.                                *
 *---------------------------------------------------------------------------*
 * 			Database management module			     *
 *****************************************************************************/

#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <dirent.h>
#if !defined(WIN32)
   #include <dlfcn.h>
#else
   #include <windows.h>
   #define dlopen( libname, flags ) LoadLibrary( (libname) )
#endif
#include "h/mud.h"

#define WEATHER_FILE SYSTEM_DIR "weather.dat"

void free_phistory( PER_HISTORY *phistory );
void init_supermob( void );
void load_deity( void );
void load_fnames( void );
void load_timeinfo( void );
void mprog_read_programs( FILE *fp, MOB_INDEX_DATA *pMobIndex );
void oprog_read_programs( FILE *fp, OBJ_INDEX_DATA *pObjIndex );
void rprog_read_programs( FILE *fp, ROOM_INDEX_DATA *pRoomIndex );

/* Globals. */
SHOP_DATA *first_shop, *last_shop;
REPAIR_DATA *first_repair, *last_repair;
CHAR_DATA *first_char, *last_char;
OBJ_DATA *first_object, *last_object;
OBJ_DATA *first_corpse, *last_corpse;
AREA_DATA *first_area, *last_area;
AREA_DATA *first_area_name, *last_area_name;   /*Used for alphanum. sort */
AREA_DATA *first_build, *last_build;
AREA_DATA *first_asort, *last_asort;
AREA_DATA *first_bsort, *last_bsort;

OBJ_DATA *extracted_obj_queue;
EXTRACT_CHAR_DATA *extracted_char_queue;
time_t last_restore_all_time = 0;
WEATHER_DATA weather_info;

int weath_unit;   /* global weather param */
int rand_factor;
int climate_factor;
int neigh_factor;
int max_vector;

int cur_qchars;
int nummobsloaded;
int numobjsloaded;
int physicalobjects;
int last_pkroom;

bool MOBtrigger;
int area_version;
bool DONT_UPPER;

short gsn_barehanded;
short gsn_pugilism;
short gsn_long_blades;
short gsn_short_blades;
short gsn_flexible_arms;
short gsn_talonous_arms;
short gsn_bludgeons;
short gsn_missile_weapons;
short gsn_detrap;
short gsn_backstab;
short gsn_circle;
short gsn_dodge;
short gsn_duck;
short gsn_block;
short gsn_shieldblock;
short gsn_counter;
short gsn_hide;
short gsn_peek;
short gsn_pick_lock;
short gsn_sneak;
short gsn_steal;
short gsn_gouge;
short gsn_poison_weapon;
short gsn_disarm;
short gsn_enhanced_damage;
short gsn_kick;
short gsn_parry;
short gsn_rescue;
short gsn_punch;
short gsn_bash;
short gsn_stun;
short gsn_bashdoor;
short gsn_grip;
short gsn_berserk;
short gsn_tumble;
short gsn_feed;
short gsn_bloodlet;
short gsn_chop;
short gsn_makefire;
short gsn_aid;
short gsn_track;
short gsn_search;
short gsn_dig;
short gsn_mount;
short gsn_bite;
short gsn_claw;
short gsn_tail;
short gsn_scribe;
short gsn_brew;
short gsn_imbue;
short gsn_concoct;
short gsn_carve;
short gsn_mix;
short gsn_climb;
short gsn_cook;
short gsn_scan;
short gsn_slice;
short gsn_aqua_breath;
short gsn_blindness;
short gsn_charm_person;
short gsn_curse;
short gsn_invis;
short gsn_poison;
short gsn_sleep;
short gsn_lightning_bolt;
short gsn_common;

/* for searching */
short gsn_first_spell;
short gsn_first_skill;
short gsn_first_weapon;
short gsn_first_tongue;
short gsn_top_sn;

/* For styles?  Trying to rebuild from some kind of accident here - Blod */
short gsn_style_evasive;
short gsn_style_defensive;
short gsn_style_standard;
short gsn_style_aggressive;
short gsn_style_berserk;

/* Locals. */
MOB_INDEX_DATA *mob_index_hash[MAX_KEY_HASH];
OBJ_INDEX_DATA *obj_index_hash[MAX_KEY_HASH];
ROOM_INDEX_DATA *room_index_hash[MAX_KEY_HASH];

SYSTEM_DATA sysdata;

extern int top_help;
extern int top_news;
extern int top_bti;
int top_affect;
int top_area;
int top_ed;
int top_exit;
int top_mob_index;
int top_obj_index;
int top_reset;
int top_room;
int top_shop;
int top_repair;

/* Semi-locals. */
bool fBootDb;
char strArea[MIL];
FILE *fpArea;

/* Local booting procedures. */
void create_transfer( void );
void load_transfer( void );
void init_mm( void );
void boot_log( const char *str, ... );
void load_area( FILE *fp );
void load_author( AREA_DATA *tarea, FILE *fp );
void load_resetmsg( AREA_DATA *tarea, FILE *fp );   /* Rennard */
void load_flags( AREA_DATA *tarea, FILE *fp );
void load_helps( void );
void load_news( void );
void load_highscores( void );
void load_hints( void );
void load_calendarinfo( void );
void load_mwresets( void );
void load_channels( void );
void load_bti( void );
void load_banks( void );
void load_rewards( void );
void load_rooms( AREA_DATA *tarea, FILE *fp );
void load_ranges( AREA_DATA *tarea, FILE *fp );
void load_climate( AREA_DATA *tarea, FILE *fp ); /* FB */
void load_neighbor( AREA_DATA *tarea, FILE *fp );
void load_buildlist( void );
void load_systemdata( void );
void load_version( AREA_DATA *tarea, FILE *fp );
void load_reserved( void );
void fix_exits( void );
void load_weatherdata( void );
void load_specfuns( void );

/* External booting function */
void load_corpses( void );
void renumber_put_resets( ROOM_INDEX_DATA *room );
void wipe_resets( ROOM_INDEX_DATA *room );

/* Assign gsn's for skills which need them. */
void assign_gsns( void )
{
   log_string( "Assigning gsn's" );
   ASSIGN_GSN( gsn_style_evasive, "evasive style" );
   ASSIGN_GSN( gsn_style_defensive, "defensive style" );
   ASSIGN_GSN( gsn_style_standard, "standard style" );
   ASSIGN_GSN( gsn_style_aggressive, "aggressive style" );
   ASSIGN_GSN( gsn_style_berserk, "berserk style" );
   ASSIGN_GSN( gsn_barehanded, "barehanded" );
   ASSIGN_GSN( gsn_pugilism, "pugilism" );
   ASSIGN_GSN( gsn_long_blades, "long blades" );
   ASSIGN_GSN( gsn_short_blades, "short blades" );
   ASSIGN_GSN( gsn_flexible_arms, "flexible arms" );
   ASSIGN_GSN( gsn_talonous_arms, "talonous arms" );
   ASSIGN_GSN( gsn_bludgeons, "bludgeons" );
   ASSIGN_GSN( gsn_missile_weapons, "missile weapons" );
   ASSIGN_GSN( gsn_detrap, "detrap" );
   ASSIGN_GSN( gsn_backstab, "backstab" );
   ASSIGN_GSN( gsn_circle, "circle" );
   ASSIGN_GSN( gsn_tumble, "tumble" );
   ASSIGN_GSN( gsn_dodge, "dodge" );
   ASSIGN_GSN( gsn_duck, "duck" );
   ASSIGN_GSN( gsn_block, "block" );
   ASSIGN_GSN( gsn_shieldblock, "shieldblock" );
   ASSIGN_GSN( gsn_counter, "counter" );
   ASSIGN_GSN( gsn_hide, "hide" );
   ASSIGN_GSN( gsn_peek, "peek" );
   ASSIGN_GSN( gsn_pick_lock, "pick lock" );
   ASSIGN_GSN( gsn_sneak, "sneak" );
   ASSIGN_GSN( gsn_steal, "steal" );
   ASSIGN_GSN( gsn_gouge, "gouge" );
   ASSIGN_GSN( gsn_poison_weapon, "poison weapon" );
   ASSIGN_GSN( gsn_disarm, "disarm" );
   ASSIGN_GSN( gsn_enhanced_damage, "enhanced damage" );
   ASSIGN_GSN( gsn_kick, "kick" );
   ASSIGN_GSN( gsn_parry, "parry" );
   ASSIGN_GSN( gsn_rescue, "rescue" );
   ASSIGN_GSN( gsn_punch, "punch" );
   ASSIGN_GSN( gsn_bash, "bash" );
   ASSIGN_GSN( gsn_stun, "stun" );
   ASSIGN_GSN( gsn_bashdoor, "doorbash" );
   ASSIGN_GSN( gsn_grip, "grip" );
   ASSIGN_GSN( gsn_berserk, "berserk" );
   ASSIGN_GSN( gsn_feed, "feed" );
   ASSIGN_GSN( gsn_bloodlet, "bloodlet" );
   ASSIGN_GSN( gsn_chop, "chop" );
   ASSIGN_GSN( gsn_makefire, "makefire" );
   ASSIGN_GSN( gsn_aid, "aid" );
   ASSIGN_GSN( gsn_track, "track" );
   ASSIGN_GSN( gsn_search, "search" );
   ASSIGN_GSN( gsn_dig, "dig" );
   ASSIGN_GSN( gsn_mount, "mount" );
   ASSIGN_GSN( gsn_bite, "bite" );
   ASSIGN_GSN( gsn_claw, "claw" );
   ASSIGN_GSN( gsn_tail, "tail" );
   ASSIGN_GSN( gsn_scribe, "scribe" );
   ASSIGN_GSN( gsn_brew, "brew" );
   ASSIGN_GSN( gsn_imbue, "imbue" );
   ASSIGN_GSN( gsn_carve, "carve" );
   ASSIGN_GSN( gsn_concoct, "concoct" );
   ASSIGN_GSN( gsn_mix, "mix" );
   ASSIGN_GSN( gsn_climb, "climb" );
   ASSIGN_GSN( gsn_cook, "cook" );
   ASSIGN_GSN( gsn_scan, "scan" );
   ASSIGN_GSN( gsn_slice, "slice" );
   ASSIGN_GSN( gsn_lightning_bolt, "lightning bolt" );
   ASSIGN_GSN( gsn_aqua_breath, "aqua breath" );
   ASSIGN_GSN( gsn_blindness, "blindness" );
   ASSIGN_GSN( gsn_charm_person, "charm person" );
   ASSIGN_GSN( gsn_curse, "curse" );
   ASSIGN_GSN( gsn_invis, "invis" );
   ASSIGN_GSN( gsn_poison, "poison" );
   ASSIGN_GSN( gsn_sleep, "sleep" );
   ASSIGN_GSN( gsn_common, "common" );
}

void shutdown_mud( char *reason )
{
   FILE *fp;

   if( ( fp = fopen( SHUTDOWN_FILE, "a" ) ) )
   {
      fprintf( fp, "%s\n", reason );
      fclose( fp );
      fp = NULL;
   }
}

/* Big mama top level function. */
void boot_db( bool fCopyOver )
{
   fpArea = NULL;
   remove_file( BOOTLOG_FILE );

   if( !fCopyOver )
      fprintf( stderr, ".~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~[  Boot  Log  ]~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.\n" );
   else
      fprintf( stderr, ".~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~[ Hotboot Log ]~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.\n" );

   log_string( "Creating transfer data" );
   create_transfer( );

   log_string( "Loading transfer data" );
   load_transfer( );

   log_string( "Loading fish name data" );
   load_fnames( );

   log_string( "Loading channels..." );
   load_channels( );

   log_string( "Initializing libdl support..." );

   /* Open up a handle to the executable's symbol table for later use when working with commands */
   if( !( sysdata.dlHandle = dlopen( NULL, RTLD_LAZY ) ) )
   {
      log_string( "dl: Error opening local system executable as handle, please check compile flags." );
      shutdown_mud( "libdl failure" );
      exit( 1 );   
   }

   log_string( "Loading Calendar..." );
   load_calendarinfo( );

   log_string( "Loading helps..." );
   load_helps( );

   log_string( "Loading news..." );
   load_news( );

   log_string( "Loading HighScores..." );
   load_highscores( );

   log_string( "Loading Hints..." );
   load_hints( );

   log_string( "Loading mud wide resets..." );
   load_mwresets( );

   log_string( "Loading bugs, typos, and ideas..." );
   load_bti( );

   log_string( "Loading banks..." );
   load_banks( );

   log_string( "Loading commands..." );
   load_commands( );

   log_string( "Loading spec_funs..." );
   load_specfuns( );

   log_string( "Loading sysdata configuration..." );
   load_systemdata( );

   log_string( "Loading socials" );
   load_socials( );

   log_string( "Loading skill table" );
   load_skill_table( );
   sort_skill_table( );
   remap_slot_numbers( ); /* must be after the sort */
   assign_gsns( );

   log_string( "Loading classes" );
   load_classes( );

   log_string( "Loading races" );
   load_races( );

   log_string( "Loading herb table" );
   load_herb_table( );

   log_string( "Loading tongues" );
   load_tongues( );

   log_string( "Making wizlist" );
   make_wizlist( );

   fBootDb = true;

   top_shop = 0;
   top_repair = 0;
   nummobsloaded = 0;
   numobjsloaded = 0;
   physicalobjects = 0;
   sysdata.maxplayers = 0;
   first_object = last_object = NULL;
   first_corpse = last_corpse = NULL;
   first_char = last_char = NULL;
   first_area = last_area = NULL;
   first_area_name = last_area_name = NULL; /* Used for alphanum. sort */
   first_build = last_build = NULL;
   first_shop = last_shop = NULL;
   first_repair = last_repair = NULL;
   first_asort = last_asort = NULL;
   extracted_obj_queue = NULL;
   extracted_char_queue = NULL;
   cur_qchars = 0;
   cur_char = NULL;
   cur_obj = 0;
   cur_char_died = false;
   cur_obj_extracted = false;
   quitting_char = NULL;
   loading_char = NULL;
   saving_char = NULL;
   last_pkroom = 1;
   first_ban_class = last_ban_class = NULL;
   first_ban_race = last_ban_race = NULL;
   first_ban = last_ban = NULL;

   weath_unit = 10;
   rand_factor = 2;
   climate_factor = 1;
   neigh_factor = 3;
   max_vector = weath_unit * 3;

   /* Init random number generator. */
   log_string( "Initializing random number generator" );
   init_mm( );

   /* Load time_info */
   log_string( "Loading time_info" );
   load_timeinfo( );

   /* Assign gsn's for skills which need them. */
   {
      log_string( "Assigning gsn's" );
      ASSIGN_GSN( gsn_style_evasive, "evasive style" );
      ASSIGN_GSN( gsn_style_defensive, "defensive style" );
      ASSIGN_GSN( gsn_style_standard, "standard style" );
      ASSIGN_GSN( gsn_style_aggressive, "aggressive style" );
      ASSIGN_GSN( gsn_style_berserk, "berserk style" );
      ASSIGN_GSN( gsn_barehanded, "barehanded" );
      ASSIGN_GSN( gsn_pugilism, "pugilism" );
      ASSIGN_GSN( gsn_long_blades, "long blades" );
      ASSIGN_GSN( gsn_short_blades, "short blades" );
      ASSIGN_GSN( gsn_flexible_arms, "flexible arms" );
      ASSIGN_GSN( gsn_talonous_arms, "talonous arms" );
      ASSIGN_GSN( gsn_bludgeons, "bludgeons" );
      ASSIGN_GSN( gsn_missile_weapons, "missile weapons" );
      ASSIGN_GSN( gsn_detrap, "detrap" );
      ASSIGN_GSN( gsn_backstab, "backstab" );
      ASSIGN_GSN( gsn_circle, "circle" );
      ASSIGN_GSN( gsn_tumble, "tumble" );
      ASSIGN_GSN( gsn_dodge, "dodge" );
      ASSIGN_GSN( gsn_duck, "duck" );
      ASSIGN_GSN( gsn_block, "block" );
      ASSIGN_GSN( gsn_shieldblock, "shieldblock" );
      ASSIGN_GSN( gsn_counter, "counter" );
      ASSIGN_GSN( gsn_hide, "hide" );
      ASSIGN_GSN( gsn_peek, "peek" );
      ASSIGN_GSN( gsn_pick_lock, "pick lock" );
      ASSIGN_GSN( gsn_sneak, "sneak" );
      ASSIGN_GSN( gsn_steal, "steal" );
      ASSIGN_GSN( gsn_gouge, "gouge" );
      ASSIGN_GSN( gsn_poison_weapon, "poison weapon" );
      ASSIGN_GSN( gsn_disarm, "disarm" );
      ASSIGN_GSN( gsn_enhanced_damage, "enhanced damage" );
      ASSIGN_GSN( gsn_kick, "kick" );
      ASSIGN_GSN( gsn_parry, "parry" );
      ASSIGN_GSN( gsn_rescue, "rescue" );
      ASSIGN_GSN( gsn_punch, "punch" );
      ASSIGN_GSN( gsn_bash, "bash" );
      ASSIGN_GSN( gsn_stun, "stun" );
      ASSIGN_GSN( gsn_bashdoor, "doorbash" );
      ASSIGN_GSN( gsn_grip, "grip" );
      ASSIGN_GSN( gsn_berserk, "berserk" );
      ASSIGN_GSN( gsn_feed, "feed" );
      ASSIGN_GSN( gsn_bloodlet, "bloodlet" );
      ASSIGN_GSN( gsn_chop, "chop" );
      ASSIGN_GSN( gsn_makefire, "makefire" );
      ASSIGN_GSN( gsn_aid, "aid" );
      ASSIGN_GSN( gsn_track, "track" );
      ASSIGN_GSN( gsn_search, "search" );
      ASSIGN_GSN( gsn_dig, "dig" );
      ASSIGN_GSN( gsn_mount, "mount" );
      ASSIGN_GSN( gsn_bite, "bite" );
      ASSIGN_GSN( gsn_claw, "claw" );
      ASSIGN_GSN( gsn_tail, "tail" );
      ASSIGN_GSN( gsn_scribe, "scribe" );
      ASSIGN_GSN( gsn_brew, "brew" );
      ASSIGN_GSN( gsn_imbue, "imbue" );
      ASSIGN_GSN( gsn_carve, "carve" );
      ASSIGN_GSN( gsn_concoct, "concoct" );
      ASSIGN_GSN( gsn_mix, "mix" );
      ASSIGN_GSN( gsn_climb, "climb" );
      ASSIGN_GSN( gsn_cook, "cook" );
      ASSIGN_GSN( gsn_scan, "scan" );
      ASSIGN_GSN( gsn_slice, "slice" );
      ASSIGN_GSN( gsn_lightning_bolt, "lightning bolt" );
      ASSIGN_GSN( gsn_aqua_breath, "aqua breath" );
      ASSIGN_GSN( gsn_blindness, "blindness" );
      ASSIGN_GSN( gsn_charm_person, "charm person" );
      ASSIGN_GSN( gsn_curse, "curse" );
      ASSIGN_GSN( gsn_invis, "invis" );
      ASSIGN_GSN( gsn_poison, "poison" );
      ASSIGN_GSN( gsn_sleep, "sleep" );
      ASSIGN_GSN( gsn_common, "common" );
   }

   /* Read in all the area files. */
   {
      FILE *fp;

      log_string( "Reading in area files..." );
      if( !( fp = fopen( AREA_LIST, "r" ) ) )
      {
         perror( AREA_LIST );
         shutdown_mud( "Unable to open area list" );
         exit( 1 );
      }

      for( ;; )
      {
         mudstrlcpy( strArea, fread_word( fp ), sizeof( strArea ) );
         if( strArea[0] == '$' )
            break;

         load_area_file( last_area, strArea );
      }
      fclose( fp );
      fp = NULL;
   }

   log_string( "Loading buildlist" );
   load_buildlist( );

   /* initialize supermob. must be done before reset_area! */
   init_supermob( );

   /*
    * Fix up exits.
    * Declare db booting over.
    * Reset all areas once.
    * Load up the notes file.
    */
   log_string( "Fixing exits" );
   fix_exits( );
   fBootDb = false;

   log_string( "Resetting areas" );
   area_update( );

   log_string( "Loading boards" );
   load_boards( );

   load_clans( );
   load_councils( );
   load_deity( );

   log_string( "Loading bans" );
   load_banlist( );

   log_string( "Loading rewards" );
   load_rewards( );

   log_string( "Loading reserved names" );
   load_reserved( );

   log_string( "Loading corpses" );
   load_corpses( );

   /* Morphs MUST be loaded after class and race tables are set up --Shaddai */
   log_string( "Loading Morphs" );
   load_morphs( );
   MOBtrigger = true;

   /* Initialize area weather data */
   load_weatherdata( );
   init_area_weather( );
}

/* Load an 'area' header line. */
void load_area( FILE *fp )
{
   AREA_DATA *pArea;

   CREATE( pArea, AREA_DATA, 1 );
   pArea->first_room = pArea->last_room = NULL;
   pArea->name = fread_string( fp );
   pArea->author = STRALLOC( "unknown" );
   pArea->filename = STRALLOC( strArea );
   pArea->age = 15;
   pArea->nplayer = 0;
   pArea->low_vnum = 0;
   pArea->hi_vnum = 0;
   pArea->low_soft_range = 0;
   pArea->hi_soft_range = MAX_LEVEL;
   pArea->low_hard_range = 0;
   pArea->hi_hard_range = MAX_LEVEL;

   /* initialize weather data - FB */
   CREATE( pArea->weather, WEATHER_DATA, 1 );
   pArea->weather->temp = 0;
   pArea->weather->precip = 0;
   pArea->weather->wind = 0;
   pArea->weather->temp_vector = 0;
   pArea->weather->precip_vector = 0;
   pArea->weather->wind_vector = 0;
   pArea->weather->climate_temp = 2;
   pArea->weather->climate_precip = 2;
   pArea->weather->climate_wind = 2;
   pArea->weather->first_neighbor = NULL;
   pArea->weather->last_neighbor = NULL;
   pArea->weather->echo = NULL;
   pArea->weather->echo_color = AT_GREY;
   area_version = 0;
   LINK( pArea, first_area, last_area, next, prev );
   top_area++;
}

/*
 * Load the version number of the area file if none exists, then it
 * is set to version 0 when #AREA is read in which is why we check for
 * the #AREA here.  --Shaddai
 */
void load_version( AREA_DATA *tarea, FILE *fp )
{
   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      if( fBootDb )
      {
         shutdown_mud( "No #AREA" );
         exit( 1 );
      }
      else
         return;
   }

   area_version = fread_number( fp );
}

/* Load an author section. Scryn 2/1/96 */
void load_author( AREA_DATA *tarea, FILE *fp )
{
   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      if( fBootDb )
      {
         shutdown_mud( "No #AREA" );
         exit( 1 );
      }
      else
         return;
   }

   STRFREE( tarea->author );
   tarea->author = fread_string( fp );
}

/* Reset Message Load, Rennard */
void load_resetmsg( AREA_DATA *tarea, FILE *fp )
{
   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      if( fBootDb )
      {
         shutdown_mud( "No #AREA" );
         exit( 1 );
      }
      else
         return;
   }

   STRFREE( tarea->resetmsg );
   tarea->resetmsg = fread_string( fp );
}

void load_flags( AREA_DATA *tarea, FILE *fp )
{
   char *infoflags;
   char flag[MSL];
   int value;

   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      if( fBootDb )
      {
         shutdown_mud( "No #AREA" );
         exit( 1 );
      }
      else
         return;
   }
   infoflags = fread_flagstring( fp );
   while( infoflags && infoflags[0] != '\0' )
   {
      infoflags = one_argument( infoflags, flag );
      value = get_flag( flag, area_flags, AFLAG_MAX );
      if( value < 0 || value >= AFLAG_MAX )
         bug( "%s: invalid area flag (%s)", __FUNCTION__, flag );
      else
         xSET_BIT( tarea->flags, value );
   }
}

/* Add a character to the list of all characters - Thoric */
void add_char( CHAR_DATA *ch )
{
   LINK( ch, first_char, last_char, next, prev );
}

/* load a mob section. */
void load_mobiles( AREA_DATA *tarea, FILE *fp )
{
   MOB_INDEX_DATA *pMobIndex;
   const char *word;
   char *infoflags = NULL, flag[MIL];
   bool oldmob, tmpBootDb, fMatch;
   int vnum, iHash, value;

   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      if( fBootDb )
      {
         shutdown_mud( "No #AREA" );
         exit( 1 );
      }
      else
         return;
   }

   for( ;; )
   {
      word = feof( fp ) ? "End" : fread_word( fp );
      fMatch = false;
      if( word[0] == EOF )
      {
         bug( "%s: feof didn't detect EOF but fread_word returned EOF...ending now!", __FUNCTION__ );
         word = "End";
      }

      switch( UPPER( word[0] ) )
      {
         case '*':
            fMatch = true;
            fread_to_eol( fp );
            break;

         case '>':
            if( !strcmp( word, ">" ) )
            {
               ungetc( '>', fp );
               mprog_read_programs( fp, pMobIndex );
               fMatch = true;
               break;
            }
            break;

         case '#':
            if( !strcmp( word, "#0" ) )
               return;
            break;

         case 'A':
            KEY( "Alignment", pMobIndex->alignment, fread_number( fp ) );
            KEY( "Armor", pMobIndex->ac, fread_number( fp ) );
            WEXTKEY( "Attacks", pMobIndex->attacks, fp, attack_flags, ATCK_MAX );
            WEXTKEY( "Affected", pMobIndex->affected_by, fp, a_flags, AFF_MAX );
            break;

         case 'D':
            KEY( "DamRoll", pMobIndex->damroll, fread_number( fp ) );
            KEY( "Description", pMobIndex->description, fread_string( fp ) );
            WEXTKEY( "Defenses", pMobIndex->defenses, fp, defense_flags, DFND_MAX );
            SKEY( "DefPosition", pMobIndex->defposition, fp, pos_names, POS_MAX );
            break;

         case 'E':
            if( !strcmp( word, "End" ) )
            {
               if( pMobIndex->ac < 0 )
                  pMobIndex->ac = -pMobIndex->ac;

               /* Make sure its set to NPC if it isn't already */
               if( !xIS_SET( pMobIndex->act, ACT_IS_NPC ) )
                  xSET_BIT( pMobIndex->act, ACT_IS_NPC );
               pMobIndex->position = pMobIndex->defposition;
               if( !oldmob )
               {
                  iHash = vnum % MAX_KEY_HASH;
                  pMobIndex->next = mob_index_hash[iHash];
                  mob_index_hash[iHash] = pMobIndex;
                  top_mob_index++;
               }
               fMatch = true;
               break;
            }
            break;

         case 'F':
            WEXTKEY( "Flags", pMobIndex->act, fp, act_flags, ACT_MAX );
            break;

         case 'G':
            KEY( "Gold", pMobIndex->gold, fread_number( fp ) );
            break;

         case 'H':
            KEY( "HitRoll", pMobIndex->hitroll, fread_number( fp ) );
            KEY( "Height", pMobIndex->height, fread_number( fp ) );
            if( !strcmp( word, "Hit" ) )
            {
               pMobIndex->minhit = fread_number( fp );
               pMobIndex->maxhit = fread_number( fp );
               fMatch = true;
               break;
            }
            break;

         case 'I':
            WEXTKEY( "Immune", pMobIndex->immune, fp, ris_flags, RIS_MAX );
            break;

         case 'L':
            KEY( "Level", pMobIndex->level, fread_number( fp ) );
            KEY( "Long", pMobIndex->long_descr, fread_string( fp ) );
            break;

         case 'M':
            KEY( "MGold", pMobIndex->mgold, fread_number( fp ) );
            break;

         case 'N':
            KEY( "NumAttacks", pMobIndex->numattacks, fread_number( fp ) );
            KEY( "Name", pMobIndex->name, fread_string( fp ) );
            if( !strcmp( word, "NRepair" ) )
            {
               REPAIR_DATA *rShop;
               int tmpvalue;
               char *srepair,sarg[MSL];

               CREATE( rShop, REPAIR_DATA, 1 );
               for( tmpvalue = 0; tmpvalue < ITEM_TYPE_MAX; tmpvalue++ )
                  rShop->fix_type[tmpvalue] = false;
               rShop->keeper = pMobIndex->vnum;
               rShop->profit_fix = fread_number( fp );
               rShop->open_hour = fread_number( fp );
               rShop->close_hour = fread_number( fp );
               srepair = fread_flagstring( fp );
               while( srepair && srepair[0] != '\0' )
               {
                  srepair = one_argument( srepair, sarg );
                  tmpvalue = get_flag( sarg, o_types, ITEM_TYPE_MAX );
                  if( tmpvalue > 0 || tmpvalue < ITEM_TYPE_MAX )
                     rShop->fix_type[tmpvalue] = true;
               }
               pMobIndex->rShop = rShop;
               LINK( rShop, first_repair, last_repair, next, prev );
               top_repair++;
               fMatch = true;
               break;
            }
            if( !strcmp( word, "NShop" ) )
            {
               SHOP_DATA *pShop;
               int tmpvalue;
               char *stype, sarg[MSL];

               CREATE( pShop, SHOP_DATA, 1 );
               for( tmpvalue = 0; tmpvalue < ITEM_TYPE_MAX; tmpvalue++ )
                  pShop->buy_type[tmpvalue] = false;
               pShop->keeper = pMobIndex->vnum;
               pShop->profit_buy = fread_number( fp );
               pShop->profit_sell = fread_number( fp );
               pShop->open_hour = fread_number( fp );
               pShop->close_hour = fread_number( fp );
               stype = fread_flagstring( fp );
               while( stype && stype[0] != '\0' )
               {
                  stype = one_argument( stype, sarg );
                  tmpvalue = get_flag( sarg, o_types, ITEM_TYPE_MAX );
                  if( tmpvalue > 0 || tmpvalue < ITEM_TYPE_MAX )
                     pShop->buy_type[tmpvalue] = true;
               }
               pShop->profit_buy = URANGE( pShop->profit_sell + 5, pShop->profit_buy, 1000 );
               pShop->profit_sell = URANGE( 0, pShop->profit_sell, pShop->profit_buy - 5 );
               pMobIndex->pShop = pShop;
               LINK( pShop, first_shop, last_shop, next, prev );
               top_shop++;
               fMatch = true;
               break;
            }
            break;

         case 'P':
            WEXTKEY( "Parts", pMobIndex->xflags, fp, part_flags, PART_MAX );
            SKEY( "Position", pMobIndex->position, fp, pos_names, POS_MAX );
            break;

         case 'R':
            if( !strcmp( word, "Repair" ) )
            {
               REPAIR_DATA *rShop;
               int tmpvalue;

               CREATE( rShop, REPAIR_DATA, 1 );
               for( tmpvalue = 0; tmpvalue < ITEM_TYPE_MAX; tmpvalue++ )
                  rShop->fix_type[tmpvalue] = false;
               for( tmpvalue = 0; tmpvalue < 3; tmpvalue++ )
               {
                  rShop->fix_type[fread_number( fp )] = true;
               }
               rShop->keeper = pMobIndex->vnum;
               rShop->profit_fix = fread_number( fp );
               fread_number( fp );
               rShop->open_hour = fread_number( fp );
               rShop->close_hour = fread_number( fp );
               pMobIndex->rShop = rShop;
               LINK( rShop, first_repair, last_repair, next, prev );
               top_repair++;
               fMatch = true;
               break;
            }
            WEXTKEY( "Resistant", pMobIndex->resistant, fp, ris_flags, RIS_MAX );
            break;

         case 'S':
            WEXTKEY( "Susceptible", pMobIndex->susceptible, fp, ris_flags, RIS_MAX );
            SKEY( "Sex", pMobIndex->sex, fp, sex_names, SEX_MAX );
            KEY( "Short", pMobIndex->short_descr, fread_string( fp ) );
            if( !strcmp( word, "Special" ) )
            {
               infoflags = fread_word( fp );
               if( !( pMobIndex->spec_fun = spec_lookup( infoflags ) ) )
               {
                  bug( "%s: 'M': vnum %d.", __FUNCTION__, pMobIndex->vnum );
                  pMobIndex->spec_funname = NULL;
               }
               else
                  pMobIndex->spec_funname = STRALLOC( infoflags );
               fMatch = true;
               break;
            }
            if( !strcmp( word, "Shop" ) )
            {
               int tmpvalue;
               SHOP_DATA *pShop;

               CREATE( pShop, SHOP_DATA, 1 );
               for( tmpvalue = 0; tmpvalue < ITEM_TYPE_MAX; tmpvalue++ )
                  pShop->buy_type[tmpvalue] = false;
               for( tmpvalue = 0; tmpvalue < 5; tmpvalue++ )
                  pShop->buy_type[fread_number( fp )] = true;
               pShop->keeper = pMobIndex->vnum;
               pShop->profit_buy = fread_number( fp );
               pShop->profit_sell = fread_number( fp );
               pShop->open_hour = fread_number( fp );
               pShop->close_hour = fread_number( fp );
               pShop->profit_buy = URANGE( pShop->profit_sell + 5, pShop->profit_buy, 1000 );
               pShop->profit_sell = URANGE( 0, pShop->profit_sell, pShop->profit_buy - 5 );
               pMobIndex->pShop = pShop;
               LINK( pShop, first_shop, last_shop, next, prev );
               top_shop++;
               fMatch = true;
               break;
            }
            if( !strcmp( word, "Saves" ) )
            {
               pMobIndex->saving_poison_death = fread_number( fp );
               pMobIndex->saving_wand = fread_number( fp );
               pMobIndex->saving_para_petri = fread_number( fp );
               pMobIndex->saving_breath = fread_number( fp );
               pMobIndex->saving_spell_staff = fread_number( fp );
               fMatch = true;
               break;
            }
            WEXTKEY( "Speaks", pMobIndex->speaks, fp, lang_names, LANG_UNKNOWN );
            WEXTKEY( "Speaking", pMobIndex->speaking, fp, lang_names, LANG_UNKNOWN );
            if( !strcmp( word, "Stats" ) )
            {
               int stat;

               for( stat = 0; stat < STAT_MAX; stat++ )
                  pMobIndex->perm_stats[stat] = fread_number( fp );
               fMatch = true;
               break;
            }
            break;

         case 'V':
            if( !strcmp( word, "Vnum" ) )
            {
               vnum = fread_number( fp );
               tmpBootDb = fBootDb;
               fBootDb = false;
               if( get_mob_index( vnum ) )
               {
                  if( tmpBootDb )
                  {
                     bug( "Load_mobiles: vnum %d duplicated.", vnum );
                     shutdown_mud( "duplicate vnum" );
                     exit( 1 );
                  }
                  else
                  {
                     pMobIndex = get_mob_index( vnum );
                     log_printf_plus( LOG_BUILD, sysdata.perm_log, "Cleaning mobile: %d", vnum );
                     clean_mob( pMobIndex );
                     oldmob = true;
                  }
               }
               else
               {
                  oldmob = false;
                  CREATE( pMobIndex, MOB_INDEX_DATA, 1 );
                  pMobIndex->pShop = NULL;
                  pMobIndex->rShop = NULL;
               }
               pMobIndex->vnum = vnum;
               fBootDb = tmpBootDb;
               if( fBootDb )
               {
                  if( !tarea->low_vnum )
                     tarea->low_vnum = vnum;
                  if( vnum > tarea->hi_vnum )
                     tarea->hi_vnum = vnum;
               }
               fMatch = true;
               break;
            }
            break;

         case 'W':
            KEY( "Weight", pMobIndex->weight, fread_number( fp ) );
            break;
      }
      if( !fMatch )
      {
         bug( "%s: no match: %s", __FUNCTION__, word );
         fread_to_eol( fp );
      }
   }
   bug( "%s: #0 not found?", __FUNCTION__ );
}

/* Load an obj section. */
void load_objects( AREA_DATA *tarea, FILE *fp )
{
   OBJ_INDEX_DATA *pObjIndex;
   int vnum, iHash, value, stat;
   bool tmpBootDb, oldobj, fMatch;
   char *infoflags = NULL;
   const char *word;
   char flag[MIL];

   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      if( fBootDb )
      {
         shutdown_mud( "No #AREA" );
         exit( 1 );
      }
      else
         return;
   }

   for( ;; )
   {
      word = feof( fp ) ? "End" : fread_word( fp );
      fMatch = false;
      if( word[0] == EOF )
      {
         bug( "%s: feof didn't detect EOF but fread_word returned EOF...ending now!", __FUNCTION__ );
         word = "End";
      }

      switch( UPPER( word[0] ) )
      {
         case '*':
            fMatch = true;
            fread_to_eol( fp );
            break;

         case '#':
            if( !strcmp( word, "#0" ) )
               return;
            break;

         case '>':
            if( !strcmp( word, ">" ) )
            {
               ungetc( '>', fp );
               oprog_read_programs( fp, pObjIndex );
               fMatch = true;
               break;
            }
            break;

         case 'A':
            if( !strcmp( word, "Affect" ) )
            {
               AFFECT_DATA *paf;
               bool dadd = false;

               CREATE( paf, AFFECT_DATA, 1 );
               paf->type = -1;
               paf->duration = -1;

               infoflags = fread_word( fp );
               if( str_cmp( infoflags, "0" ) )
               {
                  value = get_flag( infoflags, a_types, APPLY_MAX );
                  if( value < 0 || value >= APPLY_MAX )
                  {
                     bug( "%s: Unknown apply %s",  __FUNCTION__, infoflags );
                     paf->location = 0;
                     dadd = true;
                  }
                  else
                     paf->location = value;
               }
               else
                  paf->location = 0;

               infoflags = fread_word( fp );
               if( paf->location >= APPLY_RESISTANT && paf->location <= APPLY_SUSCEPTIBLE )
               {
                  value = get_flag( infoflags, ris_flags, RIS_MAX );
                  if( value < 0 || value >= RIS_MAX )
                  {
                     bug( "%s: Unknown resistant/immune/susceptible/absorb %s", __FUNCTION__, infoflags );
                     paf->modifier = -1;
                     dadd = true;
                  }
                  else
                     paf->modifier = value;
               }
               else if( paf->location == APPLY_EXT_AFFECT )
               {
                  value = get_flag( infoflags, a_flags, AFF_MAX );
                  if( value < 0 || value >= AFF_MAX )
                  {
                     bug( "%s: Unknown affected %s", __FUNCTION__, infoflags );
                     paf->modifier = -1;
                     dadd = true;
                  }
                  else
                     paf->modifier = value;
               }
               else if( paf->location == APPLY_WEAPONSPELL || paf->location == APPLY_WEARSPELL
               || paf->location == APPLY_REMOVESPELL || paf->location == APPLY_STRIPSN )
                  paf->modifier = slot_lookup( atoi( infoflags ) );
               else
                  paf->modifier = atoi( infoflags );
               xCLEAR_BITS( paf->bitvector );
               if( !dadd )
               {
                  LINK( paf, pObjIndex->first_affect, pObjIndex->last_affect, next, prev );
                  top_affect++;
               }
               else
               {
                  bug( "%s: Something is bad in affect and it won't be added.", __FUNCTION__ );
                  DISPOSE( paf );
               }
               fMatch = true;
               break;
            }
            KEY( "Action", pObjIndex->action_desc, fread_string( fp ) );
            break;

         case 'C':
            KEY( "Cost", pObjIndex->cost, fread_number( fp ) );
            if( !str_cmp( word, "Classes" ) )
            {
               int iclass;

               infoflags = fread_flagstring( fp );
               while( infoflags && infoflags[0] != '\0' )
               {
                  infoflags = one_argument( infoflags, flag );
                  for( iclass = 0; iclass < MAX_PC_CLASS; iclass++ )
                  {
                     if( !class_table[iclass] || !class_table[iclass]->name )
                        continue;
                     if( !str_cmp( class_table[iclass]->name, flag ) )
                     {
                        xSET_BIT( pObjIndex->class_restrict, iclass );
                        break;
                     }
                  }
               }
               fMatch = true;
               break;
            }
            break;

         case 'D':
            KEY( "Description", pObjIndex->description, fread_string( fp ) );
            break;

         case 'E':
            if( !strcmp( word, "End" ) )
            {
               if( !oldobj )
               {
                  iHash = vnum % MAX_KEY_HASH;
                  pObjIndex->next = obj_index_hash[iHash];
                  obj_index_hash[iHash] = pObjIndex;
                  top_obj_index++;
               }
               fMatch = true;
               break;
            }
            if( !strcmp( word, "E" ) )
            {
               EXTRA_DESCR_DATA *ed;

               CREATE( ed, EXTRA_DESCR_DATA, 1 );
               ed->keyword = fread_string( fp );
               ed->description = fread_string( fp );
               LINK( ed, pObjIndex->first_extradesc, pObjIndex->last_extradesc, next, prev );
               top_ed++;
               fMatch = true;
               break;
            }
            break;

         case 'F':
            WEXTKEY( "Flags", pObjIndex->extra_flags, fp, o_flags, ITEM_MAX );
            break;

         case 'L':
            KEY( "Level", pObjIndex->level, fread_number( fp ) );
            KEY( "Layers", pObjIndex->layers, fread_number( fp ) );
            break;

         case 'N':
            KEY( "Name", pObjIndex->name, fread_string( fp ) );
            break;

         case 'R':
            if( !str_cmp( word, "Races" ) )
            {
               int irace;

               infoflags = fread_flagstring( fp );
               while( infoflags && infoflags[0] != '\0' )
               {
                  infoflags = one_argument( infoflags, flag );
                  for( irace = 0; irace < MAX_PC_RACE; irace++ )
                  {
                     if( !race_table[irace] || !race_table[irace]->name )
                        continue;
                     if( !str_cmp( race_table[irace]->name, flag ) )
                     {
                        xSET_BIT( pObjIndex->race_restrict, irace );
                        break;
                     }
                  }
               }
               fMatch = true;
               break;
            }
            break;

         case 'S':
            KEY( "Short", pObjIndex->short_descr, fread_string( fp ) );
            if( !strcmp( word, "Stats" ) )
            {
               for( stat = 0; stat < STAT_MAX; stat++ )
                  pObjIndex->stat_reqs[stat] = fread_number( fp );
               fMatch = true;
               break;
            }
            break;

         case 'T':
            SKEY( "Type", pObjIndex->item_type, fp, o_types, ITEM_TYPE_MAX );
            break;

         case 'V':
            if( !strcmp( word, "Val0" ) )
            {
               pObjIndex->value[0] = fread_number( fp );
               fMatch = true;
               break;
            }
            if( !strcmp( word, "Val1" ) )
            {
               pObjIndex->value[1] = fread_number( fp );
               if( pObjIndex->item_type == ITEM_PILL || pObjIndex->item_type == ITEM_POTION
               || pObjIndex->item_type == ITEM_SCROLL )
                  pObjIndex->value[1] = skill_lookup( fread_word( fp ) );
               fMatch = true;
               break;
            }
            if( !strcmp( word, "Val2" ) )
            {
               pObjIndex->value[2] = fread_number( fp );
               if( pObjIndex->item_type == ITEM_PILL || pObjIndex->item_type == ITEM_POTION
               || pObjIndex->item_type == ITEM_SCROLL )
                  pObjIndex->value[2] = skill_lookup( fread_word( fp ) );
               fMatch = true;
               break;
            }
            if( !strcmp( word, "Val3" ) )
            {
               pObjIndex->value[3] = fread_number( fp );
               if( pObjIndex->item_type == ITEM_PILL || pObjIndex->item_type == ITEM_POTION
               || pObjIndex->item_type == ITEM_SCROLL || pObjIndex->item_type == ITEM_STAFF
               || pObjIndex->item_type == ITEM_WAND || pObjIndex->item_type == ITEM_SALVE )
                  pObjIndex->value[3] = skill_lookup( fread_word( fp ) );
               fMatch = true;
               break;
            }
            if( !strcmp( word, "Val4" ) )
            {
               pObjIndex->value[4] = fread_number( fp );
               if( pObjIndex->item_type == ITEM_SALVE )
                  pObjIndex->value[4] = skill_lookup( fread_word( fp ) );
               fMatch = true;
               break;
            }
            if( !strcmp( word, "Val5" ) )
            {
               pObjIndex->value[5] = fread_number( fp );
               if( pObjIndex->item_type == ITEM_SALVE )
                  pObjIndex->value[5] = skill_lookup( fread_word( fp ) );
               fMatch = true;
               break;
            }
            if( !strcmp( word, "Vnum" ) )
            {
               vnum = fread_number( fp );
               tmpBootDb = fBootDb;
               fBootDb = false;
               if( get_obj_index( vnum ) )
               {
                  if( tmpBootDb )
                  {
                     bug( "%s: vnum %d duplicated.", __FUNCTION__, vnum );
                     shutdown_mud( "duplicate vnum" );
                     exit( 1 );
                  }
                  else
                  {
                     pObjIndex = get_obj_index( vnum );
                     log_printf_plus( LOG_BUILD, sysdata.perm_log, "Cleaning object: %d", vnum );
                     clean_obj( pObjIndex );
                     oldobj = true;
                  }
               }
               else
               {
                  oldobj = false;
                  CREATE( pObjIndex, OBJ_INDEX_DATA, 1 );
               }
               fBootDb = tmpBootDb;
               pObjIndex->vnum = vnum;
               if( fBootDb )
               {
                  if( !tarea->low_vnum )
                     tarea->low_vnum = vnum;
                  if( vnum > tarea->hi_vnum )
                     tarea->hi_vnum = vnum;
               }
               fMatch = true;
               break;
            }
            break;

         case 'W':
            KEY( "Weight", pObjIndex->weight, fread_number( fp ) );
            WEXTKEY( "Wear", pObjIndex->wear_flags, fp, w_flags, ITEM_WEAR_MAX );
            break;
      }
      if( !fMatch )
      {
         bug( "%s: no match: %s", __FUNCTION__, word );
         fread_to_eol( fp );
      }
   }
   bug( "%s: #0 not found?", __FUNCTION__ );
}

void load_room_reset( ROOM_INDEX_DATA *room, FILE *fp )
{
   EXIT_DATA *pexit;
   char letter;
   int extra, arg1, arg2, arg3;
   bool not01 = false;
   int count = 0;

   letter = fread_letter( fp );
   extra = fread_number( fp );
   if( letter == 'M' || letter == 'O' )
      extra = 0;
   arg1 = fread_number( fp );
   arg2 = fread_number( fp );
   arg3 = ( letter == 'G' || letter == 'R' ) ? 0 : fread_number( fp );
   fread_to_eol( fp );
   ++count;

   /*
    * Validate parameters.
    * We're calling the index functions for the side effect.
    */
   switch( letter )
   {
      default:
         bug( "%s: bad command '%c'.", __FUNCTION__, letter );
         if( fBootDb )
            boot_log( "%s: %s (%d) bad command '%c'.", __FUNCTION__, room->area->filename, count, letter );
         return;

      case 'M':
         if( !get_mob_index( arg1 ) && fBootDb )
            boot_log( "%s: %s (%d) 'M': mobile %d doesn't exist.", __FUNCTION__, room->area->filename, count, arg1 );
         break;

      case 'O':
         if( !get_obj_index( arg1 ) && fBootDb )
            boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter,
                      arg1 );
         break;

      case 'P':
         if( !get_obj_index( arg1 ) && fBootDb )
            boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter,
                      arg1 );

         if( arg3 <= 0 )
            arg3 = OBJ_VNUM_MONEY_ONE; /* This may look stupid, but for some reason it works. */
         if( !get_obj_index( arg3 ) && fBootDb )
            boot_log( "%s: %s (%d) 'P': destination object %d doesn't exist.", __FUNCTION__, room->area->filename, count,
                      arg3 );
         if( extra > 1 )
            not01 = true;
         break;

      case 'G':
      case 'E':
         if( !get_obj_index( arg1 ) && fBootDb )
            boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter,
                      arg1 );
         break;

      case 'T':
      case 'H':
         break;

      case 'D':
         if( arg2 < 0 || arg2 > MAX_DIR + 1
         || !( pexit = get_exit( room, arg2 ) )
         || !xIS_SET( pexit->exit_info, EX_ISDOOR ) )
         {
            bug( "%s: 'D': exit %d not door.", __FUNCTION__, arg2 );
            bug( "Reset: %c %d %d %d %d", letter, extra, arg1, arg2, arg3 );
            if( fBootDb )
               boot_log( "%s: %s (%d) 'D': exit %d not door.", __FUNCTION__, room->area->filename, count, arg2 );
         }

         if( arg3 < 0 || arg3 > 2 )
         {
            bug( "%s: 'D': bad 'locks': %d.", __FUNCTION__, arg3 );
            if( fBootDb )
               boot_log( "%s: %s (%d) 'D': bad 'locks': %d.", __FUNCTION__, room->area->filename, count, arg3 );
         }
         break;

      case 'R':
         if( arg2 < 0 || arg2 > 10 )
         {
            bug( "%s: 'R': bad exit %d.", __FUNCTION__, arg2 );
            if( fBootDb )
               boot_log( "%s: %s (%d) 'R': bad exit %d.", __FUNCTION__, room->area->filename, count, arg2 );
            break;
         }
         break;
   }
   add_reset( room, letter, extra, arg1, arg2, arg3, 100 );

   if( !not01 )
      renumber_put_resets( room );
}

void load_new_room_reset( ROOM_INDEX_DATA *room, FILE *fp )
{
   EXIT_DATA *pexit;
   char letter;
   int extra, arg1, arg2, arg3;
   bool not01 = false;
   int count = 0;
   short rchance = 0;

   letter = fread_letter( fp );
   extra = fread_number( fp );
   if( letter == 'M' || letter == 'O' )
      extra = 0;
   arg1 = fread_number( fp );
   arg2 = fread_number( fp );
   arg3 = ( letter == 'G' || letter == 'R' ) ? 0 : fread_number( fp );

   rchance = fread_number( fp );
   rchance = URANGE( 1, rchance, 100 );

   fread_to_eol( fp );
   ++count;

   /*
    * Validate parameters.
    * We're calling the index functions for the side effect.
    */
   switch( letter )
   {
      default:
         bug( "%s: bad command '%c'.", __FUNCTION__, letter );
         if( fBootDb )
            boot_log( "%s: %s (%d) bad command '%c'.", __FUNCTION__, room->area->filename, count, letter );
         return;

      case 'M':
         if( !get_mob_index( arg1 ) && fBootDb )
            boot_log( "%s: %s (%d) 'M': mobile %d doesn't exist.", __FUNCTION__, room->area->filename, count, arg1 );
         break;

      case 'O':
         if( !get_obj_index( arg1 ) && fBootDb )
            boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter,
                      arg1 );
         break;

      case 'P':
         if( !get_obj_index( arg1 ) && fBootDb )
            boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter,
                      arg1 );

         if( arg3 <= 0 )
            arg3 = OBJ_VNUM_MONEY_ONE; /* This may look stupid, but for some reason it works. */
         if( !get_obj_index( arg3 ) && fBootDb )
            boot_log( "%s: %s (%d) 'P': destination object %d doesn't exist.", __FUNCTION__, room->area->filename, count,
                      arg3 );
         if( extra > 1 )
            not01 = true;
         break;

      case 'G':
      case 'E':
         if( !get_obj_index( arg1 ) && fBootDb )
            boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter,
                      arg1 );
         break;

      case 'T':
      case 'H':
         break;

      case 'D':
         if( arg2 < 0 || arg2 > MAX_DIR + 1
         || !( pexit = get_exit( room, arg2 ) )
         || !xIS_SET( pexit->exit_info, EX_ISDOOR ) )
         {
            bug( "%s: 'D': exit %d not door.", __FUNCTION__, arg2 );
            bug( "Reset: %c %d %d %d %d", letter, extra, arg1, arg2, arg3 );
            if( fBootDb )
               boot_log( "%s: %s (%d) 'D': exit %d not door.", __FUNCTION__, room->area->filename, count, arg2 );
         }

         if( arg3 < 0 || arg3 > 2 )
         {
            bug( "%s: 'D': bad 'locks': %d.", __FUNCTION__, arg3 );
            if( fBootDb )
               boot_log( "%s: %s (%d) 'D': bad 'locks': %d.", __FUNCTION__, room->area->filename, count, arg3 );
         }
         break;

      case 'R':
         if( arg2 < 0 || arg2 > 10 )
         {
            bug( "%s: 'R': bad exit %d.", __FUNCTION__, arg2 );
            if( fBootDb )
               boot_log( "%s: %s (%d) 'R': bad exit %d.", __FUNCTION__, room->area->filename, count, arg2 );
            break;
         }
         break;
   }
   add_reset( room, letter, extra, arg1, arg2, arg3, rchance );

   if( !not01 )
      renumber_put_resets( room );
}

/* Load a room section. */
void load_rooms( AREA_DATA *tarea, FILE *fp )
{
   ROOM_INDEX_DATA *pRoomIndex;
   const char *word;
   int vnum, door, iHash;
   bool tmpBootDb, oldroom = false, fMatch;
   char *infoflags = NULL;
   char flag[MIL];
   int value;

   if( !tarea )
   {
      bug( "Load_rooms: no #AREA seen yet." );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   tarea->first_room = tarea->last_room = NULL;
   for( ;; )
   {
      word = feof( fp ) ? "End" : fread_word( fp );
      fMatch = false;
      if( word[0] == EOF )
      {
         bug( "%s: feof didn't detect EOF but fread_word returned EOF...ending now!", __FUNCTION__ );
         word = "End";
      }

      switch( UPPER( word[0] ) )
      {
         case '*':
            fMatch = true;
            fread_to_eol( fp );
            break;

         case '#':
            if( !strcmp( word, "#0" ) )
               return;
            break;

         case '>':
            if( !strcmp( word, ">" ) )
            {
               ungetc( '>', fp );
               rprog_read_programs( fp, pRoomIndex );
               fMatch = true;
               break;
            }
            break;

         case 'D':
            KEY( "Description", pRoomIndex->description, fread_string( fp ) );
            if( !strcmp( word, "Door" ) )
            {
               EXIT_DATA *pexit;
               const char *check;
               bool fcheck, finished = false;

               door = fread_number( fp );
               if( door < 0 || door > 10 )
               {
                  bug( "%s: vnum %d has bad door number %d.", __FUNCTION__, vnum, door );
                  if( fBootDb )
                     exit( 1 );
               }
               else
               {
                  pexit = make_exit( pRoomIndex, NULL, door );
                  pexit->key = -1;
                  for( ;; )
                  {
                     check = feof( fp ) ? "End" : fread_word( fp );
                     fcheck = false;
                     if( check[0] == EOF )
                     {
                        bug( "%s: feof didn't detect EOF but fread_word returned EOF...ending now!", __FUNCTION__ );
                        check = "End";
                     }
                     switch( UPPER( check[0] ) )
                     {
                        case '*':
                           fcheck = true;
                           fread_to_eol( fp );
                           break;
                        case '#':
                           if( !strcmp( check, "#0" ) )
                              return;
                           break;
                        case 'D':
                           if( !strcmp( check, "Description" ) )
                           {
                              pexit->description = fread_string( fp );
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'E':
                           if( !strcmp( check, "End" ) )
                           {
                              finished = true;
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'F':
                           if( !strcmp( check, "Flags" ) )
                           {
                              infoflags = fread_flagstring( fp );
                              while( infoflags && infoflags[0] != '\0' )
                              {
                                 infoflags = one_argument( infoflags, flag );
                                 value = get_flag( flag, ex_flags, EX_MAX );
                                 if( value < 0 || value >= EX_MAX )
                                    bug( "%s: Unknown exit_info: %s", __FUNCTION__, flag );
                                 else
                                    xSET_BIT( pexit->exit_info, value );
                              }
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'K':
                           if( !strcmp( check, "Key" ) )
                           {
                              pexit->key = fread_number( fp );
                              fcheck = true;
                              break;
                           }
                           if( !strcmp( check, "Keyword" ) )
                           {
                              pexit->keyword = fread_string( fp );
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'P':
                           if( !strcmp( check, "Pulltype" ) )
                           {
                              infoflags = fread_flagstring( fp );
                              while( infoflags && infoflags[0] != '\0' )
                              {
                                  infoflags = one_argument( infoflags, flag );
                                  value = get_pulltype( flag );
                                  if( value == -1 )
                                     bug( "%s: Unknown pulltype: %s", __FUNCTION__, flag );
                                  else
                                     pexit->pulltype = value;
                              }
                              fcheck = true;
                              break;
                           }
                           if( !strcmp( check, "Pull" ) )
                           {
                              pexit->pull = fread_number( fp );
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'T':
                           if( !strcmp( check, "To" ) )
                           {
                              pexit->vnum = fread_number( fp );
                              fcheck = true;
                              break;
                           }
                           break;
                     }
                     if( finished )
                        break;
                     if( !fcheck )
                     {
                        bug( "%s: %s: Unknown word %s", __FUNCTION__, word, check );
                        fread_to_eol( fp );
                     }
                  }
               }
               fMatch = true;
               break;
            }
            break;

         case 'E':
            if( !strcmp( word, "Exit" ) )
            {
               EXIT_DATA *pexit;
               const char *check;
               bool fcheck, finished = false;

               infoflags = fread_word( fp );
               door = get_dir( infoflags );
               if( door < 0 || door > 10 )
               {
                  bug( "%s: vnum %d has bad door number %d(%s).", __FUNCTION__, vnum, door, infoflags );
                  if( fBootDb )
                     exit( 1 );
               }
               else
               {
                  pexit = make_exit( pRoomIndex, NULL, door );
                  pexit->key = -1;
                  for( ;; )
                  {
                     check = feof( fp ) ? "End" : fread_word( fp );
                     fcheck = false;
                     if( check[0] == EOF )
                     {
                        bug( "%s: feof didn't detect EOF but fread_word returned EOF...ending now!", __FUNCTION__ );
                        check = "End";
                     }
                     switch( UPPER( check[0] ) )
                     {
                        case '*':
                           fcheck = true;
                           fread_to_eol( fp );
                           break;
                        case '#':
                           if( !strcmp( check, "#0" ) )
                              return;
                           break;
                        case 'D':
                           if( !strcmp( check, "Description" ) )
                           {
                              pexit->description = fread_string( fp );
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'E':
                           if( !strcmp( check, "End" ) )
                           {
                              finished = true;
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'F':
                           if( !strcmp( check, "Flags" ) )
                           {
                              infoflags = fread_flagstring( fp );
                              while( infoflags && infoflags[0] != '\0' )
                              {
                                 infoflags = one_argument( infoflags, flag );
                                 value = get_flag( flag, ex_flags, EX_MAX );
                                 if( value < 0 || value >= EX_MAX )
                                    bug( "%s: Unknown exit_info: %s", __FUNCTION__, flag );
                                 else
                                    xSET_BIT( pexit->exit_info, value );
                              }
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'K':
                           if( !strcmp( check, "Key" ) )
                           {
                              pexit->key = fread_number( fp );
                              fcheck = true;
                              break;
                           }
                           if( !strcmp( check, "Keyword" ) )
                           {
                              pexit->keyword = fread_string( fp );
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'P':
                           if( !strcmp( check, "Pulltype" ) )
                           {
                              infoflags = fread_flagstring( fp );
                              while( infoflags && infoflags[0] != '\0' )
                              {
                                  infoflags = one_argument( infoflags, flag );
                                  value = get_pulltype( flag );
                                  if( value == -1 )
                                     bug( "%s: Unknown pulltype: %s", __FUNCTION__, flag );
                                  else
                                     pexit->pulltype = value;
                              }
                              fcheck = true;
                              break;
                           }
                           if( !strcmp( check, "Pull" ) )
                           {
                              pexit->pull = fread_number( fp );
                              fcheck = true;
                              break;
                           }
                           break;
                        case 'T':
                           if( !strcmp( check, "To" ) )
                           {
                              pexit->vnum = fread_number( fp );
                              fcheck = true;
                              break;
                           }
                           break;
                     }
                     if( finished )
                        break;
                     if( !fcheck )
                     {
                        bug( "%s: %s: Unknown word %s", __FUNCTION__, word, check );
                        fread_to_eol( fp );
                     }
                  }
               }
               fMatch = true;
               break;
            }
            if( !strcmp( word, "End" ) )
            {
               if( !oldroom )
               {
                  iHash = vnum % MAX_KEY_HASH;
                  pRoomIndex->next = room_index_hash[iHash];
                  room_index_hash[iHash] = pRoomIndex;
                  LINK( pRoomIndex, tarea->first_room, tarea->last_room, next_aroom, prev_aroom );
                  top_room++;
               }
               fMatch = true;
               break;
            }
            if( !strcmp( word, "E" ) )
            {
               EXTRA_DESCR_DATA *ed;

               CREATE( ed, EXTRA_DESCR_DATA, 1 );
               ed->keyword = fread_string( fp );
               ed->description = fread_string( fp );
               LINK( ed, pRoomIndex->first_extradesc, pRoomIndex->last_extradesc, next, prev );
               top_ed++;
               fMatch = true;
               break;
            }
            break;

         case 'F':
            if( !strcmp( word, "Flags" ) )
            {
               infoflags = fread_flagstring( fp );
               while( infoflags && infoflags[0] != '\0' )
               {
                  infoflags = one_argument( infoflags, flag );
                  value = get_flag( flag, r_flags, ROOM_MAX );
                  if( value < 0 || value >= ROOM_MAX )
                     bug( "%s: Unknown flag: %s", __FUNCTION__, flag );
                  else
                     xSET_BIT( pRoomIndex->room_flags, value );
               }
               fMatch = true;
               break;
            }
            break;

         case 'N':
            KEY( "Name", pRoomIndex->name, fread_string( fp ) );
            if( !strcmp( word, "NR" ) )
            {
               load_new_room_reset( pRoomIndex, fp );
               fMatch = true;
               break;
            }
            break;

         case 'R':
            if( !strcmp( word, "R" ) )
            {
               load_room_reset( pRoomIndex, fp );
               fMatch = true;
               break;
            }
            break;

         case 'S':
            SKEY( "Sector", pRoomIndex->sector_type, fp, sect_flags, SECT_MAX );
            break;

         case 'T':
            KEY( "Teledelay", pRoomIndex->tele_delay, fread_number( fp ) );
            KEY( "Televnum", pRoomIndex->tele_vnum, fread_number( fp ) );
            KEY( "Tunnel", pRoomIndex->tunnel, fread_number( fp ) );
            break;

         case 'V':
            if( !strcmp( word, "Vnum" ) )
            {
               vnum = fread_number( fp );
               tmpBootDb = fBootDb;
               fBootDb = false;
               if( get_room_index( vnum ) )
               {
                  if( tmpBootDb )
                  {
                     bug( "%s: vnum %d duplicated.", __FUNCTION__, vnum );
                     shutdown_mud( "duplicate vnum" );
                     exit( 1 );
                  }
                  else
                  {
                     pRoomIndex = get_room_index( vnum );
                     log_printf_plus( LOG_BUILD, sysdata.perm_log, "Cleaning room: %d", vnum );
                     clean_room( pRoomIndex );
                     oldroom = true;
                  }
               }
               else
               {
                  oldroom = false;
                  CREATE( pRoomIndex, ROOM_INDEX_DATA, 1 );
                  pRoomIndex->first_person = NULL;
                  pRoomIndex->last_person = NULL;
                  pRoomIndex->first_content = NULL;
                  pRoomIndex->last_content = NULL;
                  pRoomIndex->first_extradesc = NULL;
                  pRoomIndex->last_extradesc = NULL;
                  pRoomIndex->area = tarea;
                  pRoomIndex->vnum = vnum;
                  fBootDb = tmpBootDb;
                  if( fBootDb )
                  {
                     if( !tarea->low_vnum )
                        tarea->low_vnum = vnum;
                     if( vnum > tarea->hi_vnum )
                        tarea->hi_vnum = vnum;
                  }
               }
               pRoomIndex->light = 0;
               pRoomIndex->first_exit = NULL;
               pRoomIndex->last_exit = NULL;
               fMatch = true;
               break;
            }
            break;
      }
      if( !fMatch )
      {
         bug( "%s: no match: %s", __FUNCTION__, word );
         fread_to_eol( fp );
      }
   }
   bug( "%s: #0 not found?", __FUNCTION__ );
}

/* Load soft / hard area ranges. */
void load_ranges( AREA_DATA *tarea, FILE *fp )
{
   int x1, x2, x3, x4;
   char *ln;

   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }
   ln = fread_line( fp );
   sscanf( ln, "%d %d %d %d", &x1, &x2, &x3, &x4 );
   tarea->low_soft_range = x1;
   tarea->hi_soft_range = x2;
   tarea->low_hard_range = x3;
   tarea->hi_hard_range = x4;
}

/* Load climate information for the area - Fireblade */
void load_climate( AREA_DATA *tarea, FILE *fp )
{
   if( !tarea )
   {
      bug( "%s: no #AREA seen yet", __FUNCTION__ );
      if( fBootDb )
      {
         shutdown_mud( "No #AREA" );
         exit( 1 );
      }
      else
         return;
   }

   tarea->weather->climate_temp = fread_number( fp );
   tarea->weather->climate_precip = fread_number( fp );
   tarea->weather->climate_wind = fread_number( fp );
}

/* Load data for a neghboring weather system - Fireblade */
void load_neighbor( AREA_DATA *tarea, FILE *fp )
{
   NEIGHBOR_DATA *tnew;

   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      if( fBootDb )
      {
         shutdown_mud( "No #AREA" );
         exit( 1 );
      }
      else
         return;
   }

   CREATE( tnew, NEIGHBOR_DATA, 1 );
   tnew->next = NULL;
   tnew->prev = NULL;
   tnew->address = NULL;
   tnew->name = fread_string( fp );
   LINK( tnew, tarea->weather->first_neighbor, tarea->weather->last_neighbor, next, prev );
}

/*
 * Translate all room exits from virtual to real.
 * Has to be done after all rooms are read in.
 * Check for bad reverse exits.
 */
void fix_exits( void )
{
   ROOM_INDEX_DATA *pRoomIndex;
   EXIT_DATA *pexit, *pexit_next, *r_exit;
   int iHash;

   for( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
   {
      for( pRoomIndex = room_index_hash[iHash]; pRoomIndex; pRoomIndex = pRoomIndex->next )
      {
         bool fexit = false;

         for( pexit = pRoomIndex->first_exit; pexit; pexit = pexit_next )
         {
            pexit_next = pexit->next;
            pexit->rvnum = pRoomIndex->vnum;
            if( pexit->vnum <= 0 || !( pexit->to_room = get_room_index( pexit->vnum ) ) )
            {
               if( fBootDb )
                  boot_log( "Fix_exits: room %d, exit %s leads to bad vnum (%d)",
                            pRoomIndex->vnum, dir_name[pexit->vdir], pexit->vnum );

               bug( "Deleting %s exit in room %d", dir_name[pexit->vdir], pRoomIndex->vnum );
               extract_exit( pRoomIndex, pexit );
            }
            else
               fexit = true;
         }
      }
   }

   /* Set all the rexit pointers - Thoric */
   for( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
   {
      for( pRoomIndex = room_index_hash[iHash]; pRoomIndex; pRoomIndex = pRoomIndex->next )
      {
         for( pexit = pRoomIndex->first_exit; pexit; pexit = pexit->next )
         {
            if( pexit->to_room && !pexit->rexit )
            {
               r_exit = get_exit_to( pexit->to_room, rev_dir[pexit->vdir], pRoomIndex->vnum );
               if( r_exit )
               {
                  pexit->rexit = r_exit;
                  r_exit->rexit = pexit;
               }
            }
         }
      }
   }
   return;
}

/*
 * (prelude...) This is going to be fun... NOT!
 * (conclusion) QSort is f*cked!
 */
int exit_comp( EXIT_DATA **xit1, EXIT_DATA **xit2 )
{
   int d1, d2;

   d1 = ( *xit1 )->vdir;
   d2 = ( *xit2 )->vdir;

   if( d1 < d2 )
      return -1;
   if( d1 > d2 )
      return 1;
   return 0;
}

void sort_exits( ROOM_INDEX_DATA *room )
{
   EXIT_DATA *pexit;
   EXIT_DATA *exits[MAX_REXITS];
   int x, nexits;

   nexits = 0;
   for( pexit = room->first_exit; pexit; pexit = pexit->next )
   {
      exits[nexits++] = pexit;
      if( nexits > MAX_REXITS )
      {
         bug( "%s: more than %d exits in room... fatal", __FUNCTION__, nexits );
         return;
      }
   }
   qsort( &exits[0], nexits, sizeof( EXIT_DATA *), ( int ( * )( const void *, const void * ) )exit_comp );
   for( x = 0; x < nexits; x++ )
   {
      if( x > 0 )
         exits[x]->prev = exits[x - 1];
      else
      {
         exits[x]->prev = NULL;
         room->first_exit = exits[x];
      }
      if( x >= ( nexits - 1 ) )
      {
         exits[x]->next = NULL;
         room->last_exit = exits[x];
      }
      else
         exits[x]->next = exits[x + 1];
   }
}

void randomize_exits( ROOM_INDEX_DATA *room, short maxdir )
{
   EXIT_DATA *pexit;
   int nexits, d0, d1, count, door;
   int vdirs[MAX_REXITS];

   nexits = 0;
   for( pexit = room->first_exit; pexit; pexit = pexit->next )
      vdirs[nexits++] = pexit->vdir;

   for( d0 = 0; d0 < nexits; d0++ )
   {
      if( vdirs[d0] > maxdir )
         continue;
      count = 0;
      while( vdirs[( d1 = number_range( d0, nexits - 1 ) )] > maxdir || ++count > 5 );
      if( vdirs[d1] > maxdir )
         continue;
      door = vdirs[d0];
      vdirs[d0] = vdirs[d1];
      vdirs[d1] = door;
   }
   count = 0;
   for( pexit = room->first_exit; pexit; pexit = pexit->next )
      pexit->vdir = vdirs[count++];

   sort_exits( room );
}

/* Repopulate areas periodically. */
void area_update( void )
{
   AREA_DATA *pArea;

   for( pArea = first_area; pArea; pArea = pArea->next )
   {
      CHAR_DATA *pch;
      int reset_age = pArea->reset_frequency ? pArea->reset_frequency : 15;

      if( ( reset_age == -1 && pArea->age == -1 ) || ++pArea->age < ( reset_age - 1 ) )
         continue;

      /* Check for PC's. */
      if( pArea->nplayer > 0 && pArea->age == ( reset_age - 1 ) )
      {
         char buf[MSL];

         /* Rennard */
         if( pArea->resetmsg )
            snprintf( buf, sizeof( buf ), "%s\r\n", pArea->resetmsg );
         else
            mudstrlcpy( buf, "You hear some squeaking sounds...\r\n", sizeof( buf ) );
         for( pch = first_char; pch; pch = pch->next )
         {
            if( !is_npc( pch ) && is_awake( pch ) && pch->in_room && pch->in_room->area == pArea )
            {
               set_char_color( AT_RESET, pch );
               send_to_char( buf, pch );
            }
         }
      }

      /*
       * Check age and reset.
       * Note: Mud Academy resets every 3 minutes (not 15).
       */
      if( pArea->nplayer == 0 || pArea->age >= reset_age )
      {
         ROOM_INDEX_DATA *pRoomIndex;

         reset_area( pArea );
         if( reset_age == -1 )
            pArea->age = -1;
         else
            pArea->age = number_range( 0, reset_age / 5 );
         pRoomIndex = get_room_index( sysdata.room_school );
         if( pRoomIndex && pArea == pRoomIndex->area && pArea->reset_frequency == 0 )
            pArea->age = 15 - 3;
      }
   }
}

/* Create an instance of a mobile. */
CHAR_DATA *create_mobile( MOB_INDEX_DATA *pMobIndex )
{
   CHAR_DATA *mob;
   int stat;

   if( !pMobIndex )
   {
      bug( "%s: NULL pMobIndex.", __FUNCTION__ );
      exit( 1 );
   }

   CREATE( mob, CHAR_DATA, 1 );
   if( !mob )
   {
      bug( "%s: mob is still NULL after CREATE.", __FUNCTION__ );
      return NULL;
   }

   clear_char( mob );
   mob->pIndexData = pMobIndex;
   mob->name = QUICKLINK( pMobIndex->name );
   mob->short_descr = QUICKLINK( pMobIndex->short_descr );
   mob->long_descr = QUICKLINK( pMobIndex->long_descr );
   mob->description = QUICKLINK( pMobIndex->description );
   mob->spec_fun = pMobIndex->spec_fun;
   mob->spec_funname = QUICKLINK( pMobIndex->spec_funname );
   mob->mpscriptpos = 0;
   mob->level = number_range( pMobIndex->level - 1, pMobIndex->level + 1 );
   mob->level = URANGE( 1, mob->level, MAX_LEVEL );
   mob->act = pMobIndex->act;
   mob->resetvnum = -1;
   mob->resetnum = -1;
   if( xIS_SET( mob->act, ACT_MOBINVIS ) )
      mob->mobinvis = mob->level;
   mob->affected_by = pMobIndex->affected_by;
   mob->alignment = pMobIndex->alignment;
   mob->sex = pMobIndex->sex;

   if( pMobIndex->ac )
      mob->armor = pMobIndex->ac;
   else
      mob->armor = interpolate( mob->level, 100, -100 );

   mob->max_hit = UMAX( 1, number_range( pMobIndex->minhit, pMobIndex->maxhit ) );
   mob->hit = mob->max_hit;

   mob->gold = pMobIndex->gold;
   mob->mgold = pMobIndex->mgold;
   mob->exp = 0.0;
   mob->position = pMobIndex->position;
   mob->defposition = pMobIndex->defposition;

   for( stat = 0; stat < STAT_MAX; stat++ )
      mob->perm_stats[stat] = pMobIndex->perm_stats[stat];

   mob->hitroll = pMobIndex->hitroll;
   mob->damroll = pMobIndex->damroll;
   mob->xflags = pMobIndex->xflags;
   mob->saving_poison_death = pMobIndex->saving_poison_death;
   mob->saving_wand = pMobIndex->saving_wand;
   mob->saving_para_petri = pMobIndex->saving_para_petri;
   mob->saving_breath = pMobIndex->saving_breath;
   mob->saving_spell_staff = pMobIndex->saving_spell_staff;
   mob->height = pMobIndex->height;
   mob->weight = pMobIndex->weight;
   mob->resistant = pMobIndex->resistant;
   mob->immune = pMobIndex->immune;
   mob->susceptible = pMobIndex->susceptible;
   mob->attacks = pMobIndex->attacks;
   mob->defenses = pMobIndex->defenses;
   mob->numattacks = pMobIndex->numattacks;
   mob->speaks = pMobIndex->speaks;
   mob->speaking = pMobIndex->speaking;

   add_char( mob );
   pMobIndex->count++;
   nummobsloaded++;
   return mob;
}

/* Create an instance of an object. */
OBJ_DATA *create_object( OBJ_INDEX_DATA *pObjIndex, int level )
{
   OBJ_DATA *obj;

   if( !pObjIndex )
   {
      bug( "%s: NULL pObjIndex. EXITING THE MUD...", __FUNCTION__ );
      exit( 1 );
   }

   CREATE( obj, OBJ_DATA, 1 );
   if( !obj )
   {
      bug( "%s: obj is still NULL after CREATE.", __FUNCTION__ );
      return NULL;
   }

   obj->pIndexData = pObjIndex;
   obj->in_room = NULL;
   obj->level = level;
   obj->wear_loc = -1;
   obj->t_wear_loc = -1;
   obj->count = 1;

   obj->name = QUICKLINK( pObjIndex->name );
   obj->short_descr = QUICKLINK( pObjIndex->short_descr );
   obj->description = QUICKLINK( pObjIndex->description );
   obj->action_desc = QUICKLINK( pObjIndex->action_desc );
   obj->owner = NULL;
   obj->item_type = pObjIndex->item_type;
   obj->extra_flags = pObjIndex->extra_flags;
   obj->wear_flags = pObjIndex->wear_flags;
   obj->value[0] = pObjIndex->value[0];
   obj->value[1] = pObjIndex->value[1];
   obj->value[2] = pObjIndex->value[2];
   obj->value[3] = pObjIndex->value[3];
   obj->value[4] = pObjIndex->value[4];
   obj->value[5] = pObjIndex->value[5];
   obj->weight = pObjIndex->weight;
   obj->cost = pObjIndex->cost;

   /* Mess with object properties. */
   switch( obj->item_type )
   {
      default:
         break;

      case ITEM_COOK:
      case ITEM_FOOD:
      case ITEM_FISH:
         obj->timer = obj->value[1];
         break;

      case ITEM_MONEY:
         obj->value[0] = UMAX( 1, obj->cost );
         break;
   }

   LINK( obj, first_object, last_object, next, prev );
   if( obj->pIndexData->vnum == OBJ_VNUM_CORPSE_PC )
      LINK( obj, first_corpse, last_corpse, next_corpse, prev_corpse );
   ++pObjIndex->count;
   ++numobjsloaded;
   ++physicalobjects;

   return obj;
}

/* Clear a new character. */
void clear_char( CHAR_DATA *ch )
{
   int stat;

   ch->editor = NULL;
   ch->hunting = NULL;
   ch->fearing = NULL;
   ch->hating = NULL;
   ch->name = NULL;
   ch->short_descr = NULL;
   ch->long_descr = NULL;
   ch->description = NULL;
   ch->next = ch->prev = NULL;
   ch->reply = NULL;
   ch->retell = NULL;
   ch->first_carrying = ch->last_carrying = NULL;
   ch->next_in_room = ch->prev_in_room = NULL;
   ch->fighting = NULL;
   ch->first_affect = ch->last_affect = NULL;
   ch->first_host = ch->last_host = NULL;
   ch->prev_cmd = ch->last_cmd = NULL;
   ch->dest_buf = NULL;
   ch->alloc_ptr = NULL;
   ch->spare_ptr = NULL;
   ch->mount = NULL;
   ch->morph = NULL;
   ch->was_in_room = NULL;
   xCLEAR_BITS( ch->act );
   xCLEAR_BITS( ch->affected_by );
   xCLEAR_BITS( ch->no_affected_by );
   xCLEAR_BITS( ch->resistant );
   xCLEAR_BITS( ch->no_resistant );
   xCLEAR_BITS( ch->susceptible );
   xCLEAR_BITS( ch->no_susceptible );
   xCLEAR_BITS( ch->immune );
   xCLEAR_BITS( ch->no_immune );
   xCLEAR_BITS( ch->xflags );
   xCLEAR_BITS( ch->speaking );
   xSET_BIT( ch->speaking, LANG_COMMON );
   xCLEAR_BITS( ch->speaks );
   xSET_BIT( ch->speaks, LANG_COMMON );
   ch->logon = current_time;
   ch->armor = 0;
   ch->position = POS_STANDING;
   ch->mobinvis = 0;
   ch->practice = 0;
   ch->hit = 20;
   ch->max_hit = 20;
   ch->mana = 100;
   ch->max_mana = 100;
   ch->move = 100;
   ch->max_move = 100;
   ch->height = 72;
   ch->weight = 180;
   ch->race = 0;
   ch->Class = -1;
   ch->substate = 0;
   ch->tempnum = 0;
   ch->mental_state = -10;
   ch->saving_poison_death = 0;
   ch->saving_wand = 0;
   ch->saving_para_petri = 0;
   ch->saving_breath = 0;
   ch->saving_spell_staff = 0;
   ch->style = STYLE_FIGHTING;
   for( stat = 0; stat < STAT_MAX; stat++ )
      ch->perm_stats[stat] = 13;
}

/* Free a character. */
void free_char( CHAR_DATA *ch )
{
   CHAR_DATA *pet, *pet_next;
   OBJ_DATA *obj;
   AFFECT_DATA *paf;
   TIMER *timer;
   MPROG_ACT_LIST *mpact, *mpact_next;
   HOST_DATA *host, *host_next;

   if( !ch )
   {
      bug( "%s: null ch!", __FUNCTION__ );
      return;
   }

   if( ch->desc )
      bug( "%s: char still has descriptor.", __FUNCTION__ );

   if( ch->morph )
      DISPOSE( ch->morph );

   while( ( obj = ch->last_carrying ) )
      extract_obj( obj );

   while( ( paf = ch->last_affect ) )
      affect_remove( ch, paf );

   while( ( timer = ch->first_timer ) )
      extract_timer( ch, timer );

   if( ch->editor )
      stop_editing( ch );

   for( host = ch->first_host; host; host = host_next )
   {
      host_next = host->next;
      STRFREE( host->host );
      UNLINK( host, ch->first_host, ch->last_host, next, prev );
      DISPOSE( host );
   }

   STRFREE( ch->name );
   STRFREE( ch->short_descr );
   STRFREE( ch->long_descr );
   STRFREE( ch->description );
   STRFREE( ch->spec_funname );

   stop_hunting( ch );
   stop_hating( ch );
   stop_fearing( ch );
   free_fight( ch );

   if( ch->pnote )
      free_note( ch->pnote );

   if( ch->pcdata )
   {
      IGNORE_DATA *temp, *next;

      for( pet = ch->pcdata->first_pet; pet; pet = pet_next )
      {
         pet_next = pet->next_pet;
         UNLINK( pet, ch->pcdata->first_pet, ch->pcdata->last_pet, next_pet, prev_pet );
         extract_char( pet, true );
      }

      if( ch->pcdata->gnote )
         free_note( ch->pcdata->gnote );

      /* free up memory allocated to stored ignored names */
      for( temp = ch->pcdata->first_ignored; temp; temp = next )
      {
         next = temp->next;
         UNLINK( temp, ch->pcdata->first_ignored, ch->pcdata->last_ignored, next, prev );
         STRFREE( temp->name );
         DISPOSE( temp );
      }

      STRFREE( ch->pcdata->channels );
      STRFREE( ch->pcdata->spouse );
      STRFREE( ch->pcdata->filename );
      STRFREE( ch->pcdata->pwd );
      STRFREE( ch->pcdata->bamfin );
      STRFREE( ch->pcdata->bamfout );
      STRFREE( ch->pcdata->rank );
      STRFREE( ch->pcdata->title );
      STRFREE( ch->pcdata->bio );
      STRFREE( ch->pcdata->bestowments );
      STRFREE( ch->pcdata->homepage );
      STRFREE( ch->pcdata->email );
      STRFREE( ch->pcdata->msn );
      STRFREE( ch->pcdata->yahoo );
      STRFREE( ch->pcdata->gtalk );
      STRFREE( ch->pcdata->authed_by );
      STRFREE( ch->pcdata->prompt );
      STRFREE( ch->pcdata->fprompt );
      STRFREE( ch->pcdata->subprompt );
      while( ch->pcdata->last_tell )
      {
         PER_HISTORY *phistory;

         phistory = ch->pcdata->last_tell;
         UNLINK( phistory, ch->pcdata->first_tell, ch->pcdata->last_tell, next, prev );
         free_phistory( phistory );
      }
      while( ch->pcdata->last_say )
      {
         PER_HISTORY *phistory;

         phistory = ch->pcdata->last_say;
         UNLINK( phistory, ch->pcdata->first_say, ch->pcdata->last_say, next, prev );
         free_phistory( phistory );
      }
      while( ch->pcdata->last_yell )
      {
         PER_HISTORY *phistory;

         phistory = ch->pcdata->last_yell;
         UNLINK( phistory, ch->pcdata->first_yell, ch->pcdata->last_yell, next, prev );
         free_phistory( phistory );
      }
      while( ch->pcdata->last_whisper )
      {
         PER_HISTORY *phistory;

         phistory = ch->pcdata->last_whisper;
         UNLINK( phistory, ch->pcdata->first_whisper, ch->pcdata->last_whisper, next, prev );
         free_phistory( phistory );
      }
#ifdef IMC
      imc_freechardata( ch );
#endif
      DISPOSE( ch->pcdata );
   }

   for( mpact = ch->mpact; mpact; mpact = mpact_next )
   {
      mpact_next = mpact->next;
      STRFREE( mpact->buf );
      DISPOSE( mpact );
   }

   DISPOSE( ch );
}

/* Get an extra description from a list. */
char *get_extra_descr( const char *name, EXTRA_DESCR_DATA *ed )
{
   for( ; ed; ed = ed->next )
      if( is_name( name, ed->keyword ) )
         return ed->description;
   return NULL;
}

/*
 * Translates mob virtual number to its mob index struct.
 * Hash table lookup.
 */
MOB_INDEX_DATA *get_mob_index( int vnum )
{
   MOB_INDEX_DATA *pMobIndex;

   if( vnum < 0 )
      vnum = 0;

   for( pMobIndex = mob_index_hash[vnum % MAX_KEY_HASH]; pMobIndex; pMobIndex = pMobIndex->next )
      if( pMobIndex->vnum == vnum )
         return pMobIndex;

   if( fBootDb )
      bug( "%s: bad vnum %d.", __FUNCTION__, vnum );

   return NULL;
}

/*
 * Translates obj virtual number to its obj index struct.
 * Hash table lookup.
 */
OBJ_INDEX_DATA *get_obj_index( int vnum )
{
   OBJ_INDEX_DATA *pObjIndex;

   if( vnum < 0 )
      vnum = 0;

   for( pObjIndex = obj_index_hash[vnum % MAX_KEY_HASH]; pObjIndex; pObjIndex = pObjIndex->next )
      if( pObjIndex->vnum == vnum )
         return pObjIndex;

   if( fBootDb )
      bug( "%s: bad vnum %d.", __FUNCTION__, vnum );

   return NULL;
}

/*
 * Translates room virtual number to its room index struct.
 * Hash table lookup.
 */
ROOM_INDEX_DATA *get_room_index( int vnum )
{
   ROOM_INDEX_DATA *pRoomIndex;

   if( vnum < 0 )
      vnum = 0;

   for( pRoomIndex = room_index_hash[vnum % MAX_KEY_HASH]; pRoomIndex; pRoomIndex = pRoomIndex->next )
      if( pRoomIndex->vnum == vnum )
         return pRoomIndex;

   if( fBootDb )
      bug( "%s: bad vnum %d.", __FUNCTION__, vnum );

   return NULL;
}

/*
 * Added lots of EOF checks, as most of the file crashes are based on them.
 * If an area file encounters EOF, the fread_* functions will shutdown the
 * MUD, as all area files should be read in in full or bad things will
 * happen during the game.  Any files loaded in without fBootDb which
 * encounter EOF will return what they have read so far.   These files
 * should include player files, and in-progress areas that aren't loaded
 * upon bootup.
 * -- Altrag
 */

/* Read a letter from a file. */
char fread_letter( FILE *fp )
{
   char c;

   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return '\0';
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   return c;
}

/* Read a number from a file. */
int fread_number( FILE *fp )
{
   int number;
   bool sign;
   char c;

   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return 0;
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   number = 0;

   sign = false;
   if( c == '+' )
   {
      c = getc( fp );
   }
   else if( c == '-' )
   {
      sign = true;
      c = getc( fp );
   }

   if( !isdigit( c ) )
   {
      bug( "%s: bad format. (%c)", __FUNCTION__, c );
      if( fBootDb )
         exit( 1 );
      return 0;
   }

   while( isdigit( c ) )
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return number;
      }
      number = number * 10 + c - '0';
      c = getc( fp );
   }

   if( sign )
      number = 0 - number;

   if( c == '|' )
      number += fread_number( fp );
   else if( c != ' ' )
      ungetc( c, fp );

   return number;
}

unsigned int fread_un_number( FILE *fp )
{
   unsigned int number;
   char c;

   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return 0;
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   number = 0;

   if( !isdigit( c ) )
   {
      bug( "%s: bad format. (%c)", __FUNCTION__, c );
      if( fBootDb )
         exit( 1 );
      return 0;
   }

   while( isdigit( c ) )
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return number;
      }
      number = number * 10 + c - '0';
      c = getc( fp );
   }

   if( c != ' ' )
      ungetc( c, fp );

   return number;
}

/* Read a time from a file. */
time_t fread_time( FILE *fp )
{
   time_t number;
   bool sign;
   char c;

   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return 0;
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   number = 0;

   sign = false;
   if( c == '+' )
   {
      c = getc( fp );
   }
   else if( c == '-' )
   {
      sign = true;
      c = getc( fp );
   }

   if( !isdigit( c ) )
   {
      bug( "%s: bad format. (%c)", __FUNCTION__, c );
      if( fBootDb )
         exit( 1 );
      return 0;
   }

   while( isdigit( c ) )
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return number;
      }
      number = number * 10 + c - '0';
      c = getc( fp );
   }

   if( sign )
      number = ( 0 - number );

   if( c == '|' )
      number += fread_time( fp );
   else if( c != ' ' )
      ungetc( c, fp );

   return number;
}

/* Read a double from a file. */
double fread_double( FILE *fp )
{
   double number;
   bool sign;
   char c;

   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return 0;
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   number = 0.0;

   sign = false;
   if( c == '+' )
   {
      c = getc( fp );
   }
   else if( c == '-' )
   {
      sign = true;
      c = getc( fp );
   }

   if( !isdigit( c ) )
   {
      bug( "%s: bad format. (%c)", __FUNCTION__, c );
      if( fBootDb )
         exit( 1 );
      return 0;
   }

   while( isdigit( c ) )
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return number;
      }
      number = number * 10 + c - '0';
      c = getc( fp );
   }

   if( sign )
      number = 0 - number;

   if( c == '|' )
      number += fread_double( fp );
   else if( c != ' ' )
      ungetc( c, fp );

   return number;
}

/* custom str_dup using create -Thoric */
char *str_dup( char const *str )
{
   static char *ret;
   int len;

   if( !str )
      return NULL;

   len = strlen( str ) + 1;

   CREATE( ret, char, len );
   mudstrlcpy( ret, str, MSL );
   return ret;
}

/* Read a string from file and return it */
char *fread_flagstring( FILE *fp )
{
   static char flagstring[MSL];
   char *plast;
   char c;
   int ln;

   plast = flagstring;
   flagstring[0] = '\0';
   ln = 0;

   /* Skip blanks. Read first char. */
   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return "";
      }
      c = getc( fp );
   }
   while( isspace( c ) );
   if( ( *plast++ = c ) == '~' )
      return "";

   for( ;; )
   {
      if( ln >= ( MSL - 1 ) )
      {
         bug( "%s: string too long", __FUNCTION__ );
         *plast = '\0';
         return flagstring;
      }
      switch( *plast = getc( fp ) )
      {
         default:
            plast++;
            ln++;
            break;

         case EOF:
            bug( "%s: EOF", __FUNCTION__ );
            if( fBootDb )
               exit( 1 );
            *plast = '\0';
            return flagstring;
            break;

         case '\n':
            plast++;
            ln++;
            *plast++ = '\r';
            ln++;
            break;

         case '\r':
            break;

         case '~':
            *plast = '\0';
            return flagstring;
      }
   }
}

/* Read a string from file fp */
char *fread_string( FILE *fp )
{
   char buf[MSL];

   mudstrlcpy( buf, fread_flagstring( fp ), MSL );
   if( !buf || buf[0] == '\0' )
      return NULL;
   return STRALLOC( buf );
}

/* Read to end of line (for comments). */
void fread_to_eol( FILE *fp )
{
   char c;

   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         return;
      }
      c = getc( fp );
   }
   while( c != '\r' && c != '\n' );

   do
   {
      c = getc( fp );
   }
   while( c == '\r' || c == '\n' );

   ungetc( c, fp );
   return;
}

/* Read to end of line into static buffer - Thoric */
char *fread_line( FILE *fp )
{
   static char line[MSL];
   char *pline;
   char c;
   int ln;

   pline = line;
   line[0] = '\0';
   ln = 0;

   /* Skip blanks. Read first char. */
   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         mudstrlcpy( line, "", sizeof( line ) );
         return line;
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   ungetc( c, fp );
   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         *pline = '\0';
         return line;
      }
      c = getc( fp );
      *pline++ = c;
      ln++;
      if( ln >= ( MSL - 1 ) )
      {
         bug( "%s: line too long", __FUNCTION__ );
         break;
      }
   }
   while( c != '\r' && c != '\n' );

   do
   {
      c = getc( fp );
   }
   while( c == '\r' || c == '\n' );

   ungetc( c, fp );
   *pline = '\0';
   return line;
}

/* Read one word (into static buffer). */
char *fread_word( FILE *fp )
{
   static char word[MIL];
   char *pword;
   char cEnd;

   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         word[0] = '\0';
         return word;
      }
      cEnd = getc( fp );
   }
   while( isspace( cEnd ) );

   if( cEnd == '\'' || cEnd == '"' )
   {
      pword = word;
   }
   else
   {
      word[0] = cEnd;
      pword = word + 1;
      cEnd = ' ';
   }

   for( ; pword < word + MIL; pword++ )
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         if( fBootDb )
            exit( 1 );
         *pword = '\0';
         return word;
      }
      *pword = getc( fp );
      if( cEnd == ' ' ? isspace( *pword ) : *pword == cEnd )
      {
         if( cEnd == ' ' )
            ungetc( *pword, fp );
         *pword = '\0';
         return word;
      }
   }
   bug( "%s: word too long", __FUNCTION__ );
   return NULL;
}

CMDF( do_memory )
{
   char arg[MIL];
#ifdef HASHSTR
   int hash;
#endif

   set_char_color( AT_PLAIN, ch );
   argument = one_argument( argument, arg );
   send_to_char( "\r\n&wSystem Memory [arguments - hash, check, showhigh]\r\n", ch );
   ch_printf( ch, "&wObjAffects: &W%5d  &wAreas:   &W%5d\r\n", top_affect, top_area );
   ch_printf( ch, "&wExtDes:     &W%5d  &wExits:   &W%5d\r\n", top_ed, top_exit );
   ch_printf( ch, "&wHelps:      &W%5d  &wResets:  &W%5d\r\n", top_help, top_reset );
   ch_printf( ch, "&wIdxMobs:    &W%5d  &wMobiles: &W%5d\r\n", top_mob_index, nummobsloaded );
   ch_printf( ch, "&wIdxObjs:    &W%5d  &wObjs:    &W%5d(%d)\r\n", top_obj_index, numobjsloaded, physicalobjects );
   ch_printf( ch, "&wRooms:      &W%5d  &wNews:    &W%5d\r\n", top_room, top_news );
   ch_printf( ch, "&wBTI:        &W%5d  &wCurCq's  &W%5d\r\n", top_bti, cur_qchars );
   ch_printf( ch, "&wShops:      &W%5d  &wRepShps: &W%5d\r\n", top_shop, top_repair );
   ch_printf( ch, "&wPlayers:    &W%5d  &wTopSn:   &W%5d(%d)\r\n", num_descriptors, top_sn, MAX_SKILL );
   ch_printf( ch, "&wMaxplrs:    &W%5d  &wRaces:   &W%5d(%d)\r\n", sysdata.maxplayers, MAX_PC_RACE, MAX_RACE );
   ch_printf( ch, "&wMaxEver:    &W%5d  &wClasses: &W%5d(%d)\r\n", sysdata.alltimemax, MAX_PC_CLASS, MAX_CLASS );
   ch_printf( ch, "&wMaxEver was recorded &W%s\r\n", distime( sysdata.time_of_max ) );

   if( !arg || arg[0] == '\0' )
      return;

#ifndef HASHSTR
   send_to_char( "Hash strings not enabled.\r\n", ch );
#else
   if( !str_cmp( arg, "check" ) )
   {
      if( !argument || argument[0] == '\0' )
      {
         send_to_char( "Usage: memory check <string to check for>\r\n", ch );
         return;
      }
      send_to_char( check_hash( argument ), ch );
      return;
   }
   if( !str_cmp( arg, "showhigh" ) )
   {
      show_high_hash( atoi( argument ) );
      send_to_char( "You will have to look in the current log file to see the hash info you wanted.\r\n", ch );
      return;
   }
   if( argument[0] != '\0' )
      hash = atoi( argument );
   else
      hash = -1;
   if( !str_cmp( arg, "hash" ) )
   {
      ch_printf( ch, "Hash statistics:\r\n%s", hash_stats( ) );
      if( hash != -1 )
         hash_dump( hash );
   }
#endif
}

/*
 * Generate a random number.
 * Ooops was (number_mm() % to) + from which doesn't work -Shaddai
 */
int number_range( int from, int to )
{
   if( ( to - from ) < 1 )
      return from;
   return ( ( number_mm( ) % ( to - from + 1 ) ) + from );
}

/*
 * Generate a percentile roll.
 * number_mm() % 100 only does 0-99, changed to do 1-100 -Shaddai
 */
int number_percent( void )
{
   return ( number_mm( ) % 100 ) + 1;
}

/* Generate a random door. */
int number_door( void )
{
   int door;

   while( ( door = number_mm( ) & ( 16 - 1 ) ) > 9 )
      ;

   return door;
}

int number_bits( int width )
{
   return number_mm( ) & ( ( 1 << width ) - 1 );
}

/*
 * I've gotten too many bad reports on OS-supplied random number generators.
 * This is the Mitchell-Moore algorithm from Knuth Volume II.
 * Best to leave the constants alone unless you've read Knuth.
 * -- Furey
 */
static int rgiState[2 + 55];

void init_mm( void )
{
   int *piState;
   int iState;

   piState = &rgiState[2];

   piState[-2] = 55 - 55;
   piState[-1] = 55 - 24;

   piState[0] = ( ( int )current_time ) & ( ( 1 << 30 ) - 1 );
   piState[1] = 1;
   for( iState = 2; iState < 55; iState++ )
   {
      piState[iState] = ( piState[iState - 1] + piState[iState - 2] ) & ( ( 1 << 30 ) - 1 );
   }
   return;
}

int number_mm( void )
{
   int *piState;
   int iState1;
   int iState2;
   int iRand;

   piState = &rgiState[2];
   iState1 = piState[-2];
   iState2 = piState[-1];
   iRand = ( piState[iState1] + piState[iState2] ) & ( ( 1 << 30 ) - 1 );
   piState[iState1] = iRand;
   if( ++iState1 == 55 )
      iState1 = 0;
   if( ++iState2 == 55 )
      iState2 = 0;
   piState[-2] = iState1;
   piState[-1] = iState2;
   return iRand >> 6;
}

/* Roll some dice. - Thoric */
int dice( int number, int size )
{
   int idice;
   int sum;

   switch( size )
   {
      case 0:
         return 0;
      case 1:
         return number;
   }

   for( idice = 0, sum = 0; idice < number; idice++ )
      sum += number_range( 1, size );

   return sum;
}

/* Simple linear interpolation. */
int interpolate( int level, int value_00, int value_32 )
{
   return value_00 + level * ( value_32 - value_00 ) / 32;
}

/*
 * Removes the tildes from a string.
 * Used for player-entered strings that go into disk files.
 */
void smash_tilde( char *str )
{
   for( ; *str != '\0'; str++ )
      if( *str == '~' )
         *str = '-';
   return;
}

/*
 * Encodes the tildes in a string.				-Thoric
 * Used for player-entered strings that go into disk files.
 */
void hide_tilde( char *str )
{
   for( ; *str != '\0'; str++ )
      if( *str == '~' )
         *str = HIDDEN_TILDE;

   return;
}

char *show_tilde( char *str )
{
   static char buf[MSL];
   char *bufptr;

   bufptr = buf;
   for( ; *str != '\0'; str++, bufptr++ )
   {
      if( *str == HIDDEN_TILDE )
         *bufptr = '~';
      else
         *bufptr = *str;
   }
   *bufptr = '\0';

   return buf;
}

/*
 * Compare strings, case insensitive.
 * Return true if different
 *   (compatibility with historical functions).
 */
bool str_cmp( const char *astr, const char *bstr )
{
   /* If both empty they match I think */
   if( !astr && !bstr )
      return false;

   /* If just one or the other is NULL they dont match */
   if( !astr || !bstr )
      return true;

   /* Check the strings to see if they don't match */
   for( ; *astr || *bstr; astr++, bstr++ )
   {
      if( LOWER( *astr ) != LOWER( *bstr ) )
         return true;
   }

   /* If you get here then they matched */
   return false;
}

/*
 * Compare strings, case insensitive, for prefix matching.
 * Return true if astr not a prefix of bstr
 *   (compatibility with historical functions).
 */
bool str_prefix( const char *astr, const char *bstr )
{
   /* If both empty they match I think */
   if( !astr && !bstr )
      return false;

   /* If just one or the other is NULL they dont match */
   if( !astr || !bstr )
      return true;

   for( ; *astr; astr++, bstr++ )
   {
      if( LOWER( *astr ) != LOWER( *bstr ) )
         return true;
   }

   /* If you get here then they matched */
   return false;
}

/*
 * Compare strings, case insensitive, for match anywhere.
 * Returns true is astr not part of bstr.
 *   (compatibility with historical functions).
 */
bool str_infix( const char *astr, const char *bstr )
{
   int sstr1, sstr2, ichar;
   char c0;

   if( ( c0 = LOWER( astr[0] ) ) == '\0' )
      return false;

   sstr1 = strlen( astr );
   sstr2 = strlen( bstr );

   for( ichar = 0; ichar <= sstr2 - sstr1; ichar++ )
      if( c0 == LOWER( bstr[ichar] ) && !str_prefix( astr, bstr + ichar ) )
         return false;

   return true;
}

/*
 * Compare strings, case insensitive, for suffix matching.
 * Return true if astr not a suffix of bstr
 *   (compatibility with historical functions).
 */
bool str_suffix( const char *astr, const char *bstr )
{
   int sstr1, sstr2;

   sstr1 = strlen( astr );
   sstr2 = strlen( bstr );
   if( sstr1 <= sstr2 && !str_cmp( astr, bstr + sstr2 - sstr1 ) )
      return false;
   else
      return true;
}

/*
 * Returns an initial-capped string.
 * Rewritten by FearItself@AvP
 */
char *capitalize( const char *str )
{ 
   static char buf[MAX_STRING_LENGTH];
   char *dest = buf;
   enum { Normal, Color } state = Normal;
   bool bFirst = true;
   char c;

   while( (c = *str++) )
   {
      if( state == Normal )
      {
         if( c == '&' || c == '^' || c == '}' )
         {
            state = Color;
         }
         else if( isalpha(c) )
         {
            c = bFirst ? toupper(c) : tolower(c);
            bFirst = false;
         }
      }
      else
      {
         state = Normal;
      }
      *dest++ = c;
   }
   *dest = c;

   return buf;
}

/* Returns a lowercase string. */
char *strlower( const char *str )
{
   static char strlow[MSL];
   int i;

   for( i = 0; str[i] != '\0'; i++ )
      strlow[i] = LOWER( str[i] );
   strlow[i] = '\0';
   return strlow;
}

/* Returns an uppercase string. */
char *strupper( const char *str )
{
   static char strup[MSL];
   int i;

   strup[0] = '\0';
   for( i = 0; str[i] != '\0'; i++ )
      strup[i] = UPPER( str[i] );
   strup[i] = '\0';
   return strup;
}

/* Returns true or false if a letter is a vowel - Thoric */
bool isavowel( char letter )
{
   char c;

   c = LOWER( letter );
   if( c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' )
      return true;
   else
      return false;
}

/* Shove either "a " or "an " onto the beginning of a string - Thoric */
char *aoran( const char *str )
{
   static char temp[MSL];

   if( !str )
   {
      bug( "%s: NULL str", __FUNCTION__ );
      return "";
   }

   if( isavowel( str[0] ) || ( strlen( str ) > 1 && LOWER( str[0] ) == 'y' && !isavowel( str[1] ) ) )
      mudstrlcpy( temp, "an ", sizeof( temp ) );
   else
      mudstrlcpy( temp, "a ", sizeof( temp ) );
   mudstrlcat( temp, str, sizeof( temp ) );
   return temp;
}

/* Append a string to a file. */
void append_file( CHAR_DATA *ch, char *file, char *str )
{
   FILE *fp;
   struct tm *t = localtime( &current_time );

   if( ( ch && is_npc( ch ) ) || str[0] == '\0' )
      return;

   if( !( fp = fopen( file, "a" ) ) )
   {
      perror( file );
      send_to_char( "Could not open the file!\r\n", ch );
   }
   else
   {
      fprintf( fp, "[%5d] [%2.2d:%2.2d:%2.2d %2.2d/%2.2d/%4.4d] %s: %s\n", ( ch && ch->in_room ) ? ch->in_room->vnum : 0,
         t->tm_hour, t->tm_min, t->tm_sec, ( t->tm_mon + 1 ), t->tm_mday, ( t->tm_year + 1900 ),
         ( ch && ch->name ) ? ch->name : "(Unknown)", str );
      fclose( fp );
      fp = NULL;
   }
}

/* Append a string to a file. */
void append_to_file( char *file, char *str )
{
   FILE *fp;

   if( !( fp = fopen( file, "a" ) ) )
      perror( file );
   else
   {
      fprintf( fp, "%s\n", str );
      fclose( fp );
      fp = NULL;
   }
}

/* Reports a bug. */
void bug( const char *str, ... )
{
   char buf[MSL];
   FILE *fp;
   int letter;
   struct stat fst;

   mudstrlcpy( buf, "[*****] BUG: ", sizeof( buf ) );
   {
      va_list param;

      va_start( param, str );
      vsnprintf( buf + strlen( buf ), ( sizeof( buf ) - strlen( buf ) ), str, param );
      va_end( param );
   }

   log_string_plus( buf, LOG_BUG, PERM_IMM );

   if( fpArea )
   {
      int iLine;
      int iChar;

      if( fpArea == stdin )
      {
         iLine = 0;
      }
      else
      {
         iChar = ftell( fpArea );
         fseek( fpArea, 0, 0 );
         for( iLine = 0; ftell( fpArea ) < iChar; iLine++ )
         {
            while( ( letter = getc( fpArea ) ) && letter != EOF && letter != '\n' )
               ;
         }
         fseek( fpArea, iChar, 0 );
      }

      log_printf_plus( LOG_BUG, PERM_IMM, "[*****] FILE: %s LINE: %d", strArea, iLine );

      if( stat( SHUTDOWN_FILE, &fst ) != -1 )   /* file exists */
      {
         if( ( fp = fopen( SHUTDOWN_FILE, "a" ) ) )
         {
            fprintf( fp, "[*****] <FILE: %s LINE: %d> %s\n", strArea, iLine, buf );
            fclose( fp );
            fp = NULL;
         }
      }
   }
}

/* Add a string to the boot-up log - Thoric */
void boot_log( const char *str, ... )
{
   char buf[MSL];
   FILE *fp;
   va_list param;

   mudstrlcpy( buf, "[*****] BOOT: ", sizeof( buf ) );
   va_start( param, str );
   vsnprintf( buf + strlen( buf ), ( sizeof( buf ) - strlen( buf ) ), str, param );
   va_end( param );
   log_string( buf );

   if( ( fp = fopen( BOOTLOG_FILE, "a" ) ) )
   {
      fprintf( fp, "%s\n", buf );
      fclose( fp );
      fp = NULL;
   }
}

/* Dump a text file to a player, a line at a time - Thoric */
void show_file( CHAR_DATA *ch, char *filename )
{
   FILE *fp;
   char buf[MSL];
   int c;
   int num = 0;

   if( ( fp = fopen( filename, "r" ) ) )
   {
      while( !feof( fp ) )
      {
         while( ( buf[num] = fgetc( fp ) ) != EOF
         && buf[num] != '\r' && buf[num] != '\n' && num < ( MSL - 2 ) )
            num++;
         c = fgetc( fp );
         if( ( c != '\r' && c != '\n' ) || c == buf[num] )
            ungetc( c, fp );
         buf[num++] = '\r';
         buf[num++] = '\n';
         buf[num] = '\0';
         send_to_pager( buf, ch );
         num = 0;
      }
      /* Thanks to stu <sprice@ihug.co.nz> from the mailing list in pointing This out. */
      fclose( fp );
      fp = NULL;
   }
}

/* Show the boot log file - Thoric */
CMDF( do_dmesg )
{
   set_pager_color( AT_LOG, ch );
   send_to_pager( "Boot Log Messages:\r\n", ch );
   show_file( ch, BOOTLOG_FILE );
}

/* wizlist builder! - Thoric */
void towizfile( const char *line )
{
   int filler, xx;
   char outline[MSL];
   FILE *fp;

   outline[0] = '\0';
   if( line && line[0] != '\0' )
   {
      filler = ( 78 - strlen( line ) );
      if( filler < 1 )
         filler = 1;
      filler /= 2;
      for( xx = 0; xx < filler; xx++ )
         mudstrlcat( outline, " ", sizeof( outline ) );
      mudstrlcat( outline, line, sizeof( outline ) );
   }
   mudstrlcat( outline, "\r\n", sizeof( outline ) );
   if( ( fp = fopen( WIZLIST_FILE, "a" ) ) )
   {
      fputs( outline, fp );
      fclose( fp );
      fp = NULL;
   }
}

typedef struct wizent WIZENT;
/* Structure used to build wizlist */
struct wizent
{
   WIZENT *next, *last;
   char *name;
   int level;
};
WIZENT *first_wiz, *last_wiz;

void add_to_wizlist( char *name, int level )
{
   WIZENT *wiz, *tmp;

   CREATE( wiz, WIZENT, 1 );
   wiz->name = STRALLOC( name );
   wiz->level = level;

   if( !first_wiz )
   {
      wiz->last = NULL;
      wiz->next = NULL;
      first_wiz = wiz;
      last_wiz = wiz;
      return;
   }

   /* insert sort, of sorts */
   for( tmp = first_wiz; tmp; tmp = tmp->next )
   {
      if( level > tmp->level )
      {
         if( !tmp->last )
            first_wiz = wiz;
         else
            tmp->last->next = wiz;
         wiz->last = tmp->last;
         wiz->next = tmp;
         tmp->last = wiz;
         return;
      }
   }

   wiz->last = last_wiz;
   wiz->next = NULL;
   last_wiz->next = wiz;
   last_wiz = wiz;
}

/* Wizlist builder - Thoric */
void make_wizlist( void )
{
   DIR *dp;
   struct dirent *dentry;
   FILE *gfp;
   const char *word;
   int ilevel;
   EXT_BV iflags;
   WIZENT *wiz, *wiznext;
   char buf[MSL];

   first_wiz = NULL;
   last_wiz = NULL;

   dp = opendir( GOD_DIR );

   ilevel = 0;
   dentry = readdir( dp );
   while( dentry )
   {
      if( dentry->d_name[0] != '.' )
      {
         snprintf( buf, sizeof( buf ), "%s%s", GOD_DIR, dentry->d_name );
         if( ( gfp = fopen( buf, "r" ) ) )
         {
            word = feof( gfp ) ? "End" : fread_word( gfp );
            ilevel = fread_number( gfp );
            fread_to_eol( gfp );
            word = feof( gfp ) ? "End" : fread_word( gfp );
            xCLEAR_BITS( iflags );
            if( !str_cmp( word, "Pcflags" ) )
            {
               char *infoflags = fread_flagstring( gfp );
               char flag[MSL];
               int value;

               while( infoflags && infoflags[0] != '\0' )
               {
                  infoflags = one_argument( infoflags, flag );
                  value = get_flag( flag, pc_flags, PCFLAG_MAX );
                  if( value >= 0 && value < PCFLAG_MAX )
                     xSET_BIT( iflags, value );
               }
            }
            fclose( gfp );
            gfp = NULL;
            if( xIS_SET( iflags, PCFLAG_RETIRED ) )
               ilevel = -1;
            if( xIS_SET( iflags, PCFLAG_GUEST ) )
               ilevel = -2;
            add_to_wizlist( dentry->d_name, ilevel );
         }
      }
      dentry = readdir( dp );
   }
   closedir( dp );

   remove_file( WIZLIST_FILE );
   snprintf( buf, sizeof( buf ), " Masters of the %s!", sysdata.mud_name );
   towizfile( buf );
   buf[0] = '\0';
   ilevel = PERM_MAX;
   for( wiz = first_wiz; wiz; wiz = wiz->next )
   {
      if( wiz->level < ilevel )
      {
         if( buf[0] )
         {
            towizfile( buf );
            buf[0] = '\0';
         }
         towizfile( "" );
         ilevel = wiz->level;
         switch( ilevel )
         {
            case PERM_IMP:
               towizfile( " Supreme Entity" );
               break;
            case PERM_HEAD:
               towizfile( " Head" );
               break;
            case PERM_LEADER:
               towizfile( " Leader" );
               break;
            case PERM_BUILDER:
               towizfile( " Builder" );
               break;
            case PERM_IMM:
               towizfile( " Immortal" );
               break;
            case PERM_ALL:
               towizfile( " Guest" );
               break;
            case -1:
               towizfile( " Retired" );
               break;
            case -2:
               towizfile( " Guest" );
               break;
            default:
               towizfile( " Servants" );
               break;
         }
      }
      if( strlen( buf ) + strlen( wiz->name ) > 76 )
      {
         towizfile( buf );
         buf[0] = '\0';
      }
      mudstrlcat( buf, " ", sizeof( buf ) );
      mudstrlcat( buf, wiz->name, sizeof( buf ) );
      if( strlen( buf ) > 70 )
      {
         towizfile( buf );
         buf[0] = '\0';
      }
   }

   if( buf[0] )
      towizfile( buf );

   for( wiz = first_wiz; wiz; wiz = wiznext )
   {
      wiznext = wiz->next;
      STRFREE( wiz->name );
      DISPOSE( wiz );
   }
   first_wiz = last_wiz = NULL;
}

CMDF( do_makewizlist )
{
   make_wizlist( );
   send_to_char( "Done.\r\n", ch );
}

/* mud prog functions */

/* This routine reads in scripts of MUDprograms from a file */
int mprog_name_to_type( char *name )
{
   if( !str_cmp( name, "in_file_prog" ) )
      return IN_FILE_PROG;
   if( !str_cmp( name, "act_prog" ) )
      return ACT_PROG;
   if( !str_cmp( name, "speech_prog" ) )
      return SPEECH_PROG;
   if( !str_cmp( name, "rand_prog" ) )
      return RAND_PROG;
   if( !str_cmp( name, "fight_prog" ) )
      return FIGHT_PROG;
   if( !str_cmp( name, "hitprcnt_prog" ) )
      return HITPRCNT_PROG;
   if( !str_cmp( name, "death_prog" ) )
      return DEATH_PROG;
   if( !str_cmp( name, "entry_prog" ) )
      return ENTRY_PROG;
   if( !str_cmp( name, "greet_prog" ) )
      return GREET_PROG;
   if( !str_cmp( name, "all_greet_prog" ) )
      return ALL_GREET_PROG;
   if( !str_cmp( name, "give_prog" ) )
      return GIVE_PROG;
   if( !str_cmp( name, "bribe_prog" ) )
      return BRIBE_PROG;
   if( !str_cmp( name, "time_prog" ) )
      return TIME_PROG;
   if( !str_cmp( name, "hour_prog" ) )
      return HOUR_PROG;
   if( !str_cmp( name, "wear_prog" ) )
      return WEAR_PROG;
   if( !str_cmp( name, "remove_prog" ) )
      return REMOVE_PROG;
   if( !str_cmp( name, "sac_prog" ) )
      return SAC_PROG;
   if( !str_cmp( name, "look_prog" ) )
      return LOOK_PROG;
   if( !str_cmp( name, "exa_prog" ) )
      return EXA_PROG;
   if( !str_cmp( name, "zap_prog" ) )
      return ZAP_PROG;
   if( !str_cmp( name, "open_prog" ) )
      return OPEN_PROG;
   if( !str_cmp( name, "close_prog" ) )
      return CLOSE_PROG;
   if( !str_cmp( name, "get_prog" ) )
      return GET_PROG;
   if( !str_cmp( name, "drop_prog" ) )
      return DROP_PROG;
   if( !str_cmp( name, "damage_prog" ) )
      return DAMAGE_PROG;
   if( !str_cmp( name, "scrap_prog" ) )
      return SCRAP_PROG;
   if( !str_cmp( name, "put_prog" ) )
      return PUT_PROG;
   if( !str_cmp( name, "repair_prog" ) )
      return REPAIR_PROG;
   if( !str_cmp( name, "greet_prog" ) )
      return GREET_PROG;
   if( !str_cmp( name, "pull_prog" ) )
      return PULL_PROG;
   if( !str_cmp( name, "push_prog" ) )
      return PUSH_PROG;
   if( !str_cmp( name, "sleep_prog" ) )
      return SLEEP_PROG;
   if( !str_cmp( name, "rest_prog" ) )
      return REST_PROG;
   if( !str_cmp( name, "leave_prog" ) )
      return LEAVE_PROG;
   if( !str_cmp( name, "script_prog" ) )
      return SCRIPT_PROG;
   if( !str_cmp( name, "use_prog" ) )
      return USE_PROG;
   return ( ERROR_PROG );
}

void mobprog_file_read( MOB_INDEX_DATA *mob, char *f )
{
   MPROG_DATA *mprg = NULL;
   char MUDProgfile[256];
   FILE *progfile;
   char letter;

   snprintf( MUDProgfile, sizeof( MUDProgfile ), "%s%s", PROG_DIR, f );

   if( !( progfile = fopen( MUDProgfile, "r" ) ) )
   {
      bug( "%s: couldn't open mudprog file", __FUNCTION__ );
      return;
   }

   for( ;; )
   {
      letter = fread_letter( progfile );

      if( letter == '|' )
         break;

      if( letter != '>' )
      {
         bug( "%s: MUDPROG char", __FUNCTION__ );
         break;
      }

      CREATE( mprg, MPROG_DATA, 1 );
      mprg->type = mprog_name_to_type( fread_word( progfile ) );
      switch( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: mudprog file type error", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         case IN_FILE_PROG:
            bug( "%s: Nested file programs aren't allowed.", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         default:
            mprg->arglist = fread_string( progfile );
            mprg->comlist = fread_string( progfile );
            mprg->fileprog = true;
            xSET_BIT( mob->progtypes, mprg->type );
            mprg->next = mob->mudprogs;
            mob->mudprogs = mprg;
            break;
      }
   }
   fclose( progfile );
   progfile = NULL;
}

/* This procedure is responsible for reading any in_file MUDprograms. */
void mprog_read_programs( FILE *fp, MOB_INDEX_DATA *mob )
{
   MPROG_DATA *mprg;
   char letter;
   char *word;

   for( ;; )
   {
      letter = fread_letter( fp );

      if( letter == '|' )
         return;

      if( letter != '>' )
      {
         bug( "%s: vnum %d MUDPROG char", __FUNCTION__, mob->vnum );
         exit( 1 );
      }
      CREATE( mprg, MPROG_DATA, 1 );
      mprg->next = mob->mudprogs;
      mob->mudprogs = mprg;

      word = fread_word( fp );
      mprg->type = mprog_name_to_type( word );

      switch( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: vnum %d MUDPROG type.", __FUNCTION__, mob->vnum );
            exit( 1 );

         case IN_FILE_PROG:
            mprg->arglist = fread_string( fp );
            mprg->fileprog = false;
            mobprog_file_read( mob, mprg->arglist );
            break;

         default:
            xSET_BIT( mob->progtypes, mprg->type );
            mprg->fileprog = false;
            mprg->arglist = fread_string( fp );
            mprg->comlist = fread_string( fp );
            break;
      }
   }
   return;
}

/*************************************************************/
/* obj prog functions */
/* This routine transfers between alpha and numeric forms of the
 *  mob_prog bitvector types. This allows the use of the words in the
 *  mob/script files.
 */

/* This routine reads in scripts of OBJprograms from a file */
void objprog_file_read( OBJ_INDEX_DATA *obj, char *f )
{
   MPROG_DATA *mprg = NULL;
   char MUDProgfile[256];
   FILE *progfile;
   char letter;

   snprintf( MUDProgfile, sizeof( MUDProgfile ), "%s%s", PROG_DIR, f );
   if( !( progfile = fopen( MUDProgfile, "r" ) ) )
   {
      bug( "%s: couldn't open mudprog file", __FUNCTION__ );
      return;
   }

   for( ;; )
   {
      letter = fread_letter( progfile );

      if( letter == '|' )
         break;

      if( letter != '>' )
      {
         bug( "%s: MUDPROG char", __FUNCTION__ );
         break;
      }

      CREATE( mprg, MPROG_DATA, 1 );
      mprg->type = mprog_name_to_type( fread_word( progfile ) );
      switch( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: mudprog file type error", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         case IN_FILE_PROG:
            bug( "%s: Nested file programs aren't allowed.", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         default:
            mprg->arglist = fread_string( progfile );
            mprg->comlist = fread_string( progfile );
            mprg->fileprog = true;
            xSET_BIT( obj->progtypes, mprg->type );
            mprg->next = obj->mudprogs;
            obj->mudprogs = mprg;
            break;
      }
   }
   fclose( progfile );
   progfile = NULL;
}

/* This procedure is responsible for reading any in_file OBJprograms. */
void oprog_read_programs( FILE *fp, OBJ_INDEX_DATA *obj )
{
   MPROG_DATA *mprg;
   char letter;
   char *word;

   for( ;; )
   {
      letter = fread_letter( fp );

      if( letter == '|' )
         return;

      if( letter != '>' )
      {
         bug( "%s: vnum %d MUDPROG char", __FUNCTION__, obj->vnum );
         exit( 1 );
      }
      CREATE( mprg, MPROG_DATA, 1 );
      mprg->next = obj->mudprogs;
      obj->mudprogs = mprg;

      word = fread_word( fp );
      mprg->type = mprog_name_to_type( word );

      switch( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: vnum %d MUDPROG type.", __FUNCTION__, obj->vnum );
            exit( 1 );

         case IN_FILE_PROG:
            mprg->arglist = fread_string( fp );
            mprg->fileprog = false;
            objprog_file_read( obj, mprg->arglist );
            break;

         default:
            xSET_BIT( obj->progtypes, mprg->type );
            mprg->fileprog = false;
            mprg->arglist = fread_string( fp );
            mprg->comlist = fread_string( fp );
            break;
      }
   }
   return;
}

/*************************************************************/
/* room prog functions */
/* This routine transfers between alpha and numeric forms of the
 *  mob_prog bitvector types. This allows the use of the words in the
 *  mob/script files.
 */

/* This routine reads in scripts of OBJprograms from a file */
void roomprog_file_read( ROOM_INDEX_DATA *room, char *f )
{
   MPROG_DATA *mprg = NULL;
   char MUDProgfile[256];
   FILE *progfile;
   char letter;

   snprintf( MUDProgfile, sizeof( MUDProgfile ), "%s%s", PROG_DIR, f );

   if( !( progfile = fopen( MUDProgfile, "r" ) ) )
   {
      bug( "%s: couldn't open mudprog file", __FUNCTION__ );
      return;
   }

   for( ;; )
   {
      letter = fread_letter( progfile );

      if( letter == '|' )
         break;

      if( letter != '>' )
      {
         bug( "%s: MUDPROG char", __FUNCTION__ );
         break;
      }

      CREATE( mprg, MPROG_DATA, 1 );
      mprg->type = mprog_name_to_type( fread_word( progfile ) );
      switch( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: mudprog file type error", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         case IN_FILE_PROG:
            bug( "%s: Nested file programs aren't allowed.", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         default:
            mprg->arglist = fread_string( progfile );
            mprg->comlist = fread_string( progfile );
            mprg->fileprog = true;
            xSET_BIT( room->progtypes, mprg->type );
            mprg->next = room->mudprogs;
            room->mudprogs = mprg;
            break;
      }
   }
   fclose( progfile );
   progfile = NULL;
}

/* This procedure is responsible for reading any in_file ROOMprograms. */
void rprog_read_programs( FILE *fp, ROOM_INDEX_DATA *room )
{
   MPROG_DATA *mprg;
   char letter;
   char *word;

   for( ;; )
   {
      letter = fread_letter( fp );

      if( letter == '|' )
         return;

      if( letter != '>' )
      {
         bug( "%s: vnum %d MUDPROG char", __FUNCTION__, room->vnum );
         exit( 1 );
      }
      CREATE( mprg, MPROG_DATA, 1 );
      mprg->next = room->mudprogs;
      room->mudprogs = mprg;

      word = fread_word( fp );
      mprg->type = mprog_name_to_type( word );

      switch( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: vnum %d MUDPROG type.", __FUNCTION__, room->vnum );
            exit( 1 );

         case IN_FILE_PROG:
            mprg->arglist = fread_string( fp );
            mprg->fileprog = false;
            roomprog_file_read( room, mprg->arglist );
            break;

         default:
            xSET_BIT( room->progtypes, mprg->type );
            mprg->fileprog = false;
            mprg->arglist = fread_string( fp );
            mprg->comlist = fread_string( fp );
            break;
      }
   }
}

/*************************************************************/
/*
 * Function to delete a room index.
 * Called from do_rdelete in build.c - Narn, May/96
 * Don't ask me why they return bool.. :).. oh well.. -- Alty
 * Don't ask me either, so I changed it to void. - Samson
 */
void delete_room( ROOM_INDEX_DATA *room )
{
   int hash;
   ROOM_INDEX_DATA *prev, *limbo = get_room_index( sysdata.room_limbo );
   OBJ_DATA *o;
   CHAR_DATA *ch;
   EXTRA_DESCR_DATA *ed;
   EXIT_DATA *ex;
   MPROG_ACT_LIST *mpact;
   MPROG_DATA *mp;

   UNLINK( room, room->area->first_room, room->area->last_room, next_aroom, prev_aroom );

   while( ( ch = room->first_person ) )
   {
      if( !is_npc( ch ) )
      {
         char_from_room( ch );
         char_to_room( ch, limbo );
      }
      else
         extract_char( ch, true );
   }

   for( ch = first_char; ch; ch = ch->next )
   {
      if( ch->was_in_room == room )
         ch->was_in_room = ch->in_room;
      if( ch->substate == SUB_ROOM_DESC && ch->dest_buf == room )
      {
         stop_editing( ch );
         ch->substate = SUB_NONE;
         ch->dest_buf = NULL;
         send_to_char( "The room is no more.\n\r", ch );
      }
      else if( ch->substate == SUB_ROOM_EXTRA && ch->dest_buf )
      {
         for( ed = room->first_extradesc; ed; ed = ed->next )
         {
            if( ed == ch->dest_buf )
            {
               stop_editing( ch );
               ch->substate = SUB_NONE;
               ch->dest_buf = NULL;
               send_to_char( "The room is no more.\n\r", ch );
               break;
            }
         }
      }
   }

   while( ( o = room->first_content ) )
      extract_obj( o );

   wipe_resets( room );

   while( ( ed = room->first_extradesc ) )
   {
      room->first_extradesc = ed->next;
      STRFREE( ed->keyword );
      STRFREE( ed->description );
      DISPOSE( ed );
      --top_ed;
   }
   while( ( ex = room->first_exit ) )
      extract_exit( room, ex );

   while( ( mpact = room->mpact ) )
   {
      room->mpact = mpact->next;
      STRFREE( mpact->buf );
      DISPOSE( mpact );
   }
   while( ( mp = room->mudprogs ) )
   {
      room->mudprogs = mp->next;
      STRFREE( mp->arglist );
      STRFREE( mp->comlist );
      DISPOSE( mp );
   }
   STRFREE( room->name );
   STRFREE( room->description );

   hash = room->vnum % MAX_KEY_HASH;
   if( room == room_index_hash[hash] )
      room_index_hash[hash] = room->next;
   else
   {
      for( prev = room_index_hash[hash]; prev; prev = prev->next )
         if( prev->next == room )
            break;
      if( prev )
         prev->next = room->next;
      else
         bug( "delete_room: room %d not in hash bucket %d.", room->vnum, hash );
   }
   DISPOSE( room );
   --top_room;
}

/* See comment on delete_room. */
void delete_obj( OBJ_INDEX_DATA *obj )
{
   int hash;
   OBJ_INDEX_DATA *prev;
   OBJ_DATA *o, *o_next;
   CHAR_DATA *ch;
   EXTRA_DESCR_DATA *ed;
   AFFECT_DATA *af;
   MPROG_DATA *mp;

   /* Remove references to object index */
   for( o = first_object; o; o = o_next )
   {
      o_next = o->next;
      if( o->pIndexData == obj )
         extract_obj( o );
   }

   for( ch = first_char; ch; ch = ch->next )
   {
      if( ch->substate == SUB_OBJ_EXTRA && ch->dest_buf )
      {
         for( ed = obj->first_extradesc; ed; ed = ed->next )
         {
            if( ed == ch->dest_buf )
            {
               stop_editing( ch );
               ch->substate = SUB_NONE;
               send_to_char( "You suddenly forget which object you were editing!\n\r", ch );
               break;
            }
         }
      }
      else if( ch->substate == SUB_MPROG_EDIT && ch->dest_buf )
      {
         for( mp = obj->mudprogs; mp; mp = mp->next )
         {
            if( mp == ch->dest_buf )
            {
               stop_editing( ch );
               ch->dest_buf = NULL;
               ch->substate = SUB_NONE;
               send_to_char( "You suddenly forget which object you were working on.\n\r", ch );
               break;
            }
         }
      }
   }

   while( ( ed = obj->first_extradesc ) )
   {
      obj->first_extradesc = ed->next;
      STRFREE( ed->keyword );
      STRFREE( ed->description );
      DISPOSE( ed );
      --top_ed;
   }
   while( ( af = obj->first_affect ) )
   {
      obj->first_affect = af->next;
      DISPOSE( af );
      --top_affect;
   }
   while( ( mp = obj->mudprogs ) )
   {
      obj->mudprogs = mp->next;
      STRFREE( mp->arglist );
      STRFREE( mp->comlist );
      DISPOSE( mp );
   }
   STRFREE( obj->name );
   STRFREE( obj->short_descr );
   STRFREE( obj->description );
   STRFREE( obj->action_desc );

   hash = obj->vnum % MAX_KEY_HASH;
   if( obj == obj_index_hash[hash] )
      obj_index_hash[hash] = obj->next;
   else
   {
      for( prev = obj_index_hash[hash]; prev; prev = prev->next )
         if( prev->next == obj )
            break;
      if( prev )
         prev->next = obj->next;
      else
         bug( "delete_obj: object %d not in hash bucket %d.", obj->vnum, hash );
   }
   DISPOSE( obj );
   --top_obj_index;
}

/* See comment on delete_room. */
void delete_mob( MOB_INDEX_DATA *mob )
{
   int hash;
   MOB_INDEX_DATA *prev;
   CHAR_DATA *ch, *ch_next;
   MPROG_DATA *mp;

   for( ch = first_char; ch; ch = ch_next )
   {
      ch_next = ch->next;
      if( ch->pIndexData == mob )
         extract_char( ch, true );
      else if( ch->substate == SUB_MPROG_EDIT && ch->dest_buf )
      {
         for( mp = mob->mudprogs; mp; mp = mp->next )
         {
            if( mp == ch->dest_buf )
            {
               send_to_char( "Your victim has departed.\n\r", ch );
               stop_editing( ch );
               ch->dest_buf = NULL;
               ch->substate = SUB_NONE;
               break;
            }
         }
      }
   }

   while( ( mp = mob->mudprogs ) )
   {
      mob->mudprogs = mp->next;
      STRFREE( mp->arglist );
      STRFREE( mp->comlist );
      DISPOSE( mp );
   }

   if( mob->pShop )
   {
      UNLINK( mob->pShop, first_shop, last_shop, next, prev );
      DISPOSE( mob->pShop );
      --top_shop;
   }

   if( mob->rShop )
   {
      UNLINK( mob->rShop, first_repair, last_repair, next, prev );
      DISPOSE( mob->rShop );
      --top_repair;
   }

   STRFREE( mob->name );
   STRFREE( mob->short_descr );
   STRFREE( mob->long_descr );
   STRFREE( mob->description );
   STRFREE( mob->spec_funname );

   hash = mob->vnum % MAX_KEY_HASH;
   if( mob == mob_index_hash[hash] )
      mob_index_hash[hash] = mob->next;
   else
   {
      for( prev = mob_index_hash[hash]; prev; prev = prev->next )
         if( prev->next == mob )
            break;
      if( prev )
         prev->next = mob->next;
      else
         bug( "%s: mobile %d not in hash bucket %d.", __FUNCTION__, mob->vnum, hash );
   }
   DISPOSE( mob );
   --top_mob_index;
}

/* Creat a new room (for online building) - Thoric */
ROOM_INDEX_DATA *make_room( int vnum, AREA_DATA *area )
{
   ROOM_INDEX_DATA *pRoomIndex;
   int iHash;

   CREATE( pRoomIndex, ROOM_INDEX_DATA, 1 );
   if( !pRoomIndex )
   {
      bug( "%s: failed to CREATE pRoomIndex.", __FUNCTION__ );
      return NULL;
   }
   pRoomIndex->first_person = NULL;
   pRoomIndex->last_person = NULL;
   pRoomIndex->first_content = NULL;
   pRoomIndex->last_content = NULL;
   pRoomIndex->first_reset = pRoomIndex->last_reset = NULL;
   pRoomIndex->first_extradesc = NULL;
   pRoomIndex->last_extradesc = NULL;
   pRoomIndex->area = area;
   pRoomIndex->vnum = vnum;
   pRoomIndex->name = STRALLOC( "Floating in a void" );
   pRoomIndex->description = NULL;
   xCLEAR_BITS( pRoomIndex->room_flags );
   pRoomIndex->sector_type = SECT_INSIDE;
   pRoomIndex->light = 0;
   pRoomIndex->first_exit = NULL;
   pRoomIndex->last_exit = NULL;
   LINK( pRoomIndex, area->first_room, area->last_room, next_aroom, prev_aroom );

   iHash = vnum % MAX_KEY_HASH;
   pRoomIndex->next = room_index_hash[iHash];
   room_index_hash[iHash] = pRoomIndex;
   ++top_room;

   return pRoomIndex;
}

/*
 * Create a new INDEX object (for online building) - Thoric
 * Option to clone an existing index object.
 */
OBJ_INDEX_DATA *make_object( int vnum, int cvnum, char *name )
{
   OBJ_INDEX_DATA *pObjIndex = NULL, *cObjIndex = NULL;
   char buf[MSL];
   int iHash;

   if( cvnum > 0 )
      cObjIndex = get_obj_index( cvnum );
   CREATE( pObjIndex, OBJ_INDEX_DATA, 1 );
   if( !pObjIndex )
   {
      bug( "%s: failed to CREATE pObjIndex.", __FUNCTION__ );
      return NULL;
   }
   pObjIndex->vnum = vnum;
   pObjIndex->name = STRALLOC( name );
   pObjIndex->first_affect = NULL;
   pObjIndex->last_affect = NULL;
   pObjIndex->first_extradesc = NULL;
   pObjIndex->last_extradesc = NULL;
   if( !cObjIndex )
   {
      snprintf( buf, sizeof( buf ), "A newly created %s", name );
      pObjIndex->short_descr = STRALLOC( buf );
      snprintf( buf, sizeof( buf ), "Some god dropped a newly created %s here.", name );
      pObjIndex->description = STRALLOC( buf );
      pObjIndex->action_desc = NULL;
      pObjIndex->short_descr[0] = LOWER( pObjIndex->short_descr[0] );
      pObjIndex->description[0] = UPPER( pObjIndex->description[0] );
      pObjIndex->item_type = ITEM_TRASH;
      xCLEAR_BITS( pObjIndex->extra_flags );
      xSET_BIT( pObjIndex->extra_flags, ITEM_PROTOTYPE );
      xCLEAR_BITS( pObjIndex->wear_flags );
      pObjIndex->value[0] = -1;
      pObjIndex->value[1] = -1;
      pObjIndex->value[2] = -1;
      pObjIndex->value[3] = -1;
      pObjIndex->value[4] = -1;
      pObjIndex->value[5] = -1;
      pObjIndex->weight = 1;
      pObjIndex->cost = 0;
   }
   else
   {
      EXTRA_DESCR_DATA *ed, *ced;
      AFFECT_DATA *paf, *cpaf;

      pObjIndex->short_descr = QUICKLINK( cObjIndex->short_descr );
      pObjIndex->description = QUICKLINK( cObjIndex->description );
      pObjIndex->action_desc = QUICKLINK( cObjIndex->action_desc );
      pObjIndex->item_type = cObjIndex->item_type;
      pObjIndex->extra_flags = cObjIndex->extra_flags;
      xSET_BIT( pObjIndex->extra_flags, ITEM_PROTOTYPE );
      pObjIndex->wear_flags = cObjIndex->wear_flags;
      pObjIndex->value[0] = cObjIndex->value[0];
      pObjIndex->value[1] = cObjIndex->value[1];
      pObjIndex->value[2] = cObjIndex->value[2];
      pObjIndex->value[3] = cObjIndex->value[3];
      pObjIndex->value[4] = cObjIndex->value[4];
      pObjIndex->value[5] = cObjIndex->value[5];
      pObjIndex->weight = cObjIndex->weight;
      pObjIndex->cost = cObjIndex->cost;
      for( ced = cObjIndex->first_extradesc; ced; ced = ced->next )
      {
         CREATE( ed, EXTRA_DESCR_DATA, 1 );
         ed->keyword = QUICKLINK( ced->keyword );
         ed->description = QUICKLINK( ced->description );
         LINK( ed, pObjIndex->first_extradesc, pObjIndex->last_extradesc, next, prev );
         top_ed++;
      }
      for( cpaf = cObjIndex->first_affect; cpaf; cpaf = cpaf->next )
      {
         CREATE( paf, AFFECT_DATA, 1 );
         paf->type = cpaf->type;
         paf->duration = cpaf->duration;
         paf->location = cpaf->location;
         paf->modifier = cpaf->modifier;
         paf->bitvector = cpaf->bitvector;
         LINK( paf, pObjIndex->first_affect, pObjIndex->last_affect, next, prev );
         top_affect++;
      }
   }
   pObjIndex->count = 0;
   iHash = vnum % MAX_KEY_HASH;
   pObjIndex->next = obj_index_hash[iHash];
   obj_index_hash[iHash] = pObjIndex;
   top_obj_index++;

   return pObjIndex;
}

/*
 * Create a new INDEX mobile (for online building) - Thoric
 * Option to clone an existing index mobile.
 */
MOB_INDEX_DATA *make_mobile( int vnum, int cvnum, char *name )
{
   MOB_INDEX_DATA *pMobIndex = NULL, *cMobIndex = NULL;
   char buf[MSL];
   int iHash, stat;

   if( cvnum > 0 )
      cMobIndex = get_mob_index( cvnum );
   CREATE( pMobIndex, MOB_INDEX_DATA, 1 );
   if( !pMobIndex )
   {
      bug( "%s: failed to CREATE pMobIndex.", __FUNCTION__ );
      return NULL;
   }
   pMobIndex->vnum = vnum;
   pMobIndex->count = 0;
   pMobIndex->killed = 0;
   pMobIndex->name = STRALLOC( name );
   if( !cMobIndex )
   {
      snprintf( buf, sizeof( buf ), "A newly created %s", name );
      pMobIndex->short_descr = STRALLOC( buf );
      snprintf( buf, sizeof( buf ), "Some god abandoned a newly created %s here.\r\n", name );
      pMobIndex->long_descr = STRALLOC( buf );
      pMobIndex->description = NULL;
      xCLEAR_BITS( pMobIndex->act );
      xSET_BIT( pMobIndex->act, ACT_IS_NPC );
      xSET_BIT( pMobIndex->act, ACT_PROTOTYPE );
      xCLEAR_BITS( pMobIndex->affected_by );
      pMobIndex->pShop = NULL;
      pMobIndex->rShop = NULL;
      pMobIndex->spec_fun = NULL;
      pMobIndex->mudprogs = NULL;
      xCLEAR_BITS( pMobIndex->progtypes );
      pMobIndex->alignment = 0;
      pMobIndex->level = 1;
      pMobIndex->ac = 0;
      pMobIndex->minhit = 0;
      pMobIndex->maxhit = 0;
      pMobIndex->gold = 0;
      pMobIndex->mgold = 0;
      pMobIndex->position = POS_STANDING;
      pMobIndex->defposition = POS_STANDING;
      pMobIndex->sex = 0;
      for( stat = 0; stat < STAT_MAX; stat++ )
         pMobIndex->perm_stats[stat] = 13;
      pMobIndex->numattacks = 0;
      xCLEAR_BITS( pMobIndex->xflags );
      xCLEAR_BITS( pMobIndex->resistant );
      xCLEAR_BITS( pMobIndex->immune );
      xCLEAR_BITS( pMobIndex->susceptible );
      xCLEAR_BITS( pMobIndex->attacks );
      xCLEAR_BITS( pMobIndex->defenses );
   }
   else
   {
      pMobIndex->short_descr = QUICKLINK( cMobIndex->short_descr );
      pMobIndex->long_descr = QUICKLINK( cMobIndex->long_descr );
      pMobIndex->description = QUICKLINK( cMobIndex->description );
      pMobIndex->act = cMobIndex->act;
      xSET_BIT( pMobIndex->act, ACT_PROTOTYPE );
      pMobIndex->affected_by = cMobIndex->affected_by;
      pMobIndex->pShop = NULL;
      pMobIndex->rShop = NULL;
      pMobIndex->spec_fun = cMobIndex->spec_fun;
      pMobIndex->mudprogs = NULL;
      xCLEAR_BITS( pMobIndex->progtypes );
      pMobIndex->alignment = cMobIndex->alignment;
      pMobIndex->level = cMobIndex->level;
      pMobIndex->ac = cMobIndex->ac;
      pMobIndex->minhit = cMobIndex->minhit;
      pMobIndex->maxhit = cMobIndex->maxhit;
      pMobIndex->gold = cMobIndex->gold;
      pMobIndex->mgold = cMobIndex->mgold;
      pMobIndex->position = cMobIndex->position;
      pMobIndex->defposition = cMobIndex->defposition;
      pMobIndex->sex = cMobIndex->sex;
      for( stat = 0; stat < STAT_MAX; stat++ )
         pMobIndex->perm_stats[stat] = cMobIndex->perm_stats[stat];
      pMobIndex->xflags = cMobIndex->xflags;
      pMobIndex->resistant = cMobIndex->resistant;
      pMobIndex->immune = cMobIndex->immune;
      pMobIndex->susceptible = cMobIndex->susceptible;
      pMobIndex->numattacks = cMobIndex->numattacks;
      pMobIndex->attacks = cMobIndex->attacks;
      pMobIndex->defenses = cMobIndex->defenses;
   }
   iHash = vnum % MAX_KEY_HASH;
   pMobIndex->next = mob_index_hash[iHash];
   mob_index_hash[iHash] = pMobIndex;
   top_mob_index++;

   return pMobIndex;
}

/*
 * Creates a simple exit with no fields filled but rvnum and optionally to_room and vnum. - Thoric
 * Exits are inserted into the linked list based on vdir.
 */
EXIT_DATA *make_exit( ROOM_INDEX_DATA *pRoomIndex, ROOM_INDEX_DATA *to_room, short door )
{
   EXIT_DATA *pexit, *texit;
   bool broke;

   CREATE( pexit, EXIT_DATA, 1 );
   pexit->vdir = door;
   pexit->rvnum = pRoomIndex->vnum;
   pexit->to_room = to_room;
   if( to_room )
   {
      pexit->vnum = to_room->vnum;
      texit = get_exit_to( to_room, rev_dir[door], pRoomIndex->vnum );
      if( texit ) /* assign reverse exit pointers */
      {
         texit->rexit = pexit;
         pexit->rexit = texit;
      }
   }
   broke = false;
   for( texit = pRoomIndex->first_exit; texit; texit = texit->next )
      if( door < texit->vdir )
      {
         broke = true;
         break;
      }
   if( !pRoomIndex->first_exit )
      pRoomIndex->first_exit = pexit;
   else
   {
      /* keep exits in incremental order - insert exit into list */
      if( broke && texit )
      {
         if( !texit->prev )
            pRoomIndex->first_exit = pexit;
         else
            texit->prev->next = pexit;
         pexit->prev = texit->prev;
         pexit->next = texit;
         texit->prev = pexit;
         top_exit++;
         return pexit;
      }
      pRoomIndex->last_exit->next = pexit;
   }
   pexit->next = NULL;
   pexit->prev = pRoomIndex->last_exit;
   pRoomIndex->last_exit = pexit;
   top_exit++;
   return pexit;
}

void load_area_file( AREA_DATA *tarea, char *filename )
{
   char newfilename[MSL];

   if( fBootDb )
      tarea = last_area;

   if( !fBootDb && !tarea )
   {
      bug( "%s: null area!", __FUNCTION__ );
      return;
   }

   if( tarea && xIS_SET( tarea->flags, AFLAG_PROTOTYPE ) )
      snprintf( newfilename, sizeof( newfilename ), "%s%s", BUILD_DIR, filename );
   else
      snprintf( newfilename, sizeof( newfilename ), "%s%s", AREA_DIR, filename );

   if( !( fpArea = fopen( newfilename, "r" ) ) )
   {
      perror( newfilename );
      bug( "%s: error loading file (%s) for reading", __FUNCTION__, newfilename );
      return;
   }

   area_version = 0;
   for( ;; )
   {
      char *word;

      if( fread_letter( fpArea ) != '#' )
      {
         bug( "%s: #not found", __FUNCTION__ );
         exit( 1 );
      }

      word = fread_word( fpArea );

      if( word[0] == '$' )
         break;
      else if( !str_cmp( word, "AREA" ) )
      {
         if( fBootDb )
         {
            load_area( fpArea );
            tarea = last_area;
         }
         else
         {
            STRFREE( tarea->name );
            tarea->name = fread_string( fpArea );
         }
      }
      else if( !str_cmp( word, "AUTHOR" ) )
         load_author( tarea, fpArea );
      else if( !str_cmp( word, "FLAGS" ) )
         load_flags( tarea, fpArea );
      else if( !str_cmp( word, "RANGES" ) )
         load_ranges( tarea, fpArea );
      else if( !str_cmp( word, "ECONOMY" ) )
      {
         fread_number( fpArea );
         fread_number( fpArea );
      }
      else if( !str_cmp( word, "RESETMSG" ) )
         load_resetmsg( tarea, fpArea );
      else if( !str_cmp( word, "RESETFREQ" ) )
      {
         tarea->reset_frequency = fread_number( fpArea );
         tarea->age = tarea->reset_frequency;
      }
      else if( !str_cmp( word, "MOBILES" ) )
         load_mobiles( tarea, fpArea );
      else if( !str_cmp( word, "OBJECTS" ) )
         load_objects( tarea, fpArea );
      else if( !str_cmp( word, "ROOMS" ) )
         load_rooms( tarea, fpArea );
      else if( !str_cmp( word, "CLIMATE" ) )
         load_climate( tarea, fpArea );
      else if( !str_cmp( word, "NEIGHBOR" ) )
         load_neighbor( tarea, fpArea );
      else if( !str_cmp( word, "VERSION" ) )
         load_version( tarea, fpArea );
      else if( !str_cmp( word, "SPELLLIMIT" ) )
         fread_number( fpArea );
      else
      {
         bug( "%s: bad section name: %s", __FUNCTION__, word );
         if( fBootDb )
            exit( 1 );
         else
         {
            fclose( fpArea );
            fpArea = NULL;
            return;
         }
      }
   }
   fclose( fpArea );
   fpArea = NULL;
   if( tarea )
   {
      if( fBootDb )
      {
         sort_area_by_name( tarea ); /* 4/27/97 */
         sort_area( tarea, false );
      }
      log_printf( "%s: %-14s: Vnums: %5d - %-5d", __FUNCTION__, tarea->filename, tarea->low_vnum, tarea->hi_vnum );
      SET_BIT( tarea->status, AREA_LOADED );
   }
   else
      log_printf( "%s: (%s)", __FUNCTION__, filename );
}

/*
 * Build list of in_progress areas.  Do not load areas.
 * define AREA_READ if you want it to build area names rather than reading
 * them out of the area files. -- Altrag
 */
void load_buildlist( void )
{
   DIR *dp;
   struct dirent *dentry;
   AREA_DATA *pArea;
   FILE *fp;
   char temp, buf[MSL], line[81], word[81];
   int low, hi, mlow, mhi, olow, ohi, rlow, rhi;
   bool badfile = false;

   dp = opendir( GOD_DIR );
   dentry = readdir( dp );
   while( dentry )
   {
      if( dentry->d_name[0] != '.' )
      {
         snprintf( buf, sizeof( buf ), "%s%s", GOD_DIR, dentry->d_name );
         if( !( fp = fopen( buf, "r" ) ) )
         {
            bug( "%s: invalid file %s", __FUNCTION__, buf );
            perror( buf );
            dentry = readdir( dp );
            continue;
         }
         log_string( buf );
         badfile = false;
         rlow = rhi = olow = ohi = mlow = mhi = 0;
         while( !feof( fp ) && !ferror( fp ) )
         {
            low = 0;
            hi = 0;
            word[0] = 0;
            line[0] = 0;
            if( ( temp = fgetc( fp ) ) != EOF )
               ungetc( temp, fp );
            else
               break;

            fgets( line, 80, fp );
            sscanf( line, "%s %d %d", word, &low, &hi );
            if( !strcmp( word, "Trust" ) )
            {
               if( low < PERM_IMM )
               {
                  snprintf( buf, sizeof( buf ), "%s: God file with level %d < %d", dentry->d_name, low, PERM_IMM );
                  badfile = true;
               }
            }
            if( !strcmp( word, "VnumRange" ) )
               rlow = low, rhi = hi;
         }
         fclose( fp );
         if( rlow && rhi && !badfile )
         {
            snprintf( buf, sizeof( buf ), "%s%s.are", BUILD_DIR, dentry->d_name );
            if( !( fp = fopen( buf, "r" ) ) )
            {
               bug( "%s: can't open building area file [%s] for read", __FUNCTION__, buf );
               perror( buf );
               dentry = readdir( dp );
               continue;
            }

            mudstrlcpy( strArea, buf, sizeof( strArea ) );

#if !defined(READ_AREA) /* Dont always want to read stuff.. dunno.. shrug */
            mudstrlcpy( word, fread_word( fp ), sizeof( word ) );
            if( word[0] != '#' || strcmp( &word[1], "AREA" ) )
            {
               snprintf( buf, sizeof( buf ), "%s: %s.are: no #AREA found.", __FUNCTION__, dentry->d_name );
               fclose( fp );
               dentry = readdir( dp );
               continue;
            }
#endif
            CREATE( pArea, AREA_DATA, 1 );
            snprintf( buf, sizeof( buf ), "%s.are", dentry->d_name );
            pArea->author = STRALLOC( dentry->d_name );
            pArea->filename = STRALLOC( buf );
#if !defined(READ_AREA)
            pArea->name = fread_string( fp );
#else
            snprintf( buf, sizeof( buf ), "{PROTO} %s's area in progress", dentry->d_name );
            pArea->name = STRALLOC( buf );
#endif
            fclose( fp );
            pArea->low_vnum = rlow;
            pArea->hi_vnum = rhi;
            pArea->low_soft_range = -1;
            pArea->hi_soft_range = -1;
            pArea->low_hard_range = -1;
            pArea->hi_hard_range = -1;

            CREATE( pArea->weather, WEATHER_DATA, 1 );   /* FB */
            pArea->weather->temp = 0;
            pArea->weather->precip = 0;
            pArea->weather->wind = 0;
            pArea->weather->temp_vector = 0;
            pArea->weather->precip_vector = 0;
            pArea->weather->wind_vector = 0;
            pArea->weather->climate_temp = 2;
            pArea->weather->climate_precip = 2;
            pArea->weather->climate_wind = 2;
            pArea->weather->first_neighbor = NULL;
            pArea->weather->last_neighbor = NULL;
            pArea->weather->echo = NULL;
            pArea->weather->echo_color = AT_GREY;
            pArea->first_room = pArea->last_room = NULL;
            xSET_BIT( pArea->flags, AFLAG_PROTOTYPE );
            LINK( pArea, first_build, last_build, next, prev );
            sort_area( pArea, true );
            top_area++;
            fBootDb = false;
            load_area_file( pArea, pArea->filename );
            fBootDb = true;
            mudstrlcpy( strArea, "$", sizeof( strArea ) );
            reset_area( pArea );
         }
      }
      dentry = readdir( dp );
   }
   closedir( dp );
}

/* Sort areas by name alphanumercially - 4/27/97, Fireblade */
void sort_area_by_name( AREA_DATA *pArea )
{
   AREA_DATA *temp_area;

   if( !pArea )
   {
      bug( "%s: NULL pArea", __FUNCTION__ );
      return;
   }
   for( temp_area = first_area_name; temp_area; temp_area = temp_area->next_sort_name )
   {
      if( strcmp( pArea->name, temp_area->name ) < 0 )
      {
         INSERT( pArea, temp_area, first_area_name, next_sort_name, prev_sort_name );
         break;
      }
   }
   if( !temp_area )
      LINK( pArea, first_area_name, last_area_name, next_sort_name, prev_sort_name );
}

/* Sort by room vnums - Altrag & Thoric */
void sort_area( AREA_DATA *pArea, bool proto )
{
   AREA_DATA *area = NULL;
   AREA_DATA *first_sort, *last_sort;
   bool found;

   if( !pArea )
   {
      bug( "%s: NULL pArea", __FUNCTION__ );
      return;
   }

   if( proto )
   {
      first_sort = first_bsort;
      last_sort = last_bsort;
   }
   else
   {
      first_sort = first_asort;
      last_sort = last_asort;
   }

   found = false;
   pArea->prev_sort = pArea->next_sort = NULL;

   if( !first_sort )
   {
      first_sort = last_sort = pArea;
      found = true;
   }
   else
   {
      for( area = first_sort; area; area = area->next_sort )
      {
         if( pArea->low_vnum < area->low_vnum )
         {
            if( !area->prev_sort )
               first_sort = pArea;
            else
               area->prev_sort->next_sort = pArea;
            pArea->prev_sort = area->prev_sort;
            pArea->next_sort = area;
            area->prev_sort = pArea;
            found = true;
            break;
         }
      }
   }

   if( !found )
   {
      pArea->prev_sort = last_sort;
      pArea->next_sort = NULL;
      last_sort->next_sort = pArea;
      last_sort = pArea;
   }

   if( proto )
   {
      first_bsort = first_sort;
      last_bsort = last_sort;
   }
   else
   {
      first_asort = first_sort;
      last_asort = last_sort;
   }
}

/*
 * Display vnums currently assigned to areas -Altrag & Thoric
 * Sorted, and flagged if loaded.
 */
void show_vnums( CHAR_DATA *ch, int low, int high, bool proto, bool shownl, char *loadst, char *notloadst )
{
   AREA_DATA *pArea, *first_sort;
   int count, loaded;

   count = 0;
   loaded = 0;
   set_pager_color( AT_PLAIN, ch );
   if( proto )
      first_sort = first_bsort;
   else
      first_sort = first_asort;
   for( pArea = first_sort; pArea; pArea = pArea->next_sort )
   {
      if( IS_SET( pArea->status, AREA_DELETED ) )
         continue;
      if( pArea->low_vnum < low )
         continue;
      if( pArea->hi_vnum > high )
         break;
      if( IS_SET( pArea->status, AREA_LOADED ) )
         loaded++;
      else if( !shownl )
         continue;
      pager_printf( ch, "%-15s| Vnums: %5d - %-5d%s\r\n",
         ( pArea->filename ? pArea->filename : "(invalid)" ),
           pArea->low_vnum, pArea->hi_vnum,
           IS_SET( pArea->status, AREA_LOADED ) ? loadst : notloadst );
      count++;
   }
   pager_printf( ch, "Areas listed: %d  Loaded: %d\r\n", count, loaded );
}

/* Shows prototype vnums ranges, and if loaded */
CMDF( do_vnums )
{
   char arg1[MIL], arg2[MIL];
   int low, high;

   argument = one_argument( argument, arg1 );
   argument = one_argument( argument, arg2 );
   low = 1;
   high = MAX_VNUM;
   if( arg1 && arg1[0] != '\0' )
   {
      low = atoi( arg1 );
      if( arg2 && arg2[0] != '\0' )
         high = atoi( arg2 );
   }
   show_vnums( ch, low, high, true, true, " *", "" );
}

/* Shows installed areas, sorted.  Mark unloaded areas with an X */
CMDF( do_zones )
{
   char arg1[MIL], arg2[MIL];
   int low, high;

   argument = one_argument( argument, arg1 );
   argument = one_argument( argument, arg2 );
   low = 1;
   high = MAX_VNUM;
   if( arg1 && arg1[0] != '\0' )
   {
      low = atoi( arg1 );
      if( arg2 && arg2[0] != '\0' )
         high = atoi( arg2 );
   }
   show_vnums( ch, low, high, false, true, "", " X" );
}

/* Check to make sure range of vnums is free - Scryn 2/27/96 */
CMDF( do_check_vnums )
{
   AREA_DATA *pArea;
   char arg1[MSL];
   bool area_conflict;
   int low_range, high_range;

   argument = one_argument( argument, arg1 );
   if( !arg1 || arg1[0] == '\0' )
   {
      send_to_char( "Please specify the low end of the range to be searched.\r\n", ch );
      return;
   }
   low_range = atoi( arg1 );

   argument = one_argument( argument, arg1 );
   if( !arg1 || arg1[0] == '\0' )
   {
      send_to_char( "Please specify the high end of the range to be searched.\r\n", ch );
      return;
   }
   high_range = atoi( arg1 );

   if( low_range < 1 || low_range > MAX_VNUM )
   {
      send_to_char( "Invalid argument for bottom of range.\r\n", ch );
      return;
   }

   if( high_range < 1 || high_range > MAX_VNUM )
   {
      send_to_char( "Invalid argument for top of range.\r\n", ch );
      return;
   }

   if( high_range < low_range )
   {
      send_to_char( "Bottom of range must be below top of range.\r\n", ch );
      return;
   }
   set_char_color( AT_PLAIN, ch );
   for( pArea = first_asort; pArea; pArea = pArea->next_sort )
   {
      area_conflict = false;
      if( IS_SET( pArea->status, AREA_DELETED ) )
         continue;
      if( low_range < pArea->low_vnum && pArea->low_vnum < high_range )
         area_conflict = true;
      if( low_range < pArea->hi_vnum && pArea->hi_vnum < high_range )
         area_conflict = true;
      if( ( low_range >= pArea->low_vnum ) && ( low_range <= pArea->hi_vnum ) )
         area_conflict = true;
      if( ( high_range <= pArea->hi_vnum ) && ( high_range >= pArea->low_vnum ) )
         area_conflict = true;
      if( area_conflict )
      {
         ch_printf( ch, "Conflict: %-15s| ", ( pArea->filename ? pArea->filename : "(invalid)" ) );
         ch_printf( ch, "Vnums: %5d - %-5d\r\n", pArea->low_vnum, pArea->hi_vnum );
      }
   }
   for( pArea = first_bsort; pArea; pArea = pArea->next_sort )
   {
      area_conflict = false;
      if( IS_SET( pArea->status, AREA_DELETED ) )
         continue;
      if( low_range < pArea->low_vnum && pArea->low_vnum < high_range )
         area_conflict = true;
      if( low_range < pArea->hi_vnum && pArea->hi_vnum < high_range )
         area_conflict = true;
      if( ( low_range >= pArea->low_vnum ) && ( low_range <= pArea->hi_vnum ) )
         area_conflict = true;
      if( ( high_range <= pArea->hi_vnum ) && ( high_range >= pArea->low_vnum ) )
         area_conflict = true;
      if( area_conflict )
      {
         ch_printf( ch, "Conflict: %-15s| ", ( pArea->filename ? pArea->filename : "(invalid)" ) );
         ch_printf( ch, "Vnums: %5d - %-5d\r\n", pArea->low_vnum, pArea->hi_vnum );
      }
   }
}

/*
 * This function is here to aid in debugging.
 * If the last expression in a function is another function call,
 *   gcc likes to generate a JMP instead of a CALL.
 * This is called "tail chaining."
 * It hoses the debugger call stack for that call.
 * So I make this the last call in certain critical functions,
 *   where I really need the call stack to be right for debugging!
 *
 * If you don't understand this, then LEAVE IT ALONE.
 * Don't remove any calls to tail_chain anywhere.
 *
 * -- Furey
 */
void tail_chain( void )
{
   return;
}

/*
 * Initialize the weather for all the areas
 * Last Modified: July 21, 1997
 * Fireblade
 */
void init_area_weather( void )
{
   AREA_DATA *pArea;
   NEIGHBOR_DATA *neigh, *next_neigh;

   for( pArea = first_area; pArea; pArea = pArea->next )
   {
      int cf;

      /* init temp and temp vector */
      cf = pArea->weather->climate_temp - 2;
      pArea->weather->temp = number_range( -weath_unit, weath_unit ) + cf * number_range( 0, weath_unit );
      pArea->weather->temp_vector = cf + number_range( -rand_factor, rand_factor );

      /* init precip and precip vector */
      cf = pArea->weather->climate_precip - 2;
      pArea->weather->precip = number_range( -weath_unit, weath_unit ) + cf * number_range( 0, weath_unit );
      pArea->weather->precip_vector = cf + number_range( -rand_factor, rand_factor );

      /* init wind and wind vector */
      cf = pArea->weather->climate_wind - 2;
      pArea->weather->wind = number_range( -weath_unit, weath_unit ) + cf * number_range( 0, weath_unit );
      pArea->weather->wind_vector = cf + number_range( -rand_factor, rand_factor );

      /* check connections between neighbors */
      for( neigh = pArea->weather->first_neighbor; neigh; neigh = next_neigh )
      {
         AREA_DATA *tarea;
         NEIGHBOR_DATA *tneigh;

         /* get the address if needed */
         if( !neigh->address )
            neigh->address = get_area( neigh->name );

         /* area does not exist */
         if( !neigh->address )
         {
            next_neigh = neigh->next;
            UNLINK( neigh, pArea->weather->first_neighbor, pArea->weather->last_neighbor, next, prev );
            STRFREE( neigh->name );
            DISPOSE( neigh );
            fold_area( pArea, pArea->filename, false );
            continue;
         }

         /* make sure neighbors both point to each other */
         tarea = neigh->address;
         for( tneigh = tarea->weather->first_neighbor; tneigh; tneigh = tneigh->next )
         {
            if( !strcmp( pArea->name, tneigh->name ) )
               break;
         }

         if( !tneigh )
         {
            CREATE( tneigh, NEIGHBOR_DATA, 1 );
            tneigh->name = STRALLOC( pArea->name );
            LINK( tneigh, tarea->weather->first_neighbor, tarea->weather->last_neighbor, next, prev );
            fold_area( tarea, tarea->filename, false );
         }

         tneigh->address = pArea;
         next_neigh = neigh->next;
      }
   }
}

/*
 * Load weather data from appropriate file in system dir
 * Last Modified: July 24, 1997 - Fireblade
 */
void load_weatherdata( void )
{
   FILE *fp;

   if( ( fp = fopen( WEATHER_FILE, "r" ) ) )
   {
      for( ;; )
      {
         char letter;
         char *word;

         letter = fread_letter( fp );

         if( letter != '#' )
         {
            bug( "%s: # not found", __FUNCTION__ );
            return;
         }

         word = fread_word( fp );

         if( !str_cmp( word, "RANDOM" ) )
            rand_factor = fread_number( fp );
         else if( !str_cmp( word, "CLIMATE" ) )
            climate_factor = fread_number( fp );
         else if( !str_cmp( word, "NEIGHBOR" ) )
            neigh_factor = fread_number( fp );
         else if( !str_cmp( word, "UNIT" ) )
            weath_unit = fread_number( fp );
         else if( !str_cmp( word, "MAXVECTOR" ) )
            max_vector = fread_number( fp );
         else if( !str_cmp( word, "END" ) )
         {
            fclose( fp );
            fp = NULL;
            break;
         }
         else
         {
            bug( "%s: unknown field", __FUNCTION__ );
            fclose( fp );
            fp = NULL;
            break;
         }
      }
   }
}

/* Write data for global weather parameters - Fireblade */
void save_weatherdata( void )
{
   FILE *fp;

   if( !( fp = fopen( WEATHER_FILE, "w" ) ) )
   {
      bug( "%s: could not open %s for writing", __FUNCTION__, WEATHER_FILE );
      return;
   }
   fprintf( fp, "#RANDOM    %d\n", rand_factor );
   fprintf( fp, "#CLIMATE   %d\n", climate_factor );
   fprintf( fp, "#NEIGHBOR  %d\n", neigh_factor );
   fprintf( fp, "#UNIT      %d\n", weath_unit );
   fprintf( fp, "#MAXVECTOR %d\n", max_vector );
   fprintf( fp, "#END\n" );
   fclose( fp );
   fp = NULL;
}

size_t newmudstrlcpy( char *dst, const char *src, size_t siz, char *filename, int line )
{
   register char *d = dst;
   register const char *s = src;
   register size_t n = siz;

   if( !src )
   {
      bug( "%s: %s[%d] passing NULL src string!", __FUNCTION__, filename, line );
      return 0;
   }

   if( !dst )
   {
      bug( "%s: %s[%d] passing NULL dst string!", __FUNCTION__, filename, line );
      return 0;
   }

   /* Copy as many bytes as will fit */
   if( n != 0 && --n != 0 )
   {
      do
      {
         if( ( *d++ = *s++ ) == 0 )
            break;
      }
      while( --n != 0 );
   }

   /* Not enough room in dst, add NULL and traverse rest of src */
   if( n == 0 )
   {
      if( siz != 0 )
         *d = '\0';  /* NUL-terminate dst */
      while( *s++ )
         ;
   }
   return ( s - src - 1 ); /* count does not include NUL */
}

size_t mudstrlcat( char *dst, const char *src, size_t siz )
{
   register char *d = dst;
   register const char *s = src;
   register size_t n = siz;
   size_t dlen;

   if( !src )
   {
      bug( "%s: NULL src string passed!", __FUNCTION__ );
      return 0;
   }

   /* Find the end of dst and adjust bytes left but don't go past end */
   while( n-- != 0 && *d != '\0' )
      d++;
   dlen = d - dst;
   n = siz - dlen;

   if( n == 0 )
      return ( dlen + strlen( s ) );
   while( *s && *s != '\0' )
   {
      if( n != 1 )
      {
         *d++ = *s;
         n--;
      }
      s++;
   }
   *d = '\0';
   return ( dlen + ( s - src ) );   /* count does not include NUL */
}