2023-08-21 10:49:27 +02:00
|
|
|
// This file is part of the uutils coreutils package.
|
|
|
|
|
//
|
|
|
|
|
// For the full copyright and license information, please view the LICENSE
|
|
|
|
|
// file that was distributed with this source code.
|
2023-05-24 13:28:30 +02:00
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
#[cfg(unix)]
|
|
|
|
|
use nix::unistd::{read, write};
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
use std::fs::{Permissions, set_permissions};
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
use std::os::unix::ffi::OsStrExt;
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
use std::os::unix::fs::PermissionsExt;
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
use uutests::util::pty_path;
|
2025-11-21 23:39:21 +00:00
|
|
|
#[cfg(unix)]
|
2025-07-01 01:52:57 +02:00
|
|
|
use uutests::{at_and_ucmd, new_ucmd};
|
2025-05-03 13:19:43 -07:00
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn run_more_with_pty(
|
|
|
|
|
args: &[&str],
|
|
|
|
|
file: &str,
|
|
|
|
|
content: &str,
|
|
|
|
|
) -> (uutests::util::UChild, std::os::fd::OwnedFd, String) {
|
|
|
|
|
let (path, controller, _replica) = pty_path();
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
at.write(file, content);
|
|
|
|
|
|
|
|
|
|
let mut child = ucmd
|
|
|
|
|
.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.args(args)
|
|
|
|
|
.arg(file)
|
|
|
|
|
.run_no_wait();
|
|
|
|
|
|
2026-01-01 02:20:50 +07:00
|
|
|
child.delay(200);
|
2025-11-21 23:14:10 +00:00
|
|
|
let mut output = vec![0u8; 1024];
|
|
|
|
|
let n = read(&controller, &mut output).unwrap();
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output[..n]).to_string();
|
|
|
|
|
|
|
|
|
|
(child, controller, output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn quit_more(controller: &std::os::fd::OwnedFd, mut child: uutests::util::UChild) {
|
|
|
|
|
write(controller, b"q").unwrap();
|
|
|
|
|
child.delay(50);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-03 13:19:43 -07:00
|
|
|
#[cfg(unix)]
|
2020-05-11 10:44:12 +02:00
|
|
|
#[test]
|
2025-05-03 13:19:43 -07:00
|
|
|
fn test_no_arg() {
|
2025-11-21 23:14:10 +00:00
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
new_ucmd!()
|
|
|
|
|
.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.fails()
|
|
|
|
|
.stderr_contains("more: bad usage");
|
2020-05-11 10:44:12 +02:00
|
|
|
}
|
2021-04-02 21:34:02 +01:00
|
|
|
|
2023-05-17 20:38:03 +02:00
|
|
|
#[test]
|
2025-11-21 23:14:10 +00:00
|
|
|
#[cfg(unix)]
|
2023-05-17 20:38:03 +02:00
|
|
|
fn test_valid_arg() {
|
2025-11-21 23:14:10 +00:00
|
|
|
let args_list: Vec<&[&str]> = vec![
|
|
|
|
|
&["-c"],
|
|
|
|
|
&["--clean-print"],
|
|
|
|
|
&["-p"],
|
|
|
|
|
&["--print-over"],
|
|
|
|
|
&["-s"],
|
|
|
|
|
&["--squeeze"],
|
|
|
|
|
&["-u"],
|
|
|
|
|
&["--plain"],
|
|
|
|
|
&["-n", "10"],
|
|
|
|
|
&["--lines", "0"],
|
|
|
|
|
&["--number", "0"],
|
|
|
|
|
&["-F", "10"],
|
|
|
|
|
&["--from-line", "0"],
|
|
|
|
|
&["-P", "something"],
|
|
|
|
|
&["--pattern", "-1"],
|
|
|
|
|
];
|
|
|
|
|
for args in args_list {
|
|
|
|
|
test_alive(args);
|
2023-05-29 14:36:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
#[cfg(unix)]
|
2025-05-03 13:19:43 -07:00
|
|
|
fn test_alive(args: &[&str]) {
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2025-11-21 23:14:10 +00:00
|
|
|
let (path, controller, _replica) = pty_path();
|
2025-05-03 13:19:43 -07:00
|
|
|
|
2025-08-04 21:39:02 +02:00
|
|
|
let content = "test content";
|
|
|
|
|
let file = "test_file";
|
|
|
|
|
at.write(file, content);
|
|
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
let mut child = ucmd
|
|
|
|
|
.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.args(args)
|
|
|
|
|
.arg(file)
|
|
|
|
|
.run_no_wait();
|
2025-08-04 21:39:02 +02:00
|
|
|
|
|
|
|
|
// wait for more to start and display the file
|
2025-11-21 23:14:10 +00:00
|
|
|
child.delay(100);
|
2025-08-04 21:39:02 +02:00
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
assert!(child.is_alive(), "Command should still be alive");
|
2025-08-04 21:39:02 +02:00
|
|
|
|
|
|
|
|
// cleanup
|
2025-11-21 23:14:10 +00:00
|
|
|
write(&controller, b"q").unwrap();
|
|
|
|
|
child.delay(50);
|
2025-05-03 13:19:43 -07:00
|
|
|
}
|
|
|
|
|
|
2023-05-29 14:36:18 +02:00
|
|
|
#[test]
|
2025-11-21 23:14:10 +00:00
|
|
|
#[cfg(unix)]
|
2023-05-29 14:36:18 +02:00
|
|
|
fn test_invalid_arg() {
|
2025-11-21 23:14:10 +00:00
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
new_ucmd!()
|
|
|
|
|
.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.arg("--invalid")
|
|
|
|
|
.fails();
|
2023-05-29 14:36:18 +02:00
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
new_ucmd!()
|
|
|
|
|
.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.arg("--lines")
|
|
|
|
|
.arg("-10")
|
|
|
|
|
.fails();
|
2023-06-05 18:48:45 +02:00
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
new_ucmd!()
|
|
|
|
|
.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.arg("--from-line")
|
|
|
|
|
.arg("-10")
|
|
|
|
|
.fails();
|
2023-05-17 20:38:03 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-06 22:03:44 +02:00
|
|
|
#[test]
|
2025-11-21 23:14:10 +00:00
|
|
|
#[cfg(unix)]
|
2025-05-03 13:19:43 -07:00
|
|
|
fn test_file_arg() {
|
2025-11-21 23:14:10 +00:00
|
|
|
// Directory as argument
|
|
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
new_ucmd!()
|
|
|
|
|
.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.arg(".")
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stderr_contains("'.' is a directory.");
|
2025-05-03 13:19:43 -07:00
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
// Single argument errors
|
|
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
at.mkdir_all("folder");
|
|
|
|
|
ucmd.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.arg("folder")
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stderr_contains("is a directory");
|
2025-05-03 13:19:43 -07:00
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
new_ucmd!()
|
|
|
|
|
.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.arg("nonexistent_file")
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stderr_contains("No such file or directory");
|
2025-05-03 13:19:43 -07:00
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
// Multiple nonexistent files
|
|
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
new_ucmd!()
|
|
|
|
|
.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.arg("file2")
|
|
|
|
|
.arg("file3")
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stderr_contains("file2")
|
|
|
|
|
.stderr_contains("file3");
|
2021-04-02 21:34:02 +01:00
|
|
|
}
|
2023-05-23 18:03:25 +02:00
|
|
|
|
|
|
|
|
#[test]
|
2025-11-21 23:14:10 +00:00
|
|
|
#[cfg(unix)]
|
2025-05-03 13:19:43 -07:00
|
|
|
fn test_invalid_file_perms() {
|
2025-11-21 23:14:10 +00:00
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
let permissions = Permissions::from_mode(0o244);
|
|
|
|
|
at.make_file("invalid-perms.txt");
|
|
|
|
|
set_permissions(at.plus("invalid-perms.txt"), permissions).unwrap();
|
|
|
|
|
ucmd.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.arg("invalid-perms.txt")
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stderr_contains("permission denied");
|
2023-05-23 18:03:25 +02:00
|
|
|
}
|
2025-08-08 14:57:19 +02:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
fn test_more_non_utf8_paths() {
|
2025-11-21 23:14:10 +00:00
|
|
|
let (path, _controller, _replica) = pty_path();
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
let file_name = std::ffi::OsStr::from_bytes(b"test_\xFF\xFE.txt");
|
|
|
|
|
// Create test file with normal name first
|
|
|
|
|
at.write(
|
|
|
|
|
&file_name.to_string_lossy(),
|
|
|
|
|
"test content for non-UTF-8 file",
|
|
|
|
|
);
|
2025-08-08 14:57:19 +02:00
|
|
|
|
2025-11-21 23:14:10 +00:00
|
|
|
// Test that more can handle non-UTF-8 filenames without crashing
|
|
|
|
|
ucmd.set_stdin(File::open(&path).unwrap())
|
|
|
|
|
.set_stdout(File::create(&path).unwrap())
|
|
|
|
|
.arg(file_name)
|
|
|
|
|
.succeeds();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_basic_display() {
|
|
|
|
|
let (child, controller, output) = run_more_with_pty(&[], "test.txt", "line1\nline2\nline3\n");
|
|
|
|
|
assert!(output.contains("line1"));
|
|
|
|
|
quit_more(&controller, child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_squeeze_blank_lines() {
|
|
|
|
|
let (child, controller, output) =
|
|
|
|
|
run_more_with_pty(&["-s"], "test.txt", "line1\n\n\n\nline2\n");
|
|
|
|
|
assert!(output.contains("line1"));
|
|
|
|
|
quit_more(&controller, child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_pattern_search() {
|
|
|
|
|
let (child, controller, output) = run_more_with_pty(
|
|
|
|
|
&["-P", "target"],
|
|
|
|
|
"test.txt",
|
|
|
|
|
"foo\nbar\nbaz\ntarget\nend\n",
|
|
|
|
|
);
|
|
|
|
|
assert!(output.contains("target"));
|
|
|
|
|
assert!(!output.contains("foo"));
|
|
|
|
|
quit_more(&controller, child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_from_line_option() {
|
|
|
|
|
let (child, controller, output) =
|
|
|
|
|
run_more_with_pty(&["-F", "2"], "test.txt", "line1\nline2\nline3\nline4\n");
|
|
|
|
|
assert!(output.contains("line2"));
|
|
|
|
|
assert!(!output.contains("line1"));
|
|
|
|
|
quit_more(&controller, child);
|
2025-08-08 14:57:19 +02:00
|
|
|
}
|