/*
 * Plists.c
 */

#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <memory.h>
#include <signal.h>
#include <setjmp.h>

#include "fix.h"
#include "config.h"
#include "player.h"
#include "compaction.c"


/* externs */

extern player  *find_player_absolute_quiet(char *);
extern char    *gstring(player *);
extern void     do_inform(player *, char *);
extern void     do_prompt(player *, char *);
extern void     quit(player *, char *);
extern void     finish_edit(player *);
extern int      restore_player_title(player *, char *, char *);
extern void     decompress_list(saved_player *);
#if !defined(linux)
extern char    *crypt(char *, char *);
#endif /* LINUX */
extern void     player_flags(player *), player_flags_verbose(player *, char *);
extern file     load_file();
extern char    *end_string(), *convert_time(), *retrieve_room_data(),
               *retrieve_list_data(), *bit_string(), *retrieve_mail_data();
extern room    *create_room(), *convert_room();
extern void     password_mode_on(), password_mode_off(), trans_to();
extern void     construct_room_save(), construct_list_save(), construct_mail_save();
extern void     decompress_room(room *);
extern void     log(char *, char *);
extern void     handle_error(char *);
extern void     free_room_data(saved_player *);
extern void     tell_player(player *, char *);
extern void     tell_room(room *, char *);
extern void     swho(player *, char *);
extern void     pager(player *, char *, int);
extern int      possible_move(player *, room *, int);
extern file     full_msg;

extern player  *p_sess;

/* interns */

void            error_on_load();
int             bad_player_load = 0;
char            player_loading[MAX_NAME + 2];
jmp_buf         jmp_env;

file            motd_msg, connect_msg, newban_msg, banned_msg, nonewbies_msg,
                newbie_msg, newpage1_msg, newpage2_msg, disclaimer_msg,
                splat_msg;

saved_player  **saved_hash[26];
int             update[26];

void            save_player(), newbie_check_name(), link_to_program();
int             restore_player();

saved_player  **birthday_list = 0;

/* update functions .. a complete database traversal */

/* crypts the password */

char           *update_password(char *oldpass)
{
   char            key[9];
   strncpy(key, oldpass, 8);
   return crypt(key, "SP");
}

void            players_update_function(player * p)
{
   char em[MAX_EMAIL];

   if (p->email[0] == ' ')
      strcpy(em, "     <VALIDATED AS SET>");
   else
      strcpy(em, p->email);

    if (p->residency & ADMIN)
       printf("%-18s -- %-40s >(ADMIN)\n", p->name, em);
    else if (p->residency & SU)
       printf("%-18s -- %-40s >(SU)\n", p->name, em);
    else if (p->residency & PSU)
       printf("%-18s -- %-40s >(PSEUDO)\n", p->name, em);
    else
       printf("%-18s -- %s\n", p->name, em); 
}

void            flags_update_function(player * p)
{
   printf("%-18s -- %-40s\n", p->name, bit_string(p->residency));
}

void            rooms_update_function(player * p)
{
   room           *r;
   saved_player   *sp;
   sp = p->saved;
   r = sp->rooms;
   while (r)
   {
      if (r->flags & OPEN)
      {
    decompress_room(r);
    printf("-=> %s.%s (%s)\n", r->owner->lower_name, r->id, r->name);
    if (r->exits.where)
       printf(r->exits.where);
      }
      r = r->next;
   }
}


void            do_update(int rooms)
{
   player         *p;
   saved_player   *scan, **hash;
   int             i, j, fd;

   fd = open("/dev/null", O_WRONLY);

   p = (player *) MALLOC(sizeof(player));

   for (j = 0; j < 26; j++)
   {
      /* printf("Updating %c\n",j+'a'); */
      hash = saved_hash[j];
      for (i = 0; i < HASH_SIZE; i++, hash++)
      {
         for (scan = *hash; scan; scan = scan->next)
         {
            if (scan->residency != STANDARD_ROOMS
                && scan->residency != SYSTEM_ROOM
                &&(scan->residency != BANISHED) && (scan->residency != BANISHD))
            {
               memset((char *) p, 0, sizeof(player));
               p->fd = fd;
               p->script = 0;
               p->location = (room *) - 1;
               restore_player(p, scan->lower_name);
               if (rooms)
                  rooms_update_function(p);
               else if (sys_flags & UPDATE)
                  players_update_function(p);
               else
                  flags_update_function(p);
               save_player(p);
            }
         }
      }
   }
   close(fd);
}


/* return the top player in a hash list */

saved_player   *find_top_player(char c, int h)
{
   if ((c < 0) || (c > 25))
      return 0;
   if ((h < 0) || (h > HASH_SIZE))
      return 0;
   return (*(saved_hash[c] + h));
}


/* birthdays !!! */

void            do_birthdays()
{
   player         *p;
   saved_player   *scan, **hash, **list;
   int             i, j, fd;
   time_t         t;
   struct tm      *date, *bday;
   char           *oldstack;

   fd = open("/dev/null", O_WRONLY);

   oldstack = stack;
   align(stack);
   list = (saved_player **) stack;

   t = time(0);
   date = localtime(&t);

   p = (player *) MALLOC(sizeof(player));

   for (j = 0; j < 26; j++)
   {
      hash = saved_hash[j];
      for (i = 0; i < HASH_SIZE; i++, hash++)
    for (scan = *hash; scan; scan = scan->next)
       if (scan->residency != STANDARD_ROOMS
           && scan->residency != SYSTEM_ROOM
           && (scan->residency != BANISHED) && (!(scan->residency & BANISHD)))
       {
          memset((char *) p, 0, sizeof(player *));
          restore_player(p, scan->lower_name);
          bday = localtime((time_t *)&(p->birthday));
          if ((bday->tm_mon == date->tm_mon) &&
         (bday->tm_mday == date->tm_mday))
          {
        *((saved_player **) stack) = scan;
        stack += sizeof(saved_player *);
        p->age++;
        save_player(p);
          }
       }
   }
   *((saved_player **) stack) = 0;
   stack += sizeof(saved_player *);
   if (birthday_list)
      FREE(birthday_list);

   i = (int) stack - (int) list;
   if (i > 4)
   {
      birthday_list = (saved_player **) MALLOC(i);
      memcpy(birthday_list, list, i);
   } else
      birthday_list = 0;

   close(fd);
}

/* saved player stuff */

/* see if a saved player exists (given lower case name) */

saved_player   *find_saved_player(char *name)
{
   saved_player  **hash, *list;
   int             sum = 0,h;
   char           *c;

   if (!isalpha(*name))
      return 0;
   hash = saved_hash[((int) (tolower(*name)) - (int) 'a')];
   for (c = name; *c; c++)
   {
      if (isalpha(*c))
         sum += (int) (tolower(*c)) - 'a';
      else
    return 0;
   }
   list = *(hash + (sum % HASH_SIZE));
   for (; list; list = list->next)
      if (!strcmp(name, list->lower_name))
         return list;
   return 0;
}


/* hard load and save stuff (ie to disk and back) */


/* extract one player */

