1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
extern crate libc;
use libc::{c_char, c_int, size_t};
extern
{
fn getpwnam_r(name: *const c_char, pwd: *mut libc::passwd, buf: *mut c_char, buflen: size_t, result: *mut *mut libc::passwd) -> c_int;
}
fn write_user_home<W: std::io::Write>(mut writer: W, username: &[u8]) -> Result<usize, std::io::Error>
{
let mut getpw_string_buf = [0; 4096];
let mut passwd: libc::passwd = unsafe {std::mem::zeroed()};
let mut passwd_out: *mut libc::passwd = std::ptr::null_mut();
let result = if username == &[]
{
let uid = unsafe {libc::getuid()};
unsafe {libc::getpwuid_r(uid, &mut passwd as *mut _,
getpw_string_buf.as_mut_ptr(), getpw_string_buf.len() as size_t,
&mut passwd_out as *mut _)}
}
else
{
let username = match std::ffi::CString::new(username)
{
Ok(un) => un,
Err(_) => return Err(std::io::Error::from_raw_os_error(libc::ENOENT)),
};
unsafe {getpwnam_r(username.as_ptr(), &mut passwd as *mut _,
getpw_string_buf.as_mut_ptr(), getpw_string_buf.len() as size_t,
&mut passwd_out as *mut _)}
};
if result == 0 {
writer.write(unsafe {std::ffi::CStr::from_ptr(passwd.pw_dir)}.to_bytes())
}
else
{
Err(std::io::Error::from_raw_os_error(result))
}
}
pub fn tilde_expand(s: &[u8]) -> Vec<u8>
{
let mut out = Vec::with_capacity(s.len());
match s.first()
{
Some(&b'~') if s.iter().filter(|&&c| c==b'~').count() == 1 => {
let end = s.iter().position(|&c| c==b'/').unwrap_or(s.len());
let name = &s[1..end];
let _ = write_user_home(&mut out, name);
out.extend_from_slice(&s[end..]);
},
_ => out.extend_from_slice(s)
}
out
}
#[test]
fn test()
{
println!("{}", String::from_utf8_lossy(&*tilde_expand(b"~/user-test")));
println!("{}", String::from_utf8_lossy(&*tilde_expand(b"~root/root-test")));
println!("{}", String::from_utf8_lossy(&*tilde_expand(b"noexpand~/test")));
println!("{}", String::from_utf8_lossy(&*tilde_expand(b"~~/noexpand-test")));
}