pgplus/bin/
pgplus/help_files/
pgplus/port_redirector/
pgplus/src/configure/makefiles/
/*
 * Playground+ - parse.c
 * Command parsing code, timer code and more
 * ---------------------------------------------------------------------------
 */

#include <ctype.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <memory.h>
#ifndef BSDISH
#include <malloc.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include "include/config.h"
#include "include/player.h"
#include "include/fix.h"
#include "include/clist.h"
#include "include/proto.h"


/* varibles */
struct command *last_com;
char *stack_check;
int nsync = 0, synct = 0, sync_counter = 0, note_sync = NOTE_SYNC_TIME;
int account_wobble = 1;
int performance_timer = 0;
struct command *help_list = 0;
file help_file =
{0, 0};
int shutdown_count = -1;
int comm_match = 0;
int news_sync = NEWS_SYNC_INTERVAL;
int reload_count = 5;
int backup_hour;

/* interns */
void spam_warning(player *);
void show_clock(player *);
extern char *sys_time_prompt(int diff);

/* returns the first char of the input buffer */

char *first_char(player * p)
{
  char *scan;
  scan = p->ibuffer;
  while (*scan && isspace(*scan))
    scan++;
  return scan;
}

/* what happens if bad stack detected */

void bad_stack(void)
{
  int missing;
  missing = (int) stack - (int) stack_check;
  if (last_com)
    sprintf(stack_check, "Bad stack in function %s, missing %d bytes",
	    last_com->text, missing);
  else
    sprintf(stack_check, "Bad stack somewhere, missing %d bytes", missing);
  stack = end_string(stack_check);
  log("stack", stack_check);
  stack = stack_check;
}


/* flag changing routines */


/* returns the value of a flag from the flag list */

int get_flag(flag_list * list, char *str)
{
  for (; list->text; list++)
    if (!strcmp(list->text, str))
      return list->change;
  return 0;
}

int get_case_flag(flag_list * list, char *str)
{
  for (; list->text; list++)
    if (!strcasecmp(list->text, str))
      return list->change;
  return 0;
}

char *get_flag_name(flag_list * list, char *str)
{
  for (; list->text; list++)
    if (!strcasecmp(list->text, str))
      return list->text;
  return 0;
}

/* routine to get the next part of an arg */

char *next_space(char *str)
{
  while (*str && *str != ' ')
    str++;
  if (*str == ' ')
  {
    while (*str == ' ')
      str++;
    str--;
  }

  return str;
}



/* view command lists */



int command_prescan(player * p, char *str)
{
  char *oldstack = stack;

  if (!*str || !strcasecmp(str, "?") || !strcasecmp(str, "help"))
  {
    oldstack = stack;
    sprintf(stack, " Format: commands [a-z|all|comm|move|desc|info]\n"
	    "                  [item|sys|");
    stack = strchr(stack, 0);

    if (social_index >= 0)
      stack += sprintf(stack, "soc|");

    stack = strchr(stack, 0);
    if (p->residency & LIST)
    {
      strcpy(stack, "list|");
      stack = strchr(stack, 0);
    }
    if (p->residency & BUILD)
    {
      strcpy(stack, "room|");
      stack = strchr(stack, 0);
    }
    if (p->residency & (PSU | SU))
    {
      strcpy(stack, "su|");
      stack = strchr(stack, 0);
    }
    if (p->residency & (ADC | LOWER_ADMIN | ADMIN))
    {
      strcpy(stack, "ad|");
      stack = strchr(stack, 0);
    }
    strcpy(stack, "misc]\n");
    stack = end_string(stack);
    tell_player(p, oldstack);
    stack = oldstack;
    return -1;
  }
/* ok check for what area was inputted, and return appropriate value. */

  if (!strcasecmp(str, "all"))
    return 0;			/* do all commands =) */
  if (!strcasecmp(str, "comm") || !strcasecmp(str, "talk"))
    return COMMc;
  if (p->residency & LIST && (!strcasecmp(str, "list")))
    return LISTc;
  if (p->residency & BUILD && (!strcasecmp(str, "room")))
    return ROOMc;
  if (!strcasecmp(str, "go") || !strcasecmp(str, "move"))
    return MOVEc;
  if (!strcasecmp(str, "item"))
    return ITEMc;
  if (!strcasecmp(str, "info"))
    return INFOc;
  if (!strcasecmp(str, "sys") || !strcasecmp(str, "toggles"))
    return SYSc;
  if (!strcasecmp(str, "desc") || !strcasecmp(str, "personalize") || !strcasecmp(str, "personalise"))
    return DESCc;
  if ((!strcasecmp(str, "soc") || !strcasecmp(str, "social")) &&
      social_index >= 0)
    return SOCIALc;
  if (!strcasecmp(str, "misc"))
    return MISCc;
  if (p->residency & PSU &&
      (!strcasecmp(str, "su") || !strcasecmp(str, "super")))
    return SUPERc;
  if (p->residency & (LOWER_ADMIN | ADMIN | ADC) &&
      (!strcasecmp(str, "ad") || !strcasecmp(str, "admin")))
    return ADMINc;

  if (config_flags & cfNOSPAM)
  {
    if (p->residency & (LOWER_ADMIN | ADMIN | ADC) &&
	(!strcasecmp(str, "spam")))
      return SPAMc;
  }

  return THE_EVIL_Q;		/* a stupid return yes */
}

