// test just strchr()
//----------------------------------------------------------------------
// glibc-2.36/string/tester.c original from http://ftp.gnu.org/gnu/glibc/glibc-2.36.tar.xz
// added extra DEBUG printing and removed libc-diag.h dependency and all tests except:
// strspn, strcspn, strcat, strcpy, strncpy, strchr, strrchr, strstr, memcpy, memset,
// which are renamed to my_str... and my_mem... via rename.h
//
// generate tester.o using -w to disable warnings: gcc -c -w tester.c
// to get continuous messages as tests are passed: gcc -c -w tester.c -DDEBUG
//
// link with your code: gcc -std=c11 -pedantic -Wall p1.c tester.o
//----------------------------------------------------------------------
/* Tester for string functions.
Copyright (C) 1995-2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
/* Make sure we don't test the optimized inline functions if we want to
test the real implementation. */
#if !defined DO_STRING_INLINES
#undef __USE_STRING_INLINES
#endif
#include
#include
#include
#include "rename.h"
/* This file tests a range of corner cases of string functions,
including cases where truncation occurs or where sizes specified
are larger than the actual buffers, which result in various
warnings. */
#define STREQ(a, b) (strcmp((a), (b)) == 0)
const char *it = ""; /* Routine name for message routines. */
size_t errors = 0;
/* Complain if condition is not true. */
static void
check (int thing, int number)
{
if (!thing)
{
printf ("%s flunked test %d\n", it, number);
++errors;
}
#ifdef DEBUG
else
printf("%s passed test %d\n", it, number);
#endif
}
/* Complain if first two args don't strcmp as equal. */
static void
equal (const char *a, const char *b, int number)
{
check (a != NULL && b != NULL && STREQ (a, b), number);
}
char one[50];
char two[50];
char *cp;
#define SIMPLE_COPY(fn, n, str, ntest) \
do { \
int __n; \
char *cp; \
for (__n = 0; __n < (int) sizeof (one); ++__n) \
one[__n] = 'Z'; \
fn (one, str); \
for (cp = one, __n = 0; __n < n; ++__n, ++cp) \
check (*cp == '0' + (n % 10), ntest); \
check (*cp == '\0', ntest); \
} while (0)
static void
test_strcpy (void)
{
int i;
it = "strcpy";
check (strcpy (one, "abcd") == one, 1); /* Returned value. */
equal (one, "abcd", 2); /* Basic test. */
(void) strcpy (one, "x");
equal (one, "x", 3); /* Writeover. */
equal (one+2, "cd", 4); /* Wrote too much? */
(void) strcpy (two, "hi there");
(void) strcpy (one, two);
equal (one, "hi there", 5); /* Basic test encore. */
equal (two, "hi there", 6); /* Stomped on source? */
(void) strcpy (one, "");
equal (one, "", 7); /* Boundary condition. */
for (i = 0; i < 16; i++)
{
(void) strcpy (one + i, "hi there"); /* Unaligned destination. */
equal (one + i, "hi there", 8 + (i * 2));
(void) strcpy (two, one + i); /* Unaligned source. */
equal (two, "hi there", 9 + (i * 2));
}
SIMPLE_COPY(strcpy, 0, "", 41);
SIMPLE_COPY(strcpy, 1, "1", 42);
SIMPLE_COPY(strcpy, 2, "22", 43);
SIMPLE_COPY(strcpy, 3, "333", 44);
SIMPLE_COPY(strcpy, 4, "4444", 45);
SIMPLE_COPY(strcpy, 5, "55555", 46);
SIMPLE_COPY(strcpy, 6, "666666", 47);
SIMPLE_COPY(strcpy, 7, "7777777", 48);
SIMPLE_COPY(strcpy, 8, "88888888", 49);
SIMPLE_COPY(strcpy, 9, "999999999", 50);
SIMPLE_COPY(strcpy, 10, "0000000000", 51);
SIMPLE_COPY(strcpy, 11, "11111111111", 52);
SIMPLE_COPY(strcpy, 12, "222222222222", 53);
SIMPLE_COPY(strcpy, 13, "3333333333333", 54);
SIMPLE_COPY(strcpy, 14, "44444444444444", 55);
SIMPLE_COPY(strcpy, 15, "555555555555555", 56);
SIMPLE_COPY(strcpy, 16, "6666666666666666", 57);
/* Simple test using implicitly coerced `void *' arguments. */
const void *src = "frobozz";
void *dst = one;
check (strcpy (dst, src) == dst, 1);
equal (dst, "frobozz", 2);
}
static void
test_strcat (void)
{
it = "strcat";
(void) strcpy (one, "ijk");
check (strcat (one, "lmn") == one, 1); /* Returned value. */
equal (one, "ijklmn", 2); /* Basic test. */
(void) strcpy (one, "x");
(void) strcat (one, "yz");
equal (one, "xyz", 3); /* Writeover. */
equal (one+4, "mn", 4); /* Wrote too much? */
(void) strcpy (one, "gh");
(void) strcpy (two, "ef");
(void) strcat (one, two);
equal (one, "ghef", 5); /* Basic test encore. */
equal (two, "ef", 6); /* Stomped on source? */
(void) strcpy (one, "");
(void) strcat (one, "");
equal (one, "", 7); /* Boundary conditions. */
(void) strcpy (one, "ab");
(void) strcat (one, "");
equal (one, "ab", 8);
(void) strcpy (one, "");
(void) strcat (one, "cd");
equal (one, "cd", 9);
int ntest = 10;
char buf1[80] __attribute__ ((aligned (16)));
char buf2[32] __attribute__ ((aligned (16)));
for (size_t n1 = 0; n1 < 16; ++n1)
for (size_t n2 = 0; n2 < 16; ++n2)
for (size_t n3 = 0; n3 < 32; ++n3)
{
size_t olderrors = errors;
memset (buf1, 'b', sizeof (buf1));
memset (buf1 + n2, 'a', n3);
buf1[n2 + n3] = '\0';
strcpy (buf2 + n1, "123");
check (strcat (buf1 + n2, buf2 + n1) == buf1 + n2, ntest);
if (errors == olderrors)
for (size_t i = 0; i < sizeof (buf1); ++i)
{
if (i < n2)
check (buf1[i] == 'b', ntest);
else if (i < n2 + n3)
check (buf1[i] == 'a', ntest);
else if (i < n2 + n3 + 3)
check (buf1[i] == "123"[i - (n2 + n3)], ntest);
else if (i == n2 + n3 + 3)
check (buf1[i] == '\0', ntest);
else
check (buf1[i] == 'b', ntest);
if (errors != olderrors)
{
printf ("n1=%zu, n2=%zu, n3=%zu, buf1=%02hhx",
n1, n2, n3, buf1[0]);
for (size_t j = 1; j < sizeof (buf1); ++j)
printf (",%02hhx", buf1[j]);
putchar_unlocked ('\n');
break;
}
}
}
}
static void
test_strncpy (void)
{
/* Testing is a bit different because of odd semantics. */
it = "strncpy";
check (strncpy (one, "abc", 4) == one, 1); /* Returned value. */
equal (one, "abc", 2); /* Did the copy go right? */
(void) strcpy (one, "abcdefgh");
(void) strncpy (one, "xyz", 2);
equal (one, "xycdefgh", 3); /* Copy cut by count. */
(void) strcpy (one, "abcdefgh");
(void) strncpy (one, "xyz", 3); /* Copy cut just before NUL. */
equal (one, "xyzdefgh", 4);
(void) strcpy (one, "abcdefgh");
(void) strncpy (one, "xyz", 4); /* Copy just includes NUL. */
equal (one, "xyz", 5);
equal (one+4, "efgh", 6); /* Wrote too much? */
(void) strcpy (one, "abcdefgh");
(void) strncpy (one, "xyz", 5); /* Copy includes padding. */
equal (one, "xyz", 7);
equal (one+4, "", 8);
equal (one+5, "fgh", 9);
(void) strcpy (one, "abc");
(void) strncpy (one, "xyz", 0); /* Zero-length copy. */
equal (one, "abc", 10);
(void) strncpy (one, "", 2); /* Zero-length source. */
equal (one, "", 11);
equal (one+1, "", 12);
equal (one+2, "c", 13);
(void) strcpy (one, "hi there");
(void) strncpy (two, one, 9);
equal (two, "hi there", 14); /* Just paranoia. */
equal (one, "hi there", 15); /* Stomped on source? */
}
static void
test_strchr (void)
{
it = "strchr";
check (strchr ("abcd", 'z') == NULL, 1); /* Not found. */
(void) strcpy (one, "abcd");
check (strchr (one, 'c') == one+2, 2); /* Basic test. */
check (strchr (one, 'd') == one+3, 3); /* End of string. */
check (strchr (one, 'a') == one, 4); /* Beginning. */
check (strchr (one, '\0') == one+4, 5); /* Finding NUL. */
(void) strcpy (one, "ababa");
check (strchr (one, 'b') == one+1, 6); /* Finding first. */
(void) strcpy (one, "");
check (strchr (one, 'b') == NULL, 7); /* Empty string. */
check (strchr (one, '\0') == one, 8); /* NUL in empty string. */
{
char buf[4096];
int i;
char *p;
for (i=0; i < 0x100; i++)
{
p = (char *) ((unsigned long int) (buf + 0xff) & ~0xff) + i;
strcpy (p, "OK");
strcpy (p+3, "BAD/WRONG");
check (strchr (p, '/') == NULL, 9+i);
}
}
}
static void
test_strrchr (void)
{
it = "strrchr";
check (strrchr ("abcd", 'z') == NULL, 1); /* Not found. */
(void) strcpy (one, "abcd");
check (strrchr (one, 'c') == one+2, 2); /* Basic test. */
check (strrchr (one, 'd') == one+3, 3); /* End of string. */
check (strrchr (one, 'a') == one, 4); /* Beginning. */
check (strrchr (one, '\0') == one+4, 5); /* Finding NUL. */
(void) strcpy (one, "ababa");
check (strrchr (one, 'b') == one+3, 6); /* Finding last. */
(void) strcpy (one, "");
check (strrchr (one, 'b') == NULL, 7); /* Empty string. */
check (strrchr (one, '\0') == one, 8); /* NUL in empty string. */
{
char buf[4096];
int i;
char *p;
for (i=0; i < 0x100; i++)
{
p = (char *) ((unsigned long int) (buf + 0xff) & ~0xff) + i;
strcpy (p, "OK");
strcpy (p+3, "BAD/WRONG");
check (strrchr (p, '/') == NULL, 9+i);
}
}
}
static void
test_strstr (void)
{
it = "strstr";
check(strstr("abcd", "z") == NULL, 1); /* Not found. */
check(strstr("abcd", "abx") == NULL, 2); /* Dead end. */
(void) strcpy(one, "abcd");
check(strstr(one, "c") == one+2, 3); /* Basic test. */
check(strstr(one, "bc") == one+1, 4); /* Multichar. */
check(strstr(one, "d") == one+3, 5); /* End of string. */
check(strstr(one, "cd") == one+2, 6); /* Tail of string. */
check(strstr(one, "abc") == one, 7); /* Beginning. */
check(strstr(one, "abcd") == one, 8); /* Exact match. */
check(strstr(one, "abcde") == NULL, 9); /* Too long. */
check(strstr(one, "de") == NULL, 10); /* Past end. */
check(strstr(one, "") == one, 11); /* Finding empty. */
(void) strcpy(one, "ababa");
check(strstr(one, "ba") == one+1, 12); /* Finding first. */
(void) strcpy(one, "");
check(strstr(one, "b") == NULL, 13); /* Empty string. */
check(strstr(one, "") == one, 14); /* Empty in empty string. */
(void) strcpy(one, "bcbca");
check(strstr(one, "bca") == one+2, 15); /* False start. */
(void) strcpy(one, "bbbcabbca");
check(strstr(one, "bbca") == one+1, 16); /* With overlap. */
}
static void
test_strspn (void)
{
it = "strspn";
check(strspn("abcba", "abc") == 5, 1); /* Whole string. */
check(strspn("abcba", "ab") == 2, 2); /* Partial. */
check(strspn("abc", "qx") == 0, 3); /* None. */
check(strspn("", "ab") == 0, 4); /* Null string. */
check(strspn("abc", "") == 0, 5); /* Null search list. */
}
static void
test_strcspn (void)
{
it = "strcspn";
check(strcspn("abcba", "qx") == 5, 1); /* Whole string. */
check(strcspn("abcba", "cx") == 2, 2); /* Partial. */
check(strcspn("abc", "abc") == 0, 3); /* None. */
check(strcspn("", "ab") == 0, 4); /* Null string. */
check(strcspn("abc", "") == 3, 5); /* Null search list. */
}
static void
test_memcpy (void)
{
int i;
it = "memcpy";
check(memcpy(one, "abc", 4) == one, 1); /* Returned value. */
equal(one, "abc", 2); /* Did the copy go right? */
(void) strcpy(one, "abcdefgh");
(void) memcpy(one+1, "xyz", 2);
equal(one, "axydefgh", 3); /* Basic test. */
(void) strcpy(one, "abc");
(void) memcpy(one, "xyz", 0);
equal(one, "abc", 4); /* Zero-length copy. */
(void) strcpy(one, "hi there");
(void) strcpy(two, "foo");
(void) memcpy(two, one, 9);
equal(two, "hi there", 5); /* Just paranoia. */
equal(one, "hi there", 6); /* Stomped on source? */
for (i = 0; i < 16; i++)
{
const char *x = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
strcpy (one, x);
check (memcpy (one + i, "hi there", 9) == one + i,
7 + (i * 6)); /* Unaligned destination. */
check (memcmp (one, x, i) == 0, 8 + (i * 6)); /* Wrote under? */
equal (one + i, "hi there", 9 + (i * 6));
check (one[i + 9] == 'x', 10 + (i * 6)); /* Wrote over? */
check (memcpy (two, one + i, 9) == two,
11 + (i * 6)); /* Unaligned source. */
equal (two, "hi there", 12 + (i * 6));
}
}
static void
test_memset (void)
{
int i;
it = "memset";
(void) strcpy(one, "abcdefgh");
check(memset(one+1, 'x', 3) == one+1, 1); /* Return value. */
equal(one, "axxxefgh", 2); /* Basic test. */
(void) memset(one+2, 'y', 0);
equal(one, "axxxefgh", 3); /* Zero-length set. */
(void) memset(one+5, 0, 1);
equal(one, "axxxe", 4); /* Zero fill. */
equal(one+6, "gh", 5); /* And the leftover. */
(void) memset(one+2, 010045, 1);
equal(one, "ax\045xe", 6); /* Unsigned char convert. */
/* Non-8bit fill character. */
memset (one, 0x101, sizeof (one));
for (i = 0; i < (int) sizeof (one); ++i)
check (one[i] == '\01', 7);
/* Test for more complex versions of memset, for all alignments and
lengths up to 256. This test takes a little while, perhaps it should
be made weaker? */
{
char data[512];
int j;
int k;
int c;
for (i = 0; i < 512; i++)
data[i] = 'x';
for (c = 0; c <= 'y'; c += 'y') /* check for memset(,0,) and
memset(,'y',) */
for (j = 0; j < 256; j++)
for (i = 0; i < 256; i++)
{
memset (data + i, c, j);
for (k = 0; k < i; k++)
if (data[k] != 'x')
goto fail;
for (k = i; k < i+j; k++)
{
if (data[k] != c)
goto fail;
data[k] = 'x';
}
for (k = i+j; k < 512; k++)
if (data[k] != 'x')
goto fail;
continue;
fail:
check (0, 8 + i + j * 256 + (c != 0) * 256 * 256);
}
}
}
int
main (void)
{
int status;
#if 0
/* Test strcpy next because we need it to set up other tests. */
test_strcpy ();
/* strcat. */
test_strcat ();
/* strncpy. */
test_strncpy ();
/* strchr. */
test_strchr ();
/* strrchr. */
test_strrchr ();
/* strstr - somewhat like strchr. */
test_strstr ();
/* strspn. */
test_strspn ();
/* strcspn. */
test_strcspn ();
/* memcpy - need not work for overlap. */
test_memcpy ();
/* memset. */
test_memset ();
#endif
// test_strchr ();
// is char signed or unsigned?
// signed char: -128 ... 127
// unsigned char: 0 ... 255
char buf[] = { 'a'+128, 0 };
check (strchr (buf, 'a'+128) == buf, 999);
if (errors == 0)
{
status = EXIT_SUCCESS;
puts("No errors.");
}
else
{
status = EXIT_FAILURE;
printf("%Zd errors.\n", errors);
}
return status;
}
/* sample run:
$ make
gcc -c -w tester.c # -DDEBUG
gcc -std=c11 -pedantic -Wall -o p1 p1.c tester.o
$ ./p1
c = 225
c = -31
No errors.
$
*/