1#[cfg(feature = "libc")]
12pub extern crate libc;
13#[cfg(all(feature = "windows-sys", target_os = "windows"))]
14pub extern crate windows_sys;
15
16mod features; mod macros; mod mods; pub use uucore_procs::*;
23
24pub use crate::mods::display;
26pub use crate::mods::error;
27#[cfg(feature = "fs")]
28pub use crate::mods::io;
29pub use crate::mods::line_ending;
30pub use crate::mods::locale;
31pub use crate::mods::os;
32pub use crate::mods::panic;
33pub use crate::mods::posix;
34
35#[cfg(feature = "backup-control")]
37pub use crate::features::backup_control;
38#[cfg(feature = "buf-copy")]
39pub use crate::features::buf_copy;
40#[cfg(feature = "checksum")]
41pub use crate::features::checksum;
42#[cfg(feature = "colors")]
43pub use crate::features::colors;
44#[cfg(feature = "custom-tz-fmt")]
45pub use crate::features::custom_tz_fmt;
46#[cfg(feature = "encoding")]
47pub use crate::features::encoding;
48#[cfg(feature = "extendedbigdecimal")]
49pub use crate::features::extendedbigdecimal;
50#[cfg(feature = "fast-inc")]
51pub use crate::features::fast_inc;
52#[cfg(feature = "format")]
53pub use crate::features::format;
54#[cfg(feature = "fs")]
55pub use crate::features::fs;
56#[cfg(feature = "lines")]
57pub use crate::features::lines;
58#[cfg(feature = "parser")]
59pub use crate::features::parser;
60#[cfg(feature = "quoting-style")]
61pub use crate::features::quoting_style;
62#[cfg(feature = "ranges")]
63pub use crate::features::ranges;
64#[cfg(feature = "ringbuffer")]
65pub use crate::features::ringbuffer;
66#[cfg(feature = "sum")]
67pub use crate::features::sum;
68#[cfg(feature = "update-control")]
69pub use crate::features::update_control;
70#[cfg(feature = "uptime")]
71pub use crate::features::uptime;
72#[cfg(feature = "version-cmp")]
73pub use crate::features::version_cmp;
74
75#[cfg(all(not(windows), feature = "mode"))]
78pub use crate::features::mode;
79#[cfg(all(unix, feature = "entries"))]
81pub use crate::features::entries;
82#[cfg(all(unix, feature = "perms"))]
83pub use crate::features::perms;
84#[cfg(all(unix, any(feature = "pipes", feature = "buf-copy")))]
85pub use crate::features::pipes;
86#[cfg(all(unix, feature = "process"))]
87pub use crate::features::process;
88#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
89pub use crate::features::signals;
90#[cfg(all(
91 unix,
92 not(target_os = "android"),
93 not(target_os = "fuchsia"),
94 not(target_os = "openbsd"),
95 not(target_os = "redox"),
96 feature = "utmpx"
97))]
98pub use crate::features::utmpx;
99#[cfg(all(windows, feature = "wide"))]
101pub use crate::features::wide;
102
103#[cfg(feature = "fsext")]
104pub use crate::features::fsext;
105
106#[cfg(all(unix, feature = "fsxattr"))]
107pub use crate::features::fsxattr;
108
109#[cfg(all(target_os = "linux", feature = "selinux"))]
110pub use crate::features::selinux;
111
112#[cfg(unix)]
115use nix::errno::Errno;
116#[cfg(unix)]
117use nix::sys::signal::{
118 SaFlags, SigAction, SigHandler::SigDfl, SigSet, Signal::SIGBUS, Signal::SIGSEGV, sigaction,
119};
120use std::borrow::Cow;
121use std::ffi::{OsStr, OsString};
122use std::io::{BufRead, BufReader};
123use std::iter;
124#[cfg(unix)]
125use std::os::unix::ffi::{OsStrExt, OsStringExt};
126use std::str;
127use std::sync::{LazyLock, atomic::Ordering};
128
129#[cfg(unix)]
132pub fn disable_rust_signal_handlers() -> Result<(), Errno> {
133 unsafe {
134 sigaction(
135 SIGSEGV,
136 &SigAction::new(SigDfl, SaFlags::empty(), SigSet::all()),
137 )
138 }?;
139 unsafe {
140 sigaction(
141 SIGBUS,
142 &SigAction::new(SigDfl, SaFlags::empty(), SigSet::all()),
143 )
144 }?;
145 Ok(())
146}
147
148#[macro_export]
153macro_rules! bin {
154 ($util:ident) => {
155 pub fn main() {
156 use std::io::Write;
157 uucore::panic::mute_sigpipe_panic();
159 let code = $util::uumain(uucore::args_os());
161 if let Err(e) = std::io::stdout().flush() {
163 eprintln!("Error flushing stdout: {e}");
164 }
165
166 std::process::exit(code);
167 }
168 };
169}
170
171#[macro_export]
179macro_rules! crate_version {
180 () => {
181 concat!(
182 "(",
183 env!("PROJECT_NAME_FOR_VERSION_STRING"),
184 ") ",
185 env!("CARGO_PKG_VERSION")
186 )
187 };
188}
189
190pub fn format_usage(s: &str) -> String {
198 let s = s.replace('\n', &format!("\n{}", " ".repeat(7)));
199 s.replace("{}", crate::execution_phrase())
200}
201
202pub fn get_utility_is_second_arg() -> bool {
205 crate::macros::UTILITY_IS_SECOND_ARG.load(Ordering::SeqCst)
206}
207
208pub fn set_utility_is_second_arg() {
211 crate::macros::UTILITY_IS_SECOND_ARG.store(true, Ordering::SeqCst);
212}
213
214static ARGV: LazyLock<Vec<OsString>> = LazyLock::new(|| wild::args_os().collect());
217
218static UTIL_NAME: LazyLock<String> = LazyLock::new(|| {
219 let base_index = usize::from(get_utility_is_second_arg());
220 let is_man = usize::from(ARGV[base_index].eq("manpage"));
221 let argv_index = base_index + is_man;
222
223 ARGV[argv_index].to_string_lossy().into_owned()
224});
225
226pub fn util_name() -> &'static str {
228 &UTIL_NAME
229}
230
231static EXECUTION_PHRASE: LazyLock<String> = LazyLock::new(|| {
232 if get_utility_is_second_arg() {
233 ARGV.iter()
234 .take(2)
235 .map(|os_str| os_str.to_string_lossy().into_owned())
236 .collect::<Vec<_>>()
237 .join(" ")
238 } else {
239 ARGV[0].to_string_lossy().into_owned()
240 }
241});
242
243pub fn execution_phrase() -> &'static str {
245 &EXECUTION_PHRASE
246}
247
248pub trait Args: Iterator<Item = OsString> + Sized {
253 fn collect_lossy(self) -> Vec<String> {
255 self.map(|s| s.to_string_lossy().into_owned()).collect()
256 }
257
258 fn collect_ignore(self) -> Vec<String> {
260 self.filter_map(|s| s.into_string().ok()).collect()
261 }
262}
263
264impl<T: Iterator<Item = OsString> + Sized> Args for T {}
265
266pub fn args_os() -> impl Iterator<Item = OsString> {
269 ARGV.iter().cloned()
270}
271
272pub fn read_yes() -> bool {
274 let mut s = String::new();
275 match std::io::stdin().read_line(&mut s) {
276 Ok(_) => matches!(s.chars().next(), Some('y' | 'Y')),
277 _ => false,
278 }
279}
280
281pub fn os_str_as_bytes(os_string: &OsStr) -> mods::error::UResult<&[u8]> {
286 #[cfg(unix)]
287 let bytes = os_string.as_bytes();
288
289 #[cfg(not(unix))]
290 let bytes = os_string
291 .to_str()
292 .ok_or_else(|| {
293 mods::error::UUsageError::new(1, "invalid UTF-8 was detected in one or more arguments")
294 })?
295 .as_bytes();
296
297 Ok(bytes)
298}
299
300pub fn os_str_as_bytes_lossy(os_string: &OsStr) -> Cow<[u8]> {
305 #[cfg(unix)]
306 let bytes = Cow::from(os_string.as_bytes());
307
308 #[cfg(not(unix))]
309 let bytes = match os_string.to_string_lossy() {
310 Cow::Borrowed(slice) => Cow::from(slice.as_bytes()),
311 Cow::Owned(owned) => Cow::from(owned.into_bytes()),
312 };
313
314 bytes
315}
316
317pub fn os_str_from_bytes(bytes: &[u8]) -> mods::error::UResult<Cow<'_, OsStr>> {
323 #[cfg(unix)]
324 let os_str = Cow::Borrowed(OsStr::from_bytes(bytes));
325 #[cfg(not(unix))]
326 let os_str = Cow::Owned(OsString::from(str::from_utf8(bytes).map_err(|_| {
327 mods::error::UUsageError::new(1, "Unable to transform bytes into OsStr")
328 })?));
329
330 Ok(os_str)
331}
332
333pub fn os_string_from_vec(vec: Vec<u8>) -> mods::error::UResult<OsString> {
338 #[cfg(unix)]
339 let s = OsString::from_vec(vec);
340 #[cfg(not(unix))]
341 let s = OsString::from(String::from_utf8(vec).map_err(|_| {
342 mods::error::UUsageError::new(1, "invalid UTF-8 was detected in one or more arguments")
343 })?);
344
345 Ok(s)
346}
347
348pub fn read_byte_lines<R: std::io::Read>(
351 mut buf_reader: BufReader<R>,
352) -> impl Iterator<Item = Vec<u8>> {
353 iter::from_fn(move || {
354 let mut buf = Vec::with_capacity(256);
355 let size = buf_reader.read_until(b'\n', &mut buf).ok()?;
356
357 if size == 0 {
358 return None;
359 }
360
361 if buf.ends_with(b"\n") {
363 buf.pop();
364 if buf.ends_with(b"\r") {
365 buf.pop();
366 }
367 }
368
369 Some(buf)
370 })
371}
372
373pub fn read_os_string_lines<R: std::io::Read>(
377 buf_reader: BufReader<R>,
378) -> impl Iterator<Item = OsString> {
379 read_byte_lines(buf_reader).map(|byte_line| os_string_from_vec(byte_line).expect("UTF-8 error"))
380}
381
382#[macro_export]
400macro_rules! prompt_yes(
401 ($($args:tt)+) => ({
402 use std::io::Write;
403 eprint!("{}: ", uucore::util_name());
404 eprint!($($args)+);
405 eprint!(" ");
406 let res = std::io::stderr().flush().map_err(|err| {
407 $crate::error::USimpleError::new(1, err.to_string())
408 });
409 uucore::show_if_err!(res);
410 uucore::read_yes()
411 })
412);
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417 use std::ffi::OsStr;
418
419 fn make_os_vec(os_str: &OsStr) -> Vec<OsString> {
420 vec![
421 OsString::from("test"),
422 OsString::from("สวัสดี"), os_str.to_os_string(),
424 ]
425 }
426
427 #[cfg(any(unix, target_os = "redox"))]
428 fn test_invalid_utf8_args_lossy(os_str: &OsStr) {
429 assert!(os_str.to_os_string().into_string().is_err());
431 let test_vec = make_os_vec(os_str);
432 let collected_to_str = test_vec.clone().into_iter().collect_lossy();
433 assert_eq!(collected_to_str.len(), test_vec.len());
435 for index in 0..2 {
437 assert_eq!(collected_to_str[index], test_vec[index].to_str().unwrap());
438 }
439 assert_eq!(
441 *collected_to_str[2],
442 os_str.to_os_string().to_string_lossy()
443 );
444 }
445
446 #[cfg(any(unix, target_os = "redox"))]
447 fn test_invalid_utf8_args_ignore(os_str: &OsStr) {
448 assert!(os_str.to_os_string().into_string().is_err());
450 let test_vec = make_os_vec(os_str);
451 let collected_to_str = test_vec.clone().into_iter().collect_ignore();
452 assert_eq!(collected_to_str.len(), test_vec.len() - 1);
454 for index in 0..2 {
456 assert_eq!(
457 collected_to_str.get(index).unwrap(),
458 test_vec.get(index).unwrap().to_str().unwrap()
459 );
460 }
461 }
462
463 #[test]
464 fn valid_utf8_encoding_args() {
465 let test_vec = make_os_vec(&OsString::from("test2"));
467 let _ = test_vec.into_iter().collect_lossy();
469 }
470
471 #[cfg(any(unix, target_os = "redox"))]
472 #[test]
473 fn invalid_utf8_args_unix() {
474 use std::os::unix::ffi::OsStrExt;
475
476 let source = [0x66, 0x6f, 0x80, 0x6f];
477 let os_str = OsStr::from_bytes(&source[..]);
478 test_invalid_utf8_args_lossy(os_str);
479 test_invalid_utf8_args_ignore(os_str);
480 }
481
482 #[test]
483 fn test_format_usage() {
484 assert_eq!(format_usage("expr EXPRESSION"), "expr EXPRESSION");
485 assert_eq!(
486 format_usage("expr EXPRESSION\nexpr OPTION"),
487 "expr EXPRESSION\n expr OPTION"
488 );
489 }
490}