1 /*
2 **  OSSP str - String Handling
3 **  Copyright (c) 1999-2005 Ralf S. Engelschall <rse@engelschall.com>
4 **  Copyright (c) 1999-2005 The OSSP Project <http://www.ossp.org/>
5 **
6 **  This file is part of OSSP str, a string handling and manipulation
7 **  library which can be found at http://www.ossp.org/pkg/lib/str/.
8 **
9 **  Permission to use, copy, modify, and distribute this software for
10 **  any purpose with or without fee is hereby granted, provided that
11 **  the above copyright notice and this permission notice appear in all
12 **  copies.
13 **
14 **  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
15 **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
18 **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
21 **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
24 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 **  SUCH DAMAGE.
26 **
27 **  str_test.c: test suite
28 */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #if defined(HAVE_DMALLOC_H) && defined(WITH_DMALLOC)
38 #include "dmalloc.h"
39 #endif
40 
41 #include "str.h"
42 #include "ts.h"
43 
44 #define LONG_STRING 1024
45 
46 /*
47  * Test String Length
48  */
49 
TS_TEST(test_length)50 TS_TEST(test_length)
51 {
52     ts_test_check(TS_CTX, "NULL handling");
53     if (str_len(NULL) != 0)
54         ts_test_fail(TS_CTX, "unexpected non-zero return");
55 
56     ts_test_check(TS_CTX, "empty string handling");
57     if (str_len("") != 0)
58         ts_test_fail(TS_CTX, "unexpected non-zero return");
59 
60     ts_test_check(TS_CTX, "short string handling");
61     if (str_len("a") != 1)
62         ts_test_fail(TS_CTX, "unexpected return != 1");
63 
64     ts_test_check(TS_CTX, "longer string handling");
65     if (str_len("foo bar quux") != 12)
66         ts_test_fail(TS_CTX, "unexpected return != 12");
67     return;
68 }
69 
70 /*
71  * Test String Locating
72  */
73 
74 struct {
75     char *s; str_size_t n; char *p; int o;
76 } loctab[] = {
77     { "foo bar quux", 0, "", 0 },
78     { "foo bar quux", 0, "foo", 0 },
79     { "foo bar quux", 0, "bar", 4 },
80     { "foo bar quux", 0, "quux", 8 },
81     { "foo bar quux", 0, "x", 11 },
82     { "foo bar quux", 3, "foo", 0 },
83     { "foo bar quux", 4, "x", -1 },
84     { "foo bar quux", 5, "bar", -1 },
85     { "foo bar", 0, "foo bar", 0 },
86     { "foo bar", 0, "foo bar quux", -1 },
87     { NULL, 0, NULL, 0 }
88 };
89 
TS_TEST(test_locate)90 TS_TEST(test_locate)
91 {
92     int i;
93     char *rv;
94 
95     for (i = 0; loctab[i].s != NULL; i++) {
96         rv = str_locate(loctab[i].s, loctab[i].n, loctab[i].p);
97         ts_test_check(TS_CTX, "str_locate(\"%s\", %d, \"%s\") = \"%s\"",
98                       loctab[i].s, loctab[i].n, loctab[i].p, rv == NULL ? "[NULL]" : rv);
99         if (!((rv-loctab[i].s == loctab[i].o) || (rv == NULL && loctab[i].o == -1)))
100             ts_test_fail(TS_CTX, "result was \"%s\", expected \"%s\"",
101                          rv, loctab[i].s+loctab[i].o);
102     }
103     return;
104 }
105 
106 /*
107  * Test String Spanning
108  */
109 
110 struct {
111     char *s; str_size_t n; char *cs; int m; int o;
112 } spantab[] = {
113     { "foo bar quux", 0, "", 0, 0 },
114     { "foo bar quux", 0, "xyz", 0, 0 },
115     { "foo bar quux", 0, "fo ", 0, 4 },
116     { "foo bar quux", 0, "b", STR_COMPLEMENT, 4 },
117     { "foo bar quux", 0, "", STR_COMPLEMENT, 12 },
118     { "foo bar quux", 0, "", STR_RIGHT, 11 },
119     { "foo bar quux", 0, "abc", STR_RIGHT, 11 },
120     { "foo bar quux", 0, "qux ", STR_RIGHT, 6 },
121     { "foo bar quux", 0, "r", STR_RIGHT|STR_COMPLEMENT, 6 },
122     { "foo bar quux", 0, "", STR_RIGHT|STR_COMPLEMENT, 0 },
123     { "", 0, "", STR_COMPLEMENT, 0 },
124     { "", 0, "", STR_RIGHT|STR_COMPLEMENT, 0 },
125     { NULL, 0, NULL, 0, 0 }
126 };
127 
TS_TEST(test_span)128 TS_TEST(test_span)
129 {
130     int i;
131     char *rv;
132 
133     for (i = 0; spantab[i].s != NULL; i++) {
134         rv = str_span(spantab[i].s, spantab[i].n, spantab[i].cs, spantab[i].m);
135         ts_test_check(TS_CTX, "str_span(\"%s\", %d, \"%s\", %d) = \"%s\"",
136                       spantab[i].s, spantab[i].n, spantab[i].cs, spantab[i].m, rv);
137         if (rv-spantab[i].s != spantab[i].o)
138             ts_test_fail(TS_CTX, "result was \"%s\", expected \"%s\"",
139                          rv, spantab[i].s+spantab[i].o);
140     }
141     return;
142 }
143 
144 /*
145  * Test String Tokenization
146  */
147 
148 struct {
149     char *s; char *d; char *q; char *c; int f; char *r[4];
150 } toktab[] = {
151     { "foo bar quux", " \t", "\"'", "#", 0, { "foo", "bar", "quux", NULL } },
152     { " foo \t \"bar \t b'az\" quux#vwxyz", " \t", "\"'", "#", STR_STRIPQUOTES, { "foo", "bar \t b'az", "quux", NULL } },
153     { NULL, NULL, NULL, NULL, 0, { NULL, NULL, NULL, NULL } }
154 };
155 
156 static char *prstr_tab[200];
157 static int prstr_idx = 0;
158 
prstr(char * s)159 static char *prstr(char *s)
160 {
161     char *cp;
162     char *p, *q;
163 
164     if (s == NULL)
165          return "NULL";
166     if ((cp = malloc(strlen(s)+20)) == NULL)
167          return "ERROR";
168     prstr_tab[prstr_idx++] = cp;
169     q = cp;
170     p = s;
171     *q++ = '"';
172     while (*p != NUL) {
173         switch (*p) {
174             case '\t': *q++ = '\\'; *q++ = 't'; p++; break;
175             case '\n': *q++ = '\\'; *q++ = 'n'; p++; break;
176             case '\r': *q++ = '\\'; *q++ = 'r'; p++; break;
177             case '"': *q++ = '\\'; *q++ = '"'; p++; break;
178             default:   *q++ = *p++;
179         }
180     }
181     *q++ = '"';
182     *q = NUL;
183     return cp;
184 }
185 
prstr_free(void)186 static void prstr_free(void)
187 {
188     while (prstr_idx > 0)
189         free(prstr_tab[--prstr_idx]);
190     return;
191 }
192 
TS_TEST(test_tokenize)193 TS_TEST(test_tokenize)
194 {
195     char *cp;
196     char *cp2;
197     char *cp3;
198     char *rc;
199     int i, j;
200 
201     for (i = 0; toktab[i].s != NULL; i++) {
202         ts_test_check(TS_CTX, "tokenization of \"%s\"\n", prstr(toktab[i].s));
203         prstr_free();
204         cp2 = cp = strdup(toktab[i].s);
205         for (j = 0; j < 4; j++) {
206             cp3 = strdup(cp);
207             rc = str_token(&cp, toktab[i].d, toktab[i].q, toktab[i].c, toktab[i].f);
208             ts_test_check(TS_CTX, "str_token(&%s, %s, %s, %s, %d) = %s",
209                           prstr(cp3), prstr(toktab[i].d),
210                           prstr(toktab[i].q), prstr(toktab[i].c),
211                           toktab[i].f, prstr(rc)), prstr_free();
212             free(cp3);
213             if (!(   (rc == NULL && toktab[i].r[j] == NULL)
214                   || (rc != NULL && toktab[i].r[j] != NULL && strcmp(rc, toktab[i].r[j]) == 0))) {
215                 ts_test_fail(TS_CTX, "expected result is \"%s\"", prstr(toktab[i].r[j]));
216                 prstr_free();
217             }
218         }
219         free(cp2);
220     }
221     return;
222 }
223 
224 /*
225  * Test String Parsing
226  */
227 
228 struct {
229     char *s; char *p; char *r1; char *r2; char *r3; char *r4; int rv;
230 } test2_tab[] = {
231     { "foobar", "foobar", NULL, NULL, NULL, NULL, 1 },
232     { "foobar", "m/^foobar$/", NULL, NULL, NULL, NULL, 1 },
233     { "foobar", "foobarquux", NULL, NULL, NULL, NULL, 0 },
234     { "foobar", "foo", NULL, NULL, NULL, NULL, 1 },
235     { "foobar", "?", NULL, NULL, NULL, NULL, -1 },
236     { "foobar", "m/(foo|bar)(bar|foo)/", "foo", "bar", NULL, NULL, 1 },
237     { "foobar", "m&(?:foo|bar)(?:bar|foo)&", NULL, NULL, NULL, NULL, 1 },
238     { "foobar", "m/(?:foo|bar)(?:bar|foo)/o", NULL, NULL, NULL, NULL, 1 },
239     { "foobar", "m/(?:FOO|BAR)(?:bar|foo)/io", NULL, NULL, NULL, NULL, 1 },
240     { "foobar", "((f(.)\\3)(b.*))", "foobar", "foo", "o", "bar", 1 },
241     { "foobar", "s/((f(.)\\3)(b.*))/$2-$4/io", "foo-bar", NULL, NULL, NULL, 1 },
242     { "foobar", "s/((f(.)\\3)(b.*))/$2-%s-$4/io", "foo-quux-bar", "quux", NULL, NULL, 1 },
243     { "foobar", "s/((f(.)\\3)(b.*))/$2-%s-%s-%s-$4/io", "foo-quux-baz-0815-bar", "quux", "baz", "0815", 1 },
244     { "foo:bar", "m/^(f[^:]+):(.*)$/", "foo", "bar", NULL, NULL, 1 },
245     { "foo:bar", "s/^([^:]+):(.*)$/$1-%s-$2/o", "foo-quux-bar", "quux", NULL, NULL, 1 },
246     { NULL, NULL, NULL, NULL, NULL, NULL, 0 }
247 };
248 
TS_TEST(test_parsing)249 TS_TEST(test_parsing)
250 {
251     int i;
252     int rv;
253     char *r1, *r2, *r3, *r4;
254 
255     for (i = 0; test2_tab[i].s != NULL; i++) {
256         ts_test_check(TS_CTX, "str_parse(\"%s\", \"%s\", ...)", test2_tab[i].s, test2_tab[i].p);
257         if (*(test2_tab[i].p) == 's') {
258             r1 = NULL;
259             r2 = test2_tab[i].r2;
260             r3 = test2_tab[i].r3;
261             r4 = test2_tab[i].r4;
262             rv = str_parse(test2_tab[i].s, test2_tab[i].p, &r1, r2, r3, r4);
263         }
264         else {
265             r1 = r2 = r3 = r4 = NULL;
266             rv = str_parse(test2_tab[i].s, test2_tab[i].p, &r1, &r2, &r3, &r4);
267         }
268         if (rv != test2_tab[i].rv ||
269             ((r1 == NULL && test2_tab[i].r1 != NULL) ||
270              (r1 != NULL && test2_tab[i].r1 == NULL) ||
271              (r1 != NULL && test2_tab[i].r1 != NULL && strcmp(r1, test2_tab[i].r1) != 0)) ||
272             ((r2 == NULL && test2_tab[i].r2 != NULL) ||
273              (r2 != NULL && test2_tab[i].r2 == NULL) ||
274              (r2 != NULL && test2_tab[i].r2 != NULL && strcmp(r2, test2_tab[i].r2) != 0)) ||
275             ((r3 == NULL && test2_tab[i].r3 != NULL) ||
276              (r3 != NULL && test2_tab[i].r3 == NULL) ||
277              (r3 != NULL && test2_tab[i].r3 != NULL && strcmp(r3, test2_tab[i].r3) != 0)) ||
278             ((r4 == NULL && test2_tab[i].r4 != NULL) ||
279              (r4 != NULL && test2_tab[i].r4 == NULL) ||
280              (r4 != NULL && test2_tab[i].r4 != NULL && strcmp(r4, test2_tab[i].r4) != 0))) {
281             ts_test_fail(TS_CTX, "expected result: %d + <%s><%s><%s><%s>",
282                          test2_tab[i].rv,
283                          test2_tab[i].r1 == NULL ? "NULL" : test2_tab[i].r1,
284                          test2_tab[i].r2 == NULL ? "NULL" : test2_tab[i].r2,
285                          test2_tab[i].r3 == NULL ? "NULL" : test2_tab[i].r3,
286                          test2_tab[i].r4 == NULL ? "NULL" : test2_tab[i].r4);
287         }
288         if (*(test2_tab[i].p) == 's') {
289             if (r1 != NULL) free(r1);
290         }
291         else {
292             if (r1 != NULL) free(r1);
293             if (r2 != NULL) free(r2);
294             if (r3 != NULL) free(r3);
295             if (r4 != NULL) free(r4);
296         }
297     }
298     str_parse(NULL, NULL);
299     return;
300 }
301 
302 /*
303  * Test String Formatting
304  */
305 
TS_TEST(test_formatting)306 TS_TEST(test_formatting)
307 {
308     char buf1[LONG_STRING];
309     char buf2[LONG_STRING];
310     char *fp_fmt[] = {
311         "%-1.5f", "%1.5f", "%123.9f", "%10.5f", "% 10.5f",
312         "%+22.9f", "%+4.9f", "%01.3f", "%4f",
313         "%3.1f", "%3.2f", "%.0f", "%.1f",
314         NULL
315     };
316     double fp_nums[] = {
317         -1.5, 134.21, 91340.2, 341.1234, 0203.9,
318         0.96, 0.996, 0.9996, 1.996, 4.136,
319         0
320     };
321     char *int_fmt[] = {
322         "%-1.5d", "%1.5d", "%123.9d", "%5.5d", "%10.5d",
323         "% 10.5d", "%+22.33d", "%01.3d", "%4d",
324 #if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG > 0)
325         "%12qd",
326 #endif
327         NULL
328     };
329 #if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG > 0)
330     long long
331 #else
332     long
333 #endif
334         int_nums[] = {
335         -1, 134, 91340, 341, 0203,
336         4294967290UL, /* less than 2^32 */
337 #if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG > 0)
338         4294967297ULL, /* more than 2^32 (actually 2^32 + 1) */
339 #endif
340         0
341     };
342     int x, y;
343     int len;
344 
345     ts_test_check(TS_CTX, "str_format vs. vendor sprintf comparison");
346 
347     for (x = 0; fp_fmt[x] != NULL; x++) {
348         ts_test_check(TS_CTX, "str_format(..,..,\"%s\",..)", fp_fmt[x]);
349         for (y = 0; fp_nums[y] != 0; y++) {
350             len = str_format(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
351             sprintf(buf2, fp_fmt[x], fp_nums[y]);
352             if (strcmp(buf1, buf2) != 0)
353                 ts_test_fail(TS_CTX, "mismatch: str_format: \"%s\", snprintf: \"%s\"", buf1, buf2);
354         }
355     }
356 
357     for (x = 0; int_fmt[x] != NULL; x++) {
358         ts_test_check(TS_CTX, "str_format(..,..,\"%s\",..)", int_fmt[x]);
359         for (y = 0; int_nums[y] != 0; y++) {
360             len = str_format(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
361             sprintf(buf2, int_fmt[x], int_nums[y]);
362             if (strcmp(buf1, buf2) != 0)
363                 ts_test_fail(TS_CTX, "mismatch: str_format: \"%s\", snprintf: \"%s\"", buf1, buf2);
364         }
365     }
366     return;
367 }
368 
369 /*
370  * Test Base64 Encoding/Decoding
371  */
372 
test_base64_do(ts_test_t * _t,unsigned char * ucp,int ulen,int mode)373 static void test_base64_do(ts_test_t *_t, unsigned char *ucp, int ulen, int mode)
374 {
375     unsigned char ucp2[1024];
376     char cp[1024];
377     int n1, n2, n3, n4;
378     int i;
379 
380     n1 = str_base64(NULL, 0, ucp, ulen, STR_BASE64_ENCODE|mode);
381     n2 = str_base64(cp, sizeof(cp), ucp, ulen, STR_BASE64_ENCODE|mode);
382     if (n1 != n2)
383         ts_test_fail(TS_CTX, "encoding length mismatch: %d vs. %d\n", n1, n2);
384     n3 = str_base64(cp, n2, NULL, 0, STR_BASE64_DECODE|mode);
385     if (n3 != ulen)
386         ts_test_fail(TS_CTX, "decoding check length mismatch: %d vs. %d\n", n3, ulen);
387     n4 = str_base64(cp, n2, ucp2, ulen, STR_BASE64_DECODE|mode);
388     if (n3 != n4)
389         ts_test_fail(TS_CTX, "decoding length mismatch: %d vs. %d\n", n3, n4);
390     for (i = 0; i < 256; i++) {
391         if (ucp[i] != ucp2[i]) {
392             ts_test_fail(TS_CTX, "decoding contents mismatch\n");
393             break;
394         }
395     }
396     return;
397 }
398 
TS_TEST(test_base64)399 TS_TEST(test_base64)
400 {
401     unsigned char ucp[256];
402     int i;
403 
404     ts_test_check(TS_CTX, "encode/decode of 0 bytes");
405     for (i = 0; i < 256; i++)
406         ucp[i] = 0x55;
407     test_base64_do(_t, ucp, 256, STR_BASE64_STRICT);
408 
409     ts_test_check(TS_CTX, "encode/decode of increasing bytes\n");
410     for (i = 0; i < 256; i++)
411         ucp[i] = i;
412     test_base64_do(_t, ucp, 256, STR_BASE64_STRICT);
413 
414     ts_test_check(TS_CTX, "encode/decode of distributed bytes\n");
415     for (i = 0; i < 256; i++)
416         ucp[i] = i*31;
417     test_base64_do(_t, ucp, 256, STR_BASE64_STRICT);
418 
419     return;
420 }
421 
422 /*
423  * Main Test Suite Procedure
424  */
425 
main(int argc,char * argv[])426 int main(int argc, char *argv[])
427 {
428     ts_suite_t *ts;
429     int n;
430 
431     ts = ts_suite_new("OSSP str (String Handling)");
432     ts_suite_test(ts, test_length,     "String Length Determination");
433     ts_suite_test(ts, test_locate,     "String Locating");
434     ts_suite_test(ts, test_span,       "String Spanning");
435     ts_suite_test(ts, test_tokenize,   "String Tokenizing");
436     ts_suite_test(ts, test_parsing,    "String Parsing");
437     ts_suite_test(ts, test_formatting, "String Formatting");
438     ts_suite_test(ts, test_base64,     "String Encoding/Decoding");
439     n = ts_suite_run(ts);
440     ts_suite_free(ts);
441     return n;
442 }
443 
444