/*
 * mail.c
 */

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

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

/* externs */


extern char    *first_char(), *next_space();
extern char    *store_string(), *store_int(), *store_word();
extern char    *get_string(), *get_int(), *get_word(), *end_string();
extern saved_player *find_saved_player();
extern struct command mail_list[], news_list[];
extern player  *find_player_global(), *find_player_absolute_quiet();
extern void     pager(player *, char *, int);
extern void     lower_case(char *);

/* interns */

note           *n_hash[NOTE_HASH_SIZE];
int             nhash_update[NOTE_HASH_SIZE];
int             unique = 1;
int             news_start = 0, news_count = 0;

void            unlink_mail();

/* find note in hash */

note           *find_note(int id)
{
   note           *scan;
   if (!id)
      return 0;
   for (scan = n_hash[id % NOTE_HASH_SIZE]; scan; scan = scan->hash_next)
      if (scan->id == id)
    return scan;
   return 0;
}

/* create a new note */

note           *create_note()
{
   note           *n;
   int             num;
   n = (note *) MALLOC(sizeof(note));
   memset(n, 0, sizeof(note));
   n->flags = NOT_READY;
   n->date = time(0);
   n->next_sent = 0;
   n->text.where = 0;
   n->text.length = 0;
   strcpy(n->header, "DEFUNCT");
   strcpy(n->name, "system");
   n->id = unique++;
   unique &= 65535;
   num = (n->id) % NOTE_HASH_SIZE;
   n->hash_next = n_hash[num];
   n_hash[num] = n;
   nhash_update[num] = 1;
   return n;
}

/* remove a note */

void            remove_note(note * n)
{
   note           *scan, *prev;
   int             num;
   if (n->text.where)
      FREE(n->text.where);
   num = (n->id) % NOTE_HASH_SIZE;
   scan = n_hash[num];
   if (scan == n)
      n_hash[num] = n->hash_next;
   else
   {
      do
      {
    prev = scan;
    scan = scan->hash_next;
      } while (scan != n);
      prev->hash_next = n->hash_next;
   }
   FREE(n);
   nhash_update[num] = 1;
}

/* remove various types of note */

void            remove_any_note(note * n)
{
   saved_player   *sp;
   note           *scan = 0;
   char           *oldstack;
   int            *change;
   oldstack = stack;
   if (n->flags & NEWS_ARTICLE)
   {
      scan = find_note(news_start);
      change = &news_start;
      while (scan && scan != n)
      {
    change = &(scan->next_sent);
    scan = find_note(scan->next_sent);
      }
      if (scan == n)
    news_count--;
   } else
   {
      strcpy(stack, n->name);
      lower_case(stack);
      stack = end_string(stack);
      sp = find_saved_player(oldstack);
      if (!sp)
      {
    log("error", "Bad owner name in mail(1)");
    log("error", oldstack);
    unlink_mail(find_note(n->next_sent));
    scan = 0;
      } else
      {
    change = &(sp->mail_sent);
    scan = find_note(sp->mail_sent);
    while (scan && scan != n)
    {
       change = &(scan->next_sent);
       scan = find_note(scan->next_sent);
    }
      }
   }
   if (scan == n)
      (*change) = n->next_sent;
   remove_note(n);
   stack = oldstack;
}



/* dump one note onto the stack */

int             save_note(note * d)
{
   if (d->flags & NOT_READY)
      return 0;

   stack = store_int(stack, d->id);
   stack = store_int(stack, d->flags);
   stack = store_int(stack, d->date);
   stack = store_int(stack, d->read_count);
   stack = store_int(stack, d->next_sent);
   stack = store_string(stack, d->header);
   stack = store_int(stack, d->text.length);
   memcpy(stack, d->text.where, d->text.length);
   stack += d->text.length;
   stack = store_string(stack, d->name);
   return 1;
}


/* sync one hash bucket to disk */

void            sync_note_hash(int number)
{
   char           *oldstack, name[MAX_NAME + 2];
   note           *scan, *check;
   int             length, count = 0, fd, t;
   saved_player   *sp;

   oldstack = stack;

   if (sys_flags & VERBOSE)
   {
      sprintf(stack, "Syncing note hash %d.", number);
      stack = end_string(stack);
      log("sync", oldstack);
      stack = oldstack;
   }
   t = time(0);

   for (scan = n_hash[number]; scan; scan = check)
   {
      if (scan->flags & NEWS_ARTICLE)
      {
         strcpy(name, scan->name);
         lower_case(name);
         sp = find_saved_player(name);
         if ((t - (scan->date)) > NEWS_TIMEOUT)
         {
            if (!(sp && sp->residency & ADMIN))
            {
               check = scan->hash_next;
               remove_any_note(scan);
               /* scan=n_hash[number];  */
            }
         }
      } else if ((t - (scan->date)) > MAIL_TIMEOUT)
      {
         check = scan->hash_next;
         remove_any_note(scan);
         /* scan=n_hash[number];  */
      }
      check = scan->hash_next;
   }

   stack = store_int(stack, 0);

   for (scan = n_hash[number]; scan; scan = scan->hash_next)
      count+=save_note(scan);

   store_int(oldstack, count);
   length = (int) stack - (int) oldstack;

#ifdef PC
   sprintf(stack, "files\\notes\\hash%d", number);
   fd = open(stack, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY);
#else
   sprintf(stack, "files/notes/hash%d", number);
   fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR);
#endif
   if (fd < 0)
      handle_error("Failed to open note file.");
   if (write(fd, oldstack, length) < 0)
      handle_error("Failed to write note file.");
   close(fd);

   nhash_update[number] = 0;
   stack = oldstack;
}

/* throw all the notes to disk */

void            sync_notes(int background)
{
   int             n, fd;
   struct itimerval new;
   char           *oldstack;
   oldstack = stack;

   if (background && fork())
      return;

   if (sys_flags & VERBOSE || sys_flags & PANIC)
      log("sync", "Dumping notes to disk");
   for (n = 0; n < NOTE_HASH_SIZE; n++)
      sync_note_hash(n);
   if (sys_flags & VERBOSE || sys_flags & PANIC)
      log("sync", "Note dump completed");

#ifdef PC
   fd = open("files\\notes\\track", O_CREAT | O_WRONLY | O_TRUNC | O_BINARY);
#else
   fd = open("files/notes/track", O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR);
#endif
   if (fd < 0)
      handle_error("Failed to open track file.");
   stack = store_int(stack, unique);
   stack = store_int(stack, news_start);
   stack = store_int(stack, news_count);
   if (write(fd, oldstack, 12) < 0)
      handle_error("Failed to write track file.");
   close(fd);

   stack = oldstack;

   if (background)
      exit(0);
}

