mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-08-04 23:17:46 +00:00
Split utility files into separate library.
Everything in src/common has been moved to src/uucore. This is defined as a Cargo library, instead of directly included. This gives us flexibility to make the library an external crate in the future. Fixes #717.
This commit is contained in:
parent
67b07eaaa9
commit
6095dfee66
10 changed files with 1120 additions and 0 deletions
11
src/uucore/Cargo.toml
Normal file
11
src/uucore/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "uucore"
|
||||
version = "0.0.1"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
libc = "*"
|
||||
time = "*"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
240
src/uucore/c_types.rs
Normal file
240
src/uucore/c_types.rs
Normal file
|
@ -0,0 +1,240 @@
|
|||
#![allow(dead_code, non_camel_case_types, raw_pointer_derive)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use self::libc::{
|
||||
c_char,
|
||||
c_int,
|
||||
uid_t,
|
||||
gid_t,
|
||||
};
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||
use self::libc::time_t;
|
||||
#[cfg(target_os = "macos")]
|
||||
use self::libc::int32_t;
|
||||
|
||||
use self::libc::getgroups;
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::{Error, Write};
|
||||
use std::iter::repeat;
|
||||
use std::vec::Vec;
|
||||
|
||||
use std::ptr::{null_mut, read};
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct c_passwd {
|
||||
pub pw_name: *const c_char, /* user name */
|
||||
pub pw_passwd: *const c_char, /* user name */
|
||||
pub pw_uid: uid_t, /* user uid */
|
||||
pub pw_gid: gid_t, /* user gid */
|
||||
pub pw_change: time_t,
|
||||
pub pw_class: *const c_char,
|
||||
pub pw_gecos: *const c_char,
|
||||
pub pw_dir: *const c_char,
|
||||
pub pw_shell: *const c_char,
|
||||
pub pw_expire: time_t
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct c_passwd {
|
||||
pub pw_name: *const c_char, /* user name */
|
||||
pub pw_passwd: *const c_char, /* user name */
|
||||
pub pw_uid: uid_t, /* user uid */
|
||||
pub pw_gid: gid_t, /* user gid */
|
||||
pub pw_gecos: *const c_char,
|
||||
pub pw_dir: *const c_char,
|
||||
pub pw_shell: *const c_char,
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||
#[repr(C)]
|
||||
pub struct utsname {
|
||||
pub sysname: [c_char; 256],
|
||||
pub nodename: [c_char; 256],
|
||||
pub release: [c_char; 256],
|
||||
pub version: [c_char; 256],
|
||||
pub machine: [c_char; 256]
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[repr(C)]
|
||||
pub struct utsname {
|
||||
pub sysname: [c_char; 65],
|
||||
pub nodename: [c_char; 65],
|
||||
pub release: [c_char; 65],
|
||||
pub version: [c_char; 65],
|
||||
pub machine: [c_char; 65],
|
||||
pub domainame: [c_char; 65]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct c_group {
|
||||
pub gr_name: *const c_char, // group name
|
||||
pub gr_passwd: *const c_char, // password
|
||||
pub gr_gid: gid_t, // group id
|
||||
pub gr_mem: *const *const c_char, // member list
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct c_tm {
|
||||
pub tm_sec: c_int, /* seconds */
|
||||
pub tm_min: c_int, /* minutes */
|
||||
pub tm_hour: c_int, /* hours */
|
||||
pub tm_mday: c_int, /* day of the month */
|
||||
pub tm_mon: c_int, /* month */
|
||||
pub tm_year: c_int, /* year */
|
||||
pub tm_wday: c_int, /* day of the week */
|
||||
pub tm_yday: c_int, /* day in the year */
|
||||
pub tm_isdst: c_int /* daylight saving time */
|
||||
}
|
||||
|
||||
extern {
|
||||
pub fn getpwuid(uid: uid_t) -> *const c_passwd;
|
||||
pub fn getpwnam(login: *const c_char) -> *const c_passwd;
|
||||
pub fn getgrgid(gid: gid_t) -> *const c_group;
|
||||
pub fn getgrnam(name: *const c_char) -> *const c_group;
|
||||
pub fn getgrouplist(name: *const c_char,
|
||||
gid: gid_t,
|
||||
groups: *mut gid_t,
|
||||
ngroups: *mut c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
extern {
|
||||
pub fn getgroupcount(name: *const c_char, gid: gid_t) -> int32_t;
|
||||
}
|
||||
|
||||
pub fn get_pw_from_args(free: &Vec<String>) -> Option<c_passwd> {
|
||||
if free.len() == 1 {
|
||||
let username = &free[0][..];
|
||||
|
||||
// Passed user as id
|
||||
if username.chars().all(|c| c.is_digit(10)) {
|
||||
let id = username.parse::<u32>().unwrap();
|
||||
let pw_pointer = unsafe { getpwuid(id as uid_t) };
|
||||
|
||||
if !pw_pointer.is_null() {
|
||||
Some(unsafe { read(pw_pointer) })
|
||||
} else {
|
||||
crash!(1, "{}: no such user", username);
|
||||
}
|
||||
|
||||
// Passed the username as a string
|
||||
} else {
|
||||
let pw_pointer = unsafe {
|
||||
let cstr = CString::new(username).unwrap();
|
||||
getpwnam(cstr.as_bytes_with_nul().as_ptr() as *const i8)
|
||||
};
|
||||
if !pw_pointer.is_null() {
|
||||
Some(unsafe { read(pw_pointer) })
|
||||
} else {
|
||||
crash!(1, "{}: no such user", username);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_group(groupname: &str) -> Option<c_group> {
|
||||
let group = if groupname.chars().all(|c| c.is_digit(10)) {
|
||||
unsafe { getgrgid(groupname.parse().unwrap()) }
|
||||
} else {
|
||||
unsafe {
|
||||
let cstr = CString::new(groupname).unwrap();
|
||||
getgrnam(cstr.as_bytes_with_nul().as_ptr() as *const c_char)
|
||||
}
|
||||
};
|
||||
|
||||
if !group.is_null() {
|
||||
Some(unsafe { read(group) })
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_group_list(name: *const c_char, gid: gid_t) -> Vec<gid_t> {
|
||||
let mut ngroups: c_int = 32;
|
||||
let mut groups: Vec<gid_t> = Vec::with_capacity(ngroups as usize);
|
||||
|
||||
if unsafe { get_group_list_internal(name, gid, groups.as_mut_ptr(), &mut ngroups) } == -1 {
|
||||
groups.reserve(ngroups as usize);
|
||||
unsafe { get_group_list_internal(name, gid, groups.as_mut_ptr(), &mut ngroups); }
|
||||
} else {
|
||||
groups.truncate(ngroups as usize);
|
||||
}
|
||||
unsafe { groups.set_len(ngroups as usize); }
|
||||
|
||||
groups
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
#[inline(always)]
|
||||
unsafe fn get_group_list_internal(name: *const c_char, gid: gid_t, groups: *mut gid_t, grcnt: *mut c_int) -> c_int {
|
||||
getgrouplist(name, gid, groups, grcnt)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe fn get_group_list_internal(name: *const c_char, gid: gid_t, groups: *mut gid_t, grcnt: *mut c_int) -> c_int {
|
||||
let ngroups = getgroupcount(name, gid);
|
||||
let oldsize = *grcnt;
|
||||
*grcnt = ngroups;
|
||||
if oldsize >= ngroups {
|
||||
getgrouplist(name, gid, groups, grcnt);
|
||||
0
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_groups() -> Result<Vec<gid_t>, i32> {
|
||||
let ngroups = unsafe { getgroups(0, null_mut()) };
|
||||
if ngroups == -1 {
|
||||
return Err(Error::last_os_error().raw_os_error().unwrap())
|
||||
}
|
||||
|
||||
let mut groups : Vec<gid_t>= repeat(0).take(ngroups as usize).collect();
|
||||
let ngroups = unsafe { getgroups(ngroups, groups.as_mut_ptr()) };
|
||||
if ngroups == -1 {
|
||||
Err(Error::last_os_error().raw_os_error().unwrap())
|
||||
} else {
|
||||
groups.truncate(ngroups as usize);
|
||||
Ok(groups)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn group(possible_pw: Option<c_passwd>, nflag: bool) {
|
||||
let groups = match possible_pw {
|
||||
Some(pw) => Ok(get_group_list(pw.pw_name, pw.pw_gid)),
|
||||
None => get_groups(),
|
||||
};
|
||||
|
||||
match groups {
|
||||
Err(errno) =>
|
||||
crash!(1, "failed to get group list (errno={})", errno),
|
||||
Ok(groups) => {
|
||||
for &g in groups.iter() {
|
||||
if nflag {
|
||||
let group = unsafe { getgrgid(g) };
|
||||
if !group.is_null() {
|
||||
let name = unsafe {
|
||||
let gname = read(group).gr_name;
|
||||
let bytes= CStr::from_ptr(gname).to_bytes();
|
||||
String::from_utf8_lossy(bytes).to_string()
|
||||
};
|
||||
print!("{} ", name);
|
||||
}
|
||||
} else {
|
||||
print!("{} ", g);
|
||||
}
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
}
|
144
src/uucore/fs.rs
Normal file
144
src/uucore/fs.rs
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Joseph Crail <jbcrail@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
// Based on the pattern using by Cargo, I created a shim over the
|
||||
// standard PathExt trait, so that the unstable path methods could
|
||||
// be backported to stable (<= 1.1). This will likely be dropped
|
||||
// when the path trait stabilizes.
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
pub trait UUPathExt {
|
||||
fn uu_exists(&self) -> bool;
|
||||
fn uu_is_file(&self) -> bool;
|
||||
fn uu_is_dir(&self) -> bool;
|
||||
fn uu_metadata(&self) -> Result<fs::Metadata>;
|
||||
}
|
||||
|
||||
impl UUPathExt for Path {
|
||||
fn uu_exists(&self) -> bool {
|
||||
fs::metadata(self).is_ok()
|
||||
}
|
||||
|
||||
fn uu_is_file(&self) -> bool {
|
||||
fs::metadata(self).map(|m| m.is_file()).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn uu_is_dir(&self) -> bool {
|
||||
fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn uu_metadata(&self) -> Result<fs::Metadata> {
|
||||
fs::metadata(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum CanonicalizeMode {
|
||||
None,
|
||||
Normal,
|
||||
Existing,
|
||||
Missing,
|
||||
}
|
||||
|
||||
fn resolve<P: AsRef<Path>>(original: P) -> Result<PathBuf> {
|
||||
const MAX_LINKS_FOLLOWED: u32 = 255;
|
||||
let mut followed = 0;
|
||||
let mut result = original.as_ref().to_path_buf();
|
||||
loop {
|
||||
if followed == MAX_LINKS_FOLLOWED {
|
||||
return Err(Error::new(ErrorKind::InvalidInput, "maximum links followed"));
|
||||
}
|
||||
|
||||
match fs::metadata(&result) {
|
||||
Err(e) => return Err(e),
|
||||
Ok(ref m) if !m.file_type().is_symlink() => break,
|
||||
Ok(..) => {
|
||||
followed += 1;
|
||||
match fs::read_link(&result) {
|
||||
Ok(path) => {
|
||||
result.pop();
|
||||
result.push(path);
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) -> Result<PathBuf> {
|
||||
// Create an absolute path
|
||||
let original = original.as_ref();
|
||||
let original = if original.is_absolute() {
|
||||
original.to_path_buf()
|
||||
} else {
|
||||
env::current_dir().unwrap().join(original)
|
||||
};
|
||||
|
||||
let mut result = PathBuf::new();
|
||||
let mut parts = vec!();
|
||||
|
||||
// Split path by directory separator; add prefix (Windows-only) and root
|
||||
// directory to final path buffer; add remaining parts to temporary
|
||||
// vector for canonicalization.
|
||||
for part in original.components() {
|
||||
match part {
|
||||
Component::Prefix(_) | Component::RootDir => {
|
||||
result.push(part.as_os_str());
|
||||
},
|
||||
Component::CurDir => {},
|
||||
Component::ParentDir => {
|
||||
parts.pop();
|
||||
},
|
||||
Component::Normal(_) => {
|
||||
parts.push(part.as_os_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the symlinks where possible
|
||||
if parts.len() > 0 {
|
||||
for part in parts[..parts.len()-1].iter() {
|
||||
result.push(part);
|
||||
|
||||
if can_mode == CanonicalizeMode::None {
|
||||
continue;
|
||||
}
|
||||
|
||||
match resolve(&result) {
|
||||
Err(e) => match can_mode {
|
||||
CanonicalizeMode::Missing => continue,
|
||||
_ => return Err(e)
|
||||
},
|
||||
Ok(path) => {
|
||||
result.pop();
|
||||
result.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.push(parts.last().unwrap());
|
||||
|
||||
match resolve(&result) {
|
||||
Err(e) => { if can_mode == CanonicalizeMode::Existing { return Err(e); } },
|
||||
Ok(path) => {
|
||||
result.pop();
|
||||
result.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
14
src/uucore/lib.rs
Normal file
14
src/uucore/lib.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
extern crate libc;
|
||||
extern crate time;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod c_types;
|
||||
pub mod fs;
|
||||
pub mod parse_time;
|
||||
pub mod process;
|
||||
#[cfg(unix)] pub mod signals;
|
||||
#[cfg(unix)] pub mod utmpx;
|
||||
|
||||
#[cfg(windows)] pub mod wide;
|
220
src/uucore/macros.rs
Normal file
220
src/uucore/macros.rs
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Arcterus <arcterus@mail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! show_error(
|
||||
($($args:tt)+) => ({
|
||||
pipe_write!(&mut ::std::io::stderr(), "{}: error: ", module_path!());
|
||||
pipe_writeln!(&mut ::std::io::stderr(), $($args)+);
|
||||
})
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! show_warning(
|
||||
($($args:tt)+) => ({
|
||||
pipe_write!(&mut ::std::io::stderr(), "{}: warning: ", module_path!());
|
||||
pipe_writeln!(&mut ::std::io::stderr(), $($args)+);
|
||||
})
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! show_info(
|
||||
($($args:tt)+) => ({
|
||||
pipe_write!(&mut ::std::io::stderr(), "{}: ", module_path!());
|
||||
pipe_writeln!(&mut ::std::io::stderr(), $($args)+);
|
||||
})
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! eprint(
|
||||
($($args:tt)+) => (pipe_write!(&mut ::std::io::stderr(), $($args)+))
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! eprintln(
|
||||
($($args:tt)+) => (pipe_writeln!(&mut ::std::io::stderr(), $($args)+))
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! crash(
|
||||
($exitcode:expr, $($args:tt)+) => ({
|
||||
show_error!($($args)+);
|
||||
::std::process::exit($exitcode)
|
||||
})
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exit(
|
||||
($exitcode:expr) => ({
|
||||
::std::process::exit($exitcode)
|
||||
})
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! crash_if_err(
|
||||
($exitcode:expr, $exp:expr) => (
|
||||
match $exp {
|
||||
Ok(m) => m,
|
||||
Err(f) => crash!($exitcode, "{}", f),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pipe_crash_if_err(
|
||||
($exitcode:expr, $exp:expr) => (
|
||||
match $exp {
|
||||
Ok(_) => (),
|
||||
Err(f) => {
|
||||
if f.kind() == ::std::io::ErrorKind::BrokenPipe {
|
||||
()
|
||||
} else {
|
||||
crash!($exitcode, "{}", f)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! return_if_err(
|
||||
($exitcode:expr, $exp:expr) => (
|
||||
match $exp {
|
||||
Ok(m) => m,
|
||||
Err(f) => {
|
||||
show_error!("{}", f);
|
||||
return $exitcode;
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// XXX: should the pipe_* macros return an Err just to show the write failed?
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pipe_print(
|
||||
($($args:tt)+) => (
|
||||
match write!(&mut ::std::io::stdout(), $($args)+) {
|
||||
Ok(_) => true,
|
||||
Err(f) => {
|
||||
if f.kind() == ::std::io::ErrorKind::BrokenPipe {
|
||||
false
|
||||
} else {
|
||||
panic!("{}", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pipe_println(
|
||||
($($args:tt)+) => (
|
||||
match writeln!(&mut ::std::io::stdout(), $($args)+) {
|
||||
Ok(_) => true,
|
||||
Err(f) => {
|
||||
if f.kind() == ::std::io::ErrorKind::BrokenPipe {
|
||||
false
|
||||
} else {
|
||||
panic!("{}", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pipe_write(
|
||||
($fd:expr, $($args:tt)+) => (
|
||||
match write!($fd, $($args)+) {
|
||||
Ok(_) => true,
|
||||
Err(f) => {
|
||||
if f.kind() == ::std::io::ErrorKind::BrokenPipe {
|
||||
false
|
||||
} else {
|
||||
panic!("{}", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pipe_writeln(
|
||||
($fd:expr, $($args:tt)+) => (
|
||||
match writeln!($fd, $($args)+) {
|
||||
Ok(_) => true,
|
||||
Err(f) => {
|
||||
if f.kind() == ::std::io::ErrorKind::BrokenPipe {
|
||||
false
|
||||
} else {
|
||||
panic!("{}", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pipe_flush(
|
||||
() => (
|
||||
match ::std::io::stdout().flush() {
|
||||
Ok(_) => true,
|
||||
Err(f) => {
|
||||
if f.kind() == ::std::io::ErrorKind::BrokenPipe {
|
||||
false
|
||||
} else {
|
||||
panic!("{}", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($fd:expr) => (
|
||||
match $fd.flush() {
|
||||
Ok(_) => true,
|
||||
Err(f) => {
|
||||
if f.kind() == ::std::io::ErrorKind::BrokenPipe {
|
||||
false
|
||||
} else {
|
||||
panic!("{}", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! safe_write(
|
||||
($fd:expr, $($args:tt)+) => (
|
||||
match write!($fd, $($args)+) {
|
||||
Ok(_) => {}
|
||||
Err(f) => panic!(f.to_string())
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! safe_writeln(
|
||||
($fd:expr, $($args:tt)+) => (
|
||||
match writeln!($fd, $($args)+) {
|
||||
Ok(_) => {}
|
||||
Err(f) => panic!(f.to_string())
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! safe_unwrap(
|
||||
($exp:expr) => (
|
||||
match $exp {
|
||||
Ok(m) => m,
|
||||
Err(f) => crash!(1, "{}", f.to_string())
|
||||
}
|
||||
)
|
||||
);
|
35
src/uucore/parse_time.rs
Normal file
35
src/uucore/parse_time.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Arcterus <arcterus@mail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
pub fn from_str(string: &str) -> Result<f64, String> {
|
||||
let len = string.len();
|
||||
if len == 0 {
|
||||
return Err("empty string".to_string())
|
||||
}
|
||||
let slice = &string[..len - 1];
|
||||
let (numstr, times) = match string.chars().next_back().unwrap() {
|
||||
's' | 'S' => (slice, 1),
|
||||
'm' | 'M' => (slice, 60),
|
||||
'h' | 'H' => (slice, 60 * 60),
|
||||
'd' | 'D' => (slice, 60 * 60 * 24),
|
||||
val => {
|
||||
if !val.is_alphabetic() {
|
||||
(string, 1)
|
||||
} else if string == "inf" || string == "infinity" {
|
||||
("inf", 1)
|
||||
} else {
|
||||
return Err(format!("invalid time interval '{}'", string))
|
||||
}
|
||||
}
|
||||
};
|
||||
match numstr.parse::<f64>() {
|
||||
Ok(m) => Ok(m * times as f64),
|
||||
Err(e) => Err(format!("invalid time interval '{}': {}", string, e))
|
||||
}
|
||||
}
|
140
src/uucore/process.rs
Normal file
140
src/uucore/process.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Maciej Dziardziel <fiedzia@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use libc::{c_int, pid_t};
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::process::Child;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
use std::thread;
|
||||
use time::{Duration, get_time};
|
||||
use std::time::Duration as StdDuration;
|
||||
|
||||
// This is basically sys::unix::process::ExitStatus
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum ExitStatus {
|
||||
Code(i32),
|
||||
Signal(i32),
|
||||
}
|
||||
|
||||
impl ExitStatus {
|
||||
fn from_status(status: c_int) -> ExitStatus {
|
||||
if status & 0x7F != 0 { // WIFSIGNALED(status)
|
||||
ExitStatus::Signal(status & 0x7F)
|
||||
} else {
|
||||
ExitStatus::Code(status & 0xFF00 >> 8)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn success(&self) -> bool {
|
||||
match *self {
|
||||
ExitStatus::Code(code) => code == 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code(&self) -> Option<i32> {
|
||||
match *self {
|
||||
ExitStatus::Code(code) => Some(code),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signal(&self) -> Option<i32> {
|
||||
match *self {
|
||||
ExitStatus::Signal(code) => Some(code),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExitStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ExitStatus::Code(code) => write!(f, "exit code: {}", code),
|
||||
ExitStatus::Signal(code) => write!(f, "exit code: {}", code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Missing methods for Child objects
|
||||
pub trait ChildExt {
|
||||
/// Send a signal to a Child process.
|
||||
fn send_signal(&mut self, signal: usize) -> io::Result<()>;
|
||||
|
||||
/// Wait for a process to finish or return after the specified duration.
|
||||
fn wait_or_timeout(&mut self, timeout: f64) -> io::Result<Option<ExitStatus>>;
|
||||
}
|
||||
|
||||
impl ChildExt for Child {
|
||||
fn send_signal(&mut self, signal: usize) -> io::Result<()> {
|
||||
if unsafe { libc::kill(self.id() as pid_t,
|
||||
signal as i32) } != 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_or_timeout(&mut self, timeout: f64) -> io::Result<Option<ExitStatus>> {
|
||||
// The result will be written to that Option, protected by a Mutex
|
||||
// Then the Condvar will be signaled
|
||||
let state = Arc::new((
|
||||
Mutex::new(Option::None::<io::Result<ExitStatus>>),
|
||||
Condvar::new(),
|
||||
));
|
||||
|
||||
// Start the waiting thread
|
||||
let state_th = state.clone();
|
||||
let pid_th = self.id();
|
||||
thread::spawn(move || {
|
||||
let &(ref lock_th, ref cvar_th) = &*state_th;
|
||||
// Child::wait() would need a &mut to self, can't use that...
|
||||
// use waitpid() directly, with our own ExitStatus
|
||||
let mut status: c_int = 0;
|
||||
let r = unsafe { libc::waitpid(pid_th as i32, &mut status, 0) };
|
||||
// Fill the Option and notify on the Condvar
|
||||
let mut exitstatus_th = lock_th.lock().unwrap();
|
||||
if r != pid_th as c_int {
|
||||
*exitstatus_th = Some(Err(io::Error::last_os_error()));
|
||||
} else {
|
||||
let s = ExitStatus::from_status(status);
|
||||
*exitstatus_th = Some(Ok(s));
|
||||
}
|
||||
cvar_th.notify_one();
|
||||
});
|
||||
|
||||
// Main thread waits
|
||||
let &(ref lock, ref cvar) = &*state;
|
||||
let mut exitstatus = lock.lock().unwrap();
|
||||
// Condvar::wait_timeout_ms() can wake too soon, in this case wait again
|
||||
let target = get_time() +
|
||||
Duration::seconds(timeout as i64) +
|
||||
Duration::nanoseconds((timeout * 1.0e-6) as i64);
|
||||
while exitstatus.is_none() {
|
||||
let now = get_time();
|
||||
if now >= target {
|
||||
return Ok(None)
|
||||
}
|
||||
let ms = (target - get_time()).num_milliseconds() as u32;
|
||||
exitstatus = cvar.wait_timeout(exitstatus, StdDuration::new(0, ms*1000)).unwrap().0;
|
||||
}
|
||||
|
||||
// Turn Option<Result<ExitStatus>> into Result<Option<ExitStatus>>
|
||||
match exitstatus.take() {
|
||||
Some(r) => match r {
|
||||
Ok(s) => Ok(Some(s)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
None => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
156
src/uucore/signals.rs
Normal file
156
src/uucore/signals.rs
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Maciej Dziardziel <fiedzia@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
pub static DEFAULT_SIGNAL:usize= 15;
|
||||
|
||||
|
||||
pub struct Signal<'a> { pub name:&'a str, pub value: usize}
|
||||
|
||||
/*
|
||||
|
||||
Linux Programmer's Manual
|
||||
|
||||
1 HUP 2 INT 3 QUIT 4 ILL 5 TRAP 6 ABRT 7 BUS
|
||||
8 FPE 9 KILL 10 USR1 11 SEGV 12 USR2 13 PIPE 14 ALRM
|
||||
15 TERM 16 STKFLT 17 CHLD 18 CONT 19 STOP 20 TSTP 21 TTIN
|
||||
22 TTOU 23 URG 24 XCPU 25 XFSZ 26 VTALRM 27 PROF 28 WINCH
|
||||
29 POLL 30 PWR 31 SYS
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub static ALL_SIGNALS:[Signal<'static>; 31] = [
|
||||
Signal{ name: "HUP", value:1 },
|
||||
Signal{ name: "INT", value:2 },
|
||||
Signal{ name: "QUIT", value:3 },
|
||||
Signal{ name: "ILL", value:4 },
|
||||
Signal{ name: "TRAP", value:5 },
|
||||
Signal{ name: "ABRT", value:6 },
|
||||
Signal{ name: "BUS", value:7 },
|
||||
Signal{ name: "FPE", value:8 },
|
||||
Signal{ name: "KILL", value:9 },
|
||||
Signal{ name: "USR1", value:10 },
|
||||
Signal{ name: "SEGV", value:11 },
|
||||
Signal{ name: "USR2", value:12 },
|
||||
Signal{ name: "PIPE", value:13 },
|
||||
Signal{ name: "ALRM", value:14 },
|
||||
Signal{ name: "TERM", value:15 },
|
||||
Signal{ name: "STKFLT", value:16 },
|
||||
Signal{ name: "CHLD", value:17 },
|
||||
Signal{ name: "CONT", value:18 },
|
||||
Signal{ name: "STOP", value:19 },
|
||||
Signal{ name: "TSTP", value:20 },
|
||||
Signal{ name: "TTIN", value:21 },
|
||||
Signal{ name: "TTOU", value:22 },
|
||||
Signal{ name: "URG", value:23 },
|
||||
Signal{ name: "XCPU", value:24 },
|
||||
Signal{ name: "XFSZ", value:25 },
|
||||
Signal{ name: "VTALRM", value:26 },
|
||||
Signal{ name: "PROF", value:27 },
|
||||
Signal{ name: "WINCH", value:28 },
|
||||
Signal{ name: "POLL", value:29 },
|
||||
Signal{ name: "PWR", value:30 },
|
||||
Signal{ name: "SYS", value:31 },
|
||||
];
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/signal.3.html
|
||||
|
||||
|
||||
No Name Default Action Description
|
||||
1 SIGHUP terminate process terminal line hangup
|
||||
2 SIGINT terminate process interrupt program
|
||||
3 SIGQUIT create core image quit program
|
||||
4 SIGILL create core image illegal instruction
|
||||
5 SIGTRAP create core image trace trap
|
||||
6 SIGABRT create core image abort program (formerly SIGIOT)
|
||||
7 SIGEMT create core image emulate instruction executed
|
||||
8 SIGFPE create core image floating-point exception
|
||||
9 SIGKILL terminate process kill program
|
||||
10 SIGBUS create core image bus error
|
||||
11 SIGSEGV create core image segmentation violation
|
||||
12 SIGSYS create core image non-existent system call invoked
|
||||
13 SIGPIPE terminate process write on a pipe with no reader
|
||||
14 SIGALRM terminate process real-time timer expired
|
||||
15 SIGTERM terminate process software termination signal
|
||||
16 SIGURG discard signal urgent condition present on socket
|
||||
17 SIGSTOP stop process stop (cannot be caught or ignored)
|
||||
18 SIGTSTP stop process stop signal generated from keyboard
|
||||
19 SIGCONT discard signal continue after stop
|
||||
20 SIGCHLD discard signal child status has changed
|
||||
21 SIGTTIN stop process background read attempted from control terminal
|
||||
22 SIGTTOU stop process background write attempted to control terminal
|
||||
23 SIGIO discard signal I/O is possible on a descriptor (see fcntl(2))
|
||||
24 SIGXCPU terminate process cpu time limit exceeded (see setrlimit(2))
|
||||
25 SIGXFSZ terminate process file size limit exceeded (see setrlimit(2))
|
||||
26 SIGVTALRM terminate process virtual time alarm (see setitimer(2))
|
||||
27 SIGPROF terminate process profiling timer alarm (see setitimer(2))
|
||||
28 SIGWINCH discard signal Window size change
|
||||
29 SIGINFO discard signal status request from keyboard
|
||||
30 SIGUSR1 terminate process User defined signal 1
|
||||
31 SIGUSR2 terminate process User defined signal 2
|
||||
|
||||
*/
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||
pub static ALL_SIGNALS:[Signal<'static>; 31] = [
|
||||
Signal{ name: "HUP", value:1 },
|
||||
Signal{ name: "INT", value:2 },
|
||||
Signal{ name: "QUIT", value:3 },
|
||||
Signal{ name: "ILL", value:4 },
|
||||
Signal{ name: "TRAP", value:5 },
|
||||
Signal{ name: "ABRT", value:6 },
|
||||
Signal{ name: "EMT", value:7 },
|
||||
Signal{ name: "FPE", value:8 },
|
||||
Signal{ name: "KILL", value:9 },
|
||||
Signal{ name: "BUS", value:10 },
|
||||
Signal{ name: "SEGV", value:11 },
|
||||
Signal{ name: "SYS", value:12 },
|
||||
Signal{ name: "PIPE", value:13 },
|
||||
Signal{ name: "ALRM", value:14 },
|
||||
Signal{ name: "TERM", value:15 },
|
||||
Signal{ name: "URG", value:16 },
|
||||
Signal{ name: "STOP", value:17 },
|
||||
Signal{ name: "TSTP", value:18 },
|
||||
Signal{ name: "CONT", value:19 },
|
||||
Signal{ name: "CHLD", value:20 },
|
||||
Signal{ name: "TTIN", value:21 },
|
||||
Signal{ name: "TTOU", value:22 },
|
||||
Signal{ name: "IO", value:23 },
|
||||
Signal{ name: "XCPU", value:24 },
|
||||
Signal{ name: "XFSZ", value:25 },
|
||||
Signal{ name: "VTALRM", value:26 },
|
||||
Signal{ name: "PROF", value:27 },
|
||||
Signal{ name: "WINCH", value:28 },
|
||||
Signal{ name: "INFO", value:29 },
|
||||
Signal{ name: "USR1", value:30 },
|
||||
Signal{ name: "USR2", value:31 },
|
||||
];
|
||||
|
||||
pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option<usize> {
|
||||
if signal_name_or_value == "0" {
|
||||
return Some(0);
|
||||
}
|
||||
for signal in ALL_SIGNALS.iter() {
|
||||
let long_name = format!("SIG{}", signal.name);
|
||||
if signal.name == signal_name_or_value || (signal_name_or_value == signal.value.to_string()) || (long_name == signal_name_or_value) {
|
||||
return Some(signal.value);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_signal(num: usize) -> bool {
|
||||
num < ALL_SIGNALS.len()
|
||||
}
|
125
src/uucore/utmpx.rs
Normal file
125
src/uucore/utmpx.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
#![allow(dead_code, non_camel_case_types)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
pub use self::utmpx::{DEFAULT_FILE,USER_PROCESS,BOOT_TIME,c_utmp};
|
||||
#[cfg(target_os = "linux")]
|
||||
mod utmpx {
|
||||
use super::libc;
|
||||
|
||||
pub static DEFAULT_FILE: &'static str = "/var/run/utmp";
|
||||
|
||||
pub const UT_LINESIZE: usize = 32;
|
||||
pub const UT_NAMESIZE: usize = 32;
|
||||
pub const UT_IDSIZE: usize = 4;
|
||||
pub const UT_HOSTSIZE: usize = 256;
|
||||
|
||||
pub const EMPTY: libc::c_short = 0;
|
||||
pub const RUN_LVL: libc::c_short = 1;
|
||||
pub const BOOT_TIME: libc::c_short = 2;
|
||||
pub const NEW_TIME: libc::c_short = 3;
|
||||
pub const OLD_TIME: libc::c_short = 4;
|
||||
pub const INIT_PROCESS: libc::c_short = 5;
|
||||
pub const LOGIN_PROCESS: libc::c_short = 6;
|
||||
pub const USER_PROCESS: libc::c_short = 7;
|
||||
pub const DEAD_PROCESS: libc::c_short = 8;
|
||||
pub const ACCOUNTING: libc::c_short = 9;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct c_exit_status {
|
||||
pub e_termination: libc::c_short,
|
||||
pub e_exit: libc::c_short,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct c_utmp {
|
||||
pub ut_type: libc::c_short,
|
||||
pub ut_pid: libc::pid_t,
|
||||
pub ut_line: [libc::c_char; UT_LINESIZE],
|
||||
pub ut_id: [libc::c_char; UT_IDSIZE],
|
||||
|
||||
pub ut_user: [libc::c_char; UT_NAMESIZE],
|
||||
pub ut_host: [libc::c_char; UT_HOSTSIZE],
|
||||
pub ut_exit: c_exit_status,
|
||||
pub ut_session: libc::c_long,
|
||||
pub ut_tv: libc::timeval,
|
||||
|
||||
pub ut_addr_v6: [libc::int32_t; 4],
|
||||
pub __unused: [libc::c_char; 20],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod utmpx {
|
||||
use super::libc;
|
||||
|
||||
pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";
|
||||
|
||||
pub const UT_LINESIZE: usize = 32;
|
||||
pub const UT_NAMESIZE: usize = 256;
|
||||
pub const UT_IDSIZE: usize = 4;
|
||||
pub const UT_HOSTSIZE: usize = 256;
|
||||
|
||||
pub const EMPTY: libc::c_short = 0;
|
||||
pub const RUN_LVL: libc::c_short = 1;
|
||||
pub const BOOT_TIME: libc::c_short = 2;
|
||||
pub const OLD_TIME: libc::c_short = 3;
|
||||
pub const NEW_TIME: libc::c_short = 4;
|
||||
pub const INIT_PROCESS: libc::c_short = 5;
|
||||
pub const LOGIN_PROCESS: libc::c_short = 6;
|
||||
pub const USER_PROCESS: libc::c_short = 7;
|
||||
pub const DEAD_PROCESS: libc::c_short = 8;
|
||||
pub const ACCOUNTING: libc::c_short = 9;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct c_exit_status {
|
||||
pub e_termination: libc::c_short,
|
||||
pub e_exit: libc::c_short,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct c_utmp {
|
||||
pub ut_user: [libc::c_char; UT_NAMESIZE],
|
||||
pub ut_id: [libc::c_char; UT_IDSIZE],
|
||||
pub ut_line: [libc::c_char; UT_LINESIZE],
|
||||
pub ut_pid: libc::pid_t,
|
||||
pub ut_type: libc::c_short,
|
||||
pub ut_tv: libc::timeval,
|
||||
pub ut_host: [libc::c_char; UT_HOSTSIZE],
|
||||
pub __unused: [libc::c_char; 16]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
mod utmpx {
|
||||
use super::libc;
|
||||
|
||||
pub static DEFAULT_FILE : &'static str = "";
|
||||
|
||||
pub const UT_LINESIZE : usize = 16;
|
||||
pub const UT_NAMESIZE : usize = 32;
|
||||
pub const UT_IDSIZE : usize = 8;
|
||||
pub const UT_HOSTSIZE : usize = 128;
|
||||
|
||||
pub const EMPTY : libc::c_short = 0;
|
||||
pub const BOOT_TIME : libc::c_short = 1;
|
||||
pub const OLD_TIME : libc::c_short = 2;
|
||||
pub const NEW_TIME : libc::c_short = 3;
|
||||
pub const USER_PROCESS : libc::c_short = 4;
|
||||
pub const INIT_PROCESS : libc::c_short = 5;
|
||||
pub const LOGIN_PROCESS : libc::c_short = 6;
|
||||
pub const DEAD_PROCESS : libc::c_short = 7;
|
||||
pub const SHUTDOWN_TIME : libc::c_short = 8;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct c_utmp {
|
||||
pub ut_type : libc::c_short,
|
||||
pub ut_tv : libc::timeval,
|
||||
pub ut_id : [libc::c_char; UT_IDSIZE],
|
||||
pub ut_pid : libc::pid_t,
|
||||
pub ut_user : [libc::c_char; UT_NAMESIZE],
|
||||
pub ut_line : [libc::c_char; UT_LINESIZE],
|
||||
pub ut_host : [libc::c_char; UT_HOSTSIZE],
|
||||
pub ut_spare : [libc::c_char; 64],
|
||||
}
|
||||
}
|
35
src/uucore/wide.rs
Normal file
35
src/uucore/wide.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Peter Atashian <retep998@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
pub trait ToWide {
|
||||
fn to_wide(&self) -> Vec<u16>;
|
||||
fn to_wide_null(&self) -> Vec<u16>;
|
||||
}
|
||||
impl<T> ToWide for T where T: AsRef<OsStr> {
|
||||
fn to_wide(&self) -> Vec<u16> {
|
||||
self.as_ref().encode_wide().collect()
|
||||
}
|
||||
fn to_wide_null(&self) -> Vec<u16> {
|
||||
self.as_ref().encode_wide().chain(Some(0)).collect()
|
||||
}
|
||||
}
|
||||
pub trait FromWide {
|
||||
fn from_wide(wide: &[u16]) -> Self;
|
||||
fn from_wide_null(wide: &[u16]) -> Self;
|
||||
}
|
||||
impl FromWide for String {
|
||||
fn from_wide(wide: &[u16]) -> String {
|
||||
OsString::from_wide(wide).to_string_lossy().into_owned()
|
||||
}
|
||||
fn from_wide_null(wide: &[u16]) -> String {
|
||||
let len = wide.iter().take_while(|&&c| c != 0).count();
|
||||
OsString::from_wide(&wide[..len]).to_string_lossy().into_owned()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue