2023-04-02 01:17:49 +08:00
|
|
|
use crate::builtins::{printf, wait};
|
2023-01-16 11:52:08 +08:00
|
|
|
use crate::ffi::{self, parser_t, wcharz_t, Repin, RustBuiltin};
|
2023-02-11 01:22:56 +08:00
|
|
|
use crate::wchar::{self, wstr, L};
|
2023-01-16 11:52:08 +08:00
|
|
|
use crate::wchar_ffi::{c_str, empty_wstring};
|
2023-02-11 01:22:56 +08:00
|
|
|
use crate::wgetopt::{wgetopter_t, wopt, woption, woption_argument_t};
|
2023-01-16 11:52:08 +08:00
|
|
|
use libc::c_int;
|
|
|
|
use std::pin::Pin;
|
|
|
|
|
|
|
|
#[cxx::bridge]
|
|
|
|
mod builtins_ffi {
|
|
|
|
extern "C++" {
|
|
|
|
include!("wutil.h");
|
|
|
|
include!("parser.h");
|
|
|
|
include!("builtin.h");
|
|
|
|
|
|
|
|
type wcharz_t = crate::ffi::wcharz_t;
|
|
|
|
type parser_t = crate::ffi::parser_t;
|
|
|
|
type io_streams_t = crate::ffi::io_streams_t;
|
|
|
|
type RustBuiltin = crate::ffi::RustBuiltin;
|
|
|
|
}
|
|
|
|
extern "Rust" {
|
|
|
|
fn rust_run_builtin(
|
|
|
|
parser: Pin<&mut parser_t>,
|
|
|
|
streams: Pin<&mut io_streams_t>,
|
|
|
|
cpp_args: &Vec<wcharz_t>,
|
|
|
|
builtin: RustBuiltin,
|
2023-02-06 06:52:58 +08:00
|
|
|
status_code: &mut i32,
|
|
|
|
) -> bool;
|
2023-01-16 11:52:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Vec<wcharz_t> {}
|
|
|
|
}
|
|
|
|
|
2023-02-19 00:13:58 +08:00
|
|
|
/// Error message when too many arguments are supplied to a builtin.
|
|
|
|
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";
|
|
|
|
|
2023-03-01 13:05:27 +08:00
|
|
|
pub const BUILTIN_ERR_ARG_COUNT1: &str = "%ls: expected %d arguments; got %d\n";
|
|
|
|
|
2023-01-16 11:52:08 +08:00
|
|
|
/// A handy return value for successful builtins.
|
|
|
|
pub const STATUS_CMD_OK: Option<c_int> = Some(0);
|
|
|
|
|
2023-02-21 01:57:02 +08:00
|
|
|
/// The status code used for failure exit in a command (but not if the args were invalid).
|
|
|
|
pub const STATUS_CMD_ERROR: Option<c_int> = Some(1);
|
|
|
|
|
2023-04-02 01:17:49 +08:00
|
|
|
/// The status code used for invalid arguments given to a command. This is distinct from valid
|
|
|
|
/// arguments that might result in a command failure. An invalid args condition is something
|
|
|
|
/// like an unrecognized flag, missing or too many arguments, an invalid integer, etc.
|
2023-01-16 11:52:08 +08:00
|
|
|
pub const STATUS_INVALID_ARGS: Option<c_int> = Some(2);
|
|
|
|
|
|
|
|
/// A wrapper around output_stream_t.
|
|
|
|
pub struct output_stream_t(*mut ffi::output_stream_t);
|
|
|
|
|
|
|
|
impl output_stream_t {
|
|
|
|
/// \return the underlying output_stream_t.
|
|
|
|
fn ffi(&mut self) -> Pin<&mut ffi::output_stream_t> {
|
|
|
|
unsafe { (*self.0).pin() }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Append a &wtr or WString.
|
|
|
|
pub fn append<Str: AsRef<wstr>>(&mut self, s: Str) -> bool {
|
|
|
|
self.ffi().append1(c_str!(s))
|
|
|
|
}
|
2023-04-02 01:17:49 +08:00
|
|
|
|
|
|
|
/// Append a char.
|
|
|
|
pub fn append1(&mut self, c: char) -> bool {
|
|
|
|
self.append(wstr::from_char_slice(&[c]))
|
|
|
|
}
|
2023-01-16 11:52:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Convenience wrappers around C++ io_streams_t.
|
|
|
|
pub struct io_streams_t {
|
|
|
|
streams: *mut builtins_ffi::io_streams_t,
|
|
|
|
pub out: output_stream_t,
|
|
|
|
pub err: output_stream_t,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl io_streams_t {
|
2023-02-13 00:23:46 +08:00
|
|
|
pub fn new(mut streams: Pin<&mut builtins_ffi::io_streams_t>) -> io_streams_t {
|
2023-01-16 11:52:08 +08:00
|
|
|
let out = output_stream_t(streams.as_mut().get_out().unpin());
|
|
|
|
let err = output_stream_t(streams.as_mut().get_err().unpin());
|
|
|
|
let streams = streams.unpin();
|
|
|
|
io_streams_t { streams, out, err }
|
|
|
|
}
|
|
|
|
|
2023-02-13 00:23:46 +08:00
|
|
|
pub fn ffi_pin(&mut self) -> Pin<&mut builtins_ffi::io_streams_t> {
|
2023-01-16 11:52:08 +08:00
|
|
|
unsafe { Pin::new_unchecked(&mut *self.streams) }
|
|
|
|
}
|
|
|
|
|
2023-02-13 00:23:46 +08:00
|
|
|
pub fn ffi_ref(&self) -> &builtins_ffi::io_streams_t {
|
2023-01-16 11:52:08 +08:00
|
|
|
unsafe { &*self.streams }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rust_run_builtin(
|
|
|
|
parser: Pin<&mut parser_t>,
|
|
|
|
streams: Pin<&mut builtins_ffi::io_streams_t>,
|
|
|
|
cpp_args: &Vec<wcharz_t>,
|
|
|
|
builtin: RustBuiltin,
|
2023-02-06 06:52:58 +08:00
|
|
|
status_code: &mut i32,
|
|
|
|
) -> bool {
|
2023-01-16 11:52:08 +08:00
|
|
|
let mut storage = Vec::<wchar::WString>::new();
|
|
|
|
for arg in cpp_args {
|
|
|
|
storage.push(arg.into());
|
|
|
|
}
|
|
|
|
let mut args = Vec::new();
|
|
|
|
for arg in &storage {
|
|
|
|
args.push(arg.as_utfstr());
|
|
|
|
}
|
|
|
|
let streams = &mut io_streams_t::new(streams);
|
2023-02-06 06:52:58 +08:00
|
|
|
|
|
|
|
match run_builtin(parser.unpin(), streams, args.as_mut_slice(), builtin) {
|
|
|
|
None => false,
|
|
|
|
Some(status) => {
|
|
|
|
*status_code = status;
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
2023-01-16 11:52:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run_builtin(
|
|
|
|
parser: &mut parser_t,
|
|
|
|
streams: &mut io_streams_t,
|
|
|
|
args: &mut [&wstr],
|
|
|
|
builtin: RustBuiltin,
|
|
|
|
) -> Option<c_int> {
|
|
|
|
match builtin {
|
2023-02-25 00:00:05 +08:00
|
|
|
RustBuiltin::Abbr => super::abbr::abbr(parser, streams, args),
|
2023-03-01 06:42:12 +08:00
|
|
|
RustBuiltin::Bg => super::bg::bg(parser, streams, args),
|
2023-02-25 03:21:27 +08:00
|
|
|
RustBuiltin::Block => super::block::block(parser, streams, args),
|
2023-02-21 01:57:02 +08:00
|
|
|
RustBuiltin::Contains => super::contains::contains(parser, streams, args),
|
2023-02-06 05:08:32 +08:00
|
|
|
RustBuiltin::Echo => super::echo::echo(parser, streams, args),
|
2023-02-11 01:19:22 +08:00
|
|
|
RustBuiltin::Emit => super::emit::emit(parser, streams, args),
|
2023-02-19 00:13:58 +08:00
|
|
|
RustBuiltin::Exit => super::exit::exit(parser, streams, args),
|
2023-03-01 13:05:27 +08:00
|
|
|
RustBuiltin::Pwd => super::pwd::pwd(parser, streams, args),
|
2023-02-19 05:06:05 +08:00
|
|
|
RustBuiltin::Random => super::random::random(parser, streams, args),
|
2023-03-06 10:38:41 +08:00
|
|
|
RustBuiltin::Realpath => super::realpath::realpath(parser, streams, args),
|
2023-02-19 00:13:58 +08:00
|
|
|
RustBuiltin::Return => super::r#return::r#return(parser, streams, args),
|
2023-01-16 11:52:08 +08:00
|
|
|
RustBuiltin::Wait => wait::wait(parser, streams, args),
|
2023-04-02 01:17:49 +08:00
|
|
|
RustBuiltin::Printf => printf::printf(parser, streams, args),
|
2023-01-16 11:52:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Covers of these functions that take care of the pinning, etc.
|
|
|
|
// These all return STATUS_INVALID_ARGS.
|
|
|
|
pub fn builtin_missing_argument(
|
|
|
|
parser: &mut parser_t,
|
|
|
|
streams: &mut io_streams_t,
|
|
|
|
cmd: &wstr,
|
|
|
|
opt: &wstr,
|
|
|
|
print_hints: bool,
|
|
|
|
) {
|
|
|
|
ffi::builtin_missing_argument(
|
|
|
|
parser.pin(),
|
|
|
|
streams.ffi_pin(),
|
|
|
|
c_str!(cmd),
|
|
|
|
c_str!(opt),
|
|
|
|
print_hints,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn builtin_unknown_option(
|
|
|
|
parser: &mut parser_t,
|
|
|
|
streams: &mut io_streams_t,
|
|
|
|
cmd: &wstr,
|
|
|
|
opt: &wstr,
|
|
|
|
print_hints: bool,
|
|
|
|
) {
|
2023-02-05 05:28:30 +08:00
|
|
|
ffi::builtin_unknown_option(
|
2023-01-16 11:52:08 +08:00
|
|
|
parser.pin(),
|
|
|
|
streams.ffi_pin(),
|
|
|
|
c_str!(cmd),
|
|
|
|
c_str!(opt),
|
|
|
|
print_hints,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn builtin_print_help(parser: &mut parser_t, streams: &io_streams_t, cmd: &wstr) {
|
|
|
|
ffi::builtin_print_help(
|
|
|
|
parser.pin(),
|
|
|
|
streams.ffi_ref(),
|
|
|
|
c_str!(cmd),
|
|
|
|
empty_wstring(),
|
|
|
|
);
|
|
|
|
}
|
2023-02-11 01:22:56 +08:00
|
|
|
|
2023-02-19 00:13:58 +08:00
|
|
|
pub fn builtin_print_error_trailer(parser: &mut parser_t, streams: &mut io_streams_t, cmd: &wstr) {
|
|
|
|
ffi::builtin_print_error_trailer(parser.pin(), streams.err.ffi(), c_str!(cmd));
|
|
|
|
}
|
|
|
|
|
2023-02-11 01:22:56 +08:00
|
|
|
pub struct HelpOnlyCmdOpts {
|
|
|
|
pub print_help: bool,
|
|
|
|
pub optind: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HelpOnlyCmdOpts {
|
|
|
|
pub fn parse(
|
|
|
|
args: &mut [&wstr],
|
|
|
|
parser: &mut parser_t,
|
|
|
|
streams: &mut io_streams_t,
|
|
|
|
) -> Result<Self, Option<c_int>> {
|
|
|
|
let cmd = args[0];
|
|
|
|
let print_hints = true;
|
|
|
|
|
|
|
|
const shortopts: &wstr = L!("+:h");
|
|
|
|
const longopts: &[woption] = &[wopt(L!("help"), woption_argument_t::no_argument, 'h')];
|
|
|
|
|
|
|
|
let mut print_help = false;
|
|
|
|
let mut w = wgetopter_t::new(shortopts, longopts, args);
|
|
|
|
while let Some(c) = w.wgetopt_long() {
|
|
|
|
match c {
|
|
|
|
'h' => {
|
|
|
|
print_help = true;
|
|
|
|
}
|
|
|
|
':' => {
|
|
|
|
builtin_missing_argument(
|
|
|
|
parser,
|
|
|
|
streams,
|
|
|
|
cmd,
|
|
|
|
args[w.woptind - 1],
|
|
|
|
print_hints,
|
|
|
|
);
|
|
|
|
return Err(STATUS_INVALID_ARGS);
|
|
|
|
}
|
|
|
|
'?' => {
|
|
|
|
builtin_unknown_option(parser, streams, cmd, args[w.woptind - 1], print_hints);
|
|
|
|
return Err(STATUS_INVALID_ARGS);
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
panic!("unexpected retval from wgetopter::wgetopt_long()");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(HelpOnlyCmdOpts {
|
|
|
|
print_help,
|
|
|
|
optind: w.woptind,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|