Split CE code into multiple files
All checks were successful
Build documentation / build-and-deploy (push) Successful in 2m38s
All checks were successful
Build documentation / build-and-deploy (push) Successful in 2m38s
This commit is contained in:
4
ce/arena_alloc.c
Normal file
4
ce/arena_alloc.c
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#include "arena_alloc.h"
|
||||||
|
#include <arena.h>
|
||||||
|
|
||||||
|
struct arena arena;
|
||||||
8
ce/arena_alloc.h
Normal file
8
ce/arena_alloc.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _ARENA_ALLOC_H
|
||||||
|
#define _ARENA_ALLOC_H
|
||||||
|
|
||||||
|
#include <arena.h>
|
||||||
|
|
||||||
|
extern struct arena arena;
|
||||||
|
|
||||||
|
#endif // _ARENA_ALLOC_H
|
||||||
598
ce/ce.c
598
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 <arena.h>
|
#include <arena.h>
|
||||||
#include <desc.h>
|
#include <desc.h>
|
||||||
#include <filereader.h>
|
#include <filereader.h>
|
||||||
@@ -16,599 +22,9 @@
|
|||||||
#include <system.h>
|
#include <system.h>
|
||||||
#include <terminal_cursor.h>
|
#include <terminal_cursor.h>
|
||||||
|
|
||||||
#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 LINE_BUFFER_MAX 1024
|
||||||
#define TOKEN_MAX 64
|
|
||||||
#define CMD_ARGS_MAX 64
|
|
||||||
#define PROMPT "$ "
|
#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 <word1> <word2> <word3> ...\n");
|
|
||||||
cprintf (context, "help\n");
|
|
||||||
cprintf (context, "cat <file path>\n");
|
|
||||||
cprintf (context, "ls <directory path>\n");
|
|
||||||
cprintf (context, "mkfile <file path>\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) {
|
static void exec_line (const char* line) {
|
||||||
struct list_node_link* tokens = NULL;
|
struct list_node_link* tokens = NULL;
|
||||||
|
|
||||||
@@ -628,7 +44,7 @@ void app_main (void) {
|
|||||||
char line_buffer[LINE_BUFFER_MAX + 1];
|
char line_buffer[LINE_BUFFER_MAX + 1];
|
||||||
memset (line_buffer, 0, sizeof (line_buffer));
|
memset (line_buffer, 0, sizeof (line_buffer));
|
||||||
|
|
||||||
while (run) {
|
while (interp_is_running ()) {
|
||||||
mprintf (PROMPT);
|
mprintf (PROMPT);
|
||||||
|
|
||||||
char ch = 0;
|
char ch = 0;
|
||||||
|
|||||||
29
ce/context.c
Normal file
29
ce/context.c
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include "context.h"
|
||||||
|
#include "strbuf.h"
|
||||||
|
#include <liballoc.h>
|
||||||
|
#include <printf.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
14
ce/context.h
Normal file
14
ce/context.h
Normal file
@@ -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
|
||||||
274
ce/interp.c
Normal file
274
ce/interp.c
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
#include "interp.h"
|
||||||
|
#include "arena_alloc.h"
|
||||||
|
#include "context.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "self.h"
|
||||||
|
#include <desc.h>
|
||||||
|
#include <filereader.h>
|
||||||
|
#include <filewriter.h>
|
||||||
|
#include <path.h>
|
||||||
|
#include <process.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <str_status.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <system.h>
|
||||||
|
|
||||||
|
#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 <word1> <word2> <word3> ...\n");
|
||||||
|
cprintf (context, "help\n");
|
||||||
|
cprintf (context, "cat <file path>\n");
|
||||||
|
cprintf (context, "ls <directory path>\n");
|
||||||
|
cprintf (context, "mkfile <file path>\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;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
ce/interp.h
Normal file
13
ce/interp.h
Normal file
@@ -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
|
||||||
27
ce/mprintf.c
Normal file
27
ce/mprintf.c
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#include "mprintf.h"
|
||||||
|
#include "self.h"
|
||||||
|
#include <liballoc.h>
|
||||||
|
#include <printf.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <system.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
10
ce/mprintf.h
Normal file
10
ce/mprintf.h
Normal file
@@ -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
|
||||||
182
ce/parser.c
Normal file
182
ce/parser.c
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
#include "parser.h"
|
||||||
|
#include "arena_alloc.h"
|
||||||
|
#include "context.h"
|
||||||
|
#include "interp.h"
|
||||||
|
#include "mprintf.h"
|
||||||
|
#include <arena.h>
|
||||||
|
#include <list.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
ce/parser.h
Normal file
92
ce/parser.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#ifndef _PARSER_H
|
||||||
|
#define _PARSER_H
|
||||||
|
|
||||||
|
#include <list.h>
|
||||||
|
|
||||||
|
#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
|
||||||
7
ce/self.h
Normal file
7
ce/self.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#ifndef _SELF_H
|
||||||
|
#define _SELF_H
|
||||||
|
|
||||||
|
extern int e_pid;
|
||||||
|
extern int e_pgid;
|
||||||
|
|
||||||
|
#endif // _SELF_H
|
||||||
18
ce/src.mk
18
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
|
||||||
|
|||||||
24
ce/strbuf.c
Normal file
24
ce/strbuf.c
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include "strbuf.h"
|
||||||
|
#include "arena_alloc.h"
|
||||||
|
#include <arena.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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++);
|
||||||
|
}
|
||||||
15
ce/strbuf.h
Normal file
15
ce/strbuf.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef _STRBUF_H
|
||||||
|
#define _STRBUF_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user