dyrt/
dyrt/bin/
dyrt/data/MAIL/
dyrt/data/WIZ_ZONES/
dyrt/include/machine/
dyrt/src/misc/cpp/
/* iDIRT Message System Version 1.03/PE (Public Edition)
 * by Illusion (1995)
 *
 * Derived from the DYRT mailer from DYRT 1.1c
 * by Thrace [Valentin Popescu] (vpopesc@calstatela.edu)
 * -------------------------------------------------------------------------
 *                       *** NOTE ***
 * DO NOT CHANGE THE VERSION INFORMATION IN THIS MAILER!!
 * IF YOU CHANGE THE MAILER CONSIDERABLY, GIVE CREDIT TO
 * BOTH ILLUSION AND THRACE. THRACE WORKED HARD ON THIS
 * MAILER, AND ILLUSION WORKED HARD TO DEBUG IT, THEY
 * DESERVE CREDIT FOR DOING SO.
 *
 * Basically what I did was take this source code from the Dyrt 1.1c 
 * source code and started rewriting it and trying to make it work with 
 * the iDIRT (or any other Dirt) code. I had to fix a few bugs that the 
 * mailer had which were:
 *
 * 1.00
 * ----
 *	- A bug in the dump_headers() function that made the whole MUD crash 
 *        when you scan your mail when you typed 'mail' to read it (read
 *        the headers). What was happening was this: the MUD was calling a 
 *        bflush() that seemed to be crashing the MUD. Removing this bflush() 
 *        still killed it. After trying out many different techniques I 
 *        figured (basically guessed *grin*) that the error was coming from 
 *        the bflush() calling fflush() when the mail file was at the EOF (or
 *        past it, that was my idea). So I had the dump_headers() check to see
 *        if the pointer that accesses the mail file is located past the EOF 
 *        or not. Well, this fixed it and it no longer crashes.
 *
 *     - A bug in the write_mail() function that didn't let you abort the 
 *       message if you had a blank subject. 
 *
 *     - Fixed a bug in the mail menu that would cause it to stay in an 
 *       infinite mail loop. This bug was caused by a user running the 
 *       mailer from within the mailer again (*mail).
 *
 *     - Players can now no longer alias from the mail menu.
 *
 * 1.01
 * ----
 *     - Fixed the bug that allowed players to put the EOM text in the 
 *       message. Change the EOM_MARKER #define to whatever you see fit
 *	 and keep it a secret so that nobody can kill players mail files.
 *	 You may even wish to use control characters for the EOM marker, 
 *	 your decision.
 *
 *     - Added some color to the mailer.
 *
 * 1.02
 * ----
 *     - Fixed a bug in which if users entered any format character into 
 *       the subject, it crashed the MUD.
 *
 * 1.03
 * ----
 *     - Fixed a bug in which if users entered any format character into
 *	 the text, it crashed the MUD.
 *
 *     - Now the mailer saves the player's prompt before entering the mailer.
 *   
 *     - Released a Public Version removing the iDIRT specific code.
 *
 * 1.04
 * ----
 *     - Added multiple recipients
 *     - Added forwarding
 */

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include "kernel.h"
#include "objects.h"
#include "mobiles.h"
#include "mobile.h"
#include "locations.h"
#include "sflags.h"
#include "pflags.h"
#include "mflags.h"
#include "cflags.h"
#include "quests.h"
#include "sendsys.h"
#include "levels.h"
#include "parse.h"
#include "exec.h"
#include "rooms.h"
#include "mail.h"
#include "bprintf.h"
#include "stdinc.h"

/* Function Prototypes
Boolean concat_files (char *to, char *from);
*/

void cc_mail( char *u );

/* Mail system defines */
#define EOM_MARKER 	"\001\002\003"
#define MAX_LETTERS	150
#define READ		1
#define WRITE		0
#define SUBJECT		"&+CSubject: &+w"
#define CC		"&+CCC: &+w"
#define SENT		"\n&+CMessage sent.\n"
#define MAILP		"&+c:&+C> &+w"
#define PROMPT		"&+C[&+wMail&+C] &+b:&+w "
#define NOTICE1		"\
&+wEnter your message. To save your message type &+W'=s' &+wor &+W'**' &+won \
a blank line.\nTo abort your message type &+W'=a' &+wor &+W'*abort' &+won \
a blank line.\n"

int count;

