diff -uprN ../stk/Makefile ./Makefile
--- ../stk/Makefile	Sat Apr 25 22:20:43 1998
+++ ./Makefile	Tue Jun  9 22:33:20 1998
@@ -9,7 +9,7 @@ CC = gcc
 MYFLAGS = -Wall
 
 #flags for profiling (see hacker.doc for more information)
-PROFILE = 
+PROFILE =
 
 ##############################################################################
 # Do Not Modify Anything Below This Line (unless you know what you're doing) #
@@ -24,14 +24,14 @@ OBJFILES = comm.o act.comm.o act.informa
 	castle.o class.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
+	spell_parser.o spells.o utils.o weather.o buffer.o
 
 CXREF_FILES = act.comm.c act.informative.c act.item.c act.movement.c \
 	act.offensive.c act.other.c act.social.c act.wizard.c ban.c 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
+	spell_parser.c spells.c utils.c weather.c buffer.c
 
 default: all
 
@@ -48,7 +48,14 @@ circle:
 	$(MAKE) $(BINDIR)/circle
 
 $(BINDIR)/circle : $(OBJFILES)
-	$(CC) -o $(BINDIR)/circle $(PROFILE) $(OBJFILES)   
+	$(CC) -o $(BINDIR)/circle $(PROFILE) $(OBJFILES)
+
+thread.o: thread.c conf.h sysdep.h structs.h utils.h buffer.h
+	$(CC) $(CFLAGS) -c thread.c
+bufhash: bufhash.c
+	$(CC) $(CFLAGS) -o $(BINDIR)/bufhash bufhash.c
+thread: thread.o buffer.o
+	$(CC) $(CFLAGS) -o $(BINDIR)/thread thread.o buffer.o -lpthread
 
 clean:
 	rm -f *.o
@@ -91,104 +98,109 @@ ref:
 # gcc -MM)
 
 act.comm.o: act.comm.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h \
-  handler.h db.h screen.h
+  handler.h db.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) act.comm.c
 act.informative.o: act.informative.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h screen.h
+  interpreter.h handler.h db.h spells.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) act.informative.c
 act.item.o: act.item.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h \
-  handler.h db.h spells.h
+  handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) act.item.c
 act.movement.o: act.movement.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h house.h
+  interpreter.h handler.h db.h spells.h house.h buffer.h
 	$(CC) -c $(CFLAGS) act.movement.c
 act.offensive.o: act.offensive.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h
+  interpreter.h handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) act.offensive.c
 act.other.o: act.other.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h \
-  handler.h db.h spells.h screen.h house.h
+  handler.h db.h spells.h screen.h house.h buffer.h
 	$(CC) -c $(CFLAGS) act.other.c
 act.social.o: act.social.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h
+  interpreter.h handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) act.social.c
 act.wizard.o: act.wizard.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h house.h screen.h
+  interpreter.h handler.h db.h spells.h house.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) act.wizard.c
-ban.o: ban.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h db.h
+ban.o: ban.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h \
+  db.h buffer.h
 	$(CC) -c $(CFLAGS) ban.c
 boards.o: boards.c conf.h sysdep.h structs.h utils.h comm.h db.h boards.h \
-  interpreter.h handler.h
+  interpreter.h handler.h buffer.h
 	$(CC) -c $(CFLAGS) boards.c
 castle.o: castle.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h \
-  handler.h db.h spells.h
+  handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) castle.c
-class.o: class.c conf.h sysdep.h structs.h db.h utils.h spells.h interpreter.h
+class.o: class.c conf.h sysdep.h structs.h db.h utils.h spells.h \
+  interpreter.h buffer.h
 	$(CC) -c $(CFLAGS) class.c
 comm.o: comm.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h \
-  db.h house.h
+  db.h house.h buffer.h
 	$(CC) -c $(CFLAGS) comm.c
 config.o: config.c conf.h sysdep.h structs.h
 	$(CC) -c $(CFLAGS) config.c
 constants.o: constants.c conf.h sysdep.h structs.h
 	$(CC) -c $(CFLAGS) constants.c
 db.o: db.c conf.h sysdep.h structs.h utils.h db.h comm.h handler.h spells.h mail.h \
-  interpreter.h house.h
+  interpreter.h house.h buffer.h
 	$(CC) -c $(CFLAGS) db.c
 fight.o: fight.c conf.h sysdep.h structs.h utils.h comm.h handler.h interpreter.h \
-  db.h spells.h screen.h
+  db.h spells.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) fight.c
 graph.o: graph.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h \
-  db.h spells.h
+  db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) graph.c
 handler.o: handler.c conf.h sysdep.h structs.h utils.h comm.h db.h handler.h \
-  interpreter.h spells.h
+  interpreter.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) handler.c
 house.o: house.c conf.h sysdep.h structs.h comm.h handler.h db.h interpreter.h \
-  utils.h house.h
+  utils.h house.h buffer.h
 	$(CC) -c $(CFLAGS) house.c
 interpreter.o: interpreter.c conf.h sysdep.h structs.h comm.h interpreter.h db.h \
-  utils.h spells.h handler.h mail.h screen.h
+  utils.h spells.h handler.h mail.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) interpreter.c
 limits.o: limits.c conf.h sysdep.h structs.h utils.h spells.h comm.h db.h \
-  handler.h
+  handler.h buffer.h
 	$(CC) -c $(CFLAGS) limits.c
-magic.o: magic.c conf.h sysdep.h structs.h utils.h comm.h spells.h handler.h db.h
+magic.o: magic.c conf.h sysdep.h structs.h utils.h comm.h spells.h handler.h \
+  db.h buffer.h
 	$(CC) -c $(CFLAGS) magic.c
 mail.o: mail.c conf.h sysdep.h structs.h utils.h comm.h db.h interpreter.h \
-  handler.h mail.h
+  handler.h mail.h buffer.h
 	$(CC) -c $(CFLAGS) mail.c
 mobact.o: mobact.c conf.h sysdep.h structs.h utils.h db.h comm.h interpreter.h \
-  handler.h spells.h
+  handler.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) mobact.c
 modify.o: modify.c conf.h sysdep.h structs.h utils.h interpreter.h handler.h db.h \
-  comm.h spells.h mail.h boards.h
+  comm.h spells.h mail.h boards.h buffer.h
 	$(CC) -c $(CFLAGS) modify.c
 objsave.o: objsave.c conf.h sysdep.h structs.h comm.h handler.h db.h \
-  interpreter.h utils.h spells.h
+  interpreter.h utils.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) objsave.c
 olc.o: olc.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h db.h \
-  olc.h
+  olc.h buffer.h
 	$(CC) -c $(CFLAGS) olc.c
 random.o: random.c utils.h
 	$(CC) -c $(CFLAGS) random.c
 shop.o: shop.c conf.h sysdep.h structs.h comm.h handler.h db.h interpreter.h \
-  utils.h shop.h
+  utils.h shop.h buffer.h
 	$(CC) -c $(CFLAGS) shop.c
 spec_assign.o: spec_assign.c conf.h sysdep.h structs.h db.h interpreter.h \
-  utils.h
+  utils.h buffer.h
 	$(CC) -c $(CFLAGS) spec_assign.c
 spec_procs.o: spec_procs.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h
+  interpreter.h handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) spec_procs.c
 spell_parser.o: spell_parser.c conf.h sysdep.h structs.h utils.h interpreter.h \
-  spells.h handler.h comm.h db.h
+  spells.h handler.h comm.h db.h buffer.h
 	$(CC) -c $(CFLAGS) spell_parser.c
 spells.o: spells.c conf.h sysdep.h structs.h utils.h comm.h spells.h handler.h \
-  db.h
+  db.h buffer.h
 	$(CC) -c $(CFLAGS) spells.c
 utils.o: utils.c conf.h sysdep.h structs.h utils.h comm.h screen.h spells.h \
-  handler.h
+  handler.h buffer.h
 	$(CC) -c $(CFLAGS) utils.c
 weather.o: weather.c conf.h sysdep.h structs.h utils.h comm.h handler.h \
-  interpreter.h db.h
+  interpreter.h db.h buffer.h
 	$(CC) -c $(CFLAGS) weather.c
+buffer.o: buffer.c conf.h sysdep.h structs.h utils.h interpreter.h buffer.h
+	$(CC) -c $(CFLAGS) buffer.c
diff -uprN ../stk/Makefile.in ./Makefile.in
--- ../stk/Makefile.in	Sat Apr 25 22:20:42 1998
+++ ./Makefile.in	Tue Jun  9 23:03:17 1998
@@ -23,14 +23,14 @@ OBJFILES = comm.o act.comm.o act.informa
 	castle.o class.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
+	spell_parser.o spells.o utils.o weather.o buffer.o
 
 CXREF_FILES = act.comm.c act.informative.c act.item.c act.movement.c \
 	act.offensive.c act.other.c act.social.c act.wizard.c ban.c 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
+	spell_parser.c spells.c utils.c weather.c buffer.c
 
 default: all
 
@@ -90,104 +90,104 @@ ref:
 # gcc -MM)
 
 act.comm.o: act.comm.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h \
-  handler.h db.h screen.h
+  handler.h db.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) act.comm.c
 act.informative.o: act.informative.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h screen.h
+  interpreter.h handler.h db.h spells.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) act.informative.c
 act.item.o: act.item.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h \
-  handler.h db.h spells.h
+  handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) act.item.c
 act.movement.o: act.movement.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h house.h
+  interpreter.h handler.h db.h spells.h house.h buffer.h
 	$(CC) -c $(CFLAGS) act.movement.c
 act.offensive.o: act.offensive.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h
+  interpreter.h handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) act.offensive.c
 act.other.o: act.other.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h \
-  handler.h db.h spells.h screen.h house.h
+  handler.h db.h spells.h screen.h house.h buffer.h
 	$(CC) -c $(CFLAGS) act.other.c
 act.social.o: act.social.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h
+  interpreter.h handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) act.social.c
 act.wizard.o: act.wizard.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h house.h screen.h
+  interpreter.h handler.h db.h spells.h house.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) act.wizard.c
-ban.o: ban.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h db.h
+ban.o: ban.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h db.h buffer.h
 	$(CC) -c $(CFLAGS) ban.c
 boards.o: boards.c conf.h sysdep.h structs.h utils.h comm.h db.h boards.h \
-  interpreter.h handler.h
+  interpreter.h handler.h buffer.h
 	$(CC) -c $(CFLAGS) boards.c
 castle.o: castle.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h \
-  handler.h db.h spells.h
+  handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) castle.c
-class.o: class.c conf.h sysdep.h structs.h db.h utils.h spells.h interpreter.h
+class.o: class.c conf.h sysdep.h structs.h db.h utils.h spells.h interpreter.h buffer.h
 	$(CC) -c $(CFLAGS) class.c
 comm.o: comm.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h \
-  db.h house.h
+  db.h house.h buffer.h
 	$(CC) -c $(CFLAGS) comm.c
 config.o: config.c conf.h sysdep.h structs.h
 	$(CC) -c $(CFLAGS) config.c
 constants.o: constants.c conf.h sysdep.h structs.h
 	$(CC) -c $(CFLAGS) constants.c
 db.o: db.c conf.h sysdep.h structs.h utils.h db.h comm.h handler.h spells.h mail.h \
-  interpreter.h house.h
+  interpreter.h house.h buffer.h
 	$(CC) -c $(CFLAGS) db.c
 fight.o: fight.c conf.h sysdep.h structs.h utils.h comm.h handler.h interpreter.h \
-  db.h spells.h screen.h
+  db.h spells.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) fight.c
 graph.o: graph.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h \
-  db.h spells.h
+  db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) graph.c
 handler.o: handler.c conf.h sysdep.h structs.h utils.h comm.h db.h handler.h \
-  interpreter.h spells.h
+  interpreter.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) handler.c
 house.o: house.c conf.h sysdep.h structs.h comm.h handler.h db.h interpreter.h \
-  utils.h house.h
+  utils.h house.h buffer.h
 	$(CC) -c $(CFLAGS) house.c
 interpreter.o: interpreter.c conf.h sysdep.h structs.h comm.h interpreter.h db.h \
-  utils.h spells.h handler.h mail.h screen.h
+  utils.h spells.h handler.h mail.h screen.h buffer.h
 	$(CC) -c $(CFLAGS) interpreter.c
 limits.o: limits.c conf.h sysdep.h structs.h utils.h spells.h comm.h db.h \
-  handler.h
+  handler.h buffer.h
 	$(CC) -c $(CFLAGS) limits.c
-magic.o: magic.c conf.h sysdep.h structs.h utils.h comm.h spells.h handler.h db.h
+magic.o: magic.c conf.h sysdep.h structs.h utils.h comm.h spells.h handler.h db.h buffer.h
 	$(CC) -c $(CFLAGS) magic.c
 mail.o: mail.c conf.h sysdep.h structs.h utils.h comm.h db.h interpreter.h \
-  handler.h mail.h
+  handler.h mail.h buffer.h
 	$(CC) -c $(CFLAGS) mail.c
 mobact.o: mobact.c conf.h sysdep.h structs.h utils.h db.h comm.h interpreter.h \
-  handler.h spells.h
+  handler.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) mobact.c
 modify.o: modify.c conf.h sysdep.h structs.h utils.h interpreter.h handler.h db.h \
-  comm.h spells.h mail.h boards.h
+  comm.h spells.h mail.h boards.h buffer.h
 	$(CC) -c $(CFLAGS) modify.c
 objsave.o: objsave.c conf.h sysdep.h structs.h comm.h handler.h db.h \
