diff --git a/src/uu_hashsum/digest.rs.html b/src/uu_hashsum/digest.rs.html index 8d197150a..03a9a3607 100644 --- a/src/uu_hashsum/digest.rs.html +++ b/src/uu_hashsum/digest.rs.html @@ -243,6 +243,75 @@ 243 244 245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314
 // spell-checker:ignore memmem
 //! Implementations of digest functions, like md5 and sha1.
@@ -435,13 +504,31 @@
     /// "\n" before passing input bytes to the [`digest`].
     #[allow(dead_code)]
     binary: bool,
-    // TODO This is dead code only on non-Windows operating systems. It
-    // might be better to use a `#[cfg(windows)]` guard here.
+
+    /// Whether the previous
+    #[allow(dead_code)]
+    was_last_character_carriage_return: bool,
+    // TODO These are dead code only on non-Windows operating systems.
+    // It might be better to use a `#[cfg(windows)]` guard here.
 }
 
 impl<'a> DigestWriter<'a> {
     pub fn new(digest: &'a mut Box<dyn Digest>, binary: bool) -> DigestWriter {
-        DigestWriter { digest, binary }
+        let was_last_character_carriage_return = false;
+        DigestWriter {
+            digest,
+            binary,
+            was_last_character_carriage_return,
+        }
+    }
+
+    pub fn finalize(&mut self) -> bool {
+        if self.was_last_character_carriage_return {
+            self.digest.input(&[b'\r']);
+            true
+        } else {
+            false
+        }
     }
 }
 
@@ -459,22 +546,40 @@
             return Ok(buf.len());
         }
 
-        // In Windows text mode, replace each occurrence of "\r\n"
-        // with "\n".
+        // The remaining code handles Windows text mode, where we must
+        // replace each occurrence of "\r\n" with "\n".
         //
-        // Find all occurrences of "\r\n", inputting the slice just
-        // before the "\n" in the previous instance of "\r\n" and
-        // the beginning of this "\r\n".
-        //
-        // FIXME This fails if one call to `write()` ends with the
-        // "\r" and the next call to `write()` begins with the "\n".
+        // First, if the last character written was "\r" and the first
+        // character in the current buffer to write is not "\n", then we
+        // need to write the "\r" that we buffered from the previous
+        // call to `write()`.
         let n = buf.len();
+        if self.was_last_character_carriage_return && n > 0 && buf[0] != b'\n' {
+            self.digest.input(&[b'\r']);
+        }
+
+        // Next, find all occurrences of "\r\n", inputting the slice
+        // just before the "\n" in the previous instance of "\r\n" and
+        // the beginning of this "\r\n".
         let mut i_prev = 0;
         for i in memmem::find_iter(buf, b"\r\n") {
             self.digest.input(&buf[i_prev..i]);
             i_prev = i + 1;
         }
-        self.digest.input(&buf[i_prev..n]);
+
+        // Finally, check whether the last character is "\r". If so,
+        // buffer it until we know that the next character is not "\n",
+        // which can only be known on the next call to `write()`.
+        //
+        // This all assumes that `write()` will be called on adjacent
+        // blocks of the input.
+        if n > 0 && buf[n - 1] == b'\r' {
+            self.was_last_character_carriage_return = true;
+            self.digest.input(&buf[i_prev..n - 1]);
+        } else {
+            self.was_last_character_carriage_return = false;
+            self.digest.input(&buf[i_prev..n]);
+        }
 
         // Even though we dropped a "\r" for each "\r\n" we found, we
         // still report the number of bytes written as `n`. This is
@@ -489,6 +594,39 @@
         Ok(())
     }
 }
+
+#[cfg(test)]
+mod tests {
+
+    /// Test for replacing a "\r\n" sequence with "\n" when the "\r" is
+    /// at the end of one block and the "\n" is at the beginning of the
+    /// next block, when reading in blocks.
+    #[cfg(windows)]
+    #[test]
+    fn test_crlf_across_blocks() {
+        use std::io::Write;
+
+        use crate::digest::Digest;
+        use crate::digest::DigestWriter;
+
+        // Writing "\r" in one call to `write()`, and then "\n" in another.
+        let mut digest = Box::new(md5::Context::new()) as Box<dyn Digest>;
+        let mut writer_crlf = DigestWriter::new(&mut digest, false);
+        writer_crlf.write_all(&[b'\r']).unwrap();
+        writer_crlf.write_all(&[b'\n']).unwrap();
+        writer_crlf.finalize();
+        let result_crlf = digest.result_str();
+
+        // We expect "\r\n" to be replaced with "\n" in text mode on Windows.
+        let mut digest = Box::new(md5::Context::new()) as Box<dyn Digest>;
+        let mut writer_lf = DigestWriter::new(&mut digest, false);
+        writer_lf.write_all(&[b'\n']).unwrap();
+        writer_lf.finalize();
+        let result_lf = digest.result_str();
+
+        assert_eq!(result_crlf, result_lf);
+    }
+}
 
