mirror of
https://github.com/Leonmmcoset/cleonos.git
synced 2026-04-24 19:34:01 +00:00
1473 lines
32 KiB
C
1473 lines
32 KiB
C
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "../../include/cleonos_syscall.h"
|
|
|
|
#define DG_HEAP_SIZE (32U * 1024U * 1024U)
|
|
#define DG_MAX_MEM_FD 64
|
|
#define DG_PATH_MAX 192
|
|
#define DG_ENV_LINE_MAX 256
|
|
#define DG_STDIO_MAGIC 0x44474D46U
|
|
#define DG_SERIAL_LOG_CHUNK 176U
|
|
#define DG_SERIAL_LOG_PREFIX "[DOOM] "
|
|
|
|
struct dg_alloc_hdr {
|
|
size_t size;
|
|
int free;
|
|
struct dg_alloc_hdr *next;
|
|
struct dg_alloc_hdr *prev;
|
|
};
|
|
|
|
struct dg_mem_fd {
|
|
int used;
|
|
int writable;
|
|
int dirty;
|
|
size_t pos;
|
|
size_t size;
|
|
size_t cap;
|
|
unsigned char *data;
|
|
char path[DG_PATH_MAX];
|
|
};
|
|
|
|
struct dg_stream {
|
|
unsigned int magic;
|
|
int fd;
|
|
int eof_flag;
|
|
int err_flag;
|
|
};
|
|
|
|
static unsigned char g_dg_heap[DG_HEAP_SIZE];
|
|
static struct dg_alloc_hdr *g_dg_heap_head = (struct dg_alloc_hdr *)0;
|
|
static int g_dg_heap_ready = 0;
|
|
static struct dg_mem_fd g_dg_fds[DG_MAX_MEM_FD];
|
|
static struct dg_stream g_dg_stdin_stream = {DG_STDIO_MAGIC, 0, 0, 0};
|
|
static struct dg_stream g_dg_stdout_stream = {DG_STDIO_MAGIC, 1, 0, 0};
|
|
static struct dg_stream g_dg_stderr_stream = {DG_STDIO_MAGIC, 2, 0, 0};
|
|
static int g_dg_errno = 0;
|
|
static unsigned short g_dg_ctype_table[384];
|
|
static int g_dg_ctype_ready = 0;
|
|
|
|
FILE *stdin = (FILE *)(void *)&g_dg_stdin_stream;
|
|
FILE *stdout = (FILE *)(void *)&g_dg_stdout_stream;
|
|
FILE *stderr = (FILE *)(void *)&g_dg_stderr_stream;
|
|
|
|
int *__errno_location(void) {
|
|
return &g_dg_errno;
|
|
}
|
|
|
|
static int dg_is_digit(int ch) {
|
|
return (ch >= '0' && ch <= '9') ? 1 : 0;
|
|
}
|
|
|
|
static int dg_is_upper(int ch) {
|
|
return (ch >= 'A' && ch <= 'Z') ? 1 : 0;
|
|
}
|
|
|
|
static int dg_is_lower(int ch) {
|
|
return (ch >= 'a' && ch <= 'z') ? 1 : 0;
|
|
}
|
|
|
|
static int dg_is_alpha(int ch) {
|
|
return (dg_is_upper(ch) != 0 || dg_is_lower(ch) != 0) ? 1 : 0;
|
|
}
|
|
|
|
static int dg_is_xdigit(int ch) {
|
|
return (dg_is_digit(ch) != 0 || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) ? 1 : 0;
|
|
}
|
|
|
|
static int dg_is_space_char(int ch) {
|
|
return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f') ? 1 : 0;
|
|
}
|
|
|
|
static int dg_to_lower_ascii(int ch) {
|
|
return (dg_is_upper(ch) != 0) ? (ch - 'A' + 'a') : ch;
|
|
}
|
|
|
|
static void dg_init_ctype_table(void) {
|
|
int c;
|
|
|
|
if (g_dg_ctype_ready != 0) {
|
|
return;
|
|
}
|
|
|
|
for (c = -128; c < 256; c++) {
|
|
unsigned short flags = 0U;
|
|
int ch = (c < 0) ? (c + 256) : c;
|
|
|
|
if (dg_is_upper(ch) != 0) {
|
|
flags |= _ISupper;
|
|
}
|
|
if (dg_is_lower(ch) != 0) {
|
|
flags |= _ISlower;
|
|
}
|
|
if (dg_is_alpha(ch) != 0) {
|
|
flags |= _ISalpha;
|
|
}
|
|
if (dg_is_digit(ch) != 0) {
|
|
flags |= _ISdigit;
|
|
}
|
|
if (dg_is_xdigit(ch) != 0) {
|
|
flags |= _ISxdigit;
|
|
}
|
|
if (dg_is_space_char(ch) != 0) {
|
|
flags |= _ISspace;
|
|
}
|
|
if (ch == ' ' || ch == '\t') {
|
|
flags |= _ISblank;
|
|
}
|
|
if (ch < 32 || ch == 127) {
|
|
flags |= _IScntrl;
|
|
}
|
|
if (ch >= 32 && ch <= 126) {
|
|
flags |= _ISprint;
|
|
}
|
|
if (ch >= 33 && ch <= 126) {
|
|
flags |= _ISgraph;
|
|
}
|
|
if ((flags & (_ISalpha | _ISdigit)) != 0U) {
|
|
flags |= _ISalnum;
|
|
}
|
|
if ((flags & (_ISgraph | _ISalnum)) != 0U && (flags & _ISalnum) == 0U) {
|
|
flags |= _ISpunct;
|
|
}
|
|
|
|
g_dg_ctype_table[c + 128] = flags;
|
|
}
|
|
|
|
g_dg_ctype_ready = 1;
|
|
}
|
|
|
|
const unsigned short int **__ctype_b_loc(void) {
|
|
static const unsigned short int *table_ptr = (const unsigned short int *)(void *)(g_dg_ctype_table + 128);
|
|
dg_init_ctype_table();
|
|
return &table_ptr;
|
|
}
|
|
|
|
int system(const char *command) {
|
|
(void)command;
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static size_t dg_strlen(const char *text) {
|
|
size_t len = 0U;
|
|
|
|
if (text == (const char *)0) {
|
|
return 0U;
|
|
}
|
|
|
|
while (text[len] != '\0') {
|
|
len++;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static void dg_copy(char *dst, size_t dst_size, const char *src) {
|
|
size_t i = 0U;
|
|
|
|
if (dst == (char *)0 || dst_size == 0U) {
|
|
return;
|
|
}
|
|
|
|
if (src == (const char *)0) {
|
|
dst[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
while (src[i] != '\0' && i + 1U < dst_size) {
|
|
dst[i] = src[i];
|
|
i++;
|
|
}
|
|
|
|
dst[i] = '\0';
|
|
}
|
|
|
|
static int dg_starts_with(const char *text, const char *prefix) {
|
|
size_t i = 0U;
|
|
|
|
if (text == (const char *)0 || prefix == (const char *)0) {
|
|
return 0;
|
|
}
|
|
|
|
while (prefix[i] != '\0') {
|
|
if (text[i] != prefix[i]) {
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int dg_path_exists(const char *path) {
|
|
return (cleonos_sys_fs_stat_type(path) == (u64)-1) ? 0 : 1;
|
|
}
|
|
|
|
static void dg_join_path(char *out_path, size_t out_size, const char *dir, const char *name) {
|
|
size_t dlen;
|
|
size_t i = 0U;
|
|
|
|
if (out_path == (char *)0 || out_size == 0U) {
|
|
return;
|
|
}
|
|
|
|
out_path[0] = '\0';
|
|
|
|
if (dir == (const char *)0 || name == (const char *)0) {
|
|
return;
|
|
}
|
|
|
|
dlen = dg_strlen(dir);
|
|
|
|
while (i < dlen && i + 1U < out_size) {
|
|
out_path[i] = dir[i];
|
|
i++;
|
|
}
|
|
|
|
if (i > 0U && out_path[i - 1U] != '/' && i + 1U < out_size) {
|
|
out_path[i++] = '/';
|
|
}
|
|
|
|
{
|
|
size_t j = 0U;
|
|
while (name[j] != '\0' && i + 1U < out_size) {
|
|
out_path[i++] = name[j++];
|
|
}
|
|
}
|
|
|
|
out_path[i] = '\0';
|
|
}
|
|
|
|
static void dg_resolve_read_path(const char *path, char *out_path, size_t out_size) {
|
|
char candidate[DG_PATH_MAX];
|
|
|
|
if (path == (const char *)0 || out_path == (char *)0 || out_size == 0U) {
|
|
return;
|
|
}
|
|
|
|
if (path[0] == '/') {
|
|
dg_copy(out_path, out_size, path);
|
|
return;
|
|
}
|
|
|
|
dg_join_path(candidate, sizeof(candidate), "/temp", path);
|
|
if (dg_path_exists(candidate) != 0) {
|
|
dg_copy(out_path, out_size, candidate);
|
|
return;
|
|
}
|
|
|
|
dg_join_path(candidate, sizeof(candidate), "/shell", path);
|
|
if (dg_path_exists(candidate) != 0) {
|
|
dg_copy(out_path, out_size, candidate);
|
|
return;
|
|
}
|
|
|
|
dg_join_path(candidate, sizeof(candidate), "/", path);
|
|
dg_copy(out_path, out_size, candidate);
|
|
}
|
|
|
|
static void dg_resolve_write_path(const char *path, char *out_path, size_t out_size) {
|
|
if (path == (const char *)0 || out_path == (char *)0 || out_size == 0U) {
|
|
return;
|
|
}
|
|
|
|
if (path[0] == '/') {
|
|
dg_copy(out_path, out_size, path);
|
|
return;
|
|
}
|
|
|
|
dg_join_path(out_path, out_size, "/temp", path);
|
|
}
|
|
|
|
static int dg_is_temp_path(const char *path) {
|
|
return dg_starts_with(path, "/temp");
|
|
}
|
|
|
|
static int dg_fd_slot_from_fd(int fd) {
|
|
int slot = fd - 3;
|
|
|
|
if (slot < 0 || slot >= DG_MAX_MEM_FD) {
|
|
return -1;
|
|
}
|
|
|
|
if (g_dg_fds[slot].used == 0) {
|
|
return -1;
|
|
}
|
|
|
|
return slot;
|
|
}
|
|
|
|
static int dg_fd_alloc_slot(void) {
|
|
int i;
|
|
|
|
for (i = 0; i < DG_MAX_MEM_FD; i++) {
|
|
if (g_dg_fds[i].used == 0) {
|
|
g_dg_fds[i].used = 1;
|
|
g_dg_fds[i].writable = 0;
|
|
g_dg_fds[i].dirty = 0;
|
|
g_dg_fds[i].pos = 0U;
|
|
g_dg_fds[i].size = 0U;
|
|
g_dg_fds[i].cap = 0U;
|
|
g_dg_fds[i].data = (unsigned char *)0;
|
|
g_dg_fds[i].path[0] = '\0';
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int dg_fd_ensure_cap(struct dg_mem_fd *file, size_t need) {
|
|
unsigned char *new_buf;
|
|
size_t new_cap;
|
|
|
|
if (file == (struct dg_mem_fd *)0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (need <= file->cap) {
|
|
return 0;
|
|
}
|
|
|
|
new_cap = (file->cap == 0U) ? 256U : file->cap;
|
|
while (new_cap < need) {
|
|
if (new_cap > ((size_t)-1) / 2U) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
new_cap *= 2U;
|
|
}
|
|
|
|
new_buf = (unsigned char *)realloc(file->data, new_cap);
|
|
if (new_buf == (unsigned char *)0) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
file->data = new_buf;
|
|
file->cap = new_cap;
|
|
return 0;
|
|
}
|
|
|
|
static int dg_fd_flush_slot(struct dg_mem_fd *file) {
|
|
u64 wrote;
|
|
|
|
if (file == (struct dg_mem_fd *)0 || file->used == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (file->writable == 0 || file->dirty == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (dg_is_temp_path(file->path) == 0) {
|
|
errno = EACCES;
|
|
return -1;
|
|
}
|
|
|
|
wrote = cleonos_sys_fs_write(file->path, (const char *)file->data, (u64)file->size);
|
|
if (wrote != (u64)file->size) {
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
file->dirty = 0;
|
|
return 0;
|
|
}
|
|
|
|
static void dg_serial_log_bytes(const void *buffer, size_t size) {
|
|
const unsigned char *src = (const unsigned char *)buffer;
|
|
static const char prefix[] = DG_SERIAL_LOG_PREFIX;
|
|
char line[DG_SERIAL_LOG_CHUNK + sizeof(prefix) + 1U];
|
|
size_t prefix_len = sizeof(prefix) - 1U;
|
|
size_t offset = 0U;
|
|
int first = 1;
|
|
|
|
if (src == (const unsigned char *)0 || size == 0U) {
|
|
return;
|
|
}
|
|
|
|
while (offset < size) {
|
|
size_t room = DG_SERIAL_LOG_CHUNK;
|
|
size_t used = 0U;
|
|
|
|
if (first != 0 && prefix_len < sizeof(line)) {
|
|
memcpy(line, prefix, prefix_len);
|
|
used = prefix_len;
|
|
first = 0;
|
|
}
|
|
|
|
while (offset < size && room > 0U) {
|
|
unsigned char ch = src[offset++];
|
|
|
|
if (ch == '\0') {
|
|
ch = ' ';
|
|
} else if (ch < 32U && ch != '\n' && ch != '\r' && ch != '\t') {
|
|
ch = ' ';
|
|
}
|
|
|
|
line[used++] = (char)ch;
|
|
room--;
|
|
}
|
|
|
|
if (used > 0U) {
|
|
(void)cleonos_sys_log_write(line, (u64)used);
|
|
}
|
|
}
|
|
}
|
|
|
|
void *malloc(size_t size) {
|
|
struct dg_alloc_hdr *cur;
|
|
size_t aligned;
|
|
|
|
if (size == 0U) {
|
|
size = 1U;
|
|
}
|
|
|
|
aligned = (size + 15U) & ~(size_t)15U;
|
|
|
|
if (g_dg_heap_ready == 0) {
|
|
if (DG_HEAP_SIZE <= sizeof(struct dg_alloc_hdr) + 16U) {
|
|
errno = ENOMEM;
|
|
return (void *)0;
|
|
}
|
|
|
|
g_dg_heap_head = (struct dg_alloc_hdr *)(void *)g_dg_heap;
|
|
g_dg_heap_head->size = DG_HEAP_SIZE - sizeof(struct dg_alloc_hdr);
|
|
g_dg_heap_head->free = 1;
|
|
g_dg_heap_head->next = (struct dg_alloc_hdr *)0;
|
|
g_dg_heap_head->prev = (struct dg_alloc_hdr *)0;
|
|
g_dg_heap_ready = 1;
|
|
}
|
|
|
|
cur = g_dg_heap_head;
|
|
|
|
while (cur != (struct dg_alloc_hdr *)0) {
|
|
if (cur->free != 0 && cur->size >= aligned) {
|
|
size_t remain = cur->size - aligned;
|
|
|
|
if (remain > sizeof(struct dg_alloc_hdr) + 16U) {
|
|
struct dg_alloc_hdr *tail =
|
|
(struct dg_alloc_hdr *)(void *)((unsigned char *)(void *)(cur + 1) + aligned);
|
|
tail->size = remain - sizeof(struct dg_alloc_hdr);
|
|
tail->free = 1;
|
|
tail->next = cur->next;
|
|
tail->prev = cur;
|
|
if (cur->next != (struct dg_alloc_hdr *)0) {
|
|
cur->next->prev = tail;
|
|
}
|
|
cur->next = tail;
|
|
cur->size = aligned;
|
|
}
|
|
|
|
cur->free = 0;
|
|
return (void *)(cur + 1);
|
|
}
|
|
|
|
cur = cur->next;
|
|
}
|
|
|
|
errno = ENOMEM;
|
|
return (void *)0;
|
|
}
|
|
|
|
void free(void *ptr) {
|
|
struct dg_alloc_hdr *hdr;
|
|
|
|
if (ptr == (void *)0) {
|
|
return;
|
|
}
|
|
|
|
hdr = ((struct dg_alloc_hdr *)ptr) - 1;
|
|
hdr->free = 1;
|
|
|
|
if (hdr->next != (struct dg_alloc_hdr *)0 && hdr->next->free != 0) {
|
|
struct dg_alloc_hdr *next = hdr->next;
|
|
hdr->size += sizeof(struct dg_alloc_hdr) + next->size;
|
|
hdr->next = next->next;
|
|
if (next->next != (struct dg_alloc_hdr *)0) {
|
|
next->next->prev = hdr;
|
|
}
|
|
}
|
|
|
|
if (hdr->prev != (struct dg_alloc_hdr *)0 && hdr->prev->free != 0) {
|
|
struct dg_alloc_hdr *prev = hdr->prev;
|
|
prev->size += sizeof(struct dg_alloc_hdr) + hdr->size;
|
|
prev->next = hdr->next;
|
|
if (hdr->next != (struct dg_alloc_hdr *)0) {
|
|
hdr->next->prev = prev;
|
|
}
|
|
}
|
|
}
|
|
|
|
void *calloc(size_t nmemb, size_t size) {
|
|
size_t total;
|
|
void *ptr;
|
|
|
|
if (nmemb == 0U || size == 0U) {
|
|
total = 1U;
|
|
} else if (nmemb > ((size_t)-1) / size) {
|
|
errno = ENOMEM;
|
|
return (void *)0;
|
|
} else {
|
|
total = nmemb * size;
|
|
}
|
|
|
|
ptr = malloc(total);
|
|
if (ptr != (void *)0) {
|
|
memset(ptr, 0, total);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
void *realloc(void *ptr, size_t size) {
|
|
struct dg_alloc_hdr *old_hdr;
|
|
size_t old_size;
|
|
void *new_ptr;
|
|
|
|
if (ptr == (void *)0) {
|
|
return malloc(size);
|
|
}
|
|
|
|
if (size == 0U) {
|
|
free(ptr);
|
|
return (void *)0;
|
|
}
|
|
|
|
old_hdr = ((struct dg_alloc_hdr *)ptr) - 1;
|
|
old_size = old_hdr->size;
|
|
|
|
if (old_size >= size) {
|
|
return ptr;
|
|
}
|
|
|
|
if (old_hdr->next != (struct dg_alloc_hdr *)0 && old_hdr->next->free != 0 &&
|
|
old_size + sizeof(struct dg_alloc_hdr) + old_hdr->next->size >= size) {
|
|
struct dg_alloc_hdr *next = old_hdr->next;
|
|
old_hdr->size += sizeof(struct dg_alloc_hdr) + next->size;
|
|
old_hdr->next = next->next;
|
|
if (old_hdr->next != (struct dg_alloc_hdr *)0) {
|
|
old_hdr->next->prev = old_hdr;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
new_ptr = malloc(size);
|
|
if (new_ptr == (void *)0) {
|
|
return (void *)0;
|
|
}
|
|
|
|
memcpy(new_ptr, ptr, (old_size < size) ? old_size : size);
|
|
free(ptr);
|
|
return new_ptr;
|
|
}
|
|
|
|
double atof(const char *text) {
|
|
const char *p = text;
|
|
double value = 0.0;
|
|
double frac = 0.1;
|
|
int sign = 1;
|
|
|
|
if (p == (const char *)0) {
|
|
return 0.0;
|
|
}
|
|
|
|
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
|
|
p++;
|
|
}
|
|
|
|
if (*p == '-') {
|
|
sign = -1;
|
|
p++;
|
|
} else if (*p == '+') {
|
|
p++;
|
|
}
|
|
|
|
while (*p >= '0' && *p <= '9') {
|
|
value = (value * 10.0) + (double)(*p - '0');
|
|
p++;
|
|
}
|
|
|
|
if (*p == '.') {
|
|
p++;
|
|
while (*p >= '0' && *p <= '9') {
|
|
value += (double)(*p - '0') * frac;
|
|
frac *= 0.1;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
return (double)sign * value;
|
|
}
|
|
|
|
int strcasecmp(const char *left, const char *right) {
|
|
unsigned char a;
|
|
unsigned char b;
|
|
|
|
if (left == right) {
|
|
return 0;
|
|
}
|
|
|
|
while (*left != '\0' && *right != '\0') {
|
|
a = (unsigned char)dg_to_lower_ascii((unsigned char)*left);
|
|
b = (unsigned char)dg_to_lower_ascii((unsigned char)*right);
|
|
if (a != b) {
|
|
return (a < b) ? -1 : 1;
|
|
}
|
|
left++;
|
|
right++;
|
|
}
|
|
|
|
if (*left == *right) {
|
|
return 0;
|
|
}
|
|
|
|
return ((unsigned char)*left < (unsigned char)*right) ? -1 : 1;
|
|
}
|
|
|
|
int strncasecmp(const char *left, const char *right, size_t size) {
|
|
size_t i;
|
|
|
|
if (size == 0U || left == right) {
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0U; i < size; i++) {
|
|
unsigned char a = (unsigned char)dg_to_lower_ascii((unsigned char)left[i]);
|
|
unsigned char b = (unsigned char)dg_to_lower_ascii((unsigned char)right[i]);
|
|
|
|
if (a != b) {
|
|
return (a < b) ? -1 : 1;
|
|
}
|
|
|
|
if (left[i] == '\0' || right[i] == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *strdup(const char *text) {
|
|
size_t len;
|
|
char *out;
|
|
|
|
len = dg_strlen(text);
|
|
out = (char *)malloc(len + 1U);
|
|
if (out == (char *)0) {
|
|
return (char *)0;
|
|
}
|
|
|
|
memcpy(out, text, len + 1U);
|
|
return out;
|
|
}
|
|
|
|
char *getenv(const char *name) {
|
|
static char line[DG_ENV_LINE_MAX];
|
|
static char value[DG_ENV_LINE_MAX];
|
|
u64 envc;
|
|
u64 i;
|
|
size_t nlen;
|
|
|
|
if (name[0] == '\0') {
|
|
return (char *)0;
|
|
}
|
|
|
|
nlen = dg_strlen(name);
|
|
envc = cleonos_sys_proc_envc();
|
|
|
|
for (i = 0ULL; i < envc; i++) {
|
|
if (cleonos_sys_proc_env(i, line, (u64)sizeof(line)) == 0ULL) {
|
|
continue;
|
|
}
|
|
|
|
if (strncmp(line, name, nlen) == 0 && line[nlen] == '=') {
|
|
dg_copy(value, sizeof(value), line + nlen + 1U);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
return (char *)0;
|
|
}
|
|
|
|
time_t time(time_t *out_time) {
|
|
time_t value = (time_t)(cleonos_sys_timer_ticks() / 100ULL);
|
|
|
|
if (out_time != (time_t *)0) {
|
|
*out_time = value;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
int access(const char *path, int mode) {
|
|
char resolved[DG_PATH_MAX];
|
|
(void)mode;
|
|
|
|
dg_resolve_read_path(path, resolved, sizeof(resolved));
|
|
if (dg_path_exists(resolved) == 0) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mkdir(const char *path, mode_t mode) {
|
|
char resolved[DG_PATH_MAX];
|
|
(void)mode;
|
|
|
|
if (path == (const char *)0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
dg_resolve_write_path(path, resolved, sizeof(resolved));
|
|
if (dg_is_temp_path(resolved) == 0) {
|
|
errno = EACCES;
|
|
return -1;
|
|
}
|
|
|
|
if (cleonos_sys_fs_mkdir(resolved) == 0ULL) {
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int unlink(const char *path) {
|
|
char resolved[DG_PATH_MAX];
|
|
|
|
dg_resolve_write_path(path, resolved, sizeof(resolved));
|
|
if (dg_is_temp_path(resolved) == 0) {
|
|
errno = EACCES;
|
|
return -1;
|
|
}
|
|
|
|
if (cleonos_sys_fs_remove(resolved) == 0ULL) {
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int remove(const char *path) {
|
|
return unlink(path);
|
|
}
|
|
|
|
int rename(const char *old_path, const char *new_path) {
|
|
char src[DG_PATH_MAX];
|
|
char dst[DG_PATH_MAX];
|
|
u64 file_size;
|
|
char *buf;
|
|
u64 read_len;
|
|
int rc = -1;
|
|
|
|
if (old_path == (const char *)0 || new_path == (const char *)0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
dg_resolve_read_path(old_path, src, sizeof(src));
|
|
dg_resolve_write_path(new_path, dst, sizeof(dst));
|
|
|
|
if (dg_is_temp_path(dst) == 0) {
|
|
errno = EACCES;
|
|
return -1;
|
|
}
|
|
|
|
file_size = cleonos_sys_fs_stat_size(src);
|
|
if (file_size == (u64)-1) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
buf = (char *)malloc((size_t)file_size + 1U);
|
|
if (buf == (char *)0) {
|
|
return -1;
|
|
}
|
|
|
|
read_len = cleonos_sys_fs_read(src, buf, file_size);
|
|
if (read_len != file_size) {
|
|
errno = EIO;
|
|
goto done;
|
|
}
|
|
|
|
if (cleonos_sys_fs_write(dst, buf, read_len) != read_len) {
|
|
errno = EIO;
|
|
goto done;
|
|
}
|
|
|
|
if (cleonos_sys_fs_remove(src) == 0ULL) {
|
|
errno = EIO;
|
|
goto done;
|
|
}
|
|
|
|
rc = 0;
|
|
done:
|
|
free(buf);
|
|
return rc;
|
|
}
|
|
|
|
int open(const char *path, int flags, ...) {
|
|
int slot;
|
|
int fd;
|
|
struct dg_mem_fd *file;
|
|
int writable;
|
|
char resolved[DG_PATH_MAX];
|
|
|
|
writable = ((flags & O_ACCMODE) != O_RDONLY) ? 1 : 0;
|
|
if (writable != 0) {
|
|
dg_resolve_write_path(path, resolved, sizeof(resolved));
|
|
if (dg_is_temp_path(resolved) == 0) {
|
|
errno = EACCES;
|
|
return -1;
|
|
}
|
|
} else {
|
|
dg_resolve_read_path(path, resolved, sizeof(resolved));
|
|
}
|
|
|
|
slot = dg_fd_alloc_slot();
|
|
if (slot < 0) {
|
|
errno = EMFILE;
|
|
return -1;
|
|
}
|
|
|
|
file = &g_dg_fds[slot];
|
|
file->writable = writable;
|
|
dg_copy(file->path, sizeof(file->path), resolved);
|
|
|
|
if (writable == 0 || (flags & O_APPEND) != 0) {
|
|
u64 size = cleonos_sys_fs_stat_size(resolved);
|
|
|
|
if (size == (u64)-1) {
|
|
if (writable == 0) {
|
|
file->used = 0;
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
} else if (size > 0ULL) {
|
|
if (dg_fd_ensure_cap(file, (size_t)size) != 0) {
|
|
free(file->data);
|
|
file->used = 0;
|
|
file->data = (unsigned char *)0;
|
|
file->cap = 0U;
|
|
return -1;
|
|
}
|
|
|
|
if (cleonos_sys_fs_read(resolved, (char *)file->data, size) != size) {
|
|
free(file->data);
|
|
file->used = 0;
|
|
file->data = (unsigned char *)0;
|
|
file->cap = 0U;
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
file->size = (size_t)size;
|
|
file->pos = ((flags & O_APPEND) != 0) ? file->size : 0U;
|
|
}
|
|
}
|
|
|
|
if ((flags & O_TRUNC) != 0) {
|
|
file->size = 0U;
|
|
file->pos = 0U;
|
|
file->dirty = 1;
|
|
}
|
|
|
|
fd = slot + 3;
|
|
return fd;
|
|
}
|
|
|
|
int close(int fd) {
|
|
int slot = dg_fd_slot_from_fd(fd);
|
|
struct dg_mem_fd *file;
|
|
|
|
if (fd >= 0 && fd <= 2) {
|
|
return 0;
|
|
}
|
|
|
|
if (slot < 0) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
file = &g_dg_fds[slot];
|
|
|
|
if (dg_fd_flush_slot(file) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
free(file->data);
|
|
file->used = 0;
|
|
file->writable = 0;
|
|
file->dirty = 0;
|
|
file->pos = 0U;
|
|
file->size = 0U;
|
|
file->cap = 0U;
|
|
file->data = (unsigned char *)0;
|
|
file->path[0] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
ssize_t read(int fd, void *out_buf, size_t size) {
|
|
int slot;
|
|
struct dg_mem_fd *file;
|
|
size_t left;
|
|
size_t n;
|
|
|
|
if (out_buf == (void *)0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (size == 0U) {
|
|
return 0;
|
|
}
|
|
|
|
if (fd == 0) {
|
|
u64 got = cleonos_sys_fd_read(0ULL, out_buf, (u64)size);
|
|
if (got == (u64)-1) {
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
return (ssize_t)got;
|
|
}
|
|
|
|
slot = dg_fd_slot_from_fd(fd);
|
|
if (slot < 0) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
file = &g_dg_fds[slot];
|
|
left = (file->pos < file->size) ? (file->size - file->pos) : 0U;
|
|
n = (size < left) ? size : left;
|
|
|
|
if (n > 0U) {
|
|
memcpy(out_buf, file->data + file->pos, n);
|
|
file->pos += n;
|
|
}
|
|
|
|
return (ssize_t)n;
|
|
}
|
|
|
|
ssize_t write(int fd, const void *buf, size_t size) {
|
|
int slot;
|
|
struct dg_mem_fd *file;
|
|
|
|
if (buf == (const void *)0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (size == 0U) {
|
|
return 0;
|
|
}
|
|
|
|
if (fd == 1 || fd == 2) {
|
|
u64 wrote = cleonos_sys_fd_write((u64)fd, buf, (u64)size);
|
|
if (wrote == (u64)-1) {
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
dg_serial_log_bytes(buf, (size_t)wrote);
|
|
return (ssize_t)wrote;
|
|
}
|
|
|
|
slot = dg_fd_slot_from_fd(fd);
|
|
if (slot < 0) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
file = &g_dg_fds[slot];
|
|
if (file->writable == 0) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
if (dg_fd_ensure_cap(file, file->pos + size) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
memcpy(file->data + file->pos, buf, size);
|
|
file->pos += size;
|
|
if (file->pos > file->size) {
|
|
file->size = file->pos;
|
|
}
|
|
file->dirty = 1;
|
|
|
|
return (ssize_t)size;
|
|
}
|
|
|
|
off_t lseek(int fd, off_t offset, int whence) {
|
|
int slot = dg_fd_slot_from_fd(fd);
|
|
struct dg_mem_fd *file;
|
|
long long base = 0LL;
|
|
long long next;
|
|
|
|
if (slot < 0) {
|
|
errno = EBADF;
|
|
return (off_t)-1;
|
|
}
|
|
|
|
file = &g_dg_fds[slot];
|
|
|
|
if (whence == SEEK_SET) {
|
|
base = 0LL;
|
|
} else if (whence == SEEK_CUR) {
|
|
base = (long long)file->pos;
|
|
} else if (whence == SEEK_END) {
|
|
base = (long long)file->size;
|
|
} else {
|
|
errno = EINVAL;
|
|
return (off_t)-1;
|
|
}
|
|
|
|
next = base + (long long)offset;
|
|
if (next < 0LL) {
|
|
errno = EINVAL;
|
|
return (off_t)-1;
|
|
}
|
|
|
|
file->pos = (size_t)next;
|
|
return (off_t)file->pos;
|
|
}
|
|
|
|
int isatty(int fd) {
|
|
return (fd >= 0 && fd <= 2) ? 1 : 0;
|
|
}
|
|
|
|
static struct dg_stream *dg_stream_ptr(FILE *stream) {
|
|
struct dg_stream *s = (struct dg_stream *)(void *)stream;
|
|
|
|
if (s == (struct dg_stream *)0 || s->magic != DG_STDIO_MAGIC) {
|
|
return (struct dg_stream *)0;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
FILE *dg_fopen(const char *path, const char *mode) {
|
|
int flags = O_RDONLY;
|
|
int fd;
|
|
struct dg_stream *stream;
|
|
|
|
if (path == (const char *)0 || mode == (const char *)0 || mode[0] == '\0') {
|
|
errno = EINVAL;
|
|
return (FILE *)0;
|
|
}
|
|
|
|
if (mode[0] == 'r') {
|
|
flags = O_RDONLY;
|
|
} else if (mode[0] == 'w') {
|
|
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
|
} else if (mode[0] == 'a') {
|
|
flags = O_WRONLY | O_CREAT | O_APPEND;
|
|
} else {
|
|
errno = EINVAL;
|
|
return (FILE *)0;
|
|
}
|
|
|
|
if (strchr(mode, '+') != (char *)0) {
|
|
flags &= ~(O_RDONLY | O_WRONLY);
|
|
flags |= O_RDWR;
|
|
}
|
|
|
|
fd = open(path, flags, 0644);
|
|
if (fd < 0) {
|
|
return (FILE *)0;
|
|
}
|
|
|
|
stream = (struct dg_stream *)malloc(sizeof(struct dg_stream));
|
|
if (stream == (struct dg_stream *)0) {
|
|
(void)close(fd);
|
|
return (FILE *)0;
|
|
}
|
|
|
|
stream->magic = DG_STDIO_MAGIC;
|
|
stream->fd = fd;
|
|
stream->eof_flag = 0;
|
|
stream->err_flag = 0;
|
|
return (FILE *)(void *)stream;
|
|
}
|
|
|
|
int dg_fclose(FILE *stream) {
|
|
struct dg_stream *s = dg_stream_ptr(stream);
|
|
int rc;
|
|
|
|
if (s == (struct dg_stream *)0) {
|
|
errno = EINVAL;
|
|
return EOF;
|
|
}
|
|
|
|
if (s == &g_dg_stdin_stream || s == &g_dg_stdout_stream || s == &g_dg_stderr_stream) {
|
|
return 0;
|
|
}
|
|
|
|
rc = close(s->fd);
|
|
free(s);
|
|
return (rc == 0) ? 0 : EOF;
|
|
}
|
|
|
|
size_t dg_fread(void *out_buf, size_t size, size_t nmemb, FILE *stream) {
|
|
struct dg_stream *s = dg_stream_ptr(stream);
|
|
size_t want;
|
|
ssize_t got;
|
|
|
|
if (s == (struct dg_stream *)0 || out_buf == (void *)0 || size == 0U || nmemb == 0U) {
|
|
return 0U;
|
|
}
|
|
|
|
if (nmemb > ((size_t)-1) / size) {
|
|
s->err_flag = 1;
|
|
errno = EINVAL;
|
|
return 0U;
|
|
}
|
|
|
|
want = size * nmemb;
|
|
got = read(s->fd, out_buf, want);
|
|
if (got < 0) {
|
|
s->err_flag = 1;
|
|
return 0U;
|
|
}
|
|
|
|
if ((size_t)got < want) {
|
|
s->eof_flag = 1;
|
|
}
|
|
|
|
return (size == 0U) ? 0U : ((size_t)got / size);
|
|
}
|
|
|
|
size_t dg_fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream) {
|
|
struct dg_stream *s = dg_stream_ptr(stream);
|
|
size_t want;
|
|
ssize_t wrote;
|
|
|
|
if (s == (struct dg_stream *)0 || buf == (const void *)0 || size == 0U || nmemb == 0U) {
|
|
return 0U;
|
|
}
|
|
|
|
if (nmemb > ((size_t)-1) / size) {
|
|
s->err_flag = 1;
|
|
errno = EINVAL;
|
|
return 0U;
|
|
}
|
|
|
|
want = size * nmemb;
|
|
wrote = write(s->fd, buf, want);
|
|
if (wrote < 0) {
|
|
s->err_flag = 1;
|
|
return 0U;
|
|
}
|
|
|
|
return (size_t)wrote / size;
|
|
}
|
|
|
|
int dg_fseek(FILE *stream, long offset, int whence) {
|
|
struct dg_stream *s = dg_stream_ptr(stream);
|
|
|
|
if (s == (struct dg_stream *)0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (lseek(s->fd, (off_t)offset, whence) < 0) {
|
|
s->err_flag = 1;
|
|
return -1;
|
|
}
|
|
|
|
s->eof_flag = 0;
|
|
return 0;
|
|
}
|
|
|
|
long dg_ftell(FILE *stream) {
|
|
struct dg_stream *s = dg_stream_ptr(stream);
|
|
off_t pos;
|
|
|
|
if (s == (struct dg_stream *)0) {
|
|
errno = EINVAL;
|
|
return -1L;
|
|
}
|
|
|
|
pos = lseek(s->fd, 0, SEEK_CUR);
|
|
if (pos < (off_t)0) {
|
|
s->err_flag = 1;
|
|
return -1L;
|
|
}
|
|
|
|
return (long)pos;
|
|
}
|
|
|
|
int dg_fflush(FILE *stream) {
|
|
struct dg_stream *s;
|
|
int slot;
|
|
|
|
if (stream == (FILE *)0) {
|
|
return 0;
|
|
}
|
|
|
|
s = dg_stream_ptr(stream);
|
|
if (s == (struct dg_stream *)0) {
|
|
errno = EINVAL;
|
|
return EOF;
|
|
}
|
|
|
|
slot = dg_fd_slot_from_fd(s->fd);
|
|
if (slot >= 0) {
|
|
if (dg_fd_flush_slot(&g_dg_fds[slot]) != 0) {
|
|
s->err_flag = 1;
|
|
return EOF;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *dg_fgets(char *out_text, int size, FILE *stream) {
|
|
struct dg_stream *s = dg_stream_ptr(stream);
|
|
int i = 0;
|
|
char ch = '\0';
|
|
|
|
if (s == (struct dg_stream *)0 || out_text == (char *)0 || size <= 1) {
|
|
return (char *)0;
|
|
}
|
|
|
|
while (i + 1 < size) {
|
|
ssize_t got = read(s->fd, &ch, 1U);
|
|
|
|
if (got <= 0) {
|
|
if (got < 0) {
|
|
s->err_flag = 1;
|
|
} else {
|
|
s->eof_flag = 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
out_text[i++] = ch;
|
|
if (ch == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == 0) {
|
|
return (char *)0;
|
|
}
|
|
|
|
out_text[i] = '\0';
|
|
return out_text;
|
|
}
|
|
|
|
int dg_feof(FILE *stream) {
|
|
struct dg_stream *s = dg_stream_ptr(stream);
|
|
return (s == (struct dg_stream *)0) ? 1 : s->eof_flag;
|
|
}
|
|
|
|
int dg_fileno(FILE *stream) {
|
|
struct dg_stream *s = dg_stream_ptr(stream);
|
|
|
|
if (s == (struct dg_stream *)0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
return s->fd;
|
|
}
|
|
|
|
int dg_vfprintf(FILE *stream, const char *fmt, va_list args) {
|
|
struct dg_stream *s = dg_stream_ptr(stream);
|
|
int len;
|
|
char local[512];
|
|
va_list args_copy;
|
|
|
|
if (s == (struct dg_stream *)0 || fmt == (const char *)0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
va_copy(args_copy, args);
|
|
len = vsnprintf(local, sizeof(local), fmt, args_copy);
|
|
va_end(args_copy);
|
|
if (len < 0) {
|
|
s->err_flag = 1;
|
|
return -1;
|
|
}
|
|
|
|
if ((size_t)len < sizeof(local)) {
|
|
if (write(s->fd, local, (size_t)len) < 0) {
|
|
s->err_flag = 1;
|
|
return -1;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
{
|
|
size_t cap = (size_t)len + 1U;
|
|
char *dyn = (char *)malloc(cap);
|
|
int written_len;
|
|
|
|
if (dyn == (char *)0) {
|
|
s->err_flag = 1;
|
|
return -1;
|
|
}
|
|
|
|
va_copy(args_copy, args);
|
|
written_len = vsnprintf(dyn, cap, fmt, args_copy);
|
|
va_end(args_copy);
|
|
|
|
if (written_len < 0 || write(s->fd, dyn, (size_t)written_len) < 0) {
|
|
s->err_flag = 1;
|
|
return -1;
|
|
}
|
|
|
|
return written_len;
|
|
}
|
|
}
|
|
|
|
int dg_fprintf(FILE *stream, const char *fmt, ...) {
|
|
va_list args;
|
|
int rc;
|
|
|
|
va_start(args, fmt);
|
|
rc = dg_vfprintf(stream, fmt, args);
|
|
va_end(args);
|
|
return rc;
|
|
}
|
|
|
|
void dg_perror(const char *text) {
|
|
if (text != (const char *)0 && text[0] != '\0') {
|
|
(void)dg_fprintf(stderr, "%s: errno=%d\n", text, errno);
|
|
} else {
|
|
(void)dg_fprintf(stderr, "errno=%d\n", errno);
|
|
}
|
|
}
|
|
|
|
int dg_sscanf(const char *text, const char *fmt, ...) {
|
|
const char *p = text;
|
|
const char *f = fmt;
|
|
int assigned = 0;
|
|
va_list args;
|
|
|
|
if (text == (const char *)0 || fmt == (const char *)0) {
|
|
return 0;
|
|
}
|
|
|
|
va_start(args, fmt);
|
|
|
|
while (*f != '\0') {
|
|
if (*f != '%') {
|
|
if (dg_is_space_char((unsigned char)*f) != 0) {
|
|
while (dg_is_space_char((unsigned char)*p) != 0) {
|
|
p++;
|
|
}
|
|
f++;
|
|
continue;
|
|
}
|
|
|
|
if (*p != *f) {
|
|
break;
|
|
}
|
|
|
|
p++;
|
|
f++;
|
|
continue;
|
|
}
|
|
|
|
f++;
|
|
if (*f == '\0') {
|
|
break;
|
|
}
|
|
|
|
while (dg_is_space_char((unsigned char)*p) != 0) {
|
|
p++;
|
|
}
|
|
|
|
if (*f == 'd' || *f == 'i') {
|
|
int *out_i = va_arg(args, int *);
|
|
char *endp = (char *)p;
|
|
long v = strtol(p, &endp, (*f == 'i') ? 0 : 10);
|
|
if (endp == p) {
|
|
break;
|
|
}
|
|
if (out_i != (int *)0) {
|
|
*out_i = (int)v;
|
|
}
|
|
p = endp;
|
|
assigned++;
|
|
} else if (*f == 'u' || *f == 'x' || *f == 'o') {
|
|
unsigned int *out_u = va_arg(args, unsigned int *);
|
|
int base = (*f == 'x') ? 16 : ((*f == 'o') ? 8 : 10);
|
|
char *endp = (char *)p;
|
|
unsigned long v = strtoul(p, &endp, base);
|
|
if (endp == p) {
|
|
break;
|
|
}
|
|
if (out_u != (unsigned int *)0) {
|
|
*out_u = (unsigned int)v;
|
|
}
|
|
p = endp;
|
|
assigned++;
|
|
} else if (*f == 'f') {
|
|
float *out_f = va_arg(args, float *);
|
|
char *endp = (char *)p;
|
|
double v = atof(p);
|
|
while (*endp == '+' || *endp == '-' || *endp == '.' || (*endp >= '0' && *endp <= '9')) {
|
|
endp++;
|
|
}
|
|
if (endp == p) {
|
|
break;
|
|
}
|
|
if (out_f != (float *)0) {
|
|
*out_f = (float)v;
|
|
}
|
|
p = endp;
|
|
assigned++;
|
|
} else if (*f == 's') {
|
|
char *out_s = va_arg(args, char *);
|
|
int copied = 0;
|
|
if (out_s == (char *)0) {
|
|
break;
|
|
}
|
|
while (*p != '\0' && dg_is_space_char((unsigned char)*p) == 0) {
|
|
out_s[copied++] = *p++;
|
|
}
|
|
if (copied == 0) {
|
|
break;
|
|
}
|
|
out_s[copied] = '\0';
|
|
assigned++;
|
|
} else if (*f == 'c') {
|
|
char *out_c = va_arg(args, char *);
|
|
if (*p == '\0' || out_c == (char *)0) {
|
|
break;
|
|
}
|
|
*out_c = *p++;
|
|
assigned++;
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
f++;
|
|
}
|
|
|
|
va_end(args);
|
|
return assigned;
|
|
}
|