#include #include #include #include #include "syntax.h" #include "utils.h" #include "stages.h" typedef struct { StrVec completed_fields; String open_buf; } SplitBuilder; void consolidate_split_builder(SplitBuilder* builder) { if (builder->open_buf.count > 0) { push_to_StrVec(&builder->completed_fields, inner(&builder->open_buf)); builder->open_buf = make_String(256); } } void split_str_on_space(char* str, StrVec* result) { String buf = make_String(256); for (int pos = 0; str[pos] != '\0'; pos++) { if (str[pos] == ' ') { push_to_StrVec(result, inner(&buf)); buf = make_String(256); } else { push_to_String(&buf, str[pos]); } } if (buf.count > 0) { char* buf_str = inner(&buf); push_to_StrVec(result, buf_str); } } char* get_cmdsub_output(TokenVec sub_region, ShellState* shstate) { int pipe_fds[2]; pipe(pipe_fds); PipelineTree sub_tree = process_ast(sub_region, shstate); // TODO: tree cleanup // TODO: is this ok? where should we manage the pgid? int pgid = -1; Channel write_cha = {.fd = pipe_fds[1], .parent_proc_closable = false, .child_proc_closable = true}; execute_command_pipeline(*sub_tree.root, STDIN_CHA, write_cha, true, &pgid, shstate); close(pipe_fds[1]); String output_buf = make_String(256); char str_buf[256]; ssize_t len; while ((len = read(pipe_fds[0], str_buf, sizeof(str_buf))) > 0) { append_str_to_String(&output_buf, str_buf); } close(pipe_fds[0]); return inner(&output_buf); } void process_chunk(Chunk chunk, SplitBuilder* builder, ShellState* shstate) { assert(!(chunk.qmode == SINGLE_Q && chunk.kind != CHUNK_LIT)); char* chunk_str; switch (chunk.kind) { case CHUNK_LIT: chunk_str = chunk.as.lit_str; break; case CHUNK_VAR: chunk_str = get_var(chunk.as.varname, shstate); break; case CHUNK_CMDSUB: chunk_str = get_cmdsub_output(*(TokenVec*)chunk.as.cmdsub_toks, shstate); break; default: exit(77); } if (chunk.qmode == UN_Q) { StrVec split_str = make_StrVec(32); split_str_on_space(chunk_str, &split_str); append_str_to_String(&builder->open_buf, *get_from_StrVec(&split_str, 0)); consolidate_split_builder(builder); for (int i = 1; i < split_str.count; i++) { push_to_StrVec(&builder->completed_fields, *get_from_StrVec(&split_str, i)); } } else { append_str_to_String(&builder->open_buf, chunk_str); } } StrVec process_word(Word word, ShellState* shstate) { SplitBuilder split_bui = {.completed_fields = make_StrVec(256), .open_buf = make_String(256)}; for (int i = 0; i < word.count; i++) { Chunk chunk = *get_from_Word(&word, i); process_chunk(chunk, &split_bui, shstate); } consolidate_split_builder(&split_bui); return split_bui.completed_fields; } typedef struct { TokenKind kind; union { char* word; SpecialToken spec; } as; } ExpandedToken; DECLARE_VEC(ExpandedToken, ExpandedTokenVec) Pipeline* process_token_sequence(TokenVec tokens, PipelineVec* pl_pool, ShellState* shstate) { ExpandedTokenVec exp_tokens = make_ExpandedTokenVec(256); for (int m = 0; m < tokens.count; m++) { Token tok = *get_from_TokenVec(&tokens, m); StrVec strs; switch (tok.kind) { case TOK_SPEC: push_to_ExpandedTokenVec(&exp_tokens, (ExpandedToken){.kind = TOK_SPEC, .as.spec = tok.as.spec}); break; case TOK_WORD: strs = process_word(tok.as.word, shstate); for (int n = 0; n < strs.count; n++) { push_to_ExpandedTokenVec(&exp_tokens, (ExpandedToken){.kind = TOK_WORD, .as.word = *get_from_StrVec(&strs, n)}); } } } Pipeline pl = make_Pipeline(256); size_t tok_pos = 0; PlCommand curr_cmd = {.args = make_ArgVec(256), .redirects = make_RedirectVec(256), .conn = CONN_EOL}; PlCommand* root_pl; for (int i = 0; i < exp_tokens.count; i++) { ExpandedToken curr_tok = *get_from_ExpandedTokenVec(&exp_tokens, i); ExpandedToken next_tok; Redirect rd; Pipeline* sub_pl; switch (curr_tok.kind) { case TOK_WORD: push_to_ArgVec(&curr_cmd.args, (Arg){.kind = ARG_LIT, .as.lit_str = curr_tok.as.word}); break; case TOK_SPEC: switch (curr_tok.as.spec.kind) { case SPEC_OP: switch (curr_tok.as.spec.as.op) { case OP_PIPE: curr_cmd.conn = CONN_PIPE; push_to_Pipeline(&pl, curr_cmd); curr_cmd = (PlCommand){.args = make_ArgVec(256), .redirects = make_RedirectVec(256), .conn = CONN_EOL}; break; case OP_EOL: push_to_Pipeline(&pl, curr_cmd); break; case OP_REDIR_R: next_tok = *get_from_ExpandedTokenVec(&exp_tokens, ++i); assert(next_tok.kind == TOK_WORD); rd = (Redirect){.kind = REDIR_READ, .as.filename = next_tok.as.word}; push_to_RedirectVec(&curr_cmd.redirects, rd); break; case OP_REDIR_W_APPEND: next_tok = *get_from_ExpandedTokenVec(&exp_tokens, ++i); assert(next_tok.kind == TOK_WORD); rd = (Redirect){.kind = REDIR_WRITE_APPEND, .as.filename = next_tok.as.word}; push_to_RedirectVec(&curr_cmd.redirects, rd); break; case OP_REDIR_W_TRUNC: next_tok = *get_from_ExpandedTokenVec(&exp_tokens, ++i); assert(next_tok.kind == TOK_WORD); rd = (Redirect){.kind = REDIR_WRITE_TRUNC, .as.filename = next_tok.as.word}; push_to_RedirectVec(&curr_cmd.redirects, rd); break; default: exit(63); } break; case SPEC_SUB_IN: sub_pl = process_token_sequence(*(TokenVec*)curr_tok.as.spec.as.proc_sub_region, pl_pool, shstate); push_to_ArgVec(&curr_cmd.args, (Arg){.kind = ARG_PS_IN, .as.sub_pipeline = sub_pl}); break; case SPEC_SUB_OUT: sub_pl = process_token_sequence(*(TokenVec*)curr_tok.as.spec.as.proc_sub_region, pl_pool, shstate); push_to_ArgVec(&curr_cmd.args, (Arg){.kind = ARG_PS_OUT, .as.sub_pipeline = sub_pl}); break; } } } return push_to_PipelineVec(pl_pool, pl); } PipelineTree process_ast(TokenVec root, ShellState* shstate) { PipelineVec pls = make_PipelineVec(256); Pipeline* root_pl = process_token_sequence(root, &pls, shstate); return (PipelineTree){.pl_pool = pls, root_pl}; // TODO: ast cleanup }