diff -uprN ../buf17/Makefile ./Makefile
--- ../buf17/Makefile	Tue May 19 18:53:10 1998
+++ ./Makefile	Tue Jun  9 22:33:20 1998
@@ -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
diff -uprN ../buf17/Makefile.in ./Makefile.in
--- ../buf17/Makefile.in	Tue May 19 18:52:51 1998
+++ ./Makefile.in	Tue Jun  9 23:03:17 1998
@@ -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
Binary files ../buf17/act.comm.o and ./act.comm.o differ
Binary files ../buf17/act.informative.o and ./act.informative.o differ
Binary files ../buf17/act.item.o and ./act.item.o differ
Binary files ../buf17/act.movement.o and ./act.movement.o differ
Binary files ../buf17/act.offensive.o and ./act.offensive.o differ
Binary files ../buf17/act.other.o and ./act.other.o differ
Binary files ../buf17/act.social.o and ./act.social.o differ
diff -uprN ../buf17/act.wizard.c ./act.wizard.c
--- ../buf17/act.wizard.c	Tue May 19 18:52:51 1998
+++ ./act.wizard.c	Fri May 22 21:41:02 1998
@@ -2005,7 +2005,8 @@ ACMD(do_show)
     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_hits, buffer_cache_misses);
+	    buffer_cache_stat[BUFFER_CACHE_HITS],
+	    buffer_cache_stat[BUFFER_CACHE_MISSES]);
     send_to_char(buf, ch);
     break;
   case 5:
@@ -2039,7 +2040,9 @@ ACMD(do_show)
     hcontrol_list_houses(ch);
     break;
   case 10:
-    show_buffers(ch);
+    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 ../buf17/act.wizard.o and ./act.wizard.o differ
Binary files ../buf17/ban.o and ./ban.o differ
diff -uprN ../buf17/boards.c ./boards.c
--- ../buf17/boards.c	Tue May 19 18:52:51 1998
+++ ./boards.c	Wed May 27 17:49:43 1998
@@ -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 ../buf17/boards.o and ./boards.o differ
diff -uprN ../buf17/buffer.c ./buffer.c
--- ../buf17/buffer.c	Tue May 19 18:52:51 1998
+++ ./buffer.c	Tue Jun  9 22:57:36 1998
@@ -1,12 +1,11 @@
 /************************************************************************
- * buffer.c - Advanced Buffer System				v1.7	*
+ * buffer.c - Advanced Buffer System				v1.8	*
  *									*
- * Designed for CircleMUD 3.0			November 26, 1997	*
+ * Designed for CircleMUD 3.0				May 30, 1998	*
  ************************************************************************/
 
-#define _BUFFER_C_
+#define __BUFFER_C__
 
-#include <stdarg.h>
 #include "conf.h"
 #include "sysdep.h"
 
@@ -15,65 +14,96 @@
 #include "utils.h"
 #include "interpreter.h"
 
-#if defined(THREADED)
-#include <pthread.h>
-#endif
-
 /* ------------------------------------------------------------------------ */
 
 /*
+ * ... BUF_NUKE ...
  * Set	: Use a memset() to clear each buffer after use.
- * Unset: Do a *buffer = '\0'					(recommended)
- */
-#define BUF_NUKE	(1 << 0)
-/*
+ * Unset: Do a *buffer = '\0'
+ * ... BUF_CHECK ...
  * Set	: Report how much of each buffer the functions used.
- * Unset: Don't report.						(recommended)
+ * 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)
 /*
- * Set	: Reboot immediately on overflow...			(recommended)
- * Unset: Reboot after MAX_OVERFLOW corruptions if we don't crash anyway.
+ * Example: ush_int buffer_opt = BUF_OVERBOOT | BUF_VERBOSE | BUF_CHECK;
  */
-#define BUF_OVERBOOT	(1 << 2)
+ush_int buffer_opt = BUF_OVERBOOT;
+
 /*
- * Set	: Log everything.
- * Unset: Log only error messages and useful things.		(recommended)
+ * 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 BUF_VERBOSE	(1 << 3)
-#define BUF_DETAIL	(1 << 4)
+#define BUFFER_EXTREME_PARANOIA	0
+
 /*
- * Example: ush_int buffer_top = BUF_OVERBOOT | BUF_VERBOSE | BUF_CHECK;
+ * 1 = Enable multithreaded support.  You _must_ have POSIX threads for this!
+ * 0 = Use the standard heartbeat() method of freeing the buffers.
  */
-ush_int buffer_opt = BUF_OVERBOOT;
+#define BUFFER_THREADED	0
 
 /*
- * MAGIC_NUMBER - Any arbitrary hex value unlikely to show up in a string.
- *	In this case, we use a NAK character. (0x06)
- * MAX_OVERFLOW - How many times can we overflow a buffer before a reboot?
- * MAX_ALLOC - The largest buffer allowed without griping.
- * MIN_ALLOC - The lowest buffer ever handed.  If 20 bytes are requested,
- *	we will allocate a 64 byte buffer anyway (with the defaults).
+ * 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.  Only useful when we get multithreaded.
+ *	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 */
-#define MAGIC_NUMBER	(0x06)
-#define MAX_OVERFLOW	(3)
+
+/*
+ * 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 CACHE_SIZE	(3)
+#define BUFFER_HASH	(11)	/* Not arbitrary! */
+#define CACHE_SIZE	(3 + BUFFER_MEMORY * 7)	/* Cache is 10 in that case. */
 
 /*
- * Don't change these.
+ * 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.
- * ALLOC_RANGE - The maximum [] value in the buffer array.
+ * BUFFER_MINSIZE - The smallest buffer actually allocated, in bytes.
  */
-#define ALLOC_RANGE	(MAX_ALLOC - MIN_ALLOC)
-#if defined(THREADED)
+#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
@@ -81,143 +111,267 @@ ush_int buffer_opt = BUF_OVERBOOT;
 #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
 
