#define BOARD_C /****************************************************************************** ** Project : pDirt ** Module : board.c ** Author : Various ** Description: The bulletin board system. Handles all bulletin boards in the ** MUD, and their commands. *****************************************************************************/ #include <unistd.h> #include <stdlib.h> #include <string.h> #ifdef RS6000 #include <strings.h> #endif #include "kernel.h" #include "config.h" #include "board.h" #include "timing.h" #include <malloc.h> #include "bprintf.h" #include "log.h" #include "parse.h" #include "editor.h" /* ==================== Define some things about them ====================== */ #define DELETE_LEVEL LVL_ARCHWIZARD /* 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. */ #define SBB_FILE "bboard_singles" /* 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: &+wWRITE &*<heading> , &+wMESSAGE &*#, &+wERASE &*#\n"; /* =============== 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); /* * ** 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; sbb.nomess = 0; strcpy(sbb.file, SBB_FILE); sbb.level = 0; /* boot mortal board first */ load_board (&(pbb)); /* Boot singles board now */ load_board (&(sbb)); /* 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; } for (i = 0; sbboards[i] != -1; i++) { if (sbboards[i] == ob) return SINGLESBOARD; } 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); } for (i = 0; sbboards[i] != -1; i++) { if (ploc(mynum) == oloc(sbboards[i])) return &(sbb); } 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; case SINGLESBOARD: brd = &(sbb); break; } if (brd->level > plev (mynum)) { bprintf ("You look closely but can not understand the mystical writings\n"); return; } bprintf ("%s", Board_instr); if (brd->nomess > 0) bprintf("\n&+wNr Date Author Subject\n&+w%s&*\n",DASHLINE); m = brd->messages; while (m != NULL) { bprintf ("&+w%2d &+w%-11.11s &+w%-*s &+w%-*s \n", i, m->date, PNAME_LEN, m->owner, MAX_SUBJ_LEN, m->header); i++; m = m->next; } if (brd->nomess > 0) { bprintf("&+w%s&*\n",DASHLINE); bprintf ("&+wThere are a total of &+w%d&+w message(s) on this board&*\n\n", brd->nomess); } else bprintf("\n&+wThere are no messages on this board&*\n"); 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); 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; 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 */ } fclose(f); return; } /* * ** The command that actually reads message from the board for the players. * ** VERB: message */ A_COMMAND(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 ("&+wMessage by:&+w %s\n" "&+wDate :&+w %s\n" "&+wSubject :&* %s\n\n", m->owner, m->date, m->header); bprintf ("%s\n\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. */ A_COMMAND(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 new_board_handler (char *fn, char *param); A_COMMAND(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 */ start_editor(mynum,new_board_handler,MAX_SUBJ_LEN,subj); return; } /* * ** Handle the data we get from the editor, 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 new_board_handler (char *fn, char *param) { PMES msg; FILE *fp; 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,param); /* Seems to work better if copy this here */ bigbuf[0] = '\0'; if (fn == NULL) { cur_player->board = NULL; return; } fp = fopen(fn,"r"); if (fp == NULL) { bprintf("Oops lost message.. \n"); cur_player->board = NULL; return; } while ((fgets (b, sizeof (b), fp)) != NULL) { if (!EMPTY (b)) strcat (bigbuf, b); } fclose(fp); 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) */ bprintf("\r%s",cur_player->prompt); return; }