219 lines
4.9 KiB
C
219 lines
4.9 KiB
C
// 19 may 2019
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "testing.h"
|
|
#include "testingpriv.h"
|
|
|
|
void testingprivInternalError(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
fprintf(stderr, "** testing internal error: ");
|
|
vfprintf(stderr, fmt, ap);
|
|
fprintf(stderr, "; aborting\n");
|
|
va_end(ap);
|
|
abort();
|
|
}
|
|
|
|
#define sharedbitsPrefix testingpriv
|
|
#include "../../sharedbits/alloc_impl.h"
|
|
#include "../../sharedbits/array_impl.h"
|
|
#undef sharedbitsPrefix
|
|
|
|
#define sharedbitsPrefix testingprivImpl
|
|
#define sharedbitsStatic static
|
|
#define sharedbitsInternalError testingprivInternalError
|
|
#include "../../sharedbits/strsafe_impl.h"
|
|
#undef sharedbitsInternalError
|
|
#undef sharedbitsStatic
|
|
#undef sharedbitsPrefix
|
|
|
|
#define sharedbitsPrefix testingpriv
|
|
#define testingprivStrncpy testingprivImplStrncpy
|
|
#include "../../sharedbits/strdup_impl.h"
|
|
#undef testingprivStrncpy
|
|
#undef sharedbitsPrefix
|
|
|
|
int testingprivVsnprintf(char *s, size_t n, const char *fmt, va_list ap)
|
|
{
|
|
int ret;
|
|
|
|
ret = testingprivImplVsnprintf(s, n, fmt, ap);
|
|
if (ret < 0)
|
|
testingprivInternalError("encoding error in vsnprintf(); this likely means your call to testingTLogf() and the like is invalid");
|
|
return ret;
|
|
}
|
|
|
|
int testingprivSnprintf(char *s, size_t n, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int ret;
|
|
|
|
va_start(ap, fmt);
|
|
ret = testingprivVsnprintf(s, n, fmt, ap);
|
|
va_end(ap);
|
|
return ret;
|
|
}
|
|
|
|
char *testingprivVsmprintf(const char *fmt, va_list ap)
|
|
{
|
|
char *s;
|
|
va_list ap2;
|
|
int n;
|
|
|
|
va_copy(ap2, ap);
|
|
n = testingprivVsnprintf(NULL, 0, fmt, ap2);
|
|
va_end(ap2);
|
|
s = (char *) testingprivAlloc((n + 1) * sizeof (char), "char[]");
|
|
testingprivVsnprintf(s, n + 1, fmt, ap);
|
|
return s;
|
|
}
|
|
|
|
char *testingprivSmprintf(const char *fmt, ...)
|
|
{
|
|
char *s;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
s = testingprivVsmprintf(fmt, ap);
|
|
va_end(ap);
|
|
return s;
|
|
}
|
|
|
|
struct testingprivOutbuf {
|
|
testingprivArray buf;
|
|
};
|
|
|
|
testingprivOutbuf *testingprivNewOutbuf(void)
|
|
{
|
|
testingprivOutbuf *o;
|
|
|
|
o = testingprivNew(testingprivOutbuf);
|
|
testingprivArrayInit(o->buf, char, 32, "testing output buffer");
|
|
return o;
|
|
}
|
|
|
|
void testingprivOutbufFree(testingprivOutbuf *o)
|
|
{
|
|
testingprivArrayFree(o->buf);
|
|
testingprivFree(o);
|
|
}
|
|
|
|
void testingprivOutbufVprintf(testingprivOutbuf *o, const char *fmt, va_list ap)
|
|
{
|
|
char *dest;
|
|
va_list ap2;
|
|
int n;
|
|
|
|
if (o == NULL) {
|
|
vprintf(fmt, ap);
|
|
return;
|
|
}
|
|
va_copy(ap2, ap);
|
|
n = testingprivVsnprintf(NULL, 0, fmt, ap2);
|
|
va_end(ap2);
|
|
// To conserve memory, we only allocate the terminating NUL once.
|
|
if (o->buf.len == 0)
|
|
dest = (char *) testingprivArrayAppend(&(o->buf), n + 1);
|
|
else {
|
|
dest = (char *) testingprivArrayAppend(&(o->buf), n);
|
|
dest--;
|
|
}
|
|
testingprivVsnprintf(dest, n + 1, fmt, ap);
|
|
}
|
|
|
|
void testingprivOutbufVprintfIndented(testingprivOutbuf *o, const char *fmt, va_list ap)
|
|
{
|
|
char *buf;
|
|
char *lineStart, *lineEnd;
|
|
const char *indent;
|
|
|
|
buf = testingprivVsmprintf(fmt, ap);
|
|
|
|
lineStart = buf;
|
|
indent = "";
|
|
for (;;) {
|
|
lineEnd = strchr(lineStart, '\n');
|
|
if (lineEnd == NULL)
|
|
break;
|
|
*lineEnd = '\0';
|
|
testingprivOutbufPrintf(o, "%s%s\n", indent, lineStart);
|
|
lineStart = lineEnd + 1;
|
|
indent = " ";
|
|
}
|
|
// and print the last line fragment, if any
|
|
if (*lineStart != '\0')
|
|
testingprivOutbufPrintf(o, "%s%s", indent, lineStart);
|
|
testingprivFree(buf);
|
|
}
|
|
|
|
void testingprivOutbufPrintf(testingprivOutbuf *o, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
testingprivOutbufVprintf(o, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
// TODO right now this assumes the last character in o before calling this is a newline
|
|
void testingprivOutbufAppendOutbuf(testingprivOutbuf *o, testingprivOutbuf *src)
|
|
{
|
|
char *buf;
|
|
size_t n;
|
|
bool hasTrailingBlankLine;
|
|
size_t trailingBlankLinePos = 0; // silence incorrect MSVC warning
|
|
char *lineStart, *lineEnd;
|
|
|
|
buf = src->buf.buf;
|
|
n = src->buf.len;
|
|
if (n == 0)
|
|
// nothing to write
|
|
return;
|
|
|
|
// strip trailing blank lines, if any
|
|
hasTrailingBlankLine = false;
|
|
if (buf[n - 1] == '\n') {
|
|
hasTrailingBlankLine = true;
|
|
while (n > 0 && buf[n - 1] == '\n')
|
|
n--;
|
|
if (n == 0) {
|
|
// the buffer only has blank lines, so just add a single newline and be done with it
|
|
// TODO verify that this is the correct behavior
|
|
testingprivOutbufPrintf(o, "\n");
|
|
return;
|
|
}
|
|
trailingBlankLinePos = n;
|
|
buf[trailingBlankLinePos] = '\0';
|
|
}
|
|
|
|
lineStart = buf;
|
|
for (;;) {
|
|
lineEnd = strchr(lineStart, '\n');
|
|
if (lineEnd == NULL) // last line
|
|
break;
|
|
*lineEnd = '\0';
|
|
testingprivOutbufPrintf(o, " %s\n", lineStart);
|
|
// be sure to restore src to its original state
|
|
*lineEnd = '\n';
|
|
lineStart = lineEnd + 1;
|
|
}
|
|
// print the last line, if any
|
|
if (*lineStart != '\0')
|
|
testingprivOutbufPrintf(o, " %s\n", lineStart);
|
|
|
|
// restore src to its original state
|
|
if (hasTrailingBlankLine)
|
|
buf[trailingBlankLinePos] = '\n';
|
|
}
|
|
|
|
const char *testingprivOutbufString(testingprivOutbuf *o)
|
|
{
|
|
if (o->buf.buf == NULL)
|
|
return "";
|
|
return o->buf.buf;
|
|
}
|