Blob Blame History Raw
diff -urp pads-1.2.orig/lib/bstring/bstraux.c pads-1.2/lib/bstring/bstraux.c
--- pads-1.2.orig/lib/bstring/bstraux.c	2008-07-08 14:28:29.000000000 -0400
+++ pads-1.2/lib/bstring/bstraux.c	2008-07-10 18:09:21.000000000 -0400
@@ -1,8 +1,8 @@
 /*
  * This source file is part of the bstring string library.  This code was
- * written by Paul Hsieh in 2002-2004, and is covered by the BSD open source 
- * license. Refer to the accompanying documentation for details on usage and 
- * license.
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source 
+ * license and the GPL. Refer to the accompanying documentation for details 
+ * on usage and license.
  */
 
 /*
@@ -17,28 +17,16 @@
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
+#include <ctype.h>
 #include "bstrlib.h"
 #include "bstraux.h"
 
-/*  int bTrunc (bstring b, int n)
- *
- *  Truncate the bstring to at most n characters.
- */
-int bTrunc (bstring b, int n) {
-	if (b == NULL || n < 0 || b->mlen < b->slen) return -__LINE__;
-	if (b->slen > n) {
-		b->slen = n;
-		b->data[n] = '\0';	/* Required for Clib interoperability */
-	}
-	return 0;
-}
-
 /*  bstring bTail (bstring b, int n)
  *
  *  Return with a string of the last n characters of b.
  */
 bstring bTail (bstring b, int n) {
-	if (b == NULL || n < 0 || b->mlen < b->slen) return NULL;
+	if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) return NULL;
 	if (n >= b->slen) return bstrcpy (b);
 	return bmidstr (b, b->slen - n, n);
 }
@@ -48,7 +36,7 @@ bstring bTail (bstring b, int n) {
  *  Return with a string of the first n characters of b.
  */
 bstring bHead (bstring b, int n) {
-	if (b == NULL || n < 0 || b->mlen < b->slen) return NULL;
+	if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) return NULL;
 	if (n >= b->slen) return bstrcpy (b);
 	return bmidstr (b, 0, n);
 }