void view_commands(player * p, char *str)
{
  struct command *comlist;
  char *oldstack, middle[80];
  char *plyr;
  int s;
  int noc = 0, len = 0, l;
  int choice;
  player *p2 = 0;

  plyr = next_space(str);
  *plyr++ = 0;

  if (*plyr && p->residency & ADMIN)
  {
    p2 = find_player_absolute_quiet(plyr);
    if (!p2)
    {
      tell_player(p, " That player is not logged in right now ...\n");
      return;
    }
  }

  if (strlen(str) == 1)		/* all commands beginning with ... */
  {
    lower_case(str);
    if (!isalpha(*str))
    {
      (void) command_prescan(p, "help");
      return;
    }
    l = *str - 'a';
    if (l < 0 || l > 25)
    {
      (void) command_prescan(p, "help");
      return;
    }
    l++;
    oldstack = stack;
    sprintf(middle, "Commands beginning with '%c'", toupper(*str));
    pstack_mid(middle);
    sprintf(stack, "\n  ");
    stack = strchr(stack, 0);
    for (comlist = coms[l]; comlist->text; comlist++)
    {
      if ((!comlist->level || (p->residency & comlist->level)) &&
	  (!comlist->andlevel || (p->residency & comlist->andlevel)) && !(comlist->section & INVISc))
      {
	if (len + strlen(comlist->text) > 70)
	{
	  sprintf(stack, "\n  ");
	  stack = strchr(stack, 0);
	  len = 2;
	}
	sprintf(stack, "%s ", comlist->text);
	stack = strchr(stack, 0);
	len += (strlen(comlist->text) + 1);	/* +1 for the space */
	noc++;
      }
    }

    sprintf(stack, "\n\n");
    stack = strchr(stack, 0);

    sprintf(middle, "There are %d commands available to you", noc);
    pstack_mid(middle);
    *stack++ = 0;
    pager(p, oldstack);
    stack = oldstack;
    return;
  }

  choice = command_prescan(p, str);

  oldstack = stack;

  switch (choice)
  {
    case -1:
      return;
      break;
    case THE_EVIL_Q:
      tell_player(p, " That area not found. Type commands ? to list valid areas.\n");
      return;
      break;
    case 0:
      pstack_mid("Complete set of commands");
      break;
    case LISTc:
      pstack_mid("List commands");
      break;
    case COMMc:
      pstack_mid("Communication commands");
      break;
    case ROOMc:
      pstack_mid("Room commands");
      break;
    case MOVEc:
      pstack_mid("Movement commands");
      break;
    case ITEMc:
      pstack_mid("Item commands");
      break;
    case INFOc:
      pstack_mid("Info commands");
      break;
    case SYSc:
      pstack_mid("System toggle commands");
      break;
    case DESCc:
      pstack_mid("Personalisation commands");
      break;
    case SOCIALc:
      pstack_mid("Social commands");
      break;
    case MISCc:
      pstack_mid("Misc commands");
      break;
    case SUPERc:
      sprintf(middle, "%s commands", get_config_msg("staff_name"));
      pstack_mid(middle);
      break;
    case ADMINc:
      sprintf(middle, "%s commands", get_config_msg("admin_name"));
      pstack_mid(middle);
      break;
    case SPAMc:		/* leave un cased, i mean how do we get HERE if its not on? */
      pstack_mid("Commands which trigger spam code");
      break;
  }

  if (*plyr && p->residency & ADMIN)
    ADDSTACK("\n [for %s]\n\n", p2->name);
  else
  {
    ADDSTACK("\n");
    p2 = p;
  }

  ADDSTACK("  ");

  for (s = 0; s < 27; s++)
  {
    for (comlist = coms[s]; comlist->text; comlist++)
    {
      if ((!comlist->level || ((p2->residency) & comlist->level)) &&
	  (!comlist->andlevel || ((p2->residency) & comlist->andlevel))
	  && ((!choice || comlist->section & choice) && !(comlist->section & INVISc)))
      {
	if (len + strlen(comlist->text) > 70)
	{
	  ADDSTACK("\n  ");
	  stack = strchr(stack, 0);
	  len = 2;
	}
	ADDSTACK("%s ", comlist->text);
	len += (strlen(comlist->text) + 1);	/* +1 for the space */
	noc++;
      }
    }
  }
  if (choice == SOCIALc)
    noc += list_socials(p);

  ADDSTACK("\n\n");

  if (*plyr && p->residency & ADMIN)
    sprintf(middle, "There are %d commands listed here available to %s", noc, p2->name);
  else
    sprintf(middle, "There are %d commands listed here available to you", noc);
  pstack_mid(middle);
  *stack++ = 0;

  pager(p, oldstack);
  stack = oldstack;


}


void view_sub_commands(player * p, struct command *comlist)
{
  char *oldstack, middle[80];
  int noc = 0, len = 0;

  oldstack = stack;

  pstack_mid("Available sub commands");
  sprintf(stack, "\n");
  stack = strchr(stack, 0);

  for (; comlist->text; comlist++)
    if (((!comlist->level) || ((p->residency) & (comlist->level))) &&
	((!comlist->andlevel) || ((p->residency) & (comlist->andlevel))))
    {
      sprintf(stack, "%s ", comlist->text);
      stack = strchr(stack, 0);
      len += strlen(comlist->text);
      len++;
      if (len > 60)
      {
	sprintf(stack, "\n  ");
	len = 0;
      }
      noc++;
    }
  ADDSTACK("\n\n");

  sprintf(middle, "There are %d commands listed here", noc);
  pstack_mid(middle);
  *stack++ = 0;

  tell_player(p, oldstack);
  stack = oldstack;
}

/* initialise the hash array */

void init_parser()
{
  int i;
  struct command *scan;
  scan = complete_list;
  for (i = 0; i < 27; i++)
  {
    coms[i] = scan;
    while (scan->text)
      scan++;
    scan++;
  }
}

/* see if any commands fit the bill */

#ifndef COM_MATCH
char *do_match(char *str, struct command *com_entry)
{
  char *t;
  for (t = com_entry->text; *t; t++, str++)
    if (tolower(*str) != *t)
      return 0;
  if ((com_entry->space) && (*str) && (!isspace(*str)))
    return 0;
  while (*str && isspace(*str))
    str++;
  return str;
}
#else
char *do_match_old(char *str, struct command *com_entry)
{
  char *t;
  for (t = com_entry->text; *t; t++, str++)
    if (tolower(*str) != *t)
      return 0;
  if ((com_entry->space) && (*str) && (!isspace(*str)))
    return 0;
  while (*str && isspace(*str))
    str++;
  return str;
}

char *do_match(char *str, struct command *com_entry)
{
  char *scan;

  for (scan = com_entry->text; *scan && *str && !isspace(*str); scan++, str++)
    if (tolower(*str) != *scan)
      return 0;

  if ((com_entry->space) && (*str) && (!isspace(*str)))
    return 0;

  if (*scan)
  {
    if (com_entry->section & (NOMATCHc | INVISc))
      return 0;
    com_entry->section |= TAGGEDc;
    comm_match++;
    return 0;
  }

  /* while we are on spaces */
  while (*str && isspace(*str))
    str++;
  return str;
}
#endif

void clear_comlist_tags(struct command *comlist)
{
  if (!comm_match)
    return;
  while (comlist->text)
  {
    comlist->section &= ~TAGGEDc;
    comlist++;
  }
  comm_match = 0;
}

void execute_command(player * p, struct command *com, char *str)
{
  void (*fn) (player *, char *);

  if (str[strlen(str) - 1] != '^')
  {
    last_com = com;
    stack_check = stack;
    sys_flags &= ~(FAILED_COMMAND | PIPE | ROOM_TAG | FRIEND_TAG | OFRIEND_TAG | EVERYONE_TAG);
    command_type &= ~ROOM;
    fn = com->function;
    (*fn) (p, str);
    if (stack != stack_check)
      bad_stack();
  }
  else
    TELLPLAYER(p, " '%s' command cancelled.\n", p->command_used->text);

  sys_flags &= ~(FAILED_COMMAND | PIPE | ROOM_TAG | FRIEND_TAG | OFRIEND_TAG | EVERYONE_TAG);
  command_type = 0;
}


