#include "interp.h" #include "arena_alloc.h" #include "context.h" #include "edit.h" #include "mprintf.h" #include "parser.h" #include "self.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static bool run = true; bool interp_is_running (void) { return run; } void interp_shutdown (void) { run = false; } static void human_size (double size, double* out, char** str) { if (size >= 1024.0f && size < 1024.0f * 1024.0f) { *out = (double)size / 1024.0f; *str = "KiB"; } else if (size >= 1024.0f * 1024.0f && size < 1024.0f * 1024.0f * 1024.0f) { *out = (double)size / (1024.0f * 1024.0f); *str = "MiB"; } else if (size >= 1024.0f * 1024.0f * 1024.0f && size < 1024.0f * 1024.0f * 1024.0f * 1024.0f) { *out = (double)size / (1024.0f * 1024.0f * 1024.0f); *str = "GiB"; } else if (size >= 1024.0f * 1024.0f * 1024.0f * 1024.0f && size < 1024.0f * 1024.0f * 1024.0f * 1024.0f * 1024.0f) { *out = (double)size / (1024.0f * 1024.0f * 1024.0f * 1024.0f); *str = "TiB"; } else { *out = 0.0f; *str = "???"; } } static void echo (struct context* context, char** strings, size_t strings_count) { for (size_t i = 0; i < strings_count; i++) cprintf (context, "%s ", strings[i]); } static void mkfile (struct context* context, char** file_paths, size_t files_count) { char volume[VOLUME_MAX]; const char* path; int ret; for (size_t i = 0; i < files_count; i++) { const char* file_path = file_paths[i]; if (!path_parse (file_path, volume, &path)) { cprintf (context, "ERROR bad path '%s'\n", file_path); continue; } if ((ret = volume_open (volume)) < 0) { cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]); continue; } create_file (path); volume_close (); } } static void terminfo (struct context* context) { size_t cols = 0, rows = 0; terminal_dimensions (&cols, &rows); cprintf (context, "%zu x %zu\n", cols, rows); } static void mkdir (struct context* context, char** dir_paths, size_t dirs_count) { char volume[VOLUME_MAX]; const char* path; int ret; for (size_t i = 0; i < dirs_count; i++) { const char* dir_path = dir_paths[i]; if (!path_parse (dir_path, volume, &path)) { cprintf (context, "ERROR bad path '%s'\n", dir_path); continue; } if ((ret = volume_open (volume)) < 0) { cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]); continue; } create_dir (path); volume_close (); } } static void rm (struct context* context, char** paths, size_t count) { char volume[VOLUME_MAX]; const char* path; int ret; for (size_t i = 0; i < count; i++) { const char* path1 = paths[i]; if (!path_parse (path1, volume, &path)) { cprintf (context, "ERROR bad path '%s'\n", path1); continue; } if ((ret = volume_open (volume)) < 0) { cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]); continue; } remove (path); volume_close (); } } static void cat (struct context* context, char** file_paths, size_t files_count) { char volume[VOLUME_MAX]; const char* path; int ret; for (size_t i = 0; i < files_count; i++) { const char* file_path = file_paths[i]; if (!path_parse (file_path, volume, &path)) { cprintf (context, "ERROR bad path '%s'\n", file_path); continue; } struct filereader fr; if ((ret = filereader_init (&fr, volume, path)) < 0) { cprintf (context, "ERROR could not initialize filereader for '%s:%s'\n", volume, path); return; } size_t chunk_size = 1024; char* buffer = arena_malloc (&arena, chunk_size); size_t chunks = fr.file_size / chunk_size; size_t rem = fr.file_size % chunk_size; for (size_t chunk = 0; chunk < chunks; chunk++) { if ((ret = filereader_read (&fr, (uint8_t*)buffer, chunk_size)) < 0) { filereader_fini (&fr); cprintf (context, "ERROR filereader failed to read from '%s:%s'\n", volume, path); return; } mail_send (e_pgid, buffer, chunk_size); } if (rem > 0) { if ((ret = filereader_read (&fr, (uint8_t*)buffer, rem)) < 0) { filereader_fini (&fr); cprintf (context, "ERROR filereader failed to read from '%s:%s'\n", volume, path); return; } mail_send (e_pgid, buffer, rem); } filereader_fini (&fr); } } static void ls (struct context* context, const char* path_string) { struct desc desc; char volume[VOLUME_MAX]; const char* path; int ret; struct dir_entry entry; if (!path_parse (path_string, volume, &path)) { cprintf (context, "ERROR bad path '%s'\n", path_string); return; } if ((ret = volume_open (volume)) < 0) { cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]); return; } describe (path, &desc); if (desc.type != FS_DIR) { cprintf (context, "ERROR '%s' is not a directory\n", path_string); volume_close (); return; } size_t entries = desc.size; cprintf (context, "total entries: %zu\n", entries); cprintf (context, "\n"); for (size_t entry_num = 0; entry_num < entries; entry_num++) { memset (&desc, 0, sizeof (desc)); memset (&entry, 0, sizeof (entry)); read_dir_entry (path, &entry, entry_num); describe (entry.path, &desc); char* hs_string; double hs; human_size ((double)desc.size, &hs, &hs_string); char size_buf[64]; snprintf (size_buf, sizeof (size_buf), "%.2f %s", hs, hs_string); char type = (desc.type == FS_DIR ? 'D' : 'F'); cprintf (context, "%c %-40s %-40s\n", type, entry.path, size_buf); } volume_close (); } static void edit (struct context* context, const char* path_string) { struct desc desc; char volume[VOLUME_MAX]; const char* path; int ret; if (!path_parse (path_string, volume, &path)) { cprintf (context, "ERROR bad path '%s'\n", path_string); return; } if ((ret = volume_open (volume)) < 0) { cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]); return; } if ((ret = describe (path, &desc)) < 0) { cprintf (context, "ERROR could not describe '%s': %s\n", path, str_status[-ret]); volume_close (); return; } char* str = malloc (desc.size + 1); if (str == NULL) { volume_close (); return; } memset (str, 0, desc.size); if ((ret = read_file (path, 0, (uint8_t*)str, desc.size)) < 0) { free (str); volume_close (); return; } edit_start (path_string, str); volume_close (); } static void quit1 (struct context* context) { cprintf (context, "Goodbye!\n"); interp_shutdown (); } static void help (struct context* context) { cprintf (context, "Available commands:\n"); cprintf (context, "help\n"); cprintf (context, "echo ...\n"); cprintf (context, "cat \n"); cprintf (context, "ls \n"); cprintf (context, "mkfile \n"); cprintf (context, "mkdir \n"); cprintf (context, "rm \n"); cprintf (context, "edit \n"); cprintf (context, "terminfo\n"); cprintf (context, "quit\n"); } static void cmd_cancel_proc (void) { int pid = *(int*)argument_ptr (); char ch = 0; for (;;) { mail_receive (&ch, 1); if (ch == KB_CTRL ('C')) break; } kill (pid); quit (); } static void execute_cmd (struct ast_cmd* cmd, struct context* context) { if (strcmp (cmd->name, "echo") == 0) { echo (context, cmd->args, cmd->arg_count); } else if (strcmp (cmd->name, "help") == 0) { help (context); } else if (strcmp (cmd->name, "cat") == 0) { cat (context, cmd->args, cmd->arg_count); } else if (strcmp (cmd->name, "ls") == 0) { ls (context, cmd->args[0]); } else if (strcmp (cmd->name, "quit") == 0) { quit1 (context); } else if (strcmp (cmd->name, "mkfile") == 0) { mkfile (context, cmd->args, cmd->arg_count); } else if (strcmp (cmd->name, "mkdir") == 0) { mkdir (context, cmd->args, cmd->arg_count); } else if (strcmp (cmd->name, "rm") == 0) { rm (context, cmd->args, cmd->arg_count); } else if (strcmp (cmd->name, "edit") == 0) { edit (context, cmd->args[0]); } else if (strcmp (cmd->name, "terminfo") == 0) { terminfo (context); } else { char volume[VOLUME_MAX]; const char* path; if (!(path_parse (cmd->name, volume, &path))) { cprintf (context, "ERROR bad path '%s'\n", cmd->name); return; } int pid = exec (volume, path); if (pid < 0) { cprintf (context, "ERROR could not run '%s': %s\n", cmd->name, str_status[-pid]); return; } int cancel_pid = process_spawn (&cmd_cancel_proc, &pid); wait_for_pid (pid); kill (cancel_pid); } } static void execute_redir (struct ast_redir* redir, struct context* context) { char volume[VOLUME_MAX]; const char* path; int ret; if (!(path_parse (redir->file_path, volume, &path))) { cprintf (context, "ERROR bad path '%s'\n", redir->file_path); return; } struct filewriter fw; if ((ret = filewriter_init (&fw, volume, path, FW_CREATE_FILE | FW_APPEND)) < 0) { cprintf (context, "ERROR could not initialize filewriter for '%s:%s'\n", volume, path); return; } size_t chunk_size = 1024; size_t chunks = (context->strbuf.count - 1) / chunk_size; size_t rem = (context->strbuf.count - 1) % chunk_size; for (size_t chunk = 0; chunk < chunks; chunk++) { if ((ret = filewriter_write (&fw, (uint8_t*)&context->strbuf.items[chunk * chunk_size], chunk_size)) < 0) { filewriter_fini (&fw); cprintf (context, "ERROR filewriter failed to write to '%s:%s'\n", volume, path); return; } } if (rem > 0) { if ((ret = filewriter_write (&fw, (uint8_t*)&context->strbuf.items[chunks * chunk_size], rem)) < 0) { filewriter_fini (&fw); cprintf (context, "ERROR filewriter failed to write to '%s:%s'\n", volume, path); return; } } filewriter_fini (&fw); } void execute (struct ast_node* root, struct context* context) { struct context subcontext; memset (&subcontext, 0, sizeof (subcontext)); switch (root->class) { case AST_NODE_CLASS_CMD: execute_cmd (&root->u.cmd, context); break; case AST_NODE_CLASS_SUBSHELL: execute (root->u.subshell.inner, &subcontext); break; case AST_NODE_CLASS_SEQ: execute (root->u.seq.left, context); execute (root->u.seq.right, context); break; case AST_NODE_CLASS_REDIR: execute (root->u.redir.source, &subcontext); execute_redir (&root->u.redir, &subcontext); break; } }