/* su command to dump all or single notes to disk */

void            dump_com(player * p, char *str)
{
   int             num;
   char           *oldstack;
   oldstack = stack;

   if (!isdigit(*str))
     {
       if (p!=NULL)
	 tell_player(p, " Dumping all notes to disk.\n");
       sync_notes(1);
       return;
     }

   num = atoi(str);

   if (num < 0 || num >= NOTE_HASH_SIZE)
   {
     if (p!=NULL)
       tell_player(p, " Argument not in hash range !\n");
     return;
   }
   sprintf(stack, " Dumping hash %d to disk.\n", num);
   stack = end_string(stack);
   if (p!=NULL)
     tell_player(p, oldstack);
   sync_note_hash(num);
   stack = oldstack;
}


/* throw away a hash of notes */

void            discard_hash(int num)
{
   note           *scan, *next;
   for (scan = n_hash[num]; scan; scan = next)
   {
      next = scan->hash_next;
      if (scan->text.where)
    FREE(scan->text.where);
      FREE(scan);
   }
}


/* extracts one note into the hash list */

char           *extract_note(char *where)
{
   int             num;
   note           *d;
   d = (note *) MALLOC(sizeof(note));
   memset(d, 0, sizeof(note));
   where = get_int(&d->id, where);
   where = get_int(&d->flags, where);
   where = get_int(&d->date, where);
   where = get_int(&d->read_count, where);
   where = get_int(&d->next_sent, where);
   where = get_string(d->header, where);
   where = get_int(&d->text.length, where);
   d->text.where = (char *) MALLOC(d->text.length);
   memcpy(d->text.where, where, d->text.length);
   where += d->text.length;
   where = get_string(d->name, where);
   num = (d->id) % NOTE_HASH_SIZE;
   if (n_hash[num])
      d->hash_next = n_hash[num];
   else
      d->hash_next = 0;
   n_hash[num] = d;
   return where;
}

/*
 * load all notes from disk this should be changed for arbitary hashes
 */

void            init_notes()
{
   int             n, length, fd, count;
   char           *oldstack, *where, *scan;
   oldstack = stack;

   if (sys_flags & VERBOSE || sys_flags & PANIC)
      log("boot", "Loading notes from disk");

#ifdef PC
   fd = open("files\\notes\\track", O_RDONLY | O_BINARY);
#else
   fd = open("files/notes/track", O_RDONLY | O_NDELAY);
#endif
   if (fd < 0)
   {
      sprintf(oldstack, "Failed to load track file");
      stack = end_string(oldstack);
      log("error", oldstack);
      unique = 1;
      news_start = 0;
      stack = oldstack;
   } else
   {
      if (read(fd, oldstack, 12) < 0)
    handle_error("Can't read track file.");
      stack = get_int(&unique, stack);
      stack = get_int(&news_start, stack);
      stack = get_int(&news_count, stack);
      close(fd);
      stack = oldstack;
   }


   for (n = 0; n < NOTE_HASH_SIZE; n++)
   {

      discard_hash(n);
      nhash_update[n] = 0;

#ifdef PC
      sprintf(oldstack, "files\\notes\\hash%d", n);
      fd = open(oldstack, O_RDONLY | O_BINARY);
#else
      sprintf(oldstack, "files/notes/hash%d", n);
      fd = open(oldstack, O_RDONLY | O_NDELAY);
#endif
      if (fd < 0)
      {
    sprintf(oldstack, "Failed to load note hash%d", n);
    stack = end_string(oldstack);
    log("error", oldstack);
    stack = 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 note file.");
       scan = get_int(&count, where);
       for (; count; count--)
          scan = extract_note(scan);
       FREE(where);
    }
    close(fd);
      }
   }
   stack = oldstack;
}



/* store info for a player save */

void            construct_mail_save(saved_player * sp)
{
   int             count = 0, *scan;
   char           *oldstack;
   stack = store_int(stack, sp->mail_sent);
   if (!(sp->mail_received))
      stack = store_int(stack, 0);
   else
   {
      oldstack = stack;
      stack = store_int(oldstack, 0);
      for (scan = sp->mail_received; *scan; scan++, count++)
    stack = store_int(stack, *scan);
      store_int(oldstack, count);
   }
}


/* get info back from a player save */

char           *retrieve_mail_data(saved_player * sp, char *where)
{
   int             count = 0, *fill;
   where = get_int(&sp->mail_sent, where);
   where = get_int(&count, where);
   if (count)
   {
      fill = (int *) MALLOC((count + 1) * sizeof(int));
      sp->mail_received = fill;
      for (; count; count--, fill++)
    where = get_int(fill, where);
      *fill++ = 0;
   } else
      sp->mail_received = 0;
   return where;
}


/* su command to check out note hashes */

void            list_notes(player * p, char *str)
{
   int             num;
   note           *scan;
   char           *oldstack;

   oldstack = stack;

   num = atoi(str);
   if (num < 0 || num >= NOTE_HASH_SIZE)
   {
      tell_player(p, " Number not in range.\n");
      return;
   }
   strcpy(stack, " Notes:\n");
   stack = strchr(stack, 0);
   for (scan = n_hash[num]; scan; scan = scan->hash_next)
   {
      if (scan->flags & NEWS_ARTICLE)
         sprintf(stack, "  [%d] %s + %s", scan->id,
                 scan->name, scan->header);
      else
         sprintf(stack, "  [%d] %s - %s", scan->id, scan->name,
                 bit_string(scan->flags));
      stack = strchr(stack, 0);
      if (scan->flags & NOT_READY)
      {
    strcpy(stack, "(DEFUNCT)\n");
    stack = strchr(stack, 0);
      } else
    *stack++ = '\n';
   }
   strcpy(stack, " --\n");
   stack = strchr(stack, 0);
   stack++;
   tell_player(p, oldstack);
   stack = oldstack;
}


void            list_all_notes(player * p, char *str)
{
   char           *oldstack;
   int             count = 0, hash;
   note           *scan;

   oldstack = stack;

   strcpy(stack, " All notes --\n");
   stack = strchr(stack, 0);
   for (hash = 0; hash <= NOTE_HASH_SIZE; count = 0, hash++)
   {
      for (scan = n_hash[hash]; scan; scan = scan->hash_next)
         count++;
      sprintf(stack, "  %3d notes in bucket %2d", count, hash);
      stack = strchr(stack, 0);
      if (hash & 1)
    strcpy(stack, "   --   ");
      else
    strcpy(stack, "\n");
      stack = strchr(stack, 0);
   }
   strcpy(stack, " --\n");
   stack = strchr(stack, 0);
   stack++;
   tell_player(p, oldstack);
   stack = oldstack;
}

/* the news command */

void            news_command(player * p, char *str)
{
   if (p->edit_info)
   {
      tell_player(p, " Can't do news commands whilst in editor.\n");
      return;
   }
   if ((*str == '/') && (p->input_to_fn == news_command))
   {
      match_commands(p, str + 1);
      if (!(p->flags & PANIC) && (p->input_to_fn == news_command))
      {
         do_prompt(p, "News Mode >");
         p->mode |= NEWSEDIT;
      }
      return;
   }
   if (!*str)
      if (p->input_to_fn == news_command)
      {
    tell_player(p, " Format: news <action>\n");
    if (!(p->flags & PANIC) && (p->input_to_fn == news_command))
    {
       do_prompt(p, "News Mode >");
       p->mode |= NEWSEDIT;
    }
    return;
      } else
      {
    tell_player(p, " Entering news mode. Use 'end' to leave.\n"
           " '/<command>' does normal commands.\n");
    p->flags &= ~PROMPT;
    p->input_to_fn = news_command;
      }
   else
      sub_command(p, str, news_list);
   if (!(p->flags & PANIC) && (p->input_to_fn == news_command))
   {
      do_prompt(p, "News Mode >");
      p->mode |= NEWSEDIT;
   }
}


/* the mail command */

void            mail_command(player * p, char *str)
{
   if (p->edit_info)
   {
      tell_player(p, " Can't do mail commands whilst in editor.\n");
      return;
   }
   if (!p->saved)
   {
      tell_player(p, " You have no save information, and so can't use "
        "mail.\n");
      return;
   }
   if ((*str == '/') && (p->input_to_fn == mail_command))
   {
      match_commands(p, str + 1);
      if (!(p->flags & PANIC) && (p->input_to_fn == mail_command))
      { 
         do_prompt(p, "Mail Mode >");
         p->mode |= MAILEDIT;
      }
      return;
   }
   if (!*str)
      if (p->input_to_fn == mail_command)
      {
         tell_player(p, " Format: mail <action>\n");
         if (!(p->flags & PANIC) && (p->input_to_fn == mail_command))
         {
            do_prompt(p, "Mail Mode >");
            p->mode |= MAILEDIT;
         }
         return;
      } else
      {
    tell_player(p, " Entering mail mode. Use 'end' to leave.\n"
           " '/<command>' does normal commands.\n");
    p->flags &= ~PROMPT;
    p->input_to_fn = mail_command;
      }
   else
      sub_command(p, str, mail_list);
   if (!(p->flags & PANIC) && (p->input_to_fn == mail_command))
   {
      do_prompt(p, "Mail Mode >");
      p->mode |= MAILEDIT;
   }
}

/* view news commands */

void            view_news_commands(player * p, char *str)
{
   view_sub_commands(p, news_list);
}

/* view mail commands */

void            view_mail_commands(player * p, char *str)
{
   view_sub_commands(p, mail_list);
}

/* exit news mode */

void            exit_news_mode(player * p, char *str)
{
   if (p->input_to_fn != news_command)
      return;
   tell_player(p, " Leaving news mode.\n");
   p->input_to_fn = 0;
   p->flags |= PROMPT;
   p->mode &= ~NEWSEDIT;
}

/* exit mail mode */

void            exit_mail_mode(player * p, char *str)
{
   if (p->input_to_fn != mail_command)
      return;
   tell_player(p, " Leaving mail mode.\n");
   p->input_to_fn = 0;
   p->flags |= PROMPT;
   p->mode &= ~MAILEDIT;
}

/* finds news article number x */

note           *find_news_article(int n)
{
   int             integrity = 0;
   note           *scan;
   if (n < 1 || n > news_count)
      return 0;

   for (n--, scan = find_note(news_start); n; n--, integrity++)
      if (scan)
    scan = find_note(scan->next_sent);
      else
      {
    news_count = integrity;
    return 0;
      }
   return scan;
}

/* list news articles */

void            list_news(player * p, char *str)
{
   char           *oldstack, middle[80];
   int             page, pages, count, article, ncount = 1;
   note           *scan;
   oldstack = stack;

   if (!news_count)
   {
      tell_player(p, " No news articles to view.\n");
      return;
   }
   page = atoi(str);
   if (page <= 0)
      page = 1;
   page--;

   pages = (news_count - 1) / (TERM_LINES - 2);
   if (page > pages)
      page = pages;

   if (news_count == 1)
      strcpy(middle, "There is one news article");
   else
      sprintf(middle, "There are %s articles",
         number2string(news_count));
   pstack_mid(middle);

   count = page * (TERM_LINES - 2);

   for (article = news_start; count; count--, ncount++)
   {
      scan = find_note(article);
      if (!scan)
      {
    tell_player(p, " Bad news listing, aborted.\n");
    log("error", "Bad news list");
    stack = oldstack;
    return;
      }
      article = scan->next_sent;
   }
   for (count = 0; (count < (TERM_LINES - 1)); count++, ncount++)
   {
      scan = find_note(article);
      if (!scan)
    break;
      if (p->residency & ADMIN)
    sprintf(stack, "(%d) [%d] ", scan->id, ncount);
      else
    sprintf(stack, "[%d] ", ncount);
      while (*stack)
    *stack++;
      if (ncount < 10)
    *stack++ = ' ';
      strcpy(stack, scan->header);
      while (*stack)
    *stack++;
      if (scan->flags & ANONYMOUS)
      {
    if (p->residency & ADMIN)
       sprintf(stack, " <%s>\n", scan->name);
    else
       strcpy(stack, "\n");
      } else
    sprintf(stack, " (%s)\n", scan->name);
      stack = strchr(stack, 0);
      article = scan->next_sent;
   }

   sprintf(middle, "Page %d of %d", page + 1, pages + 1);
   pstack_mid(middle);

   *stack++ = 0;
   tell_player(p, oldstack);

   stack = oldstack;
}


