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()); +} | 
