commit 638edb516650cacce27ec6c41b61224d0c5c11a5 Author: Mark Date: Tue Jan 7 16:22:33 2020 +0200 Initial commit after splitting userspace from kernel tree diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e63705b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +kernel-hdr +build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ecb9dc9 --- /dev/null +++ b/Makefile @@ -0,0 +1,96 @@ +O?=build +STAGE=$(O)/stage +KERNEL_HDRS?=kernel-hdr + +CC=$(CROSS_COMPILE)gcc + +DIRS=$(O) \ + $(O)/libc \ + $(STAGE) +HDRS=$(shell find $(S) -type f -name "*.h") +STAGE_BIN=$(STAGE)/init \ + $(STAGE)/bin/hexd \ + $(STAGE)/bin/ls \ + $(STAGE)/bin/reboot + +CFLAGS=-ffreestanding \ + -nostdlib \ + -mno-sse \ + -mno-sse2 \ + -Ilibc/include \ + -ggdb \ + -O0 \ + -I$(KERNEL_HDRS) + +usr_LDFLAGS=-nostdlib \ + -lgcc \ + -Tlibc/program.ld +usr_STATIC_LIBS=$(O)/libc.a + +libc_CRTI=$(O)/libc/crti.o +libc_CRTN=$(O)/libc/crtn.o +libc_OBJS=$(O)/libc/crt0.o \ + $(O)/libc/syscall.o \ + $(O)/libc/vsnprintf.o \ + $(O)/libc/printf.o \ + $(O)/libc/string.o \ + $(O)/libc/errno.o \ + $(O)/libc/stdio.o \ + $(O)/libc/init.o \ + $(O)/libc/malloc.o \ + $(O)/libc/dirent.o \ + $(O)/libc/signal.o \ + $(O)/libc/global.o \ + $(O)/libc/time.o + +sys_CRTBEGIN=$(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o) +sys_CRTEND=$(shell $(CC) $(CFLAGS) -print-file-name=crtend.o) + +all: mkdirs $(O)/initrd.img + +clean: + rm -rf $(O) + +$(O)/initrd.img: mkstage-etc $(STAGE_BIN) + cd $(STAGE) && tar czf $(abspath $@) * + +mkdirs: + mkdir -p $(DIRS) + +mkstage-etc: + mkdir -p $(STAGE)/dev $(STAGE)/mnt $(STAGE)/bin + cp -r etc $(STAGE) + +# Application building +$(STAGE)/init: init.c $(libc_CRTI) $(libc_CRTN) $(O)/libc.a + @printf " CC\t%s\n" $(@:$(STAGE)/%=/%) + @$(CC) -o $@ $(CFLAGS) $(usr_LDFLAGS) \ + $(libc_CRTI) \ + $(sys_CRTBEGIN) \ + init.c \ + $(sys_CRTEND) \ + $(libc_CRTN) \ + $(usr_STATIC_LIBS) + +$(STAGE)/bin/%: core/bin/%.c $(libc_CRTI) $(libc_CRTN) $(O)/libc.a + @printf " CC\t%s\n" $(@:$(STAGE)/%=/%) + @$(CC) -o $@ $(CFLAGS) $(usr_LDFLAGS) \ + $(libc_CRTI) \ + $(sys_CRTBEGIN) \ + $< \ + $(sys_CRTEND) \ + $(libc_CRTN) \ + $(usr_STATIC_LIBS) + +# libc building +$(O)/libc.a: $(libc_OBJS) + @printf " AR\t%s\n" $(@:$(O)/%=%) + @$(AR) rcs $@ $(libc_OBJS) + +$(O)/libc/%.o: libc/%.c $(HDRS) + @printf " CC\t%s\n" $(@:$(O)/%=%) + @$(CC) -c -o $@ $(CFLAGS) $< + +$(O)/libc/%.o: libc/%.S + @printf " AS\t%s\n" $(@:$(O)/%=%) + @$(CC) -c -o $@ $(CFLAGS) $< diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f21f39 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +Make a symlink to kernel headers (/include) directory named kernel-hdr or +specify direct path using `KERNEL_HDRS` env variable, specify proper ``CROSS_COMPILE` and run +make diff --git a/core/bin/hexd.c b/core/bin/hexd.c new file mode 100644 index 0000000..0b74eee --- /dev/null +++ b/core/bin/hexd.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include + +#define LINE_LENGTH 16 + +static void line_print(size_t off, const char *line, size_t len) { + printf("%08x: ", off); + for (size_t i = 0; i < LINE_LENGTH; ++i) { + // XXX: This is needed because I didn't implement h/hh modifiers in printf + uint64_t byte = (uint64_t) line[i] & 0xFF; + if (i < len) { + printf("%02x", byte); + } else { + printf(" "); + } + if (i % 2) { + printf(" "); + } + } + printf("| "); + for (size_t i = 0; i < len; ++i) { + // TODO: isprint? + if (line[i] >= ' ') { + printf("%c", line[i]); + } else { + printf("."); + } + } + printf("\n"); +} + +int main(int argc, char **argv) { + int fd; + const char *path; + char buf[512]; + char line[LINE_LENGTH]; + ssize_t bread; + size_t offset; + size_t linel; + size_t n_full_zero; + + if (argc != 2) { + printf("wrong\n"); + return -1; + } + + path = argv[1]; + printf("open file %s\n", path); + + if ((fd = open(path, O_RDONLY, 0)) < 0) { + perror(path); + return -1; + } + + offset = 0; + linel = 0; + n_full_zero = 0; + while ((bread = read(fd, buf, sizeof(buf))) > 0) { + for (size_t i = 0; i < bread; ++i) { + line[linel++] = buf[i]; + if (linel == LINE_LENGTH) { + // Check if the line is all zeros + int all_zeros = 1; + for (size_t j = 0; j < LINE_LENGTH; ++j) { + if (line[j]) { + all_zeros = 0; + break; + } + } + if (all_zeros) { + ++n_full_zero; + } else { + n_full_zero = 0; + } + + if (n_full_zero < 3) { + line_print(offset, line, linel); + } else if (n_full_zero == 3) { + printf(" ... \n"); + } + offset += LINE_LENGTH; + linel = 0; + } + } + } + if (linel) { + line_print(offset, line, linel); + } + + close(fd); + + return 0; +} diff --git a/core/bin/ls.c b/core/bin/ls.c new file mode 100644 index 0000000..f5547f5 --- /dev/null +++ b/core/bin/ls.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include + +#define LS_COLOR (1 << 0) +#define LS_DETAIL (1 << 1) + +#define COLOR_DIR "\033[36m" +#define COLOR_DEV "\033[33m" +#define COLOR_UNKNOWN "\033[43m" + +#define COLOR_RESET "\033[0m" + +static int ls_dir(const char *path, int flags) { + int res; + DIR *dir; + struct dirent *ent; + struct stat ent_stat; + char ent_path[512]; + char t; + + if (!(dir = opendir(path))) { + perror(path); + return -1; + } + + while ((ent = readdir(dir))) { + if (ent->d_name[0] != '.') { + if (flags & LS_DETAIL) { + snprintf(ent_path, sizeof(ent_path), "%s/%s", + strcmp(path, "") ? path : ".", ent->d_name); + if (stat(ent_path, &ent_stat) != 0) { + printf("?????????? ? ? ? "); + } else { + switch (ent_stat.st_mode & S_IFMT) { + case S_IFDIR: + t = 'd'; + break; + case S_IFREG: + t = '-'; + break; + case S_IFBLK: + t = 'b'; + break; + case S_IFCHR: + t = 'c'; + break; + default: + t = '?'; + break; + } + + printf("%c%c%c%c%c%c%c%c%c%c ", + t, + (ent_stat.st_mode & S_IRUSR) ? 'r' : '-', + (ent_stat.st_mode & S_IWUSR) ? 'w' : '-', + (ent_stat.st_mode & S_IXUSR) ? 'x' : '-', + (ent_stat.st_mode & S_IRGRP) ? 'r' : '-', + (ent_stat.st_mode & S_IWGRP) ? 'w' : '-', + (ent_stat.st_mode & S_IXGRP) ? 'x' : '-', + (ent_stat.st_mode & S_IROTH) ? 'r' : '-', + (ent_stat.st_mode & S_IWOTH) ? 'w' : '-', + (ent_stat.st_mode & S_IXOTH) ? 'x' : '-'); + + printf("%4u %4u %8u ", + ent_stat.st_gid, + ent_stat.st_uid, + ent_stat.st_blocks); + } + } + + if (flags & LS_COLOR) { + switch (ent->d_type) { + case DT_REG: + break; + case DT_BLK: + case DT_CHR: + write(STDOUT_FILENO, COLOR_DEV, 5); + break; + case DT_DIR: + write(STDOUT_FILENO, COLOR_DIR, 5); + break; + default: + write(STDOUT_FILENO, COLOR_UNKNOWN, 5); + break; + } + } + write(STDOUT_FILENO, ent->d_name, strlen(ent->d_name)); + if (flags & LS_COLOR) { + write(STDOUT_FILENO, COLOR_RESET, 4); + } + putchar('\n'); + } + } + + closedir(dir); + + return 0; +} + +int main(int argc, char **argv) { + // Basic getopt-like features + int first = -1; + int flags = 0; + + for (int i = 1; i < argc; ++i) { + if (argv[i][0] != '-') { + first = i; + break; + } else { + if (!strcmp(argv[i], "-c")) { + flags |= LS_COLOR; + } else if (!strcmp(argv[i], "-d")) { + flags |= LS_DETAIL; + } else { + printf("Unknown option: %s\n", argv[i]); + } + } + } + + if (first == -1) { + ls_dir("", flags); + } else { + for (int i = first; i < argc; ++i) { + if (first != argc - 1) { + printf("%s:\n", argv[i]); + } + ls_dir(argv[i], flags); + } + } + + return 0; +} diff --git a/core/bin/reboot.c b/core/bin/reboot.c new file mode 100644 index 0000000..5d8a09a --- /dev/null +++ b/core/bin/reboot.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + const char *arg = NULL; + + if (argc == 2) { + arg = argv[1]; + } + + int res; + unsigned int cmd = YGG_REBOOT_RESTART; + + if (arg) { + if (!strcmp(arg, "-s")) { + cmd = YGG_REBOOT_POWER_OFF; + } else if (!strcmp(arg, "-h")) { + cmd = YGG_REBOOT_HALT; + } + } + + if ((res = reboot(YGG_REBOOT_MAGIC1, YGG_REBOOT_MAGIC2, cmd, NULL)) < 0) { + perror("reboot()"); + return res; + } + + while (1) { + usleep(1000000); + } +} diff --git a/etc/file.txt b/etc/file.txt new file mode 100644 index 0000000..ff7382d --- /dev/null +++ b/etc/file.txt @@ -0,0 +1 @@ +This file is inside the initrd. diff --git a/init.c b/init.c new file mode 100644 index 0000000..908d145 --- /dev/null +++ b/init.c @@ -0,0 +1,410 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Cursor helpers + +#define curs_save() \ + write(STDOUT_FILENO, "\033[s", 3) +#define curs_unsave() \ + write(STDOUT_FILENO, "\033[u", 3) +#define curs_set(row, col) \ + printf("\033[%d;%df", row, col) +#define clear() \ + write(STDOUT_FILENO, "\033[2J", 4) + +#define BOX_ANGLE_UL "\332" +#define BOX_ANGLE_UR "\277" +#define BOX_ANGLE_LL "\300" +#define BOX_ANGLE_LR "\331" +#define BOX_HOR "\304" +#define BOX_VERT "\263" + +// + +extern __attribute__((noreturn)) void abort(void); + +#define assert(x) \ + if (!(x)) abort() + +struct builtin { + const char *name; + const char *desc; + int (*run) (const char *arg); +}; + +static int b_cd(const char *path); +static int b_pwd(const char *_); +static int b_cat(const char *path); +static int b_curs(const char *arg); +static int b_sleep(const char *arg); +static int b_help(const char *arg); +static int b_clear(const char *arg); + +static struct builtin builtins[] = { + { + "cd", + "Change working directory", + b_cd, + }, + { + "pwd", + "Print working directory", + b_pwd, + }, + { + "cat", + "Print file contents" /* Concatenate files */, + b_cat + }, + { + "sleep", + "Sleep N seconds", + b_sleep + }, + { + "clear", + "Clear terminal", + b_clear, + }, + { + "help", + "Please help me", + b_help + }, + { NULL, NULL, NULL } +}; + +static int b_cd(const char *arg) { + int res; + if (!arg) { + arg = ""; + } + + if ((res = chdir(arg)) < 0) { + perror(arg); + } + + return res; +} + +static int b_pwd(const char *arg) { + char buf[512]; + if (!getcwd(buf, sizeof(buf))) { + perror("getcwd()"); + return -1; + } else { + puts(buf); + } + return 0; +} + +static int b_cat(const char *arg) { + char buf[512]; + int fd; + ssize_t bread; + + if (!arg) { + return -1; + } + + if ((fd = open(arg, O_RDONLY, 0)) < 0) { + perror(arg); + return fd; + } + + while ((bread = read(fd, buf, sizeof(buf))) > 0) { + write(STDOUT_FILENO, buf, bread); + } + + close(fd); + + return 0; +} + +static int b_curs(const char *arg) { + char c; + + size_t w = 60; + size_t h = 20; + size_t off_y = (25 - h) / 2 + 1; + size_t off_x = (80 - w) / 2; + + const char *lines[18] = { + NULL, + "Demo something", + NULL, + "Slow as hell" + }; + + clear(); + + while (1) { + printf("\033[47;30m"); + + curs_set(off_y, off_x); + printf(BOX_ANGLE_UL); + for (size_t i = 0; i < w; ++i) { + printf(BOX_HOR); + } + printf(BOX_ANGLE_UR); + + for (size_t i = 0; i < h - 2; ++i) { + curs_set(off_y + 1 + i, off_x); + printf(BOX_VERT); + for (size_t j = 0; j < w; ++j) { + printf(" "); + } + if (lines[i]) { + curs_set(off_y + 1 + i, off_x + (w - strlen(lines[i])) / 2); + printf("%s", lines[i]); + } + curs_set(off_y + 1 + i, off_x + w + 1); + printf(BOX_VERT); + } + + curs_set(off_y + h - 3, off_x + (w - 8) / 2); + printf("\033[0m\033[7m[ OK ]\033[47;30m"); + curs_set(off_y + h - 1, off_x); + printf(BOX_ANGLE_LL); + for (size_t i = 0; i < w; ++i) { + printf(BOX_HOR); + } + printf(BOX_ANGLE_LR); + + printf("\033[0m"); + + curs_set(1, 1); + + if (read(STDIN_FILENO, &c, 1) < 0) { + break; + } + + if (c == 'q' || c == '\n') { + break; + } + } + + curs_set(1, 1); + + return 0; +} + +static int b_sleep(const char *arg) { + if (!arg) { + return -1; + } + int seconds = 0; + while (*arg) { + seconds *= 10; + seconds += *arg - '0'; + ++arg; + } + + struct timespec ts = { seconds, 0 }; + if ((seconds = nanosleep(&ts, NULL))) { + perror("nanosleep()"); + return seconds; + } + + return 0; +} + +static int b_clear(const char *arg) { + clear(); + curs_set(1, 1); + return 0; +} + +static int b_help(const char *arg) { + if (arg) { + // Describe a specific command + for (size_t i = 0; builtins[i].run; ++i) { + if (!strcmp(arg, builtins[i].name)) { + printf("%s: %s\n", builtins[i].name, builtins[i].desc); + return 0; + } + } + + printf("%s: Unknown command\n", arg); + return -1; + } else { + for (size_t i = 0; builtins[i].run; ++i) { + printf("%s: %s\n", builtins[i].name, builtins[i].desc); + } + + return 0; + } +} + +static void prompt(void) { + char cwd[512]; + if (!getcwd(cwd, sizeof(cwd))) { + cwd[0] = '?'; + cwd[1] = 0; + } + printf("\033[36mygg\033[0m %s > ", cwd); +} + +static int cmd_subproc_exec(const char *abs_path, const char *cmd, const char *e) { + // Maximum of 8 arguments 64 chars each (63) + // Split input argument into pieces by space + char args[64 * 8] = {0}; + const char *p = e; + const char *t = NULL; + size_t argc = 0; + while (p) { + t = strchr(p, ' '); + if (!t) { + // Last argument + assert(strlen(p) < 64); + strcpy(&args[argc++ * 64], p); + break; + } else { + assert(t - p < 64); + strncpy(&args[argc * 64], p, t - p); + args[(argc++ * 64) + (t - p)] = 0; + p = t + 1; + while (*p == ' ') { + ++p; + } + if (!*p) { + break; + } + } + } + + // Fill arg pointer array + const char *argp[10] = { cmd }; + for (size_t i = 0; i < argc; ++i) { + argp[i + 1] = &args[i * 64]; + } + argp[argc + 1] = NULL; + + int pid = fork(); + int res; + int status; + + switch (pid) { + case -1: + perror("fork()"); + return -1; + case 0: + if (execve(abs_path, argp, NULL) != 0) { + perror("execve()"); + } + exit(-1); + default: + if (waitpid(pid, &status) != 0) { + perror("waitpid()"); + } + return 0; + } +} + +static int cmd_exec(const char *line) { + char cmd[64]; + const char *e = strchr(line, ' '); + + if (!e) { + assert(strlen(line) < 64); + strcpy(cmd, line); + } else { + assert(e - line < 64); + strncpy(cmd, line, e - line); + cmd[e - line] = 0; + ++e; + while (*e == ' ') { + ++e; + } + } + + if (e && !*e) { + e = NULL; + } + + for (size_t i = 0; builtins[i].run; ++i) { + if (!strcmp(cmd, builtins[i].name)) { + return builtins[i].run(e); + } + } + + // If command starts with ./ or /, try to execute it + if (((cmd[0] == '.' && cmd[1] == '/') || cmd[0] == '/') && access(cmd, X_OK) == 0) { + return cmd_subproc_exec(cmd, cmd, e); + } + // Try to execute binary from /bin + char path_buf[512]; + snprintf(path_buf, sizeof(path_buf), "/bin/%s", cmd); + if (access(path_buf, X_OK) == 0) { + return cmd_subproc_exec(path_buf, cmd, e); + } + + printf("%s: Unknown command\n", cmd); + return -1; +} + +int main(int argc, char **argv) { + if (getpid() != 1) { + printf("Won't work if PID is not 1\n"); + return -1; + } + char linebuf[512]; + char c; + size_t l = 0; + int res; + + prompt(); + +#if 0 + if ((res = fork()) < 0) { + perror("fork()"); + return -1; + } else if (res == 0) { + if (execve("/time", NULL, NULL) != 0) { + perror("execve()"); + } + exit(-1); + } +#endif + + while (1) { + if (read(STDIN_FILENO, &c, 1) < 0) { + break; + } + + if (c == '\b') { + if (l) { + linebuf[--l] = 0; + printf("\033[D \033[D"); + } + continue; + } + if (c == '\n') { + write(STDOUT_FILENO, &c, 1); + linebuf[l] = 0; + + if (!strcmp(linebuf, "exit")) { + break; + } + + l = 0; + if ((res = cmd_exec(linebuf)) != 0) { + printf("\033[31m= %d\033[0m\n", res); + } + prompt(); + continue; + } + + linebuf[l++] = c; + write(STDOUT_FILENO, &c, 1); + } + + return -1; +} diff --git a/libc/crt0.S b/libc/crt0.S new file mode 100644 index 0000000..9d23bfd --- /dev/null +++ b/libc/crt0.S @@ -0,0 +1,25 @@ +.section .text +.global _start +.type _start, %function +// Arguments: +// %rdi - argp +_start: + movq $0, %rbp + // For callee linkage + pushq %rbp + pushq %rbp + movq %rsp, %rbp + + push %rdi + call __libc_init + + call _init + + mov __libc_argc, %rdi + pop %rsi + + // exit(main(argc, argv)) + call main + movq %rax, %rdi + call exit +.size _start, . - _start diff --git a/libc/crti.S b/libc/crti.S new file mode 100644 index 0000000..a85782e --- /dev/null +++ b/libc/crti.S @@ -0,0 +1,16 @@ +/* x86_64 crti.s */ +.section .init +.global _init +.type _init, @function +_init: + push %rbp + movq %rsp, %rbp + /* gcc will nicely put the contents of crtbegin.o's .init section here. */ + +.section .fini +.global _fini +.type _fini, @function +_fini: + push %rbp + movq %rsp, %rbp + /* gcc will nicely put the contents of crtbegin.o's .fini section here. */ diff --git a/libc/crtn.S b/libc/crtn.S new file mode 100644 index 0000000..b446d9f --- /dev/null +++ b/libc/crtn.S @@ -0,0 +1,10 @@ +/* x86_64 crtn.s */ +.section .init + /* gcc will nicely put the contents of crtend.o's .init section here. */ + popq %rbp + ret + +.section .fini + /* gcc will nicely put the contents of crtend.o's .fini section here. */ + popq %rbp + ret diff --git a/libc/dirent.c b/libc/dirent.c new file mode 100644 index 0000000..73969e7 --- /dev/null +++ b/libc/dirent.c @@ -0,0 +1,57 @@ +#include "dirent.h" +#include "bits/syscall.h" +#include +#include +#include + +extern void *malloc(size_t count); +extern void free(void *p); + +struct DIR_private { + int fd; + struct dirent buf; +}; + +DIR *opendir(const char *path) { + int fd; + DIR *res; + + if (!(res = malloc(sizeof(struct DIR_private)))) { + errno = ENOMEM; + return NULL; + } + + + if ((fd = open(path, O_DIRECTORY | O_RDONLY, 0)) < 0) { + fd = errno; + free(res); + errno = fd; + // errno is set + return NULL; + } + + res->fd = fd; + memset(&res->buf, 0, sizeof(res->buf)); + + return res; +} + +void closedir(DIR *dirp) { + if (!dirp) { + return; + } + close(dirp->fd); + free(dirp); +} + +struct dirent *readdir(DIR *dirp) { + ssize_t res; + if (!dirp) { + errno = EBADF; + return NULL; + } + if ((res = sys_readdir(dirp->fd, &dirp->buf)) <= 0) { + return NULL; + } + return &dirp->buf; +} diff --git a/libc/errno.c b/libc/errno.c new file mode 100644 index 0000000..941c59a --- /dev/null +++ b/libc/errno.c @@ -0,0 +1,57 @@ +#include +#include + +static const char *err_strings0[35] = { + "Success", + "Operation not permitted", + "No such file or directory", + "No such process", + "Interrupted system call", + "I/O error", + "No such device or address", + "Argument list too long", + "Exec format error", + "Bad file number", + "No child processes", + "Try again", + "Out of memory", + "Permission denied", + "Bad address", + "Block device required", + "Device or resource busy", + "File exists", + "Cross-device link", + "No such device", + "Not a directory", + "Is a directory", + "Invalid argument", + "File table overflow", + "Too many open files", + "Not a typewriter", + "Text file busy", + "File too large", + "No space left on device", + "Illegal seek", + "Read-only file system", + "Too many links", + "Broken pipe", + "Math argument out of domain of func", + "Math result not representable" +}; + +int errno; + +char *strerror(int n) { + if (n >= 0 && n <= 34) { + return (char *) err_strings0[n]; + } + return (char *) "Unknown error"; +} + +void perror(const char *e) { + if (e && *e) { + printf("%s: %s\n", e, strerror(errno)); + } else { + puts(strerror(errno)); + } +} diff --git a/libc/global.c b/libc/global.c new file mode 100644 index 0000000..1c04fb7 --- /dev/null +++ b/libc/global.c @@ -0,0 +1,4 @@ +#include "bits/global.h" + +void *__cur_brk; +uint64_t __libc_argc = 0; diff --git a/libc/include/bits/global.h b/libc/include/bits/global.h new file mode 100644 index 0000000..6b0435b --- /dev/null +++ b/libc/include/bits/global.h @@ -0,0 +1,5 @@ +#pragma once +#include + +extern void *__cur_brk; +extern uint64_t __libc_argc; diff --git a/libc/include/bits/printf.h b/libc/include/bits/printf.h new file mode 100644 index 0000000..68ffd26 --- /dev/null +++ b/libc/include/bits/printf.h @@ -0,0 +1,7 @@ +#pragma once +#include +#include + +int printf(const char *fmt, ...); +int snprintf(char *buf, size_t size, const char *fmt, ...); +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args); diff --git a/libc/include/bits/syscall.h b/libc/include/bits/syscall.h new file mode 100644 index 0000000..33d9f72 --- /dev/null +++ b/libc/include/bits/syscall.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include +#include +#include +#include + +ssize_t read(int fd, void *buf, size_t count); +ssize_t write(int fd, const void *buf, size_t count); +int open(const char *filename, int flags, int mode); +void close(int fd); +int stat(const char *filename, struct stat *st); +void *mmap(void *addr, size_t len, int prot, int flags, int fd, uintptr_t offset); +int fork(void); +int execve(const char *filename, const char *const argv[], const char *const envp[]); +ssize_t sys_readdir(int fd, struct dirent *entp); +int kill(int pid, int signum); +void __kernel_signal(uintptr_t handler); +__attribute__((noreturn)) void __kernel_sigret(void); +int getpid(void); +int chdir(const char *filename); +char *getcwd(char *buf, size_t size); +int nanosleep(const struct timespec *req, struct timespec *rem); +int openpty(int *amaster, int *aslave); +int gettimeofday(struct timeval *tv, struct timezone *tz); +int reboot(int magic1, int magic2, unsigned int cmd, void *arg); +int access(const char *filename, int mode); +int waitpid(int pid, int *status); + +__attribute__((noreturn)) void exit(int code); diff --git a/libc/include/dirent.h b/libc/include/dirent.h new file mode 100644 index 0000000..adb5774 --- /dev/null +++ b/libc/include/dirent.h @@ -0,0 +1,8 @@ +#pragma once +#include + +typedef struct DIR_private DIR; + +DIR *opendir(const char *path); +void closedir(DIR *dirp); +struct dirent *readdir(DIR *dirp); diff --git a/libc/include/errno.h b/libc/include/errno.h new file mode 100644 index 0000000..d85454b --- /dev/null +++ b/libc/include/errno.h @@ -0,0 +1,7 @@ +#pragma once +#include + +extern int errno; + +char *strerror(int errnum); +void perror(const char *s); diff --git a/libc/include/signal.h b/libc/include/signal.h new file mode 100644 index 0000000..0e1c34f --- /dev/null +++ b/libc/include/signal.h @@ -0,0 +1,8 @@ +#pragma once +#include + +typedef void (*sighandler_t) (int); + +sighandler_t signal(int signum, sighandler_t handler); + +int raise(int signum); diff --git a/libc/include/stdio.h b/libc/include/stdio.h new file mode 100644 index 0000000..c89bef8 --- /dev/null +++ b/libc/include/stdio.h @@ -0,0 +1,6 @@ +#pragma once + +int puts(const char *s); +int putchar(int c); + +#include "bits/printf.h" diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h new file mode 100644 index 0000000..46de31c --- /dev/null +++ b/libc/include/stdlib.h @@ -0,0 +1,3 @@ +#pragma once + +void abort(void); diff --git a/libc/include/string.h b/libc/include/string.h new file mode 100644 index 0000000..0d57aa0 --- /dev/null +++ b/libc/include/string.h @@ -0,0 +1,17 @@ +#pragma once +#include + +void *memcpy(void *restrict dst, const void *restrict src, size_t cnt); +void *memmove(void *dst, const void *src, size_t cnt); +void *memset(void *dst, int v, size_t count); +int memcmp(const void *a, const void *b, size_t count); + +size_t strlen(const char *s); +int strncmp(const char *a, const char *b, size_t lim); +int strcmp(const char *a, const char *b); +char *strchr(const char *a, int c); +char *strrchr(const char *a, int c); +char *strcpy(char *dst, const char *src); +char *strncpy(char *dst, const char *src, size_t lim); +char *strcat(char *dst, const char *src); +char *strncat(char *dst, const char *src, size_t lim); diff --git a/libc/include/time.h b/libc/include/time.h new file mode 100644 index 0000000..2bfead5 --- /dev/null +++ b/libc/include/time.h @@ -0,0 +1,4 @@ +#pragma once +#include + +int usleep(long us); diff --git a/libc/include/unistd.h b/libc/include/unistd.h new file mode 100644 index 0000000..5485295 --- /dev/null +++ b/libc/include/unistd.h @@ -0,0 +1,10 @@ +#pragma once +#include "bits/syscall.h" +#include + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +void *sbrk(intptr_t diff); +int reboot(int magic1, int magic2, unsigned int cmd, void *arg); diff --git a/libc/init.c b/libc/init.c new file mode 100644 index 0000000..77cd98c --- /dev/null +++ b/libc/init.c @@ -0,0 +1,18 @@ +#include +#include "bits/global.h" + +extern char __heap_start; +extern void __libc_signal_init(void); +static const char *argp_dummy[] = { 0 }; + +void __libc_init(char **argp) { + __libc_signal_init(); + __libc_argc = 0; + if (argp) { + while (argp[__libc_argc]) { + ++__libc_argc; + } + } + __cur_brk = &__heap_start; + errno = 0; +} diff --git a/libc/malloc.c b/libc/malloc.c new file mode 100644 index 0000000..49c2a9c --- /dev/null +++ b/libc/malloc.c @@ -0,0 +1,15 @@ +#include +#include "bits/global.h" + +// XXX: I'll implement a proper heap in the following commits +void *malloc(size_t count) { + void *p = __cur_brk; + if (sbrk(count) == p) { + return NULL; + } + return p; +} + +void free(void *ptr) { + +} diff --git a/libc/printf.c b/libc/printf.c new file mode 100644 index 0000000..0bcaaec --- /dev/null +++ b/libc/printf.c @@ -0,0 +1,25 @@ +#include "bits/printf.h" +#include +#define PRINTF_BUFFER_SIZE 1024 + +int printf(const char *fmt, ...) { + char buf[PRINTF_BUFFER_SIZE]; + va_list args; + va_start(args, fmt); + int res = vsnprintf(buf, PRINTF_BUFFER_SIZE, fmt, args); + va_end(args); + + // TODO: use libc's FILE * instead + return write(STDOUT_FILENO, buf, res); +} + +int snprintf(char *buf, size_t lim, const char *fmt, ...) { + va_list args; + int res; + + va_start(args, fmt); + res = vsnprintf(buf, PRINTF_BUFFER_SIZE, fmt, args); + va_end(args); + + return res; +} diff --git a/libc/program.ld b/libc/program.ld new file mode 100644 index 0000000..f7d68df --- /dev/null +++ b/libc/program.ld @@ -0,0 +1,35 @@ +/* + * Userspace program linker script, + * partially derived from GCC's -static one + */ +ENTRY(_start) + +SECTIONS { + . = 0x400000; + + .text : ALIGN(4K) { + KEEP (*(SORT_NONE(.init))) + *(.text) + KEEP (*(SORT_NONE(.fini))) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .rodata : { + *(.rodata) + *(.eh_frame) + } + + .data : ALIGN(4K) { + *(.data) + } + + .bss : ALIGN(4K) { + *(.bss) + } + + __program_end = .; + __heap_start = .; +} diff --git a/libc/signal.c b/libc/signal.c new file mode 100644 index 0000000..c42dfb1 --- /dev/null +++ b/libc/signal.c @@ -0,0 +1,71 @@ +#include +#include +#include + +static sighandler_t signal_handlers[16] = {0}; + +void SIG_IGN(int signum) { + printf("Ignored: %d\n", signum); + + while (1); +} + +void SIG_DFL(int signum) { + int pid = getpid(); + + switch (signum) { + case SIGKILL: + printf("%d: killed\n", pid); + break; + case SIGABRT: + printf("%d: aborted\n", pid); + break; + case SIGSEGV: + printf("%d: segmentation fault\n", pid); + break; + default: + printf("%d: signal %d\n", pid, signum); + break; + } + + exit(1); +} + +static void __libc_signal_handle(int signum) { + if (signum >= 16) { + SIG_DFL(signum); + } else { + signal_handlers[signum](signum); + } + + __kernel_sigret(); +} + +void __libc_signal_init(void) { + for (size_t i = 0; i < 16; ++i) { + signal_handlers[i] = SIG_DFL; + } + + __kernel_signal((uintptr_t) __libc_signal_handle); +} + +sighandler_t signal(int signum, sighandler_t new_handler) { + sighandler_t old_handler; + + if (signum >= 16) { + printf("Fuck\n"); + exit(1); + } + + old_handler = signal_handlers[signum]; + signal_handlers[signum] = new_handler; + return old_handler; +} + +int raise(int signum) { + return kill(getpid(), signum); +} + +void abort(void) { + kill(getpid(), SIGABRT); +} diff --git a/libc/stdio.c b/libc/stdio.c new file mode 100644 index 0000000..e22aaf2 --- /dev/null +++ b/libc/stdio.c @@ -0,0 +1,22 @@ +#include +#include +#include + +int puts(const char *s) { + size_t l = strlen(s); + int w = write(STDOUT_FILENO, s, l); + char c = '\n'; + + if (w == l) { + write(STDOUT_FILENO, &c, 1); + } + return w; +} + +int putchar(int c) { + int res; + if ((res = write(STDOUT_FILENO, &c, 1)) < 0) { + return res; + } + return c; +} diff --git a/libc/string.c b/libc/string.c new file mode 100644 index 0000000..c4d835d --- /dev/null +++ b/libc/string.c @@ -0,0 +1,167 @@ +#include + +char *strcat(char *dst, const char *src) { + size_t l = strlen(dst); + strcpy(dst + l, src); + return dst; +} + +char *strncat(char *dst, const char *src, size_t lim) { + size_t l = strlen(dst); + if (l < lim) { + return dst; + } + size_t r = lim - l; + strncpy(dst + l, src, r); + return dst; +} + +size_t strlen(const char *a) { + size_t s = 0; + while (*a++) { + ++s; + } + return s; +} + +int strncmp(const char *a, const char *b, size_t n) { + size_t c = 0; + for (; c < n && (*a || *b); ++c, ++a, ++b) { + if (*a != *b) { + return -1; + } + } + return 0; +} + +int strcmp(const char *a, const char *b) { + if (a == b) { + return 0; + } + if (a == NULL || b == NULL) { + return -1; + } + for (; *a || *b; ++a, ++b) { + if (*a != *b) { + return 1; + } + } + return 0; +} + +char *strcpy(char *dst, const char *src) { + size_t i; + for (i = 0; src[i]; ++i) { + dst[i] = src[i]; + } + dst[i] = 0; + return dst; +} + +char *strncpy(char *dst, const char *src, size_t lim) { + size_t i = 0; + while (i < lim) { + if (!src[i]) { + dst[i] = 0; + return dst; + } + dst[i] = src[i]; + ++i; + } + return dst; +} + +char *strchr(const char *s, int c) { + while (*s) { + if (*s == (char) c) { + return (char *) s; + } + ++s; + } + return NULL; +} + +char *strrchr(const char *s, int c) { + ssize_t l = (ssize_t) strlen(s); + if (!l) { + return NULL; + } + while (l >= 0) { + if (s[l] == (char) c) { + return (char *) &s[l]; + } + --l; + } + return NULL; +} + +int memcmp(const void *a, const void *b, size_t n) { + const char *l = a; + const char *r = b; + for (; n && *l == *r; --n, ++l, ++r); + return n ? *l - *r : 0; +} + +void *memmove(void *dest, const void *src, size_t n) { + char *d = dest; + const char *s = src; + + if (d == s) { + return d; + } + + if ((s + n) <= d || (d + n) <= s) { + return memcpy(d, s, n); + } + + if (d < s) { + if (((uintptr_t) s) % sizeof(size_t) == ((uintptr_t) d) % sizeof(size_t)) { + while (((uintptr_t) d) % sizeof(size_t)) { + if (!n--) { + return dest; + } + *d++ = *s++; + } + for (; n >= sizeof(size_t); n -= sizeof(size_t), d += sizeof(size_t), s += sizeof(size_t)) { + *((size_t *) d) = *((size_t *) s); + } + } + for (; n; n--) { + *d++ = *s++; + } + } else { + if (((uintptr_t) s) % sizeof(size_t) == ((uintptr_t) d) % sizeof(size_t)) { + while (((uintptr_t) (d + n)) % sizeof(size_t)) { + if (!n--) { + return dest; + } + d[n] = s[n]; + } + while (n >= sizeof(size_t)) { + n -= sizeof(size_t); + *((size_t *) (d + n)) = *((size_t *) (s + n)); + } + } + while (n) { + n--; + d[n] = s[n]; + } + } + + return dest; +} + +void *memset(void *blk, int v, size_t sz) { + for (size_t i = 0; i < sz; ++i) { + ((char *) blk)[i] = v; + } + return blk; +} + +void *memcpy(void *dst, const void *src, size_t sz) { + for (size_t i = 0; i < sz; ++i) { + ((char *) dst)[i] = ((const char *) src)[i]; + } + return dst; +} + diff --git a/libc/syscall.c b/libc/syscall.c new file mode 100644 index 0000000..2c60c88 --- /dev/null +++ b/libc/syscall.c @@ -0,0 +1,179 @@ +#include "bits/syscall.h" +#include "bits/global.h" +#include + +#define ASM_REGISTER(name) \ + register uint64_t name asm (#name) + +#define ASM_SYSCALL0(r0) ({ \ + ASM_REGISTER(rax) = (uint64_t) (r0); \ + asm volatile ("syscall":::"memory"); \ + rax; \ + }) + +#define ASM_SYSCALL1(r0, r1) ({ \ + ASM_REGISTER(rdi) = (uint64_t) (r1); \ + /* + * Should be the last one because memory accesses for arguments + * fuck up %rax + */ \ + ASM_REGISTER(rax) = (uint64_t) (r0); \ + asm volatile ("syscall":::"memory"); \ + rax; \ + }) + +#define ASM_SYSCALL2(r0, r1, r2) ({ \ + ASM_REGISTER(rdi) = (uint64_t) (r1); \ + ASM_REGISTER(rsi) = (uint64_t) (r2); \ + /* + * Should be the last one because memory accesses for arguments + * fuck up %rax + */ \ + ASM_REGISTER(rax) = (uint64_t) (r0); \ + asm volatile ("syscall":::"memory", "rax", "rdi", "rsi", "rdx"); \ + rax; \ + }) + +#define ASM_SYSCALL3(r0, r1, r2, r3) ({ \ + ASM_REGISTER(rdi) = (uint64_t) (r1); \ + ASM_REGISTER(rsi) = (uint64_t) (r2); \ + ASM_REGISTER(rdx) = (uint64_t) (r3); \ + /* + * Should be the last one because memory accesses for arguments + * fuck up %rax + */ \ + ASM_REGISTER(rax) = (uint64_t) (r0); \ + asm volatile ("syscall":::"memory", "rax", "rdi", "rsi", "rdx"); \ + rax; \ + }) + +#define ASM_SYSCALL4(r0, r1, r2, r3, r4) ({ \ + ASM_REGISTER(rdi) = (uint64_t) (r1); \ + ASM_REGISTER(rsi) = (uint64_t) (r2); \ + ASM_REGISTER(rdx) = (uint64_t) (r3); \ + ASM_REGISTER(r10) = (uint64_t) (r4); \ + /* + * Should be the last one because memory accesses for arguments + * fuck up %rax + */ \ + ASM_REGISTER(rax) = (uint64_t) (r0); \ + asm volatile ("syscall":::"memory", "rax", "rdi", "rsi", "rdx", "r10"); \ + rax; \ + }) + +#define SET_ERRNO(t, r) ({ \ + t res = (t) r; \ + if (res < 0) { \ + errno = -res; \ + } \ + ((int) res) < 0 ? (t) -1 : res; \ + }) + +ssize_t write(int fd, const void *buf, size_t count) { + return SET_ERRNO(ssize_t, ASM_SYSCALL3(SYSCALL_NR_WRITE, fd, buf, count)); +} + +ssize_t read(int fd, void *buf, size_t count) { + return SET_ERRNO(ssize_t, ASM_SYSCALL3(SYSCALL_NR_READ, fd, buf, count)); +} + +ssize_t sys_readdir(int fd, struct dirent *entp) { + return SET_ERRNO(ssize_t, ASM_SYSCALL2(SYSCALL_NR_READDIR, fd, entp)); +} + +int open(const char *filename, int flags, int mode) { + return SET_ERRNO(int, ASM_SYSCALL3(SYSCALL_NR_OPEN, filename, flags, mode)); +} + +void close(int fd) { + (void) ASM_SYSCALL1(SYSCALL_NR_CLOSE, fd); +} + +int stat(const char *filename, struct stat *st) { + return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_STAT, filename, st)); +} + +int brk(void *addr) { + return SET_ERRNO(int, ASM_SYSCALL1(SYSCALL_NR_BRK, addr)); +} + +int fork(void) { + return SET_ERRNO(int, ASM_SYSCALL0(SYSCALL_NR_FORK)); +} + +int execve(const char *filename, const char *const argv[], const char *const envp[]) { + return SET_ERRNO(int, ASM_SYSCALL3(SYSCALL_NR_EXECVE, filename, argv, envp)); +} + +__attribute__((noreturn)) void exit(int code) { + (void) ASM_SYSCALL1(SYSCALL_NR_EXIT, code); + while (1); +} + +int kill(int pid, int signum) { + return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_KILL, pid, signum)); +} + +void __kernel_signal(uintptr_t entry) { + (void) ASM_SYSCALL1(SYSCALL_NRX_SIGNAL, entry); +} + +__attribute__((noreturn)) void __kernel_sigret(void) { + (void) ASM_SYSCALL0(SYSCALL_NRX_SIGRET); + while (1); +} + +int getpid(void) { + return ASM_SYSCALL0(SYSCALL_NR_GETPID); +} + +int chdir(const char *filename) { + return SET_ERRNO(int, ASM_SYSCALL1(SYSCALL_NR_CHDIR, filename)); +} + +char *getcwd(char *buf, size_t lim) { + if (SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_GETCWD, buf, lim)) == 0) { + return buf; + } else { + return NULL; + } +} + +int nanosleep(const struct timespec *req, struct timespec *rem) { + return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_NANOSLEEP, req, rem)); +} + +int openpty(int *master, int *slave) { + return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NRX_OPENPTY, master, slave)); +} + +int gettimeofday(struct timeval *tv, struct timezone *tz) { + return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_GETTIMEOFDAY, tv, tz)); +} + +int reboot(int magic1, int magic2, unsigned int cmd, void *arg) { + return SET_ERRNO(int, ASM_SYSCALL4(SYSCALL_NR_REBOOT, magic1, magic2, cmd, arg)); +} + +int access(const char *filename, int mode) { + return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_ACCESS, filename, mode)); +} + +int waitpid(int pid, int *status) { + return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NRX_WAITPID, pid, status)); +} + +// Although sbrk() is implemented in userspace, I guess it should also be here +void *sbrk(intptr_t inc) { + if (inc == 0) { + return __cur_brk; + } + + void *new_brk = (void *) ((uintptr_t) __cur_brk + inc); + + if (brk(new_brk) != 0) { + return __cur_brk; + } else { + return __cur_brk = new_brk; + } +} diff --git a/libc/time.c b/libc/time.c new file mode 100644 index 0000000..f75a68a --- /dev/null +++ b/libc/time.c @@ -0,0 +1,7 @@ +#include "time.h" +#include "bits/syscall.h" + +int usleep(long us) { + struct timespec tv = { us / 1000000, (us % 1000) * 1000 }; + return nanosleep(&tv, NULL); +} diff --git a/libc/vsnprintf.c b/libc/vsnprintf.c new file mode 100644 index 0000000..5370763 --- /dev/null +++ b/libc/vsnprintf.c @@ -0,0 +1,255 @@ +#include "bits/printf.h" +#include +#include + +union printfv { + int value_int; + unsigned int value_uint; + long value_long; + uint32_t value_uint32; + uint64_t value_uint64; + uintptr_t value_ptr; + const char *value_str; +}; + +static const char *s_print_xs_set0 = "0123456789abcdef"; +static const char *s_print_xs_set1 = "0123456789ABCDEF"; + +static int vsnprintf_ds(int64_t x, char *res, int s, int sz) { + if (!x) { + res[0] = '0'; + res[1] = 0; + return 1; + } + + int c; + uint64_t v; + + if (sz) { + if (s && x < 0) { + v = (uint64_t) -x; + } else { + s = 0; + v = (uint64_t) x; + } + } else { + if (s && ((int32_t) x) < 0) { + v = (uint64_t) -((int32_t) x); + } else { + s = 0; + v = (uint64_t) x; + } + } + + c = 0; + + while (v) { + res[c++] = '0' + v % 10; + v /= 10; + } + + if (s) { + res[c++] = '-'; + } + + res[c] = 0; + + for (int i = 0, j = c - 1; i < j; ++i, --j) { + res[i] ^= res[j]; + res[j] ^= res[i]; + res[i] ^= res[j]; + } + + return c; +} + +static int vsnprintf_xs(uint64_t v, char *res, const char *set) { + if (!v) { + res[0] = '0'; + res[1] = 0; + return 1; + } + + int c = 0; + + while (v) { + res[c++] = set[v & 0xF]; + v >>= 4; + } + + res[c] = 0; + + for (int i = 0, j = c - 1; i < j; ++i, --j) { + res[i] ^= res[j]; + res[j] ^= res[i]; + res[i] ^= res[j]; + } + + return c; +} + + + +int vsnprintf(char *buf, size_t len, const char *fmt, va_list args) { + union printfv val; + char cbuf[64]; + size_t p = 0; + // padn > 0: + // --------vvvvv + // padn < 0: + // vvvv---------- + int padn; + char padc; + int pads; + int clen; + char c; + +#define __putc(ch) \ + if (p == len - 1) { \ + goto __end; \ + } else { \ + buf[p++] = ch; \ + } + +#define __puts(s, l) \ + if (padn >= 0) { \ + if (l < padn) { \ + size_t padd = padn - l; \ + if (p + padd < len + 1) { \ + memset(buf + p, padc, padd); \ + p += padd; \ + } else { \ + memset(buf + p, padc, len - 1 - p); \ + p = len - 1; \ + goto __end; \ + } \ + } \ + if (p + l < len + 1) { \ + strncpy(buf + p, s, l); \ + p += l; \ + } else { \ + strncpy(buf + p, s, len - 1 - p); \ + p = len - 1; \ + goto __end; \ + } \ + } else { \ + padn = -padn; \ + if (p + l < len + 1) { \ + strncpy(buf + p, s, l); \ + p += l; \ + } else { \ + strncpy(buf + p, s, len - 1 - p); \ + p = len - 1; \ + goto __end; \ + } \ + if (l < padn) { \ + size_t padd = padn - l; \ + if (p + padd < len + 1) { \ + memset(buf + p, padc, padd); \ + p += padd; \ + } else { \ + memset(buf + p, padc, len - 1 - p); \ + p = len - 1; \ + goto __end; \ + } \ + } \ + } + + while ((c = *fmt)) { + switch (c) { + case '%': + c = *++fmt; + padn = 0; + padc = ' '; + pads = 1; + + if (c == '0') { + padc = c; + c = *++fmt; + } + if (c == '-') { + pads = -1; + c = *++fmt; + } + + while (c >= '0' && c <= '9') { + padn *= 10; + padn += c - '0'; + c = *++fmt; + } + + padn *= pads; + + switch (c) { + case 'l': + // Not supported + return -1; + + case 's': + if ((val.value_str = va_arg(args, const char *))) { + __puts(val.value_str, strlen(val.value_str)); + } else { + __puts("(null)", 6); + } + break; + + case 'p': + val.value_ptr = va_arg(args, uintptr_t); + __putc('0'); + __putc('x'); + padn = sizeof(uintptr_t) * 2; + padc = '0'; + clen = vsnprintf_xs(val.value_ptr, cbuf, s_print_xs_set0); + __puts(cbuf, clen); + break; + + case 'i': + case 'd': + val.value_int = va_arg(args, int); + clen = vsnprintf_ds(val.value_int, cbuf, 1, 1); + __puts(cbuf, clen); + break; + case 'u': + val.value_uint = va_arg(args, unsigned int); + clen = vsnprintf_ds(val.value_uint, cbuf, 0, 1); + __puts(cbuf, clen); + break; + + case 'x': + val.value_uint32 = va_arg(args, uint32_t); + clen = vsnprintf_xs(val.value_uint32, cbuf, s_print_xs_set0); + __puts(cbuf, clen); + break; + case 'X': + val.value_uint32 = va_arg(args, uint32_t); + clen = vsnprintf_xs(val.value_uint32, cbuf, s_print_xs_set1); + __puts(cbuf, clen); + break; + + case 'c': + val.value_int = va_arg(args, int); + __putc(val.value_int); + break; + + default: + __putc('%'); + __putc(c); + break; + } + + break; + default: + __putc(c); + break; + } + + ++fmt; + } + +#undef __puts +#undef __putc + +__end: + buf[p] = 0; + return p; +} diff --git a/time.c b/time.c new file mode 100644 index 0000000..d04801d --- /dev/null +++ b/time.c @@ -0,0 +1,12 @@ +#include +#include + +int main() { + struct timeval tv; + while (1) { + usleep(100000); + gettimeofday(&tv, NULL); + + printf("\033[s\033[1;70f%u\033[u", tv.tv_sec); + } +}