-struct wait_q {
-  int line;
-  int type;
-  const char *func;
-  struct wait_q *next;
+/*
+ * 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 allocation linked list.
- *  buffer_cache - pointer to last allocated structures, a cache for freeing.
- *  buffer_overflow - how many times we've corrupted the buffer list.
- *  buffer_cache_misses - How many times a buffer wasn't in the cache.
- *  buffer_cache_hits - How many times the buffer was in cache.
+ *  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.
- *  buflockq - The next function to get the buffer lock.
- *  buflocktail - Pointer to attach wait queues to.
+ *  buflistmutex - Make sure the threads don't stomp on each other.
  */
-struct buf_data **buffers;
-struct buf_data **buffer_cache;
-int buffer_cache_misses = 0;
-int buffer_cache_hits = 0;
-byte buffer_overflow = 0;
-#if defined(THREADED)
-pthread_t buf_thread = 0;
-struct wait_q *buflockq = NULL;
-struct wait_q *buflocktail = NULL;
-#endif
+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. */
-void send_to_char(char *, struct char_data *);
-void send_to_all(char *);
-void *buffer_thread(void *);
+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. */
-struct buf_data *new_buffer(size_t size, int type);
-struct buf_data *malloc_buffer(size_t size, int type);
-struct buf_data **get_buffer_head(void);
-struct buf_data *in_cache(const char *data);
-void set_buffer_head(struct buf_data **new_head);
-void free_buffer(struct buf_data *f);
-void clear_buffer(struct buf_data *clearme);
-void remove_buffer(struct buf_data *removeme);
-int remove_from_cache(struct buf_data *removeme);
-int add_to_cache(struct buf_data *addme);
-int get_used(struct buf_data *buf);
-int get_magnitude(ush_int number);
-#if defined(THREADED)
-void bufferlist_lock(int type, const char *func, ush_int line);
-void bufferlist_unlock(const char *func, ush_int line);
-void buffer_lock(struct buf_data *buf, int type, const char *func, ush_int line);
-void buffer_unlock(struct buf_data *buf, const char *func, ush_int line);
-#endif
-#if defined(BUFFER_MEMORY)
-void really_free(void *ptr);
-void *debug_calloc(size_t number, size_t size, char *func, int line);
-void *debug_realloc(void *ptr, size_t size, const char *func, int line);
-void debug_free(void *ptr);
+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
 
-            static struct buf_data *lastgive = NULL;
-
-#if defined(THREADED)
+#if BUFFER_THREADED
 /*
  * Wrapper macros.
  */
-#define lock_buffers(type)	(bufferlist_lock((type), __FUNCTION__, __LINE__))
+#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)		((buf)->locked = LOCK_NONE)
+#define UNLOCK(buf)		(buffer_unlock((buf), __FUNCTION__, __LINE__))
 #define LOCKED(buf)		((buf)->locked)
 #else
-#define lock_buffers(type)
-#define unlock_buffers()
-#define LOCK(buf, type)
-#define UNLOCK(buf)
+#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: get_buffer_head()
+ * 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.
  */
-inline struct buf_data **get_buffer_head()
+static int count_bits(size_t y)
 {
-  return buffers;
+  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: set_buffer_head(new buffer head)
+ * Private: round_to_pow2(number to round)
+ * Converts a number to the next highest power of two.
+ * 0000011000111010 (1594)
+ *        to
+ * 0000100000000000 (2048)
  */
-inline void set_buffer_head(struct buf_data **new_head)
+static void round_to_pow2(size_t *y)
 {
-  buffers = new_head;
+  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);
+  }
 }
 
-#if 0
 /*
- * Public: pow2(number)
- * Simply function to avoid math library.
+ * Private: buffer_reboot(none)
+ * Determine whether to reboot or continue after corruption.
  */
-int pow2(int y)
+static void buffer_reboot(void)
 {
-  int number = 2;
-  while(y-- > 1)
-    number *= 2;
-  return number;
+  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
 
 /*
- * Public: get_magnitude(number)
+ * 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.
  */
-int get_magnitude(ush_int y)
+static int get_magnitude(ush_long y)
 {
-  int number, x = y;
-
-  for (number = -1; y > 0; number++, y /= 2);
-  number = MIN(MAX_ALLOC, MAX(number, MIN_ALLOC));
-  number -= MIN_ALLOC;
+  ush_int number = buffer_hash(y);
 
-  if (number > ALLOC_RANGE)
-    log("get_magnitude: Oops.");
-
-  if (buffer_opt & BUF_VERBOSE) {
-    char gmbuf[128];
-    sprintf(gmbuf, "BUF: get_magnitude: Given:%d Returned:%d.", x, number);
-    log(gmbuf);
+  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;
 }
 
@@ -225,58 +379,57 @@ int get_magnitude(ush_int y)
  * Private: add_to_cache(structure to add)
  * Does what it says.
  */
-int add_to_cache(struct buf_data *addme)
+static void add_to_cache(struct buf_data *addme)
 {
-  int i;
-
   /*
-   * Move everybody down.
+   * Move everybody right.
    */
-  for (i = (CACHE_SIZE - 1); i > 0; i--)
-    buffer_cache[i] = buffer_cache[i - 1];
+  memmove(buffer_cache + 1, buffer_cache, sizeof(struct buf_data *) * (CACHE_SIZE - 1));
+
   /*
    * Add the structure to the front.
    */
   buffer_cache[0] = addme;
-  return TRUE;
 }
 
 /*
  * Private: remove_from_cache(structure to remove)
  * Does what it says.
  */
-int remove_from_cache(struct buf_data *removeme)
+static int remove_from_cache(struct buf_data *removeme)
 {
-  int i;
+  int i, ret = FALSE;
 
   for (i = 0; i < CACHE_SIZE; i++)
     if (buffer_cache[i] == removeme) {
       buffer_cache[i] = NULL;
-      return TRUE;
+      ret = TRUE;
     }
-  return FALSE;
+  return ret;
 }
 
 /*
  * Private: in_cache(data to search for)
  * Used internally since the cache expanded complexity.
  */
-struct buf_data *in_cache(const char *data)
+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_hits++;
+      buffer_cache_stat[BUFFER_CACHE_HITS]++;
       if (buffer_opt & BUF_VERBOSE)
-	log("BUF: in_cache: Cache hit.");
+	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.");
+	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_misses++;
+  buffer_cache_stat[BUFFER_CACHE_MISSES]++;
   return NULL;
 }
 
@@ -286,47 +439,76 @@ struct buf_data *in_cache(const char *da
  */
 void exit_buffers(void)
 {
-  int magnitude;
-  struct buf_data **head, *clear, *next_clear;
+  struct buf_data **head;
+  ush_int magnitude;
 
   log("BUF: Shutting down.");
 
-#if defined(THREADED)
+#if BUFFER_THREADED
   pthread_join(buf_thread, NULL);
 #endif
 
-  show_buffers(NULL);
-
-  lock_buffers(LOCK_CLOSED);
-
+  log("BUF: Clearing buffer memory.");
   head = get_buffer_head();
-  for (magnitude = 0; magnitude <= ALLOC_RANGE; magnitude++) {
-    for (clear = head[magnitude]; clear; clear = next_clear) {
-      next_clear = clear->next;
-      LOCK(clear, LOCK_WILL_FREE);
-      free_buffer(clear);
+  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.");
+}
 
-  if (buffer_cache)
-    really_free(buffer_cache);
-  if (head)
-    really_free(head);
+/*
+ * 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()
+ * Public: init_buffers(none)
  * This is called from main() to get everything started.
  */
 void init_buffers(void)
 {
-#if defined(THREADED)
+#if BUFFER_THREADED
   int error = 0;
 #endif
 
-  lock_buffers(LOCK_CLOSED);
+  lock_buffers();
 
-#if defined(THREADED)
+  find_hash();
+
+#if BUFFER_THREADED
   /*
    * If a buffer thread already exists, kill it.
    */
@@ -347,15 +529,11 @@ void init_buffers(void)
    * 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(ALLOC_RANGE + 1, sizeof(struct buf_data *)));
-  buffer_cache = calloc(CACHE_SIZE, sizeof(struct buf_data *));
+  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 *)));
 
-  if (buffer_opt & BUF_VERBOSE) {
-    char ibbuf[128];
-    sprintf(ibbuf, "BUF: min_alloc(%d) max_alloc(%d) alloc_range(%d)\n",
-	MIN_ALLOC, MAX_ALLOC, ALLOC_RANGE);
-    log(ibbuf);
-  }
+  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.
@@ -365,22 +543,21 @@ void init_buffers(void)
   unlock_buffers();
 }
 
