From 2a891fcdd1cb46e17563d755f312d831d02b92ce Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Tue, 3 Mar 2026 20:59:21 +0100 Subject: [PATCH] Split CE code into multiple files --- ce/arena_alloc.c | 4 + ce/arena_alloc.h | 8 + ce/ce.c | 598 +---------------------------------------------- ce/context.c | 29 +++ ce/context.h | 14 ++ ce/interp.c | 274 ++++++++++++++++++++++ ce/interp.h | 13 ++ ce/mprintf.c | 27 +++ ce/mprintf.h | 10 + ce/parser.c | 182 +++++++++++++++ ce/parser.h | 92 ++++++++ ce/self.c | 4 + ce/self.h | 7 + ce/src.mk | 18 +- ce/strbuf.c | 24 ++ ce/strbuf.h | 15 ++ 16 files changed, 726 insertions(+), 593 deletions(-) create mode 100644 ce/arena_alloc.c create mode 100644 ce/arena_alloc.h create mode 100644 ce/context.c create mode 100644 ce/context.h create mode 100644 ce/interp.c create mode 100644 ce/interp.h create mode 100644 ce/mprintf.c create mode 100644 ce/mprintf.h create mode 100644 ce/parser.c create mode 100644 ce/parser.h create mode 100644 ce/self.c create mode 100644 ce/self.h create mode 100644 ce/strbuf.c create mode 100644 ce/strbuf.h diff --git a/ce/arena_alloc.c b/ce/arena_alloc.c new file mode 100644 index 0000000..021019c --- /dev/null +++ b/ce/arena_alloc.c @@ -0,0 +1,4 @@ +#include "arena_alloc.h" +#include + +struct arena arena; diff --git a/ce/arena_alloc.h b/ce/arena_alloc.h new file mode 100644 index 0000000..0174304 --- /dev/null +++ b/ce/arena_alloc.h @@ -0,0 +1,8 @@ +#ifndef _ARENA_ALLOC_H +#define _ARENA_ALLOC_H + +#include + +extern struct arena arena; + +#endif // _ARENA_ALLOC_H diff --git a/ce/ce.c b/ce/ce.c index 5316eb4..d3468bf 100644 --- a/ce/ce.c +++ b/ce/ce.c @@ -1,3 +1,9 @@ +#include "arena_alloc.h" +#include "context.h" +#include "interp.h" +#include "mprintf.h" +#include "self.h" +#include "strbuf.h" #include #include #include @@ -16,599 +22,9 @@ #include #include -#define CTRL(x) ((x) - '@') -#define CPRINTF_BUF_MAX (1024 * 16) - -static struct arena arena; - -struct strbuf { - char* items; - size_t count, capacity; -}; - -void strbuf_append (struct strbuf* strbuf, char c) { - if (strbuf->items == NULL) { - strbuf->capacity = 256; - strbuf->count = 0; - strbuf->items = arena_malloc (&arena, strbuf->capacity); - } else { - if (strbuf->count == strbuf->capacity) { - size_t prev_capacity = strbuf->capacity; - strbuf->capacity *= 2; - strbuf->items = arena_realloc (&arena, strbuf->items, prev_capacity, strbuf->capacity); - } - } - strbuf->items[strbuf->count++] = c; -} - -void strbuf_append_str (struct strbuf* strbuf, const char* s) { - while (*s) - strbuf_append (strbuf, *s++); -} - -struct context { - struct strbuf strbuf; -}; - -void cprintf (struct context* context, const char* fmt, ...) { - va_list args; - va_start (args, fmt); - - char* buf = malloc (CPRINTF_BUF_MAX); - - if (buf == NULL) { - va_end (args); - return; - } - - int len = vsnprintf (buf, CPRINTF_BUF_MAX, fmt, args); - - va_end (args); - - if (len > 0) { - for (int i = 0; i < len; i++) - strbuf_append (&context->strbuf, buf[i]); - } - - free (buf); -} - #define LINE_BUFFER_MAX 1024 -#define TOKEN_MAX 64 -#define CMD_ARGS_MAX 64 #define PROMPT "$ " -#define TOKEN_CLASS_OPAREN 0 -#define TOKEN_CLASS_CPAREN 1 -#define TOKEN_CLASS_WORD 2 -#define TOKEN_CLASS_SEMICOLON 3 -#define TOKEN_CLASS_REDIR 4 - -struct token { - struct list_node_link tokens_link; - char buffer[TOKEN_MAX]; - int class; -}; - -#define PREC_NONE 0 -#define PREC_LOWEST 1 -#define PREC_SEQ 2 -#define PREC_REDIR 3 - -#define AST_NODE_CLASS_CMD 0 -#define AST_NODE_CLASS_SUBSHELL 1 -#define AST_NODE_CLASS_SEQ 2 -#define AST_NODE_CLASS_REDIR 3 - -struct ast_node; - -struct ast_cmd { - char* name; - char* args[CMD_ARGS_MAX]; - int arg_count; -}; - -struct ast_subshell { - struct ast_node* inner; -}; - -struct ast_seq { - struct ast_node* left; - struct ast_node* right; -}; - -struct ast_redir { - struct ast_node* source; - char* file_path; -}; - -struct ast_node { - int class; - union { - struct ast_cmd cmd; - struct ast_subshell subshell; - struct ast_seq seq; - struct ast_redir redir; - } u; -}; - -struct parser; - -typedef struct ast_node* (*nud_func_t) (struct parser* parser, struct token* token); -typedef struct ast_node* (*led_func_t) (struct parser* parser, struct token* token, - struct ast_node* left); - -struct parse_rule { - nud_func_t nud; - led_func_t led; - int precedence; -}; - -struct parser { - struct token* current; - struct token* next; -}; - -static struct ast_node* word_nud (struct parser* parser, struct token* token); - -static struct ast_node* oparen_nud (struct parser* parser, struct token* token); - -static struct ast_node* semicolon_led (struct parser* parser, struct token* token, - struct ast_node* left); - -static struct ast_node* redir_led (struct parser* parser, struct token* token, - struct ast_node* left); - -static struct parse_rule parse_rules[] = { - [TOKEN_CLASS_WORD] = {&word_nud, NULL, PREC_NONE}, - [TOKEN_CLASS_OPAREN] = {&oparen_nud, NULL, PREC_NONE}, - [TOKEN_CLASS_CPAREN] = {NULL, NULL, PREC_NONE}, - [TOKEN_CLASS_SEMICOLON] = {NULL, &semicolon_led, PREC_SEQ}, - [TOKEN_CLASS_REDIR] = {NULL, &redir_led, PREC_REDIR}, -}; - -static struct token* get_token (struct list_node_link* link) { - if (link == NULL) - return NULL; - - return list_entry (link, struct token, tokens_link); -} - -static struct token* advance (struct parser* parser) { - struct token* token = parser->next; - - if (token != NULL) { - parser->current = token; - parser->next = get_token (token->tokens_link.next); - } else { - parser->current = NULL; - } - - return token; -} - -static struct ast_node* parse_precedence (struct parser* parser, int precedence) { - struct token* token = advance (parser); - - if (token == NULL) - return NULL; - - nud_func_t nud = parse_rules[token->class].nud; - - if (nud == NULL) - return NULL; - - struct ast_node* left = nud (parser, token); - - while (parser->next && precedence < parse_rules[parser->next->class].precedence) { - token = advance (parser); - led_func_t led = parse_rules[token->class].led; - - if (led != NULL) - left = led (parser, token, left); - } - - return left; -} - -static struct ast_node* word_nud (struct parser* parser, struct token* token) { - struct ast_node* node = arena_malloc (&arena, sizeof (*node)); - node->class = AST_NODE_CLASS_CMD; - node->u.cmd.name = token->buffer; - node->u.cmd.arg_count = 0; - - while (parser->next != NULL && parser->next->class == TOKEN_CLASS_WORD) { - struct token* arg = advance (parser); - - if (node->u.cmd.arg_count < CMD_ARGS_MAX) - node->u.cmd.args[node->u.cmd.arg_count++] = arg->buffer; - } - - return node; -} - -static struct ast_node* oparen_nud (struct parser* parser, struct token* token) { - struct ast_node* node = arena_malloc (&arena, sizeof (*node)); - node->class = AST_NODE_CLASS_SUBSHELL; - - node->u.subshell.inner = parse_precedence (parser, PREC_LOWEST); - - if (parser->next != NULL && parser->next->class == TOKEN_CLASS_CPAREN) - advance (parser); - - return node; -} - -static struct ast_node* semicolon_led (struct parser* parser, struct token* token, - struct ast_node* left) { - struct ast_node* node = arena_malloc (&arena, sizeof (*node)); - node->class = AST_NODE_CLASS_SEQ; - node->u.seq.left = left; - node->u.seq.right = parse_precedence (parser, PREC_SEQ); - - return node; -} - -static struct ast_node* redir_led (struct parser* parser, struct token* token, - struct ast_node* left) { - struct ast_node* node = arena_malloc (&arena, sizeof (*node)); - node->class = AST_NODE_CLASS_REDIR; - node->u.redir.source = left; - - struct token* next_token = advance (parser); - if (next_token != NULL && next_token->class == TOKEN_CLASS_WORD) - node->u.redir.file_path = next_token->buffer; - - return node; -} - -static int e_pid; -static int e_pgid; - -static bool run = true; - -void putchar_ (char ch) { (void)ch; } - -void mprintf (const char* fmt, ...) { - va_list args; - va_start (args, fmt); - - char* buf = malloc (CPRINTF_BUF_MAX); - - if (buf == NULL) { - va_end (args); - return; - } - - int len = vsnprintf (buf, CPRINTF_BUF_MAX, fmt, args); - - va_end (args); - - mail_send (e_pgid, buf, len); - free (buf); -} - -static void tokenize (struct list_node_link** tokens, const char* text) { - const char* p = text; - - while (*p) { - if (isspace (*p)) { - p++; - continue; - } - - if (*p == '(' || *p == ')' || *p == ';' || *p == '>') { - struct token* token = arena_malloc (&arena, sizeof (*token)); - memset (token, 0, sizeof (*token)); - - token->buffer[0] = *p; - if (*p == '(') - token->class = TOKEN_CLASS_OPAREN; - else if (*p == ')') - token->class = TOKEN_CLASS_CPAREN; - else if (*p == ';') - token->class = TOKEN_CLASS_SEMICOLON; - else if (*p == '>') - token->class = TOKEN_CLASS_REDIR; - - list_append (*tokens, &token->tokens_link); - p++; - continue; - } - - if (isprint (*p)) { - struct token* token = arena_malloc (&arena, sizeof (*token)); - memset (token, 0, sizeof (*token)); - size_t i = 0; - - while (*p && !isspace (*p) && *p != '(' && *p != ')' && *p != ';' && *p != '>') { - if (i < TOKEN_MAX - 1) - token->buffer[i++] = *p; - p++; - } - - token->class = TOKEN_CLASS_WORD; - list_append (*tokens, &token->tokens_link); - continue; - } - - mprintf ("ERROR unknown character '%c'\n", *p); - p++; - } -} - -static void execute (struct ast_node* root, struct context* context); - -static void parse_and_execute (struct list_node_link* tokens) { - struct parser parser; - parser.current = NULL; - parser.next = get_token (tokens); - - while (parser.next != NULL) { - struct ast_node* root = parse_precedence (&parser, PREC_NONE); - - if (root != NULL) { - struct context context = {0}; - execute (root, &context); - - if (context.strbuf.items != NULL) - mprintf ("%.*s", (int)context.strbuf.count, context.strbuf.items); - } - } -} - -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 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); - - cprintf (context, "%c %-40s %-40zu\n", (desc.type == FS_DIR ? 'D' : 'F'), entry.path, - desc.size); - } - - volume_close (); -} - -static void quit1 (struct context* context) { - run = false; - cprintf (context, "Goodbye!\n"); -} - -static void help (struct context* context) { - cprintf (context, "Available commands:\n"); - cprintf (context, "echo ...\n"); - cprintf (context, "help\n"); - cprintf (context, "cat \n"); - cprintf (context, "ls \n"); - cprintf (context, "mkfile \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 == 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 { - /* cprintf (context, "ERROR unknown command '%s'\n", cmd->name); */ - 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); -} - -static void execute (struct ast_node* root, struct context* context) { - struct context subcontext = {0}; - - 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; - } -} - static void exec_line (const char* line) { struct list_node_link* tokens = NULL; @@ -628,7 +44,7 @@ void app_main (void) { char line_buffer[LINE_BUFFER_MAX + 1]; memset (line_buffer, 0, sizeof (line_buffer)); - while (run) { + while (interp_is_running ()) { mprintf (PROMPT); char ch = 0; diff --git a/ce/context.c b/ce/context.c new file mode 100644 index 0000000..d7f1603 --- /dev/null +++ b/ce/context.c @@ -0,0 +1,29 @@ +#include "context.h" +#include "strbuf.h" +#include +#include +#include +#include + +void cprintf (struct context* context, const char* fmt, ...) { + va_list args; + va_start (args, fmt); + + char* buf = malloc (CPRINTF_BUF_MAX); + + if (buf == NULL) { + va_end (args); + return; + } + + int len = vsnprintf (buf, CPRINTF_BUF_MAX, fmt, args); + + va_end (args); + + if (len > 0) { + for (int i = 0; i < len; i++) + strbuf_append (&context->strbuf, buf[i]); + } + + free (buf); +} diff --git a/ce/context.h b/ce/context.h new file mode 100644 index 0000000..321301d --- /dev/null +++ b/ce/context.h @@ -0,0 +1,14 @@ +#ifndef _CONTEXT_H +#define _CONTEXT_H + +#include "strbuf.h" + +#define CPRINTF_BUF_MAX (1024 * 16) + +struct context { + struct strbuf strbuf; +}; + +void cprintf (struct context* context, const char* fmt, ...); + +#endif // _CONTEXT_H diff --git a/ce/interp.c b/ce/interp.c new file mode 100644 index 0000000..c1f85b4 --- /dev/null +++ b/ce/interp.c @@ -0,0 +1,274 @@ +#include "interp.h" +#include "arena_alloc.h" +#include "context.h" +#include "parser.h" +#include "self.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CTRL(x) ((x) - '@') + +static bool run = true; + +bool interp_is_running (void) { return run; } + +void interp_shutdown (void) { run = false; } + +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 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); + + cprintf (context, "%c %-40s %-40zu\n", (desc.type == FS_DIR ? 'D' : 'F'), entry.path, + desc.size); + } + + 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, "echo ...\n"); + cprintf (context, "help\n"); + cprintf (context, "cat \n"); + cprintf (context, "ls \n"); + cprintf (context, "mkfile \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 == 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 { + /* cprintf (context, "ERROR unknown command '%s'\n", cmd->name); */ + 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 = {0}; + + 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; + } +} diff --git a/ce/interp.h b/ce/interp.h new file mode 100644 index 0000000..a05881a --- /dev/null +++ b/ce/interp.h @@ -0,0 +1,13 @@ +#ifndef _INTERP_H +#define _INTERP_H + +#include "context.h" +#include "parser.h" + +void execute (struct ast_node* root, struct context* context); + +bool interp_is_running (void); + +void interp_shutdown (void); + +#endif // _INTERP_H diff --git a/ce/mprintf.c b/ce/mprintf.c new file mode 100644 index 0000000..999103d --- /dev/null +++ b/ce/mprintf.c @@ -0,0 +1,27 @@ +#include "mprintf.h" +#include "self.h" +#include +#include +#include +#include + +void putchar_ (char ch) { (void)ch; } + +void mprintf (const char* fmt, ...) { + va_list args; + va_start (args, fmt); + + char* buf = malloc (MPRINTF_BUF_MAX); + + if (buf == NULL) { + va_end (args); + return; + } + + int len = vsnprintf (buf, MPRINTF_BUF_MAX, fmt, args); + + va_end (args); + + mail_send (e_pgid, buf, len); + free (buf); +} diff --git a/ce/mprintf.h b/ce/mprintf.h new file mode 100644 index 0000000..094b00a --- /dev/null +++ b/ce/mprintf.h @@ -0,0 +1,10 @@ +#ifndef _MPRINTF_H +#define _MPRINTF_H + +#include "context.h" + +#define MPRINTF_BUF_MAX CPRINTF_BUF_MAX + +void mprintf (const char* fmt, ...); + +#endif // _MPRINTF_H diff --git a/ce/parser.c b/ce/parser.c new file mode 100644 index 0000000..6ca8bc8 --- /dev/null +++ b/ce/parser.c @@ -0,0 +1,182 @@ +#include "parser.h" +#include "arena_alloc.h" +#include "context.h" +#include "interp.h" +#include "mprintf.h" +#include +#include +#include + +static struct parse_rule parse_rules[] = { + [TOKEN_CLASS_WORD] = {&word_nud, NULL, PREC_NONE}, + [TOKEN_CLASS_OPAREN] = {&oparen_nud, NULL, PREC_NONE}, + [TOKEN_CLASS_CPAREN] = {NULL, NULL, PREC_NONE}, + [TOKEN_CLASS_SEMICOLON] = {NULL, &semicolon_led, PREC_SEQ}, + [TOKEN_CLASS_REDIR] = {NULL, &redir_led, PREC_REDIR}, +}; + +static struct token* get_token (struct list_node_link* link) { + if (link == NULL) + return NULL; + + return list_entry (link, struct token, tokens_link); +} + +static struct token* advance (struct parser* parser) { + struct token* token = parser->next; + + if (token != NULL) { + parser->current = token; + parser->next = get_token (token->tokens_link.next); + } else { + parser->current = NULL; + } + + return token; +} + +static struct ast_node* parse_precedence (struct parser* parser, int precedence) { + struct token* token = advance (parser); + + if (token == NULL) + return NULL; + + nud_func_t nud = parse_rules[token->class].nud; + + if (nud == NULL) + return NULL; + + struct ast_node* left = nud (parser, token); + + while (parser->next && precedence < parse_rules[parser->next->class].precedence) { + token = advance (parser); + led_func_t led = parse_rules[token->class].led; + + if (led != NULL) + left = led (parser, token, left); + } + + return left; +} + +struct ast_node* word_nud (struct parser* parser, struct token* token) { + struct ast_node* node = arena_malloc (&arena, sizeof (*node)); + node->class = AST_NODE_CLASS_CMD; + node->u.cmd.name = token->buffer; + node->u.cmd.arg_count = 0; + + while (parser->next != NULL && parser->next->class == TOKEN_CLASS_WORD) { + struct token* arg = advance (parser); + + if (node->u.cmd.arg_count < CMD_ARGS_MAX) + node->u.cmd.args[node->u.cmd.arg_count++] = arg->buffer; + } + + return node; +} + +struct ast_node* oparen_nud (struct parser* parser, struct token* token) { + (void)token; + + struct ast_node* node = arena_malloc (&arena, sizeof (*node)); + node->class = AST_NODE_CLASS_SUBSHELL; + + node->u.subshell.inner = parse_precedence (parser, PREC_LOWEST); + + if (parser->next != NULL && parser->next->class == TOKEN_CLASS_CPAREN) + advance (parser); + + return node; +} + +struct ast_node* semicolon_led (struct parser* parser, struct token* token, struct ast_node* left) { + (void)token; + + struct ast_node* node = arena_malloc (&arena, sizeof (*node)); + node->class = AST_NODE_CLASS_SEQ; + node->u.seq.left = left; + node->u.seq.right = parse_precedence (parser, PREC_SEQ); + + return node; +} + +struct ast_node* redir_led (struct parser* parser, struct token* token, struct ast_node* left) { + (void)token; + + struct ast_node* node = arena_malloc (&arena, sizeof (*node)); + node->class = AST_NODE_CLASS_REDIR; + node->u.redir.source = left; + + struct token* next_token = advance (parser); + if (next_token != NULL && next_token->class == TOKEN_CLASS_WORD) + node->u.redir.file_path = next_token->buffer; + + return node; +} + +void tokenize (struct list_node_link** tokens, const char* text) { + const char* p = text; + + while (*p) { + if (isspace (*p)) { + p++; + continue; + } + + if (*p == '(' || *p == ')' || *p == ';' || *p == '>') { + struct token* token = arena_malloc (&arena, sizeof (*token)); + memset (token, 0, sizeof (*token)); + + token->buffer[0] = *p; + if (*p == '(') + token->class = TOKEN_CLASS_OPAREN; + else if (*p == ')') + token->class = TOKEN_CLASS_CPAREN; + else if (*p == ';') + token->class = TOKEN_CLASS_SEMICOLON; + else if (*p == '>') + token->class = TOKEN_CLASS_REDIR; + + list_append (*tokens, &token->tokens_link); + p++; + continue; + } + + if (isprint (*p)) { + struct token* token = arena_malloc (&arena, sizeof (*token)); + memset (token, 0, sizeof (*token)); + size_t i = 0; + + while (*p && !isspace (*p) && *p != '(' && *p != ')' && *p != ';' && *p != '>') { + if (i < TOKEN_MAX - 1) + token->buffer[i++] = *p; + p++; + } + + token->class = TOKEN_CLASS_WORD; + list_append (*tokens, &token->tokens_link); + continue; + } + + mprintf ("ERROR unknown character '%c'\n", *p); + p++; + } +} + +void parse_and_execute (struct list_node_link* tokens) { + struct parser parser; + parser.current = NULL; + parser.next = get_token (tokens); + + while (parser.next != NULL) { + struct ast_node* root = parse_precedence (&parser, PREC_NONE); + + if (root != NULL) { + struct context context = {0}; + execute (root, &context); + + if (context.strbuf.items != NULL) + mprintf ("%.*s", (int)context.strbuf.count, context.strbuf.items); + } + } +} diff --git a/ce/parser.h b/ce/parser.h new file mode 100644 index 0000000..a306bae --- /dev/null +++ b/ce/parser.h @@ -0,0 +1,92 @@ +#ifndef _PARSER_H +#define _PARSER_H + +#include + +#define TOKEN_MAX 64 +#define CMD_ARGS_MAX 64 + +#define TOKEN_CLASS_OPAREN 0 +#define TOKEN_CLASS_CPAREN 1 +#define TOKEN_CLASS_WORD 2 +#define TOKEN_CLASS_SEMICOLON 3 +#define TOKEN_CLASS_REDIR 4 + +struct token { + struct list_node_link tokens_link; + char buffer[TOKEN_MAX]; + int class; +}; + +#define PREC_NONE 0 +#define PREC_LOWEST 1 +#define PREC_SEQ 2 +#define PREC_REDIR 3 + +#define AST_NODE_CLASS_CMD 0 +#define AST_NODE_CLASS_SUBSHELL 1 +#define AST_NODE_CLASS_SEQ 2 +#define AST_NODE_CLASS_REDIR 3 + +struct ast_node; + +struct ast_cmd { + char* name; + char* args[CMD_ARGS_MAX]; + int arg_count; +}; + +struct ast_subshell { + struct ast_node* inner; +}; + +struct ast_seq { + struct ast_node* left; + struct ast_node* right; +}; + +struct ast_redir { + struct ast_node* source; + char* file_path; +}; + +struct ast_node { + int class; + union { + struct ast_cmd cmd; + struct ast_subshell subshell; + struct ast_seq seq; + struct ast_redir redir; + } u; +}; + +struct parser; + +typedef struct ast_node* (*nud_func_t) (struct parser* parser, struct token* token); +typedef struct ast_node* (*led_func_t) (struct parser* parser, struct token* token, + struct ast_node* left); + +struct parse_rule { + nud_func_t nud; + led_func_t led; + int precedence; +}; + +struct parser { + struct token* current; + struct token* next; +}; + +struct ast_node* word_nud (struct parser* parser, struct token* token); + +struct ast_node* oparen_nud (struct parser* parser, struct token* token); + +struct ast_node* semicolon_led (struct parser* parser, struct token* token, struct ast_node* left); + +struct ast_node* redir_led (struct parser* parser, struct token* token, struct ast_node* left); + +void tokenize (struct list_node_link** tokens, const char* text); + +void parse_and_execute (struct list_node_link* tokens); + +#endif // _PARSER_H diff --git a/ce/self.c b/ce/self.c new file mode 100644 index 0000000..d29bb90 --- /dev/null +++ b/ce/self.c @@ -0,0 +1,4 @@ +#include "self.h" + +int e_pid; +int e_pgid; diff --git a/ce/self.h b/ce/self.h new file mode 100644 index 0000000..dd443f5 --- /dev/null +++ b/ce/self.h @@ -0,0 +1,7 @@ +#ifndef _SELF_H +#define _SELF_H + +extern int e_pid; +extern int e_pgid; + +#endif // _SELF_H diff --git a/ce/src.mk b/ce/src.mk index 8c16599..57a6a3c 100644 --- a/ce/src.mk +++ b/ce/src.mk @@ -1,3 +1,17 @@ -c += ce.c +c += ce.c \ + arena_alloc.c \ + strbuf.c \ + context.c \ + parser.c \ + interp.c \ + mprintf.c \ + self.c -o += ce.o +o += ce.o \ + arena_alloc.o \ + strbuf.o \ + context.o \ + parser.o \ + interp.o \ + mprintf.o \ + self.o diff --git a/ce/strbuf.c b/ce/strbuf.c new file mode 100644 index 0000000..3e22c1d --- /dev/null +++ b/ce/strbuf.c @@ -0,0 +1,24 @@ +#include "strbuf.h" +#include "arena_alloc.h" +#include +#include + +void strbuf_append (struct strbuf* strbuf, char c) { + if (strbuf->items == NULL) { + strbuf->capacity = 256; + strbuf->count = 0; + strbuf->items = arena_malloc (&arena, strbuf->capacity); + } else { + if (strbuf->count == strbuf->capacity) { + size_t prev_capacity = strbuf->capacity; + strbuf->capacity *= 2; + strbuf->items = arena_realloc (&arena, strbuf->items, prev_capacity, strbuf->capacity); + } + } + strbuf->items[strbuf->count++] = c; +} + +void strbuf_append_str (struct strbuf* strbuf, const char* s) { + while (*s) + strbuf_append (strbuf, *s++); +} diff --git a/ce/strbuf.h b/ce/strbuf.h new file mode 100644 index 0000000..672026c --- /dev/null +++ b/ce/strbuf.h @@ -0,0 +1,15 @@ +#ifndef _STRBUF_H +#define _STRBUF_H + +#include + +struct strbuf { + char* items; + size_t count, capacity; +}; + +void strbuf_append (struct strbuf* strbuf, char c); + +void strbuf_append_str (struct strbuf* strbuf, const char* s); + +#endif // _STRBUF_H