All checks were successful
Build documentation / build-and-deploy (push) Successful in 2m10s
414 lines
11 KiB
C
414 lines
11 KiB
C
#include "interp.h"
|
|
#include "arena_alloc.h"
|
|
#include "context.h"
|
|
#include "edit.h"
|
|
#include "mprintf.h"
|
|
#include "parser.h"
|
|
#include "self.h"
|
|
#include <desc.h>
|
|
#include <filereader.h>
|
|
#include <filewriter.h>
|
|
#include <kb.h>
|
|
#include <liballoc.h>
|
|
#include <path.h>
|
|
#include <printf.h>
|
|
#include <process.h>
|
|
#include <stddef.h>
|
|
#include <str_status.h>
|
|
#include <string.h>
|
|
#include <system.h>
|
|
#include <terminal.h>
|
|
|
|
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 <word1> <word2> <word3> ...\n");
|
|
cprintf (context, "cat <file path>\n");
|
|
cprintf (context, "ls <directory path>\n");
|
|
cprintf (context, "mkfile <file path>\n");
|
|
cprintf (context, "mkdir <directory path>\n");
|
|
cprintf (context, "rm <path>\n");
|
|
cprintf (context, "edit <path>\n");
|
|
cprintf (context, "terminfo\n");
|
|
cprintf (context, "quit\n");
|
|
}
|
|
|
|
static void cmd_cancel_proc (void* arg) {
|
|
int pid = *(int*)arg;
|
|
|
|
char ch = 0;
|
|
for (;;) {
|
|
mail_receive (&ch, 1);
|
|
|
|
if (ch == KB_CTRL ('C'))
|
|
break;
|
|
}
|
|
|
|
kill (pid);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
struct process_data* cancel_pdata = process_spawn (&cmd_cancel_proc, &pid);
|
|
|
|
wait_for_pid (pid);
|
|
|
|
if (kill (cancel_pdata->pid) < 0)
|
|
free (cancel_pdata);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|