-#if defined(THREADED)
 /*
- * Private: decrement_all_buffers()
- * Reduce the life on all buffers by 1.  This will eventually replace
- * the functionality of release_all_buffers().
+ * Private: decrement_all_buffers(none)
+ * Reduce the life on all buffers by 1.
  */
-void decrement_all_buffers(void)
+static void decrement_all_buffers(void)
 {
   int magnitude;
   struct buf_data *clear, **head = get_buffer_head();
 
-  for (magnitude = 0; magnitude <= ALLOC_RANGE; magnitude++)
+  for (magnitude = 0; magnitude <= buffer_array[BUF_MAX]; magnitude++)
     for (clear = head[magnitude]; clear; clear = clear->next) {
-      if (clear->life <= 0)
-	continue;
-      clear->life--;
+      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--;
     }
 }
 
@@ -388,94 +565,84 @@ void decrement_all_buffers(void)
  * Private: release_old_buffers()
  * Check for any used buffers that have no remaining life.
  */
-void release_old_buffers(void)
+static void release_old_buffers(void)
 {
   int magnitude;
   struct buf_data *relbuf, **head = get_buffer_head();
 
-  for (magnitude = 0; magnitude <= ALLOC_RANGE; magnitude++)
-    for (relbuf = head[magnitude]; relbuf; relbuf = relbuf->next)
-      if (relbuf->type != BT_MALLOC && relbuf->life == 0 && relbuf->used && !LOCKED(relbuf)) {
-        char buf[128];
-	LOCK(relbuf, LOCK_WILL_CLEAR);
-        sprintf(buf, "BUF: %s:%d forgot to release %d bytes in %p.",
-		relbuf->who ? relbuf->who : "UNKNOWN", relbuf->line, relbuf->size, relbuf);
-        log(buf);
-        clear_buffer(relbuf);
-	UNLOCK(relbuf);
+  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.
  */
-void free_old_buffers(void)
+static void free_old_buffers(void)
 {
   int magnitude;
   struct buf_data *freebuf, *next_free, **head = get_buffer_head();
 
-  lock_buffers(LOCK_CLOSED);
+  lock_buffers();
 
-  for (magnitude = 0; magnitude <= ALLOC_RANGE; magnitude++)
+  for (magnitude = 0; magnitude <= buffer_array[BUF_MAX]; magnitude++)
     for (freebuf = head[magnitude]; freebuf; freebuf = next_free) {
       next_free = freebuf->next;
-      if (!freebuf->used && freebuf->life == 0 && freebuf->type == BT_STACK && !LOCKED(freebuf)) {
-        LOCK(freebuf, LOCK_WILL_REMOVE);
-	remove_buffer(freebuf);
-	LOCK(freebuf, LOCK_WILL_FREE);
-        free_buffer(freebuf);
-      }
+      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();
 }
 
-#else
-
 /*
  * Public: release_all_buffers()
  * Forcibly release all buffers currently allocated.  This is useful to
- * reclaim any forgotten buffers.  This will need a little bit of work
- * to be thread safe.
+ * reclaim any forgotten buffers.
  * See structs.h for PULSE_BUFFER to change the release rate.
  */
 void release_all_buffers(void)
 {
-  struct buf_data *clear, *next_clear, **head = get_buffer_head();
-  int magnitude;
-
-  lock_buffers(LOCK_CLOSED);
-
-  for (magnitude = 0; magnitude <= ALLOC_RANGE; magnitude++) {
-    for (clear = head[magnitude]; clear; clear = next_clear) {
-      next_clear = clear->next;
-      if (clear->type != BT_MALLOC && clear->used && clear->life == 0 && !LOCKED(clear)) {
-        char buf[128];
-	LOCK(clear, LOCK_WILL_CLEAR);
-        sprintf(buf, "BUF: %s:%d forgot to release %d bytes.",
-		clear->who, clear->line, clear->size);
-        log(buf);
-        clear_buffer(clear);
-	UNLOCK(clear);
-      } else if (!clear->used && clear->life == 0 && clear->type == BT_STACK && !LOCKED(clear)) {
-	LOCK(clear, LOCK_WILL_REMOVE);
-	remove_buffer(clear);
-	LOCK(clear, LOCK_WILL_FREE);
-        free_buffer(clear);
-      } else
-        clear->life--;
-    }
-  }
-  unlock_buffers();
-}
-
+#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.
  */
-void remove_buffer(struct buf_data *removeme)
+static void remove_buffer(struct buf_data *removeme)
 {
   struct buf_data **head, *traverse, *prev = NULL;
   int magnitude;
@@ -485,64 +652,68 @@ void remove_buffer(struct buf_data *remo
     return;
   }
 
+#if BUFFER_THREADED
   if (LOCKED(removeme) != LOCK_WILL_REMOVE)
-    log("BUF: SYSERR: remove_buffer: Lock bit not properly set!");
+    log("BUF: SYSERR: remove_buffer: Lock bit not properly set on %p!", removeme);
+#endif
 
-  head = get_buffer_head();
+  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) {
-      lock_buffers(LOCK_CLOSED);
       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.");
-      unlock_buffers();
+	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 during operation!");
-
+	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 passed.");
+  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.
  */
-void clear_buffer(struct buf_data *clearme)
+static void clear_buffer(struct buf_data *clearme)
 {
-  if (!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!");
+    log("BUF: SYSERR: clear_buffer: Lock not properly set on %p!", clearme);
+#endif
+
+  magic_check(clearme);
 
   /*
-   * If the magic number we set in acquire_buffer() is not there then
-   * we have a suspected buffer overflow.
+   * If the magic number we set is not there then we have a suspected
+   * buffer overflow.
    */
   if (clearme->data[clearme->req_size] != MAGIC_NUMBER) {
-    char buf[128];
-    sprintf(buf, "BUF: SYSERR: clear_buffer: Overflow in buffer %p from %s:%d (%d/%d).",
-		clearme, clearme->who ? clearme->who : "UNKNOWN", clearme->line, strlen(clearme->data) + 1, clearme->req_size);
-    log(buf);
-    init_buffers();
-    if (buffer_opt & BUF_OVERBOOT || ++buffer_overflow > MAX_OVERFLOW) {
-      send_to_all("Emergency reboot.. come back in a minute or two.\r\n");
-      log("BUF: SYSERR: Emergency reboot, too many buffer corruptions!");
-      circle_shutdown = circle_reboot = 1;
+    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
-      log("BUF: SYSERR: Buffer list cleared!");
+      buffer_reboot();
   } else if (clearme->type == BT_MALLOC) {
-    lock_buffers(LOCK_CLOSED);
+    lock_buffers();
     LOCK(clearme, LOCK_WILL_REMOVE);
     remove_buffer(clearme);
     LOCK(clearme, LOCK_WILL_FREE);
@@ -550,16 +721,14 @@ void clear_buffer(struct buf_data *clear
     unlock_buffers();
   } else {
     if (buffer_opt & BUF_CHECK) {
-      char buf[128];
       /*
        * If nothing in clearme->data return 0, else if paranoid_buffer, return
        * the result of get_used(), otherwise just strlen() the buffer.
        */
-      sprintf(buf, "BUF: %s:%d used %d/%d bytes.",
+      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);
-      log(buf);
+		strlen(clearme->data)) : 0), clearme->req_size, clearme);
     }
     if (buffer_opt & BUF_NUKE)
       memset(clearme->data, '\0', clearme->size);
@@ -567,117 +736,115 @@ void clear_buffer(struct buf_data *clear
       *clearme->data = '\0';
     clearme->who = NULL;
     clearme->line = 0;
-#if defined(PICKY_BUFFERS)
-    clearme->req_size = 0;
-#endif
+    clearme->req_size = clearme->size;	/* For exit_buffers() check. */
     clearme->life = B_FREELIFE;
-    clearme->used = FALSE;
+#if BUFFER_THREADED
+    if (LOCKED(clearme) != LOCK_WILL_CLEAR)
+      log("BUF: SYSERR: clear_buffer: Someone cleared lock bit on %p.", clearme);
+#endif
   }
+}
 
-  if (LOCKED(clearme) != LOCK_WILL_CLEAR)
-    log("BUF: SYSERR: clear_buffer: Ack! Someone cleared lock bit.");
+/*
+ * 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.
  */
-int detach_buffer(buffer *data, const char *func, const int line_n)
+struct buf_data *detach_buffer(char *data, byte type, const char *func, const int line_n)
 {
-  struct buf_data *clear, **b_head;
-  int magnitude;
+  struct buf_data *clear;
+  int scanned;
 
   if (data == NULL || func == NULL) {
-    log("BUF: SYSERR: detach_buffer: Invalid information passed.");
+    log("BUF: SYSERR: detach_buffer: Invalid information passed from %s:%d.", func, line_n);
     return FALSE;
   }
 
-/*
- * This search is useless when we're given the structure already.
- */
-#if !defined(BUFFER_SNPRINTF)
-  /*
-   * 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(data))) {
-    if (buffer_opt & BUF_VERBOSE)
-      fprintf(stderr, "(detach_buffer");
-    b_head = get_buffer_head();
-    for (magnitude = 0; magnitude <= ALLOC_RANGE; magnitude++) {
-      if (buffer_opt & BUF_VERBOSE)
-        fprintf(stderr, ") %d(", magnitude);
-      for (clear = b_head[magnitude]; clear; clear = clear->next) {
-	if (buffer_opt & BUF_VERBOSE)
-	  fprintf(stderr, "%p ", clear->data);
-        if (clear->data == data)
-          break;
-      }
-      if (clear) /* Found one. */
-	break;
-    }
-    if (buffer_opt & BUF_VERBOSE)
-      fprintf(stderr, ")\n");
-  }
-#else
-  clear = data;
-#endif
+  clear = find_this_buffer(data, type, &scanned);
 
   if (clear == NULL) {
-    char buf[128];
-    sprintf(buf, "BUF: SYSERR: detach_buffer: No such buffer found at %s:%d.", func, line_n);
-    log(buf);
+    log("BUF: SYSERR: detach_buffer: No buffer->data %p found for %s:%d.", data, func, line_n);
+#if BUFFER_THREADED
   } else if (LOCKED(clear)) {
-    char dbbuf[128];
     if (LOCKED(clear) == LOCK_WILL_FREE)
-      sprintf(dbbuf, "BUF: detach_buffer: Buffer %p slated to be freed.", clear);
+      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)
-      sprintf(dbbuf, "BUF: detach_buffer: Buffer %p already being detached.", 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)
-      sprintf(dbbuf, "BUF: detach_buffer: Buffer %p being removed from list.", clear);
+      log("BUF: detach_buffer: Buffer %p, requested by %s:%d, already being removed from list.", clear, func, line_n);
     else
-      sprintf(dbbuf, "BUF: detach_buffer: Buffer %p already locked.", clear);
-    log(dbbuf);
-  } else if (clear->who == NULL) {
-    char dbbuf[128];
-    sprintf(dbbuf, "BUF: SYSERR: detach_buffer: Buffer %p already released. Used: %d Locked: %d", clear, clear->used, LOCKED(clear));
-    log(dbbuf);
-  } 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)) {
-      char buf[128];
-      sprintf(buf, "BUF: %s:%d released %d bytes in %p from %s:%d.",
-		func, line_n, clear->size, clear, clear->who ? clear->who : "UNKNOWN", clear->line);
-      log(buf);
-    }
-    if (lastgive == clear) lastgive = NULL;
+    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 TRUE;
+    return clear;
   }
