changeset 293:146cfd1dc744

Merged.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 22 Feb 2016 16:02:25 +0200
parents 54032f17c7e5 (current diff) 01e7edd68075 (diff)
children 3835423074c0
files tests.c
diffstat 3 files changed, 238 insertions(+), 156 deletions(-) [+]
line wrap: on
line diff
--- a/tests.c	Mon Feb 22 16:01:04 2016 +0200
+++ b/tests.c	Mon Feb 22 16:02:25 2016 +0200
@@ -5,6 +5,7 @@
 #include "th_crypto.h"
 
 #define SET_BUF_SIZE 128
+#define SET_MAX_TESTS 64
 #define SET_SENTINEL_BYTE 0x0e5
 
 
@@ -12,7 +13,8 @@
 char *test_str_header = NULL,
      *test_str_res = NULL;
 
-int tests_failed, tests_passed, tests_total, tests_set;
+int tests_failed, tests_passed, tests_total, tests_sets, tests_nenabled;
+int tests_enabled[SET_MAX_TESTS];
 
 char buf1[SET_BUF_SIZE+2], buf2[SET_BUF_SIZE+2];
 
@@ -22,6 +24,7 @@
 {
     { 0, '?', "help",       "Show this help", OPT_NONE },
     { 1, 'v', "verbose",    "Be more verbose", OPT_NONE },
+    { 2, 't', "tests",      "Perform tests -t <set>[,<set2>..]", OPT_ARGREQ },
 };
 
 static const int arg_nopts = sizeof(arg_opts) / sizeof(arg_opts[0]);
@@ -47,6 +50,40 @@
         th_verbosityLevel++;
         break;
 
+    case 2:
+        {
+            BOOL ret = TRUE;
+            char *pos, *pstr, *next;
+            pos = pstr = th_strdup(optArg);
+            memset(tests_enabled, 0, sizeof(tests_enabled));
+
+            do {
+                next = strchr(pos, ',');
+                if (next != NULL)
+                    *next = 0;
+
+                char *tmp = th_strdup_trim(pos, TH_TRIM_BOTH);
+                if (tmp != NULL)
+                {
+                    int val = atoi(tmp);
+                    if (val > 0 && val <= SET_MAX_TESTS)
+                        tests_enabled[val] = 1;
+                    else
+                    {
+                        THERR("Invalid test number #%d, out of range [%d .. %d]\n", val, 1, SET_MAX_TESTS);
+                        ret = FALSE;
+                    }
+                    th_free(tmp);
+                }
+
+                if (next != NULL)
+                    pos = next + 1;
+            } while (next != NULL);
+            th_free(pstr);
+            return ret;
+        }
+        break;
+
     default:
         THERR("Unknown option '%s'.\n", currArg);
         return FALSE;
@@ -133,7 +170,7 @@
 
     test_result_msg(ret1 == ret2, "retval mismatch %d != %d", ret1, ret2);
     test_result_msg(strcmp(buf1, buf2) == 0, "result mismatch '%s' != '%s'", buf1, buf2);
-    
+
     test_result_msg((unsigned char) buf1[len] == SET_SENTINEL_BYTE, "buffer #1 overflow, sentinel 0x%02x", buf1[len]);
     test_result_msg((unsigned char) buf2[len] == SET_SENTINEL_BYTE, "buffer #2 overflow, sentinel 0x%02x", buf2[len]);
 
@@ -160,69 +197,24 @@
 }
 
 
-void tests_header(const char *str)
+BOOL test_set_start(const char *str)
 {
-    THPRINT(1,
-        "======================================================\n"
-        " Set #%d : %s tests\n"
-        "======================================================\n",
-        ++tests_set,
-        str);
+    if (tests_enabled[tests_sets++])
+    {
+        tests_nenabled++;
+        THPRINT(1,
+            "======================================================\n"
+            " Set #%d : %s tests\n"
+            "======================================================\n",
+            tests_sets, str);
+
+        return TRUE;
+    }
+    else
+        return FALSE;
 }
 
 
