Files
AnyPS5/core/libs/prx/libc/src/StandardStreams.cpp
T
fablevoidandClaude Opus 5.5 17e03d3c0a fix(libc): fail fgets with a size of 0 or less like FreeBSD
The guest fgets handed the size straight to the host. With a size of 0
glibc and the Windows runtime return NULL without setting errno or the
stream error indicator, and a negative size goes to the Windows
invalid-parameter path. FreeBSD's fgets checks n <= 0 before reading:
it sets __SERR on the stream, sets errno to EINVAL and returns NULL,
leaving the buffer and the file position untouched.

fgets_nid_postfix now does the same before calling the host. The
FileStream flag it uses was only set for encoding errors so far and is
renamed from SetEncodingError/encodingError to SetError/guestError; it
already stays set until clearerr like __SERR.

guest_standard_streams checks sizes 0, -1 and INT_MIN (NULL, errno 22,
error indicator set and cleared by clearerr, buffer and position
untouched) and that a following read still returns the line; it fails
without the change.

Fixes #3084

AI-assisted: yes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:49:43 +00:00

114 lines
3.9 KiB
C++

#include "prx/libc/include/FileStream.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cerrno>
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
namespace { FileStream input{stdin}; }
extern "C" {
FileStream* __stdinp_nid_postfix = &input;
FileStream* __stdoutp_nid_postfix = &_Stdout_nid_postfix;
FileStream* __stderrp_nid_postfix = &_Stderr_nid_postfix;
int __isthreaded_nid_postfix = 1;
int APS5_VABI fgetc_nid_postfix(FileStream* stream) {
const int result = std::fgetc(GetNativeStream(stream));
stream->SyncStatus();
return result;
}
std::int32_t APS5_VABI fgetwc_nid_postfix(FileStream* stream) {
return fgetc_nid_postfix(stream);
}
std::int32_t APS5_VABI ungetwc_nid_postfix(std::int32_t value, FileStream* stream) {
if (value == -1) return -1;
if (value < 0 || value > 255) {
errno = 86;
stream->SetError();
return -1;
}
const int result = std::ungetc(value, GetNativeStream(stream));
stream->SyncStatus();
return result;
}
int APS5_VABI getc_nid_postfix(FileStream* stream) { return fgetc_nid_postfix(stream); }
int APS5_VABI getc_unlocked_nid_postfix(FileStream* stream) {
#ifdef _WIN32
const int result = _fgetc_nolock(GetNativeStream(stream));
#else
const int result = ::getc_unlocked(GetNativeStream(stream));
#endif
stream->SyncUnlockedStatus();
return result;
}
int APS5_VABI __srget_nid_postfix(FileStream* stream) { return fgetc_nid_postfix(stream); }
int APS5_VABI getchar_nid_postfix() { return fgetc_nid_postfix(__stdinp_nid_postfix); }
int APS5_VABI fputc_nid_postfix(int value, FileStream* stream) {
const int result = std::fputc(value, GetNativeStream(stream));
stream->SyncStatus();
return result;
}
int APS5_VABI putc_nid_postfix(int value, FileStream* stream) { return fputc_nid_postfix(value, stream); }
int APS5_VABI putc_unlocked_nid_postfix(int value, FileStream* stream) {
#ifdef _WIN32
const int result = _fputc_nolock(value, GetNativeStream(stream));
#else
const int result = ::putc_unlocked(value, GetNativeStream(stream));
#endif
stream->SyncUnlockedStatus();
return result;
}
int APS5_VABI __swbuf_nid_postfix(int value, FileStream* stream) { return fputc_nid_postfix(value, stream); }
int APS5_VABI putchar_nid_postfix(int value) { return fputc_nid_postfix(value, __stdoutp_nid_postfix); }
int APS5_VABI ungetc_nid_postfix(int value, FileStream* stream) {
const int result = std::ungetc(value, GetNativeStream(stream));
stream->SyncStatus();
return result;
}
char* APS5_VABI fgets_nid_postfix(char* buffer, int size, FileStream* stream) {
if (size <= 0) {
stream->SetError();
errno = 22;
return nullptr;
}
auto* result = std::fgets(buffer, size, GetNativeStream(stream));
stream->SyncStatus();
return result;
}
int APS5_VABI feof_nid_postfix(FileStream* stream) {
stream->SyncStatus();
return (stream->GuestState().flags & 0x20) != 0;
}
int APS5_VABI ferror_nid_postfix(FileStream* stream) {
stream->SyncStatus();
return (stream->GuestState().flags & 0x40) != 0;
}
void APS5_VABI clearerr_nid_postfix(FileStream* stream) {
stream->ClearError();
}
int APS5_VABI fileno_nid_postfix(FileStream* stream) {
#ifdef _WIN32
const int descriptor = _fileno(GetNativeStream(stream));
#else
const int descriptor = ::fileno(GetNativeStream(stream));
#endif
stream->GuestState().descriptor = static_cast<std::int16_t>(descriptor);
return descriptor;
}
int APS5_VABI setvbuf_nid_postfix(FileStream* stream, char* buffer, int mode, std::size_t size) {
if (mode < 0 || mode > 2) return -1;
const int native = mode == 0 ? _IOFBF : mode == 1 ? _IOLBF : _IONBF;
if (native != _IONBF && size == 0) {
buffer = nullptr;
size = BUFSIZ;
}
return std::setvbuf(GetNativeStream(stream), buffer, native, size);
}
void APS5_VABI setbuf_nid_postfix(FileStream* stream, char* buffer) {
setvbuf_nid_postfix(stream, buffer, buffer ? 0 : 2, buffer ? 1024 : 0);
}
}