-  interpreter.h utils.h spells.h
+  interpreter.h utils.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) objsave.c
 olc.o: olc.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h db.h \
-  olc.h
+  olc.h buffer.h
 	$(CC) -c $(CFLAGS) olc.c
 random.o: random.c utils.h
 	$(CC) -c $(CFLAGS) random.c
 shop.o: shop.c conf.h sysdep.h structs.h comm.h handler.h db.h interpreter.h \
-  utils.h shop.h
+  utils.h shop.h buffer.h
 	$(CC) -c $(CFLAGS) shop.c
 spec_assign.o: spec_assign.c conf.h sysdep.h structs.h db.h interpreter.h \
-  utils.h
+  utils.h buffer.h
 	$(CC) -c $(CFLAGS) spec_assign.c
 spec_procs.o: spec_procs.c conf.h sysdep.h structs.h utils.h comm.h \
-  interpreter.h handler.h db.h spells.h
+  interpreter.h handler.h db.h spells.h buffer.h
 	$(CC) -c $(CFLAGS) spec_procs.c
 spell_parser.o: spell_parser.c conf.h sysdep.h structs.h utils.h interpreter.h \
-  spells.h handler.h comm.h db.h
+  spells.h handler.h comm.h db.h buffer.h
 	$(CC) -c $(CFLAGS) spell_parser.c
 spells.o: spells.c conf.h sysdep.h structs.h utils.h comm.h spells.h handler.h \
-  db.h
+  db.h buffer.h
 	$(CC) -c $(CFLAGS) spells.c
 utils.o: utils.c conf.h sysdep.h structs.h utils.h comm.h screen.h spells.h \
-  handler.h
+  handler.h buffer.h
 	$(CC) -c $(CFLAGS) utils.c
 weather.o: weather.c conf.h sysdep.h structs.h utils.h comm.h handler.h \
-  interpreter.h db.h
+  interpreter.h db.h buffer.h
 	$(CC) -c $(CFLAGS) weather.c
diff -uprN ../stk/act.comm.c ./act.comm.c
--- ../stk/act.comm.c	Sat Apr 25 22:20:42 1998
+++ ./act.comm.c	Wed May  6 20:09:37 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/act.comm.o and ./act.comm.o differ
diff -uprN ../stk/act.informative.c ./act.informative.c
--- ../stk/act.informative.c	Sat Apr 25 22:20:42 1998
+++ ./act.informative.c	Wed May  6 20:09:37 1998
@@ -12,6 +12,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/act.informative.o and ./act.informative.o differ
diff -uprN ../stk/act.item.c ./act.item.c
--- ../stk/act.item.c	Sat Apr 25 22:20:42 1998
+++ ./act.item.c	Wed May  6 20:09:37 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/act.item.o and ./act.item.o differ
diff -uprN ../stk/act.movement.c ./act.movement.c
--- ../stk/act.movement.c	Sat Apr 25 22:20:42 1998
+++ ./act.movement.c	Wed May  6 20:09:37 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/act.movement.o and ./act.movement.o differ
diff -uprN ../stk/act.offensive.c ./act.offensive.c
--- ../stk/act.offensive.c	Sat Apr 25 22:20:42 1998
+++ ./act.offensive.c	Wed May  6 20:09:37 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/act.offensive.o and ./act.offensive.o differ
diff -uprN ../stk/act.other.c ./act.other.c
--- ../stk/act.other.c	Sat Apr 25 22:20:42 1998
+++ ./act.other.c	Wed May  6 20:09:37 1998
@@ -14,6 +14,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/act.other.o and ./act.other.o differ
diff -uprN ../stk/act.social.c ./act.social.c
--- ../stk/act.social.c	Sat Apr 25 22:20:42 1998
+++ ./act.social.c	Wed May  6 20:09:38 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/act.social.o and ./act.social.o differ
diff -uprN ../stk/act.wizard.c ./act.wizard.c
--- ../stk/act.wizard.c	Sat Apr 25 22:20:42 1998
+++ ./act.wizard.c	Fri May 22 21:41:02 1998
@@ -12,6 +12,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
@@ -1901,6 +1902,7 @@ ACMD(do_show)
     { "godrooms",	LVL_GOD },
     { "shops",		LVL_IMMORT },
     { "houses",		LVL_GOD },
+    { "buffers",	LVL_GOD },			/* 10 */
     { "\n", 0 }
   };
 
@@ -2002,6 +2004,9 @@ ACMD(do_show)
     sprintf(buf, "%s  %5d large bufs\r\n", buf, buf_largecount);
     sprintf(buf, "%s  %5d buf switches     %5d overflows\r\n", buf,
 	    buf_switches, buf_overflows);
+    sprintf(buf, "%s  %5d buf cache hits   %5d buf cache misses\r\n", buf,
+	    buffer_cache_stat[BUFFER_CACHE_HITS],
+	    buffer_cache_stat[BUFFER_CACHE_MISSES]);
     send_to_char(buf, ch);
     break;
   case 5:
@@ -2033,6 +2038,11 @@ ACMD(do_show)
     break;
   case 9:
     hcontrol_list_houses(ch);
+    break;
+  case 10:
+    show_buffers(ch, -1, 1);
+    show_buffers(ch, -1, 2);
+    show_buffers(ch, -1, 0);
     break;
   default:
     send_to_char("Sorry, I don't understand that.\r\n", ch);
Binary files ../stk/act.wizard.o and ./act.wizard.o differ
diff -uprN ../stk/ban.c ./ban.c
--- ../stk/ban.c	Sat Apr 25 22:20:42 1998
+++ ./ban.c	Wed May  6 20:09:38 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/ban.o and ./ban.o differ
diff -uprN ../stk/boards.c ./boards.c
--- ../stk/boards.c	Sat Apr 25 22:20:42 1998
+++ ./boards.c	Wed May 27 17:49:43 1998
@@ -47,8 +47,8 @@ TO ADD A NEW BOARD, simply follow our ea
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "db.h"
@@ -222,10 +222,14 @@ void Board_write_message(int board_type,
   sprintf(buf2, "(%s)", GET_NAME(ch));
   sprintf(buf, "%6.10s %-12s :: %s", tmstr, buf2, arg);
   len = strlen(buf) + 1;
+#if 1
+  CREATE(NEW_MSG_INDEX(board_type).heading, char, len);
+#else
   if (!(NEW_MSG_INDEX(board_type).heading = (char *) malloc(sizeof(char) * len))) {
     send_to_char("The board is malfunctioning - sorry.\r\n", ch);
     return;
   }
+#endif
   strcpy(NEW_MSG_INDEX(board_type).heading, buf);
   NEW_MSG_INDEX(board_type).heading[len - 1] = '\0';
   NEW_MSG_INDEX(board_type).level = GET_LEVEL(ch);
@@ -473,10 +477,14 @@ void Board_load_board(int board_type)
       Board_reset_board(board_type);
       return;
     }
+#if 1
+    CREATE(tmp1, char, len1);
+#else
     if (!(tmp1 = (char *) malloc(sizeof(char) * len1))) {
       log("SYSERR: Error - malloc failed for board header");
       exit(1);
     }
+#endif
     fread(tmp1, sizeof(char), len1, fl);
     MSG_HEADING(board_type, i) = tmp1;
 
@@ -486,10 +494,14 @@ void Board_load_board(int board_type)
 	Board_reset_board(board_type);
 	return;
       }
+#if 1
+      CREATE(tmp2, char, len2);
+#else
       if (!(tmp2 = (char *) malloc(sizeof(char) * len2))) {
 	log("SYSERR: malloc failed for board text");
 	exit(1);
       }
+#endif
       fread(tmp2, sizeof(char), len2, fl);
       msg_storage[MSG_SLOTNUM(board_type, i)] = tmp2;
     }