void           extract_player(char *where, int length)
{
   int             len, sum;
   char           *oldstack, *c;
   saved_player   *old, *sp, **hash;

   oldstack = stack;
   where = get_int(&len, where);
   where = get_string(oldstack, where);
   stack = end_string(oldstack);
   old = find_saved_player(oldstack);
   sp = old;
   if (!old)
   {
      sp = (saved_player *) MALLOC(sizeof(saved_player));
      memset((char *) sp, 0, sizeof(saved_player));
      strncpy(sp->lower_name, oldstack, MAX_NAME);
      strncpy(player_loading, sp->lower_name, MAX_NAME);
      sp->rooms = 0;
      sp->mail_sent = 0;
      sp->mail_received = 0;
      sp->list_top = 0;
      hash = saved_hash[((int) sp->lower_name[0] - (int) 'a')];
      for (sum = 0, c = sp->lower_name; *c; c++)
         sum += (int) (*c) - 'a';
      hash = (hash + (sum % HASH_SIZE));
      sp->next = *hash;
      *hash = sp;
   }
   where = get_int(&sp->last_on, where);
   where = get_int(&sp->saved_flags, where);
   where = get_int(&sp->residency, where);

   if (sp->residency == BANISHED)
      sp->residency = BANISHD;
   if (sp->residency == BANISHD)
   {
      sp->last_host[0] = 0;
      sp->data.where = 0;
      sp->data.length = 0;
      stack = oldstack;
      return;
   }
   /*
    * This bit controls when idle players get wiped. After summer hols, this
    * bit MUST be uncommented
    */

     if ( ((time(0)-(sp->last_on)) > PLAYER_TIMEOUT) &&
       !(sp->residency&NO_TIMEOUT))
     {
       log("timeouts", sp->lower_name);
       remove_player_file(sp->lower_name);
       stack=oldstack;
       return;
     }

/* PUT ANYTHING TO CHANGE RESIDENCY OR OTHER FLAGS HERE */
   where = get_string(sp->last_host, where);
   where = get_int(&sp->data.length, where);
   sp->data.where = (char *) MALLOC(sp->data.length);
   memcpy(sp->data.where, where, sp->data.length);
   where += sp->data.length;
   where = retrieve_room_data(sp, where);
   where = retrieve_list_data(sp, where);
   where = retrieve_mail_data(sp, where);
   stack = oldstack;
}

/* hard load in on player file */

void            hard_load_one_file(char c)
{
   char           *oldstack, *where, *scan;
   int             fd, length, len2, i, fromjmp;

   oldstack = stack;
   if (sys_flags & VERBOSE)
   {
      sprintf(oldstack, "Loading player file '%c'.", c);
      stack = end_string(oldstack);
      log("boot", oldstack);
      stack = oldstack;
   }
#ifdef PC
   sprintf(oldstack, "files\\players\\%c", c);
   fd = open(oldstack, O_RDONLY | O_BINARY);
#else
   sprintf(oldstack, "files/players/%c", c);
   fd = open(oldstack, O_RDONLY | O_NDELAY);
#endif
   if (fd < 0)
   {
      sprintf(oldstack, "Failed to load player file '%c'", c);
      stack = end_string(oldstack);
      log("error", oldstack);
   } else
   {
      length = lseek(fd, 0, SEEK_END);
      lseek(fd, 0, SEEK_SET);
      if (length)
      {
         where = (char *) MALLOC(length);
         if (read(fd, where, length) < 0)
            handle_error("Can't read player file.");
         for (i = 0, scan = where; i < length;)
         {
            get_int(&len2, scan);
            fromjmp = setjmp(jmp_env);
            if (!fromjmp && !bad_player_load)
            {
               extract_player(scan, len2);
            } else
            {
               sprintf(oldstack, "Bad Player \'%s\' deleted on load.",
                       player_loading);
               stack = end_string(oldstack);
               log("boot", oldstack);
               stack = oldstack;
               remove_player_file(player_loading);
               bad_player_load = 0;
            }
            i += len2;
            scan += len2;
         }
         FREE(where);
      }
      close(fd);
   }
   stack = oldstack;
}


/* load in all the player files */

void            hard_load_files()
{
   char            c;
   int             i, hash_length;
   char           *oldstack;
#if defined(hpux) | defined(linux)
   struct sigaction sa;

   sa.sa_handler = error_on_load;
   sa.sa_mask = 0;
   sa.sa_flags = 0;
   sigaction(SIGSEGV, &sa, 0);
   sigaction(SIGSEGV, &sa, 0);
#else /* hpux | linux */
   signal(SIGSEGV, error_on_load);
   signal(SIGBUS, error_on_load);
#endif /* hpux | linux */
   oldstack = stack;
   hash_length = HASH_SIZE * sizeof(saved_player *);
   for (i = 0; i < 26; i++)
   {
      saved_hash[i] = (saved_player **) MALLOC(hash_length);
      memset((void *) saved_hash[i], 0, hash_length);
   }
   for (c = 'a'; c <= 'z'; c++)
      hard_load_one_file(c);
}


/* write one player file out */

void            write_to_file(saved_player * sp)
{
   char           *oldstack;
   int             length;
   oldstack = stack;
   if (sys_flags & VERBOSE && sys_flags & PANIC)
   {
      sprintf(oldstack, "Attempting to write player '%s'.", sp->lower_name);
      stack = end_string(oldstack);
      log("sync", oldstack);
      stack = oldstack;
   }
   stack += 4;
   stack = store_string(stack, sp->lower_name);
   stack = store_int(stack, sp->last_on);
   stack = store_int(stack, sp->saved_flags | COMPRESSED_LIST);
   stack = store_int(stack, sp->residency);
   if ((sp->residency != BANISHED) )
   {
      stack = store_string(stack, sp->last_host);
      stack = store_int(stack, sp->data.length);
      memcpy(stack, sp->data.where, sp->data.length);
      stack += sp->data.length;
      construct_room_save(sp);
      construct_list_save(sp);
      construct_mail_save(sp);
   }
   length = (int) stack - (int) oldstack;
   (void) store_int(oldstack, length);

}

/* sync player files corresponding to one letter */

void            sync_to_file(char c, int background)
{
   saved_player   *scan, **hash;
   char           *oldstack;
   int             fd, i, length;

   if (background && fork())
      return;

   oldstack = stack;
   if (sys_flags & VERBOSE)
   {
      sprintf(oldstack, "Syncing File '%c'.", c);
      stack = end_string(oldstack);
      log("sync", oldstack);
      stack = oldstack;
   }
   hash = saved_hash[((int) c - (int) 'a')];
   for (i = 0; i < HASH_SIZE; i++, hash++)
      for (scan = *hash; scan; scan = scan->next)
    if (scan->residency != STANDARD_ROOMS
        && scan->residency != SYSTEM_ROOM
        && (!(scan->residency & NO_SYNC) || scan->residency == BANISHED))
       write_to_file(scan);
   length = (int) stack - (int) oldstack;


   /* test that you can write out a file ok */

   strcpy(stack, "files/players/backup_write");
   fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR);
   if (fd < 0)
      handle_error("Primary open failed (player back)");
   if (write(fd, oldstack, length) < 0)
      handle_error("Primary write failed "
         "(playerback)");
   close(fd);

#ifdef PC
   sprintf(stack, "files\\players\\%c", c);
   fd = open(stack, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY);
#else
   sprintf(stack, "files/players/%c", c);
   fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR);
