From a3aba81725cc21c6526fac5c12c24c662758c00f Mon Sep 17 00:00:00 2001 From: allaboutevemirolive Date: Mon, 27 Nov 2023 10:05:23 -0500 Subject: [PATCH 1/6] unexpand_macro_fixed --- src/uu/unexpand/src/unexpand.rs | 175 ++++++++++++++++---------------- 1 file changed, 90 insertions(+), 85 deletions(-) diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 11ad43060a6..fe348fac1e4 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -14,8 +14,8 @@ use std::num::IntErrorKind; use std::str::from_utf8; use unicode_width::UnicodeWidthChar; use uucore::display::Quotable; -use uucore::error::{FromIo, UError, UResult}; -use uucore::{crash, crash_if_err, format_usage, help_about, help_usage}; +use uucore::error::{FromIo, UError, UResult, USimpleError}; +use uucore::{crash_if_err, format_usage, help_about, help_usage}; const USAGE: &str = help_usage!("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))?; - unexpand(&Options::new(&matches)?).map_err_context(String::new) + unexpand(&Options::new(&matches)?)?.map_err_context(String::new) } pub fn uu_app() -> Command { @@ -209,16 +209,19 @@ pub fn uu_app() -> Command { ) } -fn open(path: &str) -> BufReader> { +fn open(path: &str) -> UResult>> { let file_buf; if path == "-" { - BufReader::new(Box::new(stdin()) as Box) + Ok(BufReader::new(Box::new(stdin()) as Box)) } else { file_buf = match File::open(path) { Ok(a) => a, - Err(e) => crash!(1, "{}: {}", path.maybe_quote(), e), + Err(e) => { + let err = format!("{}: {}", path.maybe_quote(), e); + return Err(USimpleError::new(1,err)); + } }; - BufReader::new(Box::new(file_buf) as Box) + Ok(BufReader::new(Box::new(file_buf) as Box)) } } @@ -315,7 +318,7 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi } #[allow(clippy::cognitive_complexity)] -fn unexpand(options: &Options) -> std::io::Result<()> { +fn unexpand(options: &Options) -> UResult> { let mut output = BufWriter::new(stdout()); let ts = &options.tabstops[..]; let mut buf = Vec::new(); @@ -324,54 +327,17 @@ fn unexpand(options: &Options) -> std::io::Result<()> { for file in &options.files { 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 col = 0; // the current column - let mut scol = 0; // the start col for the current span, i.e., the already-printed width - let mut init = true; // are we at the start of the line? - let mut pctype = CharType::Other; - - while byte < buf.len() { - // when we have a finite number of columns, never convert past the last column - if lastcol > 0 && col >= lastcol { - write_tabs( - &mut output, - ts, - scol, - col, - pctype == CharType::Tab, - init, - true, - ); - output.write_all(&buf[byte..])?; - scol = col; - break; - } - - // figure out how big the next char is, if it's UTF-8 - 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 - let tabs_buffered = init || options.aflag; - match ctype { - CharType::Space | CharType::Tab => { - // compute next col, but only write space or tab chars if not buffering - col += if ctype == CharType::Space { - 1 - } else { - next_tabstop(ts, col).unwrap_or(1) - }; - - if !tabs_buffered { - output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // now printed up to this column - } - } - CharType::Other | CharType::Backspace => { - // always + while let Ok(s) = fh.as_mut().unwrap().read_until(b'\n', &mut buf) { + if s > 0 { + let mut byte = 0; // offset into the buffer + 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 init = true; // are we at the start of the line? + let mut pctype = CharType::Other; + + while byte < buf.len() { + // when we have a finite number of columns, never convert past the last column + if lastcol > 0 && col >= lastcol { write_tabs( &mut output, ts, @@ -379,42 +345,81 @@ fn unexpand(options: &Options) -> std::io::Result<()> { col, pctype == CharType::Tab, init, - options.aflag, + true, ); - init = false; // no longer at the start of a line - col = if ctype == CharType::Other { - // use computed width - col + cwidth - } else if col > 0 { - // Backspace case, but only if col > 0 - col - 1 - } else { - 0 - }; - output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // we've now printed up to this column + output.write_all(&buf[byte..])?; + scol = col; + break; + } + + // figure out how big the next char is, if it's UTF-8 + 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 + let tabs_buffered = init || options.aflag; + match ctype { + CharType::Space | CharType::Tab => { + // compute next col, but only write space or tab chars if not buffering + col += if ctype == CharType::Space { + 1 + } else { + next_tabstop(ts, col).unwrap_or(1) + }; + + if !tabs_buffered { + output.write_all(&buf[byte..byte + nbytes])?; + scol = col; // now printed up to this column + } + } + CharType::Other | CharType::Backspace => { + // always + write_tabs( + &mut output, + ts, + scol, + col, + pctype == CharType::Tab, + init, + options.aflag, + ); + init = false; // no longer at the start of a line + col = if ctype == CharType::Other { + // use computed width + col + cwidth + } else if col > 0 { + // Backspace case, but only if col > 0 + col - 1 + } else { + 0 + }; + output.write_all(&buf[byte..byte + nbytes])?; + scol = col; // we've now printed up to this column + } } + + byte += nbytes; // move on to the next char + pctype = ctype; // save the previous type } - byte += nbytes; // move on to next char - pctype = ctype; // save the previous type + // write out anything remaining + write_tabs( + &mut output, + ts, + scol, + col, + pctype == CharType::Tab, + init, + true, + ); + output.flush()?; + buf.clear(); // clear out the buffer for the next iteration + } else { + // handle case where read_until returns 0 (end of file or empty line) + break; } - - // write out anything remaining - write_tabs( - &mut output, - ts, - scol, - col, - pctype == CharType::Tab, - init, - true, - ); - output.flush()?; - buf.truncate(0); // clear out the buffer } } - output.flush() + Ok(output.flush()) } #[cfg(test)] From e4b9c1b1036e13f2a42837e0dd96e9cebd967a53 Mon Sep 17 00:00:00 2001 From: allaboutevemirolive Date: Mon, 27 Nov 2023 10:42:07 -0500 Subject: [PATCH 2/6] unexpand --- src/uu/unexpand/src/unexpand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index fe348fac1e4..772e7813f04 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -218,7 +218,7 @@ fn open(path: &str) -> UResult>> { Ok(a) => a, Err(e) => { let err = format!("{}: {}", path.maybe_quote(), e); - return Err(USimpleError::new(1,err)); + return Err(USimpleError::new(1, err)); } }; Ok(BufReader::new(Box::new(file_buf) as Box)) From 6ff05f5613754af7b32d62189c35d96ee1bcd2bc Mon Sep 17 00:00:00 2001 From: allaboutevemirolive Date: Fri, 15 Dec 2023 19:35:16 +0200 Subject: [PATCH 3/6] Remove crash --- src/uu/unexpand/src/unexpand.rs | 166 ++++++++++++++++---------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 772e7813f04..03d688cf044 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -161,7 +161,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(expand_shortcuts(&args))?; - unexpand(&Options::new(&matches)?)?.map_err_context(String::new) + unexpand(&Options::new(&matches)?).map_err_context(String::new) } pub fn uu_app() -> Command { @@ -318,26 +318,65 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi } #[allow(clippy::cognitive_complexity)] -fn unexpand(options: &Options) -> UResult> { +fn unexpand(options: &Options) -> std::io::Result<()> { 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 = open(file); - - while let Ok(s) = fh.as_mut().unwrap().read_until(b'\n', &mut buf) { - if s > 0 { - let mut byte = 0; // offset into the buffer - 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 init = true; // are we at the start of the line? - let mut pctype = CharType::Other; - - while byte < buf.len() { - // when we have a finite number of columns, never convert past the last column - if lastcol > 0 && col >= lastcol { + let fh = open(file); + + let mut fh = fh.unwrap(); + + 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 col = 0; // the current column + let mut scol = 0; // the start col for the current span, i.e., the already-printed width + let mut init = true; // are we at the start of the line? + let mut pctype = CharType::Other; + + while byte < buf.len() { + // when we have a finite number of columns, never convert past the last column + if lastcol > 0 && col >= lastcol { + write_tabs( + &mut output, + ts, + scol, + col, + pctype == CharType::Tab, + init, + true, + ); + output.write_all(&buf[byte..])?; + scol = col; + break; + } + + // figure out how big the next char is, if it's UTF-8 + 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 + let tabs_buffered = init || options.aflag; + match ctype { + CharType::Space | CharType::Tab => { + // compute next col, but only write space or tab chars if not buffering + col += if ctype == CharType::Space { + 1 + } else { + next_tabstop(ts, col).unwrap_or(1) + }; + + if !tabs_buffered { + output.write_all(&buf[byte..byte + nbytes])?; + scol = col; // now printed up to this column + } + } + CharType::Other | CharType::Backspace => { + // always write_tabs( &mut output, ts, @@ -345,81 +384,42 @@ fn unexpand(options: &Options) -> UResult> { col, pctype == CharType::Tab, init, - true, + options.aflag, ); - output.write_all(&buf[byte..])?; - scol = col; - break; + init = false; // no longer at the start of a line + col = if ctype == CharType::Other { + // use computed width + col + cwidth + } else if col > 0 { + // Backspace case, but only if col > 0 + col - 1 + } else { + 0 + }; + output.write_all(&buf[byte..byte + nbytes])?; + scol = col; // we've now printed up to this column } - - // figure out how big the next char is, if it's UTF-8 - 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 - let tabs_buffered = init || options.aflag; - match ctype { - CharType::Space | CharType::Tab => { - // compute next col, but only write space or tab chars if not buffering - col += if ctype == CharType::Space { - 1 - } else { - next_tabstop(ts, col).unwrap_or(1) - }; - - if !tabs_buffered { - output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // now printed up to this column - } - } - CharType::Other | CharType::Backspace => { - // always - write_tabs( - &mut output, - ts, - scol, - col, - pctype == CharType::Tab, - init, - options.aflag, - ); - init = false; // no longer at the start of a line - col = if ctype == CharType::Other { - // use computed width - col + cwidth - } else if col > 0 { - // Backspace case, but only if col > 0 - col - 1 - } else { - 0 - }; - output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // we've now printed up to this column - } - } - - byte += nbytes; // move on to the next char - pctype = ctype; // save the previous type } - // write out anything remaining - write_tabs( - &mut output, - ts, - scol, - col, - pctype == CharType::Tab, - init, - true, - ); - output.flush()?; - buf.clear(); // clear out the buffer for the next iteration - } else { - // handle case where read_until returns 0 (end of file or empty line) - break; + byte += nbytes; // move on to next char + pctype = ctype; // save the previous type } + + // write out anything remaining + write_tabs( + &mut output, + ts, + scol, + col, + pctype == CharType::Tab, + init, + true, + ); + output.flush()?; + buf.truncate(0); // clear out the buffer } } - Ok(output.flush()) + output.flush() } #[cfg(test)] From 8ae2a3b97daf24dec513a2ff0d8cfca37942bc32 Mon Sep 17 00:00:00 2001 From: allaboutevemirolive Date: Sun, 17 Dec 2023 07:22:09 +0200 Subject: [PATCH 4/6] Passed local test case --- src/uu/unexpand/src/unexpand.rs | 190 ++++++++++++++++---------------- 1 file changed, 93 insertions(+), 97 deletions(-) diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 03d688cf044..ab18323d70c 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -161,7 +161,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { 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 { @@ -214,13 +214,7 @@ fn open(path: &str) -> UResult>> { if path == "-" { Ok(BufReader::new(Box::new(stdin()) as Box)) } else { - file_buf = match File::open(path) { - Ok(a) => a, - Err(e) => { - let err = format!("{}: {}", path.maybe_quote(), e); - return Err(USimpleError::new(1, err)); - } - }; + file_buf = File::open(path).map_err_context(|| path.to_string())?; Ok(BufReader::new(Box::new(file_buf) as Box)) } } @@ -318,108 +312,110 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi } #[allow(clippy::cognitive_complexity)] -fn unexpand(options: &Options) -> std::io::Result<()> { +fn unexpand_line( + buf: &mut Vec, + output: &mut BufWriter, + options: &Options, + lastcol: usize, + ts: &[usize], +) -> std::io::Result<()> { + let mut byte = 0; // offset into the buffer + 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 init = true; // are we at the start of the line? + let mut pctype = CharType::Other; + + while byte < buf.len() { + // when we have a finite number of columns, never convert past the last column + if lastcol > 0 && col >= lastcol { + write_tabs(output, ts, scol, col, pctype == CharType::Tab, init, true); + output.write_all(&buf[byte..])?; + scol = col; + break; + } + + // figure out how big the next char is, if it's UTF-8 + 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 + let tabs_buffered = init || options.aflag; + match ctype { + CharType::Space | CharType::Tab => { + // compute next col, but only write space or tab chars if not buffering + col += if ctype == CharType::Space { + 1 + } else { + next_tabstop(ts, col).unwrap_or(1) + }; + + if !tabs_buffered { + output.write_all(&buf[byte..byte + nbytes])?; + scol = col; // now printed up to this column + } + } + CharType::Other | CharType::Backspace => { + // always + write_tabs( + output, + ts, + scol, + col, + pctype == CharType::Tab, + init, + options.aflag, + ); + init = false; // no longer at the start of a line + col = if ctype == CharType::Other { + // use computed width + col + cwidth + } else if col > 0 { + // Backspace case, but only if col > 0 + col - 1 + } else { + 0 + }; + output.write_all(&buf[byte..byte + nbytes])?; + scol = col; // we've now printed up to this column + } + } + + byte += nbytes; // move on to next char + pctype = ctype; // save the previous type + } + + // write out anything remaining + write_tabs(output, ts, scol, col, pctype == CharType::Tab, init, true); + output.flush()?; + buf.truncate(0); // clear out the buffer + + Ok(()) +} + +#[allow(clippy::cognitive_complexity)] +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 fh = open(file); - - let mut fh = fh.unwrap(); + let mut fh = match open(file) { + Ok(reader) => reader, + Err(err) => { + let err = format!("{}", err); + return Err(USimpleError::new(1, err)); + } + }; 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 col = 0; // the current column - let mut scol = 0; // the start col for the current span, i.e., the already-printed width - let mut init = true; // are we at the start of the line? - let mut pctype = CharType::Other; - - while byte < buf.len() { - // when we have a finite number of columns, never convert past the last column - if lastcol > 0 && col >= lastcol { - write_tabs( - &mut output, - ts, - scol, - col, - pctype == CharType::Tab, - init, - true, - ); - output.write_all(&buf[byte..])?; - scol = col; - break; - } - - // figure out how big the next char is, if it's UTF-8 - 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 - let tabs_buffered = init || options.aflag; - match ctype { - CharType::Space | CharType::Tab => { - // compute next col, but only write space or tab chars if not buffering - col += if ctype == CharType::Space { - 1 - } else { - next_tabstop(ts, col).unwrap_or(1) - }; - - if !tabs_buffered { - output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // now printed up to this column - } - } - CharType::Other | CharType::Backspace => { - // always - write_tabs( - &mut output, - ts, - scol, - col, - pctype == CharType::Tab, - init, - options.aflag, - ); - init = false; // no longer at the start of a line - col = if ctype == CharType::Other { - // use computed width - col + cwidth - } else if col > 0 { - // Backspace case, but only if col > 0 - col - 1 - } else { - 0 - }; - output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // we've now printed up to this column - } - } - - byte += nbytes; // move on to next char - pctype = ctype; // save the previous type - } - - // write out anything remaining - write_tabs( - &mut output, - ts, - scol, - col, - pctype == CharType::Tab, - init, - true, - ); - output.flush()?; - buf.truncate(0); // clear out the buffer + unexpand_line(&mut buf, &mut output, options, lastcol, ts) + .map_err_context(|| "failed to write output".to_string())?; } } - output.flush() + Ok(()) } #[cfg(test)] From 58151b94b3cf1167504a203c09eb91ab34b3b83d Mon Sep 17 00:00:00 2001 From: allaboutevemirolive Date: Sun, 17 Dec 2023 16:55:25 +0200 Subject: [PATCH 5/6] Small changes --- src/uu/unexpand/src/unexpand.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index ab18323d70c..c70a4ea1201 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -402,8 +402,7 @@ fn unexpand(options: &Options) -> UResult<()> { let mut fh = match open(file) { Ok(reader) => reader, Err(err) => { - let err = format!("{}", err); - return Err(USimpleError::new(1, err)); + return Err(USimpleError::new(1, err.to_string())); } }; @@ -411,8 +410,7 @@ fn unexpand(options: &Options) -> UResult<()> { Ok(s) => s > 0, Err(_) => !buf.is_empty(), } { - unexpand_line(&mut buf, &mut output, options, lastcol, ts) - .map_err_context(|| "failed to write output".to_string())?; + unexpand_line(&mut buf, &mut output, options, lastcol, ts)?; } } Ok(()) From 50e9316171ba79b050d8bb8f528062eec4bf3d67 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sun, 17 Dec 2023 16:04:08 +0100 Subject: [PATCH 6/6] unexpand: remove #[allow(clippy::cognitive_complexity)] --- src/uu/unexpand/src/unexpand.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index c70a4ea1201..66d9a0187b6 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -391,7 +391,6 @@ fn unexpand_line( Ok(()) } -#[allow(clippy::cognitive_complexity)] fn unexpand(options: &Options) -> UResult<()> { let mut output = BufWriter::new(stdout()); let ts = &options.tabstops[..];