/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

boards.c				Handling of runtime configurable
					bulletin boards. (At some near pt.
					these will be OLCable.)

  Most functions and structures in this file have been altered to work with 
the new system beginning on 12/29/97 -jtrhone.

		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "comm.h"
#include "db.h"
#include "boards.h"
#include "interpreter.h"
#include "handler.h"
#include "lists.h"
#include "acmd.h"
#include "global.h"
#include "descmenu.h"

// local global variables
binfotype  	board_info[MAX_BOARDS];
bmsginfo	msg_index[MAX_BOARDS][MAX_BOARD_MESSAGES];
bindex		master_index[MAX_MSGS];

// assemble filename into fname and return
void get_board_fname(int bnum, char *fname)
{
  sprintf(fname, "%s/%d.brd", BOARD_DIR, B_VNUM(bnum));
}

// return a ptr the proto of this board
obdata *get_board_proto(int bnum)
{
  int rnum;

  rnum = real_object(B_VNUM(bnum));
  if (rnum < 0)
  {
    sprintf(buf2, "SYSERR: Boards: B%d, invalid vnum.", bnum);
    mudlog(buf2, BRF, LEV_IMM, TRUE);
    return NULL;
  }
  return (&obj_proto[rnum]);
}

// assign a board some characteristics
void assign_board(int bnum, int vnum, int read, int write, int remove)
{
  obdata *ob;

  B_VNUM(bnum) = vnum;
  READ_LVL(bnum) = read;
  WRITE_LVL(bnum) = write;
  REMOVE_LVL(bnum) = remove;

  // now fill in rest...
  B_MSGS(bnum)  = 0;
  B_VALID(bnum) = TRUE;

  // make sure proto is a BOARD type
  ob = get_board_proto(bnum);
  ITEM_TYPE(ob) = ITEM_BOARD;
}

// if the BOARD_CONFIG_FILE is not found, or there is an error loading
// it, we will fill up the board info structure with default info
void use_default_boards(void)
{
  mudlog("SYSUPD: Board config file not found, using defaults.", BRF, LEV_IMM, TRUE);

  //           #  vnum  read     write       remove 
  assign_board(0, 3099, 1,       1,          LEV_GOD);
  assign_board(1, 3098, LEV_IMM, LEV_IMM,    LEV_AIMP);
  assign_board(2, 3097, LEV_IMM, LEV_FREEZE, LEV_IMPL);
  assign_board(3, 3096, 1,       1,          LEV_IMM);
  assign_board(4, 1201, LEV_IMM, LEV_IMM,    LEV_GOD);
  assign_board(5, 1202, LEV_IMM, LEV_IMM,    LEV_GOD);
  assign_board(6, 1203, 1,       LEV_IMM,    LEV_AIMP);
  assign_board(7, 1204, 1,       1,          1       );
  assign_board(8, 1205, 1,       LEV_AIMP,   LEV_AIMP);
}

// fill the board_info structure with values from config file
// return TRUE if successful, FALSE otherwise
BOOL load_board_config_from_file(void)
{
  char letter = '\0';
  FILE *fp;
  int bnum, tmp1, tmp2, tmp3, tmp4;

  if (!(fp = fopen(BOARD_CONFIG_FILE, "rt")))
    return 0;

  bnum = 0;
  while (TRUE)
  {
    letter=fread_letter(fp);
    ungetc(letter, fp);

    if (bnum >= MAX_BOARDS || letter == '$' || letter == '~') 
      break;

    fscanf(fp, "%d %d %d %d\n", &tmp1, &tmp2, &tmp3, &tmp4);
    assign_board(bnum, tmp1, tmp2, tmp3, tmp4);
    bnum++;
  }
  fclose(fp);

  mudlog("SYSUPD: Board configuration read from file.", BUG, LEV_IMM, TRUE);
  return TRUE;
}

// attempt to load board configuration file, if fail, use the defaults
void load_board_config(void)
{
  if (!load_board_config_from_file())
    use_default_boards();
}

// save the board configuration to disk
void save_board_config(void)
{
  FILE *fp;
  int bnum;

  if (!(fp = fopen(BOARD_CONFIG_FILE, "wt")))
  {
    mudlog("SYSERR: Unable to save board config file.", BRF, LEV_IMM, TRUE);
    return;
  } 

  for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++)
    fprintf(fp, "%d %d %d %d\n", B_VNUM(bnum), READ_LVL(bnum), WRITE_LVL(bnum), REMOVE_LVL(bnum));

  // stick on an EOF marker...
  fprintf(fp, "$~\n");
  fclose(fp);  

  mudlog("SYSUPD: Board configuration saved.", BUG, LEV_IMM, TRUE);
}

