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