-int main(int argc, char *argv[])
-{
-    //
-    // Initialization
-    //
-    th_init("th-test", "th-libs unit tests", "0.0.1", NULL, NULL);
-    th_verbosityLevel = 0;
-
-    if (sizeof(char) != sizeof(unsigned char))
-    {
-        THERR("sizeof(char) != sizeof(unsigned char)???\n");
-        return -1;
-    }
-
-    tests_failed = tests_passed = tests_total = tests_set = 0;
-
-    //
-    // Parse command line arguments
-    //
-    if (!th_args_process(argc, argv, arg_opts, arg_nopts,
-        arg_handle_opt, NULL, 0))
-        return 0;
-
-
-    //
-    // Test series #1
-    //
-    tests_header("printf() function family");
-    int i_vals[] = { 2, 612342, -2, -612342, 0x1fff, 0x8000000, };
-    char *i_fmts[] = { "%d", "%x", "%05d", "%5d", "%-5d", "%05x", "%5x", "", };
-    size_t i1, i2;
-
-    for (i1 = 0; i1 < sizeof(i_vals) / sizeof(i_vals[0]); i1++)
-    for (i2 = 0; i2 < sizeof(i_fmts) / sizeof(i_fmts[0]); i2++)
-        test_snprintf(i_fmts[i2], i_vals);
-
-    char *s_vals[] = { "", "asdf", "xxx yyy zzz ppp fff", NULL, "X", "abcde", };
-    char *s_fmts[] = { "%s", "%2s", "%-2s", "%5s", "%-5s", "%16s", "%-16s", "%1s", "%-1s", };
-
-    for (i1 = 0; i1 < sizeof(s_vals) / sizeof(s_vals[0]); i1++)
-    for (i2 = 0; i2 < sizeof(s_fmts) / sizeof(s_fmts[0]); i2++)
-        test_snprintf(s_fmts[i2], s_vals);
-
-    test_snprintf("a%cBC", 'x');
-    test_snprintf("%c", 'x');
-    test_snprintf("", 'x');
-
-    //
-    // String matching functions
-    //
-    tests_header("String matching");
-
 #define TEST2(fun, str1, str2, ret) do { \
         test_start(# fun  "('%s', '%s')", str1, str2); \
         test_result(( fun (str1, str2) == 0) == ret); \
@@ -238,50 +230,132 @@
         test_result(( fun (str1, str2, len) == 0) == ret); \
     } while (0)
 
-    TEST2(th_strcasecmp, "aSdFq", "asdfq", TRUE);
-    TEST2(th_strcasecmp, "aSdFq", "asFfq", FALSE);
-    TEST2(th_strcasecmp, "abcde", "abcde", TRUE);
-    TEST2(th_strcasecmp, "öäå", "öäå", TRUE);
-    TEST2(th_strcasecmp, "aöäå", "aöäå", TRUE);
+
+
+int main(int argc, char *argv[])
+{
+    size_t i1, i2;
+
+    //
+    // Initialization
+    //
+    th_init("th-test", "th-libs unit tests", "0.0.1", NULL, NULL);
+    th_verbosityLevel = 0;
+
+    if (sizeof(char) != sizeof(unsigned char))
+    {
+        THERR("sizeof(char) != sizeof(unsigned char)???\n");
+        return -1;
+    }
+
+    tests_failed = tests_passed = tests_total = tests_sets = tests_nenabled = 0;
+    for (i1 = 0; i1 < SET_MAX_TESTS; i1++)
+        tests_enabled[i1] = 1;
+
+    //
+    // Parse command line arguments
+    //
+    if (!th_args_process(argc, argv, arg_opts, arg_nopts,
+        arg_handle_opt, NULL, 0))
+        return 0;
+
+
+    //
+    // Test series #1
+    //
+    if (test_set_start("printf() function family #1"))
+    {
+        int i_vals[] = { 2, 612342, -2, -612342, 0x1fff, 0x8000000, };
+        char *i_fmts[] = { "%d", "%x", "%05d", "%5d", "%-5d", "%05x", "%5x", "", "% 3d", "% 3o", "%+3o", "%+3d", };
+
+        for (i1 = 0; i1 < sizeof(i_vals) / sizeof(i_vals[0]); i1++)
+        for (i2 = 0; i2 < sizeof(i_fmts) / sizeof(i_fmts[0]); i2++)
+            test_snprintf(i_fmts[i2], i_vals);
+    }
+
+    if (test_set_start("printf() function family #2"))
+    {
+        char *s_vals[] = { "", "XYZXYZ", "xxx yyy zzz ppp fff", NULL, "X", "abcde", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", };
+        char *s_fmts[] = { "%s", "%2s", "%-2s", "%5s", "%-5s", "%16s", "%-16s", "%1s", "%-1s", "% 2s", "%03s", "% -12s", "% 03s", };
+
+        for (i1 = 0; i1 < sizeof(s_vals) / sizeof(s_vals[0]); i1++)
+        for (i2 = 0; i2 < sizeof(s_fmts) / sizeof(s_fmts[0]); i2++)
+            test_snprintf(s_fmts[i2], s_vals);
+
+    }
 
-    TEST3(th_strncasecmp, "aSdFq", "asFfq", 4, FALSE);
-    TEST3(th_strncasecmp, "aSdFq", "asFfq", 2, TRUE);
-    TEST3(th_strncasecmp, "aSdFq", "asDfq", 3, TRUE);
-    TEST3(th_strncasecmp, "aSdFq", "asDfq", 2, TRUE);
-    TEST3(th_strncasecmp, "aSdFq", "asDfq", 0, TRUE);
-    TEST3(th_strncasecmp, "aSdFq", "QsDfq", 0, TRUE);
-    TEST3(th_strncasecmp, "aSdFq", "QsDfq", 1, FALSE);
+    if (test_set_start("printf() function family #3"))
+    {
+        test_snprintf("a%cBC", 'x');
+        test_snprintf("%c", 'x');
+        test_snprintf("", 'x');
+        test_snprintf("%0c", 'x');
+        test_snprintf("%1c", 'x');
+        test_snprintf("% c", 'x');
+        test_snprintf("%-3c", 'x');
+        test_snprintf("%3c", 'x');
+    }
+
+    //
+    // String matching functions
+    //
+    if (test_set_start("String matching #1"))
+    {
+        TEST2(th_strcasecmp, "aSdFq", "asdfq", TRUE);
+        TEST2(th_strcasecmp, "aSdFq", "asFfq", FALSE);
+        TEST2(th_strcasecmp, "abcde", "abcde", TRUE);
+        TEST2(th_strcasecmp, "öäå", "öäå", TRUE);
+        TEST2(th_strcasecmp, "aöäå", "aöäå", TRUE);
+    }
 
-    TEST2B(th_strmatch, "abba ABBAkukka lol", "*lol", TRUE);
-    TEST2B(th_strmatch, "abba ABBAkukka lol", "*lo*", TRUE);
-    TEST2B(th_strmatch, "abba ABBAkukka lol", "*lo", FALSE);
-    TEST2B(th_strmatch, "abba ABBAkukka lol", "abba", FALSE);
-    TEST2B(th_strmatch, "abba ABBAkukka lol", "abba*", TRUE);
-    TEST2B(th_strmatch, "abba ABBAkukka lol", "abbak*", FALSE);
-    TEST2B(th_strcasematch, "abba ABBAkukka lol", "abbak*", FALSE);
-    TEST2B(th_strcasematch, "abba ABBAkukka lol", "*abbak*", TRUE);
-    TEST2B(th_strcasematch, "abba ABBAkukka lol", "*abbak?", FALSE);
-    TEST2B(th_strcasematch, "abba ABBAkukka lol", "?bba?abba*", TRUE);
-    TEST2B(th_strmatch, "abba ABBAöökukka lol", "*abbaö?", FALSE);
+    if (test_set_start("String matching #2"))
+    {
+        TEST3(th_strncasecmp, "aSdFq", "asFfq", 4, FALSE);
+        TEST3(th_strncasecmp, "aSdFq", "asFfq", 2, TRUE);
+        TEST3(th_strncasecmp, "aSdFq", "asDfq", 3, TRUE);
+        TEST3(th_strncasecmp, "aSdFq", "asDfq", 2, TRUE);
+        TEST3(th_strncasecmp, "aSdFq", "asDfq", 0, TRUE);
+        TEST3(th_strncasecmp, "aSdFq", "QsDfq", 0, TRUE);
+        TEST3(th_strncasecmp, "aSdFq", "QsDfq", 1, FALSE);
+    }
+
+    if (test_set_start("String matching #3"))
+    {
+        TEST2B(th_strmatch, "abba ABBAkukka lol", "*lol", TRUE);
+        TEST2B(th_strmatch, "abba ABBAkukka lol", "*lo*", TRUE);
+        TEST2B(th_strmatch, "abba ABBAkukka lol", "*lo", FALSE);
+        TEST2B(th_strmatch, "abba ABBAkukka lol", "abba", FALSE);
+        TEST2B(th_strmatch, "abba ABBAkukka lol", "abba*", TRUE);
+        TEST2B(th_strmatch, "abba ABBAkukka lol", "abbak*", FALSE);
+        TEST2B(th_strmatch, "abba ABBAöökukka lol", "*abbaö?", FALSE);
+    }
+
+    if (test_set_start("String matching #4"))
+    {
+        TEST2B(th_strcasematch, "abba ABBAkukka lol", "abbak*", FALSE);
+        TEST2B(th_strcasematch, "abba ABBAkukka lol", "*abbak*", TRUE);
+        TEST2B(th_strcasematch, "abba ABBAkukka lol", "*abbak?", FALSE);
+        TEST2B(th_strcasematch, "abba ABBAkukka lol", "?bba?abba*", TRUE);
+    }
 
     // Tests that test for things that do not work correctly yet
     // Unicode / multibyte UTF-8 causes problems here
-    tests_header("Invalid");
-    TEST2(th_strcasecmp, "ÖÄÅ", "öäå", FALSE); // SHOULD match
-    TEST3(th_strncasecmp, "Aäöå", "aöå", 2, TRUE); // should NOT match
-
-    TEST2B(th_strmatch, "öriÖRI! lol", "?ri?RI!*", FALSE); // should match
-
+    if (test_set_start("Invalid"))
+    {
+        TEST2(th_strcasecmp, "ÖÄÅ", "öäå", FALSE); // SHOULD match
+        TEST3(th_strncasecmp, "Aäöå", "aöå", 2, TRUE); // should NOT match
+        TEST2B(th_strmatch, "öriÖRI! lol", "?ri?RI!*", FALSE); // should match
+    }
 
     //
     // Print summary and exit
     //
     THPRINT(1,
         "======================================================\n");
-    
+
     THPRINT(0,
-        "%d tests failed, %d passed (%d main tests), %d test sets.\n",
-        tests_failed, tests_passed, tests_total, tests_set);
+        "%d tests failed, %d passed (%d main tests), %d test sets of %d sets total.\n",
+        tests_failed, tests_passed, tests_total, tests_nenabled, tests_sets);
 
     return 0;
 }
--- a/th_string.c	Mon Feb 22 16:01:04 2016 +0200
+++ b/th_string.c	Mon Feb 22 16:02:25 2016 +0200
@@ -124,9 +124,21 @@
 //
 // Simple implementations of printf() type functions
 //
-static int th_vput_int(th_printf_ctx *ctx, int (*vputch)(th_printf_ctx *ctx, const char ch),
+static int th_printf_vput_pad(th_printf_ctx *ctx, th_printf_vputch vputch, int nwidth, const char padChar)
+{
+    while (nwidth--)
+    {
+        int ret;
+        if ((ret = vputch(ctx, padChar)) == EOF)
+            return ret;
+    }
+    return 0;
+}
+
+
+static int th_printf_vput_int(th_printf_ctx *ctx, th_printf_vputch vputch,
     int pval, const int radix, const char padMode, const char padChar,
-    const int width, const BOOL unsig, const BOOL upcase, const BOOL sign)
+    const int f_width, const BOOL unsig, const BOOL upcase, const BOOL f_sign)
 {
     char buf[64];
     size_t pos = 0;
@@ -165,23 +177,17 @@
     // Do we want a sign prefix? Not for unsigned values
     if (!unsig)
     {
-        char ch = sign ? (neg ? '-' : '+') : (neg ? '-' : 0);
+        char ch = f_sign ? (neg ? '-' : '+') : (neg ? '-' : 0);
         if (ch && (ret = vputch(ctx, ch)) == EOF)
             goto out;
     }
 
     // Calculate necessary padding, if any
-    int nwidth = width - pos;
+    int nwidth = f_width - pos;
 
     // Prefix padding?
-    if (padMode != '-' && nwidth > 0)
-    {
-        while (nwidth--)
-        {
-            if ((ret = vputch(ctx, padMode)) == EOF)
-                goto out;
-        }
-    }
+    if (padMode != '-' && nwidth > 0 && (ret = th_printf_vput_pad(ctx, vputch, nwidth, padMode)) == EOF)
+        goto out;
 
     // Output the value
     while (pos--)
@@ -191,23 +197,17 @@
     }
 
     // Postfix padding?
-    if (padMode == '-' && nwidth > 0)
-    {
-        while (nwidth--)
-        {
-            if ((ret = vputch(ctx, ' ')) == EOF)
-                goto out;
-        }
-    }
+    if (padMode == '-' && nwidth > 0 && (ret = th_printf_vput_pad(ctx, vputch, nwidth, ' ')) == EOF)
+        goto out;
 
 out:
     return ret;
 }
 
 
