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