root/tests/lib/libc/string/t_strchrnul.c
/* $NetBSD: t_strchrnul.c,v 1.1 2023/01/30 19:49:49 christos Exp $ */

/*
 * Written by J.T. Conklin <jtc@acorntoolworks.com>
 * Public domain.
 */

#include <atf-c.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

static char     *slow_strchrnul(char *, int);
static void      verify_strchrnul(char *, int, unsigned int, unsigned int);

char * (*volatile strchrnul_fn)(const char *, int);

static char *
slow_strchrnul(char *buf, int ch)
{
        unsigned char c = 1;

        ch &= 0xff;

        for (; ; buf++) {
                c = *buf;
                if (c == ch || c == 0)
                        return buf;
        }
}

static void
verify_strchrnul(char *buf, int ch, unsigned int t, unsigned int a)
{
        const char *off, *ok_off;

        off = strchrnul_fn(buf, ch);
        ok_off = slow_strchrnul(buf, ch);
        if (off == ok_off)
                return;

        fprintf(stderr, "test_strchrnul(\"%s\", %#x) gave %zd not %zd (test %d, "
            "alignment %d)\n",
            buf, ch, off ? off - buf : -1, ok_off ? ok_off - buf : -1, t, a);

        atf_tc_fail("Check stderr for details");
}

ATF_TC(strchrnul_basic);
ATF_TC_HEAD(strchrnul_basic, tc)
{

        atf_tc_set_md_var(tc, "descr", "Test strchrnul(3) results");
}