#endif
   if (fd < 0)
      handle_error("Failed to open player file.");
   if (write(fd, oldstack, length) < 0)
      handle_error("Failed to write player file.");
   close(fd);
   update[(int) c - (int) 'a'] = 0;
   stack = oldstack;

   if (background)
      exit(0);

}


/* sync everything to disk */

void            sync_all()
{
   char            c, *oldstack;
   oldstack = stack;
   for (c = 'a'; c <= 'z'; c++)
      sync_to_file(c, 0);
   log("sync", "Full Sync Completed.");
}

/* fork and sync the playerfiles */

void            fork_the_thing_and_sync_the_playerfiles(void)
{
   int             fl;
   char            c, *oldstack;
   fl = fork();
   if (fl == -1)
   {
      log("error", "Forked up!");
      return;
   }
   if (fl > 0)
      return;
   sync_all();
   exit(0);
}

/* flicks on the update flag for a particular player hash */

void            set_update(char c)
{
   update[(int) c - (int) 'a'] = 1;
}


/* removes an entry from the saved player lists */

int             remove_player_file(char *name)
{
   saved_player   *previous = 0, **hash, *list;
   char           *c;
   int             sum = 0;

   if (!isalpha(*name))
   {
      log("error", "Tried to remove non-player from save files.");
      return 0;
   }
   strcpy(stack, name);
   lower_case(stack);

   hash = saved_hash[((int) (*stack) - (int) 'a')];
   for (c = stack; *c; c++)
   {
      if (isalpha(*c))
         sum += (int) (*c) - 'a';
      else
      {
         log("error", "Remove bad name from save files");
         return 0;
      }
   }

   hash += (sum % HASH_SIZE);
   list = *hash;
   for (; list; previous = list, list = list->next)
   {
      if (!strcmp(stack, list->lower_name))
      {
         if (previous)
            previous->next = list->next;
         else
            *hash = list->next;
         if (list->data.where)
            FREE(list->data.where);
         if (list->mail_received)
            FREE(list->mail_received);
         free_room_data(list);
         FREE((void *) list);
         set_update(*stack);
         return 1;
      }
   }
   return 0;
}

/* remove an entire hash of players */

void            remove_entire_list(char c)
{
   saved_player  **hash, *sp, *next;
   int             i;
   if (!isalpha(c))
      return;
   hash = saved_hash[((int) (c) - (int) 'a')];
   for (i = 0; i < HASH_SIZE; i++, hash++)
   {
      sp = *hash;
      while (sp)
      {
    next = sp->next;
    if (sp->data.where)
       FREE(sp->data.where);
    free_room_data(sp);
    FREE((void *) sp);
    sp = next;
      }
      *hash = 0;
   }
   set_update(c);
}

/* routines to save a player to the save files */

/* makes the save data onto the stack */

file            construct_save_data(player * p)
{
   file            d;
   d.where = stack;

   stack = store_string(stack, p->name);
   stack = store_string(stack, p->prompt);
   stack = store_string(stack, p->converse_prompt);
   stack = store_string(stack, p->email);
   if (p->password[0] == -1)
      p->password[0] = 0;
   stack = store_string(stack, p->password);
   stack = store_string(stack, p->title);
   stack = store_string(stack, p->plan);
   stack = store_string(stack, p->description);
   stack = store_string(stack, p->enter_msg);
   stack = store_string(stack, p->pretitle);

   stack = store_string(stack, p->ignore_msg);
   stack = store_string(stack, p->room_connect);

   stack = store_int(stack, p->term_width);
   stack = store_int(stack, p->word_wrap);
   stack = store_int(stack, p->max_rooms);
   stack = store_int(stack, p->max_exits);
   stack = store_int(stack, p->max_autos);
   stack = store_int(stack, p->max_list);
   stack = store_int(stack, p->max_mail);
   stack = store_int(stack, p->gender);
   stack = store_int(stack, p->no_shout);
   stack = store_int(stack, p->total_login);
   stack = store_int(stack, p->term);
   stack = store_int(stack, p->birthday);
   stack = store_int(stack, p->age);

   /* Here goes with adding to the playerfiles */

   stack = store_int(stack, p->jetlag);
   stack = store_int(stack, p->sneezed);

   d.length = (int) stack - (int) d.where;
   stack = d.where;
   return d;
}

/* the routine that sets everything up for the save */

void            save_player(player * p)
{
   saved_player   *old, **hash, *sp;
   int             sum;
   file            data;
   char           *c, *oldstack;
   int verb = 1;

   oldstack = stack;

   if (!(p->location) || !(p->name[0])
       || p->residency == NON_RESIDENT)
      return;

   if (sys_flags & PANIC)
   {
      c = stack;
      sprintf(c, "Attempting to save player %s.", p->name);
      stack = end_string(c);
      log("boot", c);
      stack = c;
   }
   if (!(isalpha(p->lower_name[0])))
   {
      log("error", "Tried to save non-player.");
      return;
   }
   if (p->residency & SYSTEM_ROOM)
      verb = 0;
   if (verb)
   {
      if (!(p->password[0] && p->password[0] != -1))
      {
         tell_player(p, " Tried to save character but failed ...\n"
           " Your character will not save until you set a password.\n"
            " Simply type 'password' whilst in command mode to set one.\n");
         p->residency |= NO_SYNC;
         tell_player(p, " NOT saved.\n");
         stack = oldstack;
         return;
      }
      if (p->email[0] == 2)
      {
         tell_player(p, " Tried to save character but failed ...\n"
       " Your character will not save until you set an email address.\n"
       " To set this just type 'email <whatever>', where <whatever> is your\n"
       " email address.\n"
       " If you do not have an email, please speak to one of the superusers.\n");
         p->residency |= NO_SYNC;
         tell_player(p, "NOT saved.\n");
         stack = oldstack;
         return;
      }
   }
   p->residency &= ~NO_SYNC;
   p->saved_residency = p->residency;
   old = p->saved;
   sp = old;
   if (!old)
   {
      sp = (saved_player *) MALLOC(sizeof(saved_player));
      memset((char *) sp, 0, sizeof(saved_player));
      strncpy(sp->lower_name, p->lower_name, MAX_NAME);
      sp->rooms = 0;
      sp->mail_sent = 0;
      sp->mail_received = 0;
      sp->list_top = 0;
      hash = saved_hash[((int) p->lower_name[0] - (int) 'a')];
      for (sum = 0, c = p->lower_name; *c; c++)
      {
         if (isalpha(*c))
            sum += (int) (*c) - 'a';
         else
         {
            tell_player(p, " Eeek, trying to save bad player name !!\n");
            FREE(sp);
            return;
         }
      }
      hash = (hash + (sum % HASH_SIZE));
      sp->next = *hash;
      *hash = sp;
      p->saved = sp;
      sp->saved_flags = p->saved_flags;
      create_room(p);
   }
   data = construct_save_data(p);
   if (!data.length)
   {
      log("error", "Bad construct save.");
      return;
   }
   if (old && sp->data.where)
      FREE((void *) sp->data.where);
   sp->data.where = (char *) MALLOC(data.length);
   sp->data.length = data.length;
   memcpy(sp->data.where, data.where, data.length);
   sp->residency = p->saved_residency;
   sp->saved_flags = p->saved_flags;
   strncpy(sp->last_host, p->inet_addr, MAX_INET_ADDR);
   set_update(*(sp->lower_name));
   p->saved = sp;
   sp->last_on = time(0);
   if (verb)
      tell_player(p, " Character Saved ...\n");
}

