Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 52 additions & 27 deletions src/uu/head/src/head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use std::io::{self, BufWriter, Read, Seek, SeekFrom, Write};
use std::num::TryFromIntError;
#[cfg(unix)]
use std::os::fd::AsFd;
use std::path::{Path, PathBuf};
#[cfg(windows)]
use std::path::Path;
use std::path::PathBuf;
use thiserror::Error;
use uucore::display::{Quotable, print_verbatim};
use uucore::error::{FromIo, UError, UResult, USimpleError};
Expand Down Expand Up @@ -447,34 +449,66 @@ fn uu_head(options: &HeadOptions) -> UResult<()> {

Ok(())
} else {
// Stat the path first so we know whether to print the header.
// When 0 bytes or 0 lines are requested, there is nothing to
// read, so we should succeed on directories just like GNU head
// does. Skip opening the file entirely in that case.
let zero_output = matches!(options.mode, Mode::FirstBytes(0) | Mode::FirstLines(0));

// GNU head prints "==> name <==" for existing files and
// directories, but NOT for nonexistent ones — those produce
// only an error message.
let metadata = match Path::new(file).metadata() {
Ok(m) => m,
let mut print_header = || -> UResult<()> {
if (options.files.len() > 1 && !options.quiet) || options.verbose {
if !first {
writeln!(stdout)?;
}
write!(stdout, "==> ")?;
print_verbatim(file).unwrap();
writeln!(stdout, " <==")?;
first = false;
}
Ok(())
};

let mut file_handle = match File::open(file) {
Ok(f) => f,
Err(err) => {
#[cfg(windows)]
// On Windows, `File::open` on a directory fails with "Permission denied").
if err.kind() == io::ErrorKind::PermissionDenied {
if let Ok(m) = Path::new(file).metadata() {
if m.is_dir() {
// We need to print the header, as we have an existing directory
print_header()?;
if !zero_output {
show!(USimpleError::new(
1,
translate!("head-error-reading-file", "name" => file.quote(), "err" => "Is a directory")
));
}
continue;
}
}
}

show!(err.map_err_context(
|| translate!("head-error-cannot-open", "name" => file.quote())
));
continue;
}
};
if (options.files.len() > 1 && !options.quiet) || options.verbose {
if !first {
writeln!(stdout)?;

let metadata = match file_handle.metadata() {
Ok(m) => m,
Err(err) => {
show!(err.map_err_context(
|| translate!("head-error-cannot-open", "name" => file.quote())
));
continue;
}
write!(stdout, "==> ")?;
print_verbatim(file).unwrap();
writeln!(stdout, " <==")?;
first = false;
}
// When 0 bytes or 0 lines are requested, there is nothing to
// read, so we should succeed on directories just like GNU head
// does. Skip opening the file entirely in that case (also
// avoids platform differences: on Windows, `File::open` on a
// directory fails with "Permission denied").
let zero_output = matches!(options.mode, Mode::FirstBytes(0) | Mode::FirstLines(0));
};

print_header()?;
if metadata.is_dir() {
if !zero_output {
show!(USimpleError::new(
Expand All @@ -484,15 +518,6 @@ fn uu_head(options: &HeadOptions) -> UResult<()> {
}
continue;
}
let mut file_handle = match File::open(file) {
Ok(f) => f,
Err(err) => {
show!(err.map_err_context(
|| translate!("head-error-cannot-open", "name" => file.quote())
));
continue;
}
};
head_file(&mut file_handle, options)?;
Ok(())
};
Expand Down
Loading