重写Shell+增加更多Syscall

This commit is contained in:
2026-04-12 22:04:25 +08:00
parent 8f10d6a16c
commit 465cc40736
16 changed files with 2537 additions and 550 deletions

View File

@@ -0,0 +1,800 @@
#include "shell_internal.h"
#define USH_DMESG_DEFAULT 64ULL
#define USH_DMESG_LINE_MAX 256ULL
#define USH_COPY_MAX 65536U
static int ush_path_is_under_temp(const char *path) {
if (path == (const char *)0) {
return 0;
}
if (path[0] != '/' || path[1] != 't' || path[2] != 'e' || path[3] != 'm' || path[4] != 'p') {
return 0;
}
return (path[5] == '\0' || path[5] == '/') ? 1 : 0;
}
static int ush_split_first_and_rest(const char *arg, char *out_first, u64 out_first_size, const char **out_rest) {
u64 i = 0ULL;
u64 p = 0ULL;
if (arg == (const char *)0 || out_first == (char *)0 || out_first_size == 0ULL || out_rest == (const char **)0) {
return 0;
}
out_first[0] = '\0';
*out_rest = "";
while (arg[i] != '\0' && ush_is_space(arg[i]) != 0) {
i++;
}
if (arg[i] == '\0') {
return 0;
}
while (arg[i] != '\0' && ush_is_space(arg[i]) == 0) {
if (p + 1ULL < out_first_size) {
out_first[p++] = arg[i];
}
i++;
}
out_first[p] = '\0';
while (arg[i] != '\0' && ush_is_space(arg[i]) != 0) {
i++;
}
*out_rest = &arg[i];
return 1;
}
static int ush_split_two_args(const char *arg,
char *out_first,
u64 out_first_size,
char *out_second,
u64 out_second_size) {
u64 i = 0ULL;
u64 p = 0ULL;
if (arg == (const char *)0 ||
out_first == (char *)0 || out_first_size == 0ULL ||
out_second == (char *)0 || out_second_size == 0ULL) {
return 0;
}
out_first[0] = '\0';
out_second[0] = '\0';
while (arg[i] != '\0' && ush_is_space(arg[i]) != 0) {
i++;
}
if (arg[i] == '\0') {
return 0;
}
while (arg[i] != '\0' && ush_is_space(arg[i]) == 0) {
if (p + 1ULL < out_first_size) {
out_first[p++] = arg[i];
}
i++;
}
out_first[p] = '\0';
while (arg[i] != '\0' && ush_is_space(arg[i]) != 0) {
i++;
}
if (arg[i] == '\0') {
return 0;
}
p = 0ULL;
while (arg[i] != '\0' && ush_is_space(arg[i]) == 0) {
if (p + 1ULL < out_second_size) {
out_second[p++] = arg[i];
}
i++;
}
out_second[p] = '\0';
return (out_first[0] != '\0' && out_second[0] != '\0') ? 1 : 0;
}
static int ush_cmd_help(void) {
ush_writeln("commands:");
ush_writeln(" help");
ush_writeln(" ls [dir]");
ush_writeln(" cat <file>");
ush_writeln(" pwd");
ush_writeln(" cd [dir]");
ush_writeln(" exec|run <path|name>");
ush_writeln(" clear");
ush_writeln(" memstat / fsstat / taskstat / userstat / shstat / stats");
ush_writeln(" tty [index]");
ush_writeln(" dmesg [n]");
ush_writeln(" kbdstat");
ush_writeln(" mkdir <dir> (/temp only)");
ush_writeln(" touch <file> (/temp only)");
ush_writeln(" write <file> <text> (/temp only)");
ush_writeln(" append <file> <text> (/temp only)");
ush_writeln(" cp <src> <dst> (dst /temp only)");
ush_writeln(" mv <src> <dst> (/temp only)");
ush_writeln(" rm <path> (/temp only)");
ush_writeln(" rusttest / panic / elfloader (kernel shell only)");
ush_writeln("edit keys: Left/Right, Home/End, Up/Down history");
return 1;
}
static int ush_cmd_ls(const ush_state *sh, const char *arg) {
const char *target = arg;
char path[USH_PATH_MAX];
u64 count;
u64 i;
if (target == (const char *)0 || target[0] == '\0') {
target = ".";
}
if (ush_resolve_path(sh, target, path, (u64)sizeof(path)) == 0) {
ush_writeln("ls: invalid path");
return 0;
}
if (cleonos_sys_fs_stat_type(path) != 2ULL) {
ush_writeln("ls: directory not found");
return 0;
}
count = cleonos_sys_fs_child_count(path);
if (count == 0ULL) {
ush_writeln("(empty)");
return 1;
}
for (i = 0ULL; i < count; i++) {
char name[CLEONOS_FS_NAME_MAX];
name[0] = '\0';
if (cleonos_sys_fs_get_child_name(path, i, name) != 0ULL) {
ush_writeln(name);
}
}
return 1;
}
static int ush_cmd_cat(const ush_state *sh, const char *arg) {
char path[USH_PATH_MAX];
char buf[USH_CAT_MAX + 1ULL];
u64 size;
u64 req;
u64 got;
if (arg == (const char *)0 || arg[0] == '\0') {
ush_writeln("cat: file path required");
return 0;
}
if (ush_resolve_path(sh, arg, path, (u64)sizeof(path)) == 0) {
ush_writeln("cat: invalid path");
return 0;
}
if (cleonos_sys_fs_stat_type(path) != 1ULL) {
ush_writeln("cat: file not found");
return 0;
}
size = cleonos_sys_fs_stat_size(path);
if (size == (u64)-1) {
ush_writeln("cat: failed to stat file");
return 0;
}
if (size == 0ULL) {
return 1;
}
req = (size < USH_CAT_MAX) ? size : USH_CAT_MAX;
got = cleonos_sys_fs_read(path, buf, req);
if (got == 0ULL) {
ush_writeln("cat: read failed");
return 0;
}
if (got > USH_CAT_MAX) {
got = USH_CAT_MAX;
}
buf[got] = '\0';
ush_writeln(buf);
if (size > got) {
ush_writeln("[cat] output truncated");
}
return 1;
}
static int ush_cmd_pwd(const ush_state *sh) {
ush_writeln(sh->cwd);
return 1;
}
static int ush_cmd_cd(ush_state *sh, const char *arg) {
const char *target = arg;
char path[USH_PATH_MAX];
if (target == (const char *)0 || target[0] == '\0') {
target = "/";
}
if (ush_resolve_path(sh, target, path, (u64)sizeof(path)) == 0) {
ush_writeln("cd: invalid path");
return 0;
}
if (cleonos_sys_fs_stat_type(path) != 2ULL) {
ush_writeln("cd: directory not found");
return 0;
}
ush_copy(sh->cwd, (u64)sizeof(sh->cwd), path);
return 1;
}
static int ush_cmd_exec(const ush_state *sh, const char *arg) {
char path[USH_PATH_MAX];
u64 status;
if (ush_resolve_exec_path(sh, arg, path, (u64)sizeof(path)) == 0) {
ush_writeln("exec: invalid target");
return 0;
}
if (ush_path_is_under_system(path) != 0) {
ush_writeln("exec: /system/*.elf is kernel-mode (KELF), not user-exec");
return 0;
}
status = cleonos_sys_exec_path(path);
if (status == (u64)-1) {
ush_writeln("exec: request failed");
return 0;
}
if (status == 0ULL) {
ush_writeln("exec: request accepted");
return 1;
}
ush_writeln("exec: returned non-zero status");
return 0;
}
static int ush_cmd_clear(void) {
u64 i;
for (i = 0ULL; i < USH_CLEAR_LINES; i++) {
ush_write_char('\n');
}
return 1;
}
static int ush_cmd_kbdstat(void) {
ush_writeln("kbdstat:");
ush_print_kv_hex(" BUFFERED", cleonos_sys_kbd_buffered());
ush_print_kv_hex(" PUSHED", cleonos_sys_kbd_pushed());
ush_print_kv_hex(" POPPED", cleonos_sys_kbd_popped());
ush_print_kv_hex(" DROPPED", cleonos_sys_kbd_dropped());
ush_print_kv_hex(" HOTKEY_SWITCHES", cleonos_sys_kbd_hotkey_switches());
return 1;
}
static int ush_cmd_memstat(void) {
ush_writeln("memstat (user ABI limited):");
ush_print_kv_hex(" SERVICE_COUNT", cleonos_sys_service_count());
ush_print_kv_hex(" SERVICE_READY_COUNT", cleonos_sys_service_ready_count());
ush_print_kv_hex(" KELF_COUNT", cleonos_sys_kelf_count());
ush_print_kv_hex(" KELF_RUNS", cleonos_sys_kelf_runs());
return 1;
}
static int ush_cmd_fsstat(void) {
ush_writeln("fsstat:");
ush_print_kv_hex(" NODE_COUNT", cleonos_sys_fs_node_count());
ush_print_kv_hex(" ROOT_CHILDREN", cleonos_sys_fs_child_count("/"));
ush_print_kv_hex(" SYSTEM_CHILDREN", cleonos_sys_fs_child_count("/system"));
ush_print_kv_hex(" SHELL_CHILDREN", cleonos_sys_fs_child_count("/shell"));
ush_print_kv_hex(" TEMP_CHILDREN", cleonos_sys_fs_child_count("/temp"));
ush_print_kv_hex(" DRIVER_CHILDREN", cleonos_sys_fs_child_count("/driver"));
return 1;
}
static int ush_cmd_taskstat(void) {
ush_writeln("taskstat:");
ush_print_kv_hex(" TASK_COUNT", cleonos_sys_task_count());
ush_print_kv_hex(" CURRENT_TASK", cleonos_syscall(CLEONOS_SYSCALL_CUR_TASK, 0ULL, 0ULL, 0ULL));
ush_print_kv_hex(" TIMER_TICKS", cleonos_sys_timer_ticks());
ush_print_kv_hex(" CONTEXT_SWITCHES", cleonos_sys_context_switches());
return 1;
}
static int ush_cmd_userstat(void) {
ush_writeln("userstat:");
ush_print_kv_hex(" USER_SHELL_READY", cleonos_sys_user_shell_ready());
ush_print_kv_hex(" USER_EXEC_REQUESTED", cleonos_sys_user_exec_requested());
ush_print_kv_hex(" USER_LAUNCH_TRIES", cleonos_sys_user_launch_tries());
ush_print_kv_hex(" USER_LAUNCH_OK", cleonos_sys_user_launch_ok());
ush_print_kv_hex(" USER_LAUNCH_FAIL", cleonos_sys_user_launch_fail());
ush_print_kv_hex(" EXEC_REQUESTS", cleonos_sys_exec_request_count());
ush_print_kv_hex(" EXEC_SUCCESS", cleonos_sys_exec_success_count());
ush_print_kv_hex(" TTY_COUNT", cleonos_sys_tty_count());
ush_print_kv_hex(" TTY_ACTIVE", cleonos_sys_tty_active());
return 1;
}
static int ush_cmd_shstat(const ush_state *sh) {
ush_writeln("shstat:");
ush_print_kv_hex(" CMD_TOTAL", sh->cmd_total);
ush_print_kv_hex(" CMD_OK", sh->cmd_ok);
ush_print_kv_hex(" CMD_FAIL", sh->cmd_fail);
ush_print_kv_hex(" CMD_UNKNOWN", sh->cmd_unknown);
return 1;
}
static int ush_cmd_tty(const char *arg) {
u64 tty_count = cleonos_sys_tty_count();
u64 active = cleonos_sys_tty_active();
if (arg == (const char *)0 || arg[0] == '\0') {
ush_print_kv_hex("TTY_COUNT", tty_count);
ush_print_kv_hex("TTY_ACTIVE", active);
return 1;
}
{
u64 idx;
if (ush_parse_u64_dec(arg, &idx) == 0) {
ush_writeln("tty: usage tty [index]");
return 0;
}
if (idx >= tty_count) {
ush_writeln("tty: index out of range");
return 0;
}
if (cleonos_sys_tty_switch(idx) == (u64)-1) {
ush_writeln("tty: switch failed");
return 0;
}
ush_writeln("tty: switched");
ush_print_kv_hex("TTY_ACTIVE", cleonos_sys_tty_active());
return 1;
}
}
static int ush_cmd_dmesg(const char *arg) {
u64 total = cleonos_sys_log_journal_count();
u64 limit = USH_DMESG_DEFAULT;
u64 start;
u64 i;
if (arg != (const char *)0 && arg[0] != '\0') {
if (ush_parse_u64_dec(arg, &limit) == 0 || limit == 0ULL) {
ush_writeln("dmesg: usage dmesg [positive_count]");
return 0;
}
}
if (total == 0ULL) {
ush_writeln("(journal empty)");
return 1;
}
if (limit > total) {
limit = total;
}
start = total - limit;
for (i = start; i < total; i++) {
char line[USH_DMESG_LINE_MAX];
if (cleonos_sys_log_journal_read(i, line, (u64)sizeof(line)) != 0ULL) {
ush_writeln(line);
}
}
return 1;
}
static int ush_cmd_mkdir(const ush_state *sh, const char *arg) {
char path[USH_PATH_MAX];
if (arg == (const char *)0 || arg[0] == '\0') {
ush_writeln("mkdir: directory path required");
return 0;
}
if (ush_resolve_path(sh, arg, path, (u64)sizeof(path)) == 0) {
ush_writeln("mkdir: invalid path");
return 0;
}
if (ush_path_is_under_temp(path) == 0) {
ush_writeln("mkdir: target must be under /temp");
return 0;
}
if (cleonos_sys_fs_mkdir(path) == 0ULL) {
ush_writeln("mkdir: failed");
return 0;
}
return 1;
}
static int ush_cmd_touch(const ush_state *sh, const char *arg) {
static const char empty_data[1] = {'\0'};
char path[USH_PATH_MAX];
if (arg == (const char *)0 || arg[0] == '\0') {
ush_writeln("touch: file path required");
return 0;
}
if (ush_resolve_path(sh, arg, path, (u64)sizeof(path)) == 0) {
ush_writeln("touch: invalid path");
return 0;
}
if (ush_path_is_under_temp(path) == 0) {
ush_writeln("touch: target must be under /temp");
return 0;
}
if (cleonos_sys_fs_write(path, empty_data, 0ULL) == 0ULL) {
ush_writeln("touch: failed");
return 0;
}
return 1;
}
static int ush_cmd_write(const ush_state *sh, const char *arg) {
char path_arg[USH_PATH_MAX];
char abs_path[USH_PATH_MAX];
const char *payload;
u64 payload_len;
if (arg == (const char *)0 || arg[0] == '\0') {
ush_writeln("write: usage write <file> <text>");
return 0;
}
if (ush_split_first_and_rest(arg, path_arg, (u64)sizeof(path_arg), &payload) == 0) {
ush_writeln("write: usage write <file> <text>");
return 0;
}
if (ush_resolve_path(sh, path_arg, abs_path, (u64)sizeof(abs_path)) == 0) {
ush_writeln("write: invalid path");
return 0;
}
if (ush_path_is_under_temp(abs_path) == 0) {
ush_writeln("write: target must be under /temp");
return 0;
}
payload_len = ush_strlen(payload);
if (cleonos_sys_fs_write(abs_path, payload, payload_len) == 0ULL) {
ush_writeln("write: failed");
return 0;
}
return 1;
}
static int ush_cmd_append(const ush_state *sh, const char *arg) {
char path_arg[USH_PATH_MAX];
char abs_path[USH_PATH_MAX];
const char *payload;
u64 payload_len;
if (arg == (const char *)0 || arg[0] == '\0') {
ush_writeln("append: usage append <file> <text>");
return 0;
}
if (ush_split_first_and_rest(arg, path_arg, (u64)sizeof(path_arg), &payload) == 0) {
ush_writeln("append: usage append <file> <text>");
return 0;
}
if (ush_resolve_path(sh, path_arg, abs_path, (u64)sizeof(abs_path)) == 0) {
ush_writeln("append: invalid path");
return 0;
}
if (ush_path_is_under_temp(abs_path) == 0) {
ush_writeln("append: target must be under /temp");
return 0;
}
payload_len = ush_strlen(payload);
if (cleonos_sys_fs_append(abs_path, payload, payload_len) == 0ULL) {
ush_writeln("append: failed");
return 0;
}
return 1;
}
static int ush_copy_file(const char *src_path, const char *dst_path) {
static char copy_buf[USH_COPY_MAX];
u64 src_type;
u64 src_size;
u64 got;
src_type = cleonos_sys_fs_stat_type(src_path);
if (src_type != 1ULL) {
ush_writeln("cp: source file not found");
return 0;
}
src_size = cleonos_sys_fs_stat_size(src_path);
if (src_size == (u64)-1) {
ush_writeln("cp: failed to stat source");
return 0;
}
if (src_size > (u64)USH_COPY_MAX) {
ush_writeln("cp: source too large for user shell buffer");
return 0;
}
if (src_size == 0ULL) {
got = 0ULL;
} else {
got = cleonos_sys_fs_read(src_path, copy_buf, src_size);
if (got == 0ULL || got != src_size) {
ush_writeln("cp: failed to read source");
return 0;
}
}
if (cleonos_sys_fs_write(dst_path, copy_buf, got) == 0ULL) {
ush_writeln("cp: failed to write destination");
return 0;
}
return 1;
}
static int ush_cmd_cp(const ush_state *sh, const char *arg) {
char src_arg[USH_PATH_MAX];
char dst_arg[USH_PATH_MAX];
char src_path[USH_PATH_MAX];
char dst_path[USH_PATH_MAX];
if (arg == (const char *)0 || arg[0] == '\0') {
ush_writeln("cp: usage cp <src> <dst>");
return 0;
}
if (ush_split_two_args(arg, src_arg, (u64)sizeof(src_arg), dst_arg, (u64)sizeof(dst_arg)) == 0) {
ush_writeln("cp: usage cp <src> <dst>");
return 0;
}
if (ush_resolve_path(sh, src_arg, src_path, (u64)sizeof(src_path)) == 0 ||
ush_resolve_path(sh, dst_arg, dst_path, (u64)sizeof(dst_path)) == 0) {
ush_writeln("cp: invalid path");
return 0;
}
if (ush_path_is_under_temp(dst_path) == 0) {
ush_writeln("cp: destination must be under /temp");
return 0;
}
return ush_copy_file(src_path, dst_path);
}
static int ush_cmd_mv(const ush_state *sh, const char *arg) {
char src_arg[USH_PATH_MAX];
char dst_arg[USH_PATH_MAX];
char src_path[USH_PATH_MAX];
char dst_path[USH_PATH_MAX];
if (arg == (const char *)0 || arg[0] == '\0') {
ush_writeln("mv: usage mv <src> <dst>");
return 0;
}
if (ush_split_two_args(arg, src_arg, (u64)sizeof(src_arg), dst_arg, (u64)sizeof(dst_arg)) == 0) {
ush_writeln("mv: usage mv <src> <dst>");
return 0;
}
if (ush_resolve_path(sh, src_arg, src_path, (u64)sizeof(src_path)) == 0 ||
ush_resolve_path(sh, dst_arg, dst_path, (u64)sizeof(dst_path)) == 0) {
ush_writeln("mv: invalid path");
return 0;
}
if (ush_path_is_under_temp(src_path) == 0 || ush_path_is_under_temp(dst_path) == 0) {
ush_writeln("mv: source and destination must be under /temp");
return 0;
}
if (ush_copy_file(src_path, dst_path) == 0) {
return 0;
}
if (cleonos_sys_fs_remove(src_path) == 0ULL) {
ush_writeln("mv: source remove failed");
return 0;
}
return 1;
}
static int ush_cmd_rm(const ush_state *sh, const char *arg) {
char path[USH_PATH_MAX];
if (arg == (const char *)0 || arg[0] == '\0') {
ush_writeln("rm: path required");
return 0;
}
if (ush_resolve_path(sh, arg, path, (u64)sizeof(path)) == 0) {
ush_writeln("rm: invalid path");
return 0;
}
if (ush_path_is_under_temp(path) == 0) {
ush_writeln("rm: target must be under /temp");
return 0;
}
if (cleonos_sys_fs_remove(path) == 0ULL) {
ush_writeln("rm: failed (directory must be empty)");
return 0;
}
return 1;
}
static int ush_cmd_stats(const ush_state *sh) {
(void)ush_cmd_memstat();
(void)ush_cmd_fsstat();
(void)ush_cmd_taskstat();
(void)ush_cmd_userstat();
(void)ush_cmd_kbdstat();
(void)ush_cmd_shstat(sh);
return 1;
}
static int ush_cmd_not_supported(const char *name, const char *why) {
ush_write(name);
ush_write(": ");
ush_writeln(why);
return 0;
}
void ush_execute_line(ush_state *sh, const char *line) {
char line_buf[USH_LINE_MAX];
char cmd[USH_CMD_MAX];
char arg[USH_ARG_MAX];
u64 i = 0ULL;
int known = 1;
int success = 0;
if (sh == (ush_state *)0 || line == (const char *)0) {
return;
}
while (line[i] != '\0' && i + 1ULL < (u64)sizeof(line_buf)) {
line_buf[i] = line[i];
i++;
}
line_buf[i] = '\0';
ush_trim_line(line_buf);
if (line_buf[0] == '\0' || line_buf[0] == '#') {
return;
}
ush_parse_line(line_buf, cmd, (u64)sizeof(cmd), arg, (u64)sizeof(arg));
ush_trim_line(arg);
if (ush_streq(cmd, "help") != 0) {
success = ush_cmd_help();
} else if (ush_streq(cmd, "ls") != 0 || ush_streq(cmd, "dir") != 0) {
success = ush_cmd_ls(sh, arg);
} else if (ush_streq(cmd, "cat") != 0) {
success = ush_cmd_cat(sh, arg);
} else if (ush_streq(cmd, "pwd") != 0) {
success = ush_cmd_pwd(sh);
} else if (ush_streq(cmd, "cd") != 0) {
success = ush_cmd_cd(sh, arg);
} else if (ush_streq(cmd, "exec") != 0 || ush_streq(cmd, "run") != 0) {
success = ush_cmd_exec(sh, arg);
} else if (ush_streq(cmd, "clear") != 0 || ush_streq(cmd, "cls") != 0) {
success = ush_cmd_clear();
} else if (ush_streq(cmd, "memstat") != 0) {
success = ush_cmd_memstat();
} else if (ush_streq(cmd, "fsstat") != 0) {
success = ush_cmd_fsstat();
} else if (ush_streq(cmd, "taskstat") != 0) {
success = ush_cmd_taskstat();
} else if (ush_streq(cmd, "userstat") != 0) {
success = ush_cmd_userstat();
} else if (ush_streq(cmd, "shstat") != 0) {
success = ush_cmd_shstat(sh);
} else if (ush_streq(cmd, "stats") != 0) {
success = ush_cmd_stats(sh);
} else if (ush_streq(cmd, "tty") != 0) {
success = ush_cmd_tty(arg);
} else if (ush_streq(cmd, "dmesg") != 0) {
success = ush_cmd_dmesg(arg);
} else if (ush_streq(cmd, "kbdstat") != 0) {
success = ush_cmd_kbdstat();
} else if (ush_streq(cmd, "mkdir") != 0) {
success = ush_cmd_mkdir(sh, arg);
} else if (ush_streq(cmd, "touch") != 0) {
success = ush_cmd_touch(sh, arg);
} else if (ush_streq(cmd, "write") != 0) {
success = ush_cmd_write(sh, arg);
} else if (ush_streq(cmd, "append") != 0) {
success = ush_cmd_append(sh, arg);
} else if (ush_streq(cmd, "cp") != 0) {
success = ush_cmd_cp(sh, arg);
} else if (ush_streq(cmd, "mv") != 0) {
success = ush_cmd_mv(sh, arg);
} else if (ush_streq(cmd, "rm") != 0) {
success = ush_cmd_rm(sh, arg);
} else if (ush_streq(cmd, "rusttest") != 0 || ush_streq(cmd, "panic") != 0 || ush_streq(cmd, "elfloader") != 0) {
success = ush_cmd_not_supported(cmd, "this command is kernel-shell only");
} else {
known = 0;
success = 0;
ush_writeln("unknown command; type 'help'");
}
sh->cmd_total++;
if (success != 0) {
sh->cmd_ok++;
} else {
sh->cmd_fail++;
}
if (known == 0) {
sh->cmd_unknown++;
}
}