ACMD(do_save_boards)
{
  if (IS_NPC(ch))
    return;

  save_board_config();
  send_to_char("Ok.\n\r",ch);
}

ACMD(do_blist)
{
  int bnum;

  if (IS_NPC(ch))
    return;

  strcpy(buf, "%B%6Board Info Table%0:\n\r");
  for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++)
    sprintf(buf+strlen(buf), "%%6Vnum%%0:%d, %%6Read%%0:%d, %%6Write%%0:%d, %%6Remove%%0:%d\n\r",
            B_VNUM(bnum), READ_LVL(bnum), WRITE_LVL(bnum), REMOVE_LVL(bnum));

  page_string(ch->desc, buf, 1);
}

BOOL board_in_table(int vnum)
{
  int bnum;

  for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++)
    if (B_VNUM(bnum) == vnum)
      return TRUE;
  return FALSE;
}

void add_board(int vnum, int read, int write, int remove)
{
  int bnum;

  for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++)
    ;

  if (bnum >= MAX_BOARDS)
  {
    mudlog("SYSERR: Board table filled up... unable to add.",BRF, LEV_IMM, TRUE);
    return;
  }

  assign_board(bnum, vnum, read, write, remove);
}

// add a board to the board table
ACMD(do_badd)
{
  obdata *ob;
  char *argu = argument;
  int rnum, vnum, read, write, remove;
  char arg1[MAX_STRING_LENGTH], arg2[MAX_STRING_LENGTH];
  char arg3[MAX_STRING_LENGTH], arg4[MAX_STRING_LENGTH];

  if (IS_NPC(ch))
    return;

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch);
    return;
  }
 
  // first, make sure vnum exists and is NOT a board
  half_chop(argu, arg1, argu);
  if (!is_number(arg1) || (rnum = real_object((vnum = atoi(arg1)))) < 0)
  {
    send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch);
    return;
  }

  ob = &obj_proto[rnum];
  if (ITEM_TYPE(ob) != ITEM_BOARD)
  {
    send_to_char("Item must be of type BOARD.\n\r",ch);
    return;
  }

  if (board_in_table(vnum))
  {
    send_to_char("That board is already in the board table.\n\r",ch);
    return;
  }

  half_chop(argu, arg2, argu);
  if (!*arg2 || !is_number(arg2) || (read = atoi(arg2)) < 1 || read > LEV_IMPL)
  {
    send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch);
    return;
  }

  half_chop(argu, arg3, argu);
  if (!*arg3 || !is_number(arg3) || (write = atoi(arg3)) < 1 || write > LEV_IMPL)
  {
    send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch);
    return;
  }

  half_chop(argu, arg4, argu);
  if (!*arg4 || !is_number(arg4) || (remove = atoi(arg4)) < 1 || remove > LEV_IMPL)
  {
    send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch);
    return;
  }

  // okie, find first open slot in the table and add
  add_board(vnum, read, write, remove);

  do_save_boards(ch, "", 0, 0);
}

// find an available slot in the global board array
int	find_slot(void)
{
  int	i;

  for (i = 0; i < MAX_MSGS; i++)
    if (!master_index[i].used) 
    {
      master_index[i].used = TRUE;
      return i;
    }

  return -1;
}

// return TRUE if there is a board in here...
BOOL board_in_room(int rnum)
{
  obdata *ob;

  for (ob = world[rnum].contents; ob; ob=ob->next_content)
    if (ITEM_TYPE(ob) == ITEM_BOARD)
      return TRUE;

  return FALSE;
}

// find a match between objects in room and boards in room
// NOW use VNUMs
int find_board(chdata *ch)
{
  obdata *obj;
  int bnum;

  for (obj = world[ch->in_room].contents; obj; obj = obj->next_content)
    if (ITEM_TYPE(obj) == ITEM_BOARD)
      for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++)
        if (B_VNUM(bnum) == GET_OBJ_VNUM(obj))
          return bnum;

  return -1;
}

// use memset now... 12/29/97 -jtrhone
void clear_master_index(void)
{
  memset((char *)master_index, 0, sizeof(bindex) * MAX_MSGS);
}