/* execute a function from a sub command list */

void sub_command(player * p, char *str, struct command *comlist)
{
  struct command *comliststart;
  char *oldstack = stack, *rol, *space;

  /* Lowercase string here, line in case the person was rm_capped */
  if (p->system_flags & DECAPPED)
    lower_case(str);

  comliststart = comlist;

  while (comlist->text)
  {
    if (((!comlist->level) || ((p->residency) & (comlist->level))) &&
	((!comlist->andlevel) || ((p->residency) & (comlist->andlevel))))
    {
      rol = do_match(str, comlist);
      if (rol)
      {
	execute_command(p, comlist, rol);
	clear_comlist_tags(comliststart);
	return;
      }
    }
    comlist++;
  }

  if (comm_match)
  {
    if (comm_match == 1)	/* single match */
    {
      for (comlist = comliststart; comlist->text && !(comlist->section & TAGGEDc); comlist++)
	;
      for (space = comlist->text, rol = str; *space && *rol && !isspace(*rol); rol++)
	;
      while (*rol && isspace(*rol))
	rol++;
      comlist->section &= ~TAGGEDc;
      comm_match = 0;
      execute_command(p, comlist, rol);
    }
    else
    {
      strcpy(oldstack, " Multiple sub command matches: ");
      stack = strchr(stack, 0);
      for (comlist = comliststart; comlist->text; comlist++)
	if (comlist->section & TAGGEDc)
	{
	  comlist->section &= ~TAGGEDc;
	  sprintf(stack, "%s ", comlist->text);
	  stack = strchr(stack, 0);
	}
      stack--;
      strcpy(stack, ".\n");
      stack = end_string(stack);
      tell_player(p, oldstack);
      stack = oldstack;
      comm_match = 0;
    }
    return;
  }

  rol = str;
  while (*rol && !isspace(*rol))
    rol++;
  *rol = 0;
  if (p->location == prison)
    sprintf(oldstack, " They don't let you do that kinda thing down here.\n");
  else
#ifndef COM_MATCH
    sprintf(oldstack, " Cannot find sub command '%s'\n", str);
#else
    sprintf(oldstack, " Cannot match sub command '%s'\n", str);
#endif
  stack = end_string(oldstack);
  tell_player(p, oldstack);
  stack = oldstack;
}


/* match commands to the main command lists */

void old_match_commands(player * p, char *str)
{
  struct command *comlist, *comliststart;
  char *rol, *oldstack, *space;
  oldstack = stack;

  while (*str && *str == ' ')
    str++;
  space = strchr(str, 0);
  space--;
  while (*space == ' ')
    *space-- = 0;
  if (!*str)
    return;

  /* Lowercase string here, line in case the person was rm_capped */
  if (p->system_flags & DECAPPED)
    lower_case(str);

  if (isalpha(*str))
    comlist = coms[((int) (tolower(*str)) - (int) 'a' + 1)];
  else
    comlist = coms[0];

  comliststart = comlist;

  while (comlist->text)
  {
    if (((!comlist->level) || ((p->residency) & (comlist->level))) &&
	((!comlist->andlevel) || ((p->residency) & (comlist->andlevel))))
    {
      rol = do_match(str, comlist);
      if (rol)
      {
#ifdef COMMAND_STATS
	if (!(comlist->section & (SUPERc | ADMINc)))
#ifdef ROBOTS
	  if (!(comlist->section & (SUPERc | ADMINc)) && !(p->residency & ROBOT_PRIV))
#endif /* robots */
	    commandUsed(comlist->text);
#endif /* command_stats */

	if (config_flags & cfNOSPAM)
	  if (comlist->section & SPAMc)
	  {
	    if (last_com == comlist)
	      p->antispam += atoi(get_config_msg("spam_repeat"));
	    else
	      p->antispam += atoi(get_config_msg("spam_different"));
	  }

	p->command_used = comlist;

	if (!strncasecmp(rol, "/?", 2) || !strncasecmp(rol, "-h", 2))
	  help(p, p->command_used->text);
	else if (config_flags & cfNOSWEAR)
	{
	  if ((comlist->section & F_SWEARc) ||
	      ((comlist->section & M_SWEARc) &&
	       (!strcmp(p->location->owner->lower_name, SYS_ROOM_OWNER) ||
		!strcmp(p->location->owner->lower_name, "intercom"))))
	    execute_command(p, comlist, filter_rude_words(rol));
	  else
	    execute_command(p, comlist, rol);
	}
	else
	  execute_command(p, comlist, rol);
	clear_comlist_tags(comliststart);
	return;
      }
    }
    comlist++;
  }

  /* check for taggings */
  if (comm_match)
  {
    if (comm_match == 1)
    {
      for (comlist = comliststart; comlist->text && !(comlist->section & TAGGEDc); comlist++)
	;
      for (space = comlist->text, rol = str; *space && *rol && !isspace(*rol); rol++)
	;
      while (*rol && isspace(*rol))
	rol++;
      comlist->section &= ~TAGGEDc;
      comm_match = 0;
#ifdef COMMAND_STATS
      if (!(comlist->section & (SUPERc | ADMINc)))
#ifdef ROBOTS
	if (!(comlist->section & (SUPERc | ADMINc)) && !(p->residency & ROBOT_PRIV))
#endif /* robots */
	  commandUsed(comlist->text);
#endif /* command_stats */
  
        if (config_flags & cfNOSPAM)
          if (comlist->section & SPAMc)
          {
            if (last_com == comlist)
              p->antispam += atoi(get_config_msg("spam_repeat"));
            else
              p->antispam += atoi(get_config_msg("spam_different"));
          }

        p->command_used = comlist;

        if (!strncasecmp(rol, "/?", 2) || !strncasecmp(rol, "-h", 2))
          help(p, p->command_used->text);
        else
      execute_command(p, comlist, rol);

    }
    else
    {
      strcpy(oldstack, " Multiple command matches: ");
      stack = strchr(stack, 0);
      for (comlist = comliststart; comlist->text; comlist++)
	if (comlist->section & TAGGEDc)
	{
	  comlist->section &= ~TAGGEDc;
	  sprintf(stack, "%s ", comlist->text);
	  stack = strchr(stack, 0);
	}
      stack--;
      strcpy(stack, ".\n");
      stack = end_string(stack);
      tell_player(p, oldstack);
      stack = oldstack;
      comm_match = 0;
    }
    return;
  }

  rol = str;
  while (*rol && !isspace(*rol))
    rol++;
  *rol = 0;
#ifndef COM_MATCH
  sprintf(oldstack, " Cannot find command '%s'\n", str);
#else
  sprintf(oldstack, " Cannot match command '%s'\n", str);
#endif
  stack = end_string(oldstack);
  tell_player(p, oldstack);
  stack = oldstack;
}