/* mailcom(): Main loop for the mail program. This function checks to see if
 * the person trying to access the mailer is aliased, and then checks if a
 * player's name was passed through the command line and will startup the
 * mail sender if they are found.
 */
void mailcom (void)
{
  PERSONA p;

  /* Do not let aliased players use the mailer */
  if (cur_player->aliased || cur_player->polymorphed != -1)
    {
      bprintf ("You cannot use the mailer while you are aliased.\n");
      return;
    }
  /* no mailing while in jail. */
  if (ltstflg(ploc(mynum), LFL_JAIL) && plev(mynum) < LVL_ARCHWIZARD)
    {
      bprintf ("Sorry... we jailed the postman also...\n");
      return;
    }
  /* Save the mail header */
  cur_player->mails.old_handler = cur_player->inp_handler->inp_handler;

  /* Save the prompt */
  strcpy(cur_player->mails.prompt, cur_player->prompt);

  if (brkword () != -1) {
    if (!getuaf (wordbuf, &p)) {
      bprintf ("&+WPlayer not found in database.\n");
      return;
    }
    strcpy (cur_player->mails.w_name, p.p_name);
    bprintf ("&+CTo: &+w%s\n", cur_player->mails.w_name);

    bprintf (SUBJECT);
    cur_player->mails.old_handler = cur_player->inp_handler->inp_handler;
    strcpy (cur_player->cprompt, SUBJECT);

    replace_input_handler(write_mail);
    return;
  }
  read_mail (pname(mynum));
  return;
}

/* write_mail(): Gets the message subject and checks to see if to abort 
 * the message (if subject is a blank) and creates the header in the 
 * temporary mail file. Also, now you can abort the message here (before
 * it just asked you for the subject again).
 */
void write_mail (char *u)
{
  if (EMPTY (u) || u[0] == '\n') {
    bprintf ("&+RMessage Aborted.\n");
    replace_input_handler (cur_player->mails.old_handler);
    strcpy(cur_player->prompt, cur_player->mails.prompt);
    strcpy (cur_player->cprompt, build_prompt (mynum));
    bprintf("\r%s", cur_player->cprompt);
    setup_globals (mynum);
    return;
  } else {
    bprintf (CC);
    open_mail (WRITE, True);
    if (cur_player->mails.write == (FILE *) 0)
      return;
    fprintf (cur_player->mails.write, "%s\n%s\n%d\n", pname (mynum), u, time (0));
    fflush (cur_player->mails.write);
    strcpy (cur_player->cprompt, CC);
    replace_input_handler (cc_mail);
  }
  return;
}

void cc_mail( char *u ) {
  if( EMPTY( u ) || u[0] == '\n' ) {
    cur_player->mails.cc_list[ 0 ] = '\0';
  } else {
    strncpy( cur_player->mails.cc_list, u, MAX_COM_LEN );
  }
  bprintf(NOTICE1);
  bprintf(MAILP);
  strcpy( cur_player->cprompt, MAILP );
  replace_input_handler( input_mail );
}

/* input_mail(): Handles the input of the message text */
void input_mail (char *x)
{
  int p;
  char to[256];
  char from[256];
  Boolean abort = False;

  if (!strcasecmp(x,"=s") || !strcasecmp(x,"=a") ||
      !strcasecmp(x,"**") || !strcasecmp(x,"*abort")) {
    fprintf( cur_player->mails.write, "\n**CC:%s", cur_player->mails.cc_list );
    fprintf (cur_player->mails.write, "\n%s\n", EOM_MARKER);
    close_mail (WRITE);
    strcpy(cur_player->prompt, cur_player->mails.prompt);
    strcpy (cur_player->cprompt, (char *) build_prompt (mynum));
    sprintf (to, "%s/%s", MAIL_DIR, cur_player->mails.w_name);
    sprintf (from, "%s/%s.from-%s", MAIL_DIR, cur_player->mails.w_name, pname (mynum));

    if (!strcasecmp(x,"=a") || !strcasecmp(x, "*abort")) {
      bprintf("&+RMail aborted.\n");
      abort = True;
    } else {
      PERSONA pe;
      char *pCCName;

      bprintf( "Sending mail to %s\n", cur_player->mails.w_name );
      concat_files (to, from);

      /** Send mail to the cc list as well... */

      for( pCCName = strtok( cur_player->mails.cc_list, " ," );
           pCCName != NULL; pCCName = strtok( NULL, " ," ) ) {
        if (!getuaf (pCCName, &pe)) {
          bprintf ("&+WPlayer %s not found in database.\n", pCCName );
        } else {
          sprintf( to, "%s/%s", MAIL_DIR, pe.p_name );
          bprintf( "Sending mail to %s\n", pe.p_name );
          concat_files( to, from );
          if( ( p = fpbns( pe.p_name ) ) >= 0 && ( p < max_players ) ) {
            sendf (p, 
	           "&+C* &+WYou have new mail from %s &+C*\n", pname (mynum));
          }
        }
      } 
    }

    unlink (from);

    if ((p = fpbns (cur_player->mails.w_name)) >= 0) {
      if (p < max_players && !abort)
        sendf (p, "&+C* &+WYou have new mail from %s &+C*\n", pname (mynum));
    }

    if (cur_player->mails.level)
      replace_input_handler (mail_menu);
    else {
      replace_input_handler (cur_player->mails.old_handler);
      strcpy(cur_player->prompt, cur_player->mails.prompt);
      strcpy (cur_player->cprompt, build_prompt (mynum));
      bprintf("\r%s", cur_player->cprompt);
    }
    return;
  }

  fprintf (cur_player->mails.write, "%s\n", x);
  bprintf (MAILP);
  fflush (cur_player->mails.write);
  return;
}

