diff --git a/libc/Android.mk b/libc/Android.mk index 51858938a..fc95427b2 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -340,6 +340,8 @@ libc_upstream_netbsd_src_files := \ upstream-netbsd/libc/regex/regerror.c \ upstream-netbsd/libc/regex/regexec.c \ upstream-netbsd/libc/regex/regfree.c \ + upstream-netbsd/libc/stdio/getdelim.c \ + upstream-netbsd/libc/stdio/getline.c \ upstream-netbsd/libc/stdlib/tdelete.c \ upstream-netbsd/libc/stdlib/tfind.c \ upstream-netbsd/libc/stdlib/tsearch.c \ diff --git a/libc/NOTICE b/libc/NOTICE index 299f672e7..36d944981 100644 --- a/libc/NOTICE +++ b/libc/NOTICE @@ -438,6 +438,40 @@ SUCH DAMAGE. ------------------------------------------------------------------- +Copyright (c) 1982, 1986, 1993 + The Regents of the University of California. All rights reserved. +(c) UNIX System Laboratories, Inc. +All or some portions of this file are derived from material licensed +to the University of California by American Telephone and Telegraph +Co. or Unix System Laboratories, Inc. and are reproduced herein with +the permission of UNIX System Laboratories, Inc. + +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. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +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. + +------------------------------------------------------------------- + Copyright (c) 1983, 1987, 1989 The Regents of the University of California. All rights reserved. @@ -3833,6 +3867,33 @@ SUCH DAMAGE. ------------------------------------------------------------------- +Copyright (c) 2009 The NetBSD Foundation, Inc. + +This code is derived from software contributed to The NetBSD Foundation +by Roy Marples. + +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 ``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 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. + +------------------------------------------------------------------- + Copyright (c) 2010 MIPS Technologies, Inc. All rights reserved. diff --git a/libc/include/stdio.h b/libc/include/stdio.h index 8b95663f4..d0121c684 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -249,6 +249,9 @@ off_t ftello(FILE *); size_t fwrite(const void *, size_t, size_t, FILE *); int getc(FILE *); int getchar(void); +ssize_t getdelim(char ** __restrict, size_t * __restrict, int, + FILE * __restrict); +ssize_t getline(char ** __restrict, size_t * __restrict, FILE * __restrict); char *gets(char *); #if __BSD_VISIBLE && !defined(__SYS_ERRLIST) #define __SYS_ERRLIST diff --git a/libc/include/sys/param.h b/libc/include/sys/param.h index 3a815cb04..0bfdc5d7b 100644 --- a/libc/include/sys/param.h +++ b/libc/include/sys/param.h @@ -37,4 +37,6 @@ #define ALIGNBYTES 3 #define ALIGN(p) (((unsigned int)(p) + ALIGNBYTES) &~ ALIGNBYTES) +#define powerof2(x) ((((x)-1)&(x))==0) + #endif /* _SYS_PARAM_H_ */ diff --git a/libc/upstream-netbsd/libc/stdio/getdelim.c b/libc/upstream-netbsd/libc/stdio/getdelim.c new file mode 100644 index 000000000..acce3764b --- /dev/null +++ b/libc/upstream-netbsd/libc/stdio/getdelim.c @@ -0,0 +1,154 @@ +/* $NetBSD: getdelim.c,v 1.13 2011/07/22 23:12:30 joerg Exp $ */ + +/* + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * 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 ``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 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 +__RCSID("$NetBSD: getdelim.c,v 1.13 2011/07/22 23:12:30 joerg Exp $"); + +#include "namespace.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "reentrant.h" +#include "local.h" + +#ifdef __weak_alias +__weak_alias(getdelim, _getdelim) +#endif + +/* Minimum buffer size we create. + * This should allow config files to fit into our power of 2 buffer growth + * without the need for a realloc. */ +#define MINBUF 128 + +ssize_t +__getdelim(char **__restrict buf, size_t *__restrict buflen, + int sep, FILE *__restrict fp) +{ + unsigned char *p; + size_t len, newlen, off; + char *newb; + + _DIAGASSERT(fp != NULL); + + if (buf == NULL || buflen == NULL) { + errno = EINVAL; + goto error; + } + + /* If buf is NULL, we have to assume a size of zero */ + if (*buf == NULL) + *buflen = 0; + + _SET_ORIENTATION(fp, -1); + off = 0; + do { + /* If the input buffer is empty, refill it */ + if (fp->_r <= 0 && __srefill(fp)) { + if (__sferror(fp)) + goto error; + /* No error, so EOF. */ + break; + } + + /* Scan through looking for the separator */ + p = memchr(fp->_p, sep, (size_t)fp->_r); + if (p == NULL) + len = fp->_r; + else + len = (p - fp->_p) + 1; + + newlen = off + len; + /* Ensure we can handle it */ + if (newlen < off || newlen > SSIZE_MAX) { + errno = EOVERFLOW; + goto error; + } + newlen++; /* reserve space for the NULL terminator */ + if (newlen > *buflen) { + if (newlen < MINBUF) + newlen = MINBUF; + if (!powerof2(newlen)) { + /* Grow the buffer to the next power of 2 */ + newlen--; + newlen |= newlen >> 1; + newlen |= newlen >> 2; + newlen |= newlen >> 4; + newlen |= newlen >> 8; + newlen |= newlen >> 16; +#if SIZE_T_MAX > 0xffffffffU + newlen |= newlen >> 32; +#endif + newlen++; + } + + newb = realloc(*buf, newlen); + if (newb == NULL) + goto error; + *buf = newb; + *buflen = newlen; + } + + (void)memcpy((*buf + off), fp->_p, len); + /* Safe, len is never greater than what fp->_r can fit. */ + fp->_r -= (int)len; + fp->_p += (int)len; + off += len; + } while (p == NULL); + + /* POSIX demands we return -1 on EOF. */ + if (off == 0) + return -1; + + if (*buf != NULL) + *(*buf + off) = '\0'; + return off; + +error: + fp->_flags |= __SERR; + return -1; +} + +ssize_t +getdelim(char **__restrict buf, size_t *__restrict buflen, + int sep, FILE *__restrict fp) +{ + ssize_t n; + + FLOCKFILE(fp); + n = __getdelim(buf, buflen, sep, fp); + FUNLOCKFILE(fp); + return n; +} diff --git a/libc/upstream-netbsd/libc/stdio/getline.c b/libc/upstream-netbsd/libc/stdio/getline.c new file mode 100644 index 000000000..e5d4bab26 --- /dev/null +++ b/libc/upstream-netbsd/libc/stdio/getline.c @@ -0,0 +1,45 @@ +/* $NetBSD: getline.c,v 1.3 2009/12/02 08:46:33 roy Exp $ */ + +/* + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * 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 ``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 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 +__RCSID("$NetBSD: getline.c,v 1.3 2009/12/02 08:46:33 roy Exp $"); + +#include "namespace.h" + +#include + +#ifdef __weak_alias +__weak_alias(getline, _getline) +#endif + +ssize_t +getline(char **__restrict buf, size_t *__restrict buflen, FILE *__restrict fp) +{ + return getdelim(buf, buflen, '\n', fp); +} diff --git a/libc/upstream-netbsd/reentrant.h b/libc/upstream-netbsd/reentrant.h new file mode 100644 index 000000000..a4381d479 --- /dev/null +++ b/libc/upstream-netbsd/reentrant.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef _BIONIC_NETBSD_REENTRANT_H_included +#define _BIONIC_NETBSD_REENTRANT_H_included + +#include +#include + +// Placeholder. + +#endif diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp index 72af7962e..39f9b0f3d 100644 --- a/tests/stdio_test.cpp +++ b/tests/stdio_test.cpp @@ -46,3 +46,126 @@ TEST(stdio, tmpfile_fileno_fprintf_rewind_fgets) { fclose(fp); } + +TEST(stdio, getdelim) { + FILE* fp = tmpfile(); + ASSERT_TRUE(fp != NULL); + + const char* line_written = "This is a test"; + int rc = fprintf(fp, "%s", line_written); + ASSERT_EQ(rc, static_cast(strlen(line_written))); + + rewind(fp); + + char* word_read = NULL; + size_t allocated_length = 0; + + const char* expected[] = { "This ", " ", "is ", "a ", "test" }; + for (size_t i = 0; i < 5; ++i) { + ASSERT_FALSE(feof(fp)); + ASSERT_EQ(getdelim(&word_read, &allocated_length, ' ', fp), static_cast(strlen(expected[i]))); + ASSERT_GE(allocated_length, strlen(expected[i])); + ASSERT_STREQ(word_read, expected[i]); + } + // The last read should have set the end-of-file indicator for the stream. + ASSERT_TRUE(feof(fp)); + clearerr(fp); + + // getdelim returns -1 but doesn't set errno if we're already at EOF. + // It should set the end-of-file indicator for the stream, though. + errno = 0; + ASSERT_EQ(getdelim(&word_read, &allocated_length, ' ', fp), -1); + ASSERT_EQ(errno, 0); + ASSERT_TRUE(feof(fp)); + + free(word_read); + fclose(fp); +} + +TEST(stdio, getdelim_invalid) { + FILE* fp = tmpfile(); + + char* buffer = NULL; + size_t buffer_length = 0; + + // The first argument can't be NULL. + errno = 0; + ASSERT_EQ(getdelim(NULL, &buffer_length, ' ', fp), -1); + ASSERT_EQ(errno, EINVAL); + + // The second argument can't be NULL. + errno = 0; + ASSERT_EQ(getdelim(&buffer, NULL, ' ', fp), -1); + ASSERT_EQ(errno, EINVAL); + + // The stream can't be closed. + fclose(fp); + errno = 0; + ASSERT_EQ(getdelim(&buffer, &buffer_length, ' ', fp), -1); + ASSERT_EQ(errno, EBADF); +} + +TEST(stdio, getline) { + FILE* fp = tmpfile(); + ASSERT_TRUE(fp != NULL); + + const char* line_written = "This is a test for getline\n"; + const size_t line_count = 5; + + for (size_t i = 0; i < line_count; ++i) { + int rc = fprintf(fp, "%s", line_written); + ASSERT_EQ(rc, static_cast(strlen(line_written))); + } + + rewind(fp); + + char* line_read = NULL; + size_t allocated_length = 0; + + size_t read_line_count = 0; + ssize_t read_char_count; + while ((read_char_count = getline(&line_read, &allocated_length, fp)) != -1) { + ASSERT_EQ(read_char_count, static_cast(strlen(line_written))); + ASSERT_GE(allocated_length, strlen(line_written)); + ASSERT_STREQ(line_read, line_written); + ++read_line_count; + } + ASSERT_EQ(read_line_count, line_count); + + // The last read should have set the end-of-file indicator for the stream. + ASSERT_TRUE(feof(fp)); + clearerr(fp); + + // getline returns -1 but doesn't set errno if we're already at EOF. + // It should set the end-of-file indicator for the stream, though. + errno = 0; + ASSERT_EQ(getline(&line_read, &allocated_length, fp), -1); + ASSERT_EQ(errno, 0); + ASSERT_TRUE(feof(fp)); + + free(line_read); + fclose(fp); +} + +TEST(stdio, getline_invalid) { + FILE* fp = tmpfile(); + + char* buffer = NULL; + size_t buffer_length = 0; + + // The first argument can't be NULL. + errno = 0; + ASSERT_EQ(getline(NULL, &buffer_length, fp), -1); + ASSERT_EQ(errno, EINVAL); + + // The second argument can't be NULL. + errno = 0; + ASSERT_EQ(getline(&buffer, NULL, fp), -1); + ASSERT_EQ(errno, EINVAL); + + // The stream can't be closed. + fclose(fp); + errno = 0; + ASSERT_EQ(getline(&buffer, &buffer_length, fp), -1); + ASSERT_EQ(errno, EBADF); +}