diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index 93716281f5c..bdfac82a97d 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -79,8 +79,9 @@ jobs: shell: bash run: | ## Install dependencies + ## Check that libselinux is optional for build-gnu.sh by installing libselinux only on lima sudo apt-get update - sudo apt-get install -y autoconf autopoint bison texinfo gperf gcc g++ gdb python3-pyinotify jq valgrind libexpect-perl libacl1-dev libattr1-dev libcap-dev libselinux1-dev attr quilt + sudo apt-get install -y autoconf autopoint bison texinfo gperf gcc g++ gdb python3-pyinotify jq valgrind libexpect-perl libacl1-dev libattr1-dev libcap-dev attr quilt - name: Add various locales shell: bash run: | diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index e75523b5038..89a9a6f1892 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -160,7 +160,6 @@ jobs: WORKSPACE="${WORKSPACE_PARENT}/${REPO_NAME}" # pw adduser -n ${TEST_USER} -d /root/ -g wheel -c "Coreutils user to build" -w random - # chown -R ${TEST_USER}:wheel /root/ "${WORKSPACE_PARENT}"/ chown -R ${TEST_USER}:wheel /root/ "${WORKSPACE_PARENT}"/ whoami # diff --git a/.github/workflows/openbsd.yml b/.github/workflows/openbsd.yml index 1c04528c0a9..9ca20eab36c 100644 --- a/.github/workflows/openbsd.yml +++ b/.github/workflows/openbsd.yml @@ -147,11 +147,15 @@ jobs: useradd -m -G wheel ${TEST_USER} chown -R ${TEST_USER}:wheel /root/ "${WORKSPACE_PARENT}"/ whoami + # Add fake host for reverse DNS lookup (needed for hostname test) + printf "10.0.2.15\topenbsd.my.domain openbsd\n" >> /etc/hosts # # Further work needs to be done in a sudo as we are changing users sudo -i -u ${TEST_USER} sh << EOF set -e whoami + # Increase max open files (512 by default) + ulimit -n 1024 # Rust is installed from packages, no need for rustup # Set up PATH for cargo export PATH="/usr/local/bin:$PATH" diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index 70cbd937933..a3b51bfedb6 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -120,6 +120,7 @@ pseudoprimes quantiles readonly reparse +rposition seedable semver semiprime diff --git a/Cargo.lock b/Cargo.lock index 8c9f0c735b9..f2f8412cc31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3495,6 +3495,7 @@ dependencies = [ "codspeed-divan-compat", "fluent", "tempfile", + "unicode-width 0.2.2", "uucore", ] diff --git a/src/uu/fold/Cargo.toml b/src/uu/fold/Cargo.toml index 644d78b41bb..845ce2c9698 100644 --- a/src/uu/fold/Cargo.toml +++ b/src/uu/fold/Cargo.toml @@ -21,6 +21,7 @@ path = "src/fold.rs" clap = { workspace = true } uucore = { workspace = true } fluent = { workspace = true } +unicode-width = { workspace = true } [dev-dependencies] divan = { workspace = true } diff --git a/src/uu/fold/locales/en-US.ftl b/src/uu/fold/locales/en-US.ftl index 9f8c6f3b984..d4241666715 100644 --- a/src/uu/fold/locales/en-US.ftl +++ b/src/uu/fold/locales/en-US.ftl @@ -2,6 +2,7 @@ fold-about = Writes each file (or standard input if no files are given) to standard output whilst breaking long lines fold-usage = fold [OPTION]... [FILE]... fold-bytes-help = count using bytes rather than columns (meaning control characters such as newline are not treated specially) +fold-characters-help = count using character positions rather than display columns fold-spaces-help = break lines at word boundaries rather than a hard cut-off fold-width-help = set WIDTH as the maximum line width rather than 80 fold-error-illegal-width = illegal width value diff --git a/src/uu/fold/locales/fr-FR.ftl b/src/uu/fold/locales/fr-FR.ftl index 1a723594052..ce313160cf4 100644 --- a/src/uu/fold/locales/fr-FR.ftl +++ b/src/uu/fold/locales/fr-FR.ftl @@ -1,6 +1,7 @@ fold-about = Écrit chaque fichier (ou l'entrée standard si aucun fichier n'est donné) sur la sortie standard en coupant les lignes trop longues fold-usage = fold [OPTION]... [FICHIER]... fold-bytes-help = compter en octets plutôt qu'en colonnes (les caractères de contrôle comme retour chariot ne sont pas traités spécialement) +fold-characters-help = compter en caractères plutôt qu'en colonnes d'affichage fold-spaces-help = couper les lignes aux limites de mots plutôt qu'à une largeur fixe fold-width-help = définir WIDTH comme largeur de ligne maximale au lieu de 80 fold-error-illegal-width = valeur de largeur illégale diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index bbaac56bee8..f14ed3cf071 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -9,6 +9,7 @@ use clap::{Arg, ArgAction, Command}; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout}; use std::path::Path; +use unicode_width::UnicodeWidthChar; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; use uucore::format_usage; @@ -21,11 +22,28 @@ const TAB: u8 = b'\t'; mod options { pub const BYTES: &str = "bytes"; + pub const CHARACTERS: &str = "characters"; pub const SPACES: &str = "spaces"; pub const WIDTH: &str = "width"; pub const FILE: &str = "file"; } +#[derive(Clone, Copy, PartialEq, Eq)] +enum WidthMode { + Columns, + Characters, +} + +struct FoldContext<'a, W: Write> { + spaces: bool, + width: usize, + mode: WidthMode, + writer: &'a mut W, + output: &'a mut Vec, + col_count: &'a mut usize, + last_space: &'a mut Option, +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = args.collect_lossy(); @@ -34,6 +52,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?; let bytes = matches.get_flag(options::BYTES); + let characters = matches.get_flag(options::CHARACTERS); let spaces = matches.get_flag(options::SPACES); let poss_width = match matches.get_one::(options::WIDTH) { Some(v) => Some(v.clone()), @@ -55,7 +74,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { None => vec!["-".to_owned()], }; - fold(&files, bytes, spaces, width) + fold(&files, bytes, characters, spaces, width) } pub fn uu_app() -> Command { @@ -72,6 +91,13 @@ pub fn uu_app() -> Command { .help(translate!("fold-bytes-help")) .action(ArgAction::SetTrue), ) + .arg( + Arg::new(options::CHARACTERS) + .long(options::CHARACTERS) + .help(translate!("fold-characters-help")) + .conflicts_with(options::BYTES) + .action(ArgAction::SetTrue), + ) .arg( Arg::new(options::SPACES) .long(options::SPACES) @@ -107,7 +133,13 @@ fn handle_obsolete(args: &[String]) -> (Vec, Option) { (args.to_vec(), None) } -fn fold(filenames: &[String], bytes: bool, spaces: bool, width: usize) -> UResult<()> { +fn fold( + filenames: &[String], + bytes: bool, + characters: bool, + spaces: bool, + width: usize, +) -> UResult<()> { let mut output = BufWriter::new(stdout()); for filename in filenames { @@ -125,7 +157,12 @@ fn fold(filenames: &[String], bytes: bool, spaces: bool, width: usize) -> UResul if bytes { fold_file_bytewise(buffer, spaces, width, &mut output)?; } else { - fold_file(buffer, spaces, width, &mut output)?; + let mode = if characters { + WidthMode::Characters + } else { + WidthMode::Columns + }; + fold_file(buffer, spaces, width, mode, &mut output)?; } } @@ -213,6 +250,303 @@ fn fold_file_bytewise( Ok(()) } +fn next_tab_stop(col_count: usize) -> usize { + col_count + TAB_WIDTH - col_count % TAB_WIDTH +} + +fn compute_col_count(buffer: &[u8], mode: WidthMode) -> usize { + match mode { + WidthMode::Characters => std::str::from_utf8(buffer) + .map(|s| s.chars().count()) + .unwrap_or(buffer.len()), + WidthMode::Columns => { + if let Ok(s) = std::str::from_utf8(buffer) { + let mut width = 0; + for ch in s.chars() { + match ch { + '\r' => width = 0, + '\t' => width = next_tab_stop(width), + '\x08' => width = width.saturating_sub(1), + _ => width += UnicodeWidthChar::width(ch).unwrap_or(0), + } + } + width + } else { + let mut width = 0; + for &byte in buffer { + match byte { + CR => width = 0, + TAB => width = next_tab_stop(width), + 0x08 => width = width.saturating_sub(1), + _ => width += 1, + } + } + width + } + } + } +} + +fn emit_output(ctx: &mut FoldContext<'_, W>) -> UResult<()> { + let consume = match *ctx.last_space { + Some(index) => index + 1, + None => ctx.output.len(), + }; + + if consume > 0 { + ctx.writer.write_all(&ctx.output[..consume])?; + } + ctx.writer.write_all(&[NL])?; + + let last_space = *ctx.last_space; + + if consume < ctx.output.len() { + ctx.output.drain(..consume); + } else { + ctx.output.clear(); + } + + *ctx.col_count = compute_col_count(ctx.output, ctx.mode); + + if ctx.spaces { + *ctx.last_space = last_space.and_then(|idx| { + if idx < consume { + None + } else { + Some(idx - consume) + } + }); + } else { + *ctx.last_space = None; + } + Ok(()) +} + +fn process_ascii_line(line: &[u8], ctx: &mut FoldContext<'_, W>) -> UResult<()> { + let mut idx = 0; + let len = line.len(); + + while idx < len { + match line[idx] { + NL => { + *ctx.last_space = None; + emit_output(ctx)?; + break; + } + CR => { + ctx.output.push(CR); + *ctx.col_count = 0; + idx += 1; + } + 0x08 => { + ctx.output.push(0x08); + *ctx.col_count = ctx.col_count.saturating_sub(1); + idx += 1; + } + TAB if ctx.mode == WidthMode::Columns => { + loop { + let next_stop = next_tab_stop(*ctx.col_count); + if next_stop > ctx.width && !ctx.output.is_empty() { + emit_output(ctx)?; + continue; + } + *ctx.col_count = next_stop; + break; + } + if ctx.spaces { + *ctx.last_space = Some(ctx.output.len()); + } else { + *ctx.last_space = None; + } + ctx.output.push(TAB); + idx += 1; + } + 0x00..=0x07 | 0x0B..=0x0C | 0x0E..=0x1F | 0x7F => { + ctx.output.push(line[idx]); + if ctx.spaces && line[idx].is_ascii_whitespace() && line[idx] != CR { + *ctx.last_space = Some(ctx.output.len() - 1); + } else if !ctx.spaces { + *ctx.last_space = None; + } + idx += 1; + } + _ => { + let start = idx; + while idx < len + && !matches!( + line[idx], + NL | CR | TAB | 0x08 | 0x00..=0x07 | 0x0B..=0x0C | 0x0E..=0x1F | 0x7F + ) + { + idx += 1; + } + push_ascii_segment(&line[start..idx], ctx)?; + } + } + } + + Ok(()) +} + +fn push_ascii_segment(segment: &[u8], ctx: &mut FoldContext<'_, W>) -> UResult<()> { + if segment.is_empty() { + return Ok(()); + } + + let mut remaining = segment; + + while !remaining.is_empty() { + if *ctx.col_count >= ctx.width { + emit_output(ctx)?; + continue; + } + + let available = ctx.width - *ctx.col_count; + let take = remaining.len().min(available); + let base_len = ctx.output.len(); + + ctx.output.extend_from_slice(&remaining[..take]); + *ctx.col_count += take; + + if ctx.spaces { + if let Some(pos) = remaining[..take] + .iter() + .rposition(|b| b.is_ascii_whitespace() && *b != CR) + { + *ctx.last_space = Some(base_len + pos); + } + } else { + *ctx.last_space = None; + } + + remaining = &remaining[take..]; + } + + Ok(()) +} + +fn process_utf8_line(line: &str, ctx: &mut FoldContext<'_, W>) -> UResult<()> { + if line.is_ascii() { + return process_ascii_line(line.as_bytes(), ctx); + } + + let line_bytes = line.as_bytes(); + let mut iter = line.char_indices().peekable(); + + while let Some((byte_idx, ch)) = iter.next() { + let next_idx = iter.peek().map(|(idx, _)| *idx).unwrap_or(line_bytes.len()); + + if ch == '\n' { + *ctx.last_space = None; + emit_output(ctx)?; + break; + } + + if *ctx.col_count >= ctx.width { + emit_output(ctx)?; + } + + if ch == '\r' { + ctx.output + .extend_from_slice(&line_bytes[byte_idx..next_idx]); + *ctx.col_count = 0; + continue; + } + + if ch == '\x08' { + ctx.output + .extend_from_slice(&line_bytes[byte_idx..next_idx]); + *ctx.col_count = ctx.col_count.saturating_sub(1); + continue; + } + + if ctx.mode == WidthMode::Columns && ch == '\t' { + loop { + let next_stop = next_tab_stop(*ctx.col_count); + if next_stop > ctx.width && !ctx.output.is_empty() { + emit_output(ctx)?; + continue; + } + *ctx.col_count = next_stop; + break; + } + if ctx.spaces { + *ctx.last_space = Some(ctx.output.len()); + } else { + *ctx.last_space = None; + } + ctx.output + .extend_from_slice(&line_bytes[byte_idx..next_idx]); + continue; + } + + let added = match ctx.mode { + WidthMode::Columns => UnicodeWidthChar::width(ch).unwrap_or(0), + WidthMode::Characters => 1, + }; + + if ctx.mode == WidthMode::Columns + && added > 0 + && *ctx.col_count + added > ctx.width + && !ctx.output.is_empty() + { + emit_output(ctx)?; + } + + if ctx.spaces && ch.is_ascii_whitespace() { + *ctx.last_space = Some(ctx.output.len()); + } + + ctx.output + .extend_from_slice(&line_bytes[byte_idx..next_idx]); + *ctx.col_count = ctx.col_count.saturating_add(added); + } + + Ok(()) +} + +fn process_non_utf8_line(line: &[u8], ctx: &mut FoldContext<'_, W>) -> UResult<()> { + for &byte in line { + if byte == NL { + *ctx.last_space = None; + emit_output(ctx)?; + break; + } + + if *ctx.col_count >= ctx.width { + emit_output(ctx)?; + } + + match byte { + CR => *ctx.col_count = 0, + TAB => { + let next_stop = next_tab_stop(*ctx.col_count); + if next_stop > ctx.width && !ctx.output.is_empty() { + emit_output(ctx)?; + } + *ctx.col_count = next_stop; + *ctx.last_space = if ctx.spaces { + Some(ctx.output.len()) + } else { + None + }; + ctx.output.push(byte); + continue; + } + 0x08 => *ctx.col_count = ctx.col_count.saturating_sub(1), + _ if ctx.spaces && byte.is_ascii_whitespace() => { + *ctx.last_space = Some(ctx.output.len()); + *ctx.col_count = ctx.col_count.saturating_add(1); + } + _ => *ctx.col_count = ctx.col_count.saturating_add(1), + } + + ctx.output.push(byte); + } + + Ok(()) +} + /// Fold `file` to fit `width` (number of columns). /// /// By default `fold` treats tab, backspace, and carriage return specially: @@ -226,6 +560,7 @@ fn fold_file( mut file: BufReader, spaces: bool, width: usize, + mode: WidthMode, writer: &mut W, ) -> UResult<()> { let mut line = Vec::new(); @@ -233,30 +568,6 @@ fn fold_file( let mut col_count = 0; let mut last_space = None; - /// Print the output line, resetting the column and character counts. - /// - /// If `spaces` is `true`, print the output line up to the last - /// encountered whitespace character (inclusive) and set the remaining - /// characters as the start of the next line. - macro_rules! emit_output { - () => { - let consume = match last_space { - Some(i) => i + 1, - None => output.len(), - }; - - writer.write_all(&output[..consume])?; - writer.write_all(&[NL])?; - output.drain(..consume); - - // we know there are no tabs left in output, so each char counts - // as 1 column - col_count = output.len(); - - last_space = None; - }; - } - loop { if file .read_until(NL, &mut line) @@ -266,50 +577,27 @@ fn fold_file( break; } - for ch in &line { - if *ch == NL { - // make sure to _not_ split output at whitespace, since we - // know the entire output will fit - last_space = None; - emit_output!(); - break; - } - - if col_count >= width { - emit_output!(); - } - - match *ch { - CR => col_count = 0, - TAB => { - let next_tab_stop = col_count + TAB_WIDTH - col_count % TAB_WIDTH; - - if next_tab_stop > width && !output.is_empty() { - emit_output!(); - } - - col_count = next_tab_stop; - last_space = if spaces { Some(output.len()) } else { None }; - } - 0x08 => { - col_count = col_count.saturating_sub(1); - } - _ if spaces && ch.is_ascii_whitespace() => { - last_space = Some(output.len()); - col_count += 1; - } - _ => col_count += 1, - } + let mut ctx = FoldContext { + spaces, + width, + mode, + writer, + output: &mut output, + col_count: &mut col_count, + last_space: &mut last_space, + }; - output.push(*ch); + match std::str::from_utf8(&line) { + Ok(s) => process_utf8_line(s, &mut ctx)?, + Err(_) => process_non_utf8_line(&line, &mut ctx)?, } - if !output.is_empty() { - writer.write_all(&output)?; - output.truncate(0); - } + line.clear(); + } - line.truncate(0); + if !output.is_empty() { + writer.write_all(&output)?; + output.clear(); } Ok(()) diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index e94fa847db2..7d1f862aa5e 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -245,6 +245,7 @@ pub fn uu_app() -> Command { .after_help(translate!("nl-after-help")) .infer_long_args(true) .disable_help_flag(true) + .args_override_self(true) .arg( Arg::new(options::HELP) .long(options::HELP) diff --git a/tests/by-util/test_fold.rs b/tests/by-util/test_fold.rs index 4a2d381fafb..04072ab157f 100644 --- a/tests/by-util/test_fold.rs +++ b/tests/by-util/test_fold.rs @@ -41,6 +41,24 @@ fn test_default_wrap_with_newlines() { .stdout_is_fixture("lorem_ipsum_new_line_80_column.expected"); } +#[test] +fn test_wide_characters_in_column_mode() { + new_ucmd!() + .args(&["-w", "5"]) + .pipe_in("\u{B250}\u{B250}\u{B250}\n") + .succeeds() + .stdout_is("\u{B250}\u{B250}\n\u{B250}\n"); +} + +#[test] +fn test_wide_characters_with_characters_option() { + new_ucmd!() + .args(&["--characters", "-w", "5"]) + .pipe_in("\u{B250}\u{B250}\u{B250}\n") + .succeeds() + .stdout_is("\u{B250}\u{B250}\u{B250}\n"); +} + #[test] fn test_should_preserve_empty_line_without_final_newline() { new_ucmd!() diff --git a/tests/by-util/test_hostname.rs b/tests/by-util/test_hostname.rs index aedf7d01525..e4f30c3e525 100644 --- a/tests/by-util/test_hostname.rs +++ b/tests/by-util/test_hostname.rs @@ -14,8 +14,8 @@ fn test_hostname() { assert!(ls_default_res.stdout().len() >= ls_domain_res.stdout().len()); } -// FixME: fails for "MacOS" and "OpenBSD" => "failed to lookup address information" -#[cfg(not(any(target_os = "macos", target_os = "openbsd")))] +// FixME: fails for "MacOS" => "failed to lookup address information" +#[cfg(not(any(target_os = "macos")))] #[test] fn test_hostname_ip() { let result = new_ucmd!().arg("-i").succeeds(); diff --git a/tests/by-util/test_nl.rs b/tests/by-util/test_nl.rs index 953d7bcb301..ab430b20bcc 100644 --- a/tests/by-util/test_nl.rs +++ b/tests/by-util/test_nl.rs @@ -800,3 +800,104 @@ fn test_file_with_non_utf8_content() { String::from_utf8_lossy(invalid_utf8) )); } + +// Regression tests for issue #9132: repeated flags should use last value +#[test] +fn test_repeated_body_numbering_flag() { + // -ba -bt should use -bt (t=nonempty) + new_ucmd!() + .args(&["-ba", "-bt"]) + .pipe_in("a\n\nb\n\nc") + .succeeds() + .stdout_is(" 1\ta\n \n 2\tb\n \n 3\tc\n"); +} + +#[test] +fn test_repeated_header_numbering_flag() { + // -ha -ht should use -ht (number only nonempty lines in header) + new_ucmd!() + .args(&["-ha", "-ht"]) + .pipe_in("\\:\\:\\:\na\nb\n\nc") + .succeeds() + .stdout_is("\n 1\ta\n 2\tb\n \n 3\tc\n"); +} + +#[test] +fn test_repeated_footer_numbering_flag() { + // -fa -ft should use -ft (t=nonempty in footer) + new_ucmd!() + .args(&["-fa", "-ft"]) + .pipe_in("\\:\na\nb\n\nc") + .succeeds() + .stdout_is("\n 1\ta\n 2\tb\n \n 3\tc\n"); +} + +#[test] +fn test_repeated_number_format_flag() { + // -n ln -n rn should use -n rn (rn=right aligned) + new_ucmd!() + .args(&["-n", "ln", "-n", "rn"]) + .pipe_in("a\nb\nc") + .succeeds() + .stdout_is(" 1\ta\n 2\tb\n 3\tc\n"); +} + +#[test] +fn test_repeated_number_separator_flag() { + // -s ':' -s '|' should use -s '|' + new_ucmd!() + .args(&["-s", ":", "-s", "|"]) + .pipe_in("a\nb\nc") + .succeeds() + .stdout_is(" 1|a\n 2|b\n 3|c\n"); +} + +#[test] +fn test_repeated_number_width_flag() { + // -w 3 -w 8 should use -w 8 + new_ucmd!() + .args(&["-w", "3", "-w", "8"]) + .pipe_in("a\nb\nc") + .succeeds() + .stdout_is(" 1\ta\n 2\tb\n 3\tc\n"); +} + +#[test] +fn test_repeated_line_increment_flag() { + // -i 1 -i 5 should use -i 5 + new_ucmd!() + .args(&["-i", "1", "-i", "5"]) + .pipe_in("a\nb\nc") + .succeeds() + .stdout_is(" 1\ta\n 6\tb\n 11\tc\n"); +} + +#[test] +fn test_repeated_starting_line_number_flag() { + // -v 1 -v 10 should use -v 10 + new_ucmd!() + .args(&["-v", "1", "-v", "10"]) + .pipe_in("a\nb\nc") + .succeeds() + .stdout_is(" 10\ta\n 11\tb\n 12\tc\n"); +} + +#[test] +fn test_repeated_join_blank_lines_flag() { + // -l 1 -l 2 should use -l 2 + new_ucmd!() + .args(&["-l", "1", "-l", "2", "-ba"]) + .pipe_in("a\n\n\nb") + .succeeds() + .stdout_is(" 1\ta\n \n 2\t\n 3\tb\n"); +} + +#[test] +fn test_repeated_section_delimiter_flag() { + // -d ':' -d '|' should use -d '|' + new_ucmd!() + .args(&["-d", ":", "-d", "|"]) + .pipe_in("|:|:|:\na\nb\nc") + .succeeds() + .stdout_is("\n a\n b\n c\n"); +} diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 75dfdb0358d..13f80895a36 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -8,21 +8,11 @@ set -e -# Use GNU version for make, nproc, readlink and sed on *BSD -case "$OSTYPE" in - *bsd*) - MAKE="gmake" - NPROC="gnproc" - READLINK="greadlink" - SED="gsed" - ;; - *) - MAKE="make" - NPROC="nproc" - READLINK="readlink" - SED="sed" - ;; -esac +# Use system's GNU version for make, nproc, readlink and sed on *BSD +MAKE=$(command -v gmake||command -v make) +NPROC=$(command -v gnproc||command -v nproc) +READLINK=$(command -v greadlink||command -v readlink) +SED=$(command -v gsed||command -v sed) ME="${0}" ME_dir="$(dirname -- "$("${READLINK}" -fm -- "${ME}")")" @@ -49,24 +39,8 @@ path_GNU="$("${READLINK}" -fm -- "${path_GNU:-${path_UUTILS}/../gnu}")" ### -# On MacOS there is no system /usr/bin/timeout -# and trying to add it to /usr/bin (with symlink of copy binary) will fail unless system integrity protection is disabled (not ideal) -# ref: https://support.apple.com/en-us/102149 -# On MacOS the Homebrew coreutils could be installed and then "sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout" -# Set to /usr/local/bin/timeout instead if /usr/bin/timeout is not found -SYSTEM_TIMEOUT="timeout" -if [ -x /usr/bin/timeout ]; then - SYSTEM_TIMEOUT="/usr/bin/timeout" -elif [ -x /usr/local/bin/timeout ]; then - SYSTEM_TIMEOUT="/usr/local/bin/timeout" -fi - -SYSTEM_YES="yes" -if [ -x /usr/bin/yes ]; then - SYSTEM_YES="/usr/bin/yes" -elif [ -x /usr/local/bin/yes ]; then - SYSTEM_YES="/usr/local/bin/yes" -fi +SYSTEM_TIMEOUT=$(command -v timeout) +SYSTEM_YES=$(command -v yes) ### @@ -106,7 +80,8 @@ echo "UU_BUILD_DIR='${UU_BUILD_DIR}'" cd "${path_UUTILS}" && echo "[ pwd:'${PWD}' ]" export SELINUX_ENABLED # Run this script with=1 for testing SELinux -[ "${SELINUX_ENABLED}" = 1 ] && CARGO_FEATURE_FLAGS="${CARGO_FEATURE_FLAGS} selinux" +_configure_selinux=--without-selinux +[ "${SELINUX_ENABLED}" = 1 ] && CARGO_FEATURE_FLAGS="${CARGO_FEATURE_FLAGS} selinux" _configure_selinux=--with-selinux # Trim leading whitespace from feature flags CARGO_FEATURE_FLAGS="$(echo "${CARGO_FEATURE_FLAGS}" | sed -e 's/^[[:space:]]*//')" @@ -132,10 +107,8 @@ cd - # Pass the feature flags to make, which will pass them to cargo "${MAKE}" PROFILE="${UU_MAKE_PROFILE}" CARGOFLAGS="${CARGO_FEATURE_FLAGS}" -touch g -echo "stat with selinux support" -./target/debug/stat -c%C g || true -rm g +# min test for SELinux +[ ${SELINUX_ENABLED} = 1 ] && touch g && "${UU_MAKE_PROFILE}"/stat -c%C g && rm g cp "${UU_BUILD_DIR}/install" "${UU_BUILD_DIR}/ginstall" # The GNU tests rename this script before running, to avoid confusion with the make target # Create *sum binaries @@ -168,7 +141,7 @@ else # Change the PATH to test the uutils coreutils instead of the GNU coreutils sed -i "s/^[[:blank:]]*PATH=.*/ PATH='${UU_BUILD_DIR//\//\\/}\$(PATH_SEPARATOR)'\"\$\$PATH\" \\\/" tests/local.mk ./bootstrap --skip-po - ./configure --quiet --disable-gcc-warnings --disable-nls --disable-dependency-tracking --disable-bold-man-page-references + ./configure --quiet --disable-gcc-warnings --disable-nls --disable-dependency-tracking --disable-bold-man-page-references ${_configure_selinux} #Add timeout to to protect against hangs sed -i 's|^"\$@|'"${SYSTEM_TIMEOUT}"' 600 "\$@|' build-aux/test-driver sed -i 's| tr | /usr/bin/tr |' tests/init.sh diff --git a/util/show-utils.sh b/util/show-utils.sh index 0a41698d930..3cc48794030 100755 --- a/util/show-utils.sh +++ b/util/show-utils.sh @@ -5,14 +5,7 @@ # spell-checker:ignore (jq) deps startswith # Use GNU version for realpath on *BSD -case "$OSTYPE" in - *bsd*) - REALPATH="grealpath" - ;; - *) - REALPATH="realpath" - ;; -esac +REALPATH=$(command -v grealpath||command -v realpath) ME="${0}" ME_dir="$(dirname -- "${ME}")"