dyrt/
dyrt/bin/
dyrt/data/MAIL/
dyrt/data/WIZ_ZONES/
dyrt/include/machine/
dyrt/src/misc/cpp/
#include <unistd.h>
#include <stdlib.h>
#include "kernel.h"
#include "objects.h"
#include <malloc.h>
#include "stdinc.h"
/* ==================== Define some things about them ====================== */

#define DELETE_LEVEL   LVL_GOD	/* Level when can delete any mess    */
#define OWNER_CAN_DEL		/* Define if can delete what I wrote */
#define MAX_SUBJ_LEN   45	/* Maximum chars allowed in subject  */
#define MAX_MESS_LEN   200	/* Max lines per mess. (Same as mail) */
#define PBB_FILE       "bboard_ply"	/* Name of file for player bboard    */
#define WBB_FILE       "bboard_wiz"	/* Name of file for wizard bboard    */
#define AWB_FILE       "bboard_awiz"	/* Arch wizard board file name.      */
#define GDB_FILE       "bboard_god"	/* God board file name.              */

/* The instructions, printed at top of board when it is examined by a player. */

static char *Board_instr =
"You look closely at the board which has the following intructions written\n \
on it.  Commands: WRITE <heading> , MESSAGE #, ERASE #\n";

/* ==================== Type definitions for the boards ==================== */

typedef struct _mess *PMES;

typedef struct _mess
{
  char owner[PNAME_LEN + 1];	/* Name of player who wrote message    */
  char *date;			/* Ascii string for date & time        */
  char *message;		/* Pointer to the message.             */
  char *header;		/* Heading for the message.            */
  PMES next;			/* Pointer to next message on board    */
}
MESS;

typedef struct _board
{
  int nomess;			/* Number of messages on the board      */
  int level;			/* Min level to be able to read it.     */
  char file[31];		/* Name of the file for storage         */
  PMES last;			/* Last message. Just for fast updates  */
  PMES messages;		/* Pointer to first message.            */
}
BOARD;

/* =============== Function prototypes for use in board.c only ============= */
/* These are declared static because it is IMPORTANT that no outside files   */
/* call these. Reason: The rest of the mud does not know the types BOARD and */
/* MESS. This is to make board code easier to add to a mud.                  */

static BOARD *present_board ();
static void load_board (BOARD * b);
static void update_board (BOARD * b);
static Boolean is_owner (PMES pm, int pl);

/* ================ Define the objects that are to be boards =============== */

#define PBBOARD           1
BOARD pbb;
static int bboards[] =
{
  OBJ_LIMBO_BBOARD,
  -1
};

#define WBBOARD           2
BOARD wbb;
static int wbboards[] =
{
  OBJ_LIMBO_WIZ_BBOARD,
  -1
};

#define AWZBBOARD         3
BOARD abb;
static int abboards[] =
{
  OBJ_LIMBO_AWIZ_BBOARD,
  -1
};

#define GDBBOARD 	  4
BOARD gbb;
static int gbboards[] =
{
  OBJ_LIMBO_GOD_BBOARD,
  -1
};

/*
 * **  Boot the bulletin board files. NOTE, unlike most bootstrap files, the 
 * **  name of the file to boot is NOT contained in bootstrap file.
 * **            YOU MAY WANT TO EDIT SETUP VALUES HERE.
 */
void 
boot_bboards ()
{
  pbb.nomess = 0;
  strcpy (pbb.file, PBB_FILE);
  pbb.level = 0;
  
  wbb.nomess = 0;
  strcpy (wbb.file, WBB_FILE);
  wbb.level = LVL_APPREN;
  
  abb.nomess = 0;
  strcpy (abb.file, AWB_FILE);
  abb.level = LVL_ARCHWIZARD;
  
  gbb.nomess = 0;
  strcpy (gbb.file, GDB_FILE);
  gbb.level = LVL_GOD;
  
  /* boot mortal board first */
  load_board (&(pbb));
  /* boot the wizard board now */
  load_board (&(wbb));
  /* boot the arch_wizard board now */
  load_board (&(abb));
  /* boot the god board now */
  load_board (&(gbb));
  return;
}

/*
 * **  returns 0 id ob is NOT a bulletin board, and returns appropriate
 * **  number to indicate which board object represents. Also note that 
 * **  if (isa_board(ob)) will work fine.
 */