/* read_mail(): Lets the player read a message */
void read_mail (char *u)
{
  strcpy (cur_player->mails.r_name, pname (mynum));

  open_mail (READ, False);
  if (!(cur_player->mails.read)) {
    bprintf ("&+CSorry, you have no mail.\n");
    return;
  }

  strcpy (cur_player->cprompt, PROMPT);
  dump_headers (cur_player->mails.read);
  bprintf (PROMPT);
  replace_input_handler (mail_menu);
  return;
}

/* dump_headers(): Displays the message headers for your message file. 
 * There was a bug with the original code that caused the MUD to crash when
 * the bflush() was called and if the EOF of the mail file had been reached.
 * This was fixed by Illusion for iDIRT.
 */
void dump_headers (FILE * m)
{
  char   b[MAX_COM_LEN];
  char   from[MAX_COM_LEN];
  time_t time_sent;
  char   s[MAX_COM_LEN];
  count = 1;

  rewind (cur_player->mails.read);

  bprintf ("&+C No.  From         Date Sent                 Subject\n");
  bprintf ("&+b----- ------------ ------------------------  ---------------------------\n");

  while (!feof (cur_player->mails.read)) {
    fgets (b, MAX_COM_LEN, cur_player->mails.read);	/* From Field */
    if (feof(cur_player->mails.read)) 
      return;
    b[strlen (b) - 1] = '\0';
    strncpy (from, b, 12);

    fgets (s, MAX_COM_LEN, cur_player->mails.read);	/* Subject Field */

    fgets (b, 40, cur_player->mails.read);		/* Date Send Field */
    time_sent = atol (b);

    for (;;) {						/* Find the EOF text */
      if (feof (cur_player->mails.read))
        break;
      fgets (b, MAX_COM_LEN, cur_player->mails.read);
      if (strncmp (b, EOM_MARKER, 3) == 0)
	break;
    }

    if (strlen (s) > 27) {				/* Dump Header */
      s[24] = ' ';
      s[25] = s[26] = s[27] = '.';
      s[28] = '\n';
      s[29] = '\0';
    }
    bprintf("&+w[%3.3d] %-12.12s %24.24s  %s", 
            count, from, ctime (&time_sent),
	    (cur_player->mails.data)[count - 1] ? "&+R[ To Be Deleted ]\n" : s);
      
    count++;
    if (!feof(cur_player->mails.read)) /* [iDIRT] Bug fix. The MUD used */
      bflush();                        /* to crash here until the mail  */
    }                                  /* file was checked for EOF.     */
    return;
}

