Skip to content

Commit 2ba6f7c

Browse files
authored
Merge branch 'main' into casefold
2 parents e696eae + 9e2fec6 commit 2ba6f7c

File tree

9 files changed

+429
-19
lines changed

9 files changed

+429
-19
lines changed

.github/workflows/CICD.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -579,14 +579,14 @@ jobs:
579579
# - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_selinux , use-cross: use-cross }
580580
- { os: ubuntu-latest , target: i686-unknown-linux-gnu , features: "feat_os_unix,test_risky_names", use-cross: use-cross }
581581
- { os: ubuntu-latest , target: i686-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross }
582-
- { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: "feat_os_unix,test_risky_names", use-cross: use-cross }
582+
- { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: "feat_os_unix,test_risky_names", use-cross: use-cross, skip-publish: true }
583583
- { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: "feat_os_unix,uudoc" , use-cross: no, workspace-tests: true }
584584
- { os: ubuntu-latest , target: x86_64-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross }
585585
- { os: ubuntu-latest , target: x86_64-unknown-redox , features: feat_os_unix_redox , use-cross: redoxer , skip-tests: true }
586586
- { os: ubuntu-latest , target: wasm32-unknown-unknown , default-features: false, features: uucore/format, skip-tests: true, skip-package: true, skip-publish: true }
587587
- { os: macos-latest , target: aarch64-apple-darwin , features: feat_os_macos, workspace-tests: true } # M1 CPU
588-
# PR #7964: Mac should still build even if the feature is not enabled
589-
- { os: macos-latest , target: aarch64-apple-darwin , workspace-tests: true } # M1 CPU
588+
# PR #7964: Mac should still build even if the feature is not enabled. Do not publish this.
589+
- { os: macos-latest , target: aarch64-apple-darwin , workspace-tests: true, skip-publish: true } # M1 CPU
590590
- { os: macos-latest , target: x86_64-apple-darwin , features: feat_os_macos, workspace-tests: true }
591591
- { os: windows-latest , target: i686-pc-windows-msvc , features: feat_os_windows }
592592
- { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows }

src/uu/cksum/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ path = "src/cksum.rs"
1919

2020
[dependencies]
2121
clap = { workspace = true }
22-
uucore = { workspace = true, features = ["checksum", "encoding", "sum"] }
22+
uucore = { workspace = true, features = [
23+
"checksum",
24+
"encoding",
25+
"sum",
26+
"hardware",
27+
] }
2328
hex = { workspace = true }
2429
fluent = { workspace = true }
2530

src/uu/cksum/locales/en-US.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ cksum-help-status = don't output anything, status code shows success
2727
cksum-help-quiet = don't print OK for each successfully verified file
2828
cksum-help-ignore-missing = don't fail or report status for missing files
2929
cksum-help-zero = end each output line with NUL, not newline, and disable file name escaping
30+
cksum-help-debug = print CPU hardware capability detection info used by cksum
3031
3132
# Error messages
3233
cksum-error-is-directory = { $file }: Is a directory

src/uu/cksum/locales/fr-FR.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ cksum-help-status = ne rien afficher, le code de statut indique le succès
2727
cksum-help-quiet = ne pas afficher OK pour chaque fichier vérifié avec succès
2828
cksum-help-ignore-missing = ne pas échouer ou signaler le statut pour les fichiers manquants
2929
cksum-help-zero = terminer chaque ligne de sortie avec NUL, pas un saut de ligne, et désactiver l'échappement des noms de fichiers
30+
cksum-help-debug = afficher les informations de débogage sur la détection de la prise en charge matérielle du processeur
3031
3132
# Messages d'erreur
3233
cksum-error-is-directory = { $file } : Est un répertoire

src/uu/cksum/src/cksum.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,34 @@ use uucore::checksum::{
2020
sanitize_sha2_sha3_length_str,
2121
};
2222
use uucore::error::UResult;
23+
use uucore::hardware::CpuFeatures;
2324
use uucore::line_ending::LineEnding;
2425
use uucore::{format_usage, translate};
2526

27+
/// Print CPU hardware capability detection information to stderr
28+
/// This matches GNU cksum's --debug behavior
29+
fn print_cpu_debug_info() {
30+
let features = CpuFeatures::detect();
31+
32+
fn print_feature(name: &str, available: bool) {
33+
if available {
34+
eprintln!("cksum: using {name} hardware support");
35+
} else {
36+
eprintln!("cksum: {name} support not detected");
37+
}
38+
}
39+
40+
// x86/x86_64
41+
print_feature("avx512", features.has_avx512());
42+
print_feature("avx2", features.has_avx2());
43+
print_feature("pclmul", features.has_pclmul());
44+
45+
// ARM aarch64
46+
if cfg!(target_arch = "aarch64") {
47+
print_feature("vmull", features.has_vmull());
48+
}
49+
}
50+
2651
mod options {
2752
pub const ALGORITHM: &str = "algorithm";
2853
pub const FILE: &str = "file";
@@ -40,6 +65,7 @@ mod options {
4065
pub const IGNORE_MISSING: &str = "ignore-missing";
4166
pub const QUIET: &str = "quiet";
4267
pub const ZERO: &str = "zero";
68+
pub const DEBUG: &str = "debug";
4369
}
4470

4571
/// cksum has a bunch of legacy behavior. We handle this in this function to
@@ -181,6 +207,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
181207
matches.get_flag(options::BASE64),
182208
);
183209

