Add write_file () syscall, CE implement redirections, libarena arena_realloc ()
All checks were successful
Build documentation / build-and-deploy (push) Successful in 2m14s

This commit is contained in:
2026-03-01 12:04:21 +01:00
parent abd85744cc
commit a5d5e7d6a4
9 changed files with 220 additions and 44 deletions

187
ce/ce.c
View File

@@ -16,6 +16,46 @@
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;
};
#define CPRINTF_BUF_MAX 4096
#define cprintf(context, fmt, ...) \
do { \
char* __cprintf_buf = malloc (CPRINTF_BUF_MAX); \
memset (__cprintf_buf, 0, CPRINTF_BUF_MAX); \
snprintf (__cprintf_buf, CPRINTF_BUF_MAX, (fmt), ##__VA_ARGS__); \
strbuf_append_str (&(context)->strbuf, __cprintf_buf); \
free (__cprintf_buf); \
} while (0)
#define LINE_BUFFER_MAX 1024
#define TOKEN_MAX 64
#define CMD_ARGS_MAX 64
@@ -25,6 +65,7 @@ static struct arena arena;
#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;
@@ -33,12 +74,14 @@ struct token {
};
#define PREC_NONE 0
#define PREC_SEQ 1
#define PREC_LOWEST 2
#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;
@@ -57,12 +100,18 @@ struct ast_seq {
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;
};
@@ -90,11 +139,15 @@ 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) {
@@ -179,6 +232,19 @@ static struct ast_node* semicolon_led (struct parser* parser, struct token* toke
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;
@@ -211,13 +277,15 @@ static void classify_tokens (struct list_node_link* tokens) {
token->class = TOKEN_CLASS_CPAREN;
} else if (strcmp (token->buffer, ";") == 0) {
token->class = TOKEN_CLASS_SEMICOLON;
} else if (strcmp (token->buffer, ">") == 0) {
token->class = TOKEN_CLASS_REDIR;
} else {
token->class = TOKEN_CLASS_WORD;
}
}
}
static void execute (struct ast_node* root);
static void execute (struct ast_node* root, struct context* context);
static void parse_tokens (struct list_node_link* tokens) {
struct parser parser;
@@ -225,20 +293,24 @@ static void parse_tokens (struct list_node_link* tokens) {
parser.next = get_token (tokens);
while (parser.next != NULL) {
struct ast_node* root = parse_precedence (&parser, PREC_LOWEST);
struct ast_node* root = parse_precedence (&parser, PREC_NONE);
if (root != NULL) {
execute (root);
struct context context = {0};
execute (root, &context);
if (context.strbuf.items != NULL)
printf ("%.*s", (int)context.strbuf.count, context.strbuf.items);
}
}
}
static void echo (char** strings, size_t strings_count) {
static void echo (struct context* context, char** strings, size_t strings_count) {
for (size_t i = 0; i < strings_count; i++)
printf ("%s ", strings[i]);
cprintf (context, "%s ", strings[i]);
}
static void mkfile (char** file_paths, size_t files_count) {
static void mkfile (struct context* context, char** file_paths, size_t files_count) {
char volume[VOLUME_MAX];
const char* path;
int ret;
@@ -247,12 +319,12 @@ static void mkfile (char** file_paths, size_t files_count) {
const char* file_path = file_paths[i];
if (!path_parse (file_path, volume, &path)) {
printf ("ERROR bad path '%s'\n", file_path);
cprintf (context, "ERROR bad path '%s'\n", file_path);
continue;
}
if ((ret = volume_open (volume)) < 0) {
printf ("ERROR could not open volume '%s': %s\n", volume, str_status[-ret]);
cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]);
continue;
}
@@ -262,7 +334,7 @@ static void mkfile (char** file_paths, size_t files_count) {
}
}
static void cat (char** file_paths, size_t files_count) {
static void cat (struct context* context, char** file_paths, size_t files_count) {
struct desc desc;
char volume[VOLUME_MAX];
const char* path;
@@ -272,12 +344,12 @@ static void cat (char** file_paths, size_t files_count) {
const char* file_path = file_paths[i];
if (!path_parse (file_path, volume, &path)) {
printf ("ERROR bad path '%s'\n", file_path);
cprintf (context, "ERROR bad path '%s'\n", file_path);
continue;
}
if ((ret = volume_open (volume)) < 0) {
printf ("ERROR could not open volume '%s': %s\n", volume, str_status[-ret]);
cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]);
continue;
}
@@ -293,7 +365,7 @@ static void cat (char** file_paths, size_t files_count) {
memset (buffer, 0, desc.size + 1);
read_file (path, 0, (uint8_t*)buffer, desc.size);
printf ("%s\n", buffer);
cprintf (context, "%s\n", buffer);
close:
if (buffer != NULL)
@@ -302,7 +374,7 @@ static void cat (char** file_paths, size_t files_count) {
}
}
static void ls (const char* path_string) {
static void ls (struct context* context, const char* path_string) {
struct desc desc;
char volume[VOLUME_MAX];
const char* path;
@@ -310,81 +382,108 @@ static void ls (const char* path_string) {
struct dir_entry entry;
if (!path_parse (path_string, volume, &path)) {
printf ("ERROR bad path '%s'\n", path_string);
cprintf (context, "ERROR bad path '%s'\n", path_string);
return;
}
if ((ret = volume_open (volume)) < 0) {
printf ("ERROR could not open volume '%s': %s\n", volume, str_status[-ret]);
cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]);
return;
}
describe (path, &desc);
if (desc.type != FS_DIR) {
printf ("ERROR '%s' is not a directory\n", path_string);
cprintf (context, "ERROR '%s' is not a directory\n", path_string);
volume_close ();
return;
}
size_t entries = desc.size;
printf ("total entries: %zu\n", entries);
printf ("\n");
cprintf (context, "total entries: %zu\n", entries);
cprintf (context, "\n");
for (size_t entry_num = 0; entry_num < entries; entry_num++) {
read_dir_entry (path, &entry, entry_num);
describe (entry.path, &desc);
printf ("%-40s%c %-40zu\n", entry.path, (desc.type == FS_DIR ? '*' : ' '), desc.size);
cprintf (context, "%-40s%c %-40zu\n", entry.path, (desc.type == FS_DIR ? '*' : ' '), desc.size);
}
volume_close ();
}
static void quit1 (void) {
static void quit1 (struct context* context) {
run = false;
printf ("Goodbye!\n");
cprintf (context, "Goodbye!\n");
}
static void help (void) {
printf ("Available commands:\n");
printf ("echo <word1> <word2> <word3> ...\n");
printf ("help\n");
printf ("cat <file path>\n");
printf ("ls <directory path>\n");
printf ("mkfile <file path>\n");
printf ("quit\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 execute_cmd (struct ast_cmd* cmd) {
static void execute_cmd (struct ast_cmd* cmd, struct context* context) {
if (strcmp (cmd->name, "echo") == 0) {
echo (cmd->args, cmd->arg_count);
echo (context, cmd->args, cmd->arg_count);
} else if (strcmp (cmd->name, "help") == 0) {
help ();
help (context);
} else if (strcmp (cmd->name, "cat") == 0) {
cat (cmd->args, cmd->arg_count);
cat (context, cmd->args, cmd->arg_count);
} else if (strcmp (cmd->name, "ls") == 0) {
ls (cmd->args[0]);
ls (context, cmd->args[0]);
} else if (strcmp (cmd->name, "quit") == 0) {
quit1 ();
quit1 (context);
} else if (strcmp (cmd->name, "mkfile") == 0) {
mkfile (cmd->args, cmd->arg_count);
mkfile (context, cmd->args, cmd->arg_count);
} else {
printf ("ERROR unknown command '%s'\n", cmd->name);
cprintf (context, "ERROR unknown command '%s'\n", cmd->name);
}
}
static void execute (struct ast_node* root) {
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;
}
if ((ret = volume_open (volume)) < 0) {
cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]);
return;
}
create_file (path);
write_file (path, 0, (uint8_t*)context->strbuf.items, context->strbuf.count);
volume_close ();
}
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);
execute_cmd (&root->u.cmd, context);
break;
case AST_NODE_CLASS_SUBSHELL:
execute (root->u.subshell.inner);
execute (root->u.subshell.inner, &subcontext);
break;
case AST_NODE_CLASS_SEQ:
execute (root->u.seq.left);
execute (root->u.seq.right);
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;
}
}