-  return FALSE;
+  return NULL;
 }
   
 /*
  * Private: free_buffer(buffer)
  * Internal function for getting rid of buffers.
  */
-void free_buffer(struct buf_data *f)
+static void free_buffer(struct buf_data *f)
 {
-  char buf[128];
-
-  if (!f) {
+  if (f == NULL) {
     log("BUF: SYSERR: free_buffer: NULL pointer.");
     return;
   } else if (f->type == BT_PERSIST)
-    sprintf(buf, "BUF: SYSERR: free_buffer: Freeing %d byte persistant buffer!", f->size);
-  else
-    sprintf(buf, "BUF: free_buffer: Freeing %d bytes in expired buffer.", f->size);
-  if (f->type != BT_MALLOC)
-    log(buf);
+    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!");
+    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.
@@ -687,7 +854,7 @@ void free_buffer(struct buf_data *f)
   if (f->data)
     really_free(f->data);
   else
-    log("BUF: SYSERR: free_buffer: Hey, no data?");
+    log("BUF: SYSERR: free_buffer: Hey, no data in %p?", f);
   really_free(f);
 }
 
@@ -695,188 +862,181 @@ void free_buffer(struct buf_data *f)
  * Private: new_buffer(size of buffer, type flag)
  * Finds where it should place the new buffer it's trying to malloc.
  */
-struct buf_data *new_buffer(size_t size, int type)
+static struct buf_data *new_buffer(size_t size, int type)
 {
-  struct buf_data **buflist, *buft, *potential, *prev = NULL;
+  struct buf_data **buflist, *potential;
   int magnitude;
 
-  if (size <= 0) {
-    log("BUF: SYSERR: new_buffer: 0 byte (or less) buffer requested.");
+  if (size == 0) {
+    log("BUF: SYSERR: new_buffer: 0 byte buffer requested.");
     return NULL;
   }
-  buflist = get_buffer_head();
-  magnitude = get_magnitude(size);
-
-  /* There aren't any buffers. */
-  if (buflist[magnitude] == NULL) {
-    if (buffer_opt & BUF_VERBOSE)
-      log("BUF: new_buffer: Created the list.");
-    return (buflist[magnitude] = malloc_buffer(size, type));
-  }
 
-  /* Insert a buffer */
-  for (buft = buflist[magnitude]; buft; buft = buft->next) {
-    if (size <= buft->size) {		/* Found where to insert. */
-      potential = malloc_buffer(size, type);
-      potential->next = buft;
-      lock_buffers(LOCK_CLOSED);
-      if (buflist[magnitude] == buft)
-        buflist[magnitude] = potential;
-      else if (prev)
-        prev->next = potential;
-      else {
-	char nbbuf[128];
-	sprintf(nbbuf, "BUF: SYSERR: new_buffer: Oops, didn't insert buffer. (prev:%p, list:%p, pos:%p)",
-		prev, buflist[magnitude], buft);
-	log(nbbuf);
-	free_buffer(potential);
-	return NULL;
-      }
-      unlock_buffers();
-      if (buffer_opt & BUF_VERBOSE)
-	log("BUF: new_buffer: Inserted.");
-      return potential;
-    }
-    prev = buft;
-  }
+  /*
+   * 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);
 
-  /* Append. */
-  if (prev->next)
-    log("BUF: SYSERR: new_buffer: Overwrote a buffer.");
-  if (buffer_opt & BUF_VERBOSE)
-    log("BUF: new_buffer: Appended to list.");
+  /*
+   * We separate the malloc and buffer lists for efficiency.
+   */
+  buflist = (type == BT_MALLOC ? get_memory_head() : get_buffer_head());
+  magnitude = get_magnitude(size);
 
-  prev->next = malloc_buffer(size, type);
-  return prev->next;  
+  /*
+   * 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.
  */
-struct buf_data *malloc_buffer(size_t size, int type)
+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 malloc.");
+    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);
-  new_buf->data = (char *)malloc(size + 1);
-  new_buf->used = FALSE;
+
+  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->life = B_FREELIFE;
   new_buf->type = type;
   new_buf->next = NULL;
   new_buf->size = size;
-#if defined(PICKY_BUFFERS)
-  new_buf->req_size = 0;
-#endif
+  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)) {
-    char buf[128];
-    sprintf(buf, "BUF: malloc_buffer: Allocated %d byte buffer, %d byte overhead.",
-		new_buf->size, sizeof(struct buf_data));
-    log(buf);
-  }
-  if (buffer_opt & BUF_NUKE)
+  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.
  */
