push
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/cervus.h>
|
||||
|
||||
extern int __cervus_errno;
|
||||
|
||||
static long __compat_sys_ret(long r)
|
||||
{
|
||||
if (r < 0 && r > -4096) {
|
||||
__cervus_errno = (int)-r;
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int access(const char *path, int mode)
|
||||
{
|
||||
struct stat st;
|
||||
if (!path) { __cervus_errno = EFAULT; return -1; }
|
||||
long r = syscall2(SYS_STAT, path, &st);
|
||||
(void)mode;
|
||||
if (r < 0 && r > -4096) { __cervus_errno = (int)-r; return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __cervus_cwd[512] = "/";
|
||||
|
||||
int fchdir(int fd)
|
||||
{
|
||||
(void)fd;
|
||||
__cervus_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int symlink(const char *target, const char *linkpath)
|
||||
{
|
||||
(void)target; (void)linkpath;
|
||||
__cervus_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t readlink(const char *path, char *buf, size_t bufsiz)
|
||||
{
|
||||
(void)path; (void)buf; (void)bufsiz;
|
||||
__cervus_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sched_yield(void)
|
||||
{
|
||||
syscall0(SYS_YIELD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long pathconf(const char *path, int name)
|
||||
{
|
||||
(void)path;
|
||||
switch (name) {
|
||||
case 0: return 255;
|
||||
case 1: return 512;
|
||||
default: __cervus_errno = EINVAL; return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long fpathconf(int fd, int name)
|
||||
{
|
||||
(void)fd;
|
||||
switch (name) {
|
||||
case 0: return 255;
|
||||
case 1: return 512;
|
||||
default: __cervus_errno = EINVAL; return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int mprotect(void *addr, size_t len, int prot)
|
||||
{
|
||||
return (int)__compat_sys_ret(syscall3(SYS_MPROTECT, addr, len, prot));
|
||||
}
|
||||
|
||||
char *realpath(const char *path, char *resolved)
|
||||
{
|
||||
if (!path) { __cervus_errno = EINVAL; return NULL; }
|
||||
static char sbuf[512];
|
||||
char *out = resolved ? resolved : sbuf;
|
||||
|
||||
if (path[0] == '/') {
|
||||
strncpy(out, path, 511);
|
||||
out[511] = '\0';
|
||||
} else {
|
||||
strncpy(out, __cervus_cwd, 511);
|
||||
out[511] = '\0';
|
||||
size_t bl = strlen(out);
|
||||
if (bl > 0 && out[bl - 1] != '/' && bl < 510) {
|
||||
out[bl++] = '/';
|
||||
out[bl] = '\0';
|
||||
}
|
||||
strncat(out, path, 511 - strlen(out));
|
||||
}
|
||||
|
||||
char tmp[512];
|
||||
strncpy(tmp, out, 511);
|
||||
tmp[511] = '\0';
|
||||
char *parts[64];
|
||||
int np = 0;
|
||||
char *p = tmp;
|
||||
while (*p) {
|
||||
while (*p == '/') p++;
|
||||
if (!*p) break;
|
||||
char *s = p;
|
||||
while (*p && *p != '/') p++;
|
||||
if (*p) *p++ = '\0';
|
||||
if (strcmp(s, ".") == 0) continue;
|
||||
if (strcmp(s, "..") == 0) { if (np > 0) np--; continue; }
|
||||
if (np < 64) parts[np++] = s;
|
||||
}
|
||||
size_t ol = 0;
|
||||
for (int i = 0; i < np; i++) {
|
||||
out[ol++] = '/';
|
||||
size_t pl = strlen(parts[i]);
|
||||
if (ol + pl >= 511) break;
|
||||
memcpy(out + ol, parts[i], pl);
|
||||
ol += pl;
|
||||
}
|
||||
out[ol] = '\0';
|
||||
if (ol == 0) { out[0] = '/'; out[1] = '\0'; }
|
||||
return out;
|
||||
}
|
||||
|
||||
char *mkdtemp(char *tmpl)
|
||||
{
|
||||
if (!tmpl) { __cervus_errno = EINVAL; return NULL; }
|
||||
size_t len = strlen(tmpl);
|
||||
if (len < 6 || strcmp(tmpl + len - 6, "XXXXXX") != 0) {
|
||||
__cervus_errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
static uint64_t seq = 0;
|
||||
uint64_t pid = (uint64_t)getpid();
|
||||
const char *alpha = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
for (int attempt = 0; attempt < 100; attempt++) {
|
||||
uint64_t seed = (cervus_uptime_ns() ^ (pid << 32)) + (seq++);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
tmpl[len - 6 + i] = alpha[seed % 36];
|
||||
seed /= 36;
|
||||
}
|
||||
if (mkdir(tmpl, 0700) == 0) return tmpl;
|
||||
if (__cervus_errno != EEXIST) return NULL;
|
||||
}
|
||||
__cervus_errno = EEXIST;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char **__env_table = NULL;
|
||||
static int __env_count = 0;
|
||||
static int __env_cap = 0;
|
||||
|
||||
int putenv(char *str)
|
||||
{
|
||||
if (!str) return -1;
|
||||
char *eq = strchr(str, '=');
|
||||
if (!eq) return -1;
|
||||
size_t nl = (size_t)(eq - str);
|
||||
for (int i = 0; i < __env_count; i++) {
|
||||
if (strncmp(__env_table[i], str, nl) == 0 && __env_table[i][nl] == '=') {
|
||||
__env_table[i] = str;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (__env_count >= __env_cap) {
|
||||
int nc = __env_cap ? __env_cap * 2 : 16;
|
||||
char **nt = (char **)realloc(__env_table, (size_t)nc * sizeof(char *));
|
||||
if (!nt) return -1;
|
||||
__env_table = nt;
|
||||
__env_cap = nc;
|
||||
}
|
||||
__env_table[__env_count++] = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setenv(const char *name, const char *value, int overwrite)
|
||||
{
|
||||
if (!name || !value) { __cervus_errno = EINVAL; return -1; }
|
||||
size_t nl = strlen(name);
|
||||
size_t vl = strlen(value);
|
||||
for (int i = 0; i < __env_count; i++) {
|
||||
if (strncmp(__env_table[i], name, nl) == 0 && __env_table[i][nl] == '=') {
|
||||
if (!overwrite) return 0;
|
||||
char *nv = (char *)malloc(nl + vl + 2);
|
||||
if (!nv) return -1;
|
||||
memcpy(nv, name, nl);
|
||||
nv[nl] = '=';
|
||||
memcpy(nv + nl + 1, value, vl + 1);
|
||||
__env_table[i] = nv;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
char *nv = (char *)malloc(nl + vl + 2);
|
||||
if (!nv) return -1;
|
||||
memcpy(nv, name, nl);
|
||||
nv[nl] = '=';
|
||||
memcpy(nv + nl + 1, value, vl + 1);
|
||||
return putenv(nv);
|
||||
}
|
||||
|
||||
int unsetenv(const char *name)
|
||||
{
|
||||
if (!name) { __cervus_errno = EINVAL; return -1; }
|
||||
size_t nl = strlen(name);
|
||||
for (int i = 0; i < __env_count; i++) {
|
||||
if (strncmp(__env_table[i], name, nl) == 0 && __env_table[i][nl] == '=') {
|
||||
__env_table[i] = __env_table[--__env_count];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uname(struct utsname *buf)
|
||||
{
|
||||
if (!buf) { __cervus_errno = EFAULT; return -1; }
|
||||
strncpy(buf->sysname, "Cervus", _UTSNAME_LENGTH - 1);
|
||||
strncpy(buf->nodename, "cervus", _UTSNAME_LENGTH - 1);
|
||||
strncpy(buf->release, "0.0.2", _UTSNAME_LENGTH - 1);
|
||||
strncpy(buf->version, "#1", _UTSNAME_LENGTH - 1);
|
||||
strncpy(buf->machine, "x86_64", _UTSNAME_LENGTH - 1);
|
||||
buf->sysname[_UTSNAME_LENGTH - 1] = '\0';
|
||||
buf->nodename[_UTSNAME_LENGTH - 1] = '\0';
|
||||
buf->release[_UTSNAME_LENGTH - 1] = '\0';
|
||||
buf->version[_UTSNAME_LENGTH - 1] = '\0';
|
||||
buf->machine[_UTSNAME_LENGTH - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int (*cmp)(const void *, const void *))
|
||||
{
|
||||
const unsigned char *lo = (const unsigned char *)base;
|
||||
const unsigned char *hi = lo + nmemb * size;
|
||||
while (lo < hi) {
|
||||
size_t half = (size_t)((hi - lo) / (ptrdiff_t)size) / 2;
|
||||
const unsigned char *mid = lo + half * size;
|
||||
int r = cmp(key, mid);
|
||||
if (r == 0) return (void *)mid;
|
||||
if (r < 0) hi = mid;
|
||||
else lo = mid + size;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct __cervus_FILE {
|
||||
int fd;
|
||||
int eof;
|
||||
int err;
|
||||
int flags;
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
size_t buf_pos;
|
||||
};
|
||||
|
||||
FILE *fdopen(int fd, const char *mode)
|
||||
{
|
||||
(void)mode;
|
||||
FILE *f = (FILE *)malloc(sizeof(FILE));
|
||||
if (!f) { __cervus_errno = ENOMEM; return NULL; }
|
||||
f->fd = fd;
|
||||
f->eof = 0;
|
||||
f->err = 0;
|
||||
f->flags = 0;
|
||||
f->buf = NULL;
|
||||
f->buf_size = 0;
|
||||
f->buf_pos = 0;
|
||||
return f;
|
||||
}
|
||||
|
||||
FILE *popen(const char *cmd, const char *type)
|
||||
{
|
||||
if (!cmd || !type) { __cervus_errno = EINVAL; return NULL; }
|
||||
int fds[2];
|
||||
if (pipe(fds) < 0) return NULL;
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
return NULL;
|
||||
}
|
||||
if (pid == 0) {
|
||||
if (type[0] == 'r') { dup2(fds[1], 1); }
|
||||
else { dup2(fds[0], 0); }
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
char *argv[] = { "/bin/sh", "-c", (char *)cmd, NULL };
|
||||
execve("/bin/sh", argv, NULL);
|
||||
_exit(127);
|
||||
}
|
||||
if (type[0] == 'r') {
|
||||
close(fds[1]);
|
||||
return fdopen(fds[0], "r");
|
||||
} else {
|
||||
close(fds[0]);
|
||||
return fdopen(fds[1], "w");
|
||||
}
|
||||
}
|
||||
|
||||
int pclose(FILE *f)
|
||||
{
|
||||
if (!f) return -1;
|
||||
int fd = f->fd;
|
||||
free(f);
|
||||
close(fd);
|
||||
int status = 0;
|
||||
waitpid(-1, &status, 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
int ungetc(int c, FILE *f)
|
||||
{
|
||||
(void)c; (void)f;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
void rewind(FILE *f)
|
||||
{
|
||||
if (f) {
|
||||
syscall3(SYS_SEEK, f->fd, 0, 0);
|
||||
f->eof = 0;
|
||||
f->err = 0;
|
||||
}
|
||||
}
|
||||
|
||||
char *tmpnam(char *buf)
|
||||
{
|
||||
static char sbuf[32];
|
||||
static uint64_t seq = 0;
|
||||
char *out = buf ? buf : sbuf;
|
||||
uint64_t seed = cervus_uptime_ns() ^ (seq++);
|
||||
snprintf(out, 32, "/tmp/tmp%llu", (unsigned long long)seed);
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
BITS 64
|
||||
DEFAULT REL
|
||||
|
||||
section .text
|
||||
global _start
|
||||
extern main
|
||||
extern __cervus_argc
|
||||
extern __cervus_argv
|
||||
extern __cervus_filter_args
|
||||
extern __cervus_filtered_argv
|
||||
|
||||
_start:
|
||||
xor rbp, rbp
|
||||
|
||||
mov rdi, [rsp]
|
||||
lea rsi, [rsp + 8]
|
||||
|
||||
lea rax, [rel __cervus_argc]
|
||||
mov dword [rax], edi
|
||||
lea rax, [rel __cervus_argv]
|
||||
mov qword [rax], rsi
|
||||
|
||||
and rsp, -16
|
||||
|
||||
movsxd rdi, dword [rel __cervus_argc]
|
||||
mov rsi, qword [rel __cervus_argv]
|
||||
call __cervus_filter_args
|
||||
|
||||
movsxd rdi, eax
|
||||
lea rsi, [rel __cervus_filtered_argv]
|
||||
call main
|
||||
|
||||
movsxd rdi, eax
|
||||
xor rax, rax
|
||||
syscall
|
||||
|
||||
.hang:
|
||||
hlt
|
||||
jmp .hang
|
||||
|
||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <ctype.h>
|
||||
|
||||
int isdigit(int c) { return c >= '0' && c <= '9'; }
|
||||
int isalpha(int c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
|
||||
int isalnum(int c) { return isdigit(c) || isalpha(c); }
|
||||
int isupper(int c) { return c >= 'A' && c <= 'Z'; }
|
||||
int islower(int c) { return c >= 'a' && c <= 'z'; }
|
||||
int isspace(int c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; }
|
||||
int isprint(int c) { return (unsigned char)c >= 0x20 && (unsigned char)c < 0x7F; }
|
||||
int isgraph(int c) { return (unsigned char)c > 0x20 && (unsigned char)c < 0x7F; }
|
||||
int ispunct(int c) { return isgraph(c) && !isalnum(c); }
|
||||
int isxdigit(int c) { return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); }
|
||||
int iscntrl(int c) { return (unsigned char)c < 0x20 || c == 0x7F; }
|
||||
int isblank(int c) { return c == ' ' || c == '\t'; }
|
||||
int toupper(int c) { return islower(c) ? c - 'a' + 'A' : c; }
|
||||
int tolower(int c) { return isupper(c) ? c - 'A' + 'a' : c; }
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
typedef struct {
|
||||
uint64_t d_ino;
|
||||
uint8_t d_type;
|
||||
char d_name[256];
|
||||
} __kernel_dirent_t;
|
||||
|
||||
struct __cervus_DIR {
|
||||
int fd;
|
||||
struct dirent buf;
|
||||
};
|
||||
|
||||
DIR *opendir(const char *path)
|
||||
{
|
||||
int fd = open(path, O_RDONLY | O_DIRECTORY, 0);
|
||||
if (fd < 0) return NULL;
|
||||
DIR *d = (DIR *)malloc(sizeof(DIR));
|
||||
if (!d) { close(fd); return NULL; }
|
||||
d->fd = fd;
|
||||
return d;
|
||||
}
|
||||
|
||||
struct dirent *readdir(DIR *dirp)
|
||||
{
|
||||
if (!dirp) return NULL;
|
||||
__kernel_dirent_t kde;
|
||||
int r = (int)syscall2(SYS_READDIR, dirp->fd, &kde);
|
||||
if (r != 0) return NULL;
|
||||
dirp->buf.d_ino = kde.d_ino;
|
||||
dirp->buf.d_type = kde.d_type;
|
||||
size_t nl = strlen(kde.d_name);
|
||||
if (nl >= sizeof(dirp->buf.d_name)) nl = sizeof(dirp->buf.d_name) - 1;
|
||||
memcpy(dirp->buf.d_name, kde.d_name, nl);
|
||||
dirp->buf.d_name[nl] = '\0';
|
||||
return &dirp->buf;
|
||||
}
|
||||
|
||||
int closedir(DIR *dirp)
|
||||
{
|
||||
if (!dirp) return -1;
|
||||
int fd = dirp->fd;
|
||||
free(dirp);
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
void rewinddir(DIR *dirp)
|
||||
{
|
||||
if (!dirp) return;
|
||||
lseek(dirp->fd, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
int dirfd(DIR *dirp) { return dirp ? dirp->fd : -1; }
|
||||
@@ -0,0 +1,461 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/cervus.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <dirent.h>
|
||||
#include <cervus_util.h>
|
||||
|
||||
int __cervus_argc;
|
||||
char **__cervus_argv;
|
||||
|
||||
int __cervus_errno = 0;
|
||||
|
||||
static long __sys_ret(long r)
|
||||
{
|
||||
if (r < 0 && r > -4096) {
|
||||
__cervus_errno = (int)-r;
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
#define CERVUS_PATH_MAX 512
|
||||
|
||||
static char __cervus_cwd[CERVUS_PATH_MAX];
|
||||
static int __cervus_cwd_inited = 0;
|
||||
|
||||
static const char *__cervus_get_cwd(void)
|
||||
{
|
||||
if (!__cervus_cwd_inited) {
|
||||
const char *c = get_cwd_flag(__cervus_argc, __cervus_argv);
|
||||
if (!c || !*c) c = "/";
|
||||
size_t n = strlen(c);
|
||||
if (n >= sizeof(__cervus_cwd)) n = sizeof(__cervus_cwd) - 1;
|
||||
memcpy(__cervus_cwd, c, n);
|
||||
__cervus_cwd[n] = '\0';
|
||||
__cervus_cwd_inited = 1;
|
||||
}
|
||||
return __cervus_cwd;
|
||||
}
|
||||
|
||||
static const char *__cervus_resolve(const char *path, char *buf, size_t bufsz)
|
||||
{
|
||||
if (!path) return path;
|
||||
if (path[0] == '/') return path;
|
||||
resolve_path(__cervus_get_cwd(), path, buf, bufsz);
|
||||
return buf;
|
||||
}
|
||||
|
||||
ssize_t read(int fd, void *buf, size_t n)
|
||||
{
|
||||
return (ssize_t)__sys_ret(syscall3(SYS_READ, fd, buf, n));
|
||||
}
|
||||
ssize_t write(int fd, const void *buf, size_t n)
|
||||
{
|
||||
return (ssize_t)__sys_ret(syscall3(SYS_WRITE, fd, buf, n));
|
||||
}
|
||||
int close(int fd)
|
||||
{
|
||||
return (int)__sys_ret(syscall1(SYS_CLOSE, fd));
|
||||
}
|
||||
off_t lseek(int fd, off_t off, int whence)
|
||||
{
|
||||
return (off_t)__sys_ret(syscall3(SYS_SEEK, fd, (uint64_t)off, whence));
|
||||
}
|
||||
int dup(int fd)
|
||||
{
|
||||
return (int)__sys_ret(syscall1(SYS_DUP, fd));
|
||||
}
|
||||
int dup2(int oldfd, int newfd)
|
||||
{
|
||||
return (int)__sys_ret(syscall2(SYS_DUP2, oldfd, newfd));
|
||||
}
|
||||
int pipe(int fds[2])
|
||||
{
|
||||
return (int)__sys_ret(syscall1(SYS_PIPE, fds));
|
||||
}
|
||||
|
||||
int ioctl(int fd, unsigned long request, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, request);
|
||||
void *arg = va_arg(ap, void *);
|
||||
va_end(ap);
|
||||
return (int)__sys_ret(syscall3(SYS_IOCTL, fd, request, arg));
|
||||
}
|
||||
|
||||
int isatty(int fd)
|
||||
{
|
||||
struct winsize ws;
|
||||
long r = syscall3(SYS_IOCTL, fd, TIOCGWINSZ, &ws);
|
||||
if (r < 0) {
|
||||
__cervus_errno = (int)-r;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unlink(const char *path)
|
||||
{
|
||||
char abs[CERVUS_PATH_MAX];
|
||||
path = __cervus_resolve(path, abs, sizeof(abs));
|
||||
return (int)__sys_ret(syscall1(SYS_UNLINK, path));
|
||||
}
|
||||
int rmdir(const char *path)
|
||||
{
|
||||
char abs[CERVUS_PATH_MAX];
|
||||
path = __cervus_resolve(path, abs, sizeof(abs));
|
||||
return (int)__sys_ret(syscall1(SYS_RMDIR, path));
|
||||
}
|
||||
|
||||
pid_t getpid(void) { return (pid_t)syscall0(SYS_GETPID); }
|
||||
pid_t getppid(void) { return (pid_t)syscall0(SYS_GETPPID); }
|
||||
uid_t getuid(void) { return (uid_t)syscall0(SYS_GETUID); }
|
||||
gid_t getgid(void) { return (gid_t)syscall0(SYS_GETGID); }
|
||||
int setuid(uid_t u) { return (int)__sys_ret(syscall1(SYS_SETUID, u)); }
|
||||
int setgid(gid_t g) { return (int)__sys_ret(syscall1(SYS_SETGID, g)); }
|
||||
pid_t fork(void) { return (pid_t)__sys_ret(syscall0(SYS_FORK)); }
|
||||
|
||||
int execve(const char *path, char *const argv[], char *const envp[])
|
||||
{
|
||||
char abs[CERVUS_PATH_MAX];
|
||||
path = __cervus_resolve(path, abs, sizeof(abs));
|
||||
return (int)__sys_ret(syscall3(SYS_EXECVE, path, argv, envp));
|
||||
}
|
||||
|
||||
int execv(const char *path, char *const argv[])
|
||||
{
|
||||
char *empty[] = { NULL };
|
||||
return execve(path, argv, empty);
|
||||
}
|
||||
|
||||
int execvp(const char *file, char *const argv[])
|
||||
{
|
||||
if (!file || !*file) { __cervus_errno = ENOENT; return -1; }
|
||||
|
||||
int has_slash = 0;
|
||||
for (const char *p = file; *p; p++) if (*p == '/') { has_slash = 1; break; }
|
||||
if (has_slash) return execve(file, argv, NULL);
|
||||
|
||||
const char *path = getenv("PATH");
|
||||
if (!path || !*path) path = "/bin:/apps";
|
||||
|
||||
char buf[512];
|
||||
const char *p = path;
|
||||
while (*p) {
|
||||
const char *colon = p;
|
||||
while (*colon && *colon != ':') colon++;
|
||||
size_t dlen = (size_t)(colon - p);
|
||||
size_t flen = strlen(file);
|
||||
if (dlen + 1 + flen + 1 <= sizeof(buf)) {
|
||||
memcpy(buf, p, dlen);
|
||||
buf[dlen] = '/';
|
||||
memcpy(buf + dlen + 1, file, flen);
|
||||
buf[dlen + 1 + flen] = '\0';
|
||||
execve(buf, argv, NULL);
|
||||
}
|
||||
p = colon;
|
||||
if (*p == ':') p++;
|
||||
}
|
||||
__cervus_errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void _exit(int status) { syscall1(SYS_EXIT, status); __builtin_unreachable(); }
|
||||
|
||||
unsigned int sleep(unsigned int sec)
|
||||
{
|
||||
cervus_nanosleep((uint64_t)sec * 1000000000ULL);
|
||||
return 0;
|
||||
}
|
||||
int usleep(unsigned int usec)
|
||||
{
|
||||
return cervus_nanosleep((uint64_t)usec * 1000ULL);
|
||||
}
|
||||
|
||||
void sched_yield_cervus(void) { syscall0(SYS_YIELD); }
|
||||
|
||||
char *getcwd(char *buf, size_t size)
|
||||
{
|
||||
if (!buf || size < 2) { __cervus_errno = EINVAL; return NULL; }
|
||||
const char *c = __cervus_get_cwd();
|
||||
size_t n = strlen(c);
|
||||
if (n + 1 > size) { __cervus_errno = ERANGE; return NULL; }
|
||||
memcpy(buf, c, n + 1);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int chdir(const char *path)
|
||||
{
|
||||
if (!path || !*path) { __cervus_errno = ENOENT; return -1; }
|
||||
|
||||
char abs[CERVUS_PATH_MAX];
|
||||
const char *p = __cervus_resolve(path, abs, sizeof(abs));
|
||||
|
||||
struct stat st;
|
||||
if ((int)__sys_ret(syscall2(SYS_STAT, p, &st)) < 0) return -1;
|
||||
if (!S_ISDIR(st.st_mode)) { __cervus_errno = ENOTDIR; return -1; }
|
||||
|
||||
size_t n = strlen(p);
|
||||
if (n >= sizeof(__cervus_cwd)) { __cervus_errno = ENAMETOOLONG; return -1; }
|
||||
memcpy(__cervus_cwd, p, n + 1);
|
||||
__cervus_cwd_inited = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *sbrk(intptr_t incr)
|
||||
{
|
||||
uintptr_t cur = (uintptr_t)syscall1(SYS_BRK, 0);
|
||||
if (incr == 0) return (void *)cur;
|
||||
uintptr_t nw = (uintptr_t)syscall1(SYS_BRK, cur + (uintptr_t)incr);
|
||||
if (nw != cur + (uintptr_t)incr) {
|
||||
__cervus_errno = ENOMEM;
|
||||
return (void *)-1;
|
||||
}
|
||||
return (void *)cur;
|
||||
}
|
||||
int brk(void *addr)
|
||||
{
|
||||
uintptr_t r = (uintptr_t)syscall1(SYS_BRK, (uintptr_t)addr);
|
||||
if (r != (uintptr_t)addr) { __cervus_errno = ENOMEM; return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open(const char *path, int flags, ...)
|
||||
{
|
||||
mode_t mode = 0;
|
||||
if (flags & O_CREAT) {
|
||||
va_list ap;
|
||||
va_start(ap, flags);
|
||||
mode = va_arg(ap, mode_t);
|
||||
va_end(ap);
|
||||
}
|
||||
char abs[CERVUS_PATH_MAX];
|
||||
path = __cervus_resolve(path, abs, sizeof(abs));
|
||||
return (int)__sys_ret(syscall3(SYS_OPEN, path, flags, mode));
|
||||
}
|
||||
int creat(const char *path, mode_t mode)
|
||||
{
|
||||
return open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
||||
}
|
||||
int fcntl(int fd, int cmd, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, cmd);
|
||||
long arg = va_arg(ap, long);
|
||||
va_end(ap);
|
||||
return (int)__sys_ret(syscall3(SYS_FCNTL, fd, cmd, arg));
|
||||
}
|
||||
|
||||
int stat(const char *path, struct stat *out)
|
||||
{
|
||||
char abs[CERVUS_PATH_MAX];
|
||||
path = __cervus_resolve(path, abs, sizeof(abs));
|
||||
return (int)__sys_ret(syscall2(SYS_STAT, path, out));
|
||||
}
|
||||
int fstat(int fd, struct stat *out)
|
||||
{
|
||||
return (int)__sys_ret(syscall2(SYS_FSTAT, fd, out));
|
||||
}
|
||||
int mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
char abs[CERVUS_PATH_MAX];
|
||||
path = __cervus_resolve(path, abs, sizeof(abs));
|
||||
return (int)__sys_ret(syscall2(SYS_MKDIR, path, mode));
|
||||
}
|
||||
int chmod(const char *path, mode_t mode)
|
||||
{
|
||||
(void)path; (void)mode;
|
||||
return 0;
|
||||
}
|
||||
int rename(const char *oldp, const char *newp)
|
||||
{
|
||||
char absa[CERVUS_PATH_MAX], absb[CERVUS_PATH_MAX];
|
||||
oldp = __cervus_resolve(oldp, absa, sizeof(absa));
|
||||
newp = __cervus_resolve(newp, absb, sizeof(absb));
|
||||
return (int)__sys_ret(syscall2(SYS_RENAME, oldp, newp));
|
||||
}
|
||||
|
||||
void *mmap(void *a, size_t l, int p, int f, int fd, off_t o)
|
||||
{
|
||||
long r = syscall6(SYS_MMAP, a, l, p, f, fd, (uint64_t)o);
|
||||
if (r < 0 && r > -4096) { __cervus_errno = (int)-r; return MAP_FAILED; }
|
||||
return (void *)r;
|
||||
}
|
||||
int munmap(void *a, size_t l) { return (int)__sys_ret(syscall2(SYS_MUNMAP, a, l)); }
|
||||
|
||||
pid_t waitpid(pid_t p, int *s, int f) { return (pid_t)__sys_ret(syscall3(SYS_WAIT, p, s, f)); }
|
||||
pid_t wait(int *s) { return waitpid(-1, s, 0); }
|
||||
|
||||
ssize_t cervus_dbg_print(const char *b, size_t n) { return (ssize_t)syscall2(SYS_DBG_PRINT, b, n); }
|
||||
|
||||
char *optarg = NULL;
|
||||
int optind = 1;
|
||||
int optopt = 0;
|
||||
int opterr = 1;
|
||||
|
||||
static int __opt_subidx = 1;
|
||||
|
||||
int getopt(int argc, char *const argv[], const char *optstring)
|
||||
{
|
||||
if (!optstring) optstring = "";
|
||||
int colon_mode = (optstring[0] == ':');
|
||||
const char *opts = colon_mode ? optstring + 1 : optstring;
|
||||
|
||||
optarg = NULL;
|
||||
|
||||
if (optind >= argc) return -1;
|
||||
|
||||
char *cur = argv[optind];
|
||||
if (!cur || cur[0] != '-' || cur[1] == '\0') return -1;
|
||||
if (cur[0] == '-' && cur[1] == '-' && cur[2] == '\0') {
|
||||
optind++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char ch = cur[__opt_subidx];
|
||||
if (ch == '\0') {
|
||||
optind++;
|
||||
__opt_subidx = 1;
|
||||
return getopt(argc, argv, optstring);
|
||||
}
|
||||
|
||||
const char *pp = opts;
|
||||
while (*pp && *pp != ch) pp++;
|
||||
if (*pp == '\0' || ch == ':') {
|
||||
optopt = ch;
|
||||
if (opterr && !colon_mode) {
|
||||
const char *prog = argv[0] ? argv[0] : "?";
|
||||
fprintf(stderr, "%s: invalid option -- '%c'\n", prog, ch);
|
||||
}
|
||||
__opt_subidx++;
|
||||
if (cur[__opt_subidx] == '\0') {
|
||||
optind++;
|
||||
__opt_subidx = 1;
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
if (pp[1] == ':') {
|
||||
if (cur[__opt_subidx + 1] != '\0') {
|
||||
optarg = &cur[__opt_subidx + 1];
|
||||
optind++;
|
||||
__opt_subidx = 1;
|
||||
return ch;
|
||||
}
|
||||
if (optind + 1 >= argc) {
|
||||
optopt = ch;
|
||||
optind++;
|
||||
__opt_subidx = 1;
|
||||
if (opterr && !colon_mode) {
|
||||
const char *prog = argv[0] ? argv[0] : "?";
|
||||
fprintf(stderr, "%s: option requires an argument -- '%c'\n", prog, ch);
|
||||
}
|
||||
return colon_mode ? ':' : '?';
|
||||
}
|
||||
optarg = argv[optind + 1];
|
||||
optind += 2;
|
||||
__opt_subidx = 1;
|
||||
return ch;
|
||||
}
|
||||
|
||||
__opt_subidx++;
|
||||
if (cur[__opt_subidx] == '\0') {
|
||||
optind++;
|
||||
__opt_subidx = 1;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
int tcgetattr(int fd, struct termios *t)
|
||||
{
|
||||
if (!t) { __cervus_errno = EINVAL; return -1; }
|
||||
return (int)__sys_ret(syscall3(SYS_IOCTL, fd, TCGETS, t));
|
||||
}
|
||||
|
||||
int tcsetattr(int fd, int optional_actions, const struct termios *t)
|
||||
{
|
||||
if (!t) { __cervus_errno = EINVAL; return -1; }
|
||||
unsigned long req;
|
||||
switch (optional_actions) {
|
||||
case TCSADRAIN: req = TCSETSW; break;
|
||||
case TCSAFLUSH: req = TCSETSF; break;
|
||||
case TCSANOW:
|
||||
default: req = TCSETS; break;
|
||||
}
|
||||
return (int)__sys_ret(syscall3(SYS_IOCTL, fd, req, t));
|
||||
}
|
||||
|
||||
int cervus_task_info(pid_t p, cervus_task_info_t *o) { return (int)__sys_ret(syscall2(SYS_TASK_INFO, p, o)); }
|
||||
int cervus_task_kill(pid_t p) { return (int)__sys_ret(syscall1(SYS_TASK_KILL, p)); }
|
||||
uint64_t cervus_cap_get(void) { return (uint64_t)syscall0(SYS_CAP_GET); }
|
||||
int cervus_cap_drop(uint64_t m) { return (int)__sys_ret(syscall1(SYS_CAP_DROP, m)); }
|
||||
|
||||
int cervus_meminfo(cervus_meminfo_t *m) { return (int)__sys_ret(syscall1(SYS_MEMINFO, m)); }
|
||||
uint64_t cervus_uptime_ns(void) { return (uint64_t)syscall0(SYS_UPTIME); }
|
||||
int cervus_clock_gettime(int id, cervus_timespec_t *t){ return (int)__sys_ret(syscall2(SYS_CLOCK_GET, id, t)); }
|
||||
int cervus_nanosleep(uint64_t ns) { return (int)__sys_ret(syscall1(SYS_SLEEP_NS, ns)); }
|
||||
|
||||
int cervus_shutdown(void) { return (int)__sys_ret(syscall0(SYS_SHUTDOWN)); }
|
||||
int cervus_reboot(void) { return (int)__sys_ret(syscall0(SYS_REBOOT)); }
|
||||
|
||||
int cervus_disk_info(int i, cervus_disk_info_t *o) { return (int)__sys_ret(syscall2(SYS_DISK_INFO, (uint64_t)i, o)); }
|
||||
int cervus_disk_mount(const char *d, const char *p) { return (int)__sys_ret(syscall2(SYS_DISK_MOUNT, d, p)); }
|
||||
int cervus_disk_umount(const char *p) { return (int)__sys_ret(syscall1(SYS_DISK_UMOUNT, p)); }
|
||||
int cervus_disk_format(const char *d, const char *l) { return (int)__sys_ret(syscall2(SYS_DISK_FORMAT, d, l)); }
|
||||
int cervus_disk_mkfs_fat32(const char *d, const char *l) { return (int)__sys_ret(syscall2(SYS_DISK_MKFS_FAT32, d, l)); }
|
||||
int cervus_disk_partition(const char *d, const cervus_mbr_part_t *s, uint64_t n) { return (int)__sys_ret(syscall3(SYS_DISK_PARTITION, d, s, n)); }
|
||||
int cervus_disk_read_raw(const char *d, uint64_t lba, uint64_t c, void *b) { return (int)__sys_ret(syscall4(SYS_DISK_READ_RAW, d, lba, c, b)); }
|
||||
int cervus_disk_write_raw(const char *d, uint64_t lba, uint64_t c, const void *b){ return (int)__sys_ret(syscall4(SYS_DISK_WRITE_RAW, d, lba, c, b)); }
|
||||
long cervus_disk_list_parts(cervus_part_info_t *o, int m) { return __sys_ret(syscall2(SYS_DISK_LIST_PARTS, o, m)); }
|
||||
long cervus_disk_bios_install(const char *d, const void *sd, uint32_t ss) { return __sys_ret(syscall3(SYS_DISK_BIOS_INSTALL, d, sd, ss)); }
|
||||
|
||||
long cervus_list_mounts(cervus_mount_info_t *o, int m) { return __sys_ret(syscall2(SYS_LIST_MOUNTS, o, m)); }
|
||||
long cervus_statvfs(const char *p, cervus_statvfs_t *o) { return __sys_ret(syscall2(SYS_STATVFS, p, o)); }
|
||||
|
||||
uint32_t cervus_ioport_read(uint16_t p, int w) { return (uint32_t)syscall2(SYS_IOPORT_READ, p, w); }
|
||||
int cervus_ioport_write(uint16_t p, int w, uint32_t v) { return (int)__sys_ret(syscall3(SYS_IOPORT_WRITE, p, w, v)); }
|
||||
|
||||
void __cervus_assert_fail(const char *expr, const char *file, int line, const char *func)
|
||||
{
|
||||
printf("assertion failed: %s (%s:%d, %s)\n",
|
||||
expr ? expr : "(null)",
|
||||
file ? file : "(null)",
|
||||
line,
|
||||
func ? func : "(null)");
|
||||
syscall1(SYS_EXIT, 134);
|
||||
for (;;) { }
|
||||
}
|
||||
|
||||
#define _CERVUS_FILT_MAX 128
|
||||
char *__cervus_filtered_argv[_CERVUS_FILT_MAX + 1];
|
||||
|
||||
int __cervus_filter_args(int argc, char **argv)
|
||||
{
|
||||
int out = 0;
|
||||
for (int i = 0; i < argc && out < _CERVUS_FILT_MAX; i++) {
|
||||
const char *a = argv[i];
|
||||
if (i > 0 && a && a[0] == '-' && a[1] == '-' &&
|
||||
((a[2]=='c' && a[3]=='w' && a[4]=='d' && a[5]=='=') ||
|
||||
(a[2]=='e' && a[3]=='n' && a[4]=='v' && a[5]==':'))) {
|
||||
continue;
|
||||
}
|
||||
__cervus_filtered_argv[out++] = (char *)a;
|
||||
}
|
||||
__cervus_filtered_argv[out] = NULL;
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <math.h>
|
||||
|
||||
int abs(int n) {
|
||||
return n >= 0 ? n : -n;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
double fabs(double x) {
|
||||
union {
|
||||
double d;
|
||||
uint64_t i;
|
||||
} u = { .d = x };
|
||||
|
||||
u.i &= 0x7FFFFFFFFFFFFFFFULL;
|
||||
return u.d;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int isinf(double x) {
|
||||
union {
|
||||
double f;
|
||||
uint64_t i;
|
||||
} u = { .f = x };
|
||||
|
||||
uint64_t exp = (u.i >> 52) & 0x7FF;
|
||||
uint64_t mantissa = u.i & 0xFFFFFFFFFFFFFULL;
|
||||
|
||||
return (exp == 0x7FF) && (mantissa == 0);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int isnan(double x) {
|
||||
union {
|
||||
double f;
|
||||
uint64_t i;
|
||||
} u = { .f = x };
|
||||
|
||||
uint64_t exp = (u.i >> 52) & 0x7FF;
|
||||
uint64_t mantissa = u.i & 0xFFFFFFFFFFFFFULL;
|
||||
|
||||
return (exp == 0x7FF) && (mantissa != 0);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include <math.h>
|
||||
|
||||
double pow(double x, double y) {
|
||||
if (y == 0.0) return 1.0;
|
||||
|
||||
if (y == (int)y) {
|
||||
int n = (int)y;
|
||||
double result = 1.0;
|
||||
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < -n; i++) {
|
||||
result /= x;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (x > 0.0) {
|
||||
int int_part = (int)y;
|
||||
double frac_part = y - int_part;
|
||||
|
||||
double int_pow = pow(x, int_part);
|
||||
|
||||
if (frac_part > 0.0) {
|
||||
return int_pow * (1.0 + frac_part * (x - 1.0));
|
||||
} else {
|
||||
return int_pow / (1.0 - frac_part * (x - 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
return NAN;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include <math.h>
|
||||
|
||||
double pow10(int n) {
|
||||
double result = 1.0;
|
||||
|
||||
if (n >= 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
result *= 10.0;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < -n; i++) {
|
||||
result /= 10.0;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern int __cervus_errno;
|
||||
|
||||
typedef struct __mblock {
|
||||
size_t size;
|
||||
size_t prev_size;
|
||||
} __mblock_t;
|
||||
|
||||
#define MB_HDR_SZ (sizeof(__mblock_t))
|
||||
#define MB_ALIGN 16
|
||||
#define MB_MIN_TOTAL 32
|
||||
#define MB_FREE_BIT ((size_t)1)
|
||||
#define MB_SIZE(b) ((b)->size & ~MB_FREE_BIT)
|
||||
#define MB_IS_FREE(b) (((b)->size & MB_FREE_BIT) != 0)
|
||||
#define MB_USER(b) ((void *)((char *)(b) + MB_HDR_SZ))
|
||||
#define MB_FROM_USER(p) ((__mblock_t *)((char *)(p) - MB_HDR_SZ))
|
||||
|
||||
static __mblock_t *__heap_start = NULL;
|
||||
static __mblock_t *__heap_end = NULL;
|
||||
|
||||
static inline size_t __align_up(size_t n, size_t a) {
|
||||
return (n + a - 1) & ~(a - 1);
|
||||
}
|
||||
|
||||
static inline __mblock_t *__mb_next(__mblock_t *b) {
|
||||
return (__mblock_t *)((char *)b + MB_SIZE(b));
|
||||
}
|
||||
|
||||
static inline __mblock_t *__mb_prev(__mblock_t *b) {
|
||||
if (b->prev_size == 0) return NULL;
|
||||
return (__mblock_t *)((char *)b - b->prev_size);
|
||||
}
|
||||
|
||||
static __mblock_t *__heap_grow(size_t need)
|
||||
{
|
||||
size_t chunk = __align_up(need + MB_HDR_SZ, 65536);
|
||||
|
||||
if (!__heap_start) {
|
||||
void *base = sbrk((intptr_t)chunk);
|
||||
if (base == (void *)-1) return NULL;
|
||||
|
||||
uintptr_t addr = (uintptr_t)base;
|
||||
uintptr_t aligned = (addr + MB_ALIGN - 1) & ~(uintptr_t)(MB_ALIGN - 1);
|
||||
size_t lost = aligned - addr;
|
||||
if (lost >= chunk - MB_MIN_TOTAL - MB_HDR_SZ) {
|
||||
__cervus_errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__heap_start = (__mblock_t *)aligned;
|
||||
size_t usable = chunk - lost;
|
||||
|
||||
__mblock_t *first = __heap_start;
|
||||
size_t first_sz = usable - MB_HDR_SZ;
|
||||
first->size = first_sz | MB_FREE_BIT;
|
||||
first->prev_size = 0;
|
||||
|
||||
__heap_end = (__mblock_t *)((char *)first + first_sz);
|
||||
__heap_end->size = 0;
|
||||
__heap_end->prev_size = first_sz;
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
void *p = sbrk((intptr_t)chunk);
|
||||
if (p == (void *)-1) return NULL;
|
||||
if ((uintptr_t)p != (uintptr_t)__heap_end + MB_HDR_SZ) {
|
||||
__cervus_errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__mblock_t *new_block = __heap_end;
|
||||
new_block->size = chunk | MB_FREE_BIT;
|
||||
|
||||
__mblock_t *new_end = (__mblock_t *)((char *)new_block + MB_SIZE(new_block));
|
||||
new_end->size = 0;
|
||||
new_end->prev_size = MB_SIZE(new_block);
|
||||
__heap_end = new_end;
|
||||
|
||||
__mblock_t *prev = __mb_prev(new_block);
|
||||
if (prev && MB_IS_FREE(prev)) {
|
||||
size_t merged_sz = MB_SIZE(prev) + MB_SIZE(new_block);
|
||||
prev->size = merged_sz | MB_FREE_BIT;
|
||||
__heap_end->prev_size = merged_sz;
|
||||
return prev;
|
||||
}
|
||||
return new_block;
|
||||
}
|
||||
|
||||
static void __mb_split(__mblock_t *b, size_t need)
|
||||
{
|
||||
size_t cur = MB_SIZE(b);
|
||||
if (cur < need + MB_MIN_TOTAL) {
|
||||
b->size = cur;
|
||||
return;
|
||||
}
|
||||
b->size = need;
|
||||
|
||||
__mblock_t *rest = (__mblock_t *)((char *)b + need);
|
||||
rest->size = (cur - need) | MB_FREE_BIT;
|
||||
rest->prev_size = need;
|
||||
|
||||
__mblock_t *after = __mb_next(rest);
|
||||
if (after) after->prev_size = MB_SIZE(rest);
|
||||
}
|
||||
|
||||
void *malloc(size_t n)
|
||||
{
|
||||
if (n == 0) n = 1;
|
||||
size_t need = __align_up(n + MB_HDR_SZ, MB_ALIGN);
|
||||
if (need < MB_MIN_TOTAL) need = MB_MIN_TOTAL;
|
||||
|
||||
for (__mblock_t *b = __heap_start; b && b != __heap_end; b = __mb_next(b)) {
|
||||
if (MB_IS_FREE(b) && MB_SIZE(b) >= need) {
|
||||
__mb_split(b, need);
|
||||
return MB_USER(b);
|
||||
}
|
||||
}
|
||||
|
||||
__mblock_t *grown = __heap_grow(need);
|
||||
if (!grown) return NULL;
|
||||
if (MB_SIZE(grown) < need) {
|
||||
__cervus_errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
__mb_split(grown, need);
|
||||
return MB_USER(grown);
|
||||
}
|
||||
|
||||
void *calloc(size_t nm, size_t sz)
|
||||
{
|
||||
size_t t = nm * sz;
|
||||
if (nm && t / nm != sz) { __cervus_errno = ENOMEM; return NULL; }
|
||||
void *p = malloc(t);
|
||||
if (p) memset(p, 0, t);
|
||||
return p;
|
||||
}
|
||||
|
||||
void free(void *p)
|
||||
{
|
||||
if (!p) return;
|
||||
__mblock_t *b = MB_FROM_USER(p);
|
||||
b->size = MB_SIZE(b) | MB_FREE_BIT;
|
||||
|
||||
__mblock_t *next = __mb_next(b);
|
||||
if (next != __heap_end && MB_IS_FREE(next)) {
|
||||
size_t merged = MB_SIZE(b) + MB_SIZE(next);
|
||||
b->size = merged | MB_FREE_BIT;
|
||||
__mblock_t *after = __mb_next(b);
|
||||
if (after) after->prev_size = merged;
|
||||
}
|
||||
__mblock_t *prev = __mb_prev(b);
|
||||
if (prev && MB_IS_FREE(prev)) {
|
||||
size_t merged = MB_SIZE(prev) + MB_SIZE(b);
|
||||
prev->size = merged | MB_FREE_BIT;
|
||||
__mblock_t *after = __mb_next(prev);
|
||||
if (after) after->prev_size = merged;
|
||||
}
|
||||
}
|
||||
|
||||
void *realloc(void *p, size_t n)
|
||||
{
|
||||
if (!p) return malloc(n);
|
||||
if (n == 0) { free(p); return NULL; }
|
||||
|
||||
__mblock_t *b = MB_FROM_USER(p);
|
||||
size_t cur_total = MB_SIZE(b);
|
||||
size_t cur_user = cur_total - MB_HDR_SZ;
|
||||
size_t need = __align_up(n + MB_HDR_SZ, MB_ALIGN);
|
||||
if (need < MB_MIN_TOTAL) need = MB_MIN_TOTAL;
|
||||
|
||||
if (need <= cur_total) {
|
||||
if (cur_total >= need + MB_MIN_TOTAL) {
|
||||
b->size = need;
|
||||
__mblock_t *rest = (__mblock_t *)((char *)b + need);
|
||||
rest->size = (cur_total - need) | MB_FREE_BIT;
|
||||
rest->prev_size = need;
|
||||
__mblock_t *after = __mb_next(rest);
|
||||
if (after) after->prev_size = MB_SIZE(rest);
|
||||
if (after != __heap_end && MB_IS_FREE(after)) {
|
||||
size_t merged = MB_SIZE(rest) + MB_SIZE(after);
|
||||
rest->size = merged | MB_FREE_BIT;
|
||||
__mblock_t *aft2 = __mb_next(rest);
|
||||
if (aft2) aft2->prev_size = merged;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
__mblock_t *next = __mb_next(b);
|
||||
if (next != __heap_end && MB_IS_FREE(next) &&
|
||||
cur_total + MB_SIZE(next) >= need)
|
||||
{
|
||||
size_t combined = cur_total + MB_SIZE(next);
|
||||
b->size = combined;
|
||||
__mblock_t *after = __mb_next(b);
|
||||
if (after) after->prev_size = combined;
|
||||
if (combined >= need + MB_MIN_TOTAL) {
|
||||
b->size = need;
|
||||
__mblock_t *rest = (__mblock_t *)((char *)b + need);
|
||||
rest->size = (combined - need) | MB_FREE_BIT;
|
||||
rest->prev_size = need;
|
||||
__mblock_t *aft = __mb_next(rest);
|
||||
if (aft) aft->prev_size = MB_SIZE(rest);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void *np = malloc(n);
|
||||
if (!np) return NULL;
|
||||
memcpy(np, p, cur_user);
|
||||
free(p);
|
||||
return np;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
BITS 64
|
||||
DEFAULT REL
|
||||
|
||||
section .text
|
||||
|
||||
global setjmp
|
||||
global longjmp
|
||||
|
||||
setjmp:
|
||||
mov [rdi + 0x00], rbx
|
||||
mov [rdi + 0x08], rbp
|
||||
mov [rdi + 0x10], r12
|
||||
mov [rdi + 0x18], r13
|
||||
mov [rdi + 0x20], r14
|
||||
mov [rdi + 0x28], r15
|
||||
lea rax, [rsp + 8]
|
||||
mov [rdi + 0x30], rax
|
||||
mov rax, [rsp]
|
||||
mov [rdi + 0x38], rax
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
longjmp:
|
||||
mov rbx, [rdi + 0x00]
|
||||
mov rbp, [rdi + 0x08]
|
||||
mov r12, [rdi + 0x10]
|
||||
mov r13, [rdi + 0x18]
|
||||
mov r14, [rdi + 0x20]
|
||||
mov r15, [rdi + 0x28]
|
||||
mov rsp, [rdi + 0x30]
|
||||
mov eax, esi
|
||||
test eax, eax
|
||||
jnz .done
|
||||
mov eax, 1
|
||||
.done:
|
||||
jmp qword [rdi + 0x38]
|
||||
|
||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
sighandler_t signal(int signum, sighandler_t handler)
|
||||
{
|
||||
(void)signum;
|
||||
(void)handler;
|
||||
return SIG_DFL;
|
||||
}
|
||||
|
||||
int raise(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact)
|
||||
{
|
||||
(void)sig; (void)act;
|
||||
if (oldact) {
|
||||
oldact->sa_handler = SIG_DFL;
|
||||
memset(&oldact->sa_mask, 0, sizeof(oldact->sa_mask));
|
||||
oldact->sa_flags = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigemptyset(sigset_t *set) { if (set) memset(set, 0, sizeof(*set)); return 0; }
|
||||
int sigfillset(sigset_t *set) { if (set) memset(set, 0xFF, sizeof(*set)); return 0; }
|
||||
int sigaddset(sigset_t *set, int sig) { (void)set; (void)sig; return 0; }
|
||||
int sigdelset(sigset_t *set, int sig) { (void)set; (void)sig; return 0; }
|
||||
int sigismember(const sigset_t *set, int sig) { (void)set; (void)sig; return 0; }
|
||||
|
||||
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
|
||||
{
|
||||
(void)how; (void)set;
|
||||
if (oldset) memset(oldset, 0, sizeof(*oldset));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern uint64_t __cervus_strtod_bits(const char *s, char **endptr);
|
||||
|
||||
static int __is_space(int c)
|
||||
{
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
|
||||
}
|
||||
|
||||
int vsscanf(const char *str, const char *fmt, va_list ap)
|
||||
{
|
||||
if (!str || !fmt) return -1;
|
||||
const char *s = str;
|
||||
const char *f = fmt;
|
||||
int matched = 0;
|
||||
int saw_anything = 0;
|
||||
|
||||
while (*f) {
|
||||
if (__is_space((unsigned char)*f)) {
|
||||
while (__is_space((unsigned char)*s)) s++;
|
||||
f++;
|
||||
continue;
|
||||
}
|
||||
if (*f != '%') {
|
||||
if (*s != *f) return saw_anything ? matched : (matched ? matched : -1);
|
||||
s++; f++;
|
||||
continue;
|
||||
}
|
||||
f++;
|
||||
|
||||
int suppress = 0;
|
||||
if (*f == '*') { suppress = 1; f++; }
|
||||
|
||||
int width = 0;
|
||||
int has_width = 0;
|
||||
while (*f >= '0' && *f <= '9') {
|
||||
width = width * 10 + (*f - '0');
|
||||
has_width = 1;
|
||||
f++;
|
||||
}
|
||||
if (!has_width) width = 0;
|
||||
|
||||
int len_mod = 0;
|
||||
if (*f == 'h') { f++; if (*f == 'h') { f++; len_mod = 2; } else len_mod = 1; }
|
||||
else if (*f == 'l') { f++; if (*f == 'l') { f++; len_mod = 4; } else len_mod = 3; }
|
||||
else if (*f == 'z') { f++; len_mod = 5; }
|
||||
else if (*f == 'j' || *f == 't') { f++; len_mod = 4; }
|
||||
|
||||
char conv = *f;
|
||||
if (!conv) break;
|
||||
f++;
|
||||
|
||||
if (conv == '%') {
|
||||
if (*s != '%') return matched ? matched : -1;
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conv == 'n') {
|
||||
if (!suppress) {
|
||||
int read = (int)(s - str);
|
||||
if (len_mod == 3 || len_mod == 5) *va_arg(ap, long *) = read;
|
||||
else if (len_mod == 4) *va_arg(ap, long long *) = read;
|
||||
else *va_arg(ap, int *) = read;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conv == 'c') {
|
||||
int w = has_width ? width : 1;
|
||||
char *out = suppress ? NULL : va_arg(ap, char *);
|
||||
for (int i = 0; i < w; i++) {
|
||||
if (!*s) return matched ? matched : -1;
|
||||
if (out) out[i] = *s;
|
||||
s++;
|
||||
}
|
||||
if (!suppress) matched++;
|
||||
saw_anything = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conv != '[') {
|
||||
while (__is_space((unsigned char)*s)) s++;
|
||||
}
|
||||
|
||||
if (conv == 's') {
|
||||
int w = has_width ? width : 0x7FFFFFFF;
|
||||
char *out = suppress ? NULL : va_arg(ap, char *);
|
||||
int wrote = 0;
|
||||
if (!*s) return matched ? matched : -1;
|
||||
while (*s && !__is_space((unsigned char)*s) && wrote < w) {
|
||||
if (out) out[wrote] = *s;
|
||||
s++; wrote++;
|
||||
}
|
||||
if (out) out[wrote] = '\0';
|
||||
if (!suppress) matched++;
|
||||
saw_anything = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conv == '[') {
|
||||
int negate = 0;
|
||||
if (*f == '^') { negate = 1; f++; }
|
||||
char set[256];
|
||||
memset(set, 0, sizeof(set));
|
||||
if (*f == ']') { set[(unsigned char)']'] = 1; f++; }
|
||||
while (*f && *f != ']') { set[(unsigned char)*f] = 1; f++; }
|
||||
if (*f == ']') f++;
|
||||
int w = has_width ? width : 0x7FFFFFFF;
|
||||
char *out = suppress ? NULL : va_arg(ap, char *);
|
||||
int wrote = 0;
|
||||
while (*s && wrote < w) {
|
||||
int in = set[(unsigned char)*s] != 0;
|
||||
if (negate) in = !in;
|
||||
if (!in) break;
|
||||
if (out) out[wrote] = *s;
|
||||
s++; wrote++;
|
||||
}
|
||||
if (out) out[wrote] = '\0';
|
||||
if (wrote == 0) return matched ? matched : -1;
|
||||
if (!suppress) matched++;
|
||||
saw_anything = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conv == 'd' || conv == 'i' || conv == 'u' ||
|
||||
conv == 'o' || conv == 'x' || conv == 'X')
|
||||
{
|
||||
int base = 10;
|
||||
int allow_neg = (conv == 'd' || conv == 'i');
|
||||
if (conv == 'o') base = 8;
|
||||
else if (conv == 'x' || conv == 'X') base = 16;
|
||||
|
||||
const char *start = s;
|
||||
int neg = 0;
|
||||
if (allow_neg && (*s == '+' || *s == '-')) { neg = (*s == '-'); s++; }
|
||||
else if (conv == 'u' && (*s == '+')) s++;
|
||||
if (conv == 'i' || conv == 'x' || conv == 'X') {
|
||||
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
||||
base = 16; s += 2;
|
||||
} else if (conv == 'i' && s[0] == '0' && s[1] >= '0' && s[1] <= '7') {
|
||||
base = 8; s++;
|
||||
}
|
||||
}
|
||||
unsigned long long val = 0;
|
||||
int got_digit = 0;
|
||||
int wleft = has_width ? width - (int)(s - start) : 0x7FFFFFFF;
|
||||
while (*s && wleft > 0) {
|
||||
int d = -1;
|
||||
if (*s >= '0' && *s <= '9') d = *s - '0';
|
||||
else if (*s >= 'a' && *s <= 'f') d = *s - 'a' + 10;
|
||||
else if (*s >= 'A' && *s <= 'F') d = *s - 'A' + 10;
|
||||
if (d < 0 || d >= base) break;
|
||||
val = val * base + (unsigned)d;
|
||||
s++; wleft--;
|
||||
got_digit = 1;
|
||||
}
|
||||
if (!got_digit) return matched ? matched : -1;
|
||||
if (!suppress) {
|
||||
if (allow_neg && neg) {
|
||||
long long sv = -(long long)val;
|
||||
if (len_mod == 4) *va_arg(ap, long long *) = sv;
|
||||
else if (len_mod == 3 || len_mod == 5) *va_arg(ap, long *) = (long)sv;
|
||||
else if (len_mod == 1) *va_arg(ap, short *) = (short)sv;
|
||||
else if (len_mod == 2) *va_arg(ap, signed char *) = (signed char)sv;
|
||||
else *va_arg(ap, int *) = (int)sv;
|
||||
} else {
|
||||
if (len_mod == 4) *va_arg(ap, unsigned long long *) = val;
|
||||
else if (len_mod == 3 || len_mod == 5) *va_arg(ap, unsigned long *) = (unsigned long)val;
|
||||
else if (len_mod == 1) *va_arg(ap, unsigned short *) = (unsigned short)val;
|
||||
else if (len_mod == 2) *va_arg(ap, unsigned char *) = (unsigned char)val;
|
||||
else *va_arg(ap, unsigned int *) = (unsigned int)val;
|
||||
}
|
||||
matched++;
|
||||
}
|
||||
saw_anything = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conv == 'f' || conv == 'e' || conv == 'g' ||
|
||||
conv == 'E' || conv == 'G' || conv == 'a' || conv == 'A')
|
||||
{
|
||||
char *endp;
|
||||
uint64_t bits = __cervus_strtod_bits(s, &endp);
|
||||
if (endp == s) return matched ? matched : -1;
|
||||
if (!suppress) {
|
||||
if (len_mod == 3) {
|
||||
double v;
|
||||
__builtin_memcpy(&v, &bits, sizeof(double));
|
||||
*va_arg(ap, double *) = v;
|
||||
} else {
|
||||
double dv;
|
||||
__builtin_memcpy(&dv, &bits, sizeof(double));
|
||||
*va_arg(ap, float *) = (float)dv;
|
||||
}
|
||||
matched++;
|
||||
}
|
||||
s = endp;
|
||||
saw_anything = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
return matched ? matched : -1;
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
int sscanf(const char *str, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int n = vsscanf(str, fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
|
||||
int vscanf(const char *fmt, va_list ap)
|
||||
{
|
||||
char buf[1024];
|
||||
if (!fgets(buf, (int)sizeof(buf), stdin)) return EOF;
|
||||
return vsscanf(buf, fmt, ap);
|
||||
}
|
||||
|
||||
int scanf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int n = vscanf(fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
|
||||
int vfscanf(FILE *stream, const char *fmt, va_list ap)
|
||||
{
|
||||
char buf[1024];
|
||||
if (!fgets(buf, (int)sizeof(buf), stream)) return EOF;
|
||||
return vsscanf(buf, fmt, ap);
|
||||
}
|
||||
|
||||
int fscanf(FILE *stream, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int n = vfscanf(stream, fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/cervus.h>
|
||||
|
||||
extern int __cervus_errno;
|
||||
|
||||
struct __cervus_FILE {
|
||||
int fd;
|
||||
int eof;
|
||||
int err;
|
||||
int flags;
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
size_t buf_pos;
|
||||
};
|
||||
|
||||
static struct __cervus_FILE __stdin_s = { 0, 0, 0, 0, NULL, 0, 0 };
|
||||
static struct __cervus_FILE __stdout_s = { 1, 0, 0, 0, NULL, 0, 0 };
|
||||
static struct __cervus_FILE __stderr_s = { 2, 0, 0, 0, NULL, 0, 0 };
|
||||
|
||||
FILE *stdin = &__stdin_s;
|
||||
FILE *stdout = &__stdout_s;
|
||||
FILE *stderr = &__stderr_s;
|
||||
|
||||
int fileno(FILE *s) { return s ? s->fd : -1; }
|
||||
int feof(FILE *s) { return s ? s->eof : 1; }
|
||||
int ferror(FILE *s) { return s ? s->err : 1; }
|
||||
void clearerr(FILE *s) { if (s) { s->eof = 0; s->err = 0; } }
|
||||
|
||||
FILE *fopen(const char *path, const char *mode)
|
||||
{
|
||||
if (!path || !mode) return NULL;
|
||||
int flags = 0;
|
||||
int has_plus = 0;
|
||||
for (const char *m = mode + 1; *m; m++) if (*m == '+') has_plus = 1;
|
||||
switch (mode[0]) {
|
||||
case 'r': flags = has_plus ? O_RDWR : O_RDONLY; break;
|
||||
case 'w': flags = (has_plus ? O_RDWR : O_WRONLY) | O_CREAT | O_TRUNC; break;
|
||||
case 'a': flags = (has_plus ? O_RDWR : O_WRONLY) | O_CREAT | O_APPEND; break;
|
||||
default: return NULL;
|
||||
}
|
||||
int fd = open(path, flags, 0644);
|
||||
if (fd < 0) return NULL;
|
||||
FILE *f = (FILE *)malloc(sizeof(FILE));
|
||||
if (!f) { close(fd); return NULL; }
|
||||
f->fd = fd;
|
||||
f->eof = 0;
|
||||
f->err = 0;
|
||||
f->flags = 1;
|
||||
f->buf = NULL;
|
||||
f->buf_size = 0;
|
||||
f->buf_pos = 0;
|
||||
return f;
|
||||
}
|
||||
|
||||
int fclose(FILE *s)
|
||||
{
|
||||
if (!s) return EOF;
|
||||
int fd = s->fd;
|
||||
int owned = s->flags & 1;
|
||||
close(fd);
|
||||
if (owned) free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fflush(FILE *s) { (void)s; return 0; }
|
||||
|
||||
size_t fread(void *buf, size_t size, size_t nmemb, FILE *s)
|
||||
{
|
||||
if (!s || size == 0 || nmemb == 0) return 0;
|
||||
size_t total = size * nmemb;
|
||||
size_t got = 0;
|
||||
while (got < total) {
|
||||
ssize_t r = read(s->fd, (char *)buf + got, total - got);
|
||||
if (r < 0) { s->err = 1; break; }
|
||||
if (r == 0) { s->eof = 1; break; }
|
||||
got += (size_t)r;
|
||||
}
|
||||
return got / size;
|
||||
}
|
||||
|
||||
size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *s)
|
||||
{
|
||||
if (!s || size == 0 || nmemb == 0) return 0;
|
||||
size_t total = size * nmemb;
|
||||
size_t sent = 0;
|
||||
while (sent < total) {
|
||||
ssize_t w = write(s->fd, (const char *)buf + sent, total - sent);
|
||||
if (w < 0) { s->err = 1; break; }
|
||||
if (w == 0) break;
|
||||
sent += (size_t)w;
|
||||
}
|
||||
return sent / size;
|
||||
}
|
||||
|
||||
int fseek(FILE *s, long off, int whence)
|
||||
{
|
||||
if (!s) return -1;
|
||||
off_t r = lseek(s->fd, (off_t)off, whence);
|
||||
if (r == (off_t)-1) { s->err = 1; return -1; }
|
||||
s->eof = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long ftell(FILE *s)
|
||||
{
|
||||
if (!s) return -1;
|
||||
return (long)lseek(s->fd, 0, SEEK_CUR);
|
||||
}
|
||||
|
||||
int fputc(int c, FILE *s)
|
||||
{
|
||||
unsigned char ch = (unsigned char)c;
|
||||
if (fwrite(&ch, 1, 1, s) != 1) return EOF;
|
||||
return (int)ch;
|
||||
}
|
||||
|
||||
int fgetc(FILE *s)
|
||||
{
|
||||
unsigned char ch;
|
||||
if (fread(&ch, 1, 1, s) != 1) return EOF;
|
||||
return (int)ch;
|
||||
}
|
||||
|
||||
int fputs(const char *str, FILE *s)
|
||||
{
|
||||
if (!str) return EOF;
|
||||
size_t n = strlen(str);
|
||||
if (fwrite(str, 1, n, s) != n) return EOF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *fgets(char *str, int n, FILE *s)
|
||||
{
|
||||
if (!str || n <= 0 || !s) return NULL;
|
||||
int i = 0;
|
||||
while (i < n - 1) {
|
||||
int c = fgetc(s);
|
||||
if (c == EOF) {
|
||||
if (i == 0) return NULL;
|
||||
break;
|
||||
}
|
||||
str[i++] = (char)c;
|
||||
if (c == '\n') break;
|
||||
}
|
||||
str[i] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
int putchar(int c) { return fputc(c, stdout); }
|
||||
int getchar(void) { return fgetc(stdin); }
|
||||
|
||||
int puts(const char *s)
|
||||
{
|
||||
if (fputs(s, stdout) == EOF) return EOF;
|
||||
if (fputc('\n', stdout) == EOF) return EOF;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __u64_to_str(uint64_t v, char *out, int base, int upper)
|
||||
{
|
||||
char tmp[32];
|
||||
int i = 0;
|
||||
const char *digs = upper ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
if (v == 0) { out[0] = '0'; out[1] = 0; return; }
|
||||
while (v) { tmp[i++] = digs[v % (uint64_t)base]; v /= (uint64_t)base; }
|
||||
int j = 0;
|
||||
while (i > 0) out[j++] = tmp[--i];
|
||||
out[j] = 0;
|
||||
}
|
||||
|
||||
int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap)
|
||||
{
|
||||
size_t pos = 0;
|
||||
#define __PUT(s, n) do { \
|
||||
size_t __n = (n); const char *__s = (s); \
|
||||
for (size_t __i = 0; __i < __n; __i++) { \
|
||||
if (pos + 1 < sz) buf[pos] = __s[__i]; \
|
||||
pos++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
while (*fmt) {
|
||||
if (*fmt != '%') { __PUT(fmt, 1); fmt++; continue; }
|
||||
fmt++;
|
||||
int pad_zero = 0, left_align = 0, plus_flag = 0;
|
||||
while (*fmt == '0' || *fmt == '-' || *fmt == '+' || *fmt == ' ' || *fmt == '#') {
|
||||
if (*fmt == '0') pad_zero = 1;
|
||||
else if (*fmt == '-') left_align = 1;
|
||||
else if (*fmt == '+') plus_flag = 1;
|
||||
fmt++;
|
||||
}
|
||||
int width = 0;
|
||||
if (*fmt == '*') { width = va_arg(ap, int); if (width < 0) { left_align = 1; width = -width; } fmt++; }
|
||||
else while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; }
|
||||
int prec = -1;
|
||||
if (*fmt == '.') {
|
||||
fmt++;
|
||||
prec = 0;
|
||||
if (*fmt == '*') { prec = va_arg(ap, int); if (prec < 0) prec = 0; fmt++; }
|
||||
else while (*fmt >= '0' && *fmt <= '9') { prec = prec * 10 + (*fmt - '0'); fmt++; }
|
||||
}
|
||||
int is_long = 0, is_size_t = 0;
|
||||
while (*fmt == 'l') { is_long++; fmt++; }
|
||||
if (*fmt == 'z') { is_size_t = 1; fmt++; }
|
||||
if (*fmt == 'h') { fmt++; }
|
||||
|
||||
char nb[40];
|
||||
switch (*fmt) {
|
||||
case 's': {
|
||||
const char *s = va_arg(ap, const char *);
|
||||
if (!s) s = "(null)";
|
||||
size_t l = strlen(s);
|
||||
if (prec >= 0 && (size_t)prec < l) l = (size_t)prec;
|
||||
int pad = (int)(width > (int)l ? width - (int)l : 0);
|
||||
if (!left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
|
||||
__PUT(s, l);
|
||||
if (left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
|
||||
break;
|
||||
}
|
||||
case 'd': case 'i': {
|
||||
int64_t v;
|
||||
if (is_long >= 2) v = va_arg(ap, long long);
|
||||
else if (is_long) v = va_arg(ap, long);
|
||||
else if (is_size_t) v = (int64_t)va_arg(ap, size_t);
|
||||
else v = va_arg(ap, int);
|
||||
int neg = v < 0;
|
||||
uint64_t u = neg ? (uint64_t)(-v) : (uint64_t)v;
|
||||
__u64_to_str(u, nb, 10, 0);
|
||||
int numlen = (int)strlen(nb) + (neg || plus_flag ? 1 : 0);
|
||||
int pad = width > numlen ? width - numlen : 0;
|
||||
if (!left_align && !pad_zero) for (int i = 0; i < pad; i++) __PUT(" ", 1);
|
||||
if (neg) __PUT("-", 1);
|
||||
else if (plus_flag) __PUT("+", 1);
|
||||
if (!left_align && pad_zero) for (int i = 0; i < pad; i++) __PUT("0", 1);
|
||||
__PUT(nb, strlen(nb));
|
||||
if (left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
uint64_t v;
|
||||
if (is_long >= 2) v = va_arg(ap, unsigned long long);
|
||||
else if (is_long) v = va_arg(ap, unsigned long);
|
||||
else if (is_size_t) v = va_arg(ap, size_t);
|
||||
else v = va_arg(ap, unsigned);
|
||||
__u64_to_str(v, nb, 10, 0);
|
||||
int numlen = (int)strlen(nb);
|
||||
int pad = width > numlen ? width - numlen : 0;
|
||||
if (!left_align) for (int i = 0; i < pad; i++) __PUT(pad_zero ? "0" : " ", 1);
|
||||
__PUT(nb, strlen(nb));
|
||||
if (left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
|
||||
break;
|
||||
}
|
||||
case 'x': case 'X': {
|
||||
uint64_t v;
|
||||
if (is_long >= 2) v = va_arg(ap, unsigned long long);
|
||||
else if (is_long) v = va_arg(ap, unsigned long);
|
||||
else if (is_size_t) v = va_arg(ap, size_t);
|
||||
else v = va_arg(ap, unsigned);
|
||||
__u64_to_str(v, nb, 16, *fmt == 'X');
|
||||
int numlen = (int)strlen(nb);
|
||||
int pad = width > numlen ? width - numlen : 0;
|
||||
if (!left_align) for (int i = 0; i < pad; i++) __PUT(pad_zero ? "0" : " ", 1);
|
||||
__PUT(nb, strlen(nb));
|
||||
if (left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
|
||||
break;
|
||||
}
|
||||
case 'o': {
|
||||
uint64_t v;
|
||||
if (is_long >= 2) v = va_arg(ap, unsigned long long);
|
||||
else if (is_long) v = va_arg(ap, unsigned long);
|
||||
else v = va_arg(ap, unsigned);
|
||||
__u64_to_str(v, nb, 8, 0);
|
||||
__PUT(nb, strlen(nb));
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
uint64_t v = (uint64_t)(uintptr_t)va_arg(ap, void *);
|
||||
__PUT("0x", 2);
|
||||
__u64_to_str(v, nb, 16, 0);
|
||||
__PUT(nb, strlen(nb));
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
char c = (char)va_arg(ap, int);
|
||||
__PUT(&c, 1);
|
||||
break;
|
||||
}
|
||||
case '%': __PUT("%", 1); break;
|
||||
default: {
|
||||
char c = *fmt;
|
||||
__PUT("%", 1);
|
||||
__PUT(&c, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fmt++;
|
||||
}
|
||||
if (sz > 0) buf[pos < sz ? pos : sz - 1] = '\0';
|
||||
#undef __PUT
|
||||
return (int)pos;
|
||||
}
|
||||
|
||||
int snprintf(char *buf, size_t sz, const char *fmt, ...)
|
||||
{
|
||||
va_list ap; va_start(ap, fmt);
|
||||
int n = vsnprintf(buf, sz, fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
|
||||
int sprintf(char *buf, const char *fmt, ...)
|
||||
{
|
||||
va_list ap; va_start(ap, fmt);
|
||||
int n = vsnprintf(buf, (size_t)-1, fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
|
||||
int vsprintf(char *buf, const char *fmt, va_list ap)
|
||||
{
|
||||
return vsnprintf(buf, (size_t)-1, fmt, ap);
|
||||
}
|
||||
|
||||
int vfprintf(FILE *s, const char *fmt, va_list ap)
|
||||
{
|
||||
char small[512];
|
||||
va_list ap2;
|
||||
va_copy(ap2, ap);
|
||||
int needed = vsnprintf(small, sizeof(small), fmt, ap2);
|
||||
va_end(ap2);
|
||||
if (needed < (int)sizeof(small)) {
|
||||
fwrite(small, 1, (size_t)needed, s);
|
||||
return needed;
|
||||
}
|
||||
char *big = (char *)malloc((size_t)needed + 1);
|
||||
if (!big) {
|
||||
fwrite(small, 1, sizeof(small) - 1, s);
|
||||
return (int)sizeof(small) - 1;
|
||||
}
|
||||
vsnprintf(big, (size_t)needed + 1, fmt, ap);
|
||||
fwrite(big, 1, (size_t)needed, s);
|
||||
free(big);
|
||||
return needed;
|
||||
}
|
||||
|
||||
int fprintf(FILE *s, const char *fmt, ...)
|
||||
{
|
||||
va_list ap; va_start(ap, fmt);
|
||||
int n = vfprintf(s, fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
|
||||
int vprintf(const char *fmt, va_list ap) { return vfprintf(stdout, fmt, ap); }
|
||||
|
||||
int printf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap; va_start(ap, fmt);
|
||||
int n = vfprintf(stdout, fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
|
||||
void perror(const char *msg)
|
||||
{
|
||||
if (msg && *msg) { fputs(msg, stderr); fputs(": ", stderr); }
|
||||
fputs(strerror(__cervus_errno), stderr);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
int remove(const char *path) { return unlink(path); }
|
||||
|
||||
int mkstemp(char *template)
|
||||
{
|
||||
if (!template) { __cervus_errno = EINVAL; return -1; }
|
||||
size_t len = strlen(template);
|
||||
if (len < 6) { __cervus_errno = EINVAL; return -1; }
|
||||
char *suf = template + len - 6;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (suf[i] != 'X') { __cervus_errno = EINVAL; return -1; }
|
||||
}
|
||||
static uint64_t __mkstemp_seq = 0;
|
||||
uint64_t pid = (uint64_t)getpid();
|
||||
for (int attempt = 0; attempt < 100; attempt++) {
|
||||
uint64_t seed = (cervus_uptime_ns() ^ (pid << 32)) + (__mkstemp_seq++);
|
||||
const char *alpha = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
for (int i = 0; i < 6; i++) {
|
||||
suf[i] = alpha[seed % 36];
|
||||
seed /= 36;
|
||||
}
|
||||
struct stat st;
|
||||
if (stat(template, &st) == 0) continue;
|
||||
int fd = open(template, O_RDWR | O_CREAT, 0600);
|
||||
if (fd >= 0) return fd;
|
||||
}
|
||||
__cervus_errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *tmpfile(void)
|
||||
{
|
||||
char tmpl[64];
|
||||
strcpy(tmpl, "/mnt/tmp/tmpXXXXXX");
|
||||
int fd = mkstemp(tmpl);
|
||||
if (fd < 0) {
|
||||
strcpy(tmpl, "/tmp/tmpXXXXXX");
|
||||
fd = mkstemp(tmpl);
|
||||
if (fd < 0) return NULL;
|
||||
}
|
||||
unlink(tmpl);
|
||||
FILE *f = (FILE *)malloc(sizeof(FILE));
|
||||
if (!f) { close(fd); return NULL; }
|
||||
f->fd = fd;
|
||||
f->eof = 0;
|
||||
f->err = 0;
|
||||
f->flags = 1;
|
||||
f->buf = NULL;
|
||||
f->buf_size = 0;
|
||||
f->buf_pos = 0;
|
||||
return f;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
extern int __cervus_errno;
|
||||
extern int __cervus_argc;
|
||||
extern char **__cervus_argv;
|
||||
|
||||
int abs(int x) { return x < 0 ? -x : x; }
|
||||
long labs(long x) { return x < 0 ? -x : x; }
|
||||
long long llabs(long long x){ return x < 0 ? -x : x; }
|
||||
|
||||
static long long __parse_signed(const char *s, char **end, int base, int is_unsigned)
|
||||
{
|
||||
while (isspace((unsigned char)*s)) s++;
|
||||
int neg = 0;
|
||||
if (*s == '-') { neg = 1; s++; }
|
||||
else if (*s == '+') s++;
|
||||
if ((base == 0 || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
||||
s += 2; base = 16;
|
||||
} else if (base == 0 && *s == '0') {
|
||||
s++; base = 8;
|
||||
} else if (base == 0) {
|
||||
base = 10;
|
||||
}
|
||||
unsigned long long v = 0;
|
||||
while (*s) {
|
||||
int d;
|
||||
if (isdigit((unsigned char)*s)) d = *s - '0';
|
||||
else if (*s >= 'a' && *s <= 'z') d = *s - 'a' + 10;
|
||||
else if (*s >= 'A' && *s <= 'Z') d = *s - 'A' + 10;
|
||||
else break;
|
||||
if (d >= base) break;
|
||||
v = v * (unsigned long long)base + (unsigned long long)d;
|
||||
s++;
|
||||
}
|
||||
if (end) *end = (char *)s;
|
||||
if (is_unsigned) return (long long)v;
|
||||
return neg ? -(long long)v : (long long)v;
|
||||
}
|
||||
|
||||
long strtol(const char *s, char **e, int b) { return (long)__parse_signed(s, e, b, 0); }
|
||||
long long strtoll(const char *s, char **e, int b) { return __parse_signed(s, e, b, 0); }
|
||||
unsigned long strtoul(const char *s, char **e, int b) { return (unsigned long)__parse_signed(s, e, b, 1); }
|
||||
unsigned long long strtoull(const char *s, char **e, int b) { return (unsigned long long)__parse_signed(s, e, b, 1); }
|
||||
|
||||
int atoi(const char *s) { return (int)strtol(s, NULL, 10); }
|
||||
long atol(const char *s) { return strtol(s, NULL, 10); }
|
||||
long long atoll(const char *s) { return strtoll(s, NULL, 10); }
|
||||
|
||||
static unsigned long __rand_state = 1;
|
||||
int rand(void) { __rand_state = __rand_state * 1103515245UL + 12345UL; return (int)((__rand_state >> 16) & 0x7FFF); }
|
||||
void srand(unsigned int seed) { __rand_state = seed; }
|
||||
|
||||
static void __qswap(void *a, void *b, size_t sz)
|
||||
{
|
||||
unsigned char tmp;
|
||||
unsigned char *pa = (unsigned char *)a;
|
||||
unsigned char *pb = (unsigned char *)b;
|
||||
while (sz--) { tmp = *pa; *pa++ = *pb; *pb++ = tmp; }
|
||||
}
|
||||
|
||||
void qsort(void *base, size_t nmemb, size_t sz, int (*cmp)(const void *, const void *))
|
||||
{
|
||||
if (nmemb < 2) return;
|
||||
unsigned char *arr = (unsigned char *)base;
|
||||
unsigned char *pivot = arr + (nmemb - 1) * sz;
|
||||
size_t i = 0;
|
||||
for (size_t j = 0; j < nmemb - 1; j++) {
|
||||
if (cmp(arr + j * sz, pivot) <= 0) {
|
||||
if (i != j) __qswap(arr + i * sz, arr + j * sz, sz);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
__qswap(arr + i * sz, pivot, sz);
|
||||
qsort(arr, i, sz, cmp);
|
||||
qsort(arr + (i + 1) * sz, nmemb - i - 1, sz, cmp);
|
||||
}
|
||||
|
||||
char *getenv(const char *name)
|
||||
{
|
||||
if (!name) return NULL;
|
||||
size_t nl = strlen(name);
|
||||
for (int i = 1; i < __cervus_argc; i++) {
|
||||
const char *a = __cervus_argv[i];
|
||||
if (a && a[0] == '-' && a[1] == '-' &&
|
||||
a[2] == 'e' && a[3] == 'n' && a[4] == 'v' && a[5] == ':') {
|
||||
const char *kv = a + 6;
|
||||
if (strncmp(kv, name, nl) == 0 && kv[nl] == '=')
|
||||
return (char *)(kv + nl + 1);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int system(const char *cmd)
|
||||
{
|
||||
(void)cmd;
|
||||
__cervus_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define ATEXIT_MAX 32
|
||||
static void (*__atexit_fns[ATEXIT_MAX])(void);
|
||||
static int __atexit_cnt = 0;
|
||||
|
||||
int atexit(void (*fn)(void))
|
||||
{
|
||||
if (__atexit_cnt >= ATEXIT_MAX) return -1;
|
||||
__atexit_fns[__atexit_cnt++] = fn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fflush(FILE *stream);
|
||||
|
||||
void exit(int status)
|
||||
{
|
||||
while (__atexit_cnt > 0) {
|
||||
__atexit_cnt--;
|
||||
if (__atexit_fns[__atexit_cnt]) __atexit_fns[__atexit_cnt]();
|
||||
}
|
||||
extern FILE *stdout;
|
||||
extern FILE *stderr;
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
syscall1(SYS_EXIT, status);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void abort(void)
|
||||
{
|
||||
syscall1(SYS_EXIT, 134);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
uint64_t __cervus_strtod_bits(const char *s, char **endptr)
|
||||
{
|
||||
if (!s) {
|
||||
if (endptr) *endptr = (char *)s;
|
||||
return 0;
|
||||
}
|
||||
const char *p = s;
|
||||
while (*p == ' ' || *p == '\t' || *p == '\n' ||
|
||||
*p == '\r' || *p == '\f' || *p == '\v') p++;
|
||||
|
||||
int sign = 0;
|
||||
if (*p == '+') p++;
|
||||
else if (*p == '-') { sign = 1; p++; }
|
||||
|
||||
if ((p[0] == 'i' || p[0] == 'I') &&
|
||||
(p[1] == 'n' || p[1] == 'N') &&
|
||||
(p[2] == 'f' || p[2] == 'F')) {
|
||||
p += 3;
|
||||
if ((p[0] == 'i' || p[0] == 'I') &&
|
||||
(p[1] == 'n' || p[1] == 'N') &&
|
||||
(p[2] == 'i' || p[2] == 'I') &&
|
||||
(p[3] == 't' || p[3] == 'T') &&
|
||||
(p[4] == 'y' || p[4] == 'Y')) p += 5;
|
||||
if (endptr) *endptr = (char *)p;
|
||||
return ((uint64_t)sign << 63) | 0x7FF0000000000000ULL;
|
||||
}
|
||||
if ((p[0] == 'n' || p[0] == 'N') &&
|
||||
(p[1] == 'a' || p[1] == 'A') &&
|
||||
(p[2] == 'n' || p[2] == 'N')) {
|
||||
p += 3;
|
||||
if (endptr) *endptr = (char *)p;
|
||||
return 0x7FF8000000000000ULL;
|
||||
}
|
||||
|
||||
uint64_t mant = 0;
|
||||
int dec_exp = 0;
|
||||
int seen_digit = 0;
|
||||
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
seen_digit = 1;
|
||||
if (mant <= (UINT64_MAX - 9) / 10) {
|
||||
mant = mant * 10 + (uint64_t)(*p - '0');
|
||||
} else {
|
||||
dec_exp++;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
seen_digit = 1;
|
||||
if (mant <= (UINT64_MAX - 9) / 10) {
|
||||
mant = mant * 10 + (uint64_t)(*p - '0');
|
||||
dec_exp--;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
if (!seen_digit) {
|
||||
if (endptr) *endptr = (char *)s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*p == 'e' || *p == 'E') {
|
||||
const char *ep = p + 1;
|
||||
int esign = 0;
|
||||
if (*ep == '+') ep++;
|
||||
else if (*ep == '-') { esign = 1; ep++; }
|
||||
if (*ep >= '0' && *ep <= '9') {
|
||||
int eval = 0;
|
||||
while (*ep >= '0' && *ep <= '9') {
|
||||
if (eval < 10000) eval = eval * 10 + (*ep - '0');
|
||||
ep++;
|
||||
}
|
||||
dec_exp += esign ? -eval : eval;
|
||||
p = ep;
|
||||
}
|
||||
}
|
||||
if (endptr) *endptr = (char *)p;
|
||||
|
||||
if (mant == 0) {
|
||||
return (uint64_t)sign << 63;
|
||||
}
|
||||
|
||||
int bin_exp = 0;
|
||||
while ((mant >> 63) == 0) {
|
||||
mant <<= 1;
|
||||
bin_exp--;
|
||||
}
|
||||
|
||||
while (dec_exp > 0) {
|
||||
uint64_t a_hi = mant >> 32;
|
||||
uint64_t a_lo = mant & 0xFFFFFFFFULL;
|
||||
uint64_t p_lo = a_lo * 10;
|
||||
uint64_t p_hi = a_hi * 10;
|
||||
uint64_t mid_carry = p_lo >> 32;
|
||||
uint64_t lo = ((p_hi + mid_carry) << 32) | (p_lo & 0xFFFFFFFFULL);
|
||||
uint64_t hi = (p_hi + mid_carry) >> 32;
|
||||
|
||||
while (hi != 0) {
|
||||
lo = (lo >> 1) | (hi << 63);
|
||||
hi >>= 1;
|
||||
bin_exp++;
|
||||
}
|
||||
mant = lo;
|
||||
while ((mant >> 63) == 0) {
|
||||
mant <<= 1;
|
||||
bin_exp--;
|
||||
}
|
||||
dec_exp--;
|
||||
}
|
||||
while (dec_exp < 0) {
|
||||
mant = mant / 10;
|
||||
if (mant == 0) break;
|
||||
while ((mant >> 63) == 0) {
|
||||
mant <<= 1;
|
||||
bin_exp--;
|
||||
}
|
||||
dec_exp++;
|
||||
}
|
||||
|
||||
int ieee_exp = bin_exp + 63 + 1023;
|
||||
uint64_t no_implicit = mant & 0x7FFFFFFFFFFFFFFFULL;
|
||||
uint64_t round_bit = (no_implicit >> 10) & 1;
|
||||
uint64_t sticky = (no_implicit & 0x3FFULL) ? 1 : 0;
|
||||
uint64_t frac = no_implicit >> 11;
|
||||
if (round_bit && (sticky || (frac & 1))) {
|
||||
frac++;
|
||||
if (frac == (1ULL << 52)) {
|
||||
frac = 0;
|
||||
ieee_exp++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ieee_exp >= 0x7FF) {
|
||||
return ((uint64_t)sign << 63) | 0x7FF0000000000000ULL;
|
||||
}
|
||||
if (ieee_exp <= 0) {
|
||||
return (uint64_t)sign << 63;
|
||||
}
|
||||
return ((uint64_t)sign << 63) |
|
||||
((uint64_t)ieee_exp << 52) |
|
||||
(frac & 0xFFFFFFFFFFFFFULL);
|
||||
}
|
||||
|
||||
double strtod(const char *s, char **endptr)
|
||||
{
|
||||
uint64_t bits = __cervus_strtod_bits(s, endptr);
|
||||
double result;
|
||||
__builtin_memcpy(&result, &bits, sizeof(double));
|
||||
return result;
|
||||
}
|
||||
|
||||
float strtof(const char *s, char **endptr)
|
||||
{
|
||||
return (float)strtod(s, endptr);
|
||||
}
|
||||
|
||||
long double strtold(const char *s, char **endptr)
|
||||
{
|
||||
return (long double)strtod(s, endptr);
|
||||
}
|
||||
|
||||
double atof(const char *s)
|
||||
{
|
||||
return strtod(s, NULL);
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
void *memset(void *d, int c, size_t n)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)d;
|
||||
while (n--) *p++ = (unsigned char)c;
|
||||
return d;
|
||||
}
|
||||
|
||||
void *memcpy(void *d, const void *s, size_t n)
|
||||
{
|
||||
unsigned char *dd = (unsigned char *)d;
|
||||
const unsigned char *ss = (const unsigned char *)s;
|
||||
while (n--) *dd++ = *ss++;
|
||||
return d;
|
||||
}
|
||||
|
||||
void *memmove(void *d, const void *s, size_t n)
|
||||
{
|
||||
unsigned char *dd = (unsigned char *)d;
|
||||
const unsigned char *ss = (const unsigned char *)s;
|
||||
if (dd < ss) { while (n--) *dd++ = *ss++; }
|
||||
else { dd += n; ss += n; while (n--) *--dd = *--ss; }
|
||||
return d;
|
||||
}
|
||||
|
||||
int memcmp(const void *a, const void *b, size_t n)
|
||||
{
|
||||
const unsigned char *x = (const unsigned char *)a;
|
||||
const unsigned char *y = (const unsigned char *)b;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (x[i] != y[i]) return x[i] - y[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *memchr(const void *s, int c, size_t n)
|
||||
{
|
||||
const unsigned char *p = (const unsigned char *)s;
|
||||
while (n--) { if (*p == (unsigned char)c) return (void *)p; p++; }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen)
|
||||
{
|
||||
if (nlen == 0) return (void *)haystack;
|
||||
if (hlen < nlen) return NULL;
|
||||
const unsigned char *h = (const unsigned char *)haystack;
|
||||
const unsigned char *n = (const unsigned char *)needle;
|
||||
size_t last = hlen - nlen;
|
||||
for (size_t i = 0; i <= last; i++) {
|
||||
if (h[i] == n[0] && memcmp(h + i, n, nlen) == 0)
|
||||
return (void *)(h + i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t strlen(const char *s) { size_t n = 0; while (s[n]) n++; return n; }
|
||||
|
||||
size_t strnlen(const char *s, size_t max)
|
||||
{
|
||||
size_t n = 0;
|
||||
while (n < max && s[n]) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
int strcmp(const char *a, const char *b)
|
||||
{
|
||||
while (*a && *a == *b) { a++; b++; }
|
||||
return (unsigned char)*a - (unsigned char)*b;
|
||||
}
|
||||
|
||||
int strncmp(const char *a, const char *b, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (a[i] != b[i]) return (unsigned char)a[i] - (unsigned char)b[i];
|
||||
if (!a[i]) return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int strcasecmp(const char *a, const char *b)
|
||||
{
|
||||
while (*a && tolower((unsigned char)*a) == tolower((unsigned char)*b)) {
|
||||
a++; b++;
|
||||
}
|
||||
return tolower((unsigned char)*a) - tolower((unsigned char)*b);
|
||||
}
|
||||
|
||||
int strncasecmp(const char *a, const char *b, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
int ca = tolower((unsigned char)a[i]);
|
||||
int cb = tolower((unsigned char)b[i]);
|
||||
if (ca != cb) return ca - cb;
|
||||
if (!a[i]) return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *strcpy(char *d, const char *s)
|
||||
{
|
||||
char *r = d;
|
||||
while ((*d++ = *s++)) { }
|
||||
return r;
|
||||
}
|
||||
|
||||
char *strncpy(char *d, const char *s, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < n && s[i]; i++) d[i] = s[i];
|
||||
for (; i < n; i++) d[i] = 0;
|
||||
return d;
|
||||
}
|
||||
|
||||
char *strcat(char *d, const char *s)
|
||||
{
|
||||
char *r = d;
|
||||
while (*d) d++;
|
||||
while ((*d++ = *s++)) { }
|
||||
return r;
|
||||
}
|
||||
|
||||
char *strncat(char *d, const char *s, size_t n)
|
||||
{
|
||||
char *r = d;
|
||||
while (*d) d++;
|
||||
for (size_t i = 0; i < n && s[i]; i++) *d++ = s[i];
|
||||
*d = '\0';
|
||||
return r;
|
||||
}
|
||||
|
||||
char *strchr(const char *s, int c)
|
||||
{
|
||||
for (; *s; s++) if (*s == (char)c) return (char *)s;
|
||||
return c == 0 ? (char *)s : NULL;
|
||||
}
|
||||
|
||||
char *strrchr(const char *s, int c)
|
||||
{
|
||||
const char *r = NULL;
|
||||
for (; *s; s++) if (*s == (char)c) r = s;
|
||||
return (char *)(c == 0 ? s : r);
|
||||
}
|
||||
|
||||
char *strstr(const char *h, const char *n)
|
||||
{
|
||||
if (!*n) return (char *)h;
|
||||
for (; *h; h++) {
|
||||
const char *a = h, *b = n;
|
||||
while (*a && *b && *a == *b) { a++; b++; }
|
||||
if (!*b) return (char *)h;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t strspn(const char *s, const char *accept)
|
||||
{
|
||||
size_t n = 0;
|
||||
while (s[n]) {
|
||||
int found = 0;
|
||||
for (size_t i = 0; accept[i]; i++)
|
||||
if (s[n] == accept[i]) { found = 1; break; }
|
||||
if (!found) break;
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t strcspn(const char *s, const char *reject)
|
||||
{
|
||||
size_t n = 0;
|
||||
while (s[n]) {
|
||||
for (size_t i = 0; reject[i]; i++)
|
||||
if (s[n] == reject[i]) return n;
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
char *strpbrk(const char *s, const char *accept)
|
||||
{
|
||||
while (*s) {
|
||||
for (const char *a = accept; *a; a++)
|
||||
if (*s == *a) return (char *)s;
|
||||
s++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *strdup(const char *s)
|
||||
{
|
||||
size_t n = strlen(s) + 1;
|
||||
char *p = (char *)malloc(n);
|
||||
if (!p) return NULL;
|
||||
memcpy(p, s, n);
|
||||
return p;
|
||||
}
|
||||
|
||||
char *strndup(const char *s, size_t n)
|
||||
{
|
||||
size_t len = strnlen(s, n);
|
||||
char *p = (char *)malloc(len + 1);
|
||||
if (!p) return NULL;
|
||||
memcpy(p, s, len);
|
||||
p[len] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
char *strtok_r(char *str, const char *delim, char **saveptr)
|
||||
{
|
||||
char *s = str ? str : (saveptr ? *saveptr : NULL);
|
||||
if (!s || !delim) return NULL;
|
||||
|
||||
while (*s) {
|
||||
const char *d;
|
||||
for (d = delim; *d; d++) if (*s == *d) break;
|
||||
if (!*d) break;
|
||||
s++;
|
||||
}
|
||||
if (!*s) {
|
||||
if (saveptr) *saveptr = NULL;
|
||||
return NULL;
|
||||
}
|
||||
char *tok = s;
|
||||
|
||||
while (*s) {
|
||||
const char *d;
|
||||
for (d = delim; *d; d++) if (*s == *d) break;
|
||||
if (*d) {
|
||||
*s = '\0';
|
||||
if (saveptr) *saveptr = s + 1;
|
||||
return tok;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
if (saveptr) *saveptr = NULL;
|
||||
return tok;
|
||||
}
|
||||
|
||||
static char *__strtok_save = NULL;
|
||||
char *strtok(char *str, const char *delim)
|
||||
{
|
||||
return strtok_r(str, delim, &__strtok_save);
|
||||
}
|
||||
|
||||
char *strerror(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case 0: return "Success";
|
||||
case EPERM: return "Operation not permitted";
|
||||
case ENOENT: return "No such file or directory";
|
||||
case ESRCH: return "No such process";
|
||||
case EINTR: return "Interrupted system call";
|
||||
case EIO: return "Input/output error";
|
||||
case EBADF: return "Bad file descriptor";
|
||||
case ECHILD: return "No child processes";
|
||||
case EAGAIN: return "Resource temporarily unavailable";
|
||||
case ENOMEM: return "Cannot allocate memory";
|
||||
case EACCES: return "Permission denied";
|
||||
case EFAULT: return "Bad address";
|
||||
case EBUSY: return "Device or resource busy";
|
||||
case EEXIST: return "File exists";
|
||||
case ENODEV: return "No such device";
|
||||
case ENOTDIR: return "Not a directory";
|
||||
case EISDIR: return "Is a directory";
|
||||
case EINVAL: return "Invalid argument";
|
||||
case EMFILE: return "Too many open files";
|
||||
case ENOTTY: return "Inappropriate ioctl for device";
|
||||
case ENOSPC: return "No space left on device";
|
||||
case EPIPE: return "Broken pipe";
|
||||
case ENOSYS: return "Function not implemented";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
char *strsignal(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case 1: return "Hangup";
|
||||
case 2: return "Interrupt";
|
||||
case 3: return "Quit";
|
||||
case 4: return "Illegal instruction";
|
||||
case 6: return "Aborted";
|
||||
case 8: return "Floating point exception";
|
||||
case 9: return "Killed";
|
||||
case 11: return "Segmentation fault";
|
||||
case 13: return "Broken pipe";
|
||||
case 14: return "Alarm clock";
|
||||
case 15: return "Terminated";
|
||||
default: return "Unknown signal";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern int __cervus_errno;
|
||||
|
||||
typedef struct { int64_t tv_sec; int64_t tv_nsec; } __cervus_ts_raw_t;
|
||||
|
||||
time_t time(time_t *t)
|
||||
{
|
||||
__cervus_ts_raw_t ts = {0, 0};
|
||||
syscall2(SYS_CLOCK_GET, 0, &ts);
|
||||
time_t v = (time_t)ts.tv_sec;
|
||||
if (t) *t = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
int clock_gettime(int clk, struct timespec *tp)
|
||||
{
|
||||
if (!tp) { __cervus_errno = EINVAL; return -1; }
|
||||
__cervus_ts_raw_t ts = {0, 0};
|
||||
long r = syscall2(SYS_CLOCK_GET, clk, &ts);
|
||||
if (r < 0 && r > -4096) { __cervus_errno = (int)-r; return -1; }
|
||||
tp->tv_sec = (time_t)ts.tv_sec;
|
||||
tp->tv_nsec = (long)ts.tv_nsec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
clock_t clock(void)
|
||||
{
|
||||
uint64_t up_ns = (uint64_t)syscall0(SYS_UPTIME);
|
||||
return (clock_t)(up_ns / 1000ULL);
|
||||
}
|
||||
|
||||
int gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
(void)tz;
|
||||
if (!tv) { __cervus_errno = EINVAL; return -1; }
|
||||
__cervus_ts_raw_t ts = {0, 0};
|
||||
syscall2(SYS_CLOCK_GET, 0, &ts);
|
||||
tv->tv_sec = (time_t)ts.tv_sec;
|
||||
tv->tv_usec = (long)(ts.tv_nsec / 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nanosleep(const struct timespec *req, struct timespec *rem)
|
||||
{
|
||||
if (!req) { __cervus_errno = EINVAL; return -1; }
|
||||
uint64_t ns = (uint64_t)req->tv_sec * 1000000000ULL + (uint64_t)req->tv_nsec;
|
||||
long r = syscall1(SYS_SLEEP_NS, ns);
|
||||
if (rem) { rem->tv_sec = 0; rem->tv_nsec = 0; }
|
||||
if (r < 0 && r > -4096) { __cervus_errno = (int)-r; return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __is_leap(int y)
|
||||
{
|
||||
return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);
|
||||
}
|
||||
|
||||
static const int __days_in_mon[2][12] = {
|
||||
{31,28,31,30,31,30,31,31,30,31,30,31},
|
||||
{31,29,31,30,31,30,31,31,30,31,30,31},
|
||||
};
|
||||
|
||||
static struct tm __tm_buf;
|
||||
|
||||
struct tm *gmtime(const time_t *t)
|
||||
{
|
||||
if (!t) return NULL;
|
||||
long long sec = (long long)*t;
|
||||
long days = (long)(sec / 86400);
|
||||
long rem = (long)(sec % 86400);
|
||||
if (rem < 0) { rem += 86400; days--; }
|
||||
|
||||
__tm_buf.tm_hour = rem / 3600;
|
||||
rem -= __tm_buf.tm_hour * 3600;
|
||||
__tm_buf.tm_min = rem / 60;
|
||||
__tm_buf.tm_sec = rem - __tm_buf.tm_min * 60;
|
||||
|
||||
__tm_buf.tm_wday = (int)((days + 4) % 7);
|
||||
if (__tm_buf.tm_wday < 0) __tm_buf.tm_wday += 7;
|
||||
|
||||
int year = 1970;
|
||||
while (1) {
|
||||
int ly = __is_leap(year);
|
||||
int dy = ly ? 366 : 365;
|
||||
if (days >= dy) { days -= dy; year++; }
|
||||
else if (days < 0) { year--; days += __is_leap(year) ? 366 : 365; }
|
||||
else break;
|
||||
}
|
||||
__tm_buf.tm_year = year - 1900;
|
||||
__tm_buf.tm_yday = (int)days;
|
||||
int ly = __is_leap(year);
|
||||
int m = 0;
|
||||
while (m < 12 && days >= __days_in_mon[ly][m]) {
|
||||
days -= __days_in_mon[ly][m];
|
||||
m++;
|
||||
}
|
||||
__tm_buf.tm_mon = m;
|
||||
__tm_buf.tm_mday = (int)days + 1;
|
||||
__tm_buf.tm_isdst = 0;
|
||||
return &__tm_buf;
|
||||
}
|
||||
|
||||
struct tm *localtime(const time_t *t) { return gmtime(t); }
|
||||
|
||||
time_t mktime(struct tm *tm)
|
||||
{
|
||||
if (!tm) return (time_t)-1;
|
||||
int year = tm->tm_year + 1900;
|
||||
int mon = tm->tm_mon;
|
||||
long days = 0;
|
||||
for (int y = 1970; y < year; y++) days += __is_leap(y) ? 366 : 365;
|
||||
int ly = __is_leap(year);
|
||||
for (int m = 0; m < mon; m++) days += __days_in_mon[ly][m];
|
||||
days += tm->tm_mday - 1;
|
||||
long long sec = (long long)days * 86400LL
|
||||
+ (long long)tm->tm_hour * 3600LL
|
||||
+ (long long)tm->tm_min * 60LL
|
||||
+ (long long)tm->tm_sec;
|
||||
return (time_t)sec;
|
||||
}
|
||||
|
||||
static const char *__wday_name[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
|
||||
static const char *__mon_name[] = {"Jan","Feb","Mar","Apr","May","Jun",
|
||||
"Jul","Aug","Sep","Oct","Nov","Dec"};
|
||||
|
||||
static char __asctime_buf[32];
|
||||
|
||||
char *asctime(const struct tm *tm)
|
||||
{
|
||||
if (!tm) return NULL;
|
||||
int wday = tm->tm_wday; if (wday < 0 || wday > 6) wday = 0;
|
||||
int mon = tm->tm_mon; if (mon < 0 || mon > 11) mon = 0;
|
||||
int y = tm->tm_year + 1900;
|
||||
int pos = 0;
|
||||
const char *w = __wday_name[wday];
|
||||
const char *mn = __mon_name[mon];
|
||||
__asctime_buf[pos++] = w[0]; __asctime_buf[pos++] = w[1]; __asctime_buf[pos++] = w[2];
|
||||
__asctime_buf[pos++] = ' ';
|
||||
__asctime_buf[pos++] = mn[0]; __asctime_buf[pos++] = mn[1]; __asctime_buf[pos++] = mn[2];
|
||||
__asctime_buf[pos++] = ' ';
|
||||
int md = tm->tm_mday;
|
||||
__asctime_buf[pos++] = (char)('0' + (md/10 % 10));
|
||||
__asctime_buf[pos++] = (char)('0' + (md % 10));
|
||||
__asctime_buf[pos++] = ' ';
|
||||
int hh = tm->tm_hour, mm = tm->tm_min, ss = tm->tm_sec;
|
||||
__asctime_buf[pos++] = (char)('0' + (hh/10 % 10));
|
||||
__asctime_buf[pos++] = (char)('0' + (hh % 10));
|
||||
__asctime_buf[pos++] = ':';
|
||||
__asctime_buf[pos++] = (char)('0' + (mm/10 % 10));
|
||||
__asctime_buf[pos++] = (char)('0' + (mm % 10));
|
||||
__asctime_buf[pos++] = ':';
|
||||
__asctime_buf[pos++] = (char)('0' + (ss/10 % 10));
|
||||
__asctime_buf[pos++] = (char)('0' + (ss % 10));
|
||||
__asctime_buf[pos++] = ' ';
|
||||
__asctime_buf[pos++] = (char)('0' + (y/1000 % 10));
|
||||
__asctime_buf[pos++] = (char)('0' + (y/100 % 10));
|
||||
__asctime_buf[pos++] = (char)('0' + (y/10 % 10));
|
||||
__asctime_buf[pos++] = (char)('0' + (y % 10));
|
||||
__asctime_buf[pos++] = '\n';
|
||||
__asctime_buf[pos] = '\0';
|
||||
return __asctime_buf;
|
||||
}
|
||||
|
||||
char *ctime(const time_t *t) { return asctime(gmtime(t)); }
|
||||
|
||||
size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
|
||||
{
|
||||
if (!s || !fmt || !tm || max == 0) return 0;
|
||||
size_t i = 0;
|
||||
while (*fmt && i + 1 < max) {
|
||||
if (*fmt != '%') { s[i++] = *fmt++; continue; }
|
||||
fmt++;
|
||||
char tmp[16];
|
||||
int n = 0;
|
||||
switch (*fmt) {
|
||||
case 'Y': {
|
||||
int y = tm->tm_year + 1900;
|
||||
n = 4;
|
||||
tmp[0] = (char)('0' + (y/1000)%10);
|
||||
tmp[1] = (char)('0' + (y/100)%10);
|
||||
tmp[2] = (char)('0' + (y/10)%10);
|
||||
tmp[3] = (char)('0' + y%10);
|
||||
break;
|
||||
}
|
||||
case 'm': { int v=tm->tm_mon+1; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
|
||||
case 'd': { int v=tm->tm_mday; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
|
||||
case 'H': { int v=tm->tm_hour; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
|
||||
case 'M': { int v=tm->tm_min; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
|
||||
case 'S': { int v=tm->tm_sec; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
|
||||
case '%': tmp[0]='%'; n=1; break;
|
||||
default: tmp[0]='%'; tmp[1]=*fmt; n = (*fmt ? 2 : 1); break;
|
||||
}
|
||||
for (int k = 0; k < n && i + 1 < max; k++) s[i++] = tmp[k];
|
||||
if (*fmt) fmt++;
|
||||
}
|
||||
s[i] = '\0';
|
||||
return i;
|
||||
}
|
||||
Reference in New Issue
Block a user