/* the routine that sets everything up for the save */

void            create_banish_file(char *name)
{
   saved_player  **hash, *sp, *scan;
   int             sum;
   char           *c;

   sp = (saved_player *) MALLOC(sizeof(saved_player));
   memset((char *) sp, 0, sizeof(saved_player));
   strcpy(stack, name);
   lower_case(stack);
   strncpy(sp->lower_name, stack, MAX_NAME - 2);
   sp->rooms = 0;
   sp->mail_sent = 0;
   sp->mail_received = 0;
   hash = saved_hash[((int) name[0] - (int) 'a')];
   for (sum = 0, c = name; *c; c++)
      if (isalpha(*c))
    sum += (int) (*c) - 'a';
      else
      {
    log("error", "Tried to banish bad player");
    FREE(sp);
    return;
      }
   hash = (hash + (sum % HASH_SIZE));
   scan = *hash;
   while (scan)
   {
      hash = &(scan->next);
      scan = scan->next;
   }
   *hash = sp;
   sp->residency = BANISHD;
   sp->saved_flags = 0;
   sp->last_host[0] = 0;
   sp->last_on = time(0);
   sp->next = 0;
   set_update(tolower(*(sp->lower_name)));
}


/* load from a saved player into a current player */

/* actually do load */

int             load_player(player * p)
{
   saved_player   *sp;
   char           *r;

   lower_case(p->lower_name);
   sp = find_saved_player(p->lower_name);
   p->saved = sp;
   if (!sp)
      return 0;

   p->residency = sp->residency;

   p->saved_residency = p->residency;
   p->saved_flags = sp->saved_flags;
   if (sp->residency == BANISHED || sp->residency == STANDARD_ROOMS
        || sp->residency == SYSTEM_ROOM
        || sp->residency == BANISHD)
      return 1;

   r = sp->data.where;
   r = get_string(p->name, r);
   r = get_string(p->prompt, r);
   r = get_string(p->converse_prompt, r);
   r = get_string(p->email, r);
   r = get_string(p->password, r);
   r = get_string(p->title, r);
   r = get_string(p->plan, r);
   r = get_string(p->description, r);
   r = get_string(p->enter_msg, r);
   r = get_string(p->pretitle, r);

   r = get_string(p->ignore_msg, r);
   r = get_string(p->room_connect, r);

   r = get_int(&p->term_width, r);
   r = get_int(&p->word_wrap, r);
   r = get_int(&p->max_rooms, r);
   r = get_int(&p->max_exits, r);
   r = get_int(&p->max_autos, r);
   r = get_int(&p->max_list, r);
   r = get_int(&p->max_mail, r);
   r = get_int(&p->gender, r);
   r = get_int(&p->no_shout, r);
   r = get_int(&p->total_login, r);
   r = get_int(&p->term, r);
   r = get_int(&p->birthday, r);
   r = get_int(&p->age, r);

   r = get_int(&p->jetlag, r);
   r = get_int(&p->sneezed, r);

   if (((p->term_width) >> 1) <= (p->word_wrap))
      p->word_wrap = (p->term_width) >> 1;

   decompress_list(sp);
   p->saved_flags = sp->saved_flags;
   return 1;
}

/* load and do linking */

int             restore_player(player * p, char *name)
{
   return restore_player_title(p, name, 0);
}

int             restore_player_title(player * p, char *name, char *title)
{
   int             did_load;
   int             found_lower;
   char           *n;

   strncpy(p->name, name, MAX_NAME - 2);
   strncpy(p->lower_name, name, MAX_NAME - 2);
   lower_case(p->lower_name);
   if (!strcmp(p->name, p->lower_name))
      p->name[0] = toupper(p->name[0]);
   found_lower = 0;
   n = p->name;
   while (*n)
   {
      if (*n >= 'a' && *n <= 'z')
      {
         found_lower = 1;
      }
      *n++;
   }
   if (!found_lower)
   {
      n = p->name;
      *n++;
      while (*n)
      {
         *n = *n - ('A' - 'a');
         *n++;
      }
   }

/* Set up a default player structure, methinks */

   strncpy(p->prompt, "->", MAX_PROMPT);
   strncpy(p->converse_prompt, "(Converse) ->", MAX_PROMPT);

   strcpy(p->enter_msg, "enters in a standard kind of way.");


   p->term_width = 79;
   p->column = 0;
   p->word_wrap = 10;

   p->total_login = 0;

   p->gender = VOID_GENDER;
   p->no_shout = 180;

   p->saved_flags = PRIVATE_EMAIL | TAG_ECHO | MAIL_INFORM | IAC_GA_ON | NOEPREFIX;

   strncpy(p->title, "the newbie, so treat me nicely.", MAX_TITLE);
   strncpy(p->description, "Isn't it time I wrote my own description ?",
      MAX_DESC);
   strncpy(p->plan, "I must write myself a proper plan sometime ...", MAX_PLAN);

   p->max_rooms = 2;
   p->max_exits = 5;
   p->max_autos = 5;
   p->max_list = 25;
   p->max_mail = 10;

   p->birthday = 0;
   p->age = 0;
   p->jail_timeout = 0;

   p->script = 0;
   strcpy(p->script_file, "dummy.log");
   p->assisted_by[0] = 0;

   p->residency = 0;

   strcpy(p->ignore_msg, "");
   p->jetlag = 0;

   did_load = load_player(p);

   strcpy(p->assisted_by, "");

   if (title && *title)
   {
      strncpy(p->title, title, MAX_TITLE);
      p->title[MAX_TITLE] = 0;
   }
   if ((p->saved_flags & IAC_GA_ON) && (!(p->flags & EOR_ON)))
      p->flags |= IAC_GA_DO;
   else
      p->flags &= ~IAC_GA_DO;

   if (p->residency == 0 && did_load == 1)
      p->residency = SYSTEM_ROOM;

   if (p->saved_flags & SAVEDFROGGED)
      p->flags |= FROGGED;

   /* got to have someone here I'm afraid..  */

   if (!strcmp("admin", p->lower_name))
   {
      p->residency = HCADMIN_INIT;
   }
   if (p->residency & PSU)
      p->no_shout = 0;




   /* integrity .. sigh */

   p->saved_residency = p->residency;

   /*
    * if (p->max_rooms>50 || p->max_rooms<0) p->max_rooms=3; if
    * (p->max_exits>50 || p->max_exits<0) p->max_exits=10; if (p->max_autos>50
    * || p->max_autos<0) p->max_autos=10; if (p->max_list>100 ||
    * p->max_list<0) p->max_list=20; if (p->max_mail>100 || p->max_mail<0)
    * p->max_mail=10; if (p->term_width>200 || p->term_width<0)
    * p->term_width=79;
    */
   if ((p->word_wrap) > ((p->term_width) >> 1) || p->word_wrap < 0)
      p->word_wrap = (p->term_width) >> 1;
   if (p->term > 9)
      p->term = 0;

   return did_load;
}


/* current player stuff */

/* create an abstract player into the void hash list */

player         *create_player()
{
   player         *p;

   p = (player *) MALLOC(sizeof(player));
   memset((char *) p, 0, sizeof(player));

   if (flatlist_start)
      flatlist_start->flat_previous = p;
   p->flat_next = flatlist_start;
   flatlist_start = p;

   p->hash_next = hashlist[0];
   hashlist[0] = p;
   p->hash_top = 0;
   p->timer_fn = 0;
   p->timer_count = -1;
   p->edit_info = 0;
   p->logged_in = 0;
   return p;
}

/* unlink p from all the lists */

void            punlink(player * p)
{
   player         *previous, *scan;

   /* reset the session p */

   p_sess = 0;

   /* first remove from the hash list */

   scan = hashlist[p->hash_top];
   previous = 0;
   while (scan && scan != p)
   {
      previous = scan;
      scan = scan->hash_next;
   }
   if (!scan)
      log("error", "Bad hash list");
   else if (!previous)
      hashlist[p->hash_top] = p->hash_next;
   else
      previous->hash_next = p->hash_next;

   /* then remove from the flat list */

   if (p->flat_previous)
      p->flat_previous->flat_next = p->flat_next;
   else
      flatlist_start = p->flat_next;
   if (p->flat_next)
      p->flat_next->flat_previous = p->flat_previous;

   /* finally remove from the location list */

   if (p->location)
   {
      previous = 0;
      scan = p->location->players_top;
      while (scan && scan != p)
      {
    previous = scan;
    scan = scan->room_next;
      }
      if (!scan)
    log("error", "Bad Location list");
      else if (!previous)
    p->location->players_top = p->room_next;
      else
    previous->room_next = p->room_next;
   }
}

/* remove a player from the current hash lists */

void            destroy_player(player * p)
{
#ifndef PC
   if ((p->fd) > 0)
   {
      shutdown(p->fd, 0);
      close(p->fd);
   }
#endif
   if (p->name[0] && p->location)
      current_players--;
   punlink(p);
   if (p->edit_info)
      finish_edit(p);
   FREE(p);
#ifdef PC
   if (p == input_player)
   {
      input_player = flatlist_start;
      if (!input_player)
    sys_flags |= SHUTDOWN;
   }
#endif
}

/* get person to agree to disclaimer */

void            agree_disclaimer(player * p, char *str)
{
   p->input_to_fn = 0;
   if (!strcasecmp(str, "continue"))
   {
      p->saved_flags |= AGREED_DISCLAIMER;
      if (p->saved)
         p->saved->saved_flags |= AGREED_DISCLAIMER;
      link_to_program(p);
      return;
   }
   tell_player(p, "\n Disconnecting from program.\n\n");
   quit(p, "");
}

/* links a person into the program properly */

void            link_to_program(player * p)
{
   char           *oldstack;
   saved_player   *sp;
   player         *search, *previous, *scan;
   room           *r, *rm;
   int             hash;
   time_t         t;
   struct tm      *log_time;
   oldstack = stack;


   search = hashlist[((int) (p->lower_name[0])) - (int) 'a' + 1];
   for (; search; search = search->hash_next)
   {
      if (!strcmp(p->lower_name, search->lower_name))
      {
         if (p->residency == NON_RESIDENT)
         {
            tell_player(p, "\n Sorry there is already someone on the "
                           "program with that name.\n Please try again, "
                           "but use a different name.\n\n");
            quit(p, "");
            return;
         } else
         {
            tell_player(p, "\n You were already on the program !!\n\n"
                           " Closing other connection.\n\n");
            p->total_login = search->total_login;
            search->flags |= RECONNECTION;
            p->flags |= RECONNECTION;

            if (search->location)
            {
               previous = 0;
               scan = search->location->players_top;
               while (scan && scan != search)
               {
                  previous = scan;
                  scan = scan->room_next;
               }
               if (!scan)
                  log("error", "Bad Location list");
               else if (!previous)
                  search->location->players_top = search->room_next;
               else
                  previous->room_next = search->room_next;
            }
            search->location = 0;
            quit(search, 0);
         }
      }
   }


   /* do the disclaimer biz  */

   if (!(p->saved_flags & AGREED_DISCLAIMER))
   {
      if (!(p->saved && p->password[0] == 0))
      {
         tell_player(p, disclaimer_msg.where);
         do_prompt(p, "Enter 'continue' or 'end':");
         p->input_to_fn = agree_disclaimer;
         return;
      }
   }
   /* remove player from non name hash list */

   previous = 0;
   scan = hashlist[0];
   while (scan && scan != p)
   {
      previous = scan;
      scan = scan->hash_next;
   }
   if (!scan)
      log("error", "Bad non-name hash list");
   else if (!previous)
      hashlist[0] = p->hash_next;
   else
      previous->hash_next = p->hash_next;

   /* now place into named hashed lists */

   hash = (int) (p->lower_name[0]) - (int) 'a' + 1;
   p->hash_next = hashlist[hash];
   hashlist[hash] = p;
   p->hash_top = hash;

   if (p->flags & SITE_LOG)
     {
       sprintf(oldstack,"%s - %s",p->name,p->inet_addr);
       stack=end_string(oldstack);
       log("site", oldstack);
     }

   t = time(0);
   log_time = localtime(&t);

   p->flags |= PROMPT;
   p->timer_fn = 0;
   p->timer_count = -1;

   p->saved_flags &= ~NEW_SITE;

   p->mode = NONE;
   if (p->residency != NON_RESIDENT)
   {
      p->logged_in = 1;
      sp = p->saved;
      tell_player(p, motd_msg.where);
      if (p->saved_flags & CONVERSE)
         p->mode |= CONV;
   } else
   {
      tell_player(p, newbie_msg.where);
      tell_player(p, motd_msg.where);
   }
   current_players++;
   p->on_since = time(0);
   logins++;

   p->shout_index = 50;
   if (p->residency != NON_RESIDENT)
      player_flags(p);

   if (p->saved_flags & SAVEDJAIL)
   {
      p->jail_timeout = -1;
      trans_to(p, "system.prison");
   } else if (p->saved_flags & TRANS_TO_HOME || *p->room_connect)
   {
      sp = p->saved;
      if (!sp)
         tell_player(p, " Double Eeek (room_connect)!\n");
      else
      {
         if (p->saved_flags & TRANS_TO_HOME)
         {
            for (r = sp->rooms; r; r = r->next)
               if (r->flags & HOME_ROOM)
               {
                  sprintf(oldstack, "%s.%s", r->owner->lower_name, r->id);
                  stack = end_string(oldstack);
                  trans_to(p, oldstack);
                  break;
            }
         } else
         {
            rm = convert_room(p, p->room_connect);
            if (rm && rm->flags & CONFERENCE && possible_move(p, rm, 1))
               trans_to(p, p->room_connect);
         }
         if (!(p->location))
            tell_player(p, " -=> Tried to connect you to a room, but failed"
                           " !!\n\n");
      }
   }
   if (!(p->location))
   {
      trans_to(p, ENTRANCE_ROOM);
   }
   if (p->flags & RECONNECTION)
   {
      do_inform(p, "[%s reconnects] %s");
      sprintf(oldstack,
              " %s's image shimmers for a moment as %s re-connects.\n",
              p->name, gstring(p));
      stack = end_string(oldstack);
      tell_room(p->location, oldstack);
      p->flags &= ~RECONNECTION;
   } else
   {
      if (p->gender==PLURAL)
	  do_inform(p, "[%s have connected] %s");	  
      else
	  do_inform(p, "[%s has connected] %s");

      if (p->gender==PLURAL)
	sprintf(oldstack,
                " %s appear as a wobbly image that soon solidifies.\n",
                p->name);
      else
	sprintf(oldstack,
                " %s appears as a wobbly image that soon solidifies.\n",
                p->name);
      stack = end_string(oldstack);
      tell_room(p->location, oldstack);
   }
   if (p->saved)
   {
      decompress_list(p->saved);
      p->saved_flags = p->saved->saved_flags;
   }
   stack = oldstack;
}

