summaryrefslogtreecommitdiff
path: root/src/ulib/stdio.c
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-01-02 10:09:37 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-01-02 10:09:37 +0800
commit1c8e37f63d413a559825020289178d442537449e (patch)
tree9779a712b5eec91035272e7646eb6811979d7eab /src/ulib/stdio.c
parentd8eaa13f2efc1eeca4e5f230d5a2d535aaf27338 (diff)
downloadlume-1c8e37f63d413a559825020289178d442537449e.tar.gz
lume-1c8e37f63d413a559825020289178d442537449e.tar.xz
dev(sell): add simple libc and implement shell
please see the log for detail.
Diffstat (limited to 'src/ulib/stdio.c')
-rw-r--r--src/ulib/stdio.c1052
1 files changed, 1052 insertions, 0 deletions
diff --git a/src/ulib/stdio.c b/src/ulib/stdio.c
new file mode 100644
index 0000000..f8e7a9a
--- /dev/null
+++ b/src/ulib/stdio.c
@@ -0,0 +1,1052 @@
+#include "ulib/stdio.h"
+#include "ulib/string.h"
+#include "ulib/stdlib.h"
+
+/* Include STB Sprintf Implementation */
+#define STB_SPRINTF_IMPLEMENTATION
+#include "ulib/stb_sprintf.h"
+
+const int STDIN_FD = 0;
+const int STDOUT_FD = 1;
+const int STDERR_FD = 2;
+
+
+
+extern int open(const char *path, int flags);
+extern int close(int fd);
+extern int read(int fd, void *buf, uint64 count);
+extern int write(int fd, const void *buf, uint64 count);
+extern int lseek(int fd, int offset, int whence);
+
+static int isspace(int c)
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
+}
+
+/* Core Formatting Implementation */
+
+static int _ul_to_str_invert(char *buf, uint64 val, int base, int uppercase)
+{
+ int i = 0;
+ const char *digits = uppercase ? "0123456789ABCDEF" : "0123456789abcdef";
+ if (val == 0)
+ {
+ buf[i++] = '0';
+ return i;
+ }
+ while (val != 0)
+ {
+ buf[i++] = digits[val % base];
+ val /= base;
+ }
+ return i;
+}
+
+typedef struct
+{
+ char *buf;
+ uint64 limit;
+ uint64 written;
+ FILE *stream;
+} _OutContext;
+
+static void _out_char(_OutContext *ctx, char c)
+{
+ if (ctx->stream)
+ {
+ fputc(c, ctx->stream);
+ ctx->written++;
+ }
+ else
+ {
+ if (ctx->limit > 0 && ctx->written < ctx->limit - 1)
+ {
+ ctx->buf[ctx->written] = c;
+ }
+ ctx->written++;
+ }
+}
+
+static int _vprintf_core(_OutContext *ctx, const char *format, va_list ap)
+{
+ const char *p = format;
+ char num_buf[32];
+
+ while (*p)
+ {
+ if (*p != '%')
+ {
+ _out_char(ctx, *p++);
+ continue;
+ }
+ p++;
+
+ int flag_left = 0, flag_zero = 0, flag_plus = 0, flag_space = 0, flag_hash = 0;
+ while (1)
+ {
+ if (*p == '-')
+ flag_left = 1;
+ else if (*p == '0')
+ flag_zero = 1;
+ else if (*p == '+')
+ flag_plus = 1;
+ else if (*p == ' ')
+ flag_space = 1;
+ else if (*p == '#')
+ flag_hash = 1;
+ else
+ break;
+ p++;
+ }
+
+ int width = 0;
+ if (*p == '*')
+ {
+ width = va_arg(ap, int);
+ if (width < 0)
+ {
+ flag_left = 1;
+ width = -width;
+ }
+ p++;
+ }
+ else
+ {
+ while (*p >= '0' && *p <= '9')
+ {
+ width = width * 10 + (*p - '0');
+ p++;
+ }
+ }
+
+ int precision = -1;
+ if (*p == '.')
+ {
+ p++;
+ if (*p == '*')
+ {
+ precision = va_arg(ap, int);
+ p++;
+ }
+ else
+ {
+ precision = 0;
+ while (*p >= '0' && *p <= '9')
+ {
+ precision = precision * 10 + (*p - '0');
+ p++;
+ }
+ }
+ }
+
+ int len_mod = 0;
+ if (*p == 'l')
+ {
+ len_mod = 1;
+ p++;
+ if (*p == 'l')
+ {
+ len_mod = 2;
+ p++;
+ }
+ }
+ else if (*p == 'z')
+ {
+ len_mod = 2;
+ p++;
+ }
+
+ char spec = *p++;
+ if (!spec)
+ break;
+
+ if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' || spec == 'X' || spec == 'p')
+ {
+ uint64 val;
+ int is_signed = (spec == 'd' || spec == 'i');
+ int base = (spec == 'x' || spec == 'X' || spec == 'p') ? 16 : 10;
+
+ if (spec == 'p')
+ val = (uint64)va_arg(ap, void *);
+ else if (len_mod == 2)
+ val = is_signed ? (uint64)va_arg(ap, int64) : va_arg(ap, uint64);
+ else if (len_mod == 1)
+ val = is_signed ? (uint64)va_arg(ap, long) : (uint64)va_arg(ap, unsigned long);
+ else
+ val = is_signed ? (uint64)((int64)va_arg(ap, int)) : (uint64)va_arg(ap, uint32);
+
+ char sign_char = 0;
+ if (is_signed)
+ {
+ int64 sval = (int64)val;
+ if (sval < 0)
+ {
+ sign_char = '-';
+ val = (uint64)(-sval);
+ }
+ else if (flag_plus)
+ sign_char = '+';
+ else if (flag_space)
+ sign_char = ' ';
+ }
+
+ int digit_len = _ul_to_str_invert(num_buf, val, base, (spec == 'X'));
+ if (precision >= 0)
+ flag_zero = 0;
+ int prec_zeros = (precision > digit_len) ? precision - digit_len : 0;
+ if (precision == 0 && val == 0)
+ digit_len = 0;
+
+ int prefix_len = sign_char ? 1 : 0;
+ char *prefix_str = NULL;
+ if (spec == 'p' || (flag_hash && val != 0 && (spec == 'x' || spec == 'X')))
+ {
+ prefix_str = (spec == 'X') ? "0X" : "0x";
+ prefix_len += 2;
+ }
+
+ int padding = (width > prefix_len + prec_zeros + digit_len) ? width - (prefix_len + prec_zeros + digit_len) : 0;
+
+ if (!flag_left && !flag_zero)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ if (sign_char)
+ _out_char(ctx, sign_char);
+ if (prefix_str)
+ {
+ _out_char(ctx, prefix_str[0]);
+ _out_char(ctx, prefix_str[1]);
+ }
+ int total_zeros = prec_zeros + ((flag_zero && !flag_left) ? padding : 0);
+ for (int i = 0; i < total_zeros; i++)
+ _out_char(ctx, '0');
+ for (int i = digit_len - 1; i >= 0; i--)
+ _out_char(ctx, num_buf[i]);
+ if (flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ }
+ else if (spec == 'f' || spec == 'F' || spec == 'e' || spec == 'E' || spec == 'g' || spec == 'G')
+ {
+ char fmt_tmp[32];
+ int f_idx = 0;
+ fmt_tmp[f_idx++] = '%';
+ if (flag_left)
+ fmt_tmp[f_idx++] = '-';
+ if (flag_zero)
+ fmt_tmp[f_idx++] = '0';
+ if (flag_plus)
+ fmt_tmp[f_idx++] = '+';
+ if (flag_space)
+ fmt_tmp[f_idx++] = ' ';
+ if (flag_hash)
+ fmt_tmp[f_idx++] = '#';
+ if (width > 0)
+ f_idx += sprintf(&fmt_tmp[f_idx], "%d", width);
+ if (precision >= 0)
+ f_idx += sprintf(&fmt_tmp[f_idx], ".%d", precision);
+ fmt_tmp[f_idx++] = spec;
+ fmt_tmp[f_idx] = '\0';
+ double f_val = va_arg(ap, double);
+ char f_buf[128];
+ stbsp_snprintf(f_buf, sizeof(f_buf), fmt_tmp, f_val);
+ for (int i = 0; f_buf[i] != '\0'; i++)
+ _out_char(ctx, f_buf[i]);
+ }
+ else if (spec == 's')
+ {
+ const char *s = va_arg(ap, char *);
+ if (!s)
+ s = "(null)";
+ int len = 0;
+ const char *tmp = s;
+ while (*tmp && (precision < 0 || len < precision))
+ {
+ tmp++;
+ len++;
+ }
+ int padding = (width > len) ? width - len : 0;
+ if (!flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ for (int i = 0; i < len; i++)
+ _out_char(ctx, s[i]);
+ if (flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ }
+ else if (spec == 'c')
+ {
+ char c = (char)va_arg(ap, int);
+ int padding = (width > 1) ? width - 1 : 0;
+ if (!flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ _out_char(ctx, c);
+ if (flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ }
+ else
+ {
+ _out_char(ctx, '%');
+ if (spec != '%')
+ _out_char(ctx, spec);
+ }
+ }
+ return (int)ctx->written;
+}
+
+int vsnprintf(char *str, uint64 size, const char *format, va_list ap)
+{
+ _OutContext ctx;
+ ctx.buf = str;
+ ctx.limit = size;
+ ctx.written = 0;
+ ctx.stream = NULL;
+ _vprintf_core(&ctx, format, ap);
+ if (size > 0)
+ {
+ if (ctx.written < size)
+ str[ctx.written] = '\0';
+ else
+ str[size - 1] = '\0';
+ }
+ return (int)ctx.written;
+}
+int snprintf(char *str, uint64 size, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ int ret = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return ret;
+}
+int sprintf(char *str, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ int ret = vsnprintf(str, (uint64)-1, format, ap);
+ va_end(ap);
+ return ret;
+}
+int printf(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ _OutContext ctx;
+ ctx.buf = NULL;
+ ctx.limit = 0;
+ ctx.written = 0;
+ ctx.stream = stdout;
+ _vprintf_core(&ctx, format, ap);
+ va_end(ap);
+ return (int)ctx.written;
+}
+
+/* Core Function Implementation */
+
+typedef struct
+{
+ const char *buf;
+ int fd;
+ uint64 pos;
+ int ungotten;
+} _InContext;
+
+static int _in_char(_InContext *ctx)
+{
+ if (ctx->ungotten >= 0)
+ {
+ int c = ctx->ungotten;
+ ctx->ungotten = -1;
+ return c;
+ }
+ if (ctx->fd == -1)
+ {
+ if (ctx->buf[ctx->pos] == '\0')
+ return -1;
+ return (unsigned char)ctx->buf[ctx->pos++];
+ }
+ char c;
+ if (read(ctx->fd, &c, 1) == 1)
+ return (unsigned char)c;
+ return -1;
+}
+
+static void _unget_char(_InContext *ctx, int c)
+{
+ if (c != -1)
+ ctx->ungotten = c;
+}
+static void _skip_space(_InContext *ctx)
+{
+ int c;
+ while ((c = _in_char(ctx)) != -1)
+ {
+ if (!isspace(c))
+ {
+ _unget_char(ctx, c);
+ break;
+ }
+ }
+}
+
+static double _simple_strtod(const char *str, char **endptr)
+{
+ double val = 0.0;
+ int sign = 1;
+ if (*str == '-')
+ {
+ sign = -1;
+ str++;
+ }
+ else if (*str == '+')
+ str++;
+ while (*str >= '0' && *str <= '9')
+ {
+ val = val * 10.0 + (*str - '0');
+ str++;
+ }
+ if (*str == '.')
+ {
+ str++;
+ double div = 10.0;
+ while (*str >= '0' && *str <= '9')
+ {
+ val += (*str - '0') / div;
+ div *= 10.0;
+ str++;
+ }
+ }
+ if (*str == 'e' || *str == 'E')
+ {
+ str++;
+ int esign = 1;
+ if (*str == '-')
+ {
+ esign = -1;
+ str++;
+ }
+ else if (*str == '+')
+ str++;
+ int eval = 0;
+ while (*str >= '0' && *str <= '9')
+ {
+ eval = eval * 10 + (*str - '0');
+ str++;
+ }
+ double p = 1.0;
+ while (eval--)
+ p *= 10.0;
+ if (esign == 1)
+ val *= p;
+ else
+ val /= p;
+ }
+ if (endptr)
+ *endptr = (char *)str;
+ return sign * val;
+}
+
+static int _vscanf_core(_InContext *ctx, const char *format, va_list ap)
+{
+ int assigned = 0;
+ const char *p = format;
+ while (*p)
+ {
+ if (isspace(*p))
+ {
+ _skip_space(ctx);
+ while (isspace(*p))
+ p++;
+ continue;
+ }
+ if (*p != '%')
+ {
+ _skip_space(ctx);
+ int c = _in_char(ctx);
+ if (c != *p)
+ {
+ _unget_char(ctx, c);
+ return assigned;
+ }
+ p++;
+ continue;
+ }
+ p++;
+ int suppress = 0;
+ if (*p == '*')
+ {
+ suppress = 1;
+ p++;
+ }
+ int width = -1;
+ while (*p >= '0' && *p <= '9')
+ {
+ if (width == -1)
+ width = 0;
+ width = width * 10 + (*p - '0');
+ p++;
+ }
+ int len_mod = 0;
+ if (*p == 'l')
+ {
+ len_mod = 1;
+ p++;
+ if (*p == 'l')
+ {
+ len_mod = 2;
+ p++;
+ }
+ }
+ else if (*p == 'h')
+ {
+ p++;
+ if (*p == 'h')
+ p++;
+ }
+ char spec = *p++;
+ if (!spec)
+ break;
+
+ if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x')
+ {
+ _skip_space(ctx);
+ char num_buf[64];
+ int i = 0, c = _in_char(ctx);
+ if (c == '-' || c == '+')
+ {
+ num_buf[i++] = (char)c;
+ c = _in_char(ctx);
+ }
+ int base = (spec == 'x') ? 16 : (spec == 'i' ? 0 : 10);
+ while (c != -1 && i < 63)
+ {
+ int digit = (c >= '0' && c <= '9');
+ int hex = (base == 16 || base == 0) && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+ int x = (c == 'x' || c == 'X');
+ if (base == 0 && i == 1 && num_buf[0] == '0' && x)
+ base = 16;
+ if (width > 0 && i >= width)
+ break;
+ if (digit || hex || x)
+ {
+ num_buf[i++] = (char)c;
+ c = _in_char(ctx);
+ }
+ else
+ break;
+ }
+ num_buf[i] = '\0';
+ _unget_char(ctx, c);
+ if (i == 0)
+ return assigned;
+ if (!suppress)
+ {
+ int64 val = strtoll(num_buf, NULL, base);
+ if (len_mod == 2)
+ *va_arg(ap, int64 *) = val;
+ else if (len_mod == 1)
+ *va_arg(ap, long *) = (long)val;
+ else
+ *va_arg(ap, int *) = (int)val;
+ assigned++;
+ }
+ }
+ else if (spec == 'f' || spec == 'F' || spec == 'e' || spec == 'E' || spec == 'g' || spec == 'G')
+ {
+ _skip_space(ctx);
+ char num_buf[64];
+ int i = 0, c = _in_char(ctx);
+ if (c == '-' || c == '+')
+ {
+ num_buf[i++] = (char)c;
+ c = _in_char(ctx);
+ }
+ while (c != -1 && i < 63)
+ {
+ if ((c >= '0' && c <= '9') || c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-')
+ {
+ num_buf[i++] = (char)c;
+ c = _in_char(ctx);
+ }
+ else
+ break;
+ }
+ num_buf[i] = '\0';
+ _unget_char(ctx, c);
+ if (i == 0)
+ return assigned;
+ if (!suppress)
+ {
+ double val = _simple_strtod(num_buf, NULL);
+ if (len_mod == 1)
+ *va_arg(ap, double *) = val;
+ else
+ *va_arg(ap, float *) = (float)val;
+ assigned++;
+ }
+ }
+ else if (spec == 's')
+ {
+ _skip_space(ctx);
+ char *buf = suppress ? NULL : va_arg(ap, char *);
+ int i = 0, c = _in_char(ctx);
+ while (c != -1 && !isspace(c))
+ {
+ if (width > 0 && i >= width)
+ break;
+ if (!suppress)
+ buf[i] = (char)c;
+ i++;
+ c = _in_char(ctx);
+ }
+ if (!suppress)
+ buf[i] = '\0';
+ _unget_char(ctx, c);
+ if (i > 0 && !suppress)
+ assigned++;
+ }
+ else if (spec == 'c')
+ {
+ int count = (width > 0) ? width : 1;
+ char *buf = suppress ? NULL : va_arg(ap, char *);
+ for (int i = 0; i < count; i++)
+ {
+ int c = _in_char(ctx);
+ if (c == -1)
+ return assigned;
+ if (!suppress)
+ buf[i] = (char)c;
+ }
+ if (!suppress)
+ assigned++;
+ }
+ }
+ return assigned;
+}
+
+int sscanf(const char *str, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ _InContext ctx;
+ ctx.buf = str;
+ ctx.fd = -1;
+ ctx.pos = 0;
+ ctx.ungotten = -1;
+ int ret = _vscanf_core(&ctx, format, ap);
+ va_end(ap);
+ return ret;
+}
+int scanf(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ _InContext ctx;
+ ctx.buf = NULL;
+ ctx.fd = STDIN_FD;
+ ctx.pos = 0;
+ ctx.ungotten = -1;
+ int ret = _vscanf_core(&ctx, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+/* FILE Stream Implementation */
+
+#define O_RDONLY 00
+#define O_WRONLY 01
+#define O_RDWR 02
+#define O_CREAT 0100
+#define O_TRUNC 01000
+#define O_APPEND 02000
+
+
+static FILE _stdin = {.fd = 0, .buf_mode = 1};
+static FILE _stdout = {.fd = 1, .buf_mode = 1};
+static FILE _stderr = {.fd = 2, .buf_mode = 2};
+
+FILE *stdin = &_stdin;
+FILE *stdout = &_stdout;
+FILE *stderr = &_stderr;
+
+FILE *fopen(const char *filename, const char *mode)
+{
+ int flags = 0;
+ if (strcmp(mode, "r") == 0)
+ flags = O_RDONLY;
+ else if (strcmp(mode, "w") == 0)
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+ else if (strcmp(mode, "a") == 0)
+ flags = O_WRONLY | O_CREAT | O_APPEND;
+ else if (strcmp(mode, "r+") == 0)
+ flags = O_RDWR;
+ else
+ return NULL;
+
+ int64 fd = open(filename, flags);
+ if (fd < 0)
+ return NULL;
+
+ FILE *f = (FILE *)malloc(sizeof(FILE));
+ if (!f)
+ {
+ close(fd);
+ return NULL;
+ }
+
+ memset(f, 0, sizeof(FILE));
+ f->fd = (int)fd;
+ f->flags = flags;
+
+ if (fd == 1)
+ f->buf_mode = 1;
+ else if (fd == 2)
+ f->buf_mode = 2;
+ else
+ f->buf_mode = 0;
+ return f;
+}
+
+int fclose(FILE *stream)
+{
+ if (!stream)
+ return -1;
+ fflush(stream);
+ if (stream != stdin && stream != stdout && stream != stderr)
+ {
+ close(stream->fd);
+ free(stream);
+ }
+ return 0;
+}
+
+int fflush(FILE *stream)
+{
+ if (!stream)
+ return EOF;
+
+ if (stream->buf_write_pos > 0)
+ {
+ int64 ret = write(stream->fd, stream->buffer, stream->buf_write_pos);
+ if (ret < stream->buf_write_pos)
+ {
+ stream->error = 1;
+ stream->buf_write_pos = 0;
+ return EOF;
+ }
+ stream->buf_write_pos = 0;
+ }
+ return 0;
+}
+
+int fprintf(FILE *stream, const char *format, ...)
+{
+ if (!stream)
+ return -1;
+ va_list ap;
+ va_start(ap, format);
+ _OutContext ctx;
+ ctx.buf = NULL;
+ ctx.limit = 0;
+ ctx.written = 0;
+ ctx.stream = stream;
+ _vprintf_core(&ctx, format, ap);
+ va_end(ap);
+ return (int)ctx.written;
+}
+
+int fscanf(FILE *stream, const char *format, ...)
+{
+ if (!stream)
+ return -1;
+ va_list ap;
+ va_start(ap, format);
+ _InContext ctx;
+ ctx.buf = NULL;
+ ctx.fd = stream->fd;
+ ctx.pos = 0;
+ ctx.ungotten = -1;
+ int ret = _vscanf_core(&ctx, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+int fputc(int c, FILE *stream)
+{
+ if (!stream)
+ return EOF;
+ if (stream->buf_mode == 2)
+ {
+ char ch = (char)c;
+ if (write(stream->fd, &ch, 1) != 1)
+ {
+ stream->error = 1;
+ return EOF;
+ }
+ return c;
+ }
+
+ stream->buffer[stream->buf_write_pos++] = (char)c;
+
+ if (stream->buf_write_pos >= BUFSIZ)
+ {
+ fflush(stream);
+ }
+ else if (stream->buf_mode == 1 && c == '\n')
+ {
+ fflush(stream);
+ }
+
+ return c;
+}
+
+int fputs(const char *s, FILE *stream)
+{
+ if (!stream)
+ return -1;
+ while (*s)
+ {
+ if (fputc(*s++, stream) == EOF)
+ return EOF;
+ }
+ return 0;
+}
+
+int fgetc(FILE *stream)
+{
+ if (!stream)
+ return EOF;
+
+ if (stream->buf_write_pos > 0)
+ {
+ fflush(stream);
+ }
+
+ if (stream->buf_read_pos < stream->buf_read_cnt)
+ {
+ return (unsigned char)stream->buffer[stream->buf_read_pos++];
+ }
+
+ int64 ret = read(stream->fd, stream->buffer, BUFSIZ);
+ if (ret <= 0)
+ {
+ if (ret == 0)
+ stream->eof = 1;
+ else
+ stream->error = 1;
+ return EOF;
+ }
+
+ stream->buf_read_cnt = (int)ret;
+ stream->buf_read_pos = 0;
+ return (unsigned char)stream->buffer[stream->buf_read_pos++];
+}
+
+char *fgets(char *str, int n, FILE *stream)
+{
+ if (!stream || n <= 0)
+ return NULL;
+ int i = 0, c;
+ while (i < n - 1)
+ {
+ c = fgetc(stream);
+ if (c == -1)
+ {
+ if (i == 0)
+ return NULL;
+ break;
+ }
+ str[i++] = (char)c;
+ if (c == '\n')
+ break;
+ }
+ str[i] = '\0';
+ return str;
+}
+
+uint64 fread(void *ptr, uint64 size, uint64 nmemb, FILE *stream)
+{
+ if (!stream || size == 0 || nmemb == 0)
+ return 0;
+
+ uint64 total_bytes = size * nmemb;
+ uint64 bytes_read = 0;
+ char *dst = (char *)ptr;
+
+ if (stream->buf_write_pos > 0)
+ fflush(stream);
+
+ while (bytes_read < total_bytes)
+ {
+ if (stream->buf_read_pos < stream->buf_read_cnt)
+ {
+ uint64 available = stream->buf_read_cnt - stream->buf_read_pos;
+ uint64 remaining = total_bytes - bytes_read;
+ uint64 to_copy = (available < remaining) ? available : remaining;
+
+ memcpy(dst + bytes_read, stream->buffer + stream->buf_read_pos, to_copy);
+
+ stream->buf_read_pos += to_copy;
+ bytes_read += to_copy;
+ }
+ else
+ {
+ uint64 remaining = total_bytes - bytes_read;
+
+ if (remaining >= BUFSIZ)
+ {
+ int64 ret = read(stream->fd, dst + bytes_read, remaining);
+ if (ret <= 0)
+ {
+ if (ret == 0)
+ stream->eof = 1;
+ else
+ stream->error = 1;
+ return bytes_read / size;
+ }
+ bytes_read += ret;
+ }
+ else
+ {
+ stream->buf_read_pos = 0;
+ stream->buf_read_cnt = 0;
+ int64 ret = read(stream->fd, stream->buffer, BUFSIZ);
+
+ if (ret <= 0)
+ {
+ if (ret == 0)
+ stream->eof = 1;
+ else
+ stream->error = 1;
+ return bytes_read / size;
+ }
+ stream->buf_read_cnt = (int)ret;
+ }
+ }
+ }
+ return bytes_read / size;
+}
+
+uint64 fwrite(const void *ptr, uint64 size, uint64 nmemb, FILE *stream)
+{
+ if (!stream || !ptr || size == 0 || nmemb == 0)
+ return 0;
+ uint64 total_bytes = size * nmemb;
+ const char *src = (const char *)ptr;
+ uint64 bytes_written = 0;
+
+ if (stream->buf_mode == 2)
+ {
+ while (bytes_written < total_bytes)
+ {
+ int64 ret = write(stream->fd, src + bytes_written, total_bytes - bytes_written);
+ if (ret <= 0)
+ {
+ stream->error = 1;
+ break;
+ }
+ bytes_written += ret;
+ }
+ return bytes_written / size;
+ }
+
+ while (bytes_written < total_bytes)
+ {
+ uint64 space_left = BUFSIZ - stream->buf_write_pos;
+ uint64 remaining = total_bytes - bytes_written;
+
+ if (remaining <= space_left)
+ {
+ memcpy(stream->buffer + stream->buf_write_pos, src + bytes_written, remaining);
+ stream->buf_write_pos += remaining;
+ bytes_written += remaining;
+ if (stream->buf_write_pos == BUFSIZ)
+ fflush(stream);
+ }
+ else
+ {
+ memcpy(stream->buffer + stream->buf_write_pos, src + bytes_written, space_left);
+ stream->buf_write_pos += space_left;
+ bytes_written += space_left;
+ fflush(stream);
+ }
+ }
+
+ return bytes_written / size;
+}
+
+int fseek(FILE *stream, long offset, int whence)
+{
+ if (!stream)
+ return -1;
+
+ if (stream->buf_write_pos > 0)
+ {
+ fflush(stream);
+ }
+ stream->buf_read_pos = 0;
+ stream->buf_read_cnt = 0;
+ stream->eof = 0;
+
+ if (lseek(stream->fd, offset, whence) < 0)
+ {
+ stream->error = 1;
+ return -1;
+ }
+ return 0;
+}
+long ftell(FILE *stream)
+{
+ if (!stream)
+ return -1L;
+ int64 ret = lseek(stream->fd, 0, SEEK_CUR);
+ if (ret < 0)
+ {
+ stream->error = 1;
+ return -1L;
+ }
+ return (long)ret;
+}
+void rewind(FILE *stream)
+{
+ if (stream)
+ {
+ fseek(stream, 0, SEEK_SET);
+ stream->error = 0;
+ }
+}
+
+int feof(FILE *stream)
+{
+ return (stream) ? stream->eof : 1;
+}
+int ferror(FILE *stream)
+{
+ return (stream) ? stream->error : 1;
+}
+void clearerr(FILE *stream)
+{
+ if (stream)
+ {
+ stream->error = 0;
+ stream->eof = 0;
+ }
+}
+
+int ungetc(int c, FILE *stream)
+{
+ if (!stream || c == EOF)
+ return EOF;
+
+ if (stream->buf_read_pos > 0)
+ {
+ stream->buf_read_pos--;
+ stream->buffer[stream->buf_read_pos] = (char)c;
+ stream->eof = 0;
+ return c;
+ }
+ return EOF;
+} \ No newline at end of file