@@ -57,10 +45,10 @@ bstring bHead (bstring b, int n) {
  *
  *  Fill a given bstring with the character in parameter c, for a length n.
  */
-int bFill (bstring a, char c, int len) {
-	if (a == NULL || len < 0 || a->mlen < a->slen) return -__LINE__;
-	a->slen = 0;
-	return bsetstr (a, len, NULL, c);
+int bFill (bstring b, char c, int len) {
+	if (b == NULL || len < 0 || (b->mlen < b->slen && b->mlen > 0)) return -__LINE__;
+	b->slen = 0;
+	return bsetstr (b, len, NULL, c);
 }
 
 /*  int bReplicate (bstring b, int n)
@@ -79,14 +67,16 @@ int bReverse (bstring b) {
 int i, n, m;
 unsigned char t;
 
-	if (b == NULL || b->slen < 2 || b->mlen < b->slen) return -__LINE__;
+	if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__;
 	n = b->slen;
-	m = ((unsigned)n) >> 1;
-	n--;
-	for (i=0; i < m; i++) {
-		t = b->data[n - i];
-		b->data[n - i] = b->data[i];
-		b->data[i] = t;
+	if (2 <= n) {
+		m = ((unsigned)n) >> 1;
+		n--;
+		for (i=0; i < m; i++) {
+			t = b->data[n - i];
+			b->data[n - i] = b->data[i];
+			b->data[i] = t;
+		}
 	}
 	return 0;
 }
@@ -106,7 +96,7 @@ int bInsertChrs (bstring b, int pos, int
 	if (pos < b->slen) memmove (b->data + pos + len, b->data + pos, b->slen - pos);
 	memset (b->data + pos, c, len);
 	b->slen += len;
-	b->data[b->slen] = '\0';
+	b->data[b->slen] = (unsigned char) '\0';
 	return BSTR_OK;
 }
 
@@ -128,7 +118,7 @@ unsigned char c = (unsigned char) space;
 	}
 	if (j > 0 && b->data[j-1] == c) j--;
 
-	b->data[j] = '\0';
+	b->data[j] = (unsigned char) '\0';
 	b->slen = j;
 	return BSTR_OK;
 }
@@ -170,6 +160,7 @@ int bJustifyMargin (bstring b, int width
 struct bstrList * sl;
 int i, l, c;
 
+	if (b == NULL || b->slen < 0 || b->mlen == 0 || b->mlen < b->slen) return -__LINE__;
 	if (NULL == (sl = bsplit (b, (unsigned char) space))) return -__LINE__;
 	for (l=c=i=0; i < sl->qty; i++) {
 		if (sl->entry[i]->slen > 0) {
@@ -201,28 +192,74 @@ int i, l, c;
 	return BSTR_OK;
 }
 
-/*  char * bStr2NetStr (const bstring b)
+static size_t readNothing (void *buff, size_t elsize, size_t nelem, void *parm) {
+	buff = buff;
+	elsize = elsize;
+	nelem = nelem;
+	parm = parm;
+	return 0; /* Immediately indicate EOF. */
+}
+
+/*  struct bStream * bsFromBstr (const_bstring b);
+ *
+ *  Create a bStream whose contents are a copy of the bstring passed in.
+ *  This allows the use of all the bStream APIs with bstrings.
+ */
+struct bStream * bsFromBstr (const_bstring b) {
+struct bStream * s = bsopen ((bNread) readNothing, NULL);
+	bsunread (s, b); /* Push the bstring data into the empty bStream. */
+	return s;
+}
+
+static size_t readRef (void *buff, size_t elsize, size_t nelem, void *parm) {
+struct tagbstring * t = (struct tagbstring *) parm;
+size_t tsz = elsize * nelem;
+
+	if (tsz > (size_t) t->slen) tsz = (size_t) t->slen;
+	if (tsz > 0) {
+		memcpy (buff, t->data, tsz);
+		t->slen -= (int) tsz;
+		t->data += tsz;
+		return tsz / elsize;
+	}
+	return 0;
+}
+
+/*  The "by reference" version of the above function.  This function puts
+ *  a number of restrictions on the call site (the passed in struct 
+ *  tagbstring *will* be modified by this function, and the source data
+ *  must remain alive and constant for the lifetime of the bStream).  
+ *  Hence it is not presented as an extern.
+ */
+static struct bStream * bsFromBstrRef (struct tagbstring * t) {
+	if (!t) return NULL;
+	return bsopen ((bNread) readRef, t);
+}
+
+/*  char * bStr2NetStr (const_bstring b)
  *
  *  Convert a bstring to a netstring.  See 
  *  http://cr.yp.to/proto/netstrings.txt for a description of netstrings.
- *  Note: 1) The value returned should be freed with a call to free() at the
- *           point when it will no longer be referenced to avoid a memory 
+ *  Note: 1) The value returned should be freed with a call to bcstrfree() at 
+ *           the point when it will no longer be referenced to avoid a memory 
  *           leak.
  *        2) If the returned value is non-NULL, then it also '\0' terminated
  *           in the character position one past the "," terminator.
  */
-char * bStr2NetStr (const bstring b) {
+char * bStr2NetStr (const_bstring b) {
+char strnum[sizeof (b->slen) * 3 + 1];
 bstring s;
 unsigned char * buff;
 
 	if (b == NULL || b->data == NULL || b->slen < 0) return NULL;
-	if (NULL == (s = bformat ("%d:", b->slen))
-	 || bconcat (s, b) == BSTR_ERR || bconchar (s, ',') == BSTR_ERR) {
+	sprintf (strnum, "%d:", b->slen);
+	if (NULL == (s = bfromcstr (strnum))
+	 || bconcat (s, b) == BSTR_ERR || bconchar (s, (char) ',') == BSTR_ERR) {
 		bdestroy (s);
 		return NULL;
 	}
 	buff = s->data;
-	free (s);
+	bcstrfree ((char *) s);
 	return (char *) buff;
 }
 
@@ -253,18 +290,18 @@ bstring b;
 		return NULL;
 	}
 	memcpy (b->data, buff + i + 1, x);
-	b->data[x] = '\0';
+	b->data[x] = (unsigned char) '\0';
 	b->slen = x;
 	return b;
 }
 
-static unsigned char b64ETable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static char b64ETable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-/*  bstring bBase64Encode (const bstring b)
+/*  bstring bBase64Encode (const_bstring b)
  *
- *  Generate a base64 encoding.
+ *  Generate a base64 encoding.  See: RFC1341
  */
-bstring bBase64Encode (const bstring b) {
+bstring bBase64Encode (const_bstring b) {
 int i, c0, c1, c2, c3;
 bstring out;
 
@@ -273,16 +310,16 @@ bstring out;
 	out = bfromcstr ("");
 	for (i=0; i + 2 < b->slen; i += 3) {
 		if (i && ((i % 57) == 0)) {
-			if (bconchar (out, '\015') < 0 || bconchar (out, '\012') < 0) {
+			if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) {
 				bdestroy (out);
 				return NULL;
 			}
 		}
 		c0 = b->data[i] >> 2;
 		c1 = ((b->data[i] << 4) |
-			  (b->data[i+1] >> 4)) & 0x3F;
+		      (b->data[i+1] >> 4)) & 0x3F;
 		c2 = ((b->data[i+1] << 2) |
-			  (b->data[i+2] >> 6)) & 0x3F;
+		      (b->data[i+2] >> 6)) & 0x3F;
 		c3 = b->data[i+2] & 0x3F;
 		if (bconchar (out, b64ETable[c0]) < 0 ||
 			bconchar (out, b64ETable[c1]) < 0 ||
@@ -294,7 +331,7 @@ bstring out;
 	}
 
 	if (i && ((i % 57) == 0)) {
-		if (bconchar (out, '\015') < 0 || bconchar (out, '\012') < 0) {
+		if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) {
 			bdestroy (out);
 			return NULL;
 		}
@@ -308,7 +345,7 @@ bstring out;
 			if (bconchar (out, b64ETable[c0]) < 0 ||
 				bconchar (out, b64ETable[c1]) < 0 ||
 				bconchar (out, b64ETable[c2]) < 0 ||
-				bconchar (out, '=') < 0) {
+				bconchar (out, (char) '=') < 0) {
 				bdestroy (out);
 				return NULL;
 			}
@@ -317,8 +354,8 @@ bstring out;
 				c1 = (b->data[i] << 4) & 0x3F;
 			if (bconchar (out, b64ETable[c0]) < 0 ||
 				bconchar (out, b64ETable[c1]) < 0 ||
-				bconchar (out, '=') < 0 ||
-				bconchar (out, '=') < 0) {
+				bconchar (out, (char) '=') < 0 ||
+				bconchar (out, (char) '=') < 0) {
 				bdestroy (out);
 				return NULL;
 			}
@@ -344,23 +381,28 @@ static int base64DecodeSymbol (unsigned 
    else                   return B64_ERR;
 }
 
-/*  bstring bBase64Decode (const bstring b)
+/*  bstring bBase64DecodeEx (const_bstring b, int * boolTruncError)
  *
  *  Decode a base64 block of data.  All MIME headers are assumed to have been
- *  removed.
+ *  removed.  See: RFC1341
  */
-bstring bBase64Decode (const bstring b) {
+bstring bBase64DecodeEx (const_bstring b, int * boolTruncError) {
 int i, v;
 unsigned char c0, c1, c2;
 bstring out;
 
 	if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+	if (boolTruncError) *boolTruncError = 0;
 	out = bfromcstr ("");
 	i = 0;
 	for (;;) {
 		do {
 			if (i >= b->slen) return out;
-			if (b->data[i] == '=') {
+			if (b->data[i] == '=') {	/* Bad "too early" truncation */
+				if (boolTruncError) {
+					*boolTruncError = 1;
+					return out;
+				}
 				bdestroy (out);
 				return NULL;
 			}
@@ -369,7 +411,11 @@ bstring out;
 		} while (v < 0);
 		c0 = (unsigned char) (v << 2);
 		do {
-			if (i >= b->slen || b->data[i] == '=') {
+			if (i >= b->slen || b->data[i] == '=') {	/* Bad "too early" truncation */
+				if (boolTruncError) {
+					*boolTruncError = 1;
+					return out;
+				}
 				bdestroy (out);
 				return NULL;
 			}
@@ -380,13 +426,21 @@ bstring out;
 		c1  = (unsigned char) (v << 4);
 		do {
 			if (i >= b->slen) {
+				if (boolTruncError) {
+					*boolTruncError = 1;
+					return out;
+				}
 				bdestroy (out);
 				return NULL;
 			}
 			if (b->data[i] == '=') {
 				i++;
 				if (i >= b->slen || b->data[i] != '=' || bconchar (out, c0) < 0) {
-					bdestroy (out);
+					if (boolTruncError) {
+						*boolTruncError = 1;
+						return out;
+					}
+					bdestroy (out); /* Missing "=" at the end. */
 					return NULL;
 				}
 				return out;
@@ -398,14 +452,23 @@ bstring out;
 		c2  = (unsigned char) (v << 6);
 		do {
 			if (i >= b->slen) {
+				if (boolTruncError) {
+					*boolTruncError = 1;
+					return out;
+				}
 				bdestroy (out);
 				return NULL;
 			}
 			if (b->data[i] == '=') {
 				if (bconchar (out, c0) < 0 || bconchar (out, c1) < 0) {
-					bconchar (out, c0);
+					if (boolTruncError) {
+						*boolTruncError = 1;
+						return out;
+					}
+					bdestroy (out);
 					return NULL;
 				}
+				if (boolTruncError) *boolTruncError = 0;
 				return out;
 			}
 			v = base64DecodeSymbol (b->data[i]);
@@ -415,7 +478,11 @@ bstring out;
 		if (bconchar (out, c0) < 0 ||
 			bconchar (out, c1) < 0 ||
 			bconchar (out, c2) < 0) {
-			bconchar (out, c0);
+			if (boolTruncError) {
+				*boolTruncError = -1;
+				return out;
+			}
+			bdestroy (out);
 			return NULL;
 		}
 	}
@@ -425,69 +492,230 @@ bstring out;
 
 struct bUuInOut {
 	bstring src, dst;
+	int * badlines;
 };
 
+#define UU_MAX_LINELEN 45
+
 static int bUuDecLine (void * parm, int ofs, int len) {
 struct bUuInOut * io = (struct bUuInOut *) parm;
 bstring s = io->src;
 bstring t = io->dst;
-int i, llen;
+int i, llen, otlen, ret, c0, c1, c2, c3, d0, d1, d2, d3;
 
 	if (len == 0) return 0;
 	llen = UU_DECODE_BYTE (s->data[ofs]);
+	ret = 0;
 
-	if (((unsigned) llen) > 45) return -__LINE__;
-	if (len > (i = (int) ((4/3.0)*llen + 1.5))) len = i;
+	otlen = t->slen;
 
-	for (i=1; i < len; i += 4) {
-		int c0, c1, c2, c3;
+	if (((unsigned) llen) > UU_MAX_LINELEN) { ret = -__LINE__; 
+		goto bl;
+	}
+
+	llen += t->slen;
 
-		c0 =                 UU_DECODE_BYTE (s->data[ofs + i + 0]);
-		c1 = (i + 1 < len) ? UU_DECODE_BYTE (s->data[ofs + i + 1]) : -1;
-		c2 = (i + 2 < len) ? UU_DECODE_BYTE (s->data[ofs + i + 2]) : -1;
-		c3 = (i + 3 < len) ? UU_DECODE_BYTE (s->data[ofs + i + 3]) : -1;
-
-		if (((unsigned) (c0|c1) >= 0x40) || c2 >= 0x40 || c3 >= 0x40) return -__LINE__;
-
-		if (bconchar (t, (char)((c0 << 2) | ((c1 >> 4) & 0x03))) < 0) return -__LINE__;
-		if ((unsigned) c2 < 0x40) {
-			if (bconchar (t, (char)((c1 << 4) | ((c2 >> 2) & 0x0F))) < 0) return -__LINE__;
-			if ((unsigned) c3 < 0x40) if (bconchar (t, (char)((c2 << 6) | (c3 & 0x3F))) < 0) return -__LINE__;
+	for (i=1; i < s->slen && t->slen < llen;i += 4) {
+		unsigned char outoctet[3];
+		c0 = UU_DECODE_BYTE (d0 = (int) bchare (s, i+ofs+0, ' ' - 1));
+		c1 = UU_DECODE_BYTE (d1 = (int) bchare (s, i+ofs+1, ' ' - 1));
+		c2 = UU_DECODE_BYTE (d2 = (int) bchare (s, i+ofs+2, ' ' - 1));
+		c3 = UU_DECODE_BYTE (d3 = (int) bchare (s, i+ofs+3, ' ' - 1));
+
+		if (((unsigned) (c0|c1) >= 0x40)) { if (!ret) ret = -__LINE__;
+			if (d0 > 0x60 || (d0 < (' ' - 1) && !isspace (d0)) ||
+			    d1 > 0x60 || (d1 < (' ' - 1) && !isspace (d1))) {
+				t->slen = otlen;
+				goto bl;
+			}
+			c0 = c1 = 0;
+		}
+		outoctet[0] = (unsigned char) ((c0 << 2) | ((unsigned) c1 >> 4));
+		if (t->slen+1 >= llen) {
+			if (0 > bconchar (t, (char) outoctet[0])) return -__LINE__;
+			break;
+		}
+		if ((unsigned) c2 >= 0x40) { if (!ret) ret = -__LINE__;
+			if (d2 > 0x60 || (d2 < (' ' - 1) && !isspace (d2))) {
+				t->slen = otlen;
+				goto bl;
+			}
+			c2 = 0;
+		}
+		outoctet[1] = (unsigned char) ((c1 << 4) | ((unsigned) c2 >> 2));
+		if (t->slen+2 >= llen) {
+			if (0 > bcatblk (t, outoctet, 2)) return -__LINE__;
+			break;
 		}
+		if ((unsigned) c3 >= 0x40) { if (!ret) ret = -__LINE__;
+			if (d3 > 0x60 || (d3 < (' ' - 1) && !isspace (d3))) {
+				t->slen = otlen;
+				goto bl;
+			}
+			c3 = 0;
+		}
+		outoctet[2] = (unsigned char) ((c2 << 6) | ((unsigned) c3));
+		if (0 > bcatblk (t, outoctet, 3)) return -__LINE__;
 	}
-	return 0;
+	if (t->slen < llen) { if (0 == ret) ret = -__LINE__;
+		t->slen = otlen;
+	}
+	bl:;
+	if (ret && io->badlines) {
+		(*io->badlines)++;
+		return 0;
+	}
+	return ret;
 }
 
-/*  bstring bUuDecode (const bstring src)
+/*  bstring bUuDecodeEx (const_bstring src, int * badlines)
  *
- *  Performs a UUDecode of a block of data.  It is assumed that the "begin"
- *  and "end" lines have already been stripped off.  The potential security
- *  problem of writing the filename in the begin line is something that is
- *  beyond the scope of a portable library.
+ *  Performs a UUDecode of a block of data.  If there are errors in the
+ *  decoding, they are counted up and returned in "badlines", if badlines is
+ *  not NULL. It is assumed that the "begin" and "end" lines have already 
+ *  been stripped off.  The potential security problem of writing the 
+ *  filename in the begin line is something that is beyond the scope of a 
+ *  portable library.
  */
+
 #ifdef _MSC_VER
 #pragma warning(disable:4204)
 #endif
-bstring bUuDecode (const bstring src) {
-struct tagbstring ws = bsStatic ("\r\n");
-struct bUuInOut io;
 
-	if (src == NULL || src->slen < 0 || src->data == NULL) return NULL;
-	io.src = src;
-	io.dst = bfromcstr ("");
-	if (bsplitscb (src, &ws, 0, bUuDecLine, &io) < 0) bstrFree (io.dst);
-	return io.dst;
+bstring bUuDecodeEx (const_bstring src, int * badlines) {
+struct tagbstring t;
+struct bStream * s;
+struct bStream * d;
+bstring b;
+
+	if (!src) return NULL;
+	t = *src; /* Short lifetime alias to header of src */
+	s = bsFromBstrRef (&t); /* t is undefined after this */
+	if (!s) return NULL;
+	d = bsUuDecode (s, badlines);
+	b = bfromcstralloc (256, "");
+	if (NULL == b || 0 > bsread (b, d, INT_MAX)) {
+		bdestroy (b);
+		bsclose (d);
+		bsclose (s);
+		return NULL;
+	}
+	return b;
+}
+
+struct bsUuCtx {
+	struct bUuInOut io;
+	struct bStream * sInp;
+};
+
+static size_t bsUuDecodePart (void *buff, size_t elsize, size_t nelem, void *parm) {
+static struct tagbstring eol = bsStatic ("\r\n");
+struct bsUuCtx * luuCtx = (struct bsUuCtx *) parm;
+size_t tsz;
+int l, lret;
+
+	if (NULL == buff || NULL == parm) return 0;
+	tsz = elsize * nelem;
+
+	CheckInternalBuffer:;
+	/* If internal buffer has sufficient data, just output it */
+	if (((size_t) luuCtx->io.dst->slen) > tsz) {
+		memcpy (buff, luuCtx->io.dst->data, tsz);
+		bdelete (luuCtx->io.dst, 0, (int) tsz);
+		return nelem;
+	}
+
+	DecodeMore:;
+	if (0 <= (l = binchr (luuCtx->io.src, 0, &eol))) {
+		int ol = 0;
+		struct tagbstring t;
+		bstring s = luuCtx->io.src;
+		luuCtx->io.src = &t;
+
+		do {
+			if (l > ol) {
+				bmid2tbstr (t, s, ol, l - ol);
+				lret = bUuDecLine (&luuCtx->io, 0, t.slen);
+				if (0 > lret) {
+					luuCtx->io.src = s;
+					goto Done;
+				}
+			}
+			ol = l + 1;
+			if (((size_t) luuCtx->io.dst->slen) > tsz) break;
+			l = binchr (s, ol, &eol);
+		} while (BSTR_ERR != l);
+		bdelete (s, 0, ol);
+		luuCtx->io.src = s;
+		goto CheckInternalBuffer;
+	}
+
+	if (BSTR_ERR != bsreada (luuCtx->io.src, luuCtx->sInp, bsbufflength (luuCtx->sInp, BSTR_BS_BUFF_LENGTH_GET))) {
+		goto DecodeMore;
+	}
+
+	bUuDecLine (&luuCtx->io, 0, luuCtx->io.src->slen);
+
+	Done:;
+	/* Output any lingering data that has been translated */
+	if (((size_t) luuCtx->io.dst->slen) > 0) {
+		if (((size_t) luuCtx->io.dst->slen) > tsz) goto CheckInternalBuffer;
+		memcpy (buff, luuCtx->io.dst->data, luuCtx->io.dst->slen);
+		tsz = luuCtx->io.dst->slen / elsize;
+		luuCtx->io.dst->slen = 0;
+		if (tsz > 0) return tsz;
+	}
+
+	/* Deallocate once EOF becomes triggered */
+	bdestroy (luuCtx->io.dst);
+	bdestroy (luuCtx->io.src);
+	free (luuCtx);
+	return 0;
+}
+
+/*  bStream * bsUuDecode (struct bStream * sInp, int * badlines)
+ *
+ *  Creates a bStream which performs the UUDecode of an an input stream.  If
+ *  there are errors in the decoding, they are counted up and returned in 
+ *  "badlines", if badlines is not NULL. It is assumed that the "begin" and 
+ *  "end" lines have already been stripped off.  The potential security 
+ *  problem of writing the filename in the begin line is something that is 
+ *  beyond the scope of a portable library.
+ */
+
+struct bStream * bsUuDecode (struct bStream * sInp, int * badlines) {
+struct bsUuCtx * luuCtx = (struct bsUuCtx *) malloc (sizeof (struct bsUuCtx));
+struct bStream * sOut;
+
+	if (NULL == luuCtx) return NULL;
+
+	luuCtx->io.src = bfromcstr ("");
+	luuCtx->io.dst = bfromcstr ("");
+	if (NULL == luuCtx->io.dst || NULL == luuCtx->io.src) {
+		CleanUpFailureToAllocate:;
+		bdestroy (luuCtx->io.dst);
+		bdestroy (luuCtx->io.src);
+		free (luuCtx);
+		return NULL;
+	}
+	luuCtx->io.badlines = badlines;
+	if (badlines) *badlines = 0;
+
+	luuCtx->sInp = sInp;
+
+	sOut = bsopen ((bNread) bsUuDecodePart, luuCtx);
+	if (NULL == sOut) goto CleanUpFailureToAllocate;
+	return sOut;
 }
 
-#define UU_MAX_LINELEN 45
 #define UU_ENCODE_BYTE(b) (char) (((b) == 0) ? '`' : ((b) + ' '))
 
-/*  bstring bUuEncode (const bstring src)
+/*  bstring bUuEncode (const_bstring src)
  *
  *  Performs a UUEncode of a block of data.  The "begin" and "end" lines are 
  *  not appended.
  */
-bstring bUuEncode (const bstring src) {
+bstring bUuEncode (const_bstring src) {
 bstring out;
 int i, j, jm;
 unsigned int c0, c1, c2;
@@ -500,18 +728,18 @@ unsigned int c0, c1, c2;
 			break;
 		}
 		for (j = i; j < jm; j += 3) {
-			c0 = bchar (src, j    );
-			c1 = bchar (src, j + 1);
-			c2 = bchar (src, j + 2);
+			c0 = (unsigned int) bchar (src, j    );
+			c1 = (unsigned int) bchar (src, j + 1);
+			c2 = (unsigned int) bchar (src, j + 2);
 			if (bconchar (out, UU_ENCODE_BYTE ( (c0 & 0xFC) >> 2)) < 0 ||
-				bconchar (out, UU_ENCODE_BYTE (((c0 & 0x03) << 4) | ((c1 & 0xF0) >> 4))) < 0 ||
-				bconchar (out, UU_ENCODE_BYTE (((c1 & 0x0F) << 2) | ((c2 & 0xC0) >> 6))) < 0 ||
-				bconchar (out, UU_ENCODE_BYTE ( (c2 & 0x3F))) < 0) {
-					bstrFree (out);
-					goto End;
-				}
+			    bconchar (out, UU_ENCODE_BYTE (((c0 & 0x03) << 4) | ((c1 & 0xF0) >> 4))) < 0 ||
+			    bconchar (out, UU_ENCODE_BYTE (((c1 & 0x0F) << 2) | ((c2 & 0xC0) >> 6))) < 0 ||
+			    bconchar (out, UU_ENCODE_BYTE ( (c2 & 0x3F))) < 0) {
+				bstrFree (out);
+				goto End;
+			}
 		}
-		if (bconchar (out, '\r') < 0 || bconchar (out, '\n') < 0) {
+		if (bconchar (out, (char) '\r') < 0 || bconchar (out, (char) '\n') < 0) {
 			bstrFree (out);
 			break;
 		}
@@ -520,13 +748,13 @@ unsigned int c0, c1, c2;
 	return out;
 }
 
-/*  bstring bYEncode (const bstring src)
+/*  bstring bYEncode (const_bstring src)
  *
  *  Performs a YEncode of a block of data.  No header or tail info is 
  *  appended.  See: http://www.yenc.org/whatis.htm and 
  *  http://www.yenc.org/yenc-draft.1.3.txt
  */
-bstring bYEncode (const bstring src) {
+bstring bYEncode (const_bstring src) {
 int i;
 bstring out;
 unsigned char c;
@@ -536,7 +764,7 @@ unsigned char c;
 	for (i=0; i < src->slen; i++) {
 		c = (unsigned char)(src->data[i] + 42);
 		if (c == '=' || c == '\0' || c == '\r' || c == '\n') {
-			if (0 > bconchar (out, '=')) {
+			if (0 > bconchar (out, (char) '=')) {
 				bdestroy (out);
 				return NULL;
 			}
@@ -550,21 +778,27 @@ unsigned char c;
 	return out;
 }
 
-/*  bstring bYDecode (const bstring src)
+/*  bstring bYDecode (const_bstring src)
  *
  *  Performs a YDecode of a block of data.  See: 
  *  http://www.yenc.org/whatis.htm and http://www.yenc.org/yenc-draft.1.3.txt
  */
-bstring bYDecode (const bstring src) {
+#define MAX_OB_LEN (64)
+
+bstring bYDecode (const_bstring src) {
 int i;
 bstring out;
 unsigned char c;
+unsigned char octetbuff[MAX_OB_LEN];
+int obl;
 
 	if (src == NULL || src->slen < 0 || src->data == NULL) return NULL;
 	if ((out = bfromcstr ("")) == NULL) return NULL;
+
+	obl = 0;
+
 	for (i=0; i < src->slen; i++) {
-		c = src->data[i];
-		if (c == '=') {
+		if ('=' == (c = src->data[i])) { /* The = escape mode */
 			i++;
 			if (i >= src->slen) {
 				bdestroy (out);
@@ -572,7 +806,7 @@ unsigned char c;
 			}
 			c = (unsigned char) (src->data[i] - 64);
 		} else {
-			if (c == '\0') {
+			if ('\0' == c) {
 				bdestroy (out);
 				return NULL;
 			}
@@ -580,11 +814,320 @@ unsigned char c;
 			/* Extraneous CR/LFs are to be ignored. */
 			if (c == '\r' || c == '\n') continue;
 		}
-		if (0 > bconchar (out, (char)(c - (unsigned char) 42))) {
-			bdestroy (out);
-			return NULL;
+
+		octetbuff[obl] = (unsigned char) ((int) c - 42);
+		obl++;
+
+		if (obl >= MAX_OB_LEN) {
+			if (0 > bcatblk (out, octetbuff, obl)) {
+				bdestroy (out);
+				return NULL;
+			}
+			obl = 0;
 		}
 	}
+
+	if (0 > bcatblk (out, octetbuff, obl)) {
+		bdestroy (out);
+		out = NULL;
+	}
 	return out;
 }
 
+/*  bstring bStrfTime (const char * fmt, const struct tm * timeptr)
+ *
+ *  Takes a format string that is compatible with strftime and a struct tm
+ *  pointer, formats the time according to the format string and outputs
+ *  the bstring as a result. Note that if there is an early generation of a 
+ *  '\0' character, the bstring will be truncated to this end point.
+ */
+bstring bStrfTime (const char * fmt, const struct tm * timeptr) {
+#if defined (__TURBOC__) && !defined (__BORLANDC__)
+static struct tagbstring ns = bsStatic ("bStrfTime Not supported");
+	fmt = fmt;
+	timeptr = timeptr;
+	return &ns;
+#else
+bstring buff;
+int n;
+size_t r;
+
+	if (fmt == NULL) return NULL;
+
+	/* Since the length is not determinable beforehand, a search is
+	   performed using the truncating "strftime" call on increasing 
+	   potential sizes for the output result. */
+
+	if ((n = (int) (2*strlen (fmt))) < 16) n = 16;
+	buff = bfromcstralloc (n+2, "");
+
+	for (;;) {
+		if (BSTR_OK != balloc (buff, n + 2)) {
+			bdestroy (buff);
+			return NULL;
+		}
+
+		r = strftime ((char *) buff->data, n + 1, fmt, timeptr);
+
+		if (r > 0) {
+			buff->slen = (int) r;
+			break;
+		}
+
+		n += n;
+	}
+
+	return buff;
+#endif
+}
+
+/*  int bSetCstrChar (bstring a, int pos, char c)
+ *
+ *  Sets the character at position pos to the character c in the bstring a.
+ *  If the character c is NUL ('\0') then the string is truncated at this
+ *  point.  Note: this does not enable any other '\0' character in the bstring
+ *  as terminator indicator for the string.  pos must be in the position 
+ *  between 0 and b->slen inclusive, otherwise BSTR_ERR will be returned.
+ */
+int bSetCstrChar (bstring b, int pos, char c) {
+	if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen)
+		return BSTR_ERR;
+	if (pos < 0 || pos > b->slen) return BSTR_ERR;
+
+	if (pos == b->slen) {
+		if ('\0' != c) return bconchar (b, c);
+		return 0;
+	}
+
+	b->data[pos] = (unsigned char) c;
+	if ('\0' == c) b->slen = pos;
+
+	return 0;
+}
+
+/*  int bSetChar (bstring b, int pos, char c)
+ *
+ *  Sets the character at position pos to the character c in the bstring a.
+ *  The string is not truncated if the character c is NUL ('\0').  pos must
+ *  be in the position between 0 and b->slen inclusive, otherwise BSTR_ERR
+ *  will be returned.
+ */
+int bSetChar (bstring b, int pos, char c) {
+	if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen)
+		return BSTR_ERR;
+	if (pos < 0 || pos > b->slen) return BSTR_ERR;
+
+	if (pos == b->slen) {
+		return bconchar (b, c);
+	}
+
+	b->data[pos] = (unsigned char) c;
+	return 0;
+}
+
+#define INIT_SECURE_INPUT_LENGTH (256)
+
+/*  bstring bSecureInput (int maxlen, int termchar, 
+ *                        bNgetc vgetchar, void * vgcCtx)
+ *
+ *  Read input from an abstracted input interface, for a length of at most
+ *  maxlen characters.  If maxlen <= 0, then there is no length limit put
+ *  on the input.  The result is terminated early if vgetchar() return EOF
+ *  or the user specified value termchar.
+ *
+ */
+bstring bSecureInput (int maxlen, int termchar, bNgetc vgetchar, void * vgcCtx) {
+int i, m, c;
+bstring b, t;
+
+	if (!vgetchar) return NULL;
+
+	b = bfromcstralloc (INIT_SECURE_INPUT_LENGTH, "");
+	if ((c = UCHAR_MAX + 1) == termchar) c++;
+
+	for (i=0; ; i++) {
+		if (termchar == c || (maxlen > 0 && i >= maxlen)) c = EOF;
+		else c = vgetchar (vgcCtx);
+
+		if (EOF == c) break;
+
+		if (i+1 >= b->mlen) {
+
+			/* Double size, but deal with unusual case of numeric
+			   overflows */
+
+			if ((m = b->mlen << 1)   <= b->mlen &&
+			    (m = b->mlen + 1024) <= b->mlen &&
+			    (m = b->mlen + 16)   <= b->mlen &&
+			    (m = b->mlen + 1)    <= b->mlen) t = NULL;
+			else t = bfromcstralloc (m, "");
+
+			if (t) memcpy (t->data, b->data, i);
+			bSecureDestroy (b); /* Cleanse previous buffer */
+			b = t;
+			if (!b) return b;
+		}
+
+		b->data[i] = (unsigned char) c;
+	}
+
+	b->slen = i;
+	b->data[i] = (unsigned char) '\0';
+	return b;
+}
+
+#define BWS_BUFF_SZ (1024)
+
+struct bwriteStream {
+    bstring buff;    /* Buffer for underwrites                   */
+    void * parm;     /* The stream handle for core stream        */
+    bNwrite writeFn; /* fwrite work-a-like fnptr for core stream */
+    int isEOF;       /* track stream's EOF state                 */
+    int minBuffSz;
+};
+
+/*  struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm)
+ *
+ *  Wrap a given open stream (described by a fwrite work-a-like function 
+ *  pointer and stream handle) into an open bwriteStream suitable for write
+ *  streaming functions.
+ */
+struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm) {
+struct bwriteStream * ws;
+
+	if (NULL == writeFn) return NULL;
+	ws = (struct bwriteStream *) malloc (sizeof (struct bwriteStream));
+	if (ws) {
+		if (NULL == (ws->buff = bfromcstr (""))) {
+			free (ws);
+			ws = NULL;
+		} else {
+			ws->parm = parm;
+			ws->writeFn = writeFn;
+			ws->isEOF = 0;
+			ws->minBuffSz = BWS_BUFF_SZ;
+		}
+	}
+	return ws;
+}
+
+#define internal_bwswriteout(ws,b) { \
+	if ((b)->slen > 0) { \
+		if (1 != (ws->writeFn ((b)->data, (b)->slen, 1, ws->parm))) { \
+			ws->isEOF = 1; \
+			return BSTR_ERR; \
+		} \
+	} \
+}
+
+/*  int bwsWriteFlush (struct bwriteStream * ws)
+ *
+ *  Force any pending data to be written to the core stream.
+ */
+int bwsWriteFlush (struct bwriteStream * ws) {
+	if (NULL == ws || ws->isEOF || 0 >= ws->minBuffSz || 
+	    NULL == ws->writeFn || NULL == ws->buff) return BSTR_ERR;
+	internal_bwswriteout (ws, ws->buff);
+	ws->buff->slen = 0;
+	return 0;
+}
+
+/*  int bwsWriteBstr (struct bwriteStream * ws, const_bstring b)
+ *
+ *  Send a bstring to a bwriteStream.  If the stream is at EOF BSTR_ERR is
+ *  returned.  Note that there is no deterministic way to determine the exact
+ *  cut off point where the core stream stopped accepting data.
+ */
+int bwsWriteBstr (struct bwriteStream * ws, const_bstring b) {
+struct tagbstring t;
+int l;
+
+	if (NULL == ws || NULL == b || NULL == ws->buff ||
+	    ws->isEOF || 0 >= ws->minBuffSz || NULL == ws->writeFn)
+		return BSTR_ERR;
+
+	/* Buffer prepacking optimization */
+	if (b->slen > 0 && ws->buff->mlen - ws->buff->slen > b->slen) {
+		static struct tagbstring empty = bsStatic ("");
+		if (0 > bconcat (ws->buff, b)) return BSTR_ERR;
+		return bwsWriteBstr (ws, &empty);
+	}
+
+	if (0 > (l = ws->minBuffSz - ws->buff->slen)) {
+		internal_bwswriteout (ws, ws->buff);
+		ws->buff->slen = 0;
+		l = ws->minBuffSz;
+	}
+
+	if (b->slen < l) return bconcat (ws->buff, b);
+
+	if (0 > bcatblk (ws->buff, b->data, l)) return BSTR_ERR;
+	internal_bwswriteout (ws, ws->buff);
+	ws->buff->slen = 0;
+
+	bmid2tbstr (t, (bstring) b, l, b->slen);
+
+	if (t.slen >= ws->minBuffSz) {
+		internal_bwswriteout (ws, &t);
+		return 0;
+	}
+
+	return bassign (ws->buff, &t);
+}
+
+/*  int bwsWriteBlk (struct bwriteStream * ws, void * blk, int len)
+ *
+ *  Send a block of data a bwriteStream.  If the stream is at EOF BSTR_ERR is 
+ *  returned.
+ */
+int bwsWriteBlk (struct bwriteStream * ws, void * blk, int len) {
+struct tagbstring t;
+	if (NULL == blk || len < 0) return BSTR_ERR;
+	blk2tbstr (t, blk, len);
+	return bwsWriteBstr (ws, &t);
+}
+
+/*  int bwsIsEOF (const struct bwriteStream * ws)
+ *
+ *  Returns 0 if the stream is currently writable, 1 if the core stream has 
+ *  responded by not accepting the previous attempted write.
+ */
+int bwsIsEOF (const struct bwriteStream * ws) {
+	if (NULL == ws || NULL == ws->buff || 0 > ws->minBuffSz || 
+	    NULL == ws->writeFn) return BSTR_ERR;
+	return ws->isEOF;
+}
+
+/*  int bwsBuffLength (struct bwriteStream * ws, int sz)
+ *
+ *  Set the length of the buffer used by the bwsStream.  If sz is zero, the 
+ *  length is not set.  This function returns with the previous length.
+ */
+int bwsBuffLength (struct bwriteStream * ws, int sz) {
+int oldSz;
+	if (ws == NULL || sz < 0) return BSTR_ERR;
+	oldSz = ws->minBuffSz;
+	if (sz > 0) ws->minBuffSz = sz;
+	return oldSz;
+}
+
+/*  void * bwsClose (struct bwriteStream * s)
+ *
+ *  Close the bwriteStream, and return the handle to the stream that was 
+ *  originally used to open the given stream.  Note that even if the stream
+ *  is at EOF it still needs to be closed with a call to bwsClose.
+ */
+void * bwsClose (struct bwriteStream * ws) {
+void * parm;
+	if (NULL == ws || NULL == ws->buff || 0 >= ws->minBuffSz || 
+	    NULL == ws->writeFn) return NULL;
+	bwsWriteFlush (ws);
+	parm = ws->parm;
+	ws->parm = NULL;
+	ws->minBuffSz = -1;
+	ws->writeFn = NULL;
+	bstrFree (ws->buff);
+	free (ws);
+	return parm;
+}
+
diff -urp pads-1.2.orig/lib/bstring/bstraux.h pads-1.2/lib/bstring/bstraux.h
--- pads-1.2.orig/lib/bstring/bstraux.h	2008-07-08 14:28:29.000000000 -0400
+++ pads-1.2/lib/bstring/bstraux.h	2008-07-10 18:09:21.000000000 -0400
@@ -1,14 +1,14 @@
 /*
  * This source file is part of the bstring string library.  This code was
- * written by Paul Hsieh in 2002-2004, and is covered by the BSD open source 
- * license. Refer to the accompanying documentation for details on usage and 
- * license.
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source 
+ * license and the GPL. Refer to the accompanying documentation for details 
+ * on usage and license.
  */
 
 /*
  * bstraux.h
  *
- * This file is not necessarily part of the core bstring library itself, but
+ * This file is not a necessary part of the core bstring library itself, but
  * is just an auxilliary module which includes miscellaneous or trivial 
  * functions.
  */
@@ -16,37 +16,47 @@
 #ifndef BSTRAUX_INCLUDE
 #define BSTRAUX_INCLUDE
 
+#include <time.h>
 #include "bstrlib.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* Safety mechanisms */
 #define bstrDeclare(b)               bstring (b) = NULL; 
 #define bstrFree(b)                  {if ((b) != NULL && (b)->slen >= 0 && (b)->mlen >= (b)->slen) { bdestroy (b); (b) = NULL; }}
 
-/* Backward compatibilty with previous version of Bstrlib */
+/* Backward compatibilty with previous versions of Bstrlib */
 #define bAssign(a,b)                 ((bassign)((a), (b)))
 #define bSubs(b,pos,len,a,c)         ((breplace)((b),(pos),(len),(a),(unsigned char)(c)))
-#define bStrchr(b,c)                 ((bstrchr)((b),(c)))
-#define bStrchrFast(b,c)             ((bstrchr)((b),(c)))
+#define bStrchr(b,c)                 ((bstrchr)((b), (c)))
+#define bStrchrFast(b,c)             ((bstrchr)((b), (c)))
 #define bCatCstr(b,s)                ((bcatcstr)((b), (s)))
 #define bCatBlk(b,s,len)             ((bcatblk)((b),(s),(len)))
-#define bCatStatic(b,s)              bCatBlk ((b), (s), sizeof (s) - 1)
+#define bCatStatic(b,s)              bCatBlk ((b), ("" s ""), sizeof (s) - 1)
+#define bTrunc(b,n)                  ((btrunc)((b), (n)))
 #define bReplaceAll(b,find,repl,pos) ((bfindreplace)((b),(find),(repl),(pos)))
 #define bUppercase(b)                ((btoupper)(b))
 #define bLowercase(b)                ((btolower)(b))
-#define bCaselessCmp(a,b)            ((bstricmp)(a,b))
-#define bCaselessNCmp(a,b,n)         ((bstrnicmp)(a,b,n))
+#define bCaselessCmp(a,b)            ((bstricmp)((a), (b)))
+#define bCaselessNCmp(a,b,n)         ((bstrnicmp)((a), (b), (n)))
+#define bBase64Decode(b)             (bBase64DecodeEx ((b), NULL))
+#define bUuDecode(b)                 (bUuDecodeEx ((b), NULL))
 
 /* Unusual functions */
-extern int bTrunc (bstring b, int n);
+extern struct bStream * bsFromBstr (const_bstring b);
 extern bstring bTail (bstring b, int n);
 extern bstring bHead (bstring b, int n);
+extern int bSetCstrChar (bstring a, int pos, char c);
+extern int bSetChar (bstring b, int pos, char c);
 extern int bFill (bstring a, char c, int len);
 extern int bReplicate (bstring b, int n);
 extern int bReverse (bstring b);
 extern int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill);
+extern bstring bStrfTime (const char * fmt, const struct tm * timeptr);
+#define bAscTime(t) (bStrfTime ("%c\n", (t)))
+#define bCTime(t)   ((t) ? bAscTime (localtime (t)) : NULL)
 
 /* Spacing formatting */
 extern int bJustifyLeft (bstring b, int space);
@@ -55,14 +65,45 @@ extern int bJustifyMargin (bstring b, in
 extern int bJustifyCenter (bstring b, int width, int space);
 
 /* Esoteric standards specific functions */
-extern char * bStr2NetStr (const bstring b);
+extern char * bStr2NetStr (const_bstring b);
 extern bstring bNetStr2Bstr (const char * buf);
-extern bstring bBase64Encode (const bstring b);
-extern bstring bBase64Decode (const bstring b);
-extern bstring bUuDecode (const bstring src);
-extern bstring bUuEncode (const bstring src);
-extern bstring bYEncode (const bstring src);
-extern bstring bYDecode (const bstring src);
+extern bstring bBase64Encode (const_bstring b);
+extern bstring bBase64DecodeEx (const_bstring b, int * boolTruncError);
+extern struct bStream * bsUuDecode (struct bStream * sInp, int * badlines);
+extern bstring bUuDecodeEx (const_bstring src, int * badlines);
+extern bstring bUuEncode (const_bstring src);
+extern bstring bYEncode (const_bstring src);
+extern bstring bYDecode (const_bstring src);
+
+/* Writable stream */
+typedef int (* bNwrite) (const void * buf, size_t elsize, size_t nelem, void * parm);
+
+struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm);
+int bwsWriteBstr (struct bwriteStream * stream, const_bstring b);
+int bwsWriteBlk (struct bwriteStream * stream, void * blk, int len);
+int bwsWriteFlush (struct bwriteStream * stream);
+int bwsIsEOF (const struct bwriteStream * stream);
+int bwsBuffLength (struct bwriteStream * stream, int sz);
+void * bwsClose (struct bwriteStream * stream);
+
+/* Security functions */
+#define bSecureDestroy(b) {	                                            \
+bstring bstr__tmp = (b);	                                            \
+	if (bstr__tmp && bstr__tmp->mlen > 0 && bstr__tmp->data) {          \
+	    (void) memset (bstr__tmp->data, 0, (size_t) bstr__tmp->mlen);   \
+	    bdestroy (bstr__tmp);                                           \
+	}                                                                   \
+}
+#define bSecureWriteProtect(t) {	                                              \
+	if ((t).mlen >= 0) {                                                          \
+	    if ((t).mlen > (t).slen)) {                                               \
+	        (void) memset ((t).data + (t).slen, 0, (size_t) (t).mlen - (t).slen); \
+	    }                                                                         \
+	    (t).mlen = -1;                                                            \
+	}                                                                             \
+}
+extern bstring bSecureInput (int maxlen, int termchar, 
+                             bNgetc vgetchar, void * vgcCtx);
 
 #ifdef __cplusplus
 }
diff -urp pads-1.2.orig/lib/bstring/bstrlib.c pads-1.2/lib/bstring/bstrlib.c
--- pads-1.2.orig/lib/bstring/bstrlib.c	2008-07-08 14:28:29.000000000 -0400
+++ pads-1.2/lib/bstring/bstrlib.c	2008-07-10 18:09:06.000000000 -0400
@@ -1,8 +1,8 @@
 /*
  * This source file is part of the bstring string library.  This code was
- * written by Paul Hsieh in 2002-2004, and is covered by the BSD open source 
- * license. Refer to the accompanying documentation for details on usage and 
- * license.
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source 
+ * license and the GPL. Refer to the accompanying documentation for details 
+ * on usage and license.
  */
 
 /*
@@ -12,6 +12,7 @@
  */
 
 #include <stdio.h>
+#include <stddef.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
@@ -24,9 +25,41 @@
 #include "memdbg.h"
 #endif
 
+#ifndef bstr__alloc
+#define bstr__alloc(x) malloc (x)
+#endif
+
+#ifndef bstr__free
+#define bstr__free(p) free (p)
+#endif
+
+#ifndef bstr__realloc
+#define bstr__realloc(p,x) realloc ((p), (x))
+#endif
+
+#ifndef bstr__memcpy
+#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
+#endif
+
+#ifndef bstr__memmove
+#define bstr__memmove(d,s,l) memmove ((d), (s), (l))
+#endif
+
+#ifndef bstr__memset
+#define bstr__memset(d,c,l) memset ((d), (c), (l))
+#endif
+
+#ifndef bstr__memcmp
+#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
+#endif
+
+#ifndef bstr__memchr
+#define bstr__memchr(s,c,l) memchr ((s), (c), (l))
+#endif
+
 /* Just a length safe wrapper for memmove. */
 
-#define bBlockCopy(D,S,L) { if ((L) > 0) memmove ((D),(S),(L)); }
+#define bBlockCopy(D,S,L) { if ((L) > 0) bstr__memmove ((D),(S),(L)); }
 
 /* Compute the snapped size for a given requested size.  By snapping to powers
    of 2 like this, repeated reallocations are avoided. */
@@ -35,16 +68,21 @@ static int snapUpSize (int i) {
 		i = 8;
 	} else {
 		unsigned int j;
-		j = i;
+		j = (unsigned int) i;
 
-		/* Assumes your system is at least 32 bits, and your string is
-		   at most 4GB is size. */
 		j |= (j >>  1);
 		j |= (j >>  2);
 		j |= (j >>  4);
-		j |= (j >>  8);
-		j |= (j >> 16);
-		i = j + 1;		/* Least power of two greater than i */
+		j |= (j >>  8);		/* Ok, since int >= 16 bits */
+#if (UINT_MAX != 0xffff)
+		j |= (j >> 16);		/* For 32 bit int systems */
+#if (UINT_MAX > 0xffffffffUL)
+		j |= (j >> 32);		/* For 64 bit int systems */
+#endif
+#endif
+		/* Least power of two greater than i */
+		j++;
+		if ((int) j >= i) i = (int) j;
 	}
 	return i;
 }
@@ -53,16 +91,17 @@ static int snapUpSize (int i) {
  *
  *  Increase the size of the memory backing the bstring b to at least len.
  */
-int balloc (bstring b, int len) {
-	if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < 0 || 
-	    b->mlen < b->slen || len <= 0) {
+int balloc (bstring b, int olen) {
+	int len;
+	if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 || 
+	    b->mlen < b->slen || olen <= 0) {
 		return BSTR_ERR;
 	}
 
-	if (len >= b->mlen) {
+	if (olen >= b->mlen) {
 		unsigned char * x;
 
-		len = snapUpSize (len);
+		if ((len = snapUpSize (olen)) <= b->mlen) return BSTR_OK;
 
 		/* Assume probability of a non-moving realloc is 0.125 */
 		if (7 * b->mlen < 8 * b->slen) {
@@ -70,26 +109,66 @@ int balloc (bstring b, int len) {
 			/* If slen is close to mlen in size then use realloc to reduce
 			   the memory defragmentation */
 
-			x = (unsigned char *) realloc (b->data, len);
-			if (x == NULL) return BSTR_ERR;
+			reallocStrategy:;
+
+			x = (unsigned char *) bstr__realloc (b->data, (size_t) len);
+			if (x == NULL) {
+
+				/* Since we failed, try allocating the tighest possible 
+				   allocation */
+
+				if (NULL == (x = (unsigned char *) bstr__realloc (b->data, (size_t) (len = olen)))) {
+					return BSTR_ERR;
+				}
+			}
 		} else {
 
 			/* If slen is not close to mlen then avoid the penalty of copying
 			   the extra bytes that are allocated, but not considered part of
 			   the string */
 
-			x = (unsigned char *) malloc (len);
-			if (x == NULL) {
-				x = (unsigned char *) realloc (b->data, len);
-				if (x == NULL) return BSTR_ERR;
+			if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) {
+
+				/* Perhaps there is no available memory for the two 
+				   allocations to be in memory at once */
+
+				goto reallocStrategy;
+
 			} else {
-				if (b->slen) memcpy ((char *) x, (char *) b->data, b->slen);
-				free (b->data);
+				if (b->slen) bstr__memcpy ((char *) x, (char *) b->data, (size_t) b->slen);
+				bstr__free (b->data);
 			}
 		}
 		b->data = x;
 		b->mlen = len;
-		b->data[b->slen] = '\0';
+		b->data[b->slen] = (unsigned char) '\0';
+	}
+
+	return BSTR_OK;
+}
+
+/*  int ballocmin (bstring b, int len)
+ *
+ *  Set the size of the memory backing the bstring b to len or b->slen+1,
+ *  whichever is larger.  Note that repeated use of this function can degrade
+ *  performance.
+ */
+int ballocmin (bstring b, int len) {
+	unsigned char * s;
+
+	if (b == NULL || b->data == NULL || (b->slen+1) < 0 || b->mlen <= 0 || 
+	    b->mlen < b->slen || len <= 0) {
+		return BSTR_ERR;
+	}
+
+	if (len < b->slen + 1) len = b->slen + 1;
+
+	if (len != b->mlen) {
+		s = (unsigned char *) bstr__realloc (b->data, (size_t) len);
+		if (NULL == s) return BSTR_ERR;
+		s[b->slen] = (unsigned char) '\0';
+		b->data = s;
+		b->mlen = len;
 	}
 
 	return BSTR_OK;
@@ -97,31 +176,58 @@ int balloc (bstring b, int len) {
 
 /*  bstring bfromcstr (const char * str)
  *
- *  Create a bstring which contains the content of the '\0' terminated char *
+ *  Create a bstring which contains the contents of the '\0' terminated char *
  *  buffer str.
  */
 bstring bfromcstr (const char * str) {
 bstring b;
-int i,j;
+int i;
+size_t j;
 
 	if (str == NULL) return NULL;
-	b = (bstring) malloc (sizeof (struct tagbstring));
-	if (b == NULL) return NULL;
-	j = (int) (strlen) (str); 
-	b->slen = j;
+	j = (strlen) (str);
+	i = snapUpSize ((int) (j + (2 - (j != 0))));
+	if (i <= (int) j) return NULL;
+
+	b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+	if (NULL == b) return NULL;
+	b->slen = (int) j;
+	if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
+		bstr__free (b);
+		return NULL;
+	}
 
-	i = j + (2 - (j != 0));
-	i = snapUpSize (i);
+	bstr__memcpy (b->data, str, j+1);
+	return b;
+}
 
-	b->mlen = i;
+/*  bstring bfromcstralloc (int mlen, const char * str)
+ *
+ *  Create a bstring which contains the contents of the '\0' terminated char *
+ *  buffer str.  The memory buffer backing the string is at least len 
+ *  characters in length.
+ */
+bstring bfromcstralloc (int mlen, const char * str) {
+bstring b;
+int i;
+size_t j;
 
-	b->data = (unsigned char *) malloc (b->mlen);
-	if (b->data == NULL) {
-		free (b);
+	if (str == NULL) return NULL;
+	j = (strlen) (str);
+	i = snapUpSize ((int) (j + (2 - (j != 0))));
+	if (i <= (int) j) return NULL;
+
+	b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+	if (b == NULL) return NULL;
+	b->slen = (int) j;
+	if (i < mlen) i = mlen;
+
+	if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
+		bstr__free (b);
 		return NULL;
 	}
 
-	memcpy (b->data, str, j+1);
+	bstr__memcpy (b->data, str, j+1);
 	return b;
 }
 
@@ -135,7 +241,7 @@ bstring b;
 int i;
 
 	if (blk == NULL || len < 0) return NULL;
-	b = (bstring) malloc (sizeof (struct tagbstring));
+	b = (bstring) bstr__alloc (sizeof (struct tagbstring));
 	if (b == NULL) return NULL;
 	b->slen = len;
 
@@ -144,81 +250,96 @@ int i;
 
 	b->mlen = i;
 
-	b->data = (unsigned char *) malloc (b->mlen) ;
+	b->data = (unsigned char *) bstr__alloc ((size_t) b->mlen);
 	if (b->data == NULL) {
-		free (b);
+		bstr__free (b);
 		return NULL;
 	}
 
-	if (len > 0) memcpy (b->data, blk, len);
-	b->data[len] = '\0';
+	if (len > 0) bstr__memcpy (b->data, blk, (size_t) len);
+	b->data[len] = (unsigned char) '\0';
 
 	return b;
 }
 
-/*  char * bstr2cstr (const bstring s, char z)
+/*  char * bstr2cstr (const_bstring s, char z)
  *
  *  Create a '\0' terminated char * buffer which is equal to the contents of 
- *  the bstring s, except that any contained '\0' characters are converted to 
- *  the character in z. This returned value should be freed with a free() 
- *  call, by the calling application.
+ *  the bstring s, except that any contained '\0' characters are converted 
+ *  to the character in z. This returned value should be freed with a 
+ *  bcstrfree () call, by the calling application.
  */
-char * bstr2cstr (const bstring b, char z) {
-int i,l;
+char * bstr2cstr (const_bstring b, char z) {
+int i, l;
 char * r;
 
 	if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
 	l = b->slen;
-	r = (char *) malloc (l + 1);
+	r = (char *) bstr__alloc ((size_t) (l + 1));
 	if (r == NULL) return r;
 
 	for (i=0; i < l; i ++) {
 		r[i] = (char) ((b->data[i] == '\0') ? z : (char) (b->data[i]));
 	}
 
-	r[l] = '\0';
+	r[l] = (unsigned char) '\0';
 
 	return r;
 }
 
-/*  int bcstrfree (const char * s)
+/*  int bcstrfree (char * s)
  *
  *  Frees a C-string generated by bstr2cstr ().  This is normally unnecessary
- *  since it just wraps a call to free (), however, if malloc () and free () 
- *  have been redefined as a macros within the bstrlib module (via defining 
- *  them in memdbg.h after defining BSTRLIB_MEMORY_DEBUG) with some 
- *  difference in behaviour from the std library functions, then this allows 
- *  a correct way of freeing the memory that allows higher level code to be 
- *  independent from these macro redefinitions.
+ *  since it just wraps a call to bstr__free (), however, if bstr__alloc () 
+ *  and bstr__free () have been redefined as a macros within the bstrlib 
+ *  module (via defining them in memdbg.h after defining 
+ *  BSTRLIB_MEMORY_DEBUG) with some difference in behaviour from the std 
+ *  library functions, then this allows a correct way of freeing the memory 
+ *  that allows higher level code to be independent from these macro 
+ *  redefinitions.
  */
 int bcstrfree (char * s) {
 	if (s) {
-		free (s);
+		bstr__free (s);
 		return BSTR_OK;
 	}
 	return BSTR_ERR;
 }
 
-/*  int bconcat (bstring b0, const bstring b1)
+/*  int bconcat (bstring b0, const_bstring b1)
  *
  *  Concatenate the bstring b1 to the bstring b0.
  */
-int bconcat (bstring b0, const bstring b1) {
+int bconcat (bstring b0, const_bstring b1) {
 int len, d;
+bstring aux = (bstring) b1;
+
+	if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL) return BSTR_ERR;
 
-	if (b0 == NULL || b1 == NULL) return BSTR_ERR;
 	d = b0->slen;
 	len = b1->slen;
-	if ((d | (b0->mlen - d) | len) < 0) return BSTR_ERR;
-	if (balloc (b0, d + len + 1) != BSTR_OK) return BSTR_ERR;
-	bBlockCopy (&b0->data[d], &b1->data[0], len);
-	b0->data[d + len] = '\0';
-	b0->slen += len;
-	return 0;
+	if ((d | (b0->mlen - d) | len | (d + len)) < 0) return BSTR_ERR;
+
+	if (b0->mlen <= d + len + 1) {
+		ptrdiff_t pd = b1->data - b0->data;
+		if (0 <= pd && pd < b0->mlen) {
+			if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
+		}
+		if (balloc (b0, d + len + 1) != BSTR_OK) {
+			if (aux != b1) bdestroy (aux);
+			return BSTR_ERR;
+		}
+	}
+
+	bBlockCopy (&b0->data[d], &aux->data[0], (size_t) len);
+	b0->data[d + len] = (unsigned char) '\0';
+	b0->slen = d + len;
+	if (aux != b1) bdestroy (aux);
+	return BSTR_OK;
 }
 
 /*  int bconchar (bstring b, char c)
- *
+/ *
  *  Concatenate the single character c to the bstring b.
  */
 int bconchar (bstring b, char c) {
@@ -228,9 +349,9 @@ int d;
 	d = b->slen;
 	if ((d | (b->mlen - d)) < 0 || balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
 	b->data[d] = (unsigned char) c;
-	b->data[d + 1] = '\0';
+	b->data[d + 1] = (unsigned char) '\0';
 	b->slen++;
-	return 0;
+	return BSTR_OK;
 }
 
 /*  int bcatcstr (bstring b, const char * s)
@@ -238,11 +359,11 @@ int d;
  *  Concatenate a char * string to a bstring.
  */
 int bcatcstr (bstring b, const char * s) {
-struct tagbstring t;
 char * d;
 int i, l;
 
-	if (b == NULL || b->data == NULL || b->slen < 0 || s == NULL) return BSTR_ERR;
+	if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
+	 || b->mlen <= 0 || s == NULL) return BSTR_ERR;
 
 	/* Optimistically concatenate directly */
 	l = b->mlen - b->slen;
@@ -256,34 +377,40 @@ int i, l;
 	b->slen += i;
 
 	/* Need to explicitely resize and concatenate tail */
-	cstr2tbstr (t, s);
-	return bconcat (b, &t);
+	return bcatblk (b, (const void *) s, (int) strlen (s));
 }
 
-/*  int bcatblk (bstring b, unsigned char * s, int len)
+/*  int bcatblk (bstring b, const void * s, int len)
  *
  *  Concatenate a fixed length buffer to a bstring.
  */
-int bcatblk (bstring b, const unsigned char * s, int len) {
-struct tagbstring t;
+int bcatblk (bstring b, const void * s, int len) {
+int nl;
+
+	if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
+	 || b->mlen <= 0 || s == NULL || len < 0) return BSTR_ERR;
+
+	if (0 > (nl = b->slen + len)) return BSTR_ERR; /* Overflow? */
+	if (b->mlen <= nl && 0 > balloc (b, nl + 1)) return BSTR_ERR;
 
-	if (s == NULL || len < 0) return BSTR_ERR;
-	blk2tbstr (t, s, len);
-	return bconcat (b, &t);
+	bBlockCopy (&b->data[b->slen], s, (size_t) len);
+	b->slen = nl;
+	b->data[nl] = (unsigned char) '\0';
+	return BSTR_OK;
 }
 
-/*  bstring bstrcpy (const bstring b)
+/*  bstring bstrcpy (const_bstring b)
  *
  *  Create a copy of the bstring b.
  */
-bstring bstrcpy (const bstring b) {
+bstring bstrcpy (const_bstring b) {
 bstring b0;
 int i,j;
 
 	/* Attempted to copy an invalid string? */
 	if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
 
-	b0 = (bstring) malloc (sizeof (struct tagbstring));
+	b0 = (bstring) bstr__alloc (sizeof (struct tagbstring));
 	if (b0 == NULL) {
 		/* Unable to allocate memory for string header */
 		return NULL;
@@ -292,13 +419,13 @@ int i,j;
 	i = b->slen;
 	j = snapUpSize (i + 1);
 
-	b0->data = (unsigned char *) malloc (j);
+	b0->data = (unsigned char *) bstr__alloc (j);
 	if (b0->data == NULL) {
 		j = i + 1;
-		b0->data = (unsigned char *) malloc (j);
+		b0->data = (unsigned char *) bstr__alloc (j);
 		if (b0->data == NULL) {
 			/* Unable to allocate memory for string data */
-			free (b0);
+			bstr__free (b0);
 			return NULL;
 		}
 	}
@@ -306,35 +433,127 @@ int i,j;
 	b0->mlen = j;
 	b0->slen = i;
 
-	if (i) memcpy ((char *) b0->data, (char *) b->data, i);
-	b0->data[b0->slen] = '\0';
+	if (i) bstr__memcpy ((char *) b0->data, (char *) b->data, i);
+	b0->data[b0->slen] = (unsigned char) '\0';
 
 	return b0;
 }
 
-/*  int bassign (bstring a, const bstring b)
+/*  int bassign (bstring a, const_bstring b)
  *
  *  Overwrite the string a with the contents of string b.
  */
-int bassign (bstring a, const bstring b) {
-	if (b == NULL || b->data == NULL || b->slen < 0 ) 
+int bassign (bstring a, const_bstring b) {
+	if (b == NULL || b->data == NULL || b->slen < 0)
 		return BSTR_ERR;
 	if (b->slen != 0) {
 		if (balloc (a, b->slen) != BSTR_OK) return BSTR_ERR;
-		memmove (a->data, b->data, b->slen);
+		bstr__memmove (a->data, b->data, b->slen);
 	} else {
 		if (a == NULL || a->data == NULL || a->mlen < a->slen || 
 		    a->slen < 0 || a->mlen == 0) 
 			return BSTR_ERR;
 	}
-	a->data[b->slen] = '\0';
+	a->data[b->slen] = (unsigned char) '\0';
 	a->slen = b->slen;
 	return BSTR_OK;
 }
 
-#define    ascii(c) ((unsigned char)(c) < 128)
-#define   upcase(c) ((c) + (((ascii(c) && islower(c)) ? ('A'-'a') : 0)))
-#define downcase(c) ((c) + (((ascii(c) && isupper(c)) ? ('a'-'A') : 0)))
+/*  int bassignmidstr (bstring a, const_bstring b, int left, int len)
+ *
+ *  Overwrite the string a with the middle of contents of string b 
+ *  starting from position left and running for a length len.  left and 
+ *  len are clamped to the ends of b as with the function bmidstr.
+ */
+int bassignmidstr (bstring a, const_bstring b, int left, int len) {
+	if (b == NULL || b->data == NULL || b->slen < 0)
+		return BSTR_ERR;
+
+	if (left < 0) {
+		len += left;
+		left = 0;
+	}
+
+	if (len > b->slen - left) len = b->slen - left;
+
+	if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+	    a->slen < 0 || a->mlen == 0)
+		return BSTR_ERR;
+
+	if (len > 0) {
+		if (balloc (a, len) != BSTR_OK) return BSTR_ERR;
+		bstr__memmove (a->data, b->data + left, len);
+		a->slen = len;
+	} else {
+		a->slen = 0;
+	}
+	a->data[a->slen] = (unsigned char) '\0';
+	return BSTR_OK;
+}
+
+/*  int bassigncstr (bstring a, const char * str)
+ *
+ *  Overwrite the string a with the contents of char * string str.  Note that 
+ *  the bstring a must be a well defined and writable bstring.  If an error 
+ *  occurs BSTR_ERR is returned however a may be partially overwritten.
+ */
+int bassigncstr (bstring a, const char * str) {
+int i;
+size_t len;
+	if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+	    a->slen < 0 || a->mlen == 0 || NULL == str) 
+		return BSTR_ERR;
+
+	for (i=0; i < a->mlen; i++) {
+		if ('\0' == (a->data[i] = str[i])) {
+			a->slen = i;
+			return BSTR_OK;
+		}
+	}
+
+	a->slen = i;
+	len = strlen (str + i);
+	if (len > INT_MAX || i + len + 1 > INT_MAX ||
+	    0 > balloc (a, (int) (i + len + 1))) return BSTR_ERR;
+	bBlockCopy (a->data + i, str + i, (size_t) len + 1);
+	a->slen += (int) len;
+	return BSTR_OK;
+}
+
+/*  int bassignblk (bstring a, const void * s, int len)
+ *
+ *  Overwrite the string a with the contents of the block (s, len).  Note that 
+ *  the bstring a must be a well defined and writable bstring.  If an error 
+ *  occurs BSTR_ERR is returned and a is not overwritten.
+ */
+int bassignblk (bstring a, const void * s, int len) {
+	if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+	    a->slen < 0 || a->mlen == 0 || NULL == s || len + 1 < 1) 
+		return BSTR_ERR;
+	if (len + 1 > a->mlen && 0 > balloc (a, len + 1)) return BSTR_ERR;
+	bBlockCopy (a->data, s, (size_t) len);
+	a->data[len] = (unsigned char) '\0';
+	a->slen = len;
+	return BSTR_OK;
+}
+
+/*  int btrunc (bstring b, int n)
+ *
+ *  Truncate the bstring to at most n characters.
+ */
+int btrunc (bstring b, int n) {
+	if (n < 0 || b == NULL || b->data == NULL || b->mlen < b->slen ||
+	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+	if (b->slen > n) {
+		b->slen = n;
+		b->data[n] = (unsigned char) '\0';
+	}
+	return BSTR_OK;
+}
+
+#define   upcase(c) (toupper ((unsigned char) c))
+#define downcase(c) (tolower ((unsigned char) c))
+#define   wspace(c) (isspace ((unsigned char) c))
 
 /*  int btoupper (bstring b)
  *
@@ -343,7 +562,7 @@ int bassign (bstring a, const bstring b)
 int btoupper (bstring b) {
 int i, len;
 	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
-	    b->slen < 0) return BSTR_ERR;
+	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
 	for (i=0, len = b->slen; i < len; i++) {
 		b->data[i] = (unsigned char) upcase (b->data[i]);
 	}
@@ -357,50 +576,60 @@ int i, len;
 int btolower (bstring b) {
 int i, len;
 	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
-	    b->slen < 0) return BSTR_ERR;
+	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
 	for (i=0, len = b->slen; i < len; i++) {
 		b->data[i] = (unsigned char) downcase (b->data[i]);
 	}
-	return 0;
+	return BSTR_OK;
 }
 
-/*  int bstricmp (const bstring b0, const bstring b1)
+/*  int bstricmp (const_bstring b0, const_bstring b1)
  *
  *  Compare two strings without differentiating between case.  The return 
  *  value is the difference of the values of the characters where the two 
- *  strings first differ, (taking a '\0' to be the character at the end of 
- *  any bstring) otherwise 0 is returned indicating that the strings are
- *  equal.
+ *  strings first differ after lower case transformation, otherwise 0 is 
+ *  returned indicating that the strings are equal.  If the lengths are 
+ *  different, then a difference from 0 is given, but if the first extra 
+ *  character is '\0', then it is taken to be the value UCHAR_MAX+1.
  */
-int bstricmp (const bstring b0, const bstring b1) {
+int bstricmp (const_bstring b0, const_bstring b1) {
 int i, v, n;
 
 	if (bdata (b0) == NULL || b0->slen < 0 || 
 	    bdata (b1) == NULL || b1->slen < 0) return SHRT_MIN;
-	n = b0->slen; if (n > b1->slen) n = b1->slen;
-	if (b0->slen == b1->slen && b0->data == b1->data) return 0;
+	if ((n = b0->slen) > b1->slen) n = b1->slen;
+	else if (b0->slen == b1->slen && b0->data == b1->data) return BSTR_OK;
 
 	for (i = 0; i < n; i ++) {
-		v  = ((char) downcase (b0->data[i]));
-		v -= ((char) downcase (b1->data[i]));
-		if (v != 0) return v;
-		if (b0->data[i] == '\0') return 0;
+		v  = (char) downcase (b0->data[i])
+		   - (char) downcase (b1->data[i]);
+		if (0 != v) return v;
 	}
 
-	if (b0->slen > n) return   ((char) downcase (b0->data[n]));
-	if (b1->slen > n) return - ((char) downcase (b1->data[n]));
-	return 0;
+	if (b0->slen > n) {
+		v = (char) downcase (b0->data[n]);
+		if (v) return v;
+		return UCHAR_MAX + 1;
+	}
+	if (b1->slen > n) {
+		v = - (char) downcase (b1->data[n]);
+		if (v) return v;
+		return - (int) (UCHAR_MAX + 1);
+	}
+	return BSTR_OK;
 }
 
-/*  int bstrnicmp (const bstring b0, const bstring b1, int n)
+/*  int bstrnicmp (const_bstring b0, const_bstring b1, int n)
  *
  *  Compare two strings without differentiating between case for at most n
  *  characters.  If the position where the two strings first differ is
  *  before the nth position, the return value is the difference of the values
- *  of the characters, (taking a '\0' to be the character at the end of any
- *  bstring) otherwise 0 is returned.
+ *  of the characters, otherwise 0 is returned.  If the lengths are different
+ *  and less than n characters, then a difference from 0 is given, but if the 
+ *  first extra character is '\0', then it is taken to be the value 
+ *  UCHAR_MAX+1.
  */
-int bstrnicmp (const bstring b0, const bstring b1, int n) {
+int bstrnicmp (const_bstring b0, const_bstring b1, int n) {
 int i, v, m;
 
 	if (bdata (b0) == NULL || b0->slen < 0 || 
@@ -411,20 +640,26 @@ int i, v, m;
 
 	if (b0->data != b1->data) {
 		for (i = 0; i < m; i ++) {
-			v =  ((char) downcase (b0->data[i]));
-			v -= ((char) downcase (b1->data[i]));
-			if (v != 0) return v;
-			if (b0->data[i] == '\0') return 0;
+			v  = (char) downcase (b0->data[i]);
+			v -= (char) downcase (b1->data[i]);
+			if (v != 0) return b0->data[i] - b1->data[i];
 		}
 	}
 
-	if (n == m || b0->slen == b1->slen) return 0;
+	if (n == m || b0->slen == b1->slen) return BSTR_OK;
 
-	if (b0->slen > m) return (char) downcase (b0->data[m]);
-	return                 - (char) downcase (b1->data[m]);
+	if (b0->slen > m) {
+		v = (char) downcase (b0->data[m]);
+		if (v) return v;
+		return UCHAR_MAX + 1;
+	}
+
+	v = - (char) downcase (b1->data[m]);
+	if (v) return v;
+	return - (int) (UCHAR_MAX + 1);
 }
 
-/*  int biseqcaseless (const bstring b0, const bstring b1)
+/*  int biseqcaseless (const_bstring b0, const_bstring b1)
  *
  *  Compare two strings for equality without differentiating between case.  
  *  If the strings differ other than in case, 0 is returned, if the strings 
@@ -432,12 +667,12 @@ int i, v, m;
  *  the length of the strings are different, this function is O(1).  '\0' 
  *  termination characters are not treated in any special way.
  */
-int biseqcaseless (const bstring b0, const bstring b1) {
+int biseqcaseless (const_bstring b0, const_bstring b1) {
 int i, n;
 
 	if (bdata (b0) == NULL || b0->slen < 0 || 
 	    bdata (b1) == NULL || b1->slen < 0) return BSTR_ERR;
-	if (b0->slen != b1->slen) return 0;
+	if (b0->slen != b1->slen) return BSTR_OK;
 	if (b0->data == b1->data || b0->slen == 0) return 1;
 	for (i=0, n=b0->slen; i < n; i++) {
 		if (b0->data[i] != b1->data[i]) {
@@ -448,22 +683,141 @@ int i, n;
 	return 1;
 }
 
-/*  int biseq (const bstring b0, const bstring b1)
+/*  int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len)
+ *
+ *  Compare beginning of string b0 with a block of memory of length len 
+ *  without differentiating between case for equality.  If the beginning of b0
+ *  differs from the memory block other than in case (or if b0 is too short), 
+ *  0 is returned, if the strings are the same, 1 is returned, if there is an 
+ *  error, -1 is returned.  '\0' characters are not treated in any special 
+ *  way.
+ */
+int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) {
+int i;
+
+	if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
+		return BSTR_ERR;
+	if (b0->slen < len) return BSTR_OK;
+	if (b0->data == (const unsigned char *) blk || len == 0) return 1;
+
+	for (i = 0; i < len; i ++) {
+		if (b0->data[i] != ((const unsigned char *) blk)[i]) {
+			if (downcase (b0->data[i]) != 
+			    downcase (((const unsigned char *) blk)[i])) return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ * int bltrimws (bstring b)
+ *
+ * Delete whitespace contiguous from the left end of the string.
+ */
+int bltrimws (bstring b) {
+int i, len;
+
+	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+	for (len = b->slen, i = 0; i < len; i++) {
+		if (!wspace (b->data[i])) {
+			return bdelete (b, 0, i);
+		}
+	}
+
+	b->data[0] = (unsigned char) '\0';
+	b->slen = 0;
+	return BSTR_OK;
+}
+
+/*
+ * int brtrimws (bstring b)
+ *
+ * Delete whitespace contiguous from the right end of the string.
+ */
+int brtrimws (bstring b) {
+int i;
+
+	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+	for (i = b->slen - 1; i >= 0; i--) {
+		if (!wspace (b->data[i])) {
+			if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
+			b->slen = i + 1;
+			return BSTR_OK;
+		}
+	}
+
+	b->data[0] = (unsigned char) '\0';
+	b->slen = 0;
+	return BSTR_OK;
+}
+
+/*
+ * int btrimws (bstring b)
+ *
+ * Delete whitespace contiguous from both ends of the string.
+ */
+int btrimws (bstring b) {
+int i, j;
+
+	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+	for (i = b->slen - 1; i >= 0; i--) {
+		if (!wspace (b->data[i])) {
+			if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
+			b->slen = i + 1;
+			for (j = 0; wspace (b->data[j]); j++) {}
+			return bdelete (b, 0, j);
+		}
+	}
+
+	b->data[0] = (unsigned char) '\0';
+	b->slen = 0;
+	return BSTR_OK;
+}
+
+/*  int biseq (const_bstring b0, const_bstring b1)
  *
  *  Compare the string b0 and b1.  If the strings differ, 0 is returned, if 
  *  the strings are the same, 1 is returned, if there is an error, -1 is 
  *  returned.  If the length of the strings are different, this function is
  *  O(1).  '\0' termination characters are not treated in any special way.
  */
-int biseq (const bstring b0, const bstring b1) {
+int biseq (const_bstring b0, const_bstring b1) {
 	if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
 		b0->slen < 0 || b1->slen < 0) return BSTR_ERR;
-	if (b0->slen != b1->slen) return 0;
+	if (b0->slen != b1->slen) return BSTR_OK;
 	if (b0->data == b1->data || b0->slen == 0) return 1;
-	return !memcmp (b0->data, b1->data, b0->slen);
+	return !bstr__memcmp (b0->data, b1->data, b0->slen);
+}
+
+/*  int bisstemeqblk (const_bstring b0, const void * blk, int len)
+ *
+ *  Compare beginning of string b0 with a block of memory of length len for 
+ *  equality.  If the beginning of b0 differs from the memory block (or if b0 
+ *  is too short), 0 is returned, if the strings are the same, 1 is returned, 
+ *  if there is an error, -1 is returned.  '\0' characters are not treated in 
+ *  any special way.
+ */
+int bisstemeqblk (const_bstring b0, const void * blk, int len) {
+int i;
+
+	if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
+		return BSTR_ERR;
+	if (b0->slen < len) return BSTR_OK;
+	if (b0->data == (const unsigned char *) blk || len == 0) return 1;
+
+	for (i = 0; i < len; i ++) {
+		if (b0->data[i] != ((const unsigned char *) blk)[i]) return BSTR_OK;
+	}
+	return 1;
 }
 
-/*  int biseqcstr (const bstring b, const char *s)
+/*  int biseqcstr (const_bstring b, const char *s)
  *
  *  Compare the bstring b and char * string s.  The C string s must be '\0' 
  *  terminated at exactly the length of the bstring b, and the contents 
@@ -474,16 +828,40 @@ int biseq (const bstring b0, const bstri
  *  other.  If the strings are equal 1 is returned, if they are unequal 0 is 
  *  returned and if there is a detectable error BSTR_ERR is returned.
  */
-int biseqcstr (const bstring b, const char * s) {
+int biseqcstr (const_bstring b, const char * s) {
+int i;
+	if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
+	for (i=0; i < b->slen; i++) {
+		if (s[i] == '\0' || b->data[i] != (unsigned char) s[i]) return BSTR_OK;
+	}
+	return s[i] == '\0';
+}
+
+/*  int biseqcstrcaseless (const_bstring b, const char *s)
+ *
+ *  Compare the bstring b and char * string s.  The C string s must be '\0' 
+ *  terminated at exactly the length of the bstring b, and the contents 
+ *  between the two must be identical except for case with the bstring b with 
+ *  no '\0' characters for the two contents to be considered equal.  This is 
+ *  equivalent to the condition that their current contents will be always be 
+ *  equal ignoring case when comparing them in the same format after 
+ *  converting one or the other.  If the strings are equal, except for case, 
+ *  1 is returned, if they are unequal regardless of case 0 is returned and 
+ *  if there is a detectable error BSTR_ERR is returned.
+ */
+int biseqcstrcaseless (const_bstring b, const char * s) {
 int i;
 	if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
 	for (i=0; i < b->slen; i++) {
-		if (s[i] == '\0' || b->data[i] != s[i]) return 0;
+		if (s[i] == '\0' || 
+		    (b->data[i] != (unsigned char) s[i] && 
+		     downcase (b->data[i]) != (unsigned char) downcase (s[i])))
+			return BSTR_OK;
 	}
 	return s[i] == '\0';
 }
 
-/*  int bstrcmp (const bstring b0, const bstring b1)
+/*  int bstrcmp (const_bstring b0, const_bstring b1)
  *
  *  Compare the string b0 and b1.  If there is an error, SHRT_MIN is returned, 
  *  otherwise a value less than or greater than zero, indicating that the 
@@ -497,27 +875,27 @@ int i;
  *  standard C library counter part strcmp, the comparison does not proceed 
  *  past any '\0' termination characters encountered.
  */
-int bstrcmp (const bstring b0, const bstring b1) {
+int bstrcmp (const_bstring b0, const_bstring b1) {
 int i, v, n;
 
 	if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
 		b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
 	n = b0->slen; if (n > b1->slen) n = b1->slen;
 	if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0))
-		return 0;
+		return BSTR_OK;
 
 	for (i = 0; i < n; i ++) {
 		v = ((char) b0->data[i]) - ((char) b1->data[i]);
 		if (v != 0) return v;
-		if (b0->data[i] == '\0') return 0;
+		if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
 	}
 
 	if (b0->slen > n) return 1;
 	if (b1->slen > n) return -1;
-	return 0;
+	return BSTR_OK;
 }
 
-/*  int bstrncmp (const bstring b0, const bstring b1, int n)
+/*  int bstrncmp (const_bstring b0, const_bstring b1, int n)
  *
  *  Compare the string b0 and b1 for at most n characters.  If there is an 
  *  error, SHRT_MIN is returned, otherwise a value is returned as if b0 and 
@@ -527,7 +905,7 @@ int i, v, n;
  *  part strcmp, the comparison does not proceed past any '\0' termination 
  *  characters encountered.
  */
-int bstrncmp (const bstring b0, const bstring b1, int n) {
+int bstrncmp (const_bstring b0, const_bstring b1, int n) {
 int i, v, m;
 
 	if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
@@ -540,24 +918,24 @@ int i, v, m;
 		for (i = 0; i < m; i ++) {
 			v = ((char) b0->data[i]) - ((char) b1->data[i]);
 			if (v != 0) return v;
-			if (b0->data[i] == '\0') return 0;
+			if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
 		}
 	}
 
-	if (n == m || b0->slen == b1->slen) return 0;
+	if (n == m || b0->slen == b1->slen) return BSTR_OK;
 
 	if (b0->slen > m) return 1;
 	return -1;
 }
 
-/*  bstring bmidstr (const bstring b, int left, int len)
+/*  bstring bmidstr (const_bstring b, int left, int len)
  *
  *  Create a bstring which is the substring of b starting from position left
  *  and running for a length len (clamped by the end of the bstring b.)  If
  *  b is detectably invalid, then NULL is returned.  The section described 
  *  by (left, len) is clamped to the boundaries of b.
  */
-bstring bmidstr (const bstring b, int left, int len) {
+bstring bmidstr (const_bstring b, int left, int len) {
 
 	if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
 
@@ -586,7 +964,8 @@ int bdelete (bstring b, int pos, int len
 		pos = 0;
 	}
 
-	if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen) 
+	if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 || 
+	    b->mlen < b->slen || b->mlen <= 0) 
 		return BSTR_ERR;
 	if (len > 0 && pos < b->slen) {
 		if (pos + len >= b->slen) {
@@ -597,23 +976,24 @@ int bdelete (bstring b, int pos, int len
 			            b->slen - (pos+len));
 			b->slen -= len;
 		}
-		b->data[b->slen] = '\0';
+		b->data[b->slen] = (unsigned char) '\0';
 	}
 	return BSTR_OK;
 }
 
 /*  int bdestroy (bstring b)
  *
- *  free up the bstring.  Note that if b is detectably invalid or not writable
+ *  Free up the bstring.  Note that if b is detectably invalid or not writable
  *  then no action is performed and BSTR_ERR is returned.  Like a freed memory
  *  allocation, dereferences, writes or any other action on b after it has 
  *  been bdestroyed is undefined.
  */
 int bdestroy (bstring b) {
-	if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->data == NULL)
+	if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen ||
+	    b->data == NULL)
 		return BSTR_ERR;
 
-	free (b->data);
+	bstr__free (b->data);
 
 	/* In case there is any stale usage, there is one more chance to 
 	   notice this error. */
@@ -622,11 +1002,11 @@ int bdestroy (bstring b) {
 	b->mlen = -__LINE__;
 	b->data = NULL;
 
-	free (b);
-	return 0;
+	bstr__free (b);
+	return BSTR_OK;
 }
 
-/*  int binstr (const bstring b1, int pos, const bstring b2)
+/*  int binstr (const_bstring b1, int pos, const_bstring b2)
  *
  *  Search for the bstring b2 in b1 starting from position pos, and searching 
  *  forward.  If it is found then return with the first position where it is 
@@ -635,7 +1015,141 @@ int bdestroy (bstring b) {
  *  search algorithm.  Because of this there are many degenerate cases where 
  *  this can take much longer than it needs to.
  */
-int binstr (const bstring b1, int pos, const bstring b2) {
+int binstr (const_bstring b1, int pos, const_bstring b2) {
+int j, ii, ll, lf;
+unsigned char * d0;
+unsigned char c0;
+register unsigned char * d1;
+register unsigned char c1;
+register int i;
+
+	if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+	    b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+	if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR;
+	if (b1->slen < pos || pos < 0) return BSTR_ERR;
+	if (b2->slen == 0) return pos;
+
+	/* No space to find such a string? */
+	if ((lf = b1->slen - b2->slen + 1) <= pos) return BSTR_ERR;
+
+	/* An obvious alias case */
+	if (b1->data == b2->data && pos == 0) return 0;
+
+	i = pos;
+
+	d0 = b2->data;
+	d1 = b1->data;
+	ll = b2->slen;
+
+	/* Peel off the b2->slen == 1 case */
+	c0 = d0[0];
+	if (1 == ll) {
+		for (;i < lf; i++) if (c0 == d1[i]) return i;
+		return BSTR_ERR;
+	}
+
+	c1 = c0;
+	j = 0;
+	lf = b1->slen - 1;
+
+	ii = -1;
+	if (i < lf) do {
+		/* Unrolled current character test */
+		if (c1 != d1[i]) {
+			if (c1 != d1[1+i]) {
+				i += 2;
+				continue;
+			}
+			i++;
+		}
+
+		/* Take note if this is the start of a potential match */
+		if (0 == j) ii = i;
+
+		/* Shift the test character down by one */
+		j++;
+		i++;
+
+		/* If this isn't past the last character continue */
+		if (j < ll) {
+			c1 = d0[j];
+			continue;
+		}
+
+		N0:;
+
+		/* If no characters mismatched, then we matched */
+		if (i == ii+j) return ii;
+
+		/* Shift back to the beginning */
+		i -= j;
+		j  = 0;
+		c1 = c0;
+	} while (i < lf);
+
+	/* Deal with last case if unrolling caused a misalignment */
+	if (i == lf && ll == j+1 && c1 == d1[i]) goto N0;
+
+	return BSTR_ERR;
+}
+
+/*  int binstrr (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  backward.  If it is found then return with the first position where it is 
+ *  found, otherwise return BSTR_ERR.  Note that this is just a brute force 
+ *  string searcher that does not attempt clever things like the Boyer-Moore 
+ *  search algorithm.  Because of this there are many degenerate cases where 
+ *  this can take much longer than it needs to.
+ */
+int binstrr (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l;
+unsigned char * d0, * d1;
+
+	if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+	    b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+	if (b1->slen == pos && b2->slen == 0) return pos;
+	if (b1->slen < pos || pos < 0) return BSTR_ERR;
+	if (b2->slen == 0) return pos;
+
+	/* Obvious alias case */
+	if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0;
+
+	i = pos;
+	if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
+
+	/* If no space to find such a string then snap back */
+	if (l + 1 <= i) i = l;
+	j = 0;
+
+	d0 = b2->data;
+	d1 = b1->data;
+	l  = b2->slen;
+
+	for (;;) {
+		if (d0[j] == d1[i + j]) {
+			j ++;
+			if (j >= l) return i;
+		} else {
+			i --;
+			if (i < 0) break;
+			j=0;
+		}
+	}
+
+	return BSTR_ERR;
+}
+
+/*  int binstrcaseless (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  forward but without regard to case.  If it is found then return with the 
+ *  first position where it is found, otherwise return BSTR_ERR.  Note that 
+ *  this is just a brute force string searcher that does not attempt clever 
+ *  things like the Boyer-Moore search algorithm.  Because of this there are 
+ *  many degenerate cases where this can take much longer than it needs to.
+ */
+int binstrcaseless (const_bstring b1, int pos, const_bstring b2) {
 int j, i, l, ll;
 unsigned char * d0, * d1;
 
@@ -651,7 +1165,7 @@ unsigned char * d0, * d1;
 	if (l <= pos) return BSTR_ERR;
 
 	/* An obvious alias case */
-	if (b1->data == b2->data && pos == 0) return 0;
+	if (b1->data == b2->data && pos == 0) return BSTR_OK;
 
 	i = pos;
 	j = 0;
@@ -661,7 +1175,7 @@ unsigned char * d0, * d1;
 	ll = b2->slen;
 
 	for (;;) {
-		if (d0[j] == d1[i + j]) {
+		if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
 			j ++;
 			if (j >= ll) return i;
 		} else {
@@ -674,16 +1188,16 @@ unsigned char * d0, * d1;
 	return BSTR_ERR;
 }
 
-/*  int binstrr (const bstring b1, int pos, const bstring b2)
+/*  int binstrrcaseless (const_bstring b1, int pos, const_bstring b2)
  *
  *  Search for the bstring b2 in b1 starting from position pos, and searching 
- *  backward.  If it is found then return with the first position where it is 
- *  found, otherwise return BSTR_ERR.  Note that this is just a brute force 
- *  string searcher that does not attempt clever things like the Boyer-Moore 
- *  search algorithm.  Because of this there are many degenerate cases where 
- *  this can take much longer than it needs to.
+ *  backward but without regard to case.  If it is found then return with the 
+ *  first position where it is found, otherwise return BSTR_ERR.  Note that 
+ *  this is just a brute force string searcher that does not attempt clever 
+ *  things like the Boyer-Moore search algorithm.  Because of this there are 
+ *  many degenerate cases where this can take much longer than it needs to.
  */
-int binstrr (const bstring b1, int pos, const bstring b2) {
+int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) {
 int j, i, l;
 unsigned char * d0, * d1;
 
@@ -694,7 +1208,7 @@ unsigned char * d0, * d1;
 	if (b2->slen == 0) return pos;
 
 	/* Obvious alias case */
-	if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0;
+	if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return BSTR_OK;
 
 	i = pos;
 	if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
@@ -708,7 +1222,7 @@ unsigned char * d0, * d1;
 	l  = b2->slen;
 
 	for (;;) {
-		if (d0[j] == d1[i + j]) {
+		if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
 			j ++;
 			if (j >= l) return i;
 		} else {
@@ -721,78 +1235,96 @@ unsigned char * d0, * d1;
 	return BSTR_ERR;
 }
 
-/*  int bstrchr (const bstring b, int c)
+
+/*  int bstrchrp (const_bstring b, int c, int pos)
  *
- *  Search for the character c in b forwards from the start of the string.
+ *  Search for the character c in b forwards from the position pos 
+ *  (inclusive).
  */
-int bstrchr (const bstring b, int c) {
+int bstrchrp (const_bstring b, int c, int pos) {
 unsigned char * p;
 
-	if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR;
-	p = memchr (b->data, (unsigned char) c, b->slen);
+	if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
+	p = (unsigned char *) bstr__memchr ((b->data + pos), (unsigned char) c, (b->slen - pos));
 	if (p) return (int) (p - b->data);
 	return BSTR_ERR;
 }
 
-/*  int bstrrchr (const bstring b, int c)
+/*  int bstrrchrp (const_bstring b, int c, int pos)
  *
- *  Search for the character c in b backwards from the end of the string.
+ *  Search for the character c in b backwards from the position pos in string 
+ *  (inclusive).
  */
-int bstrrchr (const bstring b, int c) {
+int bstrrchrp (const_bstring b, int c, int pos) {
 int i;
-
-	if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR;
-	for (i=b->slen-1; i >= 0; i--) {
+ 
+	if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
+	for (i=pos; i >= 0; i--) {
 		if (b->data[i] == (unsigned char) c) return i;
 	}
 	return BSTR_ERR;
 }
 
-#define LONG_LOG_BITS_QTY (5)
+#if !defined (BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF)
+#define LONG_LOG_BITS_QTY (3)
 #define LONG_BITS_QTY (1 << LONG_LOG_BITS_QTY)
-#define LONG_TYPE unsigned long
+#define LONG_TYPE unsigned char
 
-struct charField { LONG_TYPE content[(1 << CHAR_BIT) / LONG_BITS_QTY]; };
+#define CFCLEN ((1 << CHAR_BIT) / LONG_BITS_QTY)
+struct charField { LONG_TYPE content[CFCLEN]; };
 #define testInCharField(cf,c) ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1))))
+#define setInCharField(cf,idx) { \
+	unsigned int c = (unsigned int) (idx); \
+	(cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE) (1ul << (c & (LONG_BITS_QTY-1))); \
+}
+
+#else
+
+#define CFCLEN (1 << CHAR_BIT)
+struct charField { unsigned char content[CFCLEN]; };
+#define testInCharField(cf,c) ((cf)->content[(unsigned char) (c)])
+#define setInCharField(cf,idx) (cf)->content[(unsigned int) (idx)] = ~0
+
+#endif
 
 /* Convert a bstring to charField */
-static int buildCharField (struct charField * cf, const bstring b1) {
+static int buildCharField (struct charField * cf, const_bstring b) {
 int i;
-	if (b1 == NULL || b1->data == NULL || b1->slen <= 0) return BSTR_ERR;
-	memset ((void *) &cf->content, 0, sizeof (struct charField));
-	for (i=0; i < b1->slen; i++) {
-		unsigned int c = (unsigned int) b1->data[i];
-		cf->content[c >> LONG_LOG_BITS_QTY] |= ((LONG_TYPE) 1) << (c & (LONG_BITS_QTY-1));
+	if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR;
+	memset ((void *) cf->content, 0, sizeof (struct charField));
+	for (i=0; i < b->slen; i++) {
+		setInCharField (cf, b->data[i]);
 	}
-	return 0;
+	return BSTR_OK;
 }
 
 static void invertCharField (struct charField * cf) {
 int i;
-	for (i=0; i < ((1 << CHAR_BIT) / LONG_BITS_QTY); i++) cf->content[i] = ~cf->content[i];
+	for (i=0; i < CFCLEN; i++) cf->content[i] = ~cf->content[i];
 }
 
 /* Inner engine for binchr */
 static int binchrCF (const unsigned char * data, int len, int pos, const struct charField * cf) {
 int i;
 	for (i=pos; i < len; i++) {
-		unsigned int c = (unsigned int) data[i];
+		unsigned char c = (unsigned char) data[i];
 		if (testInCharField (cf, c)) return i;
 	}
 	return BSTR_ERR;
 }
 
-/*  int binchr (const bstring b0, int pos, const bstring b1);
+/*  int binchr (const_bstring b0, int pos, const_bstring b1);
  *
  *  Search for the first position in b0 starting from pos or after, in which 
  *  one of the characters in b1 is found and return it.  If such a position 
  *  does not exist in b0, then BSTR_ERR is returned.
  */
-int binchr (const bstring b0, int pos, const bstring b1) {
+int binchr (const_bstring b0, int pos, const_bstring b1) {
 struct charField chrs;
 	if (pos < 0 || b0 == NULL || b0->data == NULL ||
 	    b0->slen <= pos) return BSTR_ERR;
-	if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
+	if (1 == b1->slen) return bstrchrp (b0, b1->data[0], pos);
+	if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
 	return binchrCF (b0->data, b0->slen, pos, &chrs);
 }
 
@@ -806,28 +1338,29 @@ int i;
 	return BSTR_ERR;
 }
 
-/*  int binchrr (const bstring b0, int pos, const bstring b1);
+/*  int binchrr (const_bstring b0, int pos, const_bstring b1);
  *
  *  Search for the last position in b0 no greater than pos, in which one of 
  *  the characters in b1 is found and return it.  If such a position does not 
  *  exist in b0, then BSTR_ERR is returned.
  */
-int binchrr (const bstring b0, int pos, const bstring b1) {
+int binchrr (const_bstring b0, int pos, const_bstring b1) {
 struct charField chrs;
-	if (pos < 0 || b0 == NULL || b0->data == NULL ||
+	if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL ||
 	    b0->slen < pos) return BSTR_ERR;
 	if (pos == b0->slen) pos--;
-	if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
+	if (1 == b1->slen) return bstrrchrp (b0, b1->data[0], pos);
+	if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
 	return binchrrCF (b0->data, pos, &chrs);
 }
 
-/*  int bninchr (const bstring b0, int pos, const bstring b1);
+/*  int bninchr (const_bstring b0, int pos, const_bstring b1);
  *
  *  Search for the first position in b0 starting from pos or after, in which 
  *  none of the characters in b1 is found and return it.  If such a position 
  *  does not exist in b0, then BSTR_ERR is returned.
  */
-int bninchr (const bstring b0, int pos, const bstring b1) {
+int bninchr (const_bstring b0, int pos, const_bstring b1) {
 struct charField chrs;
 	if (pos < 0 || b0 == NULL || b0->data == NULL || 
 	    b0->slen <= pos) return BSTR_ERR;
@@ -836,13 +1369,13 @@ struct charField chrs;
 	return binchrCF (b0->data, b0->slen, pos, &chrs);
 }
 
-/*  int bninchrr (const bstring b0, int pos, const bstring b1);
+/*  int bninchrr (const_bstring b0, int pos, const_bstring b1);
  *
  *  Search for the last position in b0 no greater than pos, in which none of 
  *  the characters in b1 is found and return it.  If such a position does not 
  *  exist in b0, then BSTR_ERR is returned.
  */
-int bninchrr (const bstring b0, int pos, const bstring b1) {
+int bninchrr (const_bstring b0, int pos, const_bstring b1) {
 struct charField chrs;
 	if (pos < 0 || b0 == NULL || b0->data == NULL || 
 	    b0->slen < pos) return BSTR_ERR;
@@ -859,44 +1392,50 @@ struct charField chrs;
  *  appended as necessary to make up the gap between the end of b0 and pos.
  *  If b1 is NULL, it behaves as if it were a 0-length string.
  */
-int bsetstr (bstring b0, int pos, const bstring b1, unsigned char fill) {
-int d;
-int newlen;
-	if (pos < 0 || b0 == NULL || b0->slen < 0 || b0->mlen < b0->slen) return BSTR_ERR;
+int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill) {
+int d, newlen;
+ptrdiff_t pd;
+bstring aux = (bstring) b1;
+
+	if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data || 
+	    b0->mlen < b0->slen || b0->mlen <= 0) return BSTR_ERR;
 	if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) return BSTR_ERR;
 
+	d = pos;
+
 	/* Aliasing case */
-	if ((b1 != NULL) && (b0->data == b1->data) && (pos < b0->slen)) {
-		bstring tmp = bstrcpy (b1);
-		if (tmp == NULL) return BSTR_ERR;
-		d = bsetstr (b0, pos, tmp, fill);
-		bdestroy (tmp);
-		return d;
+	if (NULL != aux) {
+		if ((pd = (ptrdiff_t) (b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) {
+			if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
+		}
+		d += aux->slen;
 	}
 
 	/* Increase memory size if necessary */
-	d = pos + blength (b1);
-	if (balloc (b0, d + 1) != BSTR_OK) return BSTR_ERR;
+	if (balloc (b0, d + 1) != BSTR_OK) {
+		if (aux != b1) bdestroy (aux);
+		return BSTR_ERR;
+	}
 
 	newlen = b0->slen;
 
 	/* Fill in "fill" character as necessary */
 	if (pos > newlen) {
-		int i;
-		for (i = b0->slen; i < pos; i ++) b0->data[i] = fill;
+		bstr__memset (b0->data + b0->slen, (int) fill, (size_t) (pos - b0->slen));
 		newlen = pos;
 	}
 
 	/* Copy b1 to position pos in b0. */
-	if (b1 != NULL) {
-		bBlockCopy ((char *) (b0->data + pos), (char *) b1->data, b1->slen);
+	if (aux != NULL) {
+		bBlockCopy ((char *) (b0->data + pos), (char *) aux->data, aux->slen);
+		if (aux != b1) bdestroy (aux);
 	}
 
 	/* Indicate the potentially increased size of b0 */
 	if (d > newlen) newlen = d;
 
 	b0->slen = newlen;
-	b0->data[newlen] = '\0';
+	b0->data[newlen] = (unsigned char) '\0';
 
 	return BSTR_OK;
 }
@@ -908,40 +1447,45 @@ int newlen;
  *  make up the gap between the end of b1 and pos.  Unlike bsetstr, binsert
  *  does not allow b2 to be NULL.
  */
-int binsert (bstring b1, int pos, const bstring b2, unsigned char fill) {
+int binsert (bstring b1, int pos, const_bstring b2, unsigned char fill) {
 int d, l;
+ptrdiff_t pd;
+bstring aux = (bstring) b2;
 
 	if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 || 
-	    b2->slen < 0 || b1->mlen < b1->slen) return BSTR_ERR;
+	    b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) return BSTR_ERR;
 
 	/* Aliasing case */
-	if ((unsigned int)(b2->data - b1->data) < (unsigned int)b1->slen) {
-		bstring tmp;
-		d = binsert (b1, pos, tmp = bstrcpy (b2), fill);
-		bdestroy (tmp);
-		return d;
+	if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->mlen) {
+		if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
 	}
 
 	/* Compute the two possible end pointers */
-	d = b1->slen + b2->slen;
-	l = pos + b2->slen;
+	d = b1->slen + aux->slen;
+	l = pos + aux->slen;
+	if ((d|l) < 0) return BSTR_ERR;
 
 	if (l > d) {
 		/* Inserting past the end of the string */
-		if (balloc (b1, l + 1) != BSTR_OK) return BSTR_ERR;
-		for (d = b1->slen; d < pos; d++) b1->data[d] = fill;
+		if (balloc (b1, l + 1) != BSTR_OK) {
+			if (aux != b2) bdestroy (aux);
+			return BSTR_ERR;
+		}
+		bstr__memset (b1->data + b1->slen, (int) fill, (size_t) (pos - b1->slen));
 		b1->slen = l;
 	} else {
 		/* Inserting in the middle of the string */
-		if (balloc (b1, d + 1) != BSTR_OK) return BSTR_ERR;
-		for (l = d - 1; l >= pos + b2->slen; l--) {
-			b1->data[l] = b1->data[l - b2->slen];
+		if (balloc (b1, d + 1) != BSTR_OK) {
+			if (aux != b2) bdestroy (aux);
+			return BSTR_ERR;
 		}
+		bBlockCopy (b1->data + l, b1->data + pos, d - l);
 		b1->slen = d;
 	}
-	bBlockCopy (b1->data + pos, b2->data, b2->slen);
-	b1->data[b1->slen] = '\0';
-	return 0;
+	bBlockCopy (b1->data + pos, aux->data, aux->slen);
+	b1->data[b1->slen] = (unsigned char) '\0';
+	if (aux != b2) bdestroy (aux);
+	return BSTR_OK;
 }
 
 /*  int breplace (bstring b1, int pos, int len, bstring b2, 
@@ -950,167 +1494,203 @@ int d, l;
  *  Replace a section of a string from pos for a length len with the string b2.
  *  fill is used is pos > b1->slen.
  */
-int breplace (bstring b1, int pos, int len, const bstring b2, 
+int breplace (bstring b1, int pos, int len, const_bstring b2, 
 			  unsigned char fill) {
-int ret;
-
-	if (pos < 0 || len < 0 || b1 == NULL || b2 == NULL || 
-	    b1->data == NULL || b2->data == NULL || b1->slen < 0 ||
-	    b2->slen < 0 || b1->mlen < b1->slen) return BSTR_ERR;
+int pl, ret;
+ptrdiff_t pd;
+bstring aux = (bstring) b2;
+
+	if (pos < 0 || len < 0 || (pl = pos + len) < 0 || b1 == NULL || 
+	    b2 == NULL || b1->data == NULL || b2->data == NULL || 
+	    b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen ||
+	    b1->mlen <= 0) return BSTR_ERR;
 
 	/* Straddles the end? */
-	if (pos + len >= b1->slen) {
+	if (pl >= b1->slen) {
 		if ((ret = bsetstr (b1, pos, b2, fill)) < 0) return ret;
 		if (pos + b2->slen < b1->slen) {
 			b1->slen = pos + b2->slen;
-			b1->data[b1->slen] = '\0';
+			b1->data[b1->slen] = (unsigned char) '\0';
 		}
 		return ret;
 	}
 
 	/* Aliasing case */
-	if ((unsigned int)(b2->data - b1->data) < (unsigned int)b1->slen) {
-		bstring tmp;
-		ret = breplace (b1, pos, len, tmp = bstrcpy (b2), fill);
-		bdestroy (tmp);
-		return ret;
+	if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->slen) {
+		if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
 	}
 
-	if (b2->slen > len) {
-		if (balloc (b1, b1->slen + b2->slen - len) != BSTR_OK) return BSTR_ERR;
+	if (aux->slen > len) {
+		if (balloc (b1, b1->slen + aux->slen - len) != BSTR_OK) {
+			if (aux != b2) bdestroy (aux);
+			return BSTR_ERR;
+		}
 	}
 
-	if (b2->slen != len) memmove (b1->data + pos + b2->slen, b1->data + pos + len, b1->slen - (pos + len));
-	memcpy (b1->data + pos, b2->data, b2->slen);
-	b1->slen += b2->slen - len;
-	b1->data[b1->slen] = '\0';
+	if (aux->slen != len) bstr__memmove (b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len));
+	bstr__memcpy (b1->data + pos, aux->data, aux->slen);
+	b1->slen += aux->slen - len;
+	b1->data[b1->slen] = (unsigned char) '\0';
+	if (aux != b2) bdestroy (aux);
 	return BSTR_OK;
 }
 
-/*  int bfindreplace (bstring b, const bstring find, const bstring repl, 
+/*  int bfindreplace (bstring b, const_bstring find, const_bstring repl, 
  *                    int pos)
  *
  *  Replace all occurrences of a find string with a replace string after a
  *  given point in a bstring.
  */
-int bfindreplace (bstring b, const bstring find, const bstring repl, int pos) {
+
+typedef int (*instr_fnptr) (const_bstring s1, int pos, const_bstring s2);
+
+static int findreplaceengine (bstring b, const_bstring find, const_bstring repl, int pos, instr_fnptr instr) {
 int i, ret, slen, mlen, delta, acc;
 int * d;
+int static_d[32];
+ptrdiff_t pd;
+bstring auxf = (bstring) find;
+bstring auxr = (bstring) repl;
 
 	if (b == NULL || b->data == NULL || find == NULL ||
 	    find->data == NULL || repl == NULL || repl->data == NULL || 
 	    pos < 0 || find->slen <= 0 || b->mlen < 0 || b->slen > b->mlen || 
-	    b->slen < 0 || repl->slen < 0) return BSTR_ERR;
-	if (pos > b->slen - find->slen) return 0;
+	    b->mlen <= 0 || b->slen < 0 || repl->slen < 0) return BSTR_ERR;
+	if (pos > b->slen - find->slen) return BSTR_OK;
 
 	/* Alias with find string */
-	i = find->data - b->data;
-	if (pos - find->slen < i && i < b->slen) {
-		bstring t;
-		i = bfindreplace (b, t = bstrcpy (find), repl, pos);
-		bdestroy (t);
-		return i;
+	pd = (ptrdiff_t) (find->data - b->data);
+	if ((ptrdiff_t) (pos - find->slen) < pd && pd < (ptrdiff_t) b->slen) {
+		if (NULL == (auxf = bstrcpy (find))) return BSTR_ERR;
 	}
 
 	/* Alias with repl string */
-	i = repl->data - b->data;
-	if (pos - repl->slen < i && i < b->slen) {
-		bstring t;
-		i = bfindreplace (b, find, t = bstrcpy (repl), pos);
-		bdestroy (t);
-		return i;
+	pd = (ptrdiff_t) (repl->data - b->data);
+	if ((ptrdiff_t) (pos - repl->slen) < pd && pd < (ptrdiff_t) b->slen) {
+		if (NULL == (auxr = bstrcpy (repl))) {
+			if (auxf != find) bdestroy (auxf);
+			return BSTR_ERR;
+		}
 	}
 
-	delta = find->slen - repl->slen;
+	delta = auxf->slen - auxr->slen;
 
 	/* in-place replacement since find and replace strings are of equal 
 	   length */
 	if (delta == 0) {
-		while ((pos = binstr (b, pos, find)) >= 0) {
-			memcpy (b->data + pos, repl->data, repl->slen);
-			pos += find->slen;
+		while ((pos = instr (b, pos, auxf)) >= 0) {
+			bstr__memcpy (b->data + pos, auxr->data, auxr->slen);
+			pos += auxf->slen;
 		}
-		return 0;
+		if (auxf != find) bdestroy (auxf);
+		if (auxr != repl) bdestroy (auxr);
+		return BSTR_OK;
 	}
 
-	/* shrinking replacement since find->slen > repl->slen */
+	/* shrinking replacement since auxf->slen > auxr->slen */
 	if (delta > 0) {
 		acc = 0;
 
-		while ((i = binstr (b, pos, find)) >= 0) {
-			if (acc && i > pos) {
-				memmove (b->data + pos - acc, b->data + pos, 
-				         i - pos);
-			}
-			if (repl->slen) {
-				memmove (b->data + i - acc, repl->data, 
-				         repl->slen);
-			}
+		while ((i = instr (b, pos, auxf)) >= 0) {
+			if (acc && i > pos)
+				bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
+			if (auxr->slen)
+				bstr__memcpy (b->data + i - acc, auxr->data, auxr->slen);
 			acc += delta;
-			pos = i + find->slen;
+			pos = i + auxf->slen;
 		}
 
 		if (acc) {
 			i = b->slen;
-			if (i > pos) {
-				memmove (b->data + pos - acc, b->data + pos, 
-				         i - pos);
-			}
+			if (i > pos)
+				bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
 			b->slen -= acc;
-			b->data[b->slen] = '\0';
+			b->data[b->slen] = (unsigned char) '\0';
 		}
 
-		return 0;
+		if (auxf != find) bdestroy (auxf);
+		if (auxr != repl) bdestroy (auxr);
+		return BSTR_OK;
 	}
 
 	/* expanding replacement since find->slen < repl->slen.  Its a lot 
 	   more complicated. */
 
-	mlen = 8;
-	d = (int *) malloc (sizeof (int *) * mlen);
-	if (d == NULL) return BSTR_ERR;
-	slen = 0;
+	mlen = 32;
+	d = (int *) static_d; /* Avoid malloc for trivial cases */
+	acc = slen = 0;
 
-	while ((pos = binstr (b, pos, find)) >= 0) {
-		int * t;
+	while ((pos = instr (b, pos, auxf)) >= 0) {
 		if (slen + 1 >= mlen) {
+			int sl;
+			int * t;
 			mlen += mlen;
-			t = (int *) realloc (d, sizeof (int *) * mlen);
-			if (t == NULL) {
-				free (d);
-				return BSTR_ERR;
+			sl = sizeof (int *) * mlen;
+			if (static_d == d) d = NULL;
+			if (sl < mlen || NULL == (t = (int *) bstr__realloc (d, sl))) {
+				ret = BSTR_ERR;
+				goto done;
 			}
+			if (NULL == d) bstr__memcpy (t, static_d, sizeof (static_d));
 			d = t;
 		}
 		d[slen] = pos;
 		slen++;
-		pos += find->slen;
+		acc -= delta;
+		pos += auxf->slen;
+		if (pos < 0 || acc < 0) {
+			ret = BSTR_ERR;
+			goto done;
+		}
 	}
 	d[slen] = b->slen;
 
-	acc = slen * (-delta);
 	if (BSTR_OK == (ret = balloc (b, b->slen + acc + 1))) {
 		b->slen += acc;
 		for (i = slen-1; i >= 0; i--) {
 			int s, l;
-			s = d[i] + find->slen;
+			s = d[i] + auxf->slen;
 			l = d[i+1] - s;
 			if (l) {
-				memmove (b->data + s + acc, b->data + s, l);
+				bstr__memmove (b->data + s + acc, b->data + s, l);
 			}
-			if (repl->slen) {
-				memmove (b->data + s + acc - repl->slen, 
-				         repl->data, repl->slen);
+			if (auxr->slen) {
+				bstr__memmove (b->data + s + acc - auxr->slen, 
+				         auxr->data, auxr->slen);
 			}
 			acc += delta;		
 		}
-		b->data[b->slen] = '\0';
+		b->data[b->slen] = (unsigned char) '\0';
 	}
 
-	free (d);
+	done:;
+	if (static_d == d) d = NULL;
+	bstr__free (d);
+	if (auxf != find) bdestroy (auxf);
+	if (auxr != repl) bdestroy (auxr);
 	return ret;
 }
 
+/*  int bfindreplace (bstring b, const_bstring find, const_bstring repl, 
+ *                    int pos)
+ *
+ *  Replace all occurrences of a find string with a replace string after a
+ *  given point in a bstring.
+ */
+int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos) {
+	return findreplaceengine (b, find, repl, pos, binstr);
+}
+
+/*  int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, 
+ *                    int pos)
+ *
+ *  Replace all occurrences of a find string, ignoring case, with a replace 
+ *  string after a given point in a bstring.
+ */
+int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos) {
+	return findreplaceengine (b, find, repl, pos, binstrcaseless);
+}
+
 /*  int binsertch (bstring b, int pos, int len, unsigned char fill)
  *
  *  Inserts the character fill repeatedly into b at position pos for a 
@@ -1119,42 +1699,41 @@ int * d;
  *  end of b and the position pos + len.
  */
 int binsertch (bstring b, int pos, int len, unsigned char fill) {
-int d, l;
+int d, l, i;
 
 	if (pos < 0 || b == NULL || b->slen < 0 || b->mlen < b->slen ||
-	    len < 0) return BSTR_ERR;
+	    b->mlen <= 0 || len < 0) return BSTR_ERR;
 
 	/* Compute the two possible end pointers */
 	d = b->slen + len;
 	l = pos + len;
+	if ((d|l) < 0) return BSTR_ERR;
 
 	if (l > d) {
 		/* Inserting past the end of the string */
 		if (balloc (b, l + 1) != BSTR_OK) return BSTR_ERR;
-		len += pos - b->slen;
 		pos = b->slen;
 		b->slen = l;
 	} else {
 		/* Inserting in the middle of the string */
 		if (balloc (b, d + 1) != BSTR_OK) return BSTR_ERR;
-		for (l = d - 1; l >= pos + len; l--) {
-			b->data[l] = b->data[l - len];
+		for (i = d - 1; i >= l; i--) {
+			b->data[i] = b->data[i - len];
 		}
 		b->slen = d;
 	}
 
-	d = pos + len;
-	for (l=pos; l < d; l++) b->data[l] = fill;
-	b->data[b->slen] = '\0';
-	return 0;
+	for (i=pos; i < l; i++) b->data[i] = fill;
+	b->data[b->slen] = (unsigned char) '\0';
+	return BSTR_OK;
 }
 
 /*  int bpattern (bstring b, int len)
  *
- *  Replicate the starting bstring, b, end to end repeatedly until it surpasses
- *  len characters, then chop the result to exactly len characters.  This 
- *  function operates in-place.  The function will return with BSTR_ERR if b 
- *  is NULL or of length 0, otherwise BSTR_OK is returned.
+ *  Replicate the bstring, b in place, end to end repeatedly until it 
+ *  surpasses len characters, then chop the result to exactly len characters. 
+ *  This function operates in-place.  The function will return with BSTR_ERR 
+ *  if b is NULL or of length 0, otherwise BSTR_OK is returned.
  */
 int bpattern (bstring b, int len) {
 int i, d;
@@ -1165,84 +1744,148 @@ int i, d;
 		if (d == 1) return bsetstr (b, len, NULL, b->data[0]);
 		for (i = d; i < len; i++) b->data[i] = b->data[i - d];
 	}
-	b->data[len] = '\0';
+	b->data[len] = (unsigned char) '\0';
 	b->slen = len;
-	return 0;
+	return BSTR_OK;
 }
 
-/*  bstring bgets (bNgetc getcPtr, void * parm, char terminator)
+#define BS_BUFF_SZ (1024)
+
+/*  int breada (bstring b, bNread readPtr, void * parm)
+ *
+ *  Use a finite buffer fread-like function readPtr to concatenate to the 
+ *  bstring b the entire contents of file-like source data in a roughly 
+ *  efficient way.
+ */
+int breada (bstring b, bNread readPtr, void * parm) {
+int i, l, n;
+
+	if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+	    b->mlen <= 0 || readPtr == NULL) return BSTR_ERR;
+
+	i = b->slen;
+	for (n=i+16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) {
+		if (BSTR_OK != balloc (b, n + 1)) return BSTR_ERR;
+		l = (int) readPtr ((void *) (b->data + i), 1, n - i, parm);
+		i += l;
+		b->slen = i;
+		if (i < n) break;
+	}
+
+	b->data[i] = (unsigned char) '\0';
+	return BSTR_OK;
+}
+
+/*  bstring bread (bNread readPtr, void * parm)
+ *
+ *  Use a finite buffer fread-like function readPtr to create a bstring 
+ *  filled with the entire contents of file-like source data in a roughly 
+ *  efficient way.
+ */
+bstring bread (bNread readPtr, void * parm) {
+bstring buff;
+
+	if (0 > breada (buff = bfromcstr (""), readPtr, parm)) {
+		bdestroy (buff);
+		return NULL;
+	}
+	return buff;
+}
+
+/*  int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator)
  *
  *  Use an fgetc-like single character stream reading function (getcPtr) to 
- *  obtain a sequence of characters which are concatenated into a bstring.  
- *  The stream read is terminated by the passed in terminator function.
+ *  obtain a sequence of characters which are concatenated to the end of the
+ *  bstring b.  The stream read is terminated by the passed in terminator 
+ *  parameter.
  *
  *  If getcPtr returns with a negative number, or the terminator character 
- *  (which is appended) is read, then the stream reading is halted as the 
- *  result obtained thus far is returned.  If no characters are read, or 
- *  there is some other detectable error, NULL is returned.
+ *  (which is appended) is read, then the stream reading is halted and the 
+ *  function returns with a partial result in b.  If there is an empty partial
+ *  result, 1 is returned.  If no characters are read, or there is some other 
+ *  detectable error, BSTR_ERR is returned.
  */
-bstring bgets (bNgetc getcPtr, void * parm, char terminator) {
+int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) {
 int c, d, e;
-bstring buff;
 
-	if (getcPtr == NULL) return NULL;
-	buff = bfromcstr ("");
-	if (buff == NULL) return NULL;
+	if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+	    b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
 	d = 0;
-	e = buff->mlen - 2;
+	e = b->mlen - 2;
 
 	while ((c = getcPtr (parm)) >= 0) {
 		if (d > e) {
-			buff->slen = d;
-			if (balloc (buff, d + 2) != BSTR_OK) {
-				bdestroy (buff);
-				return NULL;
-			}
-			e = buff->mlen - 2;
+			b->slen = d;
+			if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+			e = b->mlen - 2;
 		}
-		buff->data[d] = (unsigned char) c;
+		b->data[d] = (unsigned char) c;
 		d++;
 		if (c == terminator) break;
 	}
 
-	buff->data[d] = '\0';
-	buff->slen = d;
+	b->data[d] = (unsigned char) '\0';
+	b->slen = d;
 
-	if (d == 0) {
-		bdestroy (buff);
-		buff = NULL;
+	return d == 0 && c < 0;
+}
+
+/*  int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator)
+ *
+ *  Use an fgetc-like single character stream reading function (getcPtr) to 
+ *  obtain a sequence of characters which are concatenated to the end of the
+ *  bstring b.  The stream read is terminated by the passed in terminator 
+ *  parameter.
+ *
+ *  If getcPtr returns with a negative number, or the terminator character 
+ *  (which is appended) is read, then the stream reading is halted and the 
+ *  function returns with a partial result concatentated to b.  If there is 
+ *  an empty partial result, 1 is returned.  If no characters are read, or 
+ *  there is some other detectable error, BSTR_ERR is returned.
+ */
+int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) {
+int c, d, e;
+
+	if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+	    b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
+	d = b->slen;
+	e = b->mlen - 2;
+
+	while ((c = getcPtr (parm)) >= 0) {
+		if (d > e) {
+			b->slen = d;
+			if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+			e = b->mlen - 2;
+		}
+		b->data[d] = (unsigned char) c;
+		d++;
+		if (c == terminator) break;
 	}
 
-	return buff;
-}
+	b->data[d] = (unsigned char) '\0';
+	b->slen = d;
 
-#define BS_BUFF_SZ (1024)
+	return d == 0 && c < 0;
+}
 
-/*  bstring bread (bNread readPtr, void * parm)
+/*  bstring bgets (bNgetc getcPtr, void * parm, char terminator)
  *
- *  Translate a finite buffer fread-like function into something which can
- *  fill in a bstring in a roughly efficient way.
+ *  Use an fgetc-like single character stream reading function (getcPtr) to 
+ *  obtain a sequence of characters which are concatenated into a bstring.  
+ *  The stream read is terminated by the passed in terminator function.
+ *
+ *  If getcPtr returns with a negative number, or the terminator character 
+ *  (which is appended) is read, then the stream reading is halted and the 
+ *  result obtained thus far is returned.  If no characters are read, or 
+ *  there is some other detectable error, NULL is returned.
  */
-bstring bread (bNread readPtr, void * parm) {
-int i, l, n;
+bstring bgets (bNgetc getcPtr, void * parm, char terminator) {
 bstring buff;
 
-	if (readPtr == NULL) return NULL;
-	buff = bfromcstr ("");
-	if (buff == NULL) return NULL;
-
-	for (i=0, n=16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) {
-		if (BSTR_OK != balloc (buff, n + 1)) {
-			bdestroy (buff);
-			return NULL;
-		}
-		l = (int) readPtr ((void *) (buff->data + i), 1, n - i, parm);
-		i += l;
-		buff->slen = i;
-		if (i < n) break;
+	if (0 > bgetsa (buff = bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) {
+		bdestroy (buff);
+		buff = NULL;
 	}
-
-	buff->data[i] = '\0';
 	return buff;
 }
 
@@ -1264,7 +1907,7 @@ struct bStream * bsopen (bNread readPtr,
 struct bStream * s;
 
 	if (readPtr == NULL) return NULL;
-	s = (struct bStream *) malloc (sizeof (struct bStream));
+	s = (struct bStream *) bstr__alloc (sizeof (struct bStream));
 	if (s == NULL) return NULL;
 	s->parm = parm;
 	s->buff = bfromcstr ("");
@@ -1276,7 +1919,7 @@ struct bStream * s;
 
 /*  int bsbufflength (struct bStream * s, int sz)
  *
- *  Set the length of the buffer used by the bsStream.  If sz is zero, the 
+ *  Set the length of the buffer used by the bStream.  If sz is zero, the 
  *  length is not set.  This function returns with the previous length.
  */
 int bsbufflength (struct bStream * s, int sz) {
@@ -1306,7 +1949,7 @@ void * parm;
 	parm = s->parm;
 	s->parm = NULL;
 	s->isEOF = 1;
-	free (s);
+	bstr__free (s);
 	return parm;
 }
 
@@ -1318,29 +1961,30 @@ void * parm;
  *  returned, but will be retained for subsequent read operations.
  */
 int bsreadlna (bstring r, struct bStream * s, char terminator) {
-int i, l;
+int i, l, ret, rlo;
 char * b;
 struct tagbstring x;
 
-	if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0)
-		return BSTR_ERR;
+	if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 ||
+	    r->slen < 0 || r->mlen < r->slen) return BSTR_ERR;
 	l = s->buff->slen;
 	if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
 	b = (char *) s->buff->data;
 	x.data = (unsigned char *) b;
 
 	/* First check if the current buffer holds the terminator */
-	b[l] = terminator;
+	b[l] = terminator; /* Set sentinel */
 	for (i=0; b[i] != terminator; i++) ;
 	if (i < l) {
-		int ret;
 		x.slen = i + 1;
 		ret = bconcat (r, &x);
 		s->buff->slen = l;
 		if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
-		return BSTR_ERR & -(r->slen == 0);
+		return BSTR_OK;
 	}
 
+	rlo = r->slen;
+
 	/* If not then just concatenate the entire buffer to the output */
 	x.slen = l;
 	if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
@@ -1348,30 +1992,29 @@ struct tagbstring x;
 	/* Perform direct in-place reads into the destination to allow for
 	   the minimum of data-copies */
 	for (;;) {
-		balloc (r, r->slen + s->maxBuffSz + 1);
+		if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
 		b = (char *) (r->data + r->slen);
 		l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
 		if (l <= 0) {
-			r->data[r->slen] = '\0';
+			r->data[r->slen] = (unsigned char) '\0';
 			s->buff->slen = 0;
 			s->isEOF = 1;
 			/* If nothing was read return with an error message */
-			return BSTR_ERR & -(r->slen == 0);
+			return BSTR_ERR & -(r->slen == rlo);
 		}
-		b[l] = terminator;
+		b[l] = terminator; /* Set sentinel */
 		for (i=0; b[i] != terminator; i++) ;
-		if (i < l) {
-
-			/* Terminator found, push over-read back to buffer */
-			i++;
-			r->slen += i;
-			s->buff->slen = l - i;
-			memcpy (s->buff->data, b + i, l - i);
-			r->data[r->slen] = '\0';
-			return BSTR_OK;
-		}
+		if (i < l) break;
 		r->slen += l;
 	}
+
+	/* Terminator found, push over-read back to buffer */
+	i++;
+	r->slen += i;
+	s->buff->slen = l - i;
+	bstr__memcpy (s->buff->data, b + i, l - i);
+	r->data[r->slen] = (unsigned char) '\0';
+	return BSTR_OK;
 }
 
 /*  int bsreadlnsa (bstring r, struct bStream * s, bstring term)
@@ -1381,14 +2024,15 @@ struct tagbstring x;
  *  This function may read additional characters from the core stream that 
  *  are not returned, but will be retained for subsequent read operations.
  */
-int bsreadlnsa (bstring r, struct bStream * s, const bstring term) {
-int i, l;
+int bsreadlnsa (bstring r, struct bStream * s, const_bstring term) {
+int i, l, ret, rlo;
 unsigned char * b;
 struct tagbstring x;
 struct charField cf;
 
-	if (s == NULL || s->buff == NULL || r == NULL || term == NULL 
-	 || term->data == NULL || r->mlen <= 0) return BSTR_ERR;
+	if (s == NULL || s->buff == NULL || r == NULL || term == NULL ||
+	    term->data == NULL || r->mlen <= 0 || r->slen < 0 ||
+	    r->mlen < r->slen) return BSTR_ERR;
 	if (term->slen == 1) return bsreadlna (r, s, term->data[0]);
 	if (term->slen < 1 || buildCharField (&cf, term)) return BSTR_ERR;
 
@@ -1398,17 +2042,18 @@ struct charField cf;
 	x.data = b;
 
 	/* First check if the current buffer holds the terminator */
-	b[l] = term->data[0];
+	b[l] = term->data[0]; /* Set sentinel */
 	for (i=0; !testInCharField (&cf, b[i]); i++) ;
 	if (i < l) {
-		int ret;
 		x.slen = i + 1;
 		ret = bconcat (r, &x);
 		s->buff->slen = l;
 		if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
-		return BSTR_ERR & -(r->slen == 0);
+		return BSTR_OK;
 	}
 
+	rlo = r->slen;
+
 	/* If not then just concatenate the entire buffer to the output */
 	x.slen = l;
 	if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
@@ -1416,30 +2061,30 @@ struct charField cf;
 	/* Perform direct in-place reads into the destination to allow for
 	   the minimum of data-copies */
 	for (;;) {
-		balloc (r, r->slen + s->maxBuffSz + 1);
+		if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
 		b = (unsigned char *) (r->data + r->slen);
 		l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
 		if (l <= 0) {
-			r->data[r->slen] = '\0';
+			r->data[r->slen] = (unsigned char) '\0';
 			s->buff->slen = 0;
 			s->isEOF = 1;
 			/* If nothing was read return with an error message */
-			return BSTR_ERR & -(r->slen == 0);
+			return BSTR_ERR & -(r->slen == rlo);
 		}
 
-		b[l] = term->data[0];
+		b[l] = term->data[0]; /* Set sentinel */
 		for (i=0; !testInCharField (&cf, b[i]); i++) ;
-		if (i < l) {
-			/* Terminator found, push over-read back to buffer */
-			i++;
-			r->slen += i;
-			s->buff->slen = l - i;
-			memcpy (s->buff->data, b + i, l - i);
-			r->data[r->slen] = '\0';
-			return BSTR_OK;
-		}
+		if (i < l) break;
 		r->slen += l;
 	}
+
+	/* Terminator found, push over-read back to buffer */
+	i++;
+	r->slen += i;
+	s->buff->slen = l - i;
+	bstr__memcpy (s->buff->data, b + i, l - i);
+	r->data[r->slen] = (unsigned char) '\0';
+	return BSTR_OK;
 }
 
 /*  int bsreada (bstring r, struct bStream * s, int n)
@@ -1451,29 +2096,45 @@ struct charField cf;
  *  additional characters from the core stream beyond virtual stream pointer.
  */
 int bsreada (bstring r, struct bStream * s, int n) {
-int l;
+int l, ret, orslen;
 char * b;
 struct tagbstring x;
 
 	if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
-	 || r->slen < 0 || n <= 0) return BSTR_ERR;
+	 || r->slen < 0 || r->mlen < r->slen || n <= 0) return BSTR_ERR;
 
 	n += r->slen;
 	if (n <= 0) return BSTR_ERR;
 
 	l = s->buff->slen;
+
+	orslen = r->slen;
+
+	if (0 == l) {
+		if (s->isEOF) return BSTR_ERR;
+		if (r->mlen > n) {
+			l = (int) s->readFnPtr (r->data + r->slen, 1, n - r->slen, s->parm);
+			if (0 >= l || l > n - r->slen) {
+				s->isEOF = 1;
+				return BSTR_ERR;
+			}
+			r->slen += l;
+			r->data[r->slen] = (unsigned char) '\0';
+			return 0;
+		}
+	}
+
 	if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
 	b = (char *) s->buff->data;
 	x.data = (unsigned char *) b;
 
 	do {
 		if (l + r->slen >= n) {
-			int ret;
 			x.slen = n - r->slen;
 			ret = bconcat (r, &x);
 			s->buff->slen = l;
 			if (BSTR_OK == ret) bdelete (s->buff, 0, x.slen);
-			return BSTR_ERR & -(r->slen == 0);
+			return BSTR_ERR & -(r->slen == orslen);
 		}
 
 		x.slen = l;
@@ -1488,7 +2149,7 @@ struct tagbstring x;
 	if (l < 0) l = 0;
 	if (l == 0) s->isEOF = 1;
 	s->buff->slen = l;
-	return BSTR_ERR & -(r->slen == 0);
+	return BSTR_ERR & -(r->slen == orslen);
 }
 
 /*  int bsreadln (bstring r, struct bStream * s, char terminator)
@@ -1513,7 +2174,7 @@ int bsreadln (bstring r, struct bStream 
  *  This function may read additional characters from the core stream that 
  *  are not returned, but will be retained for subsequent read operations.
  */
-int bsreadlns (bstring r, struct bStream * s, const bstring term) {
+int bsreadlns (bstring r, struct bStream * s, const_bstring term) {
 	if (s == NULL || s->buff == NULL || r == NULL || term == NULL 
 	 || term->data == NULL || r->mlen <= 0) return BSTR_ERR;
 	if (term->slen == 1) return bsreadln (r, s, term->data[0]);
@@ -1539,15 +2200,15 @@ int bsread (bstring r, struct bStream * 
 	return bsreada (r, s, n);
 }
 
-/*  int bsunread (struct bStream * s, const bstring b)
+/*  int bsunread (struct bStream * s, const_bstring b)
  *
  *  Insert a bstring into the bStream at the current position.  These 
  *  characters will be read prior to those that actually come from the core 
  *  stream.
  */
-int bsunread (struct bStream * s, const bstring b) {
+int bsunread (struct bStream * s, const_bstring b) {
 	if (s == NULL || s->buff == NULL) return BSTR_ERR;
-	return binsert (s->buff, 0, b, '?');
+	return binsert (s->buff, 0, b, (unsigned char) '?');
 }
 
 /*  int bspeek (bstring r, const struct bStream * s)
@@ -1556,27 +2217,23 @@ int bsunread (struct bStream * s, const 
  *  read prior to reads from the core stream.
  */
 int bspeek (bstring r, const struct bStream * s) {
-	if (s == NULL || s->buff == NULL || r == NULL || r->slen > r->mlen || r->slen < 0) return BSTR_ERR;
-	r->slen = 0;
-	return bconcat (r, s->buff);
+	if (s == NULL || s->buff == NULL) return BSTR_ERR;
+	return bassign (r, s->buff);
 }
 
-/*  bstring bjoin (const struct bstrList * bl, const bstring sep);
+/*  bstring bjoin (const struct bstrList * bl, const_bstring sep);
  *
  *  Join the entries of a bstrList into one bstring by sequentially 
  *  concatenating them with the sep string in between.  If there is an error 
  *  NULL is returned, otherwise a bstring with the correct result is returned.
  */
-bstring bjoin (const struct bstrList * bl, const bstring sep) {
+bstring bjoin (const struct bstrList * bl, const_bstring sep) {
 bstring b;
 int i, c, v;
 
 	if (bl == NULL || bl->qty < 0) return NULL;
 	if (sep != NULL && (sep->slen < 0 || sep->data == NULL)) return NULL;
 
-	b = (bstring) malloc (sizeof (struct tagbstring));
-	if (b == NULL) return NULL; /* Out of memory */
-
 	for (i = 0, c = 1; i < bl->qty; i++) {
 		v = bl->entry[i]->slen;
 		if (v < 0) return NULL;	/* Invalid input */
@@ -1585,30 +2242,35 @@ int i, c, v;
 	}
 
 	if (sep != NULL) c += (bl->qty - 1) * sep->slen;
-	b->data = (unsigned char *) malloc (c);
-	if (b->data == NULL) return NULL;
+
+	b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+	if (NULL == b) return NULL; /* Out of memory */
+	b->data = (unsigned char *) bstr__alloc (c);
+	if (b->data == NULL) {
+		bstr__free (b);
+		return NULL;
+	}
 
 	b->mlen = c;
 	b->slen = c-1;
 
 	for (i = 0, c = 0; i < bl->qty; i++) {
 		if (i > 0 && sep != NULL) {
-			memcpy (b->data + c, sep->data, sep->slen);
+			bstr__memcpy (b->data + c, sep->data, sep->slen);
 			c += sep->slen;
 		}
 		v = bl->entry[i]->slen;
-		memcpy (b->data + c, bl->entry[i]->data, v);
+		bstr__memcpy (b->data + c, bl->entry[i]->data, v);
 		c += v;
-
 	}
-	b->data[c] = '\0';
+	b->data[c] = (unsigned char) '\0';
 	return b;
 }
 
 #define BSSSC_BUFF_LEN (256)
 
-/*  int bssplitscb (struct bStream * s, const bstring splitStr, 
- *	int (* cb) (void * parm, int ofs, const bstring entry), void * parm)
+/*  int bssplitscb (struct bStream * s, const_bstring splitStr, 
+ *	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
  *
  *  Iterate the set of disjoint sequential substrings read from a stream 
  *  divided by any of the characters in splitStr.  An empty splitStr causes 
@@ -1624,8 +2286,8 @@ int i, c, v;
  *  return with a negative value, otherwise bssplitscb will continue in an 
  *  undefined manner.
  */
-int bssplitscb (struct bStream * s, const bstring splitStr, 
-	int (* cb) (void * parm, int ofs, const bstring entry), void * parm) {
+int bssplitscb (struct bStream * s, const_bstring splitStr, 
+	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
 struct charField chrs;
 bstring buff;
 int i, p, ret;
@@ -1646,16 +2308,21 @@ int i, p, ret;
 			if (i >= buff->slen) {
 				bsreada (buff, s, BSSSC_BUFF_LEN);
 				if (i >= buff->slen) {
-					if ((ret = cb (parm, p, buff)) > 0) ret = 0;
+					if (0 < (ret = cb (parm, p, buff))) ret = 0;
 					break;
 				}
 			}
 			if (testInCharField (&chrs, buff->data[i])) {
 				struct tagbstring t;
+				unsigned char c;
+
 				blk2tbstr (t, buff->data + i + 1, buff->slen - (i + 1));
 				if ((ret = bsunread (s, &t)) < 0) break;
 				buff->slen = i;
+				c = buff->data[i];
+				buff->data[i] = (unsigned char) '\0';
 				if ((ret = cb (parm, p, buff)) < 0) break;
+				buff->data[i] = c;
 				buff->slen = 0;
 				p += i + 1;
 				i = -1;
@@ -1668,25 +2335,155 @@ int i, p, ret;
 	return ret;
 }
 
+/*  int bssplitstrcb (struct bStream * s, const_bstring splitStr, 
+ *	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings read from a stream 
+ *  divided by the entire substring splitStr.  An empty splitStr causes 
+ *  each character of the stream to be iterated.
+ *
+ *  Note: At the point of calling the cb function, the bStream pointer is 
+ *  pointed exactly at the position right after having read the split 
+ *  character.  The cb function can act on the stream by causing the bStream
+ *  pointer to move, and bssplitscb will continue by starting the next split
+ *  at the position of the pointer after the return from cb.
+ *
+ *  However, if the cb causes the bStream s to be destroyed then the cb must
+ *  return with a negative value, otherwise bssplitscb will continue in an 
+ *  undefined manner.
+ */
+int bssplitstrcb (struct bStream * s, const_bstring splitStr, 
+	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
+bstring buff;
+int i, p, ret;
+
+	if (cb == NULL || s == NULL || s->readFnPtr == NULL 
+	 || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+	if (splitStr->slen == 1) return bssplitscb (s, splitStr, cb, parm);
+
+	if (NULL == (buff = bfromcstr (""))) return BSTR_ERR;
+
+	if (splitStr->slen == 0) {
+		for (i=0; bsreada (buff, s, BSSSC_BUFF_LEN) >= 0; i++) {
+			if ((ret = cb (parm, 0, buff)) < 0) {
+				bdestroy (buff);
+				return ret;
+			}
+			buff->slen = 0;
+		}
+		return BSTR_OK;
+	} else {
+		ret = p = i = 0;
+		for (i=p=0;;) {
+			if ((ret = binstr (buff, 0, splitStr)) >= 0) {
+				struct tagbstring t;
+				blk2tbstr (t, buff->data, ret);
+				i = ret + splitStr->slen;
+				if ((ret = cb (parm, p, &t)) < 0) break;
+				p += i;
+				bdelete (buff, 0, i);
+			} else {
+				bsreada (buff, s, BSSSC_BUFF_LEN);
+				if (bseof (s)) {
+					if ((ret = cb (parm, p, buff)) > 0) ret = 0;
+					break;
+				}
+			}
+		}
+	}
+
+	bdestroy (buff);
+	return ret;
+}
+
+/*  int bstrListCreate (void)
+ *
+ *  Create a bstrList.
+ */
+struct bstrList * bstrListCreate (void) {
+struct bstrList * sl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+	if (sl) {
+		sl->entry = (bstring *) bstr__alloc (1*sizeof (bstring));
+		if (!sl->entry) {
+			bstr__free (sl);
+			sl = NULL;
+		} else {
+			sl->qty = 0;
+			sl->mlen = 1;
+		}
+	}
+	return sl;
+}
+
 /*  int bstrListDestroy (struct bstrList * sl)
  *
- *  Destroy a bstrList that has been created by bsplit or bsplits.
+ *  Destroy a bstrList that has been created by bsplit, bsplits or bstrListCreate.
  */
 int bstrListDestroy (struct bstrList * sl) {
 int i;
-	if (sl == NULL) return BSTR_ERR;
+	if (sl == NULL || sl->qty < 0) return BSTR_ERR;
 	for (i=0; i < sl->qty; i++) {
 		if (sl->entry[i]) {
 			bdestroy (sl->entry[i]);
 			sl->entry[i] = NULL;
 		}
 	}
-	sl->qty = -1;
-	free (sl);
+	sl->qty  = -1;
+	sl->mlen = -1;
+	bstr__free (sl->entry);
+	sl->entry = NULL;
+	bstr__free (sl);
+	return BSTR_OK;
+}
+
+/*  int bstrListAlloc (struct bstrList * sl, int msz)
+ *
+ *  Ensure that there is memory for at least msz number of entries for the
+ *  list.
+ */
+int bstrListAlloc (struct bstrList * sl, int msz) {
+bstring * l;
+int smsz;
+size_t nsz;
+	if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
+	if (sl->mlen >= msz) return BSTR_OK;
+	smsz = snapUpSize (msz);
+	nsz = ((size_t) smsz) * sizeof (bstring);
+	if (nsz < (size_t) smsz) return BSTR_ERR;
+	l = (bstring *) bstr__realloc (sl->entry, nsz);
+	if (!l) {
+		smsz = msz;
+		nsz = ((size_t) smsz) * sizeof (bstring);
+		l = (bstring *) bstr__realloc (sl->entry, nsz);
+		if (!l) return BSTR_ERR;
+	}
+	sl->mlen = smsz;
+	sl->entry = l;
+	return BSTR_OK;
+}
+
+/*  int bstrListAllocMin (struct bstrList * sl, int msz)
+ *
+ *  Try to allocate the minimum amount of memory for the list to include at
+ *  least msz entries or sl->qty whichever is greater.
+ */
+int bstrListAllocMin (struct bstrList * sl, int msz) {
+bstring * l;
+size_t nsz;
+	if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
+	if (msz < sl->qty) msz = sl->qty;
+	if (sl->mlen == msz) return BSTR_OK;
+	nsz = ((size_t) msz) * sizeof (bstring);
+	if (nsz < (size_t) msz) return BSTR_ERR;
+	l = (bstring *) bstr__realloc (sl->entry, nsz);
+	if (!l) return BSTR_ERR;
+	sl->mlen = msz;
+	sl->entry = l;
 	return BSTR_OK;
 }
 
-/*  int bsplitcb (const bstring str, unsigned char splitChar, int pos,
+/*  int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
  *	int (* cb) (void * parm, int ofs, int len), void * parm)
  *
  *  Iterate the set of disjoint sequential substrings over str divided by the
@@ -1701,7 +2498,7 @@ int i;
  *  cb function destroys str, then it *must* return with a negative value, 
  *  otherwise bsplitcb will continue in an undefined manner.
  */
-int bsplitcb (const bstring str, unsigned char splitChar, int pos,
+int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
 	int (* cb) (void * parm, int ofs, int len), void * parm) {
 int i, p, ret;
 
@@ -1715,11 +2512,11 @@ int i, p, ret;
 		}
 		if ((ret = cb (parm, p, i - p)) < 0) return ret;
 		p = i + 1;
-	} while (p < str->slen);
-	return 0;
+	} while (p <= str->slen);
+	return BSTR_OK;
 }
 
-/*  int bsplitscb (const bstring str, const bstring splitStr, int pos,
+/*  int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
  *	int (* cb) (void * parm, int ofs, int len), void * parm)
  *
  *  Iterate the set of disjoint sequential substrings over str divided by any 
@@ -1735,7 +2532,7 @@ int i, p, ret;
  *  cb function destroys str, then it *must* return with a negative value, 
  *  otherwise bsplitscb will continue in an undefined manner.
  */
-int bsplitscb (const bstring str, const bstring splitStr, int pos,
+int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
 	int (* cb) (void * parm, int ofs, int len), void * parm) {
 struct charField chrs;
 int i, p, ret;
@@ -1759,56 +2556,102 @@ int i, p, ret;
 		}
 		if ((ret = cb (parm, p, i - p)) < 0) return ret;
 		p = i + 1;
-	} while (p < str->slen);
-	return 0;
+	} while (p <= str->slen);
+	return BSTR_OK;
 }
 
-#include <stddef.h>
+/*  int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+ *	int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings over str divided by the 
+ *  substring splitStr.  An empty splitStr causes the whole str to be 
+ *  iterated once.
+ *
+ *  Note: Non-destructive modification of str from within the cb function 
+ *  while performing this split is not undefined.  bsplitstrcb behaves in 
+ *  sequential lock step with calls to cb.  I.e., after returning from a cb 
+ *  that return a non-negative integer, bsplitscb continues from the position 
+ *  1 character after the last detected split character and it will halt 
+ *  immediately if the length of str falls below this point.  However, if the 
+ *  cb function destroys str, then it *must* return with a negative value, 
+ *  otherwise bsplitscb will continue in an undefined manner.
+ */
+int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+	int (* cb) (void * parm, int ofs, int len), void * parm) {
+int i, p, ret;
 
-#ifdef offsetof
-#define BLE_SZ (offsetof (struct bstrList, entry))
-#else
-#define BLE_SZ (sizeof (struct bstrList))
-#endif
+	if (cb == NULL || str == NULL || pos < 0 || pos > str->slen 
+	 || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+	if (0 == splitStr->slen) {
+		for (i=pos; i < str->slen; i++) {
+			if ((ret = cb (parm, i, 1)) < 0) return ret;
+		}
+		return BSTR_OK;
+	}
+
+	if (splitStr->slen == 1) 
+		return bsplitcb (str, splitStr->data[0], pos, cb, parm);
+
+	for (i=p=pos; i <= str->slen - splitStr->slen; i++) {
+		if (0 == bstr__memcmp (splitStr->data, str->data + i, splitStr->slen)) {
+			if ((ret = cb (parm, p, i - p)) < 0) return ret;
+			i += splitStr->slen;
+			p = i;
+		}
+	}
+	if ((ret = cb (parm, p, str->slen - p)) < 0) return ret;
+	return BSTR_OK;
+}
 
 struct genBstrList {
 	bstring b;
 	struct bstrList * bl;
-	int mlen;
 };
 
 static int bscb (void * parm, int ofs, int len) {
-struct genBstrList * g = (struct genBstrList *)parm;
-	if (g->bl->qty >= g->mlen) {
-		int mlen = g->mlen * 2;
-		struct bstrList * tbl;
+struct genBstrList * g = (struct genBstrList *) parm;
+	if (g->bl->qty >= g->bl->mlen) {
+		int mlen = g->bl->mlen * 2;
+		bstring * tbl;
+
+		while (g->bl->qty >= mlen) {
+			if (mlen < g->bl->mlen) return BSTR_ERR;
+			mlen += mlen;
+		}
 
-		while (g->bl->qty >= mlen) mlen += mlen;
-		tbl = (struct bstrList *) realloc (g->bl, BLE_SZ + sizeof (bstring) * (mlen));
+		tbl = (bstring *) bstr__realloc (g->bl->entry, sizeof (bstring) * mlen);
 		if (tbl == NULL) return BSTR_ERR;
-		g->bl = tbl;
-		g->mlen = mlen;
+
+		g->bl->entry = tbl;
+		g->bl->mlen = mlen;
 	}
 
 	g->bl->entry[g->bl->qty] = bmidstr (g->b, ofs, len);
 	g->bl->qty++;
-	return 0;
+	return BSTR_OK;
 }
 
-/*  struct bstrList * bsplit (const bstring str, unsigned char splitChar)
+/*  struct bstrList * bsplit (const_bstring str, unsigned char splitChar)
  *
  *  Create an array of sequential substrings from str divided by the character
  *  splitChar.  
  */
-struct bstrList * bsplit (const bstring str, unsigned char splitChar) {
+struct bstrList * bsplit (const_bstring str, unsigned char splitChar) {
 struct genBstrList g;
 
 	if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
 
-	g.b = str;
-	g.mlen = 4;
-	g.bl = (struct bstrList *) malloc (BLE_SZ + sizeof (bstring) * g.mlen);
+	g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
 	if (g.bl == NULL) return NULL;
+	g.bl->mlen = 4;
+	g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+	if (NULL == g.bl->entry) {
+		bstr__free (g.bl);
+		return NULL;
+	}
+
+	g.b = (bstring) str;
 	g.bl->qty = 0;
 	if (bsplitcb (str, splitChar, 0, bscb, &g) < 0) {
 		bstrListDestroy (g.bl);
@@ -1817,24 +2660,58 @@ struct genBstrList g;
 	return g.bl;
 }
 
-/*  struct bstrList * bsplits (const bstring str, bstring splitStr)
+/*  struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr)
+ *
+ *  Create an array of sequential substrings from str divided by the entire
+ *  substring splitStr.
+ */
+struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) {
+struct genBstrList g;
+
+	if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
+
+	g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+	if (g.bl == NULL) return NULL;
+	g.bl->mlen = 4;
+	g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+	if (NULL == g.bl->entry) {
+		bstr__free (g.bl);
+		return NULL;
+	}
+
+	g.b = (bstring) str;
+	g.bl->qty = 0;
+	if (bsplitstrcb (str, splitStr, 0, bscb, &g) < 0) {
+		bstrListDestroy (g.bl);
+		return NULL;
+	}
+	return g.bl;
+}
+
+/*  struct bstrList * bsplits (const_bstring str, bstring splitStr)
  *
  *  Create an array of sequential substrings from str divided by any of the 
  *  characters in splitStr.  An empty splitStr causes a single entry bstrList
  *  containing a copy of str to be returned.
  */
-struct bstrList * bsplits (const bstring str, const bstring splitStr) {
+struct bstrList * bsplits (const_bstring str, const_bstring splitStr) {
 struct genBstrList g;
 
 	if (     str == NULL ||      str->slen < 0 ||      str->data == NULL ||
 	    splitStr == NULL || splitStr->slen < 0 || splitStr->data == NULL)
 		return NULL;
 
-	g.b = str;
-	g.mlen = 4;
-	g.bl = (struct bstrList *) malloc (BLE_SZ + sizeof (bstring) * g.mlen);
+	g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
 	if (g.bl == NULL) return NULL;
+	g.bl->mlen = 4;
+	g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+	if (NULL == g.bl->entry) {
+		bstr__free (g.bl);
+		return NULL;
+	}
+	g.b = (bstring) str;
 	g.bl->qty = 0;
+
 	if (bsplitscb (str, splitStr, 0, bscb, &g) < 0) {
 		bstrListDestroy (g.bl);
 		return NULL;
@@ -1842,7 +2719,7 @@ struct genBstrList g;
 	return g.bl;
 }
 
-#if defined(__TURBOC__)
+#if defined (__TURBOC__) && !defined (__BORLANDC__)
 # ifndef BSTRLIB_NOVSNP
 #  define BSTRLIB_NOVSNP
 # endif
@@ -1869,6 +2746,8 @@ extern int vsnprintf (char *buf, size_t 
 #endif
 #endif
 
+#if !defined (BSTRLIB_NOVSNP)
+
 #ifndef START_VSNBUFF
 #define START_VSNBUFF (16)
 #endif
@@ -1880,16 +2759,18 @@ extern int vsnprintf (char *buf, size_t 
    than n, then changing n to the return value will reduce the number of
    iterations required. */
 
-/*  bstring bformata (bstring b, const char * fmt, ...)
+/*  int bformata (bstring b, const char * fmt, ...)
  *
- *  Takes the same parameters as printf (), but rather than outputting results
- *  to stdio, it forms appends the results to a bstring which contains what 
- *  would have been output. Note that if there is an early generation of a 
- *  '\0' character, the bstring will be truncated to this end point.
+ *  After the first parameter, it takes the same parameters as printf (), but 
+ *  rather than outputting results to stdio, it appends the results to 
+ *  a bstring which contains what would have been output. Note that if there 
+ *  is an early generation of a '\0' character, the bstring will be truncated 
+ *  to this end point.
  */
 int bformata (bstring b, const char * fmt, ...) {
 va_list arglist;
-int n, slen, r;
+bstring buff;
+int n, r;
 
 	if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 
 	 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
@@ -1898,25 +2779,81 @@ int n, slen, r;
 	   performed using the truncating "vsnprintf" call (to avoid buffer
 	   overflows) on increasing potential sizes for the output result. */
 
-	slen = b->slen;
-	if ((n = 2*strlen (fmt)) < START_VSNBUFF) n = START_VSNBUFF;
+	if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+	if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+		n = 1;
+		if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
+	}
 
 	for (;;) {
-		if (BSTR_OK != balloc (b, n + 2 + slen)) return BSTR_ERR;
+		va_start (arglist, fmt);
+		exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+		va_end (arglist);
+
+		buff->data[n] = (unsigned char) '\0';
+		buff->slen = (int) (strlen) ((char *) buff->data);
+
+		if (buff->slen < n) break;
 
+		if (r > n) n = r; else n += n;
+
+		if (BSTR_OK != balloc (buff, n + 2)) {
+			bdestroy (buff);
+			return BSTR_ERR;
+		}
+	}
+
+	r = bconcat (b, buff);
+	bdestroy (buff);
+	return r;
+}
+
+/*  int bassignformat (bstring b, const char * fmt, ...)
+ *
+ *  After the first parameter, it takes the same parameters as printf (), but 
+ *  rather than outputting results to stdio, it outputs the results to 
+ *  the bstring parameter b. Note that if there is an early generation of a 
+ *  '\0' character, the bstring will be truncated to this end point.
+ */
+int bassignformat (bstring b, const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+	if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 
+	 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+	/* Since the length is not determinable beforehand, a search is
+	   performed using the truncating "vsnprintf" call (to avoid buffer
+	   overflows) on increasing potential sizes for the output result. */
+
+	if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+	if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+		n = 1;
+		if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
+	}
+
+	for (;;) {
 		va_start (arglist, fmt);
-		exvsnprintf (r, (char *) b->data + slen, n + 1, fmt, arglist);
+		exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
 		va_end (arglist);
 
-		b->data[n + slen] = '\0';
-		b->slen = slen + (int) (strlen) ((char *) b->data + slen);
+		buff->data[n] = (unsigned char) '\0';
+		buff->slen = (int) (strlen) ((char *) buff->data);
 
-		if (b->slen - slen < n) break;
+		if (buff->slen < n) break;
 
 		if (r > n) n = r; else n += n;
+
+		if (BSTR_OK != balloc (buff, n + 2)) {
+			bdestroy (buff);
+			return BSTR_ERR;
+		}
 	}
 
-	return 0;
+	r = bassign (b, buff);
+	bdestroy (buff);
+	return r;
 }
 
 /*  bstring bformat (const char * fmt, ...)
@@ -1937,27 +2874,82 @@ int n, r;
 	   performed using the truncating "vsnprintf" call (to avoid buffer
 	   overflows) on increasing potential sizes for the output result. */
 
-	if ((n = 2*strlen (fmt)) < START_VSNBUFF) n = START_VSNBUFF;
-	buff = bfromcstr ("");
+	if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+	if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+		n = 1;
+		if (NULL == (buff = bfromcstralloc (n + 2, ""))) return NULL;
+	}
 
 	for (;;) {
-		if (BSTR_OK != balloc (buff, n + 2)) {
-			bdestroy (buff);
-			return NULL;
-		}
-
 		va_start (arglist, fmt);
 		exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
 		va_end (arglist);
 
-		buff->data[n] = '\0';
+		buff->data[n] = (unsigned char) '\0';
 		buff->slen = (int) (strlen) ((char *) buff->data);
 
 		if (buff->slen < n) break;
 
 		if (r > n) n = r; else n += n;
+
+		if (BSTR_OK != balloc (buff, n + 2)) {
+			bdestroy (buff);
+			return NULL;
+		}
 	}
 
 	return buff;
 }
 
+/*  int bvcformata (bstring b, int count, const char * fmt, va_list arglist)
+ *
+ *  The bvcformata function formats data under control of the format control 
+ *  string fmt and attempts to append the result to b.  The fmt parameter is 
+ *  the same as that of the printf function.  The variable argument list is 
+ *  replaced with arglist, which has been initialized by the va_start macro.
+ *  The size of the output is upper bounded by count.  If the required output
+ *  exceeds count, the string b is not augmented with any contents and a value
+ *  below BSTR_ERR is returned.  If a value below -count is returned then it
+ *  is recommended that the negative of this value be used as an update to the
+ *  count in a subsequent pass.  On other errors, such as running out of 
+ *  memory, parameter errors or numeric wrap around BSTR_ERR is returned.  
+ *  BSTR_OK is returned when the output is successfully generated and 
+ *  appended to b.
+ *
+ *  Note: There is no sanity checking of arglist, and this function is
+ *  destructive of the contents of b from the b->slen point onward.  If there 
+ *  is an early generation of a '\0' character, the bstring will be truncated 
+ *  to this end point.
+ */
+int bvcformata (bstring b, int count, const char * fmt, va_list arg) {
+int n, r, l;
+
+	if (b == NULL || fmt == NULL || count <= 0 || b->data == NULL
+	 || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+	if (count > (n = b->slen + count) + 2) return BSTR_ERR;
+	if (BSTR_OK != balloc (b, n + 2)) return BSTR_ERR;
+
+	exvsnprintf (r, (char *) b->data + b->slen, count + 2, fmt, arg);
+
+	/* Did the operation complete successfully within bounds? */
+
+	if (n >= (l = b->slen + (int) (strlen) ((const char *) b->data + b->slen))) {
+		b->slen = l;
+		return BSTR_OK;
+	}
+
+	/* Abort, since the buffer was not large enough.  The return value 
+	   tries to help set what the retry length should be. */
+
+	b->data[b->slen] = '\0';
+	if (r > count+1) l = r; else {
+		l = count+count;
+		if (count > l) l = INT_MAX;
+	}
+	n = -l;
+	if (n > BSTR_ERR-1) n = BSTR_ERR-1;
+	return n;
+}
+
+#endif
diff -urp pads-1.2.orig/lib/bstring/bstrlib.h pads-1.2/lib/bstring/bstrlib.h
--- pads-1.2.orig/lib/bstring/bstrlib.h	2008-07-08 14:28:29.000000000 -0400
+++ pads-1.2/lib/bstring/bstrlib.h	2008-07-10 18:09:06.000000000 -0400
@@ -1,8 +1,8 @@
 /*
  * This source file is part of the bstring string library.  This code was
- * written by Paul Hsieh in 2002-2004, and is covered by the BSD open source 
- * license. Refer to the accompanying documentation for details on usage and 
- * license.
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source 
+ * license and the GPL. Refer to the accompanying documentation for details 
+ * on usage and license.
  */
 
 /*
@@ -21,80 +21,140 @@ extern "C" {
 #include <stdarg.h>
 #include <string.h>
 #include <limits.h>
+#include <ctype.h>
+
+#if !defined (BSTRLIB_VSNP_OK) && !defined (BSTRLIB_NOVSNP)
+# if defined (__TURBOC__) && !defined (__BORLANDC__)
+#  define BSTRLIB_NOVSNP
+# endif
+#endif
 
 #define BSTR_ERR (-1)
 #define BSTR_OK (0)
+#define BSTR_BS_BUFF_LENGTH_GET (0)
 
 typedef struct tagbstring * bstring;
+typedef const struct tagbstring * const_bstring;
 
 /* Copy functions */
 #define cstr2bstr bfromcstr
 extern bstring bfromcstr (const char * str);
+extern bstring bfromcstralloc (int mlen, const char * str);
 extern bstring blk2bstr (const void * blk, int len);
-extern char * bstr2cstr (const bstring s, char z);
+extern char * bstr2cstr (const_bstring s, char z);
 extern int bcstrfree (char * s);
-extern bstring bstrcpy (const bstring b1);
-extern int bassign (bstring a, const bstring b);
+extern bstring bstrcpy (const_bstring b1);
+extern int bassign (bstring a, const_bstring b);
+extern int bassignmidstr (bstring a, const_bstring b, int left, int len);
+extern int bassigncstr (bstring a, const char * str);
+extern int bassignblk (bstring a, const void * s, int len);
 
 /* Destroy function */
 extern int bdestroy (bstring b);
 
-/* Space allocation hinting function */
+/* Space allocation hinting functions */
 extern int balloc (bstring s, int len);
+extern int ballocmin (bstring b, int len);
 
 /* Substring extraction */
-extern bstring bmidstr (const bstring b, int left, int len);
+extern bstring bmidstr (const_bstring b, int left, int len);
 
 /* Various standard manipulations */
-extern int bconcat (bstring b0, const bstring b1);
+extern int bconcat (bstring b0, const_bstring b1);
 extern int bconchar (bstring b0, char c);
 extern int bcatcstr (bstring b, const char * s);
-extern int bcatblk (bstring b, const unsigned char * s, int len);
-extern int binsert (bstring s1, int pos, const bstring s2, unsigned char fill);
+extern int bcatblk (bstring b, const void * s, int len);
+extern int binsert (bstring s1, int pos, const_bstring s2, unsigned char fill);
 extern int binsertch (bstring s1, int pos, int len, unsigned char fill);
-extern int breplace (bstring b1, int pos, int len, const bstring b2, unsigned char fill);
+extern int breplace (bstring b1, int pos, int len, const_bstring b2, unsigned char fill);
 extern int bdelete (bstring s1, int pos, int len);
-extern int bsetstr (bstring b0, int pos, const bstring b1, unsigned char fill);
+extern int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill);
+extern int btrunc (bstring b, int n);
 
 /* Scan/search functions */
-extern int bstricmp (const bstring b0, const bstring b1);
-extern int bstrnicmp (const bstring b0, const bstring b1, int n);
-extern int biseqcaseless (const bstring b0, const bstring b1);
-extern int biseq (const bstring b0, const bstring b1);
-extern int biseqcstr (const bstring b, const char * s);
-extern int bstrcmp (const bstring b0, const bstring b1);
-extern int bstrncmp (const bstring b0, const bstring b1, int n);
-extern int binstr (const bstring s1, int pos, const bstring s2);
-extern int binstrr (const bstring s1, int pos, const bstring s2);
-extern int bstrchr (const bstring b, int c);
-extern int bstrrchr (const bstring b, int c);
-extern int binchr (const bstring b0, int pos, const bstring b1);
-extern int binchrr (const bstring b0, int pos, const bstring b1);
-extern int bninchr (const bstring b0, int pos, const bstring b1);
-extern int bninchrr (const bstring b0, int pos, const bstring b1);
-extern int bfindreplace (bstring b, const bstring find, const bstring repl, int pos);
+extern int bstricmp (const_bstring b0, const_bstring b1);
+extern int bstrnicmp (const_bstring b0, const_bstring b1, int n);
+extern int biseqcaseless (const_bstring b0, const_bstring b1);
+extern int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len);
+extern int biseq (const_bstring b0, const_bstring b1);
+extern int bisstemeqblk (const_bstring b0, const void * blk, int len);
+extern int biseqcstr (const_bstring b, const char * s);
+extern int biseqcstrcaseless (const_bstring b, const char * s);
+extern int bstrcmp (const_bstring b0, const_bstring b1);
+extern int bstrncmp (const_bstring b0, const_bstring b1, int n);
+extern int binstr (const_bstring s1, int pos, const_bstring s2);
+extern int binstrr (const_bstring s1, int pos, const_bstring s2);
+extern int binstrcaseless (const_bstring s1, int pos, const_bstring s2);
+extern int binstrrcaseless (const_bstring s1, int pos, const_bstring s2);
+extern int bstrchrp (const_bstring b, int c, int pos);
+extern int bstrrchrp (const_bstring b, int c, int pos);
+#define bstrchr(b,c) bstrchrp ((b), (c), 0)
+#define bstrrchr(b,c) bstrrchrp ((b), (c), blength(b)-1)
+extern int binchr (const_bstring b0, int pos, const_bstring b1);
+extern int binchrr (const_bstring b0, int pos, const_bstring b1);
+extern int bninchr (const_bstring b0, int pos, const_bstring b1);
+extern int bninchrr (const_bstring b0, int pos, const_bstring b1);
+extern int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos);
+extern int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos);
 
+/* List of string container functions */
 struct bstrList {
-    int qty;
-    bstring entry[1];
+    int qty, mlen;
+    bstring * entry;
 };
+extern struct bstrList * bstrListCreate (void);
+extern int bstrListDestroy (struct bstrList * sl);
+extern int bstrListAlloc (struct bstrList * sl, int msz);
+extern int bstrListAllocMin (struct bstrList * sl, int msz);
 
 /* String split and join functions */
-extern struct bstrList * bsplit (const bstring str, unsigned char splitChar);
-extern struct bstrList * bsplits (const bstring str, const bstring splitStr);
-extern bstring bjoin (const struct bstrList * bl, const bstring sep);
-extern int bstrListDestroy (struct bstrList * sl);
-extern int bsplitcb (const bstring str, unsigned char splitChar, int pos,
+extern struct bstrList * bsplit (const_bstring str, unsigned char splitChar);
+extern struct bstrList * bsplits (const_bstring str, const_bstring splitStr);
+extern struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr);
+extern bstring bjoin (const struct bstrList * bl, const_bstring sep);
+extern int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+	int (* cb) (void * parm, int ofs, int len), void * parm);
+extern int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
 	int (* cb) (void * parm, int ofs, int len), void * parm);
-extern int bsplitscb (const bstring str, const bstring splitStr, int pos,
+extern int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
 	int (* cb) (void * parm, int ofs, int len), void * parm);
 
 /* Miscellaneous functions */
 extern int bpattern (bstring b, int len);
 extern int btoupper (bstring b);
 extern int btolower (bstring b);
+extern int bltrimws (bstring b);
+extern int brtrimws (bstring b);
+extern int btrimws (bstring b);
+
+#if !defined (BSTRLIB_NOVSNP)
 extern bstring bformat (const char * fmt, ...);
 extern int bformata (bstring b, const char * fmt, ...);
+extern int bassignformat (bstring b, const char * fmt, ...);
+extern int bvcformata (bstring b, int count, const char * fmt, va_list arglist);
+
+#define bvformata(ret, b, fmt, lastarg) { \
+bstring bstrtmp_b = (b); \
+const char * bstrtmp_fmt = (fmt); \
+int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \
+	for (;;) { \
+		va_list bstrtmp_arglist; \
+		va_start (bstrtmp_arglist, lastarg); \
+		bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \
+		va_end (bstrtmp_arglist); \
+		if (bstrtmp_r >= 0) { /* Everything went ok */ \
+			bstrtmp_r = BSTR_OK; \
+			break; \
+		} else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \
+			bstrtmp_r = BSTR_ERR; \
+			break; \
+		} \
+		bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \
+	} \
+	ret = bstrtmp_r; \
+}
+
+#endif
 
 typedef int (*bNgetc) (void *parm);
 typedef size_t (* bNread) (void *buff, size_t elsize, size_t nelem, void *parm);
@@ -102,21 +162,26 @@ typedef size_t (* bNread) (void *buff, s
 /* Input functions */
 extern bstring bgets (bNgetc getcPtr, void * parm, char terminator);
 extern bstring bread (bNread readPtr, void * parm);
+extern int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator);
+extern int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator);
+extern int breada (bstring b, bNread readPtr, void * parm);
 
 /* Stream functions */
 extern struct bStream * bsopen (bNread readPtr, void * parm);
 extern void * bsclose (struct bStream * s);
 extern int bsbufflength (struct bStream * s, int sz);
 extern int bsreadln (bstring b, struct bStream * s, char terminator);
-extern int bsreadlns (bstring r, struct bStream * s, const bstring term);
+extern int bsreadlns (bstring r, struct bStream * s, const_bstring term);
 extern int bsread (bstring b, struct bStream * s, int n);
 extern int bsreadlna (bstring b, struct bStream * s, char terminator);
-extern int bsreadlnsa (bstring r, struct bStream * s, const bstring term);
+extern int bsreadlnsa (bstring r, struct bStream * s, const_bstring term);
 extern int bsreada (bstring b, struct bStream * s, int n);
-extern int bsunread (struct bStream * s, const bstring b);
+extern int bsunread (struct bStream * s, const_bstring b);
 extern int bspeek (bstring r, const struct bStream * s);
-extern int bssplitscb (struct bStream * s, const bstring splitStr, 
-	int (* cb) (void * parm, int ofs, const bstring entry), void * parm);
+extern int bssplitscb (struct bStream * s, const_bstring splitStr, 
+	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm);
+extern int bssplitstrcb (struct bStream * s, const_bstring splitStr, 
+	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm);
 extern int bseof (const struct bStream * s);
 
 struct tagbstring {
@@ -126,9 +191,9 @@ struct tagbstring {
 };
 
 /* Accessor macros */
-#define blengthe(b, e)      (((b) == (void *)0 || (b)->slen < 0) ? (unsigned int)(e) : ((b)->slen))
+#define blengthe(b, e)      (((b) == (void *)0 || (b)->slen < 0) ? (int)(e) : ((b)->slen))
 #define blength(b)          (blengthe ((b), 0))
-#define bdataofse(b, o, e)  (((b) == (void *)0 || (b)->data == (void*)0) ? (unsigned char *)(e) : ((b)->data) + (o))
+#define bdataofse(b, o, e)  (((b) == (void *)0 || (b)->data == (void*)0) ? (char *)(e) : ((char *)(b)->data) + (o))
 #define bdataofs(b, o)      (bdataofse ((b), (o), (void *)0))
 #define bdatae(b, e)        (bdataofse (b, 0, e))
 #define bdata(b)            (bdataofs (b, 0))
@@ -136,28 +201,101 @@ struct tagbstring {
 #define bchar(b, p)         bchare ((b), (p), '\0')
 
 /* Static constant string initialization macro */
-#define bsStatic(q)         {-__LINE__, sizeof(q)-1, (unsigned char *)(q)}
+#define bsStaticMlen(q,m)   {(m), (int) sizeof(q)-1, (unsigned char *) ("" q "")}
+#if defined(_MSC_VER)
+# define bsStatic(q)        bsStaticMlen(q,-32)
+#endif
+#ifndef bsStatic
+# define bsStatic(q)        bsStaticMlen(q,-__LINE__)
+#endif
+
+/* Static constant block parameter pair */
+#define bsStaticBlkParms(q) ((void *)("" q "")), ((int) sizeof(q)-1)
 
 /* Reference building macros */
 #define cstr2tbstr btfromcstr
-#define btfromcstr(t,s) {                         \
-    (t).data = (unsigned char *) (s);             \
-    (t).slen = (int) (strlen) ((char *)(t).data); \
-    (t).mlen = -1;                                \
+#define btfromcstr(t,s) {                                            \
+    (t).data = (unsigned char *) (s);                                \
+    (t).slen = ((t).data) ? ((int) (strlen) ((char *)(t).data)) : 0; \
+    (t).mlen = -1;                                                   \
 }
 #define blk2tbstr(t,s,l) {            \
+    (t).data = (unsigned char *) (s); \
     (t).slen = l;                     \
     (t).mlen = -1;                    \
-    (t).data = (unsigned char *) (s); \
+}
+#define btfromblk(t,s,l) blk2tbstr(t,s,l)
+#define bmid2tbstr(t,b,p,l) {                                                \
+    const_bstring bstrtmp_s = (b);                                           \
+    if (bstrtmp_s && bstrtmp_s->data && bstrtmp_s->slen >= 0) {              \
+        int bstrtmp_left = (p);                                              \
+        int bstrtmp_len  = (l);                                              \
+        if (bstrtmp_left < 0) {                                              \
+            bstrtmp_len += bstrtmp_left;                                     \
+            bstrtmp_left = 0;                                                \
+        }                                                                    \
+        if (bstrtmp_len > bstrtmp_s->slen - bstrtmp_left)                    \
+            bstrtmp_len = bstrtmp_s->slen - bstrtmp_left;                    \
+        if (bstrtmp_len <= 0) {                                              \
+            (t).data = (unsigned char *)"";                                  \
+            (t).slen = 0;                                                    \
+        } else {                                                             \
+            (t).data = bstrtmp_s->data + bstrtmp_left;                       \
+            (t).slen = bstrtmp_len;                                          \
+        }                                                                    \
+    } else {                                                                 \
+        (t).data = (unsigned char *)"";                                      \
+        (t).slen = 0;                                                        \
+    }                                                                        \
+    (t).mlen = -__LINE__;                                                    \
+}
+#define btfromblkltrimws(t,s,l) {                                            \
+    int bstrtmp_idx = 0, bstrtmp_len = (l);                                  \
+    unsigned char * bstrtmp_s = (s);                                         \
+    if (bstrtmp_s && bstrtmp_len >= 0) {                                     \
+        for (; bstrtmp_idx < bstrtmp_len; bstrtmp_idx++) {                   \
+            if (!isspace (bstrtmp_s[bstrtmp_idx])) break;                    \
+        }                                                                    \
+    }                                                                        \
+    (t).data = bstrtmp_s + bstrtmp_idx;                                      \
+    (t).slen = bstrtmp_len - bstrtmp_idx;                                    \
+    (t).mlen = -__LINE__;                                                    \
+}
+#define btfromblkrtrimws(t,s,l) {                                            \
+    int bstrtmp_len = (l) - 1;                                               \
+    unsigned char * bstrtmp_s = (s);                                         \
+    if (bstrtmp_s && bstrtmp_len >= 0) {                                     \
+        for (; bstrtmp_len >= 0; bstrtmp_len--) {                            \
+            if (!isspace (bstrtmp_s[bstrtmp_len])) break;                    \
+        }                                                                    \
+    }                                                                        \
+    (t).data = bstrtmp_s;                                                    \
+    (t).slen = bstrtmp_len + 1;                                              \
+    (t).mlen = -__LINE__;                                                    \
+}
+#define btfromblktrimws(t,s,l) {                                             \
+    int bstrtmp_idx = 0, bstrtmp_len = (l) - 1;                              \
+    unsigned char * bstrtmp_s = (s);                                         \
+    if (bstrtmp_s && bstrtmp_len >= 0) {                                     \
+        for (; bstrtmp_idx <= bstrtmp_len; bstrtmp_idx++) {                  \
+            if (!isspace (bstrtmp_s[bstrtmp_idx])) break;                    \
+        }                                                                    \
+        for (; bstrtmp_len >= bstrtmp_idx; bstrtmp_len--) {                  \
+            if (!isspace (bstrtmp_s[bstrtmp_len])) break;                    \
+        }                                                                    \
+    }                                                                        \
+    (t).data = bstrtmp_s + bstrtmp_idx;                                      \
+    (t).slen = bstrtmp_len + 1 - bstrtmp_idx;                                \
+    (t).mlen = -__LINE__;                                                    \
 }
 
 /* Write protection macros */
-#define bwriteprotect(t) { if ((t).mlen >=  0) (t).mlen = -1; }
-#define bwriteallow(t)   { if ((t).mlen == -1) (t).mlen = (t).slen + ((t).slen == 0); }
+#define bwriteprotect(t)     { if ((t).mlen >=  0) (t).mlen = -1; }
+#define bwriteallow(t)       { if ((t).mlen == -1) (t).mlen = (t).slen + ((t).slen == 0); }
+#define biswriteprotected(t) ((t).mlen <= 0)
 
 #ifdef __cplusplus
 }
 #endif
 
-
 #endif