#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
use std::ffi::CString;
use crate::ported::utils::{metafy, unmetafy, zwarnnam};
use crate::ported::zsh_h::{module, options, OPT_ISSET};
#[cfg(target_os = "macos")]
const XATTR_NOFOLLOW: i32 = 0x0001;
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn xgetxattr(path: &str, name: &str, value: &mut [u8], symlink: i32) -> isize { let path_c = match CString::new(path) { Ok(c) => c, Err(_) => return -1 };
let name_c = match CString::new(name) { Ok(c) => c, Err(_) => return -1 };
let val_ptr = if value.is_empty() {
std::ptr::null_mut()
} else {
value.as_mut_ptr() as *mut libc::c_void
};
#[cfg(target_os = "macos")]
{
unsafe {
libc::getxattr(path_c.as_ptr(), name_c.as_ptr(), val_ptr, value.len(), 0,
if symlink != 0 { XATTR_NOFOLLOW } else { 0 })
}
}
#[cfg(target_os = "linux")]
{
match symlink {
0 => unsafe { libc::getxattr(path_c.as_ptr(), name_c.as_ptr(), val_ptr, value.len()) },
_ => unsafe { libc::lgetxattr(path_c.as_ptr(), name_c.as_ptr(), val_ptr, value.len()) },
}
}
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
pub fn xgetxattr(_path: &str, _name: &str, _value: &mut [u8], _symlink: i32) -> isize { -1 }
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn xlistxattr(path: &str, list: &mut [u8], symlink: i32) -> isize { let path_c = match CString::new(path) { Ok(c) => c, Err(_) => return -1 };
let list_ptr = if list.is_empty() {
std::ptr::null_mut()
} else {
list.as_mut_ptr() as *mut libc::c_char
};
#[cfg(target_os = "macos")]
{
unsafe {
libc::listxattr(path_c.as_ptr(), list_ptr, list.len(),
if symlink != 0 { XATTR_NOFOLLOW } else { 0 })
}
}
#[cfg(target_os = "linux")]
{
match symlink {
0 => unsafe { libc::listxattr(path_c.as_ptr(), list_ptr, list.len()) },
_ => unsafe { libc::llistxattr(path_c.as_ptr(), list_ptr, list.len()) },
}
}
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
pub fn xlistxattr(_path: &str, _list: &mut [u8], _symlink: i32) -> isize { -1 }
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn xsetxattr(path: &str, name: &str, value: &[u8], flags: i32, symlink: i32) -> i32 { let path_c = match CString::new(path) { Ok(c) => c, Err(_) => return -1 };
let name_c = match CString::new(name) { Ok(c) => c, Err(_) => return -1 };
let val_ptr = value.as_ptr() as *const libc::c_void;
#[cfg(target_os = "macos")]
{
let combined = if (flags | symlink) != 0 { XATTR_NOFOLLOW } else { 0 };
unsafe {
libc::setxattr(path_c.as_ptr(), name_c.as_ptr(), val_ptr, value.len(), 0, combined)
}
}
#[cfg(target_os = "linux")]
{
match symlink {
0 => unsafe { libc::setxattr(path_c.as_ptr(), name_c.as_ptr(), val_ptr, value.len(), flags) },
_ => unsafe { libc::lsetxattr(path_c.as_ptr(), name_c.as_ptr(), val_ptr, value.len(), flags) },
}
}
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
pub fn xsetxattr(_path: &str, _name: &str, _value: &[u8], _flags: i32, _symlink: i32) -> i32 { -1 }
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn xremovexattr(path: &str, name: &str, symlink: i32) -> i32 { let path_c = match CString::new(path) { Ok(c) => c, Err(_) => return -1 };
let name_c = match CString::new(name) { Ok(c) => c, Err(_) => return -1 };
#[cfg(target_os = "macos")]
{
unsafe { libc::removexattr(path_c.as_ptr(), name_c.as_ptr(),
if symlink != 0 { XATTR_NOFOLLOW } else { 0 }) }
}
#[cfg(target_os = "linux")]
{
match symlink {
0 => unsafe { libc::removexattr(path_c.as_ptr(), name_c.as_ptr()) },
_ => unsafe { libc::lremovexattr(path_c.as_ptr(), name_c.as_ptr()) },
}
}
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
#[allow(unused_variables)]
pub fn xremovexattr(path: &str, name: &str, symlink: i32) -> i32 { -1 }
#[allow(unused_variables)]
pub fn bin_getattr(nam: &str, argv: &[String], ops: &options, func: i32) -> i32 { let mut ret: i32 = 0;
let mut val_len: isize = 0;
let mut attr_len: isize = 0;
let _slen: usize;
let file_arg = argv.get(0).map(|s| s.as_str()).unwrap_or("");
let attr_arg = argv.get(1).map(|s| s.as_str()).unwrap_or("");
let param: Option<&str> = argv.get(2).map(|s| s.as_str());
let symlink: i32 = if OPT_ISSET(ops, b'h') { 1 } else { 0 };
let mut file_bytes = file_arg.as_bytes().to_vec();
_slen = unmetafy(&mut file_bytes);
let mut attr_bytes = attr_arg.as_bytes().to_vec();
unmetafy(&mut attr_bytes);
let file = std::str::from_utf8(&file_bytes).unwrap_or(file_arg);
let attr = std::str::from_utf8(&attr_bytes).unwrap_or(attr_arg);
val_len = xgetxattr(file, attr, &mut [], symlink);
if val_len == 0 { if let Some(p) = param { unsetparam(p); }
return 0; }
if val_len > 0 { let mut value: Vec<u8> = vec![0u8; (val_len + 1) as usize];
attr_len = xgetxattr(file, attr, &mut value[..val_len as usize], symlink);
if attr_len > 0 && attr_len <= val_len { value[attr_len as usize] = b'\0'; let val_plain = String::from_utf8_lossy(&value[..attr_len as usize]).into_owned();
if let Some(p) = param { setsparam(p, &metafy(&val_plain));
} else {
println!("{}", val_plain); }
}
}
if val_len < 0 || attr_len < 0 || attr_len > val_len { zwarnnam(nam, &format!("{}: {}", metafy(file), std::io::Error::last_os_error()));
ret = 1 + i32::from((val_len > 0 && attr_len > val_len) || attr_len < 0);
}
ret }
#[allow(unused_variables)]
pub fn bin_setattr(nam: &str, argv: &[String], ops: &options, func: i32) -> i32 { let _slen: usize;
let vlen: usize;
let symlink: i32 = if OPT_ISSET(ops, b'h') { 1 } else { 0 };
let file_arg = argv.get(0).map(|s| s.as_str()).unwrap_or("");
let attr_arg = argv.get(1).map(|s| s.as_str()).unwrap_or("");
let value_arg = argv.get(2).map(|s| s.as_str()).unwrap_or("");
let mut file_bytes = file_arg.as_bytes().to_vec();
_slen = unmetafy(&mut file_bytes);
let mut attr_bytes = attr_arg.as_bytes().to_vec();
unmetafy(&mut attr_bytes);
let mut value_bytes = value_arg.as_bytes().to_vec();
vlen = unmetafy(&mut value_bytes);
let file = std::str::from_utf8(&file_bytes).unwrap_or(file_arg);
let attr = std::str::from_utf8(&attr_bytes).unwrap_or(attr_arg);
if xsetxattr(file, attr, &value_bytes[..vlen], 0, symlink) != 0 {
zwarnnam(nam, &format!("{}: {}", metafy(file), std::io::Error::last_os_error()));
return 1; }
0 }
#[allow(unused_variables)]
pub fn bin_delattr(nam: &str, argv: &[String], ops: &options, func: i32) -> i32 {
let _slen: usize;
let symlink: i32 = if OPT_ISSET(ops, b'h') { 1 } else { 0 };
let file_arg = argv.get(0).map(|s| s.as_str()).unwrap_or("");
let mut file_bytes = file_arg.as_bytes().to_vec();
_slen = unmetafy(&mut file_bytes);
let file = std::str::from_utf8(&file_bytes)
.map(|s| s.to_string())
.unwrap_or_else(|_| file_arg.to_string());
for attr_arg in &argv[1..] {
let mut attr_bytes = attr_arg.as_bytes().to_vec();
unmetafy(&mut attr_bytes);
let attr = std::str::from_utf8(&attr_bytes).unwrap_or(attr_arg);
if xremovexattr(&file, attr, symlink) != 0 { zwarnnam(nam, &format!("{}: {}", metafy(&file), std::io::Error::last_os_error()));
return 1; }
}
0 }
#[allow(unused_variables)]
pub fn bin_listattr(nam: &str, argv: &[String], ops: &options, func: i32) -> i32 {
let mut ret: i32 = 0;
let val_len: isize;
let mut list_len: isize = 0;
let _slen: usize;
let file_arg = argv.get(0).map(|s| s.as_str()).unwrap_or("");
let param: Option<&str> = argv.get(1).map(|s| s.as_str());
let symlink: i32 = if OPT_ISSET(ops, b'h') { 1 } else { 0 };
let mut file_bytes = file_arg.as_bytes().to_vec();
_slen = unmetafy(&mut file_bytes);
let file_owned = std::str::from_utf8(&file_bytes)
.map(|s| s.to_string())
.unwrap_or_else(|_| file_arg.to_string());
let file = file_owned.as_str();
val_len = xlistxattr(file, &mut [], symlink);
if val_len == 0 { if let Some(p) = param { unsetparam(p); }
return 0; }
if val_len > 0 { let mut value: Vec<u8> = vec![0u8; (val_len + 1) as usize];
list_len = xlistxattr(file, &mut value[..val_len as usize], symlink);
if list_len > 0 && list_len <= val_len { let names_bytes = &value[..list_len as usize];
let raw_names: Vec<&[u8]> = names_bytes
.split(|&b| b == 0)
.filter(|s| !s.is_empty())
.collect();
if let Some(p) = param { let metafied_names: Vec<String> = raw_names
.iter()
.map(|n| metafy(&String::from_utf8_lossy(n)))
.collect();
setaparam(p, metafied_names); } else {
for n in &raw_names {
println!("{}", String::from_utf8_lossy(n)); }
}
}
}
if val_len < 0 || list_len < 0 || list_len > val_len { zwarnnam(nam, &format!("{}: {}", metafy(file), std::io::Error::last_os_error()));
ret = 1 + i32::from(list_len > val_len || list_len < 0);
}
ret }
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 { 0
}
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 {
*features = featuresarray(m, module_features());
0 }
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 {
handlefeatures(m, module_features(), enables) }
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 { 0
}
pub fn cleanup_(m: *const module) -> i32 {
setfeatureenables(m, module_features(), None) }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 { 0
}
fn setsparam(name: &str, value: &str) {
crate::ported::params::setsparam(name, value);
}
fn setaparam(name: &str, value: Vec<String>) {
crate::ported::params::setsparam(name, &value.join(":"));
}
fn unsetparam(v: &str) {
std::env::remove_var(v);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::zsh_h::MAX_OPS;
fn empty_ops() -> options {
options { ind: [0u8; MAX_OPS], args: Vec::new(), argscount: 0, argsalloc: 0 }
}
#[test]
fn xgetxattr_nonexistent_returns_negative() {
let mut buf = [0u8; 0];
let r = xgetxattr("/nonexistent/path", "user.test", &mut buf, 0);
assert!(r < 0);
}
#[test]
fn xsetxattr_nonexistent_returns_negative() {
let r = xsetxattr("/nonexistent/path", "user.test", b"value", 0, 0);
assert!(r < 0);
}
#[test]
fn xlistxattr_nonexistent_returns_negative() {
let mut buf = [0u8; 0];
let r = xlistxattr("/nonexistent/path", &mut buf, 0);
assert!(r < 0);
}
#[test]
fn xremovexattr_nonexistent_returns_negative() {
let r = xremovexattr("/nonexistent/path", "user.test", 0);
assert!(r < 0);
}
#[test]
fn bin_getattr_nonexistent_path_returns_nonzero() {
let ops = empty_ops();
let argv: Vec<String> = vec!["/nonexistent/path".into(), "user.test".into()];
let rc = bin_getattr("zgetattr", &argv, &ops, 0);
assert_ne!(rc, 0);
}
#[test]
fn bin_setattr_nonexistent_path_returns_one() {
let ops = empty_ops();
let argv: Vec<String> = vec!["/nonexistent/path".into(), "user.test".into(), "value".into()];
let rc = bin_setattr("zsetattr", &argv, &ops, 0);
assert_eq!(rc, 1);
}
#[test]
fn module_loaders_return_zero() {
let m: *const module = std::ptr::null();
let mut features: Vec<String> = Vec::new();
let mut enables: Option<Vec<i32>> = None;
assert_eq!(setup_(m), 0);
assert_eq!(features_(m, &mut features), 0);
assert_eq!(features.len(), 4);
assert_eq!(enables_(m, &mut enables), 0);
assert_eq!(boot_(m), 0);
assert_eq!(cleanup_(m), 0);
assert_eq!(finish_(m), 0);
}
}
use crate::ported::zsh_h::features as features_t;
use std::sync::{Mutex, OnceLock};
static MODULE_FEATURES: OnceLock<Mutex<features_t>> = OnceLock::new();
fn module_features() -> &'static Mutex<features_t> {
MODULE_FEATURES.get_or_init(|| Mutex::new(features_t {
bn_list: None,
bn_size: 4,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 0,
n_abstract: 0,
}))
}
fn featuresarray(_m: *const module, _f: &Mutex<features_t>) -> Vec<String> {
vec!["b:zgetattr".to_string(), "b:zsetattr".to_string(), "b:zdelattr".to_string(), "b:zlistattr".to_string()]
}
fn handlefeatures(
_m: *const module,
_f: &Mutex<features_t>,
enables: &mut Option<Vec<i32>>,
) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 4]);
}
0
}
fn setfeatureenables(
_m: *const module,
_f: &Mutex<features_t>,
_e: Option<&[i32]>,
) -> i32 {
0
}