View File

@@ -0,0 +1,322 @@
#include "shell_internal.h"
static void ush_history_cancel_nav(ush_state *sh) {
if (sh == (ush_state *)0) {
return;
}
sh->history_nav = -1;
sh->nav_saved_len = 0ULL;
sh->nav_saved_cursor = 0ULL;
sh->nav_saved_line[0] = '\0';
}
static void ush_reset_line(ush_state *sh) {
if (sh == (ush_state *)0) {
return;
}
sh->line_len = 0ULL;
sh->cursor = 0ULL;
sh->rendered_len = 0ULL;
sh->line[0] = '\0';
}
static void ush_load_line(ush_state *sh, const char *line) {
if (sh == (ush_state *)0) {
return;
}
if (line == (const char *)0) {
ush_reset_line(sh);
return;
}
ush_copy(sh->line, (u64)sizeof(sh->line), line);
sh->line_len = ush_strlen(sh->line);
sh->cursor = sh->line_len;
}
static void ush_render_line(ush_state *sh) {
u64 i;
if (sh == (ush_state *)0) {
return;
}
ush_write_char('\r');
ush_prompt(sh);
for (i = 0ULL; i < sh->line_len; i++) {
ush_write_char(sh->line[i]);
}
for (i = sh->line_len; i < sh->rendered_len; i++) {
ush_write_char(' ');
}
ush_write_char('\r');
ush_prompt(sh);
for (i = 0ULL; i < sh->cursor; i++) {
ush_write_char(sh->line[i]);
}
sh->rendered_len = sh->line_len;
}
static int ush_line_has_non_space(const char *line) {
u64 i = 0ULL;
if (line == (const char *)0) {
return 0;
}
while (line[i] != '\0') {
if (ush_is_space(line[i]) == 0) {
return 1;
}
i++;
}
return 0;
}
static void ush_history_push(ush_state *sh, const char *line) {
if (sh == (ush_state *)0) {
return;
}
if (ush_line_has_non_space(line) == 0) {
ush_history_cancel_nav(sh);
return;
}
if (sh->history_count > 0ULL && ush_streq(sh->history[sh->history_count - 1ULL], line) != 0) {
ush_history_cancel_nav(sh);
return;
}
if (sh->history_count < USH_HISTORY_MAX) {
ush_copy(sh->history[sh->history_count], (u64)sizeof(sh->history[sh->history_count]), line);
sh->history_count++;
} else {
u64 i;
for (i = 1ULL; i < USH_HISTORY_MAX; i++) {
ush_copy(sh->history[i - 1ULL], (u64)sizeof(sh->history[i - 1ULL]), sh->history[i]);
}
ush_copy(sh->history[USH_HISTORY_MAX - 1ULL], (u64)sizeof(sh->history[USH_HISTORY_MAX - 1ULL]), line);
}
ush_history_cancel_nav(sh);
}
static void ush_history_apply_current(ush_state *sh) {
if (sh == (ush_state *)0) {
return;
}
if (sh->history_nav >= 0) {
ush_load_line(sh, sh->history[(u64)sh->history_nav]);
} else {
ush_copy(sh->line, (u64)sizeof(sh->line), sh->nav_saved_line);
sh->line_len = sh->nav_saved_len;
if (sh->line_len > USH_LINE_MAX - 1ULL) {
sh->line_len = USH_LINE_MAX - 1ULL;
sh->line[sh->line_len] = '\0';
}
sh->cursor = sh->nav_saved_cursor;
if (sh->cursor > sh->line_len) {
sh->cursor = sh->line_len;
}
}
ush_render_line(sh);
}
static void ush_history_up(ush_state *sh) {
if (sh == (ush_state *)0 || sh->history_count == 0ULL) {
return;
}
if (sh->history_nav < 0) {
ush_copy(sh->nav_saved_line, (u64)sizeof(sh->nav_saved_line), sh->line);
sh->nav_saved_len = sh->line_len;
sh->nav_saved_cursor = sh->cursor;
sh->history_nav = (i64)sh->history_count - 1;
} else if (sh->history_nav > 0) {
sh->history_nav--;
}
ush_history_apply_current(sh);
}
static void ush_history_down(ush_state *sh) {
if (sh == (ush_state *)0 || sh->history_nav < 0) {
return;
}
if ((u64)sh->history_nav + 1ULL < sh->history_count) {
sh->history_nav++;
} else {
sh->history_nav = -1;
}
ush_history_apply_current(sh);
}
static char ush_read_char_blocking(void) {
for (;;) {
u64 ch = cleonos_sys_kbd_get_char();
if (ch != (u64)-1) {
return (char)(ch & 0xFFULL);
}
__asm__ volatile("pause");
}
}
void ush_read_line(ush_state *sh, char *out_line, u64 out_size) {
if (sh == (ush_state *)0 || out_line == (char *)0 || out_size == 0ULL) {
return;
}
ush_reset_line(sh);
ush_history_cancel_nav(sh);
out_line[0] = '\0';
ush_prompt(sh);
for (;;) {
char ch = ush_read_char_blocking();
if (ch == '\r') {
continue;
}
if (ch == '\n') {
ush_write_char('\n');
sh->line[sh->line_len] = '\0';
ush_history_push(sh, sh->line);
ush_copy(out_line, out_size, sh->line);
ush_reset_line(sh);
return;
}
if (ch == USH_KEY_UP) {
ush_history_up(sh);
continue;
}
if (ch == USH_KEY_DOWN) {
ush_history_down(sh);
continue;
}
if (ch == USH_KEY_LEFT) {
if (sh->cursor > 0ULL) {
sh->cursor--;
ush_render_line(sh);
}
continue;
}
if (ch == USH_KEY_RIGHT) {
if (sh->cursor < sh->line_len) {
sh->cursor++;
ush_render_line(sh);
}
continue;
}
if (ch == USH_KEY_HOME) {
if (sh->cursor != 0ULL) {
sh->cursor = 0ULL;
ush_render_line(sh);
}
continue;
}
if (ch == USH_KEY_END) {
if (sh->cursor != sh->line_len) {
sh->cursor = sh->line_len;
ush_render_line(sh);
}
continue;
}
if (ch == '\b' || ch == 127) {
if (sh->cursor > 0ULL && sh->line_len > 0ULL) {
u64 i;
ush_history_cancel_nav(sh);
for (i = sh->cursor - 1ULL; i < sh->line_len; i++) {
sh->line[i] = sh->line[i + 1ULL];
}
sh->line_len--;
sh->cursor--;
ush_render_line(sh);
}
continue;
}
if (ch == USH_KEY_DELETE) {
if (sh->cursor < sh->line_len) {
u64 i;
ush_history_cancel_nav(sh);
for (i = sh->cursor; i < sh->line_len; i++) {
sh->line[i] = sh->line[i + 1ULL];
}
sh->line_len--;
ush_render_line(sh);
}
continue;
}
if (ch == '\t') {
ch = ' ';
}
if (ush_is_printable(ch) == 0) {
continue;
}
if (sh->line_len + 1ULL >= USH_LINE_MAX) {
continue;
}
ush_history_cancel_nav(sh);
if (sh->cursor == sh->line_len) {
sh->line[sh->line_len++] = ch;
sh->line[sh->line_len] = '\0';
sh->cursor = sh->line_len;
ush_write_char(ch);
sh->rendered_len = sh->line_len;
continue;
}
{
u64 i;
for (i = sh->line_len; i > sh->cursor; i--) {
sh->line[i] = sh->line[i - 1ULL];
}
sh->line[sh->cursor] = ch;
sh->line_len++;
sh->cursor++;
sh->line[sh->line_len] = '\0';
ush_render_line(sh);
}
}
}

View File

@@ -0,0 +1,74 @@
#ifndef CLEONOS_USER_SHELL_INTERNAL_H
#define CLEONOS_USER_SHELL_INTERNAL_H
#include <cleonos_syscall.h>
typedef long long i64;
#define USH_CMD_MAX 32ULL
#define USH_ARG_MAX 160ULL
#define USH_LINE_MAX 192ULL
#define USH_PATH_MAX 192ULL
#define USH_CAT_MAX 512ULL
#define USH_SCRIPT_MAX 1024ULL
#define USH_CLEAR_LINES 56ULL
#define USH_HISTORY_MAX 16ULL
#define USH_KEY_LEFT ((char)0x01)
#define USH_KEY_RIGHT ((char)0x02)
#define USH_KEY_UP ((char)0x03)
#define USH_KEY_DOWN ((char)0x04)
#define USH_KEY_HOME ((char)0x05)
#define USH_KEY_END ((char)0x06)
#define USH_KEY_DELETE ((char)0x07)
typedef struct ush_state {
char line[USH_LINE_MAX];
u64 line_len;
u64 cursor;
u64 rendered_len;
char cwd[USH_PATH_MAX];
char history[USH_HISTORY_MAX][USH_LINE_MAX];
u64 history_count;
i64 history_nav;
char nav_saved_line[USH_LINE_MAX];
u64 nav_saved_len;
u64 nav_saved_cursor;
u64 cmd_total;
u64 cmd_ok;
u64 cmd_fail;
u64 cmd_unknown;
} ush_state;
void ush_init_state(ush_state *sh);
u64 ush_strlen(const char *str);
int ush_streq(const char *left, const char *right);
int ush_is_space(char ch);
int ush_is_printable(char ch);
int ush_has_suffix(const char *name, const char *suffix);
int ush_contains_char(const char *text, char needle);
int ush_parse_u64_dec(const char *text, u64 *out_value);
void ush_copy(char *dst, u64 dst_size, const char *src);
void ush_trim_line(char *line);
void ush_parse_line(const char *line, char *out_cmd, u64 cmd_size, char *out_arg, u64 arg_size);
void ush_write(const char *text);
void ush_write_char(char ch);
void ush_writeln(const char *text);
void ush_prompt(const ush_state *sh);
void ush_write_hex_u64(u64 value);
void ush_print_kv_hex(const char *label, u64 value);
int ush_resolve_path(const ush_state *sh, const char *arg, char *out_path, u64 out_size);
int ush_resolve_exec_path(const ush_state *sh, const char *arg, char *out_path, u64 out_size);
int ush_path_is_under_system(const char *path);
void ush_read_line(ush_state *sh, char *out_line, u64 out_size);
int ush_run_script_file(ush_state *sh, const char *path);
void ush_execute_line(ush_state *sh, const char *line);
#endif

