From 4776bfc291c310e75ba04d39bf476013c5a29523 Mon Sep 17 00:00:00 2001
From: Georg Brandl <georg@python.org>
Date: Tue, 31 May 2016 16:49:20 +0200
Subject: [PATCH] Replace Read::chars() by a private copy.

---
 src/char_iter.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++++
 src/error.rs     |   8 ++--
 src/lib.rs       |  16 ++++---
 3 files changed, 118 insertions(+), 12 deletions(-)
 create mode 100644 src/char_iter.rs

diff --git a/src/char_iter.rs b/src/char_iter.rs
new file mode 100644
index 00000000..7d0c46de
--- /dev/null
+++ b/src/char_iter.rs
@@ -0,0 +1,106 @@
+//! An iterator over the `char`s of a reader.
+//!
+//! A copy of the unstable code from the stdlib's std::io::Read::chars.
+
+use std::error;
+use std::fmt;
+use std::io;
+use std::io::Read;
+use std::str;
+
+pub fn chars<R: Read>(read: R) -> Chars<R> where R: Sized {
+    Chars { inner: read }
+}
+
+// https://tools.ietf.org/html/rfc3629
+static UTF8_CHAR_WIDTH: [u8; 256] = [
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
+    0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
+    3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
+    4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
+];
+
+/// Given a first byte, determine how many bytes are in this UTF-8 character
+#[inline]
+fn utf8_char_width(b: u8) -> usize {
+    return UTF8_CHAR_WIDTH[b as usize] as usize;
+}
+
+pub struct Chars<R> {
+    inner: R,
+}
+
+#[derive(Debug)]
+pub enum CharsError {
+    NotUtf8,
+    Other(io::Error),
+}
+
+impl<R: Read> Iterator for Chars<R> {
+    type Item = Result<char, CharsError>;
+
+    fn next(&mut self) -> Option<Result<char, CharsError>> {
+        let mut buf = [0];
+        let first_byte = match self.inner.read(&mut buf) {
+            Ok(0) => return None,
+            Ok(..) => buf[0],
+            Err(e) => return Some(Err(CharsError::Other(e))),
+        };
+        let width = utf8_char_width(first_byte);
+        if width == 1 { return Some(Ok(first_byte as char)) }
+        if width == 0 { return Some(Err(CharsError::NotUtf8)) }
+        let mut buf = [first_byte, 0, 0, 0];
+        {
+            let mut start = 1;
+            while start < width {
+                match self.inner.read(&mut buf[start..width]) {
+                    Ok(0) => return Some(Err(CharsError::NotUtf8)),
+                    Ok(n) => start += n,
+                    Err(e) => return Some(Err(CharsError::Other(e))),
+                }
+            }
+        }
+        Some(match str::from_utf8(&buf[..width]).ok() {
+            Some(s) => Ok(s.chars().next().unwrap()),
+            None => Err(CharsError::NotUtf8),
+        })
+    }
+}
+
+impl error::Error for CharsError {
+    fn description(&self) -> &str {
+        match *self {
+            CharsError::NotUtf8 => "invalid utf8 encoding",
+            CharsError::Other(ref e) => error::Error::description(e),
+        }
+    }
+    fn cause(&self) -> Option<&error::Error> {
+        match *self {
+            CharsError::NotUtf8 => None,
+            CharsError::Other(ref e) => e.cause(),
+        }
+    }
+}
+
+impl fmt::Display for CharsError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            CharsError::NotUtf8 => {
+                "byte stream did not contain valid utf8".fmt(f)
+            }
+            CharsError::Other(ref e) => e.fmt(f),
+        }
+    }
+}
diff --git a/src/error.rs b/src/error.rs
index d4654a09..844805b5 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -4,6 +4,8 @@ use std::error;
 use std::fmt;
 use nix;
 
+use char_iter;
+
 /// The error type for Rustyline errors that can arise from
 /// I/O related errors or Errno when using the nix-rust library
 #[derive(Debug)]
@@ -13,7 +15,7 @@ pub enum ReadlineError {
     /// Error from syscall
     Errno(nix::Error),
     /// Chars Error
-    Char(io::CharsError),
+    Char(char_iter::CharsError),
     /// EOF (Ctrl-d)
     Eof,
     /// Ctrl-C
@@ -56,8 +58,8 @@ impl From<nix::Error> for ReadlineError {
     }
 }
 
-impl From<io::CharsError> for ReadlineError {
-    fn from(err: io::CharsError) -> ReadlineError {
+impl From<char_iter::CharsError> for ReadlineError {
+    fn from(err: char_iter::CharsError) -> ReadlineError {
         ReadlineError::Char(err)
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 14b8de74..2c717294 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,7 +14,6 @@
 //!     Err(_)   => println!("No input"),
 //! }
 //! ```
-#![feature(io)]
 #![cfg_attr(feature="clippy", feature(plugin))]
 #![cfg_attr(feature="clippy", plugin(clippy))]
 
@@ -30,9 +29,10 @@ pub mod error;
 pub mod history;
 mod kill_ring;
 pub mod line_buffer;
+mod char_iter;
 
 use std::fmt;
-use std::io::{self, Read, Write};
+use std::io::{self, Write};
 use std::mem;
 use std::path::Path;
 use std::result;
@@ -525,7 +525,7 @@ fn edit_history_next(s: &mut State, history: &History, prev: bool) -> Result<()>
 }
 
 /// Completes the line/word
-fn complete_line<R: io::Read>(chars: &mut io::Chars<R>,
+fn complete_line<R: io::Read>(chars: &mut char_iter::Chars<R>,
                               s: &mut State,
                               completer: &Completer)
                               -> Result<Option<char>> {
@@ -578,7 +578,7 @@ fn complete_line<R: io::Read>(chars: &mut io::Chars<R>,
 
 /// Incremental search
 #[cfg_attr(feature="clippy", allow(if_not_else))]
-fn reverse_incremental_search<R: io::Read>(chars: &mut io::Chars<R>,
+fn reverse_incremental_search<R: io::Read>(chars: &mut char_iter::Chars<R>,
                                            s: &mut State,
                                            history: &History)
                                            -> Result<Option<KeyPress>> {
@@ -655,7 +655,7 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut io::Chars<R>,
     Ok(Some(key))
 }
 
-fn escape_sequence<R: io::Read>(chars: &mut io::Chars<R>) -> Result<KeyPress> {
+fn escape_sequence<R: io::Read>(chars: &mut char_iter::Chars<R>) -> Result<KeyPress> {
     // Read the next two bytes representing the escape sequence.
     let seq1 = try!(chars.next().unwrap());
     if seq1 == '[' {
@@ -733,7 +733,7 @@ fn readline_edit(prompt: &str,
     kill_ring.reset();
     let mut s = State::new(&mut stdout, prompt, MAX_LINE, get_columns(), history.len());
     let stdin = io::stdin();
-    let mut chars = stdin.lock().chars();
+    let mut chars = char_iter::chars(stdin.lock());
     loop {
         let c = chars.next().unwrap();
         if c.is_err() && SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst) {
@@ -1162,12 +1162,10 @@ mod test {
 
     #[test]
     fn complete_line() {
-        use std::io::Read;
-
         let mut out = ::std::io::sink();
         let mut s = init_state(&mut out, "rus", 3, 80);
         let input = b"\n";
-        let mut chars = input.chars();
+        let mut chars = ::char_iter::chars(&input[..]);
         let completer = SimpleCompleter;
         let ch = super::complete_line(&mut chars, &mut s, &completer).unwrap();
         assert_eq!(Some('\n'), ch);
-- 
GitLab