// initialize the boards... 
void init_boards(void)
{
  int	bnum, msg;
  char	buf[MAX_INPUT_LENGTH];

  clear_master_index();

  load_board_config();

  for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++) 
  {
    if (real_object(B_VNUM(bnum)) < 0) 
    {
      sprintf(buf, "SYSERR: Fatal board error: board vnum %d does not exist!", B_VNUM(msg));
      log(buf);

      B_VALID(bnum) = FALSE;
      continue;
    }

    for (msg = 0; msg < MAX_BOARD_MESSAGES; msg++) 
    {
      memset(&(MESSAGE(bnum, msg)), '\0', sizeof(bmsginfo));
      MSG_SLOTNUM(bnum, msg) = -1;
    }

    Board_load_board(bnum);
  }
}

// direct command to erase a message (used to be handled by do_remove)  4/14/98 -jtrhone
ACMD(do_erase)
{
  if (board_in_room(ch->in_room) && board_interaction(ch, argument, get_command("erase")))
    return;

  send_to_char("You can't find anything to erase.\n\r",ch);
}

// used to be hard coded SPECIAL(gen_board)...
// not anymore  12/30/97 -jtrhone
// boards are now initiated at BOOTUP (look in db.c)
int board_interaction(chdata *ch, char *arg, int cmd)
{
  int bnum;

  if ((bnum = find_board(ch)) == -1) 
  {
    sprintf(buf, "SYSERR: Degenerate board... via %s.", GET_NAME(ch));
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return 0;
  }

  if (!B_VALID(bnum))
  {
    send_to_char("Board appears to be invalid... report to an immortal.\n\r",ch);
    return 0;
  }

  if (CMD_IS(cmd, "write"))
  {
    Board_write_message(bnum, ch, arg);
    return 1;
  }
  else
  if (CMD_IS(cmd, "look"))
      return (Board_show_board(bnum, ch, arg));
  else
  if (CMD_IS(cmd, "read"))
      return (Board_display_msg(bnum, ch, arg));
  else
  if (CMD_IS(cmd, "erase"))
      return (Board_remove_msg(bnum, ch, arg));
  else
    return 0;
}

// a descriptor menu for easier board writing and allowance of roa client usage 7/18/98 -jtrhone
// will get called after do_var_string_arg finishes and pops and calls
ROA_DESCMENU(doneposting)
{
  Board_save_board(BOARD_WRITING(d->character));
  BOARD_WRITING(d->character) = -1;
  REMOVE_BIT(PLR_FLAGS(d->character), PLR_WRITING);
  send_to_char("Board posting complete.\n\r", d->character);

  // no more menus...
  DESCMENU_PROMPT(d)  = NULL;
  DESCMENU_HANDLER(d) = NULL;
  DESCMENU_DEPTH(d)   = 0;
}

// ok, startem writing a message
void	Board_write_message(int bnum, chdata *ch, char *arg)
{
  char	*tmstr, buf[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH];
  int	len;
  long	ct;

  if (IS_NPC(ch)) return;

  if (GET_LEVEL(ch) < WRITE_LVL(bnum)) {
    send_to_char("You are not holy enough to write on this board.\n\r", ch);
    return;
  }

  if (B_MSGS(bnum) >= MAX_BOARD_MESSAGES) {
    send_to_char("The board is full.\n\r", ch);
    return;
  }

  // get the next available slot in the master_index
  if ((NEW_MSG_INDEX(bnum).slot_num = find_slot()) == -1) {
    send_to_char("No empty message slots available...\n\r", ch);
    mudlog("SYSERR: Boards: No slots left in master index...", BRF, LEV_IMM, TRUE);
    return;
  }

  skip_spaces(&arg);

  if (!*arg) {
    send_to_char("You must have a message heading.\n\r", ch);
    return;
  }

  // get a timestamp
  ct = time(0);
  tmstr = (char *) asctime(localtime(&ct));
  // wax the newline
  *(tmstr + strlen(tmstr) - 1) = '\0';

  // construct the one line heading
  sprintf(buf2, "(%s)", GET_NAME(ch));
  sprintf(buf, "%6.10s %-12s :: %s", tmstr, buf2, arg);
  len = strlen(buf) + 1;

  if (!(NEW_MSG_INDEX(bnum).heading = (char *)malloc(sizeof(char)*len))) 
  {
    send_to_char("No memory available for message heading...\n\r", ch);
    mudlog("SYSERR: Boards: No memory available for message heading...", BRF, LEV_IMM, TRUE);
    return;
  }

  strcpy(NEW_MSG_INDEX(bnum).heading, buf);
  NEW_MSG_INDEX(bnum).heading[len-1] = '\0';
  NEW_MSG_INDEX(bnum).level = GET_LEVEL(ch);

  act("$n starts to write a message.", TRUE, ch, 0, 0, TO_ROOM);
  SET_BIT(PLR_FLAGS(ch), PLR_WRITING);
  BOARD_WRITING(ch) = bnum;
  descmenu_next(ch->desc, doneposting);
  do_var_string_arg_desc(ch->desc, "Write your message.  Terminate with a '@':\n\r",
                         &(master_index[NEW_MSG_INDEX(bnum).slot_num].msg),
                         MAX_MESSAGE_LENGTH);
  B_MSGS(bnum)++;
}