/* mail_menu(): The menuing system for the mailer */
void mail_menu (char *u)
{
  time_t tim;
  char   buffer[MAX_COM_LEN + 5];
  int    quitting_mail = False;
  int    do_cc = False;

  cur_player->inmailer = True;

  if (cur_player->mails.level) {
    bprintf (PROMPT);
    cur_player->mails.level = 0;
  }

  strcpy (cur_player->cprompt, PROMPT);

  if (isupper (u[0]))
    u[0] = tolower (u[0]);

  switch (u[0]) {
    case 'R': do_cc = True;
    case 'r':			/* Reply */
      tim = time (0);
      if (EMPTY (cur_player->mails.last))
	bprintf ("&+WYou haven't read any messages yet.\n");
      else {
	if (strncasecmp (cur_player->mails.subject, "Re: ", 4) != 0)
	  sprintf (buffer, "&+CRe: &+w%s", cur_player->mails.subject);
	else
	  strcpy (buffer, cur_player->mails.subject);

	strcpy (cur_player->mails.w_name, cur_player->mails.last);
	open_mail (WRITE, True);
	if (!cur_player->mails.write) {
          close_mail (READ);
	  return;
	}

	bprintf ("&+CTo: &+w%s\n&+CSubject: &+w%s\n", 
		  cur_player->mails.last, buffer);
        bprintf (CC);
        fprintf (cur_player->mails.write, "%s\n%s%d\n", 
                 pname (mynum), buffer, tim);

        cur_player->mails.level = 1;
        replace_input_handler (cc_mail);
	return;
      }
    break;
    
    case 'd':			/* Delete */
      if (u[1] != ' ' || !isdigit (u[2]))
	bprintf ("\nUsage: d [message number]\n");
      else
						/*or statement added by jwn*/
        if ((atoi(&u[2]) - 1) > MAX_LETTERS || atoi(&u[2]) > (count-1) )  
          {
          bprintf("Message number cannot be greater than %d\n", (count-1));
          mudlog("Bad &+rMAIL&* by %s, Number was %d",
                 pname(mynum), (atoi(&u[2]) - 1));
          bprintf (PROMPT);
          return;
          }     
	if((atoi(&u[2]) - 1) < 0) {
	  bprintf("Message number must be at least 1.\n");
	  mudlog("Bad &+rMAIL&* by %s, Number was %d",
		 pname(mynum), (atoi(&u[2]) - 1));
	  bprintf(PROMPT);
	  return;
	}

	(cur_player->mails.data)[atoi (&u[2]) - 1] = True;
    break;

    case 'u':			/* Undelete */
      if (u[1] != ' ' || !isdigit (u[2]))
	bprintf ("\nUsage: u [message number]\n");
      else
        if ((atoi(&u[2]) - 1) > MAX_LETTERS || atoi(&u[2]) > (count-1) )  
          {
          bprintf("Message number cannot be greater than %d\n", MAX_LETTERS);
          mudlog("Bad &+rMAIL&* by %s, Number was %d",
                 pname(mynum), (atoi(&u[2]) - 1));
          bprintf (PROMPT);
          return;
          }     
	if((atoi(&u[2]) - 1) < 0) {
	  bprintf("Message number must be at least 1.\n");
	  mudlog("Bad &+rMAIL&* by %s, Number was %d",
		 pname(mynum), (atoi(&u[2]) - 1));
	  bprintf(PROMPT);
	  return;
	}
	(cur_player->mails.data)[atoi (&u[2]) - 1] = False;
    break;
    
    case '*':			/* Game command */
      gamecom (&u[1], False);
    break;
 
    case 'i':			/* Info */
      bprintf ("\001f%s\003\n", "mail.info");
    break;
    
    case '?':
      bprintf ("\001f%s\003\n", "mail.help");
    break;
    
    case 'h':
      bprintf ("\n");
      dump_headers (cur_player->mails.read);
    break;

    case 'v':
      bprintf("iDIRT Mailer Version 1.04/PE (Public Edition)\n");
      bprintf("1994, 95 by Illusion\n\n");
      bprintf("Derived from the DYRT mailer from DYRT 1.1c\n");
      bprintf("by Valentin Popescu (vpopesc@calstatela.edu)\n");
      bprintf("Multiple recepient & forwarding capabilities added 1998\n" );
      bprintf(" by DuRandir\n" );
    break;

    case '\0':
    
    case '\n':
    break;
    
    case 'x':
    
    case 'q':			/* quit the mailer */
      replace_input_handler (cur_player->mails.old_handler); 
      close_mail (WRITE);
      delete_mail ();
      close_mail (READ);
      strcpy(cur_player->prompt, cur_player->mails.prompt);
      strcpy (cur_player->cprompt, build_prompt (mynum));
      setup_globals (mynum);
      cur_player->inmailer = False;
      quitting_mail = True;
    break;
    
    default:
    
    /* Check to see if a number was inputed */
    {
      int number;
      number = atoi (u);
      if (number < 1 || number > MAX_LETTERS) {
        bprintf ("\nUnknown command. Type '?' for help.\n");
        break;
      }
      read_msg (number);
    }
    break;
  }

  if (!quitting_mail) {
    bprintf (PROMPT);
  } else {
    strcpy(cur_player->prompt, cur_player->mails.prompt);
    bprintf ("%s", (char *) build_prompt (mynum));
  }

  return;
}

