tilde_expand/
lib.rs

1extern crate libc;
2
3use libc::{c_char, c_int, size_t};
4
5extern
6{
7	//fn getpwuid_r(uid: uid_t, pwd: *mut libc::passwd, buf: *mut c_char, buflen: size_t, result: *mut *mut libc::passwd) -> c_int
8	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
43/// perform tilde-expansion, replacing an initial ~ or ~username with that username's home directory as determined by getpwnam
44pub fn tilde_expand(s: &[u8]) -> Vec<u8>
45{
46	let mut out = Vec::with_capacity(s.len());
47	/* if it starts with ~ and has no other tildes, tilde-expand it */
48	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}