void match_commands(player * p, char *str)
{

  char *rol, holder[1000];
  alias *al;

  if (!(p->saved))
  {
    old_match_commands(p, str);
    return;
  }
  /* lets try an alias match first */
  rol = do_alias_match(p, str);
  if (strcmp(rol, "\n"))
  {
    al = get_alias(p, str);
    if (!al)
    {
      tell_player(p, " Alias matching error\n");
      return;
    }
    /* NEED TO LOOP THROUGH HERE */
    strcpy(holder, splice_argument(p, al->sub, rol, 0));
    while (holder[0])
    {
      if (p->system_flags & DECAPPED)
	lower_case(holder);
      old_match_commands(p, holder);
      strcpy(holder, splice_argument(p, al->sub, rol, 1));
    }
  }
  else
    old_match_commands(p, str);
}

/* handle input from one player */

void input_for_one(player * p)
{
  char *pick;

  masks_reset();
  this_rand = (player *) NULL;	/* dynatext random player reset */
  if (p->input_to_fn)
  {
    p->idle = 0;
    p->idle_index = 0;
    p->idle_msg[0] = 0;
    last_com = &input_to;
    stack_check = stack;
    sys_flags &= ~(FAILED_COMMAND | PIPE | ROOM_TAG | FRIEND_TAG | OFRIEND_TAG | EVERYONE_TAG);
    command_type &= ~ROOM;
    (*p->input_to_fn) (p, p->ibuffer);
    if (stack != stack_check)
      bad_stack();
    sys_flags &= ~(FAILED_COMMAND | PIPE | ROOM_TAG | FRIEND_TAG | OFRIEND_TAG | EVERYONE_TAG);
    command_type = 0;
    return;
  }
  if (!p->ibuffer[0])
    return;
  p->idle = 0;
  p->idle_index = 0;
  p->idle_msg[0] = 0;
  action = "doing command";
  if (p->ibuffer[0] != '#')
  {
    if (p->custom_flags & CONVERSE)
    {
      pick = p->ibuffer;
      while (*pick && isspace(*pick))
	pick++;
      if (*pick)
      {
	if (*pick == '/' || *pick == '.')
	  if (current_room == prison && !(p->residency & (ADMIN | SU)))
	    sub_command(p, pick + 1, restricted_list);
	  else
	  {
	    if (!match_social(p, pick + 1))
	      match_commands(p, pick + 1);
	  }
	else if (!isalpha(*pick))
	  if (current_room == prison && !(p->residency & (ADMIN | SU)))
	    sub_command(p, pick, restricted_list);
	  else
	  {
	    if (!match_social(p, pick))
	      match_commands(p, pick);
	  }
	else
        {
          if (config_flags & cfNOSWEAR &&
             (!strcmp(p->location->owner->lower_name, SYS_ROOM_OWNER) ||
              !strcmp(p->location->owner->lower_name, "intercom")))
            pick = filter_rude_words(pick);
	  say(p, pick);
        }
      }
    }
    else if (current_room == prison && !(p->residency & (ADMIN | SU)))
      sub_command(p, p->ibuffer, restricted_list);
    else
    {
      if (!match_social(p, p->ibuffer))
	match_commands(p, p->ibuffer);
    }
  }
}

void su_quit_log(player * p)
{

  char *oldstack;
  int csu;

  csu = true_count_su();
  oldstack = stack;
  sprintf(stack, "%s leaves -- %d sus left", p->name, csu - 1);
  stack = end_string(stack);
  log("su", oldstack);
  stack = oldstack;
}

/* scan through the players and see if anything needs doing */

void process_players()
{
  player *scan, *sparky;
  char *oldstack, *hasta, thetime[10];
  FILE *fp;

  if (current_players > max_ppl_on_so_far)
  {
    max_ppl_on_so_far = current_players;
    max_ppl_on_so_far_time = time(0);
    if (max_ppl_on_so_far > max_ppl_ever_so_far)
    {
      oldstack = stack;
      max_ppl_ever_so_far = max_ppl_on_so_far;
      max_ppl_ever_so_far_time = max_ppl_on_so_far_time;
      ADDSTACK("max_ppl: %d\nmax_ppl_time: %d",
               max_ppl_ever_so_far, (int) max_ppl_ever_so_far_time);
      ENDSTACK("\n");
      fp = fopen("files/stats_info", "w");
      if (!fp)
        LOGF("error", "Couldn't open stats_info file in process_players");
      else
      {
        fprintf(fp, oldstack);
        fclose(fp);
      }
      stack = oldstack;
    }
  }

  for (scan = flatlist_start; scan; scan = sparky)
  {
    sparky = scan->flat_next;
    if (scan->flat_next)
      if (((player *) scan->flat_next)->flat_previous != scan)
      {
	raw_wall("\n\n   -=> Non-terminated flatlist <=-\n\n");
	raw_wall("\n\n   -=> Dumping end off of list <=-\n\n");
	scan->flat_next = NULL;
      }
#ifdef ROBOTS
    if ((scan->fd < 0 && !(scan->flags & ROBOT)) || (scan->flags & PANIC) || (scan->flags & CHUCKOUT))
#else
    if ((scan->fd < 0) || (scan->flags & PANIC) || (scan->flags & CHUCKOUT))
#endif
    {

      oldstack = stack;
      current_player = scan;

      if (scan->location && scan->name[0] && !(scan->flags & RECONNECTION))
      {
#ifdef LAST
	stampLogout(scan->name);
#endif
	if (scan->residency & SU && true_count_su() <= 1)
	  su_quit_log(scan);

	hasta = do_alias_match(scan, "_logoff");
	if (strcmp(hasta, "\n"))
	{
	  match_commands(scan, "_logoff");
	}
	if (scan == p_sess)
	{
	  session_reset = 0;
	}
	if (strlen(scan->logoffmsg) < 1)
	{
	  stack += sprintf(stack, "%s %s ",
			   get_config_msg("logoff_prefix"), scan->name);
	  stack += sprintf(stack, "%s ", get_config_msg("def_logout"));
	  stack += sprintf(stack, "^N%s\n", get_config_msg("logoff_suffix"));
	}
	else
	{
	  if (emote_no_break(*scan->logoffmsg))
	  {
	    stack += sprintf(stack, "%s %s%s ", get_config_msg("logoff_prefix"),
			     scan->name, scan->logoffmsg);
	    stack += sprintf(stack, "^N%s\n", get_config_msg("logoff_suffix"));
	  }
	  else
	  {
	    stack += sprintf(stack, "%s %s %s ", get_config_msg("logoff_prefix"),
			     scan->name, scan->logoffmsg);
	    stack += sprintf(stack, "^N%s\n", get_config_msg("logoff_suffix"));
	  }
	}

	stack = end_string(stack);
	command_type |= LOGOUT_TAG;
	tell_room(scan->location, oldstack);
	command_type &= ~LOGOUT_TAG;
	stack = oldstack;
	save_player(scan);
	command_type = 0;
	do_inform(scan, get_plists_msg("inform_logout"));
	if (scan->saved && !(scan->flags & NO_SAVE_LAST_ON))
	  scan->saved->last_on = time(0);
      }
      if (sys_flags & VERBOSE || scan->residency == 0)
      {
	/* Ignore "advanced" talker listings which spam the newconn log */
        if (!unlogged_site(scan->inet_addr) && !unlogged_site(scan->num_addr))
	{
	  if (scan->name[0])
	  {
	    if (scan->newbieinform)
	      SUWALL(" -=*> '%s' has disconnected (non-resident)\n", scan->name);
	    LOGF("newconn", "%s disconnects from %s", scan->name,
		 get_address(scan, NULL));
	  }
	  else
	    LOGF("nonconn", "%s disconnects from login", scan->inet_addr);
	}
      }
      destroy_player(scan);
      current_player = 0;
      stack = oldstack;
    }
    else if (scan->flags & INPUT_READY)
    {
      if (!(scan->lagged) && !(scan->system_flags & SAVE_LAGGED))
      {
	current_player = scan;
	current_room = scan->location;
	input_for_one(scan);
	action = "processing players";
	current_player = 0;
	current_room = 0;
	sys_color_atm = SYSsc;
      }
      if (scan->flags & PROMPT)
      {
	if (scan->custom_flags & CONVERSE)
	  do_prompt(scan, scan->converse_prompt);
	else if ((scan->system_flags & TIMEPROMPT) && !(scan->custom_flags & CONVERSE))
	{
	  sprintf(thetime, "[%s]> ", sys_time_prompt(scan->jetlag));
	  do_prompt(scan, thetime);
	}
	else if (!(scan->system_flags & TIMEPROMPT))
	  do_prompt(scan, scan->prompt);
      }
      memset(scan->ibuffer, 0, IBUFFER_LENGTH);
      scan->flags &= ~INPUT_READY;
    }
  }
}




