use clap::{Arg, ArgAction, Command};
use libc::{PRIO_PROCESS, c_char, c_int, execvp};
use std::ffi::{CString, OsString};
use std::io::{Error, Write};
use std::ptr;
use uucore::translate;
use uucore::{
error::{UResult, USimpleError, UUsageError, set_exit_code},
format_usage, show_error,
};
pub mod options {
pub static ADJUSTMENT: &str = "adjustment";
pub static COMMAND: &str = "COMMAND";
}
fn is_prefix_of(maybe_prefix: &str, target: &str, min_match: usize) -> bool {
if maybe_prefix.len() < min_match || maybe_prefix.len() > target.len() {
return false;
}
&target[0..maybe_prefix.len()] == maybe_prefix
}
fn standardize_nice_args(mut args: impl uucore::Args) -> impl uucore::Args {
let mut v = Vec::<OsString>::new();
let mut saw_n = false;
let mut saw_command = false;
if let Some(cmd) = args.next() {
v.push(cmd);
}
for s in args {
if saw_command {
v.push(s);
} else if saw_n {
let mut new_arg: OsString = "-n".into();
new_arg.push(s);
v.push(new_arg);
saw_n = false;
} else if s.to_str() == Some("-n")
|| s.to_str()
.is_some_and(|s| is_prefix_of(s, "--adjustment", "--a".len()))
{
saw_n = true;
} else if let Ok(s) = s.clone().into_string() {
if let Some(stripped) = s.strip_prefix('-') {
match stripped.parse::<i64>() {
Ok(ix) => {
let mut new_arg: OsString = "-n".into();
new_arg.push(ix.to_string());
v.push(new_arg);
}
Err(_) => {
v.push(s.into());
}
}
} else {
saw_command = true;
v.push(s.into());
}
} else {
saw_command = true;
v.push(s);
}
}
if saw_n {
v.push("-n".into());
}
v.into_iter()
}
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = standardize_nice_args(args);
let matches =
uucore::clap_localization::handle_clap_result_with_exit_code(uu_app(), args, 125)?;
nix::errno::Errno::clear();
let mut niceness = unsafe { libc::getpriority(PRIO_PROCESS, 0) };
if Error::last_os_error().raw_os_error().unwrap() != 0 {
return Err(USimpleError::new(
125,
format!("getpriority: {}", Error::last_os_error()),
));
}
let adjustment = match matches.get_one::<String>(options::ADJUSTMENT) {
Some(nstr) => {
if !matches.contains_id(options::COMMAND) {
return Err(UUsageError::new(
125,
translate!("nice-error-command-required-with-adjustment"),
));
}
match nstr.parse::<i32>() {
Ok(num) => num,
Err(e) => {
return Err(USimpleError::new(
125,
translate!("nice-error-invalid-number", "value" => nstr.clone(), "error" => e),
));
}
}
}
None => {
if !matches.contains_id(options::COMMAND) {
println!("{niceness}");
return Ok(());
}
10_i32
}
};
niceness += adjustment;
if unsafe { libc::setpriority(PRIO_PROCESS, 0, niceness) } == -1 {
let warning_msg = translate!("nice-warning-setpriority", "util_name" => uucore::util_name(), "error" => Error::last_os_error());
if write!(std::io::stderr(), "{warning_msg}").is_err() {
set_exit_code(125);
return Ok(());
}
}
let cstrs: Vec<CString> = matches
.get_many::<String>(options::COMMAND)
.unwrap()
.map(|x| CString::new(x.as_bytes()).unwrap())
.collect();
let mut args: Vec<*const c_char> = cstrs.iter().map(|s| s.as_ptr()).collect();
args.push(ptr::null::<c_char>());
unsafe {
execvp(args[0], args.as_mut_ptr());
}
show_error!("execvp: {}", Error::last_os_error());
let exit_code = if Error::last_os_error().raw_os_error().unwrap() as c_int == libc::ENOENT {
127
} else {
126
};
set_exit_code(exit_code);
Ok(())
}
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.about(translate!("nice-about"))
.override_usage(format_usage(&translate!("nice-usage")))
.trailing_var_arg(true)
.infer_long_args(true)
.version(uucore::crate_version!())
.help_template(uucore::localized_help_template(uucore::util_name()))
.arg(
Arg::new(options::ADJUSTMENT)
.short('n')
.long(options::ADJUSTMENT)
.help(translate!("nice-help-adjustment"))
.action(ArgAction::Set)
.overrides_with(options::ADJUSTMENT)
.allow_hyphen_values(true),
)
.arg(
Arg::new(options::COMMAND)
.action(ArgAction::Append)
.value_hint(clap::ValueHint::CommandName),
)
}