Repository: uutils/procps (rust)

Issue:
`w`: GNU w output header includes output from uptime
The header in GNU version contains the output from uptime as part of the header. 
```
 15:12:54 up  5:47,  1 user,  load average: 0.68, 0.61, 0.74
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
```

coreutils already has an implementation for uptime so the code for this already exists. 

Pull request description:
w: add uptime header
fix #60 

output like:

```text
$ cargo run -q w
 00:23:47  0:25,  0 user,  load average: 0.65, 0.50, 0.46
USER     TTY       LOGIN@   IDLE   JCPU   PCPU  WHAT
```

Patch:
diff --git a/src/uu/pgrep/src/process.rs b/src/uu/pgrep/src/process.rs
index 91b8a3b..50521b2 100644
--- a/src/uu/pgrep/src/process.rs
+++ b/src/uu/pgrep/src/process.rs
@@ -79,7 +79,7 @@ impl TryFrom<PathBuf> for Teletype {
         let f = |prefix: &str| {
             value
                 .iter()
-                .next_back()?
+                .last()?
                 .to_str()?
                 .strip_prefix(prefix)?
                 .parse::<u64>()
@@ -229,7 +229,7 @@ impl ProcessInformation {
         let pid = {
             value
                 .iter()
-                .next_back()
+                .last()
                 .ok_or(io::ErrorKind::Other)?
                 .to_str()
                 .ok_or(io::ErrorKind::InvalidData)?
diff --git a/src/uu/snice/src/snice.rs b/src/uu/snice/src/snice.rs
index f9a8e6b..8a6af84 100644
--- a/src/uu/snice/src/snice.rs
+++ b/src/uu/snice/src/snice.rs
@@ -213,7 +213,7 @@ fn construct_verbose_result(pids: &[u32], action_results: &[Option<ActionResult>
 
             let mut cmd = process
                 .exe()
-                .and_then(|it| it.iter().next_back())
+                .and_then(|it| it.iter().last())
                 .unwrap_or("?".as_ref());
             let cmd = cmd.to_str().unwrap();
 
diff --git a/src/uu/top/src/picker.rs b/src/uu/top/src/picker.rs
index beae924..f103e9b 100644
--- a/src/uu/top/src/picker.rs
+++ b/src/uu/top/src/picker.rs
@@ -193,7 +193,7 @@ fn command(pid: u32) -> String {
     };
 
     proc.exe()
-        .and_then(|it| it.iter().next_back())
+        .and_then(|it| it.iter().last())
         .map(|it| it.to_str().unwrap())
         .unwrap_or(&f(proc.cmd()))
         .into()
diff --git a/src/uu/top/src/top.rs b/src/uu/top/src/top.rs
index a182000..885ebd1 100644
--- a/src/uu/top/src/top.rs
+++ b/src/uu/top/src/top.rs
@@ -154,7 +154,7 @@ where
         input.chars().take(width).collect()
     } else {
         let mut result = String::from(&input);
-        result.extend(std::iter::repeat_n(' ', width - input.len()));
+        result.extend(std::iter::repeat(' ').take(width - input.len()));
         result
     }
 }
diff --git a/src/uu/vmstat/src/vmstat.rs b/src/uu/vmstat/src/vmstat.rs
index 79294bd..4fd6a31 100644
--- a/src/uu/vmstat/src/vmstat.rs
+++ b/src/uu/vmstat/src/vmstat.rs
@@ -23,7 +23,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
     let mut section: Vec<String> = vec![];
     let mut title: Vec<String> = vec![];
     let mut data: Vec<String> = vec![];
-    let mut data_len_excess = 0;
 
     #[cfg(target_os = "linux")]
     let func = [
@@ -39,7 +38,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
     let func: [ConcatFunc; 0] = [];
 
     func.iter()
-        .for_each(|f| f(&mut section, &mut title, &mut data, &mut data_len_excess));
+        .for_each(|f| f(&mut section, &mut title, &mut data));
 
     println!("{}", section.join(" "));
     println!("{}", title.join(" "));
@@ -48,26 +47,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
     Ok(())
 }
 
-type ConcatFunc = Box<dyn Fn(&mut Vec<String>, &mut Vec<String>, &mut Vec<String>, &mut usize)>;
+type ConcatFunc = Box<dyn Fn(&mut Vec<String>, &mut Vec<String>, &mut Vec<String>)>;
 
 #[allow(dead_code)]
-fn concat_helper(
-    func: impl Fn() -> (String, String, Vec<(usize, String)>) + 'static,
-) -> ConcatFunc {
+fn concat_helper(func: impl Fn() -> (String, String, String) + 'static) -> ConcatFunc {
     Box::from(
-        move |section: &mut Vec<String>,
-              title: &mut Vec<String>,
-              data: &mut Vec<String>,
-              data_len_excess: &mut usize| {
+        move |section: &mut Vec<String>, title: &mut Vec<String>, data: &mut Vec<String>| {
             let output = func();
             section.push(output.0);
             title.push(output.1);
-            output.2.iter().for_each(|(len, value)| {
-                let len = len - *data_len_excess;
-                let formatted_value = format!("{:>width$}", value, width = len);
-                *data_len_excess = formatted_value.len() - len;
-                data.push(formatted_value);
-            });
+            data.push(output.2);
         },
     )
 }
@@ -81,19 +70,19 @@ fn up_secs() -> f64 {
 }
 
 #[cfg(target_os = "linux")]
-fn get_process_info() -> (String, String, Vec<(usize, String)>) {
+fn get_process_info() -> (String, String, String) {
     let stat = procfs::KernelStats::current().unwrap();
     let runnable = stat.procs_running.unwrap_or_default();
     let blocked = stat.procs_blocked.unwrap_or_default();
     (
         "procs".into(),
         " r  b".into(),
-        vec![(2, format!("{}", runnable)), (2, format!("{}", blocked))],
+        format!("{:>2} {:>2}", runnable, blocked),
     )
 }
 
 #[cfg(target_os = "linux")]
-fn get_memory_info() -> (String, String, Vec<(usize, String)>) {
+fn get_memory_info() -> (String, String, String) {
     let memory_info = procfs::Meminfo::current().unwrap();
     let swap_used = (memory_info.swap_total - memory_info.swap_free) / 1024;
     let free = memory_info.mem_free / 1024;
@@ -103,17 +92,12 @@ fn get_memory_info() -> (String, String, Vec<(usize, String)>) {
     (
         "-----------memory----------".into(),
         "  swpd   free   buff  cache".into(),
-        vec![
-            (6, format!("{}", swap_used)),
-            (6, format!("{}", free)),
-            (6, format!("{}", buffer)),
-            (6, format!("{}", cache)),
-        ],
+        format!("{:>6} {:>6} {:>5} {:>6}", swap_used, free, buffer, cache),
     )
 }
 
 #[cfg(target_os = "linux")]
-fn get_swap_info() -> (String, String, Vec<(usize, String)>) {
+fn get_swap_info() -> (String, String, String) {
     let uptime = up_secs();
     let vmstat = procfs::vmstat().unwrap();
     let swap_in = vmstat.get("pswpin").unwrap();
@@ -121,15 +105,16 @@ fn get_swap_info() -> (String, String, Vec<(usize, String)>) {
     (
         "---swap--".into(),
         "  si   so".into(),
-        vec![
-            (4, format!("{:.0}", *swap_in as f64 / uptime)),
-            (4, format!("{:.0}", *swap_out as f64 / uptime)),
-        ],
+        format!(
+            "{:>2} {:>4}",
+            *swap_in as f64 / uptime,
+            *swap_out as f64 / uptime
+        ),
     )
 }
 
 #[cfg(target_os = "linux")]
-fn get_io_info() -> (String, String, Vec<(usize, String)>) {
+fn get_io_info() -> (String, String, String) {
     let uptime = up_secs();
     let vmstat = procfs::vmstat().unwrap();
     let read_bytes = vmstat.get("pgpgin").unwrap();
@@ -137,15 +122,16 @@ fn get_io_info() -> (String, String, Vec<(usize, String)>) {
     (
         "-----io----".into(),
         "   bi    bo".into(),
-        vec![
-            (5, format!("{:.0}", *read_bytes as f64 / uptime)),
-            (5, format!("{:.0}", *write_bytes as f64 / uptime)),
-        ],
+        format!(
+            "{:>5.0} {:>5.0}",
+            *read_bytes as f64 / uptime,
+            *write_bytes as f64 / uptime
+        ),
     )
 }
 
 #[cfg(target_os = "linux")]
-fn get_system_info() -> (String, String, Vec<(usize, String)>) {
+fn get_system_info() -> (String, String, String) {
     let uptime = up_secs();
     let stat = parse_proc_file("/proc/stat");
 
@@ -162,28 +148,30 @@ fn get_system_info() -> (String, String, Vec<(usize, String)>) {
     (
         "-system--".into(),
         "  in   cs".into(),
-        vec![
-            (4, format!("{:.0}", interrupts as f64 / uptime)),
-            (4, format!("{:.0}", context_switches as f64 / uptime)),
-        ],
+        format!(
+            "{:>4.0} {:>4.0}",
+            interrupts as f64 / uptime,
+            context_switches as f64 / uptime
+        ),
     )
 }
 
 #[cfg(target_os = "linux")]
-fn get_cpu_info() -> (String, String, Vec<(usize, String)>) {
+fn get_cpu_info() -> (String, String, String) {
     let cpu_load = CpuLoad::current();
 
     (
         "-------cpu-------".into(),
         "us sy id wa st gu".into(),
-        vec![
-            (2, format!("{:.0}", cpu_load.user)),
-            (2, format!("{:.0}", cpu_load.system)),
-            (2, format!("{:.0}", cpu_load.idle)),
-            (2, format!("{:.0}", cpu_load.io_wait)),
-            (2, format!("{:.0}", cpu_load.steal_time)),
-            (2, format!("{:.0}", cpu_load.guest)),
-        ],
+        format!(
+            "{:>2.0} {:>2.0} {:>2.0} {:>2.0} {:>2.0} {:>2.0}",
+            cpu_load.user,
+            cpu_load.system,
+            cpu_load.idle,
+            cpu_load.io_wait,
+            cpu_load.steal_time,
+            cpu_load.guest
+        ),
     )
 }
 
diff --git a/src/uu/w/Cargo.toml b/src/uu/w/Cargo.toml
index 6be5ccf..e8f9c23 100644
--- a/src/uu/w/Cargo.toml
+++ b/src/uu/w/Cargo.toml
@@ -12,7 +12,7 @@ keywords = ["acl", "uutils", "cross-platform", "cli", "utility"]
 categories = ["command-line-utilities"]
 
 [dependencies]
-uucore = { workspace = true, features = ["utmpx"] }
+uucore = { workspace = true, features = ["utmpx", "uptime"] }
 clap = { workspace = true }
 chrono = { workspace = true, default-features = false, features = [
   "clock",
diff --git a/src/uu/w/src/w.rs b/src/uu/w/src/w.rs
index 852b6b0..bacfe94 100644
--- a/src/uu/w/src/w.rs
+++ b/src/uu/w/src/w.rs
@@ -12,6 +12,9 @@ use libc::{sysconf, _SC_CLK_TCK};
 #[cfg(target_os = "linux")]
 use std::{collections::HashMap, fs, path::Path, time::SystemTime};
 use std::{process, time::Duration};
+use uucore::uptime::{
+    get_formated_uptime, get_formatted_loadavg, get_formatted_nusers, get_formatted_time,
+};
 #[cfg(target_os = "linux")]
 use uucore::utmpx::Utmpx;
 use uucore::{error::UResult, format_usage, help_about, help_usage};
@@ -184,6 +187,21 @@ fn fetch_user_info() -> Result<Vec<UserInfo>, std::io::Error> {
     Ok(user_info_list)
 }
 
+fn print_uptime() {
+    print!(" {} ", get_formatted_time());
+    if let Ok(uptime) = get_formated_uptime(None) {
+        print!("{}, ", uptime);
+    } else {
+        print!("up ???? days ??:??, ");
+    }
+
+    print!(" {}", get_formatted_nusers());
+    if let Ok(loadavg) = get_formatted_loadavg() {
+        print!(",  {}", loadavg);
+    }
+    println!();
+}
+
 #[cfg(any(target_os = "macos", target_os = "windows"))]
 fn fetch_user_info() -> Result<Vec<UserInfo>, std::io::Error> {
     Ok(Vec::new())
@@ -200,6 +218,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
     match fetch_user_info() {
         Ok(user_info) => {
             if !no_header {
+                print_uptime();
                 if short {
                     println!("{:<9}{:<9}{:<7}{:<}", "USER", "TTY", "IDLE", "WHAT");
                 } else {
diff --git a/src/uu/watch/src/watch.rs b/src/uu/watch/src/watch.rs
index b1e7d6b..6343c79 100644
--- a/src/uu/watch/src/watch.rs
+++ b/src/uu/watch/src/watch.rs
@@ -118,7 +118,6 @@ pub fn uu_app() -> Command {
                 .long("interval")
                 .help("Seconds to wait between updates")
                 .default_value("2")
-                .env("WATCH_INTERVAL")
                 .value_name("SECONDS"),
         )
         .arg(


Test patch:
diff --git a/tests/by-util/test_w.rs b/tests/by-util/test_w.rs
index 57cd8c2..41bbc76 100644
--- a/tests/by-util/test_w.rs
+++ b/tests/by-util/test_w.rs
@@ -26,6 +26,10 @@ fn test_no_header() {
 
         let result = cmd.stdout_str();
 
+        assert!(match result.lines().next() {
+            None => true,
+            Some(line) => !line.contains("user") && !line.contains("load average"),
+        });
         assert!(!result.contains("USER     TTY      LOGIN@   IDLE   JCPU   PCPU WHAT"));
     }
 }
@@ -41,7 +45,7 @@ fn test_option_short() {
 
     let cmd_output = cmd.stdout_str();
     let cmd_output_lines: Vec<&str> = cmd_output.split('\n').collect();
-    let line_output_header = cmd_output_lines[0];
+    let line_output_header = cmd_output_lines[1];
     let line_output_data_words: Vec<&str> = cmd_output_lines[1]
         .trim()
         .split(' ')
diff --git a/tests/by-util/test_watch.rs b/tests/by-util/test_watch.rs
index 4492625..9555c6f 100644
--- a/tests/by-util/test_watch.rs
+++ b/tests/by-util/test_watch.rs
@@ -14,12 +14,6 @@ const TRUE_CMD: &str = "%SYSTEMROOT%\\System32\\rundll32.exe";
 #[cfg(not(windows))]
 const TRUE_CMD: &str = "true";
 
-#[cfg(windows)]
-const ECHO_HELLO_CMD: &str = "echo | set /p dummyName=hello";
-
-#[cfg(not(windows))]
-const ECHO_HELLO_CMD: &str = "printf hello";
-
 #[test]
 fn test_invalid_arg() {
     new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
@@ -68,20 +62,3 @@ fn test_valid_interval_comma() {
         .no_stderr()
         .no_stdout();
 }
-
-#[test]
-fn test_interval_environment_variable() {
-    let mut p = new_ucmd!()
-        .arg(ECHO_HELLO_CMD)
-        .env("WATCH_INTERVAL", "0.3")
-        .run_no_wait();
-    // With 0.5 seconds runtime, the watched command is called twice if
-    // `WATCH_INTERVAL` (0.3 seconds) is taken into account, but only once if the default
-    // interval (2 seconds) is used.
-    p.make_assertion_with_delay(500).is_alive();
-    p.kill()
-        .make_assertion()
-        .with_all_output()
-        .no_stderr()
-        .stdout_is_bytes(b"hellohello");
-}
diff --git a/tests/common/util.rs b/tests/common/util.rs
index a269d87..cfd914f 100644
--- a/tests/common/util.rs
+++ b/tests/common/util.rs
@@ -1186,9 +1186,9 @@ impl TestScenario {
 
 /// A `UCommand` is a builder wrapping an individual Command that provides several additional features:
 /// 1. it has convenience functions that are more ergonomic to use for piping in stdin, spawning the command
-///    and asserting on the results.
+///       and asserting on the results.
 /// 2. it tracks arguments provided so that in test cases which may provide variations of an arg in loops
-///    the test failure can display the exact call which preceded an assertion failure.
+///     the test failure can display the exact call which preceded an assertion failure.
 /// 3. it provides convenience construction methods to set the Command uutils utility and temporary directory.
 ///
 /// Per default `UCommand` runs a command given as an argument in a shell, platform independently.