Repository: uutils/procps
Pull request #218 (2024-09-15 10:06:01)

free: Fix free: `--si` doesn't work without `--human`
Fix: #216 


Patch:
diff --git a/src/uu/free/src/free.rs b/src/uu/free/src/free.rs
index a253577..74b16a6 100644
--- a/src/uu/free/src/free.rs
+++ b/src/uu/free/src/free.rs
@@ -230,7 +230,7 @@ pub fn uu_app() -> Command {
             arg!(   --tebi   "show output in tebibytes").action(ArgAction::SetTrue),
             arg!(   --pebi   "show output in pebibytes").action(ArgAction::SetTrue),
             arg!(-h --human  "show human-readable output").action(ArgAction::SetTrue),
-            arg!(   --si     "use powers of 1000 not 1024").action(ArgAction::SetFalse),
+            arg!(   --si     "use powers of 1000 not 1024").action(ArgAction::SetTrue),
             arg!(-l --lohi   "show detailed low and high memory statistics")
                 .action(ArgAction::SetTrue),
             arg!(-t --total "show total for RAM + swap").action(ArgAction::SetTrue),
@@ -418,7 +418,7 @@ fn construct_committed_str(mem_info: &MemInfo, n2s: &dyn Fn(u64) -> String) -> S
 
 // Here's the `-h` `--human` flag processing logic
 fn humanized(kib: u64, si: bool) -> String {
-    let binding = ByteSize::kib(kib).to_string_as(si);
+    let binding = ByteSize::kib(kib).to_string_as(!si);
     let split: Vec<&str> = binding.split(' ').collect();
 
     // TODO: finish the logic of automatic scale.
@@ -433,18 +433,30 @@ fn humanized(kib: u64, si: bool) -> String {
 }
 
 fn detect_unit(arg: &ArgMatches) -> fn(u64) -> u64 {
+    let si = arg.get_flag("si");
     match arg {
         _ if arg.get_flag("bytes") => |kib: u64| ByteSize::kib(kib).0,
-        _ if arg.get_flag("kilo") => |kib: u64| ByteSize::kib(kib).0 / KB,
-        _ if arg.get_flag("mega") => |kib: u64| ByteSize::kib(kib).0 / MB,
-        _ if arg.get_flag("giga") => |kib: u64| ByteSize::kib(kib).0 / GB,
-        _ if arg.get_flag("tera") => |kib: u64| ByteSize::kib(kib).0 / TB,
-        _ if arg.get_flag("peta") => |kib: u64| ByteSize::kib(kib).0 / PB,
+        _ if arg.get_flag("kilo") || (si && arg.get_flag("kibi")) => {
+            |kib: u64| ByteSize::kib(kib).0 / KB
+        }
+        _ if arg.get_flag("mega") || (si && arg.get_flag("mebi")) => {
+            |kib: u64| ByteSize::kib(kib).0 / MB
+        }
+        _ if arg.get_flag("giga") || (si && arg.get_flag("gibi")) => {
+            |kib: u64| ByteSize::kib(kib).0 / GB
+        }
+        _ if arg.get_flag("tera") || (si && arg.get_flag("tebi")) => {
+            |kib: u64| ByteSize::kib(kib).0 / TB
+        }
+        _ if arg.get_flag("peta") || (si && arg.get_flag("pebi")) => {
+            |kib: u64| ByteSize::kib(kib).0 / PB
+        }
         _ if arg.get_flag("kibi") => |kib: u64| ByteSize::kib(kib).0 / KIB,
         _ if arg.get_flag("mebi") => |kib: u64| ByteSize::kib(kib).0 / MIB,
         _ if arg.get_flag("gibi") => |kib: u64| ByteSize::kib(kib).0 / GIB,
         _ if arg.get_flag("tebi") => |kib: u64| ByteSize::kib(kib).0 / TIB,
         _ if arg.get_flag("pebi") => |kib: u64| ByteSize::kib(kib).0 / PIB,
+        _ if si => |kib: u64| ByteSize::kib(kib).0 / KB,
         _ => |kib: u64| kib,
     }
 }


Test patch:
diff --git a/tests/by-util/test_free.rs b/tests/by-util/test_free.rs
index 33e13e5..fcd6a86 100644
--- a/tests/by-util/test_free.rs
+++ b/tests/by-util/test_free.rs
@@ -147,6 +147,58 @@ fn test_seconds_zero() {
     }
 }
 
+#[test]
+fn test_unit() {
+    fn extract_total(re: &Regex, output: &str) -> u64 {
+        re.captures(output)
+            .unwrap()
+            .get(1)
+            .unwrap()
+            .as_str()
+            .parse::<u64>()
+            .unwrap()
+    }
+
+    let kibi_output = new_ucmd!().succeeds().stdout_move_str();
+    let total_mem_re = Regex::new(r"Mem:\s+(\d{1,12})").unwrap();
+    let total_swap_re = Regex::new(r"Swap:\s+(\d{1,12})").unwrap();
+    let total_mem_bytes = extract_total(&total_mem_re, &kibi_output) * 1024;
+    let total_swap_bytes = extract_total(&total_swap_re, &kibi_output) * 1024;
+
+    let base: u64 = 1024;
+    let base_si: u64 = 1000;
+    for (args, divisor) in vec![
+        (vec!["--kilo"], base_si),
+        (vec!["--mega"], base_si.pow(2)),
+        (vec!["--giga"], base_si.pow(3)),
+        (vec!["--tera"], base_si.pow(4)),
+        (vec!["--peta"], base_si.pow(5)),
+        (vec!["--kilo", "--si"], base_si),
+        (vec!["--mega", "--si"], base_si.pow(2)),
+        (vec!["--giga", "--si"], base_si.pow(3)),
+        (vec!["--tera", "--si"], base_si.pow(4)),
+        (vec!["--peta", "--si"], base_si.pow(5)),
+        (vec!["--kibi"], base),
+        (vec!["--mebi"], base.pow(2)),
+        (vec!["--gibi"], base.pow(3)),
+        (vec!["--tebi"], base.pow(4)),
+        (vec!["--pebi"], base.pow(5)),
+        (vec!["--kibi", "--si"], base_si),
+        (vec!["--mebi", "--si"], base_si.pow(2)),
+        (vec!["--gibi", "--si"], base_si.pow(3)),
+        (vec!["--tebi", "--si"], base_si.pow(4)),
+        (vec!["--pebi", "--si"], base_si.pow(5)),
+        (vec![], base),
+        (vec!["--si"], base_si),
+    ] {
+        let output = new_ucmd!().args(&args).succeeds().stdout_move_str();
+        let total_mem = extract_total(&total_mem_re, &output);
+        let total_swap = extract_total(&total_swap_re, &output);
+        assert!(total_mem == total_mem_bytes / divisor);
+        assert!(total_swap == total_swap_bytes / divisor);
+    }
+}
+
 fn assert_default_format(s: &str) {
     let header_pattern = r"^ {15}total {8}used {8}free {6}shared {2}buff/cache {3}available$";
     let mem_pattern = r"^Mem:( +\d+){6}$";