1/*
2   String handling tests
3   Copyright (C) 2001-2007, 2009, Joe Orton <joe@manyfish.co.uk>
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19*/
20
21#include "config.h"
22
23#ifdef HAVE_STDLIB_H
24#include <stdlib.h>
25#endif
26#ifdef HAVE_STRING_H
27#include <string.h>
28#endif
29#ifdef HAVE_ERRNO_H
30#include <errno.h> /* for the ENOENT definitions in str_errors */
31#endif
32
33#include "ne_string.h"
34#include "ne_utils.h"
35
36#include "tests.h"
37
38#undef ONCMP
39#define ONCMP(a,b) ONV(strcmp(a, b), \
40		       ("result was [%s] not [%s]", a, b))
41
42static int simple(void) {
43    ne_buffer *s = ne_buffer_create();
44    ON(s == NULL);
45    ne_buffer_zappend(s, "abcde");
46    ONCMP(s->data, "abcde");
47    ON(ne_buffer_size(s) != 5);
48    ne_buffer_destroy(s);
49    return OK;
50}
51
52static int buf_concat(void)
53{
54    ne_buffer *s = ne_buffer_create();
55    ON(s == NULL);
56    ne_buffer_concat(s, "a", "b", "c", "d", "e", "f", "g", NULL);
57    ONCMP(s->data, "abcdefg");
58    ON(ne_buffer_size(s) != 7);
59    ne_buffer_destroy(s);
60    return OK;
61}
62
63static int buf_concat2(void)
64{
65#define RES "alphabetagammadeltaepsilonetatheta"
66    ne_buffer *s = ne_buffer_create();
67    ON(s == NULL);
68    ne_buffer_concat(s, "alpha", "beta", "gamma", "delta", "epsilon",
69		     "eta", "theta", NULL);
70    ONCMP(s->data, RES);
71    ON(ne_buffer_size(s) != strlen(RES));
72    ne_buffer_destroy(s);
73    return OK;
74}
75
76static int buf_concat3(void)
77{
78    ne_buffer *s = ne_buffer_create();
79    ON(s == NULL);
80    ne_buffer_zappend(s, "foobar");
81    ne_buffer_concat(s, "norman", NULL);
82    ONCMP(s->data, "foobarnorman");
83    ON(ne_buffer_size(s) != 12);
84    ne_buffer_destroy(s);
85    return OK;
86}
87
88static int append(void)
89{
90    ne_buffer *s = ne_buffer_create();
91    ON(s == NULL);
92    ne_buffer_append(s, "a", 1);
93    ne_buffer_append(s, "b", 1);
94    ne_buffer_append(s, "c", 1);
95    ONCMP(s->data, "abc");
96    ON(ne_buffer_size(s) != 3);
97    ne_buffer_zappend(s, "hello");
98    ONCMP(s->data, "abchello");
99    ne_buffer_czappend(s, "world");
100    ONCMP(s->data, "abchelloworld");
101    ON(ne_buffer_size(s) != 13);
102    ne_buffer_destroy(s);
103    return OK;
104}
105
106static int grow(void)
107{
108    ne_buffer *s = ne_buffer_ncreate(2);
109    ON(s == NULL);
110    ne_buffer_append(s, "a", 1);
111    ne_buffer_grow(s, 4);
112    ONCMP(s->data, "a");
113    ne_buffer_destroy(s);
114    return OK;
115}
116
117static int alter(void) {
118    ne_buffer *s = ne_buffer_create();
119    char *d;
120    ON(s == NULL);
121    ne_buffer_zappend(s, "abcdefg");
122    d = s->data;
123    ON(d == NULL);
124    d[2] = '\0';
125    ne_buffer_altered(s);
126    ONCMP(s->data, "ab");
127    ON(ne_buffer_size(s) != 2);
128    ne_buffer_zappend(s, "hijkl");
129    ONCMP(s->data, "abhijkl");
130    ne_buffer_destroy(s);
131    return OK;
132}
133
134/* Macros for testing ne_token. */
135
136#define TEST(res) do { \
137  char *tok = ne_token(&pnt, ','); \
138  ONN(res ": return", tok == NULL); \
139  ONN(res ": compare", strcmp(tok, (res))); \
140  ONN(res ": modify", pnt == NULL); \
141} while (0)
142
143#define LASTTEST(res) do { \
144  char *tok = ne_token(&pnt, ','); \
145  ONN(res ": last return", tok == NULL); \
146  ONN(res ": last compare", strcmp(tok, (res))); \
147  ONN(res ": last modify", pnt != NULL); \
148} while (0)
149
150#define QTEST(res) do { \
151  char *tok = ne_qtoken(&pnt, ',', QUOTES); \
152  ONN(res ": return", tok == NULL); \
153  ONN(res ": compare", strcmp(tok, (res))); \
154  ONN(res ": modify", pnt == NULL); \
155} while (0)
156
157#define QLASTTEST(res) do { \
158  char *tok = ne_qtoken(&pnt, ',', QUOTES); \
159  ONN(res ": last return", tok == NULL); \
160  ONN(res ": last compare", strcmp(tok, (res))); \
161  ONN(res ": last modify", pnt != NULL); \
162} while (0)
163
164static int token1(void)
165{
166    char *str = ne_strdup("a,b,c,d"), *pnt = str;
167
168    TEST("a"); TEST("b"); TEST("c"); LASTTEST("d");
169
170    ne_free(str);
171    return OK;
172}
173
174static int token2(void)
175{
176    char *str = ne_strdup("norman,fishing, elsewhere"), *pnt = str;
177
178    TEST("norman"); TEST("fishing"); LASTTEST(" elsewhere");
179
180    ne_free(str);
181    return OK;
182}
183
184static int nulls(void)
185{
186    char *str = ne_strdup("alpha,,gamma"), *pnt = str;
187
188    TEST("alpha"); TEST(""); LASTTEST("gamma");
189    ne_free(str);
190
191    pnt = str = ne_strdup(",,,wooo");
192    TEST(""); TEST(""); TEST(""); LASTTEST("wooo");
193    ne_free(str);
194
195    pnt = str = ne_strdup("wooo,,,");
196    TEST("wooo"); TEST(""); TEST(""); LASTTEST("");
197    ne_free(str);
198
199    return OK;
200}
201
202static int empty(void)
203{
204    char *str = ne_strdup(""), *pnt = str;
205
206    LASTTEST("");
207    ne_free(str);
208
209    return OK;
210}
211
212#undef QUOTES
213#define QUOTES "'"
214
215static int quoted(void)
216{
217    char *str =
218	ne_strdup("alpha,'beta, a fish called HELLO!?',sandwiches");
219    char *pnt = str;
220
221    QTEST("alpha");
222    QTEST("'beta, a fish called HELLO!?'");
223    QLASTTEST("sandwiches");
224
225    ne_free(str);
226    return OK;
227}
228
229static int badquotes(void)
230{
231    char *str = ne_strdup("alpha,'blah"), *pnt = str;
232
233    QTEST("alpha");
234    ON(ne_qtoken(&pnt, ',', QUOTES) != NULL);
235
236    ne_free(str);
237    return OK;
238}
239
240/* for testing ne_shave. */
241#undef TEST
242#define TEST(str, ws, res) do { \
243  char *s = ne_strdup((str)); \
244  char *r = ne_shave(s, (ws)); \
245  ONN("[" str "]", strcmp(r, (res))); \
246  ne_free(s); \
247} while (0)
248
249static int shave(void)
250{
251    TEST(" b ", " ", "b");
252    TEST("b", " ", "b");
253    TEST("   b    ", " ", "b");
254    TEST("--bbb-----", "-", "bbb");
255    TEST("hello, world    ", " ", "hello, world");
256    TEST("<<><<<><<this is foo<<><<<<><<", "<>", "this is foo");
257    TEST("09809812342347I once saw an helicopter0012312312398", "0123456789",
258	 "I once saw an helicopter");
259    return OK;
260}
261
262/* Regression test for ne_shave call which should produce an empty
263 * string. */
264static int shave_regress(void)
265{
266    TEST("\"\"", "\"", "");
267    return OK;
268}
269
270/* Test the ne_token/ne_shave combination. */
271
272#undef TEST
273#undef LASTTEST
274
275#define TEST(res) do { \
276  char *tok = ne_token(&pnt, ','); \
277  ONN(res ": return", tok == NULL); \
278  tok = ne_shave(tok, " "); \
279  ONN(res ": shave", tok == NULL); \
280  ONN(res ": compare", strcmp(tok, (res))); \
281  ONN(res ": modify", pnt == NULL); \
282} while (0)
283
284
285#define LASTTEST(res) do { \
286  char *tok = ne_token(&pnt, ','); \
287  ONN(res ": last return", tok == NULL); \
288  tok = ne_shave(tok, " "); \
289  ONN(res ": last shave", tok == NULL); \
290  ONN(res ": last compare", strcmp(tok, (res))); \
291  ONN(res ": last modify", pnt != NULL); \
292} while (0)
293
294/* traditional use of ne_token/ne_shave. */
295static int combo(void)
296{
297    char *str = ne_strdup("  fred , mary, jim , alice, david"), *pnt = str;
298
299    TEST("fred"); TEST("mary"); TEST("jim"); TEST("alice");
300    LASTTEST("david");
301
302    ne_free(str);
303    return 0;
304}
305
306static int concat(void)
307{
308#define CAT(res, args) do { char *str = ne_concat args; \
309ONCMP(str, res); \
310ne_free(str); } while (0)
311    CAT("alphabeta", ("alpha", "beta", NULL));
312    CAT("alpha", ("alpha", "", "", NULL));
313    CAT("", ("", NULL));
314    CAT("", ("", "", "", NULL));
315    CAT("alpha", ("", "a", "lph", "", "a", NULL));
316    return OK;
317}
318
319static int str_errors(void)
320{
321    char expect[200], actual[200];
322
323    strncpy(expect, strerror(ENOENT), sizeof(expect));
324    ONN("ne_strerror did not return passed-in buffer",
325	ne_strerror(ENOENT, actual, sizeof(actual)) != actual);
326
327    ONV(strcmp(expect, actual),
328	("error from ENOENT was `%s' not `%s'", actual, expect));
329
330    /* Test truncated error string is still NUL-terminated. */
331    ne_strerror(ENOENT, actual, 6);
332    NE_DEBUG(NE_DBG_HTTP, "error: %s\n", actual);
333    ONN("truncated string had wrong length", strlen(actual) != 5);
334
335    ne_strerror(-1, actual, 6);
336    ONN("truncated string for bad error had wrong length",
337        strlen(actual) != 5);
338
339    return OK;
340}
341
342static int strnzcpy(void)
343{
344    char buf[5];
345
346    ne_strnzcpy(buf, "abcdefghi", sizeof buf);
347    ONV(strcmp(buf, "abcd"), ("result was `%s' not `abcd'", buf));
348
349    ne_strnzcpy(buf, "ab", sizeof buf);
350    ONV(strcmp(buf, "ab"), ("result was `%s' not `ab'", buf));
351
352    return OK;
353}
354
355#define FOX_STRING "The quick brown fox jumped over the lazy dog"
356#define PUNC_STRING "<>,.;'#:@~[]{}!\"$%^&*()_+-="
357
358static int cleaner(void)
359{
360    static const char *strings[] = {
361        "alpha", "alpha",
362        "pretty\033[41mcolours", "pretty [41mcolours",
363        "beta\n", "beta ",
364        "del\rt\na", "del t a",
365        FOX_STRING, FOX_STRING,
366        "0123456789", "0123456789",
367        PUNC_STRING, PUNC_STRING,
368        "\01blah blee\05bloo", " blah blee bloo",
369        NULL,
370    };
371    unsigned int n;
372
373    for (n = 0; strings[n]; n+=2) {
374        char *act = ne_strclean(ne_strdup(strings[n]));
375
376        ONV(strcmp(act, strings[n+1]),
377            ("cleansed to `%s' not `%s'", act, strings[n+1]));
378
379        ne_free(act);
380    }
381
382    return OK;
383}
384
385/* Check that raw data 'raw', of length 'len', has base64 encoding
386 * of 'expected'. */
387static int b64_check(const unsigned char *raw, size_t len,
388                     const char *expected)
389{
390    char *encoded = ne_base64(raw, len);
391    unsigned char *decoded;
392    size_t dlen;
393
394    ONV(strcmp(encoded, expected),
395        ("base64(\"%s\") gave \"%s\" not \"%s\"", raw, encoded, expected));
396
397    dlen = ne_unbase64(encoded, &decoded);
398    ONV(dlen != len,
399        ("decoded `%s' length was %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T,
400         expected, dlen, len));
401
402    ONV(memcmp(raw, decoded, dlen),
403        ("decoded `%s' as `%.*s' not `%.*s'",
404         expected, (int)dlen, decoded, (int)dlen, raw));
405
406    ne_free(decoded);
407    ne_free(encoded);
408    return OK;
409}
410
411/* ALLBITS: base64 encoding of "\0..\377" */
412#define ALLBITS \
413"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss" \
414"LS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZ" \
415"WltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWG" \
416"h4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz" \
417"tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g" \
418"4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
419
420static int base64(void)
421{
422    unsigned char bits[256];
423    size_t n;
424
425#define B64B(x, l, y) CALL(b64_check((unsigned char *)x, l, y))
426#define B64(x, y) B64B(x, strlen(x), y)
427
428    /* invent these with
429     *  $ printf "string" | uuencode -m blah
430     */
431    B64("a", "YQ==");
432    B64("bb", "YmI=");
433    B64("ccc", "Y2Nj");
434    B64("Hello, world", "SGVsbG8sIHdvcmxk");
435    B64("Aladdin:open sesame", "QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
436    B64("I once saw a dog called norman.\n",
437	"SSBvbmNlIHNhdyBhIGRvZyBjYWxsZWQgbm9ybWFuLgo=");
438    B64("The quick brown fox jumped over the lazy dog",
439	"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2c=");
440
441    /* binary data..
442     *   $ printf "string" | wc -c # get the length
443     *   $ printf "string" | uuencode -m blah # get the base64
444     */
445    B64B("\0\0\0\0\0\n", 6, "AAAAAAAK");
446    B64B("I once wished \0 upon a \0 fish.", 30,
447	 "SSBvbmNlIHdpc2hlZCAAIHVwb24gYSAAIGZpc2gu");
448    B64B("\201\202\203\204", 4, "gYKDhA==");
449
450    for (n = 0; n < sizeof bits; n++)
451        bits[n] = (unsigned char)n;
452    CALL(b64_check(bits, sizeof bits, ALLBITS));
453
454#undef B64
455#undef B64B
456    return OK;
457}
458
459static int unbase64(void)
460{
461    static const char *ts[] = {
462        "", "a", "ab", "abc",
463        "}bcd", "a}cd", "ab}d", "abc}", "    ",
464        "^bcd", "a^cd", "ab^d", "abc^",
465        "====", "=bcd", "a=cd", "ab=d", "a==d", "a=c=",
466        NULL
467    };
468    size_t n;
469
470    for (n = 0; ts[n]; n++) {
471        unsigned char *tmp;
472        ONV(ne_unbase64(ts[n], &tmp) != 0,
473            ("invalid string `%s' was decoded", ts[n]));
474    }
475
476    return OK;
477}
478
479static int printing(void)
480{
481    struct {
482        const char *in, *out;
483        size_t pass, ret;
484    } ts[] = {
485        { "alpha", "alpha", 10, 5 },
486        { "alpha", "alph", 5, 4 },
487        { "foobar", "", 1, 0 },
488        { NULL, NULL, 0, 0}
489    };
490    size_t n;
491
492    for (n = 0; ts[n].in; n++) {
493        char buf[512];
494        size_t ret;
495
496        memset(buf, 'A', sizeof buf);
497
498        ret = ne_snprintf(buf, ts[n].pass, "%s", ts[n].in);
499
500        ONCMP(buf, ts[n].out);
501        ONV(ret != ts[n].ret,
502            ("got return value %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T,
503             ret, ts[n].ret));
504
505        /* byte past the NUL must still be 'A' */
506        ONN("buffer over-ran!", buf[ret + 1] != 'A');
507    }
508
509    return OK;
510}
511
512static int casecmp(void)
513{
514    static const struct {
515        const char *left, *right;
516        int expect;
517    } ts[] = {
518        { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0 },
519        { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", 0 },
520        { "foo", "bar", 1 },
521        { "!#:[@\377", "!#:[@\377", 0 },
522        { "bar", "foo", -1 },
523        { "foop", "foo", 1 },
524        { "foo", "foop", -1 },
525        { NULL, NULL, 0 }
526    };
527    size_t n;
528
529    for (n = 0; ts[n].left; n++) {
530        int actual;
531
532        actual = ne_strcasecmp(ts[n].left, ts[n].right);
533
534        ONV(ts[n].expect == 0 && actual != 0,
535            ("strcasecmp(%s, %s) gave %d, expected 0",
536             ts[n].left, ts[n].right, actual));
537
538        ONV(ts[n].expect > 0 && actual <= 0,
539            ("strcasecmp(%s, %s) gave %d, expected > 0",
540             ts[n].left, ts[n].right, actual));
541
542        ONV(ts[n].expect < 0 && actual >= 0,
543            ("strcasecmp(%s, %s) gave %d, expected < 0",
544             ts[n].left, ts[n].right, actual));
545    }
546
547    ONN("comparison of identical pointers did not give zero",
548        ne_strcasecmp(ts[0].left, ts[0].left) != 0);
549
550    return OK;
551}
552
553static int casencmp(void)
554{
555    static const struct {
556        const char *left, *right;
557        size_t n;
558        int expect;
559    } ts[] = {
560        { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 30, 0 },
561        { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10, 0 },
562        { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", 0, 0 },
563        { "foo", "bar", 3, 1 },
564        { "bar", "foo", 4, -1 },
565        { "bar", "foo", 3, -1 },
566        { "foop", "foo", 4, 1 },
567        { "foo", "foop", 4, -1 },
568        { "bee", "bar", 0, 0},
569        { NULL, NULL, 0, 0 }
570    };
571    size_t n;
572
573    for (n = 0; ts[n].left; n++) {
574        int actual;
575
576        actual = ne_strncasecmp(ts[n].left, ts[n].right, ts[n].n);
577
578        ONV(ts[n].expect == 0 && actual != 0,
579            ("strncasecmp(%s, %s, %" NE_FMT_SIZE_T ") gave %d, expected 0",
580             ts[n].left, ts[n].right, ts[n].n, actual));
581
582        ONV(ts[n].expect > 0 && actual <= 0,
583            ("strncasecmp(%s, %s, %" NE_FMT_SIZE_T ") gave %d, expected > 0",
584             ts[n].left, ts[n].right, ts[n].n, actual));
585
586        ONV(ts[n].expect < 0 && actual >= 0,
587            ("strncasecmp(%s, %s, %" NE_FMT_SIZE_T ") gave %d, expected < 0",
588             ts[n].left, ts[n].right, ts[n].n, actual));
589    }
590
591    ONN("comparison of identical pointers did not give zero",
592        ne_strncasecmp(ts[0].left, ts[0].left, 5) != 0);
593
594    return OK;
595}
596
597static int buf_print(void)
598{
599    ne_buffer *buf = ne_buffer_create();
600
601    ne_buffer_czappend(buf, "foo-");
602    ne_buffer_snprintf(buf, 20, "bar-%s-asda", "norman");
603    ne_buffer_czappend(buf, "-bloo");
604    ONN("snprintf return value", ne_buffer_snprintf(buf, 2, "---") != 1);
605
606    ONCMP(buf->data, "foo-bar-norman-asda-bloo-");
607
608    ne_buffer_destroy(buf);
609
610    return OK;
611}
612
613static int qappend(void)
614{
615    static const struct {
616        const char *in;
617        size_t inlen;
618        const char *out;
619    } ts[] = {
620        { "", 0, "" },
621        { "a", 1, "a" },
622        { "b", 2, "b\\x00" },
623        { "alpha\0alpha", 11, "alpha\\x00alpha" },
624        { "a\tb", 3, "a\\x09b" },
625        { NULL }
626    };
627    unsigned n;
628
629    for (n = 0; ts[n].in; n++) {
630        ne_buffer *buf = ne_buffer_create();
631        char *s;
632        const unsigned char *in = (const unsigned char *)ts[n].in;
633
634        ne_buffer_qappend(buf, in, ts[n].inlen);
635
636        ONCMP(buf->data, ts[n].out);
637
638        ONV(strlen(buf->data) + 1 != buf->used,
639            ("bad buffer length for '%s': %" NE_FMT_SIZE_T,
640             ts[n].out, buf->used));
641
642        s = ne_strnqdup(in, ts[n].inlen);
643
644        ONCMP(s, ts[n].out);
645
646        ne_free(s);
647        ne_buffer_destroy(buf);
648    }
649
650    return OK;
651}
652
653ne_test tests[] = {
654    T(simple),
655    T(buf_concat),
656    T(buf_concat2),
657    T(buf_concat3),
658    T(append),
659    T(grow),
660    T(alter),
661    T(token1),
662    T(token2),
663    T(nulls),
664    T(empty),
665    T(quoted),
666    T(badquotes),
667    T(shave),
668    T(shave_regress),
669    T(combo),
670    T(concat),
671    T(str_errors),
672    T(strnzcpy),
673    T(cleaner),
674    T(base64),
675    T(unbase64),
676    T(printing),
677    T(casecmp),
678    T(casencmp),
679    T(buf_print),
680    T(qappend),
681    T(NULL)
682};
683
684