Binary files ../stk/boards.o and ./boards.o differ
diff -uprN ../stk/buffer.c ./buffer.c
--- ../stk/buffer.c	Wed Dec 31 19:00:00 1969
+++ ./buffer.c	Tue Jun  9 22:57:36 1998
@@ -0,0 +1,1404 @@
+/************************************************************************
+ * buffer.c - Advanced Buffer System				v1.8	*
+ *									*
+ * Designed for CircleMUD 3.0				May 30, 1998	*
+ ************************************************************************/
+
+#define __BUFFER_C__
+
+#include "conf.h"
+#include "sysdep.h"
+
+#include "structs.h"
+#include "buffer.h"
+#include "utils.h"
+#include "interpreter.h"
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * ... BUF_NUKE ...
+ * Set	: Use a memset() to clear each buffer after use.
+ * Unset: Do a *buffer = '\0'
+ * ... BUF_CHECK ...
+ * Set	: Report how much of each buffer the functions used.
+ * Unset: Don't report.
+ * ... BUF_OVERBOOT ...
+ * Set	: Reboot immediately on overflow. (recommended)
+ * Unset: Try to keep running after an overflow.
+ * ... BUF_DETAIL
+ * Set  : Log details of what is going on.
+ * Unset: Only report errors.
+ * ... BUF_VERBOSE ...
+ * Set	: Output extremely detailed information.
+ * Unset: Log only error messages.
+ */
+#define BUF_NUKE	(1 << 0)
+#define BUF_CHECK	(1 << 1)
+#define BUF_OVERBOOT	(1 << 2)
+#define BUF_DETAIL	(1 << 3)	/* VERBOSE overrides DETAIL. */
+#define BUF_VERBOSE	(1 << 4)
+/*
+ * Example: ush_int buffer_opt = BUF_OVERBOOT | BUF_VERBOSE | BUF_CHECK;
+ */
+ush_int buffer_opt = BUF_OVERBOOT;
+
+/*
+ * 1 = Check every pointer in every buffer allocated on every new allocation.
+ * 0 = Recommended, unless you are tracking memory corruption at the time.
+ *
+ * NOTE: This only works under Linux.  Do not try to use with anything else!
+ */
+#define BUFFER_EXTREME_PARANOIA	0
+
+/*
+ * 1 = Enable multithreaded support.  You _must_ have POSIX threads for this!
+ * 0 = Use the standard heartbeat() method of freeing the buffers.
+ */
+#define BUFFER_THREADED	0
+
+/*
+ * MAGIC_NUMBER - Any arbitrary character unlikely to show up in a string.
+ *	In this case, we use a control character. (0x6)
+ * BUFFER_LIFE - How long can a buffer be unused before it is free()'d?
+ * LEASE_LIFE - The amount of time a buffer can be used before it is
+ *	considered forgotten and released automatically.
+ */
+#if !defined(MAGIC_NUMBER)
+#define MAGIC_NUMBER	(0x6)	/* Also in bpl13. */
+#endif
+#define BUFFER_LIFE	(600 RL_SEC)	/* 5 minutes */
+#define LEASE_LIFE	(60 RL_SEC)	/* 1 minute */
+
+/*
+ * MEMORY ARRAY OPTIONS
+ *
+ * If you change the maximum or minimum allocations, you'll probably also want
+ * to change the hash number.  See the 'bufhash.c' program for determining a
+ * decent one.  Think prime numbers.
+ *
+ * MAX_ALLOC - The largest buffer magnitude allowed without griping about it.
+ * MIN_ALLOC - The lowest buffer magnitude ever handed out.  If 20 bytes are
+ *	requested, we will allocate this size buffer anyway.
+ * BUFFER_HASH - The direct size->array hash mapping number. This is _not_ an
+	arbitrary number.
+ * CACHE_SIZE - How many data->buffer mappings to maintain.
+ */
+#define MAX_ALLOC	(16)	/* 2**16 = 65536 bytes	*/
+#define MIN_ALLOC	(6)	/* 2**6  = 64 bytes	*/
+#define BUFFER_HASH	(11)	/* Not arbitrary! */
+#define CACHE_SIZE	(3 + BUFFER_MEMORY * 7)	/* Cache is 10 in that case. */
+
+/*
+ * End of configurables section.
+ * ----------------------------------------------------------------------------
+ */
+
+/*
+ * Don't change these, they are derived from other settings above.
+ *
+ * B_FREELIFE - The buffer life in pulses.
+ * B_GIVELIFE - The lease life in pulses.
+ * BUFFER_MINSIZE - The smallest buffer actually allocated, in bytes.
+ */
+#define BUFFER_MINSIZE	(1 << MIN_ALLOC)	/* Minimum size, in bytes. */
+#define BUFFER_MAXSIZE	(1 << MAX_ALLOC)	/* Maximum size, in bytes. */
+#if BUFFER_THREADED
+#define B_FREELIFE	(BUFFER_LIFE / PASSES_PER_SEC)
+#define B_GIVELIFE	(BUFFER_LIFE / PASSES_PER_SEC)
+#else
+#define B_FREELIFE	(BUFFER_LIFE / PULSE_BUFFER / PASSES_PER_SEC)
+#define B_GIVELIFE	(LEASE_LIFE / PULSE_BUFFER / PASSES_PER_SEC)	
+#endif
+
+#if BUFFER_THREADED
+/*
+ * Assorted lock types.
+ */
+#define LOCK_NONE               0
+#define LOCK_ACQUIRE            1
+#define LOCK_WILL_CLEAR         2
+#define LOCK_WILL_FREE          4
+#define LOCK_WILL_REMOVE        8
+#endif
+
+/*
+ * NOTE: There are reasons for not making a 'mem_data' structure.  We would
+ * have to modify all of the iterator functions to work with both structures,
+ * possibly though void pointers, which is ugly.  Currently, the only change
+ * required was to pick get_{memory,buffer}_head() based on what type of
+ * variable is requested.  This does waste 4 bytes for every malloc() that we
+ * could save with a separate mem_data, but the added complexity is not worth
+ * it in my opinion.  Debugging isn't necessarily cheap. Beware of the union.
+ */
+struct buf_mem_data {
+  struct buf_data **buf;
+  struct buf_data **mem;
+};
+
+struct buf_data {
+  byte magic;		/* Have we been trashed?		*/
+#if BUFFER_THREADED
+  byte locked;		/* Don't touch this item, we're locked.	*/
+#endif
+  byte type;		/* What type of buffer are we?		*/
+  ush_int line;         /* What source code line is using this. */
+  size_t req_size;	/* How much did the function request?	*/
+  union {
+    sh_long life;	/* An idle counter to free unused ones.	(B) */
+    const char *var;	/* Name of variable allocated to.	(M) */
+  } var_life;
+  size_t size;          /* How large is this buffer?		*/
+  const char *who;      /* Name of the function using this.     */
+  char *data;           /* The buffer passed back to functions. */
+  struct buf_data *next;	/* Next structure.		*/
+};
+
+/*
+ * The union support.
+ */
+#define life	var_life.life
+#define var	var_life.var
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Global variables.
+ *  buffers - Head of the main buffer allocation linked list.
+ *  buffer_cache - A list of the most recently acquired buffers.
+ *  buffer_cache_stat - Holds how many buffer cache hits/misses we've had.
+ *  buffer_array - The minimum and maximum array ranges for 'buffers'.
+ *  buf_thread - Handle of the buffer thread.
+ *  buflistmutex - Make sure the threads don't stomp on each other.
+ */
+static struct buf_mem_data buffers;
+static struct buf_data *buffer_cache[CACHE_SIZE];
+int buffer_cache_stat[2] = { 0, 0 };	/* Used in act.wizard.c also. */
+static int buffer_array[2] = { 0, 0 };
+#define BUF_MAX	0	/* buffer_array */
+#define BUF_MIN	1
+
+/* External functions and variables. */
+extern void send_to_char(char *, struct char_data *);
+extern void send_to_all(char *);
+extern int circle_shutdown;
+extern int circle_reboot;
+
+/* Private functions. */
+static struct buf_data *new_buffer(size_t size, int type);
+static struct buf_data *malloc_buffer(size_t size, int type);
+static struct buf_data *in_cache(const char *data);
+static void free_buffer(struct buf_data *f);
+static void clear_buffer(struct buf_data *clearme);
+static void remove_buffer(struct buf_data *removeme);
+static int remove_from_cache(struct buf_data *removeme);
+static void add_to_cache(struct buf_data *addme);
+static int get_used(struct buf_data *buf);
+static int get_magnitude(ush_long number);
+#if BUFFER_THREADED
+static void *buffer_thread(void *);
+static void bufferlist_lock(const char *func, ush_int line);
+static void bufferlist_unlock(const char *func, ush_int line);
+static void buffer_lock(struct buf_data *buf, int type, const char *func, ush_int line);
+static void buffer_unlock(struct buf_data *buf, const char *func, ush_int line);
+#endif
+#if BUFFER_MEMORY
+static void really_free(void *ptr);
+#endif
+
+#if BUFFER_THREADED
+/*
+ * Wrapper macros.
+ */
+#include <pthread.h>
+static pthread_mutex_t buflistmutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_t buf_thread = 0;
+#define lock_buffers()		(bufferlist_lock(__FUNCTION__, __LINE__))
+#define unlock_buffers()	(bufferlist_unlock(__FUNCTION__, __LINE__))
+#define LOCK(buf, type)		(buffer_lock((buf), (type), __FUNCTION__, __LINE__))
+#define UNLOCK(buf)		(buffer_unlock((buf), __FUNCTION__, __LINE__))
+#define LOCKED(buf)		((buf)->locked)
+#else
+#define lock_buffers()		/* Not necessary. */
+#define unlock_buffers()	/* Not necessary. */
+#define LOCK(buf, type)		/* Not necessary. */
+#define UNLOCK(buf)		/* Not necessary. */
+#define LOCKED(buf)		(FALSE)
+#endif
+
+/*
+ * Useful macros.
+ */
+#define buffer_hash(x)		((x) % BUFFER_HASH)
+#define USED(x)			((x)->who)
+#define get_buffer_head()	(buffers.buf)
+#define set_buffer_head(x)	do { buffers.buf = (x); } while(0)
+#define get_memory_head()	(buffers.mem)
+#define set_memory_head(x)	do { buffers.mem = (x); } while(0)
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Private: count_bits(number to count)
+ * Find out how many bits are in a number.  Used to see if it can be
+ * rounded into a power of 2.
+ */
+static int count_bits(size_t y)
+{
+  int i, count = 0;
+
+  for (i = 0; i < sizeof(size_t) * 8; i++)
+    count += (y & (1 << i)) >> i;	/* Avoiding a jump to get 0 or 1. */
+  return count;
+}
+
+/*
+ * Private: round_to_pow2(number to round)
+ * Converts a number to the next highest power of two.
+ * 0000011000111010 (1594)
+ *        to
+ * 0000100000000000 (2048)
+ */
+static void round_to_pow2(size_t *y)
+{
+  int i, start;
+
+  if (count_bits(*y) <= 1)
+    return;
+
+  if (buffer_opt & BUF_VERBOSE)
+    fprintf(stderr, "BUF: round_to_pow2: %d -> ", *y);
+
+  for (i = sizeof(size_t) * 8, start = FALSE; i >= 0; i--) {
+    if (start)
+      *y &= ~(1 << i);
+    else if (*y & (1 << (i - 1))) {
+      start = TRUE;
+      *y |= (1 << i);
+    }
+  }
+  if (buffer_opt & BUF_VERBOSE) {
+    fprintf(stderr, "%d\n", *y);
+    fflush(stderr);
+  }
+}
+
+/*
+ * Private: buffer_reboot(none)
+ * Determine whether to reboot or continue after corruption.
+ */
+static void buffer_reboot(void)
+{
+  init_buffers();
+  if (buffer_opt & BUF_OVERBOOT) {
+    send_to_all("Emergency reboot.. come back in a minute or two.\r\n");
+    log("BUF: SYSERR: Emergency reboot, buffer corrupted!");
+    circle_shutdown = circle_reboot = 1;
+    return;
+  }
+  log("BUF: SYSERR: Buffer list cleared, crash imminent!");
+}
+
+#if BUFFER_EXTREME_PARANOIA	/* only works on Linux */
+
+#define valid_pointer(ptr)	(((unsigned long)(ptr) & 0x48000000))
+
+/*
+ * Private: buffer_sanity_check(nothing)
+ * Checks the entire buffer/memory list for overruns.
+ */
+static void buffer_sanity_check(struct buf_data **check)
+{
+  int magnitude, die = FALSE;
+
+  if (check == NULL) {
+    buffer_sanity_check(get_buffer_head());
+    buffer_sanity_check(get_memory_head());
+    return;
+  }
+  for (magnitude = 0; magnitude <= buffer_array[BUF_MAX]; magnitude++) {
+    struct buf_data *chk;
+    if (check[magnitude] && !valid_pointer(check[magnitude]) && (die = TRUE))
+      log("BUF: SYSERR: %s/%d head corrupted.", check == get_buffer_head() ? "buffer" : "memory", magnitude);
+    for (chk = check[magnitude]; chk; chk = chk->next) {
+      if (!valid_pointer(chk->data) && (die = TRUE))
+	log("BUF: SYSERR: %p->%p (data) corrupted.", chk, chk->data);
+      if (chk->next && !valid_pointer(chk->next) && (die = TRUE))
+	log("BUF: SYSERR: %p->%p (next) corrupted.", chk, chk->next);
+      if (chk->type != BT_MALLOC && ((unsigned long)chk->var & 0xff0000) != 0)
+	log("BUF: SYSERR: %p->%p is set on non-malloc memory.", chk, chk->var);
+      if (chk->magic != MAGIC_NUMBER && (die = TRUE))
+	log("BUF: SYSERR: %p->%d (magic) corrupted.", chk, chk->magic);
+      if (chk->data[chk->req_size] != MAGIC_NUMBER && (die = TRUE))
+	log("BUF: SYSERR: %p (%s:%d) overflowed requested size (%d).", chk, chk->who, chk->line, chk->req_size);
+      if (chk->data[chk->size] != MAGIC_NUMBER && (die = TRUE))
+	log("BUF: SYSERR: %p (%s:%d) overflowed real size (%d).", chk, chk->who, chk->line, chk->size);
+    }
+  }
+  if (die)
+    buffer_reboot();
+}
+#endif
+
+/*
+ * Private: magic_check(buffer to check)
+ * Makes sure a buffer hasn't been overwritten by something else.
+ */
+static void magic_check(struct buf_data *buf)
+{
+  if (buf->magic != MAGIC_NUMBER) {
+    char trashed[16];
+    strncpy(trashed, (char *)buf, 15);
+    trashed[15] = '\0';
+    log("BUF: SYSERR: Buffer %p was trashed! (%s)", buf, trashed);
+    buffer_reboot();
+  }
+}
+
+/*
+ * Private: get_magnitude(number)
+ * Simple function to avoid math library, and enforce maximum allocations.
+ * If it is not enforced here, we will overrun the bounds of the array.
+ */
+static int get_magnitude(ush_long y)
+{
+  ush_int number = buffer_hash(y);
+
+  if (number > buffer_array[BUF_MAX]) {
+    log("BUF: SYSERR: Hash result %d out of range 0-%d.", number, buffer_array[BUF_MAX]);
+    number = buffer_array[BUF_MAX];
+  }
+
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: get_magnitude: %ld bytes -> %d", y, number);
+
+  return number;
+}
+
+/*
+ * Private: add_to_cache(structure to add)
+ * Does what it says.
+ */
+static void add_to_cache(struct buf_data *addme)
+{
+  /*
+   * Move everybody right.
+   */
+  memmove(buffer_cache + 1, buffer_cache, sizeof(struct buf_data *) * (CACHE_SIZE - 1));
+
+  /*
+   * Add the structure to the front.
+   */
+  buffer_cache[0] = addme;
+}
+
+/*
+ * Private: remove_from_cache(structure to remove)
+ * Does what it says.
+ */
+static int remove_from_cache(struct buf_data *removeme)
+{
+  int i, ret = FALSE;
+
+  for (i = 0; i < CACHE_SIZE; i++)
+    if (buffer_cache[i] == removeme) {
+      buffer_cache[i] = NULL;
+      ret = TRUE;
+    }
+  return ret;
+}
+
+/*
+ * Private: in_cache(data to search for)
+ * Used internally since the cache expanded complexity.
+ */
+static struct buf_data *in_cache(const char *data)
+{
+  int i;
+  for (i = 0; i < CACHE_SIZE; i++)
+    if (buffer_cache[i] && buffer_cache[i]->data == data) {
+      buffer_cache_stat[BUFFER_CACHE_HITS]++;
+      if (buffer_opt & BUF_VERBOSE)
+	log("BUF: in_cache: Cache hit %p->%p from %p.", buffer_cache[i], buffer_cache[i]->data, data);
+#if BUFFER_THREADED
+      if (LOCKED(buffer_cache[i]))
+	log("BUF: in_cache: Giving out locked buffer reference %p.", buffer_cache[i]);
+#endif
+      return buffer_cache[i];
+    }
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: in_cache: Cache miss.");
+
+  buffer_cache_stat[BUFFER_CACHE_MISSES]++;
+  return NULL;
+}
+
+/*
+ * Public: exit_buffers()
+ * Called to clear out the buffer structures and log any BT_MALLOC's left.
+ */
+void exit_buffers(void)
+{
+  struct buf_data **head;
+  ush_int magnitude;
+
+  log("BUF: Shutting down.");
+
+#if BUFFER_THREADED
+  pthread_join(buf_thread, NULL);
+#endif
+
+  log("BUF: Clearing buffer memory.");
+  head = get_buffer_head();
+  for (magnitude = 0; magnitude <= buffer_array[BUF_MAX]; magnitude++) {
+    struct buf_data *b, *bn;
+    for (b = head[magnitude]; b; b = bn) {
+      bn = b->next;
+      LOCK(b, LOCK_WILL_CLEAR);
+      clear_buffer(b);
+      LOCK(b, LOCK_WILL_REMOVE);
+      remove_buffer(b);
+      LOCK(b, LOCK_WILL_FREE);
+      free_buffer(b);
+    }
+  }
+  log("BUF: Clearing malloc()'d memory.");
+  head = get_memory_head();
+  for (magnitude = 0; magnitude <= buffer_array[BUF_MAX]; magnitude++)
+    while (head[magnitude]) {
+      LOCK(head[magnitude], LOCK_WILL_CLEAR);
+      clear_buffer(head[magnitude]);
+    }
+  log("BUF: Done.");
+}
+
+/*
+ * Private: find_hash(none)
+ * Determine the size of the buffer array based on the hash size and the
+ * allocation limits.
+ */
+static int find_hash(void)
+{
+  int maxhash = 0, result, i;
+  
+  log("BUF: Calculating array size...");
+  for (i = MIN_ALLOC; i <= MAX_ALLOC; i++) {
+    result = buffer_hash(1 << i);
+    if (result > maxhash)
+      maxhash = result;
+    log("BUF: %7d -> %2d", 1 << i, result);
+  }
+  log("BUF: ...done.");
+  log("BUF: Array range is 0-%d.", maxhash);
+  buffer_array[BUF_MAX] = maxhash;
+  return maxhash;
+}
+
+/*
+ * Public: init_buffers(none)
+ * This is called from main() to get everything started.
+ */
+void init_buffers(void)
+{
+#if BUFFER_THREADED
+  int error = 0;
+#endif
+
+  lock_buffers();
+
+  find_hash();
+
+#if BUFFER_THREADED
+  /*
+   * If a buffer thread already exists, kill it.
+   */
+  if (buf_thread != 0)
+    pthread_cancel(buf_thread);
+
+  /*
+   * Initialize pthread information and start the thread.
+   */
+  if ((error = pthread_create(&buf_thread, NULL, buffer_thread, NULL)) < 0) {
+    perror("pthread_create");
+    printf("%d\n", error);
+    exit(1);
+  }
+#endif
+
+  /*
+   * Allocate room for the array of pointers.  We can't use the CREATE
+   * macro here because that uses the array we're trying to make!
+   */
+  set_buffer_head(calloc(buffer_array[BUF_MAX] + 1, sizeof(struct buf_data *)));
+  set_memory_head(calloc(buffer_array[BUF_MAX] + 1, sizeof(struct buf_data *)));
+
+  log("BUF: Buffer cache: %d elements, %d bytes.", CACHE_SIZE, sizeof(struct buf_data *) * CACHE_SIZE);
+  log("BUF: Allocations: %d-%d bytes", 1 << MIN_ALLOC, 1 << MAX_ALLOC);
+
+  /*
+   * Put any persistant buffers here.
+   * Ex: new_buffer(8192, BT_PERSIST);
+   */
+
+  unlock_buffers();
+}
+
+/*
+ * Private: decrement_all_buffers(none)
+ * Reduce the life on all buffers by 1.
+ */
+static void decrement_all_buffers(void)
+{
+  int magnitude;
+  struct buf_data *clear, **head = get_buffer_head();
+
+  for (magnitude = 0; magnitude <= buffer_array[BUF_MAX]; magnitude++)
+    for (clear = head[magnitude]; clear; clear = clear->next) {
+      if (clear->life < 0)
+	log("BUF: SYSERR: %p from %s:%d has %ld life.", clear, clear->who, clear->line, clear->life);
+      else if (clear->life != 0)	/* We don't want to go negative. */
+	clear->life--;
+    }
+}
+
+/*
+ * Private: release_old_buffers()
+ * Check for any used buffers that have no remaining life.
+ */
+static void release_old_buffers(void)
+{
+  int magnitude;
+  struct buf_data *relbuf, **head = get_buffer_head();
+
+  for (magnitude = 0; magnitude <= buffer_array[BUF_MAX]; magnitude++)
+    for (relbuf = head[magnitude]; relbuf; relbuf = relbuf->next) {
+      if (relbuf->type == BT_MALLOC) {
+	log("BUF: SYSERR: Ack, memory (%p, %s:%d, '%s') in buffer list!", relbuf, relbuf->who, relbuf->line, relbuf->var);
+	continue;
+      }
+      if (relbuf->life > 0)	/* We only want expired buffers. */
+	continue;
+      if (!USED(relbuf))	/* Can't release, no one is using this. */
+	continue;
+      if (LOCKED(relbuf))	/* Someone has this locked already. */
+	continue;
+      LOCK(relbuf, LOCK_WILL_CLEAR);
+      log("BUF: %s:%d forgot to release %d bytes in %p.", relbuf->who ? relbuf->who : "UNKNOWN", relbuf->line, relbuf->size, relbuf);
+      clear_buffer(relbuf);
+      UNLOCK(relbuf);
+    }
+}
+
+/*
+ * Private: free_old_buffers(void)
+ * Get rid of any old unused buffers.
+ */
+static void free_old_buffers(void)
+{
+  int magnitude;
+  struct buf_data *freebuf, *next_free, **head = get_buffer_head();
+
+  lock_buffers();
+
+  for (magnitude = 0; magnitude <= buffer_array[BUF_MAX]; magnitude++)
+    for (freebuf = head[magnitude]; freebuf; freebuf = next_free) {
+      next_free = freebuf->next;
+      if (freebuf->type != BT_STACK)	/* We don't free persistent ones. */
+	continue;
+      if (USED(freebuf))	/* Needs to be cleared first if used. */
+	continue;
+      if (freebuf->life > 0)	/* Hasn't expired yet. */
+	continue;
+      if (LOCKED(freebuf))	/* Already locked, skip it. */
+	continue;
+
+      LOCK(freebuf, LOCK_WILL_REMOVE);
+      remove_buffer(freebuf);
+      LOCK(freebuf, LOCK_WILL_FREE);
+      free_buffer(freebuf);
+    }
+
+  unlock_buffers();
+}
+
+/*
+ * Public: release_all_buffers()
+ * Forcibly release all buffers currently allocated.  This is useful to
+ * reclaim any forgotten buffers.
+ * See structs.h for PULSE_BUFFER to change the release rate.
+ */
+void release_all_buffers(void)
+{
+#if BUFFER_THREADED
+  /* We don't do anything, there's already a thread doing this. */
+#else
+  decrement_all_buffers();
+  release_old_buffers();
+  free_old_buffers();
+#endif
+}
+
+/*
+ * Private: remove_buffer(buffer to remove)
+ * Removes a buffer from the list without freeing it.
+ */
+static void remove_buffer(struct buf_data *removeme)
+{
+  struct buf_data **head, *traverse, *prev = NULL;
+  int magnitude;
+
+  if (!removeme) {
+    log("BUF: SYSERR: NULL buf_data given to remove_buffer.");
+    return;
+  }
+
+#if BUFFER_THREADED
+  if (LOCKED(removeme) != LOCK_WILL_REMOVE)
+    log("BUF: SYSERR: remove_buffer: Lock bit not properly set on %p!", removeme);
+#endif
+
+  head = (removeme->type == BT_MALLOC ? get_memory_head() : get_buffer_head());
+  magnitude = get_magnitude(removeme->size);
+
+  for (traverse = head[magnitude]; traverse; traverse = traverse->next) {
+    if (traverse == removeme) {
+      if (traverse == head[magnitude])
+	head[magnitude] = traverse->next;
+      else if (prev)
+	prev->next = traverse->next;
+      else
+	log("BUF: SYSERR: remove_buffer: Don't know what to do with %p.", removeme);
+#if BUFFER_THREADED
+      if (LOCKED(removeme) != LOCK_WILL_REMOVE)
+	log("BUF: SYSERR: remove_buffer: Lock bit removed from %p during operation!", removeme);
+#endif
+      return;
+    }
+    prev = traverse;
+  }
+  log("BUF: SYSERR: remove_buffer: Couldn't find the buffer %p from %s:%d.", removeme, removeme->who, removeme->line);
+}
+
+/*
+ * Private: clear_buffer(buffer to clear)
+ * This is used to declare an allocated buffer unused.
+ */
+static void clear_buffer(struct buf_data *clearme)
+{
+  if (clearme == NULL) {
+    log("BUF: SYSERR: clear_buffer: NULL argument.");
+    return;
+  }
+
+#if BUFFER_THREADED
+  if (LOCKED(clearme) != LOCK_WILL_CLEAR)
+    log("BUF: SYSERR: clear_buffer: Lock not properly set on %p!", clearme);
+#endif
+
+  magic_check(clearme);
+
+  /*
+   * If the magic number we set is not there then we have a suspected
+   * buffer overflow.
+   */
+  if (clearme->data[clearme->req_size] != MAGIC_NUMBER) {
+    log("BUF: SYSERR: Overflow in %p (%s) from %s:%d.",
+		clearme, clearme->type == BT_MALLOC ? clearme->var : "a buffer", clearme->who ? clearme->who : "UNKNOWN", clearme->line);
+    log("BUF: SYSERR: ... ticketed for doing %d in a %d (%d) zone.",
+		strlen(clearme->data) + 1, clearme->req_size, clearme->size);
+    if (clearme->data[clearme->size] == MAGIC_NUMBER) {
+      log("BUF: SYSERR: ... overflow did not compromise memory.");
+      clearme->data[clearme->req_size - 1] = '\0';
+      clearme->data[clearme->req_size] = MAGIC_NUMBER;
+    } else
+      buffer_reboot();
+  } else if (clearme->type == BT_MALLOC) {
+    lock_buffers();
+    LOCK(clearme, LOCK_WILL_REMOVE);
+    remove_buffer(clearme);
+    LOCK(clearme, LOCK_WILL_FREE);
+    free_buffer(clearme);
+    unlock_buffers();
+  } else {
+    if (buffer_opt & BUF_CHECK) {
+      /*
+       * If nothing in clearme->data return 0, else if paranoid_buffer, return
+       * the result of get_used(), otherwise just strlen() the buffer.
+       */
+      log("BUF: %s:%d used %d/%d bytes in %p.",
+		clearme->who, clearme->line, (clearme->data && *clearme->data ?
+		((buffer_opt & BUF_NUKE) ? get_used(clearme) :
+		strlen(clearme->data)) : 0), clearme->req_size, clearme);
+    }
+    if (buffer_opt & BUF_NUKE)
+      memset(clearme->data, '\0', clearme->size);
+    else
+      *clearme->data = '\0';
+    clearme->who = NULL;
+    clearme->line = 0;
+    clearme->req_size = clearme->size;	/* For exit_buffers() check. */
+    clearme->life = B_FREELIFE;
+#if BUFFER_THREADED
+    if (LOCKED(clearme) != LOCK_WILL_CLEAR)
+      log("BUF: SYSERR: clear_buffer: Someone cleared lock bit on %p.", clearme);
+#endif
+  }
+}
+
+/*
+ * Private: find_this_buffer(buffer data pointer)
+ * Used by detach_buffer() to locate the correct buffer.
+ */
+static struct buf_data *find_this_buffer(char *given, int type, int *searches)
+{
+  struct buf_data *clear, **b_head;
+  int magnitude, scanned;
+
+  /*
+   * We cache the last allocated buffer to speed up cases where we
+   * allocate a buffer and then free it shortly afterwards.
+   */
+  if ((clear = in_cache(given)) != NULL) {
+    if (searches)
+      *searches = 0;
+    return clear;
+  }
+
+  b_head = (type == BT_MALLOC ? get_memory_head() : get_buffer_head());
+
+  for (magnitude = 0, scanned = 0; magnitude <= buffer_array[BUF_MAX]; magnitude++)
+    for (clear = b_head[magnitude]; clear; clear = clear->next, scanned++)
+      if (clear->data == given)
+        goto got_it;	/* Cleaner than attempting two 'break;' commands. */
+
+got_it:
+
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: find_this_buffer: Scanning for %p, found %p->%p (%d scans).\n", given, clear, clear ? clear->data : NULL, scanned);
+
+  if (searches)
+    *searches = scanned;
+  return clear;
+}
+
+/*
+ * Public: as release_buffer(buffer data pointer)
+ * Used throughout the code to finish their use of the buffer.
+ */
+struct buf_data *detach_buffer(char *data, byte type, const char *func, const int line_n)
+{
+  struct buf_data *clear;
+  int scanned;
+
+  if (data == NULL || func == NULL) {
+    log("BUF: SYSERR: detach_buffer: Invalid information passed from %s:%d.", func, line_n);
+    return FALSE;
+  }
+
+  clear = find_this_buffer(data, type, &scanned);
+
+  if (clear == NULL) {
+    log("BUF: SYSERR: detach_buffer: No buffer->data %p found for %s:%d.", data, func, line_n);
+#if BUFFER_THREADED
+  } else if (LOCKED(clear)) {
+    if (LOCKED(clear) == LOCK_WILL_FREE)
+      log("BUF: detach_buffer: Buffer %p, requested by %s:%d, already slated to be freed.", clear, func, line_n);
+    else if (LOCKED(clear) == LOCK_WILL_CLEAR)
+      log("BUF: detach_buffer: Buffer %p, requested by %s:%d, already being detached.", clear, func, line_n);
+    else if (LOCKED(clear) == LOCK_WILL_REMOVE)
+      log("BUF: detach_buffer: Buffer %p, requested by %s:%d, already being removed from list.", clear, func, line_n);
+    else
+      log("BUF: detach_buffer: Buffer %p, requested by %s:%d, already locked.", clear, func, line_n);
+#endif
+  } else if (clear->who == NULL)
+    log("BUF: SYSERR: detach_buffer: Buffer %p, requested by %s:%d, already released. Locked: %d", clear, func, line_n, LOCKED(clear));
+  else {
+    LOCK(clear, LOCK_WILL_CLEAR);
+    if (buffer_opt & (BUF_VERBOSE | BUF_DETAIL))
+      log("BUF: %s:%d released %d bytes in '%s' (%p, %d scans) from %s:%d.", func, line_n,
+		clear->size, clear->type == BT_MALLOC ? clear->var : "a buffer", clear, scanned, clear->who ? clear->who : "UNKNOWN", clear->line);
+    clear_buffer(clear);
+    UNLOCK(clear);
+    return clear;
+  }
+  return NULL;
+}
+  
+/*
+ * Private: free_buffer(buffer)
+ * Internal function for getting rid of buffers.
+ */
+static void free_buffer(struct buf_data *f)
+{
+  if (f == NULL) {
+    log("BUF: SYSERR: free_buffer: NULL pointer.");
+    return;
+  } else if (f->type == BT_PERSIST)
+    log("BUF: SYSERR: free_buffer: Freeing %d byte persistant buffer %p.", f->size, f);
+  else if (f->type == BT_MALLOC) {
+    if (buffer_opt & (BUF_VERBOSE | BUF_DETAIL))
+      log("BUF: free_buffer: Freeing %d bytes in '%s' (%p) from %s:%d.", f->size, f->var, f, f->who, f->line);
+  } else
+    log("BUF: free_buffer: Freeing %d bytes in expired buffer %p.", f->size, f);
+
+#if BUFFER_THREADED
+  if (LOCKED(f) != LOCK_WILL_FREE)
+    log("BUF: SYSERR: free_buffer: Lock not properly set on %p!", f);
+#endif
+
+  /*
+   * Would be very bad to reuse this buffer after it is free()'d.
+   */
+  remove_from_cache(f);
+
+  if (f->data)
+    really_free(f->data);
+  else
+    log("BUF: SYSERR: free_buffer: Hey, no data in %p?", f);
+  really_free(f);
+}
+
+/*
+ * Private: new_buffer(size of buffer, type flag)
+ * Finds where it should place the new buffer it's trying to malloc.
+ */
+static struct buf_data *new_buffer(size_t size, int type)
+{
+  struct buf_data **buflist, *potential;
+  int magnitude;
+
+  if (size == 0) {
+    log("BUF: SYSERR: new_buffer: 0 byte buffer requested.");
+    return NULL;
+  }
+
+  /*
+   * This caused a severe bug when it was in malloc_buffer().  Basically the
+   * buffer would be placed according to the non-rounded size and then later
+   * rounded.  This would cause a lookup to fail later when the hash returns
+   * a different value based on the new size.
+   */
+  if (type != BT_MALLOC)
+    round_to_pow2(&size);
+
+  /*
+   * We separate the malloc and buffer lists for efficiency.
+   */
+  buflist = (type == BT_MALLOC ? get_memory_head() : get_buffer_head());
+  magnitude = get_magnitude(size);
+
+  /*
+   * The new method of prepending and rounding is extremely fast and clean.
+   */
+  potential = malloc_buffer(size, type);
+  potential->next = buflist[magnitude];
+  buflist[magnitude] = potential;
+  return potential;
+}
+
+/*
+ * Private: malloc_buffer(size of buffer, type flag)
+ * Creates a new buffer for use.
+ */
+static struct buf_data *malloc_buffer(size_t size, int type)
+{
+  struct buf_data *new_buf;
+
+  if (!(new_buf = (struct buf_data *)malloc(sizeof(struct buf_data)))) {
+    log("BUF: SYSERR: malloc_buffer: Failed %d byte 'buf_data' malloc().", sizeof(struct buf_data));
+    perror("malloc_buffer()");
+    return NULL;
+  }
+
+  memset(new_buf, 0, sizeof(struct buf_data));
+
+  LOCK(new_buf, LOCK_ACQUIRE);
+
+  if ((new_buf->data = (char *)malloc(size + 1)) == NULL)
+    log("BUF: SYSERR: malloc_buffer: Failed %d byte 'data' malloc().", size);
+
+  new_buf->magic = MAGIC_NUMBER;
+  new_buf->who = NULL;
+  new_buf->line = 0;
+  new_buf->type = type;
+  new_buf->next = NULL;
+  new_buf->size = size;
+  new_buf->req_size = size;	/* do_buffer fools overflow code otherwise. */
+  if (type != BT_MALLOC)
+    new_buf->life = B_FREELIFE;
+  else
+    new_buf->var = NULL;
+
+  if (buffer_opt & (BUF_VERBOSE | BUF_DETAIL))
+    log("BUF: malloc_buffer: Allocated %d byte buffer %p, %d byte overhead.",
+		new_buf->size, new_buf, sizeof(struct buf_data));
+
+  /* We emulate calloc(), remember? */
+  if (type == BT_MALLOC || buffer_opt & BUF_NUKE)
+    memset(new_buf->data, '\0', new_buf->size);
+  else
+    *new_buf->data = '\0';
+
+  /* Implant number here. */
+  new_buf->data[new_buf->size] = MAGIC_NUMBER;
+
+  return new_buf;
+}
+
+/*
+ * We no longer search for buffers outside the specified power of 2 since
+ * the next highest array index may actually be smaller.
+ */
+struct buf_data *find_free_buffer(size_t size)
+{
+  struct buf_data *search, **head = get_buffer_head();
+  int magnitude = get_magnitude(size), scans = 0;
+
+  lock_buffers();
+
+  for (search = head[magnitude]; search; search = search->next) {
+    scans++;
+    if (search->type == BT_MALLOC) {
+      log("BUF: SYSERR: Ack, memory (%p, %s:%d, '%s') in buffer list!", search, search->who, search->line, search->var);
+      continue;
+    }
+    if (LOCKED(search))
+      continue;
+    if (USED(search))
+      continue;
+    if (search->size < size)
+      continue;
+    LOCK(search, LOCK_ACQUIRE);
+    break;
+  }
+  if (buffer_opt & (BUF_VERBOSE | BUF_DETAIL))
+    log("BUF: find_free_buffer: %d scans for %d bytes, found %p.", scans, size, search);
+  unlock_buffers();
+  return search;
+}
+
+/*
+ * Public: as get_buffer(size of buffer)
+ * Requests a buffer from the free pool.  If a buffer of the desired size
+ * is not available, one is created.
+ */
+char *acquire_buffer(size_t size, int type, const char *varname, const char *who, ush_int line)
+{
+  struct buf_data *give;
+
+#if BUFFER_EXTREME_PARANOIA
+  buffer_sanity_check(NULL);
+#endif
+
+  if (size > BUFFER_MAXSIZE)
+    log("BUF: %s:%d requested %d bytes for '%s'.", who, line, size, varname);
+
+  if (size == 0) {
+    log("BUF: SYSERR: %s:%d requested 0 bytes.", who, line);
+    return NULL;
+  } else if (type == BT_MALLOC)
+    give = new_buffer(size, type);
+  else if ((give = find_free_buffer(size)) == NULL) {
+    /*
+     * Minimum buffer size, since small ones have high overhead.
+     */
+    ush_long allocate_size;
+    if ((allocate_size = size) < BUFFER_MINSIZE && type != BT_MALLOC)
+      allocate_size = BUFFER_MINSIZE;
+
+    if (buffer_opt & (BUF_VERBOSE | BUF_DETAIL))
+      log("BUF: acquire_buffer: Making a new %ld byte buffer for %s:%d.", allocate_size, who, line);
+
+    /*
+     * If we don't have a valid pointer by now, we're out of memory so just
+     * return NULL and hope things don't crash too soon.
+     */
+    if ((give = new_buffer(allocate_size, type)) == NULL)
+      return NULL;
+  }
+
+  magic_check(give);
+
+#if BUFFER_THREADED
+  if (LOCKED(give) != LOCK_ACQUIRE) {
+    log("BUF: SYSERR: acquire_buffer: Someone stole my buffer.");
+    abort();
+  }
+#endif
+
+  give->who = who;
+  give->line = line;
+  give->req_size = size;
+
+  if (type != BT_MALLOC)
+    give->life = B_GIVELIFE;
+  else
+    give->var = varname;
+
+  if (buffer_opt & (BUF_VERBOSE | BUF_DETAIL))
+    log("BUF: %s:%d requested %d bytes for '%s', received %d in %p.", who, line, size, varname ? varname : "a buffer", give->size, give);
+
+  /*
+   * Plant a magic number to see if someone overruns the buffer. If the first
+   * character of the buffer is not NUL then somehow our memory was
+   * overwritten...most likely by someone doing a release_buffer() and
+   * keeping a secondary pointer to the buffer.
+   */
+  give->data[give->req_size] = MAGIC_NUMBER;
+  if (*give->data != '\0') {
+    log("BUF: SYSERR: acquire_buffer: Buffer %p is not empty as it ought to be!", give);
+    *give->data = '\0';
+  }
+
+#if 0
+  /* This will only work if the buf_data is allocated by valloc(). */
+  if (type == BT_MALLOC)
+    if (mprotect(give, sizeof(struct buf_data), PROT_READ) < 0)
+      abort();
+#endif
+
+  UNLOCK(give);
+
+  add_to_cache(give);	/* Cache this entry. */
+
+  return give->data;
+}
+
+/*
+ * Public: as 'show buffers'
+ * This is really only useful to see who has lingering buffers around
+ * or if you are curious.  It can't be called in the middle of a
+ * command run by a player so it'll usually show the same thing.
+ * You can call this with a NULL parameter to have it logged at any
+ * time though.
+ */
+/*
+ * XXX: This code works but is ugly and misleading.
+ */
+void show_buffers(struct char_data *ch, int buffer_type, int display_type)
+{
+  struct buf_data *disp, **head = NULL;
+  char *buf;
+  long i, size;
+  char *buf_type[] = { "Stack", "Persist", "Malloc" };
+
+  if (display_type == 1) {
+    head = get_buffer_head();
+    log("BUF: --- Buffer list --- (Inaccurate if not perfect hash.)");
+  } else if (display_type == 2) {
+    head = get_memory_head();
+    log("BUF: --- Memory list --- (Byte categories are inaccurate.)");
+  }
+  if (display_type == 1 || display_type == 2) {
+    for (size = MIN_ALLOC; size <= MAX_ALLOC; size++) {
+      long bytes;
+      int magnitude = get_magnitude(1 << size);
+      for (i = 0, bytes = 0, disp = head[magnitude]; disp; disp = disp->next, i++)
+	bytes += disp->size;
+      log("%5d bytes (%2d): %5ld items, %6ld bytes, %5ld overhead.", (1 << size), magnitude, i, bytes, sizeof(struct buf_data) * i);
+    }
+    return;
+  }
+
+  buf = get_buffer(MAX_STRING_LENGTH);
+  head = get_buffer_head();
+
+  for (size = 0; size <= buffer_array[BUF_MAX]; size++)
+    for (i = 0, disp = head[size]; disp; disp = disp->next) {
+      if (buffer_type != -1 && buffer_type != disp->type) /* -1 == all */
+	continue;
+      sprintf(buf, "%1ld #%2ld %5d bytes, Life: %5ld, Type: %7s, Allocated: %s/%d.%s",
+		size, ++i, disp->size, disp->life,
+		buf_type[(int)disp->type], disp->who ? disp->who : "unused",
+		disp->line ? disp->line : 0, ch ? "\r\n" : "");
+      if (ch)
+	send_to_char(buf, ch);
+      else
+	log(buf);
+    }
+
+  release_buffer(buf);
+}
+
+/*
+ * Tests the overflow code.  Do not use. :)
+ */
+ACMD(do_overflow)
+{
+  /*
+   * Write 256 bytes into the 130 byte buffer. Tweak to suit.
+   */
+  int write = 256, bufsize = 130;
+  char *buf;
+
+  buf = get_buffer(bufsize);
+  while (--write)
+    buf[write] = write;
+  release_buffer(buf);
+
+  if (ch)
+    send_to_char("Ok!\r\n", ch);
+}
+
+char *BUFFER_FORMAT =
+"buffer (add | delete) size (persistant | temporary)\r\n"
+"buffer verbose - toggle verbose mode.\r\n"
+"buffer detailed - toggle detailed mode.\r\n"
+"buffer paranoid - toggle between memset() or *buf = NUL.\r\n"
+"buffer check - toggle buffer usage checking.\r\n"
+"buffer overflow - toggle immediate reboot on overflow.\r\n";
+
+ACMD(do_buffer)
+{
+  char *arg1, *arg2, *arg3;
+  long size;
+  int persistant = FALSE;
+
+  /* This looks nifty. */
+  half_chop(argument, (arg1 = get_buffer(MAX_INPUT_LENGTH)), argument);
+  half_chop(argument, (arg2 = get_buffer(MAX_INPUT_LENGTH)), argument);
+  half_chop(argument, (arg3 = get_buffer(MAX_INPUT_LENGTH)), argument);
+
+  if (!*arg1)
+    size = -1;
+  else if (!*arg2 || !*arg3)
+    size = -2;
+  else if ((size = atoi(arg2)) == 0)
+    size = -1;
+  else if (is_abbrev(arg3, "persistant"))
+    persistant = TRUE;
+  else if (is_abbrev(arg3, "temporary"))
+    persistant = FALSE;
+  else
+    persistant = FALSE;
+
+  /* Don't need these now. */
+  release_buffer(arg2);
+  release_buffer(arg3);
+
+  if (size == -1)	/* Oops, error. */
+    send_to_char(BUFFER_FORMAT, ch);
+  else if (size == -2) {	/* -2 means a toggle command. */
+    if (is_abbrev(arg1, "verbose")) {
+      buffer_opt ^= BUF_VERBOSE;
+      send_to_char((buffer_opt & BUF_VERBOSE) ? "Verbose On.\r\n" : "Verbose Off.\r\n", ch);
+    } else if (is_abbrev(arg1, "detailed")) {
+      buffer_opt ^= BUF_DETAIL;
+      send_to_char((buffer_opt & BUF_DETAIL) ? "Detailed On.\r\n" : "Detailed Off.\r\n", ch);
+    } else if (is_abbrev(arg1, "paranoid")) {
+      buffer_opt ^= BUF_NUKE;
+      send_to_char((buffer_opt & BUF_NUKE) ? "Now paranoid.\r\n" : "No longer paranoid.\r\n", ch);
+    } else if (is_abbrev(arg1, "check")) {
+      buffer_opt ^= BUF_CHECK;
+      send_to_char((buffer_opt & BUF_CHECK) ? "Checking on.\r\n" : "Not checking.\r\n", ch);
+    } else if (is_abbrev(arg1, "overflow")) {
+      buffer_opt ^= BUF_OVERBOOT;
+      send_to_char((buffer_opt & BUF_OVERBOOT) ? "Reboot on overflow.\r\n" : "Will try to keep going.\r\n", ch);
+    } else
+      send_to_char(BUFFER_FORMAT, ch);
+  } else if (is_abbrev(arg1, "delete")) {
+    struct buf_data *toy, **b_head = get_buffer_head();
+    for (toy = b_head[get_magnitude(size)]; toy; toy = toy->next) {
+      if (USED(toy))
+	continue;
+      if (toy->size != size)
+	continue;
+      if (persistant != toy->type)
+	continue;
+      if (LOCKED(toy))
+	continue;
+      LOCK(toy, LOCK_WILL_REMOVE);
+      remove_buffer(toy);
+      LOCK(toy, LOCK_WILL_FREE);
+      free_buffer(toy);
+      break;
+    }
+    if (!toy)
+      send_to_char("Not found.\r\n", ch);
+  } else if (is_abbrev(arg1, "add"))
+    new_buffer(size, persistant); /* So easy. :) */
+  else
+    send_to_char(BUFFER_FORMAT, ch);
+
+  release_buffer(arg1);
+}
+
+/*
+ * Private: get_used(buffer to search)
+ * Used mainly in when releasing a buffer and check size is set.
+ * Does a backwards search for the first non-NUL character.
+ * Useful for when a function uses a large buffer and then uses
+ * it later for a smaller string, this will always return the
+ * most used value.
+ */
+static int get_used(struct buf_data *buf)
+{
+  int cnt;
+  for (cnt = buf->req_size - 1; cnt > 0 && buf->data[cnt] == '\0'; cnt--);
+  return cnt;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#if BUFFER_MEMORY
+
+char *debug_str_dup(const char *txt, const char *varname, const char *func, ush_int line)
+{
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: debug_str_dup: %d bytes from %s:%d for '%s'.", strlen(txt) + 1, func, line, varname);
+  return strcpy(acquire_buffer(strlen(txt) + 1, BT_MALLOC, varname, func, line), txt);
+}
+
+void *debug_calloc(size_t number, size_t size, const char *varname, const char *func, int line)
+{
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: debug_calloc: %d*%d bytes from %s:%d for '%s'.", number, size, func, line, varname);
+  return acquire_buffer(number * size, BT_MALLOC, varname, func, line);
+}
+
+void debug_free(void *ptr, const char *func, ush_int line)
+{
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: debug_free: %p from %s:%d.", ptr, func, line);
+  /* BT_MALLOC's are free()'d */
+  detach_buffer(ptr, BT_MALLOC, func, line);
+}
+
+void *debug_realloc(void *ptr, size_t size, const char *varname, const char *func, int line)
+{
+  void *xtra;
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: debug_realloc: '%s' (%p) resized to %d bytes for %s:%d.", varname, ptr, size, func, line);
+  xtra = acquire_buffer(size, BT_MALLOC, varname, func, line);
+  memmove(xtra, ptr, size);
+  detach_buffer(ptr, BT_MALLOC, func, line);
+  return xtra;
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+#if BUFFER_THREADED
+
+static void bufferlist_lock(const char *func, ush_int line)
+{
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: List locked by %s:%d.", func, line);
+  pthread_mutex_lock(&buflistmutex);
+}
+
+static void bufferlist_unlock(const char *func, ush_int line)
+{
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: List unlocked by %s:%d.", func, line);
+  pthread_mutex_unlock(&buflistmutex);
+}
+
+static void buffer_lock(struct buf_data *buf, int type, const char *func, ush_int line)
+{
+  if (buf == NULL)
+    log("BUF: SYSERR: buffer_lock: buf == NULL from %s:%d", func, line);
+  else if (LOCKED(buf) != LOCK_NONE && LOCKED(buf) != LOCK_WILL_REMOVE && type != LOCK_WILL_FREE && LOCKED(buf) != LOCK_WILL_CLEAR && type != LOCK_WILL_REMOVE)
+    log("BUF: SYSERR: buffer_lock: Trying to lock a buffer %p already locked, from %d to %d at %s:%d.", buf, LOCKED(buf), type, func, line);
+  else
+    buf->locked = type;
+}
+
+static void buffer_unlock(struct buf_data *buf, const char *func, ush_int line)
+{
+  if (buf == NULL)
+    log("BUF: SYSERR: buffer_unlock: buf == NULL from %s:%d.", func, line);
+  else if (LOCKED(buf) == LOCK_NONE)
+    log("BUF: SYSERR: buffer_unlock: Buffer %p isn't locked, from %s:%d.", buf, func, line);
+  else
+    buf->locked = LOCK_NONE;
+}
+
+/*
+ * Public: buffer_thread()
+ * If we ever have a multithreaded base, this would be a very nice
+ * application for it.  I plan to add a timer on how long someone
+ * has used a buffer.
+ *
+ * Work in progress.
+ */
+void *buffer_thread(void *nothing)
+{
+  struct timeval tv;
+
+  tv.tv_sec = 0;
+  tv.tv_usec = OPT_USEC;
+
+  log("BUF: Started buffer thread.");
+
+  while (!circle_shutdown) {
+    /* This is a generic function to reduce the life on all buffers by 1. */
+    decrement_all_buffers();
+
+    /* This checks for any buffers which were never released. */
+    release_old_buffers();
+
+    /* Here we free() any buffer which has not been used in a while. */
+    free_old_buffers();
+
+    /* Sleep the same amount of time the main loop does. */
+    select(0, NULL, NULL, NULL, &tv);
+  
+    /* See if we should exit. */
+    pthread_testcancel();
+  }
+
+  log("BUF: Buffer thread exited.");
+  return NULL;
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+#if 0 /* BUFFER_SNPRINTF */
+/*
+ * Buffer using sprintf() with bounds checking via snprintf()
+ */
+int bprintf(buffer *str, const char *format, ...)
+{
+  va_list args;
+  int chars;
+
+  va_start(args, format);
+  chars = vsnprintf(str->data, str->req_size, args);
+  va_end(args);
+  return chars;
+}
+
+/*
+ * Buffer wrapper.
+ * b2b = from buffer to buffer.
+ * s2b = from string to buffer.
+ * b2s = from buffer to string.
+ * s2s = doesn't exist, think about it.
+ *
+ * Another options would be to make b2b_cpy the default strcpy with #define.
+ */
+char *b2b_cpy(buffer *str1, const buffer *str2) { strcpy(str1->data, str2->data); }
+char *s2b_cpy(buffer *str1, const char *str2) { strcpy(str1->data, str2); }
+char *b2s_cpy(char *str1, const buffer *str2) { strcpy(str1, str2->data); )
+char *b2s_cat(char *str1, const buffer *str2) { strcat(str1, str2->data); }
+char *s2b_cat(buffer *str1, const char *str2) { strcat(str1->data, str2); }
+char *b2b_cat(buffer *str1, const buffer *str2) { strcat(str1->data, str2->data); }
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * At the bottom because we need to undefine the macro to get correct
+ * results.
+ */
+#if BUFFER_MEMORY
+#undef free
+void really_free(void *ptr)
+{
+  if (!ptr)	/* Your OS may already do this, but it's insignificant. */
+    return;
+  free(ptr);
+}
+#endif
diff -uprN ../stk/buffer.h ./buffer.h
--- ../stk/buffer.h	Wed Dec 31 19:00:00 1969
+++ ./buffer.h	Sat May 30 20:30:35 1998
@@ -0,0 +1,99 @@
+#if !defined(__BUFFER_H__)
+#define __BUFFER_H__
+
+/*
+ * CONFIGURABLES (aka, The place to shoot your own foot.) :)
+ * ---------------------------------------------------------
+ */
+
+/*
+ * 1 = use buffer system for all memory allocations using CREATE().
+ * 0 = use standard calloc/realloc in the CREATE() macro.
+ *
+ * The advantage to using the buffer system is that it will keep track of
+ * all your allocations and warn if one of the malloc buffers is overflowed.
+ * You can also view every allocation, what file and line it was allocated
+ * from, and how large it was.  This would be useful to detect a memory leak.
+ * Using this option, stock CircleMUD bpl12 takes 1.8 seconds to boot on my
+ * Pentium 133, and 1.6 seconds to boot without it.
+ */
+#define BUFFER_MEMORY	1
+
+/*
+ * 1 = Include original CircleMUD buffers too.
+ * 0 = Use only new buffer system.
+ *
+ * This will helpfully point out all your existing global buffer uses if you
+ * decide to convert to all buffer system.
+ */
+#define USE_CIRCLE_BUFFERS 1
+
+/*
+ * -----------------------------------------------------
+ * No tweakables below! See 'buffer.c' for more options.
+ * -----------------------------------------------------
+ */
+
+/*
+ * Handle GCC-isms.
+ */
+#if !defined(__GNUC__)
+#define __attribute__(x)
+#define __FUNCTION__	__FILE__
+#endif
+
+/*
+ * These macros neatly pass the required information without breaking the
+ * rest of the code.  The 'get_buffer()' code should be used for a temporary
+ * memory chunk such as the current CircleMUD 'buf,' 'buf1,' and 'buf2'
+ * variables.  Remember to use 'release_buffer()' to give up the requested
+ * buffer when finished with it.  'release_my_buffers()' may be used in
+ * functions with a lot of return statements but it is _not_ encouraged.
+ * 'get_memory()' and 'release_memory()' should only be used for memory that
+ * you always want handled here regardless of BUFFER_MEMORY.
+ */
+#define get_buffer(a)		acquire_buffer((a), BT_STACK, NULL, __FUNCTION__, __LINE__)
+#define get_memory(a)		acquire_buffer((a), BT_MALLOC, NULL, __FUNCTION__, __LINE__)
+#define release_buffer(a)	do { detach_buffer((a), BT_STACK, __FUNCTION__, __LINE__); (a) = NULL; } while(0)
+#define release_memory(a)	do { detach_buffer((a), BT_MALLOC, __FUNCTION__, __LINE__); (a) = NULL; } while(0)
+#define release_my_buffers()	detach_my_buffers(__FUNCTION__, __LINE__)
+
+/*
+ * Types for the memory to allocate.  It should never be necessary to use
+ * these definitions directly.
+ */
+#define BT_STACK	0	/* Stack type memory.			*/
+#define BT_PERSIST	1	/* A buffer that doesn't time out.	*/
+#define BT_MALLOC	2	/* A malloc() memory tracker.		*/
+
+/*
+ * Check for released and timed out buffers every 5 seconds.  This can be
+ * be arbitrarily changed.
+ */
+#define PULSE_BUFFER	(5 RL_SEC)
+
+/*
+ * Public functions for outside use.
+ */
+#if 0 /* BUFFER_SNPRINTF */
+buffer *str_cpy(buffer *d, buffer*s);
+int bprintf(buffer *buf, const char *format, ...);
+#endif
+#if BUFFER_MEMORY
+void *debug_calloc(size_t number, size_t size, const char *var, const char *func, int line);
+void *debug_realloc(void *ptr, size_t size, const char *var, const char *func, int line);
+void debug_free(void *ptr, const char *func, ush_int line);
+char *debug_str_dup(const char *txt, const char *var, const char *func, ush_int line);
+#endif
+void init_buffers(void);
+void exit_buffers(void);
+void release_all_buffers(void);
+struct buf_data *detach_buffer(char *data, byte type, const char *func, const int line_n);
+void detach_my_buffers(const char *func, const int line_n);
+char *acquire_buffer(size_t size, int type, const char *var, const char *who, ush_int line);
+void show_buffers(struct char_data *ch, int buffer_type, int display_type);
+extern int buffer_cache_stat[];
+#define BUFFER_CACHE_HITS	0
+#define BUFFER_CACHE_MISSES	1
+
+#endif
Binary files ../stk/buffer.o and ./buffer.o differ
diff -uprN ../stk/buffer.txt ./buffer.txt
--- ../stk/buffer.txt	Wed Dec 31 19:00:00 1969
+++ ./buffer.txt	Sat May 30 22:11:43 1998
@@ -0,0 +1,84 @@
+				OVERVIEW
+
+Since the buffer system is really different from the current CircleMUD
+system, this file will help get you started with how to use it.  One of
+the many advantages of the enhanced buffer allocation system is overflow
+checking.  Every time a buffer is released, it is checked to ensure that
+it did not copy data past the region it had allocated.  Such unchecked
+overflows will eventually crash seconds, minutes, or even hours later. 
+This erratic behavior makes debugging very hard.  It can also let you know
+how much of the allocated buffer the function actually uses.  Also allows
+tracking of function allocation, memory leak tracking, and statistics.
+
+char *get_buffer(int size)
+
+	You pass it a number, it will pass you a pointer to a buffer of
+the correct size or larger, but don't count on larger.
+
+void release_buffer(char *buffer)
+
+	When you are done with the buffer obtained by get_buffer(), pass
+it as the argument to release_buffer() so it is free for other uses. 
+
+void release_my_buffers()
+
+	Will release all buffers currently associated with the function
+that calls this or all buffers in the file on compilers other than GCC. 
+Because of clearing all buffers in the file, this function is _not_
+recommended to be used unless absolutely necessary.  A separate call to
+release_buffer() for every buffer is preferred. 
+
+				EXAMPLE
+
+ACMD(do_hi)
+{
+  char *buf = get_buffer(128);
+
+  sprintf(buf, "Hello, %s.\r\n", GET_NAME(ch));
+  send_to_char(buf);
+
+  release_buffer(buf);
+}
+
+If you forget to call release_buffer() on a buffer, it will time out in 10
+seconds(*) and a notification message will be printed in the logs.  Any
+buffers that are not marked 'persistant' will be free()'d if not used for
+5 minutes(*). 
+
+(*) -  Configurable.
+
+			CONFIGURABLE OPTIONS
+
+	There are _many_ configurable options in buffer.c so take a look. 
+If you wish to still have the CircleMUD global buffers, take a look at
+buffer.h for the correct #define statement.  Another important variable,
+PULSE_BUFFER defines how often the buffer list is checked for forgotten
+buffers. 
+
+			THE 'buffer' COMMAND
+
+Syntax: buf[fer] ( a[dd] | d[elete] ) size ( t[emporary] | p[ersistant] )
+
+So: buffer add 8192 persistant - creates an 8k buffer that won't time out.
+    buf a 8192 p	- does the same as above.
+
+ADD:
+
+The add function can be used to create new buffers of the specified size
+in the buffer chain.  Temporary buffers will be destroyed if they are not
+used in 5 minutes but persistant buffers will never do away unless
+explicitly killed by the delete function. 
+
+DELETE:
+
+The delete function will destroy any unused buffer in the chain that
+matches the size and type (temporary or persistant) specified on the
+command line.  This is useful to free up memory if too many buffers are
+around, although it should not happen.
+
+			THE 'overflow' COMMAND
+
+It causes a buffer overflow to test the detection code.  You probably do
+not want to use it too often. 
+
+-George Greer 5/30/98
diff -uprN ../stk/bufhash.c ./bufhash.c
--- ../stk/bufhash.c	Wed Dec 31 19:00:00 1969
+++ ./bufhash.c	Mon May 25 15:16:31 1998
@@ -0,0 +1,28 @@
+#include <stdio.h>
+
+int pow2(int n)
+{
+  return (--n, n == -1 ? 1 : pow2(n) * 2);
+}
+
+int hash(int i, int w)
+{
+  return (i % w);
+}
+
+/*
+ * To use:
+ *   gcc -o hash bufhash.c
+ *   ./hash <hash_size> <MIN_ALLOC> <MAX_ALLOC>
+ *
+ * i.e.: hash 11 6 16
+ */
+int main(int argc, char **argv)
+{
+  int i, hv = atoi(argv[1]), min = atoi(argv[2]), max = atoi(argv[3]);
+  for (i = min; i <= max; i++) {
+    printf("%2d: %5d -> %2d\n", i, pow2(i), hash(pow2(i), hv));
+  }
+  return 0;
+}
+
diff -uprN ../stk/castle.c ./castle.c
--- ../stk/castle.c	Sat Apr 25 22:20:42 1998
+++ ./castle.c	Sat May  9 12:13:58 1998
@@ -12,8 +12,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
@@ -29,6 +29,7 @@ extern struct descriptor_data *descripto
 extern struct index_data *mob_index;
 extern struct index_data *obj_index;
 extern struct time_info_data time_info;
+extern int mini_mud;
 
 /* IMPORTANT!
    The below defined number is the zone number of the Kings Castle.
@@ -44,7 +45,14 @@ extern struct time_info_data time_info;
 |* Coded by Sapowox (d90-jkr@nada.kth.se)                             *|
 \**********************************************************************/
 
-#define C_MOB_SPEC(zone,mob) (mob_index[real_mobile(((zone)*100)+(mob))].func)
+#define C_MOB_SPEC(zone,mob)					\
+	if ((check = real_mobile(((zone)*100)+(mob))) < 0) {	\
+	  if (!mini_mud) {					\
+	    sprintf(buf, "assign_kings_castle(): can't find mob #%d.", ((zone)*100)+(mob));	\
+	    log(buf);						\
+	  }							\
+	} else							\
+	  mob_index[check].func
 
 #define R_MOB(zone, mob) (real_mobile(((zone)*100)+(mob)))
 #define R_OBJ(zone, obj) (real_object(((zone)*100)+(obj)))
@@ -70,6 +78,8 @@ SPECIAL(jerry);
 
 void assign_kings_castle(void)
 {
+  int check;
+
   C_MOB_SPEC(Z_KINGS_C, 0) = CastleGuard;   /* Gwydion */
   /* Added the previous line -- Furry */
   C_MOB_SPEC(Z_KINGS_C, 1) = king_welmar;   /* Our dear friend, the King */
Binary files ../stk/castle.o and ./castle.o differ
diff -uprN ../stk/class.c ./class.c
--- ../stk/class.c	Sat Apr 25 22:20:42 1998
+++ ./class.c	Wed May  6 20:09:38 1998
@@ -21,6 +21,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "db.h"
 #include "utils.h"
 #include "spells.h"
Binary files ../stk/class.o and ./class.o differ
diff -uprN ../stk/comm.c ./comm.c
--- ../stk/comm.c	Sat Apr 25 22:20:42 1998
+++ ./comm.c	Fri May 29 16:41:37 1998
@@ -43,6 +43,7 @@
 /* Note, includes for UNIX are in sysdep.h */
 
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
@@ -69,6 +70,7 @@ extern int DFLT_PORT;
 extern char *DFLT_DIR;
 extern int MAX_PLAYERS;
 extern int MAX_DESCRIPTORS_AVAILABLE;
+extern int buffer_opt;
 
 extern struct room_data *world;	/* In db.c */
 extern int top_of_world;	/* In db.c */
@@ -77,7 +79,6 @@ extern char help[];
 
 /* local globals */
 struct descriptor_data *descriptor_list = NULL;		/* master desc list */
-struct txt_block *bufpool = 0;	/* pool of large output buffers */
 int buf_largecount = 0;		/* # of large buffers which exist */
 int buf_overflows = 0;		/* # of overflows of output */
 int buf_switches = 0;		/* # of switches from small to large buf */
@@ -166,11 +167,23 @@ int main(int argc, char **argv)
   int pos = 1;
   char *dir;
 
+  init_buffers();
+
   port = DFLT_PORT;
   dir = DFLT_DIR;
 
   while ((pos < argc) && (*(argv[pos]) == '-')) {
     switch (*(argv[pos] + 1)) {
+    case 'v':
+      if (*(argv[pos] + 2))
+	buffer_opt = atoi(argv[pos] + 2);
+      else if (++pos < argc)
+	buffer_opt = atoi(argv[pos]);
+      else {
+	log("SYSERR: Number expected after option -v.");
+	exit(1);
+      }
+      break;
     case 'd':
       if (*(argv[pos] + 2))
 	dir = argv[pos] + 2;
@@ -212,7 +225,7 @@ int main(int argc, char **argv)
 
   if (pos < argc) {
     if (!isdigit(*argv[pos])) {
-      fprintf(stderr, "Usage: %s [-c] [-m] [-q] [-r] [-s] [-d pathname] [port #]\n", argv[0]);
+      fprintf(stderr, "Usage: %s [-c] [-m] [-q] [-r] [-s] [-v val] [-d pathname] [port #]\n", argv[0]);
       exit(1);
     } else if ((port = atoi(argv[pos])) <= 1024) {
       fprintf(stderr, "SYSERR: Illegal port number.\n");
@@ -237,6 +250,8 @@ int main(int argc, char **argv)
     init_game(port);
   }
 
+  exit_buffers();
+
   return 0;
 }
 
@@ -689,6 +704,10 @@ void heartbeat(int pulse)
 {
   static int mins_since_crashsave = 0;
 
+  /* Clear out all the global buffers now in case someone forgot. */
+  if (!(pulse % PULSE_BUFFER))
+    release_all_buffers();
+
   if (!(pulse % PULSE_ZONE))
     zone_update();
 
@@ -926,8 +945,8 @@ void flush_queues(struct descriptor_data
   int dummy;
 
   if (d->large_outbuf) {
-    d->large_outbuf->next = bufpool;
-    bufpool = d->large_outbuf;
+    release_buffer(d->large_outbuf);
+    d->output = d->small_outbuf;	/* necessary? */
   }
   while (get_from_q(&d->input, buf2, &dummy));
 }
@@ -961,22 +980,14 @@ void write_to_output(const char *txt, st
   }
   buf_switches++;
 
-  /* if the pool has a buffer in it, grab it */
-  if (bufpool != NULL) {
-    t->large_outbuf = bufpool;
-    bufpool = bufpool->next;
-  } else {			/* else create a new one */
-    CREATE(t->large_outbuf, struct txt_block, 1);
-    CREATE(t->large_outbuf->text, char, LARGE_BUFSIZE);
-    buf_largecount++;
-  }
-
-  strcpy(t->large_outbuf->text, t->output);	/* copy to big buffer */
-  t->output = t->large_outbuf->text;	/* make big buffer primary */
-  strcat(t->output, txt);	/* now add new text */
-
-  /* set the pointer for the next write */
-  t->bufptr = strlen(t->output);
+  /*
+   * Just request the buffer. Copy the contents of the old, and make it
+   * the primary buffer.
+   */
+  t->large_outbuf = get_buffer(LARGE_BUFSIZE);
+  strcpy(t->large_outbuf, t->output);
+  t->output = t->large_outbuf;
+  strcat(t->output, txt);
 
   /* calculate how much space is left in the buffer */
   t->bufspace = LARGE_BUFSIZE - 1 - t->bufptr;
@@ -1159,9 +1170,7 @@ int process_output(struct descriptor_dat
    * and switch back to the small one
    */
   if (t->large_outbuf) {
-    t->large_outbuf->next = bufpool;
-    bufpool = t->large_outbuf;
-    t->large_outbuf = NULL;
+    release_buffer(t->large_outbuf);
     t->output = t->small_outbuf;
   }
   /* reset total bufspace back to that of a small buffer */
Binary files ../stk/comm.o and ./comm.o differ
Binary files ../stk/config.o and ./config.o differ
Binary files ../stk/constants.o and ./constants.o differ
diff -uprN ../stk/db.c ./db.c
--- ../stk/db.c	Sat Apr 25 22:20:43 1998
+++ ./db.c	Wed May  6 20:09:38 1998
@@ -13,8 +13,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "db.h"
 #include "comm.h"
Binary files ../stk/db.o and ./db.o differ
diff -uprN ../stk/fight.c ./fight.c
--- ../stk/fight.c	Sat Apr 25 22:20:43 1998
+++ ./fight.c	Wed May  6 20:09:38 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "handler.h"
Binary files ../stk/fight.o and ./fight.o differ
diff -uprN ../stk/graph.c ./graph.c
--- ../stk/graph.c	Sat Apr 25 22:20:43 1998
+++ ./graph.c	Wed May  6 20:09:38 1998
@@ -19,8 +19,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/graph.o and ./graph.o differ
diff -uprN ../stk/handler.c ./handler.c
--- ../stk/handler.c	Sat Apr 25 22:20:43 1998
+++ ./handler.c	Wed May  6 20:09:38 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "db.h"
Binary files ../stk/handler.o and ./handler.o differ
Binary files ../stk/hash and ./hash differ
diff -uprN ../stk/house.c ./house.c
--- ../stk/house.c	Sat Apr 25 22:20:43 1998
+++ ./house.c	Wed May  6 20:09:38 1998
@@ -11,9 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
-
 #include "structs.h"
+#include "buffer.h"
 #include "comm.h"
 #include "handler.h"
 #include "db.h"
Binary files ../stk/house.o and ./house.o differ
diff -uprN ../stk/interpreter.c ./interpreter.c
--- ../stk/interpreter.c	Sat Apr 25 22:20:43 1998
+++ ./interpreter.c	Wed May  6 23:40:45 1998
@@ -14,6 +14,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "comm.h"
 #include "interpreter.h"
 #include "db.h"
@@ -60,6 +61,7 @@ ACMD(do_at);
 ACMD(do_backstab);
 ACMD(do_ban);
 ACMD(do_bash);
+ACMD(do_buffer);
 ACMD(do_cast);
 ACMD(do_color);
 ACMD(do_commands);
@@ -115,6 +117,7 @@ ACMD(do_not_here);
 ACMD(do_offer);
 ACMD(do_olc);
 ACMD(do_order);
+ACMD(do_overflow);
 ACMD(do_page);
 ACMD(do_poofset);
 ACMD(do_pour);
@@ -228,6 +231,7 @@ const struct command_info cmd_info[] = {
   { "burp"     , POS_RESTING , do_action   , 0, 0 },
   { "buy"      , POS_STANDING, do_not_here , 0, 0 },
   { "bug"      , POS_DEAD    , do_gen_write, 0, SCMD_BUG },
+  { "buffer"   , POS_DEAD    , do_buffer   , LVL_IMPL, 0 },
 
   { "cast"     , POS_SITTING , do_cast     , 1, 0 },
   { "cackle"   , POS_RESTING , do_action   , 0, 0 },
@@ -372,7 +376,7 @@ const struct command_info cmd_info[] = {
   { "order"    , POS_RESTING , do_order    , 1, 0 },
   { "offer"    , POS_STANDING, do_not_here , 1, 0 },
   { "open"     , POS_SITTING , do_gen_door , 0, SCMD_OPEN },
-
+  { "overflow" , POS_DEAD    , do_overflow , LVL_IMPL, 0 },
   { "put"      , POS_RESTING , do_put      , 0, 0 },
   { "pat"      , POS_RESTING , do_action   , 0, 0 },
   { "page"     , POS_DEAD    , do_page     , LVL_GOD, 0 },
Binary files ../stk/interpreter.o and ./interpreter.o differ
diff -uprN ../stk/limits.c ./limits.c
--- ../stk/limits.c	Sat Apr 25 22:20:43 1998
+++ ./limits.c	Wed May  6 20:09:38 1998
@@ -12,6 +12,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "spells.h"
 #include "comm.h"
Binary files ../stk/limits.o and ./limits.o differ
diff -uprN ../stk/magic.c ./magic.c
--- ../stk/magic.c	Sat Apr 25 22:20:43 1998
+++ ./magic.c	Wed May  6 20:09:38 1998
@@ -13,6 +13,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "spells.h"
Binary files ../stk/magic.o and ./magic.o differ
diff -uprN ../stk/mail.c ./mail.c
--- ../stk/mail.c	Sat Apr 25 22:20:43 1998
+++ ./mail.c	Wed May 27 17:50:15 1998
@@ -17,8 +17,8 @@ Written by Jeremy Elson (jelson@cs.jhu.e
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "db.h"
@@ -475,7 +475,11 @@ void postmaster_send_mail(struct char_da
   SET_BIT(PLR_FLAGS(ch), PLR_MAILING | PLR_WRITING);
 
   ch->desc->mail_to = recipient;
+#if 1
+  CREATE(ch->desc->str, char *, 1);
+#else
   ch->desc->str = (char **) malloc(sizeof(char *));
+#endif
   *(ch->desc->str) = NULL;
   ch->desc->max_str = MAX_MAIL_SIZE;
 }
Binary files ../stk/mail.o and ./mail.o differ
diff -uprN ../stk/mobact.c ./mobact.c
--- ../stk/mobact.c	Sat Apr 25 22:20:43 1998
+++ ./mobact.c	Wed May  6 20:09:38 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "db.h"
 #include "comm.h"
Binary files ../stk/mobact.o and ./mobact.o differ
diff -uprN ../stk/modify.c ./modify.c
--- ../stk/modify.c	Sat Apr 25 22:20:43 1998
+++ ./modify.c	Mon May 25 16:03:11 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "interpreter.h"
 #include "handler.h"
@@ -303,7 +303,8 @@ void page_string(struct descriptor_data 
     send_to_char("", d->character);
     return;
   }
-  CREATE(d->showstr_vector, char *, d->showstr_count = count_pages(str));
+  d->showstr_count = count_pages(str);
+  CREATE(d->showstr_vector, char *, d->showstr_count);
 
   if (keep_internal) {
     d->showstr_head = str_dup(str);
Binary files ../stk/modify.o and ./modify.o differ
diff -uprN ../stk/objsave.c ./objsave.c
--- ../stk/objsave.c	Sat Apr 25 22:20:43 1998
+++ ./objsave.c	Wed May  6 20:09:38 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "comm.h"
 #include "handler.h"
 #include "db.h"
Binary files ../stk/objsave.o and ./objsave.o differ
diff -uprN ../stk/olc.c ./olc.c
--- ../stk/olc.c	Sat Apr 25 22:20:43 1998
+++ ./olc.c	Wed May  6 20:09:38 1998
@@ -18,6 +18,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/olc.o and ./olc.o differ
Binary files ../stk/random.o and ./random.o differ
diff -uprN ../stk/shop.c ./shop.c
--- ../stk/shop.c	Sat Apr 25 22:20:43 1998
+++ ./shop.c	Wed May 20 01:55:11 1998
@@ -16,6 +16,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "comm.h"
 #include "handler.h"
 #include "db.h"
@@ -1084,6 +1085,8 @@ void assign_the_shopkeepers(void)
   cmd_slap = find_command("slap");
   cmd_puke = find_command("puke");
   for (index = 0; index < top_shop; index++) {
+    if (SHOP_KEEPER(index) == NOBODY)
+      continue;
     if (mob_index[SHOP_KEEPER(index)].func)
       SHOP_FUNC(index) = mob_index[SHOP_KEEPER(index)].func;
     mob_index[SHOP_KEEPER(index)].func = shop_keeper;
Binary files ../stk/shop.o and ./shop.o differ
diff -uprN ../stk/spec_assign.c ./spec_assign.c
--- ../stk/spec_assign.c	Sat Apr 25 22:20:43 1998
+++ ./spec_assign.c	Wed May  6 20:09:38 1998
@@ -12,6 +12,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "db.h"
 #include "interpreter.h"
 #include "utils.h"
Binary files ../stk/spec_assign.o and ./spec_assign.o differ
diff -uprN ../stk/spec_procs.c ./spec_procs.c
--- ../stk/spec_procs.c	Sat Apr 25 22:20:43 1998
+++ ./spec_procs.c	Wed May  6 20:09:38 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "interpreter.h"
Binary files ../stk/spec_procs.o and ./spec_procs.o differ
diff -uprN ../stk/spell_parser.c ./spell_parser.c
--- ../stk/spell_parser.c	Sat Apr 25 22:20:43 1998
+++ ./spell_parser.c	Wed May  6 20:09:38 1998
@@ -12,8 +12,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "interpreter.h"
 #include "spells.h"
Binary files ../stk/spell_parser.o and ./spell_parser.o differ
diff -uprN ../stk/spells.c ./spells.c
--- ../stk/spells.c	Sat Apr 25 22:20:43 1998
+++ ./spells.c	Wed May  6 20:09:38 1998
@@ -13,6 +13,7 @@
 #include "sysdep.h"
 
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "spells.h"
Binary files ../stk/spells.o and ./spells.o differ
diff -uprN ../stk/structs.h ./structs.h
--- ../stk/structs.h	Sat Apr 25 22:20:43 1998
+++ ./structs.h	Sat May 30 22:20:56 1998
@@ -461,6 +461,8 @@ typedef signed char		sbyte;
 typedef unsigned char		ubyte;
 typedef signed short int	sh_int;
 typedef unsigned short int	ush_int;
+typedef signed long		sh_long;
+typedef unsigned long		ush_long;
 typedef char			bool;
 
 #ifndef CIRCLE_WINDOWS
@@ -910,7 +912,7 @@ struct descriptor_data {
    char *output;		/* ptr to the current output buffer	*/
    int  bufptr;			/* ptr to end of current output		*/
    int	bufspace;		/* space left in the output buffer	*/
-   struct txt_block *large_outbuf; /* ptr to large buffer, if we need it */
+   char *large_outbuf;		/* ptr to large buffer, if we need it	*/
    struct txt_q input;		/* q of unprocessed input		*/
    struct char_data *character;	/* linked to char			*/
    struct char_data *original;	/* original char if switched		*/
diff -uprN ../stk/thread.c ./thread.c
--- ../stk/thread.c	Wed Dec 31 19:00:00 1969
+++ ./thread.c	Wed May  6 20:09:38 1998
@@ -0,0 +1,101 @@
+/*
+ * thread.c - Testing stub for buffer.c
+ * Copyright 1997, George Greer
+ */
+
+#include "conf.h"
+#include "sysdep.h"
+#include <pthread.h>
+#include "structs.h"
+#include "buffer.h"
+#include "utils.h"
+
+void _thr_main() {}
+
+long random();
+void send_to_all(char *t) { printf("%s", t); }
+void send_to_char(char *t, struct char_data *ch) { printf("%s", t); }
+int MIN(int a, int b) { return a < b ? a : b; }
+int MAX(int a, int b) { return a > b ? a : b; }
+int number(int from, int to) { return ((random() % (to - from + 1)) + from); }
+
+int circle_shutdown = 0;
+int circle_reboot = 0;
+
+void log(char *str)
+{
+  time_t ct;
+  char *tmstr;
+
+  ct = time(0);
+  tmstr = asctime(localtime(&ct));
+  *(tmstr + strlen(tmstr) - 1) = '\0';
+  fprintf(stdout, "%-15.15s :: %s\n", tmstr + 4, str);
+  fflush(stdout);
+}
+
+void *thread(void *hi)
+{
+  FILE *dn;
+  int i;
+  char *tbuf, backup[128];;
+
+  sleep(1);
+  for (i = 0; i < INT_MAX; i++) {
+    tbuf = get_buffer(number(21,3000));
+    strcpy(tbuf, "12345678901234567890");
+    sprintf(tbuf, "%d ", i);
+    strcpy(backup, tbuf);
+    dn = fopen("/dev/null", "w");
+    fprintf(dn, "%s", tbuf);
+    fclose(dn);
+    if (strcmp(backup, tbuf)) { /* different */
+      sprintf(backup, "thread on %d: Someone changed my buffer!", i);
+      log(backup);
+    }
+    release_buffer(tbuf);
+    if (circle_shutdown)
+      return NULL;
+  }
+  log("thread: done.");
+  return NULL;
+}
+
+#define NUM_THREADS	60
+
+int main(void)
+{
+  int i;
+  char *mbuf;
+  pthread_t thread_id[NUM_THREADS];
+  extern ush_int buffer_opt;
+
+  buffer_opt |= (1 << 2);
+//  buffer_opt |= (1 << 4);
+
+  log("main: init_buffers();");
+  init_buffers();
+  sleep(1);
+
+  log("main: starting threads.");
+  for (i = 0; i < NUM_THREADS; i++) {
+    printf("%d ", i);
+    if (pthread_create(&thread_id[i], NULL, thread, NULL) < 0)
+      log("thread: Failed thread creation.");
+  }
+  printf("\n");
+
+  sleep(5);
+
+  log("main: waiting...");
+  for (i = 0; i < NUM_THREADS; i++)
+    pthread_join(thread_id[i], NULL);
+  circle_shutdown = 1;
+  log("main: done.");
+
+  sleep(5);
+
+  log("main: exit_buffers()");
+  exit_buffers();
+  return 0;
+}
diff -uprN ../stk/utils.c ./utils.c
--- ../stk/utils.c	Sat Apr 25 22:20:43 1998
+++ ./utils.c	Mon May 25 15:33:09 1998
@@ -11,14 +11,16 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "screen.h"
 #include "spells.h"
 #include "handler.h"
 
+#include <stdarg.h>
+
 extern struct time_data time_info;
 extern struct room_data *world;
 
@@ -64,6 +66,7 @@ int MAX(int a, int b)
 }
 
 
+#if BUFFER_MEMORY == FALSE
 
 /* Create a duplicate of a string */
 char *str_dup(const char *source)
@@ -74,7 +77,7 @@ char *str_dup(const char *source)
   return (strcpy(new, source));
 }
 
-
+#endif
 
 /* str_cmp: a case-insensitive version of strcmp */
 /* returns: 0 if equal, 1 if arg1 > arg2, -1 if arg1 < arg2  */
@@ -124,15 +127,22 @@ void log_death_trap(struct char_data * c
 
 
 /* writes a string to the log */
-void log(char *str)
+void basic_mud_log(char *format, ...)
 {
-  time_t ct;
-  char *tmstr;
+  va_list args;
+  time_t ct = time(0);
+  char *time_s = asctime(localtime(&ct));
+
+  time_s[strlen(time_s) - 1] = '\0';
+
+  fprintf(stderr, "%-15.15s :: ", time_s + 4);
+
+  va_start(args, format);
+  vfprintf(stderr, format, args);
+  va_end(args);
 
-  ct = time(0);
-  tmstr = asctime(localtime(&ct));
-  *(tmstr + strlen(tmstr) - 1) = '\0';
-  fprintf(stderr, "%-19.19s :: %s\n", tmstr, str);
+  fprintf(stderr, "\n");
+  fflush(stderr);
 }
 
 
diff -uprN ../stk/utils.h ./utils.h
--- ../stk/utils.h	Sat Apr 25 22:20:43 1998
+++ ./utils.h	Tue Jun  9 22:06:28 1998
@@ -8,18 +8,17 @@
 *  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
 ************************************************************************ */
 
-
 /* external declarations and prototypes **********************************/
 
 extern struct weather_data weather_info;
 
-#define log(x) basic_mud_log(x)
+#define log basic_mud_log
 
 /* public functions in utils.c */
 char	*str_dup(const char *source);
 int	str_cmp(char *arg1, char *arg2);
 int	strn_cmp(char *arg1, char *arg2, int n);
-void	basic_mud_log(char *str);
+void	basic_mud_log(char *str, ...) __attribute__ ((format (printf, 1, 2)));
 int	touch(char *path);
 void	mudlog(char *str, char type, int level, byte file);
 void	log_death_trap(struct char_data *ch);
@@ -124,6 +123,25 @@ void	update_pos(struct char_data *victim
 
 /* memory utils **********************************************************/
 
+#if !defined(__STRING)
+#define __STRING(x)	#x
+#endif
+
+#if BUFFER_MEMORY
+
+#define CREATE(result, type, number)  do {\
+	if (!((result) = (type *) debug_calloc ((number), sizeof(type), __STRING(result), __FUNCTION__, __LINE__)))\
+		{ perror("malloc failure"); abort(); } } while(0)
+
+#define RECREATE(result,type,number) do {\
+	if (!((result) = (type *) debug_realloc ((result), sizeof(type) * (number), __STRING(result), __FUNCTION__, __LINE__)))\
+		{ perror("realloc failure"); abort(); } } while(0)
+
+#define free(variable)	debug_free((variable), __FUNCTION__, __LINE__)
+
+#define str_dup(variable)	debug_str_dup((variable), __STRING(variable), __FUNCTION__, __LINE__)
+
+#else
 
 #define CREATE(result, type, number)  do {\
 	if (!((result) = (type *) calloc ((number), sizeof(type))))\
@@ -132,6 +150,8 @@ void	update_pos(struct char_data *victim
 #define RECREATE(result,type,number) do {\
   if (!((result) = (type *) realloc ((result), sizeof(type) * (number))))\
 		{ perror("realloc failure"); abort(); } } while(0)
+
+#endif
 
 /*
  * the source previously used the same code in many places to remove an item
Binary files ../stk/utils.o and ./utils.o differ
diff -uprN ../stk/weather.c ./weather.c
--- ../stk/weather.c	Sat Apr 25 22:20:43 1998
+++ ./weather.c	Wed May  6 20:09:38 1998
@@ -11,8 +11,8 @@
 #include "conf.h"
 #include "sysdep.h"
 
-
 #include "structs.h"
+#include "buffer.h"
 #include "utils.h"
 #include "comm.h"
 #include "handler.h"
Binary files ../stk/weather.o and ./weather.o differ
