1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-31 04:57:45 +00:00

unexpand: remove crash! macro (#5588)

* unexpand_macro_fixed

* unexpand

* Remove crash

* Passed local test case

* Small changes

* unexpand: remove #[allow(clippy::cognitive_complexity)]

---------

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
This commit is contained in:
Akmal 2023-12-17 17:43:00 +02:00 committed by GitHub
parent 35ae43e71f
commit 052f38f14d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -14,8 +14,8 @@ use std::num::IntErrorKind;
use std::str::from_utf8; use std::str::from_utf8;
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UError, UResult}; use uucore::error::{FromIo, UError, UResult, USimpleError};
use uucore::{crash, crash_if_err, format_usage, help_about, help_usage}; use uucore::{crash_if_err, format_usage, help_about, help_usage};
const USAGE: &str = help_usage!("unexpand.md"); const USAGE: &str = help_usage!("unexpand.md");
const ABOUT: &str = help_about!("unexpand.md"); const ABOUT: &str = help_about!("unexpand.md");
@ -161,7 +161,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(expand_shortcuts(&args))?; let matches = uu_app().try_get_matches_from(expand_shortcuts(&args))?;
unexpand(&Options::new(&matches)?).map_err_context(String::new) unexpand(&Options::new(&matches)?)
} }
pub fn uu_app() -> Command { pub fn uu_app() -> Command {
@ -209,16 +209,13 @@ pub fn uu_app() -> Command {
) )
} }
fn open(path: &str) -> BufReader<Box<dyn Read + 'static>> { fn open(path: &str) -> UResult<BufReader<Box<dyn Read + 'static>>> {
let file_buf; let file_buf;
if path == "-" { if path == "-" {
BufReader::new(Box::new(stdin()) as Box<dyn Read>) Ok(BufReader::new(Box::new(stdin()) as Box<dyn Read>))
} else { } else {
file_buf = match File::open(path) { file_buf = File::open(path).map_err_context(|| path.to_string())?;
Ok(a) => a, Ok(BufReader::new(Box::new(file_buf) as Box<dyn Read>))
Err(e) => crash!(1, "{}: {}", path.maybe_quote(), e),
};
BufReader::new(Box::new(file_buf) as Box<dyn Read>)
} }
} }
@ -315,19 +312,13 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi
} }
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
fn unexpand(options: &Options) -> std::io::Result<()> { fn unexpand_line(
let mut output = BufWriter::new(stdout()); buf: &mut Vec<u8>,
let ts = &options.tabstops[..]; output: &mut BufWriter<std::io::Stdout>,
let mut buf = Vec::new(); options: &Options,
let lastcol = if ts.len() > 1 { *ts.last().unwrap() } else { 0 }; lastcol: usize,
ts: &[usize],
for file in &options.files { ) -> std::io::Result<()> {
let mut fh = open(file);
while match fh.read_until(b'\n', &mut buf) {
Ok(s) => s > 0,
Err(_) => !buf.is_empty(),
} {
let mut byte = 0; // offset into the buffer let mut byte = 0; // offset into the buffer
let mut col = 0; // the current column let mut col = 0; // the current column
let mut scol = 0; // the start col for the current span, i.e., the already-printed width let mut scol = 0; // the start col for the current span, i.e., the already-printed width
@ -337,22 +328,14 @@ fn unexpand(options: &Options) -> std::io::Result<()> {
while byte < buf.len() { while byte < buf.len() {
// when we have a finite number of columns, never convert past the last column // when we have a finite number of columns, never convert past the last column
if lastcol > 0 && col >= lastcol { if lastcol > 0 && col >= lastcol {
write_tabs( write_tabs(output, ts, scol, col, pctype == CharType::Tab, init, true);
&mut output,
ts,
scol,
col,
pctype == CharType::Tab,
init,
true,
);
output.write_all(&buf[byte..])?; output.write_all(&buf[byte..])?;
scol = col; scol = col;
break; break;
} }
// figure out how big the next char is, if it's UTF-8 // figure out how big the next char is, if it's UTF-8
let (ctype, cwidth, nbytes) = next_char_info(options.uflag, &buf, byte); let (ctype, cwidth, nbytes) = next_char_info(options.uflag, buf, byte);
// now figure out how many columns this char takes up, and maybe print it // now figure out how many columns this char takes up, and maybe print it
let tabs_buffered = init || options.aflag; let tabs_buffered = init || options.aflag;
@ -373,7 +356,7 @@ fn unexpand(options: &Options) -> std::io::Result<()> {
CharType::Other | CharType::Backspace => { CharType::Other | CharType::Backspace => {
// always // always
write_tabs( write_tabs(
&mut output, output,
ts, ts,
scol, scol,
col, col,
@ -401,20 +384,35 @@ fn unexpand(options: &Options) -> std::io::Result<()> {
} }
// write out anything remaining // write out anything remaining
write_tabs( write_tabs(output, ts, scol, col, pctype == CharType::Tab, init, true);
&mut output,
ts,
scol,
col,
pctype == CharType::Tab,
init,
true,
);
output.flush()?; output.flush()?;
buf.truncate(0); // clear out the buffer buf.truncate(0); // clear out the buffer
Ok(())
}
fn unexpand(options: &Options) -> UResult<()> {
let mut output = BufWriter::new(stdout());
let ts = &options.tabstops[..];
let mut buf = Vec::new();
let lastcol = if ts.len() > 1 { *ts.last().unwrap() } else { 0 };
for file in &options.files {
let mut fh = match open(file) {
Ok(reader) => reader,
Err(err) => {
return Err(USimpleError::new(1, err.to_string()));
}
};
while match fh.read_until(b'\n', &mut buf) {
Ok(s) => s > 0,
Err(_) => !buf.is_empty(),
} {
unexpand_line(&mut buf, &mut output, options, lastcol, ts)?;
} }
} }
output.flush() Ok(())
} }
#[cfg(test)] #[cfg(test)]