// if someone looks at a board they see.....
int Board_show_board(int bnum, chdata *ch, char *arg)
{
  int	msg;
  obdata *ob;

  if (GET_LEVEL(ch) < READ_LVL(bnum)) {
    send_to_char("You are unable to decipher any meaning from those words.\n\r", ch);
    return 1;
  }

  if (!(ob = get_board_proto(bnum)))
    return 0;

  act("$n studies $p.", TRUE, ch, ob, 0, TO_ROOM);

  strcpy(buf, "This item can be used as a board.  See %Bhelp boards%0 for more info.\n\r");
  if (!B_MSGS(bnum))
  {
    str_cat(buf, "There is nothing written on $p.\n\r", MAX_STRING_LENGTH, "Board_show_board");
    act(buf, TRUE, ch, ob, 0, TO_CHAR);
    return 1;
  }
  else
  {
    sprintf(buf + strlen(buf), "There are %d messages on %s.\n\r", B_MSGS(bnum), ob->shdesc);

    for (msg = 0; msg < B_MSGS(bnum); msg++) 
    {
	 if (MSG_HEADING(bnum, msg))
	    sprintf(buf + strlen(buf), "%-2d : %s\n\r", msg + 1, MSG_HEADING(bnum, msg));
	 else 
         {
            sprintf(buf2, "SYSERR: Boards: B%d, M%d missing header.", bnum, msg);
            mudlog(buf2, BRF, LEV_IMM, TRUE);
	    send_to_char("Sorry, the board isn't working.\n\r", ch);
	    return 1;
	 }
    }
  }

  page_string(ch->desc, buf, 1);
  return 1;
}

// show msg to character
int Board_display_msg(int bnum, chdata *ch, char *arg)
{
  char	number[MAX_STRING_LENGTH], buffer[MAX_STRING_LENGTH];
  int	msg, ind;

  if (!ch->desc) 
    return 0;

  one_argument(arg, number);

  if (!*number || !isdigit(*number))
    return 0;

  if (!(msg = atoi(number)))
    return 0;

  if (GET_LEVEL(ch) < READ_LVL(bnum)) {
    send_to_char("You are unable to decipher any meaning from those words.\n\r", ch);
    return 1;
  }

  if (!B_MSGS(bnum)) {
    send_to_char("The board is empty!\n\r", ch);
    return(1);
  }

  if (msg < 1 || msg > B_MSGS(bnum)) {
    send_to_char("That message exists only in your imagination.\n\r", ch);
    return(1);
  }

  ind = msg - 1;
  if (MSG_SLOTNUM(bnum, ind) < 0 || MSG_SLOTNUM(bnum, ind) >= MAX_MSGS) 
  {
    send_to_char("Sorry, the board is not working.\n\r", ch);
    sprintf(buf, "SYSERR: Boards: B%d, M%d invalid slotnum.",bnum, ind);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return 1;
  }

  if (!(MSG_HEADING(bnum, ind))) {
    send_to_char("That message appears to be screwed up.\n\r", ch);
    sprintf(buf, "SYSERR: Boards: B%d, M%d missing heading.",bnum, ind);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return 1;
  }

  if (!(master_index[MSG_SLOTNUM(bnum, ind)].used)) {
    send_to_char("That message seems to be empty.\n\r", ch);
    sprintf(buf, "SYSERR: Boards: B%d, M%d empty message block.",bnum, ind);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return 1;
  }

  sprintf(buffer, "Message %d : %s\n\r\n\r%s\n\r", msg,
       MSG_HEADING(bnum, ind), master_index[MSG_SLOTNUM(bnum, ind)].msg);

  /* REMIND me what message i just read!! RoA James Rhone*/
  sprintf(buf, "\n\rEnd of message #%d.\n\r", msg);
  str_cat(buffer, buf, MAX_STRING_LENGTH, "Board_display_msg");  

  page_string(ch->desc, buffer, 1);
  return 1;
}

