#include "printf.h" #include #include #include #include #define IDX_STDIN 0 #define IDX_STDOUT 1 #define IDX_STDERR 2 static enum b_status stdio_read( struct b_stream *stream, unsigned char *out, size_t max, size_t *nr_read) { FILE *fp = stream->s_ptr; enum b_status status = B_SUCCESS; size_t count = fread(out, 1, max, fp); if (ferror(fp)) { status = B_ERR_IO_FAILURE; } *nr_read = count; return status; } static enum b_status stdio_write( struct b_stream *stream, const unsigned char *data, size_t count, size_t *nr_written) { FILE *fp = stream->s_ptr; enum b_status status = B_SUCCESS; size_t w = fwrite(data, 1, count, fp); if (ferror(fp)) { status = B_ERR_IO_FAILURE; } *nr_written = w; return status; } static enum b_status stdio_seek( struct b_stream *stream, long long offset, b_stream_seek_origin origin) { FILE *fp = stream->s_ptr; int whence = 0; switch (origin) { case B_STREAM_SEEK_START: whence = SEEK_SET; break; case B_STREAM_SEEK_CURRENT: whence = SEEK_CUR; break; case B_STREAM_SEEK_END: whence = SEEK_END; break; default: return B_ERR_INVALID_ARGUMENT; } int ret = fseek(fp, offset, whence); if (ret != 0) { return B_ERR_NOT_SUPPORTED; } return B_SUCCESS; } static enum b_status stdio_tell(const struct b_stream *stream, size_t *cursor) { FILE *fp = stream->s_ptr; long pos = ftell(fp); if (pos == -1L) { return B_ERR_NOT_SUPPORTED; } *cursor = (size_t)pos; return B_SUCCESS; } static struct b_stream stdio[] = { [IDX_STDIN] = { .s_mode = B_STREAM_READ, .s_read = stdio_read, .s_seek = stdio_seek, .s_tell = stdio_tell, .s_ptr = NULL, /* set to stdin (stdio.h) at runtime */ }, [IDX_STDOUT] = { .s_mode = B_STREAM_WRITE, .s_write = stdio_write, .s_seek = stdio_seek, .s_tell = stdio_tell, .s_ptr = NULL, /* set to stdout (stdio.h) at runtime */ }, [IDX_STDERR] = { .s_mode = B_STREAM_WRITE, .s_write = stdio_write, .s_seek = stdio_seek, .s_tell = stdio_tell, .s_ptr = NULL, /* set to stderr (stdio.h) at runtime */ }, }; struct b_stream *z__b_stream_get_stdin(void) { stdio[IDX_STDIN].s_ptr = stdin; return &stdio[IDX_STDIN]; } struct b_stream *z__b_stream_get_stdout(void) { stdio[IDX_STDOUT].s_ptr = stdout; return &stdio[IDX_STDOUT]; } struct b_stream *z__b_stream_get_stderr(void) { stdio[IDX_STDERR].s_ptr = stderr; return &stdio[IDX_STDERR]; } enum b_status b_stream_pipeline_create( size_t buffer_size, struct b_stream_pipeline **out) { struct b_stream_pipeline *pipeline = malloc(sizeof *pipeline); if (!pipeline) { return B_ERR_NO_MEMORY; } memset(pipeline, 0x0, sizeof *pipeline); pipeline->p_buf = malloc(buffer_size); if (!pipeline->p_buf) { free(pipeline); return B_ERR_NO_MEMORY; } pipeline->p_buf_len = buffer_size; pipeline->p_flags = B_STREAM_PIPELINE_F_DYNAMIC | B_STREAM_PIPELINE_F_BUF_DYNAMIC; *out = pipeline; return B_SUCCESS; } enum b_status b_stream_pipeline_init( void *p, size_t len, struct b_stream_pipeline *out) { memset(out, 0x0, sizeof *out); out->p_buf = p; out->p_buf_len = len; return B_SUCCESS; } enum b_status b_stream_pipeline_destroy(struct b_stream_pipeline *pipeline) { if (pipeline->p_flags & B_STREAM_PIPELINE_F_BUF_DYNAMIC) { free(pipeline->p_buf); } if (pipeline->p_flags & B_STREAM_PIPELINE_F_DYNAMIC) { free(pipeline); } return B_SUCCESS; } struct b_stream *b_stream_open_fp(FILE *fp) { struct b_stream *stream = malloc(sizeof *stream); if (!stream) { return NULL; } memset(stream, 0x0, sizeof *stream); stream->s_mode = B_STREAM_READ | B_STREAM_WRITE; stream->s_ptr = fp; stream->s_read = stdio_read; stream->s_write = stdio_write; stream->s_seek = stdio_seek; stream->s_tell = stdio_tell; return stream; } enum b_status b_stream_close(b_stream *stream) { if (stream->s_istack) { free(stream->s_istack); } if (stream->s_close) { stream->s_close(stream); } if (!(stream->s_mode & Z__B_STREAM_STATIC)) { free(stream); } return B_SUCCESS; } enum b_status b_stream_reserve(b_stream *stream, size_t len) { if (!stream->s_reserve) { return B_ERR_NOT_SUPPORTED; } return stream->s_reserve(stream, len); } enum b_status b_stream_seek( b_stream *stream, long long offset, b_stream_seek_origin origin) { if (!stream->s_seek) { return B_ERR_NOT_SUPPORTED; } return stream->s_seek(stream, offset, origin); } size_t b_stream_cursor(const b_stream *stream) { if (!stream->s_tell) { return 0; } size_t cursor = 0; enum b_status status = stream->s_tell(stream, &cursor); if (!B_OK(status)) { return 0; } return cursor; } enum b_status b_stream_push_indent(b_stream *stream, int indent) { if (!(stream->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } if (stream->s_mode & B_STREAM_BINARY) { return B_ERR_NOT_SUPPORTED; } if (!stream->s_istack) { stream->s_istack = calloc(4, sizeof(int)); stream->s_istack_size = 4; stream->s_istack_ptr = 0; } if (stream->s_istack_ptr + 1 >= stream->s_istack_size) { int *buf = realloc( stream->s_istack, (stream->s_istack_size + 4) * sizeof(int)); if (!buf) { return B_ERR_NO_MEMORY; } stream->s_istack = buf; stream->s_istack_size += 4; } int cur_indent = stream->s_istack[stream->s_istack_ptr]; stream->s_istack[++stream->s_istack_ptr] = cur_indent + indent; return B_SUCCESS; } enum b_status b_stream_pop_indent(b_stream *stream) { if (!(stream->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } if (stream->s_mode & B_STREAM_BINARY) { return B_ERR_NOT_SUPPORTED; } if (!stream->s_istack || !stream->s_istack_size || !stream->s_istack_ptr) { return B_SUCCESS; } stream->s_istack_ptr--; return B_SUCCESS; } enum b_status b_stream_read_char(struct b_stream *stream, int *c) { if (!(stream->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } enum b_status status = B_ERR_NOT_SUPPORTED; if (stream->s_getc) { status = stream->s_getc(stream, c); } else if (stream->s_read) { size_t r; unsigned char v = 0; status = stream->s_read(stream, &v, 1, &r); *c = v; if (status == B_SUCCESS && r < 1) { status = B_ERR_NO_DATA; } } return status; } enum b_status b_stream_read_bytes( struct b_stream *stream, void *buf, size_t count, size_t *nr_read) { if (!(stream->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } enum b_status status = B_ERR_NOT_SUPPORTED; if (!stream->s_read) { return status; } return stream->s_read(stream, buf, count, nr_read); } enum b_status b_stream_read_line(struct b_stream *stream, char *s, size_t max) { if (!(stream->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } enum b_status status = B_SUCCESS; size_t i = 0; int c = 0; while (1) { if (i >= max) { break; } status = b_stream_read_char(stream, &c); if (status != B_SUCCESS) { break; } if (c == '\n') { break; } s[i++] = c; s[i] = '\0'; } return B_SUCCESS; } enum b_status b_stream_read_line_s(struct b_stream *src, b_stream *dest) { if (!(src->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } if (!(dest->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } enum b_status status = B_SUCCESS; size_t i = 0; int c = 0; while (1) { status = b_stream_read_char(src, &c); if (status != B_SUCCESS) { break; } b_stream_write_char(dest, c); i++; if (c == '\n') { break; } } if (status == B_ERR_NO_DATA && i > 0) { status = B_SUCCESS; } return status; } enum b_status b_stream_read_all_bytes( struct b_stream *stream, void *p, size_t max, size_t *out_nr_read) { if (!(stream->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } enum b_status status = B_SUCCESS; size_t nr_read = 0; unsigned char *s = p; while (nr_read < max) { int c; status = b_stream_read_char(stream, &c); if (status != B_SUCCESS) { break; } s[nr_read++] = c; } if (status == B_ERR_NO_DATA && nr_read > 0) { status = B_SUCCESS; } *out_nr_read = nr_read; return status; } enum b_status b_stream_read_all_bytes_s( struct b_stream *src, struct b_stream *dest, struct b_stream_pipeline *pipeline, size_t *out_nr_read) { if (!(src->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } if (!(dest->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } if (!pipeline) { return B_ERR_INVALID_ARGUMENT; } if (src->s_seek && dest->s_reserve) { size_t offset = b_stream_cursor(src); b_stream_seek(src, 0, B_STREAM_SEEK_END); size_t length = b_stream_cursor(src); b_stream_seek(src, offset, B_STREAM_SEEK_START); b_stream_reserve(dest, length); } enum b_status status = B_SUCCESS; size_t nr_read = 0; while (1) { size_t r = 0, w = 0; status = b_stream_read_bytes( src, pipeline->p_buf, pipeline->p_buf_len, &r); if (status != B_SUCCESS) { break; } status = b_stream_write_bytes(dest, pipeline->p_buf, r, &w); nr_read += w; if (status != B_SUCCESS || w != pipeline->p_buf_len) { break; } } if (status == B_ERR_NO_DATA && nr_read > 0) { status = B_SUCCESS; } if (out_nr_read) { *out_nr_read = nr_read; } return status; } static enum b_status __write_char(struct b_stream *stream, char c) { size_t w; enum b_status status = stream->s_write(stream, (unsigned char *)&c, 1, &w); if (status == B_SUCCESS && w < 1) { status = B_ERR_IO_FAILURE; } return status; } static int current_indent(struct b_stream *stream) { if (!stream->s_istack || !stream->s_istack_size) { return 0; } return stream->s_istack[stream->s_istack_ptr]; } enum b_status b_stream_write_char(struct b_stream *stream, char c) { if (!(stream->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } enum b_status status = B_ERR_NOT_SUPPORTED; if (!stream->s_write) { return B_ERR_NOT_SUPPORTED; } if (c == '\n') { stream->s_add_indent = 1; } if (!stream->s_istack_size) { return __write_char(stream, c); } if (stream->s_add_indent && c != '\n') { int indent = current_indent(stream); for (int i = 0; i < indent; i++) { __write_char(stream, ' '); __write_char(stream, ' '); } stream->s_add_indent = 0; } __write_char(stream, c); if (c == '\n') { stream->s_add_indent = 1; } return B_SUCCESS; } enum b_status b_stream_write_string( b_stream *stream, const char *s, size_t *nr_written) { size_t i; enum b_status status = B_SUCCESS; for (i = 0; s[i]; i++) { status = b_stream_write_char(stream, s[i]); if (!B_OK(status)) { break; } } if (nr_written) { *nr_written = i; } return status; } enum b_status b_stream_write_bytes( struct b_stream *stream, const void *buf, size_t count, size_t *nr_written) { if (!(stream->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } enum b_status status = B_ERR_NOT_SUPPORTED; if (!stream->s_write) { return status; } return stream->s_write(stream, buf, count, nr_written); } static void fctprintf_callback(char c, void *p) { struct b_stream *stream = p; b_stream_write_char(stream, c); } enum b_status b_stream_write_fmt( b_stream *stream, size_t *nr_written, const char *format, ...) { va_list arg; va_start(arg, format); int w = z__b_fctprintf(fctprintf_callback, stream, format, arg); va_end(arg); if (nr_written) { *nr_written = w; } return B_SUCCESS; } enum b_status b_stream_write_vfmt( b_stream *stream, size_t *nr_written, const char *format, va_list arg) { int w = z__b_fctprintf(fctprintf_callback, stream, format, arg); if (nr_written) { *nr_written = w; } return B_SUCCESS; }