-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathminishell_builtins.hpp
More file actions
259 lines (224 loc) · 8.13 KB
/
Copy pathminishell_builtins.hpp
File metadata and controls
259 lines (224 loc) · 8.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#ifndef MINISHELL_BUILTINS_HPP
#define MINISHELL_BUILTINS_HPP
#include <string>
#include <vector>
#include <unordered_map>
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <cctype>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <filesystem>
// Version info
#define MINISHELL_VERSION "1.0.0"
namespace mshell {
// -------------------------------------------------------------------------------------------------
// Environment for builtins (currently: alias table).
// -------------------------------------------------------------------------------------------------
struct BuiltinEnv {
std::unordered_map<std::string, std::string> aliases;
};
// -------------------------------------------------------------------------------------------------
// Small helpers
// -------------------------------------------------------------------------------------------------
inline std::string home_dir() {
if (const char* h = std::getenv("HOME")) return h;
if (passwd* pw = getpwuid(getuid())) {
if (pw->pw_dir) return pw->pw_dir;
}
return "/";
}
inline void trim_inplace(std::string& s) {
while (!s.empty() && std::isspace(static_cast<unsigned char>(s.front()))) s.erase(s.begin());
while (!s.empty() && std::isspace(static_cast<unsigned char>(s.back()))) s.pop_back();
}
inline std::string unquote_if(const std::string& s) {
if (s.size() >= 2) {
char q = s.front();
if ((q == '\'' || q == '"') && s.back() == q) return s.substr(1, s.size() - 2);
}
return s;
}
// -------------------------------------------------------------------------------------------------
// Minimal rc-line evaluator (supports: alias, export, echo, setprompt) – used by load_rc and source
// -------------------------------------------------------------------------------------------------
inline void eval_rc_line(BuiltinEnv& env, std::string line) {
// strip comments
auto hash = line.find('#');
if (hash != std::string::npos) line = line.substr(0, hash);
trim_inplace(line);
if (line.empty()) return;
if (line.rfind("alias ", 0) == 0) {
auto eq = line.find('=');
if (eq != std::string::npos) {
std::string key = line.substr(6, eq - 6);
std::string val = line.substr(eq + 1);
trim_inplace(key); trim_inplace(val);
env.aliases[key] = unquote_if(val);
}
return;
}
if (line.rfind("export ", 0) == 0) {
std::string rest = line.substr(7);
auto eq = rest.find('=');
if (eq != std::string::npos) {
std::string k = rest.substr(0, eq);
std::string v = rest.substr(eq + 1);
trim_inplace(k); trim_inplace(v);
setenv(k.c_str(), v.c_str(), 1);
}
return;
}
if (line.rfind("echo ", 0) == 0) {
std::cout << line.substr(5) << "\n";
return;
}
if (line.rfind("setprompt ", 0) == 0) {
std::string v = unquote_if(line.substr(10));
setenv("MINISHELL_PROMPT", v.c_str(), 1);
return;
}
}
// -------------------------------------------------------------------------------------------------
// Load ~/.minishellrc
// -------------------------------------------------------------------------------------------------
inline void load_rc(BuiltinEnv& env) {
std::ifstream in(home_dir() + "/.minishellrc");
if (!in) return;
std::string line;
while (std::getline(in, line)) {
eval_rc_line(env, line);
}
}
// -------------------------------------------------------------------------------------------------
// Builtin dispatcher
// Return true if command handled; set exit_status accordingly.
// -------------------------------------------------------------------------------------------------
inline bool builtin_dispatch(BuiltinEnv& env,
const std::vector<std::string>& argv,
int& exit_status)
{
if (argv.empty()) { exit_status = 0; return true; }
const std::string& cmd = argv[0];
// Handle --version flag
if (cmd == "--version" || (argv.size() > 1 && argv[1] == "--version")) {
std::cout << "MiniShell version " << MINISHELL_VERSION << "\n";
exit_status = 0; return true;
}
if (cmd == "cd") {
const char* target = (argv.size() > 1) ? argv[1].c_str() : std::getenv("HOME");
if (!target) target = "/";
if (chdir(target) != 0) std::cerr << "cd: " << std::strerror(errno) << "\n";
exit_status = 0; return true;
}
if (cmd == "pwd") {
char buf[4096];
if (getcwd(buf, sizeof(buf))) std::cout << buf << "\n";
exit_status = 0; return true;
}
if (cmd == "echo") {
for (size_t i = 1; i < argv.size(); ++i) {
if (i > 1) std::cout << ' ';
std::cout << argv[i];
}
std::cout << "\n";
exit_status = 0; return true;
}
if (cmd == "export") {
for (size_t i = 1; i < argv.size(); ++i) {
auto eq = argv[i].find('=');
if (eq != std::string::npos) {
std::string k = argv[i].substr(0, eq);
std::string v = argv[i].substr(eq + 1);
setenv(k.c_str(), v.c_str(), 1);
}
}
exit_status = 0; return true;
}
if (cmd == "unset") {
for (size_t i = 1; i < argv.size(); ++i) unsetenv(argv[i].c_str());
exit_status = 0; return true;
}
if (cmd == "alias") {
if (argv.size() == 1) {
for (const auto& kv : env.aliases)
std::cout << "alias " << kv.first << "='" << kv.second << "'\n";
} else {
for (size_t i = 1; i < argv.size(); ++i) {
auto eq = argv[i].find('=');
if (eq != std::string::npos) {
std::string k = argv[i].substr(0, eq);
std::string v = unquote_if(argv[i].substr(eq + 1));
env.aliases[k] = v;
}
}
}
exit_status = 0; return true;
}
if (cmd == "unalias") {
if (argv.size() > 1) env.aliases.erase(argv[1]);
exit_status = 0; return true;
}
if ((cmd == "source" || cmd == ".") && argv.size() > 1) {
std::ifstream in(argv[1]);
if (!in) {
std::cerr << cmd << ": cannot open " << argv[1] << "\n";
exit_status = 1; return true;
}
std::string line;
while (std::getline(in, line)) eval_rc_line(env, line);
exit_status = 0; return true;
}
(void)env;
return false;
}
// -------------------------------------------------------------------------------------------------
// Alias expansion (single-word head) with safety:
// - Ignores empty/whitespace alias bodies
// - Prevents simple recursion (depth cap)
// -------------------------------------------------------------------------------------------------
inline std::vector<std::string> alias_expand(BuiltinEnv& env,
const std::vector<std::string>& argv)
{
if (argv.empty()) return argv;
auto it = env.aliases.find(argv[0]);
if (it == env.aliases.end()) return argv;
std::string body = it->second;
trim_inplace(body);
if (body.empty()) return argv;
std::vector<std::string> head;
{
std::istringstream iss(body);
std::string w;
while (iss >> w) head.push_back(w);
}
if (head.empty()) return argv;
static thread_local int depth = 0;
if (depth > 10) return argv;
++depth;
if (head[0] == argv[0]) {
head.insert(head.end(), argv.begin() + 1, argv.end());
--depth;
return head;
}
head.insert(head.end(), argv.begin() + 1, argv.end());
--depth;
return head;
}
inline bool try_autocd(const std::vector<std::string>& argv) {
if (argv.empty()) return false;
std::error_code ec;
if (std::filesystem::is_directory(argv[0], ec)) {
if (chdir(argv[0].c_str()) != 0)
std::cerr << "cd: " << std::strerror(errno) << "\n";
return true;
}
return false;
}
}
#endif // MINISHELL_BUILTINS_HPP