From 22fb31b543a1e313765c480666144a30361e67ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Fri, 20 Dec 2024 13:03:07 +0100 Subject: [PATCH] Change `Process::Status#to_s` to hex format on Windows (#15285) Status numbers on Windows are usually represented in hexadecimal notation. This applies that for all large exit status values (above `UInt16::MAX`, inspired from https://cs.opensource.google/go/go/+/refs/tags/go1.23.4:src/os/exec_posix.go;l=117-121). This again improves usability because it's easier to interpret unknown status values and compare them with listings sharing the typical number format. In #15283 we already changed `Status#to_s` to print the name of known values. But not all status codes are named. --- spec/std/process/status_spec.cr | 16 ++++++++++++++++ src/process/status.cr | 16 +++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/spec/std/process/status_spec.cr b/spec/std/process/status_spec.cr index 124a2b58add4..5561b106613b 100644 --- a/spec/std/process/status_spec.cr +++ b/spec/std/process/status_spec.cr @@ -273,6 +273,14 @@ describe Process::Status do assert_prints Process::Status.new(Signal.new(126).value).to_s, "Signal[126]" end {% end %} + + {% if flag?(:win32) %} + it "hex format" do + assert_prints Process::Status.new(UInt16::MAX).to_s, "0x0000FFFF" + assert_prints Process::Status.new(0x01234567).to_s, "0x01234567" + assert_prints Process::Status.new(UInt32::MAX).to_s, "0xFFFFFFFF" + end + {% end %} end describe "#inspect" do @@ -302,5 +310,13 @@ describe Process::Status do assert_prints Process::Status.new(unknown_signal.value).inspect, "Process::Status[Signal[126]]" end {% end %} + + {% if flag?(:win32) %} + it "hex format" do + assert_prints Process::Status.new(UInt16::MAX).inspect, "Process::Status[0x0000FFFF]" + assert_prints Process::Status.new(0x01234567).inspect, "Process::Status[0x01234567]" + assert_prints Process::Status.new(UInt32::MAX).inspect, "Process::Status[0xFFFFFFFF]" + end + {% end %} end end diff --git a/src/process/status.cr b/src/process/status.cr index 0b7a3c827fb8..2bbcb175c033 100644 --- a/src/process/status.cr +++ b/src/process/status.cr @@ -294,7 +294,7 @@ class Process::Status if name = name_for_win32_exit_status io << "LibC::" << name else - @exit_status.to_s(io) + stringify_exit_status_windows(io) end {% else %} if signal = exit_signal? @@ -336,7 +336,7 @@ class Process::Status if name = name_for_win32_exit_status io << name else - @exit_status.to_s(io) + stringify_exit_status_windows(io) end {% else %} if signal = exit_signal? @@ -358,7 +358,7 @@ class Process::Status # A signal exit status prints the name of the `Signal` member (`HUP`, `INT`, etc.). def to_s : String {% if flag?(:win32) %} - name_for_win32_exit_status || @exit_status.to_s + name_for_win32_exit_status || String.build { |io| stringify_exit_status_windows(io) } {% else %} if signal = exit_signal? signal.member_name || signal.inspect @@ -367,4 +367,14 @@ class Process::Status end {% end %} end + + private def stringify_exit_status_windows(io) + # On Windows large status codes are typically expressed in hexadecimal + if @exit_status >= UInt16::MAX + io << "0x" + @exit_status.to_s(base: 16, upcase: true).rjust(io, 8, '0') + else + @exit_status.to_s(io) + end + end end