// when character removes a message...
int	Board_remove_msg(int bnum, chdata *ch, char *arg)
{
  int	ind, msg, slot_num;
  char	number[MAX_INPUT_LENGTH], buf[MAX_INPUT_LENGTH];
  dsdata *d;

  if (IS_NPC(ch)) 
    return 0;

  one_argument(arg, number);

  if (!*number || !isdigit(*number))
    return 0;

  if (!(msg = atoi(number)))
    return(0);

  if (!B_MSGS(bnum)) {
    send_to_char("The board is empty!\n\r", ch);
    return 1;
  }

  if (msg < 1 || msg > B_MSGS(bnum)) {
    send_to_char("That message exists only in your imagination..\n\r", ch);
    return 1;
  }

  ind = msg - 1;
  if (!MSG_HEADING(bnum, ind)) {
    send_to_char("That message appears to be screwed up.\n\r", ch);
    sprintf(buf, "SYSERR: Boards: B%d, M%d missing heading.",bnum, ind);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return 1;
  }

  sprintf(buf, "(%s)", GET_NAME(ch));
  if (GET_LEVEL(ch) < REMOVE_LVL(bnum) && !(strstr(MSG_HEADING(bnum, ind), buf))) 
  {
    send_to_char("You are unable to remove other's messages.\n\r", ch);
    return 1;
  }

  if (GET_LEVEL(ch) < MSG_LEVEL(bnum, ind)) {
    send_to_char("You cannot remove that message.\n\r", ch);
    return 1;
  }

  slot_num = MSG_SLOTNUM(bnum, ind);
  if (slot_num < 0 || slot_num >= MAX_MSGS) 
  {
    sprintf(buf, "SYSERR: Boards: B%d, M%d invalid slotnum in master index.",bnum, ind);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    send_to_char("That message is majorly screwed up.\n\r", ch);
    return 1;
  }

  // scan people connected to make sure nobody is currently writing this message
  for (d = descriptor_list; d; d = d->next)
    if (!d->connected && d->str == &(master_index[slot_num].msg)) 
    {
	 send_to_char("At least wait until the author is finished before removing it!\n\r", ch);
	 return 1;
    }

  master_index[slot_num].used = FALSE;
  FREENULL(master_index[slot_num].msg); 
  FREENULL(MSG_HEADING(bnum, ind));

  for ( ; ind < B_MSGS(bnum) - 1; ind++) 
  {
    MSG_HEADING(bnum, ind) = MSG_HEADING(bnum, ind + 1);
    MSG_SLOTNUM(bnum, ind) = MSG_SLOTNUM(bnum, ind + 1);
    MSG_LEVEL(bnum, ind)   = MSG_LEVEL(bnum, ind + 1);
  }

  B_MSGS(bnum)--;
  send_to_char("Message removed.\n\r", ch);
  sprintf(buf, "$n just removed message %d.", msg);
  act(buf, TRUE, ch, 0, 0, TO_ROOM);
  Board_save_board(bnum);

  return 1;
}

// save an individual board to disk...
void	Board_save_board(int bnum)
{
  FILE *fl;
  int	msg;
  char	fname[MAX_INPUT_LENGTH], *tmp1 = 0, *tmp2 = 0;

  get_board_fname(bnum, fname);

  // if no messages, simply remove the file...
  if (B_MSGS(bnum) <= 0) 
  {
    unlink(fname);
    return;
  }

  if (!(fl = fopen(fname, "wb"))) {
    sprintf(buf, "SYSERR: Boards: B%d error writing to file...",bnum);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return;
  }

  // first, write the number of messages to file
  fwrite(&(B_MSGS(bnum)), sizeof(int), 1, fl);

  for (msg = 0; msg < B_MSGS(bnum); msg++) 
  {
      if ((tmp1 = MSG_HEADING(bnum, msg)))
	 msg_index[bnum][msg].heading_len = strlen(tmp1) + 1;
      else
	 msg_index[bnum][msg].heading_len = 0;

      if (MSG_SLOTNUM(bnum, msg) < 0 || MSG_SLOTNUM(bnum, msg) >= MAX_MSGS || 
          (!(tmp2 = master_index[MSG_SLOTNUM(bnum, msg)].msg)))
	 msg_index[bnum][msg].message_len = 0;
      else
	 msg_index[bnum][msg].message_len = strlen(tmp2) + 1;

      // for each message, write the message info out
      fwrite(&(msg_index[bnum][msg]), sizeof(bmsginfo), 1, fl);

      // if we have a heading, write out the heading
      if (tmp1)
	 fwrite(tmp1, sizeof(char), msg_index[bnum][msg].heading_len, fl);

      // if we have a message, write out the message
      if (tmp2)
	 fwrite(tmp2, sizeof(char), msg_index[bnum][msg].message_len, fl);
   }

  // we're done...
  fclose(fl);
}

// load an individual board from disk...
void	Board_load_board(int bnum)
{
  FILE *fl;
  int msg, len1 = 0, len2 = 0;
  char fname[MAX_INPUT_LENGTH], *tmp = 0;

  get_board_fname(bnum, fname);

  // no error, just dont load the board
  if (!(fl = fopen(fname, "rb"))) 
  {
    return;
  }

  fread(&(B_MSGS(bnum)), sizeof(int), 1, fl);

  if (B_MSGS(bnum) < 1 || B_MSGS(bnum) > MAX_BOARD_MESSAGES) 
  {
    sprintf(buf, "SYSERR: Boards: B%d corrupt, resetting...",bnum);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    Board_reset_board(bnum);
    return;
  }

  for (msg = 0; msg < B_MSGS(bnum); msg++) 
  {
     fread(&(msg_index[bnum][msg]), sizeof(bmsginfo), 1, fl);
     if (!(len1 = msg_index[bnum][msg].heading_len)) 
     {
        sprintf(buf, "SYSERR: Boards: B%d M%d corrupt, resetting...",bnum, msg);
        mudlog(buf, BRF, LEV_IMM, TRUE);
        Board_reset_board(bnum);
        return;
     }

     // allocate and read into
     CREATE(MSG_HEADING(bnum, msg), char, len1);
     fread(MSG_HEADING(bnum, msg), sizeof(char), len1, fl);

     if ((len2 = msg_index[bnum][msg].message_len)) 
     {
       if ((MSG_SLOTNUM(bnum, msg) = find_slot()) == -1) 
       {
         sprintf(buf, "SYSERR: Boards: B%d M%d no slot available, resetting.", bnum, msg);
         mudlog(buf, BRF, LEV_IMM, TRUE);
         Board_reset_board(bnum);
	 return;
       }

       // allocate for message block and read into
       CREATE(tmp, char, len2);
       fread(tmp, sizeof(char), len2, fl);
       
       // ok, assign master index this message txt
       master_index[MSG_SLOTNUM(bnum, msg)].msg = tmp;
     }
   }

   /* NULL out remaining heading pointers -RoA 9/1/97 */
   for ( ; msg < MAX_BOARD_MESSAGES; msg++) 
     MSG_HEADING(bnum, msg) = NULL;

   fclose(fl);
}

// in case of an error, reset a particular board
void	Board_reset_board(int bnum)
{
  int msg;
  char fname[MAX_INPUT_LENGTH];

  for (msg = 0; msg < B_MSGS(bnum) - 1; msg++) 
  {
    FREENULL(MSG_HEADING(bnum, msg));
    FREENULL(master_index[MSG_SLOTNUM(bnum, msg)].msg);
    master_index[MSG_SLOTNUM(bnum, msg)].used = FALSE;
    memset(&(msg_index[bnum][msg]), '\0', sizeof(bmsginfo));
    msg_index[bnum][msg].slot_num = -1;
  }

  B_MSGS(bnum) = 0;
  
  get_board_fname(bnum, fname);
  unlink(fname);
}

// free all the boards...
void	free_the_boards(void)
{
  int	i, bnum;

  for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++)
   for (i = 0; i < B_MSGS(bnum); i++) 
   {
     FREENULL(MSG_HEADING(bnum, i));
     FREENULL(master_index[MSG_SLOTNUM(bnum, i)].msg);
   }
}