-static int th_vput_str(th_printf_ctx *ctx, int (*vputch)(th_printf_ctx *ctx, const char ch),
+static int th_printf_vput_str(th_printf_ctx *ctx, th_printf_vputch vputch,
     const char *str, const char padMode, const char padChar,
-    const int width, const int prec)
+    const int f_width, const int f_prec)
 {
     int nwidth, ret = 0;
 
@@ -215,17 +215,11 @@
     if (str == NULL)
         str = "(null)";
 
-    nwidth = width - strlen(str);
+    nwidth = f_width - strlen(str);
 
     // Prefix padding?
-    if (padMode != '-' && nwidth > 0)
-    {
-        while (nwidth--)
-        {
-            if ((ret = vputch(ctx, padMode)) == EOF)
-                goto out;
-        }
-    }
+    if (padMode != '-' && nwidth > 0 && (ret = th_printf_vput_pad(ctx, vputch, nwidth, padMode)) == EOF)
+        goto out;
 
     while (*str)
     {
@@ -234,23 +228,15 @@
     }
 
     // Postfix padding?
-    if (padMode == '-' && nwidth > 0)
-    {
-        while (nwidth--)
-        {
-            if ((ret = vputch(ctx, ' ')) == EOF)
-                goto out;
-        }
-    }
+    if (padMode == '-' && nwidth > 0 && (ret = th_printf_vput_pad(ctx, vputch, nwidth, ' ')) == EOF)
+        goto out;
 
 out:
     return ret;
 }
 
 