int 
isa_board (int ob)
{
  int i;
  
  /* Is it a player bboard */
  for (i = 0; bboards[i] != -1; i++)
    {
      if (bboards[i] == ob)
	return PBBOARD;
    }
  
  /* is it a wiz bboard */
  for (i = 0; wbboards[i] != -1; i++)
    {
      if (wbboards[i] == ob)
	return WBBOARD;
    }
  
  /* is it an arch_wizard bboard */
  for (i = 0; abboards[i] != -1; i++)
    {
      if (abboards[i] == ob)
	return AWZBBOARD;
    }
  /* is it a god bboard */
  for (i = 0; gbboards[i] != -1; i++)
    {
      if (gbboards[i] == ob)
	return GDBBOARD;
    }
  return 0;
}

/*
 * **  Simply returns the address of the first board it can find in room with
 * **  mynum. Mortal boards first. Null if none. This should NOT be called outside
 * **  this module, the mud does not know what a BOARD is. :)
 */
static BOARD *
present_board ()
{
  int i;
  
  for (i = 0; bboards[i] != -1; i++)
    {
      if (ploc (mynum) == oloc (bboards[i]))
	return &(pbb);
    }
  
  for (i = 0; wbboards[i] != -1; i++)
    {
      if (ploc (mynum) == oloc (wbboards[i]))
	return &(wbb);
    }
  
  for (i = 0; abboards[i] != -1; i++)
    {
      if (ploc (mynum) == oloc (abboards[i]))
	return &(abb);
    }
  
  for (i = 0; gbboards[i] != -1; i++)
    {
      if (ploc (mynum) == oloc (gbboards[i]))
	return &(gbb);
    }
  return ((BOARD *) NULL);
}

/*
 * **  Displays intructs on how to use the board as well as list of messages
 */

void 
lat_board (int ob)
{
  BOARD *brd = NULL;
  PMES m = NULL;
  int i = 1;
  
  switch (isa_board (ob))
    {
    case 0:			/* NOT a board */
      return;
      
    case PBBOARD:
      brd = &(pbb);
      break;
      
    case WBBOARD:
      brd = &(wbb);
      break;
      
    case AWZBBOARD:
      brd = &(abb);
      break;
      
    case GDBBOARD:
      brd = &(gbb);
      break;
    }
  if (brd->level > plev (mynum))
    {
      bprintf ("You look closely but can not understand the mystical writings\n");
      return;
    }
  
  bprintf ("%s", Board_instr);
  bprintf ("There are %d messages on this board\n\n", brd->nomess);
  
  m = brd->messages;
  while (m != NULL)
    {
      bprintf ("%2d %-*s %-*s \n", i, PNAME_LEN, m->owner, MAX_SUBJ_LEN, m->header);
      i++;
      m = m->next;
    }
  return;
}

/*
 * **  dump the boards to their files. See load_board for file format.
 */
static void 
update_board (BOARD * b)
{
  FILE *f = NULL;
  PMES m = NULL;
  
  if (b == (BOARD *) NULL)
    {
      mudlog ("BB ERROR: update null board");
      return;
    }
  if ((b->file == (char *) NULL) || (b->file[0] == '\0'))
    {
      mudlog ("BB ERROR: update to null file");
      return;
    }
  if ((f = fopen (b->file, "w")) == (FILE *) NULL)
    {
      mudlog ("BB ERROR: Could not open file %s for write", b->file);
      return;
    }
  
  fprintf (f, "%d\n", b->nomess);
  m = b->messages;
  while (m != NULL)
    {
      fprintf (f, "%s\n%s\n%s\n", m->owner, m->date, m->header);
      fprintf (f, "%d\n%s\n^\n", strlen (m->message), m->message);
      m = m->next;
    }
  fclose(f);
  if (f) fclose (f);
  return;
}

/*
 * **  Reads the file b->file and loads all the messages into memory that are in 
 * **  the file. FORMAT of the files is: (ignore the first ** each line)
 * **
 * **  <#num messages><NL>
 * **  <owner><NL>
 * **  <date><NL>
 * **  <header><NL>
 * **  <#strlen of the message><NL>
 * **  <message><NL><^><NL>
 * **  <owner><NL> ......
 */
