From 2b2e6341820f4bf973e758ae57713a8e6d356a3a Mon Sep 17 00:00:00 2001 From: jhb Date: Wed, 27 Feb 2013 19:50:46 +0000 Subject: Add an implementation of open_memstream() and open_wmemstream(). These routines provide write-only stdio FILE objects that store their data in a dynamically allocated buffer. They are a string builder interface somewhat akin to a completely dynamic sbuf. Reviewed by: bde, jilles (earlier versions) MFC after: 1 month --- tools/regression/lib/libc/stdio/Makefile | 4 +- .../lib/libc/stdio/test-open_memstream.c | 203 +++++++++++++++++++++ .../lib/libc/stdio/test-open_memstream.t | 10 + .../lib/libc/stdio/test-open_wmemstream.c | 203 +++++++++++++++++++++ .../lib/libc/stdio/test-open_wmemstream.t | 10 + 5 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 tools/regression/lib/libc/stdio/test-open_memstream.c create mode 100644 tools/regression/lib/libc/stdio/test-open_memstream.t create mode 100644 tools/regression/lib/libc/stdio/test-open_wmemstream.c create mode 100644 tools/regression/lib/libc/stdio/test-open_wmemstream.t (limited to 'tools/regression/lib/libc') diff --git a/tools/regression/lib/libc/stdio/Makefile b/tools/regression/lib/libc/stdio/Makefile index d62ac84..f496f73 100644 --- a/tools/regression/lib/libc/stdio/Makefile +++ b/tools/regression/lib/libc/stdio/Makefile @@ -1,6 +1,8 @@ # $FreeBSD$ -TESTS= test-getdelim test-perror test-print-positional test-printbasic test-printfloat test-scanfloat +TESTS= test-fmemopen test-getdelim test-open_memstream test-open_wmemstream \ + test-perror test-print-positional test-printbasic test-printfloat \ + test-scanfloat CFLAGS+= -lm .PHONY: tests diff --git a/tools/regression/lib/libc/stdio/test-open_memstream.c b/tools/regression/lib/libc/stdio/test-open_memstream.c new file mode 100644 index 0000000..1a168c6 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-open_memstream.c @@ -0,0 +1,203 @@ +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} + +static void +open_group_test(void) +{ + FILE *fp; + off_t eob; + + fp = open_memstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); + + 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); +} + +static void +simple_tests(void) +{ + static const char zerobuf[] = + { 'f', 'o', 'o', 0, 0, 0, 0, 'b', 'a', 'r', 0 }; + char c; + FILE *fp; + + fp = open_memstream(&buf, NULL); + if (fp != NULL) + errx(1, "did not fail to open stream"); + else if (errno != EINVAL) + err(1, "incorrect error for bad length pointer"); + fp = open_memstream(NULL, &len); + if (fp != NULL) + errx(1, "did not fail to open stream"); + else if (errno != EINVAL) + err(1, "incorrect error for bad buffer pointer"); + fp = open_memstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); + 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); +} + +static void +seek_tests(void) +{ + FILE *fp; + + fp = open_memstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); +#define SEEK_FAIL(offset, whence, error) do { \ + errno = 0; \ + if (fseeko(fp, (offset), (whence)) == 0) \ + printf("fseeko(%s, %s) did not fail, set pos to %jd\n", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp)); \ + else if (errno != (error)) \ + printf("fseeko(%s, %s) failed with %d rather than %s\n",\ + __STRING(offset), __STRING(whence), errno, \ + __STRING(error)); \ +} while (0) + +#define SEEK_OK(offset, whence, result) do { \ + if (fseeko(fp, (offset), (whence)) != 0) \ + printf("fseeko(%s, %s) failed: %s\n", \ + __STRING(offset), __STRING(whence), strerror(errno)); \ + else if (ftello(fp) != (result)) \ + printf("fseeko(%s, %s) seeked to %jd rather than %s\n", \ + __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); +} + +int +main(int ac, char **av) +{ + + open_group_test(); + simple_tests(); + seek_tests(); + return (0); +} diff --git a/tools/regression/lib/libc/stdio/test-open_memstream.t b/tools/regression/lib/libc/stdio/test-open_memstream.t new file mode 100644 index 0000000..8bdfd03 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-open_memstream.t @@ -0,0 +1,10 @@ +#!/bin/sh +# $FreeBSD$ + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +exec ./$executable diff --git a/tools/regression/lib/libc/stdio/test-open_wmemstream.c b/tools/regression/lib/libc/stdio/test-open_wmemstream.c new file mode 100644 index 0000000..4cd0ab9 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-open_wmemstream.c @@ -0,0 +1,203 @@ +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} + +static void +open_group_test(void) +{ + FILE *fp; + off_t eob; + + fp = open_wmemstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); + + 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); +} + +static void +simple_tests(void) +{ + 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); + if (fp != NULL) + errx(1, "did not fail to open stream"); + else if (errno != EINVAL) + err(1, "incorrect error for bad length pointer"); + fp = open_wmemstream(NULL, &len); + if (fp != NULL) + errx(1, "did not fail to open stream"); + else if (errno != EINVAL) + err(1, "incorrect error for bad buffer pointer"); + fp = open_wmemstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); + 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); +} + +static void +seek_tests(void) +{ + FILE *fp; + + fp = open_wmemstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); +#define SEEK_FAIL(offset, whence, error) do { \ + errno = 0; \ + if (fseeko(fp, (offset), (whence)) == 0) \ + printf("fseeko(%s, %s) did not fail, set pos to %jd\n", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp)); \ + else if (errno != (error)) \ + printf("fseeko(%s, %s) failed with %d rather than %s\n",\ + __STRING(offset), __STRING(whence), errno, \ + __STRING(error)); \ +} while (0) + +#define SEEK_OK(offset, whence, result) do { \ + if (fseeko(fp, (offset), (whence)) != 0) \ + printf("fseeko(%s, %s) failed: %s\n", \ + __STRING(offset), __STRING(whence), strerror(errno)); \ + else if (ftello(fp) != (result)) \ + printf("fseeko(%s, %s) seeked to %jd rather than %s\n", \ + __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); +} + +int +main(int ac, char **av) +{ + + open_group_test(); + simple_tests(); + seek_tests(); + return (0); +} diff --git a/tools/regression/lib/libc/stdio/test-open_wmemstream.t b/tools/regression/lib/libc/stdio/test-open_wmemstream.t new file mode 100644 index 0000000..8bdfd03 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-open_wmemstream.t @@ -0,0 +1,10 @@ +#!/bin/sh +# $FreeBSD$ + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +exec ./$executable -- cgit v1.1 From 8435d3c02c8802b5019ee98f13f0270b332106c5 Mon Sep 17 00:00:00 2001 From: jilles Date: Mon, 1 Apr 2013 20:50:07 +0000 Subject: wordexp(): Remove wrong IFS usage. Words in shell script are separated by spaces or tabs independent of the value of IFS. The value of IFS is only relevant for the result of substitutions. Therefore, there should be a space between 'wordexp' and the words to be expanded, not an IFS character. Paranoia might dictate that the shell ignore IFS from the environment (even though our sh currently uses it), so do not depend on it in the new test case. --- tools/regression/lib/libc/gen/test-wordexp.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'tools/regression/lib/libc') diff --git a/tools/regression/lib/libc/gen/test-wordexp.c b/tools/regression/lib/libc/gen/test-wordexp.c index df8f885..d94f870 100644 --- a/tools/regression/lib/libc/gen/test-wordexp.c +++ b/tools/regression/lib/libc/gen/test-wordexp.c @@ -208,6 +208,25 @@ main(int argc, char *argv[]) assert(strcmp(we.we_wordv[1], "world") == 0); assert(we.we_wordv[2] == NULL); wordfree(&we); + sa.sa_handler = SIG_DFL; + r = sigaction(SIGCHLD, &sa, NULL); + assert(r == 0); + + /* + * With IFS set to a non-default value (without depending on whether + * IFS is inherited or not). + */ + r = setenv("IFS", ":", 1); + assert(r == 0); + r = wordexp("hello world", &we, 0); + assert(r == 0); + assert(we.we_wordc == 2); + assert(strcmp(we.we_wordv[0], "hello") == 0); + assert(strcmp(we.we_wordv[1], "world") == 0); + assert(we.we_wordv[2] == NULL); + wordfree(&we); + r = unsetenv("IFS"); + assert(r == 0); printf("PASS wordexp()\n"); printf("PASS wordfree()\n"); -- cgit v1.1