/* timer things */


/* automessages */

void do_automessage(room * r)
{
  int count = 0, type;
  char *scan, *oldstack;
  oldstack = stack;
  scan = r->automessage.where;
  if (!scan)
  {
    r->flags &= ~AUTO_MESSAGE;
    return;
  }
  for (; *scan; scan++)
    if (*scan == '\n')
      count++;
  if (!count)
  {
    FREE(r->automessage.where);
    r->automessage.where = 0;
    r->automessage.length = 0;
    r->flags &= ~AUTO_MESSAGE;
    stack = oldstack;
    return;
  }
  count = rand() % count;
  for (scan = r->automessage.where; count; count--, scan++)
    while (*scan != '\n')
      scan++;
  while (*scan != '\n')
    *stack++ = *scan++;
  *stack++ = '\n';
  *stack++ = 0;
  type = command_type;
  command_type = AUTO;
  tell_room(r, oldstack);
  command_type = type;
  r->auto_count = r->auto_base + (rand() & 63);
  stack = oldstack;
}


/* file syncing */

void do_sync(void)
{
  int origin;
  action = "doing sync";
  sync_counter = SYNC_TIME;
  origin = synct;
  while (!update[synct])
  {
    synct = (synct + 1) % 26;
    if (synct == origin)
      break;
  }
  if (update[synct])
  {
    sync_to_file(synct + 'a', 0);
    synct = (synct + 1) % 26;
  }
}

/* this is the actual timer pling */

