diff options
Diffstat (limited to 'lib/libc/tests/stdio')
22 files changed, 4773 insertions, 0 deletions
diff --git a/lib/libc/tests/stdio/Makefile b/lib/libc/tests/stdio/Makefile new file mode 100644 index 000000000000..6dca927c4fb7 --- /dev/null +++ b/lib/libc/tests/stdio/Makefile @@ -0,0 +1,55 @@ +.include <bsd.own.mk> + +ATF_TESTS_C+= eintr_test +ATF_TESTS_C+= fdopen_test +ATF_TESTS_C+= flushlbuf_test +ATF_TESTS_C+= fmemopen2_test +ATF_TESTS_C+= fopen2_test +ATF_TESTS_C+= freopen_test +ATF_TESTS_C+= getdelim_test +ATF_TESTS_C+= gets_s_test +ATF_TESTS_C+= mkostemp_test +ATF_TESTS_C+= open_memstream2_test +ATF_TESTS_C+= open_wmemstream_test +ATF_TESTS_C+= perror_test +ATF_TESTS_C+= print_positional_test +ATF_TESTS_C+= printbasic_test +ATF_TESTS_C+= printfloat_test +ATF_TESTS_C+= scanfloat_test +ATF_TESTS_C+= snprintf_test +ATF_TESTS_C+= sscanf_test +ATF_TESTS_C+= swprintf_test +ATF_TESTS_C+= swscanf_test + +SRCS.fopen2_test= fopen_test.c + +NETBSD_ATF_TESTS_C= clearerr_test +NETBSD_ATF_TESTS_C+= fflush_test +NETBSD_ATF_TESTS_C+= fmemopen_test +NETBSD_ATF_TESTS_C+= fopen_test +NETBSD_ATF_TESTS_C+= fputc_test +NETBSD_ATF_TESTS_C+= mktemp_test +NETBSD_ATF_TESTS_C+= open_memstream_test +NETBSD_ATF_TESTS_C+= popen_test +NETBSD_ATF_TESTS_C+= printf_test +NETBSD_ATF_TESTS_C+= scanf_test + +LIBADD.eintr_test+= md +LIBADD.printfloat_test+= m +LIBADD.scanfloat_test+= m + +# Older toolchains won't understand C23 %b, %wN, %wfN +PROG_OVERRIDE_VARS+= NO_WFORMAT +NO_WFORMAT.snprintf_test= +NO_WFORMAT.sscanf_test= +NO_WFORMAT.swprintf_test= +NO_WFORMAT.swscanf_test= + +.if ${COMPILER_TYPE} == "gcc" +# 90: use of assignment suppression and length modifier together in scanf format +NO_WFORMAT.scanfloat_test= +.endif + +.include "../Makefile.netbsd-tests" + +.include <bsd.test.mk> diff --git a/lib/libc/tests/stdio/Makefile.depend b/lib/libc/tests/stdio/Makefile.depend new file mode 100644 index 000000000000..e5ddbc552be2 --- /dev/null +++ b/lib/libc/tests/stdio/Makefile.depend @@ -0,0 +1,19 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/atf/libatf-c \ + lib/libc \ + lib/libcompiler_rt \ + lib/libnetbsd \ + lib/msun \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/libc/tests/stdio/eintr_test.c b/lib/libc/tests/stdio/eintr_test.c new file mode 100644 index 000000000000..55354c8926c5 --- /dev/null +++ b/lib/libc/tests/stdio/eintr_test.c @@ -0,0 +1,143 @@ +/* + * Initially written by Yar Tikhiy <yar@freebsd.org> in PR 76398. + * Bug fixes and instrumentation by kib@freebsd.org. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <md5.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#define NDATA 1000 +#define DELAY 2 + +static void +hup(int signo __unused) +{ +} + +static int ndata, seq; + +static void +setdata(int n) +{ + ndata = n; + seq = 0; +} + +static char * +getdata(void) +{ + static char databuf[256]; + static char xeof[] = "#"; + + if (seq > ndata) + return (NULL); + if (seq == ndata) { + seq++; + return (xeof); + } + sprintf(databuf, "%08d xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", seq++); + return (databuf); +} + +ATF_TC_WITHOUT_HEAD(eintr_test); +ATF_TC_BODY(eintr_test, tc) +{ + char c, digest0[33], digest[33], *p; + FILE *fp; + int i, s[2], total0, total; + MD5_CTX md5; + pid_t child; + struct sigaction sa; + + MD5Init(&md5); + setdata(NDATA); + for (total0 = 0; (p = getdata()) != NULL; total0 += strlen(p)) + MD5Update(&md5, p, strlen(p)); + p = MD5End(&md5, digest0); + + sa.sa_handler = hup; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + ATF_REQUIRE(sigaction(SIGHUP, &sa, NULL) == 0); + + ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) == 0); + + switch (child = fork()) { + case -1: + atf_tc_fail("fork failed %s", strerror(errno)); + break; + + case 0: + ATF_REQUIRE((fp = fdopen(s[0], "w")) != NULL); + close(s[1]); + setdata(NDATA); + while ((p = getdata())) { + for (; *p;) { + if (fputc(*p, fp) == EOF) { + if (errno == EINTR) { + clearerr(fp); + } else { + atf_tc_fail("fputc errno %s", + strerror(errno)); + } + } else { + p++; + } + } + } + fclose(fp); + break; + + default: + close(s[0]); + ATF_REQUIRE((fp = fdopen(s[1], "r")) != NULL); + sleep(DELAY); + ATF_REQUIRE(kill(child, SIGHUP) != -1); + sleep(DELAY); + MD5Init(&md5); + for (total = 0;;) { + i = fgetc(fp); + if (i == EOF) { + if (errno == EINTR) { + clearerr(fp); + } else { + atf_tc_fail("fgetc errno %s", + strerror(errno)); + } + continue; + } + total++; + c = i; + MD5Update(&md5, &c, 1); + if (i == '#') + break; + } + MD5End(&md5, digest); + fclose(fp); + ATF_REQUIRE_MSG(total == total0, + "Total number of bytes read does not match: %d %d", + total, total0); + ATF_REQUIRE_MSG(strcmp(digest, digest0) == 0, + "Digests do not match %s %s", digest, digest0); + break; + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, eintr_test); + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/fdopen_test.c b/lib/libc/tests/stdio/fdopen_test.c new file mode 100644 index 000000000000..5abaed7375b6 --- /dev/null +++ b/lib/libc/tests/stdio/fdopen_test.c @@ -0,0 +1,222 @@ +/*- + * Copyright (c) 2014 Jilles Tjoelker + * 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. + */ + +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +static void +runtest(const char *fname, int intmode, const char *strmode, bool success) +{ + FILE *fp; + int fd; + + fd = open(fname, intmode); + ATF_REQUIRE_MSG(fd != -1, + "open(\"%s\", %#x) failed; errno=%d", fname, intmode, errno); + + fp = fdopen(fd, strmode); + if (fp == NULL) { + close(fd); + ATF_REQUIRE_MSG(success == false, + "fdopen(open(\"%s\", %#x), \"%s\") succeeded unexpectedly", + fname, intmode, strmode); + return; + } + ATF_REQUIRE_MSG(success == true, + "fdopen(open(\"%s\", %#x), \"%s\") failed; errno=%d", + fname, intmode, strmode, errno); + fclose(fp); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDONLY__r_test); +ATF_TC_BODY(null__O_RDONLY__r_test, tc) +{ + + runtest(_PATH_DEVNULL, O_RDONLY, "r", true); +} + +ATF_TC_WITHOUT_HEAD(null__O_WRONLY__r_test); +ATF_TC_BODY(null__O_WRONLY__r_test, tc) +{ + + runtest(_PATH_DEVNULL, O_WRONLY, "r", false); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDWR__r_test); +ATF_TC_BODY(null__O_RDWR__r_test, tc) +{ + + runtest(_PATH_DEVNULL, O_RDWR, "r", true); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDONLY__w_test); +ATF_TC_BODY(null__O_RDONLY__w_test, tc) +{ + + runtest(_PATH_DEVNULL, O_RDONLY, "w", false); +} + +ATF_TC_WITHOUT_HEAD(null__O_WRONLY__w_test); +ATF_TC_BODY(null__O_WRONLY__w_test, tc) +{ + + runtest(_PATH_DEVNULL, O_WRONLY, "w", true); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDWR__w_test); +ATF_TC_BODY(null__O_RDWR__w_test, tc) +{ + + runtest(_PATH_DEVNULL, O_RDWR, "w", true); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDONLY__a_test); +ATF_TC_BODY(null__O_RDONLY__a_test, tc) +{ + + runtest(_PATH_DEVNULL, O_RDONLY, "a", false); +} + +ATF_TC_WITHOUT_HEAD(null__O_WRONLY__a_test); +ATF_TC_BODY(null__O_WRONLY__a_test, tc) +{ + + runtest(_PATH_DEVNULL, O_WRONLY, "a", true); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDWR__test); +ATF_TC_BODY(null__O_RDWR__test, tc) +{ + + runtest(_PATH_DEVNULL, O_RDWR, "a", true); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDONLY__r_append); +ATF_TC_BODY(null__O_RDONLY__r_append, tc) +{ + + runtest(_PATH_DEVNULL, O_RDONLY, "r+", false); +} + +ATF_TC_WITHOUT_HEAD(null__O_WRONLY__r_append); +ATF_TC_BODY(null__O_WRONLY__r_append, tc) +{ + + runtest(_PATH_DEVNULL, O_WRONLY, "r+", false); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDWR__r_append); +ATF_TC_BODY(null__O_RDWR__r_append, tc) +{ + + runtest(_PATH_DEVNULL, O_RDWR, "r+", true); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDONLY__w_append); +ATF_TC_BODY(null__O_RDONLY__w_append, tc) +{ + + runtest(_PATH_DEVNULL, O_RDONLY, "w+", false); +} + +ATF_TC_WITHOUT_HEAD(null__O_WRONLY__w_append); +ATF_TC_BODY(null__O_WRONLY__w_append, tc) +{ + + runtest(_PATH_DEVNULL, O_WRONLY, "w+", false); +} + +ATF_TC_WITHOUT_HEAD(null__O_RDWR__w_append); +ATF_TC_BODY(null__O_RDWR__w_append, tc) +{ + + runtest(_PATH_DEVNULL, O_RDWR, "w+", true); +} + +ATF_TC_WITHOUT_HEAD(sh__O_EXEC__r); +ATF_TC_BODY(sh__O_EXEC__r, tc) +{ + + runtest("/bin/sh", O_EXEC, "r", false); +} + +ATF_TC_WITHOUT_HEAD(sh__O_EXEC__w); +ATF_TC_BODY(sh__O_EXEC__w, tc) +{ + + runtest("/bin/sh", O_EXEC, "w", false); +} + +ATF_TC_WITHOUT_HEAD(sh__O_EXEC__r_append); +ATF_TC_BODY(sh__O_EXEC__r_append, tc) +{ + + runtest("/bin/sh", O_EXEC, "r+", false); +} + +ATF_TC_WITHOUT_HEAD(sh__O_EXEC__w_append); +ATF_TC_BODY(sh__O_EXEC__w_append, tc) +{ + + runtest("/bin/sh", O_EXEC, "w+", false); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, null__O_RDONLY__r_test); + ATF_TP_ADD_TC(tp, null__O_WRONLY__r_test); + ATF_TP_ADD_TC(tp, null__O_RDWR__r_test); + ATF_TP_ADD_TC(tp, null__O_RDONLY__w_test); + ATF_TP_ADD_TC(tp, null__O_WRONLY__w_test); + ATF_TP_ADD_TC(tp, null__O_RDWR__w_test); + ATF_TP_ADD_TC(tp, null__O_RDONLY__a_test); + ATF_TP_ADD_TC(tp, null__O_WRONLY__a_test); + ATF_TP_ADD_TC(tp, null__O_RDWR__test); + ATF_TP_ADD_TC(tp, null__O_RDONLY__r_append); + ATF_TP_ADD_TC(tp, null__O_WRONLY__r_append); + ATF_TP_ADD_TC(tp, null__O_RDWR__r_append); + ATF_TP_ADD_TC(tp, null__O_RDONLY__w_append); + ATF_TP_ADD_TC(tp, null__O_WRONLY__w_append); + ATF_TP_ADD_TC(tp, null__O_RDWR__w_append); + ATF_TP_ADD_TC(tp, sh__O_EXEC__r); + ATF_TP_ADD_TC(tp, sh__O_EXEC__w); + ATF_TP_ADD_TC(tp, sh__O_EXEC__r_append); + ATF_TP_ADD_TC(tp, sh__O_EXEC__w_append); + + return (atf_no_error()); +} + +/* + vim:ts=8:cin:sw=8 + */ diff --git a/lib/libc/tests/stdio/flushlbuf_test.c b/lib/libc/tests/stdio/flushlbuf_test.c new file mode 100644 index 000000000000..3f8a6378f933 --- /dev/null +++ b/lib/libc/tests/stdio/flushlbuf_test.c @@ -0,0 +1,155 @@ +/*- + * Copyright (c) 2023 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <errno.h> +#include <stdio.h> + +#include <atf-c.h> + +#define BUFSIZE 16 + +static const char seq[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +struct stream { + char buf[BUFSIZE]; + unsigned int len; + unsigned int pos; +}; + +static int +writefn(void *cookie, const char *buf, int len) +{ + struct stream *s = cookie; + int written = 0; + + if (len <= 0) + return (0); + while (len > 0 && s->pos < s->len) { + s->buf[s->pos++] = *buf++; + written++; + len--; + } + if (written > 0) + return (written); + errno = EAGAIN; + return (-1); +} + +ATF_TC_WITHOUT_HEAD(flushlbuf_partial); +ATF_TC_BODY(flushlbuf_partial, tc) +{ + static struct stream s; + static char buf[BUFSIZE + 1]; + FILE *f; + unsigned int i = 0; + int ret = 0; + + /* + * Create the stream and its buffer, print just enough characters + * to the stream to fill the buffer without triggering a flush, + * then check the state. + */ + s.len = BUFSIZE / 2; // write will fail after this amount + ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL); + ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0); + while (i < BUFSIZE) + if ((ret = fprintf(f, "%c", seq[i++])) < 0) + break; + ATF_CHECK_EQ(BUFSIZE, i); + ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]); + ATF_CHECK_EQ(1, ret); + ATF_CHECK_EQ(0, s.pos); + + /* + * At this point, the buffer is full but writefn() has not yet + * been called. The next fprintf() call will trigger a preemptive + * fflush(), and writefn() will consume s.len characters before + * returning EAGAIN, causing fprintf() to fail without having + * written anything (which is why we don't increment i here). + */ + ret = fprintf(f, "%c", seq[i]); + ATF_CHECK_ERRNO(EAGAIN, ret < 0); + ATF_CHECK_EQ(s.len, s.pos); + + /* + * We have consumed s.len characters from the buffer, so continue + * printing until it is full again and check that no overflow has + * occurred yet. + */ + while (i < BUFSIZE + s.len) + fprintf(f, "%c", seq[i++]); + ATF_CHECK_EQ(BUFSIZE + s.len, i); + ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]); + ATF_CHECK_EQ(0, buf[BUFSIZE]); + + /* + * The straw that breaks the camel's back: libc fails to recognize + * that the buffer is full and continues to write beyond its end. + */ + fprintf(f, "%c", seq[i++]); + ATF_CHECK_EQ(0, buf[BUFSIZE]); +} + +ATF_TC_WITHOUT_HEAD(flushlbuf_full); +ATF_TC_BODY(flushlbuf_full, tc) +{ + static struct stream s; + static char buf[BUFSIZE]; + FILE *f; + unsigned int i = 0; + int ret = 0; + + /* + * Create the stream and its buffer, print just enough characters + * to the stream to fill the buffer without triggering a flush, + * then check the state. + */ + s.len = 0; // any attempt to write will fail + ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL); + ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0); + while (i < BUFSIZE) + if ((ret = fprintf(f, "%c", seq[i++])) < 0) + break; + ATF_CHECK_EQ(BUFSIZE, i); + ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]); + ATF_CHECK_EQ(1, ret); + ATF_CHECK_EQ(0, s.pos); + + /* + * At this point, the buffer is full but writefn() has not yet + * been called. The next fprintf() call will trigger a preemptive + * fflush(), and writefn() will immediately return EAGAIN, causing + * fprintf() to fail without having written anything (which is why + * we don't increment i here). + */ + ret = fprintf(f, "%c", seq[i]); + ATF_CHECK_ERRNO(EAGAIN, ret < 0); + ATF_CHECK_EQ(s.len, s.pos); + + /* + * Now make our stream writeable. + */ + s.len = sizeof(s.buf); + + /* + * Flush the stream again. The data we failed to write previously + * should still be in the buffer and will now be written to the + * stream. + */ + ATF_CHECK_EQ(0, fflush(f)); + ATF_CHECK_EQ(seq[0], s.buf[0]); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, flushlbuf_partial); + ATF_TP_ADD_TC(tp, flushlbuf_full); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/fmemopen2_test.c b/lib/libc/tests/stdio/fmemopen2_test.c new file mode 100644 index 000000000000..a558ff3515e9 --- /dev/null +++ b/lib/libc/tests/stdio/fmemopen2_test.c @@ -0,0 +1,318 @@ +/*- +Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> + +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 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 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 basic FILE * functions (fread, fwrite, fseek, fclose) against + * a FILE * retrieved using fmemopen() + */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> + +#include <atf-c.h> + +ATF_TC_WITHOUT_HEAD(test_preexisting); +ATF_TC_BODY(test_preexisting, tc) +{ + /* Use a pre-existing buffer. */ + char buf[512]; + char buf2[512]; + char str[] = "Test writing some stuff"; + char str2[] = "AAAAAAAAA"; + char str3[] = "AAAA writing some stuff"; + FILE *fp; + size_t nofw, nofr; + int rc; + + /* Open a FILE * using fmemopen. */ + fp = fmemopen(buf, sizeof(buf), "w"); + ATF_REQUIRE(fp != NULL); + + /* Write to the buffer. */ + nofw = fwrite(str, 1, sizeof(str), fp); + ATF_REQUIRE(nofw == sizeof(str)); + + /* Close the FILE *. */ + rc = fclose(fp); + ATF_REQUIRE(rc == 0); + + /* Re-open the FILE * to read back the data. */ + fp = fmemopen(buf, sizeof(buf), "r"); + ATF_REQUIRE(fp != NULL); + + /* Read from the buffer. */ + bzero(buf2, sizeof(buf2)); + nofr = fread(buf2, 1, sizeof(buf2), fp); + ATF_REQUIRE(nofr == sizeof(buf2)); + + /* + * Since a write on a FILE * retrieved by fmemopen + * will add a '\0' (if there's space), we can check + * the strings for equality. + */ + ATF_REQUIRE(strcmp(str, buf2) == 0); + + /* Close the FILE *. */ + rc = fclose(fp); + ATF_REQUIRE(rc == 0); + + /* Now open a FILE * on the first 4 bytes of the string. */ + fp = fmemopen(str, 4, "w"); + ATF_REQUIRE(fp != NULL); + + /* + * Try to write more bytes than we shoud, we'll get a short count (4). + */ + nofw = fwrite(str2, 1, sizeof(str2), fp); + ATF_REQUIRE(nofw == 4); + + /* Close the FILE *. */ + rc = fclose(fp); + ATF_REQUIRE(rc == 0); + + /* Check that the string was not modified after the first 4 bytes. */ + ATF_REQUIRE(strcmp(str, str3) == 0); +} + +ATF_TC_WITHOUT_HEAD(test_autoalloc); +ATF_TC_BODY(test_autoalloc, tc) +{ + /* Let fmemopen allocate the buffer. */ + FILE *fp; + long pos; + size_t nofw, i; + int rc; + + /* Open a FILE * using fmemopen. */ + fp = fmemopen(NULL, 512, "w+"); + ATF_REQUIRE(fp != NULL); + + /* fill the buffer */ + for (i = 0; i < 512; i++) { + nofw = fwrite("a", 1, 1, fp); + ATF_REQUIRE(nofw == 1); + } + + /* Get the current position into the stream. */ + pos = ftell(fp); + ATF_REQUIRE(pos == 512); + + /* Try to write past the end, we should get a short object count (0) */ + nofw = fwrite("a", 1, 1, fp); + ATF_REQUIRE(nofw == 0); + + /* Close the FILE *. */ + rc = fclose(fp); + ATF_REQUIRE(rc == 0); + + /* Open a FILE * using a wrong mode */ + fp = fmemopen(NULL, 512, "r"); + ATF_REQUIRE(fp == NULL); + ATF_REQUIRE(errno == EINVAL); + + fp = fmemopen(NULL, 512, "w"); + ATF_REQUIRE(fp == NULL); + ATF_REQUIRE(errno == EINVAL); +} + +ATF_TC_WITHOUT_HEAD(test_data_length); +ATF_TC_BODY(test_data_length, tc) +{ + /* + * Here we test that a read operation doesn't go past the end of the + * data actually written, and that a SEEK_END seeks from the end of the + * data, not of the whole buffer. + */ + FILE *fp; + char buf[512] = {'\0'}; + char str[] = "Test data length. "; + char str2[] = "Do we have two sentences?"; + char str3[sizeof(str) + sizeof(str2) -1]; + long pos; + size_t nofw, nofr; + int rc; + + /* Open a FILE * for updating our buffer. */ + fp = fmemopen(buf, sizeof(buf), "w+"); + ATF_REQUIRE(fp != NULL); + + /* Write our string into the buffer. */ + nofw = fwrite(str, 1, sizeof(str), fp); + ATF_REQUIRE(nofw == sizeof(str)); + + /* Now seek to the end and check that ftell gives us sizeof(str). */ + rc = fseek(fp, 0, SEEK_END); + ATF_REQUIRE(rc == 0); + pos = ftell(fp); + ATF_REQUIRE(pos == sizeof(str)); + + /* Close the FILE *. */ + rc = fclose(fp); + ATF_REQUIRE(rc == 0); + + /* Reopen the buffer for appending. */ + fp = fmemopen(buf, sizeof(buf), "a+"); + ATF_REQUIRE(fp != NULL); + + /* We should now be writing after the first string. */ + nofw = fwrite(str2, 1, sizeof(str2), fp); + ATF_REQUIRE(nofw == sizeof(str2)); + + /* Rewind the FILE *. */ + rc = fseek(fp, 0, SEEK_SET); + ATF_REQUIRE(rc == 0); + + /* Make sure we're at the beginning. */ + pos = ftell(fp); + ATF_REQUIRE(pos == 0); + + /* Read the whole buffer. */ + nofr = fread(str3, 1, sizeof(buf), fp); + ATF_REQUIRE(nofr == sizeof(str3)); + + /* Make sure the two strings are there. */ + ATF_REQUIRE(strncmp(str3, str, sizeof(str) - 1) == 0); + ATF_REQUIRE(strncmp(str3 + sizeof(str) - 1, str2, sizeof(str2)) == 0); + + /* Close the FILE *. */ + rc = fclose(fp); + ATF_REQUIRE(rc == 0); +} + +ATF_TC_WITHOUT_HEAD(test_binary); +ATF_TC_BODY(test_binary, tc) +{ + /* + * Make sure that NULL bytes are never appended when opening a buffer + * in binary mode. + */ + + FILE *fp; + char buf[20]; + char str[] = "Test"; + size_t nofw; + int rc, i; + + /* Pre-fill the buffer. */ + memset(buf, 'A', sizeof(buf)); + + /* Open a FILE * in binary mode. */ + fp = fmemopen(buf, sizeof(buf), "w+b"); + ATF_REQUIRE(fp != NULL); + + /* Write some data into it. */ + nofw = fwrite(str, 1, strlen(str), fp); + ATF_REQUIRE(nofw == strlen(str)); + + /* Make sure that the buffer doesn't contain any NULL bytes. */ + for (i = 0; i < sizeof(buf); i++) + ATF_REQUIRE(buf[i] != '\0'); + + /* Close the FILE *. */ + rc = fclose(fp); + ATF_REQUIRE(rc == 0); +} + +ATF_TC_WITHOUT_HEAD(test_append_binary_pos); +ATF_TC_BODY(test_append_binary_pos, tc) +{ + /* + * For compatibility with other implementations (glibc), we set the + * position to 0 when opening an automatically allocated binary stream + * for appending. + */ + + FILE *fp; + + fp = fmemopen(NULL, 16, "ab+"); + ATF_REQUIRE(fp != NULL); + ATF_REQUIRE(ftell(fp) == 0L); + fclose(fp); + + /* Make sure that a pre-allocated buffer behaves correctly. */ + char buf[] = "Hello"; + fp = fmemopen(buf, sizeof(buf), "ab+"); + ATF_REQUIRE(fp != NULL); + ATF_REQUIRE(ftell(fp) == strlen(buf)); + fclose(fp); +} + +ATF_TC_WITHOUT_HEAD(test_size_0); +ATF_TC_BODY(test_size_0, tc) +{ + /* POSIX mandates that we return EINVAL if size is 0. */ + + FILE *fp; + + fp = fmemopen(NULL, 0, "r+"); + ATF_REQUIRE(fp == NULL); + ATF_REQUIRE(errno == EINVAL); +} + +/* + * PR281953 - ensure we cannot write in read-only only mode, and cannot read in + * write-only mode. + */ +ATF_TC_WITHOUT_HEAD(test_rdonly_wronly); +ATF_TC_BODY(test_rdonly_wronly, tc) +{ + FILE *fp; + char buf[16]; + char buf_orig[16] = "input data"; + char buf_write[16] = "write"; + size_t sz; + + memcpy(buf, buf_orig, sizeof(buf)); + fp = fmemopen(buf, sizeof(buf), "r"); + ATF_REQUIRE(fp != NULL); + sz = fwrite(buf_write, 1, strlen(buf_write), fp); + ATF_REQUIRE(sz == 0); + ATF_REQUIRE(errno == EBADF); + ATF_REQUIRE(memcmp(buf, buf_orig, sizeof(buf)) == 0); + fclose(fp); + + fp = fmemopen(buf_orig, sizeof(buf), "w"); + ATF_REQUIRE(fp != NULL); + sz = fread(buf, sizeof(buf), 1, fp); + ATF_REQUIRE(sz == 0); + ATF_REQUIRE(errno == EBADF); + fclose(fp); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, test_autoalloc); + ATF_TP_ADD_TC(tp, test_preexisting); + ATF_TP_ADD_TC(tp, test_data_length); + ATF_TP_ADD_TC(tp, test_binary); + ATF_TP_ADD_TC(tp, test_append_binary_pos); + ATF_TP_ADD_TC(tp, test_size_0); + ATF_TP_ADD_TC(tp, test_rdonly_wronly); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/fopen_test.c b/lib/libc/tests/stdio/fopen_test.c new file mode 100644 index 000000000000..4dda5cbf0e5e --- /dev/null +++ b/lib/libc/tests/stdio/fopen_test.c @@ -0,0 +1,202 @@ +/*- + * Copyright (c) 2013 Jilles Tjoelker + * 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. + */ + +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <string.h> + +#include <atf-c.h> + +/* + * O_ACCMODE is currently defined incorrectly. This is what it should be. + * Various code depends on the incorrect value. + */ +#define CORRECT_O_ACCMODE (O_ACCMODE | O_EXEC) + +static void +runtest(const char *fname, const char *mode) +{ + FILE *fp; + int exp_fget_ret, fget_ret, fd, flags, wantedflags; + + fp = fopen(fname, mode); + ATF_REQUIRE_MSG(fp != NULL, + "fopen(\"%s\", \"%s\") failed", fname, mode); + fd = fileno(fp); + ATF_REQUIRE_MSG(fd >= 0, "fileno() failed for fopen"); + exp_fget_ret = strchr(mode, 'e') != NULL ? FD_CLOEXEC : 0; + ATF_REQUIRE_MSG((fget_ret = fcntl(fd, F_GETFD)) == exp_fget_ret, + "fcntl(.., F_GETFD) didn't FD_CLOEXEC as expected %d != %d", + exp_fget_ret, fget_ret); + flags = fcntl(fd, F_GETFL); + if (strchr(mode, '+')) + wantedflags = O_RDWR | (*mode == 'a' ? O_APPEND : 0); + else if (*mode == 'r') + wantedflags = O_RDONLY; + else if (*mode == 'w') + wantedflags = O_WRONLY; + else if (*mode == 'a') + wantedflags = O_WRONLY | O_APPEND; + else + wantedflags = -1; + fclose(fp); + if (wantedflags == -1) + atf_tc_fail("unrecognized mode: %s", mode); + else if ((flags & (CORRECT_O_ACCMODE | O_APPEND)) != wantedflags) + atf_tc_fail("incorrect access mode: %s", mode); +} + +ATF_TC_WITHOUT_HEAD(fopen_r_test); +ATF_TC_BODY(fopen_r_test, tc) +{ + + runtest(_PATH_DEVNULL, "r"); +} + +ATF_TC_WITHOUT_HEAD(fopen_r_append_test); +ATF_TC_BODY(fopen_r_append_test, tc) +{ + + runtest(_PATH_DEVNULL, "r+"); +} + +ATF_TC_WITHOUT_HEAD(fopen_w_test); +ATF_TC_BODY(fopen_w_test, tc) +{ + + runtest(_PATH_DEVNULL, "w"); +} + +ATF_TC_WITHOUT_HEAD(fopen_w_append_test); +ATF_TC_BODY(fopen_w_append_test, tc) +{ + + runtest(_PATH_DEVNULL, "w+"); +} + +ATF_TC_WITHOUT_HEAD(fopen_a_test); +ATF_TC_BODY(fopen_a_test, tc) +{ + + runtest(_PATH_DEVNULL, "a"); +} + +ATF_TC_WITHOUT_HEAD(fopen_a_append_test); +ATF_TC_BODY(fopen_a_append_test, tc) +{ + + runtest(_PATH_DEVNULL, "a+"); +} + +ATF_TC_WITHOUT_HEAD(fopen_re_test); +ATF_TC_BODY(fopen_re_test, tc) +{ + + runtest(_PATH_DEVNULL, "re"); +} + +ATF_TC_WITHOUT_HEAD(fopen_r_append_e_test); +ATF_TC_BODY(fopen_r_append_e_test, tc) +{ + + runtest(_PATH_DEVNULL, "r+e"); +} + +ATF_TC_WITHOUT_HEAD(fopen_we_test); +ATF_TC_BODY(fopen_we_test, tc) +{ + + runtest(_PATH_DEVNULL, "we"); +} + +ATF_TC_WITHOUT_HEAD(fopen_w_append_e_test); +ATF_TC_BODY(fopen_w_append_e_test, tc) +{ + + runtest(_PATH_DEVNULL, "w+e"); +} + +ATF_TC_WITHOUT_HEAD(fopen_ae_test); +ATF_TC_BODY(fopen_ae_test, tc) +{ + + runtest(_PATH_DEVNULL, "ae"); +} + +ATF_TC_WITHOUT_HEAD(fopen_a_append_e_test); +ATF_TC_BODY(fopen_a_append_e_test, tc) +{ + + runtest(_PATH_DEVNULL, "a+e"); +} + +ATF_TC_WITHOUT_HEAD(fopen_re_append_test); +ATF_TC_BODY(fopen_re_append_test, tc) +{ + + runtest(_PATH_DEVNULL, "re+"); +} + +ATF_TC_WITHOUT_HEAD(fopen_we_append_test); +ATF_TC_BODY(fopen_we_append_test, tc) +{ + + runtest(_PATH_DEVNULL, "we+"); +} + +ATF_TC_WITHOUT_HEAD(fopen_ae_append_test); +ATF_TC_BODY(fopen_ae_append_test, tc) +{ + + runtest(_PATH_DEVNULL, "ae+"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, fopen_r_test); + ATF_TP_ADD_TC(tp, fopen_r_append_test); + ATF_TP_ADD_TC(tp, fopen_w_test); + ATF_TP_ADD_TC(tp, fopen_w_append_test); + ATF_TP_ADD_TC(tp, fopen_a_test); + ATF_TP_ADD_TC(tp, fopen_a_append_test); + ATF_TP_ADD_TC(tp, fopen_re_test); + ATF_TP_ADD_TC(tp, fopen_r_append_e_test); + ATF_TP_ADD_TC(tp, fopen_we_test); + ATF_TP_ADD_TC(tp, fopen_w_append_e_test); + ATF_TP_ADD_TC(tp, fopen_ae_test); + ATF_TP_ADD_TC(tp, fopen_a_append_e_test); + ATF_TP_ADD_TC(tp, fopen_re_append_test); + ATF_TP_ADD_TC(tp, fopen_we_append_test); + ATF_TP_ADD_TC(tp, fopen_ae_append_test); + + return (atf_no_error()); +} + +/* + vim:ts=8:cin:sw=8 + */ diff --git a/lib/libc/tests/stdio/freopen_test.c b/lib/libc/tests/stdio/freopen_test.c new file mode 100644 index 000000000000..55fe3c580dd3 --- /dev/null +++ b/lib/libc/tests/stdio/freopen_test.c @@ -0,0 +1,220 @@ +/*- + * Copyright (c) 2014 Jilles Tjoelker + * 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. + */ + +#include <errno.h> +#include <paths.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <atf-c.h> + +static void +runtest(const char *fname1, const char *mode1, const char *fname2, + const char *mode2, bool success) +{ + FILE *fp1, *fp2; + const char *fname2_print; + + fname2_print = fname2 != NULL ? fname2 : "<NULL>"; + fp1 = fopen(fname1, mode1); + ATF_REQUIRE_MSG(fp1 != NULL, + "fopen(\"%s\", \"%s\") failed; errno=%d", fname1, mode1, errno); + fp2 = freopen(fname2, mode2, fp1); + if (fp2 == NULL) { + ATF_REQUIRE_MSG(success == false, + "freopen(\"%s\", \"%s\", fopen(\"%s\", \"%s\")) succeeded " + "unexpectedly", fname2_print, mode2, fname1, mode1); + return; + } + ATF_REQUIRE_MSG(success == true, + "freopen(\"%s\", \"%s\", fopen(\"%s\", \"%s\")) failed: %d", + fname2_print, mode2, fname1, mode1, errno); + fclose(fp2); +} + +ATF_TC_WITHOUT_HEAD(null__r__r__test); +ATF_TC_BODY(null__r__r__test, tc) +{ + + runtest(_PATH_DEVNULL, "r", NULL, "r", true); +} + +ATF_TC_WITHOUT_HEAD(null__w__r__test); +ATF_TC_BODY(null__w__r__test, tc) +{ + + runtest(_PATH_DEVNULL, "w", NULL, "r", false); +} + +ATF_TC_WITHOUT_HEAD(null__r_append__r__test); +ATF_TC_BODY(null__r_append__r__test, tc) +{ + + runtest(_PATH_DEVNULL, "r+", NULL, "r", true); +} + +ATF_TC_WITHOUT_HEAD(null__r__w__test); +ATF_TC_BODY(null__r__w__test, tc) +{ + + runtest(_PATH_DEVNULL, "r", NULL, "w", false); +} + +ATF_TC_WITHOUT_HEAD(null__w__w__test); +ATF_TC_BODY(null__w__w__test, tc) +{ + + runtest(_PATH_DEVNULL, "w", NULL, "w", true); +} + +ATF_TC_WITHOUT_HEAD(null__r_append__w__test); +ATF_TC_BODY(null__r_append__w__test, tc) +{ + + runtest(_PATH_DEVNULL, "r+", NULL, "w", true); +} + +ATF_TC_WITHOUT_HEAD(null__r__a__test); +ATF_TC_BODY(null__r__a__test, tc) +{ + + runtest(_PATH_DEVNULL, "r", NULL, "a", false); +} + +ATF_TC_WITHOUT_HEAD(null__w__a__test); +ATF_TC_BODY(null__w__a__test, tc) +{ + + runtest(_PATH_DEVNULL, "w", NULL, "a", true); +} + +ATF_TC_WITHOUT_HEAD(null__r_append__a__test); +ATF_TC_BODY(null__r_append__a__test, tc) +{ + + runtest(_PATH_DEVNULL, "r+", NULL, "a", true); +} + +ATF_TC_WITHOUT_HEAD(null__r__r_append__test); +ATF_TC_BODY(null__r__r_append__test, tc) +{ + + runtest(_PATH_DEVNULL, "r", NULL, "r+", false); +} + +ATF_TC_WITHOUT_HEAD(null__w__r_append__test); +ATF_TC_BODY(null__w__r_append__test, tc) +{ + + runtest(_PATH_DEVNULL, "w", NULL, "r+", false); +} + +ATF_TC_WITHOUT_HEAD(null__r_append__r_append__test); +ATF_TC_BODY(null__r_append__r_append__test, tc) +{ + + runtest(_PATH_DEVNULL, "r+", NULL, "r+", true); +} + +ATF_TC_WITHOUT_HEAD(null__r__w_append__test); +ATF_TC_BODY(null__r__w_append__test, tc) +{ + + runtest(_PATH_DEVNULL, "r", NULL, "w+", false); +} + +ATF_TC_WITHOUT_HEAD(null__w__w_append__test); +ATF_TC_BODY(null__w__w_append__test, tc) +{ + + runtest(_PATH_DEVNULL, "w", NULL, "w+", false); +} + +ATF_TC_WITHOUT_HEAD(null__r_append__w_append__test); +ATF_TC_BODY(null__r_append__w_append__test, tc) +{ + + runtest(_PATH_DEVNULL, "r+", NULL, "w+", true); +} + +ATF_TC_WITHOUT_HEAD(sh__r__r__test); +ATF_TC_BODY(sh__r__r__test, tc) +{ + + runtest("/bin/sh", "r", NULL, "r", true); +} + +ATF_TC_WITHOUT_HEAD(sh__sh__r__r__test); +ATF_TC_BODY(sh__sh__r__r__test, tc) +{ + + runtest("/bin/sh", "r", "/bin/sh", "r", true); +} + +ATF_TC_WITHOUT_HEAD(sh__null__r__r__test); +ATF_TC_BODY(sh__null__r__r__test, tc) +{ + + runtest("/bin/sh", "r", _PATH_DEVNULL, "r", true); +} + +ATF_TC_WITHOUT_HEAD(sh__null__r__w__test); +ATF_TC_BODY(sh__null__r__w__test, tc) +{ + + runtest("/bin/sh", "r", _PATH_DEVNULL, "w", true); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, null__r__r__test); + ATF_TP_ADD_TC(tp, null__w__r__test); + ATF_TP_ADD_TC(tp, null__r_append__r__test); + ATF_TP_ADD_TC(tp, null__r__w__test); + ATF_TP_ADD_TC(tp, null__w__w__test); + ATF_TP_ADD_TC(tp, null__r_append__w__test); + ATF_TP_ADD_TC(tp, null__r__a__test); + ATF_TP_ADD_TC(tp, null__w__a__test); + ATF_TP_ADD_TC(tp, null__r_append__a__test); + ATF_TP_ADD_TC(tp, null__r__r_append__test); + ATF_TP_ADD_TC(tp, null__w__r_append__test); + ATF_TP_ADD_TC(tp, null__r_append__r_append__test); + ATF_TP_ADD_TC(tp, null__r__w_append__test); + ATF_TP_ADD_TC(tp, null__w__w_append__test); + ATF_TP_ADD_TC(tp, null__r_append__w_append__test); + ATF_TP_ADD_TC(tp, sh__r__r__test); + ATF_TP_ADD_TC(tp, sh__sh__r__r__test); + ATF_TP_ADD_TC(tp, sh__null__r__r__test); + ATF_TP_ADD_TC(tp, sh__null__r__w__test); + + return (atf_no_error()); +} + +/* + vim:ts=8:cin:sw=8 + */ diff --git a/lib/libc/tests/stdio/getdelim_test.c b/lib/libc/tests/stdio/getdelim_test.c new file mode 100644 index 000000000000..01a0ab0949fe --- /dev/null +++ b/lib/libc/tests/stdio/getdelim_test.c @@ -0,0 +1,431 @@ +/*- + * Copyright (c) 2009 David Schultz <das@FreeBSD.org> + * Copyright (c) 2021 Dell EMC + * 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. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +#define CHUNK_MAX 10 + +/* The assertions depend on this string. */ +char apothegm[] = "All work and no play\0 makes Jack a dull boy.\n"; + +/* + * This is a neurotic reader function designed to give getdelim() a + * hard time. It reads through the string `apothegm' and returns a + * random number of bytes up to the requested length. + */ +static int +_reader(void *cookie, char *buf, int len) +{ + size_t *offp = cookie; + size_t r; + + r = random() % CHUNK_MAX + 1; + if (len > r) + len = r; + if (len > sizeof(apothegm) - *offp) + len = sizeof(apothegm) - *offp; + memcpy(buf, apothegm + *offp, len); + *offp += len; + return (len); +} + +static FILE * +mkfilebuf(void) +{ + size_t *offp; + + offp = malloc(sizeof(*offp)); /* XXX leak */ + *offp = 0; + return (fropen(offp, _reader)); +} + +ATF_TC_WITHOUT_HEAD(getline_basic); +ATF_TC_BODY(getline_basic, tc) +{ + FILE *fp; + char *line; + size_t linecap; + int i; + + srandom(0); + + /* + * Test multiple times with different buffer sizes + * and different _reader() return values. + */ + errno = 0; + for (i = 0; i < 8; i++) { + fp = mkfilebuf(); + linecap = i; + line = malloc(i); + /* First line: the full apothegm */ + ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1); + ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0); + ATF_REQUIRE(linecap >= sizeof(apothegm)); + /* Second line: the NUL terminator following the newline */ + ATF_REQUIRE(getline(&line, &linecap, fp) == 1); + ATF_REQUIRE(line[0] == '\0' && line[1] == '\0'); + /* Third line: EOF */ + line[0] = 'X'; + ATF_REQUIRE(getline(&line, &linecap, fp) == -1); + ATF_REQUIRE(line[0] == '\0'); + free(line); + line = NULL; + ATF_REQUIRE(feof(fp)); + ATF_REQUIRE(!ferror(fp)); + fclose(fp); + } + ATF_REQUIRE(errno == 0); +} + +ATF_TC_WITHOUT_HEAD(stream_error); +ATF_TC_BODY(stream_error, tc) +{ + char *line; + size_t linecap; + + /* Make sure read errors are handled properly. */ + line = NULL; + linecap = 0; + errno = 0; + ATF_REQUIRE(getline(&line, &linecap, stdout) == -1); + ATF_REQUIRE(errno == EBADF); + errno = 0; + ATF_REQUIRE(getdelim(&line, &linecap, 'X', stdout) == -1); + ATF_REQUIRE(errno == EBADF); + ATF_REQUIRE(ferror(stdout)); +} + +ATF_TC_WITHOUT_HEAD(invalid_params); +ATF_TC_BODY(invalid_params, tc) +{ + FILE *fp; + char *line; + size_t linecap; + + /* Make sure NULL linep or linecapp pointers are handled. */ + fp = mkfilebuf(); + ATF_REQUIRE(getline(NULL, &linecap, fp) == -1); + ATF_REQUIRE(errno == EINVAL); + ATF_REQUIRE(getline(&line, NULL, fp) == -1); + ATF_REQUIRE(errno == EINVAL); + ATF_REQUIRE(ferror(fp)); + fclose(fp); +} + +ATF_TC_WITHOUT_HEAD(eof); +ATF_TC_BODY(eof, tc) +{ + FILE *fp; + char *line; + size_t linecap; + + /* Make sure getline() allocates memory as needed if fp is at EOF. */ + errno = 0; + fp = mkfilebuf(); + while (!feof(fp)) /* advance to EOF; can't fseek this stream */ + getc(fp); + line = NULL; + linecap = 0; + printf("getline\n"); + ATF_REQUIRE(getline(&line, &linecap, fp) == -1); + ATF_REQUIRE(line[0] == '\0'); + ATF_REQUIRE(linecap > 0); + ATF_REQUIRE(errno == 0); + printf("feof\n"); + ATF_REQUIRE(feof(fp)); + ATF_REQUIRE(!ferror(fp)); + fclose(fp); +} + +ATF_TC_WITHOUT_HEAD(nul); +ATF_TC_BODY(nul, tc) +{ + FILE *fp; + char *line; + size_t linecap, n; + + errno = 0; + line = NULL; + linecap = 0; + /* Make sure a NUL delimiter works. */ + fp = mkfilebuf(); + n = strlen(apothegm); + printf("getdelim\n"); + ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1); + ATF_REQUIRE(strcmp(line, apothegm) == 0); + ATF_REQUIRE(line[n + 1] == '\0'); + ATF_REQUIRE(linecap > n + 1); + n = strlen(apothegm + n + 1); + printf("getdelim 2\n"); + ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1); + ATF_REQUIRE(line[n + 1] == '\0'); + ATF_REQUIRE(linecap > n + 1); + ATF_REQUIRE(errno == 0); + ATF_REQUIRE(!ferror(fp)); + fclose(fp); +} + +ATF_TC_WITHOUT_HEAD(empty_NULL_buffer); +ATF_TC_BODY(empty_NULL_buffer, tc) +{ + FILE *fp; + char *line; + size_t linecap; + + /* Make sure NULL *linep and zero *linecapp are handled. */ + fp = mkfilebuf(); + line = NULL; + linecap = 42; + ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1); + ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0); + fp = mkfilebuf(); + free(line); + line = malloc(100); + linecap = 0; + ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1); + ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0); + free(line); + ATF_REQUIRE(!ferror(fp)); + fclose(fp); +} + +static void +_ipc_read(int fd, char wait_c) +{ + char c; + ssize_t len; + + c = 0; + while (c != wait_c) { + len = read(fd, &c, 1); + ATF_CHECK_MSG(len != 0, + "EOF on IPC pipe while waiting. Did other side fail?"); + ATF_CHECK_MSG(len == 1 || errno == EINTR, + "read %zu bytes errno %d\n", len, errno); + if (len != 1 || errno != EINTR) + break; + } +} + +static void +_ipc_write(int fd, char c) +{ + + while ((write(fd, &c, 1) != 1)) + ATF_REQUIRE(errno == EINTR); +} + +static void +ipc_wait(int ipcfd[2]) +{ + + _ipc_read(ipcfd[0], '+'); + /* Send ACK. */ + _ipc_write(ipcfd[1], '-'); +} + +static void +ipc_wakeup(int ipcfd[2]) +{ + + _ipc_write(ipcfd[1], '+'); + /* Wait for ACK. */ + _ipc_read(ipcfd[0], '-'); +} + +static void +_nonblock_eagain(int buf_mode) +{ + FILE *fp; + const char delim = '!'; + const char *strs[] = { + "first line partial!", + "second line is sent in full!", + "third line is sent partially!", + "last line is sent in full!", + }; + char *line; + size_t linecap, strslen[nitems(strs)]; + ssize_t linelen; + int fd_fifo, flags, i, ipcfd[2], pipedes[2], pipedes2[2], status; + pid_t pid; + + line = NULL; + linecap = 0; + for (i = 0; i < nitems(strslen); i++) + strslen[i] = strlen(strs[i]); + ATF_REQUIRE(pipe2(pipedes, O_CLOEXEC) == 0); + ATF_REQUIRE(pipe2(pipedes2, O_CLOEXEC) == 0); + + (void)unlink("fifo"); + ATF_REQUIRE(mkfifo("fifo", 0666) == 0); + ATF_REQUIRE((pid = fork()) >= 0); + if (pid == 0) { + close(pipedes[0]); + ipcfd[1] = pipedes[1]; + ipcfd[0] = pipedes2[0]; + close(pipedes2[1]); + + ATF_REQUIRE((fd_fifo = open("fifo", O_WRONLY)) != -1); + + /* Partial write. */ + ATF_REQUIRE(write(fd_fifo, strs[0], strslen[0] - 3) == + strslen[0] - 3); + ipc_wakeup(ipcfd); + + ipc_wait(ipcfd); + /* Finish off the first line. */ + ATF_REQUIRE(write(fd_fifo, + &(strs[0][strslen[0] - 3]), 3) == 3); + /* And include the second full line and a partial 3rd line. */ + ATF_REQUIRE(write(fd_fifo, strs[1], strslen[1]) == strslen[1]); + ATF_REQUIRE(write(fd_fifo, strs[2], strslen[2] - 3) == + strslen[2] - 3); + ipc_wakeup(ipcfd); + + ipc_wait(ipcfd); + /* Finish the partial write and partially send the last. */ + ATF_REQUIRE(write(fd_fifo, + &(strs[2][strslen[2] - 3]), 3) == 3); + ATF_REQUIRE(write(fd_fifo, strs[3], strslen[3] - 3) == + strslen[3] - 3); + ipc_wakeup(ipcfd); + + ipc_wait(ipcfd); + /* Finish the write */ + ATF_REQUIRE(write(fd_fifo, + &(strs[3][strslen[3] - 3]), 3) == 3); + ipc_wakeup(ipcfd); + _exit(0); + } + ipcfd[0] = pipedes[0]; + close(pipedes[1]); + close(pipedes2[0]); + ipcfd[1] = pipedes2[1]; + + ATF_REQUIRE((fp = fopen("fifo", "r")) != NULL); + setvbuf(fp, (char *)NULL, buf_mode, 0); + ATF_REQUIRE((flags = fcntl(fileno(fp), F_GETFL, 0)) != -1); + ATF_REQUIRE(fcntl(fileno(fp), F_SETFL, flags | O_NONBLOCK) >= 0); + + /* Wait until the writer completes its partial write. */ + ipc_wait(ipcfd); + ATF_REQUIRE_ERRNO(EAGAIN, + (linelen = getdelim(&line, &linecap, delim, fp)) == -1); + ATF_REQUIRE_STREQ("", line); + ATF_REQUIRE(ferror(fp)); + ATF_REQUIRE(!feof(fp)); + clearerr(fp); + ipc_wakeup(ipcfd); + + ipc_wait(ipcfd); + /* + * Should now have the finished first line, a full second line, + * and a partial third line. + */ + ATF_CHECK(getdelim(&line, &linecap, delim, fp) == strslen[0]); + ATF_REQUIRE_STREQ(strs[0], line); + ATF_REQUIRE(getdelim(&line, &linecap, delim, fp) == strslen[1]); + ATF_REQUIRE_STREQ(strs[1], line); + + ATF_REQUIRE_ERRNO(EAGAIN, + (linelen = getdelim(&line, &linecap, delim, fp)) == -1); + ATF_REQUIRE_STREQ("", line); + ATF_REQUIRE(ferror(fp)); + ATF_REQUIRE(!feof(fp)); + clearerr(fp); + ipc_wakeup(ipcfd); + + /* Wait for the partial write to be completed and another to be done. */ + ipc_wait(ipcfd); + ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1); + ATF_REQUIRE(!ferror(fp)); + ATF_REQUIRE(!feof(fp)); + ATF_REQUIRE_STREQ(strs[2], line); + ATF_REQUIRE(linelen == strslen[2]); + + ATF_REQUIRE_ERRNO(EAGAIN, + (linelen = getdelim(&line, &linecap, delim, fp)) == -1); + ATF_REQUIRE_STREQ("", line); + ATF_REQUIRE(ferror(fp)); + ATF_REQUIRE(!feof(fp)); + clearerr(fp); + ipc_wakeup(ipcfd); + + ipc_wait(ipcfd); + ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1); + ATF_REQUIRE(!ferror(fp)); + ATF_REQUIRE(!feof(fp)); + ATF_REQUIRE_STREQ(strs[3], line); + ATF_REQUIRE(linelen == strslen[3]); + + ATF_REQUIRE(waitpid(pid, &status, WEXITED) != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 0); +} + +ATF_TC_WITHOUT_HEAD(nonblock_eagain_buffered); +ATF_TC_BODY(nonblock_eagain_buffered, tc) +{ + + _nonblock_eagain(_IOFBF); +} + +ATF_TC_WITHOUT_HEAD(nonblock_eagain_unbuffered); +ATF_TC_BODY(nonblock_eagain_unbuffered, tc) +{ + + _nonblock_eagain(_IONBF); +} + + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, getline_basic); + ATF_TP_ADD_TC(tp, stream_error); + ATF_TP_ADD_TC(tp, eof); + ATF_TP_ADD_TC(tp, invalid_params); + ATF_TP_ADD_TC(tp, nul); + ATF_TP_ADD_TC(tp, empty_NULL_buffer); + ATF_TP_ADD_TC(tp, nonblock_eagain_unbuffered); + ATF_TP_ADD_TC(tp, nonblock_eagain_buffered); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/gets_s_test.c b/lib/libc/tests/stdio/gets_s_test.c new file mode 100644 index 000000000000..4e1a59006b60 --- /dev/null +++ b/lib/libc/tests/stdio/gets_s_test.c @@ -0,0 +1,142 @@ +/*- + * Copyright (c) 2017 Cyril S. E. Schubert + * + * 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 REGENTS 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 REGENTS 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 <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/wait.h> + +#include <atf-c.h> + +static errno_t error_code; +static const char * message; + +void +h(const char * msg, void * ptr __unused, errno_t error) +{ + error_code = error; + message = msg; +} + +/* null ptr */ +ATF_TC_WITHOUT_HEAD(null_ptr); +ATF_TC_BODY(null_ptr, tc) +{ + ATF_CHECK_MSG(gets_s(NULL, 1) == NULL, + "gets_s() failed to handle NULL pointer"); +} + +/* normal */ +ATF_TC_WITHOUT_HEAD(normal); +ATF_TC_BODY(normal, tc) +{ + pid_t kidpid; + int fd[2]; + int nfd; + + // close(STDIN_FILENO); + // close(STDOUT_FILENO); + pipe(fd); + + if ((kidpid = fork()) == 0) { + char b[10]; + + close(fd[1]); + nfd = dup2(fd[0], 0); + close(fd[0]); + stdin = fdopen(nfd, "r"); + ATF_CHECK_MSG(gets_s(b, sizeof(b)) == 0, "gets_s() normal failed"); + fclose(stdin); + } else { + int stat; + + close(fd[0]); + stdout = fdopen(fd[1], "w"); + puts("a sting"); + fclose(stdout); + (void) waitpid(kidpid, &stat, WEXITED); + } +} + +/* n > rmax */ +ATF_TC_WITHOUT_HEAD(n_gt_rmax); +ATF_TC_BODY(n_gt_rmax, tc) +{ + char b; + + ATF_CHECK_MSG(gets_s(&b, RSIZE_MAX + 1) == NULL, + "gets_s() n > RSIZE_MAX"); +} + +/* n == 0 */ +ATF_TC_WITHOUT_HEAD(n_eq_zero); +ATF_TC_BODY(n_eq_zero, tc) +{ + char b; + + ATF_CHECK_MSG(gets_s(&b, 0) == NULL, "gets_s() n is zero"); +} + +/* n > rmax, handler */ +ATF_TC_WITHOUT_HEAD(n_gt_rmax_handler); +ATF_TC_BODY(n_gt_rmax_handler, tc) +{ + char b; + + error_code = 0; + message = NULL; + set_constraint_handler_s(h); + ATF_CHECK_MSG(gets_s(&b, RSIZE_MAX + 1) == NULL, "gets_s() n > RSIZE_MAX"); + ATF_CHECK_MSG(error_code > 0, "gets_s() error code is %d", error_code); + ATF_CHECK_MSG(strcmp(message, "gets_s : n > RSIZE_MAX") == 0, "gets_s(): incorrect error message"); +} + +/* n == 0, handler */ +ATF_TC_WITHOUT_HEAD(n_eq_zero_handler); +ATF_TC_BODY(n_eq_zero_handler, tc) +{ + char b; + + error_code = 0; + message = NULL; + set_constraint_handler_s(h); + ATF_CHECK(gets_s(&b, 0) == NULL); + ATF_CHECK_MSG(error_code > 0, "gets_s() error code is %d", error_code); + ATF_CHECK_MSG(strcmp(message, "gets_s : n == 0") == 0, "gets_s(): incorrect error message"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, null_ptr); + ATF_TP_ADD_TC(tp, normal); + ATF_TP_ADD_TC(tp, n_gt_rmax); + ATF_TP_ADD_TC(tp, n_eq_zero); + ATF_TP_ADD_TC(tp, n_gt_rmax_handler); + ATF_TP_ADD_TC(tp, n_eq_zero_handler); + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/mkostemp_test.c b/lib/libc/tests/stdio/mkostemp_test.c new file mode 100644 index 000000000000..b31335fed43f --- /dev/null +++ b/lib/libc/tests/stdio/mkostemp_test.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 2013 Jilles Tjoelker + * 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 mkostemp(). + */ + +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +static const char template[] = "mkostemp.XXXXXXXX"; +static int testnum; + +#define MISCFLAGS (O_APPEND | O_DIRECT | O_SHLOCK | O_EXLOCK | O_SYNC) + +static void +test_one(int oflags) +{ + char tmpf[sizeof(template)]; + struct stat st1, st2; + int fd; + + memcpy(tmpf, template, sizeof(tmpf)); + fd = mkostemp(tmpf, oflags); + if (fd < 0) { + printf("not ok %d - oflags=%#x " + "mkostemp() reported failure: %s\n", + testnum++, oflags, strerror(errno)); + return; + } + if (memcmp(tmpf, template, sizeof(tmpf) - 8 - 1) != 0) { + printf("not ok %d - oflags=%#x " + "returned pathname does not match template: %s\n", + testnum++, oflags, tmpf); + return; + } + do { + if (fcntl(fd, F_GETFD) != + (oflags & O_CLOEXEC ? FD_CLOEXEC : 0)) { + printf("not ok %d - oflags=%#x " + "close-on-exec flag incorrect\n", + testnum++, oflags); + break; + } + if ((fcntl(fd, F_GETFL) & MISCFLAGS) != (oflags & MISCFLAGS)) { + printf("not ok %d - oflags=%#x " + "open flags incorrect\n", + testnum++, oflags); + break; + } + if (stat(tmpf, &st1) == -1) { + printf("not ok %d - oflags=%#x " + "cannot stat returned pathname %s: %s\n", + testnum++, oflags, tmpf, strerror(errno)); + break; + } + if (fstat(fd, &st2) == -1) { + printf("not ok %d - oflags=%#x " + "cannot fstat returned fd %d: %s\n", + testnum++, oflags, fd, strerror(errno)); + break; + } + if (!S_ISREG(st1.st_mode) || (st1.st_mode & 0777) != 0600 || + st1.st_nlink != 1 || st1.st_size != 0) { + printf("not ok %d - oflags=%#x " + "named file attributes incorrect\n", + testnum++, oflags); + break; + } + if (!S_ISREG(st2.st_mode) || (st2.st_mode & 0777) != 0600 || + st2.st_nlink != 1 || st2.st_size != 0) { + printf("not ok %d - oflags=%#x " + "opened file attributes incorrect\n", + testnum++, oflags); + break; + } + if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) { + printf("not ok %d - oflags=%#x " + "named and opened file do not match\n", + testnum++, oflags); + break; + } + (void)unlink(tmpf); + if (fstat(fd, &st2) == -1) + printf("not ok %d - oflags=%#x " + "cannot fstat returned fd %d again: %s\n", + testnum++, oflags, fd, strerror(errno)); + else if (st2.st_nlink != 0) + printf("not ok %d - oflags=%#x " + "st_nlink is not 0 after unlink\n", + testnum++, oflags); + else + printf("ok %d - oflags=%#x\n", testnum++, oflags); + (void)close(fd); + return; + } while (0); + (void)close(fd); + (void)unlink(tmpf); +} + +ATF_TC_WITHOUT_HEAD(zero); +ATF_TC_BODY(zero, tc) +{ + + test_one(0); +} + +ATF_TC_WITHOUT_HEAD(O_CLOEXEC); +ATF_TC_BODY(O_CLOEXEC, tc) +{ + + test_one(O_CLOEXEC); +} + +ATF_TC_WITHOUT_HEAD(O_APPEND); +ATF_TC_BODY(O_APPEND, tc) +{ + + test_one(O_APPEND); +} + +ATF_TC_WITHOUT_HEAD(O_APPEND__O_CLOEXEC); +ATF_TC_BODY(O_APPEND__O_CLOEXEC, tc) +{ + + test_one(O_APPEND|O_CLOEXEC); +} + +ATF_TC_WITHOUT_HEAD(bad_flags); +ATF_TC_BODY(bad_flags, tc) +{ + + char tmpf[sizeof(template)]; + + memcpy(tmpf, template, sizeof(tmpf)); + ATF_REQUIRE_MSG(mkostemp(tmpf, O_CREAT) == -1, + "mkostemp(O_CREAT) succeeded unexpectedly"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, zero); + ATF_TP_ADD_TC(tp, O_CLOEXEC); + ATF_TP_ADD_TC(tp, O_APPEND); + ATF_TP_ADD_TC(tp, O_APPEND__O_CLOEXEC); + ATF_TP_ADD_TC(tp, bad_flags); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/open_memstream2_test.c b/lib/libc/tests/stdio/open_memstream2_test.c new file mode 100644 index 000000000000..c9c6528832d8 --- /dev/null +++ b/lib/libc/tests/stdio/open_memstream2_test.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2013 Hudson River Trading LLC + * Written by: John H. Baldwin <jhb@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. + */ + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <atf-c.h> + +static char *buf; +static size_t len; + +static void +assert_stream(const char *contents) +{ + if (strlen(contents) != len) + printf("bad length %zd for \"%s\"\n", len, contents); + else if (strncmp(buf, contents, strlen(contents)) != 0) + printf("bad buffer \"%s\" for \"%s\"\n", buf, contents); +} + +ATF_TC_WITHOUT_HEAD(open_group_test); +ATF_TC_BODY(open_group_test, tc) +{ + FILE *fp; + off_t eob; + + fp = open_memstream(&buf, &len); + ATF_REQUIRE_MSG(fp != NULL, "open_memstream failed"); + + fprintf(fp, "hello my world"); + fflush(fp); + assert_stream("hello my world"); + eob = ftello(fp); + rewind(fp); + fprintf(fp, "good-bye"); + fseeko(fp, eob, SEEK_SET); + fclose(fp); + assert_stream("good-bye world"); + free(buf); +} + +ATF_TC_WITHOUT_HEAD(simple_tests); +ATF_TC_BODY(simple_tests, tc) +{ + static const char zerobuf[] = + { 'f', 'o', 'o', 0, 0, 0, 0, 'b', 'a', 'r', 0 }; + char c; + FILE *fp; + + fp = open_memstream(&buf, NULL); + ATF_REQUIRE_MSG(fp == NULL, "open_memstream did not fail"); + ATF_REQUIRE_MSG(errno == EINVAL, + "open_memstream didn't fail with EINVAL"); + fp = open_memstream(NULL, &len); + ATF_REQUIRE_MSG(fp == NULL, "open_memstream did not fail"); + ATF_REQUIRE_MSG(errno == EINVAL, + "open_memstream didn't fail with EINVAL"); + fp = open_memstream(&buf, &len); + ATF_REQUIRE_MSG(fp != NULL, "open_memstream failed; errno=%d", errno); + fflush(fp); + assert_stream(""); + if (fwide(fp, 0) >= 0) + printf("stream is not byte-oriented\n"); + + fprintf(fp, "fo"); + fflush(fp); + assert_stream("fo"); + fputc('o', fp); + fflush(fp); + assert_stream("foo"); + rewind(fp); + fflush(fp); + assert_stream(""); + fseek(fp, 0, SEEK_END); + fflush(fp); + assert_stream("foo"); + + /* + * Test seeking out past the current end. Should zero-fill the + * intermediate area. + */ + fseek(fp, 4, SEEK_END); + fprintf(fp, "bar"); + fflush(fp); + + /* + * Can't use assert_stream() here since this should contain + * embedded null characters. + */ + if (len != 10) + printf("bad length %zd for zero-fill test\n", len); + else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0) + printf("bad buffer for zero-fill test\n"); + + fseek(fp, 3, SEEK_SET); + fprintf(fp, " in "); + fflush(fp); + assert_stream("foo in "); + fseek(fp, 0, SEEK_END); + fflush(fp); + assert_stream("foo in bar"); + + rewind(fp); + if (fread(&c, sizeof(c), 1, fp) != 0) + printf("fread did not fail\n"); + else if (!ferror(fp)) + printf("error indicator not set after fread\n"); + else + clearerr(fp); + + fseek(fp, 4, SEEK_SET); + fprintf(fp, "bar baz"); + fclose(fp); + assert_stream("foo bar baz"); + free(buf); +} + +ATF_TC_WITHOUT_HEAD(seek_tests); +ATF_TC_BODY(seek_tests, tc) +{ + FILE *fp; + + fp = open_memstream(&buf, &len); + ATF_REQUIRE_MSG(fp != NULL, "open_memstream failed: %d", errno); + +#define SEEK_FAIL(offset, whence, error) do { \ + errno = 0; \ + ATF_REQUIRE_MSG(fseeko(fp, (offset), (whence)) != 0, \ + "fseeko(%s, %s) did not fail, set pos to %jd", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp)); \ + ATF_REQUIRE_MSG(errno == (error), \ + "fseeko(%s, %s) failed with %d rather than %s", \ + __STRING(offset), __STRING(whence), errno, \ + __STRING(error)); \ +} while (0) + +#define SEEK_OK(offset, whence, result) do { \ + ATF_REQUIRE_MSG(fseeko(fp, (offset), (whence)) == 0, \ + "fseeko(%s, %s) failed: %s", \ + __STRING(offset), __STRING(whence), strerror(errno)); \ + ATF_REQUIRE_MSG(ftello(fp) == (result), \ + "fseeko(%s, %s) seeked to %jd rather than %s", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp), __STRING(result)); \ +} while (0) + + SEEK_FAIL(-1, SEEK_SET, EINVAL); + SEEK_FAIL(-1, SEEK_CUR, EINVAL); + SEEK_FAIL(-1, SEEK_END, EINVAL); + fprintf(fp, "foo"); + SEEK_OK(-1, SEEK_CUR, 2); + SEEK_OK(0, SEEK_SET, 0); + SEEK_OK(-1, SEEK_END, 2); + SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1); + SEEK_FAIL(2, SEEK_CUR, EOVERFLOW); + fclose(fp); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, open_group_test); + ATF_TP_ADD_TC(tp, simple_tests); + ATF_TP_ADD_TC(tp, seek_tests); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/open_wmemstream_test.c b/lib/libc/tests/stdio/open_wmemstream_test.c new file mode 100644 index 000000000000..7ab882a4740a --- /dev/null +++ b/lib/libc/tests/stdio/open_wmemstream_test.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2013 Hudson River Trading LLC + * Written by: John H. Baldwin <jhb@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. + */ + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <atf-c.h> + +static wchar_t *buf; +static size_t len; + +static void +assert_stream(const wchar_t *contents) +{ + if (wcslen(contents) != len) + printf("bad length %zd for \"%ls\"\n", len, contents); + else if (wcsncmp(buf, contents, wcslen(contents)) != 0) + printf("bad buffer \"%ls\" for \"%ls\"\n", buf, contents); +} + +ATF_TC_WITHOUT_HEAD(open_group_test); +ATF_TC_BODY(open_group_test, tc) +{ + FILE *fp; + off_t eob; + + fp = open_wmemstream(&buf, &len); + ATF_REQUIRE_MSG(fp != NULL, "open_wmemstream failed"); + + fwprintf(fp, L"hello my world"); + fflush(fp); + assert_stream(L"hello my world"); + eob = ftello(fp); + rewind(fp); + fwprintf(fp, L"good-bye"); + fseeko(fp, eob, SEEK_SET); + fclose(fp); + assert_stream(L"good-bye world"); + free(buf); +} + +ATF_TC_WITHOUT_HEAD(simple_tests); +ATF_TC_BODY(simple_tests, tc) +{ + static const wchar_t zerobuf[] = + { L'f', L'o', L'o', 0, 0, 0, 0, L'b', L'a', L'r', 0 }; + wchar_t c; + FILE *fp; + + fp = open_wmemstream(&buf, NULL); + ATF_REQUIRE_MSG(fp == NULL, "open_wmemstream did not fail"); + ATF_REQUIRE_MSG(errno == EINVAL, + "open_wmemstream didn't fail with EINVAL"); + fp = open_wmemstream(NULL, &len); + ATF_REQUIRE_MSG(fp == NULL, "open_wmemstream did not fail"); + ATF_REQUIRE_MSG(errno == EINVAL, + "open_wmemstream didn't fail with EINVAL"); + fp = open_wmemstream(&buf, &len); + ATF_REQUIRE_MSG(fp != NULL, "open_memstream failed; errno=%d", errno); + fflush(fp); + assert_stream(L""); + if (fwide(fp, 0) <= 0) + printf("stream is not wide-oriented\n"); + + fwprintf(fp, L"fo"); + fflush(fp); + assert_stream(L"fo"); + fputwc(L'o', fp); + fflush(fp); + assert_stream(L"foo"); + rewind(fp); + fflush(fp); + assert_stream(L""); + fseek(fp, 0, SEEK_END); + fflush(fp); + assert_stream(L"foo"); + + /* + * Test seeking out past the current end. Should zero-fill the + * intermediate area. + */ + fseek(fp, 4, SEEK_END); + fwprintf(fp, L"bar"); + fflush(fp); + + /* + * Can't use assert_stream() here since this should contain + * embedded null characters. + */ + if (len != 10) + printf("bad length %zd for zero-fill test\n", len); + else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0) + printf("bad buffer for zero-fill test\n"); + + fseek(fp, 3, SEEK_SET); + fwprintf(fp, L" in "); + fflush(fp); + assert_stream(L"foo in "); + fseek(fp, 0, SEEK_END); + fflush(fp); + assert_stream(L"foo in bar"); + + rewind(fp); + if (fread(&c, sizeof(c), 1, fp) != 0) + printf("fread did not fail\n"); + else if (!ferror(fp)) + printf("error indicator not set after fread\n"); + else + clearerr(fp); + + fseek(fp, 4, SEEK_SET); + fwprintf(fp, L"bar baz"); + fclose(fp); + assert_stream(L"foo bar baz"); + free(buf); +} + +ATF_TC_WITHOUT_HEAD(seek_tests); +ATF_TC_BODY(seek_tests, tc) +{ + FILE *fp; + + fp = open_wmemstream(&buf, &len); + ATF_REQUIRE_MSG(fp != NULL, "open_wmemstream failed; errno=%d", errno); + +#define SEEK_FAIL(offset, whence, error) do { \ + errno = 0; \ + ATF_REQUIRE_MSG(fseeko(fp, (offset), (whence)) != 0, \ + "fseeko(%s, %s) did not fail, set pos to %jd", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp)); \ + ATF_REQUIRE_MSG(errno == (error), \ + "fseeko(%s, %s) failed with %d rather than %s", \ + __STRING(offset), __STRING(whence), errno, \ + __STRING(error)); \ +} while (0) + +#define SEEK_OK(offset, whence, result) do { \ + ATF_REQUIRE_MSG(fseeko(fp, (offset), (whence)) == 0, \ + "fseeko(%s, %s) failed: %s", \ + __STRING(offset), __STRING(whence), strerror(errno)); \ + ATF_REQUIRE_MSG(ftello(fp) == (result), \ + "fseeko(%s, %s) seeked to %jd rather than %s", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp), __STRING(result)); \ +} while (0) + + SEEK_FAIL(-1, SEEK_SET, EINVAL); + SEEK_FAIL(-1, SEEK_CUR, EINVAL); + SEEK_FAIL(-1, SEEK_END, EINVAL); + fwprintf(fp, L"foo"); + SEEK_OK(-1, SEEK_CUR, 2); + SEEK_OK(0, SEEK_SET, 0); + SEEK_OK(-1, SEEK_END, 2); + SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1); + SEEK_FAIL(2, SEEK_CUR, EOVERFLOW); + fclose(fp); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, open_group_test); + ATF_TP_ADD_TC(tp, simple_tests); + ATF_TP_ADD_TC(tp, seek_tests); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/perror_test.c b/lib/libc/tests/stdio/perror_test.c new file mode 100644 index 000000000000..f3cb646d094e --- /dev/null +++ b/lib/libc/tests/stdio/perror_test.c @@ -0,0 +1,104 @@ +/*- + * Copyright (c) 2002 Tim J. Robbins + * 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 perror() as specified by IEEE Std. 1003.1-2001 and + * ISO/IEC 9899:1999. + */ + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +static char tmpfil[PATH_MAX]; + +ATF_TC_WITHOUT_HEAD(perror_test); +ATF_TC_BODY(perror_test, tc) +{ + char lbuf[512]; + int i; + char *s; + + strcpy(tmpfil, "perror.XXXXXXXX"); + ATF_REQUIRE(mkstemp(tmpfil) >= 0); + /* Reopen stderr on a file descriptor other than 2. */ + fclose(stderr); + for (i = 0; i < 3; i++) + dup(0); + ATF_REQUIRE(freopen(tmpfil, "r+", stderr) != NULL); + + /* + * Test that perror() doesn't call strerror() (4.4BSD bug), + * the two ways of omitting a program name, and the formatting when + * a program name is specified. + */ + s = strerror(ENOENT); + ATF_REQUIRE_MSG(strcmp(s, "No such file or directory") == 0, + "message obtained was: %s", s); + errno = EPERM; + perror(NULL); + perror(""); + perror("perror_test"); + ATF_REQUIRE_MSG(strcmp(s, "No such file or directory") == 0, + "message obtained was: %s", s); + + /* + * Read it back to check... + */ + rewind(stderr); + s = fgets(lbuf, sizeof(lbuf), stderr); + ATF_REQUIRE(s != NULL); + ATF_REQUIRE_MSG(strcmp(s, "Operation not permitted\n") == 0, + "message obtained was: %s", s); + s = fgets(lbuf, sizeof(lbuf), stderr); + ATF_REQUIRE(s != NULL); + ATF_REQUIRE_MSG(strcmp(s, "Operation not permitted\n") == 0, + "message obtained was: %s", s); + s = fgets(lbuf, sizeof(lbuf), stderr); + ATF_REQUIRE(s != NULL); + ATF_REQUIRE_MSG( + strcmp(s, "perror_test: Operation not permitted\n") == 0, + "message obtained was: %s", s); + s = fgets(lbuf, sizeof(lbuf), stderr); + ATF_REQUIRE(s == NULL); + fclose(stderr); + +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, perror_test); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/print_positional_test.c b/lib/libc/tests/stdio/print_positional_test.c new file mode 100644 index 000000000000..f896a30882f4 --- /dev/null +++ b/lib/libc/tests/stdio/print_positional_test.c @@ -0,0 +1,154 @@ +/* $OpenBSD: sprintf_test.c,v 1.3 2004/09/16 20:22:26 otto Exp $ */ + +/* + * Copyright (c) 2003 Theo de Raadt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <atf-c.h> + +const char correct[] = + "|xx 01 02 03 04\n" + "|xx 05 06 07 08\n" + "|xx 09 10 11 12\n" + "|xx 13 14 15 16\n" + "|xx 17 18 19 20\n" + "|xx 21 22 23 24\n" + "|xx 25 26 27 28\n" + "|xx 29 30 31 32\n" + "|xx 33 34 35 36\n" + "|xx 37 38 39 40\n" + "|xx 41 42 43 44\n" + "|xx 45 -1 1 -1 1\n"; + +const char correct2[] = + "b bs BSD"; +static char buf[1024]; +static wchar_t wbuf1[1024], wbuf2[1024]; +static const char *temp; + +ATF_TC_WITHOUT_HEAD(positional_normal); +ATF_TC_BODY(positional_normal, tc) +{ + + /* Test positional arguments */ + snprintf(buf, sizeof buf, + "|xx %1$s %2$s %3$s %4$s\n" + "|xx %5$s %6$s %7$s %8$s\n" + "|xx %9$s %10$s %11$s %12$s\n" + "|xx %13$s %14$s %15$s %16$s\n" + "|xx %17$s %18$s %19$s %20$s\n" + "|xx %21$s %22$s %23$s %24$s\n" + "|xx %25$s %26$s %27$s %28$s\n" + "|xx %29$s %30$s %31$s %32$s\n" + "|xx %33$s %34$s %35$s %36$s\n" + "|xx %37$s %38$s %39$s %40$s\n" + "|xx %41$s %42$s %43$s %44$s\n" + "|xx %45$d %46$ld %47$lld %48$d %49$lld\n", + "01", "02", "03", "04", "05", "06", + "07", "08", "09", "10", "11", "12", + "13", "14", "15", "16", "17", "18", + "19", "20", "21", "22", "23", "24", + "25", "26", "27", "28", "29", "30", + "31", "32", "33", "34", "35", "36", + "37", "38", "39", "40", "41", "42", + "43", "44", 45, -1L, 1LL, -1, 1LL + ); + ATF_REQUIRE_MSG(wcscmp(wbuf1, wbuf2) == 0, + "buffers didn't match"); +} + +ATF_TC_WITHOUT_HEAD(positional_wide); +ATF_TC_BODY(positional_wide, tc) +{ + + swprintf(wbuf1, nitems(wbuf1), + L"|xx %1$s %2$s %3$s %4$s\n" + "|xx %5$s %6$s %7$s %8$s\n" + "|xx %9$s %10$s %11$s %12$s\n" + "|xx %13$s %14$s %15$s %16$s\n" + "|xx %17$s %18$s %19$s %20$s\n" + "|xx %21$s %22$s %23$s %24$s\n" + "|xx %25$s %26$s %27$s %28$s\n" + "|xx %29$s %30$s %31$s %32$s\n" + "|xx %33$s %34$s %35$s %36$s\n" + "|xx %37$s %38$s %39$s %40$s\n" + "|xx %41$s %42$s %43$s %44$s\n" + "|xx %45$d %46$ld %47$lld %48$d %49$lld\n", + "01", "02", "03", "04", "05", "06", + "07", "08", "09", "10", "11", "12", + "13", "14", "15", "16", "17", "18", + "19", "20", "21", "22", "23", "24", + "25", "26", "27", "28", "29", "30", + "31", "32", "33", "34", "35", "36", + "37", "38", "39", "40", "41", "42", + "43", "44", 45, -1L, 1LL, -1, 1LL + ); + temp = correct; + mbsrtowcs(wbuf2, &temp, nitems(wbuf2), NULL); + ATF_REQUIRE_MSG(wcscmp(wbuf1, wbuf2) == 0, + "buffers didn't match"); +} + +ATF_TC_WITHOUT_HEAD(positional_precision); +ATF_TC_BODY(positional_precision, tc) +{ + + snprintf(buf, sizeof buf, "%2$.*4$s %2$.*3$s %1$s", + "BSD", "bsd", 2, 1); + ATF_REQUIRE_MSG(strcmp(buf, correct2) == 0, + "buffers didn't match"); +} + +ATF_TC_WITHOUT_HEAD(positional_precision_wide); +ATF_TC_BODY(positional_precision_wide, tc) +{ + + swprintf(wbuf1, sizeof buf, L"%2$.*4$s %2$.*3$s %1$s", + "BSD", "bsd", 2, 1); + temp = correct2; + mbsrtowcs(wbuf2, &temp, nitems(wbuf2), NULL); + ATF_REQUIRE_MSG(wcscmp(wbuf1, wbuf2) == 0, + "buffers didn't match"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, positional_normal); + ATF_TP_ADD_TC(tp, positional_wide); + ATF_TP_ADD_TC(tp, positional_precision); + ATF_TP_ADD_TC(tp, positional_precision_wide); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/printbasic_test.c b/lib/libc/tests/stdio/printbasic_test.c new file mode 100644 index 000000000000..411dff518795 --- /dev/null +++ b/lib/libc/tests/stdio/printbasic_test.c @@ -0,0 +1,159 @@ +/*- + * Copyright (c) 2009 David Schultz <das@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. + */ + +/* + * Tests for basic and miscellaneous printf() formats. + */ + +#include <err.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <atf-c.h> + +#define S_UINT64MAX "18446744073709551615" +#define S_UINT32MAX "4294967295" +#define S_INT64MIN "-9223372036854775808" +#define S_INT32MIN "-2147483648" + +#define S_SIZEMAX (SIZE_MAX == UINT64_MAX ? S_UINT64MAX : S_UINT32MAX) +#define S_ULONGMAX (ULONG_MAX == UINT64_MAX ? S_UINT64MAX : S_UINT32MAX) +#define S_ULLONGMAX (ULLONG_MAX == UINT64_MAX ? S_UINT64MAX : S_UINT32MAX) + +static void +smash_stack(void) +{ + static uint32_t junk = 0xdeadbeef; + uint32_t buf[512]; + int i; + + for (i = 0; i < sizeof(buf) / sizeof(buf[0]); i++) + buf[i] = junk; +} + +#define testfmt(result, fmt, ...) \ + _testfmt((result), #__VA_ARGS__, fmt, __VA_ARGS__) +static void +_testfmt(const char *result, const char *argstr, const char *fmt,...) +{ +#define BUF 100 + wchar_t ws[BUF], wfmt[BUF], wresult[BUF]; + char s[BUF]; + va_list ap, ap2; + + va_start(ap, fmt); + va_copy(ap2, ap); + smash_stack(); + vsnprintf(s, sizeof(s), fmt, ap); + ATF_CHECK_MSG(strcmp(result, s) == 0, + "printf(\"%s\", %s) ==> [%s], expected [%s]", + fmt, argstr, s, result); + + smash_stack(); + mbstowcs(ws, s, BUF - 1); + mbstowcs(wfmt, fmt, BUF - 1); + mbstowcs(wresult, result, BUF - 1); + vswprintf(ws, sizeof(ws) / sizeof(ws[0]), wfmt, ap2); + ATF_CHECK_MSG(wcscmp(wresult, ws) == 0, + "wprintf(\"%ls\", %s) ==> [%ls], expected [%ls]", + wfmt, argstr, ws, wresult); + + va_end(ap); + va_end(ap2); +} + +ATF_TC_WITHOUT_HEAD(int_within_limits); +ATF_TC_BODY(int_within_limits, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + /* The test requires these to be true. */ + ATF_REQUIRE(UINTMAX_MAX == UINT64_MAX); + ATF_REQUIRE(UINT_MAX == UINT32_MAX); + ATF_REQUIRE(USHRT_MAX == 0xffff); + ATF_REQUIRE(UCHAR_MAX == 0xff); + + /* Make sure we handle signed vs. unsigned args correctly. */ + testfmt("-1", "%jd", (intmax_t)-1); + testfmt(S_UINT64MAX, "%ju", UINT64_MAX); + + if (sizeof(ptrdiff_t) != sizeof(uintmax_t)) + atf_tc_expect_fail("the %%t qualifier is broken on 32-bit " + "platforms where there's a mismatch between ptrdiff_t and " + "uintmax_t's type width; bug # 191674"); + + testfmt("-1", "%td", (ptrdiff_t)-1); + testfmt(S_SIZEMAX, "%tu", (size_t)-1); + + testfmt("-1", "%zd", (ssize_t)-1); + testfmt(S_SIZEMAX, "%zu", (ssize_t)-1); + + testfmt("-1", "%ld", (long)-1); + testfmt(S_ULONGMAX, "%lu", ULONG_MAX); + + testfmt("-1", "%lld", (long long)-1); + testfmt(S_ULLONGMAX, "%llu", ULLONG_MAX); + + testfmt("-1", "%d", -1); + testfmt(S_UINT32MAX, "%u", UINT32_MAX); + + testfmt("-1", "%hd", -1); + testfmt("65535", "%hu", USHRT_MAX); + + testfmt("-1", "%hhd", -1); + testfmt("255", "%hhu", UCHAR_MAX); +} + +ATF_TC_WITHOUT_HEAD(int_limits); +ATF_TC_BODY(int_limits, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + /* + * Check that printing the largest negative number does not cause + * overflow when it is negated. + */ + testfmt(S_INT32MIN, "%d", INT_MIN); + testfmt(S_INT64MIN, "%jd", INTMAX_MIN); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, int_within_limits); + ATF_TP_ADD_TC(tp, int_limits); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/printfloat_test.c b/lib/libc/tests/stdio/printfloat_test.c new file mode 100644 index 000000000000..4493fe1c15d3 --- /dev/null +++ b/lib/libc/tests/stdio/printfloat_test.c @@ -0,0 +1,432 @@ +/*- + * Copyright (c) 2002-2009 David Schultz <das@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 for printf() floating point formats. + */ + +#include <err.h> +#include <fenv.h> +#include <float.h> +#include <locale.h> +#include <math.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <atf-c.h> + +static void +smash_stack(void) +{ + static uint32_t junk = 0xdeadbeef; + uint32_t buf[512]; + size_t i; + + for (i = 0; i < sizeof(buf) / sizeof(buf[0]); i++) + buf[i] = junk; +} + +#define testfmt(result, fmt, ...) \ + _testfmt((result), #__VA_ARGS__, fmt, __VA_ARGS__) +static void +_testfmt(const char *result, const char *argstr, const char *fmt,...) +{ +#define BUF 100 + wchar_t ws[BUF], wfmt[BUF], wresult[BUF]; + char s[BUF]; + va_list ap, ap2; + + va_start(ap, fmt); + va_copy(ap2, ap); + smash_stack(); + vsnprintf(s, sizeof(s), fmt, ap); + ATF_CHECK_MSG(strcmp(result, s) == 0, + "printf(\"%s\", %s) ==> [%s], expected [%s]", + fmt, argstr, s, result); + + smash_stack(); + mbstowcs(ws, s, BUF - 1); + mbstowcs(wfmt, fmt, BUF - 1); + mbstowcs(wresult, result, BUF - 1); + vswprintf(ws, sizeof(ws) / sizeof(ws[0]), wfmt, ap2); + ATF_CHECK_MSG(wcscmp(wresult, ws) == 0, + "wprintf(\"%ls\", %s) ==> [%ls], expected [%ls]", + wfmt, argstr, ws, wresult); + + va_end(ap); + va_end(ap2); +} + +ATF_TC_WITHOUT_HEAD(float_within_limits); +ATF_TC_BODY(float_within_limits, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + /* Basic tests of decimal output functionality. */ + testfmt(" 1.000000E+00", "%13E", 1.0); + testfmt(" 1.000000", "%13f", 1.0); + testfmt(" 1", "%13G", 1.0); + testfmt(" 1.000000E+00", "%13LE", 1.0L); + testfmt(" 1.000000", "%13Lf", 1.0L); + testfmt(" 1", "%13LG", 1.0L); + + testfmt("2.718282", "%.*f", -2, 2.7182818); + + testfmt("1.234568e+06", "%e", 1234567.8); + testfmt("1234567.800000", "%f", 1234567.8); + testfmt("1.23457E+06", "%G", 1234567.8); + testfmt("1.234568e+06", "%Le", 1234567.8L); + testfmt("1234567.800000", "%Lf", 1234567.8L); + testfmt("1.23457E+06", "%LG", 1234567.8L); + +#if (LDBL_MANT_DIG > DBL_MANT_DIG) && !defined(__i386__) + testfmt("123456789.864210", "%Lf", 123456789.8642097531L); + testfmt("-1.23457E+08", "%LG", -123456789.8642097531L); + testfmt("123456789.8642097531", "%.10Lf", 123456789.8642097531L); + testfmt(" 3.141592653589793238e-4000", "%L27.18Le", + 3.14159265358979323846e-4000L); +#endif +} + +ATF_TC_WITHOUT_HEAD(infinities_and_nans); +ATF_TC_BODY(infinities_and_nans, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + testfmt("nan", "%e", NAN); + testfmt("NAN", "%F", NAN); + testfmt("nan", "%g", NAN); + testfmt("NAN", "%LE", (long double)NAN); + testfmt(" nan", "%05e", NAN); + + testfmt("INF", "%E", HUGE_VAL); + testfmt("-inf", "%f", -HUGE_VAL); + testfmt("+inf", "%+g", HUGE_VAL); + testfmt(" inf", "%4.2Le", HUGE_VALL); + testfmt("-inf", "%Lf", -HUGE_VALL); + testfmt(" inf", "%05e", HUGE_VAL); + testfmt(" -inf", "%05e", -HUGE_VAL); +} + +ATF_TC_WITHOUT_HEAD(padding); +ATF_TC_BODY(padding, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + testfmt("0.000000e+00", "%e", 0.0); + testfmt("0.000000", "%F", (double)0.0); + testfmt("0", "%G", 0.0); + testfmt(" 0", "%3.0Lg", 0.0L); + testfmt(" 0", "%5.0f", 0.001); +} + +ATF_TC_WITHOUT_HEAD(precision_specifiers); +ATF_TC_BODY(precision_specifiers, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + testfmt("1.0123e+00", "%.4e", 1.0123456789); + testfmt("1.0123", "%.4f", 1.0123456789); + testfmt("1.012", "%.4g", 1.0123456789); + testfmt("1.2346e-02", "%.4e", 0.0123456789); + testfmt("0.0123", "%.4f", 0.0123456789); + testfmt("0.01235", "%.4g", 0.0123456789); +} + +ATF_TC_WITHOUT_HEAD(thousands_separator_and_other_locale_tests); +ATF_TC_BODY(thousands_separator_and_other_locale_tests, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + testfmt("12345678.0625", "%'.04f", 12345678.0625); + testfmt("0012345678.0625", "%'015.4F", 12345678.0625); + + ATF_REQUIRE(setlocale(LC_NUMERIC, "hi_IN.ISCII-DEV")); /* grouping == 2;3 */ + testfmt("1,23,45,678.0625", "%'.4f", 12345678.0625); + testfmt("01,23,45,678.0625", "%'017.4F", 12345678.0625); + testfmt(" 9,000", "%'6.0f", 9000.0); + testfmt("9,000.0", "%'.1f", 9000.0); + + ATF_REQUIRE(setlocale(LC_NUMERIC, "ru_RU.ISO8859-5")); /* decimalpoint==, */ + testfmt("3,1415", "%g", 3.1415); + + /* thousands=. decimalpoint=, grouping=3;3 */ + ATF_REQUIRE(setlocale(LC_NUMERIC, "el_GR.ISO8859-7")); /* decimalpoint==, */ + testfmt("1.234,00", "%'.2f", 1234.00); + testfmt("123.456,789", "%'.3f", 123456.789); + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + testfmt("12345678.062500", "%'f", 12345678.0625); + testfmt("9000.000000", "%'f", 9000.0); +} + +ATF_TC_WITHOUT_HEAD(signed_conversions); +ATF_TC_BODY(signed_conversions, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + testfmt("+2.500000e-01", "%+e", 0.25); + testfmt("+0.000000", "%+F", 0.0); + testfmt("-1", "%+g", -1.0); + + testfmt("-1.000000e+00", "% e", -1.0); + testfmt("+1.000000", "% +f", 1.0); + testfmt(" 1", "% g", 1.0); + testfmt(" 0", "% g", 0.0); +} + +ATF_TC_WITHOUT_HEAD(alternate_form); +ATF_TC_BODY(alternate_form, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + testfmt("1.250e+00", "%#.3e", 1.25); + testfmt("123.000000", "%#f", 123.0); + testfmt(" 12345.", "%#7.5g", 12345.0); + testfmt(" 1.00000", "%#8g", 1.0); + testfmt("0.0", "%#.2g", 0.0); +} + +ATF_TC_WITHOUT_HEAD(padding_and_decimal_point_placement); +ATF_TC_BODY(padding_and_decimal_point_placement, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + testfmt("03.2E+00", "%08.1E", 3.25); + testfmt("003.25", "%06.2F", 3.25); + testfmt("0003.25", "%07.4G", 3.25); + + testfmt("3.14159e-05", "%g", 3.14159e-5); + testfmt("0.000314159", "%g", 3.14159e-4); + testfmt("3.14159e+06", "%g", 3.14159e6); + testfmt("314159", "%g", 3.14159e5); + testfmt("314159.", "%#g", 3.14159e5); + + testfmt(" 9.000000e+03", "%13e", 9000.0); + testfmt(" 9000.000000", "%12f", 9000.0); + testfmt(" 9000", "%5g", 9000.0); + testfmt(" 900000.", "%#8g", 900000.0); + testfmt(" 9e+06", "%6g", 9000000.0); + testfmt(" 9.000000e-04", "%13e", 0.0009); + testfmt(" 0.000900", "%9f", 0.0009); + testfmt(" 0.0009", "%7g", 0.0009); + testfmt(" 9e-05", "%6g", 0.00009); + testfmt(" 9.00000e-05", "%#12g", 0.00009); + testfmt(" 9.e-05", "%#7.1g", 0.00009); + + testfmt(" 0.0", "%4.1f", 0.0); + testfmt("90.0", "%4.1f", 90.0); + testfmt(" 100", "%4.0f", 100.0); + testfmt("9.0e+01", "%4.1e", 90.0); + testfmt("1e+02", "%4.0e", 100.0); +} + +ATF_TC_WITHOUT_HEAD(decimal_rounding); +ATF_TC_BODY(decimal_rounding, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + fesetround(FE_DOWNWARD); + testfmt("4.437", "%.3f", 4.4375); + testfmt("-4.438", "%.3f", -4.4375); + testfmt("4.437", "%.3Lf", 4.4375L); + testfmt("-4.438", "%.3Lf", -4.4375L); + + fesetround(FE_UPWARD); + testfmt("4.438", "%.3f", 4.4375); + testfmt("-4.437", "%.3f", -4.4375); + testfmt("4.438", "%.3Lf", 4.4375L); + testfmt("-4.437", "%.3Lf", -4.4375L); + + fesetround(FE_TOWARDZERO); + testfmt("4.437", "%.3f", 4.4375); + testfmt("-4.437", "%.3f", -4.4375); + testfmt("4.437", "%.3Lf", 4.4375L); + testfmt("-4.437", "%.3Lf", -4.4375L); + + fesetround(FE_TONEAREST); + testfmt("4.438", "%.3f", 4.4375); + testfmt("-4.438", "%.3f", -4.4375); + testfmt("4.438", "%.3Lf", 4.4375L); + testfmt("-4.438", "%.3Lf", -4.4375L); +} + +ATF_TC_WITHOUT_HEAD(hexadecimal_floating_point); +ATF_TC_BODY(hexadecimal_floating_point, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + /* + * Hexadecimal floating point (%a, %A) tests. Some of these + * are only valid if the implementation converts to hex digits + * on nibble boundaries. + */ + testfmt("0x0p+0", "%a", 0x0.0p0); + testfmt("0X0.P+0", "%#LA", 0x0.0p0L); + testfmt("inf", "%La", (long double)INFINITY); + testfmt("+INF", "%+A", INFINITY); + testfmt("nan", "%La", (long double)NAN); + testfmt("NAN", "%A", NAN); + + testfmt(" 0x1.23p+0", "%10a", 0x1.23p0); + testfmt(" 0x1.23p-500", "%12a", 0x1.23p-500); + testfmt(" 0x1.2p+40", "%10.1a", 0x1.23p40); + testfmt(" 0X1.230000000000000000000000P-4", "%32.24A", 0x1.23p-4); + testfmt("0x1p-1074", "%a", 0x1p-1074); + testfmt("0x1.2345p-1024", "%a", 0x1.2345p-1024); + +#if (LDBL_MANT_DIG == 64) + testfmt("0x1.921fb54442d18468p+1", "%La", 0x3.243f6a8885a308dp0L); + testfmt("0x1p-16445", "%La", 0x1p-16445L); + testfmt("0x1.30ecap-16381", "%La", 0x9.8765p-16384L); +#elif (LDBL_MANT_DIG == 113) + testfmt("0x1.921fb54442d18469898cc51701b8p+1", "%La", + 0x3.243f6a8885a308d313198a2e037p0L); + testfmt("0x1p-16494", "%La", 0x1p-16494L); + testfmt("0x1.2345p-16384", "%La", 0x1.2345p-16384L); +#else + testfmt("0x1.921fb54442d18p+1", "%La", 0x3.243f6a8885a31p0L); + testfmt("0x1p-1074", "%La", 0x1p-1074L); + testfmt("0x1.30ecap-1021", "%La", 0x9.8765p-1024L); +#endif +} + +ATF_TC_WITHOUT_HEAD(hexadecimal_rounding); +ATF_TC_BODY(hexadecimal_rounding, tc) +{ + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + fesetround(FE_TOWARDZERO); + testfmt("0X1.23456789ABCP+0", "%.11A", 0x1.23456789abcdep0); + testfmt("-0x1.23456p+0", "%.5a", -0x1.23456789abcdep0); + testfmt("0x1.23456p+0", "%.5a", 0x1.23456789abcdep0); + testfmt("0x1.234567p+0", "%.6a", 0x1.23456789abcdep0); + testfmt("-0x1.234566p+0", "%.6a", -0x1.23456689abcdep0); + + fesetround(FE_DOWNWARD); + testfmt("0X1.23456789ABCP+0", "%.11A", 0x1.23456789abcdep0); + testfmt("-0x1.23457p+0", "%.5a", -0x1.23456789abcdep0); + testfmt("0x1.23456p+0", "%.5a", 0x1.23456789abcdep0); + testfmt("0x1.234567p+0", "%.6a", 0x1.23456789abcdep0); + testfmt("-0x1.234567p+0", "%.6a", -0x1.23456689abcdep0); + + fesetround(FE_UPWARD); + testfmt("0X1.23456789ABDP+0", "%.11A", 0x1.23456789abcdep0); + testfmt("-0x1.23456p+0", "%.5a", -0x1.23456789abcdep0); + testfmt("0x1.23457p+0", "%.5a", 0x1.23456789abcdep0); + testfmt("0x1.234568p+0", "%.6a", 0x1.23456789abcdep0); + testfmt("-0x1.234566p+0", "%.6a", -0x1.23456689abcdep0); + + fesetround(FE_TONEAREST); + testfmt("0x1.23456789abcdep+4", "%a", 0x1.23456789abcdep4); + testfmt("0X1.23456789ABDP+0", "%.11A", 0x1.23456789abcdep0); + testfmt("-0x1.23456p+0", "%.5a", -0x1.23456789abcdep0); + testfmt("0x1.23456p+0", "%.5a", 0x1.23456789abcdep0); + testfmt("0x1.234568p+0", "%.6a", 0x1.23456789abcdep0); + testfmt("-0x1.234567p+0", "%.6a", -0x1.23456689abcdep0); + testfmt("0x1.00p-1029", "%.2a", 0x1.fffp-1030); + testfmt("0x1.00p-1026", "%.2a", 0xf.fffp-1030); + testfmt("0x1.83p+0", "%.2a", 1.51); +} + +ATF_TC_WITHOUT_HEAD(subnormal_double); +ATF_TC_BODY(subnormal_double, tc) +{ + /* Regression test for https://bugs.freebsd.org/253847 */ + double positive = __DBL_DENORM_MIN__; + testfmt("4.9406564584124654418e-324", "%20.20g", positive); + testfmt("4.9406564584124654418E-324", "%20.20G", positive); + testfmt("0x1p-1074", "%a", positive); + testfmt("0X1P-1074", "%A", positive); + double negative = -__DBL_DENORM_MIN__; + testfmt("-4.9406564584124654418e-324", "%20.20g", negative); + testfmt("-4.9406564584124654418E-324", "%20.20G", negative); + testfmt("-0x1p-1074", "%a", negative); + testfmt("-0X1P-1074", "%A", negative); +} + +ATF_TC_WITHOUT_HEAD(subnormal_float); +ATF_TC_BODY(subnormal_float, tc) +{ + float positive = __FLT_DENORM_MIN__; + testfmt("1.4012984643248170709e-45", "%20.20g", positive); + testfmt("1.4012984643248170709E-45", "%20.20G", positive); + testfmt("0x1p-149", "%a", positive); + testfmt("0X1P-149", "%A", positive); + float negative = -__FLT_DENORM_MIN__; + testfmt("-1.4012984643248170709e-45", "%20.20g", negative); + testfmt("-1.4012984643248170709E-45", "%20.20G", negative); + testfmt("-0x1p-149", "%a", negative); + testfmt("-0X1P-149", "%A", negative); +} + +ATF_TC_WITHOUT_HEAD(hexadecimal_rounding_fullprec); +ATF_TC_BODY(hexadecimal_rounding_fullprec, tc) +{ + /* Double: %.13a with binary64 mantissa=53 */ + testfmt("0x1.1234567890bbbp+0", "%.13a", 0x1.1234567890bbbp+0); + +#if defined(__aarch64__) + /* On arm64, long double is IEEE binary128 (mantissa=113) */ + testfmt("0x1.3c0ca428c59fbbbbbbbbbbbbbbbbp+0", "%.28La", 0x1.3c0ca428c59fbbbbbbbbbbbbbbbbp+0L); +#endif +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, float_within_limits); + ATF_TP_ADD_TC(tp, infinities_and_nans); + ATF_TP_ADD_TC(tp, padding); + ATF_TP_ADD_TC(tp, precision_specifiers); + ATF_TP_ADD_TC(tp, thousands_separator_and_other_locale_tests); + ATF_TP_ADD_TC(tp, signed_conversions); + ATF_TP_ADD_TC(tp, alternate_form); + ATF_TP_ADD_TC(tp, padding_and_decimal_point_placement); + ATF_TP_ADD_TC(tp, decimal_rounding); + ATF_TP_ADD_TC(tp, hexadecimal_floating_point); + ATF_TP_ADD_TC(tp, hexadecimal_rounding); + ATF_TP_ADD_TC(tp, subnormal_double); + ATF_TP_ADD_TC(tp, subnormal_float); + ATF_TP_ADD_TC(tp, hexadecimal_rounding_fullprec); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/scanfloat_test.c b/lib/libc/tests/stdio/scanfloat_test.c new file mode 100644 index 000000000000..635d93d560b3 --- /dev/null +++ b/lib/libc/tests/stdio/scanfloat_test.c @@ -0,0 +1,313 @@ +/*- + * Copyright (C) 2003, 2005 David Schultz <das@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 for scanf() floating point formats. + */ + +#include <fenv.h> +#include <float.h> +#include <locale.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +#define eq(type, a, b) _eq(type##_EPSILON, (a), (b)) +static int +_eq(long double epsilon, long double a, long double b) +{ + long double delta; + + delta = fabsl(a - b); + return (delta <= epsilon); +} + +ATF_TC_WITHOUT_HEAD(normalized_numbers); +ATF_TC_BODY(normalized_numbers, tc) +{ + char buf[128]; + long double ld = 0.0; + double d = 0.0; + float f = 0.0; + + buf[0] = '\0'; + ATF_REQUIRE(setlocale(LC_NUMERIC, "")); + + ATF_REQUIRE_EQ(1, sscanf("3.141592", "%e", &f)); + ATF_REQUIRE(eq(FLT, f, 3.141592)); + + ATF_REQUIRE_EQ(1, sscanf("3.141592653589793", "%lf", &d)); + ATF_REQUIRE(eq(DBL, d, 3.141592653589793)); + + ATF_REQUIRE_EQ(1, sscanf("1.234568e+06", "%E", &f)); + ATF_REQUIRE(eq(FLT, f, 1.234568e+06)); + + ATF_REQUIRE_EQ(1, sscanf("-1.234568e6", "%lF", &d)); + ATF_REQUIRE(eq(DBL, d, -1.234568e6)); + + ATF_REQUIRE_EQ(1, sscanf("+1.234568e-52", "%LG", &ld)); + ATF_REQUIRE(eq(LDBL, ld, 1.234568e-52L)); + + ATF_REQUIRE_EQ(1, sscanf("0.1", "%la", &d)); + ATF_REQUIRE(eq(DBL, d, 0.1)); + + ATF_REQUIRE_EQ(1, sscanf("00.2", "%lA", &d)); + ATF_REQUIRE(eq(DBL, d, 0.2)); + + ATF_REQUIRE_EQ(2, sscanf("123456", "%5le%s", &d, buf)); + ATF_REQUIRE(eq(DBL, d, 12345.)); + ATF_REQUIRE(strcmp(buf, "6") == 0); + + ATF_REQUIRE_EQ(1, sscanf("1.0Q", "%*5le%s", buf)); + ATF_REQUIRE(strcmp(buf, "Q") == 0); + + ATF_REQUIRE_EQ(2, sscanf("-1.23e", "%e%s", &f, buf)); + ATF_REQUIRE(eq(FLT, f, -1.23)); + ATF_REQUIRE(strcmp(buf, "e") == 0); + + ATF_REQUIRE_EQ(2, sscanf("1.25e+", "%le%s", &d, buf)); + ATF_REQUIRE(eq(DBL, d, 1.25)); + ATF_REQUIRE(strcmp(buf, "e+") == 0); + + ATF_REQUIRE_EQ(2, sscanf("1.23E4E5", "%le%s", &d, buf)); + ATF_REQUIRE(eq(DBL, d, 1.23e4)); + ATF_REQUIRE(strcmp(buf, "E5") == 0); + + ATF_REQUIRE_EQ(1, sscanf("12e6", "%le", &d)); + ATF_REQUIRE(eq(DBL, d, 12e6)); + + ATF_REQUIRE_EQ(2, sscanf("1.a", "%le%s", &d, buf)); + ATF_REQUIRE(eq(DBL, d, 1.0)); + ATF_REQUIRE(strcmp(buf, "a") == 0); + + ATF_REQUIRE_EQ(2, sscanf(".0p4", "%le%s", &d, buf)); + ATF_REQUIRE(eq(DBL, d, 0.0)); + ATF_REQUIRE(strcmp(buf, "p4") == 0); + + d = 0.25; + ATF_REQUIRE_EQ(0, sscanf(".", "%le", &d)); + ATF_REQUIRE(d == 0.25); + + ATF_REQUIRE_EQ(1, sscanf("0x08", "%le", &d)); + ATF_REQUIRE(d == 0x8p0); + + ATF_REQUIRE_EQ(2, sscanf("0x90a.bcdefP+09a", "%le%s", &d, buf)); + ATF_REQUIRE(d == 0x90a.bcdefp+09); + ATF_REQUIRE(strcmp(buf, "a") == 0); + +#if (LDBL_MANT_DIG > DBL_MANT_DIG) && !defined(__i386__) + ATF_REQUIRE_EQ(1, sscanf("3.14159265358979323846", "%Lg", &ld)); + ATF_REQUIRE(eq(LDBL, ld, 3.14159265358979323846L)); + + ATF_REQUIRE_EQ(2, sscanf(" 0X.0123456789abcdefffp-3g", "%Le%s", &ld, buf)); + ATF_REQUIRE(ld == 0x0.0123456789abcdefffp-3L); + ATF_REQUIRE(strcmp(buf, "g") == 0); +#endif + + ATF_REQUIRE_EQ(2, sscanf("0xg", "%le%s", &d, buf)); + ATF_REQUIRE(d == 0.0); + ATF_REQUIRE(strcmp(buf, "xg") == 0); + + ATF_REQUIRE(setlocale(LC_NUMERIC, "ru_RU.ISO8859-5")); /* decimalpoint==, */ + + ATF_REQUIRE_EQ(2, sscanf("1.23", "%le%s", &d, buf)); + ATF_REQUIRE(d == 1.0); + ATF_REQUIRE(strcmp(buf, ".23") == 0); + + ATF_REQUIRE_EQ(1, sscanf("1,23", "%le", &d)); + ATF_REQUIRE(d == 1.23); + + ATF_REQUIRE(setlocale(LC_NUMERIC, "")); +} + +ATF_TC_WITHOUT_HEAD(infinities_and_nans); +ATF_TC_BODY(infinities_and_nans, tc) +{ + char buf[128]; + long double ld = 0.0; + double d = 0.0; + float f = 0.0; + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + ATF_REQUIRE_EQ(1, sscanf("-Inf", "%le", &d)); + ATF_REQUIRE(d < 0.0 && isinf(d)); + + ATF_REQUIRE_EQ(2, sscanf("iNfInItY and beyond", "%le%s", &d, buf)); + ATF_REQUIRE(d > 0.0 && isinf(d)); + ATF_REQUIRE(strcmp(buf, " and beyond")); + + ATF_REQUIRE_EQ(1, sscanf("NaN", "%le", &d)); + ATF_REQUIRE(isnan(d)); + + ATF_REQUIRE_EQ(2, sscanf("NAN(123Y", "%le%s", &d, buf)); + ATF_REQUIRE(isnan(d)); + ATF_REQUIRE(strcmp(buf, "(123Y") == 0); + + ATF_REQUIRE_EQ(2, sscanf("nan(f00f)plugh", "%le%s", &d, buf)); + ATF_REQUIRE(isnan(d)); + ATF_REQUIRE(strcmp(buf, "plugh") == 0); + + ATF_REQUIRE_EQ(1, sscanf("-nan", "%le", &d)); + ATF_REQUIRE(isnan(d)); + + /* Only quiet NaNs should be returned. */ + ATF_REQUIRE_EQ(1, sscanf("NaN", "%e", &f)); + ATF_REQUIRE_EQ(1, sscanf("nan", "%le", &d)); + ATF_REQUIRE_EQ(1, sscanf("nan", "%Le", &ld)); + ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT)); + ATF_REQUIRE(f != f); + ATF_REQUIRE(d != d); + ATF_REQUIRE(ld != ld); + ATF_REQUIRE(fetestexcept(FE_INVALID) == 0); + ATF_REQUIRE_EQ(1, sscanf("nan(1234)", "%e", &f)); + ATF_REQUIRE_EQ(1, sscanf("nan(1234)", "%le", &d)); + ATF_REQUIRE_EQ(1, sscanf("nan(1234)", "%Le", &ld)); + ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT)); + ATF_REQUIRE(f != f); + ATF_REQUIRE(d != d); + ATF_REQUIRE(ld != ld); + /* POSIX says we should only generate quiet NaNs. */ + ATF_REQUIRE(fetestexcept(FE_INVALID) == 0); +} + +ATF_TC_WITHOUT_HEAD(rounding_tests); +ATF_TC_BODY(rounding_tests, tc) +{ + long double ld = 0.0; + double d = 0.0; + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + ATF_REQUIRE_EQ(0, fesetround(FE_DOWNWARD)); + + ATF_REQUIRE_EQ(1, sscanf("1.999999999999999999999999999999999", "%le", &d)); + ATF_REQUIRE(d < 2.0); + ATF_REQUIRE_EQ(1, sscanf("0x1.ffffffffffffffp0", "%le", &d)); + ATF_REQUIRE(d < 2.0); + ATF_REQUIRE_EQ(1, sscanf("1.999999999999999999999999999999999", "%Le", &ld)); + ATF_REQUIRE(ld < 2.0); + + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d)); + ATF_REQUIRE(d == 0x1.0ea3f4af0dc59p0); + ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d)); + ATF_REQUIRE(d == -0x1.0ea3f4af0dc5ap0); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d)); + ATF_REQUIRE(d == 0x1.0ea3f4af0dc5ap0); + + ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d)); + ATF_REQUIRE(d == 0.0); + + ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d)); + ATF_REQUIRE(d == 0x1.234567p-1050); + + ATF_REQUIRE_EQ(0, fesetround(FE_UPWARD)); + + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d)); + ATF_REQUIRE(d == 0x1.0ea3f4af0dc5ap0); + ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d)); + ATF_REQUIRE(d == -0x1.0ea3f4af0dc59p0); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d)); + ATF_REQUIRE(d == 0x1.0ea3f4af0dc5bp0); + + ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d)); + ATF_REQUIRE(d == 0x1p-1074); + + ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d)); + ATF_REQUIRE(d == 0x1.234568p-1050); + + ATF_REQUIRE_EQ(0, fesetround(FE_TOWARDZERO)); + + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d)); + ATF_REQUIRE(d == 0x1.0ea3f4af0dc59p0); + ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d)); + ATF_REQUIRE(d == -0x1.0ea3f4af0dc59p0); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d)); + ATF_REQUIRE(d == 0x1.0ea3f4af0dc5ap0); + + ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d)); + ATF_REQUIRE(d == 0.0); + + ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d)); + ATF_REQUIRE(d == 0x1.234567p-1050); + + ATF_REQUIRE_EQ(0, fesetround(FE_TONEAREST)); + + /* 1.0571892669084007 is slightly closer to 0x1.0ea3f4af0dc59p0 */ + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d)); + ATF_REQUIRE(d == 0x1.0ea3f4af0dc59p0); + ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d)); + ATF_REQUIRE(d == -0x1.0ea3f4af0dc59p0); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d)); + ATF_REQUIRE(d == 0x1.0ea3f4af0dc5bp0); + + /* strtod() should round small numbers to 0. */ + ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d)); + ATF_REQUIRE(d == 0.0); + + /* Extra digits in a denormal shouldn't break anything. */ + ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d)); + ATF_REQUIRE(d == 0x1.234568p-1050); +} + +ATF_TC_WITHOUT_HEAD(strtod); +ATF_TC_BODY(strtod, tc) +{ + char *endp; + + ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); + + ATF_REQUIRE(strtod("0xy", &endp) == 0); + ATF_REQUIRE(strcmp("xy", endp) == 0); + + /* This used to cause an infinite loop and round the wrong way. */ + ATF_REQUIRE_EQ(0, fesetround(FE_DOWNWARD)); + ATF_REQUIRE(strtof("3.5e38", &endp) == FLT_MAX); + ATF_REQUIRE(strtod("2e308", &endp) == DBL_MAX); + ATF_REQUIRE_EQ(0, fesetround(FE_UPWARD)); + ATF_REQUIRE(strtof("3.5e38", &endp) == INFINITY); + ATF_REQUIRE(strtod("2e308", &endp) == INFINITY); + ATF_REQUIRE_EQ(0, fesetround(FE_TOWARDZERO)); + ATF_REQUIRE(strtof("3.5e38", &endp) == FLT_MAX); + ATF_REQUIRE(strtod("2e308", &endp) == DBL_MAX); + ATF_REQUIRE_EQ(0, fesetround(FE_TONEAREST)); + ATF_REQUIRE(strtof("3.5e38", &endp) == INFINITY); + ATF_REQUIRE(strtod("2e308", &endp) == INFINITY); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, normalized_numbers); + ATF_TP_ADD_TC(tp, infinities_and_nans); + ATF_TP_ADD_TC(tp, rounding_tests); + ATF_TP_ADD_TC(tp, strtod); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/snprintf_test.c b/lib/libc/tests/stdio/snprintf_test.c new file mode 100644 index 000000000000..29b908b65120 --- /dev/null +++ b/lib/libc/tests/stdio/snprintf_test.c @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 2023 Dag-Erling Smørgrav + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <assert.h> +#include <limits.h> +#include <locale.h> +#include <stdint.h> +#include <stdio.h> + +#include <atf-c.h> + +#ifndef nitems +#define nitems(a) (sizeof(a) / sizeof(a[0])) +#endif + +#define SNPRINTF_TEST(output, format, ...) \ + do { \ + char buf[256]; \ + assert(strlen(output) < nitems(buf)); \ + int ret = snprintf(buf, nitems(buf), format, \ + __VA_ARGS__); \ + ATF_CHECK_EQ(strlen(output), ret); \ + if (ret > 0) { \ + ATF_CHECK_EQ(0, strcmp(output, buf)); \ + } \ + } while (0) + +ATF_TC_WITHOUT_HEAD(snprintf_b); +ATF_TC_BODY(snprintf_b, tc) +{ + SNPRINTF_TEST("0", "%b", 0); + SNPRINTF_TEST(" 0", "%12b", 0); + SNPRINTF_TEST("000000000000", "%012b", 0); + SNPRINTF_TEST("1", "%b", 1); + SNPRINTF_TEST(" 1", "%12b", 1); + SNPRINTF_TEST("000000000001", "%012b", 1); + SNPRINTF_TEST("1111111111111111111111111111111", "%b", INT_MAX); + SNPRINTF_TEST("0", "%#b", 0); + SNPRINTF_TEST(" 0", "%#12b", 0); + SNPRINTF_TEST("000000000000", "%#012b", 0); + SNPRINTF_TEST("0b1", "%#b", 1); + SNPRINTF_TEST(" 0b1", "%#12b", 1); + SNPRINTF_TEST("0b0000000001", "%#012b", 1); + SNPRINTF_TEST("0b1111111111111111111111111111111", "%#b", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(snprintf_B); +ATF_TC_BODY(snprintf_B, tc) +{ + SNPRINTF_TEST("0", "%B", 0); + SNPRINTF_TEST(" 0", "%12B", 0); + SNPRINTF_TEST("000000000000", "%012B", 0); + SNPRINTF_TEST("1", "%B", 1); + SNPRINTF_TEST(" 1", "%12B", 1); + SNPRINTF_TEST("000000000001", "%012B", 1); + SNPRINTF_TEST("1111111111111111111111111111111", "%B", INT_MAX); + SNPRINTF_TEST("0", "%#B", 0); + SNPRINTF_TEST(" 0", "%#12B", 0); + SNPRINTF_TEST("000000000000", "%#012B", 0); + SNPRINTF_TEST("0B1", "%#B", 1); + SNPRINTF_TEST(" 0B1", "%#12B", 1); + SNPRINTF_TEST("0B0000000001", "%#012B", 1); + SNPRINTF_TEST("0B1111111111111111111111111111111", "%#B", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(snprintf_d); +ATF_TC_BODY(snprintf_d, tc) +{ + SNPRINTF_TEST("0", "%d", 0); + SNPRINTF_TEST(" 0", "%12d", 0); + SNPRINTF_TEST("000000000000", "%012d", 0); + SNPRINTF_TEST("1", "%d", 1); + SNPRINTF_TEST(" 1", "%12d", 1); + SNPRINTF_TEST("000000000001", "%012d", 1); + SNPRINTF_TEST("2147483647", "%d", INT_MAX); + SNPRINTF_TEST(" 2147483647", "%12d", INT_MAX); + SNPRINTF_TEST("002147483647", "%012d", INT_MAX); + SNPRINTF_TEST("2,147,483,647", "%'d", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(snprintf_x); +ATF_TC_BODY(snprintf_x, tc) +{ + SNPRINTF_TEST("0", "%x", 0); + SNPRINTF_TEST(" 0", "%12x", 0); + SNPRINTF_TEST("000000000000", "%012x", 0); + SNPRINTF_TEST("1", "%x", 1); + SNPRINTF_TEST(" 1", "%12x", 1); + SNPRINTF_TEST("000000000001", "%012x", 1); + SNPRINTF_TEST("7fffffff", "%x", INT_MAX); + SNPRINTF_TEST(" 7fffffff", "%12x", INT_MAX); + SNPRINTF_TEST("00007fffffff", "%012x", INT_MAX); + SNPRINTF_TEST("0", "%#x", 0); + SNPRINTF_TEST(" 0", "%#12x", 0); + SNPRINTF_TEST("000000000000", "%#012x", 0); + SNPRINTF_TEST("0x1", "%#x", 1); + SNPRINTF_TEST(" 0x1", "%#12x", 1); + SNPRINTF_TEST("0x0000000001", "%#012x", 1); + SNPRINTF_TEST("0x7fffffff", "%#x", INT_MAX); + SNPRINTF_TEST(" 0x7fffffff", "%#12x", INT_MAX); + SNPRINTF_TEST("0x007fffffff", "%#012x", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(snprintf_X); +ATF_TC_BODY(snprintf_X, tc) +{ + SNPRINTF_TEST("0", "%X", 0); + SNPRINTF_TEST(" 0", "%12X", 0); + SNPRINTF_TEST("000000000000", "%012X", 0); + SNPRINTF_TEST("1", "%X", 1); + SNPRINTF_TEST(" 1", "%12X", 1); + SNPRINTF_TEST("000000000001", "%012X", 1); + SNPRINTF_TEST("7FFFFFFF", "%X", INT_MAX); + SNPRINTF_TEST(" 7FFFFFFF", "%12X", INT_MAX); + SNPRINTF_TEST("00007FFFFFFF", "%012X", INT_MAX); + SNPRINTF_TEST("0", "%#X", 0); + SNPRINTF_TEST(" 0", "%#12X", 0); + SNPRINTF_TEST("000000000000", "%#012X", 0); + SNPRINTF_TEST("0X1", "%#X", 1); + SNPRINTF_TEST(" 0X1", "%#12X", 1); + SNPRINTF_TEST("0X0000000001", "%#012X", 1); + SNPRINTF_TEST("0X7FFFFFFF", "%#X", INT_MAX); + SNPRINTF_TEST(" 0X7FFFFFFF", "%#12X", INT_MAX); + SNPRINTF_TEST("0X007FFFFFFF", "%#012X", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(snprintf_wN); +ATF_TC_BODY(snprintf_wN, tc) +{ + SNPRINTF_TEST("0", "%w8d", (int8_t)0); + SNPRINTF_TEST("-128", "%w8d", (int8_t)SCHAR_MIN); + SNPRINTF_TEST("127", "%w8d", (int8_t)SCHAR_MAX); + SNPRINTF_TEST("0", "%w8u", (uint8_t)0); + SNPRINTF_TEST("255", "%w8u", (uint8_t)UCHAR_MAX); + + SNPRINTF_TEST("0", "%w16d", (int16_t)0); + SNPRINTF_TEST("-32768", "%w16d", (int16_t)SHRT_MIN); + SNPRINTF_TEST("32767", "%w16d", (int16_t)SHRT_MAX); + SNPRINTF_TEST("0", "%w16u", (uint16_t)0); + SNPRINTF_TEST("65535", "%w16u", (uint16_t)USHRT_MAX); + + SNPRINTF_TEST("0", "%w32d", (int32_t)0); + SNPRINTF_TEST("-2147483648", "%w32d", (int32_t)INT_MIN); + SNPRINTF_TEST("2147483647", "%w32d", (int32_t)INT_MAX); + SNPRINTF_TEST("0", "%w32u", (uint32_t)0); + SNPRINTF_TEST("4294967295", "%w32u", (uint32_t)UINT_MAX); + + SNPRINTF_TEST("0", "%w64d", (int64_t)0); + SNPRINTF_TEST("-9223372036854775808", "%w64d", (int64_t)LLONG_MIN); + SNPRINTF_TEST("9223372036854775807", "%w64d", (int64_t)LLONG_MAX); + SNPRINTF_TEST("0", "%w64u", (uint64_t)0); + SNPRINTF_TEST("18446744073709551615", "%w64u", (uint64_t)ULLONG_MAX); + + SNPRINTF_TEST("wd", "%wd", 0); + SNPRINTF_TEST("w1d", "%w1d", 0); + SNPRINTF_TEST("w128d", "%w128d", 0); +} + +ATF_TC_WITHOUT_HEAD(snprintf_wfN); +ATF_TC_BODY(snprintf_wfN, tc) +{ + SNPRINTF_TEST("0", "%wf8d", (int_fast8_t)0); + SNPRINTF_TEST("-2147483648", "%wf8d", (int_fast8_t)INT_MIN); + SNPRINTF_TEST("2147483647", "%wf8d", (int_fast8_t)INT_MAX); + SNPRINTF_TEST("0", "%wf8u", (uint8_t)0); + SNPRINTF_TEST("4294967295", "%wf8u", (uint_fast8_t)UINT_MAX); + + SNPRINTF_TEST("0", "%wf16d", (int_fast16_t)0); + SNPRINTF_TEST("-2147483648", "%wf16d", (int_fast16_t)INT_MIN); + SNPRINTF_TEST("2147483647", "%wf16d", (int_fast16_t)INT_MAX); + SNPRINTF_TEST("0", "%wf16u", (uint16_t)0); + SNPRINTF_TEST("4294967295", "%wf16u", (uint_fast16_t)UINT_MAX); + + SNPRINTF_TEST("0", "%wf32d", (int_fast32_t)0); + SNPRINTF_TEST("-2147483648", "%wf32d", (int_fast32_t)INT_MIN); + SNPRINTF_TEST("2147483647", "%wf32d", (int_fast32_t)INT_MAX); + SNPRINTF_TEST("0", "%wf32u", (uint32_t)0); + SNPRINTF_TEST("4294967295", "%wf32u", (uint_fast32_t)UINT_MAX); + + SNPRINTF_TEST("0", "%wf64d", (int_fast64_t)0); + SNPRINTF_TEST("-9223372036854775808", "%wf64d", (int_fast64_t)LLONG_MIN); + SNPRINTF_TEST("9223372036854775807", "%wf64d", (int_fast64_t)LLONG_MAX); + SNPRINTF_TEST("0", "%wf64u", (uint64_t)0); + SNPRINTF_TEST("18446744073709551615", "%wf64u", (uint_fast64_t)ULLONG_MAX); + + SNPRINTF_TEST("wfd", "%wfd", 0); + SNPRINTF_TEST("wf1d", "%wf1d", 0); + SNPRINTF_TEST("wf128d", "%wf128d", 0); +} + +ATF_TP_ADD_TCS(tp) +{ + setlocale(LC_NUMERIC, "en_US.UTF-8"); + ATF_TP_ADD_TC(tp, snprintf_b); + ATF_TP_ADD_TC(tp, snprintf_B); + ATF_TP_ADD_TC(tp, snprintf_d); + ATF_TP_ADD_TC(tp, snprintf_x); + ATF_TP_ADD_TC(tp, snprintf_X); + ATF_TP_ADD_TC(tp, snprintf_wN); + ATF_TP_ADD_TC(tp, snprintf_wfN); + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/sscanf_test.c b/lib/libc/tests/stdio/sscanf_test.c new file mode 100644 index 000000000000..e43292820eeb --- /dev/null +++ b/lib/libc/tests/stdio/sscanf_test.c @@ -0,0 +1,356 @@ +/*- + * Copyright (c) 2023 Dag-Erling Smørgrav + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <limits.h> +#include <locale.h> +#include <stdint.h> +#include <stdio.h> + +#include <atf-c.h> + +static const struct sscanf_test_case { + char input[8]; + struct { + int ret, val, len; + } b, o, d, x, i; +} sscanf_test_cases[] = { +// input binary octal decimal hexadecimal automatic + // all digits + { "0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + { "1", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, }, + { "2", { 0, 0, 0 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, }, + { "3", { 0, 0, 0 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, }, + { "4", { 0, 0, 0 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, }, + { "5", { 0, 0, 0 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, }, + { "6", { 0, 0, 0 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, }, + { "7", { 0, 0, 0 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, }, + { "8", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 8, 1 }, { 1, 8, 1 }, { 1, 8, 1 }, }, + { "9", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 9, 1 }, { 1, 9, 1 }, { 1, 9, 1 }, }, + { "A", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, }, + { "B", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, }, + { "C", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, }, + { "D", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, }, + { "E", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, }, + { "F", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, }, + { "X", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, }, + { "a", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, }, + { "b", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, }, + { "c", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, }, + { "d", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, }, + { "e", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, }, + { "f", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, }, + { "x", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, }, + // all digits with leading zero + { "00", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, + { "01", { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, }, + { "02", { 1, 0, 1 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, }, + { "03", { 1, 0, 1 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, }, + { "04", { 1, 0, 1 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, }, + { "05", { 1, 0, 1 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, }, + { "06", { 1, 0, 1 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, }, + { "07", { 1, 0, 1 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, }, + { "08", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 2 }, { 1, 8, 2 }, { 1, 0, 1 }, }, + { "09", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 2 }, { 1, 9, 2 }, { 1, 0, 1 }, }, + { "0A", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, }, + { "0B", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, }, + { "0C", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, }, + { "0D", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, }, + { "0E", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, }, + { "0F", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, }, + { "0X", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + { "0a", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, }, + { "0b", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, }, + { "0c", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, }, + { "0d", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, }, + { "0e", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, }, + { "0f", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, }, + { "0x", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + // all digits with two leading zeroes + { "000", { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, }, + { "001", { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, }, + { "002", { 1, 0, 2 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, }, + { "003", { 1, 0, 2 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, }, + { "004", { 1, 0, 2 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, }, + { "005", { 1, 0, 2 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, }, + { "006", { 1, 0, 2 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, }, + { "007", { 1, 0, 2 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, }, + { "008", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 8, 3 }, { 1, 8, 3 }, { 1, 0, 2 }, }, + { "009", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 9, 3 }, { 1, 9, 3 }, { 1, 0, 2 }, }, + { "00A", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, }, + { "00B", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, }, + { "00C", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, }, + { "00D", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, }, + { "00E", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, }, + { "00F", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, }, + { "00X", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, + { "00a", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, }, + { "00b", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, }, + { "00c", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, }, + { "00d", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, }, + { "00e", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, }, + { "00f", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, }, + { "00x", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, + // all digits with leading one + { "10", { 1, 2, 2 }, { 1, 8, 2 }, { 1, 10, 2 }, { 1, 16, 2 }, { 1, 10, 2 }, }, + { "11", { 1, 3, 2 }, { 1, 9, 2 }, { 1, 11, 2 }, { 1, 17, 2 }, { 1, 11, 2 }, }, + { "12", { 1, 1, 1 }, { 1, 10, 2 }, { 1, 12, 2 }, { 1, 18, 2 }, { 1, 12, 2 }, }, + { "13", { 1, 1, 1 }, { 1, 11, 2 }, { 1, 13, 2 }, { 1, 19, 2 }, { 1, 13, 2 }, }, + { "14", { 1, 1, 1 }, { 1, 12, 2 }, { 1, 14, 2 }, { 1, 20, 2 }, { 1, 14, 2 }, }, + { "15", { 1, 1, 1 }, { 1, 13, 2 }, { 1, 15, 2 }, { 1, 21, 2 }, { 1, 15, 2 }, }, + { "16", { 1, 1, 1 }, { 1, 14, 2 }, { 1, 16, 2 }, { 1, 22, 2 }, { 1, 16, 2 }, }, + { "17", { 1, 1, 1 }, { 1, 15, 2 }, { 1, 17, 2 }, { 1, 23, 2 }, { 1, 17, 2 }, }, + { "18", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 18, 2 }, { 1, 24, 2 }, { 1, 18, 2 }, }, + { "19", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 19, 2 }, { 1, 25, 2 }, { 1, 19, 2 }, }, + { "1A", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, }, + { "1B", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, }, + { "1C", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, }, + { "1D", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, }, + { "1E", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, }, + { "1F", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, }, + { "1X", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, }, + { "1a", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, }, + { "1b", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, }, + { "1c", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, }, + { "1d", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, }, + { "1e", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, }, + { "1f", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, }, + { "1x", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, }, + // all digits with leading binary prefix + { "0b0", { 1, 0, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 176, 3 }, { 1, 0, 3 }, }, + { "0b1", { 1, 1, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 177, 3 }, { 1, 1, 3 }, }, + { "0b2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 178, 3 }, { 1, 0, 1 }, }, + { "0b3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 179, 3 }, { 1, 0, 1 }, }, + { "0b4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 180, 3 }, { 1, 0, 1 }, }, + { "0b5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 181, 3 }, { 1, 0, 1 }, }, + { "0b6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 182, 3 }, { 1, 0, 1 }, }, + { "0b7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 183, 3 }, { 1, 0, 1 }, }, + { "0b8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 184, 3 }, { 1, 0, 1 }, }, + { "0b9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 185, 3 }, { 1, 0, 1 }, }, + { "0bA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, }, + { "0bB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, }, + { "0bC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, }, + { "0bD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, }, + { "0bE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, }, + { "0bF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, }, + { "0bX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, }, + { "0ba", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, }, + { "0bb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, }, + { "0bc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, }, + { "0bd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, }, + { "0be", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, }, + { "0bf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, }, + { "0bx", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, }, + // all digits with leading hexadecimal prefix + { "0x0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 3 }, { 1, 0, 3 }, }, + { "0x1", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 1, 3 }, { 1, 1, 3 }, }, + { "0x2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, }, + { "0x3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 3, 3 }, { 1, 3, 3 }, }, + { "0x4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 4, 3 }, { 1, 4, 3 }, }, + { "0x5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 5, 3 }, { 1, 5, 3 }, }, + { "0x6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 6, 3 }, { 1, 6, 3 }, }, + { "0x7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 7, 3 }, { 1, 7, 3 }, }, + { "0x8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 3 }, { 1, 8, 3 }, }, + { "0x9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 3 }, { 1, 9, 3 }, }, + { "0xA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, }, + { "0xB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, }, + { "0xC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, }, + { "0xD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, }, + { "0xE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, }, + { "0xF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, }, + { "0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + { "0xa", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, }, + { "0xb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, }, + { "0xc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, }, + { "0xd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, }, + { "0xe", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, }, + { "0xf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, }, + { "0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + // terminator + { "" } +}; + +#define SSCANF_TEST(string, format, expret, expval, explen) \ + do { \ + int ret = 0, val = 0, len = 0; \ + ret = sscanf(string, format "%n", &val, &len); \ + ATF_CHECK_EQ(expret, ret); \ + if (expret && ret) { \ + ATF_CHECK_EQ(expval, val); \ + ATF_CHECK_EQ(explen, len); \ + } \ + } while (0) + +ATF_TC_WITHOUT_HEAD(sscanf_b); +ATF_TC_BODY(sscanf_b, tc) +{ + const struct sscanf_test_case *stc; + char input[16]; + + for (stc = sscanf_test_cases; *stc->input; stc++) { + strcpy(input + 1, stc->input); + SSCANF_TEST(input + 1, "%b", stc->b.ret, stc->b.val, stc->b.len); + input[0] = '+'; + SSCANF_TEST(input, "%b", stc->b.ret, stc->b.val, stc->b.len ? stc->b.len + 1 : 0); + input[0] = '-'; + SSCANF_TEST(input, "%b", stc->b.ret, -stc->b.val, stc->b.len ? stc->b.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(sscanf_o); +ATF_TC_BODY(sscanf_o, tc) +{ + const struct sscanf_test_case *stc; + char input[16]; + + for (stc = sscanf_test_cases; *stc->input; stc++) { + strcpy(input + 1, stc->input); + SSCANF_TEST(input + 1, "%o", stc->o.ret, stc->o.val, stc->o.len); + input[0] = '+'; + SSCANF_TEST(input, "%o", stc->o.ret, stc->o.val, stc->o.len ? stc->o.len + 1 : 0); + input[0] = '-'; + SSCANF_TEST(input, "%o", stc->o.ret, -stc->o.val, stc->o.len ? stc->o.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(sscanf_d); +ATF_TC_BODY(sscanf_d, tc) +{ + const struct sscanf_test_case *stc; + char input[16]; + + for (stc = sscanf_test_cases; *stc->input; stc++) { + strcpy(input + 1, stc->input); + SSCANF_TEST(input + 1, "%d", stc->d.ret, stc->d.val, stc->d.len); + input[0] = '+'; + SSCANF_TEST(input, "%d", stc->d.ret, stc->d.val, stc->d.len ? stc->d.len + 1 : 0); + input[0] = '-'; + SSCANF_TEST(input, "%d", stc->d.ret, -stc->d.val, stc->d.len ? stc->d.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(sscanf_x); +ATF_TC_BODY(sscanf_x, tc) +{ + const struct sscanf_test_case *stc; + char input[16]; + + for (stc = sscanf_test_cases; *stc->input; stc++) { + strcpy(input + 1, stc->input); + SSCANF_TEST(input + 1, "%x", stc->x.ret, stc->x.val, stc->x.len); + input[0] = '+'; + SSCANF_TEST(input, "%x", stc->x.ret, stc->x.val, stc->x.len ? stc->x.len + 1 : 0); + input[0] = '-'; + SSCANF_TEST(input, "%x", stc->x.ret, -stc->x.val, stc->x.len ? stc->x.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(sscanf_i); +ATF_TC_BODY(sscanf_i, tc) +{ + const struct sscanf_test_case *stc; + char input[16]; + + for (stc = sscanf_test_cases; *stc->input; stc++) { + strcpy(input + 1, stc->input); + SSCANF_TEST(input + 1, "%i", stc->i.ret, stc->i.val, stc->i.len); + input[0] = '+'; + SSCANF_TEST(input, "%i", stc->i.ret, stc->i.val, stc->i.len ? stc->i.len + 1 : 0); + input[0] = '-'; + SSCANF_TEST(input, "%i", stc->i.ret, -stc->i.val, stc->i.len ? stc->i.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(sscanf_wN); +ATF_TC_BODY(sscanf_wN, tc) +{ + const char x00[] = "0x00"; + const char x7f[] = "0x7fffffffffffffff"; + const char xff[] = "0xffffffffffffffff"; + +#define SSCANF_WN_TEST(N, imin, umax) \ + do { \ + int##N##_t i; \ + uint##N##_t u; \ + ATF_CHECK_EQ(1, sscanf(x00, "%w" #N "i", &i)); \ + ATF_CHECK_EQ(0, i); \ + ATF_CHECK_EQ(1, sscanf(x7f, "%w" #N "i", &i)); \ + ATF_CHECK_EQ(imin, i); \ + ATF_CHECK_EQ(1, sscanf(x00, "%w" #N "x", &u)); \ + ATF_CHECK_EQ(0, u); \ + ATF_CHECK_EQ(1, sscanf(xff, "%w" #N "x", &u)); \ + ATF_CHECK_EQ(umax, u); \ + } while (0) + SSCANF_WN_TEST(8, -1, UCHAR_MAX); + SSCANF_WN_TEST(16, -1, USHRT_MAX); + SSCANF_WN_TEST(32, -1, UINT_MAX); + SSCANF_WN_TEST(64, LLONG_MAX, ULLONG_MAX); +#undef SSCANF_WN_TEST + + ATF_CHECK_EQ(0, sscanf(x00, "%wi", (int *)NULL)); + ATF_CHECK_EQ(0, sscanf(x00, "%w1i", (int *)NULL)); + ATF_CHECK_EQ(0, sscanf(x00, "%w128i", (int *)NULL)); +} + +ATF_TC_WITHOUT_HEAD(sscanf_wfN); +ATF_TC_BODY(sscanf_wfN, tc) +{ + const char x00[] = "0x00"; + const char x7f[] = "0x7fffffffffffffff"; + const char xff[] = "0xffffffffffffffff"; + +#define SSCANF_WFN_TEST(N, imin, umax) \ + do { \ + int_fast##N##_t i; \ + uint_fast##N##_t u; \ + ATF_CHECK_EQ(1, sscanf(x00, "%wf" #N "i", &i)); \ + ATF_CHECK_EQ(0, i); \ + ATF_CHECK_EQ(1, sscanf(x7f, "%wf" #N "i", &i)); \ + ATF_CHECK_EQ(imin, i); \ + ATF_CHECK_EQ(1, sscanf(x00, "%wf" #N "x", &u)); \ + ATF_CHECK_EQ(0, u); \ + ATF_CHECK_EQ(1, sscanf(xff, "%wf" #N "x", &u)); \ + ATF_CHECK_EQ(umax, u); \ + } while (0) + SSCANF_WFN_TEST(8, -1, UINT_MAX); + SSCANF_WFN_TEST(16, -1, UINT_MAX); + SSCANF_WFN_TEST(32, -1, UINT_MAX); + SSCANF_WFN_TEST(64, LLONG_MAX, ULLONG_MAX); +#undef SSCANF_WFN_TEST + + ATF_CHECK_EQ(0, sscanf(x00, "%wfi", (int *)NULL)); + ATF_CHECK_EQ(0, sscanf(x00, "%wf1i", (int *)NULL)); + ATF_CHECK_EQ(0, sscanf(x00, "%wf128i", (int *)NULL)); +} + +/* + * Test termination cases: non-numeric character, fixed width, EOF + */ +ATF_TC_WITHOUT_HEAD(sscanf_termination); +ATF_TC_BODY(sscanf_termination, tc) +{ + int a = 0, b = 0, c = 0; + char d = 0; + + ATF_CHECK_EQ(4, sscanf("3.1415", "%d%c%2d%d", &a, &d, &b, &c)); + ATF_CHECK_EQ(3, a); + ATF_CHECK_EQ(14, b); + ATF_CHECK_EQ(15, c); + ATF_CHECK_EQ('.', d); +} + +ATF_TP_ADD_TCS(tp) +{ + setlocale(LC_NUMERIC, "en_US.UTF-8"); + ATF_TP_ADD_TC(tp, sscanf_b); + ATF_TP_ADD_TC(tp, sscanf_o); + ATF_TP_ADD_TC(tp, sscanf_d); + ATF_TP_ADD_TC(tp, sscanf_x); + ATF_TP_ADD_TC(tp, sscanf_i); + ATF_TP_ADD_TC(tp, sscanf_wN); + ATF_TP_ADD_TC(tp, sscanf_wfN); + ATF_TP_ADD_TC(tp, sscanf_termination); + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/swprintf_test.c b/lib/libc/tests/stdio/swprintf_test.c new file mode 100644 index 000000000000..f00ecc7f1c1d --- /dev/null +++ b/lib/libc/tests/stdio/swprintf_test.c @@ -0,0 +1,206 @@ +/*- + * Copyright (c) 2023 Dag-Erling Smørgrav + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <assert.h> +#include <limits.h> +#include <locale.h> +#include <stdint.h> +#include <stdio.h> +#include <wchar.h> + +#include <atf-c.h> + +#ifndef nitems +#define nitems(a) (sizeof(a) / sizeof(a[0])) +#endif + +#define SWPRINTF_TEST(output, format, ...) \ + do { \ + wchar_t buf[256]; \ + assert(wcslen(L##output) < nitems(buf)); \ + int ret = swprintf(buf, nitems(buf), L##format, \ + __VA_ARGS__); \ + ATF_CHECK_EQ(wcslen(L##output), ret); \ + if (ret > 0) { \ + ATF_CHECK_EQ(0, wcscmp(L##output, buf)); \ + } \ + } while (0) + +ATF_TC_WITHOUT_HEAD(swprintf_b); +ATF_TC_BODY(swprintf_b, tc) +{ + SWPRINTF_TEST("0", "%b", 0); + SWPRINTF_TEST(" 0", "%12b", 0); + SWPRINTF_TEST("000000000000", "%012b", 0); + SWPRINTF_TEST("1", "%b", 1); + SWPRINTF_TEST(" 1", "%12b", 1); + SWPRINTF_TEST("000000000001", "%012b", 1); + SWPRINTF_TEST("1111111111111111111111111111111", "%b", INT_MAX); + SWPRINTF_TEST("0", "%#b", 0); + SWPRINTF_TEST(" 0", "%#12b", 0); + SWPRINTF_TEST("000000000000", "%#012b", 0); + SWPRINTF_TEST("0b1", "%#b", 1); + SWPRINTF_TEST(" 0b1", "%#12b", 1); + SWPRINTF_TEST("0b0000000001", "%#012b", 1); + SWPRINTF_TEST("0b1111111111111111111111111111111", "%#b", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(swprintf_B); +ATF_TC_BODY(swprintf_B, tc) +{ + SWPRINTF_TEST("0", "%B", 0); + SWPRINTF_TEST(" 0", "%12B", 0); + SWPRINTF_TEST("000000000000", "%012B", 0); + SWPRINTF_TEST("1", "%B", 1); + SWPRINTF_TEST(" 1", "%12B", 1); + SWPRINTF_TEST("000000000001", "%012B", 1); + SWPRINTF_TEST("1111111111111111111111111111111", "%B", INT_MAX); + SWPRINTF_TEST("0", "%#B", 0); + SWPRINTF_TEST(" 0", "%#12B", 0); + SWPRINTF_TEST("000000000000", "%#012B", 0); + SWPRINTF_TEST("0B1", "%#B", 1); + SWPRINTF_TEST(" 0B1", "%#12B", 1); + SWPRINTF_TEST("0B0000000001", "%#012B", 1); + SWPRINTF_TEST("0B1111111111111111111111111111111", "%#B", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(swprintf_d); +ATF_TC_BODY(swprintf_d, tc) +{ + SWPRINTF_TEST("0", "%d", 0); + SWPRINTF_TEST(" 0", "%12d", 0); + SWPRINTF_TEST("000000000000", "%012d", 0); + SWPRINTF_TEST("1", "%d", 1); + SWPRINTF_TEST(" 1", "%12d", 1); + SWPRINTF_TEST("000000000001", "%012d", 1); + SWPRINTF_TEST("2147483647", "%d", INT_MAX); + SWPRINTF_TEST(" 2147483647", "%12d", INT_MAX); + SWPRINTF_TEST("002147483647", "%012d", INT_MAX); + SWPRINTF_TEST("2,147,483,647", "%'d", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(swprintf_x); +ATF_TC_BODY(swprintf_x, tc) +{ + SWPRINTF_TEST("0", "%x", 0); + SWPRINTF_TEST(" 0", "%12x", 0); + SWPRINTF_TEST("000000000000", "%012x", 0); + SWPRINTF_TEST("1", "%x", 1); + SWPRINTF_TEST(" 1", "%12x", 1); + SWPRINTF_TEST("000000000001", "%012x", 1); + SWPRINTF_TEST("7fffffff", "%x", INT_MAX); + SWPRINTF_TEST(" 7fffffff", "%12x", INT_MAX); + SWPRINTF_TEST("00007fffffff", "%012x", INT_MAX); + SWPRINTF_TEST("0", "%#x", 0); + SWPRINTF_TEST(" 0", "%#12x", 0); + SWPRINTF_TEST("000000000000", "%#012x", 0); + SWPRINTF_TEST("0x1", "%#x", 1); + SWPRINTF_TEST(" 0x1", "%#12x", 1); + SWPRINTF_TEST("0x0000000001", "%#012x", 1); + SWPRINTF_TEST("0x7fffffff", "%#x", INT_MAX); + SWPRINTF_TEST(" 0x7fffffff", "%#12x", INT_MAX); + SWPRINTF_TEST("0x007fffffff", "%#012x", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(swprintf_X); +ATF_TC_BODY(swprintf_X, tc) +{ + SWPRINTF_TEST("0", "%X", 0); + SWPRINTF_TEST(" 0", "%12X", 0); + SWPRINTF_TEST("000000000000", "%012X", 0); + SWPRINTF_TEST("1", "%X", 1); + SWPRINTF_TEST(" 1", "%12X", 1); + SWPRINTF_TEST("000000000001", "%012X", 1); + SWPRINTF_TEST("7FFFFFFF", "%X", INT_MAX); + SWPRINTF_TEST(" 7FFFFFFF", "%12X", INT_MAX); + SWPRINTF_TEST("00007FFFFFFF", "%012X", INT_MAX); + SWPRINTF_TEST("0", "%#X", 0); + SWPRINTF_TEST(" 0", "%#12X", 0); + SWPRINTF_TEST("000000000000", "%#012X", 0); + SWPRINTF_TEST("0X1", "%#X", 1); + SWPRINTF_TEST(" 0X1", "%#12X", 1); + SWPRINTF_TEST("0X0000000001", "%#012X", 1); + SWPRINTF_TEST("0X7FFFFFFF", "%#X", INT_MAX); + SWPRINTF_TEST(" 0X7FFFFFFF", "%#12X", INT_MAX); + SWPRINTF_TEST("0X007FFFFFFF", "%#012X", INT_MAX); +} + +ATF_TC_WITHOUT_HEAD(swprintf_wN); +ATF_TC_BODY(swprintf_wN, tc) +{ + SWPRINTF_TEST("0", "%w8d", (int8_t)0); + SWPRINTF_TEST("-128", "%w8d", (int8_t)SCHAR_MIN); + SWPRINTF_TEST("127", "%w8d", (int8_t)SCHAR_MAX); + SWPRINTF_TEST("0", "%w8u", (uint8_t)0); + SWPRINTF_TEST("255", "%w8u", (uint8_t)UCHAR_MAX); + + SWPRINTF_TEST("0", "%w16d", (int16_t)0); + SWPRINTF_TEST("-32768", "%w16d", (int16_t)SHRT_MIN); + SWPRINTF_TEST("32767", "%w16d", (int16_t)SHRT_MAX); + SWPRINTF_TEST("0", "%w16u", (uint16_t)0); + SWPRINTF_TEST("65535", "%w16u", (uint16_t)USHRT_MAX); + + SWPRINTF_TEST("0", "%w32d", (int32_t)0); + SWPRINTF_TEST("-2147483648", "%w32d", (int32_t)INT_MIN); + SWPRINTF_TEST("2147483647", "%w32d", (int32_t)INT_MAX); + SWPRINTF_TEST("0", "%w32u", (uint32_t)0); + SWPRINTF_TEST("4294967295", "%w32u", (uint32_t)UINT_MAX); + + SWPRINTF_TEST("0", "%w64d", (int64_t)0); + SWPRINTF_TEST("-9223372036854775808", "%w64d", (int64_t)LLONG_MIN); + SWPRINTF_TEST("9223372036854775807", "%w64d", (int64_t)LLONG_MAX); + SWPRINTF_TEST("0", "%w64u", (uint64_t)0); + SWPRINTF_TEST("18446744073709551615", "%w64u", (uint64_t)ULLONG_MAX); + + SWPRINTF_TEST("wd", "%wd", 0); + SWPRINTF_TEST("w1d", "%w1d", 0); + SWPRINTF_TEST("w128d", "%w128d", 0); +} + +ATF_TC_WITHOUT_HEAD(swprintf_wfN); +ATF_TC_BODY(swprintf_wfN, tc) +{ + SWPRINTF_TEST("0", "%wf8d", (int_fast8_t)0); + SWPRINTF_TEST("-2147483648", "%wf8d", (int_fast8_t)INT_MIN); + SWPRINTF_TEST("2147483647", "%wf8d", (int_fast8_t)INT_MAX); + SWPRINTF_TEST("0", "%wf8u", (uint8_t)0); + SWPRINTF_TEST("4294967295", "%wf8u", (uint_fast8_t)UINT_MAX); + + SWPRINTF_TEST("0", "%wf16d", (int_fast16_t)0); + SWPRINTF_TEST("-2147483648", "%wf16d", (int_fast16_t)INT_MIN); + SWPRINTF_TEST("2147483647", "%wf16d", (int_fast16_t)INT_MAX); + SWPRINTF_TEST("0", "%wf16u", (uint16_t)0); + SWPRINTF_TEST("4294967295", "%wf16u", (uint_fast16_t)UINT_MAX); + + SWPRINTF_TEST("0", "%wf32d", (int_fast32_t)0); + SWPRINTF_TEST("-2147483648", "%wf32d", (int_fast32_t)INT_MIN); + SWPRINTF_TEST("2147483647", "%wf32d", (int_fast32_t)INT_MAX); + SWPRINTF_TEST("0", "%wf32u", (uint32_t)0); + SWPRINTF_TEST("4294967295", "%wf32u", (uint_fast32_t)UINT_MAX); + + SWPRINTF_TEST("0", "%wf64d", (int_fast64_t)0); + SWPRINTF_TEST("-9223372036854775808", "%wf64d", (int_fast64_t)LLONG_MIN); + SWPRINTF_TEST("9223372036854775807", "%wf64d", (int_fast64_t)LLONG_MAX); + SWPRINTF_TEST("0", "%wf64u", (uint64_t)0); + SWPRINTF_TEST("18446744073709551615", "%wf64u", (uint_fast64_t)ULLONG_MAX); + + SWPRINTF_TEST("wfd", "%wfd", 0); + SWPRINTF_TEST("wf1d", "%wf1d", 0); + SWPRINTF_TEST("wf128d", "%wf128d", 0); +} + +ATF_TP_ADD_TCS(tp) +{ + setlocale(LC_NUMERIC, "en_US.UTF-8"); + ATF_TP_ADD_TC(tp, swprintf_b); + ATF_TP_ADD_TC(tp, swprintf_B); + ATF_TP_ADD_TC(tp, swprintf_d); + ATF_TP_ADD_TC(tp, swprintf_x); + ATF_TP_ADD_TC(tp, swprintf_X); + ATF_TP_ADD_TC(tp, swprintf_wN); + ATF_TP_ADD_TC(tp, swprintf_wfN); + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdio/swscanf_test.c b/lib/libc/tests/stdio/swscanf_test.c new file mode 100644 index 000000000000..f0638081e16f --- /dev/null +++ b/lib/libc/tests/stdio/swscanf_test.c @@ -0,0 +1,359 @@ +/*- + * Copyright (c) 2023 Dag-Erling Smørgrav + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <limits.h> +#include <locale.h> +#include <stdint.h> +#include <stdio.h> +#include <wchar.h> + +#include <atf-c.h> + +#define L(s) L ## s + +static const struct swscanf_test_case { + wchar_t input[8]; + struct { + int ret, val, len; + } b, o, d, x, i; +} swscanf_test_cases[] = { +// input binary octal decimal hexadecimal automatic + // all digits + { L"0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + { L"1", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, }, + { L"2", { 0, 0, 0 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, }, + { L"3", { 0, 0, 0 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, }, + { L"4", { 0, 0, 0 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, }, + { L"5", { 0, 0, 0 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, }, + { L"6", { 0, 0, 0 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, }, + { L"7", { 0, 0, 0 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, }, + { L"8", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 8, 1 }, { 1, 8, 1 }, { 1, 8, 1 }, }, + { L"9", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 9, 1 }, { 1, 9, 1 }, { 1, 9, 1 }, }, + { L"A", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, }, + { L"B", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, }, + { L"C", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, }, + { L"D", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, }, + { L"E", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, }, + { L"F", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, }, + { L"X", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, }, + { L"a", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, }, + { L"b", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, }, + { L"c", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, }, + { L"d", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, }, + { L"e", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, }, + { L"f", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, }, + { L"x", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, }, + // all digits with leading zero + { L"00", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, + { L"01", { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, }, + { L"02", { 1, 0, 1 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, }, + { L"03", { 1, 0, 1 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, }, + { L"04", { 1, 0, 1 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, }, + { L"05", { 1, 0, 1 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, }, + { L"06", { 1, 0, 1 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, }, + { L"07", { 1, 0, 1 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, }, + { L"08", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 2 }, { 1, 8, 2 }, { 1, 0, 1 }, }, + { L"09", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 2 }, { 1, 9, 2 }, { 1, 0, 1 }, }, + { L"0A", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, }, + { L"0B", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, }, + { L"0C", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, }, + { L"0D", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, }, + { L"0E", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, }, + { L"0F", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, }, + { L"0X", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + { L"0a", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, }, + { L"0b", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, }, + { L"0c", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, }, + { L"0d", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, }, + { L"0e", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, }, + { L"0f", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, }, + { L"0x", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + // all digits with two leading zeroes + { L"000", { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, }, + { L"001", { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, }, + { L"002", { 1, 0, 2 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, }, + { L"003", { 1, 0, 2 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, }, + { L"004", { 1, 0, 2 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, }, + { L"005", { 1, 0, 2 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, }, + { L"006", { 1, 0, 2 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, }, + { L"007", { 1, 0, 2 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, }, + { L"008", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 8, 3 }, { 1, 8, 3 }, { 1, 0, 2 }, }, + { L"009", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 9, 3 }, { 1, 9, 3 }, { 1, 0, 2 }, }, + { L"00A", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, }, + { L"00B", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, }, + { L"00C", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, }, + { L"00D", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, }, + { L"00E", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, }, + { L"00F", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, }, + { L"00X", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, + { L"00a", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, }, + { L"00b", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, }, + { L"00c", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, }, + { L"00d", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, }, + { L"00e", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, }, + { L"00f", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, }, + { L"00x", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, + // all digits with leading one + { L"10", { 1, 2, 2 }, { 1, 8, 2 }, { 1, 10, 2 }, { 1, 16, 2 }, { 1, 10, 2 }, }, + { L"11", { 1, 3, 2 }, { 1, 9, 2 }, { 1, 11, 2 }, { 1, 17, 2 }, { 1, 11, 2 }, }, + { L"12", { 1, 1, 1 }, { 1, 10, 2 }, { 1, 12, 2 }, { 1, 18, 2 }, { 1, 12, 2 }, }, + { L"13", { 1, 1, 1 }, { 1, 11, 2 }, { 1, 13, 2 }, { 1, 19, 2 }, { 1, 13, 2 }, }, + { L"14", { 1, 1, 1 }, { 1, 12, 2 }, { 1, 14, 2 }, { 1, 20, 2 }, { 1, 14, 2 }, }, + { L"15", { 1, 1, 1 }, { 1, 13, 2 }, { 1, 15, 2 }, { 1, 21, 2 }, { 1, 15, 2 }, }, + { L"16", { 1, 1, 1 }, { 1, 14, 2 }, { 1, 16, 2 }, { 1, 22, 2 }, { 1, 16, 2 }, }, + { L"17", { 1, 1, 1 }, { 1, 15, 2 }, { 1, 17, 2 }, { 1, 23, 2 }, { 1, 17, 2 }, }, + { L"18", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 18, 2 }, { 1, 24, 2 }, { 1, 18, 2 }, }, + { L"19", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 19, 2 }, { 1, 25, 2 }, { 1, 19, 2 }, }, + { L"1A", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, }, + { L"1B", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, }, + { L"1C", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, }, + { L"1D", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, }, + { L"1E", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, }, + { L"1F", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, }, + { L"1X", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, }, + { L"1a", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, }, + { L"1b", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, }, + { L"1c", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, }, + { L"1d", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, }, + { L"1e", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, }, + { L"1f", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, }, + { L"1x", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, }, + // all digits with leading binary prefix + { L"0b0", { 1, 0, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 176, 3 }, { 1, 0, 3 }, }, + { L"0b1", { 1, 1, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 177, 3 }, { 1, 1, 3 }, }, + { L"0b2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 178, 3 }, { 1, 0, 1 }, }, + { L"0b3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 179, 3 }, { 1, 0, 1 }, }, + { L"0b4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 180, 3 }, { 1, 0, 1 }, }, + { L"0b5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 181, 3 }, { 1, 0, 1 }, }, + { L"0b6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 182, 3 }, { 1, 0, 1 }, }, + { L"0b7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 183, 3 }, { 1, 0, 1 }, }, + { L"0b8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 184, 3 }, { 1, 0, 1 }, }, + { L"0b9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 185, 3 }, { 1, 0, 1 }, }, + { L"0bA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, }, + { L"0bB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, }, + { L"0bC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, }, + { L"0bD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, }, + { L"0bE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, }, + { L"0bF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, }, + { L"0bX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, }, + { L"0ba", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, }, + { L"0bb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, }, + { L"0bc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, }, + { L"0bd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, }, + { L"0be", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, }, + { L"0bf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, }, + { L"0bx", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, }, + // all digits with leading hexadecimal prefix + { L"0x0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 3 }, { 1, 0, 3 }, }, + { L"0x1", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 1, 3 }, { 1, 1, 3 }, }, + { L"0x2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, }, + { L"0x3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 3, 3 }, { 1, 3, 3 }, }, + { L"0x4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 4, 3 }, { 1, 4, 3 }, }, + { L"0x5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 5, 3 }, { 1, 5, 3 }, }, + { L"0x6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 6, 3 }, { 1, 6, 3 }, }, + { L"0x7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 7, 3 }, { 1, 7, 3 }, }, + { L"0x8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 3 }, { 1, 8, 3 }, }, + { L"0x9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 3 }, { 1, 9, 3 }, }, + { L"0xA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, }, + { L"0xB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, }, + { L"0xC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, }, + { L"0xD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, }, + { L"0xE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, }, + { L"0xF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, }, + { L"0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + { L"0xa", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, }, + { L"0xb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, }, + { L"0xc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, }, + { L"0xd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, }, + { L"0xe", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, }, + { L"0xf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, }, + { L"0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + // terminator + { L"" } +}; + +#define SWSCANF_TEST(string, format, expret, expval, explen) \ + do { \ + int ret = 0, val = 0, len = 0; \ + ret = swscanf(string, format L"%n", &val, &len); \ + ATF_CHECK_EQ(expret, ret); \ + if (expret && ret) { \ + ATF_CHECK_EQ(expval, val); \ + ATF_CHECK_EQ(explen, len); \ + } \ + } while (0) + +ATF_TC_WITHOUT_HEAD(swscanf_b); +ATF_TC_BODY(swscanf_b, tc) +{ + const struct swscanf_test_case *stc; + wchar_t input[16]; + + for (stc = swscanf_test_cases; *stc->input; stc++) { + wcscpy(input + 1, stc->input); + SWSCANF_TEST(input + 1, L"%b", stc->b.ret, stc->b.val, stc->b.len); + input[0] = L'+'; + SWSCANF_TEST(input, L"%b", stc->b.ret, stc->b.val, stc->b.len ? stc->b.len + 1 : 0); + input[0] = L'-'; + SWSCANF_TEST(input, L"%b", stc->b.ret, -stc->b.val, stc->b.len ? stc->b.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(swscanf_o); +ATF_TC_BODY(swscanf_o, tc) +{ + const struct swscanf_test_case *stc; + wchar_t input[16]; + + for (stc = swscanf_test_cases; *stc->input; stc++) { + wcscpy(input + 1, stc->input); + SWSCANF_TEST(input + 1, L"%o", stc->o.ret, stc->o.val, stc->o.len); + input[0] = L'+'; + SWSCANF_TEST(input, L"%o", stc->o.ret, stc->o.val, stc->o.len ? stc->o.len + 1 : 0); + input[0] = L'-'; + SWSCANF_TEST(input, L"%o", stc->o.ret, -stc->o.val, stc->o.len ? stc->o.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(swscanf_d); +ATF_TC_BODY(swscanf_d, tc) +{ + const struct swscanf_test_case *stc; + wchar_t input[16]; + + for (stc = swscanf_test_cases; *stc->input; stc++) { + wcscpy(input + 1, stc->input); + SWSCANF_TEST(input + 1, L"%d", stc->d.ret, stc->d.val, stc->d.len); + input[0] = L'+'; + SWSCANF_TEST(input, L"%d", stc->d.ret, stc->d.val, stc->d.len ? stc->d.len + 1 : 0); + input[0] = L'-'; + SWSCANF_TEST(input, L"%d", stc->d.ret, -stc->d.val, stc->d.len ? stc->d.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(swscanf_x); +ATF_TC_BODY(swscanf_x, tc) +{ + const struct swscanf_test_case *stc; + wchar_t input[16]; + + for (stc = swscanf_test_cases; *stc->input; stc++) { + wcscpy(input + 1, stc->input); + SWSCANF_TEST(input + 1, L"%x", stc->x.ret, stc->x.val, stc->x.len); + input[0] = L'+'; + SWSCANF_TEST(input, L"%x", stc->x.ret, stc->x.val, stc->x.len ? stc->x.len + 1 : 0); + input[0] = L'-'; + SWSCANF_TEST(input, L"%x", stc->x.ret, -stc->x.val, stc->x.len ? stc->x.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(swscanf_i); +ATF_TC_BODY(swscanf_i, tc) +{ + const struct swscanf_test_case *stc; + wchar_t input[16]; + + for (stc = swscanf_test_cases; *stc->input; stc++) { + wcscpy(input + 1, stc->input); + SWSCANF_TEST(input + 1, L"%i", stc->i.ret, stc->i.val, stc->i.len); + input[0] = L'+'; + SWSCANF_TEST(input, L"%i", stc->i.ret, stc->i.val, stc->i.len ? stc->i.len + 1 : 0); + input[0] = L'-'; + SWSCANF_TEST(input, L"%i", stc->i.ret, -stc->i.val, stc->i.len ? stc->i.len + 1 : 0); + } +} + +ATF_TC_WITHOUT_HEAD(swscanf_wN); +ATF_TC_BODY(swscanf_wN, tc) +{ + const wchar_t x00[] = L"0x00"; + const wchar_t x7f[] = L"0x7fffffffffffffff"; + const wchar_t xff[] = L"0xffffffffffffffff"; + +#define SWSCANF_WN_TEST(N, imin, umax) \ + do { \ + int##N##_t i; \ + uint##N##_t u; \ + ATF_CHECK_EQ(1, swscanf(x00, L"%w" L(#N) L"i", &i)); \ + ATF_CHECK_EQ(0, i); \ + ATF_CHECK_EQ(1, swscanf(x7f, L"%w" L(#N) L"i", &i)); \ + ATF_CHECK_EQ(imin, i); \ + ATF_CHECK_EQ(1, swscanf(x00, L"%w" L(#N) L"x", &u)); \ + ATF_CHECK_EQ(0, u); \ + ATF_CHECK_EQ(1, swscanf(xff, L"%w" L(#N) L"x", &u)); \ + ATF_CHECK_EQ(umax, u); \ + } while (0) + SWSCANF_WN_TEST(8, -1, UCHAR_MAX); + SWSCANF_WN_TEST(16, -1, USHRT_MAX); + SWSCANF_WN_TEST(32, -1, UINT_MAX); + SWSCANF_WN_TEST(64, LLONG_MAX, ULLONG_MAX); +#undef SWSCANF_WN_TEST + + ATF_CHECK_EQ(0, swscanf(x00, L"%wi", (int *)NULL)); + ATF_CHECK_EQ(0, swscanf(x00, L"%w1i", (int *)NULL)); + ATF_CHECK_EQ(0, swscanf(x00, L"%w128i", (int *)NULL)); +} + +ATF_TC_WITHOUT_HEAD(swscanf_wfN); +ATF_TC_BODY(swscanf_wfN, tc) +{ + const wchar_t x00[] = L"0x00"; + const wchar_t x7f[] = L"0x7fffffffffffffff"; + const wchar_t xff[] = L"0xffffffffffffffff"; + +#define SWSCANF_WFN_TEST(N, imin, umax) \ + do { \ + int_fast##N##_t i; \ + uint_fast##N##_t u; \ + ATF_CHECK_EQ(1, swscanf(x00, L"%wf" L(#N) L"i", &i)); \ + ATF_CHECK_EQ(0, i); \ + ATF_CHECK_EQ(1, swscanf(x7f, L"%wf" L(#N) L"i", &i)); \ + ATF_CHECK_EQ(imin, i); \ + ATF_CHECK_EQ(1, swscanf(x00, L"%wf" L(#N) L"x", &u)); \ + ATF_CHECK_EQ(0, u); \ + ATF_CHECK_EQ(1, swscanf(xff, L"%wf" L(#N) L"x", &u)); \ + ATF_CHECK_EQ(umax, u); \ + } while (0) + SWSCANF_WFN_TEST(8, -1, UINT_MAX); + SWSCANF_WFN_TEST(16, -1, UINT_MAX); + SWSCANF_WFN_TEST(32, -1, UINT_MAX); + SWSCANF_WFN_TEST(64, LLONG_MAX, ULLONG_MAX); +#undef SWSCANF_WFN_TEST + + ATF_CHECK_EQ(0, swscanf(x00, L"%wfi", (int *)NULL)); + ATF_CHECK_EQ(0, swscanf(x00, L"%wf1i", (int *)NULL)); + ATF_CHECK_EQ(0, swscanf(x00, L"%wf128i", (int *)NULL)); +} + +/* + * Test termination cases: non-numeric character, fixed width, EOF + */ +ATF_TC_WITHOUT_HEAD(swscanf_termination); +ATF_TC_BODY(swscanf_termination, tc) +{ + int a = 0, b = 0, c = 0; + wchar_t d = 0; + + ATF_CHECK_EQ(4, swscanf(L"3.1415", L"%d%lc%2d%d", &a, &d, &b, &c)); + ATF_CHECK_EQ(3, a); + ATF_CHECK_EQ(14, b); + ATF_CHECK_EQ(15, c); + ATF_CHECK_EQ(L'.', d); +} + +ATF_TP_ADD_TCS(tp) +{ + setlocale(LC_NUMERIC, "en_US.UTF-8"); + ATF_TP_ADD_TC(tp, swscanf_b); + ATF_TP_ADD_TC(tp, swscanf_o); + ATF_TP_ADD_TC(tp, swscanf_d); + ATF_TP_ADD_TC(tp, swscanf_x); + ATF_TP_ADD_TC(tp, swscanf_i); + ATF_TP_ADD_TC(tp, swscanf_wN); + ATF_TP_ADD_TC(tp, swscanf_wfN); + ATF_TP_ADD_TC(tp, swscanf_termination); + return (atf_no_error()); +} |