-buffer *acquire_buffer(size_t size, int type, const char *who, ush_int line)
+char *acquire_buffer(size_t size, int type, const char *varname, const char *who, ush_int line)
 {
-  struct buf_data *give = NULL, **head = get_buffer_head();
-  int magnitude;
+  struct buf_data *give;
 
-  /*
-   * I'd like to avoid this lock but it causes the same buffer to be
-   * given out multiple times without it.
-   */
-  lock_buffers(LOCK_CLOSED);
+#if BUFFER_EXTREME_PARANOIA
+  buffer_sanity_check(NULL);
+#endif
 
-  /*
-   * Search the list for an unused buffer that is large enough.
-   */
-retry:
-  for (magnitude = get_magnitude(size); magnitude <= ALLOC_RANGE; magnitude++)
-    for (give = head[magnitude]; give; give = give->next)
-      if (!give->used && give->size >= size && give->type != BT_MALLOC && !LOCKED(give)) {
-	if (LOCKED(give) != LOCK_NONE)
-	  log("BUF: SYSERR: acquire_buffer: Dammit!");
-	LOCK(give, LOCK_ACQUIRE);
-	unlock_buffers();
-	goto grabbed_buffer;
-      }
+  if (size > BUFFER_MAXSIZE)
+    log("BUF: %s:%d requested %d bytes for '%s'.", who, line, size, varname);
 
-  unlock_buffers();
+  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;
 
-  /*
-   * Everything big enough is being used, create a new one.
-   */
-  if (give == NULL) {
-    if (buffer_opt & (BUF_VERBOSE | BUF_DETAIL)) {
-      char buf[128];
-      sprintf(buf, "BUF: Didn't find %d byte buffer! Making a new one.", size);
-      log(buf);
-    }
+    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(size, FALSE)) == NULL) {
-      char buf[128];
-      sprintf(buf, "BUF: SYSERR: acquire_buffer: Couldn't get %d byte buffer for %s:%d.", size, who, line);
-      log(buf);
+    if ((give = new_buffer(allocate_size, type)) == NULL)
       return NULL;
-    }
-    /* LOCK(give, LOCK_ACQUIRE); - We're already locked in malloc_buffer. */
   }
 
-grabbed_buffer:
+  magic_check(give);
 
-#if defined(THREADED)
+#if BUFFER_THREADED
   if (LOCKED(give) != LOCK_ACQUIRE) {
     log("BUF: SYSERR: acquire_buffer: Someone stole my buffer.");
-    goto retry;
+    abort();
   }
 #endif
 
-  if (give == lastgive) {
-    char abbuf[128];
-    sprintf(abbuf, "BUF: SYSERR: acquire_buffer: Giving out buffer again to %s:%d.", who, line);
-    log(abbuf);
-    exit(1);
-  }
-  lastgive = give;
-
-  give->used = TRUE;
   give->who = who;
   give->line = line;
-  if (type == BT_MALLOC)
-    give->type = BT_MALLOC;
-  give->life = B_GIVELIFE;
-#if defined(PICKY_BUFFERS)
   give->req_size = size;
-#endif
-  if (buffer_opt & (BUF_VERBOSE | BUF_DETAIL)) {
-    char buf[128];
-    sprintf(buf, "BUF: %s:%d requested %d bytes, received %d in %p.", who, line, size, give->size, give);
-    log(buf);
-  }
+
+  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
@@ -886,20 +1046,22 @@ grabbed_buffer:
    */
   give->data[give->req_size] = MAGIC_NUMBER;
   if (*give->data != '\0') {
-    log("BUF: SYSERR: acquire_buffer: Buffer is not NULL as it ought to be!");
+    log("BUF: SYSERR: acquire_buffer: Buffer %p is not empty as it ought to be!", give);
     *give->data = '\0';
   }
 
-  add_to_cache(give);	/* Cache this entry. */
+#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);
-  lastgive = give;
 
-#if defined(BUFFER_SNPRINTF)
-  return give;	/* Hope they don't screw with anything. */
-#else
+  add_to_cache(give);	/* Cache this entry. */
+
   return give->data;
-#endif
 }
 
 /*
@@ -910,18 +1072,45 @@ grabbed_buffer:
  * You can call this with a NULL parameter to have it logged at any
  * time though.
  */