View File

@@ -0,0 +1,202 @@
#include "shell_internal.h"
static int ush_path_push_component(char *path, u64 path_size, u64 *io_len, const char *component, u64 comp_len) {
u64 i;
if (path == (char *)0 || io_len == (u64 *)0 || component == (const char *)0 || comp_len == 0ULL) {
return 0;
}
if (*io_len == 1ULL) {
if (*io_len + comp_len >= path_size) {
return 0;
}
for (i = 0ULL; i < comp_len; i++) {
path[1ULL + i] = component[i];
}
*io_len = 1ULL + comp_len;
path[*io_len] = '\0';
return 1;
}
if (*io_len + 1ULL + comp_len >= path_size) {
return 0;
}
path[*io_len] = '/';
for (i = 0ULL; i < comp_len; i++) {
path[*io_len + 1ULL + i] = component[i];
}
*io_len += (1ULL + comp_len);
path[*io_len] = '\0';
return 1;
}
static void ush_path_pop_component(char *path, u64 *io_len) {
if (path == (char *)0 || io_len == (u64 *)0) {
return;
}
if (*io_len <= 1ULL) {
path[0] = '/';
path[1] = '\0';
*io_len = 1ULL;
return;
}
while (*io_len > 1ULL && path[*io_len - 1ULL] != '/') {
(*io_len)--;
}
if (*io_len > 1ULL) {
(*io_len)--;
}
path[*io_len] = '\0';
}
static int ush_path_parse_into(const char *src, char *out_path, u64 out_size, u64 *io_len) {
u64 i = 0ULL;
if (src == (const char *)0 || out_path == (char *)0 || io_len == (u64 *)0) {
return 0;
}
if (src[0] == '/') {
i = 1ULL;
}
while (src[i] != '\0') {
u64 start;
u64 len;
while (src[i] == '/') {
i++;
}
if (src[i] == '\0') {
break;
}
start = i;
while (src[i] != '\0' && src[i] != '/') {
i++;
}
len = i - start;
if (len == 1ULL && src[start] == '.') {
continue;
}
if (len == 2ULL && src[start] == '.' && src[start + 1ULL] == '.') {
ush_path_pop_component(out_path, io_len);
continue;
}
if (ush_path_push_component(out_path, out_size, io_len, src + start, len) == 0) {
return 0;
}
}
return 1;
}
int ush_resolve_path(const ush_state *sh, const char *arg, char *out_path, u64 out_size) {
u64 len = 1ULL;
if (sh == (const ush_state *)0 || out_path == (char *)0 || out_size < 2ULL) {
return 0;
}
out_path[0] = '/';
out_path[1] = '\0';
if (arg == (const char *)0 || arg[0] == '\0') {
return ush_path_parse_into(sh->cwd, out_path, out_size, &len);
}
if (arg[0] != '/') {
if (ush_path_parse_into(sh->cwd, out_path, out_size, &len) == 0) {
return 0;
}
}
return ush_path_parse_into(arg, out_path, out_size, &len);
}
int ush_resolve_exec_path(const ush_state *sh, const char *arg, char *out_path, u64 out_size) {
u64 i;
u64 cursor = 0ULL;
if (sh == (const ush_state *)0 || arg == (const char *)0 || out_path == (char *)0 || out_size == 0ULL) {
return 0;
}
if (arg[0] == '\0') {
return 0;
}
out_path[0] = '\0';
if (arg[0] == '/') {
ush_copy(out_path, out_size, arg);
} else if (ush_contains_char(arg, '/') != 0) {
if (ush_resolve_path(sh, arg, out_path, out_size) == 0) {
return 0;
}
} else {
static const char prefix[] = "/shell/";
u64 prefix_len = (u64)(sizeof(prefix) - 1U);
if (prefix_len + 1ULL >= out_size) {
return 0;
}
for (i = 0ULL; i < prefix_len; i++) {
out_path[cursor++] = prefix[i];
}
for (i = 0ULL; arg[i] != '\0'; i++) {
if (cursor + 1ULL >= out_size) {
return 0;
}
out_path[cursor++] = arg[i];
}
out_path[cursor] = '\0';
}
if (ush_has_suffix(out_path, ".elf") == 0) {
static const char suffix[] = ".elf";
cursor = ush_strlen(out_path);
for (i = 0ULL; suffix[i] != '\0'; i++) {
if (cursor + 1ULL >= out_size) {
return 0;
}
out_path[cursor++] = suffix[i];
}
out_path[cursor] = '\0';
}
return 1;
}
int ush_path_is_under_system(const char *path) {
if (path == (const char *)0) {
return 0;
}
if (path[0] != '/' || path[1] != 's' || path[2] != 'y' || path[3] != 's' || path[4] != 't' || path[5] != 'e' || path[6] != 'm') {
return 0;
}
return (path[7] == '\0' || path[7] == '/') ? 1 : 0;
}