210+
// Print hardware debug info if requested
211+
if matches.get_flag(options::DEBUG) {
212+
print_cpu_debug_info();
213+
}
214+
184215
let opts = ChecksumComputeOptions {
185216
algo_kind: algo,
186217
output_format,
@@ -317,5 +348,11 @@ pub fn uu_app() -> Command {
317348
.help(translate!("cksum-help-zero"))
318349
.action(ArgAction::SetTrue),
319350
)
351+
.arg(
352+
Arg::new(options::DEBUG)
353+
.long(options::DEBUG)
354+
.help(translate!("cksum-help-debug"))
355+
.action(ArgAction::SetTrue),
356+
)
320357
.after_help(translate!("cksum-after-help"))
321358
}

src/uu/stty/src/stty.rs

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// spell-checker:ignore isig icanon iexten echoe crterase echok echonl noflsh xcase tostop echoprt prterase echoctl ctlecho echoke crtkill flusho extproc
1111
// spell-checker:ignore lnext rprnt susp swtch vdiscard veof veol verase vintr vkill vlnext vquit vreprint vstart vstop vsusp vswtc vwerase werase
1212
// spell-checker:ignore sigquit sigtstp
13-
// spell-checker:ignore cbreak decctlq evenp litout oddp tcsadrain exta extb
13+
// spell-checker:ignore cbreak decctlq evenp litout oddp tcsadrain exta extb NCCS
1414

1515
mod flags;
1616

@@ -30,7 +30,7 @@ use std::num::IntErrorKind;
3030
use std::os::fd::{AsFd, BorrowedFd};
3131
use std::os::unix::fs::OpenOptionsExt;
3232
use std::os::unix::io::{AsRawFd, RawFd};
33-
use uucore::error::{UError, UResult, USimpleError};
33+
use uucore::error::{UError, UResult, USimpleError, UUsageError};
3434
use uucore::format_usage;
3535
use uucore::translate;
3636

@@ -150,6 +150,7 @@ enum ArgOptions<'a> {
150150
Mapping((S, u8)),
151151
Special(SpecialSetting),
152152
Print(PrintSetting),
153+
SavedState(Vec<u32>),
153154
}
154155