/* read_msg(): Reads in a message number, taking the number as a parameter */
void read_msg (int num)
{
  /* to get to a particular message num, we must skip exactly num - 1 EOFs */

  char   buffer[MAX_COM_LEN];
  time_t time_sent;

  if (num <= 0 || num > MAX_LETTERS)
    return;

  rewind (cur_player->mails.read);

  for (num = num - 1; num != 0; num--) {
    for (;;) {
      fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
        if (feof (cur_player->mails.read))
          break;
        if (strncmp (buffer, EOM_MARKER, 3) == 0)
	    break;
    }
  }

  /* Now we should be at the right place, and we dump the letter */

  fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
  if (feof (cur_player->mails.read)) {
    bprintf ("\n&+WMessage out of range.\n", num);
    return;
  }
  buffer[strlen (buffer) - 1] = '\0';
  strncpy (cur_player->mails.last, buffer, 12);

  if ((cur_player->mails.data)[num] == True)
    bprintf ("\n&+R* Note: This letter is marked for deletion *\n");

  /* dump the header */
  bprintf ("\n&+CFrom: &+w%s\n", buffer);
  fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
  strncpy (cur_player->mails.subject, buffer, MAX_COM_LEN);
  bprintf ("&+CSubject: &+w%s", buffer);
  fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
  time_sent = atoi (buffer);
  bprintf ("&+CSent: &+w%s", ctime (&time_sent));
  bprintf ("\n");

  /* Dump the message body */

  for (;;) {
    fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
    if (strncmp (buffer, EOM_MARKER, 3) == 0)
      break;
    if( buffer[0] == '*' && buffer[1] == '*' ) {
      bprintf( "%s", buffer + 2 );
      /* strncpy (cur_player->mails.cc_list, buffer, MAX_COM_LEN); */
    } else { 
      bprintf("%s", buffer);
    }
    if (feof (cur_player->mails.read))
      break;
  }
  bflush ();
  return;
}

/* open_mail(): Handles the opening of a mail file. */
void open_mail (char mode, Boolean complain)
{
  char buf[1024];
  FILE *tf;
  Boolean fail = False;

  switch (mode) {
    case READ:
      sprintf (buf, "%s/%s", MAIL_DIR, cur_player->mails.r_name);
      tf = fopen (buf, "r");
      if (!tf)
        fail = True;
      else
        cur_player->mails.read = tf;
      break;
    case WRITE:
      sprintf (buf, "%s/%s.from-%s", MAIL_DIR, cur_player->mails.w_name, pname (mynum));

      tf = fopen (buf, "w");
      if (!tf) {
        progerror ("mail-write");
        fail = True;
      }
      cur_player->mails.write = tf;
      break;
    }

  if (complain && fail) {
    bprintf ("We are experiencing technical difficulties. Try again later.\n");
    progerror ("open_mail");
    replace_input_handler (cur_player->mails.old_handler);
  }
  return;
}

/* close_mail(): Handles the closing of the mail files */
void close_mail (char mode)
{
  switch (mode) {
    case READ:
      if (cur_player->mails.read)
        fclose (cur_player->mails.read);
      cur_player->mails.read = 0;
      break;
    case WRITE:
      if (cur_player->mails.write)
        fclose (cur_player->mails.write);
      cur_player->mails.write = 0;
      break;
    }
}