void actual_timer(int c)
{
  static int pling = TIMER_CLICK;
  player *scan;
  time_t t;
  int timeon;

  if (sys_flags & PANIC)
    return;

#if !defined(hpux) && !defined(linux)
  if ((int) signal(SIGALRM, actual_timer) < 0)
    handle_error("Can't set timer signal.");
#endif /* hpux */

  t = time(0);
  if ((splat_timeout - t) <= 0)
    splat1 = splat2 = 0;
  pling--;
  if (pling)
    return;

  pling = TIMER_CLICK;

  sys_flags |= DO_TIMER;

  /* if (mem_use_log_count > 0)
     mem_use_log_count--; */
  if (shutdown_count > 0)
    shutdown_count--;

#ifdef ROBOTS
  process_robot_counters();
#endif

#ifdef ALLOW_MULTIS
  update_multis();
#endif
  this_rand = (player *) NULL;	/* dynatext random player reset, here too, 
				   in case dynatext is not run from a command */

  for (scan = flatlist_start; scan; scan = scan->flat_next)
    if (!(scan->flags & PANIC))
    {
      if (scan->next_ping == 0)
      {
	scan->next_ping = -1;
	ping_timed(scan);
      }
      if (scan->next_ping > 0)
	scan->next_ping--;

      scan->idle++;
      scan->idle_index++;
      scan->total_login++;
      if (scan->total_login % ONE_HOUR == 0)
	scan->pennies += 10;
      if (scan->pennies > 100000)
	scan->pennies = 100000;
      if (scan->residency && !(scan->residency & NO_SYNC) && scan->total_login % 1200 == 800)
	save_player(scan);
      if (scan->location && !strcmp(scan->location->owner->lower_name, SYS_ROOM_OWNER))
	scan->time_in_main++;
      if (scan->script && scan->script > 1)
	scan->script--;
      if (scan->timer_fn && scan->timer_count > 0)
	scan->timer_count--;
      if (scan->no_shout > 0)
	scan->no_shout--;
      if (scan->no_move > 0 && !(scan->system_flags & SAVED_RM_MOVE))
	scan->no_move--;
      if (scan->lagged > 0)
	scan->lagged--;
      if (scan->shout_index > 0)
	scan->shout_index--;
      if (scan->social_index)
	scan->social_index--;

/* for sing */
      if (scan->no_sing > 0)
	scan->no_sing--;

/* for clock */
      if (time(0) % ONE_HOUR == 0 && !(scan->custom_flags & NO_CLOCK))
	show_clock(scan);

/* For max login time */
      timeon = time(0) - scan->on_since;
#ifdef ROBOTS
      if (scan->location && timeon > longest_time && !(scan->residency & ROBOT_PRIV))
#else
      if (scan->location && timeon > longest_time)
#endif
      {
	longest_time = timeon;
	strncpy(longest_player, scan->name, 20);
      }
/* For slots */
      if (pot <= 150)
	pot = atoi(get_config_msg("initial_pot"));

/* For residency things */
      if (scan->awaiting_residency == 1)
	make_resident(scan);
      else if (scan->awaiting_residency > 0)
      {
	scan->awaiting_residency--;
	if (scan->awaiting_residency == 31 || scan->awaiting_residency == 16)
	{
	  TELLPLAYER(scan, " -=*> You have %d seconds left before you become a resident.\n", scan->awaiting_residency - 1);
	  SUWALL(" -=*> %s will be granted residency in %d seconds ...\n", scan->name, scan->awaiting_residency - 1);
	}
      }
/* for asty's idle thang */
      if (scan->idle_index > 300 && !(scan->residency & ADMIN))
      {
	scan->total_idle_time += (290 + (rand() % 40));
	scan->idle_index = 0;
      }
      if (scan->jail_timeout > 0)
	scan->jail_timeout--;
      /* unidle admins */
      if (config_flags & cfADMINIDLE)
	if (scan->idle > 3000 && scan->residency & ADMIN)
	  scan->idle = 0;

      if (config_flags & cfNOSPAM)
      {
	/* Anti-spam code */
	scan->antispam -= 7;
	if (scan->antispam < 0)
	  scan->antispam = 0;
	if (scan->antispam > 50)
	  spam_warning(scan);
      }

      /* Channel stuff */
      if (strlen(scan->zchannel) > 0)
      {
	scan->zcidle++;
	if (scan->zcidle > ONE_HOUR)
	{
	  if (scan->misc_flags & CHAN_HI)
	    command_type |= HIGHLIGHT;
	  sys_color_atm = UCEsc;
	  TELLPLAYER(scan, " <(%s)> -- This channel is too idle and has been destroyed\n", scan->zchannel);
	  sys_color_atm = SYSsc;
	  if (scan->misc_flags & CHAN_HI)
	    command_type &= ~HIGHLIGHT;
	  strcpy(scan->zchannel, "");
	  scan->zcidle = 0;
	}
      }
      /* timing out REALLY idle gits... */
      if (config_flags & cfIDLEBAD)
      {
	if (scan->idle == 3000 || scan->idle == 3300 ||
	 (!(scan->residency) && (scan->idle == 1500 || scan->idle == 1620)))
	{
	  TELLPLAYER(scan, " -=*> Warning - you are now %d minutes idle.\007\n",
		     scan->idle / ONE_MINUTE);
	}
	if (scan->idle == 3540 || (!(scan->residency) && scan->idle == 1740))
	{
	  TELLPLAYER(scan, " -=*> You're %d minutes idle. 1 minute till auto-disconnect.\007\n",
		     scan->idle / ONE_MINUTE);
	}
	if (scan->idle >= ONE_HOUR || (!(scan->residency) && scan->idle >= (ONE_HOUR / 2)))
	{
	  TELLPLAYER(scan, "\n\n"
		     LINE
		     "\nThank you for visiting %s.\n\n"
		     "Next time though, when you decide to leave, you ought to quit for yourself.\n\n"
		     LINE
		     "\n\n", scan->name);
	  scan->idled_out_count++;
	  log("idle", scan->lower_name);
	  quit(scan, 0);
	}
      }
    }
  net_count--;
  if (!net_count)
  {
    net_count = 10;
    in_total += in_current;
    out_total += out_current;
    in_pack_total += in_pack_current;
    out_pack_total += out_pack_current;
    in_bps = in_current / 10;
    out_bps = out_current / 10;
    in_pps = in_pack_current / 10;
    out_pps = out_pack_current / 10;
    in_average = (in_average + in_bps) >> 1;
    out_average = (out_average + out_bps) >> 1;
    in_pack_average = (in_pack_average + in_pps) >> 1;
    out_pack_average = (out_pack_average + out_pps) >> 1;
    in_current = 0;
    out_current = 0;
    in_pack_current = 0;
    out_pack_current = 0;
  }
}

/* If they've been naughty - then they get kicked off. Heh heh heh --Silver */

void spam_warning(player * p)
{
  int no1, no2, no3, no4, gon;

  gon = atoi(get_config_msg("spam_eject"));
  TELLPLAYER(p, "\n\n"
	     LINE
  "\nYou have triggered the anti-spam code. As well as being pointless and\n"
  "irritating, spamming is also unnecessary and annoys other residents.\n\n"
    "You have been prevented from logging in for the next %d minute(s).\n\n"
	     LINE
	     "\n\n\007", gon);
  LOGF("spam", "%s is ejected for %d min(s) for spamming", p->name, gon);
  SW_BUT(p, " -=*> %s is ejected for %d min(s) for spamming\n", p->name, gon);
  p->sneezed = time(0) + (gon * 60);
  soft_timeout = time(0) + (gon * 60);
  sscanf(p->num_addr, "%d.%d.%d.%d", &no1, &no2, &no3, &no4);
  soft_splat1 = no1;
  soft_splat2 = no2;
  quit(p, "");
}



/* the timer function */