-int th_vprintf_do(th_printf_ctx *ctx,
-    int (*vputch)(th_printf_ctx *ctx, const char ch),
-    const char *fmt, va_list ap)
+int th_vprintf_do(th_printf_ctx *ctx, th_printf_vputch vputch, const char *fmt, va_list ap)
 {
     int ret = 0;
 
@@ -264,15 +250,15 @@
         else
         {
             char padMode = ' ', padChar = 0;
-            BOOL sign = FALSE;
-            int width = 0, prec = -1;
+            int f_width = 0, f_prec = -1;
+            BOOL f_sign = FALSE;
 
             fmt++;
 
-            // Check sign
+            // Check for field sign
             if (*fmt == '+')
             {
-                sign = TRUE;
+                f_sign = TRUE;
                 fmt++;
             }
 
@@ -292,9 +278,9 @@
                     return -102;
             }
 
-            // Get width
+            // Get field width
             while (th_isdigit(*fmt))
-                width = width * 10 + (*fmt++ - '0');
+                f_width = f_width * 10 + (*fmt++ - '0');
 
             // Check for precision
             if (*fmt == '.')
@@ -303,9 +289,20 @@
                 if (!th_isdigit(*fmt))
                     return -103;
 
-                prec = 0;
+                f_prec = 0;
                 while (th_isdigit(*fmt))
-                    prec = prec * 10 + (*fmt++ - '0');
+                    f_prec = f_prec * 10 + (*fmt++ - '0');
+            }
+            // Check for length modifiers (NOT SUPPORTED CURRENTLY)
+            switch (*fmt)
+            {
+                case 'l':
+                case 'L':
+                case 'h':
+                case 'j':
+                case 'z':
+                case 't':
+                    return -202;
             }
 
             switch (*fmt)