/* delete_mail(): Deletes messages that have been marked for removal */
void delete_mail (void)
{
  char tempfile[128];
  char tempfile2[128];
  char b[MAX_COM_LEN];
  FILE *fout;
  FILE *fin;
  struct stat s;
  register int t;
  int pid;
  extern int errno;

  pid = getpid ();

  sprintf (tempfile, "%s/TEMP.%s.%d", MAIL_DIR, pname (mynum), pid);
  sprintf (tempfile2, "%s/%s", MAIL_DIR, pname (mynum));
  (void) unlink (tempfile);

  /* copy the file, filtering out the offending message */

  fout = fopen (tempfile, "w");
  if (!fout) {
    bprintf ("System error detected, mail NOT deleted.\n");
    progerror ("delete_file write");
    return;
  }

  fin = fopen (tempfile2, "r");
  if (!fin) {
    bprintf ("System error detected, mail NOT deleted.\n");
    progerror ("delete_file read");
    fclose (fout);
    unlink (tempfile);
    errno = 0;		/* unlink may cause an errorr */
    return;
  }

  /* now we have both files open */

  for (t = 0; t != MAX_LETTERS; t++) {

    if (feof (fin) != 0)
      break;

    /* Should we delete messages? */
    if ((cur_player->mails.data)[t] != 0) { 	/* Yes, delete messages */
      while (1) {				/* Search for 'EOF'     */
        bzero (b, MAX_COM_LEN);
	fgets (b, MAX_COM_LEN, fin);
	if (feof (fin) != 0)
          break;
	if (strncmp (b, EOM_MARKER, 3) == 0)
          break;
      }
    } else {					/* No, do not delete   */
      while (1) {
        bzero (b, MAX_COM_LEN);
        fgets (b, MAX_COM_LEN, fin);
        fprintf (fout, "%s", b);
        if (feof (fin) != 0)
          break;
        if (strncmp (b, EOM_MARKER, 3) == 0)
          break;
      }
    }
    /* unset the marker */
    (cur_player->mails.data)[t] = 0;
  }

  rewind (fin);

  fclose (fin);
  fclose (fout);

  /* copy the file over */
  unlink (tempfile2);
  rename (tempfile, tempfile2);

  if (stat (tempfile2, &s) != -1)
    if (s.st_size < 4)
      unlink (tempfile2);

  return;
}

/* check_files(): Checks if any files have been updated since login. A 
 * problem with iDIRT (and any DIRT for that matter) has been fixed here and 
 * it uses all of the correct DIRT user variables now. 
 */
void check_files (void)
{
  PERSONA me;
  struct stat s;
  time_t last;
  char buff[512];

  getuaf(pname(mynum), &me);
  last = me.p_last_on;

  sprintf (buff, "%s/%s", MAIL_DIR, pname (mynum));

#if 0	/* If you do have bulletins, this code is helpful */
  if (stat(BULLETIN_DIR"/"MORTALBULL, &s) != -1) {
    if (s.st_mtime > last)
      bprintf ("New bulletin as of %19.19s.\nType 'bulletin' to read it.\n", ctime (&s.st_mtime));
  }

  if ((stat (BULLETIN_DIR"/"WIZARDBULL, &s) != -1) && plev (mynum) >= LVL_WIZARD) {
    if (s.st_mtime > last)
      bprintf ("New Wizard bulletin as of %19.19s.\nType 'wbull' to read it.\n", ctime (&s.st_mtime));
  }

  if ((stat (BULLETIN_DIR"/"ARCHWIZBULL, &s) != -1) && plev (mynum) >= LVL_ARCHWIZARD) {
    if (s.st_mtime > last)
      bprintf ("New Arch-Wizard+ bulletin as of %19.19s.\nType 'abull' to read it.\n", ctime (&s.st_mtime));
  }

  if ((stat (BULLETIN_DIR"/"DEMIGODBULL, &s) != -1) && plev (mynum) >= LVL_DEMI) {
    if (s.st_mtime > last)
      bprintf ("New DemiGod+ bulletin as of %19.19s.\nType 'gbull' to read it.\n", ctime (&s.st_mtime));
  }
#endif

  if (stat (buff, &s) != -1) {
    if (s.st_size > 3) {
      if (s.st_mtime > last)
        bprintf ("&+WYou have new mail.\n");
      else
        bprintf ("&+WYou have old mail in your mailbox.\n");
    }
  }
  bprintf("\n");
}

/* concat_files(): Puts two files together (like the 'cat' shell command)
Boolean concat_files (char *to, char *from)
{
  FILE *fin;
  FILE *fout;
  char x[1024];

  fin = fopen (from, "r");
  if (!fin) {
    fprintf (stderr, "error on opening source file for concat_files.\n");
    return (False);
  }
  fout = fopen (to, "a+");
  if (!fout) {
    fprintf (stderr, "error on opening destination file for concat_files.\n");
    fclose (fin);
    return (False);
  }

  while (!feof (fin)) {
    fgets (x, 1024, fin);
    fprintf (fout, "%s", x);
    x[0] = '\0';
  }
  fclose (fin);
  fclose (fout);
  return (True);
}
*/