/* post a news article */

void            quit_post(player * p)
{
   tell_player(p, " Article NOT posted.\n");
   remove_note((note *) p->edit_info->misc);
   p->mode &= ~NEWSEDIT;
}


void            end_post(player * p, char *str)
{
   note           *article;
   char           *oldstack;
   oldstack = stack;

   article = (note *) p->edit_info->misc;
   stack = store_string(oldstack, p->edit_info->buffer);
   article->text.length = (int) stack - (int) oldstack;
   article->text.where = (char *) MALLOC(article->text.length);
   memcpy(article->text.where, oldstack, article->text.length);
   stack = oldstack;

   article->next_sent = news_start;
   news_start = article->id;
   news_count++;
   article->flags &= ~NOT_READY;
   article->read_count = 0;
   tell_player(p, " Article posted....\n");
   p->mode &= ~NEWSEDIT;
   if (p->edit_info->input_copy == news_command)
   {
      do_prompt(p, "News Mode >");
      p->mode |= NEWSEDIT;
   }
}

void            post_news(player * p, char *str)
{
   note           *article;
   char           *scan, *first, *secure;
   if (!*str)
   {
      tell_player(p, " Format: post <header>\n");
      return;
   }

   secure=strchr(str,')');
   if (secure)
     if (*(++secure)=='\0')
       if (strchr(str,'('))
	 {
	   tell_player(p," You may not have bracketed comments at the end "
		       "of news titles\n");
	   return;
	 }

   secure=strchr(str,'>');
   if (secure)
     if (*(++secure)=='\0')
       if (strchr(str,'>'))
	 {
	   tell_player(p," You may not have bracketed comments at the end "
		       "of news titles\n");
	   return;
	 }
   
   
   
   article = create_note();
   strncpy(article->header, str, MAX_TITLE - 2);
   article->flags |= NEWS_ARTICLE;
   first = first_char(p);
   scan = strstr(first, "post");
   if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a'))
      article->flags |= ANONYMOUS;
   strcpy(article->name, p->name);
   tell_player(p, " Now enter the main body text for the article.\n");
   *stack = 0;
   start_edit(p, MAX_ARTICLE_SIZE, end_post, quit_post, stack);
   if (p->edit_info)
      p->edit_info->misc = (void *) article;
   else
      FREE(article);
}

/* follow up an article */

void            followup(player * p, char *str)
{
   char           *oldstack, *body, *indent, *scan, *first;
   note           *article, *old;
   oldstack = stack;

   old = find_news_article(atoi(str));
   if (!old)
   {
      sprintf(stack, " No such news article '%s'\n", str);
      stack = end_string(stack);
      tell_player(p, oldstack);
      stack = oldstack;
      return;
   }
   article = create_note();
   if (strstr(old->header, "Re: ") == old->header)
      strcpy(article->header, old->header);
   else
   {
      sprintf(stack, "Re: %s", old->header);
      strncpy(article->header, stack, MAX_TITLE - 2);
   }
   article->flags |= NEWS_ARTICLE;

   first = first_char(p);
   scan = strstr(first, "followup");
   if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a'))
      article->flags |= ANONYMOUS;

   strcpy(article->name, p->name);

   indent = stack;
   get_string(stack, old->text.where);
   stack = end_string(stack);
   body = stack;

   if (old->flags & ANONYMOUS)
      sprintf(stack, "From anonymous article written on %s ...\n",
         convert_time(old->date));
   else
      sprintf(stack, "On %s, %s wrote ...\n",
         convert_time(old->date), old->name);
   stack = strchr(stack, 0);

   while (*indent)
   {
      *stack++ = '>';
      *stack++ = ' ';
      while (*indent && *indent != '\n')
    *stack++ = *indent++;
      *stack++ = '\n';
      indent++;
   }
   *stack++ = '\n';
   *stack++ = 0;

   tell_player(p, " Please trim article as much as is possible ....\n");
   start_edit(p, MAX_ARTICLE_SIZE, end_post, quit_post, body);
   if (p->edit_info)
      p->edit_info->misc = (void *) article;
   else
      FREE(article);
   stack = oldstack;
}

/* wipe a news article */

void            remove_article(player * p, char *str)
{
   note           *article, *prev, *scan;
   char           *oldstack;
   oldstack = stack;
   article = find_news_article(atoi(str));
   if (!article)
   {
      sprintf(stack, " No such news article '%s'\n", str);
      stack = end_string(stack);
      tell_player(p, oldstack);
      stack = oldstack;
      return;
   }
   strcpy(stack, article->name);
   lower_case(stack);
   if (!(p->residency & ADMIN) && strcmp(stack, p->lower_name))
   {
      tell_player(p, " You can't remove an article that isn't yours.\n");
      return;
   }
   scan = find_note(news_start);
   if (scan == article)
      news_start = article->next_sent;
   else
   {
      do
      {
    prev = scan;
    scan = find_note(scan->next_sent);
      } while (scan != article);
      prev->next_sent = article->next_sent;
   }
   news_count--;
   remove_note(article);
   tell_player(p, " Article removed.\n");
   stack = oldstack;
}

/* read an article */

void            read_article(player * p, char *str)
{
   char           *oldstack;
   note           *article;
   oldstack = stack;

   if (!*str)
   {
      tell_player(p, " Format: read <article-number>\n");
      return;
   }
   article = find_news_article(atoi(str));
   if (!article)
   {
      sprintf(stack, " No such news article '%s'\n", str);
      stack = end_string(stack);
      tell_player(p, oldstack);
      stack = oldstack;
      return;
   }
   article->read_count++;
   if (article->flags & ANONYMOUS)
   {
      if (p->residency & ADMIN)
    sprintf(stack, "Subject: %s\nPosted anonymously on %s by %s.\n",
       article->header, convert_time(article->date), article->name);
      else
    sprintf(stack, "Subject: %s\nPosted anonymously on %s.\n",
       article->header, convert_time(article->date));
   } else
      sprintf(stack, "Subject: %s\nPosted by %s on %s.\n", article->header,
         article->name, convert_time(article->date));
   stack = strchr(stack, 0);
   if (article->read_count == 1)
      strcpy(stack, "Article read once only.\n\n");
   else
      sprintf(stack, "Article has been read %s times.\n\n",
         number2string(article->read_count));
   stack = strchr(stack, 0);
   get_string(stack, article->text.where);
   stack = end_string(stack);
   pager(p, oldstack, 0);
   stack = oldstack;
}



