aboutsummaryrefslogtreecommitdiff
path: root/test
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
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')
-rw-r--r--test/interop/CMakeLists.txt5
-rw-r--r--test/interop/big_message_test.c59
-rw-r--r--test/interop/bstraux.c1161
-rw-r--r--test/interop/bstraux.h115
4 files changed, 1340 insertions, 0 deletions
diff --git a/test/interop/CMakeLists.txt b/test/interop/CMakeLists.txt
index d4b43d7..b06e28b 100644
--- a/test/interop/CMakeLists.txt
+++ b/test/interop/CMakeLists.txt
@@ -20,3 +20,8 @@ ADD_INTEROP_TEST(dollar_topics_test)
ADD_INTEROP_TEST(username_and_password_test)
ADD_INTEROP_TEST(ping_test)
ADD_INTEROP_TEST(unsubscribe_test)
+ADD_INTEROP_TEST(big_message_test)
+
+ADD_LIBRARY(bstraux STATIC bstraux.c)
+TARGET_INCLUDE_DIRECTORIES(bstraux PUBLIC ${PROJECT_SOURCE_DIR}/src/lib/bstrlib)
+TARGET_LINK_LIBRARIES(big_message_test PRIVATE bstraux)
diff --git a/test/interop/big_message_test.c b/test/interop/big_message_test.c
new file mode 100644
index 0000000..16cee84
--- /dev/null
+++ b/test/interop/big_message_test.c
@@ -0,0 +1,59 @@
+#include "greatest.h"
+#include "testclient.h"
+#include "cleanup.c"
+#include "topics.c"
+#include <bstrlib/bstrlib.h>
+#include "bstraux.h"
+
+static const struct tagbstring message = bsStatic(
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eu elit vel nisl fringilla ornare. Vestibulum eget sem lobortis, molestie velit in, gravida turpis. Donec ac sapien eu neque pellentesque dictum. Maecenas sed malesuada augue, nec ullamcorper libero. Donec consectetur sit amet orci non viverra. Morbi pharetra, urna ac luctus consequat, nibh urna semper metus, nec consectetur eros sapien in lorem. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce elit magna, fringilla vel velit ac, finibus interdum nibh. Donec sit amet volutpat elit. Sed sodales finibus nisl, ut vulputate tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vel egestas tellus. Aliquam eget orci eget tortor porttitor ullamcorper in vel nulla. Cras facilisis tristique turpis vel molestie. Quisque suscipit orci orci, et convallis est eleifend sit amet."
+"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In hac habitasse platea dictumst. Donec auctor ante odio, vitae tristique nisi egestas a. Suspendisse sit amet fermentum libero, viverra tempor neque. Nunc eleifend quam ac lacus ullamcorper fermentum. Integer lorem turpis, lobortis eget risus nec, auctor convallis sapien. In laoreet mauris at mi vehicula bibendum. Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+"Quisque commodo nisi vel tellus sodales, nec laoreet arcu gravida. Mauris vitae ligula nisl. Maecenas in euismod odio, vel vulputate arcu. Mauris vehicula tortor nec tempus euismod. Maecenas at tortor in libero pretium consequat a sed augue. Phasellus tortor erat, hendrerit id placerat id, pulvinar eget lacus. Curabitur rhoncus lobortis augue, hendrerit sodales tellus faucibus at. Donec a eros tellus. Sed at urna a lectus scelerisque lobortis."
+"Duis accumsan ut augue sit amet suscipit. Cras tincidunt quam elementum magna faucibus eleifend. Etiam magna elit, commodo a tortor tempus, tempor vestibulum lorem. Nullam volutpat, libero a semper porttitor, neque turpis auctor augue, ut consequat diam nunc non tellus. Morbi nec varius ipsum, at imperdiet nisl. Fusce a est leo. Sed vitae turpis ligula. Vivamus eget eros id magna tincidunt consequat ut vel lorem. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam consectetur in tellus sit amet blandit. Cras cursus dictum ex, at iaculis sem ultricies quis. Fusce vitae pretium tellus, non cursus sem. Suspendisse ac dui eu quam semper eleifend ac sit amet orci. Nunc a nibh felis. Vivamus porta fermentum diam, vel commodo sem tincidunt ac."
+"Nam dapibus, tellus nec pharetra efficitur, velit mauris faucibus nulla, ac sodales enim ex interdum tortor. Sed eget metus quis dolor euismod elementum vitae non felis. Nullam gravida diam sit amet suscipit iaculis. Quisque vehicula maximus lorem non volutpat. Vestibulum nec dui eu neque sodales finibus. Pellentesque eleifend fermentum erat, a tincidunt nisl luctus ultricies. Aliquam malesuada enim metus, nec pharetra orci dictum id. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In neque urna, vehicula nec ante vel, porta dignissim lorem. Duis fringilla arcu nec tellus lacinia facilisis."
+);
+
+TEST big_message_test()
+{
+ TestClient *client;
+ bstring encodedMessage;
+ bstring fullMessage;
+ int need = 1024 * 1024 * 3.5;
+
+ fullMessage = bstrcpy(&message);
+ bpattern(fullMessage, need);
+
+ encodedMessage = bBase64Encode(fullMessage);
+
+ printf("ENCODED MESSAGE SIZE %d\n", blength(encodedMessage));
+
+ client = TestClientNew("clienta");
+ ASSERT(TestClientConnect(client, "localhost", 1883, 60, 1));
+ ASSERT(TestClientSubscribe(client, topics[0], 1));
+ ASSERT(TestClientPublish(client, 1, 0, topics[0], bdata(encodedMessage)));
+ ASSERT(TestClientWait(client, 2000));
+ TestClientDisconnect(client);
+
+ ASSERT_EQ(1, TestClientMessageCount(client));
+ ASSERT_EQ(blength(encodedMessage), SIMPLEQ_FIRST(&client->messages)->size);
+ ASSERT_MEM_EQ(bdata(encodedMessage),
+ SIMPLEQ_FIRST(&client->messages)->data,
+ blength(encodedMessage));
+
+ TestClientFree(client);
+
+ bdestroy(encodedMessage);
+ bdestroy(fullMessage);
+
+ PASS();
+}
+
+GREATEST_MAIN_DEFS();
+
+int main(int argc, char **argv)
+{
+ GREATEST_MAIN_BEGIN();
+ cleanup();
+ RUN_TEST(big_message_test);
+ GREATEST_MAIN_END();
+}
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;
+}
diff --git a/test/interop/bstraux.h b/test/interop/bstraux.h
new file mode 100644
index 0000000..9f30e3c
--- /dev/null
+++ b/test/interop/bstraux.h
@@ -0,0 +1,115 @@
+/*
+ * 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.h
+ *
+ * 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.
+ */
+
+#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 versions of Bstrlib */
+#if !defined(BSTRLIB_REDUCE_NAMESPACE_POLLUTION)
+#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 bCatCstr(b,s) ((bcatcstr)((b), (s)))
+#define bCatBlk(b,s,len) ((bcatblk)((b),(s),(len)))
+#define bCatStatic(b,s) bcatStatic(b,s)
+#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 bBase64Decode(b) (bBase64DecodeEx ((b), NULL))
+#define bUuDecode(b) (bUuDecodeEx ((b), NULL))
+#endif
+
+/* Unusual functions */
+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);
+extern int bJustifyRight (bstring b, int width, int space);
+extern int bJustifyMargin (bstring b, int width, int space);
+extern int bJustifyCenter (bstring b, int width, int space);
+
+/* Esoteric standards specific functions */
+extern char * bStr2NetStr (const_bstring b);
+extern bstring bNetStr2Bstr (const char * buf);
+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);
+extern int bSGMLEncode (bstring b);
+
+/* 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
+}
+#endif
+
+#endif