Merge remote-tracking branch 'upstream/master' into HEAD

* upstream/master: (342 commits)
  Update TODO list
  Fix some rustc warnings
  Fix some Clippy warnings
  Rustfmt
  Another fix for stderr support
  Rustfmt
  Simplify surrogate pair handling on windows
  Fix on windows when using stderr.
  Allow customization of the output stream
  Ignore all IO errors during completion
  Prepare 2.1.0 release
  Fix line refresh/redraw (#149)
  [unstable] Fix error: scoped lint `...` is experimental
  Update TODO list
  Flag ReadlineError, Cmd, KeyPress as non exhaustive
  Improve key press handling
  Fix clippy warnings
  Rename consts.rs to keys.rs
  Prepare 2.0.1 release
  Windows: fix enable_raw_mode
  ...

Change-Id: I4a5ea48918ba09f93037b8cabdb259af9edab1f2
diff --git a/Cargo.toml b/Cargo.toml
index a8ed288..43a3648 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,8 +15,8 @@
 appveyor = { repository = "kkawakam/rustyline" }
 maintenance = { status = "actively-developed" }
 
+
 [dependencies]
-dirs = "1.0"
 libc = "0.2"
 log = "0.4"
 unicode-width = "0.1"
@@ -24,9 +24,16 @@
 memchr = "2.0"
 
 [target.'cfg(unix)'.dependencies]
-nix = "0.11"
 utf8parse = "0.1"
 
+[target.'cfg(all(unix, not(any(target_os = "fuchsia"))))'.dependencies]
+dirs = "1.0"
+nix = "0.11"
+
+[target.'cfg(target_os = "fuchsia")'.dependencies]
+fuchsia-zircon = "0.3.2"
+fuchsia-device = "0.1.0"
+
 [target.'cfg(windows)'.dependencies]
 winapi = { version = "0.3", features = ["consoleapi", "handleapi", "minwindef", "processenv", "winbase", "wincon", "winuser"] }
 
diff --git a/src/completion.rs b/src/completion.rs
index 39fe213..b71e762 100644
--- a/src/completion.rs
+++ b/src/completion.rs
@@ -267,6 +267,7 @@
     break_chars: &[u8],
     quote: Quote,
 ) -> Result<Vec<Pair>> {
+    #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
     use dirs::home_dir;
     use std::env::current_dir;
 
@@ -277,25 +278,41 @@
     };
 
     let dir_path = Path::new(dir_name);
