myshell 2.0.0
Loading...
Searching...
No Matches
msh_command.h
1// This is a personal academic project. Dear PVS-Studio, please check it.
2// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
3
4//
5// Created by andrew on 10/8/23.
6//
7
8#ifndef TEMPLATE_MSH_COMMAND_H
9#define TEMPLATE_MSH_COMMAND_H
10
11#include "internal/msh_error.h"
12#include "internal/msh_builtin.h"
13#include "internal/msh_exec.h"
14#include "internal/msh_jobs.h"
15#include "internal/msh_utils.h"
16#include "internal/msh_redirects.h"
17
18#include "msh_redirect.h"
19#include "msh_token.h"
20#include "msh_command_fwd.h"
21#include "msh_exception.h"
22
23
24#include <string>
25#include <utility>
26#include <vector>
27#include <utility>
28#include <iostream>
29#include <algorithm>
30#include <array>
31#include <memory>
32#include <variant>
33#include <sys/wait.h>
34
35
48struct command {
49 std::variant<simple_command_ptr, connection_command_ptr> cmd;
50 int flags = 0;
51
61 int execute(int in = STDIN_FILENO, int out = STDOUT_FILENO) {
62 std::visit([this, &in, &out](auto &&arg) {
63 if (arg) {
64 msh_errno = msh_exec_internal(*this, in, out, flags);
65 }
66 }, cmd);
67 return msh_errno;
68 }
69
70 void set_flags(int flag) {
71 flags |= flag;
72 }
73};
74
75
87typedef struct simple_command {
88 using args_t = std::vector<std::string>;
89 using argv_c_t = std::vector<char *>;
90 using redirects_t = std::vector<redirect>;
91
92 tokens_t tokens;
93 args_t argv;
94 argv_c_t argv_c;
95 std::array<int, 3> saved_fds{};
96 redirects_t redirects;
97 int argc = 0;
98
99 explicit simple_command(tokens_t tokens) : tokens(std::move(tokens)) {}
100
108 bool construct() {
109 for (auto const &token: tokens) {
110 if (token.get_flag(WORD_LIKE) && !token.value.empty()) {
111 argv.push_back(token.value);
112 }
113 }
114 for (auto &arg: argv) {
115 argv_c.push_back(arg.data());
116 }
117 argv_c.push_back(nullptr);
118 return (argc = static_cast<int>(argv.size())) != 0;
119 }
120
133 int do_redirects(std::vector<int> *fd_to_close) {
134 if (redirects.empty()) {
135 return 0;
136 }
137
138 saved_fds[0] = dup(STDIN_FILENO);
139 saved_fds[1] = dup(STDOUT_FILENO);
140 saved_fds[2] = dup(STDERR_FILENO);
141 for (auto const &redirect: redirects) {
142 if (auto res = redirect.do_redirect(fd_to_close); res != 0) {
143 return res;
144 }
145 }
146 return 0;
147 }
148
159 void undo_redirects(std::vector<int> const &fd_to_close) {
160 if (redirects.empty()) {
161 return;
162 }
163
164 dup2(saved_fds[0], STDIN_FILENO);
165 dup2(saved_fds[1], STDOUT_FILENO);
166 dup2(saved_fds[2], STDERR_FILENO);
167 std::ranges::for_each(saved_fds, close);
168 std::ranges::for_each(fd_to_close, close);
169 }
170
181 int execute(int in = STDIN_FILENO, int out = STDOUT_FILENO, int flags = 0) {
182 try {
183 process_tokens(tokens);
184 redirects = parse_redirects(tokens);
185 } catch (msh_exception &e) {
186 msh_error(e.what());
187 return e.code();
188 }
189
190 if (!construct()) {
191 return 0;
192 }
193
194 is_builtin(argv[0]) ? flags |= BUILTIN : flags |= 0;
195 msh_errno = msh_exec_simple(*this, in, out, flags);
196
197 return msh_errno;
198 }
200
201
214typedef struct connection_command {
215 Token connector;
216 command lhs;
217 command rhs;
218
230 int execute(int in = STDIN_FILENO, int out = STDOUT_FILENO, int flags = 0) {
231 switch (connector.type) {
232 using enum TokenType;
233 case TokenType::PIPE_AMP:
234 flags |= PIPE_STDERR;
235 [[fallthrough]];
236 case PIPE:
237 return execute_pipeline(in, out, flags);
238 case SEMICOLON:
239 return execute_sequence(in, out, flags);
240 case AMP:
241 return execute_background(in, out, flags);
242 case TokenType::AND:
243 case TokenType::OR:
244 return execute_conditional(in, out, flags, connector.type);
245 default:
246 return UNKNOWN_ERROR; // FIXME: Add error handling
247 }
248 }
249
250private:
265 int execute_conditional(int in, int out, int flags, TokenType op) {
266 lhs.set_flags(flags & ASYNC);
267 rhs.set_flags(flags & ASYNC);
268
269 auto res = flags & FORCE_PIPE ? lhs.execute(in, out) : lhs.execute();
270 if ((res == 0 && op == TokenType::AND) || (res != 0 && op == TokenType::OR)) {
271 rhs.execute(in, out);
272 }
273
274 return res;
275 }
276
289 int execute_background(int in, int out, int flags) {
290 lhs.set_flags(ASYNC);
291 rhs.set_flags(flags & ASYNC);
292
293 flags & FORCE_PIPE ? lhs.execute(in, out) : lhs.execute();
294 rhs.execute(in, out);
295 return msh_errno;
296 }
297
310 int execute_sequence(int in, int out, int flags) {
311 rhs.set_flags(flags & ASYNC);
312
313 flags & FORCE_PIPE ? lhs.execute(in, out) : lhs.execute();
314 rhs.execute(in, out);
315 return msh_errno;
316 }
317
332 int execute_pipeline(int in, int out, int flags) {
333 int pipefd[2];
334 if (pipe(pipefd) == -1) {
335 msh_error(strerror(errno));
336 return UNKNOWN_ERROR; // FIXME: Add error handling
337 }
338
339 lhs.set_flags(flags | FORK_NO_WAIT);
340 rhs.set_flags(flags);
341
342 lhs.execute(in, pipefd[1]);
343 close(pipefd[1]);
344 rhs.execute(pipefd[0], out);
345 close(pipefd[0]);
346
347 if (!(flags & FORK_NO_WAIT) && !(flags & ASYNC)) {
349 }
350 return msh_errno;
351 }
353
354#endif //TEMPLATE_MSH_COMMAND_H
Internal exception class.
Definition msh_exception.h:18
constexpr int WORD_LIKE
This token is a potential argument/command that will be forwarded to argv.
Definition msh_token.h:57
bool is_builtin(const std::string &cmd)
Check if a command is a built-in command.
Definition msh_builtin.cpp:47
void msh_error(const std::string &msg)
Print an error message to stderr with a myshell: prefix.
Definition msh_error.cpp:37
int msh_exec_internal(command &cmd, int in, int out, int flags)
Internal command execution function.
Definition msh_exec.cpp:220
int msh_exec_simple(simple_command &cmd, int pipe_in=STDIN_FILENO, int pipe_out=STDOUT_FILENO, int flags=0)
Executes a simple command.
Definition msh_exec.cpp:146
int reap_children()
Wait for all background processes to finish.
Definition msh_jobs.cpp:114
redirects_t parse_redirects(tokens_t &tokens)
Parse redirects from command's tokens.
Definition msh_redirects.cpp:57
Token structure and related types.
void process_tokens(tokens_t &tokens)
Process a vector of tokens.
Definition msh_utils.cpp:519
Structure representing a token.
Definition msh_token.h:74
Top-level command structure.
Definition msh_command.h:48
int execute(int in=STDIN_FILENO, int out=STDOUT_FILENO)
Execute the command.
Definition msh_command.h:61
Connection command structure.
Definition msh_command.h:214
int execute_pipeline(int in, int out, int flags)
Executes pipeline connections, i.e. | and |&.
Definition msh_command.h:332
int execute_sequence(int in, int out, int flags)
Executes sequential connections, i.e. ;.
Definition msh_command.h:310
int execute(int in=STDIN_FILENO, int out=STDOUT_FILENO, int flags=0)
Execute the command.
Definition msh_command.h:230
int execute_conditional(int in, int out, int flags, TokenType op)
Executes conditional connections, i.e. && and ||.
Definition msh_command.h:265
int execute_background(int in, int out, int flags)
Executes background connections, i.e. &.
Definition msh_command.h:289
Structure representing a single redirection.
Definition msh_redirect.h:72
int do_redirect(std::vector< int > *fd_to_close) const
Opens the redirectees with respect to the redirection type and duplicates the appropriate file descri...
Definition msh_redirect.h:122
Simple command structure.
Definition msh_command.h:87
int do_redirects(std::vector< int > *fd_to_close)
Perform redirections attached to the command.
Definition msh_command.h:133
void undo_redirects(std::vector< int > const &fd_to_close)
Undo redirections attached to the command.
Definition msh_command.h:159
int execute(int in=STDIN_FILENO, int out=STDOUT_FILENO, int flags=0)
Execute the command.
Definition msh_command.h:181
bool construct()
Construct the command.
Definition msh_command.h:108