mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 13:32:45 +00:00 
			
		
		
		
	Shell: Allow assignment-prefixed commands to run builtins
`env` is not capable of running shell builtins, so make a simple builtin in its place.
This commit is contained in:
		
							parent
							
								
									e2336d9de5
								
							
						
					
					
						commit
						fca5a34ad3
					
				
					 3 changed files with 131 additions and 52 deletions
				
			
		|  | @ -1876,6 +1876,62 @@ ErrorOr<int> Shell::builtin_read(Main::Arguments arguments) | |||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<int> Shell::builtin_run_with_env(Main::Arguments arguments) | ||||
| { | ||||
|     Vector<DeprecatedString> environment_variables; | ||||
|     Vector<StringView> command_and_arguments; | ||||
| 
 | ||||
|     Core::ArgsParser parser; | ||||
|     parser.add_option(environment_variables, "Environment variables to set", "env", 'e', "NAME=VALUE"); | ||||
|     parser.add_positional_argument(command_and_arguments, "Command and arguments to run", "command", Core::ArgsParser::Required::Yes); | ||||
|     parser.set_stop_on_first_non_option(true); | ||||
| 
 | ||||
|     if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore)) | ||||
|         return 1; | ||||
| 
 | ||||
|     if (command_and_arguments.is_empty()) { | ||||
|         warnln("run_with_env: No command to run"); | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     AST::Command command; | ||||
|     TRY(command.argv.try_ensure_capacity(command_and_arguments.size())); | ||||
|     for (auto& arg : command_and_arguments) | ||||
|         command.argv.append(TRY(String::from_utf8(arg))); | ||||
| 
 | ||||
|     auto commands = TRY(expand_aliases({ move(command) })); | ||||
| 
 | ||||
|     HashMap<DeprecatedString, Optional<DeprecatedString>> old_environment_entries; | ||||
|     for (auto& variable : environment_variables) { | ||||
|         auto parts = variable.split_limit('=', 2, SplitBehavior::KeepEmpty); | ||||
|         if (parts.size() != 2) { | ||||
|             warnln("run_with_env: Invalid environment variable: '{}'", variable); | ||||
|             return 1; | ||||
|         } | ||||
| 
 | ||||
|         DeprecatedString name = parts[0]; | ||||
|         old_environment_entries.set(name, getenv(name.characters()) ?: Optional<DeprecatedString> {}); | ||||
| 
 | ||||
|         DeprecatedString value = parts[1]; | ||||
|         setenv(name.characters(), value.characters(), 1); | ||||
|     } | ||||
| 
 | ||||
|     int exit_code = 0; | ||||
|     for (auto& job : run_commands(commands)) { | ||||
|         block_on_job(job); | ||||
|         exit_code = job->exit_code(); | ||||
|     } | ||||
| 
 | ||||
|     for (auto& entry : old_environment_entries) { | ||||
|         if (entry.value.has_value()) | ||||
|             setenv(entry.key.characters(), entry.value->characters(), 1); | ||||
|         else | ||||
|             unsetenv(entry.key.characters()); | ||||
|     } | ||||
| 
 | ||||
|     return exit_code; | ||||
| } | ||||
| 
 | ||||
| bool Shell::has_builtin(StringView name) const | ||||
| { | ||||
|     if (name == ":"sv) | ||||
|  |  | |||
|  | @ -1849,22 +1849,33 @@ ErrorOr<RefPtr<AST::Node>> Parser::parse_simple_command() | |||
|     while (peek().type == Token::Type::AssignmentWord) { | ||||
|         definitions.append(peek().value); | ||||
| 
 | ||||
|         if (!nodes.is_empty()) { | ||||
|             nodes.append( | ||||
|                 make_ref_counted<AST::BarewordLiteral>( | ||||
|                     peek().position.value_or(empty_position()), | ||||
|                     consume().value)); | ||||
|         } else { | ||||
|             // env (assignments) (command)
 | ||||
|         if (nodes.is_empty()) { | ||||
|             // run_with_env -e*(assignments) -- (command)
 | ||||
|             nodes.append(make_ref_counted<AST::BarewordLiteral>( | ||||
|                 empty_position(), | ||||
|                 "env"_short_string)); | ||||
| 
 | ||||
|             nodes.append( | ||||
|                 make_ref_counted<AST::BarewordLiteral>( | ||||
|                     peek().position.value_or(empty_position()), | ||||
|                     consume().value)); | ||||
|                 TRY("run_with_env"_string))); | ||||
|         } | ||||
| 
 | ||||
|         auto position = peek().position.value_or(empty_position()); | ||||
|         nodes.append(make_ref_counted<AST::ImmediateExpression>( | ||||
|             position, | ||||
|             AST::NameWithPosition { | ||||
|                 TRY("reexpand"_string), | ||||
|                 position, | ||||
|             }, | ||||
|             Vector<NonnullRefPtr<AST::Node>> { | ||||
|                 make_ref_counted<AST::StringLiteral>( | ||||
|                     position, | ||||
|                     TRY(String::formatted("-e{}", consume().value)), | ||||
|                     AST::StringLiteral::EnclosureType::DoubleQuotes), | ||||
|             }, | ||||
|             Optional<AST::Position> {})); | ||||
|     } | ||||
| 
 | ||||