/* mail stuff */



/* count how many mails have been posted */

int             posted_count(player * p)
{
   int             scan, count = 0;
   note           *mail, *next;

   if (!p->saved)
      return 0;
   scan = p->saved->mail_sent;
   mail = find_note(scan);
   if (!mail)
   {
      p->saved->mail_sent = 0;
      return 0;
   }
   while (mail)
   {
      count++;
      scan = mail->next_sent;
      next = find_note(scan);
      if (!next && scan)
      {
    mail->next_sent = 0;
    mail = 0;
      } else
    mail = next;
   }
   return count;
}



/* view mail that has been sent */

void view_sent(player * p, char *str)
{
   char *oldstack;
   note *mail, *next;
   int count = 1, scan;
   saved_player   *sp;

   oldstack = stack;
   if (*str && p->residency & ADMIN)
   {
      sp = find_saved_player(str);
      if (!sp)
      {
         tell_player(p, " No such player in save files.\n");
         return;
      } else
      {
         scan = sp->mail_sent;
         mail = find_note(scan);
         if (!mail)
         {
            sp->mail_sent = 0;
            tell_player(p, " They have sent no mail.\n");
            return;
         }
      }
   } else
   {
      if (!p->saved)
      {
         tell_player(p, " You have no save file, and therefore no mail "
                        "either.\n");
         return;
      }
      scan = p->saved->mail_sent;
      mail = find_note(scan);
      if (!mail)
      {
         p->saved->mail_sent = 0;
         tell_player(p, " You have sent no mail.\n");
         return;
      }
   }
   strcpy(stack, "Listing mail sent...\n");
   stack = strchr(stack, 0);
   while (mail)
   {
      if (p->residency & ADMIN)
      {
         sprintf(stack, "(%d) [%d] %d - %s\n", mail->id, count,
                 mail->read_count, mail->header);
      } else
      {
         sprintf(stack, "[%d] %d - %s\n", count, mail->read_count,
                 mail->header);
      }
      stack = strchr(stack, 0);
      scan = mail->next_sent;
      count++;
      next = find_note(scan);
      if (!next && scan)
      {
         mail->next_sent = 0;
         mail = 0;
      } else
      {
         mail = next;
      }
   }
   sprintf(stack, " You have sent %d out of your maximum of %d mails.\n",
           count - 1, p->max_mail);
   stack = end_string(stack);
   tell_player(p, oldstack);
   stack = oldstack;
}


/* this fn goes through the received list and removes dud note pointers */

void            reconfigure_received_list(saved_player * sp)
{
   int             count = 0;
   int            *scan, *fill;
   char           *oldstack;
   oldstack = stack;

   align(stack);
   fill = (int *) stack;
   scan = sp->mail_received;
   if (!scan)
      return;
   for (; *scan; scan++)
      if (((*scan) < 0) || ((*scan) > 65536))
      {
    count = 0;
    break;
      } else if (find_note(*scan))
      {
    *(int *) stack = *scan;
    stack += sizeof(int);
    count++;
      }
   FREE(sp->mail_received);
   if (count)
   {
      *(int *) stack = 0;
      stack += sizeof(int);
      count++;
      sp->mail_received = (int *) MALLOC(count * sizeof(int));
      memcpy(sp->mail_received, oldstack, count * sizeof(int));
   } else
      sp->mail_received = 0;
   stack = oldstack;
}


/* view mail that has been received */

void            view_received(player * p, char *str)
{
   char           *oldstack, middle[80], *page_input;
   int             page, pages, *scan, *scan_count, mcount = 0, ncount = 1,
                   n;
   note           *mail;
   saved_player   *sp;
   oldstack = stack;

   if (*str && !isdigit(*str) && p->residency & ADMIN)
   {
      page_input = next_space(str);
      if (*page_input)
    *page_input++ = 0;
      sp = find_saved_player(str);
      if (!sp)
      {
    tell_player(p, " No such player in save files.\n");
    return;
      } else
    scan = sp->mail_received;
      str = page_input;
   } else
   {
      if (!p->saved)
      {
         tell_player(p, " You have no save file, "
                        "and therefore no mail either.\n");
         return;
      }
      sp = p->saved;
      scan = sp->mail_received;
   }

   if (!scan)
   {
      tell_player(p, " You have received no mail.\n");
      return;
   }
   p->saved_flags &= ~NEW_MAIL;

   for (scan_count = scan; *scan_count; scan_count++)
      mcount++;

   page = atoi(str);
   if (page <= 0)
      page = 1;
   page--;

   pages = (mcount - 1) / (TERM_LINES - 2);
   if (page > pages)
      page = pages;

   if (mcount == 1)
      strcpy(middle, "You have received one letter");
   else
      sprintf(middle, "You have received %s letters",
         number2string(mcount));
   pstack_mid(middle);

   ncount = page * (TERM_LINES - 2);

   scan += ncount;

   for (n = 0, ncount++; n < (TERM_LINES - 1); n++, ncount++, scan++)
   {
      if (!*scan)
         break;
      mail = find_note(*scan);
      if (!mail)
      {
         stack = oldstack;
         tell_player(p, " Found mail that owner had deleted ...\n");
         reconfigure_received_list(sp);
         if (sp != p->saved)
         {
            tell_player(p, " Reconfigured ... try again\n");
            return;
         }
         view_received(p, str);
         return;
      }
      if (p->residency & ADMIN)
         sprintf(stack, "(%d) [%d] ", mail->id, ncount);
      else
         sprintf(stack, "[%d] ", ncount);
      while (*stack)
         *stack++;
      if (ncount < 10)
         *stack++ = ' ';
      strcpy(stack, mail->header);
      while (*stack)
         *stack++;
      if (mail->flags & ANONYMOUS)
      {
         if (p->residency & ADMIN)
            sprintf(stack, " <%s>\n", mail->name);
         else
            strcpy(stack, "\n");
      } else
         sprintf(stack, " (%s)\n", mail->name);
      while (*stack)
         *stack++;
   }

   sprintf(middle, "Page %d of %d", page + 1, pages + 1);
   pstack_mid(middle);

   *stack++ = 0;
   tell_player(p, oldstack);

   stack = oldstack;
}


