copperline 0.3.0

Pure-Rust Command Line Editing Library
Documentation
use std::os::unix::io::RawFd;

use std;
use libc;
use nix;
use nix::errno::Errno;
use nix::unistd::{read, write};
use nix::fcntl::{flock, FlockArg};
use nix::sys::termios;
use nix::sys::termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON, OPOST, CS8, ECHO, ICANON, IEXTEN, ISIG, VMIN, VTIME};

use error::Error;

pub struct RawMode {
    fd: RawFd,
    original_termios: termios::Termios
}

impl RawMode {

    fn acquire(fd: RawFd) -> Result<RawMode, nix::Error> {
        try!(flock(fd, FlockArg::LockExclusive));

        let original_termios = try!(termios::tcgetattr(fd));

        let mut raw = original_termios;
        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        raw.c_oflag = raw.c_oflag & !(OPOST);
        raw.c_cflag = raw.c_cflag | (CS8);
        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1;
        raw.c_cc[VTIME] = 0;

        try!(termios::tcsetattr(fd, termios::TCSAFLUSH, &raw));

        Ok(RawMode{
            fd: fd,
            original_termios: original_termios
        })
    }

    pub fn write(&mut self, bytes: &[u8]) -> Result<usize, nix::Error> {
        write(self.fd, bytes)
    }

    pub fn clear(&mut self) -> Result<(), nix::Error> {
        self.write(b"\x1b[H\x1b[2J").map(|_| ())
    }

}

impl Drop for RawMode {
    fn drop(&mut self) {
        let _ = termios::tcsetattr(self.fd, termios::TCSAFLUSH, &self.original_termios);
        let _ = flock(self.fd, FlockArg::Unlock);
    }
}

pub struct Term {
    in_fd: RawFd,
    out_fd: RawFd
}

static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb","cons25","emacs"];

impl Term {

    pub fn new(in_fd: RawFd, out_fd: RawFd) -> Term {
        Term {
            in_fd: in_fd,
            out_fd: out_fd
        }
    }

    pub fn is_unsupported_term() -> bool {
        match std::env::var("TERM") {
            Ok(term) => UNSUPPORTED_TERM.iter().any(|x| &&term == x),
            Err(_) => false
        }
    }

    pub fn is_a_tty(&self) -> bool {
        unsafe { libc::isatty(self.out_fd) != 0 }
    }

    pub fn acquire_raw_mode(&self) -> Result<RawMode, Error> {
        if !self.is_a_tty() {
            return Err(Error::from(nix::Error::from_errno(Errno::ENOTTY)));
        }
        RawMode::acquire(self.out_fd).map_err(Error::from)
    }

    pub fn read_byte(&mut self) -> Result<Option<u8>, nix::Error> {
        let mut input: [u8; 1] = [0; 1];
        let n = try!(read(self.in_fd, &mut input));
        if n == 0 {
            return Ok(None);
        }
        Ok(Some(input[0]))
    }

}