More missing _unlocked <stdio.h> functions.

Also simplify trivial one-liners like perror/puts/fputs, and clean up
fread/fwrite slightly.

Fix perror to match POSIX.

Add basic perror and *_unlocked tests.

Bug: N/A
Test: ran tests
Change-Id: I63f83c8e0c15c3c4096509d17421ac331b6fc23d
This commit is contained in:
Elliott Hughes 2017-10-30 17:47:12 -07:00
parent e379a20250
commit 37ad959783
22 changed files with 382 additions and 665 deletions

View file

@ -10,6 +10,7 @@ New libc functions in P:
* `__freading`/`__fwriting` (completing <stdio_ext.h>)
* `endhostent`/endnetent`/`endprotoent`/`getnetent`/`getprotoent`/`sethostent`/`setnetent`/`setprotoent` (completing <netdb.h>)
* `fexecve`
* `fflush_unlocked`/`fgetc_unlocked`/`fgets_unlocked`/`fputc_unlocked`/`fputs_unlocked`/`fread_unlocked`/`fwrite_unlocked`
* `getentropy`/`getrandom` (adding <sys/random.h>)
* `getlogin_r`
* `glob`/`globfree` (adding <glob.h>)

View file

@ -14,7 +14,6 @@ libc_common_src_files = [
"bionic/siginterrupt.c",
"bionic/sigsetmask.c",
"stdio/fmemopen.cpp",
"stdio/fread.cpp",
"stdio/parsefloat.c",
"stdio/refill.c",
"stdio/stdio.cpp",
@ -439,28 +438,22 @@ cc_library_static {
"upstream-openbsd/lib/libc/net/ntohl.c",
"upstream-openbsd/lib/libc/net/ntohs.c",
"upstream-openbsd/lib/libc/net/res_random.c",
"upstream-openbsd/lib/libc/stdio/fflush.c",
"upstream-openbsd/lib/libc/stdio/fgetln.c",
"upstream-openbsd/lib/libc/stdio/fgets.c",
"upstream-openbsd/lib/libc/stdio/fgetwc.c",
"upstream-openbsd/lib/libc/stdio/fgetws.c",
"upstream-openbsd/lib/libc/stdio/flags.c",
"upstream-openbsd/lib/libc/stdio/fpurge.c",
"upstream-openbsd/lib/libc/stdio/fputs.c",
"upstream-openbsd/lib/libc/stdio/fputwc.c",
"upstream-openbsd/lib/libc/stdio/fputws.c",
"upstream-openbsd/lib/libc/stdio/fvwrite.c",
"upstream-openbsd/lib/libc/stdio/fwalk.c",
"upstream-openbsd/lib/libc/stdio/fwide.c",
"upstream-openbsd/lib/libc/stdio/fwrite.c",
"upstream-openbsd/lib/libc/stdio/getdelim.c",
"upstream-openbsd/lib/libc/stdio/gets.c",
"upstream-openbsd/lib/libc/stdio/makebuf.c",
"upstream-openbsd/lib/libc/stdio/mktemp.c",
"upstream-openbsd/lib/libc/stdio/open_memstream.c",
"upstream-openbsd/lib/libc/stdio/open_wmemstream.c",
"upstream-openbsd/lib/libc/stdio/perror.c",
"upstream-openbsd/lib/libc/stdio/puts.c",
"upstream-openbsd/lib/libc/stdio/rget.c",
"upstream-openbsd/lib/libc/stdio/setvbuf.c",
"upstream-openbsd/lib/libc/stdio/tempnam.c",

View file

@ -256,7 +256,20 @@ int ferror_unlocked(FILE* __fp) __INTRODUCED_IN(23);
int fileno_unlocked(FILE* __fp) __INTRODUCED_IN(24);
#define fropen(cookie, fn) funopen(cookie, fn, 0, 0, 0)
#define fwopen(cookie, fn) funopen(cookie, 0, fn, 0, 0)
#endif /* __USE_BSD */
#endif
#if defined(__USE_BSD)
int fflush_unlocked(FILE* __fp) __INTRODUCED_IN_FUTURE;
int fgetc_unlocked(FILE* __fp) __INTRODUCED_IN_FUTURE;
int fputc_unlocked(int __ch, FILE* __fp) __INTRODUCED_IN_FUTURE;
size_t fread_unlocked(void* __buf, size_t __size, size_t __count, FILE* __fp) __INTRODUCED_IN_FUTURE;
size_t fwrite_unlocked(const void* __buf, size_t __size, size_t __count, FILE* __fp) __INTRODUCED_IN_FUTURE;
#endif
#if defined(__USE_GNU)
int fputs_unlocked(const char* __s, FILE* __fp) __INTRODUCED_IN_FUTURE;
char* fgets_unlocked(char* __buf, int __size, FILE* __fp) __INTRODUCED_IN_FUTURE;
#endif
#if defined(__BIONIC_INCLUDE_FORTIFY_HEADERS)
#include <bits/fortify/stdio.h>

View file

@ -1326,6 +1326,13 @@ LIBC_P { # introduced=P
endnetent;
endprotoent;
fexecve;
fflush_unlocked;
fgetc_unlocked;
fgets_unlocked;
fputc_unlocked;
fputs_unlocked;
fread_unlocked;
fwrite_unlocked;
getentropy;
getnetent;
getprotoent;

View file

@ -1246,6 +1246,13 @@ LIBC_P { # introduced=P
endnetent;
endprotoent;
fexecve;
fflush_unlocked;
fgetc_unlocked;
fgets_unlocked;
fputc_unlocked;
fputs_unlocked;
fread_unlocked;
fwrite_unlocked;
getentropy;
getnetent;
getprotoent;

View file

@ -1351,6 +1351,13 @@ LIBC_P { # introduced=P
endnetent;
endprotoent;
fexecve;
fflush_unlocked;
fgetc_unlocked;
fgets_unlocked;
fputc_unlocked;
fputs_unlocked;
fread_unlocked;
fwrite_unlocked;
getentropy;
getnetent;
getprotoent;

View file

@ -1310,6 +1310,13 @@ LIBC_P { # introduced=P
endnetent;
endprotoent;
fexecve;
fflush_unlocked;
fgetc_unlocked;
fgets_unlocked;
fputc_unlocked;
fputs_unlocked;
fread_unlocked;
fwrite_unlocked;
getentropy;
getnetent;
getprotoent;

View file

@ -1246,6 +1246,13 @@ LIBC_P { # introduced=P
endnetent;
endprotoent;
fexecve;
fflush_unlocked;
fgetc_unlocked;
fgets_unlocked;
fputc_unlocked;
fputs_unlocked;
fread_unlocked;
fwrite_unlocked;
getentropy;
getnetent;
getprotoent;

View file

@ -1308,6 +1308,13 @@ LIBC_P { # introduced=P
endnetent;
endprotoent;
fexecve;
fflush_unlocked;
fgetc_unlocked;
fgets_unlocked;
fputc_unlocked;
fputs_unlocked;
fread_unlocked;
fwrite_unlocked;
getentropy;
getnetent;
getprotoent;

View file

@ -1246,6 +1246,13 @@ LIBC_P { # introduced=P
endnetent;
endprotoent;
fexecve;
fflush_unlocked;
fgetc_unlocked;
fgets_unlocked;
fputc_unlocked;
fputs_unlocked;
fread_unlocked;
fwrite_unlocked;
getentropy;
getnetent;
getprotoent;

View file

@ -1,141 +0,0 @@
/* $OpenBSD: fread.c,v 1.12 2014/05/01 16:40:36 deraadt Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/param.h>
#include "local.h"
#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4))
size_t
fread(void *buf, size_t size, size_t count, FILE *fp) __overloadable
{
CHECK_FP(fp);
/*
* Extension: Catch integer overflow.
*/
if ((size >= MUL_NO_OVERFLOW || count >= MUL_NO_OVERFLOW) &&
size > 0 && SIZE_MAX / size < count) {
errno = EOVERFLOW;
fp->_flags |= __SERR;
return (0);
}
const size_t desired_total = count * size;
size_t total = desired_total;
/*
* ANSI and SUSv2 require a return value of 0 if size or count are 0.
*/
if (total == 0) {
return (0);
}
FLOCKFILE(fp);
_SET_ORIENTATION(fp, -1);
// TODO: how can this ever happen?!
if (fp->_r < 0)
fp->_r = 0;
/*
* Ensure _bf._size is valid.
*/
if (fp->_bf._base == NULL) {
__smakebuf(fp);
}
char* dst = static_cast<char*>(buf);
while (total > 0) {
/*
* Copy data out of the buffer.
*/
size_t buffered_bytes = MIN((size_t) fp->_r, total);
memcpy(dst, fp->_p, buffered_bytes);
fp->_p += buffered_bytes;
fp->_r -= buffered_bytes;
dst += buffered_bytes;
total -= buffered_bytes;
/*
* Are we done?
*/
if (total == 0) {
goto out;
}
/*
* Do we have so much more to read that we should
* avoid copying it through the buffer?
*/
if (total > (size_t) fp->_bf._size) {
/*
* Make sure that fseek doesn't think it can
* reuse the buffer since we are going to read
* directly from the file descriptor.
*/
fp->_flags |= __SMOD;
break;
}
/*
* Less than a buffer to go, so refill the buffer and
* go around the loop again.
*/
if (__srefill(fp)) {
goto out;
}
}
/*
* Read directly into the caller's buffer.
*/
while (total > 0) {
ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total);
if (bytes_read <= 0) {
fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR;
break;
}
dst += bytes_read;
total -= bytes_read;
}
out:
FUNLOCKFILE(fp);
return ((desired_total - total) / size);
}

View file

@ -145,11 +145,12 @@ struct __sfileext {
// #define __SOPT 0x0400 --- historical (do fseek() optimization).
// #define __SNPT 0x0800 --- historical (do not do fseek() optimization).
// #define __SOFF 0x1000 --- historical (set iff _offset is in fact correct).
#define __SMOD 0x2000 // true => fgetln modified _p text.
// #define __SMOD 0x2000 --- historical (set iff fgetln modified _p text).
#define __SALC 0x4000 // Allocate string space dynamically.
#define __SIGN 0x8000 // Ignore this file in _fwalk.
// TODO: remove remaining references to these obsolete flags (see above).
#define __SMOD 0
#define __SNPT 0
#define __SOPT 0
@ -243,10 +244,18 @@ int __vfwscanf(FILE *, const wchar_t *, va_list);
#define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF)))
#define __sgetc(p) (--(p)->_r < 0 ? __srget(p) : (int)(*(p)->_p++))
/* OpenBSD declares these in fvwrite.h but we want to ensure they're hidden. */
struct __suio;
extern int __sfvwrite(FILE *, struct __suio *);
wint_t __fputwc_unlock(wchar_t wc, FILE *fp);
/* OpenBSD declares these in fvwrite.h, but we share them with C++ parts of the implementation. */
struct __siov {
void* iov_base;
size_t iov_len;
};
struct __suio {
struct __siov* uio_iov;
int uio_iovcnt;
size_t uio_resid;
};
int __sfvwrite(FILE*, struct __suio*);
wint_t __fputwc_unlock(wchar_t wc, FILE* fp);
/* Remove the if (!__sdidinit) __sinit() idiom from untouched upstream stdio code. */
extern void __sinit(void); // Not actually implemented.

View file

@ -111,7 +111,6 @@ __srefill(FILE *fp)
}
fp->_p = fp->_bf._base;
fp->_r = (*fp->_read)(fp->_cookie, (char *)fp->_p, fp->_bf._size);
fp->_flags &= ~__SMOD; /* buffer contents are again pristine */
if (fp->_r <= 0) {
if (fp->_r == 0)
fp->_flags |= __SEOF;

View file

@ -91,7 +91,7 @@ FILE* stdin = &__sF[0];
FILE* stdout = &__sF[1];
FILE* stderr = &__sF[2];
struct glue __sglue = { NULL, 3, __sF };
struct glue __sglue = { nullptr, 3, __sF };
static struct glue* lastglue = &__sglue;
class ScopedFileLock {
@ -116,7 +116,7 @@ static glue* moreglue(int n) {
glue* g = reinterpret_cast<glue*>(data);
FILE* p = reinterpret_cast<FILE*>(ALIGN(data + sizeof(*g)));
__sfileext* pext = reinterpret_cast<__sfileext*>(ALIGN(data + sizeof(*g)) + n * sizeof(FILE));
g->next = NULL;
g->next = nullptr;
g->niobs = n;
g->iobs = p;
while (--n >= 0) {
@ -144,7 +144,7 @@ FILE* __sfp(void) {
struct glue *g;
_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
for (g = &__sglue; g != NULL; g = g->next) {
for (g = &__sglue; g != nullptr; g = g->next) {
for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
if (fp->_flags == 0)
goto found;
@ -152,8 +152,7 @@ FILE* __sfp(void) {
/* release lock while mallocing */
_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
if ((g = moreglue(NDYNAMIC)) == NULL)
return (NULL);
if ((g = moreglue(NDYNAMIC)) == nullptr) return nullptr;
_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
lastglue->next = g;
lastglue = g;
@ -161,15 +160,15 @@ FILE* __sfp(void) {
found:
fp->_flags = 1; /* reserve this slot; caller sets real flags */
_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
fp->_p = NULL; /* no current pointer */
fp->_p = nullptr; /* no current pointer */
fp->_w = 0; /* nothing to read or write */
fp->_r = 0;
fp->_bf._base = NULL; /* no buffer */
fp->_bf._base = nullptr; /* no buffer */
fp->_bf._size = 0;
fp->_lbfsize = 0; /* not line buffered */
fp->_file = -1; /* no file */
fp->_lb._base = NULL; /* no line buffer */
fp->_lb._base = nullptr; /* no line buffer */
fp->_lb._size = 0;
_FILEEXT_INIT(fp);
@ -288,8 +287,8 @@ FILE* freopen(const char* file, const char* mode, FILE* fp) {
// Flush the stream; ANSI doesn't require this.
if (fp->_flags & __SWR) __sflush(fp);
// If close is NULL, closing is a no-op, hence pointless.
isopen = fp->_close != NULL;
// If close is null, closing is a no-op, hence pointless.
isopen = (fp->_close != nullptr);
if ((wantfd = fp->_file) < 0 && isopen) {
(*fp->_close)(fp->_cookie);
isopen = 0;
@ -316,8 +315,8 @@ FILE* freopen(const char* file, const char* mode, FILE* fp) {
if (fp->_flags & __SMBF) free(fp->_bf._base);
fp->_w = 0;
fp->_r = 0;
fp->_p = NULL;
fp->_bf._base = NULL;
fp->_p = nullptr;
fp->_bf._base = nullptr;
fp->_bf._size = 0;
fp->_lbfsize = 0;
if (HASUB(fp)) FREEUB(fp);
@ -374,7 +373,7 @@ int fclose(FILE* fp) {
ScopedFileLock sfl(fp);
WCIO_FREE(fp);
int r = fp->_flags & __SWR ? __sflush(fp) : 0;
if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0) {
if (fp->_close != nullptr && (*fp->_close)(fp->_cookie) < 0) {
r = EOF;
}
if (fp->_flags & __SMBF) free(fp->_bf._base);
@ -438,6 +437,36 @@ int ferror(FILE* fp) {
return ferror_unlocked(fp);
}
int __sflush(FILE* fp) {
// Flushing a read-only file is a no-op.
if ((fp->_flags & __SWR) == 0) return 0;
// Flushing a file without a buffer is a no-op.
unsigned char* p = fp->_bf._base;
if (p == nullptr) return 0;
// Set these immediately to avoid problems with longjmp and to allow
// exchange buffering (via setvbuf) in user write function.
int n = fp->_p - p;
fp->_p = p;
fp->_w = (fp->_flags & (__SLBF|__SNBF)) ? 0 : fp->_bf._size;
while (n > 0) {
int written = (*fp->_write)(fp->_cookie, reinterpret_cast<char*>(p), n);
if (written <= 0) {
fp->_flags |= __SERR;
return EOF;
}
n -= written, p += written;
}
return 0;
}
int __sflush_locked(FILE* fp) {
ScopedFileLock sfl(fp);
return __sflush(fp);
}
int __sread(void* cookie, char* buf, int n) {
FILE* fp = reinterpret_cast<FILE*>(cookie);
return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
@ -495,7 +524,7 @@ static off64_t __ftello64_unlocked(FILE* fp) {
// smaller than that in the underlying object.
result -= fp->_r;
if (HASUB(fp)) result -= fp->_ur;
} else if (fp->_flags & __SWR && fp->_p != NULL) {
} else if (fp->_flags & __SWR && fp->_p != nullptr) {
// Writing. Any buffered characters cause the
// position to be greater than that in the
// underlying object.
@ -527,7 +556,7 @@ int __fseeko64(FILE* fp, off64_t offset, int whence, int off_t_bits) {
return -1;
}
if (fp->_bf._base == NULL) __smakebuf(fp);
if (fp->_bf._base == nullptr) __smakebuf(fp);
// Flush unwritten data and attempt the seek.
if (__sflush(fp) || __seek_unlocked(fp, offset, whence) == -1) {
@ -670,11 +699,90 @@ int fgetc(FILE* fp) {
return getc(fp);
}
int fgetc_unlocked(FILE* fp) {
CHECK_FP(fp);
return getc_unlocked(fp);
}
/*
* Read at most n-1 characters from the given file.
* Stop when a newline has been read, or the count runs out.
* Return first argument, or NULL if no characters were read.
* Do not return NULL if n == 1.
*/
char* fgets(char* buf, int n, FILE* fp) __overloadable {
CHECK_FP(fp);
ScopedFileLock sfl(fp);
return fgets_unlocked(buf, n, fp);
}
char* fgets_unlocked(char* buf, int n, FILE* fp) {
if (n <= 0) {
errno = EINVAL;
return nullptr;
}
_SET_ORIENTATION(fp, -1);
char* s = buf;
n--; // Leave space for NUL.
while (n != 0) {
// If the buffer is empty, refill it.
if (fp->_r <= 0) {
if (__srefill(fp)) {
// EOF/error: stop with partial or no line.
if (s == buf) return nullptr;
break;
}
}
size_t len = fp->_r;
unsigned char* p = fp->_p;
// Scan through at most n bytes of the current buffer,
// looking for '\n'. If found, copy up to and including
// newline, and stop. Otherwise, copy entire chunk and loop.
if (len > static_cast<size_t>(n)) len = n;
unsigned char* t = static_cast<unsigned char*>(memchr(p, '\n', len));
if (t != nullptr) {
len = ++t - p;
fp->_r -= len;
fp->_p = t;
memcpy(s, p, len);
s[len] = '\0';
return buf;
}
fp->_r -= len;
fp->_p += len;
memcpy(s, p, len);
s += len;
n -= len;
}
*s = '\0';
return buf;
}
int fputc(int c, FILE* fp) {
CHECK_FP(fp);
return putc(c, fp);
}
int fputc_unlocked(int c, FILE* fp) {
CHECK_FP(fp);
return putc_unlocked(c, fp);
}
int fputs(const char* s, FILE* fp) {
CHECK_FP(fp);
ScopedFileLock sfl(fp);
return fputs_unlocked(s, fp);
}
int fputs_unlocked(const char* s, FILE* fp) {
CHECK_FP(fp);
size_t length = strlen(s);
return (fwrite_unlocked(s, 1, length, fp) == length) ? 0 : EOF;
}
int fscanf(FILE* fp, const char* fmt, ...) {
CHECK_FP(fp);
PRINTF_IMPL(vfscanf(fp, fmt, ap));
@ -723,6 +831,11 @@ wint_t getwchar() {
return fgetwc(stdin);
}
void perror(const char* msg) {
if (msg == nullptr) msg = "";
fprintf(stderr, "%s%s%s\n", msg, (*msg == '\0') ? "" : ": ", strerror(errno));
}
int printf(const char* fmt, ...) {
PRINTF_IMPL(vfprintf(stdout, fmt, ap));
}
@ -754,6 +867,13 @@ int putchar_unlocked(int c) {
return putc_unlocked(c, stdout);
}
int puts(const char* s) {
size_t length = strlen(s);
ScopedFileLock sfl(stdout);
return (fwrite_unlocked(s, 1, length, stdout) == length &&
putc_unlocked('\n', stdout) != EOF) ? 0 : EOF;
}
wint_t putwc(wchar_t wc, FILE* fp) {
CHECK_FP(fp);
return fputwc(wc, fp);
@ -869,6 +989,116 @@ int wscanf(const wchar_t* fmt, ...) {
PRINTF_IMPL(vfwscanf(stdin, fmt, ap));
}
static int fflush_all() {
return _fwalk(__sflush_locked);
}
int fflush(FILE* fp) {
if (fp == nullptr) return fflush_all();
ScopedFileLock sfl(fp);
return fflush_unlocked(fp);
}
int fflush_unlocked(FILE* fp) {
if (fp == nullptr) return fflush_all();
if ((fp->_flags & (__SWR | __SRW)) == 0) {
errno = EBADF;
return EOF;
}
return __sflush(fp);
}
size_t fread(void* buf, size_t size, size_t count, FILE* fp) __overloadable {
CHECK_FP(fp);
ScopedFileLock sfl(fp);
return fread_unlocked(buf, size, count, fp);
}
size_t fread_unlocked(void* buf, size_t size, size_t count, FILE* fp) {
CHECK_FP(fp);
size_t desired_total;
if (__builtin_mul_overflow(size, count, &desired_total)) {
errno = EOVERFLOW;
fp->_flags |= __SERR;
return 0;
}
size_t total = desired_total;
if (total == 0) return 0;
_SET_ORIENTATION(fp, -1);
// TODO: how can this ever happen?!
if (fp->_r < 0) fp->_r = 0;
// Ensure _bf._size is valid.
if (fp->_bf._base == nullptr) __smakebuf(fp);
char* dst = static_cast<char*>(buf);
while (total > 0) {
// Copy data out of the buffer.
size_t buffered_bytes = MIN(static_cast<size_t>(fp->_r), total);
memcpy(dst, fp->_p, buffered_bytes);
fp->_p += buffered_bytes;
fp->_r -= buffered_bytes;
dst += buffered_bytes;
total -= buffered_bytes;
// Are we done?
if (total == 0) goto out;
// Do we have so much more to read that we should avoid copying it through the buffer?
if (total > static_cast<size_t>(fp->_bf._size)) break;
// Less than a buffer to go, so refill the buffer and go around the loop again.
if (__srefill(fp)) goto out;
}
// Read directly into the caller's buffer.
while (total > 0) {
ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total);
if (bytes_read <= 0) {
fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR;
break;
}
dst += bytes_read;
total -= bytes_read;
}
out:
return ((desired_total - total) / size);
}
size_t fwrite(const void* buf, size_t size, size_t count, FILE* fp) {
CHECK_FP(fp);
ScopedFileLock sfl(fp);
return fwrite_unlocked(buf, size, count, fp);
}
size_t fwrite_unlocked(const void* buf, size_t size, size_t count, FILE* fp) {
CHECK_FP(fp);
size_t n;
if (__builtin_mul_overflow(size, count, &n)) {
errno = EOVERFLOW;
fp->_flags |= __SERR;
return 0;
}
if (n == 0) return 0;
__siov iov = { .iov_base = const_cast<void*>(buf), .iov_len = n };
__suio uio = { .uio_iov = &iov, .uio_iovcnt = 1, .uio_resid = n };
_SET_ORIENTATION(fp, -1);
// The usual case is success (__sfvwrite returns 0); skip the divide if this happens,
// since divides are generally slow.
return (__sfvwrite(fp, &uio) == 0) ? count : ((n - uio.uio_resid) / size);
}
namespace {
namespace phony {

View file

@ -1,98 +0,0 @@
/* $OpenBSD: fflush.c,v 1.9 2015/08/31 02:53:57 guenther Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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.
*/
#include <errno.h>
#include <stdio.h>
#include "local.h"
/* Flush a single file, or (if fp is NULL) all files. */
int
fflush(FILE *fp)
{
int r;
if (fp == NULL)
return (_fwalk(__sflush_locked));
FLOCKFILE(fp);
if ((fp->_flags & (__SWR | __SRW)) == 0) {
errno = EBADF;
r = EOF;
} else
r = __sflush(fp);
FUNLOCKFILE(fp);
return (r);
}
DEF_STRONG(fflush);
int
__sflush(FILE *fp)
{
unsigned char *p;
int n, t;
t = fp->_flags;
if ((t & __SWR) == 0)
return (0);
if ((p = fp->_bf._base) == NULL)
return (0);
n = fp->_p - p; /* write this much */
/*
* Set these immediately to avoid problems with longjmp and to allow
* exchange buffering (via setvbuf) in user write function.
*/
fp->_p = p;
fp->_w = t & (__SLBF|__SNBF) ? 0 : fp->_bf._size;
for (; n > 0; n -= t, p += t) {
t = (*fp->_write)(fp->_cookie, (char *)p, n);
if (t <= 0) {
fp->_flags |= __SERR;
return (EOF);
}
}
return (0);
}
int
__sflush_locked(FILE *fp)
{
int r;
FLOCKFILE(fp);
r = __sflush(fp);
FUNLOCKFILE(fp);
return (r);
}

View file

@ -1,106 +0,0 @@
/* $OpenBSD: fgets.c,v 1.16 2016/09/21 04:38:56 guenther Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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.
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "local.h"
/*
* Read at most n-1 characters from the given file.
* Stop when a newline has been read, or the count runs out.
* Return first argument, or NULL if no characters were read.
* Do not return NULL if n == 1.
*/
char *
fgets(char *buf, int n, FILE *fp) __overloadable
{
size_t len;
char *s;
unsigned char *p, *t;
if (n <= 0) { /* sanity check */
errno = EINVAL;
return (NULL);
}
FLOCKFILE(fp);
_SET_ORIENTATION(fp, -1);
s = buf;
n--; /* leave space for NUL */
while (n != 0) {
/*
* If the buffer is empty, refill it.
*/
if (fp->_r <= 0) {
if (__srefill(fp)) {
/* EOF/error: stop with partial or no line */
if (s == buf) {
FUNLOCKFILE(fp);
return (NULL);
}
break;
}
}
len = fp->_r;
p = fp->_p;
/*
* Scan through at most n bytes of the current buffer,
* looking for '\n'. If found, copy up to and including
* newline, and stop. Otherwise, copy entire chunk
* and loop.
*/
if (len > n)
len = n;
t = memchr(p, '\n', len);
if (t != NULL) {
len = ++t - p;
fp->_r -= len;
fp->_p = t;
(void)memcpy(s, p, len);
s[len] = '\0';
FUNLOCKFILE(fp);
return (buf);
}
fp->_r -= len;
fp->_p += len;
(void)memcpy(s, p, len);
s += len;
n -= len;
}
*s = '\0';
FUNLOCKFILE(fp);
return (buf);
}
DEF_STRONG(fgets);

View file

@ -1,59 +0,0 @@
/* $OpenBSD: fputs.c,v 1.11 2015/08/31 02:53:57 guenther Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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.
*/
#include <stdio.h>
#include <string.h>
#include "local.h"
#include "fvwrite.h"
/*
* Write the given string to the given file.
*/
int
fputs(const char *s, FILE *fp)
{
struct __suio uio;
struct __siov iov;
int ret;
iov.iov_base = (void *)s;
iov.iov_len = uio.uio_resid = strlen(s);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
FLOCKFILE(fp);
_SET_ORIENTATION(fp, -1);
ret = __sfvwrite(fp, &uio);
FUNLOCKFILE(fp);
return (ret);
}
DEF_STRONG(fputs);

View file

@ -32,20 +32,4 @@
* SUCH DAMAGE.
*/
/*
* I/O descriptors for __sfvwrite().
*/
struct __siov {
void *iov_base;
size_t iov_len;
};
struct __suio {
struct __siov *uio_iov;
int uio_iovcnt;
int uio_resid;
};
__BEGIN_HIDDEN_DECLS
extern int __sfvwrite(FILE *, struct __suio *);
wint_t __fputwc_unlock(wchar_t wc, FILE *fp);
__END_HIDDEN_DECLS
/* Moved to "local.h". */

View file

@ -1,89 +0,0 @@
/* $OpenBSD: fwrite.c,v 1.12 2015/08/31 02:53:57 guenther Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include "local.h"
#include "fvwrite.h"
#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4))
/*
* Write `count' objects (each size `size') from memory to the given file.
* Return the number of whole objects written.
*/
size_t
fwrite(const void *buf, size_t size, size_t count, FILE *fp) __overloadable
{
size_t n;
struct __suio uio;
struct __siov iov;
int ret;
/*
* Extension: Catch integer overflow
*/
if ((size >= MUL_NO_OVERFLOW || count >= MUL_NO_OVERFLOW) &&
size > 0 && SIZE_MAX / size < count) {
errno = EOVERFLOW;
fp->_flags |= __SERR;
return (0);
}
/*
* ANSI and SUSv2 require a return value of 0 if size or count are 0.
*/
if ((n = count * size) == 0)
return (0);
iov.iov_base = (void *)buf;
uio.uio_resid = iov.iov_len = n;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
/*
* The usual case is success (__sfvwrite returns 0);
* skip the divide if this happens, since divides are
* generally slow and since this occurs whenever size==0.
*/
FLOCKFILE(fp);
_SET_ORIENTATION(fp, -1);
ret = __sfvwrite(fp, &uio);
FUNLOCKFILE(fp);
if (ret == 0)
return (count);
return ((n - uio.uio_resid) / size);
}
DEF_STRONG(fwrite);

View file

@ -1,63 +0,0 @@
/* $OpenBSD: perror.c,v 1.9 2015/08/31 02:53:57 guenther Exp $ */
/*
* Copyright (c) 1988, 1993
* The Regents of the University of California. 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.
* 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.
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
void
perror(const char *s)
{
struct iovec *v;
struct iovec iov[4];
char buf[NL_TEXTMAX];
v = iov;
if (s && *s) {
v->iov_base = (char *)s;
v->iov_len = strlen(s);
v++;
v->iov_base = ": ";
v->iov_len = 2;
v++;
}
(void)strerror_r(errno, buf, sizeof(buf));
v->iov_base = buf;
v->iov_len = strlen(v->iov_base);
v++;
v->iov_base = "\n";
v->iov_len = 1;
(void)writev(STDERR_FILENO, iov, (v - iov) + 1);
}
DEF_STRONG(perror);

View file

@ -1,63 +0,0 @@
/* $OpenBSD: puts.c,v 1.12 2015/08/31 02:53:57 guenther Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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.
*/
#include <stdio.h>
#include <string.h>
#include "local.h"
#include "fvwrite.h"
/*
* Write the given string to stdout, appending a newline.
*/
int
puts(const char *s)
{
size_t c = strlen(s);
struct __suio uio;
struct __siov iov[2];
int ret;
iov[0].iov_base = (void *)s;
iov[0].iov_len = c;
iov[1].iov_base = "\n";
iov[1].iov_len = 1;
uio.uio_resid = c + 1;
uio.uio_iov = &iov[0];
uio.uio_iovcnt = 2;
FLOCKFILE(stdout);
_SET_ORIENTATION(stdout, -1);
ret = __sfvwrite(stdout, &uio);
FUNLOCKFILE(stdout);
return (ret ? EOF : '\n');
}
DEF_STRONG(puts);

View file

@ -1962,3 +1962,61 @@ TEST(STDIO_TEST, constants) {
ASSERT_LE(FILENAME_MAX, PATH_MAX);
ASSERT_EQ(L_tmpnam, PATH_MAX);
}
TEST(STDIO_TEST, perror) {
ExecTestHelper eth;
eth.Run([&]() { errno = EINVAL; perror("a b c"); exit(0); }, 0, "a b c: Invalid argument\n");
eth.Run([&]() { errno = EINVAL; perror(nullptr); exit(0); }, 0, "Invalid argument\n");
eth.Run([&]() { errno = EINVAL; perror(""); exit(0); }, 0, "Invalid argument\n");
}
TEST(STDIO_TEST, puts) {
ExecTestHelper eth;
eth.Run([&]() { exit(puts("a b c")); }, 0, "a b c\n");
}
TEST(STDIO_TEST, unlocked) {
TemporaryFile tf;
FILE* fp = fopen(tf.filename, "w+");
ASSERT_TRUE(fp != nullptr);
clearerr_unlocked(fp);
ASSERT_FALSE(feof_unlocked(fp));
ASSERT_FALSE(ferror_unlocked(fp));
ASSERT_EQ(fileno(fp), fileno_unlocked(fp));
ASSERT_NE(EOF, putc_unlocked('a', fp));
ASSERT_NE(EOF, putc('b', fp));
ASSERT_NE(EOF, fputc_unlocked('c', fp));
ASSERT_NE(EOF, fputc('d', fp));
rewind(fp);
ASSERT_EQ('a', getc_unlocked(fp));
ASSERT_EQ('b', getc(fp));
ASSERT_EQ('c', fgetc_unlocked(fp));
ASSERT_EQ('d', fgetc(fp));
rewind(fp);
ASSERT_EQ(2U, fwrite_unlocked("AB", 1, 2, fp));
ASSERT_EQ(2U, fwrite("CD", 1, 2, fp));
ASSERT_EQ(0, fflush_unlocked(fp));
rewind(fp);
char buf[BUFSIZ] = {};
ASSERT_EQ(2U, fread_unlocked(&buf[0], 1, 2, fp));
ASSERT_EQ(2U, fread(&buf[2], 1, 2, fp));
ASSERT_STREQ("ABCD", buf);
rewind(fp);
ASSERT_NE(EOF, fputs("hello ", fp));
ASSERT_NE(EOF, fputs_unlocked("world", fp));
ASSERT_NE(EOF, fputc('\n', fp));
rewind(fp);
ASSERT_TRUE(fgets_unlocked(buf, sizeof(buf), fp) != nullptr);
ASSERT_STREQ("hello world\n", buf);
ASSERT_EQ(0, fclose(fp));
}