diff -uNp orig/Makefile ./Makefile
--- orig/Makefile	Fri May 23 23:01:01 2003
+++ ./Makefile	Fri May 23 23:02:14 2003
@@ -29,7 +29,7 @@ OBJFILES = act.comm.o act.informative.o 
 	boards.o castle.o class.o comm.o config.o constants.o db.o fight.o \
 	graph.o handler.o house.o interpreter.o limits.o magic.o mail.o \
 	mobact.o modify.o objsave.o olc.o random.o shop.o spec_assign.o \
-	spec_procs.o spell_parser.o spells.o utils.o weather.o \
+	spec_procs.o spell_parser.o spells.o utils.o warn.o weather.o \
 	bsd-snprintf.o
 
 CXREF_FILES = act.comm.c act.informative.c act.item.c act.movement.c \
@@ -37,7 +37,7 @@ CXREF_FILES = act.comm.c act.informative
 	boards.c castle.c class.c comm.c config.c constants.c db.c fight.c \
 	graph.c handler.c house.c interpreter.c limits.c magic.c mail.c \
 	mobact.c modify.c objsave.c olc.c random.c shop.c spec_assign.c\
-	spec_procs.c spell_parser.c spells.c utils.c weather.c \
+	spec_procs.c spell_parser.c spells.c utils.c warn.c weather.c \
 	bsd-snprintf.c
 
 default: all
@@ -205,3 +205,6 @@ weather.o: weather.c conf.h sysdep.h str
 	$(CC) -c $(CFLAGS) weather.c
 bsd-snprintf.o: bsd-snprintf.c conf.h sysdep.h
 	$(CC) -c $(CFLAGS) bsd-snprintf.c
+warn.o: warn.c comm.h db.h handler.h interpreter.h structs.h utils.h conf.h \
+  sysdep.h
+	$(CC) -c $(CFLAGS) warn.c
diff -uNp orig/Makefile.in ./Makefile.in
--- orig/Makefile.in	Fri May 23 23:01:01 2003
+++ ./Makefile.in	Fri May 23 23:02:14 2003
@@ -28,7 +28,7 @@ OBJFILES = act.comm.o act.informative.o 
 	boards.o castle.o class.o comm.o config.o constants.o db.o fight.o \
 	graph.o handler.o house.o interpreter.o limits.o magic.o mail.o \
 	mobact.o modify.o objsave.o olc.o random.o shop.o spec_assign.o \
-	spec_procs.o spell_parser.o spells.o utils.o weather.o \
+	spec_procs.o spell_parser.o spells.o utils.o warn.o weather.o \
 	bsd-snprintf.o
 
 CXREF_FILES = act.comm.c act.informative.c act.item.c act.movement.c \
@@ -36,7 +36,7 @@ CXREF_FILES = act.comm.c act.informative
 	boards.c castle.c class.c comm.c config.c constants.c db.c fight.c \
 	graph.c handler.c house.c interpreter.c limits.c magic.c mail.c \
 	mobact.c modify.c objsave.c olc.c random.c shop.c spec_assign.c\
-	spec_procs.c spell_parser.c spells.c utils.c weather.c \
+	spec_procs.c spell_parser.c spells.c utils.c warn.c weather.c \
 	bsd-snprintf.c
 
 default: all
@@ -204,3 +204,6 @@ weather.o: weather.c conf.h sysdep.h str
 	$(CC) -c $(CFLAGS) weather.c
 bsd-snprintf.o: bsd-snprintf.c conf.h sysdep.h
 	$(CC) -c $(CFLAGS) bsd-snprintf.c
+warn.o: warn.c comm.h db.h handler.h interpreter.h structs.h utils.h conf.h \
+  sysdep.h
+	$(CC) -c $(CFLAGS) warn.c
diff -uNp orig/act.other.c ./act.other.c
--- orig/act.other.c	Fri May 23 23:01:01 2003
+++ ./act.other.c	Fri May 23 23:02:14 2003
@@ -43,6 +43,7 @@ SPECIAL(shop_keeper);
 ACMD(do_gen_comm);
 void die(struct char_data *ch);
 void Crash_rentsave(struct char_data *ch, int cost);
+void save_warnings(struct char_data *ch);
 
 /* local functions */
 ACMD(do_quit);
@@ -126,6 +127,7 @@ ACMD(do_save)
   }
 
   write_aliases(ch);
+  save_warnings(ch);
   save_char(ch);
   Crash_crashsave(ch);
   if (ROOM_FLAGGED(IN_ROOM(ch), ROOM_HOUSE_CRASH))
