Add write_file () syscall, CE implement redirections, libarena arena_realloc ()
All checks were successful
Build documentation / build-and-deploy (push) Successful in 2m14s
All checks were successful
Build documentation / build-and-deploy (push) Successful in 2m14s
This commit is contained in:
187
ce/ce.c
187
ce/ce.c
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user