From 1c8e37f63d413a559825020289178d442537449e Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Fri, 2 Jan 2026 10:09:37 +0800 Subject: dev(sell): add simple libc and implement shell please see the log for detail. --- src/ulib/stdio.c | 1052 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1052 insertions(+) create mode 100644 src/ulib/stdio.c (limited to 'src/ulib/stdio.c') 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 -- cgit v1.2.3