mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 21:33:09 +00:00
parent
6809a8dfbc
commit
77fe9933e2
15 changed files with 141 additions and 117 deletions
|
@ -106,7 +106,7 @@ set(FISH_BUILTIN_SRCS
|
|||
src/builtins/eval.cpp src/builtins/fg.cpp
|
||||
src/builtins/function.cpp src/builtins/functions.cpp src/builtins/history.cpp
|
||||
src/builtins/jobs.cpp src/builtins/math.cpp src/builtins/printf.cpp src/builtins/path.cpp
|
||||
src/builtins/pwd.cpp src/builtins/read.cpp
|
||||
src/builtins/read.cpp
|
||||
src/builtins/realpath.cpp src/builtins/set.cpp
|
||||
src/builtins/set_color.cpp src/builtins/source.cpp src/builtins/status.cpp
|
||||
src/builtins/string.cpp src/builtins/test.cpp src/builtins/type.cpp src/builtins/ulimit.cpp
|
||||
|
|
1
fish-rust/Cargo.lock
generated
1
fish-rust/Cargo.lock
generated
|
@ -348,6 +348,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"autocxx",
|
||||
"autocxx-build",
|
||||
"bitflags",
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
"cxx-gen",
|
||||
|
|
|
@ -9,6 +9,7 @@ rust-version = "1.67"
|
|||
widestring-suffix = { path = "./widestring-suffix/" }
|
||||
|
||||
autocxx = "0.23.1"
|
||||
bitflags = "1.3.2"
|
||||
cxx = "1.0"
|
||||
errno = "0.2.8"
|
||||
inventory = { version = "0.3.3", optional = true}
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::builtins::shared::{
|
|||
STATUS_CMD_OK, STATUS_INVALID_ARGS,
|
||||
};
|
||||
use crate::common::{escape_string, valid_func_name, EscapeStringStyle};
|
||||
use crate::env::flags::ENV_UNIVERSAL;
|
||||
use crate::env::flags::EnvMode;
|
||||
use crate::env::status::{ENV_NOT_FOUND, ENV_OK};
|
||||
use crate::ffi::{self, parser_t};
|
||||
use crate::re::regex_make_anchored;
|
||||
|
@ -417,7 +417,7 @@ fn abbr_erase(opts: &Options, parser: &mut parser_t) -> Option<c_int> {
|
|||
let esc_src = escape_string(arg, EscapeStringStyle::Script(Default::default()));
|
||||
if !esc_src.is_empty() {
|
||||
let var_name = WString::from_str("_fish_abbr_") + esc_src.as_utfstr();
|
||||
let ret = parser.remove_var(&var_name, ENV_UNIVERSAL);
|
||||
let ret = parser.remove_var(&var_name, EnvMode::UNIVERSAL.into());
|
||||
|
||||
if ret == autocxx::c_int(ENV_OK) {
|
||||
result = STATUS_CMD_OK
|
||||
|
|
|
@ -7,6 +7,7 @@ pub mod contains;
|
|||
pub mod echo;
|
||||
pub mod emit;
|
||||
pub mod exit;
|
||||
pub mod pwd;
|
||||
pub mod random;
|
||||
pub mod r#return;
|
||||
pub mod wait;
|
||||
|
|
80
fish-rust/src/builtins/pwd.rs
Normal file
80
fish-rust/src/builtins/pwd.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
//! Implementation of the pwd builtin.
|
||||
use errno::errno;
|
||||
use libc::c_int;
|
||||
|
||||
use crate::{
|
||||
builtins::shared::{io_streams_t, BUILTIN_ERR_ARG_COUNT1},
|
||||
env::flags::EnvMode,
|
||||
ffi::parser_t,
|
||||
wchar::{wstr, WString, L},
|
||||
wchar_ffi::{WCharFromFFI, WCharToFFI},
|
||||
wgetopt::{wgetopter_t, wopt, woption, woption_argument_t::no_argument},
|
||||
wutil::{wgettext_fmt, wrealpath},
|
||||
};
|
||||
|
||||
use super::shared::{
|
||||
builtin_print_help, builtin_unknown_option, STATUS_CMD_ERROR, STATUS_CMD_OK,
|
||||
STATUS_INVALID_ARGS,
|
||||
};
|
||||
|
||||
// The pwd builtin. Respect -P to resolve symbolic links. Respect -L to not do that (the default).
|
||||
const short_options: &wstr = L!("LPh");
|
||||
const long_options: &[woption] = &[
|
||||
wopt(L!("help"), no_argument, 'h'),
|
||||
wopt(L!("logical"), no_argument, 'L'),
|
||||
wopt(L!("physical"), no_argument, 'P'),
|
||||
];
|
||||
|
||||
pub fn pwd(parser: &mut parser_t, streams: &mut io_streams_t, argv: &mut [&wstr]) -> Option<c_int> {
|
||||
let cmd = argv[0];
|
||||
let argc = argv.len();
|
||||
let mut resolve_symlinks = false;
|
||||
let mut w = wgetopter_t::new(short_options, long_options, argv);
|
||||
while let Some(opt) = w.wgetopt_long() {
|
||||
match opt {
|
||||
'L' => resolve_symlinks = false,
|
||||
'P' => resolve_symlinks = true,
|
||||
'h' => {
|
||||
builtin_print_help(parser, streams, cmd);
|
||||
return STATUS_CMD_OK;
|
||||
}
|
||||
'?' => {
|
||||
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1], false);
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
_ => panic!("unexpected retval from wgetopt_long"),
|
||||
}
|
||||
}
|
||||
|
||||
if w.woptind != argc {
|
||||
streams
|
||||
.err
|
||||
.append(wgettext_fmt!(BUILTIN_ERR_ARG_COUNT1, cmd, 0, argc - 1));
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
|
||||
let mut pwd = WString::new();
|
||||
let tmp = parser
|
||||
.vars1()
|
||||
.get_or_null(&L!("PWD").to_ffi(), EnvMode::DEFAULT.bits());
|
||||
if !tmp.is_null() {
|
||||
pwd = tmp.as_string().from_ffi();
|
||||
}
|
||||
if resolve_symlinks {
|
||||
if let Some(real_pwd) = wrealpath(&pwd) {
|
||||
pwd = real_pwd;
|
||||
} else {
|
||||
streams.err.append(wgettext_fmt!(
|
||||
"%ls: realpath failed: %s\n",
|
||||
cmd,
|
||||
errno().to_string()
|
||||
));
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
}
|
||||
if pwd.is_empty() {
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
streams.out.append(pwd + L!("\n"));
|
||||
return STATUS_CMD_OK;
|
||||
}
|
|
@ -37,6 +37,8 @@ pub const BUILTIN_ERR_TOO_MANY_ARGUMENTS: &str = "%ls: too many arguments\n";
|
|||
/// Error message when integer expected
|
||||
pub const BUILTIN_ERR_NOT_NUMBER: &str = "%ls: %ls: invalid integer\n";
|
||||
|
||||
pub const BUILTIN_ERR_ARG_COUNT1: &str = "%ls: expected %d arguments; got %d\n";
|
||||
|
||||
/// A handy return value for successful builtins.
|
||||
pub const STATUS_CMD_OK: Option<c_int> = Some(0);
|
||||
|
||||
|
@ -125,6 +127,7 @@ pub fn run_builtin(
|
|||
RustBuiltin::Echo => super::echo::echo(parser, streams, args),
|
||||
RustBuiltin::Emit => super::emit::emit(parser, streams, args),
|
||||
RustBuiltin::Exit => super::exit::exit(parser, streams, args),
|
||||
RustBuiltin::Pwd => super::pwd::pwd(parser, streams, args),
|
||||
RustBuiltin::Random => super::random::random(parser, streams, args),
|
||||
RustBuiltin::Return => super::r#return::r#return(parser, streams, args),
|
||||
RustBuiltin::Wait => wait::wait(parser, streams, args),
|
||||
|
|
|
@ -1,30 +1,43 @@
|
|||
/// Flags that may be passed as the 'mode' in env_stack_t::set() / environment_t::get().
|
||||
pub mod flags {
|
||||
use autocxx::c_int;
|
||||
use bitflags::bitflags;
|
||||
|
||||
/// Default mode. Used with `env_stack_t::get()` to indicate the caller doesn't care what scope
|
||||
/// the var is in or whether it is exported or unexported.
|
||||
pub const ENV_DEFAULT: c_int = c_int(0);
|
||||
/// Flag for local (to the current block) variable.
|
||||
pub const ENV_LOCAL: c_int = c_int(1 << 0);
|
||||
pub const ENV_FUNCTION: c_int = c_int(1 << 1);
|
||||
/// Flag for global variable.
|
||||
pub const ENV_GLOBAL: c_int = c_int(1 << 2);
|
||||
/// Flag for universal variable.
|
||||
pub const ENV_UNIVERSAL: c_int = c_int(1 << 3);
|
||||
/// Flag for exported (to commands) variable.
|
||||
pub const ENV_EXPORT: c_int = c_int(1 << 4);
|
||||
/// Flag for unexported variable.
|
||||
pub const ENV_UNEXPORT: c_int = c_int(1 << 5);
|
||||
/// Flag to mark a variable as a path variable.
|
||||
pub const ENV_PATHVAR: c_int = c_int(1 << 6);
|
||||
/// Flag to unmark a variable as a path variable.
|
||||
pub const ENV_UNPATHVAR: c_int = c_int(1 << 7);
|
||||
/// Flag for variable update request from the user. All variable changes that are made directly
|
||||
/// by the user, such as those from the `read` and `set` builtin must have this flag set. It
|
||||
/// serves one purpose: to indicate that an error should be returned if the user is attempting
|
||||
/// to modify a var that should not be modified by direct user action; e.g., a read-only var.
|
||||
pub const ENV_USER: c_int = c_int(1 << 8);
|
||||
bitflags! {
|
||||
/// Flags that may be passed as the 'mode' in env_stack_t::set() / environment_t::get().
|
||||
#[repr(C)]
|
||||
pub struct EnvMode: u16 {
|
||||
/// Default mode. Used with `env_stack_t::get()` to indicate the caller doesn't care what scope
|
||||
/// the var is in or whether it is exported or unexported.
|
||||
const DEFAULT = 0;
|
||||
/// Flag for local (to the current block) variable.
|
||||
const LOCAL = 1 << 0;
|
||||
const FUNCTION = 1 << 1;
|
||||
/// Flag for global variable.
|
||||
const GLOBAL = 1 << 2;
|
||||
/// Flag for universal variable.
|
||||
const UNIVERSAL = 1 << 3;
|
||||
/// Flag for exported (to commands) variable.
|
||||
const EXPORT = 1 << 4;
|
||||
/// Flag for unexported variable.
|
||||
const UNEXPORT = 1 << 5;
|
||||
/// Flag to mark a variable as a path variable.
|
||||
const PATHVAR = 1 << 6;
|
||||
/// Flag to unmark a variable as a path variable.
|
||||
const UNPATHVAR = 1 << 7;
|
||||
/// Flag for variable update request from the user. All variable changes that are made directly
|
||||
/// by the user, such as those from the `read` and `set` builtin must have this flag set. It
|
||||
/// serves one purpose: to indicate that an error should be returned if the user is attempting
|
||||
/// to modify a var that should not be modified by direct user action; e.g., a read-only var.
|
||||
const USER = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EnvMode> for c_int {
|
||||
fn from(val: EnvMode) -> Self {
|
||||
c_int(i32::from(val.bits()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return values for `env_stack_t::set()`.
|
||||
|
|
|
@ -103,6 +103,7 @@ include_cpp! {
|
|||
generate!("io_chain_t")
|
||||
|
||||
generate!("termsize_container_t")
|
||||
generate!("env_var_t")
|
||||
}
|
||||
|
||||
impl parser_t {
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
#include "builtins/math.h"
|
||||
#include "builtins/path.h"
|
||||
#include "builtins/printf.h"
|
||||
#include "builtins/pwd.h"
|
||||
#include "builtins/read.h"
|
||||
#include "builtins/realpath.h"
|
||||
#include "builtins/set.h"
|
||||
|
@ -395,7 +394,7 @@ static constexpr builtin_data_t builtin_datas[] = {
|
|||
{L"or", &builtin_generic, N_(L"Execute command if previous command failed")},
|
||||
{L"path", &builtin_path, N_(L"Handle paths")},
|
||||
{L"printf", &builtin_printf, N_(L"Prints formatted text")},
|
||||
{L"pwd", &builtin_pwd, N_(L"Print the working directory")},
|
||||
{L"pwd", &implemented_in_rust, N_(L"Print the working directory")},
|
||||
{L"random", &implemented_in_rust, N_(L"Generate random number")},
|
||||
{L"read", &builtin_read, N_(L"Read a line of input into variables")},
|
||||
{L"realpath", &builtin_realpath, N_(L"Show absolute path sans symlinks")},
|
||||
|
@ -541,6 +540,9 @@ static maybe_t<RustBuiltin> try_get_rust_builtin(const wcstring &cmd) {
|
|||
if (cmd == L"exit") {
|
||||
return RustBuiltin::Exit;
|
||||
}
|
||||
if (cmd == L"pwd") {
|
||||
return RustBuiltin::Pwd;
|
||||
}
|
||||
if (cmd == L"random") {
|
||||
return RustBuiltin::Random;
|
||||
}
|
||||
|
|
|
@ -116,6 +116,7 @@ enum RustBuiltin : int32_t {
|
|||
Echo,
|
||||
Emit,
|
||||
Exit,
|
||||
Pwd,
|
||||
Random,
|
||||
Return,
|
||||
Wait,
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
// Implementation of the pwd builtin.
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "pwd.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "../builtin.h"
|
||||
#include "../common.h"
|
||||
#include "../env.h"
|
||||
#include "../fallback.h" // IWYU pragma: keep
|
||||
#include "../io.h"
|
||||
#include "../maybe.h"
|
||||
#include "../parser.h"
|
||||
#include "../wgetopt.h"
|
||||
#include "../wutil.h" // IWYU pragma: keep
|
||||
|
||||
/// The pwd builtin. Respect -P to resolve symbolic links. Respect -L to not do that (the default).
|
||||
static const wchar_t *const short_options = L"LPh";
|
||||
static const struct woption long_options[] = {{L"help", no_argument, 'h'},
|
||||
{L"logical", no_argument, 'L'},
|
||||
{L"physical", no_argument, 'P'},
|
||||
{}};
|
||||
maybe_t<int> builtin_pwd(parser_t &parser, io_streams_t &streams, const wchar_t **argv) {
|
||||
UNUSED(parser);
|
||||
const wchar_t *cmd = argv[0];
|
||||
int argc = builtin_count_args(argv);
|
||||
bool resolve_symlinks = false;
|
||||
wgetopter_t w;
|
||||
int opt;
|
||||
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, nullptr)) != -1) {
|
||||
switch (opt) {
|
||||
case 'L':
|
||||
resolve_symlinks = false;
|
||||
break;
|
||||
case 'P':
|
||||
resolve_symlinks = true;
|
||||
break;
|
||||
case 'h':
|
||||
builtin_print_help(parser, streams, cmd);
|
||||
return STATUS_CMD_OK;
|
||||
case '?': {
|
||||
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected retval from wgetopt_long");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (w.woptind != argc) {
|
||||
streams.err.append_format(BUILTIN_ERR_ARG_COUNT1, cmd, 0, argc - 1);
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
|
||||
wcstring pwd;
|
||||
if (auto tmp = parser.vars().get(L"PWD")) {
|
||||
pwd = tmp->as_string();
|
||||
}
|
||||
if (resolve_symlinks) {
|
||||
if (auto real_pwd = wrealpath(pwd)) {
|
||||
pwd = std::move(*real_pwd);
|
||||
} else {
|
||||
const char *error = std::strerror(errno);
|
||||
streams.err.append_format(L"%ls: realpath failed: %s\n", cmd, error);
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
}
|
||||
if (pwd.empty()) {
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
streams.out.append(pwd + L"\n");
|
||||
return STATUS_CMD_OK;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Prototypes for executing builtin_pwd function.
|
||||
#ifndef FISH_BUILTIN_PWD_H
|
||||
#define FISH_BUILTIN_PWD_H
|
||||
|
||||
#include "../maybe.h"
|
||||
|
||||
class parser_t;
|
||||
struct io_streams_t;
|
||||
|
||||
maybe_t<int> builtin_pwd(parser_t &parser, io_streams_t &streams, const wchar_t **argv);
|
||||
#endif
|
|
@ -1476,6 +1476,13 @@ const std::shared_ptr<env_stack_t> &env_stack_t::principal_ref() {
|
|||
new env_stack_t(env_stack_impl_t::create())};
|
||||
return s_principal;
|
||||
}
|
||||
__attribute__((unused)) std::unique_ptr<env_var_t> env_stack_t::get_or_null(
|
||||
wcstring const &key, env_mode_flags_t mode) const {
|
||||
auto variable = get(key, mode);
|
||||
return variable.missing_or_empty()
|
||||
? std::unique_ptr<env_var_t>()
|
||||
: std::unique_ptr<env_var_t>(new env_var_t(variable.value()));
|
||||
}
|
||||
|
||||
env_stack_t::~env_stack_t() = default;
|
||||
|
||||
|
|
|
@ -290,6 +290,9 @@ class env_stack_t final : public environment_t {
|
|||
/// \return a list of events for changed variables.
|
||||
std::vector<rust::Box<Event>> universal_sync(bool always);
|
||||
|
||||
__attribute__((unused)) std::unique_ptr<env_var_t> get_or_null(
|
||||
const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const;
|
||||
|
||||
// Compatibility hack; access the "environment stack" from back when there was just one.
|
||||
static const std::shared_ptr<env_stack_t> &principal_ref();
|
||||
static env_stack_t &principal() { return *principal_ref(); }
|
||||
|
|
Loading…
Reference in a new issue