-void show_buffers(struct char_data *ch)
+/*
+ * 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 = get_buffer_head();
-  char *buf = get_buffer(MAX_STRING_LENGTH * 4);
-  int i, magnitude;
+  struct buf_data *disp, **head = NULL;
+  char *buf;
+  long i, size;
   char *buf_type[] = { "Stack", "Persist", "Malloc" };
 
-  for (magnitude = 0; magnitude <= ALLOC_RANGE; magnitude++)
-    for (i = 0, disp = head[magnitude]; disp; disp = disp->next) {
-      sprintf(buf, "%1d #%2d %5d bytes, Life: %5d, Type: %7s, Allocated: %s/%d.\r\n",
-		magnitude, ++i, disp->size, disp->life, buf_type[(int)disp->type],
-		disp->used ? disp->who : "unused", disp->used ? disp->line : 0);
+  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
@@ -931,33 +1120,39 @@ void show_buffers(struct char_data *ch)
   release_buffer(buf);
 }
 
+/*
+ * Tests the overflow code.  Do not use. :)
+ */
 ACMD(do_overflow)
 {
-  buffer *buf = get_buffer(130);
-
-  strcpy(buf, "01234567890123489798494561613163164984981916513213216546947894"
-	"78949491613156194898191698132136484321321467897984132132156416879413"
-	"21321654897894651321321564789794446346892656843657843653846578436537");
+  /*
+   * 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);
 }
 
-#if !defined(BUFFER_TEST)
-
 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)
 {
-  buffer *arg1, *arg2, *arg3;
-  int size, persistant = FALSE;
+  char *arg1, *arg2, *arg3;
+  long size;
+  int persistant = FALSE;
 
   /* This looks nifty. */
   half_chop(argument, (arg1 = get_buffer(MAX_INPUT_LENGTH)), argument);
@@ -987,6 +1182,9 @@ ACMD(do_buffer)
     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);
@@ -1000,16 +1198,23 @@ ACMD(do_buffer)
       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 (!toy->used && toy->size == size && persistant == toy->type && !LOCKED(toy)) {
-	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);
+    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
@@ -1017,7 +1222,6 @@ ACMD(do_buffer)
 
   release_buffer(arg1);
 }
-#endif
 
 /*
  * Private: get_used(buffer to search)
@@ -1027,128 +1231,86 @@ ACMD(do_buffer)
  * it later for a smaller string, this will always return the
  * most used value.
  */
-int get_used(struct buf_data *buf)
+static int get_used(struct buf_data *buf)
 {
-  int cnt = buf->req_size - 1;
+  int cnt;
+  for (cnt = buf->req_size - 1; cnt > 0 && buf->data[cnt] == '\0'; cnt--);
+  return cnt;
+}
 
-  for (; 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);
 }
 
