diff --git a/libc/Android.mk b/libc/Android.mk index aa787ce2a..6345c3194 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -62,6 +62,7 @@ libc_common_src_files := \ bionic/system_properties_compat.c \ stdio/snprintf.c\ stdio/sprintf.c \ + stdio/stdio_ext.cpp \ # Fortify implementations of libc functions. libc_common_src_files += \ diff --git a/libc/include/stdio.h b/libc/include/stdio.h index 516f8cb10..8727a9fa2 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -343,6 +343,11 @@ int setlinebuf(FILE *); int vasprintf(char ** __restrict, const char * __restrict, __va_list) __printflike(2, 0); + +void clearerr_unlocked(FILE*); +int feof_unlocked(FILE*); +int ferror_unlocked(FILE*); + __END_DECLS /* diff --git a/libc/include/stdio_ext.h b/libc/include/stdio_ext.h new file mode 100644 index 000000000..f299e54e2 --- /dev/null +++ b/libc/include/stdio_ext.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * 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 OWNER 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. + */ + +#ifndef _STDIO_EXT_H +#define _STDIO_EXT_H + +#include +#include + +#define FSETLOCKING_QUERY 0 +#define FSETLOCKING_INTERNAL 1 +#define FSETLOCKING_BYCALLER 2 + +__BEGIN_DECLS + +size_t __fbufsize(FILE*); +int __freading(FILE*); +int __fwriting(FILE*); +int __freadable(FILE*); +int __fwritable(FILE*); +int __flbf(FILE*); +void __fpurge(FILE*); +size_t __fpending(FILE*); +void _flushlbf(void); +int __fsetlocking(FILE*, int); + +__END_DECLS + +#endif /* _STDIO_EXT_H */ diff --git a/libc/stdio/stdio_ext.cpp b/libc/stdio/stdio_ext.cpp new file mode 100644 index 000000000..bfdecb81c --- /dev/null +++ b/libc/stdio/stdio_ext.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * 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 OWNER 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 + +#include +#include "local.h" + +#define FSETLOCKING_QUERY 0 +#define FSETLOCKING_INTERNAL 1 +#define FSETLOCKING_BYCALLER 2 + +size_t __fbufsize(FILE* fp) { + return fp->_bf._size; +} + +/* For a _SRW stream, we don't know whether we last read or wrote. +int __freading(FILE* fp) { + return (fp->_flags & _SRD) != 0 || ...; +} +*/ + +/* For a _SRW stream, we don't know whether we last read or wrote. +int __fwriting(FILE*) { + return (fp->_flags & _SWR) != 0 || ...; +} +*/ + +int __freadable(FILE* fp) { + return (fp->_flags & (__SRD|__SRW)) != 0; +} + +int __fwritable(FILE* fp) { + return (fp->_flags & (__SWR|__SRW)) != 0; +} + +int __flbf(FILE* fp) { + return (fp->_flags & __SLBF) != 0; +} + +void __fpurge(FILE* fp) { + fpurge(fp); +} + +size_t __fpending(FILE* fp) { + return fp->_p - fp->_bf._base; +} + +void _flushlbf() { + // If we flush all streams, we know we've flushed all the line-buffered streams. + fflush(NULL); +} + +int __fsetlocking(FILE*, int) { + // We don't currently have an implementation that would obey this, + // so make setting the state a no-op and always return "we handle locking for you". + // http://b/17154740 suggests ways we could fix this. + return FSETLOCKING_INTERNAL; +} + +void clearerr_unlocked(FILE* fp) { + return __sclearerr(fp); +} + +int feof_unlocked(FILE* fp) { + return __sfeof(fp); +} + +int ferror_unlocked(FILE* fp) { + return __sferror(fp); +} diff --git a/tests/Android.mk b/tests/Android.mk index b370b92c1..ef33afcd8 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -92,6 +92,7 @@ libBionicStandardTests_src_files := \ stdatomic_test.cpp \ stdint_test.cpp \ stdio_test.cpp \ + stdio_ext_test.cpp \ stdlib_test.cpp \ string_test.cpp \ strings_test.cpp \ diff --git a/tests/stdio_ext_test.cpp b/tests/stdio_ext_test.cpp new file mode 100644 index 000000000..3dbc485a6 --- /dev/null +++ b/tests/stdio_ext_test.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TemporaryFile.h" + +TEST(stdio_ext, __fbufsize) { + FILE* fp = fopen("/proc/version", "r"); + + char buf[128]; + + ASSERT_EQ(0, setvbuf(fp, buf, _IOFBF, 1)); + ASSERT_EQ(1U, __fbufsize(fp)); + + ASSERT_EQ(0, setvbuf(fp, buf, _IOFBF, 8)); + ASSERT_EQ(8U, __fbufsize(fp)); + + fclose(fp); +} + +TEST(stdio_ext, __flbf) { + FILE* fp = fopen("/proc/version", "r"); + + ASSERT_FALSE(__flbf(fp)); + + char buf[128]; + ASSERT_EQ(0, setvbuf(fp, buf, _IOLBF, sizeof(buf))); + + ASSERT_TRUE(__flbf(fp)); + + fclose(fp); +} + +TEST(stdio_ext, __fpending) { + FILE* fp = fopen("/dev/null", "w"); + ASSERT_EQ(0U, __fpending(fp)); + ASSERT_EQ('x', fputc('x', fp)); + ASSERT_EQ(1U, __fpending(fp)); + ASSERT_EQ('y', fputc('y', fp)); + ASSERT_EQ(2U, __fpending(fp)); + fflush(fp); + ASSERT_EQ(0U, __fpending(fp)); + fclose(fp); +} + +TEST(stdio_ext, __fpurge) { + FILE* fp = tmpfile(); + + ASSERT_EQ('a', fputc('a', fp)); + ASSERT_EQ(1U, __fpending(fp)); + __fpurge(fp); + ASSERT_EQ(0U, __fpending(fp)); + + ASSERT_EQ('b', fputc('b', fp)); + ASSERT_EQ('\n', fputc('\n', fp)); + ASSERT_EQ(2U, __fpending(fp)); + + rewind(fp); + + char buf[16]; + char* s = fgets(buf, sizeof(buf), fp); + ASSERT_TRUE(s != NULL); + ASSERT_STREQ("b\n", s); + + fclose(fp); +} + +TEST(stdio_ext, _flushlbf) { + FILE* fp = fopen("/dev/null", "w"); + + char buf[128]; + ASSERT_EQ(0, setvbuf(fp, buf, _IOLBF, sizeof(buf))); + + ASSERT_EQ('a', fputc('a', fp)); + ASSERT_EQ(1U, __fpending(fp)); + + _flushlbf(); + + ASSERT_EQ(0U, __fpending(fp)); + + fclose(fp); +} + +TEST(stdio_ext, __freadable__fwritable) { + FILE* fp = fopen("/dev/null", "r"); + ASSERT_TRUE(__freadable(fp)); + ASSERT_FALSE(__fwritable(fp)); + fclose(fp); + + fp = fopen("/dev/null", "w"); + ASSERT_FALSE(__freadable(fp)); + ASSERT_TRUE(__fwritable(fp)); + fclose(fp); + + fp = fopen("/dev/null", "w+"); + ASSERT_TRUE(__freadable(fp)); + ASSERT_TRUE(__fwritable(fp)); + fclose(fp); +} + +TEST(stdio_ext, __fsetlocking) { + FILE* fp = fopen("/proc/version", "r"); + // Android doesn't actually support the other modes. + ASSERT_EQ(FSETLOCKING_INTERNAL, __fsetlocking(fp, FSETLOCKING_QUERY)); + fclose(fp); +}