Compare commits
	
		
			7 Commits
		
	
	
		
			264d1798dc
			...
			3e68981b0b
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 3e68981b0b | |
|  | 6fd76e8d1e | |
|  | dfcd15e7c4 | |
|  | 5fa359c28d | |
|  | 5bcfc9dd50 | |
|  | f67cad326a | |
|  | 9775e83374 | 
										
											Binary file not shown.
										
									
								
							|  | @ -11,6 +11,7 @@ | ||||||
| #include <pwd.h> | #include <pwd.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  | #include <sys/select.h> | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
| #include <sys/wait.h> | #include <sys/wait.h> | ||||||
| #include <termios.h> | #include <termios.h> | ||||||
|  | @ -35,6 +36,7 @@ struct SingleCommand | ||||||
| 
 | 
 | ||||||
| struct PipedCommand | struct PipedCommand | ||||||
| { | { | ||||||
|  | 	bool background; | ||||||
| 	BAN::Vector<SingleCommand> commands; | 	BAN::Vector<SingleCommand> commands; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -61,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) | ||||||
| { | { | ||||||
|  | @ -208,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) | ||||||
|  | @ -332,27 +334,19 @@ 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); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static PipedCommand parse_piped_command(BAN::StringView command_view) | static PipedCommand parse_piped_command(BAN::StringView command_view) | ||||||
| { | { | ||||||
|  | 	while (!command_view.empty() && isspace(command_view.back())) | ||||||
|  | 		command_view = command_view.substring(0, command_view.size() - 1); | ||||||
|  | 	const bool background = !command_view.empty() && command_view.back() == '&'; | ||||||
|  | 	if (background) | ||||||
|  | 		command_view = command_view.substring(0, command_view.size() - 1); | ||||||
|  | 
 | ||||||
| 	PipedCommand result; | 	PipedCommand result; | ||||||
|  | 	result.background = background; | ||||||
| 
 | 
 | ||||||
| 	for (size_t i = 0; i < command_view.size(); i++) | 	for (size_t i = 0; i < command_view.size(); i++) | ||||||
| 	{ | 	{ | ||||||
|  | @ -385,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++) | ||||||
|  | @ -447,7 +521,7 @@ static CommandList parse_command_list(BAN::StringView command_view) | ||||||
| 	return BAN::move(result); | 	return BAN::move(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int execute_command(const SingleCommand& command, int fd_in, int fd_out); | static int execute_command(const SingleCommand& command, int fd_in, int fd_out, bool background); | ||||||
| 
 | 
 | ||||||
| static int source_script(const BAN::String& path); | static int source_script(const BAN::String& path); | ||||||
| 
 | 
 | ||||||
|  | @ -533,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; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | @ -562,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)); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | @ -643,7 +703,7 @@ static void install_builtin_commands() | ||||||
| 			if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) | 			if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) | ||||||
| 				ERROR_RETURN("clock_gettime", 1); | 				ERROR_RETURN("clock_gettime", 1); | ||||||
| 
 | 
 | ||||||
| 			int ret = execute_command(timed_command, fd_in, fd_out); | 			int ret = execute_command(timed_command, fd_in, fd_out, false); | ||||||
| 
 | 
 | ||||||
| 			if (clock_gettime(CLOCK_MONOTONIC, &end) == -1) | 			if (clock_gettime(CLOCK_MONOTONIC, &end) == -1) | ||||||
| 				ERROR_RETURN("clock_gettime", 1); | 				ERROR_RETURN("clock_gettime", 1); | ||||||
|  | @ -660,24 +720,9 @@ static void install_builtin_commands() | ||||||
| 			return ret; | 			return ret; | ||||||
| 		} | 		} | ||||||
| 	)); | 	)); | ||||||
| 
 |  | ||||||
