mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
@@ -34,6 +34,7 @@ unix = [
|
||||
"unlink",
|
||||
"uptime",
|
||||
"users",
|
||||
"who",
|
||||
]
|
||||
generic = [
|
||||
"base32",
|
||||
@@ -187,6 +188,7 @@ unlink = { optional=true, path="src/unlink" }
|
||||
uptime = { optional=true, path="src/uptime" }
|
||||
users = { optional=true, path="src/users" }
|
||||
wc = { optional=true, path="src/wc" }
|
||||
who = { optional=true, path="src/who" }
|
||||
whoami = { optional=true, path="src/whoami" }
|
||||
yes = { optional=true, path="src/yes" }
|
||||
|
||||
|
||||
@@ -129,7 +129,8 @@ UNIX_PROGS := \
|
||||
uname \
|
||||
unlink \
|
||||
uptime \
|
||||
users
|
||||
users \
|
||||
who
|
||||
|
||||
ifneq ($(OS),Windows_NT)
|
||||
PROGS := $(PROGS) $(UNIX_PROGS)
|
||||
@@ -195,7 +196,8 @@ TEST_PROGS := \
|
||||
unexpand \
|
||||
uniq \
|
||||
unlink \
|
||||
wc
|
||||
wc \
|
||||
who
|
||||
|
||||
TESTS := \
|
||||
$(sort $(filter $(UTILS),$(filter-out $(SKIP_UTILS),$(TEST_PROGS))))
|
||||
|
||||
@@ -170,7 +170,6 @@ To do
|
||||
- tail (not all features implemented)
|
||||
- test (not all features implemented)
|
||||
- uniq (a couple of missing options)
|
||||
- who
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
+2
-2
@@ -251,7 +251,7 @@ fn time_string(ut: &Utmpx) -> String {
|
||||
impl Pinky {
|
||||
fn print_entry(&self, ut: &Utmpx) {
|
||||
let mut pts_path = PathBuf::from("/dev");
|
||||
pts_path.push(ut.tty_device().as_ref());
|
||||
pts_path.push(ut.tty_device().as_str());
|
||||
|
||||
let mesg;
|
||||
let last_change;
|
||||
@@ -298,7 +298,7 @@ impl Pinky {
|
||||
print!(" {}", time_string(&ut));
|
||||
|
||||
if self.include_where && !ut.host().is_empty() {
|
||||
let ut_host = ut.host().into_owned();
|
||||
let ut_host = ut.host();
|
||||
let mut res = ut_host.split(':');
|
||||
let host = match res.next() {
|
||||
Some(_) => ut.canon_host().unwrap_or(ut_host.clone()),
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ fn exec(filename: &str) {
|
||||
let mut users = Utmpx::iter_all_records()
|
||||
.read_from(filename)
|
||||
.filter(|ut| ut.is_user_process())
|
||||
.map(|ut| ut.user().into_owned())
|
||||
.map(|ut| ut.user())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !users.is_empty() {
|
||||
|
||||
+38
-20
@@ -2,6 +2,14 @@
|
||||
//!
|
||||
//! **ONLY** support linux, macos and freebsd for the time being
|
||||
|
||||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// (c) Jian Zeng <anonymousknight96@gmail.com>
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
//
|
||||
|
||||
use super::libc;
|
||||
pub extern crate time;
|
||||
use self::time::{Tm, Timespec};
|
||||
@@ -9,8 +17,6 @@ use self::time::{Tm, Timespec};
|
||||
use ::std::io::Result as IOResult;
|
||||
use ::std::io::Error as IOError;
|
||||
use ::std::ptr;
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::ffi::CStr;
|
||||
use ::std::ffi::CString;
|
||||
|
||||
pub use self::ut::*;
|
||||
@@ -28,11 +34,10 @@ pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
|
||||
0
|
||||
}
|
||||
|
||||
macro_rules! bytes2cow {
|
||||
($name:expr) => (
|
||||
unsafe {
|
||||
CStr::from_ptr($name.as_ref().as_ptr()).to_string_lossy()
|
||||
}
|
||||
// In case the c_char array doesn' t end with NULL
|
||||
macro_rules! chars2string {
|
||||
($arr:expr) => (
|
||||
$arr.iter().take_while(|i| **i > 0).map(|&i| i as u8 as char).collect::<String>()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -138,26 +143,40 @@ impl Utmpx {
|
||||
self.inner.ut_pid as i32
|
||||
}
|
||||
/// A.K.A. ut.ut_id
|
||||
pub fn terminal_suffix(&self) -> Cow<str> {
|
||||
bytes2cow!(self.inner.ut_id)
|
||||
pub fn terminal_suffix(&self) -> String {
|
||||
chars2string!(self.inner.ut_id)
|
||||
}
|
||||
/// A.K.A. ut.ut_user
|
||||
pub fn user(&self) -> Cow<str> {
|
||||
bytes2cow!(self.inner.ut_user)
|
||||
pub fn user(&self) -> String {
|
||||
chars2string!(self.inner.ut_user)
|
||||
}
|
||||
/// A.K.A. ut.ut_host
|
||||
pub fn host(&self) -> Cow<str> {
|
||||
bytes2cow!(self.inner.ut_host)
|
||||
pub fn host(&self) -> String {
|
||||
chars2string!(self.inner.ut_host)
|
||||
}
|
||||
/// A.K.A. ut.ut_line
|
||||
pub fn tty_device(&self) -> Cow<str> {
|
||||
bytes2cow!(self.inner.ut_line)
|
||||
pub fn tty_device(&self) -> String {
|
||||
chars2string!(self.inner.ut_line)
|
||||
}
|
||||
/// A.K.A. ut.ut_tv
|
||||
pub fn login_time(&self) -> Tm {
|
||||
time::at(Timespec::new(self.inner.ut_tv.tv_sec as i64,
|
||||
self.inner.ut_tv.tv_usec as i32))
|
||||
}
|
||||
/// A.K.A. ut.ut_exit
|
||||
///
|
||||
/// Return (e_termination, e_exit)
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn exit_status(&self) -> (i16, i16) {
|
||||
(self.inner.ut_exit.e_termination, self.inner.ut_exit.e_exit)
|
||||
}
|
||||
/// A.K.A. ut.ut_exit
|
||||
///
|
||||
/// Return (0, 0) on Non-Linux platform
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn exit_status(&self) -> (i16, i16) {
|
||||
(0, 0)
|
||||
}
|
||||
/// Consumes the `Utmpx`, returning the underlying C struct utmpx
|
||||
pub fn into_inner(self) -> utmpx {
|
||||
self.inner
|
||||
@@ -170,6 +189,7 @@ impl Utmpx {
|
||||
pub fn canon_host(&self) -> IOResult<String> {
|
||||
const AI_CANONNAME: libc::c_int = 0x2;
|
||||
let host = self.host();
|
||||
let host = host.split(':').nth(0).unwrap();
|
||||
let hints = libc::addrinfo {
|
||||
ai_flags: AI_CANONNAME,
|
||||
ai_family: 0,
|
||||
@@ -180,7 +200,7 @@ impl Utmpx {
|
||||
ai_canonname: ptr::null_mut(),
|
||||
ai_next: ptr::null_mut(),
|
||||
};
|
||||
let c_host = CString::new(host.as_ref()).unwrap();
|
||||
let c_host = CString::new(host).unwrap();
|
||||
let mut res = ptr::null_mut();
|
||||
let status = unsafe {
|
||||
libc::getaddrinfo(c_host.as_ptr(),
|
||||
@@ -194,7 +214,7 @@ impl Utmpx {
|
||||
// says Darwin 7.9.0 getaddrinfo returns 0 but sets
|
||||
// res->ai_canonname to NULL.
|
||||
let ret = if info.ai_canonname.is_null() {
|
||||
Ok(String::from(host.as_ref()))
|
||||
Ok(String::from(host))
|
||||
} else {
|
||||
Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() })
|
||||
};
|
||||
@@ -236,9 +256,7 @@ impl Iterator for UtmpxIter {
|
||||
unsafe {
|
||||
let res = getutxent();
|
||||
if !res.is_null() {
|
||||
Some(Utmpx {
|
||||
inner: ptr::read(res as *const _)
|
||||
})
|
||||
Some(Utmpx { inner: ptr::read(res as *const _) })
|
||||
} else {
|
||||
endutxent();
|
||||
None
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "who"
|
||||
version = "0.0.1"
|
||||
authors = []
|
||||
|
||||
[lib]
|
||||
name = "uu_who"
|
||||
path = "who.rs"
|
||||
|
||||
[dependencies.uucore]
|
||||
path = "../uucore"
|
||||
default-features = false
|
||||
features = ["utmpx"]
|
||||
|
||||
[dependencies.clippy]
|
||||
version = "*"
|
||||
optional = true
|
||||
|
||||
[[bin]]
|
||||
name = "who"
|
||||
path = "main.rs"
|
||||
@@ -0,0 +1,5 @@
|
||||
extern crate uu_who;
|
||||
|
||||
fn main() {
|
||||
std::process::exit(uu_who::uumain(std::env::args().collect()));
|
||||
}
|
||||
+519
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,88 @@
|
||||
use common::util::*;
|
||||
|
||||
static UTIL_NAME: &'static str = "who";
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn test_count() {
|
||||
for opt in ["-q", "--count"].into_iter() {
|
||||
let scene = TestScenario::new(UTIL_NAME);
|
||||
let args = [*opt];
|
||||
scene.ucmd().args(&args).run().stdout_is(expected_result(&args));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn test_boot() {
|
||||
for opt in ["-b", "--boot"].into_iter() {
|
||||
let scene = TestScenario::new(UTIL_NAME);
|
||||
let args = [*opt];
|
||||
scene.ucmd().args(&args).run().stdout_is(expected_result(&args));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn test_heading() {
|
||||
for opt in ["-H"].into_iter() {
|
||||
let scene = TestScenario::new(UTIL_NAME);
|
||||
let args = [*opt];
|
||||
scene.ucmd().args(&args).run().stdout_is(expected_result(&args));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn test_short() {
|
||||
for opt in ["-s", "--short"].into_iter() {
|
||||
let scene = TestScenario::new(UTIL_NAME);
|
||||
let args = [*opt];
|
||||
scene.ucmd().args(&args).run().stdout_is(expected_result(&args));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn test_login() {
|
||||
for opt in ["-l", "--login"].into_iter() {
|
||||
let scene = TestScenario::new(UTIL_NAME);
|
||||
let args = [*opt];
|
||||
scene.ucmd().args(&args).run().stdout_is(expected_result(&args));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn test_m() {
|
||||
for opt in ["-m"].into_iter() {
|
||||
let scene = TestScenario::new(UTIL_NAME);
|
||||
let args = [*opt];
|
||||
scene.ucmd().args(&args).run().stdout_is(expected_result(&args));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn test_dead() {
|
||||
for opt in ["-d", "--dead"].into_iter() {
|
||||
let scene = TestScenario::new(UTIL_NAME);
|
||||
let args = [*opt];
|
||||
scene.ucmd().args(&args).run().stdout_is(expected_result(&args));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn test_all() {
|
||||
for opt in ["-a", "--all"].into_iter() {
|
||||
let scene = TestScenario::new(UTIL_NAME);
|
||||
let args = [*opt];
|
||||
scene.ucmd().args(&args).run().stdout_is(expected_result(&args));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn expected_result(args: &[&str]) -> String {
|
||||
TestScenario::new(UTIL_NAME).cmd_keepenv(UTIL_NAME).args(args).run().stdout
|
||||
}
|
||||
@@ -21,6 +21,7 @@ unix_only! {
|
||||
"stdbuf", test_stdbuf;
|
||||
"touch", test_touch;
|
||||
"unlink", test_unlink;
|
||||
"who", test_who;
|
||||
// Be aware of the trailing semicolon after the last item
|
||||
"stat", test_stat
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user