// Check a FILE* isn't nullptr, so we can emit a clear diagnostic message // instead of just crashing with SIGSEGV. #define CHECK_FP(fp) \ if (fp == nullptr) __fortify_fatal("%s: null FILE*", __FUNCTION__)
#define NDYNAMIC 10/* add ten more whenever necessary */
#define PRINTF_IMPL(expr) \
va_list ap; \
va_start(ap, fmt); \ int result = (expr); \
va_end(ap); \ return result;
// __sF is exported for backwards compatibility. Until M, we didn't have symbols // for stdin/stdout/stderr; they were macros accessing __sF.
FILE __sF[3] = {
MAKE_STD_STREAM(__SRD, STDIN_FILENO),
MAKE_STD_STREAM(__SWR, STDOUT_FILENO),
MAKE_STD_STREAM(__SWR|__SNBF, STDERR_FILENO),
};
static uint64_t __get_file_tag(FILE* fp) { // Don't use a tag for the standard streams. // They don't really own their file descriptors, because the values are well-known, and you're // allowed to do things like `close(STDIN_FILENO); open("foo", O_RDONLY)` when single-threaded. if (fp == stdin || fp == stderr || fp == stdout) { return0;
}
// The first few FILEs are statically allocated; others are dynamically // allocated and linked in via this glue structure. // TODO: replace this with an intrusive doubly-linked list of the FILE*s (via _EXT()) struct glue { struct glue* next; int niobs;
FILE* iobs;
}; static pthread_mutex_t __sglue_mutex = PTHREAD_MUTEX_INITIALIZER; struct glue __sglue = { nullptr, 3, __sF }; staticstruct glue* lastglue = &__sglue;
// Caller sets cookie, _read/_write etc. // We explicitly clear _seek and _seek64 to prevent subtle bugs.
fp->_seek = nullptr;
_EXT(fp)->_seek64 = nullptr;
return fp;
}
int _fwalk(int (*callback)(FILE*)) {
pthread_mutex_lock(&__sglue_mutex); int result = 0; for (glue* g = &__sglue; g != nullptr; g = g->next) {
FILE* fp = g->iobs; for (int n = g->niobs; --n >= 0; ++fp) {
ScopedFileLock sfl(fp); if (fp->_flags != 0) {
result |= (*callback)(fp);
}
}
}
pthread_mutex_unlock(&__sglue_mutex); return result;
}
extern"C" __LIBC_HIDDEN__ void __libc_stdio_cleanup() { // This matches our historical behavior, but what about code that writes after this runs? // glibc sets all streams to unbuffered (so future writes go straight out). // musl takes the locks but keeps them held (so future writes deadlock).
fflush(nullptr);
}
/* *Refillastdiobuffer. *ReturnEOFoneoforerror,0otherwise.
*/ int __srefill(FILE* fp) {
fp->_r = 0; /* largely a convenience for callers */
/* if not already reading, have to be reading and writing */ if ((fp->_flags & __SRD) == 0) { if ((fp->_flags & __SRW) == 0) {
errno = EBADF;
fp->_flags |= __SERR; return EOF;
} /* switch to reading */ if (fp->_flags & __SWR) { if (__sflush(fp)) return EOF;
fp->_flags &= ~__SWR;
fp->_w = 0;
fp->_lbfsize = 0;
}
fp->_flags |= __SRD;
} else { /* *Wewerereading.Ifthereisanungetcbuffer, *wemusthavebeenreadingfromthat.Dropit, *restoringthepreviousbuffer(ifany).Ifthere *isanythinginthatbuffer,return.
*/ if (HASUB(fp)) {
FREEUB(fp); if ((fp->_r = fp->_ur) != 0) {
fp->_p = fp->_up; return0;
}
}
}
if (fp->_bf._base == NULL) __smakebuf(fp);
// If we're about to read from an unbuffered or line-buffered stream, what do we need to do? // // Everyone points to the ISO C standard, but disagree about how to interpret it. // The two most important sentences are C23 5.1.2.3 paragraph 6 bullet 3 which says that // "the intent ... is that unbuffered or line-buffered output appear as soon as possible, // to ensure that prompting messages appear prior to a program waiting for input", // and C23 7.23.3 paragraph 3 which qualifies the extended description of these behaviors with // "Support for these characteristics is implementation-defined". // // In practice, BSD flushes all streams, glibc only flushes stdout, and musl flushes no streams. // // Being BSD-derived, bionic historically flushed all streams. This was never implemented in a // completely thread-safe manner, and all attempts to fix that failed by introducing deadlocks. // Upstream BSD even adds a __SIGN ("ignore") flag as a partial workaround, but that only covers // the per-file locks' deadlocks, not deadlocks that would be caused by adding the missing locking // of the _list_ of streams. // // Long term I think we should move to the musl model, but the simple safe choice is to behave // like glibc and just flush stdout. (A transition glibc made in 2004, having had inherited BSD // behavior before then.) if (fp->_flags & (__SLBF|__SNBF)) { // We use fflush() rather than __sflush() for stdout since we don't hold the stdout lock.
fflush(stdout);
// Now flush _this_ file without locking it (because our caller should have either taken the // lock or know it's in a context -- such as fread_unlocked() -- where the lock isn't needed). if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR)) __sflush(fp);
}
fp->_p = fp->_bf._base;
fp->_r = (*fp->_read)(fp->_cookie, reinterpret_cast<char*>(fp->_p), fp->_bf._size); if (fp->_r <= 0) { if (fp->_r == 0) {
fp->_flags |= __SEOF;
} else {
fp->_r = 0;
fp->_flags |= __SERR;
} return EOF;
} return0;
}
// Handle getc() when the buffer ran out: refill then return the first new byte buffer. int __srget(FILE* fp) {
_SET_ORIENTATION(fp, -1); if (__srefill(fp) == 0) {
fp->_r--; return (*fp->_p++);
} return EOF;
}
/* *Allocateafilebuffer,orswitchtounbufferedI/O. *PertheANSICstandard,ALLttydevicesdefaulttolinebuffered.
*/ void __smakebuf(FILE* fp) { unsignedchar *p; int flags = 0;
size_t size; int couldbetty;
// For append mode, O_APPEND sets the write position for free, but we need to // set the read position manually. if ((mode_flags & O_APPEND) != 0) __sseek64(fp, 0, SEEK_END); return fp;
}
__strong_alias(fopen64, fopen);
FILE* fdopen(int fd, constchar* mode) { int mode_flags; int flags = __sflags(mode, &mode_flags); if (flags == 0) return nullptr;
// Make sure the mode the user wants is a subset of the actual mode. int fd_flags = fcntl(fd, F_GETFL, 0); if (fd_flags == -1) return nullptr; int tmp = fd_flags & O_ACCMODE; if (tmp != O_RDWR && (tmp != (mode_flags & O_ACCMODE))) {
errno = EINVAL; return nullptr;
}
// Make sure O_APPEND is set on the underlying fd if our mode has 'a'. // POSIX says we just take the current offset of the underlying fd. if ((mode_flags & O_APPEND) && !(fd_flags & O_APPEND)) { if (fcntl(fd, F_SETFL, fd_flags | O_APPEND) == -1) return nullptr;
}
// Make sure O_CLOEXEC is set on the underlying fd if our mode has 'e'. if ((mode_flags & O_CLOEXEC) && !((tmp = fcntl(fd, F_GETFD)) & FD_CLOEXEC)) {
fcntl(fd, F_SETFD, tmp | FD_CLOEXEC);
}
// POSIX says: "If pathname is a null pointer, the freopen() function shall // attempt to change the mode of the stream to that specified by mode, as if // the name of the file currently associated with the stream had been used. In // this case, the file descriptor associated with the stream need not be // closed if the call to freopen() succeeds. It is implementation-defined // which changes of mode are permitted (if any), and under what // circumstances." // // Linux is quite restrictive about what changes you can make with F_SETFL, // and in particular won't let you touch the access bits. It's easiest and // most effective to just rely on /proc/self/fd/...
FdPath fd_path(fp->_file); if (file == nullptr) file = fd_path.c_str();
int mode_flags; int flags = __sflags(mode, &mode_flags); if (flags == 0) {
fclose(fp); return nullptr;
}
// TODO: rewrite this mess completely.
// There are actually programs that depend on being able to "freopen" // descriptors that weren't originally open. Keep this from breaking. // Remember whether the stream was open to begin with, and which file // descriptor (if any) was associated with it. If it was attached to // a descriptor, defer closing it; freopen("/dev/stdin", "r", stdin) // should work. This is unnecessary if it was not a Unix file. int isopen, wantfd; if (fp->_flags == 0) {
fp->_flags = __SEOF; // Hold on to it.
isopen = 0;
wantfd = -1;
} else { // 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 != nullptr); if ((wantfd = fp->_file) < 0 && isopen) {
(*fp->_close)(fp->_cookie);
isopen = 0;
}
}
// Get a new descriptor to refer to the new file. int fd = open(file, mode_flags, DEFFILEMODE); if (fd < 0 && isopen) { // If out of fd's close the old one and try again. if (errno == ENFILE || errno == EMFILE) {
(*fp->_close)(fp->_cookie);
isopen = 0;
fd = open(file, mode_flags, DEFFILEMODE);
}
}
int sverrno = errno;
// Finish closing fp. Even if the open succeeded above, we cannot // keep fp->_base: it may be the wrong size. This loses the effect // of any setbuffer calls, but stdio has always done this before. if (isopen && fd != wantfd) (*fp->_close)(fp->_cookie); if (fp->_flags & __SMBF) free(fp->_bf._base);
fp->_w = 0;
fp->_r = 0;
fp->_p = nullptr;
fp->_bf._base = nullptr;
fp->_bf._size = 0;
fp->_lbfsize = 0; if (HASUB(fp)) FREEUB(fp);
_UB(fp)._size = 0;
WCIO_FREE(fp);
free_fgetln_buffer(fp);
if (fd < 0) { // Did not get it after all.
fp->_flags = 0; // Release.
errno = sverrno; // Restore errno in case _close clobbered it. return nullptr;
}
// If reopening something that was open before on a real file, try // to maintain the descriptor. Various C library routines (perror) // assume stderr is always fd STDERR_FILENO, even if being freopen'd. if (wantfd >= 0 && fd != wantfd) { if (dup3(fd, wantfd, mode_flags & O_CLOEXEC) >= 0) {
close(fd);
fd = wantfd;
}
}
__FILE_init(fp, fd, flags);
// For append mode, O_APPEND sets the write position for free, but we need to // set the read position manually. if ((mode_flags & O_APPEND) != 0) __sseek64(fp, 0, SEEK_END);
return fp;
}
__strong_alias(freopen64, freopen);
int fclose(FILE* fp) {
CHECK_FP(fp);
ScopedFileLock sfl(fp);
WCIO_FREE(fp); int r = fp->_flags & __SWR ? __sflush(fp) : 0; if (fp->_close != nullptr && (*fp->_close)(fp->_cookie) < 0) {
r = EOF;
} if (fp->_flags & __SMBF) free(fp->_bf._base); if (HASUB(fp)) FREEUB(fp);
free_fgetln_buffer(fp);
// If we were created by popen(3), wait for the child.
pid_t pid = _EXT(fp)->_popen_pid; if (pid > 0) { int status; if (TEMP_FAILURE_RETRY(wait4(pid, &status, 0, nullptr)) != -1) {
r = status;
}
}
_EXT(fp)->_popen_pid = 0;
// Poison this FILE so accesses after fclose will be obvious.
fp->_file = -1;
fp->_r = fp->_w = 0;
// Release this FILE for reuse.
fp->_flags = 0; return r;
}
__strong_alias(pclose, fclose);
int fileno_unlocked(FILE* fp) {
CHECK_FP(fp); int fd = fp->_file; if (fd == -1) {
errno = EBADF; return -1;
} return fd;
}
int fileno(FILE* fp) {
CHECK_FP(fp);
ScopedFileLock sfl(fp); return fileno_unlocked(fp);
}
int feof(FILE* fp) {
CHECK_FP(fp);
ScopedFileLock sfl(fp); return feof_unlocked(fp);
}
int ferror_unlocked(FILE* fp) {
CHECK_FP(fp); return __sferror(fp);
}
int ferror(FILE* fp) {
CHECK_FP(fp);
ScopedFileLock sfl(fp); return ferror_unlocked(fp);
}
int __sflush(FILE* fp) { // Flushing is a no-op if we're not currently writing. if ((fp->_flags & __SWR) == 0) return0;
// Flushing a file without a buffer is a no-op. unsignedchar* p = fp->_bf._base; if (p == nullptr) return0;
// 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;
} return0;
}
int __sread(void* cookie, char* buf, int n) {
FILE* fp = reinterpret_cast<FILE*>(cookie); return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
}
int __swrite(void* cookie, constchar* buf, int n) {
FILE* fp = reinterpret_cast<FILE*>(cookie); return TEMP_FAILURE_RETRY(write(fp->_file, buf, n));
}
static off64_t __seek_unlocked(FILE* fp, off64_t offset, int whence) { // Use `_seek64` if set, but fall back to `_seek`. if (_EXT(fp)->_seek64 != nullptr) { return (*_EXT(fp)->_seek64)(fp->_cookie, offset, whence);
} elseif (fp->_seek != nullptr) {
off64_t result = (*fp->_seek)(fp->_cookie, offset, whence); #if !defined(__LP64__) // Avoid sign extension if off64_t is larger than off_t. if (result != -1) result &= 0xffffffff; #endif return result;
} else {
errno = ESPIPE; return -1;
}
}
static off64_t __ftello64_unlocked(FILE* fp) { // Find offset of underlying I/O object, then adjust for buffered bytes.
__sflush(fp); // May adjust seek offset on append stream.
off64_t result = __seek_unlocked(fp, 0, SEEK_CUR); if (result == -1) { return -1;
}
if (fp->_flags & __SRD) { // Reading. Any unread characters (including // those from ungetc) cause the position to be // smaller than that in the underlying object.
result -= fp->_r; if (HASUB(fp)) result -= fp->_ur;
} elseif (fp->_flags & __SWR && fp->_p != nullptr) { // Writing. Any buffered characters cause the // position to be greater than that in the // underlying object.
result += fp->_p - fp->_bf._base;
} return result;
}
staticint __fseeko64(FILE* fp, off64_t offset, int whence, int off_t_bits) {
ScopedFileLock sfl(fp);
// Change any SEEK_CUR to SEEK_SET, and check `whence` argument. // After this, whence is either SEEK_SET or SEEK_END. if (whence == SEEK_CUR) {
fpos64_t current_offset = __ftello64_unlocked(fp); if (current_offset == -1) { return -1;
}
offset += current_offset;
whence = SEEK_SET;
} elseif (whence != SEEK_SET && whence != SEEK_END) {
errno = EINVAL; return -1;
}
// If our caller has a 32-bit interface, refuse to go past a 32-bit file offset. if (off_t_bits == 32 && offset > LONG_MAX) {
errno = EOVERFLOW; return -1;
}
if (fp->_bf._base == nullptr) __smakebuf(fp);
// Flush unwritten data and attempt the seek. if (__sflush(fp) || __seek_unlocked(fp, offset, whence) == -1) { return -1;
}
// Traditional implementations required support for dynamically growing the // buffer pointed to by a FILE*, and would then trim over-large cases. // FreeBSD (and thus iOS) used linear 128 byte growth, NetBSD doubled, and // OpenBSD (and thus Android) had a page-based compromise that reduced the // number of realloc() calls for long strings, but made it more likely that // you'd need one final pass to shrink back to size. // Rather than get involved in all that, we solve the 99% case with a stack // buffer and a single exact-size allocation+memcpy(), and handle the 1% case // by using the required size returned by the first vsnprintf() to do a single // exact-size allocation followed by another call to vsnprintf(). int vasprintf(char** s, constchar* fmt, va_list ap) {
va_list ap2;
va_copy(ap2, ap);
char buf[BUFSIZ] __attribute__((__uninitialized__)); int n = vsnprintf(buf, sizeof(buf), fmt, ap); if (n == -1) return -1;
char* result = static_cast<char*>(malloc(n + 1)); if (result == nullptr) return -1;
if (n < static_cast<int>(sizeof(buf))) {
memcpy(result, buf, n + 1);
} else {
vsnprintf(result, n + 1, fmt, ap2);
}
int fgetc(FILE* fp) {
CHECK_FP(fp); return getc(fp);
}
int fgetc_unlocked(FILE* fp) {
CHECK_FP(fp); return getc_unlocked(fp);
}
char* fgetln(FILE* fp, size_t* length_ptr) {
CHECK_FP(fp);
ScopedFileLock sfl(fp); // Implementing fgetln() in terms of getdelim() means lines are actually always NUL terminated. // We could explicitly overwrite the NUL to be "bug compatible", but that seems silly?
ssize_t n = getdelim(reinterpret_cast<char**>(&fp->fgetln_buffer._base),
&fp->fgetln_buffer._size, '\n', fp); if (n <= 0) return nullptr;
*length_ptr = n; returnreinterpret_cast<char*>(fp->fgetln_buffer._base);
}
char* fgets(char* buf, int n, FILE* fp) {
CHECK_FP(fp);
ScopedFileLock sfl(fp); return fgets_unlocked(buf, n, fp);
}
// Reads at most n-1 characters from the given file. // Stops when a newline has been read, or the count runs out. // Returns first argument, or nullptr if no characters were read. // Does not return nullptr if n == 1. char* fgets_unlocked(char* buf, int n, FILE* fp) { if (n <= 0) __fortify_fatal("fgets: buffer size %d <= 0", n);
_SET_ORIENTATION(fp, ORIENT_BYTES);
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;
}
}
// Scan through at most n bytes of the current buffer, looking for '\n'.
size_t len = fp->_r; unsignedchar* p = fp->_p; if (len > static_cast<size_t>(n)) len = n; unsignedchar* t = static_cast<unsignedchar*>(memchr(p, '\n', len)); // If found, copy up to and including newline and stop. if (t != nullptr) {
len = ++t - p;
fp->_r -= len;
fp->_p = t;
memcpy(s, p, len);
s[len] = '\0'; return buf;
} // Otherwise, copy entire chunk and loop.
fp->_r -= len;
fp->_p += len;
memcpy(s, p, len);
s += len;
n -= len;
}
*s = '\0'; return buf;
}
int vsnprintf(char* s, size_t n, constchar* fmt, va_list ap) { // stdio internals use int rather than size_t.
static_assert(INT_MAX <= SSIZE_MAX, "SSIZE_MAX too large to fit in int");
__check_count("vsnprintf", "size", n);
// Stdio internals do not deal correctly with zero length buffer. char one_byte_buffer[1]; if (n == 0) {
s = one_byte_buffer;
n = 1;
}
int vswscanf(constwchar_t* str, constwchar_t* fmt, va_list ap) { // We convert the wide character string to multibyte, which __vfwscanf() will convert back to // wide characters, but no-one really cares about the wchar_t stuff so this isn't worth improving.
size_t len = wcslen(str) * MB_CUR_MAX; char* mbstr = static_cast<char*>(malloc(len + 1)); if (mbstr == nullptr) return EOF;
size_t total = desired_total; if (total == 0) return0;
_SET_ORIENTATION(fp, ORIENT_BYTES);
// 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) { // The _read function pointer takes an int instead of a size_t. int chunk_size = MIN(total, INT_MAX);
ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, chunk_size); if (bytes_read <= 0) {
fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR; break;
}
dst += bytes_read;
total -= bytes_read;
}
// 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);
}
int ftrylockfile(FILE* fp) {
CHECK_FP(fp); // The specification for ftrylockfile() says it returns 0 on success, // or non-zero on error. We don't bother canonicalizing to 0/-1... return pthread_mutex_trylock(&_EXT(fp)->_lock);
}
int fwide(FILE* fp, int mode) {
CHECK_FP(fp);
ScopedFileLock sfl(fp); if (mode != 0) _SET_ORIENTATION(fp, mode); return WCIO_GET(fp)->orientation;
}
namespace {
namespace phony { #include <bits/struct_file.h>
}
static_assert(sizeof(::__sFILE) == sizeof(phony::__sFILE), "size mismatch between `struct __sFILE` implementation and public stub");
static_assert(alignof(::__sFILE) == alignof(phony::__sFILE), "alignment mismatch between `struct __sFILE` implementation and public stub");
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.35 Sekunden
(vorverarbeitet am 2026-06-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.