Skip to content

Commit 143136d

Browse files
committed
[time] adsutments
1 parent a976a6b commit 143136d

File tree

2 files changed

+142
-2
lines changed

2 files changed

+142
-2
lines changed

Diff for: datetime/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,4 @@ path = "./sleep.rs"
2727

2828
[[bin]]
2929
name = "time"
30-
path = "src/time.rs"
31-
30+
path = "./time.rs"

Diff for: datetime/time.rs

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//
2+
// Copyright (c) 2024 Hemi Labs, Inc.
3+
//
4+
// This file is part of the posixutils-rs project covered under
5+
// the MIT License. For the full license text, please see the LICENSE
6+
// file in the root directory of this project.
7+
// SPDX-License-Identifier: MIT
8+
//
9+
10+
use std::process::{Command, Stdio};
11+
use std::time::Instant;
12+
use std::io::{self, Write};
13+
14+
use clap::Parser;
15+
16+
use gettextrs::{bind_textdomain_codeset, setlocale, textdomain, LocaleCategory};
17+
use plib::PROJECT_NAME;
18+
19+
#[derive(Parser, Debug)]
20+
#[command(author, version, about, long_about = None)]
21+
struct Args {
22+
/// Write timing output to standard error in POSIX format
23+
#[arg(short, long)]
24+
posix: bool,
25+
26+
/// The utility to be invoked
27+
utility: String,
28+
29+
/// Arguments for the utility
30+
#[arg(name = "ARGUMENT", trailing_var_arg = true)]
31+
arguments: Vec<String>,
32+
}
33+
34+
enum TimeError {
35+
ExecCommand(String),
36+
ExecTime,
37+
CommandNotFound(String),
38+
}
39+
40+
fn time(args: Args) -> Result<(), TimeError> {
41+
let start_time = Instant::now();
42+
// SAFETY: std::mem::zeroed() is used to create an instance of libc::tms with all fields set to zero.
43+
// This is safe here because libc::tms is a Plain Old Data type, and zero is a valid value for all its fields.
44+
let mut tms_start: libc::tms = unsafe { std::mem::zeroed() };
45+
// SAFETY: sysconf is a POSIX function that returns the number of clock ticks per second.
46+
// It is safe to call because it does not modify any memory and has no side effects.
47+
let clock_ticks_per_second = unsafe { libc::sysconf(libc::_SC_CLK_TCK) as f64 };
48+
49+
// SAFETY: times is a POSIX function that fills the provided tms structure with time-accounting information.
50+
// It is safe to call because we have correctly allocated and initialized tms_start, and the function
51+
// only writes to this structure.
52+
unsafe { libc::times(&mut tms_start) };
53+
54+
let mut child = Command::new(&args.utility)
55+
.args(args.arguments)
56+
.stdout(Stdio::inherit())
57+
.stderr(Stdio::inherit())
58+
.spawn()
59+
.map_err(|e| {
60+
match e.kind() {
61+
io::ErrorKind::NotFound => TimeError::CommandNotFound(args.utility),
62+
_ => TimeError::ExecCommand(args.utility),
63+
}
64+
})?;
65+
66+
let _ = child.wait().map_err(|_| TimeError::ExecTime)?;
67+
68+
let elapsed = start_time.elapsed();
69+
let tms_end: libc::tms = unsafe { std::mem::zeroed() };
70+
71+
let user_time = (tms_start.tms_utime - tms_end.tms_utime) as f64 / clock_ticks_per_second;
72+
let system_time = (tms_start.tms_stime - tms_end.tms_stime) as f64 / clock_ticks_per_second;
73+
74+
if args.posix {
75+
writeln!(
76+
io::stderr(),
77+
"real {:.6}\nuser {:.6}\nsys {:.6}",
78+
elapsed.as_secs_f64(),
79+
user_time,
80+
system_time
81+
).map_err(|_| TimeError::ExecTime)?;
82+
} else {
83+
writeln!(
84+
io::stderr(),
85+
"Elapsed time: {:.6} seconds\nUser time: {:.6} seconds\nSystem time: {:.6} seconds",
86+
elapsed.as_secs_f64(),
87+
user_time,
88+
system_time
89+
).map_err(|_| TimeError::ExecTime)?;
90+
}
91+
92+
Ok(())
93+
}
94+
95+
enum Status {
96+
Ok,
97+
TimeError,
98+
UtilError,
99+
UtilNotFound,
100+
}
101+
102+
impl Status {
103+
fn exit(self) -> ! {
104+
let res = match self {
105+
Status::Ok => 0,
106+
Status::TimeError => 1,
107+
Status::UtilError => 126,
108+
Status::UtilNotFound => 127,
109+
};
110+
111+
std::process::exit(res)
112+
}
113+
}
114+
115+
116+
fn main() -> Result<(), Box<dyn std::error::Error>> {
117+
let args = Args::parse();
118+
119+
setlocale(LocaleCategory::LcAll, "");
120+
textdomain(PROJECT_NAME)?;
121+
bind_textdomain_codeset(PROJECT_NAME, "UTF-8")?;
122+
123+
if let Err(err) = time(args) {
124+
match err {
125+
TimeError::CommandNotFound(err) => {
126+
eprintln!("Command not found: {}", err);
127+
Status::UtilNotFound.exit()
128+
},
129+
TimeError::ExecCommand(err) => {
130+
eprintln!("Error while executing command: {}", err);
131+
Status::UtilError.exit()
132+
}
133+
TimeError::ExecTime => {
134+
eprintln!("Error while executing time utility");
135+
Status::TimeError.exit()
136+
},
137+
}
138+
}
139+
140+
Status::Ok.exit()
141+
}

0 commit comments

Comments
 (0)