static void 
load_board (BOARD * b)
{
  FILE *f = NULL;
  int i = 0, j = 0, size, c;
  PMES m = NULL;
  char buf[81];
  char *p;
  
  if ((f = fopen (b->file, "r")) == NULL)
    {
      /*    printf("BB ERROR: can't open %s.\n",b->file); */
      b->nomess = 0;
      /*fclose (f); */
      if (f) fclose (f);
      return;
    }
  fscanf (f, "%d\n", &i);
  b->nomess = i;
  b->messages = NULL;
  for (; i > 0; i--)
    {
      m = (PMES) xmalloc (1, sizeof (MESS));
      m->next = (PMES) NULL;
      
      /* Load the message */
      fscanf (f, "%s\n", m->owner);
      fgets (buf, 81, f);
      if ((p = strchr (buf, '\n')) != NULL)
	{
	  *p = '\0';
	}
      m->date = COPY (buf);
      fgets (buf, 81, f);
      if ((p = strchr (buf, '\n')) != NULL)
	{
	  *p = '\0';
	}
      m->header = COPY (buf);
      fscanf (f, "%d\n", &j);
      j += 10;			/* Just in case */
      m->message = (char *) xmalloc (1, j);
      m->message[0] = '\0';
      size = 0;
      while (((c = getc (f)) != EOF) && (size < j - 1))
	{
	  if (c == '\n')
	    {
	      c = getc (f);
	      if (c == '^')
		{
		  m->message[size] = '\0';
		  size = j + 1;
		}
	      else
		{
		  m->message[size++] = '\n';
		  ungetc (c, f);
		}
	    }
	  else
	    {
	      m->message[size++] = (char) c;
	    }
	}
      
      /* Check errors and report/recover */
      if (c == EOF)		/* Eeek!! */
	{
	  printf ("BB ERROR: Eeek!! %s ended before expected\n", b->file);
	  i = -1;
	}
      else if (size < j)
	{
	  m->message[size] = '\0';
	}
      
      /* Copy message to the board list */
      if (b->messages == NULL)	/* special first case */
	{
	  b->messages = m;
	}
      else
	{
	  b->last->next = m;
	}
      b->last = m;		/* Remember last message */
    }
  return;
}

/*
 * **  The command that actually reads message from the board for the players.
 * **  VERB: message
 */
void 
do_read ()
{
  BOARD *b;
  PMES m;
  int num = 0, i;
  
  b = present_board ();
  if (b == NULL)		/* No board present               */
    {
      bprintf ("I don't see the board here.\n");
      return;
    }
  if ((brkword () == -1) && !isdigit (wordbuf[0]))
    {
      bprintf ("Which board would that message be written on then?\n");
      return;
    }
  
  if (b->level > plev (mynum))	/* Not able to read cause of level */
    {
      bprintf ("You look closely but can not understand the mystical writings\n");
      return;
    }
  
  num = atoi (wordbuf);
  if ((num > b->nomess) || (num < 1))	/* Number out of valid range      */
    {
      bprintf ("That message only exist in your imagination\n");
      return;
    }
  num--;
  for (i = 0, m = b->messages; (i < num) && (m != NULL); m = m->next, i++)
    ;
  if (m == NULL)
    {
      bprintf ("The message just vanished.\n");
      mudlog ("BB ERROR: could not find message in do_read()");
      return;
    }
  bprintf ("Message by:%s (%s)\nSubject:%s\n",
	   m->owner, m->date, m->header);
  bprintf ("%s\n", m->message);
  return;
}

/*
 * **  Erase a message from the board in this room. Checks for permission.
 * **  You can erase if you can read the board and you wrote the message or
 * **  you are at or above the level defined above where you can erase any message
 * **  You must still be able to see the board of course.
 */
void 
do_erase ()
{
  BOARD *b;
  PMES m, m2;
  int num = 0, i;
  
  if ((b = present_board ()) == NULL)
    {
      bprintf ("Erase a message from what?\n");
      return;
    }
  
  if ((brkword () == -1) && !isdigit (wordbuf[0]))
    {
      bprintf ("Which board would that message be written on then?\n");
      return;
    }
  
  if (b->nomess <= 0)
    {
      bprintf ("Nothing written on it to delete.\n");
      return;
    }
  
  if (plev (mynum) < b->level)
    {
      bprintf ("You look closely but can not understand the mystical writings\n");
      return;
    }
  
  num = atoi (wordbuf);
  if ((num > b->nomess) || (num < 1))	/* Number out of valid range      */
    {
      bprintf ("That message only exist in your imagination\n");
      return;
    }
  num--;
  for (i = 0, m2 = NULL, m = b->messages; (i < num) && (m != NULL); i++)
    {
      m2 = m;
      m = m->next;
    }
  if (m == NULL)		/* Someone beat us to it */
    {
      bprintf ("The message just vanished.\n");
      mudlog ("BB ERROR: could not find message in do_erase()");
      return;
    }
  if ((plev (mynum) >= DELETE_LEVEL) || is_owner (m, mynum))
    {
      if (m2 == NULL)		/* del first message */
	{
	  b->messages = m->next;
	}
      else
	{
	  m2->next = m->next;
	}
      if (m == b->last)		/* deleting the last message */
	b->last = m2;
      
      free (m->message);
      free (m->date);
      free (m->header);
      free (m);
      b->nomess--;
      update_board (b);
    }
  else
    {
      bprintf ("I don't think so!!\n");
    }
  return;
}

