151 lines
6.3 KiB
C
151 lines
6.3 KiB
C
#ifndef VLIBC_STDIO_STDIO_IMPL_H
|
|
#define VLIBC_STDIO_STDIO_IMPL_H
|
|
|
|
/*
|
|
* vlibc — internal FILE layout and stream helpers (todo 15).
|
|
*
|
|
* struct vlibc_FILE is the object behind the opaque FILE of <stdio.h>.
|
|
* Position invariant (authoritative):
|
|
*
|
|
* - Read mode: `pos` is the file offset of buf[0]. The logical position
|
|
* (offset of the byte at rpos) is pos + (rpos - buf), and the kernel
|
|
* position is pos + (rstop - buf); with an empty buffer
|
|
* (rpos == rstop) the kernel sits exactly at the logical position.
|
|
* - Write mode: `pos` is the file offset of buf[0]; the next written byte
|
|
* lands at pos + (wpos - buf). With an empty buffer the kernel sits at
|
|
* pos. A flush advances pos by the number of bytes written.
|
|
*
|
|
* Mode switching: reading while write data is pending flushes it first;
|
|
* writing while unread buffered data is pending seeks the kernel back to
|
|
* the logical position (discarding the unread data). F_WRITE doubles as
|
|
* the current-mode marker and is cleared by the read switch; F_RDWR
|
|
* records the write capability of update streams so the write switch can
|
|
* re-set F_WRITE. F_PUSHED marks the single guaranteed ungetc pushback
|
|
* byte in `ungot`; a successful seek discards it.
|
|
*
|
|
* The helper prototypes below are shared with the formatted-I/O todos
|
|
* (16: vfprintf, 17: vfscanf) so they operate on the same buffer state.
|
|
*
|
|
* Locking (todo 18): the process is single-threaded until the thread
|
|
* todo (#45) lands, so flockfile/ftrylockfile/funlockfile only track
|
|
* ownership state here. `lock` is the recursion depth (0 = unlocked),
|
|
* `lock_owner` the owning thread's FS base while locked. When real
|
|
* threads arrive, the same todo turns `lock` into the actual per-stream
|
|
* lock word (blocking acquisition, mutex/cond state) and these two
|
|
* fields become the bookkeeping that makes flockfile recursive.
|
|
*
|
|
* Memory streams (todo 18): fmemopen/open_memstream build a FILE whose
|
|
* `mem` points at an opaque backend (defined in fmemopen.c) instead of a
|
|
* descriptor. Whenever `mem` is non-NULL the buffered core below calls
|
|
* back into fmemopen.c's stdio_mem_* hooks in place of every descriptor
|
|
* syscall, so the position invariant above is unchanged: the backend
|
|
* simply acts as an in-memory device with the same kernel-position
|
|
* semantics (reads advance it past the read-ahead, writes advance it on
|
|
* flush, discards rewind it to the logical position).
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "../internal/libc.h"
|
|
|
|
/* Opaque memory-stream backend (fmemopen/open_memstream, todo 18). */
|
|
struct vlibc_memstream;
|
|
|
|
struct vlibc_FILE
|
|
{
|
|
unsigned char *buf; /* buffer base: library- or user-supplied */
|
|
unsigned char *rpos; /* read mode: next byte to deliver */
|
|
unsigned char *rstop; /* read mode: end of valid buffered data */
|
|
unsigned char *wpos; /* write mode: next byte to store */
|
|
unsigned char *wstop; /* write mode: end of the buffer */
|
|
int fd; /* underlying descriptor; -1 when closed */
|
|
int flags; /* F_* bits below */
|
|
size_t buf_size; /* usable buffer size; 0 = not yet sized */
|
|
off_t pos; /* position per the invariant above */
|
|
struct vlibc_FILE *next; /* registry link for fflush(NULL) */
|
|
unsigned char ungot; /* pushed-back byte when F_PUSHED */
|
|
int lock; /* flockfile recursion depth; 0 = unlocked */
|
|
void *lock_owner; /* owning thread's FS base while locked */
|
|
struct vlibc_memstream *mem; /* memory-stream backend, or NULL */
|
|
};
|
|
|
|
/* Stream flags. */
|
|
#define F_READ 0x001 /* reads are enabled by the open mode */
|
|
#define F_WRITE 0x002 /* writes are enabled by the open mode */
|
|
#define F_EOF 0x004 /* end of file has been seen */
|
|
#define F_ERR 0x008 /* an I/O error occurred */
|
|
#define F_LINEBUF 0x010 /* line buffered: flush on newline */
|
|
#define F_UNBUF 0x020 /* unbuffered: 1-byte buffer */
|
|
#define F_APPEND 0x040 /* append mode: writes land at end of file */
|
|
#define F_OWNBUF 0x080 /* buf was allocated by the library */
|
|
#define F_HEAP 0x100 /* the FILE struct itself is heap-allocated */
|
|
#define F_PUSHED 0x200 /* ungot holds a pushed-back byte */
|
|
#define F_RDWR 0x400 /* update stream: reads and writes both enabled */
|
|
|
|
/* Flush pending write data; returns 0, or -1 with F_ERR set. */
|
|
hidden int
|
|
stdio_flush(FILE *f);
|
|
|
|
/* Refill the read buffer from the descriptor (read mode only). */
|
|
hidden void
|
|
stdio_refill(FILE *f);
|
|
|
|
/* Discard unread buffered data, seeking the kernel back to pos. */
|
|
hidden int
|
|
stdio_discard_read(FILE *f);
|
|
|
|
/* Allocate the buffer lazily (std streams, setvbuf with NULL). */
|
|
hidden void
|
|
stdio_init_if_needed(FILE *f);
|
|
|
|
/* Allocate and register a FILE over an open descriptor. */
|
|
hidden FILE *
|
|
stdio_alloc_file(int fd, int m);
|
|
|
|
/*
|
|
* Parse a fopen-style mode string. On success stores the F_READ/F_WRITE
|
|
* capability bits in *m, the openat flags in *oflags, and returns 1;
|
|
* returns 0 for an invalid mode.
|
|
*/
|
|
hidden int
|
|
stdio_parse_mode(const char *mode, int *m, int *oflags);
|
|
|
|
/*
|
|
* Raw lseek with errno translation. Separate from syscall_ret() because
|
|
* syscall_ret narrows results to int and would truncate large offsets.
|
|
*/
|
|
hidden off_t
|
|
stdio_lseek(int fd, off_t off, int whence);
|
|
|
|
/*
|
|
* Memory-stream device hooks (fmemopen.c, todo 18). The stdio core calls
|
|
* these in place of the descriptor syscalls whenever f->mem is set:
|
|
*
|
|
* - stdio_mem_flush: write the pending cache to the backing region
|
|
* (stdio_flush's device-write half, F_APPEND included).
|
|
* - stdio_mem_refill: read the next cache-full from the region
|
|
* (stdio_refill's device-read half).
|
|
* - stdio_mem_discard: rewind the device to the logical position,
|
|
* discarding unread cached data (stdio_discard_read's seek half).
|
|
* - stdio_mem_lseek: resolve a SEEK_SET/SEEK_END target against the
|
|
* region (used by fseeko instead of stdio_lseek on memory streams).
|
|
* - stdio_mem_close: run on fclose before the FILE is released
|
|
* (open_memstream's final NUL/size sync, owned-buffer release).
|
|
*/
|
|
hidden int
|
|
stdio_mem_flush(FILE *f);
|
|
|
|
hidden void
|
|
stdio_mem_refill(FILE *f);
|
|
|
|
hidden int
|
|
stdio_mem_discard(FILE *f);
|
|
|
|
hidden off_t
|
|
stdio_mem_lseek(FILE *f, off_t off, int whence);
|
|
|
|
hidden void
|
|
stdio_mem_close(FILE *f);
|
|
|
|
#endif /* VLIBC_STDIO_STDIO_IMPL_H */
|