@@ -314,7 +311,7 @@
                     return -104;
 
                 case 'c':
-                    if (padMode != ' ' || width > 0 || prec >= 0 || sign)
+                    if (padMode != ' ' || f_width > 0 || f_prec >= 0 || f_sign)
                         return -105;
 
                     if ((ret = vputch(ctx, va_arg(ap, int))) == EOF)
@@ -323,21 +320,21 @@
 
                 case 'u':
                 case 'd':
-                    if ((padMode != '0' && padMode != '-' && padMode != ' ') || prec >= 0)
+                    if ((padMode != '0' && padMode != '-' && padMode != ' ') || f_prec >= 0)
                         return -105;
 
-                    if ((ret = th_vput_int(ctx, vputch, va_arg(ap, unsigned int),
-                        10, padMode, padChar, width, *fmt == 'u', FALSE, sign)) == EOF)
+                    if ((ret = th_printf_vput_int(ctx, vputch, va_arg(ap, unsigned int),
+                        10, padMode, padChar, f_width, *fmt == 'u', FALSE, f_sign)) == EOF)
                         goto out;
                     break;
 
                 case 'x':
                 case 'X':
-                    if ((padMode != '0' && padMode != '-' && padMode != ' ') || prec >= 0)
+                    if ((padMode != '0' && padMode != '-' && padMode != ' ') || f_prec >= 0)
                         return -106;
 