-    let dir = if dir_path.starts_with("~") {
-        // ~[/...]
-        if let Some(home) = home_dir() {
-            match dir_path.strip_prefix("~") {
-                Ok(rel_path) => home.join(rel_path),
-                _ => home,
+    #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
+    let dir = {
+        if dir_path.starts_with("~") {
+            // ~[/...]
+            if let Some(home) = home_dir() {
+                match dir_path.strip_prefix("~") {
+                    Ok(rel_path) => home.join(rel_path),
+                    _ => home,
+                }
+            } else {
+                dir_path.to_path_buf()
+            }
+        } else if dir_path.is_relative() {
+            // TODO ~user[/...] (https://crates.io/crates/users)
+            if let Ok(cwd) = current_dir() {
+                cwd.join(dir_path)
+            } else {
+                dir_path.to_path_buf()
             }
         } else {
             dir_path.to_path_buf()
         }
-    } else if dir_path.is_relative() {
-        // TODO ~user[/...] (https://crates.io/crates/users)
-        if let Ok(cwd) = current_dir() {
-            cwd.join(dir_path)
+    };
+    #[cfg(target_os = "fuchsia")]
+    let dir = {
+        if dir_path.is_relative() {
+            // TODO ~user[/...] (https://crates.io/crates/users)
+            if let Ok(cwd) = current_dir() {
+                cwd.join(dir_path)
+            } else {
+                dir_path.to_path_buf()
+            }
         } else {
             dir_path.to_path_buf()
         }
-    } else {
-        dir_path.to_path_buf()
     };
 
     let mut entries: Vec<Pair> = Vec::new();
diff --git a/src/error.rs b/src/error.rs
index c49595b..f096521 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,5 +1,5 @@
 //! Contains error type for handling I/O and Errno errors
-#[cfg(unix)]
+#[cfg(all(unix, not(any(target_os = "fuchsia"))))]
 use nix;
 #[cfg(windows)]
 use std::char;
@@ -23,7 +23,7 @@
     #[cfg(unix)]
     Utf8Error,
     /// Unix Error from syscall
-    #[cfg(unix)]
+    #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
     Errno(nix::Error),
     #[cfg(windows)]
     WindowResize,
@@ -39,7 +39,7 @@
             ReadlineError::Interrupted => write!(f, "Interrupted"),
             #[cfg(unix)]
             ReadlineError::Utf8Error => write!(f, "invalid utf-8: corrupt contents"),
-            #[cfg(unix)]
+            #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
             ReadlineError::Errno(ref err) => err.fmt(f),
             #[cfg(windows)]
             ReadlineError::WindowResize => write!(f, "WindowResize"),
@@ -57,7 +57,7 @@
             ReadlineError::Interrupted => "Interrupted",
             #[cfg(unix)]
             ReadlineError::Utf8Error => "invalid utf-8: corrupt contents",
-            #[cfg(unix)]
+            #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
             ReadlineError::Errno(ref err) => err.description(),
             #[cfg(windows)]
             ReadlineError::WindowResize => "WindowResize",
@@ -73,7 +73,7 @@
     }
 }
 
-#[cfg(unix)]
+#[cfg(all(unix, not(any(target_os = "fuchsia"))))]
 impl From<nix::Error> for ReadlineError {
     fn from(err: nix::Error) -> ReadlineError {
         ReadlineError::Errno(err)
diff --git a/src/lib.rs b/src/lib.rs
index 97436a5..cb1d284 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,12 +19,13 @@
 // #![feature(non_exhaustive)]
 // #![feature(tool_lints)]
 
+#[cfg(all(unix, not(any(target_os = "fuchsia"))))]
 extern crate dirs;
 extern crate libc;
 #[macro_use]
 extern crate log;
 extern crate memchr;
-#[cfg(unix)]
+#[cfg(all(unix, not(any(target_os = "fuchsia"))))]
 extern crate nix;
 extern crate unicode_segmentation;
 extern crate unicode_width;
@@ -32,6 +33,12 @@
 extern crate utf8parse;
 #[cfg(windows)]
 extern crate winapi;
+#[cfg(windows)]
+extern crate kernel32;
+#[cfg(target_os = "fuchsia")]
+extern crate fuchsia_zircon as zx;
+#[cfg(target_os = "fuchsia")]
+extern crate fuchsia_device;
 
 pub mod completion;
 pub mod config;
@@ -598,7 +605,7 @@
             Cmd::Interrupt => {
                 return Err(error::ReadlineError::Interrupted);
             }
-            #[cfg(unix)]
+            #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
             Cmd::Suspend => {
                 try!(original_mode.disable_raw_mode());
                 try!(tty::suspend());
diff --git a/src/tty/fuchsia.rs b/src/tty/fuchsia.rs
new file mode 100644
index 0000000..a63723b
--- /dev/null
+++ b/src/tty/fuchsia.rs
@@ -0,0 +1,244 @@
+//! Fuchsia specific definitions
+use std::io::{self, Read, Stdout, Write};
+use std::mem;
+use std::sync::atomic;
+use libc;
+use char_iter;
+
+use config::Config;
+use consts::{self, KeyPress};
+use error;
+use Result;
+use super::{RawMode, RawReader, Term};
+use fuchsia_device::pty;
+
+const STDIN_FILENO: usize = 0;
+const STDOUT_FILENO: usize = 1;
+
+fn get_win_size() -> (usize, usize) {
+    match pty::get_window_size() {
+        Ok(size) => {
+            (size.width as usize, size.height as usize)
+        }
+        _ => (80, 24),
+    }
+}
+
+struct StdinRaw {}
+
+impl Read for StdinRaw {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        loop {
+            let res = unsafe {
+                libc::read(
+                    STDIN_FILENO as i32,
+                    buf.as_mut_ptr() as *mut libc::c_void,
+                    buf.len() as libc::size_t,
+                )
+            };
+            if res == -1 {
+                let error = io::Error::last_os_error();
+                if error.kind() != io::ErrorKind::Interrupted {
+                    return Err(error);
+                }
+            } else {
+                return Ok(res as usize);
+            }
+        }
+    }
+}
+
+pub type Mode = ConsoleMode;
+
+#[derive(Clone, Copy, Debug)]
+pub struct ConsoleMode {}
+
+impl RawMode for Mode {
+    /// RAW mode is never on w/ Fuchsia
+    fn disable_raw_mode(&self) -> Result<()> {
+        Ok(())
+    }
+}
+
+pub type Terminal = Console;
+
+#[derive(Clone, Debug)]
+pub struct Console {
+    stdin_isatty: bool,
+}
+
+pub struct FuchsiaRawReader {
+    chars: char_iter::Chars<StdinRaw>,
+}
+
+impl FuchsiaRawReader {
+    pub fn new() -> Result<FuchsiaRawReader> {
+        let stdin = StdinRaw {};
+        Ok(FuchsiaRawReader {
+            chars: char_iter::chars(stdin),
+        })
+    }
+    fn escape_sequence(&mut self) -> Result<KeyPress> {
+        // Read the next two bytes representing the escape sequence.
+        let seq1 = try!(self.next_char());
+        if seq1 == '[' {
+            // ESC [ sequences.
+            let seq2 = try!(self.next_char());
+            if seq2.is_digit(10) {
+                // Extended escape, read additional byte.
+                let seq3 = try!(self.next_char());
+                if seq3 == '~' {
+                    Ok(match seq2 {
+                        '1' | '7' => KeyPress::Home, // '1': xterm
+                        '3' => KeyPress::Delete,
+                        '4' | '8' => KeyPress::End, // '4': xterm
+                        '5' => KeyPress::PageUp,
+                        '6' => KeyPress::PageDown,
+                        _ => {
+                            debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}{:?}", seq1, seq2, seq3);
+                            KeyPress::UnknownEscSeq
+                        }
+                    })
+                } else {
+                    debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}{:?}", seq1, seq2, seq3);
+                    Ok(KeyPress::UnknownEscSeq)
+                }
+            } else {
+                Ok(match seq2 {
+                    'A' => KeyPress::Up, // ANSI
+                    'B' => KeyPress::Down,
+                    'C' => KeyPress::Right,
+                    'D' => KeyPress::Left,
+                    'F' => KeyPress::End,
+                    'H' => KeyPress::Home,
+                    _ => {
+                        debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}", seq1, seq2);
+                        KeyPress::UnknownEscSeq
+                    }
+                })
+            }
+        } else if seq1 == 'O' {
+            // ESC O sequences.
+            let seq2 = try!(self.next_char());
+            Ok(match seq2 {
+                'A' => KeyPress::Up,
+                'B' => KeyPress::Down,
+                'C' => KeyPress::Right,
+                'D' => KeyPress::Left,
+                'F' => KeyPress::End,
+                'H' => KeyPress::Home,
+                _ => {
+                    debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}", seq1, seq2);
+                    KeyPress::UnknownEscSeq
+                }
+            })
+        } else {
+            // TODO ESC-R (r): Undo all changes made to this line.
+            Ok(match seq1 {
+                '\x08' => KeyPress::Meta('\x08'), // Backspace
+                '-' => KeyPress::Meta('-'),
+                '0'...'9' => KeyPress::Meta(seq1),
+                '<' => KeyPress::Meta('<'),
+                '>' => KeyPress::Meta('>'),
+                'b' | 'B' => KeyPress::Meta('B'),
+                'c' | 'C' => KeyPress::Meta('C'),
+                'd' | 'D' => KeyPress::Meta('D'),
+                'f' | 'F' => KeyPress::Meta('F'),
+                'l' | 'L' => KeyPress::Meta('L'),
+                'n' | 'N' => KeyPress::Meta('N'),
+                'p' | 'P' => KeyPress::Meta('P'),
+                't' | 'T' => KeyPress::Meta('T'),
+                'u' | 'U' => KeyPress::Meta('U'),
+                'y' | 'Y' => KeyPress::Meta('Y'),
+                '\x7f' => KeyPress::Meta('\x7f'), // Delete
+                _ => {
+                    debug!(target: "rustyline", "unsupported esc sequence: M-{:?}", seq1);
+                    KeyPress::UnknownEscSeq
+                }
+            })
+        }
+    }
+}
+
+// TODO properly set raw mode, process escape keys
+
+impl RawReader for FuchsiaRawReader {
+    fn next_key(&mut self) -> Result<KeyPress> {
+        let c = try!(self.next_char());
+
+        let mut key = consts::char_to_key_press(c);
+        if key == KeyPress::Esc {
+            // TODO
+            debug!(target: "rustyline", "ESC + {:?} currently unsupported", key);
+        }
+
+        Ok(key)
+    }
+
+    fn next_char(&mut self) -> Result<char> {
+        match self.chars.next() {
+            Some(ch) => Ok(ch?),
+            None => Err(error::ReadlineError::Eof),
+        }
+    }
+}
+
+impl Term for Console {
+    type Reader = FuchsiaRawReader;
+    type Writer = Stdout;
+    type Mode = Mode;
+
+    fn new() -> Console {
+        let stdin_isatty = true;
+        Console {
+            stdin_isatty: stdin_isatty,
+        }
+    }
+
+    /// Checking for an unsupported TERM in fuchsia is a no-op
+    fn is_unsupported(&self) -> bool {
+        false
+    }
+
+    fn is_stdin_tty(&self) -> bool {
+        self.stdin_isatty
+    }
+
+    /// Try to get the number of columns in the current terminal,
+    /// or assume 80 if it fails.
+    fn get_columns(&self) -> usize {
+        let (cols, _) = get_win_size();
+        cols
+    }
+
+    /// Try to get the number of rows in the current terminal,
+    /// or assume 24 if it fails.
+    fn get_rows(&self) -> usize {
+        let (_, rows) = get_win_size();
+        rows
+    }
+
+    /// Enable RAW mode for the terminal. No termios support, so this fakes it
+    fn enable_raw_mode(&self) -> Result<Mode> {
+        Ok(Mode {})
+    }
+
+    fn create_reader(&self, _: &Config) -> Result<FuchsiaRawReader> {
+        FuchsiaRawReader::new()
+    }
+
+    fn create_writer(&self) -> Stdout {
+        io::stdout()
+    }
+
+    fn sigwinch(&self) -> bool {
+        false
+    }
+
+    /// Clear the screen. Used to handle ctrl+l
+    fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
+        try!(w.write_all(b"\x1b[H\x1b[2J"));
+        try!(w.flush());
+        Ok(())
+    }
+}
diff --git a/src/tty/mod.rs b/src/tty/mod.rs
index 1c6bf41..48f917c 100644
--- a/src/tty/mod.rs
+++ b/src/tty/mod.rs
@@ -208,11 +208,18 @@
 
 // If on Unix platform import Unix TTY module
 // and re-export into mod.rs scope
-#[cfg(all(unix, not(test)))]
+#[cfg(all(unix, not(any(test, target_os = "fuchsia"))))]
 mod unix;
-#[cfg(all(unix, not(test)))]
+#[cfg(all(unix, not(any(test, target_os = "fuchsia"))))]
 pub use self::unix::*;
 
+// If on a Fuchsia platform import Fuchsia TTY module
+// and re-export into mod.rs scope
+#[cfg(target_os = "fuchsia")]
+mod fuchsia;
+#[cfg(target_os = "fuchsia")]
+pub use self::fuchsia::*;
+
 #[cfg(test)]
 mod test;
 #[cfg(test)]