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