diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/exec.c | 189 | ||||
| -rw-r--r-- | src/exec.h | 26 | ||||
| -rw-r--r-- | src/expand.c | 104 | ||||
| -rw-r--r-- | src/expand.h | 31 | ||||
| -rw-r--r-- | src/main.c | 23 | ||||
| -rw-r--r-- | src/preproc.c | 318 | ||||
| -rw-r--r-- | src/preproc.h | 116 | ||||
| -rw-r--r-- | src/utils.c | 71 | ||||
| -rw-r--r-- | src/utils.h | 111 |
9 files changed, 989 insertions, 0 deletions
diff --git a/src/exec.c b/src/exec.c new file mode 100644 index 0000000..fccda7a --- /dev/null +++ b/src/exec.c @@ -0,0 +1,189 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/wait.h> + +#include "utils.h" +#include "exec.h" +#include "preproc.h" +#include "expand.h" + + +void execute_command(Command cmd, int* pgid) { + + pid_t pid = fork(); + + if (pid == 0) { + if (*pgid == -1) { + setpgid(0, 0); + } else { + setpgid(0, *pgid); + } + + dup2(cmd.stdin_fd, STDIN_FILENO); + dup2(cmd.stdout_fd, STDOUT_FILENO); + + for (int j = 0; j < cmd.redirs.count; j++) { + FdRedirect fd_rd = *get_from_FdRedirectVec(&cmd.redirs, j); + dup2(fd_rd.new_fd, fd_rd.old_fd); + } + + for (int i = 0; i < cmd.child_close_fds.count; i++) { + close(*get_from_IntVec(&cmd.child_close_fds, i)); + } + + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + signal(SIGTTIN, SIG_DFL); + signal(SIGTTOU, SIG_DFL); + + execvp(cmd.args[0], cmd.args); + exit(67); + } + + if (*pgid == -1) { + setpgid(pid, pid); + *pgid = pid; + } else { + setpgid(pid, *pgid); + } +} + +Command make_command(ExpandedPipelineElement elem) { + Command cmd = { + .child_close_fds = make_IntVec(64), + .parent_close_fds = make_IntVec(64), + .redirs = make_FdRedirectVec(32), + .term = elem.conn, + }; + StrVec arg_strs = make_StrVec(256); + for (int i = 0; i < elem.cmd_slots.count; i++) { + Slot curr_slot = *get_from_SlotVec(&elem.cmd_slots, i); + Slot next_slot; + int fd; + FdRedirect redir; + switch (curr_slot.kind) { + case SLOT_STR: + push_to_StrVec(&arg_strs, curr_slot.as.str); + break; + case SLOT_SPEC: + switch (curr_slot.as.spec) { + case READ_REDIR: + i++; + next_slot = *get_from_SlotVec(&elem.cmd_slots, i); + if (next_slot.kind != SLOT_STR) exit(6); + fd = open(next_slot.as.str, O_RDONLY, 0644); + redir = (FdRedirect){.old_fd = STDIN_FILENO, .new_fd = fd}; + push_to_FdRedirectVec(&cmd.redirs, redir); + push_to_IntVec(&cmd.parent_close_fds, fd); + push_to_IntVec(&cmd.child_close_fds, fd); + break; + case WRITE_REDIR_TRUNC: + i++; + next_slot = *get_from_SlotVec(&elem.cmd_slots, i); + if (next_slot.kind != SLOT_STR) exit(6); + fd = open(next_slot.as.str, O_WRONLY | O_CREAT | O_TRUNC , 0644); + redir = (FdRedirect){.old_fd = STDOUT_FILENO, .new_fd = fd}; + push_to_FdRedirectVec(&cmd.redirs, redir); + push_to_IntVec(&cmd.parent_close_fds, fd); + push_to_IntVec(&cmd.child_close_fds, fd); + break; + case WRITE_REDIR_APPEND: + i++; + next_slot = *get_from_SlotVec(&elem.cmd_slots, i); + if (next_slot.kind != SLOT_STR) exit(6); + fd = open(next_slot.as.str, O_WRONLY | O_CREAT | O_APPEND, 0644); + redir = (FdRedirect){.old_fd = STDOUT_FILENO, .new_fd = fd}; + push_to_FdRedirectVec(&cmd.redirs, redir); + push_to_IntVec(&cmd.parent_close_fds, fd); + push_to_IntVec(&cmd.child_close_fds, fd); + break; + default: + exit(7); + } + break; + default: + exit(67); + // TODO: other slot kinds + } + } + cmd.args = malloc(sizeof(char*) * arg_strs.count); + for (int j = 0; j < arg_strs.count; j++) { + cmd.args[j] = *get_from_StrVec(&arg_strs, j); + } + return cmd; +} + + +void run_cmds(CommandVec cmds) { + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + + int trunk_fd = STDIN_FILENO; + int pgid = -1; + + for (int i = 0; i < cmds.count; i++) { + Command cmd = *get_from_CommandVec(&cmds, i); + cmd.stdin_fd = trunk_fd; + + if (i > 0) { + push_to_IntVec(&cmd.child_close_fds, trunk_fd); + push_to_IntVec(&cmd.parent_close_fds, trunk_fd); + } + + cmd.stdout_fd = STDOUT_FILENO; + if (cmd.term == CONN_PIPE) { + int pipe_fds[2]; + pipe(pipe_fds); + cmd.stdout_fd = pipe_fds[1]; + push_to_IntVec(&cmd.child_close_fds, pipe_fds[1]); + push_to_IntVec(&cmd.parent_close_fds, pipe_fds[1]); + trunk_fd = pipe_fds[0]; + } + + execute_command(cmd, &pgid); + + for (int j = 0; j < cmd.parent_close_fds.count; j++) { + close(*get_from_IntVec(&cmd.parent_close_fds, j)); + } + } + + tcsetpgrp(STDIN_FILENO, pgid); + while (waitpid(-pgid, NULL, 0) > 0); + tcsetpgrp(STDIN_FILENO, getpgrp()); +} + + +CommandVec make_cmd_vec_pl(ExpandedPipeline pl) { + CommandVec cmds = make_CommandVec(256); + for (int i = 0; i < pl.count; i++) { + ExpandedPipelineElement elem = *get_from_ExpandedPipeline(&pl, i); + Command cmd = make_command(elem); + push_to_CommandVec(&cmds, cmd); + } + return cmds; +} + + +void process_input_line(char* line, ShellState* shstate) { + PreprocArena arena = { + .tokens = make_TokenVec(256), + .chunks = make_ChunkVec(256), + .regions = make_RegionVec(256), + }; + TokenPtrVec line_tok_ptrs = lex(line, &arena); + Token* line_toks = malloc(sizeof(Token) * line_tok_ptrs.count); + for (int i = 0; i < line_tok_ptrs.count; i++) { + line_toks[i] = **get_from_TokenPtrVec(&line_tok_ptrs, i); + } + Pipeline pl = parse_tokstream(line_toks); + ExpandedPipeline exp_pl = expand_pipeline(&pl, shstate); + CommandVec cmds = make_cmd_vec_pl(exp_pl); + run_cmds(cmds); + free(line_toks); +} + diff --git a/src/exec.h b/src/exec.h new file mode 100644 index 0000000..9ee8164 --- /dev/null +++ b/src/exec.h @@ -0,0 +1,26 @@ +#pragma once + +#include "utils.h" +#include "preproc.h" + + +typedef struct { + int old_fd; + int new_fd; +} FdRedirect; + +DECLARE_VEC(FdRedirect, FdRedirectVec) + +typedef struct { + char** args; + FdRedirectVec redirs; + int stdin_fd; + int stdout_fd; + IntVec child_close_fds; + IntVec parent_close_fds; + Connector term; +} Command; + +DECLARE_VEC(Command, CommandVec) + +void process_input_line(char* line, ShellState* shstate); diff --git a/src/expand.c b/src/expand.c new file mode 100644 index 0000000..11764f9 --- /dev/null +++ b/src/expand.c @@ -0,0 +1,104 @@ +#include "expand.h" +#include "preproc.h" +#include "utils.h" +#include <string.h> + + +typedef struct { + QuotationMode qmode; + char* content; +} ExpandedChunk; + + +ExpandedChunk expand_chunk(Chunk chunk, ShellState* shstate) { + char* content; + if (chunk.qmode == SINGLE_Q || chunk.kind == LITERAL) { + if (chunk.qmode == SINGLE_Q && chunk.kind != LITERAL) exit(69); + content = chunk.as.literal_str; + } else if (chunk.kind == VAR_EXP) { + content = get_var(chunk.as.varname, shstate); + } else if (chunk.kind == REGION_EXP) { + exit(7); // Not supported yet; arithm + } + return (ExpandedChunk){.qmode = chunk.qmode, .content = content}; +} + + +typedef struct { + StrVec complete_strs; + String active_str; +} WordSplittingGerm; + +void add_expchunk(char* next_str, WordSplittingGerm* curr_res) { + for (int pos = 0; pos < strlen(next_str); pos++) { + if (next_str[pos] == ' ') { + push_to_StrVec(&curr_res->complete_strs, inner(&curr_res->active_str)); + curr_res->active_str = make_String(256); + } else { + push_to_String(&curr_res->active_str, next_str[pos]); + } + } +} + + +void expand_word(Word word, SlotVec* target, ShellState* shstate) { + WordSplittingGerm curr_res = { + .complete_strs = make_StrVec(256), + .active_str = make_String(256) + }; + for (int i = 0; i < word.count; i++) { + Chunk chunk = **get_from_Word(&word, i); + ExpandedChunk expchunk = expand_chunk(chunk, shstate); + switch (expchunk.qmode) { + case UN_Q: + add_expchunk(expchunk.content, &curr_res); + break; + case SINGLE_Q: + append_str_to_String(&curr_res.active_str, expchunk.content); + break; + case DOUBLE_Q: + // TODO: further expansions here + append_str_to_String(&curr_res.active_str, expchunk.content); + break; + } + } + if (curr_res.active_str.count > 0) { + push_to_StrVec(&curr_res.complete_strs, inner(&curr_res.active_str)); + } + for (int j = 0; j < curr_res.complete_strs.count; j++) { + char* str = *get_from_StrVec(&curr_res.complete_strs, j); + push_to_SlotVec(target, (Slot){.kind = SLOT_STR, .as.str = str}); + } +} + +ExpandedPipelineElement expand_pipeline_element(PipelineElement elem, ShellState* shstate) { + ExpandedPipelineElement result = {.cmd_slots = make_SlotVec(256), .conn = elem.conn}; + for (int i = 0; i < elem.cmd_toks.count; i++) { + Token curr_tok = *get_from_TokenVec(&elem.cmd_toks, i); + switch (curr_tok.kind) { + case TOK_WORD: + expand_word(curr_tok.as.word, &result.cmd_slots, shstate); + break; + case TOK_SPECIAL: + push_to_SlotVec(&result.cmd_slots, (Slot){.kind = SLOT_SPEC, .as.spec = curr_tok.as.spec}); + break; + case TOK_PROCSUB_IN: + push_to_SlotVec(&result.cmd_slots, (Slot){.kind = SLOT_PROC_IN, .as.proc_reg= curr_tok.as.procsub_region}); + break; + case TOK_PROCSUB_OUT: + push_to_SlotVec(&result.cmd_slots, (Slot){.kind = SLOT_PROC_OUT, .as.proc_reg= curr_tok.as.procsub_region}); + break; + } + } + return result; +} + +ExpandedPipeline expand_pipeline(Pipeline* pl, ShellState *shstate) { + ExpandedPipeline exp_pl = make_ExpandedPipeline(256); + for (int i = 0; i < pl->count; i++) { + PipelineElement elem = *get_from_Pipeline(pl, i); + ExpandedPipelineElement exp_elem = expand_pipeline_element(elem, shstate); + push_to_ExpandedPipeline(&exp_pl, exp_elem); + } + return exp_pl; +} diff --git a/src/expand.h b/src/expand.h new file mode 100644 index 0000000..b353f39 --- /dev/null +++ b/src/expand.h @@ -0,0 +1,31 @@ +# pragma once + +#include "preproc.h" +#include "utils.h" + +typedef enum { + SLOT_STR, + SLOT_SPEC, + SLOT_PROC_IN, + SLOT_PROC_OUT, +} SlotKind; + +typedef struct { + SlotKind kind; + union { + char* str; + SpecialToken spec; + Region* proc_reg; + } as; +} Slot; + +DECLARE_VEC(Slot, SlotVec) + +typedef struct { + SlotVec cmd_slots; + Connector conn; +} ExpandedPipelineElement; + +DECLARE_VEC(ExpandedPipelineElement, ExpandedPipeline) + +ExpandedPipeline expand_pipeline(Pipeline* pl, ShellState* shstate); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..35c2bff --- /dev/null +++ b/src/main.c @@ -0,0 +1,23 @@ +#include <stdlib.h> +#include <stdio.h> +#include <readline/readline.h> +#include <readline/history.h> +#include <string.h> + +#include "exec.h" + + +int main() { + char* line; + ShellState shstate = {.shell_vars = make_KeyValMap(1), .aliases = make_KeyValMap(1)}; + while (1) { + line = readline("$ "); + if (line[0] == '\0') continue; + if (strcmp(line, "exit") == 0) exit(0); + add_history(line); + process_input_line(line, &shstate); + } + free(line); +} + + diff --git a/src/preproc.c b/src/preproc.c new file mode 100644 index 0000000..4cd1c5b --- /dev/null +++ b/src/preproc.c @@ -0,0 +1,318 @@ +#include <ctype.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <stdio.h> + +#include "preproc.h" +#include "utils.h" + + +// Lexing + +typedef struct { + char* str; + size_t pos; +} RawLine; + + +String collect_identifier(RawLine* line) { + String id_buf = make_String(256); + while (line->str[line->pos] == '_' || isalnum(line->str[line->pos])) { + push_to_String(&id_buf, line->str[line->pos++]); + } + return id_buf; +} + + + +TokenPtrVec lex_level(RawLine* line, char until, PreprocArena* arena); + + +void expect_char(RawLine* line, char expected) { + if (line->str[line->pos] != expected) { + exit(45); // TODO: nicer handling + } + line->pos++; +} + + +/* +MaybeChunk lex_expansion_chunk(RawLine* line, RegionVec* reg_arena, QuotationMode qmode) { + line->pos++; // Consume $ + if (line->str[line->pos] == '(') { + line->pos++; + if (line->str[line->pos] == '(') { + exit(1); + // TODO: this (and potentially the other parsers must be custom, we need to recognize arithm-internal parens from the terminating `))` ) + + + TokenVec inner_toks = lex_level(line, ')', reg_arena); + expect_char(line, ')'); + expect_char(line, ')'); + Region* reg_ptr = push_to_RegionVec( + reg_arena, + (Region){.mode = ARITHM, .tokens = inner_toks} + ); + return (MaybeChunk){ + .some = true, + .value = (Chunk){ + .qmode = qmode, + .kind = REGION_EXP, + .as.reg_ptr = reg_ptr + }, + }; + } else { + TokenVec inner_toks = lex_level(line, ')', reg_arena); + expect_char(line, ')'); + Region* reg_ptr = push_to_RegionVec( + reg_arena, + (Region){.mode = CMDS, .tokens = inner_toks} + ); + return (MaybeChunk) { + .some = true, + .value = (Chunk){ + .qmode = qmode, + .kind = REGION_EXP, + .as.reg_ptr = reg_ptr, + } + }; + } + } else { + String id_name = collect_identifier(line); + if (id_name.count > 0) { + return (MaybeChunk){ + .some = true, + .value = (Chunk){ + .qmode = qmode, + .kind = VAR_EXP, + .as.varname = inner(&id_name), + } + }; + } + } + return (MaybeChunk){.some = false}; +} +*/ + + +void lex_double_quoted_section(RawLine* line, Word* coll, PreprocArena* arena) { + line->pos++; + String accum = make_String(256); + while (line->str[line->pos] != '\"') { + if (line->str[line->pos] == '\0') exit(43); + + MaybeChunk exp_chunk = {.some = false}; + + if (line->str[line->pos] == '$') { + exit(1); + /* + exp_chunk = lex_expansion_chunk(line, reg_arena, DOUBLE_Q); + if (!exp_chunk.some) { + push_to_String(&accum, '$'); + } + */ + } else { + push_to_String(&accum, line->str[line->pos++]); + } + // TODO: other special cases beyond $ + + if (exp_chunk.some) { + if (accum.count > 0) { + Chunk accum_chunk = {.qmode = DOUBLE_Q, .kind = LITERAL, .as.literal_str = inner(&accum)}; + Chunk* accum_ptr = push_to_ChunkVec(&arena->chunks, accum_chunk); + push_to_Word(coll, accum_ptr); + accum = make_String(256); + } + Chunk* exp_ptr = push_to_ChunkVec(&arena->chunks, exp_chunk.value); + push_to_Word(coll, exp_ptr); + exp_chunk.some = false; + } + } + line->pos++; + if (accum.count > 0) { + Chunk accum_chunk = {.qmode = DOUBLE_Q, .kind = LITERAL, .as.literal_str = inner(&accum)}; + Chunk* accum_ptr = push_to_ChunkVec(&arena->chunks, accum_chunk); + push_to_Word(coll, accum_ptr); + accum = make_String(256); + } +} + + +Chunk next_word_unquoted(RawLine* line, RegionVec* reg_arena) { + String buf = make_String(256); + for (char c; (c=line->str[line->pos], c != '$' && c != '\0' && c != ' '); line->pos++) { + push_to_String(&buf, c); + } + return (Chunk){.qmode = UN_Q, .kind = LITERAL, .as.literal_str = inner(&buf)}; +} + + +Chunk collect_single_quoted_section(RawLine* line) { + line->pos++; // Consume quote mark + String chunk_str = make_String(256); + while (line->str[line->pos] != '\'') { + if (line->str[line->pos] == '\0') exit(44); + push_to_String(&chunk_str, line->str[line->pos++]); + } + line->pos++; // Consume closing quote + return (Chunk){ + .qmode = SINGLE_Q, + .kind = LITERAL, + .as.literal_str = inner(&chunk_str), + }; +} + + +typedef struct { + Word word_germ; + String unq_liter_germ; +} LexState; + + +void close_word_and_lit_germs(LexState* state, TokenPtrVec* toks, PreprocArena* arena) { + if (state->unq_liter_germ.count > 0) { + Chunk chu = { + .qmode = UN_Q, + .kind = LITERAL, + .as.literal_str = inner(&state->unq_liter_germ) + }; + Chunk* chu_ptr = push_to_ChunkVec(&arena->chunks, chu); + push_to_Word(&state->word_germ, chu_ptr); + state->unq_liter_germ = make_String(256); + } + if (state->word_germ.count > 0) { + Token tok = {.kind = TOK_WORD, .as.word = state->word_germ}; + Token* tok_ptr = push_to_TokenVec(&arena->tokens, tok); + push_to_TokenPtrVec(toks, tok_ptr); + state->word_germ = make_Word(256); + } +} + +TokenPtrVec lex_level(RawLine* line, char until, PreprocArena* arena) { + + TokenPtrVec line_toks = make_TokenPtrVec(256); + LexState state = { + .word_germ = make_Word(256), + .unq_liter_germ = make_String(256) + }; + + while (line->str[line->pos] != until) { + + if (line->str[line->pos] == ' ') { + close_word_and_lit_germs(&state, &line_toks, arena); + while (line->str[line->pos] == ' ') line->pos++; + continue; + } + + Token* tok_ptr; + + switch (line->str[line->pos]) { + case '\'': + push_to_Word(&state.word_germ, push_to_ChunkVec(&arena->chunks, collect_single_quoted_section(line))); + break; + case '\"': + lex_double_quoted_section(line, &state.word_germ, arena); + break; + case '|': + line->pos++; + close_word_and_lit_germs(&state, &line_toks, arena); + tok_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = PIPE}); + push_to_TokenPtrVec(&line_toks, tok_ptr); + break; + case '$': + exit(1); + /* + line->pos++; + MaybeChunk mchunk = lex_expansion_chunk(line, reg_arena, UN_Q); + if (mchunk.some) { + push_to_Word(&state.word_germ, mchunk.value); + } else { + push_to_String(&state.unq_liter_germ, '$'); + } + */ + break; + case '>': + close_word_and_lit_germs(&state, &line_toks, arena); + line->pos++; + + switch (line->str[line->pos]) { + case '(': + line->pos++; + TokenPtrVec sub_regtoks = lex_level(line, ')', arena); + line->pos++; + Region subreg = {.mode = CMDS, .tokens = sub_regtoks}; + Region* subreg_ptr = push_to_RegionVec(&arena->regions, subreg); + Token tok = {.kind = TOK_PROCSUB_OUT, .as.procsub_region = subreg_ptr}; + tok_ptr = push_to_TokenVec(&arena->tokens, tok); + push_to_TokenPtrVec(&line_toks, tok_ptr); + break; + case '>': + line->pos++; + tok_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = WRITE_REDIR_APPEND}); + push_to_TokenPtrVec(&line_toks, tok_ptr); + break; + default: + tok_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = WRITE_REDIR_TRUNC}); + push_to_TokenPtrVec(&line_toks, tok_ptr); + } + break; + case '<': + close_word_and_lit_germs(&state, &line_toks, arena); + line->pos++; + if (line->str[line->pos] == '(') { + line->pos++; + TokenPtrVec sub_regtoks = lex_level(line, ')', arena); + line->pos++; + Region subreg = {.mode = CMDS, .tokens = sub_regtoks}; + Region* reg_ptr = push_to_RegionVec(&arena->regions, subreg); + Token reg_tok = {.kind = TOK_PROCSUB_IN, .as.procsub_region = reg_ptr}; + Token* reg_tok_ptr = push_to_TokenVec(&arena->tokens, reg_tok); + push_to_TokenPtrVec(&line_toks, reg_tok_ptr); + } else { + Token* redir_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = READ_REDIR}); + push_to_TokenPtrVec(&line_toks, redir_ptr); + } + break; + default: + push_to_String(&state.unq_liter_germ, line->str[line->pos++]); + } + } + // TODO: handle until char _and_ EOL + close_word_and_lit_germs(&state, &line_toks, arena); + return line_toks; +} + + +TokenPtrVec lex(char* input, PreprocArena* arena) { + RawLine line = {.str = input, .pos = 0}; + TokenPtrVec toks = lex_level(&line, '\0', arena); + Token* eol_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = EOL}); + push_to_TokenPtrVec(&toks, eol_ptr); + return toks; +} + + + + +// Parsing + +Pipeline parse_tokstream(Token* toks) { + size_t tok_pos = 0; + Pipeline result = make_Pipeline(256); + TokenVec tok_buf = make_TokenVec(256); + while (true) { + Token curr_tok = toks[tok_pos++]; + if (curr_tok.kind == TOK_SPECIAL && curr_tok.as.spec == PIPE) { + push_to_Pipeline(&result, (PipelineElement){.cmd_toks = tok_buf, .conn = CONN_PIPE}); + tok_buf = make_TokenVec(256); + } else if (curr_tok.kind == TOK_SPECIAL && curr_tok.as.spec == EOL) { + push_to_Pipeline(&result, (PipelineElement){.cmd_toks = tok_buf, .conn = CONN_EOL}); + break; + } else { + push_to_TokenVec(&tok_buf, curr_tok); + } + } + return result; +} diff --git a/src/preproc.h b/src/preproc.h new file mode 100644 index 0000000..7907f9d --- /dev/null +++ b/src/preproc.h @@ -0,0 +1,116 @@ +#pragma once + +#include "utils.h" + +typedef struct Region Region; + + +// Lexing stage +typedef enum { + UN_Q, + SINGLE_Q, + DOUBLE_Q, +} QuotationMode; + +typedef enum { + LITERAL, + REGION_EXP, + VAR_EXP, // TODO: Later include other exps +} ChunkKind; + +typedef struct { + ChunkKind kind; + QuotationMode qmode; + union { + char* literal_str; + Region* reg_ptr; + char* varname; + } as; +} Chunk; +DECLARE_MAYBE(Chunk, MaybeChunk); +DECLARE_VEC(Chunk*, Word) + +typedef enum { + TOK_WORD, + TOK_SPECIAL, + TOK_PROCSUB_IN, + TOK_PROCSUB_OUT, +} TokenKind; + +typedef enum { + PIPE, + WRITE_REDIR_TRUNC, + WRITE_REDIR_APPEND, + READ_REDIR, + EOL, +} SpecialToken; +DECLARE_MAYBE(SpecialToken, MaybeSpecialToken) + +typedef struct { + TokenKind kind; + + union { + Word word; + SpecialToken spec; + Region* procsub_region; + } as; +} Token; + + +DECLARE_VEC(Token*, TokenPtrVec) + + +typedef enum { + CMDS, + ARITHM, +} RegionMode; + +struct Region { + RegionMode mode; + TokenPtrVec tokens; +}; +DECLARE_VEC(Region, RegionVec) + + + +DECLARE_VEC(Chunk, ChunkVec) +DECLARE_VEC(Token, TokenVec) + + + +// Lexing + +typedef struct { + RegionVec regions; + TokenVec tokens; + ChunkVec chunks; +} PreprocArena; + + +TokenPtrVec lex(char* input, PreprocArena* arena); + + +// Parsing + +typedef enum { + CONN_PIPE, + CONN_EOL, + // TODO: add the |& thingy +} Connector; + +DECLARE_MAYBE(Connector, MaybeConnector) + +typedef struct { + TokenVec cmd_toks; + Connector conn; +} PipelineElement; + +DECLARE_VEC(PipelineElement, Pipeline) + +Pipeline parse_tokstream(Token* tokstream); + + + + + + diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..1f08445 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,71 @@ +#include "utils.h" +#include <string.h> +#include <stdlib.h> + +void append_str_to_String(String* target, const char* src_str) { + size_t len = strlen(src_str); + + for (size_t i = 0; i < len; i++) { + push_to_String(target, src_str[i]); + } +} + +char* inner(String* str) { + char *out = malloc(str->count + 1); + if (!out) abort(); + + for (size_t i = 0; i < str->count; i++) { + out[i] = *get_from_String(str, i); + } + + out[str->count] = '\0'; + return out; +} + + +void set_map_pair(KeyValMap* map, char* key, char* val) { +for (int i = 0; i < map->count; i++) { + char* key_i = get_from_KeyValMap(map, i)->key; + if (key_i == NULL) continue; + if (strcmp(key_i, key) == 0) { + // TODO: maybe copy instead + strcpy( + map->blocks[i / map->block_size][i % map->block_size].val, + val + ); + break; + } + } +} + +MaybeStr get_map_value(KeyValMap* map, char* key) { + for (int i = 0; i < map->count; i++) { + char* key_i = get_from_KeyValMap(map, i)->key; + if (key_i == NULL) continue; + if (strcmp(key_i, key) == 0) { + return (MaybeStr){ + .some = true, + .value = get_from_KeyValMap(map, i)->val + }; + } + } + return (MaybeStr){.some = false}; +} + +void unset_map_key(KeyValMap* map, char* key) { + for (int i = 0; i < map->count; i++) { + char* key_i = get_from_KeyValMap(map, i)->key; + if (key_i == NULL) continue; + if (strcmp(key_i, key) == 0) { + map->blocks[i / map->block_size][i % map->block_size].key = NULL; + break; + } + } +} + + +char* get_var(char* varname, ShellState* shstate) { + MaybeStr maybe_varval = get_map_value(&shstate->shell_vars, varname); + if (maybe_varval.some == true) return maybe_varval.value; + return getenv(varname); +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..a213c71 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,111 @@ +#pragma once + +#include <ctype.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + + +#define DECLARE_VEC(T, Name) \ + typedef struct { \ + T **blocks; \ + size_t count; \ + size_t used; \ + size_t cap; \ + size_t block_size; \ + } Name; \ + \ + static inline Name make_##Name(size_t block_size) { \ + return (Name){ \ + .blocks = NULL, \ + .count = 0, \ + .used = 0, \ + .cap = 0, \ + .block_size = block_size, \ + }; \ + } \ + \ + static inline T *push_to_##Name(Name *vec, T item) { \ + size_t block_index = vec->count / vec->block_size; \ + size_t item_index = vec->count % vec->block_size; \ + \ + if (block_index == vec->used) { \ + if (vec->used == vec->cap) { \ + size_t new_cap = vec->cap \ + ? vec->cap * 2 \ + : 8; \ + \ + T **new_blocks = realloc( \ + vec->blocks, \ + new_cap * sizeof(T *) \ + ); \ + \ + if (!new_blocks) abort(); \ + \ + vec->blocks = new_blocks; \ + vec->cap = new_cap; \ + } \ + \ + vec->blocks[vec->used] = \ + malloc(vec->block_size * sizeof(T)); \ + \ + if (!vec->blocks[vec->used]) abort(); \ + \ + vec->used++; \ + } \ + \ + T *ptr = &vec->blocks[block_index][item_index]; \ + *ptr = item; \ + \ + vec->count++; \ + return ptr; \ + } \ + \ + static inline T* get_from_##Name(Name *vec, size_t index) { \ + if (index >= vec->count) return NULL; \ + \ + return &vec->blocks \ + [index / vec->block_size] \ + [index % vec->block_size]; \ + } + + +DECLARE_VEC(int, IntVec) + +DECLARE_VEC(char, String); +DECLARE_VEC(char*, StrVec) + + +void append_str_to_String(String* target, const char* src_str); +char* inner(String* str); + + + +#define DECLARE_MAYBE(T, Name) \ + typedef struct { \ + bool some; \ + T value; \ + } Name; + +DECLARE_MAYBE(char*, MaybeStr) + + +typedef struct { + char* key; + char* val; +} StrPair; +DECLARE_VEC(StrPair, KeyValMap) + +void set_map_pair(KeyValMap* map, char* key, char* val); +MaybeStr get_map_value(KeyValMap* map, char* key); +void unset_map_key(KeyValMap* map, char* key); + + +typedef struct { + KeyValMap aliases; + KeyValMap shell_vars; +} ShellState; + +char* get_var(char* varname, ShellState* shstate); |
