mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 05:32:44 +00:00 
			
		
		
		
	Shell: Parse and correctly evaluate 'command &' and 'command &&'
This commit adds the InBackground and ShortCircuitOnFailure attributes to commands, which respectively cause the command to be run in the background, and abort the command chain with exit status != 0.
This commit is contained in:
		
							parent
							
								
									143be7234f
								
							
						
					
					
						commit
						c23c354779
					
				
					 3 changed files with 70 additions and 5 deletions
				
			
		|  | @ -49,11 +49,11 @@ void Parser::commit_subcommand() | |||
|     m_subcommands.append({ move(m_tokens), move(m_redirections), {} }); | ||||
| } | ||||
| 
 | ||||
| void Parser::commit_command() | ||||
| void Parser::commit_command(Attributes attributes) | ||||
| { | ||||
|     if (m_subcommands.is_empty()) | ||||
|         return; | ||||
|     m_commands.append({ move(m_subcommands) }); | ||||
|     m_commands.append({ move(m_subcommands), attributes }); | ||||
| } | ||||
| 
 | ||||
| void Parser::do_pipe() | ||||
|  | @ -109,6 +109,31 @@ Vector<Command> Parser::parse() | |||
|                 commit_command(); | ||||
|                 break; | ||||
|             } | ||||
|             if (ch == '&') { | ||||
|                 commit_token(Token::Special); | ||||
| 
 | ||||
|                 if (i + 1 >= m_input.length()) { | ||||
|                 in_background:; | ||||
|                     // Nothing interesting past this token, commit with InBackground
 | ||||
|                     commit_subcommand(); | ||||
|                     commit_command(Attributes::InBackground); | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 ch = m_input.characters()[++i]; | ||||
|                 ++m_position; | ||||
| 
 | ||||
|                 if (ch == '&') { | ||||
|                     // This is '&&', commit with ShortCircuit
 | ||||
|                     commit_subcommand(); | ||||
|                     commit_command(Attributes::ShortCircuitOnFailure); | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 --i; | ||||
|                 --m_position; | ||||
|                 goto in_background; | ||||
|             } | ||||
|             if (ch == '|') { | ||||
|                 commit_token(Token::Special); | ||||
|                 if (m_tokens.is_empty()) { | ||||
|  |  | |||
|  | @ -45,6 +45,12 @@ struct Token { | |||
|     Type type; | ||||
| }; | ||||
| 
 | ||||
| enum Attributes { | ||||
|     None = 0x0, | ||||
|     ShortCircuitOnFailure = 0x1, | ||||
|     InBackground = 0x2, | ||||
| }; | ||||
| 
 | ||||
| struct Redirection { | ||||
|     enum Type { | ||||
|         Pipe, | ||||
|  | @ -71,6 +77,7 @@ struct Subcommand { | |||
| 
 | ||||
| struct Command { | ||||
|     Vector<Subcommand> subcommands; | ||||
|     Attributes attributes; | ||||
| }; | ||||
| 
 | ||||
| class Parser { | ||||
|  | @ -89,7 +96,7 @@ private: | |||
|     }; | ||||
|     void commit_token(Token::Type, AllowEmptyToken = AllowEmptyToken::No); | ||||
|     void commit_subcommand(); | ||||
|     void commit_command(); | ||||
|     void commit_command(Attributes = None); | ||||
|     void do_pipe(); | ||||
|     void begin_redirect_read(int fd); | ||||
|     void begin_redirect_write(int fd); | ||||
|  |  | |||
|  | @ -1052,7 +1052,6 @@ IterationDecision Shell::wait_for_pid(const Shell::SpawnedProcess& process, bool | |||
|         if (WEXITSTATUS(wstatus) != 0) | ||||
|             dbg() << "Shell: " << process.name << ":" << process.pid << " exited with status " << WEXITSTATUS(wstatus); | ||||
| 
 | ||||
|         if (is_first_command_in_chain) | ||||
|         return_value = WEXITSTATUS(wstatus); | ||||
| 
 | ||||
|         if (job) { | ||||
|  | @ -1128,6 +1127,7 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | |||
|                     break; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             dbgprintf("\n"); | ||||
|             for (auto& redirecton : command.subcommands[i].redirections) { | ||||
|                 for (size_t j = 0; j < i; ++j) | ||||
|  | @ -1151,6 +1151,13 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | |||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (auto attributes = command.attributes) { | ||||
|             dbgprintf("\n "); | ||||
|             if (attributes & Attributes::InBackground) | ||||
|                 dbgprintf("InBackground "); | ||||
|             if (attributes & Attributes::ShortCircuitOnFailure) | ||||
|                 dbgprintf("ShortCircuitOnFailure "); | ||||
|         } | ||||
|         dbgprintf("\n"); | ||||
|     } | ||||
| #endif | ||||
|  | @ -1159,8 +1166,20 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | |||
|     tcgetattr(0, &trm); | ||||
| 
 | ||||
|     int return_value = 0; | ||||
|     bool fail_short_circuits = false; | ||||
| 
 | ||||
|     for (auto& command : commands) { | ||||
|         if (fail_short_circuits) { | ||||
|             if (command.attributes & Attributes::ShortCircuitOnFailure) | ||||
|                 continue; | ||||
| 
 | ||||
|             // Do not fail any command after this one, as we've reached the end of a short-circuit chain,
 | ||||
|             // e.g. foo && bar && baz ; foobar
 | ||||
|             //                    ^ we reached this command.
 | ||||
|             fail_short_circuits = false; | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         if (command.subcommands.is_empty()) | ||||
|             continue; | ||||
| 
 | ||||
|  | @ -1306,6 +1325,14 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | |||
|             dbgprintf("  %d (%s)\n", child.pid, child.name.characters()); | ||||
| #endif | ||||
| 
 | ||||
|         if (command.attributes & Attributes::InBackground) { | ||||
|             // Set the jobs as running in background and continue without waiting.
 | ||||
|             for (auto& child : children) | ||||
|                 const_cast<Job*>(jobs.get(child.pid).value())->set_running_in_background(true); | ||||
| 
 | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         for (size_t i = 0; i < children.size(); ++i) { | ||||
|             auto& child = children[i]; | ||||
|             dbg() << "Now waiting for " << child.name << " (" << child.pid << ")"; | ||||
|  | @ -1314,6 +1341,12 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | |||
|                     break; | ||||
|             } while (errno == EINTR); | ||||
|         } | ||||
| 
 | ||||
|         if (command.attributes & Attributes::ShortCircuitOnFailure) { | ||||
|             if (return_value != 0) { | ||||
|                 fail_short_circuits = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     last_return_code = return_value; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 AnotherTest
						AnotherTest