155156
impl<'a> From<AllFlags<'a>> for ArgOptions<'a> {
@@ -352,8 +353,12 @@ fn stty(opts: &Options) -> UResult<()> {
352353
valid_args.push(ArgOptions::Print(PrintSetting::Size));
353354
}
354355
_ => {
356+
// Try to parse saved format (hex string like "6d02:5:4bf:8a3b:...")
357+
if let Some(state) = parse_saved_state(arg) {
358+
valid_args.push(ArgOptions::SavedState(state));
359+
}
355360
// control char
356-
if let Some(char_index) = cc_to_index(arg) {
361+
else if let Some(char_index) = cc_to_index(arg) {
357362
if let Some(mapping) = args_iter.next() {
358363
let cc_mapping = string_to_control_char(mapping).map_err(|e| {
359364
let message = match e {
@@ -370,7 +375,7 @@ fn stty(opts: &Options) -> UResult<()> {
370375
)
371376
}
372377
};
373-
USimpleError::new(1, message)
378+
UUsageError::new(1, message)
374379
})?;
375380
valid_args.push(ArgOptions::Mapping((char_index, cc_mapping)));
376381
} else {
@@ -418,6 +423,9 @@ fn stty(opts: &Options) -> UResult<()> {
418423
ArgOptions::Print(setting) => {
419424
print_special_setting(setting, opts.file.as_raw_fd())?;
420425
}
426+
ArgOptions::SavedState(state) => {
427+
apply_saved_state(&mut termios, state)?;
428+
}
421429
}
422430
}
423431
tcsetattr(opts.file.as_fd(), set_arg, &termios)?;
@@ -429,8 +437,9 @@ fn stty(opts: &Options) -> UResult<()> {
429437
Ok(())
430438
}
431439

440+
// The GNU implementation adds the --help message when the args are incorrectly formatted
432441
fn missing_arg<T>(arg: &str) -> Result<T, Box<dyn UError>> {
433-
Err::<T, Box<dyn UError>>(USimpleError::new(
442+
Err(UUsageError::new(
434443
1,
435444
translate!(
436445
"stty-error-missing-argument",
@@ -440,7 +449,7 @@ fn missing_arg<T>(arg: &str) -> Result<T, Box<dyn UError>> {
440449
}
441450

442451
fn invalid_arg<T>(arg: &str) -> Result<T, Box<dyn UError>> {
443-
Err::<T, Box<dyn UError>>(USimpleError::new(
452+
Err(UUsageError::new(
444453
1,
445454
translate!(
446455
"stty-error-invalid-argument",
@@ -450,7 +459,7 @@ fn invalid_arg<T>(arg: &str) -> Result<T, Box<dyn UError>> {
450459
}
451460

452461
fn invalid_integer_arg<T>(arg: &str) -> Result<T, Box<dyn UError>> {
453-
Err::<T, Box<dyn UError>>(USimpleError::new(
462+
Err(UUsageError::new(
454463
1,
455464
translate!(
456465
"stty-error-invalid-integer-argument",
@@ -478,6 +487,43 @@ fn parse_rows_cols(arg: &str) -> Option<u16> {
478487
None
479488
}
480489

490+
/// Parse a saved terminal state string in stty format.
491+
///
492+
/// The format is colon-separated hexadecimal values:
493+
/// `input_flags:output_flags:control_flags:local_flags:cc0:cc1:cc2:...`
494+
///
495+
/// - Must have exactly 4 + NCCS parts (4 flags + platform-specific control characters)
496+
/// - All parts must be non-empty valid hex values
497+
/// - Control characters must fit in u8 (0-255)
498+
/// - Returns `None` if format is invalid
499+
fn parse_saved_state(arg: &str) -> Option<Vec<u32>> {
500+
let parts: Vec<&str> = arg.split(':').collect();
501+
let expected_parts = 4 + nix::libc::NCCS;
502+
503+
// GNU requires exactly the right number of parts for this platform
504+
if parts.len() != expected_parts {
505+
return None;
506+
}
507+
508+
// Validate all parts are non-empty valid hex
509+
let mut values = Vec::with_capacity(expected_parts);
510+
for (i, part) in parts.iter().enumerate() {
511+
if part.is_empty() {
512+
return None; // GNU rejects empty hex values
513+
}
514+
let val = u32::from_str_radix(part, 16).ok()?;
515+
516+
// Control characters (indices 4+) must fit in u8
517+
if i >= 4 && val > 255 {
518+
return None;
519+
}
520+
521+
values.push(val);
522+
}
523+
524+
Some(values)
525+
}
526+
481527
fn check_flag_group<T>(flag: &Flag<T>, remove: bool) -> bool {
482528
remove && flag.group.is_some()
483529
}
@@ -857,6 +903,39 @@ fn apply_char_mapping(termios: &mut Termios, mapping: &(S, u8)) {
857903
termios.control_chars[mapping.0 as usize] = mapping.1;
858904
}
859905

906+
/// Apply a saved terminal state to the current termios.
907+
///
908+
/// The state array contains:
909+
/// - `state[0]`: input flags
910+
/// - `state[1]`: output flags
911+
/// - `state[2]`: control flags
912+
/// - `state[3]`: local flags
913+
/// - `state[4..]`: control characters (optional)
914+
///
915+
/// If state has fewer than 4 elements, no changes are applied. This is a defensive
916+
/// check that should never trigger since `parse_saved_state` rejects such states.
917+
fn apply_saved_state(termios: &mut Termios, state: &[u32]) -> nix::Result<()> {
918+
// Require at least 4 elements for the flags (defensive check)
919+
if state.len() < 4 {
920+
return Ok(()); // No-op for invalid state (already validated by parser)
921+
}
922+
923+
// Apply the four flag groups, done (as _) for MacOS size compatibility
924+
termios.input_flags = InputFlags::from_bits_truncate(state[0] as _);
925+
termios.output_flags = OutputFlags::from_bits_truncate(state[1] as _);
926+
termios.control_flags = ControlFlags::from_bits_truncate(state[2] as _);
927+
termios.local_flags = LocalFlags::from_bits_truncate(state[3] as _);
928+
929+
// Apply control characters if present (stored as u32 but used as u8)
930+
for (i, &cc_val) in state.iter().skip(4).enumerate() {
931+
if i < termios.control_chars.len() {
932+
termios.control_chars[i] = cc_val as u8;
933+
}
934+
}
935+
936+
Ok(())
937+
}
938+
860939
fn apply_special_setting(
861940
_termios: &mut Termios,
862941
setting: &SpecialSetting,

0 commit comments

Comments
 (0)