Shell: Rewrite alias parsing to allow chained commands
You can now have "chained" commands in alias e.g. `alias foo='echo hello && ls`
This commit is contained in:
		
							parent
							
								
									dfcd15e7c4
								
							
						
					
					
						commit
						6fd76e8d1e
					
				| 
						 | 
					@ -63,7 +63,7 @@ struct BuiltinCommand
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
static BAN::HashMap<BAN::String, BuiltinCommand> s_builtin_commands;
 | 
					static BAN::HashMap<BAN::String, BuiltinCommand> s_builtin_commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static BAN::HashMap<BAN::String, BAN::Vector<BAN::String>> s_aliases;
 | 
					static BAN::HashMap<BAN::String, BAN::String> s_aliases;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static BAN::StringView strip_whitespace(BAN::StringView sv)
 | 
					static BAN::StringView strip_whitespace(BAN::StringView sv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -210,7 +210,7 @@ static BAN::Optional<BAN::String> parse_dollar(BAN::StringView command, size_t&
 | 
				
			||||||
	return temp;
 | 
						return temp;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static SingleCommand parse_single_command(BAN::StringView command_view, bool parse_aliases = true)
 | 
					static SingleCommand parse_single_command(BAN::StringView command_view)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	constexpr auto can_escape =
 | 
						constexpr auto can_escape =
 | 
				
			||||||
		[](char c)
 | 
							[](char c)
 | 
				
			||||||
| 
						 | 
					@ -334,21 +334,6 @@ static SingleCommand parse_single_command(BAN::StringView command_view, bool par
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	MUST(result.arguments.push_back(BAN::move(current_argument)));
 | 
						MUST(result.arguments.push_back(BAN::move(current_argument)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (parse_aliases)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		BAN::HashSet<BAN::String> matched_aliases;
 | 
					 | 
				
			||||||
		while (!result.arguments.empty() && !matched_aliases.contains(result.arguments.front()))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			auto it = s_aliases.find(result.arguments.front());
 | 
					 | 
				
			||||||
			if (it == s_aliases.end())
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			MUST(matched_aliases.insert(result.arguments.front()));
 | 
					 | 
				
			||||||
			result.arguments.remove(0);
 | 
					 | 
				
			||||||
			for (size_t i = 0; i < it->value.size(); i++)
 | 
					 | 
				
			||||||
				MUST(result.arguments.insert(i, it->value[i]));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return BAN::move(result);
 | 
						return BAN::move(result);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -394,8 +379,88 @@ static PipedCommand parse_piped_command(BAN::StringView command_view)
 | 
				
			||||||
	return BAN::move(result);
 | 
						return BAN::move(result);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static BAN::String parse_aliases(BAN::StringView command_view)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						while (!command_view.empty() && isspace(command_view.front()))
 | 
				
			||||||
 | 
							command_view = command_view.substring(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BAN::String result;
 | 
				
			||||||
 | 
						MUST(result.append(command_view));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BAN::HashSet<BAN::String> matched_aliases;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i = 0; i < result.size();)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							size_t command_len = 0;
 | 
				
			||||||
 | 
							for (; command_len < result.size() - i; command_len++)
 | 
				
			||||||
 | 
								if (isspace(result[i + command_len]))
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							auto command = result.sv().substring(i, command_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!matched_aliases.contains(command))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								auto it = s_aliases.find(command);
 | 
				
			||||||
 | 
								if (it != s_aliases.end())
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									MUST(matched_aliases.insert(command));
 | 
				
			||||||
 | 
									for (size_t j = 0; j < command_len; j++)
 | 
				
			||||||
 | 
										result.remove(i);
 | 
				
			||||||
 | 
									MUST(result.insert(it->value, i));
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							matched_aliases.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (; i < result.size(); i++)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								bool should_break = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const char current = result[i];
 | 
				
			||||||
 | 
								switch (current)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									case '\\':
 | 
				
			||||||
 | 
										i++;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case '\'':
 | 
				
			||||||
 | 
									case '"':
 | 
				
			||||||
 | 
										while (++i < result.size())
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											if (result[i] == current)
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
											if (result[i] == '\\')
 | 
				
			||||||
 | 
												i++;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case '|':
 | 
				
			||||||
 | 
									case '&':
 | 
				
			||||||
 | 
										if (i + 1 < result.size() && result[i + 1] == current)
 | 
				
			||||||
 | 
											i++;
 | 
				
			||||||
 | 
										else if (current == '&')
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
										// fall through
 | 
				
			||||||
 | 
									case ';':
 | 
				
			||||||
 | 
										i++;
 | 
				
			||||||
 | 
										should_break = true;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (should_break)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while (i < result.size() && isspace(result[i]))
 | 
				
			||||||
 | 
								i++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return BAN::move(result);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static CommandList parse_command_list(BAN::StringView command_view)
 | 
					static CommandList parse_command_list(BAN::StringView command_view)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						const auto command_with_aliases_parsed = parse_aliases(command_view);
 | 
				
			||||||
 | 
						command_view = command_with_aliases_parsed.sv();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CommandList result;
 | 
						CommandList result;
 | 
				
			||||||
	CommandList::Condition next_condition = CommandList::Condition::Always;
 | 
						CommandList::Condition next_condition = CommandList::Condition::Always;
 | 
				
			||||||
	for (size_t i = 0; i < command_view.size(); i++)
 | 
						for (size_t i = 0; i < command_view.size(); i++)
 | 
				
			||||||
| 
						 | 
					@ -542,23 +607,10 @@ static void install_builtin_commands()
 | 
				
			||||||
	MUST(s_builtin_commands.emplace("alias"_sv,
 | 
						MUST(s_builtin_commands.emplace("alias"_sv,
 | 
				
			||||||
		[](const SingleCommand& command, FILE* fout, int, int) -> int
 | 
							[](const SingleCommand& command, FILE* fout, int, int) -> int
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			const auto print_alias =
 | 
					 | 
				
			||||||
				[fout](const BAN::String& alias, const BAN::Vector<BAN::String>& value)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					fprintf(fout, "%s='", alias.data());
 | 
					 | 
				
			||||||
					for (size_t i = 0; i < value.size(); i++)
 | 
					 | 
				
			||||||
					{
 | 
					 | 
				
			||||||
						if (i != 0)
 | 
					 | 
				
			||||||
							fprintf(fout, " ");
 | 
					 | 
				
			||||||
						fprintf(fout, "%s", value[i].data());
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					fprintf(fout, "'\n");
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (command.arguments.size() == 1)
 | 
								if (command.arguments.size() == 1)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				for (const auto& [alias, value] : s_aliases)
 | 
									for (const auto& [alias, value] : s_aliases)
 | 
				
			||||||
					print_alias(alias, value);
 | 
										fprintf(fout, "%s='%s'\n", alias.data(), value.data());
 | 
				
			||||||
				return 0;
 | 
									return 0;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -571,17 +623,16 @@ static void install_builtin_commands()
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					auto it = s_aliases.find(command.arguments[i]);
 | 
										auto it = s_aliases.find(command.arguments[i]);
 | 
				
			||||||
					if (it != s_aliases.end())
 | 
										if (it != s_aliases.end())
 | 
				
			||||||
						print_alias(command.arguments[i], it->value);
 | 
											fprintf(fout, "%s='%s'\n", command.arguments[i].data(), it->value.data());
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				else
 | 
									else
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					auto alias = command.arguments[i].sv().substring(0, idx.value());
 | 
										auto alias = command.arguments[i].sv().substring(0, idx.value());
 | 
				
			||||||
					auto value = command.arguments[i].sv().substring(idx.value() + 1);
 | 
										auto value = command.arguments[i].sv().substring(idx.value() + 1);
 | 
				
			||||||
					auto parsed_alias = parse_single_command(value, false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if (s_aliases.contains(alias))
 | 
										if (s_aliases.contains(alias))
 | 
				
			||||||
						s_aliases.remove(alias);
 | 
											s_aliases.remove(alias);
 | 
				
			||||||
					MUST(s_aliases.insert(alias, BAN::move(parsed_alias.arguments)));
 | 
										MUST(s_aliases.insert(alias, value));
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue