std1.0.0-beta-7 SafeC docs
// 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