1use futures::future::BoxFuture;
2use std::collections::HashMap;
3use std::io::Write;
4
5#[allow(clippy::wildcard_imports)]
6use super::*;
7
8use crate::builtins;
9use crate::commands::{self, CommandArg};
10use crate::error;
11
12pub trait SimpleCommand {
14 fn get_content(name: &str, content_type: builtins::ContentType)
16 -> Result<String, error::Error>;
17
18 fn execute<I: Iterator<Item = S>, S: AsRef<str>>(
20 context: commands::ExecutionContext<'_>,
21 args: I,
22 ) -> Result<builtins::BuiltinResult, error::Error>;
23}
24
25pub fn simple_builtin<B: SimpleCommand + Send + Sync>() -> builtins::Registration {
28 builtins::Registration {
29 execute_func: exec_simple_builtin::<B>,
30 content_func: B::get_content,
31 disabled: false,
32 special_builtin: false,
33 declaration_builtin: false,
34 }
35}
36
37pub fn builtin<B: builtins::Command + Send + Sync>() -> builtins::Registration {
40 builtins::Registration {
41 execute_func: exec_builtin::<B>,
42 content_func: get_builtin_content::<B>,
43 disabled: false,
44 special_builtin: false,
45 declaration_builtin: false,
46 }
47}
48
49fn decl_builtin<B: builtins::DeclarationCommand + Send + Sync>() -> builtins::Registration {
50 builtins::Registration {
51 execute_func: exec_declaration_builtin::<B>,
52 content_func: get_builtin_content::<B>,
53 disabled: false,
54 special_builtin: false,
55 declaration_builtin: true,
56 }
57}
58
59fn get_builtin_content<T: builtins::Command + Send + Sync>(
60 name: &str,
61 content_type: builtins::ContentType,
62) -> Result<String, error::Error> {
63 T::get_content(name, content_type)
64}
65
66fn exec_simple_builtin<T: SimpleCommand + Send + Sync>(
67 context: commands::ExecutionContext<'_>,
68 args: Vec<CommandArg>,
69) -> BoxFuture<'_, Result<builtins::BuiltinResult, error::Error>> {
70 Box::pin(async move { exec_simple_builtin_impl::<T>(context, args).await })
71}
72
73#[allow(clippy::unused_async)]
74async fn exec_simple_builtin_impl<T: SimpleCommand + Send + Sync>(
75 context: commands::ExecutionContext<'_>,
76 args: Vec<CommandArg>,
77) -> Result<builtins::BuiltinResult, error::Error> {
78 let plain_args = args.into_iter().map(|arg| match arg {
79 CommandArg::String(s) => s,
80 CommandArg::Assignment(a) => a.to_string(),
81 });
82
83 T::execute(context, plain_args)
84}
85
86fn exec_builtin<T: builtins::Command + Send + Sync>(
87 context: commands::ExecutionContext<'_>,
88 args: Vec<CommandArg>,
89) -> BoxFuture<'_, Result<builtins::BuiltinResult, error::Error>> {
90 Box::pin(async move { exec_builtin_impl::<T>(context, args).await })
91}
92
93async fn exec_builtin_impl<T: builtins::Command + Send + Sync>(
94 context: commands::ExecutionContext<'_>,
95 args: Vec<CommandArg>,
96) -> Result<builtins::BuiltinResult, error::Error> {
97 let plain_args = args.into_iter().map(|arg| match arg {
98 CommandArg::String(s) => s,
99 CommandArg::Assignment(a) => a.to_string(),
100 });
101
102 let result = T::new(plain_args);
103 let command = match result {
104 Ok(command) => command,
105 Err(e) => {
106 writeln!(context.stderr(), "{e}")?;
107 return Ok(builtins::BuiltinResult {
108 exit_code: builtins::ExitCode::InvalidUsage,
109 });
110 }
111 };
112
113 Ok(builtins::BuiltinResult {
114 exit_code: command.execute(context).await?,
115 })
116}
117
118fn exec_declaration_builtin<T: builtins::DeclarationCommand + Send + Sync>(
119 context: commands::ExecutionContext<'_>,
120 args: Vec<CommandArg>,
121) -> BoxFuture<'_, Result<builtins::BuiltinResult, error::Error>> {
122 Box::pin(async move { exec_declaration_builtin_impl::<T>(context, args).await })
123}
124
125async fn exec_declaration_builtin_impl<T: builtins::DeclarationCommand + Send + Sync>(
126 context: commands::ExecutionContext<'_>,
127 args: Vec<CommandArg>,
128) -> Result<builtins::BuiltinResult, error::Error> {
129 let mut options = vec![];
130 let mut declarations = vec![];
131
132 for (i, arg) in args.into_iter().enumerate() {
133 match arg {
134 CommandArg::String(s) if i == 0 || s.starts_with('-') || s.starts_with('+') => {
135 options.push(s);
136 }
137 _ => declarations.push(arg),
138 }
139 }
140
141 let result = T::new(options);
142 let mut command = match result {
143 Ok(command) => command,
144 Err(e) => {
145 writeln!(context.stderr(), "{e}")?;
146 return Ok(builtins::BuiltinResult {
147 exit_code: builtins::ExitCode::InvalidUsage,
148 });
149 }
150 };
151
152 command.set_declarations(declarations);
153
154 Ok(builtins::BuiltinResult {
155 exit_code: command.execute(context).await?,
156 })
157}
158
159#[allow(clippy::too_many_lines)]
160pub(crate) fn get_default_builtins(
161 options: &crate::CreateOptions,
162) -> HashMap<String, builtins::Registration> {
163 let mut m = HashMap::<String, builtins::Registration>::new();
164
165 m.insert("break".into(), builtin::<break_::BreakCommand>().special());
173 m.insert(
174 ":".into(),
175 simple_builtin::<colon::ColonCommand>().special(),
176 );
177 m.insert(
178 "continue".into(),
179 builtin::<continue_::ContinueCommand>().special(),
180 );
181 m.insert(".".into(), builtin::<dot::DotCommand>().special());
182 m.insert("eval".into(), builtin::<eval::EvalCommand>().special());
183 #[cfg(unix)]
184 m.insert("exec".into(), builtin::<exec::ExecCommand>().special());
185 m.insert("exit".into(), builtin::<exit::ExitCommand>().special());
186 m.insert(
187 "export".into(),
188 decl_builtin::<export::ExportCommand>().special(),
189 );
190 m.insert(
191 "return".into(),
192 builtin::<return_::ReturnCommand>().special(),
193 );
194 m.insert("set".into(), builtin::<set::SetCommand>().special());
195 m.insert("shift".into(), builtin::<shift::ShiftCommand>().special());
196 m.insert("trap".into(), builtin::<trap::TrapCommand>().special());
197 m.insert("unset".into(), builtin::<unset::UnsetCommand>().special());
198
199 m.insert(
200 "readonly".into(),
201 decl_builtin::<declare::DeclareCommand>().special(),
202 );
203 m.insert("times".into(), builtin::<times::TimesCommand>().special());
204
205 m.insert("alias".into(), builtin::<alias::AliasCommand>()); m.insert("bg".into(), builtin::<bg::BgCommand>());
211 m.insert("cd".into(), builtin::<cd::CdCommand>());
212 m.insert("command".into(), builtin::<command::CommandCommand>());
213 m.insert("false".into(), builtin::<false_::FalseCommand>());
214 m.insert("fg".into(), builtin::<fg::FgCommand>());
215 m.insert("getopts".into(), builtin::<getopts::GetOptsCommand>());
216 m.insert("hash".into(), builtin::<hash::HashCommand>());
217 m.insert("help".into(), builtin::<help::HelpCommand>());
218 m.insert("jobs".into(), builtin::<jobs::JobsCommand>());
219 #[cfg(unix)]
220 m.insert("kill".into(), builtin::<kill::KillCommand>());
221 m.insert("local".into(), decl_builtin::<declare::DeclareCommand>());
222 m.insert("pwd".into(), builtin::<pwd::PwdCommand>());
223 m.insert("read".into(), builtin::<read::ReadCommand>());
224 m.insert("true".into(), builtin::<true_::TrueCommand>());
225 m.insert("type".into(), builtin::<type_::TypeCommand>());
226 #[cfg(unix)]
227 m.insert("umask".into(), builtin::<umask::UmaskCommand>());
228 m.insert("unalias".into(), builtin::<unalias::UnaliasCommand>());
229 m.insert("wait".into(), builtin::<wait::WaitCommand>());
230
231 m.insert("fc".into(), builtin::<unimp::UnimplementedCommand>());
233 m.insert("ulimit".into(), builtin::<unimp::UnimplementedCommand>());
234
235 if !options.sh_mode {
236 m.insert("builtin".into(), builtin::<builtin_::BuiltinCommand>());
237 m.insert("declare".into(), decl_builtin::<declare::DeclareCommand>());
238 m.insert("echo".into(), builtin::<echo::EchoCommand>());
239 m.insert("enable".into(), builtin::<enable::EnableCommand>());
240 m.insert("let".into(), builtin::<let_::LetCommand>());
241 m.insert("mapfile".into(), builtin::<mapfile::MapFileCommand>());
242 m.insert("printf".into(), builtin::<printf::PrintfCommand>());
243 m.insert("shopt".into(), builtin::<shopt::ShoptCommand>());
244 m.insert("source".into(), builtin::<dot::DotCommand>().special());
245 #[cfg(unix)]
246 m.insert("suspend".into(), builtin::<suspend::SuspendCommand>());
247 m.insert("test".into(), builtin::<test::TestCommand>());
248 m.insert("[".into(), builtin::<test::TestCommand>());
249 m.insert("typeset".into(), builtin::<declare::DeclareCommand>());
250
251 m.insert("complete".into(), builtin::<complete::CompleteCommand>());
253 m.insert("compgen".into(), builtin::<complete::CompGenCommand>());
254 m.insert("compopt".into(), builtin::<complete::CompOptCommand>());
255
256 m.insert("dirs".into(), builtin::<dirs::DirsCommand>());
258 m.insert("popd".into(), builtin::<popd::PopdCommand>());
259 m.insert("pushd".into(), builtin::<pushd::PushdCommand>());
260
261 m.insert("bind".into(), builtin::<bind::BindCommand>());
263
264 m.insert("caller".into(), builtin::<unimp::UnimplementedCommand>());
266 m.insert("disown".into(), builtin::<unimp::UnimplementedCommand>());
267 m.insert("history".into(), builtin::<unimp::UnimplementedCommand>());
268 m.insert("logout".into(), builtin::<unimp::UnimplementedCommand>());
269 m.insert("readarray".into(), builtin::<unimp::UnimplementedCommand>());
270 }
271
272 m.insert("brushinfo".into(), builtin::<brushinfo::BrushInfoCommand>());
276
277 m
278}