View File

@@ -0,0 +1,46 @@
#include "shell_internal.h"
int ush_run_script_file(ush_state *sh, const char *path) {
char script[USH_SCRIPT_MAX + 1ULL];
char line[USH_LINE_MAX];
u64 got;
u64 i;
u64 line_pos = 0ULL;
if (sh == (ush_state *)0 || path == (const char *)0 || path[0] == '\0') {
return 0;
}
got = cleonos_sys_fs_read(path, script, USH_SCRIPT_MAX);
if (got == 0ULL) {
return 0;
}
if (got > USH_SCRIPT_MAX) {
got = USH_SCRIPT_MAX;
}
script[got] = '\0';
for (i = 0ULL; i <= got; i++) {
char ch = script[i];
if (ch == '\r') {
continue;
}
if (ch == '\n' || ch == '\0') {
line[line_pos] = '\0';
ush_execute_line(sh, line);
line_pos = 0ULL;
continue;
}
if (line_pos + 1ULL < (u64)sizeof(line)) {
line[line_pos++] = ch;
}
}
return 1;
}

View File

@@ -0,0 +1,273 @@
#include "shell_internal.h"
void ush_init_state(ush_state *sh) {
if (sh == (ush_state *)0) {
return;
}
sh->line[0] = '\0';
sh->line_len = 0ULL;
sh->cursor = 0ULL;
sh->rendered_len = 0ULL;
ush_copy(sh->cwd, (u64)sizeof(sh->cwd), "/");
sh->history_count = 0ULL;
sh->history_nav = -1;
sh->nav_saved_line[0] = '\0';
sh->nav_saved_len = 0ULL;
sh->nav_saved_cursor = 0ULL;
sh->cmd_total = 0ULL;
sh->cmd_ok = 0ULL;
sh->cmd_fail = 0ULL;
sh->cmd_unknown = 0ULL;
}
u64 ush_strlen(const char *str) {
u64 len = 0ULL;
if (str == (const char *)0) {
return 0ULL;
}
while (str[len] != '\0') {
len++;
}
return len;
}
int ush_streq(const char *left, const char *right) {
u64 i = 0ULL;
if (left == (const char *)0 || right == (const char *)0) {
return 0;
}
while (left[i] != '\0' && right[i] != '\0') {
if (left[i] != right[i]) {
return 0;
}
i++;
}
return (left[i] == right[i]) ? 1 : 0;
}
int ush_is_space(char ch) {
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') ? 1 : 0;
}
int ush_is_printable(char ch) {
return (ch >= 32 && ch <= 126) ? 1 : 0;
}
int ush_has_suffix(const char *name, const char *suffix) {
u64 name_len;
u64 suffix_len;
u64 i;
if (name == (const char *)0 || suffix == (const char *)0) {
return 0;
}
name_len = ush_strlen(name);
suffix_len = ush_strlen(suffix);
if (suffix_len > name_len) {
return 0;
}
for (i = 0ULL; i < suffix_len; i++) {
if (name[name_len - suffix_len + i] != suffix[i]) {
return 0;
}
}
return 1;
}
int ush_contains_char(const char *text, char needle) {
u64 i = 0ULL;
if (text == (const char *)0) {
return 0;
}
while (text[i] != '\0') {
if (text[i] == needle) {
return 1;
}
i++;
}
return 0;
}
int ush_parse_u64_dec(const char *text, u64 *out_value) {
u64 value = 0ULL;
u64 i = 0ULL;
if (text == (const char *)0 || out_value == (u64 *)0 || text[0] == '\0') {
return 0;
}
while (text[i] != '\0') {
u64 digit;
if (text[i] < '0' || text[i] > '9') {
return 0;
}
digit = (u64)(text[i] - '0');
if (value > ((0xFFFFFFFFFFFFFFFFULL - digit) / 10ULL)) {
return 0;
}
value = (value * 10ULL) + digit;
i++;
}
*out_value = value;
return 1;
}
void ush_copy(char *dst, u64 dst_size, const char *src) {
u64 i = 0ULL;
if (dst == (char *)0 || src == (const char *)0 || dst_size == 0ULL) {
return;
}
while (src[i] != '\0' && i + 1ULL < dst_size) {
dst[i] = src[i];
i++;
}
dst[i] = '\0';
}
void ush_trim_line(char *line) {
u64 start = 0ULL;
u64 i = 0ULL;
u64 len;
if (line == (char *)0) {
return;
}
while (line[start] != '\0' && ush_is_space(line[start]) != 0) {
start++;
}
if (start > 0ULL) {
while (line[start + i] != '\0') {
line[i] = line[start + i];
i++;
}
line[i] = '\0';
}
len = ush_strlen(line);
while (len > 0ULL && ush_is_space(line[len - 1ULL]) != 0) {
line[len - 1ULL] = '\0';
len--;
}
}
void ush_parse_line(const char *line, char *out_cmd, u64 cmd_size, char *out_arg, u64 arg_size) {
u64 i = 0ULL;
u64 cmd_pos = 0ULL;
u64 arg_pos = 0ULL;
if (line == (const char *)0 || out_cmd == (char *)0 || out_arg == (char *)0) {
return;
}
out_cmd[0] = '\0';
out_arg[0] = '\0';
while (line[i] != '\0' && ush_is_space(line[i]) != 0) {
i++;
}
while (line[i] != '\0' && ush_is_space(line[i]) == 0) {
if (cmd_pos + 1ULL < cmd_size) {
out_cmd[cmd_pos++] = line[i];
}
i++;
}
out_cmd[cmd_pos] = '\0';
while (line[i] != '\0' && ush_is_space(line[i]) != 0) {
i++;
}
while (line[i] != '\0') {
if (arg_pos + 1ULL < arg_size) {
out_arg[arg_pos++] = line[i];
}
i++;
}
out_arg[arg_pos] = '\0';
}
void ush_write(const char *text) {
u64 len;
if (text == (const char *)0) {
return;
}
len = ush_strlen(text);
if (len == 0ULL) {
return;
}
(void)cleonos_sys_tty_write(text, len);
}
void ush_write_char(char ch) {
(void)cleonos_sys_tty_write_char(ch);
}
void ush_writeln(const char *text) {
ush_write(text);
ush_write_char('\n');
}
void ush_prompt(const ush_state *sh) {
if (sh == (const ush_state *)0) {
ush_write("cleonos(user)> ");
return;
}
ush_write("cleonos(user:");
ush_write(sh->cwd);
ush_write(")> ");
}
void ush_write_hex_u64(u64 value) {
i64 nibble;
ush_write("0X");
for (nibble = 15; nibble >= 0; nibble--) {
u64 current = (value >> (u64)(nibble * 4)) & 0x0FULL;
char out = (current < 10ULL) ? (char)('0' + current) : (char)('A' + (current - 10ULL));
ush_write_char(out);
}
}
void ush_print_kv_hex(const char *label, u64 value) {
ush_write(label);
ush_write(": ");
ush_write_hex_u64(value);
ush_write_char('\n');
}

