aboutsummaryrefslogtreecommitdiff
path: root/test/interop/bstraux.c
diff options
context:
space:
mode:
authorOskari Timperi <oskari.timperi@iki.fi>2017-03-18 09:19:29 +0200
committerOskari Timperi <oskari.timperi@iki.fi>2017-03-18 09:19:29 +0200
commit2c76b0da9e0aba2211d5b4a8e51c79e47ad9b6c8 (patch)
tree894753ced0495f725ad8362859f88d5b61e29eb7 /test/interop/bstraux.c
parentcec17d970b99058da216ec194d19c5421802b78e (diff)
downloadmqtt-2c76b0da9e0aba2211d5b4a8e51c79e47ad9b6c8.tar.gz
mqtt-2c76b0da9e0aba2211d5b4a8e51c79e47ad9b6c8.zip
Add big_message_test
The test tries to publish and receive 3.5 megabyte message.
Diffstat (limited to 'test/interop/bstraux.c')
-rw-r--r--test/interop/bstraux.c1161
1 files changed, 1161 insertions, 0 deletions
diff --git a/test/interop/bstraux.c b/test/interop/bstraux.c
new file mode 100644
index 0000000..ac97836
--- /dev/null
+++ b/test/interop/bstraux.c
@@ -0,0 +1,1161 @@
+
+/*
+ * This source file is part of the bstring string library. This code was
+ * written by Paul Hsieh in 2002-2015, and is covered by the BSD open source
+ * license and the GPL. Refer to the accompanying documentation for details
+ * on usage and license.
+ */
+
+/*
+ * bstraux.c
+ *
+ * This file is not necessarily part of the core bstring library itself, but
+ * is just an auxilliary module which includes miscellaneous or trivial
+ * functions.
+ */
+
+#if defined (_MSC_VER)
+# define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include "bstrlib.h"
+#include "bstraux.h"
+
+#ifndef UNUSED
+#define UNUSED(x) (void)(x)
+#endif
+
+/* 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 && b->mlen > 0)) return NULL;
+ if (n >= b->slen) return bstrcpy (b);
+ return bmidstr (b, b->slen - n, n);
+}
+
+/* bstring bHead (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 && b->mlen > 0)) return NULL;
+ if (n >= b->slen) return bstrcpy (b);
+ return bmidstr (b, 0, n);
+}
+
+/* int bFill (bstring a, char c, int len)
+ *
+ * Fill a given bstring with the character in parameter c, for a length n.
+ */
+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)
+ *
+ * Replicate the contents of b end to end n times and replace it in b.
+ */
+int bReplicate (bstring b, int n) {
+ return bpattern (b, n * b->slen);
+}
+
+/* int bReverse (bstring b)
+ *
+ * Reverse the contents of b in place.
+ */
+int bReverse (bstring b) {
+int i, n, m;
+unsigned char t;
+
+ if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__;
+ n = b->slen;
+ 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;
+}
+
+/* int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill)
+ *
+ * Insert a repeated sequence of a given character into the string at
+ * position pos for a length len.
+ */
+int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill) {
+ if (b == NULL || b->slen < 0 || b->mlen < b->slen || pos < 0 || len <= 0) return -__LINE__;
+
+ if (pos > b->slen
+ && 0 > bsetstr (b, pos, NULL, fill)) return -__LINE__;
+
+ if (0 > balloc (b, b->slen + len)) return -__LINE__;
+ 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] = (unsigned char) '\0';
+ return BSTR_OK;
+}
+
+/* int bJustifyLeft (bstring b, int space)
+ *
+ * Left justify a string.
+ */
+int bJustifyLeft (bstring b, int space) {
+int j, i, s, t;
+unsigned char c = (unsigned char) space;
+
+ if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__;
+ if (space != (int) c) return BSTR_OK;
+
+ for (s=j=i=0; i < b->slen; i++) {
+ t = s;
+ s = c != (b->data[j] = b->data[i]);
+ j += (t|s);
+ }
+ if (j > 0 && b->data[j-1] == c) j--;
+
+ b->data[j] = (unsigned char) '\0';
+ b->slen = j;
+ return BSTR_OK;
+}
+
+/* int bJustifyRight (bstring b, int width, int space)
+ *
+ * Right justify a string to within a given width.
+ */
+int bJustifyRight (bstring b, int width, int space) {
+int ret;
+ if (width <= 0) return -__LINE__;
+ if (0 > (ret = bJustifyLeft (b, space))) return ret;
+ if (b->slen <= width)
+ return bInsertChrs (b, 0, width - b->slen, (unsigned char) space, (unsigned char) space);
+ return BSTR_OK;
+}
+
+/* int bJustifyCenter (bstring b, int width, int space)
+ *
+ * Center a string's non-white space characters to within a given width by
+ * inserting whitespaces at the beginning.
+ */
+int bJustifyCenter (bstring b, int width, int space) {
+int ret;
+ if (width <= 0) return -__LINE__;
+ if (0 > (ret = bJustifyLeft (b, space))) return ret;
+ if (b->slen <= width)
+ return bInsertChrs (b, 0, (width - b->slen + 1) >> 1, (unsigned char) space, (unsigned char) space);
+ return BSTR_OK;
+}
+
+/* int bJustifyMargin (bstring b, int width, int space)
+ *
+ * Stretch a string to flush against left and right margins by evenly
+ * distributing additional white space between words. If the line is too
+ * long to be margin justified, it is left justified.
+ */
+int bJustifyMargin (bstring b, int width, int space) {
+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) {
+ c ++;
+ l += sl->entry[i]->slen;
+ }
+ }
+
+ if (l + c >= width || c < 2) {
+ bstrListDestroy (sl);
+ return bJustifyLeft (b, space);
+ }
+
+ b->slen = 0;
+ for (i=0; i < sl->qty; i++) {
+ if (sl->entry[i]->slen > 0) {
+ if (b->slen > 0) {
+ int s = (width - l + (c / 2)) / c;
+ bInsertChrs (b, b->slen, s, (unsigned char) space, (unsigned char) space);
+ l += s;
+ }
+ bconcat (b, sl->entry[i]);
+ c--;
+ if (c <= 0) break;
+ }
+ }
+
+ bstrListDestroy (sl);
+ return BSTR_OK;
+}
+
+static size_t readNothing (void *buff, size_t elsize, size_t nelem, void *parm) {
+ UNUSED(buff);
+ UNUSED(elsize);
+ UNUSED(nelem);
+ UNUSED(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 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 strnum[sizeof (b->slen) * 3 + 1];
+bstring s;
+unsigned char * buff;
+
+ if (b == NULL || b->data == NULL || b->slen < 0) return NULL;
+ 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;
+ bcstrfree ((char *) s);
+ return (char *) buff;
+}
+
+/* bstring bNetStr2Bstr (const char * buf)
+ *
+ * Convert a netstring to a bstring. See
+ * http://cr.yp.to/proto/netstrings.txt for a description of netstrings.
+ * Note that the terminating "," *must* be present, however a following '\0'
+ * is *not* required.
+ */
+bstring bNetStr2Bstr (const char * buff) {
+int i, x;
+bstring b;
+ if (buff == NULL) return NULL;
+ x = 0;
+ for (i=0; buff[i] != ':'; i++) {
+ unsigned int v = buff[i] - '0';
+ if (v > 9 || x > ((INT_MAX - (signed int)v) / 10)) return NULL;
+ x = (x * 10) + v;
+ }
+
+ /* This thing has to be properly terminated */
+ if (buff[i + 1 + x] != ',') return NULL;
+
+ if (NULL == (b = bfromcstr (""))) return NULL;
+ if (balloc (b, x + 1) != BSTR_OK) {
+ bdestroy (b);
+ return NULL;
+ }
+ memcpy (b->data, buff + i + 1, x);
+ b->data[x] = (unsigned char) '\0';
+ b->slen = x;
+ return b;
+}
+
+static char b64ETable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/* bstring bBase64Encode (const_bstring b)
+ *
+ * Generate a base64 encoding. See: RFC1341
+ */
+bstring bBase64Encode (const_bstring b) {
+int i, c0, c1, c2, c3;
+bstring out;
+
+ if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+
+ out = bfromcstr ("");
+ for (i=0; i + 2 < b->slen; i += 3) {
+ if (i && ((i % 57) == 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;
+ c2 = ((b->data[i+1] << 2) |
+ (b->data[i+2] >> 6)) & 0x3F;
+ c3 = b->data[i+2] & 0x3F;
+ if (bconchar (out, b64ETable[c0]) < 0 ||
+ bconchar (out, b64ETable[c1]) < 0 ||
+ bconchar (out, b64ETable[c2]) < 0 ||
+ bconchar (out, b64ETable[c3]) < 0) {
+ bdestroy (out);
+ return NULL;
+ }
+ }
+
+ if (i && ((i % 57) == 0)) {
+ if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) {
+ bdestroy (out);
+ return NULL;
+ }
+ }
+
+ switch (i + 2 - b->slen) {
+ case 0: c0 = b->data[i] >> 2;
+ c1 = ((b->data[i] << 4) |
+ (b->data[i+1] >> 4)) & 0x3F;
+ c2 = (b->data[i+1] << 2) & 0x3F;
+ if (bconchar (out, b64ETable[c0]) < 0 ||
+ bconchar (out, b64ETable[c1]) < 0 ||
+ bconchar (out, b64ETable[c2]) < 0 ||
+ bconchar (out, (char) '=') < 0) {
+ bdestroy (out);
+ return NULL;
+ }
+ break;
+ case 1: c0 = b->data[i] >> 2;
+ c1 = (b->data[i] << 4) & 0x3F;
+ if (bconchar (out, b64ETable[c0]) < 0 ||
+ bconchar (out, b64ETable[c1]) < 0 ||
+ bconchar (out, (char) '=') < 0 ||
+ bconchar (out, (char) '=') < 0) {
+ bdestroy (out);
+ return NULL;
+ }
+ break;
+ case 2: break;
+ }
+
+ return out;
+}
+
+#define B64_PAD (-2)
+#define B64_ERR (-1)
+
+static int base64DecodeSymbol (unsigned char alpha) {
+ if ((alpha >= 'A') && (alpha <= 'Z')) return (int)(alpha - 'A');
+ else if ((alpha >= 'a') && (alpha <= 'z'))
+ return 26 + (int)(alpha - 'a');
+ else if ((alpha >= '0') && (alpha <= '9'))
+ return 52 + (int)(alpha - '0');
+ else if (alpha == '+') return 62;
+ else if (alpha == '/') return 63;
+ else if (alpha == '=') return B64_PAD;
+ else return B64_ERR;
+}
+
+/* bstring bBase64DecodeEx (const_bstring b, int * boolTruncError)
+ *
+ * Decode a base64 block of data. All MIME headers are assumed to have been
+ * removed. See: RFC1341
+ */
+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] == '=') { /* Bad "too early" truncation */
+ if (boolTruncError) {
+ *boolTruncError = 1;
+ return out;
+ }
+ bdestroy (out);
+ return NULL;
+ }
+ v = base64DecodeSymbol (b->data[i]);
+ i++;
+ } while (v < 0);
+ c0 = (unsigned char) (v << 2);
+ do {
+ if (i >= b->slen || b->data[i] == '=') { /* Bad "too early" truncation */
+ if (boolTruncError) {
+ *boolTruncError = 1;
+ return out;
+ }
+ bdestroy (out);
+ return NULL;
+ }
+ v = base64DecodeSymbol (b->data[i]);
+ i++;
+ } while (v < 0);
+ c0 |= (unsigned char) (v >> 4);
+ 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) {
+ if (boolTruncError) {
+ *boolTruncError = 1;
+ return out;
+ }
+ bdestroy (out); /* Missing "=" at the end. */
+ return NULL;
+ }
+ return out;
+ }
+ v = base64DecodeSymbol (b->data[i]);
+ i++;
+ } while (v < 0);
+ c1 |= (unsigned char) (v >> 2);
+ 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) {
+ if (boolTruncError) {
+ *boolTruncError = 1;
+ return out;
+ }
+ bdestroy (out);
+ return NULL;
+ }
+ if (boolTruncError) *boolTruncError = 0;
+ return out;
+ }
+ v = base64DecodeSymbol (b->data[i]);
+ i++;
+ } while (v < 0);
+ c2 |= (unsigned char) (v);
+ if (bconchar (out, c0) < 0 ||
+ bconchar (out, c1) < 0 ||
+ bconchar (out, c2) < 0) {
+ if (boolTruncError) {
+ *boolTruncError = -1;
+ return out;
+ }
+ bdestroy (out);
+ return NULL;
+ }
+ }
+}
+
+#define UU_DECODE_BYTE(b) (((b) == (signed int)'`') ? 0 : (b) - (signed int)' ')
+
+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, otlen, ret, c0, c1, c2, c3, d0, d1, d2, d3;
+
+ if (len == 0) return 0;
+ llen = UU_DECODE_BYTE (s->data[ofs]);
+ ret = 0;
+
+ otlen = t->slen;
+
+ if (((unsigned) llen) > UU_MAX_LINELEN) { ret = -__LINE__;
+ goto bl;
+ }
+
+ llen += t->slen;
+
+ 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__;
+ }
+ if (t->slen < llen) { if (0 == ret) ret = -__LINE__;
+ t->slen = otlen;
+ }
+ bl:;
+ if (ret && io->badlines) {
+ (*io->badlines)++;
+ return 0;
+ }
+ return ret;
+}
+
+/* bstring bUuDecodeEx (const_bstring src, int * badlines)
+ *
+ * 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 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);
+ b = NULL;
+ }
+ bsclose (d);
+ bsclose (s);
+ 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_ENCODE_BYTE(b) (char) (((b) == 0) ? '`' : ((b) + ' '))
+
+/* 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 out;
+int i, j, jm;
+unsigned int c0, c1, c2;
+ if (src == NULL || src->slen < 0 || src->data == NULL) return NULL;
+ if ((out = bfromcstr ("")) == NULL) return NULL;
+ for (i=0; i < src->slen; i += UU_MAX_LINELEN) {
+ if ((jm = i + UU_MAX_LINELEN) > src->slen) jm = src->slen;
+ if (bconchar (out, UU_ENCODE_BYTE (jm - i)) < 0) {
+ bstrFree (out);
+ break;
+ }
+ for (j = i; j < jm; j += 3) {
+ 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;
+ }
+ }
+ if (bconchar (out, (char) '\r') < 0 || bconchar (out, (char) '\n') < 0) {
+ bstrFree (out);
+ break;
+ }
+ }
+ End:;
+ return out;
+}
+
+/* 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) {
+int i;
+bstring out;
+unsigned char c;
+
+ if (src == NULL || src->slen < 0 || src->data == NULL) return NULL;
+ if ((out = bfromcstr ("")) == NULL) return NULL;
+ 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, (char) '=')) {
+ bdestroy (out);
+ return NULL;
+ }
+ c += (unsigned char) 64;
+ }
+ if (0 > bconchar (out, c)) {
+ bdestroy (out);
+ return NULL;
+ }
+ }
+ return out;
+}
+
+/* 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
+ */
+#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++) {
+ if ('=' == (c = src->data[i])) { /* The = escape mode */
+ i++;
+ if (i >= src->slen) {
+ bdestroy (out);
+ return NULL;
+ }
+ c = (unsigned char) (src->data[i] - 64);
+ } else {
+ if ('\0' == c) {
+ bdestroy (out);
+ return NULL;
+ }
+
+ /* Extraneous CR/LFs are to be ignored. */
+ if (c == '\r' || c == '\n') continue;
+ }
+
+ 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;
+}
+
+/* int bSGMLEncode (bstring b)
+ *
+ * Change the string into a version that is quotable in SGML (HTML, XML).
+ */
+int bSGMLEncode (bstring b) {
+static struct tagbstring fr[4][2] = {
+ { bsStatic("&"), bsStatic("&amp;") },
+ { bsStatic("\""), bsStatic("&quot;") },
+ { bsStatic("<"), bsStatic("&lt;") },
+ { bsStatic(">"), bsStatic("&gt;") } };
+int i;
+ for (i = 0; i < 4; i++) {
+ int ret = bfindreplace (b, &fr[i][0], &fr[i][1], 0);
+ if (0 > ret) return ret;
+ }
+ return 0;
+}
+
+/* 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)) break;
+ c = vgetchar (vgcCtx);
+ if (EOF == c) break;
+
+ if (i+1 >= b->mlen) {
+
+ /* Double size, and deal with numeric overflows */
+
+ if (b->mlen <= INT_MAX / 2) m = b->mlen << 1;
+ else if (b->mlen <= INT_MAX - 1024) m = b->mlen + 1024;
+ else if (b->mlen <= INT_MAX - 16) m = b->mlen + 16;
+ else if (b->mlen <= INT_MAX - 1) m = b->mlen + 1;
+ else {
+ bSecureDestroy (b); /* Cleanse partial buffer */
+ return NULL;
+ }
+
+ t = bfromcstrrangealloc (b->mlen + 1, 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;
+}