From 9bec2005a1d8ad30df9fb0d79274c87393846584 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 19 May 2019 23:15:35 -0400 Subject: [PATCH] Added smprintf() and outbuf to the testingpriv.h functions, introducing a simpler outbuf API along the way. Changing the test suite to actually use this comes next. --- common/uipriv.h | 2 +- test/lib/testing.c | 1 - test/lib/testingpriv.c | 144 +++++++++++++++++++++++++++++++++++++++++ test/lib/testingpriv.h | 12 +++- 4 files changed, 156 insertions(+), 3 deletions(-) diff --git a/common/uipriv.h b/common/uipriv.h index bbea1a6d..66f1143b 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -31,7 +31,7 @@ struct uiprivArray { arr.what = whatstr; #define uiprivArrayFree(arr) \ uiprivFree(arr.buf); \ - memset(&arr, 0, sizeof (uiprivArray); + memset(&arr, 0, sizeof (uiprivArray)); #define uiprivArrayAt(arr, T, n) (((T *) (arr.buf)) + (n)) extern void *uiprivArrayAppend(uiprivArray *arr, size_t n); extern void *uiprivArrayInsertAt(uiprivArray *arr, size_t pos, size_t n); diff --git a/test/lib/testing.c b/test/lib/testing.c index c63b0b87..705f51b4 100644 --- a/test/lib/testing.c +++ b/test/lib/testing.c @@ -62,7 +62,6 @@ static void outbufVprintf(struct outbuf *o, int indent, const char *format, va_l va_copy(ap2, ap); n = testingprivVsnprintf(NULL, 0, format, ap2); - // TODO handle n < 0 case va_end(ap2); buf = testingprivNewArray(char, n + 1); testingprivVsnprintf(buf, n + 1, format, ap); diff --git a/test/lib/testingpriv.c b/test/lib/testingpriv.c index 852c25be..356ad956 100644 --- a/test/lib/testingpriv.c +++ b/test/lib/testingpriv.c @@ -139,3 +139,147 @@ char *testingprivStrdup(const char *s) strcpy(t, s); return t; } + +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, ap); + 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 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, int indent) +{ + char *buf; + size_t n; + int hasTrailingBlankLine; + size_t trailingBlankLinePos; + char *lineStart, *lineEnd; + int firstLine; + char *indentstr; + int indentoff; + + buf = src->buf.buf; + n = src->buf.len; + if (n == 0) + // nothing to write + return; + + // strip trailing blank lines, if any + hasTrailingBlankLine = 0; + if (buf[n - 1] == '\n') { + hasTrailingBlankLine = 1; + 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'; + } + + // precompute the indent string so we don't have to repeatedly print four spaces each time + // the + 4 is because lines after the first have one extra level of indent; indentoff is used to control that + indentstr = (char *) testingprivAlloc((indent * 4 + 4 + 1) * sizeof (char), "indent string"); + // the NUL was added by testingprivAlloc() + memset(indentstr, ' ', (indent * 4 + 4) * sizeof (char)); + firstLine = 1; + indentoff = 4; + + lineStart = buf; + for (;;) { + lineEnd = strchr(lineStart, '\n'); + if (lineEnd == NULL) // last line + break; + *lineEnd = '\0'; + testingprivOutbufPrintf(o, "%s%s\n", indentstr + indentoff, lineStart); + // be sure to restore src to its original state + *lineEnd = '\n'; + lineStart = lineEnd + 1; + if (firstLine) { + // subsequent lines are indented twice + firstLine = 0; + indentoff = 0; + } + } + // print the last line + testingprivOutbufPrintf(o, "%s%s\n", indentstr + indentoff, lineStart); + + testingprivFree(indentstr); + + // restore src to its original state + if (hasTrailingBlankLine) + buf[trailingBlankLinePos] = '\n'; +} diff --git a/test/lib/testingpriv.h b/test/lib/testingpriv.h index dd9c5132..6d2f59ec 100644 --- a/test/lib/testingpriv.h +++ b/test/lib/testingpriv.h @@ -26,7 +26,7 @@ struct testingprivArray { arr.what = whatstr; #define testingprivArrayFree(arr) \ testingprivFree(arr.buf); \ - memset(&arr, 0, sizeof (testingprivArray); + memset(&arr, 0, sizeof (testingprivArray)); #define testingprivArrayAt(arr, T, n) (((T *) (arr.buf)) + (n)) extern void *testingprivArrayAppend(testingprivArray *arr, size_t n); extern void *testingprivArrayInsertAt(testingprivArray *arr, size_t pos, size_t n); @@ -38,3 +38,13 @@ extern void testingprivArrayQsort(testingprivArray *arr, int (*compare)(const vo extern int testingprivVsnprintf(char *s, size_t n, const char *format, va_list ap); extern int testingprivSnprintf(char *s, size_t n, const char *format, ...); extern char *testingprivStrdup(const char *s); +extern char *testingprivVsmprintf(const char *fmt, va_list ap); +extern char *testingprivSmprintf(const char *fmt, ...); + +// a testingprivOutbuf of NULL writes directly to stdout +typedef struct testingprivOutbuf testingprivOutbuf; +extern testingprivOutbuf *testingprivNewOutbuf(void); +extern void testingprivOutbufFree(testingprivOutbuf *o); +extern void testingprivOutbufVprintf(testingprivOutbuf *o, const char *fmt, va_list ap); +extern void testingprivOutbufPrintf(testingprivOutbuf *o, const char *fmt, ...); +extern void testingprivOutbufAppendOutbuf(testingprivOutbuf *o, testingprivOutbuf *src, int indent);