View File

@@ -1,539 +1,18 @@
#include <cleonos_syscall.h>
#define SHELL_CMD_MAX 32ULL
#define SHELL_ARG_MAX 160ULL
#define SHELL_LINE_MAX 192ULL
#define SHELL_CAT_MAX 512ULL
#define SHELL_SCRIPT_MAX 1024ULL
#define SHELL_CLEAR_LINES 56ULL
static u64 shell_strlen(const char *str) {
u64 len = 0ULL;
if (str == (const char *)0) {
return 0ULL;
}
while (str[len] != '\0') {
len++;
}
return len;
}
static int shell_streq(const char *left, const char *right) {
u64 i = 0ULL;
if (left == (const char *)0 || right == (const char *)0) {
return 0;
}
while (left[i] != '\0' && right[i] != '\0') {
if (left[i] != right[i]) {
return 0;
}
i++;
}
return (left[i] == right[i]) ? 1 : 0;
}
static int shell_is_space(char ch) {
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') ? 1 : 0;
}
static int shell_is_printable(char ch) {
return (ch >= 32 && ch <= 126) ? 1 : 0;
}
static void shell_copy(char *dst, u64 dst_size, const char *src) {
u64 i = 0ULL;
if (dst == (char *)0 || src == (const char *)0 || dst_size == 0ULL) {
return;
}
while (src[i] != '\0' && i + 1ULL < dst_size) {
dst[i] = src[i];
i++;
}
dst[i] = '\0';
}
static void shell_write(const char *text) {
u64 len;
if (text == (const char *)0) {
return;
}
len = shell_strlen(text);
if (len == 0ULL) {
return;
}
(void)cleonos_sys_tty_write(text, len);
}
static void shell_write_char(char ch) {
(void)cleonos_sys_tty_write_char(ch);
}
static void shell_writeln(const char *text) {
shell_write(text);
shell_write_char('\n');
}
static void shell_prompt(void) {
shell_write("cleonos(user)> ");
}
static void shell_trim_line(char *line) {
u64 start = 0ULL;
u64 i = 0ULL;
u64 len;
if (line == (char *)0) {
return;
}
while (line[start] != '\0' && shell_is_space(line[start]) != 0) {
start++;
}
if (start > 0ULL) {
while (line[start + i] != '\0') {
line[i] = line[start + i];
i++;
}
line[i] = '\0';
}
len = shell_strlen(line);
while (len > 0ULL && shell_is_space(line[len - 1ULL]) != 0) {
line[len - 1ULL] = '\0';
len--;
}
}
static void shell_parse_line(const char *line, char *out_cmd, u64 cmd_size, char *out_arg, u64 arg_size) {
u64 i = 0ULL;
u64 cmd_pos = 0ULL;
u64 arg_pos = 0ULL;
if (line == (const char *)0 || out_cmd == (char *)0 || out_arg == (char *)0) {
return;
}
out_cmd[0] = '\0';
out_arg[0] = '\0';
while (line[i] != '\0' && shell_is_space(line[i]) != 0) {
i++;
}
while (line[i] != '\0' && shell_is_space(line[i]) == 0) {
if (cmd_pos + 1ULL < cmd_size) {
out_cmd[cmd_pos++] = line[i];
}
i++;
}
out_cmd[cmd_pos] = '\0';
while (line[i] != '\0' && shell_is_space(line[i]) != 0) {
i++;
}
while (line[i] != '\0') {
if (arg_pos + 1ULL < arg_size) {
out_arg[arg_pos++] = line[i];
}
i++;
}
out_arg[arg_pos] = '\0';
}
static int shell_has_suffix(const char *name, const char *suffix) {
u64 name_len;
u64 suffix_len;
u64 i;
if (name == (const char *)0 || suffix == (const char *)0) {
return 0;
}
name_len = shell_strlen(name);
suffix_len = shell_strlen(suffix);
if (suffix_len > name_len) {
return 0;
}
for (i = 0ULL; i < suffix_len; i++) {
if (name[name_len - suffix_len + i] != suffix[i]) {
return 0;
}
}
return 1;
}
static int shell_resolve_exec_path(const char *arg, char *out_path, u64 out_size) {
u64 cursor = 0ULL;
u64 i;
if (arg == (const char *)0 || out_path == (char *)0 || out_size == 0ULL) {
return 0;
}
if (arg[0] == '\0') {
return 0;
}
out_path[0] = '\0';
if (arg[0] == '/') {
shell_copy(out_path, out_size, arg);
return 1;
}
{
static const char prefix[] = "/shell/";
u64 prefix_len = (u64)(sizeof(prefix) - 1U);
if (prefix_len + 1ULL >= out_size) {
return 0;
}
for (i = 0ULL; i < prefix_len; i++) {
out_path[cursor++] = prefix[i];
}
}
i = 0ULL;
while (arg[i] != '\0' && cursor + 1ULL < out_size) {
out_path[cursor++] = arg[i++];
}
out_path[cursor] = '\0';
if (arg[i] != '\0') {
return 0;
}
if (shell_has_suffix(out_path, ".elf") == 0) {
static const char suffix[] = ".elf";
for (i = 0ULL; suffix[i] != '\0'; i++) {
if (cursor + 1ULL >= out_size) {
return 0;
}
out_path[cursor++] = suffix[i];
}
out_path[cursor] = '\0';
}
return 1;
}
static int shell_path_is_under_system(const char *path) {
if (path == (const char *)0) {
return 0;
}
if (path[0] != '/' || path[1] != 's' || path[2] != 'y' || path[3] != 's' || path[4] != 't' || path[5] != 'e' || path[6] != 'm') {
return 0;
}
return (path[7] == '\0' || path[7] == '/') ? 1 : 0;
}
static void shell_cmd_help(void) {
shell_writeln("commands:");
shell_writeln(" help");
shell_writeln(" ls [dir]");
shell_writeln(" cat <file>");
shell_writeln(" exec <path|name>");
shell_writeln(" clear");
shell_writeln(" stats");
}
static void shell_cmd_ls(const char *arg) {
const char *path = arg;
u64 count;
u64 i;
if (path == (const char *)0 || path[0] == '\0') {
path = "/";
}
count = cleonos_sys_fs_child_count(path);
if (count == (u64)-1) {
shell_writeln("ls: directory not found");
return;
}
if (count == 0ULL) {
shell_writeln("(empty)");
return;
}
for (i = 0ULL; i < count; i++) {
char name[CLEONOS_FS_NAME_MAX];
name[0] = '\0';
if (cleonos_sys_fs_get_child_name(path, i, name) == 0ULL) {
continue;
}
shell_writeln(name);
}
}
static void shell_cmd_cat(const char *arg) {
char cat_buf[SHELL_CAT_MAX + 1ULL];
u64 got;
if (arg == (const char *)0 || arg[0] == '\0') {
shell_writeln("cat: file path required");
return;
}
got = cleonos_sys_fs_read(arg, cat_buf, SHELL_CAT_MAX);
if (got == 0ULL) {
shell_writeln("cat: file not found");
return;
}
if (got > SHELL_CAT_MAX) {
got = SHELL_CAT_MAX;
}
cat_buf[got] = '\0';
shell_writeln(cat_buf);
}
static void shell_cmd_exec(const char *arg) {
char path[SHELL_LINE_MAX];
u64 status;
if (shell_resolve_exec_path(arg, path, (u64)sizeof(path)) == 0) {
shell_writeln("exec: invalid target");
return;
}
if (shell_path_is_under_system(path) != 0) {
shell_writeln("exec: /system/*.elf is kernel-mode (KELF), not user-exec");
return;
}
status = cleonos_sys_exec_path(path);
if (status == (u64)-1) {
shell_writeln("exec: request failed");
} else if (status == 0ULL) {
shell_writeln("exec: request accepted");
} else {
shell_writeln("exec: returned non-zero status");
}
}
static void shell_cmd_clear(void) {
u64 i;
for (i = 0ULL; i < SHELL_CLEAR_LINES; i++) {
shell_write_char('\n');
}
}
static void shell_cmd_stats(void) {
(void)cleonos_sys_task_count();
shell_writeln("stats: use kernel log channel for full counters");
}
static void shell_execute_line(const char *line) {
char cmd[SHELL_CMD_MAX];
char arg[SHELL_ARG_MAX];
char line_buf[SHELL_LINE_MAX];
u64 i = 0ULL;
if (line == (const char *)0) {
return;
}
while (line[i] != '\0' && i + 1ULL < (u64)sizeof(line_buf)) {
line_buf[i] = line[i];
i++;
}
line_buf[i] = '\0';
shell_trim_line(line_buf);
if (line_buf[0] == '\0' || line_buf[0] == '#') {
return;
}
shell_parse_line(line_buf, cmd, (u64)sizeof(cmd), arg, (u64)sizeof(arg));
if (shell_streq(cmd, "help") != 0) {
shell_cmd_help();
return;
}
if (shell_streq(cmd, "ls") != 0) {
shell_cmd_ls(arg);
return;
}
if (shell_streq(cmd, "cat") != 0) {
shell_cmd_cat(arg);
return;
}
if (shell_streq(cmd, "exec") != 0 || shell_streq(cmd, "run") != 0) {
shell_cmd_exec(arg);
return;
}
if (shell_streq(cmd, "clear") != 0) {
shell_cmd_clear();
return;
}
if (shell_streq(cmd, "stats") != 0) {
shell_cmd_stats();
return;
}
shell_writeln("unknown command; type 'help'");
}
static int shell_run_script_file(const char *path) {
char script[SHELL_SCRIPT_MAX + 1ULL];
char line[SHELL_LINE_MAX];
u64 got;
u64 i;
u64 line_pos = 0ULL;
if (path == (const char *)0 || path[0] == '\0') {
return 0;
}
got = cleonos_sys_fs_read(path, script, SHELL_SCRIPT_MAX);
if (got == 0ULL) {
return 0;
}
if (got > SHELL_SCRIPT_MAX) {
got = SHELL_SCRIPT_MAX;
}
script[got] = '\0';
for (i = 0ULL; i <= got; i++) {
char ch = script[i];
if (ch == '\r') {
continue;
}
if (ch == '\n' || ch == '\0') {
line[line_pos] = '\0';
shell_execute_line(line);
line_pos = 0ULL;
continue;
}
if (line_pos + 1ULL < (u64)sizeof(line)) {
line[line_pos++] = ch;
}
}
return 1;
}
static char shell_read_char_blocking(void) {
for (;;) {
u64 ch = cleonos_sys_kbd_get_char();
if (ch != (u64)-1) {
return (char)(ch & 0xFFULL);
}
__asm__ volatile("pause");
}
}
static void shell_read_line(char *out_line, u64 out_size) {
u64 cursor = 0ULL;
if (out_line == (char *)0 || out_size == 0ULL) {
return;
}
out_line[0] = '\0';
for (;;) {
char ch = shell_read_char_blocking();
if (ch == '\r') {
continue;
}
if (ch == '\n') {
shell_write_char('\n');
break;
}
if (ch == '\b' || ch == 127) {
if (cursor > 0ULL) {
cursor--;
out_line[cursor] = '\0';
shell_write_char('\b');
}
continue;
}
if (ch == '\t') {
ch = ' ';
}
if (shell_is_printable(ch) == 0) {
continue;
}
if (cursor + 1ULL >= out_size) {
continue;
}
out_line[cursor++] = ch;
out_line[cursor] = '\0';
shell_write_char(ch);
}
}
#include "shell/shell_internal.h"
int cleonos_app_main(void) {
char line[SHELL_LINE_MAX];
ush_state sh;
char line[USH_LINE_MAX];
shell_writeln("[USER][SHELL] interactive framework online");
ush_init_state(&sh);
ush_writeln("[USER][SHELL] interactive framework online");
if (shell_run_script_file("/shell/init.cmd") == 0) {
shell_writeln("[USER][SHELL] /shell/init.cmd missing");
if (ush_run_script_file(&sh, "/shell/init.cmd") == 0) {
ush_writeln("[USER][SHELL] /shell/init.cmd missing");
}
for (;;) {
shell_prompt();
shell_read_line(line, (u64)sizeof(line));
shell_execute_line(line);
ush_read_line(&sh, line, (u64)sizeof(line));
ush_execute_line(&sh, line);
}
}