diff --git a/src/uu_hashsum/hashsum.rs.html b/src/uu_hashsum/hashsum.rs.html index d778748f0..8b5e8c362 100644 --- a/src/uu_hashsum/hashsum.rs.html +++ b/src/uu_hashsum/hashsum.rs.html @@ -624,6 +624,14 @@ 624 625 626 +627 +628 +629 +630 +631 +632 +633 +634
 //  * This file is part of the uutils coreutils package.
 //  *
@@ -1238,8 +1246,16 @@
     // If `binary` is `false` and the operating system is Windows, then
     // `DigestWriter` replaces "\r\n" with "\n" before it writes the
     // bytes into `digest`. Otherwise, it just inserts the bytes as-is.
+    //
+    // In order to support replacing "\r\n", we must call `finalize()`
+    // in order to support the possibility that the last character read
+    // from the reader was "\r". (This character gets buffered by
+    // `DigestWriter` and only written if the following character is
+    // "\n". But when "\r" is the last character read, we need to force
+    // it to be written.)
     let mut digest_writer = DigestWriter::new(digest, binary);
     std::io::copy(reader, &mut digest_writer)?;
+    digest_writer.finalize();
 
     if digest.output_bits() > 0 {
         Ok(digest.result_str())
diff --git a/src/uu_head/head.rs.html b/src/uu_head/head.rs.html
index 31d680eb1..f660693dd 100644
--- a/src/uu_head/head.rs.html
+++ b/src/uu_head/head.rs.html
@@ -589,7 +589,6 @@
 589
 590
 591
-592
 
 //  * This file is part of the uutils coreutils package.
 //  *
@@ -612,7 +611,6 @@
 const ABOUT: &str = "\
                      Print the first 10 lines of each FILE to standard output.\n\
                      With more than one FILE, precede each with a header giving the file name.\n\
-                     \n\
                      With no FILE, or when FILE is -, read standard input.\n\
                      \n\
                      Mandatory arguments to long flags are mandatory for short flags too.\
diff --git a/src/uu_tail/tail.rs.html b/src/uu_tail/tail.rs.html
index 873ef4599..639a6decd 100644
--- a/src/uu_tail/tail.rs.html
+++ b/src/uu_tail/tail.rs.html
@@ -480,6 +480,15 @@
 480
 481
 482
+483
+484
+485
+486
+487
+488
+489
+490
+491
 
 //  * This file is part of the uutils coreutils package.
 //  *
@@ -518,6 +527,15 @@
 #[cfg(unix)]
 use std::os::unix::fs::MetadataExt;
 
+const ABOUT: &str = "\
+                     Print the last 10 lines of each FILE to standard output.\n\
+                     With more than one FILE, precede each with a header giving the file name.\n\
+                     With no FILE, or when FILE is -, read standard input.\n\
+                     \n\
+                     Mandatory arguments to long flags are mandatory for short flags too.\
+                     ";
+const USAGE: &str = "tail [FLAG]... [FILE]...";
+
 pub mod options {
     pub mod verbosity {
         pub static QUIET: &str = "quiet";
@@ -701,8 +719,8 @@
 pub fn uu_app() -> App<'static, 'static> {
     App::new(uucore::util_name())
         .version(crate_version!())
-        .about("output the last part of files")
-        // TODO: add usage
+        .about(ABOUT)
+        .usage(USAGE)
         .arg(
             Arg::with_name(options::BYTES)
                 .short("c")
diff --git a/uu_hashsum/index.html b/uu_hashsum/index.html
index 6e58e5627..16be3ec86 100644
--- a/uu_hashsum/index.html
+++ b/uu_hashsum/index.html
@@ -1,4 +1,4 @@
-uu_hashsum - Rust

Crate uu_hashsum[][src]

Functions

+uu_hashsum - Rust

Crate uu_hashsum[][src]

Functions

\ No newline at end of file diff --git a/uu_head/fn.uu_app.html b/uu_head/fn.uu_app.html index 9462a2b60..2752ab186 100644 --- a/uu_head/fn.uu_app.html +++ b/uu_head/fn.uu_app.html @@ -1,3 +1,3 @@ -uu_app in uu_head - Rust

Function uu_head::uu_app[][src]

pub fn uu_app() -> App<'static, 'static>
+uu_app in uu_head - Rust

Function uu_head::uu_app[][src]

pub fn uu_app() -> App<'static, 'static>
\ No newline at end of file diff --git a/uu_head/fn.uumain.html b/uu_head/fn.uumain.html index 5f0c58034..bb05d2ab3 100644 --- a/uu_head/fn.uumain.html +++ b/uu_head/fn.uumain.html @@ -1,3 +1,3 @@ -uumain in uu_head - Rust

Function uu_head::uumain[][src]

pub fn uumain(args: impl Args) -> i32
+uumain in uu_head - Rust

Function uu_head::uumain[][src]

pub fn uumain(args: impl Args) -> i32
\ No newline at end of file diff --git a/uu_head/index.html b/uu_head/index.html index 39b40cb96..cbcab7618 100644 --- a/uu_head/index.html +++ b/uu_head/index.html @@ -1,4 +1,4 @@ -uu_head - Rust

Crate uu_head[][src]

Functions

+uu_head - Rust

Crate uu_head[][src]

Functions

\ No newline at end of file diff --git a/uu_tail/fn.uu_app.html b/uu_tail/fn.uu_app.html index b95b7cd84..7c23f36e7 100644 --- a/uu_tail/fn.uu_app.html +++ b/uu_tail/fn.uu_app.html @@ -1,3 +1,3 @@ -uu_app in uu_tail - Rust

Function uu_tail::uu_app[][src]

pub fn uu_app() -> App<'static, 'static>
+uu_app in uu_tail - Rust

Function uu_tail::uu_app[][src]

pub fn uu_app() -> App<'static, 'static>
\ No newline at end of file diff --git a/uu_tail/fn.uumain.html b/uu_tail/fn.uumain.html index 5b2606f45..dfab8915b 100644 --- a/uu_tail/fn.uumain.html +++ b/uu_tail/fn.uumain.html @@ -1,3 +1,3 @@ -uumain in uu_tail - Rust

Function uu_tail::uumain[][src]

pub fn uumain(args: impl Args) -> i32
+uumain in uu_tail - Rust

Function uu_tail::uumain[][src]

pub fn uumain(args: impl Args) -> i32
\ No newline at end of file diff --git a/uu_tail/index.html b/uu_tail/index.html index 409de5d0d..0c849a5ad 100644 --- a/uu_tail/index.html +++ b/uu_tail/index.html @@ -1,4 +1,4 @@ -uu_tail - Rust

Crate uu_tail[][src]

Modules

+uu_tail - Rust

Crate uu_tail[][src]

Modules

Functions

diff --git a/uu_tail/options/index.html b/uu_tail/options/index.html index 7c01a623b..d215a89a7 100644 --- a/uu_tail/options/index.html +++ b/uu_tail/options/index.html @@ -1,4 +1,4 @@ -uu_tail::options - Rust

Module uu_tail::options[][src]

Modules

+uu_tail::options - Rust

Module uu_tail::options[][src]

Modules

Statics

diff --git a/uu_tail/options/static.ARG_FILES.html b/uu_tail/options/static.ARG_FILES.html index a00bfd8f0..e5369ac25 100644 --- a/uu_tail/options/static.ARG_FILES.html +++ b/uu_tail/options/static.ARG_FILES.html @@ -1,3 +1,3 @@ -ARG_FILES in uu_tail::options - Rust

Static uu_tail::options::ARG_FILES[][src]

pub static ARG_FILES: &str
+ARG_FILES in uu_tail::options - Rust

Static uu_tail::options::ARG_FILES[][src]

pub static ARG_FILES: &str
\ No newline at end of file diff --git a/uu_tail/options/static.BYTES.html b/uu_tail/options/static.BYTES.html index 1346f57d6..e80d9ba50 100644 --- a/uu_tail/options/static.BYTES.html +++ b/uu_tail/options/static.BYTES.html @@ -1,3 +1,3 @@ -BYTES in uu_tail::options - Rust

Static uu_tail::options::BYTES[][src]

pub static BYTES: &str
+BYTES in uu_tail::options - Rust

Static uu_tail::options::BYTES[][src]

pub static BYTES: &str
\ No newline at end of file diff --git a/uu_tail/options/static.FOLLOW.html b/uu_tail/options/static.FOLLOW.html index fbcd2b5ee..b944f6b3e 100644 --- a/uu_tail/options/static.FOLLOW.html +++ b/uu_tail/options/static.FOLLOW.html @@ -1,3 +1,3 @@ -FOLLOW in uu_tail::options - Rust

Static uu_tail::options::FOLLOW[][src]

pub static FOLLOW: &str
+FOLLOW in uu_tail::options - Rust

Static uu_tail::options::FOLLOW[][src]

pub static FOLLOW: &str
\ No newline at end of file diff --git a/uu_tail/options/static.LINES.html b/uu_tail/options/static.LINES.html index 9530484b9..76acf024c 100644 --- a/uu_tail/options/static.LINES.html +++ b/uu_tail/options/static.LINES.html @@ -1,3 +1,3 @@ -LINES in uu_tail::options - Rust

Static uu_tail::options::LINES[][src]

pub static LINES: &str
+LINES in uu_tail::options - Rust

Static uu_tail::options::LINES[][src]

pub static LINES: &str
\ No newline at end of file diff --git a/uu_tail/options/static.PID.html b/uu_tail/options/static.PID.html index ab47df9a3..3a2e23954 100644 --- a/uu_tail/options/static.PID.html +++ b/uu_tail/options/static.PID.html @@ -1,3 +1,3 @@ -PID in uu_tail::options - Rust

Static uu_tail::options::PID[][src]

pub static PID: &str
+PID in uu_tail::options - Rust

Static uu_tail::options::PID[][src]

pub static PID: &str
\ No newline at end of file diff --git a/uu_tail/options/static.SLEEP_INT.html b/uu_tail/options/static.SLEEP_INT.html index b48d2c118..d9ddc6dee 100644 --- a/uu_tail/options/static.SLEEP_INT.html +++ b/uu_tail/options/static.SLEEP_INT.html @@ -1,3 +1,3 @@ -SLEEP_INT in uu_tail::options - Rust

Static uu_tail::options::SLEEP_INT[][src]

pub static SLEEP_INT: &str
+SLEEP_INT in uu_tail::options - Rust

Static uu_tail::options::SLEEP_INT[][src]

pub static SLEEP_INT: &str
\ No newline at end of file diff --git a/uu_tail/options/static.ZERO_TERM.html b/uu_tail/options/static.ZERO_TERM.html index 19874c94b..7fb96f02f 100644 --- a/uu_tail/options/static.ZERO_TERM.html +++ b/uu_tail/options/static.ZERO_TERM.html @@ -1,3 +1,3 @@ -ZERO_TERM in uu_tail::options - Rust

Static uu_tail::options::ZERO_TERM[][src]

pub static ZERO_TERM: &str
+ZERO_TERM in uu_tail::options - Rust

Static uu_tail::options::ZERO_TERM[][src]

pub static ZERO_TERM: &str
\ No newline at end of file diff --git a/uu_tail/options/verbosity/index.html b/uu_tail/options/verbosity/index.html index 36b1a299f..b15d19a8c 100644 --- a/uu_tail/options/verbosity/index.html +++ b/uu_tail/options/verbosity/index.html @@ -1,4 +1,4 @@ -uu_tail::options::verbosity - Rust

Module uu_tail::options::verbosity[][src]

Statics

+uu_tail::options::verbosity - Rust

Module uu_tail::options::verbosity[][src]

Statics

\ No newline at end of file diff --git a/uu_tail/options/verbosity/static.QUIET.html b/uu_tail/options/verbosity/static.QUIET.html index e3eb9119f..6b4d6ca79 100644 --- a/uu_tail/options/verbosity/static.QUIET.html +++ b/uu_tail/options/verbosity/static.QUIET.html @@ -1,3 +1,3 @@ -QUIET in uu_tail::options::verbosity - Rust

Static uu_tail::options::verbosity::QUIET[][src]

pub static QUIET: &str
+QUIET in uu_tail::options::verbosity - Rust

Static uu_tail::options::verbosity::QUIET[][src]

pub static QUIET: &str
\ No newline at end of file diff --git a/uu_tail/options/verbosity/static.VERBOSE.html b/uu_tail/options/verbosity/static.VERBOSE.html index 642fce429..a8916b25d 100644 --- a/uu_tail/options/verbosity/static.VERBOSE.html +++ b/uu_tail/options/verbosity/static.VERBOSE.html @@ -1,3 +1,3 @@ -VERBOSE in uu_tail::options::verbosity - Rust

Static uu_tail::options::verbosity::VERBOSE[][src]

pub static VERBOSE: &str
+VERBOSE in uu_tail::options::verbosity - Rust

Static uu_tail::options::verbosity::VERBOSE[][src]

pub static VERBOSE: &str
\ No newline at end of file