-                    if ((ret = th_vput_int(ctx, vputch, va_arg(ap, unsigned int),
-                        16, padMode, padChar, width, TRUE, *fmt == 'X', FALSE)) == EOF)
+                    if ((ret = th_printf_vput_int(ctx, vputch, va_arg(ap, unsigned int),
+                        16, padMode, padChar, f_width, TRUE, *fmt == 'X', FALSE)) == EOF)
                         goto out;
                     break;
 
@@ -346,14 +343,14 @@
                     break;
 
                 case 's':
-                    if ((padMode != '-' && padMode != ' ') || sign)
+                    if ((padMode != '-' && padMode != ' ') || f_sign)
                         return -108;
 
-                    if ((ret = th_vput_str(ctx, vputch, va_arg(ap, char *), padMode, padChar, width, prec)) == EOF)
+                    if ((ret = th_printf_vput_str(ctx, vputch, va_arg(ap, char *), padMode, padChar, f_width, f_prec)) == EOF)
                         goto out;
                     break;
 
-                case '%':
+                //case '%':
                 default:
                     if ((ret = vputch(ctx, *fmt)) == EOF)
                         goto out;
@@ -364,7 +361,7 @@
     }
 
 out:
-    return ret == EOF ? ret : ctx->pos;
+    return ret == EOF ? ret : ctx->ipos;
 }
 
 
@@ -373,7 +370,9 @@
 {
     if (ctx->pos < ctx->size)
         ctx->buf[ctx->pos] = ch;
+
     ctx->pos++;
+    ctx->ipos++;
     return ch;
 }
 
@@ -381,6 +380,7 @@
 static int th_stdio_vputch(th_printf_ctx *ctx, const char ch)
 {
     ctx->pos++;
+    ctx->ipos++;
     return fputc(ch, (FILE *) ctx->data);
 }
 #endif
@@ -394,6 +394,7 @@
     ctx.buf = buf;
     ctx.size = size;
     ctx.pos = 0;
+    ctx.ipos = 0;
 
     ret = th_vprintf_do(&ctx, th_pbuf_vputch, fmt, ap);
 
@@ -431,6 +432,7 @@
     th_printf_ctx ctx;
     ctx.data = (void *) fh;
     ctx.pos = 0;
+    ctx.ipos = 0;
 
     return th_vprintf_do(&ctx, th_stdio_vputch, fmt, ap);
 #else
@@ -450,6 +452,8 @@
 #ifdef TH_USE_INTERNAL_SPRINTF
     ctx.data = (void *) fh;
     ctx.pos = 0;
+    ctx.ipos = 0;
+
     ret = th_vprintf_do(&ctx, th_stdio_vputch, fmt, ap);
 #else
     ret = fprintf(fh, fmt, ap);
@@ -552,7 +556,7 @@
     {
         int k = th_tolower(*s1) - th_tolower(*s2);
         if (k != 0)
-            return k; 
+            return k;
         s1++;
         s2++;
     }
@@ -574,7 +578,7 @@
     {
         int k = th_tolower(*s1) - th_tolower(*s2);
         if (k != 0)
-            return k; 
+            return k;
         s1++;
         s2++;
         n--;
--- a/th_string.h	Mon Feb 22 16:01:04 2016 +0200
+++ b/th_string.h	Mon Feb 22 16:02:25 2016 +0200
@@ -52,10 +52,14 @@
 {
     char *buf;
     size_t size, pos;
+    int ipos;
     void *data;
 } th_printf_ctx;
 
 
+typedef int (*th_printf_vputch)(th_printf_ctx *ctx, const char ch);
+
+
 /* Normal NUL-terminated string functions
  */
 size_t   th_strlen(const char *str);