From 6095dfee66749df7f05d6633d8993f230d5ac82f Mon Sep 17 00:00:00 2001 From: Joseph Crail Date: Mon, 23 Nov 2015 20:00:51 -0500 Subject: [PATCH] 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. --- src/uucore/Cargo.toml | 11 ++ src/uucore/c_types.rs | 240 +++++++++++++++++++++++++++++++++++++++ src/uucore/fs.rs | 144 +++++++++++++++++++++++ src/uucore/lib.rs | 14 +++ src/uucore/macros.rs | 220 +++++++++++++++++++++++++++++++++++ src/uucore/parse_time.rs | 35 ++++++ src/uucore/process.rs | 140 +++++++++++++++++++++++ src/uucore/signals.rs | 156 +++++++++++++++++++++++++ src/uucore/utmpx.rs | 125 ++++++++++++++++++++ src/uucore/wide.rs | 35 ++++++ 10 files changed, 1120 insertions(+) create mode 100644 src/uucore/Cargo.toml create mode 100644 src/uucore/c_types.rs create mode 100644 src/uucore/fs.rs create mode 100644 src/uucore/lib.rs create mode 100644 src/uucore/macros.rs create mode 100644 src/uucore/parse_time.rs create mode 100644 src/uucore/process.rs create mode 100644 src/uucore/signals.rs create mode 100644 src/uucore/utmpx.rs create mode 100644 src/uucore/wide.rs diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml new file mode 100644 index 000000000..65c4d6a01 --- /dev/null +++ b/src/uucore/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "uucore" +version = "0.0.1" +authors = [] + +[dependencies] +libc = "*" +time = "*" + +[lib] +path = "lib.rs" diff --git a/src/uucore/c_types.rs b/src/uucore/c_types.rs new file mode 100644 index 000000000..49e01d582 --- /dev/null +++ b/src/uucore/c_types.rs @@ -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) -> Option { + 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::().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 { + 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 { + let mut ngroups: c_int = 32; + let mut groups: Vec = 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, 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= 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, 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!(""); + } + } +} diff --git a/src/uucore/fs.rs b/src/uucore/fs.rs new file mode 100644 index 000000000..e0372b357 --- /dev/null +++ b/src/uucore/fs.rs @@ -0,0 +1,144 @@ +/* + * This file is part of the uutils coreutils package. + * + * (c) Joseph Crail + * + * 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; +} + +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(self) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CanonicalizeMode { + None, + Normal, + Existing, + Missing, +} + +fn resolve>(original: P) -> Result { + 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>(original: P, can_mode: CanonicalizeMode) -> Result { + // 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) +} diff --git a/src/uucore/lib.rs b/src/uucore/lib.rs new file mode 100644 index 000000000..adbe7fe80 --- /dev/null +++ b/src/uucore/lib.rs @@ -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; diff --git a/src/uucore/macros.rs b/src/uucore/macros.rs new file mode 100644 index 000000000..89fa1c2a6 --- /dev/null +++ b/src/uucore/macros.rs @@ -0,0 +1,220 @@ +/* + * This file is part of the uutils coreutils package. + * + * (c) Arcterus + * + * 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()) + } + ) +); diff --git a/src/uucore/parse_time.rs b/src/uucore/parse_time.rs new file mode 100644 index 000000000..608601fe1 --- /dev/null +++ b/src/uucore/parse_time.rs @@ -0,0 +1,35 @@ +/* + * This file is part of the uutils coreutils package. + * + * (c) Arcterus + * + * 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 { + 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::() { + Ok(m) => Ok(m * times as f64), + Err(e) => Err(format!("invalid time interval '{}': {}", string, e)) + } +} diff --git a/src/uucore/process.rs b/src/uucore/process.rs new file mode 100644 index 000000000..d5ac317ea --- /dev/null +++ b/src/uucore/process.rs @@ -0,0 +1,140 @@ +/* + * This file is part of the uutils coreutils package. + * + * (c) Maciej Dziardziel + * + * 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 { + match *self { + ExitStatus::Code(code) => Some(code), + _ => None, + } + } + + pub fn signal(&self) -> Option { + 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>; +} + +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> { + // 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::>), + 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> into Result> + match exitstatus.take() { + Some(r) => match r { + Ok(s) => Ok(Some(s)), + Err(e) => Err(e), + }, + None => panic!(), + } + } +} diff --git a/src/uucore/signals.rs b/src/uucore/signals.rs new file mode 100644 index 000000000..62db82f1b --- /dev/null +++ b/src/uucore/signals.rs @@ -0,0 +1,156 @@ +/* + * This file is part of the uutils coreutils package. + * + * (c) Maciej Dziardziel + * + * 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 { + 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() +} diff --git a/src/uucore/utmpx.rs b/src/uucore/utmpx.rs new file mode 100644 index 000000000..58bedc91a --- /dev/null +++ b/src/uucore/utmpx.rs @@ -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], + } +} diff --git a/src/uucore/wide.rs b/src/uucore/wide.rs new file mode 100644 index 000000000..189e84438 --- /dev/null +++ b/src/uucore/wide.rs @@ -0,0 +1,35 @@ +/* + * This file is part of the uutils coreutils package. + * + * (c) Peter Atashian + * + * 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; + fn to_wide_null(&self) -> Vec; +} +impl ToWide for T where T: AsRef { + fn to_wide(&self) -> Vec { + self.as_ref().encode_wide().collect() + } + fn to_wide_null(&self) -> Vec { + 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() + } +} \ No newline at end of file