Add support for Fuchsia
diff --git a/Cargo.toml b/Cargo.toml
index ec6586a..c35c55d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,7 @@
 travis-ci = { repository = "kkawakam/rustyline" }
 appveyor = { repository = "kkawakam/rustyline" }
 
+
 [dependencies]
 libc = "0.2.7"
 log = "0.3"
@@ -21,9 +22,13 @@
 unicode-segmentation = "1.0"
 encode_unicode = "0.1.3"
 
-[target.'cfg(unix)'.dependencies]
+[target.'cfg(all(unix, not(any(target_os = "fuchsia"))))'.dependencies]
 nix = "0.8"
 
+[target.'cfg(target_os = "fuchsia")'.dependencies]
+fuchsia-zircon = "0.3.2"
+fuchsia-device = "0.1.0"
+
 [target.'cfg(windows)'.dependencies]
 winapi = "0.2"
 kernel32-sys = "0.2"
diff --git a/src/error.rs b/src/error.rs
index 009ec65..d778960 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -4,7 +4,7 @@
 use std::io;
 use std::error;
 use std::fmt;
-#[cfg(unix)]
+#[cfg(all(unix, not(any(target_os = "fuchsia"))))]
 use nix;
 
 #[cfg(unix)]
@@ -24,7 +24,7 @@
     #[cfg(unix)]
     Char(char_iter::CharsError),
     /// Unix Error from syscall
-    #[cfg(unix)]
+    #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
     Errno(nix::Error),
     #[cfg(windows)]
     WindowResize,
@@ -40,7 +40,7 @@
             ReadlineError::Interrupted => write!(f, "Interrupted"),
             #[cfg(unix)]
             ReadlineError::Char(ref err) => err.fmt(f),
-            #[cfg(unix)]
+            #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
             ReadlineError::Errno(ref err) => write!(f, "Errno: {}", err.errno().desc()),
             #[cfg(windows)]
             ReadlineError::WindowResize => write!(f, "WindowResize"),
@@ -58,7 +58,7 @@
             ReadlineError::Interrupted => "Interrupted",
             #[cfg(unix)]
             ReadlineError::Char(ref err) => err.description(),
-            #[cfg(unix)]
+            #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
             ReadlineError::Errno(ref err) => err.errno().desc(),
             #[cfg(windows)]
             ReadlineError::WindowResize => "WindowResize",
@@ -74,7 +74,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 24829e0..133b569 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -22,12 +22,16 @@
 extern crate log;
 extern crate unicode_segmentation;
 extern crate unicode_width;
-#[cfg(unix)]
+#[cfg(all(unix, not(any(target_os = "fuchsia"))))]
 extern crate nix;
 #[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;
 mod consts;
@@ -1089,7 +1093,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 fe3ac99..f7ce051 100644
--- a/src/tty/mod.rs
+++ b/src/tty/mod.rs
@@ -55,11 +55,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)]