-#if defined(BUFFER_MEMORY)
-void *debug_calloc(size_t number, size_t size, char *func, int line)
+void *debug_calloc(size_t number, size_t size, const char *varname, const char *func, int line)
 {
-  return acquire_buffer(number * size, BT_MALLOC, func, 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)
+void debug_free(void *ptr, const char *func, ush_int line)
 {
-  release_buffer(ptr);
-  really_free(ptr);
+  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 *func, int line)
+void *debug_realloc(void *ptr, size_t size, const char *varname, const char *func, int line)
 {
-  void *xtra = acquire_buffer(size, BT_MALLOC, func, line);
-  detach_buffer(ptr, func, 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 defined(THREADED)
-/*
- * Sanity functions.
- */
-
-/*
- * Add a function to the waiting queue.
- */
-void add_to_wait_q(struct wait_q *std)
-{
-  struct wait_q *oldtail;
-
-  if (buflocktail == NULL) {
-    buflocktail = buflockq = std;
-    return;
-  }
-  oldtail = buflocktail;
-  buflocktail = std;
-  oldtail->next = std;
-}
-
-/*
- * Remove item from the lock waiting queue.
- */
-void remove_from_wait_q(const char *func, int line)
-{
-  struct wait_q *remove = buflockq;
+/* ------------------------------------------------------------------------ */
 
-  if (strcmp(func, buflockq->func) != 0)
-    log("BUF: SYSERR: remove_from_wait_q: Ack! Removing wrong function.");
+#if BUFFER_THREADED
 
-  buflockq = buflockq->next;
-  if (buflockq == NULL)
-    buflocktail = NULL;
-  really_free(remove);
-}
-
-void bufferlist_lock(int type, const char *func, ush_int line)
+static void bufferlist_lock(const char *func, ush_int line)
 {
-  struct wait_q *bufwait = (struct wait_q *)malloc(sizeof(struct wait_q));
-  bufwait->line = line;
-  bufwait->type = type;
-  bufwait->func = func;
-  bufwait->next = NULL;
-  add_to_wait_q(bufwait);
-
-  if (buflockq != bufwait /* && buflockq->type > bufwait->type */ ) {
-    if (buffer_opt & (BUF_VERBOSE | BUF_DETAIL)) {
-      char blbuf[128];
-      sprintf(blbuf, "BUF: %s:%d:%p waiting on lock by %s:%d:%p.", func, line, bufwait, buflockq->func, buflockq->line, buflockq);
-      log(blbuf);
-    }
-    while (buflockq != bufwait /* && buflockq->type > bufwait->type */ )
-      sleep(1);
-    if (buffer_opt & BUF_VERBOSE) {
-      char blbuf[128];
-      sprintf(blbuf, "BUF: %s:%d:%p grabbed buffer lock.", func, line, bufwait);
-      log(blbuf);
-    }
-  }
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: List locked by %s:%d.", func, line);
+  pthread_mutex_lock(&buflistmutex);
 }
 
-void bufferlist_unlock(const char *func, ush_int line)
+static void bufferlist_unlock(const char *func, ush_int line)
 {
-  remove_from_wait_q(func, line);
+  if (buffer_opt & BUF_VERBOSE)
+    log("BUF: List unlocked by %s:%d.", func, line);
+  pthread_mutex_unlock(&buflistmutex);
 }
 
-void buffer_lock(struct buf_data *buf, int type, const char *func, ush_int line)
+static void buffer_lock(struct buf_data *buf, int type, const char *func, ush_int line)
 {
-  if (buf == NULL) {
-    char blbuf[128];
-    sprintf(blbuf, "BUF: SYSERR: buffer_lock: buf == NULL from %s:%d", func, line);
-    log(blbuf);
-  } else if (LOCKED(buf) != LOCK_NONE && LOCKED(buf) != LOCK_WILL_REMOVE && type != LOCK_WILL_FREE) {
-    char blbuf[128];
-    sprintf(blbuf, "BUF: SYSERR: buffer_lock: Trying to lock a buffer already locked, from %d to %d at %s:%d.", LOCKED(buf), type, func, line);
-    log(blbuf);
-  } else
+  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;
 }
 
-void buffer_unlock(struct buf_data *buf, const char *func, ush_int line)
+static void buffer_unlock(struct buf_data *buf, const char *func, ush_int line)
 {
-  if (buf == NULL) {
-    char bubuf[128];
-    sprintf(bubuf, "BUF: SYSERR: buffer_unlock: buf == NULL from %s:%d.", func, line);
-    log(bubuf);
-  } else if (LOCKED(buf) == LOCK_NONE) {
-    char bubuf[128];
-    sprintf(bubuf, "BUF: SYSERR: buffer_unlock: Buffer %p isn't locked, from %s:%d.", buf, func, line);
-    log(bubuf);
-  } else
+  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;
 }
 
@@ -1162,6 +1324,11 @@ void buffer_unlock(struct buf_data *buf,
  */
 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) {
@@ -1174,9 +1341,10 @@ void *buffer_thread(void *nothing)
     /* Here we free() any buffer which has not been used in a while. */
     free_old_buffers();
 
-    /* that gettimeofday() stuff is too complicated. */
-    sleep((PULSE_BUFFER / PASSES_PER_SEC));
-
+    /* Sleep the same amount of time the main loop does. */
+    select(0, NULL, NULL, NULL, &tv);
+  
+    /* See if we should exit. */
     pthread_testcancel();
   }
 
@@ -1185,7 +1353,9 @@ void *buffer_thread(void *nothing)
 }
 #endif
 
-#if defined(BUFFER_SNPRINTF)
+/* ------------------------------------------------------------------------ */
+
+#if 0 /* BUFFER_SNPRINTF */
 /*
  * Buffer using sprintf() with bounds checking via snprintf()
  */
@@ -1195,21 +1365,40 @@ int bprintf(buffer *str, const char *for
   int chars;
 
   va_start(args, format);
-  chars = vsnprintf(str->data, str->size, args);
+  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 *b2bcpy(buffer *str1, buffer *str2) { strcpy(str1->data, str2->data); }
-char *s2bcpy(buffer *str1, const char *str2) { strcpy(str1->data, str2); }
-char *b2scpy(char *str1, const buffer *str2) { strcpy(str1, str2->data; )
-char *bufcat(buffer *str1, const char *str2) { strcat(str1->data, str2); }
+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
 
-#if defined(BUFFER_MEMORY)
+/* ------------------------------------------------------------------------ */
+
+/*
+ * At the bottom because we need to undefine the macro to get correct
+ * results.
+ */
+#if BUFFER_MEMORY
 #undef free
-void really_free(void *ptr) { free(ptr); }
+void really_free(void *ptr)
+{
+  if (!ptr)	/* Your OS may already do this, but it's insignificant. */
+    return;
+  free(ptr);
+}
 #endif
diff -uprN ../buf17/buffer.h ./buffer.h
--- ../buf17/buffer.h	Tue May 19 18:54:35 1998
+++ ./buffer.h	Sat May 30 20:30:35 1998
@@ -1,62 +1,38 @@
-#if !defined(_BUFFER_H_)
-#define _BUFFER_H_
-
-/* Do NOT define this. */
-/* #define BUFFER_TEST	1 */
-
-/*
- * #if 1 = use buffer system for memory allocations. (not done, don't use)
- * #if 0 = use standard calloc/realloc
- */
-#if 1
-#define BUFFER_MEMORY	1
-#endif
+#if !defined(__BUFFER_H__)
+#define __BUFFER_H__
 
 /*
- * #if 1 = use a threaded buffer system. (You must have pthreads.)
- * #if 0 = use the standard heartbeat() method.
+ * CONFIGURABLES (aka, The place to shoot your own foot.) :)
+ * ---------------------------------------------------------
  */
-#if 0
-#define THREADED	1
-#endif
 
 /*
- * #if 1 = return a pointer to the buffer structures
- * #if 0 = return only the data buffer (define this or all hell will break)
+ * 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.
  */
-#if 0
-#define BUFFER_SNPRINTF	1
-typedef struct buf_data buffer;
-#else
-typedef char buffer;
-#endif
+#define BUFFER_MEMORY	1
 
 /*
- * #if 1 = Include original CircleMUD buffers too.
- * #if 0 = Use only new buffer system.
+ * 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.
  */
-#if 1
 #define USE_CIRCLE_BUFFERS 1
-#endif
 
 /*
- * #if 1 = maintain a variable of how large a buffer the function requested.
- *      This will keep better track of buffer overruns when we give them a
- *      bigger buffer than requested.  An overrun with this on does not
- *      necessarily mean anything has been corrupted, but it does mean that
- *      it will happen and you have a serious problem. (recommended)
- * #if 0 = save sizeof(int) bytes per buffer structure and only check for
- *      overruns that corrupt memory past the buffer and do bad things.
- *
- * I'm thinking of removing this in favor of always being picky.
+ * -----------------------------------------------------
+ * No tweakables below! See 'buffer.c' for more options.
+ * -----------------------------------------------------
  */
-#if 1
-#define PICKY_BUFFERS   1
-#else
-#define req_size	size
-#endif
-
-/* *** No tweakables below *** */
 
 /*
  * Handle GCC-isms.
@@ -67,72 +43,57 @@ typedef char buffer;
 #endif
 
 /*
- * Some macros to imitate C++ class styles. release_buffer() automatically
- * NULL's a pointer to prevent further use.
- */
-#define get_buffer(a)		acquire_buffer((a), BT_STACK, __FUNCTION__, __LINE__)
-#define release_buffer(a)	do { detach_buffer((a), __FUNCTION__, __LINE__); (a) = NULL; } while(0)
+ * 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.
+ * 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() implementation		*/
-
-#define PULSE_BUFFER	(5 RL_SEC)
+#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.		*/
 
 /*
- * Assorted lock types.
+ * Check for released and timed out buffers every 5 seconds.  This can be
+ * be arbitrarily changed.
  */
-#define LOCK_NONE	0
-#define LOCK_OPEN	0
-#define LOCK_RO		1
-#define LOCK_CLOSED	2
-#define LOCK_ACQUIRE	9
-#define LOCK_WILL_CLEAR	10
-#define LOCK_WILL_FREE	11
-#define LOCK_WILL_REMOVE	12
+#define PULSE_BUFFER	(5 RL_SEC)
 
 /*
  * Public functions for outside use.
  */
-#if defined(BUFFER_SNPRINTF)
+#if 0 /* BUFFER_SNPRINTF */
 buffer *str_cpy(buffer *d, buffer*s);
 int bprintf(buffer *buf, const char *format, ...);
 #endif
-#if defined(BUFFER_MEMORY)
-void *debug_calloc(size_t number, size_t size, char *func, int line);
-void *debug_realloc(void *ptr, size_t size, const char *func, int line);
-void debug_free(void *ptr);
+#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);
-int detach_buffer(buffer *data, const char *func, const int line_n);
+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);
-buffer *acquire_buffer(size_t size, int type, const char *who, ush_int line);
-void show_buffers(struct char_data *ch);
-
-extern int buffer_cache_hits;
-extern int buffer_cache_misses;
-
-#if defined(BUFFER_SNPRINTF) || defined(_BUFFER_C_)
-struct buf_data {
-  bool locked;		/* Don't touch this buffer.		*/
-  bool used;            /* Is someone using this buffer?        */
-  byte type;		/* What type of buffer are we?		*/
-  size_t size;          /* How large is this buffer?            */
-  ush_int line;         /* What source code line is using this. */
-  sh_int life;          /* An idle counter to free unused ones. */
-#if defined(PICKY_BUFFERS)
-  sh_int req_size;      /* How much did the function request?   */
-#endif
-  char *data;           /* The buffer passed back to functions. */
-  const char *who;      /* Name of the function using this.     */
-  struct buf_data *next;        /* The next structure.          */
-};
-#endif
+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 ../buf17/buffer.o and ./buffer.o differ
diff -uprN ../buf17/buffer.txt ./buffer.txt
--- ../buf17/buffer.txt	Tue May 19 18:52:52 1998
+++ ./buffer.txt	Sat May 30 22:11:43 1998
@@ -1,20 +1,32 @@
 				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. 
+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.
+
+	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 'freed'.
+
+	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 in Windows.  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.
+
+	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
 
@@ -29,15 +41,17 @@ ACMD(do_hi)
 }
 
 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
+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.
+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.  The only other option is in structs.h,
+	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. 
 
@@ -60,11 +74,11 @@ 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.
+around, although it should not happen.
 
-			THE 'crash' COMMAND
+			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 8/20/97
+-George Greer 5/30/98
diff -uprN ../buf17/bufhash.c ./bufhash.c
--- ../buf17/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 ../buf17/castle.c ./castle.c
--- ../buf17/castle.c	Tue May 19 18:52:52 1998
+++ ./castle.c	Sat May  9 12:13:58 1998
@@ -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 ../buf17/castle.o and ./castle.o differ
Binary files ../buf17/class.o and ./class.o differ
diff -uprN ../buf17/comm.c ./comm.c
--- ../buf17/comm.c	Tue May 19 18:52:52 1998
+++ ./comm.c	Fri May 29 16:41:37 1998
@@ -70,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 */
@@ -173,6 +174,16 @@ int main(int argc, char **argv)
 
   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;
@@ -214,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");
@@ -693,11 +704,9 @@ void heartbeat(int pulse)
 {
   static int mins_since_crashsave = 0;
 
-#if !defined(THREADED)
   /* Clear out all the global buffers now in case someone forgot. */
   if (!(pulse % PULSE_BUFFER))
     release_all_buffers();
-#endif
 
   if (!(pulse % PULSE_ZONE))
     zone_update();
@@ -935,9 +944,10 @@ void flush_queues(struct descriptor_data
 {
   int dummy;
 
-  if (d->large_outbuf)
+  if (d->large_outbuf) {
     release_buffer(d->large_outbuf);
-
+    d->output = d->small_outbuf;	/* necessary? */
+  }
   while (get_from_q(&d->input, buf2, &dummy));
 }
 
Binary files ../buf17/comm.o and ./comm.o differ
Binary files ../buf17/config.o and ./config.o differ
Binary files ../buf17/constants.o and ./constants.o differ
Binary files ../buf17/db.o and ./db.o differ
Binary files ../buf17/fight.o and ./fight.o differ
Binary files ../buf17/graph.o and ./graph.o differ
Binary files ../buf17/handler.o and ./handler.o differ
Binary files ../buf17/hash and ./hash differ
Binary files ../buf17/house.o and ./house.o differ
diff -uprN ../buf17/interpreter.c ./interpreter.c
--- ../buf17/interpreter.c	Tue May 19 18:52:52 1998
+++ ./interpreter.c	Wed May  6 23:40:45 1998
@@ -117,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);
@@ -375,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 ../buf17/interpreter.o and ./interpreter.o differ
Binary files ../buf17/limits.o and ./limits.o differ
Binary files ../buf17/magic.o and ./magic.o differ
diff -uprN ../buf17/mail.c ./mail.c
--- ../buf17/mail.c	Tue May 19 18:52:52 1998
+++ ./mail.c	Wed May 27 17:50:15 1998
@@ -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 ../buf17/mail.o and ./mail.o differ
Binary files ../buf17/mobact.o and ./mobact.o differ
diff -uprN ../buf17/modify.c ./modify.c
--- ../buf17/modify.c	Tue May 19 18:52:52 1998
+++ ./modify.c	Mon May 25 16:03:11 1998
@@ -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 ../buf17/modify.o and ./modify.o differ
Binary files ../buf17/objsave.o and ./objsave.o differ
Binary files ../buf17/olc.o and ./olc.o differ
Binary files ../buf17/random.o and ./random.o differ
diff -uprN ../buf17/shop.c ./shop.c
--- ../buf17/shop.c	Tue May 19 18:52:52 1998
+++ ./shop.c	Wed May 20 01:55:11 1998
@@ -1085,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 ../buf17/shop.o and ./shop.o differ
Binary files ../buf17/spec_assign.o and ./spec_assign.o differ
Binary files ../buf17/spec_procs.o and ./spec_procs.o differ
Binary files ../buf17/spell_parser.o and ./spell_parser.o differ
Binary files ../buf17/spells.o and ./spells.o differ
diff -uprN ../buf17/structs.h ./structs.h
--- ../buf17/structs.h	Tue May 19 18:52:52 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
diff -uprN ../buf17/utils.c ./utils.c
--- ../buf17/utils.c	Tue May 19 18:52:52 1998
+++ ./utils.c	Mon May 25 15:33:09 1998
@@ -19,6 +19,8 @@
 #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 ../buf17/utils.h ./utils.h
--- ../buf17/utils.h	Tue May 19 18:52:52 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,17 +123,23 @@ void	update_pos(struct char_data *victim
 
 /* memory utils **********************************************************/
 
+#if !defined(__STRING)
+#define __STRING(x)	#x
+#endif
+
+#if BUFFER_MEMORY
 
-#if defined(BUFFER_MEMORY)
 #define CREATE(result, type, number)  do {\
-	if (!((result) = (type *) debug_calloc ((number), sizeof(type), __FUNCTION__, __LINE__)))\
+	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), __FUNCTION__, __LINE__)))\
+	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))
+#define free(variable)	debug_free((variable), __FUNCTION__, __LINE__)
+
+#define str_dup(variable)	debug_str_dup((variable), __STRING(variable), __FUNCTION__, __LINE__)
 
 #else
 
@@ -146,7 +151,6 @@ void	update_pos(struct char_data *victim
   if (!((result) = (type *) realloc ((result), sizeof(type) * (number))))\
 		{ perror("realloc failure"); abort(); } } while(0)
 
-#define really_free(variable) free((variable))
 #endif
 
 /*
Binary files ../buf17/utils.o and ./utils.o differ
Binary files ../buf17/weather.o and ./weather.o differ