/* get new gender */

void            enter_gender(player * p, char *str)
{
   switch (tolower(*str))
   {
   case 'm':
     p->gender = MALE;
     tell_player(p, " Gender set to Male.\n");
     break;
   case 'f':
     p->gender = FEMALE;
     tell_player(p, " Gender set to Female.\n");
     break;
   case 'p':
     p->gender = PLURAL;
     strncpy(p->title, "the newbies, so treat us nicely.", MAX_TITLE);
     strncpy(p->description, "Isn't it time we wrote our own descriptions ?",
	     MAX_DESC);
     strncpy(p->plan, "We must write ourselves a proper plan sometime ...",
	     MAX_PLAN);
     strcpy(p->enter_msg, "enter in a standard kind of way.");
     tell_player(p, " Gender set to Plural.\n");
     break;
   case 'n':
     p->gender = OTHER;
     tell_player(p, " Gender set to well, erm, something.\n");
     break;
   default:
     tell_player(p, " No gender set.\n");
     break;
   }
   p->input_to_fn = 0;
   link_to_program(p);
}


/* time out */

void            login_timeout(player * p)
{
   tell_player(p, "\n\n Connection Timed Out ...\n\n");
   quit(p, 0);
}


/* newbie stuff */

void            newbie_get_gender(player * p, char *str)
{
   tell_player(p, "\n\n The program requires that you enter your gender.\n"
     " This is used solely for the purposes of correct english and grammer.\n"
      " If you object to this, then simply type 'n' for not applicable.\n\n");
   do_prompt(p, "Enter (M)ale, (F)emale, (P)lural, or (N)ot applicable:\n");
   p->input_to_fn = enter_gender;
}

void            got_new_name(player * p, char *str)
{
   char           *oldstack, *cpy;
   int             length = 0;
   oldstack = stack;

   for (cpy = str; *cpy; cpy++)
      if (isalpha(*cpy))
      {
    *stack++ = *cpy;
    length++;
      }
   *stack++ = 0;
   length++;
   if (length > (MAX_NAME - 2))
   {
      tell_player(p, " Sorry, that name is too long, please enter something "
        "shorter.\n\n");
      do_prompt(p, "Please enter a name:");
      p->input_to_fn = got_new_name;
      if (sys_flags & VERBOSE)
      {
    cpy = stack;
    sprintf(cpy, "Name too long : %s\n", str);
    stack = end_string(cpy);
    log("connection", cpy);
    stack = cpy;
      }
      stack = oldstack;
      return;
   }
   if (length < 3)
   {
      tell_player(p, " Thats a bit short, try something longer.\n\n");
      do_prompt(p, "Please enter different name:");
      p->input_to_fn = got_new_name;
      stack = oldstack;
      return;
   }
   if (restore_player_title(p, oldstack, 0))
   {
      tell_player(p, " Sorry, there is already someone who uses the "
                     "program with that name.\n\n");
      do_prompt(p, "Please enter different name:");
      p->input_to_fn = got_new_name;
      stack = oldstack;
      return;
   }
   newbie_get_gender(p, str);
   stack = oldstack;
}


void            newbie_got_name_answer(player * p, char *str)
{
   switch (tolower(*str))
   {
    case 'y':
    newbie_get_gender(p, str);
    break;
      case 'n':
    tell_player(p, "\n\n Ok, then, please enter a new name ...\n\n");
    do_prompt(p, "Enter a name:");
    p->input_to_fn = got_new_name;
    break;
      default:
    tell_player(p, " Please answer with Y or N.\n");
    newbie_check_name(p, str);
    break;
   }
}

void            newbie_check_name(player * p, char *str)
{
   char           *oldstack;
   oldstack = stack;

   sprintf(stack, "\n\n You entered the name '%s' when you first logged in.\n"
   " Is this the name that you wish to be known as on the program ?\n\n",
      p->name);
   stack = end_string(stack);
   tell_player(p, oldstack);
   do_prompt(p, "Answer Y or N:");
   p->input_to_fn = newbie_got_name_answer;
   stack = oldstack;
}

void            newbie_start(player * p, char *str)
{
   tell_player(p, newpage2_msg.where);
   do_prompt(p, "Hit return to continue:");
   p->input_to_fn = newbie_check_name;
}


/* test password */

int             check_password(char *password, char *entered, player * p)
{
   char            key[9];
   strncpy(key, entered, 8);
   return (!strncmp(crypt(key, p->lower_name), password, 11));
}


void            got_password(player * p, char *str)
{
   char           *oldstack;
   oldstack = stack;
   p->input_to_fn = 0;
   password_mode_off(p);

   if (!check_password(p->password, str, p))
   {
      tell_player(p, "\n\n Hey !! that ain't right !\n"
                     " Wrong password .... closing connection.\n\n");
      sprintf(stack, "Password fail: %s - %s", p->inet_addr, p->name);
      stack = end_string(stack);
      if (p->residency & SU)
      {
         log("sufailpass", oldstack);
      } else
      {
         log("connection", oldstack);
      }
      stack = oldstack;
      p->flags |= NO_SAVE_LAST_ON;
      quit(p, "");
      return;
   }
   if (p->gender < 0)
   {
      p->input_to_fn = enter_gender;
      tell_player(p, "\n You have no gender set.\n");
      do_prompt(p, "Please choose M(ale), F(female) or N(ot applicable):");
      return;
   }
   stack = oldstack;
   link_to_program(p);
}

/* check for soft splats */

int             site_soft_splat(player * p)
{
   int             no1 = 0, no2 = 0, no3 = 0, no4 = 0, out;

   out = time(0);
   sscanf(p->num_addr, "%d.%d.%d.%d", &no1, &no2, &no3, &no4);
   if (out <= soft_timeout && no1 == soft_splat1 && no2 == soft_splat2)
      return 1;
   else
      return 0;
}

/* calls here when the player has entered their name */