/* send a letter */


void            quit_mail(player * p)
{
   tell_player(p, " Letter NOT posted.\n");
   remove_note((note *) p->edit_info->misc);
}

void            end_mail(player * p, char *str)
{
   note           *mail;
   char           *oldstack, *name_list, *body, *tcpy, *comp, *text;
   saved_player  **player_list, **pscan, **pfill;
   player         *on;
   int             receipt_count, n, m, *received_lists, *r;
   oldstack = stack;

   mail = (note *) p->edit_info->misc;

   align(stack);
   player_list = (saved_player **) stack;
   receipt_count = saved_tag(p, mail->text.where);
   if (mail->text.where)
      FREE(mail->text.where);
   mail->text.where = 0;
   if (!receipt_count)
   {
      tell_player(p, " No one to send the letter to !\n");
      remove_note(mail);
      stack = oldstack;
      if (p->edit_info->input_copy == mail_command)
      {
         do_prompt(p, "Mail Mode >");
         p->mode |= MAILEDIT;
      }
      return;
   }
   if (!p->saved)
   {
      tell_player(p, " Eeek, no save file !\n");
      remove_note(mail);
      stack = oldstack;
      if (p->edit_info->input_copy == mail_command)
      {
         do_prompt(p, "Mail Mode >");
         p->mode |= MAILEDIT;
      }
      return;
   }
   pscan = player_list;
   pfill = player_list;
   m = receipt_count;
   for (n = 0; n < m; n++)
   {
      if (((*pscan)->residency == SYSTEM_ROOM)
          || ((*pscan)->residency & BANISHD))
      {
         tell_player(p, " You can't send mail to a room, "
                        "or someone who is banished.\n");
         receipt_count--;
         pscan++;
      } else
      {
         if ((mail->flags & ANONYMOUS)
             && ((*pscan)->saved_flags & NO_ANONYMOUS))
         {
            text = stack;
            sprintf(stack, " %s is not receiving anonymous mail.\n",
                    (*pscan)->lower_name);
            stack = end_string(stack);
            tell_player(p, text);
            stack = text;
            receipt_count--;
            pscan++;
         } else
            *pfill++ = *pscan++;
      }
   }

   if (receipt_count > 0)
   {
      pscan = player_list;
      name_list = stack;
      if (mail->flags & SUPRESS_NAME)
      {
    strcpy(stack, "Anonymous");
    stack = end_string(stack);
      } else
      {
    strcpy(stack, (*pscan)->lower_name);
    stack = strchr(stack, 0);
    if (receipt_count > 1)
    {
       pscan++;
       for (n = 2; n < receipt_count; n++, pscan++)
       {
          sprintf(stack, ", %s", (*pscan)->lower_name);
          stack = strchr(stack, 0);
       }
       sprintf(stack, " and %s", (*pscan)->lower_name);
       stack = strchr(stack, 0);
    }
    *stack++ = 0;
      }

      body = stack;
      sprintf(stack, " Sending mail to %s.\n", name_list);
      stack = end_string(stack);
      tell_player(p, body);
      stack = body;

      if (mail->flags & ANONYMOUS)
    sprintf(stack, " Anonymous mail dated %s.\nSubject: %s\nTo: %s\n\n",
       convert_time(mail->date), mail->header, name_list);
      else
    sprintf(stack, " Mail dated %s.\nSubject: %s\nTo: %s\nFrom: %s\n\n",
         convert_time(mail->date), mail->header, name_list, mail->name);
      stack = strchr(stack, 0);

      tcpy = p->edit_info->buffer;
      for (n = 0; n < p->edit_info->size; n++)
    *stack++ = *tcpy++;

      comp = stack;
      stack = store_string(stack, body);
      mail->text.length = (int) stack - (int) comp;
      mail->text.where = (char *) MALLOC(mail->text.length);
      memcpy(mail->text.where, comp, mail->text.length);

      mail->next_sent = p->saved->mail_sent;
      p->saved->mail_sent = mail->id;

      text = stack;
      command_type |= HIGHLIGHT;

      if (mail->flags & ANONYMOUS)
    sprintf(stack, "     -=>  New mail, '%s' sent anonymously\n\n",
       mail->header);
      else
    sprintf(stack, "     -=>  New mail, '%s' from %s.\n\n",
       mail->header, mail->name);
      stack = end_string(stack);

      align(stack);
      received_lists = (int *) stack;

      pscan = player_list;
      for (n = 0; n < receipt_count; n++, pscan++)
      {
    stack = (char *) received_lists;
    r = (*pscan)->mail_received;
    if (!r)
       m = 2 * sizeof(int);
    else
    {
       for (m = 2 * sizeof(int); *r; r++, m += sizeof(int))
       {
          *(int *) stack = *r;
          stack += sizeof(int);
       }
    }
    *(int *) stack = mail->id;
    stack += sizeof(int);
    *(int *) stack = 0;
    if ((*pscan)->mail_received)
       FREE((*pscan)->mail_received);
    r = (int *) MALLOC(m);
    memcpy(r, received_lists, m);
    (*pscan)->mail_received = r;
    on = find_player_absolute_quiet((*pscan)->lower_name);
    if (on && on->saved_flags & MAIL_INFORM)
    {
       tell_player(on, "\007\n");
       tell_player(on, text);
    } else
       (*pscan)->saved_flags |= NEW_MAIL;
      }

      command_type &= ~HIGHLIGHT;

      mail->read_count = receipt_count;
      mail->flags &= ~NOT_READY;
      tell_player(p, " Mail posted....\n");
   } else
      tell_player(p, " No mail posted.\n");
   p->mode &= ~MAILEDIT;
   if (p->edit_info->input_copy == mail_command)
   {
      do_prompt(p, "Mail Mode >");
      p->mode |= MAILEDIT;
   }
   stack = oldstack;
}


void            send_letter(player * p, char *str)
{
   note           *mail;
   char           *subject, *scan, *first;
   int             length;

   if (posted_count(p) >= p->max_mail)
   {
      tell_player(p, " Sorry, you have reached your mail limit.\n");
      return;
   }
   subject = next_space(str);

   if (!*subject)
   {
      tell_player(p, " Format: post <character(s)> <subject>\n");
      return;
   }
   *subject++ = 0;

   mail = create_note();

   first = first_char(p);
   scan = strstr(first, "post");
   if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a'))
      mail->flags |= ANONYMOUS;

   strcpy(mail->name, p->name);
   strncpy(mail->header, subject, MAX_TITLE - 2);

   tell_player(p, " Enter main text of the letter...\n");
   *stack = 0;
   start_edit(p, MAX_ARTICLE_SIZE, end_mail, quit_mail, stack);
   if (p->edit_info)
   {
      p->edit_info->misc = (void *) mail;
      length = strlen(str) + 1;
      mail->text.where = (char *) MALLOC(length);
      memcpy(mail->text.where, str, length);
   } else
      FREE(mail);
}


/* find the note corresponing to letter number */

note           *find_received(saved_player * sp, int n)
{
   int            *scan, count = 1;
   note           *mail;
   scan = sp->mail_received;
   if (!scan)
      return 0;
   for (; *scan; count++, scan++)
      if (count == n)
      {
    mail = find_note(*scan);
    if (!mail)
    {
       reconfigure_received_list(sp);
       return find_received(sp, n);
    }
    return mail;
      }
   return 0;
}

/* view a letter */

void            read_letter(player * p, char *str)
{
   char           *oldstack, *which;
   saved_player   *sp;
   note           *mail;
   oldstack = stack;

   which = str;
   sp = p->saved;
   if (!sp)
   {
      tell_player(p, " You have no save info.\n");
      return;
   }
   mail = find_received(sp, atoi(which));
   if (!mail)
   {
      sprintf(stack, " No such letter '%s'\n", which);
      stack = end_string(stack);
      tell_player(p, oldstack);
      stack = oldstack;
      return;
   }
   get_string(stack, mail->text.where);
   stack = end_string(stack);
   pager(p, oldstack, 0);
   p->saved_flags &= ~NEW_MAIL;
   stack = oldstack;
}


/* reply to a letter */

void            reply_letter(player * p, char *str)
{
   note           *mail, *old;
   char           *indent, *body, *oldstack, *scan, *first;
   int             length;

   oldstack = stack;

   if (!*str)
   {
      tell_player(p, " Format: reply <number>\n");
      return;
   }
   if (posted_count(p) >= p->max_mail)
   {
      tell_player(p, " Sorry, you have reached your mail limit.\n");
      return;
   }
   if (!p->saved)
   {
      tell_player(p, " Eeek, no saved bits.\n");
      return;
   }
   old = find_received(p->saved, atoi(str));
   if (!old)
   {
      sprintf(stack, " Can't find letter '%s'\n", str);
      stack = end_string(stack);
      tell_player(p, oldstack);
      stack = oldstack;
      return;
   }
   mail = create_note();

   first = first_char(p);
   scan = strstr(first, "reply");
   if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a'))
      mail->flags |= ANONYMOUS;

   if (old->flags & ANONYMOUS)
      mail->flags |= SUPRESS_NAME;
   strcpy(mail->name, p->name);

   if (strstr(old->header, "Re: ") == old->header)
      strcpy(mail->header, old->header);
   else
   {
      sprintf(stack, "Re: %s", old->header);
      strncpy(mail->header, stack, MAX_TITLE - 2);
   }

   indent = stack;
   get_string(stack, old->text.where);
   stack = end_string(stack);
   body = stack;

   sprintf(stack, "In reply to '%s'\n", old->header);
   stack = strchr(stack, 0);

   while (*indent)
   {
      *stack++ = '>';
      *stack++ = ' ';
      while (*indent && *indent != '\n')
    *stack++ = *indent++;
      *stack++ = '\n';
      indent++;
   }
   *stack++ = '\n';
   *stack++ = 0;

   tell_player(p, " Please trim letter as much as possible...\n");
   start_edit(p, MAX_ARTICLE_SIZE, end_mail, quit_mail, body);
   if (p->edit_info)
   {
      p->edit_info->misc = (void *) mail;
      length = strlen(old->name) + 1;
      mail->text.where = (char *) MALLOC(length);
      memcpy(mail->text.where, old->name, length);
   } else
      FREE(mail);
   stack = oldstack;
}


/* reply to an article */

void            reply_article(player * p, char *str)
{
   note           *mail, *old;
   char           *indent, *body, *oldstack, *scan, *first;
   int             length;

   oldstack = stack;

   if (!*str)
   {
      tell_player(p, " Format: reply <no>\n");
      return;
   }
   if (posted_count(p) >= p->max_mail)
   {
      tell_player(p, " Sorry, you have reached your mail limit.\n");
      return;
   }
   if (!p->saved)
   {
      tell_player(p, " Eeek, no saved bits.\n");
      return;
   }
   old = find_news_article(atoi(str));
   if (!old)
   {
      sprintf(stack, " Can't find letter '%s'\n", str);
      stack = end_string(stack);
      tell_player(p, oldstack);
      stack = oldstack;
      return;
   }
   mail = create_note();

   first = first_char(p);
   scan = strstr(first, "reply");
   if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a'))
      mail->flags |= ANONYMOUS;

   if (old->flags & ANONYMOUS)
      mail->flags |= SUPRESS_NAME;
   strcpy(mail->name, p->name);

   if (strstr(old->header, "Re: ") == old->header)
      strcpy(mail->header, old->header);
   else
   {
      sprintf(stack, "Re: %s", old->header);
      strncpy(mail->header, stack, MAX_TITLE - 2);
   }

   indent = stack;
   get_string(stack, old->text.where);
   stack = end_string(stack);
   body = stack;

   sprintf(stack, "In your article '%s' you wrote ...\n", old->header);
   stack = strchr(stack, 0);

   while (*indent)
   {
      *stack++ = '>';
      *stack++ = ' ';
      while (*indent && *indent != '\n')
    *stack++ = *indent++;
      *stack++ = '\n';
      indent++;
   }
   *stack++ = '\n';
   *stack++ = 0;

   tell_player(p, " Please trim letter as much as possible...\n");
   start_edit(p, MAX_ARTICLE_SIZE, end_mail, quit_mail, body);
   if (p->edit_info)
   {
      p->edit_info->misc = (void *) mail;
      length = strlen(old->name) + 1;
      mail->text.where = (char *) MALLOC(length);
      memcpy(mail->text.where, old->name, length);
   } else
      FREE(mail);
   stack = oldstack;
}


/* unlink and remove a mail note */

void            unlink_mail(note * m)
{
   char           *oldstack, *temp;
   saved_player   *sp;
   int            *change;
   note           *scan, *tmp;
   if (!m)
      return;
   oldstack = stack;
   strcpy(stack, m->name);
   lower_case(stack);
   stack = end_string(stack);
   sp = find_saved_player(oldstack);
   if (!sp)
   {
      temp = stack;
      if (current_player)
      {
         sprintf(stack, "(2) mail: %s - current: %s", m->name,
            current_player->name);
      } else
      {
         sprintf(stack, "(2) mail, %s - no current", m->name);
      }
      stack = end_string(stack);
      log("error", temp);
      stack = temp;
      unlink_mail(find_note(m->next_sent));
   } else
   {
      change = &(sp->mail_sent);
      scan = find_note(sp->mail_sent);
      while (scan && scan != m)
      {
    change = &(scan->next_sent);
    scan = find_note(scan->next_sent);
      }
      tmp = find_note(m->next_sent);
      if (tmp)
    *change = tmp->id;
      else
    *change = 0;
   }
   remove_note(m);
   stack = oldstack;
}


/* remove sent mail */

void            delete_sent(player * p, char *str)
{
   int             number;
   note           *scan;
   number = atoi(str);
   if (number <= 0)
   {
      tell_player(p, " Format: remove <mail number>\n");
      return;
   }
   if (!(p->saved))
   {
      tell_player(p, " You have no save information, "
                     "and therefore no mail either.\n");
      return;
   }
   scan = find_note(p->saved->mail_sent);
   for (number--; number; number--)
      if (scan)
    scan = find_note(scan->next_sent);
      else
    break;
   if (!scan)
   {
      tell_player(p, " You haven't sent that many mails.\n");
      return;
   }
   unlink_mail(scan);
   tell_player(p, " Removed mail ...\n");
}


/* remove received mail */

void            delete_received(player * p, char *str)
{
   int             number, count = 0;
   int            *make, *scan;
   char           *oldstack;
   note           *deleted;
   number = atoi(str);
   if (number <= 0)
   {
      tell_player(p, " Format: delete <mail number>\n");
      return;
   }
   if (!(p->saved))
   {
      tell_player(p, " You have no save information, "
                     "and therefore no mail either.\n");
      return;
   }
   scan = p->saved->mail_received;
   if (!scan)
   {
      tell_player(p, " You have recieved no mail to delete.\n");
      return;
   }
   oldstack = stack;
   align(stack);
   make = (int *) stack;
   for (number--; number; number--)
      if (*scan)
      {
    *make++ = *scan++;
    count++;
      } else
    break;
   if (!*scan)
   {
      tell_player(p, " Not that many pieces of mail.\n");
      stack = oldstack;
      return;
   }
   deleted = find_note(*scan++);
   while (*scan)
   {
      *make++ = *scan++;
      count++;
   }
   *make++ = 0;
   count++;
   if (p->saved->mail_received)
      FREE(p->saved->mail_received);
   if (count != 1)
   {
      p->saved->mail_received = (int *) MALLOC(sizeof(int) * count);
      memcpy(p->saved->mail_received, stack, sizeof(int) * count);
   } else
      p->saved->mail_received = 0;
   if (deleted)
   {
      deleted->read_count--;
      if (!(deleted->read_count))
    unlink_mail(deleted);
   }
   tell_player(p, " Mail deleted....\n");
   stack = oldstack;
}


/* admin ability to view notes */

void            view_note(player * p, char *str)
{
   note           *n;
   char           *oldstack;
   oldstack = stack;
   if (!*str)
   {
      tell_player(p, " Format: view_note <number>\n");
      return;
   }
   n = find_note(atoi(str));
   if (!n)
   {
      tell_player(p, " Can't find note with that number.\n");
      return;
   }
   if (n->flags & NEWS_ARTICLE)
      strcpy(stack, " News Article.\n");
   else
      strcpy(stack, " Mail (?).\n");
   stack = strchr(stack, 0);
   sprintf(stack, " Posted on %s, by %s.\n Read count: %d\n",
      convert_time(n->date), n->name, n->read_count);
   stack = strchr(stack, 0);
   if (n->flags & ANONYMOUS)
   {
      strcpy(stack, " Posted anonymously.\n");
      stack = strchr(stack, 0);
   }
   if (n->flags & NOT_READY)
   {
      strcpy(stack, " Not ready flag set.\n");
      stack = strchr(stack, 0);
   }
   if (n->flags & SUPRESS_NAME)
   {
      strcpy(stack, " Name suppressed.\n");
      stack = strchr(stack, 0);
   }
   sprintf(stack, " Next link -> %d\n", n->next_sent);
   stack = end_string(stack);
   pager(p, oldstack, 0);
   stack = oldstack;
}



/* remove a note from */

void            dest_note(player * p, char *str)
{
   note           *n;

   if (!*str)
   {
      tell_player(p, " Format: dest_note <number>\n");
      return;
   }
   n = find_note(atoi(str));
   if (!n)
   {
      tell_player(p, " Can't find note with that number.\n");
      return;
   }
   remove_any_note(n);
   tell_player(p, " Note removed\n");
}



/* relink emergancy command */

void            relink_note(player * p, char *str)
{
   char           *to;
   int             id1, id2;
   note           *n;
   to = next_space(str);
   *to++ = 0;
   id1 = atoi(str);
   id2 = atoi(to);
   /*
    * if (!id1 || !id2) { tell_player(p,"Format : relink <number>
    * <number>\n"); return; }
    */
   n = find_note(id1);
   if (!n)
   {
      tell_player(p, " Can't find first note\n");
      return;
   }
   n->next_sent = id2;
   tell_player(p, " next_sent pointer twiddled.\n");
   return;
}


/* recount emergancy command */

void            recount_news(player * p, char *str)
{
   int             article;
   note           *scan;
   news_count = 0;
   scan = find_note(news_start);
   if (scan)
   {
      article = scan->next_sent;
      for (; scan; article = scan->next_sent, news_count++)
      {
    scan = find_note(article);
    if (!scan)
       break;
      }
      news_count++;
   }
   tell_player(p, " Tis Done ...\n");
}