| 	MUST(s_builtin_commands.emplace("start-gui"_sv, |  | ||||||
| 		[](const SingleCommand&, FILE*, int, int) -> int |  | ||||||
| 		{ |  | ||||||
| 			const pid_t pid = fork(); |  | ||||||
| 			if (pid == -1) |  | ||||||
| 				return 1; |  | ||||||
| 			if (pid == 0) |  | ||||||
| 				execl("/bin/WindowServer", "WindowServer", NULL); |  | ||||||
| 			if (fork() == 0) |  | ||||||
| 				execl("/bin/Terminal", "Terminal", NULL); |  | ||||||
| 			waitpid(pid, nullptr, 0); |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 	)); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static pid_t execute_command_no_wait(const SingleCommand& command, int fd_in, int fd_out, pid_t pgrp) | static pid_t execute_command_no_wait(const SingleCommand& command, int fd_in, int fd_out, pid_t pgrp, bool background) | ||||||
| { | { | ||||||
| 	ASSERT(!command.arguments.empty()); | 	ASSERT(!command.arguments.empty()); | ||||||
| 
 | 
 | ||||||
|  | @ -760,7 +805,9 @@ static pid_t execute_command_no_wait(const SingleCommand& command, int fd_in, in | ||||||
| 	if (pid == -1) | 	if (pid == -1) | ||||||
| 		ERROR_RETURN("fork", -1); | 		ERROR_RETURN("fork", -1); | ||||||
| 
 | 
 | ||||||
| 	if (pgrp == 0 && isatty(0)) | 	if (background) | ||||||
|  | 		; | ||||||
|  | 	else if (pgrp == 0 && isatty(0)) | ||||||
| 	{ | 	{ | ||||||
| 		if(setpgid(pid, pid) == -1) | 		if(setpgid(pid, pid) == -1) | ||||||
| 			perror("setpgid"); | 			perror("setpgid"); | ||||||
|  | @ -775,11 +822,13 @@ static pid_t execute_command_no_wait(const SingleCommand& command, int fd_in, in | ||||||
| 	return pid; | 	return pid; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int execute_command(const SingleCommand& command, int fd_in, int fd_out) | static int execute_command(const SingleCommand& command, int fd_in, int fd_out, bool background) | ||||||
| { | { | ||||||
| 	const pid_t pid = execute_command_no_wait(command, fd_in, fd_out, 0); | 	const pid_t pid = execute_command_no_wait(command, fd_in, fd_out, 0, background); | ||||||
| 	if (pid == -1) | 	if (pid == -1) | ||||||
| 		return 1; | 		return 1; | ||||||
|  | 	if (background) | ||||||
|  | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	int status; | 	int status; | ||||||
| 	if (waitpid(pid, &status, 0) == -1) | 	if (waitpid(pid, &status, 0) == -1) | ||||||
|  | @ -804,7 +853,7 @@ static int execute_piped_commands(const PipedCommand& piped_command) | ||||||
| 		auto& command = piped_command.commands.front(); | 		auto& command = piped_command.commands.front(); | ||||||
| 		if (auto ret = execute_builtin(command, STDIN_FILENO, STDOUT_FILENO); ret.has_value()) | 		if (auto ret = execute_builtin(command, STDIN_FILENO, STDOUT_FILENO); ret.has_value()) | ||||||
| 			return ret.value(); | 			return ret.value(); | ||||||
| 		return execute_command(command, STDIN_FILENO, STDOUT_FILENO); | 		return execute_command(command, STDIN_FILENO, STDOUT_FILENO, piped_command.background); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	BAN::Vector<int> exit_codes(piped_command.commands.size(), 0); | 	BAN::Vector<int> exit_codes(piped_command.commands.size(), 0); | ||||||
|  | @ -830,7 +879,7 @@ static int execute_piped_commands(const PipedCommand& piped_command) | ||||||
| 			exit_codes[i] = builtin_ret.value(); | 			exit_codes[i] = builtin_ret.value(); | ||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
| 			pid_t pid = execute_command_no_wait(piped_command.commands[i], next_stdin, pipefd[1], pgrp); | 			const pid_t pid = execute_command_no_wait(piped_command.commands[i], next_stdin, pipefd[1], pgrp, piped_command.background); | ||||||
| 			processes[i] = pid; | 			processes[i] = pid; | ||||||
| 			if (pgrp == 0) | 			if (pgrp == 0) | ||||||
| 				pgrp = pid; | 				pgrp = pid; | ||||||
|  | @ -843,6 +892,9 @@ static int execute_piped_commands(const PipedCommand& piped_command) | ||||||
| 		next_stdin = pipefd[0]; | 		next_stdin = pipefd[0]; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (piped_command.background) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
| 	for (size_t i = 0; i < piped_command.commands.size(); i++) | 	for (size_t i = 0; i < piped_command.commands.size(); i++) | ||||||
| 	{ | 	{ | ||||||
| 		if (processes[i] == -1) | 		if (processes[i] == -1) | ||||||
|  | @ -882,8 +934,18 @@ static int parse_and_execute_command(BAN::StringView command) | ||||||
| 	tcsetattr(0, TCSANOW, &old_termios); | 	tcsetattr(0, TCSANOW, &old_termios); | ||||||
| 
 | 
 | ||||||
| 	last_return = 0; | 	last_return = 0; | ||||||
| 	for (const auto& [expression, condition] : command_list.commands) | 
 | ||||||
|  | 	for (size_t i = 0; i < command_list.commands.size(); i++) | ||||||
| 	{ | 	{ | ||||||
|  | 		const auto& [expression, condition] = command_list.commands[i]; | ||||||
|  | 
 | ||||||
|  | 		const auto parsed_command = parse_piped_command(expression); | ||||||
|  | 		if (parsed_command.background && i + 1 < command_list.commands.size() && command_list.commands[i + 1].condition != CommandList::Condition::Always) | ||||||
|  | 		{ | ||||||
|  | 			printf("invalid background command with conditional execution\n"); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		bool should_run = false; | 		bool should_run = false; | ||||||
| 		switch (condition) | 		switch (condition) | ||||||
| 		{ | 		{ | ||||||
|  | @ -901,7 +963,9 @@ static int parse_and_execute_command(BAN::StringView command) | ||||||
| 		if (!should_run) | 		if (!should_run) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		last_return = execute_piped_commands(parse_piped_command(expression)); | 		int return_value = execute_piped_commands(parsed_command); | ||||||
|  | 		if (!parsed_command.background) | ||||||
|  | 			last_return = return_value; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tcsetattr(0, TCSANOW, &new_termios); | 	tcsetattr(0, TCSANOW, &new_termios); | ||||||
|  | @ -1243,6 +1307,101 @@ static void print_prompt() | ||||||
| 	fflush(stdout); | 	fflush(stdout); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool detect_cursor_position_support() | ||||||
|  | { | ||||||
|  | 	constexpr auto getchar_nonblock = | ||||||
|  | 		[]() -> char | ||||||
|  | 		{ | ||||||
|  | 			fd_set fds; | ||||||
|  | 			FD_ZERO(&fds); | ||||||
|  | 			FD_SET(STDIN_FILENO, &fds); | ||||||
|  | 
 | ||||||
|  | 			timeval timeout; | ||||||
|  | 			timeout.tv_sec = 0; | ||||||
|  | 			timeout.tv_usec = 100'000; | ||||||
|  | 
 | ||||||
|  | 			int nselect = select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &timeout); | ||||||
|  | 			if (nselect != 1) | ||||||
|  | 				return '\0'; | ||||||
|  | 
 | ||||||
|  | 			char ch; | ||||||
|  | 			if (read(STDIN_FILENO, &ch, 1) != 1) | ||||||
|  | 				return '\0'; | ||||||
|  | 			return ch; | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 	if (write(STDOUT_FILENO, "\e[6n", 4) != 4) | ||||||
|  | 		return false; | ||||||
|  | 
 | ||||||
|  | 	char ch = getchar_nonblock(); | ||||||
|  | 	if (ch != '\e') | ||||||
|  | 	{ | ||||||
|  | 		if (ch != '\0') | ||||||
|  | 			ungetc(ch, stdin); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	if (getchar_nonblock() != '[') | ||||||
|  | 		return false; | ||||||
|  | 
 | ||||||
|  | 	int cur; | ||||||
|  | 	while (isdigit(cur = getchar_nonblock())) | ||||||
|  | 		; | ||||||
|  | 	if (cur != ';') | ||||||
|  | 		return false; | ||||||
|  | 	while (isdigit(cur = getchar_nonblock())) | ||||||
|  | 		; | ||||||
|  | 	if (cur != 'R') | ||||||
|  | 		return false; | ||||||
|  | 
 | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct CursorPosition | ||||||
|  | { | ||||||
|  | 	int x; | ||||||
|  | 	int y; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static BAN::Optional<CursorPosition> try_read_cursor_position() | ||||||
|  | { | ||||||
|  | #if __banan_os__ | ||||||
|  | 	return {}; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	static BAN::Optional<bool> s_supports_cursor_position; | ||||||
|  | 	if (!s_supports_cursor_position.has_value()) | ||||||
|  | 		s_supports_cursor_position = detect_cursor_position_support(); | ||||||
|  | 
 | ||||||
|  | 	if (!s_supports_cursor_position.value()) | ||||||
|  | 		return {}; | ||||||
|  | 
 | ||||||
|  | 	if (write(STDOUT_FILENO, "\e[6n", 4) != 4) | ||||||
|  | 		return {}; | ||||||
|  | 
 | ||||||
|  | 	char ch = getchar(); | ||||||
|  | 	if (ch != '\e') | ||||||
|  | 	{ | ||||||
|  | 		ungetc(ch, stdin); | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 	if (getchar() != '[') | ||||||
|  | 		return {}; | ||||||
|  | 
 | ||||||
|  | 	int cur, x = 0, y = 0; | ||||||
|  | 	while (isdigit(cur = getchar())) | ||||||
|  | 		y = (y * 10) + (cur - '0'); | ||||||
|  | 	if (cur != ';') | ||||||
|  | 		return {}; | ||||||
|  | 	while (isdigit(cur = getchar())) | ||||||
|  | 		x = (x * 10) + (cur - '0'); | ||||||
|  | 	if (cur != 'R') | ||||||
|  | 		return {}; | ||||||
|  | 
 | ||||||
|  | 	if (x > 0) x--; | ||||||
|  | 	if (y > 0) y--; | ||||||
|  | 	return CursorPosition { x, y }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int main(int argc, char** argv) | int main(int argc, char** argv) | ||||||
| { | { | ||||||
| 	realpath(argv[0], s_shell_path); | 	realpath(argv[0], s_shell_path); | ||||||
|  | @ -1466,6 +1625,10 @@ int main(int argc, char** argv) | ||||||
| 				MUST(history.push_back(buffers[index])); | 				MUST(history.push_back(buffers[index])); | ||||||
| 				buffers = history; | 				buffers = history; | ||||||
| 				MUST(buffers.emplace_back(""_sv)); | 				MUST(buffers.emplace_back(""_sv)); | ||||||
|  | 
 | ||||||
|  | 				auto cursor_pos = try_read_cursor_position(); | ||||||
|  | 				if (cursor_pos.has_value() && cursor_pos->x > 0) | ||||||
|  | 					printf("\e[7m%%\e[m\n"); | ||||||
| 			} | 			} | ||||||
| 			print_prompt(); | 			print_prompt(); | ||||||
| 			index = buffers.size() - 1; | 			index = buffers.size() - 1; | ||||||
|  | @ -1509,12 +1672,12 @@ int main(int argc, char** argv) | ||||||
| 				} | 				} | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
| 			for (size_t i = 0; i < completions.size() - 1; i++) | 			for (size_t i = 1; i < completions.size();) | ||||||
| 			{ | 			{ | ||||||
| 				if (completions[i] != completions[i + 1]) | 				if (completions[i - 1] == completions[i]) | ||||||
| 					continue; | 					completions.remove(i); | ||||||
| 				completions.remove(i + 1); | 				else | ||||||
| 				i--; | 					i++; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (completions.empty()) | 			if (completions.empty()) | ||||||
|  | @ -1543,9 +1706,28 @@ int main(int argc, char** argv) | ||||||
| 
 | 
 | ||||||
| 			if (all_match_len) | 			if (all_match_len) | ||||||
| 			{ | 			{ | ||||||
| 				col += all_match_len; | 				auto completion = completions.front().sv().substring(0, all_match_len); | ||||||
| 				MUST(buffers[index].append(completions.front().sv().substring(0, all_match_len))); | 
 | ||||||
| 				printf("%.*s", (int)all_match_len, completions.front().data()); | 				BAN::String temp_escaped; | ||||||
|  | 				if (should_escape_spaces) | ||||||
|  | 				{ | ||||||
|  | 					MUST(temp_escaped.append(completion)); | ||||||
|  | 					for (size_t i = 0; i < temp_escaped.size(); i++) | ||||||
|  | 					{ | ||||||
|  | 						if (!isspace(temp_escaped[i])) | ||||||
|  | 							continue; | ||||||
|  | 						MUST(temp_escaped.insert('\\', i)); | ||||||
|  | 						i++; | ||||||
|  | 					} | ||||||
|  | 					completion = temp_escaped.sv(); | ||||||
|  | 
 | ||||||
|  | 					if (!buffers[index].empty() && buffers[index].back() == '\\' && completion.front() == '\\') | ||||||
|  | 						completion = completion.substring(1); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				col += completion.size(); | ||||||
|  | 				MUST(buffers[index].append(completion)); | ||||||
|  | 				printf("%.*s", (int)completion.size(), completion.data()); | ||||||
| 				fflush(stdout); | 				fflush(stdout); | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -202,7 +202,8 @@ int list_directory(const BAN::String& path, config_t config) | ||||||
| 		{ | 		{ | ||||||
| 			if (i > 0) | 			if (i > 0) | ||||||
| 				printf(" "); | 				printf(" "); | ||||||
| 			printf("%s%s\e[m", entry_color(entries[i].st.st_mode), entries[i].name.data()); | 			const char* format = entries[i].name.sv().contains(' ') ? "'%s%s\e[m'" : "%s%s\e[m"; | ||||||
|  | 			printf(format, entry_color(entries[i].st.st_mode), entries[i].name.data()); | ||||||
| 		} | 		} | ||||||
| 		printf("\n"); | 		printf("\n"); | ||||||
| 		return ret; | 		return ret; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue