aboutsummaryrefslogtreecommitdiff
path: root/lib/libc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc')
-rw-r--r--lib/libc/gen/Makefile.inc1
-rw-r--r--lib/libc/gen/h_ctype_abuse.c138
-rw-r--r--lib/libc/gen/h_execsig.c55
-rw-r--r--lib/libc/gen/t_arc4random.c670
-rw-r--r--lib/libc/gen/t_ctype.c1236
-rw-r--r--lib/libc/gen/t_timespec_get.c123
-rw-r--r--lib/libc/locale/t_c16rtomb.c287
-rw-r--r--lib/libc/locale/t_c32rtomb.c60
-rw-r--r--lib/libc/locale/t_c8rtomb.c355
-rw-r--r--lib/libc/locale/t_mbrtoc16.c364
-rw-r--r--lib/libc/locale/t_mbrtoc32.c61
-rw-r--r--lib/libc/locale/t_mbrtoc8.c415
-rw-r--r--lib/libc/locale/t_uchar.c81
-rw-r--r--lib/libc/misc/t_vis.c165
-rw-r--r--lib/libc/regex/t_regex_binary.c80
-rw-r--r--lib/libc/setjmp/t_sigstack.c389
-rw-r--r--lib/libc/ssp/h_getcwd2.c18
-rw-r--r--lib/libc/stdlib/h_sort.c225
-rw-r--r--lib/libc/stdlib/t_sort.sh115
-rw-r--r--lib/libc/sys/t_aio_cancel.c223
-rw-r--r--lib/libc/sys/t_aio_lio.c264
-rw-r--r--lib/libc/sys/t_aio_rw.c167
-rw-r--r--lib/libc/sys/t_aio_suspend.c170
-rw-r--r--lib/libc/sys/t_ptrace_kill.c131
24 files changed, 5793 insertions, 0 deletions
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
new file mode 100644
index 000000000000..01b5f23410c8
--- /dev/null
+++ b/lib/libc/gen/Makefile.inc
@@ -0,0 +1 @@
+.include "../Makefile.inc"
diff --git a/lib/libc/gen/h_ctype_abuse.c b/lib/libc/gen/h_ctype_abuse.c
new file mode 100644
index 000000000000..fdff8552f8f4
--- /dev/null
+++ b/lib/libc/gen/h_ctype_abuse.c
@@ -0,0 +1,138 @@
+/* $NetBSD: h_ctype_abuse.c,v 1.2 2025/09/15 17:32:01 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Helper program to verify effects of ctype(3) abuse.
+ *
+ * NOTE: This program intentionally triggers undefined behaviour by
+ * passing int values to the ctype(3) functions which are neither EOF
+ * nor representable by unsigned char. The purpose is to verify that
+ * NetBSD's ctype(3) _does not_ trap the undefined behaviour when the
+ * environment variable LIBC_ALLOWCTYPEABUSE. (It does not check
+ * anything about the results, which are perforce nonsense -- just that
+ * it gets a result without crashing.)
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_ctype_abuse.c,v 1.2 2025/09/15 17:32:01 riastradh Exp $");
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define FOREACHCTYPE(M) \
+ M(ISALPHA, isalpha) \
+ M(ISUPPER, isupper) \
+ M(ISLOWER, islower) \
+ M(ISDIGIT, isdigit) \
+ M(ISXDIGIT, isxdigit) \
+ M(ISALNUM, isalnum) \
+ M(ISSPACE, isspace) \
+ M(ISPUNCT, ispunct) \
+ M(ISPRINT, isprint) \
+ M(ISGRAPH, isgraph) \
+ M(ISCNTRL, iscntrl) \
+ M(ISBLANK, isblank) \
+ M(TOUPPER, toupper) \
+ M(TOLOWER, tolower)
+
+int
+main(int argc, char **argv)
+{
+ enum {
+#define M(upper, lower) upper,
+ FOREACHCTYPE(M)
+#undef M
+ } fn;
+ enum {
+ MACRO,
+ FUNCTION,
+ } mode;
+ int ch;
+ volatile int result;
+
+ setprogname(argv[0]);
+ if (argc != 3 && argc != 4) {
+ errx(1, "Usage: %s <function> <mode> [<locale>]",
+ getprogname());
+ }
+
+#define M(upper, lower) \
+ else if (strcmp(argv[1], #lower) == 0) \
+ fn = upper;
+
+ if (0)
+ ;
+ FOREACHCTYPE(M)
+ else
+ errx(1, "unknown ctype function");
+#undef M
+
+ if (strcmp(argv[2], "macro") == 0)
+ mode = MACRO;
+ else if (strcmp(argv[2], "function") == 0)
+ mode = FUNCTION;
+ else
+ errx(1, "unknown usage mode");
+
+ if (argc == 4) {
+ if (setlocale(LC_CTYPE, argv[3]) == NULL)
+ err(1, "setlocale");
+ }
+
+ /*
+ * Make sure we cover EOF as well.
+ */
+ __CTASSERT(CHAR_MIN == 0 || CHAR_MIN <= EOF);
+ __CTASSERT(EOF <= UCHAR_MAX);
+
+ for (ch = (CHAR_MIN == 0 ? EOF : CHAR_MIN); ch <= UCHAR_MAX; ch++) {
+ switch (fn) {
+#define M(upper, lower) \
+ case upper: \
+ switch (mode) { \
+ case MACRO: \
+ result = lower(ch); \
+ break; \
+ case FUNCTION: \
+ result = (lower)(ch); \
+ break; \
+ } \
+ break;
+ FOREACHCTYPE(M)
+#undef M
+ }
+ (void)result;
+ }
+
+ return 0;
+}
diff --git a/lib/libc/gen/h_execsig.c b/lib/libc/gen/h_execsig.c
new file mode 100644
index 000000000000..a954ca071094
--- /dev/null
+++ b/lib/libc/gen/h_execsig.c
@@ -0,0 +1,55 @@
+/* $NetBSD: h_execsig.c,v 1.1 2025/03/13 01:27:27 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_execsig.c,v 1.1 2025/03/13 01:27:27 riastradh Exp $");
+
+/*
+ * Helper program for testing signal delivery during execve(2) and
+ * posix_spawn(2). The caller will:
+ *
+ * 1. fork and exec, or spawn
+ * 2. kill the child
+ * 3. write a byte to be read from the child's stdin
+ *
+ * Since the caller issues syscalls in that order, the signal should be
+ * delivered to the child first, and it should be interrupted by a
+ * signal before it returnsa byte from read(2).
+ */
+
+#include <err.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+
+ if (read(STDIN_FILENO, (char[]){0}, 1) == -1)
+ err(1, "read");
+ return 0;
+}
diff --git a/lib/libc/gen/t_arc4random.c b/lib/libc/gen/t_arc4random.c
new file mode 100644
index 000000000000..b26c1b8a931a
--- /dev/null
+++ b/lib/libc/gen/t_arc4random.c
@@ -0,0 +1,670 @@
+/* $NetBSD: t_arc4random.c,v 1.5 2025/03/09 18:11:55 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _REENTRANT
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_arc4random.c,v 1.5 2025/03/09 18:11:55 riastradh Exp $");
+
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "arc4random.h"
+#include "reentrant.h"
+#include "h_macros.h"
+
+/*
+ * iszero(buf, len)
+ *
+ * True if len bytes at buf are all zero, false if any one of them
+ * is nonzero.
+ */
+static bool
+iszero(const void *buf, size_t len)
+{
+ const unsigned char *p = buf;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (p[i] != 0)
+ return false;
+ }
+ return true;
+}
+
+/*
+ * arc4random_prng()
+ *
+ * Get a pointer to the current arc4random state, without updating
+ * any of the state, not even lazy initialization.
+ */
+static struct arc4random_prng *
+arc4random_prng(void)
+{
+ struct arc4random_prng *prng = NULL;
+
+ /*
+ * If arc4random has been initialized and there is a thread key
+ * (i.e., libc was built with _REENTRANT), get the thread-local
+ * arc4random state if there is one.
+ */
+ if (arc4random_global.per_thread)
+ prng = thr_getspecific(arc4random_global.thread_key);
+
+ /*
+ * If we couldn't get the thread-local state, get the global
+ * state instead.
+ */
+ if (prng == NULL)
+ prng = &arc4random_global.prng;
+
+ return prng;
+}
+
+/*
+ * arc4random_global_buf(buf, len)
+ *
+ * Same as arc4random_buf, but force use of the global state.
+ * Must happen before any other use of arc4random.
+ */
+static void
+arc4random_global_buf(void *buf, size_t len)
+{
+ struct rlimit rlim, orlim;
+ struct arc4random_prng *prng;
+
+ /*
+ * Save the address space limit.
+ */
+ RL(getrlimit(RLIMIT_AS, &orlim));
+ memcpy(&rlim, &orlim, sizeof(rlim));
+
+ /*
+ * Get a sample while the address space limit is zero. This
+ * should try, and fail, to allocate a thread-local arc4random
+ * state with mmap(2).
+ */
+ rlim.rlim_cur = 0;
+ RL(setrlimit(RLIMIT_AS, &rlim));
+ arc4random_buf(buf, len);
+ RL(setrlimit(RLIMIT_AS, &orlim));
+
+ /*
+ * Restore the address space limit.
+ */
+ RL(setrlimit(RLIMIT_AS, &orlim));
+
+ /*
+ * Verify the PRNG is the global one, not the thread-local one,
+ * and that it was initialized.
+ */
+ prng = arc4random_prng();
+ ATF_CHECK_EQ(prng, &arc4random_global.prng);
+ ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng)));
+ ATF_CHECK(prng->arc4_epoch != 0);
+}
+
+/*
+ * arc4random_global_thread(cookie)
+ *
+ * Start routine for a thread that just grabs an output from the
+ * global state.
+ */
+static void *
+arc4random_global_thread(void *cookie)
+{
+ unsigned char buf[32];
+
+ arc4random_global_buf(buf, sizeof(buf));
+
+ return NULL;
+}
+
+ATF_TC(addrandom);
+ATF_TC_HEAD(addrandom, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random_addrandom updates the state");
+}
+ATF_TC_BODY(addrandom, tc)
+{
+ unsigned char buf[32], zero32[32] = {0};
+ struct arc4random_prng *prng, copy;
+
+ /*
+ * Get a sample to start things off.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * By this point, the global state must be initialized -- if
+ * not, the process should have aborted.
+ */
+ ATF_CHECK(arc4random_global.initialized);
+
+ /*
+ * Get the PRNG, global or local. By this point, the PRNG
+ * state should be nonzero (with overwhelmingly high
+ * probability) and the epoch should also be nonzero.
+ */
+ prng = arc4random_prng();
+ ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng)));
+ ATF_CHECK(prng->arc4_epoch != 0);
+
+ /*
+ * Save a copy and update the state with arc4random_addrandom.
+ */
+ copy = *prng;
+ arc4random_addrandom(zero32, sizeof(zero32));
+
+ /*
+ * The state should have changed. (The epoch may or may not.)
+ */
+ ATF_CHECK(memcmp(&prng->arc4_prng, &copy.arc4_prng,
+ sizeof(copy.arc4_prng)) != 0);
+
+ /*
+ * Save a copy and update the state with arc4random_stir.
+ */
+ copy = *prng;
+ arc4random_stir();
+
+ /*
+ * The state should have changed. (The epoch may or may not.)
+ */
+ ATF_CHECK(memcmp(&prng->arc4_prng, &copy.arc4_prng,
+ sizeof(copy.arc4_prng)) != 0);
+}
+
+ATF_TC(consolidate);
+ATF_TC_HEAD(consolidate, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test consolidating entropy resets the epoch");
+}
+ATF_TC_BODY(consolidate, tc)
+{
+ unsigned char buf[32];
+ struct arc4random_prng *local, *global = &arc4random_global.prng;
+ unsigned localepoch, globalepoch;
+ const int consolidate = 1;
+ pthread_t thread;
+
+ /*
+ * Get a sample from the global state to make sure the global
+ * state is initialized. Remember the epoch.
+ */
+ arc4random_global_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(!iszero(&global->arc4_prng, sizeof(global->arc4_prng)));
+ ATF_CHECK((globalepoch = global->arc4_epoch) != 0);
+
+ /*
+ * Get a sample from the local state too to make sure the local
+ * state is initialized. Remember the epoch.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ local = arc4random_prng();
+ ATF_CHECK(!iszero(&local->arc4_prng, sizeof(local->arc4_prng)));
+ ATF_CHECK((localepoch = local->arc4_epoch) != 0);
+
+ /*
+ * Trigger entropy consolidation.
+ */
+ RL(sysctlbyname("kern.entropy.consolidate", /*oldp*/NULL, /*oldlen*/0,
+ &consolidate, sizeof(consolidate)));
+
+ /*
+ * Verify the epoch cache isn't changed yet until we ask for
+ * more data.
+ */
+ ATF_CHECK_EQ_MSG(globalepoch, global->arc4_epoch,
+ "global epoch was %u, now %u", globalepoch, global->arc4_epoch);
+ ATF_CHECK_EQ_MSG(localepoch, local->arc4_epoch,
+ "local epoch was %u, now %u", localepoch, local->arc4_epoch);
+
+ /*
+ * Request new output and verify the local epoch cache has
+ * changed.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK_MSG(localepoch != local->arc4_epoch,
+ "local epoch unchanged from %u", localepoch);
+
+ /*
+ * Create a new thread to grab output from the global state,
+ * wait for it to complete, and verify the global epoch cache
+ * has changed. (Now that we have already used the local state
+ * in this thread, we can't use the global state any more.)
+ */
+ RZ(pthread_create(&thread, NULL, &arc4random_global_thread, NULL));
+ RZ(pthread_join(thread, NULL));
+ ATF_CHECK_MSG(globalepoch != global->arc4_epoch,
+ "global epoch unchanged from %u", globalepoch);
+}
+
+ATF_TC(chroot);
+ATF_TC_HEAD(chroot, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random in an empty chroot");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(chroot, tc)
+{
+ pid_t pid;
+ int status;
+
+ /*
+ * Create an empty chroot.
+ */
+ RL(mkdir("root", 0500));
+
+ /*
+ * In a child process, enter the chroot and verify that we
+ * can't open /dev/urandom but we can use arc4random.
+ *
+ * (atf gets unhappy if we chroot in the same process, when it
+ * later tries to create a results file.)
+ */
+ RL(pid = fork());
+ if (pid == 0) {
+ unsigned char buf[32] = {0};
+
+ if (chroot("root") == -1)
+ err(1, "chroot");
+ if (open(_PATH_URANDOM, O_RDONLY) != -1)
+ errx(1, "open /dev/urandom must fail in empty chroot");
+ if (errno != ENOENT) {
+ err(1, "expected open to fail with %d=ENOENT, not %d",
+ ENOENT, errno);
+ }
+ arc4random_buf(buf, sizeof(buf));
+ if (iszero(buf, sizeof(buf))) /* Pr[fail] = 1/2^256 */
+ errx(1, "arc4random returned all-zero");
+ if (arc4random_prng()->arc4_epoch == 0)
+ errx(1, "arc4random failed to observe entropy epoch");
+ _exit(0);
+ }
+
+ /*
+ * Wait for the child process to finish.
+ */
+ RL(waitpid(pid, &status, 0));
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+ "child exited status 0x%x", status);
+}
+
+ATF_TC(fdlimit);
+ATF_TC_HEAD(fdlimit, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random works even if we have hit the fd limit");
+}
+ATF_TC_BODY(fdlimit, tc)
+{
+ pid_t pid;
+ int status;
+
+ /*
+ * In a child process, clamp down on the file descriptor
+ * resource limit and verify that we can't open /dev/urandom
+ * but we can use arc4random.
+ *
+ * (atf gets unhappy if we chroot in the same process, when it
+ * later tries to create a results file.)
+ */
+ RL(pid = fork());
+ if (pid == 0) {
+ struct rlimit rlim = {.rlim_cur = 0, .rlim_max = 0};
+ unsigned char buf[32] = {0};
+
+ if (setrlimit(RLIMIT_NOFILE, &rlim) == -1)
+ err(1, "setrlimit(RLIMIT_NOFILE)");
+ if (open(_PATH_URANDOM, O_RDONLY) != -1)
+ errx(1, "open must fail with zero RLIMIT_NOFILE");
+ if (errno != EMFILE) {
+ err(1, "expected open to fail with %d=EMFILE, not %d",
+ EMFILE, errno);
+ }
+ arc4random_buf(buf, sizeof(buf));
+ if (iszero(buf, sizeof(buf))) /* Pr[fail] = 1/2^256 */
+ errx(1, "arc4random returned all-zero");
+ if (arc4random_prng()->arc4_epoch == 0)
+ errx(1, "arc4random failed to observe entropy epoch");
+ _exit(0);
+ }
+
+ /*
+ * Wait for the child process to finish.
+ */
+ RL(waitpid(pid, &status, 0));
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+ "child exited status 0x%x", status);
+}
+
+ATF_TC(fork);
+ATF_TC_HEAD(fork, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test fork zeros the state and gets independent state");
+}
+ATF_TC_BODY(fork, tc)
+{
+ unsigned char buf[32];
+ struct arc4random_prng *local, *global = &arc4random_global.prng;
+ struct arc4random_prng childstate;
+ int fd[2];
+ pid_t child, pid;
+ ssize_t nread;
+ int status;
+
+ /*
+ * Get a sample from the global state to make sure the global
+ * state is initialized.
+ */
+ arc4random_global_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(!iszero(&global->arc4_prng, sizeof(global->arc4_prng)));
+ ATF_CHECK(global->arc4_epoch != 0);
+
+ /*
+ * Get a sample from the local state too to make sure the local
+ * state is initialized.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ local = arc4random_prng();
+ ATF_CHECK(!iszero(&local->arc4_prng, sizeof(local->arc4_prng)));
+ ATF_CHECK(local->arc4_epoch != 0);
+
+ /*
+ * Create a pipe to transfer the state from child to parent.
+ */
+ RL(pipe(fd));
+
+ /*
+ * Fork a child.
+ */
+ RL(child = fork());
+ if (child == 0) {
+ status = 0;
+
+ /*
+ * Verify the states have been zero'd on fork.
+ */
+ if (!iszero(local, sizeof(*local))) {
+ fprintf(stderr, "failed to zero local state\n");
+ status = 1;
+ }
+ if (!iszero(global, sizeof(*global))) {
+ fprintf(stderr, "failed to zero global state\n");
+ status = 1;
+ }
+
+ /*
+ * Verify we generate nonzero output.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ if (iszero(buf, sizeof(buf))) {
+ fprintf(stderr, "failed to generate nonzero output\n");
+ status = 1;
+ }
+
+ /*
+ * Share the state to compare with parent.
+ */
+ if ((size_t)write(fd[1], local, sizeof(*local)) !=
+ sizeof(*local)) {
+ fprintf(stderr, "failed to share local state\n");
+ status = 1;
+ }
+ _exit(status);
+ }
+
+ /*
+ * Verify the global state has been zeroed as expected. (This
+ * way it is never available to the child, even shortly after
+ * the fork syscall returns before the atfork handler is
+ * called.)
+ */
+ ATF_CHECK(iszero(global, sizeof(*global)));
+
+ /*
+ * Read the state from the child.
+ */
+ RL(nread = read(fd[0], &childstate, sizeof(childstate)));
+ ATF_CHECK_EQ_MSG(nread, sizeof(childstate),
+ "nread=%zu sizeof(childstate)=%zu", nread, sizeof(childstate));
+
+ /*
+ * Verify the child state is distinct. (The global state has
+ * been zero'd so it's OK it if coincides.) Check again after
+ * we grab another output.
+ */
+ ATF_CHECK(memcmp(local, &childstate, sizeof(*local)) != 0);
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(local, &childstate, sizeof(*local)) != 0);
+
+ /*
+ * Wait for the child to complete and verify it passed.
+ */
+ RL(pid = waitpid(child, &status, 0));
+ ATF_CHECK_EQ_MSG(status, 0, "child exited with nonzero status=%d",
+ status);
+}
+
+ATF_TC(global_aslimit);
+ATF_TC_HEAD(global_aslimit, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test the global state is used when address space limit is hit");
+}
+ATF_TC_BODY(global_aslimit, tc)
+{
+ unsigned char buf[32], buf1[32];
+
+ /*
+ * Get a sample from the global state (and verify it was using
+ * the global state).
+ */
+ arc4random_global_buf(buf, sizeof(buf));
+
+ /*
+ * Verify we got a sample.
+ */
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * Get a sample from whatever state and make sure it wasn't
+ * repeated, which happens only with probability 1/2^256.
+ */
+ arc4random_buf(buf1, sizeof(buf1));
+ ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
+}
+
+ATF_TC(global_threadkeylimit);
+ATF_TC_HEAD(global_threadkeylimit, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test the global state is used we run out of thread keys");
+}
+ATF_TC_BODY(global_threadkeylimit, tc)
+{
+ unsigned char buf[32], buf1[32];
+
+ /*
+ * Get a sample from the global state (and verify it was using
+ * the global state).
+ */
+ arc4random_global_buf(buf, sizeof(buf));
+
+ /*
+ * Verify we got a sample.
+ */
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * Artificially disable the per-thread state, make it an
+ * invalid thread key altogether, and clear the epoch. Make
+ * sure we're using the global PRNG state now.
+ */
+ arc4random_global.per_thread = false;
+ memset(&arc4random_global.thread_key, 0x5a,
+ sizeof(arc4random_global.thread_key));
+ arc4random_global.prng.arc4_epoch = 0;
+ ATF_CHECK(arc4random_prng() == &arc4random_global.prng);
+
+ /*
+ * Get a sample again and make sure it wasn't repeated, which
+ * happens only with probability 1/2^256.
+ */
+ arc4random_buf(buf1, sizeof(buf1));
+ ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
+
+ /*
+ * Verify this had the effect of updating the global epoch,
+ * meaning we used the global state and not the per-thread
+ * state.
+ */
+ ATF_CHECK(arc4random_global.prng.arc4_epoch != 0);
+}
+
+ATF_TC(local);
+ATF_TC_HEAD(local, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random uses thread-local state");
+ /* XXX skip if libc was built without _REENTRANT */
+}
+ATF_TC_BODY(local, tc)
+{
+ unsigned char buf[32], buf1[32];
+ struct arc4random_prng *prng;
+
+ /*
+ * Get a sample to start things off.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * Verify the arc4random state is _not_ the global state.
+ */
+ prng = arc4random_prng();
+ ATF_CHECK(prng != &arc4random_global.prng);
+ ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng)));
+ ATF_CHECK(prng->arc4_epoch != 0);
+
+ /*
+ * Get another sample and make sure it wasn't repeated, which
+ * happens only with probability 1/2^256.
+ */
+ arc4random_buf(buf1, sizeof(buf1));
+ ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
+}
+
+ATF_TC(stackfallback);
+ATF_TC_HEAD(stackfallback, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random with pthread_atfork and thr_keycreate failure");
+}
+ATF_TC_BODY(stackfallback, tc)
+{
+ unsigned char buf[32], buf1[32];
+ struct arc4random_prng *local;
+
+ /*
+ * Get a sample to start things off. This makes the library
+ * gets initialized.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * Clear the arc4random global state, and the local state if it
+ * exists, and pretend pthread_atfork and thr_keycreate had
+ * both failed.
+ */
+ memset(&arc4random_global.prng, 0, sizeof(arc4random_global.prng));
+ if ((local = arc4random_prng()) != NULL)
+ memset(local, 0, sizeof(*local));
+ arc4random_global.forksafe = false;
+ arc4random_global.per_thread = false;
+
+ /*
+ * Make sure it still works to get a sample.
+ */
+ arc4random_buf(buf1, sizeof(buf1));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
+
+ /*
+ * Make sure the global and local epochs did not change.
+ */
+ ATF_CHECK_EQ_MSG(arc4random_global.prng.arc4_epoch, 0,
+ "global epoch: %d", arc4random_global.prng.arc4_epoch);
+ if (local != NULL) {
+ ATF_CHECK_EQ_MSG(local->arc4_epoch, 0,
+ "local epoch: %d", local->arc4_epoch);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, addrandom);
+ ATF_TP_ADD_TC(tp, chroot);
+ ATF_TP_ADD_TC(tp, consolidate);
+ ATF_TP_ADD_TC(tp, fdlimit);
+ ATF_TP_ADD_TC(tp, fork);
+ ATF_TP_ADD_TC(tp, global_aslimit);
+ ATF_TP_ADD_TC(tp, global_threadkeylimit);
+ ATF_TP_ADD_TC(tp, local);
+ ATF_TP_ADD_TC(tp, stackfallback);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/gen/t_ctype.c b/lib/libc/gen/t_ctype.c
new file mode 100644
index 000000000000..ee4db34304b7
--- /dev/null
+++ b/lib/libc/gen/t_ctype.c
@@ -0,0 +1,1236 @@
+/* $NetBSD: t_ctype.c,v 1.12 2025/09/15 00:11:55 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Tests for the ctype(3) character classification macros.
+ *
+ * NOTE: These tests intentionally trigger undefined behaviour by
+ * passing int values to the ctype(3) functions which are neither EOF
+ * nor representable by unsigned char. The purpose is to verify
+ * NetBSD's intentional trapping of this undefined behaviour -- or
+ * intentional allowing of this undefined behaviour, when the
+ * environment variable LIBC_ALLOWCTYPEABUSE is set.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_ctype.c,v 1.12 2025/09/15 00:11:55 riastradh Exp $");
+
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <ctype.h>
+#include <locale.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+#ifdef __CHAR_UNSIGNED__
+enum { CHAR_UNSIGNED = 1 };
+#else
+enum { CHAR_UNSIGNED = 0 };
+#endif
+
+/*
+ * libc has a guard page for the LC_CTYPE=C ctype(3) tables only on
+ * some platforms. We skip it if char is unsigned (in which case the
+ * common kind of ctype(3) abuse is unlikely). We also skip it in
+ * static builds -- this is determined in the Makefile.
+ */
+#ifndef _CTYPE_GUARD_PAGE
+# ifdef __CHAR_UNSIGNED__
+# define _CTYPE_GUARD_PAGE 0
+# else
+# define _CTYPE_GUARD_PAGE 1
+# endif
+#endif
+
+static const char *const locales[] = { "C.UTF-8", "fr_FR.ISO8859-1", "C" };
+
+static int isalpha_wrapper(int ch) { return isalpha(ch); }
+static int isupper_wrapper(int ch) { return isupper(ch); }
+static int islower_wrapper(int ch) { return islower(ch); }
+static int isdigit_wrapper(int ch) { return isdigit(ch); }
+static int isxdigit_wrapper(int ch) { return isxdigit(ch); }
+static int isalnum_wrapper(int ch) { return isalnum(ch); }
+static int isspace_wrapper(int ch) { return isspace(ch); }
+static int ispunct_wrapper(int ch) { return ispunct(ch); }
+static int isprint_wrapper(int ch) { return isprint(ch); }
+static int isgraph_wrapper(int ch) { return isgraph(ch); }
+static int iscntrl_wrapper(int ch) { return iscntrl(ch); }
+static int isblank_wrapper(int ch) { return isblank(ch); }
+static int toupper_wrapper(int ch) { return toupper(ch); }
+static int tolower_wrapper(int ch) { return tolower(ch); }
+
+jmp_buf env;
+
+static void
+handle_signal(int signo)
+{
+
+ longjmp(env, 1);
+}
+
+static void
+test_abuse(const char *name, int (*ctypefn)(int))
+{
+ volatile int ch; /* for longjmp */
+
+ for (ch = CHAR_MIN; ch < 0; ch++) {
+ volatile int result;
+
+ if (ch == EOF)
+ continue;
+ ATF_REQUIRE_MSG(ch != (int)(unsigned char)ch, "ch=%d", ch);
+ if (setjmp(env) == 0) {
+ REQUIRE_LIBC(signal(SIGABRT, &handle_signal), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, &handle_signal), SIG_ERR);
+ result = (*ctypefn)(ch);
+ REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR);
+ atf_tc_fail_nonfatal("%s failed to detect invalid %d,"
+ " returned %d",
+ name, ch, result);
+ } else {
+ REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR);
+ }
+ }
+
+ for (; ch <= CHAR_MAX; ch++)
+ ATF_REQUIRE_MSG(ch == (int)(unsigned char)ch, "ch=%d", ch);
+}
+
+static void
+test_abuse_in_locales(const char *name, int (*ctypefn)(int), bool macro)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(locales); i++) {
+ char buf[128];
+
+ if (!_CTYPE_GUARD_PAGE && macro &&
+ strcmp(locales[i], "C") == 0) {
+ fprintf(stderr, "skip LC_CTYPE=C ctype(3) abuse --"
+ " no libc guard page on this platform\n");
+ }
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, locales[i]) != NULL,
+ "locales[i]=%s", locales[i]);
+ snprintf(buf, sizeof(buf), "[%s]%s", locales[i], name);
+ test_abuse(buf, ctypefn);
+ }
+}
+
+static void
+test_use(const char *name, int (*ctypefn)(int))
+{
+ volatile int ch; /* for longjmp */
+
+ for (ch = EOF; ch <= CHAR_MAX; ch = (ch == EOF ? 0 : ch + 1)) {
+ volatile int result;
+
+ if (setjmp(env) == 0) {
+ REQUIRE_LIBC(signal(SIGABRT, &handle_signal), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, &handle_signal), SIG_ERR);
+ result = (*ctypefn)(ch);
+ REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR);
+ (void)result;
+ } else {
+ REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR);
+ atf_tc_fail_nonfatal("%s(%d) raised SIGSEGV",
+ name, ch);
+ }
+ }
+}
+
+static void
+test_isalpha_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isalpha", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isupper_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isupper", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_islower_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("islower", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isdigit_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isdigit", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isxdigit_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isxdigit", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isalnum_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isalnum", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isspace_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isspace", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_ispunct_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("ispunct", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isprint_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isprint", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isgraph_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isgraph", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_iscntrl_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("iscntrl", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isblank_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isblank", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_toupper_locale(const char *L, int (*ctypefn)(int))
+{
+ int result;
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("toupper", ctypefn);
+ ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF,
+ "result=%d, expected EOF=%d", result, EOF);
+}
+
+static void
+test_tolower_locale(const char *L, int (*ctypefn)(int))
+{
+ int result;
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("tolower", ctypefn);
+ ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF,
+ "result=%d, expected EOF=%d", result, EOF);
+}
+
+static void
+test_isalpha_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a': case 'A':
+ case 'b': case 'B':
+ case 'c': case 'C':
+ case 'd': case 'D':
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'g': case 'G':
+ case 'h': case 'H':
+ case 'i': case 'I':
+ case 'j': case 'J':
+ case 'k': case 'K':
+ case 'l': case 'L':
+ case 'm': case 'M':
+ case 'n': case 'N':
+ case 'o': case 'O':
+ case 'p': case 'P':
+ case 'q': case 'Q':
+ case 'r': case 'R':
+ case 's': case 'S':
+ case 't': case 'T':
+ case 'u': case 'U':
+ case 'v': case 'V':
+ case 'w': case 'W':
+ case 'x': case 'X':
+ case 'y': case 'Y':
+ case 'z': case 'Z':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isupper_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_islower_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isdigit_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isxdigit_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a': case 'A':
+ case 'b': case 'B':
+ case 'c': case 'C':
+ case 'd': case 'D':
+ case 'e': case 'E':
+ case 'f': case 'F':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isalnum_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a': case 'A':
+ case 'b': case 'B':
+ case 'c': case 'C':
+ case 'd': case 'D':
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'g': case 'G':
+ case 'h': case 'H':
+ case 'i': case 'I':
+ case 'j': case 'J':
+ case 'k': case 'K':
+ case 'l': case 'L':
+ case 'm': case 'M':
+ case 'n': case 'N':
+ case 'o': case 'O':
+ case 'p': case 'P':
+ case 'q': case 'Q':
+ case 'r': case 'R':
+ case 's': case 'S':
+ case 't': case 'T':
+ case 'u': case 'U':
+ case 'v': case 'V':
+ case 'w': case 'W':
+ case 'x': case 'X':
+ case 'y': case 'Y':
+ case 'z': case 'Z':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isspace_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case ' ':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\v':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_ispunct_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ default:
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ case 0 ... 0x1f:
+ case 0x20: /* space is printing but not punctuation */
+ case 0x7f:
+ case 0x80 ... 0xff:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ case 'a': case 'A':
+ case 'b': case 'B':
+ case 'c': case 'C':
+ case 'd': case 'D':
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'g': case 'G':
+ case 'h': case 'H':
+ case 'i': case 'I':
+ case 'j': case 'J':
+ case 'k': case 'K':
+ case 'l': case 'L':
+ case 'm': case 'M':
+ case 'n': case 'N':
+ case 'o': case 'O':
+ case 'p': case 'P':
+ case 'q': case 'Q':
+ case 'r': case 'R':
+ case 's': case 'S':
+ case 't': case 'T':
+ case 'u': case 'U':
+ case 'v': case 'V':
+ case 'w': case 'W':
+ case 'x': case 'X':
+ case 'y': case 'Y':
+ case 'z': case 'Z':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isprint_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 0x20: /* space is printing but not graphic */
+ default:
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ case 0 ... 0x1f:
+ case 0x7f:
+ case 0x80 ... 0xff:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isgraph_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ default:
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ case 0 ... 0x1f:
+ case 0x20: /* space is printing but not graphic */
+ case 0x7f:
+ case 0x80 ... 0xff:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_iscntrl_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 0 ... 0x1f:
+ case 0x7f:
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isblank_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case ' ':
+ case '\t':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_toupper_c(int (*ctypefn)(int))
+{
+ int ch, result, expected;
+
+ ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF,
+ "result=%d, expected EOF=%d", result, EOF);
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a': case 'A': expected = 'A'; break;
+ case 'b': case 'B': expected = 'B'; break;
+ case 'c': case 'C': expected = 'C'; break;
+ case 'd': case 'D': expected = 'D'; break;
+ case 'e': case 'E': expected = 'E'; break;
+ case 'f': case 'F': expected = 'F'; break;
+ case 'g': case 'G': expected = 'G'; break;
+ case 'h': case 'H': expected = 'H'; break;
+ case 'i': case 'I': expected = 'I'; break;
+ case 'j': case 'J': expected = 'J'; break;
+ case 'k': case 'K': expected = 'K'; break;
+ case 'l': case 'L': expected = 'L'; break;
+ case 'm': case 'M': expected = 'M'; break;
+ case 'n': case 'N': expected = 'N'; break;
+ case 'o': case 'O': expected = 'O'; break;
+ case 'p': case 'P': expected = 'P'; break;
+ case 'q': case 'Q': expected = 'Q'; break;
+ case 'r': case 'R': expected = 'R'; break;
+ case 's': case 'S': expected = 'S'; break;
+ case 't': case 'T': expected = 'T'; break;
+ case 'u': case 'U': expected = 'U'; break;
+ case 'v': case 'V': expected = 'V'; break;
+ case 'w': case 'W': expected = 'W'; break;
+ case 'x': case 'X': expected = 'X'; break;
+ case 'y': case 'Y': expected = 'Y'; break;
+ case 'z': case 'Z': expected = 'Z'; break;
+ default:
+ expected = ch;
+ break;
+ }
+ ATF_CHECK_MSG((result = (*ctypefn)(ch)) == expected,
+ "result=%d expected=%d", result, expected);
+ }
+}
+
+static void
+test_tolower_c(int (*ctypefn)(int))
+{
+ int ch, result, expected;
+
+ ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF,
+ "result=%d, expected EOF=%d", result, EOF);
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a': case 'A': expected = 'a'; break;
+ case 'b': case 'B': expected = 'b'; break;
+ case 'c': case 'C': expected = 'c'; break;
+ case 'd': case 'D': expected = 'd'; break;
+ case 'e': case 'E': expected = 'e'; break;
+ case 'f': case 'F': expected = 'f'; break;
+ case 'g': case 'G': expected = 'g'; break;
+ case 'h': case 'H': expected = 'h'; break;
+ case 'i': case 'I': expected = 'i'; break;
+ case 'j': case 'J': expected = 'j'; break;
+ case 'k': case 'K': expected = 'k'; break;
+ case 'l': case 'L': expected = 'l'; break;
+ case 'm': case 'M': expected = 'm'; break;
+ case 'n': case 'N': expected = 'n'; break;
+ case 'o': case 'O': expected = 'o'; break;
+ case 'p': case 'P': expected = 'p'; break;
+ case 'q': case 'Q': expected = 'q'; break;
+ case 'r': case 'R': expected = 'r'; break;
+ case 's': case 'S': expected = 's'; break;
+ case 't': case 'T': expected = 't'; break;
+ case 'u': case 'U': expected = 'u'; break;
+ case 'v': case 'V': expected = 'v'; break;
+ case 'w': case 'W': expected = 'w'; break;
+ case 'x': case 'X': expected = 'x'; break;
+ case 'y': case 'Y': expected = 'y'; break;
+ case 'z': case 'Z': expected = 'z'; break;
+ default:
+ expected = ch;
+ break;
+ }
+ ATF_CHECK_MSG((result = (*ctypefn)(ch)) == expected,
+ "result=%d expected=%d", result, expected);
+ }
+}
+
+extern char **environ;
+
+static void
+test_abuse_override(const struct atf_tc *tc, const char *fn, const char *mode,
+ const char *locale)
+{
+ char h_ctype_abuse[PATH_MAX];
+ pid_t pid;
+ int status;
+
+ RL(snprintf(h_ctype_abuse, sizeof(h_ctype_abuse), "%s/h_ctype_abuse",
+ atf_tc_get_config_var(tc, "srcdir")));
+
+ RL(setenv("LIBC_ALLOWCTYPEABUSE", "", 1));
+
+ RL(pid = vfork());
+ if (pid == 0) { /* child */
+ char *const argv[] = {
+ h_ctype_abuse,
+ __UNCONST(fn),
+ __UNCONST(mode),
+ __UNCONST(locale),
+ NULL,
+ };
+
+ if (execve(argv[0], argv, environ) == -1)
+ _exit(1);
+ _exit(2);
+ }
+
+ RL(waitpid(pid, &status, 0));
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail_nonfatal("child exited on signal %d (%s)",
+ WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail_nonfatal("child exited status=0x%x", status);
+ } else {
+ ATF_CHECK_MSG(WEXITSTATUS(status) == 0,
+ "child exited with code %d",
+ WEXITSTATUS(status));
+ }
+}
+
+static void
+test_abuse_override_in_locales(const struct atf_tc *tc, const char *fn,
+ const char *mode)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(locales); i++) {
+ fprintf(stderr, "# locale %s\n", locales[i]);
+ test_abuse_override(tc, fn, mode, locales[i]);
+ }
+}
+
+#define ADD_TEST_ABUSE(TP, FN) do \
+{ \
+ ATF_TP_ADD_TC(TP, abuse_##FN##_macro_c); \
+ ATF_TP_ADD_TC(TP, abuse_##FN##_function_c); \
+ ATF_TP_ADD_TC(TP, abuse_##FN##_macro_locale); \
+ ATF_TP_ADD_TC(TP, abuse_##FN##_function_locale); \
+} while (0)
+
+#define DEF_TEST_ABUSE(FN) \
+ATF_TC(abuse_##FN##_macro_c); \
+ATF_TC_HEAD(abuse_##FN##_macro_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test abusing "#FN" macro with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(abuse_##FN##_macro_c, tc) \
+{ \
+ if (CHAR_UNSIGNED) { \
+ atf_tc_skip("runtime ctype(3) abuse is impossible with" \
+ " unsigned char"); \
+ } \
+ if (!_CTYPE_GUARD_PAGE) \
+ atf_tc_skip("no LC_CTYPE=C guard page on this platform"); \
+ test_abuse(#FN, &FN##_wrapper); \
+} \
+ATF_TC(abuse_##FN##_function_c); \
+ATF_TC_HEAD(abuse_##FN##_function_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test abusing "#FN" function with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(abuse_##FN##_function_c, tc) \
+{ \
+ if (CHAR_UNSIGNED) { \
+ atf_tc_skip("runtime ctype(3) abuse is impossible with" \
+ " unsigned char"); \
+ } \
+ if (!_CTYPE_GUARD_PAGE) \
+ atf_tc_skip("no LC_CTYPE=C guard page on this platform"); \
+ test_abuse(#FN, &FN); \
+} \
+ATF_TC(abuse_##FN##_macro_locale); \
+ATF_TC_HEAD(abuse_##FN##_macro_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test abusing "#FN" macro with locales"); \
+} \
+ATF_TC_BODY(abuse_##FN##_macro_locale, tc) \
+{ \
+ if (CHAR_UNSIGNED) { \
+ atf_tc_skip("runtime ctype(3) abuse is impossible with" \
+ " unsigned char"); \
+ } \
+ test_abuse_in_locales(#FN, &FN##_wrapper, /*macro*/true); \
+} \
+ATF_TC(abuse_##FN##_function_locale); \
+ATF_TC_HEAD(abuse_##FN##_function_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test abusing "#FN" function with locales"); \
+} \
+ATF_TC_BODY(abuse_##FN##_function_locale, tc) \
+{ \
+ if (CHAR_UNSIGNED) { \
+ atf_tc_skip("runtime ctype(3) abuse is impossible with" \
+ " unsigned char"); \
+ } \
+ test_abuse_in_locales(#FN, &FN, /*macro*/false); \
+}
+
+#define ADD_TEST_ABUSE_OVERRIDE(TP, FN) do \
+{ \
+ ATF_TP_ADD_TC(TP, abuse_override_##FN##_macro_c); \
+ ATF_TP_ADD_TC(TP, abuse_override_##FN##_function_c); \
+ ATF_TP_ADD_TC(TP, abuse_override_##FN##_macro_locale); \
+ ATF_TP_ADD_TC(TP, abuse_override_##FN##_function_locale); \
+} while (0)
+
+#define DEF_TEST_ABUSE_OVERRIDE(FN) \
+ATF_TC(abuse_override_##FN##_macro_c); \
+ATF_TC_HEAD(abuse_override_##FN##_macro_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test allowing abuse of "#FN" macro with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(abuse_override_##FN##_macro_c, tc) \
+{ \
+ test_abuse_override(tc, #FN, "macro", NULL); \
+} \
+ATF_TC(abuse_override_##FN##_function_c); \
+ATF_TC_HEAD(abuse_override_##FN##_function_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test allowing abuse "#FN" function with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(abuse_override_##FN##_function_c, tc) \
+{ \
+ test_abuse_override(tc, #FN, "function", NULL); \
+} \
+ATF_TC(abuse_override_##FN##_macro_locale); \
+ATF_TC_HEAD(abuse_override_##FN##_macro_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test allowing abuse of "#FN" macro with locales"); \
+} \
+ATF_TC_BODY(abuse_override_##FN##_macro_locale, tc) \
+{ \
+ test_abuse_override_in_locales(tc, #FN, "macro"); \
+} \
+ATF_TC(abuse_override_##FN##_function_locale); \
+ATF_TC_HEAD(abuse_override_##FN##_function_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test allowing abuse of "#FN" function with locales"); \
+} \
+ATF_TC_BODY(abuse_override_##FN##_function_locale, tc) \
+{ \
+ test_abuse_override_in_locales(tc, #FN, "function"); \
+}
+
+#define ADD_TEST_USE(TP, FN) do \
+{ \
+ ATF_TP_ADD_TC(TP, FN##_macro_c); \
+ ATF_TP_ADD_TC(TP, FN##_function_c); \
+ ATF_TP_ADD_TC(TP, FN##_macro_locale); \
+ ATF_TP_ADD_TC(TP, FN##_function_locale); \
+} while (0)
+
+#define DEF_TEST_USE(FN) \
+ATF_TC(FN##_macro_c); \
+ATF_TC_HEAD(FN##_macro_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test "#FN" macro with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(FN##_macro_c, tc) \
+{ \
+ test_##FN##_c(&FN##_wrapper); \
+} \
+ATF_TC(FN##_function_c); \
+ATF_TC_HEAD(FN##_function_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test "#FN" function with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(FN##_function_c, tc) \
+{ \
+ test_##FN##_c(&FN); \
+} \
+ATF_TC(FN##_macro_locale); \
+ATF_TC_HEAD(FN##_macro_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test "#FN" macro with locales"); \
+} \
+ATF_TC_BODY(FN##_macro_locale, tc) \
+{ \
+ size_t i; \
+ \
+ for (i = 0; i < __arraycount(locales); i++) \
+ test_##FN##_locale(locales[i], &FN##_wrapper); \
+} \
+ATF_TC(FN##_function_locale); \
+ATF_TC_HEAD(FN##_function_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test "#FN" function with locales"); \
+} \
+ATF_TC_BODY(FN##_function_locale, tc) \
+{ \
+ size_t i; \
+ \
+ for (i = 0; i < __arraycount(locales); i++) \
+ test_##FN##_locale(locales[i], &FN); \
+}
+
+DEF_TEST_ABUSE(isalpha)
+DEF_TEST_ABUSE(isupper)
+DEF_TEST_ABUSE(islower)
+DEF_TEST_ABUSE(isdigit)
+DEF_TEST_ABUSE(isxdigit)
+DEF_TEST_ABUSE(isalnum)
+DEF_TEST_ABUSE(isspace)
+DEF_TEST_ABUSE(ispunct)
+DEF_TEST_ABUSE(isprint)
+DEF_TEST_ABUSE(isgraph)
+DEF_TEST_ABUSE(iscntrl)
+DEF_TEST_ABUSE(isblank)
+DEF_TEST_ABUSE(toupper)
+DEF_TEST_ABUSE(tolower)
+
+DEF_TEST_ABUSE_OVERRIDE(isalpha)
+DEF_TEST_ABUSE_OVERRIDE(isupper)
+DEF_TEST_ABUSE_OVERRIDE(islower)
+DEF_TEST_ABUSE_OVERRIDE(isdigit)
+DEF_TEST_ABUSE_OVERRIDE(isxdigit)
+DEF_TEST_ABUSE_OVERRIDE(isalnum)
+DEF_TEST_ABUSE_OVERRIDE(isspace)
+DEF_TEST_ABUSE_OVERRIDE(ispunct)
+DEF_TEST_ABUSE_OVERRIDE(isprint)
+DEF_TEST_ABUSE_OVERRIDE(isgraph)
+DEF_TEST_ABUSE_OVERRIDE(iscntrl)
+DEF_TEST_ABUSE_OVERRIDE(isblank)
+DEF_TEST_ABUSE_OVERRIDE(toupper)
+DEF_TEST_ABUSE_OVERRIDE(tolower)
+
+DEF_TEST_USE(isalpha)
+DEF_TEST_USE(isupper)
+DEF_TEST_USE(islower)
+DEF_TEST_USE(isdigit)
+DEF_TEST_USE(isxdigit)
+DEF_TEST_USE(isalnum)
+DEF_TEST_USE(isspace)
+DEF_TEST_USE(ispunct)
+DEF_TEST_USE(isprint)
+DEF_TEST_USE(isgraph)
+DEF_TEST_USE(iscntrl)
+DEF_TEST_USE(isblank)
+DEF_TEST_USE(toupper)
+DEF_TEST_USE(tolower)
+
+ATF_TC(eof_confusion_iso8859_1);
+ATF_TC_HEAD(eof_confusion_iso8859_1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test potential confusion with EOF in ISO-8859-1");
+}
+ATF_TC_BODY(eof_confusion_iso8859_1, tc)
+{
+ int ydots = 0xff; /* ÿ, LATIN SMALL LETTER Y WITH DIAERESIS */
+ int ch;
+
+ /*
+ * The LATIN SMALL LETTER Y WITH DIAERESIS code point 0xff in
+ * ISO-8859-1 is curious primarily because its bit pattern
+ * coincides with an 8-bit signed -1, which is to say, EOF as
+ * an 8-bit quantity; of course, for EOF, all of the is*
+ * functions are supposed to return false (as we test above).
+ * It also has the curious property that it lacks any
+ * corresponding uppercase code point in ISO-8859-1, so we
+ * can't distinguish it from EOF by tolower/toupper.
+ */
+ ATF_REQUIRE(setlocale(LC_CTYPE, "fr_FR.ISO8859-1") != NULL);
+ ATF_CHECK(isalpha(ydots));
+ ATF_CHECK(!isupper(ydots));
+ ATF_CHECK(islower(ydots));
+ ATF_CHECK(!isdigit(ydots));
+ ATF_CHECK(!isxdigit(ydots));
+ ATF_CHECK(isalnum(ydots));
+ ATF_CHECK(!isspace(ydots));
+ ATF_CHECK(!ispunct(ydots));
+ ATF_CHECK(isprint(ydots));
+ ATF_CHECK(isgraph(ydots));
+ ATF_CHECK(!iscntrl(ydots));
+ ATF_CHECK(!isblank(ydots));
+ ATF_CHECK_MSG((ch = toupper(ydots)) == ydots, "ch=0x%x", ch);
+ ATF_CHECK_MSG((ch = tolower(ydots)) == ydots, "ch=0x%x", ch);
+}
+
+ATF_TC(eof_confusion_koi8_u);
+ATF_TC_HEAD(eof_confusion_koi8_u, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test potential confusion with EOF in KOI8-U");
+}
+ATF_TC_BODY(eof_confusion_koi8_u, tc)
+{
+ int Hard = 0xff; /* Ъ, CYRILLIC CAPITAL LETTER HARD SIGN */
+ int hard = 0xdf; /* ъ, CYRILLIC SMALL LETTER HARD SIGN */
+ int ch;
+
+ /*
+ * The CYRILLIC CAPITAL LETTER HARD SIGN code point 0xff in
+ * KOI8-U (and KOI8-R) also coincides with the bit pattern of
+ * an 8-bit signed -1. Unlike LATIN SMALL LETTER Y WITH
+ * DIAERESIS, it has a lowercase equivalent in KOI8-U.
+ */
+ ATF_REQUIRE(setlocale(LC_CTYPE, "uk_UA.KOI8-U") != NULL);
+ ATF_CHECK(isalpha(Hard));
+ ATF_CHECK(isupper(Hard));
+ ATF_CHECK(!islower(Hard));
+ ATF_CHECK(!isdigit(Hard));
+ ATF_CHECK(!isxdigit(Hard));
+ ATF_CHECK(isalnum(Hard));
+ ATF_CHECK(!isspace(Hard));
+ ATF_CHECK(!ispunct(Hard));
+ ATF_CHECK(isprint(Hard));
+ ATF_CHECK(isgraph(Hard));
+ ATF_CHECK(!iscntrl(Hard));
+ ATF_CHECK(!isblank(Hard));
+ ATF_CHECK_MSG((ch = toupper(Hard)) == Hard, "ch=0x%x", ch);
+ ATF_CHECK_MSG((ch = tolower(Hard)) == hard, "ch=0x%x", ch);
+}
+
+ATF_TC(eof_confusion_pt154);
+ATF_TC_HEAD(eof_confusion_pt154, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test potential confusion with EOF in PT154");
+}
+ATF_TC_BODY(eof_confusion_pt154, tc)
+{
+ int ya = 0xff; /* я, CYRILLIC SMALL LETTER YA */
+ int Ya = 0xdf; /* Я, CYRILLIC CAPITAL LETTER YA */
+ int ch;
+
+ /*
+ * The CYRILLIC SMALL LETTER YA code point 0xff in PT154 also
+ * coincides with the bit pattern of an 8-bit signed -1, and is
+ * lowercase with a corresponding uppercase code point in
+ * PT154.
+ */
+ ATF_REQUIRE(setlocale(LC_CTYPE, "kk_KZ.PT154") != NULL);
+ ATF_CHECK(isalpha(ya));
+ ATF_CHECK(!isupper(ya));
+ ATF_CHECK(islower(ya));
+ ATF_CHECK(!isdigit(ya));
+ ATF_CHECK(!isxdigit(ya));
+ ATF_CHECK(isalnum(ya));
+ ATF_CHECK(!isspace(ya));
+ ATF_CHECK(!ispunct(ya));
+ ATF_CHECK(isprint(ya));
+ ATF_CHECK(isgraph(ya));
+ ATF_CHECK(!iscntrl(ya));
+ ATF_CHECK(!isblank(ya));
+ ATF_CHECK_MSG((ch = toupper(ya)) == Ya, "ch=0x%x", ch);
+ ATF_CHECK_MSG((ch = tolower(ya)) == ya, "ch=0x%x", ch);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ADD_TEST_ABUSE(tp, isalpha);
+ ADD_TEST_ABUSE(tp, isupper);
+ ADD_TEST_ABUSE(tp, islower);
+ ADD_TEST_ABUSE(tp, isdigit);
+ ADD_TEST_ABUSE(tp, isxdigit);
+ ADD_TEST_ABUSE(tp, isalnum);
+ ADD_TEST_ABUSE(tp, isspace);
+ ADD_TEST_ABUSE(tp, ispunct);
+ ADD_TEST_ABUSE(tp, isprint);
+ ADD_TEST_ABUSE(tp, isgraph);
+ ADD_TEST_ABUSE(tp, iscntrl);
+ ADD_TEST_ABUSE(tp, isblank);
+ ADD_TEST_ABUSE(tp, toupper);
+ ADD_TEST_ABUSE(tp, tolower);
+
+ ADD_TEST_ABUSE_OVERRIDE(tp, isalpha);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isupper);
+ ADD_TEST_ABUSE_OVERRIDE(tp, islower);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isdigit);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isxdigit);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isalnum);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isspace);
+ ADD_TEST_ABUSE_OVERRIDE(tp, ispunct);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isprint);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isgraph);
+ ADD_TEST_ABUSE_OVERRIDE(tp, iscntrl);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isblank);
+ ADD_TEST_ABUSE_OVERRIDE(tp, toupper);
+ ADD_TEST_ABUSE_OVERRIDE(tp, tolower);
+
+ ADD_TEST_USE(tp, isalpha);
+ ADD_TEST_USE(tp, isupper);
+ ADD_TEST_USE(tp, islower);
+ ADD_TEST_USE(tp, isdigit);
+ ADD_TEST_USE(tp, isxdigit);
+ ADD_TEST_USE(tp, isalnum);
+ ADD_TEST_USE(tp, isspace);
+ ADD_TEST_USE(tp, ispunct);
+ ADD_TEST_USE(tp, isprint);
+ ADD_TEST_USE(tp, isgraph);
+ ADD_TEST_USE(tp, iscntrl);
+ ADD_TEST_USE(tp, isblank);
+ ADD_TEST_USE(tp, toupper);
+ ADD_TEST_USE(tp, tolower);
+
+ ATF_TP_ADD_TC(tp, eof_confusion_iso8859_1);
+ ATF_TP_ADD_TC(tp, eof_confusion_koi8_u);
+ ATF_TP_ADD_TC(tp, eof_confusion_pt154);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/gen/t_timespec_get.c b/lib/libc/gen/t_timespec_get.c
new file mode 100644
index 000000000000..b51625311032
--- /dev/null
+++ b/lib/libc/gen/t_timespec_get.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nia Alarie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <limits.h>
+#include <time.h>
+
+ATF_TC(timespec_getres);
+ATF_TC_HEAD(timespec_getres, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Resolution tests for timespec_getres");
+}
+
+ATF_TC_BODY(timespec_getres, tc)
+{
+ struct timespec ts, ts2;
+
+ ATF_REQUIRE_EQ(timespec_getres(&ts, TIME_MONOTONIC), TIME_MONOTONIC);
+ ATF_REQUIRE_EQ(clock_getres(CLOCK_MONOTONIC, &ts2), 0);
+
+ ATF_REQUIRE_EQ(ts.tv_sec, ts2.tv_sec);
+ ATF_REQUIRE_EQ(ts.tv_nsec, ts2.tv_nsec);
+
+ ATF_REQUIRE_EQ(timespec_getres(&ts, TIME_UTC), TIME_UTC);
+ ATF_REQUIRE_EQ(clock_getres(CLOCK_REALTIME, &ts2), 0);
+
+ ATF_REQUIRE_EQ(ts.tv_sec, ts2.tv_sec);
+ ATF_REQUIRE_EQ(ts.tv_nsec, ts2.tv_nsec);
+
+ /* now an invalid value */
+ ATF_REQUIRE_EQ(timespec_getres(&ts, INT_MAX), 0);
+}
+
+ATF_TC(timespec_get);
+ATF_TC_HEAD(timespec_get, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Basic tests for timespec_get");
+}
+
+ATF_TC_BODY(timespec_get, tc)
+{
+ struct timespec ts, ts2;
+
+ ATF_REQUIRE_EQ(timespec_get(&ts, TIME_UTC), TIME_UTC);
+ ATF_REQUIRE_EQ(clock_gettime(CLOCK_REALTIME, &ts2), 0);
+
+ /*
+ * basically test that these clocks (which should be the same source)
+ * aren't too wildly apart...
+ */
+
+ if (ts2.tv_sec >= ts.tv_sec) {
+ ATF_REQUIRE((ts2.tv_sec - ts.tv_sec) < 86400);
+ } else {
+ ATF_REQUIRE((ts.tv_sec - ts2.tv_sec) < 86400);
+ }
+
+ /* now an invalid value */
+ ATF_REQUIRE_EQ(timespec_get(&ts, INT_MAX), 0);
+}
+
+ATF_TC(timespec_get_monotonic);
+ATF_TC_HEAD(timespec_get_monotonic, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Monotonic tests for timespec_getres");
+}
+
+ATF_TC_BODY(timespec_get_monotonic, tc)
+{
+ struct timespec ts, ts2;
+
+ ATF_REQUIRE_EQ(timespec_get(&ts, TIME_MONOTONIC), TIME_MONOTONIC);
+ ATF_REQUIRE_EQ(clock_gettime(CLOCK_MONOTONIC, &ts2), 0);
+
+ /*
+ * basically test that these clocks (which should be the same source)
+ * aren't too wildly apart...
+ */
+
+ ATF_REQUIRE(ts2.tv_sec >= ts.tv_sec);
+ ATF_REQUIRE((ts2.tv_sec - ts.tv_sec) < 86400);
+
+ /* test that it's actually monotonic */
+ ATF_REQUIRE_EQ(timespec_get(&ts2, TIME_MONOTONIC), TIME_MONOTONIC);
+ ATF_REQUIRE(ts2.tv_sec >= ts.tv_sec);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, timespec_getres);
+ ATF_TP_ADD_TC(tp, timespec_get);
+ ATF_TP_ADD_TC(tp, timespec_get_monotonic);
+
+ return atf_no_error();
+}
+
diff --git a/lib/libc/locale/t_c16rtomb.c b/lib/libc/locale/t_c16rtomb.c
new file mode 100644
index 000000000000..3d695db8dc97
--- /dev/null
+++ b/lib/libc/locale/t_c16rtomb.c
@@ -0,0 +1,287 @@
+/* $NetBSD: t_c16rtomb.c,v 1.6 2024/08/19 16:22:10 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Test program for c16rtomb() as specified by ISO/IEC 9899:2011.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_c16rtomb.c,v 1.6 2024/08/19 16:22:10 riastradh Exp $");
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+#include <uchar.h>
+
+#include <atf-c.h>
+
+static void
+require_lc_ctype(const char *locale_name)
+{
+ char *lc_ctype_set;
+
+ lc_ctype_set = setlocale(LC_CTYPE, locale_name);
+ if (lc_ctype_set == NULL)
+ atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d",
+ locale_name, errno);
+
+ ATF_REQUIRE_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0,
+ "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name);
+}
+
+static mbstate_t s;
+static char buf[7*MB_LEN_MAX + 1];
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_c_locale_test);
+ATF_TC_BODY(c16rtomb_c_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("C");
+
+ /*
+ * If the buffer argument is NULL, c16 is implicitly 0,
+ * c16rtomb() resets its internal state.
+ */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, 0xdc00, NULL)), 1, "n=%zu", n);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'A', NULL)), 1, "n=%zu", n);
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'A', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xdca9, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_iso2022jp_locale_test);
+ATF_TC_BODY(c16rtomb_iso2022jp_locale_test, tc)
+{
+ char *p;
+ size_t n;
+
+ require_lc_ctype("ja_JP.ISO-2022-JP");
+
+ /*
+ * If the buffer argument is NULL, c16 is implicitly 0,
+ * c16rtomb() resets its internal state.
+ */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, 0xdc00, NULL)), 1, "n=%zu", n);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'A', NULL)), 1, "n=%zu", n);
+
+ /*
+ * 1. U+0042 LATIN CAPITAL LETTER A
+ * 2. U+00A5 YEN SIGN
+ * 3. U+00A5 YEN SIGN (again, no shift needed)
+ * 4. U+30A2 KATAKANA LETTER A
+ * 5. U+30A2 KATAKANA LETTER A (again, no shift needed)
+ * 6. incomplete UTF-16 surrogate pair -- no output
+ * 7. U+0000 NUL (plus shift sequence to initial state)
+ */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ p = buf;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, L'A', &s)), 1, "n=%zu", n); /* 1 */
+ p += 1;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0xa5, &s)), 4, "n=%zu", n); /* 2 */
+ p += 4;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0xa5, &s)), 1, "n=%zu", n); /* 3 */
+ p += 1;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0x30a2, &s)), 5, "n=%zu", n); /* 4 */
+ p += 5;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0x30a2, &s)), 2, "n=%zu", n); /* 5 */
+ p += 2;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0xd800, &s)), 0, "n=%zu", n); /* 6 */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, L'\0', &s)), 4, "n=%zu", n); /* 7 */
+ p += 4;
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' &&
+ (unsigned char)buf[1] == 0x1b && /* shift ISO/IEC 646:JP */
+ (unsigned char)buf[2] == '(' &&
+ (unsigned char)buf[3] == 'J' &&
+ (unsigned char)buf[4] == 0x5c && /* YEN SIGN */
+ (unsigned char)buf[5] == 0x5c && /* YEN SIGN */
+ (unsigned char)buf[6] == 0x1b && /* shift JIS X 0208 */
+ (unsigned char)buf[7] == '$' &&
+ (unsigned char)buf[8] == 'B' &&
+ (unsigned char)buf[9] == 0x25 && /* KATAKANA LETTER A */
+ (unsigned char)buf[10] == 0x22 &&
+ (unsigned char)buf[11] == 0x25 && /* KATAKANA LETTER A */
+ (unsigned char)buf[12] == 0x22 &&
+ (unsigned char)buf[13] == 0x1b && /* shift US-ASCII */
+ (unsigned char)buf[14] == '(' &&
+ (unsigned char)buf[15] == 'B' &&
+ (unsigned char)buf[16] == '\0' &&
+ (unsigned char)buf[17] == 0xcc),
+ "buf=[%02x %02x %02x %02x %02x %02x %02x %02x "
+ " %02x %02x %02x %02x %02x %02x %02x %02x "
+ " %02x %02x]",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], buf[15],
+ buf[16], buf[17]);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_iso_8859_1_test);
+ATF_TC_BODY(c16rtomb_iso_8859_1_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0x20ac, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_iso_8859_15_test);
+ATF_TC_BODY(c16rtomb_iso_8859_15_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0x20ac, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0xa4 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_utf_8_test);
+ATF_TC_BODY(c16rtomb_utf_8_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xdca9, &s)), 4, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0xf0 &&
+ (unsigned char)buf[1] == 0x9f &&
+ (unsigned char)buf[2] == 0x92 &&
+ (unsigned char)buf[3] == 0xa9 &&
+ (unsigned char)buf[4] == 0xcc),
+ "buf=[%02x %02x %02x %02x %02x]",
+ buf[0], buf[1], buf[2], buf[3], buf[4]);
+
+ /* Invalid code; 'Pile of poo' without the trail surrogate. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'A', &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Invalid code; 'Pile of poo' without the lead surrogate. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xdca9, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'\0', &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, c16rtomb_c_locale_test);
+ ATF_TP_ADD_TC(tp, c16rtomb_iso2022jp_locale_test);
+ ATF_TP_ADD_TC(tp, c16rtomb_iso_8859_1_test);
+ ATF_TP_ADD_TC(tp, c16rtomb_iso_8859_15_test);
+ ATF_TP_ADD_TC(tp, c16rtomb_utf_8_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/locale/t_c32rtomb.c b/lib/libc/locale/t_c32rtomb.c
new file mode 100644
index 000000000000..7233e14f7967
--- /dev/null
+++ b/lib/libc/locale/t_c32rtomb.c
@@ -0,0 +1,60 @@
+/* $NetBSD: t_c32rtomb.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_c32rtomb.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $");
+
+#include <atf-c.h>
+#include <locale.h>
+#include <uchar.h>
+
+#include "h_macros.h"
+
+ATF_TC(c32rtomb_null);
+ATF_TC_HEAD(c32rtomb_null, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test null string output to c32rtomb");
+}
+ATF_TC_BODY(c32rtomb_null, tc)
+{
+ char *locale;
+ mbstate_t ps = {0};
+ size_t n;
+
+ REQUIRE_LIBC((locale = setlocale(LC_ALL, "C")), NULL);
+ ATF_REQUIRE_EQ_MSG(strcmp(locale, "C"), 0, "locale=%s", locale);
+
+ ATF_CHECK_EQ_MSG((n = c32rtomb(NULL, L'x', &ps)), 1, "n=%zu", n);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, c32rtomb_null);
+ return atf_no_error();
+}
diff --git a/lib/libc/locale/t_c8rtomb.c b/lib/libc/locale/t_c8rtomb.c
new file mode 100644
index 000000000000..ce03e6ed1d20
--- /dev/null
+++ b/lib/libc/locale/t_c8rtomb.c
@@ -0,0 +1,355 @@
+/* $NetBSD: t_c8rtomb.c,v 1.7 2024/08/19 16:22:10 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Test program for c8rtomb() as specified by C23.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_c8rtomb.c,v 1.7 2024/08/19 16:22:10 riastradh Exp $");
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+#include <uchar.h>
+
+#include <atf-c.h>
+
+static void
+require_lc_ctype(const char *locale_name)
+{
+ char *lc_ctype_set;
+
+ lc_ctype_set = setlocale(LC_CTYPE, locale_name);
+ if (lc_ctype_set == NULL)
+ atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d",
+ locale_name, errno);
+
+ ATF_REQUIRE_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0,
+ "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name);
+}
+
+static mbstate_t s;
+static char buf[7*MB_LEN_MAX + 1];
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_c_locale_test);
+ATF_TC_BODY(c8rtomb_c_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("C");
+
+ /*
+ * If the buffer argument is NULL, c8 is implicitly 0,
+ * c8rtomb() resets its internal state.
+ */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0x80, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xc0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xe0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf8, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfc, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfe, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xff, NULL)), 1, "n=%zu", n);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 'A', NULL)), 1, "n=%zu", n);
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 'A', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xa9, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_iso2022jp_locale_test);
+ATF_TC_BODY(c8rtomb_iso2022jp_locale_test, tc)
+{
+ char *p;
+ size_t n;
+
+ require_lc_ctype("ja_JP.ISO-2022-JP");
+
+ /*
+ * If the buffer argument is NULL, c8 is implicitly 0,
+ * c8rtomb() resets its internal state.
+ */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0x80, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xc0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xe0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf8, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfc, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfe, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xff, NULL)), 1, "n=%zu", n);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 'A', NULL)), 1, "n=%zu", n);
+
+ /*
+ * 1. U+0042 LATIN CAPITAL LETTER A
+ * 2. U+00A5 YEN SIGN
+ * 3. U+00A5 YEN SIGN (again, no shift needed)
+ * 4. U+30A2 KATAKANA LETTER A
+ * 5. U+30A2 KATAKANA LETTER A (again, no shift needed)
+ * 6. incomplete UTF-8 multibyte sequence -- no output
+ * 7. U+0000 NUL (plus shift sequence to initial state)
+ */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ p = buf;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 'A', &s)), 1, "n=%zu", n); /* 1 */
+ p += 1;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xc2, &s)), 0, "n=%zu", n); /* 2 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa5, &s)), 4, "n=%zu", n);
+ p += 4;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xc2, &s)), 0, "n=%zu", n); /* 3 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa5, &s)), 1, "n=%zu", n);
+ p += 1;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xe3, &s)), 0, "n=%zu", n); /* 4 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa2, &s)), 5, "n=%zu", n);
+ p += 5;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xe3, &s)), 0, "n=%zu", n); /* 5 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa2, &s)), 2, "n=%zu", n);
+ p += 2;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xe3, &s)), 0, "n=%zu", n); /* 6 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, '\0', &s)), 4, "n=%zu", n); /* 7 */
+ p += 4;
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' &&
+ (unsigned char)buf[1] == 0x1b && /* shift ISO/IEC 646:JP */
+ (unsigned char)buf[2] == '(' &&
+ (unsigned char)buf[3] == 'J' &&
+ (unsigned char)buf[4] == 0x5c && /* YEN SIGN */
+ (unsigned char)buf[5] == 0x5c && /* YEN SIGN */
+ (unsigned char)buf[6] == 0x1b && /* shift JIS X 0208 */
+ (unsigned char)buf[7] == '$' &&
+ (unsigned char)buf[8] == 'B' &&
+ (unsigned char)buf[9] == 0x25 && /* KATAKANA LETTER A */
+ (unsigned char)buf[10] == 0x22 &&
+ (unsigned char)buf[11] == 0x25 && /* KATAKANA LETTER A */
+ (unsigned char)buf[12] == 0x22 &&
+ (unsigned char)buf[13] == 0x1b && /* shift US-ASCII */
+ (unsigned char)buf[14] == '(' &&
+ (unsigned char)buf[15] == 'B' &&
+ (unsigned char)buf[16] == '\0' &&
+ (unsigned char)buf[17] == 0xcc),
+ "buf=[%02x %02x %02x %02x %02x %02x %02x %02x "
+ " %02x %02x %02x %02x %02x %02x %02x %02x "
+ " %02x %02x]",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], buf[15],
+ buf[16], buf[17]);
+}
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_iso_8859_1_test);
+ATF_TC_BODY(c8rtomb_iso_8859_1_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xe2, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xac, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+}
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_iso_8859_15_test);
+ATF_TC_BODY(c8rtomb_iso_8859_15_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xe2, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xac, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0xa4 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_utf_8_test);
+ATF_TC_BODY(c8rtomb_utf_8_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xa9, &s)), 4, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0xf0 &&
+ (unsigned char)buf[1] == 0x9f &&
+ (unsigned char)buf[2] == 0x92 &&
+ (unsigned char)buf[3] == 0xa9 &&
+ (unsigned char)buf[4] == 0xcc),
+ "buf=[%02x %02x %02x %02x %02x]",
+ buf[0], buf[1], buf[2], buf[3], buf[4]);
+
+ /* Invalid code; 'Pile of poo' without the last byte. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 'A', &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Invalid code; 'Pile of poo' without the first byte. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, c8rtomb_c_locale_test);
+ ATF_TP_ADD_TC(tp, c8rtomb_iso2022jp_locale_test);
+ ATF_TP_ADD_TC(tp, c8rtomb_iso_8859_1_test);
+ ATF_TP_ADD_TC(tp, c8rtomb_iso_8859_15_test);
+ ATF_TP_ADD_TC(tp, c8rtomb_utf_8_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/locale/t_mbrtoc16.c b/lib/libc/locale/t_mbrtoc16.c
new file mode 100644
index 000000000000..cac804c84eed
--- /dev/null
+++ b/lib/libc/locale/t_mbrtoc16.c
@@ -0,0 +1,364 @@
+/* $NetBSD: t_mbrtoc16.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Test program for mbrtoc16() as specified by ISO/IEC 9899:2011.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_mbrtoc16.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $");
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.h>
+#include <string.h>
+#include <uchar.h>
+
+#include <atf-c.h>
+
+static void
+require_lc_ctype(const char *locale_name)
+{
+ char *lc_ctype_set;
+
+ lc_ctype_set = setlocale(LC_CTYPE, locale_name);
+ if (lc_ctype_set == NULL)
+ atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d",
+ locale_name, errno);
+
+ ATF_REQUIRE_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0,
+ "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name);
+}
+
+static mbstate_t s;
+static char16_t c16;
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_c_locale_test);
+ATF_TC_BODY(mbrtoc16_c_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("C");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Incomplete character sequence. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%"PRIx16" L'z'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Check that mbrtoc16() doesn't access the buffer when n == 0. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%"PRIx16" L'z'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Check that mbrtoc16() doesn't read ahead too aggressively. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "AB", 2, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "C", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'C', "c16=U+%"PRIx16" L'C'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'C');
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_iso2022jp_locale_test);
+ATF_TC_BODY(mbrtoc16_iso2022jp_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("ja_JP.ISO-2022-JP");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16" L'A'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16" L'A'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Incomplete character sequence. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%04"PRIx16" L'z'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Check that mbrtoc16() doesn't access the buffer when n == 0. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%04"PRIx16" L'z'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Check that mbrtoc16() doesn't read ahead too aggressively. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "AB", 2, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16" L'A'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "C", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'C', "c16=U+%04"PRIx16" L'C'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'C');
+
+ /* Incomplete character sequence (shift sequence only). */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(J", 3, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /* Same as above, but complete (U+00A5 YEN SIGN). */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(J\x5c", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xa5, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /* Test restarting behaviour. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(", 2, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "J\x5c", 2, &s)), 2, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xa5, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /*
+ * Test shift sequence state in various increments:
+ * 1. U+0042 LATIN CAPITAL LETTER A
+ * 2. (shift ISO/IEC 646:JP) U+00A5 YEN SIGN
+ * 3. U+00A5 YEN SIGN
+ * 4. (shift JIS X 0208) U+30A2 KATAKANA LETTER A
+ * 5. U+30A2 KATAKANA LETTER A
+ * 6. (shift to initial state) U+0000 NUL
+ */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A\x1b(J", 4, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(J", 3, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x5c\x5c", 2, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x00a5, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x5c\x1b$", 3, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x00a5, "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 0x1234;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x1234, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "$B\x25\x22", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x30a2, "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x25", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x22\x1b(B\x00", 5, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x30a2, "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(", 2, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 42;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "B\x00", 2, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_iso_8859_1_test);
+ATF_TC_BODY(mbrtoc16_iso_8859_1_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Currency sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xa4", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xa4, "c16=U+%"PRIx16, (uint16_t)c16);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_iso_8859_15_test);
+ATF_TC_BODY(mbrtoc16_iso_8859_15_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Euro sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xa4", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x20ac, "c16=U+%"PRIx16, (uint16_t)c16);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_utf_8_test);
+ATF_TC_BODY(mbrtoc16_utf_8_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Incomplete character sequence (zero length). */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%"PRIx16" L'z'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Incomplete character sequence (truncated double-byte). */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+
+ /* Same as above, but complete. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3\x84", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xc4, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Test restarting behaviour. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xb7", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xf7, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Surrogate pair. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xf0\x9f\x92\xa9", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xd83d, "c16=U+%"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xdca9, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Letter e with acute, precomposed. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3\xa9", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xe9, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Letter e with acute, combined. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x65\xcc\x81", 3, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x65, "c16=U+%"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xcc\x81", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x301, "c16=U+%"PRIx16, (uint16_t)c16);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbrtoc16_c_locale_test);
+ ATF_TP_ADD_TC(tp, mbrtoc16_iso2022jp_locale_test);
+ ATF_TP_ADD_TC(tp, mbrtoc16_iso_8859_1_test);
+ ATF_TP_ADD_TC(tp, mbrtoc16_iso_8859_15_test);
+ ATF_TP_ADD_TC(tp, mbrtoc16_utf_8_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/locale/t_mbrtoc32.c b/lib/libc/locale/t_mbrtoc32.c
new file mode 100644
index 000000000000..8ccc421400a8
--- /dev/null
+++ b/lib/libc/locale/t_mbrtoc32.c
@@ -0,0 +1,61 @@
+/* $NetBSD: t_mbrtoc32.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_mbrtoc32.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $");
+
+#include <atf-c.h>
+#include <locale.h>
+#include <uchar.h>
+
+#include "h_macros.h"
+
+ATF_TC(mbrtoc32_null);
+ATF_TC_HEAD(mbrtoc32_null, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test null string input to mbrtoc32");
+}
+ATF_TC_BODY(mbrtoc32_null, tc)
+{
+ char *locale;
+ char32_t c32;
+ mbstate_t ps = {0};
+ size_t n;
+
+ REQUIRE_LIBC((locale = setlocale(LC_ALL, "C")), NULL);
+ ATF_REQUIRE_EQ_MSG(strcmp(locale, "C"), 0, "locale=%s", locale);
+
+ ATF_CHECK_EQ_MSG((n = mbrtoc32(&c32, NULL, 0, &ps)), 0, "n=%zu", n);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbrtoc32_null);
+ return atf_no_error();
+}
diff --git a/lib/libc/locale/t_mbrtoc8.c b/lib/libc/locale/t_mbrtoc8.c
new file mode 100644
index 000000000000..760f19a62f08
--- /dev/null
+++ b/lib/libc/locale/t_mbrtoc8.c
@@ -0,0 +1,415 @@
+/* $NetBSD: t_mbrtoc8.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Test program for mbrtoc8() as specified by C23.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_mbrtoc8.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $");
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.h>
+#include <string.h>
+#include <uchar.h>
+
+#include <atf-c.h>
+
+static void
+require_lc_ctype(const char *locale_name)
+{
+ char *lc_ctype_set;
+
+ lc_ctype_set = setlocale(LC_CTYPE, locale_name);
+ if (lc_ctype_set == NULL)
+ atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d",
+ locale_name, errno);
+
+ ATF_REQUIRE_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0,
+ "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name);
+}
+
+static mbstate_t s;
+static char8_t c8;
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_c_locale_test);
+ATF_TC_BODY(mbrtoc8_c_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("C");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Incomplete character sequence. */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Check that mbrtoc8() doesn't access the buffer when n == 0. */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Check that mbrtoc8() doesn't read ahead too aggressively. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "AB", 2, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "C", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'C', "c8=0x%"PRIx8" 'C'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'C');
+
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_iso2022jp_locale_test);
+ATF_TC_BODY(mbrtoc8_iso2022jp_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("ja_JP.ISO-2022-JP");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Incomplete character sequence. */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Check that mbrtoc8() doesn't access the buffer when n == 0. */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Check that mbrtoc8() doesn't read ahead too aggressively. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "AB", 2, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "C", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'C', "c8=0x%"PRIx8" 'C'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'C');
+
+ /* Incomplete character sequence (shift sequence only). */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(J", 3, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Same as above, but complete (U+00A5 YEN SIGN). */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(J\x5c", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Test restarting behaviour. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "J\x5c", 2, &s)), 2, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /*
+ * Test shift sequence state in various increments:
+ * 1. U+0042 LATIN CAPITAL LETTER A
+ * 2. (shift ISO/IEC 646:JP) U+00A5 YEN SIGN
+ * 3. U+00A5 YEN SIGN
+ * 4. (shift JIS X 0208) U+30A2 KATAKANA LETTER A
+ * 5. U+30A2 KATAKANA LETTER A
+ * 6. (shift to initial state) U+0000 NUL
+ */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A\x1b(J", 4, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(J", 3, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x5c\x5c", 2, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x5c\x1b$", 3, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x5c\x1b$", 3, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b", 1, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 0xff;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xff, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "$B\x25\x22", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xe3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x25", 1, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x82, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x25", 1, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa2, "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x25", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x22\x1b(B\x00", 5, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xe3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x82, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa2, "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 42;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "B\x00", 2, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_iso_8859_1_test);
+ATF_TC_BODY(mbrtoc8_iso_8859_1_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Currency sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xa4", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa4, "c8=0x%"PRIx8, (uint8_t)c8);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_iso_8859_15_test);
+ATF_TC_BODY(mbrtoc8_iso_8859_15_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Euro sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xa4", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xe2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x82, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xac, "c8=0x%"PRIx8, (uint8_t)c8);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_utf_8_test);
+ATF_TC_BODY(mbrtoc8_utf_8_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Incomplete character sequence (zero length). */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Incomplete character sequence (truncated double-byte). */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+
+ /* Same as above, but complete. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3\x84", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x84, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Test restarting behaviour. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xb7", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xb7, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Four-byte sequence. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xf0\x9f\x92\xa9", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xf0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x9f, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x92, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa9, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Letter e with acute, precomposed. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3\xa9", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa9, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Letter e with acute, combined. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x65\xcc\x81", 3, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x65, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xcc\x81", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xcc, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x81, "c8=0x%"PRIx8, (uint8_t)c8);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbrtoc8_c_locale_test);
+ ATF_TP_ADD_TC(tp, mbrtoc8_iso2022jp_locale_test);
+ ATF_TP_ADD_TC(tp, mbrtoc8_iso_8859_1_test);
+ ATF_TP_ADD_TC(tp, mbrtoc8_iso_8859_15_test);
+ ATF_TP_ADD_TC(tp, mbrtoc8_utf_8_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/locale/t_uchar.c b/lib/libc/locale/t_uchar.c
new file mode 100644
index 000000000000..66e4830fb88a
--- /dev/null
+++ b/lib/libc/locale/t_uchar.c
@@ -0,0 +1,81 @@
+/* $NetBSD: t_uchar.c,v 1.3 2024/10/14 06:02:14 rillig Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Include <uchar.h> first to verify it declares everything we need.
+ */
+#include <uchar.h>
+typedef mbstate_t nbtest_mbstate_t;
+typedef size_t nbtest_size_t;
+typedef char8_t nbtest_char8_t;
+typedef char16_t nbtest_char16_t;
+typedef char32_t nbtest_char32_t;
+static size_t (*nbtest_mbrtoc8)(char8_t *restrict, const char *restrict,
+ size_t, mbstate_t *restrict) __unused = &mbrtoc8;
+static size_t (*nbtest_c8rtomb)(char *restrict, char8_t,
+ mbstate_t *restrict) __unused = &c8rtomb;
+static size_t (*nbtest_mbrtoc16)(char16_t *restrict, const char *restrict,
+ size_t, mbstate_t *restrict) __unused = &mbrtoc16;
+static size_t (*nbtest_c16rtomb)(char *restrict, char16_t,
+ mbstate_t *restrict) __unused = &c16rtomb;
+static size_t (*nbtest_mbrtoc32)(char32_t *restrict, const char *restrict,
+ size_t, mbstate_t *restrict) __unused = mbrtoc32;
+static size_t (*nbtest_c32rtomb)(char *restrict, char32_t,
+ mbstate_t *restrict) __unused = &c32rtomb;
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_uchar.c,v 1.3 2024/10/14 06:02:14 rillig Exp $");
+
+#include <atf-c.h>
+#include <stdint.h>
+
+ATF_TC(uchartypes);
+ATF_TC_HEAD(uchartypes, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test <uchar.h> types are reasonable");
+}
+ATF_TC_BODY(uchartypes, tc)
+{
+
+ ATF_CHECK_EQ_MSG(sizeof(char8_t), sizeof(unsigned char),
+ "char8_t %zu, unsigned char %zu",
+ sizeof(char8_t), sizeof(unsigned char));
+ ATF_CHECK_EQ_MSG(sizeof(char16_t), sizeof(uint_least16_t),
+ "char16_t %zu, uint_least16_t %zu",
+ sizeof(char16_t), sizeof(uint_least16_t));
+ ATF_CHECK_EQ_MSG(sizeof(char32_t), sizeof(uint_least32_t),
+ "char32_t %zu, uint_least32_t %zu",
+ sizeof(char32_t), sizeof(uint_least32_t));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, uchartypes);
+ return atf_no_error();
+}
diff --git a/lib/libc/misc/t_vis.c b/lib/libc/misc/t_vis.c
new file mode 100644
index 000000000000..a712ffa2f9d2
--- /dev/null
+++ b/lib/libc/misc/t_vis.c
@@ -0,0 +1,165 @@
+/* $NetBSD: t_vis.c,v 1.2 2025/11/16 12:43:25 nia Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nia Alarie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+#include <string.h>
+#include <stdio.h>
+#include "vis_types.h"
+#include "vis_proto.h"
+
+ATF_TC(vis_test_addsub);
+
+ATF_TC_HEAD(vis_test_addsub, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test 32-bit packed add/subtract");
+}
+
+ATF_TC_BODY(vis_test_addsub, tc)
+{
+ vis_d64 v1, v2, v3;
+ vis_f32 f1, f2;
+ vis_u32 u1, u2;
+
+ v1 = vis_to_double(8, 16);
+ v2 = vis_to_double(16, 8);
+
+ v3 = vis_fpadd32(v1, v2);
+
+ f1 = vis_read_lo(v3);
+ memcpy(&u1, &f1, sizeof(f1));
+ f2 = vis_read_hi(v3);
+ memcpy(&u2, &f2, sizeof(f2));
+
+ ATF_REQUIRE(u1 == 24 && u2 == 24);
+
+ v2 = vis_to_double(4, 4);
+ v3 = vis_fpsub32(v3, v2);
+
+ f1 = vis_read_lo(v3);
+ memcpy(&u1, &f1, sizeof(f1));
+ f2 = vis_read_hi(v3);
+ memcpy(&u2, &f2, sizeof(f2));
+
+ ATF_REQUIRE(u1 == 20 && u2 == 20);
+}
+
+ATF_TC(vis_test_bitwise);
+
+ATF_TC_HEAD(vis_test_bitwise, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test 32-bit packed bitwise");
+}
+
+ATF_TC_BODY(vis_test_bitwise, tc)
+{
+ static vis_u8 testbytes1[8] = { 1, 0, 1, 1, 1, 0, 1, 1 };
+ static vis_u8 testbytes2[8] = { 1, 1, 0, 1, 1, 1, 0, 1 };
+ static vis_u8 test_and[8] = { 1, 0, 0, 1, 1, 0, 0, 1 };
+ static vis_u8 test_or[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+ static vis_u8 test_zero[8] = { 0 };
+ static vis_u64 test_ones = 0xffffffffffffffff;
+ double v1, v2, v3;
+
+ memcpy(&v1, testbytes1, sizeof(v1));
+ memcpy(&v2, testbytes2, sizeof(v2));
+
+ v3 = vis_fand(v1, v2);
+
+ ATF_REQUIRE(memcmp(&v3, test_and, sizeof(v3)) == 0);
+
+ v3 = vis_fone();
+
+ ATF_REQUIRE(memcmp(&v3, &test_ones, sizeof(v3)) == 0);
+
+ v3 = vis_for(v1, v2);
+
+ ATF_REQUIRE(memcmp(&v3, test_or, sizeof(v3)) == 0);
+
+ v3 = vis_fzero();
+
+ ATF_REQUIRE(memcmp(&v3, test_zero, sizeof(v3)) == 0);
+}
+
+ATF_TC(vis_test_fcmpeq16);
+
+ATF_TC_HEAD(vis_test_fcmpeq16, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test 16-bit packed compare");
+}
+
+ATF_TC_BODY(vis_test_fcmpeq16, tc)
+{
+ static vis_u16 testshort1[4] = { 16000, 16000, 16000, 16000 };
+ static vis_u16 testshort2[4] = { 32000, 16000, 32000, 16000 };
+ static vis_u16 testshort3[4] = { 48000, 48000, 48000, 48000 };
+ vis_d64 v1, v2, v3;
+
+ memcpy(&v1, testshort1, sizeof(v1));
+ memcpy(&v2, testshort2, sizeof(v2));
+ memcpy(&v3, testshort3, sizeof(v3));
+
+ ATF_REQUIRE((!!vis_fcmpeq16(v1, v2)) != 0);
+ ATF_REQUIRE((!!vis_fcmpeq16(v1, v3)) == 0);
+ ATF_REQUIRE((!!vis_fcmpne16(v1, v3)) != 0);
+}
+
+ATF_TC(vis_test_fcmpeq32);
+
+ATF_TC_HEAD(vis_test_fcmpeq32, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test 32-bit packed compare");
+}
+
+ATF_TC_BODY(vis_test_fcmpeq32, tc)
+{
+ static vis_u32 testlong1[2] = { 16000, 16000 };
+ static vis_u32 testlong2[2] = { 32000, 16000 };
+ static vis_u32 testlong3[2] = { 48000, 48000 };
+ vis_d64 v1, v2, v3;
+
+ memcpy(&v1, testlong1, sizeof(v1));
+ memcpy(&v2, testlong2, sizeof(v2));
+ memcpy(&v3, testlong3, sizeof(v3));
+
+ ATF_REQUIRE((!!vis_fcmpeq32(v1, v2)) != 0);
+ ATF_REQUIRE((!!vis_fcmpeq32(v1, v3)) == 0);
+ ATF_REQUIRE((!!vis_fcmpne32(v1, v3)) != 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, vis_test_addsub);
+ ATF_TP_ADD_TC(tp, vis_test_bitwise);
+ ATF_TP_ADD_TC(tp, vis_test_fcmpeq16);
+ ATF_TP_ADD_TC(tp, vis_test_fcmpeq32);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/regex/t_regex_binary.c b/lib/libc/regex/t_regex_binary.c
new file mode 100644
index 000000000000..0c3d8cb9b908
--- /dev/null
+++ b/lib/libc/regex/t_regex_binary.c
@@ -0,0 +1,80 @@
+/* $NetBSD: t_regex_binary.c,v 1.1 2025/01/01 18:13:48 christos Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_regex_binary.c,v 1.1 2025/01/01 18:13:48 christos Exp $");
+
+#include <atf-c.h>
+#include <regex.h>
+
+ATF_TC(negative_ranges);
+ATF_TC_HEAD(negative_ranges, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test negative ranges compilation");
+}
+ATF_TC_BODY(negative_ranges, tc)
+{
+ regex_t re;
+ char msg[1024];
+ int e;
+
+ if ((e = regcomp(&re, "[\xe0-\xf1][\xa0-\xd1].*", REG_EXTENDED)) != 0) {
+ regerror(e, &re, msg, sizeof(msg));
+ ATF_REQUIRE_MSG(0, "regcomp failed %s", msg);
+ }
+}
+
+ATF_TC(negative_char);
+ATF_TC_HEAD(negative_char, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test negative char in braces compilation");
+}
+ATF_TC_BODY(negative_char, tc)
+{
+ regex_t re;
+ char msg[1024];
+ int e;
+
+ /* PR/58910 */
+ if ((e = regcomp(&re, ": j:[]j:[]j:[\xd9j:[]", REG_EXTENDED)) != 0) {
+ regerror(e, &re, msg, sizeof(msg));
+ ATF_REQUIRE_MSG(0, "regcomp failed %s", msg);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, negative_ranges);
+ ATF_TP_ADD_TC(tp, negative_char);
+ return atf_no_error();
+}
diff --git a/lib/libc/setjmp/t_sigstack.c b/lib/libc/setjmp/t_sigstack.c
new file mode 100644
index 000000000000..ea827da9545c
--- /dev/null
+++ b/lib/libc/setjmp/t_sigstack.c
@@ -0,0 +1,389 @@
+/* $NetBSD: t_sigstack.c,v 1.25 2025/05/12 14:46:19 christos Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_sigstack.c,v 1.25 2025/05/12 14:46:19 christos Exp $");
+
+#include <dlfcn.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <ucontext.h>
+
+#include "h_macros.h"
+
+struct sigaltstack ss[3];
+jmp_buf jmp;
+sigjmp_buf sigjmp;
+unsigned nentries;
+const char *bailname;
+void (*bailfn)(void) __dead;
+
+/*
+ * Optional compat13 functions from when sigcontext was expanded.
+ * Fortunately the only change visible to the caller is that the size
+ * of jmp_buf increased, so we can always use the old symbols with new
+ * jmp_buf arrays.
+ */
+int (*compat13_sigsetjmp)(sigjmp_buf, int);
+void (*compat13_siglongjmp)(sigjmp_buf, int) __dead;
+int (*compat13_setjmp)(jmp_buf);
+void (*compat13_longjmp)(jmp_buf, int) __dead;
+
+/*
+ * compatsigsys(signo)
+ *
+ * Signal handler for SIGSYS in case compat_13_sigreturn13 is not
+ * implemented by the kernel -- we will just skip the test in that
+ * case.
+ */
+static void
+compatsigsys(int signo)
+{
+
+ atf_tc_skip("no compat syscalls to test");
+}
+
+static void
+compatsetup(void)
+{
+
+ /*
+ * Grab the libc library symbols if available.
+ */
+ if ((compat13_sigsetjmp = dlsym(RTLD_SELF, "sigsetjmp")) == NULL ||
+ (compat13_siglongjmp = dlsym(RTLD_SELF, "siglongjmp")) == NULL ||
+ (compat13_setjmp = dlsym(RTLD_SELF, "setjmp")) == NULL ||
+ (compat13_longjmp = dlsym(RTLD_SELF, "longjmp")) == NULL)
+ atf_tc_skip("no compat functions to test");
+
+ /*
+ * Arrange for SIGSYS to skip the test -- this happens if the
+ * libc stub has the function, but the kernel isn't built with
+ * support for the compat13 sigreturn syscall for longjmp.
+ */
+ REQUIRE_LIBC(signal(SIGSYS, &compatsigsys), SIG_ERR);
+}
+
+static void
+on_sigusr1(int signo, siginfo_t *si, void *ctx)
+{
+ ucontext_t *uc = ctx;
+ void *sp = (void *)(uintptr_t)_UC_MACHINE_SP(uc);
+ void *fp = __builtin_frame_address(0);
+ struct sigaltstack *ssp;
+
+ /*
+ * Ensure we haven't re-entered the signal handler too many
+ * times. We should enter only twice.
+ */
+ ATF_REQUIRE_MSG(nentries < 2,
+ "%u recursive signal handler entries is too many in this test",
+ nentries + 1);
+
+ /*
+ * Ensure that the signal handler was called in the alternate
+ * signal stack.
+ */
+ ssp = &ss[nentries];
+ ATF_REQUIRE_MSG((fp >= ssp->ss_sp &&
+ fp < (void *)((char *)ssp->ss_sp + ssp->ss_size)),
+ "sigaltstack failed to take effect on entry %u --"
+ " signal handler's frame pointer %p doesn't lie in sigaltstack"
+ " [%p, %p), size 0x%zx",
+ nentries,
+ fp, ssp->ss_sp, (char *)ssp->ss_sp + ssp->ss_size, ssp->ss_size);
+
+ /*
+ * Ensure that if we enter the signal handler, we are entering
+ * it from the original stack, not from any of the alternate
+ * signal stacks.
+ */
+ for (ssp = &ss[0]; ssp < &ss[__arraycount(ss)]; ssp++) {
+ ATF_REQUIRE_MSG((sp < ssp->ss_sp ||
+ sp >= (void *)((char *)ssp->ss_sp + ssp->ss_size)),
+ "%s failed to restore stack"
+ " before allowing signal on entry %u --"
+ " interrupted stack pointer %p lies in sigaltstack %zd"
+ " [%p, %p), size 0x%zx",
+ bailname,
+ nentries,
+ sp, ssp - ss,
+ ssp->ss_sp, (char *)ssp->ss_sp + ssp->ss_size,
+ ssp->ss_size);
+ }
+
+ /*
+ * First time through, we want to test whether longjmp restores
+ * the signal mask first, or restores the stack pointer first.
+ * The signal should be blocked at this point, so we re-raise
+ * the signal to queue it up for delivery as soon as it is
+ * unmasked -- which should wait until the stack pointer has
+ * been restored in longjmp.
+ */
+ if (nentries++ == 0)
+ RL(raise(SIGUSR1));
+
+ /*
+ * Set up the next sigaltstack. We can't reuse the current one
+ * for the next signal handler re-entry until the system clears
+ * the SS_ONSTACK process state -- which normal return from
+ * signal handler does, but which longjmp does not. So to keep
+ * it simple (ha), we just use another sigaltstack.
+ */
+ RL(sigaltstack(&ss[nentries], NULL));
+
+ /*
+ * Jump back to the original context.
+ */
+ (*bailfn)();
+}
+
+static void
+go(const char *name, void (*fn)(void) __dead)
+{
+ struct sigaction sa;
+ unsigned i;
+
+ bailname = name;
+ bailfn = fn;
+
+ /*
+ * Allocate a stack for the signal handler to run in, and
+ * configure the system to use the first one.
+ *
+ * XXX Should maybe use a guard page but this is simpler.
+ */
+ for (i = 0; i < __arraycount(ss); i++) {
+ ss[i].ss_size = SIGSTKSZ;
+ REQUIRE_LIBC(ss[i].ss_sp = malloc(ss[i].ss_size), NULL);
+ }
+ RL(sigaltstack(&ss[0], NULL));
+
+ /*
+ * Set up a test signal handler for SIGUSR1. Allow all
+ * signals, except SIGUSR1 (which is masked by default) -- that
+ * way we don't inadvertently obscure weird crashes in the
+ * signal handler.
+ *
+ * Set SA_SIGINFO so the system will pass siginfo -- and, more
+ * to the point, ucontext, so the signal handler can determine
+ * the stack pointer of the logic it interrupted.
+ *
+ * Set SA_ONSTACK so the system will use the alternate signal
+ * stack to call the signal handler -- that way, it can tell
+ * whether the stack was restored before the second time
+ * around.
+ */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = &on_sigusr1;
+ RL(sigemptyset(&sa.sa_mask));
+ sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
+ RL(sigaction(SIGUSR1, &sa, NULL));
+
+ /*
+ * Raise the signal to enter the signal handler the first time.
+ */
+ RL(raise(SIGUSR1));
+
+ /*
+ * If we ever reach this point, something went seriously wrong.
+ */
+ atf_tc_fail("unreachable");
+}
+
+static void __dead
+bail_longjmp(void)
+{
+
+ longjmp(jmp, 1);
+}
+
+ATF_TC(setjmp);
+ATF_TC_HEAD(setjmp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test longjmp restores stack first, then signal mask");
+}
+ATF_TC_BODY(setjmp, tc)
+{
+
+#if defined __ia64__
+ atf_tc_expect_fail("PR lib/57946:"
+ " longjmp fails to restore stack first before"
+ " restoring signal mask on most architectures");
+#endif
+
+ /*
+ * Set up a return point for the signal handler: when the
+ * signal handler does longjmp(jmp, 1), it comes flying out of
+ * here.
+ */
+ if (setjmp(jmp) == 1)
+ return;
+
+ /*
+ * Run the test with longjmp.
+ */
+ go("longjmp", &bail_longjmp);
+}
+
+static void __dead
+bail_compat13_longjmp(void)
+{
+
+ (*compat13_longjmp)(jmp, 1);
+}
+
+ATF_TC(compat13_setjmp);
+ATF_TC_HEAD(compat13_setjmp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test compat13 longjmp restores stack first, then signal mask");
+}
+ATF_TC_BODY(compat13_setjmp, tc)
+{
+
+ compatsetup();
+
+#if defined __arm__ || defined __i386__ || defined __sh3__
+#ifndef __arm__ /* will be exposed once PR 59351 is fixed */
+ atf_tc_expect_fail("PR lib/57946:"
+ " longjmp fails to restore stack first before"
+ " restoring signal mask on most architectures");
+#endif
+#endif
+#ifdef __arm__
+ atf_tc_expect_signal(-1, "PR port-arm/59351: compat_setjmp is busted");
+#endif
+
+ /*
+ * Set up a return point for the signal handler: when the
+ * signal handler does (*compat13_longjmp)(jmp, 1), it comes
+ * flying out of here.
+ */
+ if ((*compat13_setjmp)(jmp) == 1)
+ return;
+
+ /*
+ * Run the test with compat13_longjmp.
+ */
+ go("longjmp", &bail_compat13_longjmp);
+}
+
+static void __dead
+bail_siglongjmp(void)
+{
+
+ siglongjmp(sigjmp, 1);
+}
+
+ATF_TC(sigsetjmp);
+ATF_TC_HEAD(sigsetjmp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test siglongjmp restores stack first, then signal mask");
+}
+ATF_TC_BODY(sigsetjmp, tc)
+{
+
+#if defined __ia64__
+ atf_tc_expect_fail("PR lib/57946:"
+ " longjmp fails to restore stack first before"
+ " restoring signal mask on most architectures");
+#endif
+
+ /*
+ * Set up a return point for the signal handler: when the
+ * signal handler does siglongjmp(sigjmp, 1), it comes flying
+ * out of here.
+ */
+ if (sigsetjmp(sigjmp, /*savesigmask*/1) == 1)
+ return;
+
+ /*
+ * Run the test with siglongjmp.
+ */
+ go("siglongjmp", &bail_siglongjmp);
+}
+
+static void __dead
+bail_compat13_siglongjmp(void)
+{
+
+ (*compat13_siglongjmp)(sigjmp, 1);
+}
+
+ATF_TC(compat13_sigsetjmp);
+ATF_TC_HEAD(compat13_sigsetjmp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test compat13 siglongjmp restores stack first,"
+ " then signal mask");
+}
+ATF_TC_BODY(compat13_sigsetjmp, tc)
+{
+
+ compatsetup();
+
+#if defined __arm__ || defined __i386__ || defined __sh3__
+#ifndef __arm__ /* will be exposed once PR 59351 is fixed */
+ atf_tc_expect_fail("PR lib/57946:"
+ " longjmp fails to restore stack first before"
+ " restoring signal mask on most architectures");
+#endif
+#endif
+#ifdef __arm__
+ atf_tc_expect_signal(-1, "PR port-arm/59351: compat_setjmp is busted");
+#endif
+
+ /*
+ * Set up a return point for the signal handler: when the
+ * signal handler does (*compat13_siglongjmp)(sigjmp, 1), it
+ * comes flying out of here.
+ */
+ if ((*compat13_sigsetjmp)(sigjmp, /*savesigmask*/1) == 1)
+ return;
+
+ /*
+ * Run the test with compat13_siglongjmp.
+ */
+ go("siglongjmp", &bail_compat13_siglongjmp);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, compat13_setjmp);
+ ATF_TP_ADD_TC(tp, compat13_sigsetjmp);
+ ATF_TP_ADD_TC(tp, setjmp);
+ ATF_TP_ADD_TC(tp, sigsetjmp);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/ssp/h_getcwd2.c b/lib/libc/ssp/h_getcwd2.c
new file mode 100644
index 000000000000..86cfd09f2bb0
--- /dev/null
+++ b/lib/libc/ssp/h_getcwd2.c
@@ -0,0 +1,18 @@
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+
+char*
+getcwd(char* buf, size_t buflen)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+int
+main(void)
+{
+ char buf[256];
+ if (getcwd(buf, sizeof buf) == NULL)
+ err(1, "getcwd failed");
+ return 0;
+}
diff --git a/lib/libc/stdlib/h_sort.c b/lib/libc/stdlib/h_sort.c
new file mode 100644
index 000000000000..1e5f4b3b0798
--- /dev/null
+++ b/lib/libc/stdlib/h_sort.c
@@ -0,0 +1,225 @@
+/* $NetBSD: h_sort.c,v 1.3 2025/03/02 23:11:19 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_sort.c,v 1.3 2025/03/02 23:11:19 riastradh Exp $");
+
+#include <assert.h>
+#include <err.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void
+heapsort_r_wrapper(void *a, size_t n, size_t sz,
+ int (*cmp)(const void *, const void *, void *), void *cookie)
+{
+
+ if (heapsort_r(a, n, sz, cmp, cookie) == -1)
+ err(1, "heapsort_r");
+}
+
+static void
+mergesort_r_wrapper(void *a, size_t n, size_t sz,
+ int (*cmp)(const void *, const void *, void *), void *cookie)
+{
+
+ if (mergesort_r(a, n, sz, cmp, cookie) == -1)
+ err(1, "mergesort_r");
+}
+
+struct context {
+ const char *buf;
+ const size_t *linepos;
+};
+
+static int
+cmp(const void *va, const void *vb, void *cookie)
+{
+ const struct context *C = cookie;
+ const size_t *a = va;
+ const size_t *b = vb;
+
+ return strcmp(C->buf + C->linepos[*a], C->buf + C->linepos[*b]);
+}
+
+static void __dead
+usage(void)
+{
+
+ fprintf(stderr, "Usage: %s [-n] <sortfn>\n", getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ int nflag = 0;
+ void (*sortfn)(void *, size_t, size_t,
+ int (*)(const void *, const void *, void *), void *);
+ char *buf = NULL;
+ size_t nbuf;
+ size_t *linepos = NULL;
+ size_t nlines;
+ size_t *permutation = NULL;
+ size_t off;
+ ssize_t nread;
+ char *p;
+ size_t i;
+ int error;
+
+ /*
+ * Parse arguments.
+ */
+ setprogname(argv[0]);
+ while ((ch = getopt(argc, argv, "hn")) != -1) {
+ switch (ch) {
+ case 'n':
+ nflag = 1;
+ break;
+ case '?':
+ case 'h':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ if (strcmp(argv[0], "heapsort_r") == 0)
+ sortfn = &heapsort_r_wrapper;
+ else if (strcmp(argv[0], "mergesort_r") == 0)
+ sortfn = &mergesort_r_wrapper;
+ else if (strcmp(argv[0], "qsort_r") == 0)
+ sortfn = &qsort_r;
+ else
+ errx(1, "unknown sort: %s", argv[0]);
+
+ /*
+ * Allocate an initial 4K buffer.
+ */
+ nbuf = 0x1000;
+ error = reallocarr(&buf, nbuf, 1);
+ if (error)
+ errc(1, error, "reallocarr");
+
+ /*
+ * Read the input into a contiguous buffer. Reject input with
+ * embedded NULs so we can use strcmp(3) to compare lines.
+ */
+ off = 0;
+ while ((nread = read(STDIN_FILENO, buf + off, nbuf - off - 1)) != 0) {
+ if (nread == -1)
+ err(1, "read");
+ if ((size_t)nread > nbuf - off - 1)
+ errx(1, "overlong read: %zu", (size_t)nread);
+ if (memchr(buf + off, '\0', (size_t)nread) != NULL)
+ errx(1, "NUL byte in input");
+ off += (size_t)nread;
+
+ /*
+ * If we filled the buffer, reallocate it with double
+ * the size. Bail if that would overflow.
+ */
+ if (nbuf - off == 1) {
+ if (nbuf > SIZE_MAX/2)
+ errx(1, "input overflow");
+ nbuf *= 2;
+ error = reallocarr(&buf, nbuf, 1);
+ if (error)
+ errc(1, error, "reallocarr");
+ }
+ }
+
+ /*
+ * If the input was empty, nothing to do.
+ */
+ if (off == 0)
+ return 0;
+
+ /*
+ * NUL-terminate the input and count the lines. The last line
+ * may have no trailing \n.
+ */
+ buf[off] = '\0';
+ nlines = 1;
+ for (p = buf; (p = strchr(p, '\n')) != NULL;) {
+ if (*++p == '\0')
+ break;
+ nlines++;
+ }
+
+ /*
+ * Create an array of line positions to sort. NUL-terminate
+ * each line so we can use strcmp(3).
+ */
+ error = reallocarr(&linepos, nlines, sizeof(linepos[0]));
+ if (error)
+ errc(1, error, "reallocarr");
+ i = 0;
+ for (p = buf; linepos[i++] = p - buf, (p = strchr(p, '\n')) != NULL;) {
+ *p = '\0';
+ if (*++p == '\0')
+ break;
+ }
+ assert(i == nlines);
+
+ /*
+ * Create an array of permuted line numbers.
+ */
+ error = reallocarr(&permutation, nlines, sizeof(permutation[0]));
+ if (error)
+ errc(1, error, "reallocarr");
+ for (i = 0; i < nlines; i++)
+ permutation[i] = i;
+
+ /*
+ * Sort the lines via comparison function that consults the
+ * buffer as a cookie.
+ */
+ (*sortfn)(permutation, nlines, sizeof(permutation[0]), &cmp,
+ &(struct context) { .buf = buf, .linepos = linepos });
+
+ /*
+ * Print the lines in sorted order with the original line
+ * numbers.
+ */
+ for (i = 0; i < nlines; i++) {
+ const size_t j = permutation[i];
+ if (nflag)
+ printf("%zu %s\n", j, buf + linepos[j]);
+ else
+ printf("%s\n", buf + linepos[j]);
+ }
+ fflush(stdout);
+ return ferror(stdout);
+}
diff --git a/lib/libc/stdlib/t_sort.sh b/lib/libc/stdlib/t_sort.sh
new file mode 100644
index 000000000000..8987ac1ec623
--- /dev/null
+++ b/lib/libc/stdlib/t_sort.sh
@@ -0,0 +1,115 @@
+# $NetBSD: t_sort.sh,v 1.2 2025/03/02 20:00:32 riastradh Exp $
+#
+# Copyright (c) 2025 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+check_sort()
+{
+ local sortfn
+
+ set -eu
+
+ sortfn="$1"
+
+ printf 'foo\nbar\nbaz\nquux' >in1
+ printf '1 bar\n2 baz\n0 foo\n3 quux\n' >out1
+ atf_check -s exit:0 -o file:out1 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in1
+
+ atf_check -s exit:0 -o empty sh -c 'exec shuffle -f - <in1 >in2'
+ printf 'bar\nbaz\nfoo\nquux\n' >out2
+ atf_check -s exit:0 -o file:out2 \
+ "$(atf_get_srcdir)"/h_sort "$sortfn" <in2
+}
+
+check_stablesort()
+{
+ local sortfn
+
+ set -eu
+
+ sortfn="$1"
+
+ printf 'foo\nfoo\nfoo\nfoo\nfoo' >in1
+ printf '0 foo\n1 foo\n2 foo\n3 foo\n4 foo\n' >out1
+ atf_check -s exit:0 -o file:out1 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in1
+
+ printf 'foo\nfoo\nfoo\nfoo\nfoo\nbar\nbar\nbar\nbar\nbar' >in2
+ printf '5 bar\n6 bar\n7 bar\n8 bar\n9 bar\n' >out2
+ printf '0 foo\n1 foo\n2 foo\n3 foo\n4 foo\n' >>out2
+ atf_check -s exit:0 -o file:out2 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in2
+
+ printf 'foo\nfoo\nbar\nbaz\nquux' >in3
+ printf '2 bar\n3 baz\n0 foo\n1 foo\n4 quux\n' >out3
+ atf_check -s exit:0 -o file:out3 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in3
+
+ printf 'foo\nbar\nbar\nbaz\nquux' >in4
+ printf '1 bar\n2 bar\n3 baz\n0 foo\n4 quux\n' >out4
+ atf_check -s exit:0 -o file:out4 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in4
+
+ printf 'foo\nbar\nbaz\nbaz\nquux' >in5
+ printf '1 bar\n2 baz\n3 baz\n0 foo\n4 quux\n' >out5
+ atf_check -s exit:0 -o file:out5 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in5
+
+ printf 'foo\nbar\nbaz\nquux\nquux' >in6
+ printf '1 bar\n2 baz\n0 foo\n3 quux\n4 quux\n' >out6
+ atf_check -s exit:0 -o file:out6 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in6
+}
+
+sortfn_case()
+{
+ local sortfn
+
+ sortfn="$1"
+
+ eval "${sortfn}_head() { atf_set descr \"Test ${sortfn}\"; }"
+ eval "${sortfn}_body() { check_sort $sortfn; }"
+ atf_add_test_case "$sortfn"
+}
+
+stablesortfn_case()
+{
+ local sortfn
+
+ sortfn="$1"
+
+ eval "${sortfn}_stable_head() { atf_set descr \"Test ${sortfn}\"; }"
+ eval "${sortfn}_stable_body() { check_stablesort $sortfn; }"
+ atf_add_test_case "${sortfn}_stable"
+}
+
+atf_init_test_cases()
+{
+
+ sortfn_case heapsort_r
+ sortfn_case mergesort_r
+ sortfn_case qsort_r
+ stablesortfn_case mergesort_r
+}
diff --git a/lib/libc/sys/t_aio_cancel.c b/lib/libc/sys/t_aio_cancel.c
new file mode 100644
index 000000000000..43965cada251
--- /dev/null
+++ b/lib/libc/sys/t_aio_cancel.c
@@ -0,0 +1,223 @@
+/* $NetBSD: t_aio_cancel.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+static int mktemp_file(char *, size_t);
+static void fill_pattern(uint8_t *, size_t, uint8_t);
+static void wait_all(const struct aiocb * const [], size_t);
+
+static int
+mktemp_file(char *path, size_t pathlen)
+{
+ int fd, n;
+
+ n = snprintf(path, pathlen, "t_aio_cancel.XXXXXX");
+ ATF_REQUIRE(n > 0 && (size_t)n < pathlen);
+
+ fd = mkstemp(path);
+ ATF_REQUIRE(fd >= 0);
+
+ return fd;
+}
+
+static void
+fill_pattern(uint8_t *buf, size_t len, uint8_t seed)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t)(seed + (i & 0xff));
+ }
+}
+
+static void
+wait_all(const struct aiocb * const list[], size_t nent)
+{
+ size_t i;
+ int pending, rv;
+
+ for (;;) {
+ pending = 0;
+
+ for (i = 0; i < nent; i++) {
+ int err;
+
+ if (list[i] == NULL) {
+ continue;
+ }
+
+ err = aio_error(list[i]);
+ if (err == EINPROGRESS) {
+ pending = 1;
+ }
+ }
+
+ if (!pending) {
+ break;
+ }
+
+ rv = aio_suspend(list, (int)nent, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s",
+ strerror(errno));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(cancel_active_write);
+ATF_TC_BODY(cancel_active_write, tc)
+{
+ char path[64];
+ int fd, rv, crv, err;
+ const size_t blksz = 0x1000;
+ uint8_t *wbuf;
+ struct aiocb cb;
+ const struct aiocb *list[1];
+
+ fd = mktemp_file(path, sizeof(path));
+
+ wbuf = malloc(blksz);
+ ATF_REQUIRE(wbuf != NULL);
+ fill_pattern(wbuf, blksz, 0x33);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.aio_fildes = fd;
+ cb.aio_buf = wbuf;
+ cb.aio_nbytes = blksz;
+ cb.aio_offset = 0;
+
+ rv = aio_write(&cb);
+ ATF_REQUIRE_EQ(0, rv);
+
+ crv = aio_cancel(fd, &cb);
+ ATF_REQUIRE(crv == AIO_CANCELED || crv == AIO_NOTCANCELED
+ || crv == AIO_ALLDONE);
+
+ if (crv == AIO_CANCELED) {
+ do {
+ err = aio_error(&cb);
+ } while (err == EINPROGRESS);
+ ATF_REQUIRE_EQ(ECANCELED, err);
+ ATF_REQUIRE_EQ(-1, aio_return(&cb));
+ } else if (crv == AIO_NOTCANCELED) {
+ list[0] = &cb;
+ wait_all(list, 1);
+ ATF_REQUIRE_EQ(0, aio_error(&cb));
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb));
+ } else {
+ do {
+ err = aio_error(&cb);
+ } while (err == EINPROGRESS);
+ ATF_REQUIRE_EQ(0, err);
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb));
+ }
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+
+ free(wbuf);
+}
+
+ATF_TC_WITHOUT_HEAD(cancel_completed_request);
+ATF_TC_BODY(cancel_completed_request, tc)
+{
+ char path[64];
+ int fd, rv, crv;
+ const size_t blksz = 4096;
+ uint8_t *wbuf;
+ struct aiocb cb;
+ const struct aiocb *list[1];
+
+ fd = mktemp_file(path, sizeof(path));
+
+ wbuf = malloc(blksz);
+ ATF_REQUIRE(wbuf != NULL);
+ memset(wbuf, 0x7E, blksz);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.aio_fildes = fd;
+ cb.aio_buf = wbuf;
+ cb.aio_nbytes = blksz;
+ cb.aio_offset = 0;
+
+ rv = aio_write(&cb);
+ ATF_REQUIRE_EQ(0, rv);
+
+ list[0] = &cb;
+ wait_all(list, 1);
+ ATF_REQUIRE_EQ(0, aio_error(&cb));
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb));
+
+ crv = aio_cancel(fd, &cb);
+ ATF_REQUIRE_EQ(AIO_ALLDONE, crv);
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+
+ free(wbuf);
+}
+
+ATF_TC_WITHOUT_HEAD(cancel_invalid_fd);
+ATF_TC_BODY(cancel_invalid_fd, tc)
+{
+ struct aiocb cb;
+ int crv;
+
+ memset(&cb, 0, sizeof(cb));
+ cb.aio_fildes = -1;
+
+ errno = 0;
+ crv = aio_cancel(-1, &cb);
+ ATF_REQUIRE_EQ(-1, crv);
+ ATF_REQUIRE_EQ(EBADF, errno);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, cancel_active_write);
+ ATF_TP_ADD_TC(tp, cancel_completed_request);
+ ATF_TP_ADD_TC(tp, cancel_invalid_fd);
+ return atf_no_error();
+}
diff --git a/lib/libc/sys/t_aio_lio.c b/lib/libc/sys/t_aio_lio.c
new file mode 100644
index 000000000000..991db8d5a7cd
--- /dev/null
+++ b/lib/libc/sys/t_aio_lio.c
@@ -0,0 +1,264 @@
+/* $NetBSD: t_aio_lio.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static int mktemp_file(char *, size_t);
+static void fill_pattern(uint8_t *, size_t, uint8_t);
+static void wait_all(const struct aiocb * const [], size_t);
+
+static int
+mktemp_file(char *path, size_t pathlen)
+{
+ int fd, n;
+
+ n = snprintf(path, pathlen, "t_aio_lio.XXXXXX");
+ ATF_REQUIRE(n > 0 && (size_t)n < pathlen);
+
+ fd = mkstemp(path);
+ ATF_REQUIRE(fd >= 0);
+
+ return fd;
+}
+
+static void
+fill_pattern(uint8_t *buf, size_t len, uint8_t seed)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t)(seed + (i & 0xff));
+ }
+}
+
+static void
+wait_all(const struct aiocb * const list[], size_t nent)
+{
+ size_t i;
+ int pending, rv;
+
+ for (;;) {
+ pending = 0;
+
+ for (i = 0; i < nent; i++) {
+ int err;
+
+ if (list[i] == NULL) {
+ continue;
+ }
+
+ err = aio_error(list[i]);
+ if (err == EINPROGRESS) {
+ pending = 1;
+ }
+ }
+
+ if (!pending) {
+ break;
+ }
+
+ rv = aio_suspend(list, (int)nent, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s",
+ strerror(errno));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(lio_nowait);
+ATF_TC_BODY(lio_nowait, tc)
+{
+ char path[64];
+ int fd, rv;
+#define NW_REQS 8
+#define NW_BLKSIZ 8192
+ uint8_t *bufs[NW_REQS];
+ struct aiocb cbs[NW_REQS];
+ struct aiocb *list[NW_REQS];
+ off_t off;
+ size_t i;
+
+ fd = mktemp_file(path, sizeof(path));
+
+ off = 0;
+ for (i = 0; i < NW_REQS; i++) {
+ bufs[i] = malloc(NW_BLKSIZ);
+ ATF_REQUIRE(bufs[i] != NULL);
+
+ fill_pattern(bufs[i], NW_BLKSIZ, (uint8_t)i);
+
+ memset(&cbs[i], 0, sizeof(cbs[i]));
+ cbs[i].aio_fildes = fd;
+ cbs[i].aio_buf = bufs[i];
+ cbs[i].aio_nbytes = NW_BLKSIZ;
+ cbs[i].aio_offset = off;
+ cbs[i].aio_lio_opcode = LIO_WRITE;
+
+ list[i] = &cbs[i];
+ off += (off_t)NW_BLKSIZ;
+ }
+
+ rv = lio_listio(LIO_NOWAIT, list, (int)NW_REQS, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio failed: %s",
+ strerror(errno));
+
+ wait_all((const struct aiocb * const *)list, NW_REQS);
+
+ for (i = 0; i < NW_REQS; i++) {
+ int err;
+ ssize_t done;
+
+ err = aio_error(&cbs[i]);
+ ATF_REQUIRE_EQ(0, err);
+
+ done = aio_return(&cbs[i]);
+ ATF_REQUIRE_EQ(NW_BLKSIZ, done);
+
+ free(bufs[i]);
+ }
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+}
+
+ATF_TC_WITHOUT_HEAD(lio_wait_write_then_read);
+ATF_TC_BODY(lio_wait_write_then_read, tc)
+{
+ char path[64];
+ int fd, rv;
+#define WWTR_REQS 4
+#define WWTR_BLKSIZ 4096
+
+ uint8_t *wbufs[WWTR_REQS];
+ struct aiocb wcbs[WWTR_REQS];
+ struct aiocb *wlist[WWTR_REQS];
+
+ uint8_t *rbufs[WWTR_REQS];
+ struct aiocb rcbs[WWTR_REQS];
+ struct aiocb *rlist[WWTR_REQS];
+
+ size_t i;
+ off_t off;
+
+ fd = mktemp_file(path, sizeof(path));
+
+ off = 0;
+ for (i = 0; i < WWTR_REQS; i++) {
+ wbufs[i] = malloc(WWTR_BLKSIZ);
+ ATF_REQUIRE(wbufs[i] != NULL);
+
+ fill_pattern(wbufs[i], WWTR_BLKSIZ, (uint8_t)(0xA0 + i));
+
+ memset(&wcbs[i], 0, sizeof(wcbs[i]));
+ wcbs[i].aio_fildes = fd;
+ wcbs[i].aio_buf = wbufs[i];
+ wcbs[i].aio_nbytes = WWTR_BLKSIZ;
+ wcbs[i].aio_offset = off;
+ wcbs[i].aio_lio_opcode = LIO_WRITE;
+
+ wlist[i] = &wcbs[i];
+ off += WWTR_BLKSIZ;
+ }
+
+ rv = lio_listio(LIO_WAIT, wlist, (int)WWTR_REQS, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio write failed: %s",
+ strerror(errno));
+
+ for (i = 0; i < WWTR_REQS; i++) {
+ int err;
+ ssize_t done;
+
+ err = aio_error(&wcbs[i]);
+ ATF_REQUIRE_EQ(0, err);
+
+ done = aio_return(&wcbs[i]);
+ ATF_REQUIRE_EQ(WWTR_BLKSIZ, done);
+ }
+
+ for (i = 0; i < WWTR_REQS; i++) {
+ rbufs[i] = calloc(1, WWTR_BLKSIZ);
+ ATF_REQUIRE(rbufs[i] != NULL);
+
+ memset(&rcbs[i], 0, sizeof(rcbs[i]));
+ rcbs[i].aio_fildes = fd;
+ rcbs[i].aio_buf = rbufs[i];
+ rcbs[i].aio_nbytes = WWTR_BLKSIZ;
+ rcbs[i].aio_offset = (off_t)i * WWTR_BLKSIZ;
+ rcbs[i].aio_lio_opcode = LIO_READ;
+
+ rlist[i] = &rcbs[i];
+ }
+
+ rv = lio_listio(LIO_NOWAIT, rlist, WWTR_REQS, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio read failed: %s",
+ strerror(errno));
+
+ wait_all((const struct aiocb * const *)rlist, WWTR_REQS);
+
+ for (i = 0; i < WWTR_REQS; i++) {
+ int err;
+ ssize_t done;
+
+ err = aio_error(&rcbs[i]);
+ ATF_REQUIRE_EQ(0, err);
+
+ done = aio_return(&rcbs[i]);
+ ATF_REQUIRE_EQ(WWTR_BLKSIZ, done);
+
+ ATF_REQUIRE_EQ(0, memcmp(wbufs[i], rbufs[i], WWTR_BLKSIZ));
+
+ free(wbufs[i]);
+ free(rbufs[i]);
+ }
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, lio_nowait);
+ ATF_TP_ADD_TC(tp, lio_wait_write_then_read);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/sys/t_aio_rw.c b/lib/libc/sys/t_aio_rw.c
new file mode 100644
index 000000000000..e7a5b4fa67d1
--- /dev/null
+++ b/lib/libc/sys/t_aio_rw.c
@@ -0,0 +1,167 @@
+/* $NetBSD: t_aio_rw.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+static int mktemp_file(char *, size_t);
+static void fill_pattern(uint8_t *, size_t, uint8_t);
+static void wait_all(const struct aiocb * const [], size_t);
+
+static int
+mktemp_file(char *path, size_t pathlen)
+{
+ int fd, n;
+
+ n = snprintf(path, pathlen, "t_aio_rw.XXXXXX");
+ ATF_REQUIRE(n > 0 && (size_t)n < pathlen);
+
+ fd = mkstemp(path);
+ ATF_REQUIRE(fd >= 0);
+
+ return fd;
+}
+
+static void
+fill_pattern(uint8_t *buf, size_t len, uint8_t seed)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t)(seed + (i & 0xff));
+ }
+}
+
+static void
+wait_all(const struct aiocb * const list[], size_t nent)
+{
+ size_t i;
+ int pending, rv, error;
+
+ for (;;) {
+ pending = 0;
+
+ for (i = 0; i < nent; i++) {
+ if (list[i] == NULL) {
+ continue;
+ }
+
+ error = aio_error(list[i]);
+ if (error == EINPROGRESS) {
+ pending = 1;
+ }
+ }
+
+ if (!pending) {
+ break;
+ }
+
+ rv = aio_suspend(list, (int)nent, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s",
+ strerror(errno));
+ }
+}
+
+/*
+ * write_then_read_back
+ * Write a block then read it back asynchronously and compare.
+ */
+ATF_TC_WITHOUT_HEAD(write_then_read_back);
+ATF_TC_BODY(write_then_read_back, tc)
+{
+ char path[64];
+ int fd, rv;
+ const size_t blksz = 0x2000;
+ uint8_t *wbuf, *rbuf;
+ struct aiocb wcb, rcb;
+ const struct aiocb *wlist[1], *rlist[1];
+
+ fd = mktemp_file(path, sizeof(path));
+
+ wbuf = malloc(blksz);
+ rbuf = calloc(1, blksz);
+ ATF_REQUIRE(wbuf != NULL && rbuf != NULL);
+
+ fill_pattern(wbuf, blksz, 0xA0);
+
+ memset(&wcb, 0, sizeof(wcb));
+ wcb.aio_fildes = fd;
+ wcb.aio_buf = wbuf;
+ wcb.aio_nbytes = blksz;
+ wcb.aio_offset = 0;
+
+ rv = aio_write(&wcb);
+ ATF_REQUIRE_EQ(0, rv);
+ wlist[0] = &wcb;
+ wait_all(wlist, 1);
+
+ ATF_REQUIRE_EQ(0, aio_error(&wcb));
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&wcb));
+
+ memset(&rcb, 0, sizeof(rcb));
+ rcb.aio_fildes = fd;
+ rcb.aio_buf = rbuf;
+ rcb.aio_nbytes = blksz;
+ rcb.aio_offset = 0;
+
+ rv = aio_read(&rcb);
+ ATF_REQUIRE_EQ(0, rv);
+ rlist[0] = &rcb;
+ wait_all(rlist, 1);
+
+ ATF_REQUIRE_EQ(0, aio_error(&rcb));
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&rcb));
+ ATF_REQUIRE_EQ(0, memcmp(wbuf, rbuf, blksz));
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+
+ free(wbuf);
+ free(rbuf);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, write_then_read_back);
+ return atf_no_error();
+}
diff --git a/lib/libc/sys/t_aio_suspend.c b/lib/libc/sys/t_aio_suspend.c
new file mode 100644
index 000000000000..96b0dc2cf6d0
--- /dev/null
+++ b/lib/libc/sys/t_aio_suspend.c
@@ -0,0 +1,170 @@
+/* $NetBSD: t_aio_suspend.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+static int mktemp_file(char *, size_t);
+static void fill_pattern(uint8_t *, size_t, uint8_t);
+static void wait_cb(struct aiocb *);
+
+static int
+mktemp_file(char *path, size_t pathlen)
+{
+ int fd, n;
+
+ n = snprintf(path, pathlen, "t_aio_suspend.XXXXXX");
+ ATF_REQUIRE(n > 0 && (size_t)n < pathlen);
+
+ fd = mkstemp(path);
+ ATF_REQUIRE(fd >= 0);
+
+ return fd;
+}
+
+static void
+fill_pattern(uint8_t *buf, size_t len, uint8_t seed)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t)(seed + (i & 0xff));
+ }
+}
+
+static void
+wait_cb(struct aiocb *cb)
+{
+ const struct aiocb *one[1];
+ int rv;
+
+ one[0] = cb;
+ while (aio_error(cb) == EINPROGRESS) {
+ rv = aio_suspend(one, 1, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+ }
+ if (aio_error(cb) == 0) {
+ aio_return(cb);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(suspend_any);
+ATF_TC_BODY(suspend_any, tc)
+{
+ char path[64];
+ int fd, rv;
+ const size_t blksz = 4096;
+ uint8_t *buf0, *buf1;
+ struct aiocb cb0, cb1;
+ const struct aiocb *list[2];
+ int done;
+
+ fd = mktemp_file(path, sizeof(path));
+
+ buf0 = malloc(blksz);
+ buf1 = malloc(blksz);
+ ATF_REQUIRE(buf0 != NULL && buf1 != NULL);
+ fill_pattern(buf0, blksz, 0x20);
+ fill_pattern(buf1, blksz, 0x40);
+
+ memset(&cb0, 0, sizeof(cb0));
+ cb0.aio_fildes = fd;
+ cb0.aio_buf = buf0;
+ cb0.aio_nbytes = blksz;
+ cb0.aio_offset = 0;
+
+ memset(&cb1, 0, sizeof(cb1));
+ cb1.aio_fildes = fd;
+ cb1.aio_buf = buf1;
+ cb1.aio_nbytes = blksz;
+ cb1.aio_offset = blksz;
+
+ ATF_REQUIRE_EQ(0, aio_write(&cb0));
+ ATF_REQUIRE_EQ(0, aio_write(&cb1));
+
+ list[0] = &cb0;
+ list[1] = &cb1;
+
+ rv = aio_suspend(list, 2, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ done = 0;
+ if (aio_error(&cb0) != EINPROGRESS) {
+ done++;
+ if (aio_error(&cb0) == 0) {
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb0));
+ } else {
+ ATF_REQUIRE_EQ(ECANCELED, aio_error(&cb0));
+ ATF_REQUIRE_EQ(-1, aio_return(&cb0));
+ }
+ }
+ if (aio_error(&cb1) != EINPROGRESS) {
+ done++;
+ if (aio_error(&cb1) == 0) {
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb1));
+ } else {
+ ATF_REQUIRE_EQ(ECANCELED, aio_error(&cb1));
+ ATF_REQUIRE_EQ(-1, aio_return(&cb1));
+ }
+ }
+ ATF_REQUIRE(done >= 1);
+
+ if (aio_error(&cb0) == EINPROGRESS) {
+ wait_cb(&cb0);
+ }
+ if (aio_error(&cb1) == EINPROGRESS) {
+ wait_cb(&cb1);
+ }
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+
+ free(buf0);
+ free(buf1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, suspend_any);
+ return atf_no_error();
+}
diff --git a/lib/libc/sys/t_ptrace_kill.c b/lib/libc/sys/t_ptrace_kill.c
new file mode 100644
index 000000000000..fdd6298c2a8a
--- /dev/null
+++ b/lib/libc/sys/t_ptrace_kill.c
@@ -0,0 +1,131 @@
+/* $NetBSD: t_ptrace_kill.c,v 1.2 2025/05/02 02:24:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_ptrace_kill.c,v 1.2 2025/05/02 02:24:32 riastradh Exp $");
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+#include <atf-c.h>
+
+#define SYSCALL(a, b) ATF_REQUIRE_EQ_MSG(a, b, "%s got %s", #a, strerror(errno))
+
+ATF_TC(pt_kill);
+ATF_TC_HEAD(pt_kill, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test PT_KILL of a PT_STOP'ed process");
+}
+
+static void
+child(int *fdto, int *fdfrom)
+{
+ char p = '2', q;
+ printf("%d: born\n", getpid());
+ write(fdfrom[1], &p, 1);
+ read(fdto[0], &q, 1);
+ printf("%d: seppuku %c\n", getpid(), q);
+ write(fdfrom[1], &p, 1);
+ read(fdto[0], &q, 1);
+// *(int *)1 = 0;
+// kill(getpid(), SIGSEGV);
+// kill(getpid(), SIGSTOP);
+ for (;;)
+ sleep(1);
+
+}
+
+static void *
+waitthread(void *pidp)
+{
+ int status = 0;
+ pid_t rpid, pid;
+
+ pid = *(pid_t *)pidp;
+ printf("waiting for %d\n", pid);
+ while ((rpid = waitpid(pid, &status, 0)) != pid) {
+ printf("waitpid %d = %d status = %#x", pid, rpid, status);
+ }
+ printf("done waitpid %d = %d status = %#x", pid, rpid, status);
+ return NULL;
+}
+
+ATF_TC_BODY(pt_kill, tc)
+{
+ pid_t pid;
+ int fdto[2], fdfrom[2];
+ char p = '1', q;
+ int status;
+ pthread_t thread;
+
+ SYSCALL(pipe(fdto), 0);
+ SYSCALL(pipe(fdfrom), 0);
+ switch (pid = fork()) {
+ case 0:
+ child(fdto, fdfrom);
+ break;
+ case -1:
+ err(EXIT_FAILURE, "fork failed");
+ default:
+ SYSCALL(pthread_create(&thread, NULL, waitthread, &pid), 0);
+ sleep(1); // XXX: too lazy to sync properly
+ SYSCALL(read(fdfrom[0], &q, 1), 1);
+ printf("%d: read %c\n", pid, q);
+ SYSCALL(ptrace(PT_ATTACH, pid, NULL, 0), 0);
+ printf("%d: attached\n", pid);
+ SYSCALL(write(fdto[1], &p, 1), 1);
+ waitpid(pid, NULL, WNOHANG);
+ printf("%d: sent\n", pid);
+ SYSCALL(ptrace(PT_CONTINUE, pid, (void *)1, 0), 0);
+ SYSCALL(read(fdfrom[0], &p, 1), 1);
+ printf("%d: received\n", pid);
+ SYSCALL(ptrace(PT_STOP, pid, NULL, 0), 0);
+ SYSCALL(ptrace(PT_KILL, pid, NULL, 0), 0);
+ SYSCALL(waitpid(pid, &status, 0), pid);
+ ATF_REQUIRE(status == 9);
+ break;
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, pt_kill);
+
+ return atf_no_error();
+}