diff -uNp orig/db.c ./db.c
--- orig/db.c	Fri May 23 23:01:01 2003
+++ ./db.c	Fri May 23 23:02:14 2003
@@ -2465,13 +2465,22 @@ char *fread_string(FILE *fl, const char 
 void free_char(struct char_data *ch)
 {
   int i;
-  struct alias_data *a;
+  struct alias_data *a = NULL;
+  struct warning_data *w = NULL;
 
   if (ch->player_specials != NULL && ch->player_specials != &dummy_mob) {
     while ((a = GET_ALIASES(ch)) != NULL) {
-      GET_ALIASES(ch) = (GET_ALIASES(ch))->next;
+      GET_ALIASES(ch) = GET_ALIASES(ch)->next;
       free_alias(a);
     }
+    while ((w = GET_WARNINGS(ch)) != NULL) {
+      GET_WARNINGS(ch) = GET_WARNINGS(ch)->next;
+      if (w->admin != NULL)
+	free(w->admin);
+      if (w->message != NULL)
+	free(w->message);
+      free(w);
+    }
     if (ch->player_specials->poofin)
       free(ch->player_specials->poofin);
     if (ch->player_specials->poofout)
diff -uNp orig/db.h ./db.h
--- orig/db.h	Fri May 23 23:01:01 2003
+++ ./db.h	Fri May 23 23:02:14 2003
@@ -25,6 +25,7 @@
 #define LIB_PLRTEXT	":plrtext:"
 #define LIB_PLROBJS	":plrobjs:"
 #define LIB_PLRALIAS	":plralias:"
+#define LIB_PLRWARN	":plrwarn:"
 #define LIB_HOUSE	":house:"
 #define SLASH		":"
 #elif defined(CIRCLE_AMIGA) || defined(CIRCLE_UNIX) || defined(CIRCLE_WINDOWS) || defined(CIRCLE_ACORN) || defined(CIRCLE_VMS)
@@ -36,6 +37,7 @@
 #define LIB_PLRTEXT	"plrtext/"
 #define LIB_PLROBJS	"plrobjs/"
 #define LIB_PLRALIAS	"plralias/"
+#define LIB_PLRWARN	"plrwarn/"
 #define LIB_HOUSE	"house/"
 #define SLASH		"/"
 #else
@@ -45,6 +47,7 @@
 #define SUF_OBJS	"objs"
 #define SUF_TEXT	"text"
 #define SUF_ALIAS	"alias"
+#define SUF_WARN	"warn"
 
 #if defined(CIRCLE_AMIGA)
 #define FASTBOOT_FILE   "/.fastboot"    /* autorun: boot without sleep  */
Common subdirectories: orig/doc and ./doc
diff -uNp orig/interpreter.c ./interpreter.c
--- orig/interpreter.c	Fri May 23 23:01:01 2003
+++ ./interpreter.c	Fri May 23 23:02:14 2003
@@ -51,6 +51,8 @@ int isbanned(char *hostname);
 int Valid_Name(char *newname);
 void read_aliases(struct char_data *ch);
 void delete_aliases(const char *charname);
+void kill_warnings(const char *charname);
+void load_warnings(struct char_data *ch);
 
 /* local functions */
 int perform_dupe_check(struct descriptor_data *d);
@@ -173,6 +175,7 @@ ACMD(do_visible);
 ACMD(do_vnum);
 ACMD(do_vstat);
 ACMD(do_wake);
+ACMD(do_warn);
 ACMD(do_wear);
 ACMD(do_weather);
 ACMD(do_where);
@@ -512,6 +515,7 @@ cpp_extern const struct command_info cmd
   { "vstat"    , POS_DEAD    , do_vstat    , LVL_IMMORT, 0 },
 
   { "wake"     , POS_SLEEPING, do_wake     , 0, 0 },
+  { "warn"     , POS_DEAD    , do_warn     , LVL_GRGOD, 0 },
   { "wave"     , POS_RESTING , do_action   , 0, 0 },
   { "wear"     , POS_RESTING , do_wear     , 0, 0 },
   { "weather"  , POS_RESTING , do_weather  , 0, 0 },
@@ -1548,6 +1552,7 @@ void nanny(struct descriptor_data *d, ch
     case '1':
       reset_char(d->character);
       read_aliases(d->character);
+      load_warnings(d->character);
 
       if (PLR_FLAGGED(d->character, PLR_INVSTART))
 	GET_INVIS_LEV(d->character) = GET_LEVEL(d->character);
@@ -1672,6 +1677,7 @@ void nanny(struct descriptor_data *d, ch
       save_char(d->character);
       Crash_delete_file(GET_NAME(d->character));
       delete_aliases(GET_NAME(d->character));
+      kill_warnings(GET_NAME(d->character));
       write_to_output(d, "Character '%s' deleted!\r\n"
 	      "Goodbye.\r\n", GET_NAME(d->character));
       mudlog(NRM, LVL_GOD, TRUE, "%s (lev %d) has self-deleted.", GET_NAME(d->character), GET_LEVEL(d->character));
Common subdirectories: orig/orig and ./orig
diff -uNp orig/structs.h ./structs.h
--- orig/structs.h	Fri May 23 23:01:01 2003
+++ ./structs.h	Fri May 23 23:02:14 2003
@@ -803,6 +803,13 @@ struct char_special_data {
    struct char_special_data_saved saved; /* constants saved in plrfile	*/
 };
 
+/* Player warning record. */
+struct warning_data {
+  char		*admin;			/* Name of admin sending warn.	*/
+  char		*message;		/* Warning message sent.	*/
+  time_t	time;			/* Time the warning occured.	*/
+  struct warning_data *next;		/* Next warning in warning list.*/
+};
 
 /*
  *  If you want to add new values to the playerfile, do it here.  DO NOT
@@ -864,6 +871,7 @@ struct player_special_data {
    char	*poofin;		/* Description on arrival of a god.     */
    char	*poofout;		/* Description upon a god's exit.       */
    struct alias_data *aliases;	/* Character's aliases			*/
+   struct warning_data *warnings; /* List of character's warnings.	*/
    long last_tell;		/* idnum of last tell from		*/
    void *last_olc_targ;		/* olc control				*/
    int last_olc_mode;		/* olc control				*/
Common subdirectories: orig/util and ./util
diff -uNp orig/utils.c ./utils.c
--- orig/utils.c	Fri May 23 23:01:01 2003
+++ ./utils.c	Fri May 23 23:02:14 2003
@@ -570,6 +570,10 @@ int get_filename(char *filename, size_t 
     prefix = LIB_PLRTEXT;
     suffix = SUF_TEXT;
     break;
+  case WARN_FILE:
+    prefix = LIB_PLRWARN;
+    suffix = SUF_WARN;
+    break;
   default:
     return (0);
   }
diff -uNp orig/utils.h ./utils.h
--- orig/utils.h	Fri May 23 23:01:01 2003
+++ ./utils.h	Fri May 23 23:02:15 2003
@@ -103,9 +103,10 @@ void	update_pos(struct char_data *victim
 #define CMP	3
 
 /* get_filename() */
-#define CRASH_FILE	0
-#define ETEXT_FILE	1
-#define ALIAS_FILE	2
+#define CRASH_FILE		(0)
+#define ETEXT_FILE		(1)
+#define ALIAS_FILE		(2)
+#define WARN_FILE		(3)
 
 /* breadth-first searching */
 #define BFS_ERROR		(-1)
@@ -323,6 +324,7 @@ void	update_pos(struct char_data *victim
 #define GET_LAST_OLC_TARG(ch)	CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->last_olc_targ))
 #define GET_LAST_OLC_MODE(ch)	CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->last_olc_mode))
 #define GET_ALIASES(ch)		CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->aliases))
+#define GET_WARNINGS(ch)	CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->warnings))
 #define GET_LAST_TELL(ch)	CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->last_tell))
 
 #define GET_SKILL(ch, i)	CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->saved.skills[i]))
diff -uNp orig/warn.c ./warn.c
--- orig/warn.c	Wed Dec 31 17:00:00 1969
+++ ./warn.c	Fri May 23 23:41:01 2003
@@ -0,0 +1,289 @@
+#include "conf.h"
+#include "sysdep.h"
+#include "structs.h"
+#include "utils.h"
+
+#include "comm.h"
+#include "db.h"
+#include "handler.h"
+#include "interpreter.h"
+
+/*
+ * Local functions
+ */
+ACMD(do_warn);
+void kill_warnings(const char *charname);
+void load_warnings(struct char_data *ch);
+void save_warnings(struct char_data *ch);
+
+
+
+#define WARN_USAGE \
+  "Usage: warn [file] <player> [message]\r\n"
+
+ACMD(do_warn)
+{
+  bool is_file = FALSE;
+  char arg[MAX_INPUT_LENGTH];
+  int num = 0, player_i = 0;
+  struct char_data *vict = NULL;
+  struct char_file_u ch_store;
+  struct warning_data *warn = NULL;
+
+  /* Get the first two arguments. */
+  argument = one_argument(argument, arg);
+
+  /*
+   * If we need to look in the file, set the flag and get the other
+   * arguments we need from the command line.
+   */
+  if (str_cmp(arg, "file") == 0) {
+    argument = one_argument(argument, arg);
+    is_file = TRUE;
+  }
+
+  /* Skip any spaced before the argument. */
+  skip_spaces(&argument);
+
+  if (arg[0] == '\0')
+    send_to_char(ch, "%s", WARN_USAGE);
+  else {
+    if (!is_file)
+      vict = get_player_vis(ch, arg, NULL, FIND_CHAR_WORLD);
+    else {
+      if ((player_i = load_char(arg, &ch_store)) >= 0) {
+	/* Allocate a new character structure and initialize it. */
+	CREATE(vict, struct char_data, 1);
+	clear_char(vict);
+
+	/* Unpack the character data. */
+	store_to_char(&ch_store, vict);
+
+	/* Load the player's warnings. */
+	load_warnings(vict);
+      }
+    }
+
+    if (vict == NULL)
+      send_to_char(ch, "There's nobody here by that name.\r\n");
+    else if (argument[0] == '\0') {
+      if (GET_WARNINGS(vict) == NULL)
+	send_to_char(ch, "%s has no warnings.\r\n", GET_NAME(vict));
+      else {
+	send_to_char(ch, "%s has these warnings:\r\n", GET_NAME(vict));
+	for (warn = GET_WARNINGS(vict); warn != NULL; warn = warn->next) {
+	  send_to_char(ch, "%2d. %-12.12s : %-10.10s : %s\r\n", ++num,
+		asctime(localtime(&(warn->time))) + 4, warn->admin,
+		warn->message);
+	}
+      }
+    } else {
+      /* Create a new warning record. */
+      CREATE(warn, struct warning_data, 1);
+
+      /* Initialize the warning fields. */
+      warn->admin = strdup(GET_NAME(ch));
+      warn->message = strdup(argument);
+      warn->time = time(0);
+      warn->next = NULL;
+
+      /* Add the warning to the player's warning list. */
+      warn->next = GET_WARNINGS(vict);
+      GET_WARNINGS(vict) = warn;
+
+      /* Log the warning to online immortals. */
+      mudlog(NRM, MAX(LVL_GOD, MAX(GET_INVIS_LEV(vict), GET_INVIS_LEV(ch))),    
+	TRUE, "(GC) %s warned by %s: %s", GET_NAME(vict), GET_NAME(ch),
+	argument);
+
+      /* Tell the player. */
+      send_to_char(vict,
+	"You have received a warning from the immortal staff.  We kindly suggest\r\n"
+	"this warning be heeded and your ways amended.  The reason for this warning\r\n"
+	"is as follows: %s\r\n", argument);
+
+      /* Tell the imm it was done. */
+      send_to_char(ch, "%s", OK);
+
+      /* Save the victim's warning messages. */
+      save_warnings(vict);
+    }
+
+    /* Clean up after ourselves. */
+    if (is_file && vict != NULL)
+      free_char(vict);
+  }
+}
+
+
+
+void kill_warnings(const char *charname)
+{
+  char filename[MAX_STRING_LENGTH];
+
+  if (get_filename(filename, sizeof(filename), WARN_FILE, charname)) {
+    if (remove(filename) < 0 && errno != ENOENT)
+      log("SYSERR: deleting warning file %s: %s", filename, strerror(errno));
+  }
+}
+
+
+
+void load_warnings(struct char_data *ch)
+{   
+  char filename[MAX_STRING_LENGTH];
+  char line[MAX_STRING_LENGTH], tag[MAX_STRING_LENGTH], *p = NULL;
+  FILE *file = NULL;
+  struct warning_data *twarn = NULL, *warn = NULL;
+
+  get_filename(filename, sizeof(filename), WARN_FILE, GET_NAME(ch));
+
+  if ((file = fopen(filename, "rt")) == NULL) {
+    if (errno != ENOENT) {
+      log("SYSERR: Couldn't open warn file '%s' for %s.", filename,
+	GET_NAME(ch));
+    }
+  } else {
+    while (get_line(file, line)) {
+      /* Skip blank lines too. */
+      if (line[0] == '\0')
+        continue;
+       
+      /* Get the first word on the line. */
+      p = any_one_arg(line, tag);
+      
+      /* Skip any whitespace. */
+      skip_spaces(&p);
+        
+      /*
+       * Copy what's left of the line back.
+       */
+      strncpy(line, p, sizeof(line));
+
+      switch (tag[0]) {
+      case '#':
+	if (str_cmp(tag, "#WARNING") == 0) {
+	  if (warn != NULL) {
+	    log("SYSERR: #WARNING tag with no #END in file '%s'.", filename);
+	    if (warn->admin != NULL)
+	      free(warn->admin);
+	    if (warn->message != NULL)
+	      free(warn->message);
+	    free(warn);
+	  }
+	  CREATE(warn, struct warning_data, 1);
+	  warn->admin = strdup("Nobody");
+	  warn->message = NULL;
+	  warn->time = 0;
+	  warn->next = NULL;
+	} else if (str_cmp(tag, "#END") == 0) {
+	  if (warn == NULL)
+	    log("SYSERR: #END tag without #WARNING in file '%s'.", filename);
+	  else {
+	    if (GET_WARNINGS(ch) == NULL ||
+		warn->time > GET_WARNINGS(ch)->time) {
+	      warn->next = GET_WARNINGS(ch);
+	      GET_WARNINGS(ch) = warn;
+	    } else {
+	      for (twarn = GET_WARNINGS(ch); twarn; twarn = twarn->next) {
+		if (twarn->next == NULL)
+		  break;
+		else if (warn->time > twarn->next->time)
+		  break;
+	      }
+	      warn->next = twarn->next;
+	      twarn->next = warn;
+	    }
+	  }
+	  warn = NULL;
+	} else
+	  log("SYSERR: Unknown tag [%s] in file [%s].", tag, filename);
+	break;
+      case 'a':
+      case 'A':
+        if (str_cmp(tag, "Admin") == 0) {
+	  if (warn == NULL) {
+	    log("SYSERR: 'Admin' tag without #WARNING tag in file '%s'.",
+		filename);
+	  } else {
+	    if (warn->admin != NULL)
+	      free(warn->admin);
+	    warn->admin = strdup(line);
+	  }
+	} else
+	  log("SYSERR: Unknown tag [%s] in file [%s].", tag, filename);
+	break;
+      case 'm':
+      case 'M':
+        if (str_cmp(tag, "Message") == 0) {
+	  if (warn == NULL) {
+	    log("SYSERR: 'Message' tag without #WARNING tag in file '%s'.",
+		filename);
+	  } else {
+            if (warn->message != NULL)
+	      free(warn->message);
+	    warn->message = strdup(line);
+	  }
+	} else
+	  log("SYSERR: Unknown tag [%s] in file [%s].", tag, filename);
+	break;
+      case 't':
+      case 'T':
+        if (str_cmp(tag, "Time") == 0) {
+	  if (warn == NULL) {
+	    log("SYSERR: 'Time' tag without #WARNING tag in file '%s'.",
+		filename);
+	  } else
+	    warn->time = (time_t) atoi(line);
+	} else
+	  log("SYSERR: Unknown tag [%s] in file [%s].", tag, filename);
+	break;
+      default:
+	log("SYSERR: Unknown tag [%s] in file [%s].", tag, filename);
+	break;
+      }
+    }
+
+    /* Guard against an incomplete warning. */
+    if (warn != NULL) {
+      log("SYSERR: #WARNING tag with no #END in file '%s'.", filename);
+      if (warn->admin != NULL)
+	free(warn->admin);
+      if (warn->message != NULL)
+	free(warn->message);
+      free(warn);
+    }
+
+    /* Close the file. */
+    fclose(file);
+  }
+} 
+
+
+
+void save_warnings(struct char_data *ch)
+{
+  char filename[MAX_STRING_LENGTH];
+  FILE *file = NULL;
+  struct warning_data *warn = NULL;
+
+  if (!get_filename(filename, sizeof(filename), WARN_FILE, GET_NAME(ch)))
+    log("save_warnings(): Couldn't get filename for %s.", GET_NAME(ch));
+  else {
+    if (GET_WARNINGS(ch) == NULL)
+      remove(filename);
+    else if ((file = fopen(filename, "wt")) == NULL) {
+      log("SYSERR: Couldn't save warnings for %s in '%s'.", GET_NAME(ch),
+	filename);
+    } else {
+      for (warn = GET_WARNINGS(ch); warn != NULL; warn = warn->next) {
+	fprintf(file, "#WARNING\n");
+	fprintf(file, "Admin           %s\n", warn->admin);
+	fprintf(file, "Message         %s\n", warn->message);
+	fprintf(file, "Time            %d\n", (int) warn->time);
+	fprintf(file, "#END\n");
+      }
+      fclose(file);
+    }
+  }
+}
