1313// See https://github.com/uutils/coreutils/pull/7289 for discussion.
1414
1515use crate :: error:: { UError , UResult } ;
16+ use crate :: locale:: { self , LocalizationError } ;
1617use crate :: translate;
1718use chrono:: Local ;
1819use libc:: time_t;
20+ use std:: cell:: Cell ;
1921use thiserror:: Error ;
2022
2123#[ derive( Debug , Error ) ]
@@ -36,6 +38,29 @@ impl UError for UptimeError {
3638 }
3739}
3840
41+ thread_local ! {
42+ static LOCALE_READY : Cell <bool > = Cell :: new( false ) ;
43+ }
44+
45+ fn ensure_uptime_locale ( ) {
46+ LOCALE_READY . with ( |ready| {
47+ if ready. get ( ) {
48+ return ;
49+ }
50+
51+ match locale:: setup_localization ( "uptime" ) {
52+ Ok ( ( ) ) => ready. set ( true ) ,
53+ Err ( LocalizationError :: Bundle ( msg) ) if msg. contains ( "already initialized" ) => {
54+ ready. set ( true ) ;
55+ }
56+ Err ( err) => {
57+ #[ cfg( debug_assertions) ]
58+ eprintln ! ( "uucore::uptime localization setup failed: {err}" ) ;
59+ }
60+ }
61+ } ) ;
62+ }
63+
3964/// Returns the formatted time string, e.g. "12:34:56"
4065pub fn get_formatted_time ( ) -> String {
4166 Local :: now ( ) . time ( ) . format ( "%H:%M:%S" ) . to_string ( )
@@ -52,6 +77,7 @@ pub fn get_formatted_time() -> String {
5277/// Returns a UResult with the uptime in seconds if successful, otherwise an UptimeError.
5378#[ cfg( target_os = "openbsd" ) ]
5479pub fn get_uptime ( _boot_time : Option < time_t > ) -> UResult < i64 > {
80+ ensure_uptime_locale ( ) ;
5581 use libc:: CLOCK_BOOTTIME ;
5682 use libc:: clock_gettime;
5783
@@ -91,6 +117,7 @@ pub fn get_uptime(_boot_time: Option<time_t>) -> UResult<i64> {
91117#[ cfg( unix) ]
92118#[ cfg( not( target_os = "openbsd" ) ) ]
93119pub fn get_uptime ( boot_time : Option < time_t > ) -> UResult < i64 > {
120+ ensure_uptime_locale ( ) ;
94121 use crate :: utmpx:: Utmpx ;
95122 use libc:: BOOT_TIME ;
96123 use std:: fs:: File ;
@@ -150,6 +177,7 @@ pub fn get_uptime(boot_time: Option<time_t>) -> UResult<i64> {
150177/// Returns a UResult with the uptime in seconds if successful, otherwise an UptimeError.
151178#[ cfg( windows) ]
152179pub fn get_uptime ( _boot_time : Option < time_t > ) -> UResult < i64 > {
180+ ensure_uptime_locale ( ) ;
153181 use windows_sys:: Win32 :: System :: SystemInformation :: GetTickCount ;
154182 // SAFETY: always return u32
155183 let uptime = unsafe { GetTickCount ( ) } ;
@@ -167,6 +195,7 @@ pub fn get_uptime(_boot_time: Option<time_t>) -> UResult<i64> {
167195/// Returns a UResult with the uptime in a human-readable format(e.g. "1 day, 3:45") if successful, otherwise an UptimeError.
168196#[ inline]
169197pub fn get_formatted_uptime ( boot_time : Option < time_t > ) -> UResult < String > {
198+ ensure_uptime_locale ( ) ;
170199 let up_secs = get_uptime ( boot_time) ?;
171200
172201 if up_secs < 0 {
@@ -307,6 +336,7 @@ pub fn get_nusers() -> usize {
307336/// e.g. "0 users", "1 user", "2 users"
308337#[ inline]
309338pub fn format_nusers ( n : usize ) -> String {
339+ ensure_uptime_locale ( ) ;
310340 translate ! (
311341 "uptime-user-count" ,
312342 "count" => n
@@ -320,6 +350,7 @@ pub fn format_nusers(n: usize) -> String {
320350/// e.g. "0 user", "1 user", "2 users"
321351#[ inline]
322352pub fn get_formatted_nusers ( ) -> String {
353+ ensure_uptime_locale ( ) ;
323354 #[ cfg( not( target_os = "openbsd" ) ) ]
324355 return format_nusers ( get_nusers ( ) ) ;
325356
@@ -335,6 +366,7 @@ pub fn get_formatted_nusers() -> String {
335366/// The load average is a tuple of three floating point numbers representing the 1-minute, 5-minute, and 15-minute load averages.
336367#[ cfg( unix) ]
337368pub fn get_loadavg ( ) -> UResult < ( f64 , f64 , f64 ) > {
369+ ensure_uptime_locale ( ) ;
338370 use crate :: libc:: c_double;
339371 use libc:: getloadavg;
340372
@@ -357,6 +389,7 @@ pub fn get_loadavg() -> UResult<(f64, f64, f64)> {
357389/// Returns a UResult with an UptimeError.
358390#[ cfg( windows) ]
359391pub fn get_loadavg ( ) -> UResult < ( f64 , f64 , f64 ) > {
392+ ensure_uptime_locale ( ) ;
360393 Err ( UptimeError :: WindowsLoadavg ) ?
361394}
362395
@@ -368,6 +401,7 @@ pub fn get_loadavg() -> UResult<(f64, f64, f64)> {
368401/// e.g. "load average: 0.00, 0.00, 0.00"
369402#[ inline]
370403pub fn get_formatted_loadavg ( ) -> UResult < String > {
404+ ensure_uptime_locale ( ) ;
371405 let loadavg = get_loadavg ( ) ?;
372406 Ok ( translate ! (
373407 "uptime-lib-format-loadavg" ,
@@ -392,4 +426,24 @@ mod tests {
392426 assert_eq ! ( "1 user" , format_nusers( 1 ) ) ;
393427 assert_eq ! ( "2 users" , format_nusers( 2 ) ) ;
394428 }
429+
430+ #[ test]
431+ fn test_format_nusers_threaded ( ) {
432+ unsafe {
433+ std:: env:: set_var ( "LANG" , "en_US.UTF-8" ) ;
434+ }
435+ let _ = locale:: setup_localization ( "top" ) ;
436+ let _ = locale:: setup_localization ( "uptime" ) ;
437+
438+ assert_eq ! ( "uptime-user-count" , format_nusers( 0 ) ) ;
439+
440+ std:: thread:: spawn ( move || {
441+ let _ = locale:: setup_localization ( "uptime" ) ;
442+ assert_eq ! ( "0 users" , format_nusers( 0 ) ) ;
443+ assert_eq ! ( "1 user" , format_nusers( 1 ) ) ;
444+ assert_eq ! ( "2 users" , format_nusers( 2 ) ) ;
445+ } )
446+ . join ( )
447+ . expect ( "thread should succeed" ) ;
448+ }
395449}
0 commit comments