mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-23 17:52:26 -05:00
Shell: Added pushd
, popd
and dirs
builtins
Added a few builtin functions to the shell to make navigating a bit easier in the terminal. `pushd` allows a user to "push" the current directory to the directory stack, and then `cd` to the new directory. `popd` allows the used to take the directory on the top of the stack off before `cd`'ing to it. `dirs` gives the state of the current directory stack. This is only a partial implementation of the `bash` version (gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html) , and doesn't include any of the +N or -N commands as of yet.
This commit is contained in:
parent
85d629103d
commit
ecdaf991c6
Notes:
sideshowbarker
2024-07-19 12:06:23 +09:00
Author: https://github.com/Quaker762 Commit: https://github.com/SerenityOS/serenity/commit/ecdaf991c62 Pull-request: https://github.com/SerenityOS/serenity/pull/563 Reviewed-by: https://github.com/awesomekling
2 changed files with 211 additions and 1 deletions
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <termios.h>
|
||||
|
||||
struct GlobalState {
|
||||
|
@ -15,6 +16,7 @@ struct GlobalState {
|
|||
bool was_interrupted { false };
|
||||
bool was_resized { false };
|
||||
int last_return_code { 0 };
|
||||
Vector<String> directory_stack;
|
||||
};
|
||||
|
||||
extern GlobalState g;
|
||||
|
|
210
Shell/main.cpp
210
Shell/main.cpp
|
@ -153,6 +153,200 @@ static int sh_umask(int argc, char** argv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sh_popd(int argc, char** argv)
|
||||
{
|
||||
if (g.directory_stack.size() <= 1) {
|
||||
fprintf(stderr, "Shell: popd: directory stack empty\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool should_switch = true;
|
||||
String path = g.directory_stack.take_last();
|
||||
|
||||
// When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory.
|
||||
if (argc == 1) {
|
||||
int rc = chdir(path.characters());
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "chdir(%s) failed: %s", path.characters(), strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
g.cwd = path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const char* arg = argv[i];
|
||||
if (!strcmp(arg, "-n")) {
|
||||
should_switch = false;
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemPath canonical_path(path.characters());
|
||||
if (!canonical_path.is_valid()) {
|
||||
fprintf(stderr, "FileSystemPath failed to canonicalize '%s'\n", path.characters());
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* real_path = canonical_path.string().characters();
|
||||
g.directory_stack.append(g.cwd.characters());
|
||||
|
||||
struct stat st;
|
||||
int rc = stat(real_path, &st);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "stat(%s) failed: %s\n", real_path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
fprintf(stderr, "Not a directory: %s\n", real_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (should_switch) {
|
||||
int rc = chdir(real_path);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "chdir(%s) failed: %s\n", real_path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
g.cwd = canonical_path.string();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_pushd(int argc, char** argv)
|
||||
{
|
||||
StringBuilder path_builder;
|
||||
bool should_switch = true;
|
||||
|
||||
// From the BASH reference manual: https://www.gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html
|
||||
// With no arguments, pushd exchanges the top two directories and makes the new top the current directory.
|
||||
if (argc == 1) {
|
||||
if (g.directory_stack.size() < 2) {
|
||||
fprintf(stderr, "pushd: no other directory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
String dir1 = g.directory_stack.take_first();
|
||||
String dir2 = g.directory_stack.take_first();
|
||||
g.directory_stack.insert(0, dir2);
|
||||
g.directory_stack.insert(1, dir1);
|
||||
|
||||
int rc = chdir(dir2.characters());
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "chdir(%s) failed: %s", dir2.characters(), strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
g.cwd = dir2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Let's assume the user's typed in 'pushd <dir>'
|
||||
if (argc == 2) {
|
||||
if (argv[1][0] == '/') {
|
||||
path_builder.append(argv[1]);
|
||||
}
|
||||
else
|
||||
path_builder.appendf("%s/%s", g.cwd.characters(), argv[1]);
|
||||
} else if (argc == 3) {
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const char* arg = argv[i];
|
||||
|
||||
if (arg[0] != '-') {
|
||||
if (arg[0] == '/') {
|
||||
path_builder.append(arg);
|
||||
}
|
||||
else
|
||||
path_builder.appendf("%s/%s", g.cwd.characters(), arg);
|
||||
}
|
||||
|
||||
if (!strcmp(arg, "-n"))
|
||||
should_switch = false;
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemPath canonical_path(path_builder.to_string());
|
||||
if (!canonical_path.is_valid()) {
|
||||
fprintf(stderr, "FileSystemPath failed to canonicalize '%s'\n", path_builder.to_string().characters());
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* real_path = canonical_path.string().characters();
|
||||
g.directory_stack.append(real_path);
|
||||
|
||||
struct stat st;
|
||||
int rc = stat(real_path, &st);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "stat(%s) failed: %s\n", real_path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
fprintf(stderr, "Not a directory: %s\n", real_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (should_switch) {
|
||||
int rc = chdir(real_path);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "chdir(%s) failed: %s\n", real_path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
g.cwd = canonical_path.string();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_dirs(int argc, char** argv)
|
||||
{
|
||||
// The first directory in the stack is ALWAYS the current directory
|
||||
g.directory_stack.at(0) = g.cwd.characters();
|
||||
|
||||
if (argc == 1) {
|
||||
for (String dir : g.directory_stack)
|
||||
printf("%s ", dir.characters());
|
||||
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool printed = false;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const char* arg = argv[i];
|
||||
if (!strcmp(arg, "-c")) {
|
||||
for (int i = 1; i < g.directory_stack.size(); i++)
|
||||
g.directory_stack.remove(i);
|
||||
|
||||
printed = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-p") && !printed) {
|
||||
for (auto& directory : g.directory_stack)
|
||||
printf("%s\n", directory.characters());
|
||||
|
||||
printed = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-v") && !printed) {
|
||||
int idx = 0;
|
||||
for (auto& directory : g.directory_stack) {
|
||||
printf("%d %s\n", idx++, directory.characters());
|
||||
}
|
||||
|
||||
printed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool handle_builtin(int argc, char** argv, int& retval)
|
||||
{
|
||||
if (argc == 0)
|
||||
|
@ -185,6 +379,18 @@ static bool handle_builtin(int argc, char** argv, int& retval)
|
|||
retval = sh_umask(argc, argv);
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(argv[0], "dirs")) {
|
||||
retval = sh_dirs(argc, argv);
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(argv[0], "pushd")) {
|
||||
retval = sh_pushd(argc, argv);
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(argv[0], "popd")) {
|
||||
retval = sh_popd(argc, argv);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -228,7 +434,7 @@ static bool is_glob(const StringView& s)
|
|||
return false;
|
||||
}
|
||||
|
||||
static Vector<StringView> split_path(const StringView &path)
|
||||
static Vector<StringView> split_path(const StringView& path)
|
||||
{
|
||||
Vector<StringView> parts;
|
||||
|
||||
|
@ -667,6 +873,8 @@ int main(int argc, char** argv)
|
|||
free(cwd);
|
||||
}
|
||||
|
||||
g.directory_stack.append(g.cwd);
|
||||
|
||||
load_history();
|
||||
atexit(save_history);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue