Compare commits
7 Commits
264d1798dc
...
3e68981b0b
Author | SHA1 | Date |
---|---|---|
Bananymous | 3e68981b0b | |
Bananymous | 6fd76e8d1e | |
Bananymous | dfcd15e7c4 | |
Bananymous | 5fa359c28d | |
Bananymous | 5bcfc9dd50 | |
Bananymous | f67cad326a | |
Bananymous | 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