void            got_name(player * p, char *str)
{
   int             t;
   char           *oldstack, *cpy, *space;
   int             length = 0, isspace = 0, nologin;
   player         *search;

   oldstack = stack;

   for (cpy = str; *cpy && *cpy != ' '; cpy++)
      if (isalpha(*cpy))
      {
         *stack++ = *cpy;
         length++;
      }
   if (*cpy == ' ')
      isspace = 1;
   *stack++ = 0;
   space = stack;
   length++;

   if (length > (MAX_NAME - 2))
   {
      tell_player(p, " Sorry, that name is too long, please enter something "
                     "shorter.\n\n");
      do_prompt(p, "Please enter a name:");
      p->input_to_fn = got_name;

      if (sys_flags & VERBOSE)
      {
	cpy = stack;
	sprintf(cpy, "Name too long : %s\n", str);
	stack = end_string(cpy);
	log("connection", cpy);
	stack = cpy;
      }
      stack = oldstack;
      return;
   }

   if (length < 3)
   {
      tell_player(p, " Thats a bit short, try something longer.\n\n");
      do_prompt(p, "Please enter a name:");
      p->input_to_fn = got_name;
      stack = oldstack;
      return;
   }

   if (!strcasecmp("who", oldstack))
   {
      swho(p, 0);
      p->input_to_fn = got_name;
      do_prompt(p, "\nPlease enter a name:");
      stack = oldstack;
      return;
   }

   if (!strcasecmp("quit", oldstack))
   {
      quit(p, "");
      stack = oldstack;
      return;
   }
   if (isspace)
      while (*++cpy == ' ');
   if (restore_player_title(p, oldstack, isspace ? cpy : 0))
   {
      if (p->residency & BANISHD)
      {
	tell_player(p, banned_msg.where);
	quit(p, "");
	stack = oldstack;
	return;
      }
      if (p->residency == SYSTEM_ROOM)
      {
	tell_player(p, "\n Sorry but that name is reserved.\n"
		    " Choose a different name ...\n\n");
	do_prompt(p, "Please enter a name:");
	p->input_to_fn = got_name;
	stack = oldstack;
	return;
      }
      t = time(0);
      if (p->sneezed > t)
      {
	nologin = p->sneezed - t;
	stack = oldstack;
	sprintf(stack, "\n Sorry, you have been prevented from logging on for "
		"another %d seconds (and counting ...)\n\n", nologin);
	stack = end_string(stack);
	tell_player(p, oldstack);
	quit(p, "");
	stack = oldstack;
	return;
      } else
	p->sneezed = 0;
/* login limits checks here */
      if (!(p->residency & (LOWER_ADMIN | ADMIN)))
      {
	if (current_players >= max_players)
         {
	   tell_player(p, full_msg.where);
	   quit(p, 0);
         }
      }
      sprintf(oldstack, "\n Password needed for '%s'\n\n", p->name);
      stack = end_string(oldstack);
      tell_player(p, oldstack);
      stack = oldstack;

      if (p->password[0])
      {
	password_mode_on(p);
	do_prompt(p, "Please enter your password:");
	p->input_to_fn = got_password;
	p->timer_count = 60;
	p->timer_fn = login_timeout;
	stack = oldstack;
	return;
      }
      stack = oldstack;
      link_to_program(p);
      tell_player(p, "\n You have no password !!!\n"
	" Please set one as soon as possible with the 'password' command.\n"
        " If you don't your character will not save\n");
      p->input_to_fn = 0;
      return;
   }
   p->input_to_fn = 0;

   if (p->flags & CLOSED_TO_NEWBIES)
   {
      tell_player(p, newban_msg.where);
      quit(p, "");
      stack = oldstack;
      return;
   }
   if (site_soft_splat(p))
   {
      tell_player(p, newban_msg.where);
      quit(p, "");
      stack = oldstack;
      return;
   }
   if (sys_flags & CLOSED_TO_NEWBIES)
   {
      tell_player(p, nonewbies_msg.where);
      quit(p, "");
      stack = oldstack;
      return;
   }
   search = hashlist[((int) (p->lower_name[0])) - (int) 'a' + 1];
   for (; search; search = search->hash_next)
      if (!strcmp(p->lower_name, search->lower_name))
      {
    tell_player(p, "\n Sorry there is already someone on the program "
           "with that name.\nPlease try again, but use a "
           "different name.\n\n");
    quit(p, "");
    return;
      }
   tell_player(p, newpage1_msg.where);
   do_prompt(p, "Hit return to continue:");
   p->input_to_fn = newbie_start;
   p->timer_count = 1800;
   stack = oldstack;
}

/* a new player has connected */

void            connect_to_prog(player * p)
{
   player *cp;

   cp = current_player;
   current_player = p;
   tell_player(p, "\377\373\031");   /* send will EOR */
   tell_player(p, connect_msg.where);
   do_prompt(p, "Please enter your name:");
   p->input_to_fn = got_name;
   p->timer_count = 60;
   p->timer_fn = login_timeout;
   current_player = cp;
}

/* tell player motd */

void            motd(player * p, char *str)
{
   if (!p->residency)
      tell_player(p, newbie_msg.where);
   tell_player(p, motd_msg.where);
}


/* init everything needed for the plist file */

void            init_plist()
{
   char           *oldstack;
   int             i;

   oldstack = stack;

#ifdef PC
   newban_msg = load_file("files\\newban.msg");
   nonewbies_msg = load_file("files\\nonew.msg");
   connect_msg = load_file("files\\connect.msg");
   motd_msg = load_file("files\\motd.msg");
   banned_msg = load_file("files\\banned.msg");
   newbie_msg = load_file("files\\newbie.msg");
   newpage1_msg = load_file("files\\newpage1.msg");
   newpage2_msg = load_file("files\\newpage2.msg");
#else
   newban_msg = load_file("files/newban.msg");
   nonewbies_msg = load_file("files/nonew.msg");
   connect_msg = load_file("files/connect.msg");
   motd_msg = load_file("files/motd.msg");
   banned_msg = load_file("files/banned.msg");
   newbie_msg = load_file("files/newbie.msg");
   newpage1_msg = load_file("files/newpage1.msg");
   newpage2_msg = load_file("files/newpage2.msg");
   disclaimer_msg = load_file("files/disclaimer.msg");

   splat_msg = load_file("files/splat.msg");

#endif

   hard_load_files();
   for (i = 0; i < 26; i++)
      update[i] = 0;

   stack = oldstack;
}


/* various associated routines */

/* find one player given a (partial) name */

/* little subroutinette */

int             match_player(char *str1, char *str2)
{
   for (; *str2; str1++, str2++)
   {
      if (*str1 != *str2 && *str2 != ' ')
      {
    if (!(*str1) && *str2 == '.' && !(*(str2 + 1)))
       return 1;
    return 0;
      }
   }
   if (*str1)
      return -1;
   return 1;
}


/* command to view res files */

void            view_saved_lists(player * p, char *str)
{
   saved_player   *scan, **hash;
   int             i, j;
   char           *oldstack;
   oldstack = stack;
   if (!*str || !isalpha(*str))
   {
      tell_player(p, " Argument is a letter.\n");
      return;
   }
   strcpy(stack, "[HASH] [NAME]               12345678901234567890123456789012\n");
   stack = strchr(stack, 0);
   hash = saved_hash[((int) (tolower(*str)) - (int) 'a')];
   for (i = 0; i < HASH_SIZE; i++, hash++)
      for (scan = *hash; scan; scan = scan->next)
      {
    sprintf(stack, "[%d]", i);
    j = strlen(stack);
    stack = strchr(stack, 0);
    for (j = 7 - j; j; j--)
       *stack++ = ' ';
    strcpy(stack, scan->lower_name);
    j = strlen(stack);
    stack = strchr(stack, 0);
    for (j = 21 - j; j; j--)
       *stack++ = ' ';
    switch (scan->residency)
    {
       case STANDARD_ROOMS:
          strcpy(stack, "Standard room file.");
          break;
       case BANISHD:
          strcpy(stack, "BANISHED (Name Only)");
          break;
       default:
          if (scan->residency & BANISHD)
          {
             strcpy(stack, "BANISHED");
          } else
          {
             strcpy(stack, bit_string(scan->residency));
          }
          break;
    }
    stack = strchr(stack, 0);
    *stack++ = '\n';
      }
   *stack++ = 0;
   pager(p, oldstack, 1);
   stack = oldstack;
}

/* external routine to check updates */

void            check_updates(player * p, char *str)
{
   char           *oldstack;
   int             i;
   oldstack = stack;
   strcpy(stack, "abcdefghijklmnopqrstuvwxyz\n");
   stack = strchr(stack, 0);
   for (i = 0; i < 26; i++)
      if (update[i])
    *stack++ = '*';
      else
    *stack++ = ' ';
   *stack++ = '\n';
   *stack++ = 0;
   tell_player(p, oldstack);
   stack = oldstack;
}


void            player_flags(player * p)
{
   char           *oldstack, *str;
   oldstack = stack;
   str = stack;
   if (p->residency == NON_RESIDENT)
   {
      log("error", "You've sponged Chris. Tried to player_flags a non-resi");
      return;
   }
   if (!p->saved)
      tell_player(p, " Eeeeeeek ! No saved bits !\n");
   else
   {
      sprintf(oldstack, "\n --\n Last logged in %s from %s.\n",
         convert_time(p->saved->last_on), p->saved->last_host);
      stack = strchr(stack, 0);
      strcpy(stack, " You are ");
      stack = strchr(stack, 0);
      if (p->saved_flags & HIDING)
      {
    strcpy(stack, "in hiding, ");
    stack = strchr(stack, 0);
      }
      if (p->saved_flags & BLOCK_SHOUT)
      {
    strcpy(stack, "ignoring shouts, ");
    stack = strchr(stack, 0);
      }
      if (p->saved_flags & BLOCK_TELLS)
      {
    strcpy(stack, "ignoring tells, ");
    stack = strchr(stack, 0);
      }
      if (p->saved_flags & CONVERSE)
      {
    strcpy(stack, "in converse mode, ");
    stack = strchr(stack, 0);
      }
      if (p->saved_flags & NOPREFIX)
      {
    strcpy(stack, "ignoring prefixes, ");
    stack = strchr(stack, 0);
      }
      if (str = strrchr(oldstack, ','))
      {
    *str++ = '.';
    *str++ = '\n';
    *str++ = 0;
    stack = strchr(stack, 0);
    *stack++;
    tell_player(p, oldstack);
      }
   }
   if (p->saved_flags & NEW_MAIL)
   {
      command_type |= HIGHLIGHT;
      tell_player(p, " You have unread mail\n");
      p->saved_flags &= ~NEW_MAIL;
      p->saved->saved_flags &= ~NEW_MAIL;
      command_type &= ~HIGHLIGHT;
   }
   if (p->residency & SU && sys_flags & CLOSED_TO_NEWBIES)
     {
      command_type |= HIGHLIGHT;
      tell_player(p, " Summink is closed to newbies\n");
      command_type &= ~HIGHLIGHT;
    }       
   tell_player(p, " --\n");
   stack = oldstack;
}

void            player_flags_verbose(player * p, char *str)
{
   char           *oldstack, *wibble, *argh;
   player         *p2;
   oldstack = stack;

   if (*str && (p->residency & SU || p->residency & ADMIN))
   {
      p2 = find_player_absolute_quiet(str);
      if (!p2)
      {
         tell_player(p, " No-one on of that name.\n");
         return;
      }
   } else
      p2 = p;

   if (p2->residency == NON_RESIDENT)
   {
      tell_player(p, " You aren't a resident, so your character won't be "
                     "saved when you log off.\n");
      return;
   }
   strcpy(stack, "\n --\n");
   stack = strchr(stack, 0);
   argh = stack;
   strcpy(stack, " You are ");
   stack = strchr(stack, 0);

   if (p2->saved_flags & HIDING)
   {
      strcpy(stack, "in hiding, ");
      stack = strchr(stack, 0);
   }

   if (p2->saved_flags & BLOCK_SHOUT)
   {
      strcpy(stack, "ignoring shouts, ");
      stack = strchr(stack, 0);
   }

   if (p2->saved_flags & BLOCK_TELLS)
   {
      strcpy(stack, "ignoring tells, ");
      stack = strchr(stack, 0);
   }

   if (p2->saved_flags & CONVERSE)
   {
      strcpy(stack, "in converse mode, ");
      stack = strchr(stack, 0);
   }

   if (p2->saved_flags & NOPREFIX)
   {
      strcpy(stack, "ignoring prefixes, ");
      stack = strchr(stack, 0);
   }

   if (wibble = strrchr(oldstack, ','))
   {
      *wibble++ = '.';
      *wibble++ = '\n';
      *wibble = 0;
   } else
      stack = argh;

   if (p2->saved_flags & PRIVATE_EMAIL)
      strcpy(stack, " Your email is private.\n");
   else
      strcpy(stack, " Your email is public for all to see.\n");
   stack = strchr(stack, 0);

   if (p2->saved_flags & TRANS_TO_HOME)
   {
      strcpy(stack, " You will be taken to your home when you log in.\n");
      stack = strchr(stack, 0);
   } else if (*p2->room_connect)
   {
      sprintf(stack, " You will try to connect to room '%s' when you log"
         " in\n", p->room_connect);
      stack = strchr(stack, 0);
   }

   if (p2->saved_flags & NO_ANONYMOUS)
      strcpy(stack, " You won't receive anonymous mail.\n");
   else
      strcpy(stack, " You are currently able to receive anonymous mail.\n");
   stack = strchr(stack, 0);

   if (p2->saved_flags & IAC_GA_ON)
      strcpy(stack, " Iacga prompting is turned on.\n");
   else
      strcpy(stack, " Iacga prompting is turned off.\n");
   stack = strchr(stack, 0);

   if (p2->saved_flags & NO_PAGER)
      strcpy(stack, " You are not recieving paged output.\n");
   else
      strcpy(stack, " You are recieving paged output.\n");
   stack = strchr(stack, 0);

   if (p2->flags & BLOCK_SU && p->residency & PSU)
   {
      strcpy(stack, " You are ignoring sus.\n");
      stack = strchr(stack, 0);
   }

   /* will they be notified when people enter their rooms (ie have they
      room notify set) */
   if (p2->saved_flags & ROOM_ENTER)
       strcpy(stack, " You will be informed when someone enters one of "
	      "your rooms.\n");
   else
       strcpy(stack, " You will not be informed when someone enters "
	      "one of your rooms.\n");
   stack = strchr(stack, 0);

   strcpy(stack, " --\n");
   stack = end_string(stack);
   tell_player(p, oldstack);
   stack = oldstack;
}


/* Catch SEGV's and BUS's on load of players, hopefully... */

void error_on_load()
{
   bad_player_load = 1;
   longjmp(jmp_env, 0);
   longjmp(jmp_env, 0);
}