diff options
| author | _ov4 <ov4@ov4.org> | 2026-09-16 11:28:14 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-09-16 11:28:14 +0000 |
| commit | e489a2db5a1e56e66f6df12642ad8cfd988d4b30 (patch) | |
| tree | aafcb1d962e9ce24ddccf00aff82d0ed0804fc4c | |
| parent | 165ead4adbf8494db82f7be35c6f8d52ac47ebef (diff) | |
implement subshell, and fix vector operator[] unvalid index
| -rw-r--r-- | include/eval.h | 5 | ||||
| -rw-r--r-- | include/lexer/ast.h | 3 | ||||
| -rw-r--r-- | module-test/lexer_test.cc | 7 | ||||
| -rw-r--r-- | src/eval.cc | 88 | ||||
| -rw-r--r-- | src/lexer/ast.cc | 9 | ||||
| -rw-r--r-- | src/lexer/token.cc | 28 | ||||
| -rw-r--r-- | src/main.cc | 4 |
7 files changed, 118 insertions, 26 deletions
diff --git a/include/eval.h b/include/eval.h index 5c819a4..214a1ae 100644 --- a/include/eval.h +++ b/include/eval.h @@ -1,11 +1,12 @@ #pragma once #include "all.h" -#include "lexer/token.h" +#include "lexer/lexer_class.h" namespace ov4 { - +int eval_exe(const std::string &s, bool is_aync); +int eval_tree_cd(size_t i, const T_lexer &lexer_instance, bool inside_subshell); void eval(char *cmdline); } diff --git a/include/lexer/ast.h b/include/lexer/ast.h index d4b0fd5..871afaf 100644 --- a/include/lexer/ast.h +++ b/include/lexer/ast.h @@ -21,6 +21,9 @@ public: size_t left = string::npos; size_t right = string::npos; + // if this is marked as `subshell` + // it will fork a subshell + // then the subshell will handler the rest bool subshell; }; diff --git a/module-test/lexer_test.cc b/module-test/lexer_test.cc index 364c890..50b6da0 100644 --- a/module-test/lexer_test.cc +++ b/module-test/lexer_test.cc @@ -37,7 +37,7 @@ constexpr int MAXLINE = 1024; int main() { verbose = true; - int test_case = 15; + int test_case = 16; string s; switch (test_case) @@ -116,6 +116,11 @@ int main() s = "alpha && (beta || gamma) && (sleep &)"; break; + // \n test + case 16: + s = "(ls)"; + break; + default: return 0; } diff --git a/src/eval.cc b/src/eval.cc index 0192ab7..6d16cf7 100644 --- a/src/eval.cc +++ b/src/eval.cc @@ -30,7 +30,7 @@ extern char **environ; namespace ov4 { -int eval_exe(string s, bool is_aync) +int eval_exe(const string &s, bool is_aync) { loggerln("eval_exe: receive {}", s); @@ -69,10 +69,13 @@ int eval_exe(string s, bool is_aync) sigprocmask(SIG_SETMASK, &prev, nullptr); - // program inside execve may use SIGTTIN/SIGTTOU so just restore in child + // program inside execve may use SIGTTIN/SIGTTOU + // so just restore in child sigprocmask(SIG_UNBLOCK, &block_io, nullptr); execve(cmd_c, argv, environ); + // execve: if success, never returns + wrap_eliminator(cmdline); cout << cmdline << flush; switch (errno) @@ -135,12 +138,85 @@ int eval_exe(string s, bool is_aync) return 0; } -int eval_tree_cd(size_t i, const T_lexer &lexer_instance) +int eval_subshell(size_t i, const T_lexer &lexer_instance) +{ + println("eval_tree_cd: processing subshell"); + + // TODO: implement async + bool is_aync = false; + + char cmdline[] = "subshell"; + + // block_io signals can stop shell + // when child is at foreground, parent shell is at background + sigprocmask(SIG_BLOCK, &block_io, nullptr); + + sigset_t prev; + block_all(&prev); + + shell_pgid = getpgid(0); + pid_t pid = fork(); + + if (pid == 0) // child + { + setpgid(0, 0); + if (!is_aync) // foreground + tcsetpgrp(tty_fd, getpgrp()); + + sigprocmask(SIG_SETMASK, &prev, nullptr); + + // program inside execve may use SIGTTIN/SIGTTOU + // so just restore in child + sigprocmask(SIG_UNBLOCK, &block_io, nullptr); + eval_tree_cd(i, lexer_instance, true); + exit(0); + } + + // parent + + exit_required_pid = pid; + + if (!is_aync) // foreground + { + addjob(pid, FG, cmdline); + sigset_t prev_with_sigchld_blocked = prev; + sigaddset(&prev_with_sigchld_blocked, SIGCHLD); + sigprocmask(SIG_SETMASK, &prev_with_sigchld_blocked, NULL); + waitfg(pid); + sigprocmask(SIG_SETMASK, &prev, NULL); + + return exit_code; + } + else // background + { + addjob(pid, BG, cmdline); + sigprocmask(SIG_SETMASK, &prev, NULL); + cout << "[" << pid2jid(pid) << "] (" << pid << ") " << cmdline; + + // POSIX: + // async command returns 0 immediately + // TODO: recurring async + return 0; + } +} + +int eval_tree_cd(size_t i, const T_lexer &lexer_instance, bool inside_subshell) { // TODO: async int ret = -1; + println("eval_tree_cd: processing {} subshell {}", i, (lexer_instance.ast[i].subshell ? "TRUE" : "FALSE")); + + // if it's a subshell, and current AST's subshell status is not subshell + // (subshell should be forkerd already, then set inside_subshell = true) + // fork itself + if (lexer_instance.ast[i].subshell && !inside_subshell) + { + eval_subshell(i, lexer_instance); + return 0; + } + if (lexer_instance.ast[i].token_type == TEXT) { ret = eval_exe(lexer_instance.ast[i].command_text, false); @@ -149,7 +225,7 @@ int eval_tree_cd(size_t i, const T_lexer &lexer_instance) if (lexer_instance.ast[i].left != string::npos) { - ret = eval_tree_cd(lexer_instance.ast[i].left, lexer_instance); + ret = eval_tree_cd(lexer_instance.ast[i].left, lexer_instance, false); } if (lexer_instance.ast[i].right != string::npos) @@ -157,7 +233,7 @@ int eval_tree_cd(size_t i, const T_lexer &lexer_instance) if (lexer_instance.ast[i].token_type == LOGIC_AND && ret != 0) return ret; if (lexer_instance.ast[i].token_type == LOGIC_OR && ret == 0) return ret; - ret = eval_tree_cd(lexer_instance.ast[i].right, lexer_instance); + ret = eval_tree_cd(lexer_instance.ast[i].right, lexer_instance, false); } return ret; } @@ -190,7 +266,7 @@ void eval(char *cmdline) return; } - eval_tree_cd(0, lexer_instance); + eval_tree_cd(0, lexer_instance, false); return; } diff --git a/src/lexer/ast.cc b/src/lexer/ast.cc index 292728b..da96c6f 100644 --- a/src/lexer/ast.cc +++ b/src/lexer/ast.cc @@ -65,10 +65,11 @@ void T_lexer::parse(size_t cmd_begin, size_t cmd_end, size_t ast_vec) // if it's fully inside bracket // then it's subshell - loggerln("parse: left token type: {}, right: {}", - magic_enum::enum_name(token[cmd_begin].token_type), - magic_enum::enum_name(token[cmd_end-1].token_type)); - if (token[cmd_begin].token_type == LEFT_BRACKET && token[cmd_end-1].token_type == RIGHT_BRACKET) + if (cmd_begin != cmd_end) + loggerln("parse: left token type: {}, right: {}", + magic_enum::enum_name(token[cmd_begin].token_type), + magic_enum::enum_name(token[cmd_end-1].token_type)); + if (cmd_begin != cmd_end && token[cmd_begin].token_type == LEFT_BRACKET && token[cmd_end-1].token_type == RIGHT_BRACKET) { ast[ast_vec].subshell = true; loggerln("parse: detect subshell {}", ast_vec); diff --git a/src/lexer/token.cc b/src/lexer/token.cc index 317b456..ab9f263 100644 --- a/src/lexer/token.cc +++ b/src/lexer/token.cc @@ -12,6 +12,8 @@ using namespace std; // remove this +constexpr bool TOKEN_DEBUG = false; + namespace ov4 { @@ -43,7 +45,7 @@ void T_lexer::bracket_depth_changer(char c, int &bracket_depth) if (c == '(') { bracket_depth++; - loggerln("changed depth: {}", bracket_depth); + if constexpr (TOKEN_DEBUG) loggerln("changed depth: {}", bracket_depth); return; } if (c == ')') @@ -64,7 +66,7 @@ void T_lexer::bracket_depth_changer(char c, int &bracket_depth) */ bool T_lexer::token_continue(const string &s, char next) { - loggerln("token_continue: receiving {} {}", s, next); + if constexpr (TOKEN_DEBUG) loggerln("token_continue: receiving {} {}", s, next); // s is empty, so next can be part of the token if (s.size() == 0) return true; @@ -95,7 +97,7 @@ bool T_lexer::token_continue(const string &s, char next) } else { - loggerln("symbol not found in map"); + if constexpr (TOKEN_DEBUG) loggerln("symbol not found in map"); return false; } } @@ -108,16 +110,16 @@ void T_lexer::token_push(const string &push_s, int bracket_depth, bool force_tex string trimmed = trim(push_s); if (trimmed == "") return; - loggerln("finding {} in map", trimmed); + if constexpr (TOKEN_DEBUG) loggerln("finding {} in map", trimmed); if (symbol_property.contains(trimmed) && !force_text) // symbol { - loggerln("token_push: adding symbol"); + if constexpr (TOKEN_DEBUG) loggerln("token_push: adding symbol"); tmp.token_type = symbol_property.at(trimmed).enum_type; } else { - loggerln("token_push: adding text"); + if constexpr (TOKEN_DEBUG) loggerln("token_push: adding text"); if (force_text) trimmed = trim(trimmed, "\'\""); tmp.token_type = TEXT; @@ -145,32 +147,32 @@ int T_lexer::tokenizer(const string &s) prev_str += c; - loggerln("---"); - loggerln("prev_str .{}. next char {}", prev_str, s[i+1]); + if constexpr (TOKEN_DEBUG) loggerln("---"); + if constexpr (TOKEN_DEBUG) loggerln("prev_str .{}. next char {}", prev_str, s[i+1]); if (c == '\'' || c == '\"') { - loggerln("quotation"); + if constexpr (TOKEN_DEBUG) loggerln("quotation"); // turn on GUARD if (guard == GUARD_OFF) { guard = (c == '\'') ? GUARD_STRONG : GUARD_WEAK; - loggerln("set guard {}", (c == '\'') ? "GUARD_STRONG" : "GUARD_WEAK"); + if constexpr (TOKEN_DEBUG) loggerln("set guard {}", (c == '\'') ? "GUARD_STRONG" : "GUARD_WEAK"); continue; } // turn off else if (guard == GUARD_STRONG && c == '\'') { guard = GUARD_OFF; - loggerln("quitting strong guard"); + if constexpr (TOKEN_DEBUG) loggerln("quitting strong guard"); force_text = true; } //turn off else if (guard == GUARD_WEAK && c == '\"') { guard = GUARD_OFF; - loggerln("quitting weak guard"); + if constexpr (TOKEN_DEBUG) loggerln("quitting weak guard"); force_text = true; } else continue; @@ -178,7 +180,7 @@ int T_lexer::tokenizer(const string &s) // not quotation mark else if ((guard == GUARD_STRONG || guard == GUARD_WEAK)) { - loggerln("tokenizer: guarded, continue"); + if constexpr (TOKEN_DEBUG) loggerln("tokenizer: guarded, continue"); continue; } diff --git a/src/main.cc b/src/main.cc index a884f78..088b4ca 100644 --- a/src/main.cc +++ b/src/main.cc @@ -75,7 +75,11 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char **argv) exit(0); } + /* Evaluate the command line */ + // trim \n + if(cmdline[strlen(cmdline)-1] == '\n') + cmdline[strlen(cmdline)-1] = '\0'; eval(cmdline); fflush(stdout); fflush(stdout); |
