/* SPDX-License-Identifier: GPL-2.0-or-later UMKa - User-Mode KolibriOS developer tools umka_shell - the shell Copyright (C) 2017-2023 Ivan Baravy Copyright (C) 2021 Magomed Kostoev */ #include #include #include #include #include #include #include #include #include #include #include #include #include // TODO: Cleanup #ifndef _WIN32 #include #else #include #include #endif #include "shell.h" #include "vdisk.h" #include "vnet.h" #include "umka.h" #include "trace.h" #include "pci.h" #include "umkart.h" #include "lodepng/lodepng.h" #include "optparse/optparse.h" #include "isocline/include/isocline.h" #define MAX_COMMAND_ARGS 42 #define PRINT_BYTES_PER_LINE 32 #define MAX_DIRENTS_TO_READ 100 #define MAX_BYTES_TO_READ (1024*1024) #define DEFAULT_READDIR_ENCODING UTF8 #define DEFAULT_PATH_ENCODING UTF8 #define SHELL_CMD_BUF_LEN 0x10 enum { UMKA_CMD_NONE, UMKA_CMD_SET_MOUSE_DATA, UMKA_CMD_WAIT_FOR_IDLE, UMKA_CMD_WAIT_FOR_OS_IDLE, UMKA_CMD_WAIT_FOR_WINDOW, UMKA_CMD_SYS_CSLEEP, UMKA_CMD_SYS_PROCESS_INFO, UMKA_CMD_SYS_GET_MOUSE_POS_SCREEN, UMKA_CMD_SYS_LFN, }; enum { SHELL_CMD_STATUS_EMPTY, SHELL_CMD_STATUS_READY, SHELL_CMD_STATUS_DONE, }; struct cmd_set_mouse_data_arg { uint32_t btn_state; int32_t xmoving; int32_t ymoving; int32_t vscroll; int32_t hscroll; }; struct cmd_set_mouse_data_ret { char stub; }; struct cmd_set_mouse_data { struct cmd_set_mouse_data_arg arg; struct cmd_set_mouse_data_ret ret; }; struct cmd_sys_lfn_arg { f70or80_t f70or80; f7080s1arg_t *bufptr; f7080ret_t *r; }; struct cmd_sys_lfn_ret { f7080ret_t status; }; struct cmd_sys_lfn { struct cmd_sys_lfn_arg arg; struct cmd_sys_lfn_ret ret; }; struct cmd_sys_process_info_arg { int32_t pid; void *param; }; struct cmd_sys_process_info_ret { char stub; }; struct cmd_sys_process_info { struct cmd_sys_process_info_arg arg; struct cmd_sys_process_info_ret ret; }; struct cmd_sys_get_mouse_pos_screen_arg { char stub; }; struct cmd_sys_get_mouse_pos_screen_ret { struct point16s pos; }; struct cmd_sys_get_mouse_pos_screen { struct cmd_sys_get_mouse_pos_screen_arg arg; struct cmd_sys_get_mouse_pos_screen_ret ret; }; struct cmd_sys_csleep_arg { uint32_t csec; }; struct cmd_sys_csleep_ret { char stub; }; struct cmd_sys_csleep { struct cmd_sys_csleep_arg arg; struct cmd_sys_csleep_ret ret; }; struct cmd_wait_for_window_arg { char *wnd_title; }; struct cmd_wait_for_window_ret { char stub; }; struct cmd_wait_for_window { struct cmd_wait_for_window_arg arg; struct cmd_wait_for_window_ret ret; }; struct umka_cmd { atomic_int status; uint32_t type; union { // internal funcs struct cmd_set_mouse_data set_mouse_data; struct cmd_wait_for_window wait_for_window; // syscalls struct cmd_sys_csleep sys_csleep; struct cmd_sys_process_info sys_process_info; struct cmd_sys_lfn sys_lfn; struct cmd_sys_get_mouse_pos_screen sys_get_mouse_pos_screen; }; }; struct umka_cmd umka_cmd_buf[SHELL_CMD_BUF_LEN]; char prompt_line[PATH_MAX]; char cur_dir[PATH_MAX] = "/"; const char *last_dir = cur_dir; bool cur_dir_changed = true; typedef struct { char *name; void (*func) (struct shell_ctx *, int, char **); } func_table_t; void shell_run_cmd_sync(struct shell_ctx *ctx); static void shell_run_cmd(struct shell_ctx *ctx) { struct umka_cmd *cmd = umka_cmd_buf; atomic_store_explicit(&cmd->status, SHELL_CMD_STATUS_READY, memory_order_release); if (atomic_load_explicit(ctx->running, memory_order_acquire) == UMKA_RUNNING_YES) { pthread_cond_wait(&ctx->cmd_done, &ctx->cmd_mutex); } else { shell_run_cmd_sync(ctx); } } static uint32_t shell_run_cmd_wait_test(void /* struct appdata * with wait_param is in ebx */) { appdata_t *app; __asm__ __volatile__ __inline__ ("":"=b"(app)::); struct umka_cmd *cmd = (struct umka_cmd*)app->wait_param; return (uint32_t)(atomic_load_explicit(&cmd->status, memory_order_acquire) == SHELL_CMD_STATUS_READY); } static void thread_cmd_runner(void *arg) { umka_sti(); struct shell_ctx *ctx = arg; while (1) { kos_wait_events(shell_run_cmd_wait_test, umka_cmd_buf); shell_run_cmd_sync(ctx); } } const char *f70_status_name[] = { "success", "disk_base", "unsupported_fs", "unknown_fs", "partition", "file_not_found", "end_of_file", "memory_pointer", "disk_full", "fs_fail", "access_denied", "device", "out_of_memory" }; static const char * get_f70_status_name(int s) { switch (s) { case KOS_ERROR_SUCCESS: // return ""; case KOS_ERROR_DISK_BASE: case KOS_ERROR_UNSUPPORTED_FS: case KOS_ERROR_UNKNOWN_FS: case KOS_ERROR_PARTITION: case KOS_ERROR_FILE_NOT_FOUND: case KOS_ERROR_END_OF_FILE: case KOS_ERROR_MEMORY_POINTER: case KOS_ERROR_DISK_FULL: case KOS_ERROR_FS_FAIL: case KOS_ERROR_ACCESS_DENIED: case KOS_ERROR_DEVICE: case KOS_ERROR_OUT_OF_MEMORY: return f70_status_name[s]; default: return "unknown"; } } static void convert_f70_file_attr(uint32_t attr, char s[KF_ATTR_CNT+1]) { s[0] = (attr & KF_READONLY) ? 'r' : '-'; s[1] = (attr & KF_HIDDEN) ? 'h' : '-'; s[2] = (attr & KF_SYSTEM) ? 's' : '-'; s[3] = (attr & KF_LABEL) ? 'l' : '-'; s[4] = (attr & KF_FOLDER) ? 'f' : '-'; s[5] = '\0'; } static void print_f70_status(struct shell_ctx *ctx, f7080ret_t *r, int use_ebx) { fprintf(ctx->fout, "status = %d %s", r->status, get_f70_status_name(r->status)); if (use_ebx && (r->status == KOS_ERROR_SUCCESS || r->status == KOS_ERROR_END_OF_FILE)) { fprintf(ctx->fout, ", count = %d", r->count); } fprintf(ctx->fout, "\n"); } static bool parse_uintmax(const char *str, uintmax_t *res) { char *endptr; *res = strtoumax(str, &endptr, 0); bool ok = (str != endptr) && (*endptr == '\0'); return ok; } static bool parse_uint32(struct shell_ctx *ctx, const char *str, uint32_t *res) { uintmax_t x; if (parse_uintmax(str, &x) && x <= UINT32_MAX) { *res = (uint32_t)x; return true; } else { fprintf(ctx->fout, "invalid number: %s\n", str); return false; } } static bool parse_uint64(struct shell_ctx *ctx, const char *str, uint64_t *res) { uintmax_t x; if (parse_uintmax(str, &x) && x <= UINT64_MAX) { *res = x; return true; } else { fprintf(ctx->fout, "invalid number: %s\n", str); return false; } } static struct shell_var * shell_var_get(struct shell_ctx *ctx, const char *name) { for (struct shell_var *var = ctx->var; var; var = var->next) { if (!strcmp(var->name, name)) { return var; } } return NULL; } static bool shell_parse_sint(struct shell_ctx *ctx, ssize_t *value, const char *s) { if (s[0] == '$') { struct shell_var *var = shell_var_get(ctx, s); if (var) { *value = var->value.sint; return true; } } else { *value = strtol(s, NULL, 0); return true; } return false; } static bool shell_parse_uint(struct shell_ctx *ctx, size_t *value, const char *s) { if (s[0] == '$') { struct shell_var *var = shell_var_get(ctx, s); if (var) { *value = var->value.uint; return true; } } else { *value = strtoul(s, NULL, 0); return true; } return false; } static bool shell_parse_ptr(struct shell_ctx *ctx, void **value, const char *s) { if (s[0] == '$') { struct shell_var *var = shell_var_get(ctx, s); if (var) { *value = var->value.ptr; return true; } } else { *value = (void*)strtoul(s, NULL, 0); return true; } return false; } static bool shell_var_name(struct shell_ctx *ctx, const char *name) { struct shell_var *var = ctx->var; if (!var || var->name[0] != '\0') { return false; } if (name[0] != '$' || strlen(name) >= SHELL_VAR_NAME_LEN) { return false; } strcpy(var->name, name); return true; } struct shell_var * shell_var_new(void) { struct shell_var *var = (struct shell_var*)malloc(sizeof(struct shell_var)); var->next = NULL; var->name[0] = '\0'; return var; } static struct shell_var * shell_var_add(struct shell_ctx *ctx) { struct shell_var *var = ctx->var; struct shell_var *new_var; if (!var) { new_var = shell_var_new(); ctx->var = new_var; } else { if (var->name[0] == '\0') { new_var = var; } else { new_var = shell_var_new(); new_var->next = var; ctx->var = new_var; } } return new_var; } static bool shell_var_add_sint(struct shell_ctx *ctx, ssize_t value) { struct shell_var *var = shell_var_add(ctx); if (!var) { return false; } var->type = SHELL_VAR_SINT; var->value.sint = value; return true; } static bool shell_var_add_uint(struct shell_ctx *ctx, size_t value) { struct shell_var *var = shell_var_add(ctx); if (!var) { return false; } var->type = SHELL_VAR_UINT; var->value.uint = value; return true; } static bool shell_var_add_ptr(struct shell_ctx *ctx, void *value) { struct shell_var *var = shell_var_add(ctx); if (!var) { return false; } var->type = SHELL_VAR_POINTER; var->value.ptr = value; return true; } static void print_bytes(struct shell_ctx *ctx, uint8_t *x, size_t len) { for (size_t i = 0; i < len; i++) { if (i % PRINT_BYTES_PER_LINE == 0 && i != 0) { fprintf(ctx->fout, "\n"); } fprintf(ctx->fout, "%2.2x", x[i]); } fprintf(ctx->fout, "\n"); } static void print_hash(struct shell_ctx *ctx, uint8_t *x, size_t len) { hash_context hash; hash_oneshot(&hash, x, len); for (size_t i = 0; i < HASH_SIZE; i++) { fprintf(ctx->fout, "%2.2x", hash.hash[i]); } fprintf(ctx->fout, "\n"); } void *host_load_file(const char *fname) { FILE *f = fopen(fname, "rb"); if (!f) { return NULL; } fseek(f, 0, SEEK_END); size_t fsize = ftell(f); rewind(f); void *fdata = malloc(fsize); fread(fdata, 1, fsize, f); fclose(f); return fdata; } static const char * get_last_dir(const char *path) { const char *last = strrchr(path, '/'); if (!last) { last = path; } else if (last != path || last[1] != '\0') { last++; } return last; } static void prompt(struct shell_ctx *ctx) { if (cur_dir_changed) { if (ctx->umka->booted) { COVERAGE_ON(); umka_sys_get_cwd(cur_dir, PATH_MAX); COVERAGE_OFF(); } last_dir = get_last_dir(cur_dir); cur_dir_changed = false; } fprintf(ctx->fout, "%s> ", last_dir); fflush(stdout); } static void completer(ic_completion_env_t *cenv, const char *prefix) { (void)cenv; (void)prefix; } static void highlighter(ic_highlight_env_t *henv, const char *input, void *arg) { (void)henv; (void)input; (void)arg; } static int split_args(char *s, char **argv) { int argc = -1; for (; (argv[++argc] = strtok(s, " \t\n\r")) != NULL; s = NULL); return argc; } static void cmd_var(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: var <$name>\n" " $name variable name, must start with $ sign\n"; if (argc != 2) { fputs(usage, ctx->fout); return; } bool status = shell_var_name(ctx, argv[1]); if (!status) { fprintf(ctx->fout, "fail\n"); } } static void cmd_send_scancode(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: send_scancode ...\n" " code dec or hex number\n"; if (argc < 2) { fputs(usage, ctx->fout); return; } argc -= 1; argv += 1; while (argc) { char *endptr; size_t code = strtoul(argv[0], &endptr, 0); if (*endptr == '\0') { umka_set_keyboard_data(code); argc--; argv++; } else { fprintf(ctx->fout, "not an integer: %s\n", argv[0]); fputs(usage, ctx->fout); return; } } } static void cmd_umka_boot(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; const char *usage = \ "usage: umka_boot\n"; if (argc != 1) { fputs(usage, ctx->fout); return; } COVERAGE_ON(); umka_boot(); COVERAGE_OFF(); if (*ctx->running != UMKA_RUNNING_NEVER) { char *stack = malloc(UMKA_DEFAULT_THREAD_STACK_SIZE); char *stack_top = stack + UMKA_DEFAULT_THREAD_STACK_SIZE; size_t tid = umka_new_sys_threads(0, thread_cmd_runner, stack_top, ctx, "cmd_runner"); (void)tid; } } static void cmd_umka_set_boot_params(struct shell_ctx *ctx, int argc, char **argv) { const char *usage = \ "usage: umka_set_boot_params [--x_res ] [--y_res ]\n" " --x_res screen width\n" " --y_res screen height\n" " --bpp screen bits per pixel\n" " --pitch screen line length in bytes\n"; argc -= 1; argv += 1; while (argc) { if (!strcmp(argv[0], "--x_res") && argc > 1) { kos_boot.x_res = strtoul(argv[1], NULL, 0); kos_boot.pitch = kos_boot.x_res * kos_boot.bpp/8; argc -= 2; argv += 2; continue; } else if (!strcmp(argv[0], "--y_res") && argc > 1) { kos_boot.y_res = strtoul(argv[1], NULL, 0); argc -= 2; argv += 2; continue; } else if (!strcmp(argv[0], "--bpp") && argc > 1) { kos_boot.bpp = strtoul(argv[1], NULL, 0); kos_boot.pitch = kos_boot.x_res * kos_boot.bpp/8; argc -= 2; argv += 2; continue; } else if (!strcmp(argv[0], "--pitch") && argc > 1) { kos_boot.pitch = strtoul(argv[1], NULL, 0); argc -= 2; argv += 2; continue; } else { fprintf(ctx->fout, "bad option: %s\n", argv[0]); fputs(usage, ctx->fout); return; } } } static void cmd_csleep(struct shell_ctx *ctx, int argc, char **argv) { const char *usage = \ "usage: csleep\n"; if (argc != 2) { fputs(usage, ctx->fout); return; } struct umka_cmd *cmd = umka_cmd_buf; struct cmd_sys_csleep_arg *c = &cmd->sys_csleep.arg; cmd->type = UMKA_CMD_SYS_CSLEEP; c->csec = strtoul(argv[1], NULL, 0); shell_run_cmd(ctx); atomic_store_explicit(&cmd->status, SHELL_CMD_STATUS_EMPTY, memory_order_release); } static uint32_t umka_wait_for_idle_test(void) { return (uint32_t)(atomic_load_explicit(&idle_scheduled, memory_order_acquire)); } static void umka_wait_for_idle(void) { atomic_store_explicit(&idle_scheduled, 0, memory_order_release); kos_wait_events(umka_wait_for_idle_test, NULL); } static void cmd_wait_for_idle(struct shell_ctx *ctx, int argc, char **argv) { (void)argv; const char *usage = \ "usage: wait_for_idle\n"; if (argc != 1) { fputs(usage, ctx->fout); return; } struct umka_cmd *cmd = umka_cmd_buf; cmd->type = UMKA_CMD_WAIT_FOR_IDLE; shell_run_cmd(ctx); atomic_store_explicit(&cmd->status, SHELL_CMD_STATUS_EMPTY, memory_order_release); } static uint32_t umka_wait_for_os_test(void) { return (uint32_t)(atomic_load_explicit(&os_scheduled, memory_order_acquire)); } static void umka_wait_for_os_idle(void) { atomic_store_explicit(&os_scheduled, 0, memory_order_release); kos_wait_events(umka_wait_for_os_test, NULL); atomic_store_explicit(&idle_scheduled, 0, memory_order_release); kos_wait_events(umka_wait_for_idle_test, NULL); } static void cmd_wait_for_os_idle(struct shell_ctx *ctx, int argc, char **argv) { (void)argv; const char *usage = \ "usage: wait_for_os_idle\n"; if (argc != 1) { fputs(usage, ctx->fout); return; } struct umka_cmd *cmd = umka_cmd_buf; cmd->type = UMKA_CMD_WAIT_FOR_OS_IDLE; shell_run_cmd(ctx); atomic_store_explicit(&cmd->status, SHELL_CMD_STATUS_EMPTY, memory_order_release); } static uint32_t umka_wait_for_window_test(void) { appdata_t *app; wdata_t *wdata; __asm__ __volatile__ __inline__ ("":"=b"(app)::); const char *wnd_title = (const char *)app->wait_param; for (size_t i = 0; i < 256; i++) { app = kos_slot_base + i; wdata = kos_window_data + i; if (app->state != KOS_TSTATE_FREE && wdata->caption && !strcmp(wdata->caption, wnd_title)) { return 1; } } return 0; } static void umka_wait_for_window(char *wnd_title) { kos_wait_events(umka_wait_for_window_test, wnd_title); } static void cmd_wait_for_window(struct shell_ctx *ctx, int argc, char **argv) { (void)argv; const char *usage = \ "usage: wait_for_window \n"; if (argc != 2) { fputs(usage, ctx->fout); return; } struct umka_cmd *cmd = umka_cmd_buf; cmd->type = UMKA_CMD_WAIT_FOR_WINDOW; struct cmd_wait_for_window_arg *c = &cmd->wait_for_window.arg; c->wnd_title = argv[1]; shell_run_cmd(ctx); atomic_store_explicit(&cmd->status, SHELL_CMD_STATUS_EMPTY, memory_order_release); } static void cmd_i40(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: i40 [ebx [ecx [edx [esi [edi [ebp]]]]]]...\n" " see '/kernel/docs/sysfuncs.txt' for details\n"; if (argc < 2 || argc > 8) { fputs(usage, ctx->fout); return; } pushad_t regs = {0, 0, 0, 0, 0, 0, 0, 0}; if (argv[1]) regs.eax = strtoul(argv[1], NULL, 0); if (argv[2]) regs.ebx = strtoul(argv[2], NULL, 0); if (argv[3]) regs.ecx = strtoul(argv[3], NULL, 0); if (argv[4]) regs.edx = strtoul(argv[4], NULL, 0); if (argv[5]) regs.esi = strtoul(argv[5], NULL, 0); if (argv[6]) regs.edi = strtoul(argv[6], NULL, 0); if (argv[7]) regs.ebp = strtoul(argv[7], NULL, 0); COVERAGE_ON(); umka_i40(®s); COVERAGE_OFF(); fprintf(ctx->fout, "eax = %8.8x %" PRIu32 " %" PRIi32 "\n" "ebx = %8.8x %" PRIu32 " %" PRIi32 "\n", regs.eax, regs.eax, (int32_t)regs.eax, regs.ebx, regs.ebx, (int32_t)regs.ebx); } static void bytes_to_kmgtpe(uint64_t *bytes, const char **kmg) { lldiv_t d; *kmg = "B"; if (*bytes == 0) { return; } d = lldiv(*bytes, 1024); if (d.rem != 0) { return; } *bytes = d.quot; *kmg = "kiB"; d = lldiv(*bytes, 1024); if (d.rem != 0) { return; } *bytes = d.quot; *kmg = "MiB"; d = lldiv(*bytes, 1024); if (d.rem != 0) { return; } *bytes = d.quot; *kmg = "GiB"; d = lldiv(*bytes, 1024); if (d.rem != 0) { return; } *bytes = d.quot; *kmg = "TiB"; d = lldiv(*bytes, 1024); if (d.rem != 0) { return; } *bytes = d.quot; *kmg = "PiB"; d = lldiv(*bytes, 1024); if (d.rem != 0) { return; } *bytes = d.quot; *kmg = "EiB"; } static void disk_list_partitions(struct shell_ctx *ctx, disk_t *d) { uint64_t kmgtpe_count = d->media_info.sector_size * d->media_info.capacity; const char *kmgtpe = NULL; bytes_to_kmgtpe(&kmgtpe_count, &kmgtpe); fprintf(ctx->fout, "/%s: sector_size=%u, capacity=%" PRIu64 " (%" PRIu64 " %s), num_partitions=%u\n", d->name, d->media_info.sector_size, d->media_info.capacity, kmgtpe_count, kmgtpe, d->num_partitions); for (size_t i = 0; i < d->num_partitions; i++) { partition_t *p = d->partitions[i]; const char *fsname; if (p->fs_user_functions == xfs_user_functions) { fsname = "xfs"; } else if (p->fs_user_functions == ext_user_functions) { fsname = "ext"; } else if (p->fs_user_functions == fat_user_functions) { fsname = "fat"; } else if (p->fs_user_functions == exfat_user_functions) { fsname = "exfat"; } else if (p->fs_user_functions == ntfs_user_functions) { fsname = "ntfs"; } else { fsname = "???"; } kmgtpe_count = d->media_info.sector_size * p->first_sector; bytes_to_kmgtpe(&kmgtpe_count, &kmgtpe); fprintf(ctx->fout, "/%s/%d: fs=%s, start=%" PRIu64 " (%" PRIu64 " %s)", d->name, i+1, fsname, p->first_sector, kmgtpe_count, kmgtpe); kmgtpe_count = d->media_info.sector_size * p->length; bytes_to_kmgtpe(&kmgtpe_count, &kmgtpe); fprintf(ctx->fout, ", length=%" PRIu64 " (%" PRIu64 " %s)\n", p->length, kmgtpe_count, kmgtpe); } } static void cmd_ramdisk_init(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: ramdisk_init \n" " absolute or relative path\n"; if (argc != 2) { fputs(usage, ctx->fout); return; } const char *fname = argv[1]; FILE *f = fopen(fname, "rb"); if (!f) { fprintf(ctx->fout, "[!] can't open file '%s': %s\n", fname, strerror(errno)); return; } fseek(f, 0, SEEK_END); size_t fsize = ftell(f); if (fsize > RAMDISK_MAX_LEN) { fprintf(ctx->fout, "[!] file '%s' is too big, max size is %d bytes\n", fname, RAMDISK_MAX_LEN); return; } rewind(f); fread(kos_ramdisk, fsize, 1, f); fclose(f); COVERAGE_ON(); void *ramdisk = kos_ramdisk_init(); COVERAGE_OFF(); disk_list_partitions(ctx, ramdisk); } static void cmd_disk_add(struct shell_ctx *ctx, int argc, char **argv) { const char *usage = \ "usage: disk_add [option]...\n" " absolute or relative path\n" " disk name, e.g. hd0 or rd\n" " -c cache size size of disk cache in bytes\n"; if (argc < 3) { fputs(usage, ctx->fout); return; } size_t cache_size = 0; int adjust_cache_size = 0; int opt; optparse_init(&ctx->opts, argv); const char *file_name = optparse_arg(&ctx->opts); const char *disk_name = optparse_arg(&ctx->opts); while ((opt = optparse(&ctx->opts, "c:")) != -1) { switch (opt) { case 'c': cache_size = strtoul(ctx->opts.optarg, NULL, 0); adjust_cache_size = 1; break; default: fputs(usage, ctx->fout); return; } } struct vdisk *umka_disk = vdisk_init(file_name, adjust_cache_size, cache_size, ctx->io); if (umka_disk) { COVERAGE_ON(); disk_t *disk = disk_add(&umka_disk->diskfunc, disk_name, umka_disk, 0); COVERAGE_OFF(); if (disk) { COVERAGE_ON(); disk_media_changed(disk, 1); COVERAGE_OFF(); disk_list_partitions(ctx, disk); return; } } fprintf(ctx->fout, "umka: can't add file '%s' as disk '%s'\n", file_name, disk_name); return; } static void disk_del_by_name(struct shell_ctx *ctx, const char *name) { for(disk_t *d = disk_list.next; d != &disk_list; d = d->next) { if (!strcmp(d->name, name)) { COVERAGE_ON(); disk_del(d); COVERAGE_OFF(); return; } } fprintf(ctx->fout, "umka: can't find disk '%s'\n", name); } static void cmd_disk_del(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: disk_del \n" " name disk name, i.e. rd or hd0\n"; if (argc != 2) { fputs(usage, ctx->fout); return; } const char *name = argv[1]; disk_del_by_name(ctx, name); return; } static void cmd_pwd(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: pwd\n"; if (argc != 1) { fputs(usage, ctx->fout); return; } (void)argv; bool quoted = false; const char *quote = quoted ? "'" : ""; COVERAGE_ON(); umka_sys_get_cwd(cur_dir, PATH_MAX); COVERAGE_OFF(); fprintf(ctx->fout, "%s%s%s\n", quote, cur_dir, quote); } static void cmd_set_pixel(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: set_pixel [-i]\n" " x x window coordinate\n" " y y window coordinate\n" " color argb in hex\n" " -i inverted color\n"; if (argc < 4) { fputs(usage, ctx->fout); return; } size_t x = strtoul(argv[1], NULL, 0); size_t y = strtoul(argv[2], NULL, 0); uint32_t color = strtoul(argv[3], NULL, 16); int invert = (argc == 5) && !strcmp(argv[4], "-i"); COVERAGE_ON(); umka_sys_set_pixel(x, y, color, invert); COVERAGE_OFF(); } static void cmd_write_text(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: write_text " " " " \n" " x x window coordinate\n" " y y window coordinate\n" " color argb in hex\n" " string escape spaces\n" " asciiz 1 if the string is zero-terminated\n" " fill_bg fill text background with specified color\n" " font_and_enc font size and string encoding\n" " draw_to_buf draw to the buffer pointed to by the next param\n" " length length of the string if it is non-asciiz\n" " bg_color_or_buf argb or pointer\n"; if (argc != 12) { fputs(usage, ctx->fout); return; } size_t x = strtoul(argv[1], NULL, 0); size_t y = strtoul(argv[2], NULL, 0); uint32_t color = strtoul(argv[3], NULL, 16); const char *string = argv[4]; int asciiz = strtoul(argv[5], NULL, 0); int fill_background = strtoul(argv[6], NULL, 0); int font_and_encoding = strtoul(argv[7], NULL, 0); int draw_to_buffer = strtoul(argv[8], NULL, 0); int scale_factor = strtoul(argv[9], NULL, 0); int length = strtoul(argv[10], NULL, 0); int background_color_or_buffer = strtoul(argv[11], NULL, 0); COVERAGE_ON(); umka_sys_write_text(x, y, color, asciiz, fill_background, font_and_encoding, draw_to_buffer, scale_factor, string, length, background_color_or_buffer); COVERAGE_OFF(); } static void cmd_get_key(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; const char *usage = \ "usage: get_key\n"; if (argc > 1) { fputs(usage, ctx->fout); return; } fprintf(ctx->fout, "0x%8.8" PRIx32 "\n", umka_get_key()); } static void cmd_dump_key_buff(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: dump_key_buff [count]\n" " count how many items to dump (all by default)\n"; if (argc > 2) { fputs(usage, ctx->fout); return; } int count = INT_MAX; if (argc > 1) { count = strtol(argv[1], NULL, 0); } for (int i = 0; i < count && i < kos_key_count; i++) { fprintf(ctx->fout, "%3i 0x%2.2x 0x%2.2x\n", i, kos_key_buff[i], kos_key_buff[120+2+i]); } } static void cmd_dump_win_stack(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: dump_win_stack [count]\n" " count how many items to dump\n"; if (argc > 2) { fputs(usage, ctx->fout); return; } int depth = 5; if (argc > 1) { depth = strtol(argv[1], NULL, 0); } for (int i = 0; i < depth; i++) { fprintf(ctx->fout, "%3i: %3u\n", i, kos_win_stack[i]); } } static void cmd_dump_win_pos(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: dump_win_pos [count]\n" " count how many items to dump\n"; if (argc < 1) { fputs(usage, ctx->fout); return; } int depth = 5; if (argc > 1) { depth = strtol(argv[1], NULL, 0); } for (int i = 0; i < depth; i++) { fprintf(ctx->fout, "%3i: %3u\n", i, kos_win_pos[i]); } } static void cmd_dump_win_map(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; // TODO: area const char *usage = \ "usage: dump_win_map\n"; if (argc < 0) { fputs(usage, ctx->fout); return; } for (size_t y = 0; y < kos_display.height; y++) { for (size_t x = 0; x < kos_display.width; x++) { fprintf(ctx->fout, "%c", kos_display.win_map[y * kos_display.width + x] + '0'); } fprintf(ctx->fout, "\n"); } } static void cmd_dump_wdata(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: dump_wdata \n" " index index into wdata array to dump\n" " -p print fields that are pointers\n"; if (argc < 2) { fputs(usage, ctx->fout); return; } int show_pointers = 0; int idx = strtol(argv[1], NULL, 0); if (argc > 2 && !strcmp(argv[2], "-p")) { show_pointers = 1; } wdata_t *w = kos_window_data + idx; fprintf(ctx->fout, "captionEncoding: %u\n", w->caption_encoding); fprintf(ctx->fout, "caption: %s\n", w->caption); fprintf(ctx->fout, "clientbox (ltwh): %u %u %u %u\n", w->clientbox.left, w->clientbox.top, w->clientbox.width, w->clientbox.height); fprintf(ctx->fout, "draw_bgr_x: %u\n", w->draw_bgr_x); fprintf(ctx->fout, "draw_bgr_y: %u\n", w->draw_bgr_y); fprintf(ctx->fout, "thread: %u\n", (uintptr_t)(w->thread - kos_slot_base)); if (show_pointers) { fprintf(ctx->fout, "thread: %p\n", (void*)w->thread); fprintf(ctx->fout, "cursor: %p\n", (void*)w->cursor); } } static void cmd_dump_appdata(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: dump_appdata [-p]\n" " index index into appdata array to dump\n" " -p print fields that are pointers\n"; if (argc < 2) { fputs(usage, ctx->fout); return; } int show_pointers = 0; int idx = strtol(argv[1], NULL, 0); if (argc > 2 && !strcmp(argv[2], "-p")) { show_pointers = 1; } appdata_t *a = kos_slot_base + idx; fprintf(ctx->fout, "app_name: %s\n", a->app_name); if (show_pointers) { fprintf(ctx->fout, "process: %p\n", (void*)a->process); fprintf(ctx->fout, "fpu_state: %p\n", (void*)a->fpu_state); fprintf(ctx->fout, "exc_handler: %p\n", (void*)a->exc_handler); } fprintf(ctx->fout, "except_mask: %" PRIx32 "\n", a->except_mask); if (show_pointers) { fprintf(ctx->fout, "pl0_stack: %p\n", (void*)a->pl0_stack); fprintf(ctx->fout, "fd_ev: %p\n", (void*)a->fd_ev); fprintf(ctx->fout, "bk_ev: %p\n", (void*)a->bk_ev); fprintf(ctx->fout, "fd_obj: %p\n", (void*)a->fd_obj); fprintf(ctx->fout, "bk_obj: %p\n", (void*)a->bk_obj); fprintf(ctx->fout, "saved_esp: %p\n", (void*)a->saved_esp); } fprintf(ctx->fout, "dbg_state: %u\n", a->dbg_state); fprintf(ctx->fout, "cur_dir: %s\n", a->cur_dir); fprintf(ctx->fout, "event_mask: %" PRIx32 "\n", a->event_mask); fprintf(ctx->fout, "tid: %" PRId32 "\n", a->tid); fprintf(ctx->fout, "state: 0x%" PRIx8 "\n", a->state); fprintf(ctx->fout, "wnd_number: %" PRIu8 "\n", a->wnd_number); fprintf(ctx->fout, "terminate_protection: %u\n", a->terminate_protection); fprintf(ctx->fout, "keyboard_mode: %u\n", a->keyboard_mode); fprintf(ctx->fout, "exec_params: %s\n", a->exec_params); fprintf(ctx->fout, "priority: %u\n", a->priority); fprintf(ctx->fout, "in_schedule: prev"); if (show_pointers) { fprintf(ctx->fout, " %p", (void*)a->in_schedule.prev); } fprintf(ctx->fout, " (%u), next", (appdata_t*)a->in_schedule.prev - kos_slot_base); if (show_pointers) { fprintf(ctx->fout, " %p", (void*)a->in_schedule.next); } fprintf(ctx->fout, " (%u)\n", (appdata_t*)a->in_schedule.next - kos_slot_base); } static void cmd_switch_to_thread(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: switch_to_thread \n" " thread id to switch to\n"; if (argc != 2) { fputs(usage, ctx->fout); return; } uint8_t tid = strtoul(argv[1], NULL, 0); kos_current_slot_idx = tid; kos_current_slot = kos_slot_base + tid; } static void cmd_get(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: get \n" " variable to get\n"; if (argc != 2) { fputs(usage, ctx->fout); return; } const char *var = argv[1]; if (!strcmp(var, "redraw_background")) { fprintf(ctx->fout, "%i\n", kos_redraw_background); } else if (!strcmp(var, "key_count")) { fprintf(ctx->fout, "%i\n", kos_key_count); } else if (!strcmp(var, "syslang")) { fprintf(ctx->fout, "%i\n", kos_syslang); } else if (!strcmp(var, "keyboard")) { fprintf(ctx->fout, "%i\n", kos_keyboard); } else if (!strcmp(var, "keyboard_mode")) { fprintf(ctx->fout, "%i\n", kos_keyboard_mode); } else { fprintf(ctx->fout, "no such variable: %s\n", var); fputs(usage, ctx->fout); return; } } static void cmd_set(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: set \n" " variable to set\n" " decimal or hex value\n"; if (argc != 3) { fputs(usage, ctx->fout); return; } const char *var = argv[1]; const char *val_str = argv[2]; char *endptr; ssize_t value = strtol(val_str, &endptr, 0); if (*endptr != '\0') { fprintf(ctx->fout, "integer required: %s\n", val_str); return; } if (!strcmp(var, "redraw_background")) { kos_redraw_background = value; } else if (!strcmp(var, "syslang")) { kos_syslang = value; } else if (!strcmp(var, "keyboard")) { kos_keyboard = value; } else if (!strcmp(var, "keyboard_mode")) { kos_keyboard_mode = value; } else { fprintf(ctx->fout, "bad option: %s\n", argv[1]); fputs(usage, ctx->fout); return; } } static void cmd_new_sys_thread(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; // FIXME const char *usage = \ "usage: new_sys_thread\n"; if (!argc) { fputs(usage, ctx->fout); return; } size_t tid = kos_new_sys_threads(0, NULL, NULL); fprintf(ctx->fout, "tid: %u\n", tid); } static void cmd_mouse_move(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: mouse_move [-l] [-m] [-r] [-x {+|-|=}]" " [-y {+|-|=}] [-h {+|-}] [-v {+|-}]\n" " -l left button is held\n" " -m middle button is held\n" " -r right button is held\n" " -x increase, decrease or set x coordinate\n" " -y increase, decrease or set y coordinate\n" " -h scroll horizontally\n" " -v scroll vertically\n"; if (!argc) { fputs(usage, ctx->fout); return; } int lbheld = 0, mbheld = 0, rbheld = 0, xabs = 0, yabs = 0; int32_t xmoving = 0, ymoving = 0, hscroll = 0, vscroll = 0; int opt; optparse_init(&ctx->opts, argv); while ((opt = optparse(&ctx->opts, "lmrx:y:h:v:")) != -1) { switch (opt) { case 'l': lbheld = 1; break; case 'm': mbheld = 1; break; case 'r': rbheld = 1; break; case 'x': switch (*ctx->opts.optarg++) { case '=': xabs = 1; __attribute__ ((fallthrough)); case '+': xmoving = strtol(ctx->opts.optarg, NULL, 0); break; case '-': xmoving = -strtol(ctx->opts.optarg, NULL, 0); break; default: fputs(usage, ctx->fout); return; } break; case 'y': switch (*ctx->opts.optarg++) { case '=': yabs = 1; __attribute__ ((fallthrough)); case '+': ymoving = strtol(ctx->opts.optarg, NULL, 0); break; case '-': ymoving = -strtol(ctx->opts.optarg, NULL, 0); break; default: fputs(usage, ctx->fout); return; } break; case 'h': if ((ctx->opts.optarg[0] != '+') && (ctx->opts.optarg[0] != '-')) { fputs(usage, ctx->fout); return; } hscroll = strtol(ctx->opts.optarg, NULL, 0); break; case 'v': if ((ctx->opts.optarg[0] != '+') && (ctx->opts.optarg[0] != '-')) { fputs(usage, ctx->fout); return; } vscroll = strtol(ctx->opts.optarg, NULL, 0); break; default: fputs(usage, ctx->fout); return; } } uint32_t btn_state = lbheld + (rbheld << 1) + (mbheld << 2) + (yabs << 30) + (xabs << 31); struct umka_cmd *cmd = umka_cmd_buf; struct cmd_set_mouse_data_arg *c = &cmd->set_mouse_data.arg; cmd->type = UMKA_CMD_SET_MOUSE_DATA; c->btn_state = btn_state; c->xmoving = xmoving; c->ymoving = ymoving; c->vscroll = vscroll; c->hscroll = hscroll; shell_run_cmd(ctx); atomic_store_explicit(&cmd->status, SHELL_CMD_STATUS_EMPTY, memory_order_release); } static void cmd_process_info(struct shell_ctx *ctx, int argc, char **argv) { const char *usage = \ "usage: process_info \n" " pid process id to dump, -1 for self\n"; if (argc != 2) { fputs(usage, ctx->fout); return; } process_information_t info; ssize_t pid; shell_parse_sint(ctx, &pid, argv[1]); COVERAGE_ON(); umka_sys_process_info(pid, &info); COVERAGE_OFF(); fprintf(ctx->fout, "cpu_usage: %u\n", info.cpu_usage); fprintf(ctx->fout, "window_stack_position: %u\n", info.window_stack_position); fprintf(ctx->fout, "window_stack_value: %u\n", info.window_stack_value); fprintf(ctx->fout, "process_name: %s\n", info.process_name); fprintf(ctx->fout, "memory_start: 0x%.8" PRIx32 "\n", info.memory_start); fprintf(ctx->fout, "used_memory: %u (0x%x)\n", info.used_memory, info.used_memory); fprintf(ctx->fout, "pid: %u\n", info.pid); fprintf(ctx->fout, "box: %u %u %u %u\n", info.box.left, info.box.top, info.box.width, info.box.height); fprintf(ctx->fout, "slot_state: %u\n", info.slot_state); fprintf(ctx->fout, "client_box: %u %u %u %u\n", info.client_box.left, info.client_box.top, info.client_box.width, info.client_box.height); fprintf(ctx->fout, "wnd_state: 0x%.2" PRIx8 "\n", info.wnd_state); } static void cmd_check_for_event(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; const char *usage = \ "usage: check_for_event\n"; if (argc != 1) { fputs(usage, ctx->fout); return; } COVERAGE_ON(); uint32_t event = umka_sys_check_for_event(); COVERAGE_OFF(); fprintf(ctx->fout, "%" PRIu32 "\n", event); } static void cmd_display_number(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: display_number " " " " \n" " is_pointer if num_or_ptr argument is a pointer\n" " base 0 - dec, 1 - hex, 2 - bin\n" " num_digits how many digits to print\n" " is_qword if 1, is_pointer = 1 and num_or_ptr is pointer\n" " show_lead_zeros 0/1\n" " num_or_ptr number itself or a pointer to it\n" " x x window coord\n" " y y window coord\n" " color argb in hex\n" " fill_bg 0/1\n" " font 0 = 6x9, 1 = 8x16\n" " draw_to_buf 0/1\n" " scale_factor 0 = x1, ..., 7 = x8\n" " bg_color_or_buf depending on flags fill_bg and draw_to_buf\n"; if (argc != 15) { fputs(usage, ctx->fout); return; } int is_pointer = strtoul(argv[1], NULL, 0); int base = strtoul(argv[2], NULL, 0); if (base == 10) base = 0; else if (base == 16) base = 1; else if (base == 2) base = 2; else base = 0; size_t digits_to_display = strtoul(argv[3], NULL, 0); int is_qword = strtoul(argv[4], NULL, 0); int show_leading_zeros = strtoul(argv[5], NULL, 0); uintptr_t number_or_pointer = strtoul(argv[6], NULL, 0); size_t x = strtoul(argv[7], NULL, 0); size_t y = strtoul(argv[8], NULL, 0); uint32_t color = strtoul(argv[9], NULL, 16); int fill_background = strtoul(argv[10], NULL, 0); int font = strtoul(argv[11], NULL, 0); int draw_to_buffer = strtoul(argv[12], NULL, 0); int scale_factor = strtoul(argv[13], NULL, 0); uintptr_t background_color_or_buffer = strtoul(argv[14], NULL, 16); COVERAGE_ON(); umka_sys_display_number(is_pointer, base, digits_to_display, is_qword, show_leading_zeros, number_or_pointer, x, y, color, fill_background, font, draw_to_buffer, scale_factor, background_color_or_buffer); COVERAGE_OFF(); } static void cmd_set_window_colors(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: set_window_colors " " " " \n" " * all colors are in hex\n"; if (argc != (1 + sizeof(system_colors_t)/4)) { fputs(usage, ctx->fout); return; } system_colors_t colors; colors.frame = strtoul(argv[1], NULL, 16); colors.grab = strtoul(argv[2], NULL, 16); colors.work_3d_dark = strtoul(argv[3], NULL, 16); colors.work_3d_light = strtoul(argv[4], NULL, 16); colors.grab_text = strtoul(argv[5], NULL, 16); colors.work = strtoul(argv[6], NULL, 16); colors.work_button = strtoul(argv[7], NULL, 16); colors.work_button_text = strtoul(argv[8], NULL, 16); colors.work_text = strtoul(argv[9], NULL, 16); colors.work_graph = strtoul(argv[10], NULL, 16); COVERAGE_ON(); umka_sys_set_window_colors(&colors); COVERAGE_OFF(); } static void cmd_get_window_colors(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; const char *usage = \ "usage: get_window_colors\n"; if (argc != 1) { fputs(usage, ctx->fout); return; } system_colors_t colors; memset(&colors, 0xaa, sizeof(colors)); COVERAGE_ON(); umka_sys_get_window_colors(&colors); COVERAGE_OFF(); fprintf(ctx->fout, "0x%.8" PRIx32 " frame\n", colors.frame); fprintf(ctx->fout, "0x%.8" PRIx32 " grab\n", colors.grab); fprintf(ctx->fout, "0x%.8" PRIx32 " work_3d_dark\n", colors.work_3d_dark); fprintf(ctx->fout, "0x%.8" PRIx32 " work_3d_light\n", colors.work_3d_light); fprintf(ctx->fout, "0x%.8" PRIx32 " grab_text\n", colors.grab_text); fprintf(ctx->fout, "0x%.8" PRIx32 " work\n", colors.work); fprintf(ctx->fout, "0x%.8" PRIx32 " work_button\n", colors.work_button); fprintf(ctx->fout, "0x%.8" PRIx32 " work_button_text\n", colors.work_button_text); fprintf(ctx->fout, "0x%.8" PRIx32 " work_text\n", colors.work_text); fprintf(ctx->fout, "0x%.8" PRIx32 " work_graph\n", colors.work_graph); } static void cmd_get_skin_height(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; const char *usage = \ "usage: get_skin_height\n"; if (argc != 1) { fputs(usage, ctx->fout); return; } COVERAGE_ON(); uint32_t skin_height = umka_sys_get_skin_height(); COVERAGE_OFF(); fprintf(ctx->fout, "%" PRIu32 "\n", skin_height); } static void cmd_get_screen_area(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; const char *usage = \ "usage: get_screen_area\n"; if (argc != 1) { fputs(usage, ctx->fout); return; } rect_t wa; COVERAGE_ON(); umka_sys_get_screen_area(&wa); COVERAGE_OFF(); fprintf(ctx->fout, "%" PRIu32 " left\n", wa.left); fprintf(ctx->fout, "%" PRIu32 " top\n", wa.top); fprintf(ctx->fout, "%" PRIu32 " right\n", wa.right); fprintf(ctx->fout, "%" PRIu32 " bottom\n", wa.bottom); } static void cmd_set_screen_area(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: set_screen_area \n" " left left x coord\n" " top top y coord\n" " right right x coord (not length!)\n" " bottom bottom y coord\n"; if (argc != 5) { fputs(usage, ctx->fout); return; } rect_t wa; wa.left = strtoul(argv[1], NULL, 0); wa.top = strtoul(argv[2], NULL, 0); wa.right = strtoul(argv[3], NULL, 0); wa.bottom = strtoul(argv[4], NULL, 0); COVERAGE_ON(); umka_sys_set_screen_area(&wa); COVERAGE_OFF(); } static void cmd_get_skin_margins(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; const char *usage = \ "usage: get_skin_margins\n"; if (argc != 1) { fputs(usage, ctx->fout); return; } rect_t wa; COVERAGE_ON(); umka_sys_get_skin_margins(&wa); COVERAGE_OFF(); fprintf(ctx->fout, "%" PRIu32 " left\n", wa.left); fprintf(ctx->fout, "%" PRIu32 " top\n", wa.top); fprintf(ctx->fout, "%" PRIu32 " right\n", wa.right); fprintf(ctx->fout, "%" PRIu32 " bottom\n", wa.bottom); } static void cmd_set_button_style(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; const char *usage = \ "usage: set_button_style