ATF_TC_BODY(strchrnul_basic, tc)
{
        void *dl_handle;
        char *off;
        char buf[32];
        unsigned int t, a;

        const char *tab[] = {
                "",
                "a",
                "aa",
                "abc",
                "abcd",
                "abcde",
                "abcdef",
                "abcdefg",
                "abcdefgh",

                "/",
                "//",
                "/a",
                "/a/",
                "/ab",
                "/ab/",
                "/abc",
                "/abc/",
                "/abcd",
                "/abcd/",
                "/abcde",
                "/abcde/",
                "/abcdef",
                "/abcdef/",
                "/abcdefg",
                "/abcdefg/",
                "/abcdefgh",
                "/abcdefgh/",

                "a/",
                "a//",
                "a/a",
                "a/a/",
                "a/ab",
                "a/ab/",
                "a/abc",
                "a/abc/",
                "a/abcd",
                "a/abcd/",
                "a/abcde",
                "a/abcde/",
                "a/abcdef",
                "a/abcdef/",
                "a/abcdefg",
                "a/abcdefg/",
                "a/abcdefgh",
                "a/abcdefgh/",

                "ab/",
                "ab//",
                "ab/a",
                "ab/a/",
                "ab/ab",
                "ab/ab/",
                "ab/abc",
                "ab/abc/",
                "ab/abcd",
                "ab/abcd/",
                "ab/abcde",
                "ab/abcde/",
                "ab/abcdef",
                "ab/abcdef/",
                "ab/abcdefg",
                "ab/abcdefg/",
                "ab/abcdefgh",
                "ab/abcdefgh/",

                "abc/",
                "abc//",
                "abc/a",
                "abc/a/",
                "abc/ab",
                "abc/ab/",
                "abc/abc",
                "abc/abc/",
                "abc/abcd",
                "abc/abcd/",
                "abc/abcde",
                "abc/abcde/",
                "abc/abcdef",
                "abc/abcdef/",
                "abc/abcdefg",
                "abc/abcdefg/",
                "abc/abcdefgh",
                "abc/abcdefgh/",

                "abcd/",
                "abcd//",
                "abcd/a",
                "abcd/a/",
                "abcd/ab",
                "abcd/ab/",
                "abcd/abc",
                "abcd/abc/",
                "abcd/abcd",
                "abcd/abcd/",
                "abcd/abcde",
                "abcd/abcde/",
                "abcd/abcdef",
                "abcd/abcdef/",
                "abcd/abcdefg",
                "abcd/abcdefg/",
                "abcd/abcdefgh",
                "abcd/abcdefgh/",

                "abcde/",
                "abcde//",
                "abcde/a",
                "abcde/a/",
                "abcde/ab",
                "abcde/ab/",
                "abcde/abc",
                "abcde/abc/",
                "abcde/abcd",
                "abcde/abcd/",
                "abcde/abcde",
                "abcde/abcde/",
                "abcde/abcdef",
                "abcde/abcdef/",
                "abcde/abcdefg",
                "abcde/abcdefg/",
                "abcde/abcdefgh",
                "abcde/abcdefgh/",

                "abcdef/",
                "abcdef//",
                "abcdef/a",
                "abcdef/a/",
                "abcdef/ab",
                "abcdef/ab/",
                "abcdef/abc",
                "abcdef/abc/",
                "abcdef/abcd",
                "abcdef/abcd/",
                "abcdef/abcde",
                "abcdef/abcde/",
                "abcdef/abcdef",
                "abcdef/abcdef/",
                "abcdef/abcdefg",
                "abcdef/abcdefg/",
                "abcdef/abcdefgh",
                "abcdef/abcdefgh/",

                "abcdefg/",
                "abcdefg//",
                "abcdefg/a",
                "abcdefg/a/",
                "abcdefg/ab",
                "abcdefg/ab/",
                "abcdefg/abc",
                "abcdefg/abc/",
                "abcdefg/abcd",
                "abcdefg/abcd/",
                "abcdefg/abcde",
                "abcdefg/abcde/",
                "abcdefg/abcdef",
                "abcdefg/abcdef/",
                "abcdefg/abcdefg",
                "abcdefg/abcdefg/",
                "abcdefg/abcdefgh",
                "abcdefg/abcdefgh/",

                "abcdefgh/",
                "abcdefgh//",
                "abcdefgh/a",
                "abcdefgh/a/",
                "abcdefgh/ab",
                "abcdefgh/ab/",
                "abcdefgh/abc",
                "abcdefgh/abc/",
                "abcdefgh/abcd",
                "abcdefgh/abcd/",
                "abcdefgh/abcde",
                "abcdefgh/abcde/",
                "abcdefgh/abcdef",
                "abcdefgh/abcdef/",
                "abcdefgh/abcdefg",
                "abcdefgh/abcdefg/",
                "abcdefgh/abcdefgh",
                "abcdefgh/abcdefgh/",
        };

        dl_handle = dlopen(NULL, RTLD_LAZY);
        strchrnul_fn = dlsym(dl_handle, "test_strchrnul");
        if (!strchrnul_fn)
                strchrnul_fn = strchrnul;

        for (a = 3; a < 3 + sizeof(long); ++a) {
                /* Put char and a \0 before the buffer */
                buf[a-1] = '/';
                buf[a-2] = '0';
                buf[a-3] = 0xff;
                for (t = 0; t < (sizeof(tab) / sizeof(tab[0])); ++t) {
                        int len = strlen(tab[t]) + 1;
                        memcpy(&buf[a], tab[t], len);

                        /* Put the char we are looking for after the \0 */
                        buf[a + len] = '/';

                        /* Check search for NUL at end of string */
                        verify_strchrnul(buf + a, 0, t, a);

                        /* Then for the '/' in the strings */
                        verify_strchrnul(buf + a, '/', t, a);

                        /* check zero extension of char arg */
                        verify_strchrnul(buf + a, 0xffffff00 | '/', t, a);

                        /* Replace all the '/' with 0xff */
                        while (*(off = slow_strchrnul(buf + a, '/')) != '\0')
                                *off = 0xff;

                        buf[a + len] = 0xff;

                        /* Check we can search for 0xff as well as '/' */
                        verify_strchrnul(buf + a, 0xff, t, a);
                }
        }
        (void)dlclose(dl_handle);
}

ATF_TP_ADD_TCS(tp)
{

        ATF_TP_ADD_TC(tp, strchrnul_basic);

        return atf_no_error();
}