-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
head: make head fail when writing to /dev/full #7068
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,22 +3,21 @@ | |
// For the full copyright and license information, please view the LICENSE | ||
// file that was distributed with this source code. | ||
|
||
// spell-checker:ignore (vars) BUFWRITER seekable | ||
// spell-checker:ignore (vars) seekable | ||
|
||
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command}; | ||
use std::ffi::OsString; | ||
use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write}; | ||
use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; | ||
use std::num::TryFromIntError; | ||
use thiserror::Error; | ||
use uucore::display::Quotable; | ||
use uucore::error::{FromIo, UResult, USimpleError}; | ||
use uucore::error::{FromIo, UError, UResult}; | ||
use uucore::line_ending::LineEnding; | ||
use uucore::lines::lines; | ||
use uucore::{format_usage, help_about, help_usage, show}; | ||
|
||
const BUF_SIZE: usize = 65536; | ||
|
||
/// The capacity in bytes for buffered writers. | ||
const BUFWRITER_CAPACITY: usize = 16_384; // 16 kilobytes | ||
|
||
const ABOUT: &str = help_about!("head.md"); | ||
const USAGE: &str = help_usage!("head.md"); | ||
|
||
|
@@ -37,6 +36,36 @@ mod take; | |
use take::take_all_but; | ||
use take::take_lines; | ||
|
||
#[derive(Error, Debug)] | ||
enum HeadError { | ||
/// Wrapper around `io::Error` | ||
#[error("error reading {name}: {err}")] | ||
Io { name: String, err: io::Error }, | ||
|
||
#[error("parse error: {0}")] | ||
ParseError(String), | ||
|
||
#[error("bad argument encoding")] | ||
BadEncoding, | ||
|
||
#[error("{0}: number of -bytes or -lines is too large")] | ||
NumTooLarge(#[from] TryFromIntError), | ||
|
||
#[error("clap error: {0}")] | ||
Clap(#[from] clap::Error), | ||
|
||
#[error("{0}")] | ||
MatchOption(String), | ||
} | ||
|
||
impl UError for HeadError { | ||
fn code(&self) -> i32 { | ||
1 | ||
} | ||
} | ||
|
||
type HeadResult<T> = Result<T, HeadError>; | ||
|
||
pub fn uu_app() -> Command { | ||
Command::new(uucore::util_name()) | ||
.version(crate_version!()) | ||
|
@@ -152,30 +181,27 @@ impl Mode { | |
|
||
fn arg_iterate<'a>( | ||
mut args: impl uucore::Args + 'a, | ||
) -> UResult<Box<dyn Iterator<Item = OsString> + 'a>> { | ||
) -> HeadResult<Box<dyn Iterator<Item = OsString> + 'a>> { | ||
// argv[0] is always present | ||
let first = args.next().unwrap(); | ||
if let Some(second) = args.next() { | ||
if let Some(s) = second.to_str() { | ||
match parse::parse_obsolete(s) { | ||
Some(Ok(iter)) => Ok(Box::new(vec![first].into_iter().chain(iter).chain(args))), | ||
Some(Err(e)) => match e { | ||
parse::ParseError::Syntax => Err(USimpleError::new( | ||
1, | ||
format!("bad argument format: {}", s.quote()), | ||
)), | ||
parse::ParseError::Overflow => Err(USimpleError::new( | ||
1, | ||
format!( | ||
"invalid argument: {} Value too large for defined datatype", | ||
s.quote() | ||
), | ||
)), | ||
parse::ParseError::Syntax => Err(HeadError::ParseError(format!( | ||
"bad argument format: {}", | ||
s.quote() | ||
))), | ||
parse::ParseError::Overflow => Err(HeadError::ParseError(format!( | ||
"invalid argument: {} Value too large for defined datatype", | ||
s.quote() | ||
))), | ||
}, | ||
None => Ok(Box::new(vec![first, second].into_iter().chain(args))), | ||
} | ||
} else { | ||
Err(USimpleError::new(1, "bad argument encoding".to_owned())) | ||
Err(HeadError::BadEncoding) | ||
} | ||
} else { | ||
Ok(Box::new(vec![first].into_iter())) | ||
|
@@ -226,6 +252,11 @@ where | |
|
||
io::copy(&mut reader, &mut stdout)?; | ||
|
||
// Make sure we finish writing everything to the target before | ||
// exiting. Otherwise, when Rust is implicitly flushing, any | ||
// error will be silently ignored. | ||
stdout.flush()?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe add a comment explaining why the flush is important There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added |
||
|
||
Ok(()) | ||
} | ||
|
||
|
@@ -234,11 +265,14 @@ fn read_n_lines(input: &mut impl std::io::BufRead, n: u64, separator: u8) -> std | |
let mut reader = take_lines(input, n, separator); | ||
|
||
// Write those bytes to `stdout`. | ||
let stdout = std::io::stdout(); | ||
let stdout = stdout.lock(); | ||
let mut writer = BufWriter::with_capacity(BUFWRITER_CAPACITY, stdout); | ||
let mut stdout = std::io::stdout(); | ||
|
||
io::copy(&mut reader, &mut stdout)?; | ||
|
||
io::copy(&mut reader, &mut writer)?; | ||
// Make sure we finish writing everything to the target before | ||
// exiting. Otherwise, when Rust is implicitly flushing, any | ||
// error will be silently ignored. | ||
stdout.flush()?; | ||
|
||
Ok(()) | ||
} | ||
|
@@ -247,10 +281,7 @@ fn catch_too_large_numbers_in_backwards_bytes_or_lines(n: u64) -> Option<usize> | |
match usize::try_from(n) { | ||
Ok(value) => Some(value), | ||
Err(e) => { | ||
show!(USimpleError::new( | ||
1, | ||
format!("{e}: number of -bytes or -lines is too large") | ||
)); | ||
show!(HeadError::NumTooLarge(e)); | ||
None | ||
} | ||
} | ||
|
@@ -511,16 +542,17 @@ fn uu_head(options: &HeadOptions) -> UResult<()> { | |
head_file(&mut file, options) | ||
} | ||
}; | ||
if res.is_err() { | ||
if let Err(e) = res { | ||
let name = if file.as_str() == "-" { | ||
"standard input" | ||
} else { | ||
file | ||
}; | ||
show!(USimpleError::new( | ||
1, | ||
format!("error reading {name}: Input/output error") | ||
)); | ||
return Err(HeadError::Io { | ||
name: name.to_string(), | ||
err: e, | ||
} | ||
.into()); | ||
} | ||
first = false; | ||
} | ||
|
@@ -537,7 +569,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { | |
let args = match HeadOptions::get_from(&matches) { | ||
Ok(o) => o, | ||
Err(s) => { | ||
return Err(USimpleError::new(1, s)); | ||
return Err(HeadError::MatchOption(s).into()); | ||
} | ||
}; | ||
uu_head(&args) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
much better, thanks :)
I think we should move all the quick_error to thiserror in the code base if you are interested :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that'd be a lot more consistent. I'd definitely do that