|     if (!definitions.is_empty()) { | ||||
|         nodes.append(make_ref_counted<AST::BarewordLiteral>( | ||||
|             empty_position(), | ||||
|             "--"_short_string)); | ||||
|     } | ||||
| 
 | ||||
|     // WORD or io_redirect: IO_NUMBER or io_file
 | ||||
|  | @ -1879,13 +1890,24 @@ ErrorOr<RefPtr<AST::Node>> Parser::parse_simple_command() | |||
|                 auto split_offset = equal_offset.value_or(definition.bytes().size()); | ||||
|                 auto name = make_ref_counted<AST::BarewordLiteral>( | ||||
|                     empty_position(), | ||||
|                     definition.substring_from_byte_offset_with_shared_superstring(0, split_offset).release_value_but_fixme_should_propagate_errors()); | ||||
|                     TRY(definition.substring_from_byte_offset_with_shared_superstring(0, split_offset))); | ||||
| 
 | ||||
|                 auto value = make_ref_counted<AST::BarewordLiteral>( | ||||
|                     empty_position(), | ||||
|                     definition.substring_from_byte_offset_with_shared_superstring(equal_offset.map([](auto x) { return x + 1; }).value_or(definition.bytes().size())).release_value_but_fixme_should_propagate_errors()); | ||||
|                 auto position = peek().position.value_or(empty_position()); | ||||
|                 auto expanded_value = make_ref_counted<AST::ImmediateExpression>( | ||||
|                     position, | ||||
|                     AST::NameWithPosition { | ||||
|                         TRY("reexpand"_string), | ||||
|                         position, | ||||
|                     }, | ||||
|                     Vector<NonnullRefPtr<AST::Node>> { | ||||
|                         make_ref_counted<AST::StringLiteral>( | ||||
|                             position, | ||||
|                             TRY(definition.substring_from_byte_offset_with_shared_superstring(split_offset + 1)), | ||||
|                             AST::StringLiteral::EnclosureType::DoubleQuotes), | ||||
|                     }, | ||||
|                     Optional<AST::Position> {}); | ||||
| 
 | ||||
|                 variables.append({ move(name), move(value) }); | ||||
|                 variables.append({ move(name), move(expanded_value) }); | ||||
|             } | ||||
| 
 | ||||
|             return make_ref_counted<AST::VariableDeclarations>(empty_position(), move(variables)); | ||||
|  |  | |||
|  | @ -23,40 +23,41 @@ | |||
| #include <LibMain/Main.h> | ||||
| #include <termios.h> | ||||
| 
 | ||||
| #define ENUMERATE_SHELL_BUILTINS()      \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(alias)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(where)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(cd)       \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(cdh)      \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(pwd)      \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(type)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(exec)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(exit)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(export)   \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(glob)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(unalias)  \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(unset)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(history)  \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(umask)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(not )     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(dirs)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(pushd)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(popd)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(setopt)   \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(shift)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(source)   \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(time)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(jobs)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(disown)   \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(fg)       \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(bg)       \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(wait)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(dump)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(kill)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(noop)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(break)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(continue) \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(read)     \ | ||||
| #define ENUMERATE_SHELL_BUILTINS()          \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(alias)        \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(where)        \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(cd)           \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(cdh)          \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(pwd)          \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(type)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(exec)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(exit)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(export)       \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(glob)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(unalias)      \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(unset)        \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(history)      \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(umask)        \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(not )         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(dirs)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(pushd)        \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(popd)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(setopt)       \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(shift)        \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(source)       \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(time)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(jobs)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(disown)       \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(fg)           \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(bg)           \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(wait)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(dump)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(kill)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(noop)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(break)        \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(continue)     \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(read)         \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(run_with_env) \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(argsparser_parse) | ||||
| 
 | ||||
| #define ENUMERATE_SHELL_OPTIONS()                                                                                    \ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ali Mohammad Pur
						Ali Mohammad Pur