/*
 * **  if owners are allowed to delete their messages then returns True if
 * **  mynum is the owner, False otherwise
 */
static Boolean 
is_owner (PMES pm, int pl)
{
#ifdef OWNER_CAN_DEL
  if (strcasecmp (pname (pl), pm->owner) == 0)
    return True;
  else
    return False;
  
#else
  return False;
#endif
}

/*
 * **  Allow players to write on the board. Uses the same writer as mail, 
 * **  except that it's handler buffers the input in memory, not writing it 
 * **  to a file.
 */
void 
write_board ()
{
  BOARD *b;
  char subj[MAX_COM_LEN];

  void board_handler (void *w, void *ad, int adlen);
  
  if (cur_player->aliased || cur_player->polymorphed != -1)
    {
      bprintf ("Not while aliased.\n");
      return;
    }
  if (cur_player->board != NULL)
    {
      bprintf ("You're writing on one already, don't be greedy.\n");
      return;
    }
  if ((b = present_board ()) == NULL)
    {
      bprintf ("There is no board to write on.\n");
      return;
    }
  if (plev (mynum) < b->level)
    {
      bprintf ("Strange, your pen doesn't seem to work.\n");
      return;
    }
  
  getreinput (subj);
  if (strlen (subj) > MAX_SUBJ_LEN)
    bprintf ("Warning: Subject to long, truncated.\n");
  subj[MAX_SUBJ_LEN + 1] = '\0';
  
  cur_player->board = b;	/* Remember the board + stop 2 writes at same time */
  
  ssetflg(mynum, SFL_POSTING);
  start_writer ("End the message with ** on the beginning of a new line",
		"BOARD>",
		subj,
		MAX_SUBJ_LEN + 1,
		board_handler,
		WR_CMD | '*',
		MAX_MESS_LEN);
  sclrflg(mynum, SFL_POSTING);
  return;
}

/*
 * **  Handle the data we get from the writer, create a message, put it in 
 * **  the board, save the board to a file, and finally free the cur_player->board
 * **  variable, allowing them to start another write.
 */
void board_handler (void *w, void *ad, int adlen)
{
  PMES msg;
  char b[100], subj[MAX_SUBJ_LEN + 1];
  char bigbuf[(MAX_COM_LEN + 1) * MAX_MESS_LEN];	/* More than we could want */
  BOARD *brd = (BOARD *) cur_player->board;
  int size = 0;
  
  if (brd == NULL)		/* Should never happen, but better than crashing mud   */
    {				/* if it does.                                         */
      mudlog ("Board went null on %s\n", pname (mynum));
      return;
    }
  
  strcpy (subj, (char *) ad);	/* Seems to work better if copy this here */
  bigbuf[0] = '\0';
  while ((wgets (b, sizeof (b), w)) != NULL)
    {
      if (!EMPTY (b))
	strcat (bigbuf, b);
    }
  
  if (EMPTY (bigbuf))
    {
      bprintf ("Ok, I won't bother posting then\n");
      cur_player->board = NULL;
      return;
    }
  
  /* Check for trailing \n or \r and get rid of them */
  size = strlen (bigbuf);
  if ((bigbuf[size - 1] == '\n') || (bigbuf[size - 1] == '\r'))
    bigbuf[size - 1] = '\0';
  else if ((bigbuf[size] == '\n') || (bigbuf[size] == '\r'))
    bigbuf[size] = '\0';
  
  /* Lets create this message */
  msg = (PMES) xmalloc (1, sizeof (MESS));
  msg->message = COPY (bigbuf);
  msg->date = COPY (time2ascii (TIME_CURRENT));
  msg->header = COPY (subj);
  strcpy (msg->owner, pname (mynum));
  msg->next = NULL;
  
  if (brd->last == NULL)
    brd->messages = msg;
  else
    brd->last->next = msg;
  brd->last = msg;
  brd->nomess++;
  update_board (brd);
  
  cur_player->board = NULL;	/* Free up the board(locking write for mynum) */
  
  return;
}