mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:52:45 +00:00 
			
		
		
		
	Shell: Add support for ARGV (and $*, $#)
This patchset also adds the 'shift' builtin, as well as the usual tests. closes #2948.
This commit is contained in:
		
							parent
							
								
									192b2383ac
								
							
						
					
					
						commit
						12af65c1c9
					
				
					 8 changed files with 79 additions and 1 deletions
				
			
		|  | @ -2004,6 +2004,7 @@ RefPtr<Value> SimpleVariableValue::resolve_without_cast(RefPtr<Shell> shell) | |||
| SpecialVariableValue::~SpecialVariableValue() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| Vector<String> SpecialVariableValue::resolve_as_list(RefPtr<Shell> shell) | ||||
| { | ||||
|     switch (m_name) { | ||||
|  | @ -2011,6 +2012,19 @@ Vector<String> SpecialVariableValue::resolve_as_list(RefPtr<Shell> shell) | |||
|         return { String::number(shell->last_return_code) }; | ||||
|     case '$': | ||||
|         return { String::number(getpid()) }; | ||||
|     case '*': | ||||
|         if (auto argv = shell->lookup_local_variable("ARGV")) | ||||
|             return argv->resolve_as_list(shell); | ||||
|         return {}; | ||||
|     case '#': | ||||
|         if (auto argv = shell->lookup_local_variable("ARGV")) { | ||||
|             if (argv->is_list()) { | ||||
|                 auto list_argv = static_cast<AST::ListValue*>(argv.ptr()); | ||||
|                 return { String::number(list_argv->values().size()) }; | ||||
|             } | ||||
|             return { "1" }; | ||||
|         } | ||||
|         return { "0" }; | ||||
|     default: | ||||
|         return { "" }; | ||||
|     } | ||||
|  |  | |||
|  | @ -233,6 +233,7 @@ public: | |||
|     } | ||||
| 
 | ||||
|     const Vector<RefPtr<Value>>& values() const { return m_contained_values; } | ||||
|     Vector<RefPtr<Value>>& values() { return m_contained_values; } | ||||
| 
 | ||||
| private: | ||||
|     Vector<RefPtr<Value>> m_contained_values; | ||||
|  |  | |||
|  | @ -678,6 +678,40 @@ int Shell::builtin_setopt(int argc, const char** argv) | |||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int Shell::builtin_shift(int argc, const char** argv) | ||||
| { | ||||
|     int count = 1; | ||||
| 
 | ||||
|     Core::ArgsParser parser; | ||||
|     parser.add_positional_argument(count, "Shift count", "count", Core::ArgsParser::Required::No); | ||||
| 
 | ||||
|     if (!parser.parse(argc, const_cast<char**>(argv), false)) | ||||
|         return 1; | ||||
| 
 | ||||
|     if (count < 1) | ||||
|         return 0; | ||||
| 
 | ||||
|     auto argv_ = lookup_local_variable("ARGV"); | ||||
|     if (!argv_) { | ||||
|         fprintf(stderr, "shift: ARGV is unset\n"); | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     if (!argv_->is_list()) | ||||
|         argv_ = *new AST::ListValue({ argv_ }); | ||||
| 
 | ||||
|     auto& values = static_cast<AST::ListValue*>(argv_.ptr())->values(); | ||||
|     if ((size_t)count > values.size()) { | ||||
|         fprintf(stderr, "shift: shift count must not be greater than %zu\n", values.size()); | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     for (auto i = 0; i < count; ++i) | ||||
|         values.take_first(); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int Shell::builtin_time(int argc, const char** argv) | ||||
| { | ||||
|     Vector<const char*> args; | ||||
|  |  | |||
|  | @ -772,6 +772,8 @@ RefPtr<AST::Node> Parser::parse_variable() | |||
|     switch (peek()) { | ||||
|     case '$': | ||||
|     case '?': | ||||
|     case '*': | ||||
|     case '#': | ||||
|         return create<AST::SpecialVariable>(consume()); // Variable Special
 | ||||
|     default: | ||||
|         break; | ||||
|  |  | |||
|  | @ -162,6 +162,8 @@ dquoted_string_inner :: '\' . dquoted_string_inner?       {concat} | |||
| variable :: '$' identifier | ||||
|           | '$' '$' | ||||
|           | '$' '?' | ||||
|           | '$' '*' | ||||
|           | '$' '#' | ||||
|           | ... | ||||
| 
 | ||||
| comment :: '#' [^\n]* | ||||
|  |  | |||
|  | @ -53,6 +53,7 @@ | |||
|     __ENUMERATE_SHELL_BUILTIN(pushd)   \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(popd)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(setopt)  \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(shift)   \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(time)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(jobs)    \ | ||||
|     __ENUMERATE_SHELL_BUILTIN(disown)  \ | ||||
|  |  | |||
							
								
								
									
										15
									
								
								Shell/Tests/special-vars.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Shell/Tests/special-vars.sh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| test "$*" = "" || echo "Fail: Argv list not empty" && exit 1 | ||||
| test "$#" -eq 0 || echo "Fail: Argv list empty but count non-zero" && exit 1 | ||||
| test "$ARGV" = "$*" || echo "Fail: \$ARGV not equal to \$*" && exit 1 | ||||
| 
 | ||||
| ARGV=(1 2 3) | ||||
| test "$#" -eq 3 || echo "Fail: Assignment to ARGV does not affect \$#" && exit 1 | ||||
| test "$*" = "1 2 3" || echo "Fail: Assignment to ARGV does not affect \$*" && exit 1 | ||||
| 
 | ||||
| shift | ||||
| test "$*" = "2 3" || echo "Fail: 'shift' does not work correctly" && exit 1 | ||||
| 
 | ||||
| shift 2 | ||||
| test "$*" = "" || echo "Fail: 'shift 2' does not work correctly" && exit 1 | ||||
|  | @ -159,12 +159,14 @@ int main(int argc, char** argv) | |||
| 
 | ||||
|     const char* command_to_run = nullptr; | ||||
|     const char* file_to_read_from = nullptr; | ||||
|     Vector<const char*> script_args; | ||||
|     bool skip_rc_files = false; | ||||
| 
 | ||||
|     Core::ArgsParser parser; | ||||
|     parser.add_option(command_to_run, "String to read commands from", "command-string", 'c', "command-string"); | ||||
|     parser.add_positional_argument(file_to_read_from, "File to read commands from", "file", Core::ArgsParser::Required::No); | ||||
|     parser.add_option(skip_rc_files, "Skip running shellrc files", "skip-shellrc", 0); | ||||
|     parser.add_positional_argument(file_to_read_from, "File to read commands from", "file", Core::ArgsParser::Required::No); | ||||
|     parser.add_positional_argument(script_args, "Extra argumets to pass to the script (via $* and co)", "argument", Core::ArgsParser::Required::No); | ||||
| 
 | ||||
|     parser.parse(argc, argv); | ||||
| 
 | ||||
|  | @ -181,6 +183,13 @@ int main(int argc, char** argv) | |||
|         run_rc_file(Shell::local_init_file_path); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         Vector<String> args; | ||||
|         for (auto* arg : script_args) | ||||
|             args.empend(arg); | ||||
|         shell->set_local_variable("ARGV", *new AST::ListValue(move(args))); | ||||
|     } | ||||
| 
 | ||||
|     if (command_to_run) { | ||||
|         dbgprintf("sh -c '%s'\n", command_to_run); | ||||
|         shell->run_command(command_to_run); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 AnotherTest
						AnotherTest