use crate::compsys::ported::_arguments::_arguments;
use crate::ported::zle::compcore::{get_compstate_str, set_compstate_str};
const XARGS: [&str; 2] = [
"-display:display:_x_display",
"-geometry:geometry:_x_geometry",
];
fn build_xargs(no_positionals: bool) -> Vec<String> {
let mut x: Vec<String> = XARGS.iter().map(|s| s.to_string()).collect();
if no_positionals {
x.push("*:default: _default".to_string()); }
x
}
fn splice_xargs(argv: &[String], xargs: &[String]) -> Vec<String> {
match argv.iter().rposition(|a| a == "--") {
Some(idx) => {
let mut out = Vec::with_capacity(argv.len() + xargs.len());
out.extend_from_slice(&argv[..idx]);
out.extend_from_slice(xargs);
out.push("--".to_string());
out.extend_from_slice(&argv[idx + 1..]);
out
}
None => {
let mut out = argv.to_vec();
out.extend_from_slice(xargs);
out
}
}
}
fn matches_leading_opt(w: &str) -> bool {
let b = w.as_bytes();
if b.len() < 2 || b[0] != b'-' {
return false;
}
match b[1] {
b'O' => true,
b'C' | b'R' | b'W' | b's' | b'w' => b.len() == 2,
_ => false,
}
}
fn parse_leading(argv: &[String]) -> (Vec<String>, Vec<String>, bool) {
let mut opts: Vec<String> = Vec::new();
let mut rawret = false;
let mut p = 0usize;
while p < argv.len() {
let w = &argv[p];
if !matches_leading_opt(w) {
break;
}
opts.push(w.clone()); if w == "-R" {
rawret = true; }
p += 1; }
let remaining: Vec<String> = argv[p..].to_vec();
(opts, remaining, rawret)
}
fn nmatches() -> i64 {
get_compstate_str("nmatches")
.and_then(|s| s.trim().parse::<i64>().ok())
.unwrap_or(0)
}
pub fn _x_arguments(args: &[String]) -> i32 {
let _fn_scope = crate::compsys::ported::shared::FnScope::enter("_x_arguments");
let nm = nmatches();
let xargs = build_xargs(args.is_empty());
let argv = splice_xargs(args, &xargs);
let (opts, remaining, rawret) = parse_leading(&argv);
let mut call: Vec<String> = Vec::with_capacity(1 + opts.len() + remaining.len());
call.push("-R".to_string());
call.extend(opts);
call.extend(remaining);
let mut ret =
crate::compsys::ported::shared::call_compfn("_arguments", &call, || _arguments(&call));
if ret == 300 {
set_compstate_str("restore", ""); if !rawret {
ret = if nm == nmatches() { 1 } else { 0 };
}
}
ret }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn leading_opt_guard_matches_spec() {
for ok in ["-C", "-R", "-W", "-s", "-w", "-O", "-Oxx"] {
assert!(matches_leading_opt(ok), "{ok} should match");
}
for no in [
"-Wx", "-Cx", "-d", "-r", "--", "-", "", "-F", "-Fgrp", "spec",
] {
assert!(!matches_leading_opt(no), "{no} should not match");
}
}
#[test]
fn build_xargs_appends_default_only_when_empty() {
let with = build_xargs(true);
assert_eq!(with.len(), 3);
assert_eq!(with.last().unwrap(), "*:default: _default");
assert!(with.contains(&"-display:display:_x_display".to_string()));
assert!(with.contains(&"-geometry:geometry:_x_geometry".to_string()));
let without = build_xargs(false);
assert_eq!(without.len(), 2);
assert!(!without.iter().any(|s| s == "*:default: _default"));
}
#[test]
fn splice_inserts_before_last_dashdash_else_appends() {
let xargs = vec!["A".to_string(), "B".to_string()];
let out = splice_xargs(&["x".to_string(), "y".to_string()], &xargs);
assert_eq!(out, vec!["x", "y", "A", "B"]);
let argv = vec![
"-C".to_string(),
"--".to_string(),
"tail".to_string(),
"--".to_string(),
"z".to_string(),
];
let out = splice_xargs(&argv, &xargs);
assert_eq!(out, vec!["-C", "--", "tail", "A", "B", "--", "z"]);
}
#[test]
fn parse_leading_collects_opts_and_rawret() {
let (opts, remaining, rawret) = parse_leading(&[
"-C".to_string(),
"-R".to_string(),
"-display:display:_x_display".to_string(),
]);
assert_eq!(opts, vec!["-C", "-R"]);
assert!(rawret);
assert_eq!(remaining, vec!["-display:display:_x_display".to_string()]);
}
#[test]
fn parse_leading_stops_at_first_nonopt() {
let (opts, remaining, rawret) =
parse_leading(&["-W".to_string(), "spec".to_string(), "-C".to_string()]);
assert_eq!(opts, vec!["-W"]);
assert!(!rawret);
assert_eq!(remaining, vec!["spec".to_string(), "-C".to_string()]);
}
}