// SafeC Standard Library — I/O (C11/C17/C23 stdio coverage)
// Wraps stdio functions for formatted, raw, and interactive I/O.
// Explicit extern declarations are used because macOS stdio.h uses FILE*,
// __attribute__, and macros that SafeC's parser cannot handle directly.
#pragma once
#include <std/stddef.h>
#include <std/io.h>
#include <std/stderr_compat.h>
// ── Explicit extern declarations for stdio functions ─────────────────────────
namespace std {
extern int printf(const char* fmt, ...);
extern int fprintf(CFile_* stream, const char* fmt, ...);
extern int fputs(const char* s, CFile_* stream);
extern int fputc(int c, CFile_* stream);
extern int fflush(CFile_* stream);
extern int puts(const char* s);
extern int putchar(int c);
extern char* fgets(char* buf, int n, CFile_* stream);
extern int getchar();
extern int ungetc(int c, CFile_* stream);
extern int scanf(const char* fmt, ...);
extern int fscanf(CFile_* stream, const char* fmt, ...);
extern int sscanf(const char* buf, const char* fmt, ...);
extern int snprintf(char* buf, size_t n, const char* fmt, ...);
extern int remove(const char* path);
extern int rename(const char* oldpath, const char* newpath);
extern CFile_* tmpfile();
extern size_t strlen(const char* s);
// ── FILE* stream handles ──────────────────────────────────────────────────────
// stdin/stdout/stderr are each a macro on every libc here, never a plain
// fixed symbol under one universal name — the underlying handle differs by
// platform:
// - macOS/BSD: '__stdinp'/'__stdoutp'/'__stderrp', real exported globals.
// - glibc: 'stdin'/'stdout'/'stderr' themselves, exported globals.
// - MSVC/UCRT: no exported globals at all; each macro expands to a call,
// '__acrt_iob_func(0/1/2)' (fd-style index order).
// SAFEC_STDERR_ (stderr specifically) comes from stderr_compat.h, shared
// with mem.sc/assert.sc/etc. stdin/stdout only matter here, so they're
// declared locally with the same platform split rather than growing that
// shared header for consumers that don't need them.
#if defined(__APPLE__)
extern CFile_* __stdinp;
extern CFile_* __stdoutp;
#define SAFEC_STDIN_ __stdinp
#define SAFEC_STDOUT_ __stdoutp
#elif defined(_WIN32)
#define SAFEC_STDIN_ __acrt_iob_func(0)
#define SAFEC_STDOUT_ __acrt_iob_func(1)
#else
extern CFile_* stdin;
extern CFile_* stdout;
#define SAFEC_STDIN_ stdin
#define SAFEC_STDOUT_ stdout
#endif
// ── Basic stdout output ───────────────────────────────────────────────────────
// Print a string without a trailing newline.
inline void print(const &char s) {
unsafe { fputs(s, SAFEC_STDOUT_); }
}
// Print a string followed by a newline.
inline void println(const &char s) {
unsafe { puts(s); }
}
// Print a string to stderr without a trailing newline.
inline void eprint(const &char s) {
unsafe { fputs(s, SAFEC_STDERR_); }
}
// Print a string to stderr followed by a newline.
inline void eprintln(const &char s) {
unsafe {
fputs(s, SAFEC_STDERR_);
fputc('\n', SAFEC_STDERR_);
}
}
// Print a single character to stdout.
inline void print_char(int c) {
unsafe { putchar(c); }
}
// Print a signed 64-bit integer to stdout.
inline void print_int(long long v) {
unsafe { printf("%lld", v); }
}
// Print a double to stdout.
inline void print_float(double v) {
unsafe { printf("%g", v); }
}
// Print a signed 64-bit integer followed by newline.
inline void println_int(long long v) {
unsafe { printf("%lld\n", v); }
}
// Print a double followed by newline.
inline void println_float(double v) {
unsafe { printf("%g\n", v); }
}
// Flush stdout — call before program exit if using print() without println().
inline void flush_stdout() {
unsafe { fflush(SAFEC_STDOUT_); }
}
// Flush stderr.
inline void flush_stderr() {
unsafe { fflush(SAFEC_STDERR_); }
}
// ── Additional stdout output ───────────────────────────────────────────────────
inline void print_uint(unsigned long long v) {
unsafe { printf("%llu", v); }
}
inline void print_hex(unsigned long long v) {
unsafe { printf("0x%llx", v); }
}
inline void print_oct(unsigned long long v) {
unsafe { printf("0%llo", v); }
}
inline void print_ptr_raw_(const unsigned char* p) {
unsafe { printf("%p", p); }
}
inline void println_uint(unsigned long long v) {
unsafe { printf("%llu\n", v); }
}
inline void println_char(int c) {
unsafe {
putchar(c);
putchar('\n');
}
}
// ── Stderr output ─────────────────────────────────────────────────────────────
inline void eprint_int(long long v) {
unsafe { fprintf(SAFEC_STDERR_, "%lld", v); }
}
inline void eprint_float(double v) {
unsafe { fprintf(SAFEC_STDERR_, "%g", v); }
}
// ── Buffer formatting (snprintf wrappers) ─────────────────────────────────────
inline int io_fmt_int([]char buf_s, long long v) {
int n = buf_s.len > 2147483647UL ? 2147483647 : (int)buf_s.len;
char buf_d_[1];
&char buf = &buf_d_[0];
if (n > 0) { unsafe { buf = (&char)buf_s.ptr; } }
unsafe { return snprintf(buf, (unsigned long)n, "%lld", v); }
}
inline int io_fmt_uint([]char buf_s, unsigned long long v) {
int n = buf_s.len > 2147483647UL ? 2147483647 : (int)buf_s.len;
char buf_d_[1];
&char buf = &buf_d_[0];
if (n > 0) { unsafe { buf = (&char)buf_s.ptr; } }
unsafe { return snprintf(buf, (unsigned long)n, "%llu", v); }
}
inline int io_fmt_float([]char buf_s, double v) {
int n = buf_s.len > 2147483647UL ? 2147483647 : (int)buf_s.len;
char buf_d_[1];
&char buf = &buf_d_[0];
if (n > 0) { unsafe { buf = (&char)buf_s.ptr; } }
unsafe { return snprintf(buf, (unsigned long)n, "%g", v); }
}
inline int io_fmt_float_prec([]char buf_s, double v, int prec) {
int n = buf_s.len > 2147483647UL ? 2147483647 : (int)buf_s.len;
char buf_d_[1];
&char buf = &buf_d_[0];
if (n > 0) { unsafe { buf = (&char)buf_s.ptr; } }
unsafe { return snprintf(buf, (unsigned long)n, "%.*f", prec, v); }
}
inline int io_fmt_hex([]char buf_s, unsigned long long v) {
int n = buf_s.len > 2147483647UL ? 2147483647 : (int)buf_s.len;
char buf_d_[1];
&char buf = &buf_d_[0];
if (n > 0) { unsafe { buf = (&char)buf_s.ptr; } }
unsafe { return snprintf(buf, (unsigned long)n, "0x%llx", v); }
}
inline int io_fmt_str([]char buf_s, const &char s) {
int n = buf_s.len > 2147483647UL ? 2147483647 : (int)buf_s.len;
char buf_d_[1];
&char buf = &buf_d_[0];
if (n > 0) { unsafe { buf = (&char)buf_s.ptr; } }
unsafe { return snprintf(buf, (unsigned long)n, "%s", s); }
}
// ── Input ─────────────────────────────────────────────────────────────────────
// Read a single character from stdin. Returns the character or SC_EOF (-1).
inline int io_getchar() {
unsafe { return getchar(); }
}
// Push a character back so the next read returns it.
inline int io_ungetc(int c) {
unsafe { return ungetc(c, SAFEC_STDIN_); }
}
// Read a line (up to n-1 chars + NUL) from stdin.
// Returns the number of characters read (excluding NUL), or -1 on EOF/error.
inline int io_read_line([]char buf_s) {
int n = buf_s.len > 2147483647UL ? 2147483647 : (int)buf_s.len;
char buf_d_[1];
&char buf = &buf_d_[0];
if (n > 0) { unsafe { buf = (&char)buf_s.ptr; } }
if (n <= 0) { return -1; }
unsafe {
if (fgets(buf, n, SAFEC_STDIN_) == (char*)0) { return -1; }
int len = (int)strlen(buf);
// Strip trailing newline if present.
if (len > 0 && buf[len - 1] == '\n') {
len = len - 1;
buf[len] = '\0';
}
return len;
}
}
// Read one whitespace-delimited token from stdin into buf[0..n-1].
// Returns number of chars read, 0 = EOF before any token, -1 = buffer too small.
inline int io_read_token([]char buf_s) {
int n = buf_s.len > 2147483647UL ? 2147483647 : (int)buf_s.len;
char buf_d_[1];
&char buf = &buf_d_[0];
if (n > 0) { unsafe { buf = (&char)buf_s.ptr; } }
if (n <= 1) { return -1; } // needs room for a char + NUL ("%0s" is invalid)
unsafe {
int c = getchar();
// Skip leading whitespace
while (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
c = getchar();
}
if (c == -1) { return 0; }
ungetc(c, SAFEC_STDIN_);
char fmt[16];
snprintf(fmt, (unsigned long)16, "%%%ds", n - 1);
int read = scanf(fmt, buf);
if (read == 1) { return (int)strlen(buf); }
return 0;
}
}
// Parse a signed decimal integer from stdin. Returns 1 on success.
inline int io_scan_int(&long long out) {
unsafe {
int r = scanf("%lld", (long long*)out);
return r == 1;
}
}
// Parse an unsigned decimal integer from stdin. Returns 1 on success.
inline int io_scan_uint(&unsigned long long out) {
unsafe {
int r = scanf("%llu", (unsigned long long*)out);
return r == 1;
}
}
// Parse a floating-point number from stdin. Returns 1 on success.
inline int io_scan_float(&double out) {
unsafe {
int r = scanf("%lf", (double*)out);
return r == 1;
}
}
// ── File system ───────────────────────────────────────────────────────────────
// Delete a file. Returns 0 on success, non-zero on error.
inline int io_remove(const &char path) {
unsafe { return remove(path); }
}
// Rename / move a file. Returns 0 on success, non-zero on error.
inline int io_rename(const &char old_path, const &char new_path) {
unsafe { return rename(old_path, new_path); }
}
// Create a temporary file opened in "w+b" mode. Returns handle or NULL.
inline ?&File io_tmpfile() {
unsafe { return (?&File)tmpfile(); }
}
} // namespace std