void timer_function()
{
  player *scan, *old_current;
  room *r, **list;
  char *oldstack;
  int count = 0, pcount = 0;
  char *action_cpy;
  struct tm *ts;
  time_t t;

#if !defined(linux)
#ifndef BSDISH
  struct mallinfo minfo;
#endif
#else
  /* struct mstats memstats; */
#endif /* LINUX */

#ifdef AUTOSHUTDOWN
  int nump = 0;
#endif

  if (!(sys_flags & DO_TIMER))
    return;
  sys_flags &= ~DO_TIMER;

  waitpid((pid_t) - 1, (int *) 0, WNOHANG);
  /* wait3(0,WNOHANG,0); */

  old_current = current_player;
  action_cpy = action;

  oldstack = stack;
  if (shutdown_count > -1)
  {
    command_type |= HIGHLIGHT;
    switch (shutdown_count)
    {
      case ONE_YEAR:
	raw_wall("\n\n -=*>           Your attention please.           <*=-\n"
		 " -=*>   We'll be rebooting in exactly one year   <*=-\n"
	       " -=*> Anyone still here at that time needs help! <*=-\n\n");
	break;
      case ONE_DAY:
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("1day"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case (15 * ONE_MINUTE):
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("15mins"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case (10 * ONE_MINUTE):
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("10mins"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case (5 * ONE_MINUTE):
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("5mins"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case (3 * ONE_MINUTE):
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("3mins"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case (2 * ONE_MINUTE):
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("2mins"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case ONE_MINUTE:
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("1min"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 45:
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("45secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 30:
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("30secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 15:
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("15secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 10:
	sprintf(stack, "\n %s\n\n", get_shutdowns_msg("10secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 9:
	sprintf(stack, "\n %s\n", get_shutdowns_msg("9secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 8:
	sprintf(stack, "\n %s\n", get_shutdowns_msg("8secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 7:
	sprintf(stack, "\n %s\n", get_shutdowns_msg("7secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 6:
	sprintf(stack, "\n %s\n", get_shutdowns_msg("6secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 5:
	sprintf(stack, "\n %s\n", get_shutdowns_msg("5secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 4:
	sprintf(stack, "\n %s\n", get_shutdowns_msg("4secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 3:
	sprintf(stack, "\n %s\n", get_shutdowns_msg("3secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 2:
	sprintf(stack, "\n %s\n", get_shutdowns_msg("2secs"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 1:
	sprintf(stack, "\n %s\n", get_shutdowns_msg("1sec"));
	stack = end_string(stack);
	raw_wall(oldstack);
	stack = oldstack;
	break;
      case 0:
	log("shutdown", shutdown_reason);
	sys_flags |= SHUTDOWN;
	stack = oldstack;
	return;
    }
    command_type &= ~HIGHLIGHT;
  }
  if (sync_counter)
    sync_counter--;
  else
    do_sync();

  if (note_sync)
    note_sync--;
  else
  {
    note_sync = NOTE_SYNC_TIME;
    sync_notes(1);
  }
  scan_news();

  if (news_sync)
    news_sync--;
  else
  {
    news_sync = NEWS_SYNC_INTERVAL;
    sync_all_news_headers();
  }

  if (reload_count)
    reload_count--;
  else
  {
    reload_count = 5;
    init_messages();
  }

  align(stack);
  list = (room **) stack;

  for (scan = flatlist_start; scan; scan = scan->flat_next)
  {
    if (!(scan->flags & PANIC))
    {
      if (scan->script && scan->script == 1)
	end_emergency(scan);

      if (scan->timer_fn && !scan->timer_count)
      {
	current_player = scan;
        sys_flags &= ~(FAILED_COMMAND | PIPE | ROOM_TAG | FRIEND_TAG | OFRIEND_TAG | EVERYONE_TAG);
	command_type &= ~ROOM;
	(*scan->timer_fn) (scan);
	scan->timer_fn = 0;
	scan->timer_count = -1;
      }
      current_player = old_current;
      action = "processing autos";
      r = scan->location;
      if (r)
      {
	pcount++;
	if (r->flags & AUTO_MESSAGE && !(r->flags & AUTOS_TAG))
	{
	  if (!r->auto_count)
	    do_automessage(r);
	  else
	    r->auto_count--;
	  *(room **) stack = r;
	  stack += sizeof(room *);
	  count++;
	  r->flags |= AUTOS_TAG;
	}
      }
/* Jail timeout thang */
      if (scan->jail_timeout == 0 && scan->location == prison)
      {
	command_type |= HIGHLIGHT;
	tell_player(scan, " After serving your sentence you are flung out"
		    " to society again.\n");
	command_type &= ~HIGHLIGHT;
	move_to(scan, sys_room_id(ENTRANCE_ROOM), 0);
      }
    }
  }
  for (; count; count--, list++)
    (*list)->flags &= ~AUTOS_TAG;
  stack = oldstack;
  action = action_cpy;
  current_players = pcount;

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

  if ((backup_hour != -1) && (ts->tm_hour == backup_hour) &&
      !(ts->tm_min) && !(ts->tm_sec))
    do_backup(0, 0);

#ifdef AUTOSHUTDOWN
  if (auto_sd == 1)
  {
    nump = 0;
    for (scan = flatlist_start; scan; scan = scan->flat_next)
#ifdef ROBOTS
      if (!(scan->residency & ROBOT_PRIV))
#endif
	nump++;

    if (nump == 0)
    {
      LOGF("shutdown", "Auto-Shutdown sequence started");
      close_down();
    }
  }
#endif

#ifdef SEAMLESS_REBOOT
  if (awaiting_reboot)
  {
    awaiting_reboot = 0;
    for (scan = flatlist_start; scan; scan = scan->flat_next)
      if (scan->location && (scan->flags & IN_EDITOR || scan->mode & (MAILEDIT | ROOMEDIT | NEWSEDIT)))
	awaiting_reboot = 1;
    if (!awaiting_reboot)
      do_reboot();
  }
#endif

}

/* the help system (aargh argh argh) */


/* look through all possible places to find a bit of help */

struct command *find_help(char *str)
{
  struct command *comlist;
  if (isalpha(*str))
    comlist = coms[((int) (tolower(*str)) - (int) 'a' + 1)];
  else
    comlist = coms[0];

  for (; comlist->text; comlist++)
#ifdef COM_MATCH
    if (do_match_old(str, comlist))
#else
    if (do_match(str, comlist))
#endif
      return comlist;
  comlist = help_list;
  if (!comlist)
    return 0;
  for (; comlist->text; comlist++)
#ifdef COM_MATCH
    if (do_match_old(str, comlist))
#else
    if (do_match(str, comlist))
#endif
      return comlist;

  return 0;
}


void next_line(file * hf)
{
  while (hf->length > 0 && *(hf->where) != '\n')
  {
    hf->where++;
    hf->length--;
  }
  if (hf->length > 0)
  {
    hf->where++;
    hf->length--;
  }
}

void init_help()
{
  file hf;
  struct command *found, *hstart;
  char *oldstack, *start, *scan;
  int length;
  oldstack = stack;


  if (sys_flags & VERBOSE)
    log("boot", "Loading help pages");


  if (help_list)
    FREE(help_list);
  help_list = 0;

  if (help_file.where)
    FREE(help_file.where);
  help_file = load_file("doc/help");
  hf = help_file;

  align(stack);
  hstart = (struct command *) stack;

  while (hf.length > 0)
  {
    while (hf.length > 0 && *(hf.where) != ':')
      next_line(&hf);
    if (hf.length > 0)
    {
      scan = hf.where;
      next_line(&hf);
      *scan++ = 0;
      while (scan != hf.where)
      {
	start = scan;
	while (*scan != ',' && *scan != '\n')
	  scan++;
	*scan++ = 0;
	found = find_help(start);
	if (!found)
	{
	  found = (struct command *) stack;
	  stack += sizeof(struct command);
	  found->text = start;
	  found->function = 0;
	  found->level = 0;
	  found->andlevel = 0;
	  found->space = 1;
	  found->section = 0;
	}
	found->help = hf.where;
      }
    }
  }
  *(hf.where - 1) = 0;
  found = (struct command *) stack;
  stack += sizeof(struct command);
  found->text = 0;
  found->function = 0;
  found->level = 0;
  found->andlevel = 0;
  found->space = 0;
  found->help = 0;
  found->section = 0;
  length = (int) stack - (int) hstart;
  help_list = (struct command *) MALLOC(length);
  memcpy(help_list, hstart, length);
  stack = oldstack;
}


/* load that help file in  */

int get_help(player * p, char *str)
{
  int fail = 0;
  file text;
  char *oldstack = stack;

  char header[75];

  if (*str == '.')
  {
    stack = oldstack;
    return 0;
  }

  sprintf(stack, "doc/%s.help", str);
  stack = end_string(stack);
  text = load_file_verbose(oldstack, 0);
  if (text.where)
  {
    if (*(text.where))
    {
      stack = oldstack;
      snprintf(header, 70, "%s Online Help: %s",
	       get_config_msg("talker_name"), str);
      pstack_mid(header);

      sprintf(stack, "\n%s"
	      LINE, text.where);
      stack = end_string(stack);
      pager(p, oldstack);
      fail = 1;
    }
    else
      fail = 0;
    FREE(text.where);
  }
  stack = oldstack;
  return fail;
}



int get_victim(player * p, char *text)
{
  return 0;
}


/* the help command */

void help(player * p, char *str)
{
  char *oldstack, header[75];
  struct command *fn, *comlist;
  char command[100] = "";
  int i = 0;

  oldstack = stack;

  if (!*str)
  {
    if (p->residency)
      str = "general";
    else
      str = "newbie";
  }
  /* so ressies can see "help su" */
  if (!strcasecmp(str, "su"))
  {
    str = "superuser";
  }
  if (isalpha(*str))
    comlist = coms[((int) (tolower(*str)) - (int) 'a' + 1)];
  else
    comlist = coms[0];

  /* check they aren't exploiting a pg96 bug to get su/admin help files */

  for (i = 0; i < strlen(str); i++)
  {
    if (str[i] == ' ')
    {
      str[i] = 0;
      break;
    }
  }

  strncpy(command, str, 95);

  /* Here it is - check person's privs before helping them =) */
  for (; comlist->text; comlist++)
    if (!strcmp(comlist->text, str))
      if ((!(p->residency & comlist->level)) && comlist->level != 0)
	str = " ";
  fn = find_help(str);
  if (!fn || !(fn->help))
  {
    if (get_help(p, str))
      return;
    stack = oldstack;
    TELLPLAYER(p, " Cannot find any help on '%s'. \n", command);
    LOGF("help", "%s requested help on '%s'", p->name, command);
    return;
  }
  if (!strcasecmp(str, "newbie"))
    if (get_victim(p, fn->help))
    {
      stack = oldstack;
      return;
    }
  snprintf(header, 70, "%s Online Help: %s",
	   get_config_msg("talker_name"), command);
  pstack_mid(header);

  sprintf(stack, "%s"
	  LINE, fn->help);
  stack = end_string(stack);

  pager(p, oldstack);
  stack = oldstack;
}


void forcehelp(player * p, char *str)
{
  player *p2;
  char *temp;

  temp = next_space(str);
  *temp++ = 0;

  if (!*str)
  {
    tell_player(p, " Format: forcehelp <player> <help file>\n");
    return;
  }
  p2 = find_player_global(str);
  if (!p2)
    return;

  if (p2 == p)
  {
    tell_player(p, " You don't need to forcehelp yourself something!\n");
    return;
  }

  tell_player(p2, " -=> I think you need to read this ... \n");
  help(p2, temp);

  SUWALL(" -=*> %s shows help '%s' to %s\n", p->name, temp, p2->name);
  LOGF("forcehelp", "%s forcehelpped '%s' to %s.", p->name, temp, p2->name);
}

/* Credit goes to Renegade for spotting a bug with this code. The original
   PG one didn't check to see if the SU/Minister/etc was actually a person
   on the talker (indeed a person at all). Must amusement came from typing the
   command "push <name> a large cliff" or "push <name> a fluffy sheep" */

void redtape(player * p, char *str)
{
  player *p2, *p3;
  char *temp, *oldstack;

  temp = next_space(str);
  *temp++ = 0;

  oldstack = stack;
  if ((!*str) || (!*temp))
  {
    tell_player(p, " Format: redtape <git> <SuperUser/Minister/etc>\n");
    return;
  }
  p2 = find_player_global(str);
  if (!p2)
    return;

  p3 = find_player_global(temp);
  if (!p3)
    return;

  if (p2 == p3)
  {
    tell_player(p, "Are you sure about that??\n");
    return;
  }
  sprintf(stack, "\n -=*> %s may be able to help you better. \n\n", p3->name);
  stack = end_string(oldstack);
  command_type |= HIGHLIGHT;
  tell_player(p2, oldstack);
  command_type &= ~HIGHLIGHT;
  stack = oldstack;

  sprintf(stack, " -=*> %s pushes %s towards %s\n", p->name, p2->name, p3->name);
  stack = end_string(oldstack);
  su_wall(oldstack);
  stack = oldstack;
}


/* Show the clock - okay so this is one poxy little procedure but I
   like it this way, so there. --Silver
   (function seperation is a good thing, it leads to easily
   readable and modifiable code *grin* ~phy) */

void show_clock(player * p)
{
  char *oldstack = stack;
  time_t t;
  static char buf[80];

  if (p->jetlag)
    t = time(0) + (ONE_HOUR * p->jetlag);
  else
    t = time(0);
  strftime(buf, 79, "%I:%M %p", localtime(&t));

  sprintf(stack, get_config_msg("hourly_clock"), buf);
  stack = end_string(stack);
  TELLPLAYER(p, " %s\007\n", oldstack);
  stack = oldstack;
}


/* Specilised match code becuase strnomatch is no good */

int swear_match(char *str1, char *str2)
{

  char *s1p, *s2p;

  s1p = str1;
  s2p = str2;

  for (; *s1p; s1p++, s2p++)
  {
    if (tolower(*s1p) != tolower(*s2p))
      return 1;
  }
  return 0;
}


/* Swear words removal code :o) 
   A list of rude words can be found in admin.h - gasp! */

char rand_curse_char(void)
{
  srand(rand());
  return (char) (rand() % 6 + 33);
}


char *filter_rude_words(char *str)
{
  char *ptr = str, *rude;

  while ((rude = strcaseline(ptr, rude_msg.where)))
    while (*rude && isalpha(*rude))
      *rude++ = rand_curse_char();

  return str;
}

void swear_version(void)
{
  sprintf(stack, " -=*> Swear filtering v